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
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ def ref_or_id(ref_or_id, model_name, AccountChartTemplate=AccountChartTemplate):
}
for record_id, record_data in template_data[model_name].items()
if any(record_data.get(key) for key in field_names)
and ref_or_id(record_id, model_name)
}
AccountChartTemplate._load_data(company_data)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,73 @@ def stock_location_valuation_account_id(env):
)


def product_value_aggregate_move_rows(env):
"""
product.value is the history of MANUAL value overrides in 19.0:
_get_manual_value() takes the single LATEST row per move as the move's
entire value and suppresses the landed-cost extra. One row per former
valuation layer makes the newest layer (often a landed-cost adjustment)
silently replace the whole move value on the first ORM recompute (e.g.
posting a vendor bill against a migrated receipt). Collapse the layers
into one summed row per move.
"""
env.cr.execute(
"""
WITH agg AS (
SELECT move_id, sum(value) AS value, max(date) AS date,
min(id) AS keep_id,
string_agg(description, ' + ' ORDER BY id) AS description
FROM product_value
WHERE move_id IS NOT NULL
GROUP BY move_id
HAVING count(*) > 1
)
UPDATE product_value pv
SET value = agg.value, date = agg.date, description = agg.description
FROM agg
WHERE pv.id = agg.keep_id
"""
)
env.cr.execute(
"""
DELETE FROM product_value pv
USING (
SELECT move_id, min(id) AS keep_id
FROM product_value
WHERE move_id IS NOT NULL
GROUP BY move_id
HAVING count(*) > 1
) agg
WHERE pv.move_id = agg.move_id AND pv.id != agg.keep_id
"""
)


def product_value_outbound_sign(env):
"""
18.0 valuation layers carry a direction sign (outbound = negative);
19.0 stores the magnitude of the cost moved (a 19-created delivery or
production consumption has a positive value). A negative migrated value
flips the anglo-saxon COGS journal entry when the delivery is invoiced
after the migration. Normalize outbound moves to magnitudes.
"""
env.cr.execute(
"""
UPDATE product_value pv
SET value = -pv.value
FROM stock_move sm,
stock_location src,
stock_location dst
WHERE sm.id = pv.move_id
AND src.id = sm.location_id
AND dst.id = sm.location_dest_id
AND pv.value < 0
AND src.usage IN ('internal', 'transit')
AND dst.usage NOT IN ('internal', 'transit')
"""
)


def stock_move_value(env):
"""
Set stock.move#value to sum of product.value#value for this move
Expand All @@ -100,4 +167,6 @@ def migrate(env, version):
stock_move_account_move_id(env)
product_category_property_valuation(env)
stock_location_valuation_account_id(env)
product_value_aggregate_move_rows(env)
product_value_outbound_sign(env)
stock_move_value(env)
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
env = locals().get("env")

# A FIFO receipt carrying TWO valuation layers (base + an adjustment, the
# landed-cost shape) and a partial FIFO delivery (negative layer). The
# post-migration test asserts the layers collapse to one product.value row
# per move and that the outbound value migrates as a magnitude.
categ = env["product.category"].create(
{"name": "OU SVL agg test", "property_cost_method": "fifo"}
)
product = env["product.product"].create(
{
"name": "OU SVL agg part",
"type": "consu",
"is_storable": True,
"categ_id": categ.id,
}
)
Move = env["stock.move"]
in_move = Move.create(
{
"name": "OU test receipt",
"product_id": product.id,
"product_uom_qty": 10,
"product_uom": product.uom_id.id,
"location_id": env.ref("stock.stock_location_suppliers").id,
"location_dest_id": env.ref("stock.stock_location_stock").id,
"price_unit": 100.0,
}
)
in_move._action_confirm()
in_move.quantity = 10
in_move.picked = True
in_move._action_done()
# second layer on the same move: the landed-cost/adjustment shape
env["stock.valuation.layer"].create(
{
"product_id": product.id,
"quantity": 0,
"value": 50.0,
"stock_move_id": in_move.id,
"company_id": in_move.company_id.id,
"description": "OU test adjustment",
}
)
out_move = Move.create(
{
"name": "OU test delivery",
"product_id": product.id,
"product_uom_qty": 4,
"product_uom": product.uom_id.id,
"location_id": env.ref("stock.stock_location_stock").id,
"location_dest_id": env.ref("stock.stock_location_customers").id,
}
)
out_move._action_confirm()
out_move._action_assign()
out_move.quantity = 4
out_move.picked = True
out_move._action_done()
env["ir.model.data"].create(
[
{
"module": "openupgrade_test",
"name": "stock_account_in_move",
"model": "stock.move",
"res_id": in_move.id,
"noupdate": True,
},
{
"module": "openupgrade_test",
"name": "stock_account_out_move",
"model": "stock.move",
"res_id": out_move.id,
"noupdate": True,
},
]
)
env.cr.commit()
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
from odoo.tests import TransactionCase

from odoo.addons.openupgrade_framework import openupgrade_test


@openupgrade_test
class TestStockAccountMigration(TransactionCase):
def test_product_value_aggregated_per_move(self):
"""
product.value is the manual-override history: _get_manual_value()
takes the single latest row per move as the move's entire value, so
the former layers must collapse to one summed row — otherwise the
first recompute (e.g. posting a vendor bill) replaces a $1,050
receipt with its $50 adjustment layer.
"""
in_move = self.env.ref("openupgrade_test.stock_account_in_move")
rows = self.env["product.value"].search([("move_id", "=", in_move.id)])
self.assertEqual(
len(rows), 1, "former valuation layers must collapse to one row"
)
self.assertAlmostEqual(rows.value, 1050.0)
self.assertAlmostEqual(in_move.value, 1050.0)
# the recompute path must agree with the migrated value
self.assertAlmostEqual(in_move._get_value(), 1050.0)

def test_outbound_value_is_magnitude(self):
"""
18.0 layers carry a direction sign (deliveries negative); 19.0
stores cost magnitudes. A negative migrated value flips the
anglo-saxon COGS entry when the delivery is invoiced on 19.0.
"""
out_move = self.env.ref("openupgrade_test.stock_account_out_move")
# 4 units consumed at the receipt's $100 FIFO cost (the raw adjustment
# layer doesn't feed remaining_value the way a validated landed cost
# does); 18.0 recorded the layer as -400 — 19.0 must hold +400.
self.assertAlmostEqual(out_move.value, 400.0)
Loading