fiscalapi 4.0.386__tar.gz → 4.0.417__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {fiscalapi-4.0.386/fiscalapi.egg-info → fiscalapi-4.0.417}/PKG-INFO +231 -9
- {fiscalapi-4.0.386 → fiscalapi-4.0.417}/README.md +230 -8
- {fiscalapi-4.0.386 → fiscalapi-4.0.417}/fiscalapi/__init__.py +26 -2
- {fiscalapi-4.0.386 → fiscalapi-4.0.417}/fiscalapi/models/__init__.py +30 -7
- {fiscalapi-4.0.386 → fiscalapi-4.0.417}/fiscalapi/models/carta_porte_models.py +12 -12
- {fiscalapi-4.0.386 → fiscalapi-4.0.417}/fiscalapi/models/comercio_exterior_models.py +2 -2
- {fiscalapi-4.0.386 → fiscalapi-4.0.417}/fiscalapi/models/common_models.py +1 -5
- {fiscalapi-4.0.386 → fiscalapi-4.0.417}/fiscalapi/models/fiscalapi_models.py +73 -89
- fiscalapi-4.0.417/fiscalapi/models/sat_validation_models.py +96 -0
- {fiscalapi-4.0.386 → fiscalapi-4.0.417}/fiscalapi/services/__init__.py +3 -1
- {fiscalapi-4.0.386 → fiscalapi-4.0.417}/fiscalapi/services/base_service.py +10 -8
- {fiscalapi-4.0.386 → fiscalapi-4.0.417}/fiscalapi/services/fiscalapi_client.py +2 -0
- fiscalapi-4.0.417/fiscalapi/services/sat_validation_service.py +90 -0
- {fiscalapi-4.0.386 → fiscalapi-4.0.417}/fiscalapi/services/stamp_service.py +9 -2
- {fiscalapi-4.0.386 → fiscalapi-4.0.417/fiscalapi.egg-info}/PKG-INFO +231 -9
- {fiscalapi-4.0.386 → fiscalapi-4.0.417}/fiscalapi.egg-info/SOURCES.txt +2 -0
- {fiscalapi-4.0.386 → fiscalapi-4.0.417}/setup.py +1 -1
- {fiscalapi-4.0.386 → fiscalapi-4.0.417}/LICENSE.txt +0 -0
- {fiscalapi-4.0.386 → fiscalapi-4.0.417}/MANIFEST.in +0 -0
- {fiscalapi-4.0.386 → fiscalapi-4.0.417}/fiscalapi/models/manifest_models.py +0 -0
- {fiscalapi-4.0.386 → fiscalapi-4.0.417}/fiscalapi/py.typed +0 -0
- {fiscalapi-4.0.386 → fiscalapi-4.0.417}/fiscalapi/services/api_key_service.py +0 -0
- {fiscalapi-4.0.386 → fiscalapi-4.0.417}/fiscalapi/services/catalog_service.py +0 -0
- {fiscalapi-4.0.386 → fiscalapi-4.0.417}/fiscalapi/services/download_catalog_service.py +0 -0
- {fiscalapi-4.0.386 → fiscalapi-4.0.417}/fiscalapi/services/download_request_service.py +0 -0
- {fiscalapi-4.0.386 → fiscalapi-4.0.417}/fiscalapi/services/download_rule_service.py +0 -0
- {fiscalapi-4.0.386 → fiscalapi-4.0.417}/fiscalapi/services/employee_service.py +0 -0
- {fiscalapi-4.0.386 → fiscalapi-4.0.417}/fiscalapi/services/employer_service.py +0 -0
- {fiscalapi-4.0.386 → fiscalapi-4.0.417}/fiscalapi/services/invoice_service.py +0 -0
- {fiscalapi-4.0.386 → fiscalapi-4.0.417}/fiscalapi/services/manifest_service.py +0 -0
- {fiscalapi-4.0.386 → fiscalapi-4.0.417}/fiscalapi/services/people_service.py +0 -0
- {fiscalapi-4.0.386 → fiscalapi-4.0.417}/fiscalapi/services/product_service.py +0 -0
- {fiscalapi-4.0.386 → fiscalapi-4.0.417}/fiscalapi/services/tax_file_service.py +0 -0
- {fiscalapi-4.0.386 → fiscalapi-4.0.417}/fiscalapi.egg-info/dependency_links.txt +0 -0
- {fiscalapi-4.0.386 → fiscalapi-4.0.417}/fiscalapi.egg-info/requires.txt +0 -0
- {fiscalapi-4.0.386 → fiscalapi-4.0.417}/fiscalapi.egg-info/top_level.txt +0 -0
- {fiscalapi-4.0.386 → fiscalapi-4.0.417}/pyproject.toml +0 -0
- {fiscalapi-4.0.386 → fiscalapi-4.0.417}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: fiscalapi
|
|
3
|
-
Version: 4.0.
|
|
3
|
+
Version: 4.0.417
|
|
4
4
|
Summary: Genera facturas CFDI válidas ante el SAT consumiendo el API de https://www.fiscalapi.com
|
|
5
5
|
Home-page: https://www.fiscalapi.com
|
|
6
6
|
Author: Fiscalapi
|
|
@@ -76,10 +76,7 @@ Dynamic: summary
|
|
|
76
76
|
- **Configuración de datos fiscales** (RFC, domicilio fiscal, régimen fiscal)
|
|
77
77
|
- **Datos de empleado** (agrega/actualiza/elimina datos de empleado a una persona. CFDI Nómina)
|
|
78
78
|
- **Datos de empleador** (agrega/actualiza/elimina datos de empleador a una persona. CFDI Nómina)
|
|
79
|
-
|
|
80
|
-
## 🎖️ Gestión de Timbres
|
|
81
|
-
- **Gestión de folios fiscales** Compra timbres a fiscalapi y transfiere/retira a las personas de tu organizacion segun tus reglas de negocio.
|
|
82
|
-
|
|
79
|
+
- **Firma de carta manifiesto** (firma el manifiesto de una persona con su e.firma/FIEL)
|
|
83
80
|
|
|
84
81
|
## 🛍️ Gestión de Productos/Servicios
|
|
85
82
|
- **Gestión de productos y servicios** con catálogo personalizable
|
|
@@ -91,11 +88,21 @@ Dynamic: summary
|
|
|
91
88
|
- **Búsqueda de información** en catálogos del SAT con filtros avanzados
|
|
92
89
|
- **Acceso y búsqueda** en catálogos completos
|
|
93
90
|
|
|
94
|
-
## 🎫 Gestión de Timbres
|
|
95
|
-
- **
|
|
91
|
+
## 🎫 Gestión de Timbres y Créditos de Validación
|
|
92
|
+
- **Gestión de folios fiscales**: compra timbres a FiscalAPI y transfiere/retira a las personas de tu organización según tus reglas de negocio
|
|
93
|
+
- **Listar transacciones** del ledger con paginación
|
|
96
94
|
- **Consultar transacciones** por ID
|
|
97
95
|
- **Transferir timbres** entre personas
|
|
98
96
|
- **Retirar timbres** de una persona
|
|
97
|
+
- **Transferir y retirar créditos de validación SAT** con `credit_type=CreditType.VALIDATION`
|
|
98
|
+
|
|
99
|
+
## 🛡️ Validaciones SAT
|
|
100
|
+
- **Estructura del XML** contra el Anexo 20 y los complementos declarados
|
|
101
|
+
- **Vigencia del certificado** del emisor a la fecha de emisión
|
|
102
|
+
- **Sello del CFDI** y **sello del SAT** en el Timbre Fiscal Digital
|
|
103
|
+
- **Estatus del comprobante** en el SAT (vigente, cancelado, no encontrado)
|
|
104
|
+
- **Listas negras del artículo 69-B y 69-B Bis** del CFF, por CFDI o por RFC
|
|
105
|
+
- **Catálogo de tipos de validación** y de los estatus que cada uno puede tomar
|
|
99
106
|
|
|
100
107
|
## 📖 Recursos Adicionales
|
|
101
108
|
- **Cientos de ejemplos de código** disponibles en múltiples lenguajes de programación
|
|
@@ -357,6 +364,211 @@ else:
|
|
|
357
364
|
print(api_response.message)
|
|
358
365
|
```
|
|
359
366
|
|
|
367
|
+
### 8. Validaciones SAT
|
|
368
|
+
|
|
369
|
+
Cada tipo de validación solicitado consume un crédito de validación. El cobro es todo o nada: si el saldo no alcanza para todos, no se ejecuta ninguno y la API responde 403.
|
|
370
|
+
|
|
371
|
+
```python
|
|
372
|
+
import base64
|
|
373
|
+
from pathlib import Path
|
|
374
|
+
from fiscalapi import SatValidationRequest, SatValidationTypeIds
|
|
375
|
+
|
|
376
|
+
# Validar un CFDI timbrado: con xml puedes solicitar cualquier tipo de validación
|
|
377
|
+
request = SatValidationRequest(
|
|
378
|
+
xml=base64.b64encode(Path("factura.xml").read_bytes()).decode("ascii"),
|
|
379
|
+
validation_types=[
|
|
380
|
+
SatValidationTypeIds.XML_STRUCTURE,
|
|
381
|
+
SatValidationTypeIds.CFDI_SELLO,
|
|
382
|
+
SatValidationTypeIds.CFDI_STATUS,
|
|
383
|
+
SatValidationTypeIds.BLACKLIST_69B,
|
|
384
|
+
]
|
|
385
|
+
)
|
|
386
|
+
|
|
387
|
+
api_response = client.sat_validations.validate(request)
|
|
388
|
+
|
|
389
|
+
if api_response.succeeded:
|
|
390
|
+
for result in api_response.data:
|
|
391
|
+
veredicto = "PASSED" if result.passed else "FAILED"
|
|
392
|
+
print(f"[{veredicto}] {result.type.id.value} -> {result.status.id.value}")
|
|
393
|
+
if result.status.details:
|
|
394
|
+
print(f" {result.status.details}")
|
|
395
|
+
else:
|
|
396
|
+
print(api_response.details)
|
|
397
|
+
```
|
|
398
|
+
|
|
399
|
+
Para consultar únicamente listas negras basta el RFC, sin enviar el CFDI:
|
|
400
|
+
|
|
401
|
+
```python
|
|
402
|
+
request = SatValidationRequest(
|
|
403
|
+
tin="FUNK671228PH6",
|
|
404
|
+
validation_types=[
|
|
405
|
+
SatValidationTypeIds.BLACKLIST_69B,
|
|
406
|
+
SatValidationTypeIds.BLACKLIST_69B_BIS,
|
|
407
|
+
]
|
|
408
|
+
)
|
|
409
|
+
|
|
410
|
+
api_response = client.sat_validations.validate(request)
|
|
411
|
+
```
|
|
412
|
+
|
|
413
|
+
Envía `xml` o `tin`, nunca ambos y nunca ninguno: con `tin` solo se pueden solicitar listas negras.
|
|
414
|
+
|
|
415
|
+
El catálogo de tipos y los estatus que cada tipo puede tomar se consultan sin consumir créditos:
|
|
416
|
+
|
|
417
|
+
```python
|
|
418
|
+
api_response = client.sat_validations.get_types()
|
|
419
|
+
api_response = client.sat_validations.get_type_by_id(SatValidationTypeIds.CFDI_STATUS)
|
|
420
|
+
api_response = client.sat_validations.get_statuses(SatValidationTypeIds.CFDI_STATUS)
|
|
421
|
+
```
|
|
422
|
+
|
|
423
|
+
### 9. Transferir Créditos de Validación
|
|
424
|
+
|
|
425
|
+
Los saldos nunca se mezclan: `CreditType.STAMP` mueve timbres (`available_balance`) y `CreditType.VALIDATION` mueve créditos de validación (`available_validation_balance`).
|
|
426
|
+
|
|
427
|
+
```python
|
|
428
|
+
from fiscalapi import CreditType, StampTransactionParams
|
|
429
|
+
|
|
430
|
+
params = StampTransactionParams(
|
|
431
|
+
from_person_id="3f3478b4-60fd-459e-8bfc-f8239fc96257",
|
|
432
|
+
to_person_id="96b46762-d246-4a67-a562-510a25dbafa9",
|
|
433
|
+
amount=10,
|
|
434
|
+
comments="Asignación de créditos de validación",
|
|
435
|
+
credit_type=CreditType.VALIDATION
|
|
436
|
+
)
|
|
437
|
+
|
|
438
|
+
api_response = client.stamps.transfer_stamps(params)
|
|
439
|
+
```
|
|
440
|
+
|
|
441
|
+
### 10. Factura de Comercio Exterior
|
|
442
|
+
|
|
443
|
+
El complemento se envía en `Invoice.complement.comercio_exterior`. Puede combinarse con carta porte en el mismo comprobante.
|
|
444
|
+
|
|
445
|
+
> **Escala decimal:** el SAT valida la cantidad de decimales de varios campos. Construye siempre los montos con `Decimal("...")` a partir de una cadena con la escala exacta (`Decimal("0.160000")`, no `Decimal(0.16)`): el SDK preserva los ceros finales al serializar. Una escala incorrecta produce rechazos `CFDI40179` (en `taxRate`) o `CCE122` (en `valorDolares`).
|
|
446
|
+
|
|
447
|
+
> **Tipo de cambio:** `tipo_cambio_usd` debe ser el publicado en el DOF para la fecha de emisión, y la fecha del comprobante no puede exceder 72 horas al momento del timbrado. Si no coinciden, el PAC responde `CCE121` e indica el valor esperado.
|
|
448
|
+
|
|
449
|
+
```python
|
|
450
|
+
from datetime import datetime
|
|
451
|
+
from decimal import Decimal
|
|
452
|
+
|
|
453
|
+
from fiscalapi.models import (
|
|
454
|
+
Invoice, InvoiceIssuer, InvoiceRecipient, InvoiceItem, ItemTax,
|
|
455
|
+
InvoiceComplement, TaxCredential,
|
|
456
|
+
ComercioExteriorComplement, ComercioExteriorEmisor, ComercioExteriorEmisorDomicilio,
|
|
457
|
+
ComercioExteriorReceptor, ComercioExteriorReceptorDomicilio, ComercioExteriorMercancia,
|
|
458
|
+
)
|
|
459
|
+
|
|
460
|
+
invoice = Invoice(
|
|
461
|
+
version_code="4.0",
|
|
462
|
+
payment_form_code="99",
|
|
463
|
+
payment_method_code="PPD",
|
|
464
|
+
currency_code="USD",
|
|
465
|
+
type_code="I",
|
|
466
|
+
expedition_zip_code="42501",
|
|
467
|
+
series="CCE",
|
|
468
|
+
date=datetime.now().replace(microsecond=0),
|
|
469
|
+
export_code="02",
|
|
470
|
+
issuer=InvoiceIssuer(
|
|
471
|
+
tin="EKU9003173C9",
|
|
472
|
+
legal_name="ESCUELA KEMPER URGATE",
|
|
473
|
+
tax_regime_code="601",
|
|
474
|
+
tax_credentials=[
|
|
475
|
+
TaxCredential(base64_file="<CER_BASE64>", file_type=0, password="<CSD_PASSWORD>"),
|
|
476
|
+
TaxCredential(base64_file="<KEY_BASE64>", file_type=1, password="<CSD_PASSWORD>"),
|
|
477
|
+
],
|
|
478
|
+
),
|
|
479
|
+
# Receptor extranjero: country_id (ResidenciaFiscal) es obligatorio si se envía foreign_tin.
|
|
480
|
+
recipient=InvoiceRecipient(
|
|
481
|
+
tin="XEXX010101000",
|
|
482
|
+
legal_name="U.S. 0026 SW",
|
|
483
|
+
zip_code="42501",
|
|
484
|
+
tax_regime_code="616",
|
|
485
|
+
cfdi_use_code="S01",
|
|
486
|
+
country_id="USA",
|
|
487
|
+
foreign_tin="123456789",
|
|
488
|
+
),
|
|
489
|
+
items=[
|
|
490
|
+
InvoiceItem(
|
|
491
|
+
item_code="50211503",
|
|
492
|
+
item_sku="131494-1055", # debe coincidir con mercancia.no_identificacion
|
|
493
|
+
quantity=Decimal("2"),
|
|
494
|
+
unit_of_measurement_code="H87",
|
|
495
|
+
description="Cigarros",
|
|
496
|
+
unit_price=Decimal("200.00"),
|
|
497
|
+
discount=Decimal("0"),
|
|
498
|
+
tax_object_code="02",
|
|
499
|
+
item_taxes=[
|
|
500
|
+
ItemTax(tax_code="002", tax_type_code="Tasa", tax_rate=Decimal("0.160000"), tax_flag_code="T"),
|
|
501
|
+
],
|
|
502
|
+
),
|
|
503
|
+
],
|
|
504
|
+
complement=InvoiceComplement(
|
|
505
|
+
comercio_exterior=ComercioExteriorComplement(
|
|
506
|
+
clave_de_pedimento_id="A1",
|
|
507
|
+
certificado_origen=0, # 0 = no funge como certificado de origen, 1 = sí
|
|
508
|
+
incoterm_id="FOB",
|
|
509
|
+
tipo_cambio_usd=Decimal("16.9722"),
|
|
510
|
+
# Domicilio del emisor: campos de catálogo SAT, con sufijo Id.
|
|
511
|
+
emisor=ComercioExteriorEmisor(
|
|
512
|
+
domicilio=ComercioExteriorEmisorDomicilio(
|
|
513
|
+
calle="CALLE DEL PAPEL",
|
|
514
|
+
colonia_id="0214",
|
|
515
|
+
localidad_id="01",
|
|
516
|
+
municipio_id="014",
|
|
517
|
+
estado_id="QUE",
|
|
518
|
+
pais_id="MEX",
|
|
519
|
+
codigo_postal_id="76199",
|
|
520
|
+
),
|
|
521
|
+
),
|
|
522
|
+
# Domicilio del receptor: texto libre, sin sufijo Id (salvo pais_id).
|
|
523
|
+
receptor=ComercioExteriorReceptor(
|
|
524
|
+
num_reg_id_trib="123456789",
|
|
525
|
+
domicilio=ComercioExteriorReceptorDomicilio(
|
|
526
|
+
calle="ST. A",
|
|
527
|
+
estado="TX",
|
|
528
|
+
pais_id="USA",
|
|
529
|
+
codigo_postal="00000",
|
|
530
|
+
),
|
|
531
|
+
),
|
|
532
|
+
mercancias=[
|
|
533
|
+
ComercioExteriorMercancia(
|
|
534
|
+
no_identificacion="131494-1055",
|
|
535
|
+
fraccion_arancelaria_id="2402200100",
|
|
536
|
+
cantidad_aduana=Decimal("117.64"),
|
|
537
|
+
unidad_aduana_id="01",
|
|
538
|
+
valor_unitario_aduana=Decimal("3.40"),
|
|
539
|
+
valor_dolares=Decimal("400.00"),
|
|
540
|
+
),
|
|
541
|
+
],
|
|
542
|
+
),
|
|
543
|
+
),
|
|
544
|
+
)
|
|
545
|
+
|
|
546
|
+
api_response = client.invoices.create(invoice)
|
|
547
|
+
```
|
|
548
|
+
|
|
549
|
+
### 11. Firmar Carta Manifiesto
|
|
550
|
+
|
|
551
|
+
Firma la carta manifiesto de una persona y devuelve el PDF resultante en base64.
|
|
552
|
+
|
|
553
|
+
Requiere la **e.firma (FIEL)** del contribuyente, no el CSD de timbrado, y que exista una persona con ese RFC en el tenant.
|
|
554
|
+
|
|
555
|
+
```python
|
|
556
|
+
from fiscalapi.models import SignManifestRequest
|
|
557
|
+
|
|
558
|
+
api_response = client.manifests.sign(SignManifestRequest(
|
|
559
|
+
base64_cer="<FIEL_CER_BASE64>",
|
|
560
|
+
base64_key="<FIEL_KEY_BASE64>",
|
|
561
|
+
password="<FIEL_PASSWORD>",
|
|
562
|
+
))
|
|
563
|
+
|
|
564
|
+
if api_response.succeeded:
|
|
565
|
+
print(api_response.data.file_name) # EKU9003173C9.pdf
|
|
566
|
+
print(api_response.data.file_extension) # .pdf
|
|
567
|
+
print(api_response.data.base64_file) # PDF firmado en base64
|
|
568
|
+
```
|
|
569
|
+
|
|
570
|
+
Tras una firma exitosa, `client.people.get_by_id(...)` refleja el nuevo estado en `manifest_status_id`.
|
|
571
|
+
|
|
360
572
|
## 📋 Operaciones Principales
|
|
361
573
|
|
|
362
574
|
- **Facturas (CFDI)**
|
|
@@ -365,16 +577,26 @@ else:
|
|
|
365
577
|
Alta y administración de personas, gestión de certificados (CSD).
|
|
366
578
|
- **Productos y Servicios**
|
|
367
579
|
Administración de catálogos de productos, búsqueda en catálogos SAT.
|
|
368
|
-
- **Timbres**
|
|
369
|
-
Listar transacciones, transferir y retirar timbres entre personas.
|
|
580
|
+
- **Timbres y créditos de validación**
|
|
581
|
+
Listar transacciones, transferir y retirar timbres o créditos de validación entre personas.
|
|
582
|
+
- **Validaciones SAT**
|
|
583
|
+
Validar estructura, certificado, sellos, estatus en el SAT y listas negras 69-B de un CFDI o un RFC.
|
|
584
|
+
- **Carta manifiesto**
|
|
585
|
+
Firmar la carta manifiesto de una persona con su e.firma (FIEL) y obtener el PDF resultante.
|
|
370
586
|
|
|
371
587
|
## 📂 Más Ejemplos
|
|
372
588
|
|
|
373
589
|
- [Gestión de Timbres](examples/ejemplos-timbres.py)
|
|
590
|
+
- [Validaciones SAT](examples/ejemplos-validaciones-sat.py)
|
|
374
591
|
- [Complementos de Pago](examples/ejemplos-facturas-de-complemento-pago.py)
|
|
375
592
|
- [Facturas de Nómina](examples/ejemplos-facturas-de-nomina.py)
|
|
376
593
|
- [Impuestos Locales (Por Valores)](examples/ejemplos-factura-impuestos-locales-valores.py)
|
|
377
594
|
- [Impuestos Locales (Por Referencias)](examples/ejemplos-factura-impuestos-locales-referencias.py)
|
|
595
|
+
- [Comercio Exterior (Por Valores)](examples/ejemplos-factura-comercio-exterior-valores.py)
|
|
596
|
+
- [Comercio Exterior (Por Referencias)](examples/ejemplos-factura-comercio-exterior-referencias.py)
|
|
597
|
+
- [Carta Porte (Por Valores)](examples/ejemplos-factura-carta-porte-valores.py)
|
|
598
|
+
- [Carta Porte (Por Referencias)](examples/ejemplos-factura-carta-porte-referencias.py)
|
|
599
|
+
- [Firma de Cartas Manifiesto](examples/ejemplos-firma-manifiestos.py)
|
|
378
600
|
|
|
379
601
|
## 🤝 Contribuir
|
|
380
602
|
|
|
@@ -38,10 +38,7 @@
|
|
|
38
38
|
- **Configuración de datos fiscales** (RFC, domicilio fiscal, régimen fiscal)
|
|
39
39
|
- **Datos de empleado** (agrega/actualiza/elimina datos de empleado a una persona. CFDI Nómina)
|
|
40
40
|
- **Datos de empleador** (agrega/actualiza/elimina datos de empleador a una persona. CFDI Nómina)
|
|
41
|
-
|
|
42
|
-
## 🎖️ Gestión de Timbres
|
|
43
|
-
- **Gestión de folios fiscales** Compra timbres a fiscalapi y transfiere/retira a las personas de tu organizacion segun tus reglas de negocio.
|
|
44
|
-
|
|
41
|
+
- **Firma de carta manifiesto** (firma el manifiesto de una persona con su e.firma/FIEL)
|
|
45
42
|
|
|
46
43
|
## 🛍️ Gestión de Productos/Servicios
|
|
47
44
|
- **Gestión de productos y servicios** con catálogo personalizable
|
|
@@ -53,11 +50,21 @@
|
|
|
53
50
|
- **Búsqueda de información** en catálogos del SAT con filtros avanzados
|
|
54
51
|
- **Acceso y búsqueda** en catálogos completos
|
|
55
52
|
|
|
56
|
-
## 🎫 Gestión de Timbres
|
|
57
|
-
- **
|
|
53
|
+
## 🎫 Gestión de Timbres y Créditos de Validación
|
|
54
|
+
- **Gestión de folios fiscales**: compra timbres a FiscalAPI y transfiere/retira a las personas de tu organización según tus reglas de negocio
|
|
55
|
+
- **Listar transacciones** del ledger con paginación
|
|
58
56
|
- **Consultar transacciones** por ID
|
|
59
57
|
- **Transferir timbres** entre personas
|
|
60
58
|
- **Retirar timbres** de una persona
|
|
59
|
+
- **Transferir y retirar créditos de validación SAT** con `credit_type=CreditType.VALIDATION`
|
|
60
|
+
|
|
61
|
+
## 🛡️ Validaciones SAT
|
|
62
|
+
- **Estructura del XML** contra el Anexo 20 y los complementos declarados
|
|
63
|
+
- **Vigencia del certificado** del emisor a la fecha de emisión
|
|
64
|
+
- **Sello del CFDI** y **sello del SAT** en el Timbre Fiscal Digital
|
|
65
|
+
- **Estatus del comprobante** en el SAT (vigente, cancelado, no encontrado)
|
|
66
|
+
- **Listas negras del artículo 69-B y 69-B Bis** del CFF, por CFDI o por RFC
|
|
67
|
+
- **Catálogo de tipos de validación** y de los estatus que cada uno puede tomar
|
|
61
68
|
|
|
62
69
|
## 📖 Recursos Adicionales
|
|
63
70
|
- **Cientos de ejemplos de código** disponibles en múltiples lenguajes de programación
|
|
@@ -319,6 +326,211 @@ else:
|
|
|
319
326
|
print(api_response.message)
|
|
320
327
|
```
|
|
321
328
|
|
|
329
|
+
### 8. Validaciones SAT
|
|
330
|
+
|
|
331
|
+
Cada tipo de validación solicitado consume un crédito de validación. El cobro es todo o nada: si el saldo no alcanza para todos, no se ejecuta ninguno y la API responde 403.
|
|
332
|
+
|
|
333
|
+
```python
|
|
334
|
+
import base64
|
|
335
|
+
from pathlib import Path
|
|
336
|
+
from fiscalapi import SatValidationRequest, SatValidationTypeIds
|
|
337
|
+
|
|
338
|
+
# Validar un CFDI timbrado: con xml puedes solicitar cualquier tipo de validación
|
|
339
|
+
request = SatValidationRequest(
|
|
340
|
+
xml=base64.b64encode(Path("factura.xml").read_bytes()).decode("ascii"),
|
|
341
|
+
validation_types=[
|
|
342
|
+
SatValidationTypeIds.XML_STRUCTURE,
|
|
343
|
+
SatValidationTypeIds.CFDI_SELLO,
|
|
344
|
+
SatValidationTypeIds.CFDI_STATUS,
|
|
345
|
+
SatValidationTypeIds.BLACKLIST_69B,
|
|
346
|
+
]
|
|
347
|
+
)
|
|
348
|
+
|
|
349
|
+
api_response = client.sat_validations.validate(request)
|
|
350
|
+
|
|
351
|
+
if api_response.succeeded:
|
|
352
|
+
for result in api_response.data:
|
|
353
|
+
veredicto = "PASSED" if result.passed else "FAILED"
|
|
354
|
+
print(f"[{veredicto}] {result.type.id.value} -> {result.status.id.value}")
|
|
355
|
+
if result.status.details:
|
|
356
|
+
print(f" {result.status.details}")
|
|
357
|
+
else:
|
|
358
|
+
print(api_response.details)
|
|
359
|
+
```
|
|
360
|
+
|
|
361
|
+
Para consultar únicamente listas negras basta el RFC, sin enviar el CFDI:
|
|
362
|
+
|
|
363
|
+
```python
|
|
364
|
+
request = SatValidationRequest(
|
|
365
|
+
tin="FUNK671228PH6",
|
|
366
|
+
validation_types=[
|
|
367
|
+
SatValidationTypeIds.BLACKLIST_69B,
|
|
368
|
+
SatValidationTypeIds.BLACKLIST_69B_BIS,
|
|
369
|
+
]
|
|
370
|
+
)
|
|
371
|
+
|
|
372
|
+
api_response = client.sat_validations.validate(request)
|
|
373
|
+
```
|
|
374
|
+
|
|
375
|
+
Envía `xml` o `tin`, nunca ambos y nunca ninguno: con `tin` solo se pueden solicitar listas negras.
|
|
376
|
+
|
|
377
|
+
El catálogo de tipos y los estatus que cada tipo puede tomar se consultan sin consumir créditos:
|
|
378
|
+
|
|
379
|
+
```python
|
|
380
|
+
api_response = client.sat_validations.get_types()
|
|
381
|
+
api_response = client.sat_validations.get_type_by_id(SatValidationTypeIds.CFDI_STATUS)
|
|
382
|
+
api_response = client.sat_validations.get_statuses(SatValidationTypeIds.CFDI_STATUS)
|
|
383
|
+
```
|
|
384
|
+
|
|
385
|
+
### 9. Transferir Créditos de Validación
|
|
386
|
+
|
|
387
|
+
Los saldos nunca se mezclan: `CreditType.STAMP` mueve timbres (`available_balance`) y `CreditType.VALIDATION` mueve créditos de validación (`available_validation_balance`).
|
|
388
|
+
|
|
389
|
+
```python
|
|
390
|
+
from fiscalapi import CreditType, StampTransactionParams
|
|
391
|
+
|
|
392
|
+
params = StampTransactionParams(
|
|
393
|
+
from_person_id="3f3478b4-60fd-459e-8bfc-f8239fc96257",
|
|
394
|
+
to_person_id="96b46762-d246-4a67-a562-510a25dbafa9",
|
|
395
|
+
amount=10,
|
|
396
|
+
comments="Asignación de créditos de validación",
|
|
397
|
+
credit_type=CreditType.VALIDATION
|
|
398
|
+
)
|
|
399
|
+
|
|
400
|
+
api_response = client.stamps.transfer_stamps(params)
|
|
401
|
+
```
|
|
402
|
+
|
|
403
|
+
### 10. Factura de Comercio Exterior
|
|
404
|
+
|
|
405
|
+
El complemento se envía en `Invoice.complement.comercio_exterior`. Puede combinarse con carta porte en el mismo comprobante.
|
|
406
|
+
|
|
407
|
+
> **Escala decimal:** el SAT valida la cantidad de decimales de varios campos. Construye siempre los montos con `Decimal("...")` a partir de una cadena con la escala exacta (`Decimal("0.160000")`, no `Decimal(0.16)`): el SDK preserva los ceros finales al serializar. Una escala incorrecta produce rechazos `CFDI40179` (en `taxRate`) o `CCE122` (en `valorDolares`).
|
|
408
|
+
|
|
409
|
+
> **Tipo de cambio:** `tipo_cambio_usd` debe ser el publicado en el DOF para la fecha de emisión, y la fecha del comprobante no puede exceder 72 horas al momento del timbrado. Si no coinciden, el PAC responde `CCE121` e indica el valor esperado.
|
|
410
|
+
|
|
411
|
+
```python
|
|
412
|
+
from datetime import datetime
|
|
413
|
+
from decimal import Decimal
|
|
414
|
+
|
|
415
|
+
from fiscalapi.models import (
|
|
416
|
+
Invoice, InvoiceIssuer, InvoiceRecipient, InvoiceItem, ItemTax,
|
|
417
|
+
InvoiceComplement, TaxCredential,
|
|
418
|
+
ComercioExteriorComplement, ComercioExteriorEmisor, ComercioExteriorEmisorDomicilio,
|
|
419
|
+
ComercioExteriorReceptor, ComercioExteriorReceptorDomicilio, ComercioExteriorMercancia,
|
|
420
|
+
)
|
|
421
|
+
|
|
422
|
+
invoice = Invoice(
|
|
423
|
+
version_code="4.0",
|
|
424
|
+
payment_form_code="99",
|
|
425
|
+
payment_method_code="PPD",
|
|
426
|
+
currency_code="USD",
|
|
427
|
+
type_code="I",
|
|
428
|
+
expedition_zip_code="42501",
|
|
429
|
+
series="CCE",
|
|
430
|
+
date=datetime.now().replace(microsecond=0),
|
|
431
|
+
export_code="02",
|
|
432
|
+
issuer=InvoiceIssuer(
|
|
433
|
+
tin="EKU9003173C9",
|
|
434
|
+
legal_name="ESCUELA KEMPER URGATE",
|
|
435
|
+
tax_regime_code="601",
|
|
436
|
+
tax_credentials=[
|
|
437
|
+
TaxCredential(base64_file="<CER_BASE64>", file_type=0, password="<CSD_PASSWORD>"),
|
|
438
|
+
TaxCredential(base64_file="<KEY_BASE64>", file_type=1, password="<CSD_PASSWORD>"),
|
|
439
|
+
],
|
|
440
|
+
),
|
|
441
|
+
# Receptor extranjero: country_id (ResidenciaFiscal) es obligatorio si se envía foreign_tin.
|
|
442
|
+
recipient=InvoiceRecipient(
|
|
443
|
+
tin="XEXX010101000",
|
|
444
|
+
legal_name="U.S. 0026 SW",
|
|
445
|
+
zip_code="42501",
|
|
446
|
+
tax_regime_code="616",
|
|
447
|
+
cfdi_use_code="S01",
|
|
448
|
+
country_id="USA",
|
|
449
|
+
foreign_tin="123456789",
|
|
450
|
+
),
|
|
451
|
+
items=[
|
|
452
|
+
InvoiceItem(
|
|
453
|
+
item_code="50211503",
|
|
454
|
+
item_sku="131494-1055", # debe coincidir con mercancia.no_identificacion
|
|
455
|
+
quantity=Decimal("2"),
|
|
456
|
+
unit_of_measurement_code="H87",
|
|
457
|
+
description="Cigarros",
|
|
458
|
+
unit_price=Decimal("200.00"),
|
|
459
|
+
discount=Decimal("0"),
|
|
460
|
+
tax_object_code="02",
|
|
461
|
+
item_taxes=[
|
|
462
|
+
ItemTax(tax_code="002", tax_type_code="Tasa", tax_rate=Decimal("0.160000"), tax_flag_code="T"),
|
|
463
|
+
],
|
|
464
|
+
),
|
|
465
|
+
],
|
|
466
|
+
complement=InvoiceComplement(
|
|
467
|
+
comercio_exterior=ComercioExteriorComplement(
|
|
468
|
+
clave_de_pedimento_id="A1",
|
|
469
|
+
certificado_origen=0, # 0 = no funge como certificado de origen, 1 = sí
|
|
470
|
+
incoterm_id="FOB",
|
|
471
|
+
tipo_cambio_usd=Decimal("16.9722"),
|
|
472
|
+
# Domicilio del emisor: campos de catálogo SAT, con sufijo Id.
|
|
473
|
+
emisor=ComercioExteriorEmisor(
|
|
474
|
+
domicilio=ComercioExteriorEmisorDomicilio(
|
|
475
|
+
calle="CALLE DEL PAPEL",
|
|
476
|
+
colonia_id="0214",
|
|
477
|
+
localidad_id="01",
|
|
478
|
+
municipio_id="014",
|
|
479
|
+
estado_id="QUE",
|
|
480
|
+
pais_id="MEX",
|
|
481
|
+
codigo_postal_id="76199",
|
|
482
|
+
),
|
|
483
|
+
),
|
|
484
|
+
# Domicilio del receptor: texto libre, sin sufijo Id (salvo pais_id).
|
|
485
|
+
receptor=ComercioExteriorReceptor(
|
|
486
|
+
num_reg_id_trib="123456789",
|
|
487
|
+
domicilio=ComercioExteriorReceptorDomicilio(
|
|
488
|
+
calle="ST. A",
|
|
489
|
+
estado="TX",
|
|
490
|
+
pais_id="USA",
|
|
491
|
+
codigo_postal="00000",
|
|
492
|
+
),
|
|
493
|
+
),
|
|
494
|
+
mercancias=[
|
|
495
|
+
ComercioExteriorMercancia(
|
|
496
|
+
no_identificacion="131494-1055",
|
|
497
|
+
fraccion_arancelaria_id="2402200100",
|
|
498
|
+
cantidad_aduana=Decimal("117.64"),
|
|
499
|
+
unidad_aduana_id="01",
|
|
500
|
+
valor_unitario_aduana=Decimal("3.40"),
|
|
501
|
+
valor_dolares=Decimal("400.00"),
|
|
502
|
+
),
|
|
503
|
+
],
|
|
504
|
+
),
|
|
505
|
+
),
|
|
506
|
+
)
|
|
507
|
+
|
|
508
|
+
api_response = client.invoices.create(invoice)
|
|
509
|
+
```
|
|
510
|
+
|
|
511
|
+
### 11. Firmar Carta Manifiesto
|
|
512
|
+
|
|
513
|
+
Firma la carta manifiesto de una persona y devuelve el PDF resultante en base64.
|
|
514
|
+
|
|
515
|
+
Requiere la **e.firma (FIEL)** del contribuyente, no el CSD de timbrado, y que exista una persona con ese RFC en el tenant.
|
|
516
|
+
|
|
517
|
+
```python
|
|
518
|
+
from fiscalapi.models import SignManifestRequest
|
|
519
|
+
|
|
520
|
+
api_response = client.manifests.sign(SignManifestRequest(
|
|
521
|
+
base64_cer="<FIEL_CER_BASE64>",
|
|
522
|
+
base64_key="<FIEL_KEY_BASE64>",
|
|
523
|
+
password="<FIEL_PASSWORD>",
|
|
524
|
+
))
|
|
525
|
+
|
|
526
|
+
if api_response.succeeded:
|
|
527
|
+
print(api_response.data.file_name) # EKU9003173C9.pdf
|
|
528
|
+
print(api_response.data.file_extension) # .pdf
|
|
529
|
+
print(api_response.data.base64_file) # PDF firmado en base64
|
|
530
|
+
```
|
|
531
|
+
|
|
532
|
+
Tras una firma exitosa, `client.people.get_by_id(...)` refleja el nuevo estado en `manifest_status_id`.
|
|
533
|
+
|
|
322
534
|
## 📋 Operaciones Principales
|
|
323
535
|
|
|
324
536
|
- **Facturas (CFDI)**
|
|
@@ -327,16 +539,26 @@ else:
|
|
|
327
539
|
Alta y administración de personas, gestión de certificados (CSD).
|
|
328
540
|
- **Productos y Servicios**
|
|
329
541
|
Administración de catálogos de productos, búsqueda en catálogos SAT.
|
|
330
|
-
- **Timbres**
|
|
331
|
-
Listar transacciones, transferir y retirar timbres entre personas.
|
|
542
|
+
- **Timbres y créditos de validación**
|
|
543
|
+
Listar transacciones, transferir y retirar timbres o créditos de validación entre personas.
|
|
544
|
+
- **Validaciones SAT**
|
|
545
|
+
Validar estructura, certificado, sellos, estatus en el SAT y listas negras 69-B de un CFDI o un RFC.
|
|
546
|
+
- **Carta manifiesto**
|
|
547
|
+
Firmar la carta manifiesto de una persona con su e.firma (FIEL) y obtener el PDF resultante.
|
|
332
548
|
|
|
333
549
|
## 📂 Más Ejemplos
|
|
334
550
|
|
|
335
551
|
- [Gestión de Timbres](examples/ejemplos-timbres.py)
|
|
552
|
+
- [Validaciones SAT](examples/ejemplos-validaciones-sat.py)
|
|
336
553
|
- [Complementos de Pago](examples/ejemplos-facturas-de-complemento-pago.py)
|
|
337
554
|
- [Facturas de Nómina](examples/ejemplos-facturas-de-nomina.py)
|
|
338
555
|
- [Impuestos Locales (Por Valores)](examples/ejemplos-factura-impuestos-locales-valores.py)
|
|
339
556
|
- [Impuestos Locales (Por Referencias)](examples/ejemplos-factura-impuestos-locales-referencias.py)
|
|
557
|
+
- [Comercio Exterior (Por Valores)](examples/ejemplos-factura-comercio-exterior-valores.py)
|
|
558
|
+
- [Comercio Exterior (Por Referencias)](examples/ejemplos-factura-comercio-exterior-referencias.py)
|
|
559
|
+
- [Carta Porte (Por Valores)](examples/ejemplos-factura-carta-porte-valores.py)
|
|
560
|
+
- [Carta Porte (Por Referencias)](examples/ejemplos-factura-carta-porte-referencias.py)
|
|
561
|
+
- [Firma de Cartas Manifiesto](examples/ejemplos-firma-manifiestos.py)
|
|
340
562
|
|
|
341
563
|
## 🤝 Contribuir
|
|
342
564
|
|
|
@@ -66,6 +66,15 @@ from .models.manifest_models import (
|
|
|
66
66
|
SignManifestRequest,
|
|
67
67
|
SignManifestResponse,
|
|
68
68
|
)
|
|
69
|
+
from .models.sat_validation_models import (
|
|
70
|
+
SatValidationTypeIds,
|
|
71
|
+
SatValidationStatusIds,
|
|
72
|
+
SatValidationRequest,
|
|
73
|
+
SatValidationType,
|
|
74
|
+
SatValidationTypeStatus,
|
|
75
|
+
SatValidationStatus,
|
|
76
|
+
SatValidationResult,
|
|
77
|
+
)
|
|
69
78
|
|
|
70
79
|
# Modelos de dominio
|
|
71
80
|
from .models.fiscalapi_models import (
|
|
@@ -111,7 +120,6 @@ from .models.fiscalapi_models import (
|
|
|
111
120
|
PayrollDeduction,
|
|
112
121
|
PayrollDisability,
|
|
113
122
|
PayrollComplement,
|
|
114
|
-
CartaPorteComplement,
|
|
115
123
|
InvoiceComplement,
|
|
116
124
|
# Invoice models
|
|
117
125
|
InvoiceResponse,
|
|
@@ -142,6 +150,9 @@ from .models.fiscalapi_models import (
|
|
|
142
150
|
XmlComplement,
|
|
143
151
|
Xml,
|
|
144
152
|
# Stamp models
|
|
153
|
+
CreditType,
|
|
154
|
+
StampTransactionType,
|
|
155
|
+
StampTransactionStatus,
|
|
145
156
|
UserLookupDto,
|
|
146
157
|
StampTransaction,
|
|
147
158
|
StampTransactionParams,
|
|
@@ -160,6 +171,7 @@ from .services.invoice_service import InvoiceService
|
|
|
160
171
|
from .services.people_service import PeopleService
|
|
161
172
|
from .services.product_service import ProductService
|
|
162
173
|
from .services.tax_file_service import TaxFileService
|
|
174
|
+
from .services.sat_validation_service import SatValidationService
|
|
163
175
|
from .services.stamp_service import StampService
|
|
164
176
|
from .services.manifest_service import ManifestService
|
|
165
177
|
|
|
@@ -216,7 +228,6 @@ __all__ = [
|
|
|
216
228
|
"PayrollDeduction",
|
|
217
229
|
"PayrollDisability",
|
|
218
230
|
"PayrollComplement",
|
|
219
|
-
"CartaPorteComplement",
|
|
220
231
|
"InvoiceComplement",
|
|
221
232
|
# Carta Porte models
|
|
222
233
|
"UbicacionDomicilio",
|
|
@@ -224,6 +235,7 @@ __all__ = [
|
|
|
224
235
|
"RegimenAduanero",
|
|
225
236
|
"Ubicacion",
|
|
226
237
|
"DocumentoAduanero",
|
|
238
|
+
"GuiaIdentificacion",
|
|
227
239
|
"CantidadTransporta",
|
|
228
240
|
"DetalleMercancia",
|
|
229
241
|
"Mercancia",
|
|
@@ -280,12 +292,23 @@ __all__ = [
|
|
|
280
292
|
"XmlComplement",
|
|
281
293
|
"Xml",
|
|
282
294
|
# Stamp models
|
|
295
|
+
"CreditType",
|
|
296
|
+
"StampTransactionType",
|
|
297
|
+
"StampTransactionStatus",
|
|
283
298
|
"UserLookupDto",
|
|
284
299
|
"StampTransaction",
|
|
285
300
|
"StampTransactionParams",
|
|
286
301
|
# Manifest models
|
|
287
302
|
"SignManifestRequest",
|
|
288
303
|
"SignManifestResponse",
|
|
304
|
+
# SAT validation models
|
|
305
|
+
"SatValidationTypeIds",
|
|
306
|
+
"SatValidationStatusIds",
|
|
307
|
+
"SatValidationRequest",
|
|
308
|
+
"SatValidationType",
|
|
309
|
+
"SatValidationTypeStatus",
|
|
310
|
+
"SatValidationStatus",
|
|
311
|
+
"SatValidationResult",
|
|
289
312
|
# Servicios
|
|
290
313
|
"BaseService",
|
|
291
314
|
"ApiKeyService",
|
|
@@ -299,6 +322,7 @@ __all__ = [
|
|
|
299
322
|
"PeopleService",
|
|
300
323
|
"ProductService",
|
|
301
324
|
"TaxFileService",
|
|
325
|
+
"SatValidationService",
|
|
302
326
|
"StampService",
|
|
303
327
|
"ManifestService",
|
|
304
328
|
# Cliente principal
|