[12.0][MIG] mrp_multi_level:

* You can know integrate with different forecasting mechanisms.
  As a consequence integration with stock_demand_estimate is moved
  to a new module (mrp_multi_level_estimate).
* As agreed by the authors, the module is re-licensed to LGPL-3.
This commit is contained in:
Lois Rilo
2019-08-05 13:53:12 +02:00
committed by davidborromeo
parent 33ae366abf
commit dce2682fdc
25 changed files with 473 additions and 537 deletions

View File

@@ -10,17 +10,17 @@ MRP Multi Level
.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png .. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png
:target: https://odoo-community.org/page/development-status :target: https://odoo-community.org/page/development-status
:alt: Beta :alt: Beta
.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png .. |badge2| image:: https://img.shields.io/badge/licence-LGPL--3-blue.png
:target: http://www.gnu.org/licenses/agpl-3.0-standalone.html :target: http://www.gnu.org/licenses/lgpl-3.0-standalone.html
:alt: License: AGPL-3 :alt: License: LGPL-3
.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fmanufacture-lightgray.png?logo=github .. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fmanufacture-lightgray.png?logo=github
:target: https://github.com/OCA/manufacture/tree/11.0/mrp_multi_level :target: https://github.com/OCA/manufacture/tree/12.0/mrp_multi_level
:alt: OCA/manufacture :alt: OCA/manufacture
.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png .. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png
:target: https://translation.odoo-community.org/projects/manufacture-11-0/manufacture-11-0-mrp_multi_level :target: https://translation.odoo-community.org/projects/manufacture-12-0/manufacture-12-0-mrp_multi_level
:alt: Translate me on Weblate :alt: Translate me on Weblate
.. |badge5| image:: https://img.shields.io/badge/runbot-Try%20me-875A7B.png .. |badge5| image:: https://img.shields.io/badge/runbot-Try%20me-875A7B.png
:target: https://runbot.odoo-community.org/runbot/129/11.0 :target: https://runbot.odoo-community.org/runbot/129/12.0
:alt: Try me on Runbot :alt: Try me on Runbot
|badge1| |badge2| |badge3| |badge4| |badge5| |badge1| |badge2| |badge3| |badge4| |badge5|
@@ -36,11 +36,12 @@ Key Features
------------ ------------
* MRP parameters set by product variant MRP area pairs. * MRP parameters set by product variant MRP area pairs.
* Integration with `Stock Demand Estimates <https://github.com/OCA/stock-logistics-warehouse/tree/11.0/stock_demand_estimate>`_ system.
* Cron job to calculate the MRP demand. * Cron job to calculate the MRP demand.
* Manually calculate the MRP demand. * Manually calculate the MRP demand.
* Confirm the calculated MRP demand and create PO's, or MO's. * Confirm the calculated MRP demand and create PO's, or MO's.
* Able to see the products for which action is needed throught Planned Orders. * Able to see the products for which action is needed throught Planned Orders.
* Integration with `Stock Demand Estimates <https://github.com/OCA/stock-logistics-warehouse/tree/12.0/stock_demand_estimate>`_ system.
Note: You need to install `mrp_multi_level_estimate module <https://github.com/OCA/manufacture/tree/12.0/mrp_multi_level_estimate>`_.
**Table of contents** **Table of contents**
@@ -81,6 +82,14 @@ To launch replenishment orders (moves, purchases, production orders...):
Changelog Changelog
========= =========
12.0.1.0.0 (2019-08-05)
~~~~~~~~~~~~~~~~~~~~~~~
* [MIG] Migration to v12:
* Estimates as a forecasting mechanism is moved to a new module
(mrp_multi_level_estimate).
11.0.3.0.0 (2019-05-22) 11.0.3.0.0 (2019-05-22)
~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~
@@ -138,7 +147,7 @@ Bug Tracker
Bugs are tracked on `GitHub Issues <https://github.com/OCA/manufacture/issues>`_. Bugs are tracked on `GitHub Issues <https://github.com/OCA/manufacture/issues>`_.
In case of trouble, please check there if your issue has already been reported. 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 If you spotted it first, help us smashing it by providing a detailed and welcomed
`feedback <https://github.com/OCA/manufacture/issues/new?body=module:%20mrp_multi_level%0Aversion:%2011.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_. `feedback <https://github.com/OCA/manufacture/issues/new?body=module:%20mrp_multi_level%0Aversion:%2012.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.
Do not contact contributors directly about support or help with technical issues. Do not contact contributors directly about support or help with technical issues.
@@ -182,6 +191,6 @@ Current `maintainers <https://odoo-community.org/page/maintainer-role>`__:
|maintainer-jbeficent| |maintainer-lreficent| |maintainer-jbeficent| |maintainer-lreficent|
This module is part of the `OCA/manufacture <https://github.com/OCA/manufacture/tree/11.0/mrp_multi_level>`_ project on GitHub. This module is part of the `OCA/manufacture <https://github.com/OCA/manufacture/tree/12.0/mrp_multi_level>`_ project on GitHub.
You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

View File

