From eee386a97b06c57739f36fadddc242a15b073523 Mon Sep 17 00:00:00 2001 From: El Mau Date: Thu, 17 Mar 2022 21:22:27 -0600 Subject: [PATCH] Soporte basico para complemento Comercio Exterior --- CHANGELOG.md | 5 ++ VERSION | 3 +- source/app/controllers/cfdi_xml.py | 65 +++++++++++++++---------- source/app/models/main.py | 20 +++++++- source/app/settings.py | 2 +- source/static/js/controller/admin.js | 1 + source/static/js/controller/invoices.js | 60 ++++++++++++++++++++++- source/static/js/ui/admin.js | 5 ++ source/static/js/ui/invoices.js | 53 ++++++++++++++++++++ 9 files changed, 184 insertions(+), 30 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2bfb4a8..7820911 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +v 1.47.0 [17-Mar-2022] +---------------------- + - Mejora: Soporte basico para complemento Comercio Exterior. + + v 1.46.5 [10-Mar-2022] ---------------------- - Error: Al timbrar nómina con Comercio Digital diff --git a/VERSION b/VERSION index ac2d35e..21998d3 100644 --- a/VERSION +++ b/VERSION @@ -1,2 +1 @@ -1.46.5 - +1.47.0 diff --git a/source/app/controllers/cfdi_xml.py b/source/app/controllers/cfdi_xml.py index d3264c7..edb485f 100644 --- a/source/app/controllers/cfdi_xml.py +++ b/source/app/controllers/cfdi_xml.py @@ -102,6 +102,12 @@ SAT = { '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', + }, + 'comercioe': { + 'version': '1.1', + 'prefix': 'cce11', + 'xmlns': 'http://www.sat.gob.mx/ComercioExterior11', + 'schema': ' http://www.sat.gob.mx/ComercioExterior11 http://www.sat.gob.mx/sitio_internet/cfd/ComercioExterior11/ComercioExterior11.xsd', } } @@ -174,6 +180,7 @@ class CFDI(object): 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._comercio_exterior = bool(datos['complementos'].get('comercioe', False)) self._divisas = datos['comprobante'].pop('divisas', '') @@ -245,10 +252,16 @@ class CFDI(object): attributes[name] = SAT['cartaporte']['xmlns'] schema_carta_porte = SAT['cartaporte']['schema'] + schema_comercioe = '' + if self._comercio_exterior: + name = 'xmlns:{}'.format(SAT['comercioe']['prefix']) + attributes[name] = SAT['comercioe']['xmlns'] + schema_carta_porte = SAT['comercioe']['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_carta_porte + schema_carta_porte + schema_comercioe attributes.update(datos) if not 'Version' in attributes: @@ -556,57 +569,59 @@ class CFDI(object): for leyend in datos['leyendas']: ET.SubElement(node_leyend, '{}:Leyenda'.format(pre), leyend) - if 'ce' in datos: - pre = 'cce11' - datos = datos.pop('ce') + if self._comercio_exterior: + prefix = SAT['comercioe']['prefix'] + datos = datos.pop('comercioe') emisor = datos.pop('emisor') - propietario = datos.pop('propietario') + # ~ propietario = datos.pop('propietario') receptor = datos.pop('receptor') destinatario = datos.pop('destinatario') - conceptos = datos.pop('conceptos') + conceptos = datos.pop('mercancias') - attributes = {} - attributes['xmlns:{}'.format(pre)] = \ - 'http://www.sat.gob.mx/ComercioExterior11' - attributes['xsi:schemaLocation'] = \ - 'http://www.sat.gob.mx/ComercioExterior11 ' \ - 'http://www.sat.gob.mx/sitio_internet/cfd/ComercioExterior11/ComercioExterior11.xsd' - attributes.update(datos) + # ~ attributes = {} + # ~ attributes['xmlns:{}'.format(pre)] = \ + # ~ 'http://www.sat.gob.mx/ComercioExterior11' + # ~ attributes['xsi:schemaLocation'] = \ + # ~ 'http://www.sat.gob.mx/ComercioExterior11 ' \ + # ~ 'http://www.sat.gob.mx/sitio_internet/cfd/ComercioExterior11/ComercioExterior11.xsd' + + attr = {'Version': SAT['comercioe']['version']} + attr.update(datos) ce = ET.SubElement( - complemento, '{}:ComercioExterior'.format(pre), attributes) + self._complemento, f'{prefix}:ComercioExterior', attr) attributes = {} if 'Curp' in emisor: attributes = {'Curp': emisor.pop('Curp')} - node = ET.SubElement(ce, '{}:Emisor'.format(pre), attributes) - ET.SubElement(node, '{}:Domicilio'.format(pre), emisor) + node = ET.SubElement(ce, '{}:Emisor'.format(prefix), attributes) + ET.SubElement(node, '{}:Domicilio'.format(prefix), emisor) - if propietario: - ET.SubElement(ce, '{}:Propietario'.format(pre), propietario) + # ~ if propietario: + # ~ ET.SubElement(ce, '{}:Propietario'.format(prefix), propietario) attributes = {} if 'NumRegIdTrib' in receptor: attributes = {'NumRegIdTrib': receptor.pop('NumRegIdTrib')} - node = ET.SubElement(ce, '{}:Receptor'.format(pre), attributes) - ET.SubElement(node, '{}:Domicilio'.format(pre), receptor) + node = ET.SubElement(ce, '{}:Receptor'.format(prefix), attributes) + ET.SubElement(node, '{}:Domicilio'.format(prefix), receptor) attributes = {} if 'NumRegIdTrib' in destinatario: attributes = {'NumRegIdTrib': destinatario.pop('NumRegIdTrib')} if 'Nombre' in destinatario: attributes.update({'Nombre': destinatario.pop('Nombre')}) - node = ET.SubElement(ce, '{}:Destinatario'.format(pre), attributes) - ET.SubElement(node, '{}:Domicilio'.format(pre), destinatario) + node = ET.SubElement(ce, '{}:Destinatario'.format(prefix), attributes) + ET.SubElement(node, '{}:Domicilio'.format(prefix), destinatario) - node = ET.SubElement(ce, '{}:Mercancias'.format(pre)) + node = ET.SubElement(ce, '{}:Mercancias'.format(prefix)) fields = ('Marca', 'Modelo', 'SubModelo', 'NumeroSerie') for row in conceptos: detalle = {} for f in fields: if f in row: detalle[f] = row.pop(f) - concepto = ET.SubElement(node, '{}:Mercancia'.format(pre), row) + concepto = ET.SubElement(node, '{}:Mercancia'.format(prefix), row) if detalle: ET.SubElement( - concepto, '{}:DescripcionesEspecificas'.format(pre), detalle) + concepto, '{}:DescripcionesEspecificas'.format(prefix), detalle) return diff --git a/source/app/models/main.py b/source/app/models/main.py index bd47931..afd8add 100644 --- a/source/app/models/main.py +++ b/source/app/models/main.py @@ -344,6 +344,7 @@ def config_timbrar(): '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_comercioe': Configuracion.get_bool('chk_config_comercio_exterior'), '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'), @@ -482,6 +483,7 @@ class Configuracion(BaseModel): 'chk_config_ine', 'chk_config_edu', 'chk_config_carta_porte', + 'chk_config_comercio_exterior', 'chk_config_pagos', 'chk_config_divisas', 'chk_cfg_pays_data_bank', @@ -5398,6 +5400,21 @@ class Facturas(BaseModel): FacturasComplementos.create(**data) return + def _save_comercioe(self, invoice, valores): + if not valores: + return + + # ~ values = utils.loads(valores) + + data = { + 'factura': invoice, + 'nombre': 'comercioe', + # ~ 'valores': utils.dumps(values), + 'valores': valores, + } + FacturasComplementos.create(**data) + return + def _get_serie(self, user, default_serie): if user.sucursal is None: return default_serie @@ -5410,6 +5427,7 @@ class Facturas(BaseModel): relacionados = util.loads(values.pop('relacionados')) ine = values.pop('ine', {}) cartaporte = values.pop('cartaporte', {}) + comercioe = values.pop('comercioe', {}) tipo_comprobante = values['tipo_comprobante'] folio_custom = values.pop('folio_custom', '') divisas = values.pop('divisas', '') @@ -5453,6 +5471,7 @@ class Facturas(BaseModel): cls._guardar_relacionados(cls, obj, relacionados) cls._guardar_ine(cls, obj, ine) cls._save_cartaporte(cls, obj, cartaporte) + cls._save_comercioe(cls, obj, comercioe) cls._save_leyendas_fiscales(cls, obj, leyendas_fiscales) obj.subtotal = totals['subtotal'] obj.descuento = totals['descuento'] @@ -11480,4 +11499,3 @@ if __name__ == '__main__': args = _process_command_line_arguments() main(args) - diff --git a/source/app/settings.py b/source/app/settings.py index e97cec6..00624bc 100644 --- a/source/app/settings.py +++ b/source/app/settings.py @@ -42,7 +42,7 @@ except ImportError: DEBUG = DEBUG -VERSION = '1.46.5' +VERSION = '1.47.0' EMAIL_SUPPORT = ('soporte@empresalibre.mx',) TITLE_APP = '{} v{}'.format(TITLE_APP, VERSION) diff --git a/source/static/js/controller/admin.js b/source/static/js/controller/admin.js index 164ead8..0016d98 100644 --- a/source/static/js/controller/admin.js +++ b/source/static/js/controller/admin.js @@ -115,6 +115,7 @@ var controllers = { $$('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_comercio_exterior').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) diff --git a/source/static/js/controller/invoices.js b/source/static/js/controller/invoices.js index e5026ea..e060d42 100644 --- a/source/static/js/controller/invoices.js +++ b/source/static/js/controller/invoices.js @@ -22,6 +22,7 @@ var tipo_relacion = '' var anticipo = false var donativo = false var cfg_invoice = new Object() +var values_comercioe = null function init_config_invoices(){ @@ -94,6 +95,7 @@ var invoices_controllers = { $$('cmd_carta_add_product').attachEvent('onItemClick', cmd_carta_add_product_click) $$('cmd_carta_copy_from_invoice').attachEvent('onItemClick', cmd_carta_copy_from_invoice_click) $$('cmd_carta_import_json').attachEvent('onItemClick', cmd_carta_import_json_click) + $$('cmd_import_json_comercioe').attachEvent('onItemClick', cmd_import_json_comercioe_click) webix.extend($$('grid_invoices'), webix.ProgressBar) @@ -224,12 +226,17 @@ function default_config(){ if(!values.cfdi_carta_porte){ $$('tv_invoice').getTabbar().hideOption('Carta Porte') }else{ - get_leyendas_fiscales() $$('tv_invoice').getTabbar().showOption('Carta Porte') } + if(!values.cfdi_comercioe){ + $$('tv_invoice').getTabbar().hideOption('Comercio Exterior') + }else{ + $$('tv_invoice').getTabbar().showOption('Comercio Exterior') + } cfg_invoice['leyendasfiscales'] = values.cfdi_leyendasfiscales cfg_invoice['edu'] = values.cfdi_edu cfg_invoice['carta_porte'] = values.cfdi_carta_porte + cfg_invoice['comercioe'] = values.cfdi_comercioe cfg_invoice['open_pdf'] = values.cfdi_open_pdf cfg_invoice['tax_locales'] = values.cfdi_tax_locales cfg_invoice['tax_decimals'] = values.cfdi_tax_decimals @@ -769,10 +776,18 @@ function guardar_y_timbrar(values){ data['cartaporte'] = cartaporte } + var usar_comercioe = $$('chk_cfdi_usar_comercioe').getValue() + if(usar_comercioe){ + data['comercioe'] = values_comercioe + } + if(!save_invoice(data)){ return } + values_comercioe = null + $$('chk_cfdi_usar_comercioe').setValue(false) + table_relaciones.clear() tipo_relacion = '' anticipo = false @@ -816,11 +831,21 @@ function cmd_timbrar_click(id, e, node){ } msg += '

' } + usar_ine = $$('chk_cfdi_usar_ine').getValue() if(usar_ine){ msg += 'Estas usando el complemento INE

' } + if($$('chk_cfdi_usar_comercioe').getValue()){ + msg += 'Estas usando el complemento Comercio Exterior

' + if(values_comercioe === null){ + msg = 'El complemento de Comercio Exterior esta vacío' + msg_error(msg) + return + } + } + if(tipo_comprobante == 'T'){ msg += 'El Tipo de Comprobante es Traslado, el total será puesto a 0 (Cero), asegurate de que sea el tipo de comprobante correcto

' } @@ -2815,3 +2840,36 @@ function up_invoice_json_on_after_file_add(obj){ } $$('win_carta_import_json').close() } + + +function _set_from_json_comercioe(data){ + try{ + values_comercioe = JSON.parse(data) + }catch(e){ + msg_error('Revisa el archivo JSON') + webix.alert({ + title: 'Error al cargar JSON', + text: e.message, + type: 'alert-error' + }) + return + } + msg = 'Valores cargados correctamente' + msg_ok(msg) +} + + +function cmd_import_json_comercioe_click(){ + win_import_json_comercioe.init() + $$('win_import_json_comercioe').show() +} + + +function up_invoice_json_comercioe_on_after_file_add(obj){ + let reader = new FileReader() + reader.readAsText(obj.file) + reader.onload = function(){ + _set_from_json_comercioe(reader.result) + } + $$('win_import_json_comercioe').close() +} diff --git a/source/static/js/ui/admin.js b/source/static/js/ui/admin.js index 4b54347..e9f1e35 100644 --- a/source/static/js/ui/admin.js +++ b/source/static/js/ui/admin.js @@ -887,6 +887,11 @@ var options_admin_complements = [ {view: 'checkbox', id: 'chk_config_carta_porte', labelWidth: 0, labelRight: 'Usar el complemento Carta Porte'}, {maxWidth: 15}]}, + {template: 'Complemento para Comercio Exterior', type: 'section'}, + {cols: [{maxWidth: 15}, + {view: 'checkbox', id: 'chk_config_comercio_exterior', labelWidth: 0, + labelRight: 'Usar el complemento Comercio Exterior'}, + {maxWidth: 15}]}, ] diff --git a/source/static/js/ui/invoices.js b/source/static/js/ui/invoices.js index 4e60fd2..4e488f2 100644 --- a/source/static/js/ui/invoices.js +++ b/source/static/js/ui/invoices.js @@ -1198,6 +1198,16 @@ var controls_carta_porte = [ ] +var controls_comercio_exterior = [ + {cols: [{maxWidth: 15}, + {view: 'checkbox', id: 'chk_cfdi_usar_comercioe', labelWidth: 0, + labelRight: 'Usar el complemento Comercio Exterior'}, {}, + {view: 'button', id: 'cmd_import_json_comercioe', label: 'Importar JSON', + icon: 'upload', type: 'iconButton', autowidth: true, align: 'center'}, + {maxWidth: 15}]} +] + + var form_carta_porte = { type: 'space', responsive: true, @@ -1211,6 +1221,19 @@ var form_carta_porte = { } +var form_comercio_exterior = { + type: 'space', + responsive: true, + cols: [{ + view: 'form', + id: 'form_comercio_exterior', + complexData: true, + scroll: true, + elements: controls_comercio_exterior, + }] +} + + var controls_invoices = [ { view: 'tabview', @@ -1222,6 +1245,7 @@ var controls_invoices = [ {id: 'INE', rows: controls_ine}, {id: 'Leyendas Fiscales', rows: controls_leyendas_fiscales}, {id: 'Carta Porte', rows: [form_carta_porte]}, + {id: 'Comercio Exterior', rows: [form_comercio_exterior]}, ] }, ] @@ -1368,3 +1392,32 @@ var win_carta_import_json = { $$('up_invoice_json').attachEvent('onAfterFileAdd', up_invoice_json_on_after_file_add) } } + + +var body_upload_json_comercioe = {rows: [ + {view: 'form', id: 'form_upload_comercioe', rows: [ + {cols: [{}, + {view: 'uploader', id: 'up_invoice_json', autosend: false, + link: 'lst_upload_invoice', value: 'Seleccionar Archivo', + accept:'application/json', upload: ''}, {}]}, + {cols: [ + {view: 'list', id: 'lst_upload_invoice', name: 'lst_upload_invoice', + type: 'uploader', autoheight: true, borderless: true}]}, + ]}, +]} + + +var win_import_json_comercioe = { + init: function(){ + webix.ui({ + view: 'window', + id: 'win_import_json_comercioe', + width: 400, + modal: true, + position: 'center', + head: 'Importar Comerio Exterior JSON', + body: body_upload_json_comercioe, + }) + $$('up_invoice_json').attachEvent('onAfterFileAdd', up_invoice_json_comercioe_on_after_file_add) + } +}