[FIX] sale_stock_margin: use data of SOL's company
In a multi-company and multi-currency environment, the computation of
the purchase price may be based on the incorrect company (and thus lead
to incorrect results)
To reproduce the issue:
(Use demo data)
1. In Settings:
- Enable "Multi-Currencies"
2. Update "My Company (Chicago)":
- Currency: EUR
3. Edit currencies' rate for all companies(!):
- USD: 1.0
- EUR: 3.0
4. Create a product category PC:
- Costing Method: FIFO
5. Create a product P:
- Type: Storable
- Category: PC
6. Switch to "My Company (Chicago)"
- Note that in this company, the costing method of PC is "Standard
Price"
7. Edit P:
- Cost: 10
8. Create a sale order SO:
- Pricelist: EUR
- Lines (add Cost columns):
- 1 x P (Note that cost is 10€)
9. Confirm SO
Error: The sale order line is incorrect, the cost is now 30€. A rate
conversion has been applied on the amount
In `_compute_purchase_price`, several fields/methods depend on the SOL's
company: `property_cost_method`, `_compute_average_price`,
`cost_currency_id`. Therefore, we should include this specific company
in the environment of the product
OPW-2659265
closes odoo/odoo#79430
X-original-commit: 868142c3885f7bf65a7974066234feb0eb6241ae
Signed-off-by: Steve Van Essche <svs@odoo.com>
Signed-off-by: Adrien Widart <awt@odoo.com>
This commit is contained in:
@@ -11,14 +11,15 @@ class SaleOrderLine(models.Model):
|
||||
def _compute_purchase_price(self):
|
||||
lines_without_moves = self.browse()
|
||||
for line in self:
|
||||
product = line.product_id.with_company(line.company_id)
|
||||
if not line.move_ids:
|
||||
lines_without_moves |= line
|
||||
elif line.product_id.categ_id.property_cost_method != 'standard':
|
||||
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:
|
||||
purch_price = line.product_id.uom_id._compute_price(purch_price, line.product_uom)
|
||||
elif product.categ_id.property_cost_method != 'standard':
|
||||
purch_price = product._compute_average_price(0, line.product_uom_qty, line.move_ids)
|
||||
if line.product_uom and line.product_uom != product.uom_id:
|
||||
purch_price = product.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(
|
||||
line.purchase_price = product.cost_currency_id._convert(
|
||||
from_amount=purch_price,
|
||||
to_currency=to_cur,
|
||||
company=line.company_id or self.env.company,
|
||||
|
||||
@@ -200,3 +200,68 @@ class TestSaleStockMargin(TestStockValuationCommon):
|
||||
so.action_confirm()
|
||||
self.assertEqual(so_line.purchase_price, 200)
|
||||
self.assertEqual(so_line.price_unit, 400)
|
||||
|
||||
def test_so_and_multicompany(self):
|
||||
""" In a multicompany environnement, when the user is on company C01 and confirms a SO that
|
||||
belongs to a second company C02, this test ensures that the computations will be based on
|
||||
C02's data"""
|
||||
main_company = self.env['res.company']._get_main_company()
|
||||
main_company_currency = main_company.currency_id
|
||||
new_company_currency = self.env.ref('base.EUR') if main_company_currency == self.env.ref('base.USD') else self.env.ref('base.USD')
|
||||
|
||||
date = fields.Date.today()
|
||||
self.env['res.currency.rate'].search([]).unlink()
|
||||
self.env['res.currency.rate'].create([
|
||||
{'currency_id': main_company_currency.id, 'rate': 1, 'name': date, 'company_id': False},
|
||||
{'currency_id': new_company_currency.id, 'rate': 3, 'name': date, 'company_id': False},
|
||||
])
|
||||
|
||||
new_company = self.env['res.company'].create({
|
||||
'name': 'Super Company',
|
||||
'currency_id': new_company_currency.id,
|
||||
})
|
||||
self.env.user.company_id = new_company.id
|
||||
|
||||
self.pricelist.currency_id = new_company_currency.id
|
||||
|
||||
product = self._create_product()
|
||||
|
||||
incoming_picking_type = self.env['stock.picking.type'].search([('company_id', '=', new_company.id), ('code', '=', 'incoming')], limit=1)
|
||||
production_location = self.env['stock.location'].search([('company_id', '=', new_company.id), ('usage', '=', 'production')])
|
||||
|
||||
picking = self.env['stock.picking'].create({
|
||||
'picking_type_id': incoming_picking_type.id,
|
||||
'location_id': production_location.id,
|
||||
'location_dest_id': incoming_picking_type.default_location_dest_id.id,
|
||||
})
|
||||
self.env['stock.move'].create({
|
||||
'name': 'Incoming Product',
|
||||
'product_id': product.id,
|
||||
'location_id': production_location.id,
|
||||
'location_dest_id': incoming_picking_type.default_location_dest_id.id,
|
||||
'product_uom': product.uom_id.id,
|
||||
'product_uom_qty': 1,
|
||||
'price_unit': 100,
|
||||
'picking_type_id': incoming_picking_type.id,
|
||||
'picking_id': picking.id,
|
||||
})
|
||||
picking.action_confirm()
|
||||
res_dict = picking.button_validate()
|
||||
wizard = Form(self.env[(res_dict.get('res_model'))].with_context(res_dict['context'])).save()
|
||||
wizard.process()
|
||||
|
||||
self.pricelist.currency_id = new_company_currency.id
|
||||
partner = self.env['res.partner'].create({'name': 'Super Partner'})
|
||||
so = self.env['sale.order'].create({
|
||||
'name': 'Sale order',
|
||||
'partner_id': partner.id,
|
||||
'partner_invoice_id': partner.id,
|
||||
'pricelist_id': self.pricelist.id,
|
||||
})
|
||||
sol = self._create_sale_order_line(so, product, 1, price_unit=200)
|
||||
|
||||
self.env.user.company_id = main_company.id
|
||||
so.action_confirm()
|
||||
|
||||
self.assertEqual(sol.purchase_price, 100)
|
||||
self.assertEqual(sol.margin, 100)
|
||||
|
||||
Reference in New Issue
Block a user