From 2b11c57a6253e14c7d4cbf85f2c99a1470e8442b Mon Sep 17 00:00:00 2001 From: Jordi Ballester Alomar Date: Tue, 26 Jun 2018 16:18:58 +0200 Subject: [PATCH 01/42] add ddmrp_report_parts_flow_index --- ddmrp_report_part_flow_index/README.rst | 52 ++++++++++++ ddmrp_report_part_flow_index/__init__.py | 3 + ddmrp_report_part_flow_index/__manifest__.py | 23 ++++++ .../models/__init__.py | 4 + .../models/ddmrp_flow_index_group.py | 17 ++++ .../models/stock_warehouse_orderpoint.py | 13 +++ .../reports/__init__.py | 1 + .../report_ddmrp_part_plan_flow_index.py | 76 ++++++++++++++++++ ...eport_ddmrp_part_plan_flow_index_views.xml | 72 +++++++++++++++++ .../security/ir.model.access.csv | 3 + .../static/description/icon.png | Bin 0 -> 5000 bytes .../views/ddmrp_flow_index_group_views.xml | 60 ++++++++++++++ .../views/stock_warehouse_orderpoint_view.xml | 16 ++++ 13 files changed, 340 insertions(+) create mode 100644 ddmrp_report_part_flow_index/README.rst create mode 100644 ddmrp_report_part_flow_index/__init__.py create mode 100644 ddmrp_report_part_flow_index/__manifest__.py create mode 100644 ddmrp_report_part_flow_index/models/__init__.py create mode 100644 ddmrp_report_part_flow_index/models/ddmrp_flow_index_group.py create mode 100644 ddmrp_report_part_flow_index/models/stock_warehouse_orderpoint.py create mode 100644 ddmrp_report_part_flow_index/reports/__init__.py create mode 100644 ddmrp_report_part_flow_index/reports/report_ddmrp_part_plan_flow_index.py create mode 100644 ddmrp_report_part_flow_index/reports/report_ddmrp_part_plan_flow_index_views.xml create mode 100644 ddmrp_report_part_flow_index/security/ir.model.access.csv create mode 100644 ddmrp_report_part_flow_index/static/description/icon.png create mode 100644 ddmrp_report_part_flow_index/views/ddmrp_flow_index_group_views.xml create mode 100644 ddmrp_report_part_flow_index/views/stock_warehouse_orderpoint_view.xml diff --git a/ddmrp_report_part_flow_index/README.rst b/ddmrp_report_part_flow_index/README.rst new file mode 100644 index 000000000..503b77fcf --- /dev/null +++ b/ddmrp_report_part_flow_index/README.rst @@ -0,0 +1,52 @@ +.. image:: https://img.shields.io/badge/licence-LGPL--3-blue.svg + :alt: License AGPL-3 + +============================= +DDMRP Parts Flow Index Report +============================= + +Implements the Parts Flow Index Report. + +Configuration +============== + +* Go to *Inventory > Configuration > DDMRP > Flow Index Group* to + define possible groups to analyze on. + +* Go to *Inventory > Master Data > Stock Buffer Planning* and assign + the Flow Index Group to Buffers. + +Usage +===== + +Go to *Inventory > Reporting > Parts Plan Flow Index* to run the report. + + +Bug Tracker +=========== + +Bugs are tracked on `GitHub Issues +`_. In case of trouble, please +check there if your issue has already been reported. If you spotted it first, +help us smashing it by providing a detailed and welcomed feedback. + +Roadmap +======= + +* Add Parts Actual Flow Index report + +Credits +======= + +Contributors +------------ + +* Jordi Ballester +* Lois Rilo + + + +Maintainer +---------- + +This module is maintained by the Eficent. diff --git a/ddmrp_report_part_flow_index/__init__.py b/ddmrp_report_part_flow_index/__init__.py new file mode 100644 index 000000000..f1af5e151 --- /dev/null +++ b/ddmrp_report_part_flow_index/__init__.py @@ -0,0 +1,3 @@ +from . import models +from . import reports + diff --git a/ddmrp_report_part_flow_index/__manifest__.py b/ddmrp_report_part_flow_index/__manifest__.py new file mode 100644 index 000000000..5547893f8 --- /dev/null +++ b/ddmrp_report_part_flow_index/__manifest__.py @@ -0,0 +1,23 @@ +# Copyright 2017-18 Eficent Business and IT Consulting Services S.L. +# (http://www.eficent.com) +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). + +{ + "name": "DDMRP Report Parts Flow Index", + "summary": "Provides the DDMRP Parts Flow Index Report", + "version": "11.0.1.0.0", + "author": "Eficent, Odoo Community Association (OCA)", + "website": "https://github.com/OCA/ddmrp", + "category": "Warehouse Management", + "depends": [ + "ddmrp", + ], + "data": [ + "security/ir.model.access.csv", + "reports/report_ddmrp_part_plan_flow_index_views.xml", + "views/ddmrp_flow_index_group_views.xml", + "views/stock_warehouse_orderpoint_view.xml", + ], + "license": "AGPL-3", + 'installable': True, +} diff --git a/ddmrp_report_part_flow_index/models/__init__.py b/ddmrp_report_part_flow_index/models/__init__.py new file mode 100644 index 000000000..b69ec2f3a --- /dev/null +++ b/ddmrp_report_part_flow_index/models/__init__.py @@ -0,0 +1,4 @@ +from . import ddmrp_flow_index_group +from . import stock_warehouse_orderpoint + + diff --git a/ddmrp_report_part_flow_index/models/ddmrp_flow_index_group.py b/ddmrp_report_part_flow_index/models/ddmrp_flow_index_group.py new file mode 100644 index 000000000..c67ff30c1 --- /dev/null +++ b/ddmrp_report_part_flow_index/models/ddmrp_flow_index_group.py @@ -0,0 +1,17 @@ +# Copyright 2018 Eficent Business and IT Consulting Services S.L. +# (http://www.eficent.com) +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). +from odoo import api, fields, models + + +class DdmrpFlowIndexGroup(models.Model): + _name = 'ddmrp.flow.index.group' + + name = fields.Char('Name', required=True) + summary = fields.Text('Summary') + active = fields.Boolean(default=True) + + @api.multi + def toggle_active(self): + for record in self: + record.active = not record.active diff --git a/ddmrp_report_part_flow_index/models/stock_warehouse_orderpoint.py b/ddmrp_report_part_flow_index/models/stock_warehouse_orderpoint.py new file mode 100644 index 000000000..fe859c708 --- /dev/null +++ b/ddmrp_report_part_flow_index/models/stock_warehouse_orderpoint.py @@ -0,0 +1,13 @@ +# Copyright 2016-18 Eficent Business and IT Consulting Services S.L. +# (http://www.eficent.com) +# Copyright 2016 Aleph Objects, Inc. (https://www.alephobjects.com/) +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). + +from odoo import fields, models + + +class StockWarehouseOrderpoint(models.Model): + _inherit = 'stock.warehouse.orderpoint' + + flow_index_group_id = fields.Many2one( + 'ddmrp.flow.index.group', string="Flow Index Group") diff --git a/ddmrp_report_part_flow_index/reports/__init__.py b/ddmrp_report_part_flow_index/reports/__init__.py new file mode 100644 index 000000000..82734997c --- /dev/null +++ b/ddmrp_report_part_flow_index/reports/__init__.py @@ -0,0 +1 @@ +from . import report_ddmrp_part_plan_flow_index diff --git a/ddmrp_report_part_flow_index/reports/report_ddmrp_part_plan_flow_index.py b/ddmrp_report_part_flow_index/reports/report_ddmrp_part_plan_flow_index.py new file mode 100644 index 000000000..3e986e009 --- /dev/null +++ b/ddmrp_report_part_flow_index/reports/report_ddmrp_part_plan_flow_index.py @@ -0,0 +1,76 @@ +# Copyright 2018 Eficent Business and IT Consulting Services S.L. +# (http://www.eficent.com) +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). +from odoo import api, fields, models, tools +from odoo.addons import decimal_precision as dp +UNIT = dp.get_precision('Product Unit of Measure') + + +class ReportDdmrpPartsPlanFlowIndex(models.Model): + _name = 'report.ddmrp.part.plan.flow.index' + _auto = False + + orderpoint_id = fields.Many2one('stock.warehouse.orderpoint', + string='Buffer', + readonly=True) + product_id = fields.Many2one('product.product', + string='Product', + readonly=True) + location_id = fields.Many2one('stock.location', + string='Location', + readonly=True) + adu = fields.Float(string="Average Daily Usage (ADU)", + default=0.0, digits=UNIT, readonly=True) + green_zone_qty = fields.Float(string="Green Zone Qty", + digits=UNIT, readonly=True) + order_frequency = fields.Float(string="Order Frequency", + digits=UNIT, readonly=True) + order_frequency_group = fields.Integer(string="Order Frequency Group", + readonly=True) + order_frequency_group_count = fields.Integer( + string="Order Frequency Group Count", readonly=True) + flow_index_group_id = fields.Many2one( + 'ddmrp.flow.index.group', string="Flow Index Group", readonly=True) + + @api.model_cr + def init(self): + tools.drop_view_if_exists(self._cr, + 'report_ddmrp_part_plan_flow_index') + self._cr.execute( + """ + CREATE or REPLACE VIEW report_ddmrp_part_plan_flow_index AS ( + WITH a AS + (SELECT + id, + product_id, + location_id, + adu, + flow_index_group_id, + green_zone_qty, + green_zone_qty/NULLIF(adu, 0) as order_frequency, + round(green_zone_qty/NULLIF(adu, 0)) AS + order_frequency_group + FROM stock_warehouse_orderpoint + ) + SELECT + a.id as id, + a.id as orderpoint_id, + a.product_id as product_id, + a.location_id as location_id, + a.adu as adu, + a.flow_index_group_id as flow_index_group_id, + a.green_zone_qty as green_zone_qty, + a.order_frequency as order_frequency, + a.order_frequency_group as order_frequency_group, + b.order_frequency_group_count + FROM a + JOIN (SELECT + order_frequency_group, count(*) AS + order_frequency_group_count + FROM a + GROUP BY order_frequency_group + ) AS b + ON a.order_frequency_group = b.order_frequency_group + ) + """ + ) diff --git a/ddmrp_report_part_flow_index/reports/report_ddmrp_part_plan_flow_index_views.xml b/ddmrp_report_part_flow_index/reports/report_ddmrp_part_plan_flow_index_views.xml new file mode 100644 index 000000000..86ec37163 --- /dev/null +++ b/ddmrp_report_part_flow_index/reports/report_ddmrp_part_plan_flow_index_views.xml @@ -0,0 +1,72 @@ + + + + + view.ddmrp.part.plan.flow.index.pivot + report.ddmrp.part.plan.flow.index + + + + + + + + + + view.ddmrp.part.plan.flow.index.pivot.graph + report.ddmrp.part.plan.flow.index + + + + + + + + + view.ddmrp.part.plan.flow.index.pivot.tree + report.ddmrp.part.plan.flow.index + + + + + + + + + + + + + + + + + view.ddmrp.part.plan.flow.index.pivot.filter + report.ddmrp.part.plan.flow.index + + + + + + + + + + + + + Parts Plan Flow Index + report.ddmrp.part.plan.flow.index + form + graph,tree,pivot + + + + + + + diff --git a/ddmrp_report_part_flow_index/security/ir.model.access.csv b/ddmrp_report_part_flow_index/security/ir.model.access.csv new file mode 100644 index 000000000..08d581313 --- /dev/null +++ b/ddmrp_report_part_flow_index/security/ir.model.access.csv @@ -0,0 +1,3 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink +access_ddmrp_flow_index_group,ddmrp.flow.index.group,model_ddmrp_flow_index_group,stock.group_stock_manager,1,1,1,1 +access_report_ddmrp_part_plan_flow_index,ddmrp.part.plan.flow.index,model_report_ddmrp_part_plan_flow_index,stock.group_stock_manager,1,1,1,1 diff --git a/ddmrp_report_part_flow_index/static/description/icon.png b/ddmrp_report_part_flow_index/static/description/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..a0b0ed73ac91bdb4ca8c2269d6c63bb8588f5d6d GIT binary patch literal 5000 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 C40@jc literal 0 HcmV?d00001 diff --git a/ddmrp_report_part_flow_index/views/ddmrp_flow_index_group_views.xml b/ddmrp_report_part_flow_index/views/ddmrp_flow_index_group_views.xml new file mode 100644 index 000000000..5e4a8caca --- /dev/null +++ b/ddmrp_report_part_flow_index/views/ddmrp_flow_index_group_views.xml @@ -0,0 +1,60 @@ + + + + + + ddmrp.flow.index.group.tree + ddmrp.flow.index.group + + + + + + + + + ddmrp.flow.index.group.form + ddmrp.flow.index.group + +
+ +
+ +
+ + + + + + +
+
+
+
+ + + ddmrp.flow.index.group.search + ddmrp.flow.index.group + + + + + + + + + DDMRP Flow Index Group + ddmrp.flow.index.group + form + tree,form + + + + +
diff --git a/ddmrp_report_part_flow_index/views/stock_warehouse_orderpoint_view.xml b/ddmrp_report_part_flow_index/views/stock_warehouse_orderpoint_view.xml new file mode 100644 index 000000000..e62a1799b --- /dev/null +++ b/ddmrp_report_part_flow_index/views/stock_warehouse_orderpoint_view.xml @@ -0,0 +1,16 @@ + + + + + stock.warehouse.orderpoint.form + stock.warehouse.orderpoint + + + + + + + + + From 0fa6b71f7d941328378ecf4979da7a376f28fbd8 Mon Sep 17 00:00:00 2001 From: Lois Rilo Date: Wed, 1 Aug 2018 11:01:28 +0200 Subject: [PATCH 02/42] [11.0] ddmrp_report_part_flow_index: update readme and icon --- ddmrp_report_part_flow_index/README.rst | 103 +++- ddmrp_report_part_flow_index/__manifest__.py | 4 +- .../readme/CONFIGURE.rst | 4 + .../readme/CONTRIBUTORS.rst | 2 + .../readme/DESCRIPTION.rst | 1 + .../readme/HISTORY.rst | 4 + .../readme/ROADMAP.rst | 1 + ddmrp_report_part_flow_index/readme/USAGE.rst | 1 + .../report_ddmrp_part_plan_flow_index.py | 74 +-- .../static/description/icon.png | Bin 5000 -> 2440 bytes .../static/description/index.html | 457 ++++++++++++++++++ 11 files changed, 601 insertions(+), 50 deletions(-) create mode 100644 ddmrp_report_part_flow_index/readme/CONFIGURE.rst create mode 100644 ddmrp_report_part_flow_index/readme/CONTRIBUTORS.rst create mode 100644 ddmrp_report_part_flow_index/readme/DESCRIPTION.rst create mode 100644 ddmrp_report_part_flow_index/readme/HISTORY.rst create mode 100644 ddmrp_report_part_flow_index/readme/ROADMAP.rst create mode 100644 ddmrp_report_part_flow_index/readme/USAGE.rst create mode 100644 ddmrp_report_part_flow_index/static/description/index.html diff --git a/ddmrp_report_part_flow_index/README.rst b/ddmrp_report_part_flow_index/README.rst index 503b77fcf..6f3647f5e 100644 --- a/ddmrp_report_part_flow_index/README.rst +++ b/ddmrp_report_part_flow_index/README.rst @@ -1,18 +1,42 @@ -.. image:: https://img.shields.io/badge/licence-LGPL--3-blue.svg - :alt: License AGPL-3 - -============================= -DDMRP Parts Flow Index Report -============================= +============================ +DDMRP Report Part Flow Index +============================ + +.. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png + :target: https://odoo-community.org/page/development-status + :alt: Beta +.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png + :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html + :alt: License: AGPL-3 +.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fddmrp-lightgray.png?logo=github + :target: https://github.com/OCA/ddmrp/tree/11.0/ddmrp_report_part_flow_index + :alt: OCA/ddmrp +.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png + :target: https://translation.odoo-community.org/projects/ddmrp-11-0/ddmrp-11-0-ddmrp_report_part_flow_index + :alt: Translate me on Weblate +.. |badge5| image:: https://img.shields.io/badge/runbot-Try%20me-875A7B.png + :target: https://runbot.odoo-community.org/runbot/255/11.0 + :alt: Try me on Runbot + +|badge1| |badge2| |badge3| |badge4| |badge5| Implements the Parts Flow Index Report. +**Table of contents** + +.. contents:: + :local: + Configuration -============== +============= * Go to *Inventory > Configuration > DDMRP > Flow Index Group* to define possible groups to analyze on. - * Go to *Inventory > Master Data > Stock Buffer Planning* and assign the Flow Index Group to Buffers. @@ -21,32 +45,67 @@ Usage Go to *Inventory > Reporting > Parts Plan Flow Index* to run the report. +Known issues / Roadmap +====================== + +* Add Parts Actual Flow Index report + +Changelog +========= + +11.0.1.0.0 (2018-08-01) +~~~~~~~~~~~~~~~~~~~~~~~ + +* Start of the history Bug Tracker =========== -Bugs are tracked on `GitHub Issues -`_. In case of trouble, please -check there if your issue has already been reported. If you spotted it first, -help us smashing it by providing a detailed and welcomed feedback. +Bugs are tracked on `GitHub Issues `_. +In case of trouble, please check there if your issue has already been reported. +If you spotted it first, help us smashing it by providing a detailed and welcomed +`feedback `_. -Roadmap -======= - -* Add Parts Actual Flow Index report +Do not contact contributors directly about support or help with technical issues. Credits ======= +Authors +~~~~~~~ + +* Eficent + Contributors ------------- +~~~~~~~~~~~~ + +* Jordi Ballester Alomar +* Lois Rilo Antelo + +Maintainers +~~~~~~~~~~~ + +This module is maintained by the OCA. + +.. image:: https://odoo-community.org/logo.png + :alt: Odoo Community Association + :target: https://odoo-community.org + +OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use. -* Jordi Ballester -* Lois Rilo +.. |maintainer-jbeficent| image:: https://github.com/jbeficent.png?size=40px + :target: https://github.com/jbeficent + :alt: jbeficent +.. |maintainer-lreficent| image:: https://github.com/lreficent.png?size=40px + :target: https://github.com/lreficent + :alt: lreficent +Current `maintainers `__: +|maintainer-jbeficent| |maintainer-lreficent| -Maintainer ----------- +This module is part of the `OCA/ddmrp `_ project on GitHub. -This module is maintained by the Eficent. +You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/ddmrp_report_part_flow_index/__manifest__.py b/ddmrp_report_part_flow_index/__manifest__.py index 5547893f8..b82ab0165 100644 --- a/ddmrp_report_part_flow_index/__manifest__.py +++ b/ddmrp_report_part_flow_index/__manifest__.py @@ -3,10 +3,12 @@ # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). { - "name": "DDMRP Report Parts Flow Index", + "name": "DDMRP Report Part Flow Index", "summary": "Provides the DDMRP Parts Flow Index Report", "version": "11.0.1.0.0", + "development_status": "Beta", "author": "Eficent, Odoo Community Association (OCA)", + "maintainers": ['jbeficent', 'lreficent'], "website": "https://github.com/OCA/ddmrp", "category": "Warehouse Management", "depends": [ diff --git a/ddmrp_report_part_flow_index/readme/CONFIGURE.rst b/ddmrp_report_part_flow_index/readme/CONFIGURE.rst new file mode 100644 index 000000000..8da774ab6 --- /dev/null +++ b/ddmrp_report_part_flow_index/readme/CONFIGURE.rst @@ -0,0 +1,4 @@ +* Go to *Inventory > Configuration > DDMRP > Flow Index Group* to + define possible groups to analyze on. +* Go to *Inventory > Master Data > Stock Buffer Planning* and assign + the Flow Index Group to Buffers. diff --git a/ddmrp_report_part_flow_index/readme/CONTRIBUTORS.rst b/ddmrp_report_part_flow_index/readme/CONTRIBUTORS.rst new file mode 100644 index 000000000..6efb8d10d --- /dev/null +++ b/ddmrp_report_part_flow_index/readme/CONTRIBUTORS.rst @@ -0,0 +1,2 @@ +* Jordi Ballester Alomar +* Lois Rilo Antelo diff --git a/ddmrp_report_part_flow_index/readme/DESCRIPTION.rst b/ddmrp_report_part_flow_index/readme/DESCRIPTION.rst new file mode 100644 index 000000000..963881e25 --- /dev/null +++ b/ddmrp_report_part_flow_index/readme/DESCRIPTION.rst @@ -0,0 +1 @@ +Implements the Parts Flow Index Report. diff --git a/ddmrp_report_part_flow_index/readme/HISTORY.rst b/ddmrp_report_part_flow_index/readme/HISTORY.rst new file mode 100644 index 000000000..f0a4865e9 --- /dev/null +++ b/ddmrp_report_part_flow_index/readme/HISTORY.rst @@ -0,0 +1,4 @@ +11.0.1.0.0 (2018-08-01) +~~~~~~~~~~~~~~~~~~~~~~~ + +* Start of the history diff --git a/ddmrp_report_part_flow_index/readme/ROADMAP.rst b/ddmrp_report_part_flow_index/readme/ROADMAP.rst new file mode 100644 index 000000000..0b953d107 --- /dev/null +++ b/ddmrp_report_part_flow_index/readme/ROADMAP.rst @@ -0,0 +1 @@ +* Add Parts Actual Flow Index report diff --git a/ddmrp_report_part_flow_index/readme/USAGE.rst b/ddmrp_report_part_flow_index/readme/USAGE.rst new file mode 100644 index 000000000..26595d094 --- /dev/null +++ b/ddmrp_report_part_flow_index/readme/USAGE.rst @@ -0,0 +1 @@ +Go to *Inventory > Reporting > Parts Plan Flow Index* to run the report. diff --git a/ddmrp_report_part_flow_index/reports/report_ddmrp_part_plan_flow_index.py b/ddmrp_report_part_flow_index/reports/report_ddmrp_part_plan_flow_index.py index 3e986e009..0d03836fb 100644 --- a/ddmrp_report_part_flow_index/reports/report_ddmrp_part_plan_flow_index.py +++ b/ddmrp_report_part_flow_index/reports/report_ddmrp_part_plan_flow_index.py @@ -1,4 +1,4 @@ -# Copyright 2018 Eficent Business and IT Consulting Services S.L. +# Copyright 2018-19 Eficent Business and IT Consulting Services S.L. # (http://www.eficent.com) # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). from odoo import api, fields, models, tools @@ -32,45 +32,65 @@ class ReportDdmrpPartsPlanFlowIndex(models.Model): flow_index_group_id = fields.Many2one( 'ddmrp.flow.index.group', string="Flow Index Group", readonly=True) + @api.model + def _sub_select(self): + select_str = """ + id, + product_id, + location_id, + adu, + flow_index_group_id, + green_zone_qty, + green_zone_qty/NULLIF(adu, 0) as order_frequency, + round(green_zone_qty/NULLIF(adu, 0)) AS + order_frequency_group + """ + return select_str + + @api.model + def _select(self): + select_str = """ + a.id as id, + a.id as orderpoint_id, + a.product_id as product_id, + a.location_id as location_id, + a.adu as adu, + a.flow_index_group_id as flow_index_group_id, + a.green_zone_qty as green_zone_qty, + a.order_frequency as order_frequency, + a.order_frequency_group as order_frequency_group, + b.order_frequency_group_count + """ + return select_str + + @api.model + def _join_select(self): + select_str = """ + order_frequency_group, + count(*) AS order_frequency_group_count + """ + return select_str + @api.model_cr def init(self): tools.drop_view_if_exists(self._cr, 'report_ddmrp_part_plan_flow_index') self._cr.execute( """ - CREATE or REPLACE VIEW report_ddmrp_part_plan_flow_index AS ( + CREATE or REPLACE VIEW %s AS ( WITH a AS - (SELECT - id, - product_id, - location_id, - adu, - flow_index_group_id, - green_zone_qty, - green_zone_qty/NULLIF(adu, 0) as order_frequency, - round(green_zone_qty/NULLIF(adu, 0)) AS - order_frequency_group - FROM stock_warehouse_orderpoint - ) + (SELECT %s + FROM stock_warehouse_orderpoint) SELECT - a.id as id, - a.id as orderpoint_id, - a.product_id as product_id, - a.location_id as location_id, - a.adu as adu, - a.flow_index_group_id as flow_index_group_id, - a.green_zone_qty as green_zone_qty, - a.order_frequency as order_frequency, - a.order_frequency_group as order_frequency_group, - b.order_frequency_group_count + %s FROM a JOIN (SELECT - order_frequency_group, count(*) AS - order_frequency_group_count + %s FROM a GROUP BY order_frequency_group ) AS b ON a.order_frequency_group = b.order_frequency_group ) - """ + """ % (self._table, self._sub_select(), self._select(), + self._join_select()) ) diff --git a/ddmrp_report_part_flow_index/static/description/icon.png b/ddmrp_report_part_flow_index/static/description/icon.png index a0b0ed73ac91bdb4ca8c2269d6c63bb8588f5d6d..f95fc7269c40d169477766bae2f39ae6eccee046 100644 GIT binary patch literal 2440 zcmb7G`9Bl>AK!*7=E@y0N{-xeN!q zEh&nRWNdC9Ifu!yk7J+hPx!tckJsz8up#Eyyq000RWgo779 z;spji#P=W027l!TVT`?t512nvz}N5c=Z9ku{uls29VIZ}>)3zd`AKE0qaW5gIt&{h z9E%3T$HyDqjEca72FIWcqhrHM*tSRcLJ=2-b3O^BD`j83Rh-rEu*1j8Ek=TjB@H*~ z-Uhhmv0_K+-gIyqn|2;y>b8s+)R5H?oJN-2sz#RW&y$UoP?ZqE0|_gowBBp;CJ&vG zJ%NjA8G&YYME0r$^=M7<@7?+G;&OJj-RMEQNP6SP{d#;SiU4>LD%`Jn96zT7Jlzd` zdd3ORX9BvBBLm0$|Ij!2CP^%A%?PrITl_f%Dat){>>YVK{Zw~swTPd4Jtriw^TB!L zCzG_%xD~%+;^a~!8oh3S>8W77AU-?n)nY!JGn5CY?Ga_zAwuO1uIQm(5Ce4cjlz*O zfy`mRUp+9b%|G**mbKp+n;6r{lxS+)A9{jKgW`g_Aa-!prEKCQX zM)-yW7qCHv$uvgCq@O!uMcKEZYrq8+WF$GaAQeJtRBoto zo9covNY-YR{yBiLaJy<9XWCPUaZiA zyb@k)bq4yE?I|O+BgbQ8RHg!1EZ+I)Gj+gafi}poT`54l-a8pdG-KZwhx|hRxX7Lx zJ9bdG?t>4%Pps~mb~9Z-=RFI%r`w&$agX{iXlf@<)APUnwp+0LW*)AjGJsYyd$YKF zqUcR@_OOx@{@>4vJ%JG3mu3s2r)pG#zIASIQ8;tB<(p+n`bsCw>2bDf_~T%WxuKbf ztG0@h+e5#6^eh?}iJ_ra(?0pUAfwjUz<%9waR3?jzPt9kdHT7Z)~fr?jssIuqe6&M z81S_uFNt^rI3C$P{c7YWu=Q-(b9KN*OQoL4&fs~C*c;V_1+QnP+V}h(*J~hHzL&68 z2v+@p_YZQ}275i)z*QJgZK+#W%`B!E`N$oUteWVmXViNDUS_?WYAFj?xYbDUzw9Vl zk$MS_0rPT5axBEM`D(-SX2{d@U|xm*_I`(>B4v-{zn>y@Zw^JLaW`rPS&ZOt;P9X-(Bm%Bvdb$qq zk7xv{+HTT)R7KChwyMNHc#nDtbC#Bu4Jds-SqC~76Z#kIJ*|f$UB>v_XpsJ!lXUp1 zUH1#W6WLmI$MNO~4O<@XbP~1KH0K_|HYUI0Y@|N622Z0B8YJDxcWuVg89gMfw|t5m z6!rPx*YJg<6`0hg-KR&^6&*vT$K3z7?#xpd*}4e8Z@)BWyN$!7s=U_5;twt(L?%+j zLqo$$ivK>)vPmYAF2tQEs<@ZdV4|e7R%@y^znY~oJu`tlkgCx0re6Un1lnQr^?H0V zDn#8pY+CWe#J^^ErWCOtriHWeg2dwmu!lPbvEUID$g*5tmi)a}F{Q`1(>A%+? zpQ1R~vr$2(^-DvfQ06blL0PeIG_i|)f3Z?A#? z%j^rX8s^ea6eoP=%1LQZ`_*elg!1#Hr5uJ>8VawsP_7$P7{GA7;PJ8Y1RiVX@90e} z(J%(-Q_2;Enxf8WPI;hNRc&pfB0tDWoF<+%AG7t}BPq=<-p&T6U}9q5CK;Mn-AdSR zQ%jSfy%j4(OsEnZETLuR2O4!o#tjb`Vrepfpz~LKOpkziU!eH62WipvzM|-~c^$}| zgsrtnD1iWeh7DYq{q7{}imxuL!X#MrzM&EJZe`qCQpd3i;D>`kF){k)dBFTAm>MMoIcJ1)9m;tFZNMUvMda|2}=B4ZX@qJuOEd_$&pUm%f8TgS2ULTZhnz zmwJpFL0&x6O9q7sNRmmD1q6kA9Mu(n4)7|&pm8;+=9;;n_9X38j4}4ZhjlBK!m$1L z#u^h>Ho4S}PqGc%$)l!jPTJ-oXk%?di9*3ak&vV+y81go*MMnqlk>B=w+m~vFPYsr zSPR~Vfvo~@f_5R*OGoD)aK@X6KOKP zzduufBko|4yyk5>Bt9(tmbeN-0lJ)_!3`2#%OIanCjjbYYi84BXqUSlr5J;nos{yW zoB&tA5u8qNze%wBStX#g=5mkdaX8aKAi8n?g+OE=Tg?|e)v6STPG6Mci|{pf{}70B zb+q|}q9dCEf}yp5typ=1k4(aA`6)QOiSH*N#{~PpCj^Ry)+`REG-R6m(snofWRW%u%LkMBBhJu(jX-uh;&E^ zOD`Rd-+%ATyfbs|=ghr#?#wxJ@8^7O98^c0ikyuc0063I8Y-|Gw7zjGQsSGDrLG+U z0OZO}%F57Z%F5i(Hy-v*uCD;Ve=1+!&m{GjS$_Wb6^)V76{OLB=Oxf3vz#Sa!{P%g& zTHe~lP{1$^oQ_^FWfkcRNWr3Ir6;+Pbu~Ksn&Dt3jRit(+SUGc2cj+j*d++?5B086 zxBz|e0KkCf_(OjBp2h3P2B@Ghz?}jt@Y1hQfDkaC_n|>ABhbqVRO#2gBnQ#}z%?LP zi5K`p1#BHYlP3ZuD^h1zfJx--3Q`~u1Z3Y;=LbU?0c*o>*aI-O9VkLUcpnmCD?xD6 zZ({0Tcry{;n`DBZfmj3ZY^4VS0QXZcpv=iINWiT{P$W9qx=>-|8C|ad0@)TBW+ht6%cEye-~Hn z*IaiFyO38dyB@sqJbUw3P4B>j0IkmO&Xm>4$^?OD52(tg)uqA=m@)z!=T9xfr^$+5 zkpB!kO$c~;0pTnthW5&G+$N`>zzNtEH;XPO$X|;udMqS_9^I^7O$Y7*uX7;TXlzO3 zVPM|g!^#vfI2g2{zC{h4g2MMu&V*p5PJxzuP zfq=ox(q#Z>T4yycPh-*QCItYMqVW4wiX@b6j3_L1UK>XfmIZ7>tOB9r$I8+w5&wnX zCNE~qP`qV;m7XO0T1CKCM3=KjTi5pJDb?Bny?OYiCkwGPYe73}Qw$lQVlOF!4UhC& zI@_d4-XT(!>?A^78x4u~@KWxLw~TtcUA=Eg!4*o*DHa-h`@(M+r4r2{XNc)uZn5}_ z_$Kv7Y2h_`1i!#OkniPOZ1LSiUL)QTG#2m-q{|3f9j6@p8?tQV`MQrc5E7qc2OoJv zZU63%jrqG+RqHqtHo+cdyA=~jqhv`t4wEOoOzhYKn;lYqZ~~lRi_|iTw%s)XflxQm zGEpuU*y5|BZy+@$i;Q~Ieup=;T(?djkp8sO-&VRBVWNf-+~(orDG;!{L(O27s=-g) z_x`UAIgbEB>>0%~7maBR;Zprl`B7S@JT1XZgj?U-U(y;liaRp}gP=j^zCZ8l_={nb z?s~hVMTX>um8Q{aO1@H9A^cD%rj%XuNpG;C;1}tqx6*|#p1BqILPhjrD;Okvas&o* znU(uXg-gbN<~TVx9XpC_csG#EMsliV{>&V8Str?8+lV~$qoj(VitN#_y?e{$R?w|7 zR?IDtY^{$(W0~2#2BN>k*s~q7XtF#EP3|Av4`xdFL7y(2Zj>IHzV^w$(92L^Jm3?B z!DmC7&z43ZAJq(Nq01FyznDLdS8y0MRy?j8g89Lcp~HGsB`sE$5odN(H8Ig@MKKyB z26mY4TZuf(-D0oyOq3?x7!hnfaLLlPHLE$@(()xbXZIBe($ZJ<`ecu?PqEKmeLV=G z?yg?gmHLyXIm9zWH54RFAgmxiVDM~9-HOa)H0Oxfbz;FxinQ#(GM`H=hv`FLqJ z>0=+VhQ|$h4bctLKEway$Lux(HpuWl@YEZ#BkFj!!xB8pznzJl5w0hq5~Pn2w_G1R zwc8y)*sZ%{In|HsMlF`p^*c7CbUK!dJS(|VFSs4+uq^4%B5#lKxNVD)n0qzjB5Pyo zBsN{zS=#lt<=$YLOOAcRNZpwCz(DVV8Pp`IZ!1Y^JkO=TrsYr5^hILy2i3eT&4kAZ zJsDFO!Tm%T+8M{FEF*cN03*E7Z`891^UVjFZWG3?P7%3SH({%_^zTy&>zuG>80 z5@Yhc(R+A(_o+u)7hB|8Ia|aysUw>sfg`u0VUB^@^c)j1-ZH^&ynP0IT|JvNOBW?N zDmp7VHjEwE9g4m34-EourRjeV+-%RTnk_5L}+#>$wk4r%vt|s@=5Drz&`Vy z4v{se4YUGk4rdFmC2%3&BFrZIKq5($NYqFyLMhLrL`g8qkEdE21K&vBE~vY{t><#OmCbS*!g%k#*r$j8 z^y>7zUwa=vm_YqCJ$GnM+Doo@_g!Y%`P&;`2b8lb%++oyw;o>qsXp94_C$3rlir(w zp9~lGw{x;nE^gTiyyluCp7%G$Aj8|Pa%Bejon>`nsK3BZ)>P6I5&Sedi$1ePVI`7V z2`a*>yHe(^&B3rAl^dQ!UvHD%wg<{PCSJ)M;aQ9g5w(N**57AMn%2?eCw zdjFm8K-)-VVh%O@=k*_9skFSbyb>6dQ%0T8Z&A!$jIOwHN5n9+8qt)ckHjpxP&g5zzp}D17`Mjr-e5=h!e;>-rVp{DnX zAD0{j=7(AI8(pG5V_NNCeF!2sQr+2f_V#d{`O?~2L*@p1oXDvm?YWW*_n6AX<@kN|d~u(; zU2&!D&iu#Vpk?TK@leN1$KZT+7zR(esSzMFD&6XX5-xEok}GcBS$|iO&tb`--h#Of z-FdWZ=hGvZY+O*3e^+kr^lwD!a@otWf_xnXxnJ2c0moVw*)A4(7R?UI!vzW~-rHIj ztK+_dxwT}A-6qdN-ecQo0nBr>T&0|PsOROP#uqb>%~!|$IAhsL??dfPnUS5burp`u zxYzHK-w!Pc1Dbcbx7ac6g0r{l1sd=tzDGC~Kk{v@8H>PV|1TFUQG; z!n!S-SYxe+J4|VIsL5gruF|f@Vi!l%2h_*;)c6vU^SQX#so0wLUk;@%merIo4F3o< zx(?X%`n)!Oxpe+~*yLhzm*O;GxV18%;^58B=ZY`VJ$yY}O+x{4dmk^*L(?HU^*aso zJwtT0|kL&Z~S$Y4Tx%Zg))Qv*kMC-@iO^K<}KP=g-iI$WNc*4ccBJ zFc2^TDCGypAe#R-fY+orwO%8%1hWeY?A_gU=FlPD3s_J@8~5W}-1W7950{&}`=`u@ z7BEl%&MQEc(zSG5T2O!;ot=>wOy>FVP9#`@ zK>KZBzt594!X{7);gWUjA65>I!K>TAg12;>S_v2qVkHc%xLnrx&L;24b&?H0Ei48n zCr#V|10y3bSy|NJk13KKASfFw3J77X!hQB&At7?j&CTB_lrKXSVyQXk69pC)InVE5 zHYUnJC%F>|+JfM4AU!?(JHgFa`A79nu=D-?-z34Gu}_d2zKv3T+ve^pw!V$|8vML$ z(^I6A6M{=`Y73w43h#=|O3BE`cu`jBm;F<+q^y+04ULUb{=3%O z`gHD?-0<-5Ix20a0u!AQY^in=jmp@fr>MMqL7_RKV^Jgag~wQPktbB_UOT8n`X|TiJI9ouBAp96U zC{|slZLL$wgI(V9JX{~S0E@Cx9v&UREG&dA{Xw!1Q|DS8@GhdP>swo$?wBbzi?S5d#nx7aAxw1?ejOWpv6ozXcy(+2N4ij>>Hd-LuMYwShQer}Y{=#K7rjBM4UX_(`SWPD ztR_7`KhY$WWMIAv|iCQB~O45jUNz}8?=_p;~C%|Gpf*MXn!LZMgHh30cb zK!J@FiNq&}J-IJdAZY&KL87&-HOipQ;x^skMABBKCrSltux+o|_O_dI?o3H$z3T*YbK0fHX z!!_~~S|$P*eNKmPS8Zln$w#5lORcAU{93~hSu+04?jU~pwwGcX6maC-Lp*+Da*`A~ zN*|0j+#@itj9|yK83;`A71K2|G}JskwWosIXNyL?*%kl0tNC+Ld&*?*CMUNwc>8*Y$N##kyk$EQu#XVi=yJ<;;yc5^=P%z z2N~NQCbp4^SYq+)wDYuWs>a5~I>SA$nBcSS4&L5r{h1;R32i%}G-LBljjXBw);4@? zcwv=_o7&LFFi$S%amf!%6Ihfjk&=>f;f-N+P0c`|pmhkCQWaK-1Sqg-Vsc+jgIn@W zTRmeQ2r(yAI1J_Kf$#*-!uTO8iQgWIl5Cwt&7O-94J|E|t1we8Go3OoztmO9-B{cB zP{w-us8XXB4CK*sn={63NN(C5e8-}wU`a~Ag7YH~ASj8^V~k#1U7eYtkqR_X_wo`C zMcZGIA(6T}Q=o$Ks>;(a3AVxV?baf|{2|No$X#wt^&LhKoQcStwXNLeWUNfL^}vqA zn*eKHOE*gG8%7gsNx4BnBtO4=ee))zs1&@&A$p8A{I=&W%TL=iHZ4uEdr5uqyta@} zJ*nw;h2Jay=!vIk4hh!=m8)b<0q*ypiqdB{ySu2cH@qh%2FA+ns^?D+OY*tiQ9Wy0 zJJ7qa8DPL({DxPu2JMZ)pK%bF0Gy7g9zj7e%gf7BGzwnSK&Mws68@NA>1CK!>=T7J z|9|Znk57rgpo`G3IeCRLAWvtlk_!FrR}AbZ^1hmzDC@w_>yXb~zpp1@APB%5MsW#DM zz|QHf2ua7qWH8n{(;khDj<$CtupGE61Q6U@^IM#pYI=;8JT)a`D%35sndHGFTf#Ql zA+5ne9=*+$B5rPO14BdmwDpuGz1MALzw}8s%d65En-xCcEEtsn2%tBO<>cgPZEdYA zNoZw>qvNp?;sYf&-m@_-j_ft~g&sK@)xDhSZy3z7ZzILA-2Q2g?C*(*tn6$>!|{j{ zTv+O$ea9w#Rrt~z3c!fsBmO^q@V^xU7;(S+ih!9_tt?a_#^gT*^h{MprBcZ{;(q`g C40@jc diff --git a/ddmrp_report_part_flow_index/static/description/index.html b/ddmrp_report_part_flow_index/static/description/index.html new file mode 100644 index 000000000..9ae9cec49 --- /dev/null +++ b/ddmrp_report_part_flow_index/static/description/index.html @@ -0,0 +1,457 @@ + + + + + + +DDMRP Report Part Flow Index + + + +
+

