catalogmx 0.3.0__py3-none-any.whl

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 (81) hide show
  1. catalogmx/__init__.py +56 -0
  2. catalogmx/catalogs/__init__.py +5 -0
  3. catalogmx/catalogs/banxico/__init__.py +24 -0
  4. catalogmx/catalogs/banxico/banks.py +136 -0
  5. catalogmx/catalogs/banxico/codigos_plaza.py +287 -0
  6. catalogmx/catalogs/banxico/instituciones_financieras.py +338 -0
  7. catalogmx/catalogs/banxico/monedas_divisas.py +386 -0
  8. catalogmx/catalogs/banxico/udis.py +279 -0
  9. catalogmx/catalogs/ift/__init__.py +15 -0
  10. catalogmx/catalogs/ift/codigos_lada.py +426 -0
  11. catalogmx/catalogs/ift/operadores_moviles.py +315 -0
  12. catalogmx/catalogs/inegi/__init__.py +21 -0
  13. catalogmx/catalogs/inegi/localidades.py +207 -0
  14. catalogmx/catalogs/inegi/municipios.py +73 -0
  15. catalogmx/catalogs/inegi/municipios_completo.py +236 -0
  16. catalogmx/catalogs/inegi/states.py +148 -0
  17. catalogmx/catalogs/mexico/__init__.py +17 -0
  18. catalogmx/catalogs/mexico/hoy_no_circula.py +215 -0
  19. catalogmx/catalogs/mexico/placas_formatos.py +184 -0
  20. catalogmx/catalogs/mexico/salarios_minimos.py +156 -0
  21. catalogmx/catalogs/mexico/uma.py +207 -0
  22. catalogmx/catalogs/sat/__init__.py +13 -0
  23. catalogmx/catalogs/sat/carta_porte/__init__.py +19 -0
  24. catalogmx/catalogs/sat/carta_porte/aeropuertos.py +76 -0
  25. catalogmx/catalogs/sat/carta_porte/carreteras.py +59 -0
  26. catalogmx/catalogs/sat/carta_porte/config_autotransporte.py +54 -0
  27. catalogmx/catalogs/sat/carta_porte/material_peligroso.py +66 -0
  28. catalogmx/catalogs/sat/carta_porte/puertos_maritimos.py +63 -0
  29. catalogmx/catalogs/sat/carta_porte/tipo_embalaje.py +48 -0
  30. catalogmx/catalogs/sat/carta_porte/tipo_permiso.py +54 -0
  31. catalogmx/catalogs/sat/cfdi_4/__init__.py +42 -0
  32. catalogmx/catalogs/sat/cfdi_4/clave_prod_serv.py +383 -0
  33. catalogmx/catalogs/sat/cfdi_4/clave_unidad.py +298 -0
  34. catalogmx/catalogs/sat/cfdi_4/exportacion.py +45 -0
  35. catalogmx/catalogs/sat/cfdi_4/forma_pago.py +45 -0
  36. catalogmx/catalogs/sat/cfdi_4/impuesto.py +57 -0
  37. catalogmx/catalogs/sat/cfdi_4/meses.py +34 -0
  38. catalogmx/catalogs/sat/cfdi_4/metodo_pago.py +45 -0
  39. catalogmx/catalogs/sat/cfdi_4/objeto_imp.py +45 -0
  40. catalogmx/catalogs/sat/cfdi_4/periodicidad.py +34 -0
  41. catalogmx/catalogs/sat/cfdi_4/regimen_fiscal.py +57 -0
  42. catalogmx/catalogs/sat/cfdi_4/tasa_o_cuota.py +42 -0
  43. catalogmx/catalogs/sat/cfdi_4/tipo_comprobante.py +45 -0
  44. catalogmx/catalogs/sat/cfdi_4/tipo_factor.py +34 -0
  45. catalogmx/catalogs/sat/cfdi_4/tipo_relacion.py +45 -0
  46. catalogmx/catalogs/sat/cfdi_4/uso_cfdi.py +45 -0
  47. catalogmx/catalogs/sat/comercio_exterior/__init__.py +39 -0
  48. catalogmx/catalogs/sat/comercio_exterior/claves_pedimento.py +77 -0
  49. catalogmx/catalogs/sat/comercio_exterior/estados.py +122 -0
  50. catalogmx/catalogs/sat/comercio_exterior/incoterms.py +226 -0
  51. catalogmx/catalogs/sat/comercio_exterior/monedas.py +107 -0
  52. catalogmx/catalogs/sat/comercio_exterior/motivos_traslado.py +54 -0
  53. catalogmx/catalogs/sat/comercio_exterior/paises.py +88 -0
  54. catalogmx/catalogs/sat/comercio_exterior/registro_ident_trib.py +76 -0
  55. catalogmx/catalogs/sat/comercio_exterior/unidades_aduana.py +54 -0
  56. catalogmx/catalogs/sat/comercio_exterior/validator.py +212 -0
  57. catalogmx/catalogs/sat/nomina/__init__.py +19 -0
  58. catalogmx/catalogs/sat/nomina/banco.py +50 -0
  59. catalogmx/catalogs/sat/nomina/periodicidad_pago.py +48 -0
  60. catalogmx/catalogs/sat/nomina/riesgo_puesto.py +56 -0
  61. catalogmx/catalogs/sat/nomina/tipo_contrato.py +47 -0
  62. catalogmx/catalogs/sat/nomina/tipo_jornada.py +42 -0
  63. catalogmx/catalogs/sat/nomina/tipo_nomina.py +52 -0
  64. catalogmx/catalogs/sat/nomina/tipo_regimen.py +47 -0
  65. catalogmx/catalogs/sepomex/__init__.py +5 -0
  66. catalogmx/catalogs/sepomex/codigos_postales.py +184 -0
  67. catalogmx/cli.py +185 -0
  68. catalogmx/helpers.py +324 -0
  69. catalogmx/utils/text.py +55 -0
  70. catalogmx/validators/__init__.py +0 -0
  71. catalogmx/validators/clabe.py +233 -0
  72. catalogmx/validators/curp.py +623 -0
  73. catalogmx/validators/nss.py +255 -0
  74. catalogmx/validators/rfc.py +1004 -0
  75. catalogmx-0.3.0.dist-info/METADATA +644 -0
  76. catalogmx-0.3.0.dist-info/RECORD +81 -0
  77. catalogmx-0.3.0.dist-info/WHEEL +5 -0
  78. catalogmx-0.3.0.dist-info/entry_points.txt +2 -0
  79. catalogmx-0.3.0.dist-info/licenses/AUTHORS.rst +5 -0
  80. catalogmx-0.3.0.dist-info/licenses/LICENSE +19 -0
  81. catalogmx-0.3.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,45 @@