@@ -1,11 +1,12 @@
# Copyright 2016 Ucamco - Wim Audenaert <wim.audenaert@ucamco.com> # Copyright 2016 Ucamco - Wim Audenaert <wim.audenaert@ucamco.com>
# Copyright 2016-19 Eficent Business and IT Consulting Services S.L. # Copyright 2016-19 Eficent Business and IT Consulting Services S.L.
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). # License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html).
{ {
'name': 'MRP Multi Level', 'name': 'MRP Multi Level',
'version': '11.0.3.1.1', 'version': '12.0.1.0.0',
'development_status': 'Beta', 'development_status': 'Beta',
'license': 'AGPL-3', 'license': 'LGPL-3',
'author': 'Ucamco, ' 'author': 'Ucamco, '
'Eficent, ' 'Eficent, '
'Odoo Community Association (OCA)', 'Odoo Community Association (OCA)',
@@ -15,9 +16,7 @@
'category': 'Manufacturing', 'category': 'Manufacturing',
'depends': [ 'depends': [
'mrp', 'mrp',
'stock', 'purchase_stock',
'purchase',
'stock_demand_estimate',
'mrp_warehouse_calendar', 'mrp_warehouse_calendar',
], ],
'data': [ 'data': [

View File

@@ -6,26 +6,26 @@
</record> </record>
<record id="stock_inventory_line_1" model="stock.inventory.line"> <record id="stock_inventory_line_1" model="stock.inventory.line">
<field name="product_id" ref="product_product_pp_1"/> <field name="product_id" ref="product_product_pp_1"/>
<field name="product_uom_id" ref="product.product_uom_unit"/> <field name="product_uom_id" ref="uom.product_uom_unit"/>
<field name="inventory_id" ref="stock_inventory_mrp_example"/> <field name="inventory_id" ref="stock_inventory_mrp_example"/>
<field name="product_qty">10</field> <field name="product_qty">10</field>
<field name="location_id" ref="stock.stock_location_stock"/> <field name="location_id" ref="stock.stock_location_stock"/>
</record> </record>
<record id="stock_inventory_line_2" model="stock.inventory.line"> <record id="stock_inventory_line_2" model="stock.inventory.line">
<field name="product_id" ref="product_product_pp_2"/> <field name="product_id" ref="product_product_pp_2"/>
<field name="product_uom_id" ref="product.product_uom_unit"/> <field name="product_uom_id" ref="uom.product_uom_unit"/>
<field name="inventory_id" ref="stock_inventory_mrp_example"/> <field name="inventory_id" ref="stock_inventory_mrp_example"/>
<field name="product_qty">20</field> <field name="product_qty">20</field>
<field name="location_id" ref="stock.stock_location_stock"/> <field name="location_id" ref="stock.stock_location_stock"/>
</record> </record>
<record id="stock_inventory_line_3" model="stock.inventory.line"> <record id="stock_inventory_line_3" model="stock.inventory.line">
<field name="product_id" ref="product_product_sf_2"/> <field name="product_id" ref="product_product_sf_2"/>
<field name="product_uom_id" ref="product.product_uom_unit"/> <field name="product_uom_id" ref="uom.product_uom_unit"/>
<field name="inventory_id" ref="stock_inventory_mrp_example"/> <field name="inventory_id" ref="stock_inventory_mrp_example"/>
<field name="product_qty">15</field> <field name="product_qty">15</field>
<field name="location_id" ref="stock.stock_location_stock"/> <field name="location_id" ref="stock.stock_location_stock"/>
</record> </record>
<function model="stock.inventory" name="action_done"> <function model="stock.inventory" name="action_validate">
<function eval="[[('id', '=', ref('stock_inventory_mrp_example'))]]" model="stock.inventory" name="search"/> <function eval="[[('id', '=', ref('stock_inventory_mrp_example'))]]" model="stock.inventory" name="search"/>
</function> </function>

View File

@@ -4,20 +4,20 @@
<record id="mrp_bom_fp_1" model="mrp.bom"> <record id="mrp_bom_fp_1" model="mrp.bom">
<field name="product_tmpl_id" <field name="product_tmpl_id"
ref="product_product_fp_1_product_template"/> ref="product_product_fp_1_product_template"/>
<field name="product_uom_id" ref="product.product_uom_unit"/> <field name="product_uom_id" ref="uom.product_uom_unit"/>
<field name="sequence">5</field> <field name="sequence">5</field>
</record> </record>
<record id="mrp_bom_fp_1_line_pp_1" model="mrp.bom.line"> <record id="mrp_bom_fp_1_line_pp_1" model="mrp.bom.line">
<field name="product_id" ref="product_product_pp_1"/> <field name="product_id" ref="product_product_pp_1"/>
<field name="product_qty">2</field> <field name="product_qty">2</field>
<field name="product_uom_id" ref="product.product_uom_unit"/> <field name="product_uom_id" ref="uom.product_uom_unit"/>
<field name="sequence">5</field> <field name="sequence">5</field>
<field name="bom_id" ref="mrp_bom_fp_1"/> <field name="bom_id" ref="mrp_bom_fp_1"/>
</record> </record>
<record id="mrp_bom_fp_1_line_pp_2" model="mrp.bom.line"> <record id="mrp_bom_fp_1_line_pp_2" model="mrp.bom.line">
<field name="product_id" ref="product_product_pp_2"/> <field name="product_id" ref="product_product_pp_2"/>
<field name="product_qty">3</field> <field name="product_qty">3</field>
<field name="product_uom_id" ref="product.product_uom_unit"/> <field name="product_uom_id" ref="uom.product_uom_unit"/>
<field name="sequence">5</field> <field name="sequence">5</field>
<field name="bom_id" ref="mrp_bom_fp_1"/> <field name="bom_id" ref="mrp_bom_fp_1"/>
</record> </record>
@@ -26,20 +26,20 @@
<record id="mrp_bom_fp_2" model="mrp.bom"> <record id="mrp_bom_fp_2" model="mrp.bom">
<field name="product_tmpl_id" <field name="product_tmpl_id"
ref="product_product_fp_2_product_template"/> ref="product_product_fp_2_product_template"/>
<field name="product_uom_id" ref="product.product_uom_unit"/> <field name="product_uom_id" ref="uom.product_uom_unit"/>
<field name="sequence">5</field> <field name="sequence">5</field>
</record> </record>
<record id="mrp_bom_fp_2_line_sf_1" model="mrp.bom.line"> <record id="mrp_bom_fp_2_line_sf_1" model="mrp.bom.line">
<field name="product_id" ref="product_product_sf_1"/> <field name="product_id" ref="product_product_sf_1"/>
<field name="product_qty">2</field> <field name="product_qty">2</field>
<field name="product_uom_id" ref="product.product_uom_unit"/> <field name="product_uom_id" ref="uom.product_uom_unit"/>
<field name="sequence">5</field> <field name="sequence">5</field>
<field name="bom_id" ref="mrp_bom_fp_2"/> <field name="bom_id" ref="mrp_bom_fp_2"/>
</record> </record>
<record id="mrp_bom_fp_2_line_sf_2" model="mrp.bom.line"> <record id="mrp_bom_fp_2_line_sf_2" model="mrp.bom.line">
<field name="product_id" ref="product_product_sf_2"/> <field name="product_id" ref="product_product_sf_2"/>
<field name="product_qty">3</field> <field name="product_qty">3</field>
<field name="product_uom_id" ref="product.product_uom_unit"/> <field name="product_uom_id" ref="uom.product_uom_unit"/>
<field name="sequence">5</field> <field name="sequence">5</field>
<field name="bom_id" ref="mrp_bom_fp_2"/> <field name="bom_id" ref="mrp_bom_fp_2"/>
</record> </record>
@@ -47,20 +47,20 @@
<record id="mrp_bom_sf_1" model="mrp.bom"> <record id="mrp_bom_sf_1" model="mrp.bom">
<field name="product_tmpl_id" <field name="product_tmpl_id"
ref="product_product_sf_1_product_template"/> ref="product_product_sf_1_product_template"/>
<field name="product_uom_id" ref="product.product_uom_unit"/> <field name="product_uom_id" ref="uom.product_uom_unit"/>
<field name="sequence">5</field> <field name="sequence">5</field>
</record> </record>
<record id="mrp_bom_sf_1_line_pp_1" model="mrp.bom.line"> <record id="mrp_bom_sf_1_line_pp_1" model="mrp.bom.line">
<field name="product_id" ref="product_product_pp_1"/> <field name="product_id" ref="product_product_pp_1"/>
<field name="product_qty">3</field> <field name="product_qty">3</field>
<field name="product_uom_id" ref="product.product_uom_unit"/> <field name="product_uom_id" ref="uom.product_uom_unit"/>
<field name="sequence">5</field> <field name="sequence">5</field>
<field name="bom_id" ref="mrp_bom_sf_1"/> <field name="bom_id" ref="mrp_bom_sf_1"/>
</record> </record>
<record id="mrp_bom_sf_1_line_pp_2" model="mrp.bom.line"> <record id="mrp_bom_sf_1_line_pp_2" model="mrp.bom.line">
<field name="product_id" ref="product_product_pp_2"/> <field name="product_id" ref="product_product_pp_2"/>
<field name="product_qty">2</field> <field name="product_qty">2</field>
<field name="product_uom_id" ref="product.product_uom_unit"/> <field name="product_uom_id" ref="uom.product_uom_unit"/>
<field name="sequence">5</field> <field name="sequence">5</field>
<field name="bom_id" ref="mrp_bom_sf_1"/> <field name="bom_id" ref="mrp_bom_sf_1"/>
</record> </record>
@@ -68,13 +68,13 @@
<record id="mrp_bom_sf_2" model="mrp.bom"> <record id="mrp_bom_sf_2" model="mrp.bom">
<field name="product_tmpl_id" <field name="product_tmpl_id"
ref="product_product_sf_2_product_template"/> ref="product_product_sf_2_product_template"/>
<field name="product_uom_id" ref="product.product_uom_unit"/> <field name="product_uom_id" ref="uom.product_uom_unit"/>
<field name="sequence">5</field> <field name="sequence">5</field>
</record> </record>
<record id="mrp_bom_sf_2_line_pp_2" model="mrp.bom.line"> <record id="mrp_bom_sf_2_line_pp_2" model="mrp.bom.line">
<field name="product_id" ref="product_product_pp_2"/> <field name="product_id" ref="product_product_pp_2"/>
<field name="product_qty">3</field> <field name="product_qty">3</field>
<field name="product_uom_id" ref="product.product_uom_unit"/> <field name="product_uom_id" ref="uom.product_uom_unit"/>
<field name="sequence">5</field> <field name="sequence">5</field>
<field name="bom_id" ref="mrp_bom_sf_2"/> <field name="bom_id" ref="mrp_bom_sf_2"/>
</record> </record>

View File

@@ -4,8 +4,8 @@
<field name="name">FP-1</field> <field name="name">FP-1</field>
<field name="categ_id" ref="product_category_mrp"/> <field name="categ_id" ref="product_category_mrp"/>
<field name="type">product</field> <field name="type">product</field>
<field name="uom_id" ref="product.product_uom_unit"/> <field name="uom_id" ref="uom.product_uom_unit"/>
<field name="uom_po_id" ref="product.product_uom_unit"/> <field name="uom_po_id" ref="uom.product_uom_unit"/>
<field name="produce_delay">2</field> <field name="produce_delay">2</field>
<field name="route_ids" eval="[(6, 0, [ref('mrp.route_warehouse0_manufacture')])]"/> <field name="route_ids" eval="[(6, 0, [ref('mrp.route_warehouse0_manufacture')])]"/>
</record> </record>
@@ -14,8 +14,8 @@
<field name="name">FP-2</field> <field name="name">FP-2</field>
<field name="categ_id" ref="product_category_mrp"/> <field name="categ_id" ref="product_category_mrp"/>
<field name="type">product</field> <field name="type">product</field>
<field name="uom_id" ref="product.product_uom_unit"/> <field name="uom_id" ref="uom.product_uom_unit"/>
<field name="uom_po_id" ref="product.product_uom_unit"/> <field name="uom_po_id" ref="uom.product_uom_unit"/>
<field name="produce_delay">1</field> <field name="produce_delay">1</field>
<field name="route_ids" eval="[(6, 0, [ref('mrp.route_warehouse0_manufacture')])]"/> <field name="route_ids" eval="[(6, 0, [ref('mrp.route_warehouse0_manufacture')])]"/>
</record> </record>
@@ -24,8 +24,8 @@
<field name="name">SF-1</field> <field name="name">SF-1</field>
<field name="categ_id" ref="product_category_mrp"/> <field name="categ_id" ref="product_category_mrp"/>
<field name="type">product</field> <field name="type">product</field>
<field name="uom_id" ref="product.product_uom_unit"/> <field name="uom_id" ref="uom.product_uom_unit"/>
<field name="uom_po_id" ref="product.product_uom_unit"/> <field name="uom_po_id" ref="uom.product_uom_unit"/>
<field name="produce_delay">1</field> <field name="produce_delay">1</field>
<field name="route_ids" eval="[(6, 0, [ref('mrp.route_warehouse0_manufacture')])]"/> <field name="route_ids" eval="[(6, 0, [ref('mrp.route_warehouse0_manufacture')])]"/>
</record> </record>
@@ -33,8 +33,8 @@
<field name="name">SF-2</field> <field name="name">SF-2</field>
<field name="categ_id" ref="product_category_mrp"/> <field name="categ_id" ref="product_category_mrp"/>
<field name="type">product</field> <field name="type">product</field>
<field name="uom_id" ref="product.product_uom_unit"/> <field name="uom_id" ref="uom.product_uom_unit"/>
<field name="uom_po_id" ref="product.product_uom_unit"/> <field name="uom_po_id" ref="uom.product_uom_unit"/>
<field name="produce_delay">3</field> <field name="produce_delay">3</field>
<field name="route_ids" eval="[(6, 0, [ref('mrp.route_warehouse0_manufacture')])]"/> <field name="route_ids" eval="[(6, 0, [ref('mrp.route_warehouse0_manufacture')])]"/>
</record> </record>
@@ -43,17 +43,17 @@
<field name="name">PP-1</field> <field name="name">PP-1</field>
<field name="categ_id" ref="product_category_mrp"/> <field name="categ_id" ref="product_category_mrp"/>
<field name="type">product</field> <field name="type">product</field>
<field name="uom_id" ref="product.product_uom_unit"/> <field name="uom_id" ref="uom.product_uom_unit"/>
<field name="uom_po_id" ref="product.product_uom_unit"/> <field name="uom_po_id" ref="uom.product_uom_unit"/>
<field name="route_ids" eval="[(6, 0, [ref('purchase.route_warehouse0_buy')])]"/> <field name="route_ids" eval="[(6, 0, [ref('purchase_stock.route_warehouse0_buy')])]"/>
</record> </record>
<record id="product_product_pp_2" model="product.product"> <record id="product_product_pp_2" model="product.product">
<field name="name">PP-2</field> <field name="name">PP-2</field>
<field name="categ_id" ref="product_category_mrp"/> <field name="categ_id" ref="product_category_mrp"/>
<field name="type">product</field> <field name="type">product</field>
<field name="uom_id" ref="product.product_uom_unit"/> <field name="uom_id" ref="uom.product_uom_unit"/>
<field name="uom_po_id" ref="product.product_uom_unit"/> <field name="uom_po_id" ref="uom.product_uom_unit"/>
<field name="route_ids" eval="[(6, 0, [ref('purchase.route_warehouse0_buy')])]"/> <field name="route_ids" eval="[(6, 0, [ref('purchase_stock.route_warehouse0_buy')])]"/>
</record> </record>
</odoo> </odoo>

View File

@@ -1,5 +1,5 @@
# Copyright 2019 Eficent Business and IT Consulting Services, S.L. # Copyright 2019 Eficent Business and IT Consulting Services, S.L.
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). # License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html).
import logging import logging
from odoo import api, SUPERUSER_ID from odoo import api, SUPERUSER_ID

View File

@@ -2,13 +2,14 @@
# © 2016-19 Eficent Business and IT Consulting Services S.L. # © 2016-19 Eficent Business and IT Consulting Services S.L.
# - Jordi Ballester Alomar <jordi.ballester@eficent.com> # - Jordi Ballester Alomar <jordi.ballester@eficent.com>
# - Lois Rilo Antelo <lois.rilo@eficent.com> # - Lois Rilo Antelo <lois.rilo@eficent.com>
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). # License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html).
from odoo import api, fields, models from odoo import api, fields, models
class MrpArea(models.Model): class MrpArea(models.Model):
_name = 'mrp.area' _name = 'mrp.area'
_description = "MRP Area"
name = fields.Char(required=True) name = fields.Char(required=True)
warehouse_id = fields.Many2one( warehouse_id = fields.Many2one(

View File

@@ -2,7 +2,7 @@
# Copyright 2016-19 Eficent Business and IT Consulting Services S.L. # Copyright 2016-19 Eficent Business and IT Consulting Services S.L.
# - Jordi Ballester Alomar <jordi.ballester@eficent.com> # - Jordi Ballester Alomar <jordi.ballester@eficent.com>
# - Lois Rilo Antelo <lois.rilo@eficent.com> # - Lois Rilo Antelo <lois.rilo@eficent.com>
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). # License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html).
from odoo import api, fields, models from odoo import api, fields, models
@@ -34,7 +34,7 @@ class MrpInventory(models.Model):
store=True, store=True,
) )
uom_id = fields.Many2one( uom_id = fields.Many2one(
comodel_name='product.uom', string='Product UoM', comodel_name='uom.uom', string='Product UoM',
compute='_compute_uom_id', compute='_compute_uom_id',
) )
date = fields.Date(string='Date') date = fields.Date(string='Date')
@@ -84,9 +84,11 @@ class MrpInventory(models.Model):
for rec in self.filtered(lambda r: r.date): for rec in self.filtered(lambda r: r.date):
delay = rec.product_mrp_area_id.mrp_lead_time delay = rec.product_mrp_area_id.mrp_lead_time
if delay and rec.mrp_area_id.calendar_id: if delay and rec.mrp_area_id.calendar_id:
dt_date = fields.Datetime.from_string(rec.date) dt_date = fields.Datetime.to_datetime(rec.date)
# dt_date is at the beginning of the day (00:00),
# so we can subtract the delay straight forward.
order_release_date = rec.mrp_area_id.calendar_id.plan_days( order_release_date = rec.mrp_area_id.calendar_id.plan_days(
-delay - 1, dt_date).date() -delay, dt_date).date()
else: else:
order_release_date = fields.Date.from_string( order_release_date = fields.Date.from_string(
rec.date) - timedelta(days=delay) rec.date) - timedelta(days=delay)

View File

@@ -1,19 +1,21 @@
# © 2016 Ucamco - Wim Audenaert <wim.audenaert@ucamco.com> # © 2016 Ucamco - Wim Audenaert <wim.audenaert@ucamco.com>
# © 2016-18 Eficent Business and IT Consulting Services S.L. # © 2016-18 Eficent Business and IT Consulting Services S.L.
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). # License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html).
from odoo import models, fields from odoo import models, fields
class MrpMove(models.Model): class MrpMove(models.Model):
_name = 'mrp.move' _name = 'mrp.move'
_description = "MRP Move"
_order = 'product_mrp_area_id, mrp_date, mrp_type desc, id' _order = 'product_mrp_area_id, mrp_date, mrp_type desc, id'
# TODO: too many indexes... # TODO: too many indexes...
product_mrp_area_id = fields.Many2one( product_mrp_area_id = fields.Many2one(
comodel_name="product.mrp.area", comodel_name="product.mrp.area",
string="Product", index=True, string="Product MRP Area",
index=True,
required=True, required=True,
) )
mrp_area_id = fields.Many2one( mrp_area_id = fields.Many2one(

View File

@@ -1,6 +1,6 @@
# Copyright 2019 Eficent Business and IT Consulting Services S.L. # Copyright 2019 Eficent Business and IT Consulting Services S.L.
# - Lois Rilo Antelo <lois.rilo@eficent.com> # - Lois Rilo Antelo <lois.rilo@eficent.com>
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). # License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html).
from odoo import models, fields from odoo import models, fields
@@ -13,7 +13,7 @@ class MrpPlannedOrder(models.Model):
name = fields.Char(string="Description") name = fields.Char(string="Description")
product_mrp_area_id = fields.Many2one( product_mrp_area_id = fields.Many2one(
comodel_name="product.mrp.area", comodel_name="product.mrp.area",
string="Product", string="Product MRP Area",
index=True, index=True,
required=True, required=True,
) )
@@ -52,7 +52,9 @@ class MrpPlannedOrder(models.Model):
mrp_action = fields.Selection( mrp_action = fields.Selection(
selection=[("manufacture", "Manufacturing Order"), selection=[("manufacture", "Manufacturing Order"),
("buy", "Purchase Order"), ("buy", "Purchase Order"),
("move", "Transfer"), ('pull', 'Pull From'),
('push', 'Push To'),
('pull_push', 'Pull & Push'),
("none", "None")], ("none", "None")],
string="Action", string="Action",
) )

View File

@@ -2,7 +2,7 @@
# Copyright 2016-19 Eficent Business and IT Consulting Services S.L. # Copyright 2016-19 Eficent Business and IT Consulting Services S.L.
# - Jordi Ballester Alomar <jordi.ballester@eficent.com> # - Jordi Ballester Alomar <jordi.ballester@eficent.com>
# - Lois Rilo Antelo <lois.rilo@eficent.com> # - Lois Rilo Antelo <lois.rilo@eficent.com>
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). # License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html).
from math import ceil from math import ceil
@@ -80,11 +80,12 @@ class ProductMRPArea(models.Model):
selection=[('buy', 'Buy'), selection=[('buy', 'Buy'),
('none', 'Undefined'), ('none', 'Undefined'),
('manufacture', 'Produce'), ('manufacture', 'Produce'),
('move', 'Transfer')], ('pull', 'Pull From'),
('push', 'Push To'),
('pull_push', 'Pull & Push')],
string='Supply Method', string='Supply Method',
compute='_compute_supply_method', compute='_compute_supply_method',
) )
qty_available = fields.Float( qty_available = fields.Float(
string="Quantity Available", string="Quantity Available",
compute="_compute_qty_available", compute="_compute_qty_available",
@@ -99,10 +100,7 @@ class ProductMRPArea(models.Model):
inverse_name="product_mrp_area_id", inverse_name="product_mrp_area_id",
readonly=True, readonly=True,
) )
group_estimate_days = fields.Integer(
string="Group Days of Estimates",
default=1,
)
_sql_constraints = [ _sql_constraints = [
('product_mrp_area_uniq', 'unique(product_id, mrp_area_id)', ('product_mrp_area_uniq', 'unique(product_id, mrp_area_id)',
'The product/MRP Area parameters combination must be unique.'), 'The product/MRP Area parameters combination must be unique.'),
@@ -111,13 +109,12 @@ class ProductMRPArea(models.Model):
@api.multi @api.multi
@api.constrains( @api.constrains(
"mrp_minimum_order_qty", "mrp_maximum_order_qty", "mrp_qty_multiple", "mrp_minimum_order_qty", "mrp_maximum_order_qty", "mrp_qty_multiple",
"mrp_minimum_stock", "mrp_nbr_days", "group_estimate_days", "mrp_minimum_stock", "mrp_nbr_days",
) )
def _check_negatives(self): def _check_negatives(self):
values = self.read([ values = self.read([
"mrp_minimum_order_qty", "mrp_maximum_order_qty", "mrp_minimum_order_qty", "mrp_maximum_order_qty",
"mrp_qty_multiple", "mrp_qty_multiple", "mrp_minimum_stock", "mrp_nbr_days",
"mrp_minimum_stock", "mrp_nbr_days", "group_estimate_days",
]) ])
for rec in values: for rec in values:
if any(v < 0 for v in rec.values()): if any(v < 0 for v in rec.values()):

View File

@@ -1,6 +1,6 @@
# Copyright 2016 Ucamco - Wim Audenaert <wim.audenaert@ucamco.com> # Copyright 2016 Ucamco - Wim Audenaert <wim.audenaert@ucamco.com>
# Copyright 2016-18 Eficent Business and IT Consulting Services S.L. # Copyright 2016-18 Eficent Business and IT Consulting Services S.L.
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). # License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html).
import ast import ast
from odoo import api, fields, models from odoo import api, fields, models

View File

@@ -1,5 +1,5 @@
# Copyright 2018 Eficent Business and IT Consulting Services S.L. # Copyright 2018 Eficent Business and IT Consulting Services S.L.
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). # License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html).
import ast import ast
from odoo import api, fields, models from odoo import api, fields, models

