Passed
Push — main ( 97b175...ba1019 )
by Sat CFDI
05:01
created

test_create_cfdi40.test_nomina()   B

Complexity

Conditions 1

Size

Total Lines 99
Code Lines 73

Duplication

Lines 99
Ratio 100 %

Importance

Changes 0
Metric Value
cc 1
eloc 73
nop 0
dl 99
loc 99
rs 7.8836
c 0
b 0
f 0

How to fix   Long Method   

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:

1
import os
2
from datetime import datetime, date
3
from decimal import Decimal
4
from unittest import mock
5
6
import pytest
7
8
from satcfdi.cfdi import CFDI
9
from satcfdi.create.cfd import cfdi40, nomina12
10
from satcfdi.pacs.sat import SAT
11
from tests.utils import get_signer, verify_result, _uuid, get_rfc_pac, stamp_v11, SAT_Certificate_Store_Pruebas, XElementPrettyPrinter
12
13
module = 'satcfdi'
14
current_dir = os.path.dirname(__file__)
15
current_filename = os.path.splitext(os.path.basename(__file__))[0]
16
sat = SAT()
17
18
invoices = [
19
    ('xiqb891116qe4', "xiqb891116qe4_ingreso_noobjeto", None, [cfdi40.Impuesto.parse('001|Tasa|0.100000')], '13851.27', False),
20
    ('xiqb891116qe4', "xiqb891116qe4_ingreso_exento", cfdi40.Impuesto.parse('002|Exento'), [cfdi40.Impuesto.parse('001|Tasa|0.100000')], '13851.27', False),
21
    ('xiqb891116qe4', "xiqb891116qe4_ingreso_iva16", 'IVA|Tasa|0.160000', ['ISR|Tasa|0.100000', cfdi40.Impuesto.parse('IVA|Tasa|0.106667')], '14672.08', False),
22
    ('h&e951128469', "h&e951128469_ingreso_noobjeto", None, None, '15390.30', False),
23
    ('h&e951128469', "h&e951128469_ingreso_exento", cfdi40.Impuesto.parse('002|Exento'), None, '15390.30', False),
24
    ('h&e951128469', "h&e951128469_ingreso_iva16", cfdi40.Impuesto.parse('002|Tasa|0.160000'), None, '17852.75', False),
25
    ('h&e951128469', "h&e951128469_ingreso_ieps_exento", cfdi40.Impuesto.parse('003|Exento'), None, '15390.30', False)
26
]
27
28
29
def verify_invoice(invoice, path, include_metadata=False):
30
    if include_metadata:
31
        verify = verify_result(data=invoice.metadata, filename=f"{path}.txt")
32
        assert verify
33
34
    pp = XElementPrettyPrinter()
35
    verify = verify_result(data=pp.pformat(invoice), filename=f"{path}.pretty.py")
36
    assert verify
37
38
    verify = verify_result(data=invoice.xml_bytes(pretty_print=True), filename=f"{path}.xml")
39
    assert verify
40
41
    verify = verify_result(data=invoice.html_str(), filename=f"{path}.html")
42
    assert verify
43
44
45
def test_traslados_incluidos():
46
    signer = get_signer('h&e951128469')
47
    emisor = cfdi40.Issuer(signer=signer, tax_system="606")
48
49
    for t in range(10):
50
        delta = Decimal(t) / 100
51
        valor = Decimal('15390.30') + delta
52
53
        invoice = cfdi40.Comprobante(
54
            emisor=emisor,
55
            lugar_expedicion="56820",
56
            fecha=datetime.fromisoformat("2020-01-01T22:40:38"),
57
            receptor=cfdi40.Receptor(
58
                rfc='KIJ0906199R1',
59
                nombre='KIJ, S.A DE C.V.',
60
                uso_cfdi='G03',
61
                domicilio_fiscal_receptor="59820",
62
                regimen_fiscal_receptor="601"
63
            ),
64
            metodo_pago='PPD',
65
            serie="A",
66
            folio="123456",
67
            conceptos=[
68
                cfdi40.Concepto(
69
                    cuenta_predial='1234567890',
70
                    clave_prod_serv='10101702',
71
                    cantidad=Decimal('23.00'),
72
                    clave_unidad='E48',
73
                    descripcion='SERVICIOS DE FACTURACION',
74
                    valor_unitario=valor,
75
                    traslados=cfdi40.Impuesto.parse('002|Tasa|0.160000'),
76
                    retenciones=None,
77
                    _traslados_incluidos=True
78
                )
79
            ]
80
        )
