# -*- 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 . # ############################################################################## import logging from openerp import models, fields, tools, api, _ from openerp.exceptions import Warning as ValidationError import json import base64 import requests import io import hashlib _logger = logging.getLogger(__name__) class AccountInvoice(models.Model): _name = 'account.invoice' _inherit = 'account.invoice' _description = 'Add extra data to SET in account invoice' numero_factura = fields.Char(string='Factura Nº', related='number', store = True) sucursal = fields.Char(string='Establecimiento', compute='_compute_suc_sec', store=True) sec = fields.Char(string='Punto', compute='_compute_suc_sec', store=True) descripcion = fields.Text("Descripción") nro_actual = fields.Char('Nro Actual', compute='_compute_suc_sec', store=True) talonario_id = fields.Many2one('talonario', 'Talonario') # Remover required temporalmente timbrado_name = fields.Char(related='talonario_id.name', string='Número de timbrado', readonly=True) talonario_tipo_documento = fields.Selection(related='talonario_id.tipo_documento', string='Tipo de documento', readonly=True) fecha_final = fields.Date(related='talonario_id.fecha_final', string='Fecha de expiración de timbrado', readonly=True) nro_fin = fields.Integer( string='Nro Final', related='talonario_id.nro_fin', store=True, readonly=True ) no_mostrar_libro_iva = fields.Boolean(string='No visualizar en el libro IVA') importacion = fields.Boolean(string='Fact. de importación') importacion_gasto = fields.Boolean(string='Fact. de gastos de importación') # envio_lote_id = fields.Many2one('envio.lotes', string='Envio Lote') # qr_code = fields.Binary('Código QR', compute='_generate_qr_code') # texto_qr = fields.Char(invisible=True) dProtAut = fields.Char('Código de operación') operacion_credito = fields.Selection([('1','Normal'),('2','Contingencia')],'Operación Crédito') tipo_factura = fields.Selection([('1','1'),('2','2')],'Tipo de Factura') metodo_pago = fields.Selection([('1','Dinero En Efectivo'),('2','Transferencia'),('3','Cheque'),('4','Tarjeta de crédito'),('5','Tarjeta de débito'),('6','Giro'),('7','Billetera Electronica'),('8','Tarjeta Empresarial'),('9','Vales'),('10','Retención')],'Método de Pago') # mje_resultado_ids = fields.One2many('mje.resultado', 'invoice_id', string='Resultados de mensajes') # dEstRes = fields.Char('Estado de respuesta') @api.depends('number', 'journal_id', 'state') def _compute_suc_sec(self): for invoice in self: if invoice.state == 'cancel' and invoice.type == 'out_invoice': invoice.sucursal = '' invoice.sec = '' invoice.nro_actual = '' elif invoice.journal_id.id == 2 and invoice.number and invoice.type == 'out_invoice': parts = invoice.number.split('-') invoice.sucursal = parts[0] if len(parts) > 0 else '' invoice.sec = parts[1] if len(parts) > 1 else '' invoice.nro_actual = parts[2] if len(parts) > 2 else '' else: invoice.sucursal = '' invoice.sec = '' invoice.nro_actual = '' @api.constrains('nro_actual', 'nro_fin') def _check_nro_actual_vs_nro_fin(self): for invoice in self: if invoice.talonario_id and invoice.nro_fin and invoice.nro_actual: try: nro_actual_int = int(invoice.nro_actual) except ValueError: raise ValidationError("El número actual debe ser un número válido.") if nro_actual_int > invoice.nro_fin: raise ValidationError( "El número actual (%s) excede el número final permitido en el talonario (%s)." % (nro_actual_int, invoice.nro_fin) ) @api.onchange('talonario_id') def _onchange_talonario_id(self): if self.talonario_id: self.timbrado_name = self.talonario_id.name self.talonario_tipo_documento = self.talonario_id.tipo_documento self.fecha_final = self.talonario_id.fecha_final else: self.timbrado_name = False self.talonario_tipo_documento = False self.fecha_final = False @api.onchange('type', 'company_id') def _onchange_type_set_talonario_domain(self): """ - out_invoice -> talonario.tipo_documento '1' (Factura) - out_refund -> talonario.tipo_documento '5' (Nota de crédito) Siempre filtra por active=True y company_id de la factura. Autoselecciona si existe exactamente uno. """ domain = [] if self.type == 'out_invoice': domain = [('tipo_documento', '=', '1'), ('activo', '=', True)] elif self.type == 'out_refund': domain = [('tipo_documento', '=', '5'), ('activo', '=', True)] else: domain = [('id', '=', False)] if self.company_id: domain.append(('company_id', '=', self.company_id.id)) res = {'domain': {'talonario_id': domain}} # Autoseleccionar si hay exactamente un talonario válido if not self.talonario_id and domain and domain != [('id', '=', False)]: talonarios = self.env['talonario'].search(domain, limit=2) if len(talonarios) == 1: self.talonario_id = talonarios[0].id return res @api.model def create(self, vals): """ Si no se pasa talonario_id, se asigna automáticamente: - out_invoice -> talonario.tipo_documento '1' - out_refund -> talonario.tipo_documento '5' Filtrado por active=True y company_id de la factura. """ if not vals.get('talonario_id'): inv_type = vals.get('type') or self._context.get('type') company_id = vals.get('company_id') or self._context.get('company_id') if inv_type in ('out_invoice', 'out_refund'): tipo = '1' if inv_type == 'out_invoice' else '5' domain = [('tipo_documento', '=', tipo), ('activo', '=', True)] if company_id: domain.append(('company_id', '=', company_id)) tal = self.env['talonario'].search(domain, limit=1) if tal: vals['talonario_id'] = tal.id return super(AccountInvoice, self).create(vals) # class AccountInvoice1(models.Model): # _inherit = 'account.invoice' # # def send_invoice_to_sifen(self): # for record in self: # url = 'https://sifen.set.gov.py/api' # headers = {'Content-Type': 'application/json'} # data = record.prepare_invoice_data() # response = requests.post(url, json=data, headers=headers) # if response.status_code == 200: # self.handle_response(response.json()) # else: # raise UserError('Error sending invoice to SIFEN: {}'.format(response.content)) # # def prepare_invoice_data(self): # self.ensure_one() # return { # "tipoDocumento": self.talonario_tipo_documento, # "establecimiento": self.sucursal, # "codigoSeguridadAleatorio": self.cdc, # "punto": self.sec, # "numero": self.nro_actual, # "descripcion": self.descripcion, # "observacion": self.observacion, # "tipoContribuyente": self.tipo_contribuyente, # "fecha": self.date_invoice.isoformat(), # "tipoEmision": self.tipo_emision, # "tipoTransaccion": self.tipo_contribuyente, # "tipoImpuesto": self.tipo_impuesto, # "moneda": self.moneda, # "condicionAnticipo": self.condicion_anticipo, # "condicionTipoCambio": self.condicion_tipo_cambio, # "cambio": self.cambio, # "cliente": { # "contribuyente": self.partner_id.situacion, # "ruc": self.partner_id.ruc, # "razonSocial": self.partner_id.name, # "nombreFantasia": self.partner_id.name, # "tipoOperacion": self.partner_id.tipo_operacion, # "direccion": self.partner_id.street, # "numeroCasa": self.partner_id.nro_casa, # "departamento": self.partner_id.street, # "departamentoDescripcion": self.partner_id.street, # "distrito": self.partner_id.city, # "distritoDescripcion": self.partner_id.street, # "ciudad": self.partner_id.city, # "ciudadDescripcion": self.partner_id.street, # "pais": self.partner_id.country_id.code, # "paisDescripcion": self.partner_id.country_id.name, # "tipoContribuyente": self.partner_id.situacion, # "documentoTipo": self.partner_id.tipo_documento_receptor, # "documentoNumero": self.partner_id.nro_documento, # "telefono": self.partner_id.phone, # "celular": self.partner_id.mobile, # "email": self.partner_id.email, # "codigo": self.partner_id.dv # }, # # Añadir otros campos según sea necesario # } # # def handle_response(self, response): # if response.get('status') == 'approved': # self.write({'state': 'open'}) # else: # raise UserError('Factura no fue aprobado por la SIFEN: {}'.format(response.get('message'))) # # def _generate_cdc(self): # data_to_hash = ( # str(self.talonario_tipo_documento) + # self.sucursal + # self.cdc + # self.sec + # self.nro_actual + # self.date_invoice.strftime('%Y%m%d%H%M%S') + # str(self.tipo_transaccion) + # str(self.amount_total) # ) # md5_hash = hashlib.md5(data_to_hash.encode('utf-8')).digest() # cdc_base32 = base64.b32encode(md5_hash).decode('utf-8').rstrip('=') # self.cdc = cdc_base32 # # def _generate_qr_code(self): # import qrcode # from io import BytesIO # qr_data = "cdc={}&ruc={}&date={}".format(self.cdc, self.partner_id.ruc, self.date_invoice) # qr = qrcode.QRCode( # version=1, # error_correction=qrcode.constants.ERROR_CORRECT_L, # box_size=10, # border=4, # ) # qr.add_data(qr_data) # qr.make(fit=True) # img = qr.make_image(fill='black', back_color='white') # buffer = BytesIO() # img.save(buffer, format="PNG") # self.qr_code = base64.b64encode(buffer.getvalue())