DDMRP Report Part Flow Index

+ + +

Beta License: AGPL-3 OCA/ddmrp Translate me on Weblate Try me on Runbot

+

Implements the Parts Flow Index Report.

+

Table of contents

+ +
+

Configuration

+
    +
  • Go to Inventory > Configuration > DDMRP > Flow Index Group to +define possible groups to analyze on.
  • +
  • Go to Inventory > Master Data > Stock Buffer Planning and assign +the Flow Index Group to Buffers.
  • +
+
+
+

Usage

+

Go to Inventory > Reporting > Parts Plan Flow Index to run the report.

+
+
+

Known issues / Roadmap

+
    +
  • Add Parts Actual Flow Index report
  • +
+
+
+

Changelog

+
+

11.0.1.0.0 (2018-08-01)

+
    +
  • Start of the history
  • +
+
+
+
+

Bug Tracker

+

Bugs are tracked on GitHub Issues. +In case of trouble, please check there if your issue has already been reported. +If you spotted it first, help us smashing it by providing a detailed and welcomed +feedback.

+

Do not contact contributors directly about support or help with technical issues.

+
+
+

Credits

+
+

Authors

+
    +
  • Eficent
  • +
+
+
+

Contributors

+ +
+
+

Maintainers

+

This module is maintained by the OCA.