1
+ """Catálogo c_Exportacion"""
2
+
3
+ import json
4
+ from pathlib import Path
5
+
6
+
7
+ class ExportacionCatalog:
8
+ """Catálogo de Exportaciones del SAT (c_Exportacion)"""
9
+
10
+ _data: list[dict] | None = None
11
+ _by_code: dict[str, dict] | None = None
12
+
13
+ @classmethod
14
+ def _load_data(cls) -> None:
15
+ """Carga los datos del catálogo si aún no han sido cargados"""
16
+ if cls._data is None:
17
+ path = (
18
+ Path(__file__).parent.parent.parent.parent.parent.parent
19
+ / "shared-data"
20
+ / "sat"
21
+ / "cfdi_4.0"
22
+ / "exportacion.json"
23
+ )
24
+ with open(path, encoding="utf-8") as f:
25
+ data = json.load(f)
26
+ # Handle both list and dict formats
27
+ cls._data = data if isinstance(data, list) else data.get("exportaciones", data)
28
+ cls._by_code = {item["code"]: item for item in cls._data}
29
+
30
+ @classmethod
31
+ def get_exportacion(cls, code: str) -> dict | None:
32
+ """Obtiene una exportación por su código"""
33
+ cls._load_data()
34
+ return cls._by_code.get(code)
35
+
36
+ @classmethod
37
+ def is_valid(cls, code: str) -> bool:
38
+ """Valida si un código de exportación es válido"""
39
+ return cls.get_exportacion(code) is not None
40
+
41
+ @classmethod
42
+ def get_all(cls) -> list[dict]:
43
+ """Obtiene todas las exportaciones"""
44
+ cls._load_data()
45
+ return cls._data.copy()
@@ -0,0 +1,45 @@
1
+ """Catálogo c_FormaPago"""
2
+
3
+ import json
4
+ from pathlib import Path
5
+
6
+
7
+ class FormaPagoCatalog:
8
+ """Catálogo de Formas de Pago del SAT (c_FormaPago)"""
9
+
10
+ _data: list[dict] | None = None
11
+ _by_code: dict[str, dict] | None = None
12
+
13
+ @classmethod
14
+ def _load_data(cls) -> None:
15
+ """Carga los datos del catálogo si aún no han sido cargados"""
16
+ if cls._data is None:
17
+ path = (
18
+ Path(__file__).parent.parent.parent.parent.parent.parent
19
+ / "shared-data"
20
+ / "sat"
21
+ / "cfdi_4.0"
22
+ / "forma_pago.json"
23
+ )
24
+ with open(path, encoding="utf-8") as f:
25
+ data = json.load(f)
26
+ # Handle both list and dict formats
27
+ cls._data = data if isinstance(data, list) else data.get("formas_pago", data)
28
+ cls._by_code = {item["code"]: item for item in cls._data}
29
+
30
+ @classmethod
31
+ def get_forma_pago(cls, code: str) -> dict | None:
32
+ """Obtiene una forma de pago por su código"""
33
+ cls._load_data()
34
+ return cls._by_code.get(code)
35
+
36
+ @classmethod
37
+ def is_valid(cls, code: str) -> bool:
38
+ """Valida si un código de forma de pago es válido"""
39
+ return cls.get_forma_pago(code) is not None
40
+
41
+ @classmethod
42
+ def get_all(cls) -> list[dict]:
43
+ """Obtiene todas las formas de pago"""
44
+ cls._load_data()
45
+ return cls._data.copy()
@@ -0,0 +1,57 @@
1
+ """Catálogo c_Impuesto"""
2
+
3
+ import json
4
+ from pathlib import Path
5
+
6
+
7
+ class ImpuestoCatalog:
8
+ """Catálogo de Impuestos del SAT (c_Impuesto)"""
9
+
10
+ _data: list[dict] | None = None
11
+ _by_code: dict[str, dict] | None = None
12
+
13
+ @classmethod
14
+ def _load_data(cls) -> None:
15
+ """Carga los datos del catálogo si aún no han sido cargados"""
16
+ if cls._data is None:
17
+ path = (
18
+ Path(__file__).parent.parent.parent.parent.parent.parent
19
+ / "shared-data"
20
+ / "sat"
21
+ / "cfdi_4.0"
22
+ / "impuesto.json"
23
+ )
24
+ with open(path, encoding="utf-8") as f:
25
+ data = json.load(f)
26
+ # Handle both list and dict formats
27
+ cls._data = data if isinstance(data, list) else data.get("impuestos", data)
28
+ cls._by_code = {item["code"]: item for item in cls._data}
29
+
30
+ @classmethod
31
+ def get_impuesto(cls, code: str) -> dict | None:
32
+ """Obtiene un impuesto por su código"""
33
+ cls._load_data()
34
+ return cls._by_code.get(code)
35
+
36
+ @classmethod
37
+ def is_valid(cls, code: str) -> bool:
38
+ """Valida si un código de impuesto es válido"""
39
+ return cls.get_impuesto(code) is not None
40
+
41
+ @classmethod
42
+ def supports_retention(cls, code: str) -> bool:
43
+ """Valida si un impuesto soporta retención"""
44
+ impuesto = cls.get_impuesto(code)
45
+ return impuesto.get("retention", False) if impuesto else False
46
+
47
+ @classmethod
48
+ def supports_transfer(cls, code: str) -> bool:
49
+ """Valida si un impuesto soporta traslado"""
50
+ impuesto = cls.get_impuesto(code)
51
+ return impuesto.get("transfer", False) if impuesto else False
52
+
53
+ @classmethod
54
+ def get_all(cls) -> list[dict]:
55
+ """Obtiene todos los impuestos"""
56
+ cls._load_data()
57
+ return cls._data.copy()
@@ -0,0 +1,34 @@
1
+ """Catálogo de Meses (SAT)"""
2
+
3
+ import json
4
+
5
+ from ....helpers import get_project_root
6
+
7
+
8
+ class Meses:
9
+ _data = None
10
+
11
+ @classmethod
12
+ def _load_data(cls):
13
+ if cls._data is None:
14
+ root = get_project_root()
15
+ path = root / "packages" / "shared-data" / "sat" / "cfdi_4.0" / "c_Meses.json"
16
+ with open(path, encoding="utf-8") as f:
17
+ json_data = json.load(f)
18
+ cls._data = {item["valor"]: item for item in json_data["data"]}
19
+ return cls._data
20
+
21
+ @classmethod
22
+ def get_data(cls):
23
+ return cls._load_data()
24
+
25
+ @classmethod
26
+ def get_by_id(cls, mes_id):
27
+ """Busca un mes por su ID (e.g., '01', '02')."""
28
+ data = cls.get_data()
29
+ return data.get(mes_id)
30
+
31
+ @classmethod
32
+ def is_valid(cls, mes_id):
33
+ """Verifica si un ID de mes es válido."""
34
+ return mes_id in cls.get_data()
@@ -0,0 +1,45 @@
1
+ """Catálogo c_MetodoPago"""
2
+
3
+ import json
4
+ from pathlib import Path
5
+
6
+
7
+ class MetodoPagoCatalog:
8
+ """Catálogo de Métodos de Pago del SAT (c_MetodoPago)"""
9
+
10
+ _data: list[dict] | None = None
11
+ _by_code: dict[str, dict] | None = None
12
+
13
+ @classmethod
14
+ def _load_data(cls) -> None:
15
+ """Carga los datos del catálogo si aún no han sido cargados"""
16
+ if cls._data is None:
17
+ path = (
18
+ Path(__file__).parent.parent.parent.parent.parent.parent
19
+ / "shared-data"
20
+ / "sat"
21
+ / "cfdi_4.0"
22
+ / "metodo_pago.json"
23
+ )
24
+ with open(path, encoding="utf-8") as f:
25
+ data = json.load(f)
26
+ # Handle both list and dict formats
27
+ cls._data = data if isinstance(data, list) else data.get("metodos", data)
28
+ cls._by_code = {item["code"]: item for item in cls._data}
29
+
30
+ @classmethod
31
+ def get_metodo(cls, code: str) -> dict | None:
32
+ """Obtiene un método de pago por su código"""
33
+ cls._load_data()
34
+ return cls._by_code.get(code.upper())
35
+
36
+ @classmethod
37
+ def is_valid(cls, code: str) -> bool:
38
+ """Valida si un código de método de pago es válido"""
39
+ return cls.get_metodo(code) is not None
40
+
41
+ @classmethod
42
+ def get_all(cls) -> list[dict]:
43
+ """Obtiene todos los métodos de pago"""
44
+ cls._load_data()
45
+ return cls._data.copy()
@@ -0,0 +1,45 @@
1
+ """Catálogo c_ObjetoImp"""
2
+
3
+ import json
4
+ from pathlib import Path
5
+
6
+
7
+ class ObjetoImpCatalog:
8
+ """Catálogo de Objetos Impuestos del SAT (c_ObjetoImp)"""
9
+
10
+ _data: list[dict] | None = None
11
+ _by_code: dict[str, dict] | None = None
12
+
13
+ @classmethod
14
+ def _load_data(cls) -> None:
15
+ """Carga los datos del catálogo si aún no han sido cargados"""
16
+ if cls._data is None:
17
+ path = (
18
+ Path(__file__).parent.parent.parent.parent.parent.parent
19
+ / "shared-data"
20
+ / "sat"
21
+ / "cfdi_4.0"
22
+ / "objeto_imp.json"
23
+ )
24
+ with open(path, encoding="utf-8") as f:
25
+ data = json.load(f)
26
+ # Handle both list and dict formats
27
+ cls._data = data if isinstance(data, list) else data.get("objetos", data)
28
+ cls._by_code = {item["code"]: item for item in cls._data}
29
+
30
+ @classmethod
31
+ def get_objeto(cls, code: str) -> dict | None:
32
+ """Obtiene un objeto impuesto por su código"""
33
+ cls._load_data()
34
+ return cls._by_code.get(code)
35
+
36
+ @classmethod
37
+ def is_valid(cls, code: str) -> bool:
38
+ """Valida si un código de objeto impuesto es válido"""
39
+ return cls.get_objeto(code) is not None
40
+
41
+ @classmethod
42
+ def get_all(cls) -> list[dict]:
43
+ """Obtiene todos los objetos impuestos"""
44
+ cls._load_data()
45
+ return cls._data.copy()
@@ -0,0 +1,34 @@
1
+ """Catálogo de Periodicidad (SAT)"""
2
+
3
+ import json
4
+
5
+ from ....helpers import get_project_root
6
+
7
+
8
+ class Periodicidad:
9
+ _data = None
10
+
11
+ @classmethod
12
+ def _load_data(cls):
13
+ if cls._data is None:
14
+ root = get_project_root()
15
+ path = root / "packages" / "shared-data" / "sat" / "cfdi_4.0" / "c_Periodicidad.json"
16
+ with open(path, encoding="utf-8") as f:
17
+ json_data = json.load(f)
18
+ cls._data = {item["valor"]: item for item in json_data["data"]}
19
+ return cls._data
20
+
21
+ @classmethod
22
+ def get_data(cls):
23
+ return cls._load_data()
24
+
25
+ @classmethod
26
+ def get_by_id(cls, periodicidad_id):
27
+ """Busca una periodicidad por su ID."""
28
+ data = cls.get_data()
29
+ return data.get(periodicidad_id)
30
+
31
+ @classmethod
32
+ def is_valid(cls, periodicidad_id):
33
+ """Verifica si un ID de periodicidad es válido."""
34
+ return periodicidad_id in cls.get_data()
@@ -0,0 +1,57 @@
1
+ """Catálogo c_RegimenFiscal"""
2
+
3
+ import json
4
+ from pathlib import Path
5
+
6
+
7
+ class RegimenFiscalCatalog:
8
+ """Catálogo de Regímenes Fiscales del SAT (c_RegimenFiscal)"""
9
+
10
+ _data: list[dict] | None = None
11
+ _by_code: dict[str, dict] | None = None
12
+
13
+ @classmethod
14
+ def _load_data(cls) -> None:
15
+ """Carga los datos del catálogo si aún no han sido cargados"""
16
+ if cls._data is None:
17
+ path = (
18
+ Path(__file__).parent.parent.parent.parent.parent.parent
19
+ / "shared-data"
20
+ / "sat"
21
+ / "cfdi_4.0"
22
+ / "regimen_fiscal.json"
23
+ )
24
+ with open(path, encoding="utf-8") as f:
25
+ data = json.load(f)
26
+ # Handle both list and dict formats
27
+ cls._data = data if isinstance(data, list) else data.get("regimenes", data)
28
+ cls._by_code = {item["code"]: item for item in cls._data}
29
+
30
+ @classmethod
31
+ def get_regimen(cls, code: str) -> dict | None:
32
+ """Obtiene un régimen fiscal por su código"""
33
+ cls._load_data()
34
+ return cls._by_code.get(code)
35
+
36
+ @classmethod
37
+ def is_valid(cls, code: str) -> bool:
38
+ """Valida si un código de régimen fiscal es válido"""
39
+ return cls.get_regimen(code) is not None
40
+
41
+ @classmethod
42
+ def is_valid_for_persona_fisica(cls, code: str) -> bool:
43
+ """Valida si un régimen es válido para persona física"""
44
+ regimen = cls.get_regimen(code)
45
+ return regimen.get("fisica", False) if regimen else False
46
+
47
+ @classmethod
48
+ def is_valid_for_persona_moral(cls, code: str) -> bool:
49
+ """Valida si un régimen es válido para persona moral"""
50
+ regimen = cls.get_regimen(code)
51
+ return regimen.get("moral", False) if regimen else False
52
+
53
+ @classmethod
54
+ def get_all(cls) -> list[dict]:
55
+ """Obtiene todos los regímenes fiscales"""
56
+ cls._load_data()
57
+ return cls._data.copy()
@@ -0,0 +1,42 @@
1
+ """Catálogo de Tasa o Cuota (SAT)"""
2
+
3
+ import json
4
+ from pathlib import Path
5
+
6
+
7
+ class TasaOCuota:
8
+ _data = None
9
+
10
+ @classmethod
11
+ def _load_data(cls):
12
+ if cls._data is None:
13
+ path = (
14
+ Path(__file__).parent.parent.parent.parent.parent
15
+ / "shared-data"
16
+ / "sat"
17
+ / "cfdi_4.0"
18
+ / "c_TasaOCuota.json"
19
+ )
20
+ with open(path, encoding="utf-8") as f:
21
+ json_data = json.load(f)
22
+ # This catalog has a more complex structure, let's index by a combination of fields
23
+ cls._data = json_data["data"]
24
+ return cls._data
25
+
26
+ @classmethod
27
+ def get_data(cls):
28
+ return cls._load_data()
29
+
30
+ @classmethod
31
+ def get_by_range_and_tax(cls, valor_min, valor_max, impuesto, factor, trasladado, retenido):
32
+ """Finds a rate based on multiple criteria."""
33
+ data = cls.get_data()
34
+ # This is a placeholder for a more complex search logic
35
+ return [
36
+ item
37
+ for item in data
38
+ if item.get("valor_mínimo") == valor_min
39
+ and item.get("valor_máximo") == valor_max
40
+ and item.get("impuesto") == impuesto
41
+ and item.get("factor") == factor
42
+ ]
@@ -0,0 +1,45 @@
1
+ """Catálogo c_TipoComprobante"""
2
+
3
+ import json
4
+ from pathlib import Path
5
+
6
+
7
+ class TipoComprobanteCatalog:
8
+ """Catálogo de Tipos de Comprobante del SAT (c_TipoComprobante)"""
9
+
10
+ _data: list[dict] | None = None
11
+ _by_code: dict[str, dict] | None = None
12
+
13
+ @classmethod
14
+ def _load_data(cls) -> None:
15
+ """Carga los datos del catálogo si aún no han sido cargados"""
16
+ if cls._data is None:
17
+ path = (
18
+ Path(__file__).parent.parent.parent.parent.parent.parent
19
+ / "shared-data"
20
+ / "sat"
21
+ / "cfdi_4.0"
22
+ / "tipo_comprobante.json"
23
+ )
24
+ with open(path, encoding="utf-8") as f:
25
+ data = json.load(f)
26
+ # Handle both list and dict formats
27
+ cls._data = data if isinstance(data, list) else data.get("tipos", data)
28
+ cls._by_code = {item["code"]: item for item in cls._data}
29
+
30
+ @classmethod
31
+ def get_tipo(cls, code: str) -> dict | None:
32
+ """Obtiene un tipo de comprobante por su código"""
33
+ cls._load_data()
34
+ return cls._by_code.get(code.upper())
35
+
36
+ @classmethod
37
+ def is_valid(cls, code: str) -> bool:
38
+ """Valida si un código de tipo de comprobante es válido"""
39
+ return cls.get_tipo(code) is not None
40
+
41
+ @classmethod
42
+ def get_all(cls) -> list[dict]:
43
+ """Obtiene todos los tipos de comprobante"""
44
+ cls._load_data()
45
+ return cls._data.copy()
@@ -0,0 +1,34 @@
1
+ """Catálogo de Tipos de Factor (SAT)"""
2
+
3
+ import json
4
+
5
+ from ....helpers import get_project_root
6
+
7
+
8
+ class TipoFactor:
9
+ _data = None
10
+
11
+ @classmethod
12
+ def _load_data(cls):
13
+ if cls._data is None:
14
+ root = get_project_root()
15
+ path = root / "packages" / "shared-data" / "sat" / "cfdi_4.0" / "c_TipoFactor.json"
16
+ with open(path, encoding="utf-8") as f:
17
+ json_data = json.load(f)
18
+ cls._data = {item["valor"]: item for item in json_data["data"]}
19
+ return cls._data
20
+
21
+ @classmethod
22
+ def get_data(cls):
23
+ return cls._load_data()
24
+
25
+ @classmethod
26
+ def get_by_id(cls, tipo_factor_id):
27
+ """Busca un tipo de factor por su ID."""
28
+ data = cls.get_data()
29
+ return data.get(tipo_factor_id)
30
+
31
+ @classmethod
32
+ def is_valid(cls, tipo_factor_id):
33
+ """Verifica si un ID de tipo de factor es válido."""
34
+ return tipo_factor_id in cls.get_data()
@@ -0,0 +1,45 @@
1
+ """Catálogo c_TipoRelacion"""
2
+
3
+ import json
4
+ from pathlib import Path
5
+
6
+
7
+ class TipoRelacionCatalog:
8
+ """Catálogo de Tipos de Relación del SAT (c_TipoRelacion)"""
9
+
10
+ _data: list[dict] | None = None
11
+ _by_code: dict[str, dict] | None = None
12
+
13
+ @classmethod
14
+ def _load_data(cls) -> None:
15
+ """Carga los datos del catálogo si aún no han sido cargados"""
16
+ if cls._data is None:
17
+ path = (
18
+ Path(__file__).parent.parent.parent.parent.parent.parent
19
+ / "shared-data"
20
+ / "sat"
21
+ / "cfdi_4.0"
22
+ / "tipo_relacion.json"
23
+ )
24
+ with open(path, encoding="utf-8") as f:
25
+ data = json.load(f)
26
+ # Handle both list and dict formats
27
+ cls._data = data if isinstance(data, list) else data.get("tipos", data)
28
+ cls._by_code = {item["code"]: item for item in cls._data}
29
+
30
+ @classmethod
31
+ def get_tipo(cls, code: str) -> dict | None:
32
+ """Obtiene un tipo de relación por su código"""
33
+ cls._load_data()
34
+ return cls._by_code.get(code)
35
+
36
+ @classmethod
37
+ def is_valid(cls, code: str) -> bool:
38
+ """Valida si un código de tipo de relación es válido"""
39
+ return cls.get_tipo(code) is not None
40
+
41
+ @classmethod
42
+ def get_all(cls) -> list[dict]:
43
+ """Obtiene todos los tipos de relación"""
44
+ cls._load_data()
45
+ return cls._data.copy()
@@ -0,0 +1,45 @@
1
+ """Catálogo c_UsoCFDI"""
2
+
3
+ import json
4
+ from pathlib import Path
5
+
6
+
7
+ class UsoCFDICatalog:
8
+ """Catálogo de Usos del CFDI del SAT (c_UsoCFDI)"""
9
+
10
+ _data: list[dict] | None = None
11
+ _by_code: dict[str, dict] | None = None
12
+
13
+ @classmethod
14
+ def _load_data(cls) -> None:
15
+ """Carga los datos del catálogo si aún no han sido cargados"""
16
+ if cls._data is None:
17
+ path = (
18
+ Path(__file__).parent.parent.parent.parent.parent.parent
19
+ / "shared-data"
20
+ / "sat"
21
+ / "cfdi_4.0"
22
+ / "uso_cfdi.json"
23
+ )
24
+ with open(path, encoding="utf-8") as f:
25
+ data = json.load(f)
26
+ # Handle both list and dict formats
27
+ cls._data = data if isinstance(data, list) else data.get("usos", data)
28
+ cls._by_code = {item["code"]: item for item in cls._data}
29
+
30
+ @classmethod
31
+ def get_uso(cls, code: str) -> dict | None:
32
+ """Obtiene un uso del CFDI por su código"""
33
+ cls._load_data()
34
+ return cls._by_code.get(code.upper())
35
+
36
+ @classmethod
37
+ def is_valid(cls, code: str) -> bool:
38
+ """Valida si un código de uso del CFDI es válido"""
39
+ return cls.get_uso(code) is not None
40
+
41
+ @classmethod
42
+ def get_all(cls) -> list[dict]:
43
+ """Obtiene todos los usos del CFDI"""
44
+ cls._load_data()
45
+ return cls._data.copy()
@@ -0,0 +1,39 @@
1
+ """
2
+ Catálogos del SAT para Complemento de Comercio Exterior 2.0
3
+
4
+ Este módulo contiene los catálogos oficiales del SAT necesarios para la emisión
5
+ de CFDI con Complemento de Comercio Exterior versión 2.0 (vigente desde enero 18, 2024).
6
+
7
+ Catálogos incluidos:
8
+ - c_INCOTERM: 11 Incoterms 2020
9
+ - c_ClavePedimento: ~40 claves de pedimento aduanero
10
+ - c_UnidadAduana: ~30 unidades de medida aduanera
11
+ - c_MotivoTraslado: 6 motivos de traslado
12
+ - c_RegistroIdentTribReceptor: Tipos de identificación tributaria
13
+ - c_Moneda: ~180 monedas ISO 4217
14
+ - c_Pais: ~250 países ISO 3166-1
15
+ - c_Estado: Estados USA y provincias Canadá
16
+ - c_FraccionArancelaria: ~20,000 fracciones arancelarias TIGIE/NICO
17
+ """
18
+
19
+ from .claves_pedimento import ClavePedimentoCatalog
20
+ from .estados import EstadoCatalog
21
+ from .incoterms import IncotermsValidator
22
+ from .monedas import MonedaCatalog
23
+ from .motivos_traslado import MotivoTrasladoCatalog
24
+ from .paises import PaisCatalog
25
+ from .registro_ident_trib import RegistroIdentTribCatalog
26
+ from .unidades_aduana import UnidadAduanaCatalog
27
+ from .validator import ComercioExteriorValidator
28
+
29
+ __all__ = [
30
+ "IncotermsValidator",
31
+ "ClavePedimentoCatalog",
32
+ "UnidadAduanaCatalog",
33
+ "MotivoTrasladoCatalog",
34
+ "RegistroIdentTribCatalog",
35
+ "MonedaCatalog",
36
+ "PaisCatalog",
37
+ "EstadoCatalog",
38
+ "ComercioExteriorValidator",
39
+ ]