-
-
Notifications
You must be signed in to change notification settings - Fork 244
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[IMP] l10n_br_base: add pix fields for partners
- Loading branch information
1 parent
c06e5d7
commit 50fc5e5
Showing
12 changed files
with
437 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
<?xml version="1.0" encoding="utf-8" ?> | ||
<odoo> | ||
|
||
<!-- | ||
Resource: res.partner.pix | ||
Partner: AMD do Brasil | ||
--> | ||
<record id="res_partner_amd_pix_cnpj" model="res.partner.pix"> | ||
<field name="partner_id" ref="res_partner_amd" /> | ||
<field name="key_type">cnpj_cpf</field> | ||
<field name="key">62228384000151</field> | ||
</record> | ||
|
||
<record id="res_partner_amd_pix_phone" model="res.partner.pix"> | ||
<field name="partner_id" ref="res_partner_amd" /> | ||
<field name="key_type">phone</field> | ||
<field name="key">1144576060</field> | ||
</record> | ||
|
||
<record id="res_partner_amd_pix_email" model="res.partner.pix"> | ||
<field name="partner_id" ref="res_partner_amd" /> | ||
<field name="key_type">email</field> | ||
<field name="key">[email protected]</field> | ||
</record> | ||
|
||
<record id="res_partner_amd_evp" model="res.partner.pix"> | ||
<field name="partner_id" ref="res_partner_amd" /> | ||
<field name="key_type">evp</field> | ||
<field name="key">123e4567-e12b-12d1-a456-426655440000</field> | ||
</record> | ||
|
||
</odoo> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,150 @@ | ||
import phonenumbers | ||
from email_validator import EmailSyntaxError, validate_email | ||
from erpbrasil.base.fiscal import cnpj_cpf | ||
|
||
from odoo import _, api, fields, models | ||
from odoo.exceptions import ValidationError | ||
|
||
|
||
class PartnerPix(models.Model): | ||
_name = "res.partner.pix" | ||
_description = "Brazilian instant payment ecosystem (Pix)" | ||
_order = "sequence, id" | ||
_rec_name = "key" | ||
|
||
_sql_constraints = [ | ||
( | ||
"partner_pix_key_unique", | ||
"unique(key_type, key, partner_id)", | ||
"A Pix Key with this values already exists in this partner.", | ||
) | ||
] | ||
|
||
KEY_TYPES = [ | ||
("cnpj_cpf", _("CPF or CNPJ")), | ||
("phone", _("Phone Number")), | ||
("email", _("E-mail")), | ||
("evp", _("Random Key")), | ||
] | ||
|
||
partner_id = fields.Many2one( | ||
comodel_name="res.partner", | ||
string="Partner", | ||
ondelete="cascade", | ||
required=True, | ||
) | ||
sequence = fields.Integer(default=10) | ||
key_type = fields.Selection( | ||
selection=KEY_TYPES, | ||
string="Type", | ||
required=True, | ||
) | ||
key = fields.Char( | ||
help="PIX Addressing key", | ||
required=True, | ||
) | ||
|
||
partner_bank_id = fields.Many2one( | ||
comodel_name="res.partner.bank", | ||
string="Bank Account", | ||
domain="[('partner_id', '=', partner_id)]", | ||
) | ||
|
||
def _normalize_email(self, email): | ||
try: | ||
result = validate_email( | ||
email, | ||
check_deliverability=False, | ||
) | ||
except EmailSyntaxError: | ||
raise ValidationError(_(f"{email.strip()} is an invalid email")) | ||
normalized_email = result["local"].lower() + "@" + result["domain_i18n"] | ||
if len(normalized_email) > 77: | ||
raise ValidationError( | ||
_( | ||
f"The email is too long, " | ||
f"a maximum of 77 characters is allowed: \n{email.strip()}" | ||
) | ||
) | ||
return normalized_email | ||
|
||
def _normalize_phone(self, phone): | ||
try: | ||
phonenumber = phonenumbers.parse(phone, "BR") | ||
except phonenumbers.phonenumberutil.NumberParseException as e: | ||
raise ValidationError(_(f"Unable to parse {phone}: {str(e)}")) | ||
if not phonenumbers.is_possible_number(phonenumber): | ||
raise ValidationError( | ||
_(f"Impossible number {phone}: probably invalid number of digits.") | ||
) | ||
if not phonenumbers.is_valid_number(phonenumber): | ||
raise ValidationError( | ||
_(f"Invalid number {phone}: probably incorrect prefix.") | ||
) | ||
phone = phonenumbers.format_number( | ||
phonenumber, phonenumbers.PhoneNumberFormat.E164 | ||
) | ||
return phone | ||
|
||
def _normalize_cnpj_cpf(self, doc_number): | ||
doc_number = "".join(char for char in doc_number if char.isdigit()) | ||
if not 11 <= len(doc_number) <= 14: | ||
raise ValidationError( | ||
_( | ||
f"Invalid Document Number {doc_number}: " | ||
f"\nThe CPF must have 11 digits and the CNPJ 14 digits." | ||
) | ||
) | ||
is_valid = cnpj_cpf.validar(doc_number) | ||
if not is_valid: | ||
raise ValidationError(_(f"Invalid Document Number: {doc_number}")) | ||
return doc_number | ||
|
||
def _normalize_evp(self, key): | ||
# EVP: Endereço Virtual de Pagamento (chave aleatória) | ||
# ex: 123e4567-e12b-12d1-a456-426655440000 | ||
key = "".join(key.split()) | ||
if len(key) != 36: | ||
raise ValidationError( | ||
_(f"Invalid Random Key: {key}, cannot be longer than 35 characters") | ||
) | ||
blocks = key.split("-") | ||
if len(blocks) != 5: | ||
raise ValidationError( | ||
_(f"Invalid Random Key: {key}, the key must consist of five blocks.") | ||
) | ||
for block in blocks: | ||
try: | ||
int(block, 16) | ||
except ValueError: | ||
raise ValidationError( | ||
_( | ||
f"Invalid Random Key: {key} \nthe block {block} " | ||
f"is not a valid hexadecimal format." | ||
) | ||
) | ||
return key | ||
|
||
@api.model | ||
def create(self, vals): | ||
self.check_vals(vals) | ||
return super(PartnerPix, self).create(vals) | ||
|
||
def write(self, vals): | ||
self.check_vals(vals) | ||
return super(PartnerPix, self).write(vals) | ||
|
||
def check_vals(self, vals): | ||
key_type = vals.get("key_type") or self.key_type | ||
key = vals.get("key") or self.key | ||
if not key or not key_type: | ||
return | ||
if key_type == "email": | ||
key = self._normalize_email(key) | ||
elif key_type == "phone": | ||
key = self._normalize_phone(key) | ||
elif key_type == "cnpj_cpf": | ||
key = self._normalize_cnpj_cpf(key) | ||
elif key_type == "evp": | ||
key = self._normalize_evp(key) | ||
vals["key"] = key |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,5 @@ | ||
"id","name","model_id:id","group_id:id","perm_read","perm_write","perm_create","perm_unlink" | ||
"state_tax_numbers_user","State Tax Numbers for User","model_state_tax_numbers","base.group_user",1,0,0,0 | ||
"state_tax_numbers_manager","State Tax Numbers for Manager","model_state_tax_numbers","base.group_system",1,1,1,1 | ||
"res_partner_pix_user","Partner PIX for User","model_res_partner_pix","base.group_user",1,0,0,0 | ||
"res_partner_pix_manager","Partner PIX for Partner Manager","model_res_partner_pix","base.group_partner_manager",1,1,1,1 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
from odoo.exceptions import UserError | ||
from odoo.tests import TransactionCase | ||
|
||
|
||
class PartnerBankTest(TransactionCase): | ||
def setUp(self): | ||
super().setUp() | ||
self.partner_bank_model = self.env["res.partner.bank"] | ||
self.partner_id = self.env.ref("l10n_br_base.res_partner_amd") | ||
self.bank_id = self.env.ref("l10n_br_base.res_bank_001") | ||
|
||
def test_ok_transactional_acc_type(self): | ||
ok_bank_vals = { | ||
"partner_id": self.partner_id.id, | ||
"transactional_acc_type": "checking", | ||
"bank_id": self.bank_id.id, | ||
"bra_number": "1020", | ||
"acc_number": "102030", | ||
"acc_number_dig": "9", | ||
} | ||
ok_acc_bank = self.partner_bank_model.with_context( | ||
tracking_disable=True | ||
).create(ok_bank_vals) | ||
self.assertTrue(ok_acc_bank.exists()) | ||
|
||
def test_wrong_transactional_acc_type(self): | ||
wrong_bank_vals = { | ||
"partner_id": self.partner_id.id, | ||
"transactional_acc_type": "checking", | ||
"bra_number": "1020", | ||
"acc_number_dig": "9", | ||
} | ||
with self.assertRaises(UserError): | ||
self.partner_bank_model.with_context(tracking_disable=True).create( | ||
wrong_bank_vals | ||
) |
Oops, something went wrong.