+Odoo Community Association +

OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use.

+

Current maintainers:

+

jbeficent lreficent

+

This module is part of the OCA/ddmrp project on GitHub.

+

You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

+
+
+
+ + From b4c19008a62ca8ce669c5c8444f311615e1a9bc1 Mon Sep 17 00:00:00 2001 From: oca-travis Date: Thu, 7 Mar 2019 20:07:18 +0000 Subject: [PATCH 03/42] [UPD] Update ddmrp_report_part_flow_index.pot --- .../i18n/ddmrp_report_part_flow_index.pot | 150 ++++++++++++++++++ 1 file changed, 150 insertions(+) create mode 100644 ddmrp_report_part_flow_index/i18n/ddmrp_report_part_flow_index.pot diff --git a/ddmrp_report_part_flow_index/i18n/ddmrp_report_part_flow_index.pot b/ddmrp_report_part_flow_index/i18n/ddmrp_report_part_flow_index.pot new file mode 100644 index 000000000..643313937 --- /dev/null +++ b/ddmrp_report_part_flow_index/i18n/ddmrp_report_part_flow_index.pot @@ -0,0 +1,150 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * ddmrp_report_part_flow_index +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 11.0\n" +"Report-Msgid-Bugs-To: \n" +"Last-Translator: <>\n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: \n" + +#. module: ddmrp_report_part_flow_index +#: model:ir.model.fields,field_description:ddmrp_report_part_flow_index.field_ddmrp_flow_index_group_active +msgid "Active" +msgstr "" + +#. module: ddmrp_report_part_flow_index +#: model:ir.model.fields,field_description:ddmrp_report_part_flow_index.field_report_ddmrp_part_plan_flow_index_adu +msgid "Average Daily Usage (ADU)" +msgstr "" + +#. module: ddmrp_report_part_flow_index +#: model:ir.model.fields,field_description:ddmrp_report_part_flow_index.field_report_ddmrp_part_plan_flow_index_orderpoint_id +msgid "Buffer" +msgstr "" + +#. module: ddmrp_report_part_flow_index +#: model:ir.model.fields,field_description:ddmrp_report_part_flow_index.field_ddmrp_flow_index_group_create_uid +msgid "Created by" +msgstr "" + +#. module: ddmrp_report_part_flow_index +#: model:ir.model.fields,field_description:ddmrp_report_part_flow_index.field_ddmrp_flow_index_group_create_date +msgid "Created on" +msgstr "" + +#. module: ddmrp_report_part_flow_index +#: model:ir.actions.act_window,name:ddmrp_report_part_flow_index.action_ddmrp_flow_index_group +msgid "DDMRP Flow Index Group" +msgstr "" + +#. module: ddmrp_report_part_flow_index +#: model:ir.model.fields,field_description:ddmrp_report_part_flow_index.field_ddmrp_flow_index_group_display_name +#: model:ir.model.fields,field_description:ddmrp_report_part_flow_index.field_report_ddmrp_part_plan_flow_index_display_name +msgid "Display Name" +msgstr "" + +#. module: ddmrp_report_part_flow_index +#: model:ir.model.fields,field_description:ddmrp_report_part_flow_index.field_report_ddmrp_part_plan_flow_index_flow_index_group_id +#: model:ir.model.fields,field_description:ddmrp_report_part_flow_index.field_stock_warehouse_orderpoint_flow_index_group_id +#: model:ir.ui.menu,name:ddmrp_report_part_flow_index.menu_ddmrp_flow_index_group +msgid "Flow Index Group" +msgstr "" + +#. module: ddmrp_report_part_flow_index +#: model:ir.model.fields,field_description:ddmrp_report_part_flow_index.field_report_ddmrp_part_plan_flow_index_green_zone_qty +msgid "Green Zone Qty" +msgstr "" + +#. module: ddmrp_report_part_flow_index +#: model:ir.model.fields,field_description:ddmrp_report_part_flow_index.field_ddmrp_flow_index_group_id +#: model:ir.model.fields,field_description:ddmrp_report_part_flow_index.field_report_ddmrp_part_plan_flow_index_id +msgid "ID" +msgstr "" + +#. module: ddmrp_report_part_flow_index +#: model:ir.model.fields,field_description:ddmrp_report_part_flow_index.field_ddmrp_flow_index_group___last_update +#: model:ir.model.fields,field_description:ddmrp_report_part_flow_index.field_report_ddmrp_part_plan_flow_index___last_update +msgid "Last Modified on" +msgstr "" + +#. module: ddmrp_report_part_flow_index +#: model:ir.model.fields,field_description:ddmrp_report_part_flow_index.field_ddmrp_flow_index_group_write_uid +msgid "Last Updated by" +msgstr "" + +#. module: ddmrp_report_part_flow_index +#: model:ir.model.fields,field_description:ddmrp_report_part_flow_index.field_ddmrp_flow_index_group_write_date +msgid "Last Updated on" +msgstr "" + +#. module: ddmrp_report_part_flow_index +#: model:ir.model.fields,field_description:ddmrp_report_part_flow_index.field_report_ddmrp_part_plan_flow_index_location_id +msgid "Location" +msgstr "" + +#. module: ddmrp_report_part_flow_index +#: model:ir.ui.view,arch_db:ddmrp_report_part_flow_index.view_ddmrp_flow_index_group_form +msgid "Log a description..." +msgstr "" + +#. module: ddmrp_report_part_flow_index +#: model:ir.model.fields,field_description:ddmrp_report_part_flow_index.field_ddmrp_flow_index_group_name +msgid "Name" +msgstr "" + +#. module: ddmrp_report_part_flow_index +#: model:ir.model.fields,field_description:ddmrp_report_part_flow_index.field_report_ddmrp_part_plan_flow_index_order_frequency +msgid "Order Frequency" +msgstr "" + +#. module: ddmrp_report_part_flow_index +#: model:ir.model.fields,field_description:ddmrp_report_part_flow_index.field_report_ddmrp_part_plan_flow_index_order_frequency_group +msgid "Order Frequency Group" +msgstr "" + +#. module: ddmrp_report_part_flow_index +#: model:ir.model.fields,field_description:ddmrp_report_part_flow_index.field_report_ddmrp_part_plan_flow_index_order_frequency_group_count +msgid "Order Frequency Group Count" +msgstr "" + +#. module: ddmrp_report_part_flow_index +#: model:ir.actions.act_window,name:ddmrp_report_part_flow_index.action_report_part_plan_flow_index_template +#: model:ir.ui.menu,name:ddmrp_report_part_flow_index.menu_report_part_plan_flow_index +#: model:ir.ui.view,arch_db:ddmrp_report_part_flow_index.view_part_plan_flow_index_filter +#: model:ir.ui.view,arch_db:ddmrp_report_part_flow_index.view_part_plan_flow_index_graph +#: model:ir.ui.view,arch_db:ddmrp_report_part_flow_index.view_part_plan_flow_index_pivot +#: model:ir.ui.view,arch_db:ddmrp_report_part_flow_index.view_part_plan_flow_index_tree +msgid "Parts Plan Flow Index" +msgstr "" + +#. module: ddmrp_report_part_flow_index +#: model:ir.model.fields,field_description:ddmrp_report_part_flow_index.field_report_ddmrp_part_plan_flow_index_product_id +msgid "Product" +msgstr "" + +#. module: ddmrp_report_part_flow_index +#: model:ir.model,name:ddmrp_report_part_flow_index.model_stock_warehouse_orderpoint +msgid "Stock Buffer" +msgstr "" + +#. module: ddmrp_report_part_flow_index +#: model:ir.model.fields,field_description:ddmrp_report_part_flow_index.field_ddmrp_flow_index_group_summary +msgid "Summary" +msgstr "" + +#. module: ddmrp_report_part_flow_index +#: model:ir.model,name:ddmrp_report_part_flow_index.model_ddmrp_flow_index_group +msgid "ddmrp.flow.index.group" +msgstr "" + +#. module: ddmrp_report_part_flow_index +#: model:ir.model,name:ddmrp_report_part_flow_index.model_report_ddmrp_part_plan_flow_index +msgid "report.ddmrp.part.plan.flow.index" +msgstr "" + From cfb00cac43eb3a4e8f26f0eff9335442a7b66dab Mon Sep 17 00:00:00 2001 From: OCA-git-bot Date: Mon, 29 Jul 2019 02:50:24 +0000 Subject: [PATCH 04/42] [UPD] README.rst --- ddmrp_report_part_flow_index/static/description/index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ddmrp_report_part_flow_index/static/description/index.html b/ddmrp_report_part_flow_index/static/description/index.html index 9ae9cec49..4cf12f949 100644 --- a/ddmrp_report_part_flow_index/static/description/index.html +++ b/ddmrp_report_part_flow_index/static/description/index.html @@ -3,7 +3,7 @@ - + DDMRP Report Part Flow Index -
-

