Files
manufacture/mrp_multi_level/models/mrp_inventory.py
Matt Taylor 33cf4af1ac [FIX] mrp_multi_level: fix kit/phantom planning
fixes #1362

Ignoring qty_available for phantom products prevents double counting the
qty_available of components.

Creating planned orders for phantom products is simpler than recursively
exploding phantom BOMs. This also makes it easier to analyze the planning data
generated by the MRP calculation.
2024-10-29 07:53:58 -06:00

140 lines
4.8 KiB
Python

# © 2016 Ucamco - Wim Audenaert <wim.audenaert@ucamco.com>
# Copyright 2016-21 ForgeFlow S.L. (https://www.forgeflow.com)
# - Jordi Ballester Alomar <jordi.ballester@forgeflow.com>
# - Lois Rilo Antelo <lois.rilo@forgeflow.com>
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html).
from datetime import date, timedelta
from odoo import _, api, fields, models
class MrpInventory(models.Model):
_name = "mrp.inventory"
_order = "product_mrp_area_id, date"
_description = "MRP inventory projections"
_rec_name = "product_mrp_area_id"
# 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
mrp_area_id = fields.Many2one(
comodel_name="mrp.area",
string="MRP Area",
related="product_mrp_area_id.mrp_area_id",
store=True,
)
product_mrp_area_id = fields.Many2one(
comodel_name="product.mrp.area",
string="Product Parameters",
index=True,
required=True,
ondelete="cascade",
)
company_id = fields.Many2one(
comodel_name="res.company",
related="product_mrp_area_id.mrp_area_id.warehouse_id.company_id",
store=True,
)
product_id = fields.Many2one(
comodel_name="product.product",
related="product_mrp_area_id.product_id",
store=True,
)
uom_id = fields.Many2one(
comodel_name="uom.uom", string="Product UoM", compute="_compute_uom_id"
)
date = fields.Date()
demand_qty = fields.Float(string="Demand")
supply_qty = fields.Float(string="Supply")
initial_on_hand_qty = fields.Float(
string="Starting Inventory", group_operator="avg"
)
final_on_hand_qty = fields.Float(
string="Forecasted Inventory", group_operator="avg"
)
to_procure = fields.Float(compute="_compute_to_procure", store=True)
running_availability = fields.Float(
string="Planned Availability",
group_operator="avg",
help="Theoretical inventory level if all planned orders were released.",
)
order_release_date = fields.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
)
supply_method = fields.Selection(
string="Supply Method",
related="product_mrp_area_id.supply_method",
readonly=True,
store=True,
)
main_supplier_id = fields.Many2one(
string="Main Supplier",
related="product_mrp_area_id.main_supplier_id",
readonly=True,
store=True,
)
mrp_planner_id = fields.Many2one(
related="product_mrp_area_id.mrp_planner_id",
readonly=True,
store=True,
)
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 = (
0.0
if rec.supply_method == "phantom"
else sum(rec.planned_order_ids.mapped("mrp_qty"))
- sum(rec.planned_order_ids.mapped("qty_released"))
)
@api.depends(
"product_mrp_area_id",
"product_mrp_area_id.main_supplierinfo_id",
"product_mrp_area_id.mrp_lead_time",
"product_mrp_area_id.mrp_area_id.calendar_id",
)
def _compute_order_release_date(self):
today = date.today()
for rec in self.filtered(lambda r: r.date):
delay = rec.product_mrp_area_id.mrp_lead_time
if delay and rec.mrp_area_id.calendar_id:
dt_date = fields.Datetime.to_datetime(rec.date)
# dt_date is at the beginning of the day (00:00),
# so we can subtract the delay straight forward.
order_release_date = rec.mrp_area_id.calendar_id.plan_days(
-delay, dt_date
).date()
elif delay:
order_release_date = fields.Date.from_string(rec.date) - timedelta(
days=delay
)
else:
order_release_date = rec.date
if order_release_date < today:
order_release_date = today
rec.order_release_date = order_release_date
def action_open_planned_orders(self):
planned_order_ids = []
for rec in self:
planned_order_ids += rec.planned_order_ids.ids
domain = [("id", "in", planned_order_ids)]
return {
"name": _("Planned Orders"),
"type": "ir.actions.act_window",
"res_model": "mrp.planned.order",
"view_mode": "tree,form",
"domain": domain,
}