From e7e5ccee63477f8f79552aa643b540023271ee16 Mon Sep 17 00:00:00 2001 From: Carlos Dauden Date: Mon, 7 Nov 2016 11:21:44 +0100 Subject: [PATCH] [MIG] [9.0] stock_putaway_product: Migration to v9.0 (#197) --- stock_putaway_product/README.rst | 90 +++++++++++++++++++ stock_putaway_product/__init__.py | 6 ++ stock_putaway_product/__openerp__.py | 27 ++++++ .../demo/product_putaway.xml | 26 ++++++ stock_putaway_product/models/__init__.py | 4 + stock_putaway_product/models/product.py | 24 +++++ .../models/product_putaway.py | 79 ++++++++++++++++ .../security/ir.model.access.csv | 3 + stock_putaway_product/tests/__init__.py | 5 ++ .../tests/test_product_putaway.py | 44 +++++++++ stock_putaway_product/views/product.xml | 31 +++++++ .../views/product_putaway.xml | 47 ++++++++++ stock_putaway_product/wizard/__init__.py | 5 ++ .../wizard/stock_change_product_qty.py | 29 ++++++ 14 files changed, 420 insertions(+) create mode 100644 stock_putaway_product/README.rst create mode 100644 stock_putaway_product/__init__.py create mode 100644 stock_putaway_product/__openerp__.py create mode 100644 stock_putaway_product/demo/product_putaway.xml create mode 100644 stock_putaway_product/models/__init__.py create mode 100644 stock_putaway_product/models/product.py create mode 100644 stock_putaway_product/models/product_putaway.py create mode 100644 stock_putaway_product/security/ir.model.access.csv create mode 100644 stock_putaway_product/tests/__init__.py create mode 100644 stock_putaway_product/tests/test_product_putaway.py create mode 100644 stock_putaway_product/views/product.xml create mode 100644 stock_putaway_product/views/product_putaway.xml create mode 100644 stock_putaway_product/wizard/__init__.py create mode 100644 stock_putaway_product/wizard/stock_change_product_qty.py diff --git a/stock_putaway_product/README.rst b/stock_putaway_product/README.rst new file mode 100644 index 000000000..d1a8fb5a5 --- /dev/null +++ b/stock_putaway_product/README.rst @@ -0,0 +1,90 @@ +.. image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg + :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html + :alt: License: AGPL-3 + +============================ +Putaway strategy per product +============================ + +This module extends the functionality of the odoo putaway strategy. +It defines a new type of putaway strategy where users can set a specific +stock location per product. + +On the product form, the case, rack, location fields are replaced with a +specific putaway strategy and location id for the product. + +A putaway strategy can be used to ensure that incoming products will be +stored in the location set on the product form. + +A recommended set-up is to create a separate putaway strategy for each +warehouse. This will ensure that the same product will be placed in the +appropriate location in each warehouse it is received. + +Installation +============ + +To install this module, just click the install button. + +Configuration +============= + +To configure this module, you need to: + +#. Go to Inventory > Configuration > Settings +#. Enable "Manage several locations per warehouse" on Location & Warehouse > + Multi Locations +#. Enable "Advanced routing of products using rules" on Location & Warehouse > + Routes +#. Go to Inventory > Configuration > Warehouse Management > Locations +#. On the main inventory location of your warehouse, + set a new putaway strategy. +#. For the new putaway strategy, select 'Fixed per product location' + as method + +Usage +===== + +To use this module, you need to: + +#. Select the proper stock locations for each product on the product form + on the "Inventory" tab + +.. image:: https://odoo-community.org/website/image/ir.attachment/5784_f2813bd/datas + :alt: Try me on Runbot + :target: https://runbot.odoo-community.org/runbot/153/8.0 + +Bug Tracker +=========== + +Bugs are tracked on `GitHub Issues +`_. In case of trouble +, please check there if your issue has already been reported. If you spotted +it first, help us smashing it by providing a detailed and welcomed feedback. + +Images +------ + +* Odoo Community Association: `Icon `_. + +Contributors +------------ + +* Jos De Graeve - Apertoso N.V. +* Carlos Dauden - Tecnativa + + +Maintainer +---------- + +.. image:: https://odoo-community.org/logo.png + :alt: Odoo Community Association + :target: https://odoo-community.org + +This module is maintained by the OCA. + +OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use. + +To contribute to this module, please visit https://odoo-community.org. + diff --git a/stock_putaway_product/__init__.py b/stock_putaway_product/__init__.py new file mode 100644 index 000000000..cf4b35415 --- /dev/null +++ b/stock_putaway_product/__init__.py @@ -0,0 +1,6 @@ +# -*- coding: utf-8 -*- +# © 2016 Jos De Graeve - Apertoso N.V. +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +from . import models +from . import tests +from . import wizard diff --git a/stock_putaway_product/__openerp__.py b/stock_putaway_product/__openerp__.py new file mode 100644 index 000000000..03fc6c065 --- /dev/null +++ b/stock_putaway_product/__openerp__.py @@ -0,0 +1,27 @@ +# -*- coding: utf-8 -*- +# © 2016 Jos De Graeve - Apertoso N.V. +# © 2016 Carlos Dauden - Tecnativa +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +{ + 'name': 'Putaway strategy per product', + 'summary': 'Set a product location and put-away strategy per product', + 'version': '9.0.1.0.0', + 'category': 'Inventory', + 'website': 'http://www.apertoso.be', + 'author': 'Apertoso N.V., ' + 'Tecnativa, ' + 'Odoo Community Association (OCA)', + 'license': 'AGPL-3', + 'depends': [ + 'product', + 'stock' + ], + 'data': [ + 'views/product.xml', + 'views/product_putaway.xml', + 'security/ir.model.access.csv', + ], + 'demo': [ + 'demo/product_putaway.xml', + ] +} diff --git a/stock_putaway_product/demo/product_putaway.xml b/stock_putaway_product/demo/product_putaway.xml new file mode 100644 index 000000000..10c8cbe9e --- /dev/null +++ b/stock_putaway_product/demo/product_putaway.xml @@ -0,0 +1,26 @@ + + + + WH - Putaway Per Product + per_product + + + + + + + + + + + + + diff --git a/stock_putaway_product/models/__init__.py b/stock_putaway_product/models/__init__.py new file mode 100644 index 000000000..97407703e --- /dev/null +++ b/stock_putaway_product/models/__init__.py @@ -0,0 +1,4 @@ +# -*- coding: utf-8 -*- + +from . import product_putaway +from . import product diff --git a/stock_putaway_product/models/product.py b/stock_putaway_product/models/product.py new file mode 100644 index 000000000..468a456fd --- /dev/null +++ b/stock_putaway_product/models/product.py @@ -0,0 +1,24 @@ +# -*- coding: utf-8 -*- +# © 2016 Jos De Graeve - Apertoso N.V. +# © 2016 Carlos Dauden - Tecnativa +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from openerp import fields, models + + +class ProductTemplate(models.Model): + _inherit = 'product.template' + + product_putaway_ids = fields.One2many( + comodel_name='stock.product.putaway.strategy', + inverse_name='product_tmpl_id', + string="Product stock locations") + + +class ProductProduct(models.Model): + _inherit = 'product.product' + + product_putaway_ids = fields.One2many( + comodel_name='stock.product.putaway.strategy', + inverse_name='product_product_id', + string="Product stock locations") diff --git a/stock_putaway_product/models/product_putaway.py b/stock_putaway_product/models/product_putaway.py new file mode 100644 index 000000000..392f4aff5 --- /dev/null +++ b/stock_putaway_product/models/product_putaway.py @@ -0,0 +1,79 @@ +# -*- coding: utf-8 -*- +# © 2016 Jos De Graeve - Apertoso N.V. +# © 2016 Carlos Dauden - Tecnativa +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from openerp import models, fields, api, _ + + +class ProductPutawayStrategy(models.Model): + _inherit = 'product.putaway' + + @api.model + def _get_putaway_options(self): + ret = super(ProductPutawayStrategy, self)._get_putaway_options() + return ret + [('per_product', 'Fixed per product location')] + + product_location_ids = fields.One2many( + comodel_name='stock.product.putaway.strategy', + inverse_name='putaway_id', + string='Fixed per product location', + copy=True) + method = fields.Selection(selection=_get_putaway_options) + + @api.multi + def get_product_putaway_strategies(self, product): + self.ensure_one() + return self.product_location_ids.filtered(lambda x: ( + x.product_product_id == product or + (not x.product_product_id and + x.product_tmpl_id == product.product_tmpl_id))) + + @api.model + def putaway_apply(self, putaway_strategy, product): + if putaway_strategy.method == 'per_product': + strategies = putaway_strategy.get_product_putaway_strategies( + product) + return strategies[:1].fixed_location_id.id + else: + return super(ProductPutawayStrategy, self).putaway_apply( + putaway_strategy, product) + + +class StockFixedPutawayStrategy(models.Model): + _name = 'stock.product.putaway.strategy' + _rec_name = 'product_product_id' + _order = 'putaway_id, sequence' + + _sql_constraints = [( + 'putaway_product_location_unique', + 'unique(putaway_id,product_product_id,fixed_location_id)', + _('There is a duplicate location by put away assignment!') + )] + + @api.model + def get_default_inventory_id(self): + return self.env['stock.inventory'].search( + [('id', '=', self.env.context.get('active_id', False))]) + + putaway_id = fields.Many2one( + comodel_name='product.putaway', + string='Put Away Strategy', + required=True, + index=True) + product_tmpl_id = fields.Many2one( + comodel_name='product.template', + string='Product Template', + index=True, + oldname='product_template_id', + required=True) + product_product_id = fields.Many2one( + comodel_name='product.product', + string='Product Variant', + index=True) + fixed_location_id = fields.Many2one( + comodel_name='stock.location', + string='Location', + required=True, + domain=[('usage', '=', 'internal')]) + sequence = fields.Integer() diff --git a/stock_putaway_product/security/ir.model.access.csv b/stock_putaway_product/security/ir.model.access.csv new file mode 100644 index 000000000..f390071c9 --- /dev/null +++ b/stock_putaway_product/security/ir.model.access.csv @@ -0,0 +1,3 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink +access_stock_product_putaway_strategy,stock_product_putaway_strategy managers,model_stock_product_putaway_strategy,stock.group_stock_manager,1,1,1,1 +access_stock_product_putaway_user,stock_product_putaway_strategy user,model_stock_product_putaway_strategy,stock.group_stock_user,1,0,0,0 diff --git a/stock_putaway_product/tests/__init__.py b/stock_putaway_product/tests/__init__.py new file mode 100644 index 000000000..21f4cf34a --- /dev/null +++ b/stock_putaway_product/tests/__init__.py @@ -0,0 +1,5 @@ +# -*- coding: utf-8 -*- +# © 2016 Jos De Graeve - Apertoso N.V. +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from . import test_product_putaway diff --git a/stock_putaway_product/tests/test_product_putaway.py b/stock_putaway_product/tests/test_product_putaway.py new file mode 100644 index 000000000..1e84b9959 --- /dev/null +++ b/stock_putaway_product/tests/test_product_putaway.py @@ -0,0 +1,44 @@ +# -*- coding: utf-8 -*- +# © 2016 Jos De Graeve - Apertoso N.V. +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from openerp.tests import common + + +class TestProductPutaway(common.TransactionCase): + # Check if "per_product" is a valid putaway method + def test_01_putaway_methods(self): + field_method = self.env[ + 'product.putaway']._fields.get('method') + self.assertIn('per_product', field_method.get_values(self.env)) + + def test_02_putway_apply(self): + putaway_per_product = self.browse_ref( + 'stock_putaway_product.product_putaway_per_product_wh') + product_ipad = self.browse_ref( + 'product.product_product_4') + location_shelf1 = self.browse_ref( + 'stock.stock_location_components') + + self.assertEqual( + self.env['product.putaway'].putaway_apply( + putaway_per_product, product_ipad), + location_shelf1.id) + + def test_03_stock_change_product_qty_default(self): + product_ipad = self.browse_ref( + 'product.product_product_4') + location_shelf1 = self.browse_ref( + 'stock.stock_location_components') + + wiz_obj = self.env['stock.change.product.qty'] + + test_context = { + 'active_model': 'product.product', + 'active_id': product_ipad.id, + } + wiz_instance = wiz_obj.with_context(test_context).create( + {'product_tmpl_id': product_ipad.product_tmpl_id.id}) + self.assertEqual( + wiz_instance.location_id, + location_shelf1) diff --git a/stock_putaway_product/views/product.xml b/stock_putaway_product/views/product.xml new file mode 100644 index 000000000..2a39d0537 --- /dev/null +++ b/stock_putaway_product/views/product.xml @@ -0,0 +1,31 @@ + + + + + product.template.product.form + product.template + + + + + + + + + + + + + + + + + + diff --git a/stock_putaway_product/views/product_putaway.xml b/stock_putaway_product/views/product_putaway.xml new file mode 100644 index 000000000..f40ad839c --- /dev/null +++ b/stock_putaway_product/views/product_putaway.xml @@ -0,0 +1,47 @@ + + + + product.putaway.form.byproduct + product.putaway + + + +
+ + + + + + + + + +
+
+
+
+ + + product_putaway_strategy_view_form + stock.product.putaway.strategy + +
+ + + + + + + + + + + + +
+
+
+ +
diff --git a/stock_putaway_product/wizard/__init__.py b/stock_putaway_product/wizard/__init__.py new file mode 100644 index 000000000..f16d13007 --- /dev/null +++ b/stock_putaway_product/wizard/__init__.py @@ -0,0 +1,5 @@ +# -*- coding: utf-8 -*- +# © 2016 Jos De Graeve - Apertoso N.V. +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from . import stock_change_product_qty diff --git a/stock_putaway_product/wizard/stock_change_product_qty.py b/stock_putaway_product/wizard/stock_change_product_qty.py new file mode 100644 index 000000000..0a1048b40 --- /dev/null +++ b/stock_putaway_product/wizard/stock_change_product_qty.py @@ -0,0 +1,29 @@ +# -*- coding: utf-8 -*- +# © 2016 Jos De Graeve - Apertoso N.V. +# © 2016 Carlos Dauden - Tecnativa +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from openerp import models, api + + +class StockChangeProductQty(models.TransientModel): + _inherit = "stock.change.product.qty" + + # When a user updates stock qty on the product form, propose the + # right location to update stock. + @api.model + def default_get(self, fields): + res = super(StockChangeProductQty, self).default_get(fields) + + product_product_id = res.get('product_id') + location_id = res.get('location_id') + + if product_product_id and location_id: + putaway = self.env['stock.product.putaway.strategy'].search([ + ('product_product_id', '=', product_product_id), + ('fixed_location_id', 'child_of', location_id), + ], limit=1) + if putaway: + res.update({'location_id': putaway.fixed_location_id.id}) + + return res