DDMRP Report Part Flow Index

+
+ + +Odoo Community Association + +
+

DDMRP Report Part Flow Index

-

Beta License: LGPL-3 OCA/ddmrp Translate me on Weblate Try me on Runboat

+

Beta License: LGPL-3 OCA/ddmrp Translate me on Weblate Try me on Runboat

Implements the Parts Flow Index Report.

Table of contents

@@ -391,7 +396,7 @@

DDMRP Report Part Flow Index

-

Configuration

+

Configuration

  • Go to Inventory > Configuration > DDMRP > Flow Index Group to define possible groups to analyze on.
  • @@ -400,26 +405,26 @@

    Configuration

-

Usage

+

Usage

Go to Inventory > Reporting > Parts Plan Flow Index to run the report.

-

Known issues / Roadmap

+

Known issues / Roadmap

  • Add Parts Actual Flow Index report
-

Bug Tracker

+

Bug Tracker

Bugs are tracked on GitHub Issues. In case of trouble, please check there if your issue has already been reported. If you spotted it first, help us to smash it by providing a detailed and welcomed @@ -427,22 +432,22 @@

Bug Tracker

Do not contact contributors directly about support or help with technical issues.

-

Credits

+

Credits

-

Authors

+

Authors

  • ForgeFlow
-

