[FIX] sale_stock_margin: convert purchase_price

When confirming a SO, if the costing method of a product's category is
not `Standard' and if the SO's currency is different from the company's
currency, the cost of the product won't be converted to the SO's
currency

To reproduce the error:
1. In Settings, enable:
    - Margins
    - Multi-Currencies
2. Create a product category PC:
    - Costing Method: FIFO
3. Create a product P:
    - Product Type: Storable
    - Product Category: PC
    - Sales Price: 200
    - Cost: 100
4. Update P's quantity to 1
5. Create a pricelist PL:
    - Currency: EUR
6. Create a SO:
    - Pricelist: PL
    - Order Lines:
        - 1 x P
7. Add field "Cost" to the tree view of the order lines
    - The value is correctly converted
8. Confirm the SO

Error: The cost of the order line is now 100€. This is actually the USD
cost, it should be converted

This code applies the same conversion as when the cost method is
standard:
https://github.com/odoo/odoo/blob/8a8ff03f6111f377bcd9c2b0f584c450b82a4182/addons/sale_margin/models/sale_order.py#L42-L48

OPW-2563442

closes odoo/odoo#73786

X-original-commit: 04e4cd91637c836b4d856b8638d88c5cf1770421
Signed-off-by: Rémy Voet <ryv-odoo@users.noreply.github.com>
Signed-off-by: Adrien Widart <adwid@users.noreply.github.com>
This commit is contained in:
Adrien Widart
2021-07-15 15:11:18 +00:00
parent 6e04057401
commit fdd86433c6
2 changed files with 54 additions and 3 deletions
@@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import api, models
from odoo import api, fields, models
class SaleOrderLine(models.Model):
@@ -14,7 +14,15 @@ class SaleOrderLine(models.Model):
if not line.move_ids:
lines_without_moves |= line
elif line.product_id.categ_id.property_cost_method != 'standard':
line.purchase_price = line.product_id.with_company(line.company_id)._compute_average_price(0, line.product_uom_qty, line.move_ids)
purch_price = line.product_id.with_company(line.company_id)._compute_average_price(0, line.product_uom_qty, line.move_ids)
if line.product_uom and line.product_uom != line.product_id.uom_id:
line.purchase_price = line.product_id.uom_id._compute_price(line.purchase_price, line.product_uom)
purch_price = line.product_id.uom_id._compute_price(purch_price, line.product_uom)
to_cur = line.currency_id or line.order_id.currency_id
line.purchase_price = line.product_id.cost_currency_id._convert(
from_amount=purch_price,
to_currency=to_cur,
company=line.company_id or self.env.company,
date=line.order_id.date_order or fields.Date.today(),
round=False,
) if to_cur and purch_price else purch_price
return super(SaleOrderLine, lines_without_moves)._compute_purchase_price()
@@ -1,11 +1,19 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import fields
from odoo.tests.common import Form
from odoo.addons.stock_account.tests.test_stockvaluationlayer import TestStockValuationCommon
class TestSaleStockMargin(TestStockValuationCommon):
@classmethod
def setUpClass(cls):
super(TestSaleStockMargin, cls).setUpClass()
cls.pricelist = cls.env['product.pricelist'].create({'name': 'Simple Pricelist'})
#########
# UTILS #
#########
@@ -15,6 +23,7 @@ class TestSaleStockMargin(TestStockValuationCommon):
'name': 'Sale order',
'partner_id': self.env.ref('base.partner_admin').id,
'partner_invoice_id': self.env.ref('base.partner_admin').id,
'pricelist_id': self.pricelist.id,
})
def _create_sale_order_line(self, sale_order, product, quantity, price_unit=0):
@@ -156,3 +165,37 @@ class TestSaleStockMargin(TestStockValuationCommon):
self.assertAlmostEqual(order_line_1.margin, 34) # (60 - 43) * 2
self.assertAlmostEqual(order_line_2.margin, 30) # (20 - 12.5) * 4
self.assertAlmostEqual(sale_order.margin, 64)
def test_so_and_multicurrency(self):
ResCurrencyRate = self.env['res.currency.rate']
company_currency = self.env.company.currency_id
other_currency = self.env.ref('base.EUR') if company_currency == self.env.ref('base.USD') else self.env.ref('base.USD')
ResCurrencyRate.create({'currency_id': company_currency.id, 'rate': 1})
other_currency_rate = ResCurrencyRate.search([('name', '=', fields.Date.today()), ('currency_id', '=', other_currency.id)])
if other_currency_rate:
other_currency_rate.rate = 2
else:
ResCurrencyRate.create({'currency_id': other_currency.id, 'rate': 2})
so = self._create_sale_order()
so.pricelist_id = self.env['product.pricelist'].create({
'name': 'Super Pricelist',
'currency_id': other_currency.id,
})
product = self._create_product()
product.standard_price = 100
product.list_price = 200
so_form = Form(so)
with so_form.order_line.new() as line:
line.product_id = product
so = so_form.save()
so_line = so.order_line
self.assertEqual(so_line.purchase_price, 200)
self.assertEqual(so_line.price_unit, 400)
so.action_confirm()
self.assertEqual(so_line.purchase_price, 200)
self.assertEqual(so_line.price_unit, 400)