View File

@@ -1,7 +1,7 @@
# © 2016 Ucamco - Wim Audenaert <wim.audenaert@ucamco.com> # © 2016 Ucamco - Wim Audenaert <wim.audenaert@ucamco.com>
# © 2016 Eficent Business and IT Consulting Services S.L. # © 2016 Eficent Business and IT Consulting Services S.L.
# - Jordi Ballester Alomar <jordi.ballester@eficent.com> # - Jordi Ballester Alomar <jordi.ballester@eficent.com>
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). # License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html).
from odoo import fields, models from odoo import fields, models

View File

@@ -9,8 +9,9 @@ Key Features
------------ ------------
* MRP parameters set by product variant MRP area pairs. * MRP parameters set by product variant MRP area pairs.
* Integration with `Stock Demand Estimates <https://github.com/OCA/stock-logistics-warehouse/tree/11.0/stock_demand_estimate>`_ system.
* Cron job to calculate the MRP demand. * Cron job to calculate the MRP demand.
* Manually calculate the MRP demand. * Manually calculate the MRP demand.
* Confirm the calculated MRP demand and create PO's, or MO's. * Confirm the calculated MRP demand and create PO's, or MO's.
* Able to see the products for which action is needed throught Planned Orders. * Able to see the products for which action is needed throught Planned Orders.
* Integration with `Stock Demand Estimates <https://github.com/OCA/stock-logistics-warehouse/tree/12.0/stock_demand_estimate>`_ system.
Note: You need to install `mrp_multi_level_estimate module <https://github.com/OCA/manufacture/tree/12.0/mrp_multi_level_estimate>`_.

View File

@@ -1,3 +1,11 @@
12.0.1.0.0 (2019-08-05)
~~~~~~~~~~~~~~~~~~~~~~~
* [MIG] Migration to v12:
* Estimates as a forecasting mechanism is moved to a new module
(mrp_multi_level_estimate).
11.0.3.0.0 (2019-05-22) 11.0.3.0.0 (2019-05-22)
~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~

View File

@@ -3,7 +3,7 @@
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head> <head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="generator" content="Docutils 0.15.1: http://docutils.sourceforge.net/" /> <meta name="generator" content="Docutils 0.15.2: http://docutils.sourceforge.net/" />
<title>MRP Multi Level</title> <title>MRP Multi Level</title>
<style type="text/css"> <style type="text/css">
@@ -367,7 +367,7 @@ ul.auto-toc {
!! This file is generated by oca-gen-addon-readme !! !! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !! !! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! --> !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->
<p><a class="reference external" href="https://odoo-community.org/page/development-status"><img alt="Beta" src="https://img.shields.io/badge/maturity-Beta-yellow.png" /></a> <a class="reference external" href="http://www.gnu.org/licenses/agpl-3.0-standalone.html"><img alt="License: AGPL-3" src="https://img.shields.io/badge/licence-AGPL--3-blue.png" /></a> <a class="reference external" href="https://github.com/OCA/manufacture/tree/11.0/mrp_multi_level"><img alt="OCA/manufacture" src="https://img.shields.io/badge/github-OCA%2Fmanufacture-lightgray.png?logo=github" /></a> <a class="reference external" href="https://translation.odoo-community.org/projects/manufacture-11-0/manufacture-11-0-mrp_multi_level"><img alt="Translate me on Weblate" src="https://img.shields.io/badge/weblate-Translate%20me-F47D42.png" /></a> <a class="reference external" href="https://runbot.odoo-community.org/runbot/129/11.0"><img alt="Try me on Runbot" src="https://img.shields.io/badge/runbot-Try%20me-875A7B.png" /></a></p> <p><a class="reference external" href="https://odoo-community.org/page/development-status"><img alt="Beta" src="https://img.shields.io/badge/maturity-Beta-yellow.png" /></a> <a class="reference external" href="http://www.gnu.org/licenses/lgpl-3.0-standalone.html"><img alt="License: LGPL-3" src="https://img.shields.io/badge/licence-LGPL--3-blue.png" /></a> <a class="reference external" href="https://github.com/OCA/manufacture/tree/12.0/mrp_multi_level"><img alt="OCA/manufacture" src="https://img.shields.io/badge/github-OCA%2Fmanufacture-lightgray.png?logo=github" /></a> <a class="reference external" href="https://translation.odoo-community.org/projects/manufacture-12-0/manufacture-12-0-mrp_multi_level"><img alt="Translate me on Weblate" src="https://img.shields.io/badge/weblate-Translate%20me-F47D42.png" /></a> <a class="reference external" href="https://runbot.odoo-community.org/runbot/129/12.0"><img alt="Try me on Runbot" src="https://img.shields.io/badge/runbot-Try%20me-875A7B.png" /></a></p>
<p>This module allows you to calculate, based in known inventory, demand, and <p>This module allows you to calculate, based in known inventory, demand, and
supply, and based on parameters set at product variant level, the new supply, and based on parameters set at product variant level, the new
procurements for each product.</p> procurements for each product.</p>
@@ -377,51 +377,53 @@ and explodes this down to the lowest level.</p>
<h1>Key Features</h1> <h1>Key Features</h1>
<ul class="simple"> <ul class="simple">
<li>MRP parameters set by product variant MRP area pairs.</li> <li>MRP parameters set by product variant MRP area pairs.</li>
<li>Integration with <a class="reference external" href="https://github.com/OCA/stock-logistics-warehouse/tree/11.0/stock_demand_estimate">Stock Demand Estimates</a> system.</li>
<li>Cron job to calculate the MRP demand.</li> <li>Cron job to calculate the MRP demand.</li>
<li>Manually calculate the MRP demand.</li> <li>Manually calculate the MRP demand.</li>
<li>Confirm the calculated MRP demand and create POs, or MOs.</li> <li>Confirm the calculated MRP demand and create POs, or MOs.</li>
<li>Able to see the products for which action is needed throught Planned Orders.</li> <li>Able to see the products for which action is needed throught Planned Orders.</li>
<li>Integration with <a class="reference external" href="https://github.com/OCA/stock-logistics-warehouse/tree/12.0/stock_demand_estimate">Stock Demand Estimates</a> system.
Note: You need to install <a class="reference external" href="https://github.com/OCA/manufacture/tree/12.0/mrp_multi_level_estimate">mrp_multi_level_estimate module</a>.</li>
</ul> </ul>
<p><strong>Table of contents</strong></p> <p><strong>Table of contents</strong></p>
<div class="contents local topic" id="contents"> <div class="contents local topic" id="contents">
<ul class="simple"> <ul class="simple">
<li><a class="reference internal" href="#configuration" id="id14">Configuration</a><ul> <li><a class="reference internal" href="#configuration" id="id15">Configuration</a><ul>
<li><a class="reference internal" href="#mrp-areas" id="id15">MRP Areas</a></li> <li><a class="reference internal" href="#mrp-areas" id="id16">MRP Areas</a></li>
<li><a class="reference internal" href="#product-mrp-area-parameters" id="id16">Product MRP Area Parameters</a></li> <li><a class="reference internal" href="#product-mrp-area-parameters" id="id17">Product MRP Area Parameters</a></li>
</ul> </ul>
</li> </li>
<li><a class="reference internal" href="#usage" id="id17">Usage</a></li> <li><a class="reference internal" href="#usage" id="id18">Usage</a></li>
<li><a class="reference internal" href="#changelog" id="id18">Changelog</a><ul> <li><a class="reference internal" href="#changelog" id="id19">Changelog</a><ul>
<li><a class="reference internal" href="#id1" id="id19">11.0.3.0.0 (2019-05-22)</a></li> <li><a class="reference internal" href="#id1" id="id20">12.0.1.0.0 (2019-08-05)</a></li>
<li><a class="reference internal" href="#id3" id="id20">11.0.2.2.0 (2019-05-02)</a></li> <li><a class="reference internal" href="#id2" id="id21">11.0.3.0.0 (2019-05-22)</a></li>
<li><a class="reference internal" href="#id5" id="id21">11.0.2.1.0 (2019-04-02)</a></li> <li><a class="reference internal" href="#id4" id="id22">11.0.2.2.0 (2019-05-02)</a></li>
<li><a class="reference internal" href="#id7" id="id22">11.0.2.0.0 (2018-11-20)</a></li> <li><a class="reference internal" href="#id6" id="id23">11.0.2.1.0 (2019-04-02)</a></li>
<li><a class="reference internal" href="#id9" id="id23">11.0.1.1.0 (2018-08-30)</a></li> <li><a class="reference internal" href="#id8" id="id24">11.0.2.0.0 (2018-11-20)</a></li>
<li><a class="reference internal" href="#id11" id="id24">11.0.1.0.1 (2018-08-03)</a></li> <li><a class="reference internal" href="#id10" id="id25">11.0.1.1.0 (2018-08-30)</a></li>
<li><a class="reference internal" href="#id13" id="id25">11.0.1.0.0 (2018-07-09)</a></li> <li><a class="reference internal" href="#id12" id="id26">11.0.1.0.1 (2018-08-03)</a></li>
<li><a class="reference internal" href="#id14" id="id27">11.0.1.0.0 (2018-07-09)</a></li>
</ul> </ul>
</li> </li>
<li><a class="reference internal" href="#bug-tracker" id="id26">Bug Tracker</a></li> <li><a class="reference internal" href="#bug-tracker" id="id28">Bug Tracker</a></li>
<li><a class="reference internal" href="#credits" id="id27">Credits</a><ul> <li><a class="reference internal" href="#credits" id="id29">Credits</a><ul>
<li><a class="reference internal" href="#authors" id="id28">Authors</a></li> <li><a class="reference internal" href="#authors" id="id30">Authors</a></li>
<li><a class="reference internal" href="#contributors" id="id29">Contributors</a></li> <li><a class="reference internal" href="#contributors" id="id31">Contributors</a></li>
<li><a class="reference internal" href="#maintainers" id="id30">Maintainers</a></li> <li><a class="reference internal" href="#maintainers" id="id32">Maintainers</a></li>
</ul> </ul>
</li> </li>
</ul> </ul>
</div> </div>
<div class="section" id="configuration"> <div class="section" id="configuration">
<h2><a class="toc-backref" href="#id14">Configuration</a></h2> <h2><a class="toc-backref" href="#id15">Configuration</a></h2>
<div class="section" id="mrp-areas"> <div class="section" id="mrp-areas">
<h3><a class="toc-backref" href="#id15">MRP Areas</a></h3> <h3><a class="toc-backref" href="#id16">MRP Areas</a></h3>
<ul class="simple"> <ul class="simple">
<li>Go to <em>Manufacturing &gt; Configuration &gt; MRP Areas</em> and define or edit <li>Go to <em>Manufacturing &gt; Configuration &gt; MRP Areas</em> and define or edit
any existing area. You can specify the working hours for every area.</li> any existing area. You can specify the working hours for every area.</li>
</ul> </ul>
</div> </div>
<div class="section" id="product-mrp-area-parameters"> <div class="section" id="product-mrp-area-parameters">
<h3><a class="toc-backref" href="#id16">Product MRP Area Parameters</a></h3> <h3><a class="toc-backref" href="#id17">Product MRP Area Parameters</a></h3>
<ul class="simple"> <ul class="simple">
<li>Go to <em>Manufacturing &gt; Master Data &gt; Product MRP Area Parameters</em> and set <li>Go to <em>Manufacturing &gt; Master Data &gt; Product MRP Area Parameters</em> and set
the MRP parameters for a given product and area.</li> the MRP parameters for a given product and area.</li>
@@ -429,7 +431,7 @@ the MRP parameters for a given product and area.</li>
</div> </div>
</div> </div>
<div class="section" id="usage"> <div class="section" id="usage">
<h2><a class="toc-backref" href="#id17">Usage</a></h2> <h2><a class="toc-backref" href="#id18">Usage</a></h2>
<p>To manually run the MRP scheduler:</p> <p>To manually run the MRP scheduler:</p>
<ol class="arabic simple"> <ol class="arabic simple">
<li>Go to <em>Manufacturing &gt; Operations &gt; Run MRP Multi Level</em>.</li> <li>Go to <em>Manufacturing &gt; Operations &gt; Run MRP Multi Level</em>.</li>
@@ -445,32 +447,42 @@ hand side gears in any record.</li>
</ol> </ol>
</div> </div>
<div class="section" id="changelog"> <div class="section" id="changelog">
<h2><a class="toc-backref" href="#id18">Changelog</a></h2> <h2><a class="toc-backref" href="#id19">Changelog</a></h2>
<div class="section" id="id1"> <div class="section" id="id1">
<h3><a class="toc-backref" href="#id19">11.0.3.0.0 (2019-05-22)</a></h3> <h3><a class="toc-backref" href="#id20">12.0.1.0.0 (2019-08-05)</a></h3>
<ul class="simple">
<li>[MIG] Migration to v12:<ul>
<li>Estimates as a forecasting mechanism is moved to a new module
(mrp_multi_level_estimate).</li>
</ul>
</li>
</ul>
</div>
<div class="section" id="id2">
<h3><a class="toc-backref" href="#id21">11.0.3.0.0 (2019-05-22)</a></h3>
<ul class="simple"> <ul class="simple">
<li>[REW/IMP] Rework to include Planned Orders. <li>[REW/IMP] Rework to include Planned Orders.
(<a class="reference external" href="https://github.com/OCA/manufacture/pull/365">#365</a>).</li> (<a class="reference external" href="https://github.com/OCA/manufacture/pull/365">#365</a>).</li>
<li>[IMP] Able to procure from a different location than the areas location.</li> <li>[IMP] Able to procure from a different location than the areas location.</li>
</ul> </ul>
</div> </div>
<div class="section" id="id3"> <div class="section" id="id4">
<h3><a class="toc-backref" href="#id20">11.0.2.2.0 (2019-05-02)</a></h3> <h3><a class="toc-backref" href="#id22">11.0.2.2.0 (2019-05-02)</a></h3>
<ul class="simple"> <ul class="simple">
<li>[IMP] Able to run MRP only for selected areas. <li>[IMP] Able to run MRP only for selected areas.
(<a class="reference external" href="https://github.com/OCA/manufacture/pull/360">#360</a>).</li> (<a class="reference external" href="https://github.com/OCA/manufacture/pull/360">#360</a>).</li>
</ul> </ul>
</div> </div>
<div class="section" id="id5"> <div class="section" id="id6">
<h3><a class="toc-backref" href="#id21">11.0.2.1.0 (2019-04-02)</a></h3> <h3><a class="toc-backref" href="#id23">11.0.2.1.0 (2019-04-02)</a></h3>
<ul class="simple"> <ul class="simple">
<li>[IMP] Implement <em>Nbr. Days</em> functionality to be able to group demand when <li>[IMP] Implement <em>Nbr. Days</em> functionality to be able to group demand when
generating supply proposals. generating supply proposals.
(<a class="reference external" href="https://github.com/OCA/manufacture/pull/345">#345</a>).</li> (<a class="reference external" href="https://github.com/OCA/manufacture/pull/345">#345</a>).</li>
</ul> </ul>
</div> </div>
<div class="section" id="id7"> <div class="section" id="id8">
<h3><a class="toc-backref" href="#id22">11.0.2.0.0 (2018-11-20)</a></h3> <h3><a class="toc-backref" href="#id24">11.0.2.0.0 (2018-11-20)</a></h3>
<ul class="simple"> <ul class="simple">
<li>[REW] Refactor MRP Area. <li>[REW] Refactor MRP Area.
(<a class="reference external" href="https://github.com/OCA/manufacture/pull/322">#322</a>):<ul> (<a class="reference external" href="https://github.com/OCA/manufacture/pull/322">#322</a>):<ul>
@@ -482,15 +494,15 @@ different areas.</li>
</li> </li>
</ul> </ul>
</div> </div>
<div class="section" id="id9"> <div class="section" id="id10">
<h3><a class="toc-backref" href="#id23">11.0.1.1.0 (2018-08-30)</a></h3> <h3><a class="toc-backref" href="#id25">11.0.1.1.0 (2018-08-30)</a></h3>
<ul class="simple"> <ul class="simple">
<li>[FIX] Consider <em>Qty Multiple</em> on product to propose the quantity to procure. <li>[FIX] Consider <em>Qty Multiple</em> on product to propose the quantity to procure.
(<a class="reference external" href="https://github.com/OCA/manufacture/pull/297">#297</a>)</li> (<a class="reference external" href="https://github.com/OCA/manufacture/pull/297">#297</a>)</li>
</ul> </ul>
</div> </div>
<div class="section" id="id11"> <div class="section" id="id12">
<h3><a class="toc-backref" href="#id24">11.0.1.0.1 (2018-08-03)</a></h3> <h3><a class="toc-backref" href="#id26">11.0.1.0.1 (2018-08-03)</a></h3>
<ul class="simple"> <ul class="simple">
<li>[FIX] User and system locales doesnt break MRP calculation. <li>[FIX] User and system locales doesnt break MRP calculation.
(<a class="reference external" href="https://github.com/OCA/manufacture/pull/290">#290</a>)</li> (<a class="reference external" href="https://github.com/OCA/manufacture/pull/290">#290</a>)</li>
@@ -499,32 +511,32 @@ as a related on MRP Areas.
(<a class="reference external" href="https://github.com/OCA/manufacture/pull/290">#290</a>)</li> (<a class="reference external" href="https://github.com/OCA/manufacture/pull/290">#290</a>)</li>
</ul> </ul>
</div> </div>
<div class="section" id="id13"> <div class="section" id="id14">
<h3><a class="toc-backref" href="#id25">11.0.1.0.0 (2018-07-09)</a></h3> <h3><a class="toc-backref" href="#id27">11.0.1.0.0 (2018-07-09)</a></h3>
<ul class="simple"> <ul class="simple">
<li>Start of the history.</li> <li>Start of the history.</li>
</ul> </ul>
</div> </div>
</div> </div>
<div class="section" id="bug-tracker"> <div class="section" id="bug-tracker">
<h2><a class="toc-backref" href="#id26">Bug Tracker</a></h2> <h2><a class="toc-backref" href="#id28">Bug Tracker</a></h2>
<p>Bugs are tracked on <a class="reference external" href="https://github.com/OCA/manufacture/issues">GitHub Issues</a>. <p>Bugs are tracked on <a class="reference external" href="https://github.com/OCA/manufacture/issues">GitHub Issues</a>.
In case of trouble, please check there if your issue has already been reported. 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 If you spotted it first, help us smashing it by providing a detailed and welcomed
<a class="reference external" href="https://github.com/OCA/manufacture/issues/new?body=module:%20mrp_multi_level%0Aversion:%2011.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**">feedback</a>.</p> <a class="reference external" href="https://github.com/OCA/manufacture/issues/new?body=module:%20mrp_multi_level%0Aversion:%2012.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**">feedback</a>.</p>
<p>Do not contact contributors directly about support or help with technical issues.</p> <p>Do not contact contributors directly about support or help with technical issues.</p>
</div> </div>
<div class="section" id="credits"> <div class="section" id="credits">
<h2><a class="toc-backref" href="#id27">Credits</a></h2> <h2><a class="toc-backref" href="#id29">Credits</a></h2>
<div class="section" id="authors"> <div class="section" id="authors">
<h3><a class="toc-backref" href="#id28">Authors</a></h3> <h3><a class="toc-backref" href="#id30">Authors</a></h3>
<ul class="simple"> <ul class="simple">
<li>Ucamco</li> <li>Ucamco</li>
<li>Eficent</li> <li>Eficent</li>
</ul> </ul>
</div> </div>
<div class="section" id="contributors"> <div class="section" id="contributors">
<h3><a class="toc-backref" href="#id29">Contributors</a></h3> <h3><a class="toc-backref" href="#id31">Contributors</a></h3>
<ul class="simple"> <ul class="simple">
<li>Wim Audenaert &lt;<a class="reference external" href="mailto:wim.audenaert&#64;ucamco.com">wim.audenaert&#64;ucamco.com</a>&gt;</li> <li>Wim Audenaert &lt;<a class="reference external" href="mailto:wim.audenaert&#64;ucamco.com">wim.audenaert&#64;ucamco.com</a>&gt;</li>
<li>Jordi Ballester &lt;<a class="reference external" href="mailto:jordi.ballester&#64;eficent.com">jordi.ballester&#64;eficent.com</a>&gt;</li> <li>Jordi Ballester &lt;<a class="reference external" href="mailto:jordi.ballester&#64;eficent.com">jordi.ballester&#64;eficent.com</a>&gt;</li>
@@ -532,7 +544,7 @@ If you spotted it first, help us smashing it by providing a detailed and welcome
</ul> </ul>
</div> </div>
<div class="section" id="maintainers"> <div class="section" id="maintainers">
<h3><a class="toc-backref" href="#id30">Maintainers</a></h3> <h3><a class="toc-backref" href="#id32">Maintainers</a></h3>
<p>This module is maintained by the OCA.</p> <p>This module is maintained by the OCA.</p>
<a class="reference external image-reference" href="https://odoo-community.org"><img alt="Odoo Community Association" src="https://odoo-community.org/logo.png" /></a> <a class="reference external image-reference" href="https://odoo-community.org"><img alt="Odoo Community Association" src="https://odoo-community.org/logo.png" /></a>
<p>OCA, or the Odoo Community Association, is a nonprofit organization whose <p>OCA, or the Odoo Community Association, is a nonprofit organization whose
@@ -540,7 +552,7 @@ mission is to support the collaborative development of Odoo features and
promote its widespread use.</p> promote its widespread use.</p>
<p>Current <a class="reference external" href="https://odoo-community.org/page/maintainer-role">maintainers</a>:</p> <p>Current <a class="reference external" href="https://odoo-community.org/page/maintainer-role">maintainers</a>:</p>
<p><a class="reference external" href="https://github.com/jbeficent"><img alt="jbeficent" src="https://github.com/jbeficent.png?size=40px" /></a> <a class="reference external" href="https://github.com/lreficent"><img alt="lreficent" src="https://github.com/lreficent.png?size=40px" /></a></p> <p><a class="reference external" href="https://github.com/jbeficent"><img alt="jbeficent" src="https://github.com/jbeficent.png?size=40px" /></a> <a class="reference external" href="https://github.com/lreficent"><img alt="lreficent" src="https://github.com/lreficent.png?size=40px" /></a></p>
<p>This module is part of the <a class="reference external" href="https://github.com/OCA/manufacture/tree/11.0/mrp_multi_level">OCA/manufacture</a> project on GitHub.</p> <p>This module is part of the <a class="reference external" href="https://github.com/OCA/manufacture/tree/12.0/mrp_multi_level">OCA/manufacture</a> project on GitHub.</p>
<p>You are welcome to contribute. To learn how please visit <a class="reference external" href="https://odoo-community.org/page/Contribute">https://odoo-community.org/page/Contribute</a>.</p> <p>You are welcome to contribute. To learn how please visit <a class="reference external" href="https://odoo-community.org/page/Contribute">https://odoo-community.org/page/Contribute</a>.</p>
</div> </div>
</div> </div>

View File

@@ -1,2 +1 @@
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from . import test_mrp_multi_level from . import test_mrp_multi_level

View File

@@ -0,0 +1,284 @@
# Copyright 2018-19 Eficent Business and IT Consulting Services S.L.
# (http://www.eficent.com)
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html).
from datetime import datetime
from odoo.tests.common import SavepointCase
class TestMrpMultiLevelCommon(SavepointCase):
@classmethod
def setUpClass(cls):
super().setUpClass()
cls.mo_obj = cls.env['mrp.production']
cls.po_obj = cls.env['purchase.order']
cls.product_obj = cls.env['product.product']
cls.loc_obj = cls.env['stock.location']
cls.mrp_area_obj = cls.env['mrp.area']
cls.product_mrp_area_obj = cls.env['product.mrp.area']
cls.partner_obj = cls.env['res.partner']
cls.res_users = cls.env['res.users']
cls.stock_picking_obj = cls.env['stock.picking']
cls.mrp_multi_level_wiz = cls.env['mrp.multi.level']
cls.mrp_inventory_procure_wiz = cls.env['mrp.inventory.procure']
cls.mrp_inventory_obj = cls.env['mrp.inventory']
cls.mrp_move_obj = cls.env['mrp.move']
cls.planned_order_obj = cls.env['mrp.planned.order']
cls.fp_1 = cls.env.ref('mrp_multi_level.product_product_fp_1')
cls.fp_2 = cls.env.ref('mrp_multi_level.product_product_fp_2')
cls.sf_1 = cls.env.ref('mrp_multi_level.product_product_sf_1')
cls.sf_2 = cls.env.ref('mrp_multi_level.product_product_sf_2')
cls.pp_1 = cls.env.ref('mrp_multi_level.product_product_pp_1')
cls.pp_2 = cls.env.ref('mrp_multi_level.product_product_pp_2')
cls.company = cls.env.ref('base.main_company')
cls.mrp_area = cls.env.ref('mrp_multi_level.mrp_area_stock_wh0')
cls.vendor = cls.env.ref('mrp_multi_level.res_partner_lazer_tech')
cls.wh = cls.env.ref('stock.warehouse0')
cls.stock_location = cls.wh.lot_stock_id
cls.customer_location = cls.env.ref(
'stock.stock_location_customers')
cls.calendar = cls.env.ref('resource.resource_calendar_std')
# Add calendar to WH:
cls.wh.calendar_id = cls.calendar
# Partner:
vendor1 = cls.partner_obj.create({'name': 'Vendor 1'})
# Create user:
group_mrp_manager = cls.env.ref('mrp.group_mrp_manager')
group_user = cls.env.ref('base.group_user')
group_stock_manager = cls.env.ref('stock.group_stock_manager')
cls.mrp_manager = cls._create_user(
'Test User',
[group_mrp_manager, group_user, group_stock_manager],
cls.company,
)
# Create secondary location and MRP Area:
cls.sec_loc = cls.loc_obj.create({
'name': 'Test location',
'usage': 'internal',
'location_id': cls.wh.view_location_id.id,
})
cls.secondary_area = cls.mrp_area_obj.create({
'name': 'Test',
'warehouse_id': cls.wh.id,
'location_id': cls.sec_loc.id,
})
# Create products:
route_buy = cls.env.ref('purchase_stock.route_warehouse0_buy').id
cls.prod_test = cls.product_obj.create({
'name': 'Test Top Seller',
'type': 'product',
'list_price': 150.0,
'produce_delay': 5.0,
'route_ids': [(6, 0, [route_buy])],
'seller_ids': [(0, 0, {'name': vendor1.id, 'price': 20.0})],
})
cls.product_mrp_area_obj.create({
'product_id': cls.prod_test.id,
'mrp_area_id': cls.mrp_area.id,
})
# Parameters in secondary area with nbr_days set.
cls.product_mrp_area_obj.create({
'product_id': cls.prod_test.id,
'mrp_area_id': cls.secondary_area.id,
'mrp_nbr_days': 7,
})
cls.prod_min = cls.product_obj.create({
'name': 'Product with minimum order qty',
'type': 'product',
'list_price': 50.0,
'route_ids': [(6, 0, [route_buy])],
'seller_ids': [(0, 0, {'name': vendor1.id, 'price': 10.0})],
})
cls.product_mrp_area_obj.create({
'product_id': cls.prod_min.id,
'mrp_area_id': cls.mrp_area.id,
'mrp_minimum_order_qty': 50.0,
'mrp_maximum_order_qty': 0.0,
'mrp_qty_multiple': 1.0,
})
cls.prod_max = cls.product_obj.create({
'name': 'Product with maximum order qty',
'type': 'product',
'list_price': 50.0,
'route_ids': [(6, 0, [route_buy])],
'seller_ids': [(0, 0, {'name': vendor1.id, 'price': 10.0})],
})
cls.product_mrp_area_obj.create({
'product_id': cls.prod_max.id,
'mrp_area_id': cls.mrp_area.id,
'mrp_minimum_order_qty': 50.0,
'mrp_maximum_order_qty': 100.0,
'mrp_qty_multiple': 1.0,
})
cls.prod_multiple = cls.product_obj.create({
'name': 'Product with qty multiple',
'type': 'product',
'list_price': 50.0,
'route_ids': [(6, 0, [route_buy])],
'seller_ids': [(0, 0, {'name': vendor1.id, 'price': 10.0})],
})
cls.product_mrp_area_obj.create({
'product_id': cls.prod_multiple.id,
'mrp_area_id': cls.mrp_area.id,
'mrp_minimum_order_qty': 50.0,
'mrp_maximum_order_qty': 500.0,
'mrp_qty_multiple': 25.0,
})
# Create test picking for FP-1 and FP-2:
res = cls.calendar.plan_days(7 + 1, datetime.today().replace(hour=0))
date_move = res.date()
cls.picking_1 = cls.stock_picking_obj.create({
'picking_type_id': cls.env.ref('stock.picking_type_out').id,
'location_id': cls.stock_location.id,
'location_dest_id': cls.customer_location.id,
'move_lines': [
(0, 0, {
'name': 'Test move fp-1',
'product_id': cls.fp_1.id,
'date_expected': date_move,
'date': date_move,
'product_uom': cls.fp_1.uom_id.id,
'product_uom_qty': 100,
'location_id': cls.stock_location.id,
'location_dest_id': cls.customer_location.id
}),
(0, 0, {
'name': 'Test move fp-2',
'product_id': cls.fp_2.id,
'date_expected': date_move,
'date': date_move,
'product_uom': cls.fp_2.uom_id.id,
'product_uom_qty': 15,
'location_id': cls.stock_location.id,
'location_dest_id': cls.customer_location.id
})]
})
cls.picking_1.action_confirm()
# Create test picking for procure qty adjustment tests:
cls.picking_2 = cls.stock_picking_obj.create({
'picking_type_id': cls.env.ref('stock.picking_type_out').id,
'location_id': cls.stock_location.id,
'location_dest_id': cls.customer_location.id,
'move_lines': [
(0, 0, {
'name': 'Test move prod_min',
'product_id': cls.prod_min.id,
'date_expected': date_move,
'date': date_move,
'product_uom': cls.prod_min.uom_id.id,
'product_uom_qty': 16,
'location_id': cls.stock_location.id,
'location_dest_id': cls.customer_location.id
}),
(0, 0, {
'name': 'Test move prod_max',
'product_id': cls.prod_max.id,
'date_expected': date_move,
'date': date_move,
'product_uom': cls.prod_max.uom_id.id,
'product_uom_qty': 140,
'location_id': cls.stock_location.id,
'location_dest_id': cls.customer_location.id
}),
(0, 0, {
'name': 'Test move prod_multiple',
'product_id': cls.prod_multiple.id,
'date_expected': date_move,
'date': date_move,
'product_uom': cls.prod_multiple.uom_id.id,
'product_uom_qty': 112,
'location_id': cls.stock_location.id,
'location_dest_id': cls.customer_location.id
})]
})
cls.picking_2.action_confirm()
# Create Test PO:
date_po = cls.calendar.plan_days(
1+1, datetime.today().replace(hour=0)).date()
cls.po = cls.po_obj.create({
'name': 'Test PO-001',
'partner_id': cls.vendor.id,
'order_line': [
(0, 0, {
'name': 'Test PP-2 line',
'product_id': cls.pp_2.id,
'date_planned': date_po,
'product_qty': 5.0,
'product_uom': cls.pp_2.uom_id.id,
'price_unit': 25.0,
})],
})
# Create test MO:
date_mo = cls.calendar.plan_days(
9 + 1, datetime.today().replace(hour=0)).date()
bom_fp_2 = cls.env.ref('mrp_multi_level.mrp_bom_fp_2')
cls.mo = cls.mo_obj.create({
'product_id': cls.fp_2.id,
'bom_id': bom_fp_2.id,
'product_qty': 12.0,
'product_uom_id': cls.fp_2.uom_id.id,
'date_planned_start': date_mo,
})
# Dates:
today = datetime.today().replace(hour=0)
cls.date_3 = cls.calendar.plan_days(3 + 1, today).date()
cls.date_5 = cls.calendar.plan_days(5 + 1, today).date()
cls.date_6 = cls.calendar.plan_days(6 + 1, today).date()
cls.date_7 = cls.calendar.plan_days(7 + 1, today).date()
cls.date_8 = cls.calendar.plan_days(8 + 1, today).date()
cls.date_9 = cls.calendar.plan_days(9 + 1, today).date()
cls.date_10 = cls.calendar.plan_days(10 + 1, today).date()
cls.date_20 = cls.calendar.plan_days(20 + 1, today).date()
cls.date_22 = cls.calendar.plan_days(22 + 1, today).date()
# Create movements in secondary area:
cls.create_demand_sec_loc(cls.date_8, 80.0)
cls.create_demand_sec_loc(cls.date_9, 50.0)
cls.create_demand_sec_loc(cls.date_10, 70.0)
cls.create_demand_sec_loc(cls.date_20, 46.0)
cls.create_demand_sec_loc(cls.date_22, 33.0)
cls.mrp_multi_level_wiz.create({}).run_mrp_multi_level()
@classmethod
def create_demand_sec_loc(cls, date_move, qty):
return cls.stock_picking_obj.create({
"picking_type_id": cls.env.ref("stock.picking_type_out").id,
"location_id": cls.sec_loc.id,
"location_dest_id": cls.customer_location.id,
"move_lines": [
(0, 0, {
"name": "Test move",
"product_id": cls.prod_test.id,
"date_expected": date_move,
"date": date_move,
"product_uom": cls.prod_test.uom_id.id,
"product_uom_qty": qty,
"location_id": cls.sec_loc.id,
"location_dest_id": cls.customer_location.id,
})],
})
@classmethod
def _create_user(cls, login, groups, company):
user = cls.res_users.create({
'name': login,
'login': login,
'password': 'demo',
'email': 'example@yourcompany.com',
'company_id': company.id,
'groups_id': [(6, 0, [group.id for group in groups])]
})
return user

View File

@@ -1,303 +1,12 @@
# Copyright 2018-19 Eficent Business and IT Consulting Services S.L. # Copyright 2018-19 Eficent Business and IT Consulting Services S.L.
# (http://www.eficent.com) # (http://www.eficent.com)
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). # License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html).
from datetime import datetime, timedelta from odoo.addons.mrp_multi_level.tests.common import TestMrpMultiLevelCommon
from odoo.tests.common import SavepointCase
from odoo import fields from odoo import fields
from dateutil.rrule import WEEKLY
class TestMrpMultiLevel(SavepointCase): class TestMrpMultiLevel(TestMrpMultiLevelCommon):
@classmethod
def setUpClass(cls):
super(TestMrpMultiLevel, cls).setUpClass()
cls.mo_obj = cls.env['mrp.production']
cls.po_obj = cls.env['purchase.order']
cls.product_obj = cls.env['product.product']
cls.loc_obj = cls.env['stock.location']
cls.mrp_area_obj = cls.env['mrp.area']
cls.product_mrp_area_obj = cls.env['product.mrp.area']
cls.partner_obj = cls.env['res.partner']
cls.res_users = cls.env['res.users']
cls.stock_picking_obj = cls.env['stock.picking']
cls.estimate_obj = cls.env['stock.demand.estimate']
cls.mrp_multi_level_wiz = cls.env['mrp.multi.level']
cls.mrp_inventory_procure_wiz = cls.env['mrp.inventory.procure']
cls.mrp_inventory_obj = cls.env['mrp.inventory']
cls.mrp_move_obj = cls.env['mrp.move']
cls.planned_order_obj = cls.env['mrp.planned.order']
cls.fp_1 = cls.env.ref('mrp_multi_level.product_product_fp_1')
cls.fp_2 = cls.env.ref('mrp_multi_level.product_product_fp_2')
cls.sf_1 = cls.env.ref('mrp_multi_level.product_product_sf_1')
cls.sf_2 = cls.env.ref('mrp_multi_level.product_product_sf_2')
cls.pp_1 = cls.env.ref('mrp_multi_level.product_product_pp_1')
cls.pp_2 = cls.env.ref('mrp_multi_level.product_product_pp_2')
cls.company = cls.env.ref('base.main_company')
cls.mrp_area = cls.env.ref('mrp_multi_level.mrp_area_stock_wh0')
cls.vendor = cls.env.ref('mrp_multi_level.res_partner_lazer_tech')
cls.wh = cls.env.ref('stock.warehouse0')
cls.stock_location = cls.wh.lot_stock_id
cls.customer_location = cls.env.ref(
'stock.stock_location_customers')
cls.calendar = cls.env.ref('resource.resource_calendar_std')
# Add calendar to WH:
cls.wh.calendar_id = cls.calendar
# Partner:
vendor1 = cls.partner_obj.create({'name': 'Vendor 1'})
# Create user:
group_mrp_manager = cls.env.ref('mrp.group_mrp_manager')
group_user = cls.env.ref('base.group_user')
group_stock_manager = cls.env.ref('stock.group_stock_manager')
cls.mrp_manager = cls._create_user(
'Test User',
[group_mrp_manager, group_user, group_stock_manager],
cls.company,
)
# Create secondary location and MRP Area:
cls.sec_loc = cls.loc_obj.create({
'name': 'Test location',
'usage': 'internal',
'location_id': cls.wh.view_location_id.id,
})
cls.secondary_area = cls.mrp_area_obj.create({
'name': 'Test',
'warehouse_id': cls.wh.id,
'location_id': cls.sec_loc.id,
})
# Create products:
route_buy = cls.env.ref('purchase.route_warehouse0_buy').id
cls.prod_test = cls.product_obj.create({
'name': 'Test Top Seller',
'type': 'product',
'list_price': 150.0,
'produce_delay': 5.0,
'route_ids': [(6, 0, [route_buy])],
'seller_ids': [(0, 0, {'name': vendor1.id, 'price': 20.0})],
})
cls.product_mrp_area_obj.create({
'product_id': cls.prod_test.id,
'mrp_area_id': cls.mrp_area.id,
})
# Parameters in secondary area with nbr_days set.
cls.product_mrp_area_obj.create({
'product_id': cls.prod_test.id,
'mrp_area_id': cls.secondary_area.id,
'mrp_nbr_days': 7,
})
cls.prod_min = cls.product_obj.create({
'name': 'Product with minimum order qty',
'type': 'product',
'list_price': 50.0,
'route_ids': [(6, 0, [route_buy])],
'seller_ids': [(0, 0, {'name': vendor1.id, 'price': 10.0})],
})
cls.product_mrp_area_obj.create({
'product_id': cls.prod_min.id,
'mrp_area_id': cls.mrp_area.id,
'mrp_minimum_order_qty': 50.0,
'mrp_maximum_order_qty': 0.0,
'mrp_qty_multiple': 1.0,
})
cls.prod_max = cls.product_obj.create({
'name': 'Product with maximum order qty',
'type': 'product',
'list_price': 50.0,
'route_ids': [(6, 0, [route_buy])],
'seller_ids': [(0, 0, {'name': vendor1.id, 'price': 10.0})],
})
cls.product_mrp_area_obj.create({
'product_id': cls.prod_max.id,
'mrp_area_id': cls.mrp_area.id,
'mrp_minimum_order_qty': 50.0,
'mrp_maximum_order_qty': 100.0,
'mrp_qty_multiple': 1.0,
})
cls.prod_multiple = cls.product_obj.create({
'name': 'Product with qty multiple',
'type': 'product',
'list_price': 50.0,
'route_ids': [(6, 0, [route_buy])],
'seller_ids': [(0, 0, {'name': vendor1.id, 'price': 10.0})],
})
cls.product_mrp_area_obj.create({
'product_id': cls.prod_multiple.id,
'mrp_area_id': cls.mrp_area.id,
'mrp_minimum_order_qty': 50.0,
'mrp_maximum_order_qty': 500.0,
'mrp_qty_multiple': 25.0,
})
# Create test picking for FP-1 and FP-2:
res = cls.calendar.plan_days(7+1, datetime.today())
date_move = res.date()
cls.picking_1 = cls.stock_picking_obj.create({
'picking_type_id': cls.env.ref('stock.picking_type_out').id,
'location_id': cls.stock_location.id,
'location_dest_id': cls.customer_location.id,
'move_lines': [
(0, 0, {
'name': 'Test move fp-1',
'product_id': cls.fp_1.id,
'date_expected': date_move,
'date': date_move,
'product_uom': cls.fp_1.uom_id.id,
'product_uom_qty': 100,
'location_id': cls.stock_location.id,
'location_dest_id': cls.customer_location.id
}),
(0, 0, {
'name': 'Test move fp-2',
'product_id': cls.fp_2.id,
'date_expected': date_move,
'date': date_move,
'product_uom': cls.fp_2.uom_id.id,
'product_uom_qty': 15,
'location_id': cls.stock_location.id,
'location_dest_id': cls.customer_location.id
})]
})
cls.picking_1.action_confirm()
# Create test picking for procure qty adjustment tests:
cls.picking_2 = cls.stock_picking_obj.create({
'picking_type_id': cls.env.ref('stock.picking_type_out').id,
'location_id': cls.stock_location.id,
'location_dest_id': cls.customer_location.id,
'move_lines': [
(0, 0, {
'name': 'Test move prod_min',
'product_id': cls.prod_min.id,
'date_expected': date_move,
'date': date_move,
'product_uom': cls.prod_min.uom_id.id,
'product_uom_qty': 16,
'location_id': cls.stock_location.id,
'location_dest_id': cls.customer_location.id
}),
(0, 0, {
'name': 'Test move prod_max',
'product_id': cls.prod_max.id,
'date_expected': date_move,
'date': date_move,
'product_uom': cls.prod_max.uom_id.id,
'product_uom_qty': 140,
'location_id': cls.stock_location.id,
'location_dest_id': cls.customer_location.id
}),
(0, 0, {
'name': 'Test move prod_multiple',
'product_id': cls.prod_multiple.id,
'date_expected': date_move,
'date': date_move,
'product_uom': cls.prod_multiple.uom_id.id,
'product_uom_qty': 112,
'location_id': cls.stock_location.id,
'location_dest_id': cls.customer_location.id
})]
})
cls.picking_2.action_confirm()
# Create Test PO:
date_po = cls.calendar.plan_days(1+1, datetime.today()).date()
cls.po = cls.po_obj.create({
'name': 'Test PO-001',
'partner_id': cls.vendor.id,
'order_line': [
(0, 0, {
'name': 'Test PP-2 line',
'product_id': cls.pp_2.id,
'date_planned': date_po,
'product_qty': 5.0,
'product_uom': cls.pp_2.uom_id.id,
'price_unit': 25.0,
})],
})
# Create test MO:
date_mo = cls.calendar.plan_days(9+1, datetime.today()).date()
bom_fp_2 = cls.env.ref('mrp_multi_level.mrp_bom_fp_2')
cls.mo = cls.mo_obj.create({
'product_id': cls.fp_2.id,
'bom_id': bom_fp_2.id,
'product_qty': 12.0,
'product_uom_id': cls.fp_2.uom_id.id,
'date_planned_start': date_mo,
})
# Dates (Strings):
today = datetime.today()
cls.date_3 = fields.Date.to_string(
cls.calendar.plan_days(3+1, datetime.today()).date())
cls.date_5 = fields.Date.to_string(
cls.calendar.plan_days(5+1, datetime.today()).date())
cls.date_6 = fields.Date.to_string(
cls.calendar.plan_days(6+1, datetime.today()).date())
cls.date_7 = fields.Date.to_string(
cls.calendar.plan_days(7+1, datetime.today()).date())
cls.date_8 = fields.Date.to_string((
cls.calendar.plan_days(8+1, datetime.today()).date()))
cls.date_9 = fields.Date.to_string((
cls.calendar.plan_days(9+1, datetime.today()).date()))
cls.date_10 = fields.Date.to_string(
cls.calendar.plan_days(10+1, datetime.today()).date())
# Create Date Ranges:
cls.dr_type = cls.env['date.range.type'].create({
'name': 'Weeks',
'company_id': False,
'allow_overlap': False,
})
generator = cls.env['date.range.generator'].create({
'date_start': today - timedelta(days=3),
'name_prefix': 'W-',
'type_id': cls.dr_type.id,
'duration_count': 1,
'unit_of_time': WEEKLY,
'count': 3})
generator.action_apply()
# Create Demand Estimates:
ranges = cls.env['date.range'].search(
[('type_id', '=', cls.dr_type.id)])
qty = 140.0
for dr in ranges:
qty += 70.0
cls._create_demand_estimate(
cls.prod_test, cls.stock_location, dr, qty)
cls._create_demand_estimate(
cls.prod_test, cls.sec_loc, dr, qty)
cls.mrp_multi_level_wiz.create({}).run_mrp_multi_level()
@classmethod
def _create_demand_estimate(cls, product, location, date_range, qty):
cls.estimate_obj.create({
'product_id': product.id,
'location_id': location.id,
'product_uom': product.uom_id.id,
'product_uom_qty': qty,
'date_range_id': date_range.id,
})
@classmethod
def _create_user(cls, login, groups, company):
user = cls.res_users.create({
'name': login,
'login': login,
'password': 'demo',
'email': 'example@yourcompany.com',
'company_id': company.id,
'groups_id': [(6, 0, [group.id for group in groups])]
})
return user
def test_01_mrp_levels(self): def test_01_mrp_levels(self):
"""Tests computation of MRP levels.""" """Tests computation of MRP levels."""
@@ -461,36 +170,7 @@ class TestMrpMultiLevel(SavepointCase):
expected = [0.0, 0.0] # No grouping, lot size nor safety stock. expected = [0.0, 0.0] # No grouping, lot size nor safety stock.
self.assertEqual(invs.mapped('running_availability'), expected) self.assertEqual(invs.mapped('running_availability'), expected)
def test_06_demand_estimates(self): def test_06_procure_mo(self):
"""Tests demand estimates integration."""
estimates = self.estimate_obj.search([
('product_id', '=', self.prod_test.id),
('location_id', '=', self.stock_location.id)])
self.assertEqual(len(estimates), 3)
moves = self.mrp_move_obj.search([
('product_id', '=', self.prod_test.id),
('mrp_area_id', '=', self.mrp_area.id),
])
# 3 weeks - 3 days in the past = 18 days of valid estimates:
moves_from_estimates = moves.filtered(lambda m: m.mrp_type == 'd')
self.assertEqual(len(moves_from_estimates), 18)
quantities = moves_from_estimates.mapped('mrp_qty')
self.assertIn(-30.0, quantities) # 210 a week => 30.0 dayly:
self.assertIn(-40.0, quantities) # 280 a week => 40.0 dayly:
self.assertIn(-50.0, quantities) # 350 a week => 50.0 dayly:
plans = self.planned_order_obj.search([
('product_id', '=', self.prod_test.id),
('mrp_area_id', '=', self.mrp_area.id),
])
action = list(set(plans.mapped("mrp_action")))
self.assertEqual(len(action), 1)
self.assertEqual(action[0], "buy")
self.assertEqual(len(plans), 18)
inventories = self.mrp_inventory_obj.search([
('mrp_area_id', '=', self.secondary_area.id)])
self.assertEqual(len(inventories), 18)
def test_07_procure_mo(self):
"""Test procurement wizard with MOs.""" """Test procurement wizard with MOs."""
mos = self.mo_obj.search([ mos = self.mo_obj.search([
('product_id', '=', self.fp_1.id)]) ('product_id', '=', self.fp_1.id)])
@@ -506,10 +186,10 @@ class TestMrpMultiLevel(SavepointCase):
('product_id', '=', self.fp_1.id)]) ('product_id', '=', self.fp_1.id)])
self.assertTrue(mos) self.assertTrue(mos)
self.assertEqual(mos.product_qty, 100.0) self.assertEqual(mos.product_qty, 100.0)
mo_date_start = mos.date_planned_start.split(' ')[0] mo_date_start = fields.Date.to_date(mos.date_planned_start)
self.assertEqual(mo_date_start, self.date_5) self.assertEqual(mo_date_start, self.date_5)
def test_08_adjust_qty_to_order(self): def test_07_adjust_qty_to_order(self):
"""Test the adjustments made to the qty to procure when minimum, """Test the adjustments made to the qty to procure when minimum,
maximum order quantities and quantity multiple are set.""" maximum order quantities and quantity multiple are set."""
# minimum order quantity: # minimum order quantity:
@@ -531,12 +211,12 @@ class TestMrpMultiLevel(SavepointCase):
('product_mrp_area_id.product_id', '=', self.prod_multiple.id)]) ('product_mrp_area_id.product_id', '=', self.prod_multiple.id)])
self.assertEqual(mrp_inv_multiple.to_procure, 125) self.assertEqual(mrp_inv_multiple.to_procure, 125)
def test_09_group_demand(self): def test_08_group_demand(self):
"""Test demand grouping functionality, `nbr_days`.""" """Test demand grouping functionality, `nbr_days`."""
estimates = self.estimate_obj.search([ pickings = self.stock_picking_obj.search([
('product_id', '=', self.prod_test.id), ('product_id', '=', self.prod_test.id),
('location_id', '=', self.sec_loc.id)]) ('location_id', '=', self.sec_loc.id)])
self.assertEqual(len(estimates), 3) self.assertEqual(len(pickings), 5)
moves = self.mrp_move_obj.search([ moves = self.mrp_move_obj.search([
('product_id', '=', self.prod_test.id), ('product_id', '=', self.prod_test.id),
('mrp_area_id', '=', self.secondary_area.id), ('mrp_area_id', '=', self.secondary_area.id),
@@ -545,20 +225,19 @@ class TestMrpMultiLevel(SavepointCase):
('product_id', '=', self.prod_test.id), ('product_id', '=', self.prod_test.id),
('mrp_area_id', '=', self.secondary_area.id), ('mrp_area_id', '=', self.secondary_area.id),
]) ])
# 3 weeks - 3 days in the past = 18 days of valid estimates: moves_demand = moves.filtered(lambda m: m.mrp_type == 'd')
moves_from_estimates = moves.filtered(lambda m: m.mrp_type == 'd') self.assertEqual(len(moves_demand), 5)
self.assertEqual(len(moves_from_estimates), 18) # two groups expected:
# 18 days of demand / 7 nbr_days = 2.57 => 3 supply moves expected. # 1. days 8, 9 and 10.
self.assertEqual(len(supply_plans), 3) # 2. days 20, and 22.
self.assertEqual(len(supply_plans), 2)
quantities = supply_plans.mapped('mrp_qty') quantities = supply_plans.mapped('mrp_qty')
week_1_expected = sum(moves_from_estimates[0:7].mapped('mrp_qty')) week_1_expected = sum(moves_demand[0:3].mapped('mrp_qty'))
self.assertIn(abs(week_1_expected), quantities) self.assertIn(abs(week_1_expected), quantities)
week_2_expected = sum(moves_from_estimates[7:14].mapped('mrp_qty')) week_2_expected = sum(moves_demand[3:].mapped('mrp_qty'))
self.assertIn(abs(week_2_expected), quantities) self.assertIn(abs(week_2_expected), quantities)
week_3_expected = sum(moves_from_estimates[14:].mapped('mrp_qty'))
self.assertIn(abs(week_3_expected), quantities)
def test_10_isolated_mrp_area_run(self): def test_09_isolated_mrp_area_run(self):
"""Test running MRP for just one area.""" """Test running MRP for just one area."""
self.mrp_multi_level_wiz.sudo(self.mrp_manager).create({ self.mrp_multi_level_wiz.sudo(self.mrp_manager).create({
'mrp_area_ids': [(6, 0, self.secondary_area.ids)], 'mrp_area_ids': [(6, 0, self.secondary_area.ids)],
@@ -571,6 +250,3 @@ class TestMrpMultiLevel(SavepointCase):
prev = self.mrp_inventory_obj.search([ prev = self.mrp_inventory_obj.search([
('mrp_area_id', '!=', self.secondary_area.id)], limit=1) ('mrp_area_id', '!=', self.secondary_area.id)], limit=1)
self.assertNotEqual(this.create_uid, prev.create_uid) self.assertNotEqual(this.create_uid, prev.create_uid)
# TODO: test procure wizard: pos, multiple...
# TODO: test multiple destination IDS:...

View File

@@ -21,7 +21,7 @@
<field name="supply_qty"/> <field name="supply_qty"/>
<field name="final_on_hand_qty"/> <field name="final_on_hand_qty"/>
<field name="to_procure"/> <field name="to_procure"/>
<field name="uom_id" groups="product.group_uom"/> <field name="uom_id" groups="uom.group_uom"/>
</group> </group>
</group> </group>
</sheet> </sheet>
@@ -38,7 +38,7 @@
<field name="mrp_area_id"/> <field name="mrp_area_id"/>
<field name="product_id"/> <field name="product_id"/>
<field name="date"/> <field name="date"/>
<field name="uom_id" groups="product.group_uom"/> <field name="uom_id" groups="uom.group_uom"/>
<field name="initial_on_hand_qty"/> <field name="initial_on_hand_qty"/>
<field name="demand_qty"/> <field name="demand_qty"/>
<field name="supply_qty"/> <field name="supply_qty"/>
@@ -94,15 +94,20 @@
domain="[['to_procure','>',0.0]]"/> domain="[['to_procure','>',0.0]]"/>
<separator/> <separator/>
<group expand="0" string="Group By..."> <group expand="0" string="Group By...">
<filter string="Product" <filter name="group_product"
string="Product"
context="{'group_by':'product_mrp_area_id'}"/> context="{'group_by':'product_mrp_area_id'}"/>
<filter string="MRP Area" <filter name="group_mrp_area"
string="MRP Area"
context="{'group_by':'mrp_area_id'}"/> context="{'group_by':'mrp_area_id'}"/>
<filter string="Date to Procure (By Day)" <filter name="group_release_date_day"
string="Date to Procure (By Day)"
context="{'group_by':'order_release_date:day'}"/> context="{'group_by':'order_release_date:day'}"/>
<filter string="Date to Procure (By Week)" <filter name="group_release_date_week"
string="Date to Procure (By Week)"
context="{'group_by':'order_release_date:week'}"/> context="{'group_by':'order_release_date:week'}"/>
<filter string="Date to Procure (By Month)" <filter name="group_release_date_month"
string="Date to Procure (By Month)"
context="{'group_by':'order_release_date:month'}"/> context="{'group_by':'order_release_date:month'}"/>
</group> </group>
</search> </search>

View File

@@ -51,7 +51,6 @@
<field name="mrp_exclude"/> <field name="mrp_exclude"/>
<field name="mrp_verified"/> <field name="mrp_verified"/>
<field name="mrp_nbr_days"/> <field name="mrp_nbr_days"/>
<field name="group_estimate_days"/>
<!--hide delays for now--> <!--hide delays for now-->
<field name="mrp_transit_delay" invisible="1"/> <field name="mrp_transit_delay" invisible="1"/>
<field name="mrp_inspection_delay" invisible="1"/> <field name="mrp_inspection_delay" invisible="1"/>

View File

@@ -1,6 +1,6 @@
# Copyright 2018-19 Eficent Business and IT Consulting Services S.L. # Copyright 2018-19 Eficent Business and IT Consulting Services S.L.
# (http://www.eficent.com) # (http://www.eficent.com)
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). # License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html).
from odoo import api, fields, models, _ from odoo import api, fields, models, _
from odoo.exceptions import UserError, ValidationError from odoo.exceptions import UserError, ValidationError
@@ -95,6 +95,7 @@ class MrpInventoryProcure(models.TransientModel):
class MrpInventoryProcureItem(models.TransientModel): class MrpInventoryProcureItem(models.TransientModel):
_name = 'mrp.inventory.procure.item' _name = 'mrp.inventory.procure.item'
_description = "MRP Inventory procure item"
wiz_id = fields.Many2one( wiz_id = fields.Many2one(
comodel_name='mrp.inventory.procure', string='Wizard', comodel_name='mrp.inventory.procure', string='Wizard',
@@ -103,7 +104,7 @@ class MrpInventoryProcureItem(models.TransientModel):
qty = fields.Float(string='Quantity') qty = fields.Float(string='Quantity')
uom_id = fields.Many2one( uom_id = fields.Many2one(
string='Unit of Measure', string='Unit of Measure',
comodel_name='product.uom', comodel_name='uom.uom',
) )
date_planned = fields.Date(string='Planned Date', required=False) date_planned = fields.Date(string='Planned Date', required=False)
mrp_inventory_id = fields.Many2one( mrp_inventory_id = fields.Many2one(

View File

@@ -21,7 +21,7 @@
<field name="location_id" groups="stock.group_stock_multi_locations" readonly="1"/> <field name="location_id" groups="stock.group_stock_multi_locations" readonly="1"/>
<field name="product_id" readonly="1"/> <field name="product_id" readonly="1"/>
<field name="qty"/> <field name="qty"/>
<field name="uom_id" groups="product.group_uom"/> <field name="uom_id" groups="uom.group_uom"/>
<field name="date_planned"/> <field name="date_planned"/>
</tree> </tree>
</field> </field>

View File

@@ -2,18 +2,17 @@
# Copyright 2016-19 Eficent Business and IT Consulting Services S.L. # Copyright 2016-19 Eficent Business and IT Consulting Services S.L.
# - Jordi Ballester Alomar <jordi.ballester@eficent.com> # - Jordi Ballester Alomar <jordi.ballester@eficent.com>
# - Lois Rilo <lois.rilo@eficent.com> # - Lois Rilo <lois.rilo@eficent.com>
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). # License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html).
from odoo import api, fields, models, exceptions, _ from odoo import api, fields, models, exceptions, _
from odoo.tools import DEFAULT_SERVER_DATETIME_FORMAT from datetime import date, timedelta
from datetime import date, datetime, timedelta
import logging import logging
from odoo.tools.float_utils import float_round
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
class MultiLevelMrp(models.TransientModel): class MultiLevelMrp(models.TransientModel):
_name = 'mrp.multi.level' _name = 'mrp.multi.level'
_description = "Multi Level MRP"
mrp_area_ids = fields.Many2many( mrp_area_ids = fields.Many2many(
comodel_name="mrp.area", comodel_name="mrp.area",
@@ -27,7 +26,6 @@ class MultiLevelMrp(models.TransientModel):
def _prepare_product_mrp_area_data(self, product_mrp_area): def _prepare_product_mrp_area_data(self, product_mrp_area):
qty_available = 0.0 qty_available = 0.0
product_obj = self.env['product.product'] product_obj = self.env['product.product']
# TODO: move mrp_qty_available computation, maybe unreserved??
location_ids = product_mrp_area.mrp_area_id._get_locations() location_ids = product_mrp_area.mrp_area_id._get_locations()
for location in location_ids: for location in location_ids:
product_l = product_obj.with_context( product_l = product_obj.with_context(
@@ -41,35 +39,6 @@ class MultiLevelMrp(models.TransientModel):
'mrp_llc': product_mrp_area.product_id.llc, 'mrp_llc': product_mrp_area.product_id.llc,
} }
@api.model
def _prepare_mrp_move_data_from_forecast(
self, estimate, product_mrp_area, date):
mrp_type = 'd'
origin = 'fc'
daily_qty = float_round(
estimate.daily_qty,
precision_rounding=product_mrp_area.product_id.uom_id.rounding,
rounding_method='HALF-UP')
return {
'mrp_area_id': product_mrp_area.mrp_area_id.id,
'product_id': product_mrp_area.product_id.id,
'product_mrp_area_id': product_mrp_area.id,
'production_id': None,
'purchase_order_id': None,
'purchase_line_id': None,
'stock_move_id': None,
'mrp_qty': -daily_qty * product_mrp_area.group_estimate_days,
'current_qty': -daily_qty,
'mrp_date': date,
'current_date': date,
'mrp_type': mrp_type,
'mrp_origin': origin,
'mrp_order_number': None,
'parent_product_id': None,
'name': 'Forecast',
'state': 'confirmed',
}
@api.model @api.model
def _prepare_mrp_move_data_from_stock_move( def _prepare_mrp_move_data_from_stock_move(
self, product_mrp_area, move, direction='in'): self, product_mrp_area, move, direction='in'):
@@ -91,7 +60,6 @@ class MultiLevelMrp(models.TransientModel):
origin = 'mo' origin = 'mo'
mo = move.production_id.id mo = move.production_id.id
else: else:
# TODO: move.move_dest_id -> move.move_dest_ids. DONE, review
if move.move_dest_ids: if move.move_dest_ids:
# move_dest_id = move.move_dest_ids[:1] # move_dest_id = move.move_dest_ids[:1]
for move_dest_id in move.move_dest_ids: for move_dest_id in move.move_dest_ids:
@@ -107,11 +75,8 @@ class MultiLevelMrp(models.TransientModel):
if order_number is None: if order_number is None:
order_number = move.name order_number = move.name
mrp_date = date.today() mrp_date = date.today()
if datetime.date(datetime.strptime( if move.date_expected.date() > date.today():
move.date_expected, mrp_date = move.date_expected.date()
DEFAULT_SERVER_DATETIME_FORMAT)) > date.today():
mrp_date = datetime.date(datetime.strptime(
move.date_expected, DEFAULT_SERVER_DATETIME_FORMAT))
return { return {
'product_id': move.product_id.id, 'product_id': move.product_id.id,
'product_mrp_area_id': product_mrp_area.id, 'product_mrp_area_id': product_mrp_area.id,
@@ -192,8 +157,9 @@ class MultiLevelMrp(models.TransientModel):
if calendar and product_mrp_area.mrp_lead_time: if calendar and product_mrp_area.mrp_lead_time:
date_str = fields.Date.to_string(mrp_date) date_str = fields.Date.to_string(mrp_date)
dt = fields.Datetime.from_string(date_str) dt = fields.Datetime.from_string(date_str)
# dt is at the beginning of the day (00:00)
res = calendar.plan_days( res = calendar.plan_days(
-1 * product_mrp_area.mrp_lead_time - 1, dt) -1 * product_mrp_area.mrp_lead_time, dt)
mrp_action_date = res.date() mrp_action_date = res.date()
else: else:
mrp_action_date = mrp_date - timedelta( mrp_action_date = mrp_date - timedelta(
@@ -359,35 +325,9 @@ class MultiLevelMrp(models.TransientModel):
logger.info(log_msg) logger.info(log_msg)
return True return True
@api.model
def _estimates_domain(self, product_mrp_area):
locations = product_mrp_area.mrp_area_id._get_locations()
return [
('product_id', '=', product_mrp_area.product_id.id),
('location_id', 'in', locations.ids),
('date_range_id.date_end', '>=', fields.Date.today()),
]
@api.model @api.model
def _init_mrp_move_from_forecast(self, product_mrp_area): def _init_mrp_move_from_forecast(self, product_mrp_area):
if not product_mrp_area.group_estimate_days: """This method is meant to be inherited to add a forecast mechanism."""
return False
today = fields.Date.today()
domain = self._estimates_domain(product_mrp_area)
estimates = self.env['stock.demand.estimate'].search(domain)
for rec in estimates:
start = rec.date_range_id.date_start
if start < today:
start = today
mrp_date = fields.Date.from_string(start)
date_end = fields.Date.from_string(rec.date_range_id.date_end)
delta = timedelta(days=product_mrp_area.group_estimate_days)
while mrp_date <= date_end:
mrp_move_data = \
self._prepare_mrp_move_data_from_forecast(
rec, product_mrp_area, mrp_date)
self.env['mrp.move'].create(mrp_move_data)
mrp_date += delta
return True return True
# TODO: move this methods to product_mrp_area?? to be able to # TODO: move this methods to product_mrp_area?? to be able to
@@ -416,7 +356,6 @@ class MultiLevelMrp(models.TransientModel):
@api.model @api.model
def _init_mrp_move_from_stock_move(self, product_mrp_area): def _init_mrp_move_from_stock_move(self, product_mrp_area):
# TODO: Should we exclude the quantity done from the moves?
move_obj = self.env['stock.move'] move_obj = self.env['stock.move']
mrp_move_obj = self.env['mrp.move'] mrp_move_obj = self.env['mrp.move']
in_domain = self._in_stock_moves_domain(product_mrp_area) in_domain = self._in_stock_moves_domain(product_mrp_area)