@@ -1,3 +1,4 @@
|
|||||||
|
* Add promotion coupon as action
|
||||||
* Use actual quantity of sale line
|
* Use actual quantity of sale line
|
||||||
|
|
||||||
Version 7.4.0 - 2024-11-04
|
Version 7.4.0 - 2024-11-04
|
||||||
|
|||||||
@@ -18,3 +18,8 @@ def register():
|
|||||||
sale.Sale,
|
sale.Sale,
|
||||||
account.InvoiceLine,
|
account.InvoiceLine,
|
||||||
module='sale_complaint', type_='model')
|
module='sale_complaint', type_='model')
|
||||||
|
Pool.register(
|
||||||
|
complaint.Complaint_PromotionCoupon,
|
||||||
|
complaint.Action_PromotionCoupon,
|
||||||
|
module='sale_complaint', type_='model',
|
||||||
|
depends=['sale_promotion_coupon'])
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
# This file is part of Tryton. The COPYRIGHT file at the top level of
|
# This file is part of Tryton. The COPYRIGHT file at the top level of
|
||||||
# this repository contains the full copyright notices and license terms.
|
# this repository contains the full copyright notices and license terms.
|
||||||
|
|
||||||
|
import datetime as dt
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
|
|
||||||
@@ -15,7 +16,7 @@ from trytond.modules.company.model import (
|
|||||||
employee_field, reset_employee, set_employee)
|
employee_field, reset_employee, set_employee)
|
||||||
from trytond.modules.currency.fields import Monetary
|
from trytond.modules.currency.fields import Monetary
|
||||||
from trytond.modules.product import price_digits
|
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.pyson import Bool, Eval, If
|
||||||
from trytond.tools import grouped_slice
|
from trytond.tools import grouped_slice
|
||||||
from trytond.transaction import Transaction
|
from trytond.transaction import Transaction
|
||||||
@@ -497,6 +498,9 @@ class Action(ModelSQL, ModelView):
|
|||||||
complaint_state = fields.Function(
|
complaint_state = fields.Function(
|
||||||
fields.Selection('get_complaint_states', "Complaint State"),
|
fields.Selection('get_complaint_states', "Complaint State"),
|
||||||
'on_change_with_complaint_state')
|
'on_change_with_complaint_state')
|
||||||
|
company = fields.Function(
|
||||||
|
fields.Many2One('company.company', "Company"),
|
||||||
|
'on_change_with_company')
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def __setup__(cls):
|
def __setup__(cls):
|
||||||
@@ -583,6 +587,11 @@ class Action(ModelSQL, ModelView):
|
|||||||
if self.complaint:
|
if self.complaint:
|
||||||
return self.complaint.state
|
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
|
@classmethod
|
||||||
def _get_result(cls):
|
def _get_result(cls):
|
||||||
'Return list of Model names for result Reference'
|
'Return list of Model names for result Reference'
|
||||||
@@ -861,3 +870,67 @@ class Action_InvoiceLine(_Action_Line, ModelView, ModelSQL):
|
|||||||
return self.unit_price
|
return self.unit_price
|
||||||
elif self.line:
|
elif self.line:
|
||||||
return self.line.unit_price
|
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,
|
||||||
|
)
|
||||||
|
|||||||
@@ -288,4 +288,11 @@ this repository contains the full copyright notices and license terms. -->
|
|||||||
<field name="name">action_line_list</field>
|
<field name="name">action_line_list</field>
|
||||||
</record>
|
</record>
|
||||||
</data>
|
</data>
|
||||||
|
<data depends="sale_promotion_coupon">
|
||||||
|
<record model="ir.ui.view" id="action_view_form_promotion_coupon">
|
||||||
|
<field name="model">sale.complaint.action</field>
|
||||||
|
<field name="inherit" ref="action_view_form"/>
|
||||||
|
<field name="name">action_form_promotion_coupon</field>
|
||||||
|
</record>
|
||||||
|
</data>
|
||||||
</tryton>
|
</tryton>
|
||||||
|
|||||||
@@ -47,7 +47,10 @@ for dep in info.get('depends', []):
|
|||||||
requires.append(get_require_version('trytond_%s' % dep))
|
requires.append(get_require_version('trytond_%s' % dep))
|
||||||
requires.append(get_require_version('trytond'))
|
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,
|
setup(name=name,
|
||||||
version=version,
|
version=version,
|
||||||
|
|||||||
@@ -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))
|
||||||
@@ -8,6 +8,7 @@ from trytond.tests.test_tryton import ModuleTestCase
|
|||||||
class SaleComplaintTestCase(CompanyTestMixin, ModuleTestCase):
|
class SaleComplaintTestCase(CompanyTestMixin, ModuleTestCase):
|
||||||
'Test Sale Complaint module'
|
'Test Sale Complaint module'
|
||||||
module = 'sale_complaint'
|
module = 'sale_complaint'
|
||||||
|
extras = ['sale_promotion_coupon']
|
||||||
|
|
||||||
|
|
||||||
del ModuleTestCase
|
del ModuleTestCase
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ depends:
|
|||||||
ir
|
ir
|
||||||
party
|
party
|
||||||
sale
|
sale
|
||||||
|
extras_depend:
|
||||||
|
sale_promotion_coupon
|
||||||
xml:
|
xml:
|
||||||
complaint.xml
|
complaint.xml
|
||||||
sale.xml
|
sale.xml
|
||||||
|
|||||||
@@ -0,0 +1,15 @@
|
|||||||
|
<?xml version="1.0"?>
|
||||||
|
<!-- This file is part of Tryton. The COPYRIGHT file at the top level of
|
||||||
|
this repository contains the full copyright notices and license terms. -->
|
||||||
|
<data>
|
||||||
|
<xpath expr="//field[@name='invoice_lines']" position="after">
|
||||||
|
<label name="promotion_coupon"/>
|
||||||
|
<field name="promotion_coupon"/>
|
||||||
|
<label name="promotion_coupon_number"/>
|
||||||
|
<field name="promotion_coupon_number"/>
|
||||||
|
|
||||||
|
<label name="promotion_coupon_duration"/>
|
||||||
|
<field name="promotion_coupon_duration"/>
|
||||||
|
<newline/>
|
||||||
|
</xpath>
|
||||||
|
</data>
|
||||||
Reference in New Issue
Block a user