Compare commits

..

48 Commits

Author SHA1 Message Date
JohannVelazquez 1c4e5349cc 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 2026-10-15 22:24:18 -06:00
Johann 4e90543486 Registro #90, adenda: push y publicación de 73d1c2a en paputec (respaldo, imagen pre-lote-20261002, sin migración) 2026-10-02 08:41:18 -06:00
Johann 4f3eb99dd0 Registro #90: prefacturas en lote (Cotizaciones y bandeja → una prefactura por cotización, en serie, folios consecutivos aunque el API de BIND vaya lento) y timbrado también desde el detalle de la factura; commit local 73d1c2a del repo del cliente, sin publicar; comprobación de que el campo de servicios sí llega de Jira (10/17 categorías resuelven) 2026-10-02 08:36:23 -06:00
Johann 987454767f Registro #89: el timbrado entra a la plataforma (prefactura → CFDI por el puente, EmitCFDI sobre el mismo documento) con dry-run, palabra TIMBRAR, verificación por lectura y bloqueo de reenvío; la cadena completa queda revisada (estado incierto de los POST de escritura cerrado, consecutivo sin la serie B, uso CFDI solo del mismo cliente), probada (212 + 10 pruebas, recorrido completo en navegador) y publicada (f5b3743) para la prueba del 1-oct con Balam; guion de la prueba y capturas del recorrido 2026-09-30 22:45:22 -06:00
Johann 604cb8da8e Registro #88, adenda: credenciales del usuario CTO en producción (autorización explícita de Johann), push de los dos repos y publicación de 597c8a2 en paputec (respaldo, imagen pre-prefactura-20260930, migración QuoteSubtotal aplicada, bundles nuevos); primer ciclo del sync con 50 subtotales y totales USD en su moneda; folios de cotización repetidos en BIND 2026-09-30 21:49:12 -06:00
Johann 63af3d634d Registro #88: el puente de prefactura queda encendido en producción (sin credenciales CTO: bloqueo del control de permisos), «Servicio de Enlace» → 106 y 11 cotizaciones con estado atrasado corregidas; lo que Pedro vio a las 7:26 (totales en cero, botones sin destino, "servicios", Arturo) tenía detrás cuatro bugs reales, corregidos y probados en 597c8a2 del repo del cliente (197 + 10 pruebas, sin push ni publicación); capturas del recorrido local
Pendientes nuevos: credenciales del usuario CTO en /opt/balam/.env (decisión de Johann), push + publicación de 597c8a2, primera prefactura real desde producción con Johann, código de BIND de las 7 categorías restantes, confirmar 2467/1084/1085 con Balam, una sola sesión operando el servidor.
2026-09-30 21:29:22 -06:00
Johann 87993489cd Registro #87, adenda: push y publicación del modal en vivo; el servidor permite el modo automático por decisión de Johann, con el tenant aún en confirmación 2026-09-30 06:55:01 -06:00
Johann bdafb06a03 Registro #87: la API key de cto entra a producción (el sync vuelve), la vista previa se valida contra el BIND real con FAC-171 y se construye en local el modal en vivo con "Aceptar", la campana y el modo automático configurable; capturas y video del recorrido 2026-09-30 01:42:32 -06:00
Johann 085e9037e8 Registro #86, adenda: push y publicación de la vista previa automática; la causa del 500 de BIND es la key de arturo (la de cto responde 200) y falta cambiarla en paputec — Claude Code no pudo escribir ese secreto 2026-09-30 00:55:16 -06:00
Johann daa6a62e86 Registro #86: vista previa automática de la cotización por cada ticket detectado ("Lista para revisar" en la bandeja), construida y probada en local (147 pruebas) y sin publicar; el #85 anota que el sync de producción falla con BIND 500 2026-09-30 00:47:36 -06:00
Johann 7e50c71791 Registro #85: el webhook de Jira FAC queda en producción (paputec) — publicación de 5145801, secreto nuevo, PUT del webhook id 1 y prueba e2e con FAC-170 detectada en 1.05 s (HMAC, verificada) y borrada; producción sigue en Bind__Mode=Write por decisión de Johann 2026-09-30 00:31:55 -06:00
Johann e88b5e1eb8 Fusión de las dos sesiones del 29-sep: el registro del webhook Jira FAC pasa de #71 a #84 (los #71–#83 son las sesiones del 21-ago al 29-sep) y PENDIENTES conserva los dos bloques 2026-09-30 00:19:37 -06:00
JohannVelazquez e5912bfc7e Registro #71, adenda: piloto del webhook Jira FAC validado (webhook id 1, FAC-169 detectado en 14.4 s con HMAC y borrado) y script de operación; pendiente mover el webhook a paputec 2026-09-30 00:01:37 -06:00
JohannVelazquez 6ff8603b48 Registro #71: Fase 1 del plan del webhook — el token de Pedro es admin de Jira (ADMINISTER), 0 webhooks de sitio y API de Automation accesible; solo GET 2026-09-29 18:14:01 -06:00
JohannVelazquez 6601e20ca3 Registro #71: hallazgos del 29-sep de la sesión paralela (primera prefactura real 2467, API público de BIND en 500, cotización 1079 surtida por error) y push de la plataforma (4d6b3d8, candado 409 conservado) 2026-09-29 18:06:12 -06:00
JohannVelazquez d76496f9a3 Registro #71 y plan del webhook Jira FAC → vista previa de cotización BIND: app en modo escritura, token de Jira, ADR-001 intacto (confirmación humana) y 5 fases con una sola escritura auditada en Jira 2026-09-29 18:02:03 -06:00
JohannVelazquez 9eef941053 cambios 2026-09-29 18:01:35 -06:00
Johann 3de2739e75 Registros #71–#83: se cierran los huecos del 21-ago al 29-sep — login web a BIND entregado, endpoint interno de la UI (Generar Venta → Prefactura, InvoiceDetails) y modelo de dos endpoints confirmado con el SDK oficial
Reconstruido desde el buzón (informes de Read AI, correos de Noé) y los commits
del repo del cliente: homologación de estados (21-ago), false = remisión (24-ago),
Generar Venta y requisitos de Azure (24/25-ago), la sesión del incidente (26-ago),
tenant de Azure + ítem por código (3-sep), publicación en paputec.mx (3-sep),
cotización en lote (7-sep), primera prefactura observada por DevTools (9-sep),
captura con Pedro (10-sep), corte de horas (15/16-sep) y la sesión de hoy.

- REGISTRO.md: encabezado Periodo, entradas #71–#83, bloque "Estado actual (al 29-sep)".
- PENDIENTES.md: sección 29-sep (credenciales fuera del repo, especificar InvoiceDetails,
  BindUiClient bajo ADR-001, CreatedByName, soporte BIND, error al crear cotización);
  cerrados los huecos de registro, el correo de causa del 21-ago, el experimento de
  folio compartido, el servicio por combo; movimientos de Azure y de la 2386.
- fuentes/: extractos de Read AI (9 informes), correo "Generar Venta" de Noé y el
  hilo de acceso a Azure.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-29 08:06:25 -06:00
Johann 68aedc7863 El Excel de avance muestra fechas REALES por tarea (primer y último día trabajado), no el calendario plan; '—' = sin iniciar
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-16 10:28:47 -06:00
Johann 69e06dfd8c Corte del 16-sep (94.58 h): +0.50 h por el Excel publicado en Google Sheets; el Sheet compartido pasa a llamarse Avance-Balam y conserva su enlace
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-16 10:24:56 -06:00
Johann d9047de6fa Corte de horas al 15-sep (94.08 h): 39.25 h del 10-ago al 15-sep reconstruidas con evidencia y Excel de avance; seis sesiones sin bitácora listadas como huecos
- Seguimiento-horas.csv: 30 filas nuevas (sesiones 'Seguimiento PAF' del 21, 24
  y 26-ago y 3, 9 y 10-sep acotadas por los informes de Read AI; bloques de
  desarrollo por commits y mtimes; comunicación con Pedro y Noé). Marcadas
  `reconstruida` salvo la sesión del 12-ago (transcript). Actividades nuevas
  E1-09, E2-02, E2-03, E3-01, E3-02. Etapa 1 32.25 h, Etapa 2 23.75 h, ajuste
  Jira 14.75 h (rebasa las 7–10 aprobadas), Etapa 3 1.50 h.
- Avance-Balam-2026-09-15.xlsx generado desde generar_avance_2026_09_15.py
  (parte del corte 06b; el reparto por fila aborta si no cuadra con el CSV):
  publicación provisional en paputec.mx, cotización individual y en lote,
  prefactura por API bloqueada por BIND.
- Seguimiento-horas.md: sección del corte y criterios de la reconstrucción.
- PENDIENTES.md: huecos de registro del corte (seis sesiones sin REGISTRO,
  publicación del 3-sep y lote del 7-sep sin registrar, validación de horas,
  correos de Noé del 24-ago).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-15 08:48:23 -06:00
Johann db16f62b28 Adenda 2 al #70: API público agotado por eliminación total (Number ignorado, 12 sondas ignoradas, 98 ops) — la vía que queda es el endpoint interno de la UI 2026-08-27 00:10:32 -06:00
Johann 2ae6880565 Adenda al #70: el folio 2384 ES prefactura (confirmado en UI) — prefacturas visibles por API, status 2 = facturada, y el discriminador es la secuencia de folios 2026-08-26 21:28:21 -06:00
Johann a48fba0ba6 Registro #70: el incidente del timbrado y el cierre de la investigación — el API de BIND ni crea ni ve prefacturas; candado triple y experimento decisivo pendiente 2026-08-26 08:01:03 -06:00
Johann d31747df93 Registro #69: primera cotización real en BIND desde FAC-106 (folio 1026) tras 5 intentos; Type=2 y VAT como tasa, indocumentados
Sesión del 21-ago: tres intentos en vivo fallaron (uno con error explícito
que se corrigió al momento, dos con 500 genérico); se aplicó el protocolo,
se verificó por lectura que nada quedó en BIND, y con autorización de Balam
se siguió sobre el ticket de prueba. La causa fue Items[].Type (2 =
servicio, el catálogo donde BIND busca el ID) y, ya emitida, la escala de
Items[].VAT (tasa, no monto) que dejó el folio 1026 con IVA absurdo y debe
cancelarse. La segunda emisión corregida queda pendiente de confirmar.

Incluye el borrador del correo a Balam con la causa y el Excel de campos y
catálogos de BIND que fue el insumo del formulario nuevo de Jira.

Co-Authored-By: Claude Fable 5 (1M context) <noreply@anthropic.com>
2026-08-21 10:16:53 -06:00
Johann 072a3cdab2 Guion 21-ago: protocolo de reacción si la emisión falla (2 reintentos, verificación anti-duplicado, guion de cierre y pivote a las definiciones)
Co-Authored-By: Claude Fable 5 (1M context) <noreply@anthropic.com>
2026-08-20 23:52:10 -06:00
Johann cabe0fe1b5 Guion de la sesión del 21-ago: aviso previo a Pedro, coreografía ReadOnly→Write→ReadOnly con cancelación en vivo, plan B del 500 y las 8 preguntas en orden de impacto
Co-Authored-By: Claude Fable 5 (1M context) <noreply@anthropic.com>
2026-08-20 23:50:50 -06:00
Johann fa8691d9e6 Pendientes de la sesión del 21-ago: wizard cableado y verificado e2e contra FAC-106; Bind:Mode de vuelta en ReadOnly
Co-Authored-By: Claude Fable 5 (1M context) <noreply@anthropic.com>
2026-08-20 23:47:35 -06:00
Johann 82f9fa5d25 Sondeo de FAC-106 y diagnóstico del 500: el payload tenía tres defectos de escala y semántica
Preparación para la sesión del 21-ago. Todo solo lectura + dry-run.

FAC-106 (creado por Pedro el 20-ago, asignado a Araceli) trae el
formulario nuevo con request type "Cotización Bind". Servicio/Producto,
Cantidad, IVA y Cliente ya son campos estructurados: el hueco de
conceptos/partidas que bloqueaba B7 queda resuelto en el origen.

Diagnóstico del 500 del 12-ago, leyendo 4 cotizaciones reales de BIND:
- VATRate va como fracción (0.16), no como 16
- el VAT de partida es un MONTO, no una tasa (Price*Qty*0.16)
- cada cotización usa un PriceListID distinto y ninguno coincide con el
  único que devuelve GET /api/PriceLists; conviene omitirlo

Dry-run corregido ejecutado (operación #18); el body queda en el informe.

Tres preguntas nuevas para la sesión: Empresa origen tiene 3 valores y
hay un solo token; el combo IVA no ofrece 0% aunque hay clientes de
exportación; y "Servicio de Enlace" existe 5 veces en BIND con unidad,
unidad SAT y ChargeVAT distintos, sin mapeo definido.

Bind:Mode está en Write fuera de sesión — hay que regresarlo a ReadOnly.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-20 23:41:14 -06:00
Johann a03f81f461 Registro #65 completo: la sesión del 12-ago aparece — Pedro releva a Erika como PM y el primer POST a BIND falla con 500
Transcript de la sesión del 12-ago localizado y archivado en fuentes/.
Reemplaza el stub que quedó abierto en el commit anterior.

Lo que resuelve:
- Noé anuncia que Erika deja de ser la PM y el seguimiento pasa a Pedro.
  El anuncio fue solo verbal; nunca se comunicó por escrito.
- Se decide que la cotización la genera la PLATAFORMA, no el comercial
  (venta nueva). Cierra la contradicción abierta desde el 6-jul.
- El molde que Noé encargó a Araceli nunca existió: Pedro construyó un
  formulario nuevo en vivo, apoyado en el Excel enviado esa mañana.
- El catálogo de BIND está sucio (servicios duplicados, nombres de personas,
  periodos en el nombre). Se acuerda depurarlo y homologar los combos de
  Jira con los de BIND: de ahí salen las 17 categorías de Pedro.
- La primera escritura real a BIND devolvió ERROR 500: faltan campos que el
  portal manda en ceros y la lista de precios. No se creó cotización.
  Corrige la lectura del #64, que quedaba en 73/73 de dry-run.

Reglas abiertas que dejó la sesión: edición vs historial de cotizaciones,
récord de cancelaciones sin ejecutarlas, quitar SLA a los tickets de
cotización, grabaciones de BIND que Pedro no envió, y la herramienta de
seguimiento del proyecto (Jira de paga vs SharePoint vs Drive).

Azure no se tocó pese a estar en la agenda; queda con Pedro.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-20 23:30:05 -06:00
Johann 5d846ecfe5 Cruce del buzón contra la bitácora: 9 comunicaciones incorporadas, 2 correcciones y la sesión del 21-ago
Lectura del correo de Outlook vía MCP ms365 (94 mensajes del dominio Balam
desde enero) cruzada contra REGISTRO.md.

Entradas nuevas:
- #0  prehistoria comercial de abril (hilo "Propuesta de servicios por hora"),
      inventario; cuerpos aún sin leer
- #31a respuesta formal de las salvaguardas del token — SÍ se envió el 6-jul 4:46 pm
- #40a reacción de Balam al prototipo: acuse de Erika y Noé fuera de oficina
- #46a procedimientos de carga de facturas (5 portales; ATC Consolidados y
      Victec nunca aparecieron en el Discovery)
- #57a diagrama del flujo de facturación de Jira
- #59a seguimiento a la API de Jira por correo, no solo por WhatsApp
- #65  sesión del 12-ago SIN MINUTA (hueco abierto a propósito)
- #66  Excel de campos y catálogos de la cotización BIND enviado a Pedro
- #67  WhatsApp 18-20 ago: Pedro reduce el catálogo a 17 categorías, Balam
      modifica el formulario de Jira y se agenda el viernes 21-ago con FAC-106
- #68  correo "Seguimiento - PAF" con la convocatoria

Correcciones:
- #3a  el correo "en verde" de Araceli apareció íntegro: IBC Bank y tax estándar
      dejan de ser inferencia de la llamada del 19-may
- #33  el pendiente de responder a Noé llevaba mes y medio abierto por error
- #62  hora del reenvío del 7-ago: 11:34 am, no 5:34 pm (UTC leída como local)

Evidencia recuperada a fuentes/, incluidos los dos adjuntos que la bitácora
pedía conservar.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-20 23:02:26 -06:00
Johann 83ee332a90 Guion del #64 afinado a la coreografía de pantalla compartida (Actualizar sin caché, Cotizar por fila, vista previa automática)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-12 00:35:01 -06:00
Johann 2fb230ef11 Registro #64: capa ticket→cotización BIND construida la noche previa a la sesión (contrato Quotes_NewQuote del portal público, dry-run + candados, 73/73); script de la prueba listo para enviar a Balam
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-11 21:30:20 -06:00
Johann a2f338fe8f Registro #63: Noé confirma la sesión del mié 12-ago 7:00 am, rutea el molde a Araceli y suma Azure a la agenda; sondeo nocturno del 11-ago: el molde aún no existe y FAC-98 lleva 17 días en el estatus disparador
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-11 20:53:20 -06:00
Johann f28fcc58c9 Merge de la otra máquina: definiciones Jira→BIND del correo 7-ago y verificación por API del 10-ago
La entrada remota '#59' colisionaba con las #59–#61 locales; se renumeró a #62
(con nota de merge) y sus referencias en PENDIENTES se ajustaron. El briefing
de Investigacion-API-Jira toma los párrafos del 10-ago y conserva el sondeo
del 6-ago; PENDIENTES queda con la sección nueva del flujo Jira→BIND.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-11 20:46:21 -06:00
Johann 6d4b781486 2.º corte del 6-ago (54.83 h): wizard de emisión asistida construido en simulación (1.00 h de sesión vespertina)
El wizard de 5 pasos de la VISTA 3 quedó navegable en balam-plataforma con
datos reales de solo lectura (cotizaciones de /api/quotes y ticket FAC vivo):
selección de cotización, datos de factura con regla PPD/PUE y advertencia SAT,
IVA por regla con totales reactivos, prefactura simulada con marca de agua y
aprobación humana visual; el timbrado queda deshabilitado hasta B7 (commits
2164f6c, 90f7633 y 0600f2f en el repo del cliente). Excel de avance del 2.º
corte generado desde script (el corte matutino no se toca); la fila de
conversión cotización→factura pasa a 40% como interfaz en simulación.
2026-08-06 14:17:06 -06:00
Johann 0d5810fb33 Regla 8 ajustada: el objetivo de cierre es el piso no comprimible y un poco más; los cortes ya reportados a Erika no se recalculan 2026-08-06 01:36:28 -06:00
Johann 311b1f64e4 Reglas de captura 7 y 8: el esfuerzo de sesión incluye pensamiento, documentación y comunicación (correos/mensajes); las horas no comprimibles son el piso de toda proyección 2026-08-06 01:33:19 -06:00
Johann 547c300015 Corte de horas al 6-ago (53.83 h): sesión nocturna de la lectura Jira (1.25 h reales — sondeo customfields, bandeja+detalle con FAC en vivo, adjuntos, shell del prototipo); Excel de avance regenerado desde script. 'Cliente de la API de Jira' queda al 100%; ajuste Jira 5.50 de 7-10 h aprobadas 2026-08-06 01:27:43 -06:00
Johann 4ce5640e76 Sondeo Jira del 6-ago: customfield IDs de FAC capturados y documentados; cierra el pendiente del script de lectura. En balam-plataforma quedan construidas la bandeja de solicitudes y el detalle con datos reales (solo lectura) + shell del prototipo; token movido a propuesta/ y user-secrets 2026-08-06 01:11:03 -06:00
Johann 7f86497314 Corte de horas al 1-ago (52.58 h): captura validada del 28-jul→1-ago, Excel de avance regenerado desde script (la versión enviada a Erika queda respaldada localmente en respaldos/)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-01 20:41:07 -06:00
Johann 8d320edfa7 Registra la recuperación técnica del 1-ago (#61), refresca pendientes, borrador de status para Erika y script de pruebas Jira de lectura
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-01 20:21:02 -06:00
Johann 2b862ee769 Registra WhatsApp 28-29 jul (#59) y la aprobación de Noé a la v1.3 (#60); actualiza pendientes
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-01 13:58:38 -06:00
Johann 07e461d07f Ordena el ajuste Jira antes de las actividades propias de la Etapa 2
El bloque quedó anidado bajo la Etapa 2 en el commit anterior, pero se
pintaba DESPUÉS de las 10 actividades propias de la etapa (29 jul-12 ago)
aunque sus 3 días (27-29 jul) son cronológicamente anteriores — al leer
de arriba a abajo, la fecha retrocedía 2 semanas de golpe. Se movió el
bloque Jira a inmediatamente debajo del encabezado de Etapa 2 y antes de
sus propias actividades: la lectura ahora es monotónica (27, 28, 29, 29,
30, 31 jul... 12 ago) sin perder el anidado visual del turno anterior.

No se tocó el orden interno de Etapa 0/Etapa 1: su ligero traslape de
fechas (el desarrollo de Etapa 1 arrancó mientras se cerraba la
validación de Etapa 0) es agrupación real por etapa contractual, no un
error de orden — desintercalarlas rompería la estructura sin ganar
claridad.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-01 13:02:07 -06:00
Johann f7f7088e01 Anida el ajuste Jira→BIND dentro de la Etapa 2 (antes leía como una 5ª etapa)
Propuesta: mueve el bloque completo (antecedente, entregables, diagrama,
candados fiscal, exclusiones, dependencia) de después de la Etapa 3 a
justo después de la Etapa 2, y le baja el peso visual — de un div .etapa
de peso igual a las 4 etapas reales, a una caja .ajuste-anidado con
barra de acento, fondo crema y kicker "AJUSTE DENTRO DE LA ETAPA 2"
(h4 bajo el h3 de Etapa 2 en el markdown). La fila de la tabla de
Inversión (§4) se reordena igual, entre Etapa 2 y Etapa 3.

Excel: mismo reordenamiento (Etapa 2 → ajuste Jira → Etapa 3) con relleno
ámbar distinto del crema de una etapa contractual, para que no compita
visualmente como una etapa más.

Cambio incidental: el glifo ↳ no existe en Caladea/Carlito y Chromium
caía a un fallback de sistema (LucidaGrande-Bold) en la fila de la tabla;
se sustituyó por » en los 3 archivos y las fuentes vuelven a embeber limpio.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-01 13:02:07 -06:00
Johann 46f86ef827 Ajusta el alcance Jira→BIND: 7-10 h (antes 9-12) e inicio lunes 27-jul
Recalcula todas las cifras dependientes en la propuesta v1.3: fila Ajuste
$4,200-$6,000, total primera etapa 119-146 h / $71,400-$87,600 (antes
121-148 h / $72,600-$88,800). El bloque de la §2 y su Excel homólogo pasan
de "se ejecuta junto con las Etapas 2-3" a "inicia el lunes 27 de julio
(token PAF ya entregado)", reflejando que la conexión a la API de Jira no
depende de la definición del flujo de facturación, solo del token que
Pedro ya entregó ese día. Las 3 actividades del plan se re-fechan a
27-29/jul y sus rangos siguen sumando exacto el total de la etapa.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-01 13:02:07 -06:00
Johann 64f7146d86 Propuesta v1.3 (Jira→BIND integrado) y plan-avance con Etapas 2-3 por actividad diaria
Reemplaza el enfoque de adenda separada: la propuesta completa sube a v1.3
con la integración Jira→BIND incorporada en §1-2 (bloque de ajuste con
entregables, candados fiscales y exclusiones), §4 (fila Ajuste 9-12 h /
$5,400-$7,200; total 121-148 h / $72,600-$88,800), §7 (supuestos del token
PAF y del flujo) y anexos A/B. PDF reconstruido con fuentes embebidas
(antes dependía de fuentes de Windows) y fixes visuales: flechas en títulos
serif, tabla de inversión sin partir, label A.4 sin huérfano.

El Excel para Erika (Avance-Balam-2026-07-28.xlsx) extiende el formato del
27-jul con Etapas 2-3 y el ajuste Jira desglosados en actividades diarias
cuyos rangos suman exactamente los contractuales; se rebalancearon los
rangos de Etapa 1 (sumaban 34-42 vs 32-39 contractual) recortando las tres
actividades con horas reales muy por debajo del estimado, con asserts que
lo verifican. Verificado con 5 agentes (paridad MD↔HTML, cifras vs
bitácora/CSV, formato y calendario, cobertura de entregables, PDF visual).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-01 13:02:07 -06:00
Johann 67bce9d691 Prepara la Adenda 01 (integración Jira→BIND, 9–12 h) y el Excel de horas por etapa
Los dos pendientes que Erika re-preguntó el 28-jul: la adenda formaliza la
generación automática de cotizaciones desde tickets FAC ($5,400–$7,200 + IVA,
consistente con las ~10 h comunicadas el 21-jul) como solicitud de cambio
conforme a la §10 de la propuesta v1.2; el Excel resume horas por etapa
(E0 cerrada en 21.83 h, E1 en 19.75 h, E2/E3 con rangos contractuales) con
importes para facturación. PDF con el mismo diseño del paquete v1.2.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-01 13:02:07 -06:00
125 changed files with 21064 additions and 68 deletions
+10
View File
@@ -0,0 +1,10 @@
{"permissions": {
"defaultMode": "auto",
"allow": [
"Bash(ssh johann@paputec.mx *)",
"Bash(az postgres flexible-server update *)",
"Bash(az postgres flexible-server firewall-rule create *)",
"PowerShell(az postgres flexible-server update *)",
"PowerShell(az postgres flexible-server firewall-rule create *)"
]
}}
@@ -264,8 +264,8 @@ def render(html_text, out_pdf, pw_format, width_pt, height_pt, timeout_ms, base_
if pw_format:
pdf_kwargs["format"] = pw_format
else: # custom size
pdf_kwargs["width"] = f"{width_pt}pt"
pdf_kwargs["height"] = f"{height_pt}pt"
pdf_kwargs["width"] = f"{round(width_pt / 72, 4)}in" # Playwright no acepta "pt"
pdf_kwargs["height"] = f"{round(height_pt / 72, 4)}in"
page.pdf(**pdf_kwargs)
browser.close()
finally:
Binary file not shown.
Binary file not shown.
Binary file not shown.
File diff suppressed because it is too large Load Diff
+84
View File
@@ -0,0 +1,84 @@
<!doctype html>
<html lang="es">
<head>
<meta charset="utf-8">
<title>Datos bancarios — Johann Joseph Velazquez Antonio</title>
<style>
@font-face{ font-family:'Caladea'; src:url("../propuesta/fonts/Caladea-Regular.ttf"); font-weight:400; font-style:normal; }
@font-face{ font-family:'Caladea'; src:url("../propuesta/fonts/Caladea-Bold.ttf"); font-weight:700; font-style:normal; }
@font-face{ font-family:'Carlito'; src:url("../propuesta/fonts/Carlito-Regular.ttf"); font-weight:400; font-style:normal; }
@font-face{ font-family:'Carlito'; src:url("../propuesta/fonts/Carlito-Bold.ttf"); font-weight:700; font-style:normal; }
:root{
--ink:#1C2B39; --body:#2A3744; --muted:#6E7A86;
--accent:#BC5B3E; --accent-d:#9A4530; --cream:#F8F5F0;
--serif:'Caladea',Georgia,serif; --sans:'Carlito',system-ui,sans-serif;
}
@page{ size:215.9mm 139.7mm; margin:0; }
*{ box-sizing:border-box; margin:0; padding:0; }
html,body{ background:#fff; }
body{ font-family:var(--sans); color:var(--body); -webkit-print-color-adjust:exact; print-color-adjust:exact; }
.sheet{ width:215.9mm; height:139.7mm; display:flex; flex-direction:column; overflow:hidden; }
.head{
background:
radial-gradient(110mm 60mm at 100% 0%, rgba(188,91,62,.22), rgba(188,91,62,0) 70%),
var(--ink);
color:#fff; padding:10mm 18mm 8mm; display:flex; justify-content:space-between; align-items:flex-end;
}
.head .rule{ width:30pt; height:2.6pt; background:var(--accent); margin-bottom:8pt; }
.head .role{ font-size:7.2pt; letter-spacing:.22em; text-transform:uppercase; color:#D9876C; font-weight:700; margin-bottom:4pt; }
.head .name{ font-family:var(--serif); font-size:19pt; line-height:1.1; }
.head .contact{ text-align:right; font-size:8.4pt; line-height:1.65; color:#B9C3CD; }
.head .contact b{ color:#fff; font-weight:400; }
.main{ flex:1; padding:0 18mm; display:flex; align-items:center; }
.bank{ width:100%; background:var(--cream); border:1px solid #ECE5DA; border-left:3pt solid var(--accent); padding:10pt 18pt 12pt; }
.bank .cap{ font-size:7.4pt; letter-spacing:.22em; text-transform:uppercase; color:var(--accent-d); font-weight:700; margin-bottom:6pt; }
.bank table{ width:100%; border-collapse:collapse; }
.bank td{ padding:4pt 0; border-bottom:1px solid #E9E2D6; vertical-align:middle; }
.bank tr:last-child td{ border-bottom:0; }
.bank td.k{ width:34%; font-size:7.6pt; letter-spacing:.16em; text-transform:uppercase; color:var(--muted); font-weight:700; }
.bank td.v{ color:var(--ink); font-size:11.5pt; font-variant-numeric:tabular-nums lining-nums; }
.bank td.v.num{ font-family:var(--serif); font-size:13pt; letter-spacing:.04em; }
.clabe{ font-family:var(--serif); font-size:17pt; font-weight:700; letter-spacing:.05em; color:var(--ink);
font-variant-numeric:tabular-nums lining-nums; line-height:1.15; }
</style>
</head>
<body>
<div class="sheet">
<header class="head">
<div>
<div class="rule"></div>
<div class="role">Consultor de Software</div>
<div class="name">Johann Joseph Velazquez Antonio</div>
</div>
<div class="contact">
<b>johann_antonio85@hotmail.com</b><br>
81 3161 0998
</div>
</header>
<main class="main">
<div class="bank">
<div class="cap">Datos bancarios</div>
<table>
<tr><td class="k">Beneficiario</td><td class="v">Johann Joseph Velazquez Antonio</td></tr>
<tr><td class="k">RFC</td><td class="v">VEAJ031228MD6</td></tr>
<tr><td class="k">Banco</td><td class="v">BBVA México</td></tr>
<tr><td class="k">Número de cuenta</td><td class="v num">1548166833</td></tr>
<tr>
<td class="k">CLABE interbancaria</td>
<td class="v">
<span class="clabe">012180015481668333</span>
</td>
</tr>
</table>
</div>
</main>
</div>
</body>
</html>
Binary file not shown.

After

Width:  |  Height:  |  Size: 417 KiB

Binary file not shown.
Binary file not shown.
+63
View File
@@ -0,0 +1,63 @@
<!doctype html>
<html lang="es">
<head>
<meta charset="utf-8">
<title>Datos bancarios — Johann Joseph Velazquez Antonio</title>
<style>
@font-face{ font-family:'Caladea'; src:url("../propuesta/fonts/Caladea-Regular.ttf"); font-weight:400; font-style:normal; }
@font-face{ font-family:'Caladea'; src:url("../propuesta/fonts/Caladea-Bold.ttf"); font-weight:700; font-style:normal; }
@font-face{ font-family:'Carlito'; src:url("../propuesta/fonts/Carlito-Regular.ttf"); font-weight:400; font-style:normal; }
@font-face{ font-family:'Carlito'; src:url("../propuesta/fonts/Carlito-Bold.ttf"); font-weight:700; font-style:normal; }
:root{
--ink:#1E2329; --muted:#7A8087; --hair:#DCD6CB; --gold:#B07D2B;
--serif:'Caladea',Georgia,serif; --sans:'Carlito',system-ui,sans-serif;
}
@page{ size:215.9mm 139.7mm; margin:0; }
*{ box-sizing:border-box; margin:0; padding:0; }
html,body{ background:#fff; }
body{ font-family:var(--serif); color:var(--ink); font-size:11.5pt; -webkit-print-color-adjust:exact; print-color-adjust:exact; }
.sheet{ width:215.9mm; height:139.7mm; padding:10mm 28mm 0; display:flex; flex-direction:column; overflow:hidden; }
.head{ text-align:center; }
.mono{ width:12mm; height:12mm; border:1.1pt solid var(--gold); border-radius:50%; margin:0 auto 2.5mm;
display:flex; align-items:center; justify-content:center; font-size:12.5pt; letter-spacing:.04em; }
.head .nm{ font-size:12.5pt; letter-spacing:.24em; text-transform:uppercase; }
.head .role{ font-family:var(--sans); font-size:7.8pt; letter-spacing:.2em; text-transform:uppercase; color:var(--gold); margin-top:2pt; }
.double{ margin:4mm 0 0; border-top:1px solid var(--hair); border-bottom:1px solid var(--hair); height:3pt; }
.data{ flex:1; display:flex; flex-direction:column; justify-content:center; padding:0 4mm; }
.row{ display:flex; align-items:baseline; padding:3.6pt 0; }
.row .k{ font-family:var(--sans); font-size:8.4pt; letter-spacing:.16em; text-transform:uppercase; color:var(--muted); white-space:nowrap; }
.row .dots{ flex:1; border-bottom:1.2pt dotted #C9C2B5; margin:0 6pt; transform:translateY(-3pt); }
.row .v{ white-space:nowrap; font-variant-numeric:tabular-nums lining-nums; }
.row.big .v{ font-size:15pt; font-weight:700; letter-spacing:.06em; }
.foot{ padding:5pt 0 7mm; border-top:1px solid var(--hair); text-align:center;
font-family:var(--sans); font-size:8pt; letter-spacing:.06em; color:var(--muted); }
.foot i{ display:inline-block; width:4pt; height:4pt; background:var(--gold); transform:rotate(45deg); margin:0 8pt 1pt; }
</style>
</head>
<body>
<div class="sheet">
<div class="head">
<div class="mono">JV</div>
<div class="nm">Johann Joseph Velazquez Antonio</div>
<div class="role">Consultor de Software</div>
</div>
<div class="double"></div>
<div class="data">
<div class="row"><span class="k">Beneficiario</span><span class="dots"></span><span class="v">Johann Joseph Velazquez Antonio</span></div>
<div class="row"><span class="k">RFC</span><span class="dots"></span><span class="v">VEAJ031228MD6</span></div>
<div class="row"><span class="k">Banco</span><span class="dots"></span><span class="v">BBVA México</span></div>
<div class="row"><span class="k">Número de cuenta</span><span class="dots"></span><span class="v">1548166833</span></div>
<div class="row big"><span class="k">CLABE interbancaria</span><span class="dots"></span><span class="v">012180015481668333</span></div>
</div>
<div class="foot">johann_antonio85@hotmail.com<i></i>81 3161 0998</div>
</div>
</body>
</html>
+84
View File
@@ -0,0 +1,84 @@
<!doctype html>
<html lang="es">
<head>
<meta charset="utf-8">
<title>Datos bancarios — Johann Joseph Velazquez Antonio</title>
<style>
@font-face{ font-family:'Caladea'; src:url("../propuesta/fonts/Caladea-Regular.ttf"); font-weight:400; font-style:normal; }
@font-face{ font-family:'Caladea'; src:url("../propuesta/fonts/Caladea-Bold.ttf"); font-weight:700; font-style:normal; }
@font-face{ font-family:'Carlito'; src:url("../propuesta/fonts/Carlito-Regular.ttf"); font-weight:400; font-style:normal; }
@font-face{ font-family:'Carlito'; src:url("../propuesta/fonts/Carlito-Bold.ttf"); font-weight:700; font-style:normal; }
:root{
--ink:#1C2B39; --muted:#6E7A86; --hair:#E4E8EC; --accent:#BC5B3E; --accent-d:#9A4530;
--serif:'Caladea',Georgia,serif; --sans:'Carlito',system-ui,sans-serif;
}
@page{ size:215.9mm 139.7mm; margin:0; }
*{ box-sizing:border-box; margin:0; padding:0; }
html,body{ background:#fff; }
body{ font-family:var(--sans); color:var(--ink); -webkit-print-color-adjust:exact; print-color-adjust:exact; }
.sheet{ width:215.9mm; height:139.7mm; display:flex; overflow:hidden; }
.side{ width:62mm; flex:none; color:#fff; padding:12mm 8mm 10mm 10mm; display:flex; flex-direction:column;
background:
radial-gradient(70mm 50mm at 0% 100%, rgba(188,91,62,.28), rgba(188,91,62,0) 70%),
var(--ink); }
.mono{ width:12mm; height:12mm; border-radius:50%; background:var(--accent); display:flex; align-items:center;
justify-content:center; font-family:var(--serif); font-size:12.5pt; font-weight:700; margin-bottom:6mm; }
.side .nm{ font-family:var(--serif); font-size:15pt; line-height:1.2; }
.side .role{ font-size:7.4pt; letter-spacing:.2em; text-transform:uppercase; color:#D9876C; font-weight:700; margin-top:4pt; }
.side .contact{ margin-top:auto; }
.side .blk{ margin-top:4mm; }
.side .lbl{ font-size:6.6pt; letter-spacing:.2em; text-transform:uppercase; color:#8D9AA7; font-weight:700; margin-bottom:1pt; }
.side .val{ font-size:9.2pt; color:#E8EDF2; }
.main{ flex:1; padding:12mm 14mm 10mm 12mm; display:flex; flex-direction:column; justify-content:center; }
.eyebrow{ font-size:7.4pt; letter-spacing:.22em; text-transform:uppercase; color:var(--accent-d); font-weight:700; margin-bottom:4mm; }
.hero{ background:var(--ink); color:#fff; border-radius:3mm; padding:9pt 14pt 11pt; position:relative; overflow:hidden; margin-bottom:3mm; }
.hero::after{ content:""; position:absolute; right:-14mm; top:-14mm; width:42mm; height:42mm; border-radius:50%;
background:radial-gradient(circle, rgba(188,91,62,.45), rgba(188,91,62,0) 70%); }
.hero .lbl{ font-size:7pt; letter-spacing:.22em; text-transform:uppercase; color:#D9876C; font-weight:700; }
.hero .clabe{ font-family:var(--serif); font-size:20pt; font-weight:700; letter-spacing:.07em; margin-top:2pt;
font-variant-numeric:tabular-nums lining-nums; }
.grid{ display:grid; grid-template-columns:1fr 1fr 1fr; gap:3mm; }
.card{ border:1px solid var(--hair); border-radius:2.5mm; padding:6pt 10pt 7pt; }
.card.wide{ grid-column:1 / -1; }
.card .lbl{ font-size:6.6pt; letter-spacing:.2em; text-transform:uppercase; color:var(--muted); font-weight:700; }
.card .val{ font-size:11pt; margin-top:1pt; font-variant-numeric:tabular-nums lining-nums; }
.card .val.num{ font-family:var(--serif); font-size:12.5pt; letter-spacing:.04em; }
</style>
</head>
<body>
<div class="sheet">
<aside class="side">
<div class="mono">JV</div>
<div class="nm">Johann Joseph<br>Velazquez Antonio</div>
<div class="role">Consultor de Software</div>
<div class="contact">
<div class="blk"><div class="lbl">Correo</div><div class="val">johann_antonio85<wbr>@hotmail.com</div></div>
<div class="blk"><div class="lbl">Teléfono</div><div class="val">81 3161 0998</div></div>
</div>
</aside>
<main class="main">
<div class="eyebrow">Datos bancarios</div>
<div class="hero">
<div class="lbl">CLABE interbancaria · BBVA México</div>
<div class="clabe">012180015481668333</div>
</div>
<div class="grid">
<div class="card wide"><div class="lbl">Beneficiario</div><div class="val">Johann Joseph Velazquez Antonio</div></div>
<div class="card"><div class="lbl">RFC</div><div class="val">VEAJ031228MD6</div></div>
<div class="card"><div class="lbl">Banco</div><div class="val">BBVA México</div></div>
<div class="card"><div class="lbl">Número de cuenta</div><div class="val num">1548166833</div></div>
</div>
</main>
</div>
</body>
</html>
+63
View File
@@ -0,0 +1,63 @@
<!doctype html>
<html lang="es">
<head>
<meta charset="utf-8">
<title>Tarjeta de datos bancarios — Johann Velazquez</title>
<style>
@font-face{ font-family:'Caladea'; src:url("../propuesta/fonts/Caladea-Regular.ttf"); font-weight:400; }
@font-face{ font-family:'Caladea'; src:url("../propuesta/fonts/Caladea-Bold.ttf"); font-weight:700; }
@font-face{ font-family:'Carlito'; src:url("../propuesta/fonts/Carlito-Regular.ttf"); font-weight:400; }
@font-face{ font-family:'Carlito'; src:url("../propuesta/fonts/Carlito-Bold.ttf"); font-weight:700; }
:root{ --ink:#1C2B39; --accent:#BC5B3E; --soft:#B9C3CD; --mut:#8D9AA7;
--serif:'Caladea',Georgia,serif; --sans:'Carlito',system-ui,sans-serif; }
*{ box-sizing:border-box; margin:0; padding:0; }
html,body{ width:1200px; height:675px; background:var(--ink); }
.card{ width:1200px; height:675px; padding:64px 76px 56px; color:#fff; font-family:var(--sans);
display:flex; flex-direction:column; justify-content:space-between; position:relative; overflow:hidden;
background:
radial-gradient(620px 420px at 100% 0%, rgba(188,91,62,.30), rgba(188,91,62,0) 70%),
radial-gradient(500px 360px at 0% 100%, rgba(188,91,62,.12), rgba(188,91,62,0) 70%),
var(--ink); }
.top{ display:flex; justify-content:space-between; align-items:flex-start; }
.rule{ width:56px; height:4px; background:var(--accent); margin-bottom:18px; }
.eyebrow{ font-size:17px; letter-spacing:.24em; text-transform:uppercase; color:#D9876C; font-weight:700; }
.name{ font-family:var(--serif); font-size:50px; line-height:1.15; margin-top:10px; }
.bank{ text-align:right; font-size:17px; letter-spacing:.2em; text-transform:uppercase; color:var(--soft); font-weight:700; padding-top:22px; }
.bank b{ display:block; font-family:var(--serif); font-size:34px; letter-spacing:.02em; text-transform:none; color:#fff; margin-top:6px; }
.clabe{ }
.lbl{ font-size:15px; letter-spacing:.24em; text-transform:uppercase; color:var(--mut); font-weight:700; }
.digits{ font-family:var(--serif); font-weight:700; font-size:80px; line-height:1.05; letter-spacing:.04em;
font-variant-numeric:tabular-nums lining-nums; margin-top:6px; }
.row{ display:flex; gap:64px; padding-top:22px; border-top:1px solid rgba(255,255,255,.14); }
.row .v{ font-size:25px; color:#E8EDF2; margin-top:4px; font-variant-numeric:tabular-nums lining-nums; }
.row .v.num{ font-family:var(--serif); letter-spacing:.05em; }
.row .contact{ margin-left:auto; text-align:right; }
.row .contact .v{ font-size:19px; color:var(--soft); line-height:1.45; }
</style>
</head>
<body>
<div class="card">
<div class="top">
<div>
<div class="rule"></div>
<div class="eyebrow">Datos bancarios · Alta de proveedor</div>
<div class="name">Johann Joseph Velazquez Antonio</div>
</div>
<div class="bank">Banco<b>BBVA México</b></div>
</div>
<div class="clabe">
<div class="lbl">CLABE interbancaria</div>
<div class="digits">012180015481668333</div>
</div>
<div class="row">
<div><div class="lbl">Número de cuenta</div><div class="v num">1548166833</div></div>
<div><div class="lbl">RFC</div><div class="v">VEAJ031228MD6</div></div>
<div class="contact"><div class="v">johann_antonio85@hotmail.com<br>81 3161 0998</div></div>
</div>
</div>
</body>
</html>
Binary file not shown.

After

Width:  |  Height:  |  Size: 363 KiB

+35
View File
@@ -0,0 +1,35 @@
{
"input_html": "Opcion-B-Clasica.html",
"output_pdf": "Datos-bancarios-Johann-Velazquez-B.pdf",
"page_size": {
"width_mm": 215.9,
"height_mm": 139.7
},
"cover_full_bleed": false,
"render_timeout_ms": 30000,
"metadata": {
"title": "Datos bancarios — Johann Joseph Velazquez Antonio",
"author": "Johann Velazquez",
"subject": "Datos bancarios para alta de proveedor",
"keywords": "datos bancarios, alta de proveedor, Balam, BBVA, CLABE"
},
"footer": {
"_comment": "Media carta horizontal, una sola página: no se estampa footer.",
"text": "",
"skip_first_page": true,
"skip_pages": [],
"page_number_format": "{page}",
"rule": false,
"font_size_pt": 7.5,
"margin_mm": 12,
"color": [
0.46,
0.51,
0.57
]
},
"fonts": {
"footer_ttf": "../propuesta/fonts/Carlito-Regular.ttf"
},
"toc": {}
}
+35
View File
@@ -0,0 +1,35 @@
{
"input_html": "Opcion-C-Ficha.html",
"output_pdf": "Datos-bancarios-Johann-Velazquez-C.pdf",
"page_size": {
"width_mm": 215.9,
"height_mm": 139.7
},
"cover_full_bleed": false,
"render_timeout_ms": 30000,
"metadata": {
"title": "Datos bancarios — Johann Joseph Velazquez Antonio",
"author": "Johann Velazquez",
"subject": "Datos bancarios para alta de proveedor",
"keywords": "datos bancarios, alta de proveedor, Balam, BBVA, CLABE"
},
"footer": {
"_comment": "Media carta horizontal, una sola página: no se estampa footer.",
"text": "",
"skip_first_page": true,
"skip_pages": [],
"page_number_format": "{page}",
"rule": false,
"font_size_pt": 7.5,
"margin_mm": 12,
"color": [
0.46,
0.51,
0.57
]
},
"fonts": {
"footer_ttf": "../propuesta/fonts/Carlito-Regular.ttf"
},
"toc": {}
}
+35
View File
@@ -0,0 +1,35 @@
{
"input_html": "Carta-datos-bancarios.html",
"output_pdf": "Datos-bancarios-Johann-Velazquez-A.pdf",
"page_size": {
"width_mm": 215.9,
"height_mm": 139.7
},
"cover_full_bleed": false,
"render_timeout_ms": 30000,
"metadata": {
"title": "Datos bancarios — Johann Joseph Velazquez Antonio",
"author": "Johann Velazquez",
"subject": "Datos bancarios para alta de proveedor",
"keywords": "datos bancarios, alta de proveedor, Balam, BBVA, CLABE"
},
"footer": {
"_comment": "Media carta horizontal, una sola página: no se estampa footer.",
"text": "",
"skip_first_page": true,
"skip_pages": [],
"page_number_format": "{page}",
"rule": false,
"font_size_pt": 7.5,
"margin_mm": 12,
"color": [
0.46,
0.51,
0.57
]
},
"fonts": {
"footer_ttf": "../propuesta/fonts/Carlito-Regular.ttf"
},
"toc": {}
}
+185 -10
View File
@@ -2,22 +2,185 @@
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-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))._
_Última actualización: **2026-10-15** — ⭐ **Azure + CI/CD**: plataforma en https://balam-paf-staging.azurewebsites.net, pipeline de GitHub Actions por OIDC (build verde; falta el rol de despliegue), PostgreSQL 17, Log Analytics, Application Insights, Key Vault y Storage creados; traspaso de la BD ensayado y pendiente ([REGISTRO #92](REGISTRO.md)). Anterior: **2026-10-02** — ⭐ **prefacturas en lote** (Cotizaciones y bandeja → una prefactura por cotización, en serie; timbrado también desde el detalle de la factura), **publicadas en paputec a las 8:40 AM** (`73d1c2a`) ([REGISTRO #90](REGISTRO.md)). Anterior: **2026-10-01, madrugada** — ⭐🔥 **el TIMBRADO entra a la plataforma** (prefactura → CFDI por el puente, con dry-run, palabra TIMBRAR, verificación por lectura y bloqueo de reenvío), la cadena completa queda revisada (estado incierto de los POST cerrado, consecutivo sin la serie B, uso CFDI del mismo cliente), probada (212 + 10 pruebas, recorrido completo en navegador) y **publicada** (`f5b3743`) para la **prueba completa del 1-oct con Balam**; guion en `planeacion/` ([REGISTRO #89](REGISTRO.md)). Anterior: **2026-09-30, noche** — 🔥 **el puente de prefactura queda ENCENDIDO en producción** (sin credenciales del usuario `CTO` en el servidor: bloqueo del control de permisos, decisión de Johann pendiente), «Servicio de Enlace» mapeado al 106 y 11 cotizaciones con estado atrasado corregidas; lo que Pedro vio a las 7:26 (totales en cero, botones que "no llevan a ninguna parte", "servicios", Arturo) tenía detrás **cuatro bugs reales**, corregidos, probados (197 + 10 pruebas) y **publicados en paputec a las 9:44 PM** (`597c8a2`, migración `QuoteSubtotal`); las credenciales del usuario `CTO` entraron al servidor a las 9:41 PM con autorización de Johann: el puente queda completo ([REGISTRO #88](REGISTRO.md)). Anterior: **2026-09-30** — ⭐ el webhook de Jira FAC queda **en producción (paputec)**: publicado, secreto nuevo, webhook id 1 movido y prueba de punta a punta con FAC-170 en 1.05 s ([REGISTRO #85](REGISTRO.md)); y la vista previa automática de la cotización queda **publicada** ([#86](REGISTRO.md)). 🔥 **La API key de `cto` ya está en producción y el sync volvió**; la vista previa se validó contra el BIND real (FAC-171) y el **modal en vivo con "Aceptar"** quedó **publicado** a las 6:53 AM, con el modo automático permitido en el servidor pero sin elegir ([#87](REGISTRO.md)). Además, fusión de las dos sesiones paralelas del 29-sep: la del webhook pasa a ser el [#84](REGISTRO.md) (nació como #71 en la otra máquina). Anterior: **2026-09-29** — ⭐ Balam entrega **login web a BIND** con permisos de prefactura; endpoint interno de la UI confirmado (*Generar Venta → Prefactura*, petición `InvoiceDetails`) y el modelo de dos endpoints corroborado con el SDK oficial ([REGISTRO #83](REGISTRO.md)). **Huecos de registro cerrados:** las sesiones del 21-ago al 10-sep se reconstruyeron desde el buzón ([#71](REGISTRO.md)–[#82](REGISTRO.md)). Anterior: **2026-09-15** — corte de horas 10-ago→15-sep reconstruido y huecos de registro listados (seis sesiones sin bitácora). Anterior: **2026-08-21, mediodía** — ⭐ primera cotización real emitida (folio 1026; la 2.ª, corregida, pendiente de confirmar). Anterior: **2026-08-20, noche.** Cruce completo del buzón contra la bitácora (MCP ms365): se incorporaron 9 comunicaciones faltantes y se corrigieron 2 errores de registro. **Hay sesión mañana viernes 21-ago, 7:00 am** con Noé, Ara y Pedro sobre el formulario modificado y `FAC-106`. Ver [REGISTRO #65](REGISTRO.md)–[#68](REGISTRO.md)._
## ☁️ 15-oct — Azure + CI/CD: lo que falta para cerrar la Etapa 1 ([REGISTRO #92](REGISTRO.md), runbook en `balam-plataforma/deploy/azure/README.md`)
- [ ] 🔑 **Balam (la única solicitud pendiente):** rol **Website Contributor** sobre la web app `balam-paf-staging` para la identidad `id-balam-paf-staging-github` (principalId `ebb12547-c96d-4993-9c57-2cc1bd7abaa0`). En cuanto exista, cada push a `main` publica solo. *Opcional en el mismo mensaje:* "Role Based Access Control Administrator" acotado a Key Vault Secrets Officer/User, Storage Blob Data Contributor, Website Contributor, Monitoring Reader y Log Analytics Reader, para no volver a pedir roles.
- [ ] 🌐 **Johann — red de la BD nueva** (`psql-balam-paf-staging`, hoy con el acceso público apagado): encenderlo con firewall solo a las IPs de salida del App Service (comandos en el README) o red privada (requiere `Microsoft.Network`, que tendría que registrar Balam).
- [ ] 🗄️ **Johann — traspaso de la BD**, en una ventana sin operación y **después de los comentarios de Pedro de la prueba del 15-oct**: detener el sitio de Azure y el API de paputec → `pg_dump` de paputec → crear `balam_app` y la base → `pg_restore` como `balam_app` → comparar conteos → configuración del sitio (cadena nueva, `Db__MigrateOnStartup=true`, `Sync__SchedulerIntervalMinutes=15`, `APPLICATIONINSIGHTS_CONNECTION_STRING`) → dejar paputec apagado. Las contraseñas generadas están solo en la máquina Windows (`%USERPROFILE%\.balam-secretos\`); en otra máquina, fijar unas nuevas.
- [ ] 🔁 **Webhook de Jira** tras el traspaso: reenviar `/balam/api/jira/webhook` desde Apache de paputec a Azure (sin tocar el Jira de Balam) o `PUT` del webhook id 1 con autorización de Balam.
- [ ] 🧹 Tras el traspaso: quitar `paputec-pg-ca.crt` de `empaquetar.sh`, borrar la regla temporal del firewall y pasar los secretos a Key Vault cuando haya roles.
- [ ] 💰 Decidir si se baja el tope de Log Analytics de 1 a 0.3 GB/día (peor caso ≈ 63 → ≈ 10 USD/mes; el uso normal cabe en los 5 GB gratis).
- [ ] ⏱️ Reconstruir el corte de horas del 3 al 15-oct (incluye la sesión de Azure del 15-oct, ≈3 h). La Etapa 1 lleva 32.50 de 32–39 h y el total 109.83 de 112–136 h (más 7–10 del ajuste Jira): avisar por escrito a Balam antes de comprometer la Etapa 3.
- [x] ~~☁️ Azure: Balam ya dio el acceso; volver a pedir la contraseña~~ → **15-oct:** acceso verificado (Contributor en el RG, proveedores registrados) y plataforma desplegada ([#92](REGISTRO.md)).
## ⭐ 2-oct — prefacturas en lote ([REGISTRO #90](REGISTRO.md))
- [x] ~~🚀 **Johann — decidir push + publicación de `73d1c2a`**~~ → **HECHO el 2-oct, 8:40 AM** (respaldo, imagen `pre-lote-20261002`, publicado; [#90](REGISTRO.md)).
- [ ] 🧹 Imagen de rollback `balam-api:pre-lote-20261002` en paputec: borrarla con las demás `pre-*` cuando la versión lleve unos días estable.
- [x] ~~📝 **Registrar el resultado de la prueba completa del 1-oct**~~ → **Registrado en [#91](REGISTRO.md) (2-oct):** el 1-oct llegó a prefactura (~20 min, el timbrado no se intentó) y el **2-oct a las 3:32 pm Pedro timbró desde la plataforma: proceso completo**. Faltan folio, UUID y ticket del primer CFDI.
- [x] ~~🔧 Deuda: la tarjeta de timbrado no vive aún en el detalle de la factura~~ → **HECHO el 2-oct** (`73d1c2a`): `/facturas/:id` muestra la tarjeta para prefacturas.
## 🔥🔥 1-oct — PRUEBA COMPLETA con Balam: ticket → cotización → prefactura → CFDI ([REGISTRO #89](REGISTRO.md), [guion](../planeacion/Guion-prueba-completa-2026-10-01.md))
- [ ] 🔥 **Johann — seguir el guion** durante la llamada: nadie en BIND como `CTO`; ticket con cliente de BIND y una de las 10 categorías mapeadas; monto reconocible; leer el **método de pago (PUE/PPD)** en el dry-run del CFDI antes de teclear TIMBRAR; acordar quién cancela el CFDI de prueba.
- [ ] 🔴 **Primer `EmitCFDI` contra el BIND real** — lo único no verificado: la forma de la respuesta y los textos de rechazo del PAC. Anotar en la bitácora lo que devuelva (queda en `WriteOperations.ResultJson`).
- [ ] 📋 **Pedir a Balam en la llamada:** los códigos de BIND de las 7 categorías sin mapear y si quieren el **modo automático** (sin clic) — con autorización escrita (ADR-001).
- [ ] 🔧 **Al cerrar la prueba:** cancelar el CFDI en BIND (Balam) → «Verificar» en la plataforma → *Cancelada*; registrar folio, UUID y tiempos en el #90.
- [ ] 🧹 Imagen de rollback `balam-api:pre-timbrado-20261001` en paputec: borrarla con las demás `pre-*` cuando la versión lleve unos días estable.
- [ ] 🧹 Evento de prueba **FAC-171** (id 2, `Lista`, ticket borrado): descartarlo desde la campana antes de la llamada para que no estorbe.
- [x] ~~🔧 Deuda: la tarjeta de timbrado no vive aún en el detalle de la factura (`/facturas/:id`)~~ → **HECHO el 2-oct** ([#90](REGISTRO.md)).
- [ ] 🔧 Deuda: el método de pago de la prefactura lo decide BIND (PUE) aunque el cuerpo lleve PPD; si Balam quiere PPD por default, hay que entender qué campo manda (`PaymentMethodCFDI33`/`PaymentTermType` del cliente) o editar la prefactura en BIND antes de timbrar.
## 🔥 30-sep (noche) — prefactura desde producción ([REGISTRO #88](REGISTRO.md))
- [x] ~~🔑🔴 **Johann — credenciales del usuario `CTO` de BIND en `/opt/balam/.env`**~~ → **HECHO el 30-sep, 9:41 PM, con autorización explícita de Johann** (adenda del [#88](REGISTRO.md)): las tres variables en el `.env` (root 0600), huella verificada en el contenedor, API sana. El puente ya entra solo a la web app; la cookie queda como respaldo.
- [x] ~~🚀 **Johann — decidir push + publicación de `597c8a2`**~~ → **HECHO el 30-sep, 9:44 PM** (adenda del [#88](REGISTRO.md)): push a GitHub y a git.paputec.mx, respaldo `balam-20261001-034359.sql.gz`, imagen `balam-api:pre-prefactura-20260930`, migración `QuoteSubtotal` aplicada, bundles nuevos servidos. Primer ciclo del sync: 50 subtotales; los 67 restantes en los dos ciclos siguientes.
- [ ] 🧹 Imagen de rollback `balam-api:pre-prefactura-20260930` en paputec (ya son cuatro `pre-*-20260930`): borrarlas cuando la versión lleve unos días estable.
- [ ] 🧪 ~~**Primera prefactura real desde producción**, solo con Johann frente a la app~~ → **pasa a la prueba completa del 1-oct** (bloque de arriba): cotización → prefactura → CFDI en una sola sesión con Balam.
- [x] ~~📋 Mapear «Servicio de Enlace»~~ → **HECHO el 30-sep, 8:04 AM:** `Emision__ServicioPorCategoria__ServicioDeEnlace=106` en producción. Era la causa de "faltaba el campo de servicios" en FAC-172/173 (el campo sí llegaba; faltaba el mapeo). Quedan **7 categorías** sin ítem en BIND (bloque del #87, abajo).
- [ ] 📋 **Confirmar con Balam** que la prefactura **2467** aparezca Cancelada por decisión suya (así se ve en BIND el 30-sep) y que las cotizaciones **1084/1085** de la sesión con Pedro se cancelaron a propósito.
- [ ] ⚠️ **Una sola sesión operando el servidor.** Entre 7:28 y 7:43 AM otra sesión tocó `Seed__Admin__Password` en el `.env` de producción y reinició el API dos veces mientras esta sesión diagnosticaba. Dos manos en `/opt/balam/.env` al mismo tiempo pueden pisarse cambios.
- [ ] 🧹 Las 11 cotizaciones que la plataforma tenía "Activas" y BIND no ya quedaron corregidas (cursor en null + relectura, 0 desfasadas a las 8:35 PM); con `597c8a2` publicado el sync barre todo cada ciclo y no vuelve a pasar.
## 🔥 30-sep — modal en vivo, aceptación y modo automático ([REGISTRO #87](REGISTRO.md))
- [x] ~~🚀 **Johann — decidir push + publicación del modal en vivo**~~ → **HECHO el 30-sep, 6:53 AM** (`aac7447`, migración aplicada). Falta ver el punto de la campana en un navegador: verde = canal en vivo por Apache; ámbar = respaldo cada 4 s.
- [ ] 🧪 **Validar en producción con Johann frente a la app:** ticket de prueba (con autorización) → modal en vivo → "Aceptar". ⚠️ Eso crea una **cotización REAL en BIND**: usar un monto de prueba reconocible y cancelarla después.
- [ ] 🔴 **Modo automático: el servidor ya lo PERMITE** (`Jira__Webhook__AutoCreateQuoteAllowed=true`, decisión de Johann del 30-sep) pero nadie lo ha elegido: producción sigue pidiendo confirmación. Antes de que alguien lo elija en la campana: **archivar la autorización escrita de Balam, enmendar ADR-001** y fijar un tope (`Jira__Webhook__AutoCreateMaxSubtotal`, hoy sin definir). Para volver a prohibirlo: `false` en el `.env` y `up -d api`.
- [ ] 📋 **Pedir a Balam (Pedro) el código de BIND para 7 categorías** del combo Servicio/Producto que no resuelven y caen en "Faltan datos": Gestión de Proyectos Senior / PMO · Gestión de Mesa de Servicio (SDM) · Soporte Especializado L1 · L2 · Gestión de Incidentes · de Cambios · de Capacidad. Se cargan en `Emision__ServicioPorCategoria__…` del `.env`. *(30-sep: «Servicio de Enlace» → 106 ya está en producción, [#88](REGISTRO.md); 10 de 17 categorías y 17 de 17 clientes resuelven. Las 7 restantes no tienen ítem equivalente en el catálogo de BIND de 11 ítems: necesitan un ítem nuevo o una decisión de Balam.)*
- [ ] 🧹 **Evento de prueba FAC-171** (id 2 en paputec, `Lista`, ticket ya borrado): descartarlo desde la campana (sigue ahí al 1-oct).
- [ ] 🧹 La API key anterior (usuario `arturo`) quedó **comentada** en `/opt/balam/.env` para rollback: borrarla cuando la de `cto` lleve unos días estable.
- [ ] 👀 En esta Mac sigue corriendo desde el **3-sep** una instancia local del API (puerto 5078) y del frontend (4200), con el código de ese día: no se tocó. Apagarla o reiniciarla cuando convenga.
- [ ] 🔧 La tabla de la bandeja se sale del ancho a 1440 px (la columna BIND y los botones quedan cortados); ya pasaba antes del chip nuevo.
- [ ] 🔧 Deuda del modal: enseña el resumen de la cotización, no el JSON del dry-run (está en `WriteOperations`); agregar "ver el body" si Balam lo pide. El canal en vivo es en memoria: vale para una sola instancia del API.
## ⭐ 30-sep — webhook de Jira en producción ([REGISTRO #85](REGISTRO.md)) y vista previa automática ([#86](REGISTRO.md))
- [x] ~~🔧 **Siguiente meta:** con cada `JiraTicketEvent` verificado, armar la vista previa (dry-run) de la cotización y marcar el ticket "lista para revisar" en la bandeja.~~ → **CONSTRUIDA el 30-sep** (commits locales `1c37eaa` y `287cb65`, 147 pruebas en verde, [#86](REGISTRO.md)). La confirmación sigue siendo humana (ADR-001).
- [x] ~~🚀 **Johann — decidir push + publicación de la vista previa automática**~~ → **HECHO el 30-sep por orden de Johann:** push de `1c37eaa` + `287cb65` y publicación en paputec (migración aplicada, trabajador activo, FAC-170 → `NoAplica`). Ver adenda del [#86](REGISTRO.md).
- [x] ~~🔥🔑 **Poner la API key del usuario `cto` en `Bind__ApiKey` de `/opt/balam/.env`**~~ → **HECHO el 30-sep con autorización de Johann** ([#87](REGISTRO.md)): `[sync] Ciclo OK`, el sync volvió tras 20 días.
- [x] ~~🧪 Con la key nueva en producción: ticket de prueba para ver una vista previa real de punta a punta.~~ → **HECHO del lado del servidor con FAC-171** (vista previa `Lista` en 4.32 s, operación #155, [#87](REGISTRO.md)). Verla en el navegador pasa al bloque del modal en vivo, arriba.
- [ ] 🧹 Imagen de rollback `balam-api:pre-vistaprevia-20260930` en paputec (además de la `pre-webhook`): borrarlas cuando la versión lleve unos días estable.
- [ ] 🔧 **Deuda:** las reglas de prellenado viven dos veces (`cotizacion-prefill.ts` y `CotizacionPrefill.cs`). Que las pantallas de cotización (individual y lote) consuman el prellenado y el dry-run ya preparado del servidor.
- [ ] 🔧 Suscribir `jira:issue_updated` (o repetir la vista previa al abrir el ticket) para que "Faltan datos" se actualice cuando alguien completa el ticket en Jira.
- [x] ~~⚠️ **Producción corre con `Bind__Mode=Write`** … falta alinear esa documentación con la decisión~~ → **Documentación alineada el 30-sep en `597c8a2`** (runbook y `CLAUDE.md` de la plataforma dicen que producción corre en Write y con el puente encendido por decisión de Johann, y cómo apagarlo). Regresar a ReadOnly solo cuando Johann lo indique.
- [ ] 🔑 **El secreto del webhook cambió:** el vigente está en `/opt/balam/.env` (`Jira__Webhook__Secret`) y en `~/jira-webhook-secret.txt` de la Mac desde la que se hizo el despliegue. El `~/jira-webhook-secret.txt` de la otra máquina (piloto del 29-sep) **ya no sirve**: reemplazarlo por un canal seguro antes de usar ahí el subcomando `mover`/`registrar`.
- [ ] 🧹 Imagen de rollback `balam-api:pre-webhook-20260930` en paputec: borrarla cuando la versión nueva lleve unos días estable.
- [ ] 🔧 **Poller de respaldo** del webhook (JQL delta con checkpoint): sigue sin construirse; hoy un aviso perdido no se recupera solo.
## 🔥 29-sep — login web a BIND y endpoint interno de la UI ([REGISTRO #83](REGISTRO.md))
- [ ] 🔑 **Guardar las credenciales del login web fuera del repo** (`dotnet user-secrets` en desarrollo; `/opt/balam/.env` en paputec) y **destruir el TXT** en correo y disco. Misma lección del token de julio (VALIDACION-API.md §8.12).
- [ ] 🔴 **Johann — revisar el error que impide cargar la solicitud al crear una cotización en BIND** (visto en la sesión) y **crear la cotización** después de corregirlo. Puntos de acción del informe de Read AI.
- [x] ~~⭐ **Especificar el endpoint interno `InvoiceDetails` con la sesión propia**~~ → **YA ESTABA HECHO en la otra máquina** (se supo al fusionar el 30-sep): el contrato real es `POST /InvoicesAPI/AddInvoiceTransaction`, capturado el 10-sep y documentado en `balam-plataforma/CLAUDE.md` (§Puente a la UI de BIND); el login de formulario `/Login/Login` también. Ver [#84](REGISTRO.md), punto 5. ⚠️ El contrato sigue **sin escribirse en esta bitácora** (hueco del 9/10-sep, abajo). Original: URL, método, `Content-Type`, shape del payload, respuesta, cookies y `__RequestVerificationToken`, y el flujo de `/Login/Login`; capturar con DevTools o Playwright, sanitizar y escribirlo como contrato antes de codificar.
- [x] ~~🔧 **Construir `BindUiClient`**~~ → **YA ESTABA HECHO** (commit `4d6b3d8` del repo del cliente): dry-run → confirmación humana → verificación por lectura, con el candado `ValidarEsPrefactura`; primera prefactura real el 29-sep, folio 2467 ([#84](REGISTRO.md)). No reemplaza el 409 de `POST /api/emision/prefactura`: ese candado se conservó y el puente vive en `POST /api/emision/prefactura-ui`. En paputec el puente está **apagado** (no hay `Bind__Ui__*` en el `.env`).
- [ ] ⚠️ **Acordar con Balam que las prefacturas creadas así llevan `CreatedByName` = cuenta de Noé** (o pedir un usuario de servicio dedicado).
- [ ] 📧 **Pedir en paralelo a soporte BIND el campo/endpoint oficial de prefactura**, con la evidencia del #70 + el swagger del SDK (`NewInvoice` sin campo de tipo) + la solicitud pública "agregar PREFACTURA" de su foro. Replicar la UI es frágil y posiblemente ajeno a sus términos de servicio.
- [ ] 📼 **Recuperar los informes completos de Read AI** (al menos 9, 10 y 29-sep) desde app.read.ai con sesión — los correos solo traen el extracto — y guardarlos en `../fuentes/`.
## ⭐ 29-sep — webhook Jira FAC → vista previa de cotización ([REGISTRO #84](REGISTRO.md), [plan](../planeacion/Plan-Webhook-Jira-2026-09-29.md))
- [ ] 📄 **Archivar en `fuentes/` la autorización escrita de Pedro** para crear el webhook/regla en Jira; anotar su alcance (¿incluye crear un ticket de prueba en FAC?).
- [x] ~~🔎 **Fase 1 (solo GET):** `myself` + `mypermissions` (ADMINISTER / ADMINISTER_PROJECTS en FAC) + inventario de webhooks → decide Vía A (webhook de sistema) o Vía B (regla de Automation).~~ → **HECHO (29-sep):** `ADMINISTER`=true, 0 webhooks de sitio, API de Automation accesible (50+ reglas). Vía A y Vía B disponibles.
- [x] ~~🔴 **Decidir el evento:** creación del ticket, transición a *En proceso de facturación* (dura ~3 min), o ambos.~~ → **Creación del ticket** (`jira:issue_created`) para el piloto; la transición a *En proceso de facturación* queda para después.
- [x] ~~🔧 **Fase 2:** `POST /api/jira/webhook` con secreto, relectura del ticket, idempotencia y solo dry-run; pruebas con `FakeHandler`. Poller de respaldo.~~ → **HECHO (29-sep, `d7238da`).** Falta el poller de respaldo.
- [x] ~~🌐 **Fase 3:** ngrok con traffic policy de una sola ruta; API en ReadOnly mientras el túnel esté abierto.~~ → **HECHO (29-sep)**; túnel apagado al cerrar la sesión.
- [x] ~~✍️ **Fase 4:** alta del webhook/regla como operación numerada (id, URL, fecha, autorización).~~ → **HECHO: webhook de sitio id 1** (19:05:53).
- [x] ~~🧪 **Fase 5:** ticket de prueba → "lista para revisar" → verificar que BIND no recibió escritura. Reversa documentada.~~ → **HECHO: FAC-169** creado → detectado en 14.4 s (HMAC, verificado) → borrado. Ver adenda del [#84](REGISTRO.md).
- [x] ~~🔥 **Mover el webhook id 1 a paputec** (`https://paputec.mx/balam/api/jira/webhook`)~~ → **HECHO el 30-sep** ([#85](REGISTRO.md)): plataforma publicada (`5145801`), secreto nuevo en `/opt/balam/.env`, `PUT` del webhook 1 → 200 (solo HMAC, sin token en la URL) y prueba con **FAC-170** detectada en 1.05 s y borrada.
- [x] ~~🔧 **Siguiente paso de la meta:** vista previa (dry-run) de la cotización por cada detección.~~ → **Construida el 30-sep** ([#86](REGISTRO.md)); su publicación está en el bloque del 30-sep, arriba.
- [x] ~~☁️ **Destino productivo del webhook:** ngrok con dominio fijo (de pago) vs Azure (sigue bloqueado por el cliente).~~ → **Resuelto por ahora con paputec** (30-sep), el mismo ambiente provisional de la plataforma. Cuando exista Azure, el webhook se mueve con otro `PUT`.
- [ ] ⚠️ **Una sola sesión de Claude Code dueña de la API** en sesiones de escritura (el 29-sep otra sesión la relanzó en ReadOnly).
- [x] ~~🔥 **API público de BIND en 500 en todos los endpoints** (key de Arturo)~~ → **RESUELTO el 30-sep** con la key del usuario `cto` en producción ([#87](REGISTRO.md)). Original: — sync detenido desde el 10-sep. Pedir a Balam la API key del usuario `CTO` o revisar el acceso de Arturo. **→ 30-sep: causa encontrada.** El API no está caído: la key de `arturo` es la que falla; la de `cto` responde 200. Falta cambiarla en paputec (bloque del 30-sep, arriba).
- [ ] 🔥 **Prefactura 2467 ($777,777 MXN, ACUNTIA, FAC-166):** confirmar con Balam si es prueba (cancelar/borrar antes de que alguien la timbre) o real; revisar que al timbrar quede **PPD**, no el PUE que BIND le puso.
- [ ] 📧 **Avisar a Pedro de la cotización 1079 marcada como Surtida por error** (`QuotesAPI/FillQuote` escribe por GET); sin efecto fiscal, irreversible desde la UI.
- [ ] 🔧 Tomar el bloque `Client` de `ClientsAPI/GetClientSelectedById` y que el wizard no muestre G03/16% cuando el puente manda lo de BIND. *(1-oct: el uso CFDI ya no se hereda de otro cliente — solo del mismo, o 23/S01 con aviso — [#89](REGISTRO.md); el bloque exacto sigue pendiente.)*
- [ ] 📝 **Hueco de bitácora:** registrar el trabajo del 9/10-sep (puente a la UI de BIND, prefactura 2463, sonda #12 / remisión 1004) — hoy solo vive en `balam-plataforma/CLAUDE.md`.
## 🚨 Huecos de registro (detectados en el corte del 15-sep)
- [x] ~~🚨 **Seis sesiones con Balam sin entrada en REGISTRO.md**~~ → **REGISTRADAS el 29-sep** ([#72](REGISTRO.md), [#73](REGISTRO.md), [#75](REGISTRO.md), [#76](REGISTRO.md), [#78](REGISTRO.md), [#80](REGISTRO.md), [#81](REGISTRO.md); la del 29-sep en [#83](REGISTRO.md)) — ⚠️ solo con el extracto del correo de Read AI, sin transcript. Detalle original (evidencia: convocatorias de Pedro e informes de Read AI en el buzón): **21-ago 1:00 pm** "Seguimiento - PAF (2)" (homologación de estados del flujo; pendiente prueba de escritura en Jira), **24-ago 7:00 am** (validación cotización→prefactura MXN/USD, IVA 16 %/0 %; Noé condiciona Azure a que las pruebas funcionen), **26-ago 7:00 am** (permisos y flujo remisión/prefactura/factura; el POST se timbró en vivo → [#70](REGISTRO.md)), **3-sep 7:00 am** (datos del tenant de Azure; conceptos de Acuntia por headcount; definición: ítem por código y concepto = título del ticket) y **1:30 pm** (tres tickets de Pedro cotizados desde la plataforma publicada), **9-sep 1:00 pm** "Seguimiento PAF - Prefactura" (Araceli crea una prefactura en la UI de BIND con "Generar venta" mientras se capturan las peticiones: `InvoiceDetails`), **10-sep 1:00 pm** (con Pedro: cotización 1078 de Acuntia y captura de la petición de red de la prefactura).
- [x] ~~📝 **Registrar la publicación del 3-sep**~~ → **HECHO (29-sep): [#77](REGISTRO.md) y [#79](REGISTRO.md).** Original: registrar la publicación del 3-sep (https://paputec.mx/balam/, runbook en `balam-plataforma/deploy/paputec/README.md`) y la **cotización en lote del 7-sep** — hoy solo constan en el CLAUDE.md del repo del cliente y en los commits `03582af`, `ed4c819`, `fbdbd18` (pusheados el 15-sep).
- [ ] ⏱️ **Validar las 39.25 h reconstruidas** del 10-ago al 15-sep en `../planeacion/Seguimiento-horas.csv` (todas marcadas `reconstruida`, con la evidencia en cada fila) y decidir a quién se reporta el corte ahora que el seguimiento es con Pedro. Excel listo: `../planeacion/Avance-Balam-2026-09-15.xlsx`.
- [ ] ⏱️ **Validar las 13.50 h `reconstruida` del corte del 2-oct** (21 filas con nota `Corte 2-oct`, 15.25 h nuevas; total 109.83 h). Detalle, reclasificación de Jira y decisiones de avance en `../planeacion/Seguimiento-horas.md`; Excel `../planeacion/Avance-Balam-2026-10-02.xlsx`. **Falta publicarlo en el Sheet "Avance-Balam"** (Drive/Balam) y mandar el manual de usuario (`../manual/Manual-usuario-Balam.pdf`).
- [x] ~~☁️ **Azure: Balam ya dio el acceso (fecha sin registrar); volver a pedir la contraseña** y desplegar ahí~~ → **HECHO el 14/15-oct** ([#92](REGISTRO.md)); lo que falta está arriba, en la sección del 15-oct.
- [ ] 🎨 **Ajustes de UI/UX pendientes** (Johann, 2-oct): botones «Ver en Jira» / «Ver en BIND» y las pantallas de Solicitudes, Cotizaciones y Facturas (Solicitudes probablemente se queda como está; falta revisarlo). En el Excel de avance es la fila «Ajustes de UI/UX de la emisión» (Etapa 2, 0%, 1–2 h).
- [x] ~~📧 **Correos de Noé del 24-ago sin registrar:**~~ → **REGISTRADOS en [#74](REGISTRO.md)**, evidencia en `../fuentes/`. Original: "Re: Seguimiento FAC-106" (explica que la prefactura se genera con *Generar venta* en Acciones) y "Acceso a Azure Balam y permisos" (pide los requisitos exactos; Johann respondió el 25-ago con suscripción + grupo de recursos + Contributor).
## 🔥🔴 26-ago — incidente del timbrado y cierre de la investigación ([REGISTRO #70](REGISTRO.md))
- [ ] 🔥🔴 **CANCELAR EL CFDI 2386 EN BIND** (motivo SAT 02 "sin relación"). ⚠️ **Al 26-ago tarde sigue `Activa` con UUID** — verificado por API. Receptor ACUNTIA (RFC genérico extranjero, $1,160 MXN). Confirmar además que BIND no le haya enviado el correo automático al cliente. **Movimiento (26-ago, en la sesión): Pedro tomó la acción de solicitar apoyo para cancelarla ([#75](REGISTRO.md)); al 29-sep no consta confirmación.**
- [x] ~~📧 **Avisar a Noé/Arturo del incidente con transparencia**~~ → **Noé y Pedro estaban en la sesión donde ocurrió** ([#75](REGISTRO.md)) y Pedro tomó la cancelación. ⚠️ A Arturo no consta que se le haya informado. Original: avisar (qué pasó, por qué, contención aplicada, CFDI cancelado). La plataforma tiene la auditoría completa en `WriteOperations` #73.
- [x] ~~🧪 **Experimento decisivo: crear UNA prefactura a mano en la UI**~~ → **RESUELTO sin crear nada: el folio 2384 (Araceli, 19-ago) ES una prefactura** — confirmado en UI por Johann. Prefacturas = filas `false`+UUID null; `Status=2` = facturada (parió a la 2385 timbrada). Ver adenda del [#70](REGISTRO.md). **La detección/vinculación automática de prefacturas ya es construible sin escribir.**
- [x] ~~🧪 **Experimento seguro pendiente de go-ahead: POST `false` apuntando a la secuencia de folios compartida**~~ → **EJECUTADO la noche del 26-ago (op #79, folio 1002): BIND ignoró el `Number`** — adenda 2 del [#70](REGISTRO.md). Original: POST `false` a la secuencia compartida (`Number` explícito + espejo de 2384: `CFDIUse=23`, `ShowIEPS=true`, plazo) para ver si la UI lo etiqueta Prefactura. Riesgo máximo: documento no fiscal borrable con folio raro. Requiere `Bind:Mode=Write` en sesión y autorización expresa.
- [ ] 📧 **Preguntar a Pedro / soporte BIND** *(sigue vigente como vía paralela al endpoint interno del [#83](REGISTRO.md), ahora con la evidencia del SDK oficial)*: "¿cómo se crea una venta de tipo *Prefactura* por API? Con `IsFiscalInvoice=false` cae como Remisión (secuencia 1000) y con `true` se timbra al crear; el diff contra una prefactura real no muestra ningún campo de tipo". Adjuntar la evidencia del #70.
- [ ] 🧹 **Borrar las remisiones 1000 y 1001** (siguen `Activas`). En la UI, o por API (`DELETE /api/Invoices/{id}`) con autorización expresa.
## ✅ 21-ago — la cotización ya se emite; lo que deja la sesión ([REGISTRO #69](REGISTRO.md))
- [ ] 🔥 **Cancelar el folio 1026 en BIND** (IVA 243,640.96 por el error de escala). Araceli o Johann, desde Ventas → Cotizaciones.
- [ ] 🔥 **Confirmar la segunda emisión** (`VAT: 0.16`): folio esperado 1027, total **1,431.44 MXN**. Verificar por lectura y anotar el folio en #69.
- [x] ~~📧 **Enviar el correo a Pedro (CC Noé, Ara)** con la causa~~ → **ENVIADO el 21-ago 7:32 am** a Noé (CC Pedro), hilo "Seguimiento FAC-106" ([#71](REGISTRO.md)); Noé respondió el 24-ago con el mecanismo *Generar Venta* ([#74](REGISTRO.md)). Original: enviar el correo con la causa — borrador listo en `../planeacion/Correo-error-resuelto-2026-08-21.md`; faltan los dos corchetes (folio y estado de la 1026).
- [ ] 📝 **Capturar la minuta de las definiciones de la sesión:** ¿se validaron las 17 categorías? ¿`Empresa origen` = 3 cuentas o 1? ¿se agrega IVA 0%? ¿Azure? ¿a quién se reportan horas ahora? *(29-sep: el extracto de Read AI de esa sesión solo alcanza a decir "Se definió que el IVA nacional…"; sigue sin minuta.)*
- [x] ~~🔧 **Resolver el servicio desde el combo, no fijo:**~~ → **HECHO el 3-sep** (definición de Pedro: categoría → código del catálogo `/api/Services`, mapeo `Emision:ServicioPorCategoria`, selector de ítem; commit `ed4c819`, [#76](REGISTRO.md)). Original: la UI manda el GUID del servicio 027 hardcodeado. Falta el mapeo 17 categorías de Jira → GUID de `/api/Services` (y BIND tiene 5 "Servicio de Enlace": hay que elegir uno por categoría, idealmente después de que Balam depure su catálogo).
- [ ] 🔧 **Lista de precios desde el cliente** *(hecho en la prefactura el 23-ago, commit `a07fff6`; falta verificar que la cotización haga lo mismo)*: usar `Clients.PriceListID` (CEMEX → "A") en vez del GUID fijo; el read model de `Quotes` reporta ese campo mal.
- [x] ~~**Sesión 21-ago**~~ → **Hecha.** Tres intentos en vivo fallaron (protocolo aplicado, nada creado); la causa se encontró después y la emisión funcionó al 5.º intento.
- [x] ~~🔒 **Bind:Mode ReadOnly al cerrar**~~ → **Hecho en esta máquina** (y API/ng apagados). ⚠️ En la otra máquina configurar user-secrets desde cero; `Bind:Mode` por default es ReadOnly.
## 🔥🔥 SESIÓN VIERNES 21-AGO, 7:00 AM — Noé, Araceli y Pedro (preparación; ya ocurrió)
- [x] ~~🔥 **Johann — inspeccionar `FAC-106` por API**~~ → **HECHO (20-ago, noche).** Informe completo: [`../planeacion/Sondeo-FAC-106-y-payload-2026-08-20.md`](../planeacion/Sondeo-FAC-106-y-payload-2026-08-20.md). ⭐ **El formulario nuevo resuelve conceptos/partidas**: `Servicio/Producto` (combo, 17 categorías), `Cantidad`, `IVA` y `Cliente` (combo, 17 clientes de BIND) ya son campos estructurados; request type nuevo **`Cotización Bind`**. Config corregida: el campo de cliente pasó de `customfield_10202` a **`customfield_11598`** — era la causa de que el 12-ago *"el cliente no se detectó"*.
- [x] ~~🔥🔴 **Resolver el ERROR 500 del POST de cotización**~~ → **DIAGNOSTICADO (20-ago)** leyendo 4 cotizaciones reales de BIND. Tres defectos en el payload: **(1)** `VATRate` va como **fracción** (`0.16`), no como `16`; **(2)** el `VAT` de partida es **monto**, no tasa (`Price × Qty × 0.16` — verificado en 3 de 4); **(3)** cada cotización real usa un `PriceListID` distinto y **ninguno coincide** con el único que devuelve `GET /api/PriceLists` → **omitirlo**. Dry-run corregido ejecutado (operación #18); body listo en el informe. Falta la confirmación real, que es la prueba de mañana.
- [ ] 🔴 **PREGUNTAR MAÑANA — `Empresa origen` tiene 3 valores: Balam, RegioTurk y Helmstone.** ¿La cotización se emite siempre en la misma cuenta de BIND o hay una por empresa? Hay **un solo token**. Si son cuentas distintas, el alcance cambia.
- [ ] 🔴 **PREGUNTAR MAÑANA — el combo `IVA` de Jira solo ofrece `16%`.** No hay **0%**, y en el combo de clientes hay tres de exportación (ACUNTIA S.A.U., VictTec LLC, IAMCFORWARDER). El Discovery registró 16%/0% y en BIND hay cotizaciones reales con `VAT = 0`. **Falta la opción.**
- [ ] 🔴 **PREGUNTAR MAÑANA — "Servicio de Enlace" existe 5 veces en BIND**, con unidad, unidad SAT y `ChargeVAT` **distintos entre sí** (una cobra IVA, cuatro no). El combo de Jira manda una sola etiqueta: **la correspondencia etiqueta → ítem de BIND no está definida.** Se depuró el combo de Jira; **falta depurar BIND y fijar el mapeo**.
- [ ] ⚠️ **Fijar por escrito que manda el combo `Cliente` (11598)**: sobrevive `Nombre del cliente` (`11596`, texto libre) en el mismo formulario.
- [x] ~~🔒🔴 **Regresar `Bind:Mode` a `ReadOnly`**~~ → **HECHO (20-ago, noche);** verificado: `/api/emision/estado` responde `ReadOnly`. Mañana en la sesión: `dotnet user-secrets set "Bind:Mode" "Write" --project src/Balam.Api` (y reiniciar el API) SOLO para emitir, y de vuelta a ReadOnly al cerrar.
- [x] ~~🔧 **Cablear los 4 campos nuevos**~~ → **HECHO (20-ago, noche).** El wizard prellena la partida completa desde el ticket (título ← Servicio/Producto, cantidad, tasa de IVA) y manda `vatRate` como **fracción**; el endpoint rechaza con 400 cualquier tasa > 1. **Verificado e2e contra `FAC-106` real:** detalle con `servicio/cantidad/iva/fechaVencimiento` poblados y dry-run (operación #19) con `VATRate=0.16`, `VAT=197.44`, sin `PriceListID`. Tests 74/74. El flujo de mañana es: abrir la solicitud → vista previa automática → encender Write → Confirmar.
- [x] ~~🔥 **Johann — preparar el caso de escritura sobre `FAC-106`**~~ → **LISTO (20-ago, noche).** El cliente ya se lee del combo `11598` (CEMEX detectado y con match único en el catálogo sincronizado) y la partida sale de los campos estructurados. Queda solo la **Confirmación real en la sesión** — el único paso que nadie puede ensayar sin escribir en producción.
- [ ] 🔥 **Johann — en la sesión: encender `Bind:Mode=Write` solo para emitir y regresarlo a ReadOnly antes de cerrar.**
- [ ] ⚠️🔥 **Johann — anunciar al abrir la sesión el destino de la cotización real** (cancelarla al terminar vs. cliente de prueba) y que la prefactura pertenece a una sesión posterior. **Pendiente arrastrado del 12-ago; sigue sin resolverse.**
- [ ] 🔴 **Validar las 17 categorías con Noé y Ara.** Pedro redujo el catálogo de servicios de **67 → 17** pero lo condicionó: *"tengo que verlas con Noé y Ara antes"*. **Es el momento de cerrar el hueco de conceptos/partidas** que bloquea B7 desde el [#62](REGISTRO.md).
- [ ] ☁️ **Azure — insistir; lleva 3 semanas sin avanzar.** El 11-ago Noé ofreció explorarlo en la sesión *"con una cuenta que estamos creando para este uso"*; **el 12-ago no se tocó** — Noé solo dijo *"eso lo resuelvo con Pedro"* ([REGISTRO #65](REGISTRO.md) §8). Ahora que Pedro es el responsable, es su pendiente directo. Requisitos ya comunicados el 28-jul: grupo de recursos + **Contributor** (app + BD + Key Vault), sin Global Admin. **Sin esto la transición automática no existe** y todo queda en disparo manual. **Movimientos:** 24-ago Noé condiciona Azure a que las pruebas funcionen y pide la app en un VPS ([#73](REGISTRO.md)); 25-ago Johann manda los requisitos exactos ([#74](REGISTRO.md)); 3-sep se piden los datos del tenant y Noé debía reenviarlos a Pedro ([#76](REGISTRO.md)); mientras, la plataforma vive en https://paputec.mx/balam/ desde el 3-sep ([#77](REGISTRO.md)).
- [x] ~~👤 **Confirmar quién es el responsable del proyecto.**~~ → **CONFIRMADO: Pedro.** Noé lo anunció el 12-ago: *"Erika ya no va a ser la PM de este proyecto. El seguimiento del proyecto lo va a traer Pedro."* ⚠️ **Nunca se comunicó por escrito** — el anuncio existe solo en la grabación. Ver [REGISTRO #65](REGISTRO.md) §1.
## 🆕 Abierto por la sesión del 12-ago ([REGISTRO #65](REGISTRO.md))
- [ ] 🔴 **Balam — definir la regla de EDICIÓN de cotizaciones.** Araceli las modifica seguido (*"siempre hay ajustes"*) y verificó en vivo que **BIND conserva el mismo número al editar**. La plataforma no implementa edición. Disyuntiva planteada por Noé y dejada a Balam: **historial de cotizaciones** (ticket nuevo o regenerar) vs. **modificar la misma**. Araceli se inclina por conservar historial. **Impacta el diseño de B7** — preguntarlo mañana.
- [ ] ⚠️ **Cancelaciones: la plataforma NO cancela, pero SÍ lleva el récord.** Acordado el 12-ago. Antes del timbrado se cancela libremente; después requiere acuerdo con el cliente por correo → cancelación en el ERP → despacho ante el SAT → **el cliente acepta en una liga del SAT**. Falta modelar el récord (*"esta factura se canceló y fue sustituida por esta"*).
- [ ] **Balam/Pedro — quitar el SLA a los tickets de cotización** para que no impacten métricas (acordado con Araceli en la sesión).
- [ ] 📼 **Pedro — enviar las grabaciones de BIND** (altas de proveedor/clientes, facturación y una tercera). Ofrecidas al cierre del 12-ago; **no consta que hayan llegado** — no aparecen en el buzón al 20-ago.
- [ ] 🗂️ **Balam — resolver la herramienta de seguimiento del proyecto.** El tablero de Jira para las actividades de Johann *"muy próximo iba a necesitar que sea de paga por el tipo de licencia"*; se barajaron **SharePoint** y **Google Drive**; Noé prefiere Jira pero falta permiso de admin para crear spaces. Mientras tanto sigue vigente lo del [#58](REGISTRO.md): **el Excel de horas ES el sistema de seguimiento**.
- [ ] 💰 **Definir a quién se le reportan horas, avances y facturación ahora.** Todo el circuito administrativo iba por **Erika**: cortes de horas, Excel de avance, recordatorios de factura ([#45](REGISTRO.md), [#51](REGISTRO.md), [#58](REGISTRO.md), [#59](REGISTRO.md)). Con el relevo del 12-ago **nadie dijo qué pasa con esa vía** — y el relevo ni siquiera se comunicó por escrito. **Preguntarlo mañana**: si el seguimiento es con Pedro, ¿los cortes de horas también, o siguen con Erika/Paola?
- [ ] 🔒 **Johann — avisar a Pedro antes de cada escritura** (cotización o prefactura), ofrecido en la sesión. Noé reafirmó el candado: *"no hacer más pruebas de lo que definiéramos, para evitar errores o cosas en producción"*.
## 🚨 Huecos de registro abiertos (detectados en el cruce del 20-ago)
- [x] ~~🚨 **Capturar la minuta de la sesión del 12-ago**~~ → **HECHO (20-ago):** transcript localizado y registrado en [REGISTRO #65](REGISTRO.md) (`../fuentes/2026-08-12 - Transcript - Sesion prueba de escritura Jira-BIND.txt`). Las cuatro preguntas quedaron respondidas: **no** se emitió cotización (error 500); **Azure no se tocó** y quedó con Pedro; **sí**, ahí Noé anunció a Pedro como responsable del seguimiento; y **el molde de Araceli nunca existió** — Pedro construyó un formulario nuevo en vivo.
- [ ] 📌 **Versionar `Cotizacion-BIND-campos-y-catalogos.xlsx`** — está en la raíz del repo sin trackear y ya es el insumo que Balam usó para rehacer el formulario de Jira ([#66](REGISTRO.md)).
- [ ] 📄 **Leer los procedimientos de carga de facturas** ya descargados en `../fuentes/` ([#46a](REGISTRO.md)): 5 portales — Cemex, Acuntia, **ATC Consolidados**, DYLO y **Victec**. ⚠️ **Dos clientes que el Discovery nunca mencionó** y Axie no aparece: el universo de destinos de envío **no está cerrado**.
- [ ] 🗺️ **Contrastar el diagrama del flujo de facturación de Jira** (`../fuentes/2026-07-22 - Correo - Flujo facturacion JIRA BALAM.png`, [#57a](REGISTRO.md)) contra el disparador acordado y lo observado por API.
- [ ] 🔎 **Identificar qué se firmó en Buk el 30-abr y el 5-may** — dos documentos anteriores al contrato de junio que la bitácora nunca registró ([REGISTRO #0](REGISTRO.md)).
- [ ] 📖 **Leer el hilo comercial de abril** ("Propuesta de servicios por hora - Balam", 23–28 abr) y completar [REGISTRO #0](REGISTRO.md): es donde se fijó la tarifa por hora.
## 🔥 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.**
- [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 #62](REGISTRO.md).
- [x] ~~🔥 **Araceli — levantar el TICKET DE EJEMPLO capturado "como debería ser"** (ruteado por Noé el 11-ago; la noche del 11 no existía)~~ → **Lo resolvió PEDRO el 20-ago con `FAC-106`**, tras rehacer el formulario de Jira contra el Excel de campos de BIND ([#66](REGISTRO.md), [#67](REGISTRO.md)). ⚠️ **No consta que Araceli haya capturado el molde** que Noé le pidió: el camino fue otro — Balam modificó el formulario y Pedro levantó el ticket. Los planes B y C (captura en vivo / `FAC-100`) quedan sin usar.
- [x] ~~🔥 **Balam/Noé — confirmar la sesión** (mié 12 o jue 13, 7:00 am)~~ → **CONFIRMADA por Noé (11-ago, 10:41 am): miércoles 12-ago, 7:00 am.** Además, **Azure se explora en la misma sesión** con una cuenta nueva creada para este uso. Ver [REGISTRO #63](REGISTRO.md).
- [ ] 🔴 **Conceptos/partidas — la vía quedó definida el 12-ago: DEPURAR EL CATÁLOGO, no agregar un campo.** Araceli mostró que el combo de servicios de BIND está sucio (`040` y `029` son el mismo servicio; entradas con nombres de personas, "monitor Samsung", periodos en el nombre) y se acordó dejar **solo opciones genéricas**, volver **combo** la descripción del ticket y **homologar los combos de Jira con los de BIND**. Pedro ejecutó la depuración: **67 → 17 categorías**, pendientes de validar **mañana** con Noé y Ara. Dato fiscal: horas de consultoría = unidad SAT **`E48`**. Ver [REGISTRO #65](REGISTRO.md) §5, [#67](REGISTRO.md).
- [ ] 🔴 **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).
- [ ] ⚠️🔥 **Johann — decidir el destino de la cotización de la prueba en BIND.** El ejercicio escribe una **cotización real en BIND producción**. Dos salidas: cancelarla al terminar o apuntarla a un cliente de prueba. **Decidir esta noche y anunciarlo al abrir la sesión del 12, antes de ejecutar.**
- [x] ~~🔴 **¿El comercial ya genera la cotización en BIND, o la genera la plataforma?**~~ → **RESUELTO en la sesión del 12-ago: la genera la PLATAFORMA.** Noé le preguntó a Araceli si le serviría que Jira hiciera la cotización y evitarle el primer paso → *"Sí, sí me serviría"* → *"vamos a hacer ese ejercicio, Johann"*. El paso comercial de cotizar a mano desaparece para **venta nueva**; la **recurrente** sigue fuera de alcance (comercial solo avisa el periodo y administración prefactura). Ver [REGISTRO #65](REGISTRO.md) §3.
- [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`.**
- [x] ~~🔥 **Johann — preparar el flujo contra el ticket de ejemplo**~~ → **CONSTRUIDO (11-ago, noche):** contrato `Quotes_NewQuote` obtenido del portal público, `CreateQuoteAsync` con guardas, `POST /api/emision/cotizacion` (dry-run/confirmación auditadas en `WriteOperations`), pantalla `solicitudes/:id/cotizar` y vínculo ticket→cotización. 73/73 pruebas. **Corre contra cualquier ticket FAC** — el molde solo mejora los datos. La escritura real sigue gateada a `Bind:Mode=Write`, que se enciende SOLO en la sesión. Ver [REGISTRO #64](REGISTRO.md).
- [ ] 🔥 **Johann — enviar a Noé/Pedro el script de la prueba** esta noche (`../planeacion/Script-cotizacion-sesion-2026-08-12.md`) — cumple la condición del 24-jul de compartirlo antes.
- [ ] 🔥 **Johann — en la sesión: encender `Bind:Mode=Write` solo para emitir y regresarlo a ReadOnly antes de cerrar.** ⚠️ La semántica fina de partidas (`Type`/`SelectedUnit`/VAT) se valida contra la primera respuesta real; si BIND rechaza, el error se lee en pantalla y se ajusta en vivo.
- [x] ~~**Balam/Noé — fecha para la prueba acompañada de escritura.**~~ → **Es la sesión confirmada del mié 12-ago, 7:00 am** (Noé, 11-ago). Formato aceptado desde el 7-ago; **no habrá `FACTEST`**. Ver [REGISTRO #63](REGISTRO.md).
- [ ] **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.
@@ -26,6 +189,15 @@ _Última actualización: 2026-08-10 (Balam definió el flujo Jira→BIND en el c
## 🔴 Johann (proveedor) — inmediato
- [x] ~~⚠️🔥 **Enviar la propuesta/adenda ajustada por la integración Jira→BIND**~~ → **Hecho (29-jul):** v1.3 enviada por WhatsApp (8:18 am) y por correo (3:56 pm) a Noé, CC Ara/Erika/Pedro. **APROBADA por Noé el 30-jul:** "De acuerdo con la propuesta actualizada". Alcance: 7–10 h ($4,200–$6,000 MXN + IVA); total primera etapa 119–146 h. Ver [REGISTRO #59](REGISTRO.md), [#60](REGISTRO.md).
- [x] ~~📊 **Enviar el Excel de horas de las etapas siguientes (2, 3)**~~ → **Hecho (29-jul, 8:18 am):** `Avance-Balam-2026-07-28.xlsx` por WhatsApp; 👍 de Erika. Ver [REGISTRO #59](REGISTRO.md).
- [x] ~~🔑 **Destrabar el acceso a la API de Jira (401 del 29-jul)**~~ → **Resuelto (30-jul):** Pedro confirmó el correo del apikey (`pedro.ayala@...`), regeneró el token y `GET /myself` respondió 200. Ver `../planeacion/Investigacion-API-Jira.md` (Bloque 0).
- [x] ~~🌱 **Seeder de datos de prueba (B9)**~~ → **Hecho (1-ago):** `DevDataSeeder` opt-in con datos ficticios (5 estados × MXN/USD × PUE/PPD). El desfase con lo reportado a Erika el 27-jul quedó saldado. Ver [REGISTRO #61](REGISTRO.md).
- [x] ~~🔑 **Copiar `propuesta/token-jira.txt` a esta máquina** y correr `../planeacion/pruebas-jira-lectura.sh`~~ → **Hecho (6-ago):** script corrido completo contra FAC (todo 200; auth OK con el token renovado) + sondeo dirigido de customfields. **Los IDs quedaron capturados y documentados** en `../planeacion/Investigacion-API-Jira.md` (sección 6-ago): sí existen "Monto sin IVA"/"Monto con IVA" pero casi nadie los llena (2/69); "Nombre del Cliente" y "Periodo de Incidencias" llegan como ADF. ⚠️ El token estaba suelto en `balam-plataforma/token-jira.txt` (repo del cliente, sin ignorar): se movió a `propuesta/` + user-secrets y se blindó el `.gitignore` del subrepo.
- [ ] 🧪 **Crear el sandbox Jira Cloud Free propio** (decidido 1-ago) para desarrollar escritura (transiciones/comentarios/creación) sin tocar FAC; la prueba en la instancia real sigue reservada a la sesión acompañada que autorizó Noé.
- [ ] ⏱️ **Capturar horas 28-jul→1-ago en el CSV** (propuesta preparada, validar montos) y **regenerar `Avance-Balam` desde script** — la copia en disco del 28-jul está desincronizada (re-guardado manual).
- [ ] 📅 **Demo del viernes 7-ago:** material listo — frontend (login→cartera→facturas→detalle+PDF→clientes), seeder demo como respaldo si BIND falla, scheduler en vivo.
- [x] ~~**Asistir al kickoff con Noé (mié 1-jul, 7:00 am)**~~ → **Hecho (1-jul).** Ver [REGISTRO #22](REGISTRO.md).
- [x] ~~📅 **Asistir a la sesión "Proceso actual de facturación y cobranza" (lun 6-jul, 7:00 am)**~~ → **Hecha (6-jul).** Proceso mapeado end-to-end; lista blanca eliminada; cotización BIND obligatoria. Ver [REGISTRO #27](REGISTRO.md).
- [x] ~~🎨 **Preparar y entregar el prototipo de la Etapa 0**~~ → **Entregado por correo (10-jul)** a Erika + Noé, CC Pedro, con PDF y versión navegable. Refleja facturación, envío, cobranza y reglas del Discovery. Ver [REGISTRO #40](REGISTRO.md).
@@ -47,7 +219,7 @@ _Última actualización: 2026-08-10 (Balam definió el flujo Jira→BIND en el c
- [ ] 🔑 **Dar seguimiento a si Pedro crea el usuario de solo lectura de BIND** que pidió Noé (mientras tanto Erika opera con el usuario de Arturo, sin bloqueo) — sigue sin resolverse una semana después (7-jul). Ver [REGISTRO #37](REGISTRO.md).
- [x] ~~🔧 **Decidir si probar endpoints de escritura (POST/PUT) de BIND**~~ → **Decidido (7-jul): NO todavía.** Contradice la instrucción de Noé de mantener solo-lectura mientras Pedro confirma el alcance del token; sin sandbox, el riesgo fiscal es real. Alternativa segura: catalogar operaciones de escritura desde el **portal de desarrolladores de BIND** (documentación), no probarlas en vivo. Ver [REGISTRO #36](REGISTRO.md).
- [x] ~~📧 **Responder el correo de Pedro**~~ → **Enviado (6-jul, ~3:28 pm):** acuse de recibo + usuario de Arturo documentado + arranque de trabajo con la API, sin comprometer entregas adicionales. Ver [REGISTRO #29](REGISTRO.md).
- [ ] 📧⚠️ **Responder el correo de Noé (importancia alta, 4:33 pm)** describiendo las **salvaguardas** ya operando: solo-lectura bloqueado en código, consultas acotadas, token en gestor de secretos fuera del repo, sin datos reales en documentos, escritura solo con autorización + candados de la propuesta. Borrador listo. **Confirmación informal ya dada por WhatsApp (6-jul, 5:01 pm) — falta la respuesta formal por correo para dejar constancia en el canal correcto.** Ver [REGISTRO #31](REGISTRO.md), [#33](REGISTRO.md).
- [x] ~~📧⚠️ **Responder el correo de Noé (importancia alta, 4:33 pm)** describiendo las salvaguardas ya operando~~ → **ESTABA HECHO DESDE EL 6-JUL, 4:46 PM** (13 minutos después del correo de Noé), a Noé y Pedro con Erika en copia. Cubre las cinco salvaguardas: solo-lectura bloqueado en código, consumo acotado, token en gestor de secretos fuera del repo, sin datos reales en la documentación, y escritura diferida. *(Corrección del 20-ago: este pendiente llevaba mes y medio abierto por error — el correo apareció al cruzar el buzón.)* Ver [REGISTRO #31a](REGISTRO.md).
- [ ] ⚠️ **No atar nada al token actual:** Pedro está revisando permisos y puede **reemplazarlo** por uno de un usuario nuevo de solo lectura — mantener el token como variable de entorno intercambiable (ya es así en el sandbox). Ver [REGISTRO #31](REGISTRO.md).
- [x] ~~🔑 **Validar la API de BIND con el token real**~~ → ✅ **Hecha (6-jul, adelantada un día):** 117 peticiones GET-only, Plan A del saldo CONFIRMADO, flujo MVP 100% sostenible en lectura, PDF por API. Hallazgo duro: **sin recurso de pagos/REP**. Documento: [`../bind-api-sandbox/VALIDACION-API.md`](../bind-api-sandbox/VALIDACION-API.md). Ver [REGISTRO #32](REGISTRO.md).
- [ ] 📨 **Escalar a Pedro las preguntas técnicas de la API** (juntas, esta semana): (1) ¿existe endpoint de **pagos individuales / complementos de pago (REP)** no documentado? — es el hueco más relevante para el flujo PPD, _y ganó caso de negocio el 7-jul: el despacho registra los pagos en BIND uno por uno a mano_; (2) **tabla de mapeo `CFDIUse`** interno → clave SAT; (3) shape del endpoint `/{id}/xml`; (4) ¿webhooks/eventos o solo polling?; (5) ⚠️ **qué token alimenta el Power BI** — el 7-jul Arturo mostró su Power BI conectado con **su** token, pero el kickoff acordó Ara=Power BI / Arturo=desarrollo; BIND emite 1 token por usuario, así que si ambos usos comparten el de Arturo, reemplazarlo por el usuario solo-lectura que planteó Noé rompería uno de los dos. Ver [REGISTRO #32](REGISTRO.md) §10, [#35](REGISTRO.md).
@@ -76,6 +248,9 @@ _Última actualización: 2026-08-10 (Balam definió el flujo Jira→BIND en el c
## 🟡 Balam
- [ ] ☁️⚠️ **Noé: otorgar el acceso a Azure** — grupo de recursos con permiso **Contributor** (app + BD + Key Vault); no requiere Global Admin ni cuenta nueva. Requisitos comunicados por Johann el 28-jul 4:53 pm; **Erika lo escaló a Noé** (5:23 pm). Bloquea ambiente, staging y CI/CD. **Movimiento (11-ago): Noé propone explorar/validar los accesos EN la sesión del 12-ago, con una cuenta que están creando para este uso** — llevar los requisitos a la mano. Ver [REGISTRO #59](REGISTRO.md), [#63](REGISTRO.md).
- [ ] 📅⚠️ **Erika/Arturo: avisar fecha de la sesión del flujo de facturación** tras la revisión interna (Ara de viaje desde el 28-jul). **Desbloquea B7 (capa de escritura).** Ver [REGISTRO #58](REGISTRO.md).
- [ ] 🧾 **Balam: decisiones de negocio de la integración Jira→BIND** — estatus disparador, request type a observar, fuente de monto y conceptos de la cotización. Sin esto no se escribe código productivo de la integración. Ver `../planeacion/Investigacion-API-Jira.md`.
- [x] ~~**Balam:** enviar el documento/contrato de firma~~ → **Hecho (25-jun):** contrato enviado vía Paola (RH); ajustado y **firmado el 26-jun**. Ver [REGISTRO #18](REGISTRO.md), [#19](REGISTRO.md).
- [ ] 🔑 **Balam: completar accesos de Etapa 1** — token BIND ✅; manual de marca ✅; invitación al repo GitHub recibida y acceso confirmado informalmente (falta validar escritura); Azure programado para semana del 21-jul. Ya no bloquea el desarrollo local, pero Azure sí condiciona despliegue/CI-CD.
- [x] ~~Erika (PM): pedir el plan de actividades~~ → **Recibido (30-jun).** Erika es la **intermediaria de todas las sesiones**, agendó el **kickoff (1-jul, 7am)** y monta el **tablero Kanban en Jira**. Ver [REGISTRO #21](REGISTRO.md).
+884 -11
View File
@@ -7,15 +7,32 @@
- **Johann** (proveedor) — `johann_antonio85@hotmail.com`
- **Noe Rocha** — CTO, Balam — `noe.rocha@balamtalentoestrategico.com`
- **Araceli Sanchez Jimenez** ("Ara") — Balam — `araceli.sanchez@balamtalentoestrategico.com`
- **Erika Chavez** — Project Manager, Balam — `erika.chavez@balamtalentoestrategico.com` · WhatsApp +52 1 81 2353 5803
- **Pedro Alberto Ayala Elizondo** — Desarrollador / contacto técnico, Balam — `pedro.ayala@balamtalentoestrategico.com`
- **Paola** — Recursos Humanos, Balam — coordinó la firma del contrato (WhatsApp)
- **Erika Chavez** — Project Manager, Balam — `erika.chavez@balamtalentoestrategico.com` · WhatsApp +52 1 81 2353 5803 · ⚠️ **PM del proyecto hasta el 12-ago-2026**, cuando Noé anunció su relevo ([#65](REGISTRO.md))
- **Pedro Alberto Ayala Elizondo** — Desarrollador / contacto técnico, Balam — `pedro.ayala@balamtalentoestrategico.com` · ⭐ **responsable del seguimiento del proyecto desde el 12-ago-2026** ([#65](REGISTRO.md))
- **Arturo Rosas Hernandez** — Gerencia administrativa / operación, Balam — `arturo.rosas@balamtalentoestrategico.com`
- **Paola de los Angeles Castro Martínez** — Recursos Humanos, Balam — `hr@balamtalentoestrategico.com` · coordinó la firma del contrato (WhatsApp)
**Periodo:** 30 abr 2026 → 22 jul 2026 (última actualización: sesión de reglas Etapa 1 — alta de clientes, fee y flujo Jira, 22-jul)
**Periodo:** 8 abr 2026 → 29 sep 2026 (última actualización: **29-sep** — ⭐ **Balam entrega login web a BIND con permisos de prefactura** y queda confirmado el modelo de dos endpoints: externo = API (remisión o CFDI timbrado), interno = UI (*Generar Venta → Prefactura*, petición `InvoiceDetails`) ([#83](REGISTRO.md)). Se reconstruyen desde el buzón las sesiones sin registro del 21-ago al 10-sep ([#71](REGISTRO.md)–[#82](REGISTRO.md)): homologación de estados, `false` = remisión, la sesión del incidente, publicación en paputec.mx, cotización en lote y la primera prefactura observada por DevTools. Anterior: **21-ago** — ⭐ **primera cotización real emitida en BIND desde un ticket** (folio 1026, [#69](REGISTRO.md)); `Type=2` y `VAT` como tasa, indocumentados. Anterior: **20-ago** — cruce completo del buzón de Outlook vía MCP ms365 + recuperación del transcript del **12-ago**. Se incorporan 10 comunicaciones que faltaban y se corrigen 2 errores. ⭐ **Cambio de gobernanza: Pedro Ayala releva a Erika en el seguimiento del proyecto desde el 12-ago.** Próxima sesión: **viernes 21-ago, 7:00 am** con Noé, Ara y Pedro sobre `FAC-106`)
**Orden:** cronológico (más antiguo arriba)
---
## 0 · Abr 8–28, 2026 · Correo · Prehistoria comercial — cómo se fijó la tarifa por hora
> ⚠️ **Entrada de inventario (añadida el 20-ago-2026).** El registro arrancaba el 30-abr con la solicitud de cotización; el buzón muestra que **la relación comercial se abrió cinco semanas antes**. Los cuerpos de estos correos **aún no se han leído** — se listan aquí para que dejen de estar fuera del registro. Es el hilo donde se pactó la tarifa por hora que sostiene todo lo demás.
| Fecha | Movimiento |
|---|---|
| 8-abr | Paola (RH) → Johann · "Johann Velazquez - Proyecto Balam" |
| 23-abr 10:18 am | **Noé abre el hilo "Propuesta de servicios por hora - Balam"** 📎 — *"En seguimiento a la llamada de días previos para colaborar con nosotros como proveedor de servicios y sobre tu interés en participar en procesos de consultoría en desarrollo, automatización e IA (agentes, automatización, SLM's)…"* |
| 23-abr | Araceli · "Sesión Balam" + dos respuestas al hilo 📎 · Johann responde dos veces |
| 24-abr | Paola 📎 · Johann responde 📎 |
| 27–28-abr | Paola cierra el hilo |
**Además, dos firmas en Buk sin explicar:** "Tu documento está listo" + código de firma el **30-abr** y otra vez el **5-may** — anteriores al contrato de servicios de junio ([#18](REGISTRO.md), [#19](REGISTRO.md)). Son dos documentos firmados que este registro nunca mencionó. **Acción:** identificar qué se firmó (¿NDA, convenio marco?) y si condiciona algo del contrato posterior.
---
## 1 · Apr 30, 2026 — 4:38 PM · Erika Chavez → Johann
> **Asunto:** Solicitud de cotización – PRD
@@ -100,12 +117,20 @@ Con estas respuestas puedo tener la propuesta lista en 24–48 horas. Si prefier
## 3a · May 6, 2026 — Araceli Sánchez → Johann · respuestas "en verde"
> **Asunto:** Re: Solicitud de cotización – PRD
> *(Fuente: lista del hilo de Outlook. Su contenido íntegro no consta en el `.eml` disponible — ver aviso.)*
> **Asunto:** RE: Solicitud de cotización – PRD · 3:42 PM · Para Noé, CC Johann
> ✅ **Evidencia recuperada el 20-ago-2026** del buzón (MCP ms365): `../fuentes/2026-05-06 - Correo - Araceli respuestas al cuestionario (hilo PRD).md`
*"Te respondo en verde. Gracias, Saludos."* Araceli responde los puntos que Noe dejó marcados **TBD Ara** el 4-may: **(1)** banco americano, **(2)** tax compliance para clientes en Texas, **(3)** tickets físicos en pagos con tarjeta, **(4)** guía de marca / assets.
> ⚠️ **Evidencia pendiente:** el texto exacto de las respuestas "en verde" **no está en el `.eml` disponible** (capturado el 3-jun, donde esos 4 puntos aún figuran resaltados en amarillo como "TBD Ara", sin responder). **Acción:** recuperar el correo verde original para transcribir las respuestas aquí. El Resumen ejecutivo infiere banco = **IBC Bank Texas** y tax = **estándar** a partir de la llamada del 19-may; falta confirmar la atribución a este correo.
✅ **RESUELTO (20-ago-2026).** El correo apareció íntegro en el buzón y las respuestas quedaron transcritas en `../fuentes/2026-05-06 - Correo - Araceli respuestas al cuestionario (hilo PRD).md`. Lo que confirma:
- ⭐ **Banco americano = "IBC BANK, es un banco Texano e ingresamos vía web."** — deja de ser inferencia de la llamada del 19-may: **está por escrito desde el 6-may**.
- ⭐ **Tax = "Por el momento TAX estándard"** — ídem.
- **PAC:** *"Integrado hasta donde se en el mismo sistema de Bind, confirmo está contenido dentro del precio del ERP."*
- **Pagos con tarjeta:** no tienen terminal; lo que buscan es **mandar un link de pago** y/o **domiciliación** ("pólizas, etc"). Matiza el punto 3 de arriba: no era un problema de registro manual.
- **Manual de marca:** sí existe y lo pueden compartir (se entregó el 1-jul, [#23](REGISTRO.md)).
> ⚠️ **Único residuo:** el cuerpo se recuperó como **texto plano**, así que el resaltado verde no es recuperable y no puede distinguirse tipográficamente qué contestó Araceli y qué Noé. El contenido, que era lo que faltaba, sí está completo.
---
@@ -620,6 +645,26 @@ Noé responde al hilo de la entrega del token con dos instrucciones:
---
## 31a · Jul 6, 2026 — 4:46 PM · Correo · Johann → Noé, Pedro (CC Erika) · respuesta formal con las salvaguardas ✅
> Evidencia: `../fuentes/2026-07-06 - Correo - Johann respuesta salvaguardas token.md`. **Recuperado del buzón el 20-ago-2026.**
> 🔧 **Corrección de registro.** Este correo faltaba. [#33](REGISTRO.md) y [PENDIENTES.md](PENDIENTES.md) daban la respuesta formal a Noé por **no enviada** ("borrador listo… falta la respuesta formal por correo"), y así llevaba **mes y medio**. Se envió el mismo 6-jul, **13 minutos después** del correo de Noé de las 4:33 pm ([#31](REGISTRO.md)).
Johann contesta punto por punto las salvaguardas que ya estaban operando:
- **Solo lectura, bloqueado en código:** *"el cliente que uso bloquea desde el código cualquier operación de escritura, así que no hay forma de modificar datos"*.
- **Consumo acotado:** pocas consultas, resultados pequeños, *"muy por debajo del límite diario de BIND"*.
- **Token fuera del repo:** *"no queda en el repositorio ni en registros, lo guardo en un gestor de secretos"*.
- **Sin datos reales en documentación:** solo estructura de la API (nombres de campos y tipos), *"sin información real de clientes o facturas"*.
- **Escritura diferida** y disposición inmediata a cambiar de token: *"si Pedro lo crea, de mi lado cambiar el token es inmediato y no afecta el avance"*.
> **Lectura estratégica:**
> - El pendiente 📧⚠️ más viejo del proyecto **nunca existió**: estaba cumplido desde el día uno. Lo que sí sigue abierto es la contraparte de Balam — que Pedro decida si crea el usuario de solo lectura ([#37](REGISTRO.md)).
> - Confirma el patrón de canal: la instrucción de riesgo de Noé se respondió por correo (evidencia formal) **y** por WhatsApp vía Erika ([#33](REGISTRO.md)).
---
## 32 · Jul 6, 2026 — tarde · Validación técnica de la API de BIND con la cuenta real ⭐ ✅
> Actividad del plan "Validación técnica de la API de BIND" (mar 7 – mié 8) — **ejecutada un día antes**, el mismo día en que llegó el token. Evidencia completa: [`../bind-api-sandbox/VALIDACION-API.md`](../bind-api-sandbox/VALIDACION-API.md) (117 peticiones, exclusivamente GET, solo estructura — cero datos reales persistidos).
@@ -649,7 +694,7 @@ Noé responde al hilo de la entrega del token con dos instrucciones:
**Resumen:** Erika transmite por WhatsApp, en versión breve, la instrucción de Noé sobre salvaguardas del token ([#31](#31--jul-6-2026--433-pm--correo--noé--johann-pedro-cc-erika--salvaguardas-sobre-el-token-)): *"referente a lo del token que se te compartió, para evitar complicaciones operativas."* Johann confirma: *"por el momento estaré haciendo operaciones solo de lectura."* Erika agradece el entendimiento.
**Acción derivada:** confirmación informal, no sustituye la respuesta formal por correo a Noé (borrador listo, ver [PENDIENTES.md](PENDIENTES.md)) — conviene enviarla igual para dejar constancia en el canal formal, tal como pidió Noé.
**Acción derivada:** confirmación informal, no sustituye la respuesta formal por correo a Noé. ✅ **La respuesta formal SÍ se envió**, el mismo 6-jul a las **4:46 pm** — ver [#31a](REGISTRO.md). *(Corrección del 20-ago: este registro y PENDIENTES la daban por pendiente con "borrador listo" desde hace mes y medio.)*
> **Lectura estratégica:**
> - Erika actúa como puente entre la instrucción formal de Noé (correo, 4:33 pm) y Johann — coherente con su rol de intermediaria única.
@@ -769,6 +814,18 @@ Balam responde que lo revisará internamente y compartirá comentarios. La retro
---
## 40a · Jul 10, 2026 · Correo · Reacción de Balam al prototipo: acuse de Erika y Noé fuera de oficina
> Añadido el 20-ago-2026 desde el buzón. Complementa [#40](REGISTRO.md), que registraba el envío pero no la respuesta.
- **2:13 pm — Erika → Johann, Noé (CC Pedro):** *"Buenas tardes Johan. Confirmo de recibido. Saludos!"* — acuse seco, sin comentarios de fondo.
- **12:14 pm — Noé: respuesta automática de ausencia.**
> **Lectura estratégica:**
> - **Explica el silencio sobre el entregable.** El prototipo se envió a Erika y Noé; Noé estaba fuera de oficina ese día, y la validación real no ocurrió hasta el **24-jul** ([#55](REGISTRO.md)) — dos semanas después. El entregable de la Etapa 0 estuvo dos semanas sin lectura del CTO, y el registro no lo hacía visible.
---
## 41 · Jul 13, 2026 · WhatsApp Erika ↔ Johann · continuidad de Etapa 1, sesión con Arturo y factura de 30 h
> Evidencia: `../fuentes/2026-07-13 a 2026-07-15 - WhatsApp - Seguimiento prototipo sesiones accesos.md`.
@@ -846,6 +903,29 @@ Erika solicita un corte del avance de la Etapa 1. Johann se compromete a prepara
---
## 46a · Jul 16, 2026 — 1:01 PM · Correo · Erika → Johann · procedimientos de carga de facturas (5 portales) 📎
> **Adjunto recuperado y guardado el 20-ago-2026:** `../fuentes/2026-07-16 - Correo - Procedimientos de cargas de facturas-clientes.zip` (1.7 MB). Cierra el pendiente *"conservar correo/adjuntos como evidencia"* de [#46](REGISTRO.md).
**Asunto:** Procedimiento de cargas de facturas-Proyecto integración Balam
El ZIP trae **16 archivos en 5 carpetas**, un procedimiento por portal de cliente, cada uno en `.docx` + `.pptx` (guía con capturas):
| # | Cliente | Archivos |
|---|---|---|
| 1 | **Cemex** | docx + pptx (912 KB — el más extenso) |
| 2 | **Acuntia** | docx + pptx |
| 3 | **ATC Consolidados** | docx + pptx |
| 4 | **DYLO** | docx + pptx |
| 5 | **Victec** | docx + pptx |
> **Lectura estratégica:**
> - ⭐ **Dos clientes que el Discovery nunca mencionó: `ATC Consolidados` y `Victec`.** Las particularidades de envío levantadas el 6-jul ([#27](REGISTRO.md)) hablaban de Axie, CEMEX, Dilo y Acuntia. Aquí Axie no aparece, "Dilo" se escribe **DYLO**, y salen dos portales nuevos. **El universo de destinos no está cerrado.**
> - **No son "correos con particularidades", son portales de carga.** El Discovery los trató como variantes del envío por correo; el material muestra procedimientos de **subida a portal del cliente**, que es otro tipo de trabajo — y otro alcance si algún día se automatiza.
> - **Sigue pendiente leerlos** y contrastarlos contra el prototipo y el MVP.
---
## 47 · Jul 19, 2026 · Gestión interna · demo del 18-jul no realizada; entorno local montado en la Mac
La **demo semanal planeada para el viernes 18-jul no ocurrió** (confirmado por Johann el 19-jul). Causa señalada: la validación del prototipo de la Etapa 0 sigue pendiente (quedó para el jue 23-jul) y eso ha retrasado el avance de la etapa. Los días 17–18 jul no registran trabajo en `../planeacion/Seguimiento-horas.csv`. La próxima demo queda apuntada al **viernes 24-jul** (cierre de Etapa 1), donde también conviene comunicar que la capa de escritura se entrega en dry-run (ver nota en `../planeacion/Agenda-semana-2026-07-20.md`).
@@ -1067,6 +1147,16 @@ Cierre de la sesión (~0.5 h real). **B6 conforme al plan:** `ITenantOwned` en l
---
## 57a · Jul 22, 2026 — 2:24 PM · Correo · Erika → Johann · flujo de facturación de Jira (diagrama) 📎
> **Adjunto recuperado y guardado el 20-ago-2026:** `../fuentes/2026-07-22 - Correo - Flujo facturacion JIRA BALAM.png`. Cierra el pendiente *"conservar el correo del flujo de facturación de Jira como evidencia/insumo"* de [#57](REGISTRO.md).
**Asunto:** Flujo de facturación JIRA — es el envío que Erika anunció por WhatsApp ese mismo día ([#57](REGISTRO.md), 2:26 pm: *"ya envié por correo el flujo de facturación de Jira"*).
Diagrama del flujo tal como Balam lo tenía documentado **antes** de las definiciones del 30-jul–7-ago ([#62](REGISTRO.md)). **Acción:** contrastarlo contra el disparador acordado (`En proceso de facturación`) y contra lo observado por API, para detectar dónde el diagrama oficial y la instancia real difieren.
---
## 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).
@@ -1085,7 +1175,7 @@ Johann envió el **avance por actividad en texto** (8:12 am), con 👍 de Erika
**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).
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). **Erika pidió a Johann contexto de qué se requiere específicamente de Azure (11:19 am)** — respondido esa tarde, ver [#59](REGISTRO.md). *(Corregido con la captura del hilo: la pregunta la hizo Erika, no Johann.)*
**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.
@@ -1103,7 +1193,91 @@ Johann envió el **avance por actividad en texto** (8:12 am), con 👍 de Erika
---
## 59 · Jul 30 – Ago 10, 2026 · Correo · Balam define el flujo Jira→BIND: disparador, campos y prueba acompañada ⭐🔥
## 59 · Jul 28–29, 2026 · WhatsApp Erika ↔ Johann · requisitos Azure escalados a Noé, propuesta v1.3 + Excel de etapas entregados, seguimiento API Jira ⭐
> Continuación de [#58](REGISTRO.md). Registrado a partir de capturas de pantalla del hilo (3 imágenes). Cierra los dos pendientes 🔥 de Johann que dejó #58.
**28-jul, tarde — requisitos de Azure definidos y escalados:** Erika había pedido contexto de qué se requiere específicamente de Azure (11:19 am, ver [#58](REGISTRO.md)). **Johann respondió (4:53 pm): "un grupo de recursos con permiso de Contributor para desplegar la aplicación, la base de datos y el Key Vault de secretos. No requiere Global Admin ni cuenta nueva. Con eso puedo montar el ambiente y el CI/CD."** → Erika (5:23 pm): *"okey de acuerdo, deja lo solicito con el inge porque desconozco si pedro puede apoyarme con ese permiso que se requiere de azure"* — es decir, **el acceso a Azure quedó escalado a Noé**, no a Pedro.
**28-jul, 5:38 pm — status check de Erika:** *"y ahorita estas detenido por algo o en que estas trabajando?"* → Johann (5:41 pm): **"Mientras tenemos la junta para ver el flujo de facturación, estoy trabajando en la integración con Jira, hoy en la noche podré compartirte el Excel de las actividades que llevamos más las de las siguientes etapas."** Erika: "De acuerdo" (5:42 pm). Buen manejo: la espera de B7 no se lee como bloqueo total porque el tiempo se redirige a la integración Jira.
**29-jul, 8:18–8:19 am — entrega doble (cierra los dos pendientes 🔥 de [#58](REGISTRO.md)):** Johann envió por WhatsApp **`Propuesta-Balam.pdf` (v1.3, 21 págs)** y **`Avance-Balam-2026-07-28.xlsx`** con el mensaje *"Te comparto el excel y la propuesta con lo de Jira, cualquier cosa lo revisamos"*. Ambos archivos con 👍 de Erika; *"Buen día johan / Lo reviso gracias"* (9:35 am).
**29-jul, 11:09 am — Erika pide formalizar por correo:** *"puedes compartirla en la cadena de correo donde adjuntaste la ultima version con los cambios que te dijo el inge noé pls para que la tengan todos"* (adjuntó captura del correo de la v1.2) → Johann: "Sin problema" (11:18 am) → **correo enviado a las 3:56 pm** (ver [#60](REGISTRO.md)).
**29-jul, tarde — seguimiento a la API de Jira:** Erika (1:27 pm): *"Johan mande un correo para ver si nos puedes apoyar con tu retroalimentacion al respecto de la API de jira, si pudiste ingresar o se necesita algo adicional va"* → Johann (4:59 pm): **"Listo, también ya lo respondí, estaba haciendo unas pruebas extra para asegurarme de que no estuviese probando mal el token"** (era la etapa del 401, ver `../planeacion/Investigacion-API-Jira.md`). Erika: *"Lo valido con Pedro para ajustar"* (5:00 pm). **Johann pidió saber el correo desde el cual se generó el apikey** (5:00 pm) → Erika: "Sí claro" (5:18 pm). *(Resuelto el 30-jul: Pedro confirmó `pedro.ayala@balamtalentoestrategico.com`, regeneró el token y el `GET /myself` respondió 200 — Bloque 0 de la investigación.)*
**Lecturas clave:**
> - **Los dos pendientes 🔥 de Johann quedaron cerrados el 29-jul por la mañana:** propuesta ajustada por Jira (arrastrada desde el 22-jul) y Excel de horas de etapas siguientes. El hilo comercial más viejo se destrabó.
> - **Azure ya no depende de Pedro:** Erika lo escaló a Noé porque duda que Pedro pueda otorgar el permiso Contributor. Los requisitos exactos ya están comunicados y por escrito (grupo de recursos + Contributor + Key Vault, sin Global Admin).
> - **Erika opera como PM activa:** status check directo ("¿estás detenido?"), pide formalizar por correo lo que llega por WhatsApp, y da seguimiento al bloqueo del token Jira con Pedro. El patrón WhatsApp→correo confirma que **el correo es el canal de evidencia** para todo lo comercial.
## 59a · Jul 29, 2026 · Correo · Erika ↔ Johann · seguimiento a la API de Jira en el hilo de Pedro
> Añadido el 20-ago-2026 desde el buzón. [#59](REGISTRO.md) registró este intercambio **como WhatsApp**; los correos existen y son el canal donde realmente se documentó.
- **1:25 pm — Erika → Johann** (hilo *"Seguimiento: medición de consumo API de Jira y accesos"*): pide retroalimentación sobre el acceso a la API de Jira.
- **4:58 pm — Johann → Erika:** responde en el hilo. Es el *"Listo, también ya lo respondí"* que Erika acusa por WhatsApp a las 4:59 pm ([#59](REGISTRO.md)).
> **Lectura estratégica:**
> - Refuerza el patrón ya asentado: **WhatsApp coordina, el correo documenta.** Al registrar solo el lado de WhatsApp, el registro perdía el rastro del canal formal — justo el que Balam pide usar.
---
## 60 · Jul 29–30, 2026 · Correo · Johann → Noé (CC Ara, Erika, Pedro) · propuesta v1.3 (Jira→BIND) enviada y APROBADA por el CTO ⭐
> Cadena de correo sobre el mismo hilo donde se envió la v1.2 (2-jul). Registrado a partir de captura de Outlook.
**29-jul, 3:56 pm — envío de la v1.3:** Johann → Noé, CC Araceli, Erika y Pedro, con `Propuesta-Balam.pdf` (3 MB) adjunto. Puntos del cuerpo:
- **Incorpora al alcance la integración Jira → BIND:** los tickets de la bandeja de Facturación generan automáticamente su cotización en BIND, con trazabilidad ticket → cotización → factura.
- **Ajuste: 7 a 10 h ($4,200 – $6,000 MXN + IVA)**, en línea con la estimación de ~10 h comunicada el 21-jul ([#51](REGISTRO.md)).
- **Total primera etapa: 119 a 146 h ($71,400 – $87,600 MXN + IVA)**, 6 a 8 semanas a media dedicación.
- **El control fiscal no cambia:** la integración crea cotizaciones, nunca facturas. Se conserva la cadena cotización → prefactura → aprobación humana → timbrado por el PAC de BIND. Tarifa, facturación semanal y garantía siguen igual.
**30-jul, 4:51 pm — ✅ APROBACIÓN de Noé (CTO):** *"De acuerdo con la propuesta actualizada para seguir avanzando con la integración de JIRA."*
**Lecturas clave:**
> - **La integración Jira→BIND queda formalmente dentro del alcance, aprobada por el CTO y por correo** — el canal de evidencia correcto. Las 7–10 h del ajuste son ahora horas contratadas.
> - Con esto, el alcance de la primera etapa queda cerrado en **119–146 h**. La aprobación llega un día después del envío: sin fricción comercial.
> - La aprobación habilita continuar la parte técnica de la integración ([`../planeacion/Investigacion-API-Jira.md`](../planeacion/Investigacion-API-Jira.md)), aunque **el código productivo sigue bloqueado por decisiones de negocio** (estatus disparador, request type, fuente de monto/conceptos) y por la aprobación explícita de cada prueba de escritura.
**Pendientes que dejan #59–#60:**
- [x] ~~Johann — enviar la propuesta/adenda ajustada por Jira→BIND~~ → **Hecho (29-jul) y APROBADA por Noé (30-jul).**
- [x] ~~Johann — enviar el Excel de horas de las etapas siguientes~~ → **Hecho (29-jul):** `Avance-Balam-2026-07-28.xlsx`.
- [x] ~~Johann/Pedro — destrabar el acceso a la API de Jira (401)~~ → **Resuelto (30-jul):** correo del apikey confirmado, token regenerado, `GET /myself` → 200.
- [ ] Balam/Noé — **otorgar el acceso a Azure** (grupo de recursos con Contributor; escalado por Erika el 28-jul 5:23 pm). Bloquea ambiente, staging y CI/CD.
- [ ] Balam/Erika — **avisar fecha de la sesión del flujo de facturación** tras su revisión interna. Desbloquea B7.
- [ ] Balam — **decisiones de negocio de la integración Jira→BIND:** estatus disparador, request type a observar, fuente de monto y conceptos de la cotización.
---
## 61 · Ago 1, 2026 · Desarrollo · Recuperación del plan: E1 técnica cerrada (B5/B6/B8/B9), frontend de lectura de E2 completo y cliente Jira ⭐
> Sesión de trabajo del sábado 1-ago para poner el proyecto donde el plan del 28-jul dice que debería estar, y adelantar todo lo que no depende de Balam. 9 commits nuevos en `pedro-balam-itsm/BALAM` (pusheados junto con los 7 que faltaban de B0–B6 — **el repo del cliente ya refleja todo el trabajo**).
**Entorno (bloqueos propios resueltos):** el backend no compilaba en esta máquina (faltaba apuntar al SDK .NET 10 de `~/.dotnet`); Docker apagado. Resuelto y documentado en el CLAUDE.md del repo. `bind_token_api.txt` borrado del disco (el token vive solo en user-secrets).
**Etapa 1 — deuda técnica cerrada (lo no bloqueado):**
- **B9 · `DevDataSeeder`** ✅ — datos demo 100% ficticios opt-in (`Seed:DevData=true`): 5 clientes (con caso de dedup), 11 facturas cubriendo los 5 estados × MXN/USD × PUE/PPD, 3 cotizaciones y 2 vínculos con ticket ficticio. Idempotente, fechas relativas a "hoy" MX. **Cierra el entregable 8 y el pendiente "se reportó como hecho el 27-jul".** Runbook en CLAUDE.md.
- **B6 · verificación RLS real** ✅ — rol `balam_app` (NOSUPERUSER/NOBYPASSRLS) creado y con ownership de las tablas; la connection string de desarrollo ya lo usa. Verificado fail-closed por psql: sin GUC 0 filas, INSERT rechazado. Script de init para volúmenes nuevos en `backend/db/init/`.
- **B5 · scheduler** ✅ — `BackgroundService` + `PeriodicTimer` cada 15 min (configurable; ≤0 lo apaga) con guardas de solape y de cuota (<10% restante salta el ciclo). **Primer ciclo real contra BIND detectó el movimiento desde el 27-jul: 3 facturas nuevas, 46 pagos, 2 cotizaciones nuevas.**
- **B8 · `Balam.Api.Tests`** ✅ — 25 pruebas de integración con el API completo contra Postgres real (`balam_test`, recreada por corrida): matriz rol→policy (401/403/200), login sin filtración de correos, cartera multimoneda contra la fixture demo, paginación/filtros, PDF demo sin tocar BIND. **Total de la solución: 61/61 pruebas verdes.**
**Etapa 2 — recuperadas las filas vencidas (29-31 jul) y adelantadas las de la semana próxima (solo lectura, nada bloqueado):** andamiaje Angular (sesión JWT en señales, interceptor, guards por rol espejo de las policies, login) · shell con identidad de marca y estado del sync · cartera por moneda **sin total combinado** · listado de facturas con búsqueda/filtros/paginación/orden del lado del servidor (el API se extendió para ello) · detalle con desglose y **descarga de PDF vía proxy** (el token de BIND nunca llega al navegador) · catálogo de clientes con dedup visible y **export CSV** (facturas y clientes). Verificado end-to-end con login real por el proxy de desarrollo. **Fuera de alcance por bloqueo:** creación de cotizaciones y conversión (flujo sin validar por Balam), XML (shape sin confirmar con Pedro), tipo de cambio DOF (diferido: la captura al facturar depende de B7).
**Integración Jira — todo lo posible sin Balam:** proyecto `Balam.Integrations.Jira` espejo del cliente BIND (Basic auth email:token, `search/jql` con `nextPageToken` — el clásico da 410—, changelog, transiciones con `expand=transitions.fields`, servicedeskapi, rate limit leído de headers `X-RateLimit-*`), con los tres modos ReadOnly/DryRun/Write y **read-only por default** (FAC es producción); 10 pruebas con `FakeHandler`. Los modelos persisten el **id numérico inmutable** (la llave FAC-nnn cambia con un move — Bloque 5). Registro condicional en el API (+`/api/jira/status`). ⚠️ **Las pruebas de lectura contra FAC real quedaron bloqueadas EN ESTA MÁQUINA: `propuesta/token-jira.txt` no existe aquí (quedó en la otra computadora, gitignoreado).** Script listo para correrlas en un comando: `planeacion/pruebas-jira-lectura.sh`.
**Pendientes que deja la sesión:**
- [ ] Johann — **copiar `propuesta/token-jira.txt` a esta máquina** y correr `planeacion/pruebas-jira-lectura.sh` (bloques 2/3/4/5 de la investigación, solo lectura).
- [ ] Johann — **crear el sandbox Jira Cloud Free propio** (decidido 1-ago) para desarrollar transiciones/comentarios/creación sin tocar FAC.
- [ ] Johann — **capturar las horas del 28-jul→1-ago** en el CSV (propuesta preparada, pendiente de validación) y regenerar el Excel de avance desde el script (la copia en disco está desincronizada — re-guardado manual del 28-jul).
- [ ] Johann — demo del viernes 7-ago: ya hay material (frontend + seeder demo + scheduler).
---
## 62 · Jul 30 – Ago 10, 2026 · Correo · Balam define el flujo Jira→BIND: disparador, campos y prueba acompañada ⭐🔥
> ⚠️ Nota de merge (11-ago): esta entrada se escribió en la otra máquina como "#59" sin conocer las #59–#61 locales; se renumeró a #62 al integrar ambas líneas de trabajo.
> 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)).
@@ -1116,7 +1290,7 @@ Johann envió el **avance por actividad en texto** (8:12 am), con 👍 de Erika
| 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. |
| 7-ago 11:34 am | Noé reenvía a Johann y **contesta la cuarta**: prueba acompañada. *(Corregido el 20-ago: el registro decía 5:34 pm — se leyó como local la hora UTC 17:34.)* |
**Las 4 definiciones, cerradas:**
@@ -1184,8 +1358,707 @@ Johann envió el **avance por actividad en texto** (8:12 am), con 👍 de Erika
---
## 63 · Ago 11, 2026 · Correo · Noé confirma la sesión (mié 12-ago, 7:00 am) y rutea el ticket de ejemplo a ARACELI; Azure entra a la agenda 🔥
> Respuesta de Noé (11-ago, 10:41 am) al correo del 10-ago ([#62](REGISTRO.md)). Registrado desde captura de Outlook. Para: Johann y Araceli; CC Pedro. **Arturo y Erika siguen fuera del hilo** (la nota del borrador de agregarlos al CC no se aplicó en el envío).
**Lo que confirma:**
- ✅ **Sesión: miércoles 12-ago, 7:00 am** (tomó la primera de las dos fechas propuestas).
- 🔀 **El ticket de ejemplo queda asignado a ARACELI** (*"@Araceli Sanchez Jimenez por favor puedes generar el ticket en JIRA"*) — no a Arturo, como preveía la apuesta de PENDIENTES. Consistente con [#62]: Araceli es la aprobadora del workflow; el molde lo captura quien aprueba.
- ☁️ **Azure entra a la agenda de la sesión:** *"De los permisos de Azure mañana lo podemos explorar para validarte los accesos con una cuenta que estamos creando para este uso"*. Primer movimiento desde el escalamiento del 28-jul ([#59](REGISTRO.md)) — y llega como **cuenta nueva creada para este uso**, no como permiso sobre una cuenta de Johann.
**Verificación por API, noche del 11-ago** (script de lectura, todo 200): **el molde aún NO existe** — el sitio sigue en 69 tickets, 0 actualizados en la ventana. Y `FAC-98` (*"Baja Jorge Herrera L2"*, nacido de Automation, sin un solo campo estructurado) **lleva desde el 24-jul parado en `En proceso de facturación`** — 17 días en el estatus "que dura minutos": los tickets de Automation también rompen la premisa de duración, y es el exhibit vivo del supuesto asentado el 10-ago.
**Lecturas clave:**
> - **La latencia fue de un día, no de ocho.** La táctica del molde + fechas cerradas funcionó: Noé contestó en <1 día hábil confirmando todo.
> - **Riesgo operativo para las 7:00 am: el molde no existe la noche anterior.** Plan B: que Araceli lo capture **en vivo al abrir la sesión** — convierte el riesgo en el mejor momento de la demo (captura → detección → wizard con sus datos, y siendo ella quien captura, el molde queda con la autoridad de la aprobadora). Plan C: correr sobre `FAC-100` (único con `Monto sin IVA` poblado; de paso pone ACUNTIA en la mesa).
> - **La sesión ganó una segunda agenda (Azure).** Llevar a la mano los requisitos ya comunicados el 28-jul: grupo de recursos + **Contributor** (app + BD + Key Vault), sin Global Admin — para validar en vivo que la cuenta nueva alcanza.
**Pendientes que deja el correo:**
- [x] ~~Balam/Noé — confirmar la sesión~~ → **CONFIRMADA: mié 12-ago, 7:00 am.**
- [ ] 🔥 Araceli — **crear el ticket de ejemplo** (pedido por Noé el 11-ago; no existe la noche del 11).
- [ ] 🔥 Johann — **decidir y anunciar al abrir la sesión** el destino de la cotización real (cancelar al terminar vs. cliente de prueba) y que la prefactura pertenece a la 2.ª sesión.
- [ ] ☁️ Johann — llevar los requisitos de Azure para validar la cuenta nueva en la sesión.
---
## 64 · Ago 11, 2026 — noche · Desarrollo · Capa de escritura mínima: ticket FAC → cotización BIND real, con dry-run y candados (lista para la sesión del 12) ⭐
> Sesión nocturna previa a la prueba acompañada. Objetivo pedido: llegar con un ejemplo real de **"leer de Jira y escribir en BIND"**, no con simulación. 2 commits en `balam-plataforma` (locales, sin push).
**El hueco que había:** el wizard de emisión (6-ago) simula la mitad *cotización → prefactura* y arranca desde una cotización BIND EXISTENTE; el ejercicio del 12 es la otra mitad, *ticket → cotización nueva*. Además el `BindClient` bloqueaba POST por diseño y el shape de creación nunca se había documentado (la doc "detallada tras login" seguía pendiente con Pedro).
**El contrato se consiguió sin login:** el data-plane del portal de developers (APIM) responde anónimo. Operación **`Quotes_NewQuote` = `POST /api/Quotes`**, body con `ClientProspect{isNew, Client{ClientId, ClientName}}`, `Items[{Title, Unit, Price, Qty, VAT, IEPS, …}]`, `CurrencyID/LocationID/VATRate/Comments`; responde el **GUID de la cotización creada**. También existen `Quotes_UpdateQuote` y `Quotes_DeleteQuote` (la reversa por API existe, aunque la cancelación de la prueba se hará desde la pantalla de BIND). ⚠️ El SDK oficial de GitHub (2018) es ANTERIOR al recurso Quotes — no sirve de referencia. ⚠️ Semántica fina de partidas (`Type`, `SelectedUnit`, VAT ¿monto o tasa?) sin documentar: se valida contra la primera respuesta real, en la sesión.
**Lo construido (73/73 pruebas verdes + smoke test de arranque):**
- `BindClient.CreateQuoteAsync` — hereda las guardas del núcleo: en ReadOnly lanza, en DryRun loguea sin enviar, solo `Bind:Mode=Write` envía. 3 pruebas nuevas con `FakeHandler` (una por modo, la de Write valida el body PascalCase con `isNew` en minúsculas, sic del portal).
- `POST /api/emision/cotizacion` (modo `DryRun`/`Confirmed`) — valida contra el catálogo sincronizado (el cliente DEBE existir en BIND), persiste **cada intento** en `WriteOperations` (ADR-001), y en Confirmed: crea, **verifica por lectura** (folio/total), refleja la Quote en nuestra BD y guarda el vínculo **ticket → cotización** en `QuoteInvoiceLink` (migración: `InvoiceId` ahora opcional — el vínculo nace en la emisión y se completa al facturar, ADR-003). Policy `PuedeOperarEscritura` (Finanzas/Administración). El 409 cuando el modo no es Write es el candado visible.
- Pantalla **`solicitudes/:id/cotizar`** ("Generar cotización en BIND", botón nuevo en el detalle) — prellena cliente/moneda/monto/concepto desde el ticket, exige confirmar el cliente a mano (Jira no trae RFC), y aplica ADR-001 en la UI: **dry-run con el body exacto del wire** antes de habilitar Emitir; cualquier edición lo invalida; Emitir pide confirmación explícita con cliente y total.
- Doc para Balam: `../planeacion/Script-cotizacion-sesion-2026-08-12.md` — el "script compartido antes" que pidió Noé el 24-jul (operación única, body de ejemplo, candados, reversa, qué NO hace). **Enviarlo esta noche.**
**Guion de la sesión (7:00 am), afinado en la madrugada del 12 para la coreografía de pantalla compartida** (ellos capturan → Johann recarga y se ve llegar → un clic → confirmación → cotización): (1) anunciar que el ejercicio crea UNA cotización real y su destino; (2) molde de Araceli — si no existe, que lo capture EN VIVO compartiendo su pantalla; (3) Johann comparte la suya: **⟳ Actualizar** en la bandeja (nuevo: salta el caché de 60 s — sin esto el ticket recién capturado no aparecía) → el ticket se ve llegar; (4) abrir el ticket, repasar los campos que llenaron; (5) **"Crear cotización en BIND →"** (ahora CTA primario; también hay botón Cotizar por fila) → la vista previa del body se genera SOLA; (6) encender `Bind:Mode=Write` + reiniciar API → confirmar el diálogo → folio en pantalla → verlo en su BIND; (7) **apagar el modo Write ahí mismo**; (8) temas verbales (conceptos con el molde, ACUNTIA, Automation con FAC-98 de exhibit, PUE/PPD, cuenta de servicio, prefactura=2.ª sesión); (9) Azure con la cuenta nueva.
**Lecturas clave:**
> - **El compromiso del correo ("llego a la sesión con el flujo ya funcionando") queda cumplible sin el molde:** el flujo corre contra cualquier ticket de FAC; el molde solo mejora los datos.
> - **La primera escritura real ocurre EN la sesión, nunca antes** — es literalmente el formato de prueba acompañada que autorizó Noé. Si BIND rechaza el primer intento (semántica de partidas), el error se lee en pantalla y se ajusta en vivo: para eso es la sesión.
> - El default ReadOnly y la pantalla con candado convierten la salvaguarda de ADR-001 en algo que Balam puede VER — vale más que explicarla.
**Pendientes que deja la sesión:**
- [ ] 🔥 Johann — **enviar el script a Noé/Pedro esta noche** (doc listo en planeacion/).
- [ ] 🔥 Johann — en la sesión: encender `Bind:Mode=Write` solo para emitir y **regresarlo a ReadOnly antes de cerrar**.
- [ ] Johann — capturar las horas de esta sesión nocturna en el corte siguiente.
- [ ] Johann — si BIND ajusta el shape en vivo (Type/SelectedUnit/VAT), documentar la semántica confirmada en `../planeacion/Investigacion-API-Jira.md` y en el CLAUDE.md del repo de la plataforma.
---
## 65 · Ago 12, 2026 — 7:00 AM · Llamada · Prueba de escritura Jira→BIND: **Pedro releva a Erika**, se decide que la plataforma genera la cotización y el primer POST falla con 500 ⭐🔥
> Evidencia: `../fuentes/2026-08-12 - Transcript - Sesion prueba de escritura Jira-BIND.txt` (52 min). Asistentes: **Noé, Araceli, Pedro y Johann**. *(La diarización del transcript es poco fiable en varios tramos — atribuye a Noé frases que claramente son de Pedro; el contenido sí es legible.)*
### 1 · ⭐ Cambio de PM — anuncio de Noé (2:26 y 4:12)
> *"Nada más creo que no te había avisado, no sé si te lo mandé por correo o no. **Erika ya no va a ser la PM de este proyecto. El seguimiento del proyecto lo va a traer Pedro.**"* — y lo repite al cerrar el bloque: *"Bueno, este es un anuncio, Johann. **El tema del seguimiento es con Pedro.**"*
Explica el vuelco de interlocución que se ve desde el 18-ago ([#67](REGISTRO.md)) y por qué Erika desaparece del hilo después del 29-jul. **Nunca llegó por correo** — el anuncio es exclusivamente verbal y vivía solo en esta grabación.
### 2 · La lógica comercial de la cotización, explicada por Araceli (9:38–13:22)
La explicación más completa que se ha obtenido de por qué existe la cotización:
- **Día 1 / venta nueva:** comercial cotiza **directamente en BIND** para que el cliente acepte el monto. Requisito previo: **el cliente ya debe estar dado de alta**, o la cotización no sale con nombre ni RFC. Cerrada la venta, comercial solicita facturación **adjuntando esa cotización**; administración busca el número (p. ej. `01`) y desde el menú de la cotización en BIND da **prefactura o factura**.
- **Recurrente:** comercial **no vuelve a cotizar**; solo avisa a administración el periodo (3, 6, 12 meses) y administración genera prefacturas y facturas. *"el área comercial no se entera cuando es recurrencia"*.
- **Por qué existe la prefactura:** *"antes nos íbamos directamente a facturación, pero luego había muchos errores y ya estaban timbradas… era todo un rollo la cancelación con el SAT"* — hay clientes que deben aceptar la cancelación y *"CEMEX se tarda años"*.
### 3 · ⭐ DECISIÓN: la cotización la genera la plataforma (13:23–14:15)
Noé pregunta a Araceli si le serviría que **Jira generara la cotización** y le ahorrara el primer paso → *"Sí, sí me serviría"*. Noé: *"vamos a hacer ese ejercicio, Johann… eso va a quitar tal H que hoy por hoy se está haciendo desde el área comercial."*
> ✅ **Cierra la contradicción 🔴 que llevaba abierta desde el 6-jul**: Ara había dicho que el comercial genera la cotización y la plataforma la convierte; Arturo dijo lo contrario el 4-ago. **Resolución: para venta nueva la genera la plataforma**, y el paso comercial desaparece. La recurrente sigue fuera de alcance.
### 4 · ⚠️ El molde nunca existió — Pedro construye el formulario EN VIVO (14:43–29:52)
Araceli confirma que **no hay tipo de ticket para cotización**: *"Jira no, fíjate que no lo tenemos, pero lo tendremos que definir"*. **El molde que Noé le encargó el 11-ago nunca se creó.** En su lugar, Pedro lo improvisa en la sesión: *"lo que puedo hacer es **construir un nuevo formulario muy rápido aquí en Jira** con campos muy muy especiales para la cotización y hacemos esa prueba en vivo"* — y se apoya en **el Excel que Johann había mandado esa misma mañana** ([#66](REGISTRO.md)): *"ya habíamos mapeado campos en un correo con Johann"*.
**Campos acordados en vivo:** Empresa origen · Summary (título del ticket) · Descripción *(hoy texto libre; **debe ser combo**)* · Nombre del cliente *(**debe ser combo**, solo clientes dados de alta en BIND)* · Tipo de moneda · Monto sin IVA · Fecha de vencimiento *(= vigencia de la cotización)*. Además, **quitarle el SLA a estos tickets para que no impacten métricas**.
### 5 · ⭐ El catálogo de BIND está sucio — y de ahí salen las 17 categorías (22:54–27:22)
Araceli abre el combo de productos y servicios de BIND en pantalla y señala el desorden: *"consultoría y horas de servicio, este es **040 y 029, esto es absolutamente lo mismo**"*; hay entradas con números de personas, *"monitor Samsung"*, *"servicios profesionales 16 horas"*, periodos metidos en el nombre. Su conclusión: debería haber **solo opciones genéricas** y editarse el detalle en la cotización.
**Acuerdo (Noé, 27:04):** *"Actividad pendiente: **depurar los campos que se van a usar en la cotización**, para que sean los mismos que van a estar en el combo desplegable y coincidan con los campos que se van a estar planchando en [BIND]."*
> ⭐ **Éste es el origen de las "17 categorías" de Pedro** ([#67](REGISTRO.md)): de los **67 servicios** que Johann catalogó en el Excel a **17** depuradas. **Es la vía por la que se está resolviendo el hueco de conceptos/partidas** — no agregando un campo de partidas a Jira, sino limpiando el catálogo y volviendo combo la descripción.
>
> Dato fiscal capturado: horas de consultoría = **unidad SAT `E48`**.
### 6 · 🔴 La primera escritura real a BIND — ERROR 500, no se creó cotización (30:08–49:59)
- ✅ **El ticket creado en vivo se reflejó en la plataforma** de inmediato: *"ya se vio reflejado el ticket que hicimos aquí de prueba"*.
- ⚠️ **El cliente no se detectó:** el mapeo venía del `summary`/`description`, no de campos estructurados. Johann lo resolvió en vivo con el endpoint de clientes de BIND y seleccionó **CEMEX**.
- 🔴 **Al ejecutar el POST: error 500.** Diagnóstico en vivo: faltaban campos que el portal envía en ceros, y sobre todo **la lista de precios**.
- **Araceli:** *"nosotros realmente **no tenemos una lista de precios**… ese lo podríamos agarrar del monto, del [Jira]. Ya ves que te pusieron **monto sin IVA. Ese sería nuestra lista de precios**."* Noé imagina que BIND multiplica cantidad × precio del servicio, pero Araceli confirma que **ellos capturan el número directo** — Pedro: *"como campo abierto"*.
- Johann reintentó con un ítem existente; **la sesión cerró sin cotización creada**, con el compromiso de validar y avisar.
> 🚨 **Corrige la lectura del [#64](REGISTRO.md).** La capa llegó probada en dry-run con 73/73, pero **el primer POST real contra BIND falló**. La semántica de partidas (`Type`/`SelectedUnit`/lista de precios) no está resuelta, y es exactamente lo que hay que llevar cerrado a la sesión del 21-ago.
### 7 · Reglas de negocio que quedaron abiertas
- **Edición de cotizaciones (32:49–39:05):** Araceli necesita modificar cotizaciones ("siempre hay ajustes"). Verificó en vivo que **BIND conserva el mismo número al editar** (cotización `10.19`) y solo registra la modificación. La plataforma **no tiene edición implementada**. Noé plantea la disyuntiva —**historial de cotizaciones** (nuevo ticket o regenerar) vs. **modificar la misma**— y la deja como definición de Balam: *"creo que como regla tenemos que definirlo nosotros, para no andar trayendo a Johann arriba y abajo"*. Araceli se inclina por conservar historial.
- **Cancelaciones (41:53–45:06):** mientras no se timbre se puede cancelar libremente — *"mientras no las [timbremos] en CFDI, podemos cancelar y hacer las prefacturas que queramos"*. Ya timbrada: acuerdo con el cliente por correo → cancelación en el ERP → el despacho la cancela ante el SAT → **el cliente debe aceptarla en una liga del SAT**. **Decisión: el desarrollo NO ejecuta cancelaciones, pero sí debe llevar el récord** (*"esta factura se canceló y fue sustituida por esta"*).
### 8 · Otros acuerdos
- ☁️ **Azure sigue del lado de Balam.** Noé: *"Pendientes de nuestro lado hacia ti es el tema del ambiente de Azure… para que puedas irlo generando"* → *"eso lo resuelvo con Pedro"*. **No se exploró en la sesión**, pese a que el 11-ago se anunció que se vería aquí ([#63](REGISTRO.md)).
- 📼 **Grabaciones de BIND:** Pedro tiene videos de altas de proveedor/clientes y facturación y quedó de enviarlos al terminar. **No consta que hayan llegado.**
- 🗂️ **Herramienta de seguimiento del proyecto sin resolver:** el tablero de Jira para las actividades de Johann *"muy próximo iba a necesitar que sea de paga por el tipo de licencia"*; se barajan **SharePoint** y **Google Drive**. Noé prefiere Jira pero falta el permiso de admin para crear spaces. Sigue vigente lo asentado en [#58](REGISTRO.md): **el Excel de horas es, de facto, el sistema de seguimiento**.
- 🔒 **Candado reafirmado (Noé, 50:28):** *"esta es la primera prueba de escritura… habíamos acordado ser muy puntuales, **de no hacer más pruebas de lo que definiéramos**, para evitar errores o cosas en producción que puedan afectar"*. Johann ofrece **avisar a Pedro cada vez** que vaya a escribir una cotización o prefactura.
- 🔁 **Pedro quiere jalar los catálogos de BIND por API** para no capturar a mano (50:59). Johann ofrece dárselos en JSON o Excel — de hecho **ya se los había mandado esa mañana** ([#66](REGISTRO.md)).
> **Lectura estratégica:**
> - **La sesión cambió la gobernanza del proyecto y no quedó registro escrito.** Un cambio de PM anunciado solo de viva voz, en una grabación que estuvo ocho días fuera de la bitácora, es exactamente el riesgo que la regla de oro busca evitar.
> - **El Excel del [#66](REGISTRO.md) llegó una hora antes y se volvió el insumo de la sesión.** Sin él, Pedro no habría podido armar el formulario en vivo. La táctica de mandar "qué necesita el destino" se validó en tiempo real.
> - **El molde de Araceli nunca existió y ya no hará falta:** el camino real fue formulario nuevo + catálogo depurado, no un ticket de ejemplo. Los planes B y C del [#63](REGISTRO.md) quedaron sin usar.
> - 🔴 **El 500 es el pendiente técnico más caro que dejó la sesión** y sigue abierto ocho días después. La sesión del 21-ago es la segunda oportunidad de emitir la primera cotización real; conviene llegar con el shape del POST ya resuelto contra el nuevo formulario.
---
## 66 · Ago 12, 2026 · Correo · Johann → Pedro (CC Ara, Noé) · campos y catálogos de la cotización BIND ⭐
> Evidencia: `../fuentes/2026-08-12 - Correo - Campos cotizacion BIND.md`. **Adjunto:** `../Cotizacion-BIND-campos-y-catalogos.xlsx`.
**8:09 am — Johann entrega la estructura completa de la cotización en BIND**, en dos partes:
- **Campos del formulario:** cada campo de la cotización, cuáles son **obligatorios**, y **el origen de las opciones** de cada combo.
- **Catálogos con datos reales de BIND hoy:** **67 servicios, 3 productos, 17 clientes**, monedas, sucursales y listas de precios.
**10:30 am — Pedro acusa:** *"Confirmo de recibido. Estaré trabajando en conjunto con el equipo para regresar con comentarios."*
> **Lectura estratégica:**
> - ⭐ **Es la jugada que destraba el hueco de conceptos/partidas.** Desde el [#62](REGISTRO.md) el único bloqueo real de B7 era que **Jira no tiene campo de conceptos**. En vez de pedirlo en prosa, Johann invierte la dirección: manda **qué necesita BIND para recibir** y deja que Balam ajuste el formulario de origen. Es la misma táctica del "molde" del 10-ago, pero al revés.
> - **Funcionó.** El 18-ago Pedro reporta que **redujo el catálogo a 17 categorías** y que el formulario se armó *"con lo que marcaste en 'Sí' en el excel compartido"* ([#67](REGISTRO.md)). El Excel de Johann se volvió la especificación del formulario de Jira.
> - ⚠️ **Los comentarios formales nunca llegaron por correo.** Pedro los prometió el 12-ago; se materializaron **6 días después y por WhatsApp**. El canal de evidencia volvió a quedar vacío.
> - 📌 El Excel **sigue sin versionar en git** (está en la raíz del repo, sin trackear).
---
## 67 · Ago 18–20, 2026 · WhatsApp Johann ↔ Pedro · Pedro toma el timón: 17 categorías, formulario modificado y sesión del viernes 21-ago ⭐🔥
> Evidencia: `../fuentes/2026-08-18 a 2026-08-20 - WhatsApp - Pedro formulario 17 categorias y sesion 21-ago.md`.
**18-ago — comentarios al Excel, por fin:**
- ⭐ Pedro: *"**logré reducirlo a 17 categorías** pero tengo que verlas con Noé y Ara antes"* (catálogo de servicios: de **67 → 17**).
- ⭐ Pedro: *"del formulario son lo que **marcaste en 'Si' en el excel compartido**"* — el formulario de Jira se rehace contra los campos obligatorios de BIND.
- Johann pide lo que necesita: **un ticket de prueba con el nuevo formulario**, para pasarlo a cotización.
**19-ago — Balam mueve el formulario y convoca:** Pedro anuncia *"ya hemos realizado algunas **modificaciones en nuestros formularios y campos**"* y pide sesión **con Noé y Ara**, el viernes a las 7. Johann ofrece **preparar el caso de escritura** para esa misma sesión; Pedro acepta (*"si me parece bien el caso"*).
**20-ago — el ticket llega el día antes, al filo:**
- 9:48 am Johann pide el ticket para el ejercicio → 10:47 am *"dame un momento"*.
- 3:43 pm ⚠️ *"de momento **no pude crear el ticket** debido a que **surgieron varios comentarios internos**, dejame tocar base con Noé"*.
- 4:23 pm ✅ *"ya me confirmaron Johan, **mañana a las 7 am**"* + **`FAC-106`**.
- 4:24 pm ⚠️ *"**no te dejará entrar debido a la licencia**"*, ofrece pantallazo de la estructura.
> **Lectura estratégica:**
> - 👤 ⭐ **Cambió el interlocutor del proyecto.** Desde el 18-ago **todo se coordina con Pedro**: comentarios, formulario, agenda, ticket y convocatoria. Erika (PM, contacto único durante toda la Etapa 0) **no aparece en el hilo desde el 29-jul**, y Noé pasó de definir a validar. Concuerda con lo que Johann recuerda de una junta donde se anunció a **Pedro como nuevo responsable del proyecto** — sin registrar ([#65](REGISTRO.md)).
> - ⭐ **Balam cambió Jira para acomodar a BIND.** Es la primera vez en el proyecto que **el sistema origen se ajusta al destino** en lugar de pedirle al desarrollo que absorba la diferencia. El Excel del [#66](REGISTRO.md) fue el instrumento.
> - ⚠️ **Las 17 categorías NO están validadas.** Pedro lo dice explícito: *"tengo que verlas con Noé y Ara antes"*. La sesión del viernes es donde eso se aprueba — y de ahí sale (o no) el mapeo de conceptos.
> - 🔁 **Se repite el patrón del 11-ago:** el insumo de la sesión llega la víspera por la tarde y **no se puede inspeccionar** (entonces el molde no existía; hoy `FAC-106` existe pero la licencia bloquea la UI). **Mitigación disponible esta noche: leerlo por API** — el token PAF corre sobre la cuenta de Pedro, que sí tiene licencia, y es como se sondeó el sitio el 6-ago y el 11-ago. No hace falta el pantallazo para conocer la estructura.
> - **El alcance sigue siendo la cotización**, no la prefactura ([#62](REGISTRO.md)); y la escritura real sigue gateada a `Bind:Mode=Write`, que se enciende solo durante la sesión ([#64](REGISTRO.md)).
---
## 68 · Ago 20, 2026 — 4:25 PM · Correo · Pedro → Noé, Johann · "Seguimiento - PAF" (convocatoria + FAC-106)
> Evidencia: `../fuentes/2026-08-20 - Correo - Pedro Seguimiento PAF.md`. **PAF** = Plataforma de Automatización Financiera, el nombre con el que Pedro bautizó el token del proyecto el 27-jul ([#56](REGISTRO.md)).
Correo escueto: la liga de **`FAC-106`** y la reunión de **Microsoft Teams** (Id. 280 788 665 449 275). Es el formalizado por correo de lo acordado por WhatsApp minutos antes ([#67](REGISTRO.md)).
> ⚠️ **El correo no dice fecha ni hora.** La convocatoria real —**viernes 21-ago, 7:00 am**— consta solo en WhatsApp. Si alguien reconstruye el proyecto desde el correo, la sesión aparece sin fecha.
> **Lectura estratégica:**
> - **Noé va en el "Para", no en copia:** la sesión tiene al CTO como asistente, no como observador. Coincide con que se validan las 17 categorías, que Pedro no puede aprobar solo.
> - Es el **primer correo del proyecto que no cuelga de un hilo previo** — asunto nuevo, con el nombre del proyecto. Encaja con el relevo de interlocución del [#67](REGISTRO.md).
---
## 69 · Ago 21, 2026 — 7:00 AM · Llamada + trabajo posterior · PRIMERA COTIZACIÓN REAL EN BIND desde un ticket (folio 1026) — tras 5 intentos y dos semánticas indocumentadas ⭐🔥
> Asistentes: Noé, Araceli, Pedro, Johann. Preparación: [`../planeacion/Guion-sesion-2026-08-21.md`](../planeacion/Guion-sesion-2026-08-21.md) y [`../planeacion/Sondeo-FAC-106-y-payload-2026-08-20.md`](../planeacion/Sondeo-FAC-106-y-payload-2026-08-20.md). ⚠️ **Minuta de la parte de definiciones (17 categorías, Empresa origen, IVA 0%, Azure) pendiente de capturar por Johann** — esta entrada cubre la parte técnica, que quedó auditada en `WriteOperations`.
**En la sesión (7:04–7:11 am) — tres intentos, cero cotizaciones creadas:**
| # | Op | Body | Respuesta de BIND |
|---|---|---|---|
| 1 | 23 | sin `ID`/`Type` de partida, sin `PriceListID` | 500 **explícito**: *"Required property 'ID'… 'Type' not found… El ID de la lista de precio es requerido"* → se corrigió en vivo (lista "A" dada por Araceli) |
| 2 | 28 | + `ID` servicio 027, `Type: 1`, `PriceListID` A, `Unit: "Servicio"`, **sin `ExchangeRate`** | 500 **genérico** `174068216` (*"comunicarte con soporte técnico"*) |
| 3 | 34 | ídem con el servicio `ChargeVAT:true` | 500 genérico `174068255` |
Se aplicó el protocolo del guion §6: parar, anunciar el diagnóstico, pivotear a las definiciones. **Verificado por lectura después de cada intento: 52 cotizaciones en BIND, ninguna nueva.** Al salir de la sesión, Balam autorizó seguir sobre `FAC-106` por ser ticket de prueba.
**Investigación posterior (solo lectura):**
- Schema tipado del portal (APIM, anónimo): `Items` son **`NewFullProducts`**, requeridos solo `ID` y `Type`, **sin enums ni descripciones**. Permiso descartado: el token trae `5|11|AgregarCotizacion`. `GET /api/Series?docType=n` devuelve `[]` para todo `n` (sin series → `DocNumberID` no aplica). El read model de `Quotes` reporta `PriceListID` = su propio `ID` (bug de BIND); la lista real de CEMEX es **"A" `03e4bb2d`** (`Clients.PriceListID`).
- Intento 4 (op 44): + `ExchangeRate 1.0`, `Unit E48` + `SelectedUnit`, `Type: 1` → 500 genérico `174068365`.
- ⭐ **Intento 5 (op 45): `Type: 2` → HTTP 200, folio 1026.** `Type` es **el catálogo donde BIND busca el `ID`: 2 = servicio, 1 = producto.** Indocumentado; solo descubrible por ensayo.
**Pero el folio 1026 salió mal:** `VAT = 243,640.96` sobre base 1,234 = **1,234 × 197.44**. ⭐ **En el write model `Items[].VAT` es TASA (0.16), no monto** — BIND lo multiplica por precio × cantidad. El read model sí devuelve montos, lo que indujo el error. **Corregido en UI y con guard de 400** (misma regla que `VATRate`). **El folio 1026 debe cancelarse** (IVA absurdo) y la segunda emisión, ya con `VAT: 0.16`, debe dar 1,234 + 197.44 = **1,431.44 MXN** — folio y confirmación pendientes de Johann.
> **Lectura estratégica:**
> - **Se cumplió lo que la prueba acompañada existe para hacer:** dos semánticas que ningún documento de BIND contiene (`Type` y la escala de `VAT`) quedaron resueltas contra producción y escritas en el modelo. El costo fue un folio de prueba que se cancela sin efecto.
> - **El primer error de BIND fue útil y los tres siguientes no:** cuando falla la validación del JSON, BIND dice qué falta; cuando falla por dentro, solo da un código. El diagnóstico salió de comparar con cotizaciones reales y del schema del portal, no del mensaje.
> - **La plataforma se portó como se diseñó:** cada intento auditado en `WriteOperations` con body y respuesta, verificación por lectura entre intentos, y el candado de `Bind:Mode` encendido solo para emitir. Es el argumento de confianza ante Noé, y va en el correo.
> - ⚠️ **Deuda que deja:** el GUID del servicio (027) y la lista de precios ("A") van **fijos en la UI**. Lo correcto es resolver el servicio desde el combo `Servicio/Producto` (17 categorías → GUID de BIND, que además hoy tiene 5 "Servicio de Enlace" distintos) y la lista desde `Clients.PriceListID`.
> - Borrador del correo a Balam: [`../planeacion/Correo-error-resuelto-2026-08-21.md`](../planeacion/Correo-error-resuelto-2026-08-21.md).
---
## 70 · Ago 26, 2026 · Incidente + investigación de cierre · IsFiscalInvoice=true TIMBRA al crear (folio 2386) — y el API de BIND ni crea ni VE prefacturas 🔥⚠️
> Contexto: entre el 23 y el 25-ago se construyó el camino cotización→prefactura (`POST /api/emision/prefactura`, mismo protocolo ADR-001). Los intentos con `IsFiscalInvoice=false` produjeron los folios 1000 y 1001 **como remisiones** (confirmado por Noé por correo), así que se cambió a `true` bajo la hipótesis "true = factura sin timbrar = la prefactura del flujo" — el API no tiene operación de timbrado (130 operaciones revisadas), y de ahí se infirió que crear no podía timbrar.
**El incidente:** el 26-ago, al confirmar desde el wizard (op #73, cotización 1028 / ticket FAC-108), BIND creó la venta **2386 como Factura Electrónica (CFDI) y la timbró en el acto** — UUID `aa70001d-…`, $1,160 MXN, receptor ACUNTIA. **El timbrado vive DENTRO de la creación**, no en una operación aparte; la ausencia de operación de timbrado significaba lo contrario de lo inferido. El tripwire de la plataforma (verificación por lectura post-creación, "⚠️ TIMBRADA" si UUID ≠ null) lo detectó al instante. Contención el mismo día: confirmación de prefactura **bloqueada con 409** en el endpoint (commit `c5143cf`, pusheado), API reiniciado en ReadOnly, instrucciones de cancelación SAT (motivo 02) comunicadas.
**Investigación de cierre (misma fecha, SOLO GETs contra producción):**
- **El body de `Invoices_AddInvoice` no tiene campo de tipo de documento.** Verificado contra dos fuentes primarias: el portal APIM (27 campos) y el swagger oficial del SDK de Python de BIND — coinciden campo por campo. El `UseStock` que sugieren los resúmenes de IA de Google **no existe** (respuesta fabricada; también da `UseCFDI:"G03"` como string cuando `CFDIUse` es un int interno).
- **`false` = remisión, comprobado con la verdad impresa:** los PDFs por API (`GET /api/Invoices/{id}/pdf`) de 1000, 1001 y 2384 titulan **"Remision"**; el de 2386 titula "Factura Electrónica (CFDI)".
- **No existe el estado intermedio:** 0 filas con `IsFiscalInvoice=true and UUID=null` en TODA la cuenta. BIND timbra al crear, siempre.
- **Censo completo de no fiscales: 58 documentos, todos remisiones.** Únicos activos: nuestros 1000 y 1001; los otros 56, cancelados. **Ni una prefactura visible en `/api/Invoices`** — la afirmación de VALIDACION-API.md (6-jul) "false = prefactura" era una inferencia errada sobre filas que en realidad son remisiones.
- `Orders` tampoco: 1 pedido (2024, cancelado). El detalle de venta no expone claves ocultas que discriminen tipo.
- ⚠️ Efecto colateral del API detectado: la **lista** reporta `Status=2` en filas cuyo **detalle** dice `Activa` (1000/1001), y `Number` no es único entre series — la búsqueda por número es ambigua. Confiar solo en detalle por GUID.
**Conclusión (revisada el mismo día — ver adenda):** la "Prefactura" del combo de la UI de BIND **no es expresable por su API pública** con lo documentado. Queda una incógnita: dónde queda almacenada una prefactura creada por la UI (en esta cuenta nunca ha existido una, así que no hay espécimen que leer).
**ADENDA (26-ago, tarde) — apareció el espécimen y reescribe medio registro.** El folio **2384** (VictTec, $1,650 USD, 19-ago, creado por Araceli) **SÍ es una Prefactura — confirmado por Johann en la UI**. Con eso:
- **Las prefacturas SÍ son visibles por API** (VALIDACION-API.md tenía razón a medias): son filas `IsFiscalInvoice=false`+`UUID=null`. Lo errado era leer `Status=2` como "cancelada": en el precursor significa **consumida/facturada** — la 2384 parió a la **2385** (misma fecha, mismo total, timbrada) y quedó en 2. Las 56 "canceladas" del censo son los precursores históricos del flujo real de Balam (el PDF por API las titula "Remision" aunque sean prefacturas: la plantilla no distingue).
- **Diff campo por campo prefactura 2384 vs remisión 1000: NO existe campo de tipo.** Diferencias completas: `CFDIUse` 23 vs 3, `ShowIEPS` true vs false, `PaymentTerms` 30 vs 0, `CFDIPaymentTerm` con valor vs vacío — todo son valores del payload, nada discrimina el tipo.
- ⭐ **El discriminador observable es la SECUENCIA DE FOLIOS:** las prefacturas de la UI comparten numeración con las facturas (2384 → 2385 → 2386 fue nuestro incidente); nuestros `false` por API caen en la secuencia de remisiones (arrancó en 1000, virgen porque Balam no usa remisiones). Hipótesis operativa: la UI etiqueta por secuencia/typo interno; el body de `Invoices_AddInvoice` trae `Number` y `Serie` — **candidato a experimento seguro**: POST con `false` + `Number` apuntando a la secuencia compartida (+ espejo exacto de 2384: `CFDIUse=23`, `ShowIEPS=true`, plazo), verificar en UI cómo cae, y borrar si cae mal. Riesgo máximo: un documento no fiscal borrable con folio raro. Solo con go-ahead explícito y `Bind:Mode=Write` en sesión.
- **Detección construible YA sin tocar nada:** prefactura de UI = fila `false` nueva (ligable a cotización/ticket por cliente+monto+fecha) y su facturación = fila pasa a 2 + gemela `true` timbrada. El flujo "plataforma cotiza → Araceli genera prefactura en BIND → plataforma detecta, liga y avisa el timbrado" funciona con el API actual, sin escribir.
**ADENDA 2 (27-ago, madrugada) — el API público queda agotado por eliminación TOTAL.** Experimentos autorizados de la sesión Write con Johann (todos con `false`, todos borrables):
- **Op #79 (folio 1002, $580, FAC-109/cotización 1029):** se mandó `Number: 2387` (siguiente de la secuencia compartida) + espejo de la 2384 (`ShowIEPS`, plazo 30) → **BIND IGNORÓ el `Number`** y asignó 1002 en la secuencia de remisiones. La palanca del folio no existe hacia adentro.
- **Sonda de campos no documentados (folio 1003, $116, borrada → "Cancelada"):** un POST con 12 candidatos a campo interno de tipo (`UseStock`, `ApplyStock`, `AffectsInventory`, `DocumentType/DocType/SaleType/InvoiceType: 2`, `IsPreInvoice/IsPrefactura/IsPreliminary/IsDraft/Prefactura: true`) → **todos ignorados** (folio de remisiones, sin cambio de tipo). `DELETE /api/Invoices/{id}` funciona (204) pero es borrado suave: deja la fila como Cancelada.
- Dump crudo del APIM: **98 operaciones** (no 130), ninguna de prefactura/conversión. `/api/Series?docType=0..20` todo vacío; `CompanyProfile` sin foliado. La web app (app.bind.com.mx) es ASP.NET MVC con login — su creación de ventas NO pasa por api.bind.com.mx.
- **Conclusión definitiva:** `Invoices_AddInvoice` produce Remisión (`false`) o CFDI timbrado (`true`); el tipo Prefactura se decide en un camino interno de su UI al que el API público no llega por ningún campo, folio ni recurso.
- **La manera que SÍ queda** (además de detección y de soporte BIND): capturar el endpoint interno de la UI con un navegador instrumentado (Playwright, script `captura-prefactura.mjs` — registra POST/PUT a bind.com.mx excluyendo login/credenciales) y evaluar replicarlo con sesión de cookies (RPA ligero) o usarlo como evidencia ante BIND para pedir el campo en su API.
> **Lectura estratégica:**
> - **La salvaguarda que funcionó fue la de diseño, no la de inferencia:** dry-run + confirmación humana + verificación por lectura acotaron el daño a un folio cancelable. La lección: contra un API sin sandbox, ninguna inferencia negativa ("no hay operación X ⇒ X no puede pasar") es válida sin prueba positiva.
> - **Candado triple ya en código** (commit local `fbb31fc`): `IsFiscalInvoice=false` clavado en el builder, `permitirFiscal:false` clavado en la llamada, y el 409 en Confirmed. `EsFiscal` quedó inerte.
> - **Experimento decisivo pendiente (riesgo cero):** que un humano cree UNA prefactura en la UI de BIND (documento no fiscal, borrable) y re-escanear el API para ver si aparece y con qué firma. Si no aparece, el flujo de plataforma debe asumir que la prefactura es invisible por API y la detección automática solo puede ocurrir al timbrado (la factura sí es visible).
> - ⚠️ **Al 26-ago (tarde) la 2386 sigue `Activa` con UUID en BIND — la cancelación SAT no se ha ejecutado.** Y 1000/1001 siguen activas (existe `DELETE /api/Invoices/{id}` si se quiere limpiar por API, con autorización).
---
## 71 · Ago 21, 2026 — 7:32 AM · Correo · Johann → Noé (CC Pedro) · "Seguimiento FAC-106": la causa del error y la primera cotización real
> Dirección: enviado. Evidencia: buzón (hilo "Seguimiento FAC-106"); borrador en [`../planeacion/Correo-error-resuelto-2026-08-21.md`](../planeacion/Correo-error-resuelto-2026-08-21.md). Complementa el [#69](REGISTRO.md). *(Registrado el 29-sep-2026 al cerrar los huecos de bitácora.)*
**Resumen:** tras la sesión de las 7:00 am, Johann informa que ya encontró la causa del error y que la cotización desde `FAC-106` **ya se genera correctamente en BIND**. El problema no eran los datos del ticket ni el formulario nuevo (*"quedó muy bien"*), sino dos detalles del API de BIND que no están en su documentación y solo se descubren probando: (1) cada partida lleva `Type` (producto vs servicio) sin valores documentados — con el valor de producto BIND buscaba el servicio en el catálogo equivocado y devolvía el error genérico de *"comunicarte con soporte técnico"*; (2) la escala del IVA por partida. Noé respondió el 24-ago con el mecanismo de la UI ([#74](REGISTRO.md)).
---
## 72 · Ago 21, 2026 — 1:00 PM · Llamada · "Seguimiento - PAF (2)" · Homologación de estados del flujo ticket → cotización → prefactura → factura
> Participan: Johann, Pedro (convocatoria de Pedro a las 7:14 am con la liga a `FAC-106`; Noé convocado). Canal: Teams. Evidencia: informe de Read AI (1:34 pm), **solo el extracto del correo, sin transcript** — [`../fuentes/2026-08-21 a 2026-09-29 - Read AI - Informes Seguimiento PAF (extractos del correo).md`](../fuentes/2026-08-21%20a%202026-09-29%20-%20Read%20AI%20-%20Informes%20Seguimiento%20PAF%20(extractos%20del%20correo).md). ⚠️ Reconstruida el 29-sep desde ese extracto.
**Resumen:** se definió la integración Jira ↔ BIND ↔ plataforma de punta a punta: creación del ticket → cotización → prefactura → factura → timbrado. Se acordó **homologar los estados del flujo**: la prefactura se reflejaría como **"En proceso de facturación"** y la factura completada como **"Facturado"** — coincide con la regla del [#62](REGISTRO.md): la aprobación de Araceli va entre esos dos estados. Quedó pendiente la **prueba de escritura en Jira** (cambiar el estado de un ticket desde la plataforma). Johann y Pedro acordaron **repetir una prueba desde cero** para validar la generación de la cotización y la escritura en BIND.
**Pendientes que surgieron:**
- [ ] Johann — prueba de escritura en Jira (transición de estado de un ticket).
- [ ] Johann — validar con Pedro la cotización creada en BIND cuando él tenga acceso para revisarla.
---
## 73 · Ago 24, 2026 — 7:00 AM · Llamada · "Seguimiento - PAF" · Validación cotización → prefactura (MXN/USD, IVA 16 %/0 %) y la primera semántica dura: `IsFiscalInvoice=false` = REMISIÓN
> Participan: Noé, Pedro, Johann. Canal: Teams. Evidencia: informe de Read AI (7:44 am), extracto. Preparación: la noche del 23-ago se construyó en la plataforma el camino cotización → prefactura (`POST /api/emision/prefactura` con el mismo protocolo ADR-001; commits `a893fa9`…`a07fff6` del repo del cliente): el wizard deja de simular (paso 4 = borrador, paso 5 = aprobación y generación real) y `CFDIUse`, `ServiceID` y `PriceListID` se heredan de una factura real y del detalle del cliente porque BIND los exige aun en prefactura.
**Resumen:** se validó el flujo de conversión de cotizaciones a prefacturas en BIND — captura, revisión, aprobación y registro financiero — con pruebas **en moneda nacional e internacional, IVA 16 % y 0 %**. Se identificaron ajustes pendientes en la lógica fiscal y en la **clasificación del documento**: los POST con `IsFiscalInvoice=false` produjeron los **folios 1000 y 1001 como Remisiones**, no prefacturas. Ajuste en vivo (commit `77d3767`, 7:38 am): `false` = remisión; la prefactura pasa a modelarse como "factura sin timbrar" con opt-in explícito; la bandeja gana una columna BIND. Se evaluará automatizar el paso Jira → BIND como cotización para reducir clics. **Noé condicionó los accesos de Azure a que las pruebas funcionen** y pidió a Johann **habilitar la aplicación en un VPS** para facilitar las pruebas — origen de la publicación provisional ([#77](REGISTRO.md)).
**Puntos de acción (según Read AI):**
- Noé — gestionar los accesos de Azure una vez validado el funcionamiento de las pruebas.
- Johann — habilitar la aplicación en el VPS para facilitar las pruebas.
> **Lectura estratégica:** la hipótesis *"true = factura sin timbrar = la prefactura del flujo"* nació aquí, de la evidencia de que `false` era remisión y de que el API no tenía operación de timbrado. Dos días después resultó falsa ([#70](REGISTRO.md)).
---
## 74 · Ago 24–25, 2026 · Correo · Noé ↔ Johann · dos hilos: cómo se hace una PREFACTURA en BIND ("Generar Venta") y los requisitos exactos de Azure ⭐
> Dirección: recibidos (24-ago 3:48 pm y 3:52 pm; 25-ago 4:19 pm) y enviado (25-ago 4:54 pm). Evidencia: [`../fuentes/2026-08-24 - Correo - Noe Generar Venta = prefactura (Re Seguimiento FAC-106).md`](../fuentes/2026-08-24%20-%20Correo%20-%20Noe%20Generar%20Venta%20=%20prefactura%20(Re%20Seguimiento%20FAC-106).md) y [`../fuentes/2026-08-25 - Correo - Noe y Johann Acceso a Azure Balam y permisos.md`](../fuentes/2026-08-25%20-%20Correo%20-%20Noe%20y%20Johann%20Acceso%20a%20Azure%20Balam%20y%20permisos.md).
**1 · "Re: Seguimiento FAC-106" (Noé, 24-ago 3:48 pm, con 4 capturas de pantalla).** Balam revisó las cotizaciones que se convirtieron en Remisión y explica el camino de la UI: *"Cuando una cotización se hace y se quiere convertir en Prefectura [sic] se selecciona la opción en Acciones en Bind que dice **'Generar Venta'**, si se selecciona la opción **Generar Pedido** se hace remisión y eso no lo manejamos en Balam."* Y en la ventana siguiente *"se selección la opción 'Prefectura' para revisar antes de mandar la factura al cliente"*. ⭐ **Primera descripción escrita del mecanismo interno:** el tipo de documento se elige **en la UI, después de "Generar Venta"** — no existe en el API público (adenda 2 del [#70](REGISTRO.md), [#83](REGISTRO.md)).
**2 · "Acceso a Azure Balam y permisos" (Noé, 25-ago 4:19 pm; antes, 24-ago 3:52 pm en el hilo de Jira).** *"Para cerrar el tema del acceso a Azure y no dar más vueltas, ¿nos ayudas a detallar exactamente qué necesitas para publicar el ambiente? … lo validamos con la cuenta que estamos preparando para este uso y te damos acceso."* Johann respondió el 25-ago 4:54 pm con la lista exacta: suscripción a nombre de Balam; grupo de recursos dedicado (`rg-balam-paf-staging`); rol **Contributor** solo sobre ese grupo; **Key Vault Secrets Officer**; proveedores para App Service Linux (.NET 10), PostgreSQL Flexible Server 17, Key Vault, Storage y Application Insights/Log Analytics; identidad OIDC para CI/CD desde GitHub; y los cinco datos a recibir (Tenant ID, Subscription ID, grupo, región, cuenta). Sin Global Admin ni facturación.
**Acción derivada:** Azure sigue en manos de Balam ([#76](REGISTRO.md)); la plataforma se publica provisionalmente en un VPS propio ([#77](REGISTRO.md)).
---
## 75 · Ago 26, 2026 — 7:00 AM · Llamada · "Seguimiento - PAF" · la sesión en la que el POST se TIMBRÓ (complemento del [#70](REGISTRO.md))
> Participan: Noé, Pedro, Johann. Canal: Teams. Evidencia: informe de Read AI (7:35 am), extracto. Preparación: la noche del 25-ago se construyó el camino prefactura → factura (commits `1ad0e27`…`02cb042`): detección del timbrado al instante, puertas idempotentes, y el botón "Timbrar" enlaza a BIND porque *"el API no lo permite (130 operaciones revisadas, ninguna de timbrado/CFDI)"* — la inferencia que falló esa misma mañana.
**Resumen:** la sesión revisó la **habilitación de permisos** y el flujo remisión / prefacturación / facturación entre la plataforma y BIND. Noé señaló que **algunas funcionalidades aún debían construirse** para permitir el uso completo de la plataforma. Johann explicó que BIND **no permite timbrar por API** y propuso generar la prefactura desde la plataforma y timbrar en BIND. Al confirmar desde el wizard (op #73, cotización 1028 / ticket `FAC-108`), BIND **creó la venta 2386 como CFDI y la timbró**: el incidente, su contención y la investigación están en el [#70](REGISTRO.md). Johann estimó que la revisión del flujo de prefacturación podría quedar resuelta durante el día — lo que quedó resuelto ese día fue el diagnóstico, no la creación.
**Puntos de acción (según Read AI):**
- Pedro — solicitar apoyo para **cancelar la factura generada durante la prueba** (2386).
- Noé — construir las funcionalidades necesarias para habilitar los permisos solicitados.
- Johann — revisar y ajustar "el estatus y el parámetro" que provoca que la operación se genere como factura en lugar de prefactura → resultado: **ese parámetro no existe en el API** (adenda 2 del [#70](REGISTRO.md)).
---
## 76 · Sep 3, 2026 — 7:00 AM · Llamada · "Seguimiento PAF - 02/09/26" · Datos del tenant de Azure, los conceptos de Acuntia por headcount y una definición: ítem por CÓDIGO de catálogo, concepto = título del ticket
> Participan: Noé, Pedro, Johann. Canal: Teams. Evidencia: informe de Read AI (7:28 am), extracto (6 capítulos, no visibles).
**Resumen:** la reunión se enfocó en automatizar la generación de cotizaciones y facturas de **Acuntia** entre Jira, BIND y el ambiente de Azure. Se revisaron los datos necesarios "de Jitco" [sic, Read AI] para configurar el **tenant de Azure** y permitir el montaje. Para Acuntia, **los conceptos dependen del headcount activo** e incluyen textos por colaborador — coherente con "1 factura por colaborador, ~40" del Discovery. **Definición de Balam (Pedro):** la categoría del combo `Servicio/Producto` de Jira se traduce a un **código del catálogo de servicios de BIND** (p. ej. "Servicios Profesionales Generales" → `101`), el ítem se conserva tal cual y **solo el concepto se sobreescribe con el título del ticket**. Sustituye el GUID del servicio 027 que iba fijo en la UI desde el 21-ago. Implementado el mismo día (commit `ed4c819`): `GET /api/Services` en `BindClient`, mapeo configurable `Emision:ServicioPorCategoria`, selector de ítem en la pantalla de cotización, y la caché de la bandeja se invalida al cotizar — en producción la pantalla mostraba "—" hasta un minuto después de crear la cotización.
**Puntos de acción (según Read AI):**
- Noé — reenviar por correo a Pedro la solicitud de datos para configurar el tenant de Azure.
- Balam/Johann — a la 1:30 pm, ejercicio de conversión de dos o tres tickets en cotizaciones ([#78](REGISTRO.md)).
---
## 77 · Sep 3, 2026 · Desarrollo · La plataforma queda PUBLICADA en https://paputec.mx/balam/ — ambiente provisional propio mientras llega Azure ⭐
> Evidencia: commit `03582af` del repo del cliente (pusheado el 15-sep) y runbook [`../balam-plataforma/deploy/paputec/README.md`](../balam-plataforma/deploy/paputec/README.md). Origen: la petición de Noé del 24-ago de habilitar la aplicación en un VPS para facilitar las pruebas ([#73](REGISTRO.md)).
**Qué se publicó:** frontend Angular bajo `/balam/` y API en `/balam/api/`, en un VPS propio (Ubuntu 24.04 · Plesk · Apache · Docker): Postgres 17 sin puertos publicados; API en contenedor *chiseled* (usuario sin privilegios, FS de solo lectura, `cap_drop ALL`) escuchando solo en loopback; Apache como única cara pública (TLS, HSTS, CSP); `deploy.sh` reproducible; rol `balam_app` sin `BYPASSRLS` (cierra el hueco de RLS del [#53](REGISTRO.md)); respaldo diario con 14 días de retención; seeder de un solo administrador. **Endurecimiento del API:** rate limit por IP (global y de login), bloqueo 15 min tras 5 fallos, contraseñas ≥ 12, `no-store`/`nosniff`, sin banner, `X-Forwarded-*` solo desde loopback. **Secretos solo en `/opt/balam/.env`** (root, 0600): nada en el repo ni en imágenes. **Producción corre con `Bind:Mode=ReadOnly` y `Jira:Mode=ReadOnly`**; para una sesión acompañada de escritura se enciende `Write` y se apaga al terminar (regla del cliente, ADR-001).
> **Lectura estratégica:** Azure llevaba cinco semanas sin destrabarse ([#59](REGISTRO.md), [#63](REGISTRO.md), [#65](REGISTRO.md), [#74](REGISTRO.md)). Publicar en infraestructura propia convirtió las sesiones de pantalla compartida en pruebas sobre una URL viva: la tarde de ese mismo día Balam cotizó tres tickets reales desde ahí ([#78](REGISTRO.md)). El costo: la plataforma vive fuera del tenant del cliente hasta que Azure exista.
---
## 78 · Sep 3, 2026 — 1:30 PM · Llamada · "Seguimiento PAF (2) 03/09/26" · Tres tickets de Pedro convertidos en cotizaciones desde la plataforma publicada
> Participan: Pedro, Johann. Canal: Teams. Evidencia: informe de Read AI (1:51 pm), extracto (capítulos y acciones bloqueados).
**Resumen:** se validó la aplicación para convertir tickets en cotizaciones como etapa previa a la facturación. Johann confirmó que **los tres tickets de Pedro ya se habían enviado como cotizaciones** a BIND y que **el concepto usa el nombre del ticket** (definición de la mañana, [#76](REGISTRO.md)). En una prueba con **un ticket de Linares** se verificó [el resto del informe está truncado]. **Primera vez que Balam opera la plataforma sobre la URL publicada.**
---
## 79 · Sep 7, 2026 · Desarrollo · Cotización EN LOTE desde la bandeja: una cotización BIND por ticket, dry-run y confirmación en serie
> Evidencia: commit `fbdbd18` del repo del cliente (pusheado el 15-sep); documentado en su `CLAUDE.md`.
La bandeja permite marcar varios tickets (solo los que aún no tienen cotización) y "Cotizar seleccionados" abre `solicitudes/cotizar-lote?ids=…`: una fila por ticket, prellenada con las mismas reglas que la pantalla individual (`cotizacion-prefill.ts`), editable, con vista previa (dry-run) y confirmación por ticket. Cada confirmación es una `WriteOperation` y un `QuoteInvoiceLink` independientes; un rechazo de BIND deja la fila en error y **no aborta el lote**. Las llamadas van **en serie** para respetar la cuota de BIND y no mezclar folios. Responde al caso Acuntia (~40 facturas al mes, una por colaborador, [#76](REGISTRO.md)).
---
## 80 · Sep 9, 2026 — 1:00 PM · Llamada · "Seguimiento PAF - Prefactura" · Araceli crea una prefactura en vivo con "Generar venta" y aparece el endpoint interno: `InvoiceDetails` ⭐
> Participan: Araceli, Pedro, Johann (Noé convocado). Canal: Teams, con pantalla compartida de Araceli. Evidencia: informe de Read AI (1:24 pm), extracto (capítulos y acciones bloqueados). Es la vía "endpoint interno de la UI" que dejó abierta la adenda 2 del [#70](REGISTRO.md).
**Resumen:** el equipo revisó el procedimiento para crear una prefactura a partir de una cotización y **confirmó que debe usarse "Generar venta"** — "Generar pedido" no habilita esa función (lo mismo que Noé escribió el 24-ago, [#74](REGISTRO.md)). Durante las pruebas, **Araceli creó y guardó una prefactura** mientras Johann analizaba las peticiones registradas en las herramientas de desarrollador del navegador. **Identificaron `InvoiceDetails`** como la petición interna de `app.bind.com.mx` involucrada en la creación [el resto del informe está truncado].
**Lo que NO quedó documentado en ningún lado (verificado el 29-sep en repo, disco y buzón):** URL completa, método, payload, respuesta ni el manejo del anti-forgery token. Solo el nombre.
---
## 81 · Sep 10, 2026 — 1:00 PM · Llamada · "Seguimiento PAF - Prefactura" · Con Pedro: cotización 1078 (Acuntia) y captura de la petición de red de la prefactura para evaluar su réplica
> Participan: Pedro, Johann. Canal: Teams. Evidencia: informe de Read AI (1:21 pm), extracto (capítulos y acciones bloqueados).
**Resumen:** Johann y Pedro analizaron la **cotización 1078 de Acuntia** para identificar la petición de red que la UI usa al generar una prefactura y **evaluar su posible replicación manual e integración posterior**. Configuraron DevTools: caché deshabilitada, *preserve log* y columna de método [el resto está truncado]. Conclusión operativa de estas dos sesiones: para replicar la petición hace falta **una sesión web propia** (cookies + token) — Johann no tenía usuario en `app.bind.com.mx`; se pidió a Balam y llegó el 29-sep ([#83](REGISTRO.md)).
---
## 82 · Sep 15–16, 2026 · Administración · Corte de horas 10-ago → 16-sep (94.58 h), Excel de avance con fechas reales publicado en Google Sheets, y push de los tres commits de septiembre al repo del cliente
> Evidencia: commits `d9047de`, `69e06df`, `68aedc7` (este repo); [`../planeacion/Seguimiento-horas.md`](../planeacion/Seguimiento-horas.md); `Avance-Balam-2026-09-16.xlsx`; Sheet compartido "Avance-Balam" en Drive:/Balam.
39.25 h del 10-ago al 15-sep **reconstruidas con evidencia** (commits, correos, informes de Read AI) y marcadas `reconstruida` en el CSV; **seis sesiones sin bitácora** quedaron listadas como huecos — cerrados en este bloque ([#72](REGISTRO.md)–[#81](REGISTRO.md)). Etapa 2 sin ajuste Jira: 23.75 h de 38–46 estimadas; el ajuste Jira → BIND rebasó (14.75 h de 7–10) por las semánticas indocumentadas del API y cinco sesiones acompañadas. El Excel muestra fechas reales por tarea (primer y último día trabajado). Los commits `03582af`, `ed4c819` y `fbdbd18` se pushearon al repo del cliente el 15-sep.
---
## 83 · Sep 29, 2026 — 7:00 AM · Llamada · "Seguimiento PAF - Integración ERP" · Balam entrega LOGIN WEB a BIND con permisos de prefactura; el modelo de dos endpoints (externo = API, interno = UI) queda confirmado con fuente primaria ⭐🔥
> Participan: Pedro, Johann (Noé convocado; las cuentas son de Noé). Canal: Teams. Convocatoria de Pedro el 28-sep 5:10 pm. Evidencia: informe de Read AI (7:23 am), extracto con capítulos y acciones: [`../fuentes/2026-08-21 a 2026-09-29 - Read AI - Informes Seguimiento PAF (extractos del correo).md`](../fuentes/2026-08-21%20a%202026-09-29%20-%20Read%20AI%20-%20Informes%20Seguimiento%20PAF%20(extractos%20del%20correo).md).
**Resumen:** la reunión se enfocó en **validar accesos** y ejecutar una **prueba de integración ERP creando cotizaciones y prefacturas en BIND**. Pedro confirmó que su perfil estaba habilitado y que **las cuentas de Noé tienen permisos para crear facturas, prefacturas y operaciones relacionadas**. Johann solicitó las credenciales; se acordó **entregarlas en un archivo TXT** para usarlas en la aplicación. Con eso, al cierre del día **Johann ya tiene login web** a `app.bind.com.mx`. Durante la prueba apareció **un error que impide cargar la solicitud al crear una cotización** desde la plataforma; queda como acción de Johann.
**Puntos de acción (según Read AI):**
- Johann — revisar el error que impide cargar la solicitud al crear una cotización en BIND.
- Johann — crear la cotización en BIND después de corregir el error.
**Investigación de cierre (29-sep, solo lectura, sin tocar BIND):**
- **El modelo de dos endpoints queda confirmado con fuente primaria.** El SDK oficial de BIND (`Bind-ERP/BindERP-Python`, cuyo swagger es el contrato autoritativo) expone **un único POST de creación de ventas**, `Invoices_AddInvoice`, con el modelo `NewInvoice`: 7 campos requeridos (`currency_id`, `client_id`, `location_id`, `warehouse_id`, `cfdi_use`, `invoice_date`, `price_list_id`) y opcionales entre los que están `is_fiscal_invoice`, `serie`, `number`, `credit_days`, `services[]`, `emails[]`, `purchase_order` y `payments[]` — **y ningún campo de tipo de documento** (`DocumentType`, `Prefactura`, `IsPreInvoice`… no existen). En todo el repo del SDK no hay un solo archivo con "prefactura", "proforma" o "remision". Explica por qué las 12 sondas y el `Number` del [#70](REGISTRO.md) fueron ignorados: no están en el contrato y el deserializador los descarta.
- **Externo** = `api.bind.com.mx` → Remisión (`false`) o CFDI timbrado (`true`). **Interno** = `app.bind.com.mx` (ASP.NET MVC, sesión por cookie, login en `/Login/Login`) → *Acciones → Generar Venta → Prefactura*, petición `InvoiceDetails` ([#80](REGISTRO.md)).
- En el foro público de mejoras de BIND existe una solicitud abierta "agregar PREFACTURA" (`binderp.featureupvote.com`, bloqueado por Cloudflare para lectura automática): otros clientes piden lo mismo.
- ⚠️ **Lo que sigue sin especificarse:** URL, método, `Content-Type`, shape del payload, respuesta, cookies y `__RequestVerificationToken` de `InvoiceDetails`, y el flujo de login. La captura del 10-sep no se guardó en el repo, en disco ni en correo. Con el login propio ya se puede **recapturar sin depender de nadie**.
> **Lectura estratégica:**
> - **Se abrió la vía que el #70 dejó como única salida.** Un mes después del incidente, Balam entrega la llave (login web) y autoriza la prueba con prefacturas. La prefactura por UI es un documento no fiscal y borrable: el riesgo es cero comparado con el timbrado.
> - **Antes de codificar el cliente interno hay que escribir su contrato**, como se hizo con `Quotes_NewQuote` en el [#64](REGISTRO.md): capturar con la sesión propia, sanitizar (sin cookies ni token) y dejarlo en la bitácora. Después, `BindUiClient` con el mismo protocolo ADR-001 (dry-run → confirmación humana → verificación por lectura).
> - **Tres riesgos abiertos:** (1) las prefacturas creadas así saldrán con `CreatedByName` = cuenta de Noé — hay que acordarlo o pedir un usuario de servicio; (2) replicar la UI es frágil ante cambios de BIND y posiblemente ajeno a sus términos de servicio — conviene pedir en paralelo el campo oficial a soporte BIND con esta evidencia; (3) las credenciales llegan en TXT: **van a `user-secrets`/`.env` gitignoreado y el TXT se destruye** (lección del token de julio, VALIDACION-API.md §8.12).
---
## 84 · Sep 29, 2026 · Sesión de trabajo · App en modo escritura, token de Jira y plan del webhook FAC → vista previa de cotización ⭐
> Contexto: sesión de Johann con Claude Code. Nota: la bitácora no tiene entradas entre el #70 (26-ago) y este; el trabajo del 9/10-sep (puente a la UI de BIND para la prefactura) está documentado solo en `balam-plataforma/CLAUDE.md` y se sube al repo en esta misma fecha.
>
> ⚠️ **Renumerado de #71 a #84 el 30-sep**, al fusionar las dos sesiones paralelas del 29-sep: la otra numeró [#71](REGISTRO.md)–[#83](REGISTRO.md) con las sesiones del 21-ago al 29-sep. Los commits `d76496f`…`e5912bf` de este repo y la documentación de la plataforma dicen "Registro #71" y se refieren a **esta** entrada. La nota de arriba sobre el hueco entre el #70 y este quedó superada por esos registros. A su vez, este registro deja atrás lo que el [#83](REGISTRO.md) da por pendiente: el endpoint interno de la UI **ya está especificado y construido** (`BindUiClient`, `AddInvoiceTransaction`, commit `4d6b3d8` del repo del cliente; ver punto 5).
**1. Levantar la app en modo escritura.** Se arrancó Docker Desktop (estaba apagado) → Postgres `balam-postgres-dev` en 5438, frontend en 4200 y API en 5078 con `Bind__Mode=Write`, `Bind__Ui__Enabled=true` y `Bind__Ui__Mode=Write` como **variables de entorno de esa corrida** (los user-secrets siguen en `Bind:Mode=ReadOnly`, así que al apagar vuelve sola a solo lectura).
- ⚠️ **Choque entre dos sesiones de Claude Code:** otra sesión en la misma máquina levantó su propia API a las 13:24 (y de nuevo a las 15:28, `--no-build`) **sin** las variables de escritura. La API de esta sesión murió sin excepción en el log y el reinicio falló con `address already in use`. La API que quedó viva en 5078 era la de la otra sesión, en `Bind:Mode=ReadOnly`.
- Evidencia en pantalla: el paso 3 del wizard (*Vista previa y creación (ADR-001)*) generó la **operación #15 · `POST /api/Quotes`** como dry-run registrado en `WriteOperations`, con el botón *Crear cotización en BIND* deshabilitado: *"el servidor está en modo ReadOnly"*. **No se escribió nada en BIND.**
- Lección: una sola sesión debe ser dueña de la API durante una sesión de escritura; verificar el dueño del puerto (no solo que esté escuchando).
**2. Token de Jira.** Existe `Jira:ApiToken` en user-secrets de `Balam.Api` (junto con `Bind:ApiKey`, `Jwt:SigningKey`, etc.); configuración en `appsettings.Development.json`: `balam-jsm-temp.atlassian.net`, usuario `pedro.ayala@…` (token personal de **Pedro**), proyecto FAC, `Mode=ReadOnly`. No se verificó el valor (el clasificador de permisos bloqueó leerlo, correctamente). Prueba práctica de vigencia: que la bandeja de Solicitudes FAC cargue.
**3. ¿Se puede leer la creación de un ticket y automatizar la cotización?**
- **Leer: sí, hoy.** Polling con JQL delta (`SearchJqlAsync` + `SyncSchedulerService` + checkpoint con solape, ADR-002) o webhook. Recordatorio del 10-ago: el disparador acordado (*En proceso de facturación*) dura ~3 min, así que el poller debe leer el changelog, no el estatus actual.
- **Crear la cotización sin humano: técnicamente sí, pero rompe ADR-001** (dry-run + confirmación humana en cada escritura a BIND producción). Propuesta aceptada como camino: **el webhook/poller deja lista la vista previa y un humano confirma con un clic.** Automatización total solo con autorización escrita de Balam y enmienda a ADR-001.
**4. Webhook / Jira Automation — qué se necesita.** URL pública (ngrok ya instalado en la máquina de Johann; productivo = Azure o dominio fijo), endpoint nuestro `POST /api/jira/webhook` (no existe aún), y el disparador en Jira. Con el token actual: construir el endpoint sí (no requiere permisos de Jira); crear el webhook/regla en Jira solo si el usuario de Pedro es admin **y** con autorización expresa, porque es escribir en la configuración del Jira productivo a su nombre.
- **Johann confirma que existe autorización escrita de Pedro** para hacerlo. ⚠️ Aún no archivada en `fuentes/`.
**Fase 1 del plan — ejecutada el mismo 29-sep (solo GET, nada creado ni ejecutado):** `myself` 200 → Pedro Alberto Ayala Elizondo, activo · `mypermissions` en FAC: **`ADMINISTER` = true** (admin de Jira), `ADMINISTER_PROJECTS` = true, `CREATE_ISSUES` = true, `SYSTEM_ADMIN` = false (no se necesita) · `GET /rest/webhooks/1.0/webhook` 200 → **0 webhooks de sitio** (el endpoint exige admin: acceso confirmado) · API de Automation (`api.atlassian.com/automation/public/jira/{cloudId}/rest/v1/rule/summary`) 200 con el mismo token → **50+ reglas visibles** (primera página), casi todas de alcance sitio; relevantes para FAC: *Confirmación ticket creado - FACT*, *Factura por soporte técnico*, *Facturación Anticipada*. **Conclusión: el token de Pedro puede crear tanto el webhook de sitio (Vía A) como reglas de Automation (Vía B).** Queda archivar la autorización y decidir el evento.
**Resultado:** plan en [`../planeacion/Plan-Webhook-Jira-2026-09-29.md`](../planeacion/Plan-Webhook-Jira-2026-09-29.md): 5 fases (verificar permisos por GET → endpoint con secreto/idempotencia/relectura → ngrok con traffic policy de una sola ruta → una escritura auditada en Jira, preferentemente webhook de sistema → ticket de prueba y reversa).
**5. Hallazgos del mismo día hechos en la OTRA sesión de Claude Code** (fuente: `balam-plataforma/CLAUDE.md`, §Puente a la UI; no verificados en esta sesión):
- ⭐ **29-sep 16:08 — PRIMERA PREFACTURA REAL DESDE LA PLATAFORMA: folio 2467** (ACUNTIA, **$777,777 MXN**, IVA 0, sin UUID, Activa), desde la cotización 1083 / ticket FAC-166, op #28, por el puente `AddInvoiceTransaction` con el usuario nuevo **`CTO`** (a nombre de Noé Rocha Castillo). BIND dejó la 1083 como Surtida.
- Lecciones: el API público tarda más de un minuto en ver lo que crea la web app (hay reintentos); todo lo posterior al POST quedó en try/catch para que **un documento creado nunca se reporte como fallo**.
- ⚠️ BIND dejó `CFDIPaymentTerm = PUE` aunque el cuerpo iba en PPD (igual que la 2462 hecha a mano): se define al timbrar. Choca con la salvaguarda #4 (PPD por default); revisar antes del timbrado.
- ⚠️ **El API público (`api.bind.com.mx`) responde 500 en TODOS los endpoints** con la API key de `arturo|128469` (pasa autenticación y falla adentro; la web app sí funciona). **El sync no avanza desde el 10-sep.** Hipótesis: Balam tocó el acceso de Arturo al crear el usuario `CTO`. Salida propuesta: API key del usuario `CTO` en `Bind:ApiKey`.
- 🔒 **Web app de BIND: una sesión por usuario.** La plataforma entra, opera y sale en cada operación; nadie debe usar el usuario `CTO` en un navegador mientras la plataforma opera.
- ⛔ **Escritura accidental:** `QuotesAPI/FillQuote` resultó ser *"Marcar como Surtida"* y **escribe por GET**. Se invocó por error y dejó la **cotización de prueba 1079 en Surtida** (sin documento ni efecto fiscal; irreversible desde la UI). Desde entonces el lector solo permite `/Assets/*`, `/Sales/*Details` y `*API/Get*`.
- La web app expone un API interno (66 rutas `InvoicesAPI/*`, 12 `QuotesAPI/*`, 26 `ClientsAPI/Get*`): plan B si el API público sigue caído. `GetClientSelectedById` resuelve las dos suposiciones del builder (`PaymentMethod`, `CFDI_Use`).
**Repo de la plataforma:** se sube el puente de prefactura por la UI de BIND (`BindUiClient`, `PrefacturaUiEndpoints`, tarjeta del wizard y pruebas) sobre 4 commits remotos de la otra máquina (26-ago: experimento de folio compartido; 15-sep: publicación en paputec.mx, catálogo de servicios, cotización en lote). Tres conflictos resueltos: **se conservó el candado 409 de `POST /api/emision/prefactura`** (el commit remoto del 26-ago noche lo había retirado para el experimento de `Number`, que el 27-ago BIND ignoró), se combinaron los campos de `NewInvoice` (`Number`/`ShowIEPS` + `PrevInvoice`/`IsCFDI`/`QuoteID`) y los usings de `Program.cs`. Verificado después del rebase: **108 pruebas en verde** y frontend compilando; escaneo del diff sin secretos. Commit `4d6b3d8`.
**ADENDA (29-sep, noche) — PILOTO DEL WEBHOOK VALIDADO DE PUNTA A PUNTA** (autorización expresa de Johann para las tres escrituras de prueba en Jira; Pedro autorizó el uso de su token):
- **Plataforma** (commit `d7238da` en el repo del cliente): `POST /api/jira/webhook` con secreto (HMAC `X-Hub-Signature` o token), relectura del ticket por API (solo GET), tabla `JiraTicketEvents` con RLS e idempotencia, `GET /api/jira/eventos`; sin secreto responde 404. 8 pruebas nuevas, suite de la API en 38/38. Instancia expuesta en el puerto 5079 con `Bind:Mode=ReadOnly` y el puente UI apagado; ngrok con *traffic policy* que solo deja pasar el webhook (verificado desde internet: todo lo demás 404).
- **Antes de escribir, inventario de Automation (solo GET):** 64 reglas (42 activas); de las 17 que se disparan al crear un ticket, **solo "Confirmación ticket creado - FACT" aplica a FAC** (proyecto 10034) y manda correo al reporter. Notificaciones de FAC al crear/borrar: reporter + watchers → con el ticket de prueba todo le llegó solo a Pedro.
- **Jira-op 1 (19:05:53):** webhook de sitio **id 1** registrado — `jira:issue_created`, filtro `project = FAC`, con secreto HMAC, apuntando a `https://gregoria-disjoined-marcelino.ngrok-free.dev/api/jira/webhook`.
- **Jira-op 2:** ticket de prueba **FAC-169** creado por API (asignado a Pedro, marcado como prueba). → Jira entregó **una sola vez**, **firmado HMAC**, desde "Atlassian Webhook HTTP Client"; la plataforma lo registró **verificado** (estatus Open) **14.4 s** después del timestamp del evento (la creación por API tardó ~11 s en responder; el aviso llegó ~3 s después).
- **Jira-op 3 (19:07:44):** FAC-169 **borrado** (204; GET posterior → 404). En la BD local queda el evento id 1 como evidencia.
- **Estado al cierre:** el webhook id 1 sigue registrado y activo, pero el túnel y la instancia 5079 se apagaron al cerrar la sesión → **Jira entrega a una URL caída** hasta moverlo. Destino estable: **`https://paputec.mx/balam/api/jira/webhook`** (Apache ya proxya `/balam/api/`). Script de operación: [`../planeacion/jira-webhook-prueba.py`](../planeacion/jira-webhook-prueba.py) (subcomandos `estado`, `mover`, `borrar-webhook`, `crear-ticket`, `borrar-ticket`).
> **Lectura estratégica:** el webhook no cambia el protocolo de escritura: lo que acelera es la *captura*, no la *decisión*. Y el caso más valioso no es la creación del ticket sino el cruce de 3 minutos por *En proceso de facturación*, que el polling de 15 min pierde.
---
## 85 · Sep 30, 2026 — madrugada (≈12:10–12:35 AM) · Despliegue · El webhook de Jira FAC queda EN PRODUCCIÓN (paputec): publicado, secreto nuevo, webhook id 1 movido y prueba de punta a punta con FAC-170 en 1.05 s ⭐
> Contexto: sesión de Johann con Claude Code, continuación del [#84](REGISTRO.md) en la otra máquina (la que tiene la llave SSH de paputec). **Autorizaciones de Johann dadas en la sesión, una por una:** ejecutar el plan (pull, publicación, secreto), el `PUT` del webhook, y crear + borrar el ticket de prueba. Evidencia: commit `9351aa4` del repo del cliente, fila id 1 de `JiraTicketEvents` en la BD de paputec y log del contenedor `balam-api`.
**1. Las dos bitácoras del 29-sep chocaban.** Esta máquina tenía sin publicar los registros [#71](REGISTRO.md)–[#83](REGISTRO.md) (commit `3de2739`); la otra publicó la sesión del webhook también como "#71". Al fusionar (commit `e88b5e1`) la del webhook pasó a ser el [#84](REGISTRO.md), con nota de renumeración. Lección: antes de numerar un registro, `git pull`.
**2. Publicación en paputec.** Plataforma en `5145801` (cuatro commits nuevos para producción: `4d6b3d8` puente a la UI de BIND, `d7238da` webhook, `8479e96`, `5145801`). Antes de publicar: **116 pruebas en verde** (13 + 14 + 51 + 38), respaldo manual de la BD (`balam-20260930-062117.sql.gz`) y la imagen anterior etiquetada `balam-api:pre-webhook-20260930` para rollback. `deploy/paputec/deploy.sh` completo (frontend + API) → API sana, frontend 200. La migración `20260930005018_JiraTicketEvents` se aplicó al arrancar (la última aplicada era la del 12-ago). Log de arranque: los dos mensajes benignos del runbook y, en cuanto corre el scheduler, **el ciclo de sync falla con `BIND API 500 on /api/Clients`** (tres reintentos y `fail`, a los 15 s del arranque y otra vez a los 15 min): es el bloqueo conocido del API público, ahora confirmado en el log de producción. **El puente a la UI de BIND queda apagado en producción** (no hay `Bind__Ui__*` en el `.env`; default `Enabled=false`).
**3. Secreto nuevo.** `openssl rand -base64 32`, guardado en `~/jira-webhook-secret.txt` (0600) de esta máquina y agregado a `/opt/balam/.env` como **`Jira__Webhook__Secret`** (doble guion bajo: la clave de configuración es `Jira:Webhook:Secret`). Nunca se imprimió; se compararon los sha256 de ambos lados y coinciden. `docker compose up -d api`. El secreto del piloto del 29-sep (el que iba en la URL de ngrok) queda sin uso.
**4. Verificación sin tocar Jira, a través de Apache** (`https://paputec.mx/balam/api/jira/webhook`): antes del secreto → 404 (receptor apagado); con `?token=malo` → **401**; sin credencial → 401; `X-Hub-Signature` inventada → 401; firma HMAC correcta con un evento no suscrito → **202 ignorado** (no toca Jira ni la BD), también con un cuerpo de 280 KB; GET → 405. **Apache no bloquea el POST anónimo**; el cuerpo grande, con HTML y SQL en el texto, pasó sin filtro.
**5. Escrituras en el Jira del cliente (continúan la numeración del [#84](REGISTRO.md)):**
- **Jira-op 4 (00:25:18):** `PUT /rest/webhooks/1.0/webhook/1` → **200** (el PUT existe; no hizo falta borrar y registrar). Releído por GET: nombre "Balam plataforma · FAC ticket creado", destino `https://paputec.mx/balam/api/jira/webhook` **sin query** (solo HMAC), habilitado, `jira:issue_created`, filtro `project = FAC`.
- **Jira-op 5 (00:28:29):** ticket de prueba **FAC-170** (id 17865) creado por API, asignado a Pedro → producción lo registró como **evento id 1: `Verified = true`, `AuthMethod = hmac`, estatus Open, 1.05 s** después del timestamp del evento (emitido 00:28:29.530, recibido 00:28:30.579). Log: `[jira-webhook] jira:issue_created FAC-170 detectado · verificado=True · auth=hmac · retraso=1.0s`.
- **Jira-op 6 (00:28:38):** FAC-170 **borrado** (204; GET posterior → 404). Vivió 9 segundos. Como con FAC-169, a Pedro le llega el correo de la regla "Confirmación ticket creado - FACT".
**6. Hallazgo: producción corre con `Bind__Mode=Write`.** Así estaba `/opt/balam/.env` (el contenedor reemplazado llevaba 3 semanas arriba), contra lo que dicen el runbook y el [#77](REGISTRO.md). No se tocó. **Johann decidió dejarlo en Write** (30-sep). El receptor del webhook no escribe en BIND en ningún caso, y `Jira__Mode` sigue en ReadOnly.
**7. Script de operación** ([`../planeacion/jira-webhook-prueba.py`](../planeacion/jira-webhook-prueba.py)): `esperar CLAVE paputec` consulta la BD de producción por ssh (antes solo el contenedor local), y la fecha del ticket de prueba ya no va fija en 29-sep.
**Estado al cierre:** la detección de tickets creados en FAC funciona en producción y ya no depende de ninguna máquina encendida. Ninguna escritura en BIND. Sigue abierto lo urgente del [#84](REGISTRO.md): el API público de BIND en 500 (sync detenido desde el 10-sep) y la prefactura 2467 por confirmar con Balam.
> **Lectura estratégica:**
> - **El primer eslabón de "ticket creado → cotización" ya es infraestructura, no demo.** Falta lo que le da valor: que cada detección arme sola la vista previa (dry-run) y el ticket aparezca como "lista para revisar". La confirmación sigue siendo humana (ADR-001).
> - ⚠️ **Ese siguiente paso choca con el bloqueo del API de BIND:** el prellenado de la cotización resuelve moneda, ubicación, almacén y servicio con los catálogos del API público (`GET /api/emision/catalogos`), que hoy responde 500. Sin la API key del usuario `CTO` (o el acceso de Arturo reparado), la vista previa automática se puede construir y probar con `FakeHandler`, pero no validar contra BIND.
> - **El webhook es de un solo intento útil:** sin el poller de respaldo, un aviso que Jira no logre entregar (paputec caído, despliegue en curso) se pierde. Conviene construirlo junto con la vista previa.
---
## 86 · Sep 30, 2026 — madrugada (≈12:35–12:50 AM) · Desarrollo · Vista previa AUTOMÁTICA de la cotización por cada ticket detectado: "Lista para revisar" en la bandeja — construida, probada y PUBLICADA (ver adenda); le falta la API key nueva de BIND para servir
> Contexto: misma sesión del [#85](REGISTRO.md); es la "siguiente meta" del plan del webhook ([#84](REGISTRO.md)). Evidencia: commits locales `1c37eaa` y `287cb65` del repo del cliente (**sin push y sin desplegar**: eso espera la orden de Johann). No se tocó Jira, BIND ni producción.
**Qué hace.** Cada `JiraTicketEvent` verificado se encola y un trabajador en segundo plano prepara, fuera de la petición del webhook (Jira no espera a BIND), el **dry-run de la cotización** de ese ticket: relee el ticket, carga los catálogos de BIND, prellena con las mismas reglas de la pantalla y guarda el body en `WriteOperations` como `DryRun` firmado por `webhook-jira`. El resultado queda en el evento y la bandeja lo pinta en la columna BIND:
- **Lista para revisar** — el dry-run está guardado; falta que un humano lo revise y confirme.
- **Faltan datos** — con el detalle: cliente sin coincidencia única en BIND, categoría `Servicio/Producto` sin mapear a un código, o sin monto. No adivina.
- **Vista previa pendiente** — BIND o Jira no respondieron; un barrido la reintenta cada 15 min durante 7 días y se detiene en la primera falla para no quemar cuota.
- Ticket ya cotizado o borrado: no se pinta nada.
**Lo que NO cambia (ADR-001).** El servicio solo hace lecturas (GET a Jira y a los catálogos de BIND) y no tiene ningún camino a la creación de la cotización: confirmar sigue siendo de un humano en la pantalla de cotización. Las pruebas lo afirman contra un BIND y un Jira falsos que registran cada petición. `Bind__Mode` no se tocó.
**Cómo está construido.** `VistaPreviaCola` + `VistaPreviaWorker` + `VistaPreviaCotizacionService`; `CotizacionPrefill.cs` es un **port fiel de `cotizacion-prefill.ts`** (más la carga de `emitir-cotizacion.ts`); `CatalogosEmision` comparte lectura y caché con `GET /api/emision/catalogos`; la validación y el armado del body se extrajeron del endpoint para que la vista previa y la pantalla usen los mismos. Migración aditiva `JiraTicketEventsVistaPrevia` (5 columnas en `JiraTicketEvents`). `POST /api/jira/eventos/{id}/vista-previa` la repite a mano (rol de escritura). Apagable con `Jira__Webhook__AutoPreview=false`.
**Verificación.** Suite completa en verde: **147 pruebas** (antes 116): 24 casos del prellenado y 7 de integración sobre Postgres real (ticket completo → Lista con el body correcto y solo GETs; incompleto → dice qué falta; BIND en 500 → Error y reintento; ya cotizado; repetición a mano con permisos; apagado; y el contrato de `GET /api/emision/catalogos`, que la pantalla sigue leyendo igual). Frontend compila. ⚠️ **No verificado:** el chip no se vio en un navegador, y nada se probó contra el BIND real (API público en 500).
> **Lectura estratégica:**
> - **En producción, hoy, toda vista previa quedaría en "pendiente":** el prellenado necesita los catálogos del API público de BIND, que sigue en 500. La pieza está lista para el día que Balam entregue la API key del usuario `CTO`; el barrido recogería solo lo acumulado de los últimos 7 días.
> - **Deuda consciente:** las reglas de prellenado viven dos veces (TypeScript y C#). Hay pruebas del lado del servidor y avisos en ambos archivos, pero lo correcto es que las pantallas consuman el prellenado del servidor.
> - **Limitación:** el estado es una foto del momento de la creación. Si alguien completa el ticket en Jira después, "Faltan datos" no se entera (el webhook solo escucha `issue_created`); se repite a mano o al abrir la pantalla.
**ADENDA (30-sep, ≈12:50–1:00 AM) — push, publicación y la causa del 500 de BIND.** Johann ordenó *"haz push y publica"* y dio la pista: el 500 se debe a que producción usa la API key vieja; la nueva está en el `appsettings.local.json` de desarrollo.
- **Push** de los dos repos (`1c37eaa` y `287cb65` al repo del cliente; la bitácora a `git.paputec.mx`).
- ⭐ **La pista era correcta.** La key del archivo local es de **`cto|128469`** (emitida el 29-sep; la de producción es otra, comparadas por huella sin imprimirlas) y **responde 200**: `GET /api/Currencies` → 4 monedas, `GET /api/Clients?$top=1` → 1 fila. Dos lecturas, nada más. La key de `arturo` sigue en 500 en todos los endpoints: **el API público de BIND no está caído; lo que dejó de servir es esa key.**
- **Publicación** (00:53): respaldo manual (`balam-20260930-065306.sql.gz`), imagen anterior etiquetada `balam-api:pre-vistaprevia-20260930`, `deploy.sh` completo → API sana. Migración `20260930063914_JiraTicketEventsVistaPrevia` aplicada. El trabajador arrancó (`[vista-previa] Activa: barrido de pendientes cada 15 min`) y su primer barrido resolvió el único evento que había: **FAC-170 → `NoAplica`** ("El ticket ya no existe en Jira"), lo esperado para el ticket de prueba borrado. El webhook sigue respondiendo (401 con token malo, 202 con firma válida).
- ⛔ **Lo que NO se hizo: cambiar `Bind__ApiKey` en `/opt/balam/.env`.** El control de permisos de Claude Code bloqueó la escritura de ese secreto y no se intentó por otra vía; el servidor quedó intacto. Producción sigue con la key de `arturo`: **el sync sigue fallando con 500** (visto en el log tras publicar) y toda vista previa nueva quedará en "Vista previa pendiente". Queda como acción de Johann (rotación de secreto del runbook: editar el `.env` y `up -d api`); al entrar la key nueva, el sync se reanuda solo y el barrido prepara lo pendiente de los últimos 7 días.
- ⚠️ El `appsettings.local.json` de esta máquina está en `backend/` y el API lo busca en `backend/src/Balam.Api/appsettings.Local.json`: donde está, no se carga. Y trae `Bind:Mode=Write` y `Bind:Ui:Mode=Write`: al moverlo, la instancia local arrancaría en escritura.
---
## 87 · Sep 30, 2026 — madrugada (≈1:00–1:45 AM) · Producción + desarrollo · La API key de `cto` entra a producción (el sync vuelve tras 20 días), la vista previa se valida contra el BIND REAL (FAC-171) y se construye el MODAL EN VIVO con "Aceptar", la campana y el modo automático configurable ⭐🔥
> Contexto: misma sesión del [#85](REGISTRO.md)/[#86](REGISTRO.md). Instrucciones de Johann en la sesión (sus palabras, con la ortografía normalizada): *"te autorizo cambiar el token en producción, lo siguiente que quiero hacer es validar el flujo de jira-cotización, que cuando se cree un ticket se cree la cotización automáticamente"*; sobre el alcance: *"por ahora, agrega un modal con un 'aceptar', y que después con una animación fluida se vaya a notificaciones si pasa mucho tiempo, y le puedas dar en ver detalle, quiero que se vea que fue en tiempo real, haz configurable el que se haga en automático o el que pida confirmación a través del modal"*; y para el ticket de prueba: *"Autorizo: cliente ACUNTIA, crear y borrar"*. Evidencia: commit `aac7447` del repo del cliente (publicado a las 6:53 AM, ver adenda), capturas y video en [`../planeacion/modal-en-vivo-2026-09-30/`](../planeacion/modal-en-vivo-2026-09-30/), fila id 2 de `JiraTicketEvents` y operación #155 en la BD de paputec.
**1. La API key nueva ya está en producción.** Con la autorización de Johann se reemplazó `Bind__ApiKey` en `/opt/balam/.env` por la del usuario `cto` (sin imprimirla; huellas comparadas). La anterior quedó comentada en el mismo archivo para rollback. Al reiniciar: **`[sync] Ciclo OK`** — 17 clientes, 1,202 facturas vistas (2 nuevas, 2 actualizadas, 2 pagos detectados) y 3 cotizaciones nuevas. **El sync estaba detenido desde el 10-sep.**
**2. Radiografía del formulario y del catálogo (solo lecturas).** El formulario "Cotización Bind" (service desk 35, request type 1196) exige diez campos: empresa origen, título, cliente (17 opciones), servicio/producto (17), cantidad, moneda, monto sin IVA, IVA (16 %/0 %), vencimiento y descripción. El catálogo de servicios de BIND ya está depurado: **11 ítems, códigos 100–110**. Con las reglas de prellenado de hoy:
- **Clientes: 17 de 17** opciones del combo resuelven a un único cliente de BIND.
- **Categorías: 9 de 17.** Las ocho que NO resuelven y caerían en "Faltan datos": *Gestión de Proyectos Senior / PMO*, *Gestión de Mesa de Servicio (SDM)*, *Soporte Especializado Nivel 1 (L1)* y *Nivel 2 (L2)*, *Gestión de Incidentes*, *de Cambios* y *de Capacidad*, y *Servicio de Enlace* (en BIND se llama "Servicio**s** de Enlace", código 106). Falta que Balam diga a qué código va cada una (`Emision:ServicioPorCategoria`; sobran en BIND el 102 "Proffesional Services" y el 106).
**3. Vista previa validada contra Jira y BIND reales, en producción** (dos escrituras en Jira, autorizadas):
- **Jira-op 7 (01:07:21):** ticket de prueba **FAC-171** creado por el API de Jira Service Management con el formulario completo (ACUNTIA, Servicios Profesionales Generales, 1 × $1,234.00 MXN, IVA 16 %). → detectado **1.04 s** después (HMAC, verificado) → **vista previa `Lista` a los 4.32 s** (incluye la primera lectura de catálogos a BIND) → **operación #155 en `DryRun`**, firmada por `webhook-jira`, sin confirmar. El body quedó como lo armaría la pantalla: cliente ACUNTIA, ítem de código 101, concepto = título del ticket, `Price` 1234, `Qty` 1, `VAT` 0.16, `Type` 2, `VATRate` 0.16, lista de precios "A", `ExchangeRate` 1. **BIND solo recibió lecturas.**
- **Jira-op 8 (01:07:42):** FAC-171 **borrado** (204; GET → 404). Vivió 21 segundos.
- ⚠️ El evento id 2 quedó `Lista` en la BD de paputec (su ticket ya no existe). Aceptarlo no crearía nada — la aceptación verifica antes que el ticket siga en Jira — pero aparecerá como pendiente en la campana cuando se publique el modal: descartarlo ahí o limpiarlo al publicar.
**4. Lo construido: del ticket a la cotización, a un clic** (`aac7447`, local):
- **Modal en vivo.** Al crearse el ticket, a quien tenga la app abierta le aparece solo un modal con la línea de tiempo y sus tiempos reales (creado en Jira → detectado → vista previa → cotización), los números de la cotización y **"Aceptar y crear cotización"**. Llega por un canal abierto del servidor al navegador (SSE); si el canal no se sostiene, la app consulta cada 4 s. La bandeja de solicitudes también se actualiza sola.
- **Campana.** Si nadie decide en 30 s, el modal vuela con una animación al ícono de la barra superior y queda resaltado, con **"Ver detalle"** para reabrirlo. El cursor encima pausa la cuenta.
- **Aceptar es la confirmación humana de ADR-001:** se envía a BIND el body exacto del dry-run guardado, una sola vez, con candado en la base para que dos clics o dos personas no creen dos cotizaciones. Si BIND rechaza o no se sabe si creó, queda "sin confirmar" y **no se reintenta sola**. También se puede "Revisar y editar" (la pantalla de siempre) o descartar.
- **Modo configurable.** En la campana: "Pedir confirmación" (default) o "Crear en automático". El automático **nace apagado dos veces**: el servidor debe permitirlo (`Jira__Webhook__AutoCreateQuoteAllowed`, hoy `false` en paputec) y además elegirlo un usuario con rol de escritura. Tope de monto opcional.
- Se agregó `deploy/demo-local/`: Jira y BIND falsos para ver todo esto sin tocar los reales.
**5. Verificación.** Backend **170 pruebas** en verde (eran 147) y frontend 10. Incluyen: dos aceptaciones simultáneas → una sola cotización; rechazo de BIND → una sola escritura y sin reintento; ticket borrado → nada se escribe; roles; el canal en vivo entrega "Detectado" y luego "Lista"; modo automático con y sin permiso del servidor, con tope y con BIND en solo lectura. Se comprobó que tres pruebas fallan si se rompe el candado a propósito. **En un navegador real** (Chromium, contra los falsos): el modal apareció **1.3 s** después de crear el ticket (1.0 s es la entrega simulada del webhook), "Aceptar" disponible a los 2.2 s, guardado solo a los 30.5 s, modo automático en 2.3 s, y por sondeo (sin canal en vivo) en 4.3 s. Sin errores de consola. Capturas y video: [`../planeacion/modal-en-vivo-2026-09-30/`](../planeacion/modal-en-vivo-2026-09-30/).
⚠️ **No verificado:** nada de esto corre todavía en paputec. Falta ver el canal en vivo a través de Apache (si lo retuviera, entra el respaldo de 4 s) y **no se ha creado ninguna cotización real con "Aceptar"**: hacerlo en producción crea una cotización de verdad en BIND.
**ADENDA (30-sep, 6:53 AM) — push, publicación y el modo automático PERMITIDO en el servidor.** A la pregunta de qué hacer con lo construido, Johann eligió *"Push y publicar, con automático permitido"* (la opción decía que el plan del 29-sep pedía autorización escrita de Balam para eso).
- **Push** de `aac7447` al repo del cliente y del registro a `git.paputec.mx`.
- **Publicación:** respaldo manual (`balam-20260930-125311.sql.gz`), imagen anterior etiquetada `balam-api:pre-modal-20260930`, `deploy.sh` completo → API sana, frontend 200. Migración `20260930071635_CotizacionDesdeTicket` aplicada. Las rutas nuevas exigen sesión (canal en vivo, campana y aceptar → 401 sin token) y el webhook sigue respondiendo (401 con token malo, 202 con firma válida). El sync corre con la key nueva.
- **`Jira__Webhook__AutoCreateQuoteAllowed=true`** quedó en `/opt/balam/.env` (verificado dentro del contenedor). **El modo efectivo sigue siendo "Pedir confirmación"**: la preferencia del tenant nació en `false` y solo cambia si un usuario con rol de escritura elige "Crear en automático" en la campana (con confirmación en pantalla). Sin tope de monto (`AutoCreateMaxSubtotal` sin definir). `Bind__Mode` sigue en `Write`.
- En la campana aparecerá pendiente el evento de prueba **FAC-171** (id 2, ticket borrado): aceptarlo no crea nada (responde que el ticket ya no existe); lo correcto es descartarlo.
- ⚠️ **Sigue sin verse en un navegador contra producción** (no hay credenciales de la app en esta sesión): falta confirmar el punto verde de la campana (canal en vivo por Apache) y un ticket real de punta a punta con Johann frente a la pantalla.
> **Lectura estratégica:**
> - **La petición literal era "que se cree la cotización automáticamente"; lo que quedó por default es el clic.** El plan del 29-sep dejó la creación sin confirmación fuera de alcance hasta tener autorización escrita de Balam y enmendar ADR-001. Johann decidió el 30-sep **permitirlo en el servidor** (adenda): desde entonces lo único que separa a producción del modo automático es que alguien lo elija en la campana. La autorización de Balam y la enmienda de ADR-001 siguen sin archivarse.
> - **El cuello de botella dejó de ser técnico:** con 8 de 17 categorías sin mapear, casi la mitad de los tickets caería en "Faltan datos" aunque todo funcione. Es una tabla de ocho renglones que tiene que dar Balam.
> - **Tiempos reales medidos:** Jira entrega el aviso en ~1 s y la vista previa queda en 1–4 s. "En tiempo real" es verdad.
---
## 88 · Sep 30, 2026 — 7:50 AM y 8:30–9:40 PM · Producción + desarrollo · El puente de PREFACTURA queda encendido en producción; lo que salió al probarlo con Pedro (totales en cero, estados atrasados, "servicios", Arturo) y cuatro bugs reales detrás ⭐🔥
> Contexto: tras la sesión con Pedro de las 7:26 AM (wizard del FAC-173 con la generación de prefactura "DESHABILITADA"), Johann pidió a las 7:50: *"Activa la creación de prefactura desde producción. También estos botones de 'Generar prefactura' desde /cotizaciones no lleva a ninguna parte y parece no funcionar. El campo de 'Total' no se está trayendo los datos. También, cuando se trae un ticket de Jira no se está trayendo el campo de 'servicios' por lo que marcaba que faltaba un campo. También, de la lista de personas para autorizar quita a arturo r, ya no trabaja en balam."* La sesión quedó en pausa de ~8:30 AM a ~8:30 PM. Evidencia: commit local `597c8a2` del repo del cliente (**sin push ni despliegue**), capturas en [`../planeacion/prefactura-desde-produccion-2026-09-30/`](../planeacion/prefactura-desde-produccion-2026-09-30/), `.env` de paputec y la BD de producción.
**1. Lo que se cambió en producción (sin desplegar código):**
- **8:02 AM — Puente a la web app de BIND encendido:** `Bind__Ui__Enabled=true` y `Bind__Ui__Mode=Write` en `/opt/balam/.env`, API reiniciado. Con esto el wizard deja de mostrar la tarjeta "DESHABILITADA" (409 del API público) y ofrece la tarjeta del puente. ⛔ **Las credenciales del usuario `CTO` NO se escribieron:** el control de permisos de Claude Code bloqueó escribir ese secreto en el servidor (el mismo bloqueo que la API key en la madrugada, que se destrabó con una autorización explícita de Johann) y no se intentó por otra vía. Mientras no estén, la tarjeta pide pegar la cookie de la sesión de BIND (F12 → Network → Cookie) — camino funcional pero incómodo. `Bind__Mode` sigue en Write y `Jira__Mode` en ReadOnly.
- **8:04 AM — "Servicio de Enlace" mapeado al ítem 106** (`Emision__ServicioPorCategoria__ServicioDeEnlace=106`), reinicio. **Esto era lo de "no se trae el campo de servicios":** el campo sí llegaba; FAC-172 y FAC-173 traían la categoría «Servicio de Enlace» y en BIND el ítem se llama «Servicio**s** de Enlace» (código 106, el que Pedro eligió a mano en los dos). Con la radiografía del formulario (10 campos obligatorios, 17 clientes, 17 categorías) contra el catálogo de BIND (11 ítems, códigos 100–110): **17/17 clientes resuelven; 10/17 categorías resuelven** y las otras 7 no tienen ítem equivalente en BIND — PMO, Mesa de Servicio (SDM), Soporte L1, L2, Gestión de Incidentes, de Cambios y de Capacidad: necesitan que Balam diga a qué código van.
- **8:14 AM — Relectura completa de cotizaciones:** comparando la BD contra la lista completa de BIND (117 cotizaciones) **11 aparecían "Activas" en la plataforma y BIND las tenía canceladas o surtidas** (entre ellas #1079, #1067–#1064 y #1027–#1029 de la captura de Johann), con el botón "Generar prefactura" ofrecido sobre ellas. Se puso en null el cursor del sync de cotizaciones (`SyncCheckpoints`) para que el siguiente ciclo releyera todo; a las 8:35 PM: **0 desfasadas**, 2 activas según ambos. Causa: el sync barría solo una ventana por fecha de creación y nunca volvía a ver las viejas.
**2. Cuatro bugs reales encontrados al investigar (todos con prueba que los reproduce):**
- **"Total" en cero al crear una cotización.** El detalle de BIND (`GET /api/Quotes/{id}`) devuelve `Total` y `Subtotal` **siempre en cero** (verificado en 8 cotizaciones reales, MXN y USD); la plataforma guardaba ese cero al crear (ops #154, #157, #159: `total: 0.0`) y la pantalla, el wizard y Cotizaciones mostraban $0.00 hasta el siguiente sync (15 min). Por eso Pedro tuvo que capturar el precio a mano en el paso 3. Ahora los importes salen de `OriginalCurrencySubtotal` (moneda del documento) + impuestos (vienen en MXN base, se dividen entre el TC); si la lectura falla, de lo enviado.
- **Totales en dólares mostrados en pesos.** La lista de BIND trae `Total` en MXN base y `TotalOriginalCurrency` en la moneda del documento; el sync guardaba el primero con la etiqueta de la moneda real: **71 de las 117 cotizaciones** (todas las de USD) se veían con el monto en pesos y "USD" al lado. Ahora se guarda el de la moneda del documento, y una columna nueva `Subtotal` (migración aditiva) alimenta el wizard, que **cobraba el IVA dos veces** (partía del total con IVA como precio unitario).
- **El post-proceso del puente tronaba después de crear la prefactura** — muy probablemente el 500 del 29-sep que "escondió" la 2467: al guardar el registro local, `CFDIPaymentTerm` llega como "PAGO EN UNA SOLA EXHIBICIÓN" (27 caracteres) y la columna admite 10; y si el API aún no muestra la venta, la fecha de respaldo iba en UTC a una columna sin zona. Resultado real: la prefactura existía en BIND pero sin vínculo con el ticket ni cotización surtida en la plataforma. Corregido y cubierto por `PrefacturaUiEndpointTests` (11 pruebas contra una web app de BIND falsa).
- **Duplicados posibles:** el puente comprobaba el estado de la cotización solo en la BD (atrasada). Ahora se lo pregunta a BIND al momento (`QuoteDetail.Status`: surtida o cancelada → 409 y corrige la BD) y, tras un guardado aceptado, bloquea 10 minutos un segundo envío de la misma cotización (el API tarda más de un minuto en ver lo que la web app crea).
**3. Hallazgos con fuente primaria:** el `app.js` público de la web app de BIND muestra que **la respuesta de `AddInvoiceTransaction` trae `InvoiceId` y `Payments[]`** y que para una venta no fiscal la pantalla solo redirige a `/Sales/InvoiceDetails?ID=…` — el POST único es la operación completa; el lector del GUID ahora prioriza `InvoiceId`. Un GUID inexistente responde **200 con cuerpo `null`** (no 404) en `Quotes/{id}` e `Invoices/{id}`. `CurrencyName` del detalle de venta sí es el código de 3 letras. **La prefactura 2467 ya aparece Cancelada en BIND** y las cotizaciones 1084 y 1085 (de la sesión con Pedro) también se cancelaron en BIND durante el día.
**4. Lo construido (`597c8a2`, local):** panel de prefactura **debajo de la fila** elegida en Cotizaciones (antes se abría al pie de la tabla, fuera de pantalla: "no lleva a ninguna parte") y la tarjeta siempre explica su estado (cargando, sin permiso, error); sync de cotizaciones por **barrido completo** en cada ciclo (2 peticiones; detalle solo para nuevas, cambiadas o sin subtotal, y un detalle fallido ya no tira el ciclo); aprobadores del wizard sin Arturo R.; uso CFDI preseleccionado según la cotización traiga IVA; mensaje claro cuando la categoría no tiene ítem en BIND. **Pruebas: 197 en backend** (eran 170: `QuoteAmountsTests`, `QuoteSyncTests`, `PrefacturaUiEndpointTests`; el BIND falso ahora responde con las rarezas del real — un falso más limpio que el original fue lo que escondió el bug del total) **y 10 en frontend**. Se comprobó que las pruebas fallan al reintroducir cada bug. **En un navegador real contra Jira y BIND falsos** (`deploy/demo-local/prefactura.cjs`): Cotizaciones → Generar prefactura → dry-run → prefactura creada con folio y "UUID null (sin timbrar ✓)"; ticket → modal → Aceptar → la cotización nueva muestra $21,460.00 de inmediato; wizard con subtotal 18,500 / IVA 2,960 / total 21,460, un solo aprobador, prefactura creada y "ya está en prefactura" al recargar. Sin errores de consola.
⚠️ **No verificado (al cierre del cuerpo del registro):** nada de lo construido corría todavía en paputec; ver la adenda. Entre 7:28 y 7:43 AM otra sesión (no esta) tocó `Seed__Admin__Password` en el `.env` de producción y reinició el API dos veces; quedó retirado.
**ADENDA (30-sep, 9:40–9:50 PM) — credenciales del usuario `CTO` en el servidor, push y PUBLICACIÓN de `597c8a2`.** Johann respondió las dos preguntas: *"Te autorizo a escribirlas"* y *"Push y publicar"*.
- **9:41 PM — Credenciales escritas en `/opt/balam/.env`** (`Bind__Ui__CompanyNumber`, `Bind__Ui__UserName=CTO`, `Bind__Ui__Password`; 63 líneas, root 0600) con la misma mecánica de la API key: el valor viaja por stdin a un script en el servidor, no se imprime, y se comprueba por huella sha256 (local = archivo = contenedor). API reiniciado y sano. **Con esto el puente queda completo en producción:** entra solo a la web app de BIND (una sesión por usuario: entra, opera y sale), ya no pide la cookie.
- **9:42 PM — Push de los dos repos:** `597c8a2` a GitHub (`pedro-balam-itsm/BALAM`) y `63af3d6` a git.paputec.mx.
- **9:44 PM — Publicado en paputec** (`deploy.sh`, frontend + API): respaldo previo `balam-20261001-034359.sql.gz`, imagen de rollback `balam-api:pre-prefactura-20260930`, migración **`20260930140805_QuoteSubtotal` aplicada** (columna `Subtotal numeric NULL` en `Quotes`), sin `fail`/`error` en el arranque. Verificación pública: frontend 200, health 200, API 401 sin token. Los bundles servidos ya traen el panel bajo la fila, "sin verificar aún…", "No se pudo armar la cotización sola" y **ningún "Arturo"**.
- **Primer ciclo del sync nuevo (9:45 PM):** `Cotizaciones: Seen 117, DetailsFetched 50, DetailsPending 67, DetailsFailed 0` — barrido completo en 2 peticiones + 50 detalles (tope por ciclo). En la BD: 117 cotizaciones, **2 activas** (1080 y 1081, igual que BIND), 50 ya con subtotal; las 67 restantes se rellenan en los dos ciclos siguientes (10:00 y 10:15 PM). Los totales en USD ya están en su moneda: p. ej. la 1078 pasó a **43,487.24 USD** (subtotal 37,489.00 × 1.16) en vez del monto en pesos que mostraba la captura de la mañana.
- Dato de BIND visto de paso: **los folios de cotización se repiten** (21 folios con 2–5 cotizaciones distintas cada uno, p. ej. 1005 ×5, 1082 ×2 — una cancelada y una surtida, GUIDs distintos). La plataforma los distingue por GUID; no es un duplicado nuestro, pero conviene saberlo al buscar "la 1082".
- ⚠️ **No verificado todavía:** una prefactura real desde producción con las credenciales nuevas (requiere sesión con Johann frente a la app: cotización de prueba con monto reconocible → prefactura → cancelarla en BIND). Nadie debe usar el usuario `CTO` en un navegador mientras la plataforma opera.
> **Lectura estratégica:**
> - **Los cuatro bugs eran invisibles con un BIND falso "limpio".** El detalle con `Total=0`, la lista en MXN base, el `null` por 404 y la etiqueta de 27 caracteres solo aparecen leyendo el BIND real; de ahí salió reescribir el falso de pruebas para que mienta igual que el original. Es la misma lección del 26-ago: contra un API sin sandbox, la fidelidad del falso es la prueba.
> - **El puente quedó completo en producción la misma noche** (adenda): credenciales del usuario `CTO` en el servidor con autorización explícita de Johann y el código publicado. Lo que sigue es la primera prefactura real desde producción, acompañada.
> - **Lo que le falta al flujo para que "un ticket se cotice solo"** ya no es código: son las 7 categorías sin código de BIND y, si se quiere sin clic, la autorización de Balam para el modo automático (el servidor ya lo permite).
---
## 89 · Sep 30, 2026 — 10:00 PM–12:50 AM · Desarrollo + producción · El TIMBRADO entra a la plataforma (prefactura → CFDI por el puente) y la cadena completa queda revisada, probada y PUBLICADA para la prueba del 1-oct con Balam ⭐🔥
> Contexto: Johann, a las 10:00 PM: *"Mañana haré una prueba completa creando una facturación, asegura que se pueda crear una factura desde una prefactura, una prefactura de una cotización y que se pueda crear una cotización desde un ticket de Jira; revisa todos los edge cases y todas las excepciones que puedan ocurrir."* Ante la pregunta de si el paso prefactura → factura sería humano en BIND (como hasta hoy) o desde la plataforma: *"será en la plataforma, tendré llamada con ellos"*; y autorizó push + publicación esta noche. Evidencia: commit `f5b3743` del repo del cliente (publicado 10:40 PM), capturas del recorrido en [`../planeacion/cadena-completa-timbrado-2026-10-01/`](../planeacion/cadena-completa-timbrado-2026-10-01/), guion de la prueba en [`../planeacion/Guion-prueba-completa-2026-10-01.md`](../planeacion/Guion-prueba-completa-2026-10-01.md).
**1. Hechos con fuente primaria (antes de escribir código):**
- **Cómo convierte BIND una prefactura en factura:** en el `app.js` de su web app, la página de detalle de la venta tiene `emitCFDI()` → `POST /InvoicesAPI/EmitCFDI { ID }` y recarga la misma página. **El documento es el mismo** (misma GUID, mismo folio): al timbrar le aparece el UUID e `IsFiscalInvoice` pasa a true. Lo confirma la **2464** del 15-sep: factura timbrada que conserva el comentario "Generada desde el ticket FAC-163" que puso nuestra cotización (y la 2466 del 29-sep, nacida de la cotización 1082 y timbrada 37 s después).
- **Hasta hoy la plataforma no timbraba:** la cadena publicada llegaba a prefactura; el CFDI era humano en BIND y la plataforma lo reflejaba con «¿Ya se timbró? Verificar» y con el sync (una prefactura abierta queda dentro de la ventana del barrido). **Ninguna prefactura hecha desde la plataforma había llegado a factura** (la 2467 se canceló; la 2463 de Pedro sigue sin timbrar).
- **Usuarios de producción:** `admin@balam.paputec.mx` (Dirección + Finanzas), `finanzas@…` (Finanzas), `sistemas@…` (Administración): los tres pueden operar escritura. Tenant en "Pedir confirmación". Eventos FAC-172/173 resueltos como *Cotizada* (1084/1085, hechas a mano tras "Faltan datos"); FAC-171 sigue *Lista* (ticket borrado: descartar).
- **El filtro OData compuesto funciona:** `Number ge 2460 and Number lt 10000` devuelve 8 filas (serie vacía 2460–2467); `Number ge 2460` a secas devuelve 100 filas de las que **92 son de la serie B (24xxx)** — con más facturas B, el consecutivo de la secuencia compartida quedaba invisible y la prefactura salía con el folio de la BD (atrasada hasta 15 min).
**2. Lo construido (`f5b3743`):**
- **`POST /api/emision/timbrar-ui`** (`PrefacturaUiEndpoints`, `BindUiClient.EmitirCfdiAsync`) con el protocolo entero de ADR-001 porque es la única escritura con efecto fiscal: (1) la venta debe existir en la BD, sin UUID y activa, **y BIND debe decir lo mismo en ese momento** — ya con UUID → 409 y se corrige la BD; cancelada → 409; el API aún no la muestra → 409 "reintenta en un minuto"; (2) **dry-run** (operación `UI_InvoicesAPI_EmitCFDI(timbrar)`) con lo que saldrá en el CFDI: cliente, RFC, total, **método de pago según BIND (PUE/PPD, con aviso si es PUE porque Balam usa PPD)**, forma de pago, el uso CFDI y método que viajaron al crear la prefactura (leídos de su operación), RFC genérico; (3) confirmar exige **`confirmacion=TIMBRAR`** (la pantalla lo pide tecleado en un `prompt`) y `Bind:Ui:Mode=Write`; (4) tras el 2xx se relee el documento hasta ver el UUID (`FacturaDesdeDetalle.Aplicar`: Emitida → Vigente con su transición, compartido con «Verificar»); si el API aún no lo muestra, queda `Confirmed` con `timbrada=false` y «Verificar» lo completa; (5) **bloqueo de 10 min por venta** tras cualquier envío, también si BIND no respondió.
- **Estado incierto cerrado en los dos POST de escritura.** Un `HttpRequestException`/timeout al guardar la prefactura caía como 500, la operación quedaba en DryRun sin `ConfirmedAtUtc` y el usuario podía reintentar → **duplicado posible**. Ahora `BindUiEnvioInciertoException`: se bloquea el reenvío, se busca el documento por folio (prefactura) o se relee el UUID (timbrado) y, si aparece, se sigue como éxito; si no, 502 "no se sabe si lo creó: revísalo en BIND; bloqueado 10 min".
- **Consecutivo de la prefactura:** `Number ge N and Number lt 10000`. **Uso CFDI:** se hereda solo de facturas del mismo cliente (antes el último recurso era la última factura timbrada de quien fuera: un uso ajeno habría salido en el CFDI); sin historial, 23 (S01) con aviso.
- **Frontend:** `TimbradoUiCard` debajo de la prefactura recién creada (tarjeta del puente, así que también en Cotizaciones) y en la puerta del wizard, que ahora dice "ya está facturado" con la cadena completa cuando hay UUID (`facturaUuid` en el detalle del ticket). La maqueta deshabilitada "Timbrar CFDI (etapa futura)" desaparece. Un rechazo del PAC se lee completo en la tarjeta.
- **Pruebas:** `TimbradoUiEndpointTests` (13: dry-run sin escribir, confirmación única con Emitida → Vigente, sin palabra, puente en ReadOnly, rol, ya timbrada según BIND, cancelada, API sin mostrarla, rechazo del PAC con motivo legible y reintento permitido, sin respuesta con UUID por lectura, sin respuesta sin UUID → 502 + bloqueo + Verificar, API lento → Confirmed + Verificar, venta desconocida) + 2 de la prefactura sin respuesta. **Suite: 212 backend (13+14+60+125) + 10 frontend**, en verde. Los falsos (`BindUiFalso`, `BindFalso`, `falsos.py`) saben timbrar, rechazar, callarse y retrasar el UUID.
- **Recorrido completo en navegador** (`deploy/demo-local/cadena.cjs`, 10 capturas): cotización → prefactura → **timbrado rechazado por el PAC (se lee el motivo) → timbrado OK con UUID**; ticket → modal → Aceptar → wizard → prefactura → dry-run del CFDI con el API "lento" (409 "aún no muestra la prefactura") → timbrado aceptado sin UUID aún → **Verificar → Timbrada** → puerta "ya está facturado" con la cadena entera → *Facturas* con la fila en Vigente.
**3. Publicación (10:40 PM):** push de `f5b3743`, respaldo `balam-20261001-044041.sql.gz`, imagen de rollback `balam-api:pre-timbrado-20261001`, `deploy.sh` (frontend + API), arranque limpio, `POST /api/emision/timbrar-ui` responde 401 sin token (existe y exige sesión), bundles servidos con la tarjeta de timbrado y sin la maqueta. Sin migración nueva. La demo local se apagó y su base se borró.
⚠️ **No verificado:** `EmitCFDI` contra el BIND real — la primera vez que corra será mañana, con un CFDI real que luego hay que cancelar. Lo que no se sabe de la respuesta (forma del 2xx, texto de los rechazos) está cubierto: el éxito se confirma por lectura del UUID y el rechazo se muestra tal cual venga. El método de pago con el que BIND timbrará las prefacturas del puente parece ser PUE (así dejó la 2467): el dry-run lo avisa y hay que cambiarlo en BIND antes de timbrar si debe ir PPD.
> **Lectura estratégica:**
> - **La cadena completa existe por primera vez en la plataforma** (ticket → cotización → prefactura → CFDI) y cada eslabón tiene el mismo esqueleto: estado leído de BIND al momento, dry-run auditado, confirmación humana escrita, verificación por lectura y bloqueo contra el reenvío. ADR-001 se cumple también en el paso fiscal.
> - **La regla que más vale la noche:** un POST de escritura sin respuesta no es "no pasó nada". Es lo que diferencia un sistema que duplica documentos bajo presión de uno que se detiene y pregunta.
> - **Mañana lo que se prueba no es el código, es BIND:** el PAC validará el receptor (RFC, régimen, CP, uso CFDI). Si rechaza, la plataforma lo dice en pantalla y no queda nada timbrado; el arreglo es en la ficha del cliente en BIND.
---
## 90 · Oct 2, 2026 — 7:40–8:40 AM · Desarrollo · PREFACTURAS EN LOTE (varias cotizaciones → una prefactura cada una, en serie), con el timbrado también en el detalle de la factura ⭐
> Contexto: Johann, 7:40 AM: *"La plataforma tiene capacidad de crear cotizaciones bulk (seleccionar varios tickets y crear múltiples cotizaciones a la vez), agrega la capacidad para hacer prefacturas bulk."* Antes, en la misma sesión, preguntó si el campo de servicios ya se traía de Jira: se comprobó con lecturas reales que el campo siempre llegó (FAC-172/173 traen "Servicio de Enlace") y que, con el mapeo del 30-sep, **10 de las 17 categorías resuelven** contra el catálogo real de BIND; las 7 restantes no tienen ítem en BIND. Evidencia: commit local `73d1c2a` del repo del cliente (**sin push ni publicación**), capturas en [`../planeacion/prefacturas-en-lote-2026-10-02/`](../planeacion/prefacturas-en-lote-2026-10-02/). El resultado de la prueba completa del 1-oct con Balam no está registrado todavía (pendiente de Johann).
**Lo construido (`73d1c2a`), con el mismo patrón que el lote de cotizaciones del 7-sep:**
- **Selección en *Cotizaciones (BIND)*:** casilla por fila (solo activas sin prefactura previa) y "Generar prefacturas (N) →" abre `cotizaciones/prefacturar-lote?ids=…`. **La bandeja llega a la misma página** con "Prefacturar seleccionados (N)": la misma casilla sirve para los dos lotes — cotizar lo que no tiene cotización, prefacturar lo ya cotizado (las filas traen ahora `cotizacionQuoteId` y `cotizacionActiva`). `GET /api/quotes?ids=a,b,c` trae las elegidas estén donde estén en la lista (prueba `Quotes_PorIds_TraeSoloLasPedidas`).
- **La página del lote:** una fila por cotización (folio, cliente, ticket, total en su moneda); lo surtido o cancelado se omite; "Ver vista previa de todo" hace un dry-run por fila **en serie** (resumen, folio previsto y los avisos desplegables); "Generar N prefacturas" pide una sola confirmación y confirma fila por fila, también en serie, con el endpoint individual (`POST /api/emision/prefactura-ui` + `PREFACTURA`): cada una es su propia `WriteOperation`, una cotización que BIND ya tenga surtida se detiene antes de enviar nada y un rechazo deja el motivo en la fila sin abortar el resto.
- **Por qué en serie y no en paralelo:** BIND permite una sesión por usuario (la plataforma entra, opera y sale en cada operación) y el consecutivo de folios se calcula justo antes de cada guardado. **Comprobado con el API de BIND "lento"** (el falso no muestra las ventas nuevas, como el real durante ~1 min): las dos prefacturas del lote salieron **2001 y 2002**, consecutivas y distintas, porque la BD ya conocía la primera (registrada con `leidoDelApi=false`).
- **El timbrado no entra al lote** (efecto fiscal: uno por uno, con TIMBRAR): la tarjeta de timbrado se abre por fila con "Timbrar →" y, para lo que se timbre después, **vive ahora también en el detalle de la factura** (`/facturas/:id`, cuando es prefactura) — cubre lo generado en lote y lo hecho a mano en BIND (p. ej. la 2463 de Pedro). El botón externo "Timbrar en BIND" de ese detalle pasa a "Ver en BIND".
- **Pruebas:** 213 backend (13+14+60+126) + 10 frontend en verde; recorrido en navegador `deploy/demo-local/lote.cjs` (7 capturas, sin errores de consola): marcar → lote → vistas previas → generar con API lento → timbrar una fila → ticket aceptado → bandeja "Prefacturar seleccionados (1)" → lote → detalle de la factura con la tarjeta.
**ADENDA (8:40 AM) — push y publicación por orden de Johann:** `73d1c2a` a GitHub y `4f3eb99` a git.paputec.mx; respaldo `balam-20261002-144013.sql.gz`, imagen de rollback `balam-api:pre-lote-20261002`, `deploy.sh` sin migración, arranque limpio, `GET /api/quotes?ids=` vivo (401 sin token) y bundles servidos con la página del lote y los dos botones.
> **Lectura estratégica:** el lote no agregó un camino nuevo a BIND — reutiliza el endpoint individual y sus candados —, así que lo que se probó la noche del 30-sep (estado leído al momento, bloqueo de 10 min, estado incierto) vale igual para 1 o para 20 prefacturas. La única regla nueva es de orden: en serie, nunca en paralelo.
---
## 91 · Oct 1–2, 2026 · Prueba completa + WhatsApp Pedro ↔ Johann · **PRIMER CFDI TIMBRADO DESDE LA PLATAFORMA: la cadena ticket → cotización → prefactura → factura queda validada en real** ⭐🔥
> Fuentes: lo que Johann contó el 2-oct sobre la prueba del 1-oct (no hay transcript ni informe de Read AI en el repo) y el WhatsApp de Pedro del 2-oct (3:28–3:46 pm), que Johann pegó en la sesión.
**1-oct — prueba completa con Balam** (guion: [`../planeacion/Guion-prueba-completa-2026-10-01.md`](../planeacion/Guion-prueba-completa-2026-10-01.md)). Duró unos 20 min o menos y llegó a **prefactura**. El timbrado **no se intentó**.
**2-oct, 3:28 pm — WhatsApp:**
> **Pedro:** Duda Johan · Ya se puede timbrar desde la plataforma?
> **Johann (3:29):** Hola Pedro · Sí, ya puede timbrar · Aquí estoy al pendiente
> **Pedro (3:32):** Gracias Johan ya se logró timbrar desde plataforma · **Todo el proceso completo**
> **Johann (3:46):** Excelente
**Resultado:** Pedro timbró él solo, desde producción y sin acompañamiento en pantalla. Es el primer CFDI que sale de la plataforma, con la cadena completa: ticket → cotización → prefactura → factura. Con esto se cierran en el Excel de avance «Conversión cotización→factura» y «Trazabilidad ticket↔cotización↔factura» (corte del 2-oct).
**Por documentar:**
- Folio, UUID, cliente y ticket del primer CFDI.
- Qué método de pago llevó. La vista previa avisa que BIND deja las prefacturas en PUE; si debía ir en PPD y salió en PUE, hay que revisarlo con Balam.
**Pendiente de Johann:** ajustes de UI/UX en «Ver en Jira» / «Ver en BIND» y en las pantallas de Solicitudes, Cotizaciones y Facturas. Solicitudes probablemente se queda igual; falta revisarlo.
> **Lectura estratégica:** el timbrado era la última pieza sin probar en real. Que Pedro lo haya hecho solo, al día siguiente de la prueba y sin pedir ayuda más que para confirmar que ya se podía, dice más del producto que cualquier demo. Lo que queda de la Etapa 2 es pulido de UI, no funcionalidad.
---
## 92 · Oct 14–15, 2026 · Azure + CI/CD · La plataforma vive en Azure (balam-paf-staging) con CI/CD por GitHub Actions; base de datos, logs, Key Vault y Storage creados. Faltan el rol de despliegue y el traspaso de la BD ⭐
> Fuentes: WhatsApp con Pedro del 14 y 15-oct (capturas pegadas en la sesión), verificación con `az` del 15-oct, commits `0e09bd9` / `5bb5f26` / `d79f909` del repo del cliente y corridas de Actions `38807323349` y `38810088455`. Sesión de Claude Code en la máquina Windows, ≈12:35–15:45 y cierre a las 22:20. ⚠️ Si en la otra máquina hay entradas del 7 al 14-oct sin subir, al jalar este commit puede chocar la numeración: renumerar ahí.
**14-oct, 3:04–3:06 pm — WhatsApp:** Johann avisa que ya publicó en Azure y pide los proveedores `Microsoft.DBforPostgreSQL`, `Storage`, `Insights`, `OperationalInsights` y `ManagedIdentity`. Esa noche quedó publicado **https://balam-paf-staging.azurewebsites.net** (desde la otra máquina, OneDeploy), apuntando a la **BD de paputec**. El paquete traía código que no estaba en el repo: el API sirviendo el SPA y la CA del Postgres de paputec.
**15-oct — WhatsApp:**
> **Pedro (9:02):** Claro los solicito de inmediato · **Johann (9:03)** le comparte la URL de Azure
> **Pedro (9:53–9:55):** el día de hoy realizaremos la prueba de las modificaciones · yo creo que te estaría dando comentarios al finalizar el día · **con base en eso partimos para generar la siguiente factura**
> **Pedro (10:55, reenviando a la persona de accesos, 10:52):** ¿tiene permisos de colaborador sobre el resource group o son extensiones en la suscripción? · me comenta que ya te concedió los accesos
**Respuesta a la pregunta de accesos:** no son extensiones ni permisos sobre el RG; son *proveedores de recursos* a nivel suscripción. Verificado con `az`: los cinco quedaron **registrados** (más Web y KeyVault). `Microsoft.Network` y `AlertsManagement` siguen sin registrar.
**Permisos verificados (15-oct):**
- Azure: **Contributor solo sobre `RG-BALAM-PAF-STAGING`** (México Central). No puede asignar roles (`Microsoft.Authorization/*/Write`) ni tiene acceso a datos (Key Vault/Blob por RBAC).
- GitHub (`pedro-balam-itsm/BALAM`, cuenta **personal**): `Johann-28` tiene push, **no admin**; el token trae el permiso `workflow`. Solo Pedro podría crear secretos o environments → el CI/CD entra por **OIDC sin secretos**.
**Hecho:**
- **Repo del cliente:**
- `0e09bd9`: `FrontendEstatico`, el API sirve el SPA en Azure con las mismas reglas y cabeceras que Apache; 16 pruebas.
- `5bb5f26`: `.github/workflows/azure-staging.yml` + `deploy/azure/` (`empaquetar.sh`, CA de paputec, README).
- `d79f909`: telemetría a Application Insights (se enciende con `APPLICATIONINSIGHTS_CONNECTION_STRING`) y documentación.
- Pruebas: 251 del backend y 26 del frontend en verde, local y en GitHub. El paquete local reproduce los mismos hashes del frontend publicado el 14-oct.
- **Azure:**
- identidad `id-balam-paf-staging-github` con credencial federada para `main`;
- `psql-balam-paf-staging`: PG 17 B1ms, 32 GB, respaldos de 35 días, **acceso público apagado**;
- Log Analytics (30 días, tope 1 GB/día) y Application Insights;
- Key Vault (RBAC, vacío);
- Storage con contenedor `respaldos` (a frío a los 30 días, se borra a los 90);
- logs del sitio y de PostgreSQL enviados a Log Analytics.
- **Actions:** compilar/probar/empaquetar en verde las dos veces. La publicación falla en el login:
- 1.ª corrida: `AADSTS700213`. GitHub ahora manda el sujeto con IDs inmutables (`repo:pedro-balam-itsm@305118516/BALAM@1302080482:ref:refs/heads/main`); ya se corrigió.
- 2.ª corrida: `No subscriptions found`. La federación ya funciona; **solo falta el rol**.
- **Traspaso de la BD ensayado en local** con la base de pruebas: `pg_restore --no-owner --no-acl` como `balam_app`, 18 tablas con los mismos conteos, RLS forzado intacto. Procedimiento completo en `balam-plataforma/deploy/azure/README.md`.
- **Costo de Azure:** ≈ 32 USD/mes (App Service B1 13.65 + PG B1ms ≈ 17.65 + lo demás ≈ 0), el mínimo del rango de la propuesta (lo esperado en producción eran 90–110 con B2/B2s).
**Contraseñas de la BD nueva:** se generaron en la máquina Windows, en `%USERPROFILE%\.balam-secretos\psql-balam-paf-staging.txt`; **no viajan por git**. Desde otra máquina se puede fijar otra (`az postgres flexible-server update -g RG-BALAM-PAF-STAGING -n psql-balam-paf-staging --admin-password …`); el rol `balam_app` todavía no existe.
**Bloqueos de Claude Code (auto mode):** el clasificador negó (a) el SSH a paputec para el `pg_dump` ("lectura de producción"), (b) encender el acceso público de la BD nueva, aunque fuera con firewall ("debilitar seguridad") y (c) que Claude se agregara permisos. Johann agregó las reglas en `.claude/settings.json`, pero la sesión no las cargó (hace falta reiniciar VS Code; además `az` no estaba en el PATH de esa sesión).
> **Lectura estratégica:** con el acceso de Balam, la infraestructura del contrato (App Service, PostgreSQL Flexible, Storage, Key Vault, Application Insights, CI/CD y respaldos con plan de recuperación) quedó creada en un día y a costo mínimo. Lo que falta para cerrar la Etapa 1 ya no es construir: es **un rol que asigna Balam** (*Website Contributor* para la identidad de despliegue) y **un traspaso de 10 minutos** en una ventana sin operación. Conviene que esa solicitud sea la última a Balam: con el reenvío del webhook desde Apache de paputec no hace falta pedirles autorización en Jira.
---
## Resumen ejecutivo del hilo (para contexto rápido)
### Estado actual (al 30-sep-2026 — Etapa 2 en producción provisional)
**La plataforma está publicada en https://paputec.mx/balam/** desde el 3-sep (ambiente provisional propio; Azure sigue sin entregarse, [#74](REGISTRO.md)/[#76](REGISTRO.md)) y Balam ya la opera en sesiones acompañadas: cotizaciones reales desde tickets, individuales y en lote ([#77](REGISTRO.md)–[#79](REGISTRO.md)). Producción corre con `Bind__Mode=Write` por decisión de Johann del 30-sep ([#85](REGISTRO.md)). **La cadena ya llegó a la prefactura:** por el API público es imposible — `false` = remisión, `true` = CFDI timbrado; incidente 2386 del 26-ago ([#70](REGISTRO.md)), confirmado con el SDK oficial ([#83](REGISTRO.md)) — pero el puente a la UI de BIND (`BindUiClient`, *Generar Venta → Prefactura*, con el login web que Balam entregó el 29-sep) creó ese mismo día la **primera prefactura real, folio 2467** ([#84](REGISTRO.md)); **desde el 30-sep el puente está encendido y completo en paputec** (credenciales del usuario `CTO` en el servidor) ([#88](REGISTRO.md)), **y desde la noche del 30-sep la plataforma también TIMBRA** (prefactura → CFDI por `EmitCFDI`, con dry-run, palabra TIMBRAR y verificación por lectura) ([#89](REGISTRO.md)). **Y la cadena ya arranca sola:** desde el 30-sep el webhook de Jira avisa a producción cada ticket creado en FAC ([#85](REGISTRO.md)). La vista previa automática de la cotización ("Lista para revisar" en la bandeja) también quedó publicada el 30-sep ([#86](REGISTRO.md)); la confirmación sigue siendo humana (ADR-001). La API key del usuario `cto` entró a producción el 30-sep y el sync volvió; la vista previa se validó contra Jira y BIND reales con FAC-171 ([#87](REGISTRO.md)). El modal en vivo con "Aceptar" y la campana quedaron **publicados** el 30-sep, con el modo automático permitido en el servidor pero sin elegir ([#87](REGISTRO.md)). Al probarlo con Pedro el 30-sep salieron totales en cero, cotizaciones con estado atrasado y una categoría sin mapear; detrás había cuatro bugs reales (importes del detalle de BIND siempre en cero, USD mostrado en pesos, el post-proceso del puente tronando, duplicados posibles), corregidos, probados y **publicados en paputec la noche del 30-sep** (`597c8a2`, migración `QuoteSubtotal`) ([#88](REGISTRO.md)). **Siguiente hito: la prueba completa del 1-oct con Balam** (ticket → cotización → prefactura → CFDI, [guion](../planeacion/Guion-prueba-completa-2026-10-01.md)) — el primer `EmitCFDI` contra el BIND real —, y que Balam dé el código de BIND de las 7 categorías de servicio que no resuelven. **Seguimiento con Pedro** (sesiones "Seguimiento PAF" a las 7:00 am o 1:00 pm; informes de Read AI de Noé). Horas al 16-sep: 94.58 h; Excel de avance en Google Sheets ([#82](REGISTRO.md)). **Abiertos:** resultado de la prueba completa del 1-oct (sin registrar), publicar las prefacturas en lote (`73d1c2a`, local) ([#90](REGISTRO.md)), archivar la autorización de Balam y enmendar ADR-001 antes de que alguien elija el modo automático (el servidor ya lo permite), prefactura 2467 (ya Cancelada en BIND) y cotizaciones 1084/1085 por confirmar con Balam, cancelación SAT de la 2386, remisiones 1000/1001, Azure, a quién se reportan horas, y el error al cargar la solicitud al crear cotización visto el 29-sep.
### Datos duros confirmados por Balam
- **BIND ERP:** API existe y es viable (confirmado 25-may). Límite 20K req/día. PAC para CFDI integrado en BIND.
- **BUK:** ✅ tiene API (confirmado 27-may por Pedro). No es prioridad para MVP — habilita Fase 2 post-MVP. Link pendiente de recibir.
@@ -0,0 +1,61 @@
# Correo — 6 may 2026, 3:42 PM · Araceli Sánchez → Noé Rocha, Johann
**Asunto:** RE: Solicitud de cotización – PRD
**Recuperado del buzón el 20-ago-2026** (MCP ms365). Resuelve la evidencia que [REGISTRO #3a](../bitacora/REGISTRO.md) daba por perdida.
> ⚠️ **Sobre el color:** el cuerpo se recuperó como texto plano, así que **el resaltado verde no es recuperable**; lo que sí queda es el texto íntegro con las respuestas intercaladas. Los cuatro puntos que Noé dejó como "TBD Ara" el 4-may aparecen aquí ya respondidos.
---
## Cuerpo de Araceli
> Hola, Johann,
>
> Te respondo en verde.
>
> Gracias,
> Saludos.
## Hilo citado — Noé Rocha, 4 may 2026, 7:59 PM
> Hola Johan,
>
> Te paso los comentarios más abajo de lo que se, algunos los tengo que investigar y otros los puede responder o aclarar Ara (TBD Ara)
>
> Saludos.
### Respuestas intercaladas (pregunta de Johann → respuesta de Balam)
**Integraciones**
- ¿BIND ERP tiene API habilitada o se trabaja con exportación de archivos? → **Exportación de archivos**
- ¿BUK expone API para consultar nómina procesada? → **No hasta donde sabemos**
- ¿Los 3 bancos entregan estados de cuenta por API, archivos (CSV/OFX) o PDFs? → **PDF's**
- ¿Cuál es el banco americano que manejan? → *TBD Ara* → **IBC BANK, es un banco Texano e ingresamos vía web.**
- ¿Pueden dar acceso a ambientes sandbox de BIND y BUK para desarrollo, o solo cuentan con producción? → **Solo producción**
**Facturación**
- ¿Ya cuentan con un PAC contratado para timbrado CFDI? ¿Cuál? → **Integrado hasta donde se en el mismo sistema de Bind, confirmo está contenido dentro del precio del ERP.**
- La facturación a clientes en Texas, ¿es invoice estándar o requiere tax compliance específico? → **Por el momento TAX estándard**
- ¿EUR entra al MVP (RF-02) o se deja preparado? → **Dejarlo preparado para fase posterior, por el momento solo es USD la facturación con empresas Europeas.**
**Cobranza**
- ¿Quiénes son los "top 3 clientes" sin recordatorios? ¿Lista fija o configurable? → **Lista Configurable de acuerdo al objetivo de negocio.**
- ¿Los tickets físicos en pagos con tarjeta son registro manual de terminal? → **No tenemos el pago con tarjeta de crédito, pero lo que estamos buscando es que podamos mandar un link para pago con tarjeta para que nuestros clientes puedan pagar, y/o mandar una liga en donde podamos domiciliar pagos como pólizas, etc**
**Datos y sistemas**
- ¿"BUk" es un sistema interno? ¿Tiene API o BD accesible? → **Es un SaaS, NOTA: no es Book, es BUK, y es nuestra plataforma de recursos humanos en donde manejamos contratos, nóminas, vacaciones, es el core de la empresa para la gestión del talento.**
- ¿Jira es solo gestión de proyectos o también fuente de datos? → **Gestión de proyectos y servicios solamente con clientes.**
**Operación**
- ¿Cuántos colaboradores en nómina y cuántas facturas al mes? → **45 Colaboradores y 5 proveedores de servicios tipo Freelancer. Emitimos no más de 50 facturas al mes**
- ¿Quién valida reglas de negocio durante el desarrollo? → **Un servidor y el gerente administrativo.**
**Infraestructura y diseño**
- ¿Servidor o nube preferida? → **Preferentemente Azure, si en costos beneficio hay otras estamos abiertos a evaluar.**
- ¿Guía de marca / assets? → **Sí, tenemos un manual de uso de marca, que lo podemos compartir**
@@ -0,0 +1,16 @@
# WhatsApp — 27 may 2026 · Pedro Ayala → Johann · primer contacto
Volcado literal aportado por Johann el 20-ago-2026. Es el mensaje que [REGISTRO #9](../bitacora/REGISTRO.md) registra como la entrada de Pedro al proyecto.
---
- **09:57 Pedro:** Hola Johan muy buenos días
- **09:58 Pedro:** te escribe Pedro Ayala de Balam.
- **10:00 Pedro:** cómo estas? / oye me comenta Noé que **te hizo llegar la información acerca del desarrollo de agentes**, crees que con esa información nos puedas dar una propuesta.
- **10:00 Pedro:** si no dinos para poder movernos a conseguir la información que se necesita
- **10:01 Pedro:** quedo al pendiente. ¡saludos!
- **10:01 Johann:** Hola Pedro, buenos días!
- **10:03 Johann:** Sí sin problema, de hecho estaba trabajando en la propuesta ya para poderselas enviar
- **10:04 Pedro:** excelente Johan, lo aprecio mucho / De igual manera si pudieras **darme copia en el correo** lo agradecería. Mi correo es pedro.ayala@balamtalentoestrategico.com
- **10:05 Johann:** De acuerdo, aprecio tu acercamiento, se las comparto cuanto antes
- **10:08 Pedro:** De acuerdo, Johan / Saludos
@@ -0,0 +1,20 @@
# Correo — 6 jul 2026, 4:46 PM · Johann → Noé Rocha, Pedro Ayala (CC Erika Chávez)
**Asunto:** Re: Entrega de Token Bind API
**Recuperado del buzón el 20-ago-2026** (MCP ms365).
> ⚠️ **Corrige el registro:** [PENDIENTES.md](../bitacora/PENDIENTES.md) y [REGISTRO #33](../bitacora/REGISTRO.md) daban esta respuesta por **no enviada** ("borrador listo"). Se envió el mismo 6-jul, **13 minutos después** del correo de Noé (4:33 pm).
---
> Buenas tardes, Noe
>
> De acuerdo, me parece bien la precaución. Te comento cómo estoy trabajando esta etapa:
>
> Todas mis consultas a la API son únicamente de lectura; el cliente que uso bloquea desde el código cualquier operación de escritura, así que no hay forma de modificar datos. Además hago pocas consultas y con resultados pequeños, muy por debajo del límite diario de BIND. El token no queda en el repositorio ni en registros, lo guardo en un gestor de secretos, y en mis notas solo documento la estructura de la API (nombres de campos y tipos), sin información real de clientes o facturas.
>
> La parte de escritura queda para más adelante.
>
> Y sin problema con el usuario de solo lectura, si Pedro lo crea, de mi lado cambiar el token es inmediato y no afecta el avance.
>
> Saludos.
Binary file not shown.

After

Width:  |  Height:  |  Size: 67 KiB

@@ -0,0 +1,27 @@
# Correo — 12 ago 2026 · Johann ↔ Pedro Ayala · campos y catálogos de la cotización BIND
**Asunto:** Campos Cotización en BIND
**Adjunto:** `Cotizacion-BIND-campos-y-catalogos.xlsx` (en la raíz del repo)
---
## 12-ago, 8:09 AM · Johann → Pedro (CC Araceli, Noé)
> Buenos días, Pedro. Espero que te encuentres muy bien.
>
> Te comparto un archivo de Excel con los campos requeridos por BIND para la creación de cotizaciones. El documento está organizado de la siguiente manera:
>
> - **Campos del formulario:** Detalla los campos de la cotización, indica cuáles son obligatorios y especifica el origen de las opciones para cada menú desplegable (combo).
> - **Hojas adicionales (Catálogos):** Contienen los datos reales de cada combo tal como se encuentran configurados actualmente en BIND. Esto incluye: servicios (67), productos (3), clientes (17), monedas, sucursales y listas de precios.
>
> Quedo al pendiente,
> Saludos
## 12-ago, 10:30 AM · Pedro → Johann
> Buenos días, Johan.
>
> Muchas gracias por la información compartida. Confirmo de recibido.
> Estaré trabajando en conjunto con el equipo para regresar con comentarios.
>
> Saludos,
@@ -0,0 +1,554 @@
RE: Seguimiento: medición de consumo API de Jira y accesos
Wed, Aug 12, 2026
0:00 - Unidentified Speaker
¡Gracias por ver el vídeo!
0:36 - Unidentified Speaker
A Pedro, buenos días.
0:41 - Unidentified Speaker
¿Qué tal, ingeniero?
0:45 - Unidentified Speaker
Buenos días.
0:47 - Pedro Alberto Ayala Elizondo
Estoy comiendo un agua, perdón.
0:54 - Unidentified Speaker
Sí, a vos, boludo.
1:10 - Pedro Alberto Ayala Elizondo
Y a qué hora se arranca?
1:14 - Noe Rocha
A qué hora se arranca? A las nueve y media. ¿La sesión? Sí, la de Dilo. La que tienen programada con Centro, me refiero. Sí, a las nueve y media. Ya hablé con Marcos para incluir a Salvador. ¿Por qué quieren que se impulgue Cruz Más Salvador en esta sesión?
1:47 - Unidentified Speaker
Ok.
2:14 - Noe Rocha
Listo. Hola Johann, buenos días.
2:16 - Unidentified Speaker
¿Cómo estás?
2:17 - Johann
¿Qué tal Pedro? ¿Qué tal Araceli? Buenos días. ¿Y ustedes? ¿Cómo están?
2:22 - Araceli Sanchez Jimenez
Hola Johann, muy bien. Muchas gracias. Buenos días.
2:26 - Noe Rocha
Estamos muy bien, gracias. Oye, Johann, bueno, esta sesión es en seguimiento a lo que hemos estado platicando. Nada más creo que no te había avisado. No sé si te lo mandé por correo o no. Erika ya no va a ser la PM de este proyecto. El seguimiento del proyecto lo va a traer Pedro. Después de todo lo que vamos a estar realizando contigo. Nada más quisiera asegurar que... Pedro, no sé si se alcanzó a ver con Erika el tema del Gira del seguimiento con las actividades de Johann.
3:05 - Noe Rocha
Sí, ya se tiene creado lo que es el Gira.
3:09 - Pedro Alberto Ayala Elizondo
Pero habíamos llegado a la conclusión de que ese tablero muy próximo iba a necesitar que sea de paga por el tipo de licencia que tenemos. Exploramos también la posibilidad de hacerlo mediante un SharePoint. Y también está la posibilidad de hacerlo con un Google Drive, que es algo más sencillo y un poco más... Que ya estamos todos familiarizados con cómo funciona un Google Drive o cómo funciona Google Drive. ¿Cómo funciona un SharePoint?
3:39 - Noe Rocha
Pero hoy vamos a ver lo del RegioTour, ¿no? También, el anterior. Sí. Ah, ahí es donde falta el admin de acceso, para que puedas crear spaces. Sí. Ah, bueno, lo vemos eso, porque me interesa hacerlo sobre Jira, porque ya que ese puede funcionar. Bueno, muy bien.
3:56 - Pedro Alberto Ayala Elizondo
Entonces, nada más sería de crearlo y sería de copiar las actividades que ya están en el otro y se agregan. Y sobre esas actividades, sobre esas tarjetas, Se van actualizando las actividades a medida que se avanza. Bueno, para resolverlo hoy.
4:12 - Noe Rocha
Bueno, este es un anuncio, Johann. El tema del seguimiento es con Pedro. Y bueno, vamos a resolver el tema de Jira para que haya el récord de lo que has estado haciendo. Pendientes de nuestro lado hacia ti es el tema del ambiente de Azure, ¿verdad? Para que puedas irlo generando.
4:34 - Unidentified Speaker
Sí.
4:35 - Noe Rocha
Ok. Bueno, eso lo resuelvo con Pedro. Y último punto. Adelante, Pedro. Sí, también tengo los videos de los que faltaban de las grabaciones de Bind.
4:46 - Pedro Alberto Ayala Elizondo1
Ah, sí. Que es el de altas de proveedor y clientes, facturación. Y al tercero no me acuerdo, pero ahí está. Ahí lo tengo en la carpeta. Si quieres, al finalizar la sesión te los hago llegar.
5:04 - Noe Rocha
Sí, es más informativo, Johann, de cómo funciona Bind, por si necesitas algún dato adicional. Contexto. Contexto y módulos, ¿no? Y ya después de eso puedes tener alguna reunión si es necesario para aclarar. Bueno, muy bien, Johann. Ahora sí, ya con lo que estuvimos viendo vía correo electrónico de los giras, cómo se generan los procesos de facturación y cómo vamos a empezar a hacer algunas integraciones, dimos algunas respuestas contigo. Sobre hacer una prueba sobre un ticket. Te dimos respuesta en cuanto a los campos que nosotros estamos considerando usar en las etiquetas o en los requisitos que se tienen en el space de facturación, que habías preguntado. Y es solamente en facturación adicional se va a tomar en cuenta. Y te comentábamos que básicamente todo lo y todos los type requests que mencionamos nosotros. Ahora, nos habéis pedido que hiciéramos un ticket, ¿cierto? Como para hacer una prueba con los campos que se tendrían que usar. ¿Por qué? Porque lo que estábamos haciendo era que nosotros metíamos una solicitud de facturación con una cotización adjunta como primer paso. Y esa cotización viene de Vine. O sea, ya viene como cotización. Lo que nosotros mencionábamos es que, aparte de la cotización, vinieran los campos ya llenados del propio JIRA, para que los datos se extrajeran de los campos de JIRA, no de un PDF o un documento junto. De acuerdo? Y de ahí vamos a partir a hacer, recuérdame, ahora sí, platícanos qué vamos a hacer con Sí, claro.
6:58 - Johann
Lo que se pretendía, lo que pretendía hacer ahorita en esta sesión, era con el ticket creado, ya con los campos definidos y llenados, como querían que fueran llenados de aquí en adelante, hacer una prueba con la plataforma, en donde vemos cómo se ve el ticket de Jira, reflejado en la bandeja de Jira, y creamos una cotización. A partir de ella. Y de ahí ver, también de mi parte, qué hay que ajustar. Porque hasta ahora he estado haciendo pruebas de dry run. O sea, no he mandado nada, no mando ningún post hacia el RP de Byte. Entonces también me serviría como primer caso. Muy bien.
7:46 - Noe Rocha
Es que mira, creo que hay una aclaración que Araceli haciendo ayer. Me dice que en la definición que ella se hizo con Arturo, que estaba haciendo parte de esto, y en las definiciones de los flujos de gira, y corrígeme, Rassely, si estoy mal para darle ese contexto a Johann, cuando se hace la solicitud de una facturación adicional, ya viene una cotización, ¿cierto? Directamente de Bain. Sí. Muy bien. Entonces tú lo que necesitarías en sí, pues ya no es la cotización cuando tú, tú como área comercial generas el ticket con una solicitud de cotización adicional.
8:39 - Unidentified Speaker
Tú lo que necesitarías sería.
8:43 - Araceli Sanchez Jimenez
Quien yo o Johann. Sí.
8:46 - Noe Rocha
Sí, no tú, tú estás hablando del proceso, de tu necesidad y proceso.
8:52 - Johann
A ver, ¿otra vez la pregunta?
8:54 - Noe Rocha
Sí, es que lo que estábamos explorando específicamente en este caso con Johann es que cuando hay una solicitud de facturación adicional, en un primer escenario se manejaba, o al menos eso es lo que yo entendí con Arturo, que la cotización fuera creada desde Jira, pero en el proceso como tú lo definiste para Jira es, no, tú ya integras la cotización desde un principio y esa cotización viene ya desde Vine. Entonces, cuando hablamos de cotización, no hablamos de un documento, una cotización fuera del sistema, sino del mismo sistema. O sea, ya no necesita ser la cotización, pues, porque tú ya lo hiciste.
9:38 - Araceli Sanchez Jimenez
Bueno, miren, si quieren les voy a explicar el proceso. Es algo que yo haya definido. Eso suena como si fuera una imposición. Se los voy a explicar porque dado que estamos haciendo algo nuevo, si hay algo que cambiar, lo hacemos. Les voy a decir cómo es el tema comercial, si y por qué nace así. Hagan de cuenta que la empresa es el primer día, el día cero. Entonces, pues no hay nada de facturación recurrente que hacer ni nada porque vendido sí entonces yo vendo el primer proyecto entonces la parte comercial para vender el primer proyecto tengo que cotizarlo para que sobre esa cotización el cliente me acepte el monto entonces yo lo que hago ahorita es yo genero directamente desde el rp la cotización si desde el rp entonces para generarla antes el cliente ya tiene que estar dado de alta porque si no pues no va a salir con su nombre ni el rfc y todo eso entonces una una vez que yo ya lo vendí y ya lo cerré pues yo solicito la factura entonces como yo ya tengo la factura del sistema yo solicito facturación y cuando yo solicito la facturación yo subo la cotización ¿Por qué? Porque como la cotización ya está en el sistema, nada más buscan mi número de cotización, el 01, entonces tú te vas al RPI, además ahí ves cotizaciones, te vas a la 01 y en el menú de esa cotización viene prefactura, factura o cancelar o modificar. Entonces tu administración le da prefactura o factura, como sea, ¿sí? Entonces ese es un proceso. Cuando yo vendo por primera vez algo y entonces yo tengo que cotizarlo. Yo qué estoy haciendo? Ya lo estoy cotizando siempre desde el sistema. Si la parte comercial. Luego qué pasa si esta venta es recurrente? Es decir, que se cierra por un año o dos. Yo el otro mes, yo como comercial, Yo no vuelvo a generar una cotización. Yo nada más informo a administración que esa facturación va a ser recurrente y le doy el tiempo 3, 6, 4. Y entonces yo comercial con ese aviso, yo ya no me encargo de cada mes estar haciendo la cotización para que facture. Lo que hace ya con el aviso administración es generar la pre-facturas y después las facturas. ¿Por qué se generan las pre-facturas? Antes nos íbamos directamente a facturación, pero luego había muchos errores y ya estaban timbradas. Entonces, era todo un rollo la cancelación con el SAT. Porque hay unos clientes que tienen que aceptar la cancelación y hay otros que no. Y SEMEC se tarda años en cancelar. Entonces, lo que hicimos fue... ¿Qué hicimos? Porque yo también estoy metida en la parte administración, administración, todo lo que es recurrente hace una prefactura, las valida y entonces ya las factura, pero es el área comercial no se entera cuando es recurrencia. Sí. Entonces. Pues así es el proceso y, por ejemplo, si la parte comercial vende algo y no es recurrencia, pues entonces la cotización que hizo comercial es la que se factura.
13:22 - Unidentified Speaker
Ok.
13:23 - Noe Rocha
Ahora, pregunta. ¿A ti se te facilitaría que desde el mismo JIRA, si no hicieras ese primer paso de cotización, JIRA hiciera la cotización? La verdad que sí. ¿Para que no tengas que estar adjuntando actividad? Bueno, para el día 1, vamos a hablar del día 1. ¿Sí te sirve que JIRA haga la cotización?
13:47 - Araceli Sanchez Jimenez
Sí, sí me serviría. Ok.
13:50 - Noe Rocha
Bueno, entonces vamos a hacer ese ejercicio, Johann. Sí, porque creo que eso va a ser una parte de lo que va a quitar, pues, tal H, tal H que hoy por hoy se está haciendo desde el área comercial. Entonces, lo que quedamos era de tener un ticket para una prueba de que ese ticket fuera a ser la cotización.
14:14 - Unidentified Speaker
¿Cierto, Johann? Sí, justamente.
14:15 - Noe Rocha
Muy bien. Ahora, Rassely. ¿Tenemos algún caso de algún ticket que ya esté creado para no tenerlo que hacer de cero? O si lo creamos ahorita, digo, no nos tardamos mucho para hacer esa prueba y ese ejercicio de decir, oye, estos son los datos ficticios, ¿verdad? Vamos a hablar de ficticios de algún cliente para que sea cotización de alguien que existe. Y vaya y hagamos la prueba con Johann para hacer la cotización.
14:43 - Araceli Sanchez Jimenez
Sí, lo podríamos hacer porque puedo inventar los datos aquí el tema es que no hay un ticket que yo pueda usar o sea una categoría por ejemplo yo podría meterme ahorita Gira y poner una actividad en general este y pero en la descripción del cuerpo y tendría que poner exactamente qué hacer si entonces yo no sé si después hay un flujo en donde sea cotización.
15:11 - Noe Rocha
Gira no, fíjate que no lo tenemos, pero lo tendremos que definir.
15:16 - Araceli Sanchez Jimenez
Sí, pero entonces si quieres, podemos hacer un, dejen, abro, vamos a hacer un, aunque el flujo ahorita no lo tengamos definido, voy a agarrar uno de, ahorita comparto la pantalla, uno de actividad general para, para verlo.
15:32 - Noe Rocha
Nada más, ojo, Johann, yo me acuerdo, tú estás trabajando sobre el space de gira de facturación, ¿verdad? Sí.
15:41 - Araceli Sanchez Jimenez
Ah, tendría que ser sobre este. Sí, bueno. Ok. Voy a agarrar este de facturación adicional, aunque sea cotización por primera vez. Estamos de acuerdo. Sí. Ok. Si ven mi pantalla.
15:56 - Unidentified Speaker
Sí, sí.
15:57 - Araceli Sanchez Jimenez
Ok. Bueno, aquí.
15:58 - Pedro Alberto Ayala Elizondo1
Ah, sí, yo lo que es es que, pero aunque te interrumpa, pero él desde un principio se había quedado que trámite de pagos se quedaba en la bandeja de administración general. Pero la verdad es que ahorita, siendo todo lo que estamos platicando y que haga sentido, tenemos sentido que la parte de trámite de pagos caiga en la bandeja de facturación.
16:23 - Araceli Sanchez Jimenez
Pago proveedores, esto no es el.
16:25 - Pedro Alberto Ayala Elizondo1
Mire, ahora sí puede ir a la pago a proveedores, papi.
16:29 - Araceli Sanchez Jimenez
Sí, a eso se refiere. Mira, es que aquí está en administración. Ajá.
16:34 - Pedro Alberto Ayala Elizondo1
Tramitación de pagos es los pagos nosotros vamos a hacer a proveedores pero bueno también tiene razón Pedro también son pagos de nómina directamente a nuestros colaboradores entonces lo que podría hacer yo es replicarlo o sea no no borrarlo de aquí para no no dejarlo fuera del alcance también de la administración general lo puedo replicar en en facturación le pongo los campos necesarios en ese y hacemos la prueba con esos campos necesarios nada más.
17:10 - Araceli Sanchez Jimenez
¿Como para cotización te refieres? Ajá.
17:13 - Pedro Alberto Ayala Elizondo1
Entonces, lo que puedo hacer es construir un nuevo formulario muy rápido aquí en Jira con campos muy muy especiales para que Yoja necesite para la cotización y hacemos esa prueba en vivo.
17:29 - Noe Rocha
Ok, como quieran.
17:31 - Noe Rocha
¿Cuánto te tardas? Para ser muy puntuales con el tiempo de Johann. Porque el hijo solo tiene una hora.
17:39 - Araceli Sanchez Jimenez
No, no, me tardo cinco minutos.
17:41 - Araceli Sanchez Jimenez
Si quieres, yo te digo los campos que tienen que tener Pedro.
17:46 - Noe Rocha
Bueno, ya habíamos mapeado campos en un correo con Johann. Que en ese correo con Johann eran los campos de, déjame ver, nada más el correo que le mandamos.
17:57 - Araceli Sanchez Jimenez
Sí, está de empresa origen. Eso sí tiene que ir, Pedro.
18:02 - Noe Rocha
Sí, porque tú ya tienes mapeados los campos con respecto a lo que te mandamos, ¿verdad, Johann?
18:11 - Johann
Sí, pero igual ahorita podemos trabajar en ajustarlos.
18:16 - Araceli Sanchez Jimenez
Pues si ya los tienen, a lo mejor trabajemos sobre esos y ya ahorita los rechecamos.
18:24 - Pedro Alberto Ayala Elizondo1
Muy bien, entonces...
18:26 - Noe Rocha
Voy a ponerlos aquí en el chat. Voy a ponerlo aquí en el chat. Porque creo que son los de... Es que sabes que Arturo no me mandó exactamente de dónde sacó esta pantalla. Pero tú la identificas, Pedro. Ah, es esta, Gordo.
18:46 - Araceli Sanchez Jimenez
La que te está... Es administración general. Es un ticket genérico. Y esa no me sirve mucho. Entonces, si quieres, yo te los puedo dictar porque yo hago las cotizaciones. Es más, mira. Lo que podemos hacer es agarrar de las cotizaciones, si me entiendes. O sea, aquí tengo un cliente, por ejemplo, este. Mira, esta es la cotización que yo genero por sistema.
19:15 - Araceli Sanchez Jimenez
Por ejemplo, es la empresa.
19:17 - Araceli Sanchez Jimenez
Pero esto ya me lo carga el ERP, ¿verdad? Y luego empresa y luego quién es el cliente. Y es en automático me lo hace y ya después de eso es la descripción verdad que es y yo le pondré la descripción el precio unitario y que me diga la moneda si es pesos o dólares ok sí y bueno que ella y ya o sea si es pesos o dólares pues le calcula el sistema en calcula automático eso y es todo lo que tiene la cotización. Yo lo que hago en la parte comercial o esto es nada más un comercio. Ahora sí, un comentario. Así que, por ejemplo, yo como vendo, ejemplo horas de consultoría proyecto soda pues eso no le dice nada al cliente yo siempre le mando esto adjunto del alcance sí o de do de mi propuesta más granular tantos o sea ya como todos los pasos y todo eso pero yo siempre le mando esto porque al final de día la factura va sobre esta descripción muy bien Listo.
20:38 - Noe Rocha
Si puede. Johann, qué tan puntuales hay que ser con la información? Porque. Porque si todos estos campos. Tienen que ser llenados en vain. El campo que no venga en el formulario no lo va a poder, no lo va a poder poner verdad?
20:56 - Araceli Sanchez Jimenez
Mira, te voy a decir cuáles campos no son, no están. Esto está cargado en vain todo esto porque es Así se configura. El receptor también. ¿Por qué? Porque yo lo que ayer te decía, que el único requisito para yo generar, para que existe en el sistema, es que esté dado de alta el cliente. ¿Sí? Y eso ya no es tema de este alcance. ¿Sí? Entonces, la parte comercial, cuando tiene un nuevo cliente, su responsabilidad, pedir en el RP que lo den de alta. Entonces, cuando la dan de alta, esto está en automático. Y ya esto se carga, esto lo hace, ya está por default, esto también es por el sistema, porque es fecha de creación. La fecha de vencimiento, ahí sí, Pedro, la fecha de vencimiento hay que preguntarla, la fecha de creación es cuando tú la solicitas, pero la fecha de vencimiento es la vigencia de la cotización, eso sí lo tendríamos que nosotros preguntar, porque eso sí yo lo hago. Esto elaborado por ISOCURSAL, eso está por default en el sistema. Y yo luego nada más lo que agarro es esto, horas de consultoría.
22:14 - Noe Rocha
¿Es un campo que existe o es un campo que llenas?
22:20 - Araceli Sanchez Jimenez
Es un campo que existe.
22:22 - Araceli Sanchez Jimenez
¿En combo? ¿Cómo que en combo? Es decir, una lista desplegable Sí, hay nada más, por ejemplo, en el ERP, pero eso ya es fuera del alcance de Johann. Miren, me voy a meter en Vine para explicarles mejor.
22:43 - Pedro Alberto Ayala Elizondo
Ya está también el formulario en Jira.
22:47 - Noe Rocha
Ah, órale. Un momentito, voy a ponerme cargado.
22:53 - Unidentified Speaker
Sí.
22:54 - Araceli Sanchez Jimenez
Mira, Johann, yo cuando hago una cotización, me meto aquí a ventas, me voy a cotizaciones y me voy a agregar. Sí, le doy a agregar. Por ejemplo, aquí voy a hacer la cotización de Cemex. Entonces, mira, aquí son las opciones. Por ejemplo, esto es lo que te tendría que preguntar qué moneda es, pesos. Aquí la fecha, te la pone por default y es la de hoy. Y aquí es la vigencia, ¿no? Entonces yo ya le pongo la vigencia. Y luego aquí ya busco el cliente. Este cliente ya tiene que estar dado de alta. Entonces aquí me aparecen las opciones de los clientes. Entonces yo le pongo CEMEX. Y luego aquí, agregar producto o servicio. Pues nosotros, normalmente vendemos intangibles, o sea, servicios. ¿Pero por qué te digo normalmente? Porque casos excepcionales, clientes nos piden equipo o accesorios como de ADM, mouse, y entonces, estas las tengo que poner aquí. Aquí yo he visto que hay un tema de depuración del ERP. Por ejemplo, yo le pongo agregar producto o servicio. Y si te das cuenta, hay varios repetidos en que, por ejemplo, consultoría y horas de servicio, este es 040 y 029, esto es absolutamente lo mismo, te das cuenta. Luego está desarrollador, aquí está equipo iPad, equipo telefónico, headhunting, pero luego, por ejemplo, pusieron ya números específicos de personas, si me entiendes, aquí habría que depurarlo, o monitor Samsung o no sé qué. Aquí lo único que debieron de haber puesto es un genérico, Por ejemplo, ¿en qué sentido? ¿Horas de consultoría? ¿Por qué? Porque si yo le doy estas horas de consultoría, yo aquí lo puedo editar. ¿Sí me entiendes? No tiene caso que haya otras horas de consultoría. Por ejemplo, esta es la 040, el código, y no tiene caso que haya un 029 porque no hay un diferenciador. Estos números nada más son de uso interno en el sentido que esto, el SAT, Entonces, con lo que se timbra, por ejemplo, aquí voy, mira. Cuando yo tengo, esta es la unidad del SAT, la E48. Entonces, las horas de consultoría tienen esta unidad ante el SAT, ¿sí? Entonces, aquí nosotros, de nuestro lado, lo que hay que hacer es, cuando dice Noe que están en un combo, debería de estar en un combo aquí. Lo depuramos para que nada más haya las opciones genéricas. ¿Sí me estoy explicando? Mira, porque aquí de servicios profesionales, mira, ve cuántos hay. Y, por ejemplo, aquí también hay código servicios profesionales, 16 horas. Y aquí, aquí habría que, mira, por ejemplo, hay unos que dieron de alta hasta con los periodos y así. Entonces, habría que depurarlo. Mira, aquí hay muchos nombres. Entonces aquí nada más debería de ser que tú pongas si es servicios y consultoría o que pongas nada más aquí el el desglose de lo que necesitas que lleve la cotización bueno pero entonces nada más para estar seguro y lo que se pueda hacer Johann si es como antes yo antes aquí nada más lo que hacer es en el espacio de la pantalla de JIRA que se desplieguen nada más esas opciones. Por ejemplo, servicios de consultoría, equipo de cómputo o eso ya yo lo definiría con Pedro en la cotización, porque dependiendo de eso sí es la unidad del SAT correspondiente.
27:04 - Noe Rocha
Actividad pendiente, depurar los campos que se van a usar en la cotización, para que sean los mismos que van a estar en el combo desplegable y coincidan con los campos que se van a estar planchando en Vine.
27:22 - Unidentified Speaker
¿De acuerdo?
27:23 - Araceli Sanchez Jimenez
Para propósitos de la prueba ahorita, ¿usaríamos cuál? El de servicios y consultoría. Está aquí en facturación, verdad, Pedro?
27:34 - Pedro Alberto Ayala Elizondo
O en dónde lo pusiste? Debe estar ahí en facturación.
27:39 - Araceli Sanchez Jimenez
No me aparece.
27:40 - Pedro Alberto Ayala Elizondo
Debe de estar. Está en general. A ver, déjame ver si yo.
27:46 - Araceli Sanchez Jimenez
En la administración general.
27:48 - Pedro Alberto Ayala Elizondo
No, no, no.
27:49 - Unidentified Speaker
En facturación.
27:50 - Unidentified Speaker
Mira, a mí sí me parece.
27:53 - Araceli Sanchez Jimenez
Si le puedo dar un refresco a todo.
27:57 - Araceli Sanchez Jimenez
Super ya cotización vain. Bueno, Empresa Origen, Malam, Sumari. Aquí en Sumari, ¿a qué te refieres, Pedro?
28:08 - Pedro Alberto Ayala Elizondo
Pues es como el título del Gira, del Ticket. Del Ticket.
28:13 - Noe Rocha
Ese es del Ticket. OK, entonces aquí luego lo ajustamos.
28:18 - Araceli Sanchez Jimenez
Sí, ponle Cotición Semex Prueba, si quieres.
28:21 - Pedro Alberto Ayala Elizondo
Esa, la descripción, hoy por hoy es texto, pero como usted dice, va a haber la lista, el convito desplegable.
28:31 - Araceli Sanchez Jimenez
Sí. Este. Horas de consultoría. Proyecto. OK. Aquí nombre del cliente se mix. También debería ser combo.
28:42 - Unidentified Speaker
Ajá.
28:43 - Araceli Sanchez Jimenez
Aquí yo no sé cómo le haríamos, porque debería de ser nada más los clientes que están dados de alta en en vain. El rp pero bueno eso eso ya lo vemos nosotros no semex tipo de moneda montos iniba esos como box tendrían exactamente el nombre de los combo box del rp va a tratar de homologar nombres y todo ok aquí la fecha de vencimiento me imagino que es la vigencia de la cotización verdad ok Bueno, luego los ajustamos bien. Ajá. Pues creo que ya. Ok. Y en este caso a estos tipos de tickets les quitaría el SLA para que no impacten métricas. Como son accionables.
29:52 - Unidentified Speaker
Ah, ok.
29:53 - Unidentified Speaker
Muy bien.
29:54 - Unidentified Speaker
Listo.
29:55 - Unidentified Speaker
Muy bien.
29:56 - Unidentified Speaker
Ok.
29:57 - Unidentified Speaker
¿Y ahora qué más?
29:59 - Pedro Alberto Ayala Elizondo
Necesito ver alguna otra pantalla o de gira.
30:08 - Johann
Ahorita estoy probando y ya se vio reflejado el ticket que hicimos aquí de prueba.
30:21 - Unidentified Speaker
¡Hija!
30:22 - Unidentified Speaker
Déjeles comprar tu pantalla.
30:26 - Johann
¡Qué emoción! Aquí, por ejemplo, Sí, ahorita, por ejemplo, la forma en la que se estaba mapeando el cliente conforme los campos, era a partir del summary o a partir de aquí, el resumen. Entonces, con el campo, Me haría falta mapearlo ahora sí, con el combo box.
31:04 - Pedro Alberto Ayala Elizondo
Ya, o sea, el summary con...
31:07 - Unidentified Speaker
o más bien, el summary o la description.
31:11 - Unidentified Speaker
Sí.
31:12 - Johann
Sí, los campos que ahorita resulten como nulos o que no los detecte, ya me haría falta ajustarlos con los nuevos campos de este nuevo formulario.
31:25 - Noe Rocha
Bueno, nada más hay que homologar entonces a Ara y Pedro, porque una de las respuestas que le dimos a Johann es que todos los requests type iban a tener el mismo tipo de formato, para que a partir de una solicitud de factura se hiciera la cotización. Entonces habría que estar seguros Porque todos los tipos de recuestas que tenemos, como ahorita es facturación recurrente también, tratar de homologar todo a lo mismo. Entendería yo que en el caso de facturación recurrente, ese trailer trae un campo adicional. ¿Te puedes devolver tantito, Johann? Que aquí mismo lo menciona. De hecho, sí lo trae mapeado. Y lo vi.
32:17 - Unidentified Speaker
Recurrencia. Recurrencia, sí.
32:19 - Noe Rocha
Sí viene.
32:20 - Noe Rocha
Ahora Johann, aquí tenemos una duda y que estuvimos discutiendo cómo resolverlo de forma interna, si lo resolvíamos a través de nosotros en el sistema o veíamos la forma de resolverlo contigo y te voy a plantear el caso. Pero antes de seguir, esto que hicimos, esta prueba que hicimos nada más para ir por pasito, ¿lo vas a convertir en una cotización?
32:49 - Araceli Sanchez Jimenez
Oigan, yo tengo un comentario. En la parte comercial pasa muchísimo que, por ejemplo, le mando la cotización y siempre hay ajustes. O sea, y yo lo que hago en Vine, porque, por ejemplo, ya generé la cotización, esta, por ejemplo, esta cotización de Cemex, ¿no? Entonces, me dijo, no, ahora sube los amasadoras, quítale, bájale, no sé qué. Entonces, a esta cotización, yo en Vine me meto y le pongo modificar, ¿sí? Y modifico aquí alcance, precio y todo eso. Entonces aquí se modifica la cotización. ¿Yo voy a tener opción también de modificar la cotización o no?
33:32 - Johann
Aquí ahora mismo no está implementada la parte de editar la cotización. Pero igual la podemos trabajar.
33:42 - Araceli Sanchez Jimenez
Sí, eso sí, Johann.
33:45 - Araceli Sanchez Jimenez
Yo lo que haría hace cuenta tendría que cancelar hasta que sea la buena y sería todo un rollo para la parte comercial. O si ustedes creen también por. Bueno, es que yo llevo el historial en mi carpeta, pues porque de repente es cotización. Digo, porque eso sí, me he dado cuenta que cada vez que modificas la cotización no te conserva el. Según yo, el mismo número o no estoy tan segura. Si te conserva el número o no.
34:15 - Noe Rocha
Yo sugiero que eso sea regla de negocio, que sería mejor o llevar un historial de las cotizaciones o estar modificando la misma cotización. En mi experiencia, a veces funciona tener el historial porque ese historial te permite saber cuántas veces le estás cotizando a alguien algún producto y en caso de que te diga Bueno, ya sabes que tienes la primera cotización origen o la base, pero es regla de negocio.
34:47 - Araceli Sanchez Jimenez
Habría que definir.
34:48 - Araceli Sanchez Jimenez
Yo creo que sí es bueno que conservemos el historial.
34:53 - Noe Rocha
Pues tendría que ser en lugar de modificar, tendríamos que generar tickets diferentes.
34:59 - Pedro Alberto Ayala Elizondo1
Y no se puede hacer algo así como. Que sea que si sea editar la cotización dentro aquí del sistema. Pero que haya un modulito de historial de facturas. Que sea la factura tal, se ha modificado tantas veces y está nacido su historial de versiones, por así decirlo.
35:19 - Noe Rocha
En teoría el historial se queda en BIND, Pedro. Y JIRA solamente sería como informativo. De decir, ah, bueno, mira, aquí en JIRA tuve cotizaciones 1, 2, 3, como informativo. Pero el documento viviría en BIND, porque va a cambiar el número de cotización. En todo caso, este caso de uso tendría que plantearse de dos formas. Como ya lo mencionamos. O generamos un nuevo ticket para generar una nueva cotización. O planteamos con Johann, con el mismo ticket, simplemente volver a pedir que genere la cotización. Los campos con los que nos están pidiendo, pero no modificar la cotización entera, simplemente volver a generar otra, pues no sé si me explica. Pero creo que como regla tenemos que definirlo nosotros, claro, para saber, para no andar trayendo a Johann arriba y abajo con Quita y Pong, ¿verdad?
36:21 - Araceli Sanchez Jimenez
Oye, nada más dejen, llámeme, estoy en Vine, reconfirmar que efectivamente cambia el número de cotización, porque si no, no se conserva el historial, no me hará sentido, pero nada más dejen Dejen ver si editar. 19. Disculpa, ese historial de personas que maneja internamente Bind, ¿cómo se genera?
36:41 - Johann
¿Es cuando editas una cotización y dentro de ahí, por ejemplo, te metes una cotización y aparece un apartado de historial que nosotros podemos acceder? ¿O cómo funciona esa parte? Pregunto para irme imaginando cómo es que se en el sistema. De pronto es un catálogo.
37:04 - Araceli Sanchez Jimenez
Oigan, fíjate que ahorita no les dije mal, no, no te da otro número de cotización. Miren, les voy a, les voy a compartir pantalla. Se mantiene, te modifica la que sobre la que estás trabajando.
37:24 - Noe Rocha
Entonces debe tener su.
37:26 - Pedro Alberto Ayala Elizondo
su historial de versiones con...
37:29 - Noe Rocha
Si tú lo modificas, no te cambia el número de cotización.
37:35 - Araceli Sanchez Jimenez
Tienes que hacer otra. Exactamente. Miren.
37:38 - Unidentified Speaker
Aquí estoy en la cotización.
37:40 - Araceli Sanchez Jimenez
Ya hay una cotización a 10.19. Entonces aquí yo en acciones me da esto, marcar como surtida, generar pedido, generar venta, copia, la, la, la. No entonces yo le voy yo le voy a dar en editar entonces yo la edito y aquí pues yo nada más le pongo cantidad 2 no y era las 10 19 perdón no sé si vieron el número de cotización si yo le voy a guardar y me mantiene esta y nada más y ahora me pone la modificación que aquí fueron dos le puede dar en actividades, si algo aparece ahí no mira, archivos no, documentos relacionados no, el detalle pues es este y desde ahí mismo se manda a facturación? Fíjate que curioso porque no sé si...
38:57 - Pedro Alberto Ayala Elizondo
O sea, usted dice de cotización 1019 a factura 1019?
39:05 - Araceli Sanchez Jimenez
Es que fíjate que aquí sí en cotización. De aquí me pasaba No me acuerdo en qué, en dónde está para facturar. No sé si es captura-venta, pero bueno, déjenlo checo, porque hace cuenta que ya con la cotización está en algún lugar todas las cotizaciones y tú nada más le pones en uno prefactura o factura y ya con eso en otro módulo en otro módulo sí pero no sé en qué en qué módulo es tendría que preguntar al al despacho tenemos la grabación si quieres Ah, súper. A ver. Bueno. Muy bien.
40:12 - Noe Rocha
¿Qué sigue, Johann, para la prueba? OK. El siguiente paso sería, con el ticket de Gira y los campus, mandar a la cotización hacia Vine.
40:25 - Johann
Será la primera prueba de escritura que haría. Lo vamos a hacer.
40:32 - Noe Rocha
Sí. Muy bien. Pues, ¿lo grabamos de una vez?
40:37 - Unidentified Speaker
Sí.
40:38 - Johann
¿Se pueden ver en pantalla? Sí. Va. Entonces, esta es la que hicimos ahorita, o la que Mercedes nos ayudó a hacer de cotización de Max Prueba. Y, por ejemplo, aquí el cliente no lo detectó. Entonces, aquí utilicé un endpoint de Bind para conseguir los clientes. Y aquí se puede conseguir. En este caso, ¿cuál selecciono?
41:06 - Unidentified Speaker
SEMEX.
41:07 - Araceli Sanchez Jimenez
¿El SEMEX?
41:08 - Johann
Sí, por favor.
41:09 - Unidentified Speaker
OK.
41:10 - Johann
Entonces, aquí como prueba estoy usando el objeto que se va a mandar para termas de depuración por si hay algún campo que falta o hay algo que deba colocarse más.
41:26 - Unidentified Speaker
OK.
41:27 - Johann
Entonces, voy a darle aquí. A ver, un momento. Esto es lo que voy a revisar. Es un error 500. Entonces, permítame revisar cuál fue el error.
41:51 - Unidentified Speaker
El error.
41:53 - Johann
Quería preguntar, con este tema de las cotizaciones y ¿Cómo es el proceso de cancelarlas? Porque, por ejemplo, si no tienen efecto fiscal como una factura, de pronto pudieran funcionar como un, no quiero decir sandbox, pero pudiéramos realizar pruebas y después cancelarlas.
42:18 - Unidentified Speaker
Sí.
42:19 - Araceli Sanchez Jimenez
Mientras tú no timbres, o sea, mientras no las compartamos en CFDI, podemos cancelar y hacer las prefacturas que queramos.
42:29 - Noe Rocha
Hasta Prefectura no hay un timbrado, pero suponiendo que nos vayamos al timbrado, ahí sí platícalo cómo se cancela.
42:38 - Araceli Sanchez Jimenez
En el timbrado, cuando ya se timbró, tienes que primero ponerte de acuerdo con el cliente, avisarle al cliente, y si está de acuerdo, primero por correo que te diga, ok, adelante, cancélala. Lo que nosotros hacemos es, le pedimos cancelación, la cancelamos en el sistema, en nuestro RP, y el despacho es una cancelación ante el SAT. Y esa cancelación tiene una vigencia para que el cliente la acepte. Entonces, nos dice el despacho, ya la cancelé en el SAT. Entonces, nosotros agarramos y le decimos al cliente, ya te aparece la cancelación, búscala. Entonces, él tiene que aceptar la cancelación y es en una liga del SAT, ¿verdad? Entonces acepta la cancelación y sólo así está cancelada ante el SAT, porque si no nosotros nada más la cancelamos en nuestro sistema, en nuestro ARP, pero no ante el SAT. Entonces ese es el proceso.
43:39 - Noe Rocha
Entonces un tema es con el cliente, otro tema es en el sistema y otro tema...
43:45 - Araceli Sanchez Jimenez
Que nos apoye en el despacho.
43:47 - Noe Rocha
Ajá. Para que quede oficialmente cancelada ante el SAT.
43:51 - Araceli Sanchez Jimenez
Ajá.
43:52 - Araceli Sanchez Jimenez
Por eso es todo un rollo, porque en algunas ocasiones... Es todo un rollo, porque también qué pasaba, que a veces las personas te cancelaban una factura sin pedir tu visto bueno. Y luego, por ejemplo, luego cancelaban facturas que tú ya habías pagado. Entonces es otro rollo. Entonces por eso pusieron todos estos requisitos, ¿no?
44:17 - Noe Rocha
Pero creo que... ¿En este desarrollo no vamos a contemplar las cancelaciones?
44:23 - Araceli Sanchez Jimenez
No, eso ya lo... Ya veríamos. Lo que sí, sí habría que contemplarlas porque, de manera extraordinaria, Cemex o los clientes nos piden cancelarla. Entonces, en el sistema para la conciliación, a lo mejor habría que ponerle, esta factura se canceló y fue sustituida por esta. No es sustituida, ¿verdad? Pero que la cancelación no la haga el sistema, a eso voy. Pero que sí lleve el récord, porque sí hay cosas o facturas que se llegan a cancelar.
45:06 - Johann
Ok, entiendo. Igual, si les parece, también en mis procesos de prueba también en comunicación con Pedro comentarles cuando vaya a hacer por ejemplo escritura a una cotización o prefactura ok sí OK. Estoy viendo que puede ser que el error que haya salido es por omitir algunos campos que el endpoint necesita y que el portal manda como en ceros. Entonces, voy a revisar si los puedo mandar ahorita como prueba y vuelvo a hacer la prueba.
46:01 - Araceli Sanchez Jimenez
OK. Si no, no te preocupes, Johann. Haz la prueba y podemos agendar otra sesión. Si quieres, si es de minutos te esperamos, como tú digas.
46:14 - Johann
Permítame hacer un momento más la prueba. Ok. Y si ya no se soluciona con esto, lo pruebo en local y le Ok. Subtítulos realizados por la comunidad de Amara.org Subtítulos por la comunidad de Amara.org Ya, ya veo.
47:02 - Johann
Me falta la lista de precios.
47:05 - Unidentified Speaker
OK.
47:05 - Noe Rocha
Lista de precios.
47:07 - Araceli Sanchez Jimenez
Nosotros realmente no tenemos una lista de precios, Johann.
47:12 - Araceli Sanchez Jimenez
Ese lo podríamos agarrar del monto, del Gira. Ya ves que te pusieron monto sin IVA. Ese sería nuestra lista de precios.
47:24 - Noe Rocha
Pero yo creo que lo menciona Johann. Porque Bain lo que... Me lo estoy imaginando. Lo que hace Bain dice cantidad. Y de acuerdo al servicio tiene un precio. Y eso lo convierte en el número final de la cotización.
47:44 - Araceli Sanchez Jimenez
Pero nosotros lo estamos poniendo directo.
47:47 - Unidentified Speaker
Ok.
47:47 - Unidentified Speaker
Si es así, ¿verdad Araceli?
47:50 - Noe Rocha
O sea, no hay una lista de precios mismo. Más bien es... Se pone en forma directa el número.
47:58 - Pedro Alberto Ayala Elizondo
Como campo abierto.
47:59 - Unidentified Speaker
Un saludo.
48:37 - Johann
Déjenme probar con un ítem existente para esta publicación de prueba, para ver si eso soluciona el error.
48:53 - Araceli Sanchez Jimenez
OK. Muchas gracias.
49:59 - Johann
Sí, creo y ya les comento cuando vuelva a hacer una prueba en cotización. Ok, perfecto.
50:11 - Araceli Sanchez Jimenez
Y mientras nosotros, nada más para resumir, nos quedamos con el pendiente de pulir muy bien el de este de Jira, todos los conceptos muy mapeados, los scrolls y todo, Pedro. Y no sé si haga falta algo adicional, Noe, aparte de esto.
50:28 - Noe Rocha
No, a partir de esto, no, porque esta es la primera prueba de escritura que íbamos a tener en Vine y habíamos acordado con Joseph muy puntuales en la prueba, de no hacer más pruebas de lo que definiéramos, para evitar pues errores, de repente, o cosas en producción que puedan afectar. Entonces, ya que una vez nos valide Johann, volvemos con él y seguimos con las siguientes actividades. Adelante, Pedro.
50:59 - Araceli Sanchez Jimenez
Yo nada más una duda con los campos de enlista que vamos a armar.
51:05 - Pedro Alberto Ayala Elizondo1
Estos campos de lista que están en vine hay una manera de dentro de vain de exportar esos esos campos o si los tengo que hacer con de manera con el api no sé si yo junto que se ha explorado más el api de vine que si hay forma de explorar eso verdad si igual te los pudiera compartir en formato json o excel como te sea más útil si esta bien al igual ahí me platico para ver de qué manera se puede obtener, o si yo lo puedo obtener yo por mi propia cuenta, ya te estaré avisando.
51:43 - Noe Rocha
Es que lo que va a querer hacer Pedro y Johann es algo parecido. Va a querer conectarse de Jirabai y jalar los datos para no tener que traer datos manuales. Entonces a lo mejor te va a preguntar un par de cosas para ir muy puntualmente por la data. Pero bueno, eso lo tenemos interno, ¿verdad? Claro.
52:03 - Araceli Sanchez Jimenez
Bueno, muchas gracias a todos. Gracias. Gracias a ustedes.
52:11 - Johann
Estamos en contacto.
52:14 - Unidentified Speaker
¡Adiós!
@@ -0,0 +1,47 @@
# WhatsApp — 18 al 20 ago 2026 · Johann ↔ Pedro Ayala (+52 1 81 2352 2958)
Volcado literal aportado por Johann el 20-ago-2026. Continúa el hilo de correo de campos de cotización ([REGISTRO #66](../bitacora/REGISTRO.md)) y cierra con la convocatoria de la sesión del **viernes 21-ago, 7:00 am**.
---
## 18-ago — comentarios al Excel: 17 categorías y campos marcados "Sí"
- **11:41 Johann:** Que tal Pedro, buenos días, cómo estás?
- **11:41 Pedro:** Que tal Johan buenos días / bien bien y tu?
- **11:42 Johann:** Tambien bien gracias, ya no te mandé mensaje acerca de lo del formulario con los nuevos campos
- **11:43 Pedro:** sii justo tengo comentarios al respecto
- **11:43 Pedro:** **logré reducirlo a 17 categorías pero tengo que verlas con Noé y Ara antes**
- **11:44 Pedro:** hablando del catálog[o]
- **11:44 Pedro:** **del formulario son lo que marcaste en "Si" en el excel compartido**
- **11:44 Johann:** Tenía pensado hacer una prueba con ese formulario y avisarte para ya hacer una prueba para mandar una cotización sobre bind, para que pronto también puedan hacer pruebas desde la plataforma
- **11:45 Pedro:** Entendido Johan, / que necesitas de nuestra parte / o cuando podríamos estar teniendo la siguiente sesión de seguimiento
- **11:47 Johann:** *(citando "logré reducirlo a 17 categorías…")* Ohh va
- **11:47 Johann:** *(citando "son lo que marcaste en Si…")* Sii, con esos ya podemos hacer una escritura en bind
- **11:49 Johann:** *(citando "que necesitas de nuestra parte")* **Estaría bien un ticket de prueba con este nuevo formulario, pudiendo pasando el ticket a una cotización sería un gran paso**
- **11:51 Johann:** *(citando "cuando la siguiente sesión")* Pudiera ser después de hacer la prueba con ese ticket, si es posible en esta semana, cómo están ustedes de tiempos?
- **11:53 Pedro:** si Johan esta semana nos viene bien. / entonces dejame definir el formulario con los campos que nos comentas que existen y regreso contigo
- **12:02 Johann:** Va, cualquier cosa estoy al pendiente
- **12:18 Pedro:** Gracias Johan
## 19-ago — Balam modificó formularios y campos; pide sesión con Noé y Ara
- **16:16 Pedro:** Que tal Johan buenas tardes / Como estas?
- **16:17 Pedro:** oye Johan para comentarte que **ya hemos realizado algunas modificaciones en nuestros formularios y campos** por lo que te solicito un espacio de tu tiempo para juntarnos a verlo con Noé y Ara
- **16:17 Pedro:** crees poder **este viernes a las 7**?
- **16:33 Johann:** Que tal Pedro, buenas tardes
- **16:34 Johann:** Sí claro, para esta junta **puedo preparar el caso de escritura**, o prefieren solo ver el formulario y lo trabajo después de la reunión
## 20-ago — se confirma la sesión y llega el ticket FAC-106
- **08:59 Pedro:** Que tal Johan / **si me parece bien el caso** / mando la invitación para mañana a las 7 am
- **09:47 Johann:** Buen día Pedro
- **09:48 Johann:** Sí claro, **podrías decirme algún ticket con el que pueda hacer ese ejercicio de escritura?**
- **10:47 Pedro:** si Johan / dame un momento / y te paso el numero de ticket
- **15:43 Pedro:** que tal Johan, **de momento no pude crear el ticket debido a que surgieron varios comentarios internos**, dejame tocar base con Noé para ver si tenemos la sesión el día de mañana
- **15:44 Johann:** Hola Pedro, okay, quedo al pendiente
- **16:23 Pedro:** **ya me confirmaron Johan, mañana a las 7 am**
- **16:24 Pedro:** te paso el link del ticekt **https://balam-jsm-temp.atlassian.net/browse/FAC-106**
- **16:24 Pedro:** **no te dejará entrar debido a la licencia** pero no se si necesites algun pantallazo de la estructura o si mañana los vems ya en sesión
- **16:24 Pedro:** te genero el meeting
- **16:28 Pedro:** te dejo el link de la sesión por aqui también *(→ correo "Seguimiento - PAF", 16:25)*
- **16:54 Johann:** entendido, gracias Pedro
@@ -0,0 +1,15 @@
# Correo — 20 ago 2026, 4:25 PM · Pedro Ayala → Noé Rocha, Johann
**Asunto:** Seguimiento - PAF
Correo con la convocatoria de la sesión del **viernes 21-ago, 7:00 am** (acordada por WhatsApp minutos antes) y la liga del ticket del ejercicio de escritura.
---
> https://balam-jsm-temp.atlassian.net/browse/FAC-106
>
> Reunión de Microsoft Teams
> Id. de reunión: 280 788 665 449 275
> Código de acceso: Xt6AH9nA
**Nota:** el cuerpo no indica fecha ni hora; la confirmación (viernes 21-ago, 7:00 am) consta en el hilo de WhatsApp con Pedro del mismo día, 4:23 pm.
@@ -0,0 +1,107 @@
# Informes de reunión de Read AI — sesiones "Seguimiento PAF" (21-ago → 29-sep 2026)
> Fuente: correos de `executiveassistant@e.read.ai` ("Noe Rocha via Read AI") en el buzón de Johann, leídos vía MCP ms365 el 29-sep-2026. **Solo el extracto que viaja en el correo**: el resumen, y capítulos/puntos de acción cuando el correo los muestra (varios vienen bloqueados: "🔒 Actualizar para ver"). Los informes completos viven en `app.read.ai/analytics/meetings/<id>` y requieren sesión. Horas en CDMX (UTC−6). Las convocatorias (Teams) las envió Pedro Ayala.
---
## 2026-08-21 · 7:00 AM · "Seguimiento - PAF" (informe 7:16 am)
**Resumen (truncado):** La reunión tuvo como objetivo validar la generación de una cotización desde Jira hacia "Vine" [sic: BIND]. Noe Rocha explicó que Pedro Alberto Ayala Elizondo homologó campos y ajustó el ticket para cumplir con los requisitos obligatorios. Se definió que el IVA nacional…
*(La parte técnica de esta sesión está en REGISTRO #69.)*
---
## 2026-08-21 · 1:00 PM · "Seguimiento - PAF (2)" (informe 1:34 pm) — convocatoria 7:14 am con https://balam-jsm-temp.atlassian.net/browse/FAC-106
**Resumen:** La reunión definió la integración entre Jira, Bind y la plataforma de cotizaciones, desde la creación del ticket y la cotización hasta la prefactura, factura y timbrado. Se acordó homologar los estados del flujo: la prefactura podría reflejarse como "en proceso de facturación" y la factura completada como "facturado". Algunos estados… *(truncado)*
**Capítulos & temas (visibles, de 9):**
- Validación inicial y pruebas de escritura
- Aún falta realizar pruebas de escritura en Jira para cambiar el estado de un ticket.
- Johann y Pedro acordaron repetir una prueba desde cero para validar la generación de la cotización y la escritura en Bind.
**Puntos de acción (visibles, de 12 capítulos):**
- Johann: Ejecuta una prueba de escritura en Jira para cambiar el estado de un ticket.
- Johann: Valida la cotización creada en "Vine" cuando Pedro tenga acceso para revisarla.
---
## 2026-08-24 · 7:00 AM · "Seguimiento - PAF" (informe 7:44 am)
**Resumen:** La reunión validó el flujo de conversión de cotizaciones a prefacturas en Bind, incluyendo captura, revisión, aprobación y registro financiero. Se realizaron pruebas en moneda nacional e internacional, con IVA del 16 % y 0 %, y se identificaron ajustes pendientes en la lógica fiscal y en la clasificación del… *(truncado)*
**Capítulos & temas (visibles, de 11):**
- Automatización entre Jira y Bind
- Se evaluará automatizar el paso de Jira a Bind como cotización para reducir clics y trabajo manual.
**Puntos de acción (visibles, de 15 capítulos):**
- Noe Rocha: Gestiona los accesos de Azure una vez validado el funcionamiento de las pruebas.
- Johann: Habilita o utiliza la aplicación en el VPS para facilitar las pruebas.
---
## 2026-08-26 · 7:00 AM · "Seguimiento - PAF" (informe 7:35 am)
**Resumen:** La reunión revisó la habilitación de permisos y el flujo de remisiones, prefacturación y facturación entre la plataforma y Bind. Noe Rocha señaló que algunas funcionalidades aún debían construirse para permitir el uso completo de la plataforma. Johann explicó que Bind no permite timbrar facturas mediante su API y propuso generar… *(truncado)*
**Capítulos & temas (visibles, de 7):**
- Coordinación para resolver la prefacturación
- Pedro debe solicitar apoyo para cancelar la factura generada durante la prueba.
- Johann estimó que la revisión del flujo de prefacturación podría quedar resuelta durante el día.
**Puntos de acción (visibles, de 5 capítulos):**
- Noe Rocha: Construye las funcionalidades necesarias para habilitar los permisos solicitados.
- Johann: Revisa y ajusta el estatus y el parámetro que está provocando que la operación se genere como factura en lugar de prefactura.
---
## 2026-09-03 · 7:00 AM · "Seguimiento PAF - 02/09/26" (informe 7:28 am)
**Resumen:** La reunión se enfocó en automatizar la generación de cotizaciones y facturas de Acuntia entre Jira, Bind y el ambiente de Azure. Se revisaron los datos necesarios de "Jitco" [sic] para configurar el Azure Tenant y permitir el montaje correspondiente. Para Acuntia, los conceptos dependen del headcount activo e incluyen textos como… *(truncado)*
**Capítulos & temas (visibles, de 6):**
- Solicitud de datos y preparación del ambiente de Azure
**Puntos de acción (visibles, de 8 capítulos):**
- Noe Rocha: Reenviar por correo a Pedro la solicitud de datos del "Jitco" para configurar el ambiente de Azure Tenant.
- Meeting participant: Realizar a la una y media el ejercicio de conversión de dos o tres tickets en cotizaciones.
---
## 2026-09-03 · 1:30 PM · "Seguimiento PAF (2) 03/09/26" (informe 1:51 pm)
**Resumen:** La reunión validó una aplicación para convertir tickets de "gira" [sic: Jira] en cotizaciones como etapa previa a la facturación. Johann confirmó que los tres tickets de Pedro ya se habían enviado como cotizaciones y que el concepto utiliza el nombre del ticket. En una prueba con un ticket de Linares se verificó… *(truncado)*
Capítulos y puntos de acción: 🔒 bloqueados en el correo.
---
## 2026-09-09 · 1:00 PM · "Seguimiento PAF - Prefactura" (informe 1:24 pm)
**Resumen:** El equipo revisó el procedimiento para crear una prefactura a partir de una cotización y confirmó que debe utilizarse «Generar venta», ya que «Generar pedido» no habilita esta función. Durante las pruebas, Araceli creó y guardó una prefactura mientras Johann analizaba las solicitudes registradas en las herramientas de desarrollador. Identificaron «InvoiceDetails»… *(truncado)*
Capítulos y puntos de acción: 🔒 bloqueados en el correo.
---
## 2026-09-10 · 1:00 PM · "Seguimiento PAF - Prefactura" (informe 1:21 pm)
**Resumen:** Johann Velazquez y Pedro Alberto Ayala Elizondo analizaron la cotización 1078 de Acuntia para identificar la petición de red utilizada al generar una prefactura y evaluar su posible replicación manual e integración posterior. Configuraron las herramientas de desarrollador, deshabilitaron la caché, conservaron los registros y habilitaron la columna de método… *(truncado)*
Capítulos y puntos de acción: 🔒 bloqueados en el correo.
---
## 2026-09-29 · 7:00 AM · "Seguimiento PAF - Integración ERP" (informe 7:23 am) — convocatoria 28-sep 5:10 pm
**Resumen:** La reunión se enfocó en validar accesos y ejecutar una prueba de integración ERP mediante la creación de cotizaciones y prefacturas en Bind. Pedro Alberto Ayala Elizondo confirmó que su perfil estaba habilitado y que las cuentas de Noe tenían permisos para crear facturas, prefacturas y operaciones relacionadas. Johann solicitó las… *(truncado)*
**Capítulos & temas (visibles, de 4):**
- Preparación de accesos para la prueba
- Pedro confirmó que las cuentas de Noe tienen permisos para crear facturas y prefacturas necesarias para la prueba
- Johann necesita recibir las credenciales en un archivo TXT para utilizarlas en la aplicación
**Puntos de acción (visibles, de 5 capítulos):**
- Johann: Revisar el error que impide cargar la solicitud al crear una cotización en Bind
- Johann: Crear la cotización en Bind después de corregir el error
@@ -0,0 +1,23 @@
# Correo · Noe Rocha → Johann (CC Pedro) · "Re: Seguimiento FAC-106" · 2026-08-24 3:48 pm
> Respuesta al correo de Johann del 21-ago 7:32 am ("Seguimiento FAC-106": causa del error 500 — `Type` de partida y escala del IVA — y cotización ya generada desde `FAC-106`). Traía **4 capturas de pantalla** de la UI de BIND (no se conservan aquí). Transcrito del buzón vía MCP ms365 el 29-sep-2026.
Hola Johan,
Ya revisamos las cotizaciones que se convirtieron en Remisión, y lo que nos explican es lo siguiente:
[captura 1]
Cuando una cotización se hace y se quiere convertir en Prefectura [sic] se selecciona la opción en Acciones en Bind que dice "Generar Venta", si se selecciona la opción Generar Pedido se hace remisión y eso no lo manejamos en Balam.
[captura 2]
Y en la siguiente ventana se selección [sic] la opción "Prefectura" para revisar antes de mandar la factura al cliente.
[captura 3]
[captura 4]
---
**Lectura (29-sep):** es la primera descripción escrita del mecanismo interno de la UI: *Acciones → Generar Venta → (ventana siguiente) Prefactura*. "Generar Pedido" = remisión, que es exactamente donde caían los POST con `IsFiscalInvoice=false` (folios 1000/1001). El tipo de documento se elige en la UI, no existe en el API público (REGISTRO #70, #74, #83).
@@ -0,0 +1,42 @@
# Correo · hilo "Acceso a Azure Balam y permisos." · 2026-08-24/25
> Transcrito del buzón vía MCP ms365 el 29-sep-2026. Antecedente: el 24-ago 3:52 pm Noé pidió lo mismo en el hilo "Re: Seguimiento: medición de consumo API de Jira y accesos" ("Para cerrar el tema del acceso a Azure vamos a necesitar de tu apoyo, ¿nos ayudas a detallar exactamente qué necesitas para publicar el ambiente?…").
## Noe Rocha → Johann (CC Pedro) · 2026-08-25 4:19 pm
Buenos días Johan,
Para cerrar el tema del acceso a Azure y no dar más vueltas, ¿nos ayudas a detallar exactamente qué necesitas para publicar el ambiente? Con eso preparamos la cuenta y los permisos correctos desde la primera vez.
En cuanto tengamos ese detalle lo validamos con la cuenta que estamos preparando para este uso y te damos acceso.
Quedo atento.
Saludos.
## Johann → Noe Rocha · 2026-08-25 4:54 pm
Buenos días,
Para publicar inicialmente el ambiente de staging de la plataforma necesito:
1. Una suscripción activa de Azure a nombre de Balam.
2. Un grupo de recursos dedicado al proyecto, por ejemplo rg-balam-paf-staging.
3. Acceso con rol Contributor únicamente sobre ese grupo de recursos.
4. Permiso Key Vault Secrets Officer sobre el Key Vault, para configurar las credenciales de BIND, Jira, JWT y base de datos.
5. Que estén habilitados los proveedores necesarios para:
- Azure App Service Linux (.NET 10)
- Azure Database for PostgreSQL Flexible Server 17
- Azure Key Vault
- Storage Account / Blob Storage
- Application Insights y Log Analytics
6. Para CI/CD desde GitHub, una identidad de despliegue mediante OpenID Connect, con rol Contributor sobre el mismo grupo de recursos. Si prefieren, puedo configurarla con apoyo de alguien que pueda realizar las asignaciones de roles.
Al habilitarlo necesitaría que me compartan:
- Tenant ID
- Subscription ID
- Nombre del grupo de recursos
- Región autorizada
- Cuenta con la que debo ingresar
No necesito Global Administrator, acceso a facturación ni permisos sobre toda la suscripción. Todo puede quedar limitado al grupo de recursos del proyecto.
Quedo atento. Saludos.
---
**Seguimiento:** al 29-sep-2026 el acceso a Azure no se ha entregado. El 3-sep se pidieron los datos del tenant en sesión (REGISTRO #76). La plataforma se publicó provisionalmente en https://paputec.mx/balam/ el 3-sep (REGISTRO #77).
+616
View File
@@ -0,0 +1,616 @@
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<title>Manual de usuario — Plataforma de Automatización Financiera · Balam</title>
<style>
@font-face{ font-family:"Caladea"; font-style:normal; font-weight:400; src:url("fonts/Caladea-Regular.ttf") format("truetype"); }
@font-face{ font-family:"Caladea"; font-style:normal; font-weight:700; src:url("fonts/Caladea-Bold.ttf") format("truetype"); }
@font-face{ font-family:"Caladea"; font-style:italic; font-weight:400; src:url("fonts/Caladea-Italic.ttf") format("truetype"); }
@font-face{ font-family:"Caladea"; font-style:italic; font-weight:700; src:url("fonts/Caladea-BoldItalic.ttf") format("truetype"); }
@font-face{ font-family:"Carlito"; font-style:normal; font-weight:400; src:url("fonts/Carlito-Regular.ttf") format("truetype"); }
@font-face{ font-family:"Carlito"; font-style:normal; font-weight:700; src:url("fonts/Carlito-Bold.ttf") format("truetype"); }
@font-face{ font-family:"Carlito"; font-style:italic; font-weight:400; src:url("fonts/Carlito-Italic.ttf") format("truetype"); }
@font-face{ font-family:"Carlito"; font-style:italic; font-weight:700; src:url("fonts/Carlito-BoldItalic.ttf") format("truetype"); }
@font-face{ font-family:"DejaVu Sans Mono"; font-style:normal; font-weight:400; src:url("fonts/DejaVuSansMono.ttf") format("truetype"); }
:root{
--ink:#1C2B39; /* deep slate navy — headings, dark fields */
--ink-2:#3C4A57; /* body text */
--muted:#6E7A86; /* meta, captions */
--hair:#E4E8EC; /* hairlines */
--hair-2:#C9D1D8; /* stronger hairlines */
--accent:#BC5B3E; /* terracotta accent */
--accent-d:#9A4530; /* darker terracotta for small text */
--accent-tint:#FBF1EC;/* pale terracotta wash */
--cream:#F8F5F0; /* warm panel */
--paper:#FFFFFF;
--ok:#2E7D5B;
--warn:#C0892E;
--no:#B23A48;
--serif:'Caladea','Bitstream Charter','DejaVu Serif',Georgia,serif;
--sans:'Carlito','DejaVu Sans','Noto Sans',system-ui,sans-serif;
--mono:'DejaVu Sans Mono','Liberation Mono',monospace;
}
*{ -webkit-print-color-adjust:exact; print-color-adjust:exact; box-sizing:border-box; }
html,body{ margin:0; padding:0; }
body{
font-family:var(--sans);
font-size:10.25pt;
line-height:1.55;
color:var(--ink-2);
background:var(--paper);
-webkit-font-smoothing:antialiased;
}
@page{ size:Letter; margin:15mm 16mm 18mm 16mm; }
@page :first{ margin:0; }
p{ margin:6pt 0; }
strong,b{ font-weight:700; color:var(--ink); }
em,i{ font-style:italic; }
a{ color:var(--accent-d); text-decoration:none; }
code{
font-family:var(--mono); font-size:8.6pt;
background:var(--cream); border:.5pt solid var(--hair);
border-radius:3pt; padding:.5pt 3pt; color:var(--ink);
}
ul,ol{ margin:6pt 0; padding-left:17pt; }
li{ margin:3pt 0; line-height:1.5; color:var(--ink-2); padding-left:2pt; }
li::marker{ color:var(--accent); }
ol li::marker{ color:var(--accent-d); font-weight:700; }
/* ---------------- COVER ---------------- */
.cover{
width:216mm; min-height:279mm;
background:var(--ink);
color:#EAEDF0;
padding:30mm 26mm 24mm;
display:flex; flex-direction:column;
position:relative; overflow:hidden;
}
.cover::after{ /* faint geometric corner motif */
content:""; position:absolute; right:-60mm; top:-60mm;
width:150mm; height:150mm; border-radius:50%;
background:radial-gradient(circle at center, rgba(188,91,62,.18), rgba(188,91,62,0) 70%);
}
.cover .top{ position:relative; z-index:2; }
.cover .rule{ width:46pt; height:3pt; background:var(--accent); margin-bottom:14pt; }
.cover .eyebrow{
font-family:var(--sans); font-size:10pt; font-weight:700;
letter-spacing:.34em; text-transform:uppercase; color:#D89A82;
}
.cover .mid{ position:relative; z-index:2; margin-top:auto; margin-bottom:auto; padding:18mm 0; }
.cover h1{
font-family:var(--serif); font-weight:700;
font-size:37pt; line-height:1.06; letter-spacing:-.01em;
margin:0; color:#FFFFFF; max-width:150mm;
}
.cover .client{
margin-top:16pt; font-family:var(--sans); font-size:12.5pt; color:#C9D1D8;
}
.cover .client b{ color:var(--accent); font-weight:700; }
.cover .meta{ position:relative; z-index:2; }
.cover .meta-grid{
display:grid; grid-template-columns:34mm 1fr;
gap:7pt 14pt; border-top:.6pt solid rgba(255,255,255,.18);
padding-top:14pt;
}
.cover .meta-grid dt{
font-family:var(--sans); font-size:7.5pt; font-weight:700;
letter-spacing:.12em; text-transform:uppercase; color:#8FA0AD; padding-top:1.5pt;
}
.cover .meta-grid dd{ margin:0; font-size:9.5pt; color:#DDE3E8; line-height:1.45; }
.cover .meta-grid dd .who{ display:block; }
.cover .meta-grid dd .who b{ color:#FFFFFF; font-weight:700; }
.cover .meta-grid dd .who span{ color:#9FB0BC; }
.cover .conf{
position:relative; z-index:2; margin-top:18pt;
display:flex; justify-content:space-between; align-items:baseline;
font-family:var(--sans); font-size:8pt; letter-spacing:.06em;
text-transform:uppercase; color:#7C8B97;
border-top:.6pt solid rgba(255,255,255,.12); padding-top:10pt;
}
.cover .conf .badge{ color:var(--accent); font-weight:700; }
/* ---------------- TOC ---------------- */
.toc{ break-before:page; padding-top:6mm; }
.toc h2{
font-family:var(--serif); font-size:20pt; color:var(--ink);
margin:0 0 2pt; font-weight:700;
}
.toc .kick{
font-family:var(--sans); font-size:8pt; font-weight:700; letter-spacing:.2em;
text-transform:uppercase; color:var(--accent-d); margin-bottom:14pt;
}
.toc .rule{ width:46pt; height:2.5pt; background:var(--accent); margin:6pt 0 16pt; }
.toc-row{ display:flex; align-items:baseline; gap:9pt; padding:6.5pt 0; border-bottom:.5pt solid var(--hair); }
.toc-row .n{ font-family:var(--serif); font-weight:700; font-size:11pt; color:var(--accent); min-width:24pt; }
.toc-row .t{ font-family:var(--serif); font-size:11.5pt; color:var(--ink); }
.toc-row.section .t{ font-weight:700; }
.toc-row .dots{ flex:1; border-bottom:.6pt dotted var(--hair-2); transform:translateY(-3pt); }
.toc-row .pg{ font-family:var(--sans); font-size:9.5pt; color:var(--muted); font-variant-numeric:tabular-nums; min-width:14pt; text-align:right; }
.toc-row.annex{ margin-top:2pt; }
.toc-row.annex .n,.toc-row.annex .t{ color:var(--ink); }
.toc-row.annex .n{ color:var(--accent); }
/* ---------------- CONTENT ---------------- */
.content{ break-before:page; }
.sec{ margin-top:20pt; }
.sec:first-child{ margin-top:0; }
.sec-head{
display:flex; align-items:baseline; gap:14pt;
margin:0 0 9pt; padding-bottom:8pt;
border-bottom:1.2pt solid var(--ink); break-after:avoid;
}
.sec-head.no-num{ gap:0; }
.sec-num{
font-family:var(--serif); font-size:29pt; font-weight:700;
color:var(--accent); line-height:.85; letter-spacing:-.02em; min-width:38pt;
}
.sec-head h2{
font-family:var(--serif); font-size:18pt; font-weight:700;
color:var(--ink); line-height:1.12; margin:0;
}
.sec-head .kick{
display:block; font-family:var(--sans); font-size:7.5pt; font-weight:700;
letter-spacing:.16em; text-transform:uppercase; color:var(--accent-d); margin-bottom:3pt;
}
h3{ font-family:var(--sans); font-size:11.5pt; font-weight:700; color:var(--ink); margin:15pt 0 5pt; break-after:avoid; }
h3 .sn{ color:var(--accent-d); font-weight:700; margin-right:7pt; }
h4{ font-family:var(--sans); font-size:10pt; font-weight:700; color:var(--ink); margin:12pt 0 4pt; break-after:avoid; }
.mini-label{
font-family:var(--sans); font-size:8pt; font-weight:700; letter-spacing:.1em;
text-transform:uppercase; color:var(--accent-d); margin:10pt 0 4pt;
}
.lede{ font-size:11pt; line-height:1.62; color:var(--ink); }
/* drop cap for executive summary */
.dropcap > p:first-of-type::first-letter{
font-family:var(--serif); font-weight:700; color:var(--accent);
font-size:38pt; line-height:.82; float:left; padding:2pt 7pt 0 0;
}
/* ETAPA block */
.etapa{ margin:16pt 0 5pt; padding-top:9pt; border-top:.8pt solid var(--hair-2); break-after:avoid; }
.etapa .et-t{ font-family:var(--serif); font-size:13pt; font-weight:700; color:var(--ink); line-height:1.2; }
.etapa .et-meta{ font-family:var(--sans); font-size:8.5pt; font-weight:700; letter-spacing:.05em; text-transform:uppercase; color:var(--accent-d); margin-top:3pt; }
/* nested sub-block: an add-on that belongs to the PRECEDING .etapa, not a phase of its own */
.ajuste-anidado{ margin:12pt 0 14pt 10pt; padding:10pt 14pt 11pt; background:var(--cream); border-left:2.5pt solid var(--accent); border-radius:0 5pt 5pt 0; break-inside:avoid; }
.ajuste-anidado .aj-kick{ font-family:var(--sans); font-size:7.6pt; font-weight:700; letter-spacing:.14em; text-transform:uppercase; color:var(--accent-d); }
.ajuste-anidado .aj-t{ font-family:var(--serif); font-size:12pt; font-weight:700; color:var(--ink); line-height:1.2; margin-top:2pt; }
.ajuste-anidado .aj-meta{ font-family:var(--sans); font-size:8.5pt; font-weight:700; letter-spacing:.03em; color:var(--muted); margin-top:3pt; }
.ajuste-anidado > .aj-body{ margin-top:9pt; }
.ajuste-anidado p, .ajuste-anidado ul{ margin:6pt 0; }
/* ---------------- TABLES ---------------- */
table{ width:100%; border-collapse:collapse; margin:10pt 0; font-size:9.2pt; }
thead{ display:table-header-group; }
th{
text-align:left; font-family:var(--sans); font-weight:700; color:var(--ink);
font-size:7.8pt; letter-spacing:.045em; text-transform:uppercase;
padding:7pt 9pt; border-bottom:1.4pt solid var(--ink); vertical-align:bottom;
}
td{ padding:6.5pt 9pt; border-bottom:.6pt solid var(--hair); vertical-align:top; color:var(--ink-2); line-height:1.42; }
tr{ break-inside:avoid; }
td.k{ color:var(--ink); font-weight:600; }
.num,th.num{ text-align:right; font-variant-numeric:tabular-nums; white-space:nowrap; }
.ctr,th.ctr{ text-align:center; }
tr.total td{ font-weight:700; color:var(--ink); background:var(--cream); border-top:1.2pt solid var(--ink); border-bottom:1.2pt solid var(--ink); }
tr.sub-total td{ font-weight:700; color:var(--ink); background:var(--accent-tint); border-top:1pt solid var(--accent); }
.cov{ font-size:11pt; text-align:center; line-height:1; }
table.compact td,table.compact th{ padding:5.5pt 8pt; }
/* ---------------- CALLOUTS ---------------- */
.callout{
break-inside:avoid; background:var(--accent-tint);
border-left:3pt solid var(--accent); border-radius:0 4pt 4pt 0;
padding:9pt 14pt; margin:11pt 0; font-size:9.4pt; line-height:1.5; color:var(--ink-2);
}
.callout strong,.callout b{ color:var(--accent-d); }
.callout.cool{ background:var(--cream); border-left-color:var(--ink); }
.callout.cool strong,.callout.cool b{ color:var(--ink); }
/* ---------------- FLOW CHIPS ---------------- */
.flow{
break-inside:avoid; display:flex; flex-wrap:wrap; align-items:center; justify-content:center;
gap:6pt; margin:14pt 0; padding:14pt 12pt; background:var(--cream); border-radius:7pt;
}
.chip{
font-family:var(--sans); font-weight:600; font-size:9pt; color:var(--ink);
background:#fff; border:.7pt solid var(--hair-2); padding:5pt 11pt; border-radius:20pt;
box-shadow:0 1pt 2pt rgba(28,43,57,.05);
}
.chip.accent{ background:var(--accent); color:#fff; border-color:var(--accent); }
.arrow{ color:var(--accent); font-weight:700; font-size:11pt; }
.aw{ font-family:var(--sans); } /* flechas →/↔ en títulos serif (Caladea no trae el glifo) */
.keep{ break-inside:avoid; }
/* ---------------- SYNTHESIS CARDS ---------------- */
.synthesis{ display:grid; grid-template-columns:1fr 1fr; gap:9pt; margin:12pt 0; break-inside:avoid; }
.scard{ border:.7pt solid var(--hair); border-top:2.5pt solid var(--accent); border-radius:5pt; padding:9pt 12pt; background:#fff; }
.scard .lab{ font-family:var(--sans); font-size:7.3pt; font-weight:700; letter-spacing:.1em; text-transform:uppercase; color:var(--accent-d); margin-bottom:3pt; }
.scard .big{ font-family:var(--serif); font-size:13pt; font-weight:700; color:var(--ink); line-height:1.15; }
.scard .val{ font-size:9pt; color:var(--ink-2); line-height:1.42; }
.scard .sm{ font-size:8.2pt; color:var(--muted); margin-top:2pt; }
/* sign-off */
.signoff{ margin-top:24pt; padding-top:14pt; border-top:1.2pt solid var(--ink); break-inside:avoid; }
.signoff .nm{ font-family:var(--serif); font-size:14pt; font-weight:700; color:var(--ink); }
.signoff .rl{ font-family:var(--sans); font-size:9pt; color:var(--muted); margin-top:2pt; letter-spacing:.03em; }
hr.div{ border:none; border-top:.6pt solid var(--hair); margin:16pt 0; }
.spacer{ height:4pt; }
/* ---------------- MANUAL: componentes propios ---------------- */
figure{ margin:10pt 0 12pt; break-inside:avoid; }
figure img{ display:block; width:84%; margin:0 auto; border:.6pt solid var(--hair-2); border-radius:4pt; }
figcaption{ width:84%; margin:4pt auto 0; font-size:8.2pt; color:var(--muted); line-height:1.4; }
figure.s img, figure.s figcaption{ width:52%; }
.fig-pair figure img, .fig-pair figcaption{ width:100%; }
.fig-pair{ display:grid; grid-template-columns:1.52fr 1fr; gap:10pt; align-items:start; margin:10pt 0 12pt; break-inside:avoid; }
.fig-pair figure{ margin:0; }
.ui{ font-family:var(--sans); font-weight:700; font-size:9pt; color:var(--ink); background:#fff;
border:.6pt solid var(--hair-2); border-radius:3pt; padding:0 3.5pt; white-space:nowrap; }
.st{ font-family:var(--sans); font-weight:700; font-size:8.6pt; color:var(--ink); background:var(--cream);
border-radius:10pt; padding:.5pt 6pt; white-space:nowrap; }
.steps{ counter-reset:paso; list-style:none; padding-left:0; margin:8pt 0; }
.steps > li{ counter-increment:paso; position:relative; padding:5pt 0 5pt 26pt; margin:0; border-bottom:.5pt solid var(--hair); break-inside:avoid; }
.steps > li:last-child{ border-bottom:none; }
.steps > li::before{ content:counter(paso); position:absolute; left:0; top:5pt; width:17pt; height:17pt; border-radius:50%;
background:var(--ink); color:#fff; font-family:var(--serif); font-weight:700; font-size:9pt; text-align:center; line-height:17pt; }
td.msg{ font-family:var(--sans); font-weight:600; color:var(--ink); }
.nota{ font-size:8.6pt; color:var(--muted); }
</style>
</head>
<body>
<!-- ============ COVER ============ -->
<div class="cover">
<div class="top">
<div class="rule"></div>
<div class="eyebrow">Manual de usuario</div>
</div>
<div class="mid">
<h1>Plataforma de Automatización Financiera</h1>
<div class="client"><b>Balam</b> · facturación y cobranza sobre BIND ERP y Jira</div>
</div>
<div class="meta">
<dl class="meta-grid">
<dt>Para</dt>
<dd>Finanzas, Administración y Dirección de Balam</dd>
<dt>Preparado por</dt>
<dd>Johann Velazquez — Consultor de Software · Monterrey, N.L.</dd>
<dt>Fecha</dt>
<dd>2 de octubre de 2026</dd>
<dt>Versión</dt>
<dd>1.0 — plataforma publicada en https://paputec.mx/balam/ (ambiente provisional mientras se despliega en Azure)</dd>
</dl>
<div class="conf">
<span class="badge">Documento confidencial</span>
<span>Uso interno de Balam</span>
</div>
</div>
</div>
<!-- ============ TOC ============ -->
<div class="toc">
<div class="kick">Contenido</div>
<h2>Índice</h2>
<div class="rule"></div>
<div class="toc-row section"><span class="n">01</span><span class="t">Antes de empezar</span><span class="dots"></span><span class="pg">{{PG_1}}</span></div>
<div class="toc-row section"><span class="n">02</span><span class="t">Consultar cartera, facturas y clientes</span><span class="dots"></span><span class="pg">{{PG_2}}</span></div>
<div class="toc-row section"><span class="n">03</span><span class="t">Solicitudes de Jira</span><span class="dots"></span><span class="pg">{{PG_3}}</span></div>
<div class="toc-row section"><span class="n">04</span><span class="t">Del ticket a la cotización</span><span class="dots"></span><span class="pg">{{PG_4}}</span></div>
<div class="toc-row section"><span class="n">05</span><span class="t">De la cotización a la prefactura</span><span class="dots"></span><span class="pg">{{PG_5}}</span></div>
<div class="toc-row section"><span class="n">06</span><span class="t">De la prefactura a la factura (timbrado)</span><span class="dots"></span><span class="pg">{{PG_6}}</span></div>
<div class="toc-row section"><span class="n">07</span><span class="t">Reglas que conviene recordar</span><span class="dots"></span><span class="pg">{{PG_7}}</span></div>
<div class="toc-row section"><span class="n">08</span><span class="t">Mensajes frecuentes y qué hacer</span><span class="dots"></span><span class="pg">{{PG_8}}</span></div>
<div class="toc-row section"><span class="n">09</span><span class="t">Lo que se sigue haciendo en BIND</span><span class="dots"></span><span class="pg">{{PG_9}}</span></div>
</div>
<!-- ============ CONTENT ============ -->
<main class="content">
<!-- ===== 01 ===== -->
<section class="sec">
<div class="sec-head"><span class="sec-num">01</span><div><span class="kick">Primeros pasos</span><h2>Antes de empezar</h2></div></div>
<p class="lede">La plataforma junta en un solo lugar los tickets de facturación de Jira, las cotizaciones y facturas de BIND y la cartera por cobrar. BIND sigue siendo el sistema oficial: la plataforma lee de BIND cada 15 minutos y, cuando escribe en él, lo hace siempre con una vista previa y la confirmación de una persona.</p>
<div class="flow">
<span class="chip">Ticket en Jira (FAC)</span><span class="arrow">→</span>
<span class="chip">Cotización en BIND</span><span class="arrow">→</span>
<span class="chip">Prefactura</span><span class="arrow">→</span>
<span class="chip accent">Factura (CFDI)</span>
</div>
<div class="callout"><b>Regla de oro.</b> Nada se crea en BIND sin que una persona vea antes la vista previa (los botones que dicen <i>dry-run</i>) y confirme. El timbrado, que es lo único con efecto fiscal, además pide teclear la palabra <b>TIMBRAR</b>.</div>
<h3>Cómo entrar</h3>
<ol>
<li>Abre <b>https://paputec.mx/balam/</b> en el navegador.</li>
<li>Teclea tu <span class="ui">Correo</span> y tu <span class="ui">Contraseña</span> y pulsa <span class="ui">Entrar</span>. La primera pantalla es <i>Cuentas por cobrar</i>.</li>
</ol>
<p>La sesión dura 8 horas; al vencer, la plataforma regresa al inicio de sesión. Cinco intentos fallidos bloquean la cuenta 15 minutos. Los usuarios los da de alta el administrador de la plataforma; no hay registro desde la pantalla.</p>
<h3>Qué puede hacer cada rol</h3>
<table class="compact">
<thead><tr><th style="width:26%">Rol</th><th>Puede</th></tr></thead>
<tbody>
<tr><td class="k">Finanzas</td><td>Todo: consultar, cotizar (uno o en lote), generar prefacturas (una o en lote), timbrar, aceptar tickets nuevos, cambiar el modo de la campana y sincronizar con BIND.</td></tr>
<tr><td class="k">Administración</td><td>Lo mismo que Finanzas.</td></tr>
<tr><td class="k">Dirección</td><td>Consultar todo. No crea documentos en BIND.</td></tr>
<tr><td class="k">Operaciones</td><td>Consultar.</td></tr>
</tbody>
</table>
<p class="nota">Con un rol de consulta, los botones de escritura avisan, por ejemplo: «Tu rol es de consulta: la prefactura la genera Finanzas o Administración.»</p>
<h3>El menú y la barra superior</h3>
<table class="compact">
<thead><tr><th style="width:20%">Grupo</th><th style="width:26%">Pantalla</th><th>Para qué sirve</th></tr></thead>
<tbody>
<tr><td class="k" rowspan="3">Facturación</td><td>Solicitudes (Jira)</td><td>Bandeja de tickets FAC y la etapa en que va cada uno en BIND (§03).</td></tr>
<tr><td>Cotizaciones (BIND)</td><td>Todas las cotizaciones de BIND, con o sin ticket, para convertirlas en prefactura (§05).</td></tr>
<tr><td>Facturas</td><td>Prefacturas y facturas con filtros, detalle, PDF y timbrado (§02, §06).</td></tr>
<tr><td class="k" rowspan="2">Cobranza</td><td>Cuentas por cobrar</td><td>Saldos por cobrar por moneda (§02).</td></tr>
<tr><td>Clientes</td><td>Catálogo de clientes sincronizado desde BIND (§02).</td></tr>
</tbody>
</table>
<p>Los módulos marcados <span class="st">próximamente</span> (Dashboard, Envío al cliente, Pagos y recordatorios y Bitácora) son de la siguiente etapa y todavía no se pueden abrir. En la barra superior, el chip <span class="st">BIND actualizado hace N min</span> dice cuándo fue la última lectura de BIND, y la <b>campana</b> guarda los tickets nuevos de Jira (§04). Abajo del menú, <i>BIND ERP · operativo</i> confirma que la sincronización funciona.</p>
<p class="nota">Las capturas de este manual se tomaron en un ambiente de demostración con datos ficticios.</p>
</section>
<!-- ===== 02 ===== -->
<section class="sec break">
<div class="sec-head"><span class="sec-num">02</span><div><span class="kick">Consulta</span><h2>Consultar cartera, facturas y clientes</h2></div></div>
<h3>Cuentas por cobrar</h3>
<p>Cuentas por cobrar es la pantalla de inicio. Muestra una tarjeta por moneda (MXN y USD): los saldos nunca se suman ni se convierten entre monedas.</p>
<table class="compact">
<thead><tr><th style="width:26%">Fila</th><th>Qué incluye</th></tr></thead>
<tbody>
<tr><td class="k">Saldo exigible</td><td>Vigente + vencido. No incluye prefacturas sin timbrar.</td></tr>
<tr><td class="k">Vigente (n)</td><td>Facturas con saldo que aún no vencen.</td></tr>
<tr><td class="k">Vencido (n)</td><td>Facturas con saldo a partir del día siguiente a su vencimiento.</td></tr>
<tr><td class="k">Emitidas (n)</td><td>Prefacturas que todavía no se timbran.</td></tr>
</tbody>
</table>
<p><span class="ui">Ver vencidas</span> abre <i>Facturas</i> ya filtrado por esa moneda y por el estado Vencida; <span class="ui">Todas</span> abre la lista completa de esa moneda.</p>
<h3>Facturas</h3>
<figure>
<img src="img/facturas.jpg" alt="Listado de facturas con filtros">
<figcaption>Facturas: búsqueda, filtros por estado, moneda y fechas, y exportación a CSV.</figcaption>
</figure>
<p>Lista las ventas de BIND, prefacturas y facturas. Puedes buscar por folio, UUID o cliente y filtrar por <i>Estado</i>, <i>Moneda</i> y fechas <i>Desde</i> / <i>Hasta</i>. <span class="ui">Exportar CSV</span> descarga lo que tengas filtrado (hasta 200 filas); Excel lo abre directo.</p>
<table class="compact">
<thead><tr><th style="width:20%">Estado</th><th>Significa</th></tr></thead>
<tbody>
<tr><td><span class="st">Prefactura</span></td><td>Todavía no tiene UUID (no está timbrada). Una prefactura nunca vence.</td></tr>
<tr><td><span class="st">Vigente</span></td><td>Timbrada, con saldo y dentro de plazo.</td></tr>
<tr><td><span class="st">Vencida</span></td><td>Con saldo a partir del día siguiente a su vencimiento (hora de México).</td></tr>
<tr><td><span class="st">Pagada</span></td><td>BIND la marca pagada, o el saldo llegó a cero con pagos registrados.</td></tr>
<tr><td><span class="st">Cancelada</span></td><td>Cancelada en BIND.</td></tr>
</tbody>
</table>
<h3>Detalle de una factura</h3>
<figure>
<img src="img/detalle-factura.jpg" alt="Detalle de una prefactura con la tarjeta de timbrado">
<figcaption>Detalle de una prefactura: montos, datos del CFDI, PDF y, abajo, la tarjeta para timbrarla (§06).</figcaption>
</figure>
<p>Al pulsar el folio se abre el detalle con sus <b>Montos</b> (total, abonos, notas de crédito y saldo; en USD también el tipo de cambio de referencia) y sus <b>Fechas y CFDI</b> (emisión, vencimiento, método de pago y UUID). <span class="ui">PDF del CFDI</span> descarga el PDF desde BIND; el XML se consulta en BIND. Si el documento es una prefactura, aparecen además <span class="ui">Ver en BIND</span>, <span class="ui">¿Ya se timbró?</span> (relee la venta en BIND y actualiza el estado al instante) y la tarjeta de timbrado.</p>
<h3>Clientes</h3>
<p>El catálogo de clientes se sincroniza desde BIND. Se busca por nombre o RFC y se exporta a CSV. Un ícono avisa de un <i>posible duplicado</i> cuando dos clientes comparten RFC, y otro abre las facturas de ese cliente.</p>
</section>
<!-- ===== 03 ===== -->
<section class="sec break">
<div class="sec-head"><span class="sec-num">03</span><div><span class="kick">Facturación</span><h2>Solicitudes de Jira</h2></div></div>
<figure>
<img src="img/bandeja.jpg" alt="Bandeja de solicitudes de Jira">
<figcaption>La bandeja: pestañas por estatus de Jira, búsqueda, botones de lote y, a la derecha, la columna BIND con la etapa de cada ticket.</figcaption>
</figure>
<p>La bandeja muestra los tickets del proyecto FAC: <i>sin ticket, no se factura</i>. Es de solo lectura: la plataforma nunca modifica Jira. Hay una pestaña por cada estatus de Jira presente (Open, En proceso de facturación, En validación, Facturado…), una búsqueda por ticket, cliente o proyecto, y <span class="ui">⟳ Actualizar</span> para volver a leer Jira en ese momento (si no, la lista se refresca cada minuto).</p>
<h3>La columna BIND</h3>
<table class="compact">
<thead><tr><th style="width:28%">Indicador</th><th>Qué quiere decir</th></tr></thead>
<tbody>
<tr><td><span class="st">Cotizada 1504</span></td><td>El ticket ya tiene cotización en BIND (folio).</td></tr>
<tr><td><span class="st">Prefactura 2003</span></td><td>Ya tiene prefactura; falta timbrarla.</td></tr>
<tr><td><span class="st">Facturada 2003</span></td><td>La cadena está completa: ya hay CFDI.</td></tr>
<tr><td><span class="st">Lista para revisar</span></td><td>La plataforma ya preparó la cotización; falta que alguien la acepte.</td></tr>
<tr><td><span class="st">Faltan datos</span></td><td>No se pudo preparar sola; al pasar el cursor dice qué falta.</td></tr>
<tr><td><span class="st">Vista previa pendiente</span></td><td>BIND o Jira no respondieron; la plataforma lo reintenta sola.</td></tr>
<tr><td><span class="st">Revisar en BIND</span></td><td>BIND no confirmó si creó la cotización: revísalo allá antes de intentar otra vez.</td></tr>
</tbody>
</table>
<h3>El detalle del ticket</h3>
<p>Al hacer clic en una fila se abre el ticket con sus datos (tipo, cliente, proyecto, moneda, días de crédito, monto estimado, orden de compra), sus adjuntos (el primer PDF se abre solo en el visor), la descripción y la cotización o prefactura vinculadas. Arriba están <span class="ui">Ver en Jira ↗</span> y <b>un solo botón con el paso siguiente</b>:</p>
<ul>
<li><span class="ui">Crear cotización en BIND →</span> si todavía no tiene cotización (§04).</li>
<li><span class="ui">Generar prefactura →</span> si ya tiene cotización (§05).</li>
<li><span class="ui">Seguimiento del timbrado →</span> si ya tiene prefactura (§06).</li>
</ul>
<p>Las casillas de la bandeja solo aparecen en los tickets que se pueden llevar a un lote: los que no tienen cotización van a <span class="ui">Cotizar seleccionados (N) →</span> y los que tienen una cotización activa sin prefactura van a <span class="ui">Prefacturar seleccionados (N) →</span>.</p>
</section>
<!-- ===== 04 ===== -->
<section class="sec break">
<div class="sec-head"><span class="sec-num">04</span><div><span class="kick">Paso 1 de la cadena</span><h2>Del ticket a la cotización</h2></div></div>
<h3>El aviso en vivo</h3>
<p>Cuando alguien crea un ticket en FAC, Jira avisa a la plataforma en alrededor de un segundo; la plataforma lee el ticket, prepara la cotización en vista previa y, a quien tenga la plataforma abierta, le aparece este aviso:</p>
<div class="fig-pair">
<figure>
<img src="img/modal.jpg" alt="Aviso en vivo de un ticket nuevo">
<figcaption>El aviso con la línea de tiempo real (creado en Jira → detectado → vista previa) y los importes de la cotización.</figcaption>
</figure>
<figure>
<img src="img/campana.jpg" alt="Campana con los tickets de Jira y el modo">
<figcaption>La campana: tickets de los últimos 7 días y el modo «Pedir confirmación» / «Crear en automático».</figcaption>
</figure>
</div>
<ul>
<li><span class="ui">Aceptar y crear cotización</span> crea en BIND exactamente lo que muestra el aviso, una sola vez: dos clics o dos personas a la vez no crean dos cotizaciones.</li>
<li><span class="ui">Revisar y editar</span> abre la pantalla de cotización para cambiar algo antes de crearla.</li>
<li><span class="ui">Más tarde</span> lo guarda en la campana; <i>Descartar</i> lo quita de pendientes sin tocar BIND.</li>
</ul>
<p>Si nadie decide en 30 segundos, el aviso se guarda solo en la campana (con el cursor encima la cuenta se pausa). Si al ticket le falta algo, el aviso dice «No se pudo armar la cotización sola» con lo que falta, por ejemplo el cliente o el servicio, y ofrece <span class="ui">Completar en pantalla</span>.</p>
<div class="callout cool"><b>Modo de la campana.</b> El modo vigente es <b>Pedir confirmación</b>: la plataforma prepara la cotización y una persona la acepta. <b>Crear en automático</b> crea cotizaciones reales en BIND sin revisión previa; solo lo pueden activar Finanzas o Administración y conviene hacerlo únicamente con la autorización de la Dirección.</div>
<h3>Cotizar un ticket en pantalla</h3>
<p>Desde la bandeja (<span class="ui">Cotizar →</span>), desde el ticket o desde el aviso se abre <i>Generar cotización en BIND</i>, ya prellenada con los datos del ticket:</p>
<ol class="steps">
<li><b>Cliente de BIND.</b> Jira no trae RFC, así que el cliente se confirma contra el catálogo de BIND; al elegirlo se ven su RFC y sus días de crédito.</li>
<li><b>Documento y concepto.</b> Moneda, ubicación, IVA (16 % nacional o 0 % extranjero), el <i>ítem del catálogo BIND</i> (se resuelve solo desde la categoría del ticket; si aparece <span class="st">Sin ítem</span>, elígelo a mano), el concepto (el título del ticket), unidad, cantidad, precio unitario sin IVA y comentarios.</li>
<li><b>Vista previa y creación.</b> <span class="ui">Ver vista previa (dry-run)</span> muestra lo que se enviará; después <span class="ui">Crear cotización en BIND →</span> pide confirmar el cliente y el monto. Al terminar aparece «✓ Cotización creada en BIND» con su folio, ya vinculada al ticket.</li>
</ol>
<p>Cualquier cambio después de la vista previa la invalida y hay que volver a generarla. Si el ticket ya tiene cotización, la pantalla lo dice y ofrece <span class="ui">Continuar a prefactura →</span>: un ticket no se cotiza dos veces.</p>
<h3>Cotizar varios tickets a la vez</h3>
<p>En la bandeja, marca los tickets y pulsa <span class="ui">Cotizar seleccionados (N) →</span>. Se crea una cotización por ticket: revisa cada fila (cliente, ítem, concepto, cantidad, precio, IVA), pulsa <span class="ui">Ver vista previa de todo (N)</span> y luego <span class="ui">Crear N cotización(es) en BIND →</span>, con una sola confirmación. Los tickets que ya tienen cotización se omiten, las filas se crean una tras otra y, si BIND rechaza una, las demás siguen y esa queda en <span class="st">Error</span> con el motivo.</p>
</section>
<!-- ===== 05 ===== -->
<section class="sec break">
<div class="sec-head"><span class="sec-num">05</span><div><span class="kick">Paso 2 de la cadena</span><h2>De la cotización a la prefactura</h2></div></div>
<p>La prefactura se genera siempre desde una cotización <b>Activa</b> de BIND. La plataforma la crea entrando a la web de BIND con su propio usuario, porque el API de BIND no permite crear prefacturas. Hay tres caminos: desde <i>Cotizaciones (BIND)</i>, desde el ticket o en lote.</p>
<h3>Desde Cotizaciones (BIND)</h3>
<figure>
<img src="img/cotizaciones.jpg" alt="Cotizaciones de BIND con dos marcadas para el lote">
<figcaption>Cotizaciones (BIND): el total va en la moneda de cada cotización; las casillas sirven para el lote.</figcaption>
</figure>
<p>Aquí están todas las cotizaciones de BIND, tengan o no ticket en Jira. El filtro <i>Estado</i> arranca en <span class="st">Activa</span>; <span class="st">Surtida</span> quiere decir que ya generó una venta y <span class="st">Cancelada</span> que se canceló en BIND. Si acabas de crear una cotización en BIND y no aparece, pulsa <span class="ui">Sincronizar ahora</span>. En una fila Activa, <span class="ui">Generar prefactura →</span> abre debajo la tarjeta de prefactura:</p>
<ol class="steps">
<li><span class="ui">1 · Probar sesión (solo lectura)</span> comprueba que la plataforma puede entrar a BIND. Es opcional.</li>
<li><span class="ui">2 · Ver JSON exacto (dry-run)</span> muestra el cliente, las partidas, el subtotal y el <b>folio previsto</b>, más un recuadro «Lo que se asumió al armar el cuerpo — léelo antes de confirmar».</li>
<li><span class="ui">3 · Generar prefactura en BIND →</span> pide confirmar y crea la prefactura. El resultado dice «Prefactura (sin timbrar…)» con su folio, y abajo aparece la tarjeta de timbrado (§06).</li>
</ol>
<p>Si el resultado dice que el API de BIND «aún no la muestra», es normal durante el primer minuto: la prefactura sí se creó. Confírmalo con <span class="ui">¿Ya se timbró? Verificar</span> un minuto después.</p>
<h3>Desde el ticket: emisión asistida</h3>
<p>En un ticket ya cotizado, <span class="ui">Generar prefactura →</span> abre <i>Preparación, aprobación y emisión de CFDI</i> en cinco pasos: confirmar la cotización de BIND, revisar los datos de la factura (método de pago PPD/PUE, uso CFDI), revisar conceptos e IVA, ver el borrador y, en el paso 5, aprobar y generar la prefactura con la misma tarjeta de arriba.</p>
<div class="callout"><b>Importante.</b> La prefactura se arma con los datos de la cotización y del cliente <b>en BIND</b>. Los pasos 2 a 4 sirven para revisar, pero lo que se elija ahí no cambia la prefactura. Si algo debe ser distinto, como el método de pago, el uso CFDI o el domicilio, corrígelo en BIND.</div>
<h3>Varias prefacturas a la vez</h3>
<figure>
<img src="img/lote-prefacturas.jpg" alt="Prefacturas en lote con vistas previas">
<figcaption>Prefacturas en lote: una fila por cotización con su vista previa y sus avisos; se generan con una sola confirmación.</figcaption>
</figure>
<p>Marca las cotizaciones en <i>Cotizaciones (BIND)</i> y pulsa <span class="ui">Generar prefacturas (N) →</span>, o en la bandeja <span class="ui">Prefacturar seleccionados (N) →</span>. Pulsa <span class="ui">Ver vista previa de todo (N)</span>, lee los avisos de cada fila y después <span class="ui">Generar N prefactura(s) en BIND →</span>. Las prefacturas se generan <b>una tras otra</b>: BIND permite una sola sesión por usuario y el folio de cada una se calcula justo antes de guardarla. Por eso el «folio previsto» puede repetirse en la vista previa; el folio real se asigna al guardar. Una cotización surtida o cancelada se omite y un rechazo no detiene el resto. El timbrado no va en lote: cada fila creada trae su botón <span class="ui">Timbrar →</span>.</p>
</section>
<!-- ===== 06 ===== -->
<section class="sec break">
<div class="sec-head"><span class="sec-num">06</span><div><span class="kick">Paso 3 de la cadena</span><h2>De la prefactura a la factura (timbrado)</h2></div></div>
<p>Timbrar es la única acción con efecto fiscal: convierte la prefactura en factura (CFDI), igual que el botón «Emitir CFDI» de BIND. Es el mismo documento, con el mismo folio, que al timbrarse recibe su UUID. La tarjeta <i>Factura (CFDI) desde la prefactura</i> aparece debajo de una prefactura recién creada, en el detalle de cualquier factura en estado Prefactura, en el seguimiento del ticket y en cada fila del lote.</p>
<ol class="steps">
<li><span class="ui">1 · Ver con qué saldrá el CFDI (dry-run)</span> muestra cliente, RFC, total, método y forma de pago y uso CFDI, más el recuadro «Léelo antes de timbrar — así saldrá el CFDI».</li>
<li><span class="ui">2 · Timbrar en BIND (emitir CFDI) →</span> abre un cuadro donde hay que <b>teclear TIMBRAR</b>. Con cualquier otra palabra no se timbra.</li>
<li>El resultado es «✓ Timbrada — ya es factura» con su CFDI, y la cadena del ticket queda completa.</li>
</ol>
<figure>
<img src="img/timbrado-vista-previa.jpg" alt="Vista previa del timbrado con avisos">
<figcaption>La vista previa del CFDI con sus avisos: método de pago según BIND, forma de pago, uso CFDI y RFC genérico.</figcaption>
</figure>
<div class="callout"><b>Antes de timbrar, revisa el método de pago.</b> BIND deja las prefacturas en <b>PUE</b> (una sola exhibición). Balam factura a crédito en <b>PPD</b>, y un PUE erróneo ya causó un requerimiento del SAT. Si el cobro no es de contado, cámbialo en BIND antes de confirmar; la vista previa lo avisa. Con RFC genérico (extranjero o público en general), el uso CFDI debe ser S01 y el IVA 0 %.</div>
<ul>
<li><b>Si BIND rechaza el timbrado</b> (por ejemplo, «El RFC del receptor no se encuentra en la lista de RFC inscritos…»), el documento sigue como prefactura. Corrige en BIND la ficha del cliente y vuelve a intentar.</li>
<li><b>Si BIND aceptó pero todavía no se ve el UUID</b>, pulsa <span class="ui">⟳ ¿Ya se timbró en BIND? Verificar</span> en un minuto.</li>
<li><b>Deshacer un timbrado es una cancelación ante el SAT</b>, y se hace en BIND.</li>
</ul>
<figure class="s">
<img src="img/cadena-completa.jpg" alt="Cadena completa del ticket con CFDI">
<figcaption>Cuando el ticket ya está facturado, la pantalla muestra la cadena completa: ticket, cotización, prefactura y CFDI.</figcaption>
</figure>
<p class="nota">El timbrado desde la plataforma está publicado desde el 30 de septiembre de 2026. El 2 de octubre de 2026 Balam emitió desde aquí su primer CFDI con la cadena completa: ticket, cotización, prefactura y factura.</p>
</section>
<!-- ===== 07 ===== -->
<section class="sec break">
<div class="sec-head"><span class="sec-num">07</span><div><span class="kick">Buenas prácticas</span><h2>Reglas que conviene recordar</h2></div></div>
<p>Estas reglas protegen a Balam de duplicados y de errores fiscales. Casi todas las aplica la plataforma sola; conviene conocerlas para entender sus avisos.</p>
<table>
<thead><tr><th style="width:30%">Regla</th><th>Por qué y qué hacer</th></tr></thead>
<tbody>
<tr><td class="k">No uses el usuario CTO en el navegador</td><td>La plataforma entra a BIND con su propio usuario (CTO), y BIND solo permite una sesión por usuario. Si alguien entra con él, la plataforma no podrá operar. Para trabajar en BIND a mano, usa tu propio usuario.</td></tr>
<tr><td class="k">Bloqueo de 10 minutos</td><td>Después de enviar una prefactura o un timbrado, la misma cotización o prefactura no se vuelve a mandar en 10 minutos. Así se evita un duplicado mientras BIND termina de procesar.</td></tr>
<tr><td class="k">«No se sabe si lo creó» = no reintentes</td><td>Si BIND no respondió, la plataforma no reintenta sola. Busca el documento en BIND o pulsa <i>Verificar</i> antes de intentar otra vez.</td></tr>
<tr><td class="k">Los lotes van en serie</td><td>Uno tras otro, nunca en paralelo: una sesión de BIND a la vez y folios consecutivos. El timbrado nunca va en lote.</td></tr>
<tr><td class="k">IVA 16 % o 0 %</td><td>16 % para clientes nacionales (uso CFDI G03), también en USD; 0 % para extranjeros (uso CFDI S01).</td></tr>
<tr><td class="k">MXN y USD no se mezclan</td><td>Cada moneda tiene su tarjeta en la cartera y cada cotización muestra el total en su moneda. El tipo de cambio sale del catálogo de monedas de BIND.</td></tr>
<tr><td class="k">PPD por default</td><td>Balam otorga crédito, así que PPD es lo normal. PUE solo si el pago se recibe el mismo día.</td></tr>
<tr><td class="k">Categorías sin ítem en BIND</td><td>De las 17 categorías del campo <i>Servicio/Producto</i> de Jira, 10 se resuelven solas. Estas 7 no tienen ítem equivalente en BIND y caen en «Faltan datos»: Gestión de Proyectos Senior / PMO, Mesa de Servicio (SDM), Soporte L1, Soporte L2, Gestión de Incidentes, Gestión de Cambios y Gestión de Capacidad. Elige el ítem a mano hasta que Balam defina su código.</td></tr>
<tr><td class="k">Sincronización cada 15 minutos</td><td>Lo que se cree directo en BIND aparece en la siguiente lectura; <i>Sincronizar ahora</i> no espera. La bandeja de Jira se refresca cada minuto o con <i>Actualizar</i>.</td></tr>
<tr><td class="k">Folios de cotización repetidos</td><td>En BIND, dos cotizaciones pueden compartir folio. Al buscar una, fíjate también en el cliente y la fecha.</td></tr>
</tbody>
</table>
</section>
<!-- ===== 08 ===== -->
<section class="sec break">
<div class="sec-head"><span class="sec-num">08</span><div><span class="kick">Soporte</span><h2>Mensajes frecuentes y qué hacer</h2></div></div>
<p>Los mensajes de la plataforma dicen qué pasó y, casi siempre, qué hacer. Estos son los más comunes.</p>
<table class="compact">
<thead><tr><th style="width:34%">Mensaje</th><th style="width:31%">Qué significa</th><th>Qué hacer</th></tr></thead>
<tbody>
<tr><td class="msg">«BIND aún no muestra la prefactura N por su API…»</td><td>BIND tarda alrededor de un minuto en mostrar lo recién creado.</td><td>Reintenta en un minuto.</td></tr>
<tr><td class="msg">«BIND no dejó entrar a la plataforma: … sesión abierta en otro equipo»</td><td>Alguien entró a BIND con el usuario de la plataforma.</td><td>Que esa persona cierre su sesión en BIND; si fue una sesión olvidada, expira sola en unos minutos.</td></tr>
<tr><td class="msg">«BIND ya aceptó una prefactura de la cotización N hace M min…»</td><td>Bloqueo de 10 minutos contra duplicados.</td><td>Revisa la prefactura en BIND; no la mandes de nuevo.</td></tr>
<tr><td class="msg">«BIND no respondió al guardar… No se sabe si creó la prefactura»</td><td>No hubo respuesta de BIND.</td><td>Búscala en BIND antes de reintentar.</td></tr>
<tr><td class="msg">«La cotización N ya está surtida en BIND…»</td><td>Esa cotización ya generó una venta.</td><td>Abre la prefactura que ya existe; no se genera otra.</td></tr>
<tr><td class="msg">«BIND rechazó el timbrado (500): CFDI40: …»</td><td>El SAT o el PAC rechazó algún dato del receptor.</td><td>Corrige la ficha del cliente en BIND (RFC, régimen, código postal) y vuelve a timbrar.</td></tr>
<tr><td class="msg">«No se timbró: la confirmación debe ser la palabra TIMBRAR.»</td><td>Se tecleó otra cosa en la confirmación.</td><td>Repite y escribe TIMBRAR.</td></tr>
<tr><td class="msg">«Faltan datos» · «servicio (… esa categoría no tiene ítem…)»</td><td>La categoría del ticket no tiene ítem en BIND.</td><td><i>Completar en pantalla</i> y elige el ítem a mano.</td></tr>
<tr><td class="msg">«Tu rol es de consulta…»</td><td>Tu usuario no tiene permiso de escritura.</td><td>Pídeselo a Finanzas o Administración.</td></tr>
<tr><td class="msg">«Correo o contraseña incorrectos.»</td><td>Datos equivocados, o cuenta bloqueada tras 5 intentos.</td><td>Espera 15 minutos o pide que restablezcan tu contraseña.</td></tr>
</tbody>
</table>
</section>
<!-- ===== 09 ===== -->
<section class="sec break">
<div class="sec-head"><span class="sec-num">09</span><div><span class="kick">Alcance</span><h2>Lo que se sigue haciendo en BIND</h2></div></div>
<p>Hay tareas que siguen haciéndose en BIND, a propósito, porque ahí se corrigen los datos fiscales:</p>
<ul>
<li>Editar una prefactura: método o forma de pago, uso CFDI y domicilio del cliente.</li>
<li>Cancelar un CFDI ante el SAT (motivo 02, «sin relación»).</li>
<li>Enviar la factura al cliente por correo.</li>
<li>Descargar el XML del CFDI.</li>
</ul>
<p>Los módulos marcados <span class="st">próximamente</span> llegan en la siguiente etapa: Dashboard, Envío al cliente, Pagos y recordatorios, Bitácora, la antigüedad de cartera por rangos de 30, 60 y 90 días, las alertas internas y el reporte operativo.</p>
<div class="signoff">
<div class="nm">Johann Velazquez</div>
<div class="rl">Consultor de Software · dudas, ajustes y reportes de la plataforma</div>
</div>
</section>
</main>
</body>
</html>
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 101 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 77 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 75 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 101 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 113 KiB

+43
View File
@@ -0,0 +1,43 @@
{
"input_html": "Manual-usuario-Balam.html",
"output_pdf": "Manual-usuario-Balam.pdf",
"page_size": "Letter",
"cover_full_bleed": true,
"render_timeout_ms": 30000,
"metadata": {
"title": "Manual de usuario — Plataforma de Automatización Financiera · Balam",
"author": "Johann Velazquez",
"subject": "Uso de la plataforma: cartera, facturas, solicitudes de Jira, cotización, prefactura y timbrado",
"keywords": "manual, Balam, BIND ERP, Jira, cotización, prefactura, CFDI"
},
"footer": {
"text": "Manual de usuario · Plataforma de Automatización Financiera · Balam — Confidencial",
"skip_first_page": true,
"skip_pages": [],
"page_number_format": "{page} / {pages}",
"rule": true,
"font_size_pt": 7.5,
"margin_mm": 12,
"color": [0.46, 0.51, 0.57]
},
"fonts": {
"footer_ttf": "fonts/Carlito-Regular.ttf"
},
"toc": {
"_comment": "key -> frase ÚNICA del CUERPO de cada sección (no el título).",
"1": "junta en un solo lugar los tickets de facturación",
"2": "Cuentas por cobrar es la pantalla de inicio",
"3": "La bandeja muestra los tickets del proyecto FAC",
"4": "Cuando alguien crea un ticket en FAC",
"5": "La prefactura se genera siempre desde una cotización",
"6": "Timbrar es la única acción con efecto fiscal",
"7": "Estas reglas protegen a Balam de duplicados",
"8": "Los mensajes de la plataforma dicen qué pasó",
"9": "Hay tareas que siguen haciéndose en BIND"
}
}
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -32,3 +32,19 @@ Johann Velázquez
## 3. Confirmación al recibir el repositorio
> Listo, ya recibí la invitación y confirmé que tengo acceso de escritura al repositorio. Muchas gracias. Voy a publicar la estructura inicial del backend y les aviso cuando quede disponible.
## 4. WhatsApp a Erika — status lunes 3-ago + lo que falta de Balam (preparado 1-ago)
> Hola Erika, buen día. Te comparto avance del fin de semana:
>
> ✅ Cierre técnico de la Etapa 1 (lo que no estaba bloqueado): sincronización automática cada 15 min, datos de demostración, y suite de pruebas del API (61 pruebas en verde).
> ✅ Adelanté el frontend de la Etapa 2 (parte de consulta): inicio de sesión por roles, cartera por moneda, listado de facturas con filtros y búsqueda, detalle con descarga del PDF del CFDI y catálogo de clientes con exportación a Excel/CSV. Todo el código ya está en el repositorio de Balam.
> ✅ La base técnica de la integración con Jira (lectura) quedó lista y probada.
>
> Para seguir avanzando solo me falta de su lado:
> 1. ☁️ El acceso a Azure que quedó con el inge Noé (grupo de recursos con Contributor) — es lo que libera el ambiente de pruebas y el despliegue.
> 2. 📅 La fecha de la sesión del flujo de facturación — desbloquea la capa de escritura (cotización→factura) y las pantallas de emisión.
> 3. 🧾 Tres definiciones de la integración Jira→BIND (pueden salir en una sola sesión con Pedro y Arturo): ¿qué tipo de solicitud se automatiza (Facturación o Facturación adicional)?, ¿qué estatus dispara la cotización?, y ¿de dónde tomamos monto y conceptos del ticket?
>
> Si te parece, el viernes les muestro todo funcionando en la demo semanal.
@@ -0,0 +1,25 @@
# Correo — 21-ago-2026 · Johann → Pedro (CC Noé, Araceli) · causa del error encontrada
**Hilo:** RE: Seguimiento - PAF
**Para:** Pedro · **CC:** Noé, Araceli
---
Buenas tardes, espero se encuentren bien.
Les comparto el resultado después de la sesión de hoy: ya encontré la causa del error y la cotización desde el ticket FAC-106 ya se genera correctamente en BIND.
El problema no era de los datos del ticket ni del formulario nuevo, que quedó muy bien. Eran dos detalles de la API de BIND que no están en su documentación y que solo se pueden descubrir probando:
1. Cada partida lleva un campo "Type" que le indica a BIND si el concepto es un producto o un servicio. La documentación no dice qué valor corresponde a cada uno; con el valor de producto BIND buscaba nuestro servicio en el catálogo equivocado y devolvía el error genérico de "comunicarte con soporte técnico". Con el valor correcto para servicio, pasó.
2. El IVA por partida se envía como tasa (0.16) y no como monto. Por eso la primera cotización que sí se creó (folio 1026) salió con un IVA incorrecto; [ya quedó cancelada / les pido de favor cancelarla], no tiene ningún efecto.
Con ambos ajustes, la cotización de prueba quedó generada con el folio [____], a nombre de CEMEX, por $1,234.00 + IVA $197.44 = $1,431.44 MXN, tal como venía en el ticket. La pueden ver en Ventas → Cotizaciones.
Dos notas que creo les darán tranquilidad:
- Los intentos fallidos de la mañana no dejaron nada en BIND; lo verifiqué por lectura después de cada uno.
- La plataforma ahora rechaza con un mensaje claro cualquier dato que venga en la escala equivocada, para que esto no pueda repetirse por error de captura.
Sobre los siguientes pasos: ya pueden crear tickets con el formulario nuevo para seguir probando el paso a cotización. Si prefieren, repetimos la emisión en vivo en una sesión corta de 15 minutos cuando les acomode. El alcance sigue siendo hasta la cotización, como acordamos.
Quedo atento. Saludos.
@@ -0,0 +1,42 @@
# Correo — Respuesta a Erika: token de Jira, límites de consumo y estado general
Borrador · 29-jul-2026 — **no enviado**
Responder a todos → Para: Erika Chávez · CC: Noé Rocha; Pedro Ayala
Asunto: (el del hilo, con `RE:`)
**Contexto:** Erika pregunta (29-jul, 1:25 pm) por comentarios sobre lo que entregó Pedro el 27-jul —token PAF por Drive + explicación de límites de consumo—, si se pudo ingresar y si se requiere algo adicional de su lado.
**Estrategia:** la pregunta de Erika abre la puerta para dar el panorama completo. El correo entrega dos cosas el mismo día (propuesta v1.3 y Excel de horas por etapa), reporta la prueba real del token con su resultado, y fecha los tres bloqueos que están del lado de Balam. Las fechas son el punto: el hueco en desarrollo del 28–29 existe porque la definición del flujo lleva en revisión interna desde el 24-jul y Azure desde la semana del 21-jul.
======================================================================
Buenas tardes Erika, Ing. Noé, Pedro:
Gracias Pedro por el token y por el detalle de los límites de consumo. Ya lo aterricé al diseño: como no hay tope mensual y lo que aplica son límites de velocidad, la integración se autorregula leyendo los headers `X-RateLimit-*` —baja el ritmo al acercarse al umbral y respeta el `Retry-After`—, así que no anticipo problema de consumo con el volumen del proceso. Que no exista dashboard no nos afecta, porque esa telemetría viaja en las respuestas.
Sobre el ingreso, ahí sí necesito tu apoyo: el token está siendo rechazado con 401. Ya descarté las causas de mi lado —la cadena está completa y bien formada (192 caracteres, con el prefijo que corresponde, sin espacios ni cortes), y probé con el correo que me confirmaste, tanto contra el sitio de Jira como por la ruta de `api.atlassian.com`. También lo validé contra el endpoint de cuenta de Atlassian, que no depende del sitio, y ahí también responde 401. Eso apunta a que la credencial ya no está activa.
¿Me ayudas a revisar en el administrador de tokens de tu cuenta si el token **PAF** sigue existiendo? Si se rotó o quedó reemplazado, con que lo regeneres y me lo compartas de nuevo lo pruebo de inmediato y les confirmo el mismo día.
Aprovecho una pregunta de diseño: convendría valorar si el token vive en una cuenta de servicio en lugar de en tu cuenta personal. Hoy todo lo que la plataforma escriba en Jira quedaría registrado a tu nombre, y una rotación tuya dejaría la integración sin acceso. Lo que ustedes decidan lo implemento igual, solo lo dejo sobre la mesa.
Aprovecho para dejarles el panorama completo. De mi lado va hoy:
- **La propuesta actualizada (v1.3)** con la integración Jira → BIND incorporada al alcance, que les envío en el hilo correspondiente.
- **El Excel de horas por etapa**, con las etapas 2 y 3 desglosadas como me lo pidió Erika para el conteo de facturación.
Y lo que está detenido esperando definición de su lado, con fecha para que sea fácil ubicar dónde está la fila:
1. **La definición del flujo de facturación**, en revisión interna desde la sesión del 24-jul. De ahí sale el estatus que dispara la creación de la cotización, y es la que tiene detenidas la capa de escritura y la automatización con Jira. Es la que más pesa.
2. **El acceso a Azure**, previsto para la semana del 21-jul. Sin él no puedo configurar el ambiente ni el CI/CD.
3. **El token de Jira**, lo que comento arriba.
En cuanto se liberen retomo de inmediato; el trabajo que no depende de ellas ya está hecho y verificado. Si les ayuda, puedo entrar a una sesión corta esta semana para cerrar la definición del flujo con Arturo y avanzar sin esperar la revisión completa.
Cuando toque la prueba en vivo de creación les aviso con anticipación para hacerla acompañados, como lo acordamos con el Ing. Noé.
Quedo a sus órdenes.
Saludos cordiales,
Johann Velázquez
@@ -0,0 +1,72 @@
# Guion — prueba completa en producción: ticket → cotización → prefactura → factura (1-oct-2026)
Para la llamada con Balam. Producción (https://paputec.mx/balam/) corre el commit `f5b3743` desde las 22:40 del 30-sep, con el puente a la web app de BIND **encendido y con credenciales** (usuario `CTO`) y el timbrado desde la plataforma publicado y probado contra BIND/Jira falsos ([REGISTRO #89](../bitacora/REGISTRO.md)). Todo lo de abajo escribe en el **BIND de producción**: la cotización y la prefactura son reales, y el CFDI también.
## 0. Antes de empezar (5 min)
- [ ] **Usuario de la plataforma con permiso de escritura:** `admin@balam.paputec.mx` (Dirección + Finanzas) o `finanzas@balam.paputec.mx`. Un rol de solo consulta ve la tarjeta pero no puede generar ni timbrar (lo dice en pantalla).
- [ ] **Nadie entra a app.bind.com.mx con el usuario `CTO` durante la prueba.** BIND permite una sesión por usuario: si alguien está dentro como `CTO`, la plataforma recibe "sesión abierta en otro equipo" (409 con instrucciones). Pedro/Araceli entran con su propio usuario si quieren mirar.
- [ ] **El ticket de Jira** (formulario *Cotización Bind* del proyecto FAC) con:
- *Cliente*: uno que exista en BIND con ese nombre (los 17 del combo resuelven). Para que el CFDI salga sin fricción, un cliente con facturas timbradas recientes (de él se hereda el uso CFDI) — p. ej. el que usen siempre para pruebas.
- *Servicio/Producto*: una de las **10 categorías que ya resuelven** (p. ej. "Servicios Profesionales Generales" → 101 o "Servicio de Enlace" → 106). Las 7 sin mapear (PMO, Mesa de Servicio/SDM, Soporte L1, L2, Gestión de Incidentes, de Cambios, de Capacidad) caen en **"Faltan datos"** y la cotización se completa a mano en la pantalla (sigue funcionando, con un paso más).
- *Monto sin IVA* reconocible (p. ej. `1234`), *Cantidad* 1, *IVA* según el cliente (0 % para extranjero), *Moneda* MXN.
- [ ] **Modo de la campana en "Pedir confirmación"** (es como está): el modal aparece y alguien da "Aceptar". Si Balam quiere ver el modo automático, se cambia ahí mismo (el servidor ya lo permite) — ese cambio crea cotizaciones sin clic en adelante; decidirlo con ellos.
- [ ] **Acordar el final:** el CFDI será real. Quién lo cancela en BIND (motivo SAT 02 "sin relación") y cuándo. La plataforma lo reflejará como *Cancelada* con «Verificar» o en el siguiente sync.
- [ ] (Johann) ventana al servidor para seguir la prueba: `ssh johann@paputec.mx` y `sudo docker logs -f balam-api 2>&1 | grep -v "libgssapi"`.
## 1. Ticket → cotización (lo que debe verse)
1. Pedro crea el ticket en Jira. En **1–5 s** aparece en la plataforma el modal *"Ticket FAC-nnn detectado"* con la línea de tiempo (creado → detectado → vista previa) y los números de la cotización preparada.
2. **"Aceptar y crear cotización"** → *"Cotización N creada"*. En *Cotizaciones (BIND)* la fila nueva aparece con su **total correcto de inmediato** (ya no $0.00) y la columna *Ticket* con FAC-nnn.
3. En *Solicitudes (Jira)* el ticket muestra "Cotizada · N".
Si algo no sale:
- **No aparece el modal:** mirar la campana (punto verde = canal en vivo; ámbar = consulta cada 4 s) — el aviso está ahí como pendiente, con "Ver detalle". Si tampoco: *Solicitudes (Jira)* → el ticket dice "Lista para revisar" o "Faltan datos"; desde ahí "Cotizar".
- **"No se pudo armar la cotización sola" (Faltan datos):** el detalle dice qué falta (cliente sin coincidencia única, categoría sin mapear, monto). "Ver detalle" → pantalla de cotizar → completar → *Vista previa* → *Confirmar*. Mismo resultado.
- **"Fallida" (BIND rechazó):** el motivo queda en el aviso; se cotiza desde la pantalla. Nunca se reintenta solo (una escritura que falló pudo haber llegado).
- **Dos personas dan Aceptar a la vez:** solo una crea; la otra ve "ya se está creando / ya tiene cotización".
## 2. Cotización → prefactura
1. En el ticket: **"Facturar"** (wizard). Paso 1 trae **solo la cotización vinculada**, ya seleccionada, con subtotal y total (sin IVA doble). Pasos 2–4: datos, concepto, borrador. Paso 5: aprobador (solo Araceli) → **"Aprobar prefactura"**.
2. Tarjeta **"Prefactura vía UI de BIND"** (dice *Sesión automática · CTO @ 128469*):
- **"2 · Ver JSON exacto (dry-run)"** → resumen (cliente, partidas, subtotal, **folio previsto** = consecutivo real de BIND, hoy 2468) y la lista de **avisos** (uso CFDI heredado, método de pago…). Leerlos.
- **"3 · Generar prefactura en BIND →"** → confirmación → *"Prefactura (sin timbrar, folio de la secuencia compartida)"* con folio, total, "UUID null (sin timbrar ✓)".
- También se puede desde *Cotizaciones (BIND)* → "Generar prefactura" (el panel se abre debajo de la fila).
3. En BIND: Ventas → la prefactura con ese folio, generada por "Noé Rocha Castillo" (usuario CTO), cotización surtida.
Si algo no sale:
- **409 "La cotización N ya está surtida/cancelada en BIND":** BIND manda; se refleja en la BD. Hay que partir de otra cotización activa.
- **409 "BIND ya aceptó una prefactura de la cotización N hace M min":** ya existe; no se manda otra en 10 min. Buscarla en BIND / recargar el ticket ("ya está en prefactura").
- **409 "sesión abierta en otro equipo":** alguien está en BIND como `CTO`; que cierre sesión ahí, o esperar unos minutos a que expire.
- **502 "BIND no respondió al guardar… No se sabe si creó la prefactura":** NO reintentar. Revisar en BIND; si existe, el sync (≤ 15 min) la trae y el ticket la muestra; la plataforma bloquea 10 min el reenvío.
- **"Prefactura creada… Su API aún no la muestra":** normal (el API de BIND tarda ~1 min en ver lo que crea la web app). Seguir al paso 3 cuando el dry-run del CFDI ya la lea.
## 3. Prefactura → factura (timbrado) — efecto fiscal
Debajo de la prefactura aparece **"Factura (CFDI) desde la prefactura · folio"** (también en el ticket, "ya está en prefactura").
1. **"1 · Ver con qué saldrá el CFDI (dry-run)"** → resumen: cliente, **RFC**, **total**, **método de pago según BIND (PUE/PPD)**, forma de pago, uso CFDI enviado al crearla, partidas; y los avisos. **Leer el método de pago:** BIND deja las prefacturas en PUE ("PAGO EN UNA SOLA EXHIBICIÓN"); si debe salir PPD, **se cambia en BIND antes de timbrar** (la plataforma no modifica la prefactura: pide emitirla tal cual está).
2. **"2 · Timbrar en BIND (emitir CFDI) →"** → el navegador pide teclear **`TIMBRAR`** → en 3–15 s: **"✓ Timbrada — ya es factura · CFDI {uuid}"**, chip *Timbrada ✓*, botón "Ver la factura →".
3. Comprobar: el ticket dice **"FAC-nnn ya está facturado"** con la cadena completa (cotización ✓ · prefactura ✓ · factura ✓ uuid); en *Facturas* el folio pasó de *Prefactura* a **Vigente** con UUID; en BIND el documento tiene UUID (mismo folio).
Si algo no sale:
- **409 "BIND aún no muestra la prefactura por su API":** esperar un minuto y repetir el dry-run.
- **502 "BIND rechazó el timbrado (500): <motivo>":** es el PAC/BIND (RFC no inscrito, régimen, código postal, uso CFDI…). Nada quedó timbrado. Corregir el dato en BIND (ficha del cliente o la prefactura) y volver a "2 · Timbrar".
- **"BIND aceptó el timbrado; su API aún no muestra el UUID":** normal; **"¿Ya se timbró? Verificar"** al minuto. No se reenvía (bloqueo de 10 min).
- **502 "BIND no respondió al timbrar… no se sabe si emitió":** NO reintentar; "Verificar" al minuto o mirar en BIND. Bloqueo de 10 min.
- **409 "ya está timbrada (CFDI …)":** alguien la timbró en BIND; la plataforma ya lo reflejó.
- **409 "cancelada en BIND":** no se timbra; generar otra prefactura desde una cotización activa.
## 4. Cierre
- Cancelar el CFDI de prueba en BIND (quien Balam decida). En la plataforma: "Verificar" en la tarjeta → *Cancelada*; el ticket vuelve a mostrar solo la cotización (surtida).
- Lo que se creó queda auditado en `WriteOperations` (dry-run, confirmación, resultado) y en la bitácora de la app.
- Pedir a Balam en la misma llamada: **los códigos de BIND de las 7 categorías** sin mapear y si quieren el **modo automático** (sin clic) — en ese caso, autorización escrita (ADR-001).
## Lo que la plataforma NO hace (para no prometerlo en la llamada)
- No edita la prefactura (método/forma de pago, uso CFDI, domicilio fiscal): eso se hace en BIND antes de timbrar.
- No manda el correo al cliente ni descarga PDF/XML del CFDI: viven en BIND (el PDF de factura sí se ve por el proxy en *Facturas*).
- No cancela CFDI.
- No reintenta sola una escritura: ante la duda, bloquea y pide verificar.
+74
View File
@@ -0,0 +1,74 @@
# Guion — sesión del viernes 21-ago-2026, 7:00 am (Noé, Araceli, Pedro)
**Objetivo:** emitir la **primera cotización real** en BIND desde `FAC-106` y validar las 17 categorías. **Alcance:** termina en la cotización (sin prefactura, sin timbrado, sin tocar Jira). Sondeo y diagnóstico de anoche: [`Sondeo-FAC-106-y-payload-2026-08-20.md`](Sondeo-FAC-106-y-payload-2026-08-20.md).
---
## 0 · Esta noche (5 min)
- [ ] **Avisar a Pedro por WhatsApp** — cumple el compromiso del 12-ago de avisar antes de cada escritura, y corrige el shape que se compartió entonces (llevaba `VATRate: 16`; el correcto es `0.16`). Borrador:
> *Pedro, ya quedó listo el caso sobre FAC-106: la plataforma ya lee el formulario nuevo (cliente, servicio, cantidad, IVA) y encontré la causa del error de la sesión pasada — la tasa de IVA iba en porcentaje y BIND la espera como fracción (0.16), ya corregido y probado en simulación. Mañana muestro la vista previa y, con su ok, emitimos la cotización real y Araceli la cancela al terminar. Nos vemos a las 7.*
## 1 · Antes de las 7:00 (6:35–6:55)
- [ ] `docker compose up -d` (Postgres ya suele estar arriba) · levantar API y frontend.
- [ ] **Verificar que el modo diga `ReadOnly`** al entrar a la pantalla de emisión (quedó así anoche; el candado se abre solo en el paso 4).
- [ ] Re-sondeo exprés de `FAC-106` (Pedro anduvo moviendo formularios): abrir la solicitud en la plataforma y confirmar que siga prellenando **CEMEX / Servicio de Enlace / 1 / 16% / 1234 / MXN**. Si algo cambió, el fallback manual sigue funcionando — se captura a mano en la misma pantalla.
- [ ] Terminal lista con los dos comandos del candado (paso 4 y cierre).
- [ ] Abrir el [sondeo](Sondeo-FAC-106-y-payload-2026-08-20.md) en una pestaña (plan B §4 y preguntas).
## 2 · Al abrir la sesión — ANUNCIAR ANTES DE EJECUTAR (pendiente arrastrado del 12-ago)
1. **Destino de la cotización:** se emite real sobre CEMEX con los datos de `FAC-106`; **al terminar, Araceli la cancela en vivo** (ella confirmó el 12-ago: sin timbrar, se cancela sin efecto). Así el ejercicio además demuestra la reversa.
2. **La prefactura pertenece a otra sesión** (candados: cotización → prefactura → aprobación humana → timbrado).
3. Qué se corrigió del 500: escala de la tasa, IVA de partida como monto, lista de precios omitida.
## 3 · Demo en ReadOnly (el camino feliz, sin riesgo)
Abrir bandeja → `FAC-106` → **Cotizar**: la partida se prellena sola desde el formulario nuevo y la **vista previa (dry-run) se genera automática** — enseñar el body exacto. Aquí se valida con Araceli que los datos son los que ella capturaría.
## 4 · Emisión real (una sola vez)
```bash
dotnet user-secrets set "Bind:Mode" "Write" --project src/Balam.Api # + reiniciar API
```
**Confirmar** → folio y total en pantalla → **"véanlo en su BIND"** (Ventas → Cotizaciones) → Araceli cancela → candado de vuelta:
```bash
dotnet user-secrets set "Bind:Mode" "ReadOnly" --project src/Balam.Api
```
**Si BIND rechaza:** el body del error se lee en pantalla (ya no es 500 ciego). Orden de ataque: (1) agregar `PriceListID` de una cotización reciente del cliente; (2) mandar el GUID del servicio en `ID`; (3) probar `Type`. Y si no sale en vivo, Araceli ya dijo la vez pasada: *"haz la prueba y podemos agendar otra sesión"* — el dry-run mostrado ya es entregable.
## 5 · Preguntas que SOLO se resuelven con ellos (en orden de impacto)
1. 🔴 **`Empresa origen` = Balam / RegioTurk / Helmstone — ¿cuentas de BIND distintas?** Hay un solo token; si cada empresa factura en su cuenta, cambia el alcance.
2. 🔴 **El combo IVA no tiene `0%`** y hay clientes de exportación en el combo (ACUNTIA S.A.U., VictTec, IAMCFORWARDER). ¿Se agrega la opción?
3. 🔴 **"Servicio de Enlace" existe 5 veces en BIND** con unidad SAT y ChargeVAT distintos — ¿quién depura BIND y cuál es el mapeo etiqueta→ítem? (segunda mitad de lo que pidió Araceli el 12-ago).
4. **Validar las 17 categorías** (Pedro las condicionó a Noé+Ara — es esta junta).
5. **Regla de edición:** ¿historial de cotizaciones o modificar la misma? (quedó como "regla de negocio de Balam" el 12-ago).
6. ☁️ **Azure** — tercera semana; Noé dijo "lo resuelvo con Pedro" y Pedro ahora es el responsable. Requisitos: grupo de recursos + Contributor (app+BD+Key Vault), sin Global Admin.
7. 👤 Con el relevo a Pedro: **¿a quién se reportan horas/avances/facturación ahora?** (todo el circuito iba por Erika).
8. Recordatorios menores: fijar que **manda el combo Cliente (11598)** sobre el texto libre (11596); quitar el **SLA** a estos tickets (Pedro lo dijo, confirmar que se hizo); **grabaciones de BIND** que Pedro ofreció el 12-ago.
## 6 · Protocolo si la emisión FALLA (decidido en frío, la noche anterior)
**Primeros 30 segundos — narrar, no disculparse.** Leer el error de BIND **en voz alta** desde la pantalla: *"BIND lo rechazó con este mensaje — exactamente para esto era la prueba acompañada."* El 12-ago el 500 fue ciego; que hoy el diagnóstico se lea en vivo ES la mejora. No silencio, no disculpa larga, no cerrar la ventana.
**Regla de intentos: máximo 2 reintentos, ~10 minutos.** Solo los del plan B y en orden (PriceListID → GUID en `ID` → `Type`) — nada de mutaciones improvisadas: cada reintento es un intento de escritura y el acuerdo con Noé es no probar más de lo definido. **Antes de cada reintento, verificar por lectura que el intento anterior NO haya creado la cotización** (un 500 puede escribir de todos modos) — evita duplicados y de paso demuestra el control.
**Al parar (guion literal):** *"Ya tengo el diagnóstico exacto en el mensaje de BIND. Lo corrijo hoy mismo y la emisión son 15 minutos — les propongo repetir solo ese paso mañana o el lunes; les confirmo a Pedro en cuanto esté."* Compromiso corto y específico; nada de "lo reviso y les aviso" vago. Araceli ya ofreció ese formato el 12-ago (*"haz la prueba y podemos agendar otra sesión"*).
**Pivotear la hora a lo que vale más que el POST:** las preguntas del §5 (17 categorías, Empresa origen/token, IVA 0%, mapeo, Azure). Si la sesión cierra esas definiciones, fue una buena sesión **aunque no haya folio**. El dry-run mostrado ya es entregable: el flujo Jira→plataforma quedó demostrado; lo que falló es la última milla contra BIND.
**Higienes aunque falle:** regresar `Bind:Mode` a ReadOnly antes de cerrar (decirlo en voz alta: *"y regreso el candado a solo-lectura"*) · mencionar que cada intento quedó auditado en `WriteOperations` (es argumento de confianza, no de vergüenza) · mismo día: resultado por WhatsApp a Pedro + entrada en la bitácora.
**Qué NO decir:** no culpar al formulario ni a BIND ni a Pedro; no abrir prefactura/timbrado "para compensar"; no aceptar en caliente compromisos de fecha más allá del re-intento de 15 min.
## Qué NO hacer
- ❌ Emitir antes de la sesión o sin anunciar el destino (candado de Noé, [REGISTRO #65](../bitacora/REGISTRO.md) §8).
- ❌ Dejar `Bind:Mode=Write` al cerrar.
- ❌ Abrir el tema de prefactura/timbrado aunque salga bien a la primera — gradualismo explícito de Arturo.
+28
View File
@@ -409,3 +409,31 @@ Se ven tres bandejas de origen: **HH** (headhunting), **SA** (staff augmentation
> **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`.**
---
## Sondeo del 6-ago-2026 — customfield IDs capturados (cierre del pendiente de mapeo)
Corridas: `pruebas-jira-lectura.sh` (todo 200; token renovado funciona, el 401 del 29-jul quedó atrás) + `GET /rest/api/3/field` + `search/jql` con los customfields sobre los **69 tickets** de FAC. Bodies en `/tmp/jira-probes-20260806-004526` (fuera del repo).
| Campo | ID | Tipo real | Poblado (de 69) |
|---|---|---|---|
| Request Type | `customfield_10010` | `sd-customerrequesttype` (`{requestType:{name}}`) | 59 |
| Nombre del Cliente | `customfield_10202` | ⚠️ **ADF** (schema dice string, el valor es doc) | 54 |
| Cliente Nuevo | `customfield_10052` | option (`{value:"No"}`) — el duplicado `10094` está vacío, no usarlo | 48 |
| Tipo de Moneda | `customfield_10053` | option | 48 |
| Días de Crédito | `customfield_10054` | option (`{value:"45"}`) | 48 |
| Periodo de Incidencias | `customfield_10203` | ⚠️ **ADF** | 46 |
| Facturación recurrente | `customfield_10552` | option (`Si`) | 32 |
| Periodo de recurrencia | `customfield_10553` | string (`"12"`) | 32 |
| Empresa origen | `customfield_11423` | option (ej. `Helmstone`) | **7** — casi vacío; NO sirve como filtro hoy |
| Monto sin IVA | `customfield_11556` | number | **2** |
| Monto con IVA | `customfield_11522` | number | 0 |
Hallazgos que corrigen al Bloque 2 del 30-jul:
- **Sí existen campos estructurados de monto** (`Monto sin IVA` / `Monto con IVA`), pero casi nadie los llena (2/69 y 0/69). La pantalla los lee (`MontoEstimado = customfield_11556`) y muestra "—" cuando falten; si Balam empieza a llenarlos, aparecen solos.
- "Nombre del Cliente" y "Periodo de Incidencias" llegan como **ADF**, no texto plano — el mapper aplana con `AdfText.Flatten`.
- El filtro del prototipo "empresa origen = Balam" no es viable aún (7/69 poblados, y con valores como `Helmstone`): la bandeja muestra todo FAC con nota honesta.
- Distribución real: 48 Facturación adicional · 6 Bajas · 4 Automatización · 1 Facturación · 10 sin RT. Estatus: 65 Facturado · 2 Cancelado · 1 En proceso · 1 En validación nacional.
Los IDs quedaron en `balam-plataforma/backend/src/Balam.Api/appsettings.Development.json` (sección `Jira:Fields`, config-driven — los IDs no son secretos).
@@ -0,0 +1,82 @@
# Plan — Webhook Jira (FAC) → vista previa automática de cotización BIND
_Creado: 2026-09-29 · Responsable: Johann · Contexto: [REGISTRO #84](../bitacora/REGISTRO.md) (nació como #71; renumerado al fusionar el 30-sep) · Antecedente técnico: [Investigacion-API-Jira.md §Bloque 3–4](Investigacion-API-Jira.md)_
## Objetivo
Que la plataforma se entere **en el momento** en que se crea (o avanza) un ticket de FAC y deje **preparada la vista previa (dry-run) de la cotización en BIND**, para que un humano solo revise y confirme con un clic.
**Fuera de alcance:** crear la cotización en BIND sin confirmación humana. ADR-001 sigue vigente: toda escritura en BIND producción = dry-run + confirmación humana + `Bind:Mode=Write` encendido solo durante la operación. La automatización total requeriría autorización escrita de Balam **y** enmienda de ADR-001, con reglas (solo clientes conocidos, IVA dentro del catálogo, tope de monto, interruptor de apagado).
## Decisiones previas (antes de escribir en Jira)
1. **¿Qué evento dispara?** Hay dos candidatos y no son equivalentes:
- `jira:issue_created`: el ticket nace. Es lo que se pidió el 29-sep.
- Transición a **`En proceso de facturación`**: es el disparador acordado en julio/agosto. Dura **~3 minutos** (FAC-100: 2m44s), así que un poller de 15 min se lo pierde; el webhook lo resuelve de raíz.
- **Propuesta:** escuchar `created` **y** `updated` (filtrado a cambio de estatus) en un solo webhook; la plataforma decide qué hacer con cada uno leyendo el changelog.
2. **Tickets de `Automation for Jira`** (10 de 69 al 10-ago, sin request type ni campos estructurados): el endpoint los recibe, pero los marca *"sin datos para cotizar"* en lugar de armar una vista previa.
3. **Autorización escrita de Pedro:** Johann confirma que existe (29-sep). **Falta archivarla en `fuentes/`** con fecha, y confirmar su alcance: ¿cubre solo crear el webhook/regla, o también crear un ticket de prueba en FAC?
## Fases
### Fase 1 — Verificación de permisos (solo lectura, riesgo cero)
- `GET /rest/api/3/myself`: el token es de Pedro y está vivo.
- `GET /rest/api/3/mypermissions?projectKey=FAC&permissions=ADMINISTER,ADMINISTER_PROJECTS`
- `ADMINISTER` = admin del sitio → **Vía A** (webhook de sistema).
- Solo `ADMINISTER_PROJECTS` → **Vía B** (regla de Automation del proyecto).
- Ninguno → Pedro lo crea en pantalla; nosotros solo damos los datos.
- `GET /rest/webhooks/1.0/webhook` (si Vía A): inventario de webhooks existentes, para no duplicar y para saber qué hay antes de tocar.
### Fase 2 — Endpoint en la plataforma (código nuestro, no toca Jira)
`POST /api/jira/webhook` en `Balam.Api/Jira/`:
- **Autenticación:** secreto compartido (Vía A: firma HMAC `X-Hub-Signature` si el sitio la ofrece; Vía B: header propio). Sin secreto válido → 401, sin leer el cuerpo. Secreto en user-secrets (`Jira:WebhookSecret`), nunca en el repo.
- **No confía en el payload:** solo extrae `issueKey`, valida que sea `FAC-\d+`, y **relee el ticket** con `JiraClient` (ReadOnly).
- **Idempotencia:** tabla/registro por `(issueKey, evento, timestamp)`; un reintento de Jira no genera una segunda vista previa.
- **Acción:** arma el body de `POST /api/Quotes` con el mismo mapeo del wizard (cliente del combo 11598, servicio, cantidad, IVA como tasa, moneda, días de crédito), lo registra en `WriteOperations` como `DryRun` y marca el ticket como **"Cotización lista para revisar"** en la bandeja. **Nunca llama a BIND en escritura.**
- **Responde rápido** (202) y procesa en segundo plano: Jira reintenta si el webhook tarda.
- **Pruebas** xUnit con `FakeHandler`: secreto inválido → 401; payload con clave ajena → 400; reintento → una sola vista previa; ticket de Automation sin campos → "sin datos"; jamás se invoca un método mutante de `BindClient`.
- El **poller** (JQL delta con solape, ADR-002) queda como respaldo: si la laptop está apagada y el webhook falla, el ticket se recupera en la siguiente pasada.
### Fase 3 — Exposición temporal con ngrok
- `ngrok http 5078` con **traffic policy** que solo deja pasar `POST /api/jira/webhook`; cualquier otra ruta → 403 en el borde. Login, cartera y emisión **no** quedan expuestos.
- Mientras el túnel esté abierto: API en `Bind:Mode=ReadOnly`.
- ⚠️ Con ngrok gratuito la URL cambia en cada reinicio y depende de la máquina de Johann encendida. Es **piloto**, no productivo. Productivo = dominio fijo de ngrok (de pago) o Azure (bloqueado por el cliente).
### Fase 4 — Alta del disparador en Jira (UNA escritura, auditada)
Operación puntual, numerada en la bitácora; **no** se cambia `Jira:Mode` de la app.
- **Vía A (preferida):** `POST /rest/webhooks/1.0/webhook` con `events: ["jira:issue_created", "jira:issue_updated"]`, `filters: { "issue-related-events-section": "project = FAC" }`, URL de ngrok y secreto. Reproducible, sin consumir cuota de Automation, reversible con `DELETE /rest/webhooks/1.0/webhook/{id}`.
- **Vía B:** regla de Automation del proyecto FAC: *Work item created* (y/o *Work item transitioned → En proceso de facturación*) → *Send web request* (POST, header del secreto, body `{"issueKey":"{{issue.key}}","evento":"{{webhookEvent}}"}`). Se crea en la UI de Automation (la API de reglas es menos estable). Consume cuota mensual de ejecuciones: verificar el plan con Pedro.
- Anotar en la bitácora: id del webhook/regla, URL, fecha, quién autorizó.
### Fase 5 — Prueba y reversa
- **Prueba:** un ticket de prueba en FAC (lo crea alguien de Balam, o Johann si la autorización lo cubre). Criterio de éxito: llega el aviso → aparece "lista para revisar" → `WriteOperations` tiene un `DryRun` → **BIND no recibió ninguna escritura** (verificar por lectura).
- **Reversa:** borrar webhook/regla (id anotado) + cerrar ngrok. El endpoint queda inerte sin secreto configurado.
## Riesgos
| Riesgo | Mitigación |
|---|---|
| API local expuesta a internet | Traffic policy de ngrok (una sola ruta), secreto, `Bind:Mode=ReadOnly` con túnel abierto |
| Escritura en el Jira productivo del cliente | Una sola operación, autorizada por escrito, numerada y reversible |
| Payload falsificado | Secreto/HMAC + relectura del ticket por API |
| Pérdida de eventos (laptop apagada, URL de ngrok cambió) | Poller de respaldo con checkpoint y solape |
| Cuota de Automation (Vía B) | Verificar plan; preferir Vía A |
| Dos sesiones de Claude Code levantando la API a la vez (29-sep) | Una sola sesión dueña de la API durante la prueba |
## Checklist
- [ ] Archivar la autorización escrita de Pedro en `fuentes/` y anotar su alcance
- [ ] Fase 1 — verificar identidad y permisos del token (solo GET)
- [ ] Decidir evento(s): creación, transición a *En proceso de facturación*, o ambos
- [ ] Fase 2 — endpoint + idempotencia + pruebas
- [ ] Fase 3 — ngrok con traffic policy de una sola ruta
- [ ] Fase 4 — alta del webhook/regla (operación numerada)
- [ ] Fase 5 — ticket de prueba, verificación y registro en bitácora
- [ ] Definir el destino productivo (ngrok de pago vs Azure)
@@ -0,0 +1,60 @@
# Script de la prueba acompañada — cotización BIND desde un ticket de Jira
**Sesión:** miércoles 12-ago-2026, 7:00 am · **Alcance:** el ejercicio termina en la **cotización** en BIND (sin prefactura, sin timbrado, sin tocar Jira).
> Para enviar a Noé/Pedro (correo o WhatsApp) ANTES de la sesión — cumple el compromiso del 24-jul de compartir el script de escritura para revisión previa.
---
## Qué se va a ejecutar (una sola operación)
`POST https://api.bind.com.mx/api/Quotes` — la operación `Quotes_NewQuote` del portal de desarrolladores de BIND. Crea **una** cotización. La respuesta es el ID del documento creado, que verificamos al momento con una lectura (`GET /api/Quotes/{id}`) para ver folio y total, y que ustedes pueden ver en su pantalla de Ventas → Cotizaciones.
**Body de ejemplo** (los valores reales salen del ticket de ejemplo y del catálogo de BIND; este es el shape exacto):
```json
{
"CurrencyID": "<GUID de la moneda, del catálogo de BIND>",
"VATRate": 16,
"Comments": "Generada desde el ticket FAC-nnn (prueba acompañada).",
"ClientProspect": {
"isNew": false,
"Client": {
"ClientId": "<GUID del cliente en BIND>",
"ClientName": "<nombre tal como está en BIND>"
}
},
"Items": [
{
"Title": "<concepto según el ticket>",
"Unit": "Servicio",
"Price": 0.0,
"Qty": 1,
"VAT": 0.0,
"IEPS": 0,
"IndexNumber": 1
}
]
}
```
Datos que se toman del ticket de Jira: **cliente** (confirmado a mano contra el catálogo sincronizado de BIND — el ticket no trae RFC), **monto sin IVA**, **moneda** y el **resumen** como concepto. No se usa ningún dato del adjunto.
## Candados (ADR-001, ya operando en el código)
1. **Dry-run primero, en pantalla.** Antes de emitir se muestra el body EXACTO que saldría — lo revisamos juntos en la sesión. Cualquier edición invalida el dry-run.
2. **La escritura está apagada por default.** El servidor corre en modo solo-lectura; el modo escritura se enciende al iniciar la sesión y **se apaga al terminar**. Fuera de ese modo, el botón de emitir no funciona (el servidor lo rechaza).
3. **Una operación por confirmación humana explícita** (diálogo de confirmación con cliente y total). Sin lotes, sin reintentos automáticos de escritura.
4. **Todo queda auditado**: cada dry-run y cada confirmación se persisten (usuario, fecha, payload, resultado) en la bitácora de operaciones de la plataforma.
5. **Permisos por rol**: solo los roles de Finanzas/Administración de la plataforma pueden usar la pantalla de emisión.
## Reversa
La cotización **no tiene efecto fiscal**. Al cierre de la sesión, ustedes deciden: **cancelarla** en BIND (queda el rastro, sin efecto) o **conservarla** si el ticket de ejemplo es un caso real que va a facturarse. Ambas salidas son de un clic en su pantalla de cotizaciones.
## Qué NO hace este ejercicio
- No crea prefactura ni factura, no timbra (eso queda para la siguiente sesión, con los candados acordados: cotización → prefactura → aprobación humana → timbrado).
- No escribe nada en Jira (ni transiciones ni comentarios).
- No crea ni modifica clientes en BIND.
- No corre nada automático: el disparo es manual en esta prueba; la versión automática (detección del cruce a "En proceso de facturación") depende del ambiente en Azure.
+66
View File
@@ -34,3 +34,69 @@ fecha,semana,etapa,actividad_id,actividad,horas,estado_hora,fuente_evidencia,fac
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-28,2026-W31,1,E1-05,Requisitos de Azure comunicados a Erika (Contributor + Key Vault) y status de avance por WhatsApp,0.25,validada,bitacora/REGISTRO.md#59,si,validada por Johann 1-ago
2026-07-28,2026-W31,1,E1-08,"Propuesta v1.3 (ajuste Jira→BIND 7-10 h), Excel de etapas Avance-Balam-2026-07-28 y scripts del plan completo",2.50,validada,bitacora/REGISTRO.md#60,si,"commits ab7cc00..7502915; validada por Johann 1-ago"
2026-07-29,2026-W31,1,E1-05,"Envío de la v1.3 y el Excel por WhatsApp (8:18 am) y por correo (3:56 pm), coordinación con Erika",0.75,validada,bitacora/REGISTRO.md#59-60,si,validada por Johann 1-ago
2026-07-29,2026-W31,2,AJ-01,Primeras pruebas del token de Jira (401) y respuesta al correo de Pedro sobre la API,1.00,validada,planeacion/Investigacion-API-Jira.md,si,"Bloque 0; validada por Johann 1-ago"
2026-07-30,2026-W31,2,AJ-01,"Desbloqueo del acceso a la API de Jira (token regenerado → 200) e investigación completa (FAC, estatus, request types, límites)",3.00,validada,planeacion/Investigacion-API-Jira.md,si,"commit 980baf0; validada por Johann 1-ago"
2026-08-01,2026-W31,1,E1-05,Bitácora #59-#60 (aprobación de Noé) y actualización de pendientes,0.50,validada,bitacora/REGISTRO.md#59-60,si,"commit 2b862ee; validada por Johann 1-ago"
2026-08-01,2026-W31,1,E1-01,"B5 scheduler + B6 verificación RLS (rol balam_app) + B8 suite de integración (25 pruebas) + B9 seeder demo",1.50,confirmada,bitacora/REGISTRO.md#61,si,"Sesión asistida por IA; tiempo real de sesión"
2026-08-01,2026-W31,2,E2-01,"Frontend de lectura E2: login JWT, shell de marca, cartera por moneda, listado con filtros, detalle+PDF, clientes+CSV",1.00,confirmada,bitacora/REGISTRO.md#61,si,"Sesión asistida por IA; tiempo real de sesión"
2026-08-01,2026-W31,2,AJ-01,Cliente de lectura de la API de Jira (Balam.Integrations.Jira) con 10 pruebas y script de sondeo,0.50,confirmada,bitacora/REGISTRO.md#61,si,"Sesión asistida por IA; incluye prueba local nocturna"
2026-08-06,2026-W32,2,AJ-01,"Sondeo de customfields en FAC (IDs capturados y documentados), token a user-secrets y endpoints de lectura /api/jira/solicitudes (+converter de fechas de Jira)",0.50,confirmada,planeacion/Investigacion-API-Jira.md,si,"Sesión asistida por IA; tiempo real de sesión; commits 4ce5640 (privado) y e2c2d18 (cliente)"
2026-08-06,2026-W32,2,AJ-01,"Bandeja de solicitudes y detalle de ticket fieles al prototipo leyendo FAC en vivo (estatus reales, solo lectura) + adjuntos descargables vía proxy",0.50,confirmada,bitacora/PENDIENTES.md,si,"Sesión asistida por IA; tiempo real de sesión; commits e2c2d18 y 76ad7b1 (cliente)"
2026-08-06,2026-W32,2,E2-01,"Shell con el chrome del prototipo aplicado a toda la app (sidebar con grupos y logo oficial, breadcrumb, chips de sync y módulos próximamente)",0.25,confirmada,prototipo/Prototipo-Etapa0.html,si,"Sesión asistida por IA; tiempo real de sesión; commit e2c2d18 (cliente)"
2026-08-06,2026-W32,2,E2-01,"Wizard de emisión asistida (5 pasos, solo UI) fiel al prototipo: cotizaciones BIND reales seleccionables, datos de factura con PPD/PUE y alerta SAT, IVA por regla con totales reactivos, prefactura simulada con marca de agua y aprobación humana visual; timbrado bloqueado hasta B7",1.00,confirmada,prototipo/Prototipo-Etapa0.html,si,"Sesión asistida por IA; tiempo real de sesión; commits 2164f6c, 90f7633 y 0600f2f (cliente)"
2026-08-10,2026-W33,2,AJ-01,"Verificación por API de las definiciones del 7-ago (Monto sin IVA, changelog, aprobaciones JSM) y correo de respuesta a Noé (molde + fechas de sesión)",1.50,reconstruida,planeacion/Correo-respuesta-Noe-2026-08-10.md,si,"REGISTRO #62; commit cbb2908; por validar por Johann"
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,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 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"
2026-08-25,2026-W35,2,E2-03,"Camino prefactura→factura: detección del timbrado, revisión de las 130 operaciones del API (sin timbrado), puertas idempotentes, detalle del ticket con el paso siguiente y botón hacia la prefactura en BIND",2.50,reconstruida,balam-plataforma commits 1ad0e27..02cb042,si,"commits 20:03–20:42 (cliente); por validar"
2026-08-26,2026-W35,2,E2-03,"Sesión 7:00 am 'Seguimiento - PAF' con Noé y Pedro: permisos y flujo remisión/prefactura/factura; el POST confirmado se timbró (CFDI 2386)",0.50,reconstruida,correo Read AI 26-ago 7:35 am,si,"REGISTRO #70; por validar"
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,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,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
34 2026-07-23 2026-W30 0 E0-07 Preparación del guion de validación del prototipo 0.45 validada planeacion/Guion-validacion-prototipo-2026-07-23.md si Guion del recorrido de la demo del 24-jul; ajustada a la baja por Johann 27-jul
35 2026-07-24 2026-W30 0 E0-07 Sesión de validación del prototipo Etapa 0 con Noé, Arturo y Araceli — APROBADO 1.05 confirmada bitacora/REGISTRO.md#55 si Duración ~1 h 4 min según transcript; Etapa 0 validada, queda 1 definición de proceso (flujo Jira)
36 2026-07-27 2026-W31 1 E1-07 Sesión de API de Jira con Pedro (token PAF, bandeja FAC) 0.20 confirmada bitacora/REGISTRO.md#56 si ~12 min; token PAF comprometido (1 año) por correo; integración inicia tras definición del flujo 28-jul
37 2026-07-28 2026-W31 1 E1-05 Requisitos de Azure comunicados a Erika (Contributor + Key Vault) y status de avance por WhatsApp 0.25 validada bitacora/REGISTRO.md#59 si validada por Johann 1-ago
38 2026-07-28 2026-W31 1 E1-08 Propuesta v1.3 (ajuste Jira→BIND 7-10 h), Excel de etapas Avance-Balam-2026-07-28 y scripts del plan completo 2.50 validada bitacora/REGISTRO.md#60 si commits ab7cc00..7502915; validada por Johann 1-ago
39 2026-07-29 2026-W31 1 E1-05 Envío de la v1.3 y el Excel por WhatsApp (8:18 am) y por correo (3:56 pm), coordinación con Erika 0.75 validada bitacora/REGISTRO.md#59-60 si validada por Johann 1-ago
40 2026-07-29 2026-W31 2 AJ-01 Primeras pruebas del token de Jira (401) y respuesta al correo de Pedro sobre la API 1.00 validada planeacion/Investigacion-API-Jira.md si Bloque 0; validada por Johann 1-ago
41 2026-07-30 2026-W31 2 AJ-01 Desbloqueo del acceso a la API de Jira (token regenerado → 200) e investigación completa (FAC, estatus, request types, límites) 3.00 validada planeacion/Investigacion-API-Jira.md si commit 980baf0; validada por Johann 1-ago
42 2026-08-01 2026-W31 1 E1-05 Bitácora #59-#60 (aprobación de Noé) y actualización de pendientes 0.50 validada bitacora/REGISTRO.md#59-60 si commit 2b862ee; validada por Johann 1-ago
43 2026-08-01 2026-W31 1 E1-01 B5 scheduler + B6 verificación RLS (rol balam_app) + B8 suite de integración (25 pruebas) + B9 seeder demo 1.50 confirmada bitacora/REGISTRO.md#61 si Sesión asistida por IA; tiempo real de sesión
44 2026-08-01 2026-W31 2 E2-01 Frontend de lectura E2: login JWT, shell de marca, cartera por moneda, listado con filtros, detalle+PDF, clientes+CSV 1.00 confirmada bitacora/REGISTRO.md#61 si Sesión asistida por IA; tiempo real de sesión
45 2026-08-01 2026-W31 2 AJ-01 Cliente de lectura de la API de Jira (Balam.Integrations.Jira) con 10 pruebas y script de sondeo 0.50 confirmada bitacora/REGISTRO.md#61 si Sesión asistida por IA; incluye prueba local nocturna
46 2026-08-06 2026-W32 2 AJ-01 Sondeo de customfields en FAC (IDs capturados y documentados), token a user-secrets y endpoints de lectura /api/jira/solicitudes (+converter de fechas de Jira) 0.50 confirmada planeacion/Investigacion-API-Jira.md si Sesión asistida por IA; tiempo real de sesión; commits 4ce5640 (privado) y e2c2d18 (cliente)
47 2026-08-06 2026-W32 2 AJ-01 Bandeja de solicitudes y detalle de ticket fieles al prototipo leyendo FAC en vivo (estatus reales, solo lectura) + adjuntos descargables vía proxy 0.50 confirmada bitacora/PENDIENTES.md si Sesión asistida por IA; tiempo real de sesión; commits e2c2d18 y 76ad7b1 (cliente)
48 2026-08-06 2026-W32 2 E2-01 Shell con el chrome del prototipo aplicado a toda la app (sidebar con grupos y logo oficial, breadcrumb, chips de sync y módulos próximamente) 0.25 confirmada prototipo/Prototipo-Etapa0.html si Sesión asistida por IA; tiempo real de sesión; commit e2c2d18 (cliente)
49 2026-08-06 2026-W32 2 E2-01 Wizard de emisión asistida (5 pasos, solo UI) fiel al prototipo: cotizaciones BIND reales seleccionables, datos de factura con PPD/PUE y alerta SAT, IVA por regla con totales reactivos, prefactura simulada con marca de agua y aprobación humana visual; timbrado bloqueado hasta B7 1.00 confirmada prototipo/Prototipo-Etapa0.html si Sesión asistida por IA; tiempo real de sesión; commits 2164f6c, 90f7633 y 0600f2f (cliente)
50 2026-08-10 2026-W33 2 AJ-01 Verificación por API de las definiciones del 7-ago (Monto sin IVA, changelog, aprobaciones JSM) y correo de respuesta a Noé (molde + fechas de sesión) 1.50 reconstruida planeacion/Correo-respuesta-Noe-2026-08-10.md si REGISTRO #62; commit cbb2908; por validar por Johann
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 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
55 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
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 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
58 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)
59 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
60 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
61 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
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
66 2026-08-25 2026-W35 2 E2-03 Camino prefactura→factura: detección del timbrado, revisión de las 130 operaciones del API (sin timbrado), puertas idempotentes, detalle del ticket con el paso siguiente y botón hacia la prefactura en BIND 2.50 reconstruida balam-plataforma commits 1ad0e27..02cb042 si commits 20:03–20:42 (cliente); por validar
67 2026-08-26 2026-W35 2 E2-03 Sesión 7:00 am 'Seguimiento - PAF' con Noé y Pedro: permisos y flujo remisión/prefactura/factura; el POST confirmado se timbró (CFDI 2386) 0.50 reconstruida correo Read AI 26-ago 7:35 am si REGISTRO #70; 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 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
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 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
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
+110 -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-07-27
**Última actualización:** 2026-10-02 (corte 16-sep → 2-oct reconstruido, por validar)
## Reglas de captura
@@ -15,6 +15,115 @@ Fuente local de horas efectivamente trabajadas para el proyecto Balam. Se mantie
4. No convertir porcentajes de avance en horas: el avance mide entregable; las horas miden esfuerzo real.
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.
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ó.
| Etapa | Horas al corte | Rango | Estado |
|---|---:|---:|---|
| Etapa 0 | **21.83 h** | 18–22 h | Cerrada y validada |
| Etapa 1 | **32.75 h** | 32–39 h | Capa de escritura implementada; publicación provisional en paputec.mx (3-sep) |
| Etapa 2 (sin ajuste Jira) | **23.75 h** | 38–46 h | Cotización individual y en lote; prefactura por API bloqueada por BIND |
| » Ajuste Jira → BIND | **14.75 h** | 7–10 h | ⚠️ Rebasado: semánticas indocumentadas del API y 5 sesiones acompañadas |
| Etapa 3 | **1.50 h** | 24–29 h | Endurecimiento y runbook hechos en la publicación |
| **Total** | **94.58 h** | | 39.75 h nuevas desde el 6-ago: 38.75 `reconstruida` + 1.00 `confirmada` (transcript del 12-ago) |
**Criterios usados en la reconstrucción:**
- Sesiones con Balam: duración acotada por la hora de inicio de la convocatoria y la hora en que llegó el informe de Read AI (redondeada a 0.25 h). Sin transcript no se infló nada; Johann puede subirlas si duraron más.
- Bloques de desarrollo: del primer archivo tocado (mtime) o del inicio estimado al último commit del bloque; incluyen la documentación del mismo bloque (regla 7).
- **Seis sesiones no tienen registro en la bitácora** (21-ago 1 pm, 24-ago, 26-ago, 3-sep 7 am y 1:30 pm, 9-sep, 10-sep): están en `PENDIENTES.md` como huecos de registro.
- Actividades nuevas: `E1-09` publicación provisional, `E2-02` creación de cotizaciones desde la plataforma, `E2-03` conversión cotización→prefactura/factura, `E3-01` endurecimiento, `E3-02` documentación técnica. El reparto CSV → filas del Excel vive en `generar_avance_2026_09_15.py`.
## Corte al 27-jul
@@ -0,0 +1,108 @@
# Sondeo de `FAC-106` y payload de cotización — 20-ago-2026, noche
Preparación para la sesión del **viernes 21-ago, 7:00 am**. Todo lo de aquí es **solo lectura**: sondeo del ticket por API de Jira, lectura de catálogos de BIND y **dry-run** del endpoint de emisión (ninguna escritura). Ver [REGISTRO #65](../bitacora/REGISTRO.md) (la sesión del 12-ago que terminó en 500) y [#67](../bitacora/REGISTRO.md).
---
## 1 · `FAC-106` — el formulario nuevo existe y es mucho mejor de lo esperado
Creado por **Pedro** el 20-ago 4:12 pm, asignado a **Araceli**. Request type nuevo: **`Cotización Bind`** (id 1196, portal 35). Estatus `Open` (⚠️ **no** `En proceso de facturación`: mañana el disparo es manual, como estaba previsto).
| Campo | ID | Tipo | Valor en FAC-106 |
|---|---|---|---|
| Empresa origen | `customfield_11423` | select | **Balam** |
| Cliente | `customfield_11598` | **select** | **CEMEX** |
| Servicio/Producto | `customfield_11599` | **select** | **Servicio de Enlace** |
| Cantidad | `customfield_11600` | number | **1.0** |
| Monto sin IVA | `customfield_11556` | number | **1234.0** |
| IVA | `customfield_11601` | select | **16%** |
| Tipo de Moneda | `customfield_10053` | select | **MXN** |
| Fecha de vencimiento | `customfield_11597` | date | **2026-08-27** |
⭐ **El hueco de conceptos/partidas quedó resuelto.** `Servicio/Producto` + `Cantidad` + `IVA` son campos estructurados: ya hay con qué armar una partida sin parsear texto ni adjuntos.
⭐ **`Cliente` dejó de ser ADF y ahora es combo** con **17 opciones** que corresponden a los clientes de BIND: ACUNTIA S.A.U. · ATC CONSOLIDADOS · BANCA AFIRME · CEMEX · DEACERO · FRISA FORJADOS · (persona física) · HELMSTONE · IAMCFORWARDER · PLAN CREDI · Público en General · REGIOTURK · SERVICIOS COMPARTIDOS TSM · SERVICIOS LOGISTICOS DYLO · TRANSPORTES LOGISTICOS ESPECIALIZADOS · TRASLADOS Y LOGISTICA ESPECIALIZADA · VictTec LLC.
⭐ **Las 17 categorías de servicio de Pedro, confirmadas** en `Servicio/Producto`: Consultoría y Administración Corporativa · Servicios Profesionales Generales · Gestión de Proyectos Senior / PMO · Gestión de Mesa de Servicio (SDM) · Soporte Especializado L1 · Soporte Especializado L2 · Gestión de Incidentes · Gestión de Cambios · Gestión de Capacidad · Mesa de Ayuda / Service Desk · Desarrollo de Software · Reclutamiento (Headhunting) · Servicio de Enlace · Horas Extraordinarias · Viáticos · Licencias y Software · Liquidaciones y Finiquitos.
### Pendientes que deja el formulario
- 🔴 **`Empresa origen` tiene 3 valores: Balam, RegioTurk y Helmstone.** ¿La cotización se emite siempre en la misma cuenta de BIND, o hay una por empresa? El token actual es uno solo. **Preguntarlo mañana.**
- 🔴 **`IVA` solo ofrece `16%`.** No hay `0%` — y hay clientes de exportación en el combo (ACUNTIA S.A.U., VictTec LLC, IAMCFORWARDER). El Discovery registró **IVA 16%/0%**. Además, en BIND existe al menos una cotización real con `VAT = 0` pese a `VATRate = 0.16`. **Falta la opción 0%.**
- ⚠️ **Sobrevive `Nombre del cliente` (`customfield_11596`, texto libre)** además del combo `11598`. Dos campos de cliente en el mismo sitio: hay que fijar por escrito que **manda el combo**.
- ⚠️ **`customfield_10202`, el campo de cliente que la plataforma leía, ya no aplica.** Era la causa de que el 12-ago *"el cliente no se detectó"*. **Corregido** en `appsettings.Development.json` → `customfield_11598`.
---
## 2 · El catálogo de BIND está peor de lo que se vio en la sesión
`GET /api/Services` devuelve **67 servicios**; `GET /api/Products`, **3 productos**. Buscando el que trae el ticket:
**"SERVICIO DE ENLACE" existe 5 veces**, con IDs distintos y **atributos fiscales que no coinciden entre sí**:
| Code | Unit | SATUnit | ChargeVAT |
|---|---|---|---|
| `81111500` | *(vacío)* | 3984 | **true** |
| `Código - 00000` | E48 | 4376 | false |
| `Código - 00000` | E48 | 4376 | false |
| `Código - 00000` | Servicio | 4376 | false |
| `027` | E48 | 3984 | false |
> 🔴 **Riesgo directo para mañana:** el combo de Jira manda **una etiqueta**, "Servicio de Enlace", y en BIND hay **cinco filas** que difieren en unidad, unidad SAT e incluso en si cobran IVA. **La correspondencia etiqueta → ítem de BIND es ambigua y nadie la ha definido.** Es la segunda mitad de la depuración que Araceli pidió el 12-ago: se depuró el combo de Jira, **falta depurar BIND y fijar el mapeo**.
---
## 3 · Los tres errores que explican el 500 del 12-ago
Verificado leyendo **4 cotizaciones reales** de BIND (folios 1001, 1005, 1012, 1021):
1. 🔴 **`VATRate` va como fracción, no como porcentaje.** Todas las cotizaciones reales traen **`VATRate = 0.16`**. Mandar `16` es un factor de 100 de diferencia.
2. 🔴 **`VAT` de partida es un MONTO, no una tasa.** Comprobado en 3 de 4: `Price × Qty × 0.16 = VAT` (p. ej. 32,422.04 → 5,187.5264). El endpoint **pasa el valor tal cual**, así que quien llame tiene que calcularlo. Para FAC-106: 1234 × 1 × 0.16 = **197.44**.
3. 🟠 **`PriceListID`: cada cotización real usa una lista distinta** (`041d4514…`, `0c4bb05e…`, `0dfbe083…`, `1087dac9…`) y **ninguna coincide** con la única que devuelve `GET /api/PriceLists` (`03e4bb2d…`, "A"). Mandar la lista equivocada es candidato serio al 500. **Recomendación: omitirlo** y dejar que BIND aplique su default — el cliente ya omite los nulos.
Además, sin confirmar:
- ⚠️ **`ID` de partida.** El read model distingue `ID` (renglón) de `ProductID` (ítem del catálogo); el write model **solo tiene `ID`**. Mandar el GUID del servicio en `ID` puede ser justo lo que BIND rechaza. El body de abajo **omite `ID`** y manda la partida como texto con `Title`/`Unit` — que además es como Araceli captura.
- ⚠️ `Type` y `SelectedUnit` siguen sin documentar; van omitidos.
---
## 4 · Body listo para mañana (dry-run ejecutado, operación #18)
IDs resueltos contra BIND en vivo: **MXN** `b7e2c065-bd52-40ca-b508-3accdd538860` · **Matriz** `65ee189a-a163-4f23-8466-87627a5ba776` (única ubicación) · **CEMEX** `6f17c5bd-e7a0-4152-9702-4a486c1b7c69` (RFC `CEM880726UZA`, 90 días de crédito).
```json
{
"LocationID": "65ee189a-a163-4f23-8466-87627a5ba776",
"CurrencyID": "b7e2c065-bd52-40ca-b508-3accdd538860",
"ExchangeRate": 1.0,
"Items": [
{ "Title": "SERVICIO DE ENLACE", "Unit": "E48", "Price": 1234.0,
"Qty": 1.0, "VAT": 197.44, "IEPS": 0, "IndexNumber": 1 }
],
"ClientProspect": {
"isNew": false,
"Client": { "ClientId": "6f17c5bd-…", "ClientName": "CEMEX" }
},
"VATRate": 0.16,
"Comments": "Generada desde el ticket FAC-106 (prueba acompañada)."
}
```
**Si BIND vuelve a rechazar**, el orden de ataque es: (1) agregar `PriceListID` con el de una cotización reciente del mismo cliente; (2) mandar el GUID del servicio en `ID`; (3) probar `Type`. El endpoint ya expone `ex.Body` de BIND en el 502, así que el diagnóstico se lee en pantalla — no se repite el 500 ciego del 12-ago.
---
## 5 · ⚠️ Seguridad: `Bind:Mode` está en **Write** ahora mismo
`GET /api/emision/estado` responde `{"modo":"Write"}` — el candado de ADR-001 está **abierto** fuera de la sesión acompañada, contra lo que se le prometió a Noé ([REGISTRO #31a](../bitacora/REGISTRO.md)). Una confirmación accidental escribiría en producción. **Regresarlo a `ReadOnly`** y encenderlo solo durante la sesión:
```bash
dotnet user-secrets set "Bind:Mode" "ReadOnly" --project src/Balam.Api
```
---
## 6 · Lo que falta cablear (no bloquea la prueba manual)
El dry-run de arriba se armó **a mano** con los datos de `FAC-106`. Para que la pantalla lo arme sola faltan cuatro campos en la configuración y en el DTO de detalle: `Servicio/Producto` (11599), `Cantidad` (11600), `IVA` (11601) y `Fecha de vencimiento` (11597). Con eso el wizard prellena la partida completa desde el ticket.
+1
View File
@@ -0,0 +1 @@
Plan-actividades-avance-2026-07-27.xlsx
Binary file not shown.

After

Width:  |  Height:  |  Size: 356 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 438 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 464 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 355 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 277 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 276 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 258 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 257 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 136 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 357 KiB

+214
View File
@@ -0,0 +1,214 @@
"""Genera Avance-Balam-2026-08-01.xlsx a partir del canónico del 28-jul.
Corte del sábado 1-ago (REGISTRO #61): cierre técnico de la Etapa 1 en lo no
bloqueado (B5 scheduler, B6 RLS verificado, B8 suite de integración, B9 seeder
demo), frontend de LECTURA de la Etapa 2 completo (adelantando las filas del
29-jul al 5-ago y la exportación CSV) y cliente de lectura de la API de Jira.
La propuesta v1.3 fue APROBADA por Noé el 30-jul (REGISTRO #60).
Las horas se leen de `Seguimiento-horas.csv` (fuente de la verdad, corte
52.58 h) y se reparten por fila con el mismo criterio trazable del generador
del 27-jul; el script aborta si el reparto no cuadra con el CSV.
"""
import csv
import os
from collections import defaultdict
import openpyxl
from openpyxl.styles import Alignment, Font, PatternFill
BASE = os.path.dirname(os.path.abspath(__file__))
SRC = os.path.join(BASE, "Avance-Balam-2026-07-28.xlsx")
DST = os.path.join(BASE, "Avance-Balam-2026-08-01.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"
R_FILL, R_FONT = "FFC7CE", "9C0006"
G_FILL, G_FONT = "F2F2F2", "595959"
C_PCT, C_EST, C_TRAB, C_STAT = 4, 5, 6, 7
# --- CSV: totales por etapa y por actividad -----------------------------------
por_act, por_etapa = defaultdict(float), defaultdict(float)
with open(CSV_HORAS, encoding="utf-8", newline="") as fh:
for r in csv.DictReader(fh):
if not (r.get("fecha") or "").strip():
continue
por_act[r["actividad_id"]] += float(r["horas"])
por_etapa[r["etapa"]] += float(r["horas"])
E0, E1 = por_etapa["0"], por_etapa["1"]
AJ = por_act["AJ-01"] # ajuste Jira→BIND (vive dentro de la etapa 2 del CSV)
E2 = por_etapa["2"] - AJ # Etapa 2 propiamente (frontend de lectura)
TOTAL = E0 + E1 + E2 + AJ
assert abs(TOTAL - sum(por_etapa.values())) < 0.01
PCT_GLOBAL = round(TOTAL / ((119 + 146) / 2), 2)
# --- Reparto por fila del delta 28-jul→1-ago (11.00 h nuevas) ------------------
# Trazabilidad (mismas reglas que el generador del 27-jul):
# E1-05 +1.50 (Azure/status 0.25 + envíos v1.3 0.75 + bitácora 0.50) → fila de seguimiento semanal
# E1-08 +2.50 (propuesta v1.3 + Excel de etapas + plan completo) → fila "Definición de alcance Jira"
# E1-01 +1.50 del 1-ago repartida por naturaleza: B5+B8 → backend; B6 → multi-tenant; B9 → modelo
# AJ-01 4.50 (pruebas 401 + investigación + cliente de lectura) → fila "Cliente de la API de Jira"
# E2-01 1.00 repartida entre las filas de frontend que cubrió la sesión
DELTAS_TRAB = {
"Demostración semanal": 1.50,
"Definición de alcance Jira": 2.50,
"Backend base": 0.50,
"Arquitectura multi-tenant": 0.50,
"Modelo de facturas": 0.50,
"Cliente de la API de Jira": 4.50,
"Andamiaje del frontend": 0.30,
"Layout base": 0.15,
"Listado de facturas": 0.25,
"Detalle de factura": 0.15,
"Catálogo de clientes": 0.10,
"Exportación de facturas": 0.05,
}
assert abs(sum(DELTAS_TRAB.values()) - 11.00) < 0.01, "El reparto no suma las 11.00 h nuevas"
# Encabezados de etapa: trab y % (Etapa 1: 7 de 8 entregables terminados — solo
# queda la capa de escritura, bloqueada por Balam; % por entregable cerrado).
ENCABEZADOS = {
"Integracion de plataformas Balam": (TOTAL, PCT_GLOBAL),
"Etapa 0": (E0, 1.0),
"Etapa 1": (E1, 0.875),
"Etapa 2": (E2, 0.50),
"» Ajuste dentro de la Etapa 2": (AJ, 0.45),
"Etapa 3": (None, 0),
}
# (pct, estatus, fill, font, bold) por prefijo de fila — refleja el 1-ago.
FILAS = {
"Modelo de facturas": (1.0, "✅ Completada (modelo, migraciones y seeder de datos demo)", V_FILL, V_FONT, False),
"Demostración semanal": (None, "🟡 En curso (semanal) · demo del vie 7-ago con material listo", A_FILL, A_FONT, False),
"Definición de alcance Jira": (0.95, "🟡 v1.3 APROBADA por Noé (30-jul) · falta el estatus disparador", A_FILL, A_FONT, False),
"Capa de escritura controlada": (0.40, "🔴 Diseño listo · impl. en pausa (flujo en revisión interna de Balam)", R_FILL, R_FONT, True),
"Configuración de infraestructura en Azure": (0, "🔴 Bloqueada · acceso Contributor escalado a Noé (28-jul)", R_FILL, R_FONT, True),
"CI/CD": (0, "🔴 Pendiente · depende de Azure", R_FILL, R_FONT, True),
# Ajuste Jira
"Cliente de la API de Jira": (0.85, "🟡 Cliente de lectura listo y probado (1-ago) · faltan muestreos en FAC", A_FILL, A_FONT, False),
"Disparador por estatus": (0, "🔴 Bloqueada · decisiones de Balam (request type, estatus, monto/conceptos)", R_FILL, R_FONT, True),
"Trazabilidad ticket↔cotización↔factura": (0, "🔴 Bloqueada · requiere el disparador y sesión de escritura acompañada", R_FILL, R_FONT, True),
# Etapa 2 (lectura adelantada el 1-ago)
"Andamiaje del frontend": (1.0, "✅ Completada (1-ago)", V_FILL, V_FONT, False),
"Layout base": (1.0, "✅ Completada (1-ago)", V_FILL, V_FONT, False),
"Listado de facturas": (1.0, "✅ Completada (1-ago, adelantada) · filtros y paginación en servidor", V_FILL, V_FONT, False),
"Detalle de factura": (0.8, "🟡 PDF listo (1-ago, adelantada) · XML pendiente de confirmar con BIND", A_FILL, A_FONT, False),
"Catálogo de clientes": (0.7, "🟡 Catálogo y búsqueda listos (adelantada) · lista blanca editable pendiente", A_FILL, A_FONT, False),
"Tipo de cambio del DOF": (0, "⚪ Próxima · la captura al facturar depende del flujo definido", G_FILL, G_FONT, False),
"Exportación de facturas": (0.8, "🟡 CSV listo (adelantada) · XLSX pendiente", A_FILL, A_FONT, False),
}
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)
wb = openpyxl.load_workbook(SRC)
ws = wb.active
ws["A2"] = ("Plan y avance al 1-ago-2026 · horas trabajadas al corte del sábado 1-ago · "
"frontend de consulta de la Etapa 2 adelantado; escritura y Azure en espera de Balam")
ws["A2"].font = Font(size=10, italic=True, color=GRIS)
aplicado = set()
for row in range(5, ws.max_row + 1):
nombre = str(ws.cell(row=row, column=1).value or "").strip()
if not nombre:
continue
for prefijo, (trab, pct) in ENCABEZADOS.items():
if nombre.startswith(prefijo):
if trab is not None:
c = ws.cell(row=row, column=C_TRAB, value=round(trab, 2))
c.number_format = "0.00"
p = ws.cell(row=row, column=C_PCT, value=pct)
p.number_format = "0%"
if pct >= 1:
pinta(p, V_FILL, V_FONT, bold=True, size=10, align="center")
elif pct > 0:
pinta(p, A_FILL, A_FONT, bold=True, size=10, align="center")
break
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.add(prefijo)
break
for prefijo, (pct, txt, fill, fcolor, bold) in FILAS.items():
if nombre.startswith(prefijo):
if pct is not None:
p = ws.cell(row=row, column=C_PCT, value=pct)
p.number_format = "0%"
if pct >= 1:
pinta(p, V_FILL, V_FONT, bold=True, size=10, align="center")
elif pct > 0:
pinta(p, A_FILL, A_FONT, bold=True, size=10, align="center")
else:
p.font = Font(size=10, color=GRIS)
s = ws.cell(row=row, column=C_STAT, value=txt)
pinta(s, fill, fcolor, bold)
break
faltan = set(DELTAS_TRAB) - aplicado
assert not faltan, f"Filas no encontradas en el Excel: {faltan}"
# --- Notas: se reemplaza el bloque del 28-jul ---------------------------------
primera_nota = None
for row in range(5, ws.max_row + 1):
v = str(ws.cell(row=row, column=1).value or "")
if v.startswith("✅ Completada ·"):
primera_nota = row
if primera_nota and row >= primera_nota:
ws.cell(row=row, column=1).value = None
notas = [
("✅ Completada · 🟡 En curso · 🔴 Pendiente/bloqueada · ⚪ Próxima · "
"(adelantada) = ejecutada antes de su fecha plan", True),
(f"Horas trabajadas al corte del sábado 1-ago: Etapa 0 {E0:.2f} h (cerrada y validada) · "
f"Etapa 1 {E1:.2f} h (rango 32–39) · Etapa 2 {E2:.2f} h · ajuste Jira {AJ:.2f} h "
f"(de 7–10 aprobadas) · total {TOTAL:.2f} h. Base de facturación a $600/h + IVA.", True),
("Etapa 1: 7 de 8 entregables contractuales terminados. El 1-ago se cerraron el "
"seeder de datos de demostración, la verificación real del aislamiento RLS, el "
"sincronizador automático (cada 15 min) y la suite de pruebas de integración "
"(61 pruebas). Solo queda la capa de escritura, en pausa por la definición del "
"flujo de facturación.", False),
("Etapa 2: el frontend de CONSULTA quedó adelantado al 1-ago (inicio de sesión por "
"roles, cartera por moneda, listado de facturas con filtros/búsqueda/paginación, "
"detalle con descarga del PDF del CFDI y catálogo de clientes con exportación). "
"Las filas de emisión (cotizaciones y conversión) siguen sujetas al flujo definido.", False),
("Integración Jira → BIND (7–10 h, v1.3 aprobada por el Ing. Noé el 30-jul): el "
"cliente técnico de lectura quedó construido y probado; el disparador y la "
"trazabilidad esperan las definiciones de negocio (request type, estatus "
"disparador y origen de monto/conceptos) y la prueba de escritura acompañada.", False),
("En espera de Balam: acceso a Azure (grupo de recursos con Contributor — con el "
"Ing. Noé desde el 28-jul; bloquea ambiente, staging y CI/CD), fecha de la sesión "
"del flujo de facturación, y las tres definiciones de la integración Jira.", False),
]
r = primera_nota if primera_nota else ws.max_row + 2
for txt, bold in notas:
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")
try:
ws.merge_cells(start_row=r, start_column=1, end_row=r, end_column=7)
except ValueError:
pass # ya estaba fusionada desde el archivo origen
ws.row_dimensions[r].height = 28 if len(txt) > 120 else 14
r += 1
ws.print_area = f"A1:G{r}"
wb.save(DST)
print("OK ->", DST)
print(f" E0 {E0:.2f} · E1 {E1:.2f} · E2 {E2:.2f} · Jira {AJ:.2f} · TOTAL {TOTAL:.2f} h "
f"· avance global {PCT_GLOBAL:.0%}")
+192
View File
@@ -0,0 +1,192 @@
"""Genera Avance-Balam-2026-08-06.xlsx a partir del corte del 1-ago.
Corte del jueves 6-ago (sesión nocturna, previa a la demo del vie 7-ago): la
LECTURA de la bandeja FAC quedó completa y en la plataforma — sondeo de
customfields (IDs capturados), endpoints /api/jira/solicitudes, bandeja de
solicitudes y detalle fieles al prototipo con datos reales (solo lectura),
adjuntos descargables vía proxy, y el shell con el chrome del prototipo
aplicado a toda la app. Disparador y trazabilidad siguen bloqueados por las
definiciones de negocio de Balam.
Las horas se leen de `Seguimiento-horas.csv` (fuente de la verdad, corte
53.83 h); el script aborta si el reparto por fila no cuadra con el CSV.
"""
import csv
import os
from collections import defaultdict
import openpyxl
from openpyxl.styles import Alignment, Font, PatternFill
BASE = os.path.dirname(os.path.abspath(__file__))
SRC = os.path.join(BASE, "Avance-Balam-2026-08-01.xlsx")
DST = os.path.join(BASE, "Avance-Balam-2026-08-06.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"
R_FILL, R_FONT = "FFC7CE", "9C0006"
G_FILL, G_FONT = "F2F2F2", "595959"
C_PCT, C_EST, C_TRAB, C_STAT = 4, 5, 6, 7
# --- CSV: totales por etapa y por actividad -----------------------------------
por_act, por_etapa = defaultdict(float), defaultdict(float)
with open(CSV_HORAS, encoding="utf-8", newline="") as fh:
for r in csv.DictReader(fh):
if not (r.get("fecha") or "").strip():
continue
por_act[r["actividad_id"]] += float(r["horas"])
por_etapa[r["etapa"]] += float(r["horas"])
E0, E1 = por_etapa["0"], por_etapa["1"]
AJ = por_act["AJ-01"] # ajuste Jira→BIND (vive dentro de la etapa 2 del CSV)
E2 = por_etapa["2"] - AJ # Etapa 2 propiamente (frontend)
TOTAL = E0 + E1 + E2 + AJ
assert abs(TOTAL - sum(por_etapa.values())) < 0.01
PCT_GLOBAL = round(TOTAL / ((119 + 146) / 2), 2)
# --- Reparto por fila del delta 1-ago→6-ago (1.25 h nuevas) --------------------
# Trazabilidad:
# AJ-01 +1.00 (sondeo de customfields + endpoints + bandeja/detalle + adjuntos)
# → fila "Cliente de la API de Jira" (su entregable es la lectura de FAC)
# E2-01 +0.25 (shell con el chrome del prototipo) → fila "Layout base"
DELTAS_TRAB = {
"Cliente de la API de Jira": 1.00,
"Layout base": 0.25,
}
assert abs(sum(DELTAS_TRAB.values()) - 1.25) < 0.01, "El reparto no suma las 1.25 h nuevas"
ENCABEZADOS = {
"Integracion de plataformas Balam": (TOTAL, PCT_GLOBAL),
"Etapa 0": (E0, 1.0),
"Etapa 1": (E1, 0.875),
"Etapa 2": (E2, 0.50),
"» Ajuste dentro de la Etapa 2": (AJ, 0.55),
"Etapa 3": (None, 0),
}
# (pct, estatus, fill, font, bold) — solo lo que cambió respecto al 1-ago.
FILAS = {
"Cliente de la API de Jira": (1.0, "✅ Completada (6-ago): bandeja de solicitudes y detalle en la plataforma "
"leyendo FAC en vivo (solo lectura) · customfields mapeados · adjuntos vía proxy",
V_FILL, V_FONT, False),
"Layout base": (1.0, "✅ Completada · chrome del prototipo aplicado a toda la app (6-ago)", V_FILL, V_FONT, False),
}
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)
wb = openpyxl.load_workbook(SRC)
ws = wb.active
ws["A2"] = ("Plan y avance al 6-ago-2026 · horas trabajadas al corte del jueves 6-ago · "
"lectura de la bandeja Jira (FAC) completa y en pantalla; disparador y escritura en espera de Balam")
ws["A2"].font = Font(size=10, italic=True, color=GRIS)
# Las notas del corte anterior van fusionadas A:G y algunas empiezan con
# "Etapa 1: …", que colisiona con los prefijos de encabezado: se localizan y
# limpian ANTES del bucle principal, y este se detiene donde empezaban.
primera_nota = None
for row in range(5, ws.max_row + 1):
v = str(ws.cell(row=row, column=1).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=1).value = None
tope = primera_nota if primera_nota else ws.max_row + 1
aplicado = set()
for row in range(5, tope):
nombre = str(ws.cell(row=row, column=1).value or "").strip()
if not nombre:
continue
for prefijo, (trab, pct) in ENCABEZADOS.items():
if nombre.startswith(prefijo):
if trab is not None:
c = ws.cell(row=row, column=C_TRAB, value=round(trab, 2))
c.number_format = "0.00"
p = ws.cell(row=row, column=C_PCT, value=pct)
p.number_format = "0%"
if pct >= 1:
pinta(p, V_FILL, V_FONT, bold=True, size=10, align="center")
elif pct > 0:
pinta(p, A_FILL, A_FONT, bold=True, size=10, align="center")
break
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.add(prefijo)
break
for prefijo, (pct, txt, fill, fcolor, bold) in FILAS.items():
if nombre.startswith(prefijo):
if pct is not None:
p = ws.cell(row=row, column=C_PCT, value=pct)
p.number_format = "0%"
if pct >= 1:
pinta(p, V_FILL, V_FONT, bold=True, size=10, align="center")
elif pct > 0:
pinta(p, A_FILL, A_FONT, bold=True, size=10, align="center")
else:
p.font = Font(size=10, color=GRIS)
s = ws.cell(row=row, column=C_STAT, value=txt)
pinta(s, fill, fcolor, bold)
break
faltan = set(DELTAS_TRAB) - aplicado
assert not faltan, f"Filas no encontradas en el Excel: {faltan}"
# --- Notas: se escribe el bloque del 6-ago donde estaba el del 1-ago ----------
notas = [
("✅ Completada · 🟡 En curso · 🔴 Pendiente/bloqueada · ⚪ Próxima · "
"(adelantada) = ejecutada antes de su fecha plan", True),
(f"Horas trabajadas al corte del jueves 6-ago: Etapa 0 {E0:.2f} h (cerrada y validada) · "
f"Etapa 1 {E1:.2f} h (rango 32–39) · Etapa 2 {E2:.2f} h · ajuste Jira {AJ:.2f} h "
f"(de 7–10 aprobadas) · total {TOTAL:.2f} h. Base de facturación a $600/h + IVA.", True),
("Etapa 1: 7 de 8 entregables contractuales terminados. Solo queda la capa de "
"escritura, en pausa por la definición del flujo de facturación.", False),
("Integración Jira → BIND (7–10 h, v1.3 aprobada por el Ing. Noé el 30-jul): la "
"LECTURA quedó completa el 6-ago — la plataforma muestra la bandeja de solicitudes "
"y el detalle de cada ticket de FAC en vivo (solo lectura), con los campos del "
"formulario mapeados y los adjuntos descargables. El disparador de cotización y la "
"trazabilidad esperan las definiciones de negocio (request type, estatus disparador "
"y origen de monto/conceptos) y la prueba de escritura acompañada.", False),
("Etapa 2: además del frontend de consulta (1-ago), toda la aplicación estrenó la "
"interfaz del prototipo aprobado (navegación por módulos, marca y estado del "
"sistema). Las filas de emisión siguen sujetas al flujo definido.", False),
("En espera de Balam: acceso a Azure (Contributor — con el Ing. Noé desde el "
"28-jul), fecha de la sesión del flujo de facturación, y las tres definiciones de "
"la integración Jira.", False),
]
r = primera_nota if primera_nota else ws.max_row + 2
for txt, bold in notas:
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")
try:
ws.merge_cells(start_row=r, start_column=1, end_row=r, end_column=7)
except ValueError:
pass # ya estaba fusionada desde el archivo origen
ws.row_dimensions[r].height = 28 if len(txt) > 120 else 14
r += 1
ws.print_area = f"A1:G{r}"
wb.save(DST)
print("OK ->", DST)
print(f" E0 {E0:.2f} · E1 {E1:.2f} · E2 {E2:.2f} · Jira {AJ:.2f} · TOTAL {TOTAL:.2f} h "
f"· avance global {PCT_GLOBAL:.0%}")
+192
View File
@@ -0,0 +1,192 @@
"""Genera Avance-Balam-2026-08-06b.xlsx a partir del corte de la tarde del 6-ago.
Segundo corte del jueves 6-ago (sesión vespertina, previa a la demo del vie
7-ago): el wizard de emisión asistida quedó construido en la plataforma como
interfaz navegable de simulación — 5 pasos fieles al prototipo aprobado, con
las cotizaciones reales de BIND y el ticket FAC vivo (solo lectura), prefactura
simulada con marca de agua y aprobación humana visual. El timbrado queda
deshabilitado hasta que Balam autorice la escritura (B7). El corte de la
mañana (Avance-Balam-2026-08-06.xlsx) no se toca.
Las horas se leen de `Seguimiento-horas.csv` (fuente de la verdad, corte
54.83 h); el script aborta si el reparto por fila no cuadra con el CSV.
"""
import csv
import os
from collections import defaultdict
import openpyxl
from openpyxl.styles import Alignment, Font, PatternFill
BASE = os.path.dirname(os.path.abspath(__file__))
SRC = os.path.join(BASE, "Avance-Balam-2026-08-06.xlsx")
DST = os.path.join(BASE, "Avance-Balam-2026-08-06b.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"
R_FILL, R_FONT = "FFC7CE", "9C0006"
G_FILL, G_FONT = "F2F2F2", "595959"
C_PCT, C_EST, C_TRAB, C_STAT = 4, 5, 6, 7
# --- CSV: totales por etapa y por actividad -----------------------------------
por_act, por_etapa = defaultdict(float), defaultdict(float)
with open(CSV_HORAS, encoding="utf-8", newline="") as fh:
for r in csv.DictReader(fh):
if not (r.get("fecha") or "").strip():
continue
por_act[r["actividad_id"]] += float(r["horas"])
por_etapa[r["etapa"]] += float(r["horas"])
E0, E1 = por_etapa["0"], por_etapa["1"]
AJ = por_act["AJ-01"] # ajuste Jira→BIND (vive dentro de la etapa 2 del CSV)
E2 = por_etapa["2"] - AJ # Etapa 2 propiamente (frontend)
TOTAL = E0 + E1 + E2 + AJ
assert abs(TOTAL - sum(por_etapa.values())) < 0.01
PCT_GLOBAL = round(TOTAL / ((119 + 146) / 2), 2)
# --- Reparto por fila del delta corte matutino→vespertino (1.00 h nueva) -------
# Trazabilidad:
# E2-01 +1.00 (wizard de emisión asistida, 5 pasos en simulación)
# → fila "Conversión cotización→factura" (el wizard ES su interfaz;
# la conversión real sigue bloqueada por B7)
DELTAS_TRAB = {
"Conversión cotización→factura": 1.00,
}
assert abs(sum(DELTAS_TRAB.values()) - 1.00) < 0.01, "El reparto no suma la 1.00 h nueva"
ENCABEZADOS = {
"Integracion de plataformas Balam": (TOTAL, PCT_GLOBAL),
"Etapa 0": (E0, 1.0),
"Etapa 1": (E1, 0.875),
"Etapa 2": (E2, 0.55),
"» Ajuste dentro de la Etapa 2": (AJ, 0.55),
"Etapa 3": (None, 0),
}
# (pct, estatus, fill, font, bold) — solo lo que cambió respecto al corte matutino.
FILAS = {
"Conversión cotización→factura": (0.4, "🟡 Interfaz completa en simulación (6-ago): wizard de 5 pasos con "
"cotizaciones y tickets reales, prefactura simulada y aprobación humana "
"visual · el timbrado real espera la autorización de escritura (B7)",
A_FILL, A_FONT, False),
}
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)
wb = openpyxl.load_workbook(SRC)
ws = wb.active
ws["A2"] = ("Plan y avance al 6-ago-2026 (2.º corte del día) · lectura de la bandeja Jira (FAC) "
"completa y wizard de emisión asistida en simulación; timbrado y escritura en espera de Balam")
ws["A2"].font = Font(size=10, italic=True, color=GRIS)
# Las notas del corte anterior van fusionadas A:G y algunas empiezan con
# "Etapa 1: …", que colisiona con los prefijos de encabezado: se localizan y
# limpian ANTES del bucle principal, y este se detiene donde empezaban.
primera_nota = None
for row in range(5, ws.max_row + 1):
v = str(ws.cell(row=row, column=1).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=1).value = None
tope = primera_nota if primera_nota else ws.max_row + 1
aplicado = set()
for row in range(5, tope):
nombre = str(ws.cell(row=row, column=1).value or "").strip()
if not nombre:
continue
for prefijo, (trab, pct) in ENCABEZADOS.items():
if nombre.startswith(prefijo):
if trab is not None:
c = ws.cell(row=row, column=C_TRAB, value=round(trab, 2))
c.number_format = "0.00"
p = ws.cell(row=row, column=C_PCT, value=pct)
p.number_format = "0%"
if pct >= 1:
pinta(p, V_FILL, V_FONT, bold=True, size=10, align="center")
elif pct > 0:
pinta(p, A_FILL, A_FONT, bold=True, size=10, align="center")
break
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.add(prefijo)
break
for prefijo, (pct, txt, fill, fcolor, bold) in FILAS.items():
if nombre.startswith(prefijo):
if pct is not None:
p = ws.cell(row=row, column=C_PCT, value=pct)
p.number_format = "0%"
if pct >= 1:
pinta(p, V_FILL, V_FONT, bold=True, size=10, align="center")
elif pct > 0:
pinta(p, A_FILL, A_FONT, bold=True, size=10, align="center")
else:
p.font = Font(size=10, color=GRIS)
s = ws.cell(row=row, column=C_STAT, value=txt)
pinta(s, fill, fcolor, bold)
break
faltan = set(DELTAS_TRAB) - aplicado
assert not faltan, f"Filas no encontradas en el Excel: {faltan}"
# --- Notas: se escribe el bloque vespertino donde estaba el matutino ----------
notas = [
("✅ Completada · 🟡 En curso · 🔴 Pendiente/bloqueada · ⚪ Próxima · "
"(adelantada) = ejecutada antes de su fecha plan", True),
(f"Horas trabajadas al 2.º corte del jueves 6-ago: Etapa 0 {E0:.2f} h (cerrada y validada) · "
f"Etapa 1 {E1:.2f} h (rango 32–39) · Etapa 2 {E2:.2f} h · ajuste Jira {AJ:.2f} h "
f"(de 7–10 aprobadas) · total {TOTAL:.2f} h. Base de facturación a $600/h + IVA.", True),
("Etapa 1: 7 de 8 entregables contractuales terminados. Solo queda la capa de "
"escritura, en pausa por la definición del flujo de facturación.", False),
("Integración Jira → BIND (7–10 h, v1.3 aprobada por el Ing. Noé el 30-jul): la "
"LECTURA quedó completa el 6-ago — la plataforma muestra la bandeja de solicitudes "
"y el detalle de cada ticket de FAC en vivo (solo lectura), con los campos del "
"formulario mapeados y los adjuntos descargables. El disparador de cotización y la "
"trazabilidad esperan las definiciones de negocio (request type, estatus disparador "
"y origen de monto/conceptos) y la prueba de escritura acompañada.", False),
("Etapa 2: al frontend de consulta y al chrome del prototipo se sumó el wizard de "
"emisión asistida (6-ago): 5 pasos sobre cotizaciones y tickets reales, con regla "
"PPD/PUE y advertencia SAT, IVA automático, prefactura simulada y aprobación humana "
"visual. El timbrado queda deshabilitado hasta que Balam autorice la escritura (B7).", False),
("En espera de Balam: acceso a Azure (Contributor — con el Ing. Noé desde el "
"28-jul), fecha de la sesión del flujo de facturación, y las tres definiciones de "
"la integración Jira.", False),
]
r = primera_nota if primera_nota else ws.max_row + 2
for txt, bold in notas:
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")
try:
ws.merge_cells(start_row=r, start_column=1, end_row=r, end_column=7)
except ValueError:
pass # ya estaba fusionada desde el archivo origen
ws.row_dimensions[r].height = 28 if len(txt) > 120 else 14
r += 1
ws.print_area = f"A1:G{r}"
wb.save(DST)
print("OK ->", DST)
print(f" E0 {E0:.2f} · E1 {E1:.2f} · E2 {E2:.2f} · Jira {AJ:.2f} · TOTAL {TOTAL:.2f} h "
f"· avance global {PCT_GLOBAL:.0%}")
+253
View File
@@ -0,0 +1,253 @@
"""Genera Avance-Balam-2026-09-15.xlsx a partir del corte del 6-ago (2.º).
Corte del martes 15-sep: cubre del 10-ago al 15-sep. Lo que pasó en el
periodo: capa de escritura real (primera cotización en BIND desde un ticket el
21-ago, folio 1026; cotización en lote el 7-sep), el camino cotización→
prefactura construido y BLOQUEADO por BIND (26-ago: IsFiscalInvoice=true timbra
al crear, false produce remisión, el tipo Prefactura solo existe en su UI),
la plataforma publicada en https://paputec.mx/balam/ el 3-sep como ambiente
provisional mientras llega Azure, y seis sesiones "Seguimiento PAF" con Balam
(21, 24 y 26-ago; 3, 9 y 10-sep). El corte del 6-ago no se toca.
Las horas se leen de `Seguimiento-horas.csv` (fuente de la verdad); el script
aborta si el reparto por fila no cuadra con las horas nuevas del CSV. Las
39.25 h nuevas están marcadas `reconstruida` (evidencia: commits, correos e
informes de reunión) y pendientes de validación por Johann.
"""
import csv
import os
from collections import defaultdict
import openpyxl
from openpyxl.styles import Alignment, Font, PatternFill
BASE = os.path.dirname(os.path.abspath(__file__))
SRC = os.path.join(BASE, "Avance-Balam-2026-08-06b.xlsx")
DST = os.path.join(BASE, "Avance-Balam-2026-09-15.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"
R_FILL, R_FONT = "FFC7CE", "9C0006"
G_FILL, G_FONT = "F2F2F2", "595959"
C_PCT, C_EST, C_TRAB, C_STAT = 4, 5, 6, 7
CORTE_ANTERIOR = 54.83 # 2.º corte del 6-ago, ya reportado
# --- CSV: totales por etapa y por actividad -----------------------------------
por_act, por_etapa = defaultdict(float), defaultdict(float)
nuevas = 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 r["fecha"] > "2026-08-06":
nuevas += h
E0, E1, E3 = por_etapa["0"], por_etapa["1"], por_etapa["3"]
AJ = por_act["AJ-01"] # ajuste Jira→BIND (vive dentro de la etapa 2 del CSV)
E2 = por_etapa["2"] - AJ # Etapa 2 propiamente (frontend + emisión)
TOTAL = E0 + E1 + E2 + AJ + E3
assert abs(TOTAL - sum(por_etapa.values())) < 0.01
assert abs(TOTAL - CORTE_ANTERIOR - nuevas) < 0.01, "El CSV no arranca del corte del 6-ago"
PCT_GLOBAL = round(TOTAL / ((119 + 146) / 2), 2)
# --- Reparto por fila de las horas nuevas (10-ago → 15-sep) --------------------
# Trazabilidad CSV → fila del Excel:
# E1-05 +4.50 (bitácora, cruce del buzón, WhatsApp con Pedro, correo Azure,
# corte de horas) → "Demostración semanal + reporte de horas" (gestión)
# E1-09 +2.50 (publicación en paputec.mx) → 2.25 a "Configuración de
# infraestructura en Azure" (ambiente provisional) y 0.25 a "CI/CD"
# (deploy.sh reproducible + secretos fuera del repo)
# AJ-01 +9.25 → 4.50 a "Disparador…" (sondeo FAC-106 + cableado 20-ago 1.50,
# investigación hasta el folio 1026 el 21-ago 3.00) y 4.75 a
# "Trazabilidad… y prueba observada" (definiciones 10-ago 1.50, sesión
# 12-ago 1.00, Excel de campos 0.75, sesiones 21-ago 0.75 y 3-sep 0.75)
# E2-02 +8.00 (capa de escritura de cotización 11/12-ago 4.50, ítem por
# código 3-sep 1.50, lote 7-sep 2.00) → "Creación de cotizaciones"
# E2-03 +13.50 (prefactura 23-ago, prefactura→factura 25-ago, incidente e
# investigación 26-ago, sesiones 24 y 26-ago, 9 y 10-sep) →
# "Conversión cotización→factura"
# E3-01 +1.00 → "Endurecimiento de seguridad"; E3-02 +0.50 → "Documentación técnica"
DELTAS_TRAB = {
"Demostración semanal": 4.50,
"Configuración de infraestructura en Azure": 2.25,
"CI/CD": 0.25,
"Disparador por estatus": 4.50,
"Trazabilidad ticket": 4.75,
"Creación de cotizaciones desde la plataforma": 8.00,
"Conversión cotización→factura": 13.50,
"Endurecimiento de seguridad": 1.00,
"Documentación técnica": 0.50,
}
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")
ENCABEZADOS = {
"Integracion de plataformas Balam": (TOTAL, PCT_GLOBAL),
"Etapa 0": (E0, 1.0),
"Etapa 1": (E1, 0.95),
"Etapa 2": (E2, 0.70),
"» Ajuste dentro de la Etapa 2": (AJ, 0.85),
"Etapa 3": (E3, 0.05),
}
# (pct, estatus, fill, font, bold) — solo lo que cambió respecto al 6-ago.
FILAS = {
"Demostración semanal": (0.6, "🟡 En curso · el seguimiento pasó a Pedro (sesiones 'Seguimiento PAF' "
"21, 24 y 26-ago; 3, 9 y 10-sep) · corte de horas al 15-sep",
A_FILL, A_FONT, False),
"Capa de escritura controlada": (0.9, "✅ Implementada: dry-run + confirmación humana + auditoría en "
"WriteOperations para cotización (21-ago) y prefactura (23-ago) · "
"el timbrado no existe en el API de BIND (es humano, en su pantalla)",
V_FILL, V_FONT, False),
"Configuración de infraestructura en Azure": (0.5, "🟡 Publicada en https://paputec.mx/balam/ (3-sep) como "
"ambiente provisional · Azure sigue con Balam (requisitos "
"enviados a Noé el 25-ago; datos del tenant revisados el 3-sep)",
A_FILL, A_FONT, False),
"CI/CD": (0.3, "🟡 Publicación reproducible (deploy.sh) con secretos fuera del repo · CI real depende de Azure",
A_FILL, A_FONT, False),
"Disparador por estatus": (0.6, "🟡 Cotización real desde el ticket (21-ago, folio 1026) y en lote (7-sep), "
"con disparo manual · el disparo automático por estatus espera el ambiente definitivo",
A_FILL, A_FONT, False),
"Trazabilidad ticket": (0.6, "🟡 Vínculo ticket→cotización→prefactura en la BD · pruebas observadas con "
"Balam el 12, 21, 24 y 26-ago y el 3-sep",
A_FILL, A_FONT, False),
"Creación de cotizaciones desde la plataforma": (0.9, "✅ Cotización individual y en lote con dry-run y "
"confirmación humana · ítem del catálogo BIND por código y "
"concepto = título del ticket (definición del 3-sep)",
V_FILL, V_FONT, False),
"Conversión cotización→factura": (0.6, "🔴 Prefactura por API bloqueada por BIND: IsFiscalInvoice=true "
"TIMBRA al crear (incidente 26-ago, CFDI 2386) y false produce remisión; "
"el tipo Prefactura solo existe en su UI · en curso la captura del "
"endpoint interno (sesiones 9 y 10-sep)",
R_FILL, R_FONT, True),
"Endurecimiento de seguridad": (0.4, "🟡 Aplicado en la publicación: contenedor de solo lectura, rate limit, "
"bloqueo de login, CSP/HSTS, respaldos diarios",
A_FILL, A_FONT, False),
"Documentación técnica": (0.3, "🟡 Runbook de publicación, operación y rollback (deploy/paputec/README.md)",
A_FILL, A_FONT, False),
}
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)
wb = openpyxl.load_workbook(SRC)
ws = wb.active
ws["A2"] = ("Plan y avance al 15-sep-2026 · plataforma publicada en paputec.mx (provisional), cotización real "
"desde el ticket (individual y en lote); prefactura por API bloqueada por BIND · horas del "
"10-ago al 15-sep reconstruidas, pendientes de validar")
ws["A2"].font = Font(size=10, italic=True, color=GRIS)
# Las notas del corte anterior van fusionadas A:G y algunas empiezan con
# "Etapa 1: …", que colisiona con los prefijos de encabezado: se localizan y
# limpian ANTES del bucle principal, y este se detiene donde empezaban.
primera_nota = None
for row in range(5, ws.max_row + 1):
v = str(ws.cell(row=row, column=1).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=1).value = None
tope = primera_nota if primera_nota else ws.max_row + 1
aplicado = set()
for row in range(5, tope):
nombre = str(ws.cell(row=row, column=1).value or "").strip()
if not nombre:
continue
for prefijo, (trab, pct) in ENCABEZADOS.items():
if nombre.startswith(prefijo):
if trab is not None:
c = ws.cell(row=row, column=C_TRAB, value=round(trab, 2))
c.number_format = "0.00"
p = ws.cell(row=row, column=C_PCT, value=pct)
p.number_format = "0%"
if pct >= 1:
pinta(p, V_FILL, V_FONT, bold=True, size=10, align="center")
elif pct > 0:
pinta(p, A_FILL, A_FONT, bold=True, size=10, align="center")
break
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.add(prefijo)
break
for prefijo, (pct, txt, fill, fcolor, bold) in FILAS.items():
if nombre.startswith(prefijo):
if pct is not None:
p = ws.cell(row=row, column=C_PCT, value=pct)
p.number_format = "0%"
if pct >= 1:
pinta(p, V_FILL, V_FONT, bold=True, size=10, align="center")
elif pct > 0:
pinta(p, A_FILL, A_FONT, bold=True, size=10, align="center")
else:
p.font = Font(size=10, color=GRIS)
s = ws.cell(row=row, column=C_STAT, value=txt)
pinta(s, fill, fcolor, bold)
break
faltan = set(DELTAS_TRAB) - aplicado
assert not faltan, f"Filas no encontradas en el Excel: {faltan}"
# --- Notas: se escribe el bloque del 15-sep donde estaba el del 6-ago ---------
notas = [
("✅ Completada · 🟡 En curso · 🔴 Pendiente/bloqueada · ⚪ Próxima · "
"(adelantada) = ejecutada antes de su fecha plan", True),
(f"Horas trabajadas al 15-sep: Etapa 0 {E0:.2f} h (cerrada y validada) · Etapa 1 {E1:.2f} h "
f"(rango 32–39) · Etapa 2 {E2:.2f} h · ajuste Jira {AJ:.2f} h (de 7–10 aprobadas) · "
f"Etapa 3 {E3:.2f} h · total {TOTAL:.2f} h. Las {nuevas:.2f} h posteriores al corte del 6-ago "
"están reconstruidas con evidencia (commits, correos, informes de reunión) y pendientes de "
"validar. Base de facturación a $600/h + IVA.", True),
("Etapa 1: la capa de escritura quedó implementada (cotización y prefactura con dry-run, "
"confirmación humana y auditoría). Solo Azure y CI/CD siguen dependiendo de Balam; mientras "
"tanto la plataforma está publicada en https://paputec.mx/balam/ desde el 3-sep (BIND y Jira "
"en solo lectura fuera de sesión).", False),
("Integración Jira → BIND: primera cotización real desde un ticket el 21-ago (folio 1026, tras "
"5 intentos por dos semánticas indocumentadas del API: Type=2 y VAT como tasa), tres tickets "
"cotizados en la sesión del 3-sep y cotización en lote desde el 7-sep. Rebasa las 7–10 h "
"aprobadas por las semánticas indocumentadas y las sesiones acompañadas (12, 21-ago, 3-sep).", False),
("Etapa 2: la prefactura por API NO es posible con lo que BIND expone (26-ago: IsFiscalInvoice=true "
"timbra al crear —CFDI 2386, a cancelar— y false produce remisión; el tipo Prefactura solo "
"existe en su UI). Sesiones del 9 y 10-sep con Araceli y Pedro para capturar el endpoint "
"interno y decidir el camino (réplica, detección al timbrado o soporte de BIND).", False),
("En espera de Balam: acceso a Azure (requisitos enviados el 25-ago), cancelación del CFDI 2386 "
"y de la cotización 1026, y la definición del camino de prefactura.", False),
]
r = primera_nota if primera_nota else ws.max_row + 2
for txt, bold in notas:
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")
try:
ws.merge_cells(start_row=r, start_column=1, end_row=r, end_column=7)
except ValueError:
pass # ya estaba fusionada desde el archivo origen
ws.row_dimensions[r].height = 40 if len(txt) > 220 else (28 if len(txt) > 120 else 14)
r += 1
ws.print_area = f"A1:G{r}"
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) · avance global {PCT_GLOBAL:.0%}")
+247
View File
@@ -0,0 +1,247 @@
"""Genera Avance-Balam-2026-09-16.xlsx a partir del corte del 15-sep.
Corte del miércoles 16-sep: sin sesiones ni desarrollo nuevos desde el 15-sep;
solo se publicó el Excel de avance en Google Sheets (Drive/Balam) y se
regeneró este corte. El corte del 15-sep no se toca.
Además, a petición de Johann (16-sep), las columnas Inicio/Fin dejan de
mostrar las fechas PLAN y muestran las fechas REALES: primer y último día
trabajado de cada tarea según `Seguimiento-horas.csv`, la bitácora y los
commits ("—" = sin iniciar; las tareas en curso llevan como Fin el último día
trabajado). Las fechas plan quedan en Avance-Balam-2026-08-06b.xlsx. Las horas se leen de
`Seguimiento-horas.csv`; el script aborta si el reparto no cuadra con las
horas nuevas del CSV.
"""
import csv
import os
from collections import defaultdict
import openpyxl
from openpyxl.styles import Alignment, Font, PatternFill
BASE = os.path.dirname(os.path.abspath(__file__))
SRC = os.path.join(BASE, "Avance-Balam-2026-09-15.xlsx")
DST = os.path.join(BASE, "Avance-Balam-2026-09-16.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_PCT, C_TRAB, C_STAT = 4, 6, 7
CORTE_ANTERIOR = 94.08 # corte del 15-sep
# --- CSV: totales por etapa y por actividad -----------------------------------
por_act, por_etapa = 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 r["fecha"] > "2026-09-15":
nuevas += 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 15-sep"
PCT_GLOBAL = round(TOTAL / ((119 + 146) / 2), 2)
# E1-05 +0.50 (Excel en Google Sheets + corte del 16-sep) → gestión y cortes.
DELTAS_TRAB = {"Demostración semanal": 0.50}
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")
ENCABEZADOS = {
"Integracion de plataformas Balam": (TOTAL, PCT_GLOBAL),
"Etapa 0": (E0, 1.0),
"Etapa 1": (E1, 0.95),
"Etapa 2": (E2, 0.70),
"» Ajuste dentro de la Etapa 2": (AJ, 0.85),
"Etapa 3": (E3, 0.05),
}
# --- Fechas reales por fila (dd/mm/yyyy o "—") ---------------------------------
# Trazabilidad: CSV (fecha por actividad), REGISTRO.md y commits de ambos repos.
SIN_INICIAR = ("—", "—")
FECHAS_REALES = {
"Integracion de plataformas Balam": ("01/07/2026", "16/09/2026"), # en curso
"Etapa 0": ("01/07/2026", "24/07/2026"), # validada por Balam el 24-jul
"Sesión de arranque (kickoff)": ("01/07/2026", "01/07/2026"),
"Sesión de Discovery": ("06/07/2026", "07/07/2026"), # facturación 6-jul, cobranza 7-jul
"Entrega y validación de accesos": ("06/07/2026", "06/07/2026"),
"Validación técnica de la API de BIND": ("06/07/2026", "06/07/2026"), # 117 GETs, adelantada un día
"Prototipo visual navegable": ("08/07/2026", "10/07/2026"),
"Documento de hallazgos": ("10/07/2026", "10/07/2026"), # PDF + correo del 10-jul
"Sesión de validación del prototipo": ("23/07/2026", "24/07/2026"), # guion 23-jul, sesión 24-jul
"Etapa 1": ("13/07/2026", "03/09/2026"), # técnica cerrada 1-ago; publicación provisional 3-sep
"Demostración semanal": ("15/07/2026", "16/09/2026"), # gestión y cortes, en curso
"Repositorio privado GitHub": ("13/07/2026", "16/07/2026"), # push del scaffold 16-jul
"Backend base": ("13/07/2026", "19/07/2026"), # B0–B2 el 19-jul
"Arquitectura multi-tenant": ("14/07/2026", "01/08/2026"), # RLS 21-jul, verificación balam_app 1-ago
"Cliente de la API de BIND": ("15/07/2026", "15/07/2026"),
"Capa de escritura controlada": ("19/07/2026", "23/08/2026"), # diseño 19-jul; cotización 11-ago; prefactura 23-ago
"Sincronización de clientes": ("19/07/2026", "19/07/2026"), # B3
"Configuración de infraestructura en Azure": ("03/09/2026", "03/09/2026"), # publicación provisional en paputec.mx
"Sincronización de facturas": ("21/07/2026", "01/08/2026"), # B4 21-jul; scheduler B5 1-ago
"Sesión de reglas y dudas": ("21/07/2026", "22/07/2026"), # agenda 21-jul, sesión 22-jul
"Definición de alcance Jira": ("22/07/2026", "07/08/2026"), # v1.3 aprobada 30-jul; definiciones cerradas 7-ago
"Modelo de facturas": ("21/07/2026", "01/08/2026"), # mapper 21-jul; seeder demo 1-ago
"CI/CD": ("03/09/2026", "03/09/2026"), # deploy.sh + secretos fuera del repo
"Etapa 2": ("01/08/2026", "10/09/2026"), # en curso
"» Ajuste dentro de la Etapa 2": ("27/07/2026", "03/09/2026"), # sesión con Pedro 27-jul → tickets cotizados 3-sep
"Cliente de la API de Jira": ("29/07/2026", "06/08/2026"),
"Disparador por estatus": ("20/08/2026", "07/09/2026"), # sondeo FAC-106 → folio 1026 (21-ago) → lote 7-sep
"Trazabilidad ticket": ("10/08/2026", "03/09/2026"), # definiciones → pruebas observadas
"Andamiaje del frontend": ("16/07/2026", "01/08/2026"), # scaffold 16-jul; login JWT 1-ago
"Layout base": ("01/08/2026", "06/08/2026"),
"Listado de facturas": ("01/08/2026", "01/08/2026"),
"Detalle de factura": ("01/08/2026", "01/08/2026"), # PDF; XML pendiente
"Catálogo de clientes": ("01/08/2026", "01/08/2026"),
"Tipo de cambio del DOF": SIN_INICIAR,
"Creación de cotizaciones desde la plataforma": ("06/08/2026", "07/09/2026"), # wizard UI 6-ago → lote 7-sep
"Conversión cotización→factura": ("06/08/2026", "10/09/2026"), # simulación 6-ago → captura del endpoint 10-sep
"Exportación de facturas": ("01/08/2026", "01/08/2026"), # CSV
"Pruebas integrales": SIN_INICIAR,
"Etapa 3": ("03/09/2026", "03/09/2026"), # solo endurecimiento y runbook
"Módulo de cobranza": SIN_INICIAR,
"Alertas internas": SIN_INICIAR,
"Tablero directivo": SIN_INICIAR,
"Reporte operativo": SIN_INICIAR,
"Endurecimiento de seguridad": ("03/09/2026", "03/09/2026"),
"Documentación técnica": ("03/09/2026", "03/09/2026"),
"Sesión de capacitación": SIN_INICIAR,
"Soporte posterior": SIN_INICIAR,
}
FILAS = {
"Demostración semanal": (0.6, "🟡 En curso · el seguimiento pasó a Pedro (sesiones 'Seguimiento PAF' "
"21, 24 y 26-ago; 3, 9 y 10-sep) · corte de horas al 16-sep, publicado en "
"Google Sheets", A_FILL, A_FONT, False),
}
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)
wb = openpyxl.load_workbook(SRC)
ws = wb.active
ws["A2"] = ("Plan y avance al 16-sep-2026 · plataforma publicada en paputec.mx (provisional), cotización real "
"desde el ticket (individual y en lote); prefactura por API bloqueada por BIND · horas del "
"10-ago al 16-sep reconstruidas, pendientes de validar")
ws["A2"].font = Font(size=10, italic=True, color=GRIS)
ws.cell(row=4, column=2, value="Inicio (real)")
ws.cell(row=4, column=3, value="Fin (real)")
# Notas del corte anterior: se limpian antes del bucle y se reescriben abajo.
primera_nota = None
for row in range(5, ws.max_row + 1):
v = str(ws.cell(row=row, column=1).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=1).value = None
tope = primera_nota if primera_nota else ws.max_row + 1
aplicado = set()
fechas_aplicadas = set()
for row in range(5, tope):
nombre = str(ws.cell(row=row, column=1).value or "").strip()
if not nombre:
continue
for prefijo, (ini, fin) in FECHAS_REALES.items():
if nombre.startswith(prefijo):
for col, val in ((2, ini), (3, fin)):
cel = ws.cell(row=row, column=col, value=val)
cel.alignment = Alignment(horizontal="center", vertical="center")
if val == "—":
cel.font = Font(size=9, color=GRIS)
fechas_aplicadas.add(prefijo)
break
for prefijo, (trab, pct) in ENCABEZADOS.items():
if nombre.startswith(prefijo):
c = ws.cell(row=row, column=C_TRAB, value=round(trab, 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")
break
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.add(prefijo)
break
for prefijo, (pct, txt, fill, fcolor, bold) in FILAS.items():
if nombre.startswith(prefijo):
p = ws.cell(row=row, column=C_PCT, value=pct)
p.number_format = "0%"
pinta(p, A_FILL, A_FONT, bold=True, size=10, align="center")
s = ws.cell(row=row, column=C_STAT, value=txt)
pinta(s, fill, fcolor, bold)
break
faltan = set(DELTAS_TRAB) - aplicado
assert not faltan, f"Filas no encontradas en el Excel: {faltan}"
faltan_fechas = set(FECHAS_REALES) - fechas_aplicadas
assert not faltan_fechas, f"Filas sin fecha real aplicada: {faltan_fechas}"
notas = [
("✅ Completada · 🟡 En curso · 🔴 Pendiente/bloqueada · ⚪ Próxima · "
"(adelantada) = ejecutada antes de su fecha plan", True),
(f"Horas trabajadas al 16-sep: Etapa 0 {E0:.2f} h (cerrada y validada) · Etapa 1 {E1:.2f} h "
f"(rango 32–39) · Etapa 2 {E2:.2f} h · ajuste Jira {AJ:.2f} h (de 7–10 aprobadas) · "
f"Etapa 3 {E3:.2f} h · total {TOTAL:.2f} h. Las {desde_6ago:.2f} h posteriores al corte del 6-ago "
"están reconstruidas con evidencia (commits, correos, informes de reunión) y pendientes de "
"validar. Base de facturación a $600/h + IVA.", True),
("Etapa 1: la capa de escritura quedó implementada (cotización y prefactura con dry-run, "
"confirmación humana y auditoría). Solo Azure y CI/CD siguen dependiendo de Balam; mientras "
"tanto la plataforma está publicada en https://paputec.mx/balam/ desde el 3-sep (BIND y Jira "
"en solo lectura fuera de sesión).", False),
("Integración Jira → BIND: primera cotización real desde un ticket el 21-ago (folio 1026, tras "
"5 intentos por dos semánticas indocumentadas del API: Type=2 y VAT como tasa), tres tickets "
"cotizados en la sesión del 3-sep y cotización en lote desde el 7-sep. Rebasa las 7–10 h "
"aprobadas por las semánticas indocumentadas y las sesiones acompañadas (12, 21-ago, 3-sep).", False),
("Etapa 2: la prefactura por API NO es posible con lo que BIND expone (26-ago: IsFiscalInvoice=true "
"timbra al crear —CFDI 2386, a cancelar— y false produce remisión; el tipo Prefactura solo "
"existe en su UI). Sesiones del 9 y 10-sep con Araceli y Pedro para capturar el endpoint "
"interno y decidir el camino (réplica, detección al timbrado o soporte de BIND).", False),
("En espera de Balam: acceso a Azure (requisitos enviados el 25-ago), cancelación del CFDI 2386 "
"y de la cotización 1026, y la definición del camino de prefactura.", False),
("Fechas: Inicio y Fin son REALES (primer y último día trabajado según el registro de horas, la "
"bitácora y los commits); las tareas en curso llevan como Fin el último día trabajado y '—' "
"significa sin iniciar. El calendario plan original está en el corte del 6-ago.", False),
]
r = primera_nota if primera_nota else ws.max_row + 2
for txt, bold in notas:
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")
try:
ws.merge_cells(start_row=r, start_column=1, end_row=r, end_column=7)
except ValueError:
pass
ws.row_dimensions[r].height = 40 if len(txt) > 220 else (28 if len(txt) > 120 else 14)
r += 1
ws.print_area = f"A1:G{r}"
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) · avance global {PCT_GLOBAL:.0%}")
+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()))
@@ -0,0 +1,359 @@
"""Genera Avance-Balam-2026-07-28.xlsx — plan y avance COMPLETO para Erika.
Pedido por Erika el 27-28 jul: el conteo de horas de cada etapa (base de la
facturación) en el mismo formato de los exceles anteriores, extendido a las
Etapas 2 y 3 desglosadas por actividades diarias.
Etapas 0-1 (hechas/en curso) se leen del Excel interno
(`Plan-actividades-avance-2026-07-27.xlsx`), que a su vez sale de
`Seguimiento-horas.csv` — una sola fuente de datos. Las Etapas 2-3 y el ajuste
Jira → BIND se planifican aquí con los rangos contractuales de la propuesta
v1.3 (§2 y §4): E2 38–46 h · E3 24–29 h · Jira 7–10 h · total 119–146 h.
"""
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-28.xlsx")
# 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"
thin = Side(style="thin", color=BORDE)
box = Border(left=thin, right=thin, top=thin, bottom=thin)
# Columnas del origen (interno 27-jul)
O_NOMBRE, O_INI, O_FIN, O_PCT, O_EST, O_TRAB, O_STAT = 1, 3, 4, 7, 9, 10, 11
# Estatus del interno que quedaron amarrados a la "sesión del 28-jul" —
# la sesión se movió a revisión interna de Balam (REGISTRO #58).
REEMPLAZOS_STAT = {
"🔴 Diseño listo · impl. en pausa (def. Balam 28-jul)":
"🔴 Diseño listo · impl. en pausa (flujo en revisión interna de Balam)",
"🟡 Conectividad lista · falta regla 28-jul":
"🟡 Conectividad lista · falta regla (revisión interna de Balam)",
}
# Los rangos por actividad del interno sumaban 34–42 h en Etapa 1, fuera del
# rango contractual de 32–39 (tope de facturación $23,400). Se recortan las
# tres actividades cuyas horas reales quedaron muy por debajo del estimado,
# para que la descomposición sume exactamente 32–39.
REEMPLAZOS_EST = {
"Sincronización de clientes": "1.5–2", # antes 2.5–3 (real: 0.5)
"Sincronización de facturas y cotizaciones": "4–4.5", # antes 4.5–5.5 (real: 0.5)
"Modelo de facturas + migraciones + datos de prueba": "3–3.5" # antes 3.5–4.5 (real: 0.5)
}
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 ""
# --- Lee Etapas 0-1 del interno ----------------------------------------------
wsrc = openpyxl.load_workbook(SRC).active
hechas = [] # filas E0/E1 (encabezados de etapa + actividades)
trab_por_etapa = {}
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
if nombre.startswith("Integracion"):
trab_por_etapa["TOTAL"] = float(wsrc.cell(row=row, column=O_TRAB).value or 0)
continue
es_etapa = nombre.startswith("Etapa")
if es_etapa:
trab_por_etapa[nombre] = float(wsrc.cell(row=row, column=O_TRAB).value or 0)
stat = str(wsrc.cell(row=row, column=O_STAT).value or "").strip()
est = str(wsrc.cell(row=row, column=O_EST).value or "")
hechas.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,
"est": REEMPLAZOS_EST.get(nombre, est),
"trab": wsrc.cell(row=row, column=O_TRAB).value,
"stat": REEMPLAZOS_STAT.get(stat, stat),
"etapa": es_etapa,
})
E0, E1 = trab_por_etapa["Etapa 0"], trab_por_etapa["Etapa 1"]
TOTAL = trab_por_etapa["TOTAL"]
assert abs((E0 + E1) - TOTAL) < 0.01, f"Horas no cuadran: {E0}+{E1} != {TOTAL}"
def suma_est(filas):
lo = hi = 0.0
for f in filas:
est = f["est"] if isinstance(f, dict) else f
if not est or est == "—":
continue
p = est.replace("–", "-").split("-")
lo += float(p[0])
hi += float(p[-1])
return lo, hi
# Las actividades de E0 y E1 deben sumar exactamente su rango contractual.
_secciones = {}
_actual = None
for f in hechas:
if f["etapa"]:
_actual = f["nombre"]
else:
_secciones.setdefault(_actual, []).append(f)
assert suma_est(_secciones["Etapa 0"]) == (18, 22), \
f"Etapa 0: actividades suman {suma_est(_secciones['Etapa 0'])} != (18, 22)"
assert suma_est(_secciones["Etapa 1"]) == (32, 39), \
f"Etapa 1: actividades suman {suma_est(_secciones['Etapa 1'])} != (32, 39)"
# --- Plan de Etapas 2-3 y ajuste Jira (actividades diarias) -------------------
PROX = "⚪ Próxima"
PROX_FLUJO = "⚪ Próxima · requiere flujo de facturación definido"
PROX_JIRA = "⚪ Próxima · sujeta a aprobación (v1.3) y flujo definido"
# (nombre, ini, fin, est_min, est_max, estatus)
ETAPA2 = ("Etapa 2 — Facturación (consulta y emisión), catálogo y multimoneda",
"29/07/2026", "12/08/2026", 38, 46, [
("Andamiaje del frontend (Angular): proyecto, login y guardias por rol",
"29/07/2026", "30/07/2026", "4–5", PROX),
("Layout base con la identidad de marca; gestión de estado y caché",
"30/07/2026", "31/07/2026", "3–4", PROX),
("Listado de facturas: filtros, búsqueda, paginación y ordenamiento",
"31/07/2026", "03/08/2026", "6–7", PROX),
("Detalle de factura con descarga de PDF/XML desde BIND",
"04/08/2026", "04/08/2026", "3–4", PROX),
("Catálogo de clientes con lista blanca configurable (editable por rol Administración)",
"05/08/2026", "05/08/2026", "3–4", PROX),
("Tipo de cambio del DOF: proceso programado y captura al facturar",
"06/08/2026", "06/08/2026", "2", PROX),
("Creación de cotizaciones desde la plataforma (con simulación dry-run)",
"06/08/2026", "07/08/2026", "5–6", PROX_FLUJO),
("Conversión cotización→factura con confirmación humana (MXN con IVA / USD sin IVA)",
"10/08/2026", "11/08/2026", "5–6", PROX_FLUJO),
("Exportación de facturas y clientes a CSV/XLSX",
"11/08/2026", "11/08/2026", "2", PROX),
("Pruebas integrales del flujo de emisión + manual de usuario",
"12/08/2026", "12/08/2026", "5–6", PROX),
])
ETAPA3 = ("Etapa 3 — Cobranza, tablero, reportes y cierre",
"13/08/2026", "21/08/2026", 24, 29, [
("Módulo de cobranza: antigüedad de cartera 30/60/90 y vistas por vencer/vencidas",
"13/08/2026", "14/08/2026", "5–6", PROX),
("Alertas internas configurables + aplicación de la lista blanca",
"14/08/2026", "17/08/2026", "4–5", PROX),
("Tablero directivo con indicadores de cuentas por cobrar",
"17/08/2026", "18/08/2026", "4–5", PROX),
("Reporte operativo configurable (frecuencia, destinatarios, CSV/XLSX/PDF)",
"19/08/2026", "19/08/2026", "3–4", PROX),
("Endurecimiento de seguridad, secretos, respaldos y plan de recuperación",
"20/08/2026", "20/08/2026", "3–4", PROX),
("Documentación técnica: README, runbook y ADRs",
"20/08/2026", "21/08/2026", "2", PROX),
("Sesión de capacitación grabada + transferencia y cierre",
"21/08/2026", "21/08/2026", "3", PROX),
("Soporte posterior al lanzamiento (2 semanas, corrección de defectos — sin costo)",
"24/08/2026", "04/09/2026", "—", "⚪ Incluida en el cierre · sin costo"),
])
JIRA = ("» Ajuste dentro de la Etapa 2: Integración Jira → BIND (en aprobación, v1.3)",
"27/07/2026", "29/07/2026", 7, 10, [
("Cliente de la API de Jira (token PAF): lectura de tickets FAC y control de límites",
"27/07/2026", "27/07/2026", "2–3", PROX_JIRA),
("Disparador por estatus → generación automática de la cotización en BIND",
"28/07/2026", "28/07/2026", "3–4", PROX_JIRA),
("Trazabilidad ticket↔cotización↔factura, alimentación del tablero y prueba observada",
"29/07/2026", "29/07/2026", "2–3", PROX_JIRA),
])
def suma_rangos(acts):
lo = hi = 0
for _, _, _, est, _ in acts:
if est == "—": # filas sin costo (soporte incluido) no suman
continue
p = est.split("–")
lo += float(p[0])
hi += float(p[-1])
return lo, hi
for titulo, _, _, lo, hi, acts in (ETAPA2, ETAPA3, JIRA):
slo, shi = suma_rangos(acts)
assert (slo, shi) == (lo, hi), f"{titulo}: actividades {slo}–{shi} != rango {lo}–{hi}"
TOT_MIN = 18 + 32 + ETAPA2[3] + ETAPA3[3] + JIRA[3] # 119
TOT_MAX = 22 + 39 + ETAPA2[4] + ETAPA3[4] + JIRA[4] # 146
PCT_GLOBAL = round(TOTAL / ((TOT_MIN + TOT_MAX) / 2), 2)
# --- Construcción -------------------------------------------------------------
wb = openpyxl.Workbook()
ws = wb.active
ws.title = "Plan y avance"
ws["A1"] = "Proyecto Balam · Plataforma de Automatización Financiera"
ws["A1"].font = Font(bold=True, size=14, color=CAFE)
ws["A2"] = ("Plan y avance al 28-jul-2026 · horas trabajadas al corte del lunes 27-jul · "
"Etapas 2-3 y ajuste Jira desglosados por actividad diaria")
ws["A2"].font = Font(size=10, italic=True, color=GRIS)
ENC = ["Actividad", "Inicio", "Fin", "Avance", "Horas\nestimadas", "Horas\ntrabajadas", "Estatus"]
ANCHOS = [56, 12, 12, 9, 11, 11, 40]
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", wrap_text=True)
c.border = box
ws.column_dimensions[get_column_letter(i)].width = ancho
ws.row_dimensions[FILA_ENC].height = 26
def pinta_fila(r, nombre, ini, fin, pct, est, trab, stat, etapa=False, fill=None):
c = ws.cell(row=r, column=1, value=nombre)
c.font = Font(bold=etapa, size=10, color=CAFE)
c.alignment = Alignment(vertical="center", wrap_text=True, indent=0 if etapa else 1)
for col, val in ((2, ini), (3, fin)):
cc = ws.cell(row=r, column=col, value=val)
cc.font = Font(size=9, color=GRIS)
cc.alignment = Alignment(horizontal="center", vertical="center")
cp = ws.cell(row=r, column=4, value=pct)
cp.number_format = "0%"
cp.alignment = Alignment(horizontal="center", vertical="center")
if pct >= 1:
cp.font = Font(bold=True, size=10, color=V_FONT)
cp.fill = PatternFill("solid", fgColor=V_FILL)
elif 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)
ce = ws.cell(row=r, column=5, value=est)
ce.font = Font(bold=etapa, size=10, color=CAFE)
ce.alignment = Alignment(horizontal="center", vertical="center")
ct = ws.cell(row=r, column=6, value=trab if trab not in (None, "") else "—")
if isinstance(trab, (int, float)):
ct.number_format = "0.00"
ct.font = Font(bold=etapa, size=10, color=CAFE)
ct.alignment = Alignment(horizontal="center", vertical="center")
cs = ws.cell(row=r, column=7, value=stat)
cs.alignment = Alignment(horizontal="left", vertical="center", wrap_text=True)
cs.font = Font(size=9, color=GRIS)
if stat.startswith("✅"):
cs.font = Font(size=9, color=V_FONT); cs.fill = PatternFill("solid", fgColor=V_FILL)
elif stat.startswith("🟡"):
cs.font = Font(size=9, color=A_FONT); cs.fill = PatternFill("solid", fgColor=A_FILL)
elif stat.startswith("🔴"):
cs.font = Font(size=9, bold=True, color=R_FONT); cs.fill = PatternFill("solid", fgColor=R_FILL)
for col in range(1, 8):
ws.cell(row=r, column=col).border = box
if etapa and col in (1, 2, 3):
ws.cell(row=r, column=col).fill = PatternFill("solid", fgColor=fill or CREMA)
r = FILA_ENC + 1
pinta_fila(r, "Integracion de plataformas Balam", "01/07/2026", "21/08/2026",
PCT_GLOBAL, f"{TOT_MIN:.0f}–{TOT_MAX:.0f}", TOTAL, "", etapa=True)
r += 1
for f in hechas:
pinta_fila(r, f["nombre"], f["ini"], f["fin"], f["pct"], f["est"], f["trab"],
f["stat"], etapa=f["etapa"])
r += 1
titulo, ini, fin, lo, hi, acts = ETAPA2
pinta_fila(r, titulo, ini, fin, 0, f"{lo}–{hi}", None, "", etapa=True)
r += 1
# El ajuste Jira se pinta ANIDADO justo debajo del encabezado de Etapa 2 (mismo
# lugar en la propuesta) y ANTES de las actividades propias de la Etapa 2: sus
# 3 días (27-29 jul) son cronológicamente anteriores al arranque de la Etapa 2
# (29 jul), así que leer de arriba a abajo también respeta el calendario.
# Relleno ámbar "pendiente" en vez del crema de una etapa contractual, y
# prefijo "»" para que no se lea como una 5ª etapa.
jtitulo, jini, jfin, jlo, jhi, jacts = JIRA
pinta_fila(r, jtitulo, jini, jfin, 0, f"{jlo}–{jhi}", None, "", etapa=True, fill=A_FILL)
r += 1
for nombre, aini, afin, est, stat in jacts:
pinta_fila(r, nombre, aini, afin, 0, est, None, stat)
r += 1
for nombre, aini, afin, est, stat in acts:
pinta_fila(r, nombre, aini, afin, 0, est, None, stat)
r += 1
titulo, ini, fin, lo, hi, acts = ETAPA3
pinta_fila(r, titulo, ini, fin, 0, f"{lo}–{hi}", None, "", etapa=True)
r += 1
for nombre, aini, afin, est, stat in acts:
pinta_fila(r, nombre, aini, afin, 0, est, None, stat)
r += 1
# --- Leyenda y notas ----------------------------------------------------------
r += 1
leyenda = "✅ Completada · 🟡 En curso · 🔴 Pendiente/bloqueada · ⚪ Próxima"
notas = [
(leyenda, False),
(f"Horas trabajadas al corte del lunes 27-jul: {E0:.2f} h en Etapa 0 (cerrada y "
f"validada, rango 18–22 h) y {E1:.2f} h en Etapa 1 ({TOTAL:.2f} h en total). "
"Las horas por etapa son la base de la facturación (tarifa $600/h + IVA).", True),
("Etapas 2 y 3 desglosadas por actividad diaria con sus rangos estimados "
"(38–46 h y 24–29 h). Las fechas planificadas asumen media jornada en promedio "
"y están sujetas a la definición del flujo de facturación (en revisión interna "
"de Balam) y al acceso a Azure. El soporte post-lanzamiento (2 semanas, sin "
"costo) corre del 24-ago al 4-sep, después del cierre.", False),
("Integración Jira → BIND (7–10 h), señalada con » dentro de la Etapa 2: "
"ajuste no contemplado en la propuesta original, incorporado en la propuesta "
"ajustada v1.3; se ejecuta tras su aprobación por escrito y una vez definido "
"el flujo. Se agrupa junto a la Etapa 2 porque automatiza el mismo entregable "
"(creación de cotizaciones); se programó a partir del lunes 27-jul —día en que "
"Pedro entregó el token PAF— en paralelo con el cierre de la Etapa 1 y el "
"arranque de la Etapa 2.", False),
(f"Total del plan con el ajuste: {TOT_MIN:.0f}–{TOT_MAX:.0f} h. El avance global "
f"({PCT_GLOBAL:.0%}) se calcula sobre el plan completo; el corte anterior (80%) "
"se calculaba solo sobre las Etapas 0-1.", False),
("En Etapa 1 siguen abiertos: capa de escritura (en pausa por la definición del "
"flujo), datos de prueba, y Azure/CI-CD (accesos del lado de Balam).", False),
]
for txt, bold in notas:
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=7)
ws.row_dimensions[r].height = 28 if len(txt) > 120 else 14
r += 1
ws.freeze_panes = f"A{FILA_ENC + 1}"
ws.sheet_view.showGridLines = False
ws.print_area = f"A1:G{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" E0 {E0:.2f} h · E1 {E1:.2f} h · trabajadas {TOTAL:.2f} h · "
f"plan {TOT_MIN:.0f}–{TOT_MAX:.0f} h · avance global {PCT_GLOBAL:.0%} · "
f"filas E2 {len(ETAPA2[5])} · E3 {len(ETAPA3[5])} · Jira {len(JIRA[5])}")
+228
View File
@@ -0,0 +1,228 @@
# Piloto del webhook Jira FAC → plataforma (29-sep-2026). Autorizado por Pedro
# (vía Johann). Cada escritura en Jira es explícita, una por subcomando:
#
# url → lee la URL pública del túnel ngrok local (solo lectura)
# registrar → POST /rest/webhooks/1.0/webhook (ESCRIBE: 1 webhook de sitio)
# crear-ticket → POST /rest/api/3/issue (ESCRIBE: 1 ticket de prueba en FAC)
# crear-cotizacion [CLIENTE] → POST /rest/servicedeskapi/request (ESCRIBE: 1 ticket de
# prueba COMPLETO por el formulario "Cotización Bind", el que sí
# alcanza para armar la vista previa; default ACUNTIA, S.A.U.)
# vista-previa CLAVE [paputec] → espera el resultado de la vista previa automática
# del ticket y lo muestra con su dry-run (solo lectura)
# esperar CLAVE [paputec] → espera la detección en la BD local o, con "paputec",
# en la de producción por ssh (solo lectura)
# borrar-ticket CLAVE → DELETE /rest/api/3/issue/CLAVE (ESCRIBE: borra el ticket de prueba)
# borrar-webhook → DELETE del webhook registrado (reversa total)
# mover URL_BASE → PUT del webhook a otra URL base (ESCRIBE), p. ej.
# https://paputec.mx/balam (el receptor vive en /api/jira/webhook)
# estado → webhooks de sitio actuales (solo lectura)
#
# Estado del piloto: webhook de sitio id 1 registrado el 29-sep apuntando al
# túnel ngrok de Johann; prueba FAC-169 creada, detectada y borrada. El 30-sep
# se movió a https://paputec.mx/balam (solo HMAC, secreto nuevo).
#
# El token de Jira y el secreto del webhook nunca se imprimen ni se versionan:
# token → propuesta/token-jira.txt (gitignoreado) o $JIRA_TOKEN_FILE
# secreto → $JIRA_WEBHOOK_SECRET_FILE (default: ~/jira-webhook-secret.txt)
import base64, json, os, subprocess, sys, time, urllib.request, urllib.error
SITE = "https://balam-jsm-temp.atlassian.net"
EMAIL = "pedro.ayala@balamtalentoestrategico.com"
PEDRO = "712020:0cde8558-7794-468c-9e2c-73dfe63bd57b"
REPO = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
HOME = os.path.expanduser("~")
TOKEN = open(os.environ.get("JIRA_TOKEN_FILE", os.path.join(REPO, "propuesta", "token-jira.txt")), encoding="utf-8").read().strip()
SECRETO = open(os.environ.get("JIRA_WEBHOOK_SECRET_FILE", os.path.join(HOME, "jira-webhook-secret.txt")), encoding="utf-8").read().strip()
AUTH = "Basic " + base64.b64encode(f"{EMAIL}:{TOKEN}".encode()).decode()
ESTADO = os.environ.get("JIRA_WEBHOOK_ESTADO", os.path.join(HOME, "jira-webhook-estado.json"))
def jira(method, path, body=None):
data = json.dumps(body).encode() if body is not None else None
req = urllib.request.Request(SITE + path, data=data, method=method, headers={
"Authorization": AUTH, "Accept": "application/json", "Content-Type": "application/json"})
try:
with urllib.request.urlopen(req, timeout=30) as r:
raw = r.read().decode("utf-8")
return r.status, (json.loads(raw) if raw else None)
except urllib.error.HTTPError as e:
return e.code, e.read().decode("utf-8", "replace")[:500]
def estado(**cambios):
s = json.load(open(ESTADO, encoding="utf-8")) if os.path.exists(ESTADO) else {}
s.update(cambios)
json.dump(s, open(ESTADO, "w", encoding="utf-8"), indent=1)
return s
def url_publica():
for puerto in (4040, 4041, 4042):
try:
with urllib.request.urlopen(f"http://127.0.0.1:{puerto}/api/tunnels", timeout=3) as r:
for t in json.loads(r.read().decode())["tunnels"]:
if t["public_url"].startswith("https://") and "5079" in t["config"]["addr"]:
return t["public_url"]
except Exception:
pass
return None
def sql(consulta, destino="local"):
# Superusuario del docker-compose (local o paputec): ve la tabla pese a RLS.
if destino == "paputec":
# La consulta viaja por stdin: sin comillas que escapar a través de ssh + sudo.
out = subprocess.run(["ssh", "-o", "BatchMode=yes", "johann@paputec.mx",
"sudo docker exec -i balam-postgres psql -U balam -d balam -At -F ' | '"],
input=consulta, capture_output=True, text=True, encoding="utf-8")
else:
out = subprocess.run(["docker", "exec", "balam-postgres-dev", "psql", "-U", "balam", "-d", "balam",
"-At", "-F", " | ", "-c", consulta], capture_output=True, text=True, encoding="utf-8")
return out.stdout.strip() or out.stderr.strip()
cmd = sys.argv[1] if len(sys.argv) > 1 else "estado"
if cmd == "url":
print(url_publica() or "Sin túnel ngrok hacia 5079.")
elif cmd == "registrar":
base = url_publica()
if not base:
sys.exit("Sin túnel ngrok hacia 5079: no se registra nada.")
cuerpo = {
"name": "Balam plataforma · FAC ticket creado (piloto 29-sep)",
"url": f"{base}/api/jira/webhook?token={SECRETO}",
"events": ["jira:issue_created"],
"filters": {"issue-related-events-section": "project = FAC"},
"excludeBody": False,
"secret": SECRETO,
}
st, r = jira("POST", "/rest/webhooks/1.0/webhook", cuerpo)
if st == 400 and "secret" in str(r).lower():
cuerpo.pop("secret")
st, r = jira("POST", "/rest/webhooks/1.0/webhook", cuerpo)
if st in (200, 201) and isinstance(r, dict):
wid = r.get("self", "").rstrip("/").split("/")[-1]
estado(webhook_id=wid, webhook_url_base=base, registrado=time.strftime("%Y-%m-%d %H:%M:%S"),
con_secreto_hmac="secret" in cuerpo)
print(f"[{st}] webhook registrado · id={wid} · eventos={r.get('events')} · filtro={r.get('filters')} · "
f"secreto HMAC enviado={'secret' in cuerpo}")
else:
print(f"[{st}] {r}")
elif cmd == "crear-ticket":
adf = {"type": "doc", "version": 1, "content": [{"type": "paragraph", "content": [{"type": "text", "text":
f"Ticket de PRUEBA de la plataforma Balam ({time.strftime('%d/%m/%Y')}), autorizado por Pedro Ayala, para validar "
"que la plataforma detecta la creación de tickets por webhook. Se elimina en cuanto se confirma."}]}]}
st, r = jira("POST", "/rest/api/3/issue", {"fields": {
"project": {"key": "FAC"}, "issuetype": {"id": "10073"},
"summary": "[PRUEBA PLATAFORMA BALAM] Detección por webhook — se borra en minutos",
"description": adf, "assignee": {"accountId": PEDRO}}})
if st == 201:
estado(ticket=r["key"], ticket_id=r["id"], creado=time.strftime("%Y-%m-%d %H:%M:%S"))
print(f"[201] ticket de prueba creado: {r['key']} (id {r['id']})")
else:
print(f"[{st}] {r}")
elif cmd == "crear-cotizacion":
# Formulario "Cotización Bind" (service desk 35, request type 1196): todos sus
# campos son obligatorios. Datos de prueba fijos y pequeños; el reporter es
# el dueño del token (Pedro), que recibe el correo de confirmación.
cliente = sys.argv[2] if len(sys.argv) > 2 else "ACUNTIA, S.A.U."
vence = time.strftime("%Y-%m-%d", time.localtime(time.time() + 7 * 86400))
st, r = jira("POST", "/rest/servicedeskapi/request", {
"serviceDeskId": "35", "requestTypeId": "1196",
"requestFieldValues": {
"summary": "[PRUEBA PLATAFORMA BALAM] Vista previa automática de cotización — se borra en minutos",
"description": f"Ticket de PRUEBA de la plataforma Balam ({time.strftime('%d/%m/%Y')}), autorizado por Johann, "
"para validar la vista previa automática de la cotización. Se elimina en cuanto se confirma.",
"customfield_11423": {"value": "Balam"}, # Empresa origen
"customfield_11598": {"value": cliente}, # Cliente
"customfield_11599": {"value": "Servicios Profesionales Generales"}, # Servicio/Producto
"customfield_11600": 1, # Cantidad
"customfield_10053": {"value": "MXN"}, # Tipo de Moneda
"customfield_11556": 1234, # Monto sin IVA
"customfield_11601": {"value": "16%"}, # IVA
"customfield_11597": vence, # Fecha de vencimiento
}})
if st == 201 and isinstance(r, dict):
estado(ticket=r["issueKey"], ticket_id=r["issueId"], creado=time.strftime("%Y-%m-%d %H:%M:%S"))
print(f"[201] ticket de prueba completo creado: {r['issueKey']} (id {r['issueId']}) · cliente {cliente}")
else:
print(f"[{st}] {r}")
elif cmd == "vista-previa":
clave = sys.argv[2]
destino = sys.argv[3] if len(sys.argv) > 3 else "local"
for i in range(60):
fila = sql(f"""SELECT e."Id", e."IssueKey", e."Verified", e."AuthMethod",
round(extract(epoch from (e."ReceivedAtUtc"-e."JiraTimestampUtc"))::numeric,2),
e."PreviewStatus", round(extract(epoch from (e."PreviewAtUtc"-e."JiraTimestampUtc"))::numeric,2),
coalesce(e."PreviewOperationId"::text,''), coalesce(e."PreviewDetail",'')
FROM "JiraTicketEvents" e WHERE e."IssueKey"='{clave}' AND e."PreviewStatus" IS NOT NULL""", destino)
if fila and "|" in fila:
print(f"VISTA PREVIA tras ~{i}s de espera: id | llave | verificado | auth | detección s | estado | lista a los s | operación | detalle")
print(" " + fila)
op = fila.split(" | ")[7].strip()
if op:
print(" dry-run guardado (WriteOperations): estatus | tipo | autor | confirmada")
print(" " + sql(f"""SELECT "Status", "Type", "CreatedBy", "ConfirmedAtUtc" IS NOT NULL
FROM "WriteOperations" WHERE "Id"={int(op)}""", destino))
print(sql(f"""SELECT "PayloadJson" FROM "WriteOperations" WHERE "Id"={int(op)}""", destino))
break
time.sleep(1)
else:
print(f"Sin vista previa de {clave} en 60 s.")
elif cmd == "esperar":
clave = sys.argv[2]
destino = sys.argv[3] if len(sys.argv) > 3 else "local"
for i in range(60):
fila = sql(f"""SELECT "Id","IssueKey","Verified","AuthMethod","JiraStatus",
to_char("JiraTimestampUtc" AT TIME ZONE 'America/Mexico_City','HH24:MI:SS.MS'),
to_char("ReceivedAtUtc" AT TIME ZONE 'America/Mexico_City','HH24:MI:SS.MS'),
round(extract(epoch from ("ReceivedAtUtc"-"JiraTimestampUtc"))::numeric,2),
coalesce("Error",'') FROM "JiraTicketEvents" WHERE "IssueKey"='{clave}'""", destino)
if fila and "|" in fila:
print(f"DETECTADO tras ~{i}s de espera: id | llave | verificado | auth | estatus Jira | emitido (MX) | recibido (MX) | retraso s | error")
print(" " + fila)
break
time.sleep(1)
else:
print(f"Sin detección de {clave} en 60 s.")
elif cmd == "borrar-ticket":
clave = sys.argv[2]
st, r = jira("DELETE", f"/rest/api/3/issue/{clave}")
st2, _ = jira("GET", f"/rest/api/3/issue/{clave}")
estado(ticket_borrado=clave if st == 204 else None, borrado=time.strftime("%Y-%m-%d %H:%M:%S"))
print(f"[{st}] DELETE {clave} · verificación GET → {st2} (404 = ya no existe)")
elif cmd == "borrar-webhook":
wid = estado().get("webhook_id")
st, r = jira("DELETE", f"/rest/webhooks/1.0/webhook/{wid}")
print(f"[{st}] DELETE webhook {wid}")
elif cmd == "mover":
# Destino estable (paputec): solo HMAC, sin el secreto en la URL.
base = sys.argv[2].rstrip("/")
wid = estado().get("webhook_id") or "1"
st, r = jira("PUT", f"/rest/webhooks/1.0/webhook/{wid}", {
"name": "Balam plataforma · FAC ticket creado",
"url": f"{base}/api/jira/webhook",
"events": ["jira:issue_created"],
"filters": {"issue-related-events-section": "project = FAC"},
"excludeBody": False,
"secret": SECRETO,
})
if st in (200, 201):
estado(webhook_id=wid, webhook_url_base=base, movido=time.strftime("%Y-%m-%d %H:%M:%S"))
print(f"[{st}] webhook {wid} → {base}/api/jira/webhook" + ("" if st in (200, 201) else f" · {r}"))
elif cmd == "estado":
st, r = jira("GET", "/rest/webhooks/1.0/webhook")
print(f"[{st}] webhooks de sitio: {len(r) if isinstance(r, list) else r}")
for w in r if isinstance(r, list) else []:
print(f" - {w.get('name')} · habilitado={w.get('enabled')} · eventos={w.get('events')} · filtro={w.get('filters')}")
print("estado local:", json.dumps(estado(), ensure_ascii=False))
Binary file not shown.

After

Width:  |  Height:  |  Size: 114 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 124 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 112 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 120 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 238 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 258 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 119 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 129 KiB

Some files were not shown because too many files have changed in this diff Show More