Merge lp:~numerigraphe-team/stock-logistic-warehouse/7.0-add-stock-available-mrp into lp:stock-logistic-warehouse

Proposed by Lionel Sausin - Initiatives/Numérigraphe
Status: Rejected
Rejected by: Yannick Vaucher @ Camptocamp
Proposed branch: lp:~numerigraphe-team/stock-logistic-warehouse/7.0-add-stock-available-mrp
Merge into: lp:stock-logistic-warehouse
Prerequisite: lp:~numerigraphe-team/stock-logistic-warehouse/7.0-add-stock-available-sale
Diff against target: 326 lines (+280/-0)
7 files modified
stock_available/res_config.py (+8/-0)
stock_available/res_config_view.xml (+4/-0)
stock_available_mrp/__init__.py (+21/-0)
stock_available_mrp/__openerp__.py (+39/-0)
stock_available_mrp/product.py (+121/-0)
stock_available_mrp/product_view.xml (+19/-0)
stock_available_mrp/test/potential_qty.yml (+68/-0)
To merge this branch: bzr merge lp:~numerigraphe-team/stock-logistic-warehouse/7.0-add-stock-available-mrp
Reviewer Review Type Date Requested Status
Alexandre Fayolle - camptocamp Needs Resubmitting
Review via email: mp+220764@code.launchpad.net

Description of the change

Add stock_available_mrp: take immediate manufacturing capability into account in the stock quantity available to promise

This branch builds upon lp:~numerigraphe-team/stock-logistic-warehouse/7.0-add-stock-available, which adds a generic module to compute the stock available to promise in a configurable way.

It also includes lp:~numerigraphe-team/stock-logistic-warehouse/7.0-add-stock-available-sale, which adds another //unrelated// implementation. This is only to avoid merge conflicts in case both are accepted, but they are independent works I can rebase this branch if needed.

Module by Loïc Bellier with contributions from your humble servant.

To post a comment you must log in.
Revision history for this message
Alexandre Fayolle - camptocamp (alexandre-fayolle-c2c) wrote :

The source code management for this project has been moved to https://github.com/OCA/stock-logistics-warehouse

Could you resubmit this MP on the new site?

review: Needs Resubmitting
Revision history for this message
Lionel Sausin - Initiatives/Numérigraphe (ls-initiatives) wrote :

Unmerged revisions

37. By Numérigraphe

[ADD] stock_available_mrp: take immediate manufaturing capability into account in the stock quantity available to promise

36. By Numérigraphe

[ADD] stock_available_sale: take sale quotations into account in the stock quantity available to promise

35. By Numérigraphe

[ADD] stock_available: generic module to compute the stock quantity available to promise using several implementations. Make stock_available_immediatly the first configurable implementation

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'stock_available/res_config.py'
--- stock_available/res_config.py 2014-05-23 07:58:49 +0000
+++ stock_available/res_config.py 2014-05-23 07:58:49 +0000
@@ -38,4 +38,12 @@
38 "This installs the modules stock_available_sale.\n"38 "This installs the modules stock_available_sale.\n"
39 "If the modules sale and sale_delivery_date are not "39 "If the modules sale and sale_delivery_date are not "
40 "installed, this will install them too"),40 "installed, this will install them too"),
41 'module_stock_available_mrp': fields.boolean(
42 'Include the production potential',
43 help="This will add the quantities of goods that can be"
44 "immediately manufactured, to the quantities available to"
45 "promise.\n"
46 "This installs the module stock_available_mrp.\n"
47 "If the module mrp is not installed, this will install it "
48 "too"),
41 }49 }
4250
=== modified file 'stock_available/res_config_view.xml'
--- stock_available/res_config_view.xml 2014-05-23 07:58:49 +0000
+++ stock_available/res_config_view.xml 2014-05-23 07:58:49 +0000
@@ -19,6 +19,10 @@
19 <field name="module_stock_available_sale" class="oe_inline" />19 <field name="module_stock_available_sale" class="oe_inline" />
20 <label for="module_stock_available_sale" />20 <label for="module_stock_available_sale" />
21 </div>21 </div>
22 <div>
23 <field name="module_stock_available_mrp" class="oe_inline" />
24 <label for="module_stock_available_mrp" />
25 </div>
22 </div>26 </div>
23 </group>27 </group>
24 </xpath>28 </xpath>
2529
=== added directory 'stock_available_mrp'
=== added file 'stock_available_mrp/__init__.py'
--- stock_available_mrp/__init__.py 1970-01-01 00:00:00 +0000
+++ stock_available_mrp/__init__.py 2014-05-23 07:58:49 +0000
@@ -0,0 +1,21 @@
1# -*- coding: utf-8 -*-
2##############################################################################
3#
4# This module is copyright (C) 2014 Numérigraphe SARL. All Rights Reserved.
5#
6# This program is free software: you can redistribute it and/or modify
7# it under the terms of the GNU Affero General Public License as
8# published by the Free Software Foundation, either version 3 of the
9# License, or (at your option) any later version.
10#
11# This program is distributed in the hope that it will be useful,
12# but WITHOUT ANY WARRANTY; without even the implied warranty of
13# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14# GNU Affero General Public License for more details.
15#
16# You should have received a copy of the GNU Affero General Public License
17# along with this program. If not, see <http://www.gnu.org/licenses/>.
18#
19##############################################################################
20
21from . import product
022
=== added file 'stock_available_mrp/__openerp__.py'
--- stock_available_mrp/__openerp__.py 1970-01-01 00:00:00 +0000
+++ stock_available_mrp/__openerp__.py 2014-05-23 07:58:49 +0000
@@ -0,0 +1,39 @@
1# -*- coding: utf-8 -*-
2##############################################################################
3#
4# This module is copyright (C) 2014 Numérigraphe SARL. All Rights Reserved.
5#
6# This program is free software: you can redistribute it and/or modify
7# it under the terms of the GNU Affero General Public License as
8# published by the Free Software Foundation, either version 3 of the
9# License, or (at your option) any later version.
10#
11# This program is distributed in the hope that it will be useful,
12# but WITHOUT ANY WARRANTY; without even the implied warranty of
13# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14# GNU Affero General Public License for more details.
15#
16# You should have received a copy of the GNU Affero General Public License
17# along with this program. If not, see <http://www.gnu.org/licenses/>.
18#
19##############################################################################
20
21{
22 'name': 'Consider the production potential is available to promise',
23 'version': '2.0',
24 'author': u'Numérigraphe',
25 'category': 'Hidden',
26 'depends': ['stock_available', 'mrp'],
27 'description': """
28This module takes the potential quantities available for Products in account in
29the quantity available to promise, where the "Potential quantity" is the
30quantity that can be manufactured with the components immediately at hand,
31following a single level of Bill of Materials.""",
32 'data': [
33 'product_view.xml',
34 ],
35 'test': [
36 'test/potential_qty.yml',
37 ],
38 'license': 'AGPL-3',
39}
040
=== added file 'stock_available_mrp/product.py'
--- stock_available_mrp/product.py 1970-01-01 00:00:00 +0000
+++ stock_available_mrp/product.py 2014-05-23 07:58:49 +0000
@@ -0,0 +1,121 @@
1# -*- coding: utf-8 -*-
2##############################################################################
3#
4# This module is copyright (C) 2014 Numérigraphe SARL. All Rights Reserved.
5#
6# This program is free software: you can redistribute it and/or modify
7# it under the terms of the GNU Affero General Public License as
8# published by the Free Software Foundation, either version 3 of the
9# License, or (at your option) any later version.
10#
11# This program is distributed in the hope that it will be useful,
12# but WITHOUT ANY WARRANTY; without even the implied warranty of
13# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14# GNU Affero General Public License for more details.
15#
16# You should have received a copy of the GNU Affero General Public License
17# along with this program. If not, see <http://www.gnu.org/licenses/>.
18#
19##############################################################################
20
21from openerp.osv import orm, fields
22import decimal_precision as dp
23
24
25class product_product(orm.Model):
26 """Add the computation for the stock available to promise"""
27 _inherit = 'product.product'
28
29 def _product_available(self, cr, uid, ids, field_names=None, arg=False,
30 context=None):
31 """Compute the potential quantities available to promise."""
32 # Check the context
33 if context is None:
34 context = {}
35 # Prepare an alternative context without 'uom', to avoid cross-category
36 # conversions when reading the available stock of components
37 if 'uom' in context:
38 context_wo_uom = context.copy()
39 del context_wo_uom['uom']
40 else:
41 context_wo_uom = context
42
43 if field_names is None:
44 field_names = []
45
46 # Compute the core quantities
47 res = super(product_product, self)._product_available(
48 cr, uid, ids, field_names=field_names, arg=arg, context=context)
49
50 # Compute the quantities quoted/potential/available to promise
51 if ('potential_qty' in field_names):
52 # Compute the potential qty from BoMs with components available
53 bom_obj = self.pool['mrp.bom']
54 to_uom = 'uom' in context and self.pool['product.uom'].browse(
55 cr, uid, context['uom'], context=context)
56
57 for product in self.browse(cr, uid, ids, context=context):
58 # _bom_find() returns a single BoM id.
59 # We will not check any other BoM for this product
60 bom_id = bom_obj._bom_find(cr, uid, product.id,
61 product.uom_id.id)
62 if bom_id:
63 min_qty = self._compute_potential_qty_from_bom(
64 cr, uid, bom_id, to_uom or product.uom_id,
65 context=context)
66
67 res[product.id]['potential_qty'] += min_qty
68 if ('immediately_usable_qty' in field_names):
69 res[product.id]['immediately_usable_qty'] += min_qty
70
71 return self._update_virtual_available(cr, uid, res, context=context)
72
73 def _compute_potential_qty_from_bom(self, cr, uid, bom_id, to_uom,
74 context):
75 """Compute the potential qty from BoMs with components available"""
76 bom_obj = self.pool['mrp.bom']
77 uom_obj = self.pool['product.uom']
78 if 'uom' in context:
79 context_wo_uom = context.copy()
80 del context_wo_uom['uom']
81 else:
82 context_wo_uom = context
83 min_qty = False
84 # Browse ignoring the UoM context to avoid cross-category conversions
85 final_product = bom_obj.browse(
86 cr, uid, [bom_id], context=context_wo_uom)[0]
87
88 # store id of final product uom
89
90 for component in final_product.bom_lines:
91 # qty available in BOM line's UoM
92 # XXX use context['uom'] instead?
93 stock_component_qty = uom_obj._compute_qty_obj(
94 cr, uid,
95 component.product_id.uom_id,
96 component.product_id.virtual_available,
97 component.product_uom)
98 # qty we can produce with this component, in the BoM's UoM
99 recipe_uom_qty = (stock_component_qty // component.product_qty
100 ) * final_product.product_qty
101 # Convert back to the reporting default UoM
102 stock_product_uom_qty = uom_obj._compute_qty_obj(
103 cr, uid, final_product.product_uom, recipe_uom_qty, to_uom)
104 if min_qty is False:
105 min_qty = stock_product_uom_qty
106 elif stock_product_uom_qty < min_qty:
107 min_qty = stock_product_uom_qty
108 if min_qty < 0.0:
109 min_qty = 0.0
110 return min_qty
111
112 _columns = {
113 'potential_qty': fields.function(
114 _product_available, method=True, multi='qty_available',
115 type='float',
116 digits_compute=dp.get_precision('Product Unit of Measure'),
117 string='Potential',
118 help="Quantity of this Product that could be produced using "
119 "the materials already at hand, following a single level"
120 "of the Bills of Materials."),
121 }
0122
=== added file 'stock_available_mrp/product_view.xml'
--- stock_available_mrp/product_view.xml 1970-01-01 00:00:00 +0000
+++ stock_available_mrp/product_view.xml 2014-05-23 07:58:49 +0000
@@ -0,0 +1,19 @@
1<?xml version="1.0" encoding="UTF-8"?>
2<openerp>
3 <data>
4 <!-- Add the quantity available to promise in the product form -->
5 <record id="view_product_form_potential_qty" model="ir.ui.view">
6 <field name="name">product.form.potential_qty</field>
7 <field name="model">product.product</field>
8 <field name="type">form</field>
9 <field name="inherit_id" ref="stock.view_normal_procurement_locations_form" />
10 <field name="arch" type="xml">
11 <data>
12 <xpath expr="//field[@name='virtual_available']" position="after">
13 <field name="potential_qty"/>
14 </xpath>
15 </data>
16 </field>
17 </record>
18 </data>
19</openerp>
020
=== added directory 'stock_available_mrp/test'
=== added file 'stock_available_mrp/test/potential_qty.yml'
--- stock_available_mrp/test/potential_qty.yml 1970-01-01 00:00:00 +0000
+++ stock_available_mrp/test/potential_qty.yml 2014-05-23 07:58:49 +0000
@@ -0,0 +1,68 @@
1- Test the computation of the potential quantity on product_product_16, a product with several multi-line BoMs
2
3- Create a UoM in the category of PCE
4- !record {model: product.uom, id: thousand}:
5 name: Thousand
6 factor: 0.001
7 rounding: 0.0001
8 uom_type: bigger
9 category_id: product.product_uom_categ_unit
10
11- Receive enough of the first component to run the BoM 1000x, and check that the potential is unchanged
12- !python {model: mrp.bom}: |
13 bom = self.browse(
14 cr, uid,
15 self._bom_find(
16 cr, uid, ref('product.product_product_16'),
17 ref('product.product_uom_unit')))
18 assert len(bom.bom_lines)>1, "The test BoM has a single line, two or more are needed for the test"
19 initial_qty = bom.product_id.potential_qty
20 component = bom.bom_lines[0]
21 assert component.product_uom.category_id.id == ref('product.product_uom_categ_unit'), "The first component's UoM is in the wrong category can't test"
22 self.pool['stock.move'].create(
23 cr, uid,
24 {
25 'name': 'Receive first component',
26 'product_id': component.product_id.id,
27 'product_qty': component.product_qty * 1000.0,
28 'product_uom': component.product_id.uom_id.id,
29 'location_id': ref('stock.stock_location_suppliers'),
30 'location_dest_id': ref('stock.stock_location_stock'),
31 'state': 'done',
32 })
33 # Re-read the potential quantity
34 new_qty = self.browse(cr, uid, bom.id).product_id.potential_qty
35 assert new_qty == initial_qty, "Receiving a single component should not change the potential qty (%s instead of %s)" % (new_qty, initial_qty)
36
37- Receive enough of all the components to run the BoM 1000x and check that the potential is correct
38- !python {model: mrp.bom}: |
39 # Select a BoM for product_product_16
40 bom = self.browse(
41 cr, uid,
42 self._bom_find(
43 cr, uid, ref('product.product_product_16'),
44 ref('product.product_uom_unit')))
45 assert len(bom.bom_lines)>1, "The test BoM has a single line, two or more are needed for the test"
46 initial_qty = bom.product_id.potential_qty
47 for component in bom.bom_lines:
48 assert component.product_uom.category_id.id == ref('product.product_uom_categ_unit'), "The first component's UoM is in the wrong category can't test"
49 x = {
50 'name': 'Receive all components',
51 'product_id': component.product_id.id,
52 'product_qty': component.product_qty * 1000.0,
53 'product_uom': component.product_id.uom_id.id,
54 'location_id': ref('stock.stock_location_suppliers'),
55 'location_dest_id': ref('stock.stock_location_stock'),
56 'state': 'done',
57 }
58 self.pool['stock.move'].create(
59 cr, uid,x)
60 # Re-read the potential quantity
61 new_qty = self.browse(cr, uid, bom.id).product_id.potential_qty
62 right_qty = initial_qty + bom.product_qty * 1000.0
63 assert new_qty == right_qty, "The potential qty is incorrect after receiveing all the components (%s instead of %s)" % (new_qty, right_qty)
64 # Re-read the potential quantity with a different UoM in the context
65 new_qty = self.browse(
66 cr, uid, bom.id, context={'uom': ref('thousand')}).product_id.potential_qty
67 right_qty = initial_qty / 1000.0 + bom.product_qty
68 assert abs(new_qty - right_qty) < 0.0001, "The potential qty is incorrect with another UoM in the context (%s instead of %s)" % (new_qty, right_qty)

Subscribers

People subscribed via source and target branches