From ad253ef4c2cb06536b99bb919a3e01ed980d2e96 Mon Sep 17 00:00:00 2001 From: Djamel Touati Date: Thu, 14 Sep 2023 10:10:35 +0200 Subject: [PATCH] [FIX] purchase: set the correct price in alternative PO MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Steps to reproduce the bug: - Create a storable product “P1”: - Price at “Wood corner”: - $5, min qty =1 - Price at “Open wood: - $6, min qty = 1 - $4, min qty = 10 - Create a purchase order at wood corner: - 100 units > the price is automatically computed at $500 - Go to Alternative tab in the PO: - Create a one for “Openwood” and copy lines Problem: The price for 100 units is computed as $600 instead of $400. When we create the alternative purchase order, in the "create" function of “purchase.order.line,” we receive only the quantity as "qty=100" in the values. Therefore, the missing fields should be prepared. https://github.com/odoo/odoo/blob/a9cbd2a2ae2e21f1ba14379aecd908c44497b8ab/addons/purchase/models/purchase.py#L1102 https://github.com/odoo/odoo/blob/a9cbd2a2ae2e21f1ba14379aecd908c44497b8ab/addons/purchase/models/purchase.py#L1102 First, the line is created with the received values (“qty=100”), and the price_unit is computed correctly as $4. https://github.com/odoo/odoo/blob/a9cbd2a2ae2e21f1ba14379aecd908c44497b8ab/addons/purchase/models/purchase.py#L1400 So, the price_unit is computed correctly at $4 However, the “onchange_product_id” function is then called, resetting the price_unit and product_qty to 0. https://github.com/odoo/odoo/blob/a9cbd2a2ae2e21f1ba14379aecd908c44497b8ab/addons/purchase/models/purchase.py#L1401 https://github.com/odoo/odoo/blob/a9cbd2a2ae2e21f1ba14379aecd908c44497b8ab/addons/purchase/models/purchase.py#L1176 Afterward, the “_suggest_quantity()” function is called https://github.com/odoo/odoo/blob/a9cbd2a2ae2e21f1ba14379aecd908c44497b8ab/addons/purchase/models/purchase.py#L1180 suggesting a minimal quantity based on the seller (so=1). https://github.com/odoo/odoo/blob/a9cbd2a2ae2e21f1ba14379aecd908c44497b8ab/addons/purchase/models/purchase.py#L1363 Therefore, the price_unit is computed based on qty=1, resulting in $6. Finally, the price is returned because it is the only missing field. opw-3497825 closes odoo/odoo#137774 X-original-commit: 0781198f1b5e5d6a4e2722ea3c663b74fb131bc3 Signed-off-by: Djamel Touati (otd) --- addons/purchase/models/purchase.py | 7 ++-- .../tests/test_purchase_requisition.py | 40 +++++++++++++++++++ 2 files changed, 43 insertions(+), 4 deletions(-) diff --git a/addons/purchase/models/purchase.py b/addons/purchase/models/purchase.py index 8a097d20a69..d36ed65c686 100644 --- a/addons/purchase/models/purchase.py +++ b/addons/purchase/models/purchase.py @@ -1165,7 +1165,8 @@ class PurchaseOrderLine(models.Model): @api.onchange('product_id') def onchange_product_id(self): - if not self.product_id: + # TODO: Remove when onchanges are replaced with computes + if not self.product_id or (self.env.context.get('origin_po_id') and self.product_qty): return # Reset date, price and quantity since _onchange_quantity will provide default values @@ -1179,9 +1180,7 @@ class PurchaseOrderLine(models.Model): if not self.product_id: return - # TODO: Remove when onchanges are replaced with computes - if not (self.env.context.get('origin_po_id') and self.product_uom and self.product_id.uom_id.category_id == self.product_uom_category_id): - self.product_uom = self.product_id.uom_po_id or self.product_id.uom_id + self.product_uom = self.product_id.uom_po_id or self.product_id.uom_id product_lang = self.product_id.with_context( lang=get_lang(self.env, self.partner_id.lang).code, partner_id=self.partner_id.id, diff --git a/addons/purchase_requisition/tests/test_purchase_requisition.py b/addons/purchase_requisition/tests/test_purchase_requisition.py index f6ffa731459..e18a6d78cfe 100644 --- a/addons/purchase_requisition/tests/test_purchase_requisition.py +++ b/addons/purchase_requisition/tests/test_purchase_requisition.py @@ -431,3 +431,43 @@ class TestPurchaseRequisition(TestPurchaseRequisitionCommon): # alt_po is cheaper than orig_po self.assertEqual(best_price_ids[0], po_alt.order_line.id) self.assertEqual(best_price_unit_ids[0], po_alt.order_line.id) + + def test_alternative_po_with_multiple_price_list(self): + vendor_a = self.env["res.partner"].create({ + "name": "Supplier A", + }) + vendor_b = self.env["res.partner"].create({ + "name": "Supplier B", + }) + product = self.env['product.product'].create({ + 'name': 'Product', + 'seller_ids': [(0, 0, { + 'partner_id': vendor_a.id, + 'price': 5, + }), (0, 0, { + 'partner_id': vendor_b.id, + 'price': 4, + 'min_qty': 10, + }), (0, 0, { + 'partner_id': vendor_b.id, + 'price': 6, + 'min_qty': 1, + }), + ] + }) + po_form = Form(self.env['purchase.order']) + po_form.partner_id = vendor_a + with po_form.order_line.new() as line: + line.product_id = product + line.product_qty = 100 + po_orig = po_form.save() + self.assertEqual(po_orig.order_line.price_unit, 5) + # Creates an alternative PO + action = po_orig.action_create_alternative() + alt_po_wizard_form = Form(self.env['purchase.requisition.create.alternative'].with_context(**action['context'])) + alt_po_wizard_form.partner_id = vendor_b + alt_po_wizard_form.copy_products = True + alt_po_wizard = alt_po_wizard_form.save() + alt_po_wizard.action_create_alternative() + po_alt = po_orig.alternative_po_ids - po_orig + self.assertEqual(po_alt.order_line.price_unit, 4)