details: https://code.tryton.org/tryton/commit/06e183433933
branch: default
user: Cédric Krier <[email protected]>
date: Fri Jan 16 16:56:47 2026 +0100
description:
Format the party's identifier codes
Closes #14485
diffstat:
modules/account_be/account.py | 4 +-
modules/account_export_winbooks/account.py | 4 +-
modules/account_export_winbooks/party.py | 12 +-
modules/account_invoice/tests/scenario_invoice.rst | 2 +-
modules/account_payment_sepa/party.py | 2 +-
modules/account_stock_eu/stock.py | 2 +-
modules/document_incoming_ocr/document.py | 14 +-
modules/edocument_peppol/account.py | 2 +-
modules/edocument_ubl/edocument.py | 6 +-
modules/edocument_ubl/template/2/base.xml | 6 +-
modules/edocument_uncefact/template/16B-CII/CrossIndustryInvoice.xml | 2 +-
modules/party/CHANGELOG | 1 +
modules/party/party.py | 76
+++++++++-
modules/stock_package_shipping_dpd/stock.py | 4 +-
modules/stock_package_shipping_sendcloud/stock.py | 10 +-
modules/web_shop_vue_storefront/party.py | 2 +-
modules/web_shop_vue_storefront/web.py | 2 +-
17 files changed, 115 insertions(+), 36 deletions(-)
diffs (435 lines):
diff -r c81ff0ed70bc -r 06e183433933 modules/account_be/account.py
--- a/modules/account_be/account.py Fri Jan 16 17:01:25 2026 +0100
+++ b/modules/account_be/account.py Fri Jan 16 16:56:47 2026 +0100
@@ -81,10 +81,10 @@
where = ((invoice.company == context.get('company'))
& (period.fiscalyear == context.get('fiscalyear')))
where &= invoice.type == 'out'
- where &= ((company_identifier.code.ilike('BE%')
+ where &= ((company_identifier.code_compact.ilike('BE%')
& (company_identifier.type == 'eu_vat'))
| (company_identifier.type == 'be_vat'))
- where &= ((party_identifier.code.ilike('BE%')
+ where &= ((party_identifier.code_compact.ilike('BE%')
& (party_identifier.type == 'eu_vat'))
| (party_identifier.type == 'be_vat'))
where &= tax.group.in_(groups)
diff -r c81ff0ed70bc -r 06e183433933 modules/account_export_winbooks/account.py
--- a/modules/account_export_winbooks/account.py Fri Jan 16 17:01:25
2026 +0100
+++ b/modules/account_export_winbooks/account.py Fri Jan 16 16:56:47
2026 +0100
@@ -309,11 +309,11 @@
if line.account.type.receivable and line.party:
doctype = DOCTYPE.CUSTOMER
if identifier := line.party.winbooks_customer_identifier:
- accountrp = identifier.code
+ accountrp = identifier.code_compact
elif line.account.type.payable and line.party:
doctype = DOCTYPE.SUPPLIER
if identifier := line.party.winbooks_supplier_identifier:
- accountrp = identifier.code
+ accountrp = identifier.code_compact
else:
doctype = DOCTYPE.GENERAL
return {
diff -r c81ff0ed70bc -r 06e183433933 modules/account_export_winbooks/party.py
--- a/modules/account_export_winbooks/party.py Fri Jan 16 17:01:25 2026 +0100
+++ b/modules/account_export_winbooks/party.py Fri Jan 16 16:56:47 2026 +0100
@@ -54,11 +54,19 @@
& (t.active == Literal(True))),
'account_export_winbooks.'
'msg_party_identifier_winbooks_party_unique'),
- ('winbooks_code_unique',
- Exclude(t, (t.code, Equal),
+ ('winbooks_code_compact_unique',
+ Exclude(t, (t.code_compact, Equal),
where=t.type.in_(
['winbooks_supplier', 'winbooks_customer'])
& (t.active == Literal(True))),
'account_export_winbooks.'
'msg_party_identifier_winbooks_code_unique'),
]
+
+ @classmethod
+ def __register__(cls, module):
+ table_h = cls.__table_handler__(module)
+ super().__register__(module)
+
+ # Migration from 7.8: replace winbooks_code_unique
+ table_h.drop_constraint('winbooks_code_unique')
diff -r c81ff0ed70bc -r 06e183433933
modules/account_invoice/tests/scenario_invoice.rst
--- a/modules/account_invoice/tests/scenario_invoice.rst Fri Jan 16
17:01:25 2026 +0100
+++ b/modules/account_invoice/tests/scenario_invoice.rst Fri Jan 16
16:56:47 2026 +0100
@@ -189,7 +189,7 @@
>>> assertEqual(invoice.posted_by, employee)
>>> invoice.state
'posted'
- >>> invoice.tax_identifier.code
+ >>> invoice.tax_identifier.code_compact
'BE0897290877'
>>> assertTrue(iso11649.validate(invoice.customer_payment_reference))
>>> bool(invoice.has_report_cache)
diff -r c81ff0ed70bc -r 06e183433933 modules/account_payment_sepa/party.py
--- a/modules/account_payment_sepa/party.py Fri Jan 16 17:01:25 2026 +0100
+++ b/modules/account_payment_sepa/party.py Fri Jan 16 16:56:47 2026 +0100
@@ -28,7 +28,7 @@
def get_sepa_creditor_identifier_used(self, name):
for identifier in self.identifiers:
if identifier.type == 'eu_at_02':
- return identifier.code
+ return identifier.code_compact
def get_sepa_identifier(self, name):
pool = Pool()
diff -r c81ff0ed70bc -r 06e183433933 modules/account_stock_eu/stock.py
--- a/modules/account_stock_eu/stock.py Fri Jan 16 17:01:25 2026 +0100
+++ b/modules/account_stock_eu/stock.py Fri Jan 16 16:56:47 2026 +0100
@@ -319,7 +319,7 @@
if not fallback:
fallback = identifier
if (self.intrastat_country
- and identifier.code.startswith(
+ and identifier.code_compact.startswith(
self.intrastat_country.code)):
break
else:
diff -r c81ff0ed70bc -r 06e183433933 modules/document_incoming_ocr/document.py
--- a/modules/document_incoming_ocr/document.py Fri Jan 16 17:01:25 2026 +0100
+++ b/modules/document_incoming_ocr/document.py Fri Jan 16 16:56:47 2026 +0100
@@ -91,7 +91,9 @@
tax_identifier_types = Party.tax_identifier_types()
for identifier in invoice.party.identifiers:
if (identifier.type in tax_identifier_types
- and identifier.code == tax_identifier):
+ and (
+ identifier.code == tax_identifier
+ or identifier.code_compact == tax_identifier)):
invoice.party_tax_identifier = identifier
currency = invoice_data.get('currency')
@@ -302,7 +304,10 @@
tax_identifier = invoice_data.get('company_tax_identifier')
if tax_identifier:
identifiers = Identifier.search([
- ('code', '=', tax_identifier),
+ ['OR',
+ ('code', '=', tax_identifier),
+ ('code_compact', '=', tax_identifier),
+ ],
('type', 'in', Party.tax_identifier_types()),
])
if len(identifiers) == 1:
@@ -342,7 +347,10 @@
tax_identifier = invoice_data.get('tax_identifier')
if tax_identifier:
identifiers = Identifier.search([
- ('code', '=', tax_identifier),
+ ['OR',
+ ('code', '=', tax_identifier),
+ ('code_compact', '=', tax_identifier),
+ ],
('type', 'in', Party.tax_identifier_types()),
])
if len(identifiers) == 1:
diff -r c81ff0ed70bc -r 06e183433933 modules/edocument_peppol/account.py
--- a/modules/edocument_peppol/account.py Fri Jan 16 17:01:25 2026 +0100
+++ b/modules/edocument_peppol/account.py Fri Jan 16 16:56:47 2026 +0100
@@ -24,7 +24,7 @@
return (identifier
and (identifier.type == 'be_vat'
or (identifier.type == 'eu_vat'
- and identifier.code.startswith('BE'))))
+ and identifier.code_compact.startswith('BE'))))
if (self.invoice_date
and self.invoice_date.year >= 2026
and is_be_vat(self.tax_identifier)
diff -r c81ff0ed70bc -r 06e183433933 modules/edocument_ubl/edocument.py
--- a/modules/edocument_ubl/edocument.py Fri Jan 16 17:01:25 2026 +0100
+++ b/modules/edocument_ubl/edocument.py Fri Jan 16 16:56:47 2026 +0100
@@ -881,7 +881,7 @@
):
if identifier.text:
domain = [
- ('code', '=', identifier.text),
+ ('code_compact', '=', identifier.text),
]
if schemeId := identifier.get('schemeID'):
if type := ISO6523.get(schemeId):
@@ -999,7 +999,7 @@
for identifier in party.identifiers:
if (identifier.type in tax_identifier_types
and identifier.iso_6523 == scheme_id
- and identifier.code == value):
+ and identifier.code_compact == value):
return identifier
else:
if create and scheme_id in ISO6523:
@@ -1042,7 +1042,7 @@
):
if identifier.text:
domain = [
- ('code', '=', identifier.text),
+ ('code_compact', '=', identifier.text),
]
if schemeId := identifier.get('schemeID'):
if type := ISO6523.get(schemeId):
diff -r c81ff0ed70bc -r 06e183433933 modules/edocument_ubl/template/2/base.xml
--- a/modules/edocument_ubl/template/2/base.xml Fri Jan 16 17:01:25 2026 +0100
+++ b/modules/edocument_ubl/template/2/base.xml Fri Jan 16 16:56:47 2026 +0100
@@ -13,7 +13,7 @@
</py:with>
<py:for each="identifier in identifications or party.identifiers">
<cac:PartyIdentification py:if="identifier and
identifier.iso_6523">
- <cbc:ID py:attrs="{'schemeID':
identifier.iso_6523}">${identifier.code}</cbc:ID>
+ <cbc:ID py:attrs="{'schemeID':
identifier.iso_6523}">${identifier.code_compact}</cbc:ID>
</cac:PartyIdentification>
</py:for>
<cac:PostalAddress py:if="address">${Address(address,
specification=specification)}</cac:PostalAddress>
@@ -23,7 +23,7 @@
<cbc:CompanyID>${tax_identifier.vatin}</cbc:CompanyID>
</py:when>
<py:otherwise>
- <cbc:CompanyID py:attrs="{'schemeID':
tax_identifier.iso_6523}">${tax_identifier.code}</cbc:CompanyID>
+ <cbc:CompanyID py:attrs="{'schemeID':
tax_identifier.iso_6523}">${tax_identifier.code_compact}</cbc:CompanyID>
</py:otherwise>
</py:choose>
<cac:TaxScheme>
@@ -34,7 +34,7 @@
<cbc:RegistrationName>${party.name}</cbc:RegistrationName>
<py:with vars="identifier = party.identifier_iso6523">
<py:if test="identifier">
- <cbc:CompanyID py:attrs="{'schemeID':
identifier.iso_6523}">${identifier.code}</cbc:CompanyID>
+ <cbc:CompanyID py:attrs="{'schemeID':
identifier.iso_6523}">${identifier.code_compact}</cbc:CompanyID>
</py:if>
</py:with>
</cac:PartyLegalEntity>
diff -r c81ff0ed70bc -r 06e183433933
modules/edocument_uncefact/template/16B-CII/CrossIndustryInvoice.xml
--- a/modules/edocument_uncefact/template/16B-CII/CrossIndustryInvoice.xml
Fri Jan 16 17:01:25 2026 +0100
+++ b/modules/edocument_uncefact/template/16B-CII/CrossIndustryInvoice.xml
Fri Jan 16 16:56:47 2026 +0100
@@ -20,7 +20,7 @@
</ram:SpecifiedLegalOrganization>
<ram:PostalTradeAddress
py:if="address">${TradeAddress(address)}</ram:PostalTradeAddress>
<ram:SpecifiedTaxRegistration py:if="tax_identifier and
tax_identifier.type == 'eu_vat'">
- <ram:ID schemeID='VA'>${tax_identifier.code}</ram:ID>
+ <ram:ID schemeID='VA'>${tax_identifier.code_compact}</ram:ID>
</ram:SpecifiedTaxRegistration>
</py:def>
<py:def function="TradeAddress(address)">
diff -r c81ff0ed70bc -r 06e183433933 modules/party/CHANGELOG
--- a/modules/party/CHANGELOG Fri Jan 16 17:01:25 2026 +0100
+++ b/modules/party/CHANGELOG Fri Jan 16 16:56:47 2026 +0100
@@ -1,3 +1,4 @@
+* Format identifier codes
* Add Senegal Tax Number identifier
* Add Azerbaijan Tax Number identifier
* Add French Trade Registration Number identifier
diff -r c81ff0ed70bc -r 06e183433933 modules/party/party.py
--- a/modules/party/party.py Fri Jan 16 17:01:25 2026 +0100
+++ b/modules/party/party.py Fri Jan 16 16:56:47 2026 +0100
@@ -343,6 +343,7 @@
return [bool_op,
('code', operator, code_value, *extra),
('identifiers.code', operator, code_value, *extra),
+ ('identifiers.code_compact', operator, code_value, *extra),
('name', operator, operand, *extra),
('contact_mechanisms.rec_name', operator, operand, *extra),
]
@@ -843,9 +844,12 @@
type_address = fields.Function(
fields.Boolean("Type of Address"), 'on_change_with_type_address')
code = fields.Char('Code', required=True)
+ code_compact = fields.Char("Code Compact", readonly=True, required=True)
@classmethod
def __setup__(cls):
+ cls.code.search_unaccented = False
+ cls.code_compact.search_unaccented = False
super().__setup__()
cls.__access__.add('party')
@@ -853,6 +857,11 @@
def __register__(cls, module_name):
cursor = Transaction().connection.cursor()
table = cls.__table__()
+ table_h = cls.__table_handler__(module_name)
+
+ fill_code_compact = (
+ table_h.column_exist('code')
+ and not table_h.column_exist('code_compact'))
super().__register__(module_name)
@@ -871,6 +880,10 @@
cursor.execute(*table.update([table.type], ['co_nit'],
where=(table.type == 'co_rut')))
+ # Migration from 7.8: Fill code_compact
+ if fill_code_compact:
+ cursor.execute(*table.update([table.code_compact], [table.code]))
+
@classmethod
def get_types(cls):
pool = Pool()
@@ -893,14 +906,20 @@
@fields.depends('type', 'code')
def on_change_with_code(self):
+ code = self.code
if self.type and '_' in self.type:
- module = get_cc_module(*self.type.split('_', 1))
- if module:
- try:
- return module.compact(self.code)
- except stdnum.exceptions.ValidationError:
- pass
- return self.code
+ if module := get_cc_module(*self.type.split('_', 1)):
+ if hasattr(module, 'compact'):
+ try:
+ code = module.compact(code)
+ except stdnum.exceptions.ValidationError:
+ pass
+ if hasattr(module, 'format'):
+ try:
+ code = module.format(code)
+ except stdnum.exceptions.ValidationError:
+ pass
+ return code
def pre_validate(self):
super().pre_validate()
@@ -922,6 +941,49 @@
code=self.code,
party=party))
+ @classmethod
+ def preprocess_values(cls, mode, values):
+ values = super().preprocess_values(mode, values)
+ if mode == 'create':
+ values['code_compact'] = values.get('code')
+ if ((type := values.get('type')) and '_' in type
+ and (code := values.get('code'))):
+ if module := get_cc_module(*type.split('_', 1)):
+ if hasattr(module, 'format'):
+ try:
+ values['code'] = module.format(code)
+ except stdnum.exceptions.ValidationError:
+ pass
+ if hasattr(module, 'compact'):
+ try:
+ values['code_compact'] = module.compact(code)
+ except stdnum.exceptions.ValidationError:
+ pass
+ return values
+
+ def compute_fields(self, field_names=None):
+ values = super().compute_fields(field_names=field_names)
+ if field_names is None or {'type', 'code'} & field_names:
+ code = getattr(self, 'code', None)
+ code_compact = getattr(self, 'code_compact', None)
+ if (type := getattr(self, 'type', None)) and '_' in type and code:
+ if module := get_cc_module(*type.split('_', 1)):
+ if hasattr(module, 'format'):
+ code = module.format(code)
+ if hasattr(module, 'compact'):
+ code_compact = module.compact(code)
+ else:
+ code_compact = code
+ if getattr(self, 'code', None) != code:
+ values['code'] = code
+ if getattr(self, 'code_compact', None) != code_compact:
+ values['code_compact'] = code_compact
+ elif code_compact != code:
+ values['code_compact'] = code
+ elif code_compact != code:
+ values['code_compact'] = code
+ return values
+
@fields.depends(methods=['_notify_duplicate'])
def on_change_notify(self):
notifications = super().on_change_notify()
diff -r c81ff0ed70bc -r 06e183433933 modules/stock_package_shipping_dpd/stock.py
--- a/modules/stock_package_shipping_dpd/stock.py Fri Jan 16 17:01:25
2026 +0100
+++ b/modules/stock_package_shipping_dpd/stock.py Fri Jan 16 16:56:47
2026 +0100
@@ -385,7 +385,7 @@
if customs_agent := shipment.customs_agent:
international.update({
'commercialInvoiceConsigneeVatNumber': (
- customs_agent.tax_identifier.code)[:20],
+ customs_agent.tax_identifier.code_compact)[:20],
'commercialInvoiceConsignee': self.shipping_party(
customs_agent.party,
customs_agent.address,
@@ -393,7 +393,7 @@
})
if shipment.tax_identifier:
international['commercialInvoiceConsignorVatNumber'] = (
- shipment.tax_identifier.code[:17])
+ shipment.tax_identifier.code_compact[:17])
international['commercialInvoiceConsignor'] = self.shipping_party(
shipment.company.party, shipment.customs_from_address)
international['additionalInvoiceLines'] = [
diff -r c81ff0ed70bc -r 06e183433933
modules/stock_package_shipping_sendcloud/stock.py
--- a/modules/stock_package_shipping_sendcloud/stock.py Fri Jan 16 17:01:25
2026 +0100
+++ b/modules/stock_package_shipping_sendcloud/stock.py Fri Jan 16 16:56:47
2026 +0100
@@ -281,32 +281,32 @@
yield {
'name': 'CNP',
'country_code': 'BR',
- 'value': tax_identifier.code[:100],
+ 'value': tax_identifier.code_compact[:100],
}
elif tax_identifier.type == 'ru_vat':
yield {
'name': 'INN',
'country_code': 'RU',
- 'value': tax_identifier.code[:100],
+ 'value': tax_identifier.code_compact[:100],
}
elif tax_identifier.type == 'eu_vat':
yield {
'name': 'VAT',
'country_code': tax_identifier.code[:2],
- 'value': tax_identifier.code[2:][:100],
+ 'value': tax_identifier.code_compact[2:][:100],
}
elif tax_identifier.type.endswith('_vat'):
yield {
'name': 'VAT',
'country_code': tax_identifier.type[:2].upper(),
- 'value': tax_identifier.code[:100],
+ 'value': tax_identifier.code_compact[:100],
}
elif tax_identifier.type in {'us_ein', 'us_ssn'}:
country, name = tax_identifier.type.upper().split('_')
yield {
'name': name,
'country_code': country,
- 'value': tax_identifier.code[:100],
+ 'value': tax_identifier.code_compact[:100],
}
def get_parcel_item(
diff -r c81ff0ed70bc -r 06e183433933 modules/web_shop_vue_storefront/party.py
--- a/modules/web_shop_vue_storefront/party.py Fri Jan 16 17:01:25 2026 +0100
+++ b/modules/web_shop_vue_storefront/party.py Fri Jan 16 16:56:47 2026 +0100
@@ -53,7 +53,7 @@
if for_party:
address['company'] = self.party.name
address['vat_id'] = (
- self.party.tax_identifier.code
+ self.party.tax_identifier.code_compact
if self.party.tax_identifier else None)
return address
diff -r c81ff0ed70bc -r 06e183433933 modules/web_shop_vue_storefront/web.py
--- a/modules/web_shop_vue_storefront/web.py Fri Jan 16 17:01:25 2026 +0100
+++ b/modules/web_shop_vue_storefront/web.py Fri Jan 16 16:56:47 2026 +0100
@@ -647,7 +647,7 @@
if address_data.get('company'):
for company_party in self.secondary_parties:
tax_code = (
- company_party.tax_identifier.code
+ company_party.tax_identifier.code_compact
if company_party.tax_identifier else '')
if (company_party.name == address_data['company']
and (not address_data.get('vat_id')