Registro #92: Azure + CI/CD (14–15-oct) — plataforma en balam-paf-staging.azurewebsites.net, pipeline de GitHub Actions por OIDC sin secretos (build verde; la publicación espera el rol Website Contributor que asigna Balam), PostgreSQL 17 B1ms con respaldos de 35 días, Log Analytics, Application Insights, Key Vault y Storage creados (≈32 USD/mes), traspaso de la BD ensayado y pendiente (red de la BD nueva, ventana tras la prueba de Pedro); pendientes para cerrar la Etapa 1. Incluye lo que estaba sin subir: #91 (primer CFDI timbrado desde la plataforma, 1–2 oct), corte de horas del 2-oct con su Excel y generador, manual de usuario, cartas de datos bancarios, reglas de permisos de Claude Code (.claude/settings.json) y start-n8n.bat

This commit is contained in:
JohannVelazquez
2026-10-15 22:24:18 -06:00
parent 4e90543486
commit 1c4e5349cc
47 changed files with 16654 additions and 14 deletions
Binary file not shown.
+31 -9
View File
@@ -51,14 +51,15 @@ fecha,semana,etapa,actividad_id,actividad,horas,estado_hora,fuente_evidencia,fac
2026-08-11,2026-W33,1,E1-05,"Bitácora #62–#63 (merge de las definiciones, respuesta de Noé) y sondeo nocturno de FAC (molde inexistente, FAC-98)",0.75,reconstruida,bitacora/REGISTRO.md#63,si,"commits f28fcc5, a2f338f; por validar"
2026-08-11,2026-W33,2,E2-02,"Capa de escritura mínima ticket→cotización: Quotes_NewQuote en BindClient, POST /api/emision/cotizacion con dry-run/confirmación, pantalla 'Generar cotización en BIND' y script para Balam (73/73 pruebas)",2.50,reconstruida,bitacora/REGISTRO.md#64,si,"commits e854eda y a99eb8b (cliente, 21:28), 2fb230e (21:30); sesión nocturna; por validar"
2026-08-12,2026-W33,2,E2-02,"Madrugada y previo a la sesión: coreografía de la demo (Actualizar sin caché, Cotizar por fila, vista previa automática), sin reintentos en escrituras, tipo de cambio, prefill y selector de cliente",2.00,reconstruida,bitacora/REGISTRO.md#64,si,"commits eafa49b 00:34, 1b062d2 01:33, 2be0432/8228c40 06:55 (cliente); por validar"
2026-08-12,2026-W33,2,AJ-01,"Sesión acompañada de escritura Jira→BIND con Noé, Araceli y Pedro: formulario construido en vivo, decisión de que la plataforma cotiza, primer POST con 500",1.00,confirmada,fuentes/2026-08-12 - Transcript - Sesion prueba de escritura Jira-BIND.txt,si,"Transcript de 52 min; REGISTRO #65; relevo de Erika por Pedro"
2026-08-12,2026-W33,2,AJ-01,"Excel de campos y catálogos de la cotización BIND (67 servicios, 3 productos, 17 clientes) y correo a Pedro (CC Ara, Noé)",0.75,reconstruida,Cotizacion-BIND-campos-y-catalogos.xlsx,si,"correo 8:09 am; REGISTRO #66; por validar"
2026-08-12,2026-W33,2,E2-02,"Sesión acompañada de escritura Jira→BIND con Noé, Araceli y Pedro: formulario construido en vivo, decisión de que la plataforma cotiza, primer POST con 500",1.00,confirmada,fuentes/2026-08-12 - Transcript - Sesion prueba de escritura Jira-BIND.txt,si,"Transcript de 52 min; REGISTRO #65; relevo de Erika por Pedro; reclasificada de AJ-01 a E2-02 el 2-oct por Johann: es cotización contra el API de BIND, no integración con Jira"
2026-08-12,2026-W33,2,E2-02,"Excel de campos y catálogos de la cotización BIND (67 servicios, 3 productos, 17 clientes) y correo a Pedro (CC Ara, Noé)",0.75,reconstruida,Cotizacion-BIND-campos-y-catalogos.xlsx,si,"correo 8:09 am; REGISTRO #66; por validar; reclasificada de AJ-01 a E2-02 el 2-oct por Johann: es cotización contra el API de BIND, no integración con Jira"
2026-08-20,2026-W34,1,E1-05,"Coordinación con Pedro por WhatsApp (18–20 ago): 17 categorías, formulario modificado, ticket FAC-106 y convocatoria del 21",0.50,reconstruida,bitacora/REGISTRO.md#67,si,"por validar"
2026-08-20,2026-W34,1,E1-05,"Cruce del buzón contra la bitácora (9 comunicaciones, 2 correcciones), registro #65 desde el transcript y guion de la sesión del 21-ago",2.00,reconstruida,bitacora/REGISTRO.md#65,si,"commits 5d846ec 23:02 → 072a3cd 23:52; por validar"
2026-08-20,2026-W34,2,AJ-01,"Sondeo de FAC-106 por API, diagnóstico del 500 (VATRate como fracción, VAT, PriceListID) y wizard cableado al formulario 'Cotización Bind', verificado e2e (74/74)",1.50,reconstruida,planeacion/Sondeo-FAC-106-y-payload-2026-08-20.md,si,"commits f839d0e 23:41 y eb04e23 23:47 (cliente); por validar"
2026-08-21,2026-W34,2,AJ-01,"Sesión 7:00 am 'Seguimiento - PAF' con Noé, Araceli y Pedro: tres intentos de emisión (500) y pivote a las definiciones",0.25,reconstruida,bitacora/REGISTRO.md#69,si,"Informe Read AI recibido 7:16 am; por validar"
2026-08-21,2026-W34,2,AJ-01,"Investigación posterior: schema del portal, intentos 4–5, PRIMERA COTIZACIÓN REAL (folio 1026), VAT corregido como tasa, registro #69, correo 'Seguimiento FAC-106' a Noé y borrador del correo de causa",3.00,reconstruida,bitacora/REGISTRO.md#69,si,"commits 46fad4c 10:15 (cliente) y d31747d 10:16; correo 7:32 am; por validar"
2026-08-21,2026-W34,2,AJ-01,"Sesión 1:00 pm 'Seguimiento - PAF (2)': homologación de estados del flujo ticket→cotización→prefactura→factura; pendiente prueba de escritura en Jira",0.50,reconstruida,correo Read AI 21-ago 1:34 pm,si,"SIN registro en bitácora; por validar"
2026-08-20,2026-W34,1,E1-05,"Cruce del buzón contra la bitácora (9 comunicaciones incorporadas, 2 correcciones)",1.00,reconstruida,commit 5d846ec 23:02,si,por validar; dividida el 2-oct: la otra hora (registro #65 y guion del 21-ago) pasó a E2-02
2026-08-20,2026-W34,2,E2-02,"Registro #65 de la sesión de cotización del 12-ago desde el transcript y guion de la sesión del 21-ago (aviso previo a Pedro, coreografía de escritura, plan B del 500, 8 preguntas, protocolo si falla)",1.00,reconstruida,"commits a03f81f 23:29, cabe0fe 23:50 y 072a3cd 23:52",si,por validar; dividida el 2-oct de la fila de gestión: documenta y prepara trabajo de cotización (regla 7)
2026-08-20,2026-W34,2,E2-02,"Sondeo de FAC-106 por API, diagnóstico del 500 (VATRate como fracción, VAT, PriceListID) y wizard cableado al formulario 'Cotización Bind', verificado e2e (74/74)",1.50,reconstruida,planeacion/Sondeo-FAC-106-y-payload-2026-08-20.md,si,"commits f839d0e 23:41 y eb04e23 23:47 (cliente); por validar; reclasificada de AJ-01 a E2-02 el 2-oct por Johann: es cotización contra el API de BIND, no integración con Jira"
2026-08-21,2026-W34,2,E2-02,"Sesión 7:00 am 'Seguimiento - PAF' con Noé, Araceli y Pedro: tres intentos de emisión (500) y pivote a las definiciones",0.25,reconstruida,bitacora/REGISTRO.md#69,si,"Informe Read AI recibido 7:16 am; por validar; reclasificada de AJ-01 a E2-02 el 2-oct por Johann: es cotización contra el API de BIND, no integración con Jira"
2026-08-21,2026-W34,2,E2-02,"Investigación posterior: schema del portal, intentos 4–5, PRIMERA COTIZACIÓN REAL (folio 1026), VAT corregido como tasa, registro #69, correo 'Seguimiento FAC-106' a Noé y borrador del correo de causa",3.00,reconstruida,bitacora/REGISTRO.md#69,si,"commits 46fad4c 10:15 (cliente) y d31747d 10:16; correo 7:32 am; por validar; reclasificada de AJ-01 a E2-02 el 2-oct por Johann: es cotización contra el API de BIND, no integración con Jira"
2026-08-21,2026-W34,2,E2-02,Sesión 1:00 pm 'Seguimiento - PAF (2)': homologación de estados del flujo ticket→cotización→prefactura→factura; pendiente prueba de escritura en Jira,0.50,reconstruida,correo Read AI 21-ago 1:34 pm,si,"SIN registro en bitácora; por validar; reclasificada de AJ-01 a E2-02 el 2-oct por Johann: es cotización contra el API de BIND, no integración con Jira"
2026-08-23,2026-W34,2,E2-03,"Camino cotización→prefactura real: Invoices_AddInvoice con candado, POST /api/emision/prefactura, wizard sin simulación (borrador + aprobación real), herencia de CFDIUse/ServiceID/PriceListID desde factura y cliente",3.00,reconstruida,balam-plataforma commits a893fa9..a07fff6,si,"commits 22:15–23:40 del 23-ago (cliente); por validar"
2026-08-24,2026-W35,2,E2-03,"Sesión 7:00 am 'Seguimiento - PAF': validación del flujo cotización→prefactura (MXN/USD, IVA 16 %/0 %) y ajuste en vivo: false = remisión, columna BIND en la bandeja",1.00,reconstruida,correo Read AI 24-ago 7:44 am,si,"commit 77d3767 7:38 (cliente); SIN registro en bitácora; por validar"
2026-08-25,2026-W35,1,E1-05,"Correo a Noé con los requisitos de Azure (suscripción, grupo de recursos, Contributor) y lectura de su explicación remisión vs prefactura",0.50,reconstruida,correo 25-ago 4:54 pm,si,"por validar"
@@ -67,14 +68,35 @@ fecha,semana,etapa,actividad_id,actividad,horas,estado_hora,fuente_evidencia,fac
2026-08-26,2026-W35,2,E2-03,"Contención del incidente: confirmación de prefactura bloqueada (409), candado triple IsFiscalInvoice=false, API en ReadOnly, registro #70",0.75,reconstruida,bitacora/REGISTRO.md#70,si,"commits c5143cf 7:25 y fbb31fc 7:59 (cliente), a48fba0 8:01; por validar"
2026-08-26,2026-W35,2,E2-03,"Investigación de cierre solo lectura: body de Invoices_AddInvoice vs portal y SDK, PDFs de 1000/1001/2384/2386, censo de 58 no fiscales, diff prefactura 2384 vs remisión — adenda al #70",2.50,reconstruida,bitacora/REGISTRO.md#70,si,"commit 2ae6880 21:28; por validar"
2026-08-26,2026-W35,2,E2-03,"Experimentos autorizados en modo Write (folio compartido, 12 sondas de campo, DELETE), dump del APIM (98 ops), script de captura Playwright y adenda 2 al #70",2.50,reconstruida,bitacora/REGISTRO.md#70,si,"commits e7775e0 21:48 (cliente) y db16f62 00:10 del 27-ago; por validar"
2026-09-03,2026-W36,2,AJ-01,"Sesión 7:00 am 'Seguimiento PAF': datos para el tenant de Azure y conceptos de Acuntia (headcount); definición: ítem por código y concepto = título del ticket",0.50,reconstruida,correo Read AI 3-sep 7:28 am,si,"SIN registro en bitácora; por validar"
2026-09-03,2026-W36,2,E2-02,Sesión 7:00 am 'Seguimiento PAF': datos para el tenant de Azure y conceptos de Acuntia (headcount); definición: ítem por código y concepto = título del ticket,0.50,reconstruida,correo Read AI 3-sep 7:28 am,si,"SIN registro en bitácora; por validar; reclasificada de AJ-01 a E2-02 el 2-oct por Johann: es cotización contra el API de BIND, no integración con Jira"
2026-09-03,2026-W36,2,E2-02,"Catálogo de servicios de BIND en la emisión (GET /api/Services), mapeo categoría Jira → código configurable, selector de ítem, y caché de la bandeja invalidada al cotizar (visto en producción)",1.50,reconstruida,balam-plataforma commit ed4c819,si,"archivos 07:46–07:58 y 12:55 del 3-sep; por validar"
2026-09-03,2026-W36,1,E1-09,"Publicación de la plataforma en https://paputec.mx/balam/ como ambiente provisional: Docker (Postgres + API chiseled read-only), Apache/proxy/SPA, deploy.sh reproducible, seeder de admin, migración al arrancar",2.50,reconstruida,balam-plataforma/deploy/paputec/README.md,si,"archivos 08:36–12:57 del 3-sep; commit 03582af; por validar"
2026-09-03,2026-W36,3,E3-01,"Endurecimiento aplicado en la publicación: CSP/HSTS, rate limit por IP, bloqueo de login, contraseñas 12+, secretos solo en .env root, respaldos diarios con retención",1.00,reconstruida,balam-plataforma/deploy/paputec/README.md,si,"commit 03582af; por validar"
2026-09-03,2026-W36,3,E3-02,"Runbook de publicación, seguridad aplicada, operación y rollback (deploy/paputec/README.md)",0.50,reconstruida,balam-plataforma/deploy/paputec/README.md,si,"commit 03582af; por validar"
2026-09-03,2026-W36,2,AJ-01,"Sesión 1:30 pm 'Seguimiento PAF (2)': tres tickets de Pedro convertidos en cotizaciones reales desde la plataforma publicada; prueba con ticket de Linares",0.25,reconstruida,correo Read AI 3-sep 1:51 pm,si,"SIN registro en bitácora; por validar"
2026-09-03,2026-W36,2,E2-02,Sesión 1:30 pm 'Seguimiento PAF (2)': tres tickets de Pedro convertidos en cotizaciones reales desde la plataforma publicada; prueba con ticket de Linares,0.25,reconstruida,correo Read AI 3-sep 1:51 pm,si,"SIN registro en bitácora; por validar; reclasificada de AJ-01 a E2-02 el 2-oct por Johann: es cotización contra el API de BIND, no integración con Jira"
2026-09-07,2026-W37,2,E2-02,"Cotización en lote desde la bandeja: selección de tickets, pantalla cotizar-lote con dry-run y confirmación por ticket en serie, reglas de prellenado compartidas (cotizacion-prefill.ts)",2.00,reconstruida,balam-plataforma commit fbdbd18,si,"archivos 07:27–07:30 del 7-sep; por validar"
2026-09-09,2026-W37,2,E2-03,"Sesión 1:00 pm 'Seguimiento PAF - Prefactura' con Araceli y Pedro: prefactura creada en la UI de BIND ('Generar venta') mientras se capturan las peticiones (InvoiceDetails)",0.50,reconstruida,correo Read AI 9-sep 1:24 pm,si,"SIN registro en bitácora; por validar"
2026-09-09,2026-W37,2,E2-03,"Tras la sesión de la 1:00 pm: análisis de la captura de 'Generar venta' en la UI de BIND, prueba de folio (no se puede sobrescribir) y diseño del puente a la web app de BIND para la prefactura",1.25,reconstruida,transcript de Claude Code 65198403 (17:36–18:53); balam-plataforma/CLAUDE.md §Puente a la UI,si,Corte 2-oct: no entró al corte del 15-sep (el código se subió hasta el 29-sep en 4d6b3d8); por validar
2026-09-10,2026-W37,2,E2-03,"Sesión 1:00 pm con Pedro: análisis de la cotización 1078 (Acuntia) y captura de la petición de red de la prefactura para evaluar su réplica",0.25,reconstruida,correo Read AI 10-sep 1:21 pm,si,"SIN registro en bitácora; por validar"
2026-09-10,2026-W37,2,E2-03,"Preparación de la segunda sesión de captura: lista de lo que hay que recabar en DevTools (Generar venta sobre la 1078, Preserve log, Fetch/XHR)",0.25,reconstruida,transcript de Claude Code 65198403 (12:21),si,Corte 2-oct; por validar
2026-09-10,2026-W37,2,E2-03,"Contrato real de AddInvoiceTransaction (PrevInvoice/FiscalInvoice/IsCFDI + QuoteID + Number), sonda autorizada op #12 (remisión 1004 creada y cancelada), BindUiClient con login de formulario, endpoints prefactura-ui, vista de cotizaciones sin ticket y mensaje a Pedro con las alternativas",1.75,reconstruida,"transcript de Claude Code 65198403 (13:21–13:29, 14:15–15:49, 16:50–16:53); commit 4d6b3d8",si,Corte 2-oct; la sesión con Pedro (1:00–1:21) ya está en su fila; por validar
2026-09-15,2026-W38,1,E1-05,"Corte de horas 6-ago→15-sep (reconstrucción desde commits, correos e informes de reunión), Excel de avance, commits y publicación de ambos repositorios",0.75,reconstruida,planeacion/Seguimiento-horas.csv,si,"sesión asistida por IA; por validar"
2026-09-16,2026-W38,1,E1-05,"Excel de avance publicado en Google Sheets (Drive/Balam vía rclone, enlace compartible) y corte del 16-sep regenerado desde script",0.50,reconstruida,planeacion/generar_avance_2026_09_16.py,si,"sesión asistida por IA; por validar"
2026-09-29,2026-W40,2,E2-03,"Sesión 7:00 am 'Seguimiento PAF - Integración ERP' con Pedro: validación de accesos, Balam entrega login web a BIND con permisos de prefactura y error al cargar la solicitud al cotizar",0.50,reconstruida,correo Read AI 29-sep 7:23 am; REGISTRO #83,si,Corte 2-oct; por validar
2026-09-29,2026-W40,2,E2-03,Cierre de los huecos de bitácora #71–#83 desde el buzón (informes de Read AI y correos del 21-ago al 29-sep) e investigación de cierre con el SDK oficial de BIND (modelo de dos endpoints),0.75,reconstruida,bitacora/REGISTRO.md#71-83; commit 3de2739 08:06,si,"Corte 2-oct; otra máquina, inicio estimado al terminar la sesión (7:23); por validar; movida de E1-05 a E2-03 el 2-oct: documenta las sesiones de prefactura e investiga el SDK de BIND (regla 7)"
2026-09-29,2026-W40,2,E2-03,"Puente de prefactura con el usuario propio CTO: credenciales y API key nueva en user-secrets, app en modo escritura, PRIMERA PREFACTURA REAL desde la plataforma (folio 2467, 16:08), una sesión por usuario, API interno de la web app y contención de la escritura accidental (FillQuote: cotización 1079 Surtida)",1.50,reconstruida,"transcript de Claude Code 65198403 y 02f58158 (10:54–11:21, 13:24–13:39, 15:28–16:26); balam-plataforma/CLAUDE.md",si,"Corte 2-oct; dos sesiones en paralelo, contadas una sola vez; por validar"
2026-09-29,2026-W40,2,AJ-01,"Webhook de Jira FAC: análisis (leer la creación de tickets vs ADR-001), plan de 5 fases, Fase 1 por GET (token de Pedro con ADMINISTER), endpoint POST /api/jira/webhook con HMAC, idempotencia y relectura (38/38), túnel ngrok y piloto e2e: webhook id 1 + FAC-169 detectado en 14.4 s y borrado",1.00,reconstruida,"transcript de Claude Code 02f58158 (15:34–15:41, 17:57–18:14, 18:39–19:08); REGISTRO #84; commit d7238da",si,"Corte 2-oct; incluye plan, bitácora y push de 4d6b3d8 (rebase con 3 conflictos); por validar"
2026-09-29,2026-W40,2,AJ-01,"Cierre de la sesión: push de ambos repos, appsettings.Local portátil de los user-secrets y relevo a la máquina con acceso a paputec",0.50,reconstruida,"transcript de Claude Code 02f58158 (23:47–00:11); commits 8479e96, 5145801, e5912bf",si,Corte 2-oct; por validar
2026-09-30,2026-W40,2,AJ-01,"Webhook en producción (paputec): fusión de las dos bitácoras del 29-sep, publicación de 5145801 (116 pruebas, respaldo y rollback), secreto nuevo, PUT del webhook id 1 y prueba e2e con FAC-170 detectada en 1.05 s",0.50,reconstruida,"bitacora/REGISTRO.md#85; commits e88b5e1 00:19, 9351aa4 00:30, 7e50c71 00:31",si,Corte 2-oct; ≈12:10–12:35 am; por validar
2026-09-30,2026-W40,2,AJ-01,"Vista previa automática de la cotización por cada ticket detectado (cola, trabajador y port del prellenado; 147 pruebas), push y publicación; causa del 500 de BIND: la API key de arturo (la de cto responde 200)",0.50,reconstruida,"bitacora/REGISTRO.md#86; commits 1c37eaa 00:45, 287cb65, 085e903 00:55",si,Corte 2-oct; ≈12:35–1:00 am; por validar
2026-09-30,2026-W40,2,AJ-01,"API key de cto en producción (el sync vuelve tras 20 días), radiografía del formulario contra el catálogo, vista previa validada contra BIND real con FAC-171 y modal en vivo con 'Aceptar', campana y modo automático configurable (170 pruebas)",0.75,reconstruida,"bitacora/REGISTRO.md#87; commits aac7447 01:40, bdafb06 01:42",si,Corte 2-oct; ≈1:00–1:45 am; por validar
2026-09-30,2026-W40,2,AJ-01,"Push y publicación del modal en vivo, con el modo automático permitido en el servidor (sin activar)",0.25,reconstruida,"bitacora/REGISTRO.md#87 adenda; commits 29a354b, 8799348 06:55",si,Corte 2-oct; respaldo de las 6:53 am; por validar
2026-09-30,2026-W40,2,E2-03,"Puente de prefactura encendido en producción, 'Servicio de Enlace' → ítem 106 y relectura completa de cotizaciones (11 con estado atrasado), tras lo que Pedro vio a las 7:26 en el wizard de FAC-173",0.75,reconstruida,bitacora/REGISTRO.md#88,si,Corte 2-oct; 7:50–8:30 am; por validar
2026-09-30,2026-W40,2,E2-03,"Cuatro bugs reales del puente y de los importes (Total en cero, USD mostrado en pesos e IVA doble, post-proceso que escondía la prefactura, duplicados posibles) con pruebas que los reproducen (197), panel bajo la fila y sync de cotizaciones por barrido completo",1.25,reconstruida,"bitacora/REGISTRO.md#88; commits 597c8a2 21:16, 63af3d6 21:29",si,Corte 2-oct; 8:30–9:40 pm; por validar
2026-09-30,2026-W40,2,E2-03,"Credenciales del usuario CTO en el .env de producción (autorización de Johann), push y publicación de 597c8a2 con la migración QuoteSubtotal y verificación del primer ciclo del sync",0.25,reconstruida,"bitacora/REGISTRO.md#88 adenda; commits 4a219b5, 604cb8d 21:49",si,Corte 2-oct; 9:40–9:50 pm; por validar
2026-09-30,2026-W40,2,E2-03,"Timbrado desde la plataforma (POST /api/emision/timbrar-ui: EmitCFDI con dry-run, palabra TIMBRAR y verificación por lectura), estado incierto cerrado en los POST, consecutivo sin la serie B, uso CFDI del mismo cliente, 212 pruebas, recorrido en navegador, publicación y guion de la prueba del 1-oct",0.75,reconstruida,"bitacora/REGISTRO.md#89; commits f5b3743 22:40, 9874547 22:45",si,Corte 2-oct; 10:00–10:45 pm según respaldo (04:40 UTC) y último commit — el título del #89 dice 10:00 pm–12:50 am: Johann no recuerda la hora de cierre (2-oct): se queda la cota de la evidencia
2026-09-30,2026-W40,2,E2-03,"Llamada con Pedro (7:26 am): revisión del wizard de FAC-173 en producción — prefactura deshabilitada, totales en cero, campo de servicios y Arturo en la lista de aprobadores",0.25,validada,bitacora/REGISTRO.md#88,si,Corte 2-oct; ~15 min según Johann (2-oct)
2026-10-01,2026-W40,2,E2-04,Prueba completa con Balam en producción: ticket → cotización → prefactura; el timbrado (prefactura → CFDI) no se intentó y queda por probar,0.25,validada,planeacion/Guion-prueba-completa-2026-10-01.md,si,Corte 2-oct; ~20 min o menos según Johann (2-oct)
2026-10-02,2026-W40,2,E2-03,"Prefacturas en lote (Cotizaciones y bandeja → una prefactura por cotización, en serie, folios consecutivos con el API lento) y timbrado también en el detalle de la factura (213 pruebas, recorrido en navegador); push y publicación",1.00,reconstruida,"bitacora/REGISTRO.md#90; commits 73d1c2a 08:35, 4f3eb99, 4e90543 08:41",si,Corte 2-oct; 7:40–8:40 am; por validar
2026-10-02,2026-W40,1,E1-05,"Corte de horas 16-sep → 2-oct: pull de ambos repos, reconstrucción desde los transcripts de Claude Code, los commits y los informes de Read AI, y Excel de avance",0.75,confirmada,transcript de Claude Code a134588d (11:47–12:25); planeacion/generar_avance_2026_10_02.py,si,"Corte 2-oct; sesión asistida por IA; tiempo real. No se cobran la proyección de costos, la reclasificación ni los ajustes posteriores del Excel (~0.75 h, decisión de Johann 2-oct)"
2026-10-02,2026-W40,2,E2-04,"Manual de usuario en PDF (14 páginas): acceso y roles, consulta, solicitudes de Jira, ticket → cotización (aviso en vivo, pantalla y lote), prefactura (individual, emisión asistida y lote), timbrado, reglas y mensajes frecuentes, con capturas de la demo",0.50,confirmada,manual/Manual-usuario-Balam.pdf; transcript de Claude Code a134588d (12:25–12:55),si,Corte 2-oct; sesión asistida por IA; tiempo real de esta sesión
1 fecha semana etapa actividad_id actividad horas estado_hora fuente_evidencia facturable notas
51 2026-08-11 2026-W33 1 E1-05 Bitácora #62–#63 (merge de las definiciones, respuesta de Noé) y sondeo nocturno de FAC (molde inexistente, FAC-98) 0.75 reconstruida bitacora/REGISTRO.md#63 si commits f28fcc5, a2f338f; por validar
52 2026-08-11 2026-W33 2 E2-02 Capa de escritura mínima ticket→cotización: Quotes_NewQuote en BindClient, POST /api/emision/cotizacion con dry-run/confirmación, pantalla 'Generar cotización en BIND' y script para Balam (73/73 pruebas) 2.50 reconstruida bitacora/REGISTRO.md#64 si commits e854eda y a99eb8b (cliente, 21:28), 2fb230e (21:30); sesión nocturna; por validar
53 2026-08-12 2026-W33 2 E2-02 Madrugada y previo a la sesión: coreografía de la demo (Actualizar sin caché, Cotizar por fila, vista previa automática), sin reintentos en escrituras, tipo de cambio, prefill y selector de cliente 2.00 reconstruida bitacora/REGISTRO.md#64 si commits eafa49b 00:34, 1b062d2 01:33, 2be0432/8228c40 06:55 (cliente); por validar
54 2026-08-12 2026-W33 2 AJ-01 E2-02 Sesión acompañada de escritura Jira→BIND con Noé, Araceli y Pedro: formulario construido en vivo, decisión de que la plataforma cotiza, primer POST con 500 1.00 confirmada fuentes/2026-08-12 - Transcript - Sesion prueba de escritura Jira-BIND.txt si Transcript de 52 min; REGISTRO #65; relevo de Erika por Pedro Transcript de 52 min; REGISTRO #65; relevo de Erika por Pedro; reclasificada de AJ-01 a E2-02 el 2-oct por Johann: es cotización contra el API de BIND, no integración con Jira
55 2026-08-12 2026-W33 2 AJ-01 E2-02 Excel de campos y catálogos de la cotización BIND (67 servicios, 3 productos, 17 clientes) y correo a Pedro (CC Ara, Noé) 0.75 reconstruida Cotizacion-BIND-campos-y-catalogos.xlsx si correo 8:09 am; REGISTRO #66; por validar correo 8:09 am; REGISTRO #66; por validar; reclasificada de AJ-01 a E2-02 el 2-oct por Johann: es cotización contra el API de BIND, no integración con Jira
56 2026-08-20 2026-W34 1 E1-05 Coordinación con Pedro por WhatsApp (18–20 ago): 17 categorías, formulario modificado, ticket FAC-106 y convocatoria del 21 0.50 reconstruida bitacora/REGISTRO.md#67 si por validar
57 2026-08-20 2026-W34 1 E1-05 Cruce del buzón contra la bitácora (9 comunicaciones, 2 correcciones), registro #65 desde el transcript y guion de la sesión del 21-ago Cruce del buzón contra la bitácora (9 comunicaciones incorporadas, 2 correcciones) 2.00 1.00 reconstruida bitacora/REGISTRO.md#65 commit 5d846ec 23:02 si commits 5d846ec 23:02 → 072a3cd 23:52; por validar por validar; dividida el 2-oct: la otra hora (registro #65 y guion del 21-ago) pasó a E2-02
58 2026-08-20 2026-W34 2 AJ-01 E2-02 Sondeo de FAC-106 por API, diagnóstico del 500 (VATRate como fracción, VAT, PriceListID) y wizard cableado al formulario 'Cotización Bind', verificado e2e (74/74) Registro #65 de la sesión de cotización del 12-ago desde el transcript y guion de la sesión del 21-ago (aviso previo a Pedro, coreografía de escritura, plan B del 500, 8 preguntas, protocolo si falla) 1.50 1.00 reconstruida planeacion/Sondeo-FAC-106-y-payload-2026-08-20.md commits a03f81f 23:29, cabe0fe 23:50 y 072a3cd 23:52 si commits f839d0e 23:41 y eb04e23 23:47 (cliente); por validar por validar; dividida el 2-oct de la fila de gestión: documenta y prepara trabajo de cotización (regla 7)
59 2026-08-21 2026-08-20 2026-W34 2 AJ-01 E2-02 Sesión 7:00 am 'Seguimiento - PAF' con Noé, Araceli y Pedro: tres intentos de emisión (500) y pivote a las definiciones Sondeo de FAC-106 por API, diagnóstico del 500 (VATRate como fracción, VAT, PriceListID) y wizard cableado al formulario 'Cotización Bind', verificado e2e (74/74) 0.25 1.50 reconstruida bitacora/REGISTRO.md#69 planeacion/Sondeo-FAC-106-y-payload-2026-08-20.md si Informe Read AI recibido 7:16 am; por validar commits f839d0e 23:41 y eb04e23 23:47 (cliente); por validar; reclasificada de AJ-01 a E2-02 el 2-oct por Johann: es cotización contra el API de BIND, no integración con Jira
60 2026-08-21 2026-W34 2 AJ-01 E2-02 Investigación posterior: schema del portal, intentos 4–5, PRIMERA COTIZACIÓN REAL (folio 1026), VAT corregido como tasa, registro #69, correo 'Seguimiento FAC-106' a Noé y borrador del correo de causa Sesión 7:00 am 'Seguimiento - PAF' con Noé, Araceli y Pedro: tres intentos de emisión (500) y pivote a las definiciones 3.00 0.25 reconstruida bitacora/REGISTRO.md#69 si commits 46fad4c 10:15 (cliente) y d31747d 10:16; correo 7:32 am; por validar Informe Read AI recibido 7:16 am; por validar; reclasificada de AJ-01 a E2-02 el 2-oct por Johann: es cotización contra el API de BIND, no integración con Jira
61 2026-08-21 2026-W34 2 AJ-01 E2-02 Sesión 1:00 pm 'Seguimiento - PAF (2)': homologación de estados del flujo ticket→cotización→prefactura→factura; pendiente prueba de escritura en Jira Investigación posterior: schema del portal, intentos 4–5, PRIMERA COTIZACIÓN REAL (folio 1026), VAT corregido como tasa, registro #69, correo 'Seguimiento FAC-106' a Noé y borrador del correo de causa 0.50 3.00 reconstruida correo Read AI 21-ago 1:34 pm bitacora/REGISTRO.md#69 si SIN registro en bitácora; por validar commits 46fad4c 10:15 (cliente) y d31747d 10:16; correo 7:32 am; por validar; reclasificada de AJ-01 a E2-02 el 2-oct por Johann: es cotización contra el API de BIND, no integración con Jira
62 2026-08-21 2026-W34 2 E2-02 Sesión 1:00 pm 'Seguimiento - PAF (2)': homologación de estados del flujo ticket→cotización→prefactura→factura; pendiente prueba de escritura en Jira 0.50 reconstruida correo Read AI 21-ago 1:34 pm si SIN registro en bitácora; por validar; reclasificada de AJ-01 a E2-02 el 2-oct por Johann: es cotización contra el API de BIND, no integración con Jira
63 2026-08-23 2026-W34 2 E2-03 Camino cotización→prefactura real: Invoices_AddInvoice con candado, POST /api/emision/prefactura, wizard sin simulación (borrador + aprobación real), herencia de CFDIUse/ServiceID/PriceListID desde factura y cliente 3.00 reconstruida balam-plataforma commits a893fa9..a07fff6 si commits 22:15–23:40 del 23-ago (cliente); por validar
64 2026-08-24 2026-W35 2 E2-03 Sesión 7:00 am 'Seguimiento - PAF': validación del flujo cotización→prefactura (MXN/USD, IVA 16 %/0 %) y ajuste en vivo: false = remisión, columna BIND en la bandeja 1.00 reconstruida correo Read AI 24-ago 7:44 am si commit 77d3767 7:38 (cliente); SIN registro en bitácora; por validar
65 2026-08-25 2026-W35 1 E1-05 Correo a Noé con los requisitos de Azure (suscripción, grupo de recursos, Contributor) y lectura de su explicación remisión vs prefactura 0.50 reconstruida correo 25-ago 4:54 pm si por validar
68 2026-08-26 2026-W35 2 E2-03 Contención del incidente: confirmación de prefactura bloqueada (409), candado triple IsFiscalInvoice=false, API en ReadOnly, registro #70 0.75 reconstruida bitacora/REGISTRO.md#70 si commits c5143cf 7:25 y fbb31fc 7:59 (cliente), a48fba0 8:01; por validar
69 2026-08-26 2026-W35 2 E2-03 Investigación de cierre solo lectura: body de Invoices_AddInvoice vs portal y SDK, PDFs de 1000/1001/2384/2386, censo de 58 no fiscales, diff prefactura 2384 vs remisión — adenda al #70 2.50 reconstruida bitacora/REGISTRO.md#70 si commit 2ae6880 21:28; por validar
70 2026-08-26 2026-W35 2 E2-03 Experimentos autorizados en modo Write (folio compartido, 12 sondas de campo, DELETE), dump del APIM (98 ops), script de captura Playwright y adenda 2 al #70 2.50 reconstruida bitacora/REGISTRO.md#70 si commits e7775e0 21:48 (cliente) y db16f62 00:10 del 27-ago; por validar
71 2026-09-03 2026-W36 2 AJ-01 E2-02 Sesión 7:00 am 'Seguimiento PAF': datos para el tenant de Azure y conceptos de Acuntia (headcount); definición: ítem por código y concepto = título del ticket 0.50 reconstruida correo Read AI 3-sep 7:28 am si SIN registro en bitácora; por validar SIN registro en bitácora; por validar; reclasificada de AJ-01 a E2-02 el 2-oct por Johann: es cotización contra el API de BIND, no integración con Jira
72 2026-09-03 2026-W36 2 E2-02 Catálogo de servicios de BIND en la emisión (GET /api/Services), mapeo categoría Jira → código configurable, selector de ítem, y caché de la bandeja invalidada al cotizar (visto en producción) 1.50 reconstruida balam-plataforma commit ed4c819 si archivos 07:46–07:58 y 12:55 del 3-sep; por validar
73 2026-09-03 2026-W36 1 E1-09 Publicación de la plataforma en https://paputec.mx/balam/ como ambiente provisional: Docker (Postgres + API chiseled read-only), Apache/proxy/SPA, deploy.sh reproducible, seeder de admin, migración al arrancar 2.50 reconstruida balam-plataforma/deploy/paputec/README.md si archivos 08:36–12:57 del 3-sep; commit 03582af; por validar
74 2026-09-03 2026-W36 3 E3-01 Endurecimiento aplicado en la publicación: CSP/HSTS, rate limit por IP, bloqueo de login, contraseñas 12+, secretos solo en .env root, respaldos diarios con retención 1.00 reconstruida balam-plataforma/deploy/paputec/README.md si commit 03582af; por validar
75 2026-09-03 2026-W36 3 E3-02 Runbook de publicación, seguridad aplicada, operación y rollback (deploy/paputec/README.md) 0.50 reconstruida balam-plataforma/deploy/paputec/README.md si commit 03582af; por validar
76 2026-09-03 2026-W36 2 AJ-01 E2-02 Sesión 1:30 pm 'Seguimiento PAF (2)': tres tickets de Pedro convertidos en cotizaciones reales desde la plataforma publicada; prueba con ticket de Linares 0.25 reconstruida correo Read AI 3-sep 1:51 pm si SIN registro en bitácora; por validar SIN registro en bitácora; por validar; reclasificada de AJ-01 a E2-02 el 2-oct por Johann: es cotización contra el API de BIND, no integración con Jira
77 2026-09-07 2026-W37 2 E2-02 Cotización en lote desde la bandeja: selección de tickets, pantalla cotizar-lote con dry-run y confirmación por ticket en serie, reglas de prellenado compartidas (cotizacion-prefill.ts) 2.00 reconstruida balam-plataforma commit fbdbd18 si archivos 07:27–07:30 del 7-sep; por validar
78 2026-09-09 2026-W37 2 E2-03 Sesión 1:00 pm 'Seguimiento PAF - Prefactura' con Araceli y Pedro: prefactura creada en la UI de BIND ('Generar venta') mientras se capturan las peticiones (InvoiceDetails) 0.50 reconstruida correo Read AI 9-sep 1:24 pm si SIN registro en bitácora; por validar
79 2026-09-09 2026-W37 2 E2-03 Tras la sesión de la 1:00 pm: análisis de la captura de 'Generar venta' en la UI de BIND, prueba de folio (no se puede sobrescribir) y diseño del puente a la web app de BIND para la prefactura 1.25 reconstruida transcript de Claude Code 65198403 (17:36–18:53); balam-plataforma/CLAUDE.md §Puente a la UI si Corte 2-oct: no entró al corte del 15-sep (el código se subió hasta el 29-sep en 4d6b3d8); por validar
80 2026-09-10 2026-W37 2 E2-03 Sesión 1:00 pm con Pedro: análisis de la cotización 1078 (Acuntia) y captura de la petición de red de la prefactura para evaluar su réplica 0.25 reconstruida correo Read AI 10-sep 1:21 pm si SIN registro en bitácora; por validar
81 2026-09-10 2026-W37 2 E2-03 Preparación de la segunda sesión de captura: lista de lo que hay que recabar en DevTools (Generar venta sobre la 1078, Preserve log, Fetch/XHR) 0.25 reconstruida transcript de Claude Code 65198403 (12:21) si Corte 2-oct; por validar
82 2026-09-10 2026-W37 2 E2-03 Contrato real de AddInvoiceTransaction (PrevInvoice/FiscalInvoice/IsCFDI + QuoteID + Number), sonda autorizada op #12 (remisión 1004 creada y cancelada), BindUiClient con login de formulario, endpoints prefactura-ui, vista de cotizaciones sin ticket y mensaje a Pedro con las alternativas 1.75 reconstruida transcript de Claude Code 65198403 (13:21–13:29, 14:15–15:49, 16:50–16:53); commit 4d6b3d8 si Corte 2-oct; la sesión con Pedro (1:00–1:21) ya está en su fila; por validar
83 2026-09-15 2026-W38 1 E1-05 Corte de horas 6-ago→15-sep (reconstrucción desde commits, correos e informes de reunión), Excel de avance, commits y publicación de ambos repositorios 0.75 reconstruida planeacion/Seguimiento-horas.csv si sesión asistida por IA; por validar
84 2026-09-16 2026-W38 1 E1-05 Excel de avance publicado en Google Sheets (Drive/Balam vía rclone, enlace compartible) y corte del 16-sep regenerado desde script 0.50 reconstruida planeacion/generar_avance_2026_09_16.py si sesión asistida por IA; por validar
85 2026-09-29 2026-W40 2 E2-03 Sesión 7:00 am 'Seguimiento PAF - Integración ERP' con Pedro: validación de accesos, Balam entrega login web a BIND con permisos de prefactura y error al cargar la solicitud al cotizar 0.50 reconstruida correo Read AI 29-sep 7:23 am; REGISTRO #83 si Corte 2-oct; por validar
86 2026-09-29 2026-W40 2 E2-03 Cierre de los huecos de bitácora #71–#83 desde el buzón (informes de Read AI y correos del 21-ago al 29-sep) e investigación de cierre con el SDK oficial de BIND (modelo de dos endpoints) 0.75 reconstruida bitacora/REGISTRO.md#71-83; commit 3de2739 08:06 si Corte 2-oct; otra máquina, inicio estimado al terminar la sesión (7:23); por validar; movida de E1-05 a E2-03 el 2-oct: documenta las sesiones de prefactura e investiga el SDK de BIND (regla 7)
87 2026-09-29 2026-W40 2 E2-03 Puente de prefactura con el usuario propio CTO: credenciales y API key nueva en user-secrets, app en modo escritura, PRIMERA PREFACTURA REAL desde la plataforma (folio 2467, 16:08), una sesión por usuario, API interno de la web app y contención de la escritura accidental (FillQuote: cotización 1079 Surtida) 1.50 reconstruida transcript de Claude Code 65198403 y 02f58158 (10:54–11:21, 13:24–13:39, 15:28–16:26); balam-plataforma/CLAUDE.md si Corte 2-oct; dos sesiones en paralelo, contadas una sola vez; por validar
88 2026-09-29 2026-W40 2 AJ-01 Webhook de Jira FAC: análisis (leer la creación de tickets vs ADR-001), plan de 5 fases, Fase 1 por GET (token de Pedro con ADMINISTER), endpoint POST /api/jira/webhook con HMAC, idempotencia y relectura (38/38), túnel ngrok y piloto e2e: webhook id 1 + FAC-169 detectado en 14.4 s y borrado 1.00 reconstruida transcript de Claude Code 02f58158 (15:34–15:41, 17:57–18:14, 18:39–19:08); REGISTRO #84; commit d7238da si Corte 2-oct; incluye plan, bitácora y push de 4d6b3d8 (rebase con 3 conflictos); por validar
89 2026-09-29 2026-W40 2 AJ-01 Cierre de la sesión: push de ambos repos, appsettings.Local portátil de los user-secrets y relevo a la máquina con acceso a paputec 0.50 reconstruida transcript de Claude Code 02f58158 (23:47–00:11); commits 8479e96, 5145801, e5912bf si Corte 2-oct; por validar
90 2026-09-30 2026-W40 2 AJ-01 Webhook en producción (paputec): fusión de las dos bitácoras del 29-sep, publicación de 5145801 (116 pruebas, respaldo y rollback), secreto nuevo, PUT del webhook id 1 y prueba e2e con FAC-170 detectada en 1.05 s 0.50 reconstruida bitacora/REGISTRO.md#85; commits e88b5e1 00:19, 9351aa4 00:30, 7e50c71 00:31 si Corte 2-oct; ≈12:10–12:35 am; por validar
91 2026-09-30 2026-W40 2 AJ-01 Vista previa automática de la cotización por cada ticket detectado (cola, trabajador y port del prellenado; 147 pruebas), push y publicación; causa del 500 de BIND: la API key de arturo (la de cto responde 200) 0.50 reconstruida bitacora/REGISTRO.md#86; commits 1c37eaa 00:45, 287cb65, 085e903 00:55 si Corte 2-oct; ≈12:35–1:00 am; por validar
92 2026-09-30 2026-W40 2 AJ-01 API key de cto en producción (el sync vuelve tras 20 días), radiografía del formulario contra el catálogo, vista previa validada contra BIND real con FAC-171 y modal en vivo con 'Aceptar', campana y modo automático configurable (170 pruebas) 0.75 reconstruida bitacora/REGISTRO.md#87; commits aac7447 01:40, bdafb06 01:42 si Corte 2-oct; ≈1:00–1:45 am; por validar
93 2026-09-30 2026-W40 2 AJ-01 Push y publicación del modal en vivo, con el modo automático permitido en el servidor (sin activar) 0.25 reconstruida bitacora/REGISTRO.md#87 adenda; commits 29a354b, 8799348 06:55 si Corte 2-oct; respaldo de las 6:53 am; por validar
94 2026-09-30 2026-W40 2 E2-03 Puente de prefactura encendido en producción, 'Servicio de Enlace' → ítem 106 y relectura completa de cotizaciones (11 con estado atrasado), tras lo que Pedro vio a las 7:26 en el wizard de FAC-173 0.75 reconstruida bitacora/REGISTRO.md#88 si Corte 2-oct; 7:50–8:30 am; por validar
95 2026-09-30 2026-W40 2 E2-03 Cuatro bugs reales del puente y de los importes (Total en cero, USD mostrado en pesos e IVA doble, post-proceso que escondía la prefactura, duplicados posibles) con pruebas que los reproducen (197), panel bajo la fila y sync de cotizaciones por barrido completo 1.25 reconstruida bitacora/REGISTRO.md#88; commits 597c8a2 21:16, 63af3d6 21:29 si Corte 2-oct; 8:30–9:40 pm; por validar
96 2026-09-30 2026-W40 2 E2-03 Credenciales del usuario CTO en el .env de producción (autorización de Johann), push y publicación de 597c8a2 con la migración QuoteSubtotal y verificación del primer ciclo del sync 0.25 reconstruida bitacora/REGISTRO.md#88 adenda; commits 4a219b5, 604cb8d 21:49 si Corte 2-oct; 9:40–9:50 pm; por validar
97 2026-09-30 2026-W40 2 E2-03 Timbrado desde la plataforma (POST /api/emision/timbrar-ui: EmitCFDI con dry-run, palabra TIMBRAR y verificación por lectura), estado incierto cerrado en los POST, consecutivo sin la serie B, uso CFDI del mismo cliente, 212 pruebas, recorrido en navegador, publicación y guion de la prueba del 1-oct 0.75 reconstruida bitacora/REGISTRO.md#89; commits f5b3743 22:40, 9874547 22:45 si Corte 2-oct; 10:00–10:45 pm según respaldo (04:40 UTC) y último commit — el título del #89 dice 10:00 pm–12:50 am: Johann no recuerda la hora de cierre (2-oct): se queda la cota de la evidencia
98 2026-09-30 2026-W40 2 E2-03 Llamada con Pedro (7:26 am): revisión del wizard de FAC-173 en producción — prefactura deshabilitada, totales en cero, campo de servicios y Arturo en la lista de aprobadores 0.25 validada bitacora/REGISTRO.md#88 si Corte 2-oct; ~15 min según Johann (2-oct)
99 2026-10-01 2026-W40 2 E2-04 Prueba completa con Balam en producción: ticket → cotización → prefactura; el timbrado (prefactura → CFDI) no se intentó y queda por probar 0.25 validada planeacion/Guion-prueba-completa-2026-10-01.md si Corte 2-oct; ~20 min o menos según Johann (2-oct)
100 2026-10-02 2026-W40 2 E2-03 Prefacturas en lote (Cotizaciones y bandeja → una prefactura por cotización, en serie, folios consecutivos con el API lento) y timbrado también en el detalle de la factura (213 pruebas, recorrido en navegador); push y publicación 1.00 reconstruida bitacora/REGISTRO.md#90; commits 73d1c2a 08:35, 4f3eb99, 4e90543 08:41 si Corte 2-oct; 7:40–8:40 am; por validar
101 2026-10-02 2026-W40 1 E1-05 Corte de horas 16-sep → 2-oct: pull de ambos repos, reconstrucción desde los transcripts de Claude Code, los commits y los informes de Read AI, y Excel de avance 0.75 confirmada transcript de Claude Code a134588d (11:47–12:25); planeacion/generar_avance_2026_10_02.py si Corte 2-oct; sesión asistida por IA; tiempo real. No se cobran la proyección de costos, la reclasificación ni los ajustes posteriores del Excel (~0.75 h, decisión de Johann 2-oct)
102 2026-10-02 2026-W40 2 E2-04 Manual de usuario en PDF (14 páginas): acceso y roles, consulta, solicitudes de Jira, ticket → cotización (aviso en vivo, pantalla y lote), prefactura (individual, emisión asistida y lote), timbrado, reglas y mensajes frecuentes, con capturas de la demo 0.50 confirmada manual/Manual-usuario-Balam.pdf; transcript de Claude Code a134588d (12:25–12:55) si Corte 2-oct; sesión asistida por IA; tiempo real de esta sesión
+89 -1
View File
@@ -5,7 +5,7 @@ Fuente local de horas efectivamente trabajadas para el proyecto Balam. Se mantie
**Tarifa contractual:** $600 MXN/h + IVA
**Cadencia:** corte semanal; factura los viernes; pago a 30 días
**Datos tabulares:** [`Seguimiento-horas.csv`](Seguimiento-horas.csv)
**Última actualización:** 2026-09-16 (corte 6-ago → 16-sep reconstruido, por validar)
**Última actualización:** 2026-10-02 (corte 16-sep → 2-oct reconstruido, por validar)
## Reglas de captura
@@ -18,6 +18,94 @@ Fuente local de horas efectivamente trabajadas para el proyecto Balam. Se mantie
7. El esfuerzo real de una sesión incluye TODO su alrededor, no solo el desarrollo: el análisis/pensamiento previo para decidir qué se hace, la documentación de cada parte (bitácora, pendientes, investigación), y la comunicación asociada (cada correo, cada mensaje de WhatsApp, minutas y reportes). Definido por Johann el 6-ago.
8. Para proyectar el costo final del proyecto, las horas no comprimibles (sesiones con Balam, pruebas acompañadas, capacitación, demos, gestión y comunicación) son el piso, y el objetivo de cierre es ese piso y un poco más — no el piso pelón ni el rango completo de la propuesta. Los cortes ya reportados a Erika nunca se recalculan retroactivamente (ajustado por Johann el 6-ago).
## Corte al 2-oct (reconstruido, por validar)
Del 16-sep al 2-oct no se capturó nada en el CSV. El 2-oct se agregaron **15.25 h**, en 21 filas con la nota `Corte 2-oct`:
- 13.50 h `reconstruida`.
- 0.50 h `validada`: Johann dio la duración el 2-oct.
- 1.25 h `confirmada`: tiempo real de la sesión del 2-oct (solo lo que se cobra).
Las fuentes fueron:
- los registros de las sesiones de Claude Code de esta máquina (marcas de tiempo de cada mensaje; las sesiones paralelas se unieron para no contar doble);
- los commits de ambos repos;
- los rangos horarios que anota la bitácora (#83–#90);
- el informe de Read AI del 29-sep.
**Johann debe validar las horas `reconstruida`** antes de reportarlas. El corte del 16-sep (94.58 h) no se tocó.
| Etapa | Horas al corte | Rango | Avance | Estado |
|---|---:|---:|---:|---|
| Etapa 0 | **21.83 h** | 18–22 h | 100% | Cerrada y validada |
| Etapa 1 | **32.50 h** | 32–39 h | 97% | Capa de escritura completa; Azure entregado, falta desplegar (75%); CI/CD 50% |
| Etapa 2 (sin ajuste Jira) | **43.50 h** | 38–46 h | 97% | Cadena completa validada en real: Pedro timbró desde la plataforma el 2-oct ([#91](../bitacora/REGISTRO.md)); quedan ajustes de UI/UX (fila nueva) |
| » Ajuste Jira → BIND | **10.50 h** | 7–10 h | 100% | 0.5 h arriba del tope: webhook en producción, vista previa automática y modal "Aceptar" |
| Etapa 3 | **1.50 h** | 24–29 h | 13% | Solo cartera por moneda (25%), endurecimiento y runbook |
| **Total** | **109.83 h** | | 81% | 55.00 h desde el 6-ago: 52.25 `reconstruida` + 2.25 `confirmada` + 0.50 `validada` |
Movimientos entre etapas en este corte (el total no cambia): 7.75 h de cotización contra BIND salen del ajuste Jira hacia la Etapa 2, y 1.75 h de documentación técnica salen de la gestión de la Etapa 1 hacia la Etapa 2 (ver abajo).
**De dónde salen las 15.25 h:**
- **9/10-sep (3.25 h) — no entraron al corte del 15-sep.** El puente a la web app de BIND para la prefactura (`BindUiClient`, contrato de `AddInvoiceTransaction`, sonda op #12) se construyó en esta máquina y se quedó sin commit hasta el 29-sep (`4d6b3d8`). Por eso la reconstrucción del 15-sep, hecha en la otra máquina, no lo vio. Las dos sesiones con Pedro de esos días ya estaban en el CSV y no se duplican.
- **29-sep (4.25 h):** la sesión de las 7:00 con Pedro (login web a BIND) y el cierre de huecos de la bitácora. Luego, en esta máquina, la primera prefactura real (folio 2467) y el piloto del webhook. Esas dos sesiones corrieron en paralelo y se cuentan una sola vez.
- **30-sep (5.25 h), en la otra máquina:** el webhook pasa a producción, con la vista previa automática y el modal en vivo. Hubo una llamada de 15 min con Pedro a las 7:26. Después, el puente se encendió en producción con cuatro bugs corregidos y se construyó el timbrado.
- **1-oct (0.25 h):** prueba completa con Balam, de unos 20 min según Johann. Llegó a prefactura; el timbrado no se intentó.
- **2-oct (2.25 h):**
- Prefacturas en lote (1.00 h).
- Este corte en sí (0.75 h). La proyección de costos, la reclasificación y los ajustes posteriores del Excel (~0.75 h) **no se cobran** (decisión de Johann, 2-oct).
- El manual de usuario en PDF (0.50 h, `manual/Manual-usuario-Balam.pdf`).
- El acompañamiento por WhatsApp a Pedro mientras timbraba (3:28–3:46 pm) **no se cobra** (decisión de Johann, 2-oct).
- **Reparto CSV → filas del Excel** (`generar_avance_2026_10_02.py`; el script se detiene si no cuadra):
- E1-05 → seguimiento semanal
- E2-03 → conversión cotización→factura
- E2-04 → pruebas integrales + manual
- AJ-01 → disparador → cotización automática
- Además se movieron 0.25 h de "Creación de cotizaciones" a "Tipo de cambio" (el TC se resolvió el 12-ago dentro de ese bloque), para que ninguna fila cerrada quede con 0 h.
**Reclasificación del 2-oct (Johann):** las 7.75 h que el corte del 15-sep cargó al ajuste Jira (`AJ-01`) y que en realidad fueron cotización contra el API de BIND pasan a la Etapa 2 como `E2-02`, a la fila «Creación de cotizaciones». Son las sesiones acompañadas del 12-ago, las dos del 21-ago y las dos del 3-sep, el Excel de catálogos, el diagnóstico del 500 y la investigación hasta el folio 1026. Así quedan:
| Línea | Antes | Ahora | Rango |
|---|---:|---:|---:|
| Ajuste Jira → BIND | 18.25 h | **10.50 h** ($6,300) | 7–10 h |
| Disparador → cotización automática | 8.00 h | **3.50 h** (webhook, vista previa, modal) | 3–4 h |
| Trazabilidad | 4.75 h | **1.50 h** | 2–3 h |
| Etapa 2 | 34.00 h | **41.75 h** | 38–46 h |
| Creación de cotizaciones | 7.75 h | **15.50 h** | 5–6 h |
El total del proyecto no cambia. Lo que Balam aprobó y controla es el rango por etapa, y ninguna etapa queda rebasada. A nivel fila, «Creación de cotizaciones» y «Conversión cotización→factura» superan su estimado: el API de BIND no documentaba cómo cotizar ni permitía crear prefacturas.
**Fila de seguimiento y gestión (2-oct, Johann):** la fila «Demostración semanal (viernes) + reporte de horas» no tenía demostraciones: las sesiones con Pedro se cargan a las filas técnicas. Lo que tenía era gestión. Por eso:
- Se renombra a «Seguimiento y gestión: reportes de horas, bitácora y comunicación con Balam».
- Lo técnico se va a su fila (regla 7):
- 20-ago: 1.00 h del registro #65 y el guion de la sesión del 21-ago pasan a «Creación de cotizaciones». La fila del CSV se dividió según los commits: 5d846ec 23:02 para el cruce del buzón; a03f81f, cabe0fe y 072a3cd para lo de cotización.
- 29-sep: 0.75 h (registros #71–#83 e investigación del SDK de BIND para la prefactura) pasan a «Conversión».
- La fila queda en **8.00 h**, unos 40 minutos por semana en 12 semanas:
| Qué | Horas | Fechas y evidencia |
|---|---:|---|
| Cortes de horas y Excel de avance | 3.75 | 15-jul 0.50 · 16-jul 1.00 (Excel y mensaje a Erika) · 21-jul 0.25 · 15-sep 0.75 · 16-sep 0.50 (Google Sheets) · 2-oct 0.75 |
| Bitácora | 2.25 | 1-ago 0.50 (#59–#60, commit 2b862ee) · 11-ago 0.75 (#62–#63, commits f28fcc5 y a2f338f) · 20-ago 1.00 (cruce del buzón, commit 5d846ec) |
| Comunicación con Balam | 2.00 | 28-jul 0.25 (Azure a Erika) · 29-jul 0.75 (envío de la v1.3) · 20-ago 0.50 (WhatsApp con Pedro) · 25-ago 0.50 (correo de Azure a Noé) |
| **Total** | **8.00** | El estimado de 2–3 h de la propuesta era para demostraciones y reporte, no para la gestión de 12 semanas |
**Decisiones de Johann sobre el Excel (2-oct):**
- **Fechas reales.** Inicio = primer día trabajado. Fin = día en que se terminó el entregable según la bitácora y los commits; en las tareas en curso, el último día trabajado.
- **Orden.** Las filas van en orden cronológico dentro de cada etapa; el bloque del ajuste Jira → BIND sigue al frente de la Etapa 2.
- **Avance de los encabezados.** Es el promedio de sus filas ponderado por las horas estimadas (regla 4). Antes se calculaba con horas consumidas entre horas estimadas.
- **Al 100%.**
- Capa de escritura, Disparador → cotización automática, Creación de cotizaciones, Pruebas integrales + manual y Demostración semanal (al día).
- Conversión cotización→factura y Trazabilidad: se cerraron al 100% el 2-oct, cuando Pedro timbró desde la plataforma y completó todo el proceso. Los ajustes de UI/UX que quedan (botones «Ver en Jira» / «Ver en BIND» y las pantallas de Solicitudes, Cotizaciones y Facturas) no reabren esas filas: van en una fila nueva de la Etapa 2, «Ajustes de UI/UX de la emisión» (⚪ Próxima, 0%, 1–2 h estimadas), que deja la Etapa 2 en 97%.
- Con alcance ajustado: Catálogo de clientes (Balam eliminó la lista blanca el 6-jul), Detalle de factura (el XML se consulta en BIND), Exportación (queda CSV) y Tipo de cambio (sale del catálogo de monedas de BIND; el proceso del DOF sale del alcance).
- **Otros avances.**
- Azure sube a 75%: Balam ya dio el acceso, aunque falta volver a pedir la contraseña.
- CI/CD queda en 50%.
- Cobranza queda en 25%: la cartera con vencidos existe desde el 1-ago.
- Endurecimiento y documentación técnica se quedan como estaban.
**Sin registro de horas:**
- Timbrado del 30-sep (#89): se dejó en 0.75 h (10:00–10:45 pm según el respaldo y el último commit). Johann no recuerda si siguió hasta las 12:50 am, como dice el título del registro.
- Comunicación del 16 al 28-sep (convocatoria del 28-sep, credenciales en TXT, autorización escrita de Pedro para el webhook) y la fecha en que Balam dio el acceso a Azure.
- Las duraciones salen de la actividad dentro de las sesiones de trabajo. Una pausa de más de 20 minutos corta el bloque, así que el tiempo de revisar o pensar fuera de la sesión no aparece (regla 7). Johann puede subirlas.
## Corte al 16-sep (reconstruido, por validar)
Del 10-ago al 15-sep no se capturó nada en el CSV; el 15-sep se reconstruyeron **39.25 h** (más 0.50 h del 16-sep: Excel publicado en Google Sheets) con evidencia fechada (commits de ambos repos, correos, informes de reunión de Read AI y mtimes de archivos) y quedaron marcadas `reconstruida` (salvo la sesión del 12-ago, `confirmada` por transcript). **Johann debe validarlas** (o ajustarlas) antes de reportarlas; el corte del 6-ago (54.83 h) no se tocó.
+1
View File
@@ -0,0 +1 @@
Plan-actividades-avance-2026-07-27.xlsx
+622
View File
@@ -0,0 +1,622 @@
"""Genera Avance-Balam-2026-10-02.xlsx a partir del corte del 16-sep.
Horas — corte del viernes 2-oct: todo lo nuevo en `Seguimiento-horas.csv`
lleva la nota "Corte 2-oct":
- 3.25 h del 9/10-sep que no entraron al corte del 15-sep: el puente a la
web app de BIND para la prefactura vivía sin commit en esta máquina (se
subió el 29-sep en 4d6b3d8); fuente: transcripts de Claude Code.
- 29-sep: sesión con Pedro (login web a BIND), primera prefactura real
(folio 2467) y webhook de Jira FAC (piloto con FAC-169).
- 30-sep: webhook en producción, vista previa automática, modal en vivo,
llamada con Pedro (7:26), puente encendido en producción con cuatro bugs
corregidos y timbrado.
- 1-oct: prueba completa con Balam (llegó a prefactura; timbrado sin probar).
- 2-oct: prefacturas en lote, manual de usuario y este corte. Ese día Pedro
timbró desde la plataforma (cadena completa con CFDI), con lo que se cierran
«Conversión» y «Trazabilidad»; el acompañamiento por WhatsApp no se cobra, y del
corte solo se cobran 0.75 h (no la proyección de costos ni la reclasificación).
- La fila "Demostración semanal + reporte de horas" pasa a llamarse "Seguimiento y
gestión"; lo técnico que traía (20-ago 1.00 h, 29-sep 0.75 h) va a su fila.
- Hoja 2 "Desglose de horas": cada registro del CSV con fecha, etapa, actividad,
horas, monto, estado y evidencia, más un resumen por etapa que cuadra con la hoja 1.
Quedan ajustes de UI/UX de esas pantallas, como fila nueva de la Etapa 2.
Avance y fechas — decisiones de Johann del 2-oct:
- Fin = día en que se terminó el entregable según bitácora y commits (las
tareas en curso: último día trabajado). Los encabezados de etapa toman el
primer Inicio y el último Fin de sus filas.
- Dentro de cada etapa las filas van en orden cronológico (Inicio, luego Fin);
las no iniciadas al final. El bloque del ajuste Jira → BIND se queda al
frente de la Etapa 2 (decisión del 29-jul), con sus filas también en orden.
- El avance de cada encabezado es el promedio de sus filas ponderado por las
horas estimadas (regla 4: el avance mide entregable, no horas). La Etapa 2
excluye las filas del ajuste, igual que sus horas.
- Cierres con alcance ajustado: lista blanca (Balam la eliminó el 6-jul), XML
(se consulta en BIND), XLSX (queda CSV) y tipo de cambio del DOF (se toma
del catálogo de monedas de BIND).
- Estatus simples: una línea de estado y viñetas cortas en cada fila; sin
bloque de notas debajo de la tabla (el detalle vive en Seguimiento-horas.md).
El corte del 16-sep no se toca.
"""
import csv
import os
from collections import defaultdict
from copy import copy
from datetime import datetime
import openpyxl
from openpyxl.styles import Alignment, Border, Font, PatternFill, Side
BASE = os.path.dirname(os.path.abspath(__file__))
SRC = os.path.join(BASE, "Avance-Balam-2026-09-16.xlsx")
DST = os.path.join(BASE, "Avance-Balam-2026-10-02.xlsx")
CSV_HORAS = os.path.join(BASE, "Seguimiento-horas.csv")
CAFE, GRIS = "331F0E", "595959"
V_FILL, V_FONT = "C6EFCE", "006100"
A_FILL, A_FONT = "FFEB9C", "9C6500"
C_NOM, C_INI, C_FIN, C_PCT, C_EST, C_TRAB, C_STAT = 1, 2, 3, 4, 5, 6, 7
CORTE_ANTERIOR = 94.58 # corte del 16-sep
MARCA = "Corte 2-oct" # nota de las filas nuevas del CSV (incluye fechas del 9/10-sep)
SIN = "—"
# --- CSV: totales por etapa y por actividad -----------------------------------
por_act, por_etapa, nuevas_act = defaultdict(float), defaultdict(float), defaultdict(float)
nuevas, desde_6ago = 0.0, 0.0
with open(CSV_HORAS, encoding="utf-8", newline="") as fh:
for r in csv.DictReader(fh):
if not (r.get("fecha") or "").strip():
continue
h = float(r["horas"])
por_act[r["actividad_id"]] += h
por_etapa[r["etapa"]] += h
if MARCA in r["notas"]:
nuevas += h
nuevas_act[r["actividad_id"]] += h
if r["fecha"] > "2026-08-06":
desde_6ago += h
E0, E1, E3 = por_etapa["0"], por_etapa["1"], por_etapa["3"]
AJ = por_act["AJ-01"]
E2 = por_etapa["2"] - AJ
TOTAL = E0 + E1 + E2 + AJ + E3
assert abs(TOTAL - CORTE_ANTERIOR - nuevas) < 0.01, "El CSV no arranca del corte del 16-sep"
# Reparto CSV → filas del Excel (mismo criterio que el corte del 15-sep):
FILA_POR_ACT = {
"E1-05": "Demostración semanal", # gestión, bitácora, cortes
"E2-03": "Conversión cotización→factura", # prefactura, timbrado, lote
"E2-04": "Pruebas integrales", # prueba completa del 1-oct + manual de usuario
"AJ-01": "Disparador por estatus", # webhook, vista previa, modal
}
sin_fila = set(nuevas_act) - set(FILA_POR_ACT)
assert not sin_fila, f"Actividades nuevas sin fila en el Excel: {sin_fila}"
DELTAS_TRAB = defaultdict(float)
for a, h in nuevas_act.items():
DELTAS_TRAB[FILA_POR_ACT[a]] += h
# Reasignación entre filas (suma cero): el tipo de cambio se resolvió el 12-ago dentro del
# bloque de cotización (CSV 2026-08-12, E2-02: "tipo de cambio, prefill y selector de cliente").
DELTAS_TRAB["Creación de cotizaciones desde la plataforma"] -= 0.25
DELTAS_TRAB["Tipo de cambio del DOF"] += 0.25
# Reclasificación del 2-oct (decisión de Johann, suma cero): las 7.75 h de cotización contra el API
# de BIND que el corte del 15-sep cargó al ajuste Jira pasan a la Etapa 2 (en el CSV, AJ-01 → E2-02).
# Disparador −4.50: sondeo de FAC-106 y diagnóstico del 500 (20-ago 1.50), investigación hasta el folio 1026 (21-ago 3.00)
# Trazabilidad −3.25: sesión 12-ago 1.00, Excel de catálogos 0.75, sesiones 21-ago 0.75 y 3-sep 0.75
DELTAS_TRAB["Disparador por estatus"] -= 4.50
DELTAS_TRAB["Trazabilidad ticket"] -= 3.25
DELTAS_TRAB["Creación de cotizaciones desde la plataforma"] += 7.75
# 20-ago: de las 2.00 h de "bitácora", 1.00 h fue el registro #65 de la sesión de cotización y el guion del 21-ago
# (commits a03f81f, cabe0fe, 072a3cd) → se dividió en el CSV (E1-05 1.00 + E2-02 1.00) y aquí sigue a su fila.
DELTAS_TRAB["Demostración semanal"] -= 1.00
DELTAS_TRAB["Creación de cotizaciones desde la plataforma"] += 1.00
assert abs(sum(DELTAS_TRAB.values()) - nuevas) < 0.01, (
f"El reparto ({sum(DELTAS_TRAB.values()):.2f}) no suma las {nuevas:.2f} h nuevas del CSV")
HORAS_ENCABEZADO = {
"Integracion de plataformas Balam": TOTAL,
"Etapa 0": E0,
"Etapa 1": E1,
"Etapa 2": E2,
"» Ajuste dentro de la Etapa 2": AJ,
"Etapa 3": E3,
}
ETAPAS = ("Etapa 0", "Etapa 1", "Etapa 2", "Etapa 3")
AJUSTE = "» Ajuste dentro de la Etapa 2"
AJUSTE_HIJOS = ("Cliente de la API de Jira", "Disparador por estatus", "Trazabilidad ticket")
# --- Fechas reales de las filas que cambian (dd/mm/yyyy) -----------------------
FECHAS_REALES = {
"Demostración semanal": ("15/07/2026", "02/10/2026"), # corte del 2-oct
"Capa de escritura controlada": ("19/07/2026", "30/09/2026"), # diseño 19-jul → timbrado 30-sep
"Disparador por estatus": ("29/09/2026", "30/09/2026"), # webhook (29-sep) → vista previa y modal (30-sep)
"Trazabilidad ticket": ("10/08/2026", "02/10/2026"), # definiciones 10-ago → Pedro completa la cadena con CFDI 2-oct
"Creación de cotizaciones desde la plataforma": ("06/08/2026", "30/09/2026"), # total desde BIND corregido 30-sep
"Conversión cotización→factura": ("06/08/2026", "02/10/2026"), # puente 9/10-sep → lote 2-oct
"Tipo de cambio del DOF": ("12/08/2026", "12/08/2026"), # TC del catálogo de monedas de BIND
"Pruebas integrales": ("30/09/2026", "02/10/2026"), # recorrido e2e 30-sep, Balam 1-oct, manual 2-oct
"Módulo de cobranza": ("21/07/2026", "01/08/2026"), # cartera por moneda (B4) → vista de vencidas
}
# --- Avance de las filas que cambian -----------------------------------------
AVANCE = {
"Demostración semanal": 1.0,
"Capa de escritura controlada": 1.0,
"Configuración de infraestructura en Azure": 0.75,
"CI/CD": 0.5,
"Definición de alcance Jira": 1.0, # decía "falta el estatus disparador", viejo desde el 7-ago (REGISTRO #62)
"Disparador por estatus": 1.0,
"Trazabilidad ticket": 1.0, # 2-oct: Pedro timbró desde la plataforma, proceso completo
"Detalle de factura": 1.0,
"Catálogo de clientes": 1.0,
"Tipo de cambio del DOF": 1.0,
"Creación de cotizaciones desde la plataforma": 1.0,
"Conversión cotización→factura": 1.0, # 2-oct: primer CFDI timbrado desde la plataforma
"Exportación de facturas": 1.0,
"Pruebas integrales": 1.0,
"Ajustes de UI/UX de la emisión": 0.0,
"Módulo de cobranza": 0.25,
}
# --- Estatus: una línea de estado y viñetas cortas (todas las filas de detalle) --
OK, CURSO, PROX = "✅ Completada", "🟡 En curso", "⚪ Próxima"
ESTATUS = {
# Etapa 0
"Sesión de arranque (kickoff)": [OK],
"Entrega y validación de accesos": [OK],
"Validación técnica de la API de BIND": [OK],
"Sesión de Discovery": [OK],
"Prototipo visual navegable": [OK],
"Documento de hallazgos": [OK],
"Sesión de validación del prototipo": ["✅ Aprobado por Balam (24-jul)"],
# Etapa 1
"Repositorio privado GitHub": [OK],
"Backend base": [OK],
"Arquitectura multi-tenant": [OK],
"Cliente de la API de BIND": [OK],
"Demostración semanal": ["✅ Al día",
"Cortes de horas y Excel de avance (último: 2-oct)",
"Bitácora y comunicación con Balam",
"Las sesiones con Pedro van en las filas técnicas"],
"Sincronización de clientes": [OK],
"Capa de escritura controlada": [OK,
"Vista previa + confirmación humana + auditoría",
"Cotización (21-ago), prefactura (29-sep) y timbrado (30-sep)",
"Timbrado validado en real por Balam (2-oct)"],
"Sesión de reglas y dudas": [OK],
"Sincronización de facturas": [OK],
"Modelo de facturas": [OK],
"Definición de alcance Jira": [OK,
"v1.3 aprobada por Noé (30-jul)",
"Disparador y campos definidos por Balam (7-ago)"],
"Configuración de infraestructura en Azure": [CURSO,
"Balam ya dio el acceso",
"Falta: volver a pedir la contraseña y desplegar",
"Mientras tanto corre en paputec.mx (staging)"],
"CI/CD": [CURSO,
"Despliegue reproducible con respaldo y rollback",
"Secretos fuera del repositorio",
"Falta: el pipeline en Azure"],
# Etapa 2 · ajuste Jira → BIND
"Cliente de la API de Jira": [OK,
"Bandeja y detalle de tickets FAC en vivo",
"Adjuntos descargables"],
"Trazabilidad ticket": [OK,
"Cadena ticket → cotización → prefactura → CFDI visible",
"Pedro completó el proceso con timbrado (2-oct)",
"El tablero se alimenta en la Etapa 3"],
"Disparador por estatus": [OK,
"Webhook de Jira en producción (30-sep)",
"Ticket detectado en ~1 s, cotización lista para aceptar",
"De Balam: 7 categorías sin ítem en BIND"],
# Etapa 2
"Andamiaje del frontend": [OK],
"Listado de facturas": [OK],
"Detalle de factura": [OK,
"Descarga del PDF",
"El XML se consulta en BIND"],
"Catálogo de clientes": [OK,
"Catálogo y búsqueda",
"Lista blanca eliminada por Balam (6-jul)"],
"Exportación de facturas": [OK,
"Facturas y clientes a CSV (abre en Excel)"],
"Layout base": [OK],
"Creación de cotizaciones desde la plataforma": [OK,
"Individual y en lote",
"Vista previa + confirmación humana"],
"Conversión cotización→factura": [OK,
"Prefactura validada con Balam (1-oct)",
"Prefacturas en lote (2-oct)",
"Pedro timbró desde la plataforma (2-oct)"],
"Tipo de cambio del DOF": [OK,
"Se toma del catálogo de monedas de BIND"],
"Pruebas integrales": [OK,
"Prueba completa con Balam (1-oct)",
"Timbrado real desde la plataforma (2-oct)",
"213 pruebas automáticas",
"Manual de usuario en PDF (2-oct)"],
# Etapa 3
"Módulo de cobranza": [CURSO,
"Cartera por moneda con vencidos",
"Falta: rangos 30/60/90 y vista por vencer"],
"Endurecimiento de seguridad": [CURSO,
"Rate limit y bloqueo de login",
"CSP/HSTS y respaldos diarios"],
"Documentación técnica": [CURSO,
"Runbook de publicación, operación y rollback"],
"Alertas internas": [PROX],
"Tablero directivo": [PROX],
"Reporte operativo": [PROX],
"Sesión de capacitación": [PROX],
"Soporte posterior": ["⚪ Incluido en el cierre, sin costo"],
"Ajustes de UI/UX de la emisión": [PROX,
"Botones «Ver en Jira» / «Ver en BIND»",
"Pantallas de Solicitudes, Cotizaciones y Facturas"],
}
ANCHO_ESTATUS = 46 # ancho de la columna G (caracteres)
def texto_estatus(lineas):
return "\n".join([lineas[0]] + [f"• {l}" for l in lineas[1:]])
def alto_fila(*textos_y_anchos):
"""Alto en puntos para que quepa el texto envuelto (fuente 9-10 pt)."""
renglones = 1
for texto, ancho in textos_y_anchos:
n = sum(max(1, -(-len(l) // max(1, int(ancho * 1.05)))) for l in str(texto or "").split("\n"))
renglones = max(renglones, n)
return max(15, 12 * renglones + 4)
def pinta(celda, fill, font_color, bold=False, size=9, align="left"):
celda.fill = PatternFill("solid", fgColor=fill)
celda.font = Font(color=font_color, bold=bold, size=size)
celda.alignment = Alignment(horizontal=align, vertical="center", wrap_text=True)
def fecha(v):
return datetime.strptime(v, "%d/%m/%Y") if isinstance(v, str) and v.count("/") == 2 else None
def estimadas(v):
"""Punto medio de 'Horas estimadas' ('2', '4–5', '1.5–2'); 0 si no aplica ('—')."""
if isinstance(v, (int, float)):
return float(v)
partes = [p for p in str(v or "").replace(" ", "").split("–") if p]
try:
nums = [float(p) for p in partes]
except ValueError:
return 0.0
return sum(nums) / len(nums) if nums else 0.0
wb = openpyxl.load_workbook(SRC)
ws = wb.active
ws["A2"] = ("Plan y avance al 2-oct-2026 · webhook de Jira FAC en producción con vista previa y modal 'Aceptar', "
"prefactura por la web app de BIND, timbrado desde la plataforma y prefacturas en lote · horas del "
"10-ago al 2-oct reconstruidas, pendientes de validar")
ws["A2"].font = Font(size=10, italic=True, color=GRIS)
# Notas del corte anterior: se limpian y se reescriben abajo.
primera_nota = None
for row in range(5, ws.max_row + 1):
v = str(ws.cell(row=row, column=C_NOM).value or "")
if v.startswith("✅ Completada ·"):
primera_nota = row
if primera_nota and row >= primera_nota:
for rango in list(ws.merged_cells.ranges):
if rango.min_row == row:
ws.unmerge_cells(str(rango))
ws.cell(row=row, column=C_NOM).value = None
tope = primera_nota if primera_nota else ws.max_row + 1
# Fila nueva (Johann, 2-oct): ajustes de UI/UX pendientes de la Etapa 2, al final de la etapa. Toma el
# formato de «Pruebas integrales», que en el corte del 16-sep era una fila ⚪ sin iniciar.
UIUX = "Ajustes de UI/UX de la emisión: «Ver en Jira» / «Ver en BIND» y pantallas de Solicitudes, Cotizaciones y Facturas"
assert not any(str(ws.cell(row=r, column=C_NOM).value or "").startswith("Ajustes de UI/UX") for r in range(5, tope))
modelo = next(r for r in range(5, tope) if str(ws.cell(row=r, column=C_NOM).value or "").startswith("Pruebas integrales"))
nueva = next(r for r in range(modelo + 1, tope) if str(ws.cell(row=r, column=C_NOM).value or "").startswith("Etapa 3"))
ws.insert_rows(nueva)
for c, valor in enumerate((UIUX, SIN, SIN, 0, "1–2", SIN, None), start=1):
cel = ws.cell(row=nueva, column=c, value=valor)
cel._style = copy(ws.cell(row=modelo, column=c)._style)
tope += 1
filas_tabla = [r for r in range(5, tope) if str(ws.cell(row=r, column=C_NOM).value or "").strip()]
assert not any(rg.min_row in filas_tabla for rg in ws.merged_cells.ranges), "Hay celdas combinadas en la tabla"
def nombre_de(row):
return str(ws.cell(row=row, column=C_NOM).value or "").strip()
def encabezado_de(nombre):
return next((p for p in HORAS_ENCABEZADO if nombre.startswith(p)), None)
# --- 1. Valores de las filas de detalle ----------------------------------------
aplicado = defaultdict(set)
for row in filas_tabla:
nombre = nombre_de(row)
if nombre.startswith(AJUSTE) and "(en aprobación, v1.3)" in nombre:
ws.cell(row=row, column=C_NOM, value=nombre.replace("(en aprobación, v1.3)", "(aprobado en la v1.3, 30-jul)"))
if encabezado_de(nombre):
continue
for prefijo, (ini, fin) in FECHAS_REALES.items():
if nombre.startswith(prefijo):
ws.cell(row=row, column=C_INI, value=ini)
ws.cell(row=row, column=C_FIN, value=fin)
aplicado["fechas"].add(prefijo)
for prefijo, delta in DELTAS_TRAB.items():
if nombre.startswith(prefijo):
actual = ws.cell(row=row, column=C_TRAB).value
actual = float(actual) if isinstance(actual, (int, float)) else 0.0
c = ws.cell(row=row, column=C_TRAB, value=round(actual + delta, 2))
c.number_format = "0.00"
aplicado["horas"].add(prefijo)
for prefijo, pct in AVANCE.items():
if nombre.startswith(prefijo):
p = ws.cell(row=row, column=C_PCT, value=pct)
p.number_format = "0%"
pinta(p, V_FILL if pct >= 1 else A_FILL, V_FONT if pct >= 1 else A_FONT, bold=True, size=10,
align="center")
aplicado["avance"].add(prefijo)
for prefijo, lineas in ESTATUS.items():
if nombre.startswith(prefijo):
cel = ws.cell(row=row, column=C_STAT, value=texto_estatus(lineas))
if lineas[0].startswith("✅"):
pinta(cel, V_FILL, V_FONT)
elif lineas[0].startswith("🟡"):
pinta(cel, A_FILL, A_FONT)
else: # ⚪ conserva su formato; solo envuelve el texto
cel.alignment = Alignment(horizontal="left", vertical="center", wrap_text=True)
ws.row_dimensions[row].height = alto_fila((nombre, 56), (cel.value, ANCHO_ESTATUS))
aplicado["estatus"].add(prefijo)
break
for col in (C_INI, C_FIN):
cel = ws.cell(row=row, column=col)
cel.alignment = Alignment(horizontal="center", vertical="center")
cel.font = Font(size=9, color=GRIS) if cel.value == SIN else Font(size=9)
sin_estatus = [nombre_de(r) for r in filas_tabla if not encabezado_de(nombre_de(r))
and not any(nombre_de(r).startswith(p) for p in ESTATUS)]
assert not sin_estatus, f"Filas sin estatus: {sin_estatus}"
for clave, esperado in (("horas", DELTAS_TRAB), ("fechas", FECHAS_REALES), ("avance", AVANCE), ("estatus", ESTATUS)):
faltan = set(esperado) - aplicado[clave]
assert not faltan, f"Filas no encontradas en el Excel ({clave}): {faltan}"
# La fila "Demostración semanal (viernes) + reporte de horas" no tiene demostraciones: las sesiones con Pedro
# están en las filas técnicas. Se renombra para que diga lo que contiene (Johann, 2-oct).
GESTION = "Seguimiento y gestión: reportes de horas, bitácora y comunicación con Balam"
renombradas = 0
for row in filas_tabla:
if nombre_de(row).startswith("Demostración semanal"):
ws.cell(row=row, column=C_NOM, value=GESTION)
renombradas += 1
assert renombradas == 1
# --- 2. Grupos: etapa → (bloque del ajuste) + filas ----------------------------
grupos = [] # [(fila_encabezado, [filas]), ...]; el ajuste es su propio grupo dentro de la Etapa 2
for row in filas_tabla:
enc = encabezado_de(nombre_de(row))
if enc:
grupos.append((row, []))
else:
grupos[-1][1].append(row)
# Las filas del ajuste aparecen justo después de su encabezado; las que siguen son de la Etapa 2.
for i, (enc_row, hijos) in enumerate(grupos):
if nombre_de(enc_row).startswith(AJUSTE):
propios = [r for r in hijos if nombre_de(r).startswith(AJUSTE_HIJOS)]
resto = [r for r in hijos if r not in propios]
assert len(propios) == len(AJUSTE_HIJOS), "No se encontraron las filas del ajuste"
grupos[i] = (enc_row, propios)
grupos.insert(i + 1, ("etapa2-resto", resto))
break
def clave_cronologica(row):
ini, fin = fecha(ws.cell(row=row, column=C_INI).value), fecha(ws.cell(row=row, column=C_FIN).value)
return (ini is None, ini or datetime.max, fin or datetime.max, row)
def promedio(filas):
pesos = [(estimadas(ws.cell(row=r, column=C_EST).value), ws.cell(row=r, column=C_PCT).value or 0) for r in filas]
total = sum(p for p, _ in pesos)
return round(sum(p * float(v) for p, v in pesos) / total, 2) if total else 0.0
def rango_fechas(filas):
inis = [fecha(ws.cell(row=r, column=C_INI).value) for r in filas]
fins = [fecha(ws.cell(row=r, column=C_FIN).value) for r in filas]
inis, fins = [d for d in inis if d], [d for d in fins if d]
return (min(inis).strftime("%d/%m/%Y") if inis else SIN, max(fins).strftime("%d/%m/%Y") if fins else SIN)
# --- 3. Encabezados: horas del CSV, avance ponderado y fechas de sus filas ------
hojas = {} # encabezado → filas de detalle que lo componen (para avance y fechas)
etapa_actual = None
for enc_row, hijos in grupos:
if enc_row == "etapa2-resto":
hojas[etapa_actual]["avance"] += hijos
hojas[etapa_actual]["fechas"] += hijos
continue
enc = encabezado_de(nombre_de(enc_row))
hojas[enc] = {"row": enc_row, "avance": list(hijos), "fechas": list(hijos)}
if enc in ETAPAS:
etapa_actual = enc
elif enc == AJUSTE:
hojas[etapa_actual]["fechas"] += hijos # el ajuste cuenta para las fechas de la Etapa 2, no para su avance
todas = [r for g in hojas.values() for r in g["avance"]]
hojas["Integracion de plataformas Balam"]["avance"] = todas
hojas["Integracion de plataformas Balam"]["fechas"] = todas
for enc, g in hojas.items():
row, pct = g["row"], promedio(g["avance"])
ini, fin = rango_fechas(g["fechas"])
ws.cell(row=row, column=C_INI, value=ini)
ws.cell(row=row, column=C_FIN, value=fin)
c = ws.cell(row=row, column=C_TRAB, value=round(HORAS_ENCABEZADO[enc], 2))
c.number_format = "0.00"
p = ws.cell(row=row, column=C_PCT, value=pct)
p.number_format = "0%"
pinta(p, V_FILL if pct >= 1 else A_FILL, V_FONT if pct >= 1 else A_FONT, bold=True, size=10, align="center")
g["pct"] = pct
# --- 4. Orden cronológico dentro de cada grupo ----------------------------------
orden = []
for enc_row, hijos in grupos:
if enc_row != "etapa2-resto":
orden.append(enc_row)
orden += sorted(hijos, key=clave_cronologica)
assert sorted(orden) == filas_tabla
foto = {r: ([(ws.cell(row=r, column=c).value, copy(ws.cell(row=r, column=c)._style)) for c in range(1, 8)],
ws.row_dimensions[r].height) for r in filas_tabla}
for destino, origen in zip(filas_tabla, orden):
celdas, alto = foto[origen]
for c, (valor, estilo) in enumerate(celdas, start=1):
cel = ws.cell(row=destino, column=c)
cel.value = valor
cel._style = copy(estilo)
ws.row_dimensions[destino].height = alto
# --- 5. Cuadre: las filas de detalle suman el total del CSV ----------------------
detalle = 0.0
for row in filas_tabla:
v = ws.cell(row=row, column=C_TRAB).value
if not encabezado_de(nombre_de(row)) and isinstance(v, (int, float)):
detalle += float(v)
assert abs(detalle - TOTAL) < 0.01, f"Las filas suman {detalle:.2f} h y el CSV {TOTAL:.2f} h"
PCT = {enc: g["pct"] for enc, g in hojas.items()}
# Sin notas debajo de la tabla (Johann, 2-oct): el estatus de cada fila lo dice todo.
for r in range(tope, ws.max_row + 1):
ws.row_dimensions[r].height = None
ws.column_dimensions["G"].width = ANCHO_ESTATUS
r = filas_tabla[-1]
ws.print_area = f"A1:G{r}"
# --- Hoja 2: desglose de horas, registro por registro (Johann, 2-oct: "quiero poder justificar todo") ----
TARIFA = 600
AMARILLO, CREMA = "F7BD0C", "FDF3D8"
ACTIVIDADES = {
"E0-01": "Kickoff y preparación", "E0-02": "Discovery de facturación y cobranza",
"E0-03": "Accesos y salvaguardas", "E0-04": "Validación técnica del API de BIND",
"E0-05": "Prototipo navegable", "E0-06": "Entregable de cierre de la Etapa 0",
"E0-07": "Validación del prototipo con Balam",
"E1-01": "Backend base, autenticación, roles y sincronización", "E1-02": "Repositorio",
"E1-03": "Arquitectura multi-tenant y RLS", "E1-04": "Cliente del API de BIND",
"E1-05": "Seguimiento y gestión", "E1-06": "Entorno local y andamiaje",
"E1-07": "Sesión del API de Jira", "E1-08": "Diseño y planeación",
"E1-09": "Publicación provisional (paputec.mx)",
"E2-01": "Pantallas de consulta", "E2-02": "Creación de cotizaciones",
"E2-03": "Prefactura y timbrado", "E2-04": "Pruebas integrales y manual",
"AJ-01": "Integración Jira → BIND",
"E3-01": "Endurecimiento de seguridad", "E3-02": "Documentación técnica",
}
ESTADOS = {"confirmada": "Documentada", "validada": "Validada", "reconstruida": "Reconstruida"}
ETAPA_NOMBRE = {"0": "Etapa 0", "1": "Etapa 1", "2": "Etapa 2", "3": "Etapa 3"}
with open(CSV_HORAS, encoding="utf-8", newline="") as fh:
registros = [x for x in csv.DictReader(fh) if (x.get("fecha") or "").strip()]
sin_nombre = {x["actividad_id"] for x in registros} - set(ACTIVIDADES)
assert not sin_nombre, f"Actividades sin nombre en el desglose: {sin_nombre}"
def etapa_de(x):
return "Ajuste Jira → BIND" if x["actividad_id"] == "AJ-01" else ETAPA_NOMBRE[x["etapa"]]
if "Desglose de horas" in wb.sheetnames:
del wb["Desglose de horas"]
wd = wb.create_sheet("Desglose de horas")
wd.sheet_view.showGridLines = False
for col, ancho in zip("ABCDEFGH", (11, 18, 30, 72, 8, 12, 14, 46)):
wd.column_dimensions[col].width = ancho
wd["A1"] = "Proyecto Balam · Desglose de horas trabajadas"
wd["A1"].font = Font(size=14, bold=True, color=CAFE)
wd["A2"] = (f"Cada registro con su fecha, actividad y evidencia · tarifa ${TARIFA}/h + IVA · corte al 2-oct-2026 · "
"Documentada = duración comprobada (transcript o sesión) · Validada = confirmada por Johann · "
"Reconstruida = armada con evidencia fechada, pendiente de validar")
wd["A2"].font = Font(size=9, italic=True, color=GRIS)
wd["A2"].alignment = Alignment(wrap_text=True, vertical="top")
wd.merge_cells("A2:H2")
wd.row_dimensions[2].height = 28
borde = Border(bottom=Side(style="thin", color="E4E2DC"))
def encabezado(fila, textos):
for c, t in enumerate(textos, start=1):
cel = wd.cell(row=fila, column=c, value=t)
cel.font = Font(size=10, bold=True, color=CAFE)
cel.fill = PatternFill("solid", fgColor=AMARILLO)
cel.alignment = Alignment(horizontal="center", vertical="center", wrap_text=True)
wd.row_dimensions[fila].height = 20
# Resumen por etapa (cuadra con la hoja principal)
encabezado(4, ["Etapa", "", "Registros", "", "Horas", "Monto (MXN)", "", ""])
orden_etapas = ["Etapa 0", "Etapa 1", "Etapa 2", "Ajuste Jira → BIND", "Etapa 3"]
esperado = {"Etapa 0": E0, "Etapa 1": E1, "Etapa 2": E2, "Ajuste Jira → BIND": AJ, "Etapa 3": E3}
fila = 5
for et in orden_etapas:
regs = [x for x in registros if etapa_de(x) == et]
horas = sum(float(x["horas"]) for x in regs)
assert abs(horas - esperado[et]) < 0.01, f"{et}: desglose {horas:.2f} h vs hoja principal {esperado[et]:.2f} h"
for c, v in ((1, et), (3, len(regs)), (5, round(horas, 2)), (6, round(horas * TARIFA, 2))):
cel = wd.cell(row=fila, column=c, value=v)
cel.font = Font(size=10, color=CAFE)
cel.border = borde
wd.cell(row=fila, column=5).number_format = "0.00"
wd.cell(row=fila, column=6).number_format = '"$"#,##0.00'
fila += 1
for c, v in ((1, "Total"), (3, len(registros)), (5, round(TOTAL, 2)), (6, round(TOTAL * TARIFA, 2))):
cel = wd.cell(row=fila, column=c, value=v)
cel.font = Font(size=10, bold=True, color=CAFE)
cel.fill = PatternFill("solid", fgColor=CREMA)
wd.cell(row=fila, column=5).number_format = "0.00"
wd.cell(row=fila, column=6).number_format = '"$"#,##0.00'
# Detalle, en orden cronológico
cab = fila + 3
encabezado(cab, ["Fecha", "Etapa", "Actividad", "Qué se hizo", "Horas", "Monto (MXN)", "Estado", "Evidencia"])
fila = cab + 1
for x in registros:
h = float(x["horas"])
valores = (datetime.strptime(x["fecha"], "%Y-%m-%d"), etapa_de(x), ACTIVIDADES[x["actividad_id"]],
x["actividad"], h, round(h * TARIFA, 2), ESTADOS.get(x["estado_hora"], x["estado_hora"]),
x["fuente_evidencia"])
for c, v in enumerate(valores, start=1):
cel = wd.cell(row=fila, column=c, value=v)
cel.font = Font(size=9, color=CAFE)
cel.alignment = Alignment(vertical="top", wrap_text=True,
horizontal="center" if c in (1, 7) else ("right" if c in (5, 6) else "left"))
cel.border = borde
wd.cell(row=fila, column=1).number_format = "dd/mm/yyyy"
wd.cell(row=fila, column=5).number_format = "0.00"
wd.cell(row=fila, column=6).number_format = '"$"#,##0.00'
if x["estado_hora"] == "reconstruida":
wd.cell(row=fila, column=7).font = Font(size=9, color=A_FONT)
renglones = max(-(-len(x["actividad"]) // 80), -(-len(x["fuente_evidencia"]) // 50), -(-len(ACTIVIDADES[x["actividad_id"]]) // 32), 1)
wd.row_dimensions[fila].height = 12 * renglones + 4
fila += 1
assert abs(sum(float(x["horas"]) for x in registros) - TOTAL) < 0.01
wd.freeze_panes = f"A{cab + 1}"
wd.auto_filter.ref = f"A{cab}:H{fila - 1}"
wd.page_setup.orientation = "landscape"
wd.page_setup.fitToWidth = 1
wd.page_setup.fitToHeight = 0
wd.sheet_properties.pageSetUpPr.fitToPage = True
wd.print_title_rows = f"{cab}:{cab}"
wb.active = 0
wb.save(DST)
print("OK ->", DST)
print(f" E0 {E0:.2f} · E1 {E1:.2f} · E2 {E2:.2f} · Jira {AJ:.2f} · E3 {E3:.2f} · TOTAL {TOTAL:.2f} h "
f"({nuevas:.2f} nuevas)")
print(" avance:", " · ".join(f"{k} {v:.0%}" for k, v in PCT.items()))
print(" reparto:", ", ".join(f"{k} {v:+.2f}" for k, v in DELTAS_TRAB.items()))