Add template for CFDI 4.0

This commit is contained in:
Mauricio Baeza 2022-05-30 13:30:47 -05:00
parent 1a507d2eeb
commit ae259e461b
4 changed files with 82 additions and 5 deletions

View File

@ -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']

View File

@ -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']]

View File

@ -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)