mirror of
https://github.com/OCA/manufacture.git
synced 2025-01-28 16:37:15 +02:00
[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:
committed by
JasminSForgeFlow
parent
4baa262471
commit
68a7cb40cb
@@ -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
|
||||
|
||||
@@ -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)])
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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'),
|
||||
|
||||
60
mrp_multi_level/models/mrp_planned_order.py
Normal file
60
mrp_multi_level/models/mrp_planned_order.py
Normal 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",
|
||||
)
|
||||
@@ -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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user