diff --git a/CHANGELOG b/CHANGELOG index 693b3cda70..64c869d6cf 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,6 @@ +* Add list of categories on promotion +* Remove unit constraint on list of products from promotion + Version 4.6.0 - 2017-10-30 * Bug fixes (see mercurial logs for details) * Support price list when computing unit price diff --git a/__init__.py b/__init__.py index 77239252e6..1b6c17a14f 100644 --- a/__init__.py +++ b/__init__.py @@ -9,6 +9,7 @@ def register(): Pool.register( SalePromotion, SalePromotion_Product, + SalePromotion_ProductCategory, Sale, SaleLine, module='sale_promotion', type_='model') diff --git a/doc/index.rst b/doc/index.rst index ccdf19e6b4..665078629e 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -18,6 +18,7 @@ The criteria are the fields: - *Price List*: The sale price list. - *Start Date*/*End Date*: The period for which the promotion is valid. - *Quantity*: The sum quantity of the sale lines which have the same *Unit*. -- *Products*: The list of products to use to compute quantity. +- *Products*: The list of products to apply the promotion. +- *Categories*: The list of product categories to apply the promotion. The new unit price is computed by the field *Formula*. diff --git a/sale.py b/sale.py index f075eda0fa..a9a95d3f60 100644 --- a/sale.py +++ b/sale.py @@ -14,7 +14,7 @@ from trytond.tools import decistmt from trytond.modules.product import price_digits __all__ = ['Sale', 'SaleLine', - 'SalePromotion', 'SalePromotion_Product'] + 'SalePromotion', 'SalePromotion_Product', 'SalePromotion_ProductCategory'] class Sale: @@ -124,14 +124,11 @@ class SalePromotion(ModelSQL, ModelView, MatchMixin): depends=['quantity']) unit_digits = fields.Function(fields.Integer('Unit Digits'), 'on_change_with_unit_digits') - unit_category = fields.Function(fields.Many2One('product.uom.category', - 'Unit Category'), 'on_change_with_unit_category') products = fields.Many2Many('sale.promotion-product.product', - 'promotion', 'product', 'Products', - domain=[ - ('default_uom_category', '=', Eval('unit_category')), - ], - depends=['unit_category']) + 'promotion', 'product', 'Products') + categories = fields.Many2Many( + 'sale.promotion-product.category', 'promotion', 'category', + "Categories") formula = fields.Char('Formula', required=True, help=('Python expression that will be evaluated with:\n' '- unit_price: the original unit_price')) @@ -159,11 +156,6 @@ class SalePromotion(ModelSQL, ModelView, MatchMixin): return self.unit.digits return 2 - @fields.depends('unit') - def on_change_with_unit_category(self, name=None): - if self.unit: - return self.unit.category.id - @classmethod def validate(cls, promotions): super(SalePromotion, cls).validate(promotions) @@ -235,10 +227,21 @@ class SalePromotion(ModelSQL, ModelView, MatchMixin): return super(SalePromotion, self).match(pattern) def is_valid_sale_line(self, line): - if self.products: - return line.product in self.products - elif self.unit: - return line.unit.category == self.unit.category + + def parents(categories): + for category in categories: + while category: + yield category + category = category.parent + + if self.unit and line.unit.category != self.unit.category: + return False + elif self.products and line.product not in self.products: + return False + elif (self.categories + and not set(parents(line.product.categories_all)).intersection( + self.categories)): + return False else: return True @@ -290,3 +293,15 @@ class SalePromotion_Product(ModelSQL): required=True, ondelete='CASCADE', select=True) product = fields.Many2One('product.product', 'Product', required=True, ondelete='CASCADE') + + +class SalePromotion_ProductCategory(ModelSQL): + "Sale Promotion - Product Category" + __name__ = 'sale.promotion-product.category' + + promotion = fields.Many2One( + 'sale.promotion', "Promotion", + required=True, ondelete='CASCADE', select=True) + category = fields.Many2One( + 'product.category', "Category", + required=True, ondelete='CASCADE') diff --git a/tests/scenario_sale_promotion.rst b/tests/scenario_sale_promotion.rst index 65b71b3d5c..3fb3d16e1b 100644 --- a/tests/scenario_sale_promotion.rst +++ b/tests/scenario_sale_promotion.rst @@ -41,6 +41,16 @@ Create products:: >>> ProductUom = Model.get('product.uom') >>> ProductTemplate = Model.get('product.template') >>> Product = Model.get('product.product') + >>> Category = Model.get('product.category') + + >>> category = Category(name="Root") + >>> category1 = category.childs.new(name="Child 1") + >>> category1b = category1.childs.new(name="Child 1b") + >>> category2 = category.childs.new(name="Child 2") + >>> category.save() + >>> category1, category2 = category.childs + >>> category1b, = category1.childs + >>> unit, = ProductUom.find([('name', '=', 'Unit')]) >>> template = ProductTemplate() >>> template.name = 'Product' @@ -50,6 +60,7 @@ Create products:: >>> template.lead_time = datetime.timedelta(0) >>> template.list_price = Decimal('20') >>> template.account_revenue = revenue + >>> template.categories.append(Category(category1b.id)) >>> template.save() >>> product1 = Product() >>> product1.template = template @@ -61,6 +72,9 @@ Create products:: >>> product3.template = template >>> product3.save() + >>> template4, = template.duplicate(default={'categories': [category2.id]}) + >>> product4 = Product(template4.products[0].id) + Create payment term:: >>> payment_term = create_payment_term() @@ -72,7 +86,8 @@ Create Promotion:: >>> promotion = Promotion(name='10% on 10 products 1 or 2') >>> promotion.quantity = 10 >>> promotion.unit = unit - >>> promotion.products.extend([product1, product2]) + >>> promotion.products.extend([product1, product2, product4]) + >>> promotion.categories.append(Category(category1.id)) >>> promotion.formula = '0.9 * unit_price' >>> promotion.save() @@ -90,7 +105,10 @@ Sale enough products for promotion:: >>> sale_line.quantity = 10 >>> sale_line = sale.lines.new() >>> sale_line.product = product3 - >>> sale_line.quantity = 10 + >>> sale_line.quantity = 5 + >>> sale_line = sale.lines.new() + >>> sale_line.product = product4 + >>> sale_line.quantity = 5 >>> sale.save() >>> sale.untaxed_amount Decimal('500.00') diff --git a/view/promotion_form.xml b/view/promotion_form.xml index 9168d5181b..976a2d7691 100644 --- a/view/promotion_form.xml +++ b/view/promotion_form.xml @@ -21,5 +21,6 @@ this repository contains the full copyright notices and license terms. -->