details: https://code.tryton.org/tryton/commit/afe3d31157bf
branch: default
user: Maxime Richez <[email protected]>
date: Fri Jan 30 17:32:18 2026 +0100
description:
Set incoterm required on extended intrastat for sale between EU
countries
Closes #14531
diffstat:
modules/account_stock_eu/sale.py | 6 +-
modules/account_stock_eu/tests/test_module.py | 116 +++++++++++++++++++++++++-
modules/incoterm/tests/test_module.py | 63 +++++++++++++-
3 files changed, 180 insertions(+), 5 deletions(-)
diffs (224 lines):
diff -r b85cb46ab892 -r afe3d31157bf modules/account_stock_eu/sale.py
--- a/modules/account_stock_eu/sale.py Fri Jan 30 15:24:46 2026 +0100
+++ b/modules/account_stock_eu/sale.py Fri Jan 30 17:32:18 2026 +0100
@@ -42,7 +42,7 @@
self.sale_date)
else:
to_europe = None
- required = (
- (from_country != to_country)
- and not (from_europe and to_europe))
+ if (from_country != to_country
+ and from_europe and to_europe):
+ required = True
return required
diff -r b85cb46ab892 -r afe3d31157bf
modules/account_stock_eu/tests/test_module.py
--- a/modules/account_stock_eu/tests/test_module.py Fri Jan 30 15:24:46
2026 +0100
+++ b/modules/account_stock_eu/tests/test_module.py Fri Jan 30 17:32:18
2026 +0100
@@ -1,6 +1,10 @@
# This file is part of Tryton. The COPYRIGHT file at the top level of
# this repository contains the full copyright notices and license terms.
-from trytond.tests.test_tryton import ModuleTestCase
+from unittest.mock import Mock, patch
+
+from trytond.modules.account.exceptions import FiscalYearNotFoundError
+from trytond.pool import Pool
+from trytond.tests.test_tryton import ModuleTestCase, with_transaction
class AccountStockEuTestCase(ModuleTestCase):
@@ -8,5 +12,115 @@
module = 'account_stock_eu'
extras = ['carrier', 'incoterm', 'production', 'stock_consignment']
+ @with_transaction()
+ def test_sale_incoterm_required(self):
+ "Test incoterm required on sale"
+ pool = Pool()
+ Sale = pool.get('sale.sale')
+ FiscalYear = pool.get('account.fiscalyear')
+ Country = pool.get('country.country')
+
+ from_country = Mock(spec=Country)
+ to_country = Mock(spec=Country)
+ sale = Mock(spec=Sale)
+ sale.warehouse.address.country = from_country
+ sale.shipment_address.country = to_country
+ type(sale)._incoterm_required = Sale._incoterm_required
+ fiscalyear = Mock(spec=FiscalYear)
+
+ with patch.object(FiscalYear, 'find') as fiscalyear_find:
+ fiscalyear_find.return_value = fiscalyear
+ for extended, from_europe, to_europe, result in [
+ (False, False, False, True),
+ (False, False, True, True),
+ (False, True, False, True),
+ (False, True, True, False),
+ (True, False, False, True),
+ (True, False, True, True),
+ (True, True, False, True),
+ (True, True, True, True),
+ (None, False, False, True),
+ (None, False, True, True),
+ (None, True, False, True),
+ (None, True, True, False),
+ ]:
+ if extended is not None:
+ fiscalyear_find.side_effect = None
+ fiscalyear.intrastat_extended = extended
+ else:
+ fiscalyear_find.side_effect = FiscalYearNotFoundError('')
+ from_country.is_member.return_value = from_europe
+ to_country.is_member.return_value = to_europe
+ with self.subTest(
+ extended=extended,
+ from_europe=from_europe,
+ to_europe=to_europe):
+ self.assertEqual(sale._incoterm_required, result)
+
+ @with_transaction()
+ def test_sale_incoterm_required_same_country(self):
+ "Test incoterm required on sale with same country"
+ pool = Pool()
+ Sale = pool.get('sale.sale')
+ FiscalYear = pool.get('account.fiscalyear')
+ Country = pool.get('country.country')
+
+ country = Mock(spec=Country)
+ sale = Mock(spec=Sale)
+ sale.warehouse.address.country = country
+ sale.shipment_address.country = country
+ type(sale)._incoterm_required = Sale._incoterm_required
+ fiscalyear = Mock(spec=FiscalYear)
+
+ with patch.object(FiscalYear, 'find') as fiscalyear_find:
+ fiscalyear_find.return_value = fiscalyear
+ for extended, europe, result in [
+ (False, False, False),
+ (False, True, False),
+ (True, False, False),
+ (True, True, False),
+ (None, False, False),
+ (None, True, False),
+ ]:
+ if extended is not None:
+ fiscalyear_find.side_effect = None
+ fiscalyear.intrastat_extended = extended
+ else:
+ fiscalyear_find.side_effect = FiscalYearNotFoundError('')
+ country.is_member.return_value = europe
+ with self.subTest(
+ extended=extended,
+ europe=europe):
+ self.assertEqual(sale._incoterm_required, result)
+
+ @with_transaction()
+ def test_sale_incoterm_required_no_country(self):
+ "Test incoterm required on sale without country"
+ pool = Pool()
+ Sale = pool.get('sale.sale')
+ FiscalYear = pool.get('account.fiscalyear')
+
+ sale = Mock(spec=Sale)
+ sale.warehouse.address.country = None
+ sale.shipment_address.country = None
+ type(sale)._incoterm_required = Sale._incoterm_required
+ fiscalyear = Mock(spec=FiscalYear)
+
+ with patch.object(FiscalYear, 'find') as fiscalyear_find:
+ fiscalyear_find.return_value = fiscalyear
+
+ for extended, result in [
+ (False, False),
+ (True, False),
+ (None, False),
+ ]:
+ if extended is not None:
+ fiscalyear_find.side_effect = None
+ fiscalyear.intrastat_extended = extended
+ else:
+ fiscalyear_find.side_effect = FiscalYearNotFoundError('')
+ with self.subTest(extended=extended):
+ self.assertEqual(sale._incoterm_required, result)
+
del ModuleTestCase
diff -r b85cb46ab892 -r afe3d31157bf modules/incoterm/tests/test_module.py
--- a/modules/incoterm/tests/test_module.py Fri Jan 30 15:24:46 2026 +0100
+++ b/modules/incoterm/tests/test_module.py Fri Jan 30 17:32:18 2026 +0100
@@ -1,7 +1,7 @@
# This file is part of Tryton. The COPYRIGHT file at the top level of
# this repository contains the full copyright notices and license terms.
-from unittest.mock import MagicMock
+from unittest.mock import MagicMock, Mock
from trytond.modules.company.tests import CompanyTestMixin
from trytond.pool import Pool
@@ -32,5 +32,66 @@
self.assertLessEqual({'incoterm', 'incoterm_location'}, fields)
self.assertLessEqual(fields, ShipmentOut._fields.keys())
+ @with_transaction()
+ def test_sale_incoterm_required(self):
+ "Test incoterm required on sale"
+ pool = Pool()
+ Sale = pool.get('sale.sale')
+ Country = pool.get('country.country')
+
+ from_country = Mock(spec=Country)
+ to_country = Mock(spec=Country)
+ sale = Mock(spec=Sale)
+ sale.warehouse.address.country = from_country
+ sale.shipment_address.country = to_country
+ type(sale)._incoterm_required = Sale._incoterm_required
+
+ for from_europe, to_europe, result in [
+ (False, False, True),
+ (False, True, True),
+ (True, False, True),
+ (True, True, False),
+ ]:
+ from_country.is_member.return_value = from_europe
+ to_country.is_member.return_value = to_europe
+ with self.subTest(
+ from_europe=from_europe,
+ to_europe=to_europe):
+ self.assertEqual(sale._incoterm_required, result)
+
+ @with_transaction()
+ def test_sale_incoterm_required_same_country(self):
+ "Test incoterm required on sale with same country"
+ pool = Pool()
+ Sale = pool.get('sale.sale')
+ Country = pool.get('country.country')
+
+ country = Mock(spec=Country)
+ sale = Mock(spec=Sale)
+ sale.warehouse.address.country = country
+ sale.shipment_address.country = country
+ type(sale)._incoterm_required = Sale._incoterm_required
+
+ for europe, result in [
+ (False, False),
+ (True, False),
+ ]:
+ country.is_member.return_value = europe
+ with self.subTest(europe=europe):
+ self.assertEqual(sale._incoterm_required, result)
+
+ @with_transaction()
+ def test_sale_incoterm_required_no_country(self):
+ "Test incoterm required on sale without country"
+ pool = Pool()
+ Sale = pool.get('sale.sale')
+
+ sale = Mock(spec=Sale)
+ sale.warehouse.address.country = None
+ sale.shipment_address.country = None
+ type(sale)._incoterm_required = Sale._incoterm_required
+
+ self.assertFalse(sale._incoterm_required)
+
del ModuleTestCase