Odoo18-Base/addons/l10n_uy/models/res_partner.py
2025-01-06 10:57:38 +07:00

101 lines
4.2 KiB
Python

import logging
import re
from odoo import api, models, _
from odoo.exceptions import ValidationError
_logger = logging.getLogger(__name__)
class ResPartner(models.Model):
_inherit = "res.partner"
@api.constrains("vat", "l10n_latam_identification_type_id")
def check_vat(self):
# EXTEND account/base_vat
""" Add validation of UY document types CI and NIE """
ci_nie_types = self.filtered(
lambda p: p.l10n_latam_identification_type_id.l10n_uy_dgi_code in ("1", "3")
and p.l10n_latam_identification_type_id.country_id.code == "UY" and p.vat)
for partner in ci_nie_types:
if not partner._l10n_uy_ci_nie_is_valid():
raise ValidationError(self._l10n_uy_build_vat_error_message(partner))
return super(ResPartner, self - ci_nie_types).check_vat()
@api.model
def _l10n_uy_build_vat_error_message(self, partner):
""" Similar to _build_vat_error_message but using latam doc type name instead of vat_label
NOTE: maybe can be implemented in master to l10n_latam_base for the use of different doc types """
vat_label = _("CI/NIE")
expected_format = _("3:402.010-2 or 93:402.010-1 (CI or NIE)")
# Catch use case where the record label is about the public user (name: False)
if partner.name:
msg = "\n" + _(
"The %(vat_label)s number [%(wrong_vat)s] for %(partner_label)s does not seem to be valid."
"\nNote: the expected format is %(expected_format)s",
vat_label=vat_label,
wrong_vat=partner.vat,
partner_label=_("partner [%s]", partner.name),
expected_format=expected_format,
)
else:
msg = "\n" + _(
"The %(vat_label)s number [%(wrong_vat)s] does not seem to be valid."
"\nNote: the expected format is %(expected_format)s",
vat_label=vat_label,
wrong_vat=partner.vat,
expected_format=expected_format,
)
return msg
def _l10n_uy_ci_nie_is_valid(self):
""" Check if the partner's CI or NIE number is a valid one.
CI:
1) The ID number is taken up to the second to last position, that is, the first 6 or 7 digits.
2) Each digit is multiplied by a different factor starting from right to left, the factors are:
2, 9, 8, 7, 6, 3, 4.
3) The products obtained are added:
4) The base module 10 is calculated on this result to obtain the check digit, expressed in another way,
the next number ending in zero is taken that follows the result of the addition (for the example
would be 60) subtracting the sum itself: 60 - 59 = 1. The verification digit of the example ID is 1.
NOTE: If the ID has fewer digits, it is preceded with zeros and the mechanism described above is applied
NIE:
The calculation for the NIE is the same as that used for the CI. The only difference is that we skip the
first number
Both algorithms where extracted from Uruware's Technical Manual (section 9.2 and 9.3)
Return: False is not valid, True is valid
"""
self.ensure_one()
# The VAT must consist only numbers (format could have these characters ":., " we can skip them later)
invalid_chars = re.findall(r"[^0-9:., \-]", self.vat)
if invalid_chars:
return False
ci_nie_number = re.sub("[^0-9]", "", self.vat)
# we get the validation digit, if NIE doc type we skip the first digit
is_nie = self.l10n_latam_identification_type_id.l10n_uy_dgi_code == "1"
verif_digit = int(ci_nie_number[-1])
ci_nie_number = ci_nie_number[1:-1] if is_nie else ci_nie_number[0:-1]
# If number is < 7 digits we add 0 to the left
ci_nie_number = "%07d" % int(ci_nie_number)
# If NIE > 7 digits is not valid
if len(ci_nie_number) > 7:
return False
verification_vector = (2, 9, 8, 7, 6, 3, 4)
num_sum = sum(int(ci_nie_number[i]) * verification_vector[i] for i in range(7))
res = -num_sum % 10
return res == verif_digit