Skip to content

Commit

Permalink
[FIX] sale_stock_margin: convert purchase_price
Browse files Browse the repository at this point in the history
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#73545

Signed-off-by: Rémy Voet <[email protected]>
  • Loading branch information
adwid committed Jul 13, 2021
1 parent 3741e14 commit 04e4cd9
Show file tree
Hide file tree
Showing 2 changed files with 54 additions and 3 deletions.
14 changes: 11 additions & 3 deletions addons/sale_stock_margin/models/sale_order_line.py
Original file line number Diff line number Diff line change
@@ -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):
Expand All @@ -14,7 +14,15 @@ def _compute_purchase_price(self):
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()
43 changes: 43 additions & 0 deletions addons/sale_stock_margin/tests/test_sale_stock_margin.py
Original file line number Diff line number Diff line change
@@ -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 #
#########
Expand All @@ -15,6 +23,7 @@ def _create_sale_order(self):
'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):
Expand Down Expand Up @@ -156,3 +165,37 @@ def test_sale_stock_margin_5(self):
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)

0 comments on commit 04e4cd9

Please sign in to comment.