fiscalapi 4.0.418__tar.gz → 4.0.439__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.
Files changed (45) hide show
  1. {fiscalapi-4.0.418/fiscalapi.egg-info → fiscalapi-4.0.439}/PKG-INFO +30 -7
  2. {fiscalapi-4.0.418 → fiscalapi-4.0.439}/README.md +29 -6
  3. {fiscalapi-4.0.418 → fiscalapi-4.0.439}/fiscalapi/__init__.py +30 -0
  4. {fiscalapi-4.0.418 → fiscalapi-4.0.439}/fiscalapi/models/__init__.py +28 -0
  5. {fiscalapi-4.0.418 → fiscalapi-4.0.439}/fiscalapi/models/common_models.py +3 -1
  6. {fiscalapi-4.0.418 → fiscalapi-4.0.439}/fiscalapi/models/fiscalapi_models.py +48 -27
  7. fiscalapi-4.0.439/fiscalapi/models/ticket_models.py +183 -0
  8. {fiscalapi-4.0.418 → fiscalapi-4.0.439}/fiscalapi/services/__init__.py +2 -0
  9. {fiscalapi-4.0.418 → fiscalapi-4.0.439}/fiscalapi/services/base_service.py +4 -0
  10. {fiscalapi-4.0.418 → fiscalapi-4.0.439}/fiscalapi/services/fiscalapi_client.py +2 -0
  11. fiscalapi-4.0.439/fiscalapi/services/ticket_service.py +102 -0
  12. {fiscalapi-4.0.418 → fiscalapi-4.0.439/fiscalapi.egg-info}/PKG-INFO +30 -7
  13. {fiscalapi-4.0.418 → fiscalapi-4.0.439}/fiscalapi.egg-info/SOURCES.txt +8 -1
  14. {fiscalapi-4.0.418 → fiscalapi-4.0.439}/pyproject.toml +3 -0
  15. {fiscalapi-4.0.418 → fiscalapi-4.0.439}/setup.py +1 -1
  16. fiscalapi-4.0.439/tests/test_cycle_contract.py +191 -0
  17. fiscalapi-4.0.439/tests/test_error_trace_identifier.py +94 -0
  18. fiscalapi-4.0.439/tests/test_invoice_item_ticket_id.py +167 -0
  19. fiscalapi-4.0.439/tests/test_response_tolerance.py +193 -0
  20. fiscalapi-4.0.439/tests/test_ticket_service.py +368 -0
  21. {fiscalapi-4.0.418 → fiscalapi-4.0.439}/LICENSE.txt +0 -0
  22. {fiscalapi-4.0.418 → fiscalapi-4.0.439}/MANIFEST.in +0 -0
  23. {fiscalapi-4.0.418 → fiscalapi-4.0.439}/fiscalapi/models/carta_porte_models.py +0 -0
  24. {fiscalapi-4.0.418 → fiscalapi-4.0.439}/fiscalapi/models/comercio_exterior_models.py +0 -0
  25. {fiscalapi-4.0.418 → fiscalapi-4.0.439}/fiscalapi/models/manifest_models.py +0 -0
  26. {fiscalapi-4.0.418 → fiscalapi-4.0.439}/fiscalapi/models/sat_validation_models.py +0 -0
  27. {fiscalapi-4.0.418 → fiscalapi-4.0.439}/fiscalapi/py.typed +0 -0
  28. {fiscalapi-4.0.418 → fiscalapi-4.0.439}/fiscalapi/services/api_key_service.py +0 -0
  29. {fiscalapi-4.0.418 → fiscalapi-4.0.439}/fiscalapi/services/catalog_service.py +0 -0
  30. {fiscalapi-4.0.418 → fiscalapi-4.0.439}/fiscalapi/services/download_catalog_service.py +0 -0
  31. {fiscalapi-4.0.418 → fiscalapi-4.0.439}/fiscalapi/services/download_request_service.py +0 -0
  32. {fiscalapi-4.0.418 → fiscalapi-4.0.439}/fiscalapi/services/download_rule_service.py +0 -0
  33. {fiscalapi-4.0.418 → fiscalapi-4.0.439}/fiscalapi/services/employee_service.py +0 -0
  34. {fiscalapi-4.0.418 → fiscalapi-4.0.439}/fiscalapi/services/employer_service.py +0 -0
  35. {fiscalapi-4.0.418 → fiscalapi-4.0.439}/fiscalapi/services/invoice_service.py +0 -0
  36. {fiscalapi-4.0.418 → fiscalapi-4.0.439}/fiscalapi/services/manifest_service.py +0 -0
  37. {fiscalapi-4.0.418 → fiscalapi-4.0.439}/fiscalapi/services/people_service.py +0 -0
  38. {fiscalapi-4.0.418 → fiscalapi-4.0.439}/fiscalapi/services/product_service.py +0 -0
  39. {fiscalapi-4.0.418 → fiscalapi-4.0.439}/fiscalapi/services/sat_validation_service.py +0 -0
  40. {fiscalapi-4.0.418 → fiscalapi-4.0.439}/fiscalapi/services/stamp_service.py +0 -0
  41. {fiscalapi-4.0.418 → fiscalapi-4.0.439}/fiscalapi/services/tax_file_service.py +0 -0
  42. {fiscalapi-4.0.418 → fiscalapi-4.0.439}/fiscalapi.egg-info/dependency_links.txt +0 -0
  43. {fiscalapi-4.0.418 → fiscalapi-4.0.439}/fiscalapi.egg-info/requires.txt +0 -0
  44. {fiscalapi-4.0.418 → fiscalapi-4.0.439}/fiscalapi.egg-info/top_level.txt +0 -0
  45. {fiscalapi-4.0.418 → fiscalapi-4.0.439}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fiscalapi
3
- Version: 4.0.418
3
+ Version: 4.0.439
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
@@ -87,13 +87,14 @@ Dynamic: summary
87
87
  - **Búsqueda de información** en catálogos del SAT con filtros avanzados
