From cf3ff6141d789ff23f9bb63daef84f0f367b7b04 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Valyi?= Date: Fri, 27 Sep 2024 01:41:19 +0000 Subject: [PATCH] [REF] l10n_br_base: use vat field for CNPJ --- l10n_br_base/models/party_mixin.py | 30 +----- l10n_br_base/models/res_company.py | 21 +++++ l10n_br_base/models/res_partner.py | 114 +++++++++++++++++++++-- l10n_br_base/tests/test_base_onchange.py | 4 +- 4 files changed, 130 insertions(+), 39 deletions(-) diff --git a/l10n_br_base/models/party_mixin.py b/l10n_br_base/models/party_mixin.py index 04557895aef8..56274037ecab 100644 --- a/l10n_br_base/models/party_mixin.py +++ b/l10n_br_base/models/party_mixin.py @@ -12,20 +12,6 @@ class PartyMixin(models.AbstractModel): _name = "l10n_br_base.party.mixin" _description = "Brazilian partner and company data mixin" - cnpj_cpf_stripped = fields.Char( - string="CNPJ/CPF Stripped", - help="CNPJ/CPF without special characters", - compute="_compute_cnpj_cpf_stripped", - store=True, - index=True, - ) - - cnpj_cpf = fields.Char( - string="CNPJ/CPF", - size=18, - unaccent=False, - ) - inscr_est = fields.Char( string="State Tax Number", size=17, @@ -74,19 +60,9 @@ class PartyMixin(models.AbstractModel): size=32, ) - @api.depends("cnpj_cpf") - def _compute_cnpj_cpf_stripped(self): - for record in self: - if record.cnpj_cpf: - record.cnpj_cpf_stripped = "".join( - char for char in record.cnpj_cpf if char.isalnum() - ) - else: - record.cnpj_cpf_stripped = False - - @api.onchange("cnpj_cpf") - def _onchange_cnpj_cpf(self): - self.cnpj_cpf = cnpj_cpf.formata(str(self.cnpj_cpf)) + @api.onchange("vat") + def _onchange_vat(self): + self.vat = cnpj_cpf.formata(str(self.vat)) @api.onchange("zip") def _onchange_zip(self): diff --git a/l10n_br_base/models/res_company.py b/l10n_br_base/models/res_company.py index 59668ba75cb6..e3d96633ea86 100644 --- a/l10n_br_base/models/res_company.py +++ b/l10n_br_base/models/res_company.py @@ -87,6 +87,17 @@ def _inverse_suframa(self): for company in self: company.partner_id.suframa = company.suframa + # this helps maintaining the compatibility with the existing codebase: + cnpj_cpf = fields.Char(related="vat", readonly=False, string="CNPJ/CPF") + + cnpj_cpf_stripped = fields.Char( + string="CNPJ/CPF Stripped", + help="CNPJ/CPF without special characters", + compute="_compute_cnpj_cpf_stripped", + store=True, + index=True, + ) + legal_name = fields.Char( compute="_compute_address", inverse="_inverse_legal_name", @@ -143,6 +154,16 @@ def _inverse_suframa(self): inverse="_inverse_suframa", ) + @api.depends("cnpj_cpf") + def _compute_cnpj_cpf_stripped(self): + for record in self: + if record.cnpj_cpf: + record.cnpj_cpf_stripped = "".join( + char for char in record.cnpj_cpf if char.isalnum() + ) + else: + record.cnpj_cpf_stripped = False + @api.model def _fields_view_get( self, view_id=None, view_type="form", toolbar=False, submenu=False diff --git a/l10n_br_base/models/res_partner.py b/l10n_br_base/models/res_partner.py index a1c82783f951..e98c47b8039a 100644 --- a/l10n_br_base/models/res_partner.py +++ b/l10n_br_base/models/res_partner.py @@ -30,7 +30,22 @@ def _inverse_street_data(self): partner.street = street return super(Partner, not_br_partner)._inverse_street_data() - vat = fields.Char(related="cnpj_cpf") + # this helps maintaining the compatibility with the existing codebase: + cnpj_cpf = fields.Char( + string="CNPJ/CPF", + inverse="_inverse_cnpj_cpf", + compute="_compute_cnpj_cpf", + ) + + cnpj_cpf_stripped = fields.Char( + string="CNPJ/CPF Stripped", + help="CNPJ/CPF without special characters", + compute="_compute_cnpj_cpf_stripped", + store=True, + index=True, + ) + + l10n_br_cpf_code = fields.Char(string="CPF", help="Natural Persons Register.") is_accountant = fields.Boolean(string="Is accountant?") @@ -61,7 +76,63 @@ def _inverse_street_data(self): help="Indicate if is a Brazilian partner", ) - @api.constrains("cnpj_cpf", "inscr_est") + @api.onchange("cnpj_cpf") + def _inverse_cnpj_cpf(self): + for partner in self: + if len(partner.cnpj_cpf) > 11: + partner.vat = partner.cnpj_cpf + else: + partner.l10n_br_cpf_code = partner.cnpj_cpf + + @api.depends("vat", "l10n_br_cpf_code") + def _compute_cnpj_cpf(self): + for partner in self: + partner.cnpj_cpf = partner.vat or partner.l10n_br_cpf_code + + @api.depends("cnpj_cpf") + def _compute_cnpj_cpf_stripped(self): + for record in self: + if record.cnpj_cpf: + record.cnpj_cpf_stripped = "".join( + char for char in record.cnpj_cpf if char.isalnum() + ) + else: + record.cnpj_cpf_stripped = False + + def _commercial_sync_from_company(self): + """ + Overriden to avoid copying the CNPJ (vat field) to children companies + """ + if not self.is_br_partner: + return super()._commercial_sync_from_company() + + commercial_partner = self.commercial_partner_id + if commercial_partner != self: + sync_vals = commercial_partner._update_fields_values( + [field for field in self._commercial_fields() if field != "vat"] + ) + self.write(sync_vals) + self._commercial_sync_to_children() + + def _commercial_sync_to_children(self): + """ + Overriden to avoid copying the CNPJ (vat field) to parent partners + """ + if not self.is_br_partner: + return super()._commercial_sync_to_children() + + commercial_partner = self.commercial_partner_id + sync_vals = commercial_partner._update_fields_values( + [field for field in self._commercial_fields() if field != "vat"] + ) + sync_children = self.child_ids.filtered(lambda c: not c.is_company) + for child in sync_children: + child._commercial_sync_to_children() + res = sync_children.write(sync_vals) + sync_children._compute_commercial_partner() + return res + + @api.constrains("vat", "inscr_est") def _check_cnpj_inscr_est(self): for record in self: domain = [] @@ -85,10 +156,18 @@ def _check_cnpj_inscr_est(self): ("parent_id", "not in", record.parent_id.ids), ] - domain += [("cnpj_cpf", "=", record.cnpj_cpf), ("id", "!=", record.id)] + if record.vat: + domain += [("vat", "=", record.vat), ("id", "!=", record.id)] + elif record.l10n_br_cpf_code: + domain += [ + ("l10n_br_cpf_code", "=", record.l10n_br_cpf_code), + ("id", "!=", record.id), + ] + else: + return - # se encontrar CNPJ iguais - if record.env["res.partner"].search(domain): + matches = record.env["res.partner"].search(domain) + if matches: if cnpj_cpf.validar_cnpj(record.cnpj_cpf): if allow_cnpj_multi_ie == "True": for partner in record.env["res.partner"].search(domain): @@ -98,20 +177,35 @@ def _check_cnpj_inscr_est(self): ): raise ValidationError( _( - "There is already a partner record with this " - "Estadual Inscription !" + "There is already a partner %(name)s " + "(ID %(partner_id)s) with this " + "Estadual Inscription %(incr_est)s!", + name=partner.name, + partner_id=partner.id, + incr_est=partner.inscr_est, ) ) else: raise ValidationError( - _("There is already a partner record with this CNPJ !") + _( + "There is already a partner %(name)s " + "(ID %(partner_id)s) with this CNPJ %(vat)s!", + name=matches[0].name, + partner_id=matches[0].id, + vat=self.vat, + ) ) else: raise ValidationError( - _("There is already a partner record with this CPF/RG!") + _( + "There is already a partner %(name)s (ID %(partner_id)s) " + "with this CPF/RG!", + name=matches[0].name, + partner_id=matches[0].id, + ) ) - @api.constrains("cnpj_cpf", "country_id") + @api.constrains("vat", "country_id") def _check_cnpj_cpf(self): for record in self: check_cnpj_cpf( diff --git a/l10n_br_base/tests/test_base_onchange.py b/l10n_br_base/tests/test_base_onchange.py index 9f12ff8cc301..001820d15a42 100644 --- a/l10n_br_base/tests/test_base_onchange.py +++ b/l10n_br_base/tests/test_base_onchange.py @@ -43,7 +43,7 @@ def test_onchange(self): """ Call all the onchange methods in l10n_br_base """ - self.company_01._onchange_cnpj_cpf() + self.company_01._onchange_vat() self.company_01._onchange_city_id() self.company_01._onchange_zip() self.company_01._onchange_state() @@ -53,7 +53,7 @@ def test_onchange(self): # chamado, por isso existe outro metodo com o final _id self.company_01._onchange_state_id() - self.partner_01._onchange_cnpj_cpf() + self.partner_01._onchange_vat() self.partner_01._onchange_city_id() self.partner_01._onchange_zip() self.partner_01._onchange_state()