Compare commits
3 Commits
bee2d02f50
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| cbb29086cc | |||
| 980baf0a0a | |||
| 820b8d41c8 |
+2
-1
@@ -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
@@ -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 40–48 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
|
||||
|
||||
|
||||
@@ -1067,6 +1067,123 @@ Cierre de la sesión (~0.5 h real). **B6 conforme al plan:** `ITenantOwned` en l
|
||||
|
||||
---
|
||||
|
||||
## 58 · Jul 27–28, 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:44–3: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:28–11: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 40–48 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 40–48 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.
@@ -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:** 32–39 h · **Horas registradas de Etapa 1:** 17.0 h
|
||||
**Rango estimado contractual:** 32–39 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 32–39 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 32–39 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 32–39 h (**~44–53 % 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 32–39 h (**~51–62 % 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 22–27 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 18–22 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 40–48 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.
|
||||
@@ -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 40–48 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` (4–5 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 40–48 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.
@@ -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:00–7: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:00–7: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
|
||||
|
||||
|
@@ -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** | 18–22 h | Límite superior consumido; prototipo pendiente de validación |
|
||||
| Etapa 1 | **8.00 h** | 32–39 h | En curso |
|
||||
| **Total** | **30.00 h** | | Coincide con la facturación inicial propuesta |
|
||||
| Etapa 0 | **21.83 h** | 18–22 h | Cerrada y **validada por Balam el 24-jul**; dentro del rango |
|
||||
| Etapa 1 | **19.75 h** | 32–39 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 (B0–B4) | 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 18–22 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 B0–B6 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 4–5.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 32–39 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 8–9 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 3–4 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.
|
||||
|
||||
@@ -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="8–9"),
|
||||
"Arquitectura multi-tenant": dict(
|
||||
nombre="Arquitectura multi-tenant (tenant_id + RLS) y diseño de la solución",
|
||||
est="3–4"),
|
||||
"Demostración semanal": dict(est="2–3"),
|
||||
# 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 32–39 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")
|
||||
Reference in New Issue
Block a user