[11.0][IMP] mrp_multi_level:

* Able to run MRP only for selected areas.
 * Clean logging messages.
This commit is contained in:
Lois Rilo
2019-05-02 13:00:48 +02:00
committed by JasminSForgeFlow
parent 9cfad09412
commit 64e88d6be1
7 changed files with 165 additions and 99 deletions

View File

@@ -81,6 +81,12 @@ To launch replenishment orders (moves, purchases, production orders...):
Changelog
=========
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)
~~~~~~~~~~~~~~~~~~~~~~~

View File

@@ -3,7 +3,7 @@
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
{
'name': 'MRP Multi Level',
'version': '11.0.2.1.0',
'version': '11.0.2.2.0',
'development_status': 'Beta',
'license': 'AGPL-3',
'author': 'Ucamco, '

View File

@@ -1,3 +1,9 @@
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)
~~~~~~~~~~~~~~~~~~~~~~~

View File

@@ -386,40 +386,41 @@ and explodes this down to the lowest level.</p>
<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="id10">Configuration</a><ul>
<li><a class="reference internal" href="#mrp-areas" id="id11">MRP Areas</a></li>
<li><a class="reference internal" href="#product-mrp-area-parameters" id="id12">Product MRP Area Parameters</a></li>
<li><a class="reference internal" href="#configuration" id="id12">Configuration</a><ul>
<li><a class="reference internal" href="#mrp-areas" id="id13">MRP Areas</a></li>
<li><a class="reference internal" href="#product-mrp-area-parameters" id="id14">Product MRP Area Parameters</a></li>
</ul>
</li>
<li><a class="reference internal" href="#usage" id="id13">Usage</a></li>
<li><a class="reference internal" href="#changelog" id="id14">Changelog</a><ul>
<li><a class="reference internal" href="#id1" id="id15">11.0.2.1.0 (2019-04-02)</a></li>
<li><a class="reference internal" href="#id3" id="id16">11.0.2.0.0 (2018-11-20)</a></li>
<li><a class="reference internal" href="#id5" id="id17">11.0.1.1.0 (2018-08-30)</a></li>
<li><a class="reference internal" href="#id7" id="id18">11.0.1.0.1 (2018-08-03)</a></li>
<li><a class="reference internal" href="#id9" id="id19">11.0.1.0.0 (2018-07-09)</a></li>
<li><a class="reference internal" href="#usage" id="id15">Usage</a></li>
<li><a class="reference internal" href="#changelog" id="id16">Changelog</a><ul>
<li><a class="reference internal" href="#id1" id="id17">11.0.2.2.0 (2019-05-02)</a></li>
<li><a class="reference internal" href="#id3" id="id18">11.0.2.1.0 (2019-04-02)</a></li>
<li><a class="reference internal" href="#id5" id="id19">11.0.2.0.0 (2018-11-20)</a></li>
<li><a class="reference internal" href="#id7" id="id20">11.0.1.1.0 (2018-08-30)</a></li>
<li><a class="reference internal" href="#id9" id="id21">11.0.1.0.1 (2018-08-03)</a></li>
<li><a class="reference internal" href="#id11" id="id22">11.0.1.0.0 (2018-07-09)</a></li>
</ul>
</li>
<li><a class="reference internal" href="#bug-tracker" id="id20">Bug Tracker</a></li>
<li><a class="reference internal" href="#credits" id="id21">Credits</a><ul>
<li><a class="reference internal" href="#authors" id="id22">Authors</a></li>
<li><a class="reference internal" href="#contributors" id="id23">Contributors</a></li>
<li><a class="reference internal" href="#maintainers" id="id24">Maintainers</a></li>
<li><a class="reference internal" href="#bug-tracker" id="id23">Bug Tracker</a></li>
<li><a class="reference internal" href="#credits" id="id24">Credits</a><ul>
<li><a class="reference internal" href="#authors" id="id25">Authors</a></li>
<li><a class="reference internal" href="#contributors" id="id26">Contributors</a></li>
<li><a class="reference internal" href="#maintainers" id="id27">Maintainers</a></li>
</ul>
</li>
</ul>
</div>
<div class="section" id="configuration">
<h2><a class="toc-backref" href="#id10">Configuration</a></h2>
<h2><a class="toc-backref" href="#id12">Configuration</a></h2>
<div class="section" id="mrp-areas">
<h3><a class="toc-backref" href="#id11">MRP Areas</a></h3>
<h3><a class="toc-backref" href="#id13">MRP Areas</a></h3>
<ul class="simple">
<li>Go to <em>Manufacturing &gt; Configuration &gt; 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="#id12">Product MRP Area Parameters</a></h3>
<h3><a class="toc-backref" href="#id14">Product MRP Area Parameters</a></h3>
<ul class="simple">
<li>Go to <em>Manufacturing &gt; Master Data &gt; Product MRP Area Parameters</em> and set
the MRP parameters for a given product and area.</li>
@@ -427,7 +428,7 @@ the MRP parameters for a given product and area.</li>
</div>
</div>
<div class="section" id="usage">
<h2><a class="toc-backref" href="#id13">Usage</a></h2>
<h2><a class="toc-backref" href="#id15">Usage</a></h2>
<p>To manually run the MRP scheduler:</p>
<ol class="arabic simple">
<li>Go to <em>Manufacturing &gt; Operations &gt; Run MRP Multi Level</em>.</li>
@@ -443,17 +444,24 @@ hand side gears in any record.</li>
</ol>
</div>
<div class="section" id="changelog">
<h2><a class="toc-backref" href="#id14">Changelog</a></h2>
<h2><a class="toc-backref" href="#id16">Changelog</a></h2>
<div class="section" id="id1">
<h3><a class="toc-backref" href="#id15">11.0.2.1.0 (2019-04-02)</a></h3>
<h3><a class="toc-backref" href="#id17">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="id3">
<h3><a class="toc-backref" href="#id18">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="id3">
<h3><a class="toc-backref" href="#id16">11.0.2.0.0 (2018-11-20)</a></h3>
<div class="section" id="id5">
<h3><a class="toc-backref" href="#id19">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>
@@ -465,15 +473,15 @@ different areas.</li>
</li>
</ul>
</div>
<div class="section" id="id5">
<h3><a class="toc-backref" href="#id17">11.0.1.1.0 (2018-08-30)</a></h3>
<div class="section" id="id7">
<h3><a class="toc-backref" href="#id20">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="id7">
<h3><a class="toc-backref" href="#id18">11.0.1.0.1 (2018-08-03)</a></h3>
<div class="section" id="id9">
<h3><a class="toc-backref" href="#id21">11.0.1.0.1 (2018-08-03)</a></h3>
<ul class="simple">
<li>[FIX] User and system locales doesnt break MRP calculation.
(<a class="reference external" href="https://github.com/OCA/manufacture/pull/290">#290</a>)</li>
@@ -482,15 +490,15 @@ 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="id9">
<h3><a class="toc-backref" href="#id19">11.0.1.0.0 (2018-07-09)</a></h3>
<div class="section" id="id11">
<h3><a class="toc-backref" href="#id22">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="#id20">Bug Tracker</a></h2>
<h2><a class="toc-backref" href="#id23">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 smashing it by providing a detailed and welcomed
@@ -498,16 +506,16 @@ If you spotted it first, help us smashing it by providing a detailed and welcome
<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="#id21">Credits</a></h2>
<h2><a class="toc-backref" href="#id24">Credits</a></h2>
<div class="section" id="authors">
<h3><a class="toc-backref" href="#id22">Authors</a></h3>
<h3><a class="toc-backref" href="#id25">Authors</a></h3>
<ul class="simple">
<li>Ucamco</li>
<li>Eficent</li>
</ul>
</div>
<div class="section" id="contributors">
<h3><a class="toc-backref" href="#id23">Contributors</a></h3>
<h3><a class="toc-backref" href="#id26">Contributors</a></h3>
<ul class="simple">
<li>Wim Audenaert &lt;<a class="reference external" href="mailto:wim.audenaert&#64;ucamco.com">wim.audenaert&#64;ucamco.com</a>&gt;</li>
<li>Jordi Ballester &lt;<a class="reference external" href="mailto:jordi.ballester&#64;eficent.com">jordi.ballester&#64;eficent.com</a>&gt;</li>
@@ -515,7 +523,7 @@ If you spotted it first, help us smashing it by providing a detailed and welcome
</ul>
</div>
<div class="section" id="maintainers">
<h3><a class="toc-backref" href="#id24">Maintainers</a></h3>
<h3><a class="toc-backref" href="#id27">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

View File

@@ -21,6 +21,7 @@ class TestMrpMultiLevel(SavepointCase):
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']
cls.res_users = cls.env['res.users']
cls.stock_picking_obj = cls.env['stock.picking']
cls.estimate_obj = cls.env['stock.demand.estimate']
cls.mrp_multi_level_wiz = cls.env['mrp.multi.level']
@@ -34,6 +35,7 @@ class TestMrpMultiLevel(SavepointCase):
cls.sf_2 = cls.env.ref('mrp_multi_level.product_product_sf_2')
cls.pp_1 = cls.env.ref('mrp_multi_level.product_product_pp_1')
cls.pp_2 = cls.env.ref('mrp_multi_level.product_product_pp_2')
cls.company = cls.env.ref('base.main_company')
cls.mrp_area = cls.env.ref('mrp_multi_level.mrp_area_stock_wh0')
cls.vendor = cls.env.ref('mrp_multi_level.res_partner_lazer_tech')
cls.wh = cls.env.ref('stock.warehouse0')
@@ -47,6 +49,16 @@ class TestMrpMultiLevel(SavepointCase):
# Partner:
vendor1 = cls.partner_obj.create({'name': 'Vendor 1'})
# Create user:
group_mrp_manager = cls.env.ref('mrp.group_mrp_manager')
group_user = cls.env.ref('base.group_user')
group_stock_manager = cls.env.ref('stock.group_stock_manager')
cls.mrp_manager = cls._create_user(
'Test User',
[group_mrp_manager, group_user, group_stock_manager],
cls.company,
)
# Create secondary location and MRP Area:
cls.sec_loc = cls.loc_obj.create({
'name': 'Test location',
@@ -274,6 +286,18 @@ class TestMrpMultiLevel(SavepointCase):
'date_range_id': date_range.id,
})
@classmethod
def _create_user(cls, login, groups, company):
user = cls.res_users.create({
'name': login,
'login': login,
'password': 'demo',
'email': 'example@yourcompany.com',
'company_id': company.id,
'groups_id': [(6, 0, [group.id for group in groups])]
})
return user
def test_01_mrp_levels(self):
"""Tests computation of MRP levels."""
self.assertEqual(self.fp_1.llc, 0)
@@ -438,12 +462,6 @@ class TestMrpMultiLevel(SavepointCase):
self.assertEqual(len(moves), 6)
expected = [200.0, 290.0, 90.0, 0.0, 72.0, 0.0]
self.assertEqual(moves.mapped('running_availability'), expected)
# Actions counters for PP-1:
# product_mrp_area = self.product_mrp_area_obj.search([
# ('product_id', '=', self.pp_1.id)
# ]) # TODO
# self.assertEqual(product_mrp_area.nbr_mrp_actions, 3) # TODO
# self.assertEqual(product_mrp_area.nbr_mrp_actions_4w, 3) # TODO
def test_06_demand_estimates(self):
"""Tests demand estimates integration."""
@@ -534,5 +552,19 @@ class TestMrpMultiLevel(SavepointCase):
week_3_expected = sum(moves_from_estimates[14:].mapped('mrp_qty'))
self.assertIn(abs(week_3_expected), quantities)
def test_10_isolated_mrp_area_run(self):
"""Test running MRP for just one area."""
self.mrp_multi_level_wiz.sudo(self.mrp_manager).create({
'mrp_area_ids': [(6, 0, self.secondary_area.ids)],
}).run_mrp_multi_level()
this = self.mrp_inventory_obj.search([
('mrp_area_id', '=', self.secondary_area.id)], limit=1)
self.assertTrue(this)
# Only recently exectued areas should have been created by test user:
self.assertEqual(this.create_uid, self.mrp_manager)
prev = self.mrp_inventory_obj.search([
('mrp_area_id', '!=', self.secondary_area.id)], limit=1)
self.assertNotEqual(this.create_uid, prev.create_uid)
# TODO: test procure wizard: pos, multiple...
# TODO: test multiple destination IDS:...

View File

@@ -15,6 +15,12 @@ logger = logging.getLogger(__name__)
class MultiLevelMrp(models.TransientModel):
_name = 'mrp.multi.level'
mrp_area_ids = fields.Many2many(
comodel_name="mrp.area",
string="MRP Areas to run",
help="If empty, all areas will be computed.",
)
# TODO: dates are not being correctly computed for supply...
@api.model
@@ -269,31 +275,36 @@ class MultiLevelMrp(models.TransientModel):
mrpmove_id2 = self.env['mrp.move'].create(move_data)
mrpmove_id.mrp_move_down_ids = [(4, mrpmove_id2.id)]
values['qty_ordered'] = qty_ordered
log_msg = '%s' % qty_ordered
logger.info(log_msg)
log_msg = '[%s] %s: qty_ordered = %s' % (
product_mrp_area_id.mrp_area_id.name,
product_mrp_area_id.product_id.default_code or
product_mrp_area_id.product_id.name,
qty_ordered,
)
logger.debug(log_msg)
return values
@api.model
def _mrp_cleanup(self):
# Some part of the code with the new API is replaced by
# sql statements due to performance issues when the auditlog is
# installed
logger.info('START MRP CLEANUP')
self.env['mrp.move'].search([]).unlink()
self.env['mrp.inventory'].search([]).unlink()
logger.info('END MRP CLEANUP')
def _mrp_cleanup(self, mrp_areas):
logger.info('Start MRP Cleanup')
domain = []
if mrp_areas:
domain += [('mrp_area_id', 'in', mrp_areas.ids)]
self.env['mrp.move'].search(domain).unlink()
self.env['mrp.inventory'].search(domain).unlink()
logger.info('End MRP Cleanup')
return True
@api.model
def _low_level_code_calculation(self):
logger.info('START LOW LEVEL CODE CALCULATION')
logger.info('Start low level code calculation')
counter = 999999
llc = 0
self.env['product.product'].search([]).write({'llc': llc})
products = self.env['product.product'].search([('llc', '=', llc)])
if products:
counter = len(products)
log_msg = 'LOW LEVEL CODE 0 FINISHED - NBR PRODUCTS: %s' % counter
log_msg = 'Low level code 0 finished - Nbr. products: %s' % counter
logger.info(log_msg)
while counter:
@@ -308,33 +319,38 @@ class MultiLevelMrp(models.TransientModel):
products.write({'llc': llc})
products = self.env['product.product'].search([('llc', '=', llc)])
counter = len(products)
log_msg = 'LOW LEVEL CODE %s FINISHED - NBR PRODUCTS: %s' % (
log_msg = 'Low level code %s finished - Nbr. products: %s' % (
llc, counter)
logger.info(log_msg)
mrp_lowest_llc = llc
logger.info('END LOW LEVEL CODE CALCULATION')
logger.info('End low level code calculation')
return mrp_lowest_llc
@api.model
def _adjust_mrp_applicable(self):
def _adjust_mrp_applicable(self, mrp_areas):
"""This method is meant to modify the products that are applicable
to MRP Multi level calculation
"""
return True
@api.model
def _calculate_mrp_applicable(self):
logger.info('CALCULATE MRP APPLICABLE')
self.env['product.mrp.area'].search([]).write(
def _calculate_mrp_applicable(self, mrp_areas):
logger.info('Start Calculate MRP Applicable')
domain = []
if mrp_areas:
domain += [('mrp_area_id', 'in', mrp_areas.ids)]
self.env['product.mrp.area'].search(domain).write(
{'mrp_applicable': False})
self.env['product.mrp.area'].search([
('product_id.type', '=', 'product'),
]).write({'mrp_applicable': True})
self._adjust_mrp_applicable()
counter = self.env['product.mrp.area'].search([
('mrp_applicable', '=', True)], count=True)
log_msg = 'END CALCULATE MRP APPLICABLE: %s' % counter
domain += [('product_id.type', '=', 'product')]
self.env['product.mrp.area'].search(domain).write(
{'mrp_applicable': True})
self._adjust_mrp_applicable(mrp_areas)
count_domain = [('mrp_applicable', '=', True)]
if mrp_areas:
count_domain += [('mrp_area_id', 'in', mrp_areas.ids)]
counter = self.env['product.mrp.area'].search(count_domain, count=True)
log_msg = 'End Calculate MRP Applicable: %s' % counter
logger.info(log_msg)
return True
@@ -484,11 +500,14 @@ class MultiLevelMrp(models.TransientModel):
return product_mrp_area.mrp_exclude
@api.model
def _mrp_initialisation(self):
logger.info('START MRP INITIALISATION')
mrp_areas = self.env['mrp.area'].search([])
def _mrp_initialisation(self, mrp_areas):
logger.info('Start MRP initialisation')
if not mrp_areas:
mrp_areas = self.env['mrp.area'].search([])
product_mrp_areas = self.env['product.mrp.area'].search([
('mrp_applicable', '=', True)])
('mrp_area_id', 'in', mrp_areas.ids),
('mrp_applicable', '=', True),
])
init_counter = 0
for mrp_area in mrp_areas:
for product_mrp_area in product_mrp_areas.filtered(
@@ -497,11 +516,11 @@ class MultiLevelMrp(models.TransientModel):
product_mrp_area.product_id, mrp_area):
continue
init_counter += 1
log_msg = 'MRP INIT: %s - %s ' % (
log_msg = 'MRP Init: %s - %s ' % (
init_counter, product_mrp_area.display_name)
logger.info(log_msg)
self._init_mrp_move(product_mrp_area)
logger.info('END MRP INITIALISATION')
logger.info('End MRP initialisation')
@api.model
def _init_mrp_move_grouped_demand(self, nbr_create, product_mrp_area):
@@ -555,11 +574,13 @@ class MultiLevelMrp(models.TransientModel):
return nbr_create
@api.model
def _mrp_calculation(self, mrp_lowest_llc):
logger.info('START MRP CALCULATION')
def _mrp_calculation(self, mrp_lowest_llc, mrp_areas):
logger.info('Start MRP calculation')
product_mrp_area_obj = self.env['product.mrp.area']
counter = 0
for mrp_area in self.env['mrp.area'].search([]):
if not mrp_areas:
mrp_areas = self.env['mrp.area'].search([])
for mrp_area in mrp_areas:
llc = 0
while mrp_lowest_llc > llc:
product_mrp_areas = product_mrp_area_obj.search(
@@ -606,11 +627,11 @@ class MultiLevelMrp(models.TransientModel):
onhand += qty_ordered
counter += 1
log_msg = 'MRP CALCULATION LLC %s FINISHED - NBR PRODUCTS: %s' % (
log_msg = 'MRP Calculation LLC %s Finished - Nbr. products: %s' % (
llc - 1, counter)
logger.info(log_msg)
logger.info('END MRP CALCULATION')
logger.info('Enb MRP calculation')
@api.model
def _get_demand_groups(self, product_mrp_area):
@@ -714,10 +735,12 @@ class MultiLevelMrp(models.TransientModel):
self.env['mrp.inventory'].create(mrp_inventory_data)
@api.model
def _mrp_final_process(self):
logger.info('START MRP FINAL PROCESS')
product_mrp_area_ids = self.env['product.mrp.area'].search([
('product_id.llc', '<', 9999)])
def _mrp_final_process(self, mrp_areas):
logger.info('Start MRP final process')
domain = [('product_id.llc', '<', 9999)]
if mrp_areas:
domain += [('mrp_area_id', 'in', mrp_areas.ids)]
product_mrp_area_ids = self.env['product.mrp.area'].search(domain)
for product_mrp_area in product_mrp_area_ids:
# Build the time-phased inventory
@@ -732,25 +755,13 @@ class MultiLevelMrp(models.TransientModel):
for move in moves:
qoh = qoh + move.mrp_qty
move.running_availability = qoh
# TODO: Possible clean up needed here
# nbr_actions = product_mrp_area.mrp_move_ids.filtered(
# lambda m: m.mrp_action != 'none')
# horizon_4w = fields.Date.to_string(
# date.today() + timedelta(weeks=4))
# nbr_actions_4w = nbr_actions.filtered(
# lambda m: m.mrp_action_date < horizon_4w)
# if nbr_actions:
# product_mrp_area.write({
# 'nbr_mrp_actions': len(nbr_actions),
# 'nbr_mrp_actions_4w': len(nbr_actions_4w),
# })
logger.info('END MRP FINAL PROCESS')
logger.info('End MRP final process')
@api.multi
def run_mrp_multi_level(self):
self._mrp_cleanup()
self._mrp_cleanup(self.mrp_area_ids)
mrp_lowest_llc = self._low_level_code_calculation()
self._calculate_mrp_applicable()
self._mrp_initialisation()
self._mrp_calculation(mrp_lowest_llc)
self._mrp_final_process()
self._calculate_mrp_applicable(self.mrp_area_ids)
self._mrp_initialisation(self.mrp_area_ids)
self._mrp_calculation(mrp_lowest_llc, self.mrp_area_ids)
self._mrp_final_process(self.mrp_area_ids)

View File

@@ -6,6 +6,9 @@
<field name="model">mrp.multi.level</field>
<field name="arch" type="xml">
<form string="Run MRP Multi Level">
<group>
<field name="mrp_area_ids" widget="many2many_tags" options="{'no_create': True}"/>
</group>
<footer>
<button name="run_mrp_multi_level" string="Run MRP" type="object" class="oe_highlight" />
or