diff --git a/modules/sale_complaint/CHANGELOG b/modules/sale_complaint/CHANGELOG
index 1b532557b4..6a491926ed 100644
--- a/modules/sale_complaint/CHANGELOG
+++ b/modules/sale_complaint/CHANGELOG
@@ -1,3 +1,4 @@
+* Add promotion coupon as action
* Use actual quantity of sale line
Version 7.4.0 - 2024-11-04
diff --git a/modules/sale_complaint/__init__.py b/modules/sale_complaint/__init__.py
index 392364bbc2..804f54fc02 100644
--- a/modules/sale_complaint/__init__.py
+++ b/modules/sale_complaint/__init__.py
@@ -18,3 +18,8 @@ def register():
sale.Sale,
account.InvoiceLine,
module='sale_complaint', type_='model')
+ Pool.register(
+ complaint.Complaint_PromotionCoupon,
+ complaint.Action_PromotionCoupon,
+ module='sale_complaint', type_='model',
+ depends=['sale_promotion_coupon'])
diff --git a/modules/sale_complaint/complaint.py b/modules/sale_complaint/complaint.py
index f392c53265..b30166f403 100644
--- a/modules/sale_complaint/complaint.py
+++ b/modules/sale_complaint/complaint.py
@@ -1,6 +1,7 @@
# This file is part of Tryton. The COPYRIGHT file at the top level of
# this repository contains the full copyright notices and license terms.
+import datetime as dt
from collections import defaultdict
from decimal import Decimal
@@ -15,7 +16,7 @@ from trytond.modules.company.model import (
employee_field, reset_employee, set_employee)
from trytond.modules.currency.fields import Monetary
from trytond.modules.product import price_digits
-from trytond.pool import Pool
+from trytond.pool import Pool, PoolMeta
from trytond.pyson import Bool, Eval, If
from trytond.tools import grouped_slice
from trytond.transaction import Transaction
@@ -497,6 +498,9 @@ class Action(ModelSQL, ModelView):
complaint_state = fields.Function(
fields.Selection('get_complaint_states', "Complaint State"),
'on_change_with_complaint_state')
+ company = fields.Function(
+ fields.Many2One('company.company', "Company"),
+ 'on_change_with_company')
@classmethod
def __setup__(cls):
@@ -583,6 +587,11 @@ class Action(ModelSQL, ModelView):
if self.complaint:
return self.complaint.state
+ @fields.depends('complaint', '_parent_complaint.company')
+ def on_change_with_company(self, name=None):
+ if self.complaint:
+ return self.complaint.company
+
@classmethod
def _get_result(cls):
'Return list of Model names for result Reference'
@@ -861,3 +870,67 @@ class Action_InvoiceLine(_Action_Line, ModelView, ModelSQL):
return self.unit_price
elif self.line:
return self.line.unit_price
+
+
+class Complaint_PromotionCoupon(metaclass=PoolMeta):
+ __name__ = 'sale.complaint'
+
+ @classmethod
+ def _actions_domains(cls):
+ domains = super()._actions_domains()
+ for name, domain in domains.items():
+ domain.append('promotion_coupon')
+ return domains
+
+
+class Action_PromotionCoupon(metaclass=PoolMeta):
+ __name__ = 'sale.complaint.action'
+
+ _promtion_coupon_states = {
+ 'invisible': Eval('action') != 'promotion_coupon',
+ 'required': Eval('action') == 'promotion_coupon',
+ }
+
+ promotion_coupon = fields.Many2One(
+ 'sale.promotion.coupon', "Promotion Coupon",
+ domain=[
+ ('company', '=', Eval('company', -1)),
+ ('number_of_use', '=', 1),
+ ('per_party', '=', False),
+ ],
+ states=_promtion_coupon_states)
+ promotion_coupon_number = fields.Char(
+ "Number", states=_promtion_coupon_states)
+ promotion_coupon_duration = fields.TimeDelta(
+ "Duration", states=_promtion_coupon_states)
+
+ del _promtion_coupon_states
+
+ @classmethod
+ def __setup__(cls):
+ super().__setup__()
+ cls.action.selection.append(('promotion_coupon', "Promotion Coupon"))
+
+ @classmethod
+ def default_promotion_coupon_duration(cls):
+ return dt.timedelta(days=90)
+
+ @classmethod
+ def _get_result(cls):
+ return super()._get_result() + ['sale.promotion.coupon.number']
+
+ def do_promotion_coupon(self):
+ pool = Pool()
+ PromotionCouponNumber = pool.get('sale.promotion.coupon.number')
+ Date = pool.get('ir.date')
+
+ with Transaction().set_context(company=self.company.id):
+ today = Date.today()
+
+ return PromotionCouponNumber(
+ number=self.promotion_coupon_number,
+ coupon=self.promotion_coupon,
+ company=self.company,
+ start_date=today,
+ end_date=today + self.promotion_coupon_duration,
+ )
diff --git a/modules/sale_complaint/complaint.xml b/modules/sale_complaint/complaint.xml
index 9aae795aa8..268e9ddfb1 100644
--- a/modules/sale_complaint/complaint.xml
+++ b/modules/sale_complaint/complaint.xml
@@ -288,4 +288,11 @@ this repository contains the full copyright notices and license terms. -->
action_line_list
+
+
+ sale.complaint.action
+
+ action_form_promotion_coupon
+
+
diff --git a/modules/sale_complaint/setup.py b/modules/sale_complaint/setup.py
index ed092ac844..02c3f4d8d3 100755
--- a/modules/sale_complaint/setup.py
+++ b/modules/sale_complaint/setup.py
@@ -47,7 +47,10 @@ for dep in info.get('depends', []):
requires.append(get_require_version('trytond_%s' % dep))
requires.append(get_require_version('trytond'))
-tests_require = [get_require_version('proteus')]
+tests_require = [
+ get_require_version('proteus'),
+ get_require_version('trytond_sale_promotion_coupon'),
+ ]
setup(name=name,
version=version,
diff --git a/modules/sale_complaint/tests/scenario_sale_complaint_promotion_coupon.rst b/modules/sale_complaint/tests/scenario_sale_complaint_promotion_coupon.rst
new file mode 100644
index 0000000000..50f8b90114
--- /dev/null
+++ b/modules/sale_complaint/tests/scenario_sale_complaint_promotion_coupon.rst
@@ -0,0 +1,122 @@
+=============================================
+Sale Complaint with Promotion Coupon Scenario
+=============================================
+
+Imports::
+
+ >>> import datetime as dt
+ >>> from decimal import Decimal
+
+ >>> from proteus import Model
+ >>> from trytond.modules.account.tests.tools import (
+ ... create_chart, create_fiscalyear, get_accounts)
+ >>> from trytond.modules.account_invoice.tests.tools import (
+ ... set_fiscalyear_invoice_sequences)
+ >>> from trytond.modules.company.tests.tools import create_company
+ >>> from trytond.tests.tools import activate_modules, assertEqual
+
+Activate modules::
+
+ >>> config = activate_modules(
+ ... ['sale_complaint', 'sale_promotion_coupon'],
+ ... create_company, create_chart)
+
+ >>> Complaint = Model.get('sale.complaint')
+ >>> IrModel = Model.get('ir.model')
+ >>> Party = Model.get('party.party')
+ >>> ProductCategory = Model.get('product.category')
+ >>> ProductTemplate = Model.get('product.template')
+ >>> ProductUom = Model.get('product.uom')
+ >>> Promotion = Model.get('sale.promotion')
+ >>> Sale = Model.get('sale.sale')
+ >>> Type = Model.get('sale.complaint.type')
+
+Create fiscal year::
+
+ >>> fiscalyear = set_fiscalyear_invoice_sequences(create_fiscalyear())
+ >>> fiscalyear.click('create_period')
+
+Get accounts::
+
+ >>> accounts = get_accounts()
+
+Create parties::
+
+ >>> customer = Party(name='Customer')
+ >>> customer.save()
+
+Create complaint type::
+
+ >>> sale_type = Type(name='Sale')
+ >>> sale_type.origin, = IrModel.find([('name', '=', 'sale.sale')])
+ >>> sale_type.save()
+
+Create account category::
+
+ >>> account_category = ProductCategory(name="Account Category")
+ >>> account_category.accounting = True
+ >>> account_category.account_revenue = accounts['revenue']
+ >>> account_category.save()
+
+Create product::
+
+ >>> unit, = ProductUom.find([('name', '=', "Unit")])
+
+ >>> template = ProductTemplate()
+ >>> template.name = "Product"
+ >>> template.default_uom = unit
+ >>> template.type = 'goods'
+ >>> template.salable = True
+ >>> template.list_price = Decimal('10')
+ >>> template.account_category = account_category
+ >>> template.save()
+ >>> product, = template.products
+
+Create Promotion with coupon::
+
+ >>> promotion = Promotion(name="50%")
+ >>> promotion.formula = '0.5 * unit_price'
+ >>> coupon = promotion.coupons.new()
+ >>> coupon.number_of_use = 1
+ >>> coupon.per_party = False
+ >>> promotion.save()
+ >>> coupon, = promotion.coupons
+
+Sale 1 product::
+
+ >>> sale = Sale()
+ >>> sale.party = customer
+ >>> sale_line = sale.lines.new()
+ >>> sale_line.product = product
+ >>> sale_line.quantity = 1
+ >>> sale.click('quote')
+ >>> sale.click('confirm')
+ >>> sale.state
+ 'processing'
+
+Fill a complaint for the sale::
+
+ >>> complaint = Complaint()
+ >>> complaint.customer = customer
+ >>> complaint.type = sale_type
+ >>> complaint.origin = sale
+ >>> complaint.save()
+
+Resolve complaint with a coupon::
+
+ >>> action = complaint.actions.new(action='promotion_coupon')
+ >>> action.promotion_coupon = coupon
+ >>> action.promotion_coupon_number = "DISC50"
+ >>> action.promotion_coupon_duration = dt.timedelta(days=30)
+ >>> complaint.click('wait')
+ >>> complaint.click('approve')
+ >>> complaint.state
+ 'done'
+
+ >>> action, = complaint.actions
+ >>> coupon_number = action.result
+ >>> coupon_number.number
+ 'DISC50'
+ >>> assertEqual(coupon_number.coupon, coupon)
+ >>> assertEqual(
+ ... coupon_number.end_date - coupon_number.start_date, dt.timedelta(days=30))
diff --git a/modules/sale_complaint/tests/test_module.py b/modules/sale_complaint/tests/test_module.py
index 065167079e..357898ef2d 100644
--- a/modules/sale_complaint/tests/test_module.py
+++ b/modules/sale_complaint/tests/test_module.py
@@ -8,6 +8,7 @@ from trytond.tests.test_tryton import ModuleTestCase
class SaleComplaintTestCase(CompanyTestMixin, ModuleTestCase):
'Test Sale Complaint module'
module = 'sale_complaint'
+ extras = ['sale_promotion_coupon']
del ModuleTestCase
diff --git a/modules/sale_complaint/tryton.cfg b/modules/sale_complaint/tryton.cfg
index 14a842de9f..67c157aff6 100644
--- a/modules/sale_complaint/tryton.cfg
+++ b/modules/sale_complaint/tryton.cfg
@@ -6,6 +6,8 @@ depends:
ir
party
sale
+extras_depend:
+ sale_promotion_coupon
xml:
complaint.xml
sale.xml
diff --git a/modules/sale_complaint/view/action_form_promotion_coupon.xml b/modules/sale_complaint/view/action_form_promotion_coupon.xml
new file mode 100644
index 0000000000..3a37827202
--- /dev/null
+++ b/modules/sale_complaint/view/action_form_promotion_coupon.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+