Skip to content

Commit

Permalink
Added Contabilidad functions
Browse files Browse the repository at this point in the history
Added Contabilidad functions

Added Contabilidad functions

Added Contabilidad functions

Updated DatePeriod
  • Loading branch information
satcfdi committed Apr 9, 2024
1 parent 9701fa0 commit 3fa2cc6
Show file tree
Hide file tree
Showing 23 changed files with 494 additions and 26 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -154,3 +154,4 @@ dmypy.json
/satcfdi/create/*/*_z.py
/docs/generated/
/satcfdi.svg
/tests/test_contabilidad_electronica/out/
228 changes: 228 additions & 0 deletions satcfdi/accounting/contabilidad.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,228 @@
import os
from typing import Sequence

from satcfdi.create.contabilidad.AuxiliarCtas13 import AuxiliarCtas, Cuenta, DetalleAux
from satcfdi.create.contabilidad.BCE13 import Balanza
from satcfdi.create.contabilidad.PLZ13 import Polizas, CompNal, Poliza
from satcfdi.create.contabilidad.RepAux13 import RepAuxFol, DetAuxFol
from satcfdi.create.contabilidad.catalogocuentas13 import Catalogo, Ctas
from .contabilidad_print import imprimir_contablidad

from .. import render

from ..models import DatePeriod


def filename(file):
if file.tag == '{http://www.sat.gob.mx/esquemas/ContabilidadE/1_3/BalanzaComprobacion}Balanza':
return file["RFC"] + str(file["Anio"]) + file["Mes"] + "B" + file["TipoEnvio"] + ".xml"
if file.tag == '{http://www.sat.gob.mx/esquemas/ContabilidadE/1_3/CatalogoCuentas}Catalogo':
return file["RFC"] + str(file["Anio"]) + file["Mes"] + "CT.xml"
if file.tag == '{http://www.sat.gob.mx/esquemas/ContabilidadE/1_3/AuxiliarCtas}AuxiliarCtas':
return file["RFC"] + str(file["Anio"]) + file["Mes"] + "XC.xml"
if file.tag == '{http://www.sat.gob.mx/esquemas/ContabilidadE/1_3/PolizasPeriodo}Polizas':
return file["RFC"] + str(file["Anio"]) + file["Mes"] + "PL.xml"
if file.tag == '{http://www.sat.gob.mx/esquemas/ContabilidadE/1_3/AuxiliarFolios}RepAuxFol':
return file["RFC"] + str(file["Anio"]) + file["Mes"] + "XF.xml"
raise ValueError(f"Unknown file type: {file.tag}")


def output_file(file, folder, fiel=None, generate_pdf=False):
if fiel:
file.sign(fiel)

output_file = os.path.join(folder, filename(file))
file.xml_write(
output_file,
pretty_print=True,
xml_declaration=True
)
if generate_pdf:
# render.html_write(file, output_file[:-4] + ".html")
render.pdf_write(file, output_file[:-4] + ".pdf")
else:
# delete file
try:
os.remove(output_file[:-4] + ".pdf")
except FileNotFoundError:
pass

return output_file


def calcular_saldos(cuentas, polizas):
for c in cuentas.values():
# c['SaldoIni'] = 0
c['Debe'] = 0
c['Haber'] = 0
c['SaldoFin'] = 0

for p in polizas:
for t in p["Transaccion"]:
num_cta = t["NumCta"]
cuenta = cuentas[num_cta]
cuenta["Debe"] += t["Debe"]
cuenta["Haber"] += t["Haber"]

# Fill Parents
for level in range(4, 1, -1):
for k, v in cuentas.items():
if v['Nivel'] == level:
parent = v['SubCtaDe']
if parent:
p_cuenta = cuentas[parent]
p_cuenta['Debe'] += v['Debe']
p_cuenta['Haber'] += v['Haber']

# Fill SaldoFin
for c in cuentas.values():
if c["Natur"] == "D":
c["SaldoFin"] += c["SaldoIni"] + c["Debe"] - c["Haber"]
else:
c["SaldoFin"] += c["SaldoIni"] + c["Haber"] - c["Debe"]


def generar_contabilidad(
dp: DatePeriod,
rfc_emisor: str,
cuentas: dict,
polizas: Sequence[Poliza],
tipo_envio='N',
fecha_mod_bal=None,
tipo_solicitud='',
numero_orden=None,
numero_tramite=None,
folder=None,
fiel=None,
generate_pdf=False):
calcular_saldos(cuentas, polizas)

plz = Polizas(
rfc=rfc_emisor,
mes=str(dp.month).zfill(2),
anio=dp.year,
tipo_solicitud=tipo_solicitud,
num_orden=numero_orden,
num_tramite=numero_tramite,
poliza=polizas
)
output_file(plz, folder, fiel, generate_pdf=generate_pdf)

cat = Catalogo(
rfc=rfc_emisor,
mes=str(dp.month).zfill(2),
anio=dp.year,
ctas=[
Ctas(
cod_agrup=v["CodAgrup"].split("_")[0],
num_cta=k,
desc=v["Desc"],
nivel=v["Nivel"],
natur=v["Natur"],
sub_cta_de=v['SubCtaDe'],
) for k, v in cuentas.items()
]
)
output_file(cat, folder, fiel)

ban = Balanza(
rfc=rfc_emisor,
mes=str(dp.month).zfill(2),
anio=dp.year,
tipo_envio=tipo_envio,
fecha_mod_bal=fecha_mod_bal,
ctas=[{
"NumCta": k,
**v,
} for k, v in cuentas.items() if v["SaldoIni"] or v["Debe"] or v["Haber"] or v["SaldoFin"]],
)
output_file(ban, folder, fiel)

aux_detalles = group_aux_cuentas(polizas)
aux = AuxiliarCtas(
rfc=rfc_emisor,
mes=str(dp.month).zfill(2),
anio=dp.year,
tipo_solicitud=tipo_solicitud,
num_orden=numero_orden,
num_tramite=numero_tramite,
cuenta=[
Cuenta(
num_cta=k,
des_cta=v["Desc"],
saldo_ini=v["SaldoIni"],
saldo_fin=v["SaldoFin"],
detalle_aux=aux_detalles[k]
) for k, v in cuentas.items() if k in aux_detalles
]
)
output_file(aux, folder, fiel, generate_pdf=generate_pdf)

auxf = RepAuxFol(
rfc=rfc_emisor,
mes=str(dp.month).zfill(2),
anio=dp.year,
tipo_solicitud=tipo_solicitud,
num_orden=numero_orden,
num_tramite=numero_tramite,
det_aux_fol=list(group_aux_folios(polizas))
)
output_file(auxf, folder, fiel, generate_pdf=generate_pdf)

imprimir_contablidad(
catalogo_cuentas=cat,
balanza_comprobacion=ban,
archivo_excel=os.path.join(folder, filename(ban)[:-4] + ".xlsx")
)

validate_saldos(cuentas)


def group_aux_cuentas(polizas):
cta_polizas = {}
for p in polizas:
for t in p["Transaccion"]:
detalles = cta_polizas.setdefault(t["NumCta"], [])
detalles.append(
DetalleAux(
fecha=p["Fecha"],
num_un_iden_pol=p["NumUnIdenPol"],
concepto=t["Concepto"],
debe=t["Debe"],
haber=t["Haber"],
)
)
return cta_polizas


def group_aux_folios(polizas):
for p in polizas:
yield DetAuxFol(
num_un_iden_pol=p["NumUnIdenPol"],
fecha=p["Fecha"],
compr_nal=p.comp_nal,
)


def validate_saldos(cuentas):
total = 0
totales = {}
for k, v in cuentas.items():
if v['Nivel'] == 1:
if v['Natur'] == 'D':
total += v['SaldoFin']
else:
total -= v['SaldoFin']
else:
totales.setdefault(v['SubCtaDe'], 0)
if v['Natur'] == 'D':
totales[v['SubCtaDe']] += v['SaldoFin']
else:
totales[v['SubCtaDe']] -= v['SaldoFin']

assert total == 0
for k, v in totales.items():
if cuentas[k]['Natur'] == 'D':
assert v == cuentas[k]['SaldoFin']
else:
assert v == -cuentas[k]['SaldoFin']
51 changes: 51 additions & 0 deletions satcfdi/accounting/contabilidad_print.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import xlsxwriter
from satcfdi.accounting.process import excel_export
from satcfdi.cfdi import CFDI

EXCEL_COLUMNS = {
'NumCta': (12, False, lambda i: i['NumCta']),
'Desc': (62, False, lambda i: ' ' * (i['Nivel'] - 1) + i['Desc']),
'Natur': (5, False, lambda i: i['Natur']),

'SaldoIni': (12, False, lambda i: i.get('SaldoIni')),
'Debe': (12, False, lambda i: i.get('Debe')),
'Haber': (12, False, lambda i: i.get('Haber')),
'SaldoFin': (12, False, lambda i: i.get('SaldoFin')),

'CodAgrup': (12, False, lambda i: i['CodAgrup'].code),
'CodDesc': (120, False, lambda i: i['CodAgrup'].description),
}


def imprimir_contablidad(
catalogo_cuentas,
balanza_comprobacion,
archivo_excel
):
# ct = CFDI.from_file(catalogo_cuentas)
# bc = CFDI.from_file(balanza_comprobacion)
ct = catalogo_cuentas
bc = balanza_comprobacion

ctas = {
c['NumCta']: {
'Desc': c['Desc'],
'Natur': c['Natur'],
'CodAgrup': c['CodAgrup'],
'Nivel': c['Nivel'],
}
for c in ct['Ctas']
}
for r in bc['Ctas']:
ctv = ctas[r['NumCta']]
r.update(ctv)

workbook = xlsxwriter.Workbook(archivo_excel)
excel_export(
workbook=workbook,
name=bc['RFC'] + str(bc['Anio']) + str(bc['Mes']),
invoices=bc['Ctas'],
columns=EXCEL_COLUMNS,
row_height=1
)
workbook.close()
4 changes: 2 additions & 2 deletions satcfdi/accounting/process.py
Original file line number Diff line number Diff line change
Expand Up @@ -248,7 +248,7 @@ def num2col(n):
return name


def excel_export(workbook: xlsxwriter.workbook.Workbook, name, invoices, columns):
def excel_export(workbook: xlsxwriter.workbook.Workbook, name, invoices, columns, row_height=3):
worksheet = workbook.add_worksheet(name)

for n, (w, s, f) in enumerate(columns.values()):
Expand All @@ -275,7 +275,7 @@ def excel_export(workbook: xlsxwriter.workbook.Workbook, name, invoices, columns
number_format = workbook.add_format({'num_format': '0.00'})
number_format.set_align('top')

row_height = 20 * 3
row_height = 20 * row_height
r = 0
for r, i in enumerate(invoices):
worksheet.set_row_pixels(1 + r, row_height)
Expand Down
1 change: 1 addition & 0 deletions satcfdi/create/contabilidad/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@

2 changes: 1 addition & 1 deletion satcfdi/render/templates/AuxiliarCtas.html
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ <h3>Auxiliar de Cuentas</h3>
<th style="width: 12%;">Debe</th>
<th style="width: 12%;">Haber</th>
</tr>
{% for c in c.DetalleAux %}
{% for c in iterate(c.DetalleAux) %}
<tr>
<td class="l">{{ c.Fecha }}</td>
<td class="l">{{ c.NumUnIdenPol }}</td>
Expand Down
12 changes: 8 additions & 4 deletions satcfdi/render/templates/RepAuxFol.html
Original file line number Diff line number Diff line change
Expand Up @@ -36,15 +36,19 @@ <h3>Auxiliar de Folios</h3>
<td colspan="2" style="padding-left: 5%; padding-right: 0%; background-color:#ffffff;">
<table >
<tr>
<th style="width: 40%;">UUID_CFDI</th>
<th style="width: 40%;">RFC</th>
<th style="width: 20%;">MontoTotal</th>
<th style="width: 35%;">UUID_CFDI</th>
<th style="width: 35%;">RFC</th>
<th style="width: 10%;">MontoTotal</th>
<th style="width: 10%;">Moneda</th>
<th style="width: 10%;">TipCamb</th>
</tr>
{% for c in c.ComprNal %}
{% for c in iterate(c.ComprNal) %}
<tr>
<td class="l">{{ c.UUID_CFDI }}</td>
<td class="l">{{ c.RFC }}</td>
<td class="r">{{ c.MontoTotal }}</td>
<td class="r">{{ c.Moneda }}</td>
<td class="r">{{ c.TipCamb }}</td>
</tr>
{% endfor %}
</table>
Expand Down
55 changes: 55 additions & 0 deletions tests/contabilidad_electronica/cuentas.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
### CUENTAS PRINCIPALES ###
'1000':
Desc: Activos
Natur: D
CodAgrup: '100' # Activo
Nivel: 1
SubCtaDe:
SaldoIni: 0

### ACTIVOS 100 ###
# BANCOS
'1020':
Desc: Bancos
Natur: D
CodAgrup: '102' # Bancos nacionales
Nivel: 2
SubCtaDe: '1000'
SaldoIni: 0
'1020.01':
Desc: Bancos Nacionales
Natur: D
CodAgrup: '102.01' # Bancos nacionales
Nivel: 2
SubCtaDe: '1020'
SaldoIni: 0
'1020.02':
Desc: Bancos Extranjeros
Natur: D
CodAgrup: '102.02' # Bancos extranjeros
Nivel: 2
SubCtaDe: '1020'
SaldoIni: 0

# CLIENTES
'1050':
Desc: Clientes
Natur: D
CodAgrup: '105' # Clientes
Nivel: 2
SubCtaDe: '1000'
SaldoIni: 0
'1050.01':
Desc: Clientes Nacionales
Natur: D
CodAgrup: '105.01' # Clientes nacionales
Nivel: 2
SubCtaDe: '1050'
SaldoIni: 0
'1050.02':
Desc: Clientes Extranjeros
Natur: D
CodAgrup: '105.02' # Clientes extranjeros
Nivel: 2
SubCtaDe: '1050'
SaldoIni: 0
Loading

0 comments on commit 3fa2cc6

Please sign in to comment.