81
82
        assert invoice["Total"] == valor * Decimal('23.00')
83
84
85
@pytest.mark.parametrize('rfc, xml_file, traslados, retenciones, total, traslado_incluido', invoices)
86
@pytest.mark.skip(reason="skiping render for performance reasons")
87
def test_create_invoice_render(rfc, xml_file, traslados, retenciones, total, traslado_incluido):
88
    xml_file = "_render" + xml_file
89
    signer = get_signer(rfc)
90
    emisor = cfdi40.Issuer(signer=signer, tax_system="606")
91
92
    invoice = cfdi40.Comprobante(
93
        emisor=emisor,
94
        lugar_expedicion="56820",
95
        fecha=datetime.fromisoformat("2020-01-01T22:40:38"),
96
        receptor=cfdi40.Receptor(
97
            rfc='KIJ0906199R1',
98
            nombre='KIJ, S.A DE C.V.',
99
            uso_cfdi='G03',
100
            domicilio_fiscal_receptor="59820",
101
            regimen_fiscal_receptor="601"
102
        ),
103
        metodo_pago='PPD',
104
        serie="A",
105
        folio="123456",
106
        conceptos=[
107
            cfdi40.Concepto(
108
                cuenta_predial='1234567890',
109
                clave_prod_serv='10101702',
110
                cantidad=Decimal('1.00'),
111
                clave_unidad='E48',
112
                descripcion='SERVICIOS DE FACTURACION',
113
                valor_unitario=Decimal('15390.30'),
114
                traslados=traslados,
115
                retenciones=retenciones,
116
                _traslados_incluidos=traslado_incluido,
117
            )
118
        ]
119
    )
120
121
    assert invoice["Total"] == Decimal(total)
122
123
    # Stamping
124
    with mock.patch('uuid.uuid4', _uuid), \
125
            mock.patch(f'{module}.Certificate.rfc_pac', get_rfc_pac):
126
        stamp_v11(invoice, signer=signer, date=datetime(year=2020, month=1, day=11))
127
128
    path = f"{xml_file}_stamped"
129
130
    os.makedirs(os.path.join(current_dir, 'renders'), exist_ok=True)
131
    invoice.html_write(target=os.path.join(current_dir, 'renders', f"{path}.html"))
132
    invoice.pdf_write(target=os.path.join(current_dir, 'renders', f"{path}.pdf"))
133
134
135
@pytest.mark.parametrize('rfc, xml_file, traslados, retenciones, total, traslado_incluido', invoices)
136
def test_create_invoice(rfc, xml_file, traslados, retenciones, total, traslado_incluido):
137
    signer = get_signer(rfc)
138
    emisor = cfdi40.Issuer(signer=signer, tax_system="606")
139
140
    invoice = cfdi40.Comprobante(
141
        emisor=emisor,
142
        lugar_expedicion="56820",
143
        fecha=datetime.fromisoformat("2020-01-01T22:40:38"),
144
        receptor=cfdi40.Receptor(
145
            rfc='KIJ0906199R1',
146
            nombre='KIJ, S.A DE C.V.',
147
            uso_cfdi='G03',
148
            domicilio_fiscal_receptor="59820",
149
            regimen_fiscal_receptor="601"
150
        ),
151
        metodo_pago='PPD',
152
        serie="A",
153
        folio="123456",
154
        conceptos=[
155
            cfdi40.Concepto(
156
                cuenta_predial='1234567890',
157
                clave_prod_serv='10101702',
158
                cantidad=Decimal('1.00'),
159
                clave_unidad='E48',
160
                descripcion='SERVICIOS DE FACTURACION',
161
                valor_unitario=Decimal('15390.30'),
162
                traslados=traslados,
163
                retenciones=retenciones,
164
                _traslados_incluidos=traslado_incluido
165
            )
166
        ]
167
    )
168
    invoice.to_xml(validate=True)
169
    assert invoice["Total"] == Decimal(total)
170
171
    verify_invoice(invoice, f"{xml_file}")
