diff --git a/addons/mrp/data/mrp_lot_demo.yml b/addons/mrp/data/mrp_lot_demo.yml index 0f5c5c987d2..bb0c54e238e 100644 --- a/addons/mrp/data/mrp_lot_demo.yml +++ b/addons/mrp/data/mrp_lot_demo.yml @@ -5,7 +5,7 @@ wiz_obj = self.env['mrp.product.produce'] ctx = dict(self.env.context, active_id=self.id) produce_wiz = wiz_obj.with_context(ctx).create({'product_qty': self.product_qty, 'lot_id': ref('lot_product_27_0')}) - produce_wiz.consume_line_ids.write({'qty_done': produce_wiz.product_qty}) + produce_wiz.produce_line_ids.write({'qty_done': produce_wiz.product_qty}) #Produce Product,Post Inventory and Set MO Done. produce_wiz.do_produce() self.post_inventory() diff --git a/addons/mrp/tests/test_workorder_operation.py b/addons/mrp/tests/test_workorder_operation.py index 15adcd236c8..04b112c31b3 100644 --- a/addons/mrp/tests/test_workorder_operation.py +++ b/addons/mrp/tests/test_workorder_operation.py @@ -229,12 +229,11 @@ class TestWorkOrderProcess(common.TransactionCase): # ----------------- # Produce 6 Unit of custom laptop will consume ( 12 Unit of keybord and 12 Unit of charger) - context = {"active_ids": [mo_custom_laptop.id], "active_id": mo_custom_laptop.id} product_consume = self.env['mrp.product.produce'].with_context(context).create({'product_qty': 6.00}) laptop_lot_001 = self.env['stock.production.lot'].create({'product_id': custom_laptop.id}) product_consume.lot_id = laptop_lot_001.id - product_consume.consume_line_ids.write({'qty_done': 12}) + product_consume.produce_line_ids.write({'qty_done': 12}) product_consume.do_produce() # Check consumed move after produce 6 quantity of customized laptop. @@ -259,8 +258,8 @@ class TestWorkOrderProcess(common.TransactionCase): product_consume = self.env['mrp.product.produce'].with_context(context).create({'product_qty': 4.00}) laptop_lot_002 = self.env['stock.production.lot'].create({'product_id': custom_laptop.id}) product_consume.lot_id = laptop_lot_002.id - self.assertEquals(len(product_consume.consume_line_ids), 2) - product_consume.consume_line_ids.write({'qty_done': 8}) + self.assertEquals(len(product_consume.produce_line_ids), 2) + product_consume.produce_line_ids.write({'qty_done': 8}) product_consume.do_produce() charger_move = mo_custom_laptop.move_raw_ids.filtered(lambda x: x.product_id.id == product_charger.id and x.state != 'done') keybord_move = mo_custom_laptop.move_raw_ids.filtered(lambda x: x.product_id.id == product_keybord.id and x.state !='done') @@ -394,9 +393,9 @@ class TestWorkOrderProcess(common.TransactionCase): product_consume = self.env['mrp.product.produce'].with_context(context).create({'product_qty': 10}) # laptop_lot_002 = self.env['stock.production.lot'].create({'product_id': custom_laptop.id}) product_consume.lot_id = lot_a.id - self.assertEquals(len(product_consume.consume_line_ids), 2) - product_consume.consume_line_ids.filtered(lambda x : x.product_id == product_C).write({'qty_done': 3000}) - product_consume.consume_line_ids.filtered(lambda x : x.product_id == product_B).write({'qty_done': 20}) + self.assertEquals(len(product_consume.produce_line_ids), 2) + product_consume.produce_line_ids.filtered(lambda x : x.product_id == product_C).write({'qty_done': 3000}) + product_consume.produce_line_ids.filtered(lambda x : x.product_id == product_B).write({'qty_done': 20}) product_consume.do_produce() mo_custom_product.post_inventory() diff --git a/addons/mrp/wizard/mrp_product_produce.py b/addons/mrp/wizard/mrp_product_produce.py index 8b08771e6e6..61bdffc4618 100644 --- a/addons/mrp/wizard/mrp_product_produce.py +++ b/addons/mrp/wizard/mrp_product_produce.py @@ -17,53 +17,61 @@ class MrpProductProduce(models.TransientModel): res = super(MrpProductProduce, self).default_get(fields) if self._context and self._context.get('active_id'): production = self.env['mrp.production'].browse(self._context['active_id']) - #serial_raw = production.move_raw_ids.filtered(lambda x: x.product_id.tracking == 'serial') - main_product_moves = production.move_finished_ids.filtered(lambda x: x.product_id.id == production.product_id.id) serial_finished = (production.product_id.tracking == 'serial') - serial = bool(serial_finished) if serial_finished: - quantity = 1.0 + todo_quantity = 1.0 else: - quantity = production.product_qty - sum(main_product_moves.mapped('quantity_done')) - quantity = quantity if (quantity > 0) else 0 - lines = [] - existing_lines = [] - for move in production.move_raw_ids.filtered(lambda x: (x.product_id.tracking != 'none') and x.state not in ('done', 'cancel')): - if not move.move_line_ids.filtered(lambda x: not x.lot_produced_id): - qty = quantity / move.bom_line_id.bom_id.product_qty * move.bom_line_id.product_qty - if move.product_id.tracking == 'serial': - while float_compare(qty, 0.0, precision_rounding=move.product_uom.rounding) > 0: + main_product_moves = production.move_finished_ids.filtered(lambda x: x.product_id.id == production.product_id.id) + todo_quantity = production.product_qty - sum(main_product_moves.mapped('quantity_done')) + todo_quantity = todo_quantity if (todo_quantity > 0) else 0 + if 'production_id' in fields: + res['production_id'] = production.id + if 'product_id' in fields: + res['product_id'] = production.product_id.id + if 'product_uom_id' in fields: + res['product_uom_id'] = production.product_uom_id.id + if 'serial' in fields: + res['serial'] = bool(serial_finished) + if 'product_qty' in fields: + res['product_qty'] = todo_quantity + if 'produce_line_ids' in fields: + lines = [] + for move in production.move_raw_ids.filtered(lambda x: (x.product_id.tracking != 'none') and x.state not in ('done', 'cancel')): + qty_to_consume = todo_quantity / move.bom_line_id.bom_id.product_qty * move.bom_line_id.product_qty + for move_line in move.move_line_ids: + if float_compare(qty_to_consume, 0.0, precision_rounding=move.product_uom.rounding) < 0: + break + to_consume_in_line = min(qty_to_consume, move_line.product_uom_qty) + lines.append({ + 'move_id': move.id, + 'qty_to_consume': to_consume_in_line, + 'qty_done': 0.0, + 'lot_id': move_line.lot_id.id, + 'product_uom_id': move.product_uom.id, + 'product_id': move.product_id.id, + }) + qty_to_consume -= to_consume_in_line + if float_compare(qty_to_consume, 0.0, precision_rounding=move.product_uom.rounding) > 0: + if move.product_id.tracking == 'serial': + while float_compare(qty_to_consume, 0.0, precision_rounding=move.product_uom.rounding) > 0: + lines.append({ + 'move_id': move.id, + 'qty_to_consume': 1, + 'qty_done': 0.0, + 'product_uom_id': move.product_uom.id, + 'product_id': move.product_id.id, + }) + qty_to_consume -= 1 + else: lines.append({ 'move_id': move.id, - 'product_qty': min(1,qty), + 'qty_to_consume': qty_to_consume, 'qty_done': 0.0, 'product_uom_id': move.product_uom.id, 'product_id': move.product_id.id, - 'production_id': production.id, - 'location_id': move.location_id.id, - 'location_dest_id': move.location_dest_id.id, }) - qty -= 1 - else: - lines.append({ - 'move_id': move.id, - 'product_qty': qty, - 'qty_done': 0.0, - 'product_uom_id': move.product_uom.id, - 'product_id': move.product_id.id, - 'production_id': production.id, - 'location_id': move.location_id.id, - 'location_dest_id': move.location_dest_id.id, - }) - else: - existing_lines += move.move_line_ids.filtered(lambda x: not x.lot_produced_id).ids - res['serial'] = serial - res['production_id'] = production.id - res['product_qty'] = quantity - res['product_id'] = production.product_id.id - res['product_uom_id'] = production.product_uom_id.id - res['consume_line_ids'] = (existing_lines and [(6, 0, [x for x in existing_lines])] or []) + [(0, 0, x) for x in lines] + res['produce_line_ids'] = [(0, 0, x) for x in lines] return res serial = fields.Boolean('Requires Serial') @@ -72,7 +80,7 @@ class MrpProductProduce(models.TransientModel): product_qty = fields.Float(string='Quantity', digits=dp.get_precision('Product Unit of Measure'), required=True) product_uom_id = fields.Many2one('product.uom', 'Unit of Measure') lot_id = fields.Many2one('stock.production.lot', string='Lot') - consume_line_ids = fields.Many2many('stock.move.line', 'mrp_produce_stock_move_line', string='Product to Track') + produce_line_ids = fields.One2many('mrp.product.produce.line', 'product_produce_id', string='Product to Track') product_tracking = fields.Selection(related="product_id.tracking") @api.multi @@ -104,14 +112,13 @@ class MrpProductProduce(models.TransientModel): @api.multi def check_finished_move_lots(self): - packs = self.env['stock.move.line'] produce_move = self.production_id.move_finished_ids.filtered(lambda x: x.product_id == self.product_id and x.state not in ('done', 'cancel')) if produce_move and produce_move.product_id.tracking != 'none': if not self.lot_id: raise UserError(_('You need to provide a lot for the finished product')) existing_move_line = produce_move.move_line_ids.filtered(lambda x: x.lot_id == self.lot_id) if existing_move_line: - existing_move_line.product_qty += self.product_qty + existing_move_line.product_uom_qty += self.product_qty existing_move_line.qty_done += self.product_qty else: vals = { @@ -125,18 +132,48 @@ class MrpProductProduce(models.TransientModel): 'location_id': produce_move.location_id.id, 'location_dest_id': produce_move.location_dest_id.id, } - packs.create(vals) - for move in self.production_id.move_raw_ids: - for moveline in move.move_line_ids.filtered(lambda x: not x.lot_produced_id): - if moveline.qty_done and self.lot_id: - #Possibly the entire move is selected - remaining_qty = moveline.product_uom_qty - moveline.qty_done - if remaining_qty > 0: - default = {'product_uom_qty': moveline.qty_done, - 'qty_done': moveline.qty_done, - 'lot_produced_id': self.lot_id.id} - new_move_line = moveline.copy(default=default) - moveline.with_context(bypass_reservation_update=True).write({'product_uom_qty': remaining_qty, 'qty_done': 0}) - else: - moveline.write({'lot_produced_id': self.lot_id.id}) + self.env['stock.move.line'].create(vals) + + for pl in self.produce_line_ids: + if pl.qty_done and pl.lot_id: + ml = pl.move_id.move_line_ids.filtered(lambda ml: ml.lot_id == pl.lot_id and not ml.lot_produced_id) + if ml: + if (ml.qty_done + pl.qty_done) >= ml.product_uom_qty: + ml.write({'qty_done': ml.qty_done + pl.qty_done, 'lot_produced_id': self.lot_id.id}) + else: + new_qty_todo = ml.product_uom_qty - (ml.qty_done + pl.qty_done) + default = {'product_uom_qty': ml.qty_done + pl.qty_done, + 'qty_done': ml.qty_done + pl.qty_done, + 'lot_produced_id': self.lot_id.id} + ml.copy(default=default) + ml.with_context(bypass_reservation_update=True).write({'product_uom_qty': new_qty_todo, 'qty_done': 0}) + else: + self.env['stock.move.line'].create({ + 'move_id': pl.move_id.id, + 'product_id': pl.product_id.id, + 'location_id': pl.move_id.location_id.id, + 'location_dest_id': pl.move_id.location_dest_id.id, + 'product_uom_qty': 0, + 'product_uom_id': pl.product_uom_id.id, + 'qty_done': pl.qty_done, + 'lot_id': pl.lot_id.id, + 'lot_produced_id': self.lot_id.id, + }) return True + +class MrpProductProduceLine(models.TransientModel): + _name = "mrp.product.produce.line" + _description = "Record Production Line" + + product_produce_id = fields.Many2one('mrp.product.produce') + product_id = fields.Many2one('product.product', 'Product') + lot_id = fields.Many2one('stock.production.lot', 'Lot') + qty_to_consume = fields.Float('To Consume') + product_uom_id = fields.Many2one('product.uom', 'Unit of Measure') + qty_done = fields.Float('Done') + move_id = fields.Many2one('stock.move') + + @api.onchange('lot_id') + def _onchange_lot_id(self): + if self.product_id.tracking == 'serial': + self.qty_done = 1 diff --git a/addons/mrp/wizard/mrp_product_produce_views.xml b/addons/mrp/wizard/mrp_product_produce_views.xml index ddcde4b3065..7f1dc53fea6 100644 --- a/addons/mrp/wizard/mrp_product_produce_views.xml +++ b/addons/mrp/wizard/mrp_product_produce_views.xml @@ -14,20 +14,20 @@