diff --git a/CHANGELOG.md b/CHANGELOG.md index 430daaa..1a38f42 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +v 2.2.0 [24-Ene-2024] + - Mejora: Soporte para complemento Comercio Exterior 2.0 + - **IMPORTANTE**: Aunque no lo uses, esto afecta al JS de facturación, por + lo que tienes que forzar el refresco (CTRL+F5) si tienes algún problema. + + v 2.1.0 [26-Dic-2023] - Mejora: Se agrega filtro por día en facturas. diff --git a/VERSION b/VERSION index 7ec1d6d..ccbccc3 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -2.1.0 +2.2.0 diff --git a/source/app/controllers/util.py b/source/app/controllers/util.py index 50d5405..f1cfb29 100644 --- a/source/app/controllers/util.py +++ b/source/app/controllers/util.py @@ -1022,6 +1022,51 @@ class LIBO(object): return + def _comercio_exterior(self, data): + if not data: + return + + emisor = data.pop('emisor') + receptor = data.pop('receptor') + mercancias = data.pop('mercancias') + + for k, v in data.items(): + self._set_cell(f'{{cce.{k}}}', v) + for k, v in emisor.items(): + self._set_cell(f'{{cce.emisor.{k}}}', v) + for k, v in receptor.items(): + self._set_cell(f'{{cce.receptor.{k}}}', v) + + first = True + count = len(mercancias) - 1 + for i, mercancia in enumerate(mercancias): + no_identificacion = mercancia['NoIdentificacion'] + fraccion = mercancia['FraccionArancelaria'] + unidad = mercancia['UnidadAduana'] + cantidad = mercancia['CantidadAduana'] + valor_unitario = mercancia['ValorUnitarioAduana'] + valor_dolares = mercancia['ValorDolares'] + if first: + first = False + cell_1 = self._set_cell('{cce.mercancia.noidentificacion}', no_identificacion) + cell_2 = self._set_cell('{cce.mercancia.fraccionarancelaria}', fraccion) + cell_3 = self._set_cell('{cce.mercancia.unidadaduana}', unidad) + cell_4 = self._set_cell('{cce.mercancia.cantidadaduana}', cantidad) + cell_5 = self._set_cell('{cce.mercancia.valorunitarioaduana}', valor_unitario) + cell_6 = self._set_cell('{cce.mercancia.valordolares}', valor_dolares) + if count > 0: + row = cell_1.CellAddress.Row + 1 + self._sheet.getRows().insertByIndex(row, count) + self._copy_paste_rows(cell_1, count) + else: + cell_1 = self._set_cell(v=no_identificacion, cell=cell_1) + cell_2 = self._set_cell(v=fraccion, cell=cell_2) + cell_3 = self._set_cell(v=unidad, cell=cell_3) + cell_4 = self._set_cell(v=cantidad, cell=cell_4) + cell_5 = self._set_cell(v=valor_unitario, cell=cell_5) + cell_6 = self._set_cell(v=valor_dolares, cell=cell_6) + return + def _nomina(self, data): if not data: return @@ -1258,6 +1303,7 @@ class LIBO(object): self._divisas(data.get('divisas', {})) self._leyendas(data.get('leyendas', '')) self._carta_porte(data.get('carta_porte', {})) + self._comercio_exterior(data.get('comercio_exterior', {})) self._timbre(data['timbre']) @@ -1655,8 +1701,13 @@ def to_pdf(data, emisor_rfc, ods=False, pdf_from='1'): default = f'plantilla_donatarias_{version}_{version_donatarias}.ods' version = f'{version}_cd_{version_donatarias}' + if 'comercio_exterior' in data: + version_cce = data['comercio_exterior']['version'] + default = f'plantilla_cce_{version}_{version_cce}.ods' + version = f'{version}_cce_{version_cce}' + template_name = f'{rfc.lower()}_{version}.ods' - # ~ print('T', template_name, default) + # ~ print('\nT', template_name, default) if APP_LIBO: app = LIBO() @@ -2370,6 +2421,7 @@ def upload_file(rfc, opt, file_obj): '_4.0_cp_2.0.ods', '_4.0_ccp_2.0.ods', '_4.0_cd_1.1.ods', + '_4.0_cce_2.0.ods', '_4.0.json', ) if opt in versions: diff --git a/source/app/controllers/utils.py b/source/app/controllers/utils.py index fbad39a..313a788 100644 --- a/source/app/controllers/utils.py +++ b/source/app/controllers/utils.py @@ -265,6 +265,7 @@ class CfdiToDict(object): 'leyendasFisc': 'http://www.sat.gob.mx/leyendasFiscales', 'cartaporte20': 'http://www.sat.gob.mx/CartaPorte20', 'nomina12': 'http://www.sat.gob.mx/nomina12', + 'cce20': 'http://www.sat.gob.mx/ComercioExterior20', } tipo_figura = { '01': '[01] Operador', @@ -513,6 +514,29 @@ class CfdiToDict(object): self._values['carta_porte'] = values + self._complemento_comercio_exterior(complemento) + + return + + def _complemento_comercio_exterior(self, complemento): + path = '//cce20:ComercioExterior' + comercio_exterior = complemento.xpath(path, namespaces=self.NS) + + if comercio_exterior: + values = CaseInsensitiveDict(comercio_exterior[0].attrib) + + for node in comercio_exterior[0]: + if 'Emisor' in node.tag: + values['emisor'] = CaseInsensitiveDict(node.attrib) + values['emisor'].update(CaseInsensitiveDict(node[0].attrib)) + elif 'Receptor' in node.tag: + values['receptor'] = CaseInsensitiveDict(node.attrib) + values['receptor'].update(CaseInsensitiveDict(node[0].attrib)) + elif 'Mercancias' in node.tag: + mercancias = [ + CaseInsensitiveDict(m.attrib) for m in node] + values['mercancias'] = mercancias + self._values['comercio_exterior'] = values return diff --git a/source/app/settings.py b/source/app/settings.py index 669bed2..a2458d9 100644 --- a/source/app/settings.py +++ b/source/app/settings.py @@ -39,7 +39,7 @@ except ImportError: DEBUG = DEBUG -VERSION = '2.1.0' +VERSION = '2.2.0' EMAIL_SUPPORT = ('soporte@empresalibre.mx',) TITLE_APP = '{} v{}'.format(TITLE_APP, VERSION) diff --git a/source/static/js/ui/admin.js b/source/static/js/ui/admin.js index 68d6c22..3c3f04c 100644 --- a/source/static/js/ui/admin.js +++ b/source/static/js/ui/admin.js @@ -605,6 +605,7 @@ var opt_templates_cfdi = [ {id: '_4.0_cp_2.0.ods', value: 'CFDI v4.0 - Pagos v2.0'}, {id: '_4.0_ccp_2.0.ods', value: 'CFDI v4.0 - Carta Porte v2.0'}, {id: '_4.0_cd_1.1.ods', value: 'CFDI v4.0 - Donativos v1.1'}, + {id: '_4.0_cce_2.0.ods', value: 'CFDI v4.0 - Comercio Exterior v2.0'}, {id: '_4.0.json', value: 'CFDI v4.0 - JSON'}, {id: '_3.3.ods', value: 'CFDI v3.3'}, {id: '_3.3_cn_1.2.ods', value: 'CFDI v3.3 - Nómina v1.2'}, diff --git a/source/templates/plantilla_cce_4.0_2.0.ods b/source/templates/plantilla_cce_4.0_2.0.ods new file mode 100644 index 0000000..a71d80a Binary files /dev/null and b/source/templates/plantilla_cce_4.0_2.0.ods differ