Skip to content

Commit 7f18843

Browse files
committed
[FIX] stock_currency_valuation: fix revaluation in secondary currency
Three defects on the revaluation of a product whose category has a secondary valuation currency. 1. The cost in currency was never written. The 18.0 migration rewrote action_validate_revaluation() as a copy of the new native method and dropped the line that updated standard_price_in_currency, so the valuation layer and the journal entry were right but the product form kept the old value. The other path that writes that field, stock.valuation.layer._update_currency_standard_price(), only runs for layers with a landed cost and the revaluation layer has none. The added value in currency is divided by product_id.quantity_svl, the same divisor the native code uses on the line above for standard_price, so both costs stay on the same basis when the revaluation targets specific layers. 2. The wizard preview added a unit cost to a total value: standard_price_in_currency + added_value_in_currency. The native code adds current_value_svl (a total) to added_value, so the figures shown on screen did not match the cost that ended up on the product. With 10 units at 10 and an added value of 50 it showed 60 (6 by unit) instead of 150 (15 by unit). 3. new_value_in_currency_by_qty declared _compute_new_value as its compute method, which never assigns it. It only resolved because reading new_value_in_currency first filled the cache. Also add standard_price_in_currency to the depends of the product.template compute. It only tracked the variant standard_price, so a change that moved only the cost in currency left the template showing a stale value. The existing standard_price dependency is kept: the field is not stored and that dependency drives the onchange in the product form. Add a test covering the four cases. closes #993 Signed-off-by: Filoquin adhoc <maq@adhoc.com.ar>
1 parent 3da22a2 commit 7f18843

4 files changed

Lines changed: 72 additions & 4 deletions

File tree

stock_currency_valuation/models/product_template.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,11 @@ def _compute_replenishment_cost(self):
4747
)
4848

4949
@api.depends_context("company")
50-
@api.depends("product_variant_ids", "product_variant_ids.standard_price")
50+
@api.depends(
51+
"product_variant_ids",
52+
"product_variant_ids.standard_price",
53+
"product_variant_ids.standard_price_in_currency",
54+
)
5155
def _compute_standard_price_in_currency(self):
5256
# Por ahora hacemos esto porque replishment cost no es compatible al 100% con variantes
5357
# obtenemos el precio del primer producto
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
from . import test_revaluation_in_currency
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
from odoo.tests.common import TransactionCase, tagged
2+
3+
4+
@tagged("post_install", "-at_install")
5+
class TestRevaluationInCurrency(TransactionCase):
6+
def test_revaluation_updates_standard_price_in_currency(self):
7+
"""La revaluacion tiene que impactar el costo en moneda secundaria de la ficha."""
8+
company = self.env.company
9+
categ = self.env["product.category"].create(
10+
{
11+
"name": "Categoria valuada en moneda",
12+
"property_cost_method": "average",
13+
"property_valuation": "manual_periodic",
14+
"valuation_currency_id": self.env.ref("base.USD").id,
15+
}
16+
)
17+
product = (
18+
self.env["product.product"]
19+
.create({"name": "Producto valuado en moneda", "is_storable": True, "categ_id": categ.id})
20+
.with_company(company)
21+
)
22+
product.standard_price = 1000.0
23+
product.standard_price_in_currency = 1.0
24+
25+
# ingreso 2 unidades por ajuste de inventario para tener layers con remaining_qty
26+
warehouse = self.env["stock.warehouse"].search([("company_id", "=", company.id)], limit=1)
27+
self.env["stock.quant"].with_context(inventory_mode=True).create(
28+
{
29+
"product_id": product.id,
30+
"location_id": warehouse.lot_stock_id.id,
31+
"inventory_quantity": 2.0,
32+
}
33+
).action_apply_inventory()
34+
35+
wizard = (
36+
self.env["stock.valuation.layer.revaluation"]
37+
.with_company(company)
38+
.create({"product_id": product.id, "added_value": 2000.0, "reason": "test"})
39+
)
40+
expected = 1.0 + wizard.added_value_in_currency / 2.0
41+
# el preview del wizard tiene que anticipar el costo que va a quedar en la ficha
42+
self.assertAlmostEqual(wizard.new_value_in_currency, expected * 2.0, places=2)
43+
self.assertAlmostEqual(wizard.new_value_in_currency_by_qty, expected, places=2)
44+
45+
wizard.action_validate_revaluation()
46+
47+
self.assertAlmostEqual(product.standard_price, 2000.0, places=2)
48+
self.assertAlmostEqual(product.standard_price_in_currency, expected, places=2)
49+
# esta lectura ademas cachea el valor del template para el chequeo de abajo
50+
self.assertAlmostEqual(product.product_tmpl_id.standard_price_in_currency, expected, places=2)
51+
52+
# si solo se mueve el costo en moneda (ej. un landed cost), el template tiene que seguirlo
53+
product.standard_price_in_currency = 5.0
54+
self.assertAlmostEqual(product.product_tmpl_id.standard_price_in_currency, 5.0, places=2)

stock_currency_valuation/wizard/stock_valuation_layer_revaluation.py

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ class StockValuationLayerRevaluation(models.TransientModel):
2626
)
2727
new_value_in_currency_by_qty = fields.Monetary(
2828
"New value in currency by quantity",
29-
compute="_compute_new_value",
29+
compute="_compute_new_value_in_currency",
3030
)
3131

3232
@api.depends("product_id", "company_id")
@@ -69,8 +69,11 @@ def _compute_added_value_in_currency(self):
6969
def _compute_new_value_in_currency(self):
7070
for reval in self:
7171
product_id = reval.product_id.with_company(reval.company_id)
72-
reval.new_value_in_currency = product_id.standard_price_in_currency + reval.added_value_in_currency
73-
if not float_is_zero(reval.current_quantity_svl, precision_rounding=self.product_id.uom_id.rounding):
72+
# standard_price_in_currency es el costo unitario: hay que llevarlo a valor total
73+
# antes de sumarle el valor agregado, igual que el nativo con current_value_svl.
74+
current_value_in_currency = product_id.standard_price_in_currency * reval.current_quantity_svl
75+
reval.new_value_in_currency = current_value_in_currency + reval.added_value_in_currency
76+
if not float_is_zero(reval.current_quantity_svl, precision_rounding=reval.product_id.uom_id.rounding):
7477
reval.new_value_in_currency_by_qty = reval.new_value_in_currency / reval.current_quantity_svl
7578
else:
7679
reval.new_value_in_currency_by_qty = 0.0
@@ -108,6 +111,12 @@ def action_validate_revaluation(self):
108111
product_id.with_context(disable_auto_svl=True).standard_price += (
109112
self.added_value / product_id.quantity_svl
110113
)
114+
# El costo en moneda secundaria no lo actualiza ningun otro camino: el
115+
# _update_currency_standard_price del layer solo corre para landed costs
116+
# y el layer de revaluacion no tiene, asi que se actualiza aca.
117+
product_id.with_context(disable_auto_svl=True).standard_price_in_currency += (
118+
self.added_value_in_currency / product_id.quantity_svl
119+
)
111120
if self.lot_id:
112121
description += _(
113122
" lot/serial number cost updated from %(previous)s to %(new_cost)s.",

0 commit comments

Comments
 (0)