details:   https://code.tryton.org/tryton/commit/73989a49f837
branch:    default
user:      Cédric Krier <[email protected]>
date:      Tue Apr 14 17:29:07 2026 +0200
description:
        Add country organization as criteria of tax rule

        Closes #14771
diffstat:

 modules/account_tax_rule_country/CHANGELOG                            |   1 +
 modules/account_tax_rule_country/account.py                           |  70 
+++++++++-
 modules/account_tax_rule_country/doc/design.rst                       |   5 +-
 modules/account_tax_rule_country/tests/test_module.py                 |  36 
+++++
 modules/account_tax_rule_country/view/tax_rule_line_form.xml          |   9 +
 modules/account_tax_rule_country/view/tax_rule_line_template_form.xml |   9 +
 modules/account_tax_rule_country/view/tax_rule_line_template_tree.xml |   2 +
 modules/account_tax_rule_country/view/tax_rule_line_tree.xml          |   2 +
 modules/account_tax_rule_country/view/tax_rule_line_tree_sequence.xml |   2 +
 9 files changed, 129 insertions(+), 7 deletions(-)

diffs (288 lines):

diff -r b0af9504b4a6 -r 73989a49f837 modules/account_tax_rule_country/CHANGELOG
--- a/modules/account_tax_rule_country/CHANGELOG        Mon Apr 13 13:12:59 
2026 +0200
+++ b/modules/account_tax_rule_country/CHANGELOG        Tue Apr 14 17:29:07 
2026 +0200
@@ -1,3 +1,4 @@
+* Add organization as criteria
 
 Version 8.0.0 - 2026-04-20
 --------------------------
diff -r b0af9504b4a6 -r 73989a49f837 modules/account_tax_rule_country/account.py
--- a/modules/account_tax_rule_country/account.py       Mon Apr 13 13:12:59 
2026 +0200
+++ b/modules/account_tax_rule_country/account.py       Tue Apr 14 17:29:07 
2026 +0200
@@ -3,7 +3,8 @@
 
 from trytond.model import fields
 from trytond.pool import Pool, PoolMeta
-from trytond.pyson import Eval
+from trytond.pyson import Bool, Eval, If
+from trytond.transaction import Transaction
 
 
 class TaxRule(metaclass=PoolMeta):
@@ -32,9 +33,29 @@
 
 class _TaxRuleLineMixin:
     __slots__ = ()
+
+    from_organization = fields.Many2One(
+        'country.organization', "From Organization", ondelete='RESTRICT',
+        domain=[
+            If(Eval('from_country'),
+                ('id', '=', -1),
+                ()),
+            ],
+        states={
+            'invisible': Bool(Eval('from_country')),
+            },
+        help="Apply only to the addresses from this organization.")
     from_country = fields.Many2One(
         'country.country', 'From Country', ondelete='RESTRICT',
-        help="Apply only to addresses of this country.")
+        domain=[
+            If(Eval('from_organization'),
+                ('id', '=', -1),
+                ()),
+            ],
+        states={
+            'invisible': Bool(Eval('from_organization')),
+            },
+        help="Apply only to the addresses from this country.")
     from_subdivision = fields.Many2One(
         'country.subdivision', "From Subdivision", ondelete='RESTRICT',
         domain=[
@@ -43,10 +64,29 @@
         states={
             'invisible': ~Eval('from_country'),
             },
-        help="Apply only to addresses in this subdivision.")
+        help="Apply only to the addresses from this subdivision.")
+    to_organization = fields.Many2One(
+        'country.organization', "To Organization", ondelete='RESTRICT',
+        domain=[
+            If(Eval('to_country'),
+                ('id', '=', -1),
+                ()),
+            ],
+        states={
+            'invisible': Bool(Eval('to_country')),
+            },
+        help="Apply only to the addresses of this organization.")
     to_country = fields.Many2One(
         'country.country', 'To Country', ondelete='RESTRICT',
-        help="Apply only to addresses of this country.")
+        domain=[
+            If(Eval('to_organization'),
+                ('id', '=', -1),
+                ()),
+            ],
+        states={
+            'invisible': Bool(Eval('to_organization')),
+            },
+        help="Apply only to the addresses to this country.")
     to_subdivision = fields.Many2One(
         'country.subdivision', "To Subdivision", ondelete='RESTRICT',
         domain=[
@@ -55,7 +95,7 @@
         states={
             'invisible': ~Eval('to_country'),
             },
-        help="Apply only to addresses in this subdivision.")
+        help="Apply only to the addresses to this subdivision.")
 
 
 class TaxRuleLineTemplate(_TaxRuleLineMixin, metaclass=PoolMeta):
