From 978801685ed20fc6e9a8cb3b72ef627319a92136 Mon Sep 17 00:00:00 2001 From: Leonardo Pistone Date: Wed, 26 Nov 2014 15:41:04 +0100 Subject: [PATCH 01/16] the reservation should reserve a quant For weird reasons, force-assigning a move does not associate quants. action_assign'ing the picking does, and is the same procedure that you can do via the interface. --- stock_reserve/model/stock_reserve.py | 2 +- stock_reserve/test/stock_reserve.yml | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/stock_reserve/model/stock_reserve.py b/stock_reserve/model/stock_reserve.py index 0b61a8b69..9581b47d9 100644 --- a/stock_reserve/model/stock_reserve.py +++ b/stock_reserve/model/stock_reserve.py @@ -125,7 +125,7 @@ class StockReservation(models.Model): move_recs = self.move_id move_recs.date_expected = fields.Datetime.now() move_recs.action_confirm() - move_recs.force_assign() + move_recs.picking_id.action_assign() return True @api.multi diff --git a/stock_reserve/test/stock_reserve.yml b/stock_reserve/test/stock_reserve.yml index e23ac9465..66b6f412e 100644 --- a/stock_reserve/test/stock_reserve.yml +++ b/stock_reserve/test/stock_reserve.yml @@ -62,6 +62,14 @@ - !python {model: stock.reservation}: | self.reserve(cr, uid, [ref('reserv_sorbet2')], context=context) +- + Then the reservation should be assigned and have reserved a quant +- + !python {model: stock.reservation, id: reserv_sorbet2}: | + from nose.tools import * + + assert_equal('assigned', self.state) + assert_equal(1, len(self.reserved_quant_ids)) - I check Virtual stock of Sorbet after update reservation - From d3c0f57a5d9e78bfec2a94f665401df344282889 Mon Sep 17 00:00:00 2001 From: Leonardo Pistone Date: Wed, 26 Nov 2014 15:45:03 +0100 Subject: [PATCH 02/16] refactor: get rid of move_recs --- stock_reserve/model/stock_reserve.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/stock_reserve/model/stock_reserve.py b/stock_reserve/model/stock_reserve.py index 9581b47d9..aab360675 100644 --- a/stock_reserve/model/stock_reserve.py +++ b/stock_reserve/model/stock_reserve.py @@ -122,10 +122,9 @@ class StockReservation(models.Model): The reservation is done using the default UOM of the product. A date until which the product is reserved can be specified. """ - move_recs = self.move_id - move_recs.date_expected = fields.Datetime.now() - move_recs.action_confirm() - move_recs.picking_id.action_assign() + self.date_expected = fields.Datetime.now() + self.move_id.action_confirm() + self.move_id.picking_id.action_assign() return True @api.multi From fd3af07ba7f0f2e9a5938179de9858bb1e0dc3ae Mon Sep 17 00:00:00 2001 From: Yannick Vaucher Date: Tue, 16 Dec 2014 11:19:08 +0100 Subject: [PATCH 03/16] Separated Lint tests --- .travis.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index e97406f77..07eae3221 100644 --- a/.travis.yml +++ b/.travis.yml @@ -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 From b11b7c3a629161dec5e68c3a1baec9a6fcb98f8b Mon Sep 17 00:00:00 2001 From: Yannick Vaucher Date: Tue, 16 Dec 2014 14:31:07 +0100 Subject: [PATCH 04/16] Remove deprecated travis_run_pep8 --- .travis.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 07eae3221..303487e9b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -21,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: From 816ac2ebfeb0635e88f6a7ffb8bb5c9c1833305a Mon Sep 17 00:00:00 2001 From: Alexandre Fayolle Date: Mon, 16 Feb 2015 10:38:27 +0100 Subject: [PATCH 05/16] fix pep8 W503 --- stock_reserve_sale/model/sale.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/stock_reserve_sale/model/sale.py b/stock_reserve_sale/model/sale.py index 1dd688a56..5723f473f 100644 --- a/stock_reserve_sale/model/sale.py +++ b/stock_reserve_sale/model/sale.py @@ -123,11 +123,11 @@ class SaleOrderLine(models.Model): def _is_stock_reservable(self): for line in self: reservable = False - if (not (line.state != 'draft' - or line._get_procure_method() == 'make_to_order' - or not line.product_id - or line.product_id.type == 'service') - and not line.reservation_ids): + if (not (line.state != 'draft' or + line._get_procure_method() == 'make_to_order' or + not line.product_id or + line.product_id.type == 'service') and + not line.reservation_ids): reservable = True line.is_stock_reservable = reservable From cb186fe1790fdb322ca14e3aa1f144e9bf78df44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lionel=20Sausin=20=28Num=C3=A9rigraphe=29?= Date: Fri, 27 Feb 2015 13:43:56 +0100 Subject: [PATCH 06/16] [ADD] stock_available Generic module to compute the stock quantity available to promise using several implementations. stock_available_immediatly is changed to become the first optional implementation. Cherry pick of commit 0b060f619fa5d60f9fb343afe1154acd5c730148 from the v7 branch --- stock_available/__init__.py | 24 ++++ stock_available/__openerp__.py | 42 ++++++ stock_available/i18n/fr.po | 98 ++++++++++++++ stock_available/i18n/stock_available.pot | 92 +++++++++++++ stock_available/product.py | 100 ++++++++++++++ .../product_view.xml | 34 +++-- stock_available/res_config.py | 34 +++++ stock_available/res_config_view.xml | 25 ++++ stock_available_immediately/__openerp__.py | 30 +++-- stock_available_immediately/product.py | 123 +++++------------- 10 files changed, 478 insertions(+), 124 deletions(-) create mode 100644 stock_available/__init__.py create mode 100644 stock_available/__openerp__.py create mode 100644 stock_available/i18n/fr.po create mode 100644 stock_available/i18n/stock_available.pot create mode 100644 stock_available/product.py rename {stock_available_immediately => stock_available}/product_view.xml (54%) create mode 100644 stock_available/res_config.py create mode 100644 stock_available/res_config_view.xml diff --git a/stock_available/__init__.py b/stock_available/__init__.py new file mode 100644 index 000000000..6755b49d2 --- /dev/null +++ b/stock_available/__init__.py @@ -0,0 +1,24 @@ +# -*- 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 +from . import res_config + +from .product import _product_available_fnct diff --git a/stock_available/__openerp__.py b/stock_available/__openerp__.py new file mode 100644 index 000000000..16ea5f8ab --- /dev/null +++ b/stock_available/__openerp__.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 . +# +############################################################################## + +{ + 'name': 'Stock available to promise', + 'version': '2.0', + 'author': u'Numérigraphe', + 'category': 'Warehouse', + 'depends': ['stock'], + 'description': """ +Stock available to promise +========================== +This module proposes several options to compute the quantity available to +promise for each product. +This quantity is based on the projected stock and, depending on the +configuration, it can account for various data such as sales quotations or +immediate production capacity. +This can be configured in the menu Settings > Configuration > Warehouse. +""", + 'license': 'AGPL-3', + 'data': [ + 'product_view.xml', + 'res_config_view.xml', + ] +} diff --git a/stock_available/i18n/fr.po b/stock_available/i18n/fr.po new file mode 100644 index 000000000..6c8e4c244 --- /dev/null +++ b/stock_available/i18n/fr.po @@ -0,0 +1,98 @@ +# Translation of OpenERP Server. +# This file contains the translation of the following modules: +# * stock_available +# +msgid "" +msgstr "" +"Project-Id-Version: OpenERP Server 7.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2014-07-30 16:42+0000\n" +"PO-Revision-Date: 2014-07-30 16:42+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 +#: field:product.product,immediately_usable_qty:0 +msgid "Available to promise" +msgstr "Disponible à la vente" + +#. module: stock_available +#: view:product.product:0 +msgid "Available to promise:" +msgstr "Disponible à la vente:" + +#. module: stock_available +#: field:stock.config.settings,module_stock_available_sale:0 +msgid "Exclude goods already in sale quotations" +msgstr "Exclure les marchandises qui sont déjà dans les devis" + +#. module: stock_available +#: field:stock.config.settings,module_stock_available_immediately:0 +msgid "Exclude incoming goods" +msgstr "Exclure les receptions attendues" + +#. module: stock_available +#: field:stock.config.settings,module_stock_available_mrp:0 +msgid "Include the production potential" +msgstr "Inclure la production potentielle" + +#. module: stock_available +#: code:_description:0 +#: model:ir.model,name:stock_available.model_product_product +#, python-format +msgid "Product" +msgstr "Article" + +#. module: stock_available +#: view:stock.config.settings:0 +msgid "Stock available to promise" +msgstr "Stock disponible à la vente" + +#. module: stock_available +#: help:product.product,immediately_usable_qty:0 +msgid "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" +msgstr "Stock de cet article qui peut sans risque être proposé à la vente aux clients.\n" +"La définition de cette valeur est paramétrable selon vos besoins" + +#. module: stock_available +#: help:stock.config.settings,module_stock_available_mrp:0 +msgid "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" +msgstr "Ceci ajoute les quantités de marchandises qui peuvent être immédiatement fabriquées, aux quantitiés disponibles à la vente.\n" +"Ceci installe le module stock_available_mrp.\n" +"Si le module mrp n'est pas encore installé, il le sera aussi" + +#. module: stock_available +#: help:stock.config.settings,module_stock_available_immediately:0 +msgid "This will subtract incoming quantities from the quantities available to promise.\n" +"This installs the module stock_available_immediately." +msgstr "Ceci soustrait les réceptions attendues des quantitiés disponibles à la vente.\n" +"Ceci installe le module stock_available_immediately." + +#. module: stock_available +#: help:stock.config.settings,module_stock_available_sale:0 +msgid "This will subtract quantities from the sale quotations from the quantities available to promise.\n" +"This installs the modules stock_available_sale.\n" +"If the modules sale and sale_delivery_date are not installed, this will install them too" +msgstr "Ceci soustrait les quantités des devis de vente des quantitiés disponibles à la vente.\n" +"Ceci installe le modules stock_available_sale.\n" +"Si les modules sale et sale_delivery_date ne sont pas encore installés, ils le seront également" + +#. module: stock_available +#: view:product.product:0 +msgid "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')" +msgstr "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')" + +#. module: stock_available +#: code:_description:0 +#: model:ir.model,name:stock_available.model_stock_config_settings +#, python-format +msgid "stock.config.settings" +msgstr "stock.config.settings" + diff --git a/stock_available/i18n/stock_available.pot b/stock_available/i18n/stock_available.pot new file mode 100644 index 000000000..6eed1c952 --- /dev/null +++ b/stock_available/i18n/stock_available.pot @@ -0,0 +1,92 @@ +# Translation of OpenERP Server. +# This file contains the translation of the following modules: +# * stock_available +# +msgid "" +msgstr "" +"Project-Id-Version: OpenERP Server 7.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2014-07-30 16:48+0000\n" +"PO-Revision-Date: 2014-07-30 16:48+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 +#: field:product.product,immediately_usable_qty:0 +msgid "Available to promise" +msgstr "" + +#. module: stock_available +#: view:product.product:0 +msgid "Available to promise:" +msgstr "" + +#. module: stock_available +#: field:stock.config.settings,module_stock_available_sale:0 +msgid "Exclude goods already in sale quotations" +msgstr "" + +#. module: stock_available +#: field:stock.config.settings,module_stock_available_immediately:0 +msgid "Exclude incoming goods" +msgstr "" + +#. module: stock_available +#: field:stock.config.settings,module_stock_available_mrp:0 +msgid "Include the production potential" +msgstr "" + +#. module: stock_available +#: code:_description:0 +#: model:ir.model,name:stock_available.model_product_product +#, python-format +msgid "Product" +msgstr "" + +#. module: stock_available +#: view:stock.config.settings:0 +msgid "Stock available to promise" +msgstr "" + +#. module: stock_available +#: help:product.product,immediately_usable_qty:0 +msgid "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" +msgstr "" + +#. module: stock_available +#: help:stock.config.settings,module_stock_available_mrp:0 +msgid "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" +msgstr "" + +#. module: stock_available +#: help:stock.config.settings,module_stock_available_immediately:0 +msgid "This will subtract incoming quantities from the quantities available to promise.\n" +"This installs the module stock_available_immediately." +msgstr "" + +#. module: stock_available +#: help:stock.config.settings,module_stock_available_sale:0 +msgid "This will subtract quantities from the sale quotations from the quantities available to promise.\n" +"This installs the modules stock_available_sale.\n" +"If the modules sale and sale_delivery_date are not installed, this will install them too" +msgstr "" + +#. module: stock_available +#: view:product.product:0 +msgid "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')" +msgstr "" + +#. module: stock_available +#: code:_description:0 +#: model:ir.model,name:stock_available.model_stock_config_settings +#, python-format +msgid "stock.config.settings" +msgstr "" + diff --git a/stock_available/product.py b/stock_available/product.py new file mode 100644 index 000000000..a4a528eb7 --- /dev/null +++ b/stock_available/product.py @@ -0,0 +1,100 @@ +# -*- 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.osv import orm, fields +import openerp.addons.decimal_precision as dp + + +# Expose the method as a function, like when the fields are defined, +# and use the pool to call the method from the other modules too. +def _product_available_fnct(self, cr, uid, ids, field_names=None, arg=False, + context=None): + return self.pool['product.product']._product_available( + cr, uid, ids, field_names=field_names, arg=arg, context=context) + + +class ProductProduct(orm.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.product' + + def __init__(self, pool, cr): + """Use _product_available_fnct to compute all the quantities.""" + # Doing this lets us change the function and not redefine fields + super(ProductProduct, self).__init__(pool, cr) + for coldef in self._columns.values(): + if (isinstance(coldef, fields.function) + and coldef._multi == 'qty_available'): + coldef._fnct = _product_available_fnct + + def _product_available(self, cr, uid, ids, field_names=None, arg=False, + context=None): + """No-op implementation of the stock available to promise. + + Must be overridden by another module that actually implement + computations. + The sub-modules MUST call super()._product_available BEFORE their own + computations + + Side-effect warning: This method may change the list passed as the + field_names parameter, which will then alter the caller's state.""" + # If we didn't get a field_names list, there's nothing to do + if field_names is None: + return super(ProductProduct, self)._product_available( + cr, uid, ids, field_names=field_names, arg=arg, + context=context) + + if context is None: + context = {} + + # Load virtual_available if it's not already asked for + # We need it to compute immediately_usable_qty + # We DO want to change the caller's list so we're NOT going to + # work on a copy of field_names. + if ('virtual_available' not in field_names + and 'immediately_usable_qty' in field_names): + field_names.append('virtual_available') + + # Compute the core quantities + res = super(ProductProduct, self)._product_available( + cr, uid, ids, field_names=field_names, arg=arg, context=context) + + # By default, available to promise = forecasted quantity + if ('immediately_usable_qty' in field_names): + for stock_qty in res.itervalues(): + stock_qty['immediately_usable_qty'] = \ + stock_qty['virtual_available'] + + return res + + _columns = { + 'immediately_usable_qty': fields.function( + _product_available_fnct, multi='qty_available', + type='float', + digits_compute=dp.get_precision('Product Unit of Measure'), + 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_immediately/product_view.xml b/stock_available/product_view.xml similarity index 54% rename from stock_available_immediately/product_view.xml rename to stock_available/product_view.xml index bdd7873f9..d05270f75 100644 --- a/stock_available_immediately/product_view.xml +++ b/stock_available/product_view.xml @@ -1,32 +1,20 @@ - - - - - product.normal.stock.active.qty.form.inherit + + Quantity available to promise (form) product.template - - - - - - - product_immediately_usable.product_product_tree_view + + + Quantity available to promise (tree) product.template @@ -34,12 +22,22 @@ 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') - + + + Quantity available to promise (kanban) + product.template + + + +
  • Available to promise:
  • +
    +
    +
    diff --git a/stock_available/res_config.py b/stock_available/res_config.py new file mode 100644 index 000000000..535f58460 --- /dev/null +++ b/stock_available/res_config.py @@ -0,0 +1,34 @@ +# -*- 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 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 General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# +############################################################################## + +from openerp.osv import orm, fields + + +class StockConfig(orm.TransientModel): + """Add options to easily install the submodules""" + _inherit = 'stock.config.settings' + + _columns = { + 'module_stock_available_immediately': fields.boolean( + 'Exclude incoming goods', + help="This will subtract incoming quantities from the quantities " + "available to promise.\n" + "This installs the module stock_available_immediately."), + } diff --git a/stock_available/res_config_view.xml b/stock_available/res_config_view.xml new file mode 100644 index 000000000..15e45c031 --- /dev/null +++ b/stock_available/res_config_view.xml @@ -0,0 +1,25 @@ + + + + + Stock settings: quantity available to promise + stock.config.settings + + + + + + + + + + + + \ No newline at end of file diff --git a/stock_available_immediately/__openerp__.py b/stock_available_immediately/__openerp__.py index 1cedaa84f..838f9db4c 100644 --- a/stock_available_immediately/__openerp__.py +++ b/stock_available_immediately/__openerp__.py @@ -20,21 +20,27 @@ # # - { - "name": "Immediately Usable Stock Quantity", - "version": "1.0", - "depends": ["product", "stock", ], + "name": "Ignore planned receptions in quantity available to promise", + "version": "2.0", + "depends": ["stock_available"], "author": "Camptocamp", "license": "AGPL-3", - "description": """ -Compute the immediately usable stock. -Immediately usable is computed : Quantity on Hand - Outgoing Stock. + "description": u""" +Ignore planned receptions in quantity available to promise +---------------------------------------------------------- + +Normally the quantity available to promise is based on the virtual stock, +which includes both planned outgoing and incoming goods. +This module will subtract the planned receptions from the quantity available to +promise. + +Contributors +------------ + * Author: Guewen Baconnier (Camptocamp SA) + * Sébastien BEAU (Akretion) + * Lionel Sausin (Numérigraphe) """, - "website": "http://tinyerp.com/module_account.html", - "category": "Generic Modules/Stock", - "data": ["product_view.xml", - ], - "active": False, + "category": "Hidden", 'installable': True } diff --git a/stock_available_immediately/product.py b/stock_available_immediately/product.py index 8e6a7dc95..939bbbb9d 100644 --- a/stock_available_immediately/product.py +++ b/stock_available_immediately/product.py @@ -19,106 +19,41 @@ # ############################################################################## -from openerp.addons import decimal_precision as dp +from openerp.osv import orm + from openerp.osv import orm, fields +class product_immediately_usable(orm.Model): + """Subtract incoming qty from immediately_usable_qty -class ProductTemplate(orm.Model): - """ - Immediately usable quantity is : real stock - outgoing qty - """ - _inherit = 'product.template' + We don't need to override the function fields, the module stock_available + takes of it for us. + + Side-effect warning: This method may change the list passed as the + field_names parameter, which will then alter the caller's state.""" + _inherit = 'product.product' def _product_available(self, cr, uid, ids, field_names=None, arg=False, context=None): - res = super(ProductTemplate, self)._product_available( - cr, uid, ids, field_names, arg, context) + """Ignore the incoming goods in the quantity available to promise""" + # If we didn't get a field_names list, there's nothing to do + if field_names is None or 'immediately_usable_qty' not in field_names: + return super(product_immediately_usable, self)._product_available( + cr, uid, ids, field_names=field_names, arg=arg, + context=context) - if 'immediately_usable_qty' in field_names: - for product_id, stock_qty in res.iteritems(): - res[product_id]['immediately_usable_qty'] = \ - stock_qty['qty_available'] - stock_qty['outgoing_qty'] + # We need available and incoming quantities to compute + # immediately usable quantity. + # We DO want to change the caller's list so we're NOT going to + # work on a copy of field_names. + field_names.append('qty_available') + field_names.append('incoming_qty') + + res = super(product_immediately_usable, self)._product_available( + cr, uid, ids, field_names=field_names, arg=arg, context=context) + + for stock_qty in res.itervalues(): + stock_qty['immediately_usable_qty'] -= \ + stock_qty['incoming_qty'] return res - - _columns = { - 'qty_available': fields.function( - _product_available, - multi='qty_available', - type='float', - digits_compute=dp.get_precision('Product UoM'), - string='Quantity On Hand', - help="Current quantity of products.\n" - "In a context with a single Stock Location, this includes " - "goods stored at this Location, or any of its children.\n" - "In a context with a single Warehouse, this includes " - "goods stored in the Stock Location of this Warehouse, " - "or any " - "of its children.\n" - "In a context with a single Shop, this includes goods " - "stored in the Stock Location of the Warehouse of this Shop, " - "or any of its children.\n" - "Otherwise, this includes goods stored in any Stock Location " - "typed as 'internal'."), - 'virtual_available': fields.function( - _product_available, - multi='qty_available', - type='float', - digits_compute=dp.get_precision('Product UoM'), - string='Quantity Available', - help="Forecast quantity (computed as Quantity On Hand " - "- Outgoing + Incoming)\n" - "In a context with a single Stock Location, this includes " - "goods stored at this Location, or any of its children.\n" - "In a context with a single Warehouse, this includes " - "goods stored in the Stock Location of this Warehouse, " - "or any " - "of its children.\n" - "In a context with a single Shop, this includes goods " - "stored in the Stock Location of the Warehouse of this Shop, " - "or any of its children.\n" - "Otherwise, this includes goods stored in any Stock Location " - "typed as 'internal'."), - 'incoming_qty': fields.function( - _product_available, - multi='qty_available', - type='float', - digits_compute=dp.get_precision('Product UoM'), - string='Incoming', - help="Quantity of products that are planned to arrive.\n" - "In a context with a single Stock Location, this includes " - "goods arriving to this Location, or any of its children.\n" - "In a context with a single Warehouse, this includes " - "goods arriving to the Stock Location of this Warehouse, or " - "any of its children.\n" - "In a context with a single Shop, this includes goods " - "arriving to the Stock Location of the Warehouse of this " - "Shop, or any of its children.\n" - "Otherwise, this includes goods arriving to any Stock " - "Location typed as 'internal'."), - 'outgoing_qty': fields.function( - _product_available, - multi='qty_available', - type='float', - digits_compute=dp.get_precision('Product UoM'), - string='Outgoing', - help="Quantity of products that are planned to leave.\n" - "In a context with a single Stock Location, this includes " - "goods leaving from this Location, or any of its children.\n" - "In a context with a single Warehouse, this includes " - "goods leaving from the Stock Location of this Warehouse, or " - "any of its children.\n" - "In a context with a single Shop, this includes goods " - "leaving from the Stock Location of the Warehouse of this " - "Shop, or any of its children.\n" - "Otherwise, this includes goods leaving from any Stock " - "Location typed as 'internal'."), - 'immediately_usable_qty': fields.function( - _product_available, - digits_compute=dp.get_precision('Product UoM'), - type='float', - string='Immediately Usable', - multi='qty_available', - help="Quantity of products really available for sale." - "Computed as: Quantity On Hand - Outgoing."), - } From 8504307c7b3e83036935f0a14a9e2c9e3b6368c0 Mon Sep 17 00:00:00 2001 From: Lionel Sausin Date: Fri, 27 Feb 2015 16:39:03 +0100 Subject: [PATCH 07/16] [IMP] stock_available* uses new API --- stock_available/__init__.py | 2 - stock_available/__openerp__.py | 10 --- stock_available/product.py | 88 +++++----------------- stock_available/res_config.py | 16 ++-- stock_available_immediately/__openerp__.py | 15 ---- stock_available_immediately/product.py | 43 +++-------- 6 files changed, 37 insertions(+), 137 deletions(-) diff --git a/stock_available/__init__.py b/stock_available/__init__.py index 6755b49d2..6dff1269a 100644 --- a/stock_available/__init__.py +++ b/stock_available/__init__.py @@ -20,5 +20,3 @@ from . import product from . import res_config - -from .product import _product_available_fnct diff --git a/stock_available/__openerp__.py b/stock_available/__openerp__.py index 16ea5f8ab..28a6a7fde 100644 --- a/stock_available/__openerp__.py +++ b/stock_available/__openerp__.py @@ -24,16 +24,6 @@ 'author': u'Numérigraphe', 'category': 'Warehouse', 'depends': ['stock'], - 'description': """ -Stock available to promise -========================== -This module proposes several options to compute the quantity available to -promise for each product. -This quantity is based on the projected stock and, depending on the -configuration, it can account for various data such as sales quotations or -immediate production capacity. -This can be configured in the menu Settings > Configuration > Warehouse. -""", 'license': 'AGPL-3', 'data': [ 'product_view.xml', diff --git a/stock_available/product.py b/stock_available/product.py index a4a528eb7..08a8aa277 100644 --- a/stock_available/product.py +++ b/stock_available/product.py @@ -18,83 +18,35 @@ # ############################################################################## -from openerp.osv import orm, fields -import openerp.addons.decimal_precision as dp +from openerp import models, fields, api +#from openerp.addons import decimal_precision as dp -# Expose the method as a function, like when the fields are defined, -# and use the pool to call the method from the other modules too. -def _product_available_fnct(self, cr, uid, ids, field_names=None, arg=False, - context=None): - return self.pool['product.product']._product_available( - cr, uid, ids, field_names=field_names, arg=arg, context=context) - - -class ProductProduct(orm.Model): +class ProductTemplate(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.product' + _inherit = 'product.template' - def __init__(self, pool, cr): - """Use _product_available_fnct to compute all the quantities.""" - # Doing this lets us change the function and not redefine fields - super(ProductProduct, self).__init__(pool, cr) - for coldef in self._columns.values(): - if (isinstance(coldef, fields.function) - and coldef._multi == 'qty_available'): - coldef._fnct = _product_available_fnct - - def _product_available(self, cr, uid, ids, field_names=None, arg=False, - context=None): + @api.depends('virtual_available') + def _product_available(self): """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. - The sub-modules MUST call super()._product_available BEFORE their own - computations + computations.""" + for product in self: + product.immediately_usable_qty = product.virtual_available - Side-effect warning: This method may change the list passed as the - field_names parameter, which will then alter the caller's state.""" - # If we didn't get a field_names list, there's nothing to do - if field_names is None: - return super(ProductProduct, self)._product_available( - cr, uid, ids, field_names=field_names, arg=arg, - context=context) - - if context is None: - context = {} - - # Load virtual_available if it's not already asked for - # We need it to compute immediately_usable_qty - # We DO want to change the caller's list so we're NOT going to - # work on a copy of field_names. - if ('virtual_available' not in field_names - and 'immediately_usable_qty' in field_names): - field_names.append('virtual_available') - - # Compute the core quantities - res = super(ProductProduct, self)._product_available( - cr, uid, ids, field_names=field_names, arg=arg, context=context) - - # By default, available to promise = forecasted quantity - if ('immediately_usable_qty' in field_names): - for stock_qty in res.itervalues(): - stock_qty['immediately_usable_qty'] = \ - stock_qty['virtual_available'] - - return res - - _columns = { - 'immediately_usable_qty': fields.function( - _product_available_fnct, multi='qty_available', - type='float', - digits_compute=dp.get_precision('Product Unit of Measure'), - 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"), - } + immediately_usable_qty = fields.Float( + compute='_product_available', + 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") + # XXX the standard doesn't honor the UoM decimal precision. Should we? + # digits=dp.get_precision('Product Unit of Measure'), diff --git a/stock_available/res_config.py b/stock_available/res_config.py index 535f58460..bf78bcb3b 100644 --- a/stock_available/res_config.py +++ b/stock_available/res_config.py @@ -18,17 +18,15 @@ # ############################################################################## -from openerp.osv import orm, fields +from openerp import models, fields -class StockConfig(orm.TransientModel): +class StockConfig(models.TransientModel): """Add options to easily install the submodules""" _inherit = 'stock.config.settings' - _columns = { - 'module_stock_available_immediately': fields.boolean( - 'Exclude incoming goods', - help="This will subtract incoming quantities from the quantities " - "available to promise.\n" - "This installs the module stock_available_immediately."), - } + module_stock_available_immediately = fields.Boolean( + string='Exclude incoming goods', + help="This will subtract incoming quantities from the quantities " + "available to promise.\n" + "This installs the module stock_available_immediately.") diff --git a/stock_available_immediately/__openerp__.py b/stock_available_immediately/__openerp__.py index 838f9db4c..197288c9b 100644 --- a/stock_available_immediately/__openerp__.py +++ b/stock_available_immediately/__openerp__.py @@ -26,21 +26,6 @@ "depends": ["stock_available"], "author": "Camptocamp", "license": "AGPL-3", - "description": u""" -Ignore planned receptions in quantity available to promise ----------------------------------------------------------- - -Normally the quantity available to promise is based on the virtual stock, -which includes both planned outgoing and incoming goods. -This module will subtract the planned receptions from the quantity available to -promise. - -Contributors ------------- - * Author: Guewen Baconnier (Camptocamp SA) - * Sébastien BEAU (Akretion) - * Lionel Sausin (Numérigraphe) -""", "category": "Hidden", 'installable': True } diff --git a/stock_available_immediately/product.py b/stock_available_immediately/product.py index 939bbbb9d..05d8ad2e7 100644 --- a/stock_available_immediately/product.py +++ b/stock_available_immediately/product.py @@ -19,41 +19,18 @@ # ############################################################################## -from openerp.osv import orm +from openerp import models, fields, api -from openerp.osv import orm, fields -class product_immediately_usable(orm.Model): - """Subtract incoming qty from immediately_usable_qty +class ProductTemplate(models.Model): + """Subtract incoming qty from immediately_usable_qty""" + _inherit = 'product.template' - We don't need to override the function fields, the module stock_available - takes of it for us. - - Side-effect warning: This method may change the list passed as the - field_names parameter, which will then alter the caller's state.""" - _inherit = 'product.product' - - def _product_available(self, cr, uid, ids, field_names=None, - arg=False, context=None): + @api.depends('virtual_available') + def _product_available(self): """Ignore the incoming goods in the quantity available to promise""" - # If we didn't get a field_names list, there's nothing to do - if field_names is None or 'immediately_usable_qty' not in field_names: - return super(product_immediately_usable, self)._product_available( - cr, uid, ids, field_names=field_names, arg=arg, - context=context) + super(ProductTemplate, self)._product_available() + for product in self: + product.immediately_usable_qty -= product.incoming_qty - # We need available and incoming quantities to compute - # immediately usable quantity. - # We DO want to change the caller's list so we're NOT going to - # work on a copy of field_names. - field_names.append('qty_available') - field_names.append('incoming_qty') - - res = super(product_immediately_usable, self)._product_available( - cr, uid, ids, field_names=field_names, arg=arg, context=context) - - for stock_qty in res.itervalues(): - stock_qty['immediately_usable_qty'] -= \ - stock_qty['incoming_qty'] - - return res + immediately_usable_qty = fields.Float(compute='_product_available') From 13c845fae7b99a0fdfe1c6cb792e33314641851d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lionel=20Sausin=20=28Num=C3=A9rigraphe=29?= Date: Fri, 28 Nov 2014 17:40:27 +0100 Subject: [PATCH 08/16] [ADD] stock_available_mrp Module to take immediate manufaturing capability into account in the stock quantity available to promise. Conflicts: stock_available/res_config.py --- stock_available/res_config.py | 9 ++ stock_available/res_config_view.xml | 4 + stock_available_mrp/__init__.py | 21 +++ stock_available_mrp/__openerp__.py | 39 ++++++ stock_available_mrp/i18n/fr.po | 34 +++++ .../i18n/stock_available_mrp.pot | 34 +++++ stock_available_mrp/product.py | 124 ++++++++++++++++++ stock_available_mrp/product_view.xml | 19 +++ stock_available_mrp/test/potential_qty.yml | 70 ++++++++++ 9 files changed, 354 insertions(+) create mode 100644 stock_available_mrp/__init__.py create mode 100644 stock_available_mrp/__openerp__.py create mode 100644 stock_available_mrp/i18n/fr.po create mode 100644 stock_available_mrp/i18n/stock_available_mrp.pot create mode 100644 stock_available_mrp/product.py create mode 100644 stock_available_mrp/product_view.xml create mode 100644 stock_available_mrp/test/potential_qty.yml diff --git a/stock_available/res_config.py b/stock_available/res_config.py index bf78bcb3b..7d4355df7 100644 --- a/stock_available/res_config.py +++ b/stock_available/res_config.py @@ -30,3 +30,12 @@ class StockConfig(models.TransientModel): help="This will subtract incoming quantities from the quantities " "available to promise.\n" "This installs the module stock_available_immediately.") + + 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") diff --git a/stock_available/res_config_view.xml b/stock_available/res_config_view.xml index 15e45c031..d4f378fb8 100644 --- a/stock_available/res_config_view.xml +++ b/stock_available/res_config_view.xml @@ -15,6 +15,10 @@