172
173
    # Stamping
174
    with mock.patch('uuid.uuid4', _uuid), \
175
            mock.patch(f'{module}.Certificate.rfc_pac', "SAT970701NN3"):
176
        stamp_v11(invoice, signer=signer, date=datetime(year=2020, month=1, day=11))
177
178
    # with mock.patch(f'{module}.cfdi.CFDI.estatus', '1'), mock.patch(f'{module}.cfdi.CFDI.fecha_cancelacion', None):
179
    verify_invoice(invoice, f"{xml_file}_stamped", include_metadata=False)
180
181
    def from_no_certificado(_, no_certificado):
182
        return signer
183
184
    # Verify Signature
185
    with mock.patch(f'{module}.transform.SAT_Certificate_Store', SAT_Certificate_Store_Pruebas), \
186
            mock.patch(f'{module}.pacs.sat.SAT.recover_certificate', from_no_certificado), \
187
            mock.patch(f'{module}.Certificate.rfc_pac', "SAT970701NN3"):
188
        ver = sat.validate(invoice)
189
        assert ver
190
191
192
@pytest.mark.parametrize('rfc, xml_file, traslados, retenciones, total, traslado_incluido', invoices)
193
def test_create_pago(rfc, xml_file, traslados, retenciones, total, traslado_incluido):
194
    signer = get_signer(rfc)
195
    emisor = cfdi40.Issuer(signer=signer, tax_system="606")
196
197
    ingreso_invoice = CFDI.from_file(os.path.join(current_dir, f"{current_filename}/{xml_file}_stamped.xml"))
198
199
    invoice = cfdi40.Comprobante.pago_comprobantes(
200
        emisor=emisor,
201
        lugar_expedicion="56820",
202
        fecha=datetime.fromisoformat("2020-01-01T22:40:38"),
203
        comprobantes=[ingreso_invoice],
204
        fecha_pago=datetime.fromisoformat("2020-01-02T22:40:38"),
205
        forma_pago="03",
206
        serie="A",
207
        folio="123456",
208
    )
209
210
    assert invoice['Complemento']['Pago'][0]['Monto'] == Decimal(total)
211
212
    verify_invoice(invoice, f"pago_{xml_file}")
213
214
215
@pytest.mark.parametrize('rfc, xml_file, traslados, retenciones, total, traslado_incluido', invoices)
216
def test_create_pago_parcial(rfc, xml_file, traslados, retenciones, total, traslado_incluido):
217
    signer = get_signer(rfc)
218
    emisor = cfdi40.Issuer(signer=signer, tax_system="606")
219
220
    ingreso_invoice = CFDI.from_file(os.path.join(current_dir, f"{current_filename}/{xml_file}_stamped.xml"))
221
222
    invoice = cfdi40.Comprobante.pago_comprobante(
223
        emisor=emisor,
224
        lugar_expedicion="56820",
225
        fecha=datetime.fromisoformat("2020-01-01T22:40:38"),
226
        comprobante=ingreso_invoice,
227
        num_parcialidad=2,
228
        imp_saldo_ant=Decimal("5000.43"),
229
        imp_pagado=Decimal("3245.12"),
230
        fecha_pago=datetime.fromisoformat("2020-01-02T22:40:38"),
231
        forma_pago="03",
232
        serie="A",
233
        folio="123456",
234
    )
235
236
    assert invoice['Complemento']['Pago'][0]['Monto'] == Decimal("3245.12")
237
238
    verify_invoice(invoice, f"pago_p_{xml_file}")
239
240
241
@pytest.mark.parametrize('rfc, xml_file, traslados, retenciones, total, traslado_incluido', invoices)
242
def test_create_pago_multiple(rfc, xml_file, traslados, retenciones, total, traslado_incluido):
243
    signer = get_signer(rfc)
244
    emisor = cfdi40.Issuer(signer=signer, tax_system="606")
245
246
    ingreso_invoice = CFDI.from_file(os.path.join(current_dir, f"{current_filename}/{xml_file}_stamped.xml"))
