Skip to content

Commit

Permalink
Merge pull request Vauxoo#1 from numerigraphe/8.0-stock_available_imm…
Browse files Browse the repository at this point in the history
…ediately2

Thank you @clonedagain  for your help.
  • Loading branch information
gfcapalbo committed Mar 17, 2015
2 parents 3bde295 + c7d0f18 commit e2b9ccb
Show file tree
Hide file tree
Showing 45 changed files with 1,412 additions and 155 deletions.
6 changes: 3 additions & 3 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@ python:
- "2.7"

env:
- VERSION="8.0" ODOO_REPO="odoo/odoo"
- VERSION="8.0" ODOO_REPO="OCA/OCB"
- VERSION="8.0" LINT_CHECK="1"
- VERSION="8.0" ODOO_REPO="odoo/odoo" LINT_CHECK="0"
- VERSION="8.0" ODOO_REPO="OCA/OCB" LINT_CHECK="0"

virtualenv:
system_site_packages: true
Expand All @@ -20,7 +21,6 @@ install:
- git clone https://github.com/OCA/stock-logistics-barcode -b ${VERSION} $HOME/stock-logistics-barcode

script:
- travis_run_flake8
- travis_run_tests

after_success:
Expand Down
2 changes: 1 addition & 1 deletion __unported__/base_product_merge/__openerp__.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
{
"name" : "Base Products Merge",
"version" : "1.0",
"author" : "Camptocamp",
"author" : "Camptocamp,Odoo Community Association (OCA)",
"category" : "Generic Modules/Base",
"description":"""
To merge 2 products, select them in the list view and execute the Action "Merge Products".
Expand Down
2 changes: 1 addition & 1 deletion __unported__/configurable_stock_level/__openerp__.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
##############################################################################
{'name' : 'name',
'version' : '0.1',
'author' : 'Camptocamp',
'author' : "Camptocamp,Odoo Community Association (OCA)",
'maintainer': 'Camptocamp',
'category': 'Warehouse',
'complexity': "normal", # easy, normal, expert
Expand Down
21 changes: 21 additions & 0 deletions __unported__/stock_available_mrp/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# -*- 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
35 changes: 35 additions & 0 deletions __unported__/stock_available_mrp/__openerp__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# -*- 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/>.
#
##############################################################################

{
'name': 'Consider the production potential is available to promise',
'version': '2.0',
"author": u"Numérigraphe,Odoo Community Association (OCA)",
'category': 'Hidden',
'depends': ['stock_available', 'mrp'],
'data': [
'product_view.xml',
],
'test': [
'test/potential_qty.yml',
],
'license': 'AGPL-3',
'installable': False
}
34 changes: 34 additions & 0 deletions __unported__/stock_available_mrp/i18n/fr.po
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# Translation of OpenERP Server.
# This file contains the translation of the following modules:
# * stock_available_mrp
#
msgid ""
msgstr ""
"Project-Id-Version: OpenERP Server 7.0\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2014-07-30 19:29+0000\n"
"PO-Revision-Date: 2014-07-30 19:29+0000\n"
"Last-Translator: <>\n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Plural-Forms: \n"

#. module: stock_available_mrp
#: field:product.product,potential_qty:0
msgid "Potential"
msgstr "Potentiel"

#. module: stock_available_mrp
#: code:_description:0
#: model:ir.model,name:stock_available_mrp.model_product_product
#, python-format
msgid "Product"
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."

34 changes: 34 additions & 0 deletions __unported__/stock_available_mrp/i18n/stock_available_mrp.pot
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# Translation of OpenERP Server.
# This file contains the translation of the following modules:
# * stock_available_mrp
#
msgid ""
msgstr ""
"Project-Id-Version: OpenERP Server 7.0\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2014-07-30 19:41+0000\n"
"PO-Revision-Date: 2014-07-30 19:41+0000\n"
"Last-Translator: <>\n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Plural-Forms: \n"

#. module: stock_available_mrp
#: field:product.product,potential_qty:0
msgid "Potential"
msgstr ""

#. module: stock_available_mrp
#: code:_description:0
#: model:ir.model,name:stock_available_mrp.model_product_product
#, python-format
msgid "Product"
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."
msgstr ""

126 changes: 126 additions & 0 deletions __unported__/stock_available_mrp/product.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
# -*- 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 SUPERUSER_ID
from openerp.osv import orm, fields
import openerp.addons.decimal_precision as dp


class product_product(orm.Model):
"""Add the computation for the stock available to promise"""
_inherit = 'product.product'

def _product_available(self, cr, uid, ids, field_names=None, arg=False,
context=None):
"""Quantity available to promise based on components at hand."""
# Compute the core quantities
res = super(product_product, self)._product_available(
cr, uid, ids, field_names=field_names, arg=arg, context=context)
# If we didn't get a field_names list, there's nothing to do
if field_names is None:
return res

if context is None:
context = {}
# Prepare an alternative context without 'uom', to avoid cross-category
# conversions when reading the available stock of components
if 'uom' in context:
context_wo_uom = context.copy()
del context_wo_uom['uom']
else:
context_wo_uom = context

# Compute the production capacity
if any([f in field_names
for f in ['potential_qty', 'immediately_usable_qty']]):
# Compute the potential qty from BoMs with components available
bom_obj = self.pool['mrp.bom']
to_uom = 'uom' in context and self.pool['product.uom'].browse(
cr, SUPERUSER_ID, context['uom'], context=context)

for product in self.browse(cr, uid, ids, context=context):
# _bom_find() returns a single BoM id.
# We will not check any other BoM for this product
bom_id = bom_obj._bom_find(cr, SUPERUSER_ID, product.id,
product.uom_id.id)
if bom_id:
min_qty = self._compute_potential_qty_from_bom(
cr, uid, bom_id, to_uom or product.uom_id,
context=context)

if 'potential_qty' in field_names:
res[product.id]['potential_qty'] += min_qty
if 'immediately_usable_qty' in field_names:
res[product.id]['immediately_usable_qty'] += min_qty

return res

def _compute_potential_qty_from_bom(self, cr, uid, bom_id, to_uom,
context=None):
"""Compute the potential qty from BoMs with components available"""
bom_obj = self.pool['mrp.bom']
uom_obj = self.pool['product.uom']
if context is None:
context = {}
if 'uom' in context:
context_wo_uom = context.copy()
del context_wo_uom['uom']
else:
context_wo_uom = context
min_qty = False
# Browse ignoring the UoM context to avoid cross-category conversions
bom = bom_obj.browse(
cr, uid, [bom_id], context=context_wo_uom)[0]

# store id of final product uom

for component in bom.bom_lines:
# qty available in BOM line's UoM
# XXX use context['uom'] instead?
stock_component_qty = uom_obj._compute_qty_obj(
cr, uid,
component.product_id.uom_id,
component.product_id.virtual_available,
component.product_uom)
# qty we can produce with this component, in the BoM's UoM
bom_uom_qty = (stock_component_qty // component.product_qty
) * bom.product_qty
# Convert back to the reporting default UoM
stock_product_uom_qty = uom_obj._compute_qty_obj(
cr, uid, bom.product_uom, bom_uom_qty,
to_uom)
if min_qty is False:
min_qty = stock_product_uom_qty
elif stock_product_uom_qty < min_qty:
min_qty = stock_product_uom_qty
if min_qty < 0.0:
min_qty = 0.0
return min_qty

_columns = {
'potential_qty': fields.function(
_product_available, method=True, multi='qty_available',
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, following a single level "
"of the Bills of Materials."),
}
19 changes: 19 additions & 0 deletions __unported__/stock_available_mrp/product_view.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<openerp>
<data>
<!-- Add the quantity available to promise in the product form -->
<record id="view_product_form_potential_qty" model="ir.ui.view">
<field name="name">product.form.potential_qty</field>
<field name="model">product.product</field>
<field name="type">form</field>
<field name="inherit_id" ref="stock.view_normal_procurement_locations_form" />
<field name="arch" type="xml">
<data>
<xpath expr="//field[@name='virtual_available']" position="after">
<field name="potential_qty"/>
</xpath>
</data>
</field>
</record>
</data>
</openerp>
70 changes: 70 additions & 0 deletions __unported__/stock_available_mrp/test/potential_qty.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
- Test the computation of the potential quantity on product_product_16, a product with several multi-line BoMs

- Create a UoM in the category of PCE
- !record {model: product.uom, id: thousand}:
name: Thousand
factor: 0.001
rounding: 0.0001
uom_type: bigger
category_id: product.product_uom_categ_unit

- Receive enough of the first component to run the BoM 1000x, and check that the potential is unchanged
- !python {model: mrp.bom}: |
bom = self.browse(
cr, uid,
self._bom_find(
cr, uid, ref('product.product_product_16'),
ref('product.product_uom_unit')))
assert len(bom.bom_lines)>1, "The test BoM has a single line, two or more are needed for the test"
initial_qty = bom.product_id.potential_qty
component = bom.bom_lines[0]
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"
self.pool['stock.move'].create(
cr, uid,
{
'name': 'Receive first component',
'product_id': component.product_id.id,
'product_qty': component.product_qty * 1000.0,
'product_uom': component.product_id.uom_id.id,
'location_id': ref('stock.stock_location_suppliers'),
'location_dest_id': ref('stock.stock_location_stock'),
'state': 'done',
})
# Re-read the potential quantity
bom.refresh()
new_qty = bom.product_id.potential_qty
assert new_qty == initial_qty, "Receiving a single component should not change the potential qty (%s instead of %s)" % (new_qty, initial_qty)

- Receive enough of all the components to run the BoM 1000x and check that the potential is correct
- !python {model: mrp.bom}: |
# Select a BoM for product_product_16
bom = self.browse(
cr, uid,
self._bom_find(
cr, uid, ref('product.product_product_16'),
ref('product.product_uom_unit')))
assert len(bom.bom_lines)>1, "The test BoM has a single line, two or more are needed for the test"
initial_qty = bom.product_id.potential_qty
for component in bom.bom_lines:
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"
self.pool['stock.move'].create(
cr, uid,
{
'name': 'Receive all components',
'product_id': component.product_id.id,
'product_qty': component.product_qty * 1000.0,
'product_uom': component.product_id.uom_id.id,
'location_id': ref('stock.stock_location_suppliers'),
'location_dest_id': ref('stock.stock_location_stock'),
'state': 'done',
})
# Re-read the potential quantity
bom.refresh()
new_qty = bom.product_id.potential_qty
right_qty = initial_qty + bom.product_qty * 1000.0
assert new_qty == right_qty, "The potential qty is incorrect after receiveing all the components (%s instead of %s)" % (new_qty, right_qty)
# Re-read the potential quantity with a different UoM in the context
new_qty = self.browse(
cr, uid, bom.id, context={'uom': ref('thousand')}).product_id.potential_qty
right_qty = initial_qty / 1000.0 + bom.product_qty
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)
Loading

0 comments on commit e2b9ccb

Please sign in to comment.