diff --git a/addons/purchase_mrp/models/purchase_mrp.py b/addons/purchase_mrp/models/purchase_mrp.py index 50c30727c6ec67955fe5c5f3f4261204869fb203..7facf43defdf7a50343a696a2d02ab630929cd43 100644 --- a/addons/purchase_mrp/models/purchase_mrp.py +++ b/addons/purchase_mrp/models/purchase_mrp.py @@ -39,6 +39,17 @@ class PurchaseOrderLine(models.Model): def _get_upstream_documents_and_responsibles(self, visited): return [(self.order_id, self.order_id.user_id, visited)] + def _get_qty_procurement(self): + self.ensure_one() + # Specific case when we change the qty on a PO for a kit product. + # We don't try to be too smart and keep a simple approach: we compare the quantity before + # and after update, and return the difference. We don't take into account what was already + # sent, or any other exceptional case. + bom = self.env['mrp.bom']._bom_find(product=self.product_id) + if bom and bom.type == 'phantom' and 'previous_product_qty' in self.env.context: + return self.env.context['previous_product_qty'].get(self.id, 0.0) + return super()._get_qty_procurement() + class StockMove(models.Model): _inherit = 'stock.move' diff --git a/addons/purchase_mrp/tests/test_purchase_mrp_flow.py b/addons/purchase_mrp/tests/test_purchase_mrp_flow.py index 972d1bf79056474a66880997fe693d1d7f6d7988..f8479c7c8d98e371d890fd505b32e8c08596ec87 100644 --- a/addons/purchase_mrp/tests/test_purchase_mrp_flow.py +++ b/addons/purchase_mrp/tests/test_purchase_mrp_flow.py @@ -94,3 +94,48 @@ class TestPurchaseMrpFlow(common.TransactionCase): aml = self.invoice.move_id.line_ids aml_input = aml.filtered(lambda l: l.account_id.id == self.account_input.id) self.assertEqual(aml_input.debit, 180, "Cost of Good Sold entry missing or mismatching") + + def test_01_purchase_mrp_kit_qty_change(self): + self.partner = self.env.ref('base.res_partner_1') + self.categ_unit = self.env.ref('uom.product_uom_categ_unit') + self.uom_unit = self.env['uom.uom'].search([('category_id', '=', self.categ_unit.id), ('uom_type', '=', 'reference')], limit=1) + Product = self.env['product.product'] + self.kit_product = Product.create({ + 'name': 'product K', + 'type': 'consu', + 'uom_id': self.uom_unit.id, + }) + self.component1 = Product.create({ + 'name': 'Component 1', + 'type': 'product',}) + self.component2 = Product.create({ + 'name': 'Component 2', + 'type': 'product',}) + self.bom = self.env['mrp.bom'].create({ + 'product_tmpl_id': self.kit_product.product_tmpl_id.id, + 'product_qty': 1.0, + 'type': 'phantom'}) + BomLine = self.env['mrp.bom.line'] + BomLine.create({ + 'product_id': self.component1.id, + 'product_qty': 2.0, + 'bom_id': self.bom.id}) + BomLine.create({ + 'product_id': self.component2.id, + 'product_qty': 1.0, + 'bom_id': self.bom.id}) + # Create a PO with one unit of the kit product + self.po = self.env['purchase.order'].create({ + 'partner_id': self.partner.id, + 'order_line': [(0, 0, {'name': self.kit_product.name, 'product_id': self.kit_product.id, 'product_qty': 1, 'product_uom': self.kit_product.uom_id.id, 'price_unit': 60.0, 'date_planned': fields.Datetime.now()})], + }) + # Validate the PO + self.po.button_confirm() + # Check the component qty in the created picking + self.assertEqual(self.po.picking_ids.move_ids_without_package[0].product_uom_qty,2, "The quantity of components must be created according to the BOM") + self.assertEqual(self.po.picking_ids.move_ids_without_package[1].product_uom_qty,1, "The quantity of components must be created according to the BOM") + + # Update the quantity of the kit in the PO + self.po.order_line[0].product_qty = 2 + self.assertEqual(self.po.picking_ids.move_ids_without_package[0].product_uom_qty,4, "The amount of the kit components must be updated when changing the quantity of the kit.") + self.assertEqual(self.po.picking_ids.move_ids_without_package[1].product_uom_qty,2, "The amount of the kit components must be updated when changing the quantity of the kit.") diff --git a/addons/purchase_stock/models/purchase.py b/addons/purchase_stock/models/purchase.py index 5e0f1d1a34c1deb829a095c8852f937b9416dd4e..e9316598d35a0bae3d5feb2461c0c74aae3f9168 100644 --- a/addons/purchase_stock/models/purchase.py +++ b/addons/purchase_stock/models/purchase.py @@ -231,6 +231,8 @@ class PurchaseOrderLine(models.Model): @api.multi def write(self, values): + lines = self.filtered(lambda l: l.order_id.state == 'purchase') + previous_product_qty = {line.id: line.product_uom_qty for line in lines} result = super(PurchaseOrderLine, self).write(values) # Update expected date of corresponding moves if 'date_planned' in values: @@ -238,7 +240,7 @@ class PurchaseOrderLine(models.Model): ('purchase_line_id', 'in', self.ids), ('state', '!=', 'done') ]).write({'date_expected': values['date_planned']}) if 'product_qty' in values: - self.filtered(lambda l: l.order_id.state == 'purchase')._create_or_update_picking() + lines.with_context(previous_product_qty=previous_product_qty)._create_or_update_picking() return result # -------------------------------------------------- @@ -304,10 +306,9 @@ class PurchaseOrderLine(models.Model): res = [] if self.product_id.type not in ['product', 'consu']: return res - qty = 0.0 price_unit = self._get_stock_move_price_unit() - for move in self.move_ids.filtered(lambda x: x.state != 'cancel' and not x.location_dest_id.usage == "supplier"): - qty += move.product_uom._compute_quantity(move.product_uom_qty, self.product_uom, rounding_method='HALF-UP') + qty = self._get_qty_procurement() + template = { # truncate to 2000 to avoid triggering index limit error # TODO: remove index in master? @@ -346,6 +347,13 @@ class PurchaseOrderLine(models.Model): res.append(template) return res + def _get_qty_procurement(self): + self.ensure_one() + qty = 0.0 + for move in self.move_ids.filtered(lambda x: x.state != 'cancel' and not x.location_dest_id.usage == "supplier"): + qty += move.product_uom._compute_quantity(move.product_uom_qty, self.product_uom, rounding_method='HALF-UP') + return qty + @api.multi def _create_stock_moves(self, picking): values = []