88
88
  - **Acceso y búsqueda** en catálogos completos
89
89
 
90
- ## 🎫 Gestión de Timbres y Créditos de Validación
90
+ ## 🎫 Gestión de Timbres, Créditos de Validación y Créditos de Ticket
91
91
  - **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
92
92
  - **Listar transacciones** del ledger con paginación
93
93
  - **Consultar transacciones** por ID
94
94
  - **Transferir timbres** entre personas
95
95
  - **Retirar timbres** de una persona
96
96
  - **Transferir y retirar créditos de validación SAT** con `credit_type=CreditType.VALIDATION`
97
+ - **Transferir y retirar créditos de ticket** con `credit_type=CreditType.TICKET`; su saldo se lee en `balances` de la persona
97
98
 
98
99
  ## 🛡️ Validaciones SAT
99
100
  - **Estructura del XML** contra el Anexo 20 y los complementos declarados
@@ -180,7 +181,7 @@ from fiscalapi import TaxFile
180
181
  # Subir certificado (CER)
181
182
  certificado_csd = TaxFile(
182
183
  person_id="3f3478b4-60fd-459e-8bfc-f8239fc96257",
183
- tin="FUNK671228PH6",
184
+ tin="FUNK671228PH6", # RFC de la persona (opcional)
184
185
  base64_file="MIIFgDCCA2igAwIBAgIUMzAwMDEwMDAwMDA1MDAwMDM0NDYwDQYJKo...",
185
186
  file_type=0, # 0 para certificado, 1 para llave privada
186
187
  password="12345678a"
@@ -189,7 +190,7 @@ certificado_csd = TaxFile(
189
190
  # Subir llave privada (KEY)
190
191
  clave_privada_csd = TaxFile(
191
192
  person_id="3f3478b4-60fd-459e-8bfc-f8239fc96257",
192
- tin="FUNK671228PH6",
193
+ tin="FUNK671228PH6", # RFC de la persona (opcional)
193
194
  base64_file="MIIFDjBABgkqhkiG9w0BBQ0wMzAbBgkqhkiG9w0BBQwwDgQIAg...",
194
195
  file_type=1,
195
196
  password="12345678a"
@@ -437,6 +438,25 @@ params = StampTransactionParams(
437
438
  api_response = client.stamps.transfer_stamps(params)
438
439
  ```
439
440
 
441
+ Los créditos de ticket se transfieren igual, con `credit_type=CreditType.TICKET`, y su saldo se lee en `balances` de la persona (un tipo ausente tiene saldo 0):
442
+
443
+ ```python
444
+ from fiscalapi import CreditType, StampTransactionParams
445
+
446
+ params = StampTransactionParams(
447
+ from_person_id="3f3478b4-60fd-459e-8bfc-f8239fc96257",
448
+ to_person_id="96b46762-d246-4a67-a562-510a25dbafa9",
449
+ amount=100,
450
+ comments="Asignación de créditos de ticket",
451
+ credit_type=CreditType.TICKET
452
+ )
453
+
454
+ api_response = client.stamps.transfer_stamps(params)
455
+
456
+ person = client.people.get_by_id("96b46762-d246-4a67-a562-510a25dbafa9").data
457
+ ticket_balance = next((b.available for b in person.balances or [] if b.credit_type == CreditType.TICKET), 0)
458
+ ```
459
+
440
460
  ### 10. Factura de Comercio Exterior
441
461
 
442
462
  El complemento se envía en `Invoice.complement.comercio_exterior`. Puede combinarse con carta porte en el mismo comprobante.
@@ -566,7 +586,7 @@ if api_response.succeeded:
566
586
  print(api_response.data.base64_file) # PDF firmado en base64
567
587
  ```
568
588
 
569
- Tras una firma exitosa, `client.people.get_by_id(...)` refleja el nuevo estado en `manifest_status_id`.
589
+ Tras una firma exitosa, todas las personas del tenant con el RFC del certificado quedan con `manifest_status_id` en `Signed`; `client.people.get_by_id(...)` lo refleja.
570
590
 
571
591
  ## 📋 Operaciones Principales
572
592
 
@@ -576,17 +596,20 @@ Tras una firma exitosa, `client.people.get_by_id(...)` refleja el nuevo estado e
576
596
  Alta y administración de personas, gestión de certificados (CSD).
577
597
  - **Productos y Servicios**
578
598
  Administración de catálogos de productos, búsqueda en catálogos SAT.
579
- - **Timbres y créditos de validación**
580
- Listar transacciones, transferir y retirar timbres o créditos de validación entre personas.
599
+ - **Timbres, créditos de validación y créditos de ticket**
600
+ Listar transacciones, transferir y retirar timbres, créditos de validación o créditos de ticket entre personas.
581
601
  - **Validaciones SAT**
582
602
  Validar estructura, certificado, sellos, estatus en el SAT y listas negras 69-B de un CFDI o un RFC.
583
603
  - **Carta manifiesto**
584
604
  Firmar la carta manifiesto de una persona con su e.firma (FIEL) y obtener el PDF resultante.
605
+ - **Tickets (Auto CFDI)**
606
+ Ingerir ventas pagadas del punto de venta con Idempotency-Key opcional, consultarlas con filtros, reimprimir su código y cancelarlas.
585
607
 
586
608
  ## 📂 Más Ejemplos
587
609
 
588
610
  - [Gestión de Timbres](examples/ejemplos-timbres.py)
589
611
  - [Validaciones SAT](examples/ejemplos-validaciones-sat.py)
612
+ - [Tickets de Auto CFDI](examples/ejemplos-tickets.py)
590
613
  - [Complementos de Pago](examples/ejemplos-facturas-de-complemento-pago.py)
591
614
  - [Facturas de Nómina](examples/ejemplos-facturas-de-nomina.py)
592
615
  - [Impuestos Locales (Por Valores)](examples/ejemplos-factura-impuestos-locales-valores.py)
@@ -50,13 +50,14 @@
50
50
  - **Búsqueda de información** en catálogos del SAT con filtros avanzados
51
51
  - **Acceso y búsqueda** en catálogos completos
52
52
 
53
- ## 🎫 Gestión de Timbres y Créditos de Validación
53
+ ## 🎫 Gestión de Timbres, Créditos de Validación y Créditos de Ticket
54
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
55
  - **Listar transacciones** del ledger con paginación
56
56
  - **Consultar transacciones** por ID
57
57
  - **Transferir timbres** entre personas
58
58
  - **Retirar timbres** de una persona
59
59
  - **Transferir y retirar créditos de validación SAT** con `credit_type=CreditType.VALIDATION`
60
+ - **Transferir y retirar créditos de ticket** con `credit_type=CreditType.TICKET`; su saldo se lee en `balances` de la persona
60
61
 
61
62
  ## 🛡️ Validaciones SAT
62
63
  - **Estructura del XML** contra el Anexo 20 y los complementos declarados
@@ -143,7 +144,7 @@ from fiscalapi import TaxFile
143
144
  # Subir certificado (CER)
144
145
  certificado_csd = TaxFile(
145
146
  person_id="3f3478b4-60fd-459e-8bfc-f8239fc96257",
146
- tin="FUNK671228PH6",
147
+ tin="FUNK671228PH6", # RFC de la persona (opcional)
147
148
  base64_file="MIIFgDCCA2igAwIBAgIUMzAwMDEwMDAwMDA1MDAwMDM0NDYwDQYJKo...",
148
149
  file_type=0, # 0 para certificado, 1 para llave privada
149
150
  password="12345678a"
@@ -152,7 +153,7 @@ certificado_csd = TaxFile(
152
153
  # Subir llave privada (KEY)
153
154
  clave_privada_csd = TaxFile(
154
155
  person_id="3f3478b4-60fd-459e-8bfc-f8239fc96257",
155
- tin="FUNK671228PH6",
156
+ tin="FUNK671228PH6", # RFC de la persona (opcional)
156
157
  base64_file="MIIFDjBABgkqhkiG9w0BBQ0wMzAbBgkqhkiG9w0BBQwwDgQIAg...",
157
158
  file_type=1,
158
159
  password="12345678a"
@@ -400,6 +401,25 @@ params = StampTransactionParams(
400
401
  api_response = client.stamps.transfer_stamps(params)
401
402
  ```
402
403
 
404
+ Los créditos de ticket se transfieren igual, con `credit_type=CreditType.TICKET`, y su saldo se lee en `balances` de la persona (un tipo ausente tiene saldo 0):
405
+
406
+ ```python
407
+ from fiscalapi import CreditType, StampTransactionParams
408
+
409
+ params = StampTransactionParams(
410
+ from_person_id="3f3478b4-60fd-459e-8bfc-f8239fc96257",
411
+ to_person_id="96b46762-d246-4a67-a562-510a25dbafa9",
412
+ amount=100,
413
+ comments="Asignación de créditos de ticket",
414
+ credit_type=CreditType.TICKET
415
+ )
416
+
417
+ api_response = client.stamps.transfer_stamps(params)
418
+
419
+ person = client.people.get_by_id("96b46762-d246-4a67-a562-510a25dbafa9").data
420
+ ticket_balance = next((b.available for b in person.balances or [] if b.credit_type == CreditType.TICKET), 0)
421
+ ```
422
+
403
423
  ### 10. Factura de Comercio Exterior
404
424
 
405
425
  El complemento se envía en `Invoice.complement.comercio_exterior`. Puede combinarse con carta porte en el mismo comprobante.
@@ -529,7 +549,7 @@ if api_response.succeeded:
529
549
  print(api_response.data.base64_file) # PDF firmado en base64
530
550
  ```
531
551
 
532
- Tras una firma exitosa, `client.people.get_by_id(...)` refleja el nuevo estado en `manifest_status_id`.
552
+ Tras una firma exitosa, todas las personas del tenant con el RFC del certificado quedan con `manifest_status_id` en `Signed`; `client.people.get_by_id(...)` lo refleja.
533
553
 
534
554
  ## 📋 Operaciones Principales
535
555
 
@@ -539,17 +559,20 @@ Tras una firma exitosa, `client.people.get_by_id(...)` refleja el nuevo estado e
539
559
  Alta y administración de personas, gestión de certificados (CSD).
540
560
  - **Productos y Servicios**
541
561
  Administración de catálogos de productos, búsqueda en catálogos SAT.
542
- - **Timbres y créditos de validación**
543
- Listar transacciones, transferir y retirar timbres o créditos de validación entre personas.
562
+ - **Timbres, créditos de validación y créditos de ticket**
563
+ Listar transacciones, transferir y retirar timbres, créditos de validación o créditos de ticket entre personas.
544
564
  - **Validaciones SAT**
545
565
  Validar estructura, certificado, sellos, estatus en el SAT y listas negras 69-B de un CFDI o un RFC.
546
566
  - **Carta manifiesto**
547
567
  Firmar la carta manifiesto de una persona con su e.firma (FIEL) y obtener el PDF resultante.
568
+ - **Tickets (Auto CFDI)**
569
+ Ingerir ventas pagadas del punto de venta con Idempotency-Key opcional, consultarlas con filtros, reimprimir su código y cancelarlas.
548
570
 
549
571
  ## 📂 Más Ejemplos
550
572
 
551
573
  - [Gestión de Timbres](examples/ejemplos-timbres.py)
552
574
  - [Validaciones SAT](examples/ejemplos-validaciones-sat.py)
575
+ - [Tickets de Auto CFDI](examples/ejemplos-tickets.py)
553
576
  - [Complementos de Pago](examples/ejemplos-facturas-de-complemento-pago.py)
554
577
  - [Facturas de Nómina](examples/ejemplos-facturas-de-nomina.py)
555
578
  - [Impuestos Locales (Por Valores)](examples/ejemplos-factura-impuestos-locales-valores.py)
@@ -75,6 +75,20 @@ from .models.sat_validation_models import (
75
75
  SatValidationStatus,
76
76
  SatValidationResult,
77
77
  )
78
+ # Modelos de tickets (Auto CFDI)
79
+ from .models.ticket_models import (
80
+ TicketStatus,
81
+ TicketEventType,
82
+ TicketActorType,
83
+ TicketIssuer,
84
+ TicketItemTax,
85
+ TicketItem,
86
+ TicketEvent,
87
+ Ticket,
88
+ TicketIngestionResult,
89
+ TicketCodeResult,
90
+ TicketListFilters,
91
+ )
78
92
 
79
93
  # Modelos de dominio
80
94
  from .models.fiscalapi_models import (
@@ -82,6 +96,7 @@ from .models.fiscalapi_models import (
82
96
  ProductTax,
83
97
  Product,
84
98
  # Person models
99
+ CreditBalance,
85
100
  Person,
86
101
  EmployeeData,
87
102
  EmployerData,
@@ -174,6 +189,7 @@ from .services.tax_file_service import TaxFileService
174
189
  from .services.sat_validation_service import SatValidationService
175
190
  from .services.stamp_service import StampService
176
191
  from .services.manifest_service import ManifestService
192
+ from .services.ticket_service import TicketService
177
193
 
178
194
  # Cliente principal
179
195
  from .services.fiscalapi_client import FiscalApiClient
@@ -190,6 +206,7 @@ __all__ = [
190
206
  "ProductTax",
191
207
  "Product",
192
208
  # Person models
209
+ "CreditBalance",
193
210
  "Person",
194
211
  "EmployeeData",
195
212
  "EmployerData",
@@ -309,6 +326,18 @@ __all__ = [
309
326
  "SatValidationTypeStatus",
310
327
  "SatValidationStatus",
311
328
  "SatValidationResult",
329
+ # Ticket models (Auto CFDI)
330
+ "TicketStatus",
331
+ "TicketEventType",
332
+ "TicketActorType",
333
+ "TicketIssuer",
334
+ "TicketItemTax",
335
+ "TicketItem",
336
+ "TicketEvent",
337
+ "Ticket",
338
+ "TicketIngestionResult",
339
+ "TicketCodeResult",
340
+ "TicketListFilters",
312
341
  # Servicios
313
342
  "BaseService",
314
343
  "ApiKeyService",
@@ -325,6 +354,7 @@ __all__ = [
325
354
  "SatValidationService",
326
355
  "StampService",
327
356
  "ManifestService",
357
+ "TicketService",
328
358
  # Cliente principal
329
359
  "FiscalApiClient",
330
360
  ]
@@ -61,11 +61,26 @@ from .sat_validation_models import (
61
61
  SatValidationStatus,
62
62
  SatValidationResult,
63
63
  )
64
+ from .ticket_models import (
65
+ # Ticket models (Auto CFDI)
66
+ TicketStatus,
67
+ TicketEventType,
68
+ TicketActorType,
69
+ TicketIssuer,
70
+ TicketItemTax,
71
+ TicketItem,
72
+ TicketEvent,
73
+ Ticket,
74
+ TicketIngestionResult,
75
+ TicketCodeResult,
76
+ TicketListFilters,
77
+ )
64
78
  from .fiscalapi_models import (
65
79
  # Product models
66
80
  ProductTax,
67
81
  Product,
68
82
  # Person models
83
+ CreditBalance,
69
84
  Person,
70
85
  EmployeeData,
71
86
  EmployerData,
@@ -188,6 +203,7 @@ __all__ = [
188
203
  "ProductTax",
189
204
  "Product",
190
205
  # Person models
206
+ "CreditBalance",
191
207
  "Person",
192
208
  "EmployeeData",
193
209
  "EmployerData",
@@ -273,4 +289,16 @@ __all__ = [
273
289
  "SatValidationTypeStatus",
274
290
  "SatValidationStatus",
275
291
  "SatValidationResult",
292
+ # Ticket models (Auto CFDI)
293
+ "TicketStatus",
294
+ "TicketEventType",
295
+ "TicketActorType",
296
+ "TicketIssuer",
297
+ "TicketItemTax",
298
+ "TicketItem",
299
+ "TicketEvent",
300
+ "Ticket",
301
+ "TicketIngestionResult",
302
+ "TicketCodeResult",
303
+ "TicketListFilters",
276
304
  ]
@@ -10,6 +10,8 @@ class ApiResponse(BaseModel, Generic[T]):
10
10
  details: Optional[str] = Field(default=None, alias="details")
11
11
  data: Optional[T] = Field(default=None, alias="data")
12
12
  http_status_code: Optional[int] = Field(default=None, alias="httpStatusCode")
13
+ trace_identifier: Optional[str] = Field(default=None, alias="traceIdentifier", description="Identificador de la petición en los registros de FiscalAPI. Viene en los errores (por ejemplo, un 500 o un 502): inclúyalo al escribir a soporte@fiscalapi.com.")
14
+ error_data: Optional[Any] = Field(default=None, alias="errorData", description="El data crudo de una respuesta de error (dict o lista tal cual), por ejemplo existingTicketId en el 409 de tickets.create o expectedTotal y computedTotal en su 422. None en las respuestas exitosas y en los errores sin data; data no cambia (en un error sigue en None).")
13
15
 
14
16
  model_config = ConfigDict(populate_by_name=True)
15
17
 
@@ -30,7 +32,7 @@ class ValidationFailure(BaseModel):
30
32
  """Modelo para errores de validación."""
31
33
  property_name: str = Field(alias="propertyName")
32
34
  error_message: str = Field(alias="errorMessage")
33
- attempted_value: Optional[Any] = Field(default=None, alias="attemptedValue")
35
+ attempted_value: Optional[Any] = Field(default=None, alias="attemptedValue", description="Valor recibido. El de un secreto (contraseñas, códigos, tokens, archivos y contraseñas de CSD/FIEL) llega como \"[masked: n]\", o \"[masked]\" si es un objeto o lista que lo contiene.")
34
36
 
35
37
  model_config = ConfigDict(populate_by_name=True)
36
38
 
@@ -4,7 +4,7 @@ from pydantic import BaseModel, ConfigDict, Field
4
4
  from fiscalapi.models.common_models import BaseDto, CatalogDto
5
5
  from fiscalapi.models.carta_porte_models import CartaPorteComplement
6
6
  from fiscalapi.models.comercio_exterior_models import ComercioExteriorComplement
7
- from typing import Literal, Optional
7
+ from typing import Optional, Union
8
8
  from datetime import datetime
9
9
 
10
10
  # products models
@@ -14,13 +14,13 @@ class ProductTax(BaseDto):
14
14
 
15
15
  rate: Decimal = Field(default=..., ge=0, le=1, alias="rate", description="Tasa de impuesto")
16
16
 
17
- tax_id: Optional[Literal["001", "002", "003"]] = Field(default=None, alias="taxId", description="Impuesto")
17
+ tax_id: Optional[str] = Field(default=None, alias="taxId", description="Impuesto: clave del catálogo c_Impuesto del SAT (\"001\" ISR, \"002\" IVA, \"003\" IEPS). La valida el API.")
18
18
  tax: Optional[CatalogDto] = Field(default=None, alias="tax", description="Impuesto expandido")
19
19
 
20
- tax_flag_id: Optional[Literal["T", "R"]] = Field(default=None, alias="taxFlagId", description="Traslado o Retención")
20
+ tax_flag_id: Optional[str] = Field(default=None, alias="taxFlagId", description="Naturaleza del impuesto: \"T\" (traslado) o \"R\" (retención). La valida el API.")
21
21
  tax_flag: Optional[CatalogDto] = Field(default=None, alias="taxFlag", description="Traslado o Retención expandido")
22
22
 
23
- tax_type_id: Optional[Literal["Tasa", "Cuota", "Exento"]] = Field(default=None, alias="taxTypeId", description="Tipo de impuesto")
23
+ tax_type_id: Optional[str] = Field(default=None, alias="taxTypeId", description="Tipo factor: clave del catálogo c_TipoFactor del SAT (\"Tasa\", \"Cuota\" o \"Exento\"). La valida el API.")
24
24
  tax_type: Optional[CatalogDto] = Field(default=None, alias="taxType", description="Tipo de impuesto expandido")
25
25
 
26
26
 
@@ -48,33 +48,49 @@ class Product(BaseDto):
48
48
 
49
49
  # people models
50
50
 
51
+ class CreditBalance(BaseModel):
52
+ """Saldo de una persona para un tipo de crédito (Person.balances).
53
+
54
+ Un tipo ausente tiene saldo 0; un tipo que este SDK no conoce conserva su número en credit_type.
55
+ """
56
+
57
+ credit_type: int = Field(default=..., alias="creditType", description="Tipo de crédito: 1 timbres, 2 créditos de validación, 3 créditos de ticket.")
58
+ available: int = Field(default=..., alias="available", description="Saldo disponible.")
59
+
60
+ model_config = ConfigDict(populate_by_name=True)
61
+
62
+
51
63
  class Person(BaseDto):
52
64
  """Modelo persona en FiscalAPI."""
53
65
 
54
66
  legal_name: Optional[str] = Field(default=None, alias="legalName", description="Razón social de la persona sin régimen de capital.")
55
67
  email: Optional[str] = Field(default=None, alias="email", description="Correo electrónico de la persona. El API valida el formato.")
56
- password: Optional[str] = Field(default=None, alias="password", description="Contraseña para acceder al dashboard.")
57
- capital_regime: Optional[str] = Field(default=None, alias="CapitalRegime", description="[Deprecado] El API no expone este campo; se conserva por compatibilidad y se ignora.")
58
- sat_tax_regime_id: Optional[Literal["601", "603", "605", "606", "607", "608", "610", "611", "612", "614", "615", "616", "620", "621", "622", "623", "624", "625", "626"]] = Field(default=None, alias="satTaxRegimeId", description="Código del régimen fiscal del emisor.")
68
+ password: Optional[str] = Field(default=None, alias="password", description="Contraseña para acceder al dashboard. Requerida al crear; al actualizar, None o vacía conserva la actual. El API nunca la devuelve.")
69
+ phone_number: Optional[str] = Field(default=None, alias="phoneNumber", description="Teléfono de la persona.")
70
+ sat_tax_regime_id: Optional[str] = Field(default=None, alias="satTaxRegimeId", description="Código del régimen fiscal: clave del catálogo c_RegimenFiscal del SAT. La valida el API.")
59
71
  sat_tax_regime: Optional[CatalogDto] = Field(default=None, alias="satTaxRegime", description="Código del régimen fiscal expandido.")
60
- sat_cfdi_use_id: Optional[Literal["G01", "G02", "G03", "I01", "I02", "I03", "I04", "I05", "I06", "I07", "I08", "D01", "D02", "D03", "D04", "D05", "D06", "D07", "D08", "D09", "D10", "S01", "CP01", "CN01"]] = Field(default=None, alias="satCfdiUseId", description="Código de uso del CFDI.")
72
+ sat_cfdi_use_id: Optional[str] = Field(default=None, alias="satCfdiUseId", description="Código de uso del CFDI: clave del catálogo c_UsoCFDI del SAT. La valida el API.")
61
73
  sat_cfdi_use: Optional[CatalogDto] = Field(default=None, alias="satCfdiUse", description="Código de uso del CFDI expandido.")
62
- user_type_id: Optional[Literal["T","C", "U"]] = Field(default=None, alias="userTypeId", description="Tipo de persona.")
74
+ user_type_id: Optional[str] = Field(default=None, alias="userTypeId", description="Tipo de persona: C (cliente, por omisión al crear) o U (usuario). T (tenant) solo llega en respuestas: el API lo rechaza al crear y al actualizar solo lo acepta si la persona ya es T.")
63
75
  user_type: Optional[CatalogDto] = Field(default=None, alias="userType", description="Tipo de persona expandido.")
64
76
  tin: Optional[str] = Field(default=None, alias="tin", description="RFC del emisor (Tax Identification Number).")
65
77
  zip_code: Optional[str] = Field(default=None, alias="zipCode", description="Código postal del emisor.")
66
78
  curp: Optional[str] = Field(default=None, alias="curp", description="CURP de la persona.")
67
79
  base64_photo: Optional[str] = Field(default=None, alias="base64Photo", description="Foto de perfil en formato base64.")
68
- tax_password: Optional[str] = Field(default=None, alias="taxPassword", description="Contraseña de los certificados CSD del emisor.")
80
+ tax_password: Optional[str] = Field(default=None, alias="taxPassword", description="Contraseña de la llave privada (.key) que la persona guarda en su perfil. El API no la usa para sellar (al timbrar usa la de los certificados registrados o la de tax_credentials). Con valor solo para la propia persona y el owner del tenant; los demás reciben None. Al actualizar, None la conserva y \"\" la borra.")
69
81
  available_balance: Optional[Decimal] = Field(default=None, alias="availableBalance", description="Saldo disponible en la cuenta.")
70
- committed_balance: Optional[Decimal] = Field(default=None, alias="committedBalance", description="Saldo en tránsito.")
82
+ committed_balance: Optional[Decimal] = Field(default=None, alias="committedBalance", description="Campo heredado: el API ya no lo calcula y siempre vale 0 (solo lectura). Para el saldo use available_balance o balances.")
71
83
  available_validation_balance: Optional[int] = Field(default=None, alias="availableValidationBalance", description="Créditos de validación SAT disponibles en la cuenta.")
84
+ balances: Optional[list[CreditBalance]] = Field(default=None, alias="balances", description="Saldos por tipo de crédito (solo lectura). Solo aparecen los tipos que la persona ha tenido.")
72
85
  manifest_status_id: Optional[str] = Field(default=None, alias="manifestStatusId", description="Estado de la carta manifiesto de la persona.")
73
86
  manifest_status: Optional[CatalogDto] = Field(default=None, alias="manifestStatus", description="Estado de la carta manifiesto expandido.")
74
87
  tenant_id: Optional[str] = Field(default=None, alias="tenantId", description="ID del tenant al que pertenece el emisor.")
75
88
  tenant: Optional[CatalogDto] = Field(default=None, alias="tenant", description="Tenant expandido.")
89
+ is_owner: Optional[bool] = Field(default=None, alias="isOwner", description="True si la persona es el owner de su tenant (solo lectura).")
90
+ valid_to: Optional[datetime] = Field(default=None, alias="validTo", description="Fin de vigencia de la persona (solo lectura). La asigna el API; casi siempre es None y es informativa (no limita el timbrado ni el acceso al API).")
76
91
  country_id: Optional[str] = Field(default=None, alias="countryId", description="Código del país de residencia para extranjeros (catálogo c_Pais).")
77
92
  foreign_tin: Optional[str] = Field(default=None, alias="foreignTin", description="Número de identificación fiscal del extranjero.")
93
+ country: Optional[CatalogDto] = Field(default=None, alias="country", description="País de residencia fiscal expandido (solo lectura).")
78
94
 
79
95
  model_config = ConfigDict(populate_by_name=True)
80
96
 
@@ -132,10 +148,10 @@ class TaxFile(BaseDto):
132
148
  """Modelo TaxFile que representa un componente de un par CSD: certificado (.cer) o llave privada (.key)."""
133
149
 
134
150
  person_id: Optional[str] = Field(default=None, alias="personId", description="Id de la persona propietaria del certificado.")
135
- tin: Optional[str] = Field(default=None, alias="tin", description="RFC del propietario del certificado. Debe coincidir con el RFC del certificado.")
151
+ tin: Optional[str] = Field(default=None, alias="tin", description="RFC de la persona. Opcional al subir: si se omite, el API usa el RFC de la persona; si se envía, debe ser el RFC de la persona (sin distinguir mayúsculas) o el API responde 400 con la falla en Tin. El API siempre devuelve el RFC de la persona.")
136
152
  base64_file: Optional[str] = Field(default=None, alias="base64File", description="Archivo certificado o llave privada en formato base64.")
137
- file_type: Optional[Literal[0, 1]] = Field(default=None, alias="fileType", description="Tipo de archivo: 0 para certificado, 1 para llave privada.")
138
- password: Optional[str] = Field(default=None, alias="password", description="Contraseña de la llave privada.")
153
+ file_type: Optional[int] = Field(default=None, alias="fileType", description="Tipo de archivo: 0 certificado CSD (.cer), 1 llave privada CSD (.key), 2 certificado FIEL (.cer), 3 llave privada FIEL (.key). El API valida el valor.")
154
+ password: Optional[str] = Field(default=None, alias="password", description="Contraseña de la llave privada. Solo se envía al subir (tax_files.create()): en las consultas (get_list(), get_by_id() y la respuesta de create()) el API la devuelve vacía (''); solo get_default_values() la devuelve.")
139
155
  valid_from: Optional[datetime] = Field(default=None, alias="validFrom", description="Fecha de inicio de vigencia del certificado o llave privada.")
140
156
  valid_to: Optional[datetime] = Field(default=None, alias="validTo", description="Fecha de fin de vigencia del certificado o llave privada.")
141
157
  sequence: Optional[int] = Field(default=None, alias="sequence", description="Numero de secuencia que identifica el par entre certificado y llave privada.")
@@ -185,11 +201,11 @@ class InvoiceRecipientEmployeeData(BaseDto):
185
201
  model_config = ConfigDict(populate_by_name=True)
186
202
 
187
203
 
188
- class TaxCredential(BaseDto):
204
+ class TaxCredential(BaseModel):
189
205
  """Modelo para los sellos del emisor (archivos .cer y .key)."""
190
206
  base64_file: str = Field(default=..., alias="base64File", description="Archivo en formato base64.")
191
- file_type: Literal[0, 1] = Field(default=..., alias="fileType", description="Tipo de archivo: 0 para certificado, 1 para llave privada.")
192
- password: str = Field(default=..., alias="password", description="Contraseña del archivo .key independientemente de si es un archivo .cer o .key.")
207
+ file_type: int = Field(default=..., alias="fileType", description="Tipo de archivo: 0 para certificado, 1 para llave privada (el par CSD). El API valida el valor.")
208
+ password: Optional[str] = Field(default=None, alias="password", description="Contraseña de la llave privada. Requerida en el objeto .key (file_type 1); en el .cer el API no la usa.")
193
209
 
194
210
  model_config = ConfigDict(populate_by_name=True)
195
211
 
@@ -230,7 +246,7 @@ class ItemTax(BaseDto):
230
246
  tax_code: Optional[str] = Field(default=None, alias="taxCode", description="Código del impuesto.")
231
247
  tax_type_code: Optional[str] = Field(default=None, alias="taxTypeCode", description="Tipo de factor.")
232
248
  tax_rate: Optional[Decimal] = Field(default=None, alias="taxRate", description="Tasa del impuesto.")
233
- tax_flag_code: Optional[Literal["T", "R"]] = Field(default=None, alias="taxFlagCode", description="Código que indica la naturaleza del impuesto. (T)raslado o (R)etención.")
249
+ tax_flag_code: Optional[str] = Field(default=None, alias="taxFlagCode", description="Código que indica la naturaleza del impuesto. (T)raslado o (R)etención.")
234
250
 
235
251
  model_config = ConfigDict(populate_by_name=True)
236
252
 
@@ -288,6 +304,7 @@ class InvoiceItem(BaseDto):
288
304
  customs_info: Optional[list[ItemCustomsInfo]] = Field(default=None, alias="customsInfo", description="Información aduanera.")
289
305
  property_info: Optional[list[ItemPropertyInfo]] = Field(default=None, alias="propertyInfo", description="Información predial.")
290
306
  parts: Optional[list[ItemPart]] = Field(default=None, alias="parts", description="Partes del concepto.")
307
+ ticket_id: Optional[str] = Field(default=None, alias="ticketId", description="Id del ticket de Auto CFDI que este concepto factura. Solo en una factura global de ingreso (tipo I con global_information) con el emisor por referencia (issuer.id); un ticket puede ir en varios conceptos, uno por grupo de objeto de impuesto e impuestos. En None no se envía.")
291
308
 
292
309
  model_config = ConfigDict(populate_by_name=True)
293
310
 
@@ -313,7 +330,7 @@ class PaidInvoiceTax(BaseDto):
313
330
  tax_code: str = Field(default=..., alias="taxCode", description="Código del impuesto.")
314
331
  tax_type_code: str = Field(default=..., alias="taxTypeCode", description="Tipo de factor.")
315
332
  tax_rate: Decimal = Field(default=..., alias="taxRate", description="Tasa del impuesto.")
316
- tax_flag_code: Optional[Literal["T", "R"]] = Field(default=None, alias="taxFlagCode", description="Código que indica la naturaleza del impuesto. (T)raslado o (R)etención.")
333
+ tax_flag_code: Optional[str] = Field(default=None, alias="taxFlagCode", description="Código que indica la naturaleza del impuesto. (T)raslado o (R)etención.")
317
334
 
318
335
  model_config = ConfigDict(populate_by_name=True)
319
336
 
@@ -343,7 +360,7 @@ class InvoicePayment(BaseDto):
343
360
  payment_date: str = Field(default=..., alias="paymentDate", description="Fecha de pago.")
344
361
  payment_form_code: str = Field(default=..., alias="paymentFormCode", description="Código de la forma de pago.")
345
362
 
346
- currency_code: Literal ["MXN", "USD", "EUR"] = Field(default="MXN", alias="currencyCode", description="Código de la moneda utilizada en el pago.")
363
+ currency_code: str = Field(default="MXN", alias="currencyCode", description="Código de la moneda utilizada en el pago: una clave del catálogo c_Moneda del SAT, en mayúsculas, distinta de XXX.")
347
364
  exchange_rate: Optional[Decimal] = Field(default=Decimal("1"), alias="exchangeRate", description="Tipo de cambio FIX conforme a la moneda registrada en la factura. Si la moneda es MXN, el tipo de cambio debe ser 1..")
348
365
  amount: Decimal = Field(default=..., description="Monto del pago.")
349
366
  source_bank_tin: str = Field(default=..., alias="sourceBankTin", description="RFC del banco origen. (Rfc del banco emisor del pago)")
@@ -363,7 +380,7 @@ class LocalTax(BaseDto):
363
380
  tax_name: Optional[str] = Field(default=None, alias="taxName", description="Nombre del impuesto local.")
364
381
  tax_rate: Optional[Decimal] = Field(default=None, alias="taxRate", description="Tasa del impuesto local.")
365
382
  tax_amount: Optional[Decimal] = Field(default=None, alias="taxAmount", description="Monto del impuesto local.")
366
- tax_flag_code: Optional[Literal["T", "R"]] = Field(default=None, alias="taxFlagCode", description="Traslado o Retención.")
383
+ tax_flag_code: Optional[str] = Field(default=None, alias="taxFlagCode", description="Traslado o Retención.")
367
384
 
368
385
  model_config = ConfigDict(populate_by_name=True)
369
386
 
@@ -588,7 +605,7 @@ class CancelInvoiceRequest(BaseDto):
588
605
  id: Optional[str] = Field(default=None, alias="id", description="ID de la factura a cancelar. Obligatorio cuando se cancela por referencias.")
589
606
  invoice_uuid: Optional[str] = Field(default=None, alias="invoiceUuid", description="UUID de la factura a cancelar. Obligatorio cuando se cancela por valores.")
590
607
  tin: Optional[str] = Field(default=None, alias="tin", description="RFC del emisor de la factura. Obligatorio cuando se cancela por valores.")
591
- cancellation_reason_code: Literal["01", "02", "03", "04"] = Field(default=..., alias="cancellationReasonCode", description="Código del motivo de cancelación.")
608
+ cancellation_reason_code: str = Field(default=..., alias="cancellationReasonCode", description="Código del motivo de cancelación, catálogo c_MotivoCancelacion del SAT: 01, 02, 03 o 04. El API valida el valor.")
592
609
  replacement_uuid: Optional[str] = Field(default=None, alias="replacementUuid", description="UUID de la factura de reemplazo. Obligatorio si el motivo de cancelación es '01'.")
593
610
  tax_credentials: Optional[list[TaxCredential]] = Field(default=None, alias="taxCredentials", description="Sellos del emisor. Obligatorio cuando se cancela por valores.")
594
611
 
@@ -738,7 +755,8 @@ class DownloadRequest(BaseDto):
738
755
  download_request_status: Optional[CatalogDto] = Field(default=None, alias="downloadRequestStatus", description="Estado de la solicitud de descarga Fiscalapi.")
739
756
 
740
757
  last_attempt_date: Optional[datetime] = Field(default=None, alias="lastAttemptDate", description="Fecha del último intento para la solicitud asociada.")
741
- next_attempt_date: Optional[datetime] = Field(default=None, alias="nextAttemptDate", description="Fecha del siguiente intento para la solicitud asociada.")
758
+ # Obsoleto: el API ya no devuelve nextAttemptDate (siempre era null). Se eliminará en una versión mayor.
759
+ next_attempt_date: Optional[datetime] = Field(default=None, alias="nextAttemptDate", description="Obsoleto: el API ya no lo devuelve; siempre era null porque Fiscalapi no programa un siguiente intento por solicitud. Se eliminará en una versión mayor; use last_attempt_date.")
742
760
 
743
761
  invoice_count: Optional[int] = Field(default=None, alias="invoiceCount", description="Número de CFDIs encontrados para la solicitud cuando la solicitud ha terminado.")
744
762
  package_ids: Optional[list[str]] = Field(default_factory=list, alias="packageIds", description="Lista de IDs de paquetes disponibles para descarga cuando la solicitud ha terminado.")
@@ -994,12 +1012,13 @@ class Xml(BaseDto):
994
1012
  class CreditType(IntEnum):
995
1013
  """Tipo de crédito de una transacción de timbres.
996
1014
 
997
- Los saldos nunca se mezclan: STAMP afecta ``Person.available_balance`` y VALIDATION afecta
998
- ``Person.available_validation_balance``.
1015
+ Los saldos nunca se mezclan: STAMP afecta ``Person.available_balance``, VALIDATION afecta
1016
+ ``Person.available_validation_balance`` y TICKET afecta el saldo de tipo TICKET en ``Person.balances``.
999
1017
  """
1000
1018
 
1001
1019
  STAMP = 1
1002
1020
  VALIDATION = 2
1021
+ TICKET = 3
1003
1022
 
1004
1023
 
1005
1024
  class StampTransactionType(IntEnum):
@@ -1038,7 +1057,9 @@ class StampTransaction(BaseDto):
1038
1057
  transaction_status: Optional[int] = Field(default=None, alias="transactionStatus", description="Estado de la transacción.")
1039
1058
  reference_id: Optional[str] = Field(default=None, alias="referenceId", description="ID de referencia de la transacción.")
1040
1059
  comments: Optional[str] = Field(default=None, alias="comments", description="Comentarios de la transacción.")
1041
- credit_type: Optional[CreditType] = Field(default=None, alias="creditType", description="Tipo de crédito que mueve la transacción: timbres o créditos de validación.")
1060
+ # left_to_right: un tipo conocido llega como miembro de CreditType y uno que este SDK todavía no conoce
1061
+ # conserva su número (int) en lugar de lanzar ValidationError y perder la respuesta completa.
1062
+ credit_type: Optional[Union[CreditType, int]] = Field(default=None, alias="creditType", union_mode="left_to_right", description="Tipo de crédito que mueve la transacción: timbres, créditos de validación o créditos de ticket. Un tipo que este SDK no conoce conserva su número (int).")
1042
1063
 
1043
1064
  model_config = ConfigDict(populate_by_name=True)
1044
1065
 
@@ -1049,6 +1070,6 @@ class StampTransactionParams(BaseModel):
1049
1070
  to_person_id: str = Field(default=..., alias="toPersonId", description="ID de la persona destino.")
1050
1071
  amount: int = Field(default=..., alias="amount", description="Cantidad de timbres a transferir.")
1051
1072
  comments: Optional[str] = Field(default=None, alias="comments", description="Comentarios de la transferencia.")
1052
- credit_type: CreditType = Field(default=CreditType.STAMP, alias="creditType", description="Tipo de crédito a transferir: STAMP (timbres, por defecto) o VALIDATION (créditos de validación SAT).")
1073
+ credit_type: CreditType = Field(default=CreditType.STAMP, alias="creditType", description="Tipo de crédito a transferir: STAMP (timbres, por defecto), VALIDATION (créditos de validación SAT) o TICKET (créditos de ticket).")
1053
1074
 
1054
1075
  model_config = ConfigDict(populate_by_name=True)