diff --git a/addons/mrp/mrp.py b/addons/mrp/mrp.py index 1055e26f759..4a1c74ff203 100644 --- a/addons/mrp/mrp.py +++ b/addons/mrp/mrp.py @@ -772,6 +772,8 @@ class mrp_production(osv.osv): return 1 def _get_produced_qty(self, cr, uid, production, context=None): + ''' returns the produced quantity of product 'production.product_id' for the given production, in the product UoM + ''' produced_qty = 0 for produced_product in production.move_created_ids2: if (produced_product.scrapped) or (produced_product.product_id.id != production.product_id.id): @@ -780,6 +782,8 @@ class mrp_production(osv.osv): return produced_qty def _get_consumed_data(self, cr, uid, production, context=None): + ''' returns a dictionary containing for each raw material of the given production, its quantity already consumed (in the raw material UoM) + ''' consumed_data = {} # Calculate already consumed qtys for consumed in production.move_lines2: @@ -790,71 +794,68 @@ class mrp_production(osv.osv): consumed_data[consumed.product_id.id] += consumed.product_qty return consumed_data - def _calculate_qty(self, cr, uid, production, product_qty = 0.0, context=None): + def _calculate_qty(self, cr, uid, production, product_qty=0.0, context=None): """ Calculates the quantity still needed to produce an extra number of products """ quant_obj = self.pool.get("stock.quant") - #In case no product qty is given, take product qty of - if product_qty == 0.0: - done = 0.0 - for move in production.move_created_ids2: - if move.product_id == production.product_id: - if not move.scrapped: - done += move.product_qty - product_qty = production.product_qty - done - produced_qty = self._get_produced_qty(cr, uid, production, context=context) consumed_data = self._get_consumed_data(cr, uid, production, context=context) + + #In case no product_qty is given, take the remaining qty to produce for the given production + if not product_qty: + product_qty = production.product_qty - produced_qty + dicts = {} # Find product qty to be consumed and consume it for scheduled in production.product_lines: - if not dicts.get(scheduled.product_id.id): - dicts[scheduled.product_id.id] = {} - # total qty of consumed product we need after this consumption - total_consume = ((product_qty + produced_qty) * scheduled.product_qty / production.product_qty) - + consumed_qty = consumed_data.get(scheduled.product_id.id, 0.0) # qty available for consume and produce - qty_avail = scheduled.product_qty - consumed_data.get(scheduled.product_id.id, 0.0) - + qty_avail = scheduled.product_qty - consumed_qty if qty_avail <= 0.0: # there will be nothing to consume for this raw material continue - - qty = total_consume - consumed_data.get(scheduled.product_id.id, 0.0) + + if not dicts.get(scheduled.product_id.id): + dicts[scheduled.product_id.id] = {} + + # total qty of consumed product we need after this consumption + total_consume = ((product_qty + produced_qty) * scheduled.product_qty / production.product_qty) + qty = total_consume - consumed_qty + # Search for quants related to this related move - move = [x for x in production.move_lines if x.product_id.id == scheduled.product_id.id] - #TODO: check if not already in dict_new - if move: + for move in production.move_lines: + if qty <= 0.0: + break + if move.product_id.id != scheduled.product_id.id: + continue product_id = scheduled.product_id.id - move = move[0] - quants = quant_obj.quants_get_prefered_domain(cr, uid, move.location_id, scheduled.product_id, qty, domain=[('qty', '>', 0.0)], + + q = min(move.product_qty, qty) + quants = quant_obj.quants_get_prefered_domain(cr, uid, move.location_id, scheduled.product_id, q, domain=[('qty', '>', 0.0)], prefered_domain=[('reservation_id', '=', move.id)], fallback_domain=[('reservation_id', '=', False)], context=context) - for quant in quants: - if quant[0]: - lot_id = quant[0].lot_id.id - prod_qty = quant[1] + for quant, quant_qty in quants: + if quant: + lot_id = quant.lot_id.id if not product_id in dicts.keys(): - dicts[product_id] = {lot_id : prod_qty} + dicts[product_id] = {lot_id : quant_qty} elif lot_id in dicts[product_id].keys(): - dicts[product_id][lot_id] += prod_qty + dicts[product_id][lot_id] += quant_qty else: - dicts[product_id][lot_id] = prod_qty - qty -= quant[1] - if qty > 0: - if dicts[product_id].get(False): - dicts[product_id][False] += qty - else: - dicts[product_id][False] = qty + dicts[product_id][lot_id] = quant_qty + qty -= quant_qty + if qty > 0: + if dicts[product_id].get(False): + dicts[product_id][False] += qty + else: + dicts[product_id][False] = qty consume_lines = [] for prod in dicts.keys(): - for lot in dicts[prod].keys(): - consume_lines.append({'product_id': prod, 'product_qty':dicts[prod][lot], 'lot_id': lot}) + for lot, qty in dicts[prod].items(): + consume_lines.append({'product_id': prod, 'product_qty': qty, 'lot_id': lot}) return consume_lines - - def action_produce(self, cr, uid, production_id, production_qty, production_mode, wiz=False, context=None): """ To produce final product based on production mode (consume/consume&produce). If Production mode is consume, all stock move lines of raw materials will be done/consumed. @@ -863,11 +864,9 @@ class mrp_production(osv.osv): @param production_id: the ID of mrp.production object @param production_qty: specify qty to produce @param production_mode: specify production mode (consume/consume&produce). - @param wizard: the mrp produce product wizard, which will tell the amount of consumed products needed + @param wiz: the mrp produce product wizard, which will tell the amount of consumed products needed @return: True """ - - stock_mov_obj = self.pool.get('stock.move') production = self.browse(cr, uid, production_id, context=context) if not production.move_lines and production.state == 'ready': @@ -902,36 +901,33 @@ class mrp_production(osv.osv): lot_id = False if wiz: lot_id = wiz.lot_id.id - new_moves = stock_mov_obj.action_consume(cr, uid, [produce_product.id], (subproduct_factor * production_qty), location_id = produce_product.location_id.id, restrict_lot_id = lot_id, context=context) + new_moves = stock_mov_obj.action_consume(cr, uid, [produce_product.id], (subproduct_factor * production_qty), location_id=produce_product.location_id.id, restrict_lot_id=lot_id, context=context) if produce_product.product_id.id == production.product_id.id and new_moves: main_production_move = new_moves[0] if production_mode in ['consume','consume_produce']: - consume_lines = [] if wiz: + consume_lines = [] for cons in wiz.consume_lines: consume_lines.append({'product_id': cons.product_id.id, 'lot_id': cons.lot_id.id, 'product_qty': cons.product_qty}) else: consume_lines = self._calculate_qty(cr, uid, production, production_qty, context=context) for consume in consume_lines: - # Search move for the product - corresponding_move = [x for x in production.move_lines if x.product_id.id == consume['product_id']] - if corresponding_move: - corresponding_move = corresponding_move[0] - default_values = {} - if corresponding_move.product_qty > consume['product_qty']: - stock_mov_obj.action_consume(cr, uid, [corresponding_move.id], consume['product_qty'], corresponding_move.location_id.id, - restrict_lot_id = consume['lot_id'], consumed_for = main_production_move, context=context) - else: - stock_mov_obj.action_consume(cr, uid, [corresponding_move.id], corresponding_move.product_qty, corresponding_move.location_id.id, - restrict_lot_id = consume['lot_id'], consumed_for = main_production_move, context=context) + remaining_qty = consume['product_qty'] + for raw_material_line in production.move_lines: + if remaining_qty <= 0: + break + if consume[product_id] != raw_material.product_id.id: + continue + consumed_qty = min(remaining_qty, raw_material_line.product_qty) + stock_mov_obj.action_consume(cr, uid, [corresponding_move.id], consumed_qty, corresponding_move.location_id.id, + restrict_lot_id=consume['lot_id'], consumed_for=main_production_move, context=context) + remaining_qty -= consumed_qty self.message_post(cr, uid, production_id, body=_("%s produced") % self._description, context=context) self.signal_button_produce_done(cr, uid, [production_id]) return True - - def _costs_generate(self, cr, uid, production): """ Calculates total costs at the end of the production. @param production: Id of production order. diff --git a/addons/mrp/stock.py b/addons/mrp/stock.py index dd516707e7b..59b41e71c90 100644 --- a/addons/mrp/stock.py +++ b/addons/mrp/stock.py @@ -113,6 +113,8 @@ class StockMove(osv.osv): @param consumed_for: optionnal parameter given to this function to make the link between raw material consumed and produced product, for a better traceability @return: Consumed lines """ + if context is None: + context = {} res = [] production_obj = self.pool.get('mrp.production') uom_obj = self.pool.get('product.uom') diff --git a/addons/mrp/wizard/mrp_product_produce_view.xml b/addons/mrp/wizard/mrp_product_produce_view.xml index 01b90433c1b..4a52e9b74ed 100644 --- a/addons/mrp/wizard/mrp_product_produce_view.xml +++ b/addons/mrp/wizard/mrp_product_produce_view.xml @@ -20,7 +20,6 @@ attrs="{'required': [('track_production', '=', True), ('mode', '=', 'consume_produce')]}" groups="stock.group_tracking_lot"/> - diff --git a/addons/stock/stock.py b/addons/stock/stock.py index 1ba006ba325..ace2da961a2 100644 --- a/addons/stock/stock.py +++ b/addons/stock/stock.py @@ -285,17 +285,17 @@ class stock_quant(osv.osv): def quants_reserve(self, cr, uid, quants, move, link=False, context=None): '''This function reserves quants for the given move (and optionally given link). If the total of quantity reserved is enough, the move's state - is also set to 'assigned' Please do not pass negative quants. + is also set to 'assigned' - :param quants: list of tuple(quant browse record or None, qty to reserve). If None is given as first tuple element, the item will be ignored + :param quants: list of tuple(quant browse record or None, qty to reserve). If None is given as first tuple element, the item will be ignored. Negative quants should not be received as argument :param move: browse record :param link: browse record (stock.move.operation.link) ''' toreserve = [] #split quants if needed for quant, qty in quants: - if qty < 0.0: - raise osv.except_osv(_('Error!'), _('You can not reserve negative quants. ')) + if qty <= 0.0 or quant.qty <= 0.0: + raise osv.except_osv(_('Error!'), _('You can not reserve a negative quantity or a negative quant.')) if not quant: continue self._quant_split(cr, uid, quant, qty, context=context) @@ -1997,7 +1997,6 @@ class stock_move(osv.osv): return new_move - class stock_inventory(osv.osv): _name = "stock.inventory" _description = "Inventory"