[IMP] mrp_multi_level:

* Planned Order release and due date become required.
* Add button to Product MRP Area to update MOQ from Supplier Info.
* Link Manufacturing Orders with Planned Orders.
* Allow Mrp Inventory Procure Wizard to be used from other models.
* Make MRP Inventory creation more extensible.
* Main Supplier computation (v13 requires explicit False definitions).
This commit is contained in:
hveficent
2020-03-02 17:13:45 +01:00
parent 48f52582c8
commit f76aec8e95
9 changed files with 136 additions and 47 deletions

View File

@@ -6,3 +6,5 @@ from . import mrp_move
from . import mrp_planned_order
from . import mrp_inventory
from . import product_mrp_area
from . import stock_rule
from . import mrp_production

View File

@@ -38,12 +38,12 @@ class MrpPlannedOrder(models.Model):
readonly=True,
)
order_release_date = fields.Date(
string="Release Date",
help="Order release date planned by MRP.",
string="Release Date", help="Order release date planned by MRP.", required=True
)
due_date = fields.Date(
string="Due Date",
help="Date in which the supply must have been completed.",
required=True,
)
qty_released = fields.Float()
fixed = fields.Boolean()

View File

@@ -0,0 +1,12 @@
# Copyright 2020 ForgeFlow S.L. (https://www.forgeflow.com)
# - Héctor Villarreal <hector.villarreal@forgeflow.com>
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html).
from odoo import fields, models
class MrpProduction(models.Model):
""" Manufacturing Orders """
_inherit = "mrp.production"
planned_order_id = fields.Many2one(comodel_name="mrp.planned.order")

View File

@@ -190,9 +190,14 @@ class ProductMRPArea(models.Model):
and (not r.company_id or r.company_id == rec.company_id)
)
if not suppliers:
rec.main_supplierinfo_id = False
rec.main_supplier_id = False
continue
rec.main_supplierinfo_id = suppliers[0]
rec.main_supplier_id = suppliers[0].name
for rec in self.filtered(lambda r: r.supply_method != "buy"):
rec.main_supplierinfo_id = False
rec.main_supplier_id = False
@api.multi
def _adjust_qty_to_order(self, qty_to_order):
@@ -209,3 +214,9 @@ class ProductMRPArea(models.Model):
self.mrp_maximum_order_qty:
return self.mrp_maximum_order_qty
return qty_to_order
def update_min_qty_from_main_supplier(self):
for rec in self.filtered(
lambda r: r.main_supplierinfo_id and r.supply_method == "buy"
):
rec.mrp_minimum_order_qty = rec.main_supplierinfo_id.min_qty

View File

@@ -0,0 +1,33 @@
# Copyright 2020 ForgeFlow S.L. (https://www.forgeflow.com)
# - Héctor Villarreal <hector.villarreal@forgeflow.com>
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html).
from odoo import models
class StockRule(models.Model):
_inherit = "stock.rule"
def _prepare_mo_vals(
self,
product_id,
product_qty,
product_uom,
location_id,
name,
origin,
values,
bom,
):
res = super()._prepare_mo_vals(
product_id,
product_qty,
product_uom,
location_id,
name,
origin,
values,
bom,
)
if "planned_order_id" in values:
res["planned_order_id"] = values["planned_order_id"]
return res

View File

@@ -24,9 +24,12 @@ class TestMrpMultiLevel(TestMrpMultiLevelCommon):
self.assertEqual(product_mrp_area.supply_method, 'buy')
self.assertEqual(product_mrp_area.main_supplier_id, self.vendor)
self.assertEqual(product_mrp_area.qty_available, 10.0)
product_mrp_area = self.product_mrp_area_obj.search([
('product_id', '=', self.sf_1.id)])
self.assertEqual(product_mrp_area.supply_method, 'manufacture')
product_mrp_area = self.product_mrp_area_obj.search(
[("product_id", "=", self.sf_1.id)]
)
self.assertEqual(product_mrp_area.supply_method, "manufacture")
self.assertFalse(product_mrp_area.main_supplier_id)
self.assertFalse(product_mrp_area.main_supplierinfo_id)
def test_03_mrp_moves(self):
"""Tests for mrp moves generated."""

View File

@@ -57,7 +57,17 @@
<field name="mrp_transit_delay" invisible="1"/>
<field name="mrp_inspection_delay" invisible="1"/>
<field name="mrp_minimum_stock"/>
<field name="mrp_minimum_order_qty"/>
<label for="mrp_minimum_order_qty"/>
<div name="mrp_minimum_order_qty" class="o_row">
<field name="mrp_minimum_order_qty"/>
<span name="update_min_qty">
<button string="Get from main supplier" type="object"
name="update_min_qty_from_main_supplier"
attrs="{'invisible':[('supply_method', '!=', 'buy'), ('main_supplierinfo_id', '=', False)]}"
class="oe_link pt-0 oe_inline"
/>
</span>
</div>
<field name="mrp_maximum_order_qty"/>
<field name="mrp_qty_multiple"/>
<field name="supply_method"/>

View File

@@ -50,20 +50,19 @@ class MrpInventoryProcure(models.TransientModel):
@api.model
def default_get(self, fields):
res = super(MrpInventoryProcure, self).default_get(fields)
mrp_inventory_obj = self.env['mrp.inventory']
mrp_inventory_ids = self.env.context['active_ids'] or []
active_model = self.env.context['active_model']
if not mrp_inventory_ids or 'item_ids' not in fields:
active_ids = self.env.context["active_ids"] or []
active_model = self.env.context["active_model"]
if not active_ids or "item_ids" not in fields:
return res
assert active_model == 'mrp.inventory', 'Bad context propagation'
items = item_obj = self.env['mrp.inventory.procure.item']
for line in mrp_inventory_obj.browse(mrp_inventory_ids).mapped(
'planned_order_ids'):
if line.qty_released < line.mrp_qty:
items += item_obj.create(self._prepare_item(line))
res['item_ids'] = [(6, 0, items.ids)]
if active_model == "mrp.inventory":
items = item_obj = self.env["mrp.inventory.procure.item"]
mrp_inventory_obj = self.env["mrp.inventory"]
for line in mrp_inventory_obj.browse(active_ids).mapped(
"planned_order_ids"
):
if line.qty_released < line.mrp_qty:
items += item_obj.create(self._prepare_item(line))
res["item_ids"] = [(6, 0, items.ids)]
return res
@api.multi
@@ -101,12 +100,9 @@ class MrpInventoryProcureItem(models.TransientModel):
comodel_name='mrp.inventory.procure', string='Wizard',
ondelete='cascade', readonly=True,
)
qty = fields.Float(string='Quantity')
uom_id = fields.Many2one(
string='Unit of Measure',
comodel_name='uom.uom',
)
date_planned = fields.Date(string='Planned Date', required=False)
qty = fields.Float(string="Quantity")
uom_id = fields.Many2one(string="Unit of Measure", comodel_name="uom.uom")
date_planned = fields.Date(string="Planned Date", required=True)
mrp_inventory_id = fields.Many2one(
string='Mrp Inventory',
comodel_name='mrp.inventory',
@@ -140,15 +136,17 @@ class MrpInventoryProcureItem(models.TransientModel):
("push", "Push To"),
("pull_push", "Pull & Push"),
],
readonly=True,
)
def _prepare_procurement_values(self, group=False):
return {
'date_planned': fields.Datetime.to_string(
fields.Date.from_string(self.date_planned)),
'warehouse_id': self.warehouse_id,
# 'company_id': self.company_id, # TODO: consider company
'group_id': group,
"date_planned": fields.Datetime.to_string(
fields.Date.from_string(self.date_planned)
),
"warehouse_id": self.warehouse_id,
"group_id": group,
"planned_order_id": self.planned_order_id.id,
}
@api.multi

View File

@@ -623,6 +623,33 @@ class MultiLevelMrp(models.TransientModel):
}
return query, params
@api.model
def _prepare_mrp_inventory_data(
self,
product_mrp_area,
mdt,
on_hand_qty,
running_availability,
demand_qty_by_date,
supply_qty_by_date,
planned_qty_by_date,
):
"""Return dict to create mrp.inventory records on MRP Multi Level Scheduler"""
mrp_inventory_data = {"product_mrp_area_id": product_mrp_area.id, "date": mdt}
demand_qty = demand_qty_by_date.get(mdt, 0.0)
mrp_inventory_data["demand_qty"] = abs(demand_qty)
supply_qty = supply_qty_by_date.get(mdt, 0.0)
mrp_inventory_data["supply_qty"] = abs(supply_qty)
mrp_inventory_data["initial_on_hand_qty"] = on_hand_qty
on_hand_qty += supply_qty + demand_qty
mrp_inventory_data["final_on_hand_qty"] = on_hand_qty
# Consider that MRP plan is followed exactly:
running_availability += (
supply_qty + demand_qty + planned_qty_by_date.get(mdt, 0.0)
)
mrp_inventory_data["running_availability"] = running_availability
return mrp_inventory_data, running_availability
@api.model
def _init_mrp_inventory(self, product_mrp_area):
mrp_move_obj = self.env['mrp.move']
@@ -659,23 +686,16 @@ class MultiLevelMrp(models.TransientModel):
product_mrp_area.product_id.id]['qty_available']
running_availability = on_hand_qty
for mdt in sorted(mrp_dates):
mrp_inventory_data = {
'product_mrp_area_id': product_mrp_area.id,
'date': mdt,
}
demand_qty = demand_qty_by_date.get(mdt, 0.0)
mrp_inventory_data['demand_qty'] = abs(demand_qty)
supply_qty = supply_qty_by_date.get(mdt, 0.0)
mrp_inventory_data['supply_qty'] = abs(supply_qty)
mrp_inventory_data['initial_on_hand_qty'] = on_hand_qty
on_hand_qty += (supply_qty + demand_qty)
mrp_inventory_data['final_on_hand_qty'] = on_hand_qty
# Consider that MRP plan is followed exactly:
running_availability += supply_qty \
+ demand_qty + planned_qty_by_date.get(mdt, 0.0)
mrp_inventory_data['running_availability'] = running_availability
inv_id = self.env['mrp.inventory'].create(mrp_inventory_data)
mrp_inventory_data, running_availability = self._prepare_mrp_inventory_data(
product_mrp_area,
mdt,
on_hand_qty,
running_availability,
demand_qty_by_date,
supply_qty_by_date,
planned_qty_by_date,
)
inv_id = self.env["mrp.inventory"].create(mrp_inventory_data)
# attach planned orders to inventory
planned_order_obj.search([
('due_date', '=', mdt),