From 063f7af8adcdb0c87b6e4de08bc81c6e6f4696eb Mon Sep 17 00:00:00 2001
From: "Nasreddin Boulif (bon)" <bon@odoo.com>
Date: Thu, 25 Aug 2022 21:08:40 +0000
Subject: [PATCH] [FIX] purchase: Update price unit on new line even if
 confirmed RFQ

Steps to reproduce:

  - Install purchase
  - Go to Settings and activate `Variant Grid Entry`
  - Create a new Requests for Quotation
  - Add a customer and add a product that has a variant min 2 variant
  - Wizard should ask for the variant
  - Select 1 variant by increasing quantity in the grid and confirm
  - Confirm order
  - Add again a product variant with the wizard

Issue:

  Price unit is not set on the new line.

Cause:

  In `_onchange_quantity` (triggered by the purchase_product_matrix
  module), we do not update price unit if order line
  is in state `purchase` or `done`.

Solution:

  Replace condition to not perform `_onchange_quantity` if order line
  has an invoice line.

opw-2956755

closes odoo/odoo#98960

Signed-off-by: Adrien Widart <awt@odoo.com>
---
 addons/purchase/models/purchase.py     |  2 +-
 addons/purchase/tests/test_purchase.py | 77 ++++++++++++++++++++++++++
 2 files changed, 78 insertions(+), 1 deletion(-)

diff --git a/addons/purchase/models/purchase.py b/addons/purchase/models/purchase.py
index 0fc49ac61322..569b532c5f6b 100644
--- a/addons/purchase/models/purchase.py
+++ b/addons/purchase/models/purchase.py
@@ -701,7 +701,7 @@ class PurchaseOrderLine(models.Model):
 
     @api.onchange('product_qty', 'product_uom')
     def _onchange_quantity(self):
-        if not self.product_id or self.state in ('purchase', 'done'):
+        if not self.product_id or self.invoice_lines:
             return
         params = {'order_id': self.order_id}
         seller = self.product_id._select_seller(
diff --git a/addons/purchase/tests/test_purchase.py b/addons/purchase/tests/test_purchase.py
index 3edfe742146a..068d99914fc0 100644
--- a/addons/purchase/tests/test_purchase.py
+++ b/addons/purchase/tests/test_purchase.py
@@ -3,6 +3,7 @@
 
 from datetime import timedelta
 
+from odoo.addons.account.tests.account_test_savepoint import AccountTestInvoicingCommon
 from odoo.tests.common import SavepointCase
 from odoo.tests import Form
 
@@ -137,3 +138,79 @@ class TestPurchase(SavepointCase):
         pol.name = "New custom description"
         pol.product_qty += 1
         self.assertEqual(pol.name, "New custom description")
+
+class TestPurchaseAccount(AccountTestInvoicingCommon):
+    @classmethod
+    def setUpClass(cls, chart_template_ref=None):
+        super().setUpClass(chart_template_ref=chart_template_ref)
+
+        cls.product_consu = cls.env['product.product'].create({
+            'name': 'Product A',
+            'type': 'consu',
+        })
+        cls.product_consu2 = cls.env['product.product'].create({
+            'name': 'Product B',
+            'type': 'consu',
+        })
+        cls.vendor = cls.env['res.partner'].create({'name': 'vendor1'})
+        cls.uom_unit = cls.env.ref('uom.product_uom_unit')
+
+    def test_on_change_quantity_price_unit(self):
+        """ When a user changes the quantity of a product in a purchase order it
+        should only update the unit price if PO line has no invoice line. """
+
+        supplierinfo_vals = {
+            'name': self.vendor.id,
+            'price': 10.0,
+            'min_qty': 1,
+            "product_id": self.product_consu.id,
+            "product_tmpl_id": self.product_consu.product_tmpl_id.id,
+        }
+
+        supplierinfo = self.env["product.supplierinfo"].create(supplierinfo_vals)
+        po_form = Form(self.env['purchase.order'])
+        po_form.partner_id = self.vendor
+        with po_form.order_line.new() as po_line_form:
+            po_line_form.product_id = self.product_consu
+            po_line_form.product_qty = 1
+        po = po_form.save()
+        po_line = po.order_line[0]
+
+        self.assertEqual(10.0, po_line.price_unit, "Unit price should be set to 10.0 for 1 quantity")
+
+        # Ensure price unit is updated when changing quantity on a un-confirmed PO
+        supplierinfo.write({'min_qty': 2, 'price': 20.0})
+        po_line.write({'product_qty': 2})
+        po_line._onchange_quantity()
+        self.assertEqual(20.0, po_line.price_unit, "Unit price should be set to 20.0 for 2 quantity")
+
+        po.button_confirm()
+
+        # Ensure price unit is updated when changing quantity on a confirmed PO
+        supplierinfo.write({'min_qty': 3, 'price': 30.0})
+        po_line.write({'product_qty': 3})
+        po_line._onchange_quantity()
+        self.assertEqual(30.0, po_line.price_unit, "Unit price should be set to 30.0 for 3 quantity")
+
+        action = po.action_view_invoice()
+        invoice_form = Form(self.env['account.move'].with_context(action['context']))
+        invoice_form.save()
+
+        # Ensure price unit is NOT updated when changing quantity on PO confirmed and line linked to an invoice line
+        supplierinfo.write({'min_qty': 4, 'price': 40.0})
+        po_line.write({'product_qty': 4})
+        po_line._onchange_quantity()
+        self.assertEqual(30.0, po_line.price_unit, "Unit price should be set to 30.0 for 3 quantity")
+
+        with po_form.order_line.new() as po_line_form:
+            po_line_form.product_id = self.product_consu
+            po_line_form.product_qty = 1
+        po = po_form.save()
+        po_line = po.order_line[1]
+
+        self.assertEqual(0.0, po_line.price_unit, "Unit price should be reset to 0 since the supplier supplies minimum of 4 quantities")
+
+        # Ensure price unit is updated when changing quantity on PO confirmed and line NOT linked to an invoice line
+        po_line.write({'product_qty': 4})
+        po_line._onchange_quantity()
+        self.assertEqual(40.0, po_line.price_unit, "Unit price should be set to 40.0 for 4 quantity")
-- 
GitLab