From 0ebbc8f1d5d274b7e6e01948a8efaabba5357239 Mon Sep 17 00:00:00 2001 From: svs-odoo Date: Fri, 7 Oct 2022 12:06:14 +0000 Subject: [PATCH] [FIX] purchase(_stock): don't recompute price unit Before this commit, when the quantity of a PO line is changed, its unit price is recomputed, which means it will be set to 0 if there is no compatible vendor price for the selected partner/product. It can be annoying because if the price unit was set manually by the user, this info is lost. To avoid that, we don't recompute the unit price if the PO line matches the following conditions: - The PO line has already a price unit; - There is actually no vendor price list for the partner (in such case, we prefer to avoid to stick to a price who doesn't match that list); - The UoM wasn't changed, because if the UoM was changed, the price should obviously recomputed (the price unit for 1 unit is not the same than for 1 dozen). How to reproduce: - Install Stock and Purchase modules; - In purchase settings, enable "Purchase Agreements"; - Create a product and in its vendor price list (in "Purchase" tab), add a price for Partner A (e.g. 10$); - Create a PO for Partner B, add a PO line for the created product and set the `price_unit` higher than the created vendor price (e.g. 15$); - Confirm the order and go in the "Alternatives" tab; - Click on "Create Alternative" and select Partner A as the vendor; - Click on "Compare Product Lines" and choose the line for Partner A; - Go back in the first created PO (with Partner B as vendor) -> the PO line's quantity is to 0, which is good, but its unit price is also set to 0 which is annoying. Additionnaly, in `_get_stock_move_price_unit`, `float_round` the value just before it was returned because previously, the rouding was done before the multiplication by the UoM's factor which can cause some rounding issue if the rouding is not done after that. closes odoo/odoo#104206 X-original-commit: f5a24b10cb3a4cf32c6c185df65f3099c8da3ff1 Signed-off-by: Tiffany Chang Signed-off-by: Steve Van Essche --- addons/purchase/models/purchase.py | 6 ++++ .../tests/test_purchase_requisition.py | 32 +++++++++++++++++++ addons/purchase_stock/models/purchase.py | 4 +-- 3 files changed, 40 insertions(+), 2 deletions(-) diff --git a/addons/purchase/models/purchase.py b/addons/purchase/models/purchase.py index 762163be252..4bebaaabbaa 100644 --- a/addons/purchase/models/purchase.py +++ b/addons/purchase/models/purchase.py @@ -1207,6 +1207,12 @@ class PurchaseOrderLine(models.Model): # If not seller, use the standard price. It needs a proper currency conversion. if not seller: + unavailable_seller = line.product_id.seller_ids.filtered( + lambda s: s.partner_id == line.order_id.partner_id) + if not unavailable_seller and line.price_unit and line.product_uom == line._origin.product_uom: + # Avoid to modify the price unit if there is no price list for this partner and + # the line has already one to avoid to override unit price set manually. + continue po_line_uom = line.product_uom or line.product_id.uom_po_id price_unit = line.env['account.tax']._fix_tax_included_price_company( line.product_id.uom_id._compute_price(line.product_id.standard_price, po_line_uom), diff --git a/addons/purchase_requisition/tests/test_purchase_requisition.py b/addons/purchase_requisition/tests/test_purchase_requisition.py index 03f23ecc25c..7ae68d4ade1 100644 --- a/addons/purchase_requisition/tests/test_purchase_requisition.py +++ b/addons/purchase_requisition/tests/test_purchase_requisition.py @@ -217,3 +217,35 @@ class TestPurchaseRequisition(TestPurchaseRequisitionCommon): groups = self.env['purchase.order.group'].search([('order_ids', 'in', pos.ids)]) self.assertEqual(len(po_5.alternative_po_ids), 0, "Last PO should auto unlink from itself since group should have auto-deleted") self.assertEqual(len(groups), 0, "The group should have auto-deleted") + + def test_09_alternative_po_line_price_unit(self): + """Checks PO line's `price_unit` is keep even if a line from an + alternative is chosen and thus the PO line's quantity was set to 0. """ + # Creates a first Purchase Order. + po_form = Form(self.env['purchase.order']) + po_form.partner_id = self.res_partner_1 + with po_form.order_line.new() as line: + line.product_id = self.product_09 + line.product_qty = 1 + line.price_unit = 16 + po_1 = po_form.save() + + # Creates an alternative PO. + action = po_1.action_create_alternative() + alt_po_wizard_form = Form(self.env['purchase.requisition.create.alternative'].with_context(**action['context'])) + alt_po_wizard_form.partner_id = self.res_partner_1 + alt_po_wizard_form.copy_products = True + alt_po_wizard = alt_po_wizard_form.save() + alt_po_wizard.action_create_alternative() + + # Set a lower price on the alternative and choses this PO line. + po_2 = po_1.alternative_po_ids - po_1 + po_2.order_line.price_unit = 12 + po_2.order_line.action_choose() + + self.assertEqual( + po_1.order_line.product_uom_qty, 0, + "Line's quantity from the original PO should be reset to 0") + self.assertEqual( + po_1.order_line.price_unit, 16, + "Line's unit price from the original PO shouldn't be changed") diff --git a/addons/purchase_stock/models/purchase.py b/addons/purchase_stock/models/purchase.py index cb32d841d68..82148aebca7 100644 --- a/addons/purchase_stock/models/purchase.py +++ b/addons/purchase_stock/models/purchase.py @@ -453,13 +453,13 @@ class PurchaseOrderLine(models.Model): price_unit = self.taxes_id.with_context(round=False).compute_all( price_unit, currency=self.order_id.currency_id, quantity=qty, product=self.product_id, partner=self.order_id.partner_id )['total_void'] - price_unit = float_round(price_unit / qty, precision_digits=price_unit_prec) + price_unit = price_unit / qty if self.product_uom.id != self.product_id.uom_id.id: price_unit *= self.product_uom.factor / self.product_id.uom_id.factor if order.currency_id != order.company_id.currency_id: price_unit = order.currency_id._convert( price_unit, order.company_id.currency_id, self.company_id, self.date_order or fields.Date.today(), round=False) - return price_unit + return float_round(price_unit, precision_digits=price_unit_prec) def _prepare_stock_moves(self, picking): """ Prepare the stock moves data for one order line. This function returns a list of