[11.0][REW/IMP] mrp_multi_level:

* Extract concept of planned orders from mrp.move.
* Fix error grouping demand when there is no supply for a the first day of grouping.
* Adapt tests.
This commit is contained in:
Lois Rilo
2019-05-21 12:51:37 +02:00
committed by JasminSForgeFlow
parent 4baa262471
commit 68a7cb40cb
18 changed files with 467 additions and 348 deletions

View File

@@ -3,5 +3,6 @@ from . import stock_location
from . import product_product
from . import product_template
from . import mrp_move
from . import mrp_planned_order
from . import mrp_inventory
from . import product_mrp_area

View File

@@ -1,15 +1,16 @@
# © 2016 Ucamco - Wim Audenaert <wim.audenaert@ucamco.com>
# © 2016 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>
# - Lois Rilo Antelo <lois.rilo@eficent.com>
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
from odoo import fields, models
from odoo import api, fields, models
class MrpArea(models.Model):
_name = 'mrp.area'
name = fields.Char('Name')
name = fields.Char(required=True)
warehouse_id = fields.Many2one(
comodel_name='stock.warehouse', string='Warehouse',
required=True,
@@ -24,3 +25,9 @@ class MrpArea(models.Model):
string='Working Hours',
related='warehouse_id.calendar_id',
)
@api.multi
def _get_locations(self):
self.ensure_one()
return self.env['stock.location'].search([
('id', 'child_of', self.location_id.id)])

View File

