Add original amounts before promotion

Closes #13633
This commit is contained in:
Cédric Krier
2024-10-26 00:10:56 +02:00
parent afc6c5a913
commit 2b6166f155
7 changed files with 179 additions and 23 deletions
+5 -1
View File
@@ -44,11 +44,15 @@ this repository contains the full copyright notices and license terms. -->
<label name="state"/> <label name="state"/>
<field name="state"/> <field name="state"/>
</group> </group>
<group col="2" colspan="2" id="amount" yfill="1"> <group col="-1" colspan="2" id="amount" yfill="1">
<label name="untaxed_amount" xalign="1.0" xexpand="1"/> <label name="untaxed_amount" xalign="1.0" xexpand="1"/>
<field name="untaxed_amount" xalign="1.0" xexpand="0"/> <field name="untaxed_amount" xalign="1.0" xexpand="0"/>
<newline/>
<label name="tax_amount" xalign="1.0" xexpand="1"/> <label name="tax_amount" xalign="1.0" xexpand="1"/>
<field name="tax_amount" xalign="1.0" xexpand="0"/> <field name="tax_amount" xalign="1.0" xexpand="0"/>
<newline/>
<label name="total_amount" xalign="1.0" xexpand="1"/> <label name="total_amount" xalign="1.0" xexpand="1"/>
<field name="total_amount" xalign="1.0" xexpand="0"/> <field name="total_amount" xalign="1.0" xexpand="0"/>
</group> </group>
+1
View File
@@ -1,3 +1,4 @@
* Add original amounts before promotion
Version 7.4.0 - 2024-11-04 Version 7.4.0 - 2024-11-04
-------------------------- --------------------------
+113 -21
View File
@@ -22,6 +22,44 @@ from .exceptions import FormulaError
class Sale(metaclass=PoolMeta): class Sale(metaclass=PoolMeta):
__name__ = 'sale.sale' __name__ = 'sale.sale'
original_untaxed_amount = fields.Function(Monetary(
"Original Untaxed", digits='currency', currency='currency',
states={
'invisible': (
~Eval('state').in_(['draft', 'quotation'])
| (Eval('original_untaxed_amount')
== Eval('untaxed_amount'))),
}),
'get_original_amount')
original_tax_amount = fields.Function(Monetary(
"Original Tax", digits='currency', currency='currency',
states={
'invisible': (
~Eval('state').in_(['draft', 'quotation'])
| (Eval('original_tax_amount') == Eval('tax_amount'))),
}),
'get_original_amount')
original_total_amount = fields.Function(Monetary(
"Original Total", digits='currency', currency='currency',
states={
'invisible': (
~Eval('state').in_(['draft', 'quotation'])
| (Eval('original_total_amount') == Eval('total_amount'))),
}),
'get_original_amount')
def get_original_amount(self, names):
amounts = dict.fromkeys(names)
if self.state in {'draft', 'quotation'}:
amounts['original_untaxed_amount'] = sum(
(line.original_amount for line in self.line_lines), Decimal(0))
if {'original_tax_amount', 'original_total_amount'} & set(names):
with Transaction().set_context(_original_amount=True):
amounts['original_tax_amount'] = self.get_tax_amount()
amounts['original_total_amount'] = sum(
filter(None, amounts.values()))
return amounts
@classmethod @classmethod
@ModelView.button @ModelView.button
@Workflow.transition('draft') @Workflow.transition('draft')
@@ -29,19 +67,7 @@ class Sale(metaclass=PoolMeta):
super(Sale, cls).draft(sales) super(Sale, cls).draft(sales)
# Reset to draft unit price # Reset to draft unit price
for sale in sales: for sale in sales:
changed = False sale.unapply_promotion()
for line in sale.lines:
if line.type != 'line':
continue
if line.draft_unit_price is not None:
line.unit_price = line.draft_unit_price
line.draft_unit_price = None
changed = True
if line.promotion:
line.promotion = None
changed = True
if changed:
sale.lines = sale.lines # Trigger changes
cls.save(sales) cls.save(sales)
@classmethod @classmethod
@@ -51,19 +77,34 @@ class Sale(metaclass=PoolMeta):
super(Sale, cls).quote(sales) super(Sale, cls).quote(sales)
# Store draft unit price before changing it # Store draft unit price before changing it
for sale in sales: for sale in sales:
for line in sale.lines:
if line.type != 'line':
continue
if line.draft_unit_price is None:
line.draft_unit_price = line.unit_price
sale.apply_promotion() sale.apply_promotion()
cls.save(sales) cls.save(sales)
def unapply_promotion(self):
"Unapply promotion"
changed = False
for line in self.lines:
if line.type != 'line':
continue
if line.original_unit_price is not None:
line.unit_price = line.original_unit_price
line.original_unit_price = None
changed = True
if line.promotion:
line.promotion = None
changed = True
if changed:
self.lines = self.lines # Trigger changes
def apply_promotion(self): def apply_promotion(self):
'Apply promotion' "Apply promotion"
pool = Pool() pool = Pool()
Promotion = pool.get('sale.promotion') Promotion = pool.get('sale.promotion')
for line in self.lines:
if line.type == 'line' and line.original_unit_price is None:
line.original_unit_price = line.unit_price
promotions = Promotion.get_promotions(self) promotions = Promotion.get_promotions(self)
for promotion in promotions: for promotion in promotions:
promotion.apply(self) promotion.apply(self)
@@ -72,17 +113,68 @@ class Sale(metaclass=PoolMeta):
class Line(metaclass=PoolMeta): class Line(metaclass=PoolMeta):
__name__ = 'sale.line' __name__ = 'sale.line'
draft_unit_price = fields.Numeric('Draft Unit Price', original_unit_price = Monetary(
digits=price_digits, readonly=True, "Original Unit Price", digits=price_digits, currency='currency',
readonly=True,
states={ states={
'required': Bool(Eval('promotion', None)), 'required': Bool(Eval('promotion', None)),
'invisible': ~Eval('promotion'),
}) })
original_amount = fields.Function(Monetary(
"Original Amount", digits='currency', currency='currency',
states={
'invisible': ~Eval('promotion'),
}),
'get_original_amount')
promotion = fields.Many2One('sale.promotion', "Promotion", promotion = fields.Many2One('sale.promotion', "Promotion",
ondelete='RESTRICT', ondelete='RESTRICT',
domain=[ domain=[
('company', '=', Eval('company', -1)), ('company', '=', Eval('company', -1)),
]) ])
@classmethod
def __register__(cls, module):
table_h = cls.__table_handler__(module)
# Migration from 7.4: rename draft_unit_price
table_h.column_rename('draft_unit_price', 'original_unit_price')
super().__register__(module)
def get_original_amount(self, name):
currency = self.sale.currency
def _amount(line):
return currency.round(
Decimal(str(line.quantity))
* (line.original_unit_price or line.unit_price))
if self.type == 'line':
return _amount(self)
elif self.type == 'subtotal':
amount = Decimal(0)
for line2 in self.sale.lines:
if line2.type == 'line':
amount += _amount(line2)
elif line2.type == 'subtotal':
if self == line2:
break
amount = Decimal(0)
return amount
@property
def taxable_lines(self):
lines = super().taxable_lines
if (getattr(self, 'type', None) == 'line'
and Transaction().context.get('_original_amount')):
lines = [(
getattr(self, 'taxes', None) or [],
getattr(self, 'original_unit_price', None)
or getattr(self, 'unit_price', None)
or Decimal(0),
getattr(self, 'quantity', None) or 0,
None,
)]
return lines
class Promotion( class Promotion(
DeactivableMixin, ModelSQL, ModelView, MatchMixin): DeactivableMixin, ModelSQL, ModelView, MatchMixin):
+12
View File
@@ -3,6 +3,18 @@
this repository contains the full copyright notices and license terms. --> this repository contains the full copyright notices and license terms. -->
<tryton> <tryton>
<data> <data>
<record model="ir.ui.view" id="sale_sale_view_form">
<field name="model">sale.sale</field>
<field name="inherit" ref="sale.sale_view_form"/>
<field name="name">sale_sale_form</field>
</record>
<record model="ir.ui.view" id="sale_line_view_form">
<field name="model">sale.line</field>
<field name="inherit" ref="sale.sale_line_view_form"/>
<field name="name">sale_line_form</field>
</record>
<record model="ir.ui.view" id="promotion_view_form"> <record model="ir.ui.view" id="promotion_view_form">
<field name="model">sale.promotion</field> <field name="model">sale.promotion</field>
<field name="type">form</field> <field name="type">form</field>
@@ -8,7 +8,8 @@ Imports::
>>> from decimal import Decimal >>> from decimal import Decimal
>>> from proteus import Model >>> from proteus import Model
>>> from trytond.modules.account.tests.tools import create_chart, get_accounts >>> from trytond.modules.account.tests.tools import (
... create_chart, create_tax, get_accounts)
>>> from trytond.modules.account_invoice.tests.tools import create_payment_term >>> from trytond.modules.account_invoice.tests.tools import create_payment_term
>>> from trytond.modules.company.tests.tools import create_company >>> from trytond.modules.company.tests.tools import create_company
>>> from trytond.tests.tools import activate_modules >>> from trytond.tests.tools import activate_modules
@@ -22,6 +23,10 @@ Get accounts::
>>> accounts = get_accounts() >>> accounts = get_accounts()
>>> revenue = accounts['revenue'] >>> revenue = accounts['revenue']
Create a tax::
>>> tax = create_tax(Decimal('0.1'))
Create customer:: Create customer::
>>> Party = Model.get('party.party') >>> Party = Model.get('party.party')
@@ -34,6 +39,7 @@ Create account category::
>>> account_category = ProductCategory(name="Account Category") >>> account_category = ProductCategory(name="Account Category")
>>> account_category.accounting = True >>> account_category.accounting = True
>>> account_category.account_revenue = revenue >>> account_category.account_revenue = revenue
>>> account_category.customer_taxes.append(tax)
>>> account_category.save() >>> account_category.save()
Create products:: Create products::
@@ -73,6 +79,8 @@ Create products::
>>> product3.save() >>> product3.save()
>>> template4, = template.duplicate(default={'categories': [category2.id]}) >>> template4, = template.duplicate(default={'categories': [category2.id]})
>>> template4.account_category = account_category
>>> template4.save()
>>> product4 = Product(template4.products[0].id) >>> product4 = Product(template4.products[0].id)
Create payment term:: Create payment term::
@@ -112,9 +120,23 @@ Sale enough products for promotion::
>>> sale.save() >>> sale.save()
>>> sale.untaxed_amount >>> sale.untaxed_amount
Decimal('500.00') Decimal('500.00')
>>> sale.tax_amount
Decimal('50.00')
>>> sale.total_amount
Decimal('550.00')
>>> sale.click('quote') >>> sale.click('quote')
>>> sale.untaxed_amount >>> sale.untaxed_amount
Decimal('470.00') Decimal('470.00')
>>> sale.tax_amount
Decimal('47.00')
>>> sale.total_amount
Decimal('517.00')
>>> sale.original_untaxed_amount
Decimal('500.00')
>>> sale.original_tax_amount
Decimal('50.00')
>>> sale.original_total_amount
Decimal('550.00')
Go back to draft reset the original price:: Go back to draft reset the original price::
@@ -0,0 +1,11 @@
<?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='quantity']" position="after">
<label name="original_unit_price"/>
<field name="original_unit_price"/>
<label name="original_amount"/>
<field name="original_amount"/>
</xpath>
</data>
@@ -0,0 +1,14 @@
<?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='untaxed_amount']" position="before">
<field name="original_untaxed_amount" xalign="1.0" xexpand="0"/>
</xpath>
<xpath expr="//field[@name='tax_amount']" position="before">
<field name="original_tax_amount" xalign="1.0" xexpand="0"/>
</xpath>
<xpath expr="//field[@name='total_amount']" position="before">
<field name="original_total_amount" xalign="1.0" xexpand="0"/>
</xpath>
</data>