Contributors

+

Contributors

-

Maintainers

+

Maintainers

This module is maintained by the OCA.

Odoo Community Association @@ -457,5 +462,6 @@

Maintainers

+
From b4adc680793c8dc00373fdfb6755a44aa9386050 Mon Sep 17 00:00:00 2001 From: Don Kendall Date: Fri, 12 Jun 2026 08:45:22 -0400 Subject: [PATCH 39/42] [IMP] ddmrp_report_part_flow_index: pre-commit auto fixes Signed-off-by: Don Kendall --- ddmrp_report_part_flow_index/README.rst | 12 ++++++------ .../static/description/index.html | 8 ++++---- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/ddmrp_report_part_flow_index/README.rst b/ddmrp_report_part_flow_index/README.rst index e3078eeb8..da5e77644 100644 --- a/ddmrp_report_part_flow_index/README.rst +++ b/ddmrp_report_part_flow_index/README.rst @@ -11,7 +11,7 @@ DDMRP Report Part Flow Index !! This file is generated by oca-gen-addon-readme !! !! changes will be overwritten. !! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - !! source digest: sha256:95173d21911534c688d6a2b7f7b325359c204547d8d4456409f8d9ffb49dc599 + !! source digest: sha256:6f08ef6cf62e7dafcc1de4a488443cb172fd871d816cb589b6ea8cd5513aeaae !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! .. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png @@ -21,13 +21,13 @@ DDMRP Report Part Flow Index :target: http://www.gnu.org/licenses/lgpl-3.0-standalone.html :alt: License: LGPL-3 .. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fddmrp-lightgray.png?logo=github - :target: https://github.com/OCA/ddmrp/tree/18.0/ddmrp_report_part_flow_index + :target: https://github.com/OCA/ddmrp/tree/19.0/ddmrp_report_part_flow_index :alt: OCA/ddmrp .. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png - :target: https://translation.odoo-community.org/projects/ddmrp-18-0/ddmrp-18-0-ddmrp_report_part_flow_index + :target: https://translation.odoo-community.org/projects/ddmrp-19-0/ddmrp-19-0-ddmrp_report_part_flow_index :alt: Translate me on Weblate .. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png - :target: https://runboat.odoo-community.org/builds?repo=OCA/ddmrp&target_branch=18.0 + :target: https://runboat.odoo-community.org/builds?repo=OCA/ddmrp&target_branch=19.0 :alt: Try me on Runboat |badge1| |badge2| |badge3| |badge4| |badge5| @@ -71,7 +71,7 @@ Bug Tracker Bugs are tracked on `GitHub Issues `_. In case of trouble, please check there if your issue has already been reported. If you spotted it first, help us to smash it by providing a detailed and welcomed -`feedback `_. +`feedback `_. Do not contact contributors directly about support or help with technical issues. @@ -113,6 +113,6 @@ Current `maintainers `__: |maintainer-JordiBForgeFlow| |maintainer-LoisRForgeFlow| -This module is part of the `OCA/ddmrp `_ project on GitHub. +This module is part of the `OCA/ddmrp `_ project on GitHub. You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/ddmrp_report_part_flow_index/static/description/index.html b/ddmrp_report_part_flow_index/static/description/index.html index 223de56fd..fc9f16a1b 100644 --- a/ddmrp_report_part_flow_index/static/description/index.html +++ b/ddmrp_report_part_flow_index/static/description/index.html @@ -372,9 +372,9 @@

DDMRP Report Part Flow Index

!! This file is generated by oca-gen-addon-readme !! !! changes will be overwritten. !! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -!! source digest: sha256:95173d21911534c688d6a2b7f7b325359c204547d8d4456409f8d9ffb49dc599 +!! source digest: sha256:6f08ef6cf62e7dafcc1de4a488443cb172fd871d816cb589b6ea8cd5513aeaae !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! --> -

Beta License: LGPL-3 OCA/ddmrp Translate me on Weblate Try me on Runboat

+

Beta License: LGPL-3 OCA/ddmrp Translate me on Weblate Try me on Runboat

Implements the Parts Flow Index Report.

Table of contents

@@ -428,7 +428,7 @@

Bug Tracker

Bugs are tracked on GitHub Issues. In case of trouble, please check there if your issue has already been reported. If you spotted it first, help us to smash it by providing a detailed and welcomed -feedback.

+feedback.

