249 lines
10 KiB
Python
249 lines
10 KiB
Python
# -*- coding: utf-8 -*-
|
|
# Copyright 2021 Joan Marín <Github@JoanMarin>
|
|
# 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
|
|
}
|