mirror of
https://github.com/OCA/manufacture.git
synced 2025-01-28 16:37:15 +02:00
[IMP] mrp_multi_level: safety stock
When a product has a safety stock in an area, and the current stock is below safety, but there are moves in the future, mrp_multi_level does not compute an immediate action to get back to the safety stock. This PR changes this behavior: when the mrp_moves are considered, before processing the first move in the future, we insert a resupply action to rebuild the safety stock. We also add some refactoring in the process: * add extension point on the wizard to compute the quantity to reorder (so we can choose in a separate module whether to rebuild safety stock or not) * add extension point on the wizard to get the date at which the safety stock must be rebuilt (defaults to today) * make the code of the wizard symetric between the groupes and non grouped configuration
This commit is contained in:
committed by
BernatPForgeFlow
parent
e2c92f9d5a
commit
9d0b11ddbe
@@ -7,7 +7,7 @@ MRP Multi Level
|
|||||||
!! This file is generated by oca-gen-addon-readme !!
|
!! This file is generated by oca-gen-addon-readme !!
|
||||||
!! changes will be overwritten. !!
|
!! changes will be overwritten. !!
|
||||||
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||||
!! source digest: sha256:6be0420cb90de94af20fe0c8f65391d1b24738d2b1135fde9200b5f6702c5b22
|
!! source digest: sha256:ec517bd88092825314f02e061fee665bbf6c106491161601a02197e6d9beff36
|
||||||
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||||
|
|
||||||
.. |badge1| image:: https://img.shields.io/badge/maturity-Production%2FStable-green.png
|
.. |badge1| image:: https://img.shields.io/badge/maturity-Production%2FStable-green.png
|
||||||
@@ -60,6 +60,8 @@ MRP Areas
|
|||||||
* Go to *Manufacturing > Configuration > MRP Areas* and define or edit
|
* Go to *Manufacturing > Configuration > MRP Areas* and define or edit
|
||||||
any existing area. You can specify the working hours for every area.
|
any existing area. You can specify the working hours for every area.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Product MRP Area Parameters
|
Product MRP Area Parameters
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
@@ -229,6 +231,8 @@ Contributors
|
|||||||
* Jordi Ballester <jordi.ballester@forgeflow.com>
|
* Jordi Ballester <jordi.ballester@forgeflow.com>
|
||||||
* Lois Rilo <lois.rilo@forgeflow.com>
|
* Lois Rilo <lois.rilo@forgeflow.com>
|
||||||
* Héctor Villarreal <hector.villarreal@forgeflow.com>
|
* Héctor Villarreal <hector.villarreal@forgeflow.com>
|
||||||
|
* Christopher Ormaza <chris.ormaza@forgeflow.com>
|
||||||
|
* Alexandre Fayolle <alexandre.fayolle@camptocamp.com>
|
||||||
|
|
||||||
Maintainers
|
Maintainers
|
||||||
~~~~~~~~~~~
|
~~~~~~~~~~~
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
# - Jordi Ballester Alomar <jordi.ballester@forgeflow.com>
|
# - Jordi Ballester Alomar <jordi.ballester@forgeflow.com>
|
||||||
# - Lois Rilo Antelo <lois.rilo@forgeflow.com>
|
# - Lois Rilo Antelo <lois.rilo@forgeflow.com>
|
||||||
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html).
|
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html).
|
||||||
|
|
||||||
from math import ceil
|
from math import ceil
|
||||||
|
|
||||||
from odoo import _, api, fields, models
|
from odoo import _, api, fields, models
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ MRP Areas
|
|||||||
* Go to *Manufacturing > Configuration > MRP Areas* and define or edit
|
* Go to *Manufacturing > Configuration > MRP Areas* and define or edit
|
||||||
any existing area. You can specify the working hours for every area.
|
any existing area. You can specify the working hours for every area.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Product MRP Area Parameters
|
Product MRP Area Parameters
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
|||||||
@@ -2,3 +2,5 @@
|
|||||||
* Jordi Ballester <jordi.ballester@forgeflow.com>
|
* Jordi Ballester <jordi.ballester@forgeflow.com>
|
||||||
* Lois Rilo <lois.rilo@forgeflow.com>
|
* Lois Rilo <lois.rilo@forgeflow.com>
|
||||||
* Héctor Villarreal <hector.villarreal@forgeflow.com>
|
* Héctor Villarreal <hector.villarreal@forgeflow.com>
|
||||||
|
* Christopher Ormaza <chris.ormaza@forgeflow.com>
|
||||||
|
* Alexandre Fayolle <alexandre.fayolle@camptocamp.com>
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||||
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
||||||
<head>
|
<head>
|
||||||
@@ -366,7 +367,7 @@ ul.auto-toc {
|
|||||||
!! This file is generated by oca-gen-addon-readme !!
|
!! This file is generated by oca-gen-addon-readme !!
|
||||||
!! changes will be overwritten. !!
|
!! changes will be overwritten. !!
|
||||||
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||||
!! source digest: sha256:6be0420cb90de94af20fe0c8f65391d1b24738d2b1135fde9200b5f6702c5b22
|
!! source digest: sha256:ec517bd88092825314f02e061fee665bbf6c106491161601a02197e6d9beff36
|
||||||
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->
|
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->
|
||||||
<p><a class="reference external image-reference" href="https://odoo-community.org/page/development-status"><img alt="Production/Stable" src="https://img.shields.io/badge/maturity-Production%2FStable-green.png" /></a> <a class="reference external image-reference" href="http://www.gnu.org/licenses/lgpl-3.0-standalone.html"><img alt="License: LGPL-3" src="https://img.shields.io/badge/licence-LGPL--3-blue.png" /></a> <a class="reference external image-reference" href="https://github.com/OCA/manufacture/tree/14.0/mrp_multi_level"><img alt="OCA/manufacture" src="https://img.shields.io/badge/github-OCA%2Fmanufacture-lightgray.png?logo=github" /></a> <a class="reference external image-reference" href="https://translation.odoo-community.org/projects/manufacture-14-0/manufacture-14-0-mrp_multi_level"><img alt="Translate me on Weblate" src="https://img.shields.io/badge/weblate-Translate%20me-F47D42.png" /></a> <a class="reference external image-reference" href="https://runboat.odoo-community.org/builds?repo=OCA/manufacture&target_branch=14.0"><img alt="Try me on Runboat" src="https://img.shields.io/badge/runboat-Try%20me-875A7B.png" /></a></p>
|
<p><a class="reference external image-reference" href="https://odoo-community.org/page/development-status"><img alt="Production/Stable" src="https://img.shields.io/badge/maturity-Production%2FStable-green.png" /></a> <a class="reference external image-reference" href="http://www.gnu.org/licenses/lgpl-3.0-standalone.html"><img alt="License: LGPL-3" src="https://img.shields.io/badge/licence-LGPL--3-blue.png" /></a> <a class="reference external image-reference" href="https://github.com/OCA/manufacture/tree/14.0/mrp_multi_level"><img alt="OCA/manufacture" src="https://img.shields.io/badge/github-OCA%2Fmanufacture-lightgray.png?logo=github" /></a> <a class="reference external image-reference" href="https://translation.odoo-community.org/projects/manufacture-14-0/manufacture-14-0-mrp_multi_level"><img alt="Translate me on Weblate" src="https://img.shields.io/badge/weblate-Translate%20me-F47D42.png" /></a> <a class="reference external image-reference" href="https://runboat.odoo-community.org/builds?repo=OCA/manufacture&target_branch=14.0"><img alt="Try me on Runboat" src="https://img.shields.io/badge/runboat-Try%20me-875A7B.png" /></a></p>
|
||||||
<p>This module allows you to calculate, based in known inventory, demand, and
|
<p>This module allows you to calculate, based in known inventory, demand, and
|
||||||
@@ -617,6 +618,8 @@ If you spotted it first, help us to smash it by providing a detailed and welcome
|
|||||||
<li>Jordi Ballester <<a class="reference external" href="mailto:jordi.ballester@forgeflow.com">jordi.ballester@forgeflow.com</a>></li>
|
<li>Jordi Ballester <<a class="reference external" href="mailto:jordi.ballester@forgeflow.com">jordi.ballester@forgeflow.com</a>></li>
|
||||||
<li>Lois Rilo <<a class="reference external" href="mailto:lois.rilo@forgeflow.com">lois.rilo@forgeflow.com</a>></li>
|
<li>Lois Rilo <<a class="reference external" href="mailto:lois.rilo@forgeflow.com">lois.rilo@forgeflow.com</a>></li>
|
||||||
<li>Héctor Villarreal <<a class="reference external" href="mailto:hector.villarreal@forgeflow.com">hector.villarreal@forgeflow.com</a>></li>
|
<li>Héctor Villarreal <<a class="reference external" href="mailto:hector.villarreal@forgeflow.com">hector.villarreal@forgeflow.com</a>></li>
|
||||||
|
<li>Christopher Ormaza <<a class="reference external" href="mailto:chris.ormaza@forgeflow.com">chris.ormaza@forgeflow.com</a>></li>
|
||||||
|
<li>Alexandre Fayolle <<a class="reference external" href="mailto:alexandre.fayolle@camptocamp.com">alexandre.fayolle@camptocamp.com</a>></li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<div class="section" id="maintainers">
|
<div class="section" id="maintainers">
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ class TestMrpMultiLevelCommon(SavepointCase):
|
|||||||
cls.po_obj = cls.env["purchase.order"]
|
cls.po_obj = cls.env["purchase.order"]
|
||||||
cls.product_obj = cls.env["product.product"]
|
cls.product_obj = cls.env["product.product"]
|
||||||
cls.loc_obj = cls.env["stock.location"]
|
cls.loc_obj = cls.env["stock.location"]
|
||||||
|
cls.quant_obj = cls.env["stock.quant"]
|
||||||
cls.mrp_area_obj = cls.env["mrp.area"]
|
cls.mrp_area_obj = cls.env["mrp.area"]
|
||||||
cls.product_mrp_area_obj = cls.env["product.mrp.area"]
|
cls.product_mrp_area_obj = cls.env["product.mrp.area"]
|
||||||
cls.partner_obj = cls.env["res.partner"]
|
cls.partner_obj = cls.env["res.partner"]
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
# Copyright 2018-19 ForgeFlow S.L. (https://www.forgeflow.com)
|
# Copyright 2018-19 ForgeFlow S.L. (https://www.forgeflow.com)
|
||||||
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html).
|
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html).
|
||||||
|
|
||||||
from datetime import date, datetime
|
from datetime import date, datetime, timedelta
|
||||||
|
|
||||||
from odoo import fields
|
from odoo import fields
|
||||||
|
|
||||||
@@ -447,3 +447,79 @@ class TestMrpMultiLevel(TestMrpMultiLevelCommon):
|
|||||||
self.fp_4.route_ids = [(4, self.env.ref("mrp.route_warehouse0_manufacture").id)]
|
self.fp_4.route_ids = [(4, self.env.ref("mrp.route_warehouse0_manufacture").id)]
|
||||||
product_mrp_area._compute_supply_method()
|
product_mrp_area._compute_supply_method()
|
||||||
self.assertEqual(product_mrp_area.supply_method, "manufacture")
|
self.assertEqual(product_mrp_area.supply_method, "manufacture")
|
||||||
|
|
||||||
|
def test_18_priorize_safety_stock(self):
|
||||||
|
now = datetime.now()
|
||||||
|
product = self.prod_test # has Buy route
|
||||||
|
product.seller_ids[0].delay = 2 # set a purchase lead time
|
||||||
|
self.quant_obj._update_available_quantity(product, self.cases_loc, 5)
|
||||||
|
self.product_mrp_area_obj.create(
|
||||||
|
{
|
||||||
|
"product_id": product.id,
|
||||||
|
"mrp_area_id": self.cases_area.id,
|
||||||
|
"mrp_minimum_stock": 15,
|
||||||
|
"mrp_applicable": True, # needed?
|
||||||
|
}
|
||||||
|
)
|
||||||
|
self._create_picking_out(
|
||||||
|
product, 6.0, now + timedelta(days=3), location=self.cases_loc
|
||||||
|
)
|
||||||
|
self._create_picking_in(
|
||||||
|
product, 10.0, now + timedelta(days=7), location=self.cases_loc
|
||||||
|
)
|
||||||
|
self._create_picking_out(
|
||||||
|
product, 12.0, now + timedelta(days=14), location=self.cases_loc
|
||||||
|
)
|
||||||
|
self.mrp_multi_level_wiz.create(
|
||||||
|
{"mrp_area_ids": [(6, 0, self.cases_area.ids)]}
|
||||||
|
).run_mrp_multi_level()
|
||||||
|
inventory = self.mrp_inventory_obj.search(
|
||||||
|
[("mrp_area_id", "=", self.cases_area.id), ("product_id", "=", product.id)]
|
||||||
|
)
|
||||||
|
expected = [
|
||||||
|
{
|
||||||
|
"date": now.date(),
|
||||||
|
"demand_qty": 0.0,
|
||||||
|
"final_on_hand_qty": 5.0,
|
||||||
|
"initial_on_hand_qty": 5.0,
|
||||||
|
"running_availability": 15.0,
|
||||||
|
"supply_qty": 0.0,
|
||||||
|
"to_procure": 10.0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"date": now.date() + timedelta(days=3),
|
||||||
|
"demand_qty": 6.0,
|
||||||
|
"final_on_hand_qty": -1.0,
|
||||||
|
"initial_on_hand_qty": 5.0,
|
||||||
|
"running_availability": 15.0,
|
||||||
|
"supply_qty": 0.0,
|
||||||
|
"to_procure": 6.0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"date": now.date() + timedelta(days=7),
|
||||||
|
"demand_qty": 0.0,
|
||||||
|
"final_on_hand_qty": 9.0,
|
||||||
|
"initial_on_hand_qty": -1.0,
|
||||||
|
"running_availability": 25.0,
|
||||||
|
"supply_qty": 10.0,
|
||||||
|
"to_procure": 0.0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"date": now.date() + timedelta(days=14),
|
||||||
|
"demand_qty": 12.0,
|
||||||
|
"final_on_hand_qty": -3.0,
|
||||||
|
"initial_on_hand_qty": 9.0,
|
||||||
|
"running_availability": 15.0,
|
||||||
|
"supply_qty": 0.0,
|
||||||
|
"to_procure": 2.0,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
self.assertEqual(len(expected), len(inventory))
|
||||||
|
for test_vals, inv in zip(expected, inventory):
|
||||||
|
for key in test_vals:
|
||||||
|
self.assertEqual(
|
||||||
|
test_vals[key],
|
||||||
|
inv[key],
|
||||||
|
f"unexpected value for {key}: {inv[key]} "
|
||||||
|
f"(expected {test_vals[key]} on {inv.date}",
|
||||||
|
)
|
||||||
|
|||||||
@@ -538,6 +538,16 @@ class MultiLevelMrp(models.TransientModel):
|
|||||||
self._init_mrp_move(product_mrp_area)
|
self._init_mrp_move(product_mrp_area)
|
||||||
logger.info("End MRP initialisation")
|
logger.info("End MRP initialisation")
|
||||||
|
|
||||||
|
def _get_qty_to_order(self, product_mrp_area, date, move_qty, onhand):
|
||||||
|
"""Compute the qty to order at a given date, for a product MRP area, given an
|
||||||
|
mrp.move quantity and an onhand quantity.
|
||||||
|
|
||||||
|
This method is an extension point, allowing a new module to change the way this
|
||||||
|
quantity should be computed.
|
||||||
|
"""
|
||||||
|
# The default rule is to resupply to rebuild the safety stock
|
||||||
|
return product_mrp_area.mrp_minimum_stock - onhand - move_qty
|
||||||
|
|
||||||
@api.model
|
@api.model
|
||||||
def _init_mrp_move_grouped_demand(self, nbr_create, product_mrp_area):
|
def _init_mrp_move_grouped_demand(self, nbr_create, product_mrp_area):
|
||||||
last_date = None
|
last_date = None
|
||||||
@@ -567,7 +577,9 @@ class MultiLevelMrp(models.TransientModel):
|
|||||||
delta_days=grouping_delta,
|
delta_days=grouping_delta,
|
||||||
)
|
)
|
||||||
origin = ",".join(list({x for x in demand_origin if x}))
|
origin = ",".join(list({x for x in demand_origin if x}))
|
||||||
qtytoorder = product_mrp_area.mrp_minimum_stock - onhand - last_qty
|
qtytoorder = self._get_qty_to_order(
|
||||||
|
product_mrp_area, last_date, last_qty, onhand
|
||||||
|
)
|
||||||
cm = self.create_action(
|
cm = self.create_action(
|
||||||
product_mrp_area_id=product_mrp_area,
|
product_mrp_area_id=product_mrp_area,
|
||||||
mrp_date=last_date,
|
mrp_date=last_date,
|
||||||
@@ -605,7 +617,9 @@ class MultiLevelMrp(models.TransientModel):
|
|||||||
delta_days=grouping_delta,
|
delta_days=grouping_delta,
|
||||||
)
|
)
|
||||||
origin = ",".join(list({x for x in demand_origin if x}))
|
origin = ",".join(list({x for x in demand_origin if x}))
|
||||||
qtytoorder = product_mrp_area.mrp_minimum_stock - onhand - last_qty
|
qtytoorder = self._get_qty_to_order(
|
||||||
|
product_mrp_area, last_date, last_qty, onhand
|
||||||
|
)
|
||||||
cm = self.create_action(
|
cm = self.create_action(
|
||||||
product_mrp_area_id=product_mrp_area,
|
product_mrp_area_id=product_mrp_area,
|
||||||
mrp_date=last_date,
|
mrp_date=last_date,
|
||||||
@@ -616,6 +630,88 @@ class MultiLevelMrp(models.TransientModel):
|
|||||||
qty_ordered = cm.get("qty_ordered", 0.0)
|
qty_ordered = cm.get("qty_ordered", 0.0)
|
||||||
onhand += qty_ordered
|
onhand += qty_ordered
|
||||||
nbr_create += 1
|
nbr_create += 1
|
||||||
|
|
||||||
|
if onhand < product_mrp_area.mrp_minimum_stock:
|
||||||
|
mrp_date = self._get_safety_stock_target_date(product_mrp_area)
|
||||||
|
qtytoorder = self._get_qty_to_order(product_mrp_area, mrp_date, 0, onhand)
|
||||||
|
name = _("Safety Stock")
|
||||||
|
cm = self.create_action(
|
||||||
|
product_mrp_area_id=product_mrp_area,
|
||||||
|
mrp_date=mrp_date,
|
||||||
|
mrp_qty=qtytoorder,
|
||||||
|
name=name,
|
||||||
|
values=dict(origin=name),
|
||||||
|
)
|
||||||
|
qty_ordered = cm["qty_ordered"]
|
||||||
|
onhand += qty_ordered
|
||||||
|
nbr_create += 1
|
||||||
|
|
||||||
|
return nbr_create
|
||||||
|
|
||||||
|
def _get_safety_stock_target_date(self, product_mrp_area):
|
||||||
|
"""Get the date at which the safety stock rebuild should be targeted
|
||||||
|
|
||||||
|
This method is an extension point for modules who need to cusomize that date."""
|
||||||
|
return date.today()
|
||||||
|
|
||||||
|
@api.model
|
||||||
|
def _init_mrp_move_non_grouped_demand(self, nbr_create, product_mrp_area):
|
||||||
|
onhand = product_mrp_area.qty_available
|
||||||
|
for move in product_mrp_area.mrp_move_ids:
|
||||||
|
if self._exclude_move(move):
|
||||||
|
continue
|
||||||
|
if onhand < product_mrp_area.mrp_minimum_stock:
|
||||||
|
qtytoorder = self._get_qty_to_order(
|
||||||
|
product_mrp_area,
|
||||||
|
self._get_safety_stock_target_date(product_mrp_area),
|
||||||
|
0,
|
||||||
|
onhand,
|
||||||
|
)
|
||||||
|
name = _("Safety Stock")
|
||||||
|
cm = self.create_action(
|
||||||
|
product_mrp_area_id=product_mrp_area,
|
||||||
|
mrp_date=self._get_safety_stock_target_date(product_mrp_area),
|
||||||
|
mrp_qty=qtytoorder,
|
||||||
|
name=name,
|
||||||
|
values=dict(origin=name),
|
||||||
|
)
|
||||||
|
qty_ordered = cm["qty_ordered"]
|
||||||
|
onhand += qty_ordered
|
||||||
|
nbr_create += 1
|
||||||
|
|
||||||
|
qtytoorder = self._get_qty_to_order(
|
||||||
|
product_mrp_area, move.mrp_date, move.mrp_qty, onhand
|
||||||
|
)
|
||||||
|
if qtytoorder > 0.0:
|
||||||
|
cm = self.create_action(
|
||||||
|
product_mrp_area_id=product_mrp_area,
|
||||||
|
mrp_date=move.mrp_date,
|
||||||
|
mrp_qty=qtytoorder,
|
||||||
|
name=move.name or "",
|
||||||
|
values=dict(origin=move.origin or ""),
|
||||||
|
)
|
||||||
|
qty_ordered = cm["qty_ordered"]
|
||||||
|
onhand += move.mrp_qty + qty_ordered
|
||||||
|
nbr_create += 1
|
||||||
|
else:
|
||||||
|
onhand += move.mrp_qty
|
||||||
|
if onhand < product_mrp_area.mrp_minimum_stock:
|
||||||
|
mrp_date = self._get_safety_stock_target_date(product_mrp_area)
|
||||||
|
qtytoorder = self._get_qty_to_order(
|
||||||
|
product_mrp_area, move.mrp_date, 0, onhand
|
||||||
|
)
|
||||||
|
name = _("Safety Stock")
|
||||||
|
cm = self.create_action(
|
||||||
|
product_mrp_area_id=product_mrp_area,
|
||||||
|
mrp_date=mrp_date,
|
||||||
|
mrp_qty=qtytoorder,
|
||||||
|
name=name,
|
||||||
|
values=dict(origin=name),
|
||||||
|
)
|
||||||
|
qty_ordered = cm["qty_ordered"]
|
||||||
|
onhand += qty_ordered
|
||||||
|
nbr_create += 1
|
||||||
|
|
||||||
return nbr_create
|
return nbr_create
|
||||||
|
|
||||||
@api.model
|
@api.model
|
||||||
@@ -641,39 +737,25 @@ class MultiLevelMrp(models.TransientModel):
|
|||||||
for product_mrp_area in product_mrp_areas:
|
for product_mrp_area in product_mrp_areas:
|
||||||
nbr_create = 0
|
nbr_create = 0
|
||||||
onhand = product_mrp_area.qty_available
|
onhand = product_mrp_area.qty_available
|
||||||
|
|
||||||
if product_mrp_area.mrp_nbr_days == 0:
|
if product_mrp_area.mrp_nbr_days == 0:
|
||||||
for move in product_mrp_area.mrp_move_ids:
|
nbr_create = self._init_mrp_move_non_grouped_demand(
|
||||||
if self._exclude_move(move):
|
nbr_create, product_mrp_area
|
||||||
continue
|
)
|
||||||
qtytoorder = (
|
|
||||||
product_mrp_area.mrp_minimum_stock
|
|
||||||
- onhand
|
|
||||||
- move.mrp_qty
|
|
||||||
)
|
|
||||||
if qtytoorder > 0.0:
|
|
||||||
cm = self.create_action(
|
|
||||||
product_mrp_area_id=product_mrp_area,
|
|
||||||
mrp_date=move.mrp_date,
|
|
||||||
mrp_qty=qtytoorder,
|
|
||||||
name=move.name or "",
|
|
||||||
values=dict(origin=move.origin or ""),
|
|
||||||
)
|
|
||||||
qty_ordered = cm["qty_ordered"]
|
|
||||||
onhand += move.mrp_qty + qty_ordered
|
|
||||||
nbr_create += 1
|
|
||||||
else:
|
|
||||||
onhand += move.mrp_qty
|
|
||||||
else:
|
else:
|
||||||
nbr_create = self._init_mrp_move_grouped_demand(
|
nbr_create = self._init_mrp_move_grouped_demand(
|
||||||
nbr_create, product_mrp_area
|
nbr_create, product_mrp_area
|
||||||
)
|
)
|
||||||
|
|
||||||
if onhand < product_mrp_area.mrp_minimum_stock and nbr_create == 0:
|
if onhand < product_mrp_area.mrp_minimum_stock and nbr_create == 0:
|
||||||
qtytoorder = product_mrp_area.mrp_minimum_stock - onhand
|
mrp_date = date.today()
|
||||||
|
qtytoorder = self._get_qty_to_order(
|
||||||
|
product_mrp_area, mrp_date, 0, onhand
|
||||||
|
)
|
||||||
name = _("Safety Stock")
|
name = _("Safety Stock")
|
||||||
cm = self.create_action(
|
cm = self.create_action(
|
||||||
product_mrp_area_id=product_mrp_area,
|
product_mrp_area_id=product_mrp_area,
|
||||||
mrp_date=date.today(),
|
mrp_date=mrp_date,
|
||||||
mrp_qty=qtytoorder,
|
mrp_qty=qtytoorder,
|
||||||
name=name,
|
name=name,
|
||||||
values=dict(origin=name),
|
values=dict(origin=name),
|
||||||
|
|||||||
Reference in New Issue
Block a user