506 lines
18 KiB
Python
506 lines
18 KiB
Python
# This file is part of Tryton. The COPYRIGHT file at the top level of
|
|
# this repository contains the full copyright notices and license terms.
|
|
from sql import Table
|
|
from trytond.exceptions import UserError
|
|
from trytond.model import ModelSQL, ModelView, Unique, fields
|
|
from trytond.pool import Pool, PoolMeta
|
|
from trytond.pyson import Eval
|
|
from trytond.transaction import Transaction
|
|
from trytond.wizard import Button, StateTransition, StateView, Wizard
|
|
|
|
from .exceptions import ValidatePartyWarning
|
|
from .invoice import TYPE_INVOICE_OUT
|
|
|
|
PRIMOS = [71, 67, 59, 53, 47, 43, 41, 37, 29, 23, 19, 17, 13, 7, 3]
|
|
K = 11
|
|
|
|
STATES_NAME = {
|
|
'invisible': ~Eval('type_person').in_(['persona_natural'])
|
|
}
|
|
|
|
TYPE_DOCUMENT = [
|
|
('11', 'Registro Civil de Nacimiento'),
|
|
('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'),
|
|
('47', 'PEP (Permiso Especial Permanencia)'),
|
|
('50', 'NIT de otro pais'),
|
|
('91', 'NUIP'),
|
|
('', ''),
|
|
]
|
|
|
|
REGIME_TAX = [
|
|
('', ''),
|
|
('regimen_responsable', 'Regimen Responsable IVA'),
|
|
('regimen_no_responsable', 'Regimen No Responsable IVA'),
|
|
('gran_contribuyente', 'Gran Contribuyente'),
|
|
('entidad_estatal', 'Entidad Estatal'),
|
|
('domiciliado_extranjero', 'Domiciliado en Extranjero'),
|
|
]
|
|
|
|
TYPE_PERSON = [
|
|
('', ''),
|
|
('persona_natural', 'Persona Natural'),
|
|
('persona_juridica', 'Persona Juridica'),
|
|
]
|
|
|
|
|
|
class Configuration(ModelSQL, ModelView):
|
|
__name__ = 'party.configuration'
|
|
validate_party = fields.Boolean('Validate Party')
|
|
regime_tax = fields.Selection(REGIME_TAX, 'Regimen de Impuestos')
|
|
type_document = fields.Selection(TYPE_DOCUMENT, 'Tipo de Documento')
|
|
type_person = fields.Selection(TYPE_PERSON, 'Tipo de Persona')
|
|
|
|
def default_validate_party():
|
|
return False
|
|
|
|
|
|
class Party(ModelSQL, ModelView):
|
|
__name__ = 'party.party'
|
|
id_number = fields.Char('Id Number')
|
|
id_number_city = fields.Char('Id Number City', states={
|
|
'invisible': (Eval('regime_tax') != 'persona_natural'),
|
|
})
|
|
vat_number_city = fields.Char('Vat Number City')
|
|
type_document = fields.Selection(TYPE_DOCUMENT, 'Tipo de Documento')
|
|
type_document_string = type_document.translated('type_document')
|
|
regime_tax = fields.Selection(REGIME_TAX, 'Regimen de Impuestos')
|
|
type_person = fields.Selection(TYPE_PERSON, 'Tipo de Persona')
|
|
regime_tax_string = regime_tax.translated('regime_tax')
|
|
check_digit = fields.Function(fields.Integer('DV'),
|
|
'get_check_digit')
|
|
# first_name = fields.Char('Primer Nombre', states=STATES_NAME)
|
|
# second_name = fields.Char('Segundo Nombre', states=STATES_NAME)
|
|
# first_family_name = fields.Char('Primer Apellido', states=STATES_NAME)
|
|
# second_family_name = fields.Char('Segundo Apellido', states=STATES_NAME)
|
|
commercial_name = fields.Char('Commercial Name')
|
|
declarante = fields.Boolean('Declarante')
|
|
ciiu_code = fields.Char('CIIU Code', size=4)
|
|
street = fields.Function(fields.Char('Street'), 'get_information_address')
|
|
country_code = fields.Function(fields.Char('Country Code'), 'get_information_address')
|
|
department_code = fields.Function(fields.Char('Department Code'),
|
|
'get_information_address')
|
|
city_code = fields.Function(fields.Char('City Code'), 'get_information_address')
|
|
country_name = fields.Function(fields.Char('Country Name'), 'get_information_address')
|
|
department_name = fields.Function(fields.Char('Department Name'),
|
|
'get_information_address')
|
|
city_name = fields.Function(fields.Char('City Name'), 'get_information_address')
|
|
bank = fields.Function(fields.Many2One('party.party', 'Party Bank'), 'get_bank_info')
|
|
bank_name = fields.Function(fields.Char('Bank Name'), 'get_bank_info')
|
|
bank_account = fields.Function(fields.Char('Bank Account'),
|
|
'get_bank_info')
|
|
bank_account_type = fields.Function(fields.Char('Bank Account Type'),
|
|
'get_bank_info')
|
|
bank_account_type_string = fields.Function(fields.Char('Bank Account Type String'),
|
|
'get_bank_info')
|
|
vat_number = fields.Function(fields.Char('VAT Number'),
|
|
'get_vat_number', searcher='search_vat_number')
|
|
id_number_full = fields.Function(fields.Char('Full Id Number'),
|
|
'get_id_number_full')
|
|
vat_number_full = fields.Function(fields.Char('VAT Number Full'),
|
|
'get_id_number_full')
|
|
currency = fields.Many2One('currency.currency', 'Currency')
|
|
# zip = fields.Function(fields.Char('Zip Code'), 'get_address')
|
|
# This must remove in future
|
|
invoice_type = fields.Selection(TYPE_INVOICE_OUT, 'Type Invoice')
|
|
born_country = fields.Many2One('party.country_code', 'Country Born')
|
|
born_subdivision = fields.Many2One('party.department_code', 'Subdivision Born')
|
|
born_city = fields.Many2One('party.city_code', 'City Born', domain=[
|
|
('department', '=', Eval('born_subdivision')),
|
|
], depends=['born_subdivision'])
|
|
|
|
@classmethod
|
|
def __setup__(cls):
|
|
super(Party, cls).__setup__()
|
|
table = cls.__table__()
|
|
cls._sql_constraints += [
|
|
('id_number_uniq', Unique(table, table.id_number),
|
|
'Id Number already exists!'),
|
|
]
|
|
cls.first_name.states['invisible'] = ~Eval('type_person').in_(['persona_natural'])
|
|
cls.second_name.states['invisible'] = ~Eval('type_person').in_(['persona_natural'])
|
|
cls.first_family_name.states['invisible'] = ~Eval('type_person').in_(['persona_natural'])
|
|
cls.second_family_name.states['invisible'] = ~Eval('type_person').in_(['persona_natural'])
|
|
|
|
@classmethod
|
|
def search_rec_name(cls, name, clause):
|
|
parties = cls.search([
|
|
('id_number',) + tuple(clause[1:]),
|
|
], limit=1)
|
|
if parties:
|
|
return [('id_number',) + tuple(clause[1:])]
|
|
return [('name',) + tuple(clause[1:])]
|
|
|
|
def check_credit_limit(self, amount, origin=None):
|
|
pool = Pool()
|
|
origin = origin.split(',')
|
|
Sale = pool.get(origin[0])
|
|
sales = Sale.search([('id', '=', origin[1])])
|
|
if sales[0].payment_term.payment_type != '1' and amount > 0:
|
|
super(Party, self).check_credit_limit(amount, origin)
|
|
|
|
def pre_validate(self):
|
|
pool = Pool()
|
|
Warning = pool.get('res.user.warning')
|
|
Configuration = pool.get('party.configuration')
|
|
Party = pool.get('party.party')
|
|
config = Configuration(1)
|
|
party_id_numbers = Party.search([('id_number', '=', self.id_number)])
|
|
if len(party_id_numbers) > 1:
|
|
msg = f'Numero de Id ya existe: {self.id_number}'
|
|
raise UserError(msg)
|
|
if config.validate_party is False:
|
|
return
|
|
if not self.id_number:
|
|
raise UserError(f'Numero de Id invalido: {self.id_number}')
|
|
return
|
|
id_number = self.id_number.replace(".", "")
|
|
if self.type_document in ['12', '13', '31'] and not id_number.isdigit():
|
|
raise UserError(f'Numero de Id invalido: {self.id_number}')
|
|
return
|
|
if self.name.isdigit() or self.name == '':
|
|
raise UserError('Nombre invalido!')
|
|
return
|
|
if not self.regime_tax:
|
|
warning_key = 'party_regime_tax'
|
|
if Warning.check(warning_key):
|
|
raise ValidatePartyWarning(warning_key, 'Tercero sin regimen de impuestos!')
|
|
if not self.type_person:
|
|
warning_key = 'party_type_person'
|
|
if Warning.check(warning_key):
|
|
raise ValidatePartyWarning(warning_key, 'Tercero sin tipo de persona!')
|
|
if not self.country_code:
|
|
warning_key = 'party_country_code'
|
|
if Warning.check(warning_key):
|
|
raise ValidatePartyWarning(warning_key, 'Tercero sin pais!')
|
|
if not self.department_code:
|
|
warning_key = 'party_department_code'
|
|
if Warning.check(warning_key):
|
|
raise ValidatePartyWarning(warning_key, 'Tercero sin departmento!')
|
|
if not self.city_code:
|
|
warning_key = 'party_city_code'
|
|
if Warning.check(warning_key):
|
|
raise ValidatePartyWarning(warning_key, 'Tercero sin ciudad!')
|
|
if not self.street:
|
|
warning_key = 'party_street'
|
|
if Warning.check(warning_key):
|
|
raise ValidatePartyWarning(warning_key, 'Tercero sin direccion!')
|
|
|
|
def get_check_digit(self, name):
|
|
if not self.id_number or self.type_document != '31':
|
|
return None
|
|
id_number = self.id_number.replace(".", "")
|
|
if not id_number.isdigit():
|
|
return None
|
|
c = 0
|
|
p = len(PRIMOS) - 1
|
|
for n in reversed(id_number):
|
|
c += int(n) * PRIMOS[p]
|
|
p -= 1
|
|
|
|
dv = c % 11
|
|
if dv > 1:
|
|
dv = 11 - dv
|
|
return dv
|
|
|
|
@staticmethod
|
|
def default_type_document():
|
|
config = Pool().get('party.configuration')(1)
|
|
if config.type_document:
|
|
return config.type_document
|
|
|
|
@staticmethod
|
|
def default_type_person():
|
|
config = Pool().get('party.configuration')(1)
|
|
if config.type_person:
|
|
return config.type_person
|
|
|
|
@staticmethod
|
|
def default_regime_tax():
|
|
config = Pool().get('party.configuration')(1)
|
|
if config.regime_tax:
|
|
return config.regime_tax
|
|
|
|
@classmethod
|
|
def default_account_payable(cls, **pattern):
|
|
pool = Pool()
|
|
Configuration = pool.get('account.configuration')
|
|
config = Configuration(1)
|
|
account = config.get_multivalue(
|
|
'default_account_payable', **pattern)
|
|
return account.id if account else None
|
|
|
|
@classmethod
|
|
def default_account_receivable(cls, **pattern):
|
|
pool = Pool()
|
|
Configuration = pool.get('account.configuration')
|
|
config = Configuration(1)
|
|
account = config.get_multivalue(
|
|
'default_account_receivable', **pattern)
|
|
return account.id if account else None
|
|
|
|
def get_information_address(self, name):
|
|
if name != 'street':
|
|
attr = name[-4:]
|
|
field = name[:-4] + 'code'
|
|
else:
|
|
field = name
|
|
attr = ''
|
|
for address in self.addresses:
|
|
if hasattr(address, field):
|
|
val = getattr(address, field)
|
|
if hasattr(val, attr):
|
|
return getattr(val, attr)
|
|
return val
|
|
return ''
|
|
|
|
def get_country_iso(self, code, val):
|
|
pool = Pool()
|
|
CountryCode = pool.get('party.country_code')
|
|
countries = CountryCode.search([
|
|
('code', '=', code),
|
|
])
|
|
if countries:
|
|
return getattr(countries[0].country_iso, val)
|
|
|
|
def get_bank_info(self, name):
|
|
res = None
|
|
if self.bank_accounts:
|
|
if name == 'bank':
|
|
res = self.bank_accounts[0].bank.id
|
|
elif name == 'bank_name':
|
|
res = self.bank_accounts[0].bank.party.name
|
|
elif name == 'bank_account':
|
|
if self.bank_accounts[0].numbers:
|
|
res = self.bank_accounts[0].numbers[0].number
|
|
elif name == 'bank_account_type':
|
|
res = self.bank_accounts[0].numbers[0].type
|
|
elif name == 'bank_account_type_string':
|
|
res = self.bank_accounts[0].numbers[0].type_string
|
|
return res
|
|
|
|
@fields.depends('name', 'type_person', 'first_name', 'second_name',
|
|
'first_family_name', 'second_family_name')
|
|
def on_change_name(self):
|
|
second_family_name = None
|
|
first_family_name = None
|
|
second_name = None
|
|
first_name = None
|
|
if self.name and self.type_person == 'persona_natural':
|
|
name = self.name.rstrip()
|
|
names = name.split(' ')
|
|
first_name = names[0]
|
|
second_family_name = names[-1]
|
|
if len(names) > 1:
|
|
first_family_name = names[-2]
|
|
if len(names) == 2:
|
|
second_family_name = None
|
|
first_family_name = names[1]
|
|
elif len(names) == 5:
|
|
second_name = names[1] + ' ' + names[2]
|
|
elif len(names) == 4:
|
|
second_name = names[1]
|
|
self.second_family_name = second_family_name
|
|
self.first_family_name = first_family_name
|
|
self.second_name = second_name
|
|
self.first_name = first_name
|
|
|
|
@fields.depends('name', 'type_person', 'first_name', 'second_name',
|
|
'first_family_name', 'second_family_name')
|
|
def on_change_type_person(self):
|
|
second_family_name = None
|
|
first_family_name = None
|
|
second_name = None
|
|
first_name = None
|
|
if self.name and self.type_person == 'persona_natural':
|
|
name = self.name.rstrip()
|
|
names = name.split(' ')
|
|
first_name = names[0]
|
|
second_family_name = names[-1]
|
|
if len(names) > 1:
|
|
first_family_name = names[-2]
|
|
if len(names) == 2:
|
|
second_family_name = None
|
|
first_family_name = names[1]
|
|
elif len(names) == 5:
|
|
second_name = names[1] + ' ' + names[2]
|
|
elif len(names) == 4:
|
|
second_name = names[1]
|
|
self.second_family_name = second_family_name
|
|
self.first_family_name = first_family_name
|
|
self.second_name = second_name
|
|
self.first_name = first_name
|
|
|
|
@classmethod
|
|
def search_vat_number(cls, name, clause):
|
|
return [
|
|
('identifiers.code',) + tuple(clause[1:]),
|
|
]
|
|
|
|
def get_vat_number_city(self, name=None):
|
|
if self.id_number_city:
|
|
return self.id_number_city
|
|
|
|
def get_vat_number(self, name=None):
|
|
if self.id_number:
|
|
return self.id_number
|
|
for identifier in self.identifiers:
|
|
return identifier.code
|
|
|
|
def get_id_number_full(self, name=None):
|
|
res = self.id_number
|
|
if self.id_number and self.id_number.isdigit():
|
|
res = '{0:,}'.format(int(self.id_number)).replace(',', '.')
|
|
if self.check_digit is not None and self.type_document == '31':
|
|
res = res + '-' + str(self.check_digit)
|
|
return res
|
|
|
|
|
|
class PartyFix(Wizard):
|
|
'Party Fix Exo'
|
|
__name__ = 'account_col.party.fix'
|
|
start_state = 'fix_party_name'
|
|
fix_party_name = StateTransition()
|
|
|
|
def transition_fix_party_name(self):
|
|
pool = Pool()
|
|
Party = pool.get('party.party')
|
|
parties = Party.browse(Transaction().context.get('active_ids'))
|
|
for party in parties:
|
|
party.on_change_type_person()
|
|
party.save()
|
|
return 'end'
|
|
|
|
|
|
class PartyFixCodeStart(ModelView):
|
|
'Party Fix Code Start'
|
|
__name__ = 'account_col.party_fix_code.start'
|
|
code = fields.Char('New Code', required=True)
|
|
|
|
|
|
class PartyFixCode(Wizard):
|
|
'Party Fix Code'
|
|
__name__ = 'account.plus.party_fix_code'
|
|
start = StateView(
|
|
'account_col.party_fix_code.start',
|
|
'account_col.party_fix_code_start_view_form', [
|
|
Button('Cancel', 'end', 'tryton-cancel'),
|
|
Button('Ok', 'accept', 'tryton-ok', default=True),
|
|
])
|
|
accept = StateTransition()
|
|
|
|
def transition_accept(self):
|
|
party = Table('party_party')
|
|
cursor = Transaction().connection.cursor()
|
|
id_ = Transaction().context['active_id']
|
|
if id_:
|
|
cursor.execute(*party.update(
|
|
columns=[party.code],
|
|
values=[self.start.code],
|
|
where=party.id == id_)
|
|
)
|
|
return 'end'
|
|
|
|
|
|
class BankAccountNumber(ModelSQL, ModelView):
|
|
__name__ = 'bank.account.number'
|
|
|
|
@classmethod
|
|
def __setup__(cls):
|
|
super(BankAccountNumber, cls).__setup__()
|
|
new_sel = [
|
|
('checking_account', 'Checking Account'),
|
|
('saving_account', 'Saving Account'),
|
|
]
|
|
if new_sel not in cls.type.selection:
|
|
cls.type.selection.extend(new_sel)
|
|
cls.type_string = super(BankAccountNumber, cls).type.translated('type')
|
|
|
|
|
|
class Address(ModelSQL, ModelView):
|
|
__name__ = 'party.address'
|
|
country_code = fields.Many2One('party.country_code', 'Country')
|
|
department_code = fields.Many2One('party.department_code',
|
|
'Department', depends=['country_code'])
|
|
city_code = fields.Many2One('party.city_code', 'City',
|
|
domain=[
|
|
('department', '=', Eval('department_code')),
|
|
], depends=['country_code'])
|
|
|
|
@staticmethod
|
|
def default_country_code():
|
|
CountryCode = Pool().get('party.country_code')
|
|
countries = CountryCode.search([
|
|
('code', '=', '169'),
|
|
])
|
|
if countries:
|
|
return countries[0].id
|
|
|
|
def get_rec_name(self, name):
|
|
party = self.party_full_name
|
|
if self.street:
|
|
street = self.street.splitlines()[0]
|
|
else:
|
|
street = None
|
|
if self.country:
|
|
country = self.country.code
|
|
else:
|
|
country = None
|
|
return ', '.join(
|
|
filter(None, [
|
|
street,
|
|
party,
|
|
self.name,
|
|
self.postal_code,
|
|
self.city,
|
|
country]))
|
|
|
|
@classmethod
|
|
def search_rec_name(cls, name, clause):
|
|
if clause[1].startswith('!') or clause[1].startswith('not '):
|
|
bool_op = 'AND'
|
|
else:
|
|
bool_op = 'OR'
|
|
return [bool_op,
|
|
('street',) + tuple(clause[1:]),
|
|
('party',) + tuple(clause[1:]),
|
|
('name',) + tuple(clause[1:]),
|
|
('postal_code',) + tuple(clause[1:]),
|
|
('city',) + tuple(clause[1:]),
|
|
('country',) + tuple(clause[1:]),
|
|
]
|
|
|
|
# @classmethod
|
|
# def country_code_validate(cls):
|
|
# # CountryCode = Pool().get('party.country_code')
|
|
# Company = Pool().get('company.company')
|
|
# company_id = Transaction().context.get('company', None)
|
|
# if company_id:
|
|
# company = Company(company_id)
|
|
# print(company, 'jdjdj')
|
|
# print(company.party)
|
|
# res = 'company.id'
|
|
# res = None
|
|
# print(res, 'res')
|
|
# return res
|
|
# # if countries:
|
|
# # return countries[0].id
|
|
|
|
@staticmethod
|
|
def default_invoice():
|
|
return True
|
|
|
|
@staticmethod
|
|
def default_delivery():
|
|
return True
|
|
|
|
|
|
class Category(metaclass=PoolMeta):
|
|
__name__ = 'party.category'
|
|
code = fields.Char("Code")
|