Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 11 additions & 5 deletions mrp_multi_level/wizards/mrp_multi_level.py
Copy link
Contributor

@LoisRForgeFlow LoisRForgeFlow Jan 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Camille0907 This is outside of the migration, can you add a test case for this?

Copy link

@vvrossem vvrossem Jan 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From the commit message, a test case already covers this, but was previously failing (TBC)

Original file line number Diff line number Diff line change
Expand Up @@ -670,19 +670,25 @@ def _init_mrp_move_non_grouped_demand(self, product_mrp_area):
for move in product_mrp_area.mrp_move_ids:
if self._exclude_move(move):
continue
# This works because mrp moves are ordered by:
# product_mrp_area_id, mrp_date, mrp_type desc, id
if onhand + move.mrp_qty < product_mrp_area.mrp_minimum_stock:
safety_stock_target_date = self._get_safety_stock_target_date(
product_mrp_area
)
onhand_before_safety_stock_date = onhand
if move.mrp_qty < 0 or move.mrp_date <= safety_stock_target_date:
# This works because mrp moves are ordered by:
# product_mrp_area_id, mrp_date, mrp_type desc, id
onhand_before_safety_stock_date += move.mrp_qty
if onhand_before_safety_stock_date < product_mrp_area.mrp_minimum_stock:
qtytoorder = self._get_qty_to_order(
product_mrp_area,
self._get_safety_stock_target_date(product_mrp_area),
safety_stock_target_date,
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_date=safety_stock_target_date,
mrp_qty=qtytoorder,
name=name,
values=dict(origin=name),
Expand Down
103 changes: 103 additions & 0 deletions mrp_multi_level_consume_safety_stock/README.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
====================================
MRP Multi Level Consume Safety Stock
====================================

..
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! source digest: sha256:ad2f169fa6d149c34adaf973e45bd63b040820bcf4909aaf906ed0be7b0cb8c1
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

.. |badge1| image:: https://img.shields.io/badge/maturity-Production%2FStable-green.png
:target: https://odoo-community.org/page/development-status
:alt: Production/Stable
.. |badge2| image:: https://img.shields.io/badge/licence-LGPL--3-blue.png
:target: http://www.gnu.org/licenses/lgpl-3.0-standalone.html
:alt: License: LGPL-3
.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fmanufacture-lightgray.png?logo=github
:target: https://github.com/OCA/manufacture/tree/18.0/mrp_multi_level_consume_safety_stock
:alt: OCA/manufacture
.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png
:target: https://translation.odoo-community.org/projects/manufacture-18-0/manufacture-18-0-mrp_multi_level_consume_safety_stock
:alt: Translate me on Weblate
.. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png
:target: https://runboat.odoo-community.org/builds?repo=OCA/manufacture&target_branch=18.0
:alt: Try me on Runboat

|badge1| |badge2| |badge3| |badge4| |badge5|

This module complements the mrp_multi_level module by allowing to set a
date on the mrp.area records until which no attempt to rebuild safety
stock will be attempted: the safety stock will be used, if the running
stock get below zero, then a resupply will be created to bring back the
stock to zero.

The idea is that your area may be under tension at a given moment (maybe
some workers are off, maybe there is high demand from customers) and you
can barely keep up with the demand. In this case, you can set Safety
stock rebuild lead date to a date in the future at which you anticipate
that the situation will have been fixed.

**Table of contents**

.. contents::
:local:

Usage
=====

When this module is installed, you get an additional parameter on the
mrp.area records:

- Safety stock lead date

Until that date, the MRP Multi Level planner will consume the safety
stock without attempting to rebuild it, and only resupply if the
forecasted stock goes below zero.

If the date is in the paste, nothing special happens.

Bug Tracker
===========

Bugs are tracked on `GitHub Issues <https://github.com/OCA/manufacture/issues>`_.
In case of trouble, please check there if your issue has already been reported.
If you spotted it first, help us to smash it by providing a detailed and welcomed
`feedback <https://github.com/OCA/manufacture/issues/new?body=module:%20mrp_multi_level_consume_safety_stock%0Aversion:%2018.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.

Do not contact contributors directly about support or help with technical issues.

Credits
=======

Authors
-------

* Camptocamp

Maintainers
-----------

This module is maintained by the OCA.

.. image:: https://odoo-community.org/logo.png
:alt: Odoo Community Association
:target: https://odoo-community.org

OCA, or the Odoo Community Association, is a nonprofit organization whose
mission is to support the collaborative development of Odoo features and
promote its widespread use.

.. |maintainer-gurneyalex| image:: https://github.com/gurneyalex.png?size=40px
:target: https://github.com/gurneyalex
:alt: gurneyalex

Current `maintainer <https://odoo-community.org/page/maintainer-role>`__:

|maintainer-gurneyalex|

This module is part of the `OCA/manufacture <https://github.com/OCA/manufacture/tree/18.0/mrp_multi_level_consume_safety_stock>`_ project on GitHub.

You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.
2 changes: 2 additions & 0 deletions mrp_multi_level_consume_safety_stock/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
from . import models
from . import wizards
18 changes: 18 additions & 0 deletions mrp_multi_level_consume_safety_stock/__manifest__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Copyright 2023 Camptocamp (https://www.camptocamp.com)
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html).

{
"name": "MRP Multi Level Consume Safety Stock",
"version": "18.0.1.0.0",
"development_status": "Production/Stable",
"license": "LGPL-3",
"author": "Camptocamp, Odoo Community Association (OCA)",
"maintainers": ["gurneyalex"],
"summary": "MRP scheduler: use safety stock during stress periods",
"website": "https://github.com/OCA/manufacture",
"category": "Manufacturing",
"depends": ["mrp_multi_level"],
"data": ["views/mrp_area_views.xml"],
"installable": True,
"application": True,
}
1 change: 1 addition & 0 deletions mrp_multi_level_consume_safety_stock/models/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from . import mrp_area, product_mrp_area
14 changes: 14 additions & 0 deletions mrp_multi_level_consume_safety_stock/models/mrp_area.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Copyright 2023 Camptocamp (https://www.camptocamp.com)
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html).

from odoo import fields, models


class MrpArea(models.Model):
_inherit = "mrp.area"

safety_stock_target_date = fields.Date(
string="Safety stock lead date",
help="We will start rebuilding safety stock on that date",
default=fields.Date.today,
)
26 changes: 26 additions & 0 deletions mrp_multi_level_consume_safety_stock/models/product_mrp_area.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Copyright 2023 Camptocamp (https://www.camptocamp.com)
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html).
from dateutil.relativedelta import relativedelta

from odoo import api, fields, models


class ProductMrpArea(models.Model):
_inherit = "product.mrp.area"

safety_stock_target_date = fields.Date(
string="Safety stock lead date",
compute="_compute_safety_stock_target_date",
help="The date when we can restart supplying for this product",
)

@api.depends(
"mrp_lead_time",
"mrp_area_id.safety_stock_target_date",
)
def _compute_safety_stock_target_date(self):
today = fields.Date.context_today(self)
for rec in self:
delta = relativedelta(days=rec.mrp_lead_time)
area_target_date = rec.mrp_area_id.safety_stock_target_date or today
rec.safety_stock_target_date = max(today, area_target_date + delta)
3 changes: 3 additions & 0 deletions mrp_multi_level_consume_safety_stock/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[build-system]
requires = ["whool"]
build-backend = "whool.buildapi"
11 changes: 11 additions & 0 deletions mrp_multi_level_consume_safety_stock/readme/DESCRIPTION.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
This module complements the mrp_multi_level module by allowing to set a
date on the mrp.area records until which no attempt to rebuild safety
stock will be attempted: the safety stock will be used, if the running
stock get below zero, then a resupply will be created to bring back the
stock to zero.

The idea is that your area may be under tension at a given moment (maybe
some workers are off, maybe there is high demand from customers) and you
can barely keep up with the demand. In this case, you can set Safety
stock rebuild lead date to a date in the future at which you anticipate
that the situation will have been fixed.
10 changes: 10 additions & 0 deletions mrp_multi_level_consume_safety_stock/readme/USAGE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
When this module is installed, you get an additional parameter on the
mrp.area records:

- Safety stock lead date

Until that date, the MRP Multi Level planner will consume the safety
stock without attempting to rebuild it, and only resupply if the
forecasted stock goes below zero.

If the date is in the paste, nothing special happens.
Loading