From 3a7ca967b5a093d7203251a49bb14749beea14d2 Mon Sep 17 00:00:00 2001 From: mendozagit Date: Wed, 14 Oct 2026 23:08:55 -0600 Subject: [PATCH 01/44] =?UTF-8?q?Plan=20del=20feature=20ai-integracion:=20?= =?UTF-8?q?tablero,=20requisitos,=20decisiones=20y=20tareas=20por=20=C3=A1?= =?UTF-8?q?rea?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Skills en español y plugin para Claude Code, Codex, Cursor y Copilot, más un MCP stdio opcional (@fiscalapi/mcp), sin nada hosteado y sin mantenimiento extra. Evidencia que sostiene las decisiones (detalle en requisitos.md): - Superficie: la colección "Fiscalapi API Reference" tiene 106 acciones; con 6 exclusiones del usuario quedan 100 para el OpenAPI público. - nginx test y live ya mandan location ^~ /api al back: el OpenAPI se sirve en /api/openapi/v4.json sin tocar nginx. - Búsqueda: sobre las operaciones del OpenAPI fallan MiniSearch, Orama y los embeddings (130 MB, 14 s de carga); sobre las 427 secciones H2 de las docs, MiniSearch con stemmer en español acierta 14 de 16. - @fiscalapi/mcp libre en npm (404); el nombre lo confirmó el usuario. --- docs/ai-integracion/README.md | 64 ++++++++++++ docs/ai-integracion/backend.md | 100 ++++++++++++++++++ docs/ai-integracion/decisiones.md | 116 +++++++++++++++++++++ docs/ai-integracion/docs.md | 54 ++++++++++ docs/ai-integracion/evals.md | 61 +++++++++++ docs/ai-integracion/mcp.md | 79 ++++++++++++++ docs/ai-integracion/medicion.md | 61 +++++++++++ docs/ai-integracion/requisitos.md | 104 +++++++++++++++++++ docs/ai-integracion/skills.md | 166 ++++++++++++++++++++++++++++++ 9 files changed, 805 insertions(+) create mode 100644 docs/ai-integracion/README.md create mode 100644 docs/ai-integracion/backend.md create mode 100644 docs/ai-integracion/decisiones.md create mode 100644 docs/ai-integracion/docs.md create mode 100644 docs/ai-integracion/evals.md create mode 100644 docs/ai-integracion/mcp.md create mode 100644 docs/ai-integracion/medicion.md create mode 100644 docs/ai-integracion/requisitos.md create mode 100644 docs/ai-integracion/skills.md diff --git a/docs/ai-integracion/README.md b/docs/ai-integracion/README.md new file mode 100644 index 0000000..1362ac1 --- /dev/null +++ b/docs/ai-integracion/README.md @@ -0,0 +1,64 @@ +# Integración asistida por IA + +## Objetivo +Que un desarrollador integre FiscalAPI en su ERP, POS o SaaS desde Claude Code, Codex, Cursor o Copilot, y logre su primer timbre correcto en test al primer intento. + +Para eso el feature publica: +- skills en español; +- un plugin para los 4 hosts; +- un MCP stdio opcional; +- los cimientos que esos tres necesitan: OpenAPI público y docs por sección. + +**Cómo se sabe que quedó:** +- Las evals muestran mejora con las skills frente a sin ellas. +- Baja el tiempo al primer timbre en test (métrica norte en `requisitos.md`). + +**Ramas:** `feat/ai-integracion` en cada repo que toque (fiscalapi-ai, fapi-back, fapi-docs, fapi-reusables), siempre desde `main`. + +**Fuera de alcance:** MCP hosteado, sandbox sin "compra" y CSD de prueba propios. Ver `requisitos.md`. + +## Cómo trabajar +Sigue el ciclo de `fapi-core` (sección 1). Particularidades de este feature: +- **El tablero vive aquí, en fiscalapi-ai,** porque el feature se escribe casi todo en este repo (decisión del usuario). El código de las tareas `BE-*` va en fapi-back y el de las `DOC-*` en fapi-docs o fapi-reusables; la fila de esas tareas se cierra con `@`. +- **Restricción dura: cero mantenimiento adicional para FiscalAPI (D-02).** Si una tarea agrega algo que hay que actualizar a mano en paralelo a las docs o al código, está mal planteada. +- **fiscalapi-ai es público.** Nada llega a `main` sin el PR que fusiona el usuario. Publicar en npm o en los marketplaces es un paso del usuario (U-04). +- **Todo en español:** skills, descripciones de herramientas del MCP y docs (D-04). + +## Tablero +Estado ∈ `pendiente` | `en curso` | `hecho` | `bloqueado`. En `bloqueado`, el motivo o la pregunta va en las Notas de la tarea. + +La siguiente tarea (global o de un área) es la primera fila `pendiente` (de esa área) cuyas dependencias sean `—` o `hecho`. El orden de las filas es la prioridad. + +| ID | Tarea | Área | Depende de | Estado | Commit | +|---|---|---|---|---|---| +| MED-01 | Medir mezcla de tipos y complementos en el restore de prod | medicion | — | pendiente | | +| MED-02 | Medir embudo y tiempo al primer timbre en el restore de Test | medicion | — | pendiente | | +| U-01 | Abrir el túnel a Seq de producción | usuario | — | pendiente | | +| MED-03 | Medir el top de rechazos del PAC en Seq | medicion | U-01 | pendiente | | +| BE-01 | Publicar la superficie de 100 acciones con `[PublicApi]` | backend | — | pendiente | | +| BE-02 | Agregar `operationId` estable y metadatos del documento | backend | BE-01 | pendiente | | +| BE-03 | Servir `/api/openapi/v4.json` anónimo en test y live | backend | BE-02 | pendiente | | +| BE-04 | Agregar el trinquete de `EndpointSummary` y cubrir facturas | backend | BE-01 | pendiente | | +| BE-05 | Completar `EndpointSummary` en el resto de la superficie | backend | BE-04 | pendiente | | +| DOC-01 | Generar `.md` por sección H2 en el build | docs | — | pendiente | | +| DOC-02 | Generar páginas de errores de Pagos y Comercio Exterior | docs | — | pendiente | | +| DOC-03 | Corregir el enlace del SDK de Node en `llms.txt` | docs | — | pendiente | | +| SK-01 | Armar el esqueleto del repo, los manifiestos y el CI de enlaces | skills | — | pendiente | | +| SK-02 | Escribir `fiscalapi-integracion` y `primer-timbre` | skills | SK-01, DOC-01 | pendiente | | +| SK-03 | Escribir `fiscalapi-diagnostico` y `explicar-error` | skills | SK-01, DOC-02 | pendiente | | +| SK-04 | Escribir `fiscalapi-ingreso-egreso` | skills | SK-02, MED-01 | pendiente | | +| SK-05 | Escribir `fiscalapi-pagos` | skills | SK-02, MED-01 | pendiente | | +| SK-06 | Escribir `fiscalapi-ciclo-de-vida` y `revisar-integracion` | skills | SK-02 | pendiente | | +| SK-07 | Escribir `fiscalapi-nomina` | skills | SK-02, MED-01 | pendiente | | +| SK-08 | Escribir `fiscalapi-carta-porte` | skills | SK-02, MED-01 | pendiente | | +| SK-09 | Escribir `fiscalapi-comercio-exterior` | skills | SK-02, MED-01 | pendiente | | +| MCP-01 | Crear el paquete stdio con `listar_operaciones` y `obtener_operacion` | mcp | SK-01, BE-03 | pendiente | | +| MCP-02 | Agregar `buscar_docs` con índice en español | mcp | MCP-01, DOC-01 | pendiente | | +| EV-01 | Montar el harness de evals con los casos de ingreso y pagos | evals | SK-05, MCP-02 | pendiente | | +| EV-02 | Agregar casos de nómina, carta porte y comercio exterior | evals | EV-01, SK-09 | pendiente | | +| EV-03 | Correr las 3 variantes y reportar | evals | EV-02 | pendiente | | +| U-02 | Confirmar el nombre y el scope del paquete npm (D-14) | usuario | — | hecho | | +| MCP-03 | Agregar el workflow de build y publicación por hash | mcp | MCP-02, U-02 | pendiente | | +| U-03 | Importar `/api/openapi/v4.json` al workspace de Postman | usuario | BE-03 | pendiente | | +| U-04 | Decidir la publicación en npm y en los marketplaces con el reporte de EV-03 | usuario | EV-03, MCP-03 | pendiente | | +| CIERRE | Suite de fapi-back, instalar en los 4 hosts y borrador de notas | cierre | todas | pendiente | | diff --git a/docs/ai-integracion/backend.md b/docs/ai-integracion/backend.md new file mode 100644 index 0000000..6489122 --- /dev/null +++ b/docs/ai-integracion/backend.md @@ -0,0 +1,100 @@ +# Integración asistida por IA: backend (fapi-back) + +El código de estas tareas va en fapi-back, en la rama `feat/ai-integracion` desde `main`. La fila del tablero se cierra con `fapi-back@`. Antes de usar cualquier API de `Microsoft.AspNetCore.OpenApi`, se consulta la documentación oficial de ASP.NET Core 10 (MCP microsoft-learn), sin responder de memoria. + +### BE-01 — Publicar la superficie de 100 acciones con `[PublicApi]` +**Objetivo:** que el documento `v4` contenga exactamente las 100 acciones de D-05 y nada más, protegido por una prueba de equivalencia con la colección. +**Depende de:** — +**Skills:** `fapi-new-feature`, `dotnet-aspnetcore:dotnet-webapi`, `dotnet-test:run-tests`, `fapi-e2e-validate`. +**Modelo sugerido (opcional):** opus. Toca qué se expone públicamente (seguridad). +**Contexto:** D-05, D-12, RF-01, RNF-03. Hoy el documento se registra en `Infrastructure/DependencyInjection` con un `BuildServiceProvider` intermedio y un documento por versión (incluido el grupo v5 de `Controllers/V5/ValuesController`). +**Alcance:** +- Atributo `PublicApiAttribute` (metadata de endpoint) en las 100 acciones. Se aplica por acción, nunca por controller: en los mismos controllers hay 52 acciones fuera de la colección. +- Registro explícito del documento `v4` con `ShouldInclude` = tiene `PublicApiAttribute`. Se quita el `BuildServiceProvider` intermedio. Si se conserva un documento completo para Scalar en Development, se decide aquí y se documenta como D-NN. +- Prueba de equivalencia en WebApi.Tests: + - lee los `*.request.yaml` de `postman/collections/Fiscalapi API Reference`; + - normaliza MÉTODO+ruta (ids y valores → `{param}`; pdf y xml del portal → `{format}`); + - resta la lista explícita de las 6 exclusiones de D-05, con su motivo en el código; + - exige igualdad con las operaciones del documento `v4`. + + El parser YAML lo decide la tarea: se reutiliza uno de la solución si existe. +**Consumidores afectados:** Scalar en Development (deja de ver lo no público si no se conserva un documento completo); `grep -rn "AddOpenApi\|MapOpenApi\|MapScalarApiReference" WebApi.*`. +**Criterios de aceptación (todos con `→ verificar`):** +- El documento local trae 100 operaciones → verificar: `curl -s http://localhost:5000/api/openapi/v4.json | jq '[.paths[]|keys[]]|length'`, una vez que BE-03 mapee la ruta; antes, desde la prueba. +- Nada interno aparece (Tenants, TestAccounts, Checkouts, Impersonation, register, login, `load`) → verificar: la prueba de equivalencia, más `jq '.paths|keys'` revisado a mano. +- La prueba falla si se quita un `[PublicApi]` o una exclusión → verificar: quitar uno, correr la prueba y verla en rojo; luego restaurar. +**Pruebas focalizadas:** sí, por contrato y exposición. `dotnet test WebApi.Tests/WebApi.Tests.csproj --filter "FullyQualifiedName~OpenApi"`. +**Fuera de alcance:** `operationId` y metadatos (BE-02); servir la ruta (BE-03); resúmenes (BE-04 y BE-05). +**Notas:** + +### BE-02 — Agregar `operationId` estable y metadatos del documento +**Objetivo:** que cada operación tenga un `operationId` estable y que el documento declare servidores reales, autenticación y descripción. +**Depende de:** BE-01. +**Skills:** `dotnet-aspnetcore:dotnet-webapi`, `dotnet-test:run-tests`. +**Modelo sugerido (opcional):** opus. +**Contexto:** D-06, D-13, RF-01. +**Alcance:** +- `operationId` en las 100 acciones con el mecanismo de nombre de endpoint de ASP.NET Core 10, en camelCase y en inglés (`createInvoice`, `cancelInvoice`, …). La convención queda en D-13 al cerrar. +- Transformer de documento: + - `info` con título, versión de la API y la `description` tomada del spec de Postman (`postman/specs/Fiscalapi Specification`, solo como fuente del texto); + - `servers` con `https://test.fiscalapi.com` y `https://live.fiscalapi.com`; + - esquemas de seguridad `apiKey` en header para `X-API-KEY` y `X-TENANT-KEY`, aplicados a las operaciones no anónimas; + - parámetro de header opcional `X-TIME-ZONE`. +- Prueba: los `operationId` son únicos y no vacíos, y las operaciones de `portal/*` no exigen seguridad. +**Textos visibles (propuesta al usuario):** el `info.description` final; se le muestra al usuario antes del commit (contrato público). +**Criterios de aceptación (todos con `→ verificar`):** +- 100 `operationId` únicos → verificar: `jq '[.paths[][]|.operationId]|unique|length'` igual a 100. +- Hay servidores y esquemas de seguridad → verificar: `jq '.servers, .components.securitySchemes'`. +- El documento pasa un lint de OpenAPI 3.1 → verificar: `npx @redocly/cli@ lint` sobre el JSON descargado. +**Pruebas focalizadas:** sí (contrato). `--filter "FullyQualifiedName~OpenApi"`. +**Fuera de alcance:** resúmenes por operación (BE-04 y BE-05). +**Notas:** + +### BE-03 — Servir `/api/openapi/v4.json` anónimo en test y live +**Objetivo:** que cualquier agente o herramienta descargue el contrato sin credenciales y sin tocar nginx. +**Depende de:** BE-02. +**Skills:** `dotnet-aspnetcore:dotnet-webapi`, `fapi-release` (verifica que no haya pasos manuales de deploy), `fapi-e2e-validate`. +**Modelo sugerido (opcional):** opus. Expone una ruta anónima en producción. +**Contexto:** D-16. Hoy `MapOpenApi` y Scalar solo se registran en Development (`WebConfigurePipeline`). +**Alcance:** +- `MapOpenApi("/api/openapi/{documentName}.json")` en todos los ambientes, anónimo y con caché HTTP. Scalar sigue solo en Development. +- Verificar que el middleware de autenticación y de tenant (`X-TENANT-KEY` obligatorio) no rechaza esta ruta. +**Criterios de aceptación (todos con `→ verificar`):** +- Responde 200 sin headers de auth en local con `ASPNETCORE_ENVIRONMENT=Test` → verificar: `curl -si http://localhost:5000/api/openapi/v4.json | head -1`. +- No existe ruta para el documento v5 ni para uno interno → verificar: `curl -si …/api/openapi/v5.json` devuelve 404. +- Tras el deploy del usuario, responde en test y live → verificar: `curl -si https://test.fiscalapi.com/api/openapi/v4.json` (en CIERRE). +**Pruebas focalizadas:** sí (exposición). Prueba de integración: la ruta responde sin auth y no expone un documento distinto de `v4`. +**Fuera de alcance:** importar a Postman (U-03). +**Notas:** + +### BE-04 — Agregar el trinquete de `EndpointSummary` y cubrir facturas +**Objetivo:** que ninguna operación pública nueva quede sin resumen, empezando por la ruta crítica del integrador. +**Depende de:** BE-01. +**Skills:** `dotnet-aspnetcore:dotnet-webapi`, `dotnet-test:run-tests`. +**Modelo sugerido (opcional):** haiku para escribir los resúmenes; opus revisa la prueba. +**Contexto:** RF-01. Cada resumen es una línea en español más una `EndpointDescription` con el enlace a la sección de docs. No se ingieren comentarios XML: los `///` son notas internas. +**Alcance:** +- Prueba trinquete: lista las operaciones públicas sin `EndpointSummary` y la compara con una lista permitida en el código que solo puede bajar. +- Resúmenes de Invoices, People y TaxFiles. +**Textos visibles (propuesta al usuario):** los resúmenes de esta tarea; el usuario los revisa en el diff antes del commit. +**Criterios de aceptación (todos con `→ verificar`):** +- Agregar un `[PublicApi]` sin resumen hace fallar la prueba → verificar: agregarlo de forma temporal y ver la prueba en rojo. +- Invoices, People y TaxFiles salen de la lista permitida → verificar: `jq '[.paths[][]|select(.summary==null)]|length'` baja en ese número. +**Pruebas focalizadas:** sí (contrato). `--filter "FullyQualifiedName~OpenApi"`. +**Fuera de alcance:** el resto de controllers (BE-05). +**Notas:** + +### BE-05 — Completar `EndpointSummary` en el resto de la superficie +**Objetivo:** que la lista permitida del trinquete quede vacía. +**Depende de:** BE-04. +**Skills:** `dotnet-aspnetcore:dotnet-webapi`, `dotnet-test:run-tests`. +**Modelo sugerido (opcional):** haiku. +**Contexto:** igual que BE-04. +**Alcance:** resúmenes y descripciones con enlace de Catalogs, Products, Stamps, ApiKeys, DownloadCatalogs, DownloadRequests, DownloadRules, Manifests, SatValidations, Tickets, Portal, AutoInvoice, Webhooks y Events (los que falten); la lista permitida se borra. +**Textos visibles (propuesta al usuario):** los resúmenes de esta tarea, revisados en el diff. +**Criterios de aceptación (todos con `→ verificar`):** +- No hay operaciones públicas sin resumen → verificar: `jq '[.paths[][]|select(.summary==null)]|length'` igual a 0. +- La lista permitida ya no existe → verificar: grep del nombre de la lista en WebApi.Tests sin resultados. +**Pruebas focalizadas:** las mismas de BE-04. +**Fuera de alcance:** — +**Notas:** diff --git a/docs/ai-integracion/decisiones.md b/docs/ai-integracion/decisiones.md new file mode 100644 index 0000000..7f9cb8f --- /dev/null +++ b/docs/ai-integracion/decisiones.md @@ -0,0 +1,116 @@ +# Integración asistida por IA: decisiones vigentes + +Reglas de este archivo: +- Cada decisión describe el estado actual. Si cambia, se reescribe en su lugar; la historia vive en git. +- Estado (uno de tres): + - `confirmada`: el usuario la aprobó o es técnica ya resuelta. + - `propuesta`: requiere el OK del usuario antes de la tarea que la usa. + - `técnica — la fija `: la decide el agente en esa tarea. +- Una decisión nueva se agrega al final, con el siguiente D-NN libre. +- Vocabulario del feature: + - **superficie pública:** las acciones con `[PublicApi]`. + - **catálogo de operaciones:** la lista compacta operationId + método + ruta + resumen. + - **sección:** un bloque H2 de una página de docs con su propia `.md`. + - **variante de eval:** sin nada, con skills, o con skills más MCP. + +--- + +### D-01 — La audiencia son los desarrolladores que integran +- **Decisión:** los recursos van dirigidos a equipos que meten FiscalAPI en su ERP, POS o SaaS desde agentes de código (Claude Code, Codex, Cursor, Copilot). No a contadores ni pymes que facturan desde un chat. +- **Por qué:** es donde se reduce el tiempo de integración, y en test no hay riesgo fiscal. +- **Consecuencias:** nada de acciones en live; las skills enseñan a escribir el código de integración. +- **Estado:** confirmada. + +### D-02 — Cero mantenimiento adicional y nada hosteado +- **Decisión:** no se crea ningún servicio en ejecución (ni MCP remoto, ni BD, ni modelos). Todo lo derivado se genera solo desde el código o las docs. +- **Por qué:** es una restricción dura del usuario: "No quiero agregar superficie de mantenimiento adicional a fiscalapi". +- **Consecuencias:** se descarta el MCP remoto; el MCP stdio lleva el índice dentro del paquete y se regenera en CI (D-07, D-14). +- **Estado:** confirmada. + +### D-03 — Las skills enrutan y enlazan; no copian las docs +- **Decisión:** 8 skills (`fiscalapi-integracion`, `-ingreso-egreso`, `-pagos`, `-nomina`, `-carta-porte`, `-comercio-exterior`, `-ciclo-de-vida`, `-diagnostico`) y 3 comandos (`primer-timbre`, `explicar-error`, `revisar-integracion`). Siguen el estándar agentskills.io. Sus `references/` solo tienen índices de enlaces a las `.md` y árboles de decisión. +- **Por qué:** si copiaran contenido de las docs, cada cambio de API obligaría a editarlas en paralelo (D-02). +- **Consecuencias:** una skill solo cambia cuando cambia "cómo se decide"; un enlace roto lo detecta el CI de SK-01. +- **Estado:** confirmada. + +### D-04 — Todo en español +- **Decisión:** las skills, las herramientas y descripciones del MCP y las docs de este feature van en español. +- **Por qué:** docs.fiscalapi.com y el vocabulario del SAT están en español. +- **Consecuencias:** las consultas de regresión y las evals se escriben en español. Si la localización de fapi-docs se fusiona, se revisa si `buscar_docs` debe indexar también el inglés. +- **Estado:** confirmada. + +### D-05 — OpenAPI desde el código; la superficie es la colección menos 6 +- **Decisión:** `/api/openapi/v4.json` se genera del código. Incluye solo las acciones con `[PublicApi]`: las 106 de la colección "Fiscalapi API Reference" menos `POST account/register`, `POST account/login`, `GET reports/params/{documentType}/{tin}/{invoiceType}`, `GET tickets/load`, `GET webhooks/load` y `GET webhooks/{id}/deliveries/load`. En total, 100. `portal/*` y `events/trigger` sí quedan públicos. +- **Por qué:** lo indicó el usuario. La colección ya es la referencia pública validada. +- **Consecuencias:** una prueba de equivalencia lee la colección, resta esas 6 exclusiones y exige igualdad con el documento. Que `portal/*` y `events/trigger` sigan públicos es interpretación del agente (el usuario no los excluyó); el usuario puede corregirla. +- **Estado:** confirmada. + +### D-06 — Un solo contrato; la colección conserva su propósito +- **Decisión:** el spec "Fiscalapi Specification" del workspace de Postman se reemplaza importando `/api/openapi/v4.json` (U-03) y deja de generarse desde la colección. La colección sigue siendo el artefacto de ejemplos curados por tipo de CFDI. +- **Por qué:** dos contratos en paralelo serían mantenimiento extra. El spec generado desde la colección no tiene esquemas ni `operationId`. +- **Consecuencias:** el `info.description` del spec de Postman pasa al transformer de BE-02. El archivo local `postman/specs/` queda obsoleto. +- **Estado:** confirmada. + +### D-07 — MCP stdio de solo lectura con 3 herramientas +- **Decisión:** `@fiscalapi/mcp` expone `listar_operaciones(tag?)`, `obtener_operacion(operationId)` (con los `$ref` resueltos y recortado) y `buscar_docs(consulta)`. No tiene `invocar`, y no usa red en runtime. +- **Por qué:** la llamada a la API debe vivir en el código del integrador. Invocar desde el MCP no adelanta la integración y agrega superficie de seguridad. +- **Consecuencias:** el OpenAPI y el índice van dentro del paquete. Se publica solo si EV-03 muestra mejora frente a las skills solas (U-04). +- **Estado:** confirmada. + +### D-08 — Se busca en las docs, no en el OpenAPI, y sin embeddings +- **Decisión:** `listar_operaciones` devuelve el catálogo completo, sin búsqueda. `buscar_docs` usa MiniSearch sobre las secciones, con un `processTerm` en español: minúsculas, sin acentos, stopwords de `@orama/stopwords` y stemmer de `@orama/stemmers`. +- **Por qué:** medido (ver `requisitos.md`). Sobre las operaciones, todos los motores fallan en lo fiscal; sobre las secciones de las docs, el léxico acierta 14 de 16. Los embeddings cuestan 130 MB y 14 s de arranque sin ganar nada. +- **Consecuencias:** las 16 consultas de la medición son la prueba de regresión de MCP-02. +- **Estado:** confirmada. + +### D-09 — El sandbox sin "compra" se pospone +- **Decisión:** no se toca billing en este feature. La skill `fiscalapi-integracion` guía por el flujo actual (cuenta, suscripción de prueba con tarjeta de prueba y llaves). +- **Por qué:** sería código nuevo en un área crítica. El usuario pidió medir primero. +- **Consecuencias:** MED-02 mide el abandono en el paso de compra y el resultado se lleva al usuario. +- **Estado:** confirmada. + +### D-10 — Los CSD de prueba se siguen enlazando desde SW +- **Decisión:** no se alojan certificados de prueba propios. +- **Por qué:** alojarlos obliga a renovarlos cuando venzan (D-02). +- **Consecuencias:** las skills enlazan a la página `testing-data` de las docs. +- **Estado:** confirmada. + +### D-11 — Evals manuales antes de cada release, en 3 variantes +- **Decisión:** cada caso CFDI se corre sin nada, con skills, y con skills más MCP. Se califica con un UUID real en test más la consulta de su estado, y se cuentan los tokens. Se corre a mano antes de publicar, no en CI nocturno. +- **Por qué:** se mide la mejora sin un costo fijo de tokens. +- **Consecuencias:** EV-01 decide si reutilizar `skill-validator` de dotnet/skills o escribir un script mínimo. +- **Estado:** técnica — la fija EV-01. + +### D-12 — El feature vive en fiscalapi-ai +- **Decisión:** el tablero y todo el trabajo de skills, MCP y evals van en `FiscalAPI/fiscalapi-ai` (público), en la rama `feat/ai-integracion` desde `main`. fapi-back y fapi-docs solo reciben los cambios mínimos de BE-* y DOC-*, cada uno en su rama. +- **Por qué:** lo indicó el usuario: "solo escribe sobre fiscalapi-ai". +- **Consecuencias:** las filas BE-* y DOC-* se cierran con `@`. +- **Estado:** confirmada. + +### D-13 — `operationId` estable por acción pública +- **Decisión:** cada acción con `[PublicApi]` declara un `operationId` estable, en camelCase y en inglés como el código (por ejemplo `createInvoice`). Se fija con el mecanismo de nombre de endpoint de ASP.NET Core 10, verificado en la documentación oficial. +- **Por qué:** el MCP y las skills lo usan como id. El spec de Postman no trae ninguno. +- **Consecuencias:** cambiar un `operationId` es un cambio visible para los consumidores del OpenAPI. +- **Estado:** técnica — la fija BE-02. + +### D-14 — Paquete `@fiscalapi/mcp`, publicado por CI cuando cambia el hash +- **Decisión:** + - Nombre `@fiscalapi/mcp`, publicado con `access: public` por el usuario npm `fiscalapi`. + - Node ≥22 y `@modelcontextprotocol/server` v2 con versión fijada, más Dependabot. + - El build descarga `/api/openapi/v4.json` de live y las `.md` de sección, y publica solo si cambia el hash. + - Publica con trusted publishing desde un runner hospedado por GitHub. +- **Por qué:** el usuario confirmó el nombre (2026-10-14), que está libre en npm. Trusted publishing no acepta runners self-hosted. +- **Consecuencias:** MCP-03 verifica en la documentación de npm si la primera publicación puede ser por OIDC o si requiere una publicación manual del usuario. +- **Estado:** confirmada. + +### D-15 — Sin `llms-full.txt` +- **Decisión:** no se genera `llms-full.txt`. Lo que se agrega es la `.md` por sección. +- **Por qué:** serían unos 600k tokens, inservibles para un agente; una sola sección pesa hasta 156 KB. +- **Consecuencias:** DOC-01 genera las secciones y `llms.txt` sigue siendo el índice. +- **Estado:** confirmada. + +### D-16 — El OpenAPI se sirve bajo `/api` para no tocar nginx +- **Decisión:** `MapOpenApi` se mapea a `/api/openapi/{documentName}.json`, anónimo y en todos los ambientes. +- **Por qué:** el nginx de test y el de live ya mandan `location ^~ /api` al back. El CD no toca nginx, que se cambia a mano (`infra/nginx/live/README.md`). Con otra ruta habría un paso manual. +- **Consecuencias:** cero cambios en nginx. La ruta hereda el rate limiting de `/api`. +- **Estado:** confirmada. diff --git a/docs/ai-integracion/docs.md b/docs/ai-integracion/docs.md new file mode 100644 index 0000000..a8a0f4f --- /dev/null +++ b/docs/ai-integracion/docs.md @@ -0,0 +1,54 @@ +# Integración asistida por IA: docs (fapi-docs y fapi-reusables) + +El código va en fapi-docs (y en fapi-reusables solo si cambia el generador), en la rama `feat/ai-integracion` desde `main`. La fila del tablero se cierra con `@`. + +**Hecho al planear:** fapi-docs tenía en curso la rama `feat/localization`. En `main` hay 0 archivos `content.en.mdx` y 52 `content.mdx`. Antes de DOC-01, revisa si la localización ya llegó a `main`; si llegó, la generación por sección cubre también las páginas en inglés y se carga `fapi-i18n`. + +### DOC-01 — Generar `.md` por sección H2 en el build +**Objetivo:** que un agente pueda leer una sola sección (por ejemplo "Crear factura de pago") sin descargar la página completa, que llega a 647 KB. +**Depende de:** — +**Skills:** `fapi-sdk-docs-sync` (docs públicas), `fapi-i18n` (solo si la localización ya está en `main`). +**Modelo sugerido (opcional):** opus. Cambia rutas públicas del sitio. +**Contexto:** D-15, RF-02. Hoy `src/app/md/[[...slug]]/route.js` y los rewrites de `next.config.mjs` sirven `.md`, y `getPageMarkdown` vive en `src/lib/docs.js`. +**Alcance:** +- Ruta de sección `//.md`, con el mismo slug que el ancla H2 que ya genera el sitio. Devuelve el markdown de esa sección, del H2 hasta el siguiente H2. +- Se genera desde los mismos MDX en build (prerender), sin contenido nuevo que mantener. +- `llms.txt` agrega, por página, la lista de sus secciones con su URL `.md`. +**Criterios de aceptación (todos con `→ verificar`):** +- La sección se sirve sola → verificar: `npm run build && npm start` y `curl -s localhost:3000/invoices-by-value/crear-factura-de-pago.md | head`; el contenido empieza en ese H2 y no incluye el siguiente. +- Cada ancla del sitio tiene su `.md` → verificar: un script de build cuenta las secciones de los MDX (427 al planear) y las rutas generadas, y las compara. +- `llms.txt` lista las secciones → verificar: `curl -s localhost:3000/llms.txt | grep -c "\.md"` sube respecto a `main`. +**Pruebas focalizadas:** ninguna (no aplica). La verificación es el conteo del build. +**Fuera de alcance:** `llms-full.txt` (D-15). +**Notas:** + +### DOC-02 — Generar páginas de errores de Pagos y Comercio Exterior +**Objetivo:** que `fiscalapi-diagnostico` y `explicar-error` puedan enlazar los errores `CRP*` y `CCE*`, igual que hoy enlazan los de CFDI 4.0, Nómina y Carta Porte. +**Depende de:** — +**Skills:** `fapi-cfdi-rules`, `fapi-sdk-docs-sync`. +**Modelo sugerido (opcional):** haiku. Corre el generador existente. +**Contexto:** RF-03. Las páginas actuales salen de `fapi-reusables/scripts/sat-docs-md-errors-generator/app.py` sobre las matrices de `fapi-reusables/sat-docs`. Ya existen `sat-docs/pagos` y `sat-docs/comercio-exterior`. +**Alcance:** +- Correr el generador con las matrices de Pagos 2.0 y Comercio Exterior 2.0 y crear las páginas `errors/pagos-20` y `errors/comercio-exterior-20` con el mismo formato que `errors/cfdi-40`. +- Agregarlas a la navegación y a `llms.txt`. +- Si el generador necesita un cambio, va en fapi-reusables en su propio commit. +**Criterios de aceptación (todos con `→ verificar`):** +- Las dos páginas responden en `.md` → verificar: `curl -s localhost:3000/errors/pagos-20.md | grep -c "CRP"` y `curl -s localhost:3000/errors/comercio-exterior-20.md | grep -c "CCE"` mayores que 0. +- El número de códigos coincide con la matriz del SAT → verificar: conteo de filas de la matriz contra el conteo en la página. +**Pruebas focalizadas:** ninguna (no aplica). +**Fuera de alcance:** traducir los mensajes del SAT. +**Notas:** + +### DOC-03 — Corregir el enlace del SDK de Node en `llms.txt` +**Objetivo:** que los agentes lleguen al repo real del SDK de Node. +**Depende de:** — +**Skills:** `fapi-sdk-docs-sync`. +**Modelo sugerido (opcional):** haiku. +**Contexto:** `src/app/llms.txt/route.js` enlaza `github.com/FiscalAPI/fiscalapi-js`, y el repo real es `fiscalapi-node`. +**Alcance:** cambiar el enlace y hacer grep de `fiscalapi-js` en todo fapi-docs por si aparece en otro lugar. +**Criterios de aceptación (todos con `→ verificar`):** +- No queda ningún `fiscalapi-js` → verificar: `grep -rn "fiscalapi-js" src` sin resultados. +- El enlace nuevo existe → verificar: `gh repo view FiscalAPI/fiscalapi-node --json name`. +**Pruebas focalizadas:** ninguna (no aplica). +**Fuera de alcance:** — +**Notas:** diff --git a/docs/ai-integracion/evals.md b/docs/ai-integracion/evals.md new file mode 100644 index 0000000..7f23a8e --- /dev/null +++ b/docs/ai-integracion/evals.md @@ -0,0 +1,61 @@ +# Integración asistida por IA: evals + +Todo va en fiscalapi-ai, en `evals/`, en la rama `feat/ai-integracion`. Las evals se corren a mano antes de cada release (D-11), con una cuenta y una llave de test de FiscalAPI que se leen de variables de entorno y nunca se escriben en el repo. + +### EV-01 — Montar el harness de evals con los casos de ingreso y pagos +**Objetivo:** medir, caso por caso, si un agente integra FiscalAPI mejor con las skills y con el MCP que sin nada. +**Depende de:** SK-05, MCP-02. +**Skills:** `anthropic-skills:skill-creator` (sección de evals), `superpowers:writing-skills`. El patrón de `skill-validator` de dotnet/skills se revisa con `gh api`. +**Modelo sugerido (opcional):** opus. Diseña la medición de la que depende U-04. +**Contexto:** D-11, RF-06. +**Alcance:** +- Elegir el runner: reutilizar `skill-validator` (dotnet/skills) o un script mínimo que lance el agente en modo no interactivo sobre un proyecto plantilla. Se decide aquí y se documenta en D-11. +- Cada caso trae: + - el prompt del integrador en español; + - un proyecto plantilla (Node Express y .NET mínimo); + - las 3 variantes de D-11. +- Calificación automática: el proyecto resultante, ejecutado, produce un UUID en test, y `GET /api/v4/invoices/{id}` confirma su estado. +- Métricas: éxito, tokens e intentos. +- Casos iniciales: factura de ingreso, nota de crédito, factura global, y factura PPD con su complemento de pago. +**Criterios de aceptación (todos con `→ verificar`):** +- Un solo comando corre los 4 casos en las 3 variantes y genera un reporte en tabla → verificar: ejecución y reporte generado. +- Un caso con una skill rota a propósito baja su tasa de éxito → verificar: corrida con un enlace o una regla falsa plantada. +**Pruebas focalizadas:** ninguna (no aplica). El harness es la prueba. +**Fuera de alcance:** correr en CI (D-11). +**Notas:** + +### EV-02 — Agregar casos de nómina, carta porte y comercio exterior +**Objetivo:** cubrir con evals todos los casos CFDI del feature. +**Depende de:** EV-01, SK-09. +**Skills:** `fapi-cfdi-rules`. +**Modelo sugerido (opcional):** haiku para escribir los casos; opus revisa la calificación. +**Contexto:** D-03; el orden y los casos extra los fija MED-01 y el top de rechazos de MED-03. +**Alcance:** +- Casos de nómina ordinaria, Carta Porte de traslado por autotransporte y exportación definitiva con Comercio Exterior. +- Un caso de "explicar error" por cada código del top 5 de MED-03. +**Criterios de aceptación (todos con `→ verificar`):** +- Los casos nuevos corren en las 3 variantes → verificar: reporte con las filas nuevas. +**Pruebas focalizadas:** ninguna (no aplica). +**Fuera de alcance:** — +**Notas:** + +### EV-03 — Correr las 3 variantes y reportar +**Objetivo:** dar al usuario los datos para U-04: publicar o no el MCP y pedir o no los marketplaces oficiales (Q-01). +**Depende de:** EV-02. +**Skills:** `superpowers:verification-before-completion`. +**Modelo sugerido (opcional):** opus. +**Contexto:** D-07, D-11, Q-01. +**Alcance:** +- Corrida completa con N repeticiones por caso (N lo fija EV-01 según la varianza). +- Tabla de éxito, tokens e intentos por caso y variante. +- Recomendación: + - si las skills mejoran frente a sin nada; + - si el MCP mejora frente a las skills solas; + - qué skills reescribir. +- El resumen va en las Notas de esta tarea y en el cuerpo del commit; no se versiona ningún archivo de evidencia. +**Criterios de aceptación (todos con `→ verificar`):** +- El reporte existe y cubre todos los casos y variantes → verificar: tabla en el cuerpo del commit. +- La recomendación se apoya en las cifras → verificar: cada afirmación cita su fila. +**Pruebas focalizadas:** ninguna (no aplica). +**Fuera de alcance:** publicar (U-04). +**Notas:** diff --git a/docs/ai-integracion/mcp.md b/docs/ai-integracion/mcp.md new file mode 100644 index 0000000..b3e6a59 --- /dev/null +++ b/docs/ai-integracion/mcp.md @@ -0,0 +1,79 @@ +# Integración asistida por IA: MCP stdio (`@fiscalapi/mcp`) + +Todo va en fiscalapi-ai, en `mcp/`, en la rama `feat/ai-integracion`. + +Antes de escribir código contra el SDK de MCP, se consulta su documentación vigente (context7 o el repo `modelcontextprotocol/typescript-sdk`). La spec cambió el 2026-07-28 y el SDK v2 se mueve rápido. + +### MCP-01 — Crear el paquete stdio con `listar_operaciones` y `obtener_operacion` +**Objetivo:** que `npx` levante un servidor stdio que dé al agente el catálogo completo de operaciones y el esquema de una operación, sin usar red. +**Depende de:** SK-01, BE-03. +**Skills:** `context7-mcp` (SDK de MCP), `superpowers:test-driven-development`. +**Modelo sugerido (opcional):** opus. Fija la base del paquete. +**Contexto:** D-07, D-08, D-13, D-14, RF-05, RNF-05, RNF-06. +**Alcance:** +- Paquete `@fiscalapi/mcp`: + - TypeScript y ESM; + - `engines.node >=22`; + - `@modelcontextprotocol/server` v2 con versión exacta; + - `bin` con shebang; + - `publishConfig.access: public`; + - licencia igual a la de los SDK. +- Script de build: + - descarga `/api/openapi/v4.json` (live por omisión; ruta local para desarrollo); + - genera `catalog.json` (operationId, método, ruta, resumen, tag) y `operations/.json` con los `$ref` resueltos y recortados a unos 10k tokens como máximo; + - guarda el hash de la fuente. +- Herramientas, con descripciones en español: + - `listar_operaciones(tag?)` devuelve el catálogo compacto; + - `obtener_operacion(operationId)` devuelve parámetros, cuerpo y respuestas; + - un operationId inexistente devuelve un error claro con sugerencias por prefijo. +- Pruebas con un cliente MCP en proceso: `tools/list`, las dos herramientas, el error de id inexistente y el límite de tamaño. +**Criterios de aceptación (todos con `→ verificar`):** +- Arranca sin red → verificar: con la red desactivada (o variable que bloquea `fetch`), `node dist/index.js` responde `tools/list`. +- `listar_operaciones` devuelve 100 operaciones y cabe en unos 3k tokens como máximo → verificar: prueba que cuenta elementos y caracteres. +- `obtener_operacion("createInvoice")` (el nombre que fije D-13) no pasa del límite → verificar: prueba de tamaño. +- Funciona registrado en Claude Code → verificar: `claude mcp add fiscalapi -- node /dist/index.js` y una consulta en la sesión. +**Pruebas focalizadas:** sí (contrato del paquete). `npm test` en `mcp/`. +**Fuera de alcance:** búsqueda en docs (MCP-02); publicación (MCP-03); herramienta `invocar` (D-07). +**Notas:** + +### MCP-02 — Agregar `buscar_docs` con índice en español +**Objetivo:** que el agente encuentre la sección de las docs que responde a su duda fiscal sin leer páginas completas. +**Depende de:** MCP-01, DOC-01. +**Skills:** `context7-mcp` (MiniSearch, `@orama/stemmers`), `superpowers:test-driven-development`. +**Modelo sugerido (opcional):** opus. +**Contexto:** D-04, D-08. Medición en `requisitos.md`: 14 de 16 aciertos con MiniSearch y stemmer en español sobre las secciones. +**Alcance:** +- En el build: leer `llms.txt`, descargar las `.md` de sección (DOC-01), sin bloques de código en el texto indexado, e indexar con MiniSearch. + - `processTerm`: minúsculas, quitar acentos (NFD), stopwords de `@orama/stopwords` en español y stemmer de `@orama/stemmers` en español. + - Boost al título. + - El índice se serializa con `toJSON`. +- `buscar_docs(consulta, limite=5)` devuelve título, URL `.md` de la sección, página y un extracto de unas 300 palabras como máximo. +- Prueba de regresión con las 16 consultas de la medición y la sección esperada de cada una. "lista negra 69b" queda documentada como caso conocido si sigue fallando. +**Criterios de aceptación (todos con `→ verificar`):** +- 15 de 16 consultas de regresión, o más, traen la sección esperada en el top 3 → verificar: `npm test`. +- La salida de `buscar_docs` no pasa de unos 10k tokens → verificar: prueba de tamaño. +- El paquete completo pesa menos de 5 MB → verificar: `npm pack --dry-run`. +**Pruebas focalizadas:** sí. `npm test` en `mcp/`. +**Fuera de alcance:** embeddings (D-08). +**Notas:** + +### MCP-03 — Agregar el workflow de build y publicación por hash +**Objetivo:** que el paquete se regenere solo cuando cambian el OpenAPI o las docs, sin que nadie lo mantenga. +**Depende de:** MCP-02, U-02. +**Skills:** `fapi-release` (CI/CD sin pasos manuales), `context7-mcp` (documentación de npm trusted publishing). +**Modelo sugerido (opcional):** opus. Publica en un registro público. +**Contexto:** D-02, D-14. Trusted publishing exige un runner hospedado por GitHub, `id-token: write`, npm ≥11.5.1 y Node ≥22.14 (docs.npmjs.com/trusted-publishers, a revalidar en la tarea). +**Alcance:** +- Workflow en push a `main` (ruta `mcp/**`), semanal y manual: + 1. build; + 2. comparar el hash de la fuente con el de la última versión publicada; + 3. si cambió, subir el patch y publicar con provenance. +- El workflow queda deshabilitado para publicar (`if:` con una variable del repo) hasta que el usuario decida U-04. +- Verificar en la documentación de npm si la primera publicación puede hacerse por OIDC o requiere una publicación manual del usuario, y dejar el paso exacto en las Notas. +- Dependabot para `mcp/package.json`. +**Criterios de aceptación (todos con `→ verificar`):** +- El build sin cambios no publica → verificar: corrida manual del workflow con publicación en dry-run; el log dice "sin cambios". +- Con un cambio simulado en la fuente, el dry-run publicaría una versión nueva → verificar: corrida con un hash forzado y `npm publish --dry-run` en el log. +**Pruebas focalizadas:** ninguna (no aplica). La prueba es el dry-run. +**Fuera de alcance:** publicar de verdad (U-04). +**Notas:** diff --git a/docs/ai-integracion/medicion.md b/docs/ai-integracion/medicion.md new file mode 100644 index 0000000..41d17ee --- /dev/null +++ b/docs/ai-integracion/medicion.md @@ -0,0 +1,61 @@ +# Integración asistida por IA: medición + +Los resultados de estas tareas van a `requisitos.md` → **Contexto medido**, con su fuente y sin datos personales (solo conteos y porcentajes). No se crea un archivo de resultados aparte. + +### MED-01 — Medir mezcla de tipos y complementos en el restore de prod +**Objetivo:** saber qué tipos de CFDI y qué complementos timbran los clientes, para ordenar SK-04 a SK-09 y las evals. +**Depende de:** — +**Skills:** `fapi-investigate`, `fapi-local-env` (restore y sqlcmd local). +**Modelo sugerido (opcional):** haiku. Son consultas de lectura, sin decisiones. +**Contexto:** D-03; la métrica norte en `requisitos.md`. +**Alcance:** +- Confirmar que el restore local es de producción (`backupset.server_name`). +- Contar, en los últimos 12 meses y por mes, las facturas timbradas por `Invoices.TypeCode`. +- Contar las facturas con fila 1:1 en `Payroll`, `CartaPorte`, `ComercioExterior`, `InvoicePayment` y `GlobalInformation`. +- Contar los tenants distintos que usan cada tipo o complemento. +**Criterios de aceptación (todos con `→ verificar`):** +- El origen del restore está confirmado → verificar: `SELECT TOP 1 server_name, backup_finish_date FROM msdb.dbo.backupset WHERE database_name='WebApi' ORDER BY backup_finish_date DESC`. +- `requisitos.md` tiene una tabla de tipos y complementos con volumen, tenants y el orden resultante → verificar: `grep -n "Mezcla de tipos" docs/ai-integracion/requisitos.md`. +- El orden de SK-04 a SK-09 y de los casos de EV-01 y EV-02 queda ajustado en el tablero si la medición lo cambia → verificar: revisar el diff del README. +**Pruebas focalizadas:** ninguna (no aplica). +**Fuera de alcance:** errores del PAC (MED-03); embudo de Test (MED-02). +**Notas:** + +### MED-02 — Medir embudo y tiempo al primer timbre en el restore de Test +**Objetivo:** fijar la línea base de la métrica norte y medir cuántas cuentas abandonan en el paso de compra de prueba (para revisar D-09). +**Depende de:** — +**Skills:** `fapi-investigate`, `fapi-local-env`. +**Modelo sugerido (opcional):** opus. Hay que interpretar el embudo y separar las cuentas internas de las reales. +**Contexto:** D-09; la métrica norte en `requisitos.md`. +**Alcance:** +- Restaurar en local el respaldo de Test (origen en la memoria local `project_local_credentials`) y confirmar su `server_name`. +- Por cohorte semanal de los últimos 6 meses: + - owners creados; + - con correo confirmado; + - con suscripción de prueba activa; + - con primer `Invoice` timbrado; + - mediana de minutos desde la creación hasta el primer timbre; + - porcentaje que timbra en menos de 24 h. +- Excluir las cuentas internas (fixtures `rl-test-*`, dominios propios) con un criterio explícito. +**Criterios de aceptación (todos con `→ verificar`):** +- `requisitos.md` tiene la tabla del embudo, la línea base de la métrica norte y el criterio de exclusión → verificar: `grep -n "Embudo de Test" docs/ai-integracion/requisitos.md`. +- El abandono en el paso de suscripción queda cuantificado y llevado al usuario como dato para D-09 → verificar: la cifra aparece en las Notas de la tarea. +**Pruebas focalizadas:** ninguna (no aplica). +**Fuera de alcance:** cambiar el onboarding (D-09). +**Notas:** + +### MED-03 — Medir el top de rechazos del PAC en Seq +**Objetivo:** saber qué códigos de rechazo del PAC son los más frecuentes, para dar prioridad a `fiscalapi-diagnostico` y a los casos de las evals. +**Depende de:** U-01 (el usuario abre el túnel a Seq de producción). +**Skills:** `fapi-investigate`. +**Modelo sugerido (opcional):** haiku. +**Contexto:** los rechazos no se guardan en la BD; solo quedan en Seq con las propiedades `PacCode` y `TypeCode` (log de `InvoiceIssuanceService`). +**Alcance:** +- En Seq, por el túnel, sacar los 30 `PacCode` más frecuentes de los últimos 90 días, con su `TypeCode` y el número de tenants afectados. +- Cruzarlos con las tablas `/errors/*.md` de las docs para confirmar que cada código tiene página. +**Criterios de aceptación (todos con `→ verificar`):** +- `requisitos.md` tiene el top 30 con su volumen y si tiene página en las docs → verificar: `grep -n "Top de rechazos" docs/ai-integracion/requisitos.md`. +- Los códigos sin página quedan como Notas de DOC-02 o como "Fuera de alcance" → verificar: revisar el diff. +**Pruebas focalizadas:** ninguna (no aplica). +**Fuera de alcance:** guardar los rechazos en la BD. +**Notas:** diff --git a/docs/ai-integracion/requisitos.md b/docs/ai-integracion/requisitos.md new file mode 100644 index 0000000..24e63fd --- /dev/null +++ b/docs/ai-integracion/requisitos.md @@ -0,0 +1,104 @@ +# Integración asistida por IA: requisitos + +## Contexto medido +Fecha de las mediciones: 2026-10-14. + +**Lo que FiscalAPI ya tiene** +- docs.fiscalapi.com (Next.js y MDX) publica `llms.txt` y una versión `.md` de cada página (agregando `.md` a la URL). +- No hay `llms-full.txt`, ni OpenAPI público, ni MCP, ni skills para integradores. Las skills de `fapi-reusables/ai/skills` son todas internas. + +**OpenAPI** +- fapi-back genera el documento con `Microsoft.AspNetCore.OpenApi` y Scalar, pero `MapOpenApi` solo se registra en Development (`WebConfigurePipeline`). +- El documento se registra con un `BuildServiceProvider` intermedio (`Infrastructure/DependencyInjection`) y expone también el grupo v5 (`Controllers/V5/ValuesController`). +- En nginx test y live, `/openapi` cae en `location /` (el front). +- Pocos controllers V4 tienen `EndpointSummary` o `EndpointDescription`. + +**Superficie pública** +- La colección `fapi-back/postman/collections/Fiscalapi API Reference` tiene 301 requests: 107 pares MÉTODO+ruta y 106 acciones. pdf y xml del portal son una sola acción, `downloads/{downloadToken}/{format}`. +- Todos los pares tienen acción en `WebApi.App/Controllers/V4`. +- En esos mismos controllers hay 52 acciones fuera de la colección: + - `load`, `all` y `lookup` de DevExtreme; + - Account (salvo register y login); + - ProductsForSale, ProductTaxes, `webapp/manifests`; + - WebhookSecrets y WebhookCliListener. + +**Spec generado desde Postman** +- Está en `fapi-back/postman/specs/Fiscalapi Specification/Fiscalapi Specification.yaml`, sin rastrear en git: OpenAPI 3.1, 107 operaciones. +- Le falta para ser contrato: + - el servidor es `{{baseUrl}}`; + - los path params van literales (``); + - hay rutas con valores fijos; + - hay un solo ejemplo por método y ruta; + - no tiene `components/schemas` de los DTO ni `operationId`. +- Su `info.description` sí es útil. + +**Tamaño de las docs** +- 52 páginas, 427 secciones H2 y 2.3 MB de MDX (unos 600k tokens). +- La página `invoices-by-value` pesa 647 KB y su sección "Modelo factura", 156 KB. + +**Prueba de búsqueda** (desechable, mismas 14 a 16 consultas de un integrador) +- Sobre las 107 operaciones, MiniSearch 7.2.0 y Orama 3.1.18 con stemmer en español fallan en las consultas fiscales: "complemento de pago", "crear cliente receptor", "estatus ante el SAT", "descarga masiva". +- Los embeddings locales (`Xenova/multilingual-e5-small` q8, transformers.js 4.3.1) descargan 130 MB de modelo, tardan 14 s en cargar y fallan en lo mismo. +- Sobre las secciones H2 de las docs, MiniSearch con stemmer y stopwords en español (`@orama/stemmers`, `@orama/stopwords`) acierta 14 de 16. +- El catálogo de las 107 operaciones (MÉTODO, ruta y resumen) ocupa unos 6.5k caracteres, alrededor de 1,600 tokens. + +**Mercado** +- Ningún PAC ni API mexicana de CFDI publica un MCP oficial que timbre. +- Sobre Facturapi hay MCP de la comunidad (`@codespar/mcp-facturapi`, con 20 herramientas). +- Gigstack tiene un MCP de docs y un CLI con `context --json`; Alegra anuncia un MCP de solo lectura. +- Los MCP generados desde OpenAPI que corren en local (Stainless con dynamic tools, @ivotoby/openapi-mcp-server) listan el catálogo completo en vez de buscar. Los que usan embeddings (Speakeasy Gram, el buscador de docs de Stainless) son hosteados. + +**npm** +- `@fiscalapi/mcp` no existe (404). +- Los paquetes `fiscalapi` y `fiscalapi-csf` los publica el usuario npm `fiscalapi`. +- El SDK de MCP v2 (`@modelcontextprotocol/server`) se publicó el 2026-07-27 con la spec 2026-07-28. +- Node 20 ya no tiene soporte. +- npm trusted publishing exige un runner hospedado por GitHub. + +**Sin medir todavía:** la mezcla de tipos y complementos (MED-01), el embudo de Test (MED-02) y el top de rechazos del PAC (MED-03). + +## Requisitos funcionales +- **RF-01 — OpenAPI público.** `GET https://{test,live}.fiscalapi.com/api/openapi/v4.json` responde sin autenticación con exactamente las 100 acciones de D-05. Cada operación tiene `operationId` estable y servidores reales. + Aceptación: la prueba de equivalencia con la colección pasa y `curl` a ambos ambientes responde 200. +- **RF-02 — Docs por sección.** Cada sección H2 de las docs tiene su versión `.md` propia, generada en el build. + Aceptación: `curl` a la `.md` de una sección devuelve solo esa sección. +- **RF-03 — Errores de Pagos y Comercio Exterior.** Las docs publican las tablas de errores de Pagos 2.0 y Comercio Exterior 2.0, igual que las de CFDI 4.0, Nómina y Carta Porte. + Aceptación: las dos rutas `.md` responden 200. +- **RF-04 — Skills.** Las 8 skills y los 3 comandos de D-03 se instalan como plugin en Claude Code, Codex, Cursor y Copilot. + Aceptación: instalación desde el repo en los 4 hosts. +- **RF-05 — MCP stdio.** `npx -y @fiscalapi/mcp` expone `listar_operaciones`, `obtener_operacion` y `buscar_docs` sin usar red en runtime. + Aceptación: las 16 consultas de regresión de `buscar_docs` pasan. +- **RF-06 — Evals.** Cada caso CFDI se corre en 3 variantes (sin nada, con skills, con skills más MCP), calificadas por un UUID real en test. + Aceptación: el reporte de EV-03 incluye la tasa de éxito y los tokens por caso y variante. + +## Requisitos no funcionales +- **RNF-01 — Cero mantenimiento adicional (D-02).** Nada hosteado. Todo lo derivado (OpenAPI, `.md` por sección, índice del MCP) se genera solo desde el código o las docs. Las skills enlazan, no copian. +- **RNF-02 — Compatibilidad v4.** Publicar el OpenAPI no cambia ningún contrato: solo agrega metadatos y una ruta de lectura. +- **RNF-03 — Sin exposición de lo interno.** Ninguna ruta Host, solo JWT, InternalKey, `webapp/`, de facturación del dashboard, register o login aparece en el OpenAPI. Lo garantiza la prueba de equivalencia. +- **RNF-04 — Seguridad de llaves.** Las skills y el MCP nunca piden ni guardan llaves live. El MCP no invoca la API (D-07). +- **RNF-05 — Presupuesto de contexto.** + - `SKILL.md` en menos de 500 líneas. + - La salida de una herramienta del MCP en menos de unos 10k tokens. + - `listar_operaciones` completo en menos de unos 3k tokens. +- **RNF-06 — Arranque del MCP.** Menos de 1 s y funcionando sin conexión: el índice va dentro del paquete y no se descarga ningún modelo. +- **RNF-07 — Español.** Skills, herramientas, descripciones y mensajes en español (D-04). + +## Métrica norte +- Mediana de minutos entre la creación del owner y su primer `Invoice` timbrado en la BD de Test, por cohorte semanal. +- Porcentaje de cuentas nuevas que timbran en menos de 24 h. + +La línea base la fija MED-02. Se mide sin telemetría nueva. + +## Preguntas abiertas +- **Q-01 — Marketplaces oficiales.** ¿Se solicita la inclusión en `claude-plugins-official` y en el catálogo curado de Codex, además del marketplace propio del repo? + - Opciones: (a) solo el marketplace propio; (b) los dos. + - Recomendación: (a) hasta tener el reporte de EV-03, porque la inclusión se pide con evidencia de calidad. + - Bloquea: U-04. + +## Fuera de alcance +- **MCP remoto u hosteado** (D-02): exige operar un servicio. +- **Sandbox sin "compra"** (D-09): requiere código nuevo en billing. Se revisa con los datos de MED-02. +- **CSD de prueba propios** (D-10): habría que renovarlos al vencer. Se sigue enlazando el ZIP de SW. +- **`llms-full.txt`** (D-15): unos 600k tokens, no le sirve a un agente. +- **Búsqueda semántica con embeddings:** medida sin ganancia (ver Contexto medido). +- **Herramienta `invocar` en el MCP** (D-07). diff --git a/docs/ai-integracion/skills.md b/docs/ai-integracion/skills.md new file mode 100644 index 0000000..b0d2dee --- /dev/null +++ b/docs/ai-integracion/skills.md @@ -0,0 +1,166 @@ +# Integración asistida por IA: skills y plugin + +Todo va en fiscalapi-ai, en la rama `feat/ai-integracion`. Reglas comunes a SK-02 a SK-09: +- **Formato agentskills.io:** + - `name` igual al nombre de la carpeta; + - `description` de 1024 caracteres como máximo, que diga qué hace la skill, cuándo usarla y cuándo no; + - `SKILL.md` en menos de 500 líneas. +- **D-03:** `references/` solo tiene índices de enlaces a las `.md` (de página o de sección) y árboles de decisión. Nada de payloads, tablas de catálogos ni mensajes de error copiados. +- **Fuentes permitidas:** + - las `.md` de docs.fiscalapi.com; + - `/api/openapi/v4.json`; + - las herramientas de `@fiscalapi/mcp` (opcionales: la skill funciona sin MCP); + - la API de test con la llave del integrador (`GET /api/v4/catalogs/{name}/{search}`, `POST /api/v4/invoices/calculate`). +- **Reglas de seguridad en todas:** nunca pedir ni escribir una llave live en el código; las llaves van en variables de entorno. + +### SK-01 — Armar el esqueleto del repo, los manifiestos y el CI de enlaces +**Objetivo:** que el repo se instale como plugin en Claude Code, Codex, Cursor y Copilot, y que un enlace roto a las docs haga fallar el CI. +**Depende de:** — +**Skills:** `superpowers:writing-skills`, `anthropic-skills:skill-creator`, `plugin-authoring`. El formato de cada manifiesto se toma del repo de referencia que ya lo usa (stripe/ai, dotnet/skills, DevExpress/agent-skills), revisado con `gh api`. +**Modelo sugerido (opcional):** opus. Fija la estructura de la que dependen todas las tareas. +**Contexto:** D-03, D-12, RF-04. +**Alcance:** +- Estructura: `skills/`, `.claude-plugin/marketplace.json` y `plugin.json`, `.codex-plugin/`, `.cursor-plugin/`, `.github/plugin/`, `LICENSE` y README de instalación con el comando de cada host. +- CI (GitHub Actions, runner hospedado por GitHub) en cada PR y una vez por semana: + - valida el frontmatter contra el estándar; + - extrae cada URL de docs.fiscalapi.com de `skills/**`; + - exige 200 en cada URL y que cada ancla exista. +- El MCP **no** se declara en los manifiestos todavía; eso depende de U-04 (D-07). +**Criterios de aceptación (todos con `→ verificar`):** +- Se instala en Claude Code desde la ruta local → verificar: `/plugin marketplace add ./` y `/plugin install`; `/plugin` lo muestra. +- Los manifiestos de Codex, Cursor y Copilot son válidos según el formato de referencia → verificar: diff de estructura contra el repo de referencia, más la instalación local en los hosts disponibles. +- El CI falla con un ancla rota → verificar: un PR de prueba con un ancla inventada sale en rojo. +**Pruebas focalizadas:** ninguna (no aplica). La prueba es el CI de enlaces. +**Fuera de alcance:** el contenido de las skills (SK-02 en adelante). +**Notas:** + +### SK-02 — Escribir `fiscalapi-integracion` y `primer-timbre` +**Objetivo:** que en un proyecto vacío el agente instale el SDK correcto, configure ambiente y llaves, y logre un UUID en test. +**Depende de:** SK-01, DOC-01. +**Skills:** `superpowers:writing-skills`, `fapi-cfdi-rules` (para no decir nada falso del CFDI). +**Modelo sugerido (opcional):** opus. Es la skill más usada y fija el tono de todas. +**Contexto:** D-01, D-03, D-09, D-10. +**Alcance:** +- `fiscalapi-integracion`, con: + - tabla de ruteo "qué vas a construir → qué skill y qué `.md` de sección"; + - ambientes y headers (`X-API-KEY`, `X-TENANT-KEY`, `X-TIME-ZONE`); + - elección de SDK según el lenguaje del proyecto; + - modo por valores o por referencias; + - flujo actual para obtener llaves de test (D-09) y datos de prueba (D-10); + - regla crítica: un 5xx o timeout puede significar ya timbrado, así que se consulta el estado antes de reintentar. +- Comando `primer-timbre`: detecta el lenguaje, instala el SDK, lee las llaves de variables de entorno y timbra una factura de ingreso en test. +**Criterios de aceptación (todos con `→ verificar`):** +- En un proyecto Node vacío y en uno .NET vacío, `primer-timbre` produce un UUID en test → verificar: correrlo con una llave de test local y consultar el UUID con `GET /api/v4/invoices/{id}`. +- La skill se dispara sola con "integra FiscalAPI" → verificar: el log de la sesión de Claude Code muestra la skill cargada. +- El CI de enlaces pasa → verificar: CI en verde. +**Pruebas focalizadas:** ninguna (no aplica). Lo cubre EV-01. +**Fuera de alcance:** complementos (SK-04 en adelante). +**Notas:** + +### SK-03 — Escribir `fiscalapi-diagnostico` y `explicar-error` +**Objetivo:** que ante una respuesta de error el agente distinga una validación de FiscalAPI, un rechazo del PAC o un 5xx, y proponga la corrección leyendo la tabla correcta. +**Depende de:** SK-01, DOC-02. +**Skills:** `superpowers:writing-skills`, `fapi-cfdi-rules`. +**Modelo sugerido (opcional):** opus. +**Contexto:** D-03; MED-03 (si ya terminó) para priorizar los códigos. +**Alcance:** +- Árbol de decisión: + - 400 con `propertyName` → validación; + - códigos `CFDI40*`, `CRP*`, `NOM*`, `CCE*`, `CP*` → la `.md` de `/errors/…` que corresponda; + - 5xx o timeout → consultar el estado antes de reintentar. +- Comando `explicar-error `. +**Criterios de aceptación (todos con `→ verificar`):** +- Con tres respuestas reales de ejemplo (una de cada tipo), `explicar-error` llega a la página y la fila correctas → verificar: transcript de la sesión. +- El CI de enlaces pasa → verificar: CI en verde. +**Pruebas focalizadas:** ninguna (no aplica). +**Fuera de alcance:** guardar o consultar rechazos históricos. +**Notas:** + +### SK-04 — Escribir `fiscalapi-ingreso-egreso` +**Objetivo:** que el agente arme bien una factura de ingreso, una nota de crédito y una factura global. +**Depende de:** SK-02, MED-01. +**Skills:** `superpowers:writing-skills`, `fapi-cfdi-rules`. +**Modelo sugerido (opcional):** opus. +**Contexto:** D-03. El orden de las secciones lo fija MED-01. +**Alcance:** +- Decisiones de régimen, uso CFDI, objeto de impuesto, método y forma de pago, factura global y relación de la nota de crédito. +- Cada decisión enlaza a su sección `.md`. +- Las claves del SAT se buscan con la API de catálogos de test. +**Criterios de aceptación (todos con `→ verificar`):** +- Con los prompts de ingreso, nota de crédito y global, el agente timbra en test → verificar: UUID y estado consultado (caso de EV-01). +- El CI de enlaces pasa → verificar: CI en verde. +**Pruebas focalizadas:** ninguna (no aplica). +**Fuera de alcance:** — +**Notas:** + +### SK-05 — Escribir `fiscalapi-pagos` +**Objetivo:** que el agente emita un REP 2.0 correcto para facturas PPD y parcialidades. +**Depende de:** SK-02, MED-01. +**Skills:** `superpowers:writing-skills`, `fapi-cfdi-rules`. +**Modelo sugerido (opcional):** opus. +**Contexto:** D-03; errores `CRP*` (DOC-02). +**Alcance:** PUE frente a PPD, saldo anterior e insoluto, parcialidades, tipo de cambio y equivalencia, impuestos del pago; cada punto con su sección `.md`. +**Criterios de aceptación (todos con `→ verificar`):** +- El agente timbra en test una factura PPD y su complemento de pago → verificar: los dos UUID y su estado (caso de EV-01). +- El CI de enlaces pasa → verificar: CI en verde. +**Pruebas focalizadas:** ninguna (no aplica). +**Fuera de alcance:** — +**Notas:** + +### SK-06 — Escribir `fiscalapi-ciclo-de-vida` y `revisar-integracion` +**Objetivo:** que el agente cubra lo que pasa después de timbrar (cancelar, consultar estado, XML y PDF, correo y webhooks) y revise una integración existente. +**Depende de:** SK-02. +**Skills:** `superpowers:writing-skills`, `fapi-cfdi-rules`. +**Modelo sugerido (opcional):** haiku para el borrador; opus revisa. +**Contexto:** D-03. `fiscalapi-cli` cubre webhooks en local (`listen`, `trigger`); la skill enlaza a su página de docs. +**Alcance:** +- Skill con cancelación (motivos y sustitución), estado ante el SAT, descarga de XML y PDF, envío por correo y verificación de la firma de webhooks. +- Comando `revisar-integracion`: busca llaves `sk_live_` o llaves en código, falta de `X-TIME-ZONE`, reintentos a ciegas tras un 5xx y webhooks sin verificar la firma. +**Criterios de aceptación (todos con `→ verificar`):** +- `revisar-integracion` detecta 4 defectos plantados en un proyecto de prueba → verificar: transcript con los 4 hallazgos. +- El CI de enlaces pasa → verificar: CI en verde. +**Pruebas focalizadas:** ninguna (no aplica). +**Fuera de alcance:** descarga masiva (se evalúa con MED-01 si merece su propia skill). +**Notas:** + +### SK-07 — Escribir `fiscalapi-nomina` +**Objetivo:** que el agente emita una nómina 1.2 correcta (empleador, empleado, percepciones y deducciones). +**Depende de:** SK-02, MED-01. +**Skills:** `superpowers:writing-skills`, `fapi-cfdi-rules`. +**Modelo sugerido (opcional):** opus. +**Contexto:** D-03; errores `NOM*` (página existente). +**Alcance:** datos de empleador y empleado en la persona, tipos de nómina, percepciones, deducciones, otros pagos e incapacidades; cada punto con su sección `.md`. +**Criterios de aceptación (todos con `→ verificar`):** +- El agente timbra en test una nómina ordinaria → verificar: UUID y estado (caso de EV-02). +- El CI de enlaces pasa → verificar: CI en verde. +**Pruebas focalizadas:** ninguna (no aplica). +**Fuera de alcance:** — +**Notas:** + +### SK-08 — Escribir `fiscalapi-carta-porte` +**Objetivo:** que el agente emita una Carta Porte 3.1 correcta (ubicaciones, mercancías y autotransporte). +**Depende de:** SK-02, MED-01. +**Skills:** `superpowers:writing-skills`, `fapi-cfdi-rules`. +**Modelo sugerido (opcional):** opus. +**Contexto:** D-03; errores de Carta Porte (página existente). +**Alcance:** tipo de CFDI (ingreso o traslado), ubicaciones y distancias, mercancías y material peligroso, autotransporte y figuras; cada punto con su sección `.md` y los catálogos por la API de test. +**Criterios de aceptación (todos con `→ verificar`):** +- El agente timbra en test una Carta Porte de traslado por autotransporte → verificar: UUID y estado (caso de EV-02). +- El CI de enlaces pasa → verificar: CI en verde. +**Pruebas focalizadas:** ninguna (no aplica). +**Fuera de alcance:** transporte marítimo, aéreo y ferroviario, salvo que MED-01 diga lo contrario. +**Notas:** + +### SK-09 — Escribir `fiscalapi-comercio-exterior` +**Objetivo:** que el agente emita un CFDI de exportación con Comercio Exterior 2.0 correcto. +**Depende de:** SK-02, MED-01. +**Skills:** `superpowers:writing-skills`, `fapi-cfdi-rules`. +**Modelo sugerido (opcional):** opus. +**Contexto:** D-03; errores `CCE*` (DOC-02). +**Alcance:** clave de pedimento, Incoterm, receptor extranjero (`NumRegIdTrib`), mercancías, fracción arancelaria y tipo de cambio; cada punto con su sección `.md`. +**Criterios de aceptación (todos con `→ verificar`):** +- El agente timbra en test una exportación definitiva → verificar: UUID y estado (caso de EV-02). +- El CI de enlaces pasa → verificar: CI en verde. +**Pruebas focalizadas:** ninguna (no aplica). +**Fuera de alcance:** — +**Notas:** From 4f6664c6d6685afce3ca02db7a4c36754b6790b7 Mon Sep 17 00:00:00 2001 From: mendozagit Date: Wed, 14 Oct 2026 23:18:37 -0600 Subject: [PATCH 02/44] MED-01: mezcla de tipos y complementos medida en el restore de prod MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Restore aparte WebApi_ProdSnap_20261014 (READ_ONLY, borrado al terminar), backupset.server_name 649fafb1cc93, corte 2026-10-15 01:00 UTC. 12 meses: 60,179 timbres de 100 tenants. I 39,005 (98 tenants), N 14,481 (9), P 6,441 (27), E 252 (9); Carta Porte 177 (2); Comercio Exterior 0 desde siempre; información global 46 (columna nueva desde 2026-10-10). El orden de SK-04 a SK-09 y de EV-01/EV-02 se sostiene: el tablero no cambia. --- docs/ai-integracion/README.md | 2 +- docs/ai-integracion/medicion.md | 2 ++ docs/ai-integracion/requisitos.md | 20 +++++++++++++++++++- 3 files changed, 22 insertions(+), 2 deletions(-) diff --git a/docs/ai-integracion/README.md b/docs/ai-integracion/README.md index 1362ac1..d738e3f 100644 --- a/docs/ai-integracion/README.md +++ b/docs/ai-integracion/README.md @@ -31,7 +31,7 @@ La siguiente tarea (global o de un área) es la primera fila `pendiente` (de esa | ID | Tarea | Área | Depende de | Estado | Commit | |---|---|---|---|---|---| -| MED-01 | Medir mezcla de tipos y complementos en el restore de prod | medicion | — | pendiente | | +| MED-01 | Medir mezcla de tipos y complementos en el restore de prod | medicion | — | hecho | fiscalapi-ai (este commit) | | MED-02 | Medir embudo y tiempo al primer timbre en el restore de Test | medicion | — | pendiente | | | U-01 | Abrir el túnel a Seq de producción | usuario | — | pendiente | | | MED-03 | Medir el top de rechazos del PAC en Seq | medicion | U-01 | pendiente | | diff --git a/docs/ai-integracion/medicion.md b/docs/ai-integracion/medicion.md index 41d17ee..69944a2 100644 --- a/docs/ai-integracion/medicion.md +++ b/docs/ai-integracion/medicion.md @@ -20,6 +20,8 @@ Los resultados de estas tareas van a `requisitos.md` → **Contexto medido**, co **Pruebas focalizadas:** ninguna (no aplica). **Fuera de alcance:** errores del PAC (MED-03); embudo de Test (MED-02). **Notas:** +- Hechos corregidos: Nómina y Pagos viven en `Payrolls` e `InvoicePayments` (1:1 por índice único en `InvoiceId`); Información global no es tabla sino columnas `Invoices.GlobalInformation_*`. +- Restore aparte `WebApi_ProdSnap_20261014` (READ_ONLY), borrado al terminar. Comercio Exterior: 0 facturas desde siempre; el orden de skills no cambia. ### MED-02 — Medir embudo y tiempo al primer timbre en el restore de Test **Objetivo:** fijar la línea base de la métrica norte y medir cuántas cuentas abandonan en el paso de compra de prueba (para revisar D-09). diff --git a/docs/ai-integracion/requisitos.md b/docs/ai-integracion/requisitos.md index 24e63fd..b4118d8 100644 --- a/docs/ai-integracion/requisitos.md +++ b/docs/ai-integracion/requisitos.md @@ -55,7 +55,25 @@ Fecha de las mediciones: 2026-10-14. - Node 20 ya no tiene soporte. - npm trusted publishing exige un runner hospedado por GitHub. -**Sin medir todavía:** la mezcla de tipos y complementos (MED-01), el embudo de Test (MED-02) y el top de rechazos del PAC (MED-03). +**Mezcla de tipos y complementos** (MED-01) +- Fuente: restore local de producción (`backupset.server_name` = `649fafb1cc93`, corte 2026-10-15 01:00 UTC), facturas con `CreatedAt` del 2025-10-15 al corte. Todas tienen `Uuid`: 60,179 timbres de 100 tenants. +- Volumen y tenants: + + | Tipo o complemento | Facturas | Tenants | Tenant mayor | Skill | + |---|---|---|---|---| + | Ingreso (`I`), sin Carta Porte | 38,828 | 97 (con `E`) | 41.5 % de `I` | SK-04 | + | Egreso (`E`) | 252 | 9 | 37.3 % | SK-04 | + | Pagos (`P`, fila en `InvoicePayments`) | 6,441 | 27 | 65.7 % | SK-05 | + | Nómina (`N`, fila en `Payrolls`) | 14,481 | 9 | 82.3 % | SK-07 | + | Carta Porte (`I` con fila en `CartaPortes`) | 177 | 2 | — | SK-08 | + | Comercio Exterior (`ComercioExteriores`) | 0 (0 desde siempre) | 0 | — | SK-09 | + | Información global (`Invoices.GlobalInformation_*`) | 46 | 11 | — | SK-04 | + +- Por mes, `I` crece de 51 (2025-10) a 10,263 (2026-09); `P` de 2 a 2,112; `N` sube a 4,636 en 2026-09. Carta Porte aparece en 2026-07 (14 a 75 al mes). +- Información global es una columna nueva (primer registro 2026-10-10), así que 46 subestima su uso. No hay tabla de Impuestos Locales. +- **Orden resultante:** ingreso-egreso, pagos, nómina, carta porte y comercio exterior. Pagos va antes que nómina porque la usan 3 veces más tenants (27 contra 9) y el 82 % de la nómina es de un solo tenant. Coincide con el orden de SK-04 a SK-09 y con EV-01 (ingreso y pagos) y EV-02 (nómina, carta porte y comercio exterior), así que el tablero no cambia. + +**Sin medir todavía:** el embudo de Test (MED-02) y el top de rechazos del PAC (MED-03). ## Requisitos funcionales - **RF-01 — OpenAPI público.** `GET https://{test,live}.fiscalapi.com/api/openapi/v4.json` responde sin autenticación con exactamente las 100 acciones de D-05. Cada operación tiene `operationId` estable y servidores reales. From 8735cd91e4ff66936e462bc98c576d87ad0c3536 Mon Sep 17 00:00:00 2001 From: mendozagit Date: Wed, 14 Oct 2026 23:18:54 -0600 Subject: [PATCH 03/44] =?UTF-8?q?BE-04=20y=20BE-05:=20descripciones=20con?= =?UTF-8?q?=20el=20vocabulario=20fiscal=20de=20las=20b=C3=BAsquedas=20que?= =?UTF-8?q?=20fallaron,=20con=20las=20skills=20de=20Postman=20(indicaci?= =?UTF-8?q?=C3=B3n=20del=20usuario)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/ai-integracion/backend.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/ai-integracion/backend.md b/docs/ai-integracion/backend.md index 6489122..e325b2f 100644 --- a/docs/ai-integracion/backend.md +++ b/docs/ai-integracion/backend.md @@ -70,9 +70,9 @@ El código de estas tareas va en fapi-back, en la rama `feat/ai-integracion` des ### BE-04 — Agregar el trinquete de `EndpointSummary` y cubrir facturas **Objetivo:** que ninguna operación pública nueva quede sin resumen, empezando por la ruta crítica del integrador. **Depende de:** BE-01. -**Skills:** `dotnet-aspnetcore:dotnet-webapi`, `dotnet-test:run-tests`. +**Skills:** `dotnet-aspnetcore:dotnet-webapi`, `dotnet-test:run-tests`, `postman:api-documentation` y `postman:ai-readiness` (para redactar resúmenes y descripciones que contextualicen el OpenAPI generado). **Modelo sugerido (opcional):** haiku para escribir los resúmenes; opus revisa la prueba. -**Contexto:** RF-01. Cada resumen es una línea en español más una `EndpointDescription` con el enlace a la sección de docs. No se ingieren comentarios XML: los `///` son notas internas. +**Contexto:** RF-01. Cada resumen es una línea en español más una `EndpointDescription` con el enlace a la sección de docs. No se ingieren comentarios XML: los `///` son notas internas. Indicación del usuario (2026-10-14): las descripciones deben usar el vocabulario fiscal con que busca un integrador, para que las consultas que fallaron en la prueba de búsqueda (`requisitos.md` → Contexto medido: "complemento de pago", "crear cliente receptor", "estatus ante el SAT", "descarga masiva") encuentren su operación. **Alcance:** - Prueba trinquete: lista las operaciones públicas sin `EndpointSummary` y la compara con una lista permitida en el código que solo puede bajar. - Resúmenes de Invoices, People y TaxFiles. @@ -87,9 +87,9 @@ El código de estas tareas va en fapi-back, en la rama `feat/ai-integracion` des ### BE-05 — Completar `EndpointSummary` en el resto de la superficie **Objetivo:** que la lista permitida del trinquete quede vacía. **Depende de:** BE-04. -**Skills:** `dotnet-aspnetcore:dotnet-webapi`, `dotnet-test:run-tests`. +**Skills:** `dotnet-aspnetcore:dotnet-webapi`, `dotnet-test:run-tests`, `postman:api-documentation` y `postman:ai-readiness`. **Modelo sugerido (opcional):** haiku. -**Contexto:** igual que BE-04. +**Contexto:** igual que BE-04, incluida la indicación del usuario sobre el vocabulario fiscal de las descripciones. **Alcance:** resúmenes y descripciones con enlace de Catalogs, Products, Stamps, ApiKeys, DownloadCatalogs, DownloadRequests, DownloadRules, Manifests, SatValidations, Tickets, Portal, AutoInvoice, Webhooks y Events (los que falten); la lista permitida se borra. **Textos visibles (propuesta al usuario):** los resúmenes de esta tarea, revisados en el diff. **Criterios de aceptación (todos con `→ verificar`):** From a5f95866d797dddd6835ba5162c41023305ce14e Mon Sep 17 00:00:00 2001 From: mendozagit Date: Wed, 14 Oct 2026 23:28:11 -0600 Subject: [PATCH 04/44] =?UTF-8?q?MED-02:=20embudo=20de=20Test=20y=20l?= =?UTF-8?q?=C3=ADnea=20base=20de=20la=20m=C3=A9trica=20norte?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Restore aparte WebApi_TestSnap (READ_ONLY, borrado al terminar), backupset.server_name 83f15e2487c3, corte 2026-10-15 01:00 UTC. 26 cohortes semanales desde 2026-04-13, 397 owners tras excluir internos: 95.2 % confirma, 41.3 % compra la prueba, 30.5 % timbra, 15.4 % en < 24 h. Mediana al primer timbre: 1,424 min. 215 de 378 confirmados (56.9 %) no compran; nadie timbra sin compra (dato para D-09). Tablero: MED-01 con su hash. --- docs/ai-integracion/README.md | 4 +-- docs/ai-integracion/medicion.md | 2 ++ docs/ai-integracion/requisitos.md | 50 ++++++++++++++++++++++++++++++- 3 files changed, 53 insertions(+), 3 deletions(-) diff --git a/docs/ai-integracion/README.md b/docs/ai-integracion/README.md index d738e3f..fa66910 100644 --- a/docs/ai-integracion/README.md +++ b/docs/ai-integracion/README.md @@ -31,8 +31,8 @@ La siguiente tarea (global o de un área) es la primera fila `pendiente` (de esa | ID | Tarea | Área | Depende de | Estado | Commit | |---|---|---|---|---|---| -| MED-01 | Medir mezcla de tipos y complementos en el restore de prod | medicion | — | hecho | fiscalapi-ai (este commit) | -| MED-02 | Medir embudo y tiempo al primer timbre en el restore de Test | medicion | — | pendiente | | +| MED-01 | Medir mezcla de tipos y complementos en el restore de prod | medicion | — | hecho | fiscalapi-ai@4f6664c | +| MED-02 | Medir embudo y tiempo al primer timbre en el restore de Test | medicion | — | hecho | fiscalapi-ai (commit «MED-02») | | U-01 | Abrir el túnel a Seq de producción | usuario | — | pendiente | | | MED-03 | Medir el top de rechazos del PAC en Seq | medicion | U-01 | pendiente | | | BE-01 | Publicar la superficie de 100 acciones con `[PublicApi]` | backend | — | pendiente | | diff --git a/docs/ai-integracion/medicion.md b/docs/ai-integracion/medicion.md index 69944a2..b6d9a11 100644 --- a/docs/ai-integracion/medicion.md +++ b/docs/ai-integracion/medicion.md @@ -45,6 +45,8 @@ Los resultados de estas tareas van a `requisitos.md` → **Contexto medido**, co **Pruebas focalizadas:** ninguna (no aplica). **Fuera de alcance:** cambiar el onboarding (D-09). **Notas:** +- Dato para D-09: 215 de 378 owners confirmados (56.9 %) nunca hacen la compra de prueba, y nadie timbra sin ella. Línea base: mediana de 1,424 min al primer timbre, 15.4 % en menos de 24 h. +- "Suscripción" se midió como cualquier fila en `Features` (desde 2026-10-05 casi no hay `Type = 'Base'`). Restore aparte `WebApi_TestSnap` (READ_ONLY), borrado al terminar. ### MED-03 — Medir el top de rechazos del PAC en Seq **Objetivo:** saber qué códigos de rechazo del PAC son los más frecuentes, para dar prioridad a `fiscalapi-diagnostico` y a los casos de las evals. diff --git a/docs/ai-integracion/requisitos.md b/docs/ai-integracion/requisitos.md index b4118d8..109ecaf 100644 --- a/docs/ai-integracion/requisitos.md +++ b/docs/ai-integracion/requisitos.md @@ -73,7 +73,55 @@ Fecha de las mediciones: 2026-10-14. - Información global es una columna nueva (primer registro 2026-10-10), así que 46 subestima su uso. No hay tabla de Impuestos Locales. - **Orden resultante:** ingreso-egreso, pagos, nómina, carta porte y comercio exterior. Pagos va antes que nómina porque la usan 3 veces más tenants (27 contra 9) y el 82 % de la nómina es de un solo tenant. Coincide con el orden de SK-04 a SK-09 y con EV-01 (ingreso y pagos) y EV-02 (nómina, carta porte y comercio exterior), así que el tablero no cambia. -**Sin medir todavía:** el embudo de Test (MED-02) y el top de rechazos del PAC (MED-03). +**Embudo de Test** (MED-02) +- Fuente: restore local de Test (`backupset.server_name` = `83f15e2487c3`, corte 2026-10-15 01:00 UTC). Cohortes semanales (lunes) del 2026-04-13 al corte; la última semana está incompleta. +- Owner = persona con `Id = CreatedBy`. Confirmado = `EmailConfirmed`. Compra de prueba = cualquier fila en `Features` del tenant. Desde 2026-10-05 casi no hay filas `Type = 'Base'` aunque sí compras, así que se cuenta cualquier tipo. Primer timbre = primer `Invoices.CreatedAt` del tenant. +- **Criterio de exclusión:** personas de sistema (`-1`, `0`, `1`) y correos `@fiscalapi.com`, `@mailinator.com`, `rl-test-*`, los que contienen `test`, `prueba` o `e2e` y el correo del equipo. Quedan 397 de 404 owners. +- Embudo de las 26 semanas: + + | Paso | Owners | De los owners | Del paso anterior | + |---|---|---|---| + | Creados | 397 | 100 % | — | + | Con correo confirmado | 378 | 95.2 % | 95.2 % | + | Con compra de prueba | 164 | 41.3 % | 43.4 % | + | Con primer timbre | 121 | 30.5 % | 73.8 % | + | Timbran en menos de 24 h | 61 | 15.4 % | 50.4 % | + +- Ningún owner timbró sin compra de prueba ni antes de ella: la compra es la puerta del primer timbre. **215 de 378 confirmados (56.9 %) nunca compran.** La mediana a la compra es de 333 min. +- **Línea base de la métrica norte:** mediana de 1,424 min (23.7 h) de la creación al primer timbre, y 15.4 % de las cuentas nuevas timbra en menos de 24 h. +- Por cohorte, la mediana va de 87 a 28,730 min. Con 5 a 23 owners por semana, la cifra semanal es ruidosa; se sigue la de 4 semanas móviles. + + | Semana | Owners | Confirmados | Compra | Timbran | < 24 h | Mediana (min) | + |---|---|---|---|---|---|---| + | 2026-04-13 | 12 | 11 | 4 | 4 | 3 | 618 | + | 2026-04-20 | 5 | 5 | 1 | 1 | 0 | 5,958 | + | 2026-04-27 | 18 | 18 | 11 | 9 | 2 | 8,706 | + | 2026-05-04 | 5 | 5 | 4 | 4 | 3 | 314 | + | 2026-05-11 | 10 | 10 | 1 | 0 | 0 | — | + | 2026-05-18 | 20 | 19 | 9 | 6 | 4 | 715 | + | 2026-05-25 | 14 | 13 | 6 | 3 | 2 | 135 | + | 2026-06-01 | 12 | 11 | 3 | 2 | 1 | 3,431 | + | 2026-06-08 | 18 | 17 | 9 | 6 | 2 | 9,147 | + | 2026-06-15 | 15 | 15 | 4 | 4 | 1 | 28,731 | + | 2026-06-22 | 11 | 11 | 8 | 8 | 1 | 15,954 | + | 2026-06-29 | 11 | 10 | 4 | 4 | 2 | 3,078 | + | 2026-07-06 | 11 | 10 | 3 | 2 | 0 | 15,139 | + | 2026-07-13 | 17 | 17 | 7 | 5 | 3 | 798 | + | 2026-07-20 | 14 | 14 | 4 | 4 | 2 | 1,281 | + | 2026-07-27 | 14 | 14 | 6 | 6 | 4 | 87 | + | 2026-08-03 | 23 | 22 | 8 | 7 | 2 | 3,853 | + | 2026-08-10 | 15 | 15 | 8 | 6 | 4 | 888 | + | 2026-08-17 | 15 | 14 | 8 | 5 | 2 | 2,794 | + | 2026-08-24 | 15 | 14 | 8 | 4 | 2 | 727 | + | 2026-08-31 | 20 | 16 | 6 | 5 | 3 | 703 | + | 2026-09-07 | 18 | 17 | 8 | 4 | 3 | 207 | + | 2026-09-14 | 18 | 17 | 9 | 5 | 3 | 1,119 | + | 2026-09-21 | 18 | 17 | 7 | 4 | 2 | 3,728 | + | 2026-09-28 | 17 | 17 | 7 | 4 | 2 | 1,623 | + | 2026-10-05 | 17 | 15 | 6 | 5 | 4 | 249 | + | 2026-10-12 (incompleta) | 14 | 14 | 5 | 4 | 4 | 128 | + +**Sin medir todavía:** el top de rechazos del PAC (MED-03). ## Requisitos funcionales - **RF-01 — OpenAPI público.** `GET https://{test,live}.fiscalapi.com/api/openapi/v4.json` responde sin autenticación con exactamente las 100 acciones de D-05. Cada operación tiene `operationId` estable y servidores reales. From 5c1855f19c5454cb3d6fccb43020d85fb58310b7 Mon Sep 17 00:00:00 2001 From: mendozagit Date: Thu, 15 Oct 2026 00:01:34 -0600 Subject: [PATCH 05/44] =?UTF-8?q?BE-01=20hecho=20(fapi-back@8d31a9aa);=20D?= =?UTF-8?q?-17:=20un=20solo=20documento=20OpenAPI,=20el=20p=C3=BAblico;=20?= =?UTF-8?q?MED-02=20con=20su=20hash?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/ai-integracion/README.md | 4 ++-- docs/ai-integracion/backend.md | 3 +++ docs/ai-integracion/decisiones.md | 6 ++++++ 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/docs/ai-integracion/README.md b/docs/ai-integracion/README.md index fa66910..04159d3 100644 --- a/docs/ai-integracion/README.md +++ b/docs/ai-integracion/README.md @@ -32,10 +32,10 @@ La siguiente tarea (global o de un área) es la primera fila `pendiente` (de esa | ID | Tarea | Área | Depende de | Estado | Commit | |---|---|---|---|---|---| | MED-01 | Medir mezcla de tipos y complementos en el restore de prod | medicion | — | hecho | fiscalapi-ai@4f6664c | -| MED-02 | Medir embudo y tiempo al primer timbre en el restore de Test | medicion | — | hecho | fiscalapi-ai (commit «MED-02») | +| MED-02 | Medir embudo y tiempo al primer timbre en el restore de Test | medicion | — | hecho | fiscalapi-ai@a5f9586 | | U-01 | Abrir el túnel a Seq de producción | usuario | — | pendiente | | | MED-03 | Medir el top de rechazos del PAC en Seq | medicion | U-01 | pendiente | | -| BE-01 | Publicar la superficie de 100 acciones con `[PublicApi]` | backend | — | pendiente | | +| BE-01 | Publicar la superficie de 100 acciones con `[PublicApi]` | backend | — | hecho | fapi-back@8d31a9aa | | BE-02 | Agregar `operationId` estable y metadatos del documento | backend | BE-01 | pendiente | | | BE-03 | Servir `/api/openapi/v4.json` anónimo en test y live | backend | BE-02 | pendiente | | | BE-04 | Agregar el trinquete de `EndpointSummary` y cubrir facturas | backend | BE-01 | pendiente | | diff --git a/docs/ai-integracion/backend.md b/docs/ai-integracion/backend.md index e325b2f..34b13c5 100644 --- a/docs/ai-integracion/backend.md +++ b/docs/ai-integracion/backend.md @@ -26,6 +26,9 @@ El código de estas tareas va en fapi-back, en la rama `feat/ai-integracion` des **Pruebas focalizadas:** sí, por contrato y exposición. `dotnet test WebApi.Tests/WebApi.Tests.csproj --filter "FullyQualifiedName~OpenApi"`. **Fuera de alcance:** `operationId` y metadatos (BE-02); servir la ruta (BE-03); resúmenes (BE-04 y BE-05). **Notas:** +- fapi-back está en el worktree `fapi-back-ai` (rama `feat/ai-integracion`): el working tree `fapi-back` lo ocupa `feat/localization`. Un solo documento `v4`, también para Scalar (D-17). +- `PublicApiSurfaceTests` resuelve cada request contra todas las acciones v4 (la más literal gana): `tickets/{id}` no tiene `:guid`, así que comparar solo contra el documento no distinguía `tickets/load`. +- Pendiente de la revisión, fuera de alcance: RNF-03 depende de la colección (no hay prueba propia de esquemas de auth), y las anotaciones OpenAPI de acciones internas ya no llegan a ningún documento. ### BE-02 — Agregar `operationId` estable y metadatos del documento **Objetivo:** que cada operación tenga un `operationId` estable y que el documento declare servidores reales, autenticación y descripción. diff --git a/docs/ai-integracion/decisiones.md b/docs/ai-integracion/decisiones.md index 7f9cb8f..a0c52b8 100644 --- a/docs/ai-integracion/decisiones.md +++ b/docs/ai-integracion/decisiones.md @@ -114,3 +114,9 @@ Reglas de este archivo: - **Por qué:** el nginx de test y el de live ya mandan `location ^~ /api` al back. El CD no toca nginx, que se cambia a mano (`infra/nginx/live/README.md`). Con otra ruta habría un paso manual. - **Consecuencias:** cero cambios en nginx. La ruta hereda el rate limiting de `/api`. - **Estado:** confirmada. + +### D-17 — Un solo documento OpenAPI: el público +- **Decisión:** fapi-back registra un solo documento, `v4`, con las acciones `[PublicApi]`. Scalar en Development muestra ese mismo documento; no se conserva un documento completo con lo interno, y el grupo v5 (`Controllers/V5/ValuesController`) se queda sin documento. +- **Por qué:** lo interno (dashboard, Host, DevExtreme) no es contrato, y un segundo documento sería configuración que nadie consume. Así Scalar muestra lo mismo que reciben los integradores. +- **Consecuencias:** las pruebas de contrato OpenAPI solo cubren operaciones públicas. Las rutas internas se siguen probando por HTTP. +- **Estado:** confirmada (técnica, la fijó BE-01). From 0f94e8df4a06152de0b69f483bfeea0e8ab6a618 Mon Sep 17 00:00:00 2001 From: mendozagit Date: Thu, 15 Oct 2026 00:05:22 -0600 Subject: [PATCH 06/44] =?UTF-8?q?BE-02=20bloqueada:=20falta=20aprobar=20el?= =?UTF-8?q?=20info.description=20del=20OpenAPI=20p=C3=BAblico?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/ai-integracion/README.md | 2 +- docs/ai-integracion/backend.md | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/ai-integracion/README.md b/docs/ai-integracion/README.md index 04159d3..7ebfe79 100644 --- a/docs/ai-integracion/README.md +++ b/docs/ai-integracion/README.md @@ -36,7 +36,7 @@ La siguiente tarea (global o de un área) es la primera fila `pendiente` (de esa | U-01 | Abrir el túnel a Seq de producción | usuario | — | pendiente | | | MED-03 | Medir el top de rechazos del PAC en Seq | medicion | U-01 | pendiente | | | BE-01 | Publicar la superficie de 100 acciones con `[PublicApi]` | backend | — | hecho | fapi-back@8d31a9aa | -| BE-02 | Agregar `operationId` estable y metadatos del documento | backend | BE-01 | pendiente | | +| BE-02 | Agregar `operationId` estable y metadatos del documento | backend | BE-01 | bloqueado | | | BE-03 | Servir `/api/openapi/v4.json` anónimo en test y live | backend | BE-02 | pendiente | | | BE-04 | Agregar el trinquete de `EndpointSummary` y cubrir facturas | backend | BE-01 | pendiente | | | BE-05 | Completar `EndpointSummary` en el resto de la superficie | backend | BE-04 | pendiente | | diff --git a/docs/ai-integracion/backend.md b/docs/ai-integracion/backend.md index 34b13c5..dc7e7c0 100644 --- a/docs/ai-integracion/backend.md +++ b/docs/ai-integracion/backend.md @@ -52,6 +52,8 @@ El código de estas tareas va en fapi-back, en la rama `feat/ai-integracion` des **Pruebas focalizadas:** sí (contrato). `--filter "FullyQualifiedName~OpenApi"`. **Fuera de alcance:** resúmenes por operación (BE-04 y BE-05). **Notas:** +- Bloqueada por **Textos visibles**: ¿apruebas este `info` del documento público? Propuesta: `title` "Fiscalapi API", `version` "4.0" y como `description` el texto del spec de Postman (párrafo de la API, "Modos de operación" y los enlaces a credenciales y datos de prueba) sin la sección "Configura un environment", que es solo de la colección, y con una línea nueva: "Ambientes: `https://test.fiscalapi.com` (pruebas) y `https://live.fiscalapi.com` (producción)". +- La fuente (`postman/specs/Fiscalapi Specification`) solo existe sin rastrear en el working tree `fapi-back`, no en `main`: el texto aprobado se copia al transformer. ### BE-03 — Servir `/api/openapi/v4.json` anónimo en test y live **Objetivo:** que cualquier agente o herramienta descargue el contrato sin credenciales y sin tocar nginx. From 86ac1007e6cdd288062112fb9845c694138211f5 Mon Sep 17 00:00:00 2001 From: mendozagit Date: Thu, 15 Oct 2026 00:07:19 -0600 Subject: [PATCH 07/44] =?UTF-8?q?BE-04=20bloqueada:=20los=20res=C3=BAmenes?= =?UTF-8?q?=20visibles=20requieren=20revisi=C3=B3n=20del=20usuario=20en=20?= =?UTF-8?q?el=20diff?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/ai-integracion/README.md | 2 +- docs/ai-integracion/backend.md | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/ai-integracion/README.md b/docs/ai-integracion/README.md index 7ebfe79..9728a97 100644 --- a/docs/ai-integracion/README.md +++ b/docs/ai-integracion/README.md @@ -38,7 +38,7 @@ La siguiente tarea (global o de un área) es la primera fila `pendiente` (de esa | BE-01 | Publicar la superficie de 100 acciones con `[PublicApi]` | backend | — | hecho | fapi-back@8d31a9aa | | BE-02 | Agregar `operationId` estable y metadatos del documento | backend | BE-01 | bloqueado | | | BE-03 | Servir `/api/openapi/v4.json` anónimo en test y live | backend | BE-02 | pendiente | | -| BE-04 | Agregar el trinquete de `EndpointSummary` y cubrir facturas | backend | BE-01 | pendiente | | +| BE-04 | Agregar el trinquete de `EndpointSummary` y cubrir facturas | backend | BE-01 | bloqueado | | | BE-05 | Completar `EndpointSummary` en el resto de la superficie | backend | BE-04 | pendiente | | | DOC-01 | Generar `.md` por sección H2 en el build | docs | — | pendiente | | | DOC-02 | Generar páginas de errores de Pagos y Comercio Exterior | docs | — | pendiente | | diff --git a/docs/ai-integracion/backend.md b/docs/ai-integracion/backend.md index dc7e7c0..657cb83 100644 --- a/docs/ai-integracion/backend.md +++ b/docs/ai-integracion/backend.md @@ -88,6 +88,7 @@ El código de estas tareas va en fapi-back, en la rama `feat/ai-integracion` des **Pruebas focalizadas:** sí (contrato). `--filter "FullyQualifiedName~OpenApi"`. **Fuera de alcance:** el resto de controllers (BE-05). **Notas:** +- Bloqueada por **Textos visibles**: los resúmenes de Invoices, People y TaxFiles se te muestran en el diff antes del commit. ¿Los redacto y te muestro el diff (sin commit) en cuanto me lo indiques? BE-05 espera a esta tarea. ### BE-05 — Completar `EndpointSummary` en el resto de la superficie **Objetivo:** que la lista permitida del trinquete quede vacía. From 5e4085657a0dc64e3a4c4c82b9c5085f65153998 Mon Sep 17 00:00:00 2001 From: mendozagit Date: Thu, 15 Oct 2026 00:08:30 -0600 Subject: [PATCH 08/44] =?UTF-8?q?DOC-01=20bloqueada:=20=C2=BFlas=20seccion?= =?UTF-8?q?es=20.md=20incluyen=20las=20p=C3=A1ginas=20de=20facturas=20excl?= =?UTF-8?q?uidas=3F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/ai-integracion/README.md | 2 +- docs/ai-integracion/docs.md | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/ai-integracion/README.md b/docs/ai-integracion/README.md index 9728a97..7a01595 100644 --- a/docs/ai-integracion/README.md +++ b/docs/ai-integracion/README.md @@ -40,7 +40,7 @@ La siguiente tarea (global o de un área) es la primera fila `pendiente` (de esa | BE-03 | Servir `/api/openapi/v4.json` anónimo en test y live | backend | BE-02 | pendiente | | | BE-04 | Agregar el trinquete de `EndpointSummary` y cubrir facturas | backend | BE-01 | bloqueado | | | BE-05 | Completar `EndpointSummary` en el resto de la superficie | backend | BE-04 | pendiente | | -| DOC-01 | Generar `.md` por sección H2 en el build | docs | — | pendiente | | +| DOC-01 | Generar `.md` por sección H2 en el build | docs | — | bloqueado | | | DOC-02 | Generar páginas de errores de Pagos y Comercio Exterior | docs | — | pendiente | | | DOC-03 | Corregir el enlace del SDK de Node en `llms.txt` | docs | — | pendiente | | | SK-01 | Armar el esqueleto del repo, los manifiestos y el CI de enlaces | skills | — | pendiente | | diff --git a/docs/ai-integracion/docs.md b/docs/ai-integracion/docs.md index a8a0f4f..a5c762f 100644 --- a/docs/ai-integracion/docs.md +++ b/docs/ai-integracion/docs.md @@ -21,6 +21,8 @@ El código va en fapi-docs (y en fapi-reusables solo si cambia el generador), en **Pruebas focalizadas:** ninguna (no aplica). La verificación es el conteo del build. **Fuera de alcance:** `llms-full.txt` (D-15). **Notas:** +- Bloqueada: `invoices-by-value` e `invoices-by-reference` (23 H2 cada una, las secciones de facturas) están en `EXCLUDED_ROUTES` de `src/lib/docs.js`: noindex, `Disallow` en robots y fuera del sitemap, de `llms.txt` y de las copias `.md` (94a60f2). ¿Las secciones `.md` y su lista en `llms.txt` las incluyen? Propuesta: sí, sin tocar noindex, robots ni el sitemap. +- Localización (`feat/localization`) agrega `npm run es:diff`, que compara byte a byte `/llms.txt` y las `.md` contra `main`; si se fusiona antes, esta tarea declara su diferencia en `scripts/i18n/es-allowed.json`. Worktree listo: `fapi-docs-ai`. ### DOC-02 — Generar páginas de errores de Pagos y Comercio Exterior **Objetivo:** que `fiscalapi-diagnostico` y `explicar-error` puedan enlazar los errores `CRP*` y `CCE*`, igual que hoy enlazan los de CFDI 4.0, Nómina y Carta Porte. From 4a4de5a6a97200bfff81cc76a01ff6313d332cb9 Mon Sep 17 00:00:00 2001 From: mendozagit Date: Thu, 15 Oct 2026 00:23:27 -0600 Subject: [PATCH 09/44] DOC-02 hecho (fapi-docs@816f3a4): errores de Pagos 2.0; Comercio Exterior pasa a DOC-04, bloqueada por la matriz del SAT --- docs/ai-integracion/README.md | 3 ++- docs/ai-integracion/docs.md | 25 +++++++++++++++++++++---- 2 files changed, 23 insertions(+), 5 deletions(-) diff --git a/docs/ai-integracion/README.md b/docs/ai-integracion/README.md index 7a01595..3c010f3 100644 --- a/docs/ai-integracion/README.md +++ b/docs/ai-integracion/README.md @@ -41,8 +41,9 @@ La siguiente tarea (global o de un área) es la primera fila `pendiente` (de esa | BE-04 | Agregar el trinquete de `EndpointSummary` y cubrir facturas | backend | BE-01 | bloqueado | | | BE-05 | Completar `EndpointSummary` en el resto de la superficie | backend | BE-04 | pendiente | | | DOC-01 | Generar `.md` por sección H2 en el build | docs | — | bloqueado | | -| DOC-02 | Generar páginas de errores de Pagos y Comercio Exterior | docs | — | pendiente | | +| DOC-02 | Generar la página de errores de Pagos | docs | — | hecho | fapi-docs@816f3a4 | | DOC-03 | Corregir el enlace del SDK de Node en `llms.txt` | docs | — | pendiente | | +| DOC-04 | Generar la página de errores de Comercio Exterior | docs | — | bloqueado | | | SK-01 | Armar el esqueleto del repo, los manifiestos y el CI de enlaces | skills | — | pendiente | | | SK-02 | Escribir `fiscalapi-integracion` y `primer-timbre` | skills | SK-01, DOC-01 | pendiente | | | SK-03 | Escribir `fiscalapi-diagnostico` y `explicar-error` | skills | SK-01, DOC-02 | pendiente | | diff --git a/docs/ai-integracion/docs.md b/docs/ai-integracion/docs.md index a5c762f..63ad8fd 100644 --- a/docs/ai-integracion/docs.md +++ b/docs/ai-integracion/docs.md @@ -24,22 +24,24 @@ El código va en fapi-docs (y en fapi-reusables solo si cambia el generador), en - Bloqueada: `invoices-by-value` e `invoices-by-reference` (23 H2 cada una, las secciones de facturas) están en `EXCLUDED_ROUTES` de `src/lib/docs.js`: noindex, `Disallow` en robots y fuera del sitemap, de `llms.txt` y de las copias `.md` (94a60f2). ¿Las secciones `.md` y su lista en `llms.txt` las incluyen? Propuesta: sí, sin tocar noindex, robots ni el sitemap. - Localización (`feat/localization`) agrega `npm run es:diff`, que compara byte a byte `/llms.txt` y las `.md` contra `main`; si se fusiona antes, esta tarea declara su diferencia en `scripts/i18n/es-allowed.json`. Worktree listo: `fapi-docs-ai`. -### DOC-02 — Generar páginas de errores de Pagos y Comercio Exterior -**Objetivo:** que `fiscalapi-diagnostico` y `explicar-error` puedan enlazar los errores `CRP*` y `CCE*`, igual que hoy enlazan los de CFDI 4.0, Nómina y Carta Porte. +### DOC-02 — Generar la página de errores de Pagos +**Objetivo:** que `fiscalapi-diagnostico` y `explicar-error` puedan enlazar los errores `CRP*`, igual que hoy enlazan los de CFDI 4.0, Nómina y Carta Porte. Comercio Exterior pasó a DOC-04. **Depende de:** — **Skills:** `fapi-cfdi-rules`, `fapi-sdk-docs-sync`. **Modelo sugerido (opcional):** haiku. Corre el generador existente. **Contexto:** RF-03. Las páginas actuales salen de `fapi-reusables/scripts/sat-docs-md-errors-generator/app.py` sobre las matrices de `fapi-reusables/sat-docs`. Ya existen `sat-docs/pagos` y `sat-docs/comercio-exterior`. **Alcance:** -- Correr el generador con las matrices de Pagos 2.0 y Comercio Exterior 2.0 y crear las páginas `errors/pagos-20` y `errors/comercio-exterior-20` con el mismo formato que `errors/cfdi-40`. +- Correr el generador con la matriz de Pagos 2.0 y crear la página `errors/pagos-20` con el mismo formato que `errors/nomina-12`. - Agregarlas a la navegación y a `llms.txt`. - Si el generador necesita un cambio, va en fapi-reusables en su propio commit. **Criterios de aceptación (todos con `→ verificar`):** -- Las dos páginas responden en `.md` → verificar: `curl -s localhost:3000/errors/pagos-20.md | grep -c "CRP"` y `curl -s localhost:3000/errors/comercio-exterior-20.md | grep -c "CCE"` mayores que 0. +- La página responde en `.md` → verificar: `curl -s localhost:3100/errors/pagos-20.md | grep -c "CRP"` mayor que 0. - El número de códigos coincide con la matriz del SAT → verificar: conteo de filas de la matriz contra el conteo en la página. **Pruebas focalizadas:** ninguna (no aplica). **Fuera de alcance:** traducir los mensajes del SAT. **Notas:** +- Comercio Exterior pasó a DOC-04: `sat-docs/comercio-exterior` no trae su matriz de errores. Las páginas de errores actuales se editaron a mano después de generarse, así que `pagos-20` toma la estructura de `nomina-12` y solo la tabla sale del generador. +- El generador se corrió con la salida en el scratchpad: fapi-reusables está en `feat/auto-invoice` con cambios propios. 103 códigos CRP en la matriz y en la página. El repo fapi-docs tiene `user.name` local "Fiscalapi": se fija el autor mendozagit al hacer commit. ### DOC-03 — Corregir el enlace del SDK de Node en `llms.txt` **Objetivo:** que los agentes lleguen al repo real del SDK de Node. @@ -54,3 +56,18 @@ El código va en fapi-docs (y en fapi-reusables solo si cambia el generador), en **Pruebas focalizadas:** ninguna (no aplica). **Fuera de alcance:** — **Notas:** + +### DOC-04 — Generar la página de errores de Comercio Exterior +**Objetivo:** que `fiscalapi-diagnostico` y `explicar-error` puedan enlazar los errores `CCE*`, como DOC-02 hizo con los `CRP*`. +**Depende de:** — +**Skills:** `fapi-cfdi-rules`, `fapi-sdk-docs-sync`. +**Modelo sugerido (opcional):** haiku. Corre el generador existente. +**Contexto:** RF-03. Se separó de DOC-02: `fapi-reusables/sat-docs/comercio-exterior` no trae la matriz de errores (solo `Catalogos_Comercio_Exterior.xlsx` y las guías, sin ningún código `CCE###`). MED-01 midió 0 facturas de Comercio Exterior en producción. +**Alcance:** agregar la matriz oficial del SAT a `sat-docs/comercio-exterior/catalogs` (commit propio en fapi-reusables) y crear `errors/comercio-exterior-20` como DOC-02 creó `errors/pagos-20`, con su entrada en la navegación y en la tabla de `errors`. +**Criterios de aceptación (todos con `→ verificar`):** +- La página responde en `.md` → verificar: `curl -s localhost:3100/errors/comercio-exterior-20.md | grep -c "CCE"` mayor que 0. +- El número de códigos coincide con la matriz → verificar: conteo de códigos `CCE` únicos de la matriz contra los de la página. +**Pruebas focalizadas:** ninguna (no aplica). +**Fuera de alcance:** traducir los mensajes del SAT. +**Notas:** +- Bloqueada: no encontré la matriz de errores de Comercio Exterior 2.0 del SAT; solo la v1.1 Rev E (`Matriz_de_Errores_CCE_v11_E_.xls`). ¿Me pasas la URL oficial de la 2.0, o la página usa la v1.1 Rev E? From 07ac1598ac307c26e0b144b7140e9b71485bd989 Mon Sep 17 00:00:00 2001 From: mendozagit Date: Thu, 15 Oct 2026 00:27:41 -0600 Subject: [PATCH 10/44] DOC-03 hecho (fapi-docs@b5eec29): enlace del SDK de Node --- docs/ai-integracion/README.md | 2 +- docs/ai-integracion/docs.md | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/ai-integracion/README.md b/docs/ai-integracion/README.md index 3c010f3..d7384f0 100644 --- a/docs/ai-integracion/README.md +++ b/docs/ai-integracion/README.md @@ -42,7 +42,7 @@ La siguiente tarea (global o de un área) es la primera fila `pendiente` (de esa | BE-05 | Completar `EndpointSummary` en el resto de la superficie | backend | BE-04 | pendiente | | | DOC-01 | Generar `.md` por sección H2 en el build | docs | — | bloqueado | | | DOC-02 | Generar la página de errores de Pagos | docs | — | hecho | fapi-docs@816f3a4 | -| DOC-03 | Corregir el enlace del SDK de Node en `llms.txt` | docs | — | pendiente | | +| DOC-03 | Corregir el enlace del SDK de Node en `llms.txt` | docs | — | hecho | fapi-docs@b5eec29 | | DOC-04 | Generar la página de errores de Comercio Exterior | docs | — | bloqueado | | | SK-01 | Armar el esqueleto del repo, los manifiestos y el CI de enlaces | skills | — | pendiente | | | SK-02 | Escribir `fiscalapi-integracion` y `primer-timbre` | skills | SK-01, DOC-01 | pendiente | | diff --git a/docs/ai-integracion/docs.md b/docs/ai-integracion/docs.md index 63ad8fd..7679fa6 100644 --- a/docs/ai-integracion/docs.md +++ b/docs/ai-integracion/docs.md @@ -56,6 +56,7 @@ El código va en fapi-docs (y en fapi-reusables solo si cambia el generador), en **Pruebas focalizadas:** ninguna (no aplica). **Fuera de alcance:** — **Notas:** +- También estaba en `src/components/Libraries.jsx` (la tarjeta del SDK en el sitio). `fiscalapi-js` es el nombre anterior del repo y GitHub redirige; los otros 4 SDK resuelven con su nombre. ### DOC-04 — Generar la página de errores de Comercio Exterior **Objetivo:** que `fiscalapi-diagnostico` y `explicar-error` puedan enlazar los errores `CCE*`, como DOC-02 hizo con los `CRP*`. From 6caa2c4cb695be25b32eb42b63e4aa870deab914 Mon Sep 17 00:00:00 2001 From: mendozagit Date: Thu, 15 Oct 2026 00:42:38 -0600 Subject: [PATCH 11/44] SK-01: esqueleto del plugin, manifiestos de los 4 hosts y CI de enlaces MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - .claude-plugin/{plugin,marketplace}.json (Claude Code, Codex y Copilot) y .cursor-plugin/plugin.json (copia exacta), sin campo skills ni MCP (D-18). - scripts/check-skills.mjs: frontmatter de agentskills.io y cada enlace a docs.fiscalapi.com con 200 y su ancla; .github/workflows/check.yml en cada PR y cada lunes, con claude plugin validate --strict y cmp de la copia de Cursor. - README con la instalación por host y LICENSE MPL-2.0 (como los SDK). - Verificado: validate --strict en verde; instalación local en Claude Code con una skill temporal (descubierta, 0 MCP) y desinstalación; el script sale en 1 con un ancla inventada, un 404, un name inválido o una description en bloque YAML. --- .claude-plugin/marketplace.json | 12 + .claude-plugin/plugin.json | 10 + .cursor-plugin/plugin.json | 10 + .github/workflows/check.yml | 33 +++ LICENSE | 373 ++++++++++++++++++++++++++++++ README.md | 56 +++++ docs/ai-integracion/README.md | 2 +- docs/ai-integracion/decisiones.md | 6 + docs/ai-integracion/skills.md | 2 + scripts/check-skills.mjs | 123 ++++++++++ 10 files changed, 626 insertions(+), 1 deletion(-) create mode 100644 .claude-plugin/marketplace.json create mode 100644 .claude-plugin/plugin.json create mode 100644 .cursor-plugin/plugin.json create mode 100644 .github/workflows/check.yml create mode 100644 LICENSE create mode 100644 scripts/check-skills.mjs diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json new file mode 100644 index 0000000..7799283 --- /dev/null +++ b/.claude-plugin/marketplace.json @@ -0,0 +1,12 @@ +{ + "name": "fiscalapi", + "description": "Plugins de FiscalAPI para agentes de código.", + "owner": { "name": "FiscalAPI" }, + "plugins": [ + { + "name": "fiscalapi", + "source": "./", + "description": "Skills en español para integrar FiscalAPI (CFDI 4.0) en tu ERP, POS o SaaS." + } + ] +} diff --git a/.claude-plugin/plugin.json b/.claude-plugin/plugin.json new file mode 100644 index 0000000..f5f1739 --- /dev/null +++ b/.claude-plugin/plugin.json @@ -0,0 +1,10 @@ +{ + "name": "fiscalapi", + "version": "0.1.0", + "description": "Skills en español para integrar FiscalAPI (CFDI 4.0) en tu ERP, POS o SaaS: primer timbre en pruebas, complementos, ciclo de vida de la factura y diagnóstico de errores del SAT.", + "author": { "name": "FiscalAPI" }, + "homepage": "https://docs.fiscalapi.com", + "repository": "https://github.com/FiscalAPI/fiscalapi-ai", + "license": "MPL-2.0", + "keywords": ["fiscalapi", "cfdi", "sat", "facturacion-electronica", "mexico"] +} diff --git a/.cursor-plugin/plugin.json b/.cursor-plugin/plugin.json new file mode 100644 index 0000000..f5f1739 --- /dev/null +++ b/.cursor-plugin/plugin.json @@ -0,0 +1,10 @@ +{ + "name": "fiscalapi", + "version": "0.1.0", + "description": "Skills en español para integrar FiscalAPI (CFDI 4.0) en tu ERP, POS o SaaS: primer timbre en pruebas, complementos, ciclo de vida de la factura y diagnóstico de errores del SAT.", + "author": { "name": "FiscalAPI" }, + "homepage": "https://docs.fiscalapi.com", + "repository": "https://github.com/FiscalAPI/fiscalapi-ai", + "license": "MPL-2.0", + "keywords": ["fiscalapi", "cfdi", "sat", "facturacion-electronica", "mexico"] +} diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml new file mode 100644 index 0000000..c8499ae --- /dev/null +++ b/.github/workflows/check.yml @@ -0,0 +1,33 @@ +name: Check + +# En cada PR y cada lunes: las docs cambian aunque este repo no, y un enlace roto deja a una skill sin su fuente. +on: + pull_request: + schedule: + - cron: '0 14 * * 1' + workflow_dispatch: + +permissions: + contents: read + +jobs: + check: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5.1.0 + + - name: Setup Node + uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7.1.0 + with: + node-version: 24 + + - name: Frontmatter y enlaces de las skills + run: node scripts/check-skills.mjs + + - name: Manifiestos de Claude Code, Codex y Copilot + run: npx -y @anthropic-ai/claude-code@2.1.293 plugin validate . --strict + + # Cursor no lee .claude-plugin: su manifiesto es una copia exacta del de Claude Code. + - name: Manifiesto de Cursor + run: cmp .claude-plugin/plugin.json .cursor-plugin/plugin.json diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..a612ad9 --- /dev/null +++ b/LICENSE @@ -0,0 +1,373 @@ +Mozilla Public License Version 2.0 +================================== + +1. Definitions +-------------- + +1.1. "Contributor" + means each individual or legal entity that creates, contributes to + the creation of, or owns Covered Software. + +1.2. "Contributor Version" + means the combination of the Contributions of others (if any) used + by a Contributor and that particular Contributor's Contribution. + +1.3. "Contribution" + means Covered Software of a particular Contributor. + +1.4. "Covered Software" + means Source Code Form to which the initial Contributor has attached + the notice in Exhibit A, the Executable Form of such Source Code + Form, and Modifications of such Source Code Form, in each case + including portions thereof. + +1.5. "Incompatible With Secondary Licenses" + means + + (a) that the initial Contributor has attached the notice described + in Exhibit B to the Covered Software; or + + (b) that the Covered Software was made available under the terms of + version 1.1 or earlier of the License, but not also under the + terms of a Secondary License. + +1.6. "Executable Form" + means any form of the work other than Source Code Form. + +1.7. "Larger Work" + means a work that combines Covered Software with other material, in + a separate file or files, that is not Covered Software. + +1.8. "License" + means this document. + +1.9. "Licensable" + means having the right to grant, to the maximum extent possible, + whether at the time of the initial grant or subsequently, any and + all of the rights conveyed by this License. + +1.10. "Modifications" + means any of the following: + + (a) any file in Source Code Form that results from an addition to, + deletion from, or modification of the contents of Covered + Software; or + + (b) any new file in Source Code Form that contains any Covered + Software. + +1.11. "Patent Claims" of a Contributor + means any patent claim(s), including without limitation, method, + process, and apparatus claims, in any patent Licensable by such + Contributor that would be infringed, but for the grant of the + License, by the making, using, selling, offering for sale, having + made, import, or transfer of either its Contributions or its + Contributor Version. + +1.12. "Secondary License" + means either the GNU General Public License, Version 2.0, the GNU + Lesser General Public License, Version 2.1, the GNU Affero General + Public License, Version 3.0, or any later versions of those + licenses. + +1.13. "Source Code Form" + means the form of the work preferred for making modifications. + +1.14. "You" (or "Your") + means an individual or a legal entity exercising rights under this + License. For legal entities, "You" includes any entity that + controls, is controlled by, or is under common control with You. For + purposes of this definition, "control" means (a) the power, direct + or indirect, to cause the direction or management of such entity, + whether by contract or otherwise, or (b) ownership of more than + fifty percent (50%) of the outstanding shares or beneficial + ownership of such entity. + +2. License Grants and Conditions +-------------------------------- + +2.1. Grants + +Each Contributor hereby grants You a world-wide, royalty-free, +non-exclusive license: + +(a) under intellectual property rights (other than patent or trademark) + Licensable by such Contributor to use, reproduce, make available, + modify, display, perform, distribute, and otherwise exploit its + Contributions, either on an unmodified basis, with Modifications, or + as part of a Larger Work; and + +(b) under Patent Claims of such Contributor to make, use, sell, offer + for sale, have made, import, and otherwise transfer either its + Contributions or its Contributor Version. + +2.2. Effective Date + +The licenses granted in Section 2.1 with respect to any Contribution +become effective for each Contribution on the date the Contributor first +distributes such Contribution. + +2.3. Limitations on Grant Scope + +The licenses granted in this Section 2 are the only rights granted under +this License. No additional rights or licenses will be implied from the +distribution or licensing of Covered Software under this License. +Notwithstanding Section 2.1(b) above, no patent license is granted by a +Contributor: + +(a) for any code that a Contributor has removed from Covered Software; + or + +(b) for infringements caused by: (i) Your and any other third party's + modifications of Covered Software, or (ii) the combination of its + Contributions with other software (except as part of its Contributor + Version); or + +(c) under Patent Claims infringed by Covered Software in the absence of + its Contributions. + +This License does not grant any rights in the trademarks, service marks, +or logos of any Contributor (except as may be necessary to comply with +the notice requirements in Section 3.4). + +2.4. Subsequent Licenses + +No Contributor makes additional grants as a result of Your choice to +distribute the Covered Software under a subsequent version of this +License (see Section 10.2) or under the terms of a Secondary License (if +permitted under the terms of Section 3.3). + +2.5. Representation + +Each Contributor represents that the Contributor believes its +Contributions are its original creation(s) or it has sufficient rights +to grant the rights to its Contributions conveyed by this License. + +2.6. Fair Use + +This License is not intended to limit any rights You have under +applicable copyright doctrines of fair use, fair dealing, or other +equivalents. + +2.7. Conditions + +Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted +in Section 2.1. + +3. Responsibilities +------------------- + +3.1. Distribution of Source Form + +All distribution of Covered Software in Source Code Form, including any +Modifications that You create or to which You contribute, must be under +the terms of this License. You must inform recipients that the Source +Code Form of the Covered Software is governed by the terms of this +License, and how they can obtain a copy of this License. You may not +attempt to alter or restrict the recipients' rights in the Source Code +Form. + +3.2. Distribution of Executable Form + +If You distribute Covered Software in Executable Form then: + +(a) such Covered Software must also be made available in Source Code + Form, as described in Section 3.1, and You must inform recipients of + the Executable Form how they can obtain a copy of such Source Code + Form by reasonable means in a timely manner, at a charge no more + than the cost of distribution to the recipient; and + +(b) You may distribute such Executable Form under the terms of this + License, or sublicense it under different terms, provided that the + license for the Executable Form does not attempt to limit or alter + the recipients' rights in the Source Code Form under this License. + +3.3. Distribution of a Larger Work + +You may create and distribute a Larger Work under terms of Your choice, +provided that You also comply with the requirements of this License for +the Covered Software. If the Larger Work is a combination of Covered +Software with a work governed by one or more Secondary Licenses, and the +Covered Software is not Incompatible With Secondary Licenses, this +License permits You to additionally distribute such Covered Software +under the terms of such Secondary License(s), so that the recipient of +the Larger Work may, at their option, further distribute the Covered +Software under the terms of either this License or such Secondary +License(s). + +3.4. Notices + +You may not remove or alter the substance of any license notices +(including copyright notices, patent notices, disclaimers of warranty, +or limitations of liability) contained within the Source Code Form of +the Covered Software, except that You may alter any license notices to +the extent required to remedy known factual inaccuracies. + +3.5. Application of Additional Terms + +You may choose to offer, and to charge a fee for, warranty, support, +indemnity or liability obligations to one or more recipients of Covered +Software. However, You may do so only on Your own behalf, and not on +behalf of any Contributor. You must make it absolutely clear that any +such warranty, support, indemnity, or liability obligation is offered by +You alone, and You hereby agree to indemnify every Contributor for any +liability incurred by such Contributor as a result of warranty, support, +indemnity or liability terms You offer. You may include additional +disclaimers of warranty and limitations of liability specific to any +jurisdiction. + +4. Inability to Comply Due to Statute or Regulation +--------------------------------------------------- + +If it is impossible for You to comply with any of the terms of this +License with respect to some or all of the Covered Software due to +statute, judicial order, or regulation then You must: (a) comply with +the terms of this License to the maximum extent possible; and (b) +describe the limitations and the code they affect. Such description must +be placed in a text file included with all distributions of the Covered +Software under this License. Except to the extent prohibited by statute +or regulation, such description must be sufficiently detailed for a +recipient of ordinary skill to be able to understand it. + +5. Termination +-------------- + +5.1. The rights granted under this License will terminate automatically +if You fail to comply with any of its terms. However, if You become +compliant, then the rights granted under this License from a particular +Contributor are reinstated (a) provisionally, unless and until such +Contributor explicitly and finally terminates Your grants, and (b) on an +ongoing basis, if such Contributor fails to notify You of the +non-compliance by some reasonable means prior to 60 days after You have +come back into compliance. Moreover, Your grants from a particular +Contributor are reinstated on an ongoing basis if such Contributor +notifies You of the non-compliance by some reasonable means, this is the +first time You have received notice of non-compliance with this License +from such Contributor, and You become compliant prior to 30 days after +Your receipt of the notice. + +5.2. If You initiate litigation against any entity by asserting a patent +infringement claim (excluding declaratory judgment actions, +counter-claims, and cross-claims) alleging that a Contributor Version +directly or indirectly infringes any patent, then the rights granted to +You by any and all Contributors for the Covered Software under Section +2.1 of this License shall terminate. + +5.3. In the event of termination under Sections 5.1 or 5.2 above, all +end user license agreements (excluding distributors and resellers) which +have been validly granted by You or Your distributors under this License +prior to termination shall survive termination. + +************************************************************************ +* * +* 6. Disclaimer of Warranty * +* ------------------------- * +* * +* Covered Software is provided under this License on an "as is" * +* basis, without warranty of any kind, either expressed, implied, or * +* statutory, including, without limitation, warranties that the * +* Covered Software is free of defects, merchantable, fit for a * +* particular purpose or non-infringing. The entire risk as to the * +* quality and performance of the Covered Software is with You. * +* Should any Covered Software prove defective in any respect, You * +* (not any Contributor) assume the cost of any necessary servicing, * +* repair, or correction. This disclaimer of warranty constitutes an * +* essential part of this License. No use of any Covered Software is * +* authorized under this License except under this disclaimer. * +* * +************************************************************************ + +************************************************************************ +* * +* 7. Limitation of Liability * +* -------------------------- * +* * +* Under no circumstances and under no legal theory, whether tort * +* (including negligence), contract, or otherwise, shall any * +* Contributor, or anyone who distributes Covered Software as * +* permitted above, be liable to You for any direct, indirect, * +* special, incidental, or consequential damages of any character * +* including, without limitation, damages for lost profits, loss of * +* goodwill, work stoppage, computer failure or malfunction, or any * +* and all other commercial damages or losses, even if such party * +* shall have been informed of the possibility of such damages. This * +* limitation of liability shall not apply to liability for death or * +* personal injury resulting from such party's negligence to the * +* extent applicable law prohibits such limitation. Some * +* jurisdictions do not allow the exclusion or limitation of * +* incidental or consequential damages, so this exclusion and * +* limitation may not apply to You. * +* * +************************************************************************ + +8. Litigation +------------- + +Any litigation relating to this License may be brought only in the +courts of a jurisdiction where the defendant maintains its principal +place of business and such litigation shall be governed by laws of that +jurisdiction, without reference to its conflict-of-law provisions. +Nothing in this Section shall prevent a party's ability to bring +cross-claims or counter-claims. + +9. Miscellaneous +---------------- + +This License represents the complete agreement concerning the subject +matter hereof. If any provision of this License is held to be +unenforceable, such provision shall be reformed only to the extent +necessary to make it enforceable. Any law or regulation which provides +that the language of a contract shall be construed against the drafter +shall not be used to construe this License against a Contributor. + +10. Versions of the License +--------------------------- + +10.1. New Versions + +Mozilla Foundation is the license steward. Except as provided in Section +10.3, no one other than the license steward has the right to modify or +publish new versions of this License. Each version will be given a +distinguishing version number. + +10.2. Effect of New Versions + +You may distribute the Covered Software under the terms of the version +of the License under which You originally received the Covered Software, +or under the terms of any subsequent version published by the license +steward. + +10.3. Modified Versions + +If you create software not governed by this License, and you want to +create a new license for such software, you may create and use a +modified version of this License if you rename the license and remove +any references to the name of the license steward (except to note that +such modified license differs from this License). + +10.4. Distributing Source Code Form that is Incompatible With Secondary +Licenses + +If You choose to distribute Source Code Form that is Incompatible With +Secondary Licenses under the terms of this version of the License, the +notice described in Exhibit B of this License must be attached. + +Exhibit A - Source Code Form License Notice +------------------------------------------- + + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. + +If it is not possible or desirable to put the notice in a particular +file, then You may include the notice in a location (such as a LICENSE +file in a relevant directory) where a recipient would be likely to look +for such a notice. + +You may add additional accurate notices of copyright ownership. + +Exhibit B - "Incompatible With Secondary Licenses" Notice +--------------------------------------------------------- + + This Source Code Form is "Incompatible With Secondary Licenses", as + defined by the Mozilla Public License, v. 2.0. diff --git a/README.md b/README.md index 246ea17..8b87812 100644 --- a/README.md +++ b/README.md @@ -1 +1,57 @@ # fiscalapi-ai + +Skills en español para integrar [FiscalAPI](https://docs.fiscalapi.com) (CFDI 4.0) en tu ERP, POS o SaaS desde tu agente de código: Claude Code, Codex, Cursor o GitHub Copilot. + +Las skills no copian la documentación: te llevan a la sección exacta de [docs.fiscalapi.com](https://docs.fiscalapi.com) y te ayudan a decidir qué endpoint, complemento o catálogo usar. + +## Instalación + +### Claude Code + +``` +/plugin install fiscalapi --marketplace FiscalAPI/fiscalapi-ai +``` + +O desde la terminal: + +``` +claude plugin marketplace add FiscalAPI/fiscalapi-ai +claude plugin install fiscalapi@fiscalapi +``` + +### Codex + +``` +codex plugin marketplace add FiscalAPI/fiscalapi-ai +codex plugin add fiscalapi@fiscalapi +``` + +### GitHub Copilot + +En Copilot CLI: + +``` +copilot plugin marketplace add FiscalAPI/fiscalapi-ai +copilot plugin install fiscalapi@fiscalapi +``` + +En VS Code, agrega el marketplace a tu configuración: + +```json +{ + "chat.plugins.enabled": true, + "chat.plugins.marketplaces": ["FiscalAPI/fiscalapi-ai"] +} +``` + +### Cursor + +Clona el repo en la carpeta de plugins locales y recarga la ventana (**Developer: Reload Window**): + +``` +git clone https://github.com/FiscalAPI/fiscalapi-ai ~/.cursor/plugins/local/fiscalapi +``` + +## Licencia + +[MPL-2.0](LICENSE) diff --git a/docs/ai-integracion/README.md b/docs/ai-integracion/README.md index d7384f0..406d29b 100644 --- a/docs/ai-integracion/README.md +++ b/docs/ai-integracion/README.md @@ -44,7 +44,7 @@ La siguiente tarea (global o de un área) es la primera fila `pendiente` (de esa | DOC-02 | Generar la página de errores de Pagos | docs | — | hecho | fapi-docs@816f3a4 | | DOC-03 | Corregir el enlace del SDK de Node en `llms.txt` | docs | — | hecho | fapi-docs@b5eec29 | | DOC-04 | Generar la página de errores de Comercio Exterior | docs | — | bloqueado | | -| SK-01 | Armar el esqueleto del repo, los manifiestos y el CI de enlaces | skills | — | pendiente | | +| SK-01 | Armar el esqueleto del repo, los manifiestos y el CI de enlaces | skills | — | hecho | fiscalapi-ai (commit «SK-01») | | SK-02 | Escribir `fiscalapi-integracion` y `primer-timbre` | skills | SK-01, DOC-01 | pendiente | | | SK-03 | Escribir `fiscalapi-diagnostico` y `explicar-error` | skills | SK-01, DOC-02 | pendiente | | | SK-04 | Escribir `fiscalapi-ingreso-egreso` | skills | SK-02, MED-01 | pendiente | | diff --git a/docs/ai-integracion/decisiones.md b/docs/ai-integracion/decisiones.md index a0c52b8..aa50f91 100644 --- a/docs/ai-integracion/decisiones.md +++ b/docs/ai-integracion/decisiones.md @@ -120,3 +120,9 @@ Reglas de este archivo: - **Por qué:** lo interno (dashboard, Host, DevExtreme) no es contrato, y un segundo documento sería configuración que nadie consume. Así Scalar muestra lo mismo que reciben los integradores. - **Consecuencias:** las pruebas de contrato OpenAPI solo cubren operaciones públicas. Las rutas internas se siguen probando por HTTP. - **Estado:** confirmada (técnica, la fijó BE-01). + +### D-18 — Manifiestos: `.claude-plugin/` para tres hosts y una copia para Cursor +- **Decisión:** el plugin `fiscalapi` (marketplace `fiscalapi`, `source: "./"`) se declara en `.claude-plugin/plugin.json` y `.claude-plugin/marketplace.json`, sin campo `skills` (cada host descubre `skills//SKILL.md`). Cursor lee `.cursor-plugin/plugin.json`, que es una copia exacta. No se crean `.codex-plugin/` ni `.github/plugin/`. +- **Por qué:** Codex lee `.claude-plugin/marketplace.json` y `.claude-plugin/plugin.json` (`MARKETPLACE_MANIFEST_RELATIVE_PATHS` y `DISCOVERABLE_PLUGIN_MANIFEST_PATHS` en `openai/codex`), y Copilot los busca en su orden documentado. Un manifiesto por host sería mantenimiento en paralelo (D-02). El campo `skills` se suma al descubrimiento en Claude, lo reemplaza en Cursor y está prohibido en Agent Plugins 1.0. +- **Consecuencias:** el CI valida con `claude plugin validate . --strict` y exige que la copia de Cursor sea idéntica (`cmp`). El MCP no se declara hasta U-04 (D-07). +- **Estado:** confirmada (técnica, la fijó SK-01). diff --git a/docs/ai-integracion/skills.md b/docs/ai-integracion/skills.md index b0d2dee..f6d8823 100644 --- a/docs/ai-integracion/skills.md +++ b/docs/ai-integracion/skills.md @@ -33,6 +33,8 @@ Todo va en fiscalapi-ai, en la rama `feat/ai-integracion`. Reglas comunes a SK-0 **Pruebas focalizadas:** ninguna (no aplica). La prueba es el CI de enlaces. **Fuera de alcance:** el contenido de las skills (SK-02 en adelante). **Notas:** +- Manifiestos según D-18. Verificado: `claude plugin validate . --strict` en verde; instalación local en Claude Code 2.1.300 con una skill temporal (descubierta sin declararla, 0 MCP), desinstalada después. Codex, Cursor y Copilot no están instalados aquí: se validó su formato contra dotnet/skills, DevExpress/agent-skills y el código de `openai/codex`. +- `scripts/check-skills.mjs` (frontmatter, 200 y anclas) sale en 1 con un ancla inventada, una página 404 o un `name` inválido (fixture local). El PR de prueba en rojo necesita push: es del usuario. La instalación en Cursor es local (`~/.cursor/plugins/local`); su marketplace es parte de U-04. ### SK-02 — Escribir `fiscalapi-integracion` y `primer-timbre` **Objetivo:** que en un proyecto vacío el agente instale el SDK correcto, configure ambiente y llaves, y logre un UUID en test. diff --git a/scripts/check-skills.mjs b/scripts/check-skills.mjs new file mode 100644 index 0000000..422aeed --- /dev/null +++ b/scripts/check-skills.mjs @@ -0,0 +1,123 @@ +// Revisa lo que publica el plugin antes de que llegue a un integrador: +// - el frontmatter de cada SKILL.md sigue el estándar de agentskills.io; +// - cada enlace a docs.fiscalapi.com responde 200 y, si lleva ancla, el ancla existe en la página. +// Las skills enlazan las docs en vez de copiarlas (D-03): un enlace roto deja a la skill sin su fuente. +// +// Uso: node scripts/check-skills.mjs [carpeta ...] (por omisión, skills y commands) +// Sale con código 1 si hay algún hallazgo. + +import { readdir, readFile, stat } from 'node:fs/promises' +import path from 'node:path' + +const DOCS_ORIGIN = 'https://docs.fiscalapi.com' +const DOCS_URL = /https:\/\/docs\.fiscalapi\.com[^\s)<>"'`\]]*/g +const SKILL_NAME = /^[a-z0-9]+(-[a-z0-9]+)*$/ +const MAX_NAME = 64 +const MAX_DESCRIPTION = 1024 +const MAX_SKILL_LINES = 500 + +const roots = process.argv.slice(2).length > 0 ? process.argv.slice(2) : ['skills', 'commands'] + +async function exists(target) { + return stat(target).then(() => true, () => false) +} + +async function markdownFiles(dir) { + const entries = await readdir(dir, { withFileTypes: true }) + const nested = await Promise.all( + entries.map((entry) => { + const full = path.join(dir, entry.name) + if (entry.isDirectory()) return markdownFiles(full) + return entry.name.endsWith('.md') ? [full] : [] + }), + ) + return nested.flat() +} + +/** Las claves de una línea del frontmatter YAML (`clave: valor`, con o sin comillas). */ +function frontmatter(source) { + const match = source.replace(/\r\n/g, '\n').match(/^---\n([\s\S]*?)\n---\n/) + if (!match) return null + const fields = {} + for (const line of match[1].split('\n')) { + const field = line.match(/^([A-Za-z-]+):\s*(.*)$/) + if (field) fields[field[1]] = field[2].trim().replace(/^(['"])([\s\S]*)\1$/, '$2') + } + return fields +} + +function skillProblems(file, source) { + const problems = [] + const folder = path.basename(path.dirname(file)) + const fields = frontmatter(source) + if (!fields) return [`${file}: falta el frontmatter (--- … ---) al inicio`] + + const { name, description } = fields + if (!name) problems.push(`${file}: falta 'name'`) + else { + if (name !== folder) problems.push(`${file}: 'name' (${name}) no es el nombre de la carpeta (${folder})`) + if (!SKILL_NAME.test(name) || name.length > MAX_NAME) + problems.push(`${file}: 'name' debe ser minúsculas, dígitos y guiones, de ${MAX_NAME} caracteres como máximo`) + } + if (!description) problems.push(`${file}: falta 'description'`) + else if (/^[>|]/.test(description)) + problems.push(`${file}: 'description' va en una sola línea (este lector no interpreta bloques YAML > o |)`) + else if (description.length > MAX_DESCRIPTION) + problems.push(`${file}: 'description' tiene ${description.length} caracteres (máximo ${MAX_DESCRIPTION})`) + + const lines = source.split('\n').length + if (lines >= MAX_SKILL_LINES) problems.push(`${file}: tiene ${lines} líneas (debe tener menos de ${MAX_SKILL_LINES})`) + return problems +} + +/** Los `id` de la página HTML: las anclas que genera el sitio para cada encabezado. */ +async function pageAnchors(pageUrl, cache) { + if (!cache.has(pageUrl)) { + cache.set( + pageUrl, + fetch(pageUrl).then(async (response) => + response.ok ? new Set([...(await response.text()).matchAll(/\sid="([^"]+)"/g)].map((m) => m[1])) : null, + ), + ) + } + return cache.get(pageUrl) +} + +async function linkProblems(url, anchorsCache) { + const [address, anchor] = url.split('#') + const response = await fetch(address, { method: 'GET', redirect: 'manual' }) + if (response.status !== 200) return [`${url}: responde ${response.status}`] + if (!anchor) return [] + + // El ancla vive en la página HTML; la copia .md de una página tiene las mismas secciones. + const anchors = await pageAnchors(address.replace(/\.md$/, ''), anchorsCache) + if (!anchors) return [`${url}: la página HTML de su ancla no responde`] + return anchors.has(anchor) ? [] : [`${url}: el ancla #${anchor} no existe`] +} + +async function main() { + const files = [] + for (const root of roots) if (await exists(root)) files.push(...(await markdownFiles(root))) + + const problems = [] + const urls = new Map() + for (const file of files) { + const source = await readFile(file, 'utf8') + if (path.basename(file) === 'SKILL.md') problems.push(...skillProblems(file, source)) + for (const [url] of source.matchAll(DOCS_URL)) { + const clean = url.replace(/[.,;:]+$/, '') + if (!urls.has(clean)) urls.set(clean, file) + } + } + + const anchorsCache = new Map() + for (const [url, file] of urls) { + for (const problem of await linkProblems(url, anchorsCache)) problems.push(`${file}: ${problem}`) + } + + console.log(`${files.length} archivos, ${urls.size} enlaces a ${DOCS_ORIGIN}, ${problems.length} hallazgos`) + for (const problem of problems) console.log(`- ${problem}`) + process.exitCode = problems.length > 0 ? 1 : 0 +} + +await main() From bce6b59d635b369937581fd2573c904cc7ac66b8 Mon Sep 17 00:00:00 2001 From: mendozagit Date: Thu, 15 Oct 2026 00:52:19 -0600 Subject: [PATCH 12/44] SK-03: skill fiscalapi-diagnostico y comando explicar-error MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Árbol de decisión: validación (data con propertyName), rechazo del PAC o del SAT ("CÓDIGO - texto" en message, data null, con la tabla por prefijo CFDI40, CRP, NOM y CP), 5xx (resultado incierto: no reintentar sin conciliar) y otros 4xx. Solo enlaza a docs.fiscalapi.com (D-03). - check-skills.mjs: acepta anclas (api-response#validationfailure) y revisa contra un build local con FISCALAPI_DOCS_ORIGIN. - Verificado: 15 enlaces sin hallazgos contra el build local de fapi-docs (pagos-20 da 404 en el sitio publicado hasta desplegar DOC-02); plugin validate --strict en verde; claude -p /fiscalapi:explicar-error con una validación, un CFDI40147 y un 500 llega a ValidationFailure, a la fila 47 de Errores CFDI 4.0 y a Errores del servidor. - Tablero: SK-01 con su hash. --- commands/explicar-error.md | 14 ++++++++ docs/ai-integracion/README.md | 4 +-- docs/ai-integracion/skills.md | 2 ++ scripts/check-skills.mjs | 12 ++++--- skills/fiscalapi-diagnostico/SKILL.md | 47 +++++++++++++++++++++++++++ 5 files changed, 73 insertions(+), 6 deletions(-) create mode 100644 commands/explicar-error.md create mode 100644 skills/fiscalapi-diagnostico/SKILL.md diff --git a/commands/explicar-error.md b/commands/explicar-error.md new file mode 100644 index 0000000..bcfc7db --- /dev/null +++ b/commands/explicar-error.md @@ -0,0 +1,14 @@ +--- +description: Explica un error de la API de FiscalAPI (un código como CFDI40147 o la respuesta JSON completa) y propone la corrección. +argument-hint: +--- + +Explica este error de FiscalAPI siguiendo la skill `fiscalapi-diagnostico`: + +$ARGUMENTS + +1. Clasifícalo con el árbol de decisión de la skill: validación de FiscalAPI, rechazo del PAC o del SAT, error del servidor u otro 4xx. +2. Abre la página o sección enlazada que le corresponde y localiza la fila del código o la regla del campo. +3. Responde con el tipo de error, la causa según esa fila o sección (con su enlace) y el cambio concreto que hay que hacer. + +Si solo recibiste un código, sin respuesta, explícalo con su fila de la tabla y pide la respuesta completa solo si hace falta para proponer el cambio. diff --git a/docs/ai-integracion/README.md b/docs/ai-integracion/README.md index 406d29b..d31dc1b 100644 --- a/docs/ai-integracion/README.md +++ b/docs/ai-integracion/README.md @@ -44,9 +44,9 @@ La siguiente tarea (global o de un área) es la primera fila `pendiente` (de esa | DOC-02 | Generar la página de errores de Pagos | docs | — | hecho | fapi-docs@816f3a4 | | DOC-03 | Corregir el enlace del SDK de Node en `llms.txt` | docs | — | hecho | fapi-docs@b5eec29 | | DOC-04 | Generar la página de errores de Comercio Exterior | docs | — | bloqueado | | -| SK-01 | Armar el esqueleto del repo, los manifiestos y el CI de enlaces | skills | — | hecho | fiscalapi-ai (commit «SK-01») | +| SK-01 | Armar el esqueleto del repo, los manifiestos y el CI de enlaces | skills | — | hecho | fiscalapi-ai@6caa2c4 | | SK-02 | Escribir `fiscalapi-integracion` y `primer-timbre` | skills | SK-01, DOC-01 | pendiente | | -| SK-03 | Escribir `fiscalapi-diagnostico` y `explicar-error` | skills | SK-01, DOC-02 | pendiente | | +| SK-03 | Escribir `fiscalapi-diagnostico` y `explicar-error` | skills | SK-01, DOC-02 | hecho | fiscalapi-ai (commit «SK-03») | | SK-04 | Escribir `fiscalapi-ingreso-egreso` | skills | SK-02, MED-01 | pendiente | | | SK-05 | Escribir `fiscalapi-pagos` | skills | SK-02, MED-01 | pendiente | | | SK-06 | Escribir `fiscalapi-ciclo-de-vida` y `revisar-integracion` | skills | SK-02 | pendiente | | diff --git a/docs/ai-integracion/skills.md b/docs/ai-integracion/skills.md index f6d8823..b9484c6 100644 --- a/docs/ai-integracion/skills.md +++ b/docs/ai-integracion/skills.md @@ -77,6 +77,8 @@ Todo va en fiscalapi-ai, en la rama `feat/ai-integracion`. Reglas comunes a SK-0 **Pruebas focalizadas:** ninguna (no aplica). **Fuera de alcance:** guardar o consultar rechazos históricos. **Notas:** +- Contrato verificado en fapi-back: un rechazo del PAC responde el status del PAC con `message` = `"CÓDIGO - texto"`, `details` = detalle del PAC y `data` = null (`InvoiceIssuanceTests`, `PacRejectionCodeTests`). Las respuestas de prueba se armaron con ese contrato y la fila real CFDI40147, sin timbrar contra el PAC. `claude -p /fiscalapi:explicar-error` llegó a la página y la fila correctas en los 3 casos. +- `errors/pagos-20` aún no está publicada: el CI sale en rojo hasta desplegar fapi-docs (DOC-02). Contra el build local pasa (`FISCALAPI_DOCS_ORIGIN=http://localhost:3100`). Sin tabla de `CCE` (DOC-04), la skill usa el texto del rechazo. Solo los H2 tienen ancla en el sitio. ### SK-04 — Escribir `fiscalapi-ingreso-egreso` **Objetivo:** que el agente arme bien una factura de ingreso, una nota de crédito y una factura global. diff --git a/scripts/check-skills.mjs b/scripts/check-skills.mjs index 422aeed..96d4578 100644 --- a/scripts/check-skills.mjs +++ b/scripts/check-skills.mjs @@ -4,12 +4,15 @@ // Las skills enlazan las docs en vez de copiarlas (D-03): un enlace roto deja a la skill sin su fuente. // // Uso: node scripts/check-skills.mjs [carpeta ...] (por omisión, skills y commands) +// FISCALAPI_DOCS_ORIGIN=http://localhost:3100 revisa los enlaces contra un build local de fapi-docs, para +// validar secciones que todavía no están publicadas. // Sale con código 1 si hay algún hallazgo. import { readdir, readFile, stat } from 'node:fs/promises' import path from 'node:path' const DOCS_ORIGIN = 'https://docs.fiscalapi.com' +const CHECK_ORIGIN = process.env.FISCALAPI_DOCS_ORIGIN ?? DOCS_ORIGIN const DOCS_URL = /https:\/\/docs\.fiscalapi\.com[^\s)<>"'`\]]*/g const SKILL_NAME = /^[a-z0-9]+(-[a-z0-9]+)*$/ const MAX_NAME = 64 @@ -70,13 +73,13 @@ function skillProblems(file, source) { return problems } -/** Los `id` de la página HTML: las anclas que genera el sitio para cada encabezado. */ +/** Las anclas de la página HTML: el `id` que el sitio genera para cada H2 y los `` explícitos. */ async function pageAnchors(pageUrl, cache) { if (!cache.has(pageUrl)) { cache.set( pageUrl, fetch(pageUrl).then(async (response) => - response.ok ? new Set([...(await response.text()).matchAll(/\sid="([^"]+)"/g)].map((m) => m[1])) : null, + response.ok ? new Set([...(await response.text()).matchAll(/\s(?:id|name)="([^"]+)"/g)].map((m) => m[1])) : null, ), ) } @@ -84,7 +87,8 @@ async function pageAnchors(pageUrl, cache) { } async function linkProblems(url, anchorsCache) { - const [address, anchor] = url.split('#') + const [published, anchor] = url.split('#') + const address = published.replace(DOCS_ORIGIN, CHECK_ORIGIN) const response = await fetch(address, { method: 'GET', redirect: 'manual' }) if (response.status !== 200) return [`${url}: responde ${response.status}`] if (!anchor) return [] @@ -115,7 +119,7 @@ async function main() { for (const problem of await linkProblems(url, anchorsCache)) problems.push(`${file}: ${problem}`) } - console.log(`${files.length} archivos, ${urls.size} enlaces a ${DOCS_ORIGIN}, ${problems.length} hallazgos`) + console.log(`${files.length} archivos, ${urls.size} enlaces a ${DOCS_ORIGIN} (revisados en ${CHECK_ORIGIN}), ${problems.length} hallazgos`) for (const problem of problems) console.log(`- ${problem}`) process.exitCode = problems.length > 0 ? 1 : 0 } diff --git a/skills/fiscalapi-diagnostico/SKILL.md b/skills/fiscalapi-diagnostico/SKILL.md new file mode 100644 index 0000000..a5d3beb --- /dev/null +++ b/skills/fiscalapi-diagnostico/SKILL.md @@ -0,0 +1,47 @@ +--- +name: fiscalapi-diagnostico +description: Diagnostica una respuesta de error de la API de FiscalAPI (CFDI 4.0) y propone la corrección. Distingue una validación de FiscalAPI (400 con propertyName), un rechazo del PAC o del SAT (código como CFDI40147, CRP20101, NOM5 o CP101 en message) y un error del servidor (5xx), y lleva a la página de docs.fiscalapi.com con la causa. Úsala cuando una llamada a FiscalAPI falla al timbrar, cancelar o consultar. No la uses para diseñar una factura nueva ni para errores de tu propio código que no vienen de la API. +--- + +# Diagnóstico de errores de FiscalAPI + +Toda respuesta de FiscalAPI es un `ApiResponse` con `succeeded`, `message`, `details`, `data`, `httpStatusCode` y, en los errores del servidor, `traceIdentifier`. El modelo completo está en [ApiResponse](https://docs.fiscalapi.com/api-response.md). + +Lee la respuesta completa antes de proponer un cambio. No inventes la causa: la tabla o la sección enlazada es la fuente. + +## Árbol de decisión + +1. **¿`data` es una lista de fallas con `propertyName`?** Es una **validación de FiscalAPI**: la solicitud no llegó al PAC y no se cobró timbre. + - `propertyName` es la ruta del campo en el request (por ejemplo, `Items[0].ItemTaxes[0].TaxCode`). Corrige ese campo; no te guíes por el texto. + - Modelo de cada falla: [ValidationFailure](https://docs.fiscalapi.com/api-response.md#validationfailure). + - Mensajes propios del timbrado: [Mensajes de validación del timbrado](https://docs.fiscalapi.com/errors.md#mensajes-de-validacion-del-timbrado). + - Decimales, longitudes y claves de catálogo: [Decimales](https://docs.fiscalapi.com/errors.md#decimales-de-importes-cantidades-y-tasas), [Valores que no caben en la factura](https://docs.fiscalapi.com/errors.md#valores-que-no-caben-en-la-factura) y [Claves de catálogo del SAT](https://docs.fiscalapi.com/errors.md#claves-de-catalogo-del-sat). + +2. **¿`message` empieza con un código seguido de ` - ` y `data` es `null`?** Es un **rechazo del PAC o del SAT**: no se timbró y el timbre se reembolsó. El código es lo que va antes de ` - `; `details` trae el detalle del PAC. Busca la fila del código en su tabla: + + | El código empieza con | Tabla | + |---|---| + | `CFDI40` | [Errores CFDI 4.0](https://docs.fiscalapi.com/errors/cfdi-40.md) | + | `CRP` | [Errores Pagos 2.0](https://docs.fiscalapi.com/errors/pagos-20.md) | + | `NOM` | [Errores Nómina 1.2](https://docs.fiscalapi.com/errors/nomina-12.md) | + | `CP` | [Errores Carta Porte 3.1](https://docs.fiscalapi.com/errors/carta-porte-31.md) | + | `CCE` | Comercio Exterior aún no tiene tabla publicada: usa el texto de `message` y `details`, y las reglas de Comercio Exterior en [Mensajes de validación del timbrado](https://docs.fiscalapi.com/errors.md#mensajes-de-validacion-del-timbrado). | + + - Si la tabla no tiene el código, o es un rechazo de cancelación (por ejemplo, `CACFDI33`), la causa es el texto de `message` y `details`. Revisa [Cancelar factura](https://docs.fiscalapi.com/invoices-by-value#cancelar-factura) si fallaba una cancelación. + - Corrige el dato y vuelve a timbrar: un rechazo es definitivo para esa solicitud. + +3. **¿Es un 5xx?** Es un **error del servidor**: no se resuelve cambiando los datos. Ver [Errores del servidor](https://docs.fiscalapi.com/errors.md#errores-del-servidor). + - Un 500 al timbrar puede ser un **resultado incierto**: el CFDI pudo quedar timbrado. **No repitas la factura de inmediato**: búscala con [Listar facturas](https://docs.fiscalapi.com/invoices-by-value#listar-facturas) y, si no aparece, escribe a soporte con el `traceIdentifier`. + - Un 5xx con un código de rechazo en `message` sigue la regla del paso 2. + +4. **¿Otro 4xx?** Lee `details`: + - 404: [Recurso no encontrado](https://docs.fiscalapi.com/errors.md#recurso-no-encontrado). + - 400 sin fallas de validación o 409: las restricciones de los datos y la concurrencia están en [Errores del servidor](https://docs.fiscalapi.com/errors.md#errores-del-servidor). + - Certificados y llaves privadas: [Certificados y llaves privadas](https://docs.fiscalapi.com/errors.md#certificados-y-llaves-privadas). + +## Cómo responder + +- Di de qué tipo es el error (validación, rechazo del PAC, error del servidor u otro 4xx) y por qué. +- Cita el campo (`propertyName`) o el código, y la fila o sección enlazada que explica la causa. +- Propón el cambio concreto en el código del integrador. +- Nunca pidas ni escribas una API key de producción: las llaves van en variables de entorno. From 13b39849dcc07a61b3257c42ea24542d989745ab Mon Sep 17 00:00:00 2001 From: mendozagit Date: Thu, 15 Oct 2026 00:52:26 -0600 Subject: [PATCH 13/44] Tablero: SK-03 con su hash --- docs/ai-integracion/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/ai-integracion/README.md b/docs/ai-integracion/README.md index d31dc1b..143b249 100644 --- a/docs/ai-integracion/README.md +++ b/docs/ai-integracion/README.md @@ -46,7 +46,7 @@ La siguiente tarea (global o de un área) es la primera fila `pendiente` (de esa | DOC-04 | Generar la página de errores de Comercio Exterior | docs | — | bloqueado | | | SK-01 | Armar el esqueleto del repo, los manifiestos y el CI de enlaces | skills | — | hecho | fiscalapi-ai@6caa2c4 | | SK-02 | Escribir `fiscalapi-integracion` y `primer-timbre` | skills | SK-01, DOC-01 | pendiente | | -| SK-03 | Escribir `fiscalapi-diagnostico` y `explicar-error` | skills | SK-01, DOC-02 | hecho | fiscalapi-ai (commit «SK-03») | +| SK-03 | Escribir `fiscalapi-diagnostico` y `explicar-error` | skills | SK-01, DOC-02 | hecho | fiscalapi-ai@bce6b59 | | SK-04 | Escribir `fiscalapi-ingreso-egreso` | skills | SK-02, MED-01 | pendiente | | | SK-05 | Escribir `fiscalapi-pagos` | skills | SK-02, MED-01 | pendiente | | | SK-06 | Escribir `fiscalapi-ciclo-de-vida` y `revisar-integracion` | skills | SK-02 | pendiente | | From f30ebf1e352e725ffe42f933a067d0da0dc5b4fe Mon Sep 17 00:00:00 2001 From: mendozagit Date: Thu, 15 Oct 2026 08:41:11 -0600 Subject: [PATCH 14/44] Tablero: BE-02, BE-04, DOC-01 y DOC-04 vuelven a pendiente con las respuestas del usuario --- docs/ai-integracion/README.md | 8 ++++---- docs/ai-integracion/backend.md | 4 ++-- docs/ai-integracion/docs.md | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/docs/ai-integracion/README.md b/docs/ai-integracion/README.md index 143b249..3b6f492 100644 --- a/docs/ai-integracion/README.md +++ b/docs/ai-integracion/README.md @@ -36,14 +36,14 @@ La siguiente tarea (global o de un área) es la primera fila `pendiente` (de esa | U-01 | Abrir el túnel a Seq de producción | usuario | — | pendiente | | | MED-03 | Medir el top de rechazos del PAC en Seq | medicion | U-01 | pendiente | | | BE-01 | Publicar la superficie de 100 acciones con `[PublicApi]` | backend | — | hecho | fapi-back@8d31a9aa | -| BE-02 | Agregar `operationId` estable y metadatos del documento | backend | BE-01 | bloqueado | | +| BE-02 | Agregar `operationId` estable y metadatos del documento | backend | BE-01 | pendiente | | | BE-03 | Servir `/api/openapi/v4.json` anónimo en test y live | backend | BE-02 | pendiente | | -| BE-04 | Agregar el trinquete de `EndpointSummary` y cubrir facturas | backend | BE-01 | bloqueado | | +| BE-04 | Agregar el trinquete de `EndpointSummary` y cubrir facturas | backend | BE-01 | pendiente | | | BE-05 | Completar `EndpointSummary` en el resto de la superficie | backend | BE-04 | pendiente | | -| DOC-01 | Generar `.md` por sección H2 en el build | docs | — | bloqueado | | +| DOC-01 | Generar `.md` por sección H2 en el build | docs | — | pendiente | | | DOC-02 | Generar la página de errores de Pagos | docs | — | hecho | fapi-docs@816f3a4 | | DOC-03 | Corregir el enlace del SDK de Node en `llms.txt` | docs | — | hecho | fapi-docs@b5eec29 | -| DOC-04 | Generar la página de errores de Comercio Exterior | docs | — | bloqueado | | +| DOC-04 | Generar la página de errores de Comercio Exterior | docs | — | pendiente | | | SK-01 | Armar el esqueleto del repo, los manifiestos y el CI de enlaces | skills | — | hecho | fiscalapi-ai@6caa2c4 | | SK-02 | Escribir `fiscalapi-integracion` y `primer-timbre` | skills | SK-01, DOC-01 | pendiente | | | SK-03 | Escribir `fiscalapi-diagnostico` y `explicar-error` | skills | SK-01, DOC-02 | hecho | fiscalapi-ai@bce6b59 | diff --git a/docs/ai-integracion/backend.md b/docs/ai-integracion/backend.md index 657cb83..b6ca4e9 100644 --- a/docs/ai-integracion/backend.md +++ b/docs/ai-integracion/backend.md @@ -52,7 +52,7 @@ El código de estas tareas va en fapi-back, en la rama `feat/ai-integracion` des **Pruebas focalizadas:** sí (contrato). `--filter "FullyQualifiedName~OpenApi"`. **Fuera de alcance:** resúmenes por operación (BE-04 y BE-05). **Notas:** -- Bloqueada por **Textos visibles**: ¿apruebas este `info` del documento público? Propuesta: `title` "Fiscalapi API", `version` "4.0" y como `description` el texto del spec de Postman (párrafo de la API, "Modos de operación" y los enlaces a credenciales y datos de prueba) sin la sección "Configura un environment", que es solo de la colección, y con una línea nueva: "Ambientes: `https://test.fiscalapi.com` (pruebas) y `https://live.fiscalapi.com` (producción)". +- **Textos visibles** aprobados por el usuario (2026-10-15): `title` "Fiscalapi API", `version` "4.0" y como `description` el texto del spec de Postman (párrafo de la API, "Modos de operación" y los enlaces a credenciales y datos de prueba) sin la sección "Configura un environment", más la línea "Ambientes: `https://test.fiscalapi.com` (pruebas) y `https://live.fiscalapi.com` (producción)". - La fuente (`postman/specs/Fiscalapi Specification`) solo existe sin rastrear en el working tree `fapi-back`, no en `main`: el texto aprobado se copia al transformer. ### BE-03 — Servir `/api/openapi/v4.json` anónimo en test y live @@ -88,7 +88,7 @@ El código de estas tareas va en fapi-back, en la rama `feat/ai-integracion` des **Pruebas focalizadas:** sí (contrato). `--filter "FullyQualifiedName~OpenApi"`. **Fuera de alcance:** el resto de controllers (BE-05). **Notas:** -- Bloqueada por **Textos visibles**: los resúmenes de Invoices, People y TaxFiles se te muestran en el diff antes del commit. ¿Los redacto y te muestro el diff (sin commit) en cuanto me lo indiques? BE-05 espera a esta tarea. +- **Textos visibles**: el usuario aprobó (2026-10-15) redactar los resúmenes de Invoices, People y TaxFiles y mostrarle el diff sin commit; el commit espera su OK sobre ese diff. BE-05 espera a esta tarea. ### BE-05 — Completar `EndpointSummary` en el resto de la superficie **Objetivo:** que la lista permitida del trinquete quede vacía. diff --git a/docs/ai-integracion/docs.md b/docs/ai-integracion/docs.md index 7679fa6..b204fb1 100644 --- a/docs/ai-integracion/docs.md +++ b/docs/ai-integracion/docs.md @@ -21,7 +21,7 @@ El código va en fapi-docs (y en fapi-reusables solo si cambia el generador), en **Pruebas focalizadas:** ninguna (no aplica). La verificación es el conteo del build. **Fuera de alcance:** `llms-full.txt` (D-15). **Notas:** -- Bloqueada: `invoices-by-value` e `invoices-by-reference` (23 H2 cada una, las secciones de facturas) están en `EXCLUDED_ROUTES` de `src/lib/docs.js`: noindex, `Disallow` en robots y fuera del sitemap, de `llms.txt` y de las copias `.md` (94a60f2). ¿Las secciones `.md` y su lista en `llms.txt` las incluyen? Propuesta: sí, sin tocar noindex, robots ni el sitemap. +- `invoices-by-value` e `invoices-by-reference` (23 H2 cada una, las secciones de facturas) están en `EXCLUDED_ROUTES` de `src/lib/docs.js`: noindex, `Disallow` en robots y fuera del sitemap, de `llms.txt` y de las copias `.md` (94a60f2). Decisión del usuario (2026-10-15): las secciones `.md` y su lista en `llms.txt` sí las incluyen, sin tocar noindex, robots ni el sitemap. - Localización (`feat/localization`) agrega `npm run es:diff`, que compara byte a byte `/llms.txt` y las `.md` contra `main`; si se fusiona antes, esta tarea declara su diferencia en `scripts/i18n/es-allowed.json`. Worktree listo: `fapi-docs-ai`. ### DOC-02 — Generar la página de errores de Pagos @@ -71,4 +71,4 @@ El código va en fapi-docs (y en fapi-reusables solo si cambia el generador), en **Pruebas focalizadas:** ninguna (no aplica). **Fuera de alcance:** traducir los mensajes del SAT. **Notas:** -- Bloqueada: no encontré la matriz de errores de Comercio Exterior 2.0 del SAT; solo la v1.1 Rev E (`Matriz_de_Errores_CCE_v11_E_.xls`). ¿Me pasas la URL oficial de la 2.0, o la página usa la v1.1 Rev E? +- El usuario agregó la matriz 2.0 Rev A (2026-10-15): `fapi-reusables/sat-docs/comercio-exterior/catalogs/Matriz_de_Errores_CCE_v2_0_r_A_76f1e17af8.xls`, sin rastrear en el working tree de fapi-reusables (rama `feat/auto-invoice`); se versiona en `feat/ai-integracion` desde `main`. From 9f5cddbe9995b6237a7206d572c08098529d9c41 Mon Sep 17 00:00:00 2001 From: mendozagit Date: Thu, 15 Oct 2026 09:11:03 -0600 Subject: [PATCH 15/44] BE-02 hecho (fapi-back@8194141e): operationId estable y metadatos del OpenAPI; D-13 confirmada --- docs/ai-integracion/README.md | 2 +- docs/ai-integracion/backend.md | 2 ++ docs/ai-integracion/decisiones.md | 4 ++-- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/docs/ai-integracion/README.md b/docs/ai-integracion/README.md index 3b6f492..90deae6 100644 --- a/docs/ai-integracion/README.md +++ b/docs/ai-integracion/README.md @@ -36,7 +36,7 @@ La siguiente tarea (global o de un área) es la primera fila `pendiente` (de esa | U-01 | Abrir el túnel a Seq de producción | usuario | — | pendiente | | | MED-03 | Medir el top de rechazos del PAC en Seq | medicion | U-01 | pendiente | | | BE-01 | Publicar la superficie de 100 acciones con `[PublicApi]` | backend | — | hecho | fapi-back@8d31a9aa | -| BE-02 | Agregar `operationId` estable y metadatos del documento | backend | BE-01 | pendiente | | +| BE-02 | Agregar `operationId` estable y metadatos del documento | backend | BE-01 | hecho | fapi-back@8194141e | | BE-03 | Servir `/api/openapi/v4.json` anónimo en test y live | backend | BE-02 | pendiente | | | BE-04 | Agregar el trinquete de `EndpointSummary` y cubrir facturas | backend | BE-01 | pendiente | | | BE-05 | Completar `EndpointSummary` en el resto de la superficie | backend | BE-04 | pendiente | | diff --git a/docs/ai-integracion/backend.md b/docs/ai-integracion/backend.md index b6ca4e9..eec847e 100644 --- a/docs/ai-integracion/backend.md +++ b/docs/ai-integracion/backend.md @@ -54,6 +54,8 @@ El código de estas tareas va en fapi-back, en la rama `feat/ai-integracion` des **Notas:** - **Textos visibles** aprobados por el usuario (2026-10-15): `title` "Fiscalapi API", `version` "4.0" y como `description` el texto del spec de Postman (párrafo de la API, "Modos de operación" y los enlaces a credenciales y datos de prueba) sin la sección "Configura un environment", más la línea "Ambientes: `https://test.fiscalapi.com` (pruebas) y `https://live.fiscalapi.com` (producción)". - La fuente (`postman/specs/Fiscalapi Specification`) solo existe sin rastrear en el working tree `fapi-back`, no en `main`: el texto aprobado se copia al transformer. +- Hecho: el operationId va en `[PublicApi("…")]` (D-13). `X-TIME-ZONE` lleva una descripción redactada desde `TimezoneMiddleware` (texto visible asumido; se revisa con BE-04). Redocly 2.57.0: válido salvo `operation-summary` (BE-04/05). +- Pendiente opcional: ninguna prueba fija los 100 nombres; renombrar uno no falla. La descripción aprobada dice que toda petición usa los dos headers, pero el portal es anónimo. ### BE-03 — Servir `/api/openapi/v4.json` anónimo en test y live **Objetivo:** que cualquier agente o herramienta descargue el contrato sin credenciales y sin tocar nginx. diff --git a/docs/ai-integracion/decisiones.md b/docs/ai-integracion/decisiones.md index aa50f91..de85de2 100644 --- a/docs/ai-integracion/decisiones.md +++ b/docs/ai-integracion/decisiones.md @@ -88,10 +88,10 @@ Reglas de este archivo: - **Estado:** confirmada. ### D-13 — `operationId` estable por acción pública -- **Decisión:** cada acción con `[PublicApi]` declara un `operationId` estable, en camelCase y en inglés como el código (por ejemplo `createInvoice`). Se fija con el mecanismo de nombre de endpoint de ASP.NET Core 10, verificado en la documentación oficial. +- **Decisión:** cada acción con `[PublicApi]` declara un `operationId` estable, en camelCase y en inglés como el código. Va como argumento obligatorio del mismo atributo (`[PublicApi("createInvoice")]`), que implementa `IEndpointNameMetadata`: el generador de ASP.NET Core 10 toma el operationId del nombre de ruta o, si no hay, de ese metadato (`OpenApiDocumentService.GetOperationId`, release/10.0). Convención: `list` para colecciones, `get` / `create` / `update` / `delete` para el CRUD y el verbo del dominio cuando lo hay (`cancelInvoice`, `revokeApiKey`, `cancelTicket`, `transferStamps`, `runSatValidations`). - **Por qué:** el MCP y las skills lo usan como id. El spec de Postman no trae ninguno. - **Consecuencias:** cambiar un `operationId` es un cambio visible para los consumidores del OpenAPI. -- **Estado:** técnica — la fija BE-02. +- **Estado:** confirmada (técnica, la fijó BE-02). ### D-14 — Paquete `@fiscalapi/mcp`, publicado por CI cuando cambia el hash - **Decisión:** From 0f712d6b76e27f9edaa8c55695d934cb662b2fdc Mon Sep 17 00:00:00 2001 From: mendozagit Date: Thu, 15 Oct 2026 09:35:05 -0600 Subject: [PATCH 16/44] =?UTF-8?q?BE-03=20hecho=20(fapi-back@3ab2a369):=20O?= =?UTF-8?q?penAPI=20an=C3=B3nimo=20en=20/api/openapi;=20D-16=20corregida?= =?UTF-8?q?=20(la=20ruta=20no=20tiene=20rate=20limit)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/ai-integracion/README.md | 2 +- docs/ai-integracion/backend.md | 2 ++ docs/ai-integracion/decisiones.md | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/ai-integracion/README.md b/docs/ai-integracion/README.md index 90deae6..3750114 100644 --- a/docs/ai-integracion/README.md +++ b/docs/ai-integracion/README.md @@ -37,7 +37,7 @@ La siguiente tarea (global o de un área) es la primera fila `pendiente` (de esa | MED-03 | Medir el top de rechazos del PAC en Seq | medicion | U-01 | pendiente | | | BE-01 | Publicar la superficie de 100 acciones con `[PublicApi]` | backend | — | hecho | fapi-back@8d31a9aa | | BE-02 | Agregar `operationId` estable y metadatos del documento | backend | BE-01 | hecho | fapi-back@8194141e | -| BE-03 | Servir `/api/openapi/v4.json` anónimo en test y live | backend | BE-02 | pendiente | | +| BE-03 | Servir `/api/openapi/v4.json` anónimo en test y live | backend | BE-02 | hecho | fapi-back@3ab2a369 | | BE-04 | Agregar el trinquete de `EndpointSummary` y cubrir facturas | backend | BE-01 | pendiente | | | BE-05 | Completar `EndpointSummary` en el resto de la superficie | backend | BE-04 | pendiente | | | DOC-01 | Generar `.md` por sección H2 en el build | docs | — | pendiente | | diff --git a/docs/ai-integracion/backend.md b/docs/ai-integracion/backend.md index eec847e..e830149 100644 --- a/docs/ai-integracion/backend.md +++ b/docs/ai-integracion/backend.md @@ -73,6 +73,8 @@ El código de estas tareas va en fapi-back, en la rama `feat/ai-integracion` des **Pruebas focalizadas:** sí (exposición). Prueba de integración: la ruta responde sin auth y no expone un documento distinto de `v4`. **Fuera de alcance:** importar a Postman (U-03). **Notas:** +- Verificado con el host de pruebas (ambiente Test) y con `curl` contra la app en Development: no se levantó la app local en Test porque ahí se activan los jobs del SAT sobre el restore de prod. "Caché" es output cache del servidor (10 min); la respuesta no lleva `Cache-Control`. +- Docs con la ruta vieja `/openapi/v4.json` "solo en Development" (prosa, nada lo consume en runtime): `fapi-back/docs/auto-invoice/{backend-emision,portal,release-notes}.md` y `fapi-auto-invoice/CLAUDE.md`. ### BE-04 — Agregar el trinquete de `EndpointSummary` y cubrir facturas **Objetivo:** que ninguna operación pública nueva quede sin resumen, empezando por la ruta crítica del integrador. diff --git a/docs/ai-integracion/decisiones.md b/docs/ai-integracion/decisiones.md index de85de2..b15ef06 100644 --- a/docs/ai-integracion/decisiones.md +++ b/docs/ai-integracion/decisiones.md @@ -112,7 +112,7 @@ Reglas de este archivo: ### D-16 — El OpenAPI se sirve bajo `/api` para no tocar nginx - **Decisión:** `MapOpenApi` se mapea a `/api/openapi/{documentName}.json`, anónimo y en todos los ambientes. - **Por qué:** el nginx de test y el de live ya mandan `location ^~ /api` al back. El CD no toca nginx, que se cambia a mano (`infra/nginx/live/README.md`). Con otra ruta habría un paso manual. -- **Consecuencias:** cero cambios en nginx. La ruta hereda el rate limiting de `/api`. +- **Consecuencias:** cero cambios en nginx. `/api` no tiene `limit_req` en nginx y el limiter de la app solo cubre familias marcadas, así que la ruta no tiene rate limit: el costo lo acota una output cache de una sola entrada (no varía por query, host ni `Authorization`). Corregido en BE-03; antes decía que heredaba el rate limiting de `/api`. - **Estado:** confirmada. ### D-17 — Un solo documento OpenAPI: el público From c4c4747c0178581c77fc39150f90805bd3f69288 Mon Sep 17 00:00:00 2001 From: mendozagit Date: Thu, 15 Oct 2026 09:39:45 -0600 Subject: [PATCH 17/44] =?UTF-8?q?Tablero:=20entorno=20local=20y=20c=C3=B3m?= =?UTF-8?q?o=20reanudar;=20BE-04=20sigue=20pendiente?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/ai-integracion/README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/ai-integracion/README.md b/docs/ai-integracion/README.md index 3750114..d302ce7 100644 --- a/docs/ai-integracion/README.md +++ b/docs/ai-integracion/README.md @@ -23,6 +23,8 @@ Sigue el ciclo de `fapi-core` (sección 1). Particularidades de este feature: - **Restricción dura: cero mantenimiento adicional para FiscalAPI (D-02).** Si una tarea agrega algo que hay que actualizar a mano en paralelo a las docs o al código, está mal planteada. - **fiscalapi-ai es público.** Nada llega a `main` sin el PR que fusiona el usuario. Publicar en npm o en los marketplaces es un paso del usuario (U-04). - **Todo en español:** skills, descripciones de herramientas del MCP y docs (D-04). +- **Entorno local:** fapi-back y fapi-docs se trabajan en los worktrees `../fapi-back-ai` y `../fapi-docs-ai` (rama `feat/ai-integracion`, ya con `npm ci` en fapi-docs-ai), para no tocar la rama `feat/localization` de sus working trees principales. fapi-reusables sigue en `feat/auto-invoice` con cambios del usuario: la matriz de DOC-04 está sin rastrear ahí y se versiona en un worktree nuevo desde `main`. +- **Reanudar:** relee este tablero y vuelve a lanzar `/loop` con el prompt del feature; toma la siguiente fila según `fapi-core`. Nada queda a medias fuera de git. ## Tablero Estado ∈ `pendiente` | `en curso` | `hecho` | `bloqueado`. En `bloqueado`, el motivo o la pregunta va en las Notas de la tarea. From 5e0a6967ebb5acc13de4d9b3916076cc29ee21ae Mon Sep 17 00:00:00 2001 From: mendozagit Date: Thu, 15 Oct 2026 10:35:28 -0600 Subject: [PATCH 18/44] BE-04 (ai-integracion): cierre con fapi-back@2bd55554 y D-19 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Trinquete de EndpointSummary y resúmenes de Invoices, People y TaxFiles en fapi-back@2bd55554 (113/113 OpenApi en verde; 69 -> 41 operaciones sin summary). D-19, asumida: vocabulario de búsqueda del integrador en los resúmenes. --- docs/ai-integracion/README.md | 2 +- docs/ai-integracion/backend.md | 4 +++- docs/ai-integracion/decisiones.md | 6 ++++++ 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/docs/ai-integracion/README.md b/docs/ai-integracion/README.md index d302ce7..83ca01a 100644 --- a/docs/ai-integracion/README.md +++ b/docs/ai-integracion/README.md @@ -40,7 +40,7 @@ La siguiente tarea (global o de un área) es la primera fila `pendiente` (de esa | BE-01 | Publicar la superficie de 100 acciones con `[PublicApi]` | backend | — | hecho | fapi-back@8d31a9aa | | BE-02 | Agregar `operationId` estable y metadatos del documento | backend | BE-01 | hecho | fapi-back@8194141e | | BE-03 | Servir `/api/openapi/v4.json` anónimo en test y live | backend | BE-02 | hecho | fapi-back@3ab2a369 | -| BE-04 | Agregar el trinquete de `EndpointSummary` y cubrir facturas | backend | BE-01 | pendiente | | +| BE-04 | Agregar el trinquete de `EndpointSummary` y cubrir facturas | backend | BE-01 | hecho | fapi-back@2bd55554 | | BE-05 | Completar `EndpointSummary` en el resto de la superficie | backend | BE-04 | pendiente | | | DOC-01 | Generar `.md` por sección H2 en el build | docs | — | pendiente | | | DOC-02 | Generar la página de errores de Pagos | docs | — | hecho | fapi-docs@816f3a4 | diff --git a/docs/ai-integracion/backend.md b/docs/ai-integracion/backend.md index e830149..ba5030f 100644 --- a/docs/ai-integracion/backend.md +++ b/docs/ai-integracion/backend.md @@ -92,7 +92,9 @@ El código de estas tareas va en fapi-back, en la rama `feat/ai-integracion` des **Pruebas focalizadas:** sí (contrato). `--filter "FullyQualifiedName~OpenApi"`. **Fuera de alcance:** el resto de controllers (BE-05). **Notas:** -- **Textos visibles**: el usuario aprobó (2026-10-15) redactar los resúmenes de Invoices, People y TaxFiles y mostrarle el diff sin commit; el commit espera su OK sobre ese diff. BE-05 espera a esta tarea. +- La lista permitida es `OperationsWithoutSummary` en `WebApi.Tests/OpenApi/PublicApiSummaryTests.cs` (41 `operationId`). BE-05 borra filas y al final borra la lista. +- Las descripciones terminan en una línea "Docs:" que se arma con `PublicApiDocument.DocsUrl` y el ancla H2 real. Antes de usar cada ancla, compruébala contra docs.fiscalapi.com. +- El usuario delegó la aprobación de los textos (2026-10-15, "analízalo tú y decide"); ver D-19. Que `details` de `getPerson` no tenga efecto quedó en `known-issues.md` de fapi-back. ### BE-05 — Completar `EndpointSummary` en el resto de la superficie **Objetivo:** que la lista permitida del trinquete quede vacía. diff --git a/docs/ai-integracion/decisiones.md b/docs/ai-integracion/decisiones.md index b15ef06..250e4dc 100644 --- a/docs/ai-integracion/decisiones.md +++ b/docs/ai-integracion/decisiones.md @@ -126,3 +126,9 @@ Reglas de este archivo: - **Por qué:** Codex lee `.claude-plugin/marketplace.json` y `.claude-plugin/plugin.json` (`MARKETPLACE_MANIFEST_RELATIVE_PATHS` y `DISCOVERABLE_PLUGIN_MANIFEST_PATHS` en `openai/codex`), y Copilot los busca en su orden documentado. Un manifiesto por host sería mantenimiento en paralelo (D-02). El campo `skills` se suma al descubrimiento en Claude, lo reemplaza en Cursor y está prohibido en Agent Plugins 1.0. - **Consecuencias:** el CI valida con `claude plugin validate . --strict` y exige que la copia de Cursor sea idéntica (`cmp`). El MCP no se declara hasta U-04 (D-07). - **Estado:** confirmada (técnica, la fijó SK-01). + +### D-19 — Resúmenes OpenAPI con el vocabulario de búsqueda del integrador +- **Decisión:** el `EndpointSummary` y la `EndpointDescription` de cada operación pública usan los términos con que busca un integrador ("complemento de pago", "cliente receptor", "estatus ante el SAT", "patrón"), aunque el vocabulario interno de fapi-core evite "cliente" para una persona. Cada descripción termina con una línea "Docs:" que enlaza la sección H2 de docs.fiscalapi.com que la documenta. +- **Por qué:** se buscan para que las consultas que fallaron en la prueba de búsqueda (`requisitos.md`) encuentren su operación, como pidió el usuario el 2026-10-14. El vocabulario interno gobierna el código y la prosa del equipo, no las palabras con que busca un integrador. +- **Consecuencias:** BE-05 sigue la misma regla. Un texto que afirme un código HTTP o un campo se valida contra el handler. +- **Estado:** asumida (agente, 2026-10-15). El usuario delegó la aprobación de los textos de BE-04 ("analízalo tú y decide"); revisar con el usuario. From df1d94a6527b300afac347c6873324e361d08496 Mon Sep 17 00:00:00 2001 From: mendozagit Date: Thu, 15 Oct 2026 11:09:29 -0600 Subject: [PATCH 19/44] BE-05 (ai-integracion): cierre con fapi-back@bc22dcec EndpointSummary en las 41 operaciones restantes (41 -> 0 sin resumen) y la lista permitida del trinquete eliminada; 113/113 OpenApi en verde. --- docs/ai-integracion/README.md | 2 +- docs/ai-integracion/backend.md | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/docs/ai-integracion/README.md b/docs/ai-integracion/README.md index 83ca01a..18719be 100644 --- a/docs/ai-integracion/README.md +++ b/docs/ai-integracion/README.md @@ -41,7 +41,7 @@ La siguiente tarea (global o de un área) es la primera fila `pendiente` (de esa | BE-02 | Agregar `operationId` estable y metadatos del documento | backend | BE-01 | hecho | fapi-back@8194141e | | BE-03 | Servir `/api/openapi/v4.json` anónimo en test y live | backend | BE-02 | hecho | fapi-back@3ab2a369 | | BE-04 | Agregar el trinquete de `EndpointSummary` y cubrir facturas | backend | BE-01 | hecho | fapi-back@2bd55554 | -| BE-05 | Completar `EndpointSummary` en el resto de la superficie | backend | BE-04 | pendiente | | +| BE-05 | Completar `EndpointSummary` en el resto de la superficie | backend | BE-04 | hecho | fapi-back@bc22dcec | | DOC-01 | Generar `.md` por sección H2 en el build | docs | — | pendiente | | | DOC-02 | Generar la página de errores de Pagos | docs | — | hecho | fapi-docs@816f3a4 | | DOC-03 | Corregir el enlace del SDK de Node en `llms.txt` | docs | — | hecho | fapi-docs@b5eec29 | diff --git a/docs/ai-integracion/backend.md b/docs/ai-integracion/backend.md index ba5030f..97bd7ff 100644 --- a/docs/ai-integracion/backend.md +++ b/docs/ai-integracion/backend.md @@ -110,3 +110,6 @@ El código de estas tareas va en fapi-back, en la rama `feat/ai-integracion` des **Pruebas focalizadas:** las mismas de BE-04. **Fuera de alcance:** — **Notas:** +- Ya no hay operaciones sin resumen (41 → 0) y `PublicApiSummaryTests` lo exige sin lista. Los textos los decidió el agente por delegación del usuario (D-19). Las 31 operaciones que ya tenían resumen (Webhooks, Tickets, Portal, AutoInvoice, Events) siguen sin enlace "Docs:". +- En Test (`FakeResults=true`), `runSatValidations` simula todos los tipos, aunque el comentario de `RunSatValidationsCommand.cs:161` diga que solo `sat.cfdi.status`. +- Dos defectos nuevos quedaron en `known-issues.md` de fapi-back, los dos de descarga masiva: el SAT puede no aceptar una solicitud y aun así se guarda, y crear o borrar solicitudes solo exige `XmlDownloader.Read`. From 20a2b75124f30ddb9e21a2dbe80a538ef80a9891 Mon Sep 17 00:00:00 2001 From: mendozagit Date: Thu, 15 Oct 2026 11:33:41 -0600 Subject: [PATCH 20/44] DOC-01 (ai-integracion): cierre con fapi-docs@68c74c4 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Copia .md por sección H2 (378 secciones, 429 copias) con guarda postbuild y lista de secciones en llms.txt (51 -> 429 enlaces .md). --- docs/ai-integracion/README.md | 2 +- docs/ai-integracion/docs.md | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/docs/ai-integracion/README.md b/docs/ai-integracion/README.md index 18719be..3894722 100644 --- a/docs/ai-integracion/README.md +++ b/docs/ai-integracion/README.md @@ -42,7 +42,7 @@ La siguiente tarea (global o de un área) es la primera fila `pendiente` (de esa | BE-03 | Servir `/api/openapi/v4.json` anónimo en test y live | backend | BE-02 | hecho | fapi-back@3ab2a369 | | BE-04 | Agregar el trinquete de `EndpointSummary` y cubrir facturas | backend | BE-01 | hecho | fapi-back@2bd55554 | | BE-05 | Completar `EndpointSummary` en el resto de la superficie | backend | BE-04 | hecho | fapi-back@bc22dcec | -| DOC-01 | Generar `.md` por sección H2 en el build | docs | — | pendiente | | +| DOC-01 | Generar `.md` por sección H2 en el build | docs | — | hecho | fapi-docs@68c74c4 | | DOC-02 | Generar la página de errores de Pagos | docs | — | hecho | fapi-docs@816f3a4 | | DOC-03 | Corregir el enlace del SDK de Node en `llms.txt` | docs | — | hecho | fapi-docs@b5eec29 | | DOC-04 | Generar la página de errores de Comercio Exterior | docs | — | pendiente | | diff --git a/docs/ai-integracion/docs.md b/docs/ai-integracion/docs.md index b204fb1..13923a2 100644 --- a/docs/ai-integracion/docs.md +++ b/docs/ai-integracion/docs.md @@ -21,8 +21,9 @@ El código va en fapi-docs (y en fapi-reusables solo si cambia el generador), en **Pruebas focalizadas:** ninguna (no aplica). La verificación es el conteo del build. **Fuera de alcance:** `llms-full.txt` (D-15). **Notas:** -- `invoices-by-value` e `invoices-by-reference` (23 H2 cada una, las secciones de facturas) están en `EXCLUDED_ROUTES` de `src/lib/docs.js`: noindex, `Disallow` en robots y fuera del sitemap, de `llms.txt` y de las copias `.md` (94a60f2). Decisión del usuario (2026-10-15): las secciones `.md` y su lista en `llms.txt` sí las incluyen, sin tocar noindex, robots ni el sitemap. -- Localización (`feat/localization`) agrega `npm run es:diff`, que compara byte a byte `/llms.txt` y las `.md` contra `main`; si se fusiona antes, esta tarea declara su diferencia en `scripts/i18n/es-allowed.json`. Worktree listo: `fapi-docs-ai`. +- Hay 378 secciones H2 en 53 páginas y 429 copias `.md`. Las 427 del plan eran una estimación. La guarda `npm run check:markdown` corre como `postbuild` y compara contra las anclas del HTML construido. Los H2 de ``, `` y `` no tienen copia. +- Las secciones de `invoices-by-value` y `invoices-by-reference` (`EXCLUDED_ROUTES`) tienen copia y van en `llms.txt` como `[Página: Sección]`, por decisión del usuario. Como robots no se tocó, su `Disallow` por prefijo también bloquea esas 46 copias para los crawlers que respetan robots. +- Antes de fusionar, `es:diff` de localización (`feat/localization`) va a marcar `/llms.txt` y las `.md` nuevas: declara la diferencia en `scripts/i18n/es-allowed.json`. ### DOC-02 — Generar la página de errores de Pagos **Objetivo:** que `fiscalapi-diagnostico` y `explicar-error` puedan enlazar los errores `CRP*`, igual que hoy enlazan los de CFDI 4.0, Nómina y Carta Porte. Comercio Exterior pasó a DOC-04. From fbf09866dabb433df2a10db571c97f192d9a3445 Mon Sep 17 00:00:00 2001 From: mendozagit Date: Thu, 15 Oct 2026 11:40:03 -0600 Subject: [PATCH 21/44] DOC-04 (ai-integracion): cierre con fapi-docs@73d326f y fapi-reusables@3be8d36 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Página errors/comercio-exterior-20 con los 73 códigos CCE de la matriz del SAT, versionada en fapi-reusables. DOC-01 cambia a fapi-docs@b97ad82: el commit se rehízo solo para fijar el autor mendozagit (fapi-docs tiene user.name local "Fiscalapi"), sin cambios de contenido. --- docs/ai-integracion/README.md | 6 +++--- docs/ai-integracion/docs.md | 4 +++- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/docs/ai-integracion/README.md b/docs/ai-integracion/README.md index 3894722..f62ce23 100644 --- a/docs/ai-integracion/README.md +++ b/docs/ai-integracion/README.md @@ -23,7 +23,7 @@ Sigue el ciclo de `fapi-core` (sección 1). Particularidades de este feature: - **Restricción dura: cero mantenimiento adicional para FiscalAPI (D-02).** Si una tarea agrega algo que hay que actualizar a mano en paralelo a las docs o al código, está mal planteada. - **fiscalapi-ai es público.** Nada llega a `main` sin el PR que fusiona el usuario. Publicar en npm o en los marketplaces es un paso del usuario (U-04). - **Todo en español:** skills, descripciones de herramientas del MCP y docs (D-04). -- **Entorno local:** fapi-back y fapi-docs se trabajan en los worktrees `../fapi-back-ai` y `../fapi-docs-ai` (rama `feat/ai-integracion`, ya con `npm ci` en fapi-docs-ai), para no tocar la rama `feat/localization` de sus working trees principales. fapi-reusables sigue en `feat/auto-invoice` con cambios del usuario: la matriz de DOC-04 está sin rastrear ahí y se versiona en un worktree nuevo desde `main`. +- **Entorno local:** fapi-back y fapi-docs se trabajan en los worktrees `../fapi-back-ai` y `../fapi-docs-ai` (rama `feat/ai-integracion`, ya con `npm ci` en fapi-docs-ai), para no tocar la rama `feat/localization` de sus working trees principales. fapi-reusables sigue en `feat/auto-invoice` con cambios del usuario; lo del feature va en el worktree `../fapi-reusables-ai` (rama `feat/ai-integracion` desde `origin/main`). fapi-docs tiene `user.name` local "Fiscalapi": todo commit ahí lleva `-c user.name=mendozagit -c user.email=mendoza.git@gmail.com`. - **Reanudar:** relee este tablero y vuelve a lanzar `/loop` con el prompt del feature; toma la siguiente fila según `fapi-core`. Nada queda a medias fuera de git. ## Tablero @@ -42,10 +42,10 @@ La siguiente tarea (global o de un área) es la primera fila `pendiente` (de esa | BE-03 | Servir `/api/openapi/v4.json` anónimo en test y live | backend | BE-02 | hecho | fapi-back@3ab2a369 | | BE-04 | Agregar el trinquete de `EndpointSummary` y cubrir facturas | backend | BE-01 | hecho | fapi-back@2bd55554 | | BE-05 | Completar `EndpointSummary` en el resto de la superficie | backend | BE-04 | hecho | fapi-back@bc22dcec | -| DOC-01 | Generar `.md` por sección H2 en el build | docs | — | hecho | fapi-docs@68c74c4 | +| DOC-01 | Generar `.md` por sección H2 en el build | docs | — | hecho | fapi-docs@b97ad82 | | DOC-02 | Generar la página de errores de Pagos | docs | — | hecho | fapi-docs@816f3a4 | | DOC-03 | Corregir el enlace del SDK de Node en `llms.txt` | docs | — | hecho | fapi-docs@b5eec29 | -| DOC-04 | Generar la página de errores de Comercio Exterior | docs | — | pendiente | | +| DOC-04 | Generar la página de errores de Comercio Exterior | docs | — | hecho | fapi-docs@73d326f, fapi-reusables@3be8d36 | | SK-01 | Armar el esqueleto del repo, los manifiestos y el CI de enlaces | skills | — | hecho | fiscalapi-ai@6caa2c4 | | SK-02 | Escribir `fiscalapi-integracion` y `primer-timbre` | skills | SK-01, DOC-01 | pendiente | | | SK-03 | Escribir `fiscalapi-diagnostico` y `explicar-error` | skills | SK-01, DOC-02 | hecho | fiscalapi-ai@bce6b59 | diff --git a/docs/ai-integracion/docs.md b/docs/ai-integracion/docs.md index 13923a2..f41fb8b 100644 --- a/docs/ai-integracion/docs.md +++ b/docs/ai-integracion/docs.md @@ -72,4 +72,6 @@ El código va en fapi-docs (y en fapi-reusables solo si cambia el generador), en **Pruebas focalizadas:** ninguna (no aplica). **Fuera de alcance:** traducir los mensajes del SAT. **Notas:** -- El usuario agregó la matriz 2.0 Rev A (2026-10-15): `fapi-reusables/sat-docs/comercio-exterior/catalogs/Matriz_de_Errores_CCE_v2_0_r_A_76f1e17af8.xls`, sin rastrear en el working tree de fapi-reusables (rama `feat/auto-invoice`); se versiona en `feat/ai-integracion` desde `main`. +- La matriz 2.0 Rev A quedó versionada en `fapi-reusables@3be8d36` (worktree `fapi-reusables-ai`). La copia sin rastrear del working tree del usuario sigue ahí y no se tocó. +- La página toma la estructura de `pagos-20` y del generador solo sale la tabla. La columna Aclaraciones vino vacía en las 73 filas, así que la tabla tiene las mismas 6 columnas que Pagos. +- Matriz y página tienen los mismos 73 códigos CCE, sin diferencias. From 37ce025688c32f4b398a4a88577991ac5da9d9ce Mon Sep 17 00:00:00 2001 From: mendozagit Date: Thu, 15 Oct 2026 11:51:58 -0600 Subject: [PATCH 22/44] ai-integracion: tarea SK-10 para validar los enlaces "Docs:" del OpenAPI MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Los 84 enlaces de las EndpointDescription de BE-04 y BE-05 apuntan a anclas H2 de docs.fiscalapi.com escritas a mano; hoy nada detecta si un título cambia. SK-10 extiende el check semanal de SK-01 para leer /api/openapi/v4.json y validarlos. Va antes de SK-02 en el tablero. --- docs/ai-integracion/README.md | 1 + docs/ai-integracion/skills.md | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+) diff --git a/docs/ai-integracion/README.md b/docs/ai-integracion/README.md index f62ce23..d38e71d 100644 --- a/docs/ai-integracion/README.md +++ b/docs/ai-integracion/README.md @@ -47,6 +47,7 @@ La siguiente tarea (global o de un área) es la primera fila `pendiente` (de esa | DOC-03 | Corregir el enlace del SDK de Node en `llms.txt` | docs | — | hecho | fapi-docs@b5eec29 | | DOC-04 | Generar la página de errores de Comercio Exterior | docs | — | hecho | fapi-docs@73d326f, fapi-reusables@3be8d36 | | SK-01 | Armar el esqueleto del repo, los manifiestos y el CI de enlaces | skills | — | hecho | fiscalapi-ai@6caa2c4 | +| SK-10 | Validar en el check semanal los enlaces "Docs:" del OpenAPI | skills | SK-01, BE-05 | pendiente | | | SK-02 | Escribir `fiscalapi-integracion` y `primer-timbre` | skills | SK-01, DOC-01 | pendiente | | | SK-03 | Escribir `fiscalapi-diagnostico` y `explicar-error` | skills | SK-01, DOC-02 | hecho | fiscalapi-ai@bce6b59 | | SK-04 | Escribir `fiscalapi-ingreso-egreso` | skills | SK-02, MED-01 | pendiente | | diff --git a/docs/ai-integracion/skills.md b/docs/ai-integracion/skills.md index b9484c6..ce84c76 100644 --- a/docs/ai-integracion/skills.md +++ b/docs/ai-integracion/skills.md @@ -168,3 +168,21 @@ Todo va en fiscalapi-ai, en la rama `feat/ai-integracion`. Reglas comunes a SK-0 **Pruebas focalizadas:** ninguna (no aplica). **Fuera de alcance:** — **Notas:** + +### SK-10 — Validar en el check semanal los enlaces "Docs:" del OpenAPI +**Objetivo:** que un título renombrado o retirado en fapi-docs no deje rotos, sin que nadie lo note, los enlaces "Docs:" de las descripciones del OpenAPI público. +**Depende de:** SK-01, BE-05. +**Skills:** ninguna de dominio. Se extiende `scripts/check-skills.mjs` de SK-01. +**Modelo sugerido (opcional):** haiku. Reutiliza el validador de enlaces que ya existe. +**Contexto:** D-02, RF-01. BE-04 y BE-05 escribieron a mano 84 enlaces a anclas H2 de docs.fiscalapi.com en las `EndpointDescription` de fapi-back, y solo se verificaron una vez. El check de SK-01 corre en cada PR y cada lunes, pero solo lee `skills/**`. +**Alcance:** +- El check también descarga `/api/openapi/v4.json`: de live por omisión, o de la URL de `FISCALAPI_OPENAPI_URL` para validar contra un back local. Extrae cada URL de docs.fiscalapi.com que aparece en `info.description` y en las `description` de cada operación. +- Esas URL se validan con la misma regla que las de las skills: 200 y ancla existente, contra `FISCALAPI_DOCS_ORIGIN` cuando está definido. Cada hallazgo indica el `operationId` donde está el enlace roto. +- Corre en el mismo job `Check`, sin un workflow nuevo. +**Criterios de aceptación (todos con `→ verificar`):** +- Los 84 enlaces del OpenAPI se validan y pasan → verificar: `FISCALAPI_OPENAPI_URL` apuntando al back local de `fapi-back-ai` y `node scripts/check-skills.mjs`, con el conteo de enlaces del OpenAPI en la salida. +- Un ancla rota en una descripción hace fallar el check y nombra la operación → verificar: un OpenAPI de prueba con un ancla inventada sale con código 1 y el `operationId` en el mensaje. +**Pruebas focalizadas:** ninguna (no aplica). La prueba es el propio check. +**Fuera de alcance:** las 31 operaciones que todavía no tienen línea "Docs:" (Webhooks, Tickets, Portal, AutoInvoice, Events). +**Notas:** +- Hasta que BE-04 y BE-05 lleguen a producción, el OpenAPI de live no trae los enlaces "Docs:" y el check los valida en 0. Por eso la verificación usa el back local. From b38786bca5ef69d11ba24d9f350a6c0f7219984e Mon Sep 17 00:00:00 2001 From: mendozagit Date: Thu, 15 Oct 2026 11:55:38 -0600 Subject: [PATCH 23/44] =?UTF-8?q?SK-10:=20el=20check=20de=20enlaces=20tamb?= =?UTF-8?q?i=C3=A9n=20valida=20los=20"Docs:"=20del=20OpenAPI=20p=C3=BAblic?= =?UTF-8?q?o?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit scripts/check-skills.mjs descarga /api/openapi/v4.json (live por omisión, o FISCALAPI_OPENAPI_URL: URL o ruta de un .json) y valida cada enlace a docs.fiscalapi.com de info.description y de las descripciones de cada operación, con la misma regla que las skills (200 y ancla existente). Cada hallazgo nombra el operationId. Evidencia: - OpenAPI del código actual (fapi-back@bc22dcec) + build local de fapi-docs-ai: 15 + 87 enlaces (100 únicos), 0 hallazgos, código 0. - Ancla inventada en transferStamps: código 1, "OpenAPI transferStamps: ... el ancla #ancla-inventada no existe". - Live hoy responde 401 en /api/openapi/v4.json (BE-03 sin desplegar): el check lo reporta y sale en 1 hasta el deploy de fapi-back. --- docs/ai-integracion/README.md | 2 +- docs/ai-integracion/skills.md | 4 ++- scripts/check-skills.mjs | 61 ++++++++++++++++++++++++++++++----- 3 files changed, 57 insertions(+), 10 deletions(-) diff --git a/docs/ai-integracion/README.md b/docs/ai-integracion/README.md index d38e71d..ccf7153 100644 --- a/docs/ai-integracion/README.md +++ b/docs/ai-integracion/README.md @@ -47,7 +47,7 @@ La siguiente tarea (global o de un área) es la primera fila `pendiente` (de esa | DOC-03 | Corregir el enlace del SDK de Node en `llms.txt` | docs | — | hecho | fapi-docs@b5eec29 | | DOC-04 | Generar la página de errores de Comercio Exterior | docs | — | hecho | fapi-docs@73d326f, fapi-reusables@3be8d36 | | SK-01 | Armar el esqueleto del repo, los manifiestos y el CI de enlaces | skills | — | hecho | fiscalapi-ai@6caa2c4 | -| SK-10 | Validar en el check semanal los enlaces "Docs:" del OpenAPI | skills | SK-01, BE-05 | pendiente | | +| SK-10 | Validar en el check semanal los enlaces "Docs:" del OpenAPI | skills | SK-01, BE-05 | hecho | | | SK-02 | Escribir `fiscalapi-integracion` y `primer-timbre` | skills | SK-01, DOC-01 | pendiente | | | SK-03 | Escribir `fiscalapi-diagnostico` y `explicar-error` | skills | SK-01, DOC-02 | hecho | fiscalapi-ai@bce6b59 | | SK-04 | Escribir `fiscalapi-ingreso-egreso` | skills | SK-02, MED-01 | pendiente | | diff --git a/docs/ai-integracion/skills.md b/docs/ai-integracion/skills.md index ce84c76..d63f0b3 100644 --- a/docs/ai-integracion/skills.md +++ b/docs/ai-integracion/skills.md @@ -185,4 +185,6 @@ Todo va en fiscalapi-ai, en la rama `feat/ai-integracion`. Reglas comunes a SK-0 **Pruebas focalizadas:** ninguna (no aplica). La prueba es el propio check. **Fuera de alcance:** las 31 operaciones que todavía no tienen línea "Docs:" (Webhooks, Tickets, Portal, AutoInvoice, Events). **Notas:** -- Hasta que BE-04 y BE-05 lleguen a producción, el OpenAPI de live no trae los enlaces "Docs:" y el check los valida en 0. Por eso la verificación usa el back local. +- `FISCALAPI_OPENAPI_URL` acepta una URL o la ruta de un `.json`. Contra el OpenAPI del código actual hay 87 enlaces (84 de operaciones y 3 de `info`). Con `FISCALAPI_DOCS_ORIGIN` en el build local de fapi-docs, los 100 enlaces pasan. Un ancla inventada sale con código 1 y nombra la operación (`OpenAPI transferStamps`). +- Hoy live responde 401 en `/api/openapi/v4.json`, porque BE-03 no está desplegado. Por eso el check de cada PR y cada lunes sale en rojo hasta el deploy de fapi-back. Es a propósito: el plugin y el MCP dependen de ese documento, y saltarlo escondería una regresión. +- También sale en rojo `errors/pagos-20.md` (SK-03), que es de DOC-02 y aún no está desplegada. diff --git a/scripts/check-skills.mjs b/scripts/check-skills.mjs index 96d4578..fece6f9 100644 --- a/scripts/check-skills.mjs +++ b/scripts/check-skills.mjs @@ -1,11 +1,15 @@ // Revisa lo que publica el plugin antes de que llegue a un integrador: // - el frontmatter de cada SKILL.md sigue el estándar de agentskills.io; -// - cada enlace a docs.fiscalapi.com responde 200 y, si lleva ancla, el ancla existe en la página. +// - cada enlace a docs.fiscalapi.com responde 200 y, si lleva ancla, el ancla existe en la página; +// - lo mismo para los enlaces "Docs:" de las descripciones del OpenAPI público (/api/openapi/v4.json), que +// fapi-back escribe a mano y que un título renombrado en fapi-docs rompería sin que nadie lo note. // Las skills enlazan las docs en vez de copiarlas (D-03): un enlace roto deja a la skill sin su fuente. // // Uso: node scripts/check-skills.mjs [carpeta ...] (por omisión, skills y commands) // FISCALAPI_DOCS_ORIGIN=http://localhost:3100 revisa los enlaces contra un build local de fapi-docs, para // validar secciones que todavía no están publicadas. +// FISCALAPI_OPENAPI_URL= lee el OpenAPI de un back local o de un archivo, para +// validar descripciones que todavía no están en producción. // Sale con código 1 si hay algún hallazgo. import { readdir, readFile, stat } from 'node:fs/promises' @@ -13,6 +17,8 @@ import path from 'node:path' const DOCS_ORIGIN = 'https://docs.fiscalapi.com' const CHECK_ORIGIN = process.env.FISCALAPI_DOCS_ORIGIN ?? DOCS_ORIGIN +const OPENAPI_SOURCE = process.env.FISCALAPI_OPENAPI_URL ?? 'https://live.fiscalapi.com/api/openapi/v4.json' +const HTTP_METHODS = ['get', 'put', 'post', 'delete', 'options', 'head', 'patch', 'trace'] const DOCS_URL = /https:\/\/docs\.fiscalapi\.com[^\s)<>"'`\]]*/g const SKILL_NAME = /^[a-z0-9]+(-[a-z0-9]+)*$/ const MAX_NAME = 64 @@ -99,6 +105,35 @@ async function linkProblems(url, anchorsCache) { return anchors.has(anchor) ? [] : [`${url}: el ancla #${anchor} no existe`] } +/** Agrega a `urls` cada enlace a las docs de `text`, recordando dónde aparece (`where`). */ +function collectDocsUrls(text, where, urls) { + for (const [url] of (text ?? '').matchAll(DOCS_URL)) { + const clean = url.replace(/[.,;:]+$/, '') + urls.set(clean, [...(urls.get(clean) ?? []), where]) + } +} + +async function readOpenApi(source) { + if (!/^https?:\/\//.test(source)) return JSON.parse(await readFile(source, 'utf8')) + const response = await fetch(source) + if (!response.ok) throw new Error(`${source} responde ${response.status}`) + return response.json() +} + +/** Los enlaces a las docs de la descripción del documento y de cada operación, por operationId. */ +async function openApiDocsUrls(source) { + const document = await readOpenApi(source) + const urls = new Map() + collectDocsUrls(document.info?.description, 'OpenAPI info.description', urls) + for (const [route, item] of Object.entries(document.paths ?? {})) { + for (const method of HTTP_METHODS) { + const operation = item[method] + if (operation) collectDocsUrls(operation.description, `OpenAPI ${operation.operationId ?? `${method} ${route}`}`, urls) + } + } + return urls +} + async function main() { const files = [] for (const root of roots) if (await exists(root)) files.push(...(await markdownFiles(root))) @@ -108,18 +143,28 @@ async function main() { for (const file of files) { const source = await readFile(file, 'utf8') if (path.basename(file) === 'SKILL.md') problems.push(...skillProblems(file, source)) - for (const [url] of source.matchAll(DOCS_URL)) { - const clean = url.replace(/[.,;:]+$/, '') - if (!urls.has(clean)) urls.set(clean, file) - } + collectDocsUrls(source, file, urls) + } + const skillUrlCount = urls.size + + let openApiUrlCount = 0 + try { + const openApiUrls = await openApiDocsUrls(OPENAPI_SOURCE) + openApiUrlCount = openApiUrls.size + for (const [url, places] of openApiUrls) urls.set(url, [...(urls.get(url) ?? []), ...places]) + } catch (error) { + problems.push(`${OPENAPI_SOURCE}: no se pudo leer el OpenAPI (${error.message})`) } const anchorsCache = new Map() - for (const [url, file] of urls) { - for (const problem of await linkProblems(url, anchorsCache)) problems.push(`${file}: ${problem}`) + for (const [url, places] of urls) { + for (const problem of await linkProblems(url, anchorsCache)) problems.push(`${places.join(', ')}: ${problem}`) } - console.log(`${files.length} archivos, ${urls.size} enlaces a ${DOCS_ORIGIN} (revisados en ${CHECK_ORIGIN}), ${problems.length} hallazgos`) + console.log( + `${files.length} archivos con ${skillUrlCount} enlaces y el OpenAPI (${OPENAPI_SOURCE}) con ${openApiUrlCount}: ` + + `${urls.size} enlaces a ${DOCS_ORIGIN} revisados en ${CHECK_ORIGIN}, ${problems.length} hallazgos`, + ) for (const problem of problems) console.log(`- ${problem}`) process.exitCode = problems.length > 0 ? 1 : 0 } From 0abba68ae1666121aaa86d055c5877bf3370a6ea Mon Sep 17 00:00:00 2001 From: mendozagit Date: Thu, 15 Oct 2026 11:55:38 -0600 Subject: [PATCH 24/44] Tablero: SK-10 con su hash --- docs/ai-integracion/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/ai-integracion/README.md b/docs/ai-integracion/README.md index ccf7153..bfb1a1e 100644 --- a/docs/ai-integracion/README.md +++ b/docs/ai-integracion/README.md @@ -47,7 +47,7 @@ La siguiente tarea (global o de un área) es la primera fila `pendiente` (de esa | DOC-03 | Corregir el enlace del SDK de Node en `llms.txt` | docs | — | hecho | fapi-docs@b5eec29 | | DOC-04 | Generar la página de errores de Comercio Exterior | docs | — | hecho | fapi-docs@73d326f, fapi-reusables@3be8d36 | | SK-01 | Armar el esqueleto del repo, los manifiestos y el CI de enlaces | skills | — | hecho | fiscalapi-ai@6caa2c4 | -| SK-10 | Validar en el check semanal los enlaces "Docs:" del OpenAPI | skills | SK-01, BE-05 | hecho | | +| SK-10 | Validar en el check semanal los enlaces "Docs:" del OpenAPI | skills | SK-01, BE-05 | hecho | fiscalapi-ai@b38786b | | SK-02 | Escribir `fiscalapi-integracion` y `primer-timbre` | skills | SK-01, DOC-01 | pendiente | | | SK-03 | Escribir `fiscalapi-diagnostico` y `explicar-error` | skills | SK-01, DOC-02 | hecho | fiscalapi-ai@bce6b59 | | SK-04 | Escribir `fiscalapi-ingreso-egreso` | skills | SK-02, MED-01 | pendiente | | From 05135bf20c54c57911767a61f978078374b803f8 Mon Sep 17 00:00:00 2001 From: mendozagit Date: Thu, 15 Oct 2026 12:35:19 -0600 Subject: [PATCH 25/44] SK-02: skill fiscalapi-integracion y comando primer-timbre MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - fiscalapi-integracion: ambiente y llaves (flujo actual de cuenta, suscripción de prueba y timbres), SDK por lenguaje, valores o referencias, datos y CSD de prueba de SW (CSD, no FIEL), tabla de ruteo a las secciones .md y a las skills, y las reglas no negociables (un 5xx o timeout no se reintenta). - references/sdks.md: paquete, cliente, emisión y respuesta de los 5 SDK 4.0.450, con las trampas de sus README. - primer-timbre: detecta el lenguaje, instala el SDK, lee las llaves de variables de entorno, timbra una factura de ingreso por valores y la consulta por id. - fiscalapi-diagnostico: los CCE enlazan errors/comercio-exterior-20 (DOC-04). Evidencia: - primer-timbre en Node y en .NET vacíos contra el back local (PAC de pruebas): UUID 6b92a718-1792-4017-834f-79c6a87a8d88 y fba6dd24-8d13-4f34-94e3-979f1df29274, confirmados con GET /api/v4/invoices/{id}. - "Quiero integrar FiscalAPI…" en claude -p (sonnet): invoca fiscalapi:fiscalapi-integracion. - check-skills contra el build local de fapi-docs: 138 enlaces, 0 hallazgos; claude plugin validate --strict en verde. --- commands/primer-timbre.md | 17 +++++ docs/ai-integracion/README.md | 2 +- docs/ai-integracion/skills.md | 3 + skills/fiscalapi-diagnostico/SKILL.md | 2 +- skills/fiscalapi-integracion/SKILL.md | 75 +++++++++++++++++++ .../fiscalapi-integracion/references/sdks.md | 48 ++++++++++++ 6 files changed, 145 insertions(+), 2 deletions(-) create mode 100644 commands/primer-timbre.md create mode 100644 skills/fiscalapi-integracion/SKILL.md create mode 100644 skills/fiscalapi-integracion/references/sdks.md diff --git a/commands/primer-timbre.md b/commands/primer-timbre.md new file mode 100644 index 0000000..a3dff5c --- /dev/null +++ b/commands/primer-timbre.md @@ -0,0 +1,17 @@ +--- +description: Timbra tu primera factura de ingreso en el ambiente de pruebas de FiscalAPI con el SDK de tu proyecto, de punta a punta, y devuelve su UUID. +argument-hint: "[lenguaje, si el proyecto todavía no tiene uno]" +--- + +Timbra una factura de ingreso de prueba en este proyecto siguiendo la skill `fiscalapi-integracion` (lee su sección de SDK y `references/sdks.md` antes de escribir código). + +$ARGUMENTS + +1. **Llaves.** Lee `FISCALAPI_API_KEY` y `FISCALAPI_TENANT` de las variables de entorno, y `FISCALAPI_API_URL` (si no existe, usa `https://test.fiscalapi.com`). Si falta la llave o el tenant, detente y explica cómo obtenerlas con el paso 1 de la skill. Nunca escribas las llaves en el código ni en un archivo, y nunca uses `https://live.fiscalapi.com`. +2. **Lenguaje y SDK.** Detecta el lenguaje con la tabla de la skill (o usa el que te indiquen arriba; si el proyecto está vacío y no te indican uno, pregunta cuál usar). Instala el SDK oficial con el comando de `references/sdks.md`. +3. **Certificados de prueba.** Descarga el zip de [Certificados de prueba](https://docs.fiscalapi.com/testing-data/certificados-de-prueba.md) en una carpeta que no se versione (agrégala al `.gitignore`). Del emisor `FUNK671228PH6`, toma el `.cer` y el `.key` de la subcarpeta `CSD_*` (no los de la FIEL) y conviértelos a base64 al ejecutar; la contraseña es `12345678a`. +4. **Factura.** Arma una factura de ingreso por valores con el cuerpo de ejemplo de [Crear factura ingreso](https://docs.fiscalapi.com/invoices-by-value/crear-factura-ingreso.md): emisor `FUNK671228PH6` (régimen 621), receptor `EKU9003173C9` (régimen 601, uso G01, CP 42501), un concepto con IVA 16 % que conserve todos los campos del concepto del ejemplo (por valores, `itemSku` es obligatorio), y la fecha actual como texto `yyyy-MM-ddTHH:mm:ss`. Usa los nombres del SDK, no los del README si `references/sdks.md` dice que el README falla. +5. **Timbra una sola vez.** Si `succeeded` es verdadero, imprime el `id` y el `uuid` de `data`. Si no, interpreta la respuesta con la skill `fiscalapi-diagnostico`, corrige y vuelve a intentar. Ante un 5xx o un timeout, no reintentes: sigue la regla de la skill. +6. **Comprueba.** Consulta la factura por su `id` con el SDK ([Obtener factura por ID](https://docs.fiscalapi.com/invoices-by-value/obtener-factura-por-id.md)) y confirma que trae el mismo `uuid`. + +Responde con el `uuid`, el `id`, los archivos que creaste y el comando para volver a correr el programa. diff --git a/docs/ai-integracion/README.md b/docs/ai-integracion/README.md index bfb1a1e..44480e4 100644 --- a/docs/ai-integracion/README.md +++ b/docs/ai-integracion/README.md @@ -48,7 +48,7 @@ La siguiente tarea (global o de un área) es la primera fila `pendiente` (de esa | DOC-04 | Generar la página de errores de Comercio Exterior | docs | — | hecho | fapi-docs@73d326f, fapi-reusables@3be8d36 | | SK-01 | Armar el esqueleto del repo, los manifiestos y el CI de enlaces | skills | — | hecho | fiscalapi-ai@6caa2c4 | | SK-10 | Validar en el check semanal los enlaces "Docs:" del OpenAPI | skills | SK-01, BE-05 | hecho | fiscalapi-ai@b38786b | -| SK-02 | Escribir `fiscalapi-integracion` y `primer-timbre` | skills | SK-01, DOC-01 | pendiente | | +| SK-02 | Escribir `fiscalapi-integracion` y `primer-timbre` | skills | SK-01, DOC-01 | hecho | | | SK-03 | Escribir `fiscalapi-diagnostico` y `explicar-error` | skills | SK-01, DOC-02 | hecho | fiscalapi-ai@bce6b59 | | SK-04 | Escribir `fiscalapi-ingreso-egreso` | skills | SK-02, MED-01 | pendiente | | | SK-05 | Escribir `fiscalapi-pagos` | skills | SK-02, MED-01 | pendiente | | diff --git a/docs/ai-integracion/skills.md b/docs/ai-integracion/skills.md index d63f0b3..3be179d 100644 --- a/docs/ai-integracion/skills.md +++ b/docs/ai-integracion/skills.md @@ -58,6 +58,9 @@ Todo va en fiscalapi-ai, en la rama `feat/ai-integracion`. Reglas comunes a SK-0 **Pruebas focalizadas:** ninguna (no aplica). Lo cubre EV-01. **Fuera de alcance:** complementos (SK-04 en adelante). **Notas:** +- `primer-timbre` dio UUID en Node (`6b92a718-…`) y en .NET (`fba6dd24-…`), confirmados con `GET /invoices/{id}`. Corrió contra el back local (PAC de pruebas) con una cuenta creada por el producto (registro, Mailpit y Stripe test). La skill se dispara con "Quiero integrar FiscalAPI…". +- Las URL `.md` por sección (DOC-01) dan 404 en live hasta el deploy de fapi-docs. El check pasa contra el build local (138 enlaces). `references/sdks.md` documenta las trampas reales de los README: Node `FiscalApiClient`, `double` en Java y fechas. +- Con resultado incierto (5xx o timeout), el back no guarda la factura, así que "Listar facturas" no la muestra y el camino real es soporte con el `traceIdentifier`. La regla de la skill sigue a las docs: falta decidir si el back debe registrarla. ### SK-03 — Escribir `fiscalapi-diagnostico` y `explicar-error` **Objetivo:** que ante una respuesta de error el agente distinga una validación de FiscalAPI, un rechazo del PAC o un 5xx, y proponga la corrección leyendo la tabla correcta. diff --git a/skills/fiscalapi-diagnostico/SKILL.md b/skills/fiscalapi-diagnostico/SKILL.md index a5d3beb..174f829 100644 --- a/skills/fiscalapi-diagnostico/SKILL.md +++ b/skills/fiscalapi-diagnostico/SKILL.md @@ -25,7 +25,7 @@ Lee la respuesta completa antes de proponer un cambio. No inventes la causa: la | `CRP` | [Errores Pagos 2.0](https://docs.fiscalapi.com/errors/pagos-20.md) | | `NOM` | [Errores Nómina 1.2](https://docs.fiscalapi.com/errors/nomina-12.md) | | `CP` | [Errores Carta Porte 3.1](https://docs.fiscalapi.com/errors/carta-porte-31.md) | - | `CCE` | Comercio Exterior aún no tiene tabla publicada: usa el texto de `message` y `details`, y las reglas de Comercio Exterior en [Mensajes de validación del timbrado](https://docs.fiscalapi.com/errors.md#mensajes-de-validacion-del-timbrado). | + | `CCE` | [Errores Comercio Exterior 2.0](https://docs.fiscalapi.com/errors/comercio-exterior-20.md) | - Si la tabla no tiene el código, o es un rechazo de cancelación (por ejemplo, `CACFDI33`), la causa es el texto de `message` y `details`. Revisa [Cancelar factura](https://docs.fiscalapi.com/invoices-by-value#cancelar-factura) si fallaba una cancelación. - Corrige el dato y vuelve a timbrar: un rechazo es definitivo para esa solicitud. diff --git a/skills/fiscalapi-integracion/SKILL.md b/skills/fiscalapi-integracion/SKILL.md new file mode 100644 index 0000000..23b0ec1 --- /dev/null +++ b/skills/fiscalapi-integracion/SKILL.md @@ -0,0 +1,75 @@ +--- +name: fiscalapi-integracion +description: Integra FiscalAPI (facturación electrónica CFDI 4.0 en México) en un ERP, POS o SaaS. Elige el SDK según el lenguaje del proyecto, configura el ambiente de pruebas y las llaves (X-API-KEY, X-TENANT-KEY, X-TIME-ZONE), decide entre el modo por valores y por referencias, y lleva a la sección exacta de docs.fiscalapi.com y a la skill de cada tipo de factura. Úsala cuando te pidan integrar FiscalAPI, timbrar o emitir facturas CFDI desde código, o empezar con FiscalAPI desde cero. No la uses para diagnosticar un error que ya devolvió la API (usa fiscalapi-diagnostico). +--- + +# Integrar FiscalAPI + +FiscalAPI es una API REST para emitir CFDI 4.0 y sus complementos, cancelarlos y consultarlos ante el SAT. Esta skill decide cómo integrarla; el detalle de cada endpoint vive en las docs, en copias Markdown por sección (`https://docs.fiscalapi.com//.md`). Lee la sección que corresponda antes de escribir el cuerpo de una petición: no lo inventes. + +Para timbrar la primera factura de punta a punta en un proyecto vacío, usa el comando `primer-timbre`. + +## 1. Ambiente y llaves + +- Trabaja siempre en **pruebas**: `https://test.fiscalapi.com`. Producción (`https://live.fiscalapi.com`) es otra cuenta con otras llaves; pasar a producción cambia solo la URL, la API key y el tenant ([Ambientes](https://docs.fiscalapi.com/environments.md)). +- Cada petición lleva `X-API-KEY` y `X-TENANT-KEY` ([Encabezados requeridos](https://docs.fiscalapi.com/authentication/encabezados-http-requeridos.md)) y `X-TIME-ZONE` con una zona IANA, por ejemplo `America/Mexico_City`: las fechas sin zona del JSON se leen y se escriben en esa zona, y si falta se usa `America/Mexico_City`. Los SDK mandan los tres headers. +- Las llaves se leen de variables de entorno (`FISCALAPI_API_URL`, `FISCALAPI_API_KEY`, `FISCALAPI_TENANT`). Nunca las escribas en el código, en un archivo versionado ni en el chat, y nunca uses una llave de producción para probar. + +Si el integrador todavía no tiene llaves de pruebas, guíalo por el flujo de [Pasos para comenzar](https://docs.fiscalapi.com/quickstart/pasos-para-comenzar.md): sin suscripción activa y sin timbres no se puede timbrar. + +1. Crear la cuenta en `https://test.fiscalapi.com/create-account` y confirmar el correo. +2. [Activar la suscripción de prueba](https://docs.fiscalapi.com/subscription-info/activar-suscripcion-de-prueba.md) y [comprar timbres de prueba](https://docs.fiscalapi.com/subscription-info/activar-timbres-de-prueba.md) con una [tarjeta de prueba](https://docs.fiscalapi.com/testing-data/tarjetas-de-prueba.md). Los pagos se hacen solo desde el dashboard. +3. [Generar la API key](https://docs.fiscalapi.com/credentials-info/generar-una-api-key.md) y copiar el [tenant (TID)](https://docs.fiscalapi.com/credentials-info/obtener-el-tenant.md) del perfil. + +## 2. SDK + +Usa el SDK oficial del lenguaje del proyecto; el paquete, la creación del cliente y las trampas de cada README están en [references/sdks.md](references/sdks.md). Si el lenguaje no tiene SDK, llama a la API por HTTP con los tres headers. + +| El proyecto tiene | SDK | +|---|---| +| `*.csproj`, `*.sln` | .NET: `Fiscalapi` | +| `package.json` | Node: `fiscalapi` | +| `pyproject.toml`, `requirements.txt` | Python: `fiscalapi` | +| `composer.json` | PHP: `fiscalapi/fiscalapi` | +| `pom.xml`, `build.gradle` | Java: `com.fiscalapi:fiscalapi` | + +## 3. Por valores o por referencias + +Los dos modos usan el mismo `POST /api/v4/invoices` y dan el mismo CFDI ([Diferencias entre modos](https://docs.fiscalapi.com/modes-of-operation/diferencias-entre-modos-de-operacion.md)). + +- **Por valores:** el cuerpo lleva todos los datos del emisor, del receptor y de los conceptos, incluidos los certificados CSD del emisor en base64 (`taxCredentials`). No hay que registrar nada antes. Conviene para la primera prueba y cuando el sistema del integrador ya guarda esos datos. +- **Por referencias:** el cuerpo lleva los `id` de personas y productos ya registrados en FiscalAPI ([emisores](https://docs.fiscalapi.com/issuers.md), [receptores](https://docs.fiscalapi.com/recipients.md), [productos](https://docs.fiscalapi.com/products.md), [certificados](https://docs.fiscalapi.com/tax-files.md)). Peticiones más chicas y sin certificados en cada llamada; es el modo recomendado para producción. + +Las secciones de facturas existen en las dos variantes con la misma ancla: basta cambiar `invoices-by-value` por `invoices-by-reference` en la URL. + +## 4. Datos de prueba + +- El SAT exige usar sus [personas de prueba](https://docs.fiscalapi.com/testing-data/personas-de-prueba.md) en este ambiente (RFC, razón social y código postal); no inventes RFC. +- Los CSD de prueba vienen de SW ([Certificados de prueba](https://docs.fiscalapi.com/testing-data/certificados-de-prueba.md)); todas las llaves usan la contraseña `12345678a`. Cada persona trae también su FIEL: para timbrar usa el `.cer` y el `.key` de la subcarpeta `CSD_*`, no el `.cer` ni el `Claveprivada_FIEL_*.key` de la carpeta de la persona. +- El ejemplo de [Crear factura ingreso por valores](https://docs.fiscalapi.com/invoices-by-value/crear-factura-ingreso.md) usa al emisor `FUNK671228PH6` (régimen 621) y al receptor `EKU9003173C9` (régimen 601, uso G01, CP 42501). + +## 5. Qué vas a construir + +| Necesidad | Sección (por valores; cambia a `invoices-by-reference` si usas referencias) | Skill | +|---|---|---| +| Factura de ingreso o nota de crédito (egreso) | [crear-factura-ingreso](https://docs.fiscalapi.com/invoices-by-value/crear-factura-ingreso.md), [crear-nota-de-credito](https://docs.fiscalapi.com/invoices-by-value/crear-nota-de-credito.md) | `fiscalapi-ingreso-egreso` | +| Complemento de pago (PPD) | [crear-factura-de-pago](https://docs.fiscalapi.com/invoices-by-value/crear-factura-de-pago.md) | `fiscalapi-pagos` | +| Recibo de nómina | [crear-factura-de-nomina](https://docs.fiscalapi.com/invoices-by-value/crear-factura-de-nomina.md) | `fiscalapi-nomina` | +| Carta porte | [crear-factura-de-carta-porte](https://docs.fiscalapi.com/invoices-by-value/crear-factura-de-carta-porte.md) | `fiscalapi-carta-porte` | +| Comercio exterior | [crear-factura-de-comercio-exterior](https://docs.fiscalapi.com/invoices-by-value/crear-factura-de-comercio-exterior.md) | `fiscalapi-comercio-exterior` | +| Factura global | [crear-factura-global](https://docs.fiscalapi.com/invoices-by-value/crear-factura-global.md) | `fiscalapi-ingreso-egreso` | +| Cancelar, PDF, XML, correo o estatus ante el SAT | [cancelar-factura](https://docs.fiscalapi.com/invoices-by-value/cancelar-factura.md), [generar-pdf-por-id](https://docs.fiscalapi.com/invoices-by-value/generar-pdf-por-id.md), [obtener-xml-por-id](https://docs.fiscalapi.com/invoices-by-value/obtener-xml-por-id.md), [enviar-factura-por-correo](https://docs.fiscalapi.com/invoices-by-value/enviar-factura-por-correo.md), [consultar-estado-de-factura](https://docs.fiscalapi.com/invoices-by-value/consultar-estado-de-factura.md) | `fiscalapi-ciclo-de-vida` | +| Un error que ya devolvió la API | [Errores](https://docs.fiscalapi.com/errors.md) | `fiscalapi-diagnostico` | +| Catálogos del SAT | [Catálogos](https://docs.fiscalapi.com/catalogs.md) | — | +| Notificaciones de eventos (webhooks) | [Webhooks](https://docs.fiscalapi.com/webhooks-info.md) | `fiscalapi-ciclo-de-vida` | +| Descarga masiva de CFDI del SAT | [Descarga masiva](https://docs.fiscalapi.com/download-info.md) | — | +| Validaciones ante el SAT (69-B, sellos, estatus) | [Validaciones SAT](https://docs.fiscalapi.com/sat-validations-info.md) | — | +| Autofactura de tickets | [Auto CFDI](https://docs.fiscalapi.com/auto-invoice.md) | — | + +## 6. Reglas que no se negocian + +- **Un 5xx o un timeout al timbrar no se reintenta.** El CFDI pudo quedar timbrado y un reintento lo duplicaría. `POST /api/v4/invoices` no es idempotente. Registra la respuesta completa (incluido `traceIdentifier`), marca la factura como "resultado incierto" en el sistema del integrador y sigue [Errores del servidor](https://docs.fiscalapi.com/errors/errores-del-servidor.md): búscala con [Listar facturas](https://docs.fiscalapi.com/invoices-by-value/listar-facturas.md) y, si no aparece, escribe a soporte con el `traceIdentifier` antes de volver a emitir. +- **Un 400 con fallas de validación o un rechazo del PAC no consumen timbre**: corrige el dato y vuelve a emitir. Para interpretarlos usa `fiscalapi-diagnostico`. +- **Lee `succeeded`, no solo el código HTTP.** Toda respuesta es un [ApiResponse](https://docs.fiscalapi.com/api-response.md); el CFDI emitido trae su `id` en FiscalAPI y su `uuid` (folio fiscal). Guarda los dos. +- **Las fechas van sin zona** (`yyyy-MM-ddTHH:mm:ss`), en la zona de `X-TIME-ZONE`. +- **Nada en producción** desde un agente: timbrar en `live` es un acto fiscal irreversible. diff --git a/skills/fiscalapi-integracion/references/sdks.md b/skills/fiscalapi-integracion/references/sdks.md new file mode 100644 index 0000000..0974b2d --- /dev/null +++ b/skills/fiscalapi-integracion/references/sdks.md @@ -0,0 +1,48 @@ +# SDK oficiales de FiscalAPI + +Los cinco SDK tienen la misma forma: un objeto de configuración con la URL base, la API key, el tenant y la zona horaria; un cliente; un servicio de facturas con `create`; y una respuesta con `succeeded` y `data`. La URL base es libre (`https://test.fiscalapi.com` o la que indique `FISCALAPI_API_URL`) y el SDK agrega `/api/v4/...`. El repo de cada uno tiene su README con más ejemplos. + +Revisado contra la versión 4.0.450 de los cinco paquetes. + +## .NET: [fiscalapi-net](https://github.com/FiscalAPI/fiscalapi-net) + +- Instalar: `dotnet add package Fiscalapi` (netstandard2.0 y .NET Framework 4.6.1 en adelante). +- Namespaces: `Fiscalapi.Common` (configuración y respuesta), `Fiscalapi.Services` (cliente) y `Fiscalapi.Models` (factura). +- Cliente: `var client = FiscalApiClient.Create(new FiscalapiSettings { ApiUrl = ..., ApiKey = ..., Tenant = ..., TimeZone = "America/Mexico_City" });`. Con inyección de dependencias: `builder.Services.AddFiscalApi()` lee la sección `FiscalapiSettings` y requiere `using FiscalApi;` (con A mayúscula, a diferencia del resto del SDK). +- Emitir: `var response = await client.Invoices.CreateAsync(invoice);` con `Invoice`, `InvoiceIssuer` (`TaxCredentials` con `FileType.CertificateCsd` y `FileType.PrivateKeyCsd`), `InvoiceRecipient`, `InvoiceItem` e `InvoiceItemTax`. +- Respuesta: `response.Succeeded`, `response.Data.Id`, `response.Data.Uuid`, `response.Message`, `response.Details`, `response.TraceIdentifier`. +- Timeout fijo de 30 s. + +## Node: [fiscalapi-node](https://github.com/FiscalAPI/fiscalapi-node) + +- Instalar: `npm install fiscalapi`. +- Cliente: `import { FiscalapiClient } from 'fiscalapi'` y `FiscalapiClient.create({ apiUrl, apiKey, tenant, timeZone: 'America/Mexico_City' })`. **La clase es `FiscalapiClient`** (con `a` minúscula); los ejemplos del README escriben `FiscalApiClient`, que no existe. +- Emitir: `const response = await client.invoices.create(invoice)` con un objeto plano; `taxCredentials` lleva `fileType: 0` (`.cer`) y `fileType: 1` (`.key`). +- Respuesta: `response.succeeded`, `response.data.id`, `response.data.uuid`, `response.message`, `response.details`. +- Fechas: manda `date` como texto `yyyy-MM-ddTHH:mm:ss` (por ejemplo con `DateTime.now().toFormat("yyyy-MM-dd'T'HH:mm:ss")` de luxon). Un `new Date()` viaja en UTC con `Z` y deja de ser la hora local de `X-TIME-ZONE`. +- `debug: true` imprime los logs y además desactiva la validación SSL: no lo uses fuera de tu máquina. + +## Python: [fiscalapi-python](https://github.com/FiscalAPI/fiscalapi-python) + +- Instalar: `pip install fiscalapi` (Python 3.9 o más reciente). +- Cliente: `from fiscalapi import FiscalApiClient, FiscalApiSettings` y `client = FiscalApiClient(settings=FiscalApiSettings(api_url=..., api_key=..., tenant=..., time_zone="America/Mexico_City"))`. +- Emitir: `response = client.invoices.create(invoice)` con `Invoice`, `InvoiceIssuer` (`tax_credentials=[TaxCredential(base64_file=..., file_type=0, password=...), TaxCredential(..., file_type=1, ...)]`), `InvoiceRecipient`, `InvoiceItem` e `ItemTax`. +- Respuesta: `response.succeeded`, `response.data.id`, `response.data.uuid`, `response.message`, `response.details`, `response.trace_identifier`. +- Fechas: `date=datetime.now().strftime("%Y-%m-%dT%H:%M:%S")`; un `datetime` sin formatear manda microsegundos. +- El SDK no fija timeout en `requests`. + +## PHP: [fiscalapi-php](https://github.com/FiscalAPI/fiscalapi-php) + +- Instalar: `composer require fiscalapi/fiscalapi` (PHP 7.4 o más reciente). +- Cliente: `$settings = new \Fiscalapi\Http\FiscalApiSettings($apiUrl, $apiKey, $tenant);` y `$client = new \Fiscalapi\Services\FiscalApiClient($settings);`. Los argumentos son posicionales: después vienen `debug`, `verifySsl`, `apiVersion` y `timeZone`. +- Emitir: `$response = $client->getInvoiceService()->create($invoice);` con un arreglo asociativo con las mismas claves del JSON de las docs. +- Respuesta: `$json = $response->getJson();` y luego `$json['succeeded']`, `$json['data']['id']`, `$json['data']['uuid']`. `isSuccessful()` solo revisa que el HTTP sea 2xx: usa `succeeded`. + +## Java: [fiscalapi-java](https://github.com/FiscalAPI/fiscalapi-java) + +- Instalar: `com.fiscalapi:fiscalapi:4.0.450` en Maven o Gradle (Java 8 o más reciente). +- Paquetes: `com.fiscalapi.common` (configuración y respuesta), `com.fiscalapi.services` (cliente) y `com.fiscalapi.models.invoicing` (factura). +- Cliente: `FiscalApiSettings` con `setApiUrl`, `setApiKey`, `setTenant` y `setTimeZone`; `FiscalApiClient client = FiscalApiClient.create(settings);`. +- Emitir: `ApiResponse response = client.getInvoiceService().create(invoice);`. `TaxCredential` usa `setFileType(0)` y `setFileType(1)`. +- Los importes, cantidades y tasas son `BigDecimal` (`new BigDecimal("0.160000")`); los ejemplos del README que pasan `double` no compilan. +- Respuesta: `response.isSucceeded()`, `response.getData().getId()`, `response.getData().getUuid()`. From 40102b6cf9e73d0d5e05f49e0695dc1b7ac97ce6 Mon Sep 17 00:00:00 2001 From: mendozagit Date: Thu, 15 Oct 2026 12:35:19 -0600 Subject: [PATCH 26/44] Tablero: SK-02 con su hash --- docs/ai-integracion/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/ai-integracion/README.md b/docs/ai-integracion/README.md index 44480e4..6ff2936 100644 --- a/docs/ai-integracion/README.md +++ b/docs/ai-integracion/README.md @@ -48,7 +48,7 @@ La siguiente tarea (global o de un área) es la primera fila `pendiente` (de esa | DOC-04 | Generar la página de errores de Comercio Exterior | docs | — | hecho | fapi-docs@73d326f, fapi-reusables@3be8d36 | | SK-01 | Armar el esqueleto del repo, los manifiestos y el CI de enlaces | skills | — | hecho | fiscalapi-ai@6caa2c4 | | SK-10 | Validar en el check semanal los enlaces "Docs:" del OpenAPI | skills | SK-01, BE-05 | hecho | fiscalapi-ai@b38786b | -| SK-02 | Escribir `fiscalapi-integracion` y `primer-timbre` | skills | SK-01, DOC-01 | hecho | | +| SK-02 | Escribir `fiscalapi-integracion` y `primer-timbre` | skills | SK-01, DOC-01 | hecho | fiscalapi-ai@05135bf | | SK-03 | Escribir `fiscalapi-diagnostico` y `explicar-error` | skills | SK-01, DOC-02 | hecho | fiscalapi-ai@bce6b59 | | SK-04 | Escribir `fiscalapi-ingreso-egreso` | skills | SK-02, MED-01 | pendiente | | | SK-05 | Escribir `fiscalapi-pagos` | skills | SK-02, MED-01 | pendiente | | From 1bcaf7d7a825655f6e203e4cb5790b05c724a8df Mon Sep 17 00:00:00 2001 From: mendozagit Date: Thu, 15 Oct 2026 12:48:54 -0600 Subject: [PATCH 27/44] SK-04: skill fiscalapi-ingreso-egreso MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Decisiones de ingreso, nota de crédito y factura global: claves del SAT por la API de catálogos (nombres reales de catálogo y /key/), régimen y uso CFDI del receptor (CFDI40158/40161), objeto de impuesto e impuestos del concepto, PUE o PPD con forma 99, relación 01/03/07 de la nota de crédito y la información global (616, S01, CP de expedición, año y bimestral). Cada decisión enlaza su sección .md. Evidencia (back local, PAC de pruebas, plugin con claude -p): - ingreso e84b01e8-88bd-4108-b9f5-5953eea761c5, nota de crédito fb6129f0-9c95-4c25-9b72-d226decc4094 (XML con CfdiRelacionados 01 al ingreso), global c4405f7f-dec2-4c66-b513-0e52152ec60e (InformacionGlobal 04/10/2026); estatus consultado con POST /invoices/status (simulado). - "factura global" dispara fiscalapi-ingreso-egreso sin skills del usuario. - check-skills contra el build local de fapi-docs: 154 enlaces, 0 hallazgos. --- docs/ai-integracion/README.md | 2 +- docs/ai-integracion/skills.md | 3 + skills/fiscalapi-ingreso-egreso/SKILL.md | 75 ++++++++++++++++++++++++ 3 files changed, 79 insertions(+), 1 deletion(-) create mode 100644 skills/fiscalapi-ingreso-egreso/SKILL.md diff --git a/docs/ai-integracion/README.md b/docs/ai-integracion/README.md index 6ff2936..50a7594 100644 --- a/docs/ai-integracion/README.md +++ b/docs/ai-integracion/README.md @@ -50,7 +50,7 @@ La siguiente tarea (global o de un área) es la primera fila `pendiente` (de esa | SK-10 | Validar en el check semanal los enlaces "Docs:" del OpenAPI | skills | SK-01, BE-05 | hecho | fiscalapi-ai@b38786b | | SK-02 | Escribir `fiscalapi-integracion` y `primer-timbre` | skills | SK-01, DOC-01 | hecho | fiscalapi-ai@05135bf | | SK-03 | Escribir `fiscalapi-diagnostico` y `explicar-error` | skills | SK-01, DOC-02 | hecho | fiscalapi-ai@bce6b59 | -| SK-04 | Escribir `fiscalapi-ingreso-egreso` | skills | SK-02, MED-01 | pendiente | | +| SK-04 | Escribir `fiscalapi-ingreso-egreso` | skills | SK-02, MED-01 | hecho | | | SK-05 | Escribir `fiscalapi-pagos` | skills | SK-02, MED-01 | pendiente | | | SK-06 | Escribir `fiscalapi-ciclo-de-vida` y `revisar-integracion` | skills | SK-02 | pendiente | | | SK-07 | Escribir `fiscalapi-nomina` | skills | SK-02, MED-01 | pendiente | | diff --git a/docs/ai-integracion/skills.md b/docs/ai-integracion/skills.md index 3be179d..937ca92 100644 --- a/docs/ai-integracion/skills.md +++ b/docs/ai-integracion/skills.md @@ -99,6 +99,9 @@ Todo va en fiscalapi-ai, en la rama `feat/ai-integracion`. Reglas comunes a SK-0 **Pruebas focalizadas:** ninguna (no aplica). **Fuera de alcance:** — **Notas:** +- Timbrado en el back local (PAC de pruebas) con el plugin. Ingreso PUE `e84b01e8-…`. Nota de crédito `fb6129f0-…`: el XML trae `TipoRelacion="01"` al ingreso. Global mensual `c4405f7f-…` (XAXX010101000, 616, S01, CP igual al de expedición). Estatus consultado (simulado fuera de Production). +- Sin las skills del usuario (`--setting-sources project,local`), "factura global" dispara `fiscalapi-ingreso-egreso`. Con ellas, el caso global cargó `fapi-cfdi-rules`, una skill personal: las pruebas de disparo van aisladas. +- Lo que el back no valida antes del PAC: PPD con forma distinta de 99, y periodicidad 05 o meses 13-18 en la global; además, con `taxObjectCode` 06 u 08 descarta `itemTaxes`. `GET invoices/{id}?details=true` no devuelve `relatedInvoices`, aunque el XML sí los trae. La docs (`invoices-by-value:5943`) etiqueta `E` como "Ingreso". ### SK-05 — Escribir `fiscalapi-pagos` **Objetivo:** que el agente emita un REP 2.0 correcto para facturas PPD y parcialidades. diff --git a/skills/fiscalapi-ingreso-egreso/SKILL.md b/skills/fiscalapi-ingreso-egreso/SKILL.md new file mode 100644 index 0000000..d04331d --- /dev/null +++ b/skills/fiscalapi-ingreso-egreso/SKILL.md @@ -0,0 +1,75 @@ +--- +name: fiscalapi-ingreso-egreso +description: Arma con FiscalAPI una factura de ingreso, una nota de crédito (egreso) o una factura global CFDI 4.0 sin rechazos del SAT. Decide régimen fiscal y uso CFDI del receptor, objeto de impuesto e impuestos del concepto, método y forma de pago (PUE o PPD), la relación de la nota de crédito y la información global de público en general, y busca las claves del SAT con la API de catálogos. Úsala cuando vayas a emitir o corregir una factura de ingreso, egreso o global. Para el complemento de pago usa fiscalapi-pagos; para un error que ya devolvió la API, fiscalapi-diagnostico. +--- + +# Ingreso, nota de crédito y factura global + +Las tres se emiten con `POST /api/v4/invoices`; cambian `typeCode`, el receptor, `relatedInvoices` y `globalInformation`. Antes de escribir el cuerpo, lee la sección del caso (por valores; cambia `invoices-by-value` por `invoices-by-reference` si usas referencias): + +| Caso | Sección | +|---|---| +| Factura de ingreso | [Crear factura ingreso](https://docs.fiscalapi.com/invoices-by-value/crear-factura-ingreso.md) | +| Nota de crédito (egreso) | [Crear nota de crédito](https://docs.fiscalapi.com/invoices-by-value/crear-nota-de-credito.md) | +| Factura global (público en general) | [Crear factura global](https://docs.fiscalapi.com/invoices-by-value/crear-factura-global.md) y [Aspectos clave de la factura global](https://docs.fiscalapi.com/global-invoice-info/aspectos-clave-de-la-factura-global.md) | +| Todos los campos | [Modelo factura](https://docs.fiscalapi.com/invoices-by-value/modelo-factura.md) | + +El ambiente, las llaves y el SDK están en la skill `fiscalapi-integracion`. + +## 1. Claves del SAT: búscalas, no las inventes + +Cada `...Code` es una clave de un catálogo del SAT ([equivalencias](https://docs.fiscalapi.com/catalogs/catalogos-de-facturacion.md)). Con la API de pruebas: + +- Buscar por descripción (mínimo 4 caracteres): `GET /api/v4/catalogs/{catalogo}/{texto}` ([Buscar en un catálogo](https://docs.fiscalapi.com/catalogs/buscar-en-un-catalogo.md)). +- Confirmar una clave: `GET /api/v4/catalogs/{catalogo}/key/{clave}` ([Obtener registro](https://docs.fiscalapi.com/catalogs/obtener-registro-por-id.md)); 404 si no existe. +- El nombre del catálogo distingue mayúsculas: `SatProductCodes` (producto o servicio), `SatUnitMeasurements` (unidad), `SatTaxRegimes`, `SatCfdiUses`, `SatPaymentForms`, `SatPaymentMethods`, `SatTaxObjects`, `SatPeriodicities`, `SatMonths`, `SatRelationshipTypes`. + +Una clave que no existe responde 400 en su campo antes de llegar al PAC ([Claves de catálogo del SAT](https://docs.fiscalapi.com/errors/claves-de-catalogo-del-sat.md)). + +## 2. Receptor: régimen fiscal y uso CFDI + +1. El tipo de persona sale del RFC: 12 caracteres es persona moral, 13 es física. +2. `recipient.taxRegimeCode` debe aplicar a ese tipo de persona ([regímenes](https://docs.fiscalapi.com/catalogs-info/cfdi-40/regimenes-fiscales.md)): 601 solo moral; 612 y 616 solo física; 626 ambas. Si no, CFDI40158. +3. `recipient.cfdiUseCode` debe aplicar al tipo de persona y admitir el régimen del receptor ([usos de CFDI](https://docs.fiscalapi.com/catalogs-info/cfdi-40/usos-de-cfdi.md)). Si no, CFDI40161. G01 y G03 no admiten 616 ni 605; S01 y CP01 admiten cualquier régimen. +4. `recipient.zipCode` es el código postal del domicilio fiscal del receptor, 5 dígitos, el de su constancia. + +FiscalAPI valida 2 y 3 antes del PAC y responde 400 en `Recipient.TaxRegimeCode` o `Recipient.CfdiUseCode` ([Mensajes de validación del timbrado](https://docs.fiscalapi.com/errors/mensajes-de-validacion-del-timbrado.md)). Pide los datos fiscales al cliente; no los deduzcas. + +## 3. Conceptos e impuestos + +- `itemCode` es la clave de producto o servicio (8 caracteres) y `unitOfMeasurementCode` la de unidad; búscalas en el catálogo. Por valores, `itemSku` es obligatorio. +- `taxObjectCode` ([objeto de impuesto](https://docs.fiscalapi.com/catalogs-info/cfdi-40/objeto-imp.md)): + - `02` (sí objeto de impuesto) exige al menos un impuesto en `itemTaxes`; sin él, FiscalAPI rechaza el concepto. + - `01`, `03` y `04` no llevan impuestos: FiscalAPI ignora `itemTaxes` con esos valores. + - `07` exige IEPS (`003`) y no admite IVA. +- Cada impuesto: `taxCode` (`001` ISR, `002` IVA, `003` IEPS), `taxTypeCode` (`Tasa`, `Cuota` o `Exento`), `taxRate` y `taxFlagCode` (`T` traslado, `R` retención). Una retención no puede ser `Exento`. +- Decimales: `quantity`, `unitPrice`, `discount` y `taxRate` admiten hasta 6; FiscalAPI no redondea ([Decimales](https://docs.fiscalapi.com/errors/decimales-de-importes-cantidades-y-tasas.md)). En JavaScript, redondea antes de enviar: `0.1 + 0.2` llega como `0.30000000000000004` y se rechaza. + +## 4. Método y forma de pago + +- **PUE** (pago en una sola exhibición): ya se pagó o se paga al emitir. `paymentFormCode` es la forma real (`01` efectivo, `03` transferencia, `04` tarjeta de crédito, ...). +- **PPD** (pago en parcialidades o diferido): todavía no se paga. `paymentFormCode` es `99` (por definir), y cada pago posterior se registra con un complemento de pago (skill `fiscalapi-pagos`). El PAC rechaza PPD con otra forma (CFDI40105) y FiscalAPI no lo valida antes: revísalo tú. +- Catálogos: [formas de pago](https://docs.fiscalapi.com/catalogs-info/cfdi-40/formas-de-pago.md) y [métodos de pago](https://docs.fiscalapi.com/catalogs-info/cfdi-40/metodos-de-pago.md). + +## 5. Nota de crédito (egreso) + +- `typeCode` `E` y `relatedInvoices: [{ "relationshipTypeCode": "...", "uuid": "" }]`; todos los elementos llevan el mismo tipo de relación ([tipos de relación](https://docs.fiscalapi.com/catalogs-info/cfdi-40/tipo-relacion.md)). + - `01`: descuento o bonificación sobre la factura relacionada. + - `03`: devolución de mercancía. + - `07`: aplicación de anticipo (con forma de pago `30`). +- Método `PUE`; la forma de pago es la del reembolso o descuento (`15` condonación si el ingreso aún no se pagó). +- Concepto típico: `itemCode` `84111506` (servicios de facturación), unidad `ACT`, cantidad 1. +- Primero emite y guarda el `uuid` del ingreso: la nota de crédito no existe sin él. + +## 6. Factura global (público en general) + +- Receptor: `tin` `XAXX010101000`, `legalName` `PUBLICO EN GENERAL`, `taxRegimeCode` `616`, `cfdiUseCode` `S01` y `zipCode` igual a `expeditionZipCode`. Con el RFC genérico, otro régimen da CFDI40159 y otro código postal CFDI40149. +- `globalInformation` es obligatoria en un ingreso a público en general (CFDI40130): `periodicityCode` (`01` diario, `02` semanal, `03` quincenal, `04` mensual, `05` bimestral), `monthCode` y `year` ([periodo de facturación](https://docs.fiscalapi.com/global-invoice-info/periodo-de-facturacion.md)). + - `year` es el de la fecha de la factura o hasta 5 años antes (CFDI40136). + - `monthCode` va de `01` a `12`; solo con `05` (bimestral, emisor en régimen 621) va de `13` a `18`. FiscalAPI no valida esta combinación antes del PAC (CFDI40132, CFDI40134, CFDI40135). +- Método `PUE`. Conceptos: `itemCode` `01010101`, unidad `ACT`, cantidad 1, un concepto por venta o ticket con su folio en `itemSku`. +- Si las ventas son tickets de Auto CFDI, usa [Crear factura global de tickets](https://docs.fiscalapi.com/tickets/crear-factura-global-de-tickets.md). + +## 7. Después de emitir + +Guarda `id` y `uuid`. Para consultar el estatus ante el SAT, cancelar o descargar el PDF y el XML, usa la skill `fiscalapi-ciclo-de-vida`. En el ambiente de pruebas el estatus del SAT se simula. From 6cf3f31eb2437c1e5f572f0c8f0b3144c3286a13 Mon Sep 17 00:00:00 2001 From: mendozagit Date: Thu, 15 Oct 2026 12:48:54 -0600 Subject: [PATCH 28/44] Tablero: SK-04 con su hash --- docs/ai-integracion/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/ai-integracion/README.md b/docs/ai-integracion/README.md index 50a7594..c43ca7c 100644 --- a/docs/ai-integracion/README.md +++ b/docs/ai-integracion/README.md @@ -50,7 +50,7 @@ La siguiente tarea (global o de un área) es la primera fila `pendiente` (de esa | SK-10 | Validar en el check semanal los enlaces "Docs:" del OpenAPI | skills | SK-01, BE-05 | hecho | fiscalapi-ai@b38786b | | SK-02 | Escribir `fiscalapi-integracion` y `primer-timbre` | skills | SK-01, DOC-01 | hecho | fiscalapi-ai@05135bf | | SK-03 | Escribir `fiscalapi-diagnostico` y `explicar-error` | skills | SK-01, DOC-02 | hecho | fiscalapi-ai@bce6b59 | -| SK-04 | Escribir `fiscalapi-ingreso-egreso` | skills | SK-02, MED-01 | hecho | | +| SK-04 | Escribir `fiscalapi-ingreso-egreso` | skills | SK-02, MED-01 | hecho | fiscalapi-ai@1bcaf7d | | SK-05 | Escribir `fiscalapi-pagos` | skills | SK-02, MED-01 | pendiente | | | SK-06 | Escribir `fiscalapi-ciclo-de-vida` y `revisar-integracion` | skills | SK-02 | pendiente | | | SK-07 | Escribir `fiscalapi-nomina` | skills | SK-02, MED-01 | pendiente | | From 557c1f60bfb7ab172e06366d2a1f23165018eb17 Mon Sep 17 00:00:00 2001 From: mendozagit Date: Thu, 15 Oct 2026 13:01:00 -0600 Subject: [PATCH 29/44] SK-05: skill fiscalapi-pagos MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Complemento de recepción de pagos 2.0: cuándo hace falta (PPD), el comprobante P (XXX, sin forma ni método en la raíz), complement.payment y paidInvoices (parcialidad, saldo anterior, pagado e insoluto, equivalencia), impuestos con base proporcional y los errores CRP más probables, con sus secciones .md. Evidencia (back local, PAC de pruebas, plugin sin skills del usuario): - PPD 99b472e9-6449-42a9-a6b8-4efe6bb13ca2 y pago 60fc947f-66de-4d9c-ab12-ea5481106219 con DoctoRelacionado a la PPD, NumParcialidad 1, 2320/1160/1160 y BaseDR 1000; estatus consultado (simulado). - check-skills contra el build local de fapi-docs: 156 enlaces, 0 hallazgos. Defecto encontrado y documentado: fapi-back@c9d1baff (FechaPago en UTC). --- docs/ai-integracion/README.md | 2 +- docs/ai-integracion/skills.md | 3 ++ skills/fiscalapi-pagos/SKILL.md | 58 +++++++++++++++++++++++++++++++++ 3 files changed, 62 insertions(+), 1 deletion(-) create mode 100644 skills/fiscalapi-pagos/SKILL.md diff --git a/docs/ai-integracion/README.md b/docs/ai-integracion/README.md index c43ca7c..dcdeede 100644 --- a/docs/ai-integracion/README.md +++ b/docs/ai-integracion/README.md @@ -51,7 +51,7 @@ La siguiente tarea (global o de un área) es la primera fila `pendiente` (de esa | SK-02 | Escribir `fiscalapi-integracion` y `primer-timbre` | skills | SK-01, DOC-01 | hecho | fiscalapi-ai@05135bf | | SK-03 | Escribir `fiscalapi-diagnostico` y `explicar-error` | skills | SK-01, DOC-02 | hecho | fiscalapi-ai@bce6b59 | | SK-04 | Escribir `fiscalapi-ingreso-egreso` | skills | SK-02, MED-01 | hecho | fiscalapi-ai@1bcaf7d | -| SK-05 | Escribir `fiscalapi-pagos` | skills | SK-02, MED-01 | pendiente | | +| SK-05 | Escribir `fiscalapi-pagos` | skills | SK-02, MED-01 | hecho | | | SK-06 | Escribir `fiscalapi-ciclo-de-vida` y `revisar-integracion` | skills | SK-02 | pendiente | | | SK-07 | Escribir `fiscalapi-nomina` | skills | SK-02, MED-01 | pendiente | | | SK-08 | Escribir `fiscalapi-carta-porte` | skills | SK-02, MED-01 | pendiente | | diff --git a/docs/ai-integracion/skills.md b/docs/ai-integracion/skills.md index 937ca92..540451a 100644 --- a/docs/ai-integracion/skills.md +++ b/docs/ai-integracion/skills.md @@ -116,6 +116,9 @@ Todo va en fiscalapi-ai, en la rama `feat/ai-integracion`. Reglas comunes a SK-0 **Pruebas focalizadas:** ninguna (no aplica). **Fuera de alcance:** — **Notas:** +- Timbrado con el plugin, sin skills del usuario. PPD `99b472e9-…` (PPD/99, total 2320). Pago `60fc947f-…`: tipo P, XXX, CP01, sin forma ni método en la raíz; DoctoRelacionado a la PPD, parcialidad 1 (2320 → 1160 → 1160) y BaseDR proporcional de 1000. Estatus consultado (simulado). +- Defecto encontrado: `FechaPago` sale en UTC (18:58) mientras `Fecha` sale en hora local (12:58). Quedó en `known-issues.md` de fapi-back (`fapi-back@c9d1baff`), con prioridad alta. +- FiscalAPI no busca la factura PPD ni calcula saldos, y no rechaza forma o método de pago en la raíz de un P (lo hace el PAC). Las docs llaman `amount` al importe pagado del documento, pero el campo real es `paymentAmount`. ### SK-06 — Escribir `fiscalapi-ciclo-de-vida` y `revisar-integracion` **Objetivo:** que el agente cubra lo que pasa después de timbrar (cancelar, consultar estado, XML y PDF, correo y webhooks) y revise una integración existente. diff --git a/skills/fiscalapi-pagos/SKILL.md b/skills/fiscalapi-pagos/SKILL.md new file mode 100644 index 0000000..c6cbce4 --- /dev/null +++ b/skills/fiscalapi-pagos/SKILL.md @@ -0,0 +1,58 @@ +--- +name: fiscalapi-pagos +description: Emite con FiscalAPI el complemento de recepción de pagos 2.0 (REP, factura de pago tipo P) de una factura PPD, en una o varias parcialidades. Decide cuándo hace falta (PUE frente a PPD), arma complement.payment con saldo anterior, importe pagado y saldo insoluto, el número de parcialidad, el tipo de cambio y la equivalencia cuando las monedas difieren, y los impuestos del documento pagado. Úsala cuando un cliente pague una factura emitida con método PPD o cuando te pidan un complemento de pago o un REP. Para la factura original usa fiscalapi-ingreso-egreso; para un error CRP que ya devolvió la API, fiscalapi-diagnostico. +--- + +# Complemento de pago (Pagos 2.0) + +Una factura **PUE** ya está pagada: no lleva complemento. Una factura **PPD** (forma de pago `99`) se paga después, y cada pago se registra con un CFDI de tipo `P` que relaciona el UUID de la factura. El SAT pide emitirlo a más tardar el día 5 del mes siguiente al pago. + +Antes de escribir el cuerpo, lee [Crear factura de pago](https://docs.fiscalapi.com/invoices-by-value/crear-factura-de-pago.md) (por referencias: [esta sección](https://docs.fiscalapi.com/invoices-by-reference/crear-factura-de-pago.md)). Los campos de `complement.payment` están en [Modelo factura](https://docs.fiscalapi.com/invoices-by-value/modelo-factura.md). El ambiente y el SDK, en la skill `fiscalapi-integracion`. + +## 1. El comprobante de pago + +Se emite con `POST /api/v4/invoices`. Manda: + +- `typeCode` `P`, `currencyCode` `XXX` (cualquier otra responde 400), `exchangeRate` 1 u omitido, `exportCode` `01`, `series`, `date`, `expeditionZipCode`, `issuer` y `recipient` (el mismo receptor de la factura). +- `complement.payment` con el pago (sección 2). + +**No mandes** `paymentFormCode`, `paymentMethodCode` ni `paymentConditions` en la raíz: el CFDI de pago no los lleva y el PAC lo rechaza (CRP20105, CRP20106, CRP20107). La forma de pago va dentro de `complement.payment`. Tampoco mandes `items`: FiscalAPI arma solo el concepto (84111506, ACT, "Pago", importe 0), fija el uso CFDI en `CP01` y calcula los totales del pago. + +## 2. El pago y los documentos que paga + +`complement.payment`: + +| Campo | Qué es | +|---|---| +| `paymentDate` | Fecha y hora en que se recibió el pago (sin zona, `yyyy-MM-ddTHH:mm:ss`) | +| `paymentFormCode` | Forma real del pago (`03` transferencia, `01` efectivo, ...); nunca `99` (CRP20212) | +| `currencyCode` y `exchangeRate` | Moneda del pago y su tipo de cambio: con `MXN` exactamente 1 | +| `amount` | Monto total recibido | +| `paidInvoices` | Una entrada por factura que cubre este pago | + +Cada `paidInvoices[]`: + +- `uuid` de la factura PPD; `series` y `number` son opcionales. +- `currencyCode` de la factura y `equivalence`: 1 si es la misma moneda del pago; si no, las unidades de la moneda de la factura por cada unidad de la del pago. +- `partialityNumber`: 1 en el primer pago de esa factura y consecutivo en los siguientes. +- `previousBalance`: en la primera parcialidad, el total de la factura; después, el saldo insoluto del pago anterior. Mayor que 0. +- `paymentAmount`: lo que este pago abona a esa factura. Mayor que 0. (La tabla de las docs lo llama `amount`; el campo real es `paymentAmount`.) +- `remainingBalance` = `previousBalance` − `paymentAmount`, nunca negativo (CRP20244). +- `taxObjectCode` y `paidInvoiceTaxes`: ver la sección 3. + +**FiscalAPI no busca la factura ni calcula saldos.** Copia `uuid`, `partialityNumber` y los saldos tal como llegan, y el PAC rechaza lo incongruente. El sistema del integrador lleva el saldo y el número de parcialidad de cada factura PPD: guárdalos al emitir cada pago. Con la misma moneda, la suma de los `paymentAmount` no puede pasar de `amount` (CRP20217). + +## 3. Impuestos del documento pagado + +- `taxObjectCode` repite el objeto de impuesto de la factura: con `02`, `paidInvoiceTaxes` es obligatorio (CRP20246); con `01`, `03` o `04` no debe venir (CRP20247). +- Cada impuesto lleva `base`, `taxCode`, `taxTypeCode`, `taxRate` y `taxFlagCode` como en la factura. La `base` es **proporcional a lo pagado**: si se paga la mitad de una factura de 1,000 + IVA, la base del IVA es 500. FiscalAPI calcula el importe de cada impuesto y los totales del pago a partir de esa base. +- Decimales: hasta 6 en importes y tasas, 10 en `equivalence`; FiscalAPI no redondea. + +## 4. Antes de timbrar, revisa + +1. La factura es PPD y tienes su `uuid`, su total y los pagos previos. +2. `partialityNumber` y `previousBalance` siguen al pago anterior; `remainingBalance` cuadra. +3. La forma del pago no es `99` y, si mandas cuentas o RFC bancarios, la forma los admite (CRP20221 a CRP20228). +4. No hay `paymentFormCode` ni `paymentMethodCode` en la raíz. + +Si el PAC rechaza, busca el código en [Errores Pagos 2.0](https://docs.fiscalapi.com/errors/pagos-20/errores-pagos-2-0.md) o usa la skill `fiscalapi-diagnostico`. Para consultar el estatus del pago ante el SAT o cancelarlo, usa la skill `fiscalapi-ciclo-de-vida`. From 57295b49e59ab7ce83f6b4effc9e36a5e66064cf Mon Sep 17 00:00:00 2001 From: mendozagit Date: Thu, 15 Oct 2026 13:01:00 -0600 Subject: [PATCH 30/44] Tablero: SK-05 con su hash --- docs/ai-integracion/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/ai-integracion/README.md b/docs/ai-integracion/README.md index dcdeede..3a61742 100644 --- a/docs/ai-integracion/README.md +++ b/docs/ai-integracion/README.md @@ -51,7 +51,7 @@ La siguiente tarea (global o de un área) es la primera fila `pendiente` (de esa | SK-02 | Escribir `fiscalapi-integracion` y `primer-timbre` | skills | SK-01, DOC-01 | hecho | fiscalapi-ai@05135bf | | SK-03 | Escribir `fiscalapi-diagnostico` y `explicar-error` | skills | SK-01, DOC-02 | hecho | fiscalapi-ai@bce6b59 | | SK-04 | Escribir `fiscalapi-ingreso-egreso` | skills | SK-02, MED-01 | hecho | fiscalapi-ai@1bcaf7d | -| SK-05 | Escribir `fiscalapi-pagos` | skills | SK-02, MED-01 | hecho | | +| SK-05 | Escribir `fiscalapi-pagos` | skills | SK-02, MED-01 | hecho | fiscalapi-ai@557c1f6 | | SK-06 | Escribir `fiscalapi-ciclo-de-vida` y `revisar-integracion` | skills | SK-02 | pendiente | | | SK-07 | Escribir `fiscalapi-nomina` | skills | SK-02, MED-01 | pendiente | | | SK-08 | Escribir `fiscalapi-carta-porte` | skills | SK-02, MED-01 | pendiente | | From e5d66906985a2c70abb27541423d305e9b17a6d2 Mon Sep 17 00:00:00 2001 From: mendozagit Date: Thu, 15 Oct 2026 13:09:24 -0600 Subject: [PATCH 31/44] SK-06: skill fiscalapi-ciclo-de-vida y comando revisar-integracion MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - fiscalapi-ciclo-de-vida: cancelación (motivos 01-04, sustituta, la respuesta no confirma: Cancelando y webhooks de resultado), estatus ante el SAT, XML, PDF y correo, y webhooks (registro, eventos reales, firma Standard Webhooks sobre el cuerpo crudo, deduplicación, reintentos, rotación y fiscalapi listen). - revisar-integracion: llaves sk_live_/sk_test_/whsec_ en el código, falta de X-TIME-ZONE, reintentos a ciegas tras un 5xx y webhooks sin verificar la firma. Evidencia: - revisar-integracion (claude -p, sin skills del usuario) sobre un proyecto con 4 defectos plantados: los 4 con archivo y línea, más 2 reales adicionales. - check-skills contra el build local de fapi-docs: 165 enlaces, 0 hallazgos; claude plugin validate --strict en verde. --- commands/revisar-integracion.md | 19 ++++++++++ docs/ai-integracion/README.md | 2 +- docs/ai-integracion/skills.md | 3 ++ skills/fiscalapi-ciclo-de-vida/SKILL.md | 49 +++++++++++++++++++++++++ 4 files changed, 72 insertions(+), 1 deletion(-) create mode 100644 commands/revisar-integracion.md create mode 100644 skills/fiscalapi-ciclo-de-vida/SKILL.md diff --git a/commands/revisar-integracion.md b/commands/revisar-integracion.md new file mode 100644 index 0000000..8c1ac72 --- /dev/null +++ b/commands/revisar-integracion.md @@ -0,0 +1,19 @@ +--- +description: Revisa una integración existente con FiscalAPI y reporta los defectos que causan problemas en producción, con la línea y la corrección de cada uno. +argument-hint: "[carpeta o archivos a revisar; por omisión, todo el proyecto]" +--- + +Revisa la integración con FiscalAPI de este proyecto, siguiendo las skills `fiscalapi-integracion` y `fiscalapi-ciclo-de-vida`. + +$ARGUMENTS + +Busca en el código (excluye `node_modules`, `bin`, `obj`, `vendor` y dependencias) estos defectos, en este orden: + +1. **Llaves en el código o en archivos versionados.** Valores con forma de API key de FiscalAPI (`sk_live_`, `sk_test_` o `sk_development_` seguidos de un GUID con guiones bajos), secretos de webhook (`whsec_`), o una API key o tenant escritos como texto en lugar de leerse del entorno o de un gestor de secretos. Revisa también `.env` versionados y archivos de configuración. Una llave `sk_live_` es crítica: indica rotarla en el dashboard. +2. **Peticiones sin `X-TIME-ZONE`.** Llamadas HTTP directas a FiscalAPI que mandan `X-API-KEY` pero no `X-TIME-ZONE`, o un cliente del SDK sin zona horaria cuando el sistema no trabaja en hora de México. +3. **Reintentos a ciegas tras un 5xx o un timeout al timbrar.** Un ciclo de reintento, una librería de retry o un `catch` que vuelve a llamar `POST /api/v4/invoices` (o `invoices.create`) sin consultar antes si la factura quedó timbrada. Un 5xx o un timeout al timbrar no se reintenta. +4. **Webhooks sin verificar la firma.** Un endpoint que recibe eventos de FiscalAPI y procesa el cuerpo sin verificar `webhook-signature` con el secreto, o que verifica sobre el JSON ya parseado en lugar del cuerpo crudo. + +Además, si los ves, repórtalos: la URL de producción (`live.fiscalapi.com`) en código de pruebas, `succeeded` sin revisar en la respuesta, y una cancelación marcada como definitiva con la sola respuesta del DELETE. + +Responde con una tabla: defecto, archivo y línea, por qué es un problema (con el enlace a la sección de las docs que aplique) y la corrección concreta. Si no encuentras un defecto de la lista, dilo explícitamente. No modifiques archivos: solo reporta. diff --git a/docs/ai-integracion/README.md b/docs/ai-integracion/README.md index 3a61742..39774cd 100644 --- a/docs/ai-integracion/README.md +++ b/docs/ai-integracion/README.md @@ -52,7 +52,7 @@ La siguiente tarea (global o de un área) es la primera fila `pendiente` (de esa | SK-03 | Escribir `fiscalapi-diagnostico` y `explicar-error` | skills | SK-01, DOC-02 | hecho | fiscalapi-ai@bce6b59 | | SK-04 | Escribir `fiscalapi-ingreso-egreso` | skills | SK-02, MED-01 | hecho | fiscalapi-ai@1bcaf7d | | SK-05 | Escribir `fiscalapi-pagos` | skills | SK-02, MED-01 | hecho | fiscalapi-ai@557c1f6 | -| SK-06 | Escribir `fiscalapi-ciclo-de-vida` y `revisar-integracion` | skills | SK-02 | pendiente | | +| SK-06 | Escribir `fiscalapi-ciclo-de-vida` y `revisar-integracion` | skills | SK-02 | hecho | | | SK-07 | Escribir `fiscalapi-nomina` | skills | SK-02, MED-01 | pendiente | | | SK-08 | Escribir `fiscalapi-carta-porte` | skills | SK-02, MED-01 | pendiente | | | SK-09 | Escribir `fiscalapi-comercio-exterior` | skills | SK-02, MED-01 | pendiente | | diff --git a/docs/ai-integracion/skills.md b/docs/ai-integracion/skills.md index 540451a..90f6f0b 100644 --- a/docs/ai-integracion/skills.md +++ b/docs/ai-integracion/skills.md @@ -135,6 +135,9 @@ Todo va en fiscalapi-ai, en la rama `feat/ai-integracion`. Reglas comunes a SK-0 **Pruebas focalizadas:** ninguna (no aplica). **Fuera de alcance:** descarga masiva (se evalúa con MED-01 si merece su propia skill). **Notas:** +- `revisar-integracion`, sin skills del usuario, encontró en un proyecto de prueba los 4 defectos plantados: llave `sk_live_` y tenant en `config.js`, falta de `X-TIME-ZONE`, reintento a ciegas tras un 5xx y webhook sin verificar la firma. Agregó otros 2, también reales: URL `live` en el código y `succeeded` sin revisar. +- El prefijo `sk_live_` sí es de FiscalAPI: `ApiKeyValueGenerator.cs` genera `sk_live_`, `sk_test_` o `sk_development_` más un GUID con guiones bajos. La firma de webhooks es Standard Webhooks (HMAC-SHA256 sobre el cuerpo crudo). +- Sin resolver: las docs (`webhooks/content.mdx:13`) dicen que sin `X-TIME-ZONE` las fechas de webhooks vienen en UTC con `Z`, pero el middleware usaría `America/Mexico_City`. La skill solo recomienda mandar siempre el header. ### SK-07 — Escribir `fiscalapi-nomina` **Objetivo:** que el agente emita una nómina 1.2 correcta (empleador, empleado, percepciones y deducciones). diff --git a/skills/fiscalapi-ciclo-de-vida/SKILL.md b/skills/fiscalapi-ciclo-de-vida/SKILL.md new file mode 100644 index 0000000..022fecb --- /dev/null +++ b/skills/fiscalapi-ciclo-de-vida/SKILL.md @@ -0,0 +1,49 @@ +--- +name: fiscalapi-ciclo-de-vida +description: Maneja lo que pasa con una factura CFDI de FiscalAPI después de timbrarla. Cubre la cancelación ante el SAT (motivos 01-04, UUID sustituto y aceptación del receptor), el estatus ante el SAT, la descarga del XML y del PDF, el envío por correo y los webhooks (registro, eventos, verificación de la firma, reintentos y pruebas en local con fiscalapi listen). Úsala cuando vayas a cancelar, consultar, descargar o enviar una factura ya emitida, o a recibir eventos de FiscalAPI en tu sistema. Para emitir usa fiscalapi-ingreso-egreso o fiscalapi-pagos; para revisar una integración completa, el comando revisar-integracion. +--- + +# Después de timbrar + +Cada operación tiene su sección en las docs (por valores; cambia `invoices-by-value` por `invoices-by-reference` si emitiste por referencias). Léela antes de escribir el cuerpo. El ambiente, las llaves y el SDK están en la skill `fiscalapi-integracion`. + +## 1. Cancelar + +`DELETE /api/v4/invoices` ([Cancelar factura](https://docs.fiscalapi.com/invoices-by-value/cancelar-factura.md)). + +- **Por referencias:** `id` de la factura en FiscalAPI. Firma con el CSD vigente del emisor guardado; una factura emitida por valores no se cancela por referencias. +- **Por valores:** `invoiceUuid` (el UUID, no el `id`), `tin` del emisor y `taxCredentials` con su `.cer` y su `.key` en base64. +- `cancellationReasonCode` ([motivos](https://docs.fiscalapi.com/catalogs-info/cfdi-40/motivos-de-cancelacion.md)): + - `01` comprobante emitido con errores con relación: exige `replacementUuid`, el UUID de la factura que la sustituye. Emite primero la sustituta. + - `02` comprobante emitido con errores sin relación. + - `03` no se llevó a cabo la operación. + - `04` operación nominativa relacionada en una factura global. + +**La respuesta no confirma la cancelación.** Que el PAC acepte la solicitud (201 o 202 en `invoiceUuids`) solo significa que llegó al SAT: la factura queda en "Cancelando". FiscalAPI consulta al SAT en segundo plano y la deja en "Cancelado" o la regresa a "Vigente" si el receptor la rechaza o no es cancelable. Si el SAT pide la aceptación del receptor, este tiene 3 días hábiles para responder. Entérate del resultado con los webhooks `invoice.canceled` e `invoice.cancellation_rejected`, o consultando el estatus. No marques la factura como cancelada en tu sistema con la respuesta del DELETE. + +## 2. Estatus ante el SAT + +`POST /api/v4/invoices/status` ([Consultar estado de factura](https://docs.fiscalapi.com/invoices-by-value/consultar-estado-de-factura.md)): por referencias con `id`; por valores con `issuerTin`, `recipientTin`, `invoiceTotal`, `invoiceUuid` y `last8DigitsIssuerSignature`. + +La respuesta trae `status` (Vigente, Cancelado o No Encontrado), `cancelableStatus` (sin aceptación, con aceptación o no cancelable) y `cancellationStatus` (en proceso, rechazada, plazo vencido, cancelado) ([modelo de respuesta](https://docs.fiscalapi.com/invoices-by-value/modelo-de-respuesta-a-la-consulta-del-estado-de-una-factura.md)). En el ambiente de pruebas la respuesta es simulada y `details` lo avisa: no sirve para probar lógica que dependa del valor. + +## 3. XML, PDF y correo + +- XML timbrado: `GET /api/v4/invoices/{id}/xml` ([Obtener XML](https://docs.fiscalapi.com/invoices-by-value/obtener-xml-por-id.md)). +- PDF: `POST /api/v4/invoices/pdf` con `invoiceId` y, opcionales, `base64Logo`, `bandColor` y `fontColor` ([Generar PDF](https://docs.fiscalapi.com/invoices-by-value/generar-pdf-por-id.md)). Para un diseño fijo por RFC, usa [reglas de reporte](https://docs.fiscalapi.com/report-rules-info/como-funcionan-las-reglas-de-reporte.md), que se configuran en el dashboard. +- Correo con el PDF y el XML: `POST /api/v4/invoices/send` con `invoiceId` y `toEmail` ([Enviar factura por correo](https://docs.fiscalapi.com/invoices-by-value/enviar-factura-por-correo.md)). El envío se programa y puede tardar unos minutos. + +Los archivos llegan en base64 (`base64File`) con `fileName`. Decodifícalos antes de guardarlos. + +## 4. Webhooks + +- **Registro:** `POST /api/v4/webhooks` con `url` (https, no localhost), `enabledEvents` (o `*`) y, opcional, `personIds` ([Registrar un webhook](https://docs.fiscalapi.com/webhooks-info/registrar-un-webhook.md)). La respuesta trae el `secret` (`whsec_...`): guárdalo como secreto; con la API key no se vuelve a ver salvo al rotarlo. +- **Eventos:** `invoice.cancellation_requested`, `invoice.canceled`, `invoice.cancellation_rejected`, `download_request.completed`, `download_request.abandoned` y `download_rule.suspended` ([Tipos de evento](https://docs.fiscalapi.com/webhooks-info/tipos-de-evento.md)). Un evento de una factura emitida por valores no trae persona: solo lo reciben los webhooks sin filtro `personIds`. +- **Verifica la firma de cada entrega** antes de procesarla ([Verificar la firma](https://docs.fiscalapi.com/webhooks-info/verificar-la-firma.md)): usa el helper del SDK (`Webhook.verify` y equivalentes) sobre el **cuerpo crudo** de la petición, sin parsearlo antes. La firma es Standard Webhooks (cabeceras `webhook-id`, `webhook-timestamp` y `webhook-signature`, HMAC-SHA256) y se rechaza con más de 5 minutos de diferencia. +- **Entrega al menos una vez y sin orden garantizado:** deduplica por `webhook-id`, responde 2xx rápido, procesa después, y consulta el recurso (`relatedObject`) para conocer su estado actual ([Reintentos](https://docs.fiscalapi.com/webhooks-info/reintentos.md)). Si no respondes 2xx, FiscalAPI reintenta hasta 10 veces en unas 75 horas. +- **Rotación:** al rotar el secreto, el anterior sigue firmando 24 horas y cada entrega lleva las dos firmas ([Rotar el secreto](https://docs.fiscalapi.com/webhooks-info/rotar-el-secreto.md)). +- **En local:** `fiscalapi listen --forward-to http://localhost:/` recibe los eventos de tu cuenta de pruebas y `fiscalapi trigger ` dispara uno ([Recibir eventos en tu servidor local](https://docs.fiscalapi.com/webhooks-info/local-testing/recibir-eventos-en-tu-servidor-local.md)). La escucha tiene su propio secreto. + +## 5. Fechas + +Manda siempre `X-TIME-ZONE`. Sin él, FiscalAPI usa `America/Mexico_City` para leer y escribir las fechas sin zona, y tu sistema puede interpretarlas en otra. El sobre de los eventos de webhook va siempre en UTC con `Z`. From 0bfdbdecaf8ae8c37c49e3835181eb59627eb53a Mon Sep 17 00:00:00 2001 From: mendozagit Date: Thu, 15 Oct 2026 13:09:25 -0600 Subject: [PATCH 32/44] Tablero: SK-06 con su hash --- docs/ai-integracion/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/ai-integracion/README.md b/docs/ai-integracion/README.md index 39774cd..34f8b75 100644 --- a/docs/ai-integracion/README.md +++ b/docs/ai-integracion/README.md @@ -52,7 +52,7 @@ La siguiente tarea (global o de un área) es la primera fila `pendiente` (de esa | SK-03 | Escribir `fiscalapi-diagnostico` y `explicar-error` | skills | SK-01, DOC-02 | hecho | fiscalapi-ai@bce6b59 | | SK-04 | Escribir `fiscalapi-ingreso-egreso` | skills | SK-02, MED-01 | hecho | fiscalapi-ai@1bcaf7d | | SK-05 | Escribir `fiscalapi-pagos` | skills | SK-02, MED-01 | hecho | fiscalapi-ai@557c1f6 | -| SK-06 | Escribir `fiscalapi-ciclo-de-vida` y `revisar-integracion` | skills | SK-02 | hecho | | +| SK-06 | Escribir `fiscalapi-ciclo-de-vida` y `revisar-integracion` | skills | SK-02 | hecho | fiscalapi-ai@e5d6690 | | SK-07 | Escribir `fiscalapi-nomina` | skills | SK-02, MED-01 | pendiente | | | SK-08 | Escribir `fiscalapi-carta-porte` | skills | SK-02, MED-01 | pendiente | | | SK-09 | Escribir `fiscalapi-comercio-exterior` | skills | SK-02, MED-01 | pendiente | | From 51cbb20eaab04cdad9f10c9b8912d35d7a7a38be Mon Sep 17 00:00:00 2001 From: mendozagit Date: Thu, 15 Oct 2026 13:18:57 -0600 Subject: [PATCH 33/44] SK-07: skill fiscalapi-nomina MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Recibo de nómina 1.2: comprobante N (sin forma de pago, receptor 605/CN01), datos de empleado y empleador por referencias o por valores, reglas de contrato, régimen, registro patronal y antigüedad, complement.payroll con percepciones, deducciones, otros pagos e incapacidades, subsidio para el empleo y los NOM más comunes, con sus secciones .md. Evidencia (back local, PAC de pruebas, plugin sin skills del usuario): - nómina ordinaria a153d832-01aa-48fd-aa7e-af8a1d1d76d4: XML sin FormaPago, 605/CN01, RegistroPatronal, P112W, TotalImpuestosRetenidos 520, OtroPago 002; estatus consultado (simulado). - check-skills contra el build local de fapi-docs: 175 enlaces, 0 hallazgos. --- docs/ai-integracion/README.md | 2 +- docs/ai-integracion/skills.md | 3 ++ skills/fiscalapi-nomina/SKILL.md | 58 ++++++++++++++++++++++++++++++++ 3 files changed, 62 insertions(+), 1 deletion(-) create mode 100644 skills/fiscalapi-nomina/SKILL.md diff --git a/docs/ai-integracion/README.md b/docs/ai-integracion/README.md index 34f8b75..2ae588a 100644 --- a/docs/ai-integracion/README.md +++ b/docs/ai-integracion/README.md @@ -53,7 +53,7 @@ La siguiente tarea (global o de un área) es la primera fila `pendiente` (de esa | SK-04 | Escribir `fiscalapi-ingreso-egreso` | skills | SK-02, MED-01 | hecho | fiscalapi-ai@1bcaf7d | | SK-05 | Escribir `fiscalapi-pagos` | skills | SK-02, MED-01 | hecho | fiscalapi-ai@557c1f6 | | SK-06 | Escribir `fiscalapi-ciclo-de-vida` y `revisar-integracion` | skills | SK-02 | hecho | fiscalapi-ai@e5d6690 | -| SK-07 | Escribir `fiscalapi-nomina` | skills | SK-02, MED-01 | pendiente | | +| SK-07 | Escribir `fiscalapi-nomina` | skills | SK-02, MED-01 | hecho | | | SK-08 | Escribir `fiscalapi-carta-porte` | skills | SK-02, MED-01 | pendiente | | | SK-09 | Escribir `fiscalapi-comercio-exterior` | skills | SK-02, MED-01 | pendiente | | | MCP-01 | Crear el paquete stdio con `listar_operaciones` y `obtener_operacion` | mcp | SK-01, BE-03 | pendiente | | diff --git a/docs/ai-integracion/skills.md b/docs/ai-integracion/skills.md index 90f6f0b..5d8b36b 100644 --- a/docs/ai-integracion/skills.md +++ b/docs/ai-integracion/skills.md @@ -152,6 +152,9 @@ Todo va en fiscalapi-ai, en la rama `feat/ai-integracion`. Reglas comunes a SK-0 **Pruebas focalizadas:** ninguna (no aplica). **Fuera de alcance:** — **Notas:** +- Timbrado con el plugin, sin skills del usuario: nómina ordinaria quincenal `a153d832-…`, de EKU9003173C9 (registro patronal B5510768108) a FUNK671228PH6. El XML no trae `FormaPago`; trae receptor 605/CN01, contrato 01 con régimen 02, `P112W`, ISR 520 en impuestos retenidos y subsidio 002 en 0. Estatus consultado (simulado). +- Riesgo del back: no limpia `paymentFormCode` en N (`ComprobanteBuilder.cs:15`) y no fuerza 605/CN01, así que la skill pide no mandar el primero y sí mandar los dos últimos. Los ejemplos de C# y Python de las docs usan 601/G01 y `PaymentFormCode`, y el cURL "por referencias" usa datos por valores. +- Las guías `md-version` de nómina en sat-docs están vacías: las reglas salen del PDF y de la matriz `errors/nomina-12`. ### SK-08 — Escribir `fiscalapi-carta-porte` **Objetivo:** que el agente emita una Carta Porte 3.1 correcta (ubicaciones, mercancías y autotransporte). diff --git a/skills/fiscalapi-nomina/SKILL.md b/skills/fiscalapi-nomina/SKILL.md new file mode 100644 index 0000000..3c3b5c6 --- /dev/null +++ b/skills/fiscalapi-nomina/SKILL.md @@ -0,0 +1,58 @@ +--- +name: fiscalapi-nomina +description: Emite con FiscalAPI un recibo de nómina CFDI 4.0 con complemento de nómina 1.2 (factura tipo N), ordinaria o extraordinaria. Decide los datos del empleado y del empleador (por referencias en la persona o por valores en el request), el tipo de nómina, el periodo y los días pagados, las percepciones, las deducciones (ISR, IMSS), los otros pagos con el subsidio para el empleo y las incapacidades, y evita los rechazos NOM más comunes. Úsala cuando vayas a timbrar nómina o recibos de pago de sueldos, o a cargar los datos laborales de empleados y empleadores. Para un error NOM que ya devolvió la API, usa fiscalapi-diagnostico. +--- + +# Recibo de nómina 1.2 + +Antes de escribir el cuerpo, lee [Crear factura de nómina](https://docs.fiscalapi.com/invoices-by-value/crear-factura-de-nomina.md) (por referencias: [esta sección](https://docs.fiscalapi.com/invoices-by-reference/crear-factura-de-nomina.md)). El ambiente, las llaves y el SDK están en la skill `fiscalapi-integracion`. Ojo con los ejemplos de C# y Python de esa página: usan un receptor 601/G01 y `PaymentFormCode`, que la nómina no admite; sigue esta skill. + +## 1. El comprobante + +`POST /api/v4/invoices` con: + +- `typeCode` `N`, `currencyCode` `MXN`, `exportCode` `01`, `paymentMethodCode` `PUE`, `series`, `date` y `expeditionZipCode`. +- **No mandes** `paymentFormCode` (el CFDI de nómina no lleva forma de pago y FiscalAPI lo copiaría al XML), `items`, `paymentConditions` ni `exchangeRate`. FiscalAPI arma solo el concepto (84111505, ACT, "Pago de nómina") con importe = percepciones + otros pagos y descuento = deducciones. +- **Emisor** (empleador): si es persona física, necesita su CURP (NOM5); una persona moral no la lleva. +- **Receptor** (empleado): RFC de persona física (13 caracteres, NOM8), `taxRegimeCode` `605` (sueldos y salarios, NOM11) y `cfdiUseCode` `CN01` (nómina, NOM12). FiscalAPI no los fuerza: mándalos. + +## 2. Datos laborales: por referencias o por valores + +- **Por referencias:** registra una vez los [datos de empleado](https://docs.fiscalapi.com/employee-data/crear-datos-de-empleado.md) (`POST /api/v4/people/{personId}/employee`) y de [empleador](https://docs.fiscalapi.com/employer-data/crear-datos-de-empleador.md) (`POST /api/v4/people/{personId}/employer`), y emite con `issuer.id` y `recipient.id`. La CURP sale de la persona ([emisor en nómina](https://docs.fiscalapi.com/issuers/emisor-en-facturas-de-nomina.md), [receptor en nómina](https://docs.fiscalapi.com/recipients/receptor-en-facturas-de-nomina.md)). Cada persona tiene un solo registro de cada tipo: si ya existe, actualízalo con PUT. +- **Por valores:** los mismos datos van en `issuer.employerData` y `recipient.employeeData`, con la `curp` dentro. + +Datos del empleado que más fallan: + +- Obligatorios: `curp`, `employeeNumber` (1-15 caracteres), `satContractTypeId`, `satTaxRegimeTypeId`, `satPaymentPeriodicityId` y `satPayrollStateId` (estado donde trabaja, por ejemplo `JAL`). +- Contrato `01` a `08` (con relación laboral): el emisor necesita `employerRegistration` (registro patronal) y el empleado `socialSecurityNumber`, `laborRelationStartDate`, `seniority`, `satJobRiskId` e `integratedDailySalary` (NOM42, NOM44); el régimen debe ser `02`, `03` o `04` (NOM57). Contratos `09`, `10` y `99` no llevan registro patronal (NOM43). +- `seniority` es la antigüedad al final del periodo: `P{semanas}W` (por ejemplo `P54W`, sin pasarte de las semanas cumplidas, NOM54) o `P{a}Y{m}M{d}D` exacta (NOM55). +- Los catálogos de nómina distinguen acentos y mayúsculas: sindicalizado es `Sí` o `No`, no `Si` ni `SÍ` ([catálogos de nómina](https://docs.fiscalapi.com/catalogs-info/nomina-12/sindicalizado.md)). + +## 3. `complement.payroll` + +| Campo | Qué es | +|---|---| +| `payrollTypeCode` | `O` ordinaria (periodicidad distinta de `99`) o `E` extraordinaria (periodicidad `99`) (NOM33, NOM34) | +| `paymentDate`, `initialPaymentDate`, `finalPaymentDate` | `yyyy-MM-dd`; el inicio no puede ser posterior al fin (NOM35) | +| `daysPaid` | Días pagados del periodo | +| `earnings.earnings[]` | Percepciones: `earningTypeCode` ([tipos](https://docs.fiscalapi.com/catalogs-info/nomina-12/tipo-percepcion.md); `001` sueldos), `code`, `concept`, `taxedAmount`, `exemptAmount` | +| `earnings.otherPayments[]` | Otros pagos: `otherPaymentTypeCode` ([tipos](https://docs.fiscalapi.com/catalogs-info/nomina-12/tipo-otro-pago.md)), `amount`; el subsidio para el empleo (`002`) lleva además `subsidyCaused` | +| `deductions[]` | Deducciones: `deductionTypeCode` ([tipos](https://docs.fiscalapi.com/catalogs-info/nomina-12/tipo-deduccion.md); `001` seguridad social, `002` ISR), `code`, `concept`, `amount` | +| `disabilities[]` | Incapacidades: `disabilityDays`, `disabilityTypeCode`, `monetaryAmount` | + +`otherPayments` va **dentro** de `earnings`; `deductions` y `disabilities` van en la raíz de `payroll`. FiscalAPI calcula todos los totales (percepciones, gravado, exento, deducciones, impuestos retenidos y otros pagos): no los mandes. FiscalAPI no calcula el ISR: el monto de la deducción `002` lo calcula el sistema de nómina del integrador. + +## 4. Subsidio para el empleo + +- Con régimen `02` (sueldos), el recibo lleva el otro pago `002` aunque sea en 0 (`amount` 0 y `subsidyCaused` 0), salvo que haya `007` o `008` (NOM105). Otros regímenes no lo llevan (NOM106). +- `amount` (subsidio entregado) no puede pasar de `subsidyCaused` (subsidio causado) (NOM107). +- Si en un periodo se entregó subsidio de más, se descuenta con la deducción `071`. + +## 5. Antes de timbrar + +1. Receptor con RFC de 13 caracteres, `605` y `CN01`; sin `paymentFormCode` en la raíz. +2. Contrato, régimen y registro patronal congruentes (sección 2). +3. Antigüedad que no exceda el periodo trabajado. +4. Subsidio `002` presente con régimen `02`. + +Si el PAC rechaza, busca el código en [Errores Nómina 1.2](https://docs.fiscalapi.com/errors/nomina-12/errores-nomina-1-2.md) o usa la skill `fiscalapi-diagnostico`. Para consultar el estatus, cancelar o enviar el recibo, usa `fiscalapi-ciclo-de-vida`. From 8c6611f1eb37b53e49b3662f64438d6e618ace03 Mon Sep 17 00:00:00 2001 From: mendozagit Date: Thu, 15 Oct 2026 13:18:57 -0600 Subject: [PATCH 34/44] Tablero: SK-07 con su hash --- docs/ai-integracion/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/ai-integracion/README.md b/docs/ai-integracion/README.md index 2ae588a..0fd1eab 100644 --- a/docs/ai-integracion/README.md +++ b/docs/ai-integracion/README.md @@ -53,7 +53,7 @@ La siguiente tarea (global o de un área) es la primera fila `pendiente` (de esa | SK-04 | Escribir `fiscalapi-ingreso-egreso` | skills | SK-02, MED-01 | hecho | fiscalapi-ai@1bcaf7d | | SK-05 | Escribir `fiscalapi-pagos` | skills | SK-02, MED-01 | hecho | fiscalapi-ai@557c1f6 | | SK-06 | Escribir `fiscalapi-ciclo-de-vida` y `revisar-integracion` | skills | SK-02 | hecho | fiscalapi-ai@e5d6690 | -| SK-07 | Escribir `fiscalapi-nomina` | skills | SK-02, MED-01 | hecho | | +| SK-07 | Escribir `fiscalapi-nomina` | skills | SK-02, MED-01 | hecho | fiscalapi-ai@51cbb20 | | SK-08 | Escribir `fiscalapi-carta-porte` | skills | SK-02, MED-01 | pendiente | | | SK-09 | Escribir `fiscalapi-comercio-exterior` | skills | SK-02, MED-01 | pendiente | | | MCP-01 | Crear el paquete stdio con `listar_operaciones` y `obtener_operacion` | mcp | SK-01, BE-03 | pendiente | | From 9849defdf468cc8766dca5f1efc3f8b8772f28d4 Mon Sep 17 00:00:00 2001 From: mendozagit Date: Thu, 15 Oct 2026 13:29:45 -0600 Subject: [PATCH 35/44] SK-08: skill fiscalapi-carta-porte MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Carta Porte 3.1 por autotransporte: traslado (T, XXX, receptor = emisor) o ingreso por el flete (I), ubicaciones con domicilio y distancias, mercancías con material peligroso, autotransporte (permiso, vehículo, seguros, remolques según la configuración) y figuras, catálogos de la API con sus límites y los CP más comunes, con sus secciones .md. Evidencia (back local, PAC de pruebas, plugin sin skills del usuario): - traslado f904a48d-3ec0-40e4-9d4b-14414738b0b4: XML T/XXX, S01, origen CMX y destino COA con TotalDistRec 850, mercancía 11121900 de 120 kg, C2 y operador 01; estatus consultado (simulado). - check-skills contra el build local de fapi-docs: 182 enlaces, 0 hallazgos. Defecto ampliado: fapi-back@f6ca9a69 (FechaHoraSalidaLlegada en UTC). --- docs/ai-integracion/README.md | 2 +- docs/ai-integracion/skills.md | 3 ++ skills/fiscalapi-carta-porte/SKILL.md | 48 +++++++++++++++++++++++++++ 3 files changed, 52 insertions(+), 1 deletion(-) create mode 100644 skills/fiscalapi-carta-porte/SKILL.md diff --git a/docs/ai-integracion/README.md b/docs/ai-integracion/README.md index 0fd1eab..c555f0a 100644 --- a/docs/ai-integracion/README.md +++ b/docs/ai-integracion/README.md @@ -54,7 +54,7 @@ La siguiente tarea (global o de un área) es la primera fila `pendiente` (de esa | SK-05 | Escribir `fiscalapi-pagos` | skills | SK-02, MED-01 | hecho | fiscalapi-ai@557c1f6 | | SK-06 | Escribir `fiscalapi-ciclo-de-vida` y `revisar-integracion` | skills | SK-02 | hecho | fiscalapi-ai@e5d6690 | | SK-07 | Escribir `fiscalapi-nomina` | skills | SK-02, MED-01 | hecho | fiscalapi-ai@51cbb20 | -| SK-08 | Escribir `fiscalapi-carta-porte` | skills | SK-02, MED-01 | pendiente | | +| SK-08 | Escribir `fiscalapi-carta-porte` | skills | SK-02, MED-01 | hecho | | | SK-09 | Escribir `fiscalapi-comercio-exterior` | skills | SK-02, MED-01 | pendiente | | | MCP-01 | Crear el paquete stdio con `listar_operaciones` y `obtener_operacion` | mcp | SK-01, BE-03 | pendiente | | | MCP-02 | Agregar `buscar_docs` con índice en español | mcp | MCP-01, DOC-01 | pendiente | | diff --git a/docs/ai-integracion/skills.md b/docs/ai-integracion/skills.md index 5d8b36b..c3569b8 100644 --- a/docs/ai-integracion/skills.md +++ b/docs/ai-integracion/skills.md @@ -169,6 +169,9 @@ Todo va en fiscalapi-ai, en la rama `feat/ai-integracion`. Reglas comunes a SK-0 **Pruebas focalizadas:** ninguna (no aplica). **Fuera de alcance:** transporte marítimo, aéreo y ferroviario, salvo que MED-01 diga lo contrario. **Notas:** +- Timbrado con el plugin, sin skills del usuario: traslado `f904a48d-…`, T/XXX con receptor igual al emisor y S01. Origen CMX y destino COA (850 km solo en el destino, igual que `TotalDistRec`), 10 cajas de 120 kg, C2 sin remolques, TPAF01 y operador 01. Estatus consultado (simulado). +- `FechaHoraSalidaLlegada` sale en UTC, el mismo defecto que `FechaPago`; quedó en `known-issues.md` (`fapi-back@f6ca9a69`). La API de catálogos no expone `c_Estado` de 3 letras ni códigos postales, así que la congruencia CP/colonia la valida solo el PAC. +- Las docs (`crear-factura-de-carta-porte:126`) dicen que en T `itemCode` debe ser igual a `bienesTranspId`. El caso 14 del SAT usa 78101800, y aquí timbró con 11121900: ninguna regla CP lo exige. ### SK-09 — Escribir `fiscalapi-comercio-exterior` **Objetivo:** que el agente emita un CFDI de exportación con Comercio Exterior 2.0 correcto. diff --git a/skills/fiscalapi-carta-porte/SKILL.md b/skills/fiscalapi-carta-porte/SKILL.md new file mode 100644 index 0000000..5c1ada6 --- /dev/null +++ b/skills/fiscalapi-carta-porte/SKILL.md @@ -0,0 +1,48 @@ +--- +name: fiscalapi-carta-porte +description: Emite con FiscalAPI un CFDI con complemento Carta Porte 3.1 por autotransporte, de traslado (tipo T) o de ingreso por el flete (tipo I). Decide moneda, receptor y conceptos según el tipo, arma ubicaciones de origen y destino con domicilio y distancias, mercancías con su clave, peso y material peligroso, el autotransporte (permiso SCT, vehículo, seguros y remolques) y las figuras de transporte, y evita los rechazos CP más comunes. Úsala cuando vayas a timbrar una carta porte o a transportar mercancías por carretera con CFDI. Para un error CP que ya devolvió la API, usa fiscalapi-diagnostico. +--- + +# Carta Porte 3.1 por autotransporte + +Antes de escribir el cuerpo, lee [Crear factura de carta porte](https://docs.fiscalapi.com/invoices-by-value/crear-factura-de-carta-porte.md) (por referencias: [esta sección](https://docs.fiscalapi.com/invoices-by-reference/crear-factura-de-carta-porte.md)) y toma su ejemplo como base. El ambiente, las llaves y el SDK están en la skill `fiscalapi-integracion`. Esta skill cubre autotransporte; marítimo, aéreo y ferroviario no. + +## 1. Traslado (T) o ingreso (I) + +| | Traslado `T` | Ingreso `I` | +|---|---|---| +| Cuándo | Mueves mercancía propia; no cobras el flete | Cobras el servicio de transporte a un cliente | +| `currencyCode` | `XXX` (CP103) | La del cobro, por ejemplo `MXN` (CP104) | +| Receptor | El mismo emisor (CP107) | El cliente | +| Uso CFDI | FiscalAPI fija `S01` | El del cliente | +| Forma y método de pago | No llevan | Los del cobro | +| Conceptos (`items`) | Obligatorios; `taxObjectCode` `01` | Clave de servicio de transporte, por ejemplo `78101800` (CP109) | + +En un traslado, FiscalAPI deja subtotal y total en 0 aunque los conceptos tengan importe. FiscalAPI no valida la moneda ni que el receptor sea el emisor: si te equivocas, rechaza el PAC. + +## 2. `complement.cartaPorte` + +FiscalAPI genera solos el `IdCCP`, el peso bruto total y el número total de mercancías: no los mandes. Los nombres de los campos son los del ejemplo de las docs; un campo mal escrito se ignora sin error, así que cópialos de ahí. + +- **Raíz:** `transpInternacId` (`No` para nacional, con acento en `Sí`), `unidadPesoId` ([unidad de peso](https://docs.fiscalapi.com/catalogs-info/carta-porte-31/clave-unidad-peso.md), por ejemplo `KGM`) y `totalDistRec`, la suma de las distancias de los destinos (CP125, CP126). +- **Ubicaciones** (al menos un origen y un destino, CP130): `tipoUbicacion` (`Origen` o `Destino`), `idUbicacion` (`OR` o `DE` más 6 dígitos), RFC y nombre del remitente o destinatario, `fechaHoraSalidaLlegada` y, **solo en los destinos**, `distanciaRecorrida` (CP141). +- **Domicilio de cada ubicación:** `estadoId` (clave de 3 letras del SAT, por ejemplo `CMX`, `JAL`, `COA`), `paisId` `MEX` y `codigoPostalId`; colonia, municipio y localidad deben corresponder a ese código postal y estado (CP143 a CP147). FiscalAPI no lo valida. +- **Mercancías:** `bienesTranspId` ([clave de producto de carta porte](https://docs.fiscalapi.com/catalogs-info/carta-porte-31/clave-prod-serv-cp.md)), `descripcion`, `cantidad`, `claveUnidadId`, `pesoEnKg` (hasta 3 decimales) y `materialPeligrosoId` (`Sí` o `No`, según lo que la clave permita, CP155). Con material peligroso, `cveMaterialPeligrosoId`, `embalajeId` y el seguro de medio ambiente son obligatorios (CP182). +- **Autotransporte:** `permSCTId` ([tipo de permiso](https://docs.fiscalapi.com/catalogs-info/carta-porte-31/tipo-permiso.md)), `numPermisoSCT`, `configVehicularId` ([configuración vehicular](https://docs.fiscalapi.com/catalogs-info/carta-porte-31/config-autotransporte.md)), `pesoBrutoVehicular`, `placaVM` (5 a 7 caracteres sin guiones ni espacios), `anioModeloVM`, `aseguraRespCivil` y `polizaRespCivil`. Los `remolques` (máximo 2, con `subTipoRemId` y `placa`) van o no según la configuración vehicular (CP184): un tractocamión los exige, un camión unitario no. +- **Figuras de transporte:** al menos el operador (`tipoFiguraId` `01`) con `rfcFigura`, `nombreFigura` y `numLicencia` (CP193, CP194). + +## 3. Catálogos + +Los catálogos de carta porte se consultan con la API de catálogos ([tabla de nombres](https://docs.fiscalapi.com/catalogs/catalogos-de-carta-porte-3-1.md)): `SatClaveUnidadPeso`, `SatMaterialPeligroso`, `SatTipoEmbalaje`, `SatTipoPermiso`, `SatConfigAutotransporte`, `SatSubTipoRem`, `SatFiguraTransporte`, `SatColonia`, `SatMunicipio` y `SatLocalidad`. Ten en cuenta sus límites: + +- `SatStates` usa claves numéricas, no las de 3 letras que pide `estadoId`. +- No hay catálogo de códigos postales, y colonia, municipio y localidad no traen su código postal: la coherencia entre ellos no se puede comprobar con la API. Toma esos datos de una fuente confiable (el domicilio fiscal real) y no los adivines. + +## 4. Antes de timbrar + +1. Tipo, moneda y receptor según la tabla de la sección 1. +2. `totalDistRec` igual a la suma de las distancias de los destinos; ninguna distancia en los orígenes. +3. Domicilios con estado de 3 letras y colonia, municipio, localidad y código postal congruentes. +4. Remolques según la configuración vehicular y un operador con licencia. + +Si el PAC rechaza, busca el código en [Errores Carta Porte 3.1](https://docs.fiscalapi.com/errors/carta-porte-31/errores-carta-porte-3-1.md) o usa la skill `fiscalapi-diagnostico`. Para consultar el estatus o cancelar, usa `fiscalapi-ciclo-de-vida`. From 74836d96d5a1173c4ccae7542dfa416364fd2e4b Mon Sep 17 00:00:00 2001 From: mendozagit Date: Thu, 15 Oct 2026 13:29:46 -0600 Subject: [PATCH 36/44] Tablero: SK-08 con su hash --- docs/ai-integracion/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/ai-integracion/README.md b/docs/ai-integracion/README.md index c555f0a..7c83fd8 100644 --- a/docs/ai-integracion/README.md +++ b/docs/ai-integracion/README.md @@ -54,7 +54,7 @@ La siguiente tarea (global o de un área) es la primera fila `pendiente` (de esa | SK-05 | Escribir `fiscalapi-pagos` | skills | SK-02, MED-01 | hecho | fiscalapi-ai@557c1f6 | | SK-06 | Escribir `fiscalapi-ciclo-de-vida` y `revisar-integracion` | skills | SK-02 | hecho | fiscalapi-ai@e5d6690 | | SK-07 | Escribir `fiscalapi-nomina` | skills | SK-02, MED-01 | hecho | fiscalapi-ai@51cbb20 | -| SK-08 | Escribir `fiscalapi-carta-porte` | skills | SK-02, MED-01 | hecho | | +| SK-08 | Escribir `fiscalapi-carta-porte` | skills | SK-02, MED-01 | hecho | fiscalapi-ai@9849def | | SK-09 | Escribir `fiscalapi-comercio-exterior` | skills | SK-02, MED-01 | pendiente | | | MCP-01 | Crear el paquete stdio con `listar_operaciones` y `obtener_operacion` | mcp | SK-01, BE-03 | pendiente | | | MCP-02 | Agregar `buscar_docs` con índice en español | mcp | MCP-01, DOC-01 | pendiente | | From c61550cc4d7dfcf3b572f150845d17dcd967c144 Mon Sep 17 00:00:00 2001 From: mendozagit Date: Thu, 15 Oct 2026 13:34:03 -0600 Subject: [PATCH 37/44] ai-integracion: MED-03 y U-01 fuera de alcance (D-20) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Decisión del usuario (2026-10-15): no se mide el top de rechazos del PAC en el Seq de producción ni se abre el túnel. Las filas salen del tablero; EV-01 usa un código representativo por matriz (CFDI40, CRP, NOM, CP, CCE). --- docs/ai-integracion/README.md | 6 ++---- docs/ai-integracion/decisiones.md | 6 ++++++ docs/ai-integracion/evals.md | 4 ++-- docs/ai-integracion/medicion.md | 1 + docs/ai-integracion/requisitos.md | 2 +- docs/ai-integracion/skills.md | 2 +- 6 files changed, 13 insertions(+), 8 deletions(-) diff --git a/docs/ai-integracion/README.md b/docs/ai-integracion/README.md index 7c83fd8..3dd2ef0 100644 --- a/docs/ai-integracion/README.md +++ b/docs/ai-integracion/README.md @@ -15,7 +15,7 @@ Para eso el feature publica: **Ramas:** `feat/ai-integracion` en cada repo que toque (fiscalapi-ai, fapi-back, fapi-docs, fapi-reusables), siempre desde `main`. -**Fuera de alcance:** MCP hosteado, sandbox sin "compra" y CSD de prueba propios. Ver `requisitos.md`. +**Fuera de alcance:** MCP hosteado, sandbox sin "compra", CSD de prueba propios y el top de rechazos del PAC en Seq (MED-03, D-20). Ver `requisitos.md`. ## Cómo trabajar Sigue el ciclo de `fapi-core` (sección 1). Particularidades de este feature: @@ -35,8 +35,6 @@ La siguiente tarea (global o de un área) es la primera fila `pendiente` (de esa |---|---|---|---|---|---| | MED-01 | Medir mezcla de tipos y complementos en el restore de prod | medicion | — | hecho | fiscalapi-ai@4f6664c | | MED-02 | Medir embudo y tiempo al primer timbre en el restore de Test | medicion | — | hecho | fiscalapi-ai@a5f9586 | -| U-01 | Abrir el túnel a Seq de producción | usuario | — | pendiente | | -| MED-03 | Medir el top de rechazos del PAC en Seq | medicion | U-01 | pendiente | | | BE-01 | Publicar la superficie de 100 acciones con `[PublicApi]` | backend | — | hecho | fapi-back@8d31a9aa | | BE-02 | Agregar `operationId` estable y metadatos del documento | backend | BE-01 | hecho | fapi-back@8194141e | | BE-03 | Servir `/api/openapi/v4.json` anónimo en test y live | backend | BE-02 | hecho | fapi-back@3ab2a369 | @@ -55,7 +53,7 @@ La siguiente tarea (global o de un área) es la primera fila `pendiente` (de esa | SK-06 | Escribir `fiscalapi-ciclo-de-vida` y `revisar-integracion` | skills | SK-02 | hecho | fiscalapi-ai@e5d6690 | | SK-07 | Escribir `fiscalapi-nomina` | skills | SK-02, MED-01 | hecho | fiscalapi-ai@51cbb20 | | SK-08 | Escribir `fiscalapi-carta-porte` | skills | SK-02, MED-01 | hecho | fiscalapi-ai@9849def | -| SK-09 | Escribir `fiscalapi-comercio-exterior` | skills | SK-02, MED-01 | pendiente | | +| SK-09 | Escribir `fiscalapi-comercio-exterior` | skills | SK-02, MED-01 | en curso | | | MCP-01 | Crear el paquete stdio con `listar_operaciones` y `obtener_operacion` | mcp | SK-01, BE-03 | pendiente | | | MCP-02 | Agregar `buscar_docs` con índice en español | mcp | MCP-01, DOC-01 | pendiente | | | EV-01 | Montar el harness de evals con los casos de ingreso y pagos | evals | SK-05, MCP-02 | pendiente | | diff --git a/docs/ai-integracion/decisiones.md b/docs/ai-integracion/decisiones.md index 250e4dc..0c50306 100644 --- a/docs/ai-integracion/decisiones.md +++ b/docs/ai-integracion/decisiones.md @@ -132,3 +132,9 @@ Reglas de este archivo: - **Por qué:** se buscan para que las consultas que fallaron en la prueba de búsqueda (`requisitos.md`) encuentren su operación, como pidió el usuario el 2026-10-14. El vocabulario interno gobierna el código y la prosa del equipo, no las palabras con que busca un integrador. - **Consecuencias:** BE-05 sigue la misma regla. Un texto que afirme un código HTTP o un campo se valida contra el handler. - **Estado:** asumida (agente, 2026-10-15). El usuario delegó la aprobación de los textos de BE-04 ("analízalo tú y decide"); revisar con el usuario. + +### D-20 — El top de rechazos del PAC queda fuera de alcance +- **Decisión:** no se mide el top de rechazos del PAC en el Seq de producción (MED-03), y no se abre el túnel a Seq (U-01). Las dos filas salen del tablero. +- **Por qué:** lo decidió el usuario el 2026-10-15. Las skills ya están escritas sin ese dato, y ninguna otra tarea depende de MED-03. +- **Consecuencias:** EV-01 usa un código representativo de cada matriz (`CFDI40`, `CRP`, `NOM`, `CP`, `CCE`) para los casos de "explicar error". `fiscalapi-diagnostico` cubre todas las familias, aunque sin ordenarlas por frecuencia real. +- **Estado:** confirmada. diff --git a/docs/ai-integracion/evals.md b/docs/ai-integracion/evals.md index 7f23a8e..b8952a0 100644 --- a/docs/ai-integracion/evals.md +++ b/docs/ai-integracion/evals.md @@ -29,10 +29,10 @@ Todo va en fiscalapi-ai, en `evals/`, en la rama `feat/ai-integracion`. Las eval **Depende de:** EV-01, SK-09. **Skills:** `fapi-cfdi-rules`. **Modelo sugerido (opcional):** haiku para escribir los casos; opus revisa la calificación. -**Contexto:** D-03; el orden y los casos extra los fija MED-01 y el top de rechazos de MED-03. +**Contexto:** D-03; el orden y los casos extra los fija MED-01; los códigos de "explicar error" son representativos de las matrices (D-20). **Alcance:** - Casos de nómina ordinaria, Carta Porte de traslado por autotransporte y exportación definitiva con Comercio Exterior. -- Un caso de "explicar error" por cada código del top 5 de MED-03. +- Un caso de "explicar error" por familia de código: `CFDI40`, `CRP`, `NOM`, `CP` y `CCE`, uno representativo de su matriz en las docs. **Criterios de aceptación (todos con `→ verificar`):** - Los casos nuevos corren en las 3 variantes → verificar: reporte con las filas nuevas. **Pruebas focalizadas:** ninguna (no aplica). diff --git a/docs/ai-integracion/medicion.md b/docs/ai-integracion/medicion.md index b6d9a11..5733ca2 100644 --- a/docs/ai-integracion/medicion.md +++ b/docs/ai-integracion/medicion.md @@ -49,6 +49,7 @@ Los resultados de estas tareas van a `requisitos.md` → **Contexto medido**, co - "Suscripción" se midió como cualquier fila en `Features` (desde 2026-10-05 casi no hay `Type = 'Base'`). Restore aparte `WebApi_TestSnap` (READ_ONLY), borrado al terminar. ### MED-03 — Medir el top de rechazos del PAC en Seq +**Estado:** fuera de alcance por decisión del usuario (D-20); se quitó del tablero junto con U-01. **Objetivo:** saber qué códigos de rechazo del PAC son los más frecuentes, para dar prioridad a `fiscalapi-diagnostico` y a los casos de las evals. **Depende de:** U-01 (el usuario abre el túnel a Seq de producción). **Skills:** `fapi-investigate`. diff --git a/docs/ai-integracion/requisitos.md b/docs/ai-integracion/requisitos.md index 109ecaf..47d3b61 100644 --- a/docs/ai-integracion/requisitos.md +++ b/docs/ai-integracion/requisitos.md @@ -121,7 +121,7 @@ Fecha de las mediciones: 2026-10-14. | 2026-10-05 | 17 | 15 | 6 | 5 | 4 | 249 | | 2026-10-12 (incompleta) | 14 | 14 | 5 | 4 | 4 | 128 | -**Sin medir todavía:** el top de rechazos del PAC (MED-03). +**Fuera de alcance:** el top de rechazos del PAC (MED-03), por decisión del usuario (D-20). ## Requisitos funcionales - **RF-01 — OpenAPI público.** `GET https://{test,live}.fiscalapi.com/api/openapi/v4.json` responde sin autenticación con exactamente las 100 acciones de D-05. Cada operación tiene `operationId` estable y servidores reales. diff --git a/docs/ai-integracion/skills.md b/docs/ai-integracion/skills.md index c3569b8..a143793 100644 --- a/docs/ai-integracion/skills.md +++ b/docs/ai-integracion/skills.md @@ -67,7 +67,7 @@ Todo va en fiscalapi-ai, en la rama `feat/ai-integracion`. Reglas comunes a SK-0 **Depende de:** SK-01, DOC-02. **Skills:** `superpowers:writing-skills`, `fapi-cfdi-rules`. **Modelo sugerido (opcional):** opus. -**Contexto:** D-03; MED-03 (si ya terminó) para priorizar los códigos. +**Contexto:** D-03. El top de rechazos del PAC (MED-03) quedó fuera de alcance (D-20). **Alcance:** - Árbol de decisión: - 400 con `propertyName` → validación; From 8cb8c77d84e172a23664fb6d675765b91df6d87c Mon Sep 17 00:00:00 2001 From: mendozagit Date: Thu, 15 Oct 2026 13:50:23 -0600 Subject: [PATCH 38/44] SK-09: skill fiscalapi-comercio-exterior MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Exportación definitiva con Comercio Exterior 2.0: exportación 02, moneda con exchangeRate, receptor extranjero (XEXX010101000, foreignTin, countryId, 616, S01), A1, Incoterm, TipoCambioUSD del DOF, domicilios (estado de catálogo para EE. UU. y Canadá), mercancías ligadas por NoIdentificacion con fracción, UMT y valor en dólares, y los CCE más comunes, con sus secciones .md. Evidencia (back local, PAC de pruebas, plugin sin skills del usuario): - exportación 72064f38-7118-4fad-900b-4d5e76a31db6: XML I/02, USD 18.3528, receptor USA 123456789 616 S01, A1 FOB, TotalUSD 400.00; estatus consultado (simulado). El primer intento dio CCE139 y la skill se corrigió. - check-skills contra el build local de fapi-docs: 185 enlaces, 0 hallazgos. --- docs/ai-integracion/README.md | 2 +- docs/ai-integracion/skills.md | 3 ++ skills/fiscalapi-comercio-exterior/SKILL.md | 52 +++++++++++++++++++++ 3 files changed, 56 insertions(+), 1 deletion(-) create mode 100644 skills/fiscalapi-comercio-exterior/SKILL.md diff --git a/docs/ai-integracion/README.md b/docs/ai-integracion/README.md index 3dd2ef0..3c39a91 100644 --- a/docs/ai-integracion/README.md +++ b/docs/ai-integracion/README.md @@ -53,7 +53,7 @@ La siguiente tarea (global o de un área) es la primera fila `pendiente` (de esa | SK-06 | Escribir `fiscalapi-ciclo-de-vida` y `revisar-integracion` | skills | SK-02 | hecho | fiscalapi-ai@e5d6690 | | SK-07 | Escribir `fiscalapi-nomina` | skills | SK-02, MED-01 | hecho | fiscalapi-ai@51cbb20 | | SK-08 | Escribir `fiscalapi-carta-porte` | skills | SK-02, MED-01 | hecho | fiscalapi-ai@9849def | -| SK-09 | Escribir `fiscalapi-comercio-exterior` | skills | SK-02, MED-01 | en curso | | +| SK-09 | Escribir `fiscalapi-comercio-exterior` | skills | SK-02, MED-01 | hecho | | | MCP-01 | Crear el paquete stdio con `listar_operaciones` y `obtener_operacion` | mcp | SK-01, BE-03 | pendiente | | | MCP-02 | Agregar `buscar_docs` con índice en español | mcp | MCP-01, DOC-01 | pendiente | | | EV-01 | Montar el harness de evals con los casos de ingreso y pagos | evals | SK-05, MCP-02 | pendiente | | diff --git a/docs/ai-integracion/skills.md b/docs/ai-integracion/skills.md index a143793..a12236c 100644 --- a/docs/ai-integracion/skills.md +++ b/docs/ai-integracion/skills.md @@ -186,6 +186,9 @@ Todo va en fiscalapi-ai, en la rama `feat/ai-integracion`. Reglas comunes a SK-0 **Pruebas focalizadas:** ninguna (no aplica). **Fuera de alcance:** — **Notas:** +- Timbrado con el plugin, sin skills del usuario: exportación definitiva `72064f38-…` con I/02, USD y TipoCambio = TipoCambioUSD 18.3528 (DOF del 14/10, tomado de Banxico por el agente). Receptor XEXX010101000 con USA, 123456789, 616 y S01. `NoIdentificacion` del concepto igual al de la mercancía, ValorDolares 400 y TotalUSD 400.00. Estatus consultado (simulado). +- El primer intento lo rechazó el PAC con CCE139: con EE. UU. el `estado` del receptor es clave de `c_Estado` (`TX`), no texto. Corregí la skill con la regla de la matriz (CCE139, CCE140). +- FiscalAPI no exige `02`, el Incoterm ni la congruencia entre conceptos y mercancías, y con USD sin `exchangeRate` escribe TipoCambio 1. El cURL de las docs trae un `valorDolares` (23.47) que no cuadra con 400 USD. ### SK-10 — Validar en el check semanal los enlaces "Docs:" del OpenAPI **Objetivo:** que un título renombrado o retirado en fapi-docs no deje rotos, sin que nadie lo note, los enlaces "Docs:" de las descripciones del OpenAPI público. diff --git a/skills/fiscalapi-comercio-exterior/SKILL.md b/skills/fiscalapi-comercio-exterior/SKILL.md new file mode 100644 index 0000000..ab4d095 --- /dev/null +++ b/skills/fiscalapi-comercio-exterior/SKILL.md @@ -0,0 +1,52 @@ +--- +name: fiscalapi-comercio-exterior +description: Emite con FiscalAPI un CFDI de exportación definitiva con complemento Comercio Exterior 2.0. Decide exportación 02, moneda y tipo de cambio, el tipo de cambio USD del DOF, el receptor extranjero (XEXX010101000, NumRegIdTrib, residencia fiscal), la clave de pedimento A1, el Incoterm, los domicilios, y las mercancías con fracción arancelaria, unidad y cantidad de aduana y valor en dólares ligadas a cada concepto. También evita los rechazos CCE más comunes. Úsala cuando vayas a facturar una venta al extranjero o una exportación de mercancías. Para un error CCE que ya devolvió la API, usa fiscalapi-diagnostico. +--- + +# Comercio Exterior 2.0 (exportación definitiva) + +Antes de escribir el cuerpo, lee [Crear factura de comercio exterior](https://docs.fiscalapi.com/invoices-by-value/crear-factura-de-comercio-exterior.md) (por referencias: [esta sección](https://docs.fiscalapi.com/invoices-by-reference/crear-factura-de-comercio-exterior.md)). **No copies la mercancía de su ejemplo cURL**: la factura es de 400 USD y la mercancía declara 23.47 en `valorDolares`, que no cuadra. El ambiente, las llaves y el SDK están en la skill `fiscalapi-integracion`. + +## 1. El comprobante + +- `typeCode` `I`, `exportCode` `02` (exportación definitiva) y `complement.comercioExterior`. Sin el complemento, `02` lo rechaza el PAC (CCE104). FiscalAPI no exige el `02`: mándalo tú. +- **Moneda:** la de la venta. Con una moneda extranjera manda siempre `exchangeRate` (por ejemplo `currencyCode` `USD` y `exchangeRate` 17.1995): si lo omites, FiscalAPI escribe `TipoCambio` 1. +- **Receptor extranjero:** `tin` `XEXX010101000`, `legalName` real, `foreignTin` (su número de identificación fiscal; con Estados Unidos o Canadá, 9 dígitos), `countryId` (país de residencia, por ejemplo `USA`), `taxRegimeCode` `616`, `cfdiUseCode` `S01` y `zipCode` igual a `expeditionZipCode` (CFDI40149, CFDI40159). Sin `foreignTin` el PAC rechaza (CCE133). +- **Cada concepto** necesita `itemSku`: se escribe como `NoIdentificacion` y es la llave que liga el concepto con su mercancía en el complemento (CCE151, CCE152). Por referencias, el `NoIdentificacion` es el `id` del producto. + +## 2. `complement.comercioExterior` + +- `claveDePedimentoId` `A1` (exportación definitiva) y `certificadoOrigen` `0` (o `1` con `numCertificadoOrigen`). +- `incotermId`: obligatorio con exportación `02` (CCE120), por ejemplo `FOB`, `CIF` o `EXW`. +- `tipoCambioUSD`: pesos por dólar (sección 3). +- `emisor.domicilio`: `calle`, `estadoId` (clave de 3 letras, por ejemplo `QUE`), `paisId` `MEX`, `codigoPostalId`, y colonia, localidad y municipio de catálogo que correspondan a ese código postal. +- `receptor.domicilio`: `paisId` del receptor, distinto de `MEX` (CCE134). Si el país tiene estados en el catálogo `c_Estado` (Estados Unidos, Canadá), `estado` es su clave, por ejemplo `TX`, no "Texas" (CCE139); en otro país es texto libre. `codigoPostal` sigue el formato de códigos postales del país (CCE140), y municipio y colonia van en texto libre. El `numRegIdTrib` del receptor lo toma FiscalAPI del `foreignTin` del CFDI. +- `mercancias[]`, una por concepto: + - `noIdentificacion`: igual al `itemSku` del concepto. + - `fraccionArancelariaId`: la fracción vigente de la tarifa ([catálogos](https://docs.fiscalapi.com/catalogs/catalogos-de-comercio-exterior-2-0.md)). + - `unidadAduanaId`: la unidad de medida de la tarifa (UMT) de esa fracción (CCE161, CCE167). El catálogo de la API no trae la UMT de cada fracción: tómala de la tarifa oficial o del agente aduanal. + - `cantidadAduana` y `valorUnitarioAduana`: van juntas con la unidad (CCE165, CCE166). + - `valorDolares`: el importe del concepto en dólares (sección 3). + +FiscalAPI calcula solo `TotalUSD` y no valida la congruencia entre conceptos y mercancías: la valida el PAC. + +## 3. Tipo de cambio y valor en dólares + +- `tipoCambioUSD` es el tipo de cambio que publica el Banco de México en el DOF el día anterior a la fecha de la factura (o el de adquisición de la moneda); si no coincide, el PAC rechaza (CCE121). FiscalAPI no lo consulta: obtenlo del DOF o de Banxico en tu sistema. +- `valorDolares` de cada mercancía (CCE158, CCE172): + - factura en USD: el importe del concepto; + - factura en MXN: importe ÷ `tipoCambioUSD`; + - otra moneda: importe × `exchangeRate` ÷ `tipoCambioUSD`. + +## 4. Catálogos + +Por la API de catálogos: `SatClavePedimento`, `SatINCOTERM`, `SatFraccionArancelaria`, `SatUnidadAduana`, `SatMotivoTraslado`, `SatTipoOperacion` y `SatCountries`. El nombre del catálogo distingue mayúsculas. No hay catálogo de códigos postales, y la fracción no trae su unidad: valida esos datos con su fuente oficial. + +## 5. Antes de timbrar + +1. `exportCode` `02`, complemento presente e `incotermId`. +2. Moneda extranjera con `exchangeRate`; `tipoCambioUSD` del DOF del día anterior. +3. Receptor `XEXX010101000` con `foreignTin`, `countryId`, `616`, `S01` y el código postal de expedición. +4. Cada concepto con `itemSku` igual al `noIdentificacion` de su mercancía, y `valorDolares` calculado con la sección 3. + +Si el PAC rechaza, busca el código en [Errores Comercio Exterior 2.0](https://docs.fiscalapi.com/errors/comercio-exterior-20/errores-comercio-exterior-2-0.md) o usa la skill `fiscalapi-diagnostico`. Para consultar el estatus o cancelar, usa `fiscalapi-ciclo-de-vida`. From bda736b6198c5ef1cbdbc8a83e7f568f4d6b5696 Mon Sep 17 00:00:00 2001 From: mendozagit Date: Thu, 15 Oct 2026 13:50:23 -0600 Subject: [PATCH 39/44] Tablero: SK-09 con su hash --- docs/ai-integracion/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/ai-integracion/README.md b/docs/ai-integracion/README.md index 3c39a91..88db163 100644 --- a/docs/ai-integracion/README.md +++ b/docs/ai-integracion/README.md @@ -53,7 +53,7 @@ La siguiente tarea (global o de un área) es la primera fila `pendiente` (de esa | SK-06 | Escribir `fiscalapi-ciclo-de-vida` y `revisar-integracion` | skills | SK-02 | hecho | fiscalapi-ai@e5d6690 | | SK-07 | Escribir `fiscalapi-nomina` | skills | SK-02, MED-01 | hecho | fiscalapi-ai@51cbb20 | | SK-08 | Escribir `fiscalapi-carta-porte` | skills | SK-02, MED-01 | hecho | fiscalapi-ai@9849def | -| SK-09 | Escribir `fiscalapi-comercio-exterior` | skills | SK-02, MED-01 | hecho | | +| SK-09 | Escribir `fiscalapi-comercio-exterior` | skills | SK-02, MED-01 | hecho | fiscalapi-ai@8cb8c77 | | MCP-01 | Crear el paquete stdio con `listar_operaciones` y `obtener_operacion` | mcp | SK-01, BE-03 | pendiente | | | MCP-02 | Agregar `buscar_docs` con índice en español | mcp | MCP-01, DOC-01 | pendiente | | | EV-01 | Montar el harness de evals con los casos de ingreso y pagos | evals | SK-05, MCP-02 | pendiente | | From 6e0c755a021da9501dc20b36c62f5cb229603cab Mon Sep 17 00:00:00 2001 From: mendozagit Date: Thu, 15 Oct 2026 14:12:59 -0600 Subject: [PATCH 40/44] MCP-01: paquete stdio @fiscalapi/mcp con listar_operaciones y obtener_operacion MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Servidor MCP de solo lectura y sin red (D-07). build:data lee el OpenAPI público (live por omisión, o FISCALAPI_OPENAPI_SOURCE) y genera el catálogo, cada operación con sus $ref resueltos y recortada a 40k caracteres, y el sha256 de la fuente para MCP-03. data/ no se versiona. Evidencia: - npm run build:data (fixture) + npm run build + npm test: 123/123 en verde. - listar_operaciones: 100 operaciones en 9,023 caracteres; la operación mayor (createInvoice) mide 28,130. Arranque sin red en ~200 ms. - Las pruebas de límite, path traversal y sugerencias se vieron en rojo al quitar su lógica. - claude -p con --mcp-config: la sesión usó listar_operaciones y obtener_operacion("cancelInvoice") y respondió la ruta y los campos. --- docs/ai-integracion/README.md | 2 +- docs/ai-integracion/mcp.md | 3 + mcp/.gitignore | 4 + mcp/LICENSE | 373 + mcp/README.md | 51 + mcp/package-lock.json | 1723 ++++ mcp/package.json | 54 + mcp/scripts/build-data.mjs | 79 + mcp/scripts/openapi.mjs | 258 + mcp/src/catalog.ts | 100 + mcp/src/index.ts | 30 + mcp/src/server.ts | 94 + mcp/test/build-data.test.ts | 212 + mcp/test/fixtures/openapi-v4.json | 15077 ++++++++++++++++++++++++++++ mcp/test/global-setup.ts | 32 + mcp/test/helpers.ts | 36 + mcp/test/offline.test.ts | 26 + mcp/test/tools.test.ts | 138 + mcp/tsconfig.json | 17 + mcp/vitest.config.ts | 8 + 20 files changed, 18316 insertions(+), 1 deletion(-) create mode 100644 mcp/.gitignore create mode 100644 mcp/LICENSE create mode 100644 mcp/README.md create mode 100644 mcp/package-lock.json create mode 100644 mcp/package.json create mode 100644 mcp/scripts/build-data.mjs create mode 100644 mcp/scripts/openapi.mjs create mode 100644 mcp/src/catalog.ts create mode 100644 mcp/src/index.ts create mode 100644 mcp/src/server.ts create mode 100644 mcp/test/build-data.test.ts create mode 100644 mcp/test/fixtures/openapi-v4.json create mode 100644 mcp/test/global-setup.ts create mode 100644 mcp/test/helpers.ts create mode 100644 mcp/test/offline.test.ts create mode 100644 mcp/test/tools.test.ts create mode 100644 mcp/tsconfig.json create mode 100644 mcp/vitest.config.ts diff --git a/docs/ai-integracion/README.md b/docs/ai-integracion/README.md index 88db163..f30617a 100644 --- a/docs/ai-integracion/README.md +++ b/docs/ai-integracion/README.md @@ -54,7 +54,7 @@ La siguiente tarea (global o de un área) es la primera fila `pendiente` (de esa | SK-07 | Escribir `fiscalapi-nomina` | skills | SK-02, MED-01 | hecho | fiscalapi-ai@51cbb20 | | SK-08 | Escribir `fiscalapi-carta-porte` | skills | SK-02, MED-01 | hecho | fiscalapi-ai@9849def | | SK-09 | Escribir `fiscalapi-comercio-exterior` | skills | SK-02, MED-01 | hecho | fiscalapi-ai@8cb8c77 | -| MCP-01 | Crear el paquete stdio con `listar_operaciones` y `obtener_operacion` | mcp | SK-01, BE-03 | pendiente | | +| MCP-01 | Crear el paquete stdio con `listar_operaciones` y `obtener_operacion` | mcp | SK-01, BE-03 | hecho | | | MCP-02 | Agregar `buscar_docs` con índice en español | mcp | MCP-01, DOC-01 | pendiente | | | EV-01 | Montar el harness de evals con los casos de ingreso y pagos | evals | SK-05, MCP-02 | pendiente | | | EV-02 | Agregar casos de nómina, carta porte y comercio exterior | evals | EV-01, SK-09 | pendiente | | diff --git a/docs/ai-integracion/mcp.md b/docs/ai-integracion/mcp.md index b3e6a59..0702008 100644 --- a/docs/ai-integracion/mcp.md +++ b/docs/ai-integracion/mcp.md @@ -35,6 +35,9 @@ Antes de escribir código contra el SDK de MCP, se consulta su documentación vi **Pruebas focalizadas:** sí (contrato del paquete). `npm test` en `mcp/`. **Fuera de alcance:** búsqueda en docs (MCP-02); publicación (MCP-03); herramienta `invocar` (D-07). **Notas:** +- `@modelcontextprotocol/server` 2.3.1 (v2: `McpServer.registerTool` y `serveStdio`, que atiende las eras del protocolo 2025 y 2026) y zod 4.6.7, con versiones exactas. `data/` no se versiona: lo genera `npm run build:data`, que lee live por omisión o `FISCALAPI_OPENAPI_SOURCE`. Las pruebas usan el fixture del OpenAPI de BE-05. +- Con el fixture: 100 operaciones; el catálogo mide 9,023 caracteres y la operación mayor, createInvoice, 28,130 (se colapsan los media types duplicados de ASP.NET). Arranca en unos 200 ms sin red y pasan 123 pruebas. En Claude Code (`--mcp-config`) respondió una consulta con las dos herramientas. +- Para publicar hay que regenerar `data/` desde live cuando BE-03 esté desplegado (hoy live responde 401). Eso lo hace MCP-03. ### MCP-02 — Agregar `buscar_docs` con índice en español **Objetivo:** que el agente encuentre la sección de las docs que responde a su duda fiscal sin leer páginas completas. diff --git a/mcp/.gitignore b/mcp/.gitignore new file mode 100644 index 0000000..b0bc5f5 --- /dev/null +++ b/mcp/.gitignore @@ -0,0 +1,4 @@ +node_modules/ +dist/ +# Lo genera npm run build:data desde el OpenAPI; se publica en el paquete, no en el repo. +data/ diff --git a/mcp/LICENSE b/mcp/LICENSE new file mode 100644 index 0000000..a612ad9 --- /dev/null +++ b/mcp/LICENSE @@ -0,0 +1,373 @@ +Mozilla Public License Version 2.0 +================================== + +1. Definitions +-------------- + +1.1. "Contributor" + means each individual or legal entity that creates, contributes to + the creation of, or owns Covered Software. + +1.2. "Contributor Version" + means the combination of the Contributions of others (if any) used + by a Contributor and that particular Contributor's Contribution. + +1.3. "Contribution" + means Covered Software of a particular Contributor. + +1.4. "Covered Software" + means Source Code Form to which the initial Contributor has attached + the notice in Exhibit A, the Executable Form of such Source Code + Form, and Modifications of such Source Code Form, in each case + including portions thereof. + +1.5. "Incompatible With Secondary Licenses" + means + + (a) that the initial Contributor has attached the notice described + in Exhibit B to the Covered Software; or + + (b) that the Covered Software was made available under the terms of + version 1.1 or earlier of the License, but not also under the + terms of a Secondary License. + +1.6. "Executable Form" + means any form of the work other than Source Code Form. + +1.7. "Larger Work" + means a work that combines Covered Software with other material, in + a separate file or files, that is not Covered Software. + +1.8. "License" + means this document. + +1.9. "Licensable" + means having the right to grant, to the maximum extent possible, + whether at the time of the initial grant or subsequently, any and + all of the rights conveyed by this License. + +1.10. "Modifications" + means any of the following: + + (a) any file in Source Code Form that results from an addition to, + deletion from, or modification of the contents of Covered + Software; or + + (b) any new file in Source Code Form that contains any Covered + Software. + +1.11. "Patent Claims" of a Contributor + means any patent claim(s), including without limitation, method, + process, and apparatus claims, in any patent Licensable by such + Contributor that would be infringed, but for the grant of the + License, by the making, using, selling, offering for sale, having + made, import, or transfer of either its Contributions or its + Contributor Version. + +1.12. "Secondary License" + means either the GNU General Public License, Version 2.0, the GNU + Lesser General Public License, Version 2.1, the GNU Affero General + Public License, Version 3.0, or any later versions of those + licenses. + +1.13. "Source Code Form" + means the form of the work preferred for making modifications. + +1.14. "You" (or "Your") + means an individual or a legal entity exercising rights under this + License. For legal entities, "You" includes any entity that + controls, is controlled by, or is under common control with You. For + purposes of this definition, "control" means (a) the power, direct + or indirect, to cause the direction or management of such entity, + whether by contract or otherwise, or (b) ownership of more than + fifty percent (50%) of the outstanding shares or beneficial + ownership of such entity. + +2. License Grants and Conditions +-------------------------------- + +2.1. Grants + +Each Contributor hereby grants You a world-wide, royalty-free, +non-exclusive license: + +(a) under intellectual property rights (other than patent or trademark) + Licensable by such Contributor to use, reproduce, make available, + modify, display, perform, distribute, and otherwise exploit its + Contributions, either on an unmodified basis, with Modifications, or + as part of a Larger Work; and + +(b) under Patent Claims of such Contributor to make, use, sell, offer + for sale, have made, import, and otherwise transfer either its + Contributions or its Contributor Version. + +2.2. Effective Date + +The licenses granted in Section 2.1 with respect to any Contribution +become effective for each Contribution on the date the Contributor first +distributes such Contribution. + +2.3. Limitations on Grant Scope + +The licenses granted in this Section 2 are the only rights granted under +this License. No additional rights or licenses will be implied from the +distribution or licensing of Covered Software under this License. +Notwithstanding Section 2.1(b) above, no patent license is granted by a +Contributor: + +(a) for any code that a Contributor has removed from Covered Software; + or + +(b) for infringements caused by: (i) Your and any other third party's + modifications of Covered Software, or (ii) the combination of its + Contributions with other software (except as part of its Contributor + Version); or + +(c) under Patent Claims infringed by Covered Software in the absence of + its Contributions. + +This License does not grant any rights in the trademarks, service marks, +or logos of any Contributor (except as may be necessary to comply with +the notice requirements in Section 3.4). + +2.4. Subsequent Licenses + +No Contributor makes additional grants as a result of Your choice to +distribute the Covered Software under a subsequent version of this +License (see Section 10.2) or under the terms of a Secondary License (if +permitted under the terms of Section 3.3). + +2.5. Representation + +Each Contributor represents that the Contributor believes its +Contributions are its original creation(s) or it has sufficient rights +to grant the rights to its Contributions conveyed by this License. + +2.6. Fair Use + +This License is not intended to limit any rights You have under +applicable copyright doctrines of fair use, fair dealing, or other +equivalents. + +2.7. Conditions + +Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted +in Section 2.1. + +3. Responsibilities +------------------- + +3.1. Distribution of Source Form + +All distribution of Covered Software in Source Code Form, including any +Modifications that You create or to which You contribute, must be under +the terms of this License. You must inform recipients that the Source +Code Form of the Covered Software is governed by the terms of this +License, and how they can obtain a copy of this License. You may not +attempt to alter or restrict the recipients' rights in the Source Code +Form. + +3.2. Distribution of Executable Form + +If You distribute Covered Software in Executable Form then: + +(a) such Covered Software must also be made available in Source Code + Form, as described in Section 3.1, and You must inform recipients of + the Executable Form how they can obtain a copy of such Source Code + Form by reasonable means in a timely manner, at a charge no more + than the cost of distribution to the recipient; and + +(b) You may distribute such Executable Form under the terms of this + License, or sublicense it under different terms, provided that the + license for the Executable Form does not attempt to limit or alter + the recipients' rights in the Source Code Form under this License. + +3.3. Distribution of a Larger Work + +You may create and distribute a Larger Work under terms of Your choice, +provided that You also comply with the requirements of this License for +the Covered Software. If the Larger Work is a combination of Covered +Software with a work governed by one or more Secondary Licenses, and the +Covered Software is not Incompatible With Secondary Licenses, this +License permits You to additionally distribute such Covered Software +under the terms of such Secondary License(s), so that the recipient of +the Larger Work may, at their option, further distribute the Covered +Software under the terms of either this License or such Secondary +License(s). + +3.4. Notices + +You may not remove or alter the substance of any license notices +(including copyright notices, patent notices, disclaimers of warranty, +or limitations of liability) contained within the Source Code Form of +the Covered Software, except that You may alter any license notices to +the extent required to remedy known factual inaccuracies. + +3.5. Application of Additional Terms + +You may choose to offer, and to charge a fee for, warranty, support, +indemnity or liability obligations to one or more recipients of Covered +Software. However, You may do so only on Your own behalf, and not on +behalf of any Contributor. You must make it absolutely clear that any +such warranty, support, indemnity, or liability obligation is offered by +You alone, and You hereby agree to indemnify every Contributor for any +liability incurred by such Contributor as a result of warranty, support, +indemnity or liability terms You offer. You may include additional +disclaimers of warranty and limitations of liability specific to any +jurisdiction. + +4. Inability to Comply Due to Statute or Regulation +--------------------------------------------------- + +If it is impossible for You to comply with any of the terms of this +License with respect to some or all of the Covered Software due to +statute, judicial order, or regulation then You must: (a) comply with +the terms of this License to the maximum extent possible; and (b) +describe the limitations and the code they affect. Such description must +be placed in a text file included with all distributions of the Covered +Software under this License. Except to the extent prohibited by statute +or regulation, such description must be sufficiently detailed for a +recipient of ordinary skill to be able to understand it. + +5. Termination +-------------- + +5.1. The rights granted under this License will terminate automatically +if You fail to comply with any of its terms. However, if You become +compliant, then the rights granted under this License from a particular +Contributor are reinstated (a) provisionally, unless and until such +Contributor explicitly and finally terminates Your grants, and (b) on an +ongoing basis, if such Contributor fails to notify You of the +non-compliance by some reasonable means prior to 60 days after You have +come back into compliance. Moreover, Your grants from a particular +Contributor are reinstated on an ongoing basis if such Contributor +notifies You of the non-compliance by some reasonable means, this is the +first time You have received notice of non-compliance with this License +from such Contributor, and You become compliant prior to 30 days after +Your receipt of the notice. + +5.2. If You initiate litigation against any entity by asserting a patent +infringement claim (excluding declaratory judgment actions, +counter-claims, and cross-claims) alleging that a Contributor Version +directly or indirectly infringes any patent, then the rights granted to +You by any and all Contributors for the Covered Software under Section +2.1 of this License shall terminate. + +5.3. In the event of termination under Sections 5.1 or 5.2 above, all +end user license agreements (excluding distributors and resellers) which +have been validly granted by You or Your distributors under this License +prior to termination shall survive termination. + +************************************************************************ +* * +* 6. Disclaimer of Warranty * +* ------------------------- * +* * +* Covered Software is provided under this License on an "as is" * +* basis, without warranty of any kind, either expressed, implied, or * +* statutory, including, without limitation, warranties that the * +* Covered Software is free of defects, merchantable, fit for a * +* particular purpose or non-infringing. The entire risk as to the * +* quality and performance of the Covered Software is with You. * +* Should any Covered Software prove defective in any respect, You * +* (not any Contributor) assume the cost of any necessary servicing, * +* repair, or correction. This disclaimer of warranty constitutes an * +* essential part of this License. No use of any Covered Software is * +* authorized under this License except under this disclaimer. * +* * +************************************************************************ + +************************************************************************ +* * +* 7. Limitation of Liability * +* -------------------------- * +* * +* Under no circumstances and under no legal theory, whether tort * +* (including negligence), contract, or otherwise, shall any * +* Contributor, or anyone who distributes Covered Software as * +* permitted above, be liable to You for any direct, indirect, * +* special, incidental, or consequential damages of any character * +* including, without limitation, damages for lost profits, loss of * +* goodwill, work stoppage, computer failure or malfunction, or any * +* and all other commercial damages or losses, even if such party * +* shall have been informed of the possibility of such damages. This * +* limitation of liability shall not apply to liability for death or * +* personal injury resulting from such party's negligence to the * +* extent applicable law prohibits such limitation. Some * +* jurisdictions do not allow the exclusion or limitation of * +* incidental or consequential damages, so this exclusion and * +* limitation may not apply to You. * +* * +************************************************************************ + +8. Litigation +------------- + +Any litigation relating to this License may be brought only in the +courts of a jurisdiction where the defendant maintains its principal +place of business and such litigation shall be governed by laws of that +jurisdiction, without reference to its conflict-of-law provisions. +Nothing in this Section shall prevent a party's ability to bring +cross-claims or counter-claims. + +9. Miscellaneous +---------------- + +This License represents the complete agreement concerning the subject +matter hereof. If any provision of this License is held to be +unenforceable, such provision shall be reformed only to the extent +necessary to make it enforceable. Any law or regulation which provides +that the language of a contract shall be construed against the drafter +shall not be used to construe this License against a Contributor. + +10. Versions of the License +--------------------------- + +10.1. New Versions + +Mozilla Foundation is the license steward. Except as provided in Section +10.3, no one other than the license steward has the right to modify or +publish new versions of this License. Each version will be given a +distinguishing version number. + +10.2. Effect of New Versions + +You may distribute the Covered Software under the terms of the version +of the License under which You originally received the Covered Software, +or under the terms of any subsequent version published by the license +steward. + +10.3. Modified Versions + +If you create software not governed by this License, and you want to +create a new license for such software, you may create and use a +modified version of this License if you rename the license and remove +any references to the name of the license steward (except to note that +such modified license differs from this License). + +10.4. Distributing Source Code Form that is Incompatible With Secondary +Licenses + +If You choose to distribute Source Code Form that is Incompatible With +Secondary Licenses under the terms of this version of the License, the +notice described in Exhibit B of this License must be attached. + +Exhibit A - Source Code Form License Notice +------------------------------------------- + + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. + +If it is not possible or desirable to put the notice in a particular +file, then You may include the notice in a location (such as a LICENSE +file in a relevant directory) where a recipient would be likely to look +for such a notice. + +You may add additional accurate notices of copyright ownership. + +Exhibit B - "Incompatible With Secondary Licenses" Notice +--------------------------------------------------------- + + This Source Code Form is "Incompatible With Secondary Licenses", as + defined by the Mozilla Public License, v. 2.0. diff --git a/mcp/README.md b/mcp/README.md new file mode 100644 index 0000000..b0a9f9c --- /dev/null +++ b/mcp/README.md @@ -0,0 +1,51 @@ +# @fiscalapi/mcp + +Servidor MCP de solo lectura para integrar [FiscalAPI](https://docs.fiscalapi.com) (CFDI 4.0) desde tu agente de código. Le da al agente el catálogo de operaciones de la API y el esquema completo de cada una, para que escriba la integración sin adivinar rutas ni campos. + +No usa red ni llaves: el OpenAPI de FiscalAPI viene dentro del paquete y el servidor solo lee esos archivos. Nunca llama a la API. + +## Instalación + +Requiere Node.js 22 o posterior. + +``` +npx -y @fiscalapi/mcp +``` + +En Claude Code: + +``` +claude mcp add fiscalapi -- npx -y @fiscalapi/mcp +``` + +En otros clientes MCP, registra un servidor stdio con el comando `npx` y los argumentos `-y @fiscalapi/mcp`. + +## Herramientas + +| Herramienta | Qué hace | +| --- | --- | +| `listar_operaciones` | Lista las operaciones agrupadas por tag: operationId, método, ruta y resumen. Acepta `tag` (opcional) para filtrar, por ejemplo `Invoices`. | +| `obtener_operacion` | Devuelve en JSON una operación por su `operationId` (por ejemplo `createInvoice`): descripción con el enlace a la documentación, parámetros, cuerpo y respuestas con los esquemas resueltos. Si el id no existe, sugiere los parecidos. | + +El flujo normal es `listar_operaciones` para encontrar el endpoint y `obtener_operacion` para ver su contrato. + +## Desarrollo + +``` +npm install +npm run build:data # genera data/ desde el OpenAPI de live +npm run build +npm test +``` + +`build:data` lee el OpenAPI de `https://live.fiscalapi.com/api/openapi/v4.json`. Para usar otra fuente, pásala como argumento o en `FISCALAPI_OPENAPI_SOURCE` (URL o ruta de archivo): + +``` +FISCALAPI_OPENAPI_SOURCE=test/fixtures/openapi-v4.json npm run build:data +``` + +`data/` no se versiona: lo genera el build y se publica dentro del paquete. `data/source.json` guarda el hash SHA-256 de la fuente. + +## Licencia + +[MPL-2.0](https://github.com/FiscalAPI/fiscalapi-ai/blob/main/LICENSE) diff --git a/mcp/package-lock.json b/mcp/package-lock.json new file mode 100644 index 0000000..e9cbad8 --- /dev/null +++ b/mcp/package-lock.json @@ -0,0 +1,1723 @@ +{ + "name": "@fiscalapi/mcp", + "version": "0.1.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "@fiscalapi/mcp", + "version": "0.1.0", + "license": "MPL-2.0", + "dependencies": { + "@modelcontextprotocol/server": "2.3.1", + "zod": "4.6.7" + }, + "bin": { + "fiscalapi-mcp": "dist/index.js" + }, + "devDependencies": { + "@modelcontextprotocol/client": "2.3.1", + "@types/node": "22.20.5", + "typescript": "7.0.2", + "vitest": "5.0.5" + }, + "engines": { + "node": ">=22" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.6.0.tgz", + "integrity": "sha512-T7jf+5zgsZHwNJ4lvQ7/aezbyk0nNX+zJVWpmHA7VYsEx7a7qr5Rg5IbtJFqkgze5Y2sruq1RUY8Q837Od7iFw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@modelcontextprotocol/client": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/@modelcontextprotocol/client/-/client-2.3.1.tgz", + "integrity": "sha512-mIGZXpHsjnZ6lD+gD/WCMpR5k8yVQQ8nNFH1N0Srf7AvnwTUMYD6pvTY8ng2+He5FfV7YMZLmrjL7cLa/cc3dQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@modelcontextprotocol/core": "2.3.1", + "cross-spawn": "^7.0.5", + "eventsource": "^3.0.2", + "eventsource-parser": "^3.0.8", + "jose": "^6.1.3", + "pkce-challenge": "^5.0.0", + "zod": "^4.2.0" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@modelcontextprotocol/core": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/@modelcontextprotocol/core/-/core-2.3.1.tgz", + "integrity": "sha512-laVmIhPGpWi7rG9q+0UigblRN0Hws/Yjlb6qLFtVqryXVLkc4h+yN8z731POD2LnAGQlFe7/tXhUm7Bi0YB91g==", + "license": "Apache-2.0", + "dependencies": { + "zod": "^4.2.0" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@modelcontextprotocol/server": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/@modelcontextprotocol/server/-/server-2.3.1.tgz", + "integrity": "sha512-e59MfWuSssj6DoQ96JCTqdW1AXT5/XtnTl0WWd6MU1Ka434fFUGGosCLJbGNySDhHGvxQWoRWK137FXYFkYNWw==", + "license": "Apache-2.0", + "dependencies": { + "@modelcontextprotocol/core": "2.3.1", + "zod": "^4.2.0" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@oxc-project/types": { + "version": "0.154.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.154.0.tgz", + "integrity": "sha512-KYOUGQFlLWR+s4QMV2Q9ko38TZm7EdO87Qm7rvZLqk/webse/cDjtyxKhg/47l0byCbs/k46LT8qc4PKjAikzA==", + "dev": true, + "license": "MIT", + "peer": true, + "funding": { + "url": "https://github.com/sponsors/oxc-project" + } + }, + "node_modules/@rolldown/binding-android-arm-eabi": { + "version": "1.2.14", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm-eabi/-/binding-android-arm-eabi-1.2.14.tgz", + "integrity": "sha512-aWR+XKH9+rW7m5fWq/5360J3mHycrx/o6xZM2DQFdJnTMdyP7/WzxPjhtLbVWdKo2y612/4U1bWqb3HQhiF7vg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "peer": true, + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-android-arm64": { + "version": "1.2.14", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.2.14.tgz", + "integrity": "sha512-QiVxHofl6A8lQU7WAtOUY4QMVZXohxwz7WJJoeh8NBEUyKfIkMRKhYyq8az/1RypNiIXzNTtiS6KwHz0erxYRQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "peer": true, + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-darwin-arm64": { + "version": "1.2.14", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.2.14.tgz", + "integrity": "sha512-lTQOzHuBIooh3jIr5XINQkjPGEuGy+Dsy0meXDr6Sjty0b99kYgGh4uBC/8mJX1T0kC8d6S0aPP1Y+bHkh6aBw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "peer": true, + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-darwin-x64": { + "version": "1.2.14", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.2.14.tgz", + "integrity": "sha512-hyMKEGatjmBYCs/Q4sbIcL/GqpFEqsX3wc+HYVQ/85A4rxc1o7rPm4DUI4GV4k/h0ITddwFSGpqQunI14GXjTA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "peer": true, + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-freebsd-x64": { + "version": "1.2.14", + "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.2.14.tgz", + "integrity": "sha512-u371GbXoVXrEgP20PwzoGNOdAOqAMhzo19JzzY+V4YmmnC+DTOoqdbMu+O+54emM59Mik/UdrtWgcuEDKWRPKA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "peer": true, + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm-gnueabihf": { + "version": "1.2.14", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.2.14.tgz", + "integrity": "sha512-BqwKsdZQf6EqVWr8fv2MrDYzAvOZ7+NYFSJESRGs87fkseEhs0TU9TXgRJTkZoRRkxTizyEg1LKUXfU3/78uqg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm64-gnu": { + "version": "1.2.14", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.2.14.tgz", + "integrity": "sha512-SD9kY8M009x/7s57Z1dgUVlP5+uYBgKQK0Tkr1WIWngrtUJmRCW1RJFMY7QDla2khXhmg2i5Io7MUjcmZNZ/KQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm64-musl": { + "version": "1.2.14", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.2.14.tgz", + "integrity": "sha512-BJ2CL19M6uuWesMgnqJzgxoI+N+eV7b0A9O9vLV42y+gitC41fNa8KElau1tHVzuLWX5GzEuxMPhAO0tQXQMkg==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-ppc64-gnu": { + "version": "1.2.14", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.2.14.tgz", + "integrity": "sha512-4+Kl8daPV8NDHaD7mfCcc1pRM+Qg7ab8DyMm5FG5WoZgv4h2i3KFN1CybYBNbQqmuYm7ftJLvmMI1eHYJ22S/Q==", + "cpu": [ + "ppc64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-s390x-gnu": { + "version": "1.2.14", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.2.14.tgz", + "integrity": "sha512-V/h8ePq1fKDrmtFMz6xcCN3DKvuvMRrwwD1q0PkBeeo4DpK64biaKvqWGVxqCeYKqeRk7gE7/iKPxLCn08O97A==", + "cpu": [ + "s390x" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-x64-gnu": { + "version": "1.2.14", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.2.14.tgz", + "integrity": "sha512-WxalXzgrLVpKC93xfpaJ3gCXUWzeCr+FRCbDVD2mUAy4L6Vo3lNVz0epUEUpxPWJkLNYUwM4OSIikv36OJ9mLg==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-x64-musl": { + "version": "1.2.14", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.2.14.tgz", + "integrity": "sha512-3G3tnAmTpdk9jO3GQtyHXqeFD2SlecfTws/Y3M2mmj9nMBwwuLT4kur8sZKeu7ENUHSpVO3cIsm5A52LEWWwlg==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-openharmony-arm64": { + "version": "1.2.14", + "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.2.14.tgz", + "integrity": "sha512-qyr5XiNGxm2ytx17vvtcJ9nXn6v1gENETi503bJx3OGwZ1tSonj7BosxbMa/xeAfSl5iS9X6KjEWYXLu6v1L7Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "peer": true, + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-win32-arm64-msvc": { + "version": "1.2.14", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.2.14.tgz", + "integrity": "sha512-oA4o5o0S2yB199YyYXDzKUv1Z56Rc+mgOjwm7Z/vJ9Z8igiDPYvoOuDyo6g7AK/ZaF09nPhDKVnGBcYHO11GXA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "peer": true, + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-win32-x64-msvc": { + "version": "1.2.14", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.2.14.tgz", + "integrity": "sha512-9mE94bf2GZUaR/fUSXCMpBoWkctu421Mi0ORpm4zqfbkjKep7RxzPFa5etcGs5h+2KApoWBOA/hAJa/7saGgEQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "peer": true, + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.1.tgz", + "integrity": "sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/@types/chai": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.3.tgz", + "integrity": "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/deep-eql": "*", + "assertion-error": "^2.0.1" + } + }, + "node_modules/@types/deep-eql": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz", + "integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/estree": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", + "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "22.20.5", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.20.5.tgz", + "integrity": "sha512-U2+DNr+wSjpsTS/wZGYHq7GcwfuSmKiKvoPvK22zwTlRhU91yOniN4qRR5KhIjvif7ysw/dz/hKmfDH0Ris4aA==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.21.0" + } + }, + "node_modules/@typescript/typescript-aix-ppc64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-aix-ppc64/-/typescript-aix-ppc64-7.0.2.tgz", + "integrity": "sha512-MTKKkWB7p/0E9xi1d1tHtZ5PiLkGEMIq88pK2CubZjOsLtYTLqhgIgi6zepFa+9GHZ6h05NMCkQxGKiPXMxXtQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-darwin-arm64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-darwin-arm64/-/typescript-darwin-arm64-7.0.2.tgz", + "integrity": "sha512-gowzar9MwS/aRWp6f3a4KUqzRjAZjOsmGNCM6LcTgXum+dBfgsBVMN+AgvOCCbguXyick6LJhpBszxMebJ8syA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-darwin-x64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-darwin-x64/-/typescript-darwin-x64-7.0.2.tgz", + "integrity": "sha512-SZ9xZInqApNlNGc9s0W1VSsktYSOe9cFqNOIqmN1Gs8SmkjKZYFt017G4VwPxASInODuAdbTW7sXiFUf893RgA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-freebsd-arm64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-freebsd-arm64/-/typescript-freebsd-arm64-7.0.2.tgz", + "integrity": "sha512-W5NH4y/J0plIIS5b2xvTEkU7JFxyqdMAOgf+Ilhl0vHQXKO5dZoxd+C/jEtq56c4F3wk71RB4BMRQ2XdI+bwYQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-freebsd-x64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-freebsd-x64/-/typescript-freebsd-x64-7.0.2.tgz", + "integrity": "sha512-UMGDx5sTpzNw3WiPebH7l90IWfJggEd+egHt/q6p7/Cm3zqoV7VxkGXt+3DxPIw8CcmvAB0j3sVVfbhX+M4Tpw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-linux-arm": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-arm/-/typescript-linux-arm-7.0.2.tgz", + "integrity": "sha512-gffT3xPz9sR7j/YJExkyPntrI0P2EP9XbOyWzth2/Gs0RstK+90RBcO0ncXoXy/beYll1SXw846Nf2zdnEz0QQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-linux-arm64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-arm64/-/typescript-linux-arm64-7.0.2.tgz", + "integrity": "sha512-Qh4eU4/y3yDjnfjjyPYihMj5/ODIlmt+Bzu17OI+fiSRDW57QmU5SiN63exPRNJPKUzcc1INa1NXdrJ+MqHjUQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-linux-loong64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-loong64/-/typescript-linux-loong64-7.0.2.tgz", + "integrity": "sha512-uEHck9i8hoAzXPiYRib1O7miOnz23SxIeVl6F4LXox+qov1K35jHcEW6VHKvZI+pyvl7fZEP4MCU5LYvIq1GuQ==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-linux-mips64el": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-mips64el/-/typescript-linux-mips64el-7.0.2.tgz", + "integrity": "sha512-R4KvAMnE43W5Qeqb0Ly56O3mWMWIAgsMyz36DCaycd5nbg/9kzm0liw3JocfRqyJY0KPmzFjbswozXyW0DnIYA==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-linux-ppc64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-ppc64/-/typescript-linux-ppc64-7.0.2.tgz", + "integrity": "sha512-DORx5b3sd/4S7eayxm4FQv+A7CrkUIGRaHiwI8oiHTAI1fAPWhF4J0vAlkC8biAlHSVVwxMQ3tjZ2/DVbnQiiA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-linux-riscv64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-riscv64/-/typescript-linux-riscv64-7.0.2.tgz", + "integrity": "sha512-wf0jqEDOjrPRnKwYRyyJDRo11KMbvMFrU+q4zqKyChODBzvlkbhNQfKvLxQCcwTpdDaXSHZTVuh0JoCrKCUMHQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-linux-s390x": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-s390x/-/typescript-linux-s390x-7.0.2.tgz", + "integrity": "sha512-IkwJc3L7yhytWd/ewjyxNDfOmswCm9GWMJT/ue/dU4aZNbwZeYAetq42VyLmsmSjvoX7z74X6ZaYCtzAr0EuGw==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-linux-x64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-x64/-/typescript-linux-x64-7.0.2.tgz", + "integrity": "sha512-EYdf2cNg7rgCWJnxCdJ+F3V39O8ihb37eHAu1LK8oAFizgTQbPOK7zHHXbPt8rX24COqODXeI3sIf0fCXG7H/A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-netbsd-arm64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-netbsd-arm64/-/typescript-netbsd-arm64-7.0.2.tgz", + "integrity": "sha512-+polYF4MF04aPpO5FTkHran9yUQDSXqy5GiSDKpsll5jy3l3+g9QLhpf39T+ePtefhXLOGrLl0QIjkQP6VnelA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-netbsd-x64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-netbsd-x64/-/typescript-netbsd-x64-7.0.2.tgz", + "integrity": "sha512-8YIT0EHM/3dq10ZOVF/A7pc/YSMtbcecct4rWtexrnSCHOPcpC2KTLXfTCR6vDpnSiY12heNb1GiN/wu+T/FyA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-openbsd-arm64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-openbsd-arm64/-/typescript-openbsd-arm64-7.0.2.tgz", + "integrity": "sha512-APT8+ClYnuYm1u9+kgGXoMj2VzWzcymwh2gNSQVySHfkRDGOTVkoWLjCmOQSaO+PoqQ57B0flRp9SA+7GnnkzQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-openbsd-x64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-openbsd-x64/-/typescript-openbsd-x64-7.0.2.tgz", + "integrity": "sha512-yX7s+Q0Dln0Dt9tEzZsAjXXR/+ytBM7AlglaqyeMPxQszJ1JhlJdZ6jLA+IzldHtflX81em7lDao1xXu+aRRkg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-sunos-x64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-sunos-x64/-/typescript-sunos-x64-7.0.2.tgz", + "integrity": "sha512-dLJDGaLZ1D4HPQn62u1n8mBDkJREwMsAkCdkwd4Ieqw+x3TUyTsqY0YiBCtE6H6OzzgGk3iuZ3vFWRS+E8/d1g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-win32-arm64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-win32-arm64/-/typescript-win32-arm64-7.0.2.tgz", + "integrity": "sha512-Gyl1Vy6OsWesLzmq+EP0Fb7b4Nid5232AvcA2SFcdYreldpNtYFFofPjnt62y9hQy7VTaZp65ICJjuAQRaVcIQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-win32-x64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-win32-x64/-/typescript-win32-x64-7.0.2.tgz", + "integrity": "sha512-0BQ3HkAHHlKLSp1qRvf3SUhGpGsDuhB/jgFw75guyqbxJqEaS0Cw/VFO8i2nHglJUzQCRtMMR/IBAKE3ETMC4g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@vitest/mocker": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-5.0.5.tgz", + "integrity": "sha512-xLL3lkDegJZw+MGoGaCCsFayDZthGgpAtr3Vziyfc4p7xH89K7KRPcRBBh/kEYm0Aqkt5DtTmkYpBtRV/FrHZg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "0.3.31", + "@vitest/spy": "5.0.5", + "estree-walker": "^3.0.3", + "magic-string": "^1.4.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "msw": "^2.4.9 || ^3.0.0", + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "msw": { + "optional": true + }, + "vite": { + "optional": true + } + } + }, + "node_modules/@vitest/spy": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-5.0.5.tgz", + "integrity": "sha512-lwJRExuH417257Q5wv9QYugN9IMJkoxcrKrYxGPQkBzl7wlNpTUxD/5yNX4ESKVUIShKPgkGchYPFMWOOk0wzw==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/assertion-error": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", + "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + } + }, + "node_modules/chai": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/chai/-/chai-6.3.0.tgz", + "integrity": "sha512-XWAtwJ6OHO+tj0EKCs0Y2UamnyOxseZWltU4x2U2wh8g4AigdjwvtUjvLP2tqkA/avxHEtzxNaqGq/YGNwckKg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "dev": true, + "license": "Apache-2.0", + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/es-module-lexer": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.3.2.tgz", + "integrity": "sha512-poHGpORABojJJucnV9KbOavETW8lBVnphkW77ER5/BQ5Fz7oXSoCNek7IH3vR5nRjdsEz926ibFYX8KtLQmdyw==", + "dev": true, + "license": "MIT" + }, + "node_modules/estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0" + } + }, + "node_modules/eventsource": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-3.0.7.tgz", + "integrity": "sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA==", + "dev": true, + "license": "MIT", + "dependencies": { + "eventsource-parser": "^3.0.1" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/eventsource-parser": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/eventsource-parser/-/eventsource-parser-3.1.1.tgz", + "integrity": "sha512-EKN1vKAMcZ8MlYMpaNuxN6R9yakzH6uajHcHVTqWJzvu5pWw9DyhbP35HH8MVBQ+dZjAfDxk+A8NiR9KWaXiyQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/expect-type": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.4.0.tgz", + "integrity": "sha512-KfYbmpRm0VbLjEvVa9yGwCi9GI34xvi7A/HXYWQO65CSD2u3MczUJSuwXKFIxlGsgBQizV9q5J9NHj4VG0n+pA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "peer": true, + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/jose": { + "version": "6.2.12", + "resolved": "https://registry.npmjs.org/jose/-/jose-6.2.12.tgz", + "integrity": "sha512-9NiFmJEex0sy2Dk58j2UGBSHgUs2ypF9eZSu4L6vjOX3Dp96Sw1F3uL+H+D1sx02jZZdzUT0HgvCy59CuvXcWw==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, + "node_modules/lightningcss": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.33.0.tgz", + "integrity": "sha512-WkUDrojuJs0xkgGf2udWxa3yGBRxPtxUkB79i6aCZLRgc7PM8fZe9TosfPDcvEpQZbuFASnHYmRLBLUbmLOIIA==", + "dev": true, + "license": "MPL-2.0", + "peer": true, + "dependencies": { + "detect-libc": "^2.0.3" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "lightningcss-android-arm64": "1.33.0", + "lightningcss-darwin-arm64": "1.33.0", + "lightningcss-darwin-x64": "1.33.0", + "lightningcss-freebsd-x64": "1.33.0", + "lightningcss-linux-arm-gnueabihf": "1.33.0", + "lightningcss-linux-arm64-gnu": "1.33.0", + "lightningcss-linux-arm64-musl": "1.33.0", + "lightningcss-linux-x64-gnu": "1.33.0", + "lightningcss-linux-x64-musl": "1.33.0", + "lightningcss-win32-arm64-msvc": "1.33.0", + "lightningcss-win32-x64-msvc": "1.33.0" + } + }, + "node_modules/lightningcss-android-arm64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.33.0.tgz", + "integrity": "sha512-gEpRTalKdosp4Bb8qWtc2iOgE5SeIHlpS1up9bFq2wAyYhl1UdTObYiHe98zEM9SQvSoqQZ1IQD0JNpg3Ml5pg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "android" + ], + "peer": true, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-arm64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.33.0.tgz", + "integrity": "sha512-Sciaz8eenNTKn9b3t7+xr0ipTp9YxKQY4npwQ3mrRuL0BAVHBLyZxofhaKBAVtzmtRZ/zTyo0/to4B1uWG/Djg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "peer": true, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-x64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.33.0.tgz", + "integrity": "sha512-Z5UPAxzrjlWNNyGy6i65cJzzvgJ5D3T6wMvs+gWpY9d7qRhANrxqAp6LhxIgZhWEw18RfJTGcRxjuLIBr+m8XQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "peer": true, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-freebsd-x64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.33.0.tgz", + "integrity": "sha512-QQM/Ti/hQajJwCY+RiWuCZ9sdtI/XQk7nDK5vC8kkdwixezOlDgvDx7+RT+QjK6FcFT4MpsuoBnHIo/O3StRRg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "peer": true, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.33.0.tgz", + "integrity": "sha512-N7FVBe6iS24MlM6R/4RBTxGhQheZGs7tiQ9U32UtF75NzP5Q7xWPRqLBCKxlRQRk3rY1jCIPLzx7WzOhuUIRLQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.33.0.tgz", + "integrity": "sha512-j2v/itmy4HlNxlc6voKXYgBqNi0Ng2LShg4z7GufpEgs05P+2suBVyi9I6YHq5uoVFx9ETin3eCEhLVyXGQnKg==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-musl": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.33.0.tgz", + "integrity": "sha512-yiO5ROMuYQgXbC60yjZU5CYSFZGKXL0HFATXt9mHJn1+zW55oCtMI9NfcVhYLMFDL7gV7oBPon/EmMMGg2OvtQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-gnu": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.33.0.tgz", + "integrity": "sha512-ar+Ju7LmcN0Jo4FpL4hpFybwNG9/3A/Br5KW2n2jyODg3MEZXaDYADdemoNS+BDNfMgKvylJLj4S5tyRActuAg==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-musl": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.33.0.tgz", + "integrity": "sha512-RYiYbkokw0trfKqqzfF55lginwEPrD3OJDfTuJzFs1MK6iFnDenaz1fqLLtX4ITG3OktJQXOeTaw1awrBAlZPw==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-arm64-msvc": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.33.0.tgz", + "integrity": "sha512-1K+MPfLSFVpphzpdbfkhlWk6wBrTObBzS2T6db10PNOZgR9GoVsAWzwNyuhUYYbTp23j+4RrncfujZ4uAzXvwA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "peer": true, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-x64-msvc": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.33.0.tgz", + "integrity": "sha512-OlEICDx/Xl0FqSp4bry8zFnCvGpig3Gl4gCquvYwHuqJKEC1+n9NgDniFvqHGmMv1ZkqDJrDqKKSykTDX+ehuA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "peer": true, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/magic-string": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-1.4.3.tgz", + "integrity": "sha512-z12OxmaPGE0F4xlpGdjuAUckipLpQlTAuAmyGhNoD7ODpYUzvDfvtxUbjM/jwznyP178oju/KDdyi220wNJ0RQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.6.0" + } + }, + "node_modules/nanoid": { + "version": "3.3.20", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.20.tgz", + "integrity": "sha512-uKdg2G3GNCKQn9byYOpxbGqrT2fGO5KRt5J/8b3pok8rT6qxGWF6hxMyJiEYtAf+FVyYuD9hRaDqX5uPFYJ4ZQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "peer": true, + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/obug": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/obug/-/obug-3.0.0.tgz", + "integrity": "sha512-5vvB5+W7ePv+p3uqxi+RcW1XAzLW0/hxt3/4X4Lc4qHudzOhmBiBwOY6DRob4WnanAEGvNcLjF+KNOufrUoEQw==", + "dev": true, + "funding": [ + "https://github.com/sponsors/sxzz", + "https://opencollective.com/debug" + ], + "license": "MIT", + "engines": { + "node": ">=12.20.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC", + "peer": true + }, + "node_modules/picomatch": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.7.tgz", + "integrity": "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pkce-challenge": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/pkce-challenge/-/pkce-challenge-5.0.1.tgz", + "integrity": "sha512-wQ0b/W4Fr01qtpHlqSqspcj3EhBvimsdh0KlHhH8HRZnMsEa0ea2fTULOXOS9ccQr3om+GcGRk4e+isrZWV8qQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/postcss": { + "version": "8.5.30", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.30.tgz", + "integrity": "sha512-zkd+e9S1AQHhmaBKye9zlFHWjOwVgqXl1q3ECYy8561K7qxR2cREf/STLskulBFd78k6AOFeA+ff1TtEzk9bAA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "peer": true, + "dependencies": { + "nanoid": "^3.3.20", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.2" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/rolldown": { + "version": "1.2.14", + "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.2.14.tgz", + "integrity": "sha512-eTBTTqMR6uHKLgcUqpJP8ntzDI6V4LYA8negAMBEI7gkgVvOdrx6VsvS2Ym+83KhOPdLCHWJu0w7UQFJZhHKNw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@oxc-project/types": "=0.154.0", + "@rolldown/pluginutils": "^1.0.0" + }, + "bin": { + "rolldown": "bin/cli.mjs" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "optionalDependencies": { + "@rolldown/binding-android-arm-eabi": "1.2.14", + "@rolldown/binding-android-arm64": "1.2.14", + "@rolldown/binding-darwin-arm64": "1.2.14", + "@rolldown/binding-darwin-x64": "1.2.14", + "@rolldown/binding-freebsd-x64": "1.2.14", + "@rolldown/binding-linux-arm-gnueabihf": "1.2.14", + "@rolldown/binding-linux-arm64-gnu": "1.2.14", + "@rolldown/binding-linux-arm64-musl": "1.2.14", + "@rolldown/binding-linux-ppc64-gnu": "1.2.14", + "@rolldown/binding-linux-s390x-gnu": "1.2.14", + "@rolldown/binding-linux-x64-gnu": "1.2.14", + "@rolldown/binding-linux-x64-musl": "1.2.14", + "@rolldown/binding-openharmony-arm64": "1.2.14", + "@rolldown/binding-win32-arm64-msvc": "1.2.14", + "@rolldown/binding-win32-x64-msvc": "1.2.14" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/source-map-js": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.2.tgz", + "integrity": "sha512-KGj/8Y43x35aZVDtt+J4mK1hoLGHULMYfSkODJNQjNDC3oW1PqPoxMwo0pLUsWM/UEGzON/NxeHywEfNXNP3Vw==", + "dev": true, + "license": "BSD-3-Clause", + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/std-env": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-4.3.0.tgz", + "integrity": "sha512-OtU/EgQ1kIm5KwqQpBC6ZEMXrZRui11w8zgfTWp8cdO9B8OaPsbA8bTHO2P+HNo1VlUTGMVBwPhydu6poeXiag==", + "dev": true, + "license": "MIT" + }, + "node_modules/tinybench": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-6.2.2.tgz", + "integrity": "sha512-nIZ2N2dFpjBsg2hgitKu7mA+FvsIJuGJOwysRngmVRRmWgR/UyPjwJj8c/fT/dhm8EkHnF5EOogRf+0Z2qrimA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/tinyexec": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.3.1.tgz", + "integrity": "sha512-GCvB3aoys96IuDFBMcTB46JOR6mdMtAToqwiW8JlWhsoh1mhHi/xn9ss/Dg7N555GiJyEt2qzoG/NHCwM6h1EA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.17", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", + "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.4" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/typescript": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-7.0.2.tgz", + "integrity": "sha512-8FYau96o3NKOhbjKi/qNvG/W5jhzxkbdm5sj9AbZ/5T5sWqn3hJgLfGx27sRKZWTvyzCP8dLRBTf5tBTSRVUNA==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc" + }, + "engines": { + "node": ">=16.20.0" + }, + "optionalDependencies": { + "@typescript/typescript-aix-ppc64": "7.0.2", + "@typescript/typescript-darwin-arm64": "7.0.2", + "@typescript/typescript-darwin-x64": "7.0.2", + "@typescript/typescript-freebsd-arm64": "7.0.2", + "@typescript/typescript-freebsd-x64": "7.0.2", + "@typescript/typescript-linux-arm": "7.0.2", + "@typescript/typescript-linux-arm64": "7.0.2", + "@typescript/typescript-linux-loong64": "7.0.2", + "@typescript/typescript-linux-mips64el": "7.0.2", + "@typescript/typescript-linux-ppc64": "7.0.2", + "@typescript/typescript-linux-riscv64": "7.0.2", + "@typescript/typescript-linux-s390x": "7.0.2", + "@typescript/typescript-linux-x64": "7.0.2", + "@typescript/typescript-netbsd-arm64": "7.0.2", + "@typescript/typescript-netbsd-x64": "7.0.2", + "@typescript/typescript-openbsd-arm64": "7.0.2", + "@typescript/typescript-openbsd-x64": "7.0.2", + "@typescript/typescript-sunos-x64": "7.0.2", + "@typescript/typescript-win32-arm64": "7.0.2", + "@typescript/typescript-win32-x64": "7.0.2" + } + }, + "node_modules/undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/vite": { + "version": "8.3.4", + "resolved": "https://registry.npmjs.org/vite/-/vite-8.3.4.tgz", + "integrity": "sha512-IftLE8phJhdNRIGNEV4JCCvfNScoZQVfcXaN8TQropWGJtOHcu+iAB9pHcuGvmEb/kddw0LBe/jS8ztdC6xdiQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "lightningcss": "^1.33.0", + "picomatch": "^4.0.7", + "postcss": "^8.5.29", + "rolldown": "~1.2.12", + "tinyglobby": "^0.2.17" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "@vitejs/devtools": "^0.7.1", + "esbuild": "^0.27.0 || ^0.28.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "@vitejs/devtools": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/vitest": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-5.0.5.tgz", + "integrity": "sha512-cPODOMF8YnkiiYtDdHo6n4wWmBrOBBbOIYH2d33M4l3CdNn72h+qMmO6uXffUN6A5AZFXNND/d+krS9mgkXRfg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/chai": "^5.2.2", + "@vitest/mocker": "5.0.5", + "chai": "^6.3.0", + "es-module-lexer": "^2.3.2", + "expect-type": "^1.4.0", + "magic-string": "^1.4.3", + "obug": "^3.0.0", + "picomatch": "^4.0.7", + "std-env": "^4.3.0", + "tinybench": "^6.2.2", + "tinyexec": "^1.3.1", + "tinyglobby": "^0.2.17", + "why-is-node-running": "3.2.1" + }, + "bin": { + "vitest": "vitest.mjs" + }, + "engines": { + "node": "^22.12.0 || ^24.0.0 || >=26.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@edge-runtime/vm": "*", + "@opentelemetry/api": "^1.9.0", + "@types/node": "^22.0.0 || >=24.0.0", + "@vitest/browser-playwright": "5.0.5", + "@vitest/browser-preview": "5.0.5", + "@vitest/browser-webdriverio": "^5.0.0-beta.5 || >=5.0.0", + "@vitest/coverage-istanbul": "5.0.5", + "@vitest/coverage-v8": "5.0.5", + "@vitest/ui": "5.0.5", + "happy-dom": "*", + "jsdom": "*", + "vite": "^6.4.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "@edge-runtime/vm": { + "optional": true + }, + "@opentelemetry/api": { + "optional": true + }, + "@types/node": { + "optional": true + }, + "@vitest/browser-playwright": { + "optional": true + }, + "@vitest/browser-preview": { + "optional": true + }, + "@vitest/browser-webdriverio": { + "optional": true + }, + "@vitest/coverage-istanbul": { + "optional": true + }, + "@vitest/coverage-v8": { + "optional": true + }, + "@vitest/ui": { + "optional": true + }, + "happy-dom": { + "optional": true + }, + "jsdom": { + "optional": true + }, + "vite": { + "optional": false + } + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/why-is-node-running": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-3.2.1.tgz", + "integrity": "sha512-Tb2FUhB4vUsGQlfSquQLYkApkuPAFQXGFzxWKHHumVz2dK+X1RUm/HnID4+TfIGYJ1kTcwOaCk/buYCEJr6YjQ==", + "dev": true, + "license": "MIT", + "bin": { + "why-is-node-running": "cli.js" + }, + "engines": { + "node": ">=20.11" + } + }, + "node_modules/zod": { + "version": "4.6.7", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.6.7.tgz", + "integrity": "sha512-g8W2D2kU6a8untc053fX1uVBKzM7yqAxW/3zwk83+6RyF2+SKOuV+c0wBDhUwzV1PaGapmZH+rmcUzPf+3CGwg==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + } + } +} diff --git a/mcp/package.json b/mcp/package.json new file mode 100644 index 0000000..9d423be --- /dev/null +++ b/mcp/package.json @@ -0,0 +1,54 @@ +{ + "name": "@fiscalapi/mcp", + "version": "0.1.0", + "description": "Servidor MCP de solo lectura con el catálogo de operaciones de la API de FiscalAPI (CFDI 4.0)", + "type": "module", + "bin": { + "fiscalapi-mcp": "dist/index.js" + }, + "files": [ + "dist", + "data", + "README.md" + ], + "scripts": { + "build:data": "node scripts/build-data.mjs", + "build": "tsc -p tsconfig.json", + "test": "vitest run" + }, + "keywords": [ + "fiscalapi", + "mcp", + "cfdi", + "factura", + "sat", + "mexico" + ], + "author": "FISCAL API S DE R.L DE C.V", + "license": "MPL-2.0", + "homepage": "https://docs.fiscalapi.com", + "repository": { + "type": "git", + "url": "git+https://github.com/FiscalAPI/fiscalapi-ai.git", + "directory": "mcp" + }, + "bugs": { + "url": "https://github.com/FiscalAPI/fiscalapi-ai/issues" + }, + "engines": { + "node": ">=22" + }, + "publishConfig": { + "access": "public" + }, + "dependencies": { + "@modelcontextprotocol/server": "2.3.1", + "zod": "4.6.7" + }, + "devDependencies": { + "@modelcontextprotocol/client": "2.3.1", + "@types/node": "22.20.5", + "typescript": "7.0.2", + "vitest": "5.0.5" + } +} diff --git a/mcp/scripts/build-data.mjs b/mcp/scripts/build-data.mjs new file mode 100644 index 0000000..9a5d5fc --- /dev/null +++ b/mcp/scripts/build-data.mjs @@ -0,0 +1,79 @@ +// Genera mcp/data desde el OpenAPI público de FiscalAPI. +// +// Uso: node scripts/build-data.mjs [fuente] [--out ] +// fuente: URL http(s) o ruta de archivo. Por omisión FISCALAPI_OPENAPI_SOURCE o el OpenAPI de live. +// --out: directorio de salida. Por omisión mcp/data. +import { createHash } from 'node:crypto'; +import { mkdir, readFile, rename, rm, writeFile } from 'node:fs/promises'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; +import { parseArgs } from 'node:util'; + +import { buildCatalog, buildOperation, extractOperations, OpenApiError, parseOpenApi, serializeOperation } from './openapi.mjs'; + +const DEFAULT_SOURCE = 'https://live.fiscalapi.com/api/openapi/v4.json'; +const DEFAULT_OUT_DIR = fileURLToPath(new URL('../data', import.meta.url)); +const FETCH_TIMEOUT_MS = 60_000; + +async function main() { + const { positionals, values } = parseArgs({ + allowPositionals: true, + options: { out: { type: 'string' } }, + }); + const source = positionals[0] ?? process.env.FISCALAPI_OPENAPI_SOURCE ?? DEFAULT_SOURCE; + const outDir = path.resolve(values.out ?? DEFAULT_OUT_DIR); + + const text = await readSource(source); + const doc = parseOpenApi(text); + const operations = extractOperations(doc); + const catalog = buildCatalog(operations); + + const staging = `${outDir}.tmp-${process.pid}`; + await rm(staging, { recursive: true, force: true }); + await mkdir(path.join(staging, 'operations'), { recursive: true }); + let largest = { id: '', chars: 0 }; + for (const entry of operations) { + const json = serializeOperation(buildOperation(doc, entry)); + if (json.length > largest.chars) largest = { id: entry.id, chars: json.length }; + await writeFile(path.join(staging, 'operations', `${entry.id}.json`), json); + } + await writeFile(path.join(staging, 'catalog.json'), JSON.stringify(catalog)); + const sha256 = createHash('sha256').update(text).digest('hex'); + await writeFile(path.join(staging, 'source.json'), JSON.stringify({ source, sha256, operations: catalog.length }, null, 2)); + + // Se reemplaza de golpe para que un build fallido nunca deje data/ a medias. + await rm(outDir, { recursive: true, force: true }); + await rename(staging, outDir); + + console.error(`build-data: ${catalog.length} operaciones en ${outDir}`); + console.error(`build-data: la más grande es ${largest.id} con ${largest.chars} caracteres`); + console.error(`build-data: sha256 ${sha256}`); +} + +async function readSource(source) { + if (!/^https?:\/\//i.test(source)) { + try { + return await readFile(source, 'utf8'); + } catch (error) { + throw new OpenApiError(`no se pudo leer ${source}: ${error.message}`); + } + } + let response; + try { + response = await fetch(source, { signal: AbortSignal.timeout(FETCH_TIMEOUT_MS) }); + } catch (error) { + throw new OpenApiError(`no se pudo descargar ${source}: ${error.message}`); + } + if (response.status !== 200) { + throw new OpenApiError(`${source} respondió HTTP ${response.status}; se esperaba 200.`); + } + return response.text(); +} + +try { + await main(); +} catch (error) { + const message = error instanceof OpenApiError ? error.message : (error?.stack ?? String(error)); + console.error(`build-data: ${message}`); + process.exitCode = 1; +} diff --git a/mcp/scripts/openapi.mjs b/mcp/scripts/openapi.mjs new file mode 100644 index 0000000..127caab --- /dev/null +++ b/mcp/scripts/openapi.mjs @@ -0,0 +1,258 @@ +// Transformación pura del OpenAPI de FiscalAPI a los datos que sirve el MCP: sin red ni disco. + +export const MAX_OPERATION_CHARS = 40_000; + +const HTTP_METHODS = ['get', 'put', 'post', 'delete', 'options', 'head', 'patch', 'trace']; +// El operationId se vuelve nombre de archivo: solo se aceptan ids que no puedan salir de data/operations. +const SAFE_OPERATION_ID = /^[A-Za-z][A-Za-z0-9_]*$/; +const PREFERRED_MEDIA_TYPE = 'application/json'; +const DEFAULT_TAG = 'General'; + +export class OpenApiError extends Error {} + +export function parseOpenApi(text) { + let doc; + try { + doc = JSON.parse(text); + } catch { + throw new OpenApiError('la fuente no es JSON válido.'); + } + if (!isObject(doc) || typeof doc.openapi !== 'string' || !doc.openapi.startsWith('3.') || !isObject(doc.paths)) { + throw new OpenApiError('la fuente no es un documento OpenAPI 3 (faltan "openapi" o "paths").'); + } + return doc; +} + +export function extractOperations(doc) { + const operations = []; + const seen = new Set(); + for (const [path, pathItem] of Object.entries(doc.paths)) { + for (const method of HTTP_METHODS) { + const operation = pathItem[method]; + if (!isObject(operation)) continue; + const id = operation.operationId; + if (typeof id !== 'string' || !SAFE_OPERATION_ID.test(id)) { + throw new OpenApiError(`${method.toUpperCase()} ${path} no tiene un operationId válido (${JSON.stringify(id)}).`); + } + if (seen.has(id)) throw new OpenApiError(`el operationId "${id}" está repetido.`); + seen.add(id); + operations.push({ id, method: method.toUpperCase(), path, pathItem, operation }); + } + } + if (operations.length === 0) throw new OpenApiError('el documento no tiene operaciones.'); + return operations; +} + +export function buildCatalog(operations) { + return operations + .map(({ id, method, path, operation }) => ({ + id, + method, + path, + summary: operation.summary ?? '', + tag: operation.tags?.[0] ?? DEFAULT_TAG, + })) + .sort((a, b) => compare(a.tag, b.tag) || compare(a.id, b.id)); +} + +/** + * Resuelve los $ref de la operación y la recorta, en etapas deterministas, hasta que su JSON + * mida como máximo maxChars. Lanza OpenApiError si ni el recorte más agresivo cabe. + */ +export function buildOperation(doc, entry, maxChars = MAX_OPERATION_CHARS) { + const full = new OperationResolver(doc, {}).resolve(entry); + if (fits(full.value, maxChars)) return full.value; + + const withoutExamples = { dropExamples: true }; + let candidate = new OperationResolver(doc, withoutExamples).resolve(entry).value; + if (fits(candidate, maxChars)) return candidate; + + for (let depth = full.maxDepth; depth >= 1; depth--) { + candidate = new OperationResolver(doc, { ...withoutExamples, descriptionDepth: depth }).resolve(entry).value; + if (fits(candidate, maxChars)) return candidate; + } + + for (let depth = full.maxDepth; depth >= 0; depth--) { + const options = { ...withoutExamples, descriptionDepth: 1, refDepth: depth }; + candidate = new OperationResolver(doc, options).resolve(entry).value; + if (fits(candidate, maxChars)) return candidate; + } + throw new OpenApiError(`la operación "${entry.id}" mide más de ${maxChars} caracteres aun recortada.`); +} + +export function serializeOperation(operation) { + return JSON.stringify(operation); +} + +/** + * Resuelve una operación con unas opciones de recorte fijas. Profundidad 0 es la raíz del esquema + * de un parámetro, cuerpo o respuesta; cada nivel de properties, items o additionalProperties suma uno. + */ +class OperationResolver { + #doc; + #dropExamples; + #descriptionDepth; + #refDepth; + #maxDepth = 0; + + constructor(doc, { dropExamples = false, descriptionDepth = Infinity, refDepth = Infinity }) { + this.#doc = doc; + this.#dropExamples = dropExamples; + this.#descriptionDepth = descriptionDepth; + this.#refDepth = refDepth; + } + + resolve({ id, method, path, pathItem, operation }) { + const parameters = [...(pathItem.parameters ?? []), ...(operation.parameters ?? [])]; + const value = { + operationId: id, + method, + path, + tag: operation.tags?.[0] ?? DEFAULT_TAG, + summary: operation.summary ?? '', + description: operation.description ?? '', + }; + if (operation.deprecated === true) value.deprecated = true; + if (parameters.length > 0) value.parameters = parameters.map((p) => this.#parameter(p)); + if (operation.requestBody) value.requestBody = this.#withContent(this.#deref(operation.requestBody)); + if (operation.responses) value.responses = this.#responses(operation.responses); + const security = operation.security ?? this.#doc.security; + if (security) { + value.security = security; + value.securitySchemes = this.#securitySchemes(security); + } + return { value, maxDepth: this.#maxDepth }; + } + + #parameter(parameter) { + const { schema, ...rest } = this.#withoutExamples(this.#deref(parameter)); + return schema === undefined ? rest : { ...rest, schema: this.#schema(schema, 0, []) }; + } + + #responses(responses) { + const result = {}; + for (const [code, response] of Object.entries(responses)) { + const resolved = this.#withContent(this.#deref(response)); + if (isObject(resolved.headers)) { + resolved.headers = mapValues(resolved.headers, (header) => this.#parameter(header)); + } + result[code] = resolved; + } + return result; + } + + #withContent(holder) { + if (!isObject(holder.content)) return { ...holder }; + const content = {}; + for (const [mediaType, media] of Object.entries(collapseMediaTypes(holder.content))) { + const { schema, ...rest } = this.#withoutExamples(media); + content[mediaType] = schema === undefined ? rest : { ...rest, schema: this.#schema(schema, 0, []) }; + } + return { ...holder, content }; + } + + #schema(schema, depth, stack) { + if (!isObject(schema)) return schema; + this.#maxDepth = Math.max(this.#maxDepth, depth); + + if (typeof schema.$ref === 'string') { + const { $ref, ...siblings } = schema; + const name = refName($ref); + if (stack.includes($ref)) return { type: 'object', 'x-recursivo': name }; + if (depth > this.#refDepth) return { type: 'object', 'x-recortado': name }; + return this.#schema({ ...this.#pointer($ref), ...siblings }, depth, [...stack, $ref]); + } + + const result = {}; + for (const [key, value] of Object.entries(schema)) { + if (this.#dropExamples && (key === 'example' || key === 'examples')) continue; + if (key === 'description' && depth >= this.#descriptionDepth) continue; + result[key] = this.#schemaKeyword(key, value, depth, stack); + } + return result; + } + + #schemaKeyword(key, value, depth, stack) { + switch (key) { + case 'properties': + case 'patternProperties': + return isObject(value) ? mapValues(value, (child) => this.#schema(child, depth + 1, stack)) : value; + case 'items': + case 'additionalProperties': + case 'not': + return this.#schema(value, depth + 1, stack); + case 'allOf': + case 'oneOf': + case 'anyOf': + case 'prefixItems': + return Array.isArray(value) ? value.map((child) => this.#schema(child, depth, stack)) : value; + default: + return value; + } + } + + #securitySchemes(security) { + const declared = this.#doc.components?.securitySchemes ?? {}; + const names = [...new Set(security.flatMap((requirement) => Object.keys(requirement)))]; + return Object.fromEntries(names.filter((name) => declared[name]).map((name) => [name, declared[name]])); + } + + #withoutExamples(object) { + if (!this.#dropExamples) return { ...object }; + const { example: _example, examples: _examples, ...rest } = object; + return rest; + } + + // Parámetros, cuerpos y respuestas también pueden venir por $ref a components. + #deref(object, seen = new Set()) { + if (!isObject(object) || typeof object.$ref !== 'string') return object; + if (seen.has(object.$ref)) throw new OpenApiError(`$ref circular fuera de un esquema: ${object.$ref}.`); + seen.add(object.$ref); + return this.#deref(this.#pointer(object.$ref), seen); + } + + #pointer(ref) { + if (!ref.startsWith('#/')) throw new OpenApiError(`solo se admiten $ref locales: ${ref}.`); + let target = this.#doc; + for (const segment of ref.slice(2).split('/')) { + const key = decodeURIComponent(segment).replaceAll('~1', '/').replaceAll('~0', '~'); + target = isObject(target) ? target[key] : undefined; + } + if (target === undefined) throw new OpenApiError(`$ref sin destino: ${ref}.`); + return target; + } +} + +// ASP.NET publica el mismo esquema en application/json, text/json, text/plain y application/*+json. +// Se conserva application/json y solo los tipos cuyo contenido es distinto. +function collapseMediaTypes(content) { + const preferred = content[PREFERRED_MEDIA_TYPE]; + if (preferred === undefined) return content; + const preferredJson = JSON.stringify(preferred); + return Object.fromEntries( + Object.entries(content).filter( + ([mediaType, media]) => mediaType === PREFERRED_MEDIA_TYPE || JSON.stringify(media) !== preferredJson, + ), + ); +} + +function fits(operation, maxChars) { + return serializeOperation(operation).length <= maxChars; +} + +function refName(ref) { + return ref.slice(ref.lastIndexOf('/') + 1); +} + +function mapValues(object, fn) { + return Object.fromEntries(Object.entries(object).map(([key, value]) => [key, fn(value)])); +} + +function isObject(value) { + return typeof value === 'object' && value !== null && !Array.isArray(value); +} + +// Orden por unidades de código: no depende del locale ni de la versión de ICU. +function compare(a, b) { + return a < b ? -1 : a > b ? 1 : 0; +} diff --git a/mcp/src/catalog.ts b/mcp/src/catalog.ts new file mode 100644 index 0000000..95d0b1d --- /dev/null +++ b/mcp/src/catalog.ts @@ -0,0 +1,100 @@ +import { readFileSync } from 'node:fs'; +import { readFile } from 'node:fs/promises'; +import { fileURLToPath } from 'node:url'; + +export interface CatalogEntry { + readonly id: string; + readonly method: string; + readonly path: string; + readonly summary: string; + readonly tag: string; +} + +const MAX_SUGGESTIONS = 5; + +/** + * Catálogo de operaciones generado por scripts/build-data.mjs. Solo lee archivos de data/ cuyo + * nombre sale del propio catálogo, nunca de la entrada del usuario. + */ +export class OperationCatalog { + readonly #entries: readonly CatalogEntry[]; + readonly #byId: ReadonlyMap; + readonly #operationsDir: URL; + + private constructor(entries: readonly CatalogEntry[], dataDir: URL) { + this.#entries = entries; + this.#byId = new Map(entries.map((entry) => [entry.id, entry])); + this.#operationsDir = new URL('operations/', dataDir); + } + + /** dataDir debe terminar en "/" para que las rutas relativas se resuelvan dentro de él. */ + static load(dataDir: URL): OperationCatalog { + const catalogFile = new URL('catalog.json', dataDir); + let entries: CatalogEntry[]; + try { + entries = JSON.parse(readFileSync(catalogFile, 'utf8')) as CatalogEntry[]; + } catch (error) { + throw new Error(`No se pudo leer el catálogo de operaciones (${fileURLToPath(catalogFile)}). Corre npm run build:data.`, { + cause: error, + }); + } + return new OperationCatalog(entries, dataDir); + } + + get tags(): string[] { + return [...new Set(this.#entries.map((entry) => entry.tag))]; + } + + /** Las operaciones en el orden del catálogo (tag, operationId); con tag, solo las de ese tag sin distinguir mayúsculas. */ + list(tag?: string): CatalogEntry[] { + if (tag === undefined) return [...this.#entries]; + const wanted = tag.toLowerCase(); + return this.#entries.filter((entry) => entry.tag.toLowerCase() === wanted); + } + + /** El JSON de la operación, o undefined si el id no está en el catálogo. */ + async readOperation(operationId: string): Promise { + const entry = this.#byId.get(operationId); + if (entry === undefined) return undefined; + return readFile(new URL(`${entry.id}.json`, this.#operationsDir), 'utf8'); + } + + /** + * Hasta 5 ids, sin distinguir mayúsculas: primero los que empiezan con la entrada y luego los que + * la contienen. Si ninguno coincide (un error de dedo como "createInvoce"), los que comparten con + * ella el prefijo más largo, de al menos 3 letras. + */ + suggest(input: string): string[] { + const needle = input.trim().toLowerCase(); + if (needle === '') return []; + const ids = this.#entries.map((entry) => entry.id); + const prefixed = ids.filter((id) => id.toLowerCase().startsWith(needle)); + const containing = ids.filter((id) => !id.toLowerCase().startsWith(needle) && id.toLowerCase().includes(needle)); + const matches = [...prefixed, ...containing]; + if (matches.length > 0) return matches.slice(0, MAX_SUGGESTIONS); + return closestByCommonPrefix(ids, needle).slice(0, MAX_SUGGESTIONS); + } +} + +const MIN_COMMON_PREFIX = 3; + +// Solo los ids con el prefijo común más largo; entre ellos, primero el de longitud más parecida a la entrada. +function closestByCommonPrefix(ids: readonly string[], needle: string): string[] { + const candidates = ids.map((id) => ({ + id, + common: commonPrefixLength(id.toLowerCase(), needle), + lengthGap: Math.abs(id.length - needle.length), + })); + const longest = Math.max(0, ...candidates.map((candidate) => candidate.common)); + if (longest < MIN_COMMON_PREFIX) return []; + return candidates + .filter((candidate) => candidate.common === longest) + .sort((a, b) => a.lengthGap - b.lengthGap) + .map((candidate) => candidate.id); +} + +function commonPrefixLength(a: string, b: string): number { + let length = 0; + while (length < a.length && length < b.length && a[length] === b[length]) length++; + return length; +} diff --git a/mcp/src/index.ts b/mcp/src/index.ts new file mode 100644 index 0000000..644b5fa --- /dev/null +++ b/mcp/src/index.ts @@ -0,0 +1,30 @@ +#!/usr/bin/env node +import { readFileSync } from 'node:fs'; + +import { serveStdio } from '@modelcontextprotocol/server/stdio'; + +import { OperationCatalog } from './catalog.js'; +import { createServer } from './server.js'; + +// stdout es el canal del protocolo: todo diagnóstico va a stderr. +const packageRoot = new URL('../', import.meta.url); + +let catalog: OperationCatalog; +let version: string; +try { + version = (JSON.parse(readFileSync(new URL('package.json', packageRoot), 'utf8')) as { version: string }).version; + catalog = OperationCatalog.load(new URL('data/', packageRoot)); +} catch (error) { + console.error(`fiscalapi-mcp: ${error instanceof Error ? error.message : String(error)}`); + process.exit(1); +} + +const handle = serveStdio(() => createServer(catalog, version), { + onerror: (error) => console.error(`fiscalapi-mcp: ${error.message}`), +}); + +for (const signal of ['SIGINT', 'SIGTERM'] as const) { + process.on(signal, () => { + void handle.close().finally(() => process.exit(0)); + }); +} diff --git a/mcp/src/server.ts b/mcp/src/server.ts new file mode 100644 index 0000000..acd85aa --- /dev/null +++ b/mcp/src/server.ts @@ -0,0 +1,94 @@ +import { McpServer } from '@modelcontextprotocol/server'; +import type { CallToolResult } from '@modelcontextprotocol/server'; +import * as z from 'zod/v4'; + +import type { CatalogEntry, OperationCatalog } from './catalog.js'; + +export const SERVER_NAME = 'fiscalapi'; + +// Las dos herramientas solo leen archivos del paquete: no llaman a la API ni a ningún otro servicio. +const READ_ONLY = { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false } as const; +const MAX_ECHOED_INPUT = 100; + +export function createServer(catalog: OperationCatalog, version: string): McpServer { + const server = new McpServer({ name: SERVER_NAME, version }); + + server.registerTool( + 'listar_operaciones', + { + title: 'Listar operaciones de FiscalAPI', + description: + 'Lista las operaciones de la API REST de FiscalAPI (facturas CFDI 4.0 y complementos, cancelación, descarga masiva del SAT, personas, productos, certificados, webhooks y más), agrupadas por tag: operationId, método, ruta y resumen. Úsala para encontrar el endpoint que necesitas; el siguiente paso es llamar a obtener_operacion con su operationId para ver parámetros, cuerpo y respuestas. No llama a la API ni pide llaves.', + inputSchema: z.object({ + tag: z + .string() + .max(MAX_ECHOED_INPUT) + .optional() + .describe('Tag por el que filtrar, sin distinguir mayúsculas (por ejemplo Invoices, People o Products). Omítelo para ver todas.'), + }), + annotations: READ_ONLY, + }, + async ({ tag }) => listOperations(catalog, tag), + ); + + server.registerTool( + 'obtener_operacion', + { + title: 'Obtener una operación de FiscalAPI', + description: + 'Devuelve en JSON el detalle de una operación de la API de FiscalAPI: método, ruta, descripción con el enlace a su documentación, parámetros, cuerpo y respuestas con los esquemas resueltos. Usa el operationId exacto que da listar_operaciones. No llama a la API ni pide llaves.', + inputSchema: z.object({ + operationId: z + .string() + .min(1) + .max(MAX_ECHOED_INPUT) + .describe('operationId exacto de la operación, tal como aparece en listar_operaciones (por ejemplo createInvoice).'), + }), + annotations: READ_ONLY, + }, + async ({ operationId }) => getOperation(catalog, operationId), + ); + + return server; +} + +function listOperations(catalog: OperationCatalog, tag: string | undefined): CallToolResult { + const entries = catalog.list(tag); + if (entries.length === 0) { + return error(`No hay operaciones con el tag "${tag}". Tags disponibles: ${catalog.tags.join(', ')}.`); + } + const header = + `FiscalAPI: ${entries.length} operaciones. Para ver parámetros, cuerpo y respuestas de una, ` + + 'llama a obtener_operacion con su operationId.'; + return text([header, ...groupByTag(entries)].join('\n')); +} + +async function getOperation(catalog: OperationCatalog, operationId: string): Promise { + const operation = await catalog.readOperation(operationId); + if (operation !== undefined) return text(operation); + + const suggestions = catalog.suggest(operationId); + const hint = suggestions.length > 0 ? `¿Quisiste decir ${suggestions.join(', ')}? ` : ''; + return error(`No existe la operación "${operationId}". ${hint}Usa listar_operaciones para ver los operationId disponibles.`); +} + +function groupByTag(entries: readonly CatalogEntry[]): string[] { + const lines: string[] = []; + let currentTag: string | undefined; + for (const entry of entries) { + if (entry.tag !== currentTag) { + currentTag = entry.tag; + lines.push('', `## ${entry.tag}`); + } + lines.push(`${entry.id} ${entry.method} ${entry.path} ${entry.summary}`); + } + return lines; +} + +function text(value: string): CallToolResult { + return { content: [{ type: 'text', text: value }] }; +} + +function error(message: string): CallToolResult { + return { content: [{ type: 'text', text: message }], isError: true }; +} diff --git a/mcp/test/build-data.test.ts b/mcp/test/build-data.test.ts new file mode 100644 index 0000000..e739885 --- /dev/null +++ b/mcp/test/build-data.test.ts @@ -0,0 +1,212 @@ +import { spawn } from 'node:child_process'; +import { mkdtempSync, rmSync, writeFileSync } from 'node:fs'; +import { createServer } from 'node:http'; +import type { AddressInfo } from 'node:net'; +import { tmpdir } from 'node:os'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; + +import { afterAll, describe, expect, it } from 'vitest'; + +import { + buildCatalog, + buildOperation, + extractOperations, + OpenApiError, + parseOpenApi, + serializeOperation, +} from '../scripts/openapi.mjs'; + +const packageDir = fileURLToPath(new URL('../', import.meta.url)); +// Límite de RNF-05 escrito aquí a propósito: si alguien sube el del build, la prueba lo detecta. +const SPEC_MAX_OPERATION_CHARS = 40_000; +const LONG_TEXT ='Texto largo de prueba para inflar el esquema. '.repeat(8); + +// Un cuerpo que resuelto mide cientos de miles de caracteres: obliga a recortar. +function hugeDoc(): Record { + const leafProperties = Object.fromEntries( + Array.from({ length: 30 }, (_, i) => [`campo${i}`, { type: 'string', description: LONG_TEXT, example: LONG_TEXT }]), + ); + const rootProperties = Object.fromEntries( + Array.from({ length: 40 }, (_, i) => [`hijo${i}`, { $ref: '#/components/schemas/Hijo' }]), + ); + return { + openapi: '3.1.1', + paths: { + '/api/v4/grande': { + post: { + operationId: 'createGrande', + tags: ['Pruebas'], + summary: 'Crear algo grande', + description: 'Operación enorme. Docs: [grande](https://docs.fiscalapi.com/grande).', + requestBody: { + content: { + 'application/json': { schema: { $ref: '#/components/schemas/Grande' } }, + 'text/json': { schema: { $ref: '#/components/schemas/Grande' } }, + }, + }, + responses: { '200': { description: 'OK' } }, + }, + }, + }, + components: { + schemas: { + Grande: { + type: 'object', + description: 'Raíz del cuerpo', + required: ['hijo0'], + properties: { ...rootProperties, estado: { type: 'string', enum: ['A', 'B'], format: 'x-codigo' } }, + }, + Hijo: { type: ['null', 'object'], required: ['campo0'], properties: leafProperties }, + }, + }, + }; +} + +function entryOf(doc: Record, id: string) { + const entry = extractOperations(doc).find((candidate: { id: string }) => candidate.id === id); + if (!entry) throw new Error(`Falta ${id} en el documento de prueba.`); + return entry; +} + +describe('buildOperation', () => { + it('recorta una operación enorme hasta el límite y conserva la estructura y la documentación', () => { + const doc = hugeDoc(); + + const operation = buildOperation(doc, entryOf(doc, 'createGrande')); + const json = serializeOperation(operation); + + expect(json.length).toBeLessThanOrEqual(SPEC_MAX_OPERATION_CHARS); + expect(operation.description).toContain('https://docs.fiscalapi.com/grande'); + expect(operation.summary).toBe('Crear algo grande'); + expect(json).not.toContain('"example"'); + expect(json).not.toContain('"$ref"'); + const body = operation.requestBody.content['application/json'].schema; + expect(Object.keys(operation.requestBody.content)).toEqual(['application/json']); + expect(body.required).toEqual(['hijo0']); + expect(body.properties.estado).toEqual({ type: 'string', enum: ['A', 'B'], format: 'x-codigo' }); + expect(Object.keys(body.properties)).toHaveLength(41); + }); + + it('quita primero ejemplos y descripciones anidadas antes de recortar subárboles', () => { + const doc = hugeDoc(); + const full = serializeOperation(buildOperation(doc, entryOf(doc, 'createGrande'), Infinity)); + + // Cabe sin ejemplos ni descripciones, pero no sin ejemplos solamente. + const operation = buildOperation(doc, entryOf(doc, 'createGrande'), full.length / 3); + const json = serializeOperation(operation); + + expect(json).not.toContain('x-recortado'); + expect(json).not.toContain(LONG_TEXT); + const body = operation.requestBody.content['application/json'].schema; + expect(body.description).toBe('Raíz del cuerpo'); + expect(body.properties.hijo0).toMatchObject({ type: ['null', 'object'], required: ['campo0'] }); + expect(body.properties.hijo0.properties.campo29).toEqual({ type: 'string' }); + }); + + it('como último recurso reemplaza los subárboles por su nombre de esquema', () => { + const doc = hugeDoc(); + + const operation = buildOperation(doc, entryOf(doc, 'createGrande'), 4_000); + + const body = operation.requestBody.content['application/json'].schema; + expect(serializeOperation(operation).length).toBeLessThanOrEqual(4_000); + expect(body.properties.hijo0).toEqual({ type: 'object', 'x-recortado': 'Hijo' }); + expect(body.properties.estado).toEqual({ type: 'string', enum: ['A', 'B'], format: 'x-codigo' }); + }); + + it('falla si la operación no cabe ni recortada', () => { + const doc = hugeDoc(); + + expect(() => buildOperation(doc, entryOf(doc, 'createGrande'), 100)).toThrow(OpenApiError); + }); + + it('corta los esquemas recursivos sin ciclar', () => { + const doc = { + openapi: '3.1.1', + paths: { + '/api/v4/nodos': { + get: { + operationId: 'listNodos', + responses: { + '200': { description: 'OK', content: { 'application/json': { schema: { $ref: '#/components/schemas/Nodo' } } } }, + }, + }, + }, + }, + components: { + schemas: { Nodo: { type: 'object', properties: { hijos: { type: 'array', items: { $ref: '#/components/schemas/Nodo' } } } } }, + }, + }; + + const operation = buildOperation(doc, entryOf(doc, 'listNodos')); + + const schema = operation.responses['200'].content['application/json'].schema; + expect(schema.properties.hijos.items).toEqual({ type: 'object', 'x-recursivo': 'Nodo' }); + }); +}); + +describe('extractOperations y buildCatalog', () => { + it('rechaza un operationId que podría salir de data/operations', () => { + const doc = { openapi: '3.1.1', paths: { '/x': { get: { operationId: '../x', responses: {} } } } }; + + expect(() => extractOperations(doc)).toThrow(/operationId válido/); + }); + + it('ordena el catálogo por tag y luego por operationId', () => { + const doc = { + openapi: '3.1.1', + paths: { + '/b': { get: { operationId: 'zeta', tags: ['B'], summary: 'z' } }, + '/a': { get: { operationId: 'beta', tags: ['A'], summary: 'b' }, post: { operationId: 'alfa', tags: ['A'], summary: 'a' } }, + }, + }; + + expect(buildCatalog(extractOperations(doc)).map((entry: { id: string }) => entry.id)).toEqual(['alfa', 'beta', 'zeta']); + }); + + it('rechaza JSON que no es OpenAPI 3', () => { + expect(() => parseOpenApi('{"swagger":"2.0","paths":{}}')).toThrow(OpenApiError); + expect(() => parseOpenApi('')).toThrow(/no es JSON/); + }); +}); + +describe('scripts/build-data.mjs', () => { + const workDir = mkdtempSync(path.join(tmpdir(), 'fiscalapi-mcp-build-')); + afterAll(() => rmSync(workDir, { recursive: true, force: true })); + + function runBuild(source: string): Promise<{ code: number | null; stderr: string }> { + return new Promise((resolve) => { + const child = spawn(process.execPath, ['scripts/build-data.mjs', source, '--out', path.join(workDir, 'data')], { + cwd: packageDir, + }); + let stderr = ''; + child.stderr.on('data', (chunk) => (stderr += chunk)); + child.on('close', (code) => resolve({ code, stderr })); + }); + } + + it('sale con código 1 si la URL no responde 200', async () => { + const server = createServer((_, response) => response.writeHead(401).end()); + await new Promise((resolve) => server.listen(0, '127.0.0.1', resolve)); + const { port } = server.address() as AddressInfo; + try { + const { code, stderr } = await runBuild(`http://127.0.0.1:${port}/api/openapi/v4.json`); + + expect(code).toBe(1); + expect(stderr).toContain('respondió HTTP 401; se esperaba 200'); + } finally { + server.close(); + } + }); + + it('sale con código 1 si el archivo no es un OpenAPI', async () => { + const notOpenApi = path.join(workDir, 'no-openapi.json'); + writeFileSync(notOpenApi, '{"hola":"mundo"}'); + + const { code, stderr } = await runBuild(notOpenApi); + + expect(code).toBe(1); + expect(stderr).toContain('no es un documento OpenAPI 3'); + }); +}); diff --git a/mcp/test/fixtures/openapi-v4.json b/mcp/test/fixtures/openapi-v4.json new file mode 100644 index 0000000..e4eb2af --- /dev/null +++ b/mcp/test/fixtures/openapi-v4.json @@ -0,0 +1,15077 @@ +{ + "openapi": "3.1.1", + "info": { + "title": "Fiscalapi API", + "description": "API REST para emitir facturas CFDI 4.0 y sus complementos, cancelarlas, validarlas, descargar CFDI del SAT y administrar personas, productos, certificados y créditos. Cada petición es JSON y se autentica con los headers `X-API-KEY` y `X-TENANT-KEY`.\n\nAmbientes: `https://test.fiscalapi.com` (pruebas) y `https://live.fiscalapi.com` (producción).\n\n## Modos de operación\n\n- **[Por valores](https://docs.fiscalapi.com/modes-of-operation):** el cuerpo lleva todos los datos de la factura, incluidos los certificados (CSD) del emisor en base64.\n- **[Por referencias](https://docs.fiscalapi.com/modes-of-operation):** el cuerpo lleva los ids de las personas y los productos ya registrados en tu cuenta, desde el dashboard o la API.\n\nConsulta [cómo obtener tus credenciales](https://docs.fiscalapi.com/credentials-info) y los [datos de prueba](https://docs.fiscalapi.com/testing-data).", + "version": "4.0" + }, + "servers": [ + { + "url": "https://test.fiscalapi.com", + "description": "Pruebas" + }, + { + "url": "https://live.fiscalapi.com", + "description": "Producción" + } + ], + "paths": { + "/api/v4/apikeys": { + "get": { + "tags": [ + "ApiKeys" + ], + "summary": "Listar API keys", + "description": "Las API keys de la persona autenticada, de la más antigua a la más reciente, paginadas con pageNumber y pageSize (10 por omisión y máximo); el owner del tenant recibe las de todas las personas. Cada una trae su valor en apiKeyValue, igual que al crearla. Docs: [listar API keys](https://docs.fiscalapi.com/api-keys#listar-api-keys).", + "operationId": "listApiKeys", + "parameters": [ + { + "name": "PageNumber", + "in": "query", + "schema": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "integer", + "string" + ], + "format": "int32" + } + }, + { + "name": "PageSize", + "in": "query", + "schema": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "integer", + "string" + ], + "format": "int32" + } + }, + { + "name": "X-TIME-ZONE", + "in": "header", + "description": "Zona horaria IANA de las fechas sin zona del cuerpo JSON: el request las lee y la respuesta las escribe en ella. Si falta o no es válida, America/Mexico_City. No aplica a los campos que la operación documenta en UTC o en hora civil.", + "schema": { + "type": "string" + }, + "example": "America/Mexico_City" + } + ], + "responses": { + "200": { + "description": "OK" + } + }, + "security": [ + { + "ApiKey": [ ], + "TenantKey": [ ] + } + ] + }, + "post": { + "tags": [ + "ApiKeys" + ], + "summary": "Crear una API key", + "description": "Genera una API key activa para personId (obligatorio), con description opcional, y devuelve su valor en apiKeyValue; obtener y listar también lo devuelven. Cada persona crea API keys para sí misma y solo el owner del tenant las crea para otra persona (403). Un personId que no es del tenant responde 400. Docs: [crear API key](https://docs.fiscalapi.com/api-keys#crear-api-key) y [API keys de otras personas](https://docs.fiscalapi.com/api-keys#api-keys-de-otras-personas).", + "operationId": "createApiKey", + "parameters": [ + { + "name": "X-TIME-ZONE", + "in": "header", + "description": "Zona horaria IANA de las fechas sin zona del cuerpo JSON: el request las lee y la respuesta las escribe en ella. Si falta o no es válida, America/Mexico_City. No aplica a los campos que la operación documenta en UTC o en hora civil.", + "schema": { + "type": "string" + }, + "example": "America/Mexico_City" + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateApiKeyCommand" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/CreateApiKeyCommand" + } + }, + "application/*+json": { + "schema": { + "$ref": "#/components/schemas/CreateApiKeyCommand" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK" + } + }, + "security": [ + { + "ApiKey": [ ], + "TenantKey": [ ] + } + ] + } + }, + "/api/v4/apikeys/{id}": { + "get": { + "tags": [ + "ApiKeys" + ], + "summary": "Obtener una API key", + "description": "Una API key por su id, con su valor (apiKeyValue), la persona a la que pertenece (personId) y su estado (apiKeyStatus: 1 activa, 0 deshabilitada). Responde 404 si no existe o si es de otra persona y la persona autenticada no es el owner del tenant. Docs: [obtener API key](https://docs.fiscalapi.com/api-keys#obtener-api-key-por-id).", + "operationId": "getApiKey", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "details", + "in": "query", + "schema": { + "type": "boolean", + "default": false + } + }, + { + "name": "X-TIME-ZONE", + "in": "header", + "description": "Zona horaria IANA de las fechas sin zona del cuerpo JSON: el request las lee y la respuesta las escribe en ella. Si falta o no es válida, America/Mexico_City. No aplica a los campos que la operación documenta en UTC o en hora civil.", + "schema": { + "type": "string" + }, + "example": "America/Mexico_City" + } + ], + "responses": { + "200": { + "description": "OK" + } + }, + "security": [ + { + "ApiKey": [ ], + "TenantKey": [ ] + } + ] + }, + "put": { + "tags": [ + "ApiKeys" + ], + "summary": "Actualizar una API key", + "description": "Cambia description y apiKeyStatus: con 0 la API key queda deshabilitada y deja de autenticar hasta que vuelva a 1. Un apiKeyStatus omitido se conserva, pero una description omitida se borra. El id de la ruta y el del cuerpo deben coincidir; responde 404 si no existe o si es de otra persona y la persona autenticada no es el owner del tenant. Docs: [actualizar API key](https://docs.fiscalapi.com/api-keys#actualizar-api-key).", + "operationId": "updateApiKey", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "X-TIME-ZONE", + "in": "header", + "description": "Zona horaria IANA de las fechas sin zona del cuerpo JSON: el request las lee y la respuesta las escribe en ella. Si falta o no es válida, America/Mexico_City. No aplica a los campos que la operación documenta en UTC o en hora civil.", + "schema": { + "type": "string" + }, + "example": "America/Mexico_City" + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpdateApiKeyCommand" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/UpdateApiKeyCommand" + } + }, + "application/*+json": { + "schema": { + "$ref": "#/components/schemas/UpdateApiKeyCommand" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK" + } + }, + "security": [ + { + "ApiKey": [ ], + "TenantKey": [ ] + } + ] + }, + "delete": { + "tags": [ + "ApiKeys" + ], + "summary": "Revocar una API key", + "description": "Deshabilita la API key y la da de baja: deja de autenticar, ya no aparece al listar y obtenerla o actualizarla responde 404, así que no se puede reactivar. Responde 404 si no existe o si es de otra persona y la persona autenticada no es el owner del tenant. Docs: [eliminar API key](https://docs.fiscalapi.com/api-keys#eliminar-api-key).", + "operationId": "revokeApiKey", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "X-TIME-ZONE", + "in": "header", + "description": "Zona horaria IANA de las fechas sin zona del cuerpo JSON: el request las lee y la respuesta las escribe en ella. Si falta o no es válida, America/Mexico_City. No aplica a los campos que la operación documenta en UTC o en hora civil.", + "schema": { + "type": "string" + }, + "example": "America/Mexico_City" + } + ], + "responses": { + "200": { + "description": "OK" + } + }, + "security": [ + { + "ApiKey": [ ], + "TenantKey": [ ] + } + ] + } + }, + "/api/v4/auto-invoice/settings": { + "get": { + "tags": [ + "AutoInvoice" + ], + "summary": "Obtener la configuración de Auto CFDI", + "description": "La ventana de autofactura (si un ticket pagado en otro mes o en otro año todavía se puede facturar) y la URL del aviso de privacidad del portal. Un tenant que nunca la guardó recibe la ventana cerrada y sin aviso. No exige el feature Auto CFDI. 401: sin API key o JWT válidos. 403: la persona no tiene el permiso AutoInvoice.Read.", + "operationId": "getAutoInvoiceSettings", + "parameters": [ + { + "name": "X-TIME-ZONE", + "in": "header", + "description": "Zona horaria IANA de las fechas sin zona del cuerpo JSON: el request las lee y la respuesta las escribe en ella. Si falta o no es válida, America/Mexico_City. No aplica a los campos que la operación documenta en UTC o en hora civil.", + "schema": { + "type": "string" + }, + "example": "America/Mexico_City" + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfAutoInvoiceSettingDto" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfAutoInvoiceSettingDto" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfAutoInvoiceSettingDto" + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + } + } + } + }, + "security": [ + { + "ApiKey": [ ], + "TenantKey": [ ] + } + ] + }, + "put": { + "tags": [ + "AutoInvoice" + ], + "summary": "Actualizar la configuración de Auto CFDI", + "description": "Guarda la ventana de autofactura y la URL https del aviso de privacidad del portal. Permitir otro año exige permitir otro mes. Exige el feature Auto CFDI. 400: una URL de aviso de privacidad inválida o permitir otro año sin permitir otro mes. 401: sin API key o JWT válidos. 403: la persona no tiene el permiso AutoInvoice.Manage (owner) o el tenant no tiene Auto CFDI.", + "operationId": "updateAutoInvoiceSettings", + "parameters": [ + { + "name": "X-TIME-ZONE", + "in": "header", + "description": "Zona horaria IANA de las fechas sin zona del cuerpo JSON: el request las lee y la respuesta las escribe en ella. Si falta o no es válida, America/Mexico_City. No aplica a los campos que la operación documenta en UTC o en hora civil.", + "schema": { + "type": "string" + }, + "example": "America/Mexico_City" + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpdateAutoInvoiceSettingCommand" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/UpdateAutoInvoiceSettingCommand" + } + }, + "application/*+json": { + "schema": { + "$ref": "#/components/schemas/UpdateAutoInvoiceSettingCommand" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfAutoInvoiceSettingDto" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfAutoInvoiceSettingDto" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfAutoInvoiceSettingDto" + } + } + } + }, + "400": { + "description": "Bad Request", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + } + } + } + }, + "security": [ + { + "ApiKey": [ ], + "TenantKey": [ ] + } + ] + } + }, + "/api/v4/auto-invoice/portal-name": { + "get": { + "tags": [ + "AutoInvoice" + ], + "summary": "Obtener el nombre del portal", + "description": "El nombre vigente del portal de autofactura del tenant, cuándo se asignó y desde cuándo puede cambiarse otra vez. Sin nombre, sus campos van en null. No exige el feature Auto CFDI. 401: sin API key o JWT válidos. 403: la persona no tiene el permiso AutoInvoice.Read.", + "operationId": "getAutoInvoicePortalName", + "parameters": [ + { + "name": "X-TIME-ZONE", + "in": "header", + "description": "Zona horaria IANA de las fechas sin zona del cuerpo JSON: el request las lee y la respuesta las escribe en ella. Si falta o no es válida, America/Mexico_City. No aplica a los campos que la operación documenta en UTC o en hora civil.", + "schema": { + "type": "string" + }, + "example": "America/Mexico_City" + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfPortalNameDto" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfPortalNameDto" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfPortalNameDto" + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + } + } + } + }, + "security": [ + { + "ApiKey": [ ], + "TenantKey": [ ] + } + ] + }, + "put": { + "tags": [ + "AutoInvoice" + ], + "summary": "Cambiar el nombre del portal", + "description": "Asigna el nombre del portal de autofactura del tenant. El nombre anterior queda como alias del tenant y el nombre solo puede cambiarse una vez por intervalo. Exige el feature Auto CFDI. 400: un nombre con formato inválido o reservado, o un cambio antes de que termine el intervalo. 401: sin API key o JWT válidos. 403: la persona no tiene el permiso AutoInvoice.Manage (owner) o el tenant no tiene Auto CFDI. 409: otro tenant tiene el nombre, o hay otro cambio del nombre en curso.", + "operationId": "updateAutoInvoicePortalName", + "parameters": [ + { + "name": "X-TIME-ZONE", + "in": "header", + "description": "Zona horaria IANA de las fechas sin zona del cuerpo JSON: el request las lee y la respuesta las escribe en ella. Si falta o no es válida, America/Mexico_City. No aplica a los campos que la operación documenta en UTC o en hora civil.", + "schema": { + "type": "string" + }, + "example": "America/Mexico_City" + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpdatePortalNameCommand" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/UpdatePortalNameCommand" + } + }, + "application/*+json": { + "schema": { + "$ref": "#/components/schemas/UpdatePortalNameCommand" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfPortalNameDto" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfPortalNameDto" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfPortalNameDto" + } + } + } + }, + "400": { + "description": "Bad Request", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + } + } + }, + "409": { + "description": "Conflict", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + } + } + } + }, + "security": [ + { + "ApiKey": [ ], + "TenantKey": [ ] + } + ] + } + }, + "/api/v4/auto-invoice/portal-names/availability": { + "get": { + "tags": [ + "AutoInvoice" + ], + "summary": "Consultar si un nombre de portal está disponible", + "description": "Si el tenant puede usar un nombre de portal. Un nombre inválido, reservado o de otro tenant responde available: false con su razón (no es un error); uno que ya es del tenant está disponible. Exige el feature Auto CFDI. 401: sin API key o JWT válidos. 403: la persona no tiene el permiso AutoInvoice.Manage (owner) o el tenant no tiene Auto CFDI.", + "operationId": "getAutoInvoicePortalNameAvailability", + "parameters": [ + { + "name": "Name", + "in": "query", + "description": "El nombre de portal a consultar; uno inválido responde available: false con su razón, no un 400.", + "schema": { + "type": "string" + } + }, + { + "name": "X-TIME-ZONE", + "in": "header", + "description": "Zona horaria IANA de las fechas sin zona del cuerpo JSON: el request las lee y la respuesta las escribe en ella. Si falta o no es válida, America/Mexico_City. No aplica a los campos que la operación documenta en UTC o en hora civil.", + "schema": { + "type": "string" + }, + "example": "America/Mexico_City" + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfPortalNameAvailabilityDto" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfPortalNameAvailabilityDto" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfPortalNameAvailabilityDto" + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + } + } + } + }, + "security": [ + { + "ApiKey": [ ], + "TenantKey": [ ] + } + ] + } + }, + "/api/v4/auto-invoice/reports/consumption": { + "get": { + "tags": [ + "AutoInvoice" + ], + "summary": "Obtener el reporte de consumo de Auto CFDI", + "description": "Los tickets pagados en el periodo por emisor y estado, y los créditos de ticket que consumió cada persona que los pagó. El periodo es obligatorio, en fechas civiles de México, de un año como máximo. No exige el feature Auto CFDI. 400: falta el periodo, una fecha es inválida, el periodo está invertido o pasa de un año. 401: sin API key o JWT válidos. 403: la persona no tiene el permiso Tickets.Read. 404: personId no es una persona del tenant.", + "operationId": "getAutoInvoiceConsumptionReport", + "parameters": [ + { + "name": "From", + "in": "query", + "description": "Primer día del periodo (incluido), fecha civil de México YYYY-MM-DD; el periodo es de un año como máximo.", + "schema": { + "type": "string" + } + }, + { + "name": "To", + "in": "query", + "description": "Último día del periodo (incluido), fecha civil de México YYYY-MM-DD.", + "schema": { + "type": "string" + } + }, + { + "name": "PersonId", + "in": "query", + "description": "Id de una persona del tenant: solo sus tickets como emisora y sus créditos como pagadora.", + "schema": { + "type": "string" + } + }, + { + "name": "X-TIME-ZONE", + "in": "header", + "description": "Zona horaria IANA de las fechas sin zona del cuerpo JSON: el request las lee y la respuesta las escribe en ella. Si falta o no es válida, America/Mexico_City. No aplica a los campos que la operación documenta en UTC o en hora civil.", + "schema": { + "type": "string" + }, + "example": "America/Mexico_City" + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfConsumptionReportDto" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfConsumptionReportDto" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfConsumptionReportDto" + } + } + } + }, + "400": { + "description": "Bad Request", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + } + } + } + }, + "security": [ + { + "ApiKey": [ ], + "TenantKey": [ ] + } + ] + } + }, + "/api/v4/catalogs": { + "get": { + "tags": [ + "Catalogs" + ], + "summary": "Listar catálogos", + "description": "Los nombres de los catálogos del SAT y de los internos de FiscalAPI, sin paginar. Cada nombre es el que va en la ruta de los demás endpoints de catálogos y distingue mayúsculas y minúsculas: c_RegimenFiscal es SatTaxRegimes, c_UsoCFDI es SatCfdiUses, c_ClaveProdServ es SatProductCodes y c_ClaveUnidad es SatUnitMeasurements. Un nombre que no está en la lista responde 404. Docs: [listar catálogos](https://docs.fiscalapi.com/catalogs#listar-catalogos).", + "operationId": "listCatalogs", + "parameters": [ + { + "name": "X-TIME-ZONE", + "in": "header", + "description": "Zona horaria IANA de las fechas sin zona del cuerpo JSON: el request las lee y la respuesta las escribe en ella. Si falta o no es válida, America/Mexico_City. No aplica a los campos que la operación documenta en UTC o en hora civil.", + "schema": { + "type": "string" + }, + "example": "America/Mexico_City" + } + ], + "responses": { + "200": { + "description": "OK" + } + }, + "security": [ + { + "ApiKey": [ ], + "TenantKey": [ ] + } + ] + } + }, + "/api/v4/catalogs/{catalogName}/key/{id}": { + "get": { + "tags": [ + "Catalogs" + ], + "summary": "Obtener un registro de un catálogo", + "description": "Un registro de un catálogo por su clave: catalogName es el nombre del catálogo (p. ej. SatTaxRegimes) e id es la clave del SAT (p. ej. 601 en c_RegimenFiscal). Responde 404 si el catálogo no existe o si la clave no está en él. Docs: [obtener registro por ID](https://docs.fiscalapi.com/catalogs#obtener-registro-por-id).", + "operationId": "getCatalogRecord", + "parameters": [ + { + "name": "catalogName", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "X-TIME-ZONE", + "in": "header", + "description": "Zona horaria IANA de las fechas sin zona del cuerpo JSON: el request las lee y la respuesta las escribe en ella. Si falta o no es válida, America/Mexico_City. No aplica a los campos que la operación documenta en UTC o en hora civil.", + "schema": { + "type": "string" + }, + "example": "America/Mexico_City" + } + ], + "responses": { + "200": { + "description": "OK" + } + }, + "security": [ + { + "ApiKey": [ ], + "TenantKey": [ ] + } + ] + } + }, + "/api/v4/catalogs/{catalogName}/{searchText}": { + "get": { + "tags": [ + "Catalogs" + ], + "summary": "Buscar en un catálogo", + "description": "Los registros de un catálogo cuya descripción contiene searchText, de al menos 4 caracteres; no busca por clave (para eso, obtén el registro por su id). Paginado con pageNumber y pageSize (50 por omisión, de 1 a 100); un parámetro fuera de rango responde 400 y un catálogo inexistente, 404. Docs: [buscar en un catálogo](https://docs.fiscalapi.com/catalogs#buscar-en-un-catalogo).", + "operationId": "searchCatalogRecords", + "parameters": [ + { + "name": "catalogName", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "searchText", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "pageNumber", + "in": "query", + "schema": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "integer", + "string" + ], + "format": "int32", + "default": 1 + } + }, + { + "name": "pageSize", + "in": "query", + "schema": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "integer", + "string" + ], + "format": "int32", + "default": 50 + } + }, + { + "name": "X-TIME-ZONE", + "in": "header", + "description": "Zona horaria IANA de las fechas sin zona del cuerpo JSON: el request las lee y la respuesta las escribe en ella. Si falta o no es válida, America/Mexico_City. No aplica a los campos que la operación documenta en UTC o en hora civil.", + "schema": { + "type": "string" + }, + "example": "America/Mexico_City" + } + ], + "responses": { + "200": { + "description": "OK" + } + }, + "security": [ + { + "ApiKey": [ ], + "TenantKey": [ ] + } + ] + } + }, + "/api/v4/download-catalogs": { + "get": { + "tags": [ + "DownloadCatalogs" + ], + "summary": "Listar los catálogos de descarga masiva", + "description": "Los nombres de los catálogos de la descarga masiva, sin paginar: SatQueryTypes, DownloadTypes, SatInvoiceStatuses, DownloadRequestStatuses y los demás que usan las reglas y las solicitudes. Usa cada nombre con listDownloadCatalogRecords. Docs: [listar catálogos](https://docs.fiscalapi.com/download-catalogs#listar-catalogos).", + "operationId": "listDownloadCatalogs", + "parameters": [ + { + "name": "X-TIME-ZONE", + "in": "header", + "description": "Zona horaria IANA de las fechas sin zona del cuerpo JSON: el request las lee y la respuesta las escribe en ella. Si falta o no es válida, America/Mexico_City. No aplica a los campos que la operación documenta en UTC o en hora civil.", + "schema": { + "type": "string" + }, + "example": "America/Mexico_City" + } + ], + "responses": { + "200": { + "description": "OK" + } + }, + "security": [ + { + "ApiKey": [ ], + "TenantKey": [ ] + } + ] + } + }, + "/api/v4/download-catalogs/{catalogName}": { + "get": { + "tags": [ + "DownloadCatalogs" + ], + "summary": "Listar los registros de un catálogo de descarga masiva", + "description": "Los registros (id y description) del catálogo catalogName, escrito como lo devuelve listDownloadCatalogs (distingue mayúsculas); un nombre que no existe responde 404. Los id son los códigos que usan las reglas y las solicitudes, como satQueryTypeId o downloadTypeId. Docs: [listar catálogo](https://docs.fiscalapi.com/download-catalogs#listar-catalogo).", + "operationId": "listDownloadCatalogRecords", + "parameters": [ + { + "name": "catalogName", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "details", + "in": "query", + "schema": { + "type": "boolean", + "default": false + } + }, + { + "name": "X-TIME-ZONE", + "in": "header", + "description": "Zona horaria IANA de las fechas sin zona del cuerpo JSON: el request las lee y la respuesta las escribe en ella. Si falta o no es válida, America/Mexico_City. No aplica a los campos que la operación documenta en UTC o en hora civil.", + "schema": { + "type": "string" + }, + "example": "America/Mexico_City" + } + ], + "responses": { + "200": { + "description": "OK" + } + }, + "security": [ + { + "ApiKey": [ ], + "TenantKey": [ ] + } + ] + } + }, + "/api/v4/download-requests": { + "get": { + "tags": [ + "DownloadRequests" + ], + "summary": "Listar solicitudes de descarga masiva", + "description": "Las solicitudes de descarga masiva de CFDI, metadata y Retenciones del SAT del tenant, manuales y automáticas, paginadas con pageNumber y pageSize (10 por omisión, máximo 50). Cada una trae su estado en FiscalAPI (downloadRequestStatus), su estatus ante el SAT (satRequestStatus) y, ya terminada en el SAT, invoiceCount y packageIds. Docs: [listar solicitudes](https://docs.fiscalapi.com/download-requests#listar-solicitudes).", + "operationId": "listDownloadRequests", + "parameters": [ + { + "name": "PageNumber", + "in": "query", + "schema": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "integer", + "string" + ], + "format": "int32" + } + }, + { + "name": "PageSize", + "in": "query", + "schema": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "integer", + "string" + ], + "format": "int32" + } + }, + { + "name": "X-TIME-ZONE", + "in": "header", + "description": "Zona horaria IANA de las fechas sin zona del cuerpo JSON: el request las lee y la respuesta las escribe en ella. Si falta o no es válida, America/Mexico_City. No aplica a los campos que la operación documenta en UTC o en hora civil.", + "schema": { + "type": "string" + }, + "example": "America/Mexico_City" + } + ], + "responses": { + "200": { + "description": "OK" + } + }, + "security": [ + { + "ApiKey": [ ], + "TenantKey": [ ] + } + ] + }, + "post": { + "tags": [ + "DownloadRequests" + ], + "summary": "Crear una solicitud de descarga masiva", + "description": "Pide al SAT en la misma petición, con la FIEL vigente de la persona de la regla, la descarga masiva de la regla downloadRuleId entre startDate y endDate; downloadRequestTypeId debe ser \"Manual\". El periodo admite hasta 31 días (hasta 400 días en reglas de Retenciones); un periodo más largo, una startDate que no sea anterior a endDate, una endDate futura o una persona sin RFC o FIEL vigente responden 400. Responde 404 si la regla no existe; 403 si no está aprobada, si su persona no tiene un asiento activo de Descarga CFDI o en el ambiente de pruebas, y 502 si el SAT rechaza la autenticación o no crea la solicitud. Aceptada, queda en Esperando SAT y FiscalAPI la procesa en segundo plano. Docs: [crear solicitud](https://docs.fiscalapi.com/download-requests#crear-solicitud).", + "operationId": "createDownloadRequest", + "parameters": [ + { + "name": "X-TIME-ZONE", + "in": "header", + "description": "Zona horaria IANA de las fechas sin zona del cuerpo JSON: el request las lee y la respuesta las escribe en ella. Si falta o no es válida, America/Mexico_City. No aplica a los campos que la operación documenta en UTC o en hora civil.", + "schema": { + "type": "string" + }, + "example": "America/Mexico_City" + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateDownloadRequestCommand" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/CreateDownloadRequestCommand" + } + }, + "application/*+json": { + "schema": { + "$ref": "#/components/schemas/CreateDownloadRequestCommand" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK" + } + }, + "security": [ + { + "ApiKey": [ ], + "TenantKey": [ ] + } + ] + } + }, + "/api/v4/download-requests/search": { + "get": { + "tags": [ + "DownloadRequests" + ], + "summary": "Buscar solicitudes de descarga masiva por fecha de creación", + "description": "Las solicitudes de descarga masiva creadas el día createdAt en UTC (obligatorio, AAAA-MM-DD; sin él responde 400), sin paginar y de la más reciente a la más antigua. Docs: [listar solicitudes](https://docs.fiscalapi.com/download-requests#listar-solicitudes).", + "operationId": "searchDownloadRequests", + "parameters": [ + { + "name": "CreatedAt", + "in": "query", + "schema": { + "type": "string", + "format": "date-time" + } + }, + { + "name": "X-TIME-ZONE", + "in": "header", + "description": "Zona horaria IANA de las fechas sin zona del cuerpo JSON: el request las lee y la respuesta las escribe en ella. Si falta o no es válida, America/Mexico_City. No aplica a los campos que la operación documenta en UTC o en hora civil.", + "schema": { + "type": "string" + }, + "example": "America/Mexico_City" + } + ], + "responses": { + "200": { + "description": "OK" + } + }, + "security": [ + { + "ApiKey": [ ], + "TenantKey": [ ] + } + ] + } + }, + "/api/v4/download-requests/{id}": { + "get": { + "tags": [ + "DownloadRequests" + ], + "summary": "Obtener una solicitud de descarga masiva", + "description": "Una solicitud por su id, con su estado en FiscalAPI (downloadRequestStatus: Esperando SAT, Esperando API, Completada o Abandonada) y en el SAT (satRequestStatus). Consúltala hasta que sea Completada para leer sus XML, su metadata o sus paquetes; 404 si no existe en el tenant. Docs: [obtener solicitud](https://docs.fiscalapi.com/download-requests#obtener-solicitud-por-id).", + "operationId": "getDownloadRequest", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "X-TIME-ZONE", + "in": "header", + "description": "Zona horaria IANA de las fechas sin zona del cuerpo JSON: el request las lee y la respuesta las escribe en ella. Si falta o no es válida, America/Mexico_City. No aplica a los campos que la operación documenta en UTC o en hora civil.", + "schema": { + "type": "string" + }, + "example": "America/Mexico_City" + } + ], + "responses": { + "200": { + "description": "OK" + } + }, + "security": [ + { + "ApiKey": [ ], + "TenantKey": [ ] + } + ] + }, + "delete": { + "tags": [ + "DownloadRequests" + ], + "summary": "Eliminar una solicitud de descarga masiva", + "description": "Borra de forma definitiva la solicitud con sus paquetes, XML y metadata; 404 si no existe en el tenant. Docs: [eliminar solicitud](https://docs.fiscalapi.com/download-requests#eliminar-solicitud-por-id).", + "operationId": "deleteDownloadRequest", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "X-TIME-ZONE", + "in": "header", + "description": "Zona horaria IANA de las fechas sin zona del cuerpo JSON: el request las lee y la respuesta las escribe en ella. Si falta o no es válida, America/Mexico_City. No aplica a los campos que la operación documenta en UTC o en hora civil.", + "schema": { + "type": "string" + }, + "example": "America/Mexico_City" + } + ], + "responses": { + "200": { + "description": "OK" + } + }, + "security": [ + { + "ApiKey": [ ], + "TenantKey": [ ] + } + ] + } + }, + "/api/v4/download-requests/{id}/xmls": { + "get": { + "tags": [ + "DownloadRequests" + ], + "summary": "Listar los CFDI (XML) de una solicitud de descarga masiva", + "description": "Los CFDI que FiscalAPI extrajo de los paquetes de una solicitud de tipo CFDI, paginados con pageNumber y pageSize (10 por omisión, máximo 50): cada uno con emisor (xmlIssuer), receptor (xmlRecipient), conceptos (xmlItems), impuestos (taxes), complementos (xmlComplements) y el XML en base64Content. El id debe medir 36 caracteres (400). Docs: [listar XML](https://docs.fiscalapi.com/download-requests#listar-xmls-por-id).", + "operationId": "listDownloadRequestXmls", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "RequestId", + "in": "query", + "schema": { + "type": "string" + } + }, + { + "name": "PageNumber", + "in": "query", + "schema": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "integer", + "string" + ], + "format": "int32" + } + }, + { + "name": "PageSize", + "in": "query", + "schema": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "integer", + "string" + ], + "format": "int32" + } + }, + { + "name": "X-TIME-ZONE", + "in": "header", + "description": "Zona horaria IANA de las fechas sin zona del cuerpo JSON: el request las lee y la respuesta las escribe en ella. Si falta o no es válida, America/Mexico_City. No aplica a los campos que la operación documenta en UTC o en hora civil.", + "schema": { + "type": "string" + }, + "example": "America/Mexico_City" + } + ], + "responses": { + "200": { + "description": "OK" + } + }, + "security": [ + { + "ApiKey": [ ], + "TenantKey": [ ] + } + ] + } + }, + "/api/v4/download-requests/{id}/meta-items": { + "get": { + "tags": [ + "DownloadRequests" + ], + "summary": "Listar la metadata de una solicitud de descarga masiva", + "description": "Los registros de metadata que FiscalAPI extrajo de los paquetes de una solicitud de tipo Metadata, paginados con pageNumber y pageSize (10 por omisión, máximo 50): invoiceUuid, issuerTin, recipientTin, amount, status y cancellationDate, entre otros. Es la vía para los CFDI cancelados. El id debe medir 36 caracteres (400). Docs: [listar metadata](https://docs.fiscalapi.com/download-requests#listar-meta-items-por-id).", + "operationId": "listDownloadRequestMetaItems", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "RequestId", + "in": "query", + "schema": { + "type": "string" + } + }, + { + "name": "PageNumber", + "in": "query", + "schema": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "integer", + "string" + ], + "format": "int32" + } + }, + { + "name": "PageSize", + "in": "query", + "schema": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "integer", + "string" + ], + "format": "int32" + } + }, + { + "name": "X-TIME-ZONE", + "in": "header", + "description": "Zona horaria IANA de las fechas sin zona del cuerpo JSON: el request las lee y la respuesta las escribe en ella. Si falta o no es válida, America/Mexico_City. No aplica a los campos que la operación documenta en UTC o en hora civil.", + "schema": { + "type": "string" + }, + "example": "America/Mexico_City" + } + ], + "responses": { + "200": { + "description": "OK" + } + }, + "security": [ + { + "ApiKey": [ ], + "TenantKey": [ ] + } + ] + } + }, + "/api/v4/download-requests/{id}/withholding-packages": { + "get": { + "tags": [ + "DownloadRequests" + ], + "summary": "Listar los paquetes de una solicitud de Retenciones", + "description": "Los paquetes que entregó el SAT para la solicitud, cada uno con satPackageId y el zip en base64Package, paginados con pageNumber y pageSize (10 por omisión y máximo). Sirve para leer por páginas una solicitud de Retenciones e información de pagos, cuyos paquetes FiscalAPI no extrae como XML ni metadata. El id debe medir 36 caracteres (400). Docs: [descargar paquete](https://docs.fiscalapi.com/download-requests#descargar-paquete-por-id).", + "operationId": "listDownloadRequestWithholdingPackages", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "RequestId", + "in": "query", + "schema": { + "type": "string" + } + }, + { + "name": "PageNumber", + "in": "query", + "schema": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "integer", + "string" + ], + "format": "int32" + } + }, + { + "name": "PageSize", + "in": "query", + "schema": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "integer", + "string" + ], + "format": "int32" + } + }, + { + "name": "X-TIME-ZONE", + "in": "header", + "description": "Zona horaria IANA de las fechas sin zona del cuerpo JSON: el request las lee y la respuesta las escribe en ella. Si falta o no es válida, America/Mexico_City. No aplica a los campos que la operación documenta en UTC o en hora civil.", + "schema": { + "type": "string" + }, + "example": "America/Mexico_City" + } + ], + "responses": { + "200": { + "description": "OK" + } + }, + "security": [ + { + "ApiKey": [ ], + "TenantKey": [ ] + } + ] + } + }, + "/api/v4/download-requests/{id}/package": { + "get": { + "tags": [ + "DownloadRequests" + ], + "summary": "Descargar los paquetes de una solicitud de descarga masiva", + "description": "Los paquetes zip que entregó el SAT para la solicitud, cada uno en base64File con fileName .zip. Solo con la solicitud en Completada: en otro estado responde 425 (Too Early) con el encabezado Retry-After: 1800, y 404 si no existe. Docs: [descargar paquete](https://docs.fiscalapi.com/download-requests#descargar-paquete-por-id).", + "operationId": "getDownloadRequestPackage", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "X-TIME-ZONE", + "in": "header", + "description": "Zona horaria IANA de las fechas sin zona del cuerpo JSON: el request las lee y la respuesta las escribe en ella. Si falta o no es válida, America/Mexico_City. No aplica a los campos que la operación documenta en UTC o en hora civil.", + "schema": { + "type": "string" + }, + "example": "America/Mexico_City" + } + ], + "responses": { + "200": { + "description": "OK" + } + }, + "security": [ + { + "ApiKey": [ ], + "TenantKey": [ ] + } + ] + } + }, + "/api/v4/download-requests/{id}/raw-request": { + "get": { + "tags": [ + "DownloadRequests" + ], + "summary": "Descargar la última petición enviada al SAT por una solicitud", + "description": "El XML de la última petición que FiscalAPI envió al SAT por la solicitud (al crearla o al verificarla), en base64File con fileName RawRequest_.xml; 404 si no existe. Docs: [descargar SAT request](https://docs.fiscalapi.com/download-requests#descargar-sat-request-por-id).", + "operationId": "getDownloadRequestRawRequest", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "X-TIME-ZONE", + "in": "header", + "description": "Zona horaria IANA de las fechas sin zona del cuerpo JSON: el request las lee y la respuesta las escribe en ella. Si falta o no es válida, America/Mexico_City. No aplica a los campos que la operación documenta en UTC o en hora civil.", + "schema": { + "type": "string" + }, + "example": "America/Mexico_City" + } + ], + "responses": { + "200": { + "description": "OK" + } + }, + "security": [ + { + "ApiKey": [ ], + "TenantKey": [ ] + } + ] + } + }, + "/api/v4/download-requests/{id}/raw-response": { + "get": { + "tags": [ + "DownloadRequests" + ], + "summary": "Descargar la última respuesta del SAT a una solicitud", + "description": "El XML de la última respuesta del SAT a la solicitud (al crearla o al verificarla), en base64File con fileName RawResponse_.xml; 404 si no existe. Docs: [descargar SAT response](https://docs.fiscalapi.com/download-requests#descargar-sat-response-por-id).", + "operationId": "getDownloadRequestRawResponse", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "X-TIME-ZONE", + "in": "header", + "description": "Zona horaria IANA de las fechas sin zona del cuerpo JSON: el request las lee y la respuesta las escribe en ella. Si falta o no es válida, America/Mexico_City. No aplica a los campos que la operación documenta en UTC o en hora civil.", + "schema": { + "type": "string" + }, + "example": "America/Mexico_City" + } + ], + "responses": { + "200": { + "description": "OK" + } + }, + "security": [ + { + "ApiKey": [ ], + "TenantKey": [ ] + } + ] + } + }, + "/api/v4/download-rules": { + "get": { + "tags": [ + "DownloadRules" + ], + "summary": "Listar reglas de descarga masiva", + "description": "Las reglas de descarga masiva del tenant, paginadas con pageNumber y pageSize (10 por omisión, máximo 50), cada una con su persona, RFC (tin), tipo de consulta (satQueryType), dirección (downloadType), estatus de los documentos (satInvoiceStatus) y estado de la regla (downloadRuleStatus). Docs: [listar reglas](https://docs.fiscalapi.com/download-rules#listar-reglas).", + "operationId": "listDownloadRules", + "parameters": [ + { + "name": "PageNumber", + "in": "query", + "schema": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "integer", + "string" + ], + "format": "int32" + } + }, + { + "name": "PageSize", + "in": "query", + "schema": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "integer", + "string" + ], + "format": "int32" + } + }, + { + "name": "X-TIME-ZONE", + "in": "header", + "description": "Zona horaria IANA de las fechas sin zona del cuerpo JSON: el request las lee y la respuesta las escribe en ella. Si falta o no es válida, America/Mexico_City. No aplica a los campos que la operación documenta en UTC o en hora civil.", + "schema": { + "type": "string" + }, + "example": "America/Mexico_City" + } + ], + "responses": { + "200": { + "description": "OK" + } + }, + "security": [ + { + "ApiKey": [ ], + "TenantKey": [ ] + } + ] + }, + "post": { + "tags": [ + "DownloadRules" + ], + "summary": "Crear una regla de descarga masiva", + "description": "Programa la descarga masiva diaria de un RFC: con la regla aprobada y la persona con asiento activo, FiscalAPI pide al SAT cada día, con la FIEL de la persona, los documentos del día anterior. Campos obligatorios: personId, description (máximo 100), satQueryTypeId (CFDI, Metadata o Retenciones), downloadTypeId (Emitidos o Recibidos) y satInvoiceStatusId (Vigente o Cancelado; Cancelado solo con Metadata). Responde 400 si personId no es del tenant o la persona no tiene RFC o FIEL vigente, y 403 si la persona es el owner del tenant y la persona autenticada no lo es, si ya hay una regla igual para el RFC, si no quedan asientos de Descarga CFDI o en el ambiente de pruebas. Docs: [crear regla](https://docs.fiscalapi.com/download-rules#crear-regla).", + "operationId": "createDownloadRule", + "parameters": [ + { + "name": "X-TIME-ZONE", + "in": "header", + "description": "Zona horaria IANA de las fechas sin zona del cuerpo JSON: el request las lee y la respuesta las escribe en ella. Si falta o no es válida, America/Mexico_City. No aplica a los campos que la operación documenta en UTC o en hora civil.", + "schema": { + "type": "string" + }, + "example": "America/Mexico_City" + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateDownloadRuleCommand" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/CreateDownloadRuleCommand" + } + }, + "application/*+json": { + "schema": { + "$ref": "#/components/schemas/CreateDownloadRuleCommand" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK" + } + }, + "security": [ + { + "ApiKey": [ ], + "TenantKey": [ ] + } + ] + } + }, + "/api/v4/download-rules/{id}": { + "get": { + "tags": [ + "DownloadRules" + ], + "summary": "Obtener una regla de descarga masiva", + "description": "Una regla por su id, con su persona, RFC (tin), tipo de consulta (satQueryType), dirección (downloadType), estatus de los documentos (satInvoiceStatus) y estado de la regla (downloadRuleStatus); 404 si no existe en el tenant. Docs: [obtener regla](https://docs.fiscalapi.com/download-rules#obtener-regla-por-id).", + "operationId": "getDownloadRule", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "X-TIME-ZONE", + "in": "header", + "description": "Zona horaria IANA de las fechas sin zona del cuerpo JSON: el request las lee y la respuesta las escribe en ella. Si falta o no es válida, America/Mexico_City. No aplica a los campos que la operación documenta en UTC o en hora civil.", + "schema": { + "type": "string" + }, + "example": "America/Mexico_City" + } + ], + "responses": { + "200": { + "description": "OK" + } + }, + "security": [ + { + "ApiKey": [ ], + "TenantKey": [ ] + } + ] + }, + "put": { + "tags": [ + "DownloadRules" + ], + "summary": "Actualizar la descripción de una regla de descarga masiva", + "description": "Solo cambia description (máximo 100 caracteres); la persona, el tipo de consulta, la dirección y el estatus de los documentos no se editan. El cuerpo lleva el mismo id de la ruta (400 si no coincide) y una description omitida deja la regla sin descripción. Responde 404 si no existe en el tenant y 403 si la regla es del owner del tenant y la persona autenticada no lo es. Docs: [actualizar regla](https://docs.fiscalapi.com/download-rules#actualizar-regla).", + "operationId": "updateDownloadRule", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "X-TIME-ZONE", + "in": "header", + "description": "Zona horaria IANA de las fechas sin zona del cuerpo JSON: el request las lee y la respuesta las escribe en ella. Si falta o no es válida, America/Mexico_City. No aplica a los campos que la operación documenta en UTC o en hora civil.", + "schema": { + "type": "string" + }, + "example": "America/Mexico_City" + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpdateDownloadRuleCommand" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/UpdateDownloadRuleCommand" + } + }, + "application/*+json": { + "schema": { + "$ref": "#/components/schemas/UpdateDownloadRuleCommand" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK" + } + }, + "security": [ + { + "ApiKey": [ ], + "TenantKey": [ ] + } + ] + }, + "delete": { + "tags": [ + "DownloadRules" + ], + "summary": "Eliminar una regla de descarga masiva", + "description": "Borra de forma definitiva la regla con todas sus solicitudes, paquetes, XML y metadata, y recalcula los asientos de Descarga CFDI ocupados. Responde 404 si no existe en el tenant y 403 si la regla es del owner del tenant y la persona autenticada no lo es. Docs: [eliminar regla](https://docs.fiscalapi.com/download-rules#eliminar-regla).", + "operationId": "deleteDownloadRule", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "X-TIME-ZONE", + "in": "header", + "description": "Zona horaria IANA de las fechas sin zona del cuerpo JSON: el request las lee y la respuesta las escribe en ella. Si falta o no es válida, America/Mexico_City. No aplica a los campos que la operación documenta en UTC o en hora civil.", + "schema": { + "type": "string" + }, + "example": "America/Mexico_City" + } + ], + "responses": { + "200": { + "description": "OK" + } + }, + "security": [ + { + "ApiKey": [ ], + "TenantKey": [ ] + } + ] + } + }, + "/api/v4/download-rules/test": { + "post": { + "tags": [ + "DownloadRules" + ], + "summary": "Simular una descarga masiva en el ambiente de pruebas", + "description": "Sin contactar al SAT, crea una regla de prueba (CFDI, Recibidos, Vigente) y una solicitud ya Completada con los últimos CFDI timbrados del tenant (hasta 3) como XML, metadata y paquete, y emite el evento download_request.completed. Responde la solicitud creada; 400 en producción o si el tenant no tiene facturas timbradas. Docs: [ambiente de pruebas](https://docs.fiscalapi.com/download-info#ambiente-de-pruebas).", + "operationId": "createDownloadRuleTestRequest", + "parameters": [ + { + "name": "X-TIME-ZONE", + "in": "header", + "description": "Zona horaria IANA de las fechas sin zona del cuerpo JSON: el request las lee y la respuesta las escribe en ella. Si falta o no es válida, America/Mexico_City. No aplica a los campos que la operación documenta en UTC o en hora civil.", + "schema": { + "type": "string" + }, + "example": "America/Mexico_City" + } + ], + "responses": { + "200": { + "description": "OK" + } + }, + "security": [ + { + "ApiKey": [ ], + "TenantKey": [ ] + } + ] + } + }, + "/api/v4/events": { + "get": { + "tags": [ + "Events" + ], + "summary": "Listar eventos de webhook", + "description": "Los eventos del tenant de los últimos 30 días, del más nuevo al más viejo; cada uno es el sobre tal como se entregó (id, type, createdAt en UTC, personId y data). Filtra por type, relatedObjectId y el rango from..to (instantes UTC, ambos incluidos). 400: un tipo de evento que no existe, from posterior a to, un rango de más de 30 días o una página fuera de rango. 401: sin API key o JWT válidos. 403: la persona no tiene el permiso Webhooks.Manage.", + "operationId": "listEvents", + "parameters": [ + { + "name": "Type", + "in": "query", + "description": "Solo los eventos de este tipo (p. ej. invoice.canceled o webhook.test).", + "schema": { + "type": "string" + } + }, + { + "name": "RelatedObjectId", + "in": "query", + "description": "Solo los eventos de este recurso (data.relatedObject.id).", + "schema": { + "type": "string" + } + }, + { + "name": "From", + "in": "query", + "description": "Desde este instante, incluido, en ISO-8601 UTC (p. ej. 2026-10-01T00:00:00Z). Junto con to (o hasta ahora si se omite) no puede abarcar más de 30 días.", + "schema": { + "type": "string", + "format": "date-time" + } + }, + { + "name": "To", + "in": "query", + "description": "Hasta este instante, incluido, en ISO-8601 UTC. Por omisión, ahora.", + "schema": { + "type": "string", + "format": "date-time" + } + }, + { + "name": "PageNumber", + "in": "query", + "description": "Número de página, desde 1 (por omisión 1).", + "schema": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "integer", + "string" + ], + "format": "int32" + } + }, + { + "name": "PageSize", + "in": "query", + "description": "Eventos por página, de 1 a 50 (por omisión 10).", + "schema": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "integer", + "string" + ], + "format": "int32" + } + }, + { + "name": "X-TIME-ZONE", + "in": "header", + "description": "Zona horaria IANA de las fechas sin zona del cuerpo JSON: el request las lee y la respuesta las escribe en ella. Si falta o no es válida, America/Mexico_City. No aplica a los campos que la operación documenta en UTC o en hora civil.", + "schema": { + "type": "string" + }, + "example": "America/Mexico_City" + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfPagedListOfJsonElement" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfPagedListOfJsonElement" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfPagedListOfJsonElement" + } + } + } + }, + "400": { + "description": "Bad Request", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + } + } + } + }, + "security": [ + { + "ApiKey": [ ], + "TenantKey": [ ] + } + ] + } + }, + "/api/v4/events/{id}": { + "get": { + "tags": [ + "Events" + ], + "summary": "Obtener un evento de webhook", + "description": "El sobre del evento tal como lo recibió el receptor, con createdAt en UTC sin importar X-TIME-ZONE. 401: sin API key o JWT válidos. 403: la persona no tiene el permiso Webhooks.Manage. 404: no existe un evento con ese id en el tenant, o tiene más de 30 días (la retención ya lo purgó o está por purgarlo).", + "operationId": "getEvent", + "parameters": [ + { + "name": "id", + "in": "path", + "description": "Id del evento: el webhook-id con el que se entregó.", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "X-TIME-ZONE", + "in": "header", + "description": "Zona horaria IANA de las fechas sin zona del cuerpo JSON: el request las lee y la respuesta las escribe en ella. Si falta o no es válida, America/Mexico_City. No aplica a los campos que la operación documenta en UTC o en hora civil.", + "schema": { + "type": "string" + }, + "example": "America/Mexico_City" + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfJsonElement" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfJsonElement" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfJsonElement" + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + } + } + } + }, + "security": [ + { + "ApiKey": [ ], + "TenantKey": [ ] + } + ] + } + }, + "/api/v4/events/{id}/resend": { + "post": { + "tags": [ + "Events" + ], + "summary": "Reenviar un evento de webhook", + "description": "Crea una entrega nueva del evento, firmada y de un solo intento (sin reintentos), al webhook webhookId o, si se omite (o sin cuerpo), a la escucha de CLI de la persona autenticada. La de una URL sale en segundo plano; la de la escucha la toma fiscalapi listen. 202: deliveryId y webhookId, para seguirla en el log de entregas. 400: un webhookId vacío (\"'Webhook Id' no debería estar vacío.\"); un campo que el cuerpo no admite (p. ej. webhookEndpointId), con el texto \"El campo '{field}' no existe en este recurso.\"; un webhook de URL deshabilitado ('El webhook está deshabilitado: habilítalo antes de reenviarle eventos.'). 401: sin API key o JWT válidos. 403: la persona no tiene el permiso Webhooks.Manage. 404: no existe un evento con ese id en el tenant, o tiene más de 30 días (la retención ya lo purgó o está por purgarlo). También 404 si el webhook no es del tenant o es la escucha de CLI de otra persona (salvo para el owner). 409: la escucha de CLI no tiene una sesión vigente de fiscalapi listen. 429: demasiados reenvíos con la misma API key.", + "operationId": "resendEvent", + "parameters": [ + { + "name": "id", + "in": "path", + "description": "Id del evento: el webhook-id con el que se entregó.", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "X-TIME-ZONE", + "in": "header", + "description": "Zona horaria IANA de las fechas sin zona del cuerpo JSON: el request las lee y la respuesta las escribe en ella. Si falta o no es válida, America/Mexico_City. No aplica a los campos que la operación documenta en UTC o en hora civil.", + "schema": { + "type": "string" + }, + "example": "America/Mexico_City" + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "oneOf": [ + { + "type": "null" + }, + { + "$ref": "#/components/schemas/ResendWebhookEventCommand" + } + ] + } + }, + "text/json": { + "schema": { + "oneOf": [ + { + "type": "null" + }, + { + "$ref": "#/components/schemas/ResendWebhookEventCommand" + } + ] + } + }, + "application/*+json": { + "schema": { + "oneOf": [ + { + "type": "null" + }, + { + "$ref": "#/components/schemas/ResendWebhookEventCommand" + } + ] + } + } + } + }, + "responses": { + "202": { + "description": "Accepted", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfWebhookEventResendDto" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfWebhookEventResendDto" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfWebhookEventResendDto" + } + } + } + }, + "400": { + "description": "Bad Request", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + } + } + }, + "409": { + "description": "Conflict", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + } + } + }, + "429": { + "description": "Too Many Requests", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + } + } + } + }, + "security": [ + { + "ApiKey": [ ], + "TenantKey": [ ] + } + ] + } + }, + "/api/v4/events/trigger": { + "post": { + "tags": [ + "Events" + ], + "summary": "Disparar un evento de webhook de prueba", + "description": "Publica un evento del tipo pedido con la forma de uno real (el recurso y los estados de su tipo) y lo entrega solo al webhook webhookId o, si se omite, a la escucha de CLI de la persona autenticada (fiscalapi trigger); los demás webhooks no lo reciben. Como el reenvío, no filtra por las suscripciones (enabledEvents) ni las personas (personIds) del destino. La entrega es una prueba de un solo intento, sin reintentos. Sin relatedObjectId, el recurso es un id sintético cuyo GET responde 404; sin personId, el evento va sin persona. 202: eventId y webhookId, para seguirlo en GET /events/{id} y en el log de entregas. 400: el cuerpo vacío ('El cuerpo de la petición es requerido y no debe estar vacío.'); un type omitido o que no existe (\"El tipo de evento '{type}' no existe.\"); un relatedObjectId vacío (\"'Related Object Id' no debería estar vacío.\") o de más de 36 caracteres (\"'Related Object Id' debe ser menor o igual que 36 caracteres. Ingresó {n} caracteres.\"); un webhookId vacío (\"'Webhook Id' no debería estar vacío.\"); un campo que el cuerpo no admite (p. ej. webhookEndpointId), con el texto \"El campo '{field}' no existe en este recurso.\"; una persona que no es del tenant ('La persona especificada no es válida o no pertenece a tu organización.'); un webhook de URL deshabilitado ('El webhook está deshabilitado: habilítalo antes de probarlo.'). 401: sin API key o JWT válidos. 403: la persona no tiene el permiso Webhooks.Manage. También 403 fuera de los ambientes de prueba (solo Development y Test): 'fiscalapi trigger no está disponible en producción; usa el ambiente de pruebas.'. 404 ('No such resource for the given Id: {id}'): el webhook no existe, está dado de baja, es de otro tenant o es la escucha de CLI de otra persona (salvo para el owner). 409 ('La escucha de CLI no está conectada; ejecuta fiscalapi listen.'): la escucha de CLI no tiene una sesión vigente de fiscalapi listen, o quien llama no tiene escucha. 429: demasiadas peticiones con la misma API key.", + "operationId": "triggerEvent", + "parameters": [ + { + "name": "X-TIME-ZONE", + "in": "header", + "description": "Zona horaria IANA de las fechas sin zona del cuerpo JSON: el request las lee y la respuesta las escribe en ella. Si falta o no es válida, America/Mexico_City. No aplica a los campos que la operación documenta en UTC o en hora civil.", + "schema": { + "type": "string" + }, + "example": "America/Mexico_City" + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TriggerWebhookEventCommand" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/TriggerWebhookEventCommand" + } + }, + "application/*+json": { + "schema": { + "$ref": "#/components/schemas/TriggerWebhookEventCommand" + } + } + }, + "required": true + }, + "responses": { + "202": { + "description": "Accepted", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfWebhookEventTriggerDto" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfWebhookEventTriggerDto" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfWebhookEventTriggerDto" + } + } + } + }, + "400": { + "description": "Bad Request", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + } + } + }, + "409": { + "description": "Conflict", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + } + } + }, + "429": { + "description": "Too Many Requests", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + } + } + } + }, + "security": [ + { + "ApiKey": [ ], + "TenantKey": [ ] + } + ] + } + }, + "/api/v4/invoices": { + "get": { + "tags": [ + "Invoices" + ], + "summary": "Listar facturas", + "description": "Las facturas (CFDI) del tenant, paginadas con pageNumber y pageSize (10 por omisión, máximo 50) y ordenadas de la más antigua a la más reciente. Docs: [por valores](https://docs.fiscalapi.com/invoices-by-value#listar-facturas) y [por referencias](https://docs.fiscalapi.com/invoices-by-reference#listar-facturas).", + "operationId": "listInvoices", + "parameters": [ + { + "name": "PageNumber", + "in": "query", + "schema": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "integer", + "string" + ], + "format": "int32" + } + }, + { + "name": "PageSize", + "in": "query", + "schema": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "integer", + "string" + ], + "format": "int32" + } + }, + { + "name": "X-TIME-ZONE", + "in": "header", + "description": "Zona horaria IANA de las fechas sin zona del cuerpo JSON: el request las lee y la respuesta las escribe en ella. Si falta o no es válida, America/Mexico_City. No aplica a los campos que la operación documenta en UTC o en hora civil.", + "schema": { + "type": "string" + }, + "example": "America/Mexico_City" + } + ], + "responses": { + "200": { + "description": "OK" + } + }, + "security": [ + { + "ApiKey": [ ], + "TenantKey": [ ] + } + ] + }, + "post": { + "tags": [ + "Invoices" + ], + "summary": "Emitir (timbrar) una factura CFDI 4.0", + "description": "Timbra el CFDI ante el PAC y cobra 1 timbre del saldo; si el PAC lo rechaza, el timbre se reembolsa. Sin saldo, responde 403. typeCode y complement deciden el comprobante: factura de ingreso (I), nota de crédito o egreso (E), complemento de pago (P), recibo de nómina (N) o traslado (T), con carta porte o comercio exterior; globalInformation la vuelve factura global. Por valores, el cuerpo lleva los datos completos del emisor y del receptor; por referencias, issuer.id y recipient.id de personas ya registradas. Docs: [por valores](https://docs.fiscalapi.com/invoices-by-value#crear-factura-ingreso) y [por referencias](https://docs.fiscalapi.com/invoices-by-reference#crear-factura-ingreso).", + "operationId": "createInvoice", + "parameters": [ + { + "name": "X-TIME-ZONE", + "in": "header", + "description": "Zona horaria IANA de las fechas sin zona del cuerpo JSON: el request las lee y la respuesta las escribe en ella. Si falta o no es válida, America/Mexico_City. No aplica a los campos que la operación documenta en UTC o en hora civil.", + "schema": { + "type": "string" + }, + "example": "America/Mexico_City" + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/InvoiceRequest" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/InvoiceRequest" + } + }, + "application/*+json": { + "schema": { + "$ref": "#/components/schemas/InvoiceRequest" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK" + } + }, + "security": [ + { + "ApiKey": [ ], + "TenantKey": [ ] + } + ] + }, + "delete": { + "tags": [ + "Invoices" + ], + "summary": "Cancelar una factura ante el SAT", + "description": "Solicita al SAT, por medio del PAC, la cancelación del CFDI con el motivo en cancellationReasonCode (01, 02, 03 o 04); el motivo 01 exige replacementUuid, el UUID de la factura que la sustituye. Por referencias basta el id; por valores van invoiceUuid, tin y taxCredentials (el CSD del emisor). La respuesta no confirma la cancelación: el SAT puede esperar la aceptación del receptor, así que consulta después su estatus. Docs: [por valores](https://docs.fiscalapi.com/invoices-by-value#cancelar-factura) y [por referencias](https://docs.fiscalapi.com/invoices-by-reference#cancelar-factura).", + "operationId": "cancelInvoice", + "parameters": [ + { + "name": "X-TIME-ZONE", + "in": "header", + "description": "Zona horaria IANA de las fechas sin zona del cuerpo JSON: el request las lee y la respuesta las escribe en ella. Si falta o no es válida, America/Mexico_City. No aplica a los campos que la operación documenta en UTC o en hora civil.", + "schema": { + "type": "string" + }, + "example": "America/Mexico_City" + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CancelInvoiceCommand" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/CancelInvoiceCommand" + } + }, + "application/*+json": { + "schema": { + "$ref": "#/components/schemas/CancelInvoiceCommand" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK" + } + }, + "security": [ + { + "ApiKey": [ ], + "TenantKey": [ ] + } + ] + } + }, + "/api/v4/invoices/{id}": { + "get": { + "tags": [ + "Invoices" + ], + "summary": "Obtener una factura", + "description": "Una factura por su id. Con details=true incluye el estado, el emisor, el receptor y los conceptos con sus impuestos. Docs: [por valores](https://docs.fiscalapi.com/invoices-by-value#obtener-factura-por-id) y [por referencias](https://docs.fiscalapi.com/invoices-by-reference#obtener-factura-por-id).", + "operationId": "getInvoice", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "details", + "in": "query", + "schema": { + "type": "boolean", + "default": false + } + }, + { + "name": "X-TIME-ZONE", + "in": "header", + "description": "Zona horaria IANA de las fechas sin zona del cuerpo JSON: el request las lee y la respuesta las escribe en ella. Si falta o no es válida, America/Mexico_City. No aplica a los campos que la operación documenta en UTC o en hora civil.", + "schema": { + "type": "string" + }, + "example": "America/Mexico_City" + } + ], + "responses": { + "200": { + "description": "OK" + } + }, + "security": [ + { + "ApiKey": [ ], + "TenantKey": [ ] + } + ] + } + }, + "/api/v4/invoices/calculate": { + "post": { + "tags": [ + "Invoices" + ], + "summary": "Calcular los totales de una factura sin timbrar", + "description": "Con el mismo cuerpo que la emisión, responde subTotal, discount y total sin timbrar, sin cobrar timbres y sin consumir folio: sirve para previsualizar los importes antes de emitir. Docs: [por valores](https://docs.fiscalapi.com/invoices-by-value#modelo-factura) y [por referencias](https://docs.fiscalapi.com/invoices-by-reference#modelo-factura).", + "operationId": "calculateInvoice", + "parameters": [ + { + "name": "X-TIME-ZONE", + "in": "header", + "description": "Zona horaria IANA de las fechas sin zona del cuerpo JSON: el request las lee y la respuesta las escribe en ella. Si falta o no es válida, America/Mexico_City. No aplica a los campos que la operación documenta en UTC o en hora civil.", + "schema": { + "type": "string" + }, + "example": "America/Mexico_City" + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/InvoiceRequest" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/InvoiceRequest" + } + }, + "application/*+json": { + "schema": { + "$ref": "#/components/schemas/InvoiceRequest" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK" + } + }, + "security": [ + { + "ApiKey": [ ], + "TenantKey": [ ] + } + ] + } + }, + "/api/v4/invoices/pdf": { + "post": { + "tags": [ + "Invoices" + ], + "summary": "Generar el PDF de una factura", + "description": "La representación impresa de una factura ya emitida, en base64 (base64File). Admite base64Logo, bandColor y fontColor para personalizarla. No timbra ni cobra timbres. Docs: [por valores](https://docs.fiscalapi.com/invoices-by-value#generar-pdf-por-id) y [por referencias](https://docs.fiscalapi.com/invoices-by-reference#generar-pdf-por-id).", + "operationId": "getInvoicePdf", + "parameters": [ + { + "name": "X-TIME-ZONE", + "in": "header", + "description": "Zona horaria IANA de las fechas sin zona del cuerpo JSON: el request las lee y la respuesta las escribe en ella. Si falta o no es válida, America/Mexico_City. No aplica a los campos que la operación documenta en UTC o en hora civil.", + "schema": { + "type": "string" + }, + "example": "America/Mexico_City" + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreatePdfCommand" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/CreatePdfCommand" + } + }, + "application/*+json": { + "schema": { + "$ref": "#/components/schemas/CreatePdfCommand" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK" + } + }, + "security": [ + { + "ApiKey": [ ], + "TenantKey": [ ] + } + ] + } + }, + "/api/v4/invoices/{id}/xml": { + "get": { + "tags": [ + "Invoices" + ], + "summary": "Descargar el XML timbrado de una factura", + "description": "El XML del CFDI timbrado de una factura, en base64 (base64File). Docs: [por valores](https://docs.fiscalapi.com/invoices-by-value#obtener-xml-por-id) y [por referencias](https://docs.fiscalapi.com/invoices-by-reference#obtener-xml-por-id).", + "operationId": "getInvoiceXml", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "X-TIME-ZONE", + "in": "header", + "description": "Zona horaria IANA de las fechas sin zona del cuerpo JSON: el request las lee y la respuesta las escribe en ella. Si falta o no es válida, America/Mexico_City. No aplica a los campos que la operación documenta en UTC o en hora civil.", + "schema": { + "type": "string" + }, + "example": "America/Mexico_City" + } + ], + "responses": { + "200": { + "description": "OK" + } + }, + "security": [ + { + "ApiKey": [ ], + "TenantKey": [ ] + } + ] + } + }, + "/api/v4/invoices/send": { + "post": { + "tags": [ + "Invoices" + ], + "summary": "Enviar una factura por correo", + "description": "Envía el PDF y el XML de la factura a toEmail o, si falta, al correo del receptor. Admite el mismo logo y colores que el PDF. El envío se programa y puede tardar unos minutos. Docs: [por valores](https://docs.fiscalapi.com/invoices-by-value#enviar-factura-por-correo) y [por referencias](https://docs.fiscalapi.com/invoices-by-reference#enviar-factura-por-correo).", + "operationId": "sendInvoice", + "parameters": [ + { + "name": "X-TIME-ZONE", + "in": "header", + "description": "Zona horaria IANA de las fechas sin zona del cuerpo JSON: el request las lee y la respuesta las escribe en ella. Si falta o no es válida, America/Mexico_City. No aplica a los campos que la operación documenta en UTC o en hora civil.", + "schema": { + "type": "string" + }, + "example": "America/Mexico_City" + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SendInvoiceByEmailCommand" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/SendInvoiceByEmailCommand" + } + }, + "application/*+json": { + "schema": { + "$ref": "#/components/schemas/SendInvoiceByEmailCommand" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK" + } + }, + "security": [ + { + "ApiKey": [ ], + "TenantKey": [ ] + } + ] + } + }, + "/api/v4/invoices/status": { + "post": { + "tags": [ + "Invoices" + ], + "summary": "Consultar el estatus de una factura ante el SAT", + "description": "Consulta en el SAT si el CFDI está vigente, cancelado o no encontrado, si es cancelable y el avance de su cancelación. Identifica la factura con su id o con issuerTin, recipientTin, invoiceTotal, invoiceUuid y last8DigitsIssuerSignature, no con ambos. En el ambiente de pruebas el SAT se simula y details lo avisa. Docs: [por valores](https://docs.fiscalapi.com/invoices-by-value#consultar-estado-de-factura) y [por referencias](https://docs.fiscalapi.com/invoices-by-reference#consultar-estado-de-factura).", + "operationId": "getInvoiceStatus", + "parameters": [ + { + "name": "X-TIME-ZONE", + "in": "header", + "description": "Zona horaria IANA de las fechas sin zona del cuerpo JSON: el request las lee y la respuesta las escribe en ella. Si falta o no es válida, America/Mexico_City. No aplica a los campos que la operación documenta en UTC o en hora civil.", + "schema": { + "type": "string" + }, + "example": "America/Mexico_City" + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/StatusCheckCommand" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/StatusCheckCommand" + } + }, + "application/*+json": { + "schema": { + "$ref": "#/components/schemas/StatusCheckCommand" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK" + } + }, + "security": [ + { + "ApiKey": [ ], + "TenantKey": [ ] + } + ] + } + }, + "/api/v4/manifests": { + "post": { + "tags": [ + "Manifests" + ], + "summary": "Firmar la carta manifiesto con la FIEL", + "description": "Firma con el PAC la carta manifiesto del RFC de la FIEL que envías en base64Cer, base64Key y password (obligatorios; cada archivo hasta 24,576 caracteres en base64 y la contraseña hasta 256) y devuelve el PDF firmado en base64File; fileName es el RFC con extensión .pdf. Todas las personas del tenant con ese RFC quedan con manifestStatusId Signed. Responde 400 si la contraseña no abre la llave o la credencial no es una FIEL vigente, y 404 si ninguna persona del tenant tiene ese RFC. Docs: [firmar carta manifiesto](https://docs.fiscalapi.com/manifests#firmar-carta-manifiesto) y [límites](https://docs.fiscalapi.com/manifests#limites).", + "operationId": "signManifest", + "parameters": [ + { + "name": "X-TIME-ZONE", + "in": "header", + "description": "Zona horaria IANA de las fechas sin zona del cuerpo JSON: el request las lee y la respuesta las escribe en ella. Si falta o no es válida, America/Mexico_City. No aplica a los campos que la operación documenta en UTC o en hora civil.", + "schema": { + "type": "string" + }, + "example": "America/Mexico_City" + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SignManifestCommand" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/SignManifestCommand" + } + }, + "application/*+json": { + "schema": { + "$ref": "#/components/schemas/SignManifestCommand" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK" + } + }, + "security": [ + { + "ApiKey": [ ], + "TenantKey": [ ] + } + ] + } + }, + "/api/v4/people": { + "get": { + "tags": [ + "People" + ], + "summary": "Listar personas: emisores, receptores, empleados y empleadores", + "description": "Todas las personas del tenant en una sola lista, paginadas con pageNumber y pageSize (50 por omisión y máximo). La misma persona puede ser emisor, cliente receptor, empleado o empleador según dónde se use su id. Docs: [emisores](https://docs.fiscalapi.com/issuers#listar-emisores) y [receptores](https://docs.fiscalapi.com/recipients#listar-receptores).", + "operationId": "listPeople", + "parameters": [ + { + "name": "PageNumber", + "in": "query", + "schema": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "integer", + "string" + ], + "format": "int32" + } + }, + { + "name": "PageSize", + "in": "query", + "schema": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "integer", + "string" + ], + "format": "int32" + } + }, + { + "name": "X-TIME-ZONE", + "in": "header", + "description": "Zona horaria IANA de las fechas sin zona del cuerpo JSON: el request las lee y la respuesta las escribe en ella. Si falta o no es válida, America/Mexico_City. No aplica a los campos que la operación documenta en UTC o en hora civil.", + "schema": { + "type": "string" + }, + "example": "America/Mexico_City" + } + ], + "responses": { + "200": { + "description": "OK" + } + }, + "security": [ + { + "ApiKey": [ ], + "TenantKey": [ ] + } + ] + }, + "post": { + "tags": [ + "People" + ], + "summary": "Crear una persona: emisor o cliente receptor", + "description": "Da de alta una persona para usar su id como emisor (issuer.id) o cliente receptor (recipient.id) al facturar por referencias, o como empleado o empleador en nómina. legalName, email y password son obligatorios y el correo no debe estar registrado; los datos fiscales van en tin, satTaxRegimeId, satCfdiUseId y zipCode. Docs: [emisores](https://docs.fiscalapi.com/issuers#crear-emisor) y [receptores](https://docs.fiscalapi.com/recipients#crear-receptor).", + "operationId": "createPerson", + "parameters": [ + { + "name": "X-TIME-ZONE", + "in": "header", + "description": "Zona horaria IANA de las fechas sin zona del cuerpo JSON: el request las lee y la respuesta las escribe en ella. Si falta o no es válida, America/Mexico_City. No aplica a los campos que la operación documenta en UTC o en hora civil.", + "schema": { + "type": "string" + }, + "example": "America/Mexico_City" + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateUserCommand" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/CreateUserCommand" + } + }, + "application/*+json": { + "schema": { + "$ref": "#/components/schemas/CreateUserCommand" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK" + } + }, + "security": [ + { + "ApiKey": [ ], + "TenantKey": [ ] + } + ] + } + }, + "/api/v4/people/{id}": { + "get": { + "tags": [ + "People" + ], + "summary": "Obtener una persona", + "description": "Una persona (emisor, cliente receptor, empleado o empleador) por su id, con sus datos fiscales: RFC (tin), régimen fiscal, uso de CFDI y código postal. Docs: [emisores](https://docs.fiscalapi.com/issuers#obtener-emisor-por-id) y [receptores](https://docs.fiscalapi.com/recipients#obtener-receptor-por-id).", + "operationId": "getPerson", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "details", + "in": "query", + "schema": { + "type": "boolean", + "default": false + } + }, + { + "name": "X-TIME-ZONE", + "in": "header", + "description": "Zona horaria IANA de las fechas sin zona del cuerpo JSON: el request las lee y la respuesta las escribe en ella. Si falta o no es válida, America/Mexico_City. No aplica a los campos que la operación documenta en UTC o en hora civil.", + "schema": { + "type": "string" + }, + "example": "America/Mexico_City" + } + ], + "responses": { + "200": { + "description": "OK" + } + }, + "security": [ + { + "ApiKey": [ ], + "TenantKey": [ ] + } + ] + }, + "patch": { + "tags": [ + "People" + ], + "summary": "Actualizar parcialmente una persona", + "description": "Cambia solo los campos que vienen: un campo omitido o null no se toca y \"\" lo borra (legalName, email y password no se pueden borrar). El id de la ruta y el del cuerpo deben coincidir; un cambio concurrente responde 409. Docs: [emisores](https://docs.fiscalapi.com/issuers#actualizar-emisor) y [receptores](https://docs.fiscalapi.com/recipients#actualizar-receptor).", + "operationId": "patchPerson", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "X-TIME-ZONE", + "in": "header", + "description": "Zona horaria IANA de las fechas sin zona del cuerpo JSON: el request las lee y la respuesta las escribe en ella. Si falta o no es válida, America/Mexico_City. No aplica a los campos que la operación documenta en UTC o en hora civil.", + "schema": { + "type": "string" + }, + "example": "America/Mexico_City" + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PatchUserCommand" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/PatchUserCommand" + } + }, + "application/*+json": { + "schema": { + "$ref": "#/components/schemas/PatchUserCommand" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK" + } + }, + "security": [ + { + "ApiKey": [ ], + "TenantKey": [ ] + } + ] + }, + "put": { + "tags": [ + "People" + ], + "summary": "Actualizar una persona", + "description": "Reemplaza los datos de la persona con el cuerpo completo: un campo omitido se borra (solo userTypeId y taxPassword se conservan). legalName y email son obligatorios. Para cambiar solo algunos campos usa la actualización parcial (PATCH). El id de la ruta y el del cuerpo deben coincidir; un cambio concurrente responde 409. Docs: [emisores](https://docs.fiscalapi.com/issuers#actualizar-emisor) y [receptores](https://docs.fiscalapi.com/recipients#actualizar-receptor).", + "operationId": "updatePerson", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "X-TIME-ZONE", + "in": "header", + "description": "Zona horaria IANA de las fechas sin zona del cuerpo JSON: el request las lee y la respuesta las escribe en ella. Si falta o no es válida, America/Mexico_City. No aplica a los campos que la operación documenta en UTC o en hora civil.", + "schema": { + "type": "string" + }, + "example": "America/Mexico_City" + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpdateUserCommand" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/UpdateUserCommand" + } + }, + "application/*+json": { + "schema": { + "$ref": "#/components/schemas/UpdateUserCommand" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK" + } + }, + "security": [ + { + "ApiKey": [ ], + "TenantKey": [ ] + } + ] + }, + "delete": { + "tags": [ + "People" + ], + "summary": "Eliminar una persona", + "description": "Da de baja la persona, libera su correo y revoca sus API keys. Valida en este orden: 400 si tiene reglas de descarga aprobadas o certificados (elimínalos primero) y 403 si es el owner, que nunca se puede eliminar. Docs: [emisores](https://docs.fiscalapi.com/issuers#eliminar-emisor) y [receptores](https://docs.fiscalapi.com/recipients#eliminar-receptor).", + "operationId": "deletePerson", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "X-TIME-ZONE", + "in": "header", + "description": "Zona horaria IANA de las fechas sin zona del cuerpo JSON: el request las lee y la respuesta las escribe en ella. Si falta o no es válida, America/Mexico_City. No aplica a los campos que la operación documenta en UTC o en hora civil.", + "schema": { + "type": "string" + }, + "example": "America/Mexico_City" + } + ], + "responses": { + "200": { + "description": "OK" + } + }, + "security": [ + { + "ApiKey": [ ], + "TenantKey": [ ] + } + ] + } + }, + "/api/v4/people/{personId}/employee": { + "get": { + "tags": [ + "People" + ], + "summary": "Obtener los datos de empleado de una persona", + "description": "Los datos del empleado que usa el recibo de nómina: número de empleado, tipo de contrato, régimen, periodicidad de pago, seguridad social y salarios, entre otros. Responde 404 si la persona no los tiene. Docs: [datos de empleado](https://docs.fiscalapi.com/employee-data#obtener-datos-de-empleado).", + "operationId": "getEmployeeData", + "parameters": [ + { + "name": "personId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "X-TIME-ZONE", + "in": "header", + "description": "Zona horaria IANA de las fechas sin zona del cuerpo JSON: el request las lee y la respuesta las escribe en ella. Si falta o no es válida, America/Mexico_City. No aplica a los campos que la operación documenta en UTC o en hora civil.", + "schema": { + "type": "string" + }, + "example": "America/Mexico_City" + } + ], + "responses": { + "200": { + "description": "OK" + } + }, + "security": [ + { + "ApiKey": [ ], + "TenantKey": [ ] + } + ] + }, + "post": { + "tags": [ + "People" + ], + "summary": "Crear los datos de empleado de una persona", + "description": "Registra los datos del empleado para el recibo de nómina. Son obligatorios employerPersonId (la persona empleadora), employeeNumber, satContractTypeId, satTaxRegimeTypeId, satPaymentPeriodicityId y satPayrollStateId; employeePersonId es el personId de la ruta. Cada persona tiene un solo registro: si ya existe, responde 400 y se modifica con PUT. Docs: [datos de empleado](https://docs.fiscalapi.com/employee-data#crear-datos-de-empleado).", + "operationId": "createEmployeeData", + "parameters": [ + { + "name": "personId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "X-TIME-ZONE", + "in": "header", + "description": "Zona horaria IANA de las fechas sin zona del cuerpo JSON: el request las lee y la respuesta las escribe en ella. Si falta o no es válida, America/Mexico_City. No aplica a los campos que la operación documenta en UTC o en hora civil.", + "schema": { + "type": "string" + }, + "example": "America/Mexico_City" + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateEmployeeDataCommand" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/CreateEmployeeDataCommand" + } + }, + "application/*+json": { + "schema": { + "$ref": "#/components/schemas/CreateEmployeeDataCommand" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK" + } + }, + "security": [ + { + "ApiKey": [ ], + "TenantKey": [ ] + } + ] + }, + "put": { + "tags": [ + "People" + ], + "summary": "Actualizar los datos de empleado de una persona", + "description": "Actualiza los datos del empleado para el recibo de nómina; employeePersonId es el personId de la ruta. Responde 404 si la persona no tiene datos de empleado. Docs: [datos de empleado](https://docs.fiscalapi.com/employee-data#actualizar-datos-de-empleado).", + "operationId": "updateEmployeeData", + "parameters": [ + { + "name": "personId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "X-TIME-ZONE", + "in": "header", + "description": "Zona horaria IANA de las fechas sin zona del cuerpo JSON: el request las lee y la respuesta las escribe en ella. Si falta o no es válida, America/Mexico_City. No aplica a los campos que la operación documenta en UTC o en hora civil.", + "schema": { + "type": "string" + }, + "example": "America/Mexico_City" + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpdateEmployeeDataCommand" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/UpdateEmployeeDataCommand" + } + }, + "application/*+json": { + "schema": { + "$ref": "#/components/schemas/UpdateEmployeeDataCommand" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK" + } + }, + "security": [ + { + "ApiKey": [ ], + "TenantKey": [ ] + } + ] + }, + "delete": { + "tags": [ + "People" + ], + "summary": "Eliminar los datos de empleado de una persona", + "description": "Borra los datos de empleado; la persona se conserva. Responde 400 (data false) si la persona no los tenía. Docs: [datos de empleado](https://docs.fiscalapi.com/employee-data#eliminar-datos-de-empleado).", + "operationId": "deleteEmployeeData", + "parameters": [ + { + "name": "personId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "X-TIME-ZONE", + "in": "header", + "description": "Zona horaria IANA de las fechas sin zona del cuerpo JSON: el request las lee y la respuesta las escribe en ella. Si falta o no es válida, America/Mexico_City. No aplica a los campos que la operación documenta en UTC o en hora civil.", + "schema": { + "type": "string" + }, + "example": "America/Mexico_City" + } + ], + "responses": { + "200": { + "description": "OK" + } + }, + "security": [ + { + "ApiKey": [ ], + "TenantKey": [ ] + } + ] + } + }, + "/api/v4/people/{personId}/employer": { + "get": { + "tags": [ + "People" + ], + "summary": "Obtener los datos de empleador de una persona", + "description": "Los datos del empleador (patrón) que usa el recibo de nómina: registro patronal (employerRegistration), RFC del patrón de origen (originEmployerTin), origen de los recursos (satFundSource) y monto de recursos propios (ownResourceAmount). Responde 404 si la persona no los tiene. Docs: [datos de empleador](https://docs.fiscalapi.com/employer-data#obtener-datos-de-empleador).", + "operationId": "getEmployerData", + "parameters": [ + { + "name": "personId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "X-TIME-ZONE", + "in": "header", + "description": "Zona horaria IANA de las fechas sin zona del cuerpo JSON: el request las lee y la respuesta las escribe en ella. Si falta o no es válida, America/Mexico_City. No aplica a los campos que la operación documenta en UTC o en hora civil.", + "schema": { + "type": "string" + }, + "example": "America/Mexico_City" + } + ], + "responses": { + "200": { + "description": "OK" + } + }, + "security": [ + { + "ApiKey": [ ], + "TenantKey": [ ] + } + ] + }, + "post": { + "tags": [ + "People" + ], + "summary": "Crear los datos de empleador de una persona", + "description": "Registra los datos del empleador (patrón) para el recibo de nómina; personId del cuerpo es el de la ruta. Cada persona tiene un solo registro: si ya existe, responde 400 y se modifica con PUT. Docs: [datos de empleador](https://docs.fiscalapi.com/employer-data#crear-datos-de-empleador).", + "operationId": "createEmployerData", + "parameters": [ + { + "name": "personId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "X-TIME-ZONE", + "in": "header", + "description": "Zona horaria IANA de las fechas sin zona del cuerpo JSON: el request las lee y la respuesta las escribe en ella. Si falta o no es válida, America/Mexico_City. No aplica a los campos que la operación documenta en UTC o en hora civil.", + "schema": { + "type": "string" + }, + "example": "America/Mexico_City" + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateEmployerDataCommand" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/CreateEmployerDataCommand" + } + }, + "application/*+json": { + "schema": { + "$ref": "#/components/schemas/CreateEmployerDataCommand" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK" + } + }, + "security": [ + { + "ApiKey": [ ], + "TenantKey": [ ] + } + ] + }, + "put": { + "tags": [ + "People" + ], + "summary": "Actualizar los datos de empleador de una persona", + "description": "Actualiza los datos del empleador (patrón) para el recibo de nómina; personId del cuerpo es el de la ruta. Responde 404 si la persona no tiene datos de empleador. Docs: [datos de empleador](https://docs.fiscalapi.com/employer-data#actualizar-datos-de-empleador).", + "operationId": "updateEmployerData", + "parameters": [ + { + "name": "personId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "X-TIME-ZONE", + "in": "header", + "description": "Zona horaria IANA de las fechas sin zona del cuerpo JSON: el request las lee y la respuesta las escribe en ella. Si falta o no es válida, America/Mexico_City. No aplica a los campos que la operación documenta en UTC o en hora civil.", + "schema": { + "type": "string" + }, + "example": "America/Mexico_City" + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpdateEmployerDataCommand" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/UpdateEmployerDataCommand" + } + }, + "application/*+json": { + "schema": { + "$ref": "#/components/schemas/UpdateEmployerDataCommand" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK" + } + }, + "security": [ + { + "ApiKey": [ ], + "TenantKey": [ ] + } + ] + }, + "delete": { + "tags": [ + "People" + ], + "summary": "Eliminar los datos de empleador de una persona", + "description": "Borra los datos de empleador; la persona se conserva. Responde 400 (data false) si la persona no los tenía. Docs: [datos de empleador](https://docs.fiscalapi.com/employer-data#eliminar-datos-de-empleador).", + "operationId": "deleteEmployerData", + "parameters": [ + { + "name": "personId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "X-TIME-ZONE", + "in": "header", + "description": "Zona horaria IANA de las fechas sin zona del cuerpo JSON: el request las lee y la respuesta las escribe en ella. Si falta o no es válida, America/Mexico_City. No aplica a los campos que la operación documenta en UTC o en hora civil.", + "schema": { + "type": "string" + }, + "example": "America/Mexico_City" + } + ], + "responses": { + "200": { + "description": "OK" + } + }, + "security": [ + { + "ApiKey": [ ], + "TenantKey": [ ] + } + ] + } + }, + "/api/v4/portal/{tenantName}/branding": { + "get": { + "tags": [ + "Portal" + ], + "summary": "Obtener la marca del portal", + "description": "La marca del portal de un tenant por su nombre de portal: nombre canónico (distinto del recibido si llegó un alias), logo, colores, si el portal atiende y la URL del aviso de privacidad. Anónimo. 404: el 404 genérico del portal, el mismo cuerpo para todo lo que no existe o no está disponible (no revela tenants ni tickets).", + "operationId": "getPortalBranding", + "parameters": [ + { + "name": "tenantName", + "in": "path", + "description": "El nombre del portal del tenant o uno de sus alias.", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "X-TIME-ZONE", + "in": "header", + "description": "Zona horaria IANA de las fechas sin zona del cuerpo JSON: el request las lee y la respuesta las escribe en ella. Si falta o no es válida, America/Mexico_City. No aplica a los campos que la operación documenta en UTC o en hora civil.", + "schema": { + "type": "string" + }, + "example": "America/Mexico_City" + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfPortalBrandingDto" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfPortalBrandingDto" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfPortalBrandingDto" + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + } + } + } + }, + "security": [ ] + } + }, + "/api/v4/portal/tickets/{ticketCode}": { + "get": { + "tags": [ + "Portal" + ], + "summary": "Consultar un ticket en el portal", + "description": "El ticket por su código: el emisor, la marca, la venta con sus conceptos e impuestos, su estado y si el receptor puede autofacturarlo (o por qué no, en ineligibleReason), con el último momento facturable en eligibleUntil (como en la API de tickets; null si no vence). Si ya tiene factura, trae el correo enmascarado para recuperarla. Anónimo, con reCAPTCHA v3 (acción autoinvoice_lookup). 403: falló la verificación de reCAPTCHA (token inválido, de otra acción o con puntuación baja). 404: el 404 genérico del portal, el mismo cuerpo para todo lo que no existe o no está disponible (no revela tenants ni tickets). 429: demasiadas solicitudes desde la misma IP; el header Retry-After dice en cuántos segundos reintentar.", + "operationId": "getPortalTicket", + "parameters": [ + { + "name": "ticketCode", + "in": "path", + "description": "El código del ticket, con o sin guiones, como lo lleva el QR.", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "X-Recaptcha-Token", + "in": "header", + "description": "Token de reCAPTCHA v3 de la acción autoinvoice_lookup.", + "schema": { + "type": "string" + } + }, + { + "name": "X-TIME-ZONE", + "in": "header", + "description": "Zona horaria IANA de las fechas sin zona del cuerpo JSON: el request las lee y la respuesta las escribe en ella. Si falta o no es válida, America/Mexico_City. No aplica a los campos que la operación documenta en UTC o en hora civil.", + "schema": { + "type": "string" + }, + "example": "America/Mexico_City" + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfPortalTicketDto" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfPortalTicketDto" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfPortalTicketDto" + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + } + } + }, + "429": { + "description": "Too Many Requests", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + } + } + } + }, + "security": [ ] + } + }, + "/api/v4/portal/tickets/{ticketCode}/invoice": { + "post": { + "tags": [ + "Portal" + ], + "summary": "Autofacturar un ticket", + "description": "Timbra la factura individual del ticket con los datos fiscales del receptor y la envía a su correo. Anónimo, con reCAPTCHA v3 (acción autoinvoice_invoice). 201: quedó timbrada (data.uuid, data.downloadToken, data.emailSent y el correo enmascarado en data.maskedEmail). 202: otra solicitud la está facturando o su resultado aún no se conoce (data.status: invoicing); consulta el ticket después. Los rechazos sobre el ticket o el receptor llevan data.errorCode, y data.field si hay que corregir un dato del receptor. 400: el cuerpo no es un JSON con la estructura esperada. 403: falló la verificación de reCAPTCHA (token inválido, de otra acción o con puntuación baja). 404: el 404 genérico del portal, el mismo cuerpo para todo lo que no existe o no está disponible (no revela tenants ni tickets). 409: el ticket ya tiene su factura (TicketAlreadyInvoiced) o está en una factura global (TicketInGlobalInvoice). 422: el ticket venció (TicketExpired), el comercio no factura en línea (SelfInvoicingDisabled) o no puede facturar (MerchantCannotInvoice), un dato del receptor no es válido para el SAT (InvalidRecipientData y Recipient*) o el SAT rechazó la factura (InvoiceRejected). 429: demasiadas solicitudes desde la misma IP; el header Retry-After dice en cuántos segundos reintentar. 503: el servicio de timbrado no respondió (ServiceUnavailable); no se cobró nada y se puede reintentar.", + "operationId": "invoicePortalTicket", + "parameters": [ + { + "name": "ticketCode", + "in": "path", + "description": "El código del ticket, con o sin guiones, como lo lleva el QR.", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "X-TIME-ZONE", + "in": "header", + "description": "Zona horaria IANA de las fechas sin zona del cuerpo JSON: el request las lee y la respuesta las escribe en ella. Si falta o no es válida, America/Mexico_City. No aplica a los campos que la operación documenta en UTC o en hora civil.", + "schema": { + "type": "string" + }, + "example": "America/Mexico_City" + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PortalInvoiceTicketBody" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/PortalInvoiceTicketBody" + } + }, + "application/*+json": { + "schema": { + "$ref": "#/components/schemas/PortalInvoiceTicketBody" + } + } + }, + "required": true + }, + "responses": { + "201": { + "description": "Created", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfPortalInvoiceDto" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfPortalInvoiceDto" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfPortalInvoiceDto" + } + } + } + }, + "202": { + "description": "Accepted", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfPortalInvoiceDto" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfPortalInvoiceDto" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfPortalInvoiceDto" + } + } + } + }, + "400": { + "description": "Bad Request", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfPortalInvoiceDto" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfPortalInvoiceDto" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfPortalInvoiceDto" + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfPortalInvoiceDto" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfPortalInvoiceDto" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfPortalInvoiceDto" + } + } + } + }, + "409": { + "description": "Conflict", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfPortalInvoiceDto" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfPortalInvoiceDto" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfPortalInvoiceDto" + } + } + } + }, + "422": { + "description": "Unprocessable Entity", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfPortalInvoiceDto" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfPortalInvoiceDto" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfPortalInvoiceDto" + } + } + } + }, + "429": { + "description": "Too Many Requests", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + } + } + }, + "503": { + "description": "Service Unavailable", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfPortalInvoiceDto" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfPortalInvoiceDto" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfPortalInvoiceDto" + } + } + } + } + }, + "security": [ ] + } + }, + "/api/v4/portal/tickets/{ticketCode}/recover": { + "post": { + "tags": [ + "Portal" + ], + "summary": "Recuperar la factura de un ticket", + "description": "Entrega el token de descarga de la factura del ticket (data.downloadToken) si el correo es el del receptor. Cinco intentos fallidos en 24 horas bloquean la recuperación del ticket por 24 horas. Anónimo, con reCAPTCHA v3 (acción autoinvoice_recover). 400: el cuerpo no es un JSON con la estructura esperada. 403: falló la verificación de reCAPTCHA (data en null) o no se pudo recuperar la factura (RecoveryRejected: correo distinto, ticket sin factura o recuperación bloqueada). 404: el 404 genérico del portal, el mismo cuerpo para todo lo que no existe o no está disponible (no revela tenants ni tickets). 422: falta el correo (InvalidRecipientData, data.field: email); no cuenta como intento. 429: demasiadas solicitudes desde la misma IP; el header Retry-After dice en cuántos segundos reintentar.", + "operationId": "recoverPortalTicketInvoice", + "parameters": [ + { + "name": "ticketCode", + "in": "path", + "description": "El código del ticket, con o sin guiones, como lo lleva el QR.", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "X-TIME-ZONE", + "in": "header", + "description": "Zona horaria IANA de las fechas sin zona del cuerpo JSON: el request las lee y la respuesta las escribe en ella. Si falta o no es válida, America/Mexico_City. No aplica a los campos que la operación documenta en UTC o en hora civil.", + "schema": { + "type": "string" + }, + "example": "America/Mexico_City" + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PortalRecoverTicketBody" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/PortalRecoverTicketBody" + } + }, + "application/*+json": { + "schema": { + "$ref": "#/components/schemas/PortalRecoverTicketBody" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfPortalRecoveryDto" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfPortalRecoveryDto" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfPortalRecoveryDto" + } + } + } + }, + "400": { + "description": "Bad Request", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfPortalRecoveryDto" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfPortalRecoveryDto" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfPortalRecoveryDto" + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfPortalRecoveryDto" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfPortalRecoveryDto" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfPortalRecoveryDto" + } + } + } + }, + "422": { + "description": "Unprocessable Entity", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfPortalRecoveryDto" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfPortalRecoveryDto" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfPortalRecoveryDto" + } + } + } + }, + "429": { + "description": "Too Many Requests", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + } + } + } + }, + "security": [ ] + } + }, + "/api/v4/portal/tickets/{ticketCode}/resend": { + "post": { + "tags": [ + "Portal" + ], + "summary": "Reenviar la factura de un ticket", + "description": "Reenvía el XML y el PDF de la factura al correo del receptor guardado en la factura, con el token de descarga. Hasta 3 reenvíos por factura en 24 horas. Anónimo, con reCAPTCHA v3 (acción autoinvoice_resend). 202: el reenvío quedó encolado (sin data). 400: el cuerpo no es un JSON con la estructura esperada. 403: falló la verificación de reCAPTCHA (token inválido, de otra acción o con puntuación baja). 404: el 404 genérico del portal, el mismo cuerpo para todo lo que no existe o no está disponible (no revela tenants ni tickets). También un token de descarga que no es de la factura del ticket. 429: se alcanzó el límite de reenvíos (ResendLimitReached, data.retryAfterSeconds) o el de solicitudes por IP; el header Retry-After dice en cuántos segundos reintentar.", + "operationId": "resendPortalTicketInvoice", + "parameters": [ + { + "name": "ticketCode", + "in": "path", + "description": "El código del ticket, con o sin guiones, como lo lleva el QR.", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "X-TIME-ZONE", + "in": "header", + "description": "Zona horaria IANA de las fechas sin zona del cuerpo JSON: el request las lee y la respuesta las escribe en ella. Si falta o no es válida, America/Mexico_City. No aplica a los campos que la operación documenta en UTC o en hora civil.", + "schema": { + "type": "string" + }, + "example": "America/Mexico_City" + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PortalResendTicketBody" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/PortalResendTicketBody" + } + }, + "application/*+json": { + "schema": { + "$ref": "#/components/schemas/PortalResendTicketBody" + } + } + }, + "required": true + }, + "responses": { + "202": { + "description": "Accepted", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfPortalResendDto" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfPortalResendDto" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfPortalResendDto" + } + } + } + }, + "400": { + "description": "Bad Request", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfPortalResendDto" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfPortalResendDto" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfPortalResendDto" + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfPortalResendDto" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfPortalResendDto" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfPortalResendDto" + } + } + } + }, + "429": { + "description": "Too Many Requests", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfPortalResendDto" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfPortalResendDto" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfPortalResendDto" + } + } + } + } + }, + "security": [ ] + } + }, + "/api/v4/portal/downloads/{downloadToken}/{format}": { + "get": { + "tags": [ + "Portal" + ], + "summary": "Descargar la factura de un ticket", + "description": "El XML o el PDF de la autofactura como adjunto (.xml o .pdf), sin caché. Anónimo: el token de descarga es la credencial. 404: el 404 genérico del portal, el mismo cuerpo para todo lo que no existe o no está disponible (no revela tenants ni tickets). También un token inválido o vencido, o un formato desconocido. 429: demasiadas solicitudes desde la misma IP; el header Retry-After dice en cuántos segundos reintentar.", + "operationId": "downloadPortalTicketInvoice", + "parameters": [ + { + "name": "downloadToken", + "in": "path", + "description": "El token de descarga que entregan la autofactura y la recuperación.", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "format", + "in": "path", + "description": "El formato del archivo: xml o pdf.", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "X-TIME-ZONE", + "in": "header", + "description": "Zona horaria IANA de las fechas sin zona del cuerpo JSON: el request las lee y la respuesta las escribe en ella. Si falta o no es válida, America/Mexico_City. No aplica a los campos que la operación documenta en UTC o en hora civil.", + "schema": { + "type": "string" + }, + "example": "America/Mexico_City" + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/xml": { + "schema": { + "$ref": "#/components/schemas/FileContentResult" + } + }, + "application/pdf": { + "schema": { + "$ref": "#/components/schemas/FileContentResult" + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + } + } + }, + "429": { + "description": "Too Many Requests", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + } + } + } + }, + "security": [ ] + } + }, + "/api/v4/portal/catalogs/tax-regimes": { + "get": { + "tags": [ + "Portal" + ], + "summary": "Listar los regímenes fiscales del receptor", + "description": "El catálogo c_RegimenFiscal del SAT con su descripción y los tipos de persona a los que aplica, para el formulario del receptor. Anónimo; no es de un tenant.", + "operationId": "listPortalTaxRegimes", + "parameters": [ + { + "name": "X-TIME-ZONE", + "in": "header", + "description": "Zona horaria IANA de las fechas sin zona del cuerpo JSON: el request las lee y la respuesta las escribe en ella. Si falta o no es válida, America/Mexico_City. No aplica a los campos que la operación documenta en UTC o en hora civil.", + "schema": { + "type": "string" + }, + "example": "America/Mexico_City" + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfListOfPortalTaxRegimeDto" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfListOfPortalTaxRegimeDto" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfListOfPortalTaxRegimeDto" + } + } + } + } + }, + "security": [ ] + } + }, + "/api/v4/portal/catalogs/cfdi-uses": { + "get": { + "tags": [ + "Portal" + ], + "summary": "Listar los usos de CFDI del receptor", + "description": "El catálogo c_UsoCFDI del SAT con los tipos de persona y los regímenes fiscales del receptor con los que se puede usar cada uso, para el formulario del receptor. Anónimo; no es de un tenant.", + "operationId": "listPortalCfdiUses", + "parameters": [ + { + "name": "X-TIME-ZONE", + "in": "header", + "description": "Zona horaria IANA de las fechas sin zona del cuerpo JSON: el request las lee y la respuesta las escribe en ella. Si falta o no es válida, America/Mexico_City. No aplica a los campos que la operación documenta en UTC o en hora civil.", + "schema": { + "type": "string" + }, + "example": "America/Mexico_City" + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfListOfPortalCfdiUseDto" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfListOfPortalCfdiUseDto" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfListOfPortalCfdiUseDto" + } + } + } + } + }, + "security": [ ] + } + }, + "/api/v4/products": { + "get": { + "tags": [ + "Products" + ], + "summary": "Listar productos", + "description": "Los productos y servicios del tenant ordenados por description, paginados con pageNumber y pageSize (10 por omisión, máximo 50). Cada uno trae su clave de producto o servicio del SAT (satProductCodeId), su clave de unidad (satUnitMeasurementId), su objeto de impuesto (satTaxObjectId) y sus impuestos (productTaxes). Docs: [listar productos](https://docs.fiscalapi.com/products#listar-productos).", + "operationId": "listProducts", + "parameters": [ + { + "name": "PageNumber", + "in": "query", + "schema": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "integer", + "string" + ], + "format": "int32" + } + }, + { + "name": "PageSize", + "in": "query", + "schema": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "integer", + "string" + ], + "format": "int32" + } + }, + { + "name": "X-TIME-ZONE", + "in": "header", + "description": "Zona horaria IANA de las fechas sin zona del cuerpo JSON: el request las lee y la respuesta las escribe en ella. Si falta o no es válida, America/Mexico_City. No aplica a los campos que la operación documenta en UTC o en hora civil.", + "schema": { + "type": "string" + }, + "example": "America/Mexico_City" + } + ], + "responses": { + "200": { + "description": "OK" + } + }, + "security": [ + { + "ApiKey": [ ], + "TenantKey": [ ] + } + ] + }, + "post": { + "tags": [ + "Products" + ], + "summary": "Crear un producto", + "description": "Da de alta un producto o servicio: description (hasta 255 caracteres) y unitPrice (mayor que 0) son obligatorios. Si se omiten, la clave de unidad (satUnitMeasurementId) es H87, el objeto de impuesto (satTaxObjectId) 02, la clave de producto o servicio (satProductCodeId) 01010101 y productTaxes trae IVA trasladado al 16 %. Cada clave debe existir en su catálogo del SAT y cada impuesto (taxId, taxTypeId, taxFlagId, rate) va una sola vez; si no, responde 400. Docs: [crear producto](https://docs.fiscalapi.com/products#crear-producto).", + "operationId": "createProduct", + "parameters": [ + { + "name": "X-TIME-ZONE", + "in": "header", + "description": "Zona horaria IANA de las fechas sin zona del cuerpo JSON: el request las lee y la respuesta las escribe en ella. Si falta o no es válida, America/Mexico_City. No aplica a los campos que la operación documenta en UTC o en hora civil.", + "schema": { + "type": "string" + }, + "example": "America/Mexico_City" + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateCommand" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/CreateCommand" + } + }, + "application/*+json": { + "schema": { + "$ref": "#/components/schemas/CreateCommand" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK" + } + }, + "security": [ + { + "ApiKey": [ ], + "TenantKey": [ ] + } + ] + } + }, + "/api/v4/products/{id}": { + "get": { + "tags": [ + "Products" + ], + "summary": "Obtener un producto", + "description": "Un producto por su id, con description, unitPrice, su clave de producto o servicio del SAT (satProductCodeId), su clave de unidad (satUnitMeasurementId), su objeto de impuesto (satTaxObjectId) y sus impuestos (productTaxes). Responde 404 si no existe. Docs: [obtener producto](https://docs.fiscalapi.com/products#obtener-producto-por-id).", + "operationId": "getProduct", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "X-TIME-ZONE", + "in": "header", + "description": "Zona horaria IANA de las fechas sin zona del cuerpo JSON: el request las lee y la respuesta las escribe en ella. Si falta o no es válida, America/Mexico_City. No aplica a los campos que la operación documenta en UTC o en hora civil.", + "schema": { + "type": "string" + }, + "example": "America/Mexico_City" + } + ], + "responses": { + "200": { + "description": "OK" + } + }, + "security": [ + { + "ApiKey": [ ], + "TenantKey": [ ] + } + ] + }, + "put": { + "tags": [ + "Products" + ], + "summary": "Actualizar un producto", + "description": "Reemplaza el producto con el cuerpo completo: description, unitPrice, satUnitMeasurementId y satTaxObjectId son obligatorios y un satProductCodeId omitido se borra. Los productTaxes que envías reemplazan a los guardados; si omites la lista o la envías vacía, se conservan. El id de la ruta y el del cuerpo deben coincidir; responde 404 si el producto no existe. Docs: [actualizar producto](https://docs.fiscalapi.com/products#actualizar-producto).", + "operationId": "updateProduct", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "X-TIME-ZONE", + "in": "header", + "description": "Zona horaria IANA de las fechas sin zona del cuerpo JSON: el request las lee y la respuesta las escribe en ella. Si falta o no es válida, America/Mexico_City. No aplica a los campos que la operación documenta en UTC o en hora civil.", + "schema": { + "type": "string" + }, + "example": "America/Mexico_City" + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpdateCommand" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/UpdateCommand" + } + }, + "application/*+json": { + "schema": { + "$ref": "#/components/schemas/UpdateCommand" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK" + } + }, + "security": [ + { + "ApiKey": [ ], + "TenantKey": [ ] + } + ] + }, + "delete": { + "tags": [ + "Products" + ], + "summary": "Eliminar un producto", + "description": "Da de baja el producto: ya no aparece al listar y obtenerlo responde 404. Responde 404 si el producto no existe. Docs: [eliminar producto](https://docs.fiscalapi.com/products#eliminar-producto).", + "operationId": "deleteProduct", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "X-TIME-ZONE", + "in": "header", + "description": "Zona horaria IANA de las fechas sin zona del cuerpo JSON: el request las lee y la respuesta las escribe en ella. Si falta o no es válida, America/Mexico_City. No aplica a los campos que la operación documenta en UTC o en hora civil.", + "schema": { + "type": "string" + }, + "example": "America/Mexico_City" + } + ], + "responses": { + "200": { + "description": "OK" + } + }, + "security": [ + { + "ApiKey": [ ], + "TenantKey": [ ] + } + ] + } + }, + "/api/v4/products/{id}/taxes": { + "get": { + "tags": [ + "Products" + ], + "summary": "Listar los impuestos de un producto", + "description": "Los impuestos del producto, cada uno con el impuesto (tax), el tipo de factor (taxType), si es trasladado o retenido (taxFlag) y la tasa o cuota (rate). Un id de producto que no existe responde 200 con la lista vacía, no 404. Docs: [modelo producto](https://docs.fiscalapi.com/products#modelo-producto).", + "operationId": "listProductTaxes", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "X-TIME-ZONE", + "in": "header", + "description": "Zona horaria IANA de las fechas sin zona del cuerpo JSON: el request las lee y la respuesta las escribe en ella. Si falta o no es válida, America/Mexico_City. No aplica a los campos que la operación documenta en UTC o en hora civil.", + "schema": { + "type": "string" + }, + "example": "America/Mexico_City" + } + ], + "responses": { + "200": { + "description": "OK" + } + }, + "security": [ + { + "ApiKey": [ ], + "TenantKey": [ ] + } + ] + } + }, + "/api/v4/sat-validations": { + "get": { + "tags": [ + "SatValidations" + ], + "summary": "Listar tipos de validación SAT", + "description": "Los tipos de validación SAT activos (id y description) en el orden del catálogo, sin paginar. El id es el que se envía en validationTypes al validar un CFDI. No consume créditos de validación. Docs: [listar tipos de validación](https://docs.fiscalapi.com/sat-validations#listar-tipos-de-validacion).", + "operationId": "listSatValidationTypes", + "parameters": [ + { + "name": "X-TIME-ZONE", + "in": "header", + "description": "Zona horaria IANA de las fechas sin zona del cuerpo JSON: el request las lee y la respuesta las escribe en ella. Si falta o no es válida, America/Mexico_City. No aplica a los campos que la operación documenta en UTC o en hora civil.", + "schema": { + "type": "string" + }, + "example": "America/Mexico_City" + } + ], + "responses": { + "200": { + "description": "OK" + } + }, + "security": [ + { + "ApiKey": [ ], + "TenantKey": [ ] + } + ] + }, + "post": { + "tags": [ + "SatValidations" + ], + "summary": "Validar un CFDI o un RFC ante el SAT", + "description": "Ejecuta los tipos de validationTypes sobre un CFDI timbrado en base64 (xml, máximo 4,000,000 de caracteres) o solo sobre un RFC (tin), nunca ambos; con tin solo se admiten sat.blacklist.69b y sat.blacklist.69bbis. Los tipos son sat.xml.structure, sat.cfdi.sello y sat.tfd.sello (verificador de CFDI del SAT), sat.cfdi.status (estatus ante el SAT), sat.certificate.validity (vigencia del certificado del emisor al emitir) y las listas 69-B y 69-B Bis del último corte importado; en el ambiente de pruebas cada tipo responde un estatus aleatorio sin consultar al SAT. Cada tipo consume 1 crédito de validación de la persona autenticada, todo o nada: sin saldo suficiente, o si la suscripción del tenant no incluye Emisión CFDI, responde 403 y no ejecuta ninguno; un cuerpo inválido o un tipo desconocido responde 400. Devuelve un resultado por tipo (type, status, passed) en el orden del catálogo, y un resultado adverso es 200 con passed false. Docs: [validar un CFDI](https://docs.fiscalapi.com/sat-validations#validar) y [validar solo por RFC](https://docs.fiscalapi.com/sat-validations#validar-solo-por-rfc).", + "operationId": "runSatValidations", + "parameters": [ + { + "name": "X-TIME-ZONE", + "in": "header", + "description": "Zona horaria IANA de las fechas sin zona del cuerpo JSON: el request las lee y la respuesta las escribe en ella. Si falta o no es válida, America/Mexico_City. No aplica a los campos que la operación documenta en UTC o en hora civil.", + "schema": { + "type": "string" + }, + "example": "America/Mexico_City" + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RunSatValidationsCommand" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/RunSatValidationsCommand" + } + }, + "application/*+json": { + "schema": { + "$ref": "#/components/schemas/RunSatValidationsCommand" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK" + } + }, + "security": [ + { + "ApiKey": [ ], + "TenantKey": [ ] + } + ] + } + }, + "/api/v4/sat-validations/{id}": { + "get": { + "tags": [ + "SatValidations" + ], + "summary": "Obtener un tipo de validación SAT", + "description": "Un tipo de validación SAT activo (id y description) por su id, p. ej. sat.cfdi.status; responde 404 si no existe o está inactivo. Sus estatus posibles se listan con listSatValidationTypeStatuses. No consume créditos de validación. Docs: [obtener tipo de validación](https://docs.fiscalapi.com/sat-validations#obtener-tipo-de-validacion-por-id).", + "operationId": "getSatValidationType", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "X-TIME-ZONE", + "in": "header", + "description": "Zona horaria IANA de las fechas sin zona del cuerpo JSON: el request las lee y la respuesta las escribe en ella. Si falta o no es válida, America/Mexico_City. No aplica a los campos que la operación documenta en UTC o en hora civil.", + "schema": { + "type": "string" + }, + "example": "America/Mexico_City" + } + ], + "responses": { + "200": { + "description": "OK" + } + }, + "security": [ + { + "ApiKey": [ ], + "TenantKey": [ ] + } + ] + } + }, + "/api/v4/sat-validations/{id}/statuses": { + "get": { + "tags": [ + "SatValidations" + ], + "summary": "Listar los estatus de un tipo de validación SAT", + "description": "Los estatus (id y description) que un tipo de validación SAT puede tomar al ejecutarse: primero los aprobatorios y, dentro de cada grupo, por id. No incluye passed, que llega en el resultado de cada validación; no ejecuta la validación ni consume créditos. Responde 404 si el tipo no existe o está inactivo. Docs: [listar estatus de un tipo](https://docs.fiscalapi.com/sat-validations#listar-estatus-de-un-tipo).", + "operationId": "listSatValidationTypeStatuses", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "X-TIME-ZONE", + "in": "header", + "description": "Zona horaria IANA de las fechas sin zona del cuerpo JSON: el request las lee y la respuesta las escribe en ella. Si falta o no es válida, America/Mexico_City. No aplica a los campos que la operación documenta en UTC o en hora civil.", + "schema": { + "type": "string" + }, + "example": "America/Mexico_City" + } + ], + "responses": { + "200": { + "description": "OK" + } + }, + "security": [ + { + "ApiKey": [ ], + "TenantKey": [ ] + } + ] + } + }, + "/api/v4/stamps": { + "get": { + "tags": [ + "Stamps" + ], + "summary": "Listar movimientos de timbres", + "description": "Los movimientos de créditos de todas las personas del tenant (compras, transferencias, consumos, autofacturas y reversos), del más antiguo al más reciente, paginados con pageNumber y pageSize (10 por omisión, máximo 50). Cada movimiento pasa amount créditos de fromPerson a toPerson en el saldo de su creditType (1 timbres, 2 validaciones, 3 tickets). Docs: [listar movimientos](https://docs.fiscalapi.com/stamps#listar-movimientos).", + "operationId": "listStampTransactions", + "parameters": [ + { + "name": "PageNumber", + "in": "query", + "schema": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "integer", + "string" + ], + "format": "int32" + } + }, + { + "name": "PageSize", + "in": "query", + "schema": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "integer", + "string" + ], + "format": "int32" + } + }, + { + "name": "X-TIME-ZONE", + "in": "header", + "description": "Zona horaria IANA de las fechas sin zona del cuerpo JSON: el request las lee y la respuesta las escribe en ella. Si falta o no es válida, America/Mexico_City. No aplica a los campos que la operación documenta en UTC o en hora civil.", + "schema": { + "type": "string" + }, + "example": "America/Mexico_City" + } + ], + "responses": { + "200": { + "description": "OK" + } + }, + "security": [ + { + "ApiKey": [ ], + "TenantKey": [ ] + } + ] + }, + "post": { + "tags": [ + "Stamps" + ], + "summary": "Transferir o retirar timbres entre personas", + "description": "Resta amount (mayor que 0) del saldo de fromPersonId y lo suma a toPersonId en un solo movimiento de transferencia; retirar timbres es la misma operación en sentido inverso. creditType elige el saldo (1 timbres por omisión, 2 validaciones, 3 tickets) y, si el del origen no alcanza, responde 403 sin mover nada. Origen y destino deben ser personas distintas del tenant con id GUID (si no, 400) y solo el owner del tenant transfiere desde una persona que no es la autenticada (403). Docs: [transferir](https://docs.fiscalapi.com/stamps#transferir-timbres) y [retirar](https://docs.fiscalapi.com/stamps#retirar-timbres).", + "operationId": "transferStamps", + "parameters": [ + { + "name": "X-TIME-ZONE", + "in": "header", + "description": "Zona horaria IANA de las fechas sin zona del cuerpo JSON: el request las lee y la respuesta las escribe en ella. Si falta o no es válida, America/Mexico_City. No aplica a los campos que la operación documenta en UTC o en hora civil.", + "schema": { + "type": "string" + }, + "example": "America/Mexico_City" + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TransferStampsCommand" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/TransferStampsCommand" + } + }, + "application/*+json": { + "schema": { + "$ref": "#/components/schemas/TransferStampsCommand" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK" + } + }, + "security": [ + { + "ApiKey": [ ], + "TenantKey": [ ] + } + ] + } + }, + "/api/v4/stamps/{id}": { + "get": { + "tags": [ + "Stamps" + ], + "summary": "Obtener un movimiento de timbres", + "description": "Un movimiento por su id: consecutive, origen (fromPerson) y destino (toPerson) con su id, RFC (tin) y legalName, amount, transactionType (1 compra, 2 transferencia, 3 consumo, 4 autofactura, 5 reverso), transactionStatus, referenceId y creditType. Responde 404 si no existe en el tenant. Docs: [obtener movimiento](https://docs.fiscalapi.com/stamps#obtener-movimiento-por-id).", + "operationId": "getStampTransaction", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "details", + "in": "query", + "schema": { + "type": "boolean", + "default": false + } + }, + { + "name": "X-TIME-ZONE", + "in": "header", + "description": "Zona horaria IANA de las fechas sin zona del cuerpo JSON: el request las lee y la respuesta las escribe en ella. Si falta o no es válida, America/Mexico_City. No aplica a los campos que la operación documenta en UTC o en hora civil.", + "schema": { + "type": "string" + }, + "example": "America/Mexico_City" + } + ], + "responses": { + "200": { + "description": "OK" + } + }, + "security": [ + { + "ApiKey": [ ], + "TenantKey": [ ] + } + ] + } + }, + "/api/v4/tax-files": { + "get": { + "tags": [ + "TaxFiles" + ], + "summary": "Listar certificados CSD y FIEL", + "description": "Los certificados (.cer) y las llaves privadas (.key) de CSD y FIEL que la persona autenticada puede ver, paginados con pageNumber y pageSize (10 por omisión, máximo 50). La contraseña nunca se devuelve. Docs: [listar certificados](https://docs.fiscalapi.com/tax-files#listar-certificados).", + "operationId": "listTaxFiles", + "parameters": [ + { + "name": "PageNumber", + "in": "query", + "schema": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "integer", + "string" + ], + "format": "int32" + } + }, + { + "name": "PageSize", + "in": "query", + "schema": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "integer", + "string" + ], + "format": "int32" + } + }, + { + "name": "X-TIME-ZONE", + "in": "header", + "description": "Zona horaria IANA de las fechas sin zona del cuerpo JSON: el request las lee y la respuesta las escribe en ella. Si falta o no es válida, America/Mexico_City. No aplica a los campos que la operación documenta en UTC o en hora civil.", + "schema": { + "type": "string" + }, + "example": "America/Mexico_City" + } + ], + "responses": { + "200": { + "description": "OK" + } + }, + "security": [ + { + "ApiKey": [ ], + "TenantKey": [ ] + } + ] + }, + "post": { + "tags": [ + "TaxFiles" + ], + "summary": "Subir un certificado CSD o FIEL", + "description": "Sube el certificado (.cer) o la llave privada (.key) del CSD o de la FIEL de una persona: personId, base64File, fileType y password, la contraseña de la llave. El certificado debe ser del RFC de la persona. Un par completo requiere dos peticiones con el mismo personId, una con el .cer y otra con la .key. Docs: [subir certificados](https://docs.fiscalapi.com/tax-files#subir-certificados).", + "operationId": "createTaxFile", + "parameters": [ + { + "name": "X-TIME-ZONE", + "in": "header", + "description": "Zona horaria IANA de las fechas sin zona del cuerpo JSON: el request las lee y la respuesta las escribe en ella. Si falta o no es válida, America/Mexico_City. No aplica a los campos que la operación documenta en UTC o en hora civil.", + "schema": { + "type": "string" + }, + "example": "America/Mexico_City" + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "oneOf": [ + { + "type": "null" + }, + { + "$ref": "#/components/schemas/CreateCommand" + } + ] + } + }, + "text/json": { + "schema": { + "oneOf": [ + { + "type": "null" + }, + { + "$ref": "#/components/schemas/CreateCommand" + } + ] + } + }, + "application/*+json": { + "schema": { + "oneOf": [ + { + "type": "null" + }, + { + "$ref": "#/components/schemas/CreateCommand" + } + ] + } + } + } + }, + "responses": { + "200": { + "description": "OK" + } + }, + "security": [ + { + "ApiKey": [ ], + "TenantKey": [ ] + } + ] + } + }, + "/api/v4/tax-files/{id}": { + "get": { + "tags": [ + "TaxFiles" + ], + "summary": "Obtener un certificado", + "description": "Un certificado o una llave privada por su id; el de otra persona responde 404, salvo para el owner. Docs: [obtener certificado](https://docs.fiscalapi.com/tax-files#obtener-certificado-por-id).", + "operationId": "getTaxFile", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "X-TIME-ZONE", + "in": "header", + "description": "Zona horaria IANA de las fechas sin zona del cuerpo JSON: el request las lee y la respuesta las escribe en ella. Si falta o no es válida, America/Mexico_City. No aplica a los campos que la operación documenta en UTC o en hora civil.", + "schema": { + "type": "string" + }, + "example": "America/Mexico_City" + } + ], + "responses": { + "200": { + "description": "OK" + } + }, + "security": [ + { + "ApiKey": [ ], + "TenantKey": [ ] + } + ] + }, + "delete": { + "tags": [ + "TaxFiles" + ], + "summary": "Eliminar un certificado", + "description": "Da de baja un certificado o una llave privada; responde 400 (data false) si no existe o la persona autenticada no lo puede ver. Un certificado no se edita: sube uno nuevo y elimina el anterior. Docs: [eliminar certificado](https://docs.fiscalapi.com/tax-files#eliminar-certificado).", + "operationId": "deleteTaxFile", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "X-TIME-ZONE", + "in": "header", + "description": "Zona horaria IANA de las fechas sin zona del cuerpo JSON: el request las lee y la respuesta las escribe en ella. Si falta o no es válida, America/Mexico_City. No aplica a los campos que la operación documenta en UTC o en hora civil.", + "schema": { + "type": "string" + }, + "example": "America/Mexico_City" + } + ], + "responses": { + "200": { + "description": "OK" + } + }, + "security": [ + { + "ApiKey": [ ], + "TenantKey": [ ] + } + ] + } + }, + "/api/v4/tax-files/{personId}/default-values": { + "get": { + "tags": [ + "TaxFiles" + ], + "summary": "Obtener el CSD vigente de una persona", + "description": "El último par .cer y .key vigente del CSD de la persona, con cada archivo en base64 y la contraseña descifrada. Solo pueden pedirlo la persona misma o el owner. Sin CSD vigente responde una lista vacía; 400 si la persona no tiene RFC y 404 si el par está incompleto. Docs: [certificados por defecto](https://docs.fiscalapi.com/tax-files#obtener-certificados-por-defecto).", + "operationId": "getTaxFileDefaultValues", + "parameters": [ + { + "name": "personId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "X-TIME-ZONE", + "in": "header", + "description": "Zona horaria IANA de las fechas sin zona del cuerpo JSON: el request las lee y la respuesta las escribe en ella. Si falta o no es válida, America/Mexico_City. No aplica a los campos que la operación documenta en UTC o en hora civil.", + "schema": { + "type": "string" + }, + "example": "America/Mexico_City" + } + ], + "responses": { + "200": { + "description": "OK" + } + }, + "security": [ + { + "ApiKey": [ ], + "TenantKey": [ ] + } + ] + } + }, + "/api/v4/tickets": { + "get": { + "tags": [ + "Tickets" + ], + "summary": "Listar tickets", + "description": "Los tickets del tenant paginados y ordenados por fecha de pago. Cada filtro aplica solo si viene: estados, persona emisora, factura global y periodo de pago en fechas civiles de México. Con details=true cada ticket trae sus conceptos con sus impuestos. No exige el feature Auto CFDI. 400: un estado desconocido, una fecha inválida, un periodo invertido o una página fuera de rango. 401: sin API key o JWT válidos. 403: la persona no tiene el permiso Tickets.Read.", + "operationId": "listTickets", + "parameters": [ + { + "name": "Status", + "in": "query", + "description": "Uno o más estados (TicketStatus en camelCase), repitiendo el parámetro: status=pending&status=invoiced.", + "schema": { + "type": "array", + "items": { + "type": "string" + } + } + }, + { + "name": "PersonId", + "in": "query", + "description": "Id de la persona emisora; una persona de otro tenant deja la lista vacía.", + "schema": { + "type": "string" + } + }, + { + "name": "GlobalInvoiceId", + "in": "query", + "description": "Id de la factura global que ampara los tickets; una de otro tenant deja la lista vacía.", + "schema": { + "type": "string" + } + }, + { + "name": "From", + "in": "query", + "description": "Primer día del pago (incluido), fecha civil de México YYYY-MM-DD.", + "schema": { + "type": "string" + } + }, + { + "name": "To", + "in": "query", + "description": "Último día del pago (incluido), fecha civil de México YYYY-MM-DD.", + "schema": { + "type": "string" + } + }, + { + "name": "Details", + "in": "query", + "description": "Con true, cada ticket trae sus conceptos con sus impuestos.", + "schema": { + "type": "boolean" + } + }, + { + "name": "PageNumber", + "in": "query", + "description": "Número de página, desde 1 (por omisión 1).", + "schema": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "integer", + "string" + ], + "format": "int32" + } + }, + { + "name": "PageSize", + "in": "query", + "description": "Tickets por página, de 1 a 50 (por omisión 10).", + "schema": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "integer", + "string" + ], + "format": "int32" + } + }, + { + "name": "X-TIME-ZONE", + "in": "header", + "description": "Zona horaria IANA de las fechas sin zona del cuerpo JSON: el request las lee y la respuesta las escribe en ella. Si falta o no es válida, America/Mexico_City. No aplica a los campos que la operación documenta en UTC o en hora civil.", + "schema": { + "type": "string" + }, + "example": "America/Mexico_City" + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfPagedListOfTicketDto" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfPagedListOfTicketDto" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfPagedListOfTicketDto" + } + } + } + }, + "400": { + "description": "Bad Request", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + } + } + } + }, + "security": [ + { + "ApiKey": [ ], + "TenantKey": [ ] + } + ] + }, + "post": { + "tags": [ + "Tickets" + ], + "summary": "Crear un ticket", + "description": "Registra una venta pagada del punto de venta y cobra 1 crédito de ticket. Responde el código del ticket y la URL del portal para su QR. Exige el feature Auto CFDI. 201: el ticket nuevo. 200: el ticket original, porque la Idempotency-Key ya se usó con el mismo cuerpo (no cobra otra vez). 400: el cuerpo no tiene la estructura esperada o le faltan datos. 401: sin API key o JWT válidos. 403: la persona no tiene el permiso Tickets.Create, el tenant no tiene Auto CFDI o no hay saldo de créditos de ticket. 409: el emisor ya tiene un ticket activo con esa serie y folio (data.existingTicketId), u otro ticket con la misma serie y folio o la misma Idempotency-Key cambió al mismo tiempo. 422: una regla fiscal o aritmética no se cumple, el emisor no puede facturar, o la Idempotency-Key ya se usó con otro cuerpo.", + "operationId": "createTicket", + "parameters": [ + { + "name": "Idempotency-Key", + "in": "header", + "description": "Llave de idempotencia opcional: repetirla con el mismo cuerpo devuelve el ticket original.", + "schema": { + "type": "string" + } + }, + { + "name": "X-TIME-ZONE", + "in": "header", + "description": "Zona horaria IANA de las fechas sin zona del cuerpo JSON: el request las lee y la respuesta las escribe en ella. Si falta o no es válida, America/Mexico_City. No aplica a los campos que la operación documenta en UTC o en hora civil.", + "schema": { + "type": "string" + }, + "example": "America/Mexico_City" + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateTicketCommand" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/CreateTicketCommand" + } + }, + "application/*+json": { + "schema": { + "$ref": "#/components/schemas/CreateTicketCommand" + } + } + }, + "required": true + }, + "responses": { + "201": { + "description": "Created", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfTicketIngestedDto" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfTicketIngestedDto" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfTicketIngestedDto" + } + } + } + }, + "200": { + "description": "OK", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfTicketIngestedDto" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfTicketIngestedDto" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfTicketIngestedDto" + } + } + } + }, + "400": { + "description": "Bad Request", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + } + } + }, + "409": { + "description": "Conflict", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfExistingTicketData" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfExistingTicketData" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfExistingTicketData" + } + } + } + }, + "422": { + "description": "Unprocessable Entity", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + } + } + } + }, + "security": [ + { + "ApiKey": [ ], + "TenantKey": [ ] + } + ] + } + }, + "/api/v4/tickets/{id}": { + "get": { + "tags": [ + "Tickets" + ], + "summary": "Obtener un ticket", + "description": "Un ticket del tenant con su estado, sus facturas y su vigencia de autofactura. Con details=true trae además sus conceptos con sus impuestos y su historia (eventos). 401: sin API key o JWT válidos. 403: la persona no tiene el permiso Tickets.Read. 404: no existe un ticket con ese id en el tenant.", + "operationId": "getTicket", + "parameters": [ + { + "name": "id", + "in": "path", + "description": "Id del ticket.", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "details", + "in": "query", + "description": "Con true, trae los conceptos y la historia del ticket.", + "schema": { + "type": "boolean", + "default": false + } + }, + { + "name": "X-TIME-ZONE", + "in": "header", + "description": "Zona horaria IANA de las fechas sin zona del cuerpo JSON: el request las lee y la respuesta las escribe en ella. Si falta o no es válida, America/Mexico_City. No aplica a los campos que la operación documenta en UTC o en hora civil.", + "schema": { + "type": "string" + }, + "example": "America/Mexico_City" + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfTicketDto" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfTicketDto" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfTicketDto" + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + } + } + } + }, + "security": [ + { + "ApiKey": [ ], + "TenantKey": [ ] + } + ] + }, + "delete": { + "tags": [ + "Tickets" + ], + "summary": "Cancelar un ticket", + "description": "Cancela un ticket en estado pending y libera su serie y folio para volver a ingerirlo. El crédito de ticket de la ingesta no se devuelve. Exige el feature Auto CFDI. 401: sin API key o JWT válidos. 403: la persona no tiene el permiso Tickets.Cancel o el tenant no tiene Auto CFDI. 404: no existe un ticket con ese id en el tenant. 409: el ticket no está en pending o cambió de estado al mismo tiempo.", + "operationId": "cancelTicket", + "parameters": [ + { + "name": "id", + "in": "path", + "description": "Id del ticket.", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "X-TIME-ZONE", + "in": "header", + "description": "Zona horaria IANA de las fechas sin zona del cuerpo JSON: el request las lee y la respuesta las escribe en ella. Si falta o no es válida, America/Mexico_City. No aplica a los campos que la operación documenta en UTC o en hora civil.", + "schema": { + "type": "string" + }, + "example": "America/Mexico_City" + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfboolean" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfboolean" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfboolean" + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + } + } + }, + "409": { + "description": "Conflict", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + } + } + } + }, + "security": [ + { + "ApiKey": [ ], + "TenantKey": [ ] + } + ] + } + }, + "/api/v4/tickets/{id}/code": { + "get": { + "tags": [ + "Tickets" + ], + "summary": "Obtener el código de un ticket", + "description": "El código del ticket y la URL del portal que abre su QR, los mismos de la ingesta, para reimprimirlo. 401: sin API key o JWT válidos. 403: la persona no tiene el permiso Tickets.Read. 404: no existe un ticket con ese id en el tenant.", + "operationId": "getTicketCode", + "parameters": [ + { + "name": "id", + "in": "path", + "description": "Id del ticket.", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "X-TIME-ZONE", + "in": "header", + "description": "Zona horaria IANA de las fechas sin zona del cuerpo JSON: el request las lee y la respuesta las escribe en ella. Si falta o no es válida, America/Mexico_City. No aplica a los campos que la operación documenta en UTC o en hora civil.", + "schema": { + "type": "string" + }, + "example": "America/Mexico_City" + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfTicketCodeDto" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfTicketCodeDto" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfTicketCodeDto" + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + } + } + } + }, + "security": [ + { + "ApiKey": [ ], + "TenantKey": [ ] + } + ] + } + }, + "/api/v4/webhooks": { + "get": { + "tags": [ + "Webhooks" + ], + "summary": "Listar webhooks", + "description": "Los webhooks del tenant en orden de alta, sin su secreto. Las escuchas de CLI (kind cliListener) solo salen para su persona y para el owner. 400: una página fuera de rango. 401: sin API key o JWT válidos. 403: la persona no tiene el permiso Webhooks.Manage.", + "operationId": "listWebhooks", + "parameters": [ + { + "name": "PageNumber", + "in": "query", + "description": "Número de página, desde 1 (por omisión 1).", + "schema": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "integer", + "string" + ], + "format": "int32" + } + }, + { + "name": "PageSize", + "in": "query", + "description": "Webhooks por página, de 1 a 50 (por omisión 10).", + "schema": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "integer", + "string" + ], + "format": "int32" + } + }, + { + "name": "X-TIME-ZONE", + "in": "header", + "description": "Zona horaria IANA de las fechas sin zona del cuerpo JSON: el request las lee y la respuesta las escribe en ella. Si falta o no es válida, America/Mexico_City. No aplica a los campos que la operación documenta en UTC o en hora civil.", + "schema": { + "type": "string" + }, + "example": "America/Mexico_City" + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfPagedListOfWebhookDto" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfPagedListOfWebhookDto" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfPagedListOfWebhookDto" + } + } + } + }, + "400": { + "description": "Bad Request", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + } + } + } + }, + "security": [ + { + "ApiKey": [ ], + "TenantKey": [ ] + } + ] + }, + "post": { + "tags": [ + "Webhooks" + ], + "summary": "Crear un webhook", + "description": "Da de alta una URL que recibe los eventos suscritos del tenant (o de las personas indicadas). 201: el webhook con su secret whsec_…, la única respuesta que lo trae sin pedirlo; durante una suplantación no lo trae. 400: la URL no es https absoluta sin usuario ni contraseña, apunta a una dirección no permitida, un tipo de evento no existe, '*' va con otros tipos, una persona no es del tenant, son más de 100 personas o el tenant ya tiene 16 webhooks de URL. 401: sin API key o JWT válidos. 403: la persona no tiene el permiso Webhooks.Manage. 409: otro cambio a los webhooks del tenant está en curso; reintenta.", + "operationId": "createWebhook", + "parameters": [ + { + "name": "X-TIME-ZONE", + "in": "header", + "description": "Zona horaria IANA de las fechas sin zona del cuerpo JSON: el request las lee y la respuesta las escribe en ella. Si falta o no es válida, America/Mexico_City. No aplica a los campos que la operación documenta en UTC o en hora civil.", + "schema": { + "type": "string" + }, + "example": "America/Mexico_City" + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateWebhookCommand" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/CreateWebhookCommand" + } + }, + "application/*+json": { + "schema": { + "$ref": "#/components/schemas/CreateWebhookCommand" + } + } + }, + "required": true + }, + "responses": { + "201": { + "description": "Created", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfWebhookWithSecretDto" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfWebhookWithSecretDto" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfWebhookWithSecretDto" + } + } + } + }, + "400": { + "description": "Bad Request", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + } + } + }, + "409": { + "description": "Conflict", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + } + } + } + }, + "security": [ + { + "ApiKey": [ ], + "TenantKey": [ ] + } + ] + } + }, + "/api/v4/webhooks/{id}": { + "get": { + "tags": [ + "Webhooks" + ], + "summary": "Obtener un webhook", + "description": "Un webhook del tenant con su estado, sin su secreto. 401: sin API key o JWT válidos. 403: la persona no tiene el permiso Webhooks.Manage. 404: no existe un webhook con ese id en el tenant (una escucha de CLI solo existe para su persona y el owner), o el id no es un GUID.", + "operationId": "getWebhook", + "parameters": [ + { + "name": "id", + "in": "path", + "description": "Id del webhook.", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + }, + { + "name": "X-TIME-ZONE", + "in": "header", + "description": "Zona horaria IANA de las fechas sin zona del cuerpo JSON: el request las lee y la respuesta las escribe en ella. Si falta o no es válida, America/Mexico_City. No aplica a los campos que la operación documenta en UTC o en hora civil.", + "schema": { + "type": "string" + }, + "example": "America/Mexico_City" + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfWebhookDto" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfWebhookDto" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfWebhookDto" + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + } + } + } + }, + "security": [ + { + "ApiKey": [ ], + "TenantKey": [ ] + } + ] + }, + "put": { + "tags": [ + "Webhooks" + ], + "summary": "Reemplazar un webhook", + "description": "Reemplaza la URL, la descripción, los tipos de evento y las personas; status omitido conserva el estado. Habilitar uno deshabilitado limpia failingSince, disabledReason y disabledAt; deshabilitar cierra sus entregas pendientes y conserva el primer motivo. 400: los mismos del alta (salvo el tope), el id del cuerpo no es el de la ruta o es una escucha de CLI. 401: sin API key o JWT válidos. 403: la persona no tiene el permiso Webhooks.Manage. 404: no existe un webhook con ese id en el tenant (una escucha de CLI solo existe para su persona y el owner), o el id no es un GUID. 409: otro cambio a los webhooks del tenant está en curso; reintenta.", + "operationId": "updateWebhook", + "parameters": [ + { + "name": "id", + "in": "path", + "description": "Id del webhook.", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + }, + { + "name": "X-TIME-ZONE", + "in": "header", + "description": "Zona horaria IANA de las fechas sin zona del cuerpo JSON: el request las lee y la respuesta las escribe en ella. Si falta o no es válida, America/Mexico_City. No aplica a los campos que la operación documenta en UTC o en hora civil.", + "schema": { + "type": "string" + }, + "example": "America/Mexico_City" + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpdateWebhookCommand" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/UpdateWebhookCommand" + } + }, + "application/*+json": { + "schema": { + "$ref": "#/components/schemas/UpdateWebhookCommand" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfWebhookDto" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfWebhookDto" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfWebhookDto" + } + } + } + }, + "400": { + "description": "Bad Request", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + } + } + }, + "409": { + "description": "Conflict", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + } + } + } + }, + "security": [ + { + "ApiKey": [ ], + "TenantKey": [ ] + } + ] + }, + "delete": { + "tags": [ + "Webhooks" + ], + "summary": "Borrar un webhook", + "description": "Da de baja el webhook (de URL o una escucha de CLI) y cierra sus entregas pendientes; su historial de entregas se conserva. 401: sin API key o JWT válidos. 403: la persona no tiene el permiso Webhooks.Manage. 404: no existe un webhook con ese id en el tenant (una escucha de CLI solo existe para su persona y el owner), o el id no es un GUID. 409: otro cambio a los webhooks del tenant está en curso; reintenta.", + "operationId": "deleteWebhook", + "parameters": [ + { + "name": "id", + "in": "path", + "description": "Id del webhook.", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + }, + { + "name": "X-TIME-ZONE", + "in": "header", + "description": "Zona horaria IANA de las fechas sin zona del cuerpo JSON: el request las lee y la respuesta las escribe en ella. Si falta o no es válida, America/Mexico_City. No aplica a los campos que la operación documenta en UTC o en hora civil.", + "schema": { + "type": "string" + }, + "example": "America/Mexico_City" + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfboolean" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfboolean" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfboolean" + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + } + } + }, + "409": { + "description": "Conflict", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + } + } + } + }, + "security": [ + { + "ApiKey": [ ], + "TenantKey": [ ] + } + ] + } + }, + "/api/v4/webhooks/{id}/deliveries": { + "get": { + "tags": [ + "Webhooks" + ], + "summary": "Listar las entregas de un webhook", + "description": "Las entregas del webhook, también si ya se dio de baja: el evento y su tipo, el origen (automatic, manual o test), el estado, los intentos y lo técnico del último (código HTTP, latencia, error y el primer KB de la respuesta). 400: una página fuera de rango. 401: sin API key o JWT válidos. 403: la persona no tiene el permiso Webhooks.Manage. 404: no existe un webhook con ese id en el tenant, ni dado de baja (una escucha de CLI solo existe para su persona y el owner), o el id no es un GUID.", + "operationId": "listWebhookDeliveries", + "parameters": [ + { + "name": "id", + "in": "path", + "description": "Id del webhook.", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + }, + { + "name": "WebhookId", + "in": "query", + "schema": { + "type": "string" + } + }, + { + "name": "PageNumber", + "in": "query", + "description": "Número de página, desde 1 (por omisión 1).", + "schema": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "integer", + "string" + ], + "format": "int32" + } + }, + { + "name": "PageSize", + "in": "query", + "description": "Entregas por página, de 1 a 50 (por omisión 10).", + "schema": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "integer", + "string" + ], + "format": "int32" + } + }, + { + "name": "X-TIME-ZONE", + "in": "header", + "description": "Zona horaria IANA de las fechas sin zona del cuerpo JSON: el request las lee y la respuesta las escribe en ella. Si falta o no es válida, America/Mexico_City. No aplica a los campos que la operación documenta en UTC o en hora civil.", + "schema": { + "type": "string" + }, + "example": "America/Mexico_City" + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfPagedListOfWebhookDeliveryDto" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfPagedListOfWebhookDeliveryDto" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfPagedListOfWebhookDeliveryDto" + } + } + } + }, + "400": { + "description": "Bad Request", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + } + } + } + }, + "security": [ + { + "ApiKey": [ ], + "TenantKey": [ ] + } + ] + } + }, + "/api/v4/webhooks/{id}/rotate-secret": { + "post": { + "tags": [ + "Webhooks" + ], + "summary": "Rotar el secreto de un webhook", + "description": "Genera un secreto de firma nuevo y lo responde en secret; el anterior sigue firmando cada entrega 24 horas para que el receptor cambie el suyo sin perder eventos. Durante una suplantación rota, pero no responde el secreto. 401: sin API key o JWT válidos. 403: la persona no tiene el permiso Webhooks.Manage. 404: no existe un webhook con ese id en el tenant (una escucha de CLI solo existe para su persona y el owner), o el id no es un GUID. 409: otro cambio a los webhooks del tenant está en curso; reintenta.", + "operationId": "rotateWebhookSecret", + "parameters": [ + { + "name": "id", + "in": "path", + "description": "Id del webhook.", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + }, + { + "name": "X-TIME-ZONE", + "in": "header", + "description": "Zona horaria IANA de las fechas sin zona del cuerpo JSON: el request las lee y la respuesta las escribe en ella. Si falta o no es válida, America/Mexico_City. No aplica a los campos que la operación documenta en UTC o en hora civil.", + "schema": { + "type": "string" + }, + "example": "America/Mexico_City" + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfWebhookWithSecretDto" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfWebhookWithSecretDto" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfWebhookWithSecretDto" + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + } + } + }, + "409": { + "description": "Conflict", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + } + } + } + }, + "security": [ + { + "ApiKey": [ ], + "TenantKey": [ ] + } + ] + } + }, + "/api/v4/webhooks/{id}/test": { + "post": { + "tags": [ + "Webhooks" + ], + "summary": "Probar un webhook", + "description": "Registra un evento webhook.test cuyo recurso es el webhook y lo entrega solo a él, firmado y en un solo intento, sin reintentos. 202: los ids del evento y de la entrega. 400: es una escucha de CLI o está deshabilitado. 401: sin API key o JWT válidos. 403: la persona no tiene el permiso Webhooks.Manage. 404: no existe un webhook con ese id en el tenant (una escucha de CLI solo existe para su persona y el owner), o el id no es un GUID. 429: demasiadas pruebas con la misma API key.", + "operationId": "testWebhook", + "parameters": [ + { + "name": "id", + "in": "path", + "description": "Id del webhook.", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + }, + { + "name": "X-TIME-ZONE", + "in": "header", + "description": "Zona horaria IANA de las fechas sin zona del cuerpo JSON: el request las lee y la respuesta las escribe en ella. Si falta o no es válida, America/Mexico_City. No aplica a los campos que la operación documenta en UTC o en hora civil.", + "schema": { + "type": "string" + }, + "example": "America/Mexico_City" + } + ], + "responses": { + "202": { + "description": "Accepted", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfWebhookTestDto" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfWebhookTestDto" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfWebhookTestDto" + } + } + } + }, + "400": { + "description": "Bad Request", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + } + } + }, + "429": { + "description": "Too Many Requests", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseOfObject" + } + } + } + } + }, + "security": [ + { + "ApiKey": [ ], + "TenantKey": [ ] + } + ] + } + } + }, + "components": { + "schemas": { + "ApiKeyStatus": { + "type": "integer" + }, + "ApiResponseOfAutoInvoiceSettingDto": { + "type": "object", + "properties": { + "data": { + "oneOf": [ + { + "type": "null" + }, + { + "$ref": "#/components/schemas/AutoInvoiceSettingDto" + } + ] + }, + "succeeded": { + "type": "boolean" + }, + "message": { + "type": [ + "null", + "string" + ] + }, + "details": { + "type": [ + "null", + "string" + ] + }, + "httpStatusCode": { + "$ref": "#/components/schemas/HttpStatusCode" + }, + "traceIdentifier": { + "type": [ + "null", + "string" + ] + } + } + }, + "ApiResponseOfboolean": { + "type": "object", + "properties": { + "data": { + "type": "boolean" + }, + "succeeded": { + "type": "boolean" + }, + "message": { + "type": [ + "null", + "string" + ] + }, + "details": { + "type": [ + "null", + "string" + ] + }, + "httpStatusCode": { + "$ref": "#/components/schemas/HttpStatusCode" + }, + "traceIdentifier": { + "type": [ + "null", + "string" + ] + } + } + }, + "ApiResponseOfConsumptionReportDto": { + "type": "object", + "properties": { + "data": { + "oneOf": [ + { + "type": "null" + }, + { + "$ref": "#/components/schemas/ConsumptionReportDto" + } + ] + }, + "succeeded": { + "type": "boolean" + }, + "message": { + "type": [ + "null", + "string" + ] + }, + "details": { + "type": [ + "null", + "string" + ] + }, + "httpStatusCode": { + "$ref": "#/components/schemas/HttpStatusCode" + }, + "traceIdentifier": { + "type": [ + "null", + "string" + ] + } + } + }, + "ApiResponseOfExistingTicketData": { + "type": "object", + "properties": { + "data": { + "oneOf": [ + { + "type": "null" + }, + { + "$ref": "#/components/schemas/ExistingTicketData" + } + ] + }, + "succeeded": { + "type": "boolean" + }, + "message": { + "type": [ + "null", + "string" + ] + }, + "details": { + "type": [ + "null", + "string" + ] + }, + "httpStatusCode": { + "$ref": "#/components/schemas/HttpStatusCode" + }, + "traceIdentifier": { + "type": [ + "null", + "string" + ] + } + } + }, + "ApiResponseOfJsonElement": { + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/JsonElement" + }, + "succeeded": { + "type": "boolean" + }, + "message": { + "type": [ + "null", + "string" + ] + }, + "details": { + "type": [ + "null", + "string" + ] + }, + "httpStatusCode": { + "$ref": "#/components/schemas/HttpStatusCode" + }, + "traceIdentifier": { + "type": [ + "null", + "string" + ] + } + } + }, + "ApiResponseOfListOfPortalCfdiUseDto": { + "type": "object", + "properties": { + "data": { + "type": [ + "null", + "array" + ], + "items": { + "$ref": "#/components/schemas/PortalCfdiUseDto" + } + }, + "succeeded": { + "type": "boolean" + }, + "message": { + "type": [ + "null", + "string" + ] + }, + "details": { + "type": [ + "null", + "string" + ] + }, + "httpStatusCode": { + "$ref": "#/components/schemas/HttpStatusCode" + }, + "traceIdentifier": { + "type": [ + "null", + "string" + ] + } + } + }, + "ApiResponseOfListOfPortalTaxRegimeDto": { + "type": "object", + "properties": { + "data": { + "type": [ + "null", + "array" + ], + "items": { + "$ref": "#/components/schemas/PortalTaxRegimeDto" + } + }, + "succeeded": { + "type": "boolean" + }, + "message": { + "type": [ + "null", + "string" + ] + }, + "details": { + "type": [ + "null", + "string" + ] + }, + "httpStatusCode": { + "$ref": "#/components/schemas/HttpStatusCode" + }, + "traceIdentifier": { + "type": [ + "null", + "string" + ] + } + } + }, + "ApiResponseOfObject": { + "type": "object", + "properties": { + "data": { }, + "succeeded": { + "type": "boolean" + }, + "message": { + "type": [ + "null", + "string" + ] + }, + "details": { + "type": [ + "null", + "string" + ] + }, + "httpStatusCode": { + "$ref": "#/components/schemas/HttpStatusCode" + }, + "traceIdentifier": { + "type": [ + "null", + "string" + ] + } + } + }, + "ApiResponseOfPagedListOfJsonElement": { + "type": "object", + "properties": { + "data": { + "oneOf": [ + { + "type": "null" + }, + { + "$ref": "#/components/schemas/PagedListOfJsonElement" + } + ] + }, + "succeeded": { + "type": "boolean" + }, + "message": { + "type": [ + "null", + "string" + ] + }, + "details": { + "type": [ + "null", + "string" + ] + }, + "httpStatusCode": { + "$ref": "#/components/schemas/HttpStatusCode" + }, + "traceIdentifier": { + "type": [ + "null", + "string" + ] + } + } + }, + "ApiResponseOfPagedListOfTicketDto": { + "type": "object", + "properties": { + "data": { + "oneOf": [ + { + "type": "null" + }, + { + "$ref": "#/components/schemas/PagedListOfTicketDto" + } + ] + }, + "succeeded": { + "type": "boolean" + }, + "message": { + "type": [ + "null", + "string" + ] + }, + "details": { + "type": [ + "null", + "string" + ] + }, + "httpStatusCode": { + "$ref": "#/components/schemas/HttpStatusCode" + }, + "traceIdentifier": { + "type": [ + "null", + "string" + ] + } + } + }, + "ApiResponseOfPagedListOfWebhookDeliveryDto": { + "type": "object", + "properties": { + "data": { + "oneOf": [ + { + "type": "null" + }, + { + "$ref": "#/components/schemas/PagedListOfWebhookDeliveryDto" + } + ] + }, + "succeeded": { + "type": "boolean" + }, + "message": { + "type": [ + "null", + "string" + ] + }, + "details": { + "type": [ + "null", + "string" + ] + }, + "httpStatusCode": { + "$ref": "#/components/schemas/HttpStatusCode" + }, + "traceIdentifier": { + "type": [ + "null", + "string" + ] + } + } + }, + "ApiResponseOfPagedListOfWebhookDto": { + "type": "object", + "properties": { + "data": { + "oneOf": [ + { + "type": "null" + }, + { + "$ref": "#/components/schemas/PagedListOfWebhookDto" + } + ] + }, + "succeeded": { + "type": "boolean" + }, + "message": { + "type": [ + "null", + "string" + ] + }, + "details": { + "type": [ + "null", + "string" + ] + }, + "httpStatusCode": { + "$ref": "#/components/schemas/HttpStatusCode" + }, + "traceIdentifier": { + "type": [ + "null", + "string" + ] + } + } + }, + "ApiResponseOfPortalBrandingDto": { + "type": "object", + "properties": { + "data": { + "oneOf": [ + { + "type": "null" + }, + { + "$ref": "#/components/schemas/PortalBrandingDto" + } + ] + }, + "succeeded": { + "type": "boolean" + }, + "message": { + "type": [ + "null", + "string" + ] + }, + "details": { + "type": [ + "null", + "string" + ] + }, + "httpStatusCode": { + "$ref": "#/components/schemas/HttpStatusCode" + }, + "traceIdentifier": { + "type": [ + "null", + "string" + ] + } + } + }, + "ApiResponseOfPortalInvoiceDto": { + "type": "object", + "properties": { + "data": { + "oneOf": [ + { + "type": "null" + }, + { + "$ref": "#/components/schemas/PortalInvoiceDto" + } + ] + }, + "succeeded": { + "type": "boolean" + }, + "message": { + "type": [ + "null", + "string" + ] + }, + "details": { + "type": [ + "null", + "string" + ] + }, + "httpStatusCode": { + "$ref": "#/components/schemas/HttpStatusCode" + }, + "traceIdentifier": { + "type": [ + "null", + "string" + ] + } + } + }, + "ApiResponseOfPortalNameAvailabilityDto": { + "type": "object", + "properties": { + "data": { + "oneOf": [ + { + "type": "null" + }, + { + "$ref": "#/components/schemas/PortalNameAvailabilityDto" + } + ] + }, + "succeeded": { + "type": "boolean" + }, + "message": { + "type": [ + "null", + "string" + ] + }, + "details": { + "type": [ + "null", + "string" + ] + }, + "httpStatusCode": { + "$ref": "#/components/schemas/HttpStatusCode" + }, + "traceIdentifier": { + "type": [ + "null", + "string" + ] + } + } + }, + "ApiResponseOfPortalNameDto": { + "type": "object", + "properties": { + "data": { + "oneOf": [ + { + "type": "null" + }, + { + "$ref": "#/components/schemas/PortalNameDto" + } + ] + }, + "succeeded": { + "type": "boolean" + }, + "message": { + "type": [ + "null", + "string" + ] + }, + "details": { + "type": [ + "null", + "string" + ] + }, + "httpStatusCode": { + "$ref": "#/components/schemas/HttpStatusCode" + }, + "traceIdentifier": { + "type": [ + "null", + "string" + ] + } + } + }, + "ApiResponseOfPortalRecoveryDto": { + "type": "object", + "properties": { + "data": { + "oneOf": [ + { + "type": "null" + }, + { + "$ref": "#/components/schemas/PortalRecoveryDto" + } + ] + }, + "succeeded": { + "type": "boolean" + }, + "message": { + "type": [ + "null", + "string" + ] + }, + "details": { + "type": [ + "null", + "string" + ] + }, + "httpStatusCode": { + "$ref": "#/components/schemas/HttpStatusCode" + }, + "traceIdentifier": { + "type": [ + "null", + "string" + ] + } + } + }, + "ApiResponseOfPortalResendDto": { + "type": "object", + "properties": { + "data": { + "oneOf": [ + { + "type": "null" + }, + { + "$ref": "#/components/schemas/PortalResendDto" + } + ] + }, + "succeeded": { + "type": "boolean" + }, + "message": { + "type": [ + "null", + "string" + ] + }, + "details": { + "type": [ + "null", + "string" + ] + }, + "httpStatusCode": { + "$ref": "#/components/schemas/HttpStatusCode" + }, + "traceIdentifier": { + "type": [ + "null", + "string" + ] + } + } + }, + "ApiResponseOfPortalTicketDto": { + "type": "object", + "properties": { + "data": { + "oneOf": [ + { + "type": "null" + }, + { + "$ref": "#/components/schemas/PortalTicketDto" + } + ] + }, + "succeeded": { + "type": "boolean" + }, + "message": { + "type": [ + "null", + "string" + ] + }, + "details": { + "type": [ + "null", + "string" + ] + }, + "httpStatusCode": { + "$ref": "#/components/schemas/HttpStatusCode" + }, + "traceIdentifier": { + "type": [ + "null", + "string" + ] + } + } + }, + "ApiResponseOfTicketCodeDto": { + "type": "object", + "properties": { + "data": { + "oneOf": [ + { + "type": "null" + }, + { + "$ref": "#/components/schemas/TicketCodeDto" + } + ] + }, + "succeeded": { + "type": "boolean" + }, + "message": { + "type": [ + "null", + "string" + ] + }, + "details": { + "type": [ + "null", + "string" + ] + }, + "httpStatusCode": { + "$ref": "#/components/schemas/HttpStatusCode" + }, + "traceIdentifier": { + "type": [ + "null", + "string" + ] + } + } + }, + "ApiResponseOfTicketDto": { + "type": "object", + "properties": { + "data": { + "oneOf": [ + { + "type": "null" + }, + { + "$ref": "#/components/schemas/TicketDto" + } + ] + }, + "succeeded": { + "type": "boolean" + }, + "message": { + "type": [ + "null", + "string" + ] + }, + "details": { + "type": [ + "null", + "string" + ] + }, + "httpStatusCode": { + "$ref": "#/components/schemas/HttpStatusCode" + }, + "traceIdentifier": { + "type": [ + "null", + "string" + ] + } + } + }, + "ApiResponseOfTicketIngestedDto": { + "type": "object", + "properties": { + "data": { + "oneOf": [ + { + "type": "null" + }, + { + "$ref": "#/components/schemas/TicketIngestedDto" + } + ] + }, + "succeeded": { + "type": "boolean" + }, + "message": { + "type": [ + "null", + "string" + ] + }, + "details": { + "type": [ + "null", + "string" + ] + }, + "httpStatusCode": { + "$ref": "#/components/schemas/HttpStatusCode" + }, + "traceIdentifier": { + "type": [ + "null", + "string" + ] + } + } + }, + "ApiResponseOfWebhookDto": { + "type": "object", + "properties": { + "data": { + "oneOf": [ + { + "type": "null" + }, + { + "$ref": "#/components/schemas/WebhookDto" + } + ] + }, + "succeeded": { + "type": "boolean" + }, + "message": { + "type": [ + "null", + "string" + ] + }, + "details": { + "type": [ + "null", + "string" + ] + }, + "httpStatusCode": { + "$ref": "#/components/schemas/HttpStatusCode" + }, + "traceIdentifier": { + "type": [ + "null", + "string" + ] + } + } + }, + "ApiResponseOfWebhookEventResendDto": { + "type": "object", + "properties": { + "data": { + "oneOf": [ + { + "type": "null" + }, + { + "$ref": "#/components/schemas/WebhookEventResendDto" + } + ] + }, + "succeeded": { + "type": "boolean" + }, + "message": { + "type": [ + "null", + "string" + ] + }, + "details": { + "type": [ + "null", + "string" + ] + }, + "httpStatusCode": { + "$ref": "#/components/schemas/HttpStatusCode" + }, + "traceIdentifier": { + "type": [ + "null", + "string" + ] + } + } + }, + "ApiResponseOfWebhookEventTriggerDto": { + "type": "object", + "properties": { + "data": { + "oneOf": [ + { + "type": "null" + }, + { + "$ref": "#/components/schemas/WebhookEventTriggerDto" + } + ] + }, + "succeeded": { + "type": "boolean" + }, + "message": { + "type": [ + "null", + "string" + ] + }, + "details": { + "type": [ + "null", + "string" + ] + }, + "httpStatusCode": { + "$ref": "#/components/schemas/HttpStatusCode" + }, + "traceIdentifier": { + "type": [ + "null", + "string" + ] + } + } + }, + "ApiResponseOfWebhookTestDto": { + "type": "object", + "properties": { + "data": { + "oneOf": [ + { + "type": "null" + }, + { + "$ref": "#/components/schemas/WebhookTestDto" + } + ] + }, + "succeeded": { + "type": "boolean" + }, + "message": { + "type": [ + "null", + "string" + ] + }, + "details": { + "type": [ + "null", + "string" + ] + }, + "httpStatusCode": { + "$ref": "#/components/schemas/HttpStatusCode" + }, + "traceIdentifier": { + "type": [ + "null", + "string" + ] + } + } + }, + "ApiResponseOfWebhookWithSecretDto": { + "type": "object", + "properties": { + "data": { + "oneOf": [ + { + "type": "null" + }, + { + "$ref": "#/components/schemas/WebhookWithSecretDto" + } + ] + }, + "succeeded": { + "type": "boolean" + }, + "message": { + "type": [ + "null", + "string" + ] + }, + "details": { + "type": [ + "null", + "string" + ] + }, + "httpStatusCode": { + "$ref": "#/components/schemas/HttpStatusCode" + }, + "traceIdentifier": { + "type": [ + "null", + "string" + ] + } + } + }, + "AutoInvoiceSettingDto": { + "required": [ + "allowBillingOutsideMonth", + "allowBillingOutsideYear", + "privacyNoticeUrl" + ], + "type": "object", + "properties": { + "allowBillingOutsideMonth": { + "type": "boolean" + }, + "allowBillingOutsideYear": { + "type": "boolean" + }, + "privacyNoticeUrl": { + "type": [ + "null", + "string" + ] + } + } + }, + "BalanceCompensationRequest": { + "type": "object", + "properties": { + "favorableBalance": { + "pattern": "^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?$", + "type": [ + "number", + "string" + ], + "format": "double" + }, + "year": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "integer", + "string" + ], + "format": "int16" + }, + "remainingFavorableBalance": { + "pattern": "^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?$", + "type": [ + "number", + "string" + ], + "format": "double" + } + } + }, + "CancelInvoiceCommand": { + "type": "object", + "properties": { + "id": { + "type": [ + "null", + "string" + ] + }, + "invoiceUuid": { + "type": [ + "null", + "string" + ] + }, + "tin": { + "type": [ + "null", + "string" + ] + }, + "cancellationReasonCode": { + "type": [ + "null", + "string" + ] + }, + "replacementUuid": { + "type": [ + "null", + "string" + ] + }, + "taxCredentials": { + "type": [ + "null", + "array" + ], + "items": { + "$ref": "#/components/schemas/CredentialInputModel" + } + } + } + }, + "CartaPorteAutotransporteRequest": { + "type": "object", + "properties": { + "permSCTId": { + "type": "string" + }, + "numPermisoSCT": { + "type": "string" + }, + "configVehicularId": { + "type": "string" + }, + "pesoBrutoVehicular": { + "pattern": "^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?$", + "type": [ + "number", + "string" + ], + "format": "double" + }, + "placaVM": { + "type": "string" + }, + "anioModeloVM": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "integer", + "string" + ], + "format": "int32" + }, + "aseguraRespCivil": { + "type": "string" + }, + "polizaRespCivil": { + "type": "string" + }, + "aseguraMedAmbiente": { + "type": [ + "null", + "string" + ] + }, + "polizaMedAmbiente": { + "type": [ + "null", + "string" + ] + }, + "aseguraCarga": { + "type": [ + "null", + "string" + ] + }, + "polizaCarga": { + "type": [ + "null", + "string" + ] + }, + "primaSeguro": { + "pattern": "^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?$", + "type": [ + "null", + "number", + "string" + ], + "format": "double" + }, + "remolques": { + "type": [ + "null", + "array" + ], + "items": { + "$ref": "#/components/schemas/CartaPorteRemolqueRequest" + } + } + } + }, + "CartaPorteCantidadTransportaRequest": { + "type": "object", + "properties": { + "cantidad": { + "pattern": "^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?$", + "type": [ + "number", + "string" + ], + "format": "double" + }, + "idOrigen": { + "type": "string" + }, + "idDestino": { + "type": "string" + }, + "cvesTransporteId": { + "type": [ + "null", + "string" + ] + } + } + }, + "CartaPorteCarroContenedorRequest": { + "type": "object", + "properties": { + "tipoContenedorId": { + "type": "string" + }, + "pesoContenedorVacio": { + "pattern": "^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?$", + "type": [ + "number", + "string" + ], + "format": "double" + }, + "pesoNetoMercancia": { + "pattern": "^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?$", + "type": [ + "number", + "string" + ], + "format": "double" + } + } + }, + "CartaPorteCarroRequest": { + "type": "object", + "properties": { + "tipoCarroId": { + "type": "string" + }, + "matriculaCarro": { + "type": "string" + }, + "guiaCarro": { + "type": "string" + }, + "toneladasNetasCarro": { + "pattern": "^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?$", + "type": [ + "number", + "string" + ], + "format": "double" + }, + "contenedores": { + "type": [ + "null", + "array" + ], + "items": { + "$ref": "#/components/schemas/CartaPorteCarroContenedorRequest" + } + } + } + }, + "CartaPorteComplementRequest": { + "type": "object", + "properties": { + "transpInternacId": { + "type": "string" + }, + "entradaSalidaMercId": { + "type": [ + "null", + "string" + ] + }, + "paisOrigenDestinoId": { + "type": [ + "null", + "string" + ] + }, + "viaEntradaSalidaId": { + "type": [ + "null", + "string" + ] + }, + "totalDistRec": { + "pattern": "^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?$", + "type": [ + "null", + "number", + "string" + ], + "format": "double" + }, + "registroISTMOId": { + "type": [ + "null", + "string" + ] + }, + "ubicacionPoloOrigenId": { + "type": [ + "null", + "string" + ] + }, + "ubicacionPoloDestinoId": { + "type": [ + "null", + "string" + ] + }, + "unidadPesoId": { + "type": "string" + }, + "pesoNetoTotal": { + "pattern": "^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?$", + "type": [ + "null", + "number", + "string" + ], + "format": "double" + }, + "cargoPorTasacion": { + "pattern": "^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?$", + "type": [ + "null", + "number", + "string" + ], + "format": "double" + }, + "logisticaInversaRecoleccionDevolucionId": { + "type": [ + "null", + "string" + ] + }, + "regimenAduaneros": { + "type": [ + "null", + "array" + ], + "items": { + "$ref": "#/components/schemas/CartaPorteRegimenAduaneroRequest" + } + }, + "ubicaciones": { + "type": "array", + "items": { + "$ref": "#/components/schemas/CartaPorteUbicacionRequest" + } + }, + "mercancias": { + "type": "array", + "items": { + "$ref": "#/components/schemas/CartaPorteMercanciaRequest" + } + }, + "autotransporte": { + "oneOf": [ + { + "type": "null" + }, + { + "$ref": "#/components/schemas/CartaPorteAutotransporteRequest" + } + ] + }, + "transporteMaritimo": { + "oneOf": [ + { + "type": "null" + }, + { + "$ref": "#/components/schemas/CartaPorteTransporteMaritimoRequest" + } + ] + }, + "transporteAereo": { + "oneOf": [ + { + "type": "null" + }, + { + "$ref": "#/components/schemas/CartaPorteTransporteAereoRequest" + } + ] + }, + "transporteFerroviario": { + "oneOf": [ + { + "type": "null" + }, + { + "$ref": "#/components/schemas/CartaPorteTransporteFerroviarioRequest" + } + ] + }, + "tiposFigura": { + "type": [ + "null", + "array" + ], + "items": { + "$ref": "#/components/schemas/CartaPorteTiposFiguraRequest" + } + } + } + }, + "CartaPorteContenedorMaritimoRequest": { + "type": "object", + "properties": { + "tipoContenedorId": { + "type": "string" + }, + "matriculaContenedor": { + "type": [ + "null", + "string" + ] + }, + "numPrecinto": { + "type": [ + "null", + "string" + ] + }, + "idCCPRelacionado": { + "type": [ + "null", + "string" + ] + }, + "placaVMCCP": { + "type": [ + "null", + "string" + ] + }, + "fechaCertificacionCCP": { + "type": [ + "null", + "string" + ], + "format": "date-time" + }, + "remolquesCCP": { + "type": [ + "null", + "array" + ], + "items": { + "$ref": "#/components/schemas/CartaPorteRemolqueCCPMaritimoRequest" + } + } + } + }, + "CartaPorteDerechosDePasoRequest": { + "type": "object", + "properties": { + "tipoDerechoDePasoId": { + "type": "string" + }, + "kilometrajePagado": { + "pattern": "^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?$", + "type": [ + "number", + "string" + ], + "format": "double" + } + } + }, + "CartaPorteDetalleMercanciaRequest": { + "type": "object", + "properties": { + "unidadPesoMercId": { + "type": "string" + }, + "pesoBruto": { + "pattern": "^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?$", + "type": [ + "number", + "string" + ], + "format": "double" + }, + "pesoNeto": { + "pattern": "^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?$", + "type": [ + "number", + "string" + ], + "format": "double" + }, + "pesoTara": { + "pattern": "^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?$", + "type": [ + "number", + "string" + ], + "format": "double" + }, + "numPiezas": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "null", + "integer", + "string" + ], + "format": "int32" + } + } + }, + "CartaPorteDocumentacionAduaneraRequest": { + "type": "object", + "properties": { + "tipoDocumentoId": { + "type": "string" + }, + "numPedimento": { + "type": [ + "null", + "string" + ] + }, + "identDocAduanero": { + "type": [ + "null", + "string" + ] + }, + "rfcImpo": { + "type": [ + "null", + "string" + ] + } + } + }, + "CartaPorteGuiaIdentificacionRequest": { + "type": "object", + "properties": { + "numeroGuiaIdentificacion": { + "type": "string" + }, + "descripGuiaIdentificacion": { + "type": "string" + }, + "pesoGuiaIdentificacion": { + "pattern": "^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?$", + "type": [ + "number", + "string" + ], + "format": "double" + } + } + }, + "CartaPorteMercanciaRequest": { + "type": "object", + "properties": { + "bienesTranspId": { + "type": "string" + }, + "claveSTCCId": { + "type": [ + "null", + "string" + ] + }, + "descripcion": { + "type": "string" + }, + "cantidad": { + "pattern": "^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?$", + "type": [ + "number", + "string" + ], + "format": "double" + }, + "claveUnidadId": { + "type": "string" + }, + "unidad": { + "type": [ + "null", + "string" + ] + }, + "dimensiones": { + "type": [ + "null", + "string" + ] + }, + "materialPeligrosoId": { + "type": [ + "null", + "string" + ] + }, + "cveMaterialPeligrosoId": { + "type": [ + "null", + "string" + ] + }, + "embalajeId": { + "type": [ + "null", + "string" + ] + }, + "descripEmbalaje": { + "type": [ + "null", + "string" + ] + }, + "sectorCOFEPRISId": { + "type": [ + "null", + "string" + ] + }, + "nombreIngredienteActivo": { + "type": [ + "null", + "string" + ] + }, + "nomQuimico": { + "type": [ + "null", + "string" + ] + }, + "denominacionGenericaProd": { + "type": [ + "null", + "string" + ] + }, + "denominacionDistintivaProd": { + "type": [ + "null", + "string" + ] + }, + "fabricante": { + "type": [ + "null", + "string" + ] + }, + "fechaCaducidad": { + "type": [ + "null", + "string" + ], + "format": "date-time" + }, + "loteMedicamento": { + "type": [ + "null", + "string" + ] + }, + "formaFarmaceuticaId": { + "type": [ + "null", + "string" + ] + }, + "condicionesEspTranspId": { + "type": [ + "null", + "string" + ] + }, + "registroSanitarioFolioAutorizacion": { + "type": [ + "null", + "string" + ] + }, + "permisoImportacion": { + "type": [ + "null", + "string" + ] + }, + "folioImpoVUCEM": { + "type": [ + "null", + "string" + ] + }, + "numCAS": { + "type": [ + "null", + "string" + ] + }, + "razonSocialEmpImp": { + "type": [ + "null", + "string" + ] + }, + "numRegSanPlagCOFEPRIS": { + "type": [ + "null", + "string" + ] + }, + "datosFabricante": { + "type": [ + "null", + "string" + ] + }, + "datosFormulador": { + "type": [ + "null", + "string" + ] + }, + "datosMaquilador": { + "type": [ + "null", + "string" + ] + }, + "usoAutorizado": { + "type": [ + "null", + "string" + ] + }, + "pesoEnKg": { + "pattern": "^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?$", + "type": [ + "number", + "string" + ], + "format": "double" + }, + "valorMercancia": { + "pattern": "^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?$", + "type": [ + "null", + "number", + "string" + ], + "format": "double" + }, + "monedaId": { + "type": [ + "null", + "string" + ] + }, + "fraccionArancelariaId": { + "type": [ + "null", + "string" + ] + }, + "uuidComercioExt": { + "type": [ + "null", + "string" + ] + }, + "tipoMateriaId": { + "type": [ + "null", + "string" + ] + }, + "descripcionMateria": { + "type": [ + "null", + "string" + ] + }, + "documentacionAduanera": { + "type": [ + "null", + "array" + ], + "items": { + "$ref": "#/components/schemas/CartaPorteDocumentacionAduaneraRequest" + } + }, + "guiasIdentificacion": { + "type": [ + "null", + "array" + ], + "items": { + "$ref": "#/components/schemas/CartaPorteGuiaIdentificacionRequest" + } + }, + "cantidadTransporta": { + "type": [ + "null", + "array" + ], + "items": { + "$ref": "#/components/schemas/CartaPorteCantidadTransportaRequest" + } + }, + "detalleMercancia": { + "oneOf": [ + { + "type": "null" + }, + { + "$ref": "#/components/schemas/CartaPorteDetalleMercanciaRequest" + } + ] + } + } + }, + "CartaPortePartesTransporteRequest": { + "type": "object", + "properties": { + "parteTransporteId": { + "type": "string" + } + } + }, + "CartaPorteRegimenAduaneroRequest": { + "type": "object", + "properties": { + "regimenAduaneroId": { + "type": "string" + } + } + }, + "CartaPorteRemolqueCCPMaritimoRequest": { + "type": "object", + "properties": { + "subTipoRemCCPId": { + "type": "string" + }, + "placaCCP": { + "type": "string" + } + } + }, + "CartaPorteRemolqueRequest": { + "type": "object", + "properties": { + "subTipoRemId": { + "type": "string" + }, + "placa": { + "type": "string" + } + } + }, + "CartaPorteTiposFiguraDomicilioRequest": { + "type": "object", + "properties": { + "calle": { + "type": [ + "null", + "string" + ] + }, + "numeroExterior": { + "type": [ + "null", + "string" + ] + }, + "numeroInterior": { + "type": [ + "null", + "string" + ] + }, + "coloniaId": { + "type": [ + "null", + "string" + ] + }, + "localidadId": { + "type": [ + "null", + "string" + ] + }, + "referencia": { + "type": [ + "null", + "string" + ] + }, + "municipioId": { + "type": [ + "null", + "string" + ] + }, + "estadoId": { + "type": "string" + }, + "paisId": { + "type": "string" + }, + "codigoPostalId": { + "type": "string" + } + } + }, + "CartaPorteTiposFiguraRequest": { + "type": "object", + "properties": { + "tipoFiguraId": { + "type": "string" + }, + "rfcFigura": { + "type": [ + "null", + "string" + ] + }, + "numLicencia": { + "type": [ + "null", + "string" + ] + }, + "nombreFigura": { + "type": "string" + }, + "numRegIdTribFigura": { + "type": [ + "null", + "string" + ] + }, + "residenciaFiscalFiguraId": { + "type": [ + "null", + "string" + ] + }, + "partesTransporte": { + "type": [ + "null", + "array" + ], + "items": { + "$ref": "#/components/schemas/CartaPortePartesTransporteRequest" + } + }, + "domicilio": { + "oneOf": [ + { + "type": "null" + }, + { + "$ref": "#/components/schemas/CartaPorteTiposFiguraDomicilioRequest" + } + ] + } + } + }, + "CartaPorteTransporteAereoRequest": { + "type": "object", + "properties": { + "permSCTId": { + "type": "string" + }, + "numPermisoSCT": { + "type": "string" + }, + "matriculaAeronave": { + "type": [ + "null", + "string" + ] + }, + "nombreAseg": { + "type": [ + "null", + "string" + ] + }, + "numPolizaSeguro": { + "type": [ + "null", + "string" + ] + }, + "numeroGuia": { + "type": "string" + }, + "lugarContrato": { + "type": [ + "null", + "string" + ] + }, + "codigoTransportistaId": { + "type": "string" + }, + "rfcEmbarcador": { + "type": [ + "null", + "string" + ] + }, + "numRegIdTribEmbarc": { + "type": [ + "null", + "string" + ] + }, + "residenciaFiscalEmbarcId": { + "type": [ + "null", + "string" + ] + }, + "nombreEmbarcador": { + "type": [ + "null", + "string" + ] + } + } + }, + "CartaPorteTransporteFerroviarioRequest": { + "type": "object", + "properties": { + "tipoDeServicioId": { + "type": "string" + }, + "tipoDeTraficoId": { + "type": "string" + }, + "nombreAseg": { + "type": [ + "null", + "string" + ] + }, + "numPolizaSeguro": { + "type": [ + "null", + "string" + ] + }, + "derechosDePaso": { + "type": [ + "null", + "array" + ], + "items": { + "$ref": "#/components/schemas/CartaPorteDerechosDePasoRequest" + } + }, + "carros": { + "type": "array", + "items": { + "$ref": "#/components/schemas/CartaPorteCarroRequest" + } + } + } + }, + "CartaPorteTransporteMaritimoRequest": { + "type": "object", + "properties": { + "permSCTId": { + "type": [ + "null", + "string" + ] + }, + "numPermisoSCT": { + "type": [ + "null", + "string" + ] + }, + "nombreAseg": { + "type": [ + "null", + "string" + ] + }, + "numPolizaSeguro": { + "type": [ + "null", + "string" + ] + }, + "tipoEmbarcacionId": { + "type": "string" + }, + "matricula": { + "type": "string" + }, + "numeroOMI": { + "type": "string" + }, + "anioEmbarcacion": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "null", + "integer", + "string" + ], + "format": "int32" + }, + "nombreEmbarc": { + "type": [ + "null", + "string" + ] + }, + "nacionalidadEmbarcId": { + "type": "string" + }, + "unidadesDeArqBruto": { + "pattern": "^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?$", + "type": [ + "number", + "string" + ], + "format": "double" + }, + "tipoCargaId": { + "type": "string" + }, + "eslora": { + "pattern": "^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?$", + "type": [ + "null", + "number", + "string" + ], + "format": "double" + }, + "manga": { + "pattern": "^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?$", + "type": [ + "null", + "number", + "string" + ], + "format": "double" + }, + "calado": { + "pattern": "^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?$", + "type": [ + "null", + "number", + "string" + ], + "format": "double" + }, + "puntal": { + "pattern": "^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?$", + "type": [ + "null", + "number", + "string" + ], + "format": "double" + }, + "lineaNaviera": { + "type": [ + "null", + "string" + ] + }, + "nombreAgenteNaviero": { + "type": "string" + }, + "numAutorizacionNavieroId": { + "type": "string" + }, + "numViaje": { + "type": [ + "null", + "string" + ] + }, + "numConocEmbarc": { + "type": [ + "null", + "string" + ] + }, + "permisoTempNavegacion": { + "type": [ + "null", + "string" + ] + }, + "contenedores": { + "type": [ + "null", + "array" + ], + "items": { + "$ref": "#/components/schemas/CartaPorteContenedorMaritimoRequest" + } + } + } + }, + "CartaPorteUbicacionDomicilioRequest": { + "type": "object", + "properties": { + "calle": { + "type": [ + "null", + "string" + ] + }, + "numeroExterior": { + "type": [ + "null", + "string" + ] + }, + "numeroInterior": { + "type": [ + "null", + "string" + ] + }, + "coloniaId": { + "type": [ + "null", + "string" + ] + }, + "localidadId": { + "type": [ + "null", + "string" + ] + }, + "referencia": { + "type": [ + "null", + "string" + ] + }, + "municipioId": { + "type": [ + "null", + "string" + ] + }, + "estadoId": { + "type": "string" + }, + "paisId": { + "type": "string" + }, + "codigoPostalId": { + "type": "string" + } + } + }, + "CartaPorteUbicacionRequest": { + "type": "object", + "properties": { + "tipoUbicacion": { + "type": "string" + }, + "idUbicacion": { + "type": [ + "null", + "string" + ] + }, + "rfcRemitenteDestinatario": { + "type": "string" + }, + "nombreRemitenteDestinatario": { + "type": [ + "null", + "string" + ] + }, + "numRegIdTrib": { + "type": [ + "null", + "string" + ] + }, + "residenciaFiscalId": { + "type": [ + "null", + "string" + ] + }, + "numEstacionId": { + "type": [ + "null", + "string" + ] + }, + "nombreEstacion": { + "type": [ + "null", + "string" + ] + }, + "navegacionTraficoId": { + "type": [ + "null", + "string" + ] + }, + "fechaHoraSalidaLlegada": { + "type": "string", + "format": "date-time" + }, + "tipoEstacionId": { + "type": [ + "null", + "string" + ] + }, + "distanciaRecorrida": { + "pattern": "^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?$", + "type": [ + "null", + "number", + "string" + ], + "format": "double" + }, + "domicilio": { + "oneOf": [ + { + "type": "null" + }, + { + "$ref": "#/components/schemas/CartaPorteUbicacionDomicilioRequest" + } + ] + } + } + }, + "ComercioExteriorComplementRequest": { + "type": "object", + "properties": { + "motivoTrasladoId": { + "type": [ + "null", + "string" + ] + }, + "claveDePedimentoId": { + "type": "string" + }, + "certificadoOrigen": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "integer", + "string" + ], + "format": "int32" + }, + "numCertificadoOrigen": { + "type": [ + "null", + "string" + ] + }, + "numeroExportadorConfiable": { + "type": [ + "null", + "string" + ] + }, + "incotermId": { + "type": [ + "null", + "string" + ] + }, + "observaciones": { + "type": [ + "null", + "string" + ] + }, + "tipoCambioUSD": { + "pattern": "^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?$", + "type": [ + "number", + "string" + ], + "format": "double" + }, + "emisor": { + "oneOf": [ + { + "type": "null" + }, + { + "$ref": "#/components/schemas/ComercioExteriorEmisorRequest" + } + ] + }, + "receptor": { + "oneOf": [ + { + "type": "null" + }, + { + "$ref": "#/components/schemas/ComercioExteriorReceptorRequest" + } + ] + }, + "propietarios": { + "type": [ + "null", + "array" + ], + "items": { + "$ref": "#/components/schemas/ComercioExteriorPropietarioRequest" + } + }, + "destinatarios": { + "type": [ + "null", + "array" + ], + "items": { + "$ref": "#/components/schemas/ComercioExteriorDestinatarioRequest" + } + }, + "mercancias": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ComercioExteriorMercanciaRequest" + } + } + } + }, + "ComercioExteriorDestinatarioDomicilioRequest": { + "type": "object", + "properties": { + "calle": { + "type": "string" + }, + "numeroExterior": { + "type": [ + "null", + "string" + ] + }, + "numeroInterior": { + "type": [ + "null", + "string" + ] + }, + "colonia": { + "type": [ + "null", + "string" + ] + }, + "localidad": { + "type": [ + "null", + "string" + ] + }, + "referencia": { + "type": [ + "null", + "string" + ] + }, + "municipio": { + "type": [ + "null", + "string" + ] + }, + "estado": { + "type": "string" + }, + "paisId": { + "type": "string" + }, + "codigoPostal": { + "type": "string" + } + } + }, + "ComercioExteriorDestinatarioRequest": { + "type": "object", + "properties": { + "numRegIdTrib": { + "type": [ + "null", + "string" + ] + }, + "nombre": { + "type": [ + "null", + "string" + ] + }, + "domicilios": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ComercioExteriorDestinatarioDomicilioRequest" + } + } + } + }, + "ComercioExteriorEmisorDomicilioRequest": { + "type": "object", + "properties": { + "calle": { + "type": "string" + }, + "numeroExterior": { + "type": [ + "null", + "string" + ] + }, + "numeroInterior": { + "type": [ + "null", + "string" + ] + }, + "coloniaId": { + "type": [ + "null", + "string" + ] + }, + "localidadId": { + "type": [ + "null", + "string" + ] + }, + "referencia": { + "type": [ + "null", + "string" + ] + }, + "municipioId": { + "type": [ + "null", + "string" + ] + }, + "estadoId": { + "type": "string" + }, + "paisId": { + "type": "string" + }, + "codigoPostalId": { + "type": "string" + } + } + }, + "ComercioExteriorEmisorRequest": { + "type": "object", + "properties": { + "curp": { + "type": [ + "null", + "string" + ] + }, + "domicilio": { + "$ref": "#/components/schemas/ComercioExteriorEmisorDomicilioRequest" + } + } + }, + "ComercioExteriorMercanciaDescripcionesEspecificasRequest": { + "type": "object", + "properties": { + "marca": { + "type": "string" + }, + "modelo": { + "type": [ + "null", + "string" + ] + }, + "subModelo": { + "type": [ + "null", + "string" + ] + }, + "numeroSerie": { + "type": [ + "null", + "string" + ] + } + } + }, + "ComercioExteriorMercanciaRequest": { + "type": "object", + "properties": { + "noIdentificacion": { + "type": "string" + }, + "fraccionArancelariaId": { + "type": [ + "null", + "string" + ] + }, + "cantidadAduana": { + "pattern": "^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?$", + "type": [ + "null", + "number", + "string" + ], + "format": "double" + }, + "unidadAduanaId": { + "type": [ + "null", + "string" + ] + }, + "valorUnitarioAduana": { + "pattern": "^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?$", + "type": [ + "null", + "number", + "string" + ], + "format": "double" + }, + "valorDolares": { + "pattern": "^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?$", + "type": [ + "number", + "string" + ], + "format": "double" + }, + "descripcionesEspecificas": { + "type": [ + "null", + "array" + ], + "items": { + "$ref": "#/components/schemas/ComercioExteriorMercanciaDescripcionesEspecificasRequest" + } + } + } + }, + "ComercioExteriorPropietarioRequest": { + "type": "object", + "properties": { + "numRegIdTrib": { + "type": "string" + }, + "residenciaFiscalId": { + "type": "string" + } + } + }, + "ComercioExteriorReceptorDomicilioRequest": { + "type": "object", + "properties": { + "calle": { + "type": "string" + }, + "numeroExterior": { + "type": [ + "null", + "string" + ] + }, + "numeroInterior": { + "type": [ + "null", + "string" + ] + }, + "colonia": { + "type": [ + "null", + "string" + ] + }, + "localidad": { + "type": [ + "null", + "string" + ] + }, + "referencia": { + "type": [ + "null", + "string" + ] + }, + "municipio": { + "type": [ + "null", + "string" + ] + }, + "estado": { + "type": "string" + }, + "paisId": { + "type": "string" + }, + "codigoPostal": { + "type": "string" + } + } + }, + "ComercioExteriorReceptorRequest": { + "type": "object", + "properties": { + "numRegIdTrib": { + "type": [ + "null", + "string" + ] + }, + "domicilio": { + "oneOf": [ + { + "type": "null" + }, + { + "$ref": "#/components/schemas/ComercioExteriorReceptorDomicilioRequest" + } + ] + } + } + }, + "ComplementRequest": { + "type": "object", + "properties": { + "localTaxes": { + "oneOf": [ + { + "type": "null" + }, + { + "$ref": "#/components/schemas/LocalTaxesComplementRequest" + } + ] + }, + "payment": { + "oneOf": [ + { + "type": "null" + }, + { + "$ref": "#/components/schemas/PaymentComplementRequest" + } + ] + }, + "payroll": { + "oneOf": [ + { + "type": "null" + }, + { + "$ref": "#/components/schemas/PayrollComplementRequest" + } + ] + }, + "cartaPorte": { + "oneOf": [ + { + "type": "null" + }, + { + "$ref": "#/components/schemas/CartaPorteComplementRequest" + } + ] + }, + "comercioExterior": { + "oneOf": [ + { + "type": "null" + }, + { + "$ref": "#/components/schemas/ComercioExteriorComplementRequest" + } + ] + } + } + }, + "ConsumptionReportDto": { + "required": [ + "from", + "to", + "issuers", + "payers", + "totals" + ], + "type": "object", + "properties": { + "from": { + "type": "string", + "format": "date" + }, + "to": { + "type": "string", + "format": "date" + }, + "issuers": { + "type": "array", + "items": { + "$ref": "#/components/schemas/IssuerConsumptionDto" + } + }, + "payers": { + "type": "array", + "items": { + "$ref": "#/components/schemas/PayerConsumptionDto" + } + }, + "totals": { + "$ref": "#/components/schemas/ConsumptionTotalsDto" + } + } + }, + "ConsumptionTotalsDto": { + "required": [ + "ingested", + "pending", + "inProgress", + "invoiced", + "globalized", + "cancelled", + "ticketCreditsConsumed" + ], + "type": "object", + "properties": { + "ingested": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "integer", + "string" + ], + "format": "int32" + }, + "pending": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "integer", + "string" + ], + "format": "int32" + }, + "inProgress": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "integer", + "string" + ], + "format": "int32" + }, + "invoiced": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "integer", + "string" + ], + "format": "int32" + }, + "globalized": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "integer", + "string" + ], + "format": "int32" + }, + "cancelled": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "integer", + "string" + ], + "format": "int32" + }, + "ticketCreditsConsumed": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "integer", + "string" + ], + "format": "int32" + } + } + }, + "CreateApiKeyCommand": { + "type": "object", + "properties": { + "personId": { + "type": [ + "null", + "string" + ] + }, + "description": { + "type": [ + "null", + "string" + ] + } + } + }, + "CreateCommand": { + "type": "object", + "properties": { + "description": { + "type": [ + "null", + "string" + ] + }, + "unitPrice": { + "pattern": "^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?$", + "type": [ + "number", + "string" + ], + "format": "double" + }, + "satUnitMeasurementId": { + "type": [ + "null", + "string" + ] + }, + "satTaxObjectId": { + "type": [ + "null", + "string" + ] + }, + "satProductCodeId": { + "type": [ + "null", + "string" + ] + }, + "productTaxes": { + "type": [ + "null", + "array" + ], + "items": { + "$ref": "#/components/schemas/CreateCommand" + } + } + } + }, + "CreateDownloadRequestCommand": { + "type": "object", + "properties": { + "downloadRuleId": { + "type": [ + "null", + "string" + ] + }, + "downloadRequestTypeId": { + "type": [ + "null", + "string" + ] + }, + "startDate": { + "type": "string", + "format": "date-time" + }, + "endDate": { + "type": "string", + "format": "date-time" + }, + "sourcePackageId": { + "type": [ + "null", + "string" + ] + } + } + }, + "CreateDownloadRuleCommand": { + "type": "object", + "properties": { + "personId": { + "type": [ + "null", + "string" + ] + }, + "description": { + "type": [ + "null", + "string" + ] + }, + "satQueryTypeId": { + "type": [ + "null", + "string" + ] + }, + "downloadTypeId": { + "type": [ + "null", + "string" + ] + }, + "satInvoiceStatusId": { + "type": [ + "null", + "string" + ] + } + } + }, + "CreateEmployeeDataCommand": { + "type": "object", + "properties": { + "employerPersonId": { + "type": "string" + }, + "employeePersonId": { + "type": "string" + }, + "socialSecurityNumber": { + "type": [ + "null", + "string" + ] + }, + "laborRelationStartDate": { + "type": [ + "null", + "string" + ], + "format": "date-time" + }, + "seniority": { + "type": [ + "null", + "string" + ] + }, + "satContractTypeId": { + "type": "string" + }, + "satUnionizedStatusId": { + "type": [ + "null", + "string" + ] + }, + "satTaxRegimeTypeId": { + "type": "string" + }, + "satWorkdayTypeId": { + "type": [ + "null", + "string" + ] + }, + "satJobRiskId": { + "type": [ + "null", + "string" + ] + }, + "satPaymentPeriodicityId": { + "type": "string" + }, + "employeeNumber": { + "type": "string" + }, + "satBankId": { + "type": [ + "null", + "string" + ] + }, + "satPayrollStateId": { + "type": "string" + }, + "department": { + "type": [ + "null", + "string" + ] + }, + "position": { + "type": [ + "null", + "string" + ] + }, + "bankAccount": { + "type": [ + "null", + "string" + ] + }, + "baseSalaryForContributions": { + "pattern": "^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?$", + "type": [ + "null", + "number", + "string" + ], + "format": "double" + }, + "integratedDailySalary": { + "pattern": "^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?$", + "type": [ + "null", + "number", + "string" + ], + "format": "double" + }, + "subcontractorRfc": { + "type": [ + "null", + "string" + ] + }, + "timePercentage": { + "pattern": "^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?$", + "type": [ + "null", + "number", + "string" + ], + "format": "double" + } + } + }, + "CreateEmployerDataCommand": { + "type": "object", + "properties": { + "personId": { + "type": [ + "null", + "string" + ] + }, + "employerRegistration": { + "type": [ + "null", + "string" + ] + }, + "originEmployerTin": { + "type": [ + "null", + "string" + ] + }, + "satFundSourceId": { + "type": [ + "null", + "string" + ] + }, + "ownResourceAmount": { + "pattern": "^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?$", + "type": [ + "null", + "number", + "string" + ], + "format": "double" + } + } + }, + "CreatePdfCommand": { + "type": "object", + "properties": { + "invoiceId": { + "type": [ + "null", + "string" + ] + }, + "base64Logo": { + "type": [ + "null", + "string" + ] + }, + "bandColor": { + "type": [ + "null", + "string" + ] + }, + "fontColor": { + "type": [ + "null", + "string" + ] + } + } + }, + "CreateTicketCommand": { + "type": "object", + "properties": { + "issuer": { + "oneOf": [ + { + "type": "null" + }, + { + "$ref": "#/components/schemas/TicketIssuerRequest" + } + ] + }, + "folio": { + "type": [ + "null", + "string" + ] + }, + "series": { + "type": [ + "null", + "string" + ] + }, + "paymentDate": { + "format": "date-time" + }, + "paymentFormCode": { + "type": [ + "null", + "string" + ] + }, + "currencyCode": { + "type": [ + "null", + "string" + ] + }, + "exchangeRate": { + "pattern": "^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?$", + "type": [ + "null", + "number", + "string" + ], + "format": "double" + }, + "expeditionZipCode": { + "type": [ + "null", + "string" + ] + }, + "total": { + "pattern": "^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?$", + "type": [ + "number", + "string" + ], + "format": "double" + }, + "items": { + "type": [ + "null", + "array" + ], + "items": { + "$ref": "#/components/schemas/TicketItemRequest" + } + } + } + }, + "CreateUserCommand": { + "type": "object", + "properties": { + "legalName": { + "type": [ + "null", + "string" + ] + }, + "email": { + "type": [ + "null", + "string" + ] + }, + "password": { + "type": [ + "null", + "string" + ] + }, + "phoneNumber": { + "type": [ + "null", + "string" + ] + }, + "satTaxRegimeId": { + "type": [ + "null", + "string" + ] + }, + "userTypeId": { + "type": [ + "null", + "string" + ] + }, + "satCfdiUseId": { + "type": [ + "null", + "string" + ] + }, + "tin": { + "type": [ + "null", + "string" + ] + }, + "zipCode": { + "type": [ + "null", + "string" + ] + }, + "base64Photo": { + "type": [ + "null", + "string" + ] + }, + "taxPassword": { + "type": [ + "null", + "string" + ] + }, + "curp": { + "type": [ + "null", + "string" + ] + }, + "countryId": { + "type": [ + "null", + "string" + ] + }, + "foreignTin": { + "type": [ + "null", + "string" + ] + } + } + }, + "CreateWebhookCommand": { + "type": "object", + "properties": { + "url": { + "type": [ + "null", + "string" + ], + "description": "URL https absoluta que recibe las entregas, sin usuario ni contraseña." + }, + "description": { + "type": [ + "null", + "string" + ], + "description": "Texto libre del tenant, hasta 256 caracteres." + }, + "enabledEvents": { + "type": [ + "null", + "array" + ], + "items": { + "type": "string" + }, + "description": "Tipos de evento suscritos, o solo \"*\" para recibirlos todos." + }, + "personIds": { + "type": [ + "null", + "array" + ], + "items": { + "type": "string" + }, + "description": "Personas del tenant cuyos eventos recibe; sin ellas (o vacío), las de todas." + } + } + }, + "CredentialInputModel": { + "type": "object", + "properties": { + "id": { + "type": [ + "null", + "string" + ] + }, + "base64File": { + "type": [ + "null", + "string" + ] + }, + "fileType": { + "oneOf": [ + { + "type": "null" + }, + { + "$ref": "#/components/schemas/FileType" + } + ] + }, + "password": { + "type": [ + "null", + "string" + ] + } + } + }, + "CreditTypeId": { + "type": "integer", + "format": "int32" + }, + "CustomsInfoRequest": { + "type": "object", + "properties": { + "customsNumber": { + "type": [ + "null", + "string" + ] + } + } + }, + "DeductionRequest": { + "type": "object", + "properties": { + "deductionTypeCode": { + "type": "string" + }, + "code": { + "type": "string" + }, + "concept": { + "type": "string" + }, + "amount": { + "pattern": "^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?$", + "type": [ + "number", + "string" + ], + "format": "double" + } + } + }, + "DisabilityRequest": { + "type": "object", + "properties": { + "disabilityDays": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "integer", + "string" + ], + "format": "int32" + }, + "disabilityTypeCode": { + "type": "string" + }, + "monetaryAmount": { + "pattern": "^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?$", + "type": [ + "number", + "string" + ], + "format": "double" + } + } + }, + "EarningRequest": { + "type": "object", + "properties": { + "earningTypeCode": { + "type": "string" + }, + "code": { + "type": "string" + }, + "concept": { + "type": "string" + }, + "taxedAmount": { + "pattern": "^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?$", + "type": [ + "number", + "string" + ], + "format": "double" + }, + "exemptAmount": { + "pattern": "^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?$", + "type": [ + "number", + "string" + ], + "format": "double" + }, + "stockOptions": { + "oneOf": [ + { + "type": "null" + }, + { + "$ref": "#/components/schemas/StockOptionsRequest" + } + ] + }, + "overtime": { + "type": [ + "null", + "array" + ], + "items": { + "$ref": "#/components/schemas/OvertimeRequest" + } + } + } + }, + "EarningsRequest": { + "type": "object", + "properties": { + "earnings": { + "type": [ + "null", + "array" + ], + "items": { + "$ref": "#/components/schemas/EarningRequest" + } + }, + "otherPayments": { + "type": [ + "null", + "array" + ], + "items": { + "$ref": "#/components/schemas/OtherPaymentRequest" + } + }, + "retirement": { + "oneOf": [ + { + "type": "null" + }, + { + "$ref": "#/components/schemas/RetirementRequest" + } + ] + }, + "severance": { + "oneOf": [ + { + "type": "null" + }, + { + "$ref": "#/components/schemas/SeveranceRequest" + } + ] + } + }, + "additionalProperties": false + }, + "EmployeeDataRequest": { + "type": "object", + "properties": { + "curp": { + "type": [ + "null", + "string" + ] + }, + "socialSecurityNumber": { + "type": [ + "null", + "string" + ] + }, + "laborRelationStartDate": { + "type": [ + "null", + "string" + ], + "format": "date-time" + }, + "seniority": { + "type": [ + "null", + "string" + ] + }, + "satContractTypeId": { + "type": [ + "null", + "string" + ] + }, + "satUnionizedStatusId": { + "type": [ + "null", + "string" + ] + }, + "satWorkdayTypeId": { + "type": [ + "null", + "string" + ] + }, + "satTaxRegimeTypeId": { + "type": [ + "null", + "string" + ] + }, + "employeeNumber": { + "type": [ + "null", + "string" + ] + }, + "department": { + "type": [ + "null", + "string" + ] + }, + "position": { + "type": [ + "null", + "string" + ] + }, + "satJobRiskId": { + "type": [ + "null", + "string" + ] + }, + "satPaymentPeriodicityId": { + "type": [ + "null", + "string" + ] + }, + "satBankId": { + "type": [ + "null", + "string" + ] + }, + "bankAccount": { + "type": [ + "null", + "string" + ] + }, + "baseSalaryForContributions": { + "pattern": "^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?$", + "type": [ + "null", + "number", + "string" + ], + "format": "double" + }, + "integratedDailySalary": { + "pattern": "^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?$", + "type": [ + "null", + "number", + "string" + ], + "format": "double" + }, + "satPayrollStateId": { + "type": [ + "null", + "string" + ] + }, + "subcontractorRfc": { + "type": [ + "null", + "string" + ] + }, + "timePercentage": { + "pattern": "^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?$", + "type": [ + "number", + "string" + ], + "format": "double" + } + } + }, + "EmployerDataRequest": { + "type": "object", + "properties": { + "curp": { + "type": [ + "null", + "string" + ] + }, + "employerRegistration": { + "type": [ + "null", + "string" + ] + }, + "originEmployerTin": { + "type": [ + "null", + "string" + ] + }, + "satFundSourceId": { + "type": [ + "null", + "string" + ] + }, + "ownResourceAmount": { + "pattern": "^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?$", + "type": [ + "null", + "number", + "string" + ], + "format": "double" + } + } + }, + "EntityTagHeaderValue": { + "type": "object", + "properties": { + "tag": { + "$ref": "#/components/schemas/StringSegment" + }, + "isWeak": { + "type": "boolean" + } + } + }, + "ExistingTicketData": { + "required": [ + "existingTicketId" + ], + "type": "object", + "properties": { + "existingTicketId": { + "type": "string" + } + } + }, + "FileContentResult": { + "type": "object", + "properties": { + "fileContents": { + "type": "string", + "format": "byte" + }, + "contentType": { + "type": [ + "null", + "string" + ] + }, + "fileDownloadName": { + "type": [ + "null", + "string" + ] + }, + "lastModified": { + "type": [ + "null", + "string" + ], + "format": "date-time" + }, + "entityTag": { + "oneOf": [ + { + "type": "null" + }, + { + "$ref": "#/components/schemas/EntityTagHeaderValue" + } + ] + }, + "enableRangeProcessing": { + "type": "boolean" + } + } + }, + "FileType": { + "type": "integer" + }, + "GlobalInfoRequest": { + "type": "object", + "properties": { + "periodicityCode": { + "type": [ + "null", + "string" + ] + }, + "monthCode": { + "type": [ + "null", + "string" + ] + }, + "year": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "integer", + "string" + ], + "format": "int32" + } + } + }, + "HttpStatusCode": { + "type": "integer" + }, + "InvoiceRequest": { + "type": "object", + "properties": { + "versionCode": { + "type": [ + "null", + "string" + ] + }, + "paymentFormCode": { + "type": [ + "null", + "string" + ] + }, + "paymentMethodCode": { + "type": [ + "null", + "string" + ] + }, + "currencyCode": { + "type": [ + "null", + "string" + ] + }, + "typeCode": { + "type": [ + "null", + "string" + ] + }, + "expeditionZipCode": { + "type": [ + "null", + "string" + ] + }, + "pacConfirmation": { + "type": [ + "null", + "string" + ] + }, + "series": { + "type": [ + "null", + "string" + ] + }, + "number": { + "type": [ + "null", + "string" + ] + }, + "date": { + "type": "string", + "format": "date-time" + }, + "paymentConditions": { + "type": [ + "null", + "string" + ] + }, + "exchangeRate": { + "pattern": "^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?$", + "type": [ + "number", + "string" + ], + "format": "double" + }, + "exportCode": { + "type": [ + "null", + "string" + ] + }, + "issuer": { + "oneOf": [ + { + "type": "null" + }, + { + "$ref": "#/components/schemas/IssuerRequest" + } + ] + }, + "recipient": { + "oneOf": [ + { + "type": "null" + }, + { + "$ref": "#/components/schemas/RecipientRequest" + } + ] + }, + "items": { + "type": [ + "null", + "array" + ], + "items": { + "$ref": "#/components/schemas/ItemRequest" + } + }, + "globalInformation": { + "oneOf": [ + { + "type": "null" + }, + { + "$ref": "#/components/schemas/GlobalInfoRequest" + } + ] + }, + "relatedInvoices": { + "type": [ + "null", + "array" + ], + "items": { + "$ref": "#/components/schemas/RelatedInvoiceRequest" + } + }, + "complement": { + "oneOf": [ + { + "type": "null" + }, + { + "$ref": "#/components/schemas/ComplementRequest" + } + ] + }, + "metadata": { + "type": [ + "null", + "object" + ], + "additionalProperties": { + "type": "string" + } + } + } + }, + "IssuerConsumptionDto": { + "required": [ + "personId", + "tin", + "legalName", + "ingested", + "pending", + "inProgress", + "invoiced", + "globalized", + "cancelled" + ], + "type": "object", + "properties": { + "personId": { + "type": "string" + }, + "tin": { + "type": [ + "null", + "string" + ] + }, + "legalName": { + "type": [ + "null", + "string" + ] + }, + "ingested": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "integer", + "string" + ], + "format": "int32" + }, + "pending": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "integer", + "string" + ], + "format": "int32" + }, + "inProgress": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "integer", + "string" + ], + "format": "int32" + }, + "invoiced": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "integer", + "string" + ], + "format": "int32" + }, + "globalized": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "integer", + "string" + ], + "format": "int32" + }, + "cancelled": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "integer", + "string" + ], + "format": "int32" + } + } + }, + "IssuerRequest": { + "type": "object", + "properties": { + "id": { + "type": [ + "null", + "string" + ] + }, + "tin": { + "type": [ + "null", + "string" + ] + }, + "legalName": { + "type": [ + "null", + "string" + ] + }, + "taxRegimeCode": { + "type": [ + "null", + "string" + ] + }, + "employerData": { + "oneOf": [ + { + "type": "null" + }, + { + "$ref": "#/components/schemas/EmployerDataRequest" + } + ] + }, + "taxCredentials": { + "type": [ + "null", + "array" + ], + "items": { + "$ref": "#/components/schemas/CredentialInputModel" + } + } + } + }, + "ItemPartRequest": { + "type": "object", + "properties": { + "itemCode": { + "type": [ + "null", + "string" + ] + }, + "itemSku": { + "type": [ + "null", + "string" + ] + }, + "quantity": { + "pattern": "^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?$", + "type": [ + "number", + "string" + ], + "format": "double" + }, + "unitOfMeasurementCode": { + "type": [ + "null", + "string" + ] + }, + "description": { + "type": [ + "null", + "string" + ] + }, + "unitPrice": { + "pattern": "^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?$", + "type": [ + "number", + "string" + ], + "format": "double" + }, + "customsInfo": { + "type": [ + "null", + "array" + ], + "items": { + "$ref": "#/components/schemas/CustomsInfoRequest" + } + } + } + }, + "ItemRequest": { + "type": "object", + "properties": { + "id": { + "type": [ + "null", + "string" + ] + }, + "itemCode": { + "type": [ + "null", + "string" + ] + }, + "itemSku": { + "type": [ + "null", + "string" + ] + }, + "quantity": { + "pattern": "^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?$", + "type": [ + "number", + "string" + ], + "format": "double" + }, + "unitOfMeasurementCode": { + "type": [ + "null", + "string" + ] + }, + "description": { + "type": [ + "null", + "string" + ] + }, + "unitPrice": { + "pattern": "^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?$", + "type": [ + "number", + "string" + ], + "format": "double" + }, + "discount": { + "pattern": "^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?$", + "type": [ + "number", + "string" + ], + "format": "double" + }, + "taxObjectCode": { + "type": [ + "null", + "string" + ] + }, + "itemTaxes": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ItemTaxRequest" + } + }, + "onBehalfOf": { + "oneOf": [ + { + "type": "null" + }, + { + "$ref": "#/components/schemas/OnBehalfOfRequest" + } + ] + }, + "customsInfo": { + "type": [ + "null", + "array" + ], + "items": { + "$ref": "#/components/schemas/CustomsInfoRequest" + } + }, + "propertyInfo": { + "type": [ + "null", + "array" + ], + "items": { + "$ref": "#/components/schemas/PropertyInfoRequest" + } + }, + "parts": { + "type": [ + "null", + "array" + ], + "items": { + "$ref": "#/components/schemas/ItemPartRequest" + } + }, + "ticketId": { + "type": [ + "null", + "string" + ] + } + } + }, + "ItemTaxRequest": { + "type": "object", + "properties": { + "taxCode": { + "type": [ + "null", + "string" + ] + }, + "taxTypeCode": { + "type": [ + "null", + "string" + ] + }, + "taxRate": { + "pattern": "^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?$", + "type": [ + "number", + "string" + ], + "format": "double" + }, + "taxFlagCode": { + "type": [ + "null", + "string" + ] + } + } + }, + "JsonElement": { }, + "LocalTaxesComplementRequest": { + "type": "object", + "properties": { + "taxes": { + "type": "array", + "items": { + "$ref": "#/components/schemas/LocalTaxRequest" + } + } + } + }, + "LocalTaxRequest": { + "type": "object", + "properties": { + "taxName": { + "type": [ + "null", + "string" + ] + }, + "taxRate": { + "pattern": "^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?$", + "type": [ + "number", + "string" + ], + "format": "double" + }, + "taxAmount": { + "pattern": "^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?$", + "type": [ + "number", + "string" + ], + "format": "double" + }, + "taxFlagCode": { + "type": [ + "null", + "string" + ] + } + } + }, + "OnBehalfOfRequest": { + "type": "object", + "properties": { + "tin": { + "type": [ + "null", + "string" + ] + }, + "legalName": { + "type": [ + "null", + "string" + ] + }, + "taxRegimeCode": { + "type": [ + "null", + "string" + ] + }, + "zipCode": { + "type": [ + "null", + "string" + ] + } + } + }, + "OtherPaymentRequest": { + "type": "object", + "properties": { + "otherPaymentTypeCode": { + "type": "string" + }, + "code": { + "type": "string" + }, + "concept": { + "type": "string" + }, + "amount": { + "pattern": "^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?$", + "type": [ + "number", + "string" + ], + "format": "double" + }, + "subsidyCaused": { + "pattern": "^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?$", + "type": [ + "null", + "number", + "string" + ], + "format": "double" + }, + "balanceCompensation": { + "oneOf": [ + { + "type": "null" + }, + { + "$ref": "#/components/schemas/BalanceCompensationRequest" + } + ] + } + } + }, + "OvertimeRequest": { + "type": "object", + "properties": { + "days": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "integer", + "string" + ], + "format": "int32" + }, + "hoursTypeCode": { + "type": "string" + }, + "extraHours": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "integer", + "string" + ], + "format": "int32" + }, + "amountPaid": { + "pattern": "^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?$", + "type": [ + "number", + "string" + ], + "format": "double" + } + } + }, + "PagedListOfJsonElement": { + "required": [ + "items", + "pageNumber" + ], + "type": "object", + "properties": { + "items": { + "type": "array" + }, + "pageNumber": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "integer", + "string" + ], + "format": "int32" + }, + "totalPages": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "integer", + "string" + ], + "format": "int32" + }, + "totalCount": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "integer", + "string" + ], + "format": "int32" + }, + "hasPreviousPage": { + "type": "boolean" + }, + "hasNextPage": { + "type": "boolean" + } + } + }, + "PagedListOfTicketDto": { + "required": [ + "items", + "pageNumber" + ], + "type": "object", + "properties": { + "items": { + "type": "array", + "items": { + "$ref": "#/components/schemas/TicketDto" + } + }, + "pageNumber": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "integer", + "string" + ], + "format": "int32" + }, + "totalPages": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "integer", + "string" + ], + "format": "int32" + }, + "totalCount": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "integer", + "string" + ], + "format": "int32" + }, + "hasPreviousPage": { + "type": "boolean" + }, + "hasNextPage": { + "type": "boolean" + } + } + }, + "PagedListOfWebhookDeliveryDto": { + "required": [ + "items", + "pageNumber" + ], + "type": "object", + "properties": { + "items": { + "type": "array", + "items": { + "$ref": "#/components/schemas/WebhookDeliveryDto" + } + }, + "pageNumber": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "integer", + "string" + ], + "format": "int32" + }, + "totalPages": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "integer", + "string" + ], + "format": "int32" + }, + "totalCount": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "integer", + "string" + ], + "format": "int32" + }, + "hasPreviousPage": { + "type": "boolean" + }, + "hasNextPage": { + "type": "boolean" + } + } + }, + "PagedListOfWebhookDto": { + "required": [ + "items", + "pageNumber" + ], + "type": "object", + "properties": { + "items": { + "type": "array", + "items": { + "$ref": "#/components/schemas/WebhookDto" + } + }, + "pageNumber": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "integer", + "string" + ], + "format": "int32" + }, + "totalPages": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "integer", + "string" + ], + "format": "int32" + }, + "totalCount": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "integer", + "string" + ], + "format": "int32" + }, + "hasPreviousPage": { + "type": "boolean" + }, + "hasNextPage": { + "type": "boolean" + } + } + }, + "PaidInvoice": { + "type": "object", + "properties": { + "id": { + "type": [ + "null", + "string" + ] + }, + "uuid": { + "type": [ + "null", + "string" + ] + }, + "series": { + "type": [ + "null", + "string" + ] + }, + "number": { + "type": [ + "null", + "string" + ] + }, + "currencyCode": { + "type": [ + "null", + "string" + ] + }, + "partialityNumber": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "integer", + "string" + ], + "format": "int32" + }, + "subTotal": { + "pattern": "^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?$", + "type": [ + "number", + "string" + ], + "format": "double" + }, + "previousBalance": { + "pattern": "^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?$", + "type": [ + "number", + "string" + ], + "format": "double" + }, + "paymentAmount": { + "pattern": "^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?$", + "type": [ + "number", + "string" + ], + "format": "double" + }, + "remainingBalance": { + "pattern": "^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?$", + "type": [ + "number", + "string" + ], + "format": "double" + }, + "taxObjectCode": { + "type": [ + "null", + "string" + ] + }, + "equivalence": { + "pattern": "^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?$", + "type": [ + "number", + "string" + ], + "format": "double" + }, + "exchangeRate": { + "pattern": "^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?$", + "type": [ + "number", + "string" + ], + "format": "double" + }, + "paidInvoiceTaxes": { + "type": [ + "null", + "array" + ], + "items": { + "$ref": "#/components/schemas/PaidInvoiceTax" + } + } + } + }, + "PaidInvoiceTax": { + "type": "object", + "properties": { + "base": { + "pattern": "^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?$", + "type": [ + "number", + "string" + ], + "format": "double" + }, + "taxCode": { + "type": [ + "null", + "string" + ] + }, + "taxTypeCode": { + "type": [ + "null", + "string" + ] + }, + "taxRate": { + "pattern": "^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?$", + "type": [ + "number", + "string" + ], + "format": "double" + }, + "taxFlagCode": { + "type": [ + "null", + "string" + ] + } + } + }, + "PatchUserCommand": { + "type": "object", + "properties": { + "id": { + "type": [ + "null", + "string" + ] + }, + "legalName": { + "type": [ + "null", + "string" + ] + }, + "email": { + "type": [ + "null", + "string" + ] + }, + "phoneNumber": { + "type": [ + "null", + "string" + ] + }, + "password": { + "type": [ + "null", + "string" + ] + }, + "satTaxRegimeId": { + "type": [ + "null", + "string" + ] + }, + "satCfdiUseId": { + "type": [ + "null", + "string" + ] + }, + "userTypeId": { + "type": [ + "null", + "string" + ] + }, + "tin": { + "type": [ + "null", + "string" + ] + }, + "zipCode": { + "type": [ + "null", + "string" + ] + }, + "base64Photo": { + "type": [ + "null", + "string" + ] + }, + "taxPassword": { + "type": [ + "null", + "string" + ] + }, + "curp": { + "type": [ + "null", + "string" + ] + }, + "countryId": { + "type": [ + "null", + "string" + ] + }, + "foreignTin": { + "type": [ + "null", + "string" + ] + } + } + }, + "PayerConsumptionDto": { + "required": [ + "personId", + "tin", + "legalName", + "ticketCreditsConsumed" + ], + "type": "object", + "properties": { + "personId": { + "type": "string" + }, + "tin": { + "type": [ + "null", + "string" + ] + }, + "legalName": { + "type": [ + "null", + "string" + ] + }, + "ticketCreditsConsumed": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "integer", + "string" + ], + "format": "int32" + } + } + }, + "PaymentComplementRequest": { + "type": "object", + "properties": { + "paymentDate": { + "type": "string", + "format": "date-time" + }, + "paymentFormCode": { + "type": [ + "null", + "string" + ] + }, + "currencyCode": { + "type": "string" + }, + "exchangeRate": { + "pattern": "^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?$", + "type": [ + "number", + "string" + ], + "format": "double" + }, + "amount": { + "pattern": "^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?$", + "type": [ + "number", + "string" + ], + "format": "double" + }, + "operationNumber": { + "type": [ + "null", + "string" + ] + }, + "sourceBankTin": { + "type": [ + "null", + "string" + ] + }, + "sourceBankAccount": { + "type": [ + "null", + "string" + ] + }, + "targetBankTin": { + "type": [ + "null", + "string" + ] + }, + "targetBankAccount": { + "type": [ + "null", + "string" + ] + }, + "foreignBankName": { + "type": [ + "null", + "string" + ] + }, + "paymentTypeCode": { + "type": [ + "null", + "string" + ] + }, + "base64PaymentCertificate": { + "type": [ + "null", + "string" + ] + }, + "paymentOriginalString": { + "type": [ + "null", + "string" + ] + }, + "signatureValue": { + "type": [ + "null", + "string" + ] + }, + "paidInvoices": { + "type": "array", + "items": { + "$ref": "#/components/schemas/PaidInvoice" + } + } + } + }, + "PayrollComplementRequest": { + "type": "object", + "properties": { + "version": { + "type": "string" + }, + "payrollTypeCode": { + "type": [ + "null", + "string" + ] + }, + "paymentDate": { + "type": "string", + "format": "date-time" + }, + "initialPaymentDate": { + "type": "string", + "format": "date-time" + }, + "finalPaymentDate": { + "type": "string", + "format": "date-time" + }, + "daysPaid": { + "pattern": "^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?$", + "type": [ + "number", + "string" + ], + "format": "double" + }, + "earnings": { + "oneOf": [ + { + "type": "null" + }, + { + "$ref": "#/components/schemas/EarningsRequest" + } + ] + }, + "deductions": { + "type": [ + "null", + "array" + ], + "items": { + "$ref": "#/components/schemas/DeductionRequest" + } + }, + "disabilities": { + "type": [ + "null", + "array" + ], + "items": { + "$ref": "#/components/schemas/DisabilityRequest" + } + } + }, + "additionalProperties": false + }, + "PortalBrand": { + "required": [ + "logo", + "bandColor", + "fontColor" + ], + "type": "object", + "properties": { + "logo": { + "type": [ + "null", + "string" + ] + }, + "bandColor": { + "type": [ + "null", + "string" + ] + }, + "fontColor": { + "type": [ + "null", + "string" + ] + } + } + }, + "PortalBrandingDto": { + "required": [ + "canonicalName", + "logo", + "bandColor", + "fontColor", + "portalStatus", + "privacyNoticeUrl" + ], + "type": "object", + "properties": { + "canonicalName": { + "type": "string" + }, + "logo": { + "type": [ + "null", + "string" + ] + }, + "bandColor": { + "type": [ + "null", + "string" + ] + }, + "fontColor": { + "type": [ + "null", + "string" + ] + }, + "portalStatus": { + "$ref": "#/components/schemas/PortalStatus" + }, + "privacyNoticeUrl": { + "type": [ + "null", + "string" + ] + } + } + }, + "PortalCfdiUseDto": { + "required": [ + "code", + "description", + "appliesToPhysicalPerson", + "appliesToLegalEntity", + "recipientTaxRegimes" + ], + "type": "object", + "properties": { + "code": { + "type": "string" + }, + "description": { + "type": "string" + }, + "appliesToPhysicalPerson": { + "type": "boolean" + }, + "appliesToLegalEntity": { + "type": "boolean" + }, + "recipientTaxRegimes": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "PortalIneligibleReason": { + "enum": [ + "invoiced", + "invoicing", + "globalized", + "portalDisabled", + "outsideWindow", + null + ], + "description": "Por qué el receptor no puede autofacturar el ticket. invoiced: ya tiene su factura; se puede recuperar. invoicing: alguien lo está facturando en este momento. globalized: está en una factura global o entrando a una. portalDisabled: el comercio no ofrece facturación en línea por el momento. outsideWindow: ya pasó el plazo para autofacturarlo." + }, + "PortalInvoiceDto": { + "type": "object", + "properties": { + "uuid": { + "type": [ + "null", + "string" + ] + }, + "downloadToken": { + "type": [ + "null", + "string" + ] + }, + "emailSent": { + "type": [ + "null", + "boolean" + ] + }, + "maskedEmail": { + "type": [ + "null", + "string" + ] + }, + "status": { + "oneOf": [ + { + "type": "null" + }, + { + "$ref": "#/components/schemas/TicketStatus" + } + ] + }, + "errorCode": { + "type": [ + "null", + "string" + ] + }, + "field": { + "type": [ + "null", + "string" + ] + } + } + }, + "PortalInvoiceTicketBody": { + "required": [ + "recipient", + "recaptchaToken" + ], + "type": "object", + "properties": { + "recipient": { + "oneOf": [ + { + "type": "null" + }, + { + "$ref": "#/components/schemas/SelfInvoiceRecipient" + } + ] + }, + "recaptchaToken": { + "type": [ + "null", + "string" + ] + } + } + }, + "PortalIssuerDto": { + "required": [ + "legalName", + "tin" + ], + "type": "object", + "properties": { + "legalName": { + "type": [ + "null", + "string" + ] + }, + "tin": { + "type": [ + "null", + "string" + ] + } + } + }, + "PortalNameAvailabilityDto": { + "required": [ + "name", + "available", + "reason" + ], + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "available": { + "type": "boolean" + }, + "reason": { + "oneOf": [ + { + "type": "null" + }, + { + "$ref": "#/components/schemas/PortalNameUnavailableReason" + } + ] + } + } + }, + "PortalNameDto": { + "required": [ + "name", + "assignedAt", + "nextChangeAllowedAt" + ], + "type": "object", + "properties": { + "name": { + "type": [ + "null", + "string" + ] + }, + "assignedAt": { + "type": [ + "null", + "string" + ], + "format": "date-time" + }, + "nextChangeAllowedAt": { + "type": [ + "null", + "string" + ], + "format": "date-time" + } + } + }, + "PortalNameUnavailableReason": { + "enum": [ + "invalidFormat", + "reserved", + "taken", + null + ], + "description": "Por qué el nombre de portal no está disponible. invalidFormat: no cumple el formato (largo, caracteres o guiones). reserved: es un nombre reservado. taken: otro tenant lo tiene." + }, + "PortalRecoverTicketBody": { + "required": [ + "email", + "recaptchaToken" + ], + "type": "object", + "properties": { + "email": { + "type": [ + "null", + "string" + ] + }, + "recaptchaToken": { + "type": [ + "null", + "string" + ] + } + } + }, + "PortalRecoveryDto": { + "type": "object", + "properties": { + "downloadToken": { + "type": [ + "null", + "string" + ] + }, + "errorCode": { + "type": [ + "null", + "string" + ] + }, + "field": { + "type": [ + "null", + "string" + ] + } + } + }, + "PortalResendDto": { + "type": "object", + "properties": { + "errorCode": { + "type": [ + "null", + "string" + ] + }, + "retryAfterSeconds": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "null", + "integer", + "string" + ], + "format": "int32" + } + } + }, + "PortalResendTicketBody": { + "required": [ + "downloadToken", + "recaptchaToken" + ], + "type": "object", + "properties": { + "downloadToken": { + "type": [ + "null", + "string" + ] + }, + "recaptchaToken": { + "type": [ + "null", + "string" + ] + } + } + }, + "PortalStatus": { + "enum": [ + "enabled", + "disabled" + ], + "description": "Si el portal de autofactura atiende. enabled: localiza y timbra tickets. disabled: no timbra (el comercio no tiene Auto CFDI o le falta el aviso de privacidad); la recuperación y la descarga siguen disponibles." + }, + "PortalTaxRegimeDto": { + "required": [ + "code", + "description", + "appliesToPhysicalPerson", + "appliesToLegalEntity" + ], + "type": "object", + "properties": { + "code": { + "type": "string" + }, + "description": { + "type": "string" + }, + "appliesToPhysicalPerson": { + "type": "boolean" + }, + "appliesToLegalEntity": { + "type": "boolean" + } + } + }, + "PortalTicketDto": { + "type": "object", + "properties": { + "issuer": { + "$ref": "#/components/schemas/PortalIssuerDto" + }, + "branding": { + "$ref": "#/components/schemas/PortalBrand" + }, + "folio": { + "type": "string" + }, + "series": { + "type": [ + "null", + "string" + ] + }, + "paymentDate": { + "format": "date-time" + }, + "currencyCode": { + "type": "string" + }, + "items": { + "type": "array", + "items": { + "$ref": "#/components/schemas/PortalTicketItemDto" + } + }, + "subtotal": { + "format": "double" + }, + "discount": { + "format": "double" + }, + "transferredTaxes": { + "type": "array", + "items": { + "$ref": "#/components/schemas/PortalTransferredTaxDto" + } + }, + "withheldTaxes": { + "type": "array", + "items": { + "$ref": "#/components/schemas/PortalWithheldTaxDto" + } + }, + "totalTransferredTaxes": { + "format": "double" + }, + "totalWithheldTaxes": { + "format": "double" + }, + "total": { + "format": "double" + }, + "status": { + "$ref": "#/components/schemas/TicketStatus" + }, + "selfInvoiceEligible": { + "type": "boolean" + }, + "ineligibleReason": { + "oneOf": [ + { + "type": "null" + }, + { + "$ref": "#/components/schemas/PortalIneligibleReason" + } + ] + }, + "eligibleUntil": { + "type": [ + "null", + "string" + ], + "format": "date-time" + }, + "maskedEmail": { + "type": [ + "null", + "string" + ] + }, + "privacyNoticeUrl": { + "type": [ + "null", + "string" + ] + } + } + }, + "PortalTicketItemDto": { + "type": "object", + "properties": { + "description": { + "type": "string" + }, + "quantity": { + "format": "double" + }, + "unitOfMeasurementCode": { + "type": "string" + }, + "unitPrice": { + "format": "double" + }, + "discount": { + "format": "double" + }, + "amount": { + "format": "double" + } + } + }, + "PortalTransferredTaxDto": { + "type": "object", + "properties": { + "taxCode": { + "type": "string" + }, + "taxTypeCode": { + "type": "string" + }, + "taxRate": { + "format": "double" + }, + "base": { + "format": "double" + }, + "amount": { + "format": "double" + } + } + }, + "PortalWithheldTaxDto": { + "type": "object", + "properties": { + "taxCode": { + "type": "string" + }, + "amount": { + "format": "double" + } + } + }, + "PropertyInfoRequest": { + "type": "object", + "properties": { + "number": { + "type": [ + "null", + "string" + ] + } + } + }, + "RecipientRequest": { + "type": "object", + "properties": { + "id": { + "type": [ + "null", + "string" + ] + }, + "tin": { + "type": [ + "null", + "string" + ] + }, + "legalName": { + "type": [ + "null", + "string" + ] + }, + "zipCode": { + "type": [ + "null", + "string" + ] + }, + "taxRegimeCode": { + "type": [ + "null", + "string" + ] + }, + "cfdiUseCode": { + "type": [ + "null", + "string" + ] + }, + "email": { + "type": [ + "null", + "string" + ] + }, + "countryId": { + "type": [ + "null", + "string" + ] + }, + "foreignTin": { + "type": [ + "null", + "string" + ] + }, + "employeeData": { + "oneOf": [ + { + "type": "null" + }, + { + "$ref": "#/components/schemas/EmployeeDataRequest" + } + ] + } + } + }, + "RelatedInvoiceRequest": { + "type": "object", + "properties": { + "relationshipTypeCode": { + "type": [ + "null", + "string" + ] + }, + "uuid": { + "type": [ + "null", + "string" + ] + } + } + }, + "ResendWebhookEventCommand": { + "type": "object", + "properties": { + "webhookId": { + "type": [ + "null", + "string" + ], + "description": "El webhook que recibe el reenvío; si se omite, la escucha de CLI de la persona autenticada (fiscalapi listen)." + } + }, + "additionalProperties": false + }, + "RetirementRequest": { + "type": "object", + "properties": { + "totalOneTime": { + "pattern": "^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?$", + "type": [ + "number", + "string" + ], + "format": "double" + }, + "totalInstallments": { + "pattern": "^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?$", + "type": [ + "number", + "string" + ], + "format": "double" + }, + "dailyAmount": { + "pattern": "^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?$", + "type": [ + "number", + "string" + ], + "format": "double" + }, + "accumulableIncome": { + "pattern": "^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?$", + "type": [ + "number", + "string" + ], + "format": "double" + }, + "nonAccumulableIncome": { + "pattern": "^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?$", + "type": [ + "number", + "string" + ], + "format": "double" + } + } + }, + "RunSatValidationsCommand": { + "type": "object", + "properties": { + "xml": { + "type": [ + "null", + "string" + ] + }, + "tin": { + "type": [ + "null", + "string" + ] + }, + "validationTypes": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "SelfInvoiceRecipient": { + "required": [ + "tin", + "legalName", + "taxRegimeCode", + "cfdiUseCode", + "zipCode", + "email" + ], + "type": "object", + "properties": { + "tin": { + "type": [ + "null", + "string" + ] + }, + "legalName": { + "type": [ + "null", + "string" + ] + }, + "taxRegimeCode": { + "type": [ + "null", + "string" + ] + }, + "cfdiUseCode": { + "type": [ + "null", + "string" + ] + }, + "zipCode": { + "type": [ + "null", + "string" + ] + }, + "email": { + "type": [ + "null", + "string" + ] + }, + "countryId": { + "type": [ + "null", + "string" + ] + }, + "foreignTin": { + "type": [ + "null", + "string" + ] + } + } + }, + "SendInvoiceByEmailCommand": { + "type": "object", + "properties": { + "invoiceId": { + "type": [ + "null", + "string" + ] + }, + "base64Logo": { + "type": [ + "null", + "string" + ] + }, + "bandColor": { + "type": [ + "null", + "string" + ] + }, + "fontColor": { + "type": [ + "null", + "string" + ] + }, + "toEmail": { + "type": [ + "null", + "string" + ] + } + } + }, + "SeveranceRequest": { + "type": "object", + "properties": { + "totalPaid": { + "pattern": "^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?$", + "type": [ + "number", + "string" + ], + "format": "double" + }, + "yearsOfService": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "integer", + "string" + ], + "format": "int32" + }, + "lastMonthlySalary": { + "pattern": "^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?$", + "type": [ + "number", + "string" + ], + "format": "double" + }, + "accumulableIncome": { + "pattern": "^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?$", + "type": [ + "number", + "string" + ], + "format": "double" + }, + "nonAccumulableIncome": { + "pattern": "^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?$", + "type": [ + "number", + "string" + ], + "format": "double" + } + } + }, + "SignManifestCommand": { + "type": "object", + "properties": { + "base64Cer": { + "type": [ + "null", + "string" + ] + }, + "base64Key": { + "type": [ + "null", + "string" + ] + }, + "password": { + "type": [ + "null", + "string" + ] + } + } + }, + "StatusCheckCommand": { + "type": "object", + "properties": { + "id": { + "type": [ + "null", + "string" + ] + }, + "issuerTin": { + "type": [ + "null", + "string" + ] + }, + "recipientTin": { + "type": [ + "null", + "string" + ] + }, + "invoiceTotal": { + "pattern": "^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?$", + "type": [ + "number", + "string" + ], + "format": "double" + }, + "invoiceUuid": { + "type": [ + "null", + "string" + ] + }, + "last8DigitsIssuerSignature": { + "type": [ + "null", + "string" + ] + } + } + }, + "StockOptionsRequest": { + "type": "object", + "properties": { + "marketPrice": { + "pattern": "^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?$", + "type": [ + "number", + "string" + ], + "format": "double" + }, + "grantPrice": { + "pattern": "^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?$", + "type": [ + "number", + "string" + ], + "format": "double" + } + } + }, + "StringSegment": { + "type": "object", + "properties": { + "buffer": { + "type": [ + "null", + "string" + ] + }, + "offset": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "integer", + "string" + ], + "format": "int32" + }, + "length": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "integer", + "string" + ], + "format": "int32" + }, + "value": { + "type": [ + "null", + "string" + ] + }, + "hasValue": { + "type": "boolean" + } + } + }, + "TicketActorType": { + "enum": [ + "tenant", + "portal", + "system" + ], + "description": "Quién provocó el hecho. tenant: una persona del tenant por la API o el dashboard. portal: el receptor en el portal de autofactura. system: un proceso en segundo plano de FiscalAPI." + }, + "TicketCodeDto": { + "required": [ + "ticketCode", + "qrUrl" + ], + "type": "object", + "properties": { + "ticketCode": { + "type": "string" + }, + "qrUrl": { + "type": "string" + } + } + }, + "TicketDto": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "personId": { + "type": "string" + }, + "folio": { + "type": "string" + }, + "series": { + "type": [ + "null", + "string" + ] + }, + "paymentDate": { + "format": "date-time" + }, + "paymentFormCode": { + "type": "string" + }, + "currencyCode": { + "type": "string" + }, + "exchangeRate": { + "format": "double" + }, + "expeditionZipCode": { + "type": "string" + }, + "subtotal": { + "format": "double" + }, + "totalTransferredTaxes": { + "format": "double" + }, + "totalWithheldTaxes": { + "format": "double" + }, + "total": { + "format": "double" + }, + "status": { + "$ref": "#/components/schemas/TicketStatus" + }, + "selfInvoiceEligible": { + "type": "boolean" + }, + "eligibleUntil": { + "type": [ + "null", + "string" + ], + "format": "date-time" + }, + "invoiceId": { + "type": [ + "null", + "string" + ] + }, + "globalInvoiceId": { + "type": [ + "null", + "string" + ] + }, + "createdAt": { + "type": "string", + "format": "date-time" + }, + "items": { + "type": [ + "null", + "array" + ], + "items": { + "$ref": "#/components/schemas/TicketItemDto" + } + }, + "events": { + "type": [ + "null", + "array" + ], + "items": { + "$ref": "#/components/schemas/TicketEventDto" + } + } + } + }, + "TicketEventDto": { + "type": "object", + "properties": { + "type": { + "$ref": "#/components/schemas/TicketEventType" + }, + "occurredAt": { + "type": "string", + "format": "date-time" + }, + "actorType": { + "$ref": "#/components/schemas/TicketActorType" + }, + "invoiceId": { + "type": [ + "null", + "string" + ] + } + } + }, + "TicketEventType": { + "enum": [ + "ingested", + "invoicingStarted", + "invoiced", + "invoiceRejected", + "outcomeUnknown", + "leaseExpired", + "globalizing", + "globalized", + "releasedFromGlobal", + "cancelled", + "recoveryFailed", + "recovered", + "resent", + "returnedToPending", + "leaseUnresolved", + "cancellationRequested", + "substituted", + "ownerNotified" + ], + "description": "Hecho de la historia del ticket. ingested: alta. invoicingStarted: el receptor empezó a facturarlo. invoiced: quedó timbrada su factura individual. invoiceRejected: el PAC rechazó el CFDI o no hubo saldo; vuelve a su estado anterior. outcomeUnknown: no se conoce el resultado del timbrado; queda para atención manual. leaseExpired: venció la reserva sin CFDI; vuelve a su estado anterior. globalizing: una global lo está timbrando. globalized: quedó en la global. releasedFromGlobal: se canceló su global y quedó libre. cancelled: el tenant lo canceló. recoveryFailed: un intento fallido de recuperar la factura en el portal. recovered: el receptor recuperó su factura. resent: se reenvió la factura por correo. returnedToPending: se canceló su factura individual y volvió a pending. leaseUnresolved: la reserva venció y no se puede resolver sin riesgo de un segundo CFDI; queda para atención manual. cancellationRequested: el PAC aceptó la solicitud de cancelación de su factura. substituted: quedó ligado a la factura que sustituye a la cancelada. ownerNotified: se avisó al owner que una autofactura se bloqueó por una causa del comercio." + }, + "TicketIngestedDto": { + "required": [ + "id", + "ticketCode", + "qrUrl", + "status", + "selfInvoiceEligible", + "eligibleUntil", + "subtotal", + "totalTransferredTaxes", + "totalWithheldTaxes", + "total" + ], + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "ticketCode": { + "type": "string" + }, + "qrUrl": { + "type": "string" + }, + "status": { + "$ref": "#/components/schemas/TicketStatus" + }, + "selfInvoiceEligible": { + "type": "boolean" + }, + "eligibleUntil": { + "type": [ + "null", + "string" + ], + "format": "date-time" + }, + "subtotal": { + "pattern": "^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?$", + "type": [ + "number", + "string" + ], + "format": "double" + }, + "totalTransferredTaxes": { + "pattern": "^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?$", + "type": [ + "number", + "string" + ], + "format": "double" + }, + "totalWithheldTaxes": { + "pattern": "^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?$", + "type": [ + "number", + "string" + ], + "format": "double" + }, + "total": { + "pattern": "^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?$", + "type": [ + "number", + "string" + ], + "format": "double" + } + } + }, + "TicketIssuerRequest": { + "type": "object", + "properties": { + "id": { + "type": [ + "null", + "string" + ] + } + } + }, + "TicketItemDto": { + "type": "object", + "properties": { + "itemCode": { + "type": "string" + }, + "itemSku": { + "type": [ + "null", + "string" + ] + }, + "quantity": { + "format": "double" + }, + "unitOfMeasurementCode": { + "type": "string" + }, + "description": { + "type": "string" + }, + "unitPrice": { + "format": "double" + }, + "discount": { + "format": "double" + }, + "taxObjectCode": { + "type": "string" + }, + "itemTaxes": { + "type": "array", + "items": { + "$ref": "#/components/schemas/TicketItemTaxDto" + } + } + } + }, + "TicketItemRequest": { + "type": "object", + "properties": { + "itemCode": { + "type": [ + "null", + "string" + ] + }, + "itemSku": { + "type": [ + "null", + "string" + ] + }, + "quantity": { + "pattern": "^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?$", + "type": [ + "number", + "string" + ], + "format": "double" + }, + "unitOfMeasurementCode": { + "type": [ + "null", + "string" + ] + }, + "description": { + "type": [ + "null", + "string" + ] + }, + "unitPrice": { + "pattern": "^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?$", + "type": [ + "number", + "string" + ], + "format": "double" + }, + "discount": { + "pattern": "^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?$", + "type": [ + "number", + "string" + ], + "format": "double" + }, + "taxObjectCode": { + "type": [ + "null", + "string" + ] + }, + "itemTaxes": { + "type": [ + "null", + "array" + ], + "items": { + "$ref": "#/components/schemas/ItemTaxRequest" + } + } + } + }, + "TicketItemTaxDto": { + "type": "object", + "properties": { + "taxCode": { + "type": "string" + }, + "taxTypeCode": { + "type": "string" + }, + "taxRate": { + "format": "double" + }, + "taxFlagCode": { + "type": "string" + } + } + }, + "TicketStatus": { + "enum": [ + "pending", + "invoicing", + "invoiced", + "globalizing", + "globalized", + "cancelled" + ], + "description": "Estado del ticket. pending: sin factura; el receptor lo puede autofacturar o el tenant incluirlo en una global. invoicing: el receptor lo está timbrando. invoiced: tiene su factura individual (invoiceId). globalizing: una factura global lo está timbrando. globalized: está en una factura global (globalInvoiceId). cancelled: el tenant lo canceló antes de facturarlo; es final y libera la serie y el folio." + }, + "TransferStampsCommand": { + "type": "object", + "properties": { + "fromPersonId": { + "type": [ + "null", + "string" + ] + }, + "toPersonId": { + "type": [ + "null", + "string" + ] + }, + "amount": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "integer", + "string" + ], + "format": "int32" + }, + "comments": { + "type": [ + "null", + "string" + ] + }, + "creditType": { + "$ref": "#/components/schemas/CreditTypeId" + } + } + }, + "TriggerWebhookEventCommand": { + "type": "object", + "properties": { + "type": { + "type": [ + "null", + "string" + ], + "description": "El tipo de evento, uno de los que se suscriben (p. ej. invoice.canceled)." + }, + "relatedObjectId": { + "type": [ + "null", + "string" + ], + "description": "El id del recurso del evento (data.relatedObject.id); si se omite, un id sintético cuyo GET responde 404." + }, + "personId": { + "type": [ + "null", + "string" + ], + "description": "La persona del tenant dueña del recurso (personId del evento); si se omite, el evento va sin persona." + }, + "webhookId": { + "type": [ + "null", + "string" + ], + "description": "El webhook que recibe el evento; si se omite, la escucha de CLI de la persona autenticada (fiscalapi listen)." + } + }, + "additionalProperties": false + }, + "UpdateApiKeyCommand": { + "type": "object", + "properties": { + "id": { + "type": [ + "null", + "string" + ] + }, + "description": { + "type": [ + "null", + "string" + ] + }, + "apiKeyStatus": { + "oneOf": [ + { + "type": "null" + }, + { + "$ref": "#/components/schemas/ApiKeyStatus" + } + ] + } + } + }, + "UpdateAutoInvoiceSettingCommand": { + "type": "object", + "properties": { + "allowBillingOutsideMonth": { + "type": "boolean" + }, + "allowBillingOutsideYear": { + "type": "boolean" + }, + "privacyNoticeUrl": { + "type": [ + "null", + "string" + ] + } + } + }, + "UpdateCommand": { + "type": "object", + "properties": { + "id": { + "type": [ + "null", + "string" + ] + }, + "description": { + "type": [ + "null", + "string" + ] + }, + "unitPrice": { + "pattern": "^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?$", + "type": [ + "number", + "string" + ], + "format": "double" + }, + "satUnitMeasurementId": { + "type": [ + "null", + "string" + ] + }, + "satTaxObjectId": { + "type": [ + "null", + "string" + ] + }, + "satProductCodeId": { + "type": [ + "null", + "string" + ] + }, + "productTaxes": { + "type": [ + "null", + "array" + ], + "items": { + "$ref": "#/components/schemas/UpdateCommand" + } + } + } + }, + "UpdateDownloadRuleCommand": { + "type": "object", + "properties": { + "id": { + "type": [ + "null", + "string" + ] + }, + "description": { + "type": [ + "null", + "string" + ] + } + } + }, + "UpdateEmployeeDataCommand": { + "type": "object", + "properties": { + "employerPersonId": { + "type": "string" + }, + "employeePersonId": { + "type": "string" + }, + "socialSecurityNumber": { + "type": [ + "null", + "string" + ] + }, + "laborRelationStartDate": { + "type": [ + "null", + "string" + ], + "format": "date-time" + }, + "seniority": { + "type": [ + "null", + "string" + ] + }, + "satContractTypeId": { + "type": "string" + }, + "satUnionizedStatusId": { + "type": [ + "null", + "string" + ] + }, + "satTaxRegimeTypeId": { + "type": "string" + }, + "satWorkdayTypeId": { + "type": [ + "null", + "string" + ] + }, + "satJobRiskId": { + "type": [ + "null", + "string" + ] + }, + "satPaymentPeriodicityId": { + "type": "string" + }, + "employeeNumber": { + "type": "string" + }, + "satBankId": { + "type": [ + "null", + "string" + ] + }, + "satPayrollStateId": { + "type": "string" + }, + "department": { + "type": [ + "null", + "string" + ] + }, + "position": { + "type": [ + "null", + "string" + ] + }, + "bankAccount": { + "type": [ + "null", + "string" + ] + }, + "baseSalaryForContributions": { + "pattern": "^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?$", + "type": [ + "null", + "number", + "string" + ], + "format": "double" + }, + "integratedDailySalary": { + "pattern": "^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?$", + "type": [ + "null", + "number", + "string" + ], + "format": "double" + }, + "subcontractorRfc": { + "type": [ + "null", + "string" + ] + }, + "timePercentage": { + "pattern": "^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?$", + "type": [ + "null", + "number", + "string" + ], + "format": "double" + } + } + }, + "UpdateEmployerDataCommand": { + "type": "object", + "properties": { + "personId": { + "type": [ + "null", + "string" + ] + }, + "employerRegistration": { + "type": [ + "null", + "string" + ] + }, + "originEmployerTin": { + "type": [ + "null", + "string" + ] + }, + "satFundSourceId": { + "type": [ + "null", + "string" + ] + }, + "ownResourceAmount": { + "pattern": "^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?$", + "type": [ + "null", + "number", + "string" + ], + "format": "double" + } + } + }, + "UpdatePortalNameCommand": { + "type": "object", + "properties": { + "name": { + "type": [ + "null", + "string" + ] + } + } + }, + "UpdateUserCommand": { + "type": "object", + "properties": { + "id": { + "type": [ + "null", + "string" + ] + }, + "legalName": { + "type": [ + "null", + "string" + ] + }, + "email": { + "type": [ + "null", + "string" + ] + }, + "phoneNumber": { + "type": [ + "null", + "string" + ] + }, + "password": { + "type": [ + "null", + "string" + ] + }, + "satTaxRegimeId": { + "type": [ + "null", + "string" + ] + }, + "satCfdiUseId": { + "type": [ + "null", + "string" + ] + }, + "userTypeId": { + "type": [ + "null", + "string" + ] + }, + "tin": { + "type": [ + "null", + "string" + ] + }, + "zipCode": { + "type": [ + "null", + "string" + ] + }, + "base64Photo": { + "type": [ + "null", + "string" + ] + }, + "taxPassword": { + "type": [ + "null", + "string" + ] + }, + "curp": { + "type": [ + "null", + "string" + ] + }, + "countryId": { + "type": [ + "null", + "string" + ] + }, + "foreignTin": { + "type": [ + "null", + "string" + ] + } + } + }, + "UpdateWebhookCommand": { + "type": "object", + "properties": { + "id": { + "type": [ + "null", + "string" + ], + "description": "Id del webhook; debe ser el de la ruta." + }, + "url": { + "type": [ + "null", + "string" + ], + "description": "URL https absoluta que recibe las entregas, sin usuario ni contraseña." + }, + "description": { + "type": [ + "null", + "string" + ], + "description": "Texto libre del tenant, hasta 256 caracteres." + }, + "enabledEvents": { + "type": [ + "null", + "array" + ], + "items": { + "type": "string" + }, + "description": "Tipos de evento suscritos, o solo \"*\" para recibirlos todos." + }, + "personIds": { + "type": [ + "null", + "array" + ], + "items": { + "type": "string" + }, + "description": "Personas del tenant cuyos eventos recibe; sin ellas (o vacío), las de todas." + }, + "status": { + "oneOf": [ + { + "type": "null" + }, + { + "description": "enabled o disabled; omitido, conserva el estado. Habilitar uno deshabilitado limpia failingSince, disabledReason y disabledAt; deshabilitar cierra sus entregas pendientes.", + "$ref": "#/components/schemas/WebhookStatus" + } + ] + } + } + }, + "WebhookDeliveryDto": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "eventId": { + "type": "string" + }, + "eventType": { + "type": "string" + }, + "kind": { + "$ref": "#/components/schemas/WebhookDeliveryKind" + }, + "status": { + "$ref": "#/components/schemas/WebhookDeliveryStatus" + }, + "attempts": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "integer", + "string" + ], + "format": "int32" + }, + "nextAttemptAt": { + "type": "string", + "format": "date-time" + }, + "lastAttemptAt": { + "type": [ + "null", + "string" + ], + "format": "date-time" + }, + "lastStatusCode": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "null", + "integer", + "string" + ], + "format": "int32" + }, + "lastLatencyMs": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "null", + "integer", + "string" + ], + "format": "int32" + }, + "lastError": { + "type": [ + "null", + "string" + ] + }, + "lastResponseExcerpt": { + "type": [ + "null", + "string" + ] + }, + "createdAt": { + "type": "string", + "format": "date-time" + } + } + }, + "WebhookDeliveryKind": { + "enum": [ + "automatic", + "manual", + "test" + ] + }, + "WebhookDeliveryStatus": { + "enum": [ + "pending", + "succeeded", + "failed" + ] + }, + "WebhookDisabledReason": { + "enum": [ + "manual", + "gone", + "failingTooLong", + null + ] + }, + "WebhookDto": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "kind": { + "$ref": "#/components/schemas/WebhookKind" + }, + "url": { + "type": [ + "null", + "string" + ] + }, + "description": { + "type": "string" + }, + "enabledEvents": { + "type": "array", + "items": { + "type": "string" + } + }, + "personIds": { + "type": [ + "null", + "array" + ], + "items": { + "type": "string" + } + }, + "status": { + "$ref": "#/components/schemas/WebhookStatus" + }, + "disabledReason": { + "oneOf": [ + { + "type": "null" + }, + { + "$ref": "#/components/schemas/WebhookDisabledReason" + } + ] + }, + "disabledAt": { + "type": [ + "null", + "string" + ], + "format": "date-time" + }, + "failingSince": { + "type": [ + "null", + "string" + ], + "format": "date-time" + }, + "listenerPersonId": { + "type": [ + "null", + "string" + ] + }, + "sessionExpiresAt": { + "type": [ + "null", + "string" + ], + "format": "date-time" + }, + "createdAt": { + "type": "string", + "format": "date-time" + }, + "updatedAt": { + "type": [ + "null", + "string" + ], + "format": "date-time" + } + } + }, + "WebhookEventResendDto": { + "required": [ + "deliveryId", + "webhookId" + ], + "type": "object", + "properties": { + "deliveryId": { + "type": "string" + }, + "webhookId": { + "type": "string" + } + } + }, + "WebhookEventTriggerDto": { + "required": [ + "eventId", + "webhookId" + ], + "type": "object", + "properties": { + "eventId": { + "type": "string" + }, + "webhookId": { + "type": "string" + } + } + }, + "WebhookKind": { + "enum": [ + "url", + "cliListener" + ] + }, + "WebhookStatus": { + "enum": [ + "enabled", + "disabled" + ] + }, + "WebhookTestDto": { + "required": [ + "eventId", + "deliveryId" + ], + "type": "object", + "properties": { + "eventId": { + "type": "string" + }, + "deliveryId": { + "type": "string" + } + } + }, + "WebhookWithSecretDto": { + "type": "object", + "properties": { + "secret": { + "type": [ + "null", + "string" + ] + }, + "id": { + "type": "string" + }, + "kind": { + "$ref": "#/components/schemas/WebhookKind" + }, + "url": { + "type": [ + "null", + "string" + ] + }, + "description": { + "type": "string" + }, + "enabledEvents": { + "type": "array", + "items": { + "type": "string" + } + }, + "personIds": { + "type": [ + "null", + "array" + ], + "items": { + "type": "string" + } + }, + "status": { + "$ref": "#/components/schemas/WebhookStatus" + }, + "disabledReason": { + "oneOf": [ + { + "type": "null" + }, + { + "$ref": "#/components/schemas/WebhookDisabledReason" + } + ] + }, + "disabledAt": { + "type": [ + "null", + "string" + ], + "format": "date-time" + }, + "failingSince": { + "type": [ + "null", + "string" + ], + "format": "date-time" + }, + "listenerPersonId": { + "type": [ + "null", + "string" + ] + }, + "sessionExpiresAt": { + "type": [ + "null", + "string" + ], + "format": "date-time" + }, + "createdAt": { + "type": "string", + "format": "date-time" + }, + "updatedAt": { + "type": [ + "null", + "string" + ], + "format": "date-time" + } + } + } + }, + "securitySchemes": { + "ApiKey": { + "type": "apiKey", + "name": "X-API-KEY", + "in": "header" + }, + "TenantKey": { + "type": "apiKey", + "name": "X-TENANT-KEY", + "in": "header" + } + } + }, + "tags": [ + { + "name": "ApiKeys" + }, + { + "name": "AutoInvoice" + }, + { + "name": "Catalogs" + }, + { + "name": "DownloadCatalogs" + }, + { + "name": "DownloadRequests" + }, + { + "name": "DownloadRules" + }, + { + "name": "Events" + }, + { + "name": "Invoices" + }, + { + "name": "Manifests" + }, + { + "name": "People" + }, + { + "name": "Portal" + }, + { + "name": "Products" + }, + { + "name": "SatValidations" + }, + { + "name": "Stamps" + }, + { + "name": "TaxFiles" + }, + { + "name": "Tickets" + }, + { + "name": "Webhooks" + } + ] +} \ No newline at end of file diff --git a/mcp/test/global-setup.ts b/mcp/test/global-setup.ts new file mode 100644 index 0000000..8442296 --- /dev/null +++ b/mcp/test/global-setup.ts @@ -0,0 +1,32 @@ +import { spawnSync } from 'node:child_process'; +import { mkdtempSync, rmSync } from 'node:fs'; +import { tmpdir } from 'node:os'; +import path from 'node:path'; +import { fileURLToPath, pathToFileURL } from 'node:url'; + +import type { TestProject } from 'vitest/node'; + +export const packageDir = fileURLToPath(new URL('../', import.meta.url)); +export const fixturePath = path.join(packageDir, 'test', 'fixtures', 'openapi-v4.json'); + +// Los datos de prueba van a un directorio temporal: npm test nunca pisa el data/ que se publica. +export default function setup(project: TestProject): () => void { + const workDir = mkdtempSync(path.join(tmpdir(), 'fiscalapi-mcp-')); + const dataDir = path.join(workDir, 'data'); + const build = spawnSync(process.execPath, ['scripts/build-data.mjs', fixturePath, '--out', dataDir], { + cwd: packageDir, + encoding: 'utf8', + }); + if (build.status !== 0) { + rmSync(workDir, { recursive: true, force: true }); + throw new Error(`build-data falló con el fixture:\n${build.stderr}`); + } + project.provide('dataDir', pathToFileURL(dataDir + path.sep).href); + return () => rmSync(workDir, { recursive: true, force: true }); +} + +declare module 'vitest' { + export interface ProvidedContext { + dataDir: string; + } +} diff --git a/mcp/test/helpers.ts b/mcp/test/helpers.ts new file mode 100644 index 0000000..d17b0dd --- /dev/null +++ b/mcp/test/helpers.ts @@ -0,0 +1,36 @@ +import { Client, InMemoryTransport } from '@modelcontextprotocol/client'; +import type { CallToolResult } from '@modelcontextprotocol/client'; +import { inject } from 'vitest'; + +import { OperationCatalog } from '../src/catalog.js'; +import { createServer } from '../src/server.js'; + +export function dataDirUrl(): URL { + return new URL(inject('dataDir')); +} + +/** Cliente MCP en proceso conectado al servidor por el transporte en memoria del SDK. */ +export async function connectClient(): Promise<{ client: Client; close: () => Promise }> { + const server = createServer(OperationCatalog.load(dataDirUrl()), '0.0.0-test'); + const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair(); + const client = new Client({ name: 'fiscalapi-mcp-test', version: '1.0.0' }); + await server.connect(serverTransport); + await client.connect(clientTransport); + return { + client, + close: async () => { + await client.close(); + await server.close(); + }, + }; +} + +export async function callTool(client: Client, name: string, args: Record = {}): Promise { + return (await client.callTool({ name, arguments: args })) as CallToolResult; +} + +export function textOf(result: CallToolResult): string { + const [block] = result.content; + if (block?.type !== 'text') throw new Error('Se esperaba un bloque de texto.'); + return block.text; +} diff --git a/mcp/test/offline.test.ts b/mcp/test/offline.test.ts new file mode 100644 index 0000000..4a64c8b --- /dev/null +++ b/mcp/test/offline.test.ts @@ -0,0 +1,26 @@ +import { afterEach, expect, it, vi } from 'vitest'; + +import { callTool, connectClient, textOf } from './helpers.js'; + +afterEach(() => { + vi.unstubAllGlobals(); +}); + +it('arranca y responde sin red: nunca llama a fetch', async () => { + const fetch = vi.fn(() => { + throw new Error('Sin red: el MCP no debe descargar nada'); + }); + vi.stubGlobal('fetch', fetch); + + const { client, close } = await connectClient(); + try { + const { tools } = await client.listTools(); + const operation = await callTool(client, 'obtener_operacion', { operationId: 'createInvoice' }); + + expect(tools).toHaveLength(2); + expect(JSON.parse(textOf(operation)).operationId).toBe('createInvoice'); + expect(fetch).not.toHaveBeenCalled(); + } finally { + await close(); + } +}); diff --git a/mcp/test/tools.test.ts b/mcp/test/tools.test.ts new file mode 100644 index 0000000..dd6c21f --- /dev/null +++ b/mcp/test/tools.test.ts @@ -0,0 +1,138 @@ +import { readFileSync } from 'node:fs'; + +import type { Client } from '@modelcontextprotocol/client'; +import { afterAll, beforeAll, describe, expect, it } from 'vitest'; + +import { callTool, connectClient, dataDirUrl, textOf } from './helpers.js'; + +// Límites duros de RNF-05: unos 3k tokens para el catálogo y unos 10k para una operación. +const MAX_CATALOG_CHARS = 12_000; +const MAX_OPERATION_CHARS = 40_000; +const OPERATION_LINE = /^\w+ {2}(GET|POST|PUT|PATCH|DELETE) \/\S* {2}.+$/; + +interface CatalogEntry { + id: string; + tag: string; +} + +const catalog = JSON.parse(readFileSync(new URL('catalog.json', dataDirUrl()), 'utf8')) as CatalogEntry[]; + +let client: Client; +let close: () => Promise; + +beforeAll(async () => { + ({ client, close } = await connectClient()); +}); + +afterAll(async () => { + await close(); +}); + +function operationLines(text: string): string[] { + return text.split('\n').filter((line) => OPERATION_LINE.test(line)); +} + +describe('tools/list', () => { + it('expone exactamente listar_operaciones y obtener_operacion con descripciones en español', async () => { + const { tools } = await client.listTools(); + + expect(tools.map((tool) => tool.name).sort()).toEqual(['listar_operaciones', 'obtener_operacion']); + const byName = Object.fromEntries(tools.map((tool) => [tool.name, tool])); + expect(byName.listar_operaciones?.description).toContain('el siguiente paso es llamar a obtener_operacion'); + expect(byName.obtener_operacion?.description).toContain('Usa el operationId exacto que da listar_operaciones'); + expect(byName.obtener_operacion?.inputSchema.required).toEqual(['operationId']); + for (const tool of tools) { + expect(tool.annotations?.readOnlyHint).toBe(true); + expect(tool.description).not.toMatch(/—/); + } + }); +}); + +describe('listar_operaciones', () => { + it('sin tag lista las 100 operaciones agrupadas por tag dentro del límite de tamaño', async () => { + const result = await callTool(client, 'listar_operaciones'); + const text = textOf(result); + + expect(result.isError).toBeFalsy(); + expect(operationLines(text)).toHaveLength(100); + expect(text).toContain('createInvoice POST /api/v4/invoices Emitir (timbrar) una factura CFDI 4.0'); + expect(text).toContain('## Invoices'); + expect(text.length).toBeLessThanOrEqual(MAX_CATALOG_CHARS); + }); + + it('con tag filtra sin distinguir mayúsculas', async () => { + const invoices = catalog.filter((entry) => entry.tag === 'Invoices').map((entry) => entry.id); + + const text = textOf(await callTool(client, 'listar_operaciones', { tag: 'invoices' })); + + const ids = operationLines(text).map((line) => line.split(' ')[0]); + expect(ids).toEqual(invoices); + expect(text).not.toContain('## People'); + }); + + it('con un tag inexistente devuelve error con los tags disponibles', async () => { + const result = await callTool(client, 'listar_operaciones', { tag: 'Facturas' }); + + expect(result.isError).toBe(true); + expect(textOf(result)).toContain('No hay operaciones con el tag "Facturas"'); + expect(textOf(result)).toContain('Invoices'); + }); +}); + +describe('obtener_operacion', () => { + it('devuelve createInvoice con su cuerpo resuelto y dentro del límite de tamaño', async () => { + const result = await callTool(client, 'obtener_operacion', { operationId: 'createInvoice' }); + const text = textOf(result); + const operation = JSON.parse(text); + + expect(result.isError).toBeFalsy(); + expect(operation).toMatchObject({ operationId: 'createInvoice', method: 'POST', path: '/api/v4/invoices' }); + expect(operation.description).toContain('https://docs.fiscalapi.com/'); + const body = operation.requestBody.content['application/json'].schema; + expect(body.properties.items.items.properties).toHaveProperty('itemCode'); + expect(text).not.toContain('"$ref"'); + expect(text.length).toBeLessThanOrEqual(MAX_OPERATION_CHARS); + }); + + it.each(catalog.map((entry) => entry.id))('%s cabe en el límite y no deja $ref sin resolver', async (operationId) => { + const result = await callTool(client, 'obtener_operacion', { operationId }); + const text = textOf(result); + + expect(result.isError).toBeFalsy(); + expect(JSON.parse(text).operationId).toBe(operationId); + expect(text).not.toContain('"$ref"'); + expect(text.length).toBeLessThanOrEqual(MAX_OPERATION_CHARS); + }); + + it('con un id mal escrito devuelve error y sugiere el correcto', async () => { + const result = await callTool(client, 'obtener_operacion', { operationId: 'createInvoce' }); + + expect(result.isError).toBe(true); + expect(textOf(result)).toContain('No existe la operación "createInvoce"'); + expect(textOf(result)).toContain('¿Quisiste decir createInvoice?'); + }); + + it('sin parecido suficiente no sugiere nada y remite a listar_operaciones', async () => { + const result = await callTool(client, 'obtener_operacion', { operationId: 'xyz' }); + + expect(result.isError).toBe(true); + expect(textOf(result)).toBe('No existe la operación "xyz". Usa listar_operaciones para ver los operationId disponibles.'); + }); + + it('sugiere primero los ids que empiezan con lo enviado y luego los que lo contienen', async () => { + const result = await callTool(client, 'obtener_operacion', { operationId: 'getinvoice' }); + + expect(result.isError).toBe(true); + expect(textOf(result)).toContain('¿Quisiste decir getInvoice, getInvoicePdf, getInvoiceStatus, getInvoiceXml?'); + }); + + it.each(['../package', '../catalog', '..\\catalog', '/etc/passwd'])('rechaza %s sin leer archivos fuera de data/operations', async (operationId) => { + const result = await callTool(client, 'obtener_operacion', { operationId }); + const text = textOf(result); + + expect(result.isError).toBe(true); + expect(text).toContain('No existe la operación'); + expect(text).not.toContain('"summary"'); + expect(text).not.toContain('@fiscalapi/mcp'); + }); +}); diff --git a/mcp/tsconfig.json b/mcp/tsconfig.json new file mode 100644 index 0000000..eaf00e3 --- /dev/null +++ b/mcp/tsconfig.json @@ -0,0 +1,17 @@ +{ + "compilerOptions": { + "target": "ES2023", + "lib": ["ES2023"], + "module": "NodeNext", + "moduleResolution": "NodeNext", + "types": ["node"], + "strict": true, + "noUncheckedIndexedAccess": true, + "noImplicitOverride": true, + "verbatimModuleSyntax": true, + "skipLibCheck": true, + "rootDir": "src", + "outDir": "dist" + }, + "include": ["src"] +} diff --git a/mcp/vitest.config.ts b/mcp/vitest.config.ts new file mode 100644 index 0000000..17cae7d --- /dev/null +++ b/mcp/vitest.config.ts @@ -0,0 +1,8 @@ +import { defineConfig } from 'vitest/config'; + +export default defineConfig({ + test: { + include: ['test/**/*.test.ts'], + globalSetup: ['test/global-setup.ts'], + }, +}); From 0b9eab23d282cbfe605ff8722e52a8610f584478 Mon Sep 17 00:00:00 2001 From: mendozagit Date: Thu, 15 Oct 2026 14:13:00 -0600 Subject: [PATCH 41/44] Tablero: MCP-01 con su hash --- docs/ai-integracion/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/ai-integracion/README.md b/docs/ai-integracion/README.md index f30617a..a2d8aa9 100644 --- a/docs/ai-integracion/README.md +++ b/docs/ai-integracion/README.md @@ -54,7 +54,7 @@ La siguiente tarea (global o de un área) es la primera fila `pendiente` (de esa | SK-07 | Escribir `fiscalapi-nomina` | skills | SK-02, MED-01 | hecho | fiscalapi-ai@51cbb20 | | SK-08 | Escribir `fiscalapi-carta-porte` | skills | SK-02, MED-01 | hecho | fiscalapi-ai@9849def | | SK-09 | Escribir `fiscalapi-comercio-exterior` | skills | SK-02, MED-01 | hecho | fiscalapi-ai@8cb8c77 | -| MCP-01 | Crear el paquete stdio con `listar_operaciones` y `obtener_operacion` | mcp | SK-01, BE-03 | hecho | | +| MCP-01 | Crear el paquete stdio con `listar_operaciones` y `obtener_operacion` | mcp | SK-01, BE-03 | hecho | fiscalapi-ai@6e0c755 | | MCP-02 | Agregar `buscar_docs` con índice en español | mcp | MCP-01, DOC-01 | pendiente | | | EV-01 | Montar el harness de evals con los casos de ingreso y pagos | evals | SK-05, MCP-02 | pendiente | | | EV-02 | Agregar casos de nómina, carta porte y comercio exterior | evals | EV-01, SK-09 | pendiente | | From 3594080286273bf21b84ad025d40014a591f6141 Mon Sep 17 00:00:00 2001 From: mendozagit Date: Thu, 15 Oct 2026 14:57:21 -0600 Subject: [PATCH 42/44] =?UTF-8?q?MCP-02:=20buscar=5Fdocs=20con=20=C3=ADndi?= =?UTF-8?q?ce=20MiniSearch=20en=20espa=C3=B1ol=20sobre=20las=20secciones?= =?UTF-8?q?=20de=20las=20docs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - build-data descarga llms.txt y las .md por sección (--docs-origin / FISCALAPI_DOCS_ORIGIN), limpia el MDX (sin código ni etiquetas; conserva name, value y tablas) e indexa 383 documentos. - processTerm único en src/docs-search.ts (sin acentos, stopwords y stemmer de @orama), compartido por el build y el runtime; sin prefijo y fuzzy solo en términos de 5 letras o más. - buscar_docs(consulta, limite 1..10): título, página, URL .md y extracto de ≤300 palabras anclado en la primera coincidencia exacta. - Regresión 15/16 en el top 3 (caso conocido: "buscar clave de producto sat"); 166 pruebas en verde. - npm pack: 342.5 kB, 1.8 MB desempaquetado; arranque de 0.6 s; probado por stdio en Claude Code. --- docs/ai-integracion/README.md | 2 +- docs/ai-integracion/mcp.md | 4 +- mcp/README.md | 23 +++- mcp/package-lock.json | 27 ++++ mcp/package.json | 5 +- mcp/scripts/build-data.mjs | 77 ++++++++--- mcp/scripts/docs.mjs | 169 ++++++++++++++++++++++++ mcp/scripts/snapshot-docs.mjs | 30 +++++ mcp/src/docs-search.ts | 48 +++++++ mcp/src/docs.ts | 109 ++++++++++++++++ mcp/src/index.ts | 8 +- mcp/src/server.ts | 57 +++++++- mcp/test/build-data.test.ts | 181 ++++++++++++++++++++++++-- mcp/test/docs-server.ts | 40 ++++++ mcp/test/docs-tool.test.ts | 106 +++++++++++++++ mcp/test/fixtures/docs-corpus.json.gz | Bin 0 -> 763984 bytes mcp/test/global-setup.ts | 20 ++- mcp/test/helpers.ts | 3 +- mcp/test/offline.test.ts | 7 +- mcp/test/run-build-data.ts | 18 +++ mcp/test/search-regression.test.ts | 56 ++++++++ mcp/test/tools.test.ts | 4 +- mcp/tsconfig.json | 1 + 23 files changed, 939 insertions(+), 56 deletions(-) create mode 100644 mcp/scripts/docs.mjs create mode 100644 mcp/scripts/snapshot-docs.mjs create mode 100644 mcp/src/docs-search.ts create mode 100644 mcp/src/docs.ts create mode 100644 mcp/test/docs-server.ts create mode 100644 mcp/test/docs-tool.test.ts create mode 100644 mcp/test/fixtures/docs-corpus.json.gz create mode 100644 mcp/test/run-build-data.ts create mode 100644 mcp/test/search-regression.test.ts diff --git a/docs/ai-integracion/README.md b/docs/ai-integracion/README.md index a2d8aa9..00fa320 100644 --- a/docs/ai-integracion/README.md +++ b/docs/ai-integracion/README.md @@ -55,7 +55,7 @@ La siguiente tarea (global o de un área) es la primera fila `pendiente` (de esa | SK-08 | Escribir `fiscalapi-carta-porte` | skills | SK-02, MED-01 | hecho | fiscalapi-ai@9849def | | SK-09 | Escribir `fiscalapi-comercio-exterior` | skills | SK-02, MED-01 | hecho | fiscalapi-ai@8cb8c77 | | MCP-01 | Crear el paquete stdio con `listar_operaciones` y `obtener_operacion` | mcp | SK-01, BE-03 | hecho | fiscalapi-ai@6e0c755 | -| MCP-02 | Agregar `buscar_docs` con índice en español | mcp | MCP-01, DOC-01 | pendiente | | +| MCP-02 | Agregar `buscar_docs` con índice en español | mcp | MCP-01, DOC-01 | hecho | | | EV-01 | Montar el harness de evals con los casos de ingreso y pagos | evals | SK-05, MCP-02 | pendiente | | | EV-02 | Agregar casos de nómina, carta porte y comercio exterior | evals | EV-01, SK-09 | pendiente | | | EV-03 | Correr las 3 variantes y reportar | evals | EV-02 | pendiente | | diff --git a/docs/ai-integracion/mcp.md b/docs/ai-integracion/mcp.md index 0702008..01e396e 100644 --- a/docs/ai-integracion/mcp.md +++ b/docs/ai-integracion/mcp.md @@ -58,7 +58,9 @@ Antes de escribir código contra el SDK de MCP, se consulta su documentación vi - El paquete completo pesa menos de 5 MB → verificar: `npm pack --dry-run`. **Pruebas focalizadas:** sí. `npm test` en `mcp/`. **Fuera de alcance:** embeddings (D-08). -**Notas:** +**Notas:** regresión 15/16 en el top 3, con las 16 consultas recuperadas de la sesión de planeación; caso conocido "buscar clave de producto sat" (sale primero la página "Catálogos SAT"). "lista negra 69b" ya pasa, y "estatus ante el sat" acepta `sat-validations/validar` (criterio `sat.cfdi.status`); ambas se asumieron y quedan documentadas en `test/search-regression.test.ts`. +Sin prefijo, porque "sat" coincidía con cada `SatXxx` de los catálogos de Carta Porte; fuzzy solo para términos de 5 letras o más. El extracto se ancla en la primera coincidencia exacta, así "CFDI40147" muestra su fila de la tabla. Paquete de 342.5 kB (1.8 MB desempaquetado) y arranque de 0.6 s. +El fixture `docs-corpus.json.gz` sale del build local de fapi-docs@73d326f, porque live todavía da 404 en las `.md` por sección. Después del deploy se regenera con `scripts/snapshot-docs.mjs`, y MCP-03 genera `data/` desde docs.fiscalapi.com. ### MCP-03 — Agregar el workflow de build y publicación por hash **Objetivo:** que el paquete se regenere solo cuando cambian el OpenAPI o las docs, sin que nadie lo mantenga. diff --git a/mcp/README.md b/mcp/README.md index b0a9f9c..e1531a0 100644 --- a/mcp/README.md +++ b/mcp/README.md @@ -1,8 +1,8 @@ # @fiscalapi/mcp -Servidor MCP de solo lectura para integrar [FiscalAPI](https://docs.fiscalapi.com) (CFDI 4.0) desde tu agente de código. Le da al agente el catálogo de operaciones de la API y el esquema completo de cada una, para que escriba la integración sin adivinar rutas ni campos. +Servidor MCP de solo lectura para integrar [FiscalAPI](https://docs.fiscalapi.com) (CFDI 4.0) desde tu agente de código. Le da al agente el catálogo de operaciones de la API, el esquema completo de cada una y una búsqueda en la documentación, para que escriba la integración sin adivinar rutas, campos ni reglas fiscales. -No usa red ni llaves: el OpenAPI de FiscalAPI viene dentro del paquete y el servidor solo lee esos archivos. Nunca llama a la API. +No usa red ni llaves: el OpenAPI y un índice de la documentación vienen dentro del paquete y el servidor solo lee esos archivos. Nunca llama a la API. ## Instalación @@ -26,14 +26,15 @@ En otros clientes MCP, registra un servidor stdio con el comando `npx` y los arg | --- | --- | | `listar_operaciones` | Lista las operaciones agrupadas por tag: operationId, método, ruta y resumen. Acepta `tag` (opcional) para filtrar, por ejemplo `Invoices`. | | `obtener_operacion` | Devuelve en JSON una operación por su `operationId` (por ejemplo `createInvoice`): descripción con el enlace a la documentación, parámetros, cuerpo y respuestas con los esquemas resueltos. Si el id no existe, sugiere los parecidos. | +| `buscar_docs` | Busca en las secciones de docs.fiscalapi.com: guías, reglas fiscales del SAT, catálogos, errores del PAC por código (por ejemplo `CFDI40147`) y la referencia de cada endpoint. Devuelve título, página, la URL `.md` de la sección y un extracto. Acepta `consulta` y `limite` (opcional, de 1 a 10; 5 por omisión). | -El flujo normal es `listar_operaciones` para encontrar el endpoint y `obtener_operacion` para ver su contrato. +El flujo normal es `listar_operaciones` para encontrar el endpoint y `obtener_operacion` para ver su contrato. `buscar_docs` responde el resto: qué regla aplica, qué significa un error o qué valor de catálogo usar. Para leer una sección completa, el agente descarga su URL `.md`. ## Desarrollo ``` npm install -npm run build:data # genera data/ desde el OpenAPI de live +npm run build:data # genera data/ desde el OpenAPI de live y docs.fiscalapi.com npm run build npm test ``` @@ -44,7 +45,19 @@ npm test FISCALAPI_OPENAPI_SOURCE=test/fixtures/openapi-v4.json npm run build:data ``` -`data/` no se versiona: lo genera el build y se publica dentro del paquete. `data/source.json` guarda el hash SHA-256 de la fuente. +La documentación se descarga de `https://docs.fiscalapi.com` (`llms.txt` y la `.md` de cada sección). Para usar otro origen, pásalo con `--docs-origin` o en `FISCALAPI_DOCS_ORIGIN`: + +``` +npm run build:data -- test/fixtures/openapi-v4.json --docs-origin http://localhost:3000 +``` + +`data/` no se versiona: lo genera el build y se publica dentro del paquete. `data/source.json` guarda el hash SHA-256 del OpenAPI y, en `docs`, el origen, el hash y el número de documentos de la documentación. + +Las pruebas no usan la red: sirven en local `test/fixtures/openapi-v4.json` y `test/fixtures/docs-corpus.json.gz`, una copia del `llms.txt` y de todas sus `.md`. Para regenerar esa copia: + +``` +node scripts/snapshot-docs.mjs https://docs.fiscalapi.com test/fixtures/docs-corpus.json.gz +``` ## Licencia diff --git a/mcp/package-lock.json b/mcp/package-lock.json index e9cbad8..c29e417 100644 --- a/mcp/package-lock.json +++ b/mcp/package-lock.json @@ -10,6 +10,9 @@ "license": "MPL-2.0", "dependencies": { "@modelcontextprotocol/server": "2.3.1", + "@orama/stemmers": "3.1.18", + "@orama/stopwords": "3.1.18", + "minisearch": "7.2.0", "zod": "4.6.7" }, "bin": { @@ -97,6 +100,24 @@ "node": ">=20" } }, + "node_modules/@orama/stemmers": { + "version": "3.1.18", + "resolved": "https://registry.npmjs.org/@orama/stemmers/-/stemmers-3.1.18.tgz", + "integrity": "sha512-xXs5fpiUvBs4jJZtnayJmUSomdxxiwwwvTbAzPHSyI6geLM7bxXHgpVlYz4JeMBycHX8bHF7j9m7F35BxhuZ8g==", + "license": "Apache-2.0", + "engines": { + "node": ">= 20.0.0" + } + }, + "node_modules/@orama/stopwords": { + "version": "3.1.18", + "resolved": "https://registry.npmjs.org/@orama/stopwords/-/stopwords-3.1.18.tgz", + "integrity": "sha512-W8V7m7RnCme+99OmKl/xs5rf6OUhFpr0aPGVmPrXzTLSg4ZqSbRY2euS2S/lgjjYi/0NhEWqwoq8nDY6Ihx4EA==", + "license": "Apache-2.0", + "engines": { + "node": ">= 20.0.0" + } + }, "node_modules/@oxc-project/types": { "version": "0.154.0", "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.154.0.tgz", @@ -1259,6 +1280,12 @@ "@jridgewell/sourcemap-codec": "^1.6.0" } }, + "node_modules/minisearch": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/minisearch/-/minisearch-7.2.0.tgz", + "integrity": "sha512-dqT2XBYUOZOiC5t2HRnwADjhNS2cecp9u+TJRiJ1Qp/f5qjkeT5APcGPjHw+bz89Ms8Jp+cG4AlE+QZ/QnDglg==", + "license": "MIT" + }, "node_modules/nanoid": { "version": "3.3.20", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.20.tgz", diff --git a/mcp/package.json b/mcp/package.json index 9d423be..9c9d9ab 100644 --- a/mcp/package.json +++ b/mcp/package.json @@ -1,7 +1,7 @@ { "name": "@fiscalapi/mcp", "version": "0.1.0", - "description": "Servidor MCP de solo lectura con el catálogo de operaciones de la API de FiscalAPI (CFDI 4.0)", + "description": "Servidor MCP de solo lectura con el catálogo de operaciones de la API de FiscalAPI (CFDI 4.0) y una búsqueda en su documentación", "type": "module", "bin": { "fiscalapi-mcp": "dist/index.js" @@ -43,6 +43,9 @@ }, "dependencies": { "@modelcontextprotocol/server": "2.3.1", + "@orama/stemmers": "3.1.18", + "@orama/stopwords": "3.1.18", + "minisearch": "7.2.0", "zod": "4.6.7" }, "devDependencies": { diff --git a/mcp/scripts/build-data.mjs b/mcp/scripts/build-data.mjs index 9a5d5fc..559e6fa 100644 --- a/mcp/scripts/build-data.mjs +++ b/mcp/scripts/build-data.mjs @@ -1,14 +1,20 @@ -// Genera mcp/data desde el OpenAPI público de FiscalAPI. +// Genera mcp/data desde el OpenAPI público de FiscalAPI y la documentación de docs.fiscalapi.com. // -// Uso: node scripts/build-data.mjs [fuente] [--out ] -// fuente: URL http(s) o ruta de archivo. Por omisión FISCALAPI_OPENAPI_SOURCE o el OpenAPI de live. -// --out: directorio de salida. Por omisión mcp/data. +// Uso: node scripts/build-data.mjs [fuente] [--docs-origin ] [--out ] +// fuente: URL http(s) o ruta de archivo. Por omisión FISCALAPI_OPENAPI_SOURCE o el OpenAPI de live. +// --docs-origin: origen del que se descargan llms.txt y las secciones .md. Por omisión +// FISCALAPI_DOCS_ORIGIN o https://docs.fiscalapi.com. +// --out: directorio de salida. Por omisión mcp/data. import { createHash } from 'node:crypto'; import { mkdir, readFile, rename, rm, writeFile } from 'node:fs/promises'; import path from 'node:path'; import { fileURLToPath } from 'node:url'; import { parseArgs } from 'node:util'; +import MiniSearch from 'minisearch'; + +import { DOCS_INDEX_OPTIONS } from '../src/docs-search.ts'; +import { DOCS_SITE, DocsError, downloadMarkdown, fetchLlms, normalizeOrigin, parseLlms, toDocument } from './docs.mjs'; import { buildCatalog, buildOperation, extractOperations, OpenApiError, parseOpenApi, serializeOperation } from './openapi.mjs'; const DEFAULT_SOURCE = 'https://live.fiscalapi.com/api/openapi/v4.json'; @@ -18,36 +24,64 @@ const FETCH_TIMEOUT_MS = 60_000; async function main() { const { positionals, values } = parseArgs({ allowPositionals: true, - options: { out: { type: 'string' } }, + options: { out: { type: 'string' }, 'docs-origin': { type: 'string' } }, }); const source = positionals[0] ?? process.env.FISCALAPI_OPENAPI_SOURCE ?? DEFAULT_SOURCE; + const docsOrigin = normalizeOrigin(values['docs-origin'] ?? process.env.FISCALAPI_DOCS_ORIGIN ?? DOCS_SITE); const outDir = path.resolve(values.out ?? DEFAULT_OUT_DIR); const text = await readSource(source); const doc = parseOpenApi(text); const operations = extractOperations(doc); const catalog = buildCatalog(operations); + const docs = await downloadDocs(docsOrigin); const staging = `${outDir}.tmp-${process.pid}`; await rm(staging, { recursive: true, force: true }); - await mkdir(path.join(staging, 'operations'), { recursive: true }); - let largest = { id: '', chars: 0 }; - for (const entry of operations) { - const json = serializeOperation(buildOperation(doc, entry)); - if (json.length > largest.chars) largest = { id: entry.id, chars: json.length }; - await writeFile(path.join(staging, 'operations', `${entry.id}.json`), json); + try { + await mkdir(path.join(staging, 'operations'), { recursive: true }); + let largest = { id: '', chars: 0 }; + for (const entry of operations) { + const json = serializeOperation(buildOperation(doc, entry)); + if (json.length > largest.chars) largest = { id: entry.id, chars: json.length }; + await writeFile(path.join(staging, 'operations', `${entry.id}.json`), json); + } + await writeFile(path.join(staging, 'catalog.json'), JSON.stringify(catalog)); + await writeFile(path.join(staging, 'docs-index.json'), docs.indexJson); + const sha256 = createHash('sha256').update(text).digest('hex'); + const sourceInfo = { + source, + sha256, + operations: catalog.length, + docs: { origin: docsOrigin, sha256: docs.sha256, documents: docs.documents }, + }; + await writeFile(path.join(staging, 'source.json'), JSON.stringify(sourceInfo, null, 2)); + + // Se reemplaza de golpe para que un build fallido nunca deje data/ a medias. + await rm(outDir, { recursive: true, force: true }); + await rename(staging, outDir); + + console.error(`build-data: ${catalog.length} operaciones en ${outDir}`); + console.error(`build-data: la más grande es ${largest.id} con ${largest.chars} caracteres`); + console.error(`build-data: sha256 ${sha256}`); + console.error(`build-data: ${docs.documents} documentos de ${docsOrigin}; el índice mide ${Buffer.byteLength(docs.indexJson)} bytes`); + console.error(`build-data: sha256 de la documentación ${docs.sha256}`); + } finally { + await rm(staging, { recursive: true, force: true }); } - await writeFile(path.join(staging, 'catalog.json'), JSON.stringify(catalog)); - const sha256 = createHash('sha256').update(text).digest('hex'); - await writeFile(path.join(staging, 'source.json'), JSON.stringify({ source, sha256, operations: catalog.length }, null, 2)); +} - // Se reemplaza de golpe para que un build fallido nunca deje data/ a medias. - await rm(outDir, { recursive: true, force: true }); - await rename(staging, outDir); +// El hash cubre el llms.txt y cada Markdown en el orden del llms.txt: cambia solo si cambia la documentación. +async function downloadDocs(origin) { + const llms = await fetchLlms(origin); + const entries = parseLlms(llms); + const markdown = await downloadMarkdown(origin, entries.map((entry) => entry.url)); - console.error(`build-data: ${catalog.length} operaciones en ${outDir}`); - console.error(`build-data: la más grande es ${largest.id} con ${largest.chars} caracteres`); - console.error(`build-data: sha256 ${sha256}`); + const hash = createHash('sha256').update(llms); + for (const body of markdown) hash.update(body); + const index = new MiniSearch(DOCS_INDEX_OPTIONS); + index.addAll(entries.map((entry, i) => toDocument(entry, markdown[i]))); + return { indexJson: JSON.stringify(index), sha256: hash.digest('hex'), documents: entries.length }; } async function readSource(source) { @@ -73,7 +107,8 @@ async function readSource(source) { try { await main(); } catch (error) { - const message = error instanceof OpenApiError ? error.message : (error?.stack ?? String(error)); + const expected = error instanceof OpenApiError || error instanceof DocsError; + const message = expected ? error.message : (error?.stack ?? String(error)); console.error(`build-data: ${message}`); process.exitCode = 1; } diff --git a/mcp/scripts/docs.mjs b/mcp/scripts/docs.mjs new file mode 100644 index 0000000..74c21e5 --- /dev/null +++ b/mcp/scripts/docs.mjs @@ -0,0 +1,169 @@ +// Documentación de docs.fiscalapi.com para el índice de buscar_docs. parseLlms y cleanSection son +// transformaciones puras; fetchLlms y downloadMarkdown son la única parte que usa la red. + +// El llms.txt siempre enlaza a este sitio; para descargar de otro origen se cambia solo el prefijo. +export const DOCS_SITE = 'https://docs.fiscalapi.com'; +export const DOWNLOAD_CONCURRENCY = 8; +export const FETCH_TIMEOUT_MS = 60_000; + +const SITE_URL = `${escapeRegExp(DOCS_SITE)}/\\S+?\\.md`; +// Una página: "- [Título](https://docs.fiscalapi.com/ruta.md): descripción". +const PAGE_LINE = new RegExp(`^- \\[([^\\]]+)\\]\\((${SITE_URL})\\)`); +// Una sección de la página anterior: " - [Título](https://docs.fiscalapi.com/ruta/slug.md)". +const SECTION_LINE = new RegExp(`^ {2}- \\[([^\\]]+)\\]\\((${SITE_URL})\\)`); +const ANY_MD_LINK = new RegExp(`\\((${SITE_URL})\\)`, 'g'); + +const CODE_BLOCK = /```[\s\S]*?```/g; +const HEADING = /^#{1,2} (.+)$/m; +const ANNOTATION = /\{\{([\s\S]*?)\}\}/g; +const ANNOTATION_TAG = /\btag:\s*'([^']*)'/; +const ANNOTATION_LABEL = /\blabel:\s*'([^']*)'/; +const TAG_WITH_NAME = /<[A-Za-z][^>]*?\sname="([^"]*)"[^>]*>/g; +// o : el valor, sin comillas. +const ITEM_VALUE = /]*?\bvalue=(?:'"?([^'"]*)"?'|"([^"]*)")[^>]*>/g; +const ANY_TAG = /<\/?[A-Za-z][^>]*>/g; +const LINK = /!?\[([^\]]*)\]\([^)]*\)/g; +const INLINE_CODE_MARK = /`/g; +const LINE_INDENT = /^[ \t]+|[ \t]+$/gm; +const REPEATED_BLANK_LINES = /\n{3,}/g; + +export class DocsError extends Error {} + +/** + * Los documentos a indexar, sin repetir URL y en el orden del llms.txt: cada sección con el título + * de su página, y cada página sin secciones entera. Una página con secciones no se indexa entera + * porque es la suma de ellas y competiría con sus propias secciones. + */ +export function parseLlms(text) { + const pages = new Map(); + let current; + for (const line of text.split(/\r?\n/)) { + const page = PAGE_LINE.exec(line); + if (page) { + const [, title, url] = page; + current = pages.get(url) ?? { title, url, sections: [] }; + pages.set(url, current); + continue; + } + const section = SECTION_LINE.exec(line); + if (section && current) current.sections.push(section[2]); + } + + const documents = new Map(); + for (const page of pages.values()) { + const urls = page.sections.length > 0 ? page.sections : [page.url]; + for (const url of urls) if (!documents.has(url)) documents.set(url, { url, page: page.title }); + } + if (documents.size === 0) throw new DocsError(`el llms.txt no enlaza ninguna página .md de ${DOCS_SITE}.`); + return [...documents.values()]; +} + +/** Todas las URLs .md del llms.txt, páginas y secciones, sin repetir. */ +export function listMarkdownUrls(text) { + return [...new Set([...text.matchAll(ANY_MD_LINK)].map((match) => match[1]))]; +} + +/** La ruta de una URL del sitio, por ejemplo /invoices/cancelar-factura.md. */ +export function sitePath(url) { + return url.slice(DOCS_SITE.length); +} + +/** + * Convierte el MDX crudo de una sección en texto indexable y legible: título del primer encabezado, + * endpoint de su anotación {{ tag, label }} y el cuerpo sin código, etiquetas ni URLs. Conserva los + * saltos de línea y las tablas para que el extracto se lea. + */ +export function cleanSection(markdown) { + const withoutCode = markdown.replace(CODE_BLOCK, ''); + const heading = HEADING.exec(withoutCode)?.[1] ?? ''; + const text = withoutCode + .replace(HEADING, '') + .replace(ANNOTATION, '') + // El name de es el nombre del campo: se busca por él. + .replace(TAG_WITH_NAME, '$1') + // El value de un es la clave del catálogo (por ejemplo el motivo de cancelación "01"). + .replace(ITEM_VALUE, (_, quoted, plain) => `${quoted ?? plain} `) + .replace(ANY_TAG, '') + .replace(LINK, '$1') + // El tokenizador no corta en el acento grave: `sat.blacklist.69b` indexaría "69b`" y no "69b". + .replace(INLINE_CODE_MARK, '') + .replace(LINE_INDENT, '') + .replace(REPEATED_BLANK_LINES, '\n\n') + .trim(); + return { title: heading.replace(ANNOTATION, '').trim(), endpoint: endpointOf(heading), text }; +} + +/** El documento del índice de una sección; si la sección no tiene encabezado, se titula como su página. */ +export function toDocument({ url, page }, markdown) { + const { title, endpoint, text } = cleanSection(markdown); + return { id: url, url, page, title: title || page, endpoint, text }; +} + +export async function fetchLlms(origin) { + return fetchMarkdown(`${origin}/llms.txt`); +} + +/** Descarga las URLs del sitio desde origin, como mucho 8 a la vez; devuelve el Markdown en el mismo orden. */ +export async function downloadMarkdown(origin, urls) { + const markdown = new Array(urls.length); + let next = 0; + let failed = false; + async function worker() { + while (!failed && next < urls.length) { + const index = next++; + try { + markdown[index] = await fetchMarkdown(`${origin}${sitePath(urls[index])}`); + } catch (error) { + failed = true; + throw error; + } + } + } + await Promise.all(Array.from({ length: Math.min(DOWNLOAD_CONCURRENCY, urls.length) }, worker)); + return markdown; +} + +/** El origen sin "/" final; solo se aceptan URLs http(s). */ +export function normalizeOrigin(origin) { + let url; + try { + url = new URL(origin); + } catch { + throw new DocsError(`el origen de la documentación no es una URL válida: ${origin}`); + } + if (url.protocol !== 'http:' && url.protocol !== 'https:') { + throw new DocsError(`el origen de la documentación debe ser http(s): ${origin}`); + } + return origin.replace(/\/+$/, ''); +} + +// Un 200 con HTML es la página del sitio (por ejemplo un 404 servido como página) y no su Markdown. +async function fetchMarkdown(url) { + let response; + let body; + try { + response = await fetch(url, { signal: AbortSignal.timeout(FETCH_TIMEOUT_MS) }); + if (response.status !== 200) { + await response.body?.cancel(); + throw new DocsError(`${url} respondió HTTP ${response.status}; se esperaba 200.`); + } + body = await response.text(); + } catch (error) { + if (error instanceof DocsError) throw error; + throw new DocsError(`no se pudo descargar ${url}: ${error.message}`); + } + if (body.trimStart().startsWith('<')) throw new DocsError(`${url} devolvió HTML en lugar de Markdown.`); + return body; +} + +function endpointOf(heading) { + const annotation = [...heading.matchAll(ANNOTATION)][0]?.[1]; + if (annotation === undefined) return ''; + const tag = ANNOTATION_TAG.exec(annotation)?.[1]; + const label = ANNOTATION_LABEL.exec(annotation)?.[1]; + return tag !== undefined && label !== undefined ? `${tag} ${label}` : ''; +} + +function escapeRegExp(value) { + return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); +} diff --git a/mcp/scripts/snapshot-docs.mjs b/mcp/scripts/snapshot-docs.mjs new file mode 100644 index 0000000..123bfc9 --- /dev/null +++ b/mcp/scripts/snapshot-docs.mjs @@ -0,0 +1,30 @@ +// Guarda una copia de la documentación para las pruebas: llms.txt y todas las .md que enlaza. +// +// Uso: node scripts/snapshot-docs.mjs +// origen: de dónde descargar, por ejemplo http://localhost:3999 o https://docs.fiscalapi.com. +// salida: { "llms": "", "files": { "/.md": "" } } comprimido con gzip. +import { writeFile } from 'node:fs/promises'; +import { gzipSync } from 'node:zlib'; + +import { DocsError, downloadMarkdown, fetchLlms, listMarkdownUrls, normalizeOrigin, sitePath } from './docs.mjs'; + +async function main() { + const [originArg, outFile] = process.argv.slice(2); + if (originArg === undefined || outFile === undefined) { + throw new DocsError('uso: node scripts/snapshot-docs.mjs '); + } + const origin = normalizeOrigin(originArg); + const llms = await fetchLlms(origin); + const urls = listMarkdownUrls(llms); + const markdown = await downloadMarkdown(origin, urls); + const files = Object.fromEntries(urls.map((url, i) => [sitePath(url), markdown[i]])); + await writeFile(outFile, gzipSync(JSON.stringify({ llms, files }))); + console.error(`snapshot-docs: ${urls.length} archivos .md de ${origin} en ${outFile}`); +} + +try { + await main(); +} catch (error) { + console.error(`snapshot-docs: ${error instanceof DocsError ? error.message : (error?.stack ?? String(error))}`); + process.exitCode = 1; +} diff --git a/mcp/src/docs-search.ts b/mcp/src/docs-search.ts new file mode 100644 index 0000000..1c6e8cd --- /dev/null +++ b/mcp/src/docs-search.ts @@ -0,0 +1,48 @@ +// Configuración única del índice de la documentación. La importan scripts/build-data.mjs, que arma +// el índice, y src/docs.ts, que lo carga: si divergieran, las búsquedas no coincidirían con lo indexado. +import { stemmer } from '@orama/stemmers/spanish'; +import { stopwords } from '@orama/stopwords/spanish'; +import type { Options, SearchOptions } from 'minisearch'; + +/** Un documento del índice: una sección de docs.fiscalapi.com, o una página entera si no tiene secciones. */ +export interface DocsDocument { + readonly id: string; + readonly url: string; + readonly page: string; + readonly title: string; + readonly endpoint: string; + readonly text: string; +} + +const MIN_FUZZY_TERM_LENGTH = 5; +const FUZZINESS = 0.2; +// Las marcas diacríticas que NFD separa de su letra: "á" queda como "a" más el acento. +const COMBINING_MARKS = /\p{Mn}/gu; + +const STOPWORDS: ReadonlySet = new Set(stopwords.map(withoutAccents)); + +/** Minúsculas y sin acentos; descarta las palabras vacías del español y reduce el resto a su raíz. */ +export function processTerm(term: string): string | null { + const folded = withoutAccents(term.toLowerCase()); + return STOPWORDS.has(folded) ? null : stemmer(folded); +} + +// Sin prefijo: con él, "sat" coincidía con cada SatXxx de las tablas de catálogos y la sección de +// catálogos de Carta Porte encabezaba consultas que no eran de catálogos. El stemmer ya cubre las +// flexiones. El fuzzy solo aplica a términos de 5 o más letras porque "sat" coincidía con "gat". +const searchOptions: SearchOptions = { + boost: { title: 3, page: 1.5, endpoint: 2 }, + prefix: false, + fuzzy: (term) => (term.length >= MIN_FUZZY_TERM_LENGTH ? FUZZINESS : false), +}; + +export const DOCS_INDEX_OPTIONS: Options = { + fields: ['title', 'page', 'endpoint', 'text'], + storeFields: ['title', 'page', 'url', 'text'], + processTerm, + searchOptions, +}; + +function withoutAccents(value: string): string { + return value.normalize('NFD').replace(COMBINING_MARKS, ''); +} diff --git a/mcp/src/docs.ts b/mcp/src/docs.ts new file mode 100644 index 0000000..a7f00a9 --- /dev/null +++ b/mcp/src/docs.ts @@ -0,0 +1,109 @@ +import { readFileSync } from 'node:fs'; +import { fileURLToPath } from 'node:url'; + +import MiniSearch from 'minisearch'; + +import { DOCS_INDEX_OPTIONS, processTerm } from './docs-search.js'; +import type { DocsDocument } from './docs-search.js'; + +export interface DocsResult { + readonly title: string; + readonly page: string; + readonly url: string; + readonly excerpt: string; +} + +// Unos 750 tokens por resultado: 10 resultados caben en el límite de unos 10k tokens por respuesta. +export const MAX_EXCERPT_WORDS = 300; +export const MAX_EXCERPT_CHARS = 3_000; +// Contexto antes de la primera coincidencia, para que se lea de qué habla. +const WORDS_BEFORE_MATCH = 40; +const ELLIPSIS = '…'; +const WORD = /\S+/g; + +type StoredFields = Pick; + +/** Índice léxico de docs.fiscalapi.com generado por scripts/build-data.mjs; se consulta sin red. */ +export class DocsIndex { + readonly #index: MiniSearch; + + private constructor(index: MiniSearch) { + this.#index = index; + } + + /** dataDir debe terminar en "/" para que las rutas relativas se resuelvan dentro de él. */ + static load(dataDir: URL): DocsIndex { + const indexFile = new URL('docs-index.json', dataDir); + try { + return new DocsIndex(MiniSearch.loadJSON(readFileSync(indexFile, 'utf8'), DOCS_INDEX_OPTIONS)); + } catch (error) { + throw new Error(`No se pudo leer el índice de la documentación (${fileURLToPath(indexFile)}). Corre npm run build:data.`, { + cause: error, + }); + } + } + + /** Los limit resultados más relevantes, cada uno con un extracto alrededor de su primera coincidencia. */ + search(query: string, limit: number): DocsResult[] { + return this.#index + .search(query) + .slice(0, limit) + .map((result) => { + const { title, page, url, text } = result as unknown as StoredFields; + return { title, page, url, excerpt: excerpt(text, result.terms, result.queryTerms) }; + }); + } +} + +const tokenize: (text: string) => string[] = MiniSearch.getDefault('tokenize'); + +/** + * Hasta 300 palabras y 3,000 caracteres del texto, desde unas 40 palabras antes de la primera que + * coincidió con la consulta. Así "CFDI40147" muestra la fila de esa tabla y no el inicio de la tabla. + */ +function excerpt(text: string, matchedTerms: readonly string[], queryTerms: readonly string[]): string { + const words = [...text.matchAll(WORD)]; + if (words.length === 0) return ''; + const first = firstWord(words, firstMatch(words, matchedTerms, queryTerms)); + const last = Math.min(words.length, first + MAX_EXCERPT_WORDS) - 1; + const budget = MAX_EXCERPT_CHARS - 2 * ELLIPSIS.length; + let body = text.slice(words[first]!.index, endOf(words[last]!)); + let cutAtEnd = last < words.length - 1; + if (body.length > budget) { + body = cutAtWhitespace(body, budget); + cutAtEnd = true; + } + return `${first > 0 ? ELLIPSIS : ''}${body}${cutAtEnd ? ELLIPSIS : ''}`; +} + +// Primero una coincidencia exacta con la consulta: el fuzzy hace que "CFDI40147" también coincida con +// CFDI40101, que está antes en la misma tabla. Si no hay exacta, la primera coincidencia cualquiera, y +// si la coincidencia fue solo en el título, el inicio del texto. +function firstMatch(words: readonly RegExpExecArray[], matchedTerms: readonly string[], queryTerms: readonly string[]): number { + const exact = new Set(matchedTerms.filter((term) => queryTerms.includes(term))); + const any = new Set(matchedTerms); + for (const terms of [exact, any]) { + const index = words.findIndex((word) => tokenize(word[0]).some((token) => terms.has(processTerm(token) ?? ''))); + if (index >= 0) return index; + } + return 0; +} + +// Retrocede hasta 40 palabras, pero sin que la coincidencia quede fuera del límite de caracteres +// (una fila de tabla sin espacios podría medir más que todo el extracto). +function firstWord(words: readonly RegExpExecArray[], match: number): number { + const matchEnd = endOf(words[match]!); + let first = Math.max(0, match - WORDS_BEFORE_MATCH); + while (first < match && matchEnd - words[first]!.index > MAX_EXCERPT_CHARS / 2) first++; + return first; +} + +function endOf(word: RegExpExecArray): number { + return word.index + word[0].length; +} + +function cutAtWhitespace(value: string, maxChars: number): string { + const cut = value.slice(0, maxChars); + const lastSpace = cut.search(/\s\S*$/); + return (lastSpace > 0 ? cut.slice(0, lastSpace) : cut).trimEnd(); +} diff --git a/mcp/src/index.ts b/mcp/src/index.ts index 644b5fa..e8ba817 100644 --- a/mcp/src/index.ts +++ b/mcp/src/index.ts @@ -4,22 +4,26 @@ import { readFileSync } from 'node:fs'; import { serveStdio } from '@modelcontextprotocol/server/stdio'; import { OperationCatalog } from './catalog.js'; +import { DocsIndex } from './docs.js'; import { createServer } from './server.js'; // stdout es el canal del protocolo: todo diagnóstico va a stderr. const packageRoot = new URL('../', import.meta.url); let catalog: OperationCatalog; +let docs: DocsIndex; let version: string; try { version = (JSON.parse(readFileSync(new URL('package.json', packageRoot), 'utf8')) as { version: string }).version; - catalog = OperationCatalog.load(new URL('data/', packageRoot)); + const dataDir = new URL('data/', packageRoot); + catalog = OperationCatalog.load(dataDir); + docs = DocsIndex.load(dataDir); } catch (error) { console.error(`fiscalapi-mcp: ${error instanceof Error ? error.message : String(error)}`); process.exit(1); } -const handle = serveStdio(() => createServer(catalog, version), { +const handle = serveStdio(() => createServer(catalog, docs, version), { onerror: (error) => console.error(`fiscalapi-mcp: ${error.message}`), }); diff --git a/mcp/src/server.ts b/mcp/src/server.ts index acd85aa..1c744c0 100644 --- a/mcp/src/server.ts +++ b/mcp/src/server.ts @@ -3,14 +3,18 @@ import type { CallToolResult } from '@modelcontextprotocol/server'; import * as z from 'zod/v4'; import type { CatalogEntry, OperationCatalog } from './catalog.js'; +import type { DocsIndex, DocsResult } from './docs.js'; export const SERVER_NAME = 'fiscalapi'; -// Las dos herramientas solo leen archivos del paquete: no llaman a la API ni a ningún otro servicio. +// Las herramientas solo leen archivos del paquete: no llaman a la API ni a ningún otro servicio. const READ_ONLY = { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false } as const; const MAX_ECHOED_INPUT = 100; +const MAX_QUERY_CHARS = 200; +const MAX_DOCS_RESULTS = 10; +const DEFAULT_DOCS_RESULTS = 5; -export function createServer(catalog: OperationCatalog, version: string): McpServer { +export function createServer(catalog: OperationCatalog, docs: DocsIndex, version: string): McpServer { const server = new McpServer({ name: SERVER_NAME, version }); server.registerTool( @@ -49,6 +53,31 @@ export function createServer(catalog: OperationCatalog, version: string): McpSer async ({ operationId }) => getOperation(catalog, operationId), ); + server.registerTool( + 'buscar_docs', + { + title: 'Buscar en la documentación de FiscalAPI', + description: + 'Busca en las secciones de docs.fiscalapi.com: guías, reglas fiscales del SAT, catálogos, errores del PAC por código (como CFDI40147) y la referencia de cada endpoint. Devuelve de cada resultado el título, la página, la URL .md de la sección y un extracto; para leer la sección completa, descarga esa URL. No llama a la API ni pide llaves.', + inputSchema: z.object({ + consulta: z + .string() + .min(1) + .max(MAX_QUERY_CHARS) + .describe('Qué buscar, en español o por código (por ejemplo "cancelar factura", "complemento de pago" o "CFDI40147").'), + limite: z + .number() + .int() + .min(1) + .max(MAX_DOCS_RESULTS) + .optional() + .describe(`Número máximo de resultados, de 1 a ${MAX_DOCS_RESULTS}. Por omisión ${DEFAULT_DOCS_RESULTS}.`), + }), + annotations: READ_ONLY, + }, + async ({ consulta, limite }) => searchDocs(docs, consulta, limite ?? DEFAULT_DOCS_RESULTS), + ); + return server; } @@ -72,6 +101,30 @@ async function getOperation(catalog: OperationCatalog, operationId: string): Pro return error(`No existe la operación "${operationId}". ${hint}Usa listar_operaciones para ver los operationId disponibles.`); } +function searchDocs(docs: DocsIndex, query: string, limit: number): CallToolResult { + const results = docs.search(query, limit); + const echoed = clip(query); + // Sin resultados no es un error de la llamada: el agente debe reformular, no reintentar igual. + if (results.length === 0) { + return text( + `FiscalAPI docs: no hay resultados para "${echoed}". Prueba con otras palabras, o usa listar_operaciones para ver los endpoints de la API.`, + ); + } + const count = results.length === 1 ? '1 resultado' : `${results.length} resultados`; + const blocks = results.map((result) => `## ${heading(result)}\n${result.url}\n\n${result.excerpt}`); + return text([`FiscalAPI docs: ${count} para "${echoed}".`, ...blocks].join('\n\n')); +} + +// Una página sin secciones se indexa entera y su título es el de la página: no se repite. +function heading({ title, page }: DocsResult): string { + return title === page ? title : `${title} (${page})`; +} + +// La consulta admite más caracteres de los que vale la pena repetir en la respuesta. +function clip(value: string): string { + return value.length > MAX_ECHOED_INPUT ? `${value.slice(0, MAX_ECHOED_INPUT)}…` : value; +} + function groupByTag(entries: readonly CatalogEntry[]): string[] { const lines: string[] = []; let currentTag: string | undefined; diff --git a/mcp/test/build-data.test.ts b/mcp/test/build-data.test.ts index e739885..36c4616 100644 --- a/mcp/test/build-data.test.ts +++ b/mcp/test/build-data.test.ts @@ -1,5 +1,4 @@ -import { spawn } from 'node:child_process'; -import { mkdtempSync, rmSync, writeFileSync } from 'node:fs'; +import { existsSync, mkdirSync, mkdtempSync, readdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs'; import { createServer } from 'node:http'; import type { AddressInfo } from 'node:net'; import { tmpdir } from 'node:os'; @@ -8,6 +7,7 @@ import { fileURLToPath } from 'node:url'; import { afterAll, describe, expect, it } from 'vitest'; +import { cleanSection, DocsError, parseLlms } from '../scripts/docs.mjs'; import { buildCatalog, buildOperation, @@ -16,8 +16,12 @@ import { parseOpenApi, serializeOperation, } from '../scripts/openapi.mjs'; +import { serveDocs } from './docs-server.js'; +import { dataDirUrl } from './helpers.js'; +import { runBuildData } from './run-build-data.js'; const packageDir = fileURLToPath(new URL('../', import.meta.url)); +const openApiFixture = path.join(packageDir, 'test', 'fixtures', 'openapi-v4.json'); // Límite de RNF-05 escrito aquí a propósito: si alguien sube el del build, la prueba lo detecta. const SPEC_MAX_OPERATION_CHARS = 40_000; const LONG_TEXT ='Texto largo de prueba para inflar el esquema. '.repeat(8); @@ -171,19 +175,16 @@ describe('extractOperations y buildCatalog', () => { }); }); -describe('scripts/build-data.mjs', () => { +// Cada prueba arranca un proceso de Node: en Windows, con los otros archivos de prueba corriendo en +// paralelo, el primero llegó a tardar más que los 5 s por omisión. +describe('scripts/build-data.mjs', { timeout: 30_000 }, () => { const workDir = mkdtempSync(path.join(tmpdir(), 'fiscalapi-mcp-build-')); afterAll(() => rmSync(workDir, { recursive: true, force: true })); - function runBuild(source: string): Promise<{ code: number | null; stderr: string }> { - return new Promise((resolve) => { - const child = spawn(process.execPath, ['scripts/build-data.mjs', source, '--out', path.join(workDir, 'data')], { - cwd: packageDir, - }); - let stderr = ''; - child.stderr.on('data', (chunk) => (stderr += chunk)); - child.on('close', (code) => resolve({ code, stderr })); - }); + const outDir = path.join(workDir, 'data'); + + function runBuild(source: string, ...args: string[]): Promise<{ code: number | null; stderr: string }> { + return runBuildData([source, ...args, '--out', outDir]); } it('sale con código 1 si la URL no responde 200', async () => { @@ -209,4 +210,160 @@ describe('scripts/build-data.mjs', () => { expect(code).toBe(1); expect(stderr).toContain('no es un documento OpenAPI 3'); }); + + // Un data/ previo con una marca: si el build fallido lo tocara, la marca desaparecería. + function seedPreviousData(): string { + rmSync(outDir, { recursive: true, force: true }); + mkdirSync(outDir, { recursive: true }); + const marker = path.join(outDir, 'previo.txt'); + writeFileSync(marker, 'data/ anterior'); + return marker; + } + + function leftovers(): string[] { + return readdirSync(workDir).filter((name) => name.startsWith('data.tmp-')); + } + + it.each([ + ['responde 404', { '/pagina/seccion-b.md': undefined }, 'pagina/seccion-b.md respondió HTTP 404; se esperaba 200'], + ['devuelve HTML', { '/pagina/seccion-b.md': '' }, 'pagina/seccion-b.md devolvió HTML en lugar de Markdown'], + ])('sale con código 1 y no toca data/ si una sección .md %s', async (_, override, message) => { + const files: Record = { + '/pagina/seccion-a.md': '## Sección A\n\nTexto.', + '/pagina/seccion-b.md': '## Sección B\n\nTexto.', + ...override, + }; + const docs = await serveDocs({ + llms: SMALL_LLMS, + files: Object.fromEntries(Object.entries(files).filter((entry): entry is [string, string] => entry[1] !== undefined)), + }); + const marker = seedPreviousData(); + try { + const { code, stderr } = await runBuild(openApiFixture, '--docs-origin', docs.origin); + + expect(code).toBe(1); + expect(stderr).toContain(`build-data: ${docs.origin}/${message}`); + expect(readFileSync(marker, 'utf8')).toBe('data/ anterior'); + expect(readdirSync(outDir)).toEqual(['previo.txt']); + expect(leftovers()).toEqual([]); + } finally { + await docs.close(); + } + }); + + it('sale con código 1 si llms.txt no existe', async () => { + const docs = await serveDocs({ llms: SMALL_LLMS, files: {} }); + try { + const { code, stderr } = await runBuild(openApiFixture, '--docs-origin', `${docs.origin}/no-existe`); + + expect(code).toBe(1); + expect(stderr).toContain(`${docs.origin}/no-existe/llms.txt respondió HTTP 404`); + } finally { + await docs.close(); + } + }); + + it('guarda en source.json el origen, el hash y el número de documentos de la documentación', () => { + const source = JSON.parse(readFileSync(new URL('source.json', dataDirUrl()), 'utf8')); + + expect(source.operations).toBe(100); + expect(source.docs).toMatchObject({ origin: expect.stringMatching(/^http:\/\/127\.0\.0\.1:\d+$/), documents: 383 }); + expect(source.docs.sha256).toMatch(/^[0-9a-f]{64}$/); + expect(existsSync(new URL('docs-index.json', dataDirUrl()))).toBe(true); + }); +}); + +const SMALL_LLMS = [ + '# FiscalAPI', + '', + '- Los endpoints están bajo `/api/v4/`.', + '- [Página](https://docs.fiscalapi.com/pagina.md): descripción.', + ' - [Sección A](https://docs.fiscalapi.com/pagina/seccion-a.md)', + ' - [Sección B](https://docs.fiscalapi.com/pagina/seccion-b.md)', +].join('\n'); + +describe('parseLlms', () => { + it('indexa cada sección con su página y las páginas sin secciones enteras, sin repetir URL', () => { + const llms = [ + SMALL_LLMS, + '', + '## Referencia', + '', + '- [Sola](https://docs.fiscalapi.com/sola.md): página sin secciones.', + '- [Otra](https://docs.fiscalapi.com/otra.md): repite una sección.', + ' - [Sección A](https://docs.fiscalapi.com/pagina/seccion-a.md)', + ' - [Propia](https://docs.fiscalapi.com/otra/propia.md)', + '- [Sola](https://docs.fiscalapi.com/sola.md): la misma página otra vez.', + '- [Externa](https://example.com/externa.md): de otro sitio.', + ].join('\n'); + + expect(parseLlms(llms)).toEqual([ + { url: 'https://docs.fiscalapi.com/pagina/seccion-a.md', page: 'Página' }, + { url: 'https://docs.fiscalapi.com/pagina/seccion-b.md', page: 'Página' }, + { url: 'https://docs.fiscalapi.com/sola.md', page: 'Sola' }, + { url: 'https://docs.fiscalapi.com/otra/propia.md', page: 'Otra' }, + ]); + }); + + it('falla si el llms.txt no enlaza ninguna .md del sitio', () => { + expect(() => parseLlms('')).toThrow(DocsError); + }); +}); + +describe('cleanSection', () => { + const section = [ + "## Cancelar factura {{ tag: 'DELETE', label: '/api/v4/invoices' }}", + '', + ' Este endpoint cancela una factura. Revisa los [valores](/modes-of-operation) y `sat.blacklist.69b`.', + ' ', + ' ', + ' Folio fiscal (UUID) de la factura.', + ' ', + '', + '', + '', + ` Comprobante emitido con errores **con** relación`, + ' Certificado CSD', + ' ', + '', + '```bash', + '# Esto no es un encabezado', + 'curl -X DELETE https://test.fiscalapi.com/api/v4/invoices', + '```', + '', + '| Código | Descripción |', + '| --- | --- |', + '| CFDI40147 | El campo DomicilioFiscalReceptor |', + ].join('\n'); + + it('separa título y endpoint de la anotación', () => { + const { title, endpoint } = cleanSection(section); + + expect(title).toBe('Cancelar factura'); + expect(endpoint).toBe('DELETE /api/v4/invoices'); + }); + + it('quita código, etiquetas, URLs y sangría; conserva el name y el value de las etiquetas, los saltos de línea y las tablas', () => { + const { text } = cleanSection(section); + + expect(text).toBe( + [ + 'Este endpoint cancela una factura. Revisa los valores y sat.blacklist.69b.', + '', + 'invoiceUuid', + 'Folio fiscal (UUID) de la factura.', + '', + '01 Comprobante emitido con errores **con** relación', + '0 Certificado CSD', + '', + '| Código | Descripción |', + '| --- | --- |', + '| CFDI40147 | El campo DomicilioFiscalReceptor |', + ].join('\n'), + ); + }); + + it('una sección sin anotación no tiene endpoint', () => { + expect(cleanSection('# Webhooks\n\nTexto.')).toEqual({ title: 'Webhooks', endpoint: '', text: 'Texto.' }); + }); }); diff --git a/mcp/test/docs-server.ts b/mcp/test/docs-server.ts new file mode 100644 index 0000000..db21940 --- /dev/null +++ b/mcp/test/docs-server.ts @@ -0,0 +1,40 @@ +import { readFileSync } from 'node:fs'; +import { createServer } from 'node:http'; +import type { AddressInfo } from 'node:net'; +import { gunzipSync } from 'node:zlib'; + +/** El formato que escribe scripts/snapshot-docs.mjs: llms.txt y cada .md por su ruta. */ +export interface DocsCorpus { + readonly llms: string; + readonly files: Readonly>; +} + +export interface DocsServer { + readonly origin: string; + close(): Promise; +} + +const corpusFile = new URL('fixtures/docs-corpus.json.gz', import.meta.url); + +export function readDocsCorpus(): DocsCorpus { + return JSON.parse(gunzipSync(readFileSync(corpusFile)).toString('utf8')) as DocsCorpus; +} + +/** Sirve el corpus en 127.0.0.1 como lo haría docs.fiscalapi.com: /llms.txt, cada ruta .md y 404 para lo demás. */ +export async function serveDocs(corpus: DocsCorpus): Promise { + const routes = new Map(Object.entries({ ...corpus.files, '/llms.txt': corpus.llms })); + const server = createServer((request, response) => { + const body = routes.get(new URL(request.url ?? '/', 'http://localhost').pathname); + if (body === undefined) { + response.writeHead(404).end(); + return; + } + response.writeHead(200, { 'content-type': 'text/markdown; charset=utf-8' }).end(body); + }); + await new Promise((resolve) => server.listen(0, '127.0.0.1', resolve)); + const { port } = server.address() as AddressInfo; + return { + origin: `http://127.0.0.1:${port}`, + close: () => new Promise((resolve, reject) => server.close((error) => (error ? reject(error) : resolve()))), + }; +} diff --git a/mcp/test/docs-tool.test.ts b/mcp/test/docs-tool.test.ts new file mode 100644 index 0000000..08f0df6 --- /dev/null +++ b/mcp/test/docs-tool.test.ts @@ -0,0 +1,106 @@ +import type { Client } from '@modelcontextprotocol/client'; +import { afterAll, beforeAll, describe, expect, it } from 'vitest'; + +import { callTool, connectClient, textOf } from './helpers.js'; + +// Límite duro de RNF-05: unos 10k tokens por respuesta, aun con 10 resultados largos. +const MAX_RESPONSE_CHARS = 40_000; +// Cada resultado lleva en su propia línea la URL .md de su sección. +const RESULT_URL = /^https:\/\/docs\.fiscalapi\.com\/\S+\.md$/gm; + +let client: Client; +let close: () => Promise; + +beforeAll(async () => { + ({ client, close } = await connectClient()); +}); + +afterAll(async () => { + await close(); +}); + +describe('buscar_docs', () => { + it('aparece en tools/list como herramienta de solo lectura', async () => { + const { tools } = await client.listTools(); + + const tool = tools.find((candidate) => candidate.name === 'buscar_docs'); + expect(tool?.title).toBe('Buscar en la documentación de FiscalAPI'); + expect(tool?.annotations).toMatchObject({ readOnlyHint: true, destructiveHint: false, openWorldHint: false }); + expect(tool?.inputSchema.required).toEqual(['consulta']); + }); + + it('con un código de error del PAC trae el extracto de la fila de ese código', async () => { + const result = await callTool(client, 'buscar_docs', { consulta: 'CFDI40147' }); + const text = textOf(result); + + expect(result.isError).toBeFalsy(); + expect(text).toMatch(/^FiscalAPI docs: \d+ resultados para "CFDI40147"\./); + expect(text).toContain('https://docs.fiscalapi.com/errors/cfdi-40/errores-cfdi-4-0.md'); + expect(text).toContain('| CFDI40147 |'); + }); + + it('respeta limite', async () => { + const text = textOf(await callTool(client, 'buscar_docs', { consulta: 'factura', limite: 2 })); + + expect(text.match(RESULT_URL)).toHaveLength(2); + }); + + it('sin limite devuelve 5 resultados', async () => { + const text = textOf(await callTool(client, 'buscar_docs', { consulta: 'factura' })); + + expect(text.match(RESULT_URL)).toHaveLength(5); + }); + + it('titula una sección con su página entre paréntesis', async () => { + const text = textOf(await callTool(client, 'buscar_docs', { consulta: 'cancelar una factura', limite: 1 })); + + expect(text).toMatch(/^FiscalAPI docs: 1 resultado para "cancelar una factura"\./); + expect(text).toMatch(/^## Cancelar factura \(.+\)$/m); + }); + + it('no repite la página cuando el resultado es la página entera', async () => { + const text = textOf(await callTool(client, 'buscar_docs', { consulta: 'catálogos sat', limite: 1 })); + + expect(text).toContain('## Catálogos SAT\nhttps://docs.fiscalapi.com/catalogs-info.md\n'); + }); + + it.each(['factura', 'CFDI40147', 'modelo factura', 'catálogo'])( + 'con limite 10, "%s" cabe en el límite de tamaño', + async (consulta) => { + const text = textOf(await callTool(client, 'buscar_docs', { consulta, limite: 10 })); + + expect(text.length).toBeLessThanOrEqual(MAX_RESPONSE_CHARS); + }, + ); + + it('sin coincidencias responde sin error y sugiere reformular', async () => { + const result = await callTool(client, 'buscar_docs', { consulta: 'zzzqqq' }); + + expect(result.isError).toBeFalsy(); + expect(textOf(result)).toContain('no hay resultados para "zzzqqq"'); + expect(textOf(result)).toContain('listar_operaciones'); + }); + + it('recorta la consulta que repite en la respuesta', async () => { + const consulta = `${'a'.repeat(150)} zzzqqq`; + + const text = textOf(await callTool(client, 'buscar_docs', { consulta })); + + expect(text).toContain(`"${'a'.repeat(100)}…"`); + expect(text).not.toContain('a'.repeat(101)); + }); + + it.each([ + ['limite 0', { consulta: 'factura', limite: 0 }], + ['limite 11', { consulta: 'factura', limite: 11 }], + ['limite no entero', { consulta: 'factura', limite: 2.5 }], + ['consulta vacía', { consulta: '' }], + ['consulta de más de 200 caracteres', { consulta: 'a'.repeat(201) }], + ['sin consulta', {}], + ])('rechaza %s', async (_, args) => { + const result = await callTool(client, 'buscar_docs', args); + + expect(result.isError).toBe(true); + expect(textOf(result)).not.toContain('FiscalAPI docs:'); + }); +}); diff --git a/mcp/test/fixtures/docs-corpus.json.gz b/mcp/test/fixtures/docs-corpus.json.gz new file mode 100644 index 0000000000000000000000000000000000000000..42d271b5b5149ebba4d1fb3f50999bb265d1ab99 GIT binary patch literal 763984 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
%_gg#y)m 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
FU>ep^$7fMv9(a1+>mLZ7BENq0= zzR1Qa%q$jruqqCfWXwk#*bp<~@P;kt!v^Mpe)(T!=2-8k;OSq{V_yyxn9=Mx~xnL%wdnFNfK2}|X!ONq%^^QCvIp0oQ57CtZbX-a06;1gzFjIWai6)j=* zADc&iaIwH){C8)Qnkg>E9jfuKW56$!%Qp~K6A06teO>Dl_JbeEk3p(|bv(ye2^$LT}VaU3+(dC{m+ z3*Y+-nGVScI{L9pY$yHzCU)J7SfnMv=MwXUmw{VNMd*K$8)3XxS^9;9 zEev^~FjVlEFY6rT?n4Df!}LnhjnoW>t+5%sH-(j|PIwt{m#c3;mPCocI28BH*QqEm zsK@Z}^+c5;7*$`RJLy-^_9(B#s{=HKr1YFv>3~?p6uxsH;8i0(9>2zoLnSNe=qV#; zHyjkLpNz;UayXZ+r-%a+K?rCX!7Mx3isIDW=4Wg17iQHyN5zD58oFRQGDo2e zxcj3R;RDGzCj5L<)sGLX3CJ~2q>Ud!`!dNHV$Wgjgfw5X7h=_HWo@T`!I6pkT(j{q>nJu zYAcfIb}bznJV89mpX);E`-UY%ZOJt|&?0U>+anxDFJ@C*%PMkum@fWC#~<-oDsLx^ z?iELghRn$tk;pRx+pb6-b5&*X_tQYQ;Nw%8t$dw`k(g4L0`diKb8>5&N#fT%@ToFt?CwO)}_$Y-HwCG02@B^3CNy$ zWi>fVIPG!0G**>S)H+xf6dfd4)+NrMjX&CAN>juDltZ+ob~6ps?oRoU77THEi zF7>r4+o^lT)LQ&=Q5SKo-e(w1TiI^gU#nM6gO5EaY`C(W%xpLrcyJg$5&awM^qY5= z5{`+k@Hea*EhDXjNhZQnE?Y8TJYwuLaQj8VlVNxi&x!cmjd>*Y1;h&`0UK4O+6MDe zp*{1`c4|psI0}(`{+Mm@k?Vr22=??Wa#$ zED!{fYT(qu1*%tgZjtA%*p4r;5Cn(DXU&zYlIch)X(TXGvzy!*vRA^|Q*$cB{6Y!_ zmhG0n3l{@R;u-|+5%^2BmvwM0MPRfd`%oQf?BYe^o6Fax8(iOZU9WlD{>^fVIVPE$ zkK5zqYfOLMRnhshXs%x(H6<3mKmDS}CN!3}cS;j)QSAt{bY5tT`&B2Z*S&1Hp#N-> zxtl|yg8GS8pgB}@ct&pRAe00-(Ep-}?g+Z2VEKH!wiX>yTBNF!3tv18cg;az&d*1n zU)W9|njq^ZOmvJxB#~ayFEWgrPiDB%iizo<%ZA)l{f%%TxUm_jAL6q1QM6ZJeUUGo zzGLa>@Nxs?z|)Bn+_XfprW3ol%=i>g$1>Yd;8FOopm0u>cu$uYA?0ONJ@2zq-m@L) zf}hLW>??hexFPSDtYG)dbt6`GXuF1x&D%IYeI8<>+-B0$qT(!vT61FC94*OGaC1WL zVq82|;V3r9J#tP(NFXM>YdDYjOC@+JLl9>wp}-)^2*8-#1MEf$Vvu@e##5*vFKbRe zlw0?u&maPAH|m}-Sdhdhl|Xlr(C}{C1FUS1n*1QDR~IJd;z>xG|DrfWxj&gbdjEp` z*L`CG>ynF$eiHRls-JN!$}187s2rg#Jy1+#!5_a#2D#tJdKakyG;-nrIRU$o^TqxIx+;6*!Wmu%!5W@2PJaF3&|!58eq+AGDP*B(7?xEGPOL>7 z2A~Gq#fP~5%zYPaeui2BgEbO}Vy@LJTv{<$EY+*7m6k+RylfZC;L?i)`vI1>&G@iL zW`kL}qrmVLCY;u9OiW@f)QT?IZyv_iQ`0<3t#xOtXcK8U1!5cOXJYqN4_sAT51*CXi#Q)L5!S zpKJjBQuv>pcks{I^0wYu!>|u=#-Lb{clu zTf#f4JzyJjTCSmLJuvpdLwlq67hqv4Bszm{cM~NgSXeB#JS|_O=yN;<|HM?Xe%2|_BsLT2h_^0jRzj%}~4-Ss-=(7L4H zD!7laI{f%8Z$Mg@AxQz%s~>7*p!wO|LLitqL!VPNAN!Bv%CEfTmZeADDY=fI31I3i z0yntL?C&NJt9RT&o)a*F$gWf0b?1eOe*L&Eet|TmpJxV%1A6QPIXc+AXXo}6C|qs= zIeRdr>ae*+9!mjH0Om^P);tN4o+@?88v~Q)fKy6N$u_~^RU~w5*DVARXi)6mBjJ0){clV zN)3Gp@_kpc$9`N_T~`;ydgx@xQa#bFN?eSAIPrTrfvk%0I@Xz9xJ5G3M_C^cC~v1B_@`~02=m@> z#_(bo=J0H>X@6|0fTuB4ggSN6(+$b`HG(F+f!>*MU$y zUc}rEFwGTELoPxCb$UwwBWpL z7}DNSx!WF)){`Ggt^Tpp(bt}S8Iu24YR9Ws)X;DBGs!Omc=6>HIp*xEdDoyk-DLFu z6t^@Ay4xfsb(18dE-_!jM~z)kF9XW@I`SM1#) z5Zx95yN~c2?~w^wWqKRbC3bz>udsadGR0N+Nr=^aZaOu3+x zrHf@^*9@VF*x6KBVTx*BkfcDYI*I#?u_ykzB6pDRQqTxq!y_%~E@i;|sxJQTgro0N zKC56vXBX831`^@&!9}it@Px^hq>uPviyk+C zh`@(-6<9-I!K5=dL^<{Z4A}>08R0x6ocbj4cdxl1)jN7l-5bu7z2MrK8EP|ssXZCq z*E+!wGRodla~8fe|6`~VJJ?QYCHO(1W-TjT-P@pPLM)OVGvH2?GVEA^W3;kgPuOOm z_rEG-mD`IZjBaw6u$bF>tQsCz&*SMLR5aXw9`oAnR~X<=ulUmp=(N?m(n@LjD(Ams zy!JzE-EN8jb4Q0&Fv9%Fds4PSKlwTL4*1^7Ewh-TI&DPj-M$2{=$FA(^uxHpS26DEZ0 zO9xh7(5e)K+6XH7D!e0xnV*?DLzbw7WjMr-q)kY{YpXI(ZRK)U1Q|*8EV(R5tBFF_x>eR zaiHXSTSD}H@-GJ@FArll%n<|WlEM{l7IiLee^6lAbI|&iH$MxBoZE-$bK>ahS^o0L z!`Tc3iFT&aY$?}{6rO)m=SKG-)yY}u>Pg|*OgPEsZS#rZ%R~Dg^-UK$6FQGiDO^h* z-qh^K*x?S&!{}xQdqc+q|BTJ$& z?Z;Q+n7p6$^-*I;nShNm@xS&{kAt;Q`4NLjQ@(oiV;){hu0-fE=4~xh7z5^2z5xHB z%PNJK(X9$GE1d4eLV~FSR2I+|=a}s^g+1|}d`^@38I#GLDE=W1#qWq9&nrk4FabBCG2g;86K@*>bGmZpYoB(0yX{-IhOQx zG`=15y_&{W1Ne$FhSa%X$j@@xjv|E|;7OxH4j6)0^hNs`$FVQq@?ZVptRZ`BVcGlt z3oKm~+oAhY?alksT$)Xf%VUdBDix1-t29{(3Mvx&z+lvJHzhgClT- z8&|bedj6t0cQDLK^%SoT_H}+#4lzFY7d<+q z)C3H$#9>0B#46h%%p5$LrY#OQ_&Rw+a&)Ehg4P~!P}3nolc0rByK$mU*ws4-`MW{eu3 zdOO`tv&H4EU*#_a@vT^r&?_Vial7E;j8GgRm+@PSO{+;Aqyn%?|3$R%FP<#`3-3hr z&VtEFV4l)FobR6=D??wjuKziOu)9c6aqkfYQLGy$c)2`ex91|j>c@d^!{qzoB00? zjVIR0?mfgKfu7DMQlDC70^my%GkLZopM8>y_aN06I zT9UKvNt^?($QaS1yvRCHxbB9WH-g@f5%qC!Ldh&Eg6CoTN&<(kcnQ=Y75Vu0v?Ha) z>XC2sMQiqSB{A&P>#|alw#6EbQeJNvy}R^SJ{@P?HrD|IoWoQHP`-2`8!1QGFmww{OYmg3OrEh>%PY?@0CY1gR`$z%ICaY{1DAXH z?xG3g8!}?FQbZTGw0=xYFn^_jq`yUtznjiOwE;&>yqPy=VnF34lwN-{)qL>p-NX0A zZEl*tOKk499{*D+>>{Dm1OjiEIsKc8v&iLG$BO7=Mo0=PHJlYXZ2qQ%QDDw~w99YI z9*MAx28;>Tm5b^O0xcZ;a}Voov%9pW9xwK)7P!SGQ{>z?sS~=`%5}nlDD5xlyL6K2 z>e(UKFbBb_LMssrasFf+5-P>vs_!0H5))prtdQYsDweow2d8VtQLT-g)Ryco%3KO_ zGKu(3Y}lLT#gkaC$r3mprLj%V?CTI!G&U0oU3(Ntdpr>)Mh>%c-Y(TiZ>E)%s zwOWxr;4I7J?WP2A!ow#wT75qhA~qs7`sEH>%}HdhZl(ifvRtkqi=s7HfUIOT>WO`} zeH}6v$>@a`b%*>>JZw^mWi{*NU&66};b&Wa)UHEzUPCsXt;Vuh-fkwQm2;MPIE+`l zmZyMnRm|FE@)1=rSOgDL0GcTPwqD*Y(5ywKp|cwUnGQ6(=k2#O_s(o^mT=bNa%HR} zEw6U4At>BKw4y_lNgcbt#*i+rG$|sI`lCSAen2U5@p`v=C@nP7!7sYiwJ)6JrZ}W= zb)=7)bp|og0yQAJYb$Sxtee;}F32W}TCrSq2u?#c-H4JNg;c(SdCpeVJ+jRF#939q zg<7X>u6Ara%41U8;%85d6t3f?wh%@G7n3MP$@XQF_pNoKIP0m|kWIxI6WWjuf!!UV zNUXSDPe9fZVyuUM#%CU@e%_3;4%39lEaW1xJUeQe_BZfpJnkN{)ML8LM zFHsizWZFro+PVR!S~tbdEW@l$SgtACd_ZAz^45Lvw#5obIH3taD^{#_`EfzpaN_gl ziH|WVEVFr~?D;``&A-w#q1xLFUN{PrJdY36gzxU_) z^3xgG@PoN74fn{iNxjTQOMPB$C!f|MU7?O;W&Cq2?l(i(YMocWANyG&Myxjx$Q@Z^ zJ`TW)xfNy_p1*jrgIe98*3LWZ2%uUNZI`y4HVpEfdHaQo>gWNO6}!WfmN2w0zhdY~ zq5=`rE&h-M$(8Swx|uN%=49w<_(*1v$~8Sy^M6A@i|ilXnl`VM_WJE1!k(6+$MuTGtq(*nfO#)jO_fa7k06BMP5`*JAUnX z*pgC?7F6xAj53Y2l^UbSm!9-HU0Hiw%PRW%L-y%Ow+iU3sQk%@Gj}s2p)$&|amRyC zki=~==JYWg#Z-Zfx^RG|u(`UxtHGYftOt*tel7{ST&go^O!+Z4*`OM@E2O- zGJF}O@iOwcehM7+Yf`$}n0O=(`F? zF@l9nEV`)$s|+A$QA7TqWKiK}qIzzQp(%Nz==4T{X$U6p0}7HC@FKvUNj>BZL~K`? z1oj52>a{h;%`%JO7A+MlgQs?lR#E*7R1`Gjz*-Q8G;xtB3T?-25MHd~M@~{dd74Fo z$=q~~3ZwXni>X;6Co|jY^p0Se*8W2r+U#KhE_G02bgm-mY;Z+W6%S>X!98w}A=Y4$ zlHs^r$9rD9q;TnVdBHgv=43N&We?Ww8r>Vn8@1&Lp-MzxdNsMiY!qF?-@5*+mcW&N z;=W$rQgP{mp}&pVN5$t`I7%CXw(TO+KWw6!)kt16i(raq1G~<1aZm4DjmrA~j@P0^ ze=S}KQ9cs|fh7Qb>VC@7Q(G7#=P1fwJgAh|8+TV2uvFM#xkMapjf8R$H4Tn&J1C6Z^K|o&ea(j1oTJOFRxWA=<%r?vXv6*&?yJ~RJbDy3Bm*f64 z0ZwqWmzPtEU$`O8^nTh#MesC^td<+{H+*e40F~=QQgY3r0->&8Foz}X}EP&8^P@uDyCxXHop zrG&EVSCv|L%OvK_hGhwWP?bms|A7JDFqZ%R`5+=Z&eTiD8Um4*z?8chex^Xr#j&>u zdg>vq-u`L?fo1G=9AM28K(-vm_4Sa|>?m?noV2)r)@Ibd_FEo$uC<9$U8`|;tiABg#OSIV34 z^{qX`Ad#^w0R{Rb&e{bPEcNHE)vkZBAHquadZXJu07WH|4`=wX8nItXMxcV zbzTO9#fT@!?_Kn92u#N=`hAHHBF4eO4Si}@(vMPVT>{Au!dfq%^$#*NB}K-kTbfXd zfVC4TORFo*oZ-i3n-S3DeI8T(*&@Ieuz!M9sKq*7qc4fGfG+G?9RJwegS%y#7CSIv z%;Td5GlGW(%0N97o483A$`Hi{!G{IBP#49e9jb+NvThqGDGQ7_g+Y__X0Cx7MUWBU znHT1lP|_#vVG0OcVTSARiVi~`)_9K^l1N{?!Q&+ybPraj&V#Etf=ay%J~~qA7xk8< z)@Ah=wX8rEVtm`7_85S~_!b8nB)$Otjd4Uw6B4Rkoe~r}ucsV9tXoqn!TttwL8X^m z&dW@j!N5>Zyz7S-jz;+>NPe~-B(19F_ZZ0r5W}XVuo^N}wJ@^=0v#QYhV`ECph7wm z$=}LGv{8Ir(9>Z5f6*p21=0^~W_|`jV>82REYI5`m8y*&t_a>+?ey3BWb4I$pm#d0 zIktQt!lqN5SFLXd3)X;|na~{dRXGJ#r8Z2xHQ);f)ROix27N)eBxz3Ap}q> zSrrz^HI@YC9)SVPI02DCLM|58qXrWKBm}i8M|xu=o4aiUW5oonL|Y?33+u#5=<#b4 z#(v?K4PKt;_mCT_(XNF#0;fE|v+6v9KOR*u*q1N#tM9)@Lazd#;#&J_=5}{LYgr&Z zR4;8}1nnIu{zABr*vK`i=?9EbQBesbe(TG)rGZvxH6BL!bfm)Q)7}1R@T3=X#~6-n z9*wQUtJ{E%RG`0U53Ne;1MJP}Jw#b}hV|Ui{qUILtSn1Kv7TJEYLks z2Cl=UFGGr|dpB$LRWm?NHgEVPJRw!(;FjcXpe3p&eiY+jrO^)(Ugc|)mIPQ0V@lOh zrb=(9QvVF@f^wA!Q)&v)jk(&)cz203z{?QVy$rhTaa*mN0n#6PgoR81YkO-tbl$UK z$NE>L)CTc--!w2JE~*aQ_f_u7Xqky>yvS>mT#Qe|KLJ_YOon|`l8Tm3g`gH<&gcg< zoYCoFY;~z~=-LG|ts=zwEiu9xT~9aE;oq0t$5YEAYCAC}O9u9ScABLZ>k+SkNHo#7 z_9%OJ_8eAW0$FVgNfuE`Pb#=1Bukc0vA2vS1)6V_b99v=3vt|!mZt787hv6-iy5RF z;`lHj%MHt>bY^e|fxH+i{FJPoRUm`HM?Js+hpE$=?#Euy0y67b!#J z4p)}~wU>UOtVy`;e^GYg&PtJ~K~PTrJ(bd_x^|}yffZsU2&nnCZVhO4)Hi3*GW*qv zI>-e%=tWp@R{y$UWI^7dYTrYg`&xttZv5e=uRLed!N?vwID3*T3lTnuBZR&yBX~v% zn;QVJmzH}9EiDwM97H;S49Bk7y66#qOtTYV3puP$c|Es4HCJwR?(j>ChlseX9yZM# z4v{sk9iY=fueJ20_waOWODOVMDnm#jY=9lA%Iqr<0E{{8h6_?VP!$%ad$BN&(APO( zk-P<#`al9E?C&xDx{xQJut;WZ-#viq7x6BP(E1H;O&5M*YZ9v#JFExYI_yu53(feJKS!^aa0R`^eMTqRK7vq&3tCz+5Z=?^KXv zpu)u|L_9rmzf@g6r<%k5an!X%bhmni^YZ+bGFtg+QsMoOTjy`>6SKegZF^*4Ni5Sew4 zNMP~Dx>O9$~nDe%`2b-JD2eBnOEl>c0|sCqONRV^9nTf>qIxNVJ}dchrL? z$Z7>`yDj?P95@1OQMDC*i{R=RES7*Kn^xq`6Q15(McV-s+>dkpZ7LU_=(4E9QSFt( z^UTWAKas1VsLt;Ntl2`3|0}e`UgR+n4X==5lqqO#x*Fy4H@6hFIi$VQICF#`{d45H zh@;m8gu5swKqFc_Fc;$n4wvYyI7OC(hMkOQAadJ73nQkR6?6$VI zXY$1UdboAJ8J-MWEL@U=W3u@kJlaX~z_}QQuD%K>LW^&k!dN?t9Vkj#aaEA$tPZOakIcT+EY#mEgavd zn!{yiW3gcK z%qlqL*Auf{(3gMOQpq!RK!3 z@rAUhFvL3-rUWC?lD^Q-Cd#R2M)@(h2E(9tcQ0jhpgh1;MQH2u;v)dAjRS}lT8(F^(AL=wG^46|vGEL*tVdy> zin_1*b-=b#=!Yu-gDcjP1JXrV(bIg1PVt9HJTo|68cY^}9=A!l|2iO9fN|BWm#LeV zq;ua66@@|qz?E;|D8qb_&r;7jOA)N?Fm`+liaDYOgqgrh3mUd*mGJ-!aV;W8+FPtF zLeeIanD9X5KT4xk`xfjiWyXOT|~y9^TS%gUUv{-|nTX(1dmPBK>v9gw2^N`_kq#r2oDZHCw_AL$MgPN?)Wk%oCjq&)<$@t< zqsa_}Zyzdq@kBF($h8aTT+V0UX>QdXROZS#;J-!@q%h2YwqRKZdvRxygc0?cB2$Q_ z1?=8L$1KEiR#Y%e{^$!rp&+?$%X)4Fx2xz$F{jWOV(bl@8q!jObpRh(3=Uc};g)jS zxO7XHQtJ>&=;j`T!N|2Y7kLTL#y#fnbrj60C8zK8>x_)Nr!wSw+eO5ejX%&7lp}u;BF_Czec5 zCRX0Skz0(B)>Z2=;63B0N!mK#J_I9D8Lo@)=ha)FuWHkh-UN*>my}LaR7Y9?9&Vu~ zw8{MlfgRZiA2k2-SR9*FAL$X>oB4*YH(G#ulFcE>?CJ0-oU33kgSQwWx%e(P^Iuvbj3MjmJB1y$c5~Cw1pcbqXC*o z(ld6J7!q;X@`sOti0Tiec29kX9pw1>W-FaND$_>8vjVLwh#7bLdYSd+gi6z~11hH~ zS@xq@&8~#-zt$b!sBzCwAXOKYRj)SgSmRjFo@|%QrbwK>q)JfWLk$FtFb&l;9i%5% zXeFU=6hBy%t{yH8Y`P|FCLw=xl{fa6jtnO{Mp2KH?O@E3Xw6EslY9ixFJG6xBB=cn zklsN7Rl)XBBBsoT7lh>9BVrkf713@D2pc~W0-7n@d5LwECRED_zB3{(pkz40M&bz+ zNnMpXX$E5$kQ>`ce6G-tt;2%N|8mxZ*AfN%=bc^{WJdtxa;d<#K~ayD+>Op2V$tet zBhL3B`mbCjM(3qX(>6ryWC_aDzO&+<3G_rt=w?AIiwfjq(IO^)iEz`A<)4;WbQ#M+ z3bADmd-YQS2Y>-L7uZ~qA6gzk&E^(-yq5ZsG#65w5iY#rDBGhc>$)+V*>zRdUD5Ta zJtY&O%?vi`v^b(iDB~AYs$u*DY(aI9>%W8S%~d5jXY6|OOsoz|h+A6`CJX*?=Q8jb zSjzUIgzzeKgw;Q%V?h(3{^I1Ls2-n!@8--LIA6J18&7T7E%U})YR*?IYQygvT^AK= zWB;To?+%)E)Qj~K@HjfI9YmI4-Pvbg*+Ft~aelv-ng05o9(@b9pKP4!VR>@;+w=(Pz{CX;CR6S#CB^=_#{Bgfz>JDL?b_Ur;bRkN{C)DFui+_~V^V(oN|z0qkUX;CpevWv&o!BVPE^QDSv z>nDo!7l$Vy8EOszM$|$9O;)VlmnU6<~b7M}L~c(|c7pTqY@Eaj?q(k&?5=MIM6k?#qT$XGb*1fHay zr4f4A#+hxGk;SA{O)=oK?YWmr-^sP|@xGTAAmdR$OEjF`eo zl}FxsCgvjzRpgOEU+eS6>=g^7D4r=C2H^h(#|GA+|)GpfS?3&gVjqDqsH3$^x zPa~7VGRqsofJeHwI*qnwUkmcYm{eEO?t09(u-8a+bZm1@1+;T^h3D;%R{i>}j613U zS7Y14iHYKoP0v`DXlK`PdrY0cQsh~T0Wlz0CsPslj3Ao20{lWqAf>EBNZA=*ET$!p zAC4CoX;Xg7K-8$-7yiJRX8WrK`(<)W_Q&6;$BW;&5psPwn2o7~hL`T1K zc}hf}z|p^OV;GZrlLCf4lTExrN;}ln%oYowg`vHGQ!^V_QZ=s5@YVl7TauF6k_1gQ z>6J~3qX*d>U!Xr})-<8m;%=7PiBdgEHQ>{KGr%2^Ar|JYF0(d_i4MIL$_X3m0#!|H zr2d$>dOuV~cvW_lyFW3Z8W}P5$}z*==7_ zOzEVa2O3VP(B)9Xw5viOYaB@hd zq+MK_L)9WL@D|o*cddpK+LDv)3ANmZ9z%$F)JH>Ft|!Fk^AB<9wOL7P3_mr*Ws1 zCxXJUa>)v7SeW<5ijfB;lLE-NSwWSO)}rmh!9JZFYy2duH&-#N>$K^AqWx<^(SuU# zi2)u{{Mt-svSGKCG42?hpD;0Ddb2@@>$`U!&ui|kUhxv)G`=wrNH@i;zUPm$@VH~u zgh_}V#SK7Kpik%;V_+NSzw!~phyUKyZPYIUth8;uL;e@?x1q&~8nD?uwR}0!w9<K+^qeobWy4*Is=vZa_Ba2GSmspF_=b1AwLk`@wNNSAxi+SBSU4_%4IOX5 z4#VyD`6-2dxrq5$3I4ONC^g0)FpR$MCM-i^03P_2Fa%&n2q+pAuU>3J$w3ExZJk%W zEx6VT+x;0po&^>nOHGy{ruq2LMF3~aWymgSZ*4?8tJ=QeSUWjBAA%D21X4F{k0TaSat``WNBYo zlHOdf%Q>jNaMmNXs?vO`IydQ;gw`hslBcIVs<75^DT=!8Wd zp@0jo){#&PVmwo=pBvgk8>FkCy`d5TncSu%)805C^-Xz&xcgya&-3Qv-@%IdK3g;QxeexdcGoIMWshRGN6&`C*WzN)ryBr?S6fvD zBJ&MQUziR9%{j{vzoRYp??40=v5`3OABF~6%Q+NY4NHjrgS{L+;-+5?}12ibR z4TYbO1F5UO$|3rw*fsH;!~!}87!cVt0><(~53{UM^vvj;6iF&R3!Hbd>0Q`ajGwRv z4?FUt!zvSO=AlHbe^&7F&j#!*-@h&OUqmjk$8@cLZ+KpxjkTuW_*O=ggE{0CoEF(K zzbkYPByWXU;}lw`pwwz%YJIi@Ksm)72>!uNj_5C6`tygicB< z|6{>|%g@gBiF)Ev@o6}ZHu93@46h5Ib~jP0T%5GWnvZdNDy)+Z`qlBd?qWhFq*<-s zvdHe)qc0z&@lj74J`uU6eLg&{Z#UqfI`%wH_p|Rfd%gasuOpI%R5XMxt8*@aY^hd~ zo>iNpwdBBU*mqM`(Ejb{2HNI;l12=^}1a6e}^;u2O4ZzZeKa1oLi{H z{^Ql>x=%PpP*uh-3wWCHL)!HRyYmqXu$r4NAn0471G_9t%o444(Z`gtGal$-j3nNwqRuq_;_ z-5%mItr*tCd4q{RT{=L>yhNF>c?M%<(`pa@LWzd#l3*5~Vq ztS@6zH}x0ggx&Gyg{ZJ$h**I{;wqs;k!c@_GoLs3eaY!uzR1>z-6IAB_NVp8eCsR} zG4)Qd>;}B>j8YV&G758~@=zi@gJd{Ys=11m#>|S>Ut#d-#Rhu(0)woI+6C`T59b$S z5AG{bGfS*es@CQ{soGZoG~i{~7PVTqmv)2q1baVVuBlM{AG1GF$3Nr}pUB*s7LvY2 z#AhQ!;#@X}Ksz}UF9(iw;A~#URle~; zg6*ix=jIo{=Za6vnjpMA-FLA3%6~jcbJ80q69OIQ?=C&bu|AP-o>|MwVb^ff=QtN}e7rn@|By4!(qTLl& z${|`A8n;$#bWcnY?mVT0ep3zW!Asf$jWcBF9vLo11K|Zn^CdJF8f+ss_@srdhaUsC zPm*x`XEekmgd@x?xlVQm;PBVS{cz2FTQn{99w=Y^$& zFGG`!L4V~>Ke$ill5A$PDr`PSKF;L7N2S00$l}#RHNV#&_RX?ma&OgfjfJROSzqvP z5x8Q|5y35g_MA%5#~l-I86=25K)NZ$*4eW&c}Kn&KXBxvKKYs(c1@k7XUpY#~H7i?|O=_o2 z_X9wQpFSsyGjZc*aXox^Hg$r5e~Z+QIWO->a#m9t^!Or;(X`>)7qj-yRu`yck4a%^ z(8;ZVqpSfVE{{!G%u_Ayv#7bf8fOkWIIt_FB_ONL#@lBpwbn*CoFywc$WD6`ysh&S za}7p^H1^GwQ|2!e&a6noA*ygP$Pt+jfLd`Q!$8qVx7sMww`bE9W*T~%sfrv>EZd&DgSE`Gr5Ihr2>d&eCa!3` zavs!&+)80M#U+X#T*X4Wt0q6Pw*F4fiXtJuHi>H%{XfD1X_tF8nVJYw-X^k@6V75# zlDlBWz5hW2C>Mz$6(|Xh1>}i4ikh?&Mj2$JCu%@(S~c6|1~8iyGn>GaCb0VnN~#Cc0oh0y{9yqlb1d^u^MQKnvv%xJzo?+)_3L4OoYQ^*$if$#xOVm*60BX`M4p;ZbK0I zIA_pr*7LNQO~JzkR!qzlbc`5FBZj`Xz(CXY$4q)l^`d+&v2`>G>e?*AsNENc6ihQm zoN!p`oWoohLO*i7jK$Wc3qZ$%uO5WinA71Z3p5;>y#e&?+txhZ)s8d@$3N+eO5@~t zR{KWcf)_|esq-&29(Kn;rRqvn#!d4OS^wV(!*JT*{`6|ptfYh@to(&z_Y7O(`;L9s z3yD#O-)8F$Av;2IrMzcPk)FCkYg!95k^%)nMP|}K0zjqmm8AIQiLXJ>9s52*2E0!> zu}PI-&cTi8AK}+}^HC4-B2c*8Q)a5Eb)$~CaPFalO?&;q;`C0};1ShXVQ)d`RFSpe zf`PU91kR9b;C-cOsHB-b&_l+Q2*YzNEt&O(Y^3V}!{*6QV)+`*|C;?bD-yQGDy_3i zGQV&r<5(^6T6n?TX^;jfMb8B5ak`BfDydSEM$kLH8ygUHRL{bHu-?i+rP^8VX$d+$ zKUYI*heWqj`6x#r_pi&^rmX8#>xfw4sl^+aEJ*krL*)oB`Fvcprd63y=&Pu?bcb~w zh#!}oY-+ShRE_~^SQp##(@34QL%U4UZU;KSLMsV@C;7#wbaruRVAnR3ptP_3k^Mi- z{#(O!K9JS?hpC&PfvR8zXpz(BBS~ZP`jBxAB#UWKh6GLDNQ3I|9#iin_bbKo_-Oh+*Q3D^&nnvW$|Jrb7 zTvs!TDq~qpBC_ses$Qw%_}Ag$0-I0pLCcppKQYvor8|@0jdI}~#@d`tTQ-d2%xr0R z97z0|-%~Uu+Q?$3%7`brhcS9br5h%y_Of1s+i;0 zPdGENg!%8OKWp#;-~V4z|1ff%%}esbzoXsfnDxoj#uFI$Gq_Bkb)KnXqlXY0JL*+B zwp=1VRWpBG;yUMo;#1(_%L`f7sF~M|RX250E%Ip)Vy-8ZQeEx7cpZ^x*D^U!iSg5K zO#dB_KUO`=e6lYd>vh4Kok!ku;+u8XhdI)z{)~BZUf`V+nYrM%^OgFlyzG=VZZjZP z`)1=O>x~{*Jz845%(OaLeP?khYYqbWN%`hw55ERva0g`64ShL+*tln#8XQqy1Qt%laK zT_9TcH>cpF4qKk06BNG2y5Ui#8wXWYu$Q6On6{@)jaX#4h_@_K5u{o)SBczV&W#d` z--b1bWuCE;HMfl7oTHkPahrwz6wMu-lf*haa{{=jP&0W+Ph3KIY)Blp*u;swm!RVF zSiZFULGhqN&PTu06$y=_g$Y%}xe<<@R2025Q4cgV<(HEB6PL%~=F+|oD%@HaTbfdX z{&TRdFxMc4!mjFN`86Xg)E|ULzLNVF+(S)MO%K-ZNxB=i289s&B{2WhGowza@#!C1 zW73Ejhr!g0(rR*E9(-V>P+X}F4j6xvpN{qf42 z6N6OwBgK6myeIRNg@Agc0%N2;Z3t|;7~9P-9wQ`UCF|s2fsMcSLr|UCCl$w4=jaH1yC|=u+%&h2yC6AoKn5 zF%1pTZ9wyCh!7`m6AGcOTgB=9ifx{h3<$Lyc>!&u@=zbx!o%`{K-_c4_n{&x7j=Lp-~}04%~$J^&*H zqPTtBxtHMmwm5RkDAbeyg;XhSG0I`6*W}>c@e)GJUbVi?jy&jv{h~3Y?qR46@BSci zd5g~T$UUljf@0A0cTN48#n3fV7J*|qMLv4k&S*mwYE!k1 z%>AP#^V^lMmVuH*m-rO_N4_KfLM)!`0-93ZPMyC{QFdVvs*qv8dMp=`&110&4AD!g zL!j1iH6Q%utY>Sk2e|uwE{sBzwQPxi#T~cCb&1O$TN4CKz&xmoX)7>W!!6FA$1^vC zAEXdHsdw>N!$GAZX!wTi%n|2(51>6VqLX4Mb*+Nwd6`;y9t^wG8jEs#Kg<6Cd~41veIBDBm% zYSt(t%IX~U!hA7*Ri3Ztu(p8S0p0`+HXBnykCbhoP6(t{heNEw^U<^=OmrtimgDjf zyWiPo^Cn@nwgWMi_B}Ad-|Cq@!*i2S#k%}0&?zfn8ZX0_jo`Lf5Bk5>jm@tYZ33Vl zc)powH4!E+WrbG-!zZJh*I8`eGNUsVN@F(fX{f!Rn!*7_)4GcUY)~0`L4Xx=#+V3= z)fI*j7L&_iPK(-S;;J`~pw*g}tgW!_Go%&=55)vWn;iZ$%e~o@i>?PN!X;if)!M3L zi929-5HxYAFq)g2mfdPeLeIKrNfs;fRPaS
8I!vy2*TZkNpPekKUFh+_0(ak*G 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̘Sy6QApss&508)1Jf2svha{e1dy39Abtu$JNO~u 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

Q88(gm#0m{|)UZ1k+K=1oWQV6!jATj0;ywoiI zJm?;2(~nvR578;`9GrzRsn5q2!mr2m6!RxD7i0gT`nQC&^+P~ijV65N4{WU#Wg z_KWmyraTrhz#KNo)>o?F8xW{F{0`se^K=2_q#Z+Md~c_#s{$KYTI|xjaU~@P_nM8f(*ju2IFXQ-Fr50P;j8i0e)>#lWNBVStnpL4@YLM(TF0CLlY9(dGBhj6gc{ylg zD&x?RL+fZMQjmqa-jpcguOb^>H6#dC_OxD%y=dQb?icm=V%h%Ke%8r&u@HFCjui?v z5`JxWdB3O!OC5Z6i^cSAgVcN_cJ(Sd*_7o4@Z&jrz{#lFyzRR0q1XA_m&uM3`*96P zuqvq9hdJ~Vs(`s*vB@%L7<#Ew7l z&Op2ioi$FfLR=gqwz(%>Zt%JVE!iA8mIc_Ls<5jlTvKeCF7r&Vk{UHNWp!nG#tFi+ zH+SeY3(t~h9>yz8ZHvDY-46#lO{eHOvudK{$K_j4v4YMZL*mvo6WMs8WRWLLloc$e zLrwWZHjs~Z|9WOm0d}W0p6rR9DjJI``I1YZZ9JQIJlk_riDNPi&7wMR~M%hr(w*3~_AtQ$E*3d&T|f!1!4ytD2_ zotGnxhBVT>Bww_eZ)wrU)&;9~I!S#XQg3G#D^Tv5btXUi<1LS~G;Q+s9^@=G6HL}c zBh7^{yrfTiM%`|sdbvB?>}7=xF9a@x7bwe7OUPPwLWcYuElus+HK{W@oFny0Fze`& z#J8z9dP8=V2ZV9Hx`(etirhMG?RFy*gCGdM*vnElHPk|WF0V~rLQ)K(p#_F?^;jLiS~H>X zUel@DB`7(?7s_6Znpx~6*Cpi?>A5yul9OV!EfG7>QJY5QBV15vKx_Dw&SQs(5(jvs zHuXx%6q3WS94)iDJ5_NNRk9>E^3bH^)v|)Ruxvr;$ct%uY(penD|elbt7$C)m)xj$@g zchAY5VZml#CyzFM4#z?{mmyc%6gH|(#-T_0=V-u1R_gG5U9 z!WKfj^*x{{&OsnEjzZ4+F2lTx;#}CdcduW`BUOY{!?b>w9tdO8&uMON9D4`@lvnes z&_v}iZGmym2m%8s!QaFPV$XaXpY8j6JP+!eyQf0|*AB^X`b@pYv(NvIUU>i>Fcb$~ zqm25eo`3>y`$E?cY1K(>hp9AF!jD|yk2`fQ<>+{xp#YNJre&75ZH6$^%(Ertj$N!udcK?VDCl#>`dGhDx=^duvj!A*!iav zW^oqhiPHJK5Az^C0kW-y!M~Lxv4ju|Yk9_5zQc3;Mg#o46Xz_#z>%plQFXlmbMGC6 z=MnwA;RwI8tP^JPI>O|ABY|SaCNzEOd-*4H{E7C!eE*O1xb9Kzfqc|O86b2VrNOgJ z%}_IC^Ts9i?KM!>0fEm7{9RMCLY}GN+PnXf0LV!-SK=O5q8I6FKz(@{kYEBwKhAl3 zt0)=BsHk41bCT29Hrg?pndrS@PK)|{HLTYa4R&T(z{}^y8Z2 zom@5boiWcu#m%04n@EFUt{2D#(JZd2ePM0dl-XuJUaCC^KJ7|sw&~aiOg>u-GCR4$ zh^}e!KV4~_2c-&*{0ztOCeGmX$S^E_o6Bl2aJAokq&;=6#JF}ua@>2QFKI`QB|G3h z_VJ=L;cXXJ2n?8)d0xwShW)ez%ho&>l~fR^CQNU}Tyq*9Ph3{Vm?tSApF?`GQvpWB zb}elU=|OrA(x)@da_!DLz3EpOl+^GqbOU?gF8-ogFPli zL|;%Tw=?Y)O_qWNP;ad@Tu_I{1L9!eLQFOSU1%EVz2?g0qU*B_U7y$sEHIGAP%!2@ zM|7bU1Z}^&#-r5YI8V2l=#AmwT$+YnQSMRZQoNwvsw>M%c9BovtmSriuTal5ktPnN zU^wZx0?>M(!zM5L`DijZ=|#?qn-b<>6!9jh8Wl6R-?^GxF|7Kp;1sVy9_l`UwG4^F$$sb(Tne>ijmlzD^lUKvUZ4 z=C}#otTwhM6tP>z5M8sWV-hv`x|sq;G3b%}J8CN8n--33fqXb!`B) z;Q$K82cr4JiKetCRi+Z8?<7pz)>$g?z`)G!_9#Hr^IXsN3|6J`M%w3jUiG}*p5GXm zH#HlJkIoy2=m|z_KRJqB;Rl~4eQ8Y@2WXuM^mNb$Dr~K$bc!i!viWKZUu{zlWX||F*N@?YbMN=W z$hB1$QyiWk4IrbZ$9|eec&h?uv$h7O^LWFl19*FjiuSs;ty9mHb&Y9qKdtTBN9CyUf*9kRQSPz_k-SYWb%&Fx z#46WrB~Gs5#cteq9OR8TS;r*=n|;97dkh2ORiL1!mV{tfkwA8SerGUmpQhPW= zU4AeJ5Cr4Sd_J5DYTdQtv|iODn_cdR_1Y7StA0(JHdvLyYfTri-_vAM1T)k@j>AbY zk58PGub>@Pyh0!hvinw+M{cd09tttGw4RWwibNgDsVMVi6S-xiaD6&Qr|gWwl7<)B z<9y0r!!-2)&Lq!t(ohl8}{e6=Rxq%bc*zv=uQrYL{oumnX3>2 z!1aI*0d$XLp-53=!5hk$9-`1V`a}wean9Rvkf%wIbXCUzWS)@D3s(prQ=j!c4 zkIe)sx|=5s3OVXQlPuWkv?#|&{J15vHZ&w1AGw9SvS(=z@NpbxKKfY{KfRfLz6HYF zaera@3xR~&-^fB>Zv6dv6}mj4KUN~S>3HQ~nLioag!gk3LZjJF9)Vh7z;~o;-S{}e z=ky-?m)_^$^B|-8mFjO{xWHF{w}-%d3QKJ5H?a`7TOihbjo#1m^zIRly@26e*R_z* zYe#_JHEHCJJb_Ncw z5e)B;PbT42QA6Ixsb^=ZUsW?9X^|+r!0>u?7h1BGY;MDCxs;m0l(#{a3#Vsu_uA^6 zZZT67&uk1-C&&w!l@i61;7L;1S}r2drzC@m^>O6oCVC|V&)r~@k5-KeEFG5aE-}MP@=n=Q{Be3i`Wo$C5t~rCo5l@j2_FC65;OX>Z|5@=Tc? z-QHHXwDd?hMNL;WQB$X!UYP@u=}3}|)wgG)Dj7XQh(C!OYtynLp>&tcwwpC7Lr|Y9 z7l6O)3d4q`f#UISEL+Y@q_G`+NJ$0r)bg$|cP#1EsCF0Lvi@CF=EFfYow7*krDMd# zZEzuEDTU@f@emqQxM2Rt>aX%(lRQ6&J|wAwcBexPU-&b#r{p0MPL4v^LzWoSD3KY| zUVQ{3Lt``#rSUb0f!-yn!!0^L>m8bDkr+s_;>uTUbV!-BaJtJZ5x!F+7#`&|bH}6N`&XH8xJwTN3n6!>b7~58nBd0>rv^^$XSK8zr#cFl+s$y? zBZflZtW?jYx#fft>$Ipm4KWLvj_oiY2Rbytx(bg@)mn+Xoc_Jw#x{CZs`@jwPVyqm zve9E!n;S+=7&!Sn=pZ{#2thJ`<;nzX+4o+{`1=VzxLryq6ke-+W*-LhuI*_Gm%Wui z-yA(FiVV-mulu|pRjPQ(A}hx0BFB?cOB8j!E)~xu%M;2s%5qfm+(P@phEocwQB;f0 z)-AJ;_lV03Ukn+Np5*(qxUi_m7_+CivaWiP8@8-TJsyju0>g#Bz3PgsORl4(I>N=L zNO8h5RDn*{2u~RyWP*+J3KjqC0C0k{u z%O;zt>NVWrJD>C)dmYs9W5xT2n!|hwoP^`)X}MFD=#x5TB)9@2^+_hFQjz|mi_l%Q zexZsGvkeOV8hKxJ*^|<=&f;)Dlt6|i6Dx{LL>k9pknBOO7TE(xznR*TmE#falR*~u zlPO4*M_@GDh4NlV{J{;+tv)3YUnehkY1V81n!FnMZOvSBfu#!)>q_%YHYTpJypkuW z2rk$vFC~tV#Bn8oSJ7L3%zpeJ;8=Y6Z3)#iwFJ!*8}QQZc%GNO06RG8Y0eI3l>rv; znf8TPhsl#9VI*M6q(5JIwgvsT+E37NUj!W1M5y6`z#4ea388krtd|~EMobnY#Pshz z2T-WK44h=nXNkvN)0KC#(kO^!9_c17r|;uc4VUB3+tMYv5M8bVWVWa9lhxS^e_wJN z7P39XOB27xe5CE#)9A>V!qAcssEyiY;}Me#c=&xF6-j+&5$3Ls6DV@(aXmB1c{F!7 zJx9pkhG&y*>5c`{X*TXEk6T!gxhDjzzOC$NOh+gLV$kF?k@fa(wrTft0G0F2xewigx*ehjg zrs1%c0xUS7$%z52+)}Cdv_v}iyb6E9xr%*ApusblW=qhEUA3DfS@^Ou{qRwGeb z=Wc~k+F>G&c0FRxTnFK~TK5MXjNThvpoV*fcc(oUxX$(C1Rq$&L090TFpt?;5Ug&6 zI%7)UQ*{a+hecxA=&dtX)p}Y!GPGwTgAIOFPH!Z$1i*w1@fTE`2SLWM!!F>w5>qu= zdcTNl3|oAdkmdyxnmRI$Y_{V*CpUSpM`O{CO~x`DUJ^2KN~M1dW@}g7?IXH-QEQPGWHZTf_BEA_}EY zH!_F8!j!F=^qTj~t`hKBIvr?%QRmjO3lH(`8g09IYhW)qz;W3@ys*$5;T;%TjelFo zc?T1Qt8Xd-qt5c}&gb1nu*G`F@<1aGGqiOc8~I-3r7qOE{7v`E4m;H}RGskBc+-k@ z{B*te9Xe$oI}rvnQ&Cl;9tT&F*OD&LL?HOJE!V69%_c z3K3c}qXOLOs^P%V$4ZCH(vo^7vUVAR0eK=)-g?jOj?I9m^$>pCx$xJNsW5N=c3=#G z=Vvh($m2aEFYno7@%D5Dr+;1B8z%v9K%NKvCvB+dn$f*2&9$Z|hEfgqm;%enci|c? zSRi?A5@jIeG#;-t$itrW0j0LlD`o$4bW9Peh1^Xo-*q?uwGp_I8|3WYi>!ok0s~s3<e@6k=}XBr?F>UO)s5ZPPr(>oi>v=1%z% zm)$f?x`Ul62pP6YxN3v8X!+dSEO2xqdZD5rFm>iL)^sH=C(?Y3rf7+~*0TqUsjirD z62_O)T-VXvDKxHkkik+N1nSxK79Z7{C31)Sewm2Sr|UW!o+jBi%YiFQ8P@c!tVo zX>hsP>?;0jO~$-U(2_c>7SAt+Evh@m{mPe`g%r_gJg1NuM@Tdg5*N$y67PKTl*4gG)4gN zlVxw9zJ22N^u3WUysqX!N+T$PV*Ul)0;Muxp-MkM4Vjp7w9Ne_Q5w9S(7G}{#17-A zYo(Rucm{LeL<}3eWma*UWrz)5*e-N6#)>hjY&LIJkHW4?R*N)jBOeabCIV#o?1b15 z@s_1TRb&{g$YH>TvEX@Rt_D@f9p~90>XwUMo#Glsb-6i+>;1FVK}`mfC6rW8^0YRJ z%TR(hur0DFg`aa`kb1YB5fid{!XKf779_yS_)WXa}rLab-uJ^-672ONa3 zf3(AUMNT9GNBcx7^Um+N|7_W`bzk*0grhR!D=_2M%=dhWbzUvGttIWEZmsV#f0Z~Q z-884m>aH4zX2bxS@aHr#T1299SbceQ7%trFL54-M`YNRb$`Z(1l}Db`bzF z2-cD?%?TJ@_c_l9_~Glw+Hh$CO2_{${jS_os?zL`M~^G=@K^v6*7Xk!uZs>KjD&s7 zI;xU5*%;0}qo2#TA9Hso-j^NHz4E_l{Z#qRqW*O3hA7!f9yRu*dkmHi_Vz1#ij=b9||oRQkq-RRw-DP{^@cw1`MA~`|fL6Xqg9XdQTYUB0jP+4szt~F|Lx|bB6 zF7_aG!;mCA{iJ!6m2Ncl!aW&$@|4dU+A0i^qiGG{PV%nW3XoBb$C)@RqrI0L2X=2& zZ3Dsa_$GOv(4mnIB0E0G(beM>2rEP}x2z^c9N?3^={PjolM%W)L$zVDH6v(zu#?5T zaJ@%(>(RETc>|A9J~L9A1#iA-sdpv*KH$nz&#JGv9R1jg(_{-OZ0RoKgEAAU33^4U zg-CWT*{g6BnXLQ#m2gL6nj`PndN#@YIqm@s{A* z@EjxXdT^K-SII$!^ZD*Mjfk7F>h}_CXUT%zUwm~_4NkRkj{}yKW3hzwAK!_WxHqzX zQVokFkaLyDW#X4`5wREG=KRg$s|F+X7kl5^cXbuT8~8$XMlDeMcm2CIA<-b-i|qcm zHj(u@kzQtdDrKFM1f{J(_x%p4Go)&;x(q&EdayoE8k~YMFEYh;=9G-q#NQbV^Yg~&$4IOCC8id@UG+2=97V32= z-F*3`iq_?Z#p5#K&`AcOaQ$)%z7ts9+^_FNmsZ>l09;%!JnO9=Dt@N#n-qrjrjE+w z$AOg~o3nc2FIl~jVR$9mwv`TZ+=NKq&z1A9y;E+AZbEpP3-?=wKcSUu{eiQdS6IO? z;o*Bi^`a%}s7X3@tt`XXb8i*tGn4H=1MGZ=+8^wF`=2QH$$_>0Pn3H<>-Huqvn4Ht zhJmE7`$rZ-Z-#dShj3W&j%3biT|dILX7)Ad)_@WJ|T)ffH{A)7OQ7M=nqx!2+l~xBq?% zp^_xXNGxuXS7b={ecOdSXyG@^0qpn^%DE%@wd!-w2ZG>3J*2@;QCO< zmT7V!W*PXhR2K7ZOY6FXKVZi`F+U>bt$?)!DW-hB9Q*Y;3W_gvX(6fFB~n*xkI{4M zUHPY+*yxuHUP?69le$qc@tL9e*lm+Oe}x6l*a{0jepgSldPLVfN~6Q_3-gK*=CySf zctUBM^-PVMo06MRq$5#4V$o8p+SeEBGlX_Qo)Qu}N9Y?{y@e5E6l@8a8^inG8HASj z%{nI|p-cfFV&E}WSm9O40q86JUHdbw^?=P`b@Y8Ss1#vAR1`+@FlFo7h9${S!cOqz z9XMNZ!f!U#qmAv(`Ws!65^RK=JEHRn-WS9{ewq-HqG~6{=85R(TgS#UbWX165_4@s z^e`Gj+>P1}8f&rh@p(t_%vSV+vm2d;AMAh>WBx0qJL`EL4Vqj-f8`P2j3;B9)CyR7 zdsxnl9?6{v5cFQS7?6QCV`)YBPoNeo-w2HOUVK^Zjv0p5pF^Plvc1opza_A|@nro4 zAnd_BjjB0#^S^%wbzAS^|57{U8saX_7ddjGZ9d5(PX7jg7i$9Bvn|QC)H8cr+0z9U zC(dJ|AUn8Zdq}s8-tJ4^CE>MAHkc_68OGekzcVc3gN)JO2*7wf8XpWIp+e=*=(O!q zoMh`iS$%!rmC$K~4EE7FvUh_z};~`^k|X{mxf@a-miX z>)6gZU+fN104KR~Ui=vP2Anl2%QVa@G{&v29;83ul>r!n_D&@%E<6<5Tb_5AXas-7 zl6B5>C<9!a^vhG>+_Rs+p^x8-yKz>g=-kC#Dpai)2Y4wl$Os@x8G-&s8o>T~v^=$^ zx76V?`3IUbt^$W4h}2kN)X+_LGya;FeKdtx91-$1wOQE5F|=iw6uwRPSN3Jt49Hu; zZo^>;vi3OHZa=L1D?1XKr$KU%F@0XJh&rcg6{^*Unk(Br@q$WIx0vNuw466ru?`DU z*C>!mSvN)_!&2S&0symZavHs4N()&j z^UZJ%$+HaJWE`Si#5yjSgeU0C_rb=HdAufc!bw{jrkVAlS0~|?g@<#|4m&>Yw0&aJ zs8d=m7~&+?3)%8T0@irWI+ZObc8tYY>EumC*9w)lTZdao)4<1k*oM9r1qzZRKN%r^ z8T!L00Mq&I)NY0;NM7D+9NI2P(&csleFc1gg0Yks-E18F)9eeQta>2hkoC>|Pm{m) z7t{z?y&z+&;LQmXQOj^o)e#ap`Tq{@njb$Y{lipPRg#P>u=4!L04Od|N92tN$w4VM zJS|=tV}~%xkA-VEoUSu&7(}n$7L38d(q8_Ot$V#hT{PK~V&k`Qn{E&%ku2OOB|nwe z@OI@N+?=S;$vQmXtJHJY4z8|=viXy{)GV)+Q@s169DpGl9w*y6G3R**@#XG`W*IkD zd^4pa^S|r&`(0|DX1QmK85)a<(<*4NI*jCL81=^DNqM0?{Z2P2Gz-+nH-_7Dvc=xi zs=@y0VWVVe=A6%yB3`Sih?q-L!3WiwnAtSneC>Q@zG}zei>fP3lQ$l<3K+E0i8gubyL|c+ZaZ^bEvxa@eInOp!?;l0M~6%lXz32E@8_B zq~Jm#e|U26CtS-ob$5@nqS$ZRtRR^Q1>O_{R`Ga@+D~TT!XC&oA&Q|ydcaI6mU0;) z@yQ12uL5%vBBAas(}~u|-<9Xfm(a=2nJ}_)kl6vf2oMN|uoIx2C6>xqOma`_*x9N) z=>|$(%1X*4!n)9(c{t~oHQ4w(MXmKQQf>Tc(L$&1VFBLHB|5qLLjp(p%6qNcqkd=$ znJY(vaI$Z)ntLioZ@A??ca8YjI^!BVnczaFDu)HN&KvP((QzCm@Zdo@fRo>MyN=S3 zrj6{RaZBEACHAe^h_EnmMat?v^g<_48K(~^B(WB1bl#y2v}3#b)WrXaR3S9<@!`PY z_A_8$-=)vTL|eV8g;ZxFwyY#VjCyth!IXnt%FSs#6hv%7Sp1M}Bgr&lr1nDw&|PV5OJ60zp>zw7s3}wv1g%EiDxbnu0bgu*_O7 zMc7pUkH<6|ko2J%Dss}D(1f4<9-#IXr1ci9`3ZlfZ{HipNxaH)=>Ne~7#7 zdqBlTyZ2iOy41BR_QxpnVk3AxZhxy556n57#LtGDz9ymGR&4;A){5&2~ z9|^{*U6dg>8Dr^*I@Gqx5(ssboOwe)B{gtI9WVLCcvn1mtyYcf6GV( z6%R7Z0ijzL?smxK?$!q))*rwJGO7UUMK?XsCXE5>4ABeuOP$gwtyp&vFV0tEoP6*S-{`HEpJHo7Pc6l7P(^Oov>rx+B8vx6 z^`h#Msg9b`DIVpuQ&!FU>oOON`Y%BA$nK_Zq!zfvF0C7)qJY2omNA1kpniM4;Mz%_ zYt3i(tv-6z@b3KdsBmvycKh_RyrR`!aW-Va-BAx!?ViGy4o5fWBYE{o<#VbZ%^rk- z)hL0-Dw5V@YW#d6_wDmMM4j5C{?^wO=|6z?R+xsmYhj$fmAEwb9Jek!EF zyAxCl+x0%>&}H9t{kCke$z17|wGWJrp)>84-S#Hh8G?%TlHo^eoMCzUfaFnW&qV_y%8Ew4qmkBz7oV z$iReQQMp7?Kq#U-nYSALoA!Eh!Pn1 z8K3B=3ajD++|Tq%sPOhBT9F_eA8*ff_~-kS0>q~N1R@7*)SM6-I(iT&t=FU-xONZQ z+cSQ{HfywO%}V}~2hsuNS)(co)>R5GEXl1&Yc^T0#-#Gmb*O8V=ojQjvjvfG_3}MV zcA+{BwaX+*Q;7WE>ZgXxuVt^S2k^!|@mZ^{p|0@3gSM4jXU2F1yW{BRB3oJhkaipO zBrfP34%AOj2ln@1_4g2)?&p`?f)CTF-J#?2o?VyXW_~15s5)6DHhbyY9Gb`+hxn#$ z9JSRypPWy>kW^!?z6VvQ#`e~~Ecx#gGG8I+M>n3eZS>xFCW1OFdm2f(0EWv2)CU44I3kXiZSLqESuyw3V8&IxvTxpijj8&Jecxvu(*ZHQT9_HBkOmZ@?Ao4sbk%thP8R8m|0RJU3ElYyF1g83PpPyKOG zSWNZ6qN7U8tvU0CmOHok6Ek z{0{SL_|Jl!rB$>ZVT74cEtYH;&;RG=VEhQ&6iYNxEyqD}9#%{uVIwQdW|2v;N%ukUCq z;gm^f?-Zd=dye84m-0($RyZ#s8idvZ2j}hi>r_lynfj$^;ji7LAp+MnHjY7Q+o^j& z>;%tATM$7913%AH7d(@uP9Is!;9b3Zhj}cAerLd8z0x97?cLj_V$_~3^S5pl{q%mw!+83#|3#&h*knIr;N>94gVG7;FFTc13z*`L(ockt;lo zC)@gC)S7P=U>M3)v24zK&+i^8+hn!+LwcOn7QO4fL6)L1wLV8XJY2HRLIi%3cpL|) z2dO`T^oW+{$FD!%fM@~;fT*@TIB4}gv+#%)YH1yew!h;+xI};bL{Bc2i0qo!VI6a} z(xoW!fl*wUG{Ot$HAdsBJJOkAAU7ju_6!mFf}+)7R;L9B6kMnhX6uD!{bf#?7*-Yo zqPWyTeTuuG6uhJ9T;u&DyS4>yYm(O;qdg2$V_O7kN*>ml7@1k$33d(EZxmEdeGmDL zHTME&H`UqGm$VH8-?eIF5NC4^T__Cu$V|deg3f83?BFaQ>fYRue-BzXwkCZ93d(&w z(kFbf^H+z>4332IEYbx%n_0y?4nZ&%PB6%A@x3&T1E0$tS1TDCH7sz z$1v7JV+x5`TxQ=_N=@|**ht=N*_tgxmgNGyz$8KgQ!WVlR)J}UhaoQLPQx%PL$@ql zH%(KA*xq;jG;P~9Ng%qeYaaoJ`C0H~D{+nF?vlGxI?ImRj$&9OSpPrdO|ca~zYNksy@?8xXVb(WF8|H@QeQla||x`)4Y<@owQ>gru_(JEQgQat}{=E4eV`I z<<6k5qJGE+1%T0ku^ra`15^bEnSbIfN3HMoxJs>3-U&nXoBclg69M3^o;a;(yKet^ zN>=v%fs7TFzZ|71P{98POD6lFKnD%QKX~o$KloL^sqduvFO+N-URMwJPUS&Q{sZ88 z1br04)p@E7PfJG(Xj2vFLMQ081NB#EaGlkYjX~=K?O!nA_ED53;btXq4u#XT7Fp#_ zZZV)nDzXgp|AyuE#+D5mu%;W99$H%KC(2>gInm7;`pHfjo7Y19Nh3Bb`JSs4+d6t8 zqPng&B=54N5&l)hF^cn1(@50DBy^U^(A=ZV@qwh zC;wx4{8n&)ktfGD^|*g5VgeKN?D8Fg>MRMb`cC zHNd*;My^$=p%{h6v<=&o2iCMWkjFx>@QuRyN`Gl$J`@a6g`MrF?qW^=Ph=Gnml-`7?!#m2duFU z{-im)#<7f1;EkTJMp7wn3Pbd-gP$`;Qmk$Wji385Lec`G90gAyUb`k@kr2P#uu507 zto_s($+kc%4PYtO;mg(*_6K1>gryO91-zPCercS}YcWs(jrM?lG&crE@MdMI0UKGK z|DX1W$I`w>z-&K%!2BonR!kySjNla9(%tpF*sOs!E7V5a<3M}ux0&N3e{($ z7c+Umg zD6G!F@5}#);Ps+s2%!A+d`;Sf8pGE5RLXCcf{);GsJ!T31n}3xyf(Q=wS1CxRY$e0 zE#`lV8#<`YZpzl%WEA$$h>0Poq8gFFnO zCqVCm38wQ0X8Q}C?_-7a@>hmU)i!CD*QV8Cwf+jy0!5*v=0`E)z)AvEG?Q}lJYp}W z^wzR{VrTy187#Ii!*yPd(40|Owna5Vp}iTnpX}tkLV~bNQ2bGhZYvgo?lFSkVE4B~ z<(8dAPO0-(XZv|JRYHo2{eU|T+W7VWs~Y{XB~e`x80Adk{L>_Y&*G;6W<#aB8b&g% z4O3KB!7Qa=8AELp<&UR&J%aRxGxuFb4A8c=J4z$ybptUDI5dXj@h9I{n!&+`$Cw;! z5L`{|L_>)+rd8|My-(~c;a@!WH$cZWzb_a{MZnj{zRiJhPl{E{6gMgK_@4;@B;qns z*q;_SG?tTpgD&v6K#)h%;?%)VyNL~!hYY(zYnb3+7~&aPt-vPVC^$)d>;9W!vcEah zrb!BXC~*57q!r1r|NOAC9DnHy-pux3yw%+i743~}!#LI{)I)b5LQ-x6Ed$5xt9gvHdhPdl~FP$*%U2fO#i;T)z+fw=vF?~mUXy#D2KOJ7y3!CjH`y~qC{4F&R4G54qEaNI8wd7-BCq3qr**D{=W$l5sO()( z8XggRrI&^77T8z(!x^ES@ZJ8He=$Z_?pmY}(~Kuz5g3FqjyI-^V$tPfFV8g9H%RxF zZ__i9ffQ3uH2@!FY-O~EFX@*K@IK8E&*|$aI7S#@tki*i$BtyAaJei2&QDt`@ zwq0BrM1RPzA)Z}>mt80tj+9--e5|esw5H2JIq1v5W2Y>8Px0foipd+)?srm0XoS9U z;A{#luCjK5v7{y^TgA#)MeAA$6Y#Ra^u^8Csp$|DJZ_iu2(VnQG}^oIFN-8K-$e8L zjmKGLqb4Y_4QJ!R^+TfvEVyeQM0zntw)A49+DtgmoaL-JJuN?a3Ga9Dyey?d!`ygT zmb|?Lf?%swupXpn2elOr{oe&y@_(0BjW9e>c&cyaJgSz|k9LUk<)?0nz7jY$tNXG& ztjJ|=+G!%(wYy*hcdRLV50$0Np&fjmWIE;be)X+(18^g&AL$SY4ud;I56Gpnu07VWT$Vhd%gI3!=|Mwvd?4()a zkljA;+|F(eqWKR+rwVQ9?Wg{o=@|aWrMu4ONiIn-AzRw`i8fu3FxVU5&Er+Qim^4l7ZjfJ&RF5FJ&qCvv31M>U5`npx5@hMG%(L z`>~+^DLz0u7(2U(D?jMOZ2X-NNgc#1O*vRMr6$o`|@;l`}m)Sg)?9X4)Q zJI*_MNun5nbC>VjP2r!g5HQ;HqpK&1?B&Z%{bDq|0e}M*5&vih4(NBKiWfNP)I}+eT_N&XR z{WZr?jm%bSnn3#6s@#B8N->PIR{7mFFK1OKeFw;L5mz-su{!M7f91a=q^}Aj;mVgA zgV5Rh$>si&*SV({S}d%1e9(~W%zbl{XFdNx_a9!ui-&jw7?LcFJ0ZxVEdI;Jp=qAx z;#v8`c(c|yUXee5AoG^KrO~kfeQv>98$tuRhJ=Gq>Utdyj=0(^x|L@As;VxFsPac` z788#>1I>!|b-2od%Joy$qWzS}QFPgV`R>y-Y(dwWrGfk7|F!W6D?4N4k^j{!J< z;=+XvM{l$tmL)uC4oBR-@+z@XQP^Pv+_WR9&DP65`5pe4%w~9MLodx!K9pTiacRlW z%6dFWaF7%g>dd!Z^2nB=Y*i zE43%yHCNs~^LL4lhDe1G?O5F~caGC3qf?N%jW*y%{9yTWrO>^DPcn>|lJztve~$t^ z%+*Ek9jDtYN3+8r)MdRGDEB*lUXiQjX%fu&9$aFBdqgmEKTr9E2n61YZ02b6T-F(z z9kS#}l?IFTAb7BLx;~oJ%JM@b(aLb{sLmn78&KO6Y<}D;zHntq*q?7Uq=SV}S?0epzv@zk(VH z+(vpsmXzDv5_`trO*(E=VvEy;tlp9qTi1r=;W|w>Y+;^>p`x;|uWajd{Q1}-?5i_l zI*%DRj7J#51ikYPA9eHlGI`(|5snl#h<8YaNyIi5ZQRgpb95uR>JbINV$ z>T+DCsuXF7OAvq*`Zb+lRAcE1Z&-hkE93_t6!&BW47)X%OaO0-RX0qM9 zo$nAUXIGl_zSz01+S^*!vmG`@)Xe)0vSW*tC#`n}Q_HfndtNF+hSiC~rufKxdpTSA z^*%II`c|pck=yARc%_N-zO|6#dZD+jv}=1l-HS$X4t;6AzcMaNxh@p8y#<6M!SaTE zx}GTE#uBC6(8LEhvZ_Ie57m+6oqpJzf1qb?e@yAX(iiW>_0<{z>DbheI;c6I#3$3$ zf^MDAMuEr+Z`hs%{*1@8CW4^yc5eL|P|A4+p#;47SRKjU|DNr6P{>YCmcZP6=SFx! zQ0seg1m=rqg$=ft@*>HHu!SEFjTJ0p2z>cz0oK^!vIXO*^*2AsWeg!K?nG2N!(e8985U!-;?5&a)%@>C1&5jw znZqcsxy+j+OHioZcfsPBc`fiqvNL6o2>^=PUc+{1$W+z*hyc7fd8Tt4IV{U8@EDHJ z@|2!H+E*1?ra`B-EBVYV0Z=1}lWke9p;J{w4y=U_h?GuMmU+K3G_M?!#1iIa^H?os zO3m_`$b72KVC2SpJ6@vmM^&>mO=q^QPx!|1)|KP124h|Gp=A!fo502BrZF2i$X}5q5B4@Ujbp96DXXG* zl(YURPgOI5aCCe&E(b|xh*T8+k~~a7A+UD zD^dkrBQ_m_QYC3K%umF2e|t`|$lljACbm+2R|LreGHufac*pCHstLKXwtPM5kjK+j z-c2DKq^yVVx$EA{SygROCM|e5zQH!GydAGm8BKM}0^=Aad#*HwML)bki~yhGre?YS z4yCAGnsV)t5s9&m@Rq$?{%aL?{5>?t|8CEJ^BOIfazYpjeb#s=W@2I(xlZi<2BsLa zFS<9Zl~{iV!GWs}&S>?_w%9+HBApf1-g0|i4153T!IsA)FE53xrFF#s4fH;)be~r> zIOa>>?B;A^dDC-RQFZE%I7_yd+l!9|277bJ=f&9z&P`r511}t~w?ivwv_&EUMck6)dh|iCA5VR5_WOwhbFE6V1&as@XW!h=sUm3pIA2o&| zqbW|l+WmDD)Fge`RwSPCg^3{uLH-hn7vroMkC9~djWk4iDd~b<116^|>d#a7V?Vm( z7d<}-emF#VK2bO3GFW=a^9e2gtw}OsL~4x!tDLHKyhpDsiY4KwwYV&`I6Bd?zU91| zJ9Y%b41=uvq4wqH>2XZ*f8GKTGcl1eB-s)QeM%L9mhnf4U2QpOH9wu$YKq}M;GEQF zMa25n)%zXN6}9#b2qP9Dk!4#~ZfFKRx#05Wibf0hTf6C5(;aoFJ_3%#dv|POr`_e^ zLfeMq&!RMNp0nG69zZv>vqYURLUcOzhYoa^_|2QBYnlZT8Qhz6+c2CrdDby}|90Y9q`g2` zo7%T|Tsd!e!ZxZvFK7t%Sb%bueC?oO%X_yVQ~kkmo>1r|)(nDvKnT9fGe!MM{wrep z4CNsp(DRzE@>upG=9n?^eJ?cT%?bvQWjI}(;BF9_rVbeR?b!OL_5&fqdjX5plv>F3 zqEfrg2T^oM8`#%+xAQYYU$VH&dqhtYtm68tFz_VquO3Eb-jX6^3o*~H$&-&N5^@ZQ zKl8`IC?tpg{V9cQF>cd?KMb3?J{fTqBJOU+1)4}VkLA0e&Qw`jRa+d!q3>8nG)vJn zlSa~}Em&{u7^aPd-a6)giEwL2GDW%PK62HMQWZ{fmZgr;CAVh9A6%+e@(hjTEY09L zUe*ZKww^^FMqa2uzMq}QgX{&t5idKtV@#~G-@g2c|Md0l!rNuFI9AimK_7?=mbb1kF{t&eLkH z(QvBABzV`90g4=y;{|Zo8xfJ~K=j$%=v~vV5Ru@9Q~|U@nU@ zer&={RcdUB%8t@GI7;HlnYW+RdX~ktO{rK(eJB3K0o;I1{)#=FMdjGXnx!ffaQ%Ia zw^ePFr+P-Aq;*!tFjjvqE)c+lTS3fWk}>%q+NORwD50qbKEt*8-TgyE#q!5_^P&Ap zSmDT%ZLl1mXI(+9cahVR4f#}2fuh#u2G`@(&URNnBASHd;91gPp%MU14qWw$?195O zhuAxT4X|r?_jKi9KNvGH$$;&|_my&3Rl^6E0lLgdhk^)RXDd$`xIDD5g_-c|E##Ef z1(k#1e5>)D5Kc{PZd4S+<(w;H7vP9k9Ae9SrLmWsWucL%x=<=Kvh|57mJsg~gq;){ z%saYV*_rK-%QY~SomcdBvUH>422u>W0rm#N+~wvcE88iEbFO03dW`c$=R%8Qn7Il; zgE~(*Z@EELXjmB0OP(eKWQzuLBcYDl;Sb`Z?dsIro^PJG@SUcJ;TE1Py?pd%lz(xh znkb8liFiA?YE0WA7Y$XopGn(O8vcYW`++?Re0>Bl2{D@22}Mg=~T420;cv0P!Xi3ZFTKz8v|5l52|=zQd+@MZ+@++0GXJ7S!`v zan3x47bcSA(>@O}?BC|BgN*+0$U!w2MfmVO1i!S^wJ}@*)qq>d~Ch_7%2a{ib6lmOU8kR<*mBxWaQ~xquJ%7#$8Q`XC}|q13ePc{rVdwO44=PEd54QT|Lm~az;PeU z9nr^xK!#yZUBa#ZQWtqu;Z6tG$tA~FxDA(Cc<2RR9k0mPHQzL?c$i3w3NjCM-|W3r z(h;~r>NYpz2d0H#M$V-mj*yO6d*Xec=j?Xvo+ry9$sz$#07OYPaNH}T{Z}u@<~9^C zEUXB*>r3qR$$&$VO)xP#=ali0M-*PkD%O$l#h~>S*$u-l8j2HtnN73DdEeA9H`%gT z+~cCGBMA|GJp4f0Zr7LI?s3gs6OTVSJiKAn)!a%7=ocxeA0m+}6Up09V60~1oKZ=_ z*qm8@)$(D`dbIr1N&GH=6gi?v8dASjqA6hO>Eq%8s$)jz{O#z-paq0QQB#3&$-vWLQIFR=3rz0odg1g)C8b^ zbt+c=!aR)^YGNU-;KHti(Yn1ACjd8sW!8KvGlnB3ZeJ2R12Vy?oeQy?5$MFU_5&Gf z``c$3?;JJhIxI(ufz}h}ma;$y(y90SOLdOw22ThT%*iI8Q#k9BXqQtkRvUeV}g37zD`Nts)Z5O7_-?rk(Lm;!H}Y*Q2aY&#BWr4Lf6Ar50KE zr*HAT8egvGIOBnU>?_7su4fzxul+$Hx9#NhO=mw=QIw~#*zQCg3|Gis>CIDu2e3nD zChC6fC&XN$G4M~fdNgx=&YJl~75g(3|Ble{84hy?M#nu@#zj97Tp}oadZ0TwC0n`Z0d1)=b5<@n8|=taDyMao^K{zmF;`mUS@hp}}j!6SS@Stte>^Soa_ZMSpm( z3g^l#4#t6XErXNMfTh@VA8f;N+4Uit1f_s<~n@)4mb4NTsf|+#sv3 zz0!z97ZIy$E;I2=8{s081;0z@}P@jduQyCiA0OIYc) zQpS}~gQ0Wl*Gq_Mf7*z1`~ryy9G4T16P-L2;&niR7?DTmkjX7ELClbQsH}7+d&)eg zLVz-=t`_oOTQ9VC`mvbQ4)`6=(U5@)>x!F%t!g?!&JSChX7Z+tesS;lD#4mr^C)f2 zr)%D(P!B?*>F)e6k}T^bO5UvW-XK+;%k8BGs-McJBk9+RtcNb=Tx;h?MQ!e<1aPdm zYe&aQ;FVf8*ME0#L;&I?xTo)vP!&@=41+ODMA|pl3AWi@Q7Q(o0#7iu>~#6`1?s1p zX?{qjh7gM&`-~Z+FRTP^Vqsx6O239nxduz`7V{KHgKSP*74V0OTJCND>N8z{sbM%g zsG(mn#On0{9q22ur7Ib0=c;ytW_=c)1?ORX%mjl%p|?mcJY668?r_UMJrR z0*z?GPVlXw)%*kD+UFGnJP4ksgL-e(T1N22G*ks%V!|+h=mhgY!~F(W@b>%|*~#c+ z8zH!@{8ZiaO67o^tS_`zo%p$Hn642u&&}(!hpXj`AfukddGEPul;UO(U}$bbRTX?I z3=3z3@go%8#nplZj(M2a`>_yMBugf3RQk{u<46o{>ML9c>=}oAj`#ZTXlD@WrZz}e zgF|aQE6^l!7gmkq+>>)(Qa<0gq-~&^KxINK|4N8Nobi?U<%@TXSoTC{8%^eQhLc5GoEYafLQ=MwFT3-US|PZLA3M#IFgG}^~w3W1x|e#7D9>ji+6*rU5pvIKQp8bao0#6?0ChD?_3 zN@goJp7qRR4<9i%#|(le`b%5ZXN@!V z#n|8COSBK*_Mrn2G-%-mGB)d_A6^D>V8tSvnQ)rIcz{3qJZcMR5Pv8p(pp+_pvG+I zu~%f#k#*DJrPb^(VCqV}e2^$`IBZxQb748Nsn(EXXoh3n64bR(8C}Fw!b|^ZSW}J- z-v0DY^{v>e>q7(-sG2`ROR@lxQqhA@JB5Q>g+8~~EbTe$@2IvfDnjlF*x`i77e=GE zbnHn~907ZlbBzO?)}VL7%D1r}{2PwK)!L0d@(8ORVC@fEse-5u2c6&i9AE~Q8~c^> z7G=Xtx_K6QItwq%Bl5gKW;FF<>vF$M*Esn$51Tz8IMQAzf@jDe^~Rhf3;J=RUy>QL zkk%fbeXcb$;TG(qd?QV_RgZLRck@hR&o7ow#3gN3-80A&c(Hjovl4p}vW z!I6OVqy8AMY81kDdrA=>J=k}%T?~22ciMmI7GX3fVU7Cv{f%Wil_sD=4>Y4aZg~s9 zqjvnOX;X;?1H8FT<5p{ug-Z)+~*>db@uR7sHe7WBiVX-TirAle3V;$R~ z>x+ogL=Tc{1&x1j2lO6A<3=LE4g}_yAwsh}pzFqzg?T;ALxxx;bL7Y3-l6P%!D!mo z4EU6p@K#%M3?xA{-k>FWb&J8q-+&)YuXCtHbc8Yp&_jhr)5(4pd_qOES*&JD~hBFE2hfM6yR{-j};hEkogihvJ{-cgub1NKkzs+2@5|mGr#tRy%dhvbU~qb z{Pa7?AA|qpUfKiDNVVkry8}|I9iVui1KxkYn`y(f(ZGJqV=?qrLQg#j=*-jD7cq0w zhx99)MVi0|hIH>*cEHt^Ql)dX25nxsRRoaW$N%Z?qyxCHJQ^>t-rFF>gv|li(zZ37 z4h*Ly3$|yj*?t`7X0+(Da|&E4tx90I;|WXWPLs&OLdbXwENif7@WjSNP`_-9bXuK$ zw=noirA7ae#qdbK*iN1vrV}|%>{^3x9ew3ff*UsrZ4iNII1a#@DQd{SQS9~rB5Y0w zm*sbQL3Gy%&{0E{Zeq4%P7_912B)7s9HQ<7p$Q*)OZ^TZWyKksP~GE}dXuzHAo{{B zR$x;<-Zfp3ASlB~09aftir7wQ1!AHg3w_W74x{jQ@fyx;j;2`{50<-q0aoaS8a`wD z#x_C$&!BDw{jt$`4*He|F{1Iig_SqbX^M^qPVs3mR)<(}>WqMmAgR&8Faj%>6%S_b zUy~s=l*49b{OWcdb>h8DMVR!Wkcq@Y_&iBjLH|VHsE0 z4}M~Or+HHUQY=4hKEMDJ{@3Bx{fG1M0%lUb2?m(O7dt_9n zP1yviU)%ZZx<#8;CRw0C=-%{COww_c`42&ly9P5ba~sQZnl3>qt8INZsoK`56aMx7 zBU-jdq2VlSXpD`b_QtFRuPER(d<0xmJzUdXkt8eAL#<(g6)2GjzeOqp%VxgFJ`#sv zdBm5S`+D*2A1?hW&JY$ucnyN6&^0-XqOhJ-*9}AGo`Sy|9F{z=$a88p8Omt%ZMHUM;%W+KX=IWV z?N7$(L)#nphTw;G{HdpX!&1rvjP(YgY4BmWGWPlCw@S`1dKuxT=sA9V(fi67Cr0!o zz!|DxYUryn8F3hxMLh_)BzR3%Nh-(mxSV(LO`RXe7~|l5&QLz0{_vyyo4ss(p?;^d zv~S_SV^w$k94&Z;X-hCIBMYo$1mRp&mieRq#5!tav|GND$J-Mm0;7%aY^t)W>tma{gVYz2JnN6hkx zS8}5p+O%FT7u^q~X>Hf0@$ois)HAbLOwknD`G~ySGgIRZjf9v5xkTIAe6>vy!a+cY zrG`6=R0DAFe}R?L272;Z$zjDW*nc11(WM0*q;uT-(4LZ{;4B7Qjkn2kA)1WB@luHu z4(%HZ>{7#`chw!^5k|4k6G!4eUJ+R7eD^a?eye~capH*1gRm*cTSxQ!mw~Q%Y^0x6 z6MSShR>!r-m0q*#<*Md6I7)I9FKf&JYnJN^}UGV->O#R6pWrWxL$jX!-2vw0e#UjCG zuJr15p>ZRJR|E{hnmx^iBnw^a zi4ku4X5Ujq2JIC_k1SAYb_OKy^T?(MYH4k|syxb^tFxv#SA;BUDZ3FSl2IZz5Ndnm zTioOtE7zB%sBksYs>0|sLvZw?GKNYDjG^eP*t!h9k{0u#E_xm;u&>OZ_I~DE8DcyW z%+FvVq=HdRZ>4!9?HD$^PDl1n$RwRqFRAR!cl0)xon*H*3hLkOg)os0~x5Y3IGmoLCB!#)ECZg4%{2$I@6wYcjFJE&Qf*RF<=rf$ z8P}!nB&7&GO|e_aaQwfPE7n|z#~Y7g$sMt2EvK?S>bUklm;N&89XvPfd>20uV+S&P zh5X3sCouQ-Xx6B#POQzP*r`U`K0&Re!Rv=mf6k$?e1f9Yy`l)r^NPYi{*}v7o?QF*sqzsC9L5BRoMMwHKR@COc7l_H$`Fe#Xc13WocU$AqPlf1qAn*HZ<4S z;YDRsywO?P{~{vnAeVYyzN}B+Po0JC&q~l(Z7^*{eXdQ zb^8}8U;w%OmZ??t6msSBPY?dO7Nq$A!3gjB##s!sZw748WC`o|Ob6bJKEkc#KbEO= z?QZo14v9ExHfy|fBUj82z$ZnDUM7`(n`wd3jzzNk6x z(nBiK_!{T@9q@Z?@_2%#ZXyR)yJ5L@dQl(t`*lAQ)VVWVxinn4DV;2Ss3oTgQ9z^rK~mIW^Ke^_Y0*$pG4hkxYQU_0|y081`z8d{3gPu~<> z{d{|xZSB&s30nP^thMRwp&~PMCIVc<*XORnK90MDjQ0q`LWKlVn9P=0*Q|pj<6+of6@MXgdm?7pC)wgqaeJo~klb5NCgAWeR zS)HyCRPvIlD6g={tA`CQ$K2P(vWM$$zT#qmxJl`AGrAM3ILYOBUo1-`o8hqtO|c-i zG1Mpw-7Ah~pUBu{<4(TJK&9ni-BNO9#!aPU7-RX+}wJL2ndUrj( z%z4UCq8p~Vm-*Mzv@dtd=ul2mgP%EWn%i|@*#I|F1=xH@;Ikbl<~SYZSL*DX_C(2Xqf zNvextU4SY#yu6H;Z6G5G+K;i6acC*{HIy1{#bRvNV0+Y07lEDjL8aJ$5n~%`vBh9W z-DHyz;q>S0X*mDnr z+QJs8OcG4{JGII8YP@6_4!P(;t!pEh|8bULPP2Iwj4&)GW1H(WG#ED{-$1Q_j#EHt z#FH^w`ql+|pb8%tvx3=~EuD&0mHuL^!0hrBM(49_D<* zN?$zn@A&3DslS($N$_E4RR)22Z~>4^5h)Nu3wjIQ%9MCrKXdKgNz3H?wbm(5W;3{W zZfC%Y7(4x$u>2i6^MzoI-*MqGuPlj7S~uJ|m+~0$yP49LU!zSPyDvP7kDY2ew+j1n z=dt&>DrU9GeN=wVYuOd|(Bq)HX633bXCRPAUG7daKM-kosGsQel>}sWzLhX%8T!h= z1qLLg8Ia(Fw!7pNxgVVy!lZBj=(@s;f$Er4XIh*#1Oos-mIzST z`SMi)U>^i4(lh23Gbe5*tR-#0xI{(Q=b{nELOfYfo>x*Dv4y$UCh?RS84-tiE-5qd z*Bof`RN@#oaWIGueEnYC^TQwL?kM@TsDEilau~mvbd!YG@cGMo1}z}N9BTJy*KEvb zynD18wil_KR0g#`jKn z6J3-t5Tuz3moLd7%raouA&USKiaF6FD4&j7Y!VlWdEWfym$+1&iFUIn;T}p7^=P0Z zn(h6UWDY2ed*R4uf<1vIJHps!G3$sYSolD46Ru^h5ObEHnwdTePEDaRANO*zM#qjI znpJ%}F`9Sp$H{WtOlpvT*bJiL|6M*MjPCiK5GIImiFjnb zmQu)wICj_*-1@O6>+c*AF*8CD zDlLp@UYapU1QGYqr)TW|q4$q!0%3HdrzvlpN|od>E*+w69xc!m?a0}^Y{q_=*fJGt z&YyzP_{zZ*mX(FyLqjp&@Y2z^lcIMppTTYnU-+5bv;AEMUfH?@BDRUhO=$0VKkARv z_1-*>eErCLp8EIiH7zv*SoAlSBk5=@z1K>t(s=;GX*}RjES}M!!PgHXxt>?z0}^tL z-nsj!OdiY>^h4k9v}58oiGOT1^B2Ho93@(NYBosixl^lP9MqisDwD<<_d4!crKh#sm2JRH%P z=gNLc1;b+N!4v`THU)TPfUY6thI%B7b{)Hw}=>mq?tVO1mV7854; z#EGn}9JgJ{%{uQG6iy$@e}{#L!kJkEvZ`Efj=nXTM>3opJ7R=JQ=Pbl?UjkIYcZ3k zmUxe-J9#dUth*E*{$3(zCtMwzDSd3-$wL%m#!FYu3Kz#;B?k?# zBd@(VI|EJj$V6JWsC%k=1q?hf1-pWEdU%~%n@h){S4-vfB)YAwK6)(I%e6O0jx11u zBVDn4ds~^Djr2*HjkG6p%(c#=;WW(=(W>n}LUl1+vZetmiOf*aU!rHgfEIhAyX~H* zodJ&nkkhpq;`?m<1=OMJ_kMGi`DxN)*8#+;AnO+i%mM2M$?qTlhRhG1Cws2GcIG)yo-_)_!Z7JGP=ptvkWeWK2sjBj3j`}YiUB|+-gkP)+i zN$C2WGTwC!4{O~JU516Kojd*HC_gCQ`Dr~sg)X8@NJ&gLXJBAf^JPacF8FyXX6MGH zpMLkLp|DdHE8y_7eyvCOXQj0Uw651_s7C2!`D$3_p>lnXRLPCf(*LO^rL$1)cgb*L z*tA(DU+p{2T(Hr#-d>eJ90kejch34#fDb9To8$!pLFw$x8p##-GCQlAC$u$$a&$OEpp;Yt~H63{jR-#AHt?Vxmjn60@Uv2OvWTt^7mwwJC7heQ0<0h9+M*w(x zZI@|6g!AcG`kXf7veia}VUD|(@xr@a0zPh~YP7&}iZ}ke5#3ulV>!Z1L5MTC1kaZI zMq2!aSSJG~aQMs;_w~*+238{ImLggkV?m5#p2?@=^%$@pRn;QMjJOrMI3wDK`VuB3 zjt0+YJ550%Bt{HL;tunw;-Fwo=`bLlWQZh}-^}&yrogC;$i90FHz{5S;GEL8(1lB5 zeZ(`*8r04d$X227^X#0XLHkbv466;yDRTE5#Dw#&;O~d!WTf9%%3_p^54h+#H|U%& zeTGJJ*8#x)iJ}MV0yc}`q9B9DWlm4?u3Mz3&k#qEmwNmfhYJ2egsTy!E2S>SIZQWr z7h+Dl^x0@;ujUkZOvH041(Z1%duEI*`K;pTH|3!Au#Yh#)?bJEM_mrzAZ~Zvmy--0 z$w=5SRL9zkVtE21c1?|maaQ;^<6Vd+WLo0pbu9zQx@LOH=3sfGvA_C^E=^refEPp9 zig+q7c5ykRp6?)oU2GLPn;ySqLJ)l{I$1j-FydX$^)Z>>M18Krm!Z1mH{h~6%$PmP z9ovhEnh`Y~7~)K5g~J@`lJs#J#14j<7!5d+3`N5bHunDMw;q)BzEKW^LXMQQt8V=s zm#`-3J8W6hXv`3p{}9rvwFfBJvNPwxUJ5L#SxFgRLapQ&vJa1>>ZRV7Nu<&jd^b-t zmNSm?fYPsOY*8uK=_kVg%1O$lIun$!gbL?DX|ma2Gfkh+cZv_baajGrEeIZk9ZK>K z0^a;|9yq{6WVgVFWY<^y7M=7+?}D{Rs^w?NqDeauy|VI6F4cz->(Im;;faz54$_>C ze~^(Ee~K1Apq$u`%TtZN==$^02B)tZfBg;zK#8v&gM+azV9C&}5SZ**UK5e^j~+n5 zdexc&`0e#8N;+f|xtZx5LEnn*TJX-iV_HBvX`|*leQ&N!7aNtu5Zr%FY|u=yWe+cv$%^j4L*u zT8W?i8tKwr7)peE1mT5Yhj1CY(BX$clrG?%Z6-*^^Re=CpnsCNjvga<4vA1oG11&P zDCRT@S!=S4|4Gg9P6l4dO0H|Zdlcyx_>wcvN|qda4AEkc7CQCpb3FUR)mYHjxMqE$ z134-FSG%;Q${I&IUPFGuG2yx|rs>K!qp9{!dzJJX2_N{gS_~C1WEkG5zpft}u5X3o zcDwOwWkAE+thp{IQXLE^I*??HxGk9o8D}O1tKVve!17CpSo(4GI56nk;XmYKI=u&j zTvhb@{7#`8ii5q*?s-%)ds+>Cj@JhKcYoub##76-wi1oY&kxpq9R5Qu{yv~W9ym&L zY(``GbA_c7v!bz-FYF2ff+^w?MAEdU8RlG%xpN^>d5qq+neLn&3L;F`JG$pKVtrl~ zH#P&q7?sm;-&bWb!R~TlBD^VWUN>EcJ1wv<51H)@P)(c3SPTX>h(y-u)vBaU|JAQ! z5Q%}v=-GK(U510op4wzky$Gz9GK`Ig&y>W+088SkpH!$w?oWXwj(H8>m*-baI8EIf+cVQF)y>>BUap}d?R zf-}`Fd8x`hWhcojcEpxZmOA71#EVt#s{RBngiu7)fvuwOwOP)X^y&z$pzkEc@_i~IjXly@#=?T<{_czvIGB8~h+GC*ZsNoDEX0J}Y zBwXyC_Q?oK7vJjPQH#h1r({f`nXyz$)0OF)e0?4Fws$jRkA7G8om< zr!7UTmi({j&IC!GH8QKm(2BOj5{cOo;?1lGTs13XjoZ*+uuGet!VL{V)wI?7ga(-2 z&&l0PJ=VH)e+Rb@i>jXSxkA>|hTil^=AZ{A)e$WQGB`E5I|4tw=5|uz{bGmFXGt*M z!kTX%nF=WZ0GX+`QS6?8ue`w5kSY5)94cm++R?q|Z2%lN?d%5=At1NdVW`a@<{P0b zr*i~dD95$I!jE<;V4~k%qDO13H9Zbk7511tOw+g3s&L>r2G!SH+t$vbZ-3R^rw>As z8@LzN`{wo?(m{kO!qz6^F=akSVbNA-nSHCLdYGvplH{jr(1LGeJG#RGclNwI#ay%R}d^~BI0 zdvTqhX&MY56ZHl1cUNG&P(&`9eULlORbsDxe76{OfT%_4<^}2Rx4MOx;j}BfehqI0N%Y%@OMz z!x?Z?%-p8=ewOEYX0r*sVCEXmteFY>l+4|c8!5$Ql%7?iy7_X4=F0rB$%;A>0dlLc z4+o6R{;2r(2JSnIy31vKwCOf%6o6dk7zf#a4&W?Pqv@E?p$)Jg1V!Wy)|#m$ZsSJ z2vaQAHIxYAr%0f)Hl3yd=9Ifz@2}7{VWTpGu-PT<{RyQq4AXJ_`PV64AhyyJ3ys^s z$xCjUs`vAoo!`m_nc)dckvcC)eosF0MNK~~fZ~ca7qWufh4#N(#G6=CI+HIk0<+%f z>Tym+(q{r#xZudOyPoWx<&@I)P3ku!F67r&%t~Y3chpAEHOZiYU$x(8N2tj8KFMl= z6?sFY15PkqF?0pyM1pwCfb{i26V-fV*{E-m{FpwfZZzYYnfQZ{@${d0i+NA-YO$QkGX3KPRF zt4$xf@jDFvsSKD-F!AS3@y-Z@1$WKGy<_0v_Xa5oFmdbqMwbC-8|yC{iG3UG@nwdX zwGMxc?qK&5u5(F$CX;cIF!*wh?W*z`d$!Q4dP&7#bo&FLeUMwAq9v6 z8}lAHE;g*btX+m9mn%MiBONoL;nX8+aN(X8MIp&G;(TuQaiiG|2F0p>MOFtmiM~368&Ww8%KmsT3Ztsow&9+!Q;EY*aOSsPi#I`n|s~3+C zRLCK6{Z0{i=nwM~B1l-O`-&)&qxd59?ip+^`YvA5%meFsUB(ZTtGgAuT4r&P&Kzk= ztt;I6dfe(}%;IOv4?T53@W8KZIZ6=zT&3SoGv3i-@=A;)=h9 z1a{w5u)+fMbb=AAs8EV5EMHiY9VJQ7(AhYPv_URUu%gqeZ!+NsWEl7me6?)MZL0Z7 z%IQ4&W{@JeAwq;K0hrJw5|UzqPrk@d(R!abO4#_u3@HkSrhp^S*IDY zZ&W3>?-U*NQ)kXzLU2teaD6%Lq&S2PrO-`r}K4I|F z+l2I%hQ&zxCRt1M27SLd{p1sdvruCSZ*Coof+oAd^zEJo;$uI{>Jwrv8u6rMS=9C} zQg4|A;<)94ZDXX0J@7jm{inPRVg@HB<)nET!n2kRPDy z9;7k4-?KvU%fGAHoKYhzF?mEMU$ACP!lebmKLeo*EdR$vPz;aN>F zXY#g)a7;(o$_50{0ix%h7`Otg;koaY=fOexZctqyUQ67rir>x$gBPs@!NJ>}mC56s zX4J+lip{i0FDK@!-XxlQ;;*^8^fqCn<&qa`zfc>Ixy~qLJ{*s0(UGrW4lGzNvoZWE z=tm*=SA_VH`p8W+)(boXlzor5H=%;Xf{G!^I@?d^QcS3(2zY(Y#=%OpO;i(-ia<` z27W7w|HImQ?OM#^YJv~s3+Hs{oOAY_a}LYde)_@wtEvyq=>et(XaXDCR3He+=AuaH zQyzwesjS-glb#MWzW_E$-G7WUJlE{(TW$aS(3qDaV)|h}KZxze_=->LWH!SqMx`7- zK;xf|q-Qp%OM<`IJ(wD>C&>hwNPBHr#r8+?JFQmIwqM$3rd}O7q7E4$oo3r-)z-u8 zwqaz2Lb}VGvCEiaLlBF4-XR|wRPw=Nc`1>1L+uZ_X#z4H&SkYl0HIU=u}UaAa*15s zkI^!VUywF0{R%Z6LB=^gjXDF)q~~f1S}Zh)#@b^+;e8Y10n9wI_Arhn#HQ!H!f;d2$Y9 z(sNB#*&_N8Fg|a(t*KbaJCfvmpVgyRw-w1^q1-kJ>GP5_%3le!kyJu)Zh_<8DO(`` z^l}lV=<$e;OcQuXkK&3sHr|qL;&z&RjZv=CfRrym4{>VQi`Xui$ksn%dvG(Nc!Buf zZ%l^t|xw+FKTruE15AD24?=&V&vSp?P0STs4qQ1DbbPmHdm2sxbBXE#3)9G58w&@!7^tOD{r~};7r!gcZ{TaYhuT8*+&mtrvLvyHlID?x~A9F*=KJA}?ZlsOzj3gjF zmziF-Z_RLut(d_NbJ@6p;UJ)lMxlx zp2Rb!4oZoB4RUszB|1SL2O+3EZN%Cx`P?EByu0I>@U&NYbL*GvR^%UKr2{}i)dt`N zP_=DMHkf+<^|p*;rrbZ+qdB4kseQWa=h>wXb0CVv4HF41tSaX@YYJ!6f@Qce$lJh~ zcSe!_VRqwk16XbngNdiVke1xZGoc(25%6@(=bOwFb+=v0q0am$Qpk40R`Yb!ay#wbAtb zfX;UBa(1EOigN&!r*_AOV(OxLGWGKM;mOZ!vJh{!i+06VwJ^auYElFwtNH`_AFwtA z;F3MBmHEW?<$Z7>t;r6m>Be)CJr`%wLb512kR}R14n{`ImCxQ5gtsLmlXf7_ zyQuJ&nz$qe?ACf|up=#gANUKTvj1d^b9qhncT&BYG47Sam!i)oKW!&=l^Nna0Uk7Yf}X+!>e2=OceFTz8LZK6109f$DYHcKIHn}oYl|&}Lkz@%ID={^%QZJbheXwKAu;ToAsi1?%XQKVNq!$ct z>>HP>@AG6r(n!F+tZaDEsm{CPinsME^q9E{Tp^xzpx1%#m7CI+9T--bZ^7f0UzQ(xq7QxtV;HN9~yXk;7y3kTFL zF6p~EWhwB8i)U66SxU6d%d%GdB=mEq*b+{gC!#4@`67Oz%d^$M-#7E) z&bf$cmaP-&G){JoLk68kvvWzix^#;4X*qM%ApCx-8`8|%)~YB=FS3u4yF$$ZP9C0F za_j-J+`9{K6Lt6yxE!6|>FAbhjkIdtY$gEFG#f*1Mx={TnT{7k4!PdCCas~-3a+r{ z;7o^FgfToSUO5!}Mp+Cvm}m|a>TyoKJa=ZgopWgA)xs4fzh3ItpJN_tzR*r8G+FIC zp6%l$__~ZU7Kn*W#+$399uT>k6$L`+hLPy@0c)O`|!{uAa%|x~_ z9!U$28sFf)Tdg%8%z(--E4hT5lzj68uzaTS-%b(0j=y-;vLU#gkn;t=A|2$=C(%A+ z$K|I(9^yK8X#wZ^njv$L|xDOo>+3IKSh8y_TC+uW}F`69hSsC^=d0Lf2(DZ}SRXUxQ$mDz@}eER0^kZ>VB8mxLvqwvga z@n83#iQ+)7lyOX`va%a8)y63W1{m`JG)U64lq)3hC<4%j;*H|D;3d0QB`n=2<@abz zPRD@`YELt1;T3O8?a%9jK_1M_^)W5Y0}0xO9%YLdooucZtY6j=`5U@!kZucB9el#w zb-F8#vM&NwR=PDPJ~H0Fh}m1;62>6D-#i+?)P(flWs$AlQ-m;|{6{XQPrs&Kg*Ome``xL0OdWUa@$Bv?Z@ zATIPFX8ei5aZTf54yS4Y1+cgU8%-0-G#oLTQi7$({1yq=#%G&wCZZrs>8me$AA{&~ z5+SaM<<#`D2&=l;s@i&Rcf=o}sNd{IK2A@O&Mu$FMBqh1^wC$Is7}b@DhGQB@BxdON~1 zhUD8_Ny}Ze>u-YWz5O7^jUkBA&o#x_4aH&IRcB~-RQ;UZu0|p z_O197m$rL=m9>J)9Na%ExyOMBtVj#o$XP>z@VTk#db*ENHErl~RZ2*$eeJT*ajWh~ z>syK-y|hVqBec4ZYW_8Egd=60fAtOz=sV`J6?NpmQU(+LYryQB16F|H7IJZB7I+6Fw@lOn<15yxpRBEgtoAsM0?Le#R{$wwPn7S3#)tq=Xh zdWt$-c4n&Q*>Qo zN^#|uOV%Q*BA>vQ{Y@f*M1H$i$yi8&I0^Y_@B^ZX@9tgC_MkJ-)Cb;O;5+_qOvJtV zskD4;uB9!~Da3e*p`}C8i@=caA`8K0NglFCr}!u6(5C$J4ATY{@`?@u?*Y;Hm4vPs z*#tw=G7KmaesO_H}eS-2d&sZYi3)J9`g$0XedPHV(}D*+G>EawFv4LTW^ z@y<851)d*W{%ZVaEvYXFQgd({1ifv$m9}hZi?a)HfHDg~&De?!qZx<{MXc$E_SiZ2 z4|Z1;5&>Y6o!A;%S3Lm}0p`E)ukI~P=?d{N$a)x4P*U_Q$0$fCwP(LYKD}8By4qnI z7It}SaSQ{ubtmguPaxNtHk7TjLqfgbq8sFzPh@6CUF=l~&#+uhL({9LhX7^EKY+sC z-^iBz^#`JxiQd6+{@zTI5Mt)-Kxm50oP3^U30kbWF{I8a1}yL-`U~4q2rTP2;c^qC z*QcVr-?vGV27!8dw6@%g1;p zQf&O>NEyMUP*|{6Og;;}dB=y=MS-k(RCeaIsL0 zYi)`KO9r*TnG_KFY=IzG;z1Rla2SzD&e%P-Z`S0c&yVIA z;bJnzw_^(^r(hXJnsWxVIK;ZfZB%GaX)x{xaYEsV)a}_5@e7FMep2WR7c&tyKa1mI zOTj~Wy0FnAI{n3tCoGdC5+Ya{p~k_u2rQu1&pp3D;&3~(%*4(!XWL`fGf4+ zVD*-8xk~t-Y1%!$2#qPm)DwF0K;+Jbc>T9Ad|sDrB{yE9LN@; z;%K8GujO0qN4IuwcahqrX~+C?4U zzO@k~5w<&bl`tyG9we?rB^FsI*CK*m^IDZ_6b)DYU<#+ux-S%%_K8z=w_laNUUdwCd67=uVEfba*kPusTvaJ{ge?b%q=8_j zFhUhJqZ<<6W?^4J|((QZ23{RYy%eP`S(#w`Wv@^&0jwEF$TI3oKTQs z(t5raYtL%4Haav1ZLvX)TZrWr(ZU@t93mbl;;+ut>|3BFLe-pV+v!UagiQy9_cBFp zJ2wyRAg9p>cbRYzn+V;Y1Py#TGIA||VJE*Qaz%F7nU4f(A!OhT?aQl!&t3KY?%B`0 z#zhv>ViO3e*~;n_A_ue}(v96&Wp7uig-gK5aCE_9TtWs^9>2>tOz(&aoaNczBMU&) z>!(pV0jv-X|4f zN6w{1L29nKbgS0o-O3%)4fj!m+3VEQ5dJ%GZLOL?n%}-z8i6m)Z`l~hYH2qVt#C|F zFADvr0}0@sat!f&N~3bbD=!XDdq>5#YG01=e|Pp9_~0|X9kq13tlW10O|Qw) zxUFXQsjh7*8)nVPvQvj{ox6S}I7ZuG}#mojuesw!pT0~mB1*3~RkCtm9J z{q1+^jO&l##jo+lZF2SNIjwD2j8mj3VZk^MK_HBA$AZ$)puRvHaZlD^vqDj$98tm- zC=diVn_H$hJ|xr=0`P?15yaBwGvggqsCeZ90c478f39Q^i8Ch@t*{#kD~ zb=-V|f>jCKZpYHPo83(Zm^Z>CcK$jq6^Se-0w2#r)CcHxH0w)4;sLlFf*(yZ-i3qoUX%=4$C5kh}>59c4g zLLt$661$@!rEM<%BVWp*2TJ2V*)AUn|Ao8YOW~QK?(&DP@Ru%Ccnsj3zaXPJCJ|(E z7npzm!wAAEc|pQ3j=(Y^#l$zmfPlhj!pJ)??F0iYdBGzxjKDGs!%56P#0d<;jvO^v{2WVW1cKKkD`WL24?6Uve`HWB;)%RE%vJ z{b?CFe+5Ab|C^8hQ9VfhQ~ysVCMZVNbN(K)f8;GG{NMTHf7F+#dzXJt#lPjD?E4R2 z{v(gdqX+(%=zr*MJQ-dJ&oDIp*yN9w8TMmDg>i3|`@b;=|7Y~y=PxG|hT(hoPrX5G z)2RDj?YhZfm;?L6D(kaI95dxVRh;+N-)y`0^n`oX!DT6uV$9sy<6M?jXl zy_!9KRyYhl`we3$;oku$A?nuqoT2ePK$fZ>vc9)Hjo>4_32->w?p4i7pxEW;M%3;y z&j)|m5&H()RW$P+UefxGY=0P|vnU6JVW7ta7R}Ib9Fq&nxL)J^UXU4h$qHM5kM@#E zMsGX3sNzUM;gS2am(0HOi%?U6M_TO^xtIsKY<(Jhe~BMMTYs`u%-$?RNLK2!HWsO( z3FSJv;3!y}UET+9zF^ai)C00Ls--^8&;S`4>iA~XAIiM&eo=E(Z==bR~Q#mUT{>*Ccg^eoDXJc6V`8Im- zU8G+9wfr5q2T%>=Ej3N1V%duYHc#A#({M6s<~M4rCTQVzDa48A6MOiSnKu&5sU?|Y z2g-1g_&qQ{sQYMzsExQ<1`F7z!CNX18hCSVcJeMox2-1RM;-oBaYglVJAuAT8MJ4h zq?8=i zaVA2~X^ z+E!hdiMda`R4r7R4xrxPr)BK^v^Xs{;)s$$#+9(m@pamKn}M(=B1;wlMpW4BqwegiQ@+3sD=5Zbo@XY2)L4RaMMlU^f7XUn8sUR41VO+%DJp=#tGZ*!98hAQ{rwrWZj$%oX4IviKvh*wa}+gZ zVxF?3HnPFWYjNqH3q)+yK|7gt&=phM6;6-~&$#LC!dy<+llxgiJb&WA!2?);k?lMn zJ$M8huEra~?bG)UOOzb7`P_l-)(uq>3q=(tgpZfC%LyL?W>&tpFDz6q@DV>u=*Wxt!~j+a1>HN`Xh3e!HE<_6iI#5#qEJQl`y+i4N1p59)KVK zrH%%#uJ2OKtg!8z@&U9_i#~Oea2Y&y)Yqkvl7IJoR&a zdR*#lCdL#|hckB<=`G8=`&?(4h$V`go%KvrX0jV%P zy~f#llb!=_-_W&~L_n7Qh!t1Rfh>#0S=vJ1yTH*D0TERxUiRy=^^yL(QlF-+*ZGt% zflX~1ukgE;jR8#EbUY-s+mUQ%$B|Wk|MQg^?OvTmh687rjt4TFpxWF0D{PfrylEg!IFg z8IjKD{Mb(2EMr(o_3iKQ@=RA4u>gYPJe!`WTgqJI1{gvwP+2Mp|EmP)pCBkSQ*G3HEr~N2^Q1PC!f!DQ8jg&MxW)2xVH;>5s zdGT7+id4AlWP{IUEJN8D=JC794#;L?DQSh#sUThG14xDTP?Xv-y@QYmPr~r6jWh>2 zzb~OC77R?{Zf-Q_H@Db@r}ghc4XN^LxolRau;?y54$AzEH$72Z;u7Nnu>0P5g;XIn z2LBRSaNki~3?F9Lt+cGb0hnm}zE8ZG1ohz`R+1tzTQ)zB=R@UN4j7HH;P$(6DL$Gl z>P^hJUs*WFc8%1i)?ghVTrfUW_!k1%g^w?IvpBD_TbG$cMa{2$qd>vokcz3B(5+jf z5d>-@4|S{@v?W-hgJL`jq=5H2dX9!rlbWW|wSG?^T4p93^VoyipQ99V;&Szk6ds0H zqu?z9{#elp*B1f-A^Y${(@A8({t5}Q2+~|B3sUKWk&Iu0O;xYW4ZM>ij{Ly2C{m$w zI>%Tm@$VN>QZ-o*P_>cHf_b!9t@S(U1jZGsSrl^Q*1c?p^s>2QpWI~5(%aYXUt?Vn zTYb$7yBWg`ib&Fu>MfP7mHJtL$QUfqL0h18G*=RR$HYoHv+Dt@iCgS)hN#@{A>L5a zN5`g5H)|ne|HAvPAT=669U-U`ptV%DHr5Ln-j`7QGY&F8 z7N0TW3!($D)@EjcHoLY|yYmcd&ukO6$dQWLW0g!7ao`{q`(e~p?05Aiu2XQE#A}WS zFCaZ~?YZ-aIY==Mvt;G2N8U<6j?|uHJiH_*GE1C{YB_Mp+RcrhAPG`@pRlU2L8%b7 z0cfYfv#?S$;A&rI!Q-a&l{?ZeI)E8AA5w#a@0B2CZ{y{#xJGc(qqr`N{1_$eA6ki$vx;_fGte~<3m9`bx^l36I@>@W4TS zDX%3(D)Fd5EMwY2N#evA%Aywn)O|-+XP`4j^<|hn_pm<$sw6JA-#q*&v{^L`M6dZF814$GI3o3;Z+7a{)>G?R(XRU(g^ePZFaC1+lY?F*?}p&bMn9Fz zL`=M<2}<61=us5^T~x9^Jkk`-JX?(7{ejL6L$u%1+vsP5C0GV`tFgz>UH(FSR}T($ z00%qF<#j?J$|C@}WLUlmjg-Qh^bdYw*LvKx{a$Uv`a1qCYV4w3Ad+i}`NfkckmVTG z?xJTbs^WH>Pla49;Zz&iG_6Gts?&<>WsTKsCwLv!1*|;S5RvlEnG|Pe*%TxHPQviU z;f$L{Y8*4C_4eJBUtm#bw-lj~nbr|ImcHgjADp|2 zDD-|zJrEd|i#*x)L#DU??S~KFSJeuw+W|IB+DCZJ96rFM*j*P&t zzct9nI@kQUDFL5iYAo*nKS030wjMOfn8lo`8eQP7OQ$jU_zA<3t2Xb5p<#qo(ZA60 zFbx>Eu;eanwH#esyjXMvf}NiWIIHaz%M4Sc)@@v6hmhb}bm5aYsMs-YrgWV`Ri4+G zq1i6nK0wF>_iiUmkkiwy9Hdg?*W&u3qj&aWnfBb$^Sklf;v#2Xjj9FS(tqRRCa7u>a{4Jmn)h_3+Rm$ zHN%l~Yj`j%Pl7|4N#)T%`NgMr1^QI$l~IHujVadB!|>zhxfun$hr9X8BN^i+nrqN7 zR%tPe%`MR-B^?x6A_NSlcwu%A*f}7Uy@ni{-Ec;PeMp-Q9*#mSC-%uQEOBt(VVB`I zgMFKX@4c``u0QX$Wmkq0O=)pQlVAuSjg`xt850e6!0I9v4FhV&+eF3H&dk|(w}&== zj^d#Rj|6(q%UdcyUA5Mg>j%(CI(flwFLrM4{1>KU-qvPJBQ~yCI)e zzS${8P{>Dc*a~!nz^8*q8xHHD5GNiC?wy4B8v@r<>9D0ckoC(@ka?UZam#LLHOE~r zE?852V*YNNdzZtuz0!OdZv|GxWD7tex3jRv=t|=x%aTpIpC0i2Psix>9jer6Er>ED z-ApSkDP<`1!lo^i`o&E0GS%83+_ zrMJD)mK9G$E#Z>Bd?`q5FNBmr*1GL#s;IuxO3iuSFe^71y>Hhq0M5WOSV8f&O= zSH@Cl_Po8sGCsgOzg!;R1$9xxmKHFtbLXl!-VZ<-UB~1|-F^3)xVl={OTuYfC1ROH zsBC^=CbkNWRG^IHPS*>FN8_5wO$g-Cj7z>}*N-O7wORr3al#a~U3;RYSf8kkZ1pbR zCyE6Wd$R`FUMQtop(?N3FdNO}Pu;AQ)c;z#07r}QVw zo?K%aZ)1jJAxZd!a~$d;buB0a}oI_K?Hc zzw|8lXCipv9~05wUCKxFq>pIN1y(wKB0mYw3KB5Vv&);K5kG27iIP{Btc)gyV9}4T z_Snc@ETK=w?**~HlMDZP-#q6SL*RzQc5H9tMlkVHR}y9)0Ox&#Y76fS55LAxQt!cw zEuEBW=rr==b~b_>E0}wC znD|MFe+3xImU*HOCF@maOMoGcG!~z_UWI9Y#!x5oU74GJ)bLH$Qy9)=kG{)uS_&(a z0BJ_=TVM1N>OR(cqc0Rci3AHUKCOq?koCH1yOD12_}p1i0*PRhlH!|^)Jqb*Xli1a z0`*X1z5VG<-3!(h_0I+{BCa%ZwJb|LOgHJi-LZN51Ouou{v1@iW$=riX?;j`dTEA8 z;N3jb&*};EPJ{zEJEw%fzTGvD0O>@NDL}hnb|zX1Fb94zo3~l*d59sw1xk(A>hLFh z)L$Snyx?IKdZUl9LcwX@=GSpUgF+Nza(g<&tdF;l7(D?*ukjnnT#2V&I#YBQD_@ zM3>kHEWDlVTUBK?SbmG-6^Yf1SRel_k&-a6Jtw>#%S1O{Zdx7Nc!R*V7Wfa9R^eZt z;`coe!Q=#_umXZKagw z`Dr^ay*D`pL7gPo3%h4(Gb-4N%{$Q`CQ8)1yXh~Bz*)pRK$e1Ar1@nPsejBi2euq5 zZrR4}58fQ&7r0vE9b2ZZFYaPs?&E<#2*J$jmlAwK%^v)Dep$@~GP>a*uT7Tvw-ug} zjuGeVj5#a4IA0EHjco`KdzKPXc@#IzQoKPPHtQC-Ys zP4B-zqFhh9$+%p2tW^Q$4>?%4p;7Mvt^6nd^QjLgojfu0=rw9j=iQ8%ug^=W|0`A+ znGl_w^54oFu=#nio{!uz5w*{CS&q!87c0Up8w+{b_7qp1u`cJ1r?*7Iv|7aakTm#nO z>korIsigqe@!Ek?^;*EZh?NKLDh}$3;`fJPCMLWHnWWLju(!;#HftuG)@r-Y0bFwM z@;8i9++&6Jw^p!JMEZn*FcUc)R~nfIi$}9=*K5;&m8=pM43x++1w-j)bRMk?#V-y{ zt3W+3B7HOKqsi^2k6xK)mz0WO3Zh_`DtB`h!0r3UV~(i3m=l16_iFi|=(4Bt2)$#_ z@N~&F?42G6b%|@4ogZX1PjU{pGn$Mz9?`mx4F~jPSn}I~cT_=46qR@6E8i)wl}rDv z7(@D7996yWwhZ%#?vq3eJrMR!n5>+bb5T4=bD0ifJ#=}3F`vP^RN|3Di+Qx?Jl^1$ z5&Kjh1R|sZ-fk4(gZQBnCG*&IONx_{Dr~1#Con8!=l$&k?vFejPONf)?=!rsl*CH< zUYk+NUa4!3R{4>|jm-I$&?H8_P>ZZNYmF1KTwyY3s?6?sImSnr0&>}jsUzHXVFO^V zfA1K=4oV@TX#ZIQIGFp^p#lx+aBY=+O|(#>@4dVDEnFNDfAvWhnp13!HOzLUr5*Ys z3%K5cG31L$?iA~dbh|u%shwVS?W~bBNBG)FAY>?sGs|0?Wa>WMnUzT*E#TP?UIuI+ z3Pe`+*)60_B6)U+1+tl&IfapNMWAw7-*V0Nc2scuBFXOqCB6OoQe~F3in^znC|^~* zoFtp5sb8q7`wc%E`y^)opTsvx?a}=^0Wk*deH1Q#k}qHNuDR_a5%P5urq~7 z_TV_ZjAEHb5Sb<$$mou!^c`oWwU=DSjx3?9E#{VUGYQ_D^oh zX9Ovn%i1nu`4`T}5hOIuV*_v2qwZz)dUJSy-lsW*9H@EQD~tc#|Bh#D-SYFT6xoyN zFAwDEtg z8UiMu50mat!j@0hAMuQQ|W0e$cjn<5YQ-MNcDw8p>r=Ww8dvT)ITI(atu_b}D0qJC5S zLc~H#G&UhnNn)$DtsC-Yv)OhXLo!HHU)T$y=h6Q0JKRF`B$8?u1slOE)_tAKY(=k4 zQ|h%!iZB{X;Fs0p!1~(^Er-0@E;n&T@9~qq@k@iTIK48`X>7I5KVw7oGlA=W$?}U$ z;lyT9ihk86xK-!IXY&U>_T$0`Z&_!5sL!7}S<#YHMY|dyt~Hr$n2&P*8AE&3gVX_O z(eM2!mcQ{tn!XGdbxZfOC~ph6E5*9d|9$_4V{}QKl1eWVBMe~EFn~9&T1W6t0FRahrb#X_7pEXP1Cnq?9mT8p;nD}jbyiqE)JBYZ6C1_H| zvPDN%(uhn9+s->L$%}^Il*#m%GV2_6jOM5f6mtmndYv60C}ueF|M2!6%d#>}w%`LX zK&v5IYt0O;A(KQu{k7cZe6s$k+ilj>gKZlRLO2nmM})71hoCm8u)99=YSd9KKE(d4 z^k$E4|8^XcETqT!*`0GA6=)bE4X(?ef9+A?XY&~2`^)*YN5Mn1$yrIfBz7m~@Vhxn z-t{NHL8rLPECH*+Z(2ij@F_L|D{<_AMJm+0mwP3HiH{PQCm~7~L`K*hQ2BfvZS0_E zu_JalWdR^Dts%FETJEEZ6gHSMOR`;H47-ry*}dQ6W#Q+{6P{syi6S|EeC>dR?8k+J zAY%b*O>8!Ja=1+eBv^*PF$3KL1bNhm?yG2RWp8bMG`aCKZ?WIHHF&-#UFrovKq|8;3jknF$vwv$^>c!=J+73V#- zsE6B3sL^Ca;-Dc*#h2kz1eUZoAK#H01xGLO7OuJb#^w;*c!?m7CUfl#kLo2pZCGSb zk{R{Ks9%cl^4`{MY2hfhSs7U{fiz>7T6*JZsP#P?UR*dk7*Beca8mpD&eZV5wU7aY z7|SAt20yga1dxuULcjP~oxrr35kHL7gBtP^4 zq|^BFQsvb*MMo^cw(Bw={GR62RtgBa7r@=L_)3vSd{w`}$WlxZ;6f9%A=&6Mo}A$n z3B&wr9lQgkodJuCAButoAzxR5=9MLbqLQNjUP_^Jp?}Yv-pXrufhT_>(ARfu!W-f5 zPf^$%mBgU_8F@r&x=_3>hoyX3;k|tipP7q}4GlR{WN98XjRD!gm<}?^Wc5y|Zqa4&%e#4tb#&d$uVnlLiYU z_{1tlUbV_2f4L`Sgv@9-@@SN(8Fgt_r8W;Zf^v+DvbExuOe6D1EfhGn*jz{6R8!#b z3PCpphH6p-uAf(G^am1TOgx`>od>5XO+a^lefcOd@di&;I|wV|86hiePQWtIK)s@y zl=VApi1dwlyK-HU8EktVP99&^L#~s*=x5#Lp$pTO!8vUcx?U)=!%aef7X!QJyfm$_ z(w)Xr1%)ZYpkQkPei2JAuu;KRsigVzenemV$e({=_{kFxFv}cUoNMig`{YNe2pUXKKDOMKWM(a~xCG;%X_Z z03#W8Kk9S_;&Xgh(dl=1&q|Ow?oO~ruq4-WoVh++oh;junzCJ z86+d4vrkTbP=~hIJL(&c+zGm&82^%HeB=bIdus_?_|;ASaOiyfJa*F$_r9ys( zfW6NjDqb5}{ECJ9JAR_9jr*My@-4RxL_C*Tl&--Pz@&_|Vny&n2N^EB@n<_o4?cZ5 zj8U1*2ccZ`D3tIvMc;BS$Kr(xu`C1)Y~@MA%a(r?y$Cf6T)46HNH@KkhQkXP6&u?j zW=1BfWGKw`_RCWyyt|M9YMs8NdArV4!y^TWFMAF%GQ-1J-<=^Ad0dNTMMdMD3hX8 zkg3U$eVU5Dh2!M+z-NE89c{R}z+hv4n>j-6fzy0cnXDm|*?R3*gS@@rdJa!nJt_Jq zOFkrlf|Eo<8R?c$s#DLsG5}9x0bD($a>!iq||&LeK4j&^^f+ z!f#}wRjLk>;!A8Sdr|Z#9&bGvZCxQ_Hq(O3%<*363=&`X2_JGWZKeim z0AV<=lU3~TGlPt6kkL0zjK1dfc$kz;UitZDfRN7TR@YH+x1&?g(3XHdTK-LKFdqe;UvDn^$~KQAB@A*#eSKEx4HIs zXo{E`7U=u{6QagRmTsK2Jeg_Ysz#0^UU+@4Qw_47&_l{iiGU|tfG1;ht*L3k{RK|2 z^7i}m?XS78-1w((EtLKch2xq7Q1z}>V!aoI<0_1uiCg?0X{Z&M~gki9hC_}x0=R}2~r>31} z2TVrgg|KCX!IXvKI@Q^)WHx= zP|dWjy`H5UjBUy-$2`MD`vjZr_9N(JZ7(J*ULP{io=ur0QEh6>W6GvH~ z6!UJWTtA$qW~`c^+(AahIxMIS_p?TgpTs{0h~$|DWtK1Z`BQ=Wn`)>nslgQ1Ktmb46P>tQqTwXlwyx~TJVoXZ zfr`hhvpz3){du=2@;U~VgADnp{mB-9gy_$$*)oCK*p6{K}a7EYC_x^67Y88YVt+QAVS&{ zBdi|g8hrC7q(4W)fZ!^YG4EH~^X&;^Nzyp=TOoP{awbF%d7SScq;cq-=mAQ62jweu$NIm;uA5q$^)TFzuMCv4ix zlrD%CEdgEM;p@Wsg+aSuKVN)kZ>__Q5SM1X%;@*>_ z4ZxSwF+pWdTVRmloJe=D0w~y4CuoIc8_3;Y85NAB3ZyI$lAhvC5_355IFmcb6O3Bc z4eU8Evk|Jp=ljDHZu9N{iW{O>Ar-RCNstjO-dyw~Iw&T};1_r@P6!q`WoOD=LApp= zkyic?be{%K13zXdnU|*??)VKKqLMVEBV{np)mu$y?EMAOrv#z6TGVCouEZ+Wv08v( zK&1;pfJFV{qLhtw!+PLnd82G}T@?g=2nt>q6b=xlGjcLURw3Mug?;FcO-a=O=+1JV zEWX{OBHHDomR$Nn`v=!xjV)@9#gXq02#Vz-X*$G&UY`G%g4f=B56wX{J|7A*^69tW zkq?fdX^rzjP<>!T>IOW!a3qePQWOxV)sTDtdPRoWEmGyC0oNC)d(;+E!Cq0IX%Sru z`Fmr{+}zi`)y_k0C?^K~y=ZYi!!CH6q>GI8LyY|){O(!$LyS!tq6~@&db@yZEhnS= zMfm0D!;7gO!Y^ZjU&3!5smphKnWYI_O+_ zf6qaH$%fnO=lQh88o6G7m^=qs0G?(kI}o!^c6X^KuO^3wTz|kV0x1S!G;YSD(T9hE zD%TrUM7>iU#4V~*3iheTya{}0GlKVz?9<{wa{8w=F1=5PFHGcHp#BYkL}se7%;jD>*3%rK&Q&h&9bLmuVIoXp9d^~N|9WA?j) ztQ2fca_zMG(6tR0`D|&B&K7^{8cN0jJ-rfd4_g``8HaT)QT`~c0cE3Y51BHCx@)T% znHG-)(SzTKs4;*ibIfP%kn0DTuTNdBK5WD}$@_`P=ob(!tlm?t?KFyiO0}0MFT`e% zOL|YsTU{IAr6B8twQpb06$MN?n;sr!h_}NT(jM?ZV%wN%RnanS-XXrj9py6}`zip{ z>?XlG;Jc^z9{i|-%lC{0H4WaIY9s$mGQz)={s2v zl!qzIPPVimGvFMV}T_sszK~MWIc#WhqVq3}=WCyWm9VuL- zh*kEtjczyN@W+eVtt^2+y@_t%JyG*I^ca{wps2ERF&I4)bjyri{CGYd6uJw<5sYcVhUG{mBK!kAy{?DT-cUtaKWJdX}-2e{wyEH z8NimzM`1|A<@H(k>0&TB?iWlpfA<=Cr_H7#=&~8-Z>lP{TFl$7zP8D4k%>_mnPsjf z5S0B&T3|Zg+7HS6qHeea5igQ{{g!)RWyqUj~;9E0ihl=4m@^>#%rO*g+Ykeqn<_a+}6_@ z|E$1b^jC7A-R)r@ObaM|*wej(^8sm-mK4YeazBs|sR)-ZogpFunQTzMaF^eFkErkl z6UBMQ1>evOApOmukMAx&d}O+h7?FQ5aVqt3%D?dul56P_-np^oaQ)^5q6o9NQ{g+p zfX11$6KQb2-Nb|eN2_|O3fS+DX>HUa*My|RV!D1z$&9w7*Y!2Cx-QSu*Pa(S_x^s_ zimT^M-lCD{TJY{aeK)4gVjuDS{=r+k^^IZPN56%-<3QvepTkK1TM`J;!~YbI;W9er zR3y5~8c4JG^YA<7Q8=2-Wp^wlye*~R8{=Rsr?F4NZ_EMaaHsL}jf?P&gOE!ilm`DP z{(%Bpe$v=IW^(giZAVH}j&m6V^boZaK$|4&3@xXPCc9>$!g&lrd#bQ&y`T!h$_xzC znS&b?g6j@hq8MG@G@8i|xzKe>zy`Uzvg|0pvUrN@9(j?sNhLs46=+U0RnsWG&LnE1 zQGL{9MldQ*HD}EEVxu<9-A1h6;#8#c$u=iU!Sdgj1<1)@GWUESB?5$mn;exwlZohJ52xFFe%f(-UNJ(j@aK+H!=$E}=+7J*-)!wtQs=w+MvB-Lk~O>F$y=ZVRkMD#?X99iPzu@ zw?TG=Js9{4kO7vG!8xeJR;E;)P;}fCU1Ohojy1+L%x~6G*mUIDfyZWi#dyjFUgm_4 z%bWqM?2Kc>y*wdvWUOMi<}G-PS|QE~Sv1X2smy@0+0jxusVMniI#7od=G&wqb}raY zrtI<>CXBL0sw)*W3ZyWs=rH!%VULBmY=+fyM#vk62N<3igr#HLE8*p)X+4JtoJPDn zX3LmlRJ1MOs)e1SM({n}oSCFl#2(`vjJ*l=Mi>EQM9*M|s^R97`&^8mc;d}6Uxx~$ z2(}{Yni#1}jx&bnT%seZj-m!8=P^p?SbGz^o8rCX?kXAJ<0aiB?yM!TZvPJ8#1vPCc~r1MKUC!gINt7f8^lF;o%s2vPw1F0dz zzUL)Nl4(QclJIkCkCYoZgkgB=&R|Hko}DC z3ZO!To?tP2QvTAcml!wE%*=&Yo&wpV2dh_t1sFUPKa_D?Y28XSLjxQSSo19`0(PqF z;wnz}YV}O=AR)m#vehd=7=$BI{EvGs3x~C1tJ;D?`PVrfUFdpJGML@Os+_nf6*SMD zUMefp4Ady441L?;XrP0-t1qfW?+Oeu<{fd1pxI0ic1NJe&N~)G?6tJ9Df&<--k(XM zlO=A2BIvKTt5$c=scYf7(K`$l)D4Iv_cmS)ks!fCe&nm%ygN`4@t_EFBT}%zZi8f5 zfV-VF$qZWUWv8Kq!*U$B`zs~-#&jNQVoTP`vIxqNj^DT;?3Q=wt*$l?Z}XmoP;-%1 zq6MC?X2>{REilJ~1qWLg(BZK!6YS@qM!XNW%9bkYFF05OWD6zOBwhyX(y(a<8!@8q z67(HuO0vXVacTWPez#JwW912{%N9`#{Uqb`6SaJ7nw$ZJNWVkZSX@Hjyedq1w*-@R%YG`v;YV3@0cdXKak?&oePsC<}s+9^cV zOD6K$l(-ULkZiLiTI+em+j1c4@)|$v9_?RQI%w$woT1)U`d>>p{b%)NT! z#rUjo7Vf-W3L6RU!CaOlhLa8W;dvCY=b`d1%$wg)^~NFDPGK^NJMG}#V4O=e;vLB= zr~LdO522zt8Efq^0N@9OYultJ6pm}{q4A?FsaH+9;9ye-czxKYk&DL4n0LU-ry8+! zAPox>vTz{S%?IJ!LZFqHRT85|2K{*Ws?c$?5}S=@+ z6Ts>*{ODd+P3Y#07yQH8Zvn&VrTd0X>u&4=k6m#bJtlmDappo<*d*0RhOMh8d`VB~ zIef3tBG@A;EVr29Fa6`~ouawk~~JXV^x`g3o-{TNI2Ji4WQ2w$JodW-pF{oE!N zdR4UsjIr~6M{70K>w4pvos&{dA?%iTa29Q#raQaN(bVqYQyc(pxJlWv%5c4Jd3Qyc zTLh@Nwx74TD@fNTmfpU|clP#OTCcbu_6S(lpta6{bU%(Re8MV>Q8XJ?cl;Q4n;UfW zh=6sfoq@+*FNR#C^nuoIthOzY1~$ZYq$(ILIsgBkZ>aFKKL!Wg2quR=wyl zSYc0rV?W-vbVnv+tH>(f57L$s2+rm(EHfG*j>H1ZY6Xd>$@+GA(UT0}z=a`hUn4bz z68k!;3o$`NVFFl8zM7=r>jJT!0@|hy9@m&#UvhjWhR)tJjn%YWUemqiM1_tQ#9zdI>b|0mgT6rD z4$~*LLJV$o9JBy`#vu!qT}fDQsEEu{7f&1FHQ> zPqNe%P?cK(mgAhpm7SC5J3nwj%!#$C&;lWCR2&}>MNnKNy(3x4=_vBL_vufFz7-w< zh#n#+iD#Q<>rq{pZ6pS1nV<+x#*-ovp22?AC4*r!*pE*t5I1S!F44!!mpE-?Z_)|8&c?ah!-v-az?$3Pz z_!U>sgnTso9(V}_vU;S`_(?tH*FIWQe(iV1Z|p$4&7<&9_>=lc>lnapAnjj$ef>9m zZKko?rjiIH9&-d}*p>2le)rdr02>NNe`B!Czv3Y>kMXX_6_N zvk0S=9UzZeX|l+nm9c<*pEV1?95ZQDs#zTa^ks#TNo0tDrjqM8!j05`CW`Aii?`9ea=J?Ebn_DItQvl@z(%atVK|Ssl_I>N44d3Vu0sv_#7r zM~xdlim5n^@6nzfbs8OY8y);DzZK{=?xKk45&kH*0@(7mj@P&E`(hf;QW}rEV}II8 zBK0U^hgLe}PyJHKpp~hGN|_<_n?B1}{pb^Yr#C$$H$CQ3xTW71iZ^xXy>KWUi{;U^ z7tiZk=ikesC0gv|W9&Jq&iW(ZmOmT@uN+3NY{leGdTiqP?6wsL@5OC&e?2$*&*w() zN1w#6-(}P%^T<8=Or_*7JnYf$zjUu8__RHBDP{MxI_k)0WB=XPdxFrLj<#*Vef4Gz zrN^jLGKd6K$0T--qQ|JOPS$9Sh5!3{x?XmF9{=U~cOD&aduuYk?+uir-@g9p{nFET zL~~RfiU*@~^uMNk+bz(r%cig|`QL2~YMVa)mv(>W`Du4H;beM^3F-j0^wq;13&gy$ zn1!;4-v4-?oalo7xXz{Fr?0H(YV)a}mATncDV&lx{FIym*c|;wzCW*jl|PTK&Ofm! zQT0{GUmJq`YIk;DrF4z|8|{_S2<1}nN54A7L=R!p0^%j`^@|gT`nce3!_~kVC^Y~BY zez$Gri2F+CQTWD7wR`0>`d~A7XES;>F#Lcp9)~deWHoq(FnHz<-TqbX!t$6Q{+d(7 z|JU{XX+Qta@Aufa{~rI}>0g`m9kYsmVR_7M-|}{Uf|bm+39^kP$By~r(8^qN_JjRT zKGpkQHr-2O^C)ArPh+DoeB+O~@ef=7$6R_i2n~PdK*M7W4;hEzW^=>)&MRv9D~{~) ztKHxJTEh>%b^V@8PD+Q;*N@-!$47g$r4$NjWFF~{5BD1Lsf04_JKr_`H7@PgiEDV| z>%L=r4TT`Y!28Ic3)eYEY6P0eM7aYxEuq?TfiVp}L@7)Xz#Y7xmsVOvc^Om1Rpx%X z9N~6^H~1noTkzOZJ`jIS*e_;Mg988r=Ys%1ufA&uj8U-2kC>;``bFRl4LZQfXD02K zP7~vbVLCTJgbTmK0y7|x2^aOVkFNI`$v52h$Jj$au15~J9}pGA1bMt?9ZhdTM)4%p zM(OooU(>@{HhTewPSfJHIwT*d;K|ejiJdvc6IqNtAdGh!OuwQs&K#Nt=stty@T@HK z3b1_s(n892UHc&oOdVbad>!d+6X^ZyaW_D5RC2YNo`xhM?gGH?L?_G&?%*czN@vS*kst>I5*2!Z}iU=2KRQpn|;icjIOsW5oOm3NrM0EtD9DldSyGv!cZ$beR$hVW;6u+GGbRMMpabmAUS+#@v zd3u%c$YHMU+8_C~^X=2{M()5y0ljk1n9>;dNDU48Vi63RR961FhN%SX<XP8WDkfIqPqhFqnM{et^h{V%&DWGF-28e!0~bh`dpl3JbL=3gY(Mr)F22b+ zBc+vyw6IYg9qL0(M^r0wW6Rhxx_Hcyp=^Vj_%mN zeOxkb)}HAdHGRp#wLy<+QO^+1ZKy*7nRuqTq#{+?n=l0cd zR`i58S4js=Hc^^tPWG}FY0+i|-KA{eT;zvk( z3+K44+9=)`9Drw{Kxpv5_TyoFzKdJyZ1^sYXXR3dccISWJNz^Y_(JnJ0vL+zCfkYl zS1k!g^^r_M6r@|g5ux@2#810CDBUY;aFj7{au8C|(Dp)h$+3Fg*5EigPE$6iaPBAW zSHl|!6V5wtYEHn(pcA_66sDEWG)su8F{H2v2_(?vz>D?PWZV0sW!0n|#ursP7T<}i zX)C`cz-W!mI?l`FBg3+W&D-=m4sT)0?)WG)7kH^!D>|D3(Rb zKwUDVvl|}vLRy2KL2iXHS@Wu+#TJl44u`(|e)U?b-=9SgKWhYs?-q$g2gFi&UlY_; zokE4l%~Wy%=+Ed#v{%{*jK$=fv*|M_eXb_mhy3isYdYUhg6v79&O1HyVA`Dqq5`lc zn%H)5rO8&}+dqpU?YZ5VF*+*Fa@lfTvDE~A$g=b#B`PJ!y0RM|5w$2}17;;{lendzL*FRq2X=W&{a2zdZCXYV2=3oxcnLx9G`mF(w-%! zY_A21W$$b4;^zj|RheHD? z+*unfN@K}J&{o1Y^N<-U4ll$dHM~7Toeo{u6uG$<-+5=dUCvi)#Y6_{7SgpWiE&#N zEnE;mLLbT4bbi>MCmvlPzP(5@rTTc2`6NvxFZSC+wQmxkuntb3k9h4;VoiXYjkj&e z*@^kqmJV!mn5IXBCF_H9gv7mV=#>{Z4AyRlw{4m1!iM6s9CzIPT8|ih7#}EY)w%88 z8kX|S(QGngOv|}4o2GxrkRu?y?a*M95DNf0jet{$zQ`pVaOw_$c-DKJo2c#*?N`io ziN6?qM0qa?&)&RfQF^jLChNV^u-bAx9|IpFRvr~V>3N!;hLa?d93u8yOf2F zCL5@7t#U+bc*~4Y7qq#!jZ&@whcT4zgcg%bJIald>#UUrGkeCdmgvNEkk$u9T?1ybcCs*dg4S8zeKApQtD()W zvs-R?H$;oKou(C>`EkOu)j2l3i(8?rTT zb8TFl##cO7;0ahJo%d_W!SEg(@n!dwi4&=g44S|(b{hhw?#~A|#S9=e^@8WCgdv*# z5{P$)O5k9t7}^7yoqV_`MqT=8GU6fEcL+Z*`&4+*l&X*tnQO_p2ESuy>q$0gqYpqG zBmL@uK@GSFfsfC$UZOr^QnplQavveY6d@n2LwfJ|*c{^o_+~zSdWW|@FR5VN*HTC& z%t?r=imf{3^zw1$!Zb9xB9*n@mRRb7HzcO#Wt476tC-$0EKlydFM)`U9Uu+>p8}-n zjRftJwIAAi1{{2;doVNA&DMa?c4A^3NO1dt&els-ZC)nqS2E28D*iqscMmH{{Qn z@S>tXQglAiTfyMb>2k19>PpoOMt(0yok9+j*P1sh2%mVcPn}>;TAzgcE`zgF)zN=3 zY}9~=OQb;oeQfD1UNaSIZ-`kwydajo_VSthgd01~{DJ4zlNnz8cTY9lEU}j2(rr@W>@mlX<_E*0v&sPUYvFc!vdpZE|as zn_bDglft_{WZhVDIl@#J#|g~nQa@Y627Q52Eosb&u!^BGLA{62ak&O9Pdb+20a?- zPZ!<8XuFEO>&!uJcdGPrhMMCa-#$iwLPm3lqp6_ zHQ@oZH^oh6Ff=1~M&A$iVMjW*ZXe6~prnKJf%j>1Q~kh@ZFPWKu;{uBx0mz;MmHYC z(ZhT4IT#?02-vEFgibx@KBJ1ffF0i}B%>hJ-E8xv7OLn!8QiPIdjI_Pcp!K=5Ic@h_iiR)ewPs|rpv7hB9Z_~-EJqorFS&HhWt!StJz7le=o6&^}=rmjQXE1=2*bd&q7xBbt z!)rPljj6y&>)pQIIS~0E+a0$=hB3mk4_wmY9b1$k%6=i7Xg z!V7x9>Fixnz>`NnuI6#q;KI7sms(W97#5S`3CTo-WGlpCpgI>iEkyEo*E{#PGHLHP z?blCJH}AZ#J&E^Z+azYuW?g&~Ep=7RPK6~d!!2?s$^bw~-Nulf-9``xX&=lP!p5mZ zOtGPY1+gKOdC-^Xe2fW-ktuQJsWNs7C*9tm&J;(5kc##w&M!UXp9a6KuBS*SiR~6x zh}ZR~N%lY?yv$g(t=DA_my`u5Bw}YV?5}!p0eYG#MPJ0qM@2!l$RHn$J>)bL*D2&g zYaLT+JV)e1Xe2O}Pj(b(Q$4Wa)5yj_l~8@S z5(*I^a`dx7$aQMwKh;PI(=7ofph~2m}sw#A}P{X_9kNrK^a05%}$fm5&x?BXpEJ;zW zLmG?QDN(HrQ(HeicCvi@T$+DNxTG8yNNp+0{m(eXKYbrC+ZI!}m)t)vi6M(bE=QXt zdYJFoK8WEfjrk0B>@kOfsz)KHN5!$Z!7-=8Ctp84YWZrZ{1?BC^B?^-zs?@Qp3=bS=%yW4NE#h>$4Yj-XB}LM8~>B>?=krQw0_I`XMcsie5s8sq@p_{Up$5_V|U3>O!Jfkp~w>Cd*8PW*R_qR%TEMLLr$4-9B2#KK)H_y6<8$i zj<>M#4NalfU+zlsK!J4CgPz!0W^u%*Jmr%JErZncJB(CLcav+Q!lok?@y*9 zdB<~3(jZcAz;r-_eb|(-ZJM!sjxd8Q;YRC-CS@SlZuZ6wk_NydWeK749yf?xbg#on zT%!9>!cPbx#TG90>qFsO1kxzkhA$jC;K0G3-Aw&T6q7B*)jjAXr^!8tNi%16QfhWs z*;B$o{;VA{+rd0dj z;z$&>*WA245MCUL7qds<8>)HJf>yS{LYGofu;iD zo3;q%et0{w>rme8zDuf&Ge@|51AK^#dvRby||VJhW6hS;@aBu^jKje7mk%CgseArt2HAGh{~3m!m-FuBtxv z{Rqut<9j6nFZ?|ninF9dKfkjGxd>uzYS{vKH!I~A6zM)p^p{s`fTdqori(A6P}wK2 zqVc(R$-N+T#{^T$Z32+0Sft5xeH+f>OUs(;6_HiN2g>UmKDo^hYQA%M^dy+cVW-7^F%kh%#Lr{-!*2P^ZQRUl z_{nZQ17dm1WAVdo_6}xp%wP3*|G!@UrO#LYhxwQO##evDr#vhF@IPw|+j@Ek3}_FU5>+cB5}D<7N-Tc~QkHIVeM?kp0zt}C`!N2vG-}-sy z+n=cE8{d9LjGKYY{#!P{zPkNA-SV5S&d{%Z5O$*v8q-%Ey&rDVZyK|2P`zg^!!H`M zSH8G5Zaht6`39u-3}*c3HS8AeeC;=E`pRxT2J{`nua3qe{JaZhpES`EoEC?n7C&6d zSC<>ZzIrIUgBTu$8@}_CZgCi9_QkF6!EJOHZt}yeeCM1F!!7=cjr}7JV*ZqEn(sL1 zy?~id@Q-VgAHL6k8sGKU-_k8Ux%A$+Ena9$KY{gr_`dJ3^&KzYK)-!r@x^ZO!}+WK zo896Q)abr*VzHZj@a;qB2lIUd#Qb7+&!2f5y z(}8-y4aDom-~Ot>fr{_H(zpCGKm0d7=P$qg&dEig(f^g}o{r!Lj$hl#t8yOVk168& z-!F4I0siaTIj2MR!VLcj4A@rEGZ6 z?~SPhgtBYkiz=W$3w6WuUhdO)x_aGG()2+SPvM;BP+NJe{^}?e;g+W>0b@gJo!PEg5?UaSyQCKG zM7YwXJUl%K~b9H_67O3c>X`Jt^XaKw|ZxADROszcr}7Uf2W-|vxa%} z@87Wd=NAk-L!CbL`Xe5wN^h0F#iD}$F!wKRQ8+sM;j;3%oe|!&r82b$i) z{ip7BunzyQ*Z%ppOMkHY4<@ztPYZVIul~cKAND-IV~kfvwN}4={?KssM{AWIe*Q4* z`r(%8{^n>uzjghv-Q+vy4<7M9JnCTM!G7Ao-@G7`V^ibxFzru#_rpE`(pm*J-a1!! z@BV}Ru5y^X{s(t-4ucIo!vA2|4};ze{^Q@DpYOI|g^v*9hgq)j%V!`)^INx${czK8 ze=&80DdGkh?+^Q{%lQ5Shk%VAhu{U+Xnx#CKYp>J$^FAIh|ViNT>p(fCU5^Z2X4Sn zW&FVm<;PuaU**5&q|%{%sBYpXcH?hUhNyyN0+a z!oNHY|MeU%g>uyq;5y@e`G-GR)H#>Ce-&RM{})Z>9~=0m{?vc5C+>f2PhvX!@S;EB z=O5Ybzf&YQyik^B`?{)#`}zv@rh-&~^rhyGxs{D&vMfAFn`{?&S(f6hDoH}`Ko z|5aD|CtkMg;N*4Y5lIm9%`qDHdt5)ZD?y!NBGmNU3t_IwS909slTi&g0w0NQlFRnM zbMU}GgV4MafJ(dXt+{%_#qgVJjkIPk*#&01vUDW%5r7Pu2CCJgf5sXSnbH@#1^mkV zrxdsY$wG6yZD5A-ZOPdD3}*s#XdxN$2z*)LJF;Q#`KI~ytN+^;{V0c)v_ucWx*Ot9eQQ(jCIUs{6UD&=!)SbApgL@oQgz#P$2x z$BqqIfyO}jr07y-uD+X{TUbk`10r9@gqm_ zm&lJ{4-t=_p6t&t!hdQie|fT>ulwt>^s@`M2n{F>U^N}$Q{k2Wp4agGXKcM{#8zo`|q#)&u{MUoNApR{@?6RaQs*d zM5fg{RfvPR_DA21ep=!Gv0d5!FS|njtzG?79?JXatV09J#;&q>`!-q|aef=QTJM)@ zw6dSRb@($k%HTW1;3L@RJoM+|jn_s^7yE)gNM)e81cKI-rK(Z_#! z)&ECd`+v}i|0_Nkk|0WXt)e;%6M>8pj@JV0BmA)U z`Bz#*GQ6}(AbsF0UYed)63qumuv&i>5)U9goqk|ynb}ayd^^m^pt)u%s8STy|8)Di z@Y3)_4OJ9b!<0E$wZjk5`l@V7)&`bHv6n?r6d5d_C~N^G`JQx6g6|L%drXr-vFFK4 z_xRdDu^_l#ht)k@vfxP#m&xvoORUwI-WZ2}F^bw#r^0&ZkjB1(EScw@_(OgaM?A3O zq6y3*x`^mT4`2rnT=rbSsqJSBAmGCMia@<2 z_BK|j;Umdz!NbArlVL*qBaKi;I8n?8Gdb4A(q+y^x=;Y!yEd(9b%&bNEyuZ#}2KCK?R! zkxBYqd~A*^BW^&}en8EM3LqD>1BLSIG@6{4yodc0#%XmZWoxI&7pzP(W>WiI-j%*b zZ7HJDb@ihnCmi%;qz7-_=f>fL*QIrAW721}x);%C(`bp>`SYY#n53zPCXEhwEC2PR zqrnoT;G=k#KgeMCjyoI1HhX4!5cX^-*C}ATw1d}_(K=q!*+Xu>(e50++9&%f0?_Lik(odoA zgBMe%B{h};hN~gfU*yxj%8u@q`aKh){D2&xX4*$vdG;hspE?X(M?4DLSNI_=C=$7T zMm|L|oRF_yd>hQoVK9W!8e-YSJ(kq&-o5d0dMW&&v@g&{U+i?#X$$@wtHx?LG!j7wT^U9`laublznxb+7(NU1@%j^ zIUUs~iF9H%w?`;SB};idY>AW!6>~yLi4_qQJxkhZ0QxwFj2yO(=TL~!R*ld7T}EV< zdege#9Y7H+Y;ypy8~bLLS?irkIyX@qj=nCnVsuP}FVP zu`KWzne5AC0n>HN{OW1JAQ9s}=JqLACw?TJ(Bs!L_j#Lck+a;7a17AXyq}?hVyDwS z`mu3j&IaLp*0X*Lm{jr>hH|Q|A+_AbDy`wcdqu;rWSORRQ9koYK9^px<9z*LI4a!v zGOV*rKYFxxAq*jAzb*x<739LzV^*pr4BCdFJLi1aU?iA2n6T-kF;Lr-pglXtz%^RA zG(RJk0_rujXa;{}G|O$KTn^(6np4Vce&^-1O&9N@@5}rF9sUy+0kI$scx+f3aX_W! zru}|kXTv0p-=$M7rBP(Q2GRBhCdDIA3$@j#6X(lw5Mg^x40s!H=8sVq9aIbU(fvnC}F+Q3j zA2n(peD2iHWNg;uF=Z;0I5u}J&$V2^fuI)o-CM|_QS z*gkri7Y^ybi=%MWPZu7FGi*eUY6#5?vRzyFcZt2-vckp8fL-$#&<>9flEAv%Tr%d4 zy9zrfZy1^d*jD=JoBAx7t;W9BBG+0&Qg5*1xV;4P?GSv1$Syw~m|fgO%ZoBGzZEXm zyT+vKfa90?F=sos}BDepQ4|;huNl!DZq>**2efBKH z;(OyLo*ZPI#g7Eouhznk4xT(IjqvtvlQto?2D)9Gdld59ba*iAe(ps)S@UHv!8YMl zYxPE}`k%rBkxM9`;weJ*5T`bKC94SFIZqV1%ke3BN$G)oWxwR7Ndg1yUxYKJ;Ts{O zYi;2UBhzS50A7oPT=d;g2UABs5@pds=mN59oiHg7T9uR1^V$kIi%WUnW*L7;!@8Xi zkIKxMBPSIJnOZ(Xm&ctqcw#OHbcpd7Iq{%KiYG69w&uOw@^qXXi@eZaY@mp=%LtJ! zUo7RT8#sOz`Xjeh9=0=j+DBjzSWKweXA__16(5ixAoDq(XbNo7U_tXc)H`>hs8R@C z9ztFb{3=tPuAmkHGxs_3cGfC$S_w^j;9*u$m=?8L*C$Qq;QelGo6roHV%o=}9gHHz zbA7b1{Ca>xKyZqBm2pbX5;#0RN=H;tO(zw~%OA=u0_}GSsZApgFWlybut})#4!lkn zP+M0ZayN1eWm2DoGtiy$c^{7Y6q~c$VHsWmDlxCihLF|k;P~!m2U$T3O;0az_}rk~ z?8a;c_cst^BytIMgYv=VVLIv#F}HvEQh)lD{b0>(sOM(wN9TJ7_#}9LgJ~ON-tLL0&$Vrso%d z#fqD6(}FlOYLTFN^R$(rDi0ZH;L>q=u2!UTTL6@75~!-;X%ges7-`x-06+O{otqc4 z{k+HfFyHiQnNKkvV}Qn(9GZnMX{5hd#)4j_+xtL5a7Bw?5aDqwf`TI3 zhvdg3<+NbW&sj+12fjLNahKGaGs`9NwU|FlOv~WaPfX5A81sTT zJFbspZ9KLv?%>D7TkL`zQn`oasS7|eTC4aziz*qtw&4Xb>Wv%B61ZbF1KGjfWKi_W zc%30xanU0O^A+>kR)-N@$FnP=_*P+y#@o}}intiXB>FtoBkpgyMDfU!XG=Ms`5n2F z38)lFT?JOZ%^ZA+Mm4s_UD|Sq^2gd`q8f75GVekB=*_{qc+HPz8S=|q?|Men?@L)R zmr!p9<{(+jrvs0NftOUghZ8kiGJ>v8HWLTryjDEXfOUiu8Cqz0I!&tC1;Z0HIgwG~ zEHnb>819VXWPvVJ(J$IW6pUAfu4x}-p~eOB5;BCbMy3n|mo;3+EsrFSHzVUPmZw4#nkYE1=qt(+u-h4B z9pU|yP<8O=t+Vr!Ef@s4fgy}%#y5g)3q{x>(>Zk1?iHd1Z0&gE6sc+Iar?rhbLb%} zUn^(#PgG8)j1CWm7>q3z4;^_h0jXU21yLqD7@{hnlbA_sg$jd_`xP;P?;d!X7POjQ zTibK-Ipa?T`Q^EA$hs7eJyhdLCxCpI-JguoAPEd4vyECr0jlG^z{_{X3fLCM*5t$# zTzE7oYE4`<+{;ui?!GIRBM84)*U&=`1bgZeicX*!Sc!??vcSMRnGAJfdgqb~YG*uq z?$kac@ztZt_e;tq=F%bWtk9naEl&_5V@HON>HtQgo7mRagPC*3s0P_S`9xU_?gyl| zoJI7<>3z}QO<)vql5HV#wg~1{+gf4NYx0vlsHMetDTL5;kb(8cr}0QR`3SNWG)W#Q zgU}EqqT4d(j`E(_cpRhJ50%_i@reCmdJY;yF zX@dicp0{$@#GWC*#$Teos9=CA;w-dFd{Y(1PDbli zEu6MZ)P_R5qogUIEUY1zO|j7ojdplgTdF0NB*L7HbSOTq_=2M8vFZnn){?t8lSAVb zc_BSBg>-dO%0z|8ATWe6jb4LhSDvwQ^I;uBiZ4>}u>km`s|<|-y3INBqqEelRxmA$ zkx#nYz!km*I3es%uqZoH7(MBViVz}jr`r_z_O6ySBWE>g?bmscq(Crh6-VBOjOtPb zlDPrKBvDdmskmOwwK^q|=s@aEIlnnu_pcaQ5WYH=Ci(rK9A&+?`BGuna(Bs&n;q@E zSI|LE+K6$>_K&neT}9nBt$2cxIa>q`pX7cb8|h5@Z6oYHZGnciO3w*d))h~`Tncas zq(W%LjH$Xcu=m3Fj0mezr32eR7=!VNaDdVv3u(^pNR^fx1mZm=-4QmNIPGFxCOp`A z#=gvFAUI&cDQ9gB9OCIhd$2FGopxau$(g`{l1_$=YoVG_;;P~Dy7Kn7`q0_1WmdW! zHhnk<&PN;nnR6!D7T2D18?Ehh$V!sKbd;gQ$|5k<94Cz+%P!J-+B&o=grhG@U9$ad zTtV=YRBm-4F*RO#*-LH*6nux-^uvXPdWO#A%%|9pu%J7tm~>i+2Ty4O@uCmJrB@k1 zVqD0KLN}}Z-8gc;Su82Hx6RdN^q9ReDE>quF6flLpN!gtG~^hF0b%4XXiYt$^Zcmt zG9*A4^30D)htk*mcri6fiMA5LI>6kHxTs?`FHI<$LG$gltnKqj?HT|o0OriSn#U!{ zaRz*4=;6ekXz*q;ZUSb}v; z&lilnXiHKo>8%CzCPUtbaoV7Wj3xY>hsyGceYJV#?t`E2154qQb46NRy5&oYM0wG z1!y8go|EqMNWWFDGx59` zN6Sk(3XNS%6bPVx(4dF5wKKV4PFjmb2I&T~X(s3{y}=EjU;2p3KXZR%DUduCusm*M zUJ?ep5T_wNr3HruDz3^Zlz<}xwy)*_U;ot>&w&YvI3E`_T` zlK9QpLu0qsa)wyS?DH9c#lMO|#_*by9T%WIkB2*;8D{=OU=J*d75IK>h9`RSMLON8 zXnvzKO#s4|DRBx?P#(Z3Oo7jK&<_VEHP5EPHL~)%ak1$^FnJOI$F^2mW{9SX3nw>d zmbt>SKLEzc-*1oexa(N+;88@KTQIsST$r@!SM!4p^|=jUqFgc=T0sLE`6FQ69cquz zJoSU6rx-SQ3Nm*dM~It7@Uu0>k*7*qUT6)MK6Vrw&C^&%nfSFWI%gN6REgPW;n!Sc zWTk~+``q@3JQZ7hr=*tyG=KWfX_wagNteP7Ps+x>aVUIt^&ruk^&7(`(dzxer8ylt zK~jorPgye2$fFUwlLRGUs&zD%e`f(+B*fMY&1AMdaBMSy3jt%KZ*+U^jz(N3)VDl zD%s`2Oe)wT>2Q4TI6-VMDv6kD!>+SCeFwNpeO4yoZlAV7JHO|m?Lezdk9%*@7+f~p zofLR5=EPgn`>0ub0ZK!{{U|h5RH!_`N7-7H`G;L?$$~g~1bQKfQf$0sD!zgfGMp%~ z`254%WnBaA=EE46J6l!ywgc*Ts9xhp0L%EIo)_+oW#*r@{6f_JH7S?{hF}^~rOeRf z3f|qMuZ6X?5hixV%xq#hrWYbG9tw^vA58$0clHIkPk;b*sX+G&vuF>?K*tUWW%!(j z!d=aeu3bQu-OV!E>OI|)P)%qsik?=^99gP$=1ChDZxQD}1CaQ>qs>nALG6S*R?5z$ zQyA4CxeyVFYZN8SK<%mRh(fS~zeD{ckCq!p`v0ikIEUpfba zG7KjOw{HGp!oU&gcK>b`m7bOpvLFED8DKpuO}$&zx)9=%_C|}`3xB2Z@DJPQ*qU6}2FGw0Wz^5>9 z5+KxS?PMaw8UNwzI1ut-V&tu&n#-$2P_9P;0qsT?a}Ys0w@}KmkK}_vBThz4dXw$6 z$cis3+%4aalGbVhjo7L-P&H1m-g({f*^deVS=|E!d2Kz2(mr4fZpI*I70LwO_P(Eh zIiI(n!?N1>P6x?H%4ljIKBXBo;Ft=hO+!My$_ZNfZDnOS21XJ>b7+vvIkp2siBf0i zf(Z`-P)On%u+jk%gyh@cpo~Xt5PrwrJ-gcX<0}pDI6%KXhdU2%7Dbj4w=niJH7;Pm zKnB9>&LxxO#Al3#p{m)hm*L{aMd`9QWrZ3F`FR}q1CQGCy< z8uctA_N?$axNYmU6Sl0!0?>3b7{atUQf_F$4Xu zVq5X{7g!uK0hnSX2GB3FV75F{_3o;CyP{K45%w+U(-eIq@=Nd*3_o*qlBQmvx&_e6@RlW+G*;fpOJi5y2E@> zn2xCa~sllbOz=1p2HmqK{mTmCiW(TJrw{@);ysXtsB^d zuit*syMoT|WR6rjx{Z2aV*9QR6jct?(krl5!O?gZAzSOk=IF|I7#cgrkmV=71Tvh- zEBRFI(x+M~Ypn-0pR%g~(_10R!wl@}1))z4VG)J}`B51#wjShJu(Q;X6sy9ulC#p2 z)1$Sm*FFe8Z*J|>vX33UTmzjmfDTg@TS0MaSj&chlvD3+YTI167sk_k6jSYmi3sAy zYZS$*)-W>e(@uHa<;?}|qXjED zcIADR9B9zRF9VyA;^rP(0GhjQXiOqnNuK7S^XE?Pf%d^#o%!Zz!6gEvfY4+QdsJsQ zq^+VZsC9jdiODz>64Yg$KD!%|`J@~I)th|@5 zNn+6$yo8=KI0wW<)mTf#Q_&Tfl+_AiVj)VoZ@T>8Bq!?)#%yr+RiR;VP2D_E?F(dI zU8I4ptw(l&zQ$c95heGHwv^)#Qnue=@}U*-!#Qs|X9BBe5>WtF?v@i)Vj*W;oS70a zsEsOHHUWX$GqLY9F~rME-QFw7fa;!@|L~R_I;I;GR>xsHP^;s;`n#K5+OmtN5NVPx z<`%!W-XMpscz6Ut^Kbluu3%Xq)$3M7AYT&7wzx>`GFh=foAz5T-IH%`AbSt+<{^Iq zEHD&|sdx?JYlX$q&^!UwB+y5EE3O< zy9XtJ$@~yGUgjS~DG16yrRu>Wax&RAaX7CaO9>)tW`gV(zCMeG2AzqeQBq5!j;r0s*^y#065^MG;t^Nl&MvZPQkXQxIxA$A7pEGeh`?6ghKUO__BWrU0sm-SVYlQVBI8t?~@GcqbH zhXhwB##re)F^SEGTe+g74qge>ER0nk0ak(eJ@(W9iVG80VtIuax-kUCkqs`$xZD$9 z&Y!ph39h|DVp=VvmebaV4Mw6SU~TN`7t1l7rca_Losawp!}v?=dUHAgjHTLo>7bk< zlL0s5CMTZV9H)%n<7e3_OtPZyPv4o-=9caO0WE}5R2}ad$HKbu{JCTbQXGjjLWtIs zDrw|ZiY zId=-rAS^o`WB-xQg-C(bgru&5&0Sm+BiJW0QV7!>W>idDI}5It3~Qfc>ojsq<`ozl zVUJ7RsM6Ulkw2xpT76;x4@I1+s8a$k6dF3^g zZU>rK(1y35H7%B!2a2V4$jN{Lbfhe!R6u)08I`a#TL?nKuRmX?eoDnawEd)%WEB&i zGSr+NG~&W8M&jjnzIQ@wh$KxytCGoyoY(MoB{8-uuFSAvM^!0VFPqF)-**kcKrLv! zL5ra}z-%Fp0F()iq^WYkY^8(eI3=c6oGj`fTR;wTAboDr3ju(m@NomHt?}knpy9q0 z+ED6fTf{8uSz(cf$9+*Y&oN>RHqz+ZQE5#K8f~vsc?HOIVm6_a-tFAtl1CFFQZl;u z>1xYYardjD0w8)MlI}*#aEFUzmKA!H2%WduiFMDK@PS_!*#5{={VaxdDpLLFTSY#|YCrEsV$XJ73d(qWdCYd}eYW!aHZ#B8 zxXcxc?ki-dVJT{9ms=P*{fs3v(Bjr_vK=E9g`NgVYBKO4h)IlMCpC8jZa07{5{xlr&$4#_|c25e_Q^qGyB8y}5Tw z3Whn5+=a(+tBnu{x;q)2ie;Kfcrv?QcwMiPI-2v8A7eo;k`g4T*WKmqVE+4vtMm*) z9u>$?01muy0#LEbr5l|0^47u>d30lgrf|9^j?atBQvOS8iE<*kl`}mm!+THgPI$IF z#6YaaV$G;~LRTF^E#@utNJuO#w+>0)o@O`!OnU}yA4U__kl|2bvrpm zp3Z!Rs%^~@*T_{m1{L?-h*R6j3FHptY3Z298<)?#ra1?MnzK=4Y8^=gX4?J3N8hK7>?us*+GoNqOyH4$ zd2tpI?%E!edQ@aoS*R_2L9>|LnY6S{$r*i@5c4L$r4I6~wy1p^A=-5nAS?6;Mocrj zR`PS)E*L7CLzAJxliT@|6XbY4es>lRpGf`^V&;I7Ey>k}ecdHZ^js46d=l?)-zcW+ zsm71E<*^247@(uC++c%I=j^>UB{F2&9$4=v=W`mt(>h6&;EN)|^oUl9`Kp*SdM%Kj zxkp247(NN7m2TT4!=-Jp~A#O|69IM@|$DYr{`pOij3 zoEV};Uw&1dqDL%!N*Yk7P0>r_5c!ZsYp=FuhA&w^fn*--j>*8-q<-(w3?qC-eRL2C zn2u#YE`lN@029}UEi8w-tUs zVf7g9|%?;GPI!l&czT~YGqbi+iL|jv4TeNB+Nb!C-|C8fN|*;}8~>OGPES<|i%>)38zy~8!7BiA zVUM{gp8}n1UG3|AiL%ULk^63ZW`!CGwes@zo&-d43IxNF#^G?6`dF$|omvSMxpoY0 z_Z9I}T6ptdOiXGUz_HkY;Ia0hIy0IH)3^MjL!+{%V=)vFf?#Fl!nl@H`glT#A1rj8 zKsafQLXu!>JNE}jHhHd5@R+}Oy~h)Og>o3?emfKfBFm%H`t(rYFa=mI`V=1GzwpNc zQ&%iAq^}InK`H$nE+Gx&UWm&xtj`)n?wZ(1YsIN}v@&kB|``vKjwzJpISqH9DRTEpxtxJdLmOBQN0S$VD}6E<<=UgsAIZ!q;y zmimZ9LULx&4&1t3y4_f_sR37rMJ_d>k({Fa ziUmC?#_DEVM$cB2X_yNLj1m3v(&PgenF&n>N;}o-R^tnZ6N+=a+07}gUa%Vr=tBZs zY!4@#4bZC}dDs}dTt6vcrCGV4~ zK1%vEcjl(_YgpVMQ9Sb5?w9V{t{(2~l#Q{x%mW=sY zdIOd^SNwjmsF(!K5#sr@pK0@?5-}b;QjRRJq_dDEplzE_+ixireZTklMS$e_0_k@Ew#);Gsc}QJa8=q8;j6`RH@~sv zOsMW_0-psjB|?kH2n)KBvivCfI(dm4>h&o_acEnpJ=;vIG8+`Q1hME?B0JCx-@rLu z5_$b+d{7{t&?vz+dOurF90B+)aA*tm(Md;5Y`3-Pz@_2twB(f)^DKPRdbu-1E$d~YIk8b0eO2acT-z>X9MRH zAxEqJM)<(3m?eeC0}o4V3G%WWps#;#{V@W$w!1pRYz=oi|Y_8X`fO{-6+UB7#J z?_Fbv%_!bxt#rYoIjAKp?0bI|EE>Lsgh(Gui_$ncS~!}jot^XTW9O)FfpOGxNx^bv zmB!e`#_C?zRRa>pp#ct|W-U-hZW-M7`oIK|sAIIa+wj%D3gyvpwrT~iDDs+M(mR%@ zuR_{`5Q!^2!8OiPK**9O5-~)2&9G#Ut*w;E=pZY;khk9s;NSy=M4OH2l6-WPYL1J*& zF}x1?om%Pkyf%2h&>cWVbojfOs4VETu_C74dS^XB6-v>|4LmJPI*JaCM^lhx!47@Z z0ommzKD36OovBX^PV81^|LOuCE{(bR*bu_1DD6qp$C{6O+BaBG);<@~whb!KB*1KO z1?nC7@fPXPO!3S-LCsopE<$MbzO@$OQS(^@O;kmxKC~*(!e-YJQWg9H70s~zNPw1g zL!*hyvU2E05kE1td>}1Q_bm4IZzD1v|L`0U*7n-IlSCD|sX?4lY~sMVgWKT=N$nlg z-j(715cl3X8>(x9>;)0vU5NMIlVLu*_i**2f9>w1r_WZ;31(7+5C{TZsHe&l8V=$( zHoF@)*gno}Zqs5|)rl}>$qAX?%p`~us*aF4IkIBjw`v1dLQq-u8?{B_$fRgB05=cB#V^U)95Ro@lTgZ{KtP&atv;C^*?Rr0x|#9-tv_{57gx#;_XS{ zk|GVH@WDSb;K85UBmDDA{)fj42_c35*Bo;2pKeMaUGl#fbzZ^$WBY$MyZpcRQKEAB za}50c@dFBo$)}RpDTw(u4-NhGS^hdW`1I!iC&prH~ zoC{)h{eRnTxx!hmL|;5p3g2jWkGDfLm!ntwG4CN*+3V3snL**KYh)s)Yno2U`>sI! zhjko9Ui@R}rJ>P4jZQ*YvG^J>fA>#&9y?(is@6V;b zC|IJ^7YQ$lPk4IfBzVvpKQp(})*6}&SSr2b5tM+9Zcxc~Y0I)mDW~X{LuUrS@61q* z$e(CNk)FR}2-vzD6&bEERW?z#hz&+)4Q0Z<U%y-vm1*i4mZk|aN!nD;fCa=f_+fo49A_ZG}p6fjvSc zJGnLI`FSw+s1}M0q^iA4+U`TKEHP^$Xa;wocjCUJCOAD3mu5cgDs~1 zw4M^YOGp2f0d<|{CmgZWpVROoa%Qia1ODZUP}e`}RCwRZSWq=C7F=0FBKO&nr`8K^lp^1*Jb#( z2AoB#BEtz{J~)pDK@PJ87bp2|V)+yqs~{)&NMiXM>8mAEdI@NQdrM9G94TCw0iRS{aG)RR73hnH>O6RO->t zL+#f=q8iN#iBE9H&gIEmu4^=^eiW-fwO+#2a;_0aq`fE{mEg$*)s>bwtEW2L$y65sWW^(3R42Q|{8SS@7Y=9#=(WMk4rk4=j@zcw8R zggm7i_?HJUgMSP5=SB@r$Fxcnz61U zV}3Ox683@Dn5_EhL0cw@jx|-D=OfZnMR5bkffvyaF%Agc)r14!;%eUtv=P!m(q@qK zSHq5Pw?lr@Ifsbn^$JR=2Ci|3v;0#%II>2)E~vg}++57eFvJMralBvf#)vcvT*;yW zL1lXWi>AumtW(QSdVcc2Dx(kJA9OC)uA_z@pPRnyXydleD$CI!7l|?(YB?}wP$R>R z*$#(4`RmM_qR*+)M23{wI>YKqzIp+j*Mr1$som<@W->b3lFcoM$WLQsd(9-E*%7SA zM_@;duqLU$+nf!@XB6cVx8!~xLFwKn;1E_*X=j>drI&Mx%wM ztiIjR$_`8tB(OE5OB4uz_28Fj+~g zdKvdLm4S3jM7McVlfA7V1t&Qun`E^{dE>23b9q1)1*n%vK1r1vL- zGA}H4xbL*9_7ToEb~xy?LOrBlPKR6Ek<+AKde4)bE|vGfx-RqglHxVPuu7@#Q}R%i z_hLIth3?a&w7-2xI#u2c_mp=c?VNc(nY-LUlYr0jjP-d*W%f>^HQ@!hi0%mN^*FuE z6-1in-qjqK*Al5v;k~}lq**$PG|2XX&v(ExVAbt+NO%iHQ!L|q%!1J?OjQJSrdrFo z<@d(q@|OjRYN&_gj3b3=6uHZyK^#l1?YY_e+V~;rvm3e46u2h%se*IqC0W9WeZQ-! z>@3sq6#$M9v^NmA!rr#+t9eOk@@c?sK47*%>^IGmsQ`O#Yt{2rkzJ+!jg2I4{`{R5 zyqCIW6h>gW#{o+voRjdh?@i@?saUb={lJvj3|L2ufuhcp99DL}IY@vkI!oAc+3OZM zdwz+&rEEe|VIb_hvDkl#Y~-fgIH6iKof=@yQlD@*R4V?vpNMsG+nn1+O&A@9CLppN zRS%WJ$K$JmHq#3h%}=D6ehXcx%H9_!HyFacebiy}KF!BQ!%tn6P-mHiFVEQIvFsUN z7G2}y*%V*>c0LO=&UVajwoa-#IROFuODKSC1>C7_M z_Fb_^qOL}%2^b_`MN&JKB(Ix#ZOas!ehmjcf}f-B4Q$SjfgLkmBuOW0ii&Hjg%zm| z&NsiG6CVQ7F-3(%im@QAqiW0|d$Ic=kt>+js(+u8w%B2~E0HPECt1^LfmU?o5BQs? zm9C$v`GMDgb7g;4ybMlw`~0fg3j6`ZUI&x>iJz>2XkAyZ*?#9lDb&7YHPhX=6v;!e zvpg1K-iLFqC+q41Y#=}WD+F8wyBu}965K(xwI)YCl13^A=ZtoWG)RB)oISDGhu0CM z@vuyqaEI$0-z_j3JV*}C(gd2zH2;$uY47fjjFpV+h@qX9N{Cfh}Y@IQ+Ny#=<#593D!K-PR3vf zzf1qwsFhV<#cLx(uT!wbK8464} zcDzaEWt1l3ubn%Zr{ZY2K)jvx_v7BtFUlqqs!*)l2E15%Net{G1eael`W+{l$KOkR z)^%Y4p~@+JRuy;)FlY;)V(apS`v4#_8XYg4Z%wjm!=C#R_EPy4yl*tf?R>~MJl4~# zQdVp}oKPw_Z_b(kH$ce04l~uoZ4)zOtO_EbODd5e#F^_b(UKSz(Z ztKU6Ir7S(kl__sdj9m34yFuq9E((k((-4vtO!6{CJe~DSn!5l7uile@84uCMP!j4( z@oVqqZwq4Mmg~uVV3$ z%Et|yHm&0#nRc<6Ng)t37J@PvVrnKA{w+}G#>j)A>@}JdslrQEDOe&uU3v8ATXp-! z;K{s1!0$-i=;YZh#PmGX>J;26TdjT-1eq~&DE0ttO@hs^010p- zmtV3iqxOB_um{UqRMhB!+b`A?8A9k1!W<2x6AjU+T0^oqj#AkiQmjxw&dHFu{zwMH z*qRX+TfAFG1cO7n|40HSVPS@FB(Kcd{TC$5VJRyp5u)X{!e;eI)f%hXzCndqda}9u zn00^XT2B56V9=O0(6Iv?3h*xzjLL@RQ>Ywa#JJ);uZ8O|?E0WZ2YU)T0)1&o=XR5Y zX!F|H3J-kBcN*uYVTV7QLta?}r7*7({k8PB6eqqxGtjtbov7M|WO5T=4xG(j+Z@31~ZY}a}!n^33G=xM06=$lAET|k#C(V39APnTLgHlo=Ykl6& z!n9SCa*7oWI?7PgsTbyZ*vz6~8OIfM(oM zhUPxM(M&l&wgj`e0c9zUDM=<96mHNM&yHXX&@M3AAS=pg^vx`wa_YkV7F9Py;zq!i z(#+&}iB&)pHqP#$HRh(i;eC$17#=V>=W3+G<6t@uT5pP|S7R(O<&NKmt?K&?gZEmo z)4~PLqJ2e<9Mw0IJf^m11v`=7*)tU14(}P7_DEuRA`LT;j+*B$yEZ(!PXa_t4ZfTa z#X4`fbc3`ur1j0{NOJ?VJL?`!dx-RS;3m~72*b9kL>Mnvx@pAzhV77p#7eVla+mI# z@@(Ieu!Bf_JTiQqWL5v>LLus(6!AIleR3p1i)h^Uc1pRjJG}-Gu4dBAxi@;M=Z=DW z7!E%TL+7sTXUz63O$WCGi$UP9vW_F13YYe(&WY^|!B&$XkU#lI4xm;2o{tbz9XH}9 zh@eHX5qtiCX?gSw`i_|N z`%SH?ob0dUM#D7Mmp-Ct5ao(mBxx&dVFFH-(SAO}oB3+ETYW}=*8LnAac{N4VA(8K z+GM*Z0jn1hI01y0&m{UeH)@gJgjXcU=>l9aMS>Z#zdGciZ#LPy7TVCq-%oyWRV}TJ zbBexFsh_ZQ#Sd%(3p@St`@jLb9_dm~3PiN)0pbnac$)A7Y@<0K|6%Cp#llWfs~Szu zs4au2R;ps}nR;DehU=i#1&i?Y*A`6FxLlPbpg?1k4UrE^o>wJJIh}?8W&VOPkuN~QYc39qR?}vI zW`5MCcHcB_f4uvM#Mvp`dR}#1Z=YPwDTb;jEZ^pMqrx-rSU%`*OK)(E zMUek=3golMz@-E{_61FZsGIX+JLQ(4Uh}@1YUVZWj z!z}}*zzM(R*=MOqcDZs_l2`Hp%w33CR&UYdB}u7w7FP%8jR-T*If=h{9p9Rt0?oP| zXO#?@q)@{Mg?LmL82p$QMA;wKfFG7FFr^|x_W`))#^a?|AcwscsAVFsk4;k`-|#+j z1|vi(KN3Haa&u;rjIW67l{Du@V`ic)c-Vfuo`@}f)BVw%Im{}M3n^$KLNPq45zb-K zW5&XQ0u2>wFR9wczLXd;Ph?~Qf1|0&dp-m2S-QydDSWe)wE6j4?5gp1^tFEEK>d^* z&SC@M?+*)nNab3n=wdm|P#{xs+;T5%?q_x5rLpkV9lObQPPfc zFEfSE3ItYo;n|A7z^+~SKYRmsbV=(@7&SlNX0*;0{=^STM75igGZm}~CUAg9v+KB% z7=s$0>m&yheWSLs0Of1>N>+(l{+JWp;@&bb)xc|(I*W?XfB!+=1fo-%F z#X(B$K*%jmV!`kflwC~MMbN)Q8a>cHrzt*K={t=B6!mTW0mZ`IC9 zSRm&Q}IFM*Tf+JhT1E7rQL`}>1@-No|>c=t+U-%Sn4`SUK-+p{-Wgzubi@ddG= zGJSY|q+v)bUZq`m(JmuQQU7_KOW(*}))(09<$0Di7?kAmpz7-gQ01DI5b_z|@Uam7 zrj+XqN~^`3;g4bEtRtdeTcf<6 zv01OeV$T*Ix|2_d2{$k|uNz275Pp2*)bHWOl(&vDumHBE4F}+j+AV z^4&=UrFY=(rr?f56Jx2sSRPPBH!goizh4FB`5u+qW#KNJj!gQNTnUo_KRra@To;R6 zAX%4yR7U~d0hhJohy*rY@2{pdblI=|WdhwbM8gAmC<)hIpLC`U5Zi@n{t7k@#Wp1# z&XLz%posx@b+Nr9cTT`CO~t) z>pnx`%eo&GnlHy^2?Mj{Pl+M@Z17Au=6P9FwwuEC4>l#!#}w|o7xU7*9C7V`V?BkO zNH3RPTlkW7X)lV>Pi+oCDr)B;7-)80T<9yNO@Y7)UL58haLS#6{(QASfq1P$jG-4o zeeS88)rtbf_AP2UW7NwSLxZ0cq#i;H9g8yHU(6dikytW_*_!ItObLreLWKjWbXRTo zQk3cH`+W#wQajGv&M5Oi_2IMNh9z$uejB19SZg^Z#o#0r-R^h)oII;3l?$1N*3XFD zFed~B402xG@v-zfT4j0?xc#VO@Ylh#FtSfj^ITA9XK zb2ObdawwCoick!CvC&-bM|H77G5BCf4Z-~2* zM~t3z=IWB{!(Qejz)IqeLh;L}NPH@qwL0);U%*{VH*6V0O#vhzLMK2f(`}*ScZv9= zR-h3dNTUKDC=Si0Gt6`V8uC;7J*bLG3*~w|k&wfWK_KUL^2jm-mxh!k*9I7HD%~O! zKTMw7UP@=l)(gO+z!4;?>4f8-om1pPM3+OfBhD#LtFZg=tOqNXJTjbYPxOt;;`GY$ zbcs37c+lFB19DE0jqIM`;}InxR{^#_ngY-gUA_mBN$fy~DrfaUxRWGNaebQ;*Pzz=8L;Xce_A-e19Oc6(eDEQuVY5*Us8Am84SF9O0kyMbObyx z3m}G~wU@fgziR+1&%bN6{Fh$Qe=tgTlSje-xOo&J|H(lW|8P+Mr!1D)1^?ozJO;P? zq_%to`lp-muigao*E}@8{=afqu;Klma9GCu-*Z^T^Z(6Z3IDYr@qdxSQvM%tSjNBM zu$;fX(f^dg!pyJ6{#y$iv9>dJxz_pFV3_e#u$o;G_8vnc z4OvQ+u21^{owq6>ozpcG7b%u-cT422UOO6=G>Web$)g~l;q^4xbTYvVfKu1A zEs!Q{5Vg_|Ct(D=Kzdj~6s*u#-h(7@9ZsH=B50}tpef=maxw(jAu62v%9ett%&QF_ zGSX`P6#{#@vP}v732j#({0YisxZc<%_8Jl5HFf5zKp0a~{kNBT?^t56JMXDCwk#8* z-Rta(6`;2kG-N5nhi>1ptvZ>~$73|ULZ))sIzj8fM`fDXq`z*D2PPg6#?ASn6sS4S$R1_^Tw0_j9vkGUCVUTv(ywl-$EEYB=vA606hFpVe z>_rFIC9%SJ{|>&YttE20;t>a)Y8>Wr8aA)2gR*6Ukcsj1lR67|VSk66H$^f64$*-c zDQfBvaBCn#Z^uzhb&Y`D@bwn(L%ikaciIi3GkT4!>#3pLi;ftm4*IPSIBY8s0+ck| z>JXAWc+teK0H}T4Jl2f!Foc&d{GF0El%yYcYaDFDucfPw3L7RGhB)G^j_T)h2-Oke zR%kG`XHe#Ai^zC>HVyKK`gL(T&qMh+`(O8Z-M-I@+yl~Qj>pbxRmdO&r-Z|hEA<(L zZZX^AZEa>Rk0h%uNrs|as{cZC6^Ll?QX>EDl$*z|_-1^BMbUZgWzI1h2{^3I8QbMW zQMT!J8hH`(?h0Lq{VSb?+8Vr(HawN3V-&d-ioxWgDP$_zNTEb(tWW8ir!zT?ozB_ zXizG9_~dcwG%nq;e*)F}K6Jt|8BZc?S&;4b6n19EUR-Dscc<#zQ8+W_PFbAhqfGHF zR{B~^8UjEDF%5ug%a#6ZWgi!!)TpH&FwO6w=diLz5`3fk?~u+LTsW!>fXgSGW{6K9 zR!VCdFabW}ucO7CXYmk_L)}CXC}X zaNCB#IixFyqkg!qanW!P4NbTydcE7TPOay`dBielyZ#n-ssM0!4v_mY|k>PCaay(HIc(j z62t)n_y%oKOqawVqysZNTLdH9UEIE;m`we5=jmYL?F4{o?QdliH83ng{*Y+xz4nzz zMdo0Z#m(4(#S>I-IiXcfuRxFrlHSeok>nqf6`hRj&_<(a{>G3Sr_Ksvt~miLlE)c! z0lQq6hyq9Vjfj|^$Z-Y#$7FR0Bi*!WD&qshzM>~Zu=K!EKutsd3^B@f4M$JZd~5Xy z*Qw}bNFQo}HA{IWW@2T>z(Ujbo2J6l=~W3son(f^5?ln!!-iM`+;3rKG1*+Bh9ZfB z1LZOicC-5OJut2g!%S)^3aO0mvM5pF=HQ!!qx;n`X;@t3z&942s@aaS*!w=|v2n*G z><5Fh>ofT5dB|f%a1wFPc`oz>yjB88k~m_m&S;Zjw1M`H(Db04EmFyRZ529w)pRd$ zA>&97O(vUpi51c3B;AWyq@XCo@#B`xZ#v-+^}Jq3UX*~{(P7C^;qYeJ4{ru2Og3=u zQ@ZEveILim5Z-o(ih)WNWeA!W34Hmz>q(HCeI2rYzuyZ^OGSD$@?y5HQ9P2hb{#5c z%0#?1IgGl*eABO5bnW*X-0w9Ku&l3h8qUrNG@lz=8gREp7>oZ2 z4xW0lu$8XdFG0ZKTBplbE#4TfwT~l-QkUD;ZLP4k@Y&iB z0E}4^u*N(d-aN0T=k!;c8Xlawlf`nr=1@b{*0vhW%OCq?=u_bce;?$WEa}wl7tULGU=R&0nkES}{e}B)iri z>MSF;zFvEeEf+?|RMCtHbw+cxNV{#_v0=n3M%)Ik%jBugDd#{gUX-h2%D0_SrEFay z`00;UZkv!Vk2ve{w&J-h1G$Fa<-9&wGOyCW+=ul|*Z`n6HGYm491ye**mf03#VWSN zF_1a3*m47qc77%^+mm940&_f0x|AN;W78sXASihuzK2;A!eJ-+?Xnd?M%Nx^LTII% z9GH7yCF^O;QfWpMh5BYOmZJerCM*i4fP&}z8mLTT9dbTI1bhV`ZH1bY{p+IdJq5<5 zA=sdI1FEIyvBp=<6U)d4vEK%B!df@NYPVi?R{`hZ9SMTu<;*ithE|?!GYW;gC9$^r z%t!N1o?8#g(n-%e^#V0qV=O%P2-G60-5m%KagMaunn{goue)EaHB3g)o=htAcBa)< zNFn30+B$cxDKR7?&GwPSh)p#ud`vk!`u{Gfr&R@+hRNh)~O~vyQ10$@}q#8)p z0-`z~52~>EQXVj?O6%vtDp`wa5Y}-u4|v=RyUuGmnQgEgGPTfPKM6-+!u=w8XIl_h zD`PNSbFIUm++{eKBDU~+mL>iYg5x_es6Wo|ylylMUa7N-i`*Dn*2PLpyw`%*CJ=pj z^qK*AHT4b#3*%O0m*YTf(!xPL!XNx^>Jvn59R^zpxiWX7^UkBLBtlolA?@e#=4u&-DY`9F%buDzsLcKGsW$xoEO#Y+5qxpk#FtnAL z`sLy;`}tTKx+5#YT!cC0Sd2&sQT<+`L)*rH)%KK+$%X>YIB#VbJE!eDtBm(BwrIe- z;_|HL2DBs8Mbarf?L^{e9Gzj#=Bm|PM@4x=bLWS20Qdpz<$C_LEe(MjXj(QX}ruW8zv zY%l=q{s^#Hg(EsY=|&d3Yhgy@Oj3hEYb$L950X3rb;Vz^vKgAJ|8)wV-LFQ8mj^KD zmE(KW0Az&tqLeE5e&Dt5}~#2ZNEG3H2>O5w|{ z1kj1Xk1tYfx@vSDPyHMp$^ibkY22jyr<+3{in@pnB{TMTDIg;Spn z&20sw>26cyi5xCU<9{KHgN1@6~mhXB53vAi)DH>3p0+M@i8BQ z>0P`%%ZF4t{sBOo574PuaK4!@xT8MrQS%q*-F8KK+2&t!Kqi?L;#@z;UUAq(%T<)q z3iFFu6&wh$+pUnCE$3j3QD~5`~K zt=a#%LA1F^vkQxmG4?8(c)40jxQUV z1S6M?5saNxR}nVl>`xOGSWT?*r^wwxa)V^u9CpR7<&N{gBB7V5BH^YUt%MX4^c5!FtK zOM>o&>42rlS#xH(C40VxgPGd6)XTi^fUolMj68n3A#eaG=ghjK%O5WWfTYUg4Du(ER_QYs!_$G6{Ak+@Q-f+Hm9I$r;6K_ykZdWSn1*%*rK5V zQY>n|$N6a}>#o-i!_HZ&_}CHbLTsDy1MnvWH#~`oH4`^GwwY(Ay_0AtFp@XyU{9La z6^KZn1!kgbM=WBIYNV`V>xkEX(UF&;#bM@{w(&CVjo%Ws>Npf z(mCEPSfk&c?2RK$saLf#0*%=-1!jOAFS+&^*wIs zn5AtQm&WYc100W+`bgh~943%Y8`Qd`s*=uVrRRxorLzVBa(@Np=Q+8QIsHKxI(74c z%HOGE&Pk>i7Gv&-@ZAKVrnQz~qEPR^ah!T0!Z`Y9GzeBHK!JWJs+SA|klvZl&(c)1 zKV)zk_VUTe{8XjrDH#7IlQl@_HxUgFYUQv3j zh6T7NY55&X#4)|qFX|3i)qt$`10~taX_|_Y+Q;?n&q_2Of+J)m`S3_&DSONv2OWRYUc1mP}ze!2394y&7XK3K0&esXoCYA zcj$XV=`>dqo&bhEC69IZ6jb)bR^j>^*`t*aKcdgr`)puv*K$kiEnNKd`hFy=;AM7n z0@fMwEwEMScb#lA`vvNtqU_9B#vATi7{rfBwG+2)t?%06q1-#1mZeJqJ(PXpohEGk zfgi0vb48z-xs}vIW%jE6!2WZ`e1sRI1p1>aEg99bv5FRstUR^8Nr@(ll*BPOvtA`h zCiQ>_Vne-P+z1Bo2C?zq*K8pY`QDj_i5q{tBobo~hnKnF0GEs2>Jt;NR`*tOIxM6d3nj zi4$m*DDVPwcl?r4zY)Hp)L(3@shZ1FpIG{tU#YDlPTzH@-$Vb4y*KMp8_5=h|B9BY z?{fO2Gm*q7SK0S6NFYE0ga%N#mK~A;5NJXJAWrok`QG}mANt|0emk%JW8Vr%j0u^f z-Q8!nl~rCzqKK^>5j*x2d+*q&(wL6zb-r3Iw@bxVeabiM<#v^w_XmwRLyLe8tPyZ< zuI^Tb?lw=TJ=>YVIm6!HkArwD>Tq0RX;;u;WxkDkY7$SXo1SZR*TGoMb!OImQyZ36 z-ClucEz13SFD_MTnjUDe3OsI7@3ShcD)NTfdTiO56e)QwlG~0aSstseCp}(JWw@XP zQzBNAHDB@9RK7NJ$T@7fL2K1naI9o)=HC1sNg zn=Y7jzci{!+I+YivfO0EwZO78#5=sTDl{3hg_lQ7yyp{Lr#dMxwB8@x*Oca*cDilg zf_%dOt}5bUS=ZN7HEz>j)gLH&!*>_8`IaBrM3;6)OpH5V;@=ZNG0#S=d)T}m*!@|{ z9xb+pRhd!IbWznBxn_ROQjBEOD7ieZQ(}GF6FbtHFLkW8*eUw$&axA)Yg6*R6E16R z$*nF{L!MM6O=lgVSZML?JtQVpCuZvARCKLywHl~O=l*`wr2#u<$|MdO7O_$`T%|&V zVmGR7oJo0Jj>p8Ru#G@&Zmn{QZQnKX{ZOF{a}wH~-Jh)mu&Ow<#?&d}A>UbAMNJs- z1+F5qGl%U%cbbdKHCC94M0wDxcH>5!gcJuCZMQoN^Lf$_YWTQc+(51~)k#Wi)MLl0 z#>*|C`SLuT zJ1YW~tk$Hx9aqANO59T=sZlP?Hsz^OX|MZmQXck~UBA{xaVr8I4D+oeHCOr-s#K;% zeXF&d&x*uIEY9^j1;L zx3=!kqnmV{vm}liOcWe%D`tKfcBk<+KUUiz=gnv0i0PPCr+)93yG(VX7K?VNIPZ<6 z+8WGwJ|5+!8>l(kVnvoGjuzAGx-!WvS;i;a^`sQEC%Nv>acq8(Z*sZ&N?~yCRPw$> z>N&ncjEcQ#pDQk;dS%|4crMEg=<%u{t^Ae5#Q0XI`-{5S>q6NJwvsTN4f}O+;{vYE za;(C7tNgteKx;voWz82nqpjkN_MBK7mQBu>?$+LtL$luO^G0siTQO#QU#SF3DB!Db zJrz;)FT$CO^jzP!i$o>AoU2N~Sx|+V(p`kLKrRWYfDhMnJg!Ve(va(pWsx&vTp5qJ z8qY9VKWg~*K$*wFYR0bA(bk>W>nYce%Wg2B#jvZ9Ev*Ws^YMthUr>$#K|Yr&iTA6? z#8OwRnJbdiD%WZhgPh^72O(UwD6v$C{1Q=!s7*yBp~#f(;r(ntI00MR+To;>r|t`& zAC1c84Nld`=0xNrVzHX8*DBB}?Qvi38T7{0@68U;3FpGFs)_@@Rwk&vRow>N1$$p6 zTEeWVP2uF8a$qAaGFnLX@5__=nl2X#wV@z3>9SCV9WSQWGqZi~+ruc#&F3AZ*|gxY zysWHEcg;44zNW$^khfD?>G+M2Rhw=%(~{iumh@^~YXoq>4vOW0q&pB-xFN4k+iEGK z2SAA(*&0z+h>!3YP7Z>RGSkKKdcI!vxPnFtfuxo6`PA+M$Ehv1juP{FB`Vz)U|~pY z4PG74GkhM&Dcvf?)#wzdx<{5gz~$g(hH?<-MZ+G^)n+hg zS3)ya!sAlcmh0NmEiSzUIgmP%U=HhCry$1+H*4u*6XM0tv<9F%wnq8>xZ=y5j>6+s z$p?izSzcJR!gf*~2sX7GlQU;oS9k{B+_PoJo|ne*m?|||<@;K`PqnRl-GicJ1Y}vx zd0K41a%)cLqe^*R)}jbZmh8Q2xcG8fa3$X6P6-_i&w(!$(!+XkbN>oXIbYAhGTVC#sLrMF$!^PC#qM-3`h zs^m8FE>|Hsodp#dVIBJ^(cOnti{i<2uy{gg+T~C$^p4KSaYHJiKaVIGF%VmwHeW}Hok-hHC z@)M_JZ>cHOTeKGBoK&dpfb=&(UJ5%M%OdKWJ1qrlgYDDCWYVpb)%#pwyQ&yL!-<(P zthc(8k?H5Fiq$a2^Lvru>UqOs%{Y>HJ0H|dXS1xXn@f`tRbn$`$o9rI6p0RJZF!(X zFh91Mi|sORc8jLTDPzW4Q%lfa*&OfI?(-CGuHD1_owOx1Ek>L~urCME- z)kysX&5{BhDzv&QbW2?hlTxw0q>sBkL&T{MugxrKEs;gRm zI}wF-<321BtW_wom9Uj>cdMe)n$)WbE46HVG9sstkD_XY%h76e5*MePqFs06sxOoc zs##UN^<-u&a@`T(Qqx?;Vw`ZbUP$G^BACsMRz6l$cdLWOq~;IIh9)>VFt=R1SZ#D? zvmW7IxGA!9j-cXA)gJrfR*`P%%lpBGRk_W^oDZDJh7GsV*>WC_mC`+4=(G8T7nUkg z7vLkJ;D%jt6EH4!FDO#C*q|p(cjYZ+8{O;WYAc~5hoZD%NM0zIR%;&BI%`7^d)vMx zE~bt|1+BQRy9HM+3hkj?H4Dw^rd3$3rY)*I9Oc(xePsZd(xpzB%a2F=)GA1<-=f`0 zAX?S_Ox#ShhEwYl%MR;mbaB)Y*7M4oFB(mra_94$WAW&55f_MpLzlOXB&6xg!gURUyyi{9<|yIJ1maPc8MRkidvy3LyBxDY>BMl zL4FE&qwlY(+k8QEI>NeKs}TKx(Pg`y-lGg--Dc{vKJXd6bMG^gF>Xd25#$C=L7)a5 zce7IZn=;+%*Ss0YHTwl>CQCxG(@^#1$gAQTBdn>BRE@`Cr&*9-e$1=vWbEL|YB7k` z<*9;O-FZ#j!bXb^2Q|G{t}BX2QS?xol}e0SnE3hTtgU)Qy}4-)$=3ZQo=3!XMN9as zrAe(2?{n22VUD}X#&AZBO0BCcTbkGi2a_>hi1{%a!CIMPNOnDGlxIS|Lv77E;r6>c zQw*gR*{kXWrqV7HjCHFw9xYeu%<$*7+nrZ?Yy-Fv3e{jjAtKoBl5Ts|y1dwq7m^y6 z^wD~vg0`O{m*svXS5w!yQr#I%CyjNjU*J}BBT@(J(y&!t)U{bZ&rL#nIjh{yh#cSA zbOwc5qgh|%>ojZ*?Rs4?^E|DHv85|B8+6OVqCL$6J|aB4i?fh#@a{mbuWYM`8zawN z$K6l7Yg$(rh7pwAmZbL-B6`8=M_sdgW4OqTs^ zkImil{mLr0ZRaW_r`XNe>s)sf-A_x>ta>lk`*mi?H^b(9(Hk%E>LeI#fE+IQMp0D; zBi<;fo$bAH4~zY!u!->QVChHxaMc^smdZxY5g=Gh%d#9rU84c`NwY4@qQa_H2$_4> zp({hdwRpA=&aHYMuIZ+g%k@izoZKq2IoOKkVtK5N2T`-xY*uCjF`gI&rY>|h%~i|7 zy@*gp_x>EPRweEm(R?zP&GNod@|K-p;LNAnk~|SMs_&73AJ$9ZfaGMp$#`XL=Fjwa zRS_5|q$W`825J657AL>qr7s^-6A8>!f~b z5_g}(@W)_O!?irS%LV9-6$0fX(1NTo3R9hPi?*mL#!8jtc9Lh%WE?%NZaK5M8jggS z*0kmWrRZo)X`1F7AlU7?mY+_Q7%%d1HRR?T!8R5#XQKL{hQZdU+Ae>96RY_i!U^R+ zq_ti{ZZ2)uI@K7DYOJbQ4rmRgyjWecbxJgvawwb0=f+SHM0GTnW-9-jp};;ufF?Tvl|09`+mIPMH=NUB!9Eb0$-n}pLub* z;pdBK)_ee-E#3Ic!Le!n35Oqn@c3h z+qj6IIL&kBC3vWhei@#quldGBcm&_5=K1<1c;ihE!MTVZ%HQBF!s|6yymuL1%+2-f zC3roNEAZmr61)P+{ot;^>oumu%lL7H-c@)Kad|!TJTFf1%kT!<-sSltd66#kF4Bvh z=eFZ3@T5XgxA!8v!4-I8q9?K!=Z~L{*PN(df|uvF<2-+nUUc;ABE9q)gF>&tUYWne z8U1;8zkD&A=iWk$rue0}=y z+(PKZHg_3dvbfT4wrxGol>5B&y%84!ycutxzp=z;ATE_&j6 z&rkGs)eKmGI*#<>Qx+}&5d^nLWs(1gLK z-BRtoi!le-@Z;w<+4Qi+=Qmgwd+4hm^i5~_`3>g7Wn}u$`1tt^qLa^WJ{?w^Z6MUF z!+b0wh%F-%`Y^!K^?Z+kz??e3aFaP<%s>Yg@JtL?$uNBjJnr$z?GQV>;|Z>zOWVM- zUWsXax13N9{cnGFOx;BoAbbP3Tld32o4#67Py-KohYK8 zXHmQ~a{(+By6)6{7D?0~G(C5>Ty_M5aKHO@*rmW8vAXL7kre{$SN+@HZ5PvB!3k?LKuXmGkq{X2!7hN*G<=XFyOsc-a5j_b=cD?02kt z*nN(R!HtPHnv3{K2py z^cm*Ez>RzzViVWL!WqP-<_bC(YkW9?`um>uASuxF-N?g2Gqlj^3UG-Q4=p#Ce*F9f z@$1iTQhxpM^P7wOI-!NV_|QNwOZYv4_yK#XOF}p0=%|4n`4;x@V7Yq2WwEzNBn2OG z$r3+KcHHMtckL^(;q&Jd(!;|H0t5Qk+tGta^HpAa=+NU2*e@Y;fD?YY(vs!q2err3 zAF!$oeNzWH4sJ}{{Zco>_)?#`>x9q=AH>*$A28r~mYL$7n+L9Q`7PBxn;`={vjgK3 zWDpTQ_+X8_{r4T2o^W_1wOnCT*y7yf8q)nxjT_PeP(dMYL>oj%Q0egE{& zKc~OZh>#e2ZV>6)tDcE{%pO-gv!1knM4dx;w;Rv9pLbK2%RQs-9Lur8d;D~BB?>?` zVMFK`Y`XTlt#>+O?2a(fVVcIt1f;8;K7#1ur~P8@5k+qXo+-e+&jN&;;*}wWJmE7$H^>O#%21i4>|c z!oaZ5{gM#G327WgoNx_iH*fxdHVzZKi|PB6Sb-yzp|1a^$&Q#JwiY(h`jMDU|*B7M^t z8C;hWXUl#j2hP`hZ&jsld7ddev;0r_K|%mv1-^p-PiN$GAfzb_vlk4ZzcO`r_wHtz@@C%maFsv3NJ6_Ea}#&Dz*P?1 zBx9-9nMq3&`x#otYkq=PCPx1B!z=zLc)Zphv0snfKgVu_kDs2hj}r!xwK!uTzh&$6 zr_o6+bA!+ZiC&vqnQ)yel14oC}!CSX? z(bcmDdurNy<{EUD7U^evbJyK++1pM%Z?+kl#t(Yn=+y569c2A_Ff`MyXyYRj9(e%_ z7po5&Sd3a&7Fa>d^UW17K*ArOZ3fA9aT4c3=AW3DzMXiJGQ<<8dnaA@^!vnW4=~fQ zpe7wJrhjzMCjZ_LmQ!24q>-2(x@z;4Eo{~$h6EcBfh1X$RxDq=?D1H>(W zHH{oMz>IHy1Hp&u@C^4o=3=V|S}Wv2kj-hrYy$IqTxh^P)MsD|Fc%HR^IaVp@DUT> z$_zjCbA1T_%6 zKScxvSYX-_T8fnXt}PUr#$-)x(}_Yic+MUCDu1vPnXmN-Gp=R2nQ+GLB#CXjJ994W zsmFgrj-}KE_0=@KCcEdzKb*LYvG4ZEhNkmM+%ZgG>c`vq1o2nBJRPEy@EHMuz;#k4 zdx{SRoydL>srr8NAU+qGtArX)SPNDM;kUn7Zq^Fz_pkaFE@@mp?16)KHi3KQCxsTA znm)FF%Wro%9fZJgr|B_6PM;X&LjnIOeH@%^uy-fGf1#boJ{d;xEgM1`OE$*Gx1ZnO z#OF6}Kh<5^^Ia|BPAOMUHz7`@{lzh2&(UlAwzn$qO+2QBo*Lq{W;1)s1S zu&MjdfKF%vq;>6v3Va{Lm^;B-Z4UJ?!0yE3mhWC<0l%<_W=-975O0Yz#7P2k3qSrl zQTj8M`-Ihx)RntO9r{SVovJH$j|&*TOZWp)Xbx=dpayM#9)d&_#tb(|unpY+J3;pG zh?WA^axn`+>}z^1!k8BP`X2j=PNR`K`zsb>Up){6Yu7ivzQ-VDP94_=*`Wi{CEV=n z?MP@{rlZ3fUd`1rW*0)oG*Skc9qIgMof1}&$Q#a-GnBjcZKUc1x0**^Qn7Eqr}P?UO=J0pS>px?9*rGUwd$gBPiF@nWJ;ebo}~G zyLrUA7Muc1cWoE@bpot3Hev-V(@6XNc8AQ$hn>Uur}PnG^%G!Pkq_S?nDvVy;PF|c z-u{xv$WL#tqlaD4xpoTKBX&N133W~cWAd_7;el4yi7e};5huz_@Y4=j@PK+E#$%a5 z2!dqDCrmu})%oo`X1I<4F$cX0nZIse9vojgSnn|d1_s1%G&yfYu)&U?BvSzl+eA~b zdwGKP2TV^imc#foWEuNd3kr&_-KP6&}l5f>6S?>rp&SN0j<~Z&Tvi-zMuB} z9jjOCOoPqmu?MX0yP5@U5bO@?w8**MO(gq4AG-yg?+ ze7HWT(84yhb&6>r1)3;!j+^=#(RM|`=|satyCT{LF8sdpBV&#mViPxVj3f{tf%Enk zG~1uvK4SG5)EAgJNdgo+-(8soG*Xd+nE?hI6t!eJK?s3?bTW`F-fN;3QeW0HaSJ~Z zvheVbIj*tvHLrlwH}ZolhDfsER>B0iX;mU1w$tv05Q=X3It9S^JQ&9 z5&6?YE7Q3UO?lGfn!%2GA(P;!=@X0QL*i+z6bPzNc+iZ(<-<3>*-M_+;20>(FEMtmn`hd+MHT-SfZp!{QFP%^*?sp=-MV1V5P&_crUU_!n- zyhlIMVbb~PnnC!>iTd&B?%k8~57hFc(c#e<#ntxohJCzsA21oMC4)~tKJJZ}5Y_|Z ztV2H|zhvSsQs42e_)+Aa-zt}o?<0jKgdG@v7w)I@gVrtapdZfl(1=nk_AEZ+>B_N3 zx=})=ND_a^WO#bQyKgH@^t6pX(Z8g$b9z8{Su;=jAQOA)eo2*)FGsG&QxL)_e}0P7 zm&|t*g42$tDl+!*n3hi`?65m_$6W{S)L}|*42nu&q+wCVtMEryRPy+>VNof_!;#Xl z5tlyA)T=Lc7NT&G&JRP_#X%tw`gno7j|OWZ|J+sDKGre5_z7`;(Om;6n77jQk?wI3 zpKGKYg3XsBYW{etY+NA!yQk05Ps*geAklp@@7?{NPG1tA3;rH0(Xr3ya*v%3@bRhw zkp8^8@W$^S%{ep1UK$SZ{&+MC82{jCPQUtXzg@6B=lN-WhIYzzUXHa(``w*vfj`3S zUKMV49^!Be^El+_!$rn&JSRt%4N`JTxLzbHIQgLGOVmSwASMr5sZ2c-^F$et6;Lb` z0HViS?!uwSett~nlBI(>ot|#)I$XAV_I6LY{?8wxq0jy)k3C^ky8+&4BwZfh>8a0t zxF<6E#E3f4ybeanbH>xjR^Qnt$ZkR?UE^1~eSOtVIo1?!Kiqjd_c^RkO z$KMXS=fGcH6~B4PQvQhG{7;YIyv(37Hgu|BpM-3lMr&SZxS!Un0~+;__J8+OIl9n6 zG@ZM1CFw%5%Z9++3#!ruKxgK_Ig%$D(_tDf7_Apf+bTNPO`3kXJM=y2hTPLx>r<6v zhpG;Jgyz98yt~Su&bsW3!8`oj&zBig)^KMK-W|JDuHm~#d=c02UBLJ%zGtlKtjiu> z;tbzC^U4~&fbY>dJOabGLuZaZUw(wn`(f!T6G$O{PswtdYeTUKjV&g8=B6p*)^-}?PUdN*DYg120S;~ zj$d=83!PxL<3H}%Yfm+sq|2GncEN^wdr&E|m)vfnz1=AnNzdnXo~*}icV_RpYS#Sr z{s%CB-Z}OZ^XE0A*J6J7Gy9lEsAr`3$|&`0_n%Vmf7?LyD<=WROtPvk9CvL&W10|u z>dfyqsDh@fr-m)ZI%%!6Ic{>p!kRT}mS&?1YOP4!;-)bY?5;R9``bcgpzwrFN>kV* z0?pw{u*EO>!?tU*gmurXtc<*o?>hRnYg4frS7NSF-E@f=FEacbH05wSWRLrLuj@;A zn#1E`r8rlI>{PJjIcU-pJ7?G2Tnf2{6mlXXar0`4+v+8*!A4wK1ZT?)*{Ni!b3q|I zouuOau-%lUVwa!m8*a`PBzj7+^Fl?isQ@U{mTk>Kty$b2<2NRmXiUn%SmAB8UfhmH zgGePQvuhIt)K`|}w$LcVoX9u%Ew||n@xovs*Sn%n=eL8fCrn}jS(-)l12B?xWx5J81eXcBoO+pT~a`4<7s*BzR&QUN)~5E zOODlHt)MBJNZ;ZnXbHI9a97>DkvHOEo{Nhs-PTteD)v&Ztb0SW_M3%HS`HhtJ2;j~ zTajv4?)x-9(`+gN3Xh6MmBu5%)5s#Pjs#aD2h)MFakPBfGn%vT`WjM_WbW3n%?x4! z(OWp)(zs^g$@g4?+wenn+T9i^5@|V_jmF=$L`B9ylUlGe6R0E=1N2_;mE+2{x8otP z1x)xK(W`)<)cYpB6lol(z;=SVnWUQtP#v z)-+bSZQ-DaQ>;D1jaIePjj6nm*CS)gMOwb?bhj)j&a>hgixt{mYK!;Om_Hsn4HU!D z^MW!~HiRk`aGnfeo*CdGsdmK{7aPiEX)KI_YN_%-_GGT9Q~f%1-mlUn+Lmg|v5NL# z^7k3-9Mhz%&a{?n^%pGOlV(G))piv#9LgjkDa}n?CB243)a+h8+!!q;QWwUU&$k<3 zzVHpER_F1yEi}E3su+36GV(^t>r1vOXsxMlNCM6`sj;YVIFKz*mHCxOQ((MkZ-&xh zC6R(J(uSb4m@!=NZKGA&h$Q8zHnoByYbnZdf4r>{hULj)$!hA00xr@nA}pyi3X?v^ zoHP@7d?SjsO7s+Jr8oGdjL(LK;{}G?#)k`HtCK{Xi!0<1XY#{sH4jF{oL^95am21P z#_GupRWuewOA>J^DfXloQNojKc~2KZQZ5daqOENU6{R7qdy+6Sn)H32nG#&XYU_EP zP)BW19kso&vOfPlDQeeWpkh&lO?S>ly~beOozoj`E8)E%`fd?%b0Stp{8}T6+p*FP zRMDLdB%T%}I+Som>eg%hp)`m%vp{s`gNScP)Y0dFrPtH|r&P2~1E~0nFnN_{<*niS&8!GNR|s)(aPV|fmJPY6RnlBK@L$Kzpbu8!LA zSP^DyQ?S@($~U|98q-&(?U-cZu1%mqRGwzbr9q3I8TF#0k!+-mrU6S+a$n|aa-C=k zGB+KJ68?wyUJ;F{FY}CKsqF!7^;0|v)ngf9IF#D5Of$owLUh4H`c3kO{}e+mo0r!oN0=*TZ3#(Ha$ZjOm)QGb6al9HMljp)QS_c-;Mom z;^glwhjZJ(mY-+ukai-L5v9S%;00RFi;_??aDpC)3@^|O`kUwIBCU)JT9U2Kkfgv+nUQ0> zSd%1~8(2)uAPq+twRuULXsUA7oS8qGMcV73F>=*f$AO~Z+f zyeKwhWdnv5z1EmoTaeq$p(Hm2S&%iF66HlvmX)w6FudH8sJ39WMTw9*vZXSD#f!2e z7X;F)DN@@JEww%v@w_0by@91RILRBz3&N4)nZh-hR&Qj@6ow^4nRg7_a}>$zYJ}GY zEp;v~7y;nzCTMQR@wVLqP2S^2f~wH&dMxXN)|CB$&P^-9N31hD>ENRdePX{>H~sl%d{E|?aiFS+vM0T_5fpru(|02sa==E+EBC@k&^{M zwwU~|Ih%_MVkwS{o=i))C<_8y6eUsQN1(~~M8%sMcu^8WftMMkBw7N`5yoxeZOL7g;ge+}qO^FZ~K{19Jqc-K{W+*HyhL>xSAgPihRtu1xNdu)_SMj1U zSn$3~tBgUnWsslMhms6rt1a_L?ZexBxlt<&ENLxTwX^jM8OXFbNme_e%!q@9p~$k@ zmlv|E5`y282ti&HX}RSwGEUSbDP2#IY4f7h4&((TLTj-VWuXlvsySF#J+P2XQEL0y zc~qMqmVnmk1dw=#411c>|XPMG_QAW;kJFtmURK66efZ zTNK^#f=Bbx9!l*tSTMaO>m@UcuwZH;&etSyRud(wJy`J05dAI5H9!}~V@nWq$r$ry zu;K_g)|+w&M+?HR)E2j}7E+^4^7%0;+UbwCLS4g&f*=V^fuXmY&G!Xcpn=>jK(W@; zMQbLRVIMf{mFmz1*_LgirS^DnN`jG9<0P6Ki#0)8&}6T<$PewJFvJ(QA$i44URvvl z<-wNB8lp;YB)c8hGf{6+GkGza%OhbUSOzCHHzj>uD-N8QEiX2GT~?#R`n8 zWyK3bd$5KLW34xN2{OFL%?3 zJ(=IC&Wr?Pdo9?7Sh3ZXLR^k0o!bSUdwrog6sh7}z-3 z8(9`F8nf|Sj`am4j}@8MT0AZ{)rHn%*nuSbvSM&bo$4tqIxpGnmXRM9ID6&~nFTSP zw@F3e$Eq^j@-0CYr8dXg)Rx1kg=BA58eS+W7SjTR+8)Y02^kgW49^0>YH;%!Ei8&I zC$&wkNs+)-31DxeF&WN!4W^~pZ5hyNOL2sup-_q>GDF#^@#1z?Fj~{CB87FaRmIh2 zWeb>Es9EiXJeS=8iQ^njwSi?7dJ;u)wy+sWyu#tkOtfaA$c#5Cqi=gsdj?`^IIyj@ zKpQMvP;Gh3`tpJp07oU^s8$CyF)&OD*Oc}~T+Bui!`Eb-D#$I1hYgkC6r(vHJQ558 zLNG%_Z;Qc}=QPofIl3)#bkQ~(emqt@pf+Y(rCDri%?&9?L=2R$AvURYH*XQTqgs$I z(qnwQkt}^B*fU-yDPk!T!p4bdMU>UfYAFR81VZ|$(rGU5d z>LSSN^KmTEGoUWk8=+P8foLdPUeALj(bt+YQ)zjFPTq4x*^4;IXn?KJRYZKJ|DEZ1 zd%chzkAzu&zFr;M5Sd+>+L5R&&diMwZ}l%1Y&~*02N&F8X(N)zGvz!%JydFR?V(UB z7apiex%yD9;zg!T7lf4Oq#rMzO@^w81Kk6_|M9eUvp~voTcE4(eXcbgdbT`k36}vGTq6x!v!v9E!_{Y|XeMad% z36I*RMO-5PWCGUG(*CRgM&cLx#v3op^mzebh z${wB&_A|x2|A_ql*F=85v`a2;iR&eMPnrL|I?Zv1BnW)Bk*-E|Kk0I;4Bb85PS;;` zw6l(d!wcq~bxZ?kL`4CF#nIB^4cUu|o5mVy7bx`HmbkZgYg zo#{-`jYo%Li-Z&He4*LXn-46WDQ~^I?d_=U(GL2Lol9H>nUwpozx@UIaP4pZ;b1W; z9jCjFt0S7Qy|iiu2qDJ2pckvu|Ix`R??)y)SwNeRO5wrRu!TI-lz&6|EB}J28qXVI2yHHsAzARwyU+ zr?Qf-g-84?mOH;OEWVCq0DTh%uR?H$x+D(!D67GZYO9%A6emmT0sru=Yhfl@3?~^+ z5Mij?uW#>)3s(~sfgTYUz{ujUpB-2e;6(Pfzxt-`29LK7$pGQ&W}HEV$FLZixhXlr zjR%c=#kwQkesY+%tIhVcTCG+eIPRg*5NERww*4UpP+8Kn<7+XJF-}~^aDx{iC1svP;Ll;Yn{@x%Ukx*S8HFP1$hi(QpnrNP$BSJ{ zsB6P?Ufr>RI?74CCj+7qgdQsG`BFC`ndC+$qo9*4|BIcHDMm}5NVOo+e0O@KfcAOU z!bw-;yg>TvZGDSrfpEugZ|zaul@v#FaZ?vED-^!Ev3ViGX%YoDwNJ(%ARA=I2n`q+ zFRl}r&_&-rt}KbpPmM@74Pd<3J(-B=URyobkU;k#G+h5h2$i$VV0`$!#B|K_nK>zD zd<&0GO6@v&{DTbIM(iYsP1)UnyYYM&fV2*(X(zW02S1|*SyKmO#fDL#bZ*3dy?X5c zYJQ!IKbazK#3yrvl+)yF88}br;7KoO*hqNIx4)wd;p}YsmX5R}ByuD2&l|sbEzsou zz&co1m=)!IUNoZ8-3@qTFWx31Kfhr=zxl+bYX6T8HC4Bej-i^~caIsV`kAto`b`q; z$2J3dr+Oy-XOM_l^o1El*=|kUJy&L^p45csC(ysXLg9fMMCac<9JF=JBt;w55zH>f z*SzhRC9Ix@Y$<7=v|ZJOFJ&w5}6vbHz#h?ns^rj2`ju!ZE7UEE4h&Z z75mYXqRLg`M&$`2ZgSzOii--;#a9_7Dz%SpGs3C_w!h2axDq#*L+7A(gx~(^+oqFM zsjM^1kJ5jp8gnq5xyBFedHdU6!_1z)Svlq)>ax?0Z(QlVMDMku?Cy}jwa`5eGgC8f z(ntFJ#xV#F_^1XbQV~$~dCV{;$=w=1&Rp~vaHdc=F$!N5K)1WzKnJ#Fx){TbP6gfN z@UOb-q_XT#p`7?Rp}V9&cG~wnW`o%EliiM=b{&mIU$MKBtI*GGY1yrETI1aZ0lKm2 zT~ggV*?%7}*Z%gm5JiUW?!v$jh2zI|XIk7}kC1ETpJp$9rH@wOd9IUO-S8?8Mz(#B zww(x(7n~XA_b(*<5I0N$4{hjYrD(70A@18>Hm2=j#CsgakK7g4B*Qazx}-k;1GecE zF4*1f-sI%*b2qrWcy|kZIUM$y%JIr@*R!Bobn^_x?nPD-RLCd$?A0CdTK?xL*~x){<<61OMt06L^W z8{-D8!;Hh zU>uU_@I@1-l&S&e-4Zs-m}pBoF4m27KXJ(104_0lhY_@p%Q?n|z(V#|fI%!%b_1*$ zn6`;nZGeG&gO?#EW^ol6SO&1sfz^u!X$enkVd02&>0@2Q#j3_KG7ooaU^R4k#0|Jq zQf(-4)Mvi?mwOR->JSJLwG;-KBaq&NK5*v93xqZsy1+%VkW@{^h*s1FPU0Ab4#s`^ z>*fX*QHjWG;%v&16l|F2x&`D7!|u>NPu2(d2V5t;isWUW6T52rHUMVnesu~ ziMi^hav(<6PE25s7^pW)Zt6EWK|S$+!{C#XTR|w~!x^kl)Zm zccfV66l4M~-QzbPMkb3tbafM^lT>%J`FKUEQ~%rFbk{-`VK#w-Lc`I=p=qtM@6j^D0Nt$wZa^-d z-l~^5EfQO-=|^6AE_IlsmjPuyIoCH#bcxFiJ)RN>2%e$dL_ye*4?tSt>f2w_1+zd1 z5?^|NiBNZ3>)YR_Xg|D+L+J#EAv)IE-I9Z(Ckp=n&i35?dr@`&7vFyO|JKcS|F~Q4 z{!86=cky<%fBZdn*KfFc-Ua!cciO#lm)$9li}%@`-DG#$JbmvCcK_Pj>wfI!x+-#w z+L{mis4Z`r*}N6l0UHbD@k=HfJ!sxS3p(~%%EH1s3=wwJLf@|>%gRvLp7V`wQ-yU(4N zy6J$7?PZa@w*XB>K=0uonGz)NFE4j3oOVUFv=@bLDjs0q-XfpbRPr~_CVY>DzPpT| zg_gy3EhG62LLY^Yqv%)Y`-!9CmKiQ(;cAhyUrsE97M!G^i#H%vz5pQrA+(Std&|@& z(!Q6RrGjN-I*2#y7wx6)jg&@OE^?hDs*sH$CUrLQ>5{w*t8z5w$GdlD+6PF;`kC&LwFem^zH8`y!uCF@b(`Q zXtpyU|Fc8QY+zci8wQJ9&986cQxF7S=3fe+v1Ofo}g zKkMM%1xJL?{vmHt-F1?o24{7s?z}|fPxY1k26=bg&4e(1JuRi7XVAiSuRD6%HR?cp zkUfI38_(1_bZ)Zh&b9OS+g(lv zA+TIj>f_Dzt%(!qe_8C`K5Fh1>|Ouce`pq>vx6e{B6rC^SFVWa8@p3y-FNc+w(cp@ zJnDYRISshi4$SRsk@y+hlyM#dvVi9Li5zh;-TL;oo3bfVVB`~EJ}^wza;M256lU6B z3SY#mkS7VExL3`*2<0*RsU@7m^C!672&M7_3KE^){t}wWP2U#qLf3m)!er2>{QwP= zmw02ZG)NRNn()>xslsFio*S5{L`3I4mJM|1WF9UIZjzL?mmn5#f+C7|Ds}g4#rpOa zJ%le)ZxJUdL^0!w#i-~MjhqOK<( z$Wsun?w>!O2_s1jotWY6!zf&U5dsZ7*(k4836>Gc*a+kAJR_{5-bk;%#?*HWs|HH* z!)5^0?=0G7H)5M=1M(eVM%^HY0%z{sX(BvZThCuBjc1|sA?Cj4~5BgYJT6ApqP z@?jbr`<)63QjH=fBWw)iCnW*Tuhv_n1jhGhEo29|K9FLJbQT%Rt z9h&fo8}Df@PazRt(thx}VZPY0nmBIKO!pUCcbXh(-Z3qA>I3`5_MDic7;!Lsl4JCw zZM80*xQt)KyhvmjsT!ES41Jd`XHH(6%#N52GJL6FHyvKrykU5w+9**m!2HGcx6;I{ zmg~QqXG$FWZZFEUAuVRE#(8wZVpnUaFkeP6gc!i4K5*cR-Zr|J=p?cU<}bPp8`W`| zy7sa>ZS?D@ZwM(isY-$N#rc?DEl7TS z<7PaI&j5Unv2u0q561ev23=!cgjl)&k(I0OG2Y$hG@&nE1XRveZr^DS2|?dY8OGB6 z;ft^;S7Gt)v!GthERpwUxQJd?f2AX#JjKi%xht=7mN5FPQvAB;TZE$sHt>1BT-xY@ zPws!eCgzsiG15%E*Q}%x%oWZHr>MqI5Mf_Wc+c18;$BfK@oV|h>EY`OTxU0guQx}D zB9cov4?BE%!f~N(!fGyUiVJNM)^eHZzb8aA1RL>tQqvDHv}p;lXY$dRq`|AEPXqx- zwb2WOM4lcdL~fWgPd?m`*1tiTe4-X^F6;E)Ig>w-l!{?!thdl3YS&9fdOW55?9k! z`!jZuCjHfk(5t^B?|scVTC(7;!bL_^@}9+|Co~?CE8s68^31YdyeR~gqN9-p4};xT zzbCPYhKv1<)uV5JLA49qq>u~tJI0}6LUV}yo;7`Vc-a5j^+smK?-;6?hD`u6C$5$g zd&IgZm^oeIUnjt_z?p$^TQ+zDgzypj9eayj`Zm?*cL(T5E#2qKA22|~39WtUwK$Gr zRC&!X#8xY5A>+0ht~KkC

oI!Sb~9(Il2C8Npc<%cha`4`;^^FQP|m9hI;{83dYoktpk7HF z?s=n=48sq{)tRxURde}4ac=q+IUD)fl&MDOs&Z@Z6wKB9o5oS|z(KzAex$MZxvU#Fg@0eV=M z=x!D3&|IGuFiB`uMf$AE_Bmp{MkP-gorLb9Ce0^JvREASFmP??I`IFX-|HFlVR{?D zrTNIhUQVd)WKoafOwt{-3%YoJ+U#(QdA@j8kA;#sM5i4uHBXy9JrQ}*^TY+5@R&qK zn9dXn+)20wK2jeskU==FFn!{@{eTfTe)jmx)0j*M`2@(`w}PIRi+Bkq$vjS!sPa>U z_a~I+JMzT+amE#%Y`h&OIbih!t$ic$gbAkx?B$j_4!bkbF0>Ml?X3jfGZ%dx?a8C> zv+w@f&1 z34nb(rx)yX0*lGyK{D|418Q*X-s{TI~&3d zjK6D02a?*aJ$<&~sp4~b4@z3*>B&m(E^@mn2D>{Um^OP+28zc5F6nbF-%b_*(Pc`Y z7sjE7NuT~V&7bl7W_7Te$E9J-XlaYwW%0fCNPlYm@>I%u@=Trp@YhXP%4m%YkeG4k{m)rSa-KU-YeEQY#P30H1S}MPf0=bWQ)3rOO zp6oFBVC$!h@&h7RqjF8=lpi~weJrRyr_@12~_sqCe!nK|b?BJG2-hAwpI9Vt$ExvKI zRR2GFZ@1hwj63d-q}`#A7-6lDD0*AQ85iP^6kaZadX| z3HxAQ!W`zH52pHTYM=SWcC7_~1W1qs<@9{B_uHzOjw}KoeinfB|70s=3FOJyEso+@ z59;%ujpBLnxemrtFYzZsd6w;?+#V83p-h9&l3mOK2UL{#jl*?hySjpmpxSvC{u9H7 z@q=Fwbu7GuKilUkk3V!C&pVHuv&S!okB^6s#{kty9)0-m;X@91BWqG<$u2-wEPIpN z15!gKALq9j=*Q$UeV1k0v5PP6A{}gDm&s1daZ|{0x~2|V<0`a^5T>E8zb6RelI}m! z_)3zZP3~$OrLs0SCd5cB*Q~(@^(q&_7*eZQ!&d3-MTmTr>}y`NCQHpS&T=;|MUy=9 z*)BeZ59jAEKtHwHNP>|csCj|b9l$($q~;GYSCGoC-BL{#|EwBb_<#|aJKmpu!DV;i# zqE5_i^f}qB6AN%8LNri%!9@gfC$Q}pB!&En%a36EOAZe?5IF+Q=|3uG^koJ0ev{1p zqY7SM*28Nm8=GYO`K4UW`OE5;9=%Fmd%gm%EM@*l@c<(FA(SieF=dbBFZ`R%Qs#k% z1>&v7M4EWW{~Yvebqfs(3l;mzvY8462%;%w$Z}O{vv})XYxV(1N!jhM+Rlo)~SbX3MI=@HDrLrk2s6)OTu+HOr{cE-I{|bv9AS z%M68!6 zH(1WRR2pw97#kgzng1$znR2aiI&W$b7@DG8ZVQ&Dtf<@zZR8`$N^x~`8`-3?GQcji zUjY!U&N!8o&*`b#oa3k|opDTU!7!CoDCnu%>D{qfW;)gMS`vK4U_?zPUgThpSe#lB z+hwdxvCf(6w+sA|p_qEVwFMufQ;(X;S)$}{qpFZH3|4>LEnozJHdtz}owcpi0NzKG z)>Tfj%QJ@k&8+9`YE~;(Puj=Zz18n?qtePr6M0)>V#+%G89g@2JN=p6m3P{}edX$< zo$h0&_lwMzC|K9)Cr>EizJ^+|@jC$?C$p8K54;BnTQ#FO|K6pJ(}qm%-xq#`wCF0u zjKsSl=`@mJ4bc=?z(9uzzb)xYX;n6=G*-B(^4nBwC|-WG-1eVW@MU?V&3*vN*(kCv z=Qe2x)5%Fs%ZgoWhi_b+xfKMu`CVRCQ1KsV3ue|-4BZIYrA%!$)%g|4 zrcgGwYUyfFa4par56mZw(xTF!ejG!L478Lxia|{&M-GpXoCIs#Rypf@A?c8SipktF zqdP)daM$_5ic>+mmcQ;6>!(fyZNXya3urC{ZO&wyhCKe9vpxtfb`A+sLMi7E?w&JCtbr-_%qxxn+)y`kDk$SbC2wGyJO zMVWSsONru68(Y>-Y~G|At**ZyP1>Y1Dmn)_>;(D=+0`@?Q-ifb8JQv$GS? zH&v$4Iw!2`-XxD>q^B_-MTIUtakTMb=q!y=*&CL z`T5z)p*xIPt0*tGj7rm%(nwBeD&CY+jkF4?Q&BE&^KPjMR8b~xe{V@Os(7y3;9CV{ zs+^jF3aX{5RIyY)*N{-Gng>2z*O$vsX=ZPwS~gMK%GBP{^l74| z$=cr16|knR!l%UK)XEf|;vGxa*;`WiiA|t}wjebW>PRi9cx|O^`lI2+%?DJk#5E5H z6}kqI;$cJ5e^UPZsFP&T-rh=;y)|@MX=yKzrJc3C^`&MLl2YO|6;zbMqL|6<-a_TD zC{UvxE=*vJ4ZejJsA(n*6xNbT`VOdd3V>&NL@ysy%q#h&u>qi^T@H?Fg%y^y7o`DbS>ZLVE2vMK_u!H!l0-$zWr!wYjtLWIcMe{%OE|ejgUv z{ozR(B)z9u?E_y8b(SN_!kx9yL!L)rSd6Bui2#39{9~wBA4;4nsUoKjiJy30acIdj4r1z=!ttu(=WHU9Wx4W@Vp$^VM1QMm=tp^E zbM}sk&}3OCNn8=%y1hZSrUPfYGwZKUJA<9gtLH#$PLsoswP=+c52{aMN&Y(1EQXA^k z##I~Y)I?dUpWn=n3_M$|WL~(1GmSGY%{QCS$&1^J4%WAKF4>^7zP|CaGZ<`acBX@k zUT4R-WS7&;>CWY#$cYqMd;754vCy6pOZ-}4kv4k0UY~P0oA95F!N7dWXpM1$AqHKG zvkksppfk5B>({q52CG?|gzO;4i1jz8YVW2EH3nioD;bb%#jdDS%K;O)V z8rT*j+2c5JUUEf4;$Z&w2;bm3EM3a63qJ;sPkxQuP{?oTc@q+l8w$9rYlLz40DAw= zfb>aw0v9H-DgDW6c{)eS{PRCG&hMC}sJCB~g3fB`9f9-D@sYCMgw@=f9~Z+%0EDn% zmiR-CiD{V_e*YR&qr?vYzR;@LhQQ+Rqe}+rGncA(eK^L;z_&nG=4s&1X!wcgC%j#{ z_hZP838`FFv_QMO?gy^lxFY6We<#rl9Job%LsI8FY6piN&JuOP^4C_{koc1v)Ge8Y z!Id3`8;3(rtAf4{#09&$n*uY$32bYmh-@$yR#iBd_v_%svbcck_Aisjsyqkm2LC{L z5IQVzYS)Q_Y@dWG)(4;vIEfVu?mAAy2!Mj!=#tJd4-z^H8FPZLwuSB7W^nGmPA&Gr z`cAH1YB{W{xzXdewjSafi^&3&*31TBW}}}1@&fu2LXj+N5L0)s!zY-Y_|o;={LD1R zE!VX8OCQiY*)D8RI1l-42gvTe^yES)lJB8_DC$#zNY5fNIe!0oQgV>vK}E|?@gkNI zSRq<_CoBpGrkbS&Ous^{x8nm2vGWID|B6(#HWd#!2+?6ufHrEp7V$DE_*D#pSV_iT zelDLFwkD^)BJxLvR5%fEis%N*hl25 zG8UbSB>q&D**NE#f>)P}=kPYg-=0*<5`qm%#R+?C9ggf_$2J|{*#*$!m;;=9-H-Z~ z%oBstq#n**V1Alj<+xGo`Y||~j{0y@B@{BO`wGr2{lxr??}+cehI7{!9=rR`pFfoT z4Tiua_x$|+Yb<1WYv%ZvK^4gzTdp)3DFt`Ld?dsu@F-!o*Y4D{;zv$S4%8+VGz6D_ zfo2sD$Gv1>5rbig!$q|?F9pPLrYD(ye)L|y==51u)EE3N%d*|~IW$-hhqCS(nbW%< z#NFQ0tG<8#enQ$B)PR_2Mbm<-90=rk^r2ow;8)y#LlVsIe~+05MVNmBIKe>SJyq`X z!9z-9&HvNn#fh3!zxvcUU+F{iX7lZ%`Z!c=pFH#^)`QcKMW7{PNQNvb+e63q|G0JM z0qJw3?NnW{6z)<^6IO-3bgC7t0!Ak`U>_o(Lqwnp$b308>XP=WjJ%|Ne>FQ546~0z zfLXtG7fZqh<@rb6rScmL_T#DOwhDM-;s;CbsGL3)-V7(Y&>jZr#27g7(%VcnEM$&v z(e4Jnx^4n5N$+_eQ|g0mh(y6OOu0NbIps+JST5|wB)*hhkmDqZ-8g#>1j!hH=Qc}eq}OCE)V`r zpNH~T>w~xWwebQE9e4|Q#qP)npU#LJgABrI6*gP^EYDX%rtrB742U0)SII0y^|3{B z9IP{89vpjjWK2EK9z}!^A(}@JZ#;MTxk%Fj^)2IBI|6l(L+7py%=H#OOHUQrJya#9 zfQ+1<6F182D$f7~+@der`~pO{aA8`kuhs8y^<{)YG>e4U;_x;Oss9Od*09+CSZnHm z41i;V6`fNb%~;GQ$M;`vEfm^YeWAe~mZn&}Ifc1pY;<^^LA; zwDO#9Sxzh|-ZzWL;T)A~A?I4}Aok&sMW{}*m|U*q*1C@NIu_Yf-psE!D)`jKf@utf z4YA^jkj-t#ug@`KU0>ja3HF@taf^x=Z1a}rekm&vIv%sU#8hJr|z_o&}W7oS8zr!-K z=;s6;)V}}^oi_`iVnxl-i!fPm-YlD>`i6uBAqXAU%)ShH_3Rk2F;- z)M<$ws%5%b?nw#1Bke9Eqab&BN;1;!LN*Gnwi+L3cjX~x1lTQ|#hfK4aCULX=19-r z(N%H_Mu8U)k1;aIg77A5WFn*K(BVS{$xYS>Bkq+Sa0da`LA9pTO+;0gzAD^RGgx0l zkaGpXvxp0X!(F(nDIn{!`#fg($1>v5gVPu78DKa*t$r0rA(7H9E}i z@EWTPp;a?c+9u%N1ShP^O>&SAeC0m4)ORLL@u}bhri6on*VR?`+ zD`R;7Y;iWt#`B*g(5Bt`b41#-`TtzqQ*b418}R$swr$(CZQFKMII%UcZA@(2HYc_- zakBHgdw<`-uDXxs4Br=N7l2Zi0Wq*w-R$)7!ZJdUOQJfeNNvo}Zn9_ktTL$+3_4 z8W&VDGr99$GkZxIQMYYj>WR3~qL1cAi2kuglXOaN0riv# z*#SJMLju$jN-(AIZZCd`|C;{v9di5vT??@#LkP}8J??Lq0-fy-u!iezbyCu6WD*k( zorZISKG_&jO#3}FWtz4%zfowa2v0A@W(g-BI5M@^C{Rj5+IW-EP)O zY@9u(W5BaEHJ%%(+U#6*lmrAal5lXqq_Y3+w5cbhY)gA16c&hoXN4CO21(|oIrKVrK?E1fj`U`X*5^4i z)mtcl*(?MpfkE&TstEsZrBDPqa~>3so{jT2PCotj6jx{<&48&UgZN4KhC1$p9~d6i zf|Sv$?^WJy2PH@D`g>hy*7zqO65Rb9@5^MXb94&H;kWguR&Gk+xKNNSuRXPtUm!L?mzs_vRFM-2a@YcCpc?Pz)o5`FjZa^|unN(g?F{Lrh<8YVGrXUBb%*`$Mpj^B zLzkRE8QVli(fZw>oB46=s-NGpsBK%MOwPfG(?Xcvpx!OkCp-$!+jwrikvsd8oA^5- zg2g}KNp&`*Ay0+Cn$7927Z4!j?Q}y~eo3!*nyC|)NIjhxCvgM5G9H1k2anRd_O0!B(}MU?}j zy0xABzQZjCYfVuv0I0DSj)#^Y#W)F-rtijZaP~uPGCzw^m}L`6ZdnY{N4sLk&x7&q2 z?D=cm=TPpnY~#82aK4N&_>Vw(&(FG4o4UcfzZr8%{xWnJK94aEK!TKX79mMh32)UJ z(w=RR)l2c+`F4tJOlPCrDv|V=sNC$%KtGTCCaK#&rp%k zG+duphzHvOCI1eM&FSLi^7T;L+)phSF7hm5zvqEn4I;F`1<+_P)Fiin)l5>8N`_M& zY_zng!rNKlu97p4H-B4%A)+m4QA`Ygx>CJ3A zCoqo?8{1Lj$4H4Who=L1{ubdZiS+m5QKG`M0xQCP0qcOoR5NXIZ{7mm+YztyU|X?D87(ACmaQrQ~K zw3UwNc(38K9C3C&`zU>5Zv&nylj)ivAAn<-{kgOPb#NGPqXcSrI5u%O?I;B9Zob(e z^LTIDq41B>HuVJ>JIUo4Om2l0t~l;cy`I2(S|eZKF}suvcZ6}~CVINK`A>-+6YELQ zCv#I(S*=BSrbYMmw8;YA(DiY00zYK_{eBM+3Q7fPR8KS~UQ3O3u30!V0~yLf(#yZ{eLQ=7D15$(#W>TkJI4Nk~reH-dw2@ihO&rUd~D^t8wy zWfAXa@_h1zEwXQ{0%2YwhHzkCy$Rqx`KmMSQlYEZukc|gpno;6XIqwus?H*WanXe}4nw*ia_SVWDJQhVoeTQvUppV8YZDilETFZ4p>V-ka ziH@}X#kIW|x?h~C(X6JSjHbV67r_)I^5XT9NMp~NQq-L@IPwH_cMS{$FYPYv@ZBI3 zS2=k%vo1bozWTrR*8BSbjsb#*{pzW)QVuiKb{%1a%kZ{9o#XDaFdGt(O=m|!K)4|5 zd{{jckUFM8AC19P)<7z%@K~lmEPnz+>mh^cgZ+Pp_tjh#n0qOyP&*LW=Im4%ix~$%7HE6lc(PjkM2(n_ zU5~$XsMxV&`>hfi)dqJlj!hn^U03>>rg}Hy;-GWFyG--`YsjTJW-$Z6Zj!>c>Xus8 zf`_V(mxr(mODd-g;+J6kBXzlCvBCY)pY($E!s^`;#hIo=YKQzs@b@10T%Dk*fdwiK z;hMZzj)~ML>WMw)96Qse zrul}?s6m9qSC5I|Ie&o@YMNf+OaCh|`lLaFYn2;lU6E6tkuMLJFNPW&(vity^X8iSK#RpA<= zg}t1@^RFPYosHm2N%uzV{w>OvU0xmE*PJ)s4WGvhK`)+w0D%BOUR`x9$NZ+Mvo@T)6p=dXCjB;+GGQ&>lUCv&;LaF=tvxD zac*@NHN<(PN8>7Sq~IW&5W5q{wYC}oVFJ8LhuwsYrj;Rtcmjr+r?eH5xS_P6(fdm; zHPf8$wH8`f{or78_~iC);XF7M^n*wqjEgo}_93K=LB4rSD+Fo<)SqJ*m4i0jN{7Qd z^+8xdCXxiu2T~Gu(et`QNkDD|WSsQ3^n6{l%~JWKDp)+Mv-H(5IW?)W#(=Lan>FN3 zjh#Geb?Yx1_!6Xgj>rWT3L;0f_8{o&j-79pAa3_hK9^PjZFCXzJnk-G#$tY3&Q{e& z-c=#B{QT|a)ReKJ%&p~N%yOD7*r(!euby4h_`-$nkU8R>r~lJ)=^GlmzU@ZWi; zp~0MTP-^2HTEE;GQU*E)emDE|EL=DY#G0m?@5V9)O-6fD7?)s5K(DkHQtap>?Bf}B z0CCHn6(+P!ZG-Wg%s8LHr6YGcCl$_)*TT1PW5fx+P1K2Xkm#k;a6g#Mnx(ZpK0@(x zR{dm2Dagwu(Ls`{!=I}C;noH5X61U-{ zX`4gJ;<&XNjF1Ir|H}>=8jrHG?95GbG7owZI#SC#&vJ2{z4P?1$-6wO_%mT zfB-ckY{hJglDpfjdzqfK08v`hLQ`kHzau`YID0x}$|zL0J3`fAQxg?ecV|`L#ugpa zc2)ODahaz4w+#5EO=%hohdL=HF2$2Ye@j^ZCSS|PiQNf^HMJL zmG2_q2)YYooxH&9%O|oHZj_DydS2 z&(nvtxVR;?&QRw=bPo~947D7{r7h*@K}c1r$?g03QqhmXesfroO~L^4uKR;0JoA`p zYY14XTf0IuR5#ghSi@R0R9A1!+ndo%%AmthUL_Y)pSg_aw}890>m2XpSIE)o;V-D~ zi#ws!?lL$f4x3ca3U$W(W@D?_`L^$uw=tbV>9mwW{%G%C51WEv;3kd88!Ys>sdh+(^2jy*RRbOs2Ux zvh=XLJc_?$&?LJu&wFk_>}JbHv`V3QtC#|$@hJF|PRU-+Tkc?kQd60T>Q<u_Qn6{GZ;Z3Mk7xauTmrjR1 z>YXixz`8N|)8;AE$ndTXRj!+!Kx1F<`S*)TKI}42m#Sb*9y@@~ag;%NP93*BgoMm# zfQS4Li}uB9cg}~|7=K=SVxmJ>S_fWxSm6FghGe7-5uq{6IbV+rJt3x8uMci(%tKIn z{f0mr_FthLOaqqTLs6>n&dOfGRFtctr*U*7RI84S}~(jFD#gV4n3~0)lexk*QdKM0?nJyzi0qJg{bKYZsK*I z&639n1dAaP7!u41$Izq2v3pjve42l$HMAMf6?uNuYCt76q@{eV=X=gPPJg5=ySP96 z&q>g$+swU@^D*?$PQdLRnSqEqIZ$UFV61PmQpM})`OxJaqmPkPJA20ajxKkNeO4{I zz8TLWR7Na&sFBxG{a!_FEiTmJt6N2Hg_$sUeGBSoQ&+-@F7(cEmQ}|w&FJ}3qlsJ8 zTt17_%?=*8=Pr?;_3)clXqFVGU51O=*Dye*m;a`;$;6}8=}=i;Qh!NKKhw$BNc}A{ z(K!mvUQ@f$aPFO4s@jtczrv$ZhlwB^u{oy^K9rEZf(pk{MC!(Rc~h1hD$jed#%4-PaM~I_K=3;aQBxy8naYv#?ZT%Gh1z z{T#XEz}Sj8gvc>UCAt-Zf}<HSM=oQ*xVfcDt+~pXTEgT zhB0fu{{U_f+em!c@u=%m^)<-6BJzpy*6P|Ilrgcwi2#4O5 z{-XTox&a{^&xI~7ZVvF~#;IIu_T-G+`sjkHM#>=)VXS3Z>m$%S)98R3gVQVaFuG_`$JXRco z&&pl`|9CN4h|gxOlpWtrW8qi+^wrhQO#X?UM|SL!f#sj>?(KDp&!?HiCwt42v*VG) zt1|UzC*l$8VewKQ%e76M+UE|%)>hA42XyVT>O`}@YwRpc;4e!Au-Yb)pr)=pEQ-+Z zzcC~*o}Wc$pf9*tw-x`ng8xX8j&m#_ri^JUS1bh31tuHC6S}C=lysCDNT;LGr6-aD zRwVh+vFynj*gdYE1~DqX+UlBO?`xjLP6UNF)b!s>>K5G9jrp3wp4ZiIngrue^Kg#v za~Pn>Drd_HO+?m)wy~5~xJeq!Sp*TaU9jPJ;j(5?;jQ;e5r9QovM|2zXG37DK}4X~ zq!X4yChQ1_#FV3o!xP}qA@OQ62cV$G6v1sF16hdI{aU8#22b6pZa8c5O!m`(lIrc^Yp2N4sB=V#diU6U0xscq$p`#6bvlRz`bU0hg{ zlLQqS<~>5Sz)MDB=Bg6PJcm3x1So#1%|Yx<6Di6O*HteVDNN)hbUwDq8KK@7wX!m{ zoA&4!rnpjuBtp9^l){LN-tk0afZS3xe2fCO;R0{{QzYrMB5S0UJ=h-ow~&b;H|0$- zA*~;M3iXYu5?|Os$zJqjB&B3CQCUf&o+{S?Pn$Z>Q|vXaJx++QMlRp=f`2KS(dPL- zn-Qj+QQ}ahEIAK}^i!SS@cWYw;g8p$s-+#G4c}-8$|%1+(->cwyhcvSWW1+wiYl|h zIUW<{7DiPY0!Uj@JbpG51)8=^Pg!(u*`{( zC{kvSz~g`)^b5Ba1}GzRosny@ks**6ioHOr8cz(w>A!FT(Z#a6h!VHfoybcGIZz2{ z*CCJKAK{KI^jXY6S9aY%T*K_*YWN&hWo>;YTfy*0g^L%O!OZ10IA*$LJ z$f$&vI-&NpR$>n@PuiXG02{VTwIEYN@>?s$QBKKDUr8~hbX-{Aak z`TV{*$G;C5NMft)ESit|I!_LYULvc$rRFrwi6MP(+i>bab5x^ruES24fu58bg4$&Z-JZ`w7 z#?d)M99e|6g+7i1_%#zqt0ZFH+K4+6^=5w`TSwMStd|6I3 z{QSb+>C2vIS-ziNR9Q$ojj&lg>K@A^a63?RVqt za{$u#;_jYmu-=^dNfVAkaRmSKS9gf)3IB^kI?<$XRrx7fJxHTUPg7u{OW$4-Y%ulH zLFV0wL;wlmi6g5`u()l-U)sQHKC%PF5LZX0mL-t(rF@q*g~TeEDK~~&opPE1R}FqB zQ))FVNq4Gfu&-i;WE8zj5_xS_k2-RdDtb%Lz)_3K9Vcyk<(ctv$LU>Wp~fO{wP6&3 znHQeXSd-Ow>H@Q+a9P#@pz6Q0<)@FR53H$FJ>NJp`i5=VH-Z6Q_8wvV4u;}l-g|m# zgn>gCFLWUMNU)Z*W&4%^;BZ+J!m==`=3Js`YtT6yMm-ZE$lDP3*3d%`P%(o;0k1%oY;P>L-T|`W05kvCYP0vLTR{*Hf!|&?8 zV%P?+6|U&9pipHXyZ+CcMBXCn8YdfUoZYzh#O{Ubjq8(Gvk6tpi}h>JE8S3QqUL2>_APbX7&D&r)Q~@p&VG<) zElsY&tUediM_`wX1##4Ls=UkIXoz=yNEfN;Ij|xgvP4`P1rbTnSWYJag!n>Jp#B9_ zc}yU->**G|NHz9Ffv-#D+;ahd4$|!KBfCwxv>pI4wvg(#ahwJ^pag3lu0QP`4{L|K zvq6VKck$OAPvskR?CK9QaFg}9umKq!WA$V9TRDT70C&$fz!u{i{sy%nN(%xh4H4)j z;}=D6(_g(lcF8Bq~E%#feb{$7OF=7(29Rc~!Ucw&;mJQ|KRyF$aWupElmJ21+2sN7{ydA zbAdjz$mmmrcA{muIvD|ux)K))X;Kpv5bDlWz)|$+e0t#^gQhu85Dl_V65Jn;bQ?sPdR=Jfiv5{IRUP45fjA~?w zEn307Fuc{DHzg!I#p1h6h+45wGn=s_BG_4A%J$>f6VD)7kv0P=p}ikKFt>BD=N9G# z6$PWLabSvjaIg0b13XJGeh(5>OKR(uzWbM5!`)#-PB+ZiGC@VwB>N{Vqv2MSwJ8R% zBD1S=Lfie2!k;o07_#9)lDHT1&bll2{X@G_BvJvQLWRe%xw|>?$4sC*HWaVY3((d> z*Wc9W{-yTI^78y@w=~#6-t|!p!W5s8kDqCT286*<%liCh@*2pe%DE3v0t5x!Y_0jE zkr`M)u3)VhrK0I9B4tk4$L~-4c2a_Wmbj0F@Sj$zi35(k-4I^q_+3~wD6h0TMw>2w zW?fNcoP}`5%HXGGTkc-%adfRZ^(ANx%)-GM0!>Kc=y_)-6@2nVw?_SX5{I9iVh>6^ zH-eg6ik&Yf5hvCU-=yGf+;6>Nk3T0WGa&Nx|DNwe5`~9u%lyEhor9>k-_MXEC@J5CLH^pEZ}vKG|Fhh!_^aM!~U5k-o`cCV@bNEw}3yZ zgIVZVX!_-ALi+WSM1uN1{h|7E1R2NG>I{VI6+2{n>pH-vRqMUS{lXY4Dwp9QS*wjz zXfool(i%dlv0o-DZ*p8OdtGX|-d`4`L4w1I$Yr;ACV_AukEg;?_Ma^{k?lz5kjHbv z0bmE7L#@|yzd+A`kl71jJl~<0&0Y|R0QY4w^6n*5jOlXFOW8_bqVac20YO|nDt3k~ z`?+T9mDXo9gBoih`ubcy1uiZo9#Tn-Iay#m!=MAY!MjCv(D^|^d(Fjax5befvGcb7 z#YiOUEmqKG6IQ>JgZA%~A|TU|I6=KK({XVb{JuL~sx2woI-3cFh4>`+4;NJ3%NSC0 z{%c8bSMuyqGZb^G2oJi644z}x7YOt4e9`XwA1+vrfTVZReW_afA1+XG=(7quiOdKP z_hX<5a!8=#JBJMbIo`}OgUkkpBqZUMA83zfqReof0B(*Rt<{Ti7}cQVK7NPi5-!fk z$3|@n7`VtwetAQu?ZPl%cn%08y>IP$?xj0|HGd3CGP%n;M>H2?L5|sR(-X#G**@du zN_7d|RDwuy_%P&w}JvmCT%}6s=?rg~u@cCmvZ%Y%AP%t@aDt3AP zWmrA{93aG>zn>IDvWP{6o`9Rs+m2a=!uhZ4)kUEpQO=E4W|%Dc8pBfSF#ijJqXLy3 z2ToC7_LIAR40%dMq>m6Qr{dN|XYze?;suJs({!#XINH1>Ln`JX-@6!LT=D`cK z`T^T1do27o$Mgc!(`DoJdv^3dEn9V$grTgB1}jGOI(LhVBwTB~(@w-j&b@ljMlOXA zMaR^pPS`890#Jm6s|pG~U?;a+BWe$qVn1jvr;Z!Cm5yVD;bCgiPw_aSHcRs{qEs&c zHmX)305_^ODS$Sr*6GX@SCMEg;v#po7Jig&!wEgjyyS?NYid&@91&Ll@JmPVE|+vnhAmM0fWBr{bg>7^vEE={GqumOL}Y0R&zSw>6X(zdlOjjoL2FP)8cZ zbdFypI#K>HtI`oqV()&F9z7pUCZZ4Hd^>zKBoF&S{eBhVXk;$3He=61-VO@elfD`8 ztaE)guIR4RBl?*&r0s~tHJJfM79|8j&?(I5?v2te!tMuSPpd zc>*5&Ivm&{Ib7+g!$A-dKhJ`+sy=q~-A)K*$i@%=9{?C%e*lTK{>=F=v&|i9-qttl z6utt#;`hb^MWT;=$; z#&B_Hq+3fj5VfXohn)ki9Kof*q88v3#65~h$EeI0d2&*y{^X<1ujN)ek+D7s%q&LD z#|Uk*fvgH&wCT~LNu{nSzaWCw^glp_GY{i1FF;IiM6rwsA=)m3UHw1pVDY=8dcN32 zGO%IKjoeB-R)L=HPhxvJw2j*({WHk@2{4o}QOX}HF?P9ZYU8i8`4Kaqz1+5&g$`c_ zye3hZNlEF5fRKl;-P&bt^?JH(72Ac-HwkRaya#y!kr$PsKwCyXpnI5{R5bXz-aTux z#J&fiI@9+y6t?p^3{c&Frs7d{gvK`&Kj<=ZgYcE06Gogt{`@U;k}!lxkk+h6D&46- zbrv0E(X8U^se5ivd9olAg%&woLpII|uqBn#iTI7pKq@#`W<*;k@tQx^Lfv&+&?G#@ zIuk>6B2j7*CRg9HRy5$?8~xQ1hxSZc0@=?;HgMq#v3#i86AlY}4eLOf1(0J=1NVo( z|4bY&BZCmE6KWS^0~T?zpdnmQe@0~~ z`luu-=H@53Wb502k6Tfq!C0**n0OTnZGPYWvkX@JK94}PzM?)tvEVl5BixyVxk7u|lnz0u-;)##~3@6wP$saBm_?{5wr2lyh&Hs6gbB1l5uf0GJN1z6-m!Tjz zapnj$OO%xt{nVGCSu|?l)BJ96Z(V?zUbY0zcfO`OQA1O}DAN7(B-7P(U)Q0Y+0O^mYTFMCFHSm4gDFS)=$Z$JU zvMqZgD((y^Nm1{#;Dj@G6j>ZKi?N5_pLRufa`mu@l@CKS*;k7!LbN=(X!;ZT_((IX zA1J?zG)(2m#u;;oOLRo|96`pFsz-DpYqqd-g`KwW)`6E(*cwKu2j4eH8 zHShnhdbQqY-}y7W070dMgbHr0y9BQ)y*fJk|yYv`%tse6JJUJZy$eT zb1t0eu|la()I8@j4z3ohTILWU3c`&eGCx!h-lQb+@d#XgJZG(ji&~TU1eLbznGhzX zMZ0@pl(d1O93;$v)9B~_Ko!1~{|~C@w+S}z*0A@md>MarJ** zCHwDxUPZW;M)v#PcQv+*`#=Yt?x$&M%g$b5 zUQnLS%S%0nYR?wNGTqO~euaRn8Mu5vOZj}4k2hinNsVcHm-{-Gp8c1kI;tqx>j`~N zAmawDN!f;I&#@gJc@Z@h-Q~PUNvM%j?loM;XH6_$aqEI9uGfl1el<9i#@t9snwqed zvZ*?qwvgfU-&&j56w9-l{Yl4~H7sv zHZoHTQH$Y-OO(9Nq?Cl-hom!>Sd8nLEO1?~Li#3|E=POv4V|9i zECqL1&?=JWKUx#{AFWB|pHTj6eEdh&$)s3n>sKdKGdsK;X?kqf31fHWe|Sif9dUtn zvgwso+4>ROS-1V^u`T$^E|~c}ahrbmJ;q68VZ4s=1Z0@UM*0)>!t?m;rPhV)RWpgZ z&!zeH&hp$nUY#i~$w1`C)`V-}<-t+RN~GWOz7Z|rMV1`UnFl@@uekOGDUL3PUwRGF z9m-r<;vov0z{RWVA?@GYDU)+_aVW#eg`q=cWw;&9G7jI4^)u41*6B0t% zS)3NIn=yfCgiCW^z2n$Ej^-O>EfBiW&sI{1e}ETlQ?giXbvDL8<2Ad*ebIy9Pn}JP z0~>u1Of{&-EDzhFJ81sMB=XyrLpJvqN1%~6n&bRBKsdasq+e_E?jEhjb z5Ffz%X%Nuen|RV<+@EZP@Re#=bQQbvPvU!dH)?8LTcA)RKo(g-Z~3``w(=vFH3xgG zlTy(|r_}`6O3QjN$(9VPZ_gPn>s>MLsYNNqo8uG?ANRjKwR9xWRHR(6n9ugI8B`mC2+NGU?of{{|eR5iJjTGF| zu%fF;@R@G{;UKs2NTe-Wh>I{Ze$KpZk$0=x6*OgyO~B!OC3g~UQR|U8EkLN)aDidn z;KtFD6NXaw%XLH?OTZrep)@%y32vnV$*f7H>_aH|K1RH?*l5tuG|Bd`jA%FcEBjsg zbZZDpJGApqzx3}RoV2FkYVvkZKp}rckBFMNw?iJrzpmPAGBI5t zFKW1d#eK22tA|Z_`vljHXMF{z>}V_ZXaX^Vo`h zwRX*RDW?Pc=>IeuWKaL&Lee#~j3~tUv4y+=D;k&@r8cW2+y65|(H@}MoD+vzY9h%j z#=N-f`fZrfP^l2Kcd42$Q`a8tzVAg0yC?%C8~VY%oq*ifLYW?UL;Ym?O&6ukd)bxK z2fTmgn?^_I*_sZVk)g^I;#$xQ7Qa9#GCwf(IfAwaAV6UI89pe(t_~o3n>0I>@eBs? zt{zfG33=jAaz zGU;nH-Q7^ieM$P$vKb9abie65B>7{RItERWeWLxO-e3BgMb_`wR5v)Ar<$b0xwYn{%L`EM2_UPH*wR zL>$=^ATq_6Cjih3l}}AM43UM)PsN0E?7xZ$3=Qy$QKrDHPf_i2Z#EFt+JJM-^h#u` z0+4_g=k;+*1j!?AIdtXNPO8lN?W?xbM<<1b;NA8MNn2OCrAV*c64}Gx==kB+HcEw{ zy*F^-cdc|g{Nw;A%o3;~W7*uHq`MCEY*NN2$t$EEgg5s=Hq6c-Q%zw-wE1=F{YoFX zWZYstC{b8enOq!gui#<0ZleccTbc8_FZeYP(|G#(7^{q)T*dcm(9OKevTzT~QQ>E8 z=v&}kN3epBk8TPKvElG-OCip$z5SC1)O3wecZOm}3IFHuc#@`+?6X_o;N5G`ClE1n z_sVEzPZt5$6ma_rnk=#tK z+VGM3{wh`G8^Dd=aRG4Q>dizoDl6h}HW4iMNqLeoKx(YhAkxQ($fz=V=>Mc?#vP#`jHlY7yS9bMEXo%3GD+F!4Lki54-mLfH%4g;*<=f^Rj0|R zSlhPaAWq%_3y1j1Hktr8PCNK>x^#)PKLI>%EI0 z4}aQOH4MYl+iF?HuusXc-c@aIFQBh18>cv{N9hbNE69~D8%LU}C;kaD4_g+)(TEym1(1lXWK_D<<~f^NS<4E7tfX=j&b`|Qry;Y<^jQXDsb@R3ze zv5_TFPx1TT7fZ+}@qfczn?s1gLxBv2Ye6+$jDKV|9u3lT=1+Ga$F2v^>>>$>B`+-;IBpoM+%V|ayP*a9reKkzPENPWn;tV< zW??0;Yp{PH=S;<#5VmWK`XYl$#;tW_;Y98DVDr)px4WJE1oF>fA>UNr0E;2M~4Ewu8y+^z~GxTe&dIXz;!O7&?pbR}oW72-x(QNvC2YYz>DTV5N- z)c|ecHm{AkAnYf%mG0^;8v%P*jP4)yG-_|Q`oo@L%CC~$HMfLe_1Y{tD{k@RER!(% z{oDOJ`${pROLE<>STwfhnkE{X!8>bzi>9bn^?RmF!4TAvO3?(1KW}Jn>aa7dL@ace zi+gyFyk~ibx2S3pX^OoCwD?r*4gvwpH1WZ#Nsrq#!~wxA9C86+)y9T1*GMIU;(V46o%m0Oni1UpleX>02r zj=EhKYVI12kJYj8KxN@dMn&w|tX@xRD8@(bIU29{{0H`p3@d$P-;f0bhhW0FpyC5;Ko`U&T z#99}ogS@0tT9P)?Gaxj2MNbLS3LY%YRx{E10_FtYa9Nd1v&kvF`lNhl#LUdamtJbn zW64Frv7X{Xmk8qzZ^Z#uQESqTwdlO%b;`VyRXM4i#-_*`N176sD}zy1^e?3v-0Ya) zVxnyc?#u^6OEb=t(($^(n22|?URUyU}m zGumCAcu=8eYzW1+Gs?J%{x(TSGv77H&B=*yr~qkOVf^d(nj`(eA+@`EG~^(jwyZK` zNZ-(D`6sZsEl!zlnp-_eO}x|RkhfNH2-c83zYe{B_ zO`mRgmYvMbz#7)wq7EZX{*SK$_*#^{VUy|=+q%pZSz%ksfthS$SVShp?s%#LBC9*P z06778%@hmy&is;X19^>xR__#ZAvw9iU8>CUKzx`)=5sH_{^Hg*3uLOr=juTN|z*7<@cz`!Bg#(K_Z zf4Z3=sRl$z^E$oaG0D!lP5VB_J?`g9i27HKh2ePW1YPPjNAbtd2uI&7wZXFyqg0M? z1@$C+p-ZuPeNr(oHuXahY+TASOkII&f;(GjmozeYjqQbU)iKXM*6}!tCV97KXH$rCS&jL4+jIuMr$%F6~PFyYJ)2DQeckTACH~% z_hwncva`RoJi|TRQj~JxJ3?XC_h6fnM{7Fin4guHQY|7!M=W8 zdB5DxDk$Gu%<7u+HA1AxmbO*9d-3Qw^Va?M?f6gc^0aCyGa&}hbk+pW65E7EV!axf zQ}%uP29dWb+wZg7M@o0-f9EcCId#ch4SbA-_5=L<(<=(I>ucBg1|A-Otj`&vXm0$s zrS{9mJcnAth3R!SOV(bny`(-1>B}9X2V7iGzg!a>%42FYRKKbHi#}?7gvqX;k_ynf zU#3?J87{V|S_37&5d^ z8CWIRBs_)`rP*~-*u&X-b5wb^9P<0)uTdmf5&YID@?L{fY3Zs0nJnGtuPg#ncPB$q zWFuc`sdT(u4bNuU`!ar+E^gCxh*`@h5OU5~HM}!#i}EtOi#tb6tZN$uIXPGB085|X z`t^TDBSt)aHkoic6Prhf>^1o0>Z`UtrL5B@z?jt&;J+@8@UECcEESYfaJWoerJUCha-B**7?jsygTeq(;C;zgQ^;!VI*M0fJe1_5(aq8k#SLwgx z(Ht_oWiX)9ky3WmuN!#XDzvQxKy4b9ss2Z}Jg|)jk}X{^#2;ngbi66?_L$wf$8N!1 zZkSovU2toj0UvjcFwxx&ElCz7*K#*JTqTM|kH(T~L`1JGgj4!?9w##sPDk^k+oDLx zZeO2-NN~!3H2NpuA=FmMLAY)cLjS<23;$YRZf);d7;R^g*F)&n6uhya?;puZ zUhys_$q5BI8@ymDajHJn*nD8CAHq~*Q|WsLXs!>UV+Jl(OBuzCZQT5Mm^1GxX~WQ> z4vc}l5~$)6FLWd9)3vVp?@2-v(VB7n5rlqBk{VEzX5IVITS4R^ZrKqoDotqke*-{@ zp=DZyHHQ^;`tww_`wt;Ml7682_`~L&n4yK?F+(lnK>m0m*{C4*9=D9cEZykh#ucf0o{F zoQ_7KN{AAAxT$%ew(77ayaKlCG55Y_3d{qzF%+*%sp{BqD>j^?hKat^zg`?`7=0jK zZE;YjbbGWUzQGseb3QmOyYS$6#A{Q)?FEhRyMKqbu2OftS&94>a)EH?`oHBhCWY<4 z{Gk4NL_Ywvd5|jcJ|yD2#(-cHF(MZSGg-_(Lv_wXzp3?cg96^}e7L0Ofw$%7OeXhp z1c5HPfM>iBT&5iC_m7;w)S*VT`cdRWbbu+m`u?$gqT>xAJiO!x&p(yX$ zp89*vNEKd5=;=gFPX9*?U5@-8F+}Xolvg@u>nyaVB!(QzKy`=#3DU#`mo;mu(7fr! zuw0Wd^Z&4Q4`G5ti;{rLwr$(4F59+k+qP}nw$WwVw(Xj}bKfjxzS(AOa&;CN|B1l9 zjb>^h{Y>mzJKgc&BB}!YtsjA}LZ31t?2YL*bUO;3br6mXiua^xG=Og9c(CuKiVU39 z$rI_P>XkGlIM@M6m9Zs~_8H_sn7$cH_hbPPm;Hx^aBvb7j%IY=hlb2G`)i+R&3!DHZJs@C>)}s6c$r7UibUl~Ljo9-R)TARJ}^eC8VE!ae-@5p1-^K3rKpd9P5T z?iP#_^i$#O4&y)a0CG@V$^12CdGBII?*}DkE8+xE9>?#$)%x!nz3{=CQzro=V}M`5 zbm64tLxTWUqi6O?LF1>*muvT3Vq$Y2eP$pnSfciEmkba&vxyIJJH<`i;sgG*j*MAq z$-4T>>Wv`)f^#L(b&ItU}Q;3)n^)Bn(+xXZfQr!%VVUmrAg7ENOMa(D=b zJC`d@_5(L{U$pP+e&DXOJ%eXcLSg13GQHsOz)SB&$R;b8b8|k3e{N`>q_OIM*wDj& z*wCKR?*Cy!t_1&oHl!L-g^9CY@jDXir^}yC*TpcI!K&Q6-EhZ0@W7+&T?p5x*!Jw?HLE zs$%U{8;9VZA(M$X_O%;IJ4ZuCk9FC9H3;`j*sJhhv)%~&^;9^Sy;eMX&?q<%8XkG%vibsCa^j-W;cR((2#?U)G#=e^ZOUmw+Lh>2XZ7) zja5$)_jD~iR*wiZj)9cHhKYNLcUJ6zv_h(mExvl@S8M)2PlM6VLSO>$sj={gi z>~WVk!36sdmbV|MEUk${$oECZ(%)-`BvgY501pYhS(&Jh<^IFJ&aVFPFA9h7|MIUO z7@c3JhD7{-{A-}|fB07_?-cWrR@b*B&Tq!tZj>#p!=INKwHHyt9sl0=&o5wjh2>#Te+ZCsA`WS*6I4lwDybyq42eD zz6yO4=}jIZ(+YXzyJ_p%LhCgNKcb$#^{Ikb+>qrHV zPEG$)EEE1r1!R6&J2dHH+_4-+%O-@%YX zF~%b~jefLA;a?V0H;VpcIfZ}Xgf>$Se-ov;_*vLCBlncmFZG6|JJfv!DF=)gZ?RXl z(1bhCv!`|q9l5hUMXvVs(OTg(ECx#CheO8vnTB^CfCH|y>a?E-S~i!5OsEB(b_3&_ z_XUG0JJbq$Xb!j7x;Gd(w>)3_!Cwc*6)l1U{&i!Zpg)O#CY>`|}% z*za_whZUBdcoX}drWuoj>uS4ujXZMS&Lt94ysqxwp|$GM%A|-VSMR)9GzIdn#@SZ?BjCZNiKx=R$#^*GYqfbkDFl zF4E!c&<6D#7PkIx5@ti}T6g+oa}hsA1Qhl!Z3q-AN5{+AXbE<9L+Z32;uo9O9lf2P z_Q|-~*i>B+*uGZ`K^&;Tqq0XY-TXUu1IK5D{^}=O%Yl8LKk}8N{VZ7*!~nCxKLA(+ zSW0gCgZkW25Ah+{cvn8_9*Or~4F-Oi@9UjOum}J}Bg=G{3|xsGr_+IQ-Sb}#rvAS< z82Xrzw@6<#)jtbXO8PSf-~QIO_C}@8E;0xX%jUh|ExJ}r6 z>9-F=aO<1HSbKxhK4Wn*_w#JyUCDT()POfT1S7J*qd?5pT2JS% zAHVxl61y(FZ06JBKXYYm_x!PYC_@+sv^sWVTz~)MC--KG@h`?i%u<>Q0x@=6_^tP# z!F%?YTf$u@anh$teNEkCOTi=(UV4|hn+6O(I6$G?W~hJ;iOxlw!R2`&0*k?zKX2fv zHq}#}=9W~?xaq$k!$#;HP41&RS}m zj`^r%yFJUVR4`|5zTGpG&{7CIVU~fn|1$vdrIwfNZdu~jhkI})#LkkI<@%= zH#iH|)5N)QxwUtM`9ldYAB7Zqr@;h!9e3AvpDC5M2X2e>(aMc7bFvC(Z@QwgcgE{# zXSDfXUzrO+h(`bmH>(s6taDi;nj2r6x0WYo|3%2pUxW+`28?Wa`AY7_M`~#Ci$f#} zXmQn%q(~(jQ#EGqvG;EhU=qws^vD6a%%4;Ndht?G7?y@yF*_4Bxz&a214WJ}KdSqkIR*m*R zaKKKZt8P^^Ol7k2lBxrdfrg!==m(4onA`fMOHWbhBOOp^nz$C*k<`%b2l6ygIb|*U ze3V%tVW9+tS_lQmgR5lWCNVJwm>GSRwTleUEDc7BX;PuykOC+66+>P!BjVeS0 zp<_}sFq^RKll_*Nl=yt$)QUVa=Q52p*%^hR$q!e@O!jvt!dY2NpoGYPn<*l zG=uR|lAp^@PPOMLtiW*qXH*14Q4rU7_)7PxxBsl!aWlZ zjb_=0Ma6J;p6r&wnx73#{f3emB$sykJiK7{X#0_13%z1ic;h-uSOQW{+EzitN41*4 zRhd<)3RuSxg$^Cc@B~MGwd)Z0gALfey(`K+2%^YC-|amIume`0>X{!DR-W}OWGG-r z=#GC52_>niAte9)1K2lZ^t@486me{Datosf-A*93U6Mjw3z2I|5zM{y7b{llBO=k~ zq9aYyJmZeQBWRWm^CTC&o?wpED_6m4gbClPUhbWwvjH{A z4Sru3l`YZJbb93UL-DR3^Pp9TE_tol<_LRTeeCHBtrZUcN^4ILN%2pL$zR>n{9eOAj}EzD|*BLGd1~L@$@~;QArwxUk!6jT2Qu<%#uK{@sSHSXu1E z>Cx;@si}-x?^x;MR<|SuN?KIk+$(QhgM*)Sj4h-Ei|lqFn;1%YiHrS>J8uxOrHsZM za5UT;zM$Mb(moGV4#bW~!vaT|sy;z>LdS7BF-`MLk10LOT>n}xyh6oxFmo1%ooe8V znET1sxF64Gg9kR2J=`FQKj0M4L&nj&xbSxdbKd}Tm0GzE`o!dq`zT4!^HO3?4W=rl z6d0m{?w%Z9t{WLB*5RA)YzMvqvdmNsEozBfPp?3b=-wE~wtXSooRRKa|41MsZj>o- zleoi#(2()82HL;-fkWr58QVAt2xTYW}ZdTZU`+%{}}QDbV^T5 z($7lsB?TFm1!<%4EmT4kbk~~s0Uxa@r$E@miMrl?X`1ZSF5zak^b$6Z0`yURB&Ndr zxyse_{Y%i~+N5IdgT7#w&Ew7(Or9_`@ATW1Sn*QV!9Md%%FqL!=V5(P=V7PcU*t9q z>4K}pRxzPHb}J_fDB<^gtR%i|ACOi3z&4H#*K2L;{bax`SW zSwtPK@RXgu^f9n$RUt(TYGWAjo9==sv{&T_|q zlMz1FYJ}P89uF$gt6W5w)dLotHVd4mmrV>~WVHg&i5wV;lW0Jz;3vjq-KB&$ATdvHm%>M6`4=AJ;=E!^kXfa`>#VBqaz}HC=|Ga zlh9T@?d2)@d-@Kb0IL-e?XCm>V5HHFD~$02ZK<7qmNQmItQJhJ%?b*_4zqm4{aL8` z_8&JnAjfOfx3P>Y5JgW+eh_p?`xvUa*G5p#cVw~}4PZ=>t+@>TIg6D+&kdj{n(0E{ zOxB^rmKX&lM;P=wkJ)0!A(v~vu-zdot`ptJ{l*tIjGrnLfmrUHve7DiHbBvPXD)fr`; z%xV_ZpG=#EMp_&#qNcuG8b;ibU+}YHiJ~1z01Ge#+S`uc+KLm>5nz-!`~xk3AePCMrpA}AY_u*mrwnC^UgAZ0&=`A)6l!(8$W;W4aDhzVc^VPeDf>QG*lx@Q`{V{Fk=Yl zU;}TkTbd$H3%q{gFcc86Rj#etPF2WwAqIzF;H}C%!kjdJ@R;7EQd2a{6PbHQ4bs;V zZ`b8Cnzx1^T%CBY8|^QJ%Z@PPZF|AfW38whmHbyKE73ju2Jp|2vrOKv1B^{R2p=dN z+L2fgIJbB+lfB?F|2EuTj~WPGe>jC1F+jvqiNG*M{KMfBuaTYtFjA&W{UBFN1GE_Y z=S$Op8i=F);WBgH5amT0jws;eKa-w$N+LYfR*XF+vQ668CAho?nH(Ghw!v;Q?AO#? zCXXUtG2D~3B_($PKD4WqA`T3nU#1C8Psx_U4G&91B3g-BRNrs^6OuH^Z)BTiz`Q)XR24G1lRGYBSRYvTDGwj4xB8iAq{#{`G zNUJ7L_vjtiphPs;Y9$5|bH3HX19bTX52c;N`|Z2x(Q-RvOQrZytJfYU_n?&`9eAhmeq4$37#phYp04^`YkL9X2O{84Xr#`` zZunQQJh=tHW{dsV%c({g;|On0VOQvpy`=m)W-a~#1HfKxd9{H^?ZJKpv%AvC`H88NWORYU%r$2=Eg0Q1rH$0Tb8d-PKGhO^8*4tB<30;R; zlGhSeX144vDX!}|WN45}&zjJq3P8QkdkoJ7CC7{c!ZM_fYLpTAj&H^oWQox2yw*bf z1Sk$ZHHF3`CTS5it>`-8egv@-b=agd=Y8i$V~_oZKU8rcSy!LGNoX#KhCl;yu$Trd zp~h@Tp#pZwR4IwfpXQhFLrt`}!mMDJL~AwV2)re9Q<>CbVX9O5W}f^4g3+G2ZW*kV zA4oT$O&|3@!Y$Yzo`8iW;-)F4LB%1d-w!&SV@eflVq`6>6DH62vejuhzPvkVi_Rj~ z1g-@`k=29;AL|I~?(Ab?g3!rZUS@!44*!&=7;rh}fM)sJT#YxVmYF=&7N`PYJ_cBl zi{-sH>FDN_pvohpDoWhIpeh3Xo;VxzugSzB5v1SY{?(0IeHCLPF~!m|j%U~|FdEd2 zj(m-#RE^tb47m?M5#$*vnP zW5QSi({8*QPq?@eKv5%KaHyDg?Ud#7MIK4nw(p}MK5^b6p{9^OP7$jcZe4sav_?|e zV?^nS8D1NKCPNWt&EVfDNed9l8=AZ$pfBE0HACKXV7N;&o)mbJ)LGDNnr?gDwX4iC zCUb{CYW^sLxDMGbyr=gOsIJN<^`hfF#<@6d{3i$8emS2){`v6iM3{+<_42(}JXC?> z%h^3t!A1EQ{*~ASvdg4FcAgnH?(69;SlYoTsIbjbvaO^$^!tM^JakraMEY;nXAbcm zQsSzbwq)Ngq#{ct!ZK0zK|7fKI=3eLy+0Xc^{mYH7NzXg?N8PvL3_SLDbKYc+LfDK z#R)tiHM}>^k2sSOyvz3x5?K|H4po699?1af)r^SCb3t&WaDJ*4C{%q>LC{o8TDgIc}0^&j7{;92}$FK6Gm+-sV9C^YKL-{`69BFN<{gd z2-{dNDT{Oij+c0SI8aZ>h7%r2dFQ`dbY?FQT^DaI8emFYWNZC2(CzBAO-<+a@FlYK zo#4GPs9r=&&BzljgT576IWpj0nm2h?O18PfZhd5wIfG4jVr?`>71?(X4<;U@-lrom zAF?!vtHtRFY$@QU3HT2-wPoxrqrMQ!1U=D$;`m<|@0V&rL90a0KTGfP5jzX+aN4`+wRA)D%)f2*(%2)*&=@ub6*^l&RaQh*`;MfHXsllAb{{GM(^|sj>ARRe8eRK7Y;>_AHM;!1S1NC! zUA#7&UX)*ppSfF;$PRfgiB@h?UsSO0ib7nTe z)2H*LLe){_?=R~g)iGzaL4uI9o;ouh7ne;Yc9{};ApEiO{W)42qxNHR^J=R!8ofo9eN$7o$St9eG05a zaW(s4F63?Bdl*Z)!BllS3{7y#w{C5MeRax1LeI6YH%*qFZ&uT=o98@Rb1rU{V&sr{ zQv#bft!506Rbtbty4TZT*qtwym(F7FZFSsnHHp2J#HEWcq;P#~n%pzFmc%AoFPZn> zrgmOv8~0}Sx+@CX%aDS2qXdDWR!>VVPtB72!Ru|lVKORdYu6J!m^_WO7nPeCGDkF2 zp%*WeyY0u31)bk&%WF&5ZMCDNw!c!t^PSt*>zF^%qZRABx_@`_s1BE|8?A1ck{m8x zR-N)8m}Iwm*>qqy(;O~bSKVC9QvN;fJ-*VJ;BfKUa(ylz<8b-ha(LEA{`bCS3)j+% zCE5EA=6l`Ndv1%<@i#yQtRgVvpN7$J< zB{2S-I;|R?##q65-I;5wZ@vEz%@)J!q*u86kY{@xo|QV5MR_|`ZR~bXFlJ{UOqD@- zPV%RF`3Iz}-bIrmz6xZUI|ljkq4oiSuWJjA60S57Lz{KezKyK@o;pmf5ZjJQwsI%7^m6zfasKWdZ+9Lm0y84ucz@?AEt?u2Und5So&E;1Q(=b1JkkLwMn*Td~x~2L|D@BvhKL#$->H63H4g ze4iDI?zhC<9N59%PwVvu8n>D<%GZl#yGNpq0l*cJ!^1J>(o(TZp3Z6jSo23b?fBdwYGOm{U#%n zLy8gxK552(-j2qRiMB$H^L)t)bd2pE=E9a@af&>#Y<{kHe_3^u2*AbQHla;9JILivfe!^vlnf?&=DhyYUNF zZ%Dz!DKWx%2pW6m3>(z>JqTy9eIBRvxo}A&vM`uEW0M?KtesXC7fmg)itR<>pUuW* zk7UAehv!L;2NrlX*QpnhDJ9+#3>k?>7JQ8Z82Rv$g&Xyaq%T9Xkus!Yc3C!6Rnq@e zM-(ZC58^69m2Eo}Z6Q-uojlh!anqp?3`w9}Ci*mXbY7bAUP(4P` zvE#+jTQ~~+yvyyZ`?HYQ$?CC;(f8T=kMqEZSD^|clh77BB$YN9-#uAOBsTjT3k^7O zJ#HqMTf!|8oSt?ksa}RE#Elf+x3Y41vU;;x`glEk`+H|J#)n#5@@nxo@#FXLSbncw zs)+$JWjC(n&6Rb_K8*zA(OKK{o1}+WKy&N?7BEXkfRyunGe!u0QVb;-1t7>PBV+4B ziy!8_UnZ)wAK|?CXV*P%<~+8*qLLUeP6*yTcihLM1q9Le$l6scUpIU9D7ab8-NF#y z2nC@k?~_W|5z*r#O!8UzzrlqhiNHNRTvG#+O%DUT+4`E`n@&9%)cj259qaeVmKn{& z`T)ezhXID_0N*7U<YF$vyLmk$lCobA7x_>y|aGZ?E&LEoM4#v_}t6bD;|1?iAdDwz~+f_WOk^$XCbdxu-=0V?1^bV}#f(vcMf z$o6e)T)4Wnx^*GYma>13gf0n8@q|ki0Om&GM;2)y?A6d)47PsGe)CU-jV59L{u^?y ze+vvj|8nZurjus6J(z&U(1twAKMc;CCwZrgh*5%B-7PRHxgA zk&D~i+Wb+jJ1pDQwO!$P&_?5*L_O=5M4=B}H(?7#-GiCr!E@Nt&FlHux)}2N`mj8| z*=85kwc^90cL-NyX3Z4$?R*baBFos~Yi8!=X4V8hQ?$|w*b{xAgtiCEBV|D&X$3{K zVdvtirE}U(^F)lHgfLt4g3=-ah(j7*ouU8Abb{OymP!YULX>mDX*u2@4f0Mh&|@WSTxb!(e72~6aUpr~Xf z(=Ecn=vXrh@PdWg744g{e+;*jMdtO+ZYVtz%eDyqlHw^EP=d<2t9DVfI$N##W(!4# z7KD6NUq9S}f%Lliw(q}vC%d%j zy6;79ay_G?>fT+0wz7niF5@yjPcM@+hQ!hxI$V>EfF_0+=`J72~uohnbbkI(HV?mSX8d&Rr*~~Be8bsRRgqKLx;_@BnCQan3=(#h zT*$pLIYNSuiN4&wlD72{{u-iVP8Cu6b8ReSW;8PUE+n5M#(c*J54Si>o1yzte#qd) zdD(RxEj$xIux3AP)&Y0<9DqF$4=%x-xIZbE9uIYW-UGkT%#oNelVk&8_%q&(^nc*Z zegFsC3LGXf9sgYFjg@$PBxg4e?7gtDJttAjcuBvZ_fp7rd`u=#n8VnYidC<0IL zVV1pBNiU&7AI2}|p~`MJ5$+%Gyw_a=|NBZI^$9qnS#x`$U>%$>s>@3D+aD!{l4naL z4nVt%WS>bi>S+Q#hV>ad%+GNb8AHCfj+guN%e*NcoOi<5V`bi+ctb95tx6}XibnQU z#uS;2%%O_Izj6T_iYN+(Xcu|epU%efGK5f>mJ{j^s||kFVT~2qaqMMc+uO2zZS0;J z%DUO}PwzZWGld>J27l`{_IV(teEp#0Gy(~-;gR%@wLB)~3`Ewaq_+!iPc2GA-jB4X zlJg+8Z*5T9oW?`(k2KM~ep>Fo%_V(gO_Izf3gf@Qzh29*(U^n!O|_;X#e!5gu>>ax zYSj`{xSJ@95!x3iDSR9#Ya}tO=P#YxG;?gL7I9s;fWLmPWy>D*S{IA0)ZhF^%)YDh zHTUC1I`*PVe=$dHc{|lNj7ksotPFu0&e`1KM9{9JsaS_AQQvEsm4^J|%k?P)dm+Cm zp7@yH^-m)Jd{vBx;Fx0&1aTUJM;SyBAB4|>5NH-nN^;BziOBYL##o5svah{yyv<)7L*7D;FGK zima0RpS<}Hp=gbaAEVLx!k`AFwJH(&d16|5rP!h7yq68~1zsV4AU`i==UGSTNP%E; zN~7%;SKQouptI$Aw~E^lof9ht5z9H+@*EnGo^DK)$(j9(@PKvMkdkGyyU(J1W<&Qe ztl0~4Joe-@>~jr&c>GCWQsu$!8w+JWo_aZ^50QmNA8!{nq)s+L@SKN6IDLs?56e<} zg?b1bwQY}++9{bU)%91=Ei}>wvG7XCo9+U0ReP8DP2AL`|MSsg-5OFwmXLl> z6K`LNn(mXnHFuY(18~1}dvPf2aKZl@F{(HO-#7P(#av?zH!}|Y+Ny|6x6w@(46M%j zX(P#}J^XegrEN2+Ws%YYu=&+HL4z%B+21foew*ereuqq0=AFQLicEK$d~dw)bYO|+ ziJpzwd)M==rZ8p={Dq=B9B9_NmkA28Ot?x*%`y%e>64Wzk;F+O1;nQv-38Ef;Y-V@ z^20v~?RK9?Ml6}u>W{j*g-bm@fK8B2xuXY48#CyhjTKp@a z0A6E-u7Dm6p(uIpeUnuf|18++^WAos<7-ye^;)XOvr4@+rr0PFbO zgA#{^08%D^nV<5kym!5V;Qt#D^=Fyum$9=u{QHO(+<9pH)ihN~pwO-h4{JkvLH_Bo zkvKdzU_Xdd2DF#WLOgJ`L_8Sw1k%m`F=CILJ^3~bO5~0h_V`m}glJeiQuLEuB@_nkJE`bFB?ZLcX2iU!>=Ihp!7_ z63u~M5ky)^V`eAe#S+}5Nso(rwU5qx;q|#q>vS0$;q@#2 zWmpt~G-}|0B!hcfx)9XbK50#hxt1Cfe&TO#vYvKwV;qz{9)6?L+5a@}7iB2js=a}@ zVMNY9uzeSTZ;P%>iojWAjqWjfk(8$=4xV2=a^<0e+WHjBhlA^`~yiNl; zp~@wNJeJ};L+0qhNcGWrg@?xmZ#t8ohd?%>y_ZH?3#yjrmoCkF^?98djb*52kiq*X zcQnXzMeSS<=4QTrMAkx8(wh`+v~x&^6($W^%?YL}+#teP6ZG#FKBmO42=8GjN#=M# zYtT(9msRdl+)b6{J{ASs3~S`AUiN)jJF|3fvElWH0p^+d)b z>6B;VF;hijBaenqVl|o2N-|#Bc#I0^=tiR_#TzWyQ$!U&tm8TsaV~Y0Mtml`eIM_ID)R$SVIgy+( zUoE6ln=(suRY|SOF@!gj$2~f)i`P3V2pCvLbT?37DxsA#>9fog90VMIt&oN8v>Ez0 z*&0;iW{x}eU=OX576(Y@IK4JewrZ*b2HrNNatB?~(34He5Pxjh{K~EOIU!)uJpQue z%sMs9kHKe=xmg7$=3SP$*F@=KHCb};nZ(u5BFv7ri5EsJc=!Ol^^7B<-kP z&Y_4aH8vn1N4r;IqxEAn?+YIT9sN_*SgmwT1`A z9TD(%XKQ2I;ufVIa~{5q5j!tiscsEY`u*y=+oQL&X?gwT$uleGhK&S+PXChJog%BJ zrsm})wH4e~6&zWqJ{(t}jn!lk@%s*@hO3r#MCQZ2-_7hNsmVTc?Y0fv(NGtoT`APg?G2&u9xIg2!auxz+f2yt2Wj zceBZ1LVMbHy*s;#m&V6X%-elNlMsvE{_6wP*!)?tmue@1ZYdMD@IU@vPKC}?@lxwc zD`_n)qe0EM(h$xu+ZP<2EZ{@hW(4O$%t8tARS2~jRQfY-BNnx4t7KEQrd1e)f3zxj z;)k0P1tv_x7hvTB!Md*HcB4hff!I|xR^yT3a`M)VeWJ>LGYAiOzUj#MN7WVPxc|M_+3!Cb{|WeVFgSC5&)c_h75;D+-(qG4h7xLw6b#7z7|W z7JC{CunIw<&o9>FPbltnW*6ruFiMU#P?EP{msvvOb&7}$@*CwU^-G9~2Vhko?9!{T zEAS(IC3(59Gr05w)gB0~%rm{_mrh7m`3-U}OMW zn}o6M6r@K4z-oZA5O-m>SB^CS!?MJP+h`*+YW7yv<6X(FfMN_lS@{cMSU4gKq;v>v z6mFi$o40gL^AI*>Xt)ADWyaT*zw&OMs5P8#t&Lq<{QmZQwFg5aXr`$#_CYdarI`Uw z+XLx$kj7H5=c*7w3)0NIOy6WJ)k};gCU<@dV!IY-uv8yoZcvQPxk}a!ViUDk>$}0( zq$9mN{4fV-)#f5EkoisiT+7!v229E|8z8TcYAHPZIBU;_){|)7 z7VMcGHL7nRV~a-O0ct+UFCqIxIw{S|&1Lc_T^d#t6_T==$lw@1;K|C#_>F`*i#qQv zk^)f0H)NW?ImSdlshuRPA=o+sz2V75z>;dgyr|wJugN2i*t@4G6@E$##5&#zoifpb z&kv~TUND6TJ5^M9lCqUIio&f9Kae(VJ-DfmOwKw}GNq+x&eAvRqe$2yu`IR6ztOHnZE1i zS?nY!l$*MwFi$XdSwhP2dX@G;Wyp>vT9qv(ms~4@>!b2{hL+U%srtw z%}9RA?+D?X-GeKPk1~irX3EejRgh$K+1$}}tJ>bBGo(V%2WV>9v~pvICYXF|L`wKK z!}e+|K{vbVXwAmXzL_;rFtN}zAt3qK>X`q&h3;`jN1o^+x5PCdDIp`VJX}f&y+6UIK|4T+7{O72KUSjN zT3w!Aoz=REo(dd0VcWNPZJSa&z^(TgR!@|C+Mcs{1?e5VKTuxmMc+mnekokH%<@et zbGlHyM;6LyUzv#^`B-*x>HOlW>qvu(thZrE>9LZ)FT#1#NV~7qaROG0=Vy4Chiu@3 zB|YiHNx)^VItLw0*T{T=RJz(Gs?YqtlK3WZd45F(3J!FCu5QcN;%r+st<9ZW^5DBfBJ_s*bZ6LR)NIEY!n7SuCK zJ}%^m2r19EcO%GT{;hpcPssnu1obKkp- zT{^0(m)NzZ?|g>PvzIN;BZW&n0Oe>o7$@$Ih1*aaom}Im{`0i}j=FAzs;an5O!g!R z(g;tk7ZTOCxlSpSCXC-rvqlC8FU^&;*DyW5A&iSvZZX$zhL%G_7e0||6@PfZ2b_}epg%Lm2 z2oeT?jYDv(JFKQ1+Pi@y0J!d(P_+MZU-AVfIL8(w++XNn84*xq3T2Y3IC-e^Fs<^0 z;Y7FTGW*IzRp(_v1ozX`O;521jbbrci9stYxqZHda`mtkD~uOx;7*h4&X7`x0>ar7e7Twbl1`~&mlQ;KnSu_%eHQErmzmn^C+Rnp1 zQ_ztH7Rg(0m4z5{!XtFliOY5s| ze-EU{fn+5wfDnJ=DCk2b0;jn5q@)n3v`A@HNa|GNF86NSX8Tf?*X!{+;=yO(%G{f}XhB847!Kigs^U#*|75Q)`AZBWjjlmIP6@TN@ZQco1Dm+JzQ@XxH84kM)WG zcry>=k5E?NB(h)xK7-NQr6|sA26#)8k_Fz?u(apx|MYyahAwztdmCdgKYhzNaQx)JSSHw&A)xx0Dw zA8kxlY$+A9lPx%(=b;P4My0H}mg&^AS@6~a(tV&MUm+b4hhG=arLFaDi1CQiUs(v4 zY?KFkWAw*=WWP38882MXv>4cHL0Ssr+2dOLg>&H z@(9CyIK86%P6+X6>3+Idb-xH31b^Cle_XZ=?Xs>~I-X!2hL%Yx zMSE`UJ(8xaS)IVGcp~rY2BWrAuv+2b_3_OY4PY5Ke&*mlNFPd(+PC0=ahX>H zW>f}2#?L!_yHS&$4b>LSe5Zs3v&YvE)Wu6GN+T~5GZS)L!D|IP$p6;CZqM56+NHoe z1vq4@C-XR^38;*b0Er&mwu|5^(5Nz&9W?jMSbmiz26}>>WLv-lMR(%xLOs*`HeqR= zcd5Ffl2dQB#z8kRzzvRw!**1grb2b!ZX2x@J*{1>1;wH55<|63JdhC0w1G$#9G8o! zvuq_)j6{xZ60*^WeFN0TKO-rjh((Ow2hg%-UWvD%epgMT2p8|iz&!miK7?gTjWy_( zi7V+_bq``V&wGhwQs4Dka8OWBBwDXZ z-Odkj9*VE)g?#KE+hugFOyKA4HLG3ZVAdTwVrnJ`H<0)jGIBu_^@Y%W;S3aziiS?s zOsU;CSa;M{yZ;ip9l=0Z;fVR-^kT?*j~EHWS(5SU-q@W#GwCYYN}wfBmIMh_-hE$u z9NFYY!fO7&o))rA*AeG|PqQj0&AYNco1#@6+%FMSbURBVA-2R1B`Fb`%oGyAsha8_53l2KHUA+P@c`ZxZ9Y04`9;4e&PBGfOCwpN7_nw)GX&F zon_Wvtiuo^WDs97wc1J5U?xgFBeKr#-^O;$SlNDg=C6;z+O;E1)@vkcO}YJ+TfTGh}@JA+Nvv5K;m-s6eTExr@q_;Y#p(Woxjp81y>Sq%i6`7T$7N~vuy@g|6Cu_^3u!HS5{F?DP zL!iq_pah2H??KU|BwCCaNGo+sLSU!Yt1iaGKAI8D%*#qu*5Qk_lYqYy5q6|hp7j?a zjALbI`nYZbq`k%3aclY{kxA zdj4VYM00za8NiPbbw&Q^VF=iOD?KSINnxxHUDI6$SJB`XVBurngHzLRX@E`*F>~Wt zFbVgOohaUnaNi(_e(79<56t&#!+C-1pN`WL>)ud1SlLpd^zijU1l-vGvp6I9vI9v! zJMcHYd5tWp)$g*+X$jXU6`nWWq-EFd7zRRzBWd2u>=3`VO(xLw$=W!I1Q$&lnF4!A z3=k+HQoNUgq46FAT<1(f<}c zXyWt6r6>l!h>7{2=TxgwzVobM<#uP&F#@y|_PnxVF>M}zlu^Au{n`r!_+e|bTiB7e ztTvC#GExC^F-M%N6P&6!!R+ld;yW6SI5{z3tIMcObl31OJ7iq5uwA-q8P8fo_c+@y#se>22C5@xiApt=r}Q+= z&3ly9ticxLMN1mpTsb>izFRJTzMg*tCW=zwCWzC+Yx%i+t||(6D)fl-<(U6(UfwQ4 zZgao1?HoEwpZ|?w_;ghSBNq$G)Qu1j3d2KNll~)&m0@+iz`@Hac)PbJ{AoJ8>C&pT zU8B&(ycSHL58H?__40D91uGaQfNimh6QF6j5kP8S84FQMRiHr~Y}_H#AWbk`teIP) z%Z@A;~TE(^&JX?kY{4 z7bB>=k%2W{COd~_T51!Isl0OO#@?l+!`vlxgmPpZL=Q`aE zBW1l#*%kS1g{BmTT~dlzp3+T0$FgiV+1W!I{lcb?LukS&@9Ml$u_UQbhXF2%dJ2Am zg)XugK${dyS!V!ov`P;x-$}tj(4}?qbdokmW(SK$EgawPktbqjO#Fu__@jOf>DICL z1V6LB45P*fdV*UoDi1%K$^C998Mp8eK!%W3u%v$Vk=6L8H^R^}G#a{*1+V6zo1%;| z^8=Z0&qx#4oQx$Nygew*4%eRff*k4MI04$SaP5CFb`D{pvt5)e-{LLXwr$_C zZQHhO+qP}nwryKo-~aTaXFWYN#0<0VZhUyU5~Z%EP? z7kdFY%iGJwNekuQ4_W+bK@^K6$Pg9Qn&e)FVehw6=DeQz>$gB2ii2YF zw~>>lhK1nY5pN|f%+oMgC34kq&6<{~J+AJ~bnz8j9OyhP#S7pk3y3HI78B@!CnujZ z+z!J@K3~`!ccc%#!)r(NMr*!b9~9Xoa$wQWiEXnd77f1Ar!KU@;l4G0bsZ&qm>vmn zVu$rSvIMr~9+=blz;GeY4F7&=Wb^SCK7}DLNCK-%COdYsL;(F1teqY{MI>v2w|MsF zq#v>yYgJ8S*}Z;_Idw6*^&sOU+wyqr$)Lp|Ztqt}|1r@G`4@uW|ej2f2f9jEKL^%btNr1VWgEG&SZd#sxXofD$28nz8X=4SgvG+|ACNWe1o~tekRG# z?ip&XZr0%J@W?{x%5I(KUzDgR`K=PfQ^K zj|=93cn>Lrps$K3T)Ez))f<(oH4Fsl{3Ma%8h|5@Jd*{s2um4>KHqLMCnI+)aOJ_( zU;Ujdq_c(5B`(Gky{#r83J+$N)5t~v$> z#G1OlXh3FPF^Hz|*@JD#{Y;|DhD|8{vP-ZWO_a2Fv z;4F-OqA!eQ0?5WmOjqBxE;tO@@7IF?FN!E`lZt(%_o0y+Lt7r@RDx;llcN>w;mgPmyaHHPNgc-R2+j!@QC=`MLAsYaN z=m^(8)(Ii>DWY5Jbox^AND6_7NW|kZ<7Ie+Q|)%#)1E3RxqM&69Qr! zW`Qt;j)#P4J(eG*Z@aFiIuHz=Er-BG>eg|0RB0%pl7Yz&X49D(uo!NmSDNp41f!!s zRlO^OQl-fw;rTfUZvYMnNgP^G(#Cs&;qZ34b$4`pXOJ1lJ2JDqjJ0gCXZY)bf}N`e z31joJidy?foOVWm58y2U9Gv$+=I**#d_o_0fQJv)-K&KXyy-86o8Z7ja^fmEeTOPB zZI`H_5){W78OVDVNx91-HSK%lPiqhbCeq?)i#GjVqRV7Qq&6sMKZsYg$>xZ+5^cy5jJS4vIm8 zz}|h`L_+H3p{a`-Q!-EfF^nf~BoRM`To6m6hP-#;KK>56gR!f(M5!RS|m?LY)SC@Ci#u`Ha&eS52Mi$Ou2Mh4j zQzaqy!G>2uG}h}1Q#2tX*pev2d_}tcAan=wgwTb^bI!i}$yFWj_eva2_OHj%D_*F1 z;o5p&4c;gCGFhGP;Qn;Bd`t#XJVgK!DI@(?sTrYWNaf!nX6pEc{_ghuUZJ0jq$N(> z&4CrkiKiN%IKfDN>V_6ftBn;mP=I=9=xYDikCSObq*_~|Ut6eRJBXEDVb*Y~y!~G~iQVGq+YMWhq8{_#PUem($wL zMv~M>9o<=%d|EwURFvQF*VdLZ1cjNLRcU2+A}qV^p(q_C$AF&D1wI zo3Q|AFyl^gMHnw&tSd214+hr-;Uzg9@3MK-?D9@?J3y98Pnyv+WHzceldtC zX9`x`BeyH#YZooB#@@OTaAz~T-?8)B$tV9Tc+PK`E2lw5X^zMkdKoweuP_K4iSQ5$ z;Jm42!U25crCclq;CDxwMmla{yECeYX)xajN(ueiB2s=wY@@5wOv;p&N2&(3^hY-a zqSVfMc?EZ1D7ZXAgxogeNXK0~Yb_ zEp-$xXW4)tfR`eUzfaXAMiZB?8d`Ec^1RtZWM8_js`O<+ea4R<_l6T0 zw2M0rkdN6(i{P_akoGlcuLAFqR5l(Ie_6%RUPL+V0@jspIrk~g$AgQ>hH3{i>O z{qgB`=kZuZ!R0ceHYju0ti%XWASZ!QO7K0k+6YkN@f7AAKlXjs-X< zBYcfG!%))VRzH5eT8a4+ zA3avas*agLqbEnA(I}U?MFxr`%OHQJ=T$bcla0*~g}k$xnABS!Pp`C}STr?3`h+KP z!NysZXz;E{r!cDOA5@6{QEGT-H3R_zS<$xMCi$Zp5@1itQ;I-NbP#FiECt7nk4<`N zbQVK3xpw~eJ0Oz_U%&b@lf&!Cfd5T^>o5f5a?3zC(9Ob+gU58)iuQ7J2oGuJDK9K} z)xLYTk%wX40~k4~LK_XMolu~1cu}AtQZj&g;Dq!3*Mg>Aj_dG?mTE1PX$a=GvwRHZ zYjg&*6#FCV+Bl?Q>WFI3+5OUa+Fzqst_;t=u(r?BkC@Y~o!`|Bfxz*80_eaPFDt;D zL@P)TAC!jp0ybX>ka(<8R}X)2D?*G|-(oASQ(T2oDfKvU5660jQ1O&3yPY-k(B8G#}NWkWW?T<(sNXaPkb#Br! zO`^T(DqZoPd)3Bo9_aW9qZ<**adqj@`Y=nh^z~}{yEpGK$%H#Uj)#WHM0zSI?Ew7` zrIB;8&FzorF$#+WA7#+r11fq$0#_XesGyNt*Tv`HSI{cHtnKG^a7P#d_`f1-QdSOc zCktZSpw%T`+y!=GbWYxXl_MFxTZ;P~ai)A^2qZ~4V$A3&NTcj;&T{A=jEe$=H!{)R z)kQ^c2x(XSGjBnqM9w7H3gHca`6NS$&GnBMdAviDB{^Uj+`sAZ;w%I<03HTD6Xl^ zG-~R0X>-eHi4=R>xgaUBIgvX@$}aN1m$o^$XXd*jFOA5t+rjw$9U z>y8<*Q@qff;DRXiP~nWEQK-d?NT7>YYdpU@)G{s!VJ+ zNTP?d`Q;7cQjCGKfq`T4?TA7K;njP8xp4a{3s+AUqxm>6SU-|UbXH5d`N=DsAZ~Cj z(yS(aM;}YEs7Re&;W)9FSp)_fDIrgRht&Y}L#O7Q(KZ#4zX6Xv5zluRgXAIc^p zPjX$8vWi(0R)S8AoAPaIYWXfrb6u3=GAF@dMvO@h6PX&!`Nw!?LP;O%0Ai9=y$e3s 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 bzYJ@ccBaLF!wS!}%PzgDcEQ void { +// La documentación sale del fixture servido en local, así que el build de prueba no usa la red. +export default async function setup(project: TestProject): Promise<() => void> { const workDir = mkdtempSync(path.join(tmpdir(), 'fiscalapi-mcp-')); const dataDir = path.join(workDir, 'data'); - const build = spawnSync(process.execPath, ['scripts/build-data.mjs', fixturePath, '--out', dataDir], { - cwd: packageDir, - encoding: 'utf8', - }); - if (build.status !== 0) { + const docs = await serveDocs(readDocsCorpus()); + let build: { code: number | null; stderr: string }; + try { + build = await runBuildData([fixturePath, '--docs-origin', docs.origin, '--out', dataDir]); + } finally { + await docs.close(); + } + if (build.code !== 0) { rmSync(workDir, { recursive: true, force: true }); throw new Error(`build-data falló con el fixture:\n${build.stderr}`); } diff --git a/mcp/test/helpers.ts b/mcp/test/helpers.ts index d17b0dd..157c5b1 100644 --- a/mcp/test/helpers.ts +++ b/mcp/test/helpers.ts @@ -3,6 +3,7 @@ import type { CallToolResult } from '@modelcontextprotocol/client'; import { inject } from 'vitest'; import { OperationCatalog } from '../src/catalog.js'; +import { DocsIndex } from '../src/docs.js'; import { createServer } from '../src/server.js'; export function dataDirUrl(): URL { @@ -11,7 +12,7 @@ export function dataDirUrl(): URL { /** Cliente MCP en proceso conectado al servidor por el transporte en memoria del SDK. */ export async function connectClient(): Promise<{ client: Client; close: () => Promise }> { - const server = createServer(OperationCatalog.load(dataDirUrl()), '0.0.0-test'); + const server = createServer(OperationCatalog.load(dataDirUrl()), DocsIndex.load(dataDirUrl()), '0.0.0-test'); const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair(); const client = new Client({ name: 'fiscalapi-mcp-test', version: '1.0.0' }); await server.connect(serverTransport); diff --git a/mcp/test/offline.test.ts b/mcp/test/offline.test.ts index 4a64c8b..3ae06de 100644 --- a/mcp/test/offline.test.ts +++ b/mcp/test/offline.test.ts @@ -6,6 +6,8 @@ afterEach(() => { vi.unstubAllGlobals(); }); +// El cliente se conecta después de cortar la red: así cargar el catálogo y el índice de la +// documentación también queda cubierto, no solo las llamadas a las herramientas. it('arranca y responde sin red: nunca llama a fetch', async () => { const fetch = vi.fn(() => { throw new Error('Sin red: el MCP no debe descargar nada'); @@ -16,9 +18,12 @@ it('arranca y responde sin red: nunca llama a fetch', async () => { try { const { tools } = await client.listTools(); const operation = await callTool(client, 'obtener_operacion', { operationId: 'createInvoice' }); + const docs = await callTool(client, 'buscar_docs', { consulta: 'cancelar factura' }); - expect(tools).toHaveLength(2); + expect(tools).toHaveLength(3); expect(JSON.parse(textOf(operation)).operationId).toBe('createInvoice'); + expect(docs.isError).toBeFalsy(); + expect(textOf(docs)).toContain('https://docs.fiscalapi.com/'); expect(fetch).not.toHaveBeenCalled(); } finally { await close(); diff --git a/mcp/test/run-build-data.ts b/mcp/test/run-build-data.ts new file mode 100644 index 0000000..a2b3c68 --- /dev/null +++ b/mcp/test/run-build-data.ts @@ -0,0 +1,18 @@ +import { spawn } from 'node:child_process'; +import { fileURLToPath } from 'node:url'; + +const packageDir = fileURLToPath(new URL('../', import.meta.url)); + +/** + * Corre scripts/build-data.mjs en otro proceso. Es asíncrono a propósito: el servidor de la + * documentación de prueba vive en este proceso y con spawnSync no podría responder. + */ +export function runBuildData(args: readonly string[]): Promise<{ code: number | null; stderr: string }> { + return new Promise((resolve, reject) => { + const child = spawn(process.execPath, ['scripts/build-data.mjs', ...args], { cwd: packageDir }); + let stderr = ''; + child.stderr.on('data', (chunk) => (stderr += chunk)); + child.on('error', reject); + child.on('close', (code) => resolve({ code, stderr })); + }); +} diff --git a/mcp/test/search-regression.test.ts b/mcp/test/search-regression.test.ts new file mode 100644 index 0000000..cc0d138 --- /dev/null +++ b/mcp/test/search-regression.test.ts @@ -0,0 +1,56 @@ +import { describe, expect, it } from 'vitest'; + +import { DocsIndex } from '../src/docs.js'; +import { dataDirUrl } from './helpers.js'; + +const TOP = 3; +const MIN_HITS = 15; + +// Caso conocido: el primer resultado es la página general "Catálogos SAT" (catalogs-info.md), que +// remite al recurso de catálogos; el endpoint "Buscar en un catálogo" queda lejos porque su texto no +// dice "producto" ni "clave". No tiene su propio it: solo cuenta para el total. +const KNOWN_MISS = 'buscar clave de producto sat'; + +// Consultas reales de un agente con la sección que debe aparecer entre los primeros 3 resultados. +// "estatus ante el sat" acepta sat-validations/validar: la validación sat.cfdi.status ("Estatus del +// comprobante") responde esa consulta. +const CASES: ReadonlyArray = [ + ['cancelar una factura', /\/cancelar-factura\.md$/], + ['complemento de pago', /crear-factura-de-pago|errores-pagos/], + ['descargar xml', /obtener-xml-por-id|listar-xmls-por-id|descargar-xml-y-pdf/], + [KNOWN_MISS, /catalogs\/buscar-en-un-catalogo|productos-y-servicios/], + ['crear cliente receptor', /recipients\/crear-receptor/], + ['subir certificado csd', /subir-certificados/], + ['nómina empleado', /employee-data\/|crear-factura-de-nomina/], + ['webhook', /\/webhooks\//], + ['saldo de timbres', /\/stamps\//], + ['estatus ante el sat', /consultar-estado-de-factura|sat-validations\/validar\.md|los-siete-criterios/], + ['descarga masiva', /download-info|download-requests\//], + ['lista negra 69b', /sat-validations/], + ['factura global', /factura-global|global-invoice/], + ['enviar factura por correo', /enviar-factura-por-correo/], + ['CFDI40147', /errores-cfdi-4-0/], + ['objeto de impuesto', /objeto-imp/], +]; + +const docs = DocsIndex.load(dataDirUrl()); + +function topUrls(query: string): string[] { + return docs.search(query, TOP).map((result) => result.url); +} + +function hits(query: string, expected: RegExp): boolean { + return topUrls(query).some((url) => expected.test(url)); +} + +describe('buscar_docs: regresión de relevancia', () => { + it.each(CASES.filter(([query]) => query !== KNOWN_MISS))('"%s" trae la sección esperada en el top 3', (query, expected) => { + expect(topUrls(query)).toEqual(expect.arrayContaining([expect.stringMatching(expected)])); + }); + + it(`acierta al menos ${MIN_HITS} de ${CASES.length} consultas`, () => { + const total = CASES.filter(([query, expected]) => hits(query, expected)).length; + + expect(total).toBeGreaterThanOrEqual(MIN_HITS); + }); +}); diff --git a/mcp/test/tools.test.ts b/mcp/test/tools.test.ts index dd6c21f..cb09e44 100644 --- a/mcp/test/tools.test.ts +++ b/mcp/test/tools.test.ts @@ -33,10 +33,10 @@ function operationLines(text: string): string[] { } describe('tools/list', () => { - it('expone exactamente listar_operaciones y obtener_operacion con descripciones en español', async () => { + it('expone exactamente buscar_docs, listar_operaciones y obtener_operacion con descripciones en español', async () => { const { tools } = await client.listTools(); - expect(tools.map((tool) => tool.name).sort()).toEqual(['listar_operaciones', 'obtener_operacion']); + expect(tools.map((tool) => tool.name).sort()).toEqual(['buscar_docs', 'listar_operaciones', 'obtener_operacion']); const byName = Object.fromEntries(tools.map((tool) => [tool.name, tool])); expect(byName.listar_operaciones?.description).toContain('el siguiente paso es llamar a obtener_operacion'); expect(byName.obtener_operacion?.description).toContain('Usa el operationId exacto que da listar_operaciones'); diff --git a/mcp/tsconfig.json b/mcp/tsconfig.json index eaf00e3..402c028 100644 --- a/mcp/tsconfig.json +++ b/mcp/tsconfig.json @@ -9,6 +9,7 @@ "noUncheckedIndexedAccess": true, "noImplicitOverride": true, "verbatimModuleSyntax": true, + "erasableSyntaxOnly": true, "skipLibCheck": true, "rootDir": "src", "outDir": "dist" From d0fc64cd31d9c1c903ba570cf12091b22971fabf Mon Sep 17 00:00:00 2001 From: mendozagit Date: Thu, 15 Oct 2026 14:57:36 -0600 Subject: [PATCH 43/44] Tablero: MCP-02 con su hash --- docs/ai-integracion/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/ai-integracion/README.md b/docs/ai-integracion/README.md index 00fa320..7eab265 100644 --- a/docs/ai-integracion/README.md +++ b/docs/ai-integracion/README.md @@ -55,7 +55,7 @@ La siguiente tarea (global o de un área) es la primera fila `pendiente` (de esa | SK-08 | Escribir `fiscalapi-carta-porte` | skills | SK-02, MED-01 | hecho | fiscalapi-ai@9849def | | SK-09 | Escribir `fiscalapi-comercio-exterior` | skills | SK-02, MED-01 | hecho | fiscalapi-ai@8cb8c77 | | MCP-01 | Crear el paquete stdio con `listar_operaciones` y `obtener_operacion` | mcp | SK-01, BE-03 | hecho | fiscalapi-ai@6e0c755 | -| MCP-02 | Agregar `buscar_docs` con índice en español | mcp | MCP-01, DOC-01 | hecho | | +| MCP-02 | Agregar `buscar_docs` con índice en español | mcp | MCP-01, DOC-01 | hecho | fiscalapi-ai@3594080 | | EV-01 | Montar el harness de evals con los casos de ingreso y pagos | evals | SK-05, MCP-02 | pendiente | | | EV-02 | Agregar casos de nómina, carta porte y comercio exterior | evals | EV-01, SK-09 | pendiente | | | EV-03 | Correr las 3 variantes y reportar | evals | EV-02 | pendiente | | From 751dd1b02b226a0463276f5d9389372497649854 Mon Sep 17 00:00:00 2001 From: mendozagit Date: Thu, 15 Oct 2026 14:59:52 -0600 Subject: [PATCH 44/44] Tablero: loop pausado por el usuario; EV-01 sigue pendiente --- docs/ai-integracion/evals.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/ai-integracion/evals.md b/docs/ai-integracion/evals.md index b8952a0..6019ed5 100644 --- a/docs/ai-integracion/evals.md +++ b/docs/ai-integracion/evals.md @@ -22,7 +22,7 @@ Todo va en fiscalapi-ai, en `evals/`, en la rama `feat/ai-integracion`. Las eval - Un caso con una skill rota a propósito baja su tasa de éxito → verificar: corrida con un enlace o una regla falsa plantada. **Pruebas focalizadas:** ninguna (no aplica). El harness es la prueba. **Fuera de alcance:** correr en CI (D-11). -**Notas:** +**Notas:** pausada el 2026-10-15 por el usuario antes de empezar: sin código ni decisiones. Al retomar, D-11 sigue sin fijar el runner. MCP-02 quedó en `fiscalapi-ai@3594080`, y las docs locales de verificación se sirven con `npx next start -p 3999` en fapi-docs-ai. ### EV-02 — Agregar casos de nómina, carta porte y comercio exterior **Objetivo:** cubrir con evals todos los casos CFDI del feature.