# -*- coding: utf-8 -*- # Copyright 2021 Joan Marín # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). import re from odoo import api, models, fields, _ from odoo.exceptions import UserError, ValidationError class ResPartner(models.Model): _inherit = "res.partner" is_einvoicing_agent = fields.Selection( selection=[ ('yes', 'Yes'), ('no_but', 'No, but has email'), ('no', 'No'), ('unknown', 'Unknown')], string='Is an E-Invoicing Agent?', default=False) einvoicing_email = fields.Char(string='E-Invoicing Email') validate_einvoicing_email = fields.Boolean( string='Validate E-Invoicing Email?', related='company_id.validate_einvoicing_email') view_einvoicing_email_field = fields.Boolean( string="View 'E-Invoicing Email' Fields", compute='_get_view_einvoicing_email_field', store=False) edit_is_einvoicing_agent_field = fields.Boolean( string="Edit 'Is an E-Invoicing Agent?' Field", compute='_get_edit_is_einvoicing_agent_field', store=False) @api.onchange('person_type') def onchange_person_type(self): if self.person_type == '1': self.is_einvoicing_agent = 'yes' self.property_account_position_id = False def _get_view_einvoicing_email_field(self): user = self.env['res.users'].search([('id', '=', self.env.user.id)]) view_einvoicing_email_field = False if user.has_group( 'l10n_co_account_e_invoicing.group_view_einvoicing_email_fields'): view_einvoicing_email_field = True for partner in self: partner.view_einvoicing_email_field = view_einvoicing_email_field def _get_edit_is_einvoicing_agent_field(self): user = self.env['res.users'].search([('id', '=', self.env.user.id)]) edit_is_einvoicing_agent_field = False if user.has_group( 'l10n_co_account_e_invoicing.group_edit_is_einvoicing_agent_field'): edit_is_einvoicing_agent_field = True for partner in self: partner.edit_is_einvoicing_agent_field = edit_is_einvoicing_agent_field @api.constrains('einvoicing_email') @api.onchange('einvoicing_email') def validate_mail(self): if self.einvoicing_email: for email in self.einvoicing_email.replace(' ', '').split(","): match = re.match( r"(^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$)", email) if match is None: raise ValidationError(_("The field 'E-Invoicing Email' is not " "correctly filled.\n\n" "Please add @ and dot (.)")) def _get_accounting_partner_party_values(self): msg1 = _("'%s' does not have a person type established.") msg2 = _("'%s' does not have a city established.") msg3 = _("'%s' does not have a state established.") msg4 = _("'%s' does not have a country established.") msg5 = _("'%s' does not have a verification digit established.") msg6 = _("'%s' does not have a DIAN document type established.") msg7 = _("The document type of '%s' does not seem to correspond with the " "person type.") msg8 = _("'%s' does not have a identification document established.") msg9 = _("'%s' does not have a fiscal position correctly configured.") msg10 = _("'%s' does not have a fiscal position established.") msg11 = _("E-Invoicing Agent: '%s' does not have a E-Invoicing Email.") name = self.name zip_code = False identification_document = self.ref_num telephone = False if not self.person_type: raise UserError(msg1 % self.name) if self.country_id: if self.country_id.code == 'CO': if not self.city_id: raise UserError(msg2 % self.name) elif not self.state_id: raise UserError(msg3 % self.name) else: raise UserError(msg4 % self.name) if self.ref_type_id: document_type_code = self.ref_type_id.code_dian if (document_type_code == '31' and not self.verification_code and str(self.verification_code) != '0'): raise UserError(msg5 % self.name) # Punto 13.2.1. Documento de identificación (Tipo de Identificador Fiscal): # cbc:CompanyID.@schemeName; sts:ProviderID.@schemeName # Factura electrónica del anexo tecnico version 1.8 if document_type_code not in ( '11', '12', '13', '21', '22', '31', '41', '42', '47', '50', '91'): if self.person_type == '1': raise UserError(msg6 % self.name) else: name = 'consumidor final' document_type_code = '13' identification_document = '222222222222' else: raise UserError(msg6 % self.name) if self.person_type == '1' and document_type_code not in ('31', '50'): raise UserError(msg7 % self.name) if not identification_document: raise UserError(msg8 % self.name) if self.property_account_position_id: if (not self.property_account_position_id.tax_level_code_ids or not self.property_account_position_id.party_tax_scheme_id or not self.property_account_position_id.listname): raise UserError(msg9 % self.name) tax_level_codes = '' tax_scheme_code = self.property_account_position_id.party_tax_scheme_id.code tax_scheme_name = self.property_account_position_id.party_tax_scheme_id.name else: raise UserError(msg10 % self.name) if (self.validate_einvoicing_email and (not self.is_einvoicing_agent or self.is_einvoicing_agent in ('yes', 'not_but')) and not self.einvoicing_email): raise UserError(msg11 % self.name) if self.zip_id: zip_code = self.zip_id.name for tax_level_code_id in self.property_account_position_id.tax_level_code_ids: if tax_level_codes == '': tax_level_codes = tax_level_code_id.code else: tax_level_codes += ';' + tax_level_code_id.code if self.phone and self.mobile: telephone = self.phone + " / " + self.mobile elif self.phone: telephone = self.phone elif self.mobile: telephone = self.mobile if identification_document == '222222222222': tax_level_codes = 'R-99-PN' tax_scheme_code = 'ZZ' tax_scheme_name = 'No causa' if self.property_account_position_id.listname != '49': raise UserError(msg8 % self.name) return { 'AdditionalAccountID': self.person_type, 'PartyName': self.commercial_name, 'Name': name, 'AddressID': (self.state_id.numeric_code or '') + (self.city_id.code or ''), 'AddressCityName': self.city_id.name or '', 'AddressPostalZone': zip_code, 'AddressCountrySubentity': self.state_id.name or '', 'AddressCountrySubentityCode': self.state_id.numeric_code or '', 'AddressLine': self.street or '', 'CompanyIDschemeID': self.verification_code, 'CompanyIDschemeName': document_type_code, 'CompanyID': identification_document, 'listName': self.property_account_position_id.listname, 'TaxLevelCode': tax_level_codes, 'TaxSchemeID': tax_scheme_code, 'TaxSchemeName': tax_scheme_name, 'CorporateRegistrationSchemeName': self.coc_registration_number, 'CountryIdentificationCode': self.country_id.code, 'CountryName': self.country_id.name, 'Telephone': telephone, 'Telefax': self.fax, 'ElectronicMail': self.einvoicing_email or self.email } def _get_delivery_values(self): msg1 = _("'%s' does not have a city established.") msg2 = _("'%s' does not have a state established.") msg3 = _("'%s' does not have a country established.") zip_code = False if self.country_id: if self.country_id.code == 'CO': if not self.city_id: raise UserError(msg1 % self.name) elif not self.state_id: raise UserError(msg2 % self.name) else: raise UserError(msg3 % self.name) if self.zip_id: zip_code = self.zip_id.name return { 'AddressID': (self.state_id.numeric_code or '') + (self.city_id.code or ''), 'AddressCityName': self.city_id.name or '', 'AddressPostalZone': zip_code, 'AddressCountrySubentity': self.state_id.name or '', 'AddressCountrySubentityCode': self.state_id.numeric_code or '', 'AddressLine': self.street or '', 'CountryIdentificationCode': self.country_id.code, 'CountryName': self.country_id.name } def _get_tax_representative_party_values(self): msg1 = _("'%s' does not have a verification digit established.") msg2 = _("'%s' does not have a document type established.") msg3 = _("'%s' does not have a identification document established.") if self.ref_type_id: if self.ref_type_id.code_dian == '31' and not self.verification_code: raise UserError(msg1 % self.name) else: raise UserError(msg2 % self.name) if not self.ref_num: raise UserError(msg3 % self.name) return { 'IDschemeID': self.verification_code, 'IDschemeName': self.ref_type_id.code_dian, 'ID': self.ref_num }