diff --git a/source/app/controllers/util.py b/source/app/controllers/util.py index 9f45f2e..0aadfc8 100644 --- a/source/app/controllers/util.py +++ b/source/app/controllers/util.py @@ -1840,7 +1840,7 @@ def _receptor(doc, version, values): return data data['usocfdi'] = values['usocfdi'] - data.update(values['receptor']) + # ~ data.update(values['receptor']) return data @@ -2121,6 +2121,7 @@ def get_data_from_xml(invoice, values, pdf_from='1'): data = {'cancelada': invoice.cancelada, 'donativo': False} if hasattr(invoice, 'donativo'): data['donativo'] = invoice.donativo + doc = parse_xml(invoice.xml) data['comprobante'] = _comprobante(doc, values) version = data['comprobante']['version'] diff --git a/source/app/controllers/utils.py b/source/app/controllers/utils.py index ade622c..2c64b4d 100644 --- a/source/app/controllers/utils.py +++ b/source/app/controllers/utils.py @@ -270,6 +270,53 @@ class CfdiToDict(object): '03': '[03] Arrendador', '04': '[04] Notificado', } + REGIMEN_FISCAL = { + '601': '[601] General de Ley Personas Morales', + '603': '[603] Personas Morales con Fines no Lucrativos', + '605': '[605] Sueldos y Salarios e Ingresos Asimilados a Salarios', + '606': '[606] Arrendamiento', + '607': '[607] Régimen de Enajenación o Adquisición de Bienes', + '608': '[608] Demás ingresos', + '610': '[610] Residentes en el Extranjero sin Establecimiento Permanente en México', + '611': '[611] Ingresos por Dividendos (socios y accionistas)', + '612': '[612] Personas Físicas con Actividades Empresariales y Profesionales', + '614': '[614] Ingresos por intereses', + '615': '[615] Régimen de los ingresos por obtención de premios', + '616': '[616] Sin obligaciones fiscales', + '620': '[620] Sociedades Cooperativas de Producción que optan por diferir sus ingresos', + '621': '[621] Incorporación Fiscal', + '622': '[622] Actividades Agrícolas, Ganaderas, Silvícolas y Pesqueras', + '623': '[623] Opcional para Grupos de Sociedades', + '624': '[624] Coordinados', + '625': '[625] Régimen de las Actividades Empresariales con ingresos a través de Plataformas Tecnológicas', + '626': '[626] Régimen Simplificado de Confianza', + } + USO_CFDI = { + 'G01': '[G01] Adquisición de mercancías.', + 'G02': '[G02] Devoluciones, descuentos o bonificaciones.', + 'G03': '[G03] Gastos en general.', + 'I01': '[I01] Construcciones.', + 'I02': '[I02] Mobiliario y equipo de oficina por inversiones.', + 'I03': '[I03] Equipo de transporte.', + 'I04': '[I04] Equipo de computo y accesorios.', + 'I05': '[I05] Dados, troqueles, moldes, matrices y herramental.', + 'I06': '[I06] Comunicaciones telefónicas.', + 'I07': '[I07] Comunicaciones satelitales.', + 'I08': '[I08] Otra maquinaria y equipo.', + 'D01': '[D01] Honorarios médicos, dentales y gastos hospitalarios.', + 'D02': '[D02] Gastos médicos por incapacidad o discapacidad.', + 'D03': '[D03] Gastos funerales.', + 'D04': '[D04] Donativos.', + 'D05': '[D05] Intereses reales efectivamente pagados por créditos hipotecarios (casa habitación).', + 'D06': '[D06] Aportaciones voluntarias al SAR.', + 'D07': '[D07] Primas por seguros de gastos médicos.', + 'D08': '[D08] Gastos de transportación escolar obligatoria.', + 'D09': '[D09] Depósitos en cuentas para el ahorro, primas que tengan como base planes de pensiones.', + 'D10': '[D10] Pagos por servicios educativos (colegiaturas).', + 'S01': '[S01] Sin efectos fiscales.', + 'CP01': '[CP01] Pagos', + 'CN01': '[CN01] Nómina', + } PAISES = { 'MEX': 'México', } @@ -324,9 +371,21 @@ class CfdiToDict(object): version = self._root.attrib['Version'] ns = f'cfdi{version}' self.NS['cfdi'] = self.NS_VERSION[ns] + + self._receptor() self._complementos() return + def _receptor(self): + path = '//cfdi:Receptor' + receptor = self._root.xpath(path, namespaces=self.NS)[0] + attr = CaseInsensitiveDict(receptor.attrib) + attr['domiciliofiscal'] = attr['DomicilioFiscalReceptor'] + attr['regimenfiscal'] = self.REGIMEN_FISCAL[attr['RegimenFiscalReceptor']] + attr['usocfdi'] = self.USO_CFDI[attr['UsoCFDI']] + self._values['receptor'] = attr + return + def _set_carta_porte_domicilio(self, data): municipio = data['Municipio'] estado = self.ESTADOS[data['Estado']] diff --git a/source/app/models/main.py b/source/app/models/main.py index a464e31..e661be7 100644 --- a/source/app/models/main.py +++ b/source/app/models/main.py @@ -4722,10 +4722,10 @@ class Facturas(BaseModel): obj = SATTipoRelacion.get(SATTipoRelacion.key==invoice.tipo_relacion) values['tiporelacion'] = str(obj) - receptor = Socios.select().where(Socios.id==invoice.cliente.id).dicts()[0] - values['receptor'] = {} - for k, v in receptor.items(): - values['receptor'][k] = v + # ~ receptor = Socios.select().where(Socios.id==invoice.cliente.id).dicts()[0] + # ~ values['receptor'] = {} + # ~ for k, v in receptor.items(): + # ~ values['receptor'][k] = v # ~ use_packing = Configuracion.get_bool('chk_use_packing') # ~ if use_packing: @@ -4739,6 +4739,21 @@ class Facturas(BaseModel): return values + def _get_not_in_xml2(self, invoice, data): + fields = ( + Socios.calle, + Socios.no_exterior, + Socios.no_interior, + Socios.colonia, + Socios.municipio, + Socios.estado, + Socios.pais, + ) + where = (Socios.id==invoice.cliente.id) + partner = Socios.select(*fields).where(where).dicts()[0] + data['receptor'].update(partner) + return + @classmethod def get_pdf(cls, id, rfc, sync=True): try: @@ -4756,7 +4771,9 @@ class Facturas(BaseModel): #Tmp to v2 data = util.get_data_from_xml(obj, values, pdf_from) + data.update(utils.CfdiToDict(obj.xml).values) + cls._get_not_in_xml2(cls, obj, data) doc = util.to_pdf(data, emisor.rfc, pdf_from=pdf_from) diff --git a/source/templates/plantilla_factura_4.0.ods b/source/templates/plantilla_factura_4.0.ods index a19230a..6d65f63 100644 Binary files a/source/templates/plantilla_factura_4.0.ods and b/source/templates/plantilla_factura_4.0.ods differ