@@ -81,10 +81,10 @@ class BEVATCustomer(ModelSQL, ModelView):
|
||||
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)
|
||||
|
||||
@@ -309,11 +309,11 @@ class MoveExport(metaclass=PoolMeta):
|
||||
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 {
|
||||
|
||||
@@ -54,11 +54,19 @@ class Identifier(metaclass=PoolMeta):
|
||||
& (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')
|
||||
|
||||
@@ -189,7 +189,7 @@ Post invoice::
|
||||
>>> 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)
|
||||
|
||||
@@ -28,7 +28,7 @@ class Party(metaclass=PoolMeta):
|
||||
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()
|
||||
|
||||
@@ -319,7 +319,7 @@ class Move(metaclass=PoolMeta):
|
||||
if not fallback:
|
||||
fallback = identifier
|
||||
if (self.intrastat_country
|
||||
and identifier.code.startswith(
|
||||
and identifier.code_compact.startswith(
|
||||
self.intrastat_country.code)):
|
||||
break
|
||||
else:
|
||||
|
||||
@@ -91,7 +91,9 @@ class IncomingSupplierInvoice(metaclass=PoolMeta):
|
||||
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 @@ class IncomingSupplierInvoice(metaclass=PoolMeta):
|
||||
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 @@ class IncomingSupplierInvoice(metaclass=PoolMeta):
|
||||
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:
|
||||
|
||||
@@ -24,7 +24,7 @@ class Invoice(metaclass=PoolMeta):
|
||||
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)
|
||||
|
||||
@@ -881,7 +881,7 @@ class Invoice(Model):
|
||||
):
|
||||
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 @@ class Invoice(Model):
|
||||
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 @@ class Invoice(Model):
|
||||
):
|
||||
if identifier.text:
|
||||
domain = [
|
||||
('code', '=', identifier.text),
|
||||
('code_compact', '=', identifier.text),
|
||||
]
|
||||
if schemeId := identifier.get('schemeID'):
|
||||
if type := ISO6523.get(schemeId):
|
||||
|
||||
@@ -13,7 +13,7 @@ this repository contains the full copyright notices and license terms. -->
|
||||
</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 @@ this repository contains the full copyright notices and license terms. -->
|
||||
<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 @@ this repository contains the full copyright notices and license terms. -->
|
||||
<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>
|
||||
|
||||
@@ -20,7 +20,7 @@ this repository contains the full copyright notices and license terms. -->
|
||||
</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)">
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
* Format identifier codes
|
||||
* Add Senegal Tax Number identifier
|
||||
* Add Azerbaijan Tax Number identifier
|
||||
* Add French Trade Registration Number identifier
|
||||
|
||||
+69
-7
@@ -343,6 +343,7 @@ class Party(
|
||||
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 @@ class Identifier(sequence_ordered(), DeactivableMixin, ModelSQL, ModelView):
|
||||
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 @@ class Identifier(sequence_ordered(), DeactivableMixin, ModelSQL, ModelView):
|
||||
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 @@ class Identifier(sequence_ordered(), DeactivableMixin, ModelSQL, ModelView):
|
||||
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 @@ class Identifier(sequence_ordered(), DeactivableMixin, ModelSQL, ModelView):
|
||||
|
||||
@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 @@ class Identifier(sequence_ordered(), DeactivableMixin, ModelSQL, ModelView):
|
||||
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()
|
||||
|
||||
@@ -385,7 +385,7 @@ class CreateDPDShipping_Customs(metaclass=PoolMeta):
|
||||
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 @@ class CreateDPDShipping_Customs(metaclass=PoolMeta):
|
||||
})
|
||||
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'] = [
|
||||
|
||||
@@ -281,32 +281,32 @@ class CreateShippingSendcloud_Customs(metaclass=PoolMeta):
|
||||
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(
|
||||
|
||||
@@ -53,7 +53,7 @@ class Address(metaclass=PoolMeta):
|
||||
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
|
||||
|
||||
|
||||
@@ -647,7 +647,7 @@ class User(metaclass=PoolMeta):
|
||||
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')
|
||||
|
||||
Reference in New Issue
Block a user