diff --git a/stock_putaway_product_template/__init__.py b/stock_putaway_product_template/__init__.py index 8a3fd4c08..1553865ca 100644 --- a/stock_putaway_product_template/__init__.py +++ b/stock_putaway_product_template/__init__.py @@ -2,3 +2,4 @@ from . import models from . import tests +from .hooks import post_init_hook diff --git a/stock_putaway_product_template/__manifest__.py b/stock_putaway_product_template/__manifest__.py index f50b53ab7..9cacc4285 100644 --- a/stock_putaway_product_template/__manifest__.py +++ b/stock_putaway_product_template/__manifest__.py @@ -8,7 +8,13 @@ "author": "Akretion, Odoo Community Association (OCA)", "license": "AGPL-3", "depends": ["stock"], + "external_dependencies": { + "python": [ + "openupgradelib", + ], + }, "data": ["views/product.xml"], "demo": ["demo/putaway_strategies.xml"], "maintainers": ["kevinkhao", "sebastienbeau"], + "post_init_hook": "post_init_hook", } diff --git a/stock_putaway_product_template/hooks.py b/stock_putaway_product_template/hooks.py new file mode 100644 index 000000000..3da582463 --- /dev/null +++ b/stock_putaway_product_template/hooks.py @@ -0,0 +1,19 @@ +# Copyright 2020 Tecnativa - Sergio Teruel +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +import logging +from openupgradelib import openupgrade + +_logger = logging.getLogger(__name__) + + +def post_init_hook(cr, registry): + openupgrade.logged_query( + cr, """ + UPDATE stock_fixed_putaway_strat sfps + SET product_tmpl_id=pp.product_tmpl_id + FROM product_product pp + WHERE pp.id=sfps.product_id AND + sfps.product_tmpl_id <> pp.product_tmpl_id + """ + ) diff --git a/stock_putaway_product_template/models/putaway_strategy.py b/stock_putaway_product_template/models/putaway_strategy.py index 64eeb8f54..99da525cc 100644 --- a/stock_putaway_product_template/models/putaway_strategy.py +++ b/stock_putaway_product_template/models/putaway_strategy.py @@ -1,6 +1,18 @@ # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +# Copyright 2020 Sergio Teruel - Tecnativa -from odoo import fields, models +from odoo import api, fields, models + + +class PutAwayStrategy(models.Model): + _inherit = 'product.putaway' + + # Remove product domain to allow to select product templates + product_location_ids = fields.One2many(domain=[]) + + def _get_putaway_rule(self, product): + return super(PutAwayStrategy, self.with_context( + filter_putaway_rule=True))._get_putaway_rule(product) class FixedPutAwayStrategy(models.Model): @@ -8,8 +20,29 @@ class FixedPutAwayStrategy(models.Model): product_tmpl_id = fields.Many2one( comodel_name="product.template", + compute="_compute_product_tmpl_id", + store=True, + inverse=lambda self: self, ondelete="cascade", - readonly=True, - related="product_id.product_tmpl_id", - store=True ) + + @api.depends('product_id') + def _compute_product_tmpl_id(self): + for rec in self: + if rec.product_id: + rec.product_tmpl_id = rec.product_id.product_tmpl_id + else: + params = self.env.context.get('params', {}) + if params.get('model', '') == 'product.template': + rec.product_tmpl_id = params.get('id', False) + + def filtered(self, func): + res = super(FixedPutAwayStrategy, self).filtered(func) + if res or not self.env.context.get('filter_putaway_rule'): + return res + product = func.__closure__[0].cell_contents + if product._name != 'product.product': + return res + return self.with_context(filter_putaway_rule=False).filtered( + lambda x: (x.product_tmpl_id == product.product_tmpl_id and + not x.product_id)) diff --git a/stock_putaway_product_template/readme/CONTRIBUTORS.rst b/stock_putaway_product_template/readme/CONTRIBUTORS.rst index 3a6013026..ae30e064e 100644 --- a/stock_putaway_product_template/readme/CONTRIBUTORS.rst +++ b/stock_putaway_product_template/readme/CONTRIBUTORS.rst @@ -1,3 +1,7 @@ -* Akretion +* `Akretion `_: * Kevin Khao + +* `Tecnativa `_: + + * Sergio Teruel diff --git a/stock_putaway_product_template/tests/test_product_putaway.py b/stock_putaway_product_template/tests/test_product_putaway.py index ea309f022..3790488a2 100644 --- a/stock_putaway_product_template/tests/test_product_putaway.py +++ b/stock_putaway_product_template/tests/test_product_putaway.py @@ -8,6 +8,10 @@ class TestProductPutaway(TransactionCase): super().setUp() self.putawayObj = self.env["product.putaway"] self.putawayLineObj = self.env["stock.fixed.putaway.strat"] + ProductTemplate = self.env["product.template"] + ProductAttribute = self.env["product.attribute"] + ProductAttributeValue = self.env["product.attribute.value"] + TemplateAttributeLine = self.env["product.template.attribute.line"] ref = self.env.ref self.product_tmpl_chair = ref( "product.product_product_11_product_template" @@ -27,6 +31,32 @@ class TestProductPutaway(TransactionCase): "stock_putaway_product_form.putaway_strat_2_line_2" ) + # Add a product with variants + self.template = ProductTemplate.create({ + 'name': 'Product test', + 'type': 'consu', + }) + self.size_attribute = ProductAttribute.create({ + 'name': 'Test size', + 'sequence': 1, + }) + self.size_m = ProductAttributeValue.create({ + 'name': 'Size M', + 'attribute_id': self.size_attribute.id, + 'sequence': 1, + }) + self.size_l = ProductAttributeValue.create({ + 'name': 'Size L', + 'attribute_id': self.size_attribute.id, + 'sequence': 2, + }) + self.template_attribute_lines = TemplateAttributeLine.create({ + 'product_tmpl_id': self.template.id, + 'attribute_id': self.size_attribute.id, + 'value_ids': [(6, 0, [self.size_m.id, self.size_l.id])], + }) + self.template.create_variant_ids() + def test_tmpl_has_putaways_from_products(self): self.assertIn( self.putaway_line_1, @@ -59,3 +89,38 @@ class TestProductPutaway(TransactionCase): self.putaway_line_4, self.product_tmpl_chair.product_putaway_categ_ids, ) + + def test_apply_putaway(self): + # Create one strategy line for product template and other with a + # specific variant + location = self.env.ref('stock.stock_location_shop0') + location1 = location.copy({ + 'name': 'Location test 1', + 'location_id': location.id + }) + location2 = location.copy({ + 'name': 'Location test 2', + 'location_id': location.id + }) + variant1 = self.template.product_variant_ids[0] + variant2 = self.template.product_variant_ids[1] + putaway = self.putawayObj.create({'name': 'Putaway for test'}) + val_list = [ + { + 'putaway_id': putaway.id, + 'product_tmpl_id': self.template.id, + 'fixed_location_id': location1.id, + }, + { + 'putaway_id': putaway.id, + 'product_id': variant2.id, + 'fixed_location_id': location2.id, + }, + ] + self.putawayLineObj.create(val_list) + location_applied = putaway._get_putaway_rule( + variant1).fixed_location_id + self.assertEqual(location_applied, location1) + location_applied = putaway._get_putaway_rule( + variant2).fixed_location_id + self.assertEqual(location_applied, location2) diff --git a/stock_putaway_product_template/views/product.xml b/stock_putaway_product_template/views/product.xml index 27a3e7781..61f1bba70 100644 --- a/stock_putaway_product_template/views/product.xml +++ b/stock_putaway_product_template/views/product.xml @@ -9,20 +9,18 @@
-

The rules defined per product will be applied before the rules defined per product category. +

Keep empty product field to apply strategy to all variants.

- + - - + + @@ -53,7 +51,7 @@ attrs="{'invisible': [('is_product_variant', '=', False)]}"> - +