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
508 changes: 508 additions & 0 deletions stock_period_evaluation/README.rst

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions stock_period_evaluation/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from . import models
from . import wizards
from . import reports
35 changes: 35 additions & 0 deletions stock_period_evaluation/__manifest__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# Copyright (C) 2023-Today:
# Dinamiche Aziendali Srl (<http://www.dinamicheaziendali.it/>)
# @author: Marco Calcagni <mcalcagni@dinamicheaziendali.it>
# @author: Giuseppe Borruso <gborruso@dinamicheaziendali.it>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).

{
"name": "Stock Period Evaluation",
"summary": "Stock evaluation with FIFO, LIFO and average methods",
"version": "19.0.1.0.0",
"author": "Pordenone Linux User Group (PNLUG), Odoo Community Association (OCA), "
"Dinamiche Aziendali srl, Sergio Corato",
"category": "Warehouse",
"website": "https://github.com/OCA/stock-logistics-reporting",
"license": "AGPL-3",
"maintainers": ["MarcoCalcagni", "Borruso"],
"depends": [
"stock",
"purchase_stock",
"stock_account",
"report_xlsx",
],
"data": [
"security/stock_close_group.xml",
"security/stock_close_rule.xml",
"security/ir.model.access.csv",
"data/ir_config_parameter_data.xml",
"views/stock_close_views.xml",
"views/stock_close_line_views.xml",
"wizards/stock_close_import.xml",
"wizards/stock_close_print.xml",
"reports/xlsx_stock_close_print.xml",
],
"installable": True,
}
7 changes: 7 additions & 0 deletions stock_period_evaluation/data/ir_config_parameter_data.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8" ?>
<odoo noupdate="1">
<record id="last_close_date_config_parameter" model="ir.config_parameter">
<field name="key">stock_period_evaluation.last_close_date</field>
<field name="value">2010-01-01</field>
</record>
</odoo>
556 changes: 556 additions & 0 deletions stock_period_evaluation/i18n/it.po

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions stock_period_evaluation/models/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
from . import product_product
from . import stock_move
from . import stock_close
from . import stock_close_line
215 changes: 215 additions & 0 deletions stock_period_evaluation/models/product_product.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,215 @@
# Copyright (C) 2023-Today:
# Dinamiche Aziendali Srl (<http://www.dinamicheaziendali.it/>)
# @author: Marco Calcagni <mcalcagni@dinamicheaziendali.it>
# @author: Giuseppe Borruso <gborruso@dinamicheaziendali.it>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).

import copy

from odoo import models


class Product(models.Model):
_inherit = "product.product"

def _get_cost(self):
# overridable method to customize cost used for evaluation
self.ensure_one()
return self.standard_price

def _compute_qty_available(self, to_date):
res = self._compute_quantities_available(to_date)
return res

def _compute_quantities_available(self, to_date=False):
date = to_date

# get quants
quan = {}
self.env.cr.execute(
"""
SELECT
stock_quant.quantity,
stock_quant.product_id,
stock_quant.location_id,
stock_quant.lot_id,
stock_quant.package_id,
stock_quant.owner_id,
stock_quant.id
FROM
stock_quant,
stock_location
WHERE
stock_quant.location_id = stock_location.id
AND stock_quant.product_id = %s
AND stock_location.company_id = %s
ORDER BY
stock_quant.product_id,
stock_quant.location_id,
stock_quant.lot_id,
stock_quant.package_id,
stock_quant.owner_id;
""",
(self.id, self.env.user.company_id.id),
)

for row2 in self.env.cr.fetchall():
quant_qty = row2[0] * 1.0
product_id = row2[1]
location_id = row2[2]
lot_id = row2[3] if self.tracking != "serial" and row2[3] is not None else 0
package_id = row2[4] if row2[4] and row2[4] is not None else 0
owner_id = row2[5] if row2[5] and row2[5] is not None else 0
key = f"{product_id}_{location_id}_{lot_id}_{package_id}_{owner_id}"
if key in quan.keys():
quan[key] = quan[key] + quant_qty
else:
quan[key] = quant_qty * 1.0

# rename quan -> move
move = quan

# duplicate quan dict
stock_now = copy.deepcopy(quan)

# recompute moves if set request date
# moves +
self.env.cr.execute(
"""
SELECT
SUM(stock_move_line.quantity),
stock_move_line.product_id,
stock_move_line.location_id,
stock_move_line.lot_id,
stock_move_line.package_id,
stock_move_line.owner_id,
stock_move_line.date
FROM
stock_move_line
WHERE
stock_move_line.date >= %s
AND stock_move_line.state = 'done'
AND stock_move_line.product_id = %s
AND stock_move_line.company_id = %s
GROUP BY
stock_move_line.product_id,
stock_move_line.location_id,
stock_move_line.lot_id,
stock_move_line.package_id,
stock_move_line.owner_id,
stock_move_line.date
ORDER BY
stock_move_line.product_id,
stock_move_line.location_id,
stock_move_line.lot_id,
stock_move_line.package_id,
stock_move_line.owner_id,
stock_move_line.date desc;
""",
(date, self.id, self.env.user.company_id.id),
)

for row in self.env.cr.fetchall():
move_qty = row[0]
product_id = row[1]
location_id = row[2]
lot_id = row[3] if self.tracking != "serial" and row[3] is not None else 0
package_id = row[4] if row[4] and row[4] is not None else 0
owner_id = row[5] if row[5] and row[5] is not None else 0
key = f"{product_id}_{location_id}_{lot_id}_{package_id}_{owner_id}"
if key in move.keys():
move[key] += move_qty
else:
move[key] = move_qty
stock_now[key] = 0

# moves -
self.env.cr.execute(
"""
SELECT
SUM(stock_move_line.quantity),
stock_move_line.product_id,
stock_move_line.location_dest_id,
stock_move_line.lot_id,
stock_move_line.package_id,
stock_move_line.owner_id,
stock_move_line.date
FROM
stock_move_line
WHERE
stock_move_line.date >= %s
AND stock_move_line.state='done'
AND stock_move_line.product_id = %s
AND stock_move_line.company_id = %s
GROUP BY
stock_move_line.product_id,
stock_move_line.location_dest_id,
stock_move_line.lot_id,
stock_move_line.package_id,
stock_move_line.owner_id,
stock_move_line.date
ORDER BY
stock_move_line.product_id,
stock_move_line.location_dest_id,
stock_move_line.lot_id,
stock_move_line.package_id,
stock_move_line.owner_id,
stock_move_line.date desc;
""",
(date, self.id, self.env.user.company_id.id),
)

for row in self.env.cr.fetchall():
move_qty = row[0]
product_id = row[1]
location_dest_id = row[2]
lot_id = row[3] if self.tracking != "serial" and row[3] is not None else 0
package_id = row[4] if row[4] and row[4] is not None else 0
owner_id = row[5] if row[5] and row[5] is not None else 0
key = f"{product_id}_{location_dest_id}_{lot_id}_{package_id}_{owner_id}"
if key in move.keys():
move[key] -= move_qty
else:
move[key] = -move_qty
stock_now[key] = 0

# compute
list_internal_quant = []
for key, _qty in move.items():
key_split = key.split("_")
product_id = int(key_split[0])
uom_id = self.env["product.product"].browse(product_id).uom_id.id
location_id = int(key_split[1])
lot_id = int(key_split[2])
if lot_id == 0:
lot_id = None
package_id = int(key_split[3])
if package_id == 0:
package_id = None
owner_id = int(key_split[4])
if owner_id == 0:
owner_id = None
mov = move[key] or 0.0
qua = stock_now[key] or 0.0
dif = qua - mov

# only internal locations
location_obj = self.env["stock.location"].search(
[("id", "=", location_id)], limit=1
)
if location_obj.usage == "internal":
list_internal_quant.append(
{
"date": date,
"product_id": product_id,
"uom_id": uom_id,
"location_id": location_id,
"lot_id": lot_id,
"package_id": package_id,
"owner_id": owner_id,
"stock_at_date": mov,
"stock_now": qua,
"diff_qty": dif,
}
)
return list_internal_quant
Loading
Loading