trytonpsk-account_col/party.py

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")