mirror of
https://github.com/OCA/manufacture.git
synced 2025-01-28 16:37:15 +02:00
@@ -1,259 +0,0 @@
|
||||
===============
|
||||
MRP Multi Level
|
||||
===============
|
||||
|
||||
..
|
||||
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
!! This file is generated by oca-gen-addon-readme !!
|
||||
!! changes will be overwritten. !!
|
||||
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
!! source digest: sha256:ade6783d411926bfe80610b33ea8aff7a931f7b8c3336b0b44c9d78eea162757
|
||||
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
|
||||
.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png
|
||||
:target: https://odoo-community.org/page/development-status
|
||||
:alt: Beta
|
||||
.. |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/13.0/mrp_multi_level
|
||||
:alt: OCA/manufacture
|
||||
.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png
|
||||
:target: https://translation.odoo-community.org/projects/manufacture-13-0/manufacture-13-0-mrp_multi_level
|
||||
: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=13.0
|
||||
:alt: Try me on Runboat
|
||||
|
||||
|badge1| |badge2| |badge3| |badge4| |badge5|
|
||||
|
||||
This module allows you to calculate, based in known inventory, demand, and
|
||||
supply, and based on parameters set at product variant level, the new
|
||||
procurements for each product.
|
||||
|
||||
To do this, the calculation starts at top level of the bill of material
|
||||
and explodes this down to the lowest level.
|
||||
|
||||
Key Features
|
||||
------------
|
||||
|
||||
* MRP parameters set by product variant MRP area pairs.
|
||||
* Cron job to calculate the MRP demand.
|
||||
* Manually calculate the MRP demand.
|
||||
* Confirm the calculated MRP demand and create PO's, or MO's.
|
||||
* Able to see the products for which action is needed throught Planned Orders.
|
||||
* Integration with `Stock Demand Estimates <https://github.com/OCA/stock-logistics-warehouse/tree/12.0/stock_demand_estimate>`_ system.
|
||||
Note: You need to install `mrp_multi_level_estimate module <https://github.com/OCA/manufacture/tree/12.0/mrp_multi_level_estimate>`_.
|
||||
|
||||
**Table of contents**
|
||||
|
||||
.. contents::
|
||||
:local:
|
||||
|
||||
Configuration
|
||||
=============
|
||||
|
||||
MRP Areas
|
||||
~~~~~~~~~
|
||||
|
||||
* Go to *Manufacturing > Configuration > MRP Areas* and define or edit
|
||||
any existing area. You can specify the working hours for every area.
|
||||
|
||||
Product MRP Area Parameters
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
* Go to *Manufacturing > Master Data > Product MRP Area Parameters* and set
|
||||
the MRP parameters for a given product and area.
|
||||
|
||||
Usage
|
||||
=====
|
||||
|
||||
To manually run the MRP scheduler:
|
||||
|
||||
#. Go to *Manufacturing > Operations > Run MRP Multi Level*.
|
||||
#. On the wizard click *Run MRP*.
|
||||
|
||||
To launch replenishment orders (moves, purchases, production orders...):
|
||||
|
||||
#. Go to *Manufacturing > Operations > MRP Inventory*.
|
||||
#. Filter with *To procure*.
|
||||
#. Select multiple records and click on *Action > Procure* or click the right
|
||||
hand side gears in any record.
|
||||
#. On the wizard, check everything is ok and click *Execute*.
|
||||
|
||||
Changelog
|
||||
=========
|
||||
|
||||
13.0.1.5.0 (2020-04-09)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
**Features**
|
||||
|
||||
- Show *Run MRP Multi Level* menu only to a specific new security group *Run MRP Manually*. (`#492 <https://github.com/OCA/manufacture/issues/492>`_)
|
||||
|
||||
|
||||
13.0.1.4.0 (2020-03-26)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
* Add menu entry for planned orders
|
||||
* Add button to navigate from planned orders to linked manufacturing orders
|
||||
* Add action to convert planned orders to fixed
|
||||
* When changing the due date in a planned order the release date is recomputed
|
||||
|
||||
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)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
* [IMP] Minor changes
|
||||
(`#468 <https://github.com/OCA/manufacture/pull/468>`_).
|
||||
|
||||
* Planned Orders become fixed on manual creation by default
|
||||
* Released Quantity becomes readonly
|
||||
* Add product reference if Planned Order name is not defined on bom explosion
|
||||
|
||||
13.0.1.1.0 (2020-02-21)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
* [FIX] Minor changes
|
||||
(`#469 <https://github.com/OCA/manufacture/pull/469>`_).
|
||||
|
||||
* Fix Main supplier computation in multi company
|
||||
* Drop Triplicated field in search view
|
||||
|
||||
|
||||
* [IMP] Minor changes
|
||||
(`#463 <https://github.com/OCA/manufacture/pull/463>`_).
|
||||
|
||||
* Show supply method on MRP Inventory
|
||||
* Allow no-MRP users to look into Products
|
||||
|
||||
13.0.1.0.0 (2019-12-18)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
* [MIG] Migration to v13.
|
||||
|
||||
12.0.1.0.0 (2019-08-05)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
* [MIG] Migration to v12:
|
||||
|
||||
* Estimates as a forecasting mechanism is moved to a new module
|
||||
(mrp_multi_level_estimate).
|
||||
|
||||
11.0.3.0.0 (2019-05-22)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
* [REW/IMP] Rework to include Planned Orders.
|
||||
(`#365 <https://github.com/OCA/manufacture/pull/365>`_).
|
||||
* [IMP] Able to procure from a different location than the area's location.
|
||||
|
||||
11.0.2.2.0 (2019-05-02)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
* [IMP] Able to run MRP only for selected areas.
|
||||
(`#360 <https://github.com/OCA/manufacture/pull/360>`_).
|
||||
|
||||
11.0.2.1.0 (2019-04-02)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
* [IMP] Implement *Nbr. Days* functionality to be able to group demand when
|
||||
generating supply proposals.
|
||||
(`#345 <https://github.com/OCA/manufacture/pull/345>`_).
|
||||
|
||||
11.0.2.0.0 (2018-11-20)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
* [REW] Refactor MRP Area.
|
||||
(`#322 <https://github.com/OCA/manufacture/pull/322>`_):
|
||||
|
||||
* MRP product concept dropped in favor of *Product MRP Area Parameters*.
|
||||
This allow to set different MRP parameters for the same product in
|
||||
different areas.
|
||||
* Menu items reordering.
|
||||
|
||||
11.0.1.1.0 (2018-08-30)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
* [FIX] Consider *Qty Multiple* on product to propose the quantity to procure.
|
||||
(`#297 <https://github.com/OCA/manufacture/pull/297>`_)
|
||||
|
||||
11.0.1.0.1 (2018-08-03)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
* [FIX] User and system locales doesn't break MRP calculation.
|
||||
(`#290 <https://github.com/OCA/manufacture/pull/290>`_)
|
||||
* [FIX] Working Hours are now defined only at Warehouse level and displayed
|
||||
as a related on MRP Areas.
|
||||
(`#290 <https://github.com/OCA/manufacture/pull/290>`__)
|
||||
|
||||
11.0.1.0.0 (2018-07-09)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
* Start of the history.
|
||||
|
||||
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%0Aversion:%2013.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
|
||||
~~~~~~~
|
||||
|
||||
* Ucamco
|
||||
* ForgeFlow
|
||||
|
||||
Contributors
|
||||
~~~~~~~~~~~~
|
||||
|
||||
* Wim Audenaert <wim.audenaert@ucamco.com>
|
||||
* Jordi Ballester <jordi.ballester@forgeflow.com>
|
||||
* Lois Rilo <lois.rilo@forgeflow.com>
|
||||
* Héctor Villarreal <hector.villarreal@forgeflow.com>
|
||||
|
||||
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-JordiBForgeFlow| image:: https://github.com/JordiBForgeFlow.png?size=40px
|
||||
:target: https://github.com/JordiBForgeFlow
|
||||
:alt: JordiBForgeFlow
|
||||
.. |maintainer-LoisRForgeFlow| image:: https://github.com/LoisRForgeFlow.png?size=40px
|
||||
:target: https://github.com/LoisRForgeFlow
|
||||
:alt: LoisRForgeFlow
|
||||
|
||||
Current `maintainers <https://odoo-community.org/page/maintainer-role>`__:
|
||||
|
||||
|maintainer-JordiBForgeFlow| |maintainer-LoisRForgeFlow|
|
||||
|
||||
This module is part of the `OCA/manufacture <https://github.com/OCA/manufacture/tree/13.0/mrp_multi_level>`_ project on GitHub.
|
||||
|
||||
You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.
|
||||
@@ -3,7 +3,6 @@
|
||||
# - 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 math import ceil
|
||||
|
||||
from odoo import _, api, fields, models
|
||||
|
||||
@@ -4,6 +4,8 @@ MRP Areas
|
||||
* Go to *Manufacturing > Configuration > MRP Areas* and define or edit
|
||||
any existing area. You can specify the working hours for every area.
|
||||
|
||||
|
||||
|
||||
Product MRP Area Parameters
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
|
||||
@@ -2,3 +2,5 @@
|
||||
* Jordi Ballester <jordi.ballester@forgeflow.com>
|
||||
* Lois Rilo <lois.rilo@forgeflow.com>
|
||||
* Héctor Villarreal <hector.villarreal@forgeflow.com>
|
||||
* Christopher Ormaza <chris.ormaza@forgeflow.com>
|
||||
* Alexandre Fayolle <alexandre.fayolle@camptocamp.com>
|
||||
|
||||
@@ -1,638 +0,0 @@
|
||||
<!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">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||
<meta name="generator" content="Docutils: https://docutils.sourceforge.io/" />
|
||||
<title>MRP Multi Level</title>
|
||||
<style type="text/css">
|
||||
|
||||
/*
|
||||
:Author: David Goodger (goodger@python.org)
|
||||
:Id: $Id: html4css1.css 8954 2022-01-20 10:10:25Z milde $
|
||||
:Copyright: This stylesheet has been placed in the public domain.
|
||||
|
||||
Default cascading style sheet for the HTML output of Docutils.
|
||||
|
||||
See https://docutils.sourceforge.io/docs/howto/html-stylesheets.html for how to
|
||||
customize this style sheet.
|
||||
*/
|
||||
|
||||
/* used to remove borders from tables and images */
|
||||
.borderless, table.borderless td, table.borderless th {
|
||||
border: 0 }
|
||||
|
||||
table.borderless td, table.borderless th {
|
||||
/* Override padding for "table.docutils td" with "! important".
|
||||
The right padding separates the table cells. */
|
||||
padding: 0 0.5em 0 0 ! important }
|
||||
|
||||
.first {
|
||||
/* Override more specific margin styles with "! important". */
|
||||
margin-top: 0 ! important }
|
||||
|
||||
.last, .with-subtitle {
|
||||
margin-bottom: 0 ! important }
|
||||
|
||||
.hidden {
|
||||
display: none }
|
||||
|
||||
.subscript {
|
||||
vertical-align: sub;
|
||||
font-size: smaller }
|
||||
|
||||
.superscript {
|
||||
vertical-align: super;
|
||||
font-size: smaller }
|
||||
|
||||
a.toc-backref {
|
||||
text-decoration: none ;
|
||||
color: black }
|
||||
|
||||
blockquote.epigraph {
|
||||
margin: 2em 5em ; }
|
||||
|
||||
dl.docutils dd {
|
||||
margin-bottom: 0.5em }
|
||||
|
||||
object[type="image/svg+xml"], object[type="application/x-shockwave-flash"] {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* Uncomment (and remove this text!) to get bold-faced definition list terms
|
||||
dl.docutils dt {
|
||||
font-weight: bold }
|
||||
*/
|
||||
|
||||
div.abstract {
|
||||
margin: 2em 5em }
|
||||
|
||||
div.abstract p.topic-title {
|
||||
font-weight: bold ;
|
||||
text-align: center }
|
||||
|
||||
div.admonition, div.attention, div.caution, div.danger, div.error,
|
||||
div.hint, div.important, div.note, div.tip, div.warning {
|
||||
margin: 2em ;
|
||||
border: medium outset ;
|
||||
padding: 1em }
|
||||
|
||||
div.admonition p.admonition-title, div.hint p.admonition-title,
|
||||
div.important p.admonition-title, div.note p.admonition-title,
|
||||
div.tip p.admonition-title {
|
||||
font-weight: bold ;
|
||||
font-family: sans-serif }
|
||||
|
||||
div.attention p.admonition-title, div.caution p.admonition-title,
|
||||
div.danger p.admonition-title, div.error p.admonition-title,
|
||||
div.warning p.admonition-title, .code .error {
|
||||
color: red ;
|
||||
font-weight: bold ;
|
||||
font-family: sans-serif }
|
||||
|
||||
/* Uncomment (and remove this text!) to get reduced vertical space in
|
||||
compound paragraphs.
|
||||
div.compound .compound-first, div.compound .compound-middle {
|
||||
margin-bottom: 0.5em }
|
||||
|
||||
div.compound .compound-last, div.compound .compound-middle {
|
||||
margin-top: 0.5em }
|
||||
*/
|
||||
|
||||
div.dedication {
|
||||
margin: 2em 5em ;
|
||||
text-align: center ;
|
||||
font-style: italic }
|
||||
|
||||
div.dedication p.topic-title {
|
||||
font-weight: bold ;
|
||||
font-style: normal }
|
||||
|
||||
div.figure {
|
||||
margin-left: 2em ;
|
||||
margin-right: 2em }
|
||||
|
||||
div.footer, div.header {
|
||||
clear: both;
|
||||
font-size: smaller }
|
||||
|
||||
div.line-block {
|
||||
display: block ;
|
||||
margin-top: 1em ;
|
||||
margin-bottom: 1em }
|
||||
|
||||
div.line-block div.line-block {
|
||||
margin-top: 0 ;
|
||||
margin-bottom: 0 ;
|
||||
margin-left: 1.5em }
|
||||
|
||||
div.sidebar {
|
||||
margin: 0 0 0.5em 1em ;
|
||||
border: medium outset ;
|
||||
padding: 1em ;
|
||||
background-color: #ffffee ;
|
||||
width: 40% ;
|
||||
float: right ;
|
||||
clear: right }
|
||||
|
||||
div.sidebar p.rubric {
|
||||
font-family: sans-serif ;
|
||||
font-size: medium }
|
||||
|
||||
div.system-messages {
|
||||
margin: 5em }
|
||||
|
||||
div.system-messages h1 {
|
||||
color: red }
|
||||
|
||||
div.system-message {
|
||||
border: medium outset ;
|
||||
padding: 1em }
|
||||
|
||||
div.system-message p.system-message-title {
|
||||
color: red ;
|
||||
font-weight: bold }
|
||||
|
||||
div.topic {
|
||||
margin: 2em }
|
||||
|
||||
h1.section-subtitle, h2.section-subtitle, h3.section-subtitle,
|
||||
h4.section-subtitle, h5.section-subtitle, h6.section-subtitle {
|
||||
margin-top: 0.4em }
|
||||
|
||||
h1.title {
|
||||
text-align: center }
|
||||
|
||||
h2.subtitle {
|
||||
text-align: center }
|
||||
|
||||
hr.docutils {
|
||||
width: 75% }
|
||||
|
||||
img.align-left, .figure.align-left, object.align-left, table.align-left {
|
||||
clear: left ;
|
||||
float: left ;
|
||||
margin-right: 1em }
|
||||
|
||||
img.align-right, .figure.align-right, object.align-right, table.align-right {
|
||||
clear: right ;
|
||||
float: right ;
|
||||
margin-left: 1em }
|
||||
|
||||
img.align-center, .figure.align-center, object.align-center {
|
||||
display: block;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
table.align-center {
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
.align-left {
|
||||
text-align: left }
|
||||
|
||||
.align-center {
|
||||
clear: both ;
|
||||
text-align: center }
|
||||
|
||||
.align-right {
|
||||
text-align: right }
|
||||
|
||||
/* reset inner alignment in figures */
|
||||
div.align-right {
|
||||
text-align: inherit }
|
||||
|
||||
/* div.align-center * { */
|
||||
/* text-align: left } */
|
||||
|
||||
.align-top {
|
||||
vertical-align: top }
|
||||
|
||||
.align-middle {
|
||||
vertical-align: middle }
|
||||
|
||||
.align-bottom {
|
||||
vertical-align: bottom }
|
||||
|
||||
ol.simple, ul.simple {
|
||||
margin-bottom: 1em }
|
||||
|
||||
ol.arabic {
|
||||
list-style: decimal }
|
||||
|
||||
ol.loweralpha {
|
||||
list-style: lower-alpha }
|
||||
|
||||
ol.upperalpha {
|
||||
list-style: upper-alpha }
|
||||
|
||||
ol.lowerroman {
|
||||
list-style: lower-roman }
|
||||
|
||||
ol.upperroman {
|
||||
list-style: upper-roman }
|
||||
|
||||
p.attribution {
|
||||
text-align: right ;
|
||||
margin-left: 50% }
|
||||
|
||||
p.caption {
|
||||
font-style: italic }
|
||||
|
||||
p.credits {
|
||||
font-style: italic ;
|
||||
font-size: smaller }
|
||||
|
||||
p.label {
|
||||
white-space: nowrap }
|
||||
|
||||
p.rubric {
|
||||
font-weight: bold ;
|
||||
font-size: larger ;
|
||||
color: maroon ;
|
||||
text-align: center }
|
||||
|
||||
p.sidebar-title {
|
||||
font-family: sans-serif ;
|
||||
font-weight: bold ;
|
||||
font-size: larger }
|
||||
|
||||
p.sidebar-subtitle {
|
||||
font-family: sans-serif ;
|
||||
font-weight: bold }
|
||||
|
||||
p.topic-title {
|
||||
font-weight: bold }
|
||||
|
||||
pre.address {
|
||||
margin-bottom: 0 ;
|
||||
margin-top: 0 ;
|
||||
font: inherit }
|
||||
|
||||
pre.literal-block, pre.doctest-block, pre.math, pre.code {
|
||||
margin-left: 2em ;
|
||||
margin-right: 2em }
|
||||
|
||||
pre.code .ln { color: grey; } /* line numbers */
|
||||
pre.code, code { background-color: #eeeeee }
|
||||
pre.code .comment, code .comment { color: #5C6576 }
|
||||
pre.code .keyword, code .keyword { color: #3B0D06; font-weight: bold }
|
||||
pre.code .literal.string, code .literal.string { color: #0C5404 }
|
||||
pre.code .name.builtin, code .name.builtin { color: #352B84 }
|
||||
pre.code .deleted, code .deleted { background-color: #DEB0A1}
|
||||
pre.code .inserted, code .inserted { background-color: #A3D289}
|
||||
|
||||
span.classifier {
|
||||
font-family: sans-serif ;
|
||||
font-style: oblique }
|
||||
|
||||
span.classifier-delimiter {
|
||||
font-family: sans-serif ;
|
||||
font-weight: bold }
|
||||
|
||||
span.interpreted {
|
||||
font-family: sans-serif }
|
||||
|
||||
span.option {
|
||||
white-space: nowrap }
|
||||
|
||||
span.pre {
|
||||
white-space: pre }
|
||||
|
||||
span.problematic {
|
||||
color: red }
|
||||
|
||||
span.section-subtitle {
|
||||
/* font-size relative to parent (h1..h6 element) */
|
||||
font-size: 80% }
|
||||
|
||||
table.citation {
|
||||
border-left: solid 1px gray;
|
||||
margin-left: 1px }
|
||||
|
||||
table.docinfo {
|
||||
margin: 2em 4em }
|
||||
|
||||
table.docutils {
|
||||
margin-top: 0.5em ;
|
||||
margin-bottom: 0.5em }
|
||||
|
||||
table.footnote {
|
||||
border-left: solid 1px black;
|
||||
margin-left: 1px }
|
||||
|
||||
table.docutils td, table.docutils th,
|
||||
table.docinfo td, table.docinfo th {
|
||||
padding-left: 0.5em ;
|
||||
padding-right: 0.5em ;
|
||||
vertical-align: top }
|
||||
|
||||
table.docutils th.field-name, table.docinfo th.docinfo-name {
|
||||
font-weight: bold ;
|
||||
text-align: left ;
|
||||
white-space: nowrap ;
|
||||
padding-left: 0 }
|
||||
|
||||
/* "booktabs" style (no vertical lines) */
|
||||
table.docutils.booktabs {
|
||||
border: 0px;
|
||||
border-top: 2px solid;
|
||||
border-bottom: 2px solid;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
table.docutils.booktabs * {
|
||||
border: 0px;
|
||||
}
|
||||
table.docutils.booktabs th {
|
||||
border-bottom: thin solid;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
h1 tt.docutils, h2 tt.docutils, h3 tt.docutils,
|
||||
h4 tt.docutils, h5 tt.docutils, h6 tt.docutils {
|
||||
font-size: 100% }
|
||||
|
||||
ul.auto-toc {
|
||||
list-style-type: none }
|
||||
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="document" id="mrp-multi-level">
|
||||
<h1 class="title">MRP Multi Level</h1>
|
||||
|
||||
<!-- !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
!! This file is generated by oca-gen-addon-readme !!
|
||||
!! changes will be overwritten. !!
|
||||
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
!! source digest: sha256:ade6783d411926bfe80610b33ea8aff7a931f7b8c3336b0b44c9d78eea162757
|
||||
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->
|
||||
<p><a class="reference external image-reference" href="https://odoo-community.org/page/development-status"><img alt="Beta" src="https://img.shields.io/badge/maturity-Beta-yellow.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/13.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-13-0/manufacture-13-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=13.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
|
||||
supply, and based on parameters set at product variant level, the new
|
||||
procurements for each product.</p>
|
||||
<p>To do this, the calculation starts at top level of the bill of material
|
||||
and explodes this down to the lowest level.</p>
|
||||
<div class="section" id="key-features">
|
||||
<h1>Key Features</h1>
|
||||
<ul class="simple">
|
||||
<li>MRP parameters set by product variant MRP area pairs.</li>
|
||||
<li>Cron job to calculate the MRP demand.</li>
|
||||
<li>Manually calculate the MRP demand.</li>
|
||||
<li>Confirm the calculated MRP demand and create PO’s, or MO’s.</li>
|
||||
<li>Able to see the products for which action is needed throught Planned Orders.</li>
|
||||
<li>Integration with <a class="reference external" href="https://github.com/OCA/stock-logistics-warehouse/tree/12.0/stock_demand_estimate">Stock Demand Estimates</a> system.
|
||||
Note: You need to install <a class="reference external" href="https://github.com/OCA/manufacture/tree/12.0/mrp_multi_level_estimate">mrp_multi_level_estimate module</a>.</li>
|
||||
</ul>
|
||||
<p><strong>Table of contents</strong></p>
|
||||
<div class="contents local topic" id="contents">
|
||||
<ul class="simple">
|
||||
<li><a class="reference internal" href="#configuration" id="toc-entry-1">Configuration</a><ul>
|
||||
<li><a class="reference internal" href="#mrp-areas" id="toc-entry-2">MRP Areas</a></li>
|
||||
<li><a class="reference internal" href="#product-mrp-area-parameters" id="toc-entry-3">Product MRP Area Parameters</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a class="reference internal" href="#usage" id="toc-entry-4">Usage</a></li>
|
||||
<li><a class="reference internal" href="#changelog" id="toc-entry-5">Changelog</a><ul>
|
||||
<li><a class="reference internal" href="#section-1" id="toc-entry-6">13.0.1.5.0 (2020-04-09)</a></li>
|
||||
<li><a class="reference internal" href="#section-2" id="toc-entry-7">13.0.1.4.0 (2020-03-26)</a></li>
|
||||
<li><a class="reference internal" href="#section-3" id="toc-entry-8">13.0.1.3.0 (2020-03-02)</a></li>
|
||||
<li><a class="reference internal" href="#section-4" id="toc-entry-9">13.0.1.2.0 (2020-02-20)</a></li>
|
||||
<li><a class="reference internal" href="#section-5" id="toc-entry-10">13.0.1.1.0 (2020-02-21)</a></li>
|
||||
<li><a class="reference internal" href="#section-6" id="toc-entry-11">13.0.1.0.0 (2019-12-18)</a></li>
|
||||
<li><a class="reference internal" href="#section-7" id="toc-entry-12">12.0.1.0.0 (2019-08-05)</a></li>
|
||||
<li><a class="reference internal" href="#section-8" id="toc-entry-13">11.0.3.0.0 (2019-05-22)</a></li>
|
||||
<li><a class="reference internal" href="#section-9" id="toc-entry-14">11.0.2.2.0 (2019-05-02)</a></li>
|
||||
<li><a class="reference internal" href="#section-10" id="toc-entry-15">11.0.2.1.0 (2019-04-02)</a></li>
|
||||
<li><a class="reference internal" href="#section-11" id="toc-entry-16">11.0.2.0.0 (2018-11-20)</a></li>
|
||||
<li><a class="reference internal" href="#section-12" id="toc-entry-17">11.0.1.1.0 (2018-08-30)</a></li>
|
||||
<li><a class="reference internal" href="#section-13" id="toc-entry-18">11.0.1.0.1 (2018-08-03)</a></li>
|
||||
<li><a class="reference internal" href="#section-14" id="toc-entry-19">11.0.1.0.0 (2018-07-09)</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a class="reference internal" href="#bug-tracker" id="toc-entry-20">Bug Tracker</a></li>
|
||||
<li><a class="reference internal" href="#credits" id="toc-entry-21">Credits</a><ul>
|
||||
<li><a class="reference internal" href="#authors" id="toc-entry-22">Authors</a></li>
|
||||
<li><a class="reference internal" href="#contributors" id="toc-entry-23">Contributors</a></li>
|
||||
<li><a class="reference internal" href="#maintainers" id="toc-entry-24">Maintainers</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="section" id="configuration">
|
||||
<h2><a class="toc-backref" href="#toc-entry-1">Configuration</a></h2>
|
||||
<div class="section" id="mrp-areas">
|
||||
<h3><a class="toc-backref" href="#toc-entry-2">MRP Areas</a></h3>
|
||||
<ul class="simple">
|
||||
<li>Go to <em>Manufacturing > Configuration > MRP Areas</em> and define or edit
|
||||
any existing area. You can specify the working hours for every area.</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="section" id="product-mrp-area-parameters">
|
||||
<h3><a class="toc-backref" href="#toc-entry-3">Product MRP Area Parameters</a></h3>
|
||||
<ul class="simple">
|
||||
<li>Go to <em>Manufacturing > Master Data > Product MRP Area Parameters</em> and set
|
||||
the MRP parameters for a given product and area.</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="section" id="usage">
|
||||
<h2><a class="toc-backref" href="#toc-entry-4">Usage</a></h2>
|
||||
<p>To manually run the MRP scheduler:</p>
|
||||
<ol class="arabic simple">
|
||||
<li>Go to <em>Manufacturing > Operations > Run MRP Multi Level</em>.</li>
|
||||
<li>On the wizard click <em>Run MRP</em>.</li>
|
||||
</ol>
|
||||
<p>To launch replenishment orders (moves, purchases, production orders…):</p>
|
||||
<ol class="arabic simple">
|
||||
<li>Go to <em>Manufacturing > Operations > MRP Inventory</em>.</li>
|
||||
<li>Filter with <em>To procure</em>.</li>
|
||||
<li>Select multiple records and click on <em>Action > Procure</em> or click the right
|
||||
hand side gears in any record.</li>
|
||||
<li>On the wizard, check everything is ok and click <em>Execute</em>.</li>
|
||||
</ol>
|
||||
</div>
|
||||
<div class="section" id="changelog">
|
||||
<h2><a class="toc-backref" href="#toc-entry-5">Changelog</a></h2>
|
||||
<div class="section" id="section-1">
|
||||
<h3><a class="toc-backref" href="#toc-entry-6">13.0.1.5.0 (2020-04-09)</a></h3>
|
||||
<p><strong>Features</strong></p>
|
||||
<ul class="simple">
|
||||
<li>Show <em>Run MRP Multi Level</em> menu only to a specific new security group <em>Run MRP Manually</em>. (<a class="reference external" href="https://github.com/OCA/manufacture/issues/492">#492</a>)</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="section" id="section-2">
|
||||
<h3><a class="toc-backref" href="#toc-entry-7">13.0.1.4.0 (2020-03-26)</a></h3>
|
||||
<blockquote>
|
||||
<ul class="simple">
|
||||
<li>Add menu entry for planned orders</li>
|
||||
<li>Add button to navigate from planned orders to linked manufacturing orders</li>
|
||||
<li>Add action to convert planned orders to fixed</li>
|
||||
<li>When changing the due date in a planned order the release date is recomputed</li>
|
||||
</ul>
|
||||
</blockquote>
|
||||
</div>
|
||||
<div class="section" id="section-3">
|
||||
<h3><a class="toc-backref" href="#toc-entry-8">13.0.1.3.0 (2020-03-02)</a></h3>
|
||||
<ul class="simple">
|
||||
<li>[IMP] Minor changes”
|
||||
(<a class="reference external" href="https://github.com/OCA/manufacture/pull/470">#470</a>).<ul>
|
||||
<li>Planned Order release and due date become required.</li>
|
||||
<li>Add button to Product MRP Area to update MOQ from Supplier Info.</li>
|
||||
<li>Link Manufacturing Orders with Planned Orders.</li>
|
||||
<li>Allow Mrp Inventory Procure Wizard to be used from other models.</li>
|
||||
<li>Make MRP Inventory creation more extensible.</li>
|
||||
<li>Main Supplier computation (v13 requires explicit False definitions)</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="section" id="section-4">
|
||||
<h3><a class="toc-backref" href="#toc-entry-9">13.0.1.2.0 (2020-02-20)</a></h3>
|
||||
<ul class="simple">
|
||||
<li>[IMP] Minor changes
|
||||
(<a class="reference external" href="https://github.com/OCA/manufacture/pull/468">#468</a>).<ul>
|
||||
<li>Planned Orders become fixed on manual creation by default</li>
|
||||
<li>Released Quantity becomes readonly</li>
|
||||
<li>Add product reference if Planned Order name is not defined on bom explosion</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="section" id="section-5">
|
||||
<h3><a class="toc-backref" href="#toc-entry-10">13.0.1.1.0 (2020-02-21)</a></h3>
|
||||
<ul class="simple">
|
||||
<li>[FIX] Minor changes
|
||||
(<a class="reference external" href="https://github.com/OCA/manufacture/pull/469">#469</a>).<ul>
|
||||
<li>Fix Main supplier computation in multi company</li>
|
||||
<li>Drop Triplicated field in search view</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>[IMP] Minor changes
|
||||
(<a class="reference external" href="https://github.com/OCA/manufacture/pull/463">#463</a>).<ul>
|
||||
<li>Show supply method on MRP Inventory</li>
|
||||
<li>Allow no-MRP users to look into Products</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="section" id="section-6">
|
||||
<h3><a class="toc-backref" href="#toc-entry-11">13.0.1.0.0 (2019-12-18)</a></h3>
|
||||
<ul class="simple">
|
||||
<li>[MIG] Migration to v13.</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="section" id="section-7">
|
||||
<h3><a class="toc-backref" href="#toc-entry-12">12.0.1.0.0 (2019-08-05)</a></h3>
|
||||
<ul class="simple">
|
||||
<li>[MIG] Migration to v12:<ul>
|
||||
<li>Estimates as a forecasting mechanism is moved to a new module
|
||||
(mrp_multi_level_estimate).</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="section" id="section-8">
|
||||
<h3><a class="toc-backref" href="#toc-entry-13">11.0.3.0.0 (2019-05-22)</a></h3>
|
||||
<ul class="simple">
|
||||
<li>[REW/IMP] Rework to include Planned Orders.
|
||||
(<a class="reference external" href="https://github.com/OCA/manufacture/pull/365">#365</a>).</li>
|
||||
<li>[IMP] Able to procure from a different location than the area’s location.</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="section" id="section-9">
|
||||
<h3><a class="toc-backref" href="#toc-entry-14">11.0.2.2.0 (2019-05-02)</a></h3>
|
||||
<ul class="simple">
|
||||
<li>[IMP] Able to run MRP only for selected areas.
|
||||
(<a class="reference external" href="https://github.com/OCA/manufacture/pull/360">#360</a>).</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="section" id="section-10">
|
||||
<h3><a class="toc-backref" href="#toc-entry-15">11.0.2.1.0 (2019-04-02)</a></h3>
|
||||
<ul class="simple">
|
||||
<li>[IMP] Implement <em>Nbr. Days</em> functionality to be able to group demand when
|
||||
generating supply proposals.
|
||||
(<a class="reference external" href="https://github.com/OCA/manufacture/pull/345">#345</a>).</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="section" id="section-11">
|
||||
<h3><a class="toc-backref" href="#toc-entry-16">11.0.2.0.0 (2018-11-20)</a></h3>
|
||||
<ul class="simple">
|
||||
<li>[REW] Refactor MRP Area.
|
||||
(<a class="reference external" href="https://github.com/OCA/manufacture/pull/322">#322</a>):<ul>
|
||||
<li>MRP product concept dropped in favor of <em>Product MRP Area Parameters</em>.
|
||||
This allow to set different MRP parameters for the same product in
|
||||
different areas.</li>
|
||||
<li>Menu items reordering.</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="section" id="section-12">
|
||||
<h3><a class="toc-backref" href="#toc-entry-17">11.0.1.1.0 (2018-08-30)</a></h3>
|
||||
<ul class="simple">
|
||||
<li>[FIX] Consider <em>Qty Multiple</em> on product to propose the quantity to procure.
|
||||
(<a class="reference external" href="https://github.com/OCA/manufacture/pull/297">#297</a>)</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="section" id="section-13">
|
||||
<h3><a class="toc-backref" href="#toc-entry-18">11.0.1.0.1 (2018-08-03)</a></h3>
|
||||
<ul class="simple">
|
||||
<li>[FIX] User and system locales doesn’t break MRP calculation.
|
||||
(<a class="reference external" href="https://github.com/OCA/manufacture/pull/290">#290</a>)</li>
|
||||
<li>[FIX] Working Hours are now defined only at Warehouse level and displayed
|
||||
as a related on MRP Areas.
|
||||
(<a class="reference external" href="https://github.com/OCA/manufacture/pull/290">#290</a>)</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="section" id="section-14">
|
||||
<h3><a class="toc-backref" href="#toc-entry-19">11.0.1.0.0 (2018-07-09)</a></h3>
|
||||
<ul class="simple">
|
||||
<li>Start of the history.</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="section" id="bug-tracker">
|
||||
<h2><a class="toc-backref" href="#toc-entry-20">Bug Tracker</a></h2>
|
||||
<p>Bugs are tracked on <a class="reference external" href="https://github.com/OCA/manufacture/issues">GitHub Issues</a>.
|
||||
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
|
||||
<a class="reference external" href="https://github.com/OCA/manufacture/issues/new?body=module:%20mrp_multi_level%0Aversion:%2013.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**">feedback</a>.</p>
|
||||
<p>Do not contact contributors directly about support or help with technical issues.</p>
|
||||
</div>
|
||||
<div class="section" id="credits">
|
||||
<h2><a class="toc-backref" href="#toc-entry-21">Credits</a></h2>
|
||||
<div class="section" id="authors">
|
||||
<h3><a class="toc-backref" href="#toc-entry-22">Authors</a></h3>
|
||||
<ul class="simple">
|
||||
<li>Ucamco</li>
|
||||
<li>ForgeFlow</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="section" id="contributors">
|
||||
<h3><a class="toc-backref" href="#toc-entry-23">Contributors</a></h3>
|
||||
<ul class="simple">
|
||||
<li>Wim Audenaert <<a class="reference external" href="mailto:wim.audenaert@ucamco.com">wim.audenaert@ucamco.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>Héctor Villarreal <<a class="reference external" href="mailto:hector.villarreal@forgeflow.com">hector.villarreal@forgeflow.com</a>></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="section" id="maintainers">
|
||||
<h3><a class="toc-backref" href="#toc-entry-24">Maintainers</a></h3>
|
||||
<p>This module is maintained by the OCA.</p>
|
||||
<a class="reference external image-reference" href="https://odoo-community.org"><img alt="Odoo Community Association" src="https://odoo-community.org/logo.png" /></a>
|
||||
<p>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.</p>
|
||||
<p>Current <a class="reference external" href="https://odoo-community.org/page/maintainer-role">maintainers</a>:</p>
|
||||
<p><a class="reference external image-reference" href="https://github.com/JordiBForgeFlow"><img alt="JordiBForgeFlow" src="https://github.com/JordiBForgeFlow.png?size=40px" /></a> <a class="reference external image-reference" href="https://github.com/LoisRForgeFlow"><img alt="LoisRForgeFlow" src="https://github.com/LoisRForgeFlow.png?size=40px" /></a></p>
|
||||
<p>This module is part of the <a class="reference external" href="https://github.com/OCA/manufacture/tree/13.0/mrp_multi_level">OCA/manufacture</a> project on GitHub.</p>
|
||||
<p>You are welcome to contribute. To learn how please visit <a class="reference external" href="https://odoo-community.org/page/Contribute">https://odoo-community.org/page/Contribute</a>.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -15,6 +15,7 @@ class TestMrpMultiLevelCommon(SavepointCase):
|
||||
cls.po_obj = cls.env["purchase.order"]
|
||||
cls.product_obj = cls.env["product.product"]
|
||||
cls.loc_obj = cls.env["stock.location"]
|
||||
cls.quant_obj = cls.env["stock.quant"]
|
||||
cls.mrp_area_obj = cls.env["mrp.area"]
|
||||
cls.product_mrp_area_obj = cls.env["product.mrp.area"]
|
||||
cls.partner_obj = cls.env["res.partner"]
|
||||
@@ -25,6 +26,8 @@ class TestMrpMultiLevelCommon(SavepointCase):
|
||||
cls.mrp_inventory_obj = cls.env["mrp.inventory"]
|
||||
cls.mrp_move_obj = cls.env["mrp.move"]
|
||||
cls.planned_order_obj = cls.env["mrp.planned.order"]
|
||||
cls.lot_model = cls.env["stock.production.lot"]
|
||||
cls.quant_model = cls.env["stock.quant"]
|
||||
|
||||
cls.fp_1 = cls.env.ref("mrp_multi_level.product_product_fp_1")
|
||||
cls.fp_2 = cls.env.ref("mrp_multi_level.product_product_fp_2")
|
||||
@@ -220,6 +223,53 @@ class TestMrpMultiLevelCommon(SavepointCase):
|
||||
cls.product_mrp_area_obj.create(
|
||||
{"product_id": cls.prod_uom_test.id, "mrp_area_id": cls.mrp_area.id}
|
||||
)
|
||||
# Product to test lots
|
||||
cls.product_lots = cls.product_obj.create(
|
||||
{
|
||||
"name": "Product Tracked by Lots",
|
||||
"type": "product",
|
||||
"tracking": "lot",
|
||||
"uom_id": cls.env.ref("uom.product_uom_unit").id,
|
||||
"list_price": 100.0,
|
||||
"produce_delay": 5.0,
|
||||
"route_ids": [(6, 0, [route_buy])],
|
||||
"seller_ids": [(0, 0, {"name": vendor1.id, "price": 25.0})],
|
||||
}
|
||||
)
|
||||
cls.product_mrp_area_obj.create(
|
||||
{"product_id": cls.product_lots.id, "mrp_area_id": cls.mrp_area.id}
|
||||
)
|
||||
cls.lot_1 = cls.lot_model.create(
|
||||
{
|
||||
"product_id": cls.product_lots.id,
|
||||
"name": "Lot 1",
|
||||
"company_id": cls.company.id,
|
||||
}
|
||||
)
|
||||
cls.lot_2 = cls.lot_model.create(
|
||||
{
|
||||
"product_id": cls.product_lots.id,
|
||||
"name": "Lot 2",
|
||||
"company_id": cls.company.id,
|
||||
}
|
||||
)
|
||||
cls.quant_model.sudo().create(
|
||||
{
|
||||
"product_id": cls.product_lots.id,
|
||||
"lot_id": cls.lot_1.id,
|
||||
"quantity": 100.0,
|
||||
"location_id": cls.stock_location.id,
|
||||
}
|
||||
)
|
||||
cls.quant_model.sudo().create(
|
||||
{
|
||||
"product_id": cls.product_lots.id,
|
||||
"lot_id": cls.lot_2.id,
|
||||
"quantity": 110.0,
|
||||
"location_id": cls.stock_location.id,
|
||||
}
|
||||
)
|
||||
|
||||
# Product MRP Parameter to test supply method computation
|
||||
cls.env.ref("stock.route_warehouse0_mto").active = True
|
||||
cls.env["stock.rule"].create(
|
||||
@@ -453,6 +503,9 @@ class TestMrpMultiLevelCommon(SavepointCase):
|
||||
cls.create_demand_sec_loc(cls.date_20, 46.0)
|
||||
cls.create_demand_sec_loc(cls.date_22, 33.0)
|
||||
|
||||
# Create pickings:
|
||||
cls._create_picking_out(cls.product_lots, 25, today)
|
||||
|
||||
cls.mrp_multi_level_wiz.create({}).run_mrp_multi_level()
|
||||
|
||||
@classmethod
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# Copyright 2018-19 ForgeFlow S.L. (https://www.forgeflow.com)
|
||||
# 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
|
||||
|
||||
@@ -447,3 +447,398 @@ class TestMrpMultiLevel(TestMrpMultiLevelCommon):
|
||||
self.fp_4.route_ids = [(4, self.env.ref("mrp.route_warehouse0_manufacture").id)]
|
||||
product_mrp_area._compute_supply_method()
|
||||
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})",
|
||||
)
|
||||
|
||||
def test_19_on_hand_with_lots(self):
|
||||
"""Check that on-hand is correctly computed when tracking by lots."""
|
||||
lots_line_1 = self.mrp_inventory_obj.search(
|
||||
[("product_mrp_area_id.product_id", "=", self.product_lots.id)]
|
||||
)
|
||||
self.assertEqual(len(lots_line_1), 1)
|
||||
self.assertEqual(lots_line_1.initial_on_hand_qty, 210)
|
||||
self.assertEqual(lots_line_1.final_on_hand_qty, 185)
|
||||
|
||||
def test_20_prioritize_safety_stock_grouped_1(self):
|
||||
"""Test grouped demand MRP but with a short nbr days.
|
||||
Safety stock should be ordered."""
|
||||
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_nbr_days": 2,
|
||||
}
|
||||
)
|
||||
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})",
|
||||
)
|
||||
|
||||
def test_21_prioritize_safety_stock_grouped_2(self):
|
||||
"""Test grouped demand MRP but with a longer nbr days.
|
||||
Safety stock should be ordered."""
|
||||
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_nbr_days": 7,
|
||||
}
|
||||
)
|
||||
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=12), 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": 21.0,
|
||||
"supply_qty": 0.0,
|
||||
"to_procure": 16.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": 0.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": 27.0,
|
||||
"supply_qty": 10.0,
|
||||
"to_procure": 2.0,
|
||||
},
|
||||
{
|
||||
"date": now.date() + timedelta(days=12),
|
||||
"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": 0.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})",
|
||||
)
|
||||
|
||||
def test_22_prioritize_safety_stock_grouped_3(self):
|
||||
"""Test grouped demand MRP but with an existing incoming supply
|
||||
Safety stock should NOT be ordered."""
|
||||
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_nbr_days": 7,
|
||||
}
|
||||
)
|
||||
self._create_picking_in(
|
||||
product, 30.0, now + timedelta(days=3), location=self.cases_loc
|
||||
)
|
||||
self._create_picking_out(
|
||||
product, 6.0, now + timedelta(days=7), location=self.cases_loc
|
||||
)
|
||||
self._create_picking_out(
|
||||
product, 12.0, now + timedelta(days=12), 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() + timedelta(days=3),
|
||||
"demand_qty": 0.0,
|
||||
"initial_on_hand_qty": 5.0,
|
||||
"final_on_hand_qty": 35.0,
|
||||
"running_availability": 35.0,
|
||||
"supply_qty": 30.0,
|
||||
"to_procure": 0.0,
|
||||
},
|
||||
{
|
||||
"date": now.date() + timedelta(days=7),
|
||||
"demand_qty": 6.0,
|
||||
"initial_on_hand_qty": 35.0,
|
||||
"final_on_hand_qty": 29.0,
|
||||
"running_availability": 29.0,
|
||||
"supply_qty": 0.0,
|
||||
"to_procure": 0.0,
|
||||
},
|
||||
{
|
||||
"date": now.date() + timedelta(days=12),
|
||||
"demand_qty": 12.0,
|
||||
"initial_on_hand_qty": 29.0,
|
||||
"final_on_hand_qty": 17.0,
|
||||
"running_availability": 17.0,
|
||||
"supply_qty": 0.0,
|
||||
"to_procure": 0.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})",
|
||||
)
|
||||
|
||||
def test_23_prioritize_safety_stock_with_mrp_moves_today(self):
|
||||
"""Test MRP but with moves today. Safety stock should not be ordered."""
|
||||
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,
|
||||
}
|
||||
)
|
||||
self._create_picking_out(product, 10.0, now, location=self.cases_loc)
|
||||
self._create_picking_in(product, 20.0, now, 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": 10.0,
|
||||
"final_on_hand_qty": 15.0,
|
||||
"initial_on_hand_qty": 5.0,
|
||||
"running_availability": 15.0,
|
||||
"supply_qty": 20.0,
|
||||
"to_procure": 0.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})",
|
||||
)
|
||||
|
||||
def test_24_prioritize_safety_stock_with_mrp_moves_today_grouped(self):
|
||||
"""Test grouped demand MRP but with moves today. Safety stock should not be ordered."""
|
||||
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_nbr_days": 2,
|
||||
}
|
||||
)
|
||||
self._create_picking_out(product, 10.0, now, location=self.cases_loc)
|
||||
self._create_picking_in(product, 20.0, now, 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": 10.0,
|
||||
"final_on_hand_qty": 15.0,
|
||||
"initial_on_hand_qty": 5.0,
|
||||
"running_availability": 15.0,
|
||||
"supply_qty": 20.0,
|
||||
"to_procure": 0.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})",
|
||||
)
|
||||
|
||||
@@ -141,6 +141,11 @@
|
||||
string="Main Supplier"
|
||||
context="{'group_by':'main_supplier_id'}"
|
||||
/>
|
||||
<filter
|
||||
name="group_date"
|
||||
string="Date"
|
||||
context="{'group_by':'date'}"
|
||||
/>
|
||||
<filter
|
||||
name="group_release_date"
|
||||
string="Date to Procure"
|
||||
|
||||
@@ -23,23 +23,6 @@ class MultiLevelMrp(models.TransientModel):
|
||||
help="If empty, all areas will be computed.",
|
||||
)
|
||||
|
||||
@api.model
|
||||
def _prepare_product_mrp_area_data(self, product_mrp_area):
|
||||
qty_available = 0.0
|
||||
product_obj = self.env["product.product"]
|
||||
location_ids = product_mrp_area._get_locations()
|
||||
for location in location_ids:
|
||||
product_l = product_obj.with_context({"location": location.id}).browse(
|
||||
product_mrp_area.product_id.id
|
||||
)
|
||||
qty_available += product_l.qty_available
|
||||
|
||||
return {
|
||||
"product_mrp_area_id": product_mrp_area.id,
|
||||
"mrp_qty_available": qty_available,
|
||||
"mrp_llc": product_mrp_area.product_id.llc,
|
||||
}
|
||||
|
||||
@api.model
|
||||
def _prepare_mrp_move_data_from_stock_move(
|
||||
self, product_mrp_area, move, direction="in"
|
||||
@@ -538,13 +521,53 @@ class MultiLevelMrp(models.TransientModel):
|
||||
self._init_mrp_move(product_mrp_area)
|
||||
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
|
||||
def _init_mrp_move_grouped_demand(self, nbr_create, product_mrp_area):
|
||||
def _init_mrp_move_grouped_demand(self, product_mrp_area):
|
||||
last_date = None
|
||||
last_qty = 0.00
|
||||
onhand = product_mrp_area.qty_available
|
||||
grouping_delta = product_mrp_area.mrp_nbr_days
|
||||
demand_origin = []
|
||||
|
||||
if (
|
||||
product_mrp_area.mrp_move_ids
|
||||
and onhand < product_mrp_area.mrp_minimum_stock
|
||||
):
|
||||
last_date = self._get_safety_stock_target_date(product_mrp_area)
|
||||
demand_origin.append("Safety Stock")
|
||||
move = fields.first(product_mrp_area.mrp_move_ids)
|
||||
if last_date and (
|
||||
fields.Date.from_string(move.mrp_date)
|
||||
>= last_date + timedelta(days=grouping_delta)
|
||||
):
|
||||
name = _("Safety Stock")
|
||||
origin = ",".join(list({x for x in demand_origin if x}))
|
||||
qtytoorder = self._get_qty_to_order(
|
||||
product_mrp_area, last_date, 0, onhand
|
||||
)
|
||||
cm = self.create_action(
|
||||
product_mrp_area_id=product_mrp_area,
|
||||
mrp_date=last_date,
|
||||
mrp_qty=qtytoorder,
|
||||
name=name,
|
||||
values=dict(origin=origin),
|
||||
)
|
||||
qty_ordered = cm.get("qty_ordered", 0.0)
|
||||
onhand = onhand + qty_ordered
|
||||
last_date = None
|
||||
last_qty = 0.00
|
||||
demand_origin = []
|
||||
|
||||
for move in product_mrp_area.mrp_move_ids:
|
||||
if self._exclude_move(move):
|
||||
continue
|
||||
@@ -566,8 +589,10 @@ class MultiLevelMrp(models.TransientModel):
|
||||
product_name=product_mrp_area.product_id.display_name,
|
||||
delta_days=grouping_delta,
|
||||
)
|
||||
origin = ",".join(list(set(demand_origin)))
|
||||
qtytoorder = product_mrp_area.mrp_minimum_stock - onhand - last_qty
|
||||
origin = ",".join(list({x for x in demand_origin if x}))
|
||||
qtytoorder = self._get_qty_to_order(
|
||||
product_mrp_area, last_date, last_qty, onhand
|
||||
)
|
||||
cm = self.create_action(
|
||||
product_mrp_area_id=product_mrp_area,
|
||||
mrp_date=last_date,
|
||||
@@ -579,13 +604,12 @@ class MultiLevelMrp(models.TransientModel):
|
||||
onhand = onhand + last_qty + qty_ordered
|
||||
last_date = None
|
||||
last_qty = 0.00
|
||||
nbr_create += 1
|
||||
demand_origin = []
|
||||
if (
|
||||
(onhand + last_qty + move.mrp_qty) < product_mrp_area.mrp_minimum_stock
|
||||
or (onhand + last_qty) < product_mrp_area.mrp_minimum_stock
|
||||
):
|
||||
if not last_date or last_qty == 0.0:
|
||||
if not last_date:
|
||||
last_date = fields.Date.from_string(move.mrp_date)
|
||||
last_qty = move.mrp_qty
|
||||
else:
|
||||
@@ -603,8 +627,10 @@ class MultiLevelMrp(models.TransientModel):
|
||||
product_name=product_mrp_area.product_id.display_name,
|
||||
delta_days=grouping_delta,
|
||||
)
|
||||
origin = ",".join(list(set(demand_origin)))
|
||||
qtytoorder = product_mrp_area.mrp_minimum_stock - onhand - last_qty
|
||||
origin = ",".join(list({x for x in demand_origin if x}))
|
||||
qtytoorder = self._get_qty_to_order(
|
||||
product_mrp_area, last_date, last_qty, onhand
|
||||
)
|
||||
cm = self.create_action(
|
||||
product_mrp_area_id=product_mrp_area,
|
||||
mrp_date=last_date,
|
||||
@@ -614,8 +640,82 @@ class MultiLevelMrp(models.TransientModel):
|
||||
)
|
||||
qty_ordered = cm.get("qty_ordered", 0.0)
|
||||
onhand += qty_ordered
|
||||
nbr_create += 1
|
||||
return nbr_create
|
||||
last_qty -= qty_ordered
|
||||
|
||||
if (onhand + last_qty) < 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
|
||||
|
||||
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, product_mrp_area):
|
||||
onhand = product_mrp_area.qty_available
|
||||
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:
|
||||
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
|
||||
|
||||
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
|
||||
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, 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
|
||||
|
||||
@api.model
|
||||
def _exclude_move(self, move):
|
||||
@@ -638,47 +738,10 @@ class MultiLevelMrp(models.TransientModel):
|
||||
llc += 1
|
||||
|
||||
for product_mrp_area in product_mrp_areas:
|
||||
nbr_create = 0
|
||||
onhand = product_mrp_area.qty_available
|
||||
if product_mrp_area.mrp_nbr_days == 0:
|
||||
for move in product_mrp_area.mrp_move_ids:
|
||||
if self._exclude_move(move):
|
||||
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,
|
||||
values=dict(origin=move.origin),
|
||||
)
|
||||
qty_ordered = cm["qty_ordered"]
|
||||
onhand += move.mrp_qty + qty_ordered
|
||||
nbr_create += 1
|
||||
else:
|
||||
onhand += move.mrp_qty
|
||||
self._init_mrp_move_non_grouped_demand(product_mrp_area)
|
||||
else:
|
||||
nbr_create = self._init_mrp_move_grouped_demand(
|
||||
nbr_create, product_mrp_area
|
||||
)
|
||||
|
||||
if onhand < product_mrp_area.mrp_minimum_stock and nbr_create == 0:
|
||||
qtytoorder = product_mrp_area.mrp_minimum_stock - onhand
|
||||
name = _("Safety Stock")
|
||||
cm = self.create_action(
|
||||
product_mrp_area_id=product_mrp_area,
|
||||
mrp_date=date.today(),
|
||||
mrp_qty=qtytoorder,
|
||||
name=name,
|
||||
values=dict(origin=name),
|
||||
)
|
||||
qty_ordered = cm["qty_ordered"]
|
||||
onhand += qty_ordered
|
||||
self._init_mrp_move_grouped_demand(product_mrp_area)
|
||||
counter += 1
|
||||
|
||||
log_msg = "MRP Calculation LLC {} Finished - Nbr. products: {}".format(
|
||||
@@ -780,9 +843,7 @@ class MultiLevelMrp(models.TransientModel):
|
||||
[("product_mrp_area_id", "=", product_mrp_area.id)], order="due_date"
|
||||
).mapped("due_date")
|
||||
mrp_dates = set(moves_dates + action_dates)
|
||||
on_hand_qty = product_mrp_area.product_id.with_context(
|
||||
location=product_mrp_area._get_locations().ids
|
||||
)._product_available()[product_mrp_area.product_id.id]["qty_available"]
|
||||
on_hand_qty = product_mrp_area.qty_available
|
||||
running_availability = on_hand_qty
|
||||
mrp_inventory_vals = []
|
||||
for mdt in sorted(mrp_dates):
|
||||
|
||||
Reference in New Issue
Block a user