Skip to content

Commit

Permalink
Merge pull request #2376 from ForgeFlow/13.0-mig-stock_account-script
Browse files Browse the repository at this point in the history
[13.0][MIG] stock_account
  • Loading branch information
pedrobaeza authored Apr 16, 2021
2 parents 1602ea5 + b463aec commit 23792da
Show file tree
Hide file tree
Showing 4 changed files with 353 additions and 1 deletion.
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
---Models in module 'stock_account'---
new model stock.valuation.layer
# DONE: post-migration: try to use data from product.price.history in some way (?)

---Fields in module 'stock_account'---
stock_account / account.move.line / is_anglo_saxon_line (boolean) : NEW
# TODO (?): is there a way to know which lines are anglosaxon??

stock_account / product.product / valuation (char) : selection_keys is now 'function' ('False')
stock_account / product.product / valuation (char) : type is now 'selection' ('char')
stock_account / product.product / cost_method (char) : selection_keys is now 'function' ('False')
stock_account / product.product / cost_method (char) : type is now 'selection' ('char')
stock_account / product.template / cost_method (char) : not a function anymore
stock_account / product.template / cost_method (char) : now related
stock_account / product.template / cost_method (char) : selection_keys is now 'function' ('False')
stock_account / product.template / cost_method (char) : type is now 'selection' ('char')
stock_account / product.template / valuation (char) : not a function anymore
stock_account / product.template / valuation (char) : now related
stock_account / product.template / valuation (char) : selection_keys is now 'function' ('False')
stock_account / product.template / valuation (char) : type is now 'selection' ('char')
# NOTHING TO DO: they are non stored fields

stock_account / product.template / property_cost_method (selection): DEL selection_keys: ['average', 'fifo', 'standard']
stock_account / product.template / property_stock_account_input (many2one): DEL relation: account.account
stock_account / product.template / property_stock_account_output (many2one): DEL relation: account.account
stock_account / product.template / property_valuation (selection): DEL selection_keys: ['manual_periodic', 'real_time']
# NOTHING TO DO: they use now the same fields from product.category instead

stock_account / stock.move / remaining_qty (float) : DEL
stock_account / stock.move / remaining_value (float) : DEL
stock_account / stock.move / value (float) : DEL
stock_account / stock.valuation.layer / remaining_qty (float) : NEW
stock_account / stock.valuation.layer / remaining_value (float) : NEW
stock_account / stock.valuation.layer / value (float) : NEW
# NOTHING TO DO: the relation between moves and valuation layers is new
# TODO (?): if the link with the stock move is found, then fill values

stock_account / stock.valuation.layer / account_move_id (many2one) : NEW relation: account.move
stock_account / stock.valuation.layer / company_id (many2one) : NEW relation: res.company, required
stock_account / stock.valuation.layer / description (char) : NEW
stock_account / stock.valuation.layer / product_id (many2one) : NEW relation: product.product, required
stock_account / stock.valuation.layer / quantity (float) : NEW
stock_account / stock.valuation.layer / stock_move_id (many2one) : NEW relation: stock.move
stock_account / stock.valuation.layer / unit_cost (float) : NEW
stock_account / stock.valuation.layer / stock_valuation_layer_id (many2one): NEW relation: stock.valuation.layer
stock_account / account.move / stock_valuation_layer_ids (one2many): NEW relation: stock.valuation.layer
stock_account / product.product / stock_valuation_layer_ids (one2many): NEW relation: stock.valuation.layer
stock_account / stock.move / stock_valuation_layer_ids (one2many): NEW relation: stock.valuation.layer
stock_account / stock.valuation.layer / stock_valuation_layer_ids (one2many): NEW relation: stock.valuation.layer
# DONE: post-migration: handle the new model (try to use data from product.price.history in some way?)

