diff --git a/modules/sale/view/sale_form.xml b/modules/sale/view/sale_form.xml
index ae0b8e783f..2260705ed2 100644
--- a/modules/sale/view/sale_form.xml
+++ b/modules/sale/view/sale_form.xml
@@ -44,11 +44,15 @@ this repository contains the full copyright notices and license terms. -->
-
+
+
+
+
+
diff --git a/modules/sale_promotion/CHANGELOG b/modules/sale_promotion/CHANGELOG
index 9a9f454e39..6024ec1059 100644
--- a/modules/sale_promotion/CHANGELOG
+++ b/modules/sale_promotion/CHANGELOG
@@ -1,3 +1,4 @@
+* Add original amounts before promotion
Version 7.4.0 - 2024-11-04
--------------------------
diff --git a/modules/sale_promotion/sale.py b/modules/sale_promotion/sale.py
index 8f0bbd92a0..ca8168dee0 100644
--- a/modules/sale_promotion/sale.py
+++ b/modules/sale_promotion/sale.py
@@ -22,6 +22,44 @@ from .exceptions import FormulaError
class Sale(metaclass=PoolMeta):
__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
@ModelView.button
@Workflow.transition('draft')
@@ -29,19 +67,7 @@ class Sale(metaclass=PoolMeta):
super(Sale, cls).draft(sales)
# Reset to draft unit price
for sale in sales:
- changed = False
- 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
+ sale.unapply_promotion()
cls.save(sales)
@classmethod
@@ -51,19 +77,34 @@ class Sale(metaclass=PoolMeta):
super(Sale, cls).quote(sales)
# Store draft unit price before changing it
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()
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):
- 'Apply promotion'
+ "Apply promotion"
pool = Pool()
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)
for promotion in promotions:
promotion.apply(self)
@@ -72,17 +113,68 @@ class Sale(metaclass=PoolMeta):
class Line(metaclass=PoolMeta):
__name__ = 'sale.line'
- draft_unit_price = fields.Numeric('Draft Unit Price',
- digits=price_digits, readonly=True,
+ original_unit_price = Monetary(
+ "Original Unit Price", digits=price_digits, currency='currency',
+ readonly=True,
states={
'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",
ondelete='RESTRICT',
domain=[
('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(
DeactivableMixin, ModelSQL, ModelView, MatchMixin):
diff --git a/modules/sale_promotion/sale.xml b/modules/sale_promotion/sale.xml
index 78d9e3484c..7ca08b3395 100644
--- a/modules/sale_promotion/sale.xml
+++ b/modules/sale_promotion/sale.xml
@@ -3,6 +3,18 @@
this repository contains the full copyright notices and license terms. -->
+
+ sale.sale
+
+ sale_sale_form
+
+
+
+ sale.line
+
+ sale_line_form
+
+
sale.promotion
form
diff --git a/modules/sale_promotion/tests/scenario_sale_promotion.rst b/modules/sale_promotion/tests/scenario_sale_promotion.rst
index 79504fe806..81b2d71b6c 100644
--- a/modules/sale_promotion/tests/scenario_sale_promotion.rst
+++ b/modules/sale_promotion/tests/scenario_sale_promotion.rst
@@ -8,7 +8,8 @@ Imports::
>>> from decimal import Decimal
>>> 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.company.tests.tools import create_company
>>> from trytond.tests.tools import activate_modules
@@ -22,6 +23,10 @@ Get accounts::
>>> accounts = get_accounts()
>>> revenue = accounts['revenue']
+Create a tax::
+
+ >>> tax = create_tax(Decimal('0.1'))
+
Create customer::
>>> Party = Model.get('party.party')
@@ -34,6 +39,7 @@ Create account category::
>>> account_category = ProductCategory(name="Account Category")
>>> account_category.accounting = True
>>> account_category.account_revenue = revenue
+ >>> account_category.customer_taxes.append(tax)
>>> account_category.save()
Create products::
@@ -73,6 +79,8 @@ Create products::
>>> product3.save()
>>> template4, = template.duplicate(default={'categories': [category2.id]})
+ >>> template4.account_category = account_category
+ >>> template4.save()
>>> product4 = Product(template4.products[0].id)
Create payment term::
@@ -112,9 +120,23 @@ Sale enough products for promotion::
>>> sale.save()
>>> sale.untaxed_amount
Decimal('500.00')
+ >>> sale.tax_amount
+ Decimal('50.00')
+ >>> sale.total_amount
+ Decimal('550.00')
>>> sale.click('quote')
>>> sale.untaxed_amount
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::
diff --git a/modules/sale_promotion/view/sale_line_form.xml b/modules/sale_promotion/view/sale_line_form.xml
new file mode 100644
index 0000000000..a1a4a4e9a1
--- /dev/null
+++ b/modules/sale_promotion/view/sale_line_form.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/modules/sale_promotion/view/sale_sale_form.xml b/modules/sale_promotion/view/sale_sale_form.xml
new file mode 100644
index 0000000000..65756c3fad
--- /dev/null
+++ b/modules/sale_promotion/view/sale_sale_form.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
+
+