From f4779dbb9938b456eb0951301f82322b0a6d670d Mon Sep 17 00:00:00 2001 From: El Mau Date: Fri, 17 Dec 2021 21:14:23 -0600 Subject: [PATCH 01/21] Validar que se seleccionen los dos archivos de los certificados --- source/app/models/main.py | 1 + source/static/js/controller/admin.js | 17 +++++++++++++++-- source/static/js/ui/admin.js | 6 ++++++ 3 files changed, 22 insertions(+), 2 deletions(-) diff --git a/source/app/models/main.py b/source/app/models/main.py index 1c89d85..50f4ff6 100644 --- a/source/app/models/main.py +++ b/source/app/models/main.py @@ -478,6 +478,7 @@ class Configuracion(BaseModel): fields = ( 'chk_config_ine', 'chk_config_edu', + 'chk_config_carta_porte', 'chk_config_pagos', 'chk_config_divisas', 'chk_cfg_pays_data_bank', diff --git a/source/static/js/controller/admin.js b/source/static/js/controller/admin.js index 774eb0d..f02390b 100644 --- a/source/static/js/controller/admin.js +++ b/source/static/js/controller/admin.js @@ -113,6 +113,7 @@ var controllers = { $$('chk_config_divisas').attachEvent('onItemClick', chk_config_item_click) $$('chk_config_ine').attachEvent('onItemClick', chk_config_item_click) $$('chk_config_edu').attachEvent('onItemClick', chk_config_item_click) + $$('chk_config_carta_porte').attachEvent('onItemClick', chk_config_item_click) $$('chk_config_leyendas_fiscales').attachEvent('onItemClick', chk_config_item_click) $$('cmd_admin_leyendas_fiscales').attachEvent('onItemClick', cmd_admin_leyendas_fiscales_click) @@ -2129,8 +2130,7 @@ function grid_usuarios_on_check(row, column, state){ value: state, } - - if(column='in_branch'){ + if(column=='in_branch'){ set_user_branch(row, state) return } @@ -2808,6 +2808,17 @@ function cmd_subir_certificado_click(){ var values = form.getValues() + if(!file_cer){ + msg = 'Selecciona el archivo CER del certificado' + msg_error(msg) + return + } + if(!file_key){ + msg = 'Selecciona el archivo KEY del certificado' + msg_error(msg) + return + } + if(!values.contra.trim()){ msg = 'La contraseña no puede estar vacía' msg_error(msg) @@ -2838,6 +2849,8 @@ function cmd_subir_certificado_click(){ $$('form_upload').setValues({}) $$('up_cert').files.data.clearAll() + file_cer = null + file_key = null } diff --git a/source/static/js/ui/admin.js b/source/static/js/ui/admin.js index b610950..e15d36f 100644 --- a/source/static/js/ui/admin.js +++ b/source/static/js/ui/admin.js @@ -867,6 +867,12 @@ var options_admin_complements = [ type: 'form', align: 'center', autowidth: true, disabled: true}, {}, {maxWidth: 15} ]}, + {maxHeight: 20}, + {template: 'Complemento para Carta Porte', type: 'section'}, + {cols: [{maxWidth: 15}, + {view: 'checkbox', id: 'chk_config_carta_porte', labelWidth: 0, + labelRight: 'Usar el complemento Carta Porte'}, + {maxWidth: 15}]}, ] From bb90b3f6d7ab342946b4f3516f546b95e00b17b1 Mon Sep 17 00:00:00 2001 From: El Mau Date: Tue, 21 Dec 2021 23:01:44 -0600 Subject: [PATCH 02/21] Nuevos controles para carta porte --- CHANGELOG.md | 19 ++++++ VERSION | 2 +- source/app/controllers/cfdi_xml.py | 6 ++ source/app/models/main.py | 2 +- source/app/settings.py | 2 +- source/static/js/controller/invoices.js | 7 +++ source/static/js/ui/invoices.js | 77 +++++++++++++++++++++++++ 7 files changed, 112 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 77c45ee..341e3ee 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,22 @@ +v 1.44.0 [00-Dic-2021] +---------------------- + - Soporte para Carta Porte v2 + +* IMPORTANTE: + +Es necesario hacer una migración: + +``` +git pull origin master + +cd source/app/models + +python main.py -bk + +python main.py -m -r RFC +``` + + v 1.43.0 [12-Dic-2021] ---------------------- - Soporte para entradas de almacen. diff --git a/VERSION b/VERSION index ed81e4f..a57c16f 100644 --- a/VERSION +++ b/VERSION @@ -1,2 +1,2 @@ -1.43.0 +1.44.0 diff --git a/source/app/controllers/cfdi_xml.py b/source/app/controllers/cfdi_xml.py index 5327d9d..57039ab 100644 --- a/source/app/controllers/cfdi_xml.py +++ b/source/app/controllers/cfdi_xml.py @@ -96,6 +96,12 @@ SAT = { 'xmlns': 'http://www.sat.gob.mx/leyendasFiscales', 'schema': ' http://www.sat.gob.mx/leyendasFiscales http://www.sat.gob.mx/sitio_internet/cfd/leyendasFiscales/leyendasFisc.xsd', }, + 'cartaporte': { + 'version': '2.0', + 'prefix': 'cartaporte20', + 'xmlns': 'http://www.sat.gob.mx/CartaPorte20', + 'schema': ' http://www.sat.gob.mx/CartaPorte20 http://www.sat.gob.mx/sitio_internet/cfd/CartaPorte/CartaPorte20.xsd', + } } diff --git a/source/app/models/main.py b/source/app/models/main.py index 50f4ff6..efa15b4 100644 --- a/source/app/models/main.py +++ b/source/app/models/main.py @@ -341,6 +341,7 @@ def config_timbrar(): 'cfdi_anticipo': Configuracion.get_('chk_config_anticipo'), 'cfdi_ine': Configuracion.get_bool('chk_config_ine'), 'cfdi_edu': Configuracion.get_bool('chk_config_edu'), + 'cfdi_carta_porte': Configuracion.get_bool('chk_config_carta_porte'), 'cfdi_divisas': Configuracion.get_bool('chk_config_divisas'), 'cfdi_metodo_pago': Configuracion.get_bool('chk_config_ocultar_metodo_pago'), 'cfdi_condicion_pago': Configuracion.get_bool('chk_config_ocultar_condiciones_pago'), @@ -5378,7 +5379,6 @@ class Facturas(BaseModel): comprobante['Fecha'] = invoice.fecha.isoformat()[:19] comprobante['FormaPago'] = invoice.forma_pago comprobante['NoCertificado'] = certificado.serie - # ~ comprobante['Certificado'] = certificado.cer_txt comprobante['SubTotal'] = FORMAT.format(invoice.subtotal) comprobante['Moneda'] = invoice.moneda comprobante['TipoCambio'] = '1' diff --git a/source/app/settings.py b/source/app/settings.py index be6edf0..d4a7804 100644 --- a/source/app/settings.py +++ b/source/app/settings.py @@ -42,7 +42,7 @@ except ImportError: DEBUG = DEBUG -VERSION = '1.43.0' +VERSION = '1.44.0' EMAIL_SUPPORT = ('soporte@empresalibre.mx',) TITLE_APP = '{} v{}'.format(TITLE_APP, VERSION) diff --git a/source/static/js/controller/invoices.js b/source/static/js/controller/invoices.js index 8f7be01..3f2dfd9 100644 --- a/source/static/js/controller/invoices.js +++ b/source/static/js/controller/invoices.js @@ -212,8 +212,15 @@ function default_config(){ get_leyendas_fiscales() $$('tv_invoice').getTabbar().showOption('Leyendas Fiscales') } + if(!values.cfdi_carta_porte){ + $$('tv_invoice').getTabbar().hideOption('Carta Porte') + }else{ + get_leyendas_fiscales() + $$('tv_invoice').getTabbar().showOption('Carta Porte') + } cfg_invoice['leyendasfiscales'] = values.cfdi_leyendasfiscales cfg_invoice['edu'] = values.cfdi_edu + cfg_invoice['carta_porte'] = values.cfdi_carta_porte cfg_invoice['open_pdf'] = values.cfdi_open_pdf cfg_invoice['tax_locales'] = values.cfdi_tax_locales cfg_invoice['tax_decimals'] = values.cfdi_tax_decimals diff --git a/source/static/js/ui/invoices.js b/source/static/js/ui/invoices.js index 983e0c1..ec4fc99 100644 --- a/source/static/js/ui/invoices.js +++ b/source/static/js/ui/invoices.js @@ -820,6 +820,7 @@ var grid_leyendas_fiscales = { columns: grid_cols_leyendas_fiscales, } + var controls_leyendas_fiscales = [ {maxHeight: 15}, {cols: [{maxWidth: 15}, @@ -830,6 +831,81 @@ var controls_leyendas_fiscales = [ ] +var opt_transporte_internacional = [ + {id: 'Sí', value: 'Sí'}, + {id: 'No', value: 'No'}, +] + + +var opt_entrada_salida = [ + {id: '', value: ''}, + {id: 'Entrada', value: 'Entrada'}, + {id: 'Salida', value: 'Salida'}, +] + + +var grid_cols_carta_ubicaciones = [ + {id: 'id', header: 'ID', hidden: true}, + {id: 'TipoUbicacion', header: 'Tipo de Ubicación', fillspace: 1}, + {id: 'IDUbicacion', header: 'ID Ubicación', fillspace: 1}, + {id: 'RFCRemitenteDestinatario', header: 'RFC Rem/Des', fillspace: 1}, + {id: 'NombreRemitenteDestinatario', header: 'Nombre Rem/Des', fillspace: 1}, +] + + +var grid_carta_ubicaciones = { + view: 'datatable', + id: 'grid_carta_ubicaciones', + select: 'row', + multiselect: false, + adjust: true, + autoheight: true, + headermenu: true, + columns: grid_cols_carta_ubicaciones, +} + + +var body_carta_ubicaciones = {rows:[ + grid_carta_ubicaciones, +]} + + +var controls_carta_porte = [ + {cols: [{maxWidth: 15}, + {view: 'checkbox', id: 'chk_cfdi_usar_cartaporte', labelWidth: 0, + labelRight: 'Usar el complemento Carta Porte'}, + {}]}, + {cols: [{maxWidth: 15}, + {view: 'richselect', id: 'lst_carta_TranspInternac', labelPosition: 'top', + label: 'Transporte Internacional', options: opt_transporte_internacional, + value: 'No'}, + {view: 'richselect', id: 'lst_carta_EntradaSalidaMerc', labelPosition: 'top', + label: 'Entrada o Salida', options: opt_entrada_salida, + value: ''}, + {view: 'richselect', id: 'lst_carta_PaisOrigenDestino', labelPosition: 'top', + label: 'País Origen/Destino', options: [], + value: ''}, + {view: 'richselect', id: 'lst_carta_ViaEntradaSalida', labelPosition: 'top', + label: 'Vía de Entrada o Salida', options: [], + value: ''}, + ]}, + {view: 'fieldset', label: 'Ubicaciones', body: body_carta_ubicaciones}, +] + + +var form_carta_porte = { + type: 'space', + responsive: true, + cols: [{ + view: 'form', + id: 'form_carta_porte', + complexData: true, + scroll: true, + elements: controls_carta_porte, + }] +} + + var controls_invoices = [ { view: 'tabview', @@ -840,6 +916,7 @@ var controls_invoices = [ {id: 'PreFacturas', rows: controls_prefactura}, {id: 'INE', rows: controls_ine}, {id: 'Leyendas Fiscales', rows: controls_leyendas_fiscales}, + {id: 'Carta Porte', rows: [form_carta_porte]}, ] }, ] From fe4829c6b8b44d30fd6148ed271f34e2857e3e7c Mon Sep 17 00:00:00 2001 From: El Mau Date: Sat, 25 Dec 2021 17:48:08 -0600 Subject: [PATCH 03/21] Agregar controles para Carta Porte --- source/static/js/ui/invoices.js | 58 ++++++++++++++++++++++++++++++--- 1 file changed, 53 insertions(+), 5 deletions(-) diff --git a/source/static/js/ui/invoices.js b/source/static/js/ui/invoices.js index ec4fc99..e479d9c 100644 --- a/source/static/js/ui/invoices.js +++ b/source/static/js/ui/invoices.js @@ -844,27 +844,74 @@ var opt_entrada_salida = [ ] +var opt_origen_destino = [ + {id: 'Origen', value: 'Origen'}, + {id: 'Destino', value: 'Destino'}, +] + + +webix.editors.$popup = { + date: { + view: "popup", + body: {view: "calendar", timepicker:true, icons:true} + } +}; + + var grid_cols_carta_ubicaciones = [ {id: 'id', header: 'ID', hidden: true}, - {id: 'TipoUbicacion', header: 'Tipo de Ubicación', fillspace: 1}, - {id: 'IDUbicacion', header: 'ID Ubicación', fillspace: 1}, - {id: 'RFCRemitenteDestinatario', header: 'RFC Rem/Des', fillspace: 1}, - {id: 'NombreRemitenteDestinatario', header: 'Nombre Rem/Des', fillspace: 1}, + {id: 'delete', header: '', width: 30, css: 'delete'}, + {id: 'TipoUbicacion', header: 'Tipo de Ubicación', editor: 'select', options: opt_origen_destino, fillspace: 1}, + {id: 'RFCRemitenteDestinatario', header: 'RFC Rem/Des', editor: 'text', fillspace: 1}, + {id: 'FechaHoraSalidaLlegada', header: 'Fecha/Hora', editor: 'date', format: webix.Date.dateToStr("%D, %d-%M-%Y %h:%i"), footer: 'Total distancia:', fillspace: 1}, + {id: 'DistanciaRecorrida', header: 'Distancia (KM)', editor: 'text', css: 'right', footer: {content: 'summColumn', css: 'right'}, fillspace: 1}, +] + + +var grid_cols_carta_mercancias = [ + {id: 'id', header: 'ID', hidden: true}, + {id: 'BienesTransp', header: 'Clave SAT', fillspace: 1}, + {id: 'Descripcion', header: 'Descripción', fillspace: 1}, + {id: 'Cantidad', header: 'Cantidad', css: 'right', footer: {content: 'summColumn', css: 'right'}, fillspace: 1}, + {id: 'ClaveUnidad', header: 'Unidad', fillspace: 1}, + {id: 'PesoEnKg', header: 'Peso (Kg)', css: 'right', footer: {content: 'summColumn', css: 'right'}, fillspace: 1}, ] var grid_carta_ubicaciones = { view: 'datatable', id: 'grid_carta_ubicaciones', - select: 'row', multiselect: false, adjust: true, autoheight: true, headermenu: true, + editable: true, + footer: true, columns: grid_cols_carta_ubicaciones, + data: [ + {delete: '-', TipoUbicacion: 'Origen'}, + {delete: '-', TipoUbicacion: 'Destino'}, + ] } +var grid_carta_mercancias = { + view: 'datatable', + id: 'grid_carta_mercancias', + multiselect: false, + adjust: true, + autoheight: true, + headermenu: true, + editable: true, + footer: true, + columns: grid_cols_carta_mercancias, +} + + +var body_carta_mercancias = {rows:[ + grid_carta_mercancias, +]} + var body_carta_ubicaciones = {rows:[ grid_carta_ubicaciones, ]} @@ -875,6 +922,7 @@ var controls_carta_porte = [ {view: 'checkbox', id: 'chk_cfdi_usar_cartaporte', labelWidth: 0, labelRight: 'Usar el complemento Carta Porte'}, {}]}, + {view: 'fieldset', label: 'Mercancias', body: body_carta_mercancias}, {cols: [{maxWidth: 15}, {view: 'richselect', id: 'lst_carta_TranspInternac', labelPosition: 'top', label: 'Transporte Internacional', options: opt_transporte_internacional, From 7fe5251153d4f3e7c6e88ef18ef29b358717637f Mon Sep 17 00:00:00 2001 From: El Mau Date: Sat, 25 Dec 2021 23:00:23 -0600 Subject: [PATCH 04/21] Agregar controles para Carta Porte --- source/static/js/ui/invoices.js | 74 ++++++++++++++++++++++++++++++++- 1 file changed, 73 insertions(+), 1 deletion(-) diff --git a/source/static/js/ui/invoices.js b/source/static/js/ui/invoices.js index e479d9c..277cb2c 100644 --- a/source/static/js/ui/invoices.js +++ b/source/static/js/ui/invoices.js @@ -872,12 +872,41 @@ var grid_cols_carta_mercancias = [ {id: 'id', header: 'ID', hidden: true}, {id: 'BienesTransp', header: 'Clave SAT', fillspace: 1}, {id: 'Descripcion', header: 'Descripción', fillspace: 1}, - {id: 'Cantidad', header: 'Cantidad', css: 'right', footer: {content: 'summColumn', css: 'right'}, fillspace: 1}, + {id: 'Cantidad', header: 'Cantidad', css: 'right', fillspace: 1}, {id: 'ClaveUnidad', header: 'Unidad', fillspace: 1}, + {id: 'ValorMercancia', header: 'Valor Mercancia', fillspace: 1}, {id: 'PesoEnKg', header: 'Peso (Kg)', css: 'right', footer: {content: 'summColumn', css: 'right'}, fillspace: 1}, ] +var grid_cols_carta_autotransporte = [ + {id: 'id', header: 'ID', hidden: true}, + {id: 'PermSCT', header: 'Tipo Permiso SCT', fillspace: 1}, + {id: 'NumPermisoSCT', header: 'Número Permiso SCT', fillspace: 1}, + {id: 'ConfigVehicular', header: 'Clave Autotransporte', fillspace: 1}, + {id: 'PlacaVM', header: 'Placa', fillspace: 1}, + {id: 'AnioModeloVM', header: 'Modelo (Año)', fillspace: 1}, +] + + +var opt_tipos_figura = [ + {id: '', value: ''}, + {id: '01', value: '[01] Operador'}, + {id: '02', value: '[02] Propietario'}, + {id: '03', value: '[03] Arrendador'}, + {id: '04', value: '[04] Notificado'}, +] + + +var grid_cols_carta_tipos_figuras = [ + {id: 'id', header: 'ID', hidden: true}, + {id: 'TipoFigura', header: 'Tipo Figura', editor: 'select', options: opt_tipos_figura, fillspace: 1}, + {id: 'RFCFigura', header: 'RFC Figura', editor: 'text', fillspace: 1}, + {id: 'NumLicencia', header: 'Número de Licencia', editor: 'text', fillspace: 1}, + +] + + var grid_carta_ubicaciones = { view: 'datatable', id: 'grid_carta_ubicaciones', @@ -908,15 +937,56 @@ var grid_carta_mercancias = { } +var grid_carta_autotransporte = { + view: 'datatable', + id: 'grid_carta_autotransporte', + multiselect: false, + adjust: true, + autoheight: true, + headermenu: true, + editable: true, + columns: grid_cols_carta_autotransporte, + data: [{id: 0}], +} + + +var grid_carta_tipos_figuras = { + view: 'datatable', + id: 'grid_carta_tipos_figuras', + multiselect: false, + adjust: true, + autoheight: true, + headermenu: true, + editable: true, + columns: grid_cols_carta_tipos_figuras, + data: [{id: 0}], +} + + var body_carta_mercancias = {rows:[ + {cols: [{view: 'richselect', id: 'lst_carta_UnidadPeso', labelPosition: 'top', + label: 'Unidad de Peso', maxWidth: 300, options: []}, {} + ]}, + {maxHeight: 10}, grid_carta_mercancias, ]} + var body_carta_ubicaciones = {rows:[ grid_carta_ubicaciones, ]} +var body_carta_autotransporte = {rows:[ + grid_carta_autotransporte, +]} + + +var body_carta_tipos_figuras = {rows:[ + grid_carta_tipos_figuras, +]} + + var controls_carta_porte = [ {cols: [{maxWidth: 15}, {view: 'checkbox', id: 'chk_cfdi_usar_cartaporte', labelWidth: 0, @@ -938,6 +1008,8 @@ var controls_carta_porte = [ value: ''}, ]}, {view: 'fieldset', label: 'Ubicaciones', body: body_carta_ubicaciones}, + {view: 'fieldset', label: 'Autotransporte', body: body_carta_autotransporte}, + {view: 'fieldset', label: 'Tipos Figura', body: body_carta_tipos_figuras}, ] From 54ab86a0898452cae8956b501fc4131aa9b87e20 Mon Sep 17 00:00:00 2001 From: El Mau Date: Mon, 27 Dec 2021 21:33:34 -0600 Subject: [PATCH 05/21] Ocultar columna temporalmente --- source/static/js/ui/invoices.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/static/js/ui/invoices.js b/source/static/js/ui/invoices.js index 277cb2c..cfa24bd 100644 --- a/source/static/js/ui/invoices.js +++ b/source/static/js/ui/invoices.js @@ -860,7 +860,7 @@ webix.editors.$popup = { var grid_cols_carta_ubicaciones = [ {id: 'id', header: 'ID', hidden: true}, - {id: 'delete', header: '', width: 30, css: 'delete'}, + {id: 'delete', header: '', hidden: true, width: 30, css: 'delete'}, {id: 'TipoUbicacion', header: 'Tipo de Ubicación', editor: 'select', options: opt_origen_destino, fillspace: 1}, {id: 'RFCRemitenteDestinatario', header: 'RFC Rem/Des', editor: 'text', fillspace: 1}, {id: 'FechaHoraSalidaLlegada', header: 'Fecha/Hora', editor: 'date', format: webix.Date.dateToStr("%D, %d-%M-%Y %h:%i"), footer: 'Total distancia:', fillspace: 1}, From 76663fdd679643af1e704556c1db3d858f594bd4 Mon Sep 17 00:00:00 2001 From: El Mau Date: Tue, 28 Dec 2021 22:26:05 -0600 Subject: [PATCH 06/21] =?UTF-8?q?Agregar=20evento=20al=20cambiar=20de=20pe?= =?UTF-8?q?sta=C3=B1a?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- source/static/js/controller/invoices.js | 8 ++++++++ source/static/js/ui/invoices.js | 18 +++++++++++++----- 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/source/static/js/controller/invoices.js b/source/static/js/controller/invoices.js index 3f2dfd9..6cb3e06 100644 --- a/source/static/js/controller/invoices.js +++ b/source/static/js/controller/invoices.js @@ -86,6 +86,9 @@ var invoices_controllers = { $$('search_by').attachEvent('onKeyPress', search_by_key_press) $$('search_by').attachEvent('onItemClick', search_by_click) + tv_invoice = $$('tv_invoice').getTabbar() + tv_invoice.attachEvent('onChange', tv_invoice_change) + webix.extend($$('grid_invoices'), webix.ProgressBar) init_config_invoices() @@ -2469,3 +2472,8 @@ function get_leyendas_fiscales(){ } }) } + + +function tv_invoice_change(nv, ov){ + msg_ok(nv) +} diff --git a/source/static/js/ui/invoices.js b/source/static/js/ui/invoices.js index cfa24bd..e042437 100644 --- a/source/static/js/ui/invoices.js +++ b/source/static/js/ui/invoices.js @@ -879,13 +879,21 @@ var grid_cols_carta_mercancias = [ ] +var opt_config_auto = [ + {id: '', value: ''}, + {id: 'VL', value: '[VL] Vehículo ligero de carga (2 llantas en el eje delantero y 2 llantas en el eje trasero)'}, + {id: 'C2', value: '[C2] Camión Unitario (2 llantas en el eje delantero y 4 llantas en el eje trasero)'}, + {id: 'C3', value: '[C3] Camión Unitario (2 llantas en el eje delantero y 6 o 8 llantas en los dos ejes traseros)'}, +] + + var grid_cols_carta_autotransporte = [ {id: 'id', header: 'ID', hidden: true}, - {id: 'PermSCT', header: 'Tipo Permiso SCT', fillspace: 1}, - {id: 'NumPermisoSCT', header: 'Número Permiso SCT', fillspace: 1}, - {id: 'ConfigVehicular', header: 'Clave Autotransporte', fillspace: 1}, - {id: 'PlacaVM', header: 'Placa', fillspace: 1}, - {id: 'AnioModeloVM', header: 'Modelo (Año)', fillspace: 1}, + {id: 'PermSCT', header: 'Tipo Permiso SCT', editor: 'text', fillspace: 1}, + {id: 'NumPermisoSCT', header: 'Número Permiso SCT', editor: 'text', fillspace: 1}, + {id: 'ConfigVehicular', header: 'Clave Autotransporte', editor: 'select', options: opt_config_auto, fillspace: 1}, + {id: 'PlacaVM', header: 'Placa', editor: 'text', fillspace: 1}, + {id: 'AnioModeloVM', header: 'Modelo (Año)', editor: 'text', fillspace: 1}, ] From 2a2689a61bd54f5764ad2f29d01c0c9653e27ea6 Mon Sep 17 00:00:00 2001 From: El Mau Date: Wed, 29 Dec 2021 22:02:17 -0600 Subject: [PATCH 07/21] Sincronizar productos para carta porte --- source/db/sat.db | Bin 2805760 -> 2830336 bytes source/db/unidad_peso.csv | 473 ++++++++++++++++++++++++ source/static/js/controller/invoices.js | 33 +- source/static/js/controller/util.js | 3 + source/static/js/ui/invoices.js | 16 +- 5 files changed, 515 insertions(+), 10 deletions(-) create mode 100644 source/db/unidad_peso.csv diff --git a/source/db/sat.db b/source/db/sat.db index 3ee5899e296567820a8d6eaa9889c98f46adff4f..fd696fb87b891117e85f9d8d7edac5e37044a2e0 100644 GIT binary patch delta 9004 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 PAx0Quf@v6evqkU&GMQV6 diff --git a/source/db/unidad_peso.csv b/source/db/unidad_peso.csv new file mode 100644 index 0000000..904df75 --- /dev/null +++ b/source/db/unidad_peso.csv @@ -0,0 +1,473 @@ +id key name +1 Tu Contenedor externo +2 X1A Tambor de acero +3 X1B Tambor de aluminio +4 X1D Tambor contrachapado +5 X1F Contenedor flexible +6 X1G Tambor de fibra +7 X1w Tambor de madera +8 X2C Barril de madera +9 X3A Bidón de acero +10 X3H Bidón de plástico +11 X43 Bolsa de gran tamaño +12 X44 Bolsa de plástico +13 X4A Caja de acero +14 X4B Caja de aluminio +15 X4C Caja de madera natural +16 X4D Caja de contrachapado +17 X4F Caja de madera reconstituida +18 X4G Caja de cartón +19 X4H Caja de plástico +20 X5H Bolsa de plástico tejido +21 X5L Bolsa textil +22 X5M Bolsa de papel +23 X6H Recipiente de plástico, Contenedor compuesto. +24 X6P Recipiente de vidrio, Contenedor compuesto. +25 X7A Estuche para carro +26 X7B Estuche de madera +27 X8A Pallet de madera +28 X8B Cajón de madera +29 X8C Madera flejada +30 XAA Contenedor intermedio para gráneles de plástico rígido +31 XAB Contenedor de fibra +32 XAC Contenedor de papel +33 XAD Contenedor de madera +34 XAE Aerosol +35 XAF Pallet modular con collares, 80cms * 60cms +36 XAG Pallet o empaquetado +37 XAH Pallet, 100cms X 110cms +38 XAI Contenedor tipo concha +39 XAJ Cono +40 XAL Esfera +41 XAM Ampolleta no protegida +42 XAP Ampolleta protegida +43 XAT Atomizador +44 XAV Cápsula +45 XB4 Cinturón +46 XBA Barril +47 XBB Bobina +48 XBC Cajón para botellas / Estante para botellas +49 XBD Tablero +50 XBE Flejado +51 XBF Globo no protegido +52 XBG Bolso +53 XBH Manojo +54 XBI Compartimiento +55 XBJ Cubeta +56 XBK Cesta +57 XBL Paca comprimida +58 XBM Cuenco +59 XBN Paca no comprimida +60 XBO Botella no-protegida y cilíndrica +61 XBP Globo protegido +62 XBQ Botella cilíndrica protegida +63 XBR Barra +64 XBS Botella, no-protegida en forma de bulbo +65 XBT Rollo de tela +66 XBU Butt +67 XBV Botella de bulbo protegido +68 XBW Caja para líquidos +69 XBX Caja +70 XBY Tablero, con fleje/ agrupados/ armados +71 XBZ Barras, con fleje/ agrupados/ armados +72 XCA Lata rectangular +73 XCB Cajón para cerveza +74 XCC Mantequera +75 XCD Lata con mango y boquilla +76 XCE Cesto tejido +77 XCF Cofre +78 XCG Contenedor tipo Jaula +79 XCH Cajonera +80 XCI Frasco +81 XCJ Ataúd +82 XCK Barrica +83 XCL Espiral +84 XCM Paquete de tarjetas +85 XCN Contenedor, no especificado como equipo de transporte +86 XCO Garrafón no protegido +87 XCP Garrafón protegido +88 XCQ Cartucho +89 XCR Cajón +90 XCS Estuche +91 XCT Cartón +92 XCU Vaso +93 XCV Cubierta +94 XCW Jaula estilo rodillo +95 XCX Lata cilíndrica +96 XCY Cilindro +97 XCZ Lona +98 XDA Cajón multicapa de plástico +99 XDB Cajón de varias capas de madera +100 XDC Cajón multicapa de cartón +101 XDG Jaula, Según la clasificación de la compañía (Commonwealth Handling Equipment Pool (CHEP)) +102 XDH Caja, Según la clasificación de la compañía (CHEP), Eurobox +103 XDI Tambor de hierro +104 XDJ damajuana o garrafa, no protegido +105 XDK Cajón a granel, cartón +106 XDL Cajas de plástico +107 XDM Cajones a granel de madera +108 XDN Dispensador +109 XDP damajuana o garrafa, protegido +110 XDR Tambor +111 XDS Bandeja de una capa sin cubierta y de plástico +112 XDT Bandeja de una capa sin cubierta y de madera +113 XDU Bandeja de una capa sin cubierta y poliestireno +114 XDV Bandeja de una capa sin cubierta y de cartón +115 XDW Bandeja de dos capas sin tapa y con bandeja de plástico +116 XDX Bandeja de dos capas sin cubierta y de madera +117 XDY Bandeja de dos capas sin cubierta y de cartón +118 XEC Bolsa de plástico +119 XED Estuche, con pallet de base +120 XEE Estuche, con pallet base de madera +121 XEF Estuche, con pallet base de cartón +122 XEG Estuche, con pallet base de plástico +123 XEH Estuche, con pallet base de metal +124 XEI Estuche isotérmico +125 XEN Sobre +126 XFB Bolsa flexible +127 XFC Cajón para fruta +128 XFD Cajón enmarcado +129 XFE Tanque flexible +130 XFI Firkin +131 XFL Matraz +132 XFO Cajón para zapatos +133 XFP Caja auxiliar para película fotográfica +134 XFR Marco +135 XFT Contenedor para alimentos +136 XFW Carro de cama plana +137 XFX Bolsa flexible tipo contenedor +138 XGB Botella para gas +139 XGI Viga +140 XGL Contenedor tipo galón +141 XGR Recipiente de vidrio +142 XGU Bandeja contenedor para apilar horizontalmente objetos planos +143 XGY Costal de Yute +144 XGZ Vigas con correas o agrupadas +145 XHA Cesta con mango y de plástico +146 XHB Cesta con mango y de madera +147 XHC Cesta con asa y de cartón +148 XHG Hogshead +149 XHN Gancho +150 XHR Cesto +151 XIA Paquete con pantalla y de madera +152 XIB Paquete con pantalla y de cartón +153 XIC Paquete con pantalla y de plástico +154 XID Paquete con pantalla y de metal +155 XIE Paquete de mostrador. +156 XIF Envase para alimentos +157 XIG Paquete envuelto en papel +158 XIH Tambor de plástico +159 XIK Paquete de cartón con los agujeros para botellas +160 XIL Bandeja rígida con tapa y apilable (CEN TS 14482: 2002) +161 XIN Lingote +162 XIZ Lingotes con correas/ agrupados +163 XJB Bolsa jumbo +164 XJC Bidón rectangular +165 XJG Jarra +166 XJR Tarro +167 XJT Bolsa de yute +168 XJY Bidón, cilíndrico +169 XKG Barrilete +170 XKI Kit (Conjunto de piezas) +171 XLE Valijas +172 XLG Bitácora +173 XLT Lote +174 XLU Caja de arrastre +175 XLV Contenedor pequeño +176 XLZ Registros con fleje/ agrupados/ armados +177 XMA Cajón metálico +178 XMB Múltiplo de bolsas +179 XMC Cajón para leche +180 XME Contenedor de metal +181 XMR Recipiente de metal +182 XMS Saco milti-pared +183 XMT Tapete +184 XMW Contenedor envuelto en plástico +185 XMX Caja pequeña de cerillos +186 XNA No disponible +187 XNE Sin empaque o no empaquetado +188 XNF Sin empaque o no empaquetado, unidad simple +189 XNG Sin empaque o no empaquetado, unidades múltiples +190 XNS Caja nido +191 XNT Red +192 XNU Red de plástico con tubo +193 XNV Red textil con tubo +194 XOA Pallet, Según la clasificación de la compañía (Commonwealth Handling Equipment Pool (CHEP) 40 cm x 60 cm +195 XOB Pallet, Según la clasificación de la compañía (Commonwealth Handling Equipment Pool (CHEP) 80 cm x 120 cm +196 XOC Pallet, Según la clasificación de la compañía (Commonwealth Handling Equipment Pool (CHEP) 100 cm x 120 cm +197 XOD Pallet, AS 4068-1993 +198 XOE Pallet, ISO T11 +199 XOF Plataforma, peso o dimensión no especificada +200 XOK Bloque +201 XOT Octabin +202 XP2 Charola +203 XPA Cajetilla +204 XPB Pallet, Caja combinada y abierta con caja y pallet. +205 XPC Paquete postal +206 XPD Pallet modular con collares (80cms * 100cms) +207 XPE Pallet modular con collares (80cms * 120cms) +208 XPF Corral +209 XPG Placa +210 XPH Cantaro +211 XPI Pleca +212 XPJ Canastilla +213 XPK Paquete +214 XPL Balde +215 XPN Tablón +216 XPO Bolsa pequeña +217 XPR Contenedor de plástico +218 XPT Maceta +219 XPU Cacerola +220 XPV Tubos, con fleje/ agrupados/ armados +221 XPX Pallet +222 XPY Placas con fleje/ agrupados/ armados +223 XPZ Tablones con fleje/ agrupados/ armados +224 XQA Tambor de acero con cabeza no desmontable +225 XQB Tambor de acero con cabeza extraíble +226 XQC Tambor de aluminio con cabeza no extraíble +227 XQD Tambor de aluminio con cabeza extraíble +228 XQF Tambor, plástico con cabeza no desmontable +229 XQG Tambor, plástico, cabeza extraíble +230 XQH Barril de madera con tapón +231 XQJ Barril de madera con cabeza desprendible +232 XQK Bidón de acero con cabeza no desmontable +233 XQL Bidón de acero con cabeza desmontable +234 XQM Bidón de plástico con cabeza no desmontable +235 XQN Bidón de plástico con cabeza extraíble +236 XQP Caja de madera natural estándar +237 XQQ Caja de madera natural con muros a prueba de filtraciones +238 XQR Caja de plástico expandida +239 XQS Caja de plástico sólida +240 XRD Rod +241 XRG Anillo +242 XRJ Estante, Perchero para ropa +243 XRK Estante +244 XRL Carrete +245 XRO Rollo +246 XRT Red Roja +247 XRZ Varillas con fleje/ agrupados/ armados +248 XSA Saco +249 XSB Losa +250 XSC Cajón poco profundo +251 XSD Huso +252 XSE Baúl +253 XSH Bolsa pequeña hermética +254 XSI Patín +255 XSK Carcasa esqueleto +256 XSL Hoja de deslizamiento +257 XSM Hoja de metal +258 XSO Carrete pequeño +259 XSP Hoja de empaque de plástico +260 XSS Cajón de acero +261 XSU Maleta +262 XSV Sobre de acero +263 XSW Envoltorio +264 XSY Manga +265 XSZ Hojas con fleje/ agrupados/ armados +266 XT1 Tableta +267 XTB Tina +268 XTC Caja para té +269 XTD Tubo plegable +270 XTG Contenedor de tanque genérico +271 XTI Tierce +272 XTK Tanque rectangular +273 XTL Tina con tapa +274 XTN Hojalata +275 XTO Tonel +276 XTR Maletero +277 XTS Estructura +278 XTT Bolsa de mano +279 XTU Tubo +280 XTV Tubo con boquilla +281 XTW Pallet tricapa +282 XTY Tanque cilíndrico +283 XTZ Tubos con fleje/ agrupados/ armados +284 XUC Sin empaque +285 XUN Unidad +286 XVA Tanque +287 XVG Tanque de gas (a 1,031 mbar y 15° C) +288 XVI Frasco pequeño +289 XVK Paquete transportable +290 XVL Contenedor para líquidos a granel +291 XVN Vehículo +292 XVO "Contenedor para sólido de partículas grandes a granel (""nódulos"")" +293 XVP Envasado al vacío +294 XVQ Tanque para Gas licuado (a temperatura / presión anormal) +295 XVR Contenedor para sólidos de partículas granulares a granel (Granos) +296 XVS Contenedor de chatarra a granel +297 XVY "Contenedor para sólido de partículas finas a granel (""polvos"")" +298 XWA Contenedor de granel intermedio +299 XWB Botella de mimbre +300 XWC Contenedor intermedio para gráneles y de acero +301 XWD Contenedor intermedio para gráneles y de aluminio +302 XWF Contenedor intermedio para gráneles y de metal +303 XWG Contenedor intermedio para gráneles y de acero presurizado menor a 10 kpa +304 XWH Contenedor intermedio para gráneles y de aluminio, presurizado menor a 10 kpa +305 XWJ Contenedor intermedio para gráneles y de metal con una presión de 10 kpa +306 XWK Contenedor intermedio para gráneles y de acero para líquido +307 XWL Contenedor intermedio para gráneles y de aluminio para líquido +308 XWM Contenedor intermedio para gráneles y de metal para líquido +309 XWN Contenedor intermedio para gráneles con tejido plástico sin capa con revestimiento +310 XWP Contenedor intermedio para gráneles con tejido plástico y recubierto +311 XWQ Contenedor intermedio para gráneles con tejido plástico con revestimiento +312 XWR Contenedor intermedio para gráneles con tejido plástico, revestido y con forro +313 XWS Contenedor intermedio para gráneles con película de plástico +314 XWT Contenedor intermedio para gráneles textil sin capa / forro +315 XWU Contenedor intermedio para gráneles de madera natural con forro interior +316 XWV Contenedor intermedio para gráneles textil recubierto +317 XWW Contenedor intermedio para gráneles textil con revestimiento +318 XWX Contenedor intermedio para gráneles textil recubierto y con forro +319 XWY Contenedor intermedio para gráneles contrachapado con revestimiento interior +320 XWZ Contenedor intermedio para gráneles de madera reconstituida con revestimiento interior +321 XXA Bolsa de tejido plástico, sin abrigo interior ni forro +322 XXB Bolsa de tejido plástico a prueba de filtraciones +323 XXC Bolsa de tejido plástico resistente al agua +324 XXD Bolsa con película de plástico +325 XXF Bolsa textil sin capa ni forro interior +326 XXG Bolsa textil a prueba de filtraciones +327 XXH Bolsa textil resistente al agua +328 XXJ Bolsa de papel multi-pared +329 XXK Bolsa de papel multi-pared, resistente al agua +330 XYA Empaque compuesto, recipiente de plástico en tambor de acero +331 XYB Empaque compuesto, recipiente de plástico en cajas de acero +332 XYC Empaque compuesto, recipiente de plástico en tambor de aluminio +333 XYD Empaque compuesto, recipiente de plástico en cajón de aluminio +334 XYF Empaque compuesto, recipiente de plástico en caja de madera +335 XYG Empaque compuesto, recipiente de plástico en tambor de madera contrachapada +336 XYH Empaque compuesto, recipiente de plástico en caja de madera contrachapada +337 XYJ Empaque compuesto, recipiente de plástico en tambor de fibra +338 XYK Empaque compuesto, recipiente de plástico en caja de cartón +339 XYL Empaque compuesto, recipiente de plástico en el tambor de plástico +340 XYM Empaque compuesto, recipiente de plástico en caja de plástico sólido +341 XYN Empaque compuesto, receptáculo de vidrio en tambor de acero +342 XYP Empaque compuesto, receptáculo de vidrio en caja de cajas de acero +343 XYQ Empaque compuesto, recipiente de vidrio en tambor de aluminio +344 XYR Empaque compuesto, receptáculo de vidrio en caja de aluminio +345 XYS Empaque compuesto, recipiente de vidrio en caja de madera +346 XYT Empaque compuesto, recipiente de vidrio en tambor de madera contrachapada +347 Xyv Empaque compuesto, recipiente de vidrio en el cesto de mimbre +348 XYW Empaque compuesto, recipiente de vidrio en tambor de fibra +349 XYX Empaque compuesto, recipiente de vidrio en caja de cartón +350 XYY Empaque compuesto, recipiente de vidrio en paquete de plástico expandible +351 XYZ Empaque compuesto, recipiente de vidrio en paquete de plástico sólido +352 XZA Contenedor de granel intermedio, papel, multi-pared +353 XZB Bolsa grande +354 XZC Contenedor intermedio para gráneles de papel, multi-pared y resistente al agua +355 XZD Contenedor intermedio para gráneles de plástico rígido, con equipo estructural para sólidos +356 XZF Contenedor intermedio para gráneles de plástico rígido, autoportante para sólidos +357 XZG Contenedor intermedio para gráneles de plástico rígido, con equipo estructural, presurizado +358 XZH Contenedor intermedio para gráneles de plástico rígido, autoportante y presurizado +359 XZJ Contenedor intermedio para gráneles de plástico rígido, con equipo estructural para líquidos +360 XZK Contenedor intermedio para gráneles de plástico rígido, autoportante, líquidos +361 XZL Contenedor intermedio para gráneles, compuesto y de plástico rígido, sólidos +362 XZM Contenedor intermedio para gráneles, compuesto y de plástico flexible, sólidos +363 XZN Contenedor intermedio para gráneles, compuesto y de plástico rígido, presurizado +364 XZP Contenedor intermedio para gráneles, compuesto y de plástico flexible, presurizado +365 XZQ Contenedor intermedio para gráneles, compuesto y de plástico rígido, líquidos +366 XZR Contenedor intermedio para gráneles, compuesto y de plástico flexible para líquidos +367 XZS Contenedor intermedio para gráneles, compuesto +368 XZT Contenedor intermedio para gráneles con tablero de fibras +369 XZU Contenedor intermedio para gráneles flexible +370 XZV Contenedor intermedio para gráneles de metal, distinto del acero +371 XZW Contenedor intermedio para gráneles, de madera natural +372 XZX Contenedor intermedio para gráneles, de contrachapado +373 XZY Contenedor intermedio para gráneles, de madera reconstituida +374 KGM Kilogramo +375 MC Microgramo +376 DJ Decagramo +377 DG Decigramo +378 GRM Gramo +379 CGM Centigramo +380 TNE Tonelada (tonelada métrica) +381 DTN Decitonelada métrica +382 MGM Miligramo +383 HGM Hectogramo +384 KTN Kilotonelada Métrica +385 2U Megagramo +386 LBR Libra +387 GRN Grano +388 ONZ Onza (avoirdupois) +389 CWI Hundredweight +390 CWA Hundred pound +391 LTN Tonelada (UK) o tonelada larga (estados unidos) +392 STI Estone (UK) +393 STN Tonelada (estados unidos) o tonelada corta (UK y estados unidos) +394 APZ Onza troy u onza farmacéutica +395 F13 Slug +396 K64 Libra (avoirdupois) por grado fahrenheit +397 L69 Tonelada por kelvin +398 L87 Tonelada corta por grado fahrenheit +399 M85 Tonelada, ensayo +400 M86 Libra Alemana +401 J33 Microgramo por kilogramo +402 L32 Nanogramo por kilogramo +403 NA Miligramo por kilogramo +404 M29 Kilogramo por kilogramo +405 M91 Libra por libra +406 Q29 Microgramo por hectogramo +407 MTQ Metro cúbico +408 MAL Megalitro +409 LTR Litro +410 MMQ Milímetro cúbico +411 CMQ Centímetro cúbico +412 DMQ Decímetro cúbico +413 MLT Mililitro +414 HLT Hectolitro +415 CLT Centilitro +416 DMA Decámetro cúbico +417 H19 Hectómetro cúbico +418 H20 Kilómetro cúbico +419 M71 Metro cúbico por pascal (joules) +420 DLT Decilitro +421 4G Microlitro +422 K6 Kilolitro +423 A44 Decalitro +424 G94 Centímetro cúbico por bar +425 G95 Litro por bar +426 G96 Metro cúbico por bar +427 G97 Mililitro por bar +428 5I Pies cúbicos estándar +429 INQ Pulgada cúbica +430 FTQ Pie cúbico +431 YDQ Yarda cúbica +432 GLI Galón (UK) +433 GLL Galón (EUA) +434 PT Pinta (US) +435 PTI Pint (uk) +436 QTI Cuarto (UK) +437 PTL Pinta líquida (estados unidos) +438 QTL Cuarto de líquido (estadis unidos) +439 PTD Pinta seca (estados unidos) +440 OZI Onza líquida (UK) +441 QT Cuarto (EUA) +442 J57 Barril (uk petróleo) +443 K21 Pie cúbico por grado fahrenheit +444 K23 Pie cúbico por psi (libra por pulgada cuadrada) +445 L43 Peck (UK) +446 L61 Pinta (US seco) +447 L62 Cuarto de galón (seco de los EUA) +448 L84 Tonelada (flota UK) +449 L86 Tonelada (flota estados unidos) +450 M11 Yarda cúbica por grado fahrenheit +451 M14 Yarda cúbica por psi (libra por pulgada cuadrada) +452 OZA Onza líquida (estados unidos) +453 BUI Bushel (UK) +454 BUA Bushel (EUA) +455 BLL Barril (EUA) +456 BLD Barril seco (EUA) +457 GLD Galón seco (EUA) +458 QTD Cuarto seco (estados unidos) +459 G26 Estere +460 G21 Taza (unidad de volumen) +461 G24 Cucharada (estados unidos) +462 G25 Cucharilla (estados unidos) +463 G23 Peck +464 M67 Acre-pie +465 M68 Cordón +466 M69 Milla cúbica (reino unido) +467 M70 Unidad tradicional de capacidad de carga +468 Q32 Femtolitro +469 Q33 Picolitro +470 Q34 Nanolitro +471 NM3 Metro cúbico normalizado +472 SM3 Metro cúbico estándar diff --git a/source/static/js/controller/invoices.js b/source/static/js/controller/invoices.js index 6cb3e06..4050a17 100644 --- a/source/static/js/controller/invoices.js +++ b/source/static/js/controller/invoices.js @@ -2474,6 +2474,35 @@ function get_leyendas_fiscales(){ } -function tv_invoice_change(nv, ov){ - msg_ok(nv) +function _tab_carta_porte(){ + var g1 = $$('grid_details') + var g2 = $$('grid_carta_mercancias') + + if(!g1.count()){ + msg = 'Agrega primero "todos" los productos a trasladar' + msg_error(msg) + activate_tab('tv_invoice', 'Generar') + return + } + + g2.clearAll() + + g1.eachRow(function(row){ + const r = g1.getItem(row) + var data = new Object() + data['BienesTransp'] = r.clave_sat + data['Descripcion'] = r.descripcion + data['Cantidad'] = r.cantidad + data['ClaveUnidad'] = r.unidad + data['ValorMercancia'] = r.importe + g2.add(data) + }) + +} + + +function tv_invoice_change(nv, ov){ + if(nv=='Carta Porte'){ + _tab_carta_porte() + } } diff --git a/source/static/js/controller/util.js b/source/static/js/controller/util.js index 8afa55f..c0d7f66 100644 --- a/source/static/js/controller/util.js +++ b/source/static/js/controller/util.js @@ -624,3 +624,6 @@ function grid_parse(grid_name, values){ } +function activate_tab(parent, name){ + $$(parent).getTabbar().setValue(name) +} diff --git a/source/static/js/ui/invoices.js b/source/static/js/ui/invoices.js index e042437..e3936e2 100644 --- a/source/static/js/ui/invoices.js +++ b/source/static/js/ui/invoices.js @@ -872,10 +872,10 @@ var grid_cols_carta_mercancias = [ {id: 'id', header: 'ID', hidden: true}, {id: 'BienesTransp', header: 'Clave SAT', fillspace: 1}, {id: 'Descripcion', header: 'Descripción', fillspace: 1}, - {id: 'Cantidad', header: 'Cantidad', css: 'right', fillspace: 1}, - {id: 'ClaveUnidad', header: 'Unidad', fillspace: 1}, - {id: 'ValorMercancia', header: 'Valor Mercancia', fillspace: 1}, - {id: 'PesoEnKg', header: 'Peso (Kg)', css: 'right', footer: {content: 'summColumn', css: 'right'}, fillspace: 1}, + {id: 'Cantidad', header: 'Cantidad', format: webix.i18n.numberFormat, css: 'right', fillspace: 1}, + {id: 'ClaveUnidad', header: 'Unidad', options: 'values/unidades', fillspace: 1}, + {id: 'ValorMercancia', header: 'Valor Mercancia', format: webix.i18n.priceFormat, css: 'right', footer: 'Total peso:', fillspace: 1}, + {id: 'PesoEnKg', header: 'Peso (Kg)', format: webix.i18n.numberFormat, css: 'right', editor: 'text', footer: {content: 'summColumn', css: 'right'}, fillspace: 1}, ] @@ -1004,16 +1004,16 @@ var controls_carta_porte = [ {cols: [{maxWidth: 15}, {view: 'richselect', id: 'lst_carta_TranspInternac', labelPosition: 'top', label: 'Transporte Internacional', options: opt_transporte_internacional, - value: 'No'}, + value: 'No', readonly: true}, {view: 'richselect', id: 'lst_carta_EntradaSalidaMerc', labelPosition: 'top', label: 'Entrada o Salida', options: opt_entrada_salida, - value: ''}, + value: '', disabled: true}, {view: 'richselect', id: 'lst_carta_PaisOrigenDestino', labelPosition: 'top', label: 'País Origen/Destino', options: [], - value: ''}, + value: '', disabled: true}, {view: 'richselect', id: 'lst_carta_ViaEntradaSalida', labelPosition: 'top', label: 'Vía de Entrada o Salida', options: [], - value: ''}, + value: '', disabled: true}, ]}, {view: 'fieldset', label: 'Ubicaciones', body: body_carta_ubicaciones}, {view: 'fieldset', label: 'Autotransporte', body: body_carta_autotransporte}, From 46d6329754e344750d669ecefdda7cbe7e43d37d Mon Sep 17 00:00:00 2001 From: El Mau Date: Thu, 30 Dec 2021 11:56:22 -0600 Subject: [PATCH 08/21] Resuelto: ticket #29 --- source/app/controllers/util.py | 31 ++++++++++++++++--------------- source/app/controllers/utils.py | 9 ++++++--- source/app/models/main.py | 3 +-- 3 files changed, 23 insertions(+), 20 deletions(-) diff --git a/source/app/controllers/util.py b/source/app/controllers/util.py index e75f510..077c7f9 100644 --- a/source/app/controllers/util.py +++ b/source/app/controllers/util.py @@ -72,6 +72,7 @@ from settings import USAR_TOKEN, API, DECIMALES_TAX # ~ from .configpac import AUTH +from .utils import get_qr # ~ v2 import segno from .pacs.cfdi_cert import SATCertificate @@ -1606,22 +1607,22 @@ def to_letters(value, currency): return NumLet(value, currency).letras -def get_qr(data, p=True): - qr = pyqrcode.create(data, mode='binary') - if p: - path = get_path_temp('.qr') - qr.png(path, scale=7) - return path +# ~ def get_qr(data, p=True): + # ~ qr = pyqrcode.create(data, mode='binary') + # ~ if p: + # ~ path = get_path_temp('.qr') + # ~ qr.png(path, scale=7) + # ~ return path - buffer = io.BytesIO() - qr.png(buffer, scale=8) - return base64.b64encode(buffer.getvalue()).decode() + # ~ buffer = io.BytesIO() + # ~ qr.png(buffer, scale=8) + # ~ return base64.b64encode(buffer.getvalue()).decode() -def get_qr2(data, kind='svg'): - buffer = io.BytesIO() - segno.make(data).save(buffer, kind=kind, scale=8, border=2) - return buffer +# ~ def get_qr2(data, kind='svg'): + # ~ buffer = io.BytesIO() + # ~ segno.make(data).save(buffer, kind=kind, scale=8, border=2) + # ~ return buffer def _get_relacionados(doc, version): @@ -1878,9 +1879,9 @@ def _timbre(doc, version, values, pdf_from='1'): qr_data = '{url}{uuid}{emisor}{receptor}{total}{sello}'.format(**qr_data) if pdf_from == '1': - data['cbb'] = get_qr2(qr_data) + data['cbb'] = get_qr(qr_data, 'png') else: - data['cbb'] = get_qr2(qr_data, 'png') + data['cbb'] = get_qr(qr_data) data['cadenaoriginal'] = CADENA.format(**data) return data diff --git a/source/app/controllers/utils.py b/source/app/controllers/utils.py index c96ec60..3e142f1 100644 --- a/source/app/controllers/utils.py +++ b/source/app/controllers/utils.py @@ -830,7 +830,10 @@ def upload_file(rfc, opt, file_obj): return result -def get_qr(data): +def get_qr(data, kind='svg', in_base64=False): buffer = io.BytesIO() - segno.make(data).save(buffer, kind='svg', scale=8, border=2) - return buffer.getvalue() + segno.make(data).save(buffer, kind=kind, scale=8, border=2) + qr = buffer + if in_base64: + qr = base64.b64encode(qr.getvalue()).decode() + return qr diff --git a/source/app/models/main.py b/source/app/models/main.py index efa15b4..6597371 100644 --- a/source/app/models/main.py +++ b/source/app/models/main.py @@ -4670,9 +4670,8 @@ class Facturas(BaseModel): f"&rr={data['receptor_rfc']}&tt={data['cfdi_total']}" f"&fe={data['cfdi_sello'][-8:]}" ) - cbb = util.get_qr(qr_data, False) + cbb = utils.get_qr(qr_data, 'png', True) data['cbb'] = f'data:image/png;base64,{cbb}' - # ~ data['cbb'] = utils.get_qr(qr_data) return data From 2f22ad4cc80820700ebfcb410e42cdd067ccd9f7 Mon Sep 17 00:00:00 2001 From: El Mau Date: Thu, 30 Dec 2021 19:41:40 -0600 Subject: [PATCH 09/21] Nuevos controles para carta porte terminada --- source/app/controllers/main.py | 18 +++++ source/app/controllers/util.py | 20 +++++ source/app/main.py | 2 + source/app/models/db.py | 9 +++ source/app/models/main.py | 77 ++++++++++++++++++ source/static/js/controller/admin.js | 114 +++++++++++++++++++++++++++ source/static/js/ui/admin.js | 86 ++++++++++++++++++++ source/static/js/ui/invoices.js | 2 +- 8 files changed, 327 insertions(+), 1 deletion(-) diff --git a/source/app/controllers/main.py b/source/app/controllers/main.py index 5df8012..7943af5 100644 --- a/source/app/controllers/main.py +++ b/source/app/controllers/main.py @@ -769,3 +769,21 @@ class AppUsers(object): user = req.env['beaker.session']['userobj'] req.context['result'] = self._db.users_post(values, user) resp.status = falcon.HTTP_200 + + +class AppSATUnidadesPeso(object): + + def __init__(self, db): + self._db = db + + def on_get(self, req, resp): + values = req.params + user = req.env['beaker.session']['userobj'] + req.context['result'] = self._db.sat_unidades_peso_get(values, user) + resp.status = falcon.HTTP_200 + + def on_post(self, req, resp): + values = req.params + user = req.env['beaker.session']['userobj'] + req.context['result'] = self._db.sat_unidades_peso_post(values, user) + resp.status = falcon.HTTP_200 diff --git a/source/app/controllers/util.py b/source/app/controllers/util.py index 077c7f9..7a93450 100644 --- a/source/app/controllers/util.py +++ b/source/app/controllers/util.py @@ -251,6 +251,26 @@ def get_sat_unidades(key): return tuple(data) +def get_sat_unidadespeso(key): + con = sqlite3.connect(DB_SAT) + con.row_factory = sqlite3.Row + cursor = con.cursor() + + filtro = '%{}%'.format(key) + sql = "SELECT * FROM unidad_peso WHERE key LIKE ? OR name LIKE ?" + + cursor.execute(sql, [filtro, filtro]) + data = cursor.fetchall() + cursor.close() + con.close() + if data is None: + return () + + data = tuple([dict(r) for r in data]) + + return data + + def get_sat_productos(key): con = sqlite3.connect(DB_SAT) con.row_factory = sqlite3.Row diff --git a/source/app/main.py b/source/app/main.py index da96dc5..6346cd4 100644 --- a/source/app/main.py +++ b/source/app/main.py @@ -24,6 +24,7 @@ from controllers.main import (AppEmpresas, AppUsers, AppWareHouse, AppWareHouseProduct, + AppSATUnidadesPeso, ) @@ -76,6 +77,7 @@ api.add_route('/warehouse', AppWareHouse(db)) api.add_route('/warehouseproduct', AppWareHouseProduct(db)) api.add_route('/ticketsdetails', AppTicketsDetails(db)) api.add_route('/users', AppUsers(db)) +api.add_route('/satunidadespeso', AppSATUnidadesPeso(db)) session_options = { diff --git a/source/app/models/db.py b/source/app/models/db.py index def0789..bb7e680 100644 --- a/source/app/models/db.py +++ b/source/app/models/db.py @@ -236,6 +236,9 @@ class StorageEngine(object): def _get_satunidades(self, values): return main.get_sat_unidades(values['key']) + def _get_satunidadespeso(self, values): + return main.get_sat_unidadespeso(values['key']) + def _get_satproductos(self, values): return main.get_sat_productos(values['key']) @@ -514,6 +517,12 @@ class StorageEngine(object): def nomina_get(self, filters, user): return main.CfdiNomina.get_data(filters, user) + def sat_unidades_peso_get(self, filters, user): + return main.SATUnidadesPeso.get_data(filters, user) + + def sat_unidades_peso_post(self, args, user): + return main.SATUnidadesPeso.post(args, user) + # Companies only in MV def _get_empresas(self, values): return main.companies_get() diff --git a/source/app/models/main.py b/source/app/models/main.py index 6597371..7cf1558 100644 --- a/source/app/models/main.py +++ b/source/app/models/main.py @@ -1358,6 +1358,77 @@ class CondicionesPago(BaseModel): return obj +class SATUnidadesPeso(BaseModel): + key = TextField(unique=True, index=True) + name = TextField(default='', index=True) + activo = BooleanField(default=False) + + class Meta: + order_by = ('name',) + indexes = ( + (('key', 'name'), True), + ) + + def __str__(self): + return '{} ({})'.format(self.name, self.key) + + @classmethod + def _get_all(cls, values, user): + rows = tuple(SATUnidadesPeso.select().dicts()) + return rows + + @classmethod + def _get_active(cls, values, user): + fields = ( + SATUnidadesPeso.key.alias('id'), + SATUnidadesPeso.name.alias('value'), + ) + where = (SATUnidadesPeso.activo==True) + rows = tuple( + SATUnidadesPeso.select(*fields).where(where).dicts() + ) + return rows + + @classmethod + def get_data(cls, values, user): + opt = values.pop('opt') + return getattr(cls, f'_get_{opt}')(values, user) + + @classmethod + def _add(cls, values, user): + result = True + try: + SATUnidadesPeso.create(**values) + except: + result = False + + return {'ok': result} + + @classmethod + def _delete(cls, values, user): + id = values['id'] + q = SATUnidadesPeso.delete().where(SATUnidadesPeso.id==id) + result = bool(q.execute()) + response = {'ok': result} + return response + + @classmethod + def _update_active(cls, values, user): + id = values['id'] + update = {'activo': bool(values['activo'])} + where = (SATUnidadesPeso.id==id) + q = SATUnidadesPeso.update(**update).where(where) + result = bool(q.execute()) + response = {'ok': result} + return response + + @classmethod + def post(cls, values, user): + opt = values['opt'] + args = utils.loads(values['values']) + return getattr(cls, f'_{opt}')(args, user) + + class SATUnidades(BaseModel): key = TextField(unique=True, index=True) name = TextField(default='', index=True) @@ -10249,6 +10320,10 @@ def get_sat_unidades(key): return util.get_sat_unidades(key) +def get_sat_unidadespeso(key): + return util.get_sat_unidadespeso(key) + + def get_sat_productos(key): return util.get_sat_productos(key) @@ -10345,6 +10420,7 @@ def _crear_tablas(rfc): InventoryEntries, PartnerInvoices, WareHouseProduct, + SATUnidadesPeso, ] log.info('Creando tablas...') database_proxy.create_tables(tablas, True) @@ -10402,6 +10478,7 @@ def _migrate_tables(rfc=''): InventoryEntries, PartnerInvoices, WareHouseProduct, + SATUnidadesPeso, ] log.info('Creando tablas nuevas...') database_proxy.create_tables(tablas, True) diff --git a/source/static/js/controller/admin.js b/source/static/js/controller/admin.js index f02390b..22b348c 100644 --- a/source/static/js/controller/admin.js +++ b/source/static/js/controller/admin.js @@ -160,6 +160,10 @@ var controllers = { $$('cmd_add_sucursal').attachEvent('onItemClick', cmd_add_sucursal_click) $$('grid_sucursales').attachEvent('onItemClick', grid_sucursales_click) + //~ Carta Porte + $$('grid_unidadpeso_found').attachEvent('onValueSuggest', grid_unidadpeso_found_click) + $$('grid_carta_unidades_peso').attachEvent('onItemClick', grid_carta_unidades_peso_click) + $$('grid_carta_unidades_peso').attachEvent('onCheck', grid_carta_unidades_peso_on_check) } } @@ -1568,6 +1572,27 @@ function agregar_nueva_unidad(obj){ } +function agregar_nueva_unidadpeso(obj){ + var grid = $$('grid_carta_unidades_peso') + var args = { + opt: 'add', + values: {key: obj.key, name: obj.name}, + } + + webix.ajax().post('/satunidadespeso', args, { + error: function(text, data, xhr) { + webix.message({type: 'error', text: 'Error al agregar'}) + }, + success: function(text, data, xhr){ + var values = data.json() + if (values.ok){ + grid.add(obj) + } + } + }) +} + + function grid_unidad_found_click(obj){ msg = '¿Estás seguro de agregar la siguiente unidad?' msg += '(' + obj.key + ')
' @@ -1589,6 +1614,27 @@ function grid_unidad_found_click(obj){ } +function grid_unidadpeso_found_click(obj){ + msg = '¿Estás seguro de agregar la siguiente unidad?' + msg += '(' + obj.key + ')
' + msg += obj.name + + webix.confirm({ + title: 'Agregar Unidad', + ok: 'Si', + cancel: 'No', + type: 'confirm-error', + text: msg, + callback:function(result){ + if(result){ + agregar_nueva_unidadpeso(obj) + } + } + }) + $$('buscar_carta_unidades_peso').setValue('') +} + + function agregar_impuesto(impuesto, tasa){ var grid = $$('grid_admin_taxes') var values = {impuesto: impuesto, tasa: tasa} @@ -3178,3 +3224,71 @@ function grid_warehouse_click(id, e, node){ } }) } + + +function delete_unit_weight(id){ + var grid = $$('grid_carta_unidades_peso') + + var values = { + opt: 'delete', + values: {id: id}, + } + + webix.ajax().post('/satunidadespeso', values, { + error:function(text, data, XmlHttpRequest){ + msg = 'Ocurrio un error, consulta a soporte técnico' + msg_error(msg) + }, + success:function(text, data, XmlHttpRequest){ + var values = data.json() + if(values.ok){ + grid.remove(id) + msg_ok('Unidad de Peso eliminada correctamente') + }else{ + msg_error(values.msg) + } + } + }) +} + + +function grid_carta_unidades_peso_click(id, e, node){ + if(id.column != 'delete'){ + return + } + + msg = '¿Estás seguro de borrar la Unidad de Peso seleccionada?' + webix.confirm({ + title: 'Borrar Unidad', + ok: 'Si', + cancel: 'No', + type: 'confirm-error', + text: msg, + callback:function(result){ + if(result){ + delete_unit_weight(id.row) + } + } + }) + +} + + +function grid_carta_unidades_peso_on_check(row, column, state){ + var values = { + opt: 'update_active', + values: {id: row, activo: state}, + } + webix.ajax().post('/satunidadespeso', values, { + error:function(text, data, XmlHttpRequest){ + msg = 'Ocurrio un error, consulta a soporte técnico' + msg_error(msg) + }, + success:function(text, data, XmlHttpRequest){ + var values = data.json() + if(!values.ok){ + msg_error(values.msg) + } + } + }) +} diff --git a/source/static/js/ui/admin.js b/source/static/js/ui/admin.js index e15d36f..127c69d 100644 --- a/source/static/js/ui/admin.js +++ b/source/static/js/ui/admin.js @@ -1303,6 +1303,91 @@ var sat_usos_cfdi = [ ] +var suggest_sat_unidades_peso = { + view: 'gridsuggest', + id: 'grid_unidadpeso_found', + name: 'grid_unidadpeso_found', + body: { + autoConfig: false, + scroll:true, + autoheight:false, + header: true, + yCount: 10, + columns: [ + {id: 'id', hidden: true}, + {id: 'key', adjust: 'data', header: 'Clave'}, + {id: 'name', adjust: 'data', header: 'Unidad'}, + ], + dataFeed:function(text){ + if (text.length > 1){ + this.load('/values/satunidadespeso?key=' + text) + }else{ + this.hide() + } + } + }, +} + + +var buscar_carta_unidades_peso = { + view: 'search', + id: 'buscar_carta_unidades_peso', + label: 'Buscar Unidad de Peso en el catálogo del SAT', + labelPosition: 'top', + suggest: suggest_sat_unidades_peso, + placeholder: 'Por clave o descripción. Captura al menos tres letras', +} + + +var columns_carta_unidades_peso = [ + {id: 'id', header: 'ID', hidden: true}, + {id: 'delete', header: '', width: 30, css: 'delete'}, + {id: 'key', header: 'Clave'}, + {id: 'name', header: 'Nombre', adjust: 'data'}, + {id: 'activo', header: 'Activo', template: '{common.checkbox()}', + editor: 'checkbox'}, +] + + +var grid_carta_unidades_peso = { + view: 'datatable', + id: 'grid_carta_unidades_peso', + url: 'satunidadespeso?opt=all', + select: 'cell', + adjust: true, + autowidth: true, + headermenu: true, + columns: columns_carta_unidades_peso, + on:{ + 'data->onStoreUpdated':function(){ + this.data.each(function(obj, i){ + obj.delete = '-' + }) + } + }, +} + + +var carta_porte_unidades_peso = [ + {maxHeight: 20}, + {cols: [{maxWidth: 15}, buscar_carta_unidades_peso, {}]}, + {maxHeight: 20}, + {cols: [{maxWidth: 15}, grid_carta_unidades_peso, {}]}, + {maxHeight: 20}, +] + + +var tab_sat_carta_porte = [{ + view: 'tabview', + id: 'tab_sat_carta_porte', + multiview: true, + animate: true, + cells: [ + {id: 'Unidades de Peso', rows: carta_porte_unidades_peso}, + ] +}] + + var tab_sat = { view: 'tabview', id: 'tab_sat', @@ -1315,6 +1400,7 @@ var tab_sat = { {id: 'Unidades', rows: sat_unidades}, {id: 'Formas de Pago', rows: sat_formasdepago}, {id: 'Usos de CFDI', rows: sat_usos_cfdi}, + {id: 'Carta Porte', rows: tab_sat_carta_porte}, ], } diff --git a/source/static/js/ui/invoices.js b/source/static/js/ui/invoices.js index e3936e2..72710c7 100644 --- a/source/static/js/ui/invoices.js +++ b/source/static/js/ui/invoices.js @@ -973,7 +973,7 @@ var grid_carta_tipos_figuras = { var body_carta_mercancias = {rows:[ {cols: [{view: 'richselect', id: 'lst_carta_UnidadPeso', labelPosition: 'top', - label: 'Unidad de Peso', maxWidth: 300, options: []}, {} + label: 'Unidad de Peso', maxWidth: 300, options: '/satunidadespeso?opt=active'}, {} ]}, {maxHeight: 10}, grid_carta_mercancias, From 7b2467f99a6bd70f04c5d183606b1c04526c1546 Mon Sep 17 00:00:00 2001 From: El Mau Date: Sat, 1 Jan 2022 21:48:02 -0600 Subject: [PATCH 10/21] Iniciar validaciones para carta porte --- README.md | 1 + source/static/js/controller/invoices.js | 39 ++++++++++++++++++------- 2 files changed, 30 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index dce0150..7f90d73 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,7 @@ contratar: administracion ARROBA empresalibre.net #### Ahora también puede aportar con criptomonedas: +G1: `A5DdXxCKPw3QKWVdDVs7CzkNugNUW1sHu5zDJFWxCU2h` BCH: `qztd3l00xle5tffdqvh2snvadkuau2ml0uqm4n875d` BTC: `3FhiXcXmAesmQzrNEngjHFnvaJRhU1AGWV` diff --git a/source/static/js/controller/invoices.js b/source/static/js/controller/invoices.js index 4050a17..7f367ab 100644 --- a/source/static/js/controller/invoices.js +++ b/source/static/js/controller/invoices.js @@ -342,11 +342,25 @@ function cmd_delete_invoice_click(id, e, node){ function validate_invoice(values){ - if(values.id_partner == 0){ - webix.UIManager.setFocus('search_client_name') - msg = 'Selecciona un cliente' - msg_error(msg) - return false + var usar_cartaporte = $$('chk_cfdi_usar_cartaporte').getValue() + if(usar_cartaporte){ + value = $$('lst_carta_UnidadPeso').getValue() + if(!value){ + msg = 'Es necesario seleccionar una Unidad de Peso' + msg_error(msg) + return false + } + + } + + var tipo_comprobante = $$('lst_tipo_comprobante').getValue() + if(tipo_comprobante != 'T'){ + if(values.id_partner == 0){ + webix.UIManager.setFocus('search_client_name') + msg = 'Selecciona un cliente' + msg_error(msg) + return false + } } if(!grid.count()){ @@ -471,11 +485,6 @@ function validate_invoice(values){ } - var tipo_comprobante = $$('lst_tipo_comprobante').getValue() - if(tipo_comprobante == 'T'){ - msg_ok('El CFDI es de tipo Traslado') - } - var rows = grid.data.getRange() for (i = 0; i < rows.length; i++) { var importe = rows[i]['importe'] @@ -688,6 +697,16 @@ function guardar_y_timbrar(values){ data['ine'] = valores } + var usar_cartaporte = $$('chk_cfdi_usar_cartaporte').getValue() + if(usar_cartaporte){ + var cartaporte = { + TranspInternac: $$('lst_carta_TranspInternac').getValue() + } + + + data['cartaporte'] = cartaporte + } + if(!save_invoice(data)){ return } From 481594839d782752990a4dd84dc436f826252072 Mon Sep 17 00:00:00 2001 From: El Mau Date: Mon, 3 Jan 2022 19:14:20 -0600 Subject: [PATCH 11/21] Guardar datos para carta porte --- source/app/controllers/cfdi_xml.py | 38 ++++++++++++++++++++++++- source/app/models/main.py | 24 ++++++++++++++-- source/app/settings.py | 4 +++ source/static/js/controller/invoices.js | 30 ++++++++++++++++++- 4 files changed, 92 insertions(+), 4 deletions(-) diff --git a/source/app/controllers/cfdi_xml.py b/source/app/controllers/cfdi_xml.py index 57039ab..4c07bd9 100644 --- a/source/app/controllers/cfdi_xml.py +++ b/source/app/controllers/cfdi_xml.py @@ -20,6 +20,7 @@ import datetime from xml.etree import ElementTree as ET from xml.dom.minidom import parseString +from dateutil import parser from logbook import Logger @@ -120,6 +121,7 @@ class CFDI(object): self._pagos = False self._is_nomina = False self._leyendas = False + self._carta_porte = False self._divisas = '' self.error = '' @@ -171,6 +173,7 @@ class CFDI(object): self._ine = True self._pagos = bool(datos['complementos'].get('pagos', False)) self._leyendas = bool(datos['complementos'].get('leyendas', False)) + self._carta_porte = bool(datos['complementos'].get('cartaporte', False)) self._divisas = datos['comprobante'].pop('divisas', '') @@ -236,9 +239,16 @@ class CFDI(object): attributes[name] = SAT['leyendas']['xmlns'] schema_leyendas = SAT['leyendas']['schema'] + schema_carta_porte = '' + if self._carta_porte: + name = 'xmlns:{}'.format(SAT['cartaporte']['prefix']) + attributes[name] = SAT['cartaporte']['xmlns'] + schema_carta_porte = SAT['cartaporte']['schema'] + attributes['xsi:schemaLocation'] = self._sat_cfdi['schema'] + \ schema_locales + schema_donativo + schema_ine + schema_edu + \ - schema_divisas + schema_nomina + schema_pagos + schema_leyendas + schema_divisas + schema_nomina + schema_pagos + schema_leyendas + \ + schema_carta_porte attributes.update(datos) if not 'Version' in attributes: @@ -461,6 +471,32 @@ class CFDI(object): self._complemento = ET.SubElement( self._cfdi, '{}:Complemento'.format(self._pre)) + if self._carta_porte: + datos = datos['cartaporte'] + print('\nDatos', datos) + ubicaciones = datos.pop('ubicaciones') + mercancias = datos.pop('mercancias', ()) + tiposfigura = datos.pop('tiposfigura', ()) + + atributos = {'Version': SAT['cartaporte']['version']} + atributos.update(datos) + + prefix = SAT['cartaporte']['prefix'] + node_carta = ET.SubElement(self._complemento, f'{prefix}:CartaPorte', atributos) + + node = ET.SubElement(node_carta, f'{prefix}:Ubicaciones') + for ubicacion in ubicaciones: + dt = parser.parse(ubicacion['FechaHoraSalidaLlegada']) + ubicacion['FechaHoraSalidaLlegada'] = dt.isoformat()[:19] + ET.SubElement(node, f'{prefix}:Ubicacion', ubicacion) + + attr = mercancias + mercancias = attr.pop('mercancias') + node = ET.SubElement(node_carta, f'{prefix}:Mercancias', attr) + for mercancia in mercancias: + ET.SubElement(node, f'{prefix}:Mercancia', mercancia) + + if self._divisas: atributos = { 'version': SAT['divisas']['version'], diff --git a/source/app/models/main.py b/source/app/models/main.py index 7cf1558..5d47532 100644 --- a/source/app/models/main.py +++ b/source/app/models/main.py @@ -43,6 +43,7 @@ from controllers import utils from settings import ( DEBUG, CANCEL_VERSION, + CARTA_PORTE, DEFAULT_GLOBAL, DB_COMPANIES, EXT, @@ -5317,6 +5318,17 @@ class Facturas(BaseModel): FacturasComplementos.create(**data) return + def _save_cartaporte(self, invoice, valores): + if not valores: + return + data = { + 'factura': invoice, + 'nombre': 'cartaporte', + 'valores': valores, + } + FacturasComplementos.create(**data) + return + def _get_serie(self, user, default_serie): if user.sucursal is None: return default_serie @@ -5328,6 +5340,7 @@ class Facturas(BaseModel): productos = util.loads(values.pop('productos')) relacionados = util.loads(values.pop('relacionados')) ine = values.pop('ine', {}) + cartaporte = values.pop('cartaporte', {}) tipo_comprobante = values['tipo_comprobante'] folio_custom = values.pop('folio_custom', '') divisas = values.pop('divisas', '') @@ -5354,9 +5367,14 @@ class Facturas(BaseModel): values['lugar_expedicion'] = emisor.cp_expedicion or emisor.codigo_postal values['anticipo'] = util.get_bool(values['anticipo']) values['donativo'] = util.get_bool(values['donativo']) + if tipo_comprobante == 'T': values['metodo_pago'] = '' values['forma_pago'] = '' + values['moneda'] = CARTA_PORTE['MONEDA'] + self_client = Socios.get(Socios.rfc==emisor.rfc) + if values['cliente'] != self_client.id: + values['cliente'] = self_client with database_proxy.atomic() as txn: # ~ print('VALUES\n\n', values) @@ -5365,6 +5383,7 @@ class Facturas(BaseModel): cls, obj, productos, tipo_comprobante, user) cls._guardar_relacionados(cls, obj, relacionados) cls._guardar_ine(cls, obj, ine) + cls._save_cartaporte(cls, obj, cartaporte) cls._save_leyendas_fiscales(cls, obj, leyendas_fiscales) obj.subtotal = totals['subtotal'] obj.descuento = totals['descuento'] @@ -5463,9 +5482,10 @@ class Facturas(BaseModel): comprobante['Descuento'] = FORMAT.format(invoice.descuento) if invoice.tipo_comprobante == 'T': - comprobante['SubTotal'] = '0.00' - comprobante['Total'] = '0.00' + comprobante['SubTotal'] = '0' + comprobante['Total'] = '0' del comprobante['FormaPago'] + del comprobante['TipoCambio'] if invoice.tipo_relacion: relacionados = { diff --git a/source/app/settings.py b/source/app/settings.py index d4a7804..16fef84 100644 --- a/source/app/settings.py +++ b/source/app/settings.py @@ -209,6 +209,10 @@ EXT = { } MXN = 'MXN' +CARTA_PORTE = { + 'MONEDA': 'XXX', +} + PATHS = { 'STATIC': path_static, 'CSS': path_css, diff --git a/source/static/js/controller/invoices.js b/source/static/js/controller/invoices.js index 7f367ab..26d5362 100644 --- a/source/static/js/controller/invoices.js +++ b/source/static/js/controller/invoices.js @@ -699,10 +699,38 @@ function guardar_y_timbrar(values){ var usar_cartaporte = $$('chk_cfdi_usar_cartaporte').getValue() if(usar_cartaporte){ + var total_distance = 0.00 + var total_weight = 0.00 var cartaporte = { - TranspInternac: $$('lst_carta_TranspInternac').getValue() + TranspInternac: $$('lst_carta_TranspInternac').getValue(), + TotalDistRec: total_distance, } + var ubicaciones = $$('grid_carta_ubicaciones').data.getRange() + ubicaciones.forEach(function(row, index){ + delete row['id'] + delete row['delete'] + if(row['DistanciaRecorrida']){ + total_distance += parseFloat(row['DistanciaRecorrida']) + } + }) + cartaporte['TotalDistRec'] = String(total_distance) + cartaporte['ubicaciones'] = ubicaciones + var mercancias = $$('grid_carta_mercancias').data.getRange() + mercancias.forEach(function(row, index){ + delete row['id'] + row['Cantidad'] = String(row['Cantidad']) + if(row['PesoEnKg']){ + total_weight += parseFloat(row['PesoEnKg']) + } + }) + var mercancias = { + 'PesoBrutoTotal': String(total_weight), + 'UnidadPeso': $$('lst_carta_UnidadPeso').getValue(), + 'NumTotalMercancias': String(mercancias.length), + mercancias: mercancias, + } + cartaporte['mercancias'] = mercancias data['cartaporte'] = cartaporte } From bf601dfcef68ca0c05b101e1602767ffdd68e096 Mon Sep 17 00:00:00 2001 From: El Mau Date: Mon, 3 Jan 2022 23:06:55 -0600 Subject: [PATCH 12/21] =?UTF-8?q?Cambios=20en=20cancelaci=C3=B3n=20para=20?= =?UTF-8?q?Finkok?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- source/app/controllers/pacs/finkok/finkok.py | 13 +- source/app/controllers/utils.py | 10 +- source/app/models/main.py | 15 +- source/static/js/controller/invoices.js | 152 +++++++++++-------- source/static/js/ui/invoices.js | 43 +++++- 5 files changed, 155 insertions(+), 78 deletions(-) diff --git a/source/app/controllers/pacs/finkok/finkok.py b/source/app/controllers/pacs/finkok/finkok.py index 1bb095b..93efd30 100644 --- a/source/app/controllers/pacs/finkok/finkok.py +++ b/source/app/controllers/pacs/finkok/finkok.py @@ -58,6 +58,7 @@ class DebugPlugin(Plugin): path = f'/tmp/soap_{name}.xml' with open(path, 'w') as f: f.write(data) + # ~ print(data) return def egress(self, envelope, http_headers, operation, binding_options): @@ -209,10 +210,16 @@ class PACFinkok(object): client = Client(self.URL[method], transport=self._transport, plugins=self._plugins) uuid_type = client.get_type('ns1:UUIDS') - sa = client.get_type('ns0:stringArray') - + # ~ sa = client.get_type('ns0:stringArray') + ns1_uuid = client.get_type('ns1:UUID') + data_uuid = { + 'UUID': cfdi_uuid, + 'FolioSustitucion': info['args']['uuid'], + 'Motivo': info['args']['reason'], + } + # ~ 'UUIDS': uuid_type(uuids=sa(string=cfdi_uuid)), args = { - 'UUIDS': uuid_type(uuids=sa(string=cfdi_uuid)), + 'UUIDS': uuid_type(ns1_uuid(**data_uuid)), 'username': auth['user'], 'password': auth['pass'], 'taxpayer_id': rfc_emisor, diff --git a/source/app/controllers/utils.py b/source/app/controllers/utils.py index 3e142f1..0b4a6a4 100644 --- a/source/app/controllers/utils.py +++ b/source/app/controllers/utils.py @@ -665,10 +665,10 @@ def get_pac_by_rfc(cfdi): return RFCS[rfc_pac] -def _cancel_with_cert(invoice, auth, certificado): +def _cancel_with_cert(invoice, args, auth, certificado): cert = SATCertificate(certificado.cer, certificado.key_enc.encode()) pac = PACS[auth['pac']]() - info = {'cer': cert.cer_pem, 'key': cert.key_pem} + info = {'cer': cert.cer_pem, 'key': cert.key_pem, 'args': args} result = pac.cancel(invoice.xml, info, auth) if pac.error: @@ -681,9 +681,9 @@ def _cancel_with_cert(invoice, auth, certificado): return data -def cancel_xml_sign(invoice, auth, certificado): - if DEBUG: - return _cancel_with_cert(invoice, auth, certificado) +def cancel_xml_sign(invoice, args, auth, certificado): + # ~ if DEBUG: + return _cancel_with_cert(invoice, args, auth, certificado) cert = SATCertificate(certificado.cer, certificado.key_enc.encode()) pac = PACS[auth['pac']]() diff --git a/source/app/models/main.py b/source/app/models/main.py index 5d47532..7884aa8 100644 --- a/source/app/models/main.py +++ b/source/app/models/main.py @@ -4379,8 +4379,8 @@ class Facturas(BaseModel): return True @classmethod - def cancel(cls, id): - obj = Facturas.get(Facturas.id==id) + def cancel(cls, args): + obj = Facturas.get(Facturas.id==args['id']) if obj.uuid is None: obj.estatus = 'Cancelada' obj.cancelada = True @@ -4389,10 +4389,10 @@ class Facturas(BaseModel): msg = 'Factura cancelada correctamente' return {'ok': True, 'msg': msg, 'row': {'estatus': obj.estatus}} - return cls._cancel_xml_sign(obj) + return cls._cancel_xml_sign(obj, args) @classmethod - def _cancel_xml_sign(cls, invoice): + def _cancel_xml_sign(cls, invoice, args): if not invoice.version in CANCEL_VERSION: msg = 'Solo es posible cancelar CFDI >= 3.3' return {'ok': False, 'msg': msg} @@ -4401,7 +4401,7 @@ class Facturas(BaseModel): auth = Configuracion.get_({'fields': 'auth_by_pac', 'pac': pac}) certificado = Certificado.get(Certificado.es_fiel==False) - result = utils.cancel_xml_sign(invoice, auth, certificado) + result = utils.cancel_xml_sign(invoice, args, auth, certificado) if result['ok']: invoice.estatus = 'Cancelada' @@ -6087,9 +6087,10 @@ class Facturas(BaseModel): result = {'ok': False, 'msg': msg, 'row': {}} return result - result = cls.cancel(args['id']) + values = utils.loads(args['values']) + result = cls.cancel(values) if result['ok']: - m = 'C {}'.format(args['id']) + m = 'C {}'.format(values['id']) _save_log(user.usuario, m, 'F') return result diff --git a/source/static/js/controller/invoices.js b/source/static/js/controller/invoices.js index 26d5362..c5a1abe 100644 --- a/source/static/js/controller/invoices.js +++ b/source/static/js/controller/invoices.js @@ -58,7 +58,7 @@ var invoices_controllers = { $$('cmd_invoice_timbrar').attachEvent('onItemClick', cmd_invoice_timbrar_click) $$('cmd_invoice_sat').attachEvent('onItemClick', cmd_invoice_sat_click) $$('cmd_invoice_verify_sat').attachEvent('onItemClick', cmd_invoice_verify_sat_click) - $$('cmd_invoice_cancelar').attachEvent('onItemClick', cmd_invoice_cancelar_click) + $$('cmd_invoice_ask_cancel').attachEvent('onItemClick', cmd_invoice_ask_cancel_click) $$('grid_invoices').attachEvent('onItemClick', grid_invoices_click) $$('grid_invoices').attachEvent('onSelectChange', grid_invoices_on_select_change) $$('grid_invoices').attachEvent('onHeaderClick', grid_invoices_on_header_click) @@ -1469,67 +1469,6 @@ function grid_invoices_click(id, e, node){ } -function send_cancel(id){ - webix.ajax().post('invoices', {opt: 'cancel', id: id}, function(text, data){ - var values = data.json() - if(values.ok){ - msg_ok(values.msg) - gi.updateItem(id, values.row) - }else{ - msg_error('No fue posible cancelar') - webix.alert({ - title: 'Error al Cancelar', - text: values.msg, - type: 'alert-error' - }) - } - }) -} - - -function cmd_invoice_cancelar_click(){ - if(gi.count() == 0){ - return - } - - var row = gi.getSelectedItem() - if (row == undefined){ - msg_error('Selecciona una factura') - return - } - - if (row instanceof Array){ - msg_error('Selecciona solo una factura') - return - } - - if(row.estatus == 'Cancelada'){ - msg_error('La factura ya esta cancelada') - return - } - - msg = '' - if(!row.uuid){ - msg = 'La factura NO esta timbrada, asegurate de que efectivamente NO este timbrada.

' - } - - msg += '¿Estás seguro de enviar a cancelar esta factura?

\ - ESTA ACCIÓN NO SE PUEDE DESHACER' - webix.confirm({ - title: 'Cancelar Factura', - ok: 'Si', - cancel: 'No', - type: 'confirm-error', - text: msg, - callback:function(result){ - if(result){ - send_cancel(row.id) - } - } - }) -} - - function get_filters_invoices(){ var filters = $$('filter_dates').getValue() @@ -2553,3 +2492,92 @@ function tv_invoice_change(nv, ov){ _tab_carta_porte() } } + + +function send_invoice_cancel(id, reason='', uuid=''){ + var args = { + opt: 'cancel', + values: { + id: id, + reason: reason, + uuid: uuid, + } + } + webix.ajax().post('invoices', args, function(text, data){ + var values = data.json() + if(values.ok){ + msg_ok(values.msg) + gi.updateItem(id, values.row) + }else{ + msg_error('No fue posible cancelar') + webix.alert({ + title: 'Error al Cancelar', + text: values.msg, + type: 'alert-error' + }) + } + }) +} + + +function cmd_invoice_cancel_click(){ + var row = $$('grid_invoices').getSelectedItem() + var reason = $$('lst_reasons_cancel').getValue() + var uuid = $$('txt_cancel_uuid').getValue() + + send_invoice_cancel(row.id, reason, uuid) + $$('win_invoice_cancel').close() +} + + +function cmd_win_cancel_close_click(){ + $$('win_invoice_cancel').close() +} + + +function cmd_invoice_ask_cancel_click(){ + var g = $$('grid_invoices') + + if(g.count() == 0){ + return + } + + var row = g.getSelectedItem() + + if (row == undefined){ + msg_error('Selecciona una factura') + return + } + + if (row instanceof Array){ + msg_error('Selecciona solo una factura') + return + } + + if(row.estatus == 'Cancelada'){ + msg_error('La factura ya esta cancelada') + return + } + + msg = '' + if(row.uuid){ + win_invoice_cancel.init() + $$('win_invoice_cancel').show() + }else{ + msg = 'La factura NO esta timbrada, asegurate de que efectivamente NO este timbrada.

' + msg += '¿Estás seguro de enviar a cancelar esta factura?

\ + ESTA ACCIÓN NO SE PUEDE DESHACER' + webix.confirm({ + title: 'Cancelar Factura', + ok: 'Si', + cancel: 'No', + type: 'confirm-error', + text: msg, + callback:function(result){ + if(result){ + send_invoice_cancel(row.id) + } + } + }) + } +} diff --git a/source/static/js/ui/invoices.js b/source/static/js/ui/invoices.js index 72710c7..a16cb9f 100644 --- a/source/static/js/ui/invoices.js +++ b/source/static/js/ui/invoices.js @@ -208,7 +208,7 @@ var toolbar_invoices_util = [ {view: 'button', id: 'cmd_invoice_verify_sat', label: 'SAT', type: 'iconButton', autowidth: true, icon: 'firefox'}, {}, - {view: 'button', id: 'cmd_invoice_cancelar', label: 'Cancelar', + {view: 'button', id: 'cmd_invoice_ask_cancel', label: 'Cancelar', type: 'iconButton', autowidth: true, icon: 'ban'}, ] @@ -1118,3 +1118,44 @@ var win_import_invoice = { $$('up_invoice').attachEvent('onUploadComplete', up_invoice_upload_complete) } } + + +var opt_reasons_cancel = [ + {id: '', value: ''}, + {id: '01', value: '[01] Comprobante emitido con errores con relación'}, + {id: '02', value: '[02] Comprobante emitido con errores sin relación'}, + {id: '03', value: '[03] No se llevó acabo la operación'}, + {id: '04', value: '[04] Operación nominativa relacionada en una factura global'}, +] + + +var body_invoice_cancel = {rows: [{minHeight: 15}, + {view: 'richselect', id: 'lst_reasons_cancel', labelPosition: 'top', label: 'Razón de cancelación', options: opt_reasons_cancel, value: '', width: 400}, + {view: 'text', id: 'txt_cancel_uuid', labelPosition: 'top', label: 'UUID que sustituye', width: 400}, + {view: 'label', label: 'Esta acción no se puede deshacer', autowidth: true, align: 'center'}, + {view: 'label', label: '¿Estás segura de continuar?', autowidth: true, align: 'center'}, + {cols: [{}, + {view: 'button', id: 'cmd_invoice_cancel', width: 100, label: 'Cancelar', type: 'danger', icon: 'ban'}, + {maxWidth: 25}, + {view: 'button', id: 'cmd_win_cancel_close', width: 100, label: 'Cerrar'}, + {} + ]}, + {minHeight: 20}, +]} + + +var win_invoice_cancel = { + init: function(){ + webix.ui({ + view: 'window', + id: 'win_invoice_cancel', + modal: true, + width: 400, + position: 'center', + head: 'Cancelar CFDI', + body: body_invoice_cancel, + }) + $$('cmd_invoice_cancel').attachEvent('onItemClick', cmd_invoice_cancel_click) + $$('cmd_win_cancel_close').attachEvent('onItemClick', cmd_win_cancel_close_click) + } +} From 4ae5197ced9b1cedcaa7d7c0e907484245fcc55c Mon Sep 17 00:00:00 2001 From: El Mau Date: Tue, 4 Jan 2022 14:29:00 -0600 Subject: [PATCH 13/21] =?UTF-8?q?Cambios=20en=20cancelaci=C3=B3n=20para=20?= =?UTF-8?q?Comercio?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controllers/pacs/comerciodigital/comercio.py | 16 +++++++++------- source/app/controllers/pacs/finkok/finkok.py | 2 +- source/static/js/controller/invoices.js | 11 +++++++++++ 3 files changed, 21 insertions(+), 8 deletions(-) diff --git a/source/app/controllers/pacs/comerciodigital/comercio.py b/source/app/controllers/pacs/comerciodigital/comercio.py index 08c4ed9..0d1c48f 100644 --- a/source/app/controllers/pacs/comerciodigital/comercio.py +++ b/source/app/controllers/pacs/comerciodigital/comercio.py @@ -46,8 +46,8 @@ class PACComercioDigital(object): api = 'https://app2.comercio-digital.mx/{}' URL = { 'timbra': ws.format('ws', 'timbre/timbrarV5.aspx'), - 'cancel': ws.format('cancela', 'cancela3/cancelarUuid'), - 'cancelxml': ws.format('cancela', 'cancela3/cancelarXml'), + 'cancel': ws.format('cancela', 'cancela4/cancelarUuid'), + 'cancelxml': ws.format('cancela', 'cancela4/cancelarXml'), 'status': ws.format('cancela', 'arws/consultaEstatus'), 'client': api.format('x3/altaEmpresa'), 'saldo': api.format('x3/saldo'), @@ -69,8 +69,8 @@ class PACComercioDigital(object): ws6 = 'https://pruebas6.comercio-digital.mx/arws/{}' URL = { 'timbra': ws.format('timbre/timbrarV5.aspx'), - 'cancel': ws.format('cancela3/cancelarUuid'), - 'cancelxml': ws.format('cancela3/cancelarXml'), + 'cancel': ws.format('cancela4/cancelarUuid'), + 'cancelxml': ws.format('cancela4/cancelarXml'), 'status': ws6.format('consultaEstatus'), 'client': api.format('x3/altaEmpresa'), 'saldo': api.format('x3/saldo'), @@ -148,7 +148,7 @@ class PACComercioDigital(object): def _get_data_cancel(self, cfdi, info, auth): info['pass'] = '12345678a' - info['tipo'] = 'cfdi3.3' + info['tipo'] = 'cfdi' info['key'] = base64.b64encode(info['key']).decode() info['cer'] = base64.b64encode(info['cer']).decode() @@ -180,16 +180,18 @@ class PACComercioDigital(object): f"PWDK={info['pass']}", f"KEYF={info['key']}", f"CERT={info['cer']}", - f"TIPO={info['tipo']}", + f"TIPO1={info['tipo']}", f"ACUS=SI", f"RFCR={rfc_receptor}", f"TIPOC={tipo}", f"TOTAL={total}", + f"UUIDREL={info['args']['uuid']}", + f"MOTIVO={info['args']['reason']}", ) return '\n'.join(data) def cancel(self, cfdi, info, auth={}): - if not auth: + if DEBUG or not auth: auth = AUTH url = self.URL['cancel'] data = self._get_data_cancel(cfdi, info, auth) diff --git a/source/app/controllers/pacs/finkok/finkok.py b/source/app/controllers/pacs/finkok/finkok.py index 93efd30..09c54b5 100644 --- a/source/app/controllers/pacs/finkok/finkok.py +++ b/source/app/controllers/pacs/finkok/finkok.py @@ -47,7 +47,7 @@ logging.getLogger('zeep').setLevel(logging.ERROR) TIMEOUT = 10 -DEBUG_SOAP = False +DEBUG_SOAP = True class DebugPlugin(Plugin): diff --git a/source/static/js/controller/invoices.js b/source/static/js/controller/invoices.js index c5a1abe..90121a1 100644 --- a/source/static/js/controller/invoices.js +++ b/source/static/js/controller/invoices.js @@ -2525,6 +2525,17 @@ function cmd_invoice_cancel_click(){ var reason = $$('lst_reasons_cancel').getValue() var uuid = $$('txt_cancel_uuid').getValue() + if(!reason){ + msg = 'Selecciona un motivo para esta cancelación' + msg_error(msg) + return + } + if(reason=='01' & !uuid){ + msg = 'Debes de capturar el UUID que reemplaza a este CFDI' + msg_error(msg) + return + } + send_invoice_cancel(row.id, reason, uuid) $$('win_invoice_cancel').close() } From 3cc11430d73e12d48b2208686d1f450216e6d48a Mon Sep 17 00:00:00 2001 From: El Mau Date: Tue, 4 Jan 2022 22:40:41 -0600 Subject: [PATCH 14/21] Cambio de formato del QR para plantilla json --- source/app/controllers/util.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/source/app/controllers/util.py b/source/app/controllers/util.py index 7a93450..a217182 100644 --- a/source/app/controllers/util.py +++ b/source/app/controllers/util.py @@ -1898,10 +1898,12 @@ def _timbre(doc, version, values, pdf_from='1'): } qr_data = '{url}{uuid}{emisor}{receptor}{total}{sello}'.format(**qr_data) - if pdf_from == '1': - data['cbb'] = get_qr(qr_data, 'png') - else: - data['cbb'] = get_qr(qr_data) + data['cbb'] = get_qr(qr_data, 'png') + + # ~ if pdf_from == '1': + # ~ data['cbb'] = get_qr(qr_data, 'png') + # ~ else: + # ~ data['cbb'] = get_qr(qr_data) data['cadenaoriginal'] = CADENA.format(**data) return data From b3663dfcc66d356d845e5b2404df78846ac64981 Mon Sep 17 00:00:00 2001 From: El Mau Date: Wed, 5 Jan 2022 09:42:40 -0600 Subject: [PATCH 15/21] Modificar selector emergente para el calendario --- source/static/js/ui/invoices.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/source/static/js/ui/invoices.js b/source/static/js/ui/invoices.js index a16cb9f..d21b3c8 100644 --- a/source/static/js/ui/invoices.js +++ b/source/static/js/ui/invoices.js @@ -850,12 +850,12 @@ var opt_origen_destino = [ ] -webix.editors.$popup = { - date: { - view: "popup", - body: {view: "calendar", timepicker:true, icons:true} - } -}; +//~ webix.editors.$popup = { + //~ date: { + //~ view: "popup", + //~ body: {view: "calendar", timepicker:true, icons:true} + //~ } +//~ }; var grid_cols_carta_ubicaciones = [ From 3ffc54f9669bc8fe6789e4992796a16b24634acb Mon Sep 17 00:00:00 2001 From: El Mau Date: Thu, 6 Jan 2022 21:15:11 -0600 Subject: [PATCH 16/21] Modificar selector emergente para el calendario --- source/static/js/ui/invoices.js | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/source/static/js/ui/invoices.js b/source/static/js/ui/invoices.js index d21b3c8..c1a28a8 100644 --- a/source/static/js/ui/invoices.js +++ b/source/static/js/ui/invoices.js @@ -850,12 +850,13 @@ var opt_origen_destino = [ ] -//~ webix.editors.$popup = { - //~ date: { - //~ view: "popup", - //~ body: {view: "calendar", timepicker:true, icons:true} - //~ } -//~ }; +var date_suggest = { + type: 'calendar', + body:{ + timepicker: true, + icons: true, + } +} var grid_cols_carta_ubicaciones = [ @@ -863,7 +864,7 @@ var grid_cols_carta_ubicaciones = [ {id: 'delete', header: '', hidden: true, width: 30, css: 'delete'}, {id: 'TipoUbicacion', header: 'Tipo de Ubicación', editor: 'select', options: opt_origen_destino, fillspace: 1}, {id: 'RFCRemitenteDestinatario', header: 'RFC Rem/Des', editor: 'text', fillspace: 1}, - {id: 'FechaHoraSalidaLlegada', header: 'Fecha/Hora', editor: 'date', format: webix.Date.dateToStr("%D, %d-%M-%Y %h:%i"), footer: 'Total distancia:', fillspace: 1}, + {id: 'FechaHoraSalidaLlegada', header: 'Fecha/Hora', editor: 'date', suggest: date_suggest, format: webix.Date.dateToStr("%D, %d-%M-%Y %h:%i"), footer: 'Total distancia:', fillspace: 1}, {id: 'DistanciaRecorrida', header: 'Distancia (KM)', editor: 'text', css: 'right', footer: {content: 'summColumn', css: 'right'}, fillspace: 1}, ] From a91feb15d73b682f3c52d291e3f2ba01e1dd9421 Mon Sep 17 00:00:00 2001 From: El Mau Date: Fri, 7 Jan 2022 22:59:33 -0600 Subject: [PATCH 17/21] Agregar nuevos regimenes fiscales --- source/db/valores_iniciales.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/source/db/valores_iniciales.json b/source/db/valores_iniciales.json index 659c993..7af3182 100644 --- a/source/db/valores_iniciales.json +++ b/source/db/valores_iniciales.json @@ -114,7 +114,9 @@ {"key": "607", "name": "Régimen de Enajenación o Adquisición de Bienes", "moral": true, "activo": false}, {"key": "629", "name": "De los Regímenes Fiscales Preferentes y de las Empresas Multinacionales", "fisica": true, "activo": false}, {"key": "630", "name": "Enajenación de acciones en bolsa de valores", "fisica": true, "activo": false}, - {"key": "615", "name": "Régimen de los ingresos por obtención de premios", "fisica": true, "activo": false} + {"key": "615", "name": "Régimen de los ingresos por obtención de premios", "fisica": true, "activo": false}, + {"key": "625", "name": "Régimen de las Actividades Empresariales con ingresos a través de Plataformas Tecnológicas", "fisica": true, "activo": true}, + {"key": "626", "name": "Régimen Simplificado de Confianza", "moral": true, "fisica": true, "activo": true} ] }, { From 86bc0324130d815a1fe6563016378ee982dbbd44 Mon Sep 17 00:00:00 2001 From: El Mau Date: Sat, 8 Jan 2022 21:24:19 -0600 Subject: [PATCH 18/21] Nuevos controles para subir las plantillas --- source/static/js/ui/admin.js | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/source/static/js/ui/admin.js b/source/static/js/ui/admin.js index 127c69d..d178033 100644 --- a/source/static/js/ui/admin.js +++ b/source/static/js/ui/admin.js @@ -594,8 +594,20 @@ var type_make_pdf = [ ] +var opt_templates_cfdi = [ + {id: 'template_3.3_cp_2.0', value: 'CFDI v3.3 - Carta Porte 2.0'}, +] + + var options_templates = [ - {maxHeight: 15}, + {maxHeight: 25}, + {cols: [{maxWidth: 20}, + {view: 'richselect', id: 'lst_templates_cfdi', label: 'Plantillas CFDI: ', labelWidth: 100, options: opt_templates_cfdi}, + {maxWidth: 10}, + {view: 'button', id: 'cmd_template_upload', type: 'iconButton', icon: 'file', width: 35}, + {}, + {maxWidth: 20} ]}, + {maxHeight: 25}, {cols: [{maxWidth: 20}, {view: 'search', id: 'txt_plantilla_factura_32', name: 'plantilla_factura_32', label: 'Plantilla Factura v3.2 (ODS): ', labelPosition: 'top', From a3d291c8936ebce81b09f70eae51ba7f3c094118 Mon Sep 17 00:00:00 2001 From: El Mau Date: Sun, 9 Jan 2022 23:52:05 -0600 Subject: [PATCH 19/21] =?UTF-8?q?Primera=20versi=C3=B3n=20m=C3=ADnima=20de?= =?UTF-8?q?=20Carta=20Porte=20timbrada?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- source/app/controllers/cfdi_xml.py | 12 ++++++++++ source/app/models/main.py | 13 +++++++++- source/static/js/controller/invoices.js | 25 ++++++++++++++++++- source/static/js/ui/invoices.js | 32 +++++++++++++++++++------ 4 files changed, 73 insertions(+), 9 deletions(-) diff --git a/source/app/controllers/cfdi_xml.py b/source/app/controllers/cfdi_xml.py index 4c07bd9..e60fb43 100644 --- a/source/app/controllers/cfdi_xml.py +++ b/source/app/controllers/cfdi_xml.py @@ -492,10 +492,22 @@ class CFDI(object): attr = mercancias mercancias = attr.pop('mercancias') + autotransporte = attr.pop('autotransporte') + identificacion = autotransporte.pop('identificacion') + seguros = autotransporte.pop('seguros') + node = ET.SubElement(node_carta, f'{prefix}:Mercancias', attr) for mercancia in mercancias: ET.SubElement(node, f'{prefix}:Mercancia', mercancia) + sub_node = ET.SubElement(node, f'{prefix}:Autotransporte', autotransporte) + ET.SubElement(sub_node, f'{prefix}:IdentificacionVehicular', identificacion) + ET.SubElement(sub_node, f'{prefix}:Seguros', seguros) + + if tiposfigura: + sub_node = ET.SubElement(node_carta, f'{prefix}:FiguraTransporte') + for figura in tiposfigura: + ET.SubElement(sub_node, f'{prefix}:TiposFigura', figura) if self._divisas: atributos = { diff --git a/source/app/models/main.py b/source/app/models/main.py index 7884aa8..95b0ccf 100644 --- a/source/app/models/main.py +++ b/source/app/models/main.py @@ -1452,6 +1452,11 @@ class SATUnidades(BaseModel): except: return None + @classmethod + def get_key_by_id(self, id): + obj = SATUnidades.get(SATUnidades.id==id) + return obj.key + @classmethod def get_(self): rows = SATUnidades.select().dicts() @@ -5321,10 +5326,16 @@ class Facturas(BaseModel): def _save_cartaporte(self, invoice, valores): if not valores: return + + values = utils.loads(valores) + mercancias = values['mercancias'] + for mercancia in mercancias['mercancias']: + mercancia['ClaveUnidad'] = SATUnidades.get_key_by_id(mercancia['ClaveUnidad']) + data = { 'factura': invoice, 'nombre': 'cartaporte', - 'valores': valores, + 'valores': utils.dumps(values), } FacturasComplementos.create(**data) return diff --git a/source/static/js/controller/invoices.js b/source/static/js/controller/invoices.js index 90121a1..f52e2dc 100644 --- a/source/static/js/controller/invoices.js +++ b/source/static/js/controller/invoices.js @@ -716,10 +716,26 @@ function guardar_y_timbrar(values){ cartaporte['TotalDistRec'] = String(total_distance) cartaporte['ubicaciones'] = ubicaciones + var row = $$('grid_carta_autotransporte').data.getRange()[0] + var autotransporte = { + PermSCT: row['PermSCT'], + NumPermisoSCT: row['NumPermisoSCT'], + identificacion: { + ConfigVehicular: row['ConfigVehicular'], + PlacaVM: row['PlacaVM'], + AnioModeloVM: row['AnioModeloVM'], + }, + seguros: { + AseguraRespCivil: row['AseguraRespCivil'], + PolizaRespCivil: row['PolizaRespCivil'], + } + } + var mercancias = $$('grid_carta_mercancias').data.getRange() mercancias.forEach(function(row, index){ delete row['id'] row['Cantidad'] = String(row['Cantidad']) + //~ row['ValorMercancia'] = String(row['ValorMercancia']) if(row['PesoEnKg']){ total_weight += parseFloat(row['PesoEnKg']) } @@ -729,9 +745,16 @@ function guardar_y_timbrar(values){ 'UnidadPeso': $$('lst_carta_UnidadPeso').getValue(), 'NumTotalMercancias': String(mercancias.length), mercancias: mercancias, + autotransporte: autotransporte, } cartaporte['mercancias'] = mercancias + var tiposfigura = $$('grid_carta_tipos_figuras').data.getRange() + tiposfigura.forEach(function(row, index){ + delete row['id'] + }) + cartaporte['tiposfigura'] = tiposfigura + data['cartaporte'] = cartaporte } @@ -2480,7 +2503,7 @@ function _tab_carta_porte(){ data['Descripcion'] = r.descripcion data['Cantidad'] = r.cantidad data['ClaveUnidad'] = r.unidad - data['ValorMercancia'] = r.importe + //~ data['ValorMercancia'] = r.importe g2.add(data) }) diff --git a/source/static/js/ui/invoices.js b/source/static/js/ui/invoices.js index c1a28a8..d16b1b5 100644 --- a/source/static/js/ui/invoices.js +++ b/source/static/js/ui/invoices.js @@ -875,7 +875,7 @@ var grid_cols_carta_mercancias = [ {id: 'Descripcion', header: 'Descripción', fillspace: 1}, {id: 'Cantidad', header: 'Cantidad', format: webix.i18n.numberFormat, css: 'right', fillspace: 1}, {id: 'ClaveUnidad', header: 'Unidad', options: 'values/unidades', fillspace: 1}, - {id: 'ValorMercancia', header: 'Valor Mercancia', format: webix.i18n.priceFormat, css: 'right', footer: 'Total peso:', fillspace: 1}, + //~ {id: 'ValorMercancia', header: 'Valor Mercancia', format: webix.i18n.priceFormat, css: 'right', footer: 'Total peso:', fillspace: 1}, {id: 'PesoEnKg', header: 'Peso (Kg)', format: webix.i18n.numberFormat, css: 'right', editor: 'text', footer: {content: 'summColumn', css: 'right'}, fillspace: 1}, ] @@ -895,6 +895,8 @@ var grid_cols_carta_autotransporte = [ {id: 'ConfigVehicular', header: 'Clave Autotransporte', editor: 'select', options: opt_config_auto, fillspace: 1}, {id: 'PlacaVM', header: 'Placa', editor: 'text', fillspace: 1}, {id: 'AnioModeloVM', header: 'Modelo (Año)', editor: 'text', fillspace: 1}, + {id: 'AseguraRespCivil', header: 'Aseguradora', editor: 'text', fillspace: 1}, + {id: 'PolizaRespCivil', header: 'Póliza', editor: 'text', fillspace: 1}, ] @@ -911,11 +913,24 @@ var grid_cols_carta_tipos_figuras = [ {id: 'id', header: 'ID', hidden: true}, {id: 'TipoFigura', header: 'Tipo Figura', editor: 'select', options: opt_tipos_figura, fillspace: 1}, {id: 'RFCFigura', header: 'RFC Figura', editor: 'text', fillspace: 1}, + {id: 'NombreFigura', header: 'Nombre Figura', editor: 'text', fillspace: 1}, {id: 'NumLicencia', header: 'Número de Licencia', editor: 'text', fillspace: 1}, ] +var data_tmp1 = [ + {delete: '-', TipoUbicacion: 'Origen', RFCRemitenteDestinatario: 'XIQB891116QE4', FechaHoraSalidaLlegada: new Date(2022, 1, 10, 12, 00)}, + {delete: '-', TipoUbicacion: 'Destino', RFCRemitenteDestinatario: 'XIQB891116QE4', FechaHoraSalidaLlegada: new Date(2022, 1, 11, 12, 00)}, +] +var data_tmp2 = [ + {id: 0, PermSCT: 'TPAF03', NumPermisoSCT: 'Transporte privado de carga', ConfigVehicular: 'C3', PlacaVM: 'YYY1234', AnioModeloVM: '2020', AseguraRespCivil: 'Compañia Aseguradora', PolizaRespCivil: '1234567890'}, +] +var data_tmp3 = [ + {id: 0, TipoFigura: '01', RFCFigura: 'XIQB891116QE4', NombreFigura: 'Homero Simpson', NumLicencia: '1234567890'}, +] + + var grid_carta_ubicaciones = { view: 'datatable', id: 'grid_carta_ubicaciones', @@ -926,10 +941,11 @@ var grid_carta_ubicaciones = { editable: true, footer: true, columns: grid_cols_carta_ubicaciones, - data: [ - {delete: '-', TipoUbicacion: 'Origen'}, - {delete: '-', TipoUbicacion: 'Destino'}, - ] + data: data_tmp1, + //~ data: [ + //~ {delete: '-', TipoUbicacion: 'Origen', }, + //~ {delete: '-', TipoUbicacion: 'Destino'}, + //~ ] } @@ -955,7 +971,8 @@ var grid_carta_autotransporte = { headermenu: true, editable: true, columns: grid_cols_carta_autotransporte, - data: [{id: 0}], + //~ data: [{id: 0}], + data: data_tmp2, } @@ -968,7 +985,8 @@ var grid_carta_tipos_figuras = { headermenu: true, editable: true, columns: grid_cols_carta_tipos_figuras, - data: [{id: 0}], + //~ data: [{id: 0}], + data: data_tmp3, } From 00c58d9b41d3acfaf9060603567ec689ccb245f3 Mon Sep 17 00:00:00 2001 From: El Mau Date: Mon, 10 Jan 2022 13:39:35 -0600 Subject: [PATCH 20/21] =?UTF-8?q?Nuevo=20m=C3=A9todo=20para=20subir=20las?= =?UTF-8?q?=20plantillas?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- source/app/controllers/utils.py | 34 ++++++++++++++++++-- source/app/models/main.py | 3 +- source/static/js/controller/admin.js | 47 ++++++++++++++++++++++++++++ source/static/js/ui/admin.js | 4 +-- source/static/js/ui/invoices.js | 47 +++++++++++++++------------- 5 files changed, 108 insertions(+), 27 deletions(-) diff --git a/source/app/controllers/utils.py b/source/app/controllers/utils.py index 0b4a6a4..8ec80f6 100644 --- a/source/app/controllers/utils.py +++ b/source/app/controllers/utils.py @@ -824,12 +824,40 @@ def _products_from_xml(rfc, data): return result -def upload_file(rfc, opt, file_obj): - if opt == 'productsadd': - result = _products_from_xml(rfc, file_obj.file.read()) +def save_file(path, data, modo='wb'): + try: + with open(path, modo) as f: + f.write(data) + return True + except: + return False + + +def _save_template(rfc, name, file_obj): + result = {'status': 'server', 'ok': False} + + ext1 = name[-3:] + ext2 = file_obj.filename.split('.')[-1].lower() + if ext1 != ext2: + msg = f'Extensión incorrecta del archivo: {ext2}' + result['error'] = msg + return result + + rfc = rfc.lower() + path = _join(PATHS['USER'], f'{rfc}{name}') + if save_file(path, file_obj.file.read()): + result['ok'] = True + return result +def upload_file(rfc, name, file_obj): + if name == 'productsadd': + return _products_from_xml(rfc, file_obj.file.read()) + + return _save_template(rfc, name, file_obj) + + def get_qr(data, kind='svg', in_base64=False): buffer = io.BytesIO() segno.make(data).save(buffer, kind=kind, scale=8, border=2) diff --git a/source/app/models/main.py b/source/app/models/main.py index 95b0ccf..76e07aa 100644 --- a/source/app/models/main.py +++ b/source/app/models/main.py @@ -129,6 +129,7 @@ class UploadFile(object): method = f'_read_{opt}' if hasattr(cls, method): return getattr(cls, method)(cls, result) + return result @@ -146,7 +147,7 @@ def upload_file(rfc, opt, file_obj): return Emisor.save_logo(file_obj) # ~ v2 - names = ('productsadd',) + names = ('productsadd', '_3.3_cp_2.0.ods') if opt in names: result = UploadFile.read(rfc, opt, file_obj) return result diff --git a/source/static/js/controller/admin.js b/source/static/js/controller/admin.js index 22b348c..c7b3a62 100644 --- a/source/static/js/controller/admin.js +++ b/source/static/js/controller/admin.js @@ -86,6 +86,7 @@ var controllers = { $$('txt_plantilla_nomina1233').attachEvent('onItemClick', txt_plantilla_nomina1233_click) $$('txt_plantilla_pagos10').attachEvent('onItemClick', txt_plantilla_pagos10_click) $$('make_pdf_from').attachEvent('onChange', opt_make_pdf_from_on_change) + $$('cmd_template_upload').attachEvent('onItemClick', cmd_template_upload_click) //~ Partners $$('chk_config_change_balance_partner').attachEvent('onItemClick', chk_config_item_click) @@ -3292,3 +3293,49 @@ function grid_carta_unidades_peso_on_check(row, column, state){ } }) } + + +function cmd_template_upload_click(e){ + + var template = $$('lst_templates_cfdi') + + if(!template.getValue()){ + msg = 'Selecciona una plantilla' + msg_error(msg) + return + } + + var body_elements = [ + {cols: [{width: 100}, {view: 'uploader', id: 'up_templates', autosend: true, link: 'lst_files', + value: 'Seleccionar archivo', upload: '/files/' + template.getValue(), + width: 200}, {width: 100}]}, + {view: 'list', id: 'lst_files', type: 'uploader', autoheight:true, + borderless: true}, + {}, + {cols: [{}, {view: 'button', label: 'Cerrar', autowidth: true, + click:("$$('win_templates').close();")}, {}]} + ] + + var w = webix.ui({ + view: 'window', + id: 'win_templates', + modal: true, + position: 'center', + head: template.getText(), + body: { + view: 'form', + elements: body_elements, + } + }) + + w.show() + + $$('up_templates').attachEvent('onUploadComplete', function(response){ + if(response.ok){ + msg_ok('Plantilla cargada correctamente') + }else{ + $$("lst_files").clearAll() + msg_error(response.error) + } + }) +} diff --git a/source/static/js/ui/admin.js b/source/static/js/ui/admin.js index d178033..74422d4 100644 --- a/source/static/js/ui/admin.js +++ b/source/static/js/ui/admin.js @@ -595,7 +595,7 @@ var type_make_pdf = [ var opt_templates_cfdi = [ - {id: 'template_3.3_cp_2.0', value: 'CFDI v3.3 - Carta Porte 2.0'}, + {id: '_3.3_cp_2.0.ods', value: 'CFDI v3.3 - Carta Porte 2.0'}, ] @@ -607,7 +607,7 @@ var options_templates = [ {view: 'button', id: 'cmd_template_upload', type: 'iconButton', icon: 'file', width: 35}, {}, {maxWidth: 20} ]}, - {maxHeight: 25}, + {maxHeight: 50}, {cols: [{maxWidth: 20}, {view: 'search', id: 'txt_plantilla_factura_32', name: 'plantilla_factura_32', label: 'Plantilla Factura v3.2 (ODS): ', labelPosition: 'top', diff --git a/source/static/js/ui/invoices.js b/source/static/js/ui/invoices.js index d16b1b5..69c961f 100644 --- a/source/static/js/ui/invoices.js +++ b/source/static/js/ui/invoices.js @@ -859,14 +859,31 @@ var date_suggest = { } +var opt_countries = [ + {id: 'MXN', value: 'México'}, +] + + var grid_cols_carta_ubicaciones = [ {id: 'id', header: 'ID', hidden: true}, {id: 'delete', header: '', hidden: true, width: 30, css: 'delete'}, {id: 'TipoUbicacion', header: 'Tipo de Ubicación', editor: 'select', options: opt_origen_destino, fillspace: 1}, {id: 'RFCRemitenteDestinatario', header: 'RFC Rem/Des', editor: 'text', fillspace: 1}, + {id: 'NombreRemitenteDestinatario', header: 'Nombre Rem/Des', editor: 'text', fillspace: 1}, {id: 'FechaHoraSalidaLlegada', header: 'Fecha/Hora', editor: 'date', suggest: date_suggest, format: webix.Date.dateToStr("%D, %d-%M-%Y %h:%i"), footer: 'Total distancia:', fillspace: 1}, {id: 'DistanciaRecorrida', header: 'Distancia (KM)', editor: 'text', css: 'right', footer: {content: 'summColumn', css: 'right'}, fillspace: 1}, + {id: 'Municipio', headerd: 'Municipio', editor: 'text', fillspace: 1}, + {id: 'Estado', headerd: 'Estado', editor: 'text', fillspace: 1}, + {id: 'Pais', headerd: 'Pais', editor: 'select', options: opt_countries, fillspace: 1}, + {id: 'CodigoPostal', headerd: 'C.P.', editor: 'text', fillspace: 1}, ] +//~ Calle +//~ NumeroExterior +//~ NumeroInterior +//~ Colonia +//~ Localidad +//~ Referencia + var grid_cols_carta_mercancias = [ @@ -919,18 +936,6 @@ var grid_cols_carta_tipos_figuras = [ ] -var data_tmp1 = [ - {delete: '-', TipoUbicacion: 'Origen', RFCRemitenteDestinatario: 'XIQB891116QE4', FechaHoraSalidaLlegada: new Date(2022, 1, 10, 12, 00)}, - {delete: '-', TipoUbicacion: 'Destino', RFCRemitenteDestinatario: 'XIQB891116QE4', FechaHoraSalidaLlegada: new Date(2022, 1, 11, 12, 00)}, -] -var data_tmp2 = [ - {id: 0, PermSCT: 'TPAF03', NumPermisoSCT: 'Transporte privado de carga', ConfigVehicular: 'C3', PlacaVM: 'YYY1234', AnioModeloVM: '2020', AseguraRespCivil: 'Compañia Aseguradora', PolizaRespCivil: '1234567890'}, -] -var data_tmp3 = [ - {id: 0, TipoFigura: '01', RFCFigura: 'XIQB891116QE4', NombreFigura: 'Homero Simpson', NumLicencia: '1234567890'}, -] - - var grid_carta_ubicaciones = { view: 'datatable', id: 'grid_carta_ubicaciones', @@ -941,11 +946,11 @@ var grid_carta_ubicaciones = { editable: true, footer: true, columns: grid_cols_carta_ubicaciones, - data: data_tmp1, - //~ data: [ - //~ {delete: '-', TipoUbicacion: 'Origen', }, - //~ {delete: '-', TipoUbicacion: 'Destino'}, - //~ ] + //~ data: data_tmp1, + data: [ + {delete: '-', TipoUbicacion: 'Origen', Pais: 'MXN'}, + {delete: '-', TipoUbicacion: 'Destino', Pais: 'MXN'}, + ] } @@ -971,8 +976,8 @@ var grid_carta_autotransporte = { headermenu: true, editable: true, columns: grid_cols_carta_autotransporte, - //~ data: [{id: 0}], - data: data_tmp2, + data: [{id: 0}], + //~ data: data_tmp2, } @@ -985,8 +990,8 @@ var grid_carta_tipos_figuras = { headermenu: true, editable: true, columns: grid_cols_carta_tipos_figuras, - //~ data: [{id: 0}], - data: data_tmp3, + data: [{id: 0}], + //~ data: data_tmp3, } From 8bbd2ba62bcb84849693ed936b29ead0a843db6f Mon Sep 17 00:00:00 2001 From: El Mau Date: Mon, 10 Jan 2022 14:10:17 -0600 Subject: [PATCH 21/21] Actualizar lista de cambios --- CHANGELOG.md | 7 +++++-- source/static/js/controller/admin.js | 1 + 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 341e3ee..22a8be0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,12 +1,15 @@ -v 1.44.0 [00-Dic-2021] +v 1.44.0 [10-Ene-2022] ---------------------- - - Soporte para Carta Porte v2 + - Soporte para Carta Porte v2 con CFDI 3.3 + - Soporte para el nuevo esquema de cancelación del SAT * IMPORTANTE: Es necesario hacer una migración: ``` +cd /opt/empresa-libre + git pull origin master cd source/app/models diff --git a/source/static/js/controller/admin.js b/source/static/js/controller/admin.js index c7b3a62..164ead8 100644 --- a/source/static/js/controller/admin.js +++ b/source/static/js/controller/admin.js @@ -3332,6 +3332,7 @@ function cmd_template_upload_click(e){ $$('up_templates').attachEvent('onUploadComplete', function(response){ if(response.ok){ + w.close() msg_ok('Plantilla cargada correctamente') }else{ $$("lst_files").clearAll()