[FIX] purchase_stock: compute pdiff based on bill date
Multi-currency setup. On a bill, if the bill date and the accounting date are not the same, there will be some errors in the stock valuation. To reproduce the issue: (Company in USD) 1. Enable EUR, with specific rates: - Today: 100 EUR = 150 USD - One day ago: 100 EUR = 130 USD - Two days ago: 100 EUR = 125 USD 2. Create an auto-avco storable product P 3. Confirm a PO with 1 x P at 100 EUR 4. Receive the product 5. Bill it: - Bill date: two days ago - Accounting date: one day ago 6. Open the inventory valuation Error: a price diff layer has been created, which makes sense, but its value is incorrect: -20 USD. It should be -25 USD. When computing the price difference, we use the bill value at accounting date (130 USD). This is incorrect, it should be based on the bill date (125 USD). OPW-3544318 closes odoo/odoo#144649 X-original-commit: 2d3d01594e09297f74d657c0ab3a0181e3754dc5 Signed-off-by: William Henrotin (whe) <whe@odoo.com> Signed-off-by: Adrien Widart (awt) <awt@odoo.com>
This commit is contained in:
@@ -203,20 +203,20 @@ class AccountMoveLine(models.Model):
|
||||
aml = self
|
||||
|
||||
aml_gross_price_unit = aml._get_gross_unit_price()
|
||||
aml_price_unit = aml.currency_id._convert(aml_gross_price_unit, aml.company_id.currency_id, aml.company_id, aml.date, round=False)
|
||||
aml_price_unit = aml.currency_id._convert(aml_gross_price_unit, aml.company_id.currency_id, aml.company_id, aml.move_id.invoice_date, round=False)
|
||||
aml_price_unit = aml.product_uom_id._compute_price(aml_price_unit, product_uom)
|
||||
|
||||
unit_valuation_difference = aml_price_unit - layer_price_unit
|
||||
|
||||
# Generate the AML values for the already out quantities
|
||||
unit_valuation_difference_curr = self.company_id.currency_id._convert(unit_valuation_difference, self.currency_id, self.company_id, self.date, round=False)
|
||||
unit_valuation_difference_curr = self.company_id.currency_id._convert(unit_valuation_difference, self.currency_id, self.company_id, self.move_id.invoice_date, round=False)
|
||||
unit_valuation_difference_curr = product_uom._compute_price(unit_valuation_difference_curr, self.product_uom_id)
|
||||
out_qty_to_invoice = product_uom._compute_quantity(out_qty_to_invoice, self.product_uom_id)
|
||||
if not float_is_zero(unit_valuation_difference_curr * out_qty_to_invoice, precision_rounding=self.currency_id.rounding):
|
||||
aml_vals_list += self._prepare_pdiff_aml_vals(out_qty_to_invoice, unit_valuation_difference_curr)
|
||||
|
||||
# Generate the SVL values for the on hand quantities (and impact the parent layer)
|
||||
po_pu_curr = po_line.currency_id._convert(po_line.price_unit, self.currency_id, self.company_id, self.date, round=False)
|
||||
po_pu_curr = po_line.currency_id._convert(po_line.price_unit, self.currency_id, self.company_id, self.move_id.invoice_date, round=False)
|
||||
price_difference_curr = po_pu_curr - aml_gross_price_unit
|
||||
if not float_is_zero(unit_valuation_difference * qty_to_correct, precision_rounding=self.company_id.currency_id.rounding):
|
||||
svl_vals = self._prepare_pdiff_svl_vals(layer, sign * qty_to_correct, unit_valuation_difference, price_difference_curr)
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
import time
|
||||
from datetime import datetime
|
||||
from datetime import datetime, timedelta
|
||||
from freezegun import freeze_time
|
||||
from unittest.mock import patch
|
||||
|
||||
@@ -2624,6 +2624,85 @@ class TestStockValuationWithCOA(AccountTestInvoicingCommon):
|
||||
self.assertEqual(svls.mapped('remaining_value'), [13.0, 12.0, 0.0, 0.0, 0.0, 0.0])
|
||||
self.assertEqual(svls.mapped('value'), [10.0, 10.0, 1.0, 2.0, -1.0, 3.0])
|
||||
|
||||
def test_pdiff_multi_curr_and_rates(self):
|
||||
"""
|
||||
Company in USD.
|
||||
Today: 100 EUR = 150 USD
|
||||
One day ago: 100 EUR = 130 USD
|
||||
Two days ago: 100 EUR = 125 USD
|
||||
Buy and receive one auto-AVCO product at 100 EUR. Bill it with:
|
||||
- Bill date: two days ago (125 USD)
|
||||
- Accounting date: one day ago (130 USD)
|
||||
The value at bill date should be used for both bill value and price
|
||||
diff value.
|
||||
"""
|
||||
usd_currency = self.env.ref('base.USD')
|
||||
eur_currency = self.env.ref('base.EUR')
|
||||
|
||||
today = fields.Date.today()
|
||||
one_day_ago = today - timedelta(days=1)
|
||||
two_days_ago = today - timedelta(days=2)
|
||||
|
||||
self.env.company.currency_id = usd_currency.id
|
||||
|
||||
self.env['res.currency.rate'].search([]).unlink()
|
||||
self.env['res.currency.rate'].create([{
|
||||
'name': day.strftime('%Y-%m-%d'),
|
||||
'rate': 1 / rate,
|
||||
'currency_id': eur_currency.id,
|
||||
'company_id': self.env.company.id,
|
||||
} for (day, rate) in [
|
||||
(today, 1.5),
|
||||
(one_day_ago, 1.3),
|
||||
(two_days_ago, 1.25),
|
||||
]])
|
||||
|
||||
self.product1.product_tmpl_id.categ_id.property_cost_method = 'average'
|
||||
|
||||
po = self.env['purchase.order'].create({
|
||||
'partner_id': self.partner_id.id,
|
||||
'currency_id': eur_currency.id,
|
||||
'order_line': [
|
||||
(0, 0, {
|
||||
'name': self.product1.name,
|
||||
'product_id': self.product1.id,
|
||||
'product_qty': 1.0,
|
||||
'product_uom': self.product1.uom_po_id.id,
|
||||
'price_unit': 100.0,
|
||||
'taxes_id': False,
|
||||
}),
|
||||
],
|
||||
})
|
||||
po.button_confirm()
|
||||
|
||||
receipt = po.picking_ids
|
||||
receipt.move_ids.move_line_ids.quantity = 1.0
|
||||
receipt.button_validate()
|
||||
|
||||
layer = receipt.move_ids.stock_valuation_layer_ids
|
||||
self.assertEqual(layer.value, 150)
|
||||
|
||||
action = po.action_create_invoice()
|
||||
bill = self.env["account.move"].browse(action["res_id"])
|
||||
bill.invoice_date = two_days_ago
|
||||
bill.date = one_day_ago
|
||||
bill.action_post()
|
||||
|
||||
pdiff_layer = layer.stock_valuation_layer_ids
|
||||
self.assertEqual(pdiff_layer.value, -25)
|
||||
self.assertEqual(layer.remaining_value, 125)
|
||||
|
||||
in_stock_amls = self.env['account.move.line'].search([
|
||||
('product_id', '=', self.product1.id),
|
||||
('account_id', '=', self.stock_input_account.id),
|
||||
], order='id')
|
||||
self.assertRecordValues(in_stock_amls, [
|
||||
# pylint: disable=bad-whitespace
|
||||
{'date': today, 'debit': 0, 'credit': 150, 'reconciled': True},
|
||||
{'date': one_day_ago, 'debit': 125, 'credit': 0, 'reconciled': True},
|
||||
{'date': one_day_ago, 'debit': 25, 'credit': 0, 'reconciled': True},
|
||||
])
|
||||
|
||||
def test_invoice_on_ordered_qty_with_backorder_and_different_currency_automated(self):
|
||||
"""Create a PO with currency different from the company currency. Set the
|
||||
product to be invoiced on ordered quantities. Receive partially the products
|
||||
|
||||
Reference in New Issue
Block a user