from trytond.model import fields, ModelSQL, ModelView, Unique, sequence_ordered from trytond.pool import PoolMeta, Pool from trytond.pyson import Eval, Not, Equal, Or, And from trytond.i18n import gettext from trytond.exceptions import UserError from .exceptions import PartyIdentifierError import logging logger = logging.getLogger(__name__) __all__ = ['PartyIdentifier','Party','ContactMechanism','Address','PartyTaxLevelCode'] TAX_CO_IDENTIFIERS = [ (None, ''), ('11', 'Registro Civil'), ('12', 'Tarjeta de Identidad'), ('13', 'Cedula de Ciudadania'), ('21', 'Tarjeta de Extranjeria'), ('22', 'Cedula de Extranjeria'), ('31', 'NIT'), ('41', 'Pasaporte'), ('42', 'Tipo de Documento Extranjero'), ('43', 'Documento de identificación extranjero'), ('50', 'NIT de otro país'), ('91', 'NUIP') ] CO_CONTACT_MECHANISM_TYPES = [ ('sms','SMS') ] class Party(ModelSQL,ModelView): "Party Colombian" __name__='party.party' __metaclass__ = PoolMeta first_name = fields.Char("Primer Nombre",states={ 'invisible': Not(Eval('type_person') == '2'),} ) second_name = fields.Char("Segundo Nombre",states={ 'invisible': Not(Eval('type_person') == '2'),} ) first_surname = fields.Char("Primer Apellido",states={ 'invisible': Not(Eval('type_person') == '2'),} ) second_surname = fields.Char("Segundo Apellido",states={ 'invisible': Not(Eval('type_person') == '2'),}, depends=['type_person']) birth_date = fields.Date('Fecha de nacimiento') tax_regime = fields.Selection([ (None, ''), ('48', 'Responsable del impuesto sobre las ventas –IVA'), ('49', 'No responsable de IVA') ], 'Tax Regime') type_taxpayer = fields.Selection([ (None, ''), ('regimen_simplificado', 'Regimen Simplificado'), ('regimen_comun', 'Regimen Comun'), ('gran_contribuyente', 'Gran Contribuyente'), ('entidad_estatal', 'Entidad Estatal'), ('domiciliado_extranjero', 'Domiciliado en Extranjero'), ], 'Type Taxpayer') type_person = fields.Selection([ (None, ''), ('1', 'Persona Juridica'), ('2', 'Persona Natural'), ], 'Type Person') self_withholding = fields.Boolean('Self Withholding') declarant = fields.Boolean('Declarant') main_ciiu = fields.Char('Main Activity', size=4) secondary_ciiu = fields.Char('Secondary Activity', size=4) other_ciiu = fields.Char('Other Activity', size=4) age = fields.Function(fields.Integer('Edad'),'on_change_with_age') type_tax_identifier = fields.Function(fields.Selection('get_types_identifier', 'Tipo de Identidad'), 'on_change_with_type_tax_identifier') tax_level_code =fields.Many2Many('party.party_tax_level_code','party', 'tax_level_code', "TaxLevelCode") @classmethod def __setup__(cls): super(Party, cls).__setup__() cls.name.required = True @staticmethod def default_type_person(): return '2' @classmethod def tax_identifier_types(cls): return ['12','13','31'] @classmethod def get_tax_level_code(cls): pool = Pool() TaxLevelCode = pool.get('party.tax_level_code') return TaxLevelCode.fields_get(['code'])['code'] @fields.depends('birth_date') def on_change_with_age(self,name=None): if self.birth_date: Date = Pool().get('ir.date') today = Date.today() age = today.year - self.birth_date.year age -= (today.month, today.day) < (self.birth_date.month, self.birth_date.day) return age @fields.depends('name','first_name','second_name','first_surname', 'second_surname','type_person') def on_change_type_person(self): if self.type_person == '2': self.on_change_name() if self.type_person == '1': pass @fields.depends('name','first_name','second_name','first_surname', 'second_surname','type_person') def on_change_name(self): self.rebuild_name() @fields.depends('name','first_name','second_name','first_surname', 'second_surname','type_person') def on_change_first_name(self): self.rebuild_name() @fields.depends('name','first_name','second_name','first_surname', 'second_surname','type_person') def on_change_second_name(self): self.rebuild_name() @fields.depends('name','first_name','second_name','first_surname', 'second_surname','type_person') def on_change_second_surname(self): self.rebuild_name() @fields.depends('name','first_name','second_name','first_surname', 'second_surname','type_person') def on_change_first_surname(self): self.rebuild_name() def rebuild_name(self): """reconstruye los campos relacionados con el nombre dependiendo de estos y del tipo de persona.""" if self.type_person == '2': if not (self.first_name or self.second_name \ or self.first_surname or self.second_surname): if self.name: self.first_name, self.second_name, self.first_surname, \ self.second_surname = self.split_name(self.name) else: return else: self.name = self.get_full_name() if self.type_person == '1': if self.name: self.clean_split_name() else: self.name = self.get_full_name() def clean_split_name(self): self.first_name = self.second_name = self.first_surname = self.second_surname = '' @staticmethod def split_name(name): """Divide un nombre completo en una lista de 4 elementos""" second_surname = None first_surname = None second_name = None first_name = None names = name.split(' ') if len(names) > 0: first_name = names[0] if len(names) == 2: second_surname = None first_surname = names[1] elif len(names) == 3: first_surname = names[1] second_surname = names[2] elif len(names) >= 4: second_name = names[1] first_surname = names[2] second_surname = " ".join(names[3:]) return [first_name,second_name,first_surname, second_surname] @fields.depends('identifiers') def on_change_with_type_tax_identifier(self,name=None): types = self._tax_identifier_types() if self.identifiers: for identifier in self.identifiers: if identifier.type in types: return identifier.type @fields.depends('identifiers') def on_change_with_tax_identifier(self,name=None): if self.identifiers: return self.get_tax_identifier(name) @classmethod def _tax_identifier_types(cls): types = super(Party, cls).tax_identifier_types() types.extend(['31','13']) return types def get_full_name(self, name=None): if self.type_person == '2': name_list = [self.first_name, self.second_name, self.first_surname,self.second_surname] return " ".join([x for x in name_list if x]) return self.name @staticmethod def get_types_identifier(): PartyIdentifier = Pool().get('party.identifier') return PartyIdentifier.type.selection def pre_validate(self): super(Party, self).pre_validate() if not self.identifiers: raise PartyIdentifierError(gettext( 'account_co_co.msg_not_identifier')) class PartyIdentifier(ModelSQL, ModelView): "Party Identifier Colombian" __name__ = "party.identifier" __metaclass__ = PoolMeta expedition_city = fields.Many2One("country.subdivision", 'Ciudad de Expedicion', help="Lugar donde fue expedido el documento de identidad", domain=[('parent', '!=', None)], states = {'invisible':Not(Equal(Eval('type'),'13'))} ) rut = fields.Boolean('Rut', states = {'invisible':And(Not(Equal(Eval('type'),('12'))), Not(Equal(Eval('type'),('13'))), Not(Equal(Eval('type'),('31')))), 'readonly':Equal(Eval('type'),('31'))}) check_digit = fields.Function(fields.Integer('DV', help='Digito de Verificacion colombiano', states = {'invisible':~Eval('rut', False)}) ,'on_change_with_check_digit') @classmethod def __setup__(cls): super(PartyIdentifier, cls).__setup__() cls.type.selection = TAX_CO_IDENTIFIERS cls.type.required = True table = cls.__table__() cls._sql_constraints += [ ('UniqueIdentifier', Unique(table, table.code,table.type), 'La identificacion ya existe') ] @fields.depends('rut','type') def on_change_type(self): if self.type == '31': self.rut = True @fields.depends('type','code') def on_change_with_check_digit(self,name=None): try: import stdnum.co.nit #la version debe ser mayor a la 1.2 o 1.3 except: raise PartyIdentifierError(gettext( 'account_co_co.msg_stdnum_error')) if self.type and self.code: if self.type in ('12','13','31') and self.code.isdigit(): return int(stdnum.co.nit.calc_check_digit(self.code)) @fields.depends('type', 'party', 'code', 'rut') def check_code(self): super(PartyIdentifier, self).check_code() tax_identifiers = ['12', '13', '31'] if self.type == '31': self.rut = True if self.rut != False and self.type in tax_identifiers: #generalizar multiples paises #puede ser por el country del party de la company actual try: import stdnum.co.nit #la version debe ser mayor a la 1.2 o 1.3 except: raise PartyIdentifierError(gettext( 'account_co_co.msg_stdnum_error')) if self.code.isdigit() is not True: raise PartyIdentifierError(gettext( 'account_co_co.msg_invalid_vat_str')) if not stdnum.co.nit.is_valid(self.code + str(self.check_digit)): if self.party and self.party.id > 0: party = self.party.rec_name else: party = '' raise PartyIdentifierError( gettext('account_co_co.msg_invalid_vat', code=self.code)) if self.type in tax_identifiers and not self.code.isdigit(): if self.party and self.party.id > 0: party = self.party.rec_name else: party = '' raise PartyIdentifierError( gettext('account_co_co.msg_invalid_vat', code=self.code)) @staticmethod def default_type_document(): return '13' @staticmethod def default_rut(): return False def get_rec_name(self,name=None): if self.code: return self.code elif self.id: return self.id return '' class PartyTaxLevelCode(ModelSQL, ModelView): "Party Tax Level Code" __name__ = 'party.party_tax_level_code' party = fields.Many2One('party.party', 'Party', select=True) tax_level_code = fields.Many2One('party.tax_level_code', 'TaxLevelCode', select=True) class ContactMechanism(sequence_ordered(), ModelSQL, ModelView): "Contact Mechanism" __name__ = 'party.contact_mechanism' __metaclass__ = PoolMeta @classmethod def __setup__(cls): super(ContactMechanism, cls).__setup__() cls.type.selection.extend(CO_CONTACT_MECHANISM_TYPES) class Address(ModelSQL, ModelView): "Address" __metaclass__ = PoolMeta __name__ = 'party.address' subdivision_municipality = fields.Many2One("country.subdivision", 'Municipality', domain=[ ('country', '=', Eval('country', -1)), ('parent', '=', Eval('subdivision', -1)), ('type', '=', 'municipality') ], depends=['country', 'subdivision', 'city'], required=True ) @fields.depends('subdivision', 'subdivision_municipality', 'country') def on_change_country(self): super(Address, self).on_change_country() if (self.subdivision_municipality and self.subdivision_municipality.parent != self.subdivision): self.subdivision_municipality = None @fields.depends('subdivision', 'subdivision_municipality') def on_change_subdivision(self): if hasattr(super(Address, self), 'on_change_subdivision'): super(Address, self).on_change_subdivision() if (self.subdivision_municipality and self.subdivision_municipality.parent != self.subdivision): self.subdivision_municipality = None @fields.depends('city', 'subdivision_municipality') def on_change_subdivision_municipality(self): if self.subdivision_municipality: self.city = self.subdivision_municipality.name else: self.city = None