247
248
    invoice = cfdi40.Comprobante.pago_comprobantes(
249
        emisor=emisor,
250
        lugar_expedicion="56820",
251
        fecha=datetime.fromisoformat("2020-01-01T22:40:38"),
252
        comprobantes=[ingreso_invoice, ingreso_invoice],
253
        fecha_pago=datetime.fromisoformat("2020-01-02T22:40:38"),
254
        forma_pago="03",
255
        serie="A",
256
        folio="123456",
257
    )
258
259
    assert invoice['Complemento']['Pago'][0]['Monto'] == Decimal(total) * 2
260
261
    verify_invoice(invoice, f"mpago_{xml_file}")
262
263
264
def test_nomina():
265
    xml_file = "invoice_nomina"
266
267
    signer = get_signer('xiqb891116qe4')
268
    emisor = cfdi40.Issuer(signer=signer, tax_system="606")
269
270
    invoice = cfdi40.Comprobante.nomina(
271
        emisor=emisor,
272
        receptor=cfdi40.Receptor(
273
            rfc='KIJ0906199R1',
274
            nombre='KIJ, S.A DE C.V.',
275
            uso_cfdi='G03',
276
            domicilio_fiscal_receptor="59820",
277
            regimen_fiscal_receptor="601"
278
        ),
279
        lugar_expedicion="56820",
280
        complemento_nomina=nomina12.Nomina(
281
            emisor={
282
                'RegistroPatronal': 'Z1234567890'
283
            },
284
            receptor={
285
                'Curp': 'XIQB891116MCHZRL72',
286
                'NumSeguridadSocial': '987654321',
287
                'FechaInicioRelLaboral': date(2020, 1, 1),
288
                'Antigüedad': 'P96W',
289
                'TipoContrato': '01',
290
                'Sindicalizado': 'No',
291
                'TipoJornada': '01',
292
                'TipoRegimen': '02',
293
                'NumEmpleado': '12345678',
294
                'Departamento': 'Departamento del Trabajo',
295
                'Puesto': 'Trabajador',
296
                'RiesgoPuesto': '2',
297
                'PeriodicidadPago': '04',
298
                'CuentaBancaria': '0001000200030004',
299
                'SalarioDiarioIntegrado': Decimal('100.01'),
300
                'ClaveEntFed': 'MOR'
301
            },
302
            percepciones={
303
                'Percepcion': [
304
                    {
305
                        'TipoPercepcion': '001',
306
                        'Clave': '001',
307
                        'Concepto': 'SUELDO',
308
                        'ImporteGravado': Decimal('1200'),
309
                        'ImporteExento': Decimal('400')
310
                    }
311
                ]
312
            },
313
            deducciones={
314
                'Deduccion': [
315
                    {
316
                        'TipoDeduccion': '002',
317
                        'Clave': '300',
318
                        'Concepto': 'ISR A CARGO',
319
                        'Importe': Decimal('1234.73')
320
                    },
321
                    {
322
                        'TipoDeduccion': '004',
323
                        'Clave': '204',
324
                        'Concepto': 'FONDO DE AHORRO EMPLEADOS',
325
                        'Importe': Decimal('521.10')
326
                    },
327
                ]
328
            },
329
            otros_pagos=[
330
                {
331
                    'TipoOtroPago': '999',
332
                    'Clave': '046',
333
                    'Concepto': 'REEMBOLSO DE GASTOS',
334
                    'Importe': Decimal('456')
335
                },
336
                {
337
                    'SubsidioAlEmpleo': Decimal('0'),
338
                    'TipoOtroPago': '002',
339
                    'Clave': '002',
340
                    'Concepto': 'SUBSIDIO EMPLEO',
341
                    'Importe': Decimal('0')
342
                }
343
            ],
344
            incapacidades=[
345
                {
346
                    "DiasIncapacidad": 2,
347
                    'TipoIncapacidad': '02',
348
                    'ImporteMonetario': Decimal(1000),
349
                }
350
            ],
351
            tipo_nomina='O',
352
            fecha_pago=date(2020, 1, 30),
353
            fecha_final_pago=date(2020, 1, 31),
354
            fecha_inicial_pago=date(2020, 1, 16),
355
            num_dias_pagados=Decimal('16.000')
356
        ),
357
        serie="A",
358
        folio="123456",
359
        fecha=datetime.fromisoformat("2020-09-29T22:40:38")
360
    )
361
362
    verify_invoice(invoice, f"{xml_file}")
363