changeset c72071b493f7 in modules/account_tax_rule_country:default
details:
https://hg.tryton.org/modules/account_tax_rule_country?cmd=changeset;node=c72071b493f7
description:
Add subdivision as criteria
issue8252
review257851003
diffstat:
CHANGELOG | 2 +
__init__.py | 1 +
account.py | 113 +++++++++++++++++++++++----
doc/index.rst | 22 ++--
purchase.py | 8 +-
sale.py | 8 +-
stock.py | 26 ++++-
tests/test_account_tax_rule_country.py | 136 +++++++++++++++++++++++++++++++++
view/tax_rule_line_form.xml | 4 +
view/tax_rule_line_template_form.xml | 4 +
view/tax_rule_line_template_tree.xml | 2 +
view/tax_rule_line_tree.xml | 2 +
view/tax_rule_line_tree_sequence.xml | 2 +
13 files changed, 293 insertions(+), 37 deletions(-)
diffs (524 lines):
diff -r f9145f64fc84 -r c72071b493f7 CHANGELOG
--- a/CHANGELOG Mon May 06 14:59:51 2019 +0200
+++ b/CHANGELOG Sun Aug 18 19:03:12 2019 +0200
@@ -1,3 +1,5 @@
+* Add subdivision as criteria
+
Version 5.2.0 - 2019-05-06
* Bug fixes (see mercurial logs for details)
diff -r f9145f64fc84 -r c72071b493f7 __init__.py
--- a/__init__.py Mon May 06 14:59:51 2019 +0200
+++ b/__init__.py Sun Aug 18 19:03:12 2019 +0200
@@ -10,6 +10,7 @@
def register():
Pool.register(
+ account.TaxRule,
account.TaxRuleLineTemplate,
account.TaxRuleLine,
module='account_tax_rule_country', type_='model')
diff -r f9145f64fc84 -r c72071b493f7 account.py
--- a/account.py Mon May 06 14:59:51 2019 +0200
+++ b/account.py Sun Aug 18 19:03:12 2019 +0200
@@ -3,16 +3,62 @@
from trytond.pool import Pool, PoolMeta
from trytond.model import fields
+from trytond.pyson import Eval
-__all__ = ['TaxRuleLineTemplate', 'TaxRuleLine', 'InvoiceLine']
+
+class TaxRule(metaclass=PoolMeta):
+ __name__ = 'account.tax.rule'
+
+ def apply(self, tax, pattern):
+ pool = Pool()
+ Subdivision = pool.get('country.subdivision')
+
+ def parents(subdivision):
+ while subdivision:
+ yield subdivision.id
+ subdivision = subdivision.parent
+
+ pattern = pattern.copy()
+ for name in ['from_subdivision', 'to_subdivision']:
+ subdivision = pattern.pop(name, None)
+ if not subdivision:
+ continue
+ subdivision = Subdivision(subdivision)
+ pattern[name] = list(parents(subdivision))
+ return super().apply(tax, pattern)
-class TaxRuleLineTemplate(metaclass=PoolMeta):
+class _TaxRuleLineMixin:
+ from_country = fields.Many2One(
+ 'country.country', 'From Country', ondelete='RESTRICT',
+ help="Apply only to addresses of this country.")
+ from_subdivision = fields.Many2One(
+ 'country.subdivision', "From Subdivision", ondelete='RESTRICT',
+ domain=[
+ ('country', '=', Eval('from_country', -1)),
+ ],
+ states={
+ 'invisible': ~Eval('from_country'),
+ },
+ depends=['from_country'],
+ help="Apply only to addresses in this subdivision.")
+ to_country = fields.Many2One(
+ 'country.country', 'To Country', ondelete='RESTRICT',
+ help="Apply only to addresses of this country.")
+ to_subdivision = fields.Many2One(
+ 'country.subdivision', "To Subdivision", ondelete='RESTRICT',
+ domain=[
+ ('country', '=', Eval('to_country', -1)),
+ ],
+ states={
+ 'invisible': ~Eval('to_country'),
+ },
+ depends=['to_country'],
+ help="Apply only to addresses in this subdivision.")
+
+
+class TaxRuleLineTemplate(_TaxRuleLineMixin, metaclass=PoolMeta):
__name__ = 'account.tax.rule.line.template'
- from_country = fields.Many2One('country.country', 'From Country',
- ondelete='RESTRICT')
- to_country = fields.Many2One('country.country', 'To Country',
- ondelete='RESTRICT')
def _get_tax_rule_line_value(self, rule_line=None):
value = super(TaxRuleLineTemplate, self)._get_tax_rule_line_value(
@@ -20,18 +66,37 @@
if not rule_line or rule_line.from_country != self.from_country:
value['from_country'] = (
self.from_country.id if self.from_country else None)
+ if (not rule_line
+ or rule_line.from_subdivision != self.from_subdivision):
+ value['from_subdivision'] = (
+ self.from_subdivision.id if self.from_subdivision else None)
if not rule_line or rule_line.to_country != self.to_country:
value['to_country'] = (
self.to_country.id if self.to_country else None)
+ if (not rule_line
+ or rule_line.to_subdivision != self.to_subdivision):
+ value['to_subdivision'] = (
+ self.to_subdivision.id if self.to_subdivision else None)
return value
-class TaxRuleLine(metaclass=PoolMeta):
+class TaxRuleLine(_TaxRuleLineMixin, metaclass=PoolMeta):
__name__ = 'account.tax.rule.line'
- from_country = fields.Many2One('country.country', 'From Country',
- ondelete='RESTRICT')
- to_country = fields.Many2One('country.country', 'To Country',
- ondelete='RESTRICT')
+
+ def match(self, pattern):
+ for name in ['from_subdivision', 'to_subdivision']:
+ subdivision = getattr(self, name)
+ if name not in pattern:
+ if subdivision:
+ return False
+ else:
+ continue
+ pattern = pattern.copy()
+ subdivisions = pattern.pop(name)
+ if (subdivision is not None
+ and subdivision.id not in subdivisions):
+ return False
+ return super().match(pattern)
class InvoiceLine(metaclass=PoolMeta):
@@ -45,16 +110,28 @@
pattern = super(InvoiceLine, self)._get_tax_rule_pattern()
- from_country, to_country = None, None
+ from_country = from_subdivision = to_country = to_subdivision = None
if isinstance(self.origin, SaleLine):
- if self.origin.warehouse.address:
- from_country = self.origin.warehouse.address.country
- to_country = self.origin.sale.shipment_address.country
+ warehouse_address = self.origin.warehouse.address
+ if warehouse_address:
+ from_country = warehouse_address.country
+ from_subdivision = warehouse_address.subdivision
+ shipment_address = self.origin.sale.shipment_address
+ to_country = shipment_address.country
+ to_subdivision = shipment_address.subdivision
elif isinstance(self.origin, PurchaseLine):
- from_country = self.origin.purchase.invoice_address.country
- if self.origin.purchase.warehouse.address:
- to_country = self.origin.purchase.warehouse.address.country
+ invoice_address = self.origin.purchase.invoice_address
+ from_country = invoice_address.country
+ from_subdivision = invoice_address.subdivision
+ warehouse_address = self.origin.purchase.warehouse.address
+ if warehouse_address:
+ to_country = warehouse_address.country
+ to_subdivision = warehouse_address.subdivision
pattern['from_country'] = from_country.id if from_country else None
+ pattern['from_subdivision'] = (
+ from_subdivision.id if from_subdivision else None)
pattern['to_country'] = to_country.id if to_country else None
+ pattern['to_subdivision'] = (
+ to_subdivision.id if to_subdivision else None)
return pattern
diff -r f9145f64fc84 -r c72071b493f7 doc/index.rst
--- a/doc/index.rst Mon May 06 14:59:51 2019 +0200
+++ b/doc/index.rst Sun Aug 18 19:03:12 2019 +0200
@@ -2,31 +2,33 @@
########################
The account_tax_rule module extends the tax rule to add origin and destination
-countries as criteria.
+countries and subdivisions as criteria.
Tax Rule Line
*************
-Two criteria fields are added:
+Four criteria fields are added:
- From Country: The country of origin
+- From Subdivision: The subdivision of origin
- To Country: The country of destination
+- To Subdivision: The subdivision of destination
The countries are picked from the origin document:
- Sale:
- - The origin country comes from the address of the warehouse.
- - The destination country comes from the shipping address.
+ - The origin country and subdivision come from the address of the warehouse.
+ - The destination country and subdivision come from the shipping address.
- Purchase:
- - The origin country comes from the invoice address.
- - The destination country comes from the address of the warehouse.
+ - The origin country and subdivision come from the invoice address.
+ - The destination country and subdivision come from the address of the
warehouse.
- Stock Consignment:
- - The origin country comes from the warehouse's address of the location or
- the delivery address for returned customer shipment.
- - The destination country comes from the warehouse's address of the location
- or the delivery address for customer shipment.
+ - The origin country and subdivision come from the warehouse's address of the
+ location or the delivery address for returned customer shipment.
+ - The destination country and subdivision come from the warehouse's address
+ of the location or the delivery address for customer shipment.
diff -r f9145f64fc84 -r c72071b493f7 purchase.py
--- a/purchase.py Mon May 06 14:59:51 2019 +0200
+++ b/purchase.py Sun Aug 18 19:03:12 2019 +0200
@@ -28,14 +28,20 @@
def _get_tax_rule_pattern(self):
pattern = super(PurchaseLine, self)._get_tax_rule_pattern()
- from_country, to_country = None, None
+ from_country = from_subdivision = to_country = to_subdivision = None
if self.purchase:
if self.purchase.invoice_address:
from_country = self.purchase.invoice_address.country
+ from_subdivision = self.purchase.invoice_address.subdivision
warehouse = self.purchase.warehouse
if warehouse and warehouse.address:
to_country = warehouse.address.country
+ to_subdivision = warehouse.address.subdivision
pattern['from_country'] = from_country.id if from_country else None
+ pattern['from_subdivision'] = (
+ from_subdivision.id if from_subdivision else None)
pattern['to_country'] = to_country.id if to_country else None
+ pattern['to_subdivision'] = (
+ to_subdivision.id if to_subdivision else None)
return pattern
diff -r f9145f64fc84 -r c72071b493f7 sale.py
--- a/sale.py Mon May 06 14:59:51 2019 +0200
+++ b/sale.py Sun Aug 18 19:03:12 2019 +0200
@@ -31,7 +31,7 @@
pattern = super(SaleLine, self)._get_tax_rule_pattern()
- from_country, to_country = None, None
+ from_country = from_subdivision = to_country = to_subdivision = None
if self.id is None or self.id < 0:
warehouse = self.get_warehouse('warehouse')
if warehouse:
@@ -40,9 +40,15 @@
warehouse = self.warehouse
if warehouse and warehouse.address:
from_country = warehouse.address.country
+ from_subdivision = warehouse.address.subdivision
if self.sale and self.sale.shipment_address:
to_country = self.sale.shipment_address.country
+ to_subdivision = self.sale.shipment_address.subdivision
pattern['from_country'] = from_country.id if from_country else None
+ pattern['from_subdivision'] = (
+ from_subdivision.id if from_subdivision else None)
pattern['to_country'] = to_country.id if to_country else None
+ pattern['to_subdivision'] = (
+ to_subdivision.id if to_subdivision else None)
return pattern
diff -r f9145f64fc84 -r c72071b493f7 stock.py
--- a/stock.py Mon May 06 14:59:51 2019 +0200
+++ b/stock.py Sun Aug 18 19:03:12 2019 +0200
@@ -14,18 +14,30 @@
pattern = super(Move, self)._get_tax_rule_pattern()
- from_country, to_country = None, None
+ from_country = from_subdivision = to_country = to_subdivision = None
if self.from_location.warehouse:
- if self.from_location.warehouse.address:
- from_country = self.from_location.warehouse.address.country
+ warehouse_address = self.from_location.warehouse.address
+ if warehouse_address:
+ from_country = warehouse_address.country
+ from_subdivision = warehouse_address.subdivision
elif isinstance(self.origin, ShipmentOutReturn):
- from_country = self.origin.delivery_address.country
+ delivery_address = self.origin.delivery_address
+ from_country = delivery_address.country
+ from_subdivision = delivery_address.subdivision
if self.to_location.warehouse:
- if self.to_location.warehouse.address:
- to_country = self.to_location.warehouse.address.country
+ warehouse_address = self.to_location.warehouse.address
+ if warehouse_address:
+ to_country = warehouse_address.country
+ to_subdivision = warehouse_address.subdivision
elif isinstance(self.origin, ShipmentOut):
- to_country = self.origin.delivery_address.country
+ delivery_address = self.origin.delivery_address
+ to_country = delivery_address.country
+ to_subdivision = delivery_address.subdivision
pattern['from_country'] = from_country.id if from_country else None
+ pattern['from_subdivision'] = (
+ from_subdivision.id if from_subdivision else None)
pattern['to_country'] = to_country.id if to_country else None
+ pattern['to_subdivision'] = (
+ to_subdivision.id if to_subdivision else None)
return pattern
diff -r f9145f64fc84 -r c72071b493f7 tests/test_account_tax_rule_country.py
--- a/tests/test_account_tax_rule_country.py Mon May 06 14:59:51 2019 +0200
+++ b/tests/test_account_tax_rule_country.py Sun Aug 18 19:03:12 2019 +0200
@@ -32,6 +32,142 @@
update_chart.start.account = root
update_chart.transition_update()
+ @classmethod
+ def _create_countries(cls):
+ pool = Pool()
+ Country = pool.get('country.country')
+ Subdivision = pool.get('country.subdivision')
+
+ country1 = Country(name="Country 1")
+ country1.save()
+ subdivision1 = Subdivision(
+ country=country1, name="Subdivision 1", code="SUB1",
+ type='province')
+ subdivision1.save()
+ subdivision11 = Subdivision(
+ country=country1, parent=subdivision1,
+ name="Sub-Subdivision 1", code="SUBSUB1", type='province')
+ subdivision11.save()
+ country2 = Country(name="Country 2")
+ country2.save()
+ subdivision2 = Subdivision(
+ country=country2, name="Subdivision 2", code="SUB2",
+ type='province')
+ subdivision2.save()
+
+ return [country1, country2]
+
+ def _get_taxes(cls):
+ pool = Pool()
+ Tax = pool.get('account.tax')
+ tax, = Tax.search([])
+ target_tax, = Tax.copy([tax])
+ return [tax, target_tax]
+
+ def _create_rule(cls, lines):
+ pool = Pool()
+ TaxRule = pool.get('account.tax.rule')
+ return TaxRule.create([{
+ 'name': 'Test',
+ 'kind': 'both',
+ 'lines': [('create', lines)],
+ }])[0]
+
+ @with_transaction()
+ def test_tax_rule(self):
+ "Test tax rule"
+ country1, country2 = self._create_countries()[:2]
+ subdivision1 = country1.subdivisions[0]
+ subdivision2 = country2.subdivisions[0]
+ company = create_company()
+ with set_company(company):
+ create_chart(company, tax=True)
+ tax, target_tax = self._get_taxes()[:2]
+ tax_rule = self._create_rule([{
+ 'from_country': country1.id,
+ 'from_subdivision': subdivision1.id,
+ 'to_country': country2.id,
+ 'to_subdivision': subdivision2.id,
+ 'origin_tax': tax.id,
+ 'tax': target_tax.id,
+ }])
+ pattern = {
+ 'from_country': country1.id,
+ 'from_subdivision': subdivision1.id,
+ 'to_country': country2.id,
+ 'to_subdivision': subdivision2.id,
+ }
+
+ self.assertListEqual(tax_rule.apply(tax, pattern), [target_tax.id])
+
+ @with_transaction()
+ def test_tax_rule_children(self):
+ "Test tax rule with children subdivision"
+ country = self._create_countries()[0]
+ parent_subdivision = [
+ s for s in country.subdivisions if not s.parent][0]
+ subdivision = [
+ s for s in country.subdivisions
+ if s.parent == parent_subdivision][0]
+ company = create_company()
+ with set_company(company):
+ create_chart(company, tax=True)
+ tax, target_tax = self._get_taxes()[:2]
+ tax_rule = self._create_rule([{
+ 'to_country': country.id,
+ 'to_subdivision': parent_subdivision.id,
+ 'origin_tax': tax.id,
+ 'tax': target_tax.id,
+ }])
+ pattern = {
+ 'to_country': country.id,
+ 'to_subdivision': subdivision.id,
+ }
+
+ self.assertListEqual(tax_rule.apply(tax, pattern), [target_tax.id])
+
+ @with_transaction()
+ def test_tax_rule_no_subdivision(self):
+ "Test tax rule without subdivision"
+ country = self._create_countries()[0]
+ subdivision = country.subdivisions[0]
+ company = create_company()
+ with set_company(company):
+ create_chart(company, tax=True)
+ tax, target_tax = self._get_taxes()[:2]
+ tax_rule = self._create_rule([{
+ 'to_country': country.id,
+ 'origin_tax': tax.id,
+ 'tax': target_tax.id,
+ }])
+ pattern = {
+ 'to_country': country.id,
+ 'to_subdivision': subdivision.id,
+ }
+
+ self.assertListEqual(tax_rule.apply(tax, pattern), [target_tax.id])
+
+ @with_transaction()
+ def test_tax_rule_no_subdivision_pattern(self):
+ "Test tax rule without subdivision in pattern"
+ country = self._create_countries()[0]
+ subdivision = country.subdivisions[0]
+ company = create_company()
+ with set_company(company):
+ create_chart(company, tax=True)
+ tax, target_tax = self._get_taxes()[:2]
+ tax_rule = self._create_rule([{
+ 'to_country': country.id,
+ 'to_subdivision': subdivision.id,
+ 'origin_tax': tax.id,
+ 'tax': target_tax.id,
+ }])
+ pattern = {
+ 'to_country': country.id,
+ }
+
+ self.assertListEqual(tax_rule.apply(tax, pattern), [tax.id])
+
def suite():
suite = trytond.tests.test_tryton.suite()
diff -r f9145f64fc84 -r c72071b493f7 view/tax_rule_line_form.xml
--- a/view/tax_rule_line_form.xml Mon May 06 14:59:51 2019 +0200
+++ b/view/tax_rule_line_form.xml Sun Aug 18 19:03:12 2019 +0200
@@ -5,7 +5,11 @@
<xpath expr="/form/field[@name='origin_tax']" position="after">
<label name="from_country"/>
<field name="from_country"/>
+ <label name="from_subdivision"/>
+ <field name="from_subdivision"/>
<label name="to_country"/>
<field name="to_country"/>
+ <label name="to_subdivision"/>
+ <field name="to_subdivision"/>
</xpath>
</data>
diff -r f9145f64fc84 -r c72071b493f7 view/tax_rule_line_template_form.xml
--- a/view/tax_rule_line_template_form.xml Mon May 06 14:59:51 2019 +0200
+++ b/view/tax_rule_line_template_form.xml Sun Aug 18 19:03:12 2019 +0200
@@ -5,7 +5,11 @@
<xpath expr="/form/field[@name='origin_tax']" position="after">
<label name="from_country"/>
<field name="from_country"/>
+ <label name="from_subdivision"/>
+ <field name="from_subdivision"/>
<label name="to_country"/>
<field name="to_country"/>
+ <label name="to_subdivision"/>
+ <field name="to_subdivision"/>
</xpath>
</data>
diff -r f9145f64fc84 -r c72071b493f7 view/tax_rule_line_template_tree.xml
--- a/view/tax_rule_line_template_tree.xml Mon May 06 14:59:51 2019 +0200
+++ b/view/tax_rule_line_template_tree.xml Sun Aug 18 19:03:12 2019 +0200
@@ -4,6 +4,8 @@
<data>
<xpath expr="/tree/field[@name='origin_tax']" position="after">
<field name="from_country"/>
+ <field name="from_subdivision"/>
<field name="to_country"/>
+ <field name="to_subdivision"/>
</xpath>
</data>
diff -r f9145f64fc84 -r c72071b493f7 view/tax_rule_line_tree.xml
--- a/view/tax_rule_line_tree.xml Mon May 06 14:59:51 2019 +0200
+++ b/view/tax_rule_line_tree.xml Sun Aug 18 19:03:12 2019 +0200
@@ -4,6 +4,8 @@
<data>
<xpath expr="/tree/field[@name='origin_tax']" position="after">
<field name="from_country"/>
+ <field name="from_subdivision"/>
<field name="to_country"/>
+ <field name="to_subdivision"/>
</xpath>
</data>
diff -r f9145f64fc84 -r c72071b493f7 view/tax_rule_line_tree_sequence.xml
--- a/view/tax_rule_line_tree_sequence.xml Mon May 06 14:59:51 2019 +0200
+++ b/view/tax_rule_line_tree_sequence.xml Sun Aug 18 19:03:12 2019 +0200
@@ -4,6 +4,8 @@
<data>
<xpath expr="/tree/field[@name='origin_tax']" position="after">
<field name="from_country"/>
+ <field name="from_subdivision"/>
<field name="to_country"/>
+ <field name="to_subdivision"/>
</xpath>
</data>