From c45272828ee85bc23dd97dc93dc741f8f59a5305 Mon Sep 17 00:00:00 2001 From: "Touati Djamel (otd)" Date: Mon, 11 Sep 2023 14:57:57 +0200 Subject: [PATCH] [FIX] purchase_requisition: convert purchase price to current currency MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bug introduced in: https://github.com/odoo/odoo/pull/128327/commits/8f96e08985ebac3309745f1036a5e83d287e6382 Steps to reproduce the bug: - Activate 2 currencies (assume EUR and USD, conversion rate: 0.65 EUR = 1 USD) - Create new product “P1”: - Purchase tab: - add two vendors: - Vendor_USD, currency = USD, price = 100 - Vendor_EUR, currency = EUR, price = 80 - Create PO: - vendor = vendor_EUR - Currency = Eur - Add P1, Unit price should be 80 - Alternative tab > Create alternative > Vendor = Vendor_EUR - Compare product lines Issue: The line with price = 100 USD is highlighted as being the cheapest option, but if we apply conversion rules, 80 EUR = 123,07 USD > 100 USD. Solution: Before selecting the cheapest line, we convert the prices of the purchase order lines that are in a currency other than the current currency of the company. opw-3378253 closes odoo/odoo#135037 Signed-off-by: Tiffany Chang (tic) --- .../purchase_requisition/models/purchase.py | 8 +-- .../tests/test_purchase_requisition.py | 50 ++++++++++++------- 2 files changed, 37 insertions(+), 21 deletions(-) diff --git a/addons/purchase_requisition/models/purchase.py b/addons/purchase_requisition/models/purchase.py index cf0bea136f6ef..2afd6da323a96 100644 --- a/addons/purchase_requisition/models/purchase.py +++ b/addons/purchase_requisition/models/purchase.py @@ -235,10 +235,10 @@ def get_tender_best_lines(self): current_price_subtotal = product_to_best_price_line[line.product_id][0].price_subtotal current_price_unit = product_to_best_price_unit[line.product_id][0].price_unit if multiple_currencies: - price_subtotal *= line.order_id.currency_rate - price_unit *= line.order_id.currency_rate - current_price_subtotal *= product_to_best_price_line[line.product_id][0].order_id.currency_rate - current_price_unit *= product_to_best_price_unit[line.product_id][0].order_id.currency_rate + price_subtotal /= line.order_id.currency_rate + price_unit /= line.order_id.currency_rate + current_price_subtotal /= product_to_best_price_line[line.product_id][0].order_id.currency_rate + current_price_unit /= product_to_best_price_unit[line.product_id][0].order_id.currency_rate if current_price_subtotal > price_subtotal: product_to_best_price_line[line.product_id] = line diff --git a/addons/purchase_requisition/tests/test_purchase_requisition.py b/addons/purchase_requisition/tests/test_purchase_requisition.py index 693ece76fa113..61ac488643294 100644 --- a/addons/purchase_requisition/tests/test_purchase_requisition.py +++ b/addons/purchase_requisition/tests/test_purchase_requisition.py @@ -7,7 +7,10 @@ from datetime import timedelta +from odoo.tests.common import tagged + +@tagged('post_install', '-at_install') class TestPurchaseRequisition(TestPurchaseRequisitionCommon): def test_00_purchase_requisition_users(self): @@ -376,30 +379,51 @@ def test_12_alternative_po_line_different_currency(self): 'currency_id': self.env.ref('base.EUR').id, 'rate': 0.5, }]) + vendor_usd = self.env["res.partner"].create({ + "name": "Supplier A", + }) + vendor_eur = self.env["res.partner"].create({ + "name": "Supplier B", + }) + product = self.env['product.product'].create({ + 'name': 'Product', + 'seller_ids': [(0, 0, { + 'partner_id': vendor_usd.id, + 'price': 100, + 'currency_id': currency_usd.id, + }), (0, 0, { + 'partner_id': vendor_eur.id, + 'price': 80, + 'currency_id': currency_eur.id, + })] + }) po_form = Form(self.env['purchase.order']) - po_form.partner_id = self.res_partner_1 - po_form.currency_id = currency_usd + po_form.partner_id = vendor_eur + po_form.currency_id = currency_eur with po_form.order_line.new() as line: - line.product_id = self.product_09 + line.product_id = product line.product_qty = 1 - line.price_unit = 10 po_orig = po_form.save() + self.assertEqual(po_orig.order_line.price_unit, 80) + self.assertEqual(po_orig.currency_id, currency_eur) # 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 = self.res_partner_1 + alt_po_wizard_form.partner_id = vendor_usd 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 - po_alt.currency_id = currency_eur - po_alt.order_line.price_unit = 12 - # po_alt has cheaper price_unit/price_subtotal after conversion USD -> EUR - # 12 USD = 12 * 0.5 = 6 EUR < 10 EUR + # Ensure that the currency in the alternative purchase order is set to USD + # because, in some case, the company's default currency is EUR. + self.assertEqual(po_alt.currency_id, currency_usd) + self.assertEqual(po_alt.order_line.price_unit, 100) + # po_alt has cheaper price_unit/price_subtotal after conversion USD -> EUR + # 80 / 0.5 = 160 USD > 100 EUR best_price_ids, best_date_ids, best_price_unit_ids = po_orig.get_tender_best_lines() self.assertEqual(len(best_price_ids), 1) # Equal dates @@ -408,11 +432,3 @@ def test_12_alternative_po_line_different_currency(self): # 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) - - po_alt.order_line.price_unit = 20 - # po_alt has same price_unit/price_subtotal after conversion USD -> EUR - # 20 USD = 20 * 0.5 = 10 EUR - best_price_ids, best_date_ids, best_price_unit_ids = po_orig.get_tender_best_lines() - self.assertEqual(len(best_price_ids), 2) - self.assertEqual(len(best_date_ids), 2) - self.assertEqual(len(best_price_unit_ids), 2)