mirror of
https://github.com/OCA/manufacture.git
synced 2025-01-28 16:37:15 +02:00
[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:
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
12
mrp_multi_level/models/mrp_production.py
Normal file
12
mrp_multi_level/models/mrp_production.py
Normal 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")
|
||||
@@ -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
|
||||
|
||||
35
mrp_multi_level/models/stock_rule.py
Normal file
35
mrp_multi_level/models/stock_rule.py
Normal 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
|
||||
@@ -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)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
|
||||
@@ -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."""
|
||||
|
||||
@@ -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"/>
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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(
|
||||
|
||||
Reference in New Issue
Block a user