Do not contact contributors directly about support or help with technical issues.

@@ -457,7 +457,7 @@

Maintainers

promote its widespread use.

Current maintainers:

JordiBForgeFlow LoisRForgeFlow

-

This module is part of the OCA/ddmrp project on GitHub.

+

This module is part of the OCA/ddmrp project on GitHub.

You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

From 4e158667329473c82a3c88321c62111c96b0842c Mon Sep 17 00:00:00 2001 From: Don Kendall Date: Mon, 15 Jun 2026 12:12:28 -0400 Subject: [PATCH 40/42] [MIG] ddmrp_report_part_flow_index: Migration to 19.0 Signed-off-by: Don Kendall --- ddmrp_report_part_flow_index/__manifest__.py | 2 +- .../models/ddmrp_flow_index_group.py | 4 ---- .../models/stock_buffer.py | 20 +++++++++---------- .../test_ddmrp_report_part_flow_index.py | 3 ++- 4 files changed, 13 insertions(+), 16 deletions(-) diff --git a/ddmrp_report_part_flow_index/__manifest__.py b/ddmrp_report_part_flow_index/__manifest__.py index e400e839c..ccbdf29f3 100644 --- a/ddmrp_report_part_flow_index/__manifest__.py +++ b/ddmrp_report_part_flow_index/__manifest__.py @@ -4,7 +4,7 @@ { "name": "DDMRP Report Part Flow Index", "summary": "Provides the DDMRP Parts Flow Index Report", - "version": "18.0.1.0.1", + "version": "19.0.1.0.0", "development_status": "Beta", "author": "ForgeFlow, Odoo Community Association (OCA)", "maintainers": ["JordiBForgeFlow", "LoisRForgeFlow"], diff --git a/ddmrp_report_part_flow_index/models/ddmrp_flow_index_group.py b/ddmrp_report_part_flow_index/models/ddmrp_flow_index_group.py index 5e7c55893..a880a79b6 100644 --- a/ddmrp_report_part_flow_index/models/ddmrp_flow_index_group.py +++ b/ddmrp_report_part_flow_index/models/ddmrp_flow_index_group.py @@ -15,7 +15,3 @@ class DdmrpFlowIndexGroup(models.Model): lower_range = fields.Float(help="Lower range used to assign in stock buffer") upper_range = fields.Float(help="Upper range used to assign in stock buffer") sequence = fields.Integer(required=True) - - def toggle_active(self): - for record in self: - record.active = not record.active diff --git a/ddmrp_report_part_flow_index/models/stock_buffer.py b/ddmrp_report_part_flow_index/models/stock_buffer.py index ab6234e8c..724c9ca58 100644 --- a/ddmrp_report_part_flow_index/models/stock_buffer.py +++ b/ddmrp_report_part_flow_index/models/stock_buffer.py @@ -12,20 +12,20 @@ class StockBuffer(models.Model): ) def _calc_flow_index_group_id(self): - flow_index_reports = self.env["report.ddmrp.part.plan.flow.index"].read_group( - domain=[("buffer_id", "in", self.ids)], - fields=["order_frequency_group"], - groupby=["buffer_id"], + self.env["stock.buffer"].flush_model() + frequency_by_buffer = dict( + self.env["report.ddmrp.part.plan.flow.index"]._read_group( + domain=[("buffer_id", "in", self.ids)], + groupby=["buffer_id"], + aggregates=["order_frequency_group:sum"], + ) ) - flow_index_groups = self.env["ddmrp.flow.index.group"].search([]) + flow_index_groups = self.env["ddmrp.flow.index.group"].search([]) # pylint: disable=no-search-all for rec in self: - flow_index_report = list( - filter(lambda x: x["buffer_id"][0] == rec.id, flow_index_reports) - ) - if not flow_index_report: + if rec not in frequency_by_buffer: continue - frequency_group = flow_index_report[0]["order_frequency_group"] + frequency_group = frequency_by_buffer[rec] for index_group in flow_index_groups: if index_group.upper_range and index_group.lower_range: if ( diff --git a/ddmrp_report_part_flow_index/tests/test_ddmrp_report_part_flow_index.py b/ddmrp_report_part_flow_index/tests/test_ddmrp_report_part_flow_index.py index ef8f128ff..40133bdbc 100644 --- a/ddmrp_report_part_flow_index/tests/test_ddmrp_report_part_flow_index.py +++ b/ddmrp_report_part_flow_index/tests/test_ddmrp_report_part_flow_index.py @@ -7,6 +7,7 @@ class TestDDMRPReportPartFlowIndex(TransactionCase): def setUp(self): super().setUp() + self.env = self.env(context=dict(self.env.context, tracking_disable=True)) self.buffer_profile_mmm = self.env.ref( "ddmrp.stock_buffer_profile_replenish_manufactured_medium_medium" ) @@ -61,6 +62,6 @@ def test_02_archived_buffers_excluded(self): self.buffer.cron_actions() report = self.env["report.ddmrp.part.plan.flow.index"] self.assertTrue(report.search([("buffer_id", "=", self.buffer.id)])) - self.buffer.toggle_active() + self.buffer.action_archive() self.env.invalidate_all() self.assertFalse(report.search([("buffer_id", "=", self.buffer.id)])) From 21d9de46b99697cbdb2e251d32bc20b88d7157b1 Mon Sep 17 00:00:00 2001 From: Don Kendall Date: Thu, 18 Jun 2026 17:28:07 -0400 Subject: [PATCH 41/42] [IMP] ddmrp_report_part_flow_index: store flow index on buffer + actual report - order_frequency / order_frequency_group / flow_index_group_id are now stored computed fields on stock.buffer (reactive on green_zone_qty / adu), replacing the cron_actions hook + _read_group + flush_model recompute path. - Drop the report.ddmrp.part.plan.flow.index SQL view; the Plan report is now a pivot / graph / list on stock.buffer. - Add the Parts Actual Flow Index report: actual_order_* fields computed at refresh from completed replenishments over the past horizon, reusing the ddmrp.flow.index.group ranges. Closes the ROADMAP item. - Add a Flow Index notebook page (Planned / Actual) on the buffer form and show the group as a badge on the form and the report lists. --- ddmrp_report_part_flow_index/__init__.py | 1 - ddmrp_report_part_flow_index/__manifest__.py | 3 +- .../models/ddmrp_flow_index_group.py | 17 +++ .../models/stock_buffer.py | 136 ++++++++++++----- .../readme/CONFIGURE.md | 14 +- .../readme/DESCRIPTION.md | 13 +- .../readme/ROADMAP.md | 1 - ddmrp_report_part_flow_index/readme/USAGE.md | 11 +- .../reports/__init__.py | 1 - .../report_ddmrp_part_plan_flow_index.py | 90 ------------ ...eport_ddmrp_part_plan_flow_index_views.xml | 138 +++++++++++++----- .../security/ir.model.access.csv | 1 - ...rt_ddmrp_part_plan_flow_index_security.xml | 10 -- .../test_ddmrp_report_part_flow_index.py | 87 ++++++++--- .../views/stock_buffer_view.xml | 19 ++- 15 files changed, 334 insertions(+), 208 deletions(-) delete mode 100644 ddmrp_report_part_flow_index/readme/ROADMAP.md delete mode 100644 ddmrp_report_part_flow_index/reports/__init__.py delete mode 100644 ddmrp_report_part_flow_index/reports/report_ddmrp_part_plan_flow_index.py delete mode 100644 ddmrp_report_part_flow_index/security/report_ddmrp_part_plan_flow_index_security.xml diff --git a/ddmrp_report_part_flow_index/__init__.py b/ddmrp_report_part_flow_index/__init__.py index 55ec7fc9a..0650744f6 100644 --- a/ddmrp_report_part_flow_index/__init__.py +++ b/ddmrp_report_part_flow_index/__init__.py @@ -1,2 +1 @@ from . import models -from . import reports diff --git a/ddmrp_report_part_flow_index/__manifest__.py b/ddmrp_report_part_flow_index/__manifest__.py index ccbdf29f3..9d417ada7 100644 --- a/ddmrp_report_part_flow_index/__manifest__.py +++ b/ddmrp_report_part_flow_index/__manifest__.py @@ -4,7 +4,7 @@ { "name": "DDMRP Report Part Flow Index", "summary": "Provides the DDMRP Parts Flow Index Report", - "version": "19.0.1.0.0", + "version": "19.0.2.0.0", "development_status": "Beta", "author": "ForgeFlow, Odoo Community Association (OCA)", "maintainers": ["JordiBForgeFlow", "LoisRForgeFlow"], @@ -15,7 +15,6 @@ ], "data": [ "security/ir.model.access.csv", - "security/report_ddmrp_part_plan_flow_index_security.xml", "reports/report_ddmrp_part_plan_flow_index_views.xml", "views/ddmrp_flow_index_group_views.xml", "views/stock_buffer_view.xml", diff --git a/ddmrp_report_part_flow_index/models/ddmrp_flow_index_group.py b/ddmrp_report_part_flow_index/models/ddmrp_flow_index_group.py index a880a79b6..bdb81477a 100644 --- a/ddmrp_report_part_flow_index/models/ddmrp_flow_index_group.py +++ b/ddmrp_report_part_flow_index/models/ddmrp_flow_index_group.py @@ -15,3 +15,20 @@ class DdmrpFlowIndexGroup(models.Model): lower_range = fields.Float(help="Lower range used to assign in stock buffer") upper_range = fields.Float(help="Upper range used to assign in stock buffer") sequence = fields.Integer(required=True) + + def _match(self, value): + """Return the first group (by sequence) in ``self`` whose range contains + ``value``. ``self`` is expected to be pre-ordered by sequence. An unset + bound (0.0) is treated as open-ended on that side.""" + for group in self: + lower, upper = group.lower_range, group.upper_range + if lower and upper: + if lower <= value <= upper: + return group + elif upper: + if value <= upper: + return group + elif lower: + if value >= lower: + return group + return self.browse() diff --git a/ddmrp_report_part_flow_index/models/stock_buffer.py b/ddmrp_report_part_flow_index/models/stock_buffer.py index 724c9ca58..1b6856b2e 100644 --- a/ddmrp_report_part_flow_index/models/stock_buffer.py +++ b/ddmrp_report_part_flow_index/models/stock_buffer.py @@ -1,50 +1,118 @@ # Copyright 2017-24 ForgeFlow S.L. (https://www.forgeflow.com) # License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html). -from odoo import fields, models +from datetime import timedelta + +from odoo import api, fields, models + +# Fallback observation window (working days) for the actual flow index when the +# buffer's ADU method defines no past horizon (e.g. fixed ADU). +DEFAULT_ACTUAL_HORIZON = 365 + + +def _round_half_up(value): + """Match PostgreSQL ``round()`` (half away from zero) for non-negative values, + instead of Python's banker's rounding.""" + return int(value + 0.5) class StockBuffer(models.Model): _inherit = "stock.buffer" + # --- Planned flow index (derived from buffer sizing, kept up to date by the + # ORM whenever the green zone or ADU change) --- + order_frequency = fields.Float( + compute="_compute_order_frequency", + store=True, + digits="Product Unit of Measure", + help="Planned order cycle, in days of ADU: Green Zone / ADU.", + ) + order_frequency_group = fields.Integer( + compute="_compute_order_frequency", + store=True, + help="Rounded planned order cycle, used to assign the Flow Index Group.", + ) flow_index_group_id = fields.Many2one( - "ddmrp.flow.index.group", string="Flow Index Group", readonly=True + "ddmrp.flow.index.group", + string="Flow Index Group", + compute="_compute_flow_index_group_id", + store=True, + readonly=True, ) - def _calc_flow_index_group_id(self): - self.env["stock.buffer"].flush_model() - frequency_by_buffer = dict( - self.env["report.ddmrp.part.plan.flow.index"]._read_group( - domain=[("buffer_id", "in", self.ids)], - groupby=["buffer_id"], - aggregates=["order_frequency_group:sum"], - ) - ) - flow_index_groups = self.env["ddmrp.flow.index.group"].search([]) # pylint: disable=no-search-all + # --- Actual flow index (derived from real replenishment history; refreshed by + # cron_actions, like the ADU) --- + actual_order_count = fields.Integer( + string="Actual Replenishments", + readonly=True, + help="Completed replenishments received over the observation window.", + ) + actual_order_frequency = fields.Float( + readonly=True, + digits="Product Unit of Measure", + help="Observed order cycle, in days: observation window / completed " + "replenishments.", + ) + actual_order_frequency_group = fields.Integer(readonly=True) + actual_flow_index_group_id = fields.Many2one( + "ddmrp.flow.index.group", + string="Actual Flow Index Group", + readonly=True, + ) + + def _flow_index_groups(self): + """Flow index groups ordered by sequence, ready for range matching.""" + return self.env["ddmrp.flow.index.group"].search([], order="sequence, id") # pylint: disable=no-search-all + + @api.depends("green_zone_qty", "adu") + def _compute_order_frequency(self): for rec in self: - if rec not in frequency_by_buffer: - continue - - frequency_group = frequency_by_buffer[rec] - for index_group in flow_index_groups: - if index_group.upper_range and index_group.lower_range: - if ( - index_group.lower_range - <= frequency_group - <= index_group.upper_range - ): - rec.flow_index_group_id = index_group - break - elif index_group.upper_range: - if index_group.upper_range >= frequency_group: - rec.flow_index_group_id = index_group - break - elif index_group.lower_range: - if frequency_group >= index_group.lower_range: - rec.flow_index_group_id = index_group - break + freq = rec.green_zone_qty / rec.adu if rec.adu else 0.0 + rec.order_frequency = freq + rec.order_frequency_group = _round_half_up(freq) + + @api.depends("order_frequency_group") + def _compute_flow_index_group_id(self): + groups = self._flow_index_groups() + for rec in self: + rec.flow_index_group_id = groups._match(rec.order_frequency_group) + + def _past_incoming_moves_domain(self, date_from, date_to, locations): + """Completed replenishment moves into the buffer locations (the supply-side + mirror of ``_past_moves_domain``).""" + self.ensure_one() + return [ + ("state", "=", "done"), + ("location_dest_id", "in", locations.ids), + ("location_id", "not in", locations.ids), + ("product_id", "=", self.product_id.id), + ("date", ">=", date_from), + ("date", "<=", date_to), + ] + + def _calc_actual_flow_index(self): + groups = self._flow_index_groups() + today = fields.Date.context_today(self) + for rec in self: + horizon = rec._get_horizon_adu_past_demand() or DEFAULT_ACTUAL_HORIZON + date_to = today - timedelta(days=1) + date_from = today - timedelta(days=horizon) + locations = ( + rec.env["stock.location"] + .with_context(active_test=False) + .search([("id", "child_of", rec.location_id.ids)]) + ) + count = rec.env["stock.move"].search_count( + rec._past_incoming_moves_domain(date_from, date_to, locations) + ) + freq = (horizon / count) if count else 0.0 + group = _round_half_up(freq) + rec.actual_order_count = count + rec.actual_order_frequency = freq + rec.actual_order_frequency_group = group + rec.actual_flow_index_group_id = groups._match(group) if count else False def cron_actions(self, only_nfp=False): res = super().cron_actions(only_nfp=only_nfp) - self._calc_flow_index_group_id() + self._calc_actual_flow_index() return res diff --git a/ddmrp_report_part_flow_index/readme/CONFIGURE.md b/ddmrp_report_part_flow_index/readme/CONFIGURE.md index 11f2fe192..313d72c35 100644 --- a/ddmrp_report_part_flow_index/readme/CONFIGURE.md +++ b/ddmrp_report_part_flow_index/readme/CONFIGURE.md @@ -1,4 +1,10 @@ -- Go to *Inventory \> Configuration \> DDMRP \> Flow Index Group* to - define possible groups to analyze on. -- Go to *Inventory \> Master Data \> Stock Buffer Planning* and assign - the Flow Index Group to Buffers. +- Go to *Inventory \> Configuration \> DDMRP \> Flow Index Group* to define + the groups (a name and an order-frequency range each). An unset bound is + treated as open-ended. + +Buffers are assigned to a group automatically -- the **planned** and **actual** +Flow Index Group are computed from the buffer's order frequency against those +ranges. The planned value updates whenever the buffer's green zone or ADU +change; the actual value is refreshed together with the buffer (the scheduled +*Buffer refresh* action, or the **Refresh Buffer** button). Changes to a group's +range therefore take effect on the next buffer refresh. diff --git a/ddmrp_report_part_flow_index/readme/DESCRIPTION.md b/ddmrp_report_part_flow_index/readme/DESCRIPTION.md index 963881e25..8b1352d79 100644 --- a/ddmrp_report_part_flow_index/readme/DESCRIPTION.md +++ b/ddmrp_report_part_flow_index/readme/DESCRIPTION.md @@ -1 +1,12 @@ -Implements the Parts Flow Index Report. +Classifies DDMRP stock buffers by their order frequency ("flow"), into +user-defined Flow Index Groups. + +Two complementary views are provided: + +- **Plan Flow Index** -- the *planned* order frequency derived from the + buffer sizing (Green Zone / ADU). +- **Actual Flow Index** -- the *observed* order frequency derived from the + real completed replenishments over the past horizon. + +Comparing planned vs actual flow highlights buffers whose real ordering +behaviour diverges from their sizing. diff --git a/ddmrp_report_part_flow_index/readme/ROADMAP.md b/ddmrp_report_part_flow_index/readme/ROADMAP.md deleted file mode 100644 index dd8897b48..000000000 --- a/ddmrp_report_part_flow_index/readme/ROADMAP.md +++ /dev/null @@ -1 +0,0 @@ -- Add Parts Actual Flow Index report diff --git a/ddmrp_report_part_flow_index/readme/USAGE.md b/ddmrp_report_part_flow_index/readme/USAGE.md index 28852d7b6..f16d2b885 100644 --- a/ddmrp_report_part_flow_index/readme/USAGE.md +++ b/ddmrp_report_part_flow_index/readme/USAGE.md @@ -1,2 +1,9 @@ -Go to *Inventory \> Reporting \> Parts Plan Flow Index* to run the -report. +- Go to *Inventory \> Reporting \> Parts Plan Flow Index* to classify + buffers by their **planned** order frequency (Green Zone / ADU). +- Go to *Inventory \> Reporting \> Parts Actual Flow Index* to classify + buffers by their **actual** order frequency (completed replenishments + observed over the past horizon). + +Both reports group buffers by Flow Index Group and expose the planned and +actual figures side by side, so a divergence between planned and actual +flow flags a buffer that may be mis-sized. diff --git a/ddmrp_report_part_flow_index/reports/__init__.py b/ddmrp_report_part_flow_index/reports/__init__.py deleted file mode 100644 index 82734997c..000000000 --- a/ddmrp_report_part_flow_index/reports/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from . import report_ddmrp_part_plan_flow_index diff --git a/ddmrp_report_part_flow_index/reports/report_ddmrp_part_plan_flow_index.py b/ddmrp_report_part_flow_index/reports/report_ddmrp_part_plan_flow_index.py deleted file mode 100644 index 4f290af1d..000000000 --- a/ddmrp_report_part_flow_index/reports/report_ddmrp_part_plan_flow_index.py +++ /dev/null @@ -1,90 +0,0 @@ -# Copyright 2017-24 ForgeFlow S.L. (https://www.forgeflow.com) -# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html). -from odoo import api, fields, models - - -class ReportDdmrpPartsPlanFlowIndex(models.Model): - _name = "report.ddmrp.part.plan.flow.index" - _description = "Report DDMRP Plan Flow Index" - _auto = False - - buffer_id = fields.Many2one("stock.buffer", string="Buffer", readonly=True) - product_id = fields.Many2one("product.product", string="Product", readonly=True) - location_id = fields.Many2one("stock.location", string="Location", readonly=True) - company_id = fields.Many2one("res.company", string="Company", readonly=True) - adu = fields.Float( - string="Average Daily Usage (ADU)", - default=0.0, - digits="Average Daily Usage", - readonly=True, - ) - green_zone_qty = fields.Float(digits="Product Unit of Measure", readonly=True) - order_frequency = fields.Float(digits="Product Unit of Measure", readonly=True) - order_frequency_group = fields.Integer(readonly=True) - order_frequency_group_count = fields.Integer(readonly=True) - flow_index_group_id = fields.Many2one( - "ddmrp.flow.index.group", string="Flow Index Group", readonly=True - ) - - @api.model - def _sub_select(self): - select_str = """ - id, - product_id, - location_id, - company_id, - adu, - flow_index_group_id, - green_zone_qty, - green_zone_qty/NULLIF(adu, 0) as order_frequency, - round(green_zone_qty/NULLIF(adu, 0)) AS - order_frequency_group - """ - return select_str - - @api.model - def _select(self): - select_str = """ - a.id as id, - a.id as buffer_id, - a.product_id as product_id, - a.location_id as location_id, - a.company_id as company_id, - a.adu as adu, - a.flow_index_group_id as flow_index_group_id, - a.green_zone_qty as green_zone_qty, - a.order_frequency as order_frequency, - a.order_frequency_group as order_frequency_group, - b.order_frequency_group_count - """ - return select_str - - @api.model - def _join_select(self): - select_str = """ - order_frequency_group, - count(*) AS order_frequency_group_count - """ - return select_str - - @api.model - def _sub_where(self): - return "WHERE active IS TRUE" - - @property - def _table_query(self): - return f""" - WITH a AS - (SELECT {self._sub_select()} - FROM stock_buffer - {self._sub_where()}) - SELECT - {self._select()} - FROM a - JOIN (SELECT - {self._join_select()} - FROM a - GROUP BY order_frequency_group - ) AS b - ON a.order_frequency_group = b.order_frequency_group - """ diff --git a/ddmrp_report_part_flow_index/reports/report_ddmrp_part_plan_flow_index_views.xml b/ddmrp_report_part_flow_index/reports/report_ddmrp_part_plan_flow_index_views.xml index 6afad2b1a..a5826a84b 100644 --- a/ddmrp_report_part_flow_index/reports/report_ddmrp_part_plan_flow_index_views.xml +++ b/ddmrp_report_part_flow_index/reports/report_ddmrp_part_plan_flow_index_views.xml @@ -2,41 +2,29 @@ - - view.ddmrp.part.plan.flow.index.pivot - report.ddmrp.part.plan.flow.index + + stock.buffer.flow.index.list + stock.buffer + 99 - - - - - - - - - view.ddmrp.part.plan.flow.index.pivot.graph - report.ddmrp.part.plan.flow.index - - - - - - - - - view.ddmrp.part.plan.flow.index.pivot.tree - report.ddmrp.part.plan.flow.index - - - + + - - - + + + + + + - - view.ddmrp.part.plan.flow.index.pivot.filter - report.ddmrp.part.plan.flow.index + + stock.buffer.flow.index.pivot + stock.buffer + 99 + + + + + + + + + + + stock.buffer.flow.index.graph + stock.buffer + 99 - + + + + + + + + stock.buffer.flow.index.search + stock.buffer + 99 + + + - - + + + + + + Parts Plan Flow Index - report.ddmrp.part.plan.flow.index - graph,list,pivot - - + stock.buffer + list,pivot,graph + + {'search_default_group_flow_index': 1} + + + + + Parts Actual Flow Index + stock.buffer + list,pivot,graph + + {'search_default_group_actual_flow_index': 1} + + + + diff --git a/ddmrp_report_part_flow_index/security/ir.model.access.csv b/ddmrp_report_part_flow_index/security/ir.model.access.csv index 1fe1a3266..caf0bfc20 100644 --- a/ddmrp_report_part_flow_index/security/ir.model.access.csv +++ b/ddmrp_report_part_flow_index/security/ir.model.access.csv @@ -1,4 +1,3 @@ id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink access_ddmrp_flow_index_group,ddmrp.flow.index.group,model_ddmrp_flow_index_group,ddmrp.group_stock_buffer_maintainer,1,0,0,0 access_ddmrp_flow_index_group_manager,ddmrp.flow.index.group,model_ddmrp_flow_index_group,ddmrp.group_ddmrp_manager,1,1,1,1 -access_report_ddmrp_part_plan_flow_index,ddmrp.part.plan.flow.index,model_report_ddmrp_part_plan_flow_index,ddmrp.group_stock_buffer_maintainer,1,0,0,0 diff --git a/ddmrp_report_part_flow_index/security/report_ddmrp_part_plan_flow_index_security.xml b/ddmrp_report_part_flow_index/security/report_ddmrp_part_plan_flow_index_security.xml deleted file mode 100644 index face041c3..000000000 --- a/ddmrp_report_part_flow_index/security/report_ddmrp_part_plan_flow_index_security.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - Report DDMRP Part Plan Flow Index multi-company - - - ['|',('company_id','=',False),('company_id', 'in', company_ids)] - - diff --git a/ddmrp_report_part_flow_index/tests/test_ddmrp_report_part_flow_index.py b/ddmrp_report_part_flow_index/tests/test_ddmrp_report_part_flow_index.py index 40133bdbc..78e4223e4 100644 --- a/ddmrp_report_part_flow_index/tests/test_ddmrp_report_part_flow_index.py +++ b/ddmrp_report_part_flow_index/tests/test_ddmrp_report_part_flow_index.py @@ -1,6 +1,9 @@ # Copyright 2025 ForgeFlow S.L. (https://www.forgeflow.com) # License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html). +from datetime import timedelta + +from odoo import fields from odoo.tests import TransactionCase @@ -12,6 +15,7 @@ def setUp(self): "ddmrp.stock_buffer_profile_replenish_manufactured_medium_medium" ) self.stock_location = self.env.ref("stock.stock_location_stock") + self.supplier_location = self.env.ref("stock.stock_location_suppliers") self.warehouse = self.env.ref("stock.warehouse0") self.adu_fixed = self.env.ref("ddmrp.adu_calculation_method_fixed") self.product = self.env["product.product"].create( @@ -37,31 +41,76 @@ def setUp(self): self.flow_index_group_2 = self.env["ddmrp.flow.index.group"].create( {"name": "Group 2", "lower_range": 11, "upper_range": 30, "sequence": 1} ) + self.flow_index_group_3 = self.env["ddmrp.flow.index.group"].create( + {"name": "Group 3", "lower_range": 31, "upper_range": 100000, "sequence": 2} + ) - def test_01_calc_flow_index_group_id(self): - """Check if the flow_index_group_id of one buffer is correctly calculated""" + def _create_done_receipt(self, days_ago): + """Create a completed replenishment move into the buffer location.""" + move = self.env["stock.move"].create( + { + "product_id": self.product.id, + "product_uom": self.product.uom_id.id, + "product_uom_qty": 10.0, + "location_id": self.supplier_location.id, + "location_dest_id": self.stock_location.id, + "company_id": self.warehouse.company_id.id, + } + ) + date = fields.Datetime.now() - timedelta(days=days_ago) + move.write({"state": "done", "date": date}) + return move + def test_01_calc_flow_index_group_id(self): + """The planned flow index group follows the buffer's order frequency.""" self.buffer.cron_actions() - self.assertEqual( - self.buffer.flow_index_group_id, - self.flow_index_group_1, - "Flow index group was not updated correctly!", - ) + self.assertEqual(self.buffer.order_frequency_group, 5) + self.assertEqual(self.buffer.flow_index_group_id, self.flow_index_group_1) + # Growing the order cycle grows the green zone -> order frequency -> group. self.buffer.order_cycle = 20 - self.env.invalidate_all() - # Command needed to update report self.buffer.cron_actions() + self.assertEqual(self.buffer.order_frequency_group, 20) + self.assertEqual(self.buffer.flow_index_group_id, self.flow_index_group_2) + + def test_02_flow_index_is_reactive(self): + """flow_index_group_id is recomputed by the ORM on green zone / ADU change, + without going through cron_actions.""" + self.buffer.cron_actions() + self.assertEqual(self.buffer.flow_index_group_id, self.flow_index_group_1) + # A direct write of the inputs re-triggers the stored computes. + self.buffer.green_zone_qty = 80.0 + self.assertEqual(self.buffer.order_frequency_group, 20) + self.assertEqual(self.buffer.flow_index_group_id, self.flow_index_group_2) + + def test_03_actual_flow_index(self): + """The actual flow index reflects real completed replenishments.""" + self._create_done_receipt(days_ago=30) + self._create_done_receipt(days_ago=120) + self.buffer.cron_actions() + self.assertEqual(self.buffer.actual_order_count, 2) + # 365-day default horizon / 2 receipts -> ~183 day actual cycle. + self.assertEqual(self.buffer.actual_order_frequency_group, 183) self.assertEqual( - self.buffer.flow_index_group_id, - self.flow_index_group_2, - "Flow index group was not updated correctly!", + self.buffer.actual_flow_index_group_id, self.flow_index_group_3 ) - def test_02_archived_buffers_excluded(self): - """Archived buffers must not appear in the report""" + def test_04_actual_flow_index_no_history(self): + """No completed replenishments -> no actual flow index group.""" self.buffer.cron_actions() - report = self.env["report.ddmrp.part.plan.flow.index"] - self.assertTrue(report.search([("buffer_id", "=", self.buffer.id)])) - self.buffer.action_archive() - self.env.invalidate_all() - self.assertFalse(report.search([("buffer_id", "=", self.buffer.id)])) + self.assertEqual(self.buffer.actual_order_count, 0) + self.assertFalse(self.buffer.actual_flow_index_group_id) + + def test_05_match_open_ended_and_gaps(self): + """_match handles open-ended bounds (0 = unbounded) and unmatched gaps.""" + Group = self.env["ddmrp.flow.index.group"] + Group.search([]).unlink() + fast = Group.create( + {"name": "Fast", "sequence": 1, "lower_range": 0, "upper_range": 5} + ) + slow = Group.create( + {"name": "Slow", "sequence": 2, "lower_range": 31, "upper_range": 0} + ) + groups = Group.search([], order="sequence, id") + self.assertEqual(groups._match(3), fast, "upper-only bound") + self.assertEqual(groups._match(50), slow, "lower-only bound") + self.assertFalse(groups._match(20), "value in the gap -> no group") diff --git a/ddmrp_report_part_flow_index/views/stock_buffer_view.xml b/ddmrp_report_part_flow_index/views/stock_buffer_view.xml index e1f78ebf8..48541037b 100644 --- a/ddmrp_report_part_flow_index/views/stock_buffer_view.xml +++ b/ddmrp_report_part_flow_index/views/stock_buffer_view.xml @@ -7,9 +7,22 @@ stock.buffer - - - + + + + + + + + + + + + + + + + From f936e0bf4e6790e41835c36c96cf5a4fadce45b7 Mon Sep 17 00:00:00 2001 From: Don Kendall Date: Thu, 18 Jun 2026 17:28:07 -0400 Subject: [PATCH 42/42] [IMP] ddmrp_report_part_flow_index: pre-commit auto fixes --- ddmrp_report_part_flow_index/README.rst | 41 ++++++++--- .../static/description/index.html | 70 ++++++++++++------- 2 files changed, 75 insertions(+), 36 deletions(-) diff --git a/ddmrp_report_part_flow_index/README.rst b/ddmrp_report_part_flow_index/README.rst index da5e77644..06b3e401a 100644 --- a/ddmrp_report_part_flow_index/README.rst +++ b/ddmrp_report_part_flow_index/README.rst @@ -11,7 +11,7 @@ DDMRP Report Part Flow Index !! This file is generated by oca-gen-addon-readme !! !! changes will be overwritten. !! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - !! source digest: sha256:6f08ef6cf62e7dafcc1de4a488443cb172fd871d816cb589b6ea8cd5513aeaae + !! source digest: sha256:33d753fbb5ac790cf572c9e97a54a10c723452eb9b22b995f3cfdde935e7766f !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! .. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png @@ -32,7 +32,18 @@ DDMRP Report Part Flow Index |badge1| |badge2| |badge3| |badge4| |badge5| -Implements the Parts Flow Index Report. +Classifies DDMRP stock buffers by their order frequency ("flow"), into +user-defined Flow Index Groups. + +Two complementary views are provided: + +- **Plan Flow Index** -- the *planned* order frequency derived from the + buffer sizing (Green Zone / ADU). +- **Actual Flow Index** -- the *observed* order frequency derived from + the real completed replenishments over the past horizon. + +Comparing planned vs actual flow highlights buffers whose real ordering +behaviour diverges from their sizing. **Table of contents** @@ -43,19 +54,29 @@ Configuration ============= - Go to *Inventory > Configuration > DDMRP > Flow Index Group* to define - possible groups to analyze on. -- Go to *Inventory > Master Data > Stock Buffer Planning* and assign the - Flow Index Group to Buffers. + the groups (a name and an order-frequency range each). An unset bound + is treated as open-ended. + +Buffers are assigned to a group automatically -- the **planned** and +**actual** Flow Index Group are computed from the buffer's order +frequency against those ranges. The planned value updates whenever the +buffer's green zone or ADU change; the actual value is refreshed +together with the buffer (the scheduled *Buffer refresh* action, or the +**Refresh Buffer** button). Changes to a group's range therefore take +effect on the next buffer refresh. Usage ===== -Go to *Inventory > Reporting > Parts Plan Flow Index* to run the report. - -Known issues / Roadmap -====================== +- Go to *Inventory > Reporting > Parts Plan Flow Index* to classify + buffers by their **planned** order frequency (Green Zone / ADU). +- Go to *Inventory > Reporting > Parts Actual Flow Index* to classify + buffers by their **actual** order frequency (completed replenishments + observed over the past horizon). -- Add Parts Actual Flow Index report +Both reports group buffers by Flow Index Group and expose the planned +and actual figures side by side, so a divergence between planned and +actual flow flags a buffer that may be mis-sized. Changelog ========= diff --git a/ddmrp_report_part_flow_index/static/description/index.html b/ddmrp_report_part_flow_index/static/description/index.html index fc9f16a1b..70826271e 100644 --- a/ddmrp_report_part_flow_index/static/description/index.html +++ b/ddmrp_report_part_flow_index/static/description/index.html @@ -3,7 +3,7 @@ -README.rst +DDMRP Report Part Flow Index