diff --git a/source/app/controllers/util.py b/source/app/controllers/util.py index 67426ac..f21dc87 100644 --- a/source/app/controllers/util.py +++ b/source/app/controllers/util.py @@ -320,6 +320,9 @@ def get_template_ods(name, default='plantilla_factura.ods'): if is_file(path): return path + if 'pagos' in name: + default='plantilla_pagos.ods' + path = _join(PATH_TEMPLATES, default) if is_file(path): return path @@ -954,6 +957,9 @@ class LIBO(object): self._set_cell('{total_cantidades}', str(self._total_cantidades)) + if self._pagos: + return + cell_title = self._set_cell('{subtotal.titulo}', 'SubTotal') value = data['subtotal'] cell_value = self._set_cell('{subtotal}', value, value=True) @@ -1150,16 +1156,114 @@ class LIBO(object): return + def _cfdipays(self, data): + related = data.pop('related', []) + for k, v in data.items(): + if k.lower() in ('monto',): + self._set_cell('{pago.%s}' % k, v, value=True) + else: + self._set_cell('{pago.%s}' % k, v) + + col1 = [] + col2 = [] + col3 = [] + col4 = [] + col5 = [] + col6 = [] + col7 = [] + col8 = [] + col9 = [] + count = len(related) + for i, doc in enumerate(related): + uuid = doc['IdDocumento'].upper() + serie = doc['Serie'] + folio = doc['Folio'] + metodo_pago = doc['MetodoDePagoDR'] + moneda = doc['MonedaDR'] + parcialidad = doc['NumParcialidad'] + saldo_anterior = doc['ImpSaldoAnt'] + importe_pagado = doc['ImpPagado'] + saldo_insoluto = doc['ImpSaldoInsoluto'] + if i == 0: + cell_1 = self._set_cell('{doc.uuid}', uuid) + cell_2 = self._set_cell('{doc.serie}', serie) + cell_3 = self._set_cell('{doc.folio}', folio) + cell_4 = self._set_cell('{doc.metodopago}', metodo_pago) + cell_5 = self._set_cell('{doc.moneda}', moneda) + cell_6 = self._set_cell('{doc.parcialidad}', parcialidad) + cell_7 = self._set_cell('{doc.saldoanterior}', saldo_anterior, value=True) + cell_8 = self._set_cell('{doc.importepagado}', importe_pagado, value=True) + cell_9 = self._set_cell('{doc.saldoinsoluto}', saldo_insoluto, value=True) + else: + col1.append((uuid,)) + col2.append((serie,)) + col3.append((folio,)) + col4.append((metodo_pago,)) + col5.append((moneda,)) + col6.append((parcialidad,)) + col7.append((float(saldo_anterior),)) + col8.append((float(importe_pagado),)) + col9.append((float(saldo_insoluto),)) + + if count == 1: + return + + count -= 1 + row1 = cell_1.getCellAddress().Row + 1 + row2 = row1 + count - 1 + self._sheet.getRows().insertByIndex(row1, count) + self._copy_paste_rows(cell_1, count) + + # ~ style_7 = self._get_style(cell_7) + # ~ style_8 = self._get_style(cell_8) + # ~ style_9 = self._get_style(cell_9) + + col = cell_1.getCellAddress().Column + target1 = self._sheet.getCellRangeByPosition(col, row1, col, row2) + col = cell_2.getCellAddress().Column + target2 = self._sheet.getCellRangeByPosition(col, row1, col, row2) + col = cell_3.getCellAddress().Column + target3 = self._sheet.getCellRangeByPosition(col, row1, col, row2) + col = cell_4.getCellAddress().Column + target4 = self._sheet.getCellRangeByPosition(col, row1, col, row2) + col = cell_5.getCellAddress().Column + target5 = self._sheet.getCellRangeByPosition(col, row1, col, row2) + col = cell_6.getCellAddress().Column + target6 = self._sheet.getCellRangeByPosition(col, row1, col, row2) + col = cell_7.getCellAddress().Column + target7 = self._sheet.getCellRangeByPosition(col, row1, col, row2) + col = cell_8.getCellAddress().Column + target8 = self._sheet.getCellRangeByPosition(col, row1, col, row2) + col = cell_9.getCellAddress().Column + target9 = self._sheet.getCellRangeByPosition(col, row1, col, row2) + + target1.setFormulaArray(tuple(col1)) + target2.setDataArray(tuple(col2)) + target3.setFormulaArray(tuple(col3)) + target4.setDataArray(tuple(col4)) + target5.setDataArray(tuple(col5)) + target6.setDataArray(tuple(col6)) + target7.setDataArray(tuple(col7)) + target8.setDataArray(tuple(col8)) + target9.setDataArray(tuple(col9)) + + return + def _render(self, data): self._set_search() self._es_pre = data.pop('es_pre', False) self._is_ticket = data.pop('is_ticket', False) self._currency = data['totales']['moneda'] + self._pagos = data.pop('pagos', False) self._comprobante(data['comprobante']) self._emisor(data['emisor']) self._receptor(data['receptor']) self._conceptos(data['conceptos']) + + if self._pagos: + self._cfdipays(data['pays']) + if 'nomina' in data and data['nomina']: self._nomina(data['nomina']) else: @@ -1167,6 +1271,7 @@ class LIBO(object): self._timbre(data['timbre']) self._donataria(data['donataria']) self._ine(data['ine']) + self._cancelado(data['cancelada']) self._clean() return @@ -1444,13 +1549,17 @@ def to_pdf(data, emisor_rfc, ods=False): if 'nomina' in data and data['nomina']: version = '{}_{}'.format(data['nomina']['version'], version) + pagos = '' + if data.get('pagos', False): + pagos = '_pagos_' + if APP_LIBO: app = LIBO() if app.is_running: donativo = '' if data['donativo']: donativo = '_donativo' - name = '{}_{}{}.ods'.format(rfc.lower(), version, donativo) + name = '{}_{}{}{}.ods'.format(rfc.lower(), pagos, version, donativo) path = get_template_ods(name) if path: return app.pdf(path, data, ods) @@ -1553,6 +1662,7 @@ def _comprobante(doc, options): 'I': 'ingreso', 'E': 'egreso', 'T': 'traslado', + 'P': 'pago', } data['tipodecomprobante'] = tipos.get(data['tipodecomprobante']) data['lugarexpedicion'] = \ @@ -1854,6 +1964,27 @@ def _nomina(doc, data, values, version_cfdi): return info +def _cfdipays(doc, data, version): + node = doc.find('{}Complemento/{}Pagos'.format(PRE[version], PRE['pagos'])) + if node is None: + return {} + + info = CaseInsensitiveDict(node.attrib.copy()) + related = [] + for n1 in node: + info.update(CaseInsensitiveDict(n1.attrib.copy())) + for n2 in n1: + related.append(CaseInsensitiveDict(n2.attrib.copy())) + + info['related'] = related + + data['comprobante']['totalenletras'] = to_letters( + float(info['monto']), info['monedap']) + data['comprobante']['moneda'] = info['monedap'] + + return info + + def get_data_from_xml(invoice, values): data = {'cancelada': invoice.cancelada, 'donativo': False} if hasattr(invoice, 'donativo'): @@ -1878,6 +2009,9 @@ def get_data_from_xml(invoice, values): data['comprobante'].update(data['timbre']) data['nomina'] = _nomina(doc, data, values, version) + data['pagos'] = values.get('pagos', False) + if data['pagos']: + data['pays'] = _cfdipays(doc, data, version) return data diff --git a/source/app/models/main.py b/source/app/models/main.py index 0e9a0f6..7778e03 100644 --- a/source/app/models/main.py +++ b/source/app/models/main.py @@ -5364,7 +5364,7 @@ class CfdiPagos(BaseModel): Facturas.folio.alias('Folio'), Facturas.moneda.alias('MonedaDR'), # ~ Facturas.tipo_cambio.alias('TipoCambioDR'), - Facturas.metodo_pago.alias('MetodoDePagoDR'), + # ~ Facturas.metodo_pago.alias('MetodoDePagoDR'), FacturasPagos.numero.alias('NumParcialidad'), FacturasPagos.saldo_anterior.alias('ImpSaldoAnt'), FacturasPagos.importe.alias('ImpPagado'), @@ -5379,6 +5379,7 @@ class CfdiPagos(BaseModel): r['Folio'] = str(r['Folio']) r['NumParcialidad'] = str(r['NumParcialidad']) # ~ r['TipoCambioDR'] = FORMAT.format(r['TipoCambioDR']) + r['MetodoDePagoDR'] = 'PDD' r['ImpSaldoAnt'] = FORMAT.format(r['ImpSaldoAnt']) r['ImpPagado'] = FORMAT.format(r['ImpPagado']) r['ImpSaldoInsoluto'] = FORMAT.format(r['ImpSaldoInsoluto']) @@ -5523,6 +5524,32 @@ class CfdiPagos(BaseModel): name = '{}{}_{}.xml'.format(obj.serie, folio, obj.socio.rfc) return obj.xml, name + def _get_not_in_xml(self, invoice, emisor): + values = {} + + values['notas'] = invoice.notas + values['fechadof'] = None + + obj = SATRegimenes.get(SATRegimenes.key==invoice.regimen_fiscal) + values['regimenfiscal'] = str(obj) + + obj = SATUsoCfdi.get(SATUsoCfdi.key=='P01') + values['usocfdi'] = str(obj) + + values['moneda'] = 'XXX' + + if invoice.tipo_relacion: + obj = SATTipoRelacion.get(SATTipoRelacion.key==invoice.tipo_relacion) + values['tiporelacion'] = str(obj) + + receptor = Socios.select().where(Socios.id==invoice.socio.id).dicts()[0] + values['receptor'] = {} + for k, v in receptor.items(): + values['receptor'][k] = v + + values['pagos'] = True + return values + @classmethod def get_file_pdf(cls, id): try: @@ -5537,12 +5564,13 @@ class CfdiPagos(BaseModel): if obj.uuid is None: return b'', name - # ~ values = cls._get_not_in_xml(cls, obj, emisor) - data = util.get_data_from_xml(obj, {}) - print(data) - # ~ doc = util.to_pdf(data, emisor.rfc) + values = cls._get_not_in_xml(cls, obj, emisor) + data = util.get_data_from_xml(obj, values) + obj = SATFormaPago.get(SATFormaPago.key==data['pays']['FormaDePagoP']) + data['pays']['formadepago'] = '{} ({})'.format(obj.name, obj.key) + doc = util.to_pdf(data, emisor.rfc) - return b'', name + return doc, name @classmethod def get_values(cls, values): diff --git a/source/app/settings.py b/source/app/settings.py index 1914484..d94ec16 100644 --- a/source/app/settings.py +++ b/source/app/settings.py @@ -132,7 +132,8 @@ PRE = { 'NOMINA': { '1.1': '{http://www.sat.gob.mx/nomina}', '1.2': '{http://www.sat.gob.mx/nomina12}', - } + }, + 'pagos': '{http://www.sat.gob.mx/Pagos}', } CURRENT_CFDI = '3.3' diff --git a/source/templates/plantilla_pagos.ods b/source/templates/plantilla_pagos.ods index 8061850..975e9a7 100644 Binary files a/source/templates/plantilla_pagos.ods and b/source/templates/plantilla_pagos.ods differ