|
@@ -0,0 +1,267 @@
|
|
|
+# -*- coding: utf-8 -*-
|
|
|
+##############################################################################
|
|
|
+#
|
|
|
+# OpenERP, Open Source Management Solution
|
|
|
+# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
|
|
|
+#
|
|
|
+# 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 <http://www.gnu.org/licenses/>.
|
|
|
+#
|
|
|
+##############################################################################
|
|
|
+
|
|
|
+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())
|