@@ -1,6 +1,7 @@
# © 2016 Ucamco - Wim Audenaert <wim.audenaert@ucamco.com>
# Copyright 2016-18 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>
# - Lois Rilo Antelo <lois.rilo@eficent.com>
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
from odoo import api, fields, models
@@ -17,7 +18,6 @@ class MrpInventory(models.Model):
# TODO: name to pass to procurements?
# TODO: compute procurement_date to pass to the wizard? not needed for
# PO at least. Check for MO and moves
# TODO: show a LT based on the procure method?
mrp_area_id = fields.Many2one(
comodel_name='mrp.area', string='MRP Area',
@@ -41,18 +41,38 @@ class MrpInventory(models.Model):
supply_qty = fields.Float(string='Supply')
initial_on_hand_qty = fields.Float(string='Starting Inventory')
final_on_hand_qty = fields.Float(string='Forecasted Inventory')
to_procure = fields.Float(string='To procure')
to_procure = fields.Float(
string="To procure",
compute="_compute_to_procure",
store=True,
)
running_availability = fields.Float(
string="Planned Availability",
help="Theoretical inventory level if all planned orders"
"were released.",
)
order_release_date = fields.Date(
string="Order Release Date",
compute="_compute_order_release_date",
store=True,
)
planned_order_ids = fields.One2many(
comodel_name="mrp.planned.order",
inverse_name="mrp_inventory_id",
readonly=True,
)
@api.multi
def _compute_uom_id(self):
for rec in self:
rec.uom_id = rec.product_mrp_area_id.product_id.uom_id
@api.depends("planned_order_ids", "planned_order_ids.qty_released")
def _compute_to_procure(self):
for rec in self:
rec.to_procure = sum(rec.planned_order_ids.mapped('mrp_qty')) - \
sum(rec.planned_order_ids.mapped('qty_released'))
@api.multi
@api.depends('product_mrp_area_id',
'product_mrp_area_id.main_supplierinfo_id',
@@ -61,12 +81,7 @@ class MrpInventory(models.Model):
def _compute_order_release_date(self):
today = date.today()
for rec in self.filtered(lambda r: r.date):
delay = 0
if rec.product_mrp_area_id.supply_method == 'buy':
delay = rec.product_mrp_area_id.main_supplierinfo_id.delay
elif rec.product_mrp_area_id.supply_method == 'manufacture':
delay = rec.product_mrp_area_id.mrp_lead_time
# TODO: 'move' supply method
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)
order_release_date = rec.mrp_area_id.calendar_id.plan_days(

View File

@@ -11,56 +11,41 @@ class MrpMove(models.Model):
# TODO: too many indexes...
product_mrp_area_id = fields.Many2one(
comodel_name="product.mrp.area",
string="Product", index=True,
)
mrp_area_id = fields.Many2one(
comodel_name='mrp.area',
related='product_mrp_area_id.mrp_area_id',
string='MRP Area',
comodel_name="mrp.area",
related="product_mrp_area_id.mrp_area_id",
string="MRP Area",
store=True,
index=True,
)
product_id = fields.Many2one(
comodel_name='product.product',
related='product_mrp_area_id.product_id',
store=True,
)
current_date = fields.Date(string='Current Date')
current_qty = fields.Float(string='Current Qty')
# TODO: cancel is not needed I think...
mrp_action = fields.Selection(
selection=[('mo', 'Manufacturing Order'),
('po', 'Purchase Order'),
('cancel', 'Cancel'),
('none', 'None')],
string='Action',
)
mrp_action_date = fields.Date(string='MRP Action Date')
mrp_date = fields.Date(string='MRP Date')
mrp_move_down_ids = fields.Many2many(
comodel_name='mrp.move',
relation='mrp_move_rel',
column1='move_up_id',
column2='move_down_id',
string='MRP Move DOWN',
)
mrp_move_up_ids = fields.Many2many(
comodel_name='mrp.move',
relation='mrp_move_rel',
column1='move_down_id',
column2='move_up_id',
string='MRP Move UP',
)
mrp_minimum_stock = fields.Float(
string='Minimum Stock',
related='product_mrp_area_id.mrp_minimum_stock',
planned_order_up_ids = fields.Many2many(
comodel_name="mrp.planned.order",
relation="mrp_move_planned_order_rel",
column1="move_down_id",
column2="order_id",
string="Planned Orders UP",
)
mrp_order_number = fields.Char(string='Order Number')
# TODO: replace by a char origin?
mrp_origin = fields.Selection(
selection=[('mo', 'Manufacturing Order'),
('po', 'Purchase Order'),
('mv', 'Move'),
('fc', 'Forecast'), ('mrp', 'MRP')],
('fc', 'Forecast'),
('mrp', 'MRP')],
string='Origin')
mrp_processed = fields.Boolean(string='Processed')
product_mrp_area_id = fields.Many2one(
comodel_name='product.mrp.area',
string='Product', index=True,
)
mrp_qty = fields.Float(string='MRP Quantity')
mrp_type = fields.Selection(
selection=[('s', 'Supply'), ('d', 'Demand')],
@@ -68,12 +53,8 @@ class MrpMove(models.Model):
)
name = fields.Char(string='Description')
parent_product_id = fields.Many2one(
comodel_name='product.product',
string='Parent Product', index=True,
)
product_id = fields.Many2one(
comodel_name='product.product',
string='Product', index=True,
comodel_name="product.product",
string="Parent Product", index=True,
)
production_id = fields.Many2one(
comodel_name='mrp.production',
@@ -87,7 +68,6 @@ class MrpMove(models.Model):
comodel_name='purchase.order',
string='Purchase Order', index=True,
)
running_availability = fields.Float(string='Running Availability')
state = fields.Selection(
selection=[('draft', 'Draft'),
('assigned', 'Assigned'),

View File

@@ -0,0 +1,60 @@
# 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).
from odoo import models, fields
class MrpPlannedOrder(models.Model):
_name = "mrp.planned.order"
_description = "Planned Order"
_order = "due_date, id"
name = fields.Char(string="Description")
product_mrp_area_id = fields.Many2one(
comodel_name="product.mrp.area",
string="Product",
index=True,
)
mrp_area_id = fields.Many2one(
comodel_name="mrp.area",
related="product_mrp_area_id.mrp_area_id",
string="MRP Area",
store=True,
index=True,
)
product_id = fields.Many2one(
comodel_name="product.product",
related="product_mrp_area_id.product_id",
store=True,
)
order_release_date = fields.Date(
string="Release Date",
help="Order release date planned by MRP.",
)
due_date = fields.Date(
string="Due Date",
help="Date in which the supply must have been completed.",
)
qty_released = fields.Float()
fixed = fields.Boolean()
mrp_qty = fields.Float(string="Quantity")
mrp_move_down_ids = fields.Many2many(
comodel_name="mrp.move",
relation="mrp_move_planned_order_rel",
column1="order_id",
column2="move_down_id",
string="MRP Move DOWN",
)
mrp_action = fields.Selection(
selection=[("manufacture", "Manufacturing Order"),
("buy", "Purchase Order"),
("move", "Transfer"),
("none", "None")],
string="Action",
)
mrp_inventory_id = fields.Many2one(
string="Associated MRP Inventory",
comodel_name="mrp.inventory",
ondelete="set null",
)

View File

@@ -1,6 +1,9 @@
# Copyright 2016 Ucamco - Wim Audenaert <wim.audenaert@ucamco.com>
# Copyright 2016-18 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>
# - Lois Rilo Antelo <lois.rilo@eficent.com>
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
from math import ceil
from odoo import api, fields, models
@@ -36,7 +39,7 @@ class ProductMRPArea(models.Model):
mrp_minimum_order_qty = fields.Float(
string='Minimum Order Qty', default=0.0,
)
mrp_minimum_stock = fields.Float(string='Minimum Stock')
mrp_minimum_stock = fields.Float(string="Safety Stock")
mrp_nbr_days = fields.Integer(
string='Nbr. Days', default=0,
help="Number of days to group demand for this product during the "
@@ -51,7 +54,7 @@ class ProductMRPArea(models.Model):
)
mrp_lead_time = fields.Float(
string='Lead Time',
related='product_id.produce_delay',
compute='_compute_mrp_lead_time',
)
main_supplier_id = fields.Many2one(
comodel_name='res.partner', string='Main Supplier',
@@ -71,13 +74,20 @@ class ProductMRPArea(models.Model):
compute='_compute_supply_method',
)
qty_available = fields.Float('Quantity Available',
compute='_compute_qty_available')
qty_available = fields.Float(
string="Quantity Available",
compute="_compute_qty_available",
)
mrp_move_ids = fields.One2many(
comodel_name='mrp.move',
inverse_name='product_mrp_area_id',
readonly=True,
)
planned_order_ids = fields.One2many(
comodel_name="mrp.planned.order",
inverse_name="product_mrp_area_id",
readonly=True,
)
_sql_constraints = [
('product_mrp_area_uniq', 'unique(product_id, mrp_area_id)',
'The product/MRP Area parameters combination must be unique.'),
@@ -89,10 +99,21 @@ class ProductMRPArea(models.Model):
area.mrp_area_id.name,
area.product_id.display_name)) for area in self]
@api.multi
def _compute_mrp_lead_time(self):
produced = self.filtered(lambda r: r.supply_method == "manufacture")
purchased = self.filtered(lambda r: r.supply_method == "buy")
for rec in produced:
rec.mrp_lead_time = rec.product_id.produce_delay
for rec in purchased:
rec.mrp_lead_time = rec.main_supplierinfo_id.delay
# TODO: 'move' supply method.
for rec in (self - produced - purchased):
rec.mrp_lead_time = 0
@api.multi
def _compute_qty_available(self):
for rec in self:
# TODO: move mrp_qty_available computation, maybe unreserved??
rec.qty_available = rec.product_id.with_context(
{'location': rec.mrp_area_id.location_id.id}).qty_available