---XML records in module 'stock_account'---
NEW ir.actions.act_window: stock_account.stock_valuation_layer_action
DEL ir.actions.act_window: stock_account.product_valuation_action
NEW ir.model.access: stock_account.access_stock_valuation_layer
NEW ir.rule: stock_account.stock_valuation_layer_company_rule
NEW ir.ui.view: stock_account.product_template_tree_view
NEW ir.ui.view: stock_account.stock_valuation_layer_form
NEW ir.ui.view: stock_account.stock_valuation_layer_picking
NEW ir.ui.view: stock_account.stock_valuation_layer_search
NEW ir.ui.view: stock_account.stock_valuation_layer_tree
NEW ir.ui.view: stock_account.view_inventory_tree
NEW ir.ui.view: stock_account.view_stock_quant_tree_editable_inherit
NEW ir.ui.view: stock_account.view_stock_quant_tree_inherit
DEL ir.ui.view: stock_account.view_move_tree_valuation_at_date
DEL ir.ui.view: stock_account.view_stock_account_aml
DEL ir.ui.view: stock_account.view_stock_product_tree2
DEL ir.ui.view: stock_account.view_stock_quantity_history
# NOTHING TO DO

NEW ir.ui.view: stock_account.product_product_normal_form_view_inherit
DEL ir.ui.view: stock_account.product_normal_form_view_inherit
# DONE: pre-migration: xmlid renamed (to avoid a xpath issue)

DEL ir.property: stock_account.default_cost_method (noupdate)
DEL ir.property: stock_account.default_valuation (noupdate)
DEL ir.property: stock_account.property_stock_account_input_prd (noupdate)
DEL ir.property: stock_account.property_stock_account_output_prd (noupdate)
# DONE: post-migration: try to delete
260 changes: 260 additions & 0 deletions addons/stock_account/migrations/13.0.1.1/post-migration.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,260 @@
# Copyright 2020 ForgeFlow <http://www.forgeflow.com>
# Copyright 2020 Andrii Skrypka
# Copyright 2021 Tecnativa - Carlos Dauden
# Copyright 2021 Tecnativa - Sergio Teruel
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).

import logging

from openupgradelib import openupgrade
from odoo import _
from odoo.tools.float_utils import float_compare, float_is_zero, float_round
from odoo.addons.base.models.ir_model import query_insert

_logger = logging.getLogger(__name__)

# Declare global variant to avoid that it is passed between methods
precision_price = 0


def _prepare_common_svl_vals(move, product):
return {
"create_uid": move["write_uid"],
"create_date": move["date"],
"write_uid": move["write_uid"],
"write_date": move["date"],
"stock_move_id": move["id"],
"company_id": move["company_id"],
"product_id": move["product_id"],
"description": move["reference"] and "%s - %s" % (move["reference"], product.name) or product.name,
"value": 0.0,
"unit_cost": 0.0,
"remaining_qty": 0.0,
"remaining_value": 0.0,
"quantity": 0.0,
"old_product_price_history_id": None,
"account_move_id": move["account_move_id"],
}


def _prepare_in_svl_vals(move, quantity, unit_cost, product, is_dropship):
vals = _prepare_common_svl_vals(move, product)
vals.update({
"value": float_round(unit_cost * quantity, precision_digits=precision_price),
"unit_cost": unit_cost,
"quantity": quantity,
})
if product.cost_method in ("average", "fifo") and not is_dropship:
vals["remaining_qty"] = quantity
vals["remaining_value"] = vals["value"]
return vals


def _prepare_out_svl_vals(move, quantity, unit_cost, product):
# Quantity is negative for out valuation layers.
quantity = -quantity
vals = _prepare_common_svl_vals(move, product)
vals.update({
"value": float_round(unit_cost * quantity, precision_digits=precision_price),
"unit_cost": unit_cost,
"quantity": quantity,
"remaining_qty": 0.0,
"remaining_value": 0.0,
})
return vals


def _prepare_man_svl_vals(price_history_rec, previous_price, quantity, company, product):
diff = price_history_rec["cost"] - previous_price
value = float_round(diff * quantity, precision_digits=precision_price)
svl_vals = {
"create_uid": price_history_rec["write_uid"],
"create_date": price_history_rec["datetime"],
"write_uid": price_history_rec["write_uid"],
"write_date": price_history_rec["datetime"],
"stock_move_id": None,
"company_id": company.id,
"product_id": product.id,
"description": _("Product value manually modified (from %s to %s)"
) % (previous_price, price_history_rec["cost"]),
"value": value,
"unit_cost": 0.0,
"remaining_qty": 0.0,
"remaining_value": 0.0,
"quantity": 0.0,
"old_product_price_history_id": price_history_rec["id"],
"account_move_id": price_history_rec["account_move_id"],
}
return svl_vals


