Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions stock_account_cost_revaluation/README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ Características
cuenta de una ubicación de scrap/ajuste: sin cuenta, sin asiento). No se usa la
cuenta de variación de stock (pertenece al cierre continental) ni la de
diferencia de precio (se usa para la diferencia de precio de compras / PPV).
- El asiento queda **vinculado al ``product.value``** que el estándar registra por
el cambio de costo (campo ``account_move_id``, que aporta ``stock_account_ux``).
Sin ese vínculo el ajuste sigue figurando como pendiente en el reporte de
valuación y el cierre de inventario lo vuelve a contabilizar.
- **No** genera asiento (por diseño): productos con costeo FIFO
(``standard_price`` es informativo), categorías con valoración periódica (lo
materializa el cierre de inventario), productos sin stock on hand y categorías
Expand All @@ -38,9 +42,10 @@ Detalles Técnicos

- Modelos heredados:

- ``product.product``: override de ``_change_standard_price`` y método
- ``product.product``: override de ``_change_standard_price``, método
``_create_cost_revaluation_entry`` que arma y postea el ``account.move`` de
revaluación.
revaluación, y ``_link_cost_revaluation_entry`` que lo deja apuntado en el
``product.value`` del cambio de costo.
- ``product.category``: campo nuevo ``property_cost_revaluation_account_id``
("Cost Revaluation Account"), Many2one a ``account.account``,
company-dependent.
Expand Down
5 changes: 4 additions & 1 deletion stock_account_cost_revaluation/__manifest__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,10 @@
"contable (standard_price) de productos con categoría de valoración perpetua "
"(real_time), en costeo estándar o promedio (AVCO).",
"depends": [
"stock_account",
# ``product.value.account_move_id``, the field this module writes the
# revaluation entry into. It is ``auto_install`` over ``stock_account``, so
# declaring it costs nothing and makes the field guaranteed.
"stock_account_ux",
],
"data": [
"views/product_category_views.xml",
Expand Down
37 changes: 36 additions & 1 deletion stock_account_cost_revaluation/models/product_product.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,44 @@ def _change_standard_price(self, old_price):
for product in self:
if product not in old_price:
continue
product._create_cost_revaluation_entry(old_price[product])
entry = product._create_cost_revaluation_entry(old_price[product])
product._link_cost_revaluation_entry(entry)
return res

def _link_cost_revaluation_entry(self, entry):
"""Point the price change's ``product.value`` at the entry that booked it.

The standard records every price change as a ``product.value`` and leaves the
accounting to the inventory closing, so ``stock_account_ux`` reads "no entry" as
"still part of the difference to adjust". An adjustment this module already
booked has to say so: otherwise it shows up as pending in the valuation report
and the closing books it a second time (functional feedback, task 64440).

The record is the one the standard ``_change_standard_price`` just created for
this product: the most recent price change of the company still without an
entry. Lot price changes are left out —they get their own ``product.value``—
because this entry values the product's on-hand stock, not a lot's.
"""
self.ensure_one()
if not entry:
return
product_value = (
self.env["product.value"]
.sudo()
.search(
[
("product_id", "=", self.id),
("move_id", "=", False),
("lot_id", "=", False),
("company_id", "=", entry.company_id.id),
("account_move_id", "=", False),
],
order="date desc, id desc",
limit=1,
)
)
product_value.account_move_id = entry

def _create_cost_revaluation_entry(self, old_price):
"""Contabiliza la diferencia de valuación de inventario por el cambio de
``standard_price`` (costo nuevo vs. previo) sobre el stock on hand.
Expand Down
27 changes: 27 additions & 0 deletions stock_account_cost_revaluation/tests/test_cost_revaluation.py
Original file line number Diff line number Diff line change
Expand Up @@ -160,3 +160,30 @@ def test_no_entry_without_stock(self):
before = self._journal_moves_count()
product.standard_price = 15.0
self.assertEqual(self._journal_moves_count(), before, "Sin stock on hand no debe generar asiento")

def test_revaluation_entry_is_linked_to_the_product_value(self):
"""The price change is recorded as a ``product.value`` and this entry is what
booked it. Without the link the adjustment stays "pending" for the valuation
report and the closing books it a second time (functional feedback, task
64440)."""
product = self._product(self._categ("standard", "real_time"))
product.standard_price = 15.0

product_value = self.env["product.value"].search(
[("product_id", "=", product.id), ("move_id", "=", False)], order="id desc", limit=1
)
move = self.env["account.move"].search([("journal_id", "=", self.stock_journal.id)], order="id desc", limit=1)
self.assertTrue(move, "The price change has to post the revaluation entry")
self.assertEqual(product_value.account_move_id, move)

def test_product_value_stays_pending_without_entry(self):
"""No entry, nothing to link: the adjustment stays pending, which is exactly what
the valuation report has to keep showing."""
product = self._product(self._categ("standard", "real_time", revaluation_account=False))
product.standard_price = 15.0

product_value = self.env["product.value"].search(
[("product_id", "=", product.id), ("move_id", "=", False)], order="id desc", limit=1
)
self.assertTrue(product_value, "The standard records the price change anyway")
self.assertFalse(product_value.account_move_id)