Passed
Push — main ( 92a304...476fed )
by Sat CFDI
05:29
created

satcfdi.accounting.contabilidad.generar_contabilidad()   B

Complexity

Conditions 1

Size

Total Lines 94
Code Lines 79

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 15
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 79
nop 12
dl 0
loc 94
ccs 15
cts 15
cp 1
crap 1
rs 7.6872
c 0
b 0
f 0

How to fix   Long Method    Many Parameters   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

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