def get_product_price_history(env, company_id, product_id):
env.cr.execute("""
WITH account_move_rel AS (
SELECT id, create_date
FROM (
SELECT id, create_date, COUNT(*) OVER(PARTITION BY create_date) AS qty
FROM account_move
WHERE stock_move_id IS NULL
) foo
WHERE qty = 1
)
SELECT pph.id, pph.company_id, pph.product_id, pph.datetime, pph.cost, rel.id AS account_move_id,
pph.create_uid, pph.create_date, pph.write_uid, pph.write_date
FROM product_price_history pph
LEFT JOIN account_move_rel rel ON rel.create_date = pph.create_date
WHERE pph.company_id = %s AND pph.product_id = %s
ORDER BY pph.datetime, pph.id
""", (company_id, product_id))
return env.cr.dictfetchall()


def get_stock_moves(env, company_id, product_id):
env.cr.execute("""
WITH account_move_rel AS (
SELECT id, stock_move_id
FROM (
SELECT id, stock_move_id, COUNT(*) OVER(PARTITION BY stock_move_id) AS qty
FROM account_move
WHERE stock_move_id IS NOT NULL
) foo
WHERE qty = 1
)
SELECT sm.id, sm.company_id, sm.product_id, sm.date, sm.product_qty, sm.reference,
COALESCE(sm.price_unit, 0.0) AS price_unit, rel.id AS account_move_id,
sm.create_uid, sm.create_date, sm.write_uid, sm.write_date,
CASE WHEN (sl.usage <> 'internal' AND (sl.usage <> 'transit' OR sl.company_id <> sm.company_id))
AND (sld.usage = 'internal' OR (sld.usage = 'transit' AND sld.company_id = sm.company_id))
THEN 'in'
WHEN (sl.usage = 'internal' OR (sl.usage = 'transit' AND sl.company_id = sm.company_id))
AND (sld.usage <> 'internal' AND (sld.usage <> 'transit' OR sld.company_id <> sm.company_id))
THEN 'out'
WHEN sl.usage = 'supplier' AND sld.usage = 'customer' THEN 'dropship'
WHEN sl.usage = 'customer' AND sld.usage = 'supplier' THEN 'dropship_return'
ELSE 'other'
END AS move_type
FROM stock_move sm
LEFT JOIN stock_location sl ON sl.id = sm.location_id
LEFT JOIN stock_location sld ON sld.id = sm.location_dest_id
LEFT JOIN account_move_rel rel ON rel.stock_move_id = sm.id
WHERE sm.company_id = %s AND sm.product_id = %s AND state = 'done'
ORDER BY sm.date, sm.id
""", (company_id, product_id))
return env.cr.dictfetchall()