@@ -85,6 +125,26 @@
     __name__ = 'account.tax.rule.line'
 
     def match(self, pattern):
+        pool = Pool()
+        Date = pool.get('ir.date')
+        Country = pool.get('country.country')
+
+        with Transaction().set_context(company=self.rule.company.id):
+            date = pattern.get('date') or Date.today()
+
+        for organization, name in [
+                (self.from_organization, 'from_country'),
+                (self.to_organization, 'to_country'),
+                ]:
+            if organization:
+                pattern = pattern.copy()
+                country = pattern.pop(name, None)
+                if not country:
+                    return False
+                country = Country(country)
+                if not country.is_member(organization, date=date):
+                    return False
+
         for name in ['from_subdivision', 'to_subdivision']:
             subdivision = getattr(self, name)
             if name not in pattern:
diff -r b0af9504b4a6 -r 73989a49f837 
modules/account_tax_rule_country/doc/design.rst
--- a/modules/account_tax_rule_country/doc/design.rst   Mon Apr 13 13:12:59 
2026 +0200
+++ b/modules/account_tax_rule_country/doc/design.rst   Tue Apr 14 17:29:07 
2026 +0200
@@ -11,8 +11,9 @@
 
 When the *Account Tax Rule Country Module* is activated, the *Tax Rule* gains
 additional criteria for the origin and destination `Country
-<country:model-country.country>` and `Subdivision
-<country:model-country.subdivision>` to match against.
+<country:model-country.country>`, `Subdivision
+<country:model-country.subdivision>` and `Organization
+<country:model-country.organization>` to match against.
 
 The countries and subdivisions are taken from the origin document that applies
 the tax rule:
diff -r b0af9504b4a6 -r 73989a49f837 
modules/account_tax_rule_country/tests/test_module.py
--- a/modules/account_tax_rule_country/tests/test_module.py     Mon Apr 13 
13:12:59 2026 +0200
+++ b/modules/account_tax_rule_country/tests/test_module.py     Tue Apr 14 
17:29:07 2026 +0200
@@ -35,10 +35,14 @@
     @classmethod
     def _create_countries(cls):
         pool = Pool()
+        Organization = pool.get('country.organization')
         Country = pool.get('country.country')
         Subdivision = pool.get('country.subdivision')
 
+        organization1 = Organization(name="Org 1")
+        organization1.save()
         country1 = Country(name="Country 1")
+        country1.members = [{'organization': organization1}]
         country1.save()
         subdivision1 = Subdivision(
             country=country1, name="Subdivision 1", code="SUB1",
@@ -48,7 +52,11 @@
             country=country1, parent=subdivision1,
             name="Sub-Subdivision 1", code="SUBSUB1", type='province')
         subdivision11.save()
+
+        organization2 = Organization(name="Org 2")
+        organization2.save()
         country2 = Country(name="Country 2")
+        country2.members = [{'organization': organization2}]
         country2.save()
         subdivision2 = Subdivision(
             country=country2, name="Subdivision 2", code="SUB2",
@@ -127,6 +135,34 @@
             self.assertListEqual(tax_rule.apply(tax, pattern), [target_tax.id])
 
     @with_transaction()
+    def test_tax_rule_organization(self):
+        "Test tax rule with organization"
+        country1, country2 = self._create_countries()
+        organization1 = country1.organizations[0]
+        organization2 = country2.organizations[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_organization': organization1.id,
+                        'to_organization': organization2.id,
+                        'origin_tax': tax.id,
+                        'tax': target_tax.id,
+                        }])
+
+            for pattern, result in [
+                    ({'from_country': country1.id, 'to_country': country2.id},
+                        [target_tax.id]),
+                    ({'from_country': country2.id, 'to_country': country1.id},
+                        [tax.id]),
+                    ({'from_country': None, 'to_country': country2.id},
+                        [tax.id]),
+                    ]:
+                with self.subTest(pattern=pattern):
+                    self.assertListEqual(tax_rule.apply(tax, pattern), result)
+
+    @with_transaction()
     def test_tax_rule_no_subdivision(self):
         "Test tax rule without subdivision"
         country = self._create_countries()[0]
diff -r b0af9504b4a6 -r 73989a49f837 
modules/account_tax_rule_country/view/tax_rule_line_form.xml
--- a/modules/account_tax_rule_country/view/tax_rule_line_form.xml      Mon Apr 
13 13:12:59 2026 +0200
+++ b/modules/account_tax_rule_country/view/tax_rule_line_form.xml      Tue Apr 
14 17:29:07 2026 +0200
@@ -3,10 +3,19 @@
 this repository contains the full copyright notices and license terms. -->
 <data>
     <xpath expr="/form/field[@name='origin_tax']" position="after">
+        <label name="from_organization"/>
+        <field name="from_organization"/>
+        <newline/>
+
         <label name="from_country"/>
         <field name="from_country"/>
         <label name="from_subdivision"/>
         <field name="from_subdivision"/>
+
+        <label name="to_organization"/>
+        <field name="to_organization"/>
+        <newline/>
+
         <label name="to_country"/>
         <field name="to_country"/>
         <label name="to_subdivision"/>
diff -r b0af9504b4a6 -r 73989a49f837 
modules/account_tax_rule_country/view/tax_rule_line_template_form.xml
--- a/modules/account_tax_rule_country/view/tax_rule_line_template_form.xml     
Mon Apr 13 13:12:59 2026 +0200
+++ b/modules/account_tax_rule_country/view/tax_rule_line_template_form.xml     
Tue Apr 14 17:29:07 2026 +0200
@@ -3,10 +3,19 @@
 this repository contains the full copyright notices and license terms. -->
 <data>
     <xpath expr="/form/field[@name='origin_tax']" position="after">
+        <label name="from_organization"/>
+        <field name="from_organization"/>
+        <newline/>
+
         <label name="from_country"/>
         <field name="from_country"/>
         <label name="from_subdivision"/>
         <field name="from_subdivision"/>
+
+        <label name="to_organization"/>
+        <field name="to_organization"/>
+        <newline/>
+
         <label name="to_country"/>
         <field name="to_country"/>
         <label name="to_subdivision"/>
diff -r b0af9504b4a6 -r 73989a49f837 
modules/account_tax_rule_country/view/tax_rule_line_template_tree.xml
--- a/modules/account_tax_rule_country/view/tax_rule_line_template_tree.xml     
Mon Apr 13 13:12:59 2026 +0200
+++ b/modules/account_tax_rule_country/view/tax_rule_line_template_tree.xml     
Tue Apr 14 17:29:07 2026 +0200
@@ -3,8 +3,10 @@
 this repository contains the full copyright notices and license terms. -->
 <data>
     <xpath expr="/tree/field[@name='origin_tax']" position="after">
+        <field name="from_organization" expand="1"/>
         <field name="from_country" expand="1"/>
         <field name="from_subdivision" expand="1"/>
+        <field name="to_organization" expand="1"/>
         <field name="to_country" expand="1"/>
         <field name="to_subdivision" expand="1"/>
     </xpath>
diff -r b0af9504b4a6 -r 73989a49f837 
modules/account_tax_rule_country/view/tax_rule_line_tree.xml
--- a/modules/account_tax_rule_country/view/tax_rule_line_tree.xml      Mon Apr 
13 13:12:59 2026 +0200
+++ b/modules/account_tax_rule_country/view/tax_rule_line_tree.xml      Tue Apr 
14 17:29:07 2026 +0200
@@ -3,8 +3,10 @@
 this repository contains the full copyright notices and license terms. -->
 <data>
     <xpath expr="/tree/field[@name='origin_tax']" position="after">
+        <field name="from_organization" expand="1"/>
         <field name="from_country" expand="1"/>
         <field name="from_subdivision" expand="1"/>
+        <field name="to_organization" expand="1"/>
         <field name="to_country" expand="1"/>
         <field name="to_subdivision" expand="1"/>
     </xpath>
diff -r b0af9504b4a6 -r 73989a49f837 
modules/account_tax_rule_country/view/tax_rule_line_tree_sequence.xml
--- a/modules/account_tax_rule_country/view/tax_rule_line_tree_sequence.xml     
Mon Apr 13 13:12:59 2026 +0200
+++ b/modules/account_tax_rule_country/view/tax_rule_line_tree_sequence.xml     
Tue Apr 14 17:29:07 2026 +0200
@@ -3,8 +3,10 @@
 this repository contains the full copyright notices and license terms. -->
 <data>
     <xpath expr="/tree/field[@name='origin_tax']" position="after">
+        <field name="from_organization" expand="1"/>
         <field name="from_country" expand="1"/>
         <field name="from_subdivision" expand="1"/>
+        <field name="to_organization" expand="1"/>
         <field name="to_country" expand="1"/>
         <field name="to_subdivision" expand="1"/>
     </xpath>

Reply via email to