1
|
1 |
|
import os |
2
|
1 |
|
from typing import Sequence |
3
|
|
|
|
4
|
1 |
|
from satcfdi.create.contabilidad.AuxiliarCtas13 import AuxiliarCtas, Cuenta, DetalleAux |
5
|
1 |
|
from satcfdi.create.contabilidad.BCE13 import Balanza |
6
|
1 |
|
from satcfdi.create.contabilidad.PLZ13 import Polizas, CompNal, Poliza |
7
|
1 |
|
from satcfdi.create.contabilidad.RepAux13 import RepAuxFol, DetAuxFol |
8
|
1 |
|
from satcfdi.create.contabilidad.catalogocuentas13 import Catalogo, Ctas |
9
|
1 |
|
from .contabilidad_print import imprimir_contablidad |
10
|
|
|
|
11
|
1 |
|
from .. import render |
12
|
|
|
|
13
|
1 |
|
from ..models import DatePeriod |
14
|
|
|
|
15
|
|
|
|
16
|
1 |
|
def filename(file): |
17
|
1 |
|
if file.tag == '{http://www.sat.gob.mx/esquemas/ContabilidadE/1_3/BalanzaComprobacion}Balanza': |
18
|
1 |
|
return file["RFC"] + str(file["Anio"]) + file["Mes"] + "B" + file["TipoEnvio"] + ".xml" |
19
|
1 |
|
if file.tag == '{http://www.sat.gob.mx/esquemas/ContabilidadE/1_3/CatalogoCuentas}Catalogo': |
20
|
1 |
|
return file["RFC"] + str(file["Anio"]) + file["Mes"] + "CT.xml" |
21
|
1 |
|
if file.tag == '{http://www.sat.gob.mx/esquemas/ContabilidadE/1_3/AuxiliarCtas}AuxiliarCtas': |
22
|
1 |
|
return file["RFC"] + str(file["Anio"]) + file["Mes"] + "XC.xml" |
23
|
1 |
|
if file.tag == '{http://www.sat.gob.mx/esquemas/ContabilidadE/1_3/PolizasPeriodo}Polizas': |
24
|
1 |
|
return file["RFC"] + str(file["Anio"]) + file["Mes"] + "PL.xml" |
25
|
1 |
|
if file.tag == '{http://www.sat.gob.mx/esquemas/ContabilidadE/1_3/AuxiliarFolios}RepAuxFol': |
26
|
1 |
|
return file["RFC"] + str(file["Anio"]) + file["Mes"] + "XF.xml" |
27
|
|
|
raise ValueError(f"Unknown file type: {file.tag}") |
28
|
|
|
|
29
|
|
|
|
30
|
1 |
|
def output_file(file, folder, fiel=None, generate_pdf=False): |
31
|
1 |
|
if fiel: |
32
|
|
|
file.sign(fiel) |
33
|
|
|
|
34
|
1 |
|
output_file = os.path.join(folder, filename(file)) |
35
|
1 |
|
file.xml_write( |
36
|
|
|
output_file, |
37
|
|
|
pretty_print=True, |
38
|
|
|
xml_declaration=True |
39
|
|
|
) |
40
|
1 |
|
if generate_pdf: |
41
|
|
|
# render.html_write(file, output_file[:-4] + ".html") |
42
|
|
|
render.pdf_write(file, output_file[:-4] + ".pdf") |
43
|
|
|
else: |
44
|
|
|
# delete file |
45
|
1 |
|
try: |
46
|
1 |
|
os.remove(output_file[:-4] + ".pdf") |
47
|
1 |
|
except FileNotFoundError: |
48
|
1 |
|
pass |
49
|
|
|
|
50
|
1 |
|
return output_file |
51
|
|
|
|
52
|
|
|
|
53
|
1 |
|
def calcular_saldos(cuentas, polizas): |
54
|
|
|
for c in cuentas.values(): |
55
|
|
|
# c['SaldoIni'] = 0 |
56
|
|
|
c['Debe'] = 0 |
57
|
|
|
c['Haber'] = 0 |
58
|
|
|
c['SaldoFin'] = 0 |
59
|
|
|
|
60
|
|
|
for p in polizas: |
61
|
|
|
for t in p["Transaccion"]: |
62
|
|
|
num_cta = t["NumCta"] |
63
|
|
|
cuenta = cuentas[num_cta] |
64
|
|
|
cuenta["Debe"] += t["Debe"] |
65
|
|
|
cuenta["Haber"] += t["Haber"] |
66
|
|
|
|
67
|
1 |
|
# Fill Parents |
68
|
|
|
for level in range(4, 1, -1): |
69
|
|
|
for k, v in cuentas.items(): |
70
|
|
|
if v['Nivel'] == level: |
71
|
|
|
parent = v['SubCtaDe'] |
72
|
|
|
if parent: |
73
|
|
|
p_cuenta = cuentas[parent] |
74
|
|
|
p_cuenta['Debe'] += v['Debe'] |
75
|
|
|
p_cuenta['Haber'] += v['Haber'] |
76
|
1 |
|
|
77
|
|
|
# Fill SaldoFin |
78
|
1 |
|
for c in cuentas.values(): |
79
|
|
|
if c["Natur"] == "D": |
80
|
|
|
c["SaldoFin"] += c["SaldoIni"] + c["Debe"] - c["Haber"] |
81
|
|
|
else: |
82
|
|
|
c["SaldoFin"] += c["SaldoIni"] + c["Haber"] - c["Debe"] |
83
|
|
|
|
84
|
|
|
|
85
|
|
|
def generar_contabilidad( |
86
|
|
|
dp: DatePeriod, |
87
|
|
|
rfc_emisor: str, |
88
|
|
|
cuentas: dict, |
89
|
|
|
polizas: Sequence[Poliza], |
90
|
|
|
tipo_envio='N', |
91
|
|
|
fecha_mod_bal=None, |
92
|
|
|
tipo_solicitud='', |
93
|
1 |
|
numero_orden=None, |
94
|
|
|
numero_tramite=None, |
95
|
1 |
|
folder=None, |
96
|
|
|
fiel=None, |
97
|
|
|
generate_pdf=False): |
98
|
|
|
calcular_saldos(cuentas, polizas) |
99
|
|
|
|
100
|
|
|
plz = Polizas( |
101
|
|
|
rfc=rfc_emisor, |
102
|
|
|
mes=str(dp.month).zfill(2), |
103
|
|
|
anio=dp.year, |
104
|
|
|
tipo_solicitud=tipo_solicitud, |
105
|
|
|
num_orden=numero_orden, |
106
|
1 |
|
num_tramite=numero_tramite, |
107
|
|
|
poliza=polizas |
108
|
1 |
|
) |
109
|
1 |
|
output_file(plz, folder, fiel, generate_pdf=generate_pdf) |
110
|
|
|
|
111
|
|
|
cat = Catalogo( |
112
|
|
|
rfc=rfc_emisor, |
113
|
|
|
mes=str(dp.month).zfill(2), |
114
|
|
|
anio=dp.year, |
115
|
|
|
ctas=[ |
116
|
|
|
Ctas( |
117
|
|
|
cod_agrup=v["CodAgrup"].split("_")[0], |
118
|
|
|
num_cta=k, |
119
|
|
|
desc=v["Desc"], |
120
|
|
|
nivel=v["Nivel"], |
121
|
|
|
natur=v["Natur"], |
122
|
|
|
sub_cta_de=v['SubCtaDe'], |
123
|
|
|
) for k, v in cuentas.items() |
124
|
|
|
] |
125
|
|
|
) |
126
|
1 |
|
output_file(cat, folder, fiel) |
127
|
|
|
|
128
|
1 |
|
ban = Balanza( |
129
|
|
|
rfc=rfc_emisor, |
130
|
|
|
mes=str(dp.month).zfill(2), |
131
|
|
|
anio=dp.year, |
132
|
|
|
tipo_envio=tipo_envio, |
133
|
|
|
fecha_mod_bal=fecha_mod_bal, |
134
|
|
|
ctas=[{ |
135
|
|
|
"NumCta": k, |
136
|
|
|
**v, |
137
|
1 |
|
} for k, v in cuentas.items() if v["SaldoIni"] or v["Debe"] or v["Haber"] or v["SaldoFin"]], |
138
|
|
|
) |
139
|
1 |
|
output_file(ban, folder, fiel) |
140
|
|
|
|
141
|
|
|
aux_detalles = group_aux_cuentas(polizas) |
142
|
|
|
aux = AuxiliarCtas( |
143
|
|
|
rfc=rfc_emisor, |
144
|
|
|
mes=str(dp.month).zfill(2), |
145
|
1 |
|
anio=dp.year, |
146
|
|
|
tipo_solicitud=tipo_solicitud, |
147
|
|
|
num_orden=numero_orden, |
148
|
1 |
|
num_tramite=numero_tramite, |
149
|
1 |
|
cuenta=[ |
150
|
1 |
|
Cuenta( |
151
|
|
|
num_cta=k, |
152
|
|
|
des_cta=v["Desc"], |
153
|
|
|
saldo_ini=v["SaldoIni"], |
154
|
|
|
saldo_fin=v["SaldoFin"], |
155
|
|
|
detalle_aux=aux_detalles[k] |
156
|
|
|
) for k, v in cuentas.items() if k in aux_detalles |
157
|
|
|
] |
158
|
|
|
) |
159
|
|
|
output_file(aux, folder, fiel, generate_pdf=generate_pdf) |
160
|
|
|
|
161
|
|
|
auxf = RepAuxFol( |
162
|
1 |
|
rfc=rfc_emisor, |
163
|
|
|
mes=str(dp.month).zfill(2), |
164
|
|
|
anio=dp.year, |
165
|
1 |
|
tipo_solicitud=tipo_solicitud, |
166
|
1 |
|
num_orden=numero_orden, |
167
|
|
|
num_tramite=numero_tramite, |
168
|
|
|
det_aux_fol=list(group_aux_folios(polizas)) |
169
|
|
|
) |
170
|
|
|
output_file(auxf, folder, fiel, generate_pdf=generate_pdf) |
171
|
|
|
|
172
|
|
|
imprimir_contablidad( |
173
|
|
|
catalogo_cuentas=cat, |
174
|
1 |
|
balanza_comprobacion=ban, |
175
|
1 |
|
archivo_excel=filename(ban)[:-4] + ".xlsx" |
176
|
1 |
|
) |
177
|
1 |
|
|
178
|
|
|
validate_saldos(cuentas) |
179
|
|
|
|
180
|
|
|
|
181
|
|
|
def group_aux_cuentas(polizas): |
182
|
|
|
cta_polizas = {} |
183
|
|
|
for p in polizas: |
184
|
|
|
for t in p["Transaccion"]: |
185
|
|
|
detalles = cta_polizas.setdefault(t["NumCta"], []) |
186
|
|
|
detalles.append( |
187
|
|
|
DetalleAux( |
188
|
|
|
fecha=p["Fecha"], |
189
|
|
|
num_un_iden_pol=p["NumUnIdenPol"], |
190
|
1 |
|
concepto=t["Concepto"], |
191
|
1 |
|
debe=t["Debe"], |
192
|
|
|
haber=t["Haber"], |
193
|
|
|
) |
194
|
|
|
) |
195
|
|
|
return cta_polizas |
196
|
|
|
|
197
|
|
|
|
198
|
|
|
def group_aux_folios(polizas): |
199
|
|
|
for p in polizas: |
200
|
|
|
yield DetAuxFol( |
201
|
|
|
num_un_iden_pol=p["NumUnIdenPol"], |
202
|
|
|
fecha=p["Fecha"], |
203
|
|
|
compr_nal=p.comp_nal, |
204
|
|
|
) |
205
|
|
|
|
206
|
|
|
|
207
|
|
|
def validate_saldos(cuentas): |
208
|
|
|
total = 0 |
209
|
|
|
totales = {} |
210
|
|
|
for k, v in cuentas.items(): |
211
|
|
|
if v['Nivel'] == 1: |
212
|
|
|
if v['Natur'] == 'D': |
213
|
|
|
total += v['SaldoFin'] |
214
|
|
|
else: |
215
|
|
|
total -= v['SaldoFin'] |
216
|
|
|
else: |
217
|
|
|
totales.setdefault(v['SubCtaDe'], 0) |
218
|
|
|
if v['Natur'] == 'D': |
219
|
|
|
totales[v['SubCtaDe']] += v['SaldoFin'] |
220
|
|
|
else: |
221
|
|
|
totales[v['SubCtaDe']] -= v['SaldoFin'] |
222
|
|
|
|
223
|
|
|
assert total == 0 |
224
|
|
|
for k, v in totales.items(): |
225
|
|
|
if cuentas[k]['Natur'] == 'D': |
226
|
|
|
assert v == cuentas[k]['SaldoFin'] |
227
|
|
|
else: |
228
|
|
|
assert v == -cuentas[k]['SaldoFin'] |
229
|
|
|
|