@openupgrade.logging()
def generate_stock_valuation_layer(env):
openupgrade.logged_query(
env.cr, """
ALTER TABLE stock_valuation_layer
ADD COLUMN old_product_price_history_id integer""",
)
company_obj = env["res.company"]
product_obj = env["product.product"]
# Needed to modify global variable
global precision_price
precision_price = env["decimal.precision"].precision_get("Product Price")
precision_uom = env["decimal.precision"].precision_get(
"Product Unit of Measure"
)
companies = company_obj.search([])
products = product_obj.with_context(active_test=False).search([("type", "in", ("product", "consu"))])
all_svl_list = []
for product in products:
for company in companies:
history_lines = get_product_price_history(env, company.id, product.id)
moves = get_stock_moves(env, company.id, product.id)
svl_in_vals_list = []
svl_out_vals_list = []
svl_man_vals_list = []
svl_in_index = 0
h_index = 0
previous_price = 0.0
previous_qty = 0.0
for move in moves:
is_dropship = True if move["move_type"] in ("dropship", "dropship_return") else False
if product.cost_method in ("average", "standard"):
# useless for Fifo because we have price unit in stock.move
# Add manual adjusts
have_qty = not float_is_zero(previous_qty, precision_digits=precision_uom)
while h_index < len(history_lines) and history_lines[h_index]["datetime"] < move["date"]:
price_history_rec = history_lines[h_index]
if float_compare(price_history_rec["cost"], previous_price, precision_digits=precision_price):
if have_qty:
svl_vals = _prepare_man_svl_vals(
price_history_rec, previous_price, previous_qty, company, product)
svl_man_vals_list.append(svl_vals)
previous_price = price_history_rec["cost"]
h_index += 1
# Add in svl
if move["move_type"] == "in" or is_dropship:
total_qty = previous_qty + move["product_qty"]
# TODO: is needed vaccum if total_qty is negative?
if float_is_zero(total_qty, precision_digits=precision_uom):
previous_price = move["price_unit"]
else:
previous_price = float_round(
(previous_price * previous_qty + move["price_unit"] * move["product_qty"]) / total_qty,
precision_digits=precision_price)
svl_vals = _prepare_in_svl_vals(
move, move["product_qty"], move["price_unit"], product, is_dropship)
svl_in_vals_list.append(svl_vals)
previous_qty = total_qty
# Add out svl
if move["move_type"] == "out" or is_dropship:
qty = move["product_qty"]
if product.cost_method in ("average", "fifo") and not is_dropship:
# Reduce remaininig qty in svl of type "in"
while qty > 0 and svl_in_index < len(svl_in_vals_list):
if svl_in_vals_list[svl_in_index]["remaining_qty"] >= qty:
candidate_cost = (svl_in_vals_list[svl_in_index]["remaining_value"] /
svl_in_vals_list[svl_in_index]["remaining_qty"])
svl_in_vals_list[svl_in_index]["remaining_qty"] -= qty
svl_in_vals_list[svl_in_index]["remaining_value"] = float_round(
candidate_cost * svl_in_vals_list[svl_in_index]["remaining_qty"],
precision_digits=precision_price)
qty = 0
else:
qty -= svl_in_vals_list[svl_in_index]["remaining_qty"]
svl_in_vals_list[svl_in_index]["remaining_qty"] = 0.0
svl_in_vals_list[svl_in_index]["remaining_value"] = 0.0
svl_in_index += 1
if product.cost_method == 'fifo':
svl_vals = _prepare_out_svl_vals(
move, move["product_qty"], move["price_unit"], product)
else:
svl_vals = _prepare_out_svl_vals(
move, move["product_qty"], previous_price, product)
svl_out_vals_list.append(svl_vals)
previous_qty -= move["product_qty"]
# Add manual adjusts after last move
if product.cost_method in ("average", "standard") and not float_is_zero(
previous_qty, precision_digits=precision_uom):
# useless for Fifo because we have price unit on product form
while h_index < len(history_lines):
price_history_rec = history_lines[h_index]
if float_compare(price_history_rec["cost"], previous_price, precision_digits=precision_price):
svl_vals = _prepare_man_svl_vals(
price_history_rec, previous_price, previous_qty, company, product)
svl_man_vals_list.append(svl_vals)
previous_price = price_history_rec["cost"]
h_index += 1
all_svl_list.extend(svl_in_vals_list + svl_out_vals_list + svl_man_vals_list)
if all_svl_list:
all_svl_list = sorted(all_svl_list, key=lambda k: (k["create_date"]))
_logger.info("To create {} svl records".format(len(all_svl_list)))
query_insert(env.cr, "stock_valuation_layer", all_svl_list)


@openupgrade.migrate()
def migrate(env, version):
generate_stock_valuation_layer(env)
openupgrade.delete_records_safely_by_xml_id(
env, [
"stock_account.default_cost_method",
"stock_account.default_valuation",
"stock_account.property_stock_account_input_prd",
"stock_account.property_stock_account_output_prd",
]
)
13 changes: 13 additions & 0 deletions addons/stock_account/migrations/13.0.1.1/pre-migration.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Copyright 2020 ForgeFlow <http://www.forgeflow.com>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from openupgradelib import openupgrade

_xmlid_renames = [
('stock_account.product_normal_form_view_inherit',
'stock_account.product_product_normal_form_view_inherit'),
]


@openupgrade.migrate()
def migrate(env, version):
openupgrade.rename_xmlids(env.cr, _xmlid_renames)
2 changes: 1 addition & 1 deletion odoo/openupgrade/doc/source/modules120-130.rst
Original file line number Diff line number Diff line change
Expand Up @@ -597,7 +597,7 @@ missing in the new release are marked with |del|.
+----------------------------------------------+-------------------------------------------------+
|stock | Done |
+----------------------------------------------+-------------------------------------------------+
|stock_account | |
|stock_account | Done |
+----------------------------------------------+-------------------------------------------------+
|stock_dropshipping | |
+----------------------------------------------+-------------------------------------------------+
Expand Down

0 comments on commit 23792da

Please sign in to comment.