Compare commits

...

3 Commits

Author SHA1 Message Date
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
JohannVelazquez 980baf0a0a Documenta la API de Jira FAC y protege su token
Consolida los hallazgos y decisiones pendientes para continuar el desarrollo sin exponer credenciales.
2026-08-01 12:38:39 -06:00
JohannVelazquez 820b8d41c8 Valida y consolida las horas al 27-jul: 41.58 h trazables desde el CSV
Las horas quedan validadas una por una contra evidencia fechada y el Excel
deja de tener cifras escritas a mano: se generan desde Seguimiento-horas.csv
y el script aborta si el detalle no cuadra con el total.

Correcciones de fondo:
- El Excel reportaba 17 h de Etapa 1 contra 16 h en el CSV (override manual).
- Comas sin escapar en el CSV ocultaban 1.00 h del 19-jul al parseo.
- El 10-jul estaba mal etiquetado: la mayor parte fue pulido del prototipo
  (E0-05), no el entregable de cierre (E0-06). Mismo total, etiquetas reales.
- Se incorporan 3.50 h no cobradas con evidencia: diseño del plan de Etapa 1
  (2.00 h) y preparación de las sesiones del 22 y 24-jul (1.50 h).
- Ajustes a la baja de Johann en horas reconstruidas: análisis del Discovery
  1.92→1.00 h, documentación de cobranza 1.50→0.75 h, guion 0.75→0.45 h.
  Las duraciones con transcript no se tocaron. Etapa 0 vuelve al rango 18–22 h.

Presentación del avance:
- Se reporta 75 % de Etapa 1 = 6 de 8 entregables cerrados, en lugar del 82 %
  ponderado anterior, que no distinguía trabajo hecho de trabajo bloqueado.
- Capa de escritura al 40 % (diseño, ADR-001 y modelo listos; falta el
  BindWriteClient y los shapes de los POST) y Jira al 90 % (conectividad
  resuelta el 27-jul; falta qué estatus dispara qué, sujeto a la definición
  de Balam). Modelo de facturas al 85 % con horas propias: ya no aparece
  avanzado con cero horas.
- Las 7 h que acumulaba "Demostración semanal" se reparten a las filas
  técnicas que les corresponden; esa fila se queda con gestión (1.75 h).

Nuevo: Avance-Balam-2026-07-27.xlsx, versión simple para Erika, derivada del
Excel interno para que ambos no puedan contradecirse.

