@@ -44,11 +44,15 @@ this repository contains the full copyright notices and license terms. -->
|
||||
<label name="state"/>
|
||||
<field name="state"/>
|
||||
</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"/>
|
||||
<field name="untaxed_amount" xalign="1.0" xexpand="0"/>
|
||||
<newline/>
|
||||
|
||||
<label name="tax_amount" xalign="1.0" xexpand="1"/>
|
||||
<field name="tax_amount" xalign="1.0" xexpand="0"/>
|
||||
<newline/>
|
||||
|
||||
<label name="total_amount" xalign="1.0" xexpand="1"/>
|
||||
<field name="total_amount" xalign="1.0" xexpand="0"/>
|
||||
</group>
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
* Add original amounts before promotion
|
||||
|
||||
Version 7.4.0 - 2024-11-04
|
||||
--------------------------
|
||||
|
||||
+113
-21
@@ -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):
|
||||
|
||||
@@ -3,6 +3,18 @@
|
||||
this repository contains the full copyright notices and license terms. -->
|
||||
<tryton>
|
||||
<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">
|
||||
<field name="model">sale.promotion</field>
|
||||
<field name="type">form</field>
|
||||
|
||||
@@ -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::
|
||||
|
||||
|
||||
@@ -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>
|
||||
Reference in New Issue
Block a user