From c5266656843cfb6cfc6e52f5ff673b9595a557ae Mon Sep 17 00:00:00 2001 From: Jan Suhr Date: Wed, 1 Jul 2026 13:07:54 +0200 Subject: [PATCH] add base_customs_territory --- base_customs_territory/README.rst | 23 +++++++++++++++++++ base_customs_territory/__init__.py | 1 + base_customs_territory/__manifest__.py | 15 ++++++++++++ .../data/res_country_state_data.xml | 22 ++++++++++++++++++ base_customs_territory/models/__init__.py | 1 + .../models/res_country_state.py | 13 +++++++++++ delivery_dhl_parcel_de/__manifest__.py | 1 + .../models/delivery_carrier.py | 5 +++- 8 files changed, 80 insertions(+), 1 deletion(-) create mode 100644 base_customs_territory/README.rst create mode 100644 base_customs_territory/__init__.py create mode 100644 base_customs_territory/__manifest__.py create mode 100644 base_customs_territory/data/res_country_state_data.xml create mode 100644 base_customs_territory/models/__init__.py create mode 100644 base_customs_territory/models/res_country_state.py diff --git a/base_customs_territory/README.rst b/base_customs_territory/README.rst new file mode 100644 index 00000000..5f23b16d --- /dev/null +++ b/base_customs_territory/README.rst @@ -0,0 +1,23 @@ +====================== +Base Customs Territory +====================== + +Some states or regions belong politically to a country that is part of a +customs union (e.g. the EU), but are themselves excluded from that customs +territory. Examples include the Canary Islands, Ceuta and Melilla (Spain). +Shipments to these regions require customs export documentation just like +shipments to third countries, even though the destination country itself is +a member of the customs union. + +This module adds an ``outside_customs_territory`` boolean field to +``res.country.state`` and pre-populates it for the known exceptions: + +* **Canary Islands** – Las Palmas, Santa Cruz de Tenerife +* **Ceuta** +* **Melilla** + +The flag is admin-editable on the state record, so future exceptions can be +activated without any code change. + +Carrier integrations can use this flag to override country-group-based +decisions. \ No newline at end of file diff --git a/base_customs_territory/__init__.py b/base_customs_territory/__init__.py new file mode 100644 index 00000000..0650744f --- /dev/null +++ b/base_customs_territory/__init__.py @@ -0,0 +1 @@ +from . import models diff --git a/base_customs_territory/__manifest__.py b/base_customs_territory/__manifest__.py new file mode 100644 index 00000000..97a7dc53 --- /dev/null +++ b/base_customs_territory/__manifest__.py @@ -0,0 +1,15 @@ +{ + "name": "Base Customs Territory", + "version": "18.0.1.0.0", + "category": "Base", + "summary": "Flag states/regions outside the customs territory of their country", + "license": "AGPL-3", + "author": "Nitrokey", + "website": "https://github.com/Nitrokey/odoo-modules/", + "maintainer": ["initOS", "Nitrokey"], + "depends": ["base"], + "data": [ + "data/res_country_state_data.xml", + ], + "installable": True, +} diff --git a/base_customs_territory/data/res_country_state_data.xml b/base_customs_territory/data/res_country_state_data.xml new file mode 100644 index 00000000..cc5ef6b3 --- /dev/null +++ b/base_customs_territory/data/res_country_state_data.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/base_customs_territory/models/__init__.py b/base_customs_territory/models/__init__.py new file mode 100644 index 00000000..a2ed07dd --- /dev/null +++ b/base_customs_territory/models/__init__.py @@ -0,0 +1 @@ +from . import res_country_state diff --git a/base_customs_territory/models/res_country_state.py b/base_customs_territory/models/res_country_state.py new file mode 100644 index 00000000..85f44a17 --- /dev/null +++ b/base_customs_territory/models/res_country_state.py @@ -0,0 +1,13 @@ +from odoo import fields, models + + +class ResCountryState(models.Model): + _inherit = "res.country.state" + + outside_customs_territory = fields.Boolean( + default=False, + help="When set, this state/region is treated as outside the customs " + "territory of its parent country, even if that country belongs to a " + "customs union (e.g. the EU). Carrier integrations use this flag to " + "require customs export documentation for shipments to this region.", + ) diff --git a/delivery_dhl_parcel_de/__manifest__.py b/delivery_dhl_parcel_de/__manifest__.py index 773f0e0e..c4bf1666 100644 --- a/delivery_dhl_parcel_de/__manifest__.py +++ b/delivery_dhl_parcel_de/__manifest__.py @@ -12,6 +12,7 @@ "stock_delivery", "delivery_carrier_account", "base_iso3166", + "base_customs_territory", "stock_picking_declared_value", "mrp", "product_harmonized_system", diff --git a/delivery_dhl_parcel_de/models/delivery_carrier.py b/delivery_dhl_parcel_de/models/delivery_carrier.py index 00641300..fd18148c 100644 --- a/delivery_dhl_parcel_de/models/delivery_carrier.py +++ b/delivery_dhl_parcel_de/models/delivery_carrier.py @@ -361,7 +361,10 @@ def dhl_parcel_de_provider_get_package_info(self, picking, insurance_value): } europe_group_id = self.env.ref("base.europe") - if recipient_address_id.country_id not in europe_group_id.country_ids: + if ( + recipient_address_id.country_id not in europe_group_id.country_ids + or recipient_address_id.state_id.outside_customs_territory + ): product_data = self.prepare_product_data_request(picking) company = self.company_id or self.env.company currency = company.currency_id and company.currency_id.name