X-Git-Url: http://git.inspyration.org/?a=blobdiff_plain;f=addons%2Fstock%2Fstock.py;h=2d49d26063abb746fecef8de61022543cd622d5d;hb=bb45636ba7e3a758dcbe4517d7b62a313e3a308e;hp=80a9aaf87d5bef3438a0e0f04d5347229957d961;hpb=cf16632b44cbb093564124f81843eec82b836d38;p=odoo%2Fodoo.git diff --git a/addons/stock/stock.py b/addons/stock/stock.py index 80a9aaf..2d49d26 100644 --- a/addons/stock/stock.py +++ b/addons/stock/stock.py @@ -25,7 +25,7 @@ import json import time from openerp.osv import fields, osv -from openerp.tools.float_utils import float_compare +from openerp.tools.float_utils import float_compare, float_round from openerp.tools.translate import _ from openerp.tools import DEFAULT_SERVER_DATETIME_FORMAT, DEFAULT_SERVER_DATE_FORMAT from openerp import SUPERUSER_ID, api @@ -378,9 +378,10 @@ class stock_quant(osv.osv): if move.picking_id: self.pool.get('stock.picking').write(cr, uid, [move.picking_id.id], {'recompute_pack_op': True}, context=context) #check if move'state needs to be set as 'assigned' - if float_compare(reserved_availability, move.product_qty, precision_rounding=move.product_uom.rounding) == 0 and move.state in ('confirmed', 'waiting') : + rounding = move.product_id.uom_id.rounding + if float_compare(reserved_availability, move.product_qty, precision_rounding=rounding) == 0 and move.state in ('confirmed', 'waiting') : self.pool.get('stock.move').write(cr, uid, [move.id], {'state': 'assigned'}, context=context) - elif float_compare(reserved_availability, 0, precision_rounding=move.product_uom.rounding) > 0 and not move.partially_available: + elif float_compare(reserved_availability, 0, precision_rounding=rounding) > 0 and not move.partially_available: self.pool.get('stock.move').write(cr, uid, [move.id], {'partially_available': True}, context=context) def quants_move(self, cr, uid, quants, move, location_to, location_from=False, lot_id=False, owner_id=False, src_package_id=False, dest_package_id=False, context=None): @@ -487,10 +488,11 @@ class stock_quant(osv.osv): context = {} price_unit = self.pool.get('stock.move').get_price_unit(cr, uid, move, context=context) location = force_location_to or move.location_dest_id + rounding = move.product_id.uom_id.rounding vals = { 'product_id': move.product_id.id, 'location_id': location.id, - 'qty': qty, + 'qty': float_round(qty, precision_rounding=rounding), 'cost': price_unit, 'history_ids': [(4, move.id)], 'in_date': datetime.now().strftime(DEFAULT_SERVER_DATETIME_FORMAT), @@ -505,7 +507,7 @@ class stock_quant(osv.osv): #it means that a negative quant has to be created as well. negative_vals = vals.copy() negative_vals['location_id'] = force_location_from and force_location_from.id or move.location_id.id - negative_vals['qty'] = -qty + negative_vals['qty'] = float_round(-qty, precision_rounding=rounding) negative_vals['cost'] = price_unit negative_vals['negative_move_id'] = move.id negative_vals['package_id'] = src_package_id @@ -518,13 +520,13 @@ class stock_quant(osv.osv): def _quant_split(self, cr, uid, quant, qty, context=None): context = context or {} - - if (quant.qty > 0 and float_compare(quant.qty, qty, precision_rounding=quant.product_id.uom_id.rounding) <= 0)\ - or (quant.qty <= 0 and float_compare(quant.qty, qty, precision_rounding=quant.product_id.uom_id.rounding) >= 0) : - #(quant.qty > 0 and quant.qty <= qty) or (quant.qty <= 0 and quant.qty >= qty): + rounding = quant.product_id.uom_id.rounding + if float_compare(abs(quant.qty), abs(qty), precision_rounding=rounding) <= 0: # if quant <= qty in abs, take it entirely return False - new_quant = self.copy(cr, SUPERUSER_ID, quant.id, default={'qty': quant.qty - qty, 'history_ids': [(4, x.id) for x in quant.history_ids]}, context=context) - self.write(cr, SUPERUSER_ID, quant.id, {'qty': qty}, context=context) + qty_round = float_round(qty, precision_rounding=rounding) + new_qty_round = float_round(quant.qty - qty, precision_rounding=rounding) + new_quant = self.copy(cr, SUPERUSER_ID, quant.id, default={'qty': new_qty_round, 'history_ids': [(4, x.id) for x in quant.history_ids]}, context=context) + self.write(cr, SUPERUSER_ID, quant.id, {'qty': qty_round}, context=context) quant.refresh() return self.browse(cr, uid, new_quant, context=context) @@ -554,6 +556,7 @@ class stock_quant(osv.osv): dom += [('lot_id', '=', quant.lot_id.id)] dom += [('owner_id', '=', quant.owner_id.id)] dom += [('package_id', '=', quant.package_id.id)] + dom += [('id', '!=', quant.propagated_from_id.id)] quants = self.quants_get(cr, uid, quant.location_id, quant.product_id, quant.qty, dom, context=context) product_uom_rounding = quant.product_id.uom_id.rounding for quant_neg, qty in quants: @@ -577,12 +580,15 @@ class stock_quant(osv.osv): remaining_to_solve_quant_ids = self.search(cr, uid, [('propagated_from_id', '=', quant_neg.id), ('id', 'not in', solved_quant_ids)], context=context) if remaining_to_solve_quant_ids: self.write(cr, SUPERUSER_ID, remaining_to_solve_quant_ids, {'propagated_from_id': remaining_neg_quant.id}, context=context) + if solving_quant.propagated_from_id and solved_quant_ids: + self.write(cr, uid, solved_quant_ids, {'propagated_from_id': solving_quant.propagated_from_id.id}, context=context) #delete the reconciled quants, as it is replaced by the solved quants self.unlink(cr, SUPERUSER_ID, [quant_neg.id], context=context) - #price update + accounting entries adjustments - self._price_update(cr, uid, solved_quant_ids, solving_quant.cost, context=context) - #merge history (and cost?) - self._quants_merge(cr, uid, solved_quant_ids, solving_quant, context=context) + if solved_quant_ids: + #price update + accounting entries adjustments + self._price_update(cr, uid, solved_quant_ids, solving_quant.cost, context=context) + #merge history (and cost?) + self._quants_merge(cr, uid, solved_quant_ids, solving_quant, context=context) self.unlink(cr, SUPERUSER_ID, [solving_quant.id], context=context) solving_quant = remaining_solving_quant @@ -619,12 +625,11 @@ class stock_quant(osv.osv): res.append((None, quantity)) break for quant in self.browse(cr, uid, quants, context=context): - qty_cmp = float_compare(quantity, abs(quant.qty), precision_rounding=product.uom_id.rounding) - qty0_cmp = float_compare(quantity, 0.0, precision_rounding=product.uom_id.rounding) - if qty_cmp >= 0: + rounding = product.uom_id.rounding + if float_compare(quantity, abs(quant.qty), precision_rounding=rounding) >= 0: res += [(quant, abs(quant.qty))] quantity -= abs(quant.qty) - elif qty0_cmp != 0: + elif float_compare(quantity, 0.0, precision_rounding=rounding) != 0: res += [(quant, quantity)] quantity = 0 break @@ -1023,6 +1028,14 @@ class stock_picking(osv.osv): product_putaway_strats[product.id] = location return location or picking.location_dest_id.id + # If we encounter an UoM that is smaller than the default UoM or the one already chosen, use the new one instead. + product_uom = {} # Determines UoM used in pack operations + for move in picking.move_lines: + if not product_uom.get(move.product_id.id): + product_uom[move.product_id.id] = move.product_id.uom_id.id + if move.product_uom.id != move.product_id.uom_id.id and move.product_uom.factor > product_uom[move.product_id.id]: + product_uom[move.product_id.id] = move.product_uom.id + pack_obj = self.pool.get("stock.quant.package") quant_obj = self.pool.get("stock.quant") vals = [] @@ -1073,17 +1086,24 @@ class stock_picking(osv.osv): qtys_grouped[key] = qty # Create the necessary operations for the grouped quants and remaining qtys + uom_obj = self.pool.get('product.uom') for key, qty in qtys_grouped.items(): + product = self.pool.get("product.product").browse(cr, uid, key[0], context=context) + uom_id = product.uom_id.id + qty_uom = qty + if product_uom.get(key[0]): + uom_id = product_uom[key[0]] + qty_uom = uom_obj._compute_qty(cr, uid, product.uom_id.id, qty, uom_id) vals.append({ 'picking_id': picking.id, - 'product_qty': qty, + 'product_qty': qty_uom, 'product_id': key[0], 'package_id': key[1], 'lot_id': key[2], 'owner_id': key[3], 'location_id': key[4], 'location_dest_id': key[5], - 'product_uom_id': self.pool.get("product.product").browse(cr, uid, key[0], context=context).uom_id.id, + 'product_uom_id': uom_id, }) return vals @@ -1272,14 +1292,21 @@ class stock_picking(osv.osv): """ Creates an extra move when there is no corresponding original move to be copied """ + uom_obj = self.pool.get("product.uom") + uom_id = product.uom_id.id + qty = remaining_qty + if op.product_id and op.product_uom_id and op.product_uom_id.id != product.uom_id.id: + if op.product_uom_id.factor > product.uom_id.factor: #If the pack operation's is a smaller unit + uom_id = op.product_uom_id.id + qty = uom_obj._compute_qty_obj(cr, uid, product.uom_id, remaining_qty, op.product_uom_id) picking = op.picking_id res = { 'picking_id': picking.id, 'location_id': picking.location_id.id, 'location_dest_id': picking.location_dest_id.id, 'product_id': product.id, - 'product_uom': product.uom_id.id, - 'product_uom_qty': remaining_qty, + 'product_uom': uom_id, + 'product_uom_qty': qty, 'name': _('Extra Move: ') + product.name, 'state': 'draft', } @@ -1294,8 +1321,8 @@ class stock_picking(osv.osv): moves = [] for op in picking.pack_operation_ids: for product_id, remaining_qty in operation_obj._get_remaining_prod_quantities(cr, uid, op, context=context).items(): - if remaining_qty > 0: - product = self.pool.get('product.product').browse(cr, uid, product_id, context=context) + product = self.pool.get('product.product').browse(cr, uid, product_id, context=context) + if float_compare(remaining_qty, 0, precision_rounding=product.uom_id.rounding) > 0: vals = self._prepare_values_extra_move(cr, uid, op, product, remaining_qty, context=context) moves.append(move_obj.create(cr, uid, vals, context=context)) if moves: @@ -1563,7 +1590,7 @@ class stock_move(osv.osv): uom_obj = self.pool.get('product.uom') res = {} for m in self.browse(cr, uid, ids, context=context): - res[m.id] = uom_obj._compute_qty_obj(cr, uid, m.product_uom, m.product_uom_qty, m.product_id.uom_id, round=False, context=context) + res[m.id] = uom_obj._compute_qty_obj(cr, uid, m.product_uom, m.product_uom_qty, m.product_id.uom_id, context=context) return res def _get_remaining_qty(self, cr, uid, ids, field_name, args, context=None): @@ -1573,8 +1600,8 @@ class stock_move(osv.osv): qty = move.product_qty for record in move.linked_move_operation_ids: qty -= record.qty - #converting the remaining quantity in the move UoM - res[move.id] = uom_obj._compute_qty_obj(cr, uid, move.product_id.uom_id, qty, move.product_uom, round=False, context=context) + # Keeping in product default UoM + res[move.id] = qty return res def _get_lot_ids(self, cr, uid, ids, field_name, args, context=None): @@ -1728,8 +1755,8 @@ class stock_move(osv.osv): 'quant_ids': fields.many2many('stock.quant', 'stock_quant_move_rel', 'move_id', 'quant_id', 'Moved Quants'), 'reserved_quant_ids': fields.one2many('stock.quant', 'reservation_id', 'Reserved quants'), 'linked_move_operation_ids': fields.one2many('stock.move.operation.link', 'move_id', string='Linked Operations', readonly=True, help='Operations that impact this move for the computation of the remaining quantities'), - 'remaining_qty': fields.function(_get_remaining_qty, type='float', string='Remaining Quantity', - digits_compute=dp.get_precision('Product Unit of Measure'), states={'done': [('readonly', True)]},), + 'remaining_qty': fields.function(_get_remaining_qty, type='float', string='Remaining Quantity', digits=0, + states={'done': [('readonly', True)]}, help="Remaining Quantity in default UoM according to operations matched with this move"), 'procurement_id': fields.many2one('procurement.order', 'Procurement'), 'group_id': fields.many2one('procurement.group', 'Procurement Group'), 'rule_id': fields.many2one('procurement.rule', 'Procurement Rule', help='The pull rule that created this stock move'), @@ -2313,11 +2340,14 @@ class stock_move(osv.osv): # Handle pack in pack if not ops.product_id and ops.package_id and ops.result_package_id.id != ops.package_id.parent_id.id: self.pool.get('stock.quant.package').write(cr, SUPERUSER_ID, [ops.package_id.id], {'parent_id': ops.result_package_id.id}, context=context) + if not move_qty.get(move.id): + raise osv.except_osv(_("Error"), _("The roundings of your Unit of Measures %s on the move vs. %s on the product don't allow to do these operations or you are not transferring the picking at once. ") % (move.product_uom.name, move.product_id.uom_id.name)) move_qty[move.id] -= record.qty #Check for remaining qtys and unreserve/check move_dest_id in move_dest_ids = set() for move in self.browse(cr, uid, ids, context=context): - if move_qty[move.id] > 0: # (=In case no pack operations in picking) + move_qty_cmp = float_compare(move_qty[move.id], 0, precision_rounding=move.product_id.uom_id.rounding) + if move_qty_cmp > 0: # (=In case no pack operations in picking) main_domain = [('qty', '>', 0)] prefered_domain = [('reservation_id', '=', move.id)] fallback_domain = [('reservation_id', '=', False)] @@ -3808,9 +3838,6 @@ class stock_pack_operation(osv.osv): qty = uom_obj._compute_qty_obj(cr, uid, ops.product_uom_id, ops.product_qty, ops.product_id.uom_id, context=context) for record in ops.linked_move_operation_ids: qty -= record.qty - #converting the remaining quantity in the pack operation UoM - if ops.product_uom_id: - qty = uom_obj._compute_qty_obj(cr, uid, ops.product_id.uom_id, qty, ops.product_uom_id, context=context) res[ops.id] = qty return res @@ -3857,7 +3884,7 @@ class stock_pack_operation(osv.osv): 'cost': fields.float("Cost", help="Unit Cost for this product line"), 'currency': fields.many2one('res.currency', string="Currency", help="Currency in which Unit cost is expressed", ondelete='CASCADE'), 'linked_move_operation_ids': fields.one2many('stock.move.operation.link', 'operation_id', string='Linked Moves', readonly=True, help='Moves impacted by this operation for the computation of the remaining quantities'), - 'remaining_qty': fields.function(_get_remaining_qty, type='float', string='Remaining Qty'), + 'remaining_qty': fields.function(_get_remaining_qty, type='float', digits = 0, string="Remaining Qty", help="Remaining quantity in default UoM according to moves matched with this operation. "), 'location_id': fields.many2one('stock.location', 'Source Location', required=True), 'location_dest_id': fields.many2one('stock.location', 'Destination Location', required=True), 'processed': fields.selection([('true','Yes'), ('false','No')],'Has been processed?', required=True),