Bitácora #58: Erika confirma que el Excel de horas es el insumo de
FACTURACIÓN, corrige su lectura de la capa de escritura al 100 %, y vuelve a
pedir la propuesta ajustada por Jira (pendiente desde el 22-jul). La sesión
del flujo de facturación no queda agendada: Balam la revisa internamente.
2026-07-28 22:21:04 -06:00
12 changed files with 1078 additions and 110 deletions
+2 -1
View File
@@ -4,8 +4,9 @@ Thumbs.db
.vscode/
.idea/
# Secretos — NUNCA versionar (token API de BIND, producción)
# Secretos — NUNCA versionar (tokens de API de BIND y Jira, producción)
bind_token_api.txt
propuesta/token-jira.txt
*.env
# Repo de código del cliente (github.com/pedro-balam-itsm/BALAM) — clon local, tiene su propio git
+21 -1
View File
@@ -2,7 +2,27 @@
Acciones vivas del proyecto. Formato: `[ ]` abierta · `[x]` cerrada (no se borran, dejan rastro). Cada una con responsable y, si aplica, fecha. Ver contexto en [REGISTRO.md](REGISTRO.md).
_Última actualización: 2026-07-23 (sesión de reglas del 22-jul REALIZADA: alta de clientes mapeada, fee = despacho, Jira confirmado en el proceso con estatus "resuelto" como disparador; no se tocaron PUE/PPD ni particularidades de envío; hoy 23-jul validación del prototipo a las 7:00 pm)._
_Última actualización: 2026-08-10 (Balam definió el flujo Jira→BIND en el correo del 7-ago: disparador = `En proceso de facturación`, todos los request types, monto/conceptos como campos de Jira, prueba acompañada sin `FACTEST`. Verificado por API el 10-ago: el campo `Monto sin IVA` ya existe, pero **los conceptos no tienen campo** y **10 de 69 tickets no traen datos estructurados**. Ver [REGISTRO #59](REGISTRO.md))._
## 🔥 Flujo Jira→BIND — abierto tras la definición del 7-ago
- [x] ~~🔥 **Johann — responder el correo del hilo de Jira**~~**Enviado el 10-ago.** Pide ticket de ejemplo, propone mié 12 / jue 13 a las 7:00 am, fija el alcance en la cotización y recuerda Azure. Texto en [REGISTRO #59](REGISTRO.md).
- [ ] 🔥 **Balam — levantar el TICKET DE EJEMPLO capturado "como debería ser"** (pedido para hoy/mañana en el correo del 10-ago). Tarea **dejada sin asignar a propósito** para que Balam la ruteé; en la práctica es de Arturo. Es el mecanismo elegido para resolver por evidencia, y no por correo, los huecos de abajo. ⚠️ **Riesgo: el precedente de latencia es de 8 días** (pregunta del 30-jul contestada el 7-ago). Si no hay respuesta para mañana al mediodía, empujar por WhatsApp con Erika, que es el canal que responde el mismo día.
- [ ] 🔥 **Balam/Noé — confirmar la sesión** (mié 12 o jue 13, 7:00 am). Sin fecha confirmada no hay prueba acompañada.
- [ ] 🔴 **Conceptos/partidas — se resuelve con el ticket de ejemplo.** El acuerdo del 4-ago resolvió el monto (`customfield_11556`) pero **no existe campo de conceptos en todo el sitio de Jira**. Si en el molde el desglose no cabe en ningún campo, hay que agregar uno; si no, la cotización va de una sola línea. **Sigue siendo el único bloqueo real para cerrar B7.**
- [ ] 🔴 **ACUNTIA — se resuelve con el segundo ticket de ejemplo.** `FAC-100` trae **un solo** `Monto sin IVA` (7,594.00 USD) para el ticket que históricamente representa 4048 facturas. ¿Un ticket → una cotización? Ver [REGISTRO #55](REGISTRO.md) punto 6.
- [ ] ⚠️ **Tickets de `Automation for Jira` — convertido en SUPUESTO, ya no bloquea.** 10 de 69 no tienen request type ni un solo campo (incluido `FAC-91`, vivo en validación nacional). En el correo del 10-ago se asienta que **siguen tramitándose a mano** y que la automatización cubre solo los que entran por el portal. Si Balam objeta, la regla de Automation tendría que propagar los campos (trabajo de ellos). El molde capturado a mano no puede resolver esto.
- [ ] ⚠️ **Johann — decidir el destino de la cotización de la prueba en BIND.** El ejercicio escribe una **cotización real en BIND producción**. Anunciado en el correo, con dos salidas ofrecidas: cancelarla al terminar o apuntarla a un cliente de prueba. Definir cuál antes de la sesión.
- [ ] 🔴 **¿El comercial ya genera la cotización en BIND, o la genera la plataforma?** Contradicción abierta: el 6-jul Ara dijo que **el comercial la genera y la plataforma la convierte** a prefactura ([REGISTRO #27](REGISTRO.md)); el 4-ago Arturo dijo que **la plataforma la genera**; y el formulario de Jira sigue exigiendo **adjuntar la cotización**. Si ambas cosas ocurren, **quedan dos cotizaciones por ticket**. Se resuelve viendo qué adjunto pone Arturo en el ticket de ejemplo: cotización de BIND ya existente (→ convertir) o documento comercial (→ crear).
- [x] ~~**Alcance del ejercicio en vivo: ¿cotización o prefactura?**~~**Decidido (10-ago): termina en la COTIZACIÓN.** Es lo que contestó Arturo el 4-ago; la aprobación de Araceli va entre `En proceso de facturación` y `Facturado`, así que la prefactura pertenece después; Arturo pidió gradualismo explícito; y la cotización se cancela sin efecto fiscal mientras la prefactura es un registro en `Invoices` con `UUID = null`. La prefactura queda para la segunda sesión. Candados acordados: **cotización → prefactura → aprobación humana → timbrado** ([REGISTRO #55](REGISTRO.md)).
- [ ] ⚠️ **La transición automática depende de Azure.** En la prueba del miércoles el disparo es **manual** (pipeline `Draft→DryRun→Confirmed` de ADR-001 con flags apagados). Para que corra automática hace falta el ambiente publicado y corriendo continuo — otro argumento para insistir en el acceso a Azure.
- [ ] 🔥 **Johann — preparar el script contra el ticket de ejemplo** en cuanto Arturo lo levante, para llegar a la sesión con el flujo funcionando. Compartirlo con Balam **antes** de la sesión (protocolo del Bloque 6 de `../planeacion/Investigacion-API-Jira.md`).
- [ ] **Balam/Noé — fecha para la prueba acompañada de escritura.** Aceptado el formato el 7-ago, sin día propuesto por ellos. En el correo del 10-ago se proponen **mié 12 o jue 13-ago, 7:00 am o después de 6:00 pm** (30 min), sobre un ticket que cree Arturo, con el script compartido de antemano. **No habrá `FACTEST`.**
- [ ] **Balam — ¿nacional vs extranjero cambia la cotización** o solo el paquete de salida (PDF+XML vs PDF)? Hay dos estatus de validación y una aprobación por rama.
- [ ] **Balam/Pedro — migrar a cuenta de servicio** en vez de la cuenta personal de Pedro. Planteado el 29-jul, sin respuesta: hoy todo lo que escriba la plataforma queda firmado como Pedro y una rotación suya mata la integración.
- [ ] **Johann — levantar el sandbox Jira Cloud propio** (plan Free, proyecto JSM que replique el workflow de FAC). Con `FACTEST` descartado, es el único lugar donde se puede equivocar sin costo.
- [ ] ⚠️ **Johann — implementar la detección por changelog, no por estatus actual.** `FAC-100` estuvo **2m44s** en el estatus disparador; un poller de 15 min que consulte el estado actual se pierde la mayoría de los disparos. Refuerza el caso de pedir un webhook / regla de Automation a Pedro.
- [ ] **Johann — mover el token de Jira a user-secrets/Key Vault** y borrarlo de disco (ya está en `.gitignore:9`). Mismo pendiente que arrastra `bind_token_api.txt`.
## 🔴 Johann (proveedor) — inmediato
+117
View File
@@ -1067,6 +1067,123 @@ Cierre de la sesión (~0.5 h real). **B6 conforme al plan:** `ITenantOwned` en l
---
## 58 · Jul 2728, 2026 · WhatsApp Erika ↔ Johann · avance de Etapa 1 entregado, Excel de horas para FACTURACIÓN y Jira aún sin licencia ⭐
> Continuación de [#57](REGISTRO.md). Registrado a partir de capturas de pantalla del hilo (4 imágenes). Cubre el envío del avance del 27-jul y los pedidos de Erika del 27 (tarde) y 28 (mañana).
**27-jul, mañana — avance de Etapa 1 entregado por mensaje:** Erika pidió los avances de Etapa 1 (📷 captura del plan, 7:11 am). Johann preguntó si ya existe **un Jira para el seguimiento de este proyecto** —es decir, dónde llevar sus actividades y horas, no la instancia a integrar— (7:14 am) → **respuesta de Erika (7:54 am): "aún estamos viendo lo de alguna licencia... pero estamos viendo una segunda opción y no Jira precisamente".** Johann ofreció trabajar en un Excel compartido; Erika contestó que le comentó a Pedro que **pudiera ser un Drive para subir la documentación, pero están en espera de la validación** (8:57 am).
> ⚠️ **Precisión importante:** este intercambio es sobre la **herramienta de gestión del proyecto** (dónde se registran actividades y horas de Johann), NO sobre la integración técnica. El Jira a integrar es la **instancia productiva de TI que administra Pedro** —bandeja FAC, token PAF a 1 año, API read+write confirmada ([#56](REGISTRO.md))— y ese no está en duda. Consecuencia práctica: **mientras no haya herramienta de gestión, el Excel de horas de Johann ES el sistema de seguimiento del proyecto**, lo que explica la insistencia de Erika en el desglose por etapa.
Johann envió el **avance por actividad en texto** (8:12 am), con 👍 de Erika y "gracias" (8:58 am):
> ✅ Sincronización de clientes — hecho y verificado · ✅ Sincronización de facturas/cotizaciones — hecho, 1,494 facturas cuadran · ✅ Modelo + migraciones + datos de prueba — hecho · ⏸️ Capa de escritura — en la junta del viernes comentaron que iban a definir el flujo de facturación; en cuanto quede, la implemento (Balam) · ⏳ Config. Azure (+ staging) — espera el acceso a Azure de Pedro/Noé (Balam) · ⏳ CI/CD + secretos — depende de Azure (Balam)
**27-jul, tarde — la fila de escritura y la sesión del proceso:** Erika preguntó (3:35 pm) si la capa de escritura *"es la que está pendiente de lo que se mencionó el viernes de que validarían el flujo"* → Johann confirmó. Erika: **"es que lo tengo al 100%"** (3:40 pm) → **Johann corrigió: "lo volví a marcar como pendiente porque se iba a validar el proceso"** y preguntó cuándo podrían tener la junta revisando el proceso validado (3:443:45 pm). Erika: le comentó a Arturo, y como **Ara sale de viaje**, mañana en la oficina ve cuándo **revisarlo internamente y después pasarlo con Johann**; queda en avisar (3:49 pm). ⚠️ **La sesión del 28-jul no quedó confirmada — se movió a revisión interna de Balam primero.**
**27-jul, 4:59 pm — pedido formal de horas:** *"johan un favor, me puedes pasar en un excel tus horas que llevas de cada etapa por favor"***Johann envió `Avance-Balam-2026-07-27.xlsx` a las 6:45 pm** (versión simplificada). Johann ofreció además pasar un Excel para las siguientes etapas; Erika (7:57 pm): *"Y las ya completas más que nada para ver cuántas son"*.
**28-jul, mañana — el propósito del Excel y dos pendientes:**
1. **(8:36 am) ⭐ "es para llevar el conteo de horas de cada etapa sobre todo para la FACTURACIÓN correspondiente de la misma."** → El Excel de horas es el insumo de facturación, no solo seguimiento.
2. **(9:12 am) "johann adicional, ¿pasaste la propuesta con lo adicional de JIRA que no estaba contemplado?"** → Johann (10:58 am): **"Aún no la comparto, te la paso a la brevedad."** ⚠️ Es el compromiso que viene arrastrándose desde el 22-jul ([#57](REGISTRO.md)).
3. **(9:27 am)** Sobre Azure: Johann preguntó si el acceso lo iba a entregar Noé o Pedro → Erika: **"Sip"** + *"deja pregunto"* (11:01 am). Johann pidió contexto de qué se requiere específicamente de Azure (11:19 am).
**Lecturas clave:**
> - **El Excel de horas es documento de facturación** (dicho por Erika, 28-jul 8:36 am). Cambia su naturaleza: ya no es reporte de avance, es sustento de cobro. Refuerza que las 41.58 h estén validadas y trazables.
> - **Erika creía la capa de escritura al 100%** y Johann la corrigió a pendiente. Buena decisión: si se hubiera dejado al 100%, la definición del proceso habría reabierto una actividad cerrada. Valida el criterio de reportar 40% y no más.
> - **Sin herramienta de gestión para el proyecto** (licencia en revisión; Erika evalúa una alternativa a Jira para ese fin). **No afecta la integración técnica** —esa va contra la instancia de TI de Pedro, ya disponible—, pero sí significa que **el Excel de horas de Johann es el registro oficial de seguimiento** y el insumo de facturación. Mantenerlo trazable y al día no es opcional.
> - **La propuesta ajustada por Jira sigue sin enviarse** (comprometida el 22-jul, re-preguntada el 28-jul). Es el pendiente comercial más viejo del hilo y ahora Erika lo pide por segunda vez.
> - **La sesión del flujo de facturación no está agendada:** Balam la revisa internamente primero (Ara de viaje). El desbloqueo de B7 se corre de fecha.
**Pendientes que deja el hilo:**
- [ ] ⚠️🔥 Johann — **enviar la propuesta/adenda ajustada por la integración Jira→BIND** (comprometida 22-jul, re-preguntada 28-jul 9:12 am). Estimación ya comunicada: ~10 h ([#51](REGISTRO.md)).
- [ ] Johann — **enviar el Excel de horas de las etapas siguientes** (2, 3) que Erika pidió el 27-jul 6:46 pm ("las ya completas más que nada para ver cuántas son").
- [ ] Balam/Erika — **avisar fecha de la sesión del flujo de facturación** tras su revisión interna. Desbloquea B7.
- [ ] Balam/Erika — **definir la herramienta de gestión del proyecto** (licencia en revisión). Mientras no exista, el Excel de Johann es el registro oficial.
- [ ] Balam/Pedro/Noé — **entregar el acceso a Azure**; Erika iba a preguntar (28-jul 11:01 am). Johann pidió el contexto de lo que se requiere.
---
## 59 · Jul 30 Ago 10, 2026 · Correo · Balam define el flujo Jira→BIND: disparador, campos y prueba acompañada ⭐🔥
> Hilo `RV: Seguimiento: medición de consumo API de Jira y accesos` (fuente original conservada localmente; no versionada porque contiene cabeceras y adjuntos completos del correo). Continúa [#56](REGISTRO.md). Es **la definición del flujo de facturación que estaba bloqueando B7 desde el 24-jul** ([#55](REGISTRO.md), [#58](REGISTRO.md)).
**Cronología (8 días de latencia):**
| Fecha | Movimiento |
|---|---|
| 30-jul 10:2811:44 am | Pedro confirma su correo, **regenera el token** y comparte la URL `https://balam-jsm-temp.atlassian.net`. Cierra el 401 del 29-jul. |
| 30-jul 1:07 pm | **Johann pregunta las 4 definiciones** (disparador, alcance de request types, fuente de monto/conceptos, y FACTEST vs prueba acompañada) + reporta el hallazgo de que el monto no está en campos estructurados. |
| 3-ago 4:58 pm | Noé **escala internamente** a Araceli y Arturo (CC Pedro), reconociendo el cambio de propósito: *"el flujo de JIRA tenía un propósito claro, validar la actividad y la evidencia, ahora cambiar a ejecutar y automatizar actividades con el desarrollo"*. Entra **Araceli Sánchez** al hilo. |
| 3-ago 4:59 pm | Noé a Johann: *"Estamos definiendo para darte respuesta mañana mismo"*. |
| 4-ago 12:15 pm | **Arturo responde en verde** sobre los tres puntos + adjunta captura del formulario y del filtro de request types. |
| 7-ago 5:34 pm | Noé reenvía a Johann y **contesta la cuarta**: prueba acompañada. |
**Las 4 definiciones, cerradas:**
1.**La cotización BIND se genera al entrar a `En proceso de facturación`.** Y: *"por el momento todo se tramitará como facturación normal, hasta el explorar el módulo de proyectos en BIND"***el caso de facturación recurrente queda fuera de alcance por ahora.**
2.**Se observan TODOS los request types** de la bandeja de facturación, no solo `Facturación adicional`.
3.**Monto y conceptos van como campos de Jira**, no del adjunto. Noé: *"resta rapidez, pero garantiza consistencia de datos"*. Arturo: *"de acuerdo, deben ser los campos de JIRA (omitir el campo recurrencia)"*.
4.**No habrá proyecto `FACTEST`:** *"Preferimos una prueba acompañada sobre un ticket"* (Noé).
**Verificación por API el 10-ago (solo lectura; detalle en `../planeacion/Investigacion-API-Jira.md`):**
-**El acuerdo ya está implementado en Jira:** `Monto sin IVA` existe como `customfield_11556` (number/float, obligatorio) en el request type 83. Desapareció `Periodo de Incidencias`.
- 🔴 **Pero solo 2 de 69 tickets lo tienen poblado**`FAC-100` y `FAC-101`, ambos creados el **4-ago**, el mismo día de la respuesta de Arturo. Sin histórico contra el cual validar el mapeo.
- 🔴 **`conceptos`/partidas NO existe como campo en todo el sitio.** El catálogo completo solo tiene `Monto sin IVA` (11556), `Monto con IVA` (11522, sin usar en el formulario) y `Total forms` (de JSM). **El acuerdo resolvió el monto pero no los conceptos.**
- 🔴 **"Todos los request types" incluye tickets sin request type:** el filtro de la bandeja muestra 6 opciones (las 4 del portal + `Empty` + `Emailed request`). **10 de 69 tickets no tienen request type ni un solo campo estructurado**; nacen de `Automation for Jira` desde HH/SA/IN/RH, con los datos codificados en el `summary`. **No son ruido:** `FAC-91` es uno de ellos y está vivo en `En validación nacional` (`[FACTURA COMPLETA - Staff Augmentation] Axians — RH-36`).
- 🔴 **El estatus disparador dura minutos.** `FAC-100` estuvo **2m44s** en `En proceso de facturación` (11:51:25 → 11:54:09 del 5-ago) y 25 min de punta a punta. Solo 1 de 69 tickets está parado ahí hoy. **Un poller que consulte el estatus actual se pierde la mayoría de los disparos → la detección por changelog pasa de preferencia a requisito.**
-**Hallazgo nuevo: el workflow tiene aprobaciones de JSM**, con **Araceli Sánchez** como aprobadora en las ramas de validación nacional/extranjera (`customfield_10003`/`10025`). El paso a `Facturado` es una aprobación formal, no una transición simple. La plataforma no debe aprobar nunca.
-`FAC-100` cierra con `status = Facturado` **y** `resolution = Done` consistentes (resuelve la trampa documentada el 30-jul).
- ✅ Headers de rate limit confirmados: `X-RateLimit-Limit: 350`, `X-RateLimit-Remaining: 348`, más los estándar `RateLimit-Policy`/`RateLimit`. **El límite observado es 350, no los 100/s que mencionó Pedro** → leer el header, no hardcodear.
- ✅ El origen es **creación por Automation, no move entre proyectos**: sin cambios de `project`/`key` en los changelogs, con `issuelinks` al ticket origen. **Escuchar FAC basta.**
- ⚠️ `Facturación recurrente` sigue siendo obligatorio y se está llenando (`FAC-100`: `Si`, periodo `12`) pese al acuerdo de omitirlo.
- ⚠️ `Nombre del Cliente` es **ADF**, no string, y **no trae RFC** — la llave de match contra BIND queda en el nombre escrito a mano.
- ⚠️ **ACUNTIA ya tiene caso real:** `FAC-100` es `Julio/CONSULTORIA Y SERVICIOS (Eduardo Fiallo CEMEXUSA)/ACUNTIA`, `Helmstone`, USD, 45 días, **un solo `Monto sin IVA` de 7,594.00** — para el ticket que históricamente representa 4048 facturas ([#55](REGISTRO.md) punto 6).
> **Lectura estratégica:**
> - **B7 queda desbloqueado en su mayor parte.** El disparador está definido y el campo de monto ya existe: se puede implementar lectura, detección por changelog, parser de ADF y mapeo. **Lo único que sigue bloqueado es el armado final de la cotización**, por la definición de conceptos.
> - **El acuerdo del 4-ago resolvió el monto y olvidó los conceptos.** Es el hueco más caro del hilo: sin partidas, la cotización BIND sale de una sola línea. Puede ser aceptable, pero tiene que ser una decisión explícita de Balam, no un default silencioso.
> - **"Todos los request types" no es implementable literalmente.** ~14% de los tickets no tiene datos con los que armar nada. Hay que acotar el alcance por escrito o pedir que la regla de Automation propague los campos — y eso es trabajo de Balam.
> - **Sin `FACTEST`, el sandbox propio deja de ser opcional.** Se escribirá en producción una sola vez, acompañado; todo el desarrollo previo tiene que ocurrir en un sitio Jira Cloud gratuito propio.
> - **8 días de latencia en la definición**, con el agravante de que la pregunta se hizo el 30-jul y el escalamiento interno no arrancó hasta el 3-ago. Suma al patrón ya documentado: la definición del flujo lleva bloqueando desde el 24-jul.
> - **Araceli entra al hilo como aprobadora**, tanto en el correo como en el workflow de Jira. Es un interlocutor nuevo para las definiciones que faltan.
> - **PUE/PPD gana evidencia:** hay tickets `Seguimiento del Ticket con pago anticipado:`, lo que sugiere casos PUE reales y refuerza la pregunta que lleva cuatro sesiones sin hacerse ([#52](REGISTRO.md)).
**Respuesta de Johann — ENVIADA el 10-ago, tarde.** Texto completo y borradores previos en `../planeacion/Correo-respuesta-Noe-2026-08-10.md`:
> Buenas tardes, espero se encuentren bien.
> Gracias por las definiciones, quedan claras y ya retomé el desarrollo, ya validé que el campo "Monto sin IVA" está creado y funcionando.
> Para aterrizar los detalles que faltan sería de ayuda que hoy o mañana se cree un ticket de ejemplo capturado "como debería ser", con todos los campos llenos tal como quieren que se capture de aquí en adelante. Contra ese molde llego a la sesión con el flujo ya funcionando.
> Para la sesión, ¿les funciona el miércoles 12 o el jueves 13, a las 7:00 am?
> El ejercicio termina en la cotización en BIND.
> Sigo pendiente del acceso a Azure para publicar el ambiente.
> Quedo atento. Saludos.
**Decisión de enfoque:** en lugar de resolver los tres huecos con otra ronda de correos, se pide **un ticket de ejemplo capturado "como debería ser"**. Balam ya está adoptando el llenado completo de campos, así que el molde resuelve por evidencia lo que la prosa tardaría dos semanas en definir, y permite preparar el script contra datos reales para llegar a la sesión con el flujo funcionando.
**La tarea se dejó deliberadamente sin asignar** ("sería de ayuda que se cree"), para que Balam la ruteé internamente: Arturo no estaba en el CC del correo de Noé, y Noé ya venía haciendo ese papel de router desde el escalamiento del 3-ago. Nombrar a un ausente habría sido peor que dejarlo abierto.
**Alcance del ejercicio fijado: termina en la COTIZACIÓN, no en la prefactura.** Razones: es lo que contestó Arturo el 4-ago; la aprobación de Araceli va entre `En proceso de facturación` y `Facturado`, así que la prefactura pertenece después; Arturo pidió gradualismo explícito el 24-jul; y una cotización se cancela sin efecto fiscal, mientras que en BIND la prefactura es un registro en `Invoices` con `UUID = null` y los shapes de los POST nunca se han probado. Respeta los tres candados de Noé: **cotización → prefactura → aprobación humana → timbrado** ([#55](REGISTRO.md)).
**Lo que el correo final dejó fuera y ahora se resuelve verbalmente en la sesión** (sin constancia escrita previa):
- Que el ejercicio **genera una cotización real en BIND producción** y que se cancela al terminar. **Anunciarlo al abrir la sesión, antes de ejecutar.**
- El compromiso de **compartir el script antes** (condición de Noé del 24-jul). Mandarlo por separado cuando esté listo.
- Que el **disparo es manual** en esta prueba (pipeline `Draft→DryRun→Confirmed` de ADR-001 con flags apagados) y que la versión automática **depende de Azure**.
- **ACUNTIA** y el **supuesto de los tickets de `Automation for Jira`**.
**Pendientes que deja el hilo:**
- [x] ~~🔥 Johann — **responder el correo**~~**Enviado el 10-ago** (texto arriba).
- [ ] 🔴 Balam/Arturo — **definir de dónde salen los conceptos/partidas** de la cotización.
- [ ] 🔴 Balam/Arturo — **decidir qué pasa con los tickets creados por Automation** (10 de 69, sin campos).
- [ ] 🔴 Balam/Arturo — **confirmar si ACUNTIA es un ticket → una cotización** o sigue siendo 4048 facturas.
- [ ] Balam/Noé — **fecha para la prueba acompañada** de escritura.
- [ ] Balam — **¿nacional vs extranjero cambia la cotización** o solo el paquete de salida?
- [ ] Balam/Pedro — **cuenta de servicio** en vez de la cuenta personal de Pedro (planteado el 29-jul, sin respuesta).
- [ ] Johann — **levantar el sandbox Jira Cloud propio** (plan Free) para desarrollar sin tocar producción.
- [ ] Johann — mover el token de Jira a **user-secrets/Key Vault** y borrarlo de disco (ya está gitignoreado).
---
## Resumen ejecutivo del hilo (para contexto rápido)
### Datos duros confirmados por Balam
Binary file not shown.
+22 -12
View File
@@ -2,7 +2,7 @@
**Fecha:** 27-jul-2026 (corte de los lunes, formato pedido por Erika: entregable + actividad + horas por etapa)
**Etapa:** Plataforma base y sincronización con BIND
**Rango estimado contractual:** 3239 h · **Horas registradas de Etapa 1:** 17.0 h
**Rango estimado contractual:** 3239 h · **Horas registradas de Etapa 1:** 19.75 h (total del proyecto: 41.58 h)
**Adjunto:** `Plan-actividades-avance-2026-07-27.xlsx`
> Documento interno de Johann. El mensaje para Erika va abajo; el resto es sustento propio (no se comparte).
@@ -17,7 +17,7 @@
>
> Lo que sigue pendiente es, sobre todo, la **capa de escritura (cotización→factura), que dejé en pausa a propósito hasta que definan el flujo de facturación en la sesión del martes**, ya que de ahí dependen las reglas. También quedan por cerrar el afinado de datos de prueba y la parte de Azure/CI-CD, que depende de los accesos de su lado.
>
> En horas voy en **17 h de Etapa 1** (rango estimado 3239 h). Con la key de Jira que nos pasó Pedro hoy y las definiciones del martes, arranco la integración con Jira y la capa de escritura. Cualquier duda quedo al pendiente. 🙌
> En horas voy en **19.75 h de Etapa 1** (rango estimado 3239 h), con **6 de los 8 entregables de la etapa ya terminados**. Con la key de Jira que nos pasó Pedro hoy y las definiciones del martes, arranco la integración con Jira y la capa de escritura. Cualquier duda quedo al pendiente. 🙌
*(Nota: el ajuste de la propuesta y el conteo de horas formal van por separado; este mensaje es solo el avance de Etapa 1.)*
@@ -37,23 +37,33 @@
- 26/26 pruebas unitarias verdes.
### En construcción / en pausa
- **Capa de escritura controlada (cotización→factura)***entregable 4*. 🔴 **En pausa deliberada** hasta la definición del flujo de facturación (sesión martes 28-jul con Arturo/Araceli): de ahí salen las reglas de prefactura, PPD/PUE, alta de cliente y qué estatus de Jira dispara qué. El andamiaje (pipeline dry-run→confirm, flags apagados, `WriteOperations`) está diseñado; se implementa contra las reglas ya cerradas.
- **Capa de escritura controlada (cotización→factura)***entregable 4*. **Al 40 %: diseño y modelo listos, implementación en pausa.** Ya hecho: entidad `WriteOperation` con el pipeline `Draft→DryRun→Confirmed` migrada, policy `PuedeOperarEscritura`, ADR-001, el diseño del `BindWriteClient` con flags apagados por default y la regla PPD/PUE (informada por el hallazgo de que 1,374/1,374 facturas del histórico son PUE). Falta el `BindWriteClient`, el pipeline implementado, los shapes reales de los POST y las pruebas. 🔴 **Pausa deliberada** hasta la definición del flujo de facturación (sesión martes 28-jul con Arturo/Araceli): de ahí salen las reglas de prefactura, PPD/PUE, alta de cliente y qué estatus de Jira dispara qué.
- **Scheduler + backfill** (sync programado cada 15 min) — el sync ya funciona en manual; falta automatizarlo.
- **Datos de prueba (seeder) + runbook***entregable 8*: migraciones ✅, seeder ficticio pendiente.
- **Datos de prueba (seeder) + runbook***entregable 8*, **al 85 %**: modelo central de facturas con el `InvoiceStatusMapper` (13 pruebas) ✅, migraciones ✅; falta el seeder de datos ficticios y el runbook.
- **Suite de pruebas de flujos críticos** (integración) — pendiente.
### Dependencias externas (lado Balam)
- **Definición del flujo de facturación/prefacturación** — sesión martes 28-jul (Erika coordina). Desbloquea la capa de escritura.
- **Correo del flujo de facturación de Jira** (Erika lo envió 22-jul) — a incorporar.
- **Token de Jira (PAF)** recibido el 27-jul + docs de Atlassian; falta confirmar límites de consumo (Pedro investiga). Bandeja: **Facturación (FAC)**.
- **Alcance Jira → cotización BIND al 90 %:** conectividad resuelta el 27-jul (token PAF con vigencia a 1 año, API confirmada de lectura **y** escritura, bandeja **Facturación (FAC)** identificada, límites de consumo aclarados por correo de Pedro: sin tope mensual, 3 límites de velocidad, autorregulación por headers `X-RateLimit-*`). Falta el 10 %: **qué estatus de Jira dispara qué** (amarrado a la definición del 28-jul) y validar si basta escuchar FAC o hay que rastrear los procesos que nacen en RH/Administración General y caen ahí.
- **Acceso a Azure + CI/CD** (Pedro/Noé) — condiciona el despliegue.
## Criterio de medición
- **Horas de Etapa 1:** 17.0 h registradas de un rango de 3239 h (**~4453 % del esfuerzo estimado**).
- **Entregables contractuales:** **6 de 8 terminados** (1, 2, 3, 5, 6, 7); el 8 parcial (migraciones sí, seeder no); el 4 (escritura) en pausa por definición de Balam.
- Se reporta por hora + entregable, no un porcentaje global único, para no dar una cifra engañosa. El Excel muestra ~82 % ponderado por actividad porque el trabajo restante (escritura, seeder, scheduler) es de menor peso que el núcleo ya hecho, pero **la escritura está bloqueada por la definición pendiente de Balam**, no por avance de desarrollo.
- **Entregables contractuales:** **6 de 8 terminados** (1, 2, 3, 5, 6, 7). Los dos restantes van parciales y su avance se muestra en su propia fila del Excel, no promediado en el encabezado: el **8 al 85 %** (migraciones sí, seeder no) y el **4 al 40 %** (escritura: diseño y modelo listos, implementación en pausa por definición de Balam). → **El Excel reporta 75 % de Etapa 1**, contando solo entregables cerrados, que es lo verificable.
- **Horas de Etapa 1:** 19.75 h de un rango de 3239 h (**~5162 % del esfuerzo estimado**).
- Se reporta por **entregable terminado**, no por promedio ponderado de actividades. El corte anterior mostraba 82 % ponderado, que era optimista e indefendible: no distinguía entre trabajo hecho y trabajo bloqueado. "6 de 8" dice exactamente qué falta y de quién depende.
- La brecha entre 75 % de entregables y ~55 % de horas **no es un error**: refleja que las sesiones de desarrollo asistidas por IA produjeron el núcleo en mucho menos tiempo de reloj que la estimación manual (ver la nota en `Seguimiento-horas.md`).
## Puntos de juicio a confirmar por Johann antes de enviar
1. **Horas 2227 jul:** solo cargué las sesiones documentadas (reglas 22-jul 0.80 h, validación 24-jul 1.05 h como Etapa 0, API Jira 27-jul 0.20 h). Si trabajaste desarrollo adicional en esa ventana, hay que sumarlo.
2. **% de Etapa 1 (82 %):** es el valor ponderado heredado del corte del 21-jul + avance; si prefieres una lectura más conservadora (por horas, ~50 %), lo ajusto.
3. **Tono del mensaje:** el borrador enmarca la capa de escritura como "en pausa por la definición del 28-jul" (pone la pelota de su lado, y es verdad). Si prefieres suavizarlo, lo cambio.
## Horas: estado de la validación (resuelto el 27-jul)
- **41.58 h en total** — Etapa 0: 21.83 h · Etapa 1: 19.75 h.
- **Cero horas pendientes de validar.** 9.13 h con duración documentada (`confirmada`) y 32.45 h reconstruidas y confirmadas por Johann contra evidencia fechada (`validada`).
- Correcciones aplicadas en este corte:
- Se corrigió un error de formato del CSV que ocultaba **1.00 h** del 19-jul (entorno local).
- Se re-etiquetó el 10-jul: la mayor parte eran 6 commits de pulido del prototipo (E0-05), no el entregable de cierre (E0-06). Mismo total, etiquetas correctas.
- Se incorporaron **3.50 h** de trabajo real que no estaba cobrado: diseño del plan de Etapa 1 (2.00 h, `Plan-Etapa1.md`) y preparación de las sesiones del 22 y 24-jul (1.50 h, agenda y guion de validación).
- **Ajustes a la baja** en las horas reconstruidas de Etapa 0: análisis del Discovery de facturación 1.92 → 1.00 h, documentación del de cobranza 1.50 → 0.75 h, guion de validación 0.75 → 0.45 h. Las duraciones de sesión documentadas en transcript no se tocaron.
- Cada celda de horas del Excel se genera desde el CSV y el script **falla** si el detalle no cuadra con el total.
- **Etapa 0 cerró en 21.83 h, dentro del rango contractual de 1822 h.**
## Punto abierto (no bloquea el envío)
- **Tono del mensaje:** el borrador enmarca la capa de escritura como "en pausa por la definición del 28-jul" — pone la pelota de su lado, y es verdad. Si prefieres suavizarlo, se ajusta.
@@ -0,0 +1,56 @@
# Correo — Respuesta a Noé/Arturo: ticket de ejemplo "como debería ser" + fecha de la prueba acompañada
Enviado · 10-ago-2026
Responder a todos sobre el hilo `RV: Seguimiento: medición de consumo API de Jira y accesos`
**Para:** Noé Rocha · **CC:** Arturo Rosas; Araceli Sánchez; Pedro Ayala; Erika Chávez
**Asunto:** (el del hilo, con `RE:`)
> ⚠️ **Agregar a Arturo y a Erika al CC:** Noé mandó su correo del 7-ago solo con Araceli y Pedro en copia, pero Arturo es quien captura y define. Erika coordina agendas.
**Contexto:** Noé reenvió el 7-ago las respuestas de Arturo del 4-ago, más la definición de que las pruebas serán acompañadas. Las cuatro preguntas del 30-jul quedan cerradas.
**Estrategia — versión mínima.** Solo cinco cosas: agradecer, pedir el ticket de ejemplo, proponer fecha, decir dónde termina el ejercicio y recordar Azure. Todo lo demás se resuelve en la sesión o con el molde. El ticket capturado "como debería ser" hace el trabajo que antes hacían tres párrafos de preguntas: si los conceptos no caben en ningún campo, se ve en el ejemplo.
**Lo que se sacó del correo y ahora hay que llevar A LA SESIÓN** (ninguno se pierde, pero ya no tienen registro escrito previo):
1. 🔴 **ACUNTIA** — que el molde no cubre si Arturo captura un caso normal. `FAC-100` trae un solo monto de 7,594.00 USD para el ticket que históricamente son 4048 facturas. **Es el riesgo más grande de alcance y ya no está pidiéndose por escrito** → preguntarlo en la sesión, o mandar un correo aparte si quieres constancia.
2. ⚠️ **El supuesto de los tickets de `Automation for Jira`** (10 de 69, sin campos, `FAC-91` activo). Ya no queda asentado por escrito que siguen manuales, así que la carga de objetar no está de su lado. Plantearlo en la sesión.
3. **Conceptos/partidas** — se resuelve solo con el molde, pero conviene mirar el ejemplo con esto en mente antes de la sesión.
4. **PUE vs PPD** y **cuenta de servicio** — ya estaban diferidos a la sesión desde la versión anterior.
**Alcance del ejercicio — cotización, NO prefactura.** Razones: (1) es literalmente lo que Arturo contestó el 4-ago; (2) la aprobación de Araceli está entre `En proceso de facturación` y `Facturado`, así que la prefactura pertenece *después* — Noé lo cerró como tres candados: **cotización → prefactura → aprobación humana → timbrado** ([REGISTRO #55](../bitacora/REGISTRO.md)); (3) Arturo pidió gradualismo explícito (*"para ir monitoreando la eficiencia de la IA"*); (4) una cotización se cancela sin efecto fiscal, mientras que en BIND la prefactura **no es un recurso aparte** sino un registro en `Invoices` con `UUID = null`, y los *shapes* de los POST nunca se han probado.
**El disparo es manual en esta prueba.** No es un modo inventado para la ocasión: son las etapas del pipeline ya modelado `Draft → DryRun → Confirmed` (ADR-001) con los feature flags apagados. La versión automática es el mismo código con el flag encendido, y **depende de Azure** para correr continuo.
> ⚠️ **Contradicción pendiente que el ticket de ejemplo resolverá solo:** el 6-jul Ara describió que **el comercial genera la cotización en BIND** y la plataforma la *convierte* a prefactura ([REGISTRO #27](../bitacora/REGISTRO.md)); el 4-ago Arturo dijo que la plataforma la *genera*; y el formulario sigue exigiendo **adjuntar la cotización**. Si ambas cosas ocurren, **quedan dos cotizaciones por ticket**. El adjunto que Arturo ponga en el ejemplo lo dirá.
======================================================================
## VERSIÓN FINAL (la que Johann envía)
Buenas tardes, espero se encuentren bien.
Gracias por las definiciones, quedan claras y ya retomé el desarrollo, ya validé que el campo "Monto sin IVA" está creado y funcionando.
Para aterrizar los detalles que faltan sería de ayuda que Arturo cree hoy o mañana un ticket de ejemplo capturado "como debería ser", con todos los campos llenos tal como quieren que se capture de aquí en adelante. Contra ese molde llego a la sesión con el flujo ya funcionando.
Para la sesión, ¿les funciona el miércoles 12 o el jueves 13, a las 7:00 am?
El ejercicio termina en la cotización en BIND.
Sigo pendiente del acceso a Azure para publicar el ambiente.
Quedo atento.
Saludos.
======================================================================
**Lo que la versión final deja fuera y por tanto pasa a la sesión (verbal, sin constancia escrita previa):**
- **El destino de la cotización de prueba.** El correo ya no dice que el ejercicio genera una cotización real en BIND producción ni que se cancela al terminar. Anunciarlo al abrir la sesión, antes de ejecutar.
- **El compromiso de compartir el script antes.** Era la condición de Noé del 24-jul. Mandarlo igual por separado cuando esté listo.
- **La prefactura como paso siguiente** y los tres candados. Explicarlo en la sesión para que no esperen ver el flujo completo.
- **ACUNTIA** y **el supuesto de los tickets de `Automation for Jira`** (ver arriba).
- ✅ **El ticket de ejemplo ya tiene dueño nombrado** (Arturo). Solo falta **agregarlo al CC**: no venía en el correo de Noé del 7-ago.
+411
View File
@@ -0,0 +1,411 @@
# 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`.**
Binary file not shown.
+33 -29
View File
@@ -1,32 +1,36 @@
fecha,semana,etapa,actividad_id,actividad,horas,estado_hora,fuente_evidencia,facturable,notas
2026-07-01,2026-W27,0,E0-01,Sesión de arranque con Noé,1.00,confirmada,bitacora/REGISTRO.md#22,si,"Sesión calendarizada de 7:00 a 8:00 am"
2026-07-01,2026-W27,0,E0-01,Preparación de plan kickoff y materiales,1.00,reconstruida,planeacion/Kickoff-2026-07-01.md,si,"Estimación retrospectiva; validar antes de facturar"
2026-07-06,2026-W28,0,E0-02,Discovery de facturación y envío,1.08,confirmada,bitacora/REGISTRO.md#27,si,"Duración documentada: aproximadamente 1 h 5 min"
2026-07-06,2026-W28,0,E0-02,Preparación y análisis de Discovery de facturación,1.92,reconstruida,bitacora/REGISTRO.md#27,si,"Completa 3.00 h para facturación/envío incluyendo sesión"
2026-07-06,2026-W28,0,E0-03,Coordinación y validación de accesos BIND y marca,1.00,reconstruida,bitacora/REGISTRO.md#28-31,si,"Recepción documentación y salvaguardas"
2026-07-06,2026-W28,0,E0-04,Validación técnica de API BIND,4.50,reconstruida,bind-api-sandbox/VALIDACION-API.md,si,"117 GET análisis y reporte técnico"
2026-07-07,2026-W28,0,E0-02,Discovery de cobranza,1.00,confirmada,bitacora/REGISTRO.md#35,si,"Sesión calendarizada de 7:00 a 8:00 am; sin grabación"
2026-07-07,2026-W28,0,E0-02,Preparación y documentación de Discovery de cobranza,1.50,reconstruida,fuentes/2026-07-07 - Notas - Proceso actual de cobranza (sin grabacion).md,si,"Estimación retrospectiva"
2026-07-08,2026-W28,0,E0-05,Diseño y construcción del prototipo,3.00,reconstruida,prototipo/Prototipo-Etapa0.html,si,"Primera mitad del esfuerzo reconstruido"
2026-07-09,2026-W28,0,E0-05,Ajustes de cobranza navegación y prototipo,3.00,reconstruida,prototipo/Prototipo-Etapa0.html,si,"Segunda mitad del esfuerzo reconstruido"
2026-07-10,2026-W28,0,E0-06,Entregable hallazgos ADRs PDF correo y plan,3.00,reconstruida,prototipo/Correo-prototipo-Etapa0.md,si,"Incluye preparación del entregable y documentación"
2026-07-13,2026-W29,1,E1-01,Backend base autenticación roles y auditoría,2.00,reconstruida,planeacion/Plan-actividades.md,si,"Trabajo local; falta conciliar contra evidencia de código"
2026-07-13,2026-W29,1,E1-02,Coordinación del repositorio GitHub,0.50,reconstruida,bitacora/REGISTRO.md#43,si,"Seguimiento de creación y acceso"
2026-07-14,2026-W29,1,E1-01,Backend base autenticación roles y auditoría,1.50,reconstruida,planeacion/Plan-actividades.md,si,"Trabajo local; completa 3.50 h"
2026-07-14,2026-W29,1,E1-03,Arquitectura multi-tenant,1.00,reconstruida,planeacion/Plan-actividades.md,si,"Diseño inicial tenant_id y RLS"
2026-07-15,2026-W29,1,E1-03,Arquitectura multi-tenant,0.50,reconstruida,planeacion/Plan-actividades.md,si,"Completa 1.50 h"
2026-07-15,2026-W29,1,E1-04,Cliente productivo de API BIND,2.00,reconstruida,bind-api-sandbox/VALIDACION-API.md,si,"Inicio de reintentos OData y manejo de errores"
2026-07-15,2026-W29,1,E1-05,Seguimiento ajuste de calendario y corte de horas,0.50,reconstruida,bitacora/REGISTRO.md#41-45,si,"Actualización de plan y control"
2026-07-16,2026-W29,1,E1-06,Scaffold backend .NET 10 + frontend Angular 21 y push al repo de Balam,2.00,reconstruida,bitacora/REGISTRO.md#46,si,"Commits 220942a..1f7f098; incluye CLAUDE.md del repo; validar duración"
2026-07-16,2026-W29,1,E1-05,Corte de avance Etapa 1 para Erika (Excel + mensaje),1.00,reconstruida,planeacion/Avance-Etapa1-2026-07-16.md,si,"Enviado 8:40 pm por WhatsApp con acuse; validar duración"
2026-07-19,2026-W29,1,E1-06,Preparación de entorno local (clon del repo, SDK .NET 10, secrets, app corriendo),1.00,reconstruida,bitacora/REGISTRO.md,si,"API y frontend verificados en local; 13/13 pruebas verdes; validar duración"
2026-07-19,2026-W29,1,E1-06,Scalar UI para el OpenAPI del backend,0.25,confirmada,bitacora/REGISTRO.md#48,si,"Sesión asistida por IA; /scalar/v1 verificado"
2026-07-19,2026-W29,1,E1-01,B0+B1: Postgres 17 local + modelo de datos + Identity + migración Inicial + seed,1.00,confirmada,bitacora/REGISTRO.md#48,si,"Sesión asistida por IA (noche del domingo); 16 tablas migradas y siembra verificada; tiempo real de sesión"
2026-07-19,2026-W29,1,E1-01,B2: login JWT + policies por rol + bitácora de auditoría,0.50,confirmada,bitacora/REGISTRO.md#49,si,"Sesión asistida por IA; matriz 401/403/200 verificada end-to-end; tiempo real de sesión"
2026-07-01,2026-W27,0,E0-01,Sesión de arranque con Noé,1.00,confirmada,bitacora/REGISTRO.md#22,si,Sesión calendarizada de 7:00 a 8:00 am
2026-07-01,2026-W27,0,E0-01,Preparación de plan kickoff y materiales,1.00,validada,planeacion/Kickoff-2026-07-01.md,si,validada por Johann 27-jul
2026-07-06,2026-W28,0,E0-02,Discovery de facturación y envío,1.08,confirmada,bitacora/REGISTRO.md#27,si,Duración documentada: aproximadamente 1 h 5 min
2026-07-06,2026-W28,0,E0-02,Preparación y análisis de Discovery de facturación,1.00,validada,bitacora/REGISTRO.md#27,si,"Completa 2.08 h con la sesión; ajustada a la baja por Johann 27-jul"
2026-07-06,2026-W28,0,E0-03,Coordinación y validación de accesos BIND y marca,1.00,validada,bitacora/REGISTRO.md#28-31,si,Recepción documentación y salvaguardas; validada por Johann 27-jul
2026-07-06,2026-W28,0,E0-04,Validación técnica de API BIND,4.50,validada,bind-api-sandbox/VALIDACION-API.md,si,117 GET análisis y reporte técnico; validada por Johann 27-jul
2026-07-07,2026-W28,0,E0-02,Discovery de cobranza,1.00,confirmada,bitacora/REGISTRO.md#35,si,Sesión calendarizada de 7:00 a 8:00 am; sin grabación
2026-07-07,2026-W28,0,E0-02,Preparación y documentación de Discovery de cobranza,0.75,validada,fuentes/2026-07-07 - Notas - Proceso actual de cobranza (sin grabacion).md,si,"Sin grabación; notas escritas a mano; ajustada a la baja por Johann 27-jul"
2026-07-08,2026-W28,0,E0-05,Diseño y construcción del prototipo,3.00,validada,prototipo/Prototipo-Etapa0.html,si,Primera mitad del esfuerzo reconstruido; validada por Johann 27-jul
2026-07-09,2026-W28,0,E0-05,Ajustes de cobranza navegación y prototipo,3.00,validada,prototipo/Prototipo-Etapa0.html,si,Segunda mitad del esfuerzo reconstruido; validada por Johann 27-jul
2026-07-10,2026-W28,0,E0-05,"Pulido final del prototipo (bandeja, modal Edo. cuenta, login, capturas)",1.50,validada,prototipo/Prototipo-Etapa0.html,si,"6 commits 00:44-01:52 (b358570..8da11ea) + 9 capturas + logo oficial; validada por Johann 27-jul"
2026-07-10,2026-W28,0,E0-06,Entregable de cierre Etapa 0: PDF hallazgos plan refinado y correo,1.50,validada,prototipo/Correo-prototipo-Etapa0.md,si,PDF generado 10:43; hallazgos y plan refinado; validada por Johann 27-jul
2026-07-13,2026-W29,1,E1-01,Backend base autenticación roles y auditoría,2.00,validada,planeacion/Plan-actividades.md,si,validada por Johann 27-jul
2026-07-13,2026-W29,1,E1-02,Coordinación del repositorio GitHub,0.50,validada,bitacora/REGISTRO.md#43,si,Seguimiento de creación y acceso; validada por Johann 27-jul
2026-07-14,2026-W29,1,E1-01,Backend base autenticación roles y auditoría,1.50,validada,planeacion/Plan-actividades.md,si,Trabajo local; completa 3.50 h; validada por Johann 27-jul
2026-07-14,2026-W29,1,E1-03,Arquitectura multi-tenant,1.00,validada,planeacion/Plan-actividades.md,si,Diseño inicial tenant_id y RLS; validada por Johann 27-jul
2026-07-15,2026-W29,1,E1-03,Arquitectura multi-tenant,0.50,validada,planeacion/Plan-actividades.md,si,Completa 1.50 h; validada por Johann 27-jul
2026-07-15,2026-W29,1,E1-04,Cliente productivo de API BIND,2.00,validada,bind-api-sandbox/VALIDACION-API.md,si,Inicio de reintentos OData y manejo de errores; validada por Johann 27-jul
2026-07-15,2026-W29,1,E1-05,Seguimiento ajuste de calendario y corte de horas,0.50,validada,bitacora/REGISTRO.md#41-45,si,Actualización de plan y control; validada por Johann 27-jul
2026-07-16,2026-W29,1,E1-06,Scaffold backend .NET 10 + frontend Angular 21 y push al repo de Balam,2.00,validada,bitacora/REGISTRO.md#46,si,Commits 220942a..1f7f098; incluye CLAUDE.md del repo; validada por Johann 27-jul
2026-07-16,2026-W29,1,E1-05,Corte de avance Etapa 1 para Erika (Excel + mensaje),1.00,validada,planeacion/Avance-Etapa1-2026-07-16.md,si,Enviado 8:40 pm por WhatsApp con acuse; validada por Johann 27-jul
2026-07-19,2026-W29,1,E1-06,"Preparación de entorno local (clon del repo, SDK .NET 10, secrets, app corriendo)",1.00,validada,bitacora/REGISTRO.md#47,si,API y frontend verificados en local; 13/13 pruebas verdes; validada por Johann 27-jul
2026-07-19,2026-W29,1,E1-08,"Diseño del plan de ejecución de Etapa 1 (bloques B0-B10, ADRs, orden)",2.00,validada,planeacion/Plan-Etapa1.md,si,Arquitectura y decisiones previas a las sesiones de desarrollo; validada por Johann 27-jul
2026-07-19,2026-W29,1,E1-06,Scalar UI para el OpenAPI del backend,0.25,confirmada,bitacora/REGISTRO.md#48,si,Sesión asistida por IA; /scalar/v1 verificado
2026-07-19,2026-W29,1,E1-01,B0+B1: Postgres 17 local + modelo de datos + Identity + migración Inicial + seed,1.00,confirmada,bitacora/REGISTRO.md#48,si,Sesión asistida por IA (noche del domingo); 16 tablas migradas y siembra verificada; tiempo real de sesión
2026-07-19,2026-W29,1,E1-01,B2: login JWT + policies por rol + bitácora de auditoría,0.50,confirmada,bitacora/REGISTRO.md#49,si,Sesión asistida por IA; matriz 401/403/200 verificada end-to-end; tiempo real de sesión
2026-07-19,2026-W29,1,E1-01,B3: sync de clientes contra BIND real + fix de modelos (GUIDs null),0.50,confirmada,bitacora/REGISTRO.md#50,si,"Sesión asistida por IA; 16 clientes sincronizados, idempotencia verificada; tiempo real de sesión"
2026-07-21,2026-W30,1,E1-01,B4: sync de facturas/cotizaciones + estados + cartera por moneda + sync histórico,1.00,confirmada,bitacora/REGISTRO.md#52,si,"Sesión asistida por IA; 1494 facturas y 43 cotizaciones sincronizadas de producción; tiempo real de sesión"
2026-07-21,2026-W30,1,E1-03,B6: RLS real de Postgres por tenant (migración + interceptor + filtros EF),0.50,confirmada,bitacora/REGISTRO.md#53,si,"Sesión asistida por IA; verificación manual en psql; tiempo real de sesión"
2026-07-21,2026-W30,1,E1-05,Cierre de sesión: Excel de avance regenerado + bitácora + control de horas,0.25,confirmada,bitacora/REGISTRO.md#52-53,si,"Sesión asistida por IA; tiempo real de sesión"
2026-07-22,2026-W30,1,E1-05,"Sesión de reglas Etapa 1 con Noé, Arturo y Araceli (alta de clientes, fee, flujo Jira)",0.80,confirmada,bitacora/REGISTRO.md#54,si,"Duración real ~48 min según transcript (7:007:48 am)"
2026-07-21,2026-W30,1,E1-01,B4: sync de facturas/cotizaciones + estados + cartera por moneda + sync histórico,1.00,confirmada,bitacora/REGISTRO.md#52,si,Sesión asistida por IA; 1494 facturas y 43 cotizaciones sincronizadas de producción; tiempo real de sesión
2026-07-21,2026-W30,1,E1-03,B6: RLS real de Postgres por tenant (migración + interceptor + filtros EF),0.50,confirmada,bitacora/REGISTRO.md#53,si,Sesión asistida por IA; verificación manual en psql; tiempo real de sesión
2026-07-21,2026-W30,1,E1-05,Cierre de sesión: Excel de avance regenerado + bitácora + control de horas,0.25,confirmada,bitacora/REGISTRO.md#52-53,si,Sesión asistida por IA; tiempo real de sesión
2026-07-21,2026-W30,1,E1-05,Preparación de la agenda de la sesión de reglas de Etapa 1,0.75,validada,planeacion/Sesion-Etapa1-2026-07-22.md,si,Agenda y preguntas para la sesión del 22-jul; validada por Johann 27-jul
2026-07-22,2026-W30,1,E1-05,"Sesión de reglas Etapa 1 con Noé, Arturo y Araceli (alta de clientes, fee, flujo Jira)",0.80,confirmada,bitacora/REGISTRO.md#54,si,Duración real ~48 min según transcript (7:007:48 am)
2026-07-23,2026-W30,0,E0-07,Preparación del guion de validación del prototipo,0.45,validada,planeacion/Guion-validacion-prototipo-2026-07-23.md,si,"Guion del recorrido de la demo del 24-jul; ajustada a la baja por Johann 27-jul"
2026-07-24,2026-W30,0,E0-07,"Sesión de validación del prototipo Etapa 0 con Noé, Arturo y Araceli — APROBADO",1.05,confirmada,bitacora/REGISTRO.md#55,si,"Duración ~1 h 4 min según transcript; Etapa 0 validada, queda 1 definición de proceso (flujo Jira)"
2026-07-27,2026-W31,1,E1-07,"Sesión de API de Jira con Pedro (token PAF, bandeja FAC)",0.20,confirmada,bitacora/REGISTRO.md#56,si,"~12 min; token PAF comprometido (1 año) por correo; integración inicia tras definición del flujo 28-jul"
2026-07-27,2026-W31,1,E1-07,"Sesión de API de Jira con Pedro (token PAF, bandeja FAC)",0.20,confirmada,bitacora/REGISTRO.md#56,si,~12 min; token PAF comprometido (1 año) por correo; integración inicia tras definición del flujo 28-jul
1 fecha semana etapa actividad_id actividad horas estado_hora fuente_evidencia facturable notas
2 2026-07-01 2026-W27 0 E0-01 Sesión de arranque con Noé 1.00 confirmada bitacora/REGISTRO.md#22 si Sesión calendarizada de 7:00 a 8:00 am
3 2026-07-01 2026-W27 0 E0-01 Preparación de plan kickoff y materiales 1.00 reconstruida validada planeacion/Kickoff-2026-07-01.md si Estimación retrospectiva; validar antes de facturar validada por Johann 27-jul
4 2026-07-06 2026-W28 0 E0-02 Discovery de facturación y envío 1.08 confirmada bitacora/REGISTRO.md#27 si Duración documentada: aproximadamente 1 h 5 min
5 2026-07-06 2026-W28 0 E0-02 Preparación y análisis de Discovery de facturación 1.92 1.00 reconstruida validada bitacora/REGISTRO.md#27 si Completa 3.00 h para facturación/envío incluyendo sesión Completa 2.08 h con la sesión; ajustada a la baja por Johann 27-jul
6 2026-07-06 2026-W28 0 E0-03 Coordinación y validación de accesos BIND y marca 1.00 reconstruida validada bitacora/REGISTRO.md#28-31 si Recepción documentación y salvaguardas Recepción documentación y salvaguardas; validada por Johann 27-jul
7 2026-07-06 2026-W28 0 E0-04 Validación técnica de API BIND 4.50 reconstruida validada bind-api-sandbox/VALIDACION-API.md si 117 GET análisis y reporte técnico 117 GET análisis y reporte técnico; validada por Johann 27-jul
8 2026-07-07 2026-W28 0 E0-02 Discovery de cobranza 1.00 confirmada bitacora/REGISTRO.md#35 si Sesión calendarizada de 7:00 a 8:00 am; sin grabación
9 2026-07-07 2026-W28 0 E0-02 Preparación y documentación de Discovery de cobranza 1.50 0.75 reconstruida validada fuentes/2026-07-07 - Notas - Proceso actual de cobranza (sin grabacion).md si Estimación retrospectiva Sin grabación; notas escritas a mano; ajustada a la baja por Johann 27-jul
10 2026-07-08 2026-W28 0 E0-05 Diseño y construcción del prototipo 3.00 reconstruida validada prototipo/Prototipo-Etapa0.html si Primera mitad del esfuerzo reconstruido Primera mitad del esfuerzo reconstruido; validada por Johann 27-jul
11 2026-07-09 2026-W28 0 E0-05 Ajustes de cobranza navegación y prototipo 3.00 reconstruida validada prototipo/Prototipo-Etapa0.html si Segunda mitad del esfuerzo reconstruido Segunda mitad del esfuerzo reconstruido; validada por Johann 27-jul
12 2026-07-10 2026-W28 0 E0-06 E0-05 Entregable hallazgos ADRs PDF correo y plan Pulido final del prototipo (bandeja, modal Edo. cuenta, login, capturas) 3.00 1.50 reconstruida validada prototipo/Correo-prototipo-Etapa0.md prototipo/Prototipo-Etapa0.html si Incluye preparación del entregable y documentación 6 commits 00:44-01:52 (b358570..8da11ea) + 9 capturas + logo oficial; validada por Johann 27-jul
13 2026-07-13 2026-07-10 2026-W29 2026-W28 1 0 E1-01 E0-06 Backend base autenticación roles y auditoría Entregable de cierre Etapa 0: PDF hallazgos plan refinado y correo 2.00 1.50 reconstruida validada planeacion/Plan-actividades.md prototipo/Correo-prototipo-Etapa0.md si Trabajo local; falta conciliar contra evidencia de código PDF generado 10:43; hallazgos y plan refinado; validada por Johann 27-jul
14 2026-07-13 2026-W29 1 E1-02 E1-01 Coordinación del repositorio GitHub Backend base autenticación roles y auditoría 0.50 2.00 reconstruida validada bitacora/REGISTRO.md#43 planeacion/Plan-actividades.md si Seguimiento de creación y acceso validada por Johann 27-jul
15 2026-07-14 2026-07-13 2026-W29 1 E1-01 E1-02 Backend base autenticación roles y auditoría Coordinación del repositorio GitHub 1.50 0.50 reconstruida validada planeacion/Plan-actividades.md bitacora/REGISTRO.md#43 si Trabajo local; completa 3.50 h Seguimiento de creación y acceso; validada por Johann 27-jul
16 2026-07-14 2026-W29 1 E1-03 E1-01 Arquitectura multi-tenant Backend base autenticación roles y auditoría 1.00 1.50 reconstruida validada planeacion/Plan-actividades.md si Diseño inicial tenant_id y RLS Trabajo local; completa 3.50 h; validada por Johann 27-jul
17 2026-07-15 2026-07-14 2026-W29 1 E1-03 Arquitectura multi-tenant 0.50 1.00 reconstruida validada planeacion/Plan-actividades.md si Completa 1.50 h Diseño inicial tenant_id y RLS; validada por Johann 27-jul
18 2026-07-15 2026-W29 1 E1-04 E1-03 Cliente productivo de API BIND Arquitectura multi-tenant 2.00 0.50 reconstruida validada bind-api-sandbox/VALIDACION-API.md planeacion/Plan-actividades.md si Inicio de reintentos OData y manejo de errores Completa 1.50 h; validada por Johann 27-jul
19 2026-07-15 2026-W29 1 E1-05 E1-04 Seguimiento ajuste de calendario y corte de horas Cliente productivo de API BIND 0.50 2.00 reconstruida validada bitacora/REGISTRO.md#41-45 bind-api-sandbox/VALIDACION-API.md si Actualización de plan y control Inicio de reintentos OData y manejo de errores; validada por Johann 27-jul
20 2026-07-16 2026-07-15 2026-W29 1 E1-06 E1-05 Scaffold backend .NET 10 + frontend Angular 21 y push al repo de Balam Seguimiento ajuste de calendario y corte de horas 2.00 0.50 reconstruida validada bitacora/REGISTRO.md#46 bitacora/REGISTRO.md#41-45 si Commits 220942a..1f7f098; incluye CLAUDE.md del repo; validar duración Actualización de plan y control; validada por Johann 27-jul
21 2026-07-16 2026-W29 1 E1-05 E1-06 Corte de avance Etapa 1 para Erika (Excel + mensaje) Scaffold backend .NET 10 + frontend Angular 21 y push al repo de Balam 1.00 2.00 reconstruida validada planeacion/Avance-Etapa1-2026-07-16.md bitacora/REGISTRO.md#46 si Enviado 8:40 pm por WhatsApp con acuse; validar duración Commits 220942a..1f7f098; incluye CLAUDE.md del repo; validada por Johann 27-jul
22 2026-07-19 2026-07-16 2026-W29 1 E1-06 E1-05 Preparación de entorno local (clon del repo Corte de avance Etapa 1 para Erika (Excel + mensaje) SDK .NET 10 1.00 secrets validada app corriendo) planeacion/Avance-Etapa1-2026-07-16.md 1.00 si reconstruida Enviado 8:40 pm por WhatsApp con acuse; validada por Johann 27-jul
23 2026-07-19 2026-W29 1 E1-06 Scalar UI para el OpenAPI del backend Preparación de entorno local (clon del repo, SDK .NET 10, secrets, app corriendo) 0.25 1.00 confirmada validada bitacora/REGISTRO.md#48 bitacora/REGISTRO.md#47 si Sesión asistida por IA; /scalar/v1 verificado API y frontend verificados en local; 13/13 pruebas verdes; validada por Johann 27-jul
24 2026-07-19 2026-W29 1 E1-01 E1-08 B0+B1: Postgres 17 local + modelo de datos + Identity + migración Inicial + seed Diseño del plan de ejecución de Etapa 1 (bloques B0-B10, ADRs, orden) 1.00 2.00 confirmada validada bitacora/REGISTRO.md#48 planeacion/Plan-Etapa1.md si Sesión asistida por IA (noche del domingo); 16 tablas migradas y siembra verificada; tiempo real de sesión Arquitectura y decisiones previas a las sesiones de desarrollo; validada por Johann 27-jul
25 2026-07-19 2026-W29 1 E1-01 E1-06 B2: login JWT + policies por rol + bitácora de auditoría Scalar UI para el OpenAPI del backend 0.50 0.25 confirmada bitacora/REGISTRO.md#49 bitacora/REGISTRO.md#48 si Sesión asistida por IA; matriz 401/403/200 verificada end-to-end; tiempo real de sesión Sesión asistida por IA; /scalar/v1 verificado
26 2026-07-19 2026-W29 1 E1-01 B0+B1: Postgres 17 local + modelo de datos + Identity + migración Inicial + seed 1.00 confirmada bitacora/REGISTRO.md#48 si Sesión asistida por IA (noche del domingo); 16 tablas migradas y siembra verificada; tiempo real de sesión
27 2026-07-19 2026-W29 1 E1-01 B2: login JWT + policies por rol + bitácora de auditoría 0.50 confirmada bitacora/REGISTRO.md#49 si Sesión asistida por IA; matriz 401/403/200 verificada end-to-end; tiempo real de sesión
28 2026-07-19 2026-W29 1 E1-01 B3: sync de clientes contra BIND real + fix de modelos (GUIDs null) 0.50 confirmada bitacora/REGISTRO.md#50 si Sesión asistida por IA; 16 clientes sincronizados, idempotencia verificada; tiempo real de sesión
29 2026-07-21 2026-W30 1 E1-01 B4: sync de facturas/cotizaciones + estados + cartera por moneda + sync histórico 1.00 confirmada bitacora/REGISTRO.md#52 si Sesión asistida por IA; 1494 facturas y 43 cotizaciones sincronizadas de producción; tiempo real de sesión
30 2026-07-21 2026-W30 1 E1-03 B6: RLS real de Postgres por tenant (migración + interceptor + filtros EF) 0.50 confirmada bitacora/REGISTRO.md#53 si Sesión asistida por IA; verificación manual en psql; tiempo real de sesión
31 2026-07-21 2026-W30 1 E1-05 Cierre de sesión: Excel de avance regenerado + bitácora + control de horas 0.25 confirmada bitacora/REGISTRO.md#52-53 si Sesión asistida por IA; tiempo real de sesión
32 2026-07-22 2026-07-21 2026-W30 1 E1-05 Sesión de reglas Etapa 1 con Noé, Arturo y Araceli (alta de clientes, fee, flujo Jira) Preparación de la agenda de la sesión de reglas de Etapa 1 0.80 0.75 confirmada validada bitacora/REGISTRO.md#54 planeacion/Sesion-Etapa1-2026-07-22.md si Duración real ~48 min según transcript (7:00–7:48 am) Agenda y preguntas para la sesión del 22-jul; validada por Johann 27-jul
33 2026-07-22 2026-W30 1 E1-05 Sesión de reglas Etapa 1 con Noé, Arturo y Araceli (alta de clientes, fee, flujo Jira) 0.80 confirmada bitacora/REGISTRO.md#54 si Duración real ~48 min según transcript (7:00–7:48 am)
34 2026-07-23 2026-W30 0 E0-07 Preparación del guion de validación del prototipo 0.45 validada planeacion/Guion-validacion-prototipo-2026-07-23.md si Guion del recorrido de la demo del 24-jul; ajustada a la baja por Johann 27-jul
35 2026-07-24 2026-W30 0 E0-07 Sesión de validación del prototipo Etapa 0 con Noé, Arturo y Araceli — APROBADO 1.05 confirmada bitacora/REGISTRO.md#55 si Duración ~1 h 4 min según transcript; Etapa 0 validada, queda 1 definición de proceso (flujo Jira)
36 2026-07-27 2026-W31 1 E1-07 Sesión de API de Jira con Pedro (token PAF, bandeja FAC) 0.20 confirmada bitacora/REGISTRO.md#56 si ~12 min; token PAF comprometido (1 año) por correo; integración inicia tras definición del flujo 28-jul
+51 -31
View File
@@ -5,7 +5,7 @@ Fuente local de horas efectivamente trabajadas para el proyecto Balam. Se mantie
**Tarifa contractual:** $600 MXN/h + IVA
**Cadencia:** corte semanal; factura los viernes; pago a 30 días
**Datos tabulares:** [`Seguimiento-horas.csv`](Seguimiento-horas.csv)
**Última actualización:** 2026-07-15
**Última actualización:** 2026-07-27
## Reglas de captura
@@ -16,47 +16,67 @@ Fuente local de horas efectivamente trabajadas para el proyecto Balam. Se mantie
5. No completar retrospectivamente una duración sin respaldo. Si solo se conoce la actividad, dejarla en la lista de reconstrucción hasta que Johann confirme el tiempo.
6. Al habilitarse Jira, conciliar ambos registros y evitar duplicados.
## Corte reconstruido al 15-jul
## Corte al 27-jul
| Etapa | Horas al corte | Rango estimado de la etapa | Estado |
|---|---:|---:|---|
| Etapa 0 | **22.00 h** | 1822 h | Límite superior consumido; prototipo pendiente de validación |
| Etapa 1 | **8.00 h** | 3239 h | En curso |
| **Total** | **30.00 h** | | Coincide con la facturación inicial propuesta |
| Etapa 0 | **21.83 h** | 1822 h | Cerrada y **validada por Balam el 24-jul**; dentro del rango |
| Etapa 1 | **19.75 h** | 3239 h | En curso; 6 de 8 entregables terminados |
| **Total** | **41.58 h** | | |
Las **26.92 h** que no corresponden a sesiones con duración documentada se reconstruyeron retrospectivamente por actividad. Deben validarse contra memoria, archivos de trabajo y Jira antes de presentarse como horas efectivamente trabajadas.
**Estado de validación de las horas (27-jul):**
## Horas con duración comprobable
| Estado | Horas | Significado |
|---|---:|---|
| `confirmada` | 9.13 h | Duración documentada (transcripts, sesiones calendarizadas, tiempo de sesión) |
| `validada` | 32.45 h | Reconstruida por actividad y **confirmada por Johann el 27-jul** contra evidencia fechada |
| **Total** | **41.58 h** | Sin horas pendientes de validar |
| Semana | Etapa | Horas confirmadas | Alcance incluido |
|---|---:|---:|---|
| 2026-W27 | 0 | 1.00 | Kickoff |
| 2026-W28 | 0 | 2.08 | Discovery de facturación/envío y cobranza |
| **Total comprobable** | | **3.08** | Solo sesiones con duración documentada |
## Distribución al 27-jul
> Las 3.08 h están incluidas dentro del corte reconstruido de 30 h; no deben sumarse nuevamente.
| Etapa | Actividad | ID | Horas |
|---|---|---|---:|
| 0 | Kickoff y preparación | E0-01 | 2.00 |
| 0 | Discovery de facturación, envío y cobranza | E0-02 | 3.83 |
| 0 | Accesos y salvaguardas | E0-03 | 1.00 |
| 0 | Validación técnica de la API de BIND | E0-04 | 4.50 |
| 0 | Prototipo navegable (incl. pulido final del 10-jul) | E0-05 | 7.50 |
| 0 | Entregable de cierre: PDF, hallazgos, plan y correo | E0-06 | 1.50 |
| 0 | Validación del prototipo con Balam (guion + sesión) | E0-07 | 1.50 |
| 1 | Backend base, auth, roles, bitácora y syncs (B0B4) | E1-01 | 6.50 |
| 1 | Coordinación del repositorio | E1-02 | 0.50 |
| 1 | Arquitectura multi-tenant y RLS (B6) | E1-03 | 2.00 |
| 1 | Cliente productivo de la API de BIND | E1-04 | 2.00 |
| 1 | Gestión, cortes de avance y sesión de reglas | E1-05 | 3.30 |
| 1 | Entorno local, scaffold y Scalar | E1-06 | 3.25 |
| 1 | Sesión de API de Jira | E1-07 | 0.20 |
| 1 | Diseño del plan de ejecución de Etapa 1 | E1-08 | 2.00 |
| | **Total** | | **41.58** |
## Distribución reconstruida
> **Ajustes a la baja aplicados por Johann el 27-jul:** el análisis del Discovery de facturación bajó de 1.92 a 1.00 h, la documentación del de cobranza de 1.50 a 0.75 h, y el guion de validación de 0.75 a 0.45 h. Las duraciones de sesión documentadas en transcript **no se tocaron**. Con esto Etapa 0 vuelve a quedar dentro del rango contractual de 1822 h.
Johann debe confirmar esta distribución antes de usarla para facturación:
### Nota sobre las sesiones asistidas por IA
| Etapa | Actividad consolidada | Horas |
|---|---|---:|
| 0 | Kickoff y preparación | 2.00 |
| 0 | Discovery de facturación, envío y cobranza | 5.50 |
| 0 | Accesos y salvaguardas | 1.00 |
| 0 | Validación técnica de BIND | 4.50 |
| 0 | Prototipo navegable | 6.00 |
| 0 | Entregable, hallazgos, ADRs y plan | 3.00 |
| 1 | Backend base | 3.50 |
| 1 | Arquitectura multi-tenant | 1.50 |
| 1 | Cliente productivo de BIND | 2.00 |
| 1 | Repositorio y seguimiento | 1.00 |
| | **Total** | **30.00** |
Los bloques B0B6 de Etapa 1 (auth, RLS, cliente de BIND, sincronización de 1,494 facturas de producción) se ejecutaron en **3.50 h de tiempo de reloj** contra una estimación manual de 45.5 h *por bloque*. Se registra el tiempo efectivamente trabajado, conforme al contrato. El trabajo intelectual que hizo posible esa velocidad —diseño de arquitectura, decisión de ADRs y plan de ejecución— se registra por separado en **E1-08 (2.00 h)**.
Consecuencia a vigilar: **Etapa 1 cerrará por debajo del rango de 3239 h.** Conviene reportar avance por entregable (6 de 8) y no solo por horas, para que la eficiencia no se lea como alcance faltante.
### Nota sobre el mapeo CSV → Excel
El CSV agrupa por **actividad contractual**; el Excel desglosa por **tarea de proyecto**, y no mapean 1:1. El reparto vive en `HORAS_POR_FILA` dentro de `generar_avance_2026_07_27.py`, con la trazabilidad comentada línea por línea, y el script **aborta** si la suma del detalle no cuadra con el CSV.
Dos casos que conviene recordar:
- **E1-06 (entorno, scaffold, Scalar — 3.25 h)** no tiene fila propia: se carga a "Backend base", que se renombró y amplió su rango a 89 h.
- **E1-08 (diseño del plan — 2.00 h)** se reparte en dos: **1.00 h** de diseño general va a "Arquitectura multi-tenant y diseño de la solución" (rango 34 h) y **1.00 h** del análisis de B7 —pipeline `Draft→DryRun→Confirm`, flags, regla PPD/PUE, ADR-001— va a la fila del **entregable 4 (capa de escritura)**, para que muestre el trabajo hecho aunque la implementación siga en pausa. La entidad `WriteOperation` y la policy `PuedeOperarEscritura` **no** se contabilizan ahí: se hicieron en B1/B2 y ya están en "Backend base" (evita doble conteo).
- La fila de **seguimiento semanal se queda solo con gestión y cortes de avance (1.75 h)**. Antes acumulaba 7.00 h de trabajo técnico bajo un nombre que no lo describía, lo que se leía como sobrecosto en una actividad trivial.
- La sesión **B4 (1.00 h)** se reparte entre "Sincronización de facturas" (0.50 h) y "Modelo de facturas" (0.50 h): produjo ambas cosas —el sync y el `InvoiceStatusMapper` con sus 13 pruebas más la migración `DetalleSyncPendiente`—, y antes el modelo aparecía al 85 % con 0 h.
**Criterio general:** ninguna fila debe mostrar avance con 0 h trabajadas. Las únicas excepciones legítimas son Azure y CI/CD, que están en 0 % y 0 h porque dependen de accesos del lado de Balam.
## Corte comercial
- La propuesta v1.2 contempla una **facturación inicial de 30 h ($18,000 + IVA)** que cubre Etapa 0 e inicio de Etapa 1.
- Esas 30 h son el concepto comercial autorizado en la propuesta, pero el contrato exige reportar horas efectivamente trabajadas.
- Antes de emitir, Johann debe validar el desglose reconstruido y conciliarlo contra Jira.
- La emisión sigue pendiente de confirmación de Balam al correo del 2-jul.
- Al 27-jul hay **41.58 h trabajadas**, es decir **11.58 h por encima** de esa facturación inicial. A tarifa de $600/h son **$6,948 + IVA** aún no facturados.
- Todas las horas están validadas (ninguna pendiente de confirmar), por lo que el desglose ya es presentable para facturación.
- Pendiente: conciliar contra Jira cuando Pedro lo habilite, y confirmar con Balam si la segunda factura va por el excedente o por corte de etapa.
- La emisión de la factura inicial sigue pendiente de confirmación de Balam al correo del 2-jul.
+190 -36
View File
@@ -1,16 +1,28 @@
"""Genera Plan-actividades-avance-2026-07-27.xlsx a partir del del 21-jul.
Aplica las horas y estatus reales al 27-jul: sesión de reglas (22-jul) y
validación del prototipo (24-jul, APROBADO) completadas; sesión de API de Jira
(27-jul) en curso; capa de escritura EN PAUSA a la espera de la definición del
flujo de facturación (sesión 28-jul con Arturo/Araceli).
Reutiliza el estilo/semáforo del archivo del 21-jul."""
import re
from datetime import date
import openpyxl
from openpyxl.styles import Font, Alignment, PatternFill, Border, Side
SRC = "/Users/johann/Desktop/Proyectos personales/BALAM/planeacion/Plan-actividades-avance-2026-07-21.xlsx"
DST = "/Users/johann/Desktop/Proyectos personales/BALAM/planeacion/Plan-actividades-avance-2026-07-27.xlsx"
Diferencia clave vs. versiones anteriores: las HORAS TRABAJADAS ya no se escriben a
mano se leen de `Seguimiento-horas.csv` (fuente de la verdad) y se agregan por
actividad del plan mediante el mapa ACT_POR_TAREA. Así cada celda de horas del
Excel es trazable a una o varias líneas del CSV, y el total del encabezado de cada
etapa es la suma real de su detalle.
Corte al 27-jul: validación del prototipo (24-jul, APROBADO) y sesión de reglas
(22-jul) completadas; API de Jira (27-jul) recibida; capa de escritura EN PAUSA a
la espera de la definición del flujo de facturación (sesión 28-jul).
"""
import csv
import os
import re
from collections import defaultdict
from datetime import date
import openpyxl
from openpyxl.styles import Alignment, Font, PatternFill
BASE = os.path.dirname(os.path.abspath(__file__))
SRC = os.path.join(BASE, "Plan-actividades-avance-2026-07-21.xlsx")
DST = os.path.join(BASE, "Plan-actividades-avance-2026-07-27.xlsx")
CSV_HORAS = os.path.join(BASE, "Seguimiento-horas.csv")
HOY = date(2026, 7, 27)
CAFE, CREMA, GRIS_B, AMARILLO = "331F0E", "FDF3D8", "D9D9D9", "F7BD0C"
@@ -21,17 +33,115 @@ G_FILL, G_FONT = "F2F2F2", "595959" # gris
C_PCT, C_BLQ, C_EST, C_TRAB, C_STAT = 7, 8, 9, 10, 11
# Prefijo de la actividad -> overrides de datos (pct=%, trab=horas, comienzo/fin=fechas texto)
# --- Horas por fila del Excel --------------------------------------------------
# Las actividades del CSV no mapean 1:1 con las filas del plan (el CSV agrupa por
# actividad contractual; el Excel desglosa por tarea de proyecto). Se reparten por
# fila usando la evidencia de la bitácora, de modo que cada fila muestre el trabajo
# que realmente le corresponde y la suma siga cuadrando con el CSV.
#
# Trazabilidad de Etapa 1 (19.75 h del CSV):
# E1-01 6.50 = backend base 3.50 (13-14 jul) + B0/B1 1.00 + B2 0.50 + B3 0.50 + B4 1.00
# E1-02 0.50 = repositorio GitHub
# E1-03 2.00 = multi-tenant 1.50 (14-15 jul) + B6 RLS 0.50
# E1-04 2.00 = cliente de BIND
# E1-05 3.30 = gestión 0.50 + corte 16-jul 1.00 + cierre 21-jul 0.25 + prep 0.75 + sesión 0.80
# E1-06 3.25 = scaffold 2.00 + entorno local 1.00 + Scalar 0.25
# E1-07 0.20 = sesión API de Jira
# E1-08 2.00 = diseño del plan de Etapa 1
HORAS_POR_FILA = {
# --- Etapa 0 (21.83 h) — una actividad del CSV por fila
# E0-02 3.83 = sesión facturación 1.08 + análisis 1.00 + sesión cobranza 1.00 + notas 0.75
# E0-07 1.50 = guion 0.45 + sesión de validación 1.05 (transcript: 1 h 4 min)
"Sesión de arranque (kickoff)": 2.00, # E0-01
"Sesión de Discovery": 3.83, # E0-02
"Entrega y validación de accesos": 1.00, # E0-03
"Validación técnica de la API de BIND": 4.50, # E0-04
"Prototipo visual navegable": 7.50, # E0-05
"Documento de hallazgos": 1.50, # E0-06
"Sesión de validación del prototipo": 1.50, # E0-07 (guion 0.45 + sesión 1.05)
# --- Etapa 1 (19.75 h)
# El trabajo de andamiaje (entorno local, scaffold, Scalar) y el diseño del plan
# no tienen fila propia en el plan original: se cargan a la fila técnica que les
# corresponde por naturaleza, no a la de seguimiento semanal.
"Repositorio privado GitHub": 0.50, # E1-02
"Backend base": 8.25, # E1-01 5.00 + E1-06 3.25 (entorno, scaffold, Scalar)
"Arquitectura multi-tenant": 3.00, # E1-03 2.00 (incl. B6 RLS) + E1-08 1.00 (diseño general)
"Cliente de la API de BIND": 2.00, # E1-04
"Sincronización de clientes": 0.50, # E1-01 parte: B3
# B4 produjo dos cosas: el sync de facturas/cotizaciones y el modelo central
# (InvoiceStatusMapper con 13 pruebas + migración DetalleSyncPendiente). Se
# reparte para que ninguna fila quede en 0 h marcada como avanzada.
"Sincronización de facturas": 0.50, # E1-01 parte: B4 (sync)
"Modelo de facturas": 0.50, # E1-01 parte: B4 (mapper + migraciones)
"Sesión de reglas y dudas": 1.55, # E1-05 parte: prep 0.75 + sesión 0.80
"Definición de alcance Jira": 0.20, # E1-07
"Demostración semanal": 1.75, # E1-05 resto: gestión 0.50 + corte 1.00 + cierre 0.25
# Análisis y diseño de B7 (pipeline Draft→DryRun→Confirm, flags, regla PPD/PUE,
# ADR-001): 1.00 h de E1-08, separada del diseño general para que la fila del
# entregable 4 muestre el trabajo que sí se hizo. La implementación sigue en pausa.
"Capa de escritura controlada": 1.00,
# --- Sin horas aún (trabajo no iniciado o dependiente de Balam)
"Configuración de infraestructura en Azure": 0.00,
"CI/CD": 0.00,
}
def horas_por_actividad(path):
"""Suma horas del CSV por actividad_id y por etapa."""
por_act = defaultdict(float)
por_etapa = defaultdict(float)
with open(path, encoding="utf-8", newline="") as fh:
rdr = csv.DictReader(fh)
for i, r in enumerate(rdr, start=2):
if not (r.get("fecha") or "").strip():
continue
if None in r or any(v is None for v in r.values()):
raise ValueError(
f"{os.path.basename(path)} línea {i}: número de campos inesperado "
f"(¿falta escapar una coma con comillas?)")
h = float(r["horas"])
por_act[r["actividad_id"]] += h
por_etapa[r["etapa"]] += h
return por_act, por_etapa
HORAS_ACT, HORAS_ETAPA = horas_por_actividad(CSV_HORAS)
TOTAL_CSV = sum(HORAS_ETAPA.values())
# % por entregables terminados (dato duro y defendible, no promedio ponderado).
# Etapa 1: 6 de 8 entregables contractuales TERMINADOS (1,2,3,5,6,7) = 75%. Los dos
# restantes van parciales y su avance se refleja en su propia fila (entregable 4 al
# 40%, entregable 8 al 85%), no promediado en el encabezado: la etapa se mide por
# entregable cerrado, que es lo verificable.
PCT_ETAPA = {"0": 1.0, "1": 0.75}
OVERRIDES = {
"Integracion de plataformas Balam": dict(pct=0.67, trab=40),
"Etapa 0": dict(pct=1.0, trab=23),
"Integracion de plataformas Balam": dict(pct=0.80, trab=TOTAL_CSV),
"Etapa 0": dict(pct=PCT_ETAPA["0"], trab=HORAS_ETAPA["0"]),
"Etapa 1": dict(pct=PCT_ETAPA["1"], trab=HORAS_ETAPA["1"]),
"Documento de hallazgos": dict(pct=1.0),
"Sesión de validación del prototipo": dict(pct=1.0, trab=1.05,
comienzo="vie 24/07/26", fin="vie 24/07/26"),
"Etapa 1": dict(pct=0.82, trab=17),
"Demostración semanal": dict(trab=1.75),
"Sesión de reglas y dudas": dict(pct=1.0, trab=0.80),
"Definición de alcance Jira": dict(pct=0.40, trab=0.20),
"Sesión de validación del prototipo": dict(pct=1.0, comienzo="vie 24/07/26",
fin="vie 24/07/26"),
# Estas dos filas absorben el andamiaje y el diseño del plan: se renombran y se
# amplía su rango estimado para que el nombre refleje el alcance que cubren.
"Backend base": dict(
nombre="Backend base: entorno, andamiaje, autenticación, roles y bitácora",
est="89"),
"Arquitectura multi-tenant": dict(
nombre="Arquitectura multi-tenant (tenant_id + RLS) y diseño de la solución",
est="34"),
"Demostración semanal": dict(est="23"),
# B7: diseño, ADR-001, entidad WriteOperation (migrada en B1) y policy
# PuedeOperarEscritura ya existen; falta BindWriteClient, el pipeline y los
# shapes de los POST (dependen de las reglas del 28-jul). Sin horas propias:
# las del modelo y la policy ya están cobradas en B1/B2 ("Backend base").
"Capa de escritura controlada": dict(pct=0.40),
"Sesión de reglas y dudas": dict(pct=1.0),
"Sincronización de clientes": dict(pct=1.0),
"Sincronización de facturas": dict(pct=1.0),
# Conectividad resuelta el 27-jul (token PAF a 1 año, API read+write, bandeja FAC,
# límites de consumo). Queda el 10 %: qué estatus dispara qué, amarrado al 28-jul.
"Definición de alcance Jira": dict(pct=0.90),
}
# Estatus a mano (gana sobre el semáforo automático): texto, fill, font, negrita
@@ -39,37 +149,51 @@ K_ESPECIAL = {
"Documento de hallazgos": ("✅ Completada", V_FILL, V_FONT, False),
"Sesión de validación del prototipo": ("✅ Validado (aprobado)", V_FILL, V_FONT, True),
"Demostración semanal": ("🟡 En curso (semanal)", A_FILL, A_FONT, False),
"Modelo de facturas": ("🟡 En curso · falta seeder", A_FILL, A_FONT, False),
"Capa de escritura controlada": ("🔴 En pausa · def. Balam 28-jul", R_FILL, R_FONT, True),
"Modelo de facturas": ("🟡 Modelo y migraciones ✓ · falta seeder", A_FILL, A_FONT, False),
"Capa de escritura controlada": ("🔴 Diseño listo · impl. en pausa (def. Balam 28-jul)",
R_FILL, R_FONT, True),
"Sesión de reglas y dudas": ("✅ Completada", V_FILL, V_FONT, False),
"Definición de alcance Jira": ("🟡 En curso · API Jira 27-jul", A_FILL, A_FONT, False),
"Definición de alcance Jira": ("🟡 Conectividad lista · falta regla 28-jul",
A_FILL, A_FONT, False),
"Configuración de infraestructura en Azure": ("🟡 Pendiente (Balam)", A_FILL, A_FONT, False),
"CI/CD": ("🔴 Pendiente · dep. Azure", R_FILL, R_FONT, True),
}
def fecha(celda):
m = re.search(r"(\d{2})/(\d{2})/(\d{2})", str(celda or ""))
return date(2000 + int(m.group(3)), int(m.group(2)), int(m.group(1))) if m else None
def pinta(celda, fill, font_color, bold=False):
celda.fill = PatternFill("solid", fgColor=fill)
celda.font = Font(color=font_color, bold=bold, size=10)
celda.alignment = Alignment(horizontal="center", vertical="center")
wb = openpyxl.load_workbook(SRC)
ws = wb.active
# 1) Aplica overrides de datos por prefijo de nombre
# 1) Horas trabajadas desde el CSV + overrides de datos por prefijo de nombre
for row in range(2, ws.max_row + 1):
nombre = str(ws.cell(row=row, column=1).value or "").strip()
if not nombre:
continue
# 1a) horas por fila (reparto trazable al CSV; ver HORAS_POR_FILA)
for prefijo, h in HORAS_POR_FILA.items():
if nombre.startswith(prefijo):
ws.cell(row=row, column=C_TRAB, value=h)
break
# 1b) overrides de %, fechas, nombre y rango estimado
for prefijo, ov in OVERRIDES.items():
if nombre.startswith(prefijo):
if "pct" in ov: ws.cell(row=row, column=C_PCT, value=ov["pct"])
if "trab" in ov: ws.cell(row=row, column=C_TRAB, value=ov["trab"])
if "trab" in ov: ws.cell(row=row, column=C_TRAB, value=round(ov["trab"], 2))
if "comienzo" in ov: ws.cell(row=row, column=3, value=ov["comienzo"])
if "fin" in ov: ws.cell(row=row, column=4, value=ov["fin"])
if "est" in ov: ws.cell(row=row, column=C_EST, value=ov["est"])
break
# 2) Recalcula el semáforo con HOY=27-jul (mismas reglas que el del 21-jul)
@@ -103,7 +227,7 @@ for row in range(2, ws.max_row + 1):
elif pct > 0: pinta(pcel, A_FILL, A_FONT, bold=True)
elif fin and fin < HOY: pinta(pcel, R_FILL, R_FONT, bold=True)
pcel.number_format = "0%"
ws.cell(row=row, column=C_TRAB).number_format = "0.0"
ws.cell(row=row, column=C_TRAB).number_format = "0.00"
# 3) Overrides finos de estatus (bloqueos y dependencias)
for row in range(2, ws.max_row + 1):
@@ -115,32 +239,62 @@ for row in range(2, ws.max_row + 1):
pinta(stat, fill, font, bold)
break
# 4) Encabezado de la columna de estatus y pie
# 4) Renombra las filas que absorbieron alcance (al final: los pasos 2-3 buscan por
# el nombre original)
for row in range(2, ws.max_row + 1):
nombre = str(ws.cell(row=row, column=1).value or "").strip()
for prefijo, ov in OVERRIDES.items():
if "nombre" in ov and nombre.startswith(prefijo):
ws.cell(row=row, column=1, value=ov["nombre"])
break
# 5) Encabezado de la columna de estatus
ws.cell(row=1, column=C_STAT, value=f"Estatus (hoy {HOY.day}-jul)")
# 5) Reescribe leyenda/notas (borra las viejas de la línea de HOY del 21-jul)
# 6) Reescribe leyenda/notas (borra las viejas)
for row in range(ws.max_row, 1, -1):
v = str(ws.cell(row=row, column=1).value or "")
if v.startswith("✅ Completada ·") or v.startswith("▬ La línea roja"):
if v.startswith(("✅ Completada ·", "▬ La línea roja", "Núcleo de la Etapa 1",
"🔴 La capa de escritura", "API de Jira:", "Horas:")):
for col in range(1, C_STAT + 1):
ws.cell(row=row, column=col).value = None
ley = ws.max_row + 2
notas = [
("✅ Completada · 🟡 En curso · 🔴 Vencida/Pendiente · ⚪ Próxima", G_FONT, True),
("Núcleo de la Etapa 1 construido y verificado contra producción: autenticación y roles, "
"bitácora, multi-tenant con RLS, cliente de BIND, sincronización de clientes/facturas/"
"cotizaciones, modelo de facturas con estados y cartera por moneda.", CAFE, False),
("🔴 La capa de escritura (cotización→factura) queda EN PAUSA a la espera de la definición "
"del flujo de facturación (sesión del martes 28-jul con Arturo y Araceli).", "9C0006", False),
("API de Jira: token recibido el 27-jul; la integración inicia una vez definido el flujo.", CAFE, False),
(f"Horas: {HORAS_ETAPA['0']:.2f} h de Etapa 0 y {HORAS_ETAPA['1']:.2f} h de Etapa 1 "
f"({TOTAL_CSV:.2f} h en total), conforme al control local de horas trabajadas. "
f"Etapa 1 estimada en 3239 h.", CAFE, False),
("Etapa 1: 6 de 8 entregables contractuales terminados — autenticación y roles con "
"bitácora, multi-tenant con RLS, cliente de BIND, sincronización de clientes, "
"sincronización de facturas y cotizaciones, y modelo de facturas con estados y "
"cartera por moneda. Verificado contra producción: 1,494 facturas sincronizadas "
"con la cartera cuadrada.", CAFE, False),
("🔴 La capa de escritura (cotización→factura) queda EN PAUSA a la espera de la "
"definición del flujo de facturación (sesión del martes 28-jul con Arturo y Araceli).",
"9C0006", False),
("Pendientes menores: datos de prueba (seeder) y verificación del aislamiento RLS "
"con rol no-superusuario. Azure y CI/CD dependen de accesos del lado de Balam.", CAFE, False),
("API de Jira: token recibido el 27-jul; la integración inicia una vez definido el flujo.",
CAFE, False),
]
for i, (txt, color, bold) in enumerate(notas):
c = ws.cell(row=ley + i, column=1, value=txt)
c.font = Font(italic=True, size=9, color=color, bold=bold)
ws.freeze_panes = "A2"
ws.oddFooter.center.text = "Avance al 27-jul-2026 · Horas conforme al control local (pendiente conciliar con Jira)"
ws.oddFooter.center.text = (f"Avance al 27-jul-2026 · {TOTAL_CSV:.2f} h trabajadas "
"conforme al control local (pendiente conciliar con Jira)")
wb.save(DST)
# --- Verificación: el detalle debe cuadrar con el encabezado de cada etapa -----
print("OK ->", DST)
print(f" Etapa 0: {HORAS_ETAPA['0']:.2f} h · Etapa 1: {HORAS_ETAPA['1']:.2f} h "
f"· TOTAL {TOTAL_CSV:.2f} h")
suma_detalle = sum(HORAS_POR_FILA.values())
ok = abs(suma_detalle - TOTAL_CSV) < 0.01
print(f" Suma del detalle mostrado: {suma_detalle:.2f} h "
f"({'cuadra con el CSV' if ok else f'NO CUADRA (CSV {TOTAL_CSV:.2f})'})")
if not ok:
raise SystemExit("El detalle por fila no cuadra con el CSV — revisar HORAS_POR_FILA.")
@@ -0,0 +1,175 @@
"""Genera Avance-Balam-2026-07-27.xlsx — versión SIMPLE para enviar a Erika.
Se construye desde el Excel interno (`Plan-actividades-avance-2026-07-27.xlsx`),
que a su vez lee las horas de `Seguimiento-horas.csv`. Así los dos archivos nunca
se contradicen: este es una vista reducida, no una segunda fuente de datos.
Formato pedido por Erika el 21-jul: entregable + actividad + horas por etapa.
Se quitan del interno: rangos estimados, bloques del plan (B1/B4/B9), predecesoras
y duración en días ruido para quien solo da seguimiento.
"""
import os
import re
from datetime import date
import openpyxl
from openpyxl.styles import Alignment, Border, Font, PatternFill, Side
from openpyxl.utils import get_column_letter
BASE = os.path.dirname(os.path.abspath(__file__))
SRC = os.path.join(BASE, "Plan-actividades-avance-2026-07-27.xlsx")
DST = os.path.join(BASE, "Avance-Balam-2026-07-27.xlsx")
HOY = date(2026, 7, 27)
# Marca Balam
CAFE, AMARILLO, CREMA = "331F0E", "F7BD0C", "FDF3D8"
V_FILL, V_FONT = "C6EFCE", "006100" # verde
A_FILL, A_FONT = "FFEB9C", "9C6500" # amarillo
R_FILL, R_FONT = "FFC7CE", "9C0006" # rojo
GRIS = "595959"
BORDE = "D9D9D9"
# Columnas del origen
O_NOMBRE, O_INI, O_FIN, O_PCT, O_TRAB, O_STAT = 1, 3, 4, 7, 10, 11
thin = Side(style="thin", color=BORDE)
box = Border(left=thin, right=thin, top=thin, bottom=thin)
def limpia_fecha(txt):
"""'vie 24/07/26' -> '24/07/2026'."""
m = re.search(r"(\d{2})/(\d{2})/(\d{2})", str(txt or ""))
return f"{m.group(1)}/{m.group(2)}/20{m.group(3)}" if m else ""
def es_etapa(nombre):
return nombre.startswith(("Etapa", "Integracion"))
# --- Lee el interno -----------------------------------------------------------
wsrc = openpyxl.load_workbook(SRC).active
filas = []
for row in range(2, wsrc.max_row + 1):
nombre = str(wsrc.cell(row=row, column=O_NOMBRE).value or "").strip()
if not nombre or nombre.startswith(("", "", "Horas:", "Etapa 1:", "🔴", "Pendientes",
"API de Jira", "Núcleo")):
continue
filas.append({
"nombre": nombre,
"ini": limpia_fecha(wsrc.cell(row=row, column=O_INI).value),
"fin": limpia_fecha(wsrc.cell(row=row, column=O_FIN).value),
"pct": wsrc.cell(row=row, column=O_PCT).value or 0,
"trab": wsrc.cell(row=row, column=O_TRAB).value,
"stat": str(wsrc.cell(row=row, column=O_STAT).value or "").strip(),
"etapa": es_etapa(nombre),
})
# --- Construye el simple ------------------------------------------------------
wb = openpyxl.Workbook()
ws = wb.active
ws.title = "Avance"
ws["A1"] = "Proyecto Balam · Plataforma de Automatización Financiera"
ws["A1"].font = Font(bold=True, size=14, color=CAFE)
ws["A2"] = f"Avance al {HOY.day}-jul-2026"
ws["A2"].font = Font(size=10, italic=True, color=GRIS)
ENC = ["Actividad", "Inicio", "Fin", "Avance", "Horas", "Estatus"]
ANCHOS = [52, 12, 12, 10, 9, 34]
FILA_ENC = 4
for i, (txt, ancho) in enumerate(zip(ENC, ANCHOS), start=1):
c = ws.cell(row=FILA_ENC, column=i, value=txt)
c.font = Font(bold=True, size=10, color=CAFE)
c.fill = PatternFill("solid", fgColor=AMARILLO)
c.alignment = Alignment(horizontal="center", vertical="center")
c.border = box
ws.column_dimensions[get_column_letter(i)].width = ancho
r = FILA_ENC + 1
for f in filas:
fill_etapa = PatternFill("solid", fgColor=CREMA) if f["etapa"] else None
c = ws.cell(row=r, column=1, value=f["nombre"])
c.font = Font(bold=f["etapa"], size=10, color=CAFE)
c.alignment = Alignment(vertical="center", wrap_text=True,
indent=0 if f["etapa"] else 1)
for col, val in ((2, f["ini"]), (3, f["fin"])):
cc = ws.cell(row=r, column=col, value=val)
cc.font = Font(size=9, color=GRIS)
cc.alignment = Alignment(horizontal="center", vertical="center")
# Avance
cp = ws.cell(row=r, column=4, value=f["pct"])
cp.number_format = "0%"
cp.alignment = Alignment(horizontal="center", vertical="center")
if f["pct"] >= 1:
cp.font = Font(bold=True, size=10, color=V_FONT)
cp.fill = PatternFill("solid", fgColor=V_FILL)
elif f["pct"] > 0:
cp.font = Font(bold=True, size=10, color=A_FONT)
cp.fill = PatternFill("solid", fgColor=A_FILL)
else:
cp.font = Font(size=10, color=GRIS)
# Horas
ch = ws.cell(row=r, column=5, value=f["trab"])
ch.number_format = "0.00"
ch.font = Font(bold=f["etapa"], size=10, color=CAFE)
ch.alignment = Alignment(horizontal="center", vertical="center")
# Estatus
cs = ws.cell(row=r, column=6, value=f["stat"])
cs.alignment = Alignment(horizontal="left", vertical="center", wrap_text=True)
cs.font = Font(size=9, color=GRIS)
if f["stat"].startswith(""):
cs.font = Font(size=9, color=V_FONT); cs.fill = PatternFill("solid", fgColor=V_FILL)
elif f["stat"].startswith("🟡"):
cs.font = Font(size=9, color=A_FONT); cs.fill = PatternFill("solid", fgColor=A_FILL)
elif f["stat"].startswith("🔴"):
cs.font = Font(size=9, bold=True, color=R_FONT); cs.fill = PatternFill("solid", fgColor=R_FILL)
for col in range(1, 7):
ws.cell(row=r, column=col).border = box
if fill_etapa and col in (1, 2, 3):
ws.cell(row=r, column=col).fill = fill_etapa
r += 1
# --- Notas --------------------------------------------------------------------
tot = next((f["trab"] for f in filas if f["nombre"].startswith("Integracion")), 0)
e0 = next((f["trab"] for f in filas if f["nombre"].startswith("Etapa 0")), 0)
e1 = next((f["trab"] for f in filas if f["nombre"].startswith("Etapa 1")), 0)
notas = [
("", False),
(f"Horas trabajadas: {e0:.2f} h en Etapa 0 y {e1:.2f} h en Etapa 1 "
f"({tot:.2f} h en total).", True),
("Etapa 1: 6 de los 8 entregables terminados — autenticación y roles con bitácora, "
"multi-tenant, cliente de la API de BIND, sincronización de clientes, "
"sincronización de facturas y cotizaciones, y modelo de facturas con estados y "
"cartera por moneda. Verificado contra producción: 1,494 facturas sincronizadas "
"con la cartera cuadrada.", False),
("La capa de escritura (cotización→factura) está en pausa a la espera de la "
"definición del flujo de facturación (sesión del martes 28-jul).", False),
("Azure y CI/CD esperan la configuración de los recursos del lado de Balam.", False),
]
for txt, bold in notas:
if txt:
c = ws.cell(row=r, column=1, value=txt)
c.font = Font(italic=True, size=9, color=CAFE, bold=bold)
c.alignment = Alignment(wrap_text=True, vertical="top")
ws.merge_cells(start_row=r, start_column=1, end_row=r, end_column=6)
ws.row_dimensions[r].height = 28 if len(txt) > 110 else 14
r += 1
ws.freeze_panes = f"A{FILA_ENC + 1}"
ws.sheet_view.showGridLines = False
ws.print_area = f"A1:F{r}"
ws.page_setup.orientation = "landscape"
ws.page_setup.fitToWidth = 1
ws.sheet_properties.pageSetUpPr.fitToPage = True
wb.save(DST)
print("OK ->", DST)
print(f" {len(filas)} filas · Etapa 0 {e0:.2f} h · Etapa 1 {e1:.2f} h · total {tot:.2f} h")