[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
: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
.. |badge2| image:: https://img.shields.io/badge/licence-LGPL--3-blue.png
:target: http://www.gnu.org/licenses/lgpl-3.0-standalone.html
:alt: License: LGPL-3
.. |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
.. |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
.. |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
|badge1| |badge2| |badge3| |badge4| |badge5|
@@ -36,11 +36,12 @@ Key Features
------------
* 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.
* Manually calculate the MRP demand.
* 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.
* 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**
@@ -81,6 +82,14 @@ To launch replenishment orders (moves, purchases, production orders...):
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)
~~~~~~~~~~~~~~~~~~~~~~~
@@ -138,7 +147,7 @@ Bug Tracker
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.
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.
@@ -182,6 +191,6 @@ Current `maintainers <https://odoo-community.org/page/maintainer-role>`__:
|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.

View File

@@ -1,11 +1,12 @@
# Copyright 2016 Ucamco - Wim Audenaert <wim.audenaert@ucamco.com>
# 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',
'version': '11.0.3.1.1',
'version': '12.0.1.0.0',
'development_status': 'Beta',
'license': 'AGPL-3',
'license': 'LGPL-3',
'author': 'Ucamco, '
'Eficent, '
'Odoo Community Association (OCA)',
@@ -15,9 +16,7 @@
'category': 'Manufacturing',
'depends': [
'mrp',
'stock',
'purchase',
'stock_demand_estimate',
'purchase_stock',
'mrp_warehouse_calendar',
],
'data': [

View File

@@ -6,26 +6,26 @@
</record>
<record id="stock_inventory_line_1" model="stock.inventory.line">
<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="product_qty">10</field>
<field name="location_id" ref="stock.stock_location_stock"/>
</record>
<record id="stock_inventory_line_2" model="stock.inventory.line">
<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="product_qty">20</field>
<field name="location_id" ref="stock.stock_location_stock"/>
</record>
<record id="stock_inventory_line_3" model="stock.inventory.line">
<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="product_qty">15</field>
<field name="location_id" ref="stock.stock_location_stock"/>
</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>

View File

@@ -4,20 +4,20 @@
<record id="mrp_bom_fp_1" model="mrp.bom">
<field name="product_tmpl_id"
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>
</record>
<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_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="bom_id" ref="mrp_bom_fp_1"/>
</record>
<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_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="bom_id" ref="mrp_bom_fp_1"/>
</record>
@@ -26,20 +26,20 @@
<record id="mrp_bom_fp_2" model="mrp.bom">
<field name="product_tmpl_id"
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>
</record>
<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_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="bom_id" ref="mrp_bom_fp_2"/>
</record>
<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_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="bom_id" ref="mrp_bom_fp_2"/>
</record>
@@ -47,20 +47,20 @@
<record id="mrp_bom_sf_1" model="mrp.bom">
<field name="product_tmpl_id"
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>
</record>
<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_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="bom_id" ref="mrp_bom_sf_1"/>
</record>
<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_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="bom_id" ref="mrp_bom_sf_1"/>
</record>
@@ -68,13 +68,13 @@
<record id="mrp_bom_sf_2" model="mrp.bom">
<field name="product_tmpl_id"
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>
</record>
<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_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="bom_id" ref="mrp_bom_sf_2"/>
</record>

View File

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

View File

@@ -1,5 +1,5 @@
# 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
from odoo import api, SUPERUSER_ID

View File

@@ -2,13 +2,14 @@
# © 2016-19 Eficent Business and IT Consulting Services S.L.
# - Jordi Ballester Alomar <jordi.ballester@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
class MrpArea(models.Model):
_name = 'mrp.area'
_description = "MRP Area"
name = fields.Char(required=True)
warehouse_id = fields.Many2one(

View File

@@ -2,7 +2,7 @@
# Copyright 2016-19 Eficent Business and IT Consulting Services S.L.
# - Jordi Ballester Alomar <jordi.ballester@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
@@ -34,7 +34,7 @@ class MrpInventory(models.Model):
store=True,
)
uom_id = fields.Many2one(
comodel_name='product.uom', string='Product UoM',
comodel_name='uom.uom', string='Product UoM',
compute='_compute_uom_id',
)
date = fields.Date(string='Date')
@@ -84,9 +84,11 @@ class MrpInventory(models.Model):
for rec in self.filtered(lambda r: r.date):
delay = rec.product_mrp_area_id.mrp_lead_time
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(
-delay - 1, dt_date).date()
-delay, dt_date).date()
else:
order_release_date = fields.Date.from_string(
rec.date) - timedelta(days=delay)

View File

@@ -1,19 +1,21 @@
# © 2016 Ucamco - Wim Audenaert <wim.audenaert@ucamco.com>
# © 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
class MrpMove(models.Model):
_name = 'mrp.move'
_description = "MRP Move"
_order = 'product_mrp_area_id, mrp_date, mrp_type desc, id'
# TODO: too many indexes...
product_mrp_area_id = fields.Many2one(
comodel_name="product.mrp.area",
string="Product", index=True,
string="Product MRP Area",
index=True,
required=True,
)
mrp_area_id = fields.Many2one(

View File

@@ -1,6 +1,6 @@
# Copyright 2019 Eficent Business and IT Consulting Services S.L.
# - 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
@@ -13,7 +13,7 @@ class MrpPlannedOrder(models.Model):
name = fields.Char(string="Description")
product_mrp_area_id = fields.Many2one(
comodel_name="product.mrp.area",
string="Product",
string="Product MRP Area",
index=True,
required=True,
)
@@ -52,7 +52,9 @@ class MrpPlannedOrder(models.Model):
mrp_action = fields.Selection(
selection=[("manufacture", "Manufacturing Order"),
("buy", "Purchase Order"),
("move", "Transfer"),
('pull', 'Pull From'),
('push', 'Push To'),
('pull_push', 'Pull & Push'),
("none", "None")],
string="Action",
)

View File

@@ -2,7 +2,7 @@
# Copyright 2016-19 Eficent Business and IT Consulting Services S.L.
# - Jordi Ballester Alomar <jordi.ballester@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
@@ -80,11 +80,12 @@ class ProductMRPArea(models.Model):
selection=[('buy', 'Buy'),
('none', 'Undefined'),
('manufacture', 'Produce'),
('move', 'Transfer')],
('pull', 'Pull From'),
('push', 'Push To'),
('pull_push', 'Pull & Push')],
string='Supply Method',
compute='_compute_supply_method',
)
qty_available = fields.Float(
string="Quantity Available",
compute="_compute_qty_available",
@@ -99,10 +100,7 @@ class ProductMRPArea(models.Model):
inverse_name="product_mrp_area_id",
readonly=True,
)
group_estimate_days = fields.Integer(
string="Group Days of Estimates",
default=1,
)
_sql_constraints = [
('product_mrp_area_uniq', 'unique(product_id, mrp_area_id)',
'The product/MRP Area parameters combination must be unique.'),
@@ -111,13 +109,12 @@ class ProductMRPArea(models.Model):
@api.multi
@api.constrains(
"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):
values = self.read([
"mrp_minimum_order_qty", "mrp_maximum_order_qty",
"mrp_qty_multiple",
"mrp_minimum_stock", "mrp_nbr_days", "group_estimate_days",
"mrp_qty_multiple", "mrp_minimum_stock", "mrp_nbr_days",
])
for rec in 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-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
from odoo import api, fields, models

View File

@@ -1,5 +1,5 @@
# 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
from odoo import api, fields, models

View File

@@ -1,7 +1,7 @@
# © 2016 Ucamco - Wim Audenaert <wim.audenaert@ucamco.com>
# © 2016 Eficent Business and IT Consulting Services S.L.
# - 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

View File

@@ -9,8 +9,9 @@ Key Features
------------
* 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.
* Manually calculate the MRP demand.
* 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.
* 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)
~~~~~~~~~~~~~~~~~~~~~~~

View File

@@ -3,7 +3,7 @@
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<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>
<style type="text/css">
@@ -367,7 +367,7 @@ ul.auto-toc {
!! This file is generated by oca-gen-addon-readme !!
!! 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
supply, and based on parameters set at product variant level, the new
procurements for each product.</p>
@@ -377,51 +377,53 @@ and explodes this down to the lowest level.</p>
<h1>Key Features</h1>
<ul class="simple">
<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>Manually calculate the MRP demand.</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>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>
<p><strong>Table of contents</strong></p>
<div class="contents local topic" id="contents">
<ul class="simple">
<li><a class="reference internal" href="#configuration" id="id14">Configuration</a><ul>
<li><a class="reference internal" href="#mrp-areas" id="id15">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="#configuration" id="id15">Configuration</a><ul>
<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="id17">Product MRP Area Parameters</a></li>
</ul>
</li>
<li><a class="reference internal" href="#usage" id="id17">Usage</a></li>
<li><a class="reference internal" href="#changelog" id="id18">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="#id3" id="id20">11.0.2.2.0 (2019-05-02)</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="#id7" id="id22">11.0.2.0.0 (2018-11-20)</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="#id11" id="id24">11.0.1.0.1 (2018-08-03)</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="#usage" id="id18">Usage</a></li>
<li><a class="reference internal" href="#changelog" id="id19">Changelog</a><ul>
<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="#id2" id="id21">11.0.3.0.0 (2019-05-22)</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="#id6" id="id23">11.0.2.1.0 (2019-04-02)</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="#id10" id="id25">11.0.1.1.0 (2018-08-30)</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>
</li>
<li><a class="reference internal" href="#bug-tracker" id="id26">Bug Tracker</a></li>
<li><a class="reference internal" href="#credits" id="id27">Credits</a><ul>
<li><a class="reference internal" href="#authors" id="id28">Authors</a></li>
<li><a class="reference internal" href="#contributors" id="id29">Contributors</a></li>
<li><a class="reference internal" href="#maintainers" id="id30">Maintainers</a></li>
<li><a class="reference internal" href="#bug-tracker" id="id28">Bug Tracker</a></li>
<li><a class="reference internal" href="#credits" id="id29">Credits</a><ul>
<li><a class="reference internal" href="#authors" id="id30">Authors</a></li>
<li><a class="reference internal" href="#contributors" id="id31">Contributors</a></li>
<li><a class="reference internal" href="#maintainers" id="id32">Maintainers</a></li>
</ul>
</li>
</ul>
</div>
<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">
<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">
<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>
</ul>
</div>
<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">
<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>
@@ -429,7 +431,7 @@ the MRP parameters for a given product and area.</li>
</div>
</div>
<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>
<ol class="arabic simple">
<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>
</div>
<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">
<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">
<li>[REW/IMP] Rework to include Planned Orders.
(<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>
</ul>
</div>
<div class="section" id="id3">
<h3><a class="toc-backref" href="#id20">11.0.2.2.0 (2019-05-02)</a></h3>
<div class="section" id="id4">
<h3><a class="toc-backref" href="#id22">11.0.2.2.0 (2019-05-02)</a></h3>
<ul class="simple">
<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>
</ul>
</div>
<div class="section" id="id5">
<h3><a class="toc-backref" href="#id21">11.0.2.1.0 (2019-04-02)</a></h3>
<div class="section" id="id6">
<h3><a class="toc-backref" href="#id23">11.0.2.1.0 (2019-04-02)</a></h3>
<ul class="simple">
<li>[IMP] Implement <em>Nbr. Days</em> functionality to be able to group demand when
generating supply proposals.
(<a class="reference external" href="https://github.com/OCA/manufacture/pull/345">#345</a>).</li>
</ul>
</div>
<div class="section" id="id7">
<h3><a class="toc-backref" href="#id22">11.0.2.0.0 (2018-11-20)</a></h3>
<div class="section" id="id8">
<h3><a class="toc-backref" href="#id24">11.0.2.0.0 (2018-11-20)</a></h3>
<ul class="simple">
<li>[REW] Refactor MRP Area.
(<a class="reference external" href="https://github.com/OCA/manufacture/pull/322">#322</a>):<ul>
@@ -482,15 +494,15 @@ different areas.</li>
</li>
</ul>
</div>
<div class="section" id="id9">
<h3><a class="toc-backref" href="#id23">11.0.1.1.0 (2018-08-30)</a></h3>
<div class="section" id="id10">
<h3><a class="toc-backref" href="#id25">11.0.1.1.0 (2018-08-30)</a></h3>
<ul class="simple">
<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>
</ul>
</div>
<div class="section" id="id11">
<h3><a class="toc-backref" href="#id24">11.0.1.0.1 (2018-08-03)</a></h3>
<div class="section" id="id12">
<h3><a class="toc-backref" href="#id26">11.0.1.0.1 (2018-08-03)</a></h3>
<ul class="simple">
<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>
@@ -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>
</ul>
</div>
<div class="section" id="id13">
<h3><a class="toc-backref" href="#id25">11.0.1.0.0 (2018-07-09)</a></h3>
<div class="section" id="id14">
<h3><a class="toc-backref" href="#id27">11.0.1.0.0 (2018-07-09)</a></h3>
<ul class="simple">
<li>Start of the history.</li>
</ul>
</div>
</div>
<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>.
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
<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>
</div>
<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">
<h3><a class="toc-backref" href="#id28">Authors</a></h3>
<h3><a class="toc-backref" href="#id30">Authors</a></h3>
<ul class="simple">
<li>Ucamco</li>
<li>Eficent</li>
</ul>
</div>
<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">
<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>
@@ -532,7 +544,7 @@ If you spotted it first, help us smashing it by providing a detailed and welcome
</ul>
</div>
<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>
<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
@@ -540,7 +552,7 @@ mission is to support the collaborative development of Odoo features and
promote its widespread use.</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>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>
</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

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.
# (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.tests.common import SavepointCase
from odoo.addons.mrp_multi_level.tests.common import TestMrpMultiLevelCommon
from odoo import fields
from dateutil.rrule import WEEKLY
class TestMrpMultiLevel(SavepointCase):
@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
class TestMrpMultiLevel(TestMrpMultiLevelCommon):
def test_01_mrp_levels(self):
"""Tests computation of MRP levels."""
@@ -461,36 +170,7 @@ class TestMrpMultiLevel(SavepointCase):
expected = [0.0, 0.0] # No grouping, lot size nor safety stock.
self.assertEqual(invs.mapped('running_availability'), expected)
def test_06_demand_estimates(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):
def test_06_procure_mo(self):
"""Test procurement wizard with MOs."""
mos = self.mo_obj.search([
('product_id', '=', self.fp_1.id)])
@@ -506,10 +186,10 @@ class TestMrpMultiLevel(SavepointCase):
('product_id', '=', self.fp_1.id)])
self.assertTrue(mos)
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)
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,
maximum order quantities and quantity multiple are set."""
# minimum order quantity:
@@ -531,12 +211,12 @@ class TestMrpMultiLevel(SavepointCase):
('product_mrp_area_id.product_id', '=', self.prod_multiple.id)])
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`."""
estimates = self.estimate_obj.search([
pickings = self.stock_picking_obj.search([
('product_id', '=', self.prod_test.id),
('location_id', '=', self.sec_loc.id)])
self.assertEqual(len(estimates), 3)
self.assertEqual(len(pickings), 5)
moves = self.mrp_move_obj.search([
('product_id', '=', self.prod_test.id),
('mrp_area_id', '=', self.secondary_area.id),
@@ -545,20 +225,19 @@ class TestMrpMultiLevel(SavepointCase):
('product_id', '=', self.prod_test.id),
('mrp_area_id', '=', self.secondary_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)
# 18 days of demand / 7 nbr_days = 2.57 => 3 supply moves expected.
self.assertEqual(len(supply_plans), 3)
moves_demand = moves.filtered(lambda m: m.mrp_type == 'd')
self.assertEqual(len(moves_demand), 5)
# two groups expected:
# 1. days 8, 9 and 10.
# 2. days 20, and 22.
self.assertEqual(len(supply_plans), 2)
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)
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)
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."""
self.mrp_multi_level_wiz.sudo(self.mrp_manager).create({
'mrp_area_ids': [(6, 0, self.secondary_area.ids)],
@@ -571,6 +250,3 @@ class TestMrpMultiLevel(SavepointCase):
prev = self.mrp_inventory_obj.search([
('mrp_area_id', '!=', self.secondary_area.id)], limit=1)
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="final_on_hand_qty"/>
<field name="to_procure"/>
<field name="uom_id" groups="product.group_uom"/>
<field name="uom_id" groups="uom.group_uom"/>
</group>
</group>
</sheet>
@@ -38,7 +38,7 @@
<field name="mrp_area_id"/>
<field name="product_id"/>
<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="demand_qty"/>
<field name="supply_qty"/>
@@ -94,15 +94,20 @@
domain="[['to_procure','>',0.0]]"/>
<separator/>
<group expand="0" string="Group By...">
<filter string="Product"
<filter name="group_product"
string="Product"
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'}"/>
<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'}"/>
<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'}"/>
<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'}"/>
</group>
</search>

View File

@@ -51,7 +51,6 @@
<field name="mrp_exclude"/>
<field name="mrp_verified"/>
<field name="mrp_nbr_days"/>
<field name="group_estimate_days"/>
<!--hide delays for now-->
<field name="mrp_transit_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.
# (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.exceptions import UserError, ValidationError
@@ -95,6 +95,7 @@ class MrpInventoryProcure(models.TransientModel):
class MrpInventoryProcureItem(models.TransientModel):
_name = 'mrp.inventory.procure.item'
_description = "MRP Inventory procure item"
wiz_id = fields.Many2one(
comodel_name='mrp.inventory.procure', string='Wizard',
@@ -103,7 +104,7 @@ class MrpInventoryProcureItem(models.TransientModel):
qty = fields.Float(string='Quantity')
uom_id = fields.Many2one(
string='Unit of Measure',
comodel_name='product.uom',
comodel_name='uom.uom',
)
date_planned = fields.Date(string='Planned Date', required=False)
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="product_id" readonly="1"/>
<field name="qty"/>
<field name="uom_id" groups="product.group_uom"/>
<field name="uom_id" groups="uom.group_uom"/>
<field name="date_planned"/>
</tree>
</field>

View File

@@ -2,18 +2,17 @@
# Copyright 2016-19 Eficent Business and IT Consulting Services S.L.
# - Jordi Ballester Alomar <jordi.ballester@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.tools import DEFAULT_SERVER_DATETIME_FORMAT
from datetime import date, datetime, timedelta
from datetime import date, timedelta
import logging
from odoo.tools.float_utils import float_round
logger = logging.getLogger(__name__)
class MultiLevelMrp(models.TransientModel):
_name = 'mrp.multi.level'
_description = "Multi Level MRP"
mrp_area_ids = fields.Many2many(
comodel_name="mrp.area",
@@ -27,7 +26,6 @@ class MultiLevelMrp(models.TransientModel):
def _prepare_product_mrp_area_data(self, product_mrp_area):
qty_available = 0.0
product_obj = self.env['product.product']
# TODO: move mrp_qty_available computation, maybe unreserved??
location_ids = product_mrp_area.mrp_area_id._get_locations()
for location in location_ids:
product_l = product_obj.with_context(
@@ -41,35 +39,6 @@ class MultiLevelMrp(models.TransientModel):
'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
def _prepare_mrp_move_data_from_stock_move(
self, product_mrp_area, move, direction='in'):
@@ -91,7 +60,6 @@ class MultiLevelMrp(models.TransientModel):
origin = 'mo'
mo = move.production_id.id
else:
# TODO: move.move_dest_id -> move.move_dest_ids. DONE, review
if move.move_dest_ids:
# move_dest_id = move.move_dest_ids[:1]
for move_dest_id in move.move_dest_ids:
@@ -107,11 +75,8 @@ class MultiLevelMrp(models.TransientModel):
if order_number is None:
order_number = move.name
mrp_date = date.today()
if datetime.date(datetime.strptime(
move.date_expected,
DEFAULT_SERVER_DATETIME_FORMAT)) > date.today():
mrp_date = datetime.date(datetime.strptime(
move.date_expected, DEFAULT_SERVER_DATETIME_FORMAT))
if move.date_expected.date() > date.today():
mrp_date = move.date_expected.date()
return {
'product_id': move.product_id.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:
date_str = fields.Date.to_string(mrp_date)
dt = fields.Datetime.from_string(date_str)
# dt is at the beginning of the day (00:00)
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()
else:
mrp_action_date = mrp_date - timedelta(
@@ -359,35 +325,9 @@ class MultiLevelMrp(models.TransientModel):
logger.info(log_msg)
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
def _init_mrp_move_from_forecast(self, product_mrp_area):
if not product_mrp_area.group_estimate_days:
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
"""This method is meant to be inherited to add a forecast mechanism."""
return True
# TODO: move this methods to product_mrp_area?? to be able to
@@ -416,7 +356,6 @@ class MultiLevelMrp(models.TransientModel):
@api.model
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']
mrp_move_obj = self.env['mrp.move']
in_domain = self._in_stock_moves_domain(product_mrp_area)