# -*- coding: utf-8 -*- ############################################################################## # # OpenERP, Open Source Management Solution # Copyright (C) 2004-2010 Tiny SPRL (). # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as # published by the Free Software Foundation, either version 3 of the # License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Affero General Public License for more details. # # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . # ############################################################################## from openerp import models, fields, tools, api, _ from openerp.exceptions import ValidationError from math import floor import re import sys import logging def calculate_dv_of_ruc(input_str): # assure that we have a string if not isinstance(input_str, str): input_str = str(input_str) int(input_str) base = 11 k = 2 the_sum = 0 for i, c in enumerate(reversed(input_str)): if k > base: k = 2 the_sum += k * int(c) k += 1 _, rem = divmod(the_sum, base) if rem > 1: dv = base - rem else: dv = 0 return dv class res_partner(models.Model): _name = 'res.partner' _inherit = 'res.partner' _description = 'Add extra data to SET' ruc_ci = fields.Char(string='Ruc/CI', required=True) digitointer = fields.Char('RUC Internacional', size=13) dv = fields.Integer('Dígito Verificador', compute='_compute_dv', store=True) esinternacional = fields.Boolean(string='Es internacional' ,default = False) retencion = fields.Boolean(string='Retiene IVA' ,default = False) retentor_renta = fields.Boolean(string='Retentor renta' ,default = False) porcentaje_retencion = fields.Integer('Porcentaje de Retención') porcentaje_retencion_renta = fields.Integer('Porcentaje de Retención Renta') porcentaje_retencion_absorbida = fields.Integer('Porcentaje de Retención Absorbida') retiene_iva_cliente = fields.Boolean(string='Es agente retentor' ,default = False) concepto_iva = fields.Selection([('femenino','Femenino'),('masculino','Masculino')],'Concepto IVA') concepto_renta = fields.Char('Concepto renta') rucdv = fields.Char('RUC completo', compute='_compute_ruc_2', store=True) naturaleza_receptor = fields.Selection([('1','Contribuyente'),('2','No contribuyente')],'Naturaleza receptor') naturaleza_vendedor = fields.Selection([('1','No contribuyente'),('2','Extranjero')],'Naturaleza vendedor') nivel = fields.Integer('Nivel', size=2) nombre_fantasia = fields.Char('Nombre de fantasia') ci = fields.Char('CI', compute='_compute_ci', store=True) nro_casa = fields.Integer('Nro. Casa') nro_documento = fields.Char('Nro. Documento') timbrado = fields.Char('Timbrado') tipo_documento_identidad = fields.Char('Tipo Documento Identidad') iva_retencion_10 = fields.Integer('Porcentaje de retención sobre el IVA 10%') iva_retencion_5 = fields.Integer('Porcentaje de retención sobre el IVA 5%') tipo_documento_receptor = fields.Selection([('1','Cédula paraguaya'),('2','Pasaporte'),('3','Cédula extranjera'),('4','Carnet de residencia'),('5','Innominado'),('6','Tarjeta diplomática de exoneración fiscal')],'Tipo Documento Receptor') tipo_documento_vendedor = fields.Selection([('1','Cédula paraguaya'),('2','Pasaporte'),('3','Cédula extranjera'),('4','Carnet de residencia')],'Tipo Documento Vendedor') tipo_operacion = fields.Selection([('1','B2B'),('2','B2C'),('3','B2G'),('4','B2F')],'Tipo Operación') situacion = fields.Selection([('contribuyente','Contribuyente'),('nocontribuyente','No contribuyente')],'Situación') # activity_id = fields.Many2one("economic.activity", # string="Default Economic Activity", # context={'active_test': False}) # economic_activities_ids = fields.Many2many('economic.activity', # string='Economic Activities', # context={'active_test': False}) tipo_identificacion = fields.Selection([('1','Cédula paraguaya'),('2','RUC'),('3','Cédula extranjera'),('4','Carnet de residencia'),('5','Innominado')],'Tipo de Identificación') @api.depends('ruc_ci') def _compute_dv(self): for record in self: if isinstance(record.ruc_ci, str): ruc_parts = record.ruc_ci.split('-') ruc_without_verification = ruc_parts[0] if len(ruc_parts) > 1 else record.ruc_ci if record.tipo_identificacion == '1': record.dv = calculate_dv_of_ruc(ruc_without_verification) else: if record.tipo_identificacion == '2': record.dv = calculate_dv_of_ruc(ruc_without_verification) else: record.dv = 0 else: record.dv = False @api.depends('ruc_ci') def _compute_ci(self): for record in self: if isinstance(record.ruc_ci, str): ruc_parts = record.ruc_ci.split('-') record.ci = ruc_parts[0] if len(ruc_parts) > 1 else record.ruc_ci @api.depends('ruc_ci', 'dv') def _compute_ruc_2(self): for record in self: ruc_without_verification = record.ci if record.ci else record.ruc_ci record.rucdv = "{}-{}".format(ruc_without_verification, record.dv) # @staticmethod # def _validate_ruc(vat): # factor = '43298765432' # sum = 0 # dig_check = None # if len(vat) != 12: # return False # try: # int(vat) # except ValueError: # return False # for f in range(0, 11): # sum += int(factor[f]) * int(vat[f]) # subtraction = 11 - floor((sum % 11)) # if subtraction < 10: # dig_check = subtraction # elif subtraction == 10: # dig_check = "" # elif subtraction == 11: # dig_check = 0 # # if not int(vat[11]) == dig_check: # return False # return True # # @staticmethod # # def _validate_ci(vat): # # def _validate_ci(vat): # logging.warning(vat) # ruc = '' # basemax = 7 # for c in str(vat).replace('-', ''): # ruc += c.isdigit() and c or str(ord(c)) # k = 2 # total = 0 # # for c in reversed(ruc[:-1]): # n = int(c) # if n > basemax: # k = 2 # total += n * k # k += 1 # resto = total % basemax # if resto > 1: # n = basemax - resto # else: # n = 0 # return n == int(ruc[-1]) # vat = vat.replace("-", "").replace('.', '') # sum = 0 # if not vat: # return False # try: # int(vat) # except ValueError: # return False # vat = "%08d" % int(vat) # long = len(vat) # if long > 8: # return False # code = [2, 9, 8, 7, 6, 3, 4] # for f in range(0, long - 1): # sum += int(vat[f]) * int(code[f]) # total = sum + int(vat[-1]) # subtraction = total % 10 # if subtraction != 0: # return False # return True # """ # El numero de una cédula de identidad tiene exactamente 7 dígitos al cual se le adiciona # un dígito verificador. # # Es así que un número valido debe respetar el siguiente formato: # # a.bcd.efg-h # # El dígito posterior al guión (h) es también llamado dígito verificador. # # Para obtener h debemos: # # Multiplicar a,b,c,d,e,f,g por las siguientes constantes: # (a; b; c; d; e; f; g) .* (2; 9; 8; 7; 6; 3; 4) # # El resultado de la suma s = 2*a + 9*b + 8*c + 7*d + 6*e + 3*f + 4*g es dividido por 10 # quedándonos con resto (M = s modulo 10) # # Finalmente h = (10 - M) % 10 # # Ejemplo practico: # Si la CI es 1.234.567: # # s = 2*1 + 9*2 + 8*3 + 7*4 + 6*5 + 3*6 + 4*7 = 148 # M = 148 % 10 = 8 # h = (10 - 8) % 10 = 2 # Obteniendo que 1.234.567-2 es un número de CI valido. # """ # class res_partner(models.Model): # _inherit = 'res.partner' # code = [2, 9, 8, 7, 6, 3, 4] # # # def __init__(self, vat): # """ Inicializa la clase # Asigna un numero de cedula a la propiedad publica numero # # Args: # numero: un numero de cedula entre 6 y 7 digitos, puede ser int o string # """ # self.ruc = vat # self._verifier = "" # # @property # def numero(self): # """ propiedad de lectura # # Returns: # _numero: la propiedad privada de numero de cedula ya normalizada # """ # return self._ruc # # @numero.setter # def numero(self, val): # """ asigna a la propiedad privada el numero de cedula asignado a numero, # lo normaliza y valida # Args: # val (int o string): el numero de cedula # # """ # try: # if isinstance(val, str): # # si la cedula es un string, le saco el formato (puntos y guiones) # numUpdated = re.sub(r"[\.|\-]", "", val) # self._ruc = numUpdated # else: # self._ruc = str(val) # # if not self._ruc.isnumeric(): # sys.exit("cedula should be only numbers") # # if (len(self._ruc) < 6 or len(self._ruc) > 7): # sys.exit("invalid cedula length...") # # except: # sys.exit( # "Cedula conversion error, check cedula length, value or format.") # # @property # def verifier(self): # """propiedad de lectura del digito verificador # # Returns: # int: digito verificador # """ # return self._verifier # # def __acumSequence(self, seq, index): # """ funcion recursiva privada que suma los digitos de una cedula # # Args: # seq: el numero en la constante SEQUENCE # index (int): la posicion en ambos arrays (secuencia y cedula) # Deben matchear en posicion # # ** NOTA: como la correspondencia debe ser 1:1 seq y _numero, en caso de ser # una cedula de 6 digitos se podia haber agregado un 0 al inicio de _numero para comenzar # con el mismo largo (7 digitos), pero asi es mas divertido :D # # Returns: # int: la acumulacion de la suma de digito de self._numero * digito de secuencia # """ # if index < (len(seq) - 1): # return (seq[index] * int(self._ruc[index])) + self.__acumSequence(seq, index + 1) # return seq[index] * int(self._ruc[index]) # # def getVerifierDigit(self): # """ calculo de digito verificador # # Returns: # int: digito verificador # """ # try: # digit_diff = (len(self.code) - len(self._ruc)) # if digit_diff >= 0: # acum = 0 # acum = self.__acumSequence(self.code[digit_diff:], 0) # mod = (acum % 10) # verifier = ((10 - mod) % 10) # self._verifier = str(verifier) # return verifier # except: # print("ERROR: unknown error, check params!") # # def formatCedula(self): # """ formatea un numero de cedula al formato de puntos y guion # ej: 12345678 => 1.234.567-8 # # Returns: # string: el numero de cedula completo y formateado # """ # self.getVerifierDigit() # if len(self.ruc) == 6: # return self.ruc[:3] + '.' + self.ruc[3:] + '-' + self.verifier # return self.ruc[:1] + '.' + self.ruc[1:4] + '.' + self.ruc[4:] + '-' + self.verifier