Files
balam/planeacion/Investigacion-API-Jira.md
T
JohannVelazquez cbb29086cc Documenta las definiciones Jira→BIND y su validación por API
Consolida el disparador, los huecos de datos y el protocolo de prueba para continuar el desarrollo con decisiones trazables.
2026-08-11 20:23:55 -06:00

412 lines
33 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Investigación de la API de Jira — qué probar exactamente
**Fecha:** 30-jul-2026 · **Actualizado:** 10-ago-2026
**Propósito:** lista cerrada de pruebas y preguntas para habilitar la integración **Jira → cotización BIND** (estimada en ~10 h, [REGISTRO #51](../bitacora/REGISTRO.md)). Documento de trabajo interno; sirve también como briefing para el agente/chat especializado en la API de Jira.
**Estado validado (30-jul):**
- Sitio: `https://balam-jsm-temp.atlassian.net`. El token regenerado de Pedro, usado con Basic auth y `pedro.ayala@balamtalentoestrategico.com`, respondió **200** a `GET /rest/api/3/myself`.
- Facturación es el proyecto JSM **`FAC`** (`projectId: 10034`, `serviceDeskId: 35`), de tipo `service_desk` y estilo `classic` (company-managed).
- La búsqueda de FAC devolvió **66 tickets**. La API vigente es `GET /rest/api/3/search/jql`, con paginación por `nextPageToken`; el endpoint clásico `/rest/api/3/search` responde **410 Gone**.
- Estatus confirmados: `Open`, `En proceso de facturación`, `En espera por colaborador`, `En validación nacional`, `En validación extranjera`, `Facturado` y `Cancelado`. No se observó un estatus llamado “Resuelto”.
- Límites de consumo ya aclarados por Pedro (sin tope mensual; 3 límites de velocidad; telemetría solo en headers `X-RateLimit-*`).
---
## ⭐ Decisiones de negocio recibidas (correo del 7-ago) y su verificación por API (10-ago)
Balam contestó las tres preguntas del 30-jul. Arturo respondió el **4-ago** (en verde sobre el correo de Noé), Noé lo reenvió el **7-ago**. Fuente: `RV_ Seguimiento_ medición de consumo API de Jira y accesos.eml`.
| Decisión de Balam | Quién / cuándo | Verificación por API (10-ago) |
|---|---|---|
| La cotización BIND **se genera al entrar a `En proceso de facturación`** | Arturo, 4-ago | ✅ Estatus y transición `Iniciar facturación` ya inventariados (Bloque 3) |
| **Todo se tramita como facturación normal**; el caso recurrente espera a que se explore el módulo de Proyectos de BIND | Arturo, 4-ago | ⚠️ El campo `Facturación recurrente` sigue siendo **obligatorio** y se está llenando (FAC-100: `Si`, periodo `12`) |
| Se observan **todos los request types** de la bandeja de facturación, no solo `Facturación adicional` | Arturo, 4-ago | 🔴 **10 de 69 tickets no tienen request type alguno** y ninguno trae campos de facturación (ver Bloque 1) |
| Monto y conceptos van **como campos de Jira**, no del adjunto; **omitir el campo de recurrencia** | Arturo, 4-ago | ⚠️ `Monto sin IVA` ya existe y funciona; **`conceptos` no existe como campo en todo el sitio** (ver Bloque 2) |
| Las pruebas de escritura son **prueba acompañada sobre un ticket**, no proyecto `FACTEST` | Noé, 7-ago | Se descarta el sandbox en la instancia de Balam → aplica el protocolo del Bloque 6 sin excepción |
**Lo que sigue abierto tras estas respuestas:** de dónde salen los **conceptos/partidas** de la cotización, qué hacer con los tickets sin campos estructurados, si nacional/extranjero cambia la cotización o solo el paquete de salida, **PUE vs PPD**, la fecha de la prueba acompañada y la migración a cuenta de servicio.
---
## Bloque 0 — Acceso autenticado ✅
El 401 del 29-jul quedó explicado por la combinación de token previo y datos de conexión incompletos. Pedro confirmó el correo, regeneró el token y compartió el dominio del sitio.
**Configuración que funcionó:**
```text
GET https://balam-jsm-temp.atlassian.net/rest/api/3/myself
Authorization: Basic base64(pedro.ayala@balamtalentoestrategico.com:<token>)
Accept: application/json
```
El resultado fue **HTTP 200**. Por tanto, para esta integración se usará la API directa del sitio con Basic auth; no hay evidencia de que se requiera una ruta `api.atlassian.com/ex/jira/{cloudId}`.
**Prueba canónica de auth (la primera que debe correrse siempre):**
```
GET /rest/api/3/myself
```
Interpretación de la respuesta:
- **200** → auth OK; además devuelve `accountId` (identidad con la que la plataforma va a escribir — dato de gobierno, ver Bloque 7).
- **401** → credencial inválida/inactiva o esquema mal armado.
- **403** → credencial VÁLIDA pero sin permiso → problema de permisos, no de token. Distinguir 401 de 403 es el diagnóstico más barato que existe.
> ⚠️ **Registrar siempre los headers de la respuesta 401 completos.** Ahí vive la pista (`WWW-Authenticate`, `X-Seraph-LoginReason`, `X-Failure-Category`).
---
## Bloque 1 — Topología: Jira Service Management confirmado ✅
FAC está confirmado como **Jira Service Management (JSM)**. Por ello la integración requiere dos familias de API:
| API | Para qué | Cuándo usarla |
|---|---|---|
| `/rest/api/3/...` | Issues genéricos: JQL, campos, transiciones, adjuntos, changelog | Lectura/sync, transiciones, trazabilidad |
| `/rest/servicedeskapi/...` | Requests de portal: request types, campos del formulario, aprobaciones, SLA, comentarios públicos vs internos | Crear tickets como los crea un humano; leer aprobaciones y SLA |
**Resultados:**
```
GET /rest/api/3/project/FAC
```
`projectTypeKey: service_desk`, `style: classic`, `id: 10034`.
```
GET /rest/servicedeskapi/servicedesk
```
→ FAC corresponde a `serviceDeskId: 35`.
Request types **publicados en el portal** (`GET /rest/servicedeskapi/servicedesk/35/requesttype`, `isLastPage: true`):
| requestTypeId | Nombre | Campos obligatorios |
|---:|---|---:|
| 84 | Bajas | 7 |
| 83 | Facturación adicional | 10 |
| 389 | Automatización | 1 (solo `summary`) |
| 12 | Facturación | 1 (solo `summary`) |
Contratos verificados el 10-ago con `/requesttype/{id}/field`:
| Request type | Hallazgo |
|---|---|
| **Bajas** (`84`) | Empresa origen, Summary, adjunto de Cálculo/VoBo de Finiquito, Nombre del Cliente, Nombre del Colaborador, Fecha de la Baja y Motivo de la Baja. |
| **Facturación adicional** (`83`) | Empresa origen, `MES / DESCRIPCIÓN / CLIENTE` (es el `summary`), Cliente Nuevo, Nombre del Cliente, adjunto de cotización/CSF, Tipo de Moneda, Días de Crédito, Facturación recurrente, Periodo de recurrencia y **`Monto sin IVA`**. Es el contrato más completo. ⚠️ **Cambió desde el 30-jul:** se agregó `Monto sin IVA` y desapareció `Periodo de Incidencias`. |
| **Automatización** (`389`) | Solo exige Summary. |
| **Facturación** (`12`) | Solo exige Summary. |
### 🔴 “Todos los request types” incluye tickets sin request type
La bandeja de FAC muestra en el filtro de la UI **6 de 6** opciones: las 4 de arriba más **`Empty`** y **`Emailed request`**. Ninguna de esas dos aparece en `servicedeskapi` porque no están publicadas en el portal. El conteo real sobre los **69 tickets** de FAC (10-ago):
| Request type | Tickets | c/Monto | c/Moneda | c/Cliente | c/Adjunto |
|---|---:|---:|---:|---:|---:|
| 83 — Facturación adicional | 48 | **2** | 48 | 48 | 48 |
| **(sin request type)** | **10** | 0 | 0 | 0 | 3 |
| 84 — Bajas | 6 | 0 | 0 | 6 | 6 |
| 389 — Automatización | 4 | 0 | 0 | 0 | 4 |
| 12 — Facturación | 1 | 0 | 0 | 0 | 1 |
Los **10 tickets sin request type** tienen `creator` y `reporter` = **`Automation for Jira`**: nacen de una regla de Automation desde otras bandejas (HH, SA, IN, RH), no del portal. Y **no son ruido**: `FAC-91` es uno de ellos, está vivo en `En validación nacional` y su summary es `[FACTURA COMPLETA - Staff Augmentation] Axians - Incident Manager — RH-36`. Es decir, **hay facturación real entrando por una vía que no tiene ni un solo campo estructurado** — los datos viajan codificados en el `summary` y en la `description` (ADF), más 2 adjuntos.
`Emailed request` hoy tiene 0 tickets, pero existe en la configuración: cualquier correo a la bandeja aterriza ahí, también sin campos.
**Implicación:** el acuerdo literal “todos los request types” significa que **~14% de los tickets no puede generar una cotización automática** con el diseño de campos estructurados. Hay dos salidas y conviene que Balam elija explícitamente: (a) acotar la automatización a los request types que sí traen campos y dejar los demás en tratamiento manual, o (b) que Automation for Jira propague los campos al crear el ticket en FAC. La opción (b) es trabajo del lado de Balam, no de la plataforma.
---
## Bloque 2 — Modelo de datos del ticket FAC (el mapeo) 🔴
Esto es lo que decide si la integración es de 10 h o de 25. **La pregunta de fondo: ¿los datos de facturación vienen en campos estructurados o en texto libre?** Si vienen en texto libre, hay que negociar campos nuevos con Pedro o meter parsing frágil.
**Lo que la plataforma necesita de cada ticket para armar una cotización en BIND:**
| Dato requerido | Por qué | Qué verificar |
|---|---|---|
| Cliente | Match contra `Clients` de BIND (por RFC o nombre normalizado) | ¿Campo custom? ¿Lista desplegable? ¿Texto libre? ¿Trae RFC? |
| Monto y moneda | Cotización BIND; la cartera **jamás mezcla monedas** | ¿Campo numérico? ¿Viene la moneda separada o embebida en el texto? |
| Nacional vs internacional | Determina PDF+XML (nacional) vs solo PDF (extranjero) | ¿Se deduce del cliente o hay campo/etiqueta? |
| Concepto / descripción | Línea de la cotización | Ver formato (ver ⚠️ ADF abajo) |
| Tipo de solicitud | adicional / recurrente / baja / headhunting / staff augmentation | ¿Request type, issue type, o campo? |
| Folio `FAC-nnn` | Trazabilidad ticket ↔ cotización ↔ factura (persistir en el modelo) | Confirmar formato y estabilidad (ver Bloque 5, ⚠️ moves) |
| Solicitante / área | Auditoría | `reporter` vs `requester` de JSM (no son lo mismo) |
| Adjuntos | Constancia fiscal, Excel de horas (CEMEX), estado de cuenta | Cómo se descargan y con qué auth |
**Resultados de lectura:**
```
GET /rest/api/3/issue/FAC-98?expand=names,renderedFields
```
→ caso real de tipo Bajas: cliente Axians, colaborador, fecha y motivo de baja en campos estructurados; una evidencia adjunta; sin descripción. Los valores de texto enriquecido regresan como ADF.
### Mapeo de campos confirmado (10-ago)
| Campo de Jira | `fieldId` | Tipo | Valores | → BIND |
|---|---|---|---|---|
| Empresa origen | `customfield_11423` | option | `Balam`, `RegioTurk`, `Helmstone` | Emisor (multi-empresa) |
| MES / DESCRIPCIÓN / CLIENTE | `summary` | string | texto libre | Referencia / parseo de respaldo |
| Cliente Nuevo | `customfield_10052` | option | `Si`, `No` | Decide si hay que dar de alta el cliente |
| Nombre del Cliente | `customfield_10202` | **ADF** (textarea) | texto enriquecido | Match contra `Clients` de BIND |
| Tipo de Moneda | `customfield_10053` | option | `MXN`, `USD`, `OTRO` | Moneda de la cotización |
| Días de Crédito | `customfield_10054` | option | `30`, `45`, `NA` | Condiciones de pago |
| Facturación recurrente | `customfield_10552` | option | `Si`, `No` | Fuera de alcance por ahora (Arturo, 4-ago) |
| Periodo de recurrencia | `customfield_10553` | string | texto | Fuera de alcance por ahora |
| **Monto sin IVA** | **`customfield_11556`** | **number/float** | — | **Total de la cotización** |
| Approvers | `customfield_10003` | array/user | — | Gobierno (ver Bloque 3) |
⚠️ **`Nombre del Cliente` es ADF, no string.** El match contra BIND tiene que extraer texto plano de un documento estructurado, no leer un campo de texto. Nada garantiza que el nombre escrito a mano coincida con la razón social en BIND; **el campo no trae RFC**, que sería la llave confiable.
### 🔴 Dos huecos que el acuerdo del 4-ago no cierra
1. **`Monto sin IVA` existe pero está prácticamente vacío.** Solo **2 de 69** tickets lo tienen: `FAC-100` y `FAC-101`, ambos creados el **4-ago** — el mismo día en que Arturo contestó el correo. El campo se agregó en ese momento; los 67 tickets anteriores lo tienen en `null`. Es buena noticia (el acuerdo ya se implementó) con dos consecuencias: no hay histórico para validar el mapeo contra facturas reales, y la obligatoriedad solo aplica al crear por portal — un ticket nacido de Automation entra con `null` igual.
2. **`conceptos` / partidas no existe como campo en NINGUNA parte del sitio.** `GET /rest/api/3/field` filtrado por monto/concepto/partida/importe/precio/cantidad/RFC devuelve únicamente:
- `customfield_11556``Monto sin IVA` (en uso)
- `customfield_11522``Monto con IVA` (existe, **no está en el formulario 83**)
- `customfield_10031``Total forms` (de JSM, no es de facturación)
Con un solo monto agregado **no se pueden armar partidas**: la cotización BIND queda de una sola línea con la descripción del `summary`. Eso puede ser aceptable como decisión, pero **hay que tomarla explícitamente**, y no cubre el caso ACUNTIA. Que exista `Monto con IVA` sin usar además abre la pregunta de si el IVA lo calcula BIND o viene dado.
**El caso ACUNTIA ya tiene evidencia:** `FAC-100` es exactamente eso — summary `Julio/CONSULTORIA Y SERVICIOS (Eduardo Fiallo CEMEXUSA)/ACUNTIA`, `Helmstone`, `USD`, 45 días, `Monto sin IVA = 7594.00`, con 2 adjuntos. **Un solo monto para un ticket que históricamente representa 4048 facturas** ([REGISTRO #55](../bitacora/REGISTRO.md) punto 6). El supuesto “un ticket → una cotización” necesita confirmarse contra este caso antes de codificar.
> ⚠️ **ADF (Atlassian Document Format).** En Jira Cloud v3, `description` y los comentarios **no son texto plano ni wiki markup: son JSON estructurado**. Hay que decidir ya si se parsea ADF o se pide `expand=renderedFields` (HTML). Y al **escribir** comentarios/descripciones hay que **construir ADF válido**, no mandar un string. Es la trampa que más tiempo cuesta si se descubre tarde.
> ⚠️ **Adjuntos:** confirmar si `content` (URL de descarga) requiere el mismo Basic auth y si redirige. Y aplica la misma regla que con BIND: los adjuntos traen **datos reales de clientes** (constancias, estados de cuenta) — no van a disco del repo ni a documentos.
---
## Bloque 3 — Workflow, estatus y transiciones (el disparador) ✅🟡
**Disparador confirmado por Arturo el 4-ago: la cotización BIND se crea al entrar a `En proceso de facturación`.** El inventario técnico ya estaba levantado, así que la regla de negocio queda cerrada.
**Inventario confirmado:**
```
GET /rest/api/3/project/FAC/statuses → estatus por tipo de issue, con id y statusCategory
```
| Estatus | ID | Categoría |
|---|---:|---|
| Open | 1 | To Do |
| En proceso de facturación | 10305 | In Progress |
| Facturado | 10306 | Done |
| En espera por colaborador | 10339 | In Progress |
| Cancelado | 10372 | Done |
| En validación nacional | 10635 | In Progress |
| En validación extranjera | 10669 | In Progress |
**Transiciones de lectura confirmadas** (son contextuales al estatus actual):
| Ticket / estatus actual | transitionId | Destino |
|---|---:|---|
| FAC-98 / En proceso de facturación | 6 | En espera por colaborador |
| FAC-98 / En proceso de facturación | 8 | En validación nacional |
| FAC-98 / En proceso de facturación | 9 | En validación extranjera |
| FAC-98 / En proceso de facturación | 2 | Cancelado |
| FAC-91 / En validación nacional | 2 | Cancelado |
| FAC-89 / Cancelado | 12 | Open |
`FAC-97` ya está Facturado y no expone transiciones. Las respuestas consultadas no devolvieron campos obligatorios para las transiciones anteriores. Aun así, no se ejecutará ninguna transición sin la sesión acompañada.
**Diagrama de workflow compartido por Balam:** confirma la ruta completa y los nombres de transición:
```text
Open --[Iniciar facturación]--> En proceso de facturación
En proceso de facturación --[Pausar actividad]--> En espera por colaborador
En espera por colaborador --[Retomar actividad]--> En proceso de facturación
En proceso de facturación --[Validar documentación nacional]--> En validación nacional
En proceso de facturación --[Validar documentación extranjera]--> En validación extranjera
En validación nacional o extranjera --[Facturación completa]--> Facturado
En validación nacional o extranjera --[Rechazar actividad]--> En proceso de facturación
```
El diagrama también contempla cancelación desde los estados de trabajo y reapertura; la API confirmó al menos `Cancelado --[Reabrir Ticket]--> Open` en FAC-89. **No existe un estado “Resuelto” en el workflow.** Con la respuesta del 4-ago, la regla queda: **`Iniciar facturación` (Open → En proceso de facturación) dispara la cotización**; `Facturado` confirma que la factura se emitió.
### 🔴 Hallazgo crítico: el estatus disparador dura minutos, no horas
Recorrido real de `FAC-100` (45 ago), desde su changelog:
| Hora | Transición | Autor |
|---|---|---|
| 05-ago 11:51:25 | `Open`**`En proceso de facturación`** | Arturo Rosas |
| 05-ago 11:54:09 | → `En validación extranjera` | Arturo Rosas |
| 05-ago 12:16:37 | → `Facturado` | **Araceli Sánchez** |
**El ticket estuvo 2 minutos 44 segundos en el estatus disparador**, y 25 minutos de punta a punta. Hoy solo **1 de los 69 tickets** está parado en `En proceso de facturación` (65 ya están `Facturado`).
**Consecuencia de diseño, no negociable:** un poller de 15 min que pregunte *“¿qué tickets están hoy en `En proceso de facturación`?”* **se pierde la mayoría de los disparos**. La detección **tiene que leer el changelog** y buscar el *cruce* de estado dentro de la ventana, exactamente como se resolvió la detección de pagos en BIND (ADR-004). Esto confirma y vuelve obligatorio lo que antes era una preferencia de arquitectura, y refuerza el caso de los webhooks (Bloque 4) para reducir la latencia.
### ⭐ Hallazgo nuevo: el workflow tiene aprobaciones de JSM
Ni FAC-98 ni el diagrama lo mostraban. Tanto `FAC-100` como `FAC-91` traen:
- `customfield_10003` **Approvers** = `Araceli Sánchez`
- `customfield_10025` **Approvals** = el estatus donde vive la aprobación (`En validación extranjera` / `En validación nacional`)
Es decir, **el paso a `Facturado` pasa por una aprobación formal de Araceli**, no por una transición simple. Implicaciones: (1) la plataforma **no debe intentar aprobar** — eso es decisión humana; (2) si algún día tuviera que transicionar a `Facturado`, la vía correcta es `/rest/servicedeskapi/request/{id}/approval`, no `/transitions`; (3) la aprobación es un buen punto de trazabilidad para saber quién autorizó cada factura.
⚠️ **Trampas:**
1. ~~**`Facturado` como estatus ≠ campo `resolution`.**~~ **Resuelto (10-ago):** en `FAC-100`, `status = Facturado` **y** `resolution = Done` **y** `resolutiondate` poblada, todo consistente. Ambas señales sirven; se usará el cruce de estatus en el changelog por coherencia con el resto del diseño.
2. **Las transiciones son contextuales**: `/transitions` solo devuelve las salidas del estado actual, y pueden tener **pantallas con campos obligatorios**. Probar con `expand=transitions.fields` para saber si transicionar por API exige llenar algo.
3. **`issuetype` de FAC es `admon`**, no “Service Request”. Cualquier JQL o creación por API debe usar ese nombre.
**Changelog = la fuente de la detección.** `expand=changelog` da cada cambio de estatus con timestamp y autor. Verificado en FAC-100 (`total: 6`) y FAC-91 (`total: 8`): vienen completos y sin paginar en tickets de este tamaño. Para tickets con mucho historial hay que usar `/rest/api/3/issue/{key}/changelog`, que sí pagina.
---
## Bloque 4 — Estrategia de sincronización: polling vs webhooks 🟡
El scheduler de la plataforma ya existe (`BackgroundService` + `PeriodicTimer` a 15 min). La pregunta es qué le pega a Jira.
**a) Búsqueda por JQL — validado.**
El endpoint clásico `GET /rest/api/3/search` respondió **410 Gone**. La integración debe usar `GET /rest/api/3/search/jql`, con `nextPageToken`. La lectura de FAC recuperó 66 tickets con ese mecanismo.
JQL de delta a validar:
```
project = FAC AND updated >= "-20m" ORDER BY updated ASC
```
⚠️ **`updated` en JQL tiene granularidad de minuto**, no de segundo → el checkpoint necesita **ventana de solape** (igual que el re-barrido de facturas abiertas de BIND) o se pierden tickets en el borde.
Validar también: `fields=` para pedir solo lo necesario (menos payload, menos puntos), y el tope real de `maxResults`.
**b) Webhooks.** Tres caminos, con dueños distintos:
| Opción | Quién la configura | Nota |
|---|---|---|
| Webhook de sitio (admin) | **Pedro** (requiere admin de Jira) | El más limpio; exige endpoint público |
| **Regla de Jira Automation** con "Send web request" | **Pedro, sin escribir código** | La más realista a corto plazo; se puede acotar a FAC y a un estatus |
| Webhook dinámico vía OAuth app | Desarrollo | **Expiran a los 30 días**, hay que refrescarlos |
**Recomendación:** **arrancar con polling** y dejar el webhook para después. Ningún webhook sirve hasta que exista un endpoint público, y Azure sigue pendiente del lado de Balam. El polling además es reversible y no depende de que Pedro toque su instancia.
**c) Rate limits — headers verificados ✅ (10-ago).** La respuesta de `/rest/api/3/search/jql` sí los emite:
```text
X-RateLimit-Limit: 350
X-RateLimit-Remaining: 348
RateLimit-Policy: "jira-burst-based";q=100;w=1
RateLimit: "jira-burst-based";r=348;t=1
```
Tres notas: (1) la telemetría existe y el autorregulado por headers es viable, como se le dijo a Erika el 29-jul; (2) el límite observado es **350**, no los 100/s que mencionó Pedro — hay que leer el header y no hardcodear el número; (3) aparecen también los headers estándar `RateLimit-*` (RFC) además de los `X-RateLimit-*`, así que el cliente debe tolerar ambos. `X-RateLimit-NearLimit` no apareció porque no se llegó al 20% del umbral. No se provocó un 429 deliberadamente contra producción.
---
## Bloque 5 — La bandeja padre: de dónde nacen realmente los tickets 🟡
Pedro dijo que procesos que **se cierran en RH o Administración General caen en Facturación**. Técnicamente eso puede ser cuatro cosas distintas, y cada una se sincroniza diferente:
| Mecanismo | Cómo detectarlo | Implicación |
|---|---|---|
| **Issue link** (RH-45 relacionado con FAC-89) | `issuelinks` en el issue | Escuchar FAC basta; el link da contexto |
| **Clon / creación por Automation** | `changelog` + campo `creator` (usuario de automation) | Escuchar FAC basta |
| **Subtarea / hijo** | `parent` / `subtasks` | Relevante para ACUNTIA (1 ticket ↔ 40 facturas) |
| **Move entre proyectos** | `changelog` con cambio del campo `project` | 🔴 **La llave del ticket CAMBIA** (RH-45 → FAC-89). Cualquier folio persistido se rompe |
**Resultado de la prueba (10-ago): es creación por Automation, no move. ✅**
Los 10 tickets sin request type tienen `creator` = `reporter` = **`Automation for Jira`**, y sus changelogs (revisados en FAC-91) **no muestran cambios de `project` ni de `key`**. El patrón es: la regla de Automation **crea** un ticket nuevo en FAC y lo **liga** al original con `issuelinks`. Ejemplos de summary, que llevan el origen embebido:
```text
FAC-91 [FACTURA COMPLETA - Staff Augmentation] Axians - Incident Manager — RH-36
FAC-1 Seguimiento del Ticket Cerrado: HH-1 - Prueba #1
FAC-28 Seguimiento del Ticket con pago anticipado: HH-8 - TLE
```
Se ven tres bandejas de origen: **HH** (headhunting), **SA** (staff augmentation), **IN**, y **RH**. Dos patrones de regla: `Seguimiento del Ticket Cerrado:` y `Seguimiento del Ticket con pago anticipado:` — el segundo probablemente implique PUE y toca la pregunta pendiente de PUE/PPD.
**Conclusiones para el modelo de datos:**
- **Escuchar FAC basta** para detectar el disparo; el origen se obtiene de `issuelinks` sin salir del proyecto.
- No hay evidencia de moves, así que la llave `FAC-nnn` **parece** estable. Aun así conviene **persistir el `issue.id` numérico** (FAC-100 = `17308`, FAC-91 = `14348`): es inmutable por diseño y el costo de guardarlo es cero.
- ⚠️ Estos tickets son la vía sin campos estructurados del Bloque 1. Si la automatización debe cubrirlos, **la regla de Automation de Balam tendría que propagar los campos** al crear el ticket en FAC.
---
## Bloque 6 — Escritura (CRUD): qué probar, cómo aprobarlo y dónde 🔴
**Confirmado por Noé el 7-ago: prueba acompañada sobre un ticket acordado. No habrá proyecto `FACTEST`.** Eso cierra la pregunta pero **deja el riesgo intacto**: se escribirá en producción, y el token tiene permisos para crear, editar, transicionar, comentar, adjuntar e incluso borrar tickets en FAC. El protocolo de abajo pasa de recomendación a requisito, y el sandbox propio (ver recuadro al final del bloque) pasa de opcional a necesario para desarrollar sin tocar Balam.
**Protocolo obligatorio para cualquier prueba de escritura:**
1. Preparar un script idempotente y de una sola operación; sin bucles, sin búsquedas masivas y sin credenciales embebidas.
2. Compartir el script y el payload de ejemplo con Pedro/Arturo antes de la sesión; documentar endpoint, ticket destino, efecto esperado y reversión.
3. Acordar el ticket de prueba y una ventana de ejecución. Preferir un proyecto sandbox (`FACTEST`) o un ticket creado expresamente para la prueba.
4. Ejecutarlo acompañado, registrar el `HTTP status`, el `issue.id`/`key` y verificar el resultado por API.
5. No probar `DELETE`; no es una capacidad necesaria para la plataforma y no es reversible en producción.
**Operaciones a validar, en este orden (de menos a más invasivo):**
1. **Comentar**`POST /rest/api/3/issue/{key}/comment` (cuerpo en ADF).
⚠️ En JSM hay **comentarios públicos (los ve el cliente) vs internos**: `POST /rest/servicedeskapi/request/{id}/comment` con `public: true|false`. **La plataforma debe escribir SIEMPRE internos.** Publicar por error un comentario visible al cliente es el error más caro y menos reversible de este bloque.
2. **Adjuntar**`POST /rest/api/3/issue/{key}/attachments`.
⚠️ Exige el header `X-Atlassian-Token: no-check` y `multipart/form-data`. Sin ese header falla con un error que no explica nada. Es la vía para dejar el PDF/XML como evidencia en el ticket.
3. **Transicionar**`POST /rest/api/3/issue/{key}/transitions` con el `transition.id` del Bloque 3.
4. **Crear** — decisión real de diseño: `POST /rest/api/3/issue` (crudo, se salta el request type y el ticket queda "raro" en el portal) vs `POST /rest/servicedeskapi/request` (respeta request type y se ve como uno normal). **Si es JSM, la segunda es la correcta.**
5. **Editar**`PUT /rest/api/3/issue/{key}`.
6. **Borrar**`DELETE /rest/api/3/issue/{key}`. Requiere permiso de admin de proyecto. **Probablemente ni se tenga ni convenga tenerlo**; en producción el borrado no debe ser una capacidad de la plataforma.
> 🔴 **Dónde probar.**
> ~~Pedir a Pedro un proyecto de pruebas `FACTEST`~~ → **descartado por Noé el 7-ago:** será prueba acompañada sobre un ticket. Queda entonces una sola vía de mitigación:
>
> **Crear un sitio propio y gratuito de Jira Cloud** (plan Free, hasta 10 usuarios) con un proyecto JSM que replique el workflow de FAC. Sirve para desarrollar el cliente, el parser de ADF, la paginación, las aprobaciones y las transiciones **sin tocar nada de Balam** ni depender de que respondan. Es el sandbox que BIND nunca tuvo. Contra producción solo se corre lectura y, al final, la única prueba acompañada.
>
> Con el `FACTEST` descartado, esto ya no es una alternativa: es el único lugar donde se puede equivocar sin costo.
---
## Bloque 7 — Gobierno y seguridad 🟡
-~~**`propuesta/token-jira.txt` NO está en `.gitignore`**~~**Resuelto:** ignorado en [`.gitignore:9`](../.gitignore). Sigue pendiente mover el token a user-secrets / Key Vault y borrarlo de disco. Mismo pendiente que arrastra `bind_token_api.txt`.
- **Cuenta de servicio vs cuenta personal de Pedro** (ya planteado en el borrador de correo del 29-jul): hoy todo lo que la plataforma escriba en Jira queda firmado con el nombre de Pedro, y una rotación suya deja la integración muerta. Verificar con `GET /rest/api/3/myself` con qué identidad se está actuando. **Sin respuesta al 10-ago.**
- 🔴 **Alcance de permisos:** validado con `GET /rest/api/3/mypermissions` para FAC. El token puede navegar, crear, editar, transicionar, comentar, adjuntar y borrar tickets. Es un alcance excesivo para producción; la integración no debe implementar borrado y conviene migrar a una cuenta de servicio con permisos mínimos.
- **Vigencia 27-jul-2027** → dejar registrado el vencimiento y el plan de rotación desde ahora.
- **Datos de clientes en adjuntos y descripciones** — misma regla que BIND: nunca a disco del repo ni a documentos compartidos.
---
## Bloque 8 — Preguntas para Pedro / Balam (no se resuelven con la API)
**Cerradas con el correo del 7-ago:**
1. ~~**¿Puede crear un proyecto de pruebas `FACTEST`?**~~**No.** Prueba acompañada sobre un ticket (Noé, 7-ago).
2. ~~**¿Debe observarse `Facturación adicional` (83), `Facturación` (12) u otro?**~~**Todos los request types** de la bandeja (Arturo, 4-ago). ⚠️ Con la salvedad del Bloque 1: 10 tickets no tienen request type ni campos.
3. ~~**¿Cuál estatus dispara la cotización?**~~**`En proceso de facturación`** (Arturo, 4-ago).
4. ~~**¿Monto del adjunto o como campo?**~~**Campo de Jira.** Ya implementado (`customfield_11556`), omitiendo recurrencia (Arturo, 4-ago).
**Abiertas:**
5. 🔴 **¿De dónde salen los CONCEPTOS/partidas?** El acuerdo resolvió el monto pero no los conceptos, y **no existe campo para ellos en el sitio**. ¿Cotización de una sola línea con el `summary` como descripción, o se agrega un campo?
6. 🔴 **¿Qué pasa con los tickets creados por Automation for Jira** (10 de 69, sin ningún campo estructurado, incluido `FAC-91` que está vivo)? ¿Se acota la automatización o la regla de Automation propaga los campos?
7. 🔴 **ACUNTIA / `FAC-100`:** ¿un ticket con `Monto sin IVA` único debe generar **una** cotización, o sigue siendo el caso de 4048 facturas?
8. **¿Nacional vs extranjero cambia la cotización** o solo el paquete de salida (PDF+XML vs PDF)? Hay dos estatus de validación distintos y una aprobación por cada rama.
9. **¿Quién puede crear webhooks o reglas de Automation** apuntando a una URL nuestra, y bajo qué proceso? Gana urgencia: el estatus disparador dura minutos (Bloque 3).
10. **PUE vs PPD** — la pregunta que no se ha alcanzado a hacer en cuatro sesiones ([REGISTRO #52](../bitacora/REGISTRO.md)). Los tickets `Seguimiento del Ticket con pago anticipado:` sugieren que sí hay casos PUE reales.
11. **Cuenta de servicio** en lugar de la cuenta personal de Pedro, y fecha para la prueba acompañada.
---
## Orden de ataque sugerido
| # | Acción | Bloqueado por |
|---|---|---|
| 1 | ✅ Blindar el token (`.gitignore`) — falta secrets/Key Vault | — |
| 2 | ✅ Mapeo de campos y contratos de request type verificados (10-ago) | — |
| 3 | Implementar lectura con `/search/jql`, cursor y ventana de solape | — |
| 4 | **Detección por changelog del cruce a `En proceso de facturación`** | — (ya desbloqueado por el acuerdo del 4-ago) |
| 5 | Parser de ADF para `Nombre del Cliente` + match contra `Clients` de BIND | ⚠️ Sin RFC; definir tolerancia del match |
| 6 | Armado de la cotización BIND | 🔴 Definición de conceptos/partidas (pregunta 5) |
| 7 | Levantar un sandbox Jira propio (plan Free) | — (ya no depende de Balam) |
| 8 | Preparar y compartir el script de escritura mínimo para revisión | — |
| 9 | Ejecutar una única prueba de escritura acompañada | Fecha de Balam + script revisado + ticket acordado |
**Lo que puede avanzar ya:** el cliente de lectura, manejo de ADF, paginación por cursor, mapeo de campos, detección incremental por changelog y el sandbox propio. **Lo único que sigue bloqueado es el armado final de la cotización**, por la definición de conceptos. La escritura queda fuera del flujo automático hasta la sesión acompañada.
---
## Briefing corto para desarrollo / revisión técnica
> Contexto validado: Jira Cloud JSM en `balam-jsm-temp.atlassian.net`; proyecto FAC company-managed (`projectId: 10034`, `serviceDeskId: 35`). La autenticación directa con Basic `email:token` funciona. El token tiene permisos de escritura amplios, pero **no se debe ejecutar ningún POST, PUT o DELETE fuera de una prueba acompañada y aprobada**.
>
> Para la capa de lectura, usar `GET /rest/api/3/search/jql`, paginar por `nextPageToken` y aplicar una ventana de solape al checkpoint de `updated`. No usar `/rest/api/3/search`: responde 410.
>
> Para JSM, usar `/rest/servicedeskapi` al consultar request types y su contrato de campos. FAC publica 4 request types: Bajas (84), Facturación adicional (83), Automatización (389) y Facturación (12); los cuatro contratos están verificados. **Facturación adicional (83) es el único con datos de facturación** e incluye `Monto sin IVA` (`customfield_11556`, number). **No existe campo de conceptos/partidas en el sitio.**
>
> **El disparador acordado es el cruce a `En proceso de facturación`, y dura minutos** (FAC-100: 2m44s). La detección debe leer el changelog y buscar el cruce dentro de la ventana; consultar el estatus actual no sirve. El `issuetype` es `admon`. El workflow tiene **aprobaciones de JSM** con Araceli como aprobadora en las ramas de validación nacional/extranjera: la plataforma nunca aprueba.
>
> **10 de 69 tickets nacen de `Automation for Jira`** desde HH/SA/IN/RH, sin request type y sin ningún campo estructurado; los datos van en el `summary` y la `description` (ADF), con el origen en `issuelinks`. Persistir el `issue.id` numérico además de la llave.
>
> Pendientes técnicos: parser de ADF para `Nombre del Cliente` (no trae RFC), armado de la cotización cuando se defina el tema de conceptos, y diseñar un script de escritura de una sola operación para revisión previa. El script debe ser idempotente, no incluir credenciales y ejecutarse únicamente en el sandbox propio o acompañado en el ticket de prueba acordado. **No habrá `FACTEST`.**