[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
committed by davidborromeo
parent 4d5c984fd4
commit 6e43a5c129
10 changed files with 139 additions and 34 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

@@ -37,10 +37,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."
string="Due Date",
help="Date in which the supply must have been completed.",
required=True,
)
qty_released = fields.Float(readonly=True)
fixed = fields.Boolean(default=True)

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

@@ -171,9 +171,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
def _adjust_qty_to_order(self, qty_to_order):
self.ensure_one()
@@ -191,3 +196,9 @@ class ProductMRPArea(models.Model):
if self.mrp_maximum_order_qty and qty_to_order > 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,35 @@
# 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,
company_id,
values,
bom,
):
res = super()._prepare_mo_vals(
product_id,
product_qty,
product_uom,
location_id,
name,
origin,
company_id,
values,
bom,
)
if "planned_order_id" in values:
res["planned_order_id"] = values["planned_order_id"]
return res

View File

@@ -1,3 +1,16 @@
13.0.1.3.0 (2020-03-02)
~~~~~~~~~~~~~~~~~~~~~~~
* [IMP] Minor changes"
(`#470 <https://github.com/OCA/manufacture/pull/470>`_).
* 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)
13.0.1.2.0 (2020-02-20)
~~~~~~~~~~~~~~~~~~~~~~~

View File

@@ -28,6 +28,8 @@ class TestMrpMultiLevel(TestMrpMultiLevelCommon):
[("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

@@ -53,7 +53,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

@@ -47,21 +47,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_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:
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
def make_procurement(self):
@@ -109,7 +107,7 @@ class MrpInventoryProcureItem(models.TransientModel):
)
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)
date_planned = fields.Date(string="Planned Date", required=True)
mrp_inventory_id = fields.Many2one(
string="Mrp Inventory", comodel_name="mrp.inventory"
)
@@ -127,6 +125,7 @@ class MrpInventoryProcureItem(models.TransientModel):
("push", "Push To"),
("pull_push", "Pull & Push"),
],
readonly=True,
)
def _prepare_procurement_values(self, group=False):
@@ -135,8 +134,8 @@ class MrpInventoryProcureItem(models.TransientModel):
fields.Date.from_string(self.date_planned)
),
"warehouse_id": self.warehouse_id,
# 'company_id': self.company_id, # TODO: consider company
"group_id": group,
"planned_order_id": self.planned_order_id.id,
}
@api.onchange("uom_id")

View File

@@ -648,6 +648,33 @@ class MultiLevelMrp(models.TransientModel):
params = {"mrp_product": product_mrp_area.id}
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"]
@@ -683,23 +710,15 @@ class MultiLevelMrp(models.TransientModel):
)._product_available()[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 = 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,
)
mrp_inventory_data["running_availability"] = running_availability
inv_id = self.env["mrp.inventory"].create(mrp_inventory_data)
# attach planned orders to inventory
planned_order_obj.search(