From 282f31497fc3ddfb99f7158926d38b07923371b6 Mon Sep 17 00:00:00 2001 From: Mauricio Baeza Date: Mon, 27 Aug 2018 00:53:52 -0500 Subject: [PATCH] Save cfdi pay --- source/app/models/db.py | 6 ++ source/app/models/main.py | 149 +++++++++++++++++++++----- source/app/settings.py | 1 + source/static/js/controller/bancos.js | 68 +++++++++++- source/static/js/ui/admin.js | 32 ++++-- source/static/js/ui/bancos.js | 29 +++-- 6 files changed, 233 insertions(+), 52 deletions(-) diff --git a/source/app/models/db.py b/source/app/models/db.py index c45a566..9faffbf 100644 --- a/source/app/models/db.py +++ b/source/app/models/db.py @@ -416,3 +416,9 @@ class StorageEngine(object): # ~ Revisado def get_invoicepay(self, values): return main.FacturasPagos.get_values(values) + + def get_cfdipay(self, values): + return main.CfdiPagos.get_values(values) + + def cfdipay(self, values): + return main.CfdiPagos.post(values) diff --git a/source/app/models/main.py b/source/app/models/main.py index 6feecc0..abeafb5 100644 --- a/source/app/models/main.py +++ b/source/app/models/main.py @@ -34,7 +34,8 @@ from controllers import util from settings import log, DEBUG, VERSION, PATH_CP, COMPANIES, PRE, CURRENT_CFDI, \ INIT_VALUES, DEFAULT_PASSWORD, DECIMALES, IMPUESTOS, DEFAULT_SAT_PRODUCTO, \ CANCEL_SIGNATURE, PUBLIC, DEFAULT_SERIE_TICKET, CURRENT_CFDI_NOMINA, \ - DEFAULT_SAT_NOMINA, DECIMALES_TAX, TITLE_APP, MV, DECIMALES_PRECIOS + DEFAULT_SAT_NOMINA, DECIMALES_TAX, TITLE_APP, MV, DECIMALES_PRECIOS, \ + DEFAULT_SERIE_CFDIPAY FORMAT = '{0:.2f}' @@ -891,6 +892,7 @@ class Certificado(BaseModel): rfc = TextField(default='') desde = DateTimeField(null=True) hasta = DateTimeField(null=True) + es_fiel = BooleanField(default=False) def __str__(self): return self.serie @@ -1947,33 +1949,6 @@ class MovimientosBanco(BaseModel): return {'ok': True, 'rows': rows} -class CfdiPagos(BaseModel): - movimiento = ForeignKeyField(MovimientosBanco) - serie = TextField(default='') - folio = IntegerField(default=0) - fecha = DateTimeField(default=util.now, formats=['%Y-%m-%d %H:%M:%S']) - fecha_timbrado = DateTimeField(null=True) - lugar_expedicion = TextField(default='') - tipo_relacion = TextField(default='') - uuid_relacionado = UUIDField(null=True) - xml = TextField(default='') - uuid = UUIDField(null=True) - estatus = TextField(default='Guardado') - estatus_sat = TextField(default='') - notas = TextField(default='') - cancelado = BooleanField(default=False) - - class Meta: - order_by = ('movimiento',) - - @classmethod - def new(cls, id_mov): - - row = {} - data = {'ok': True, 'row': row} - return data - - class SATUsoCfdi(BaseModel): key = TextField(index=True, unique=True) name = TextField(default='', index=True) @@ -5266,6 +5241,115 @@ class FacturasPagos(BaseModel): return getattr(cls, '_get_{}'.format(opt))(cls, values) +class CfdiPagos(BaseModel): + movimiento = ForeignKeyField(MovimientosBanco) + socio = ForeignKeyField(Socios) + serie = TextField(default='') + folio = IntegerField(default=0) + fecha = DateTimeField(default=util.now, formats=['%Y-%m-%d %H:%M:%S']) + fecha_timbrado = DateTimeField(null=True) + lugar_expedicion = TextField(default='') + tipo_relacion = TextField(default='') + uuid_relacionado = UUIDField(null=True) + xml = TextField(default='') + uuid = UUIDField(null=True) + estatus = TextField(default='Guardada') + estatus_sat = TextField(default='') + notas = TextField(default='') + error = TextField(default='') + cancelado = BooleanField(default=False) + + class Meta: + order_by = ('movimiento',) + + @classmethod + def post(cls, values): + opt = values.pop('opt') + return getattr(cls, '_{}'.format(opt))(cls, values) + + def _get_folio(self, serie): + folio = int(Configuracion.get_('txt_config_cfdipay_folio') or '0') + start = (CfdiPagos + .select(fn.Max(CfdiPagos.folio).alias('mf')) + .where(CfdiPagos.serie==serie) + .order_by(SQL('mf')) + .scalar()) + + if start is None: + next_folio = 1 + else: + next_folio = start + 1 + + if folio > next_folio: + next_folio = folio + + return next_folio + + def _new(self, values): + id_mov = int(values['id_mov']) + + filters = (FacturasPagos.movimiento==id_mov) + related = FacturasPagos.select().where(filters) + if not related: + msg = 'El pago no tiene facturas relacionadas' + data = {'ok': False, 'msg': msg} + return data + + partner = tuple(set([f.factura.cliente.id for f in related])) + if len(partner) > 1: + msg = 'Facturas relacionadas a diferentes clientes' + data = {'ok': False, 'msg': msg} + return data + + partner = partner[0] + partner_name = related[0].factura.cliente.nombre + + filters = ( + (CfdiPagos.movimiento==id_mov) & + (CfdiPagos.cancelado==False) + ) + previous = CfdiPagos.select().where(filters) + if previous: + msg = 'Hay una factura activa, es necesario cancelarla primero' + data = {'ok': False, 'msg': msg} + return data + + emisor = Emisor.select()[0] + serie = Configuracion.get_('txt_config_cfdipay_serie') or DEFAULT_SERIE_CFDIPAY + fields = {} + fields['movimiento'] = id_mov + fields['socio'] = partner + fields['serie'] = serie + fields['folio'] = self._get_folio(self, serie) + fields['lugar_expedicion'] = emisor.cp_expedicion or emisor.codigo_postal + + # ~ with database_proxy.atomic() as txn: + # ~ obj = CfdiPagos.create(**fields) + + # ~ row = { + # ~ 'id': obj.id, + # ~ 'serie': obj.serie, + # ~ 'folio': obj.folio, + # ~ 'uuid': obj.uuid, + # ~ 'fecha': obj.fecha, + # ~ 'tipo_comprobante': 'P', + # ~ 'estatus': obj.estatus, + # ~ 'cliente': partner_name, + # ~ } + row = { + 'id': 1, + 'serie': 'FP', + 'folio': 1, + 'uuid': '', + 'fecha': util.now(), + 'tipo_comprobante': 'P', + 'estatus': 'Timbrada', + 'cliente': partner_name, + } + data = {'ok': True, 'row': row} + return data + + class PreFacturasImpuestos(BaseModel): factura = ForeignKeyField(PreFacturas) impuesto = ForeignKeyField(SATImpuestos) @@ -7532,18 +7616,27 @@ def _migrate_tables(): if 'cancelado' in columns: migrations.append(migrator.drop_column('facturaspagos', 'cancelado')) + columns = [c.name for c in database_proxy.get_columns('certificado')] + if not 'es_fiel' in columns: + es_fiel = BooleanField(default=False) + migrations.append(migrator.add_column('certificado', 'es_fiel', es_fiel)) + columns = [c.name for c in database_proxy.get_columns('cfdipagos')] if not 'serie' in columns: + socio = ForeignKeyField(Socios, null=True, to_field=Socios.id) serie = TextField(default='') folio = IntegerField(default=0) lugar_expedicion = TextField(default='') + error = TextField(default='') tipo_relacion = TextField(default='') uuid_relacionado = UUIDField(null=True) migrations.append(migrator.add_column('cfdipagos', 'serie', serie)) migrations.append(migrator.add_column('cfdipagos', 'folio', folio)) migrations.append(migrator.add_column('cfdipagos', 'lugar_expedicion', lugar_expedicion)) + migrations.append(migrator.add_column('cfdipagos', 'error', error)) migrations.append(migrator.add_column('cfdipagos', 'tipo_relacion', tipo_relacion)) migrations.append(migrator.add_column('cfdipagos', 'uuid_relacionado', uuid_relacionado)) + migrations.append(migrator.add_column('cfdipagos', 'socio_id', socio)) if migrations: with database_proxy.atomic() as txn: diff --git a/source/app/settings.py b/source/app/settings.py index 9e07c20..0d1ed66 100644 --- a/source/app/settings.py +++ b/source/app/settings.py @@ -154,6 +154,7 @@ IMPUESTOS = { } DEFAULT_SAT_PRODUCTO = '01010101' DEFAULT_SERIE_TICKET = 'T' +DEFAULT_SERIE_CFDIPAY = 'FP' DIR_FACTURAS = 'facturas' USAR_TOKEN = False CANCEL_SIGNATURE = False diff --git a/source/static/js/controller/bancos.js b/source/static/js/controller/bancos.js index 288062b..a683c70 100644 --- a/source/static/js/controller/bancos.js +++ b/source/static/js/controller/bancos.js @@ -38,6 +38,10 @@ var bancos_controllers = { $$('filter_cuenta_year').attachEvent('onChange', filter_cuenta_change) $$('filter_cuenta_month').attachEvent('onChange', filter_cuenta_change) $$('filter_cuenta_dates').attachEvent('onChange', filter_cuenta_dates_change) + + $$('cmd_pay_stamp').attachEvent('onItemClick', cmd_pay_stamp_click) + $$('cmd_pay_cancel').attachEvent('onItemClick', cmd_pay_cancel_click) + set_year_month() } } @@ -693,7 +697,7 @@ function filter_cuenta_dates_change(range){ function set_data_pay(row){ - var form = $$('form_banco_pagos') + var form = $$('form_bank_pay') var dt = row.fecha.split(' ') var grid = $$('grid_pay_related') grid.clearAll() @@ -745,5 +749,65 @@ function cmd_complemento_pago_click(){ } set_data_pay(row) - $$('multi_bancos').setValue('banco_pagos') + $$('multi_bancos').setValue('bank_pay') +} + + +function validate_cfdi_pay(form){ + if(!form.validate()) { + msg_error('Valores inválidos') + return false + } + + return true +} + + +function save_cfdi_pay(form){ + var values = form.getValues() + var data = {'opt': 'new', 'id_mov': values.id_mov} + + webix.ajax().sync().post('cfdipay', data, { + error:function(text, data, XmlHttpRequest){ + msg = 'Ocurrio un error, consulta a soporte técnico' + msg_error(msg) + }, + success:function(text, data, XmlHttpRequest){ + result = data.json(); + if(result.ok){ + msg_ok('Factura guardada correctamente
Enviando a timbrar...') + $$('grid_cfdi_pay').add(result.row) + }else{ + msg_error(result.msg) + } + } + }) +} + + +function cmd_pay_stamp_click(){ + var form = $$('form_bank_pay') + var title = 'Timbrar Factura de Pago' + msg = '¿Estás seguro de enviar a timbrar este pago?' + + if (!validate_cfdi_pay(form)){ + return + } + + webix.confirm({ + title: title, + ok: 'Si', + cancel: 'No', + type: 'confirm-error', + text: msg, + callback:function(result){ + if(result){ + save_cfdi_pay(form) + } + } + }) +} + + +function cmd_pay_cancel_click(){ } diff --git a/source/static/js/ui/admin.js b/source/static/js/ui/admin.js index 248f6d2..7936869 100644 --- a/source/static/js/ui/admin.js +++ b/source/static/js/ui/admin.js @@ -233,18 +233,38 @@ var emisor_otros_datos= [ ] -var emisor_certificado = [ +var col_sello = {rows: [ {template: 'Certificado actual', type: 'section'}, {view: 'form', id: 'form_cert', rows: [ {cols: [{view: 'text', id: 'cert_rfc', name: 'cert_rfc', - label: 'RFC: ', readonly: true, placeholder: 'Ninguno'}, {}]}, + label: 'RFC: ', readonly: true, placeholder: 'Ninguno'}]}, {cols: [{view: 'text', id: 'cert_serie', name: 'cert_serie', - label: 'Serie: ', readonly: true, placeholder: 'Ninguno'}, {}]}, + label: 'Serie: ', readonly: true, placeholder: 'Ninguno'}]}, {cols: [{view: 'text', id: 'cert_desde', name: 'cert_desde', - label: 'Vigente desde: ', readonly: true}, {}]}, + label: 'Vigente desde: ', readonly: true}]}, {cols: [{view: 'text', id: 'cert_hasta', name: 'cert_hasta', - label: 'Vigente hasta: ', readonly: true}, {}]}, - ]}, + label: 'Vigente hasta: ', readonly: true}]}, + ]} +]} + + +var col_fiel = {rows: [ + {template: 'Fiel actual', type: 'section'}, + {view: 'form', id: 'form_fiel', rows: [ + {cols: [{view: 'text', id: 'fiel_rfc', name: 'fiel_rfc', + label: 'RFC: ', readonly: true, placeholder: 'Ninguno'}]}, + {cols: [{view: 'text', id: 'fiel_serie', name: 'fiel_serie', + label: 'Serie: ', readonly: true, placeholder: 'Ninguno'}]}, + {cols: [{view: 'text', id: 'fiel_desde', name: 'fiel_desde', + label: 'Vigente desde: ', readonly: true}]}, + {cols: [{view: 'text', id: 'fiel_hasta', name: 'fiel_hasta', + label: 'Vigente hasta: ', readonly: true}]}, + ]} +]} + + +var emisor_certificado = [ + {cols: [col_sello, col_fiel]}, {template: 'Cargar Certificado', type: 'section'}, {view: 'form', id: 'form_upload', rows: [ {cols: [{}, diff --git a/source/static/js/ui/bancos.js b/source/static/js/ui/bancos.js index 5247687..32cd3be 100644 --- a/source/static/js/ui/bancos.js +++ b/source/static/js/ui/bancos.js @@ -130,10 +130,7 @@ var grid_cfdi_pago_cols = [ adjust: 'header', sort: 'string'}, {id: "estatus", header: ["Estatus"], adjust: "data", sort:"string"}, - {id: 'total_mn', header: ['Total M.N.'], width: 150, sort: 'int', - format: webix.i18n.priceFormat, css: 'right'}, - {id: 'cliente', header: ['Razón Social'], - fillspace: true, sort: 'string', footer: {text: '$ 0.00', colspan: 2}}, + {id: 'cliente', header: ['Razón Social'], fillspace: true}, {id: 'xml', header: 'XML', adjust: 'data', template: get_icon('xml')}, {id: 'pdf', header: 'PDF', adjust: 'data', template: get_icon('pdf')}, {id: 'email', header: '', adjust: 'data', template: get_icon('email')} @@ -205,9 +202,9 @@ var grid_cfdi_este_deposito = { } -var grid_cfdi_pago = { +var grid_cfdi_pay = { view: 'datatable', - id: 'grid_cfdi_pago', + id: 'grid_cfdi_pay', select: 'row', autoConfig: false, adjust: true, @@ -266,12 +263,12 @@ var toolbar_banco_deposito = [ ] -var toolbar_banco_pagos = [ +var toolbar_bank_pay = [ {view: 'label', label: 'Factura de pago'}, {}, - {view: 'button', id: 'cmd_pago_timbrar', label: 'Timbrar', + {view: 'button', id: 'cmd_pay_stamp', label: 'Timbrar', type: 'iconButton', autowidth: true, icon: 'ticket'}, - {view: 'button', id: 'cmd_pago_cancelar', label: 'Cancelar', + {view: 'button', id: 'cmd_pay_cancel', label: 'Cancelar', type: 'iconButton', autowidth: true, icon: 'ban'}, {}, {view: 'icon', click: '$$("multi_bancos").setValue("banco_home")', @@ -349,8 +346,8 @@ var controls_banco_deposito = [ ] -var controls_banco_pagos = [ - {view: 'toolbar', elements: toolbar_banco_pagos}, +var controls_bank_pay = [ + {view: 'toolbar', elements: toolbar_bank_pay}, {view: 'label', label: 'Este depósito: '}, {cols: [ {view: 'datepicker', id: 'pay_date', name: 'pay_date', @@ -378,7 +375,7 @@ var controls_banco_pagos = [ labelWidth: 125, height: 70, readonly: true}, ]}, {view: 'label', label: 'Facturas de pago de este depósito: '}, - grid_cfdi_pago, + grid_cfdi_pay, {view: 'label', label: 'Facturas relacionadas en este pago: '}, grid_pay_related ] @@ -410,15 +407,15 @@ var form_banco_deposito = { } -var form_banco_pagos = { +var form_bank_pay = { type: 'space', responsive: true, cols: [{ view: 'form', - id: 'form_banco_pagos', + id: 'form_bank_pay', complexData: true, scroll: true, - elements: controls_banco_pagos, + elements: controls_bank_pay, }], } @@ -435,7 +432,7 @@ var multi_bancos = { ]}, {id: 'banco_retiro', rows: [form_banco_retiro]}, {id: 'banco_deposito', rows: [form_banco_deposito]}, - {id: 'banco_pagos', rows: [form_banco_pagos]} + {id: 'bank_pay', rows: [form_bank_pay]} ], }