Skip to content

Commit

Permalink
[MIGR] stock_available_mrp: migrate to v8
Browse files Browse the repository at this point in the history
Now computes potential quantities for both product templates and variants. To keep the code simple, only the biggest potential of any single variant is accounted for in the template's potential.
Now takes all levels of phantom BoM into account, respects validity dates etc. thanks to the use of the standard method _bom_explode, as suggested by @gdgellatly in OCA#5 (comment)
Tests rewritten in python and improved.
Code now adheres to new file/manifest conventions.
  • Loading branch information
Lionel Sausin committed Nov 9, 2015
1 parent d32921d commit 9b35be2
Show file tree
Hide file tree
Showing 16 changed files with 431 additions and 226 deletions.
16 changes: 8 additions & 8 deletions stock_available/models/res_config.py
Expand Up @@ -39,11 +39,11 @@ class StockConfig(models.TransientModel):
# "If the modules sale and sale_delivery_date are not "
# "installed, this will install them too")

# module_stock_available_mrp = fields.Boolean(
# string='Include the production potential',
# help="This will add the quantities of goods that can be "
# "immediately manufactured, to the quantities available to "
# "promise.\n"
# "This installs the module stock_available_mrp.\n"
# "If the module mrp is not installed, this will install it "
# "too")
module_stock_available_mrp = fields.Boolean(
string='Include the production potential',
help="This will add the quantities of goods that can be "
"immediately manufactured, to the quantities available to "
"promise.\n"
"This installs the module stock_available_mrp.\n"
"If the module mrp is not installed, this will install it "
"too")
4 changes: 2 additions & 2 deletions stock_available/views/res_config_view.xml
Expand Up @@ -19,10 +19,10 @@
<field name="module_stock_available_sale" class="oe_inline" />
<label for="module_stock_available_sale" />
</div> -->
<!-- <div>
<div>
<field name="module_stock_available_mrp" class="oe_inline" />
<label for="module_stock_available_mrp" />
</div> -->
</div>
</div>
</group>
</xpath>
Expand Down
25 changes: 20 additions & 5 deletions stock_available_mrp/README.rst
@@ -1,27 +1,41 @@
Consider the production potential is available to promise
=========================================================

This module takes the potential quantities available for Products in account in
This module takes the potential quantities available for Products into account in
the quantity available to promise, where the "Potential quantity" is the
quantity that can be manufactured with the components immediately at hand.

Known issues
============
Known issues / Roadmap
======================

Known issues
------------
The manufacturing delays are not taken into account : this module assumes that
if you have components in stock goods, you can manufacture finished goods
quickly enough.
To avoid overestimating, **only the first level** of Bill of Materials is

As a consequence, and to avoid overestimating, **only the first level** of Bill of Materials is
considered.
However Sets (a.k.a "phantom" BoMs) are taken into account: if a component must be replaced with a set, it's the stock of the set's product which will decide the potential.

If a product has several variants, only the variant with the biggest potential will be taken into account when reporting the production potential.
For example, even if you actually have enough components to make 10 iPads 16Go AND 42 iPads 32Go, we'll consider that you can promise only 42 iPads.

Removed features
----------------
Previous versions of this module used to let programmers demand to get the potential quantity in an arbitrary Unit of Measure using the `context`. This feature was present in the standard computations too until v8.0, but it has been dropped from the standard from v8.0 on.
For the sake of consistency the potential quantity is now always reported in the product's main Unit of Measure too.

Roadmap
-------

Possible improvements for future versions:
* take manufacturing delays into account: we should not promis goods to customers if they want them delivered earlier that we can make them
* include all levels of BoM, using `bom_explode`. @gdgellatly gave an example
of how to do it here: https://github.com/OCA/stock-logistics-warehouse/pull/5#issuecomment-66902191
Ideally, we will want to take manufacturing delays into account: we can't
promiss goods to customers if they want them delivered earlier that we can
make them
* Compute the quantity of finished product that can be made directly on each Bill of Material: this would be useful for production managers, and may make the computations faster by avoiding to compute the same BoM several times when several variants share the same BoM
* add an option (probably as a sub-module) to consider all raw materials as
available if they can be bought from the suppliers in time for the
manufacturing.
Expand All @@ -33,6 +47,7 @@ Contributors
------------
* Loïc Bellier (Numérigraphe) <lb@numerigraphe.com>
* Lionel Sausin (Numérigraphe) <ls@numerigraphe.com>
* many thanks to Graeme Gellatly for his advice and code review

Maintainer
----------
Expand Down
2 changes: 1 addition & 1 deletion stock_available_mrp/__init__.py
Expand Up @@ -18,4 +18,4 @@
#
##############################################################################

from . import product
from . import models
10 changes: 5 additions & 5 deletions stock_available_mrp/__openerp__.py
Expand Up @@ -20,16 +20,16 @@

{
'name': 'Consider the production potential is available to promise',
'version': '2.0',
'version': '8.0.3.0.0',
"author": u"Numérigraphe,Odoo Community Association (OCA)",
'category': 'Hidden',
'depends': ['stock_available', 'mrp'],
'data': [
'product_view.xml',
'views/product_template_view.xml',
],
'test': [
'test/potential_qty.yml',
'demo': [
'demo/mrp_bom.yml',
],
'license': 'AGPL-3',
'installable': False
'installable': True,
}
28 changes: 28 additions & 0 deletions stock_available_mrp/demo/mrp_bom.yml
@@ -0,0 +1,28 @@
- Create a UoM in the category of PCE
- !record {model: product.uom, id: thousand}:
name: Thousand
factor: 0.001
rounding: 0.001
uom_type: bigger
category_id: product.product_uom_categ_unit

- Add a BOM whereby 0.042K "RAM SR2" can be replaced with 13 dozens "HDD-SH1" + 8 CPUa8 with 50% efficiency. This lets us test UoM conversions for the finished product and the raw materials, as well as the unfolding of phantom BoMs
- !record {model: mrp.bom, id: sr2_from_hdd}:
name: RAM SR2 made from HDD-SH1
product_id: product.product_product_14
product_tmpl_id: product.product_product_14_product_template
product_uom: thousand
product_qty: 0.042
type: phantom
sequence: -1
product_efficiency: 0.5
- !record {model: mrp.bom.line, id: sr2_from_hdd_line1}:
bom_id: sr2_from_hdd
product_id: product.product_product_18
product_qty: 13
product_uom: product.product_uom_dozen
- !record {model: mrp.bom.line, id: sr2_from_hdd_line2}:
bom_id: sr2_from_hdd
product_id: product.product_product_23
product_qty: 8
product_uom: product.product_uom_unit
5 changes: 2 additions & 3 deletions stock_available_mrp/i18n/fr.po
Expand Up @@ -29,6 +29,5 @@ msgstr "Article"

#. module: stock_available_mrp
#: help:product.product,potential_qty:0
msgid "Quantity of this Product that could be produced using the materials already at hand, following a single level of the Bills of Materials."
msgstr "Quantité de cet article que l'on pourrait produire en utilisant les produits déjà disponibles, en suivant un seul niveau de nomenclature."

msgid "Quantity of this Product that could be produced using the materials already at hand."
msgstr "Quantité de cet article que l'on pourrait produire en utilisant les produits déjà disponibles."
3 changes: 1 addition & 2 deletions stock_available_mrp/i18n/stock_available_mrp.pot
Expand Up @@ -29,6 +29,5 @@ msgstr ""

#. module: stock_available_mrp
#: help:product.product,potential_qty:0
msgid "Quantity of this Product that could be produced using the materials already at hand, following a single level of the Bills of Materials."
msgid "Quantity of this Product that could be produced using the materials already at hand."
msgstr ""

22 changes: 22 additions & 0 deletions stock_available_mrp/models/__init__.py
@@ -0,0 +1,22 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# This module is copyright (C) 2014 Numérigraphe SARL. All Rights Reserved.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################

from . import product_product
from . import product_template
74 changes: 74 additions & 0 deletions stock_available_mrp/models/product_product.py
@@ -0,0 +1,74 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# This module is copyright (C) 2014 Numérigraphe SARL. All Rights Reserved.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################

from collections import Counter

from openerp import models, fields, api
from openerp.addons import decimal_precision as dp


class ProductProduct(models.Model):
_inherit = 'product.product'

potential_qty = fields.Float(
compute='_get_potential_qty',
type='float',
digits_compute=dp.get_precision('Product Unit of Measure'),
string='Potential',
help="Quantity of this Product that could be produced using "
"the materials already at hand.")

@api.multi
@api.depends('potential_qty')
def _immediately_usable_qty(self):
"""Add the potential quantity to the quantity available to promise.
This is the same implementation as for templates."""
super(ProductProduct, self)._immediately_usable_qty()
for product in self:
product.immediately_usable_qty += product.potential_qty

@api.multi
def _get_potential_qty(self):
"""Compute the potential qty based on the available components."""
# Browse the BOMs as superuser to bypass access rights
bom_obj = self.env['mrp.bom'].sudo()

for product in self:
bom_id = bom_obj._bom_find(product_id=product.id)
if not bom_id:
product.potential_qty = 0.0
continue

# Need by product (same product can be in many BOM lines/levels)
component_needs = Counter()
for component in bom_obj._bom_explode(bom_obj.browse(bom_id),
product, 1.0,)[0]:
component_needs += Counter(
{component['product_id']: component['product_qty']})
if not component_needs:
# The BoM has no line we can use
product.potential_qty = 0.0
continue

# Find the lowest quantity we can make with the stock at hand
product.potential_qty = min(
[self.browse(component_id).qty_available // need
for component_id, need in component_needs.items()])
62 changes: 62 additions & 0 deletions stock_available_mrp/models/product_template.py
@@ -0,0 +1,62 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# This module is copyright (C) 2014 Numérigraphe SARL. All Rights Reserved.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################

from openerp import models, fields, api
from openerp.addons import decimal_precision as dp


class ProductTemplate(models.Model):
_inherit = 'product.template'

potential_qty = fields.Float(
compute='_get_potential_qty',
type='float',
digits_compute=dp.get_precision('Product Unit of Measure'),
string='Potential',
help="Quantity of this Product that could be produced using "
"the materials already at hand. "
"If the product has several variants, this will be the biggest "
"quantity that can be made for a any single variant.")

@api.multi
@api.depends('potential_qty')
def _immediately_usable_qty(self):
"""Add the potential quantity to the quantity available to promise.
This is the same implementation as for variants."""
super(ProductTemplate, self)._immediately_usable_qty()
for tmpl in self:
tmpl.immediately_usable_qty += tmpl.potential_qty

@api.multi
@api.depends('product_variant_ids.potential_qty')
def _get_potential_qty(self):
"""Compute the potential as the max of all the variants's potential.
We can't add the potential of variants: if they share components we
may not be able to make all the variants.
So we set the arbitrary rule that we can promise up to the biggest
variant's potential.
"""
for tmpl in self:
if not tmpl.product_variant_ids:
continue
tmpl.potential_qty = max(
[v.potential_qty for v in tmpl.product_variant_ids])

0 comments on commit 9b35be2

Please sign in to comment.