Test Failed
Push — main ( b5f93a...b20cbc )
by Sat CFDI
05:30
created

satcfdi.accounting.contabilidad.group_aux_folios()   A

Complexity

Conditions 2

Size

Total Lines 6
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

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