diff --git a/stock_available/__init__.py b/stock_available/__init__.py
index 6dff1269a..036bf665c 100644
--- a/stock_available/__init__.py
+++ b/stock_available/__init__.py
@@ -18,5 +18,4 @@
#
##############################################################################
-from . import product
-from . import res_config
+from . import models
diff --git a/stock_available/__openerp__.py b/stock_available/__openerp__.py
index 608e6b8f4..027c5c02a 100644
--- a/stock_available/__openerp__.py
+++ b/stock_available/__openerp__.py
@@ -26,7 +26,8 @@
'depends': ['stock'],
'license': 'AGPL-3',
'data': [
- 'product_view.xml',
- 'res_config_view.xml',
- ]
+ 'views/product_template_view.xml',
+ 'views/product_product_view.xml',
+ 'views/res_config_view.xml',
+ ],
}
diff --git a/stock_available/models/__init__.py b/stock_available/models/__init__.py
new file mode 100644
index 000000000..bf26a289f
--- /dev/null
+++ b/stock_available/models/__init__.py
@@ -0,0 +1,23 @@
+# -*- 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 .
+#
+##############################################################################
+
+from . import product_template
+from . import product_product
+from . import res_config
diff --git a/stock_available/product.py b/stock_available/models/product_product.py
similarity index 54%
rename from stock_available/product.py
rename to stock_available/models/product_product.py
index 2a0f6c598..7335f641a 100644
--- a/stock_available/product.py
+++ b/stock_available/models/product_product.py
@@ -22,45 +22,29 @@ from openerp import models, fields, api
from openerp.addons import decimal_precision as dp
-class ProductTemplate(models.Model):
+class ProductProduct(models.Model):
"""Add a field for the stock available to promise.
Useful implementations need to be installed through the Settings menu or by
installing one of the modules stock_available_*
"""
- _inherit = 'product.template'
+ _inherit = 'product.product'
- # immediately usable quantity caluculated with the quant method
- @api.multi
+ @api.one
@api.depends('virtual_available')
def _immediately_usable_qty(self):
- stock_location_obj = self.env['stock.location']
- internal_locations = stock_location_obj.search([
- ('usage', '=', 'internal')])
- sublocations = self.env['stock.location']
- for location in internal_locations:
- sublocations += stock_location_obj.search(
- [('id', 'child_of', location.id)])
- for product_template in self:
- products = self.env['product.product'].search([
- ('product_tmpl_id', '=', product_template.id)])
- quant_obj = self.env['stock.quant']
- quants = quant_obj.search([
- ('location_id', 'in', sublocations.ids),
- ('product_id', 'in', products.ids),
- ('reservation_id', '=', False)])
- availability = 0
- if quants:
- for quant in quants:
- availability += quant.qty
- product_template.immediately_usable_qty = availability
+ """No-op implementation of the stock available to promise.
+
+ By default, available to promise = forecasted quantity.
+
+ Must be overridden by another module that actually implement
+ computations."""
+ self.immediately_usable_qty = self.virtual_available
immediately_usable_qty = fields.Float(
digits=dp.get_precision('Product Unit of Measure'),
compute='_immediately_usable_qty',
- string='Available to promise (quant calculation)',
+ string='Available to promise',
help="Stock for this Product that can be safely proposed "
"for sale to Customers.\n"
"The definition of this value can be configured to suit "
- "your needs , this number is obtained by using the new odoo 8 "
- "quants, so it gives us the actual current quants minus reserved"
- "quants")
+ "your needs")
diff --git a/stock_available/models/product_template.py b/stock_available/models/product_template.py
new file mode 100644
index 000000000..fe55ecc47
--- /dev/null
+++ b/stock_available/models/product_template.py
@@ -0,0 +1,42 @@
+# -*- 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 .
+#
+##############################################################################
+
+from openerp import models, fields, api
+from openerp.addons import decimal_precision as dp
+
+
+class ProductTemplate(models.Model):
+ _inherit = 'product.template'
+
+ @api.one
+ @api.depends('virtual_available')
+ def _immediately_usable_qty(self):
+ """Compute the quantity using all the variants"""
+ self.immediately_usable_qty = sum(
+ [v.immediately_usable_qty for v in self.product_variant_ids])
+
+ immediately_usable_qty = fields.Float(
+ digits=dp.get_precision('Product Unit of Measure'),
+ compute='_immediately_usable_qty',
+ string='Available to promise',
+ help="Stock for this Product that can be safely proposed "
+ "for sale to Customers.\n"
+ "The definition of this value can be configured to suit "
+ "your needs")
diff --git a/stock_available/res_config.py b/stock_available/models/res_config.py
similarity index 100%
rename from stock_available/res_config.py
rename to stock_available/models/res_config.py
diff --git a/stock_available/tests/test_stock_available.py b/stock_available/tests/test_stock_available.py
deleted file mode 100644
index 2c46f6460..000000000
--- a/stock_available/tests/test_stock_available.py
+++ /dev/null
@@ -1,120 +0,0 @@
-# -*- coding: utf-8 -*-
-##############################################################################
-#
-# Copyright (C) 2015 Therp BV
-#
-# 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 .
-#
-##############################################################################
-from openerp.tests.common import TransactionCase
-
-
-class testStockLogisticsWarehouse(TransactionCase):
-
- def test01_stock_levels(self):
- """checking that immediately_usable_qty actually reflects \
-the variations in stock, both on product and template"""
- moveObj = self.env['stock.move']
- productObj = self.env['product.product']
- templateObj = self.env['product.template']
- supplier_location = self.env.ref('stock.stock_location_suppliers')
- stock_location = self.env.ref('stock.stock_location_stock')
- customer_location = self.env.ref('stock.stock_location_customers')
- uom_unit = self.env.ref('product.product_uom_unit')
-
- # Create product template
- templateAB = templateObj.create(
- {'name': 'templAB',
- 'uom_id': uom_unit.id,
- })
-
- # Create product A and B
- productA = productObj.create(
- {'name': 'product A',
- 'standard_price': 1,
- 'type': 'product',
- 'uom_id': uom_unit.id,
- 'default_code': 'A',
- 'product_tmpl_id': templateAB.id,
- })
-
- productB = productObj.create(
- {'name': 'product B',
- 'standard_price': 1,
- 'type': 'product',
- 'uom_id': uom_unit.id,
- 'default_code': 'B',
- 'product_tmpl_id': templateAB.id,
- })
-
- # Create a stock move from INCOMING to STOCK
- stockMoveInA = moveObj.create(
- {'location_id': supplier_location.id,
- 'location_dest_id': stock_location.id,
- 'name': 'MOVE INCOMING -> STOCK ',
- 'product_id': productA.id,
- 'product_uom': productA.uom_id.id,
- 'product_uom_qty': 2,
- })
-
- stockMoveInB = moveObj.create(
- {'location_id': supplier_location.id,
- 'location_dest_id': stock_location.id,
- 'name': 'MOVE INCOMING -> STOCK ',
- 'product_id': productB.id,
- 'product_uom': productB.uom_id.id,
- 'product_uom_qty': 3,
- })
-
- def compare_product_usable_qty(product, value):
- # Refresh, because the function field is not recalculated between
- # transactions
- product.refresh()
- self.assertEqual(product.immediately_usable_qty, value)
-
- compare_product_usable_qty(productA, 0)
- compare_product_usable_qty(templateAB, 0)
-
- stockMoveInA.action_confirm()
- compare_product_usable_qty(productA, 0)
- compare_product_usable_qty(templateAB, 0)
-
- stockMoveInA.action_assign()
- compare_product_usable_qty(productA, 0)
- compare_product_usable_qty(templateAB, 0)
-
- stockMoveInA.action_done()
- compare_product_usable_qty(productA, 2)
- compare_product_usable_qty(templateAB, 2)
-
- # will directly trigger action_done on productB
- stockMoveInB.action_done()
- compare_product_usable_qty(productA, 2)
- compare_product_usable_qty(productB, 3)
- compare_product_usable_qty(templateAB, 5)
-
- # Create a stock move from STOCK to CUSTOMER
- stockMoveOutA = moveObj.create(
- {'location_id': stock_location.id,
- 'location_dest_id': customer_location.id,
- 'name': ' STOCK --> CUSTOMER ',
- 'product_id': productA.id,
- 'product_uom': productA.uom_id.id,
- 'product_uom_qty': 1,
- 'state': 'confirmed',
- })
-
- stockMoveOutA.action_done()
- compare_product_usable_qty(productA, 1)
- compare_product_usable_qty(templateAB, 4)
diff --git a/stock_available/views/product_product_view.xml b/stock_available/views/product_product_view.xml
new file mode 100644
index 000000000..22dd36b2e
--- /dev/null
+++ b/stock_available/views/product_product_view.xml
@@ -0,0 +1,20 @@
+
+
+
+
+ Quantity available to promise (variant tree)
+ product.product
+
+
+
+
+ red:immediately_usable_qty<0;blue:immediately_usable_qty>=0 and state in ('draft', 'end', 'obsolete');black:immediately_usable_qty>=0 and state not in ('draft', 'end', 'obsolete')
+
+
+
+
+
+
+
+
+
diff --git a/stock_available/product_view.xml b/stock_available/views/product_template_view.xml
similarity index 100%
rename from stock_available/product_view.xml
rename to stock_available/views/product_template_view.xml
diff --git a/stock_available/res_config_view.xml b/stock_available/views/res_config_view.xml
similarity index 100%
rename from stock_available/res_config_view.xml
rename to stock_available/views/res_config_view.xml