diff --git a/stock_inventory_turnover_report/README.rst b/stock_inventory_turnover_report/README.rst new file mode 100644 index 0000000..3960b04 --- /dev/null +++ b/stock_inventory_turnover_report/README.rst @@ -0,0 +1,8 @@ +========================= +Inventory Turnover Report +========================= + +.. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! diff --git a/stock_inventory_turnover_report/__init__.py b/stock_inventory_turnover_report/__init__.py new file mode 100644 index 0000000..5f527c2 --- /dev/null +++ b/stock_inventory_turnover_report/__init__.py @@ -0,0 +1,3 @@ +# Copyrigh 2020 Open Source Integrators +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). +from . import models diff --git a/stock_inventory_turnover_report/__manifest__.py b/stock_inventory_turnover_report/__manifest__.py new file mode 100644 index 0000000..9605385 --- /dev/null +++ b/stock_inventory_turnover_report/__manifest__.py @@ -0,0 +1,20 @@ +# Copyrigh 2020 Open Source Integrators +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). +{ + "name": "Inventory Turnover Report", + "summary": "Analize inventory turnover in the last 6 and 12 months", + "version": "12.0.1.0.0", + "development_status": "beta", + "category": "Warehouse", + "website": "https://github.com/OCA/stock-logistics-reporting", + "author": "Open Source Integrators, Odoo Community Association (OCA)", + "maintainers": ["dreispt"], + "license": "AGPL-3", + "installable": True, + "depends": [ + "stock", + ], + "data": [ + 'views/product_views.xml', + ], +} diff --git a/stock_inventory_turnover_report/models/__init__.py b/stock_inventory_turnover_report/models/__init__.py new file mode 100644 index 0000000..5c74c8c --- /dev/null +++ b/stock_inventory_turnover_report/models/__init__.py @@ -0,0 +1 @@ +from . import product_product diff --git a/stock_inventory_turnover_report/models/product_product.py b/stock_inventory_turnover_report/models/product_product.py new file mode 100644 index 0000000..fe283e1 --- /dev/null +++ b/stock_inventory_turnover_report/models/product_product.py @@ -0,0 +1,122 @@ +# Copyright 2020 Open Source Intgerators +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). + +from dateutil.relativedelta import relativedelta +from odoo import fields, models + + +class Product(models.Model): + _inherit = "product.product" + + def _compute_quantities_in_dict(self, from_date): + domain_base = [ + ('product_id', 'in', self.ids), + ('date', '>=', from_date)] + domain_in = domain_base + [ + ('picking_id.picking_type_id.code', + 'in', ['incoming', 'mrp_operation'])] + domain_return = domain_base + [ + ('location_dest_id.usage', + 'in', ['supplier', 'inventory']) # loss + ] + Move = self.env['stock.move'] + moves_in = { + item['product_id'][0]: item['product_qty'] + for item in Move.read_group( + domain_in, + ['product_id', 'product_qty'], + ['product_id'])} + moves_return = { + item['product_id'][0]: item['product_qty'] + for item in Move.read_group( + domain_return, + ['product_id', 'product_qty'], + ['product_id'])} + return { + x.id: ( + moves_in.get(x.id, 0) + - moves_return.get(x.id, 0)) + for x in self} + + def _compute_inventory_turn_report(self): + month = relativedelta(month=1) + today = fields.Date.today() + at_6m_ago = today - 6 * month + at_12m_ago = today - 12 * month + available6m = { + x.id: x.qty_available + for x in self.with_context(to_date=at_6m_ago)} + available12m = { + x.id: x.qty_available + for x in self.with_context(to_date=at_12m_ago)} + gotten6m = self._compute_quantities_in_dict(from_date=at_6m_ago) + gotten12m = self._compute_quantities_in_dict(from_date=at_12m_ago) + for prod in self: + prod.total_cost = prod.qty_available * prod.standard_price + # Quantity initially available + prod.qty_available_6m = available6m.get(prod.id) + prod.qty_available_12m = available12m.get(prod.id) + # Quantity gotten (produced + procured) + prod.qty_gotten_6m = gotten6m.get(prod.id) + prod.qty_gotten_12m = gotten12m.get(prod.id) + # Quantity consumed in period + prod.qty_consumed_6m = ( + prod.qty_available_6m + + prod.qty_gotten_6m + - prod.qty_available) + prod.qty_consumed_12m = ( + prod.qty_available_12m + + prod.qty_gotten_12m + - prod.qty_available) + # Months of Inventory + prod.months_of_inventory_6m = ( + 0.0 if not prod.qty_consumed_6m else + prod.qty_available / prod.qty_consumed_6m * 6) + prod.months_of_inventory_12m = ( + 0.0 if not prod.qty_consumed_12m else + prod.qty_available / prod.qty_consumed_12m * 12) + # Turns / Cycles per Month + prod.inventory_turns_6m = ( + 0.0 if not prod.months_of_inventory_6m else + 12 / prod.months_of_inventory_6m) + prod.inventory_turns_12m = ( + 0.0 if not prod.months_of_inventory_12m else + 12 / prod.months_of_inventory_12m) + + total_cost = fields.Float( + compute="_compute_inventory_turn_report", + help="= Qty. on hand x Current Cost") + qty_available_6m = fields.Float( + name="Qty. 6m Ago", + compute="_compute_inventory_turn_report", + help="Qty. on hand 6 months ago") + qty_gotten_6m = fields.Float( + name="Qty. Gotten 6m", + compute="_compute_inventory_turn_report", + help="Qty. Procured or Produced in the the last 6 months") + qty_consumed_6m = fields.Float( + compute="_compute_inventory_turn_report", + help="Qty. consumed in the the last 6 months") + months_of_inventory_6m = fields.Float( + compute="_compute_inventory_turn_report", + help="Months of Inventory, in the last 6 months") + inventory_turns_6m = fields.Float( + compute="_compute_inventory_turn_report", + help="Inventory Turns / Cycles in the last 6 months") + qty_available_12m = fields.Float( + name="Qty. 12m ago", + compute="_compute_inventory_turn_report", + help="= Qty. on hand 12 months ago") + qty_gotten_12m = fields.Float( + name="Qty. Gotten 12m", + compute="_compute_inventory_turn_report", + help="Qty. Procured or Produced in the the last 12 months") + qty_consumed_12m = fields.Float( + compute="_compute_inventory_turn_report", + help="Qty. consumed in the the last 12 months") + months_of_inventory_12m = fields.Float( + compute="_compute_inventory_turn_report", + help="Months of Inventory, in the last 12 months") + inventory_turns_12m = fields.Float( + compute="_compute_inventory_turn_report", + help="Inventory Turns / Cycles in the last 12 months") diff --git a/stock_inventory_turnover_report/readme/CONTRIBUTORS.rst b/stock_inventory_turnover_report/readme/CONTRIBUTORS.rst new file mode 100644 index 0000000..9d10b6f --- /dev/null +++ b/stock_inventory_turnover_report/readme/CONTRIBUTORS.rst @@ -0,0 +1,3 @@ +* Open Source Integrators + + * Daniel Reis diff --git a/stock_inventory_turnover_report/readme/DESCRIPTION.rst b/stock_inventory_turnover_report/readme/DESCRIPTION.rst new file mode 100644 index 0000000..3805119 --- /dev/null +++ b/stock_inventory_turnover_report/readme/DESCRIPTION.rst @@ -0,0 +1 @@ +Report to compute the inventory turnover cycles in the last 6 and 12 months. diff --git a/stock_inventory_turnover_report/readme/USAGE.rst b/stock_inventory_turnover_report/readme/USAGE.rst new file mode 100644 index 0000000..aeaebbe --- /dev/null +++ b/stock_inventory_turnover_report/readme/USAGE.rst @@ -0,0 +1 @@ +The report is available at Inventory > Reporting > Inventory Turnover. diff --git a/stock_inventory_turnover_report/static/description/icon.png b/stock_inventory_turnover_report/static/description/icon.png new file mode 100644 index 0000000..3a0328b Binary files /dev/null and b/stock_inventory_turnover_report/static/description/icon.png differ diff --git a/stock_inventory_turnover_report/views/product_views.xml b/stock_inventory_turnover_report/views/product_views.xml new file mode 100644 index 0000000..f3df521 --- /dev/null +++ b/stock_inventory_turnover_report/views/product_views.xml @@ -0,0 +1,55 @@ + + + + + + product.product + + + + + + + + + + + + + + + + + + + + + + + product.product + + + + + + + + + + + Inventory Turnover + ir.actions.act_window + product.product + tree,form,pivot,graph + + {'search_default_group_by_categ': True, 'search_default_filter_is_in_stock': True} + + + + + +