Compare commits

...

10 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
85 changed files with 16989 additions and 23 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.
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": {}
}
+69 -8
View File
@@ -2,13 +2,71 @@
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-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)). 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)._
_Ú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)._
## ⭐ 30-sep — webhook de Jira en producción ([REGISTRO #85](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`)
- [ ] 🔧 **Siguiente meta:** con cada `JiraTicketEvent` verificado, armar la **vista previa (dry-run) de la cotización** en BIND y marcar el ticket "lista para revisar" en la bandeja. La confirmación sigue siendo humana (ADR-001). ⚠️ El prellenado resuelve moneda, ubicación, almacén y servicio con los catálogos del API público de BIND (`/api/emision/catalogos`), que hoy responde 500: se puede construir y probar con `FakeHandler`, pero no validar contra BIND hasta que vuelva.
- [ ] ⚠️ **Producción corre con `Bind__Mode=Write`** (así estaba en `/opt/balam/.env`; el contenedor anterior llevaba 3 semanas arriba). **Johann decidió dejarlo así el 30-sep.** El runbook y el `CLAUDE.md` de la plataforma siguen diciendo que producción corre en ReadOnly: falta alinear esa documentación con la decisión, o regresarlo a ReadOnly 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 MacBook donde 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`.
- [ ] 🔑 **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.
@@ -32,13 +90,13 @@ _Última actualización: **2026-09-30** — ⭐ el webhook de Jira FAC queda **e
- [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.
- [ ] 🔧 **Siguiente paso de la meta:** vista previa (dry-run) de la cotización por cada detección → pasa al bloque del 30-sep, arriba.
- [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).
- [ ] 🔥 **API público de BIND en 500 en todos los endpoints** (key de Arturo) — sync detenido desde el 10-sep. Pedir a Balam la API key del usuario `CTO` o revisar el acceso de Arturo.
- [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` (hoy `CFDI_Use` puede heredarse de la factura de OTRO cliente) y que el wizard no muestre G03/16% cuando el puente manda lo de BIND.
- [ ] 🔧 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)
@@ -46,6 +104,9 @@ _Última actualización: **2026-09-30** — ⭐ el webhook de Jira FAC queda **e
- [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))
+223 -3
View File
@@ -1803,13 +1803,13 @@ La bandeja permite marcar varios tickets (solo los que aún no tienen cotizació
---
## 85 · Sep 30, 2026 — madrugada (≈12:10–12:45 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 ⭐
## 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: solo los dos mensajes benignos del runbook. **El puente a la UI de BIND queda apagado en producción** (no hay `Bind__Ui__*` en el `.env`; default `Enabled=false`).
**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.
@@ -1833,11 +1833,231 @@ La bandeja permite marcar varios tickets (solo los que aún no tienen cotizació
---
## 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)); en paputec el puente sigue apagado. **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)). **Siguiente hito:** con cada detección, armar la vista previa (dry-run) de la cotización y marcar el ticket "lista para revisar"; la confirmación sigue siendo humana (ADR-001). **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:** API público de BIND en 500 con la key de Arturo (sync detenido desde el 10-sep), prefactura 2467 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.
**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.
+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.
@@ -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.
+31 -9
View File
@@ -51,14 +51,15 @@ fecha,semana,etapa,actividad_id,actividad,horas,estado_hora,fuente_evidencia,fac
2026-08-11,2026-W33,1,E1-05,"Bitácora #62–#63 (merge de las definiciones, respuesta de Noé) y sondeo nocturno de FAC (molde inexistente, FAC-98)",0.75,reconstruida,bitacora/REGISTRO.md#63,si,"commits f28fcc5, a2f338f; por validar"
2026-08-11,2026-W33,2,E2-02,"Capa de escritura mínima ticket→cotización: Quotes_NewQuote en BindClient, POST /api/emision/cotizacion con dry-run/confirmación, pantalla 'Generar cotización en BIND' y script para Balam (73/73 pruebas)",2.50,reconstruida,bitacora/REGISTRO.md#64,si,"commits e854eda y a99eb8b (cliente, 21:28), 2fb230e (21:30); sesión nocturna; por validar"
2026-08-12,2026-W33,2,E2-02,"Madrugada y previo a la sesión: coreografía de la demo (Actualizar sin caché, Cotizar por fila, vista previa automática), sin reintentos en escrituras, tipo de cambio, prefill y selector de cliente",2.00,reconstruida,bitacora/REGISTRO.md#64,si,"commits eafa49b 00:34, 1b062d2 01:33, 2be0432/8228c40 06:55 (cliente); por validar"
2026-08-12,2026-W33,2,AJ-01,"Sesión acompañada de escritura Jira→BIND con Noé, Araceli y Pedro: formulario construido en vivo, decisión de que la plataforma cotiza, primer POST con 500",1.00,confirmada,fuentes/2026-08-12 - Transcript - Sesion prueba de escritura Jira-BIND.txt,si,"Transcript de 52 min; REGISTRO #65; relevo de Erika por Pedro"
2026-08-12,2026-W33,2,AJ-01,"Excel de campos y catálogos de la cotización BIND (67 servicios, 3 productos, 17 clientes) y correo a Pedro (CC Ara, Noé)",0.75,reconstruida,Cotizacion-BIND-campos-y-catalogos.xlsx,si,"correo 8:09 am; REGISTRO #66; por validar"
2026-08-12,2026-W33,2,E2-02,"Sesión acompañada de escritura Jira→BIND con Noé, Araceli y Pedro: formulario construido en vivo, decisión de que la plataforma cotiza, primer POST con 500",1.00,confirmada,fuentes/2026-08-12 - Transcript - Sesion prueba de escritura Jira-BIND.txt,si,"Transcript de 52 min; REGISTRO #65; relevo de Erika por Pedro; reclasificada de AJ-01 a E2-02 el 2-oct por Johann: es cotización contra el API de BIND, no integración con Jira"
2026-08-12,2026-W33,2,E2-02,"Excel de campos y catálogos de la cotización BIND (67 servicios, 3 productos, 17 clientes) y correo a Pedro (CC Ara, Noé)",0.75,reconstruida,Cotizacion-BIND-campos-y-catalogos.xlsx,si,"correo 8:09 am; REGISTRO #66; por validar; reclasificada de AJ-01 a E2-02 el 2-oct por Johann: es cotización contra el API de BIND, no integración con Jira"
2026-08-20,2026-W34,1,E1-05,"Coordinación con Pedro por WhatsApp (18–20 ago): 17 categorías, formulario modificado, ticket FAC-106 y convocatoria del 21",0.50,reconstruida,bitacora/REGISTRO.md#67,si,"por validar"
2026-08-20,2026-W34,1,E1-05,"Cruce del buzón contra la bitácora (9 comunicaciones, 2 correcciones), registro #65 desde el transcript y guion de la sesión del 21-ago",2.00,reconstruida,bitacora/REGISTRO.md#65,si,"commits 5d846ec 23:02 → 072a3cd 23:52; por validar"
2026-08-20,2026-W34,2,AJ-01,"Sondeo de FAC-106 por API, diagnóstico del 500 (VATRate como fracción, VAT, PriceListID) y wizard cableado al formulario 'Cotización Bind', verificado e2e (74/74)",1.50,reconstruida,planeacion/Sondeo-FAC-106-y-payload-2026-08-20.md,si,"commits f839d0e 23:41 y eb04e23 23:47 (cliente); por validar"
2026-08-21,2026-W34,2,AJ-01,"Sesión 7:00 am 'Seguimiento - PAF' con Noé, Araceli y Pedro: tres intentos de emisión (500) y pivote a las definiciones",0.25,reconstruida,bitacora/REGISTRO.md#69,si,"Informe Read AI recibido 7:16 am; por validar"
2026-08-21,2026-W34,2,AJ-01,"Investigación posterior: schema del portal, intentos 4–5, PRIMERA COTIZACIÓN REAL (folio 1026), VAT corregido como tasa, registro #69, correo 'Seguimiento FAC-106' a Noé y borrador del correo de causa",3.00,reconstruida,bitacora/REGISTRO.md#69,si,"commits 46fad4c 10:15 (cliente) y d31747d 10:16; correo 7:32 am; por validar"
2026-08-21,2026-W34,2,AJ-01,"Sesión 1:00 pm 'Seguimiento - PAF (2)': homologación de estados del flujo ticket→cotización→prefactura→factura; pendiente prueba de escritura en Jira",0.50,reconstruida,correo Read AI 21-ago 1:34 pm,si,"SIN registro en bitácora; por validar"
2026-08-20,2026-W34,1,E1-05,"Cruce del buzón contra la bitácora (9 comunicaciones incorporadas, 2 correcciones)",1.00,reconstruida,commit 5d846ec 23:02,si,por validar; dividida el 2-oct: la otra hora (registro #65 y guion del 21-ago) pasó a E2-02
2026-08-20,2026-W34,2,E2-02,"Registro #65 de la sesión de cotización del 12-ago desde el transcript y guion de la sesión del 21-ago (aviso previo a Pedro, coreografía de escritura, plan B del 500, 8 preguntas, protocolo si falla)",1.00,reconstruida,"commits a03f81f 23:29, cabe0fe 23:50 y 072a3cd 23:52",si,por validar; dividida el 2-oct de la fila de gestión: documenta y prepara trabajo de cotización (regla 7)
2026-08-20,2026-W34,2,E2-02,"Sondeo de FAC-106 por API, diagnóstico del 500 (VATRate como fracción, VAT, PriceListID) y wizard cableado al formulario 'Cotización Bind', verificado e2e (74/74)",1.50,reconstruida,planeacion/Sondeo-FAC-106-y-payload-2026-08-20.md,si,"commits f839d0e 23:41 y eb04e23 23:47 (cliente); por validar; reclasificada de AJ-01 a E2-02 el 2-oct por Johann: es cotización contra el API de BIND, no integración con Jira"
2026-08-21,2026-W34,2,E2-02,"Sesión 7:00 am 'Seguimiento - PAF' con Noé, Araceli y Pedro: tres intentos de emisión (500) y pivote a las definiciones",0.25,reconstruida,bitacora/REGISTRO.md#69,si,"Informe Read AI recibido 7:16 am; por validar; reclasificada de AJ-01 a E2-02 el 2-oct por Johann: es cotización contra el API de BIND, no integración con Jira"
2026-08-21,2026-W34,2,E2-02,"Investigación posterior: schema del portal, intentos 4–5, PRIMERA COTIZACIÓN REAL (folio 1026), VAT corregido como tasa, registro #69, correo 'Seguimiento FAC-106' a Noé y borrador del correo de causa",3.00,reconstruida,bitacora/REGISTRO.md#69,si,"commits 46fad4c 10:15 (cliente) y d31747d 10:16; correo 7:32 am; por validar; reclasificada de AJ-01 a E2-02 el 2-oct por Johann: es cotización contra el API de BIND, no integración con Jira"
2026-08-21,2026-W34,2,E2-02,Sesión 1:00 pm 'Seguimiento - PAF (2)': homologación de estados del flujo ticket→cotización→prefactura→factura; pendiente prueba de escritura en Jira,0.50,reconstruida,correo Read AI 21-ago 1:34 pm,si,"SIN registro en bitácora; por validar; reclasificada de AJ-01 a E2-02 el 2-oct por Johann: es cotización contra el API de BIND, no integración con Jira"
2026-08-23,2026-W34,2,E2-03,"Camino cotización→prefactura real: Invoices_AddInvoice con candado, POST /api/emision/prefactura, wizard sin simulación (borrador + aprobación real), herencia de CFDIUse/ServiceID/PriceListID desde factura y cliente",3.00,reconstruida,balam-plataforma commits a893fa9..a07fff6,si,"commits 22:15–23:40 del 23-ago (cliente); por validar"
2026-08-24,2026-W35,2,E2-03,"Sesión 7:00 am 'Seguimiento - PAF': validación del flujo cotización→prefactura (MXN/USD, IVA 16 %/0 %) y ajuste en vivo: false = remisión, columna BIND en la bandeja",1.00,reconstruida,correo Read AI 24-ago 7:44 am,si,"commit 77d3767 7:38 (cliente); SIN registro en bitácora; por validar"
2026-08-25,2026-W35,1,E1-05,"Correo a Noé con los requisitos de Azure (suscripción, grupo de recursos, Contributor) y lectura de su explicación remisión vs prefactura",0.50,reconstruida,correo 25-ago 4:54 pm,si,"por validar"
@@ -67,14 +68,35 @@ fecha,semana,etapa,actividad_id,actividad,horas,estado_hora,fuente_evidencia,fac
2026-08-26,2026-W35,2,E2-03,"Contención del incidente: confirmación de prefactura bloqueada (409), candado triple IsFiscalInvoice=false, API en ReadOnly, registro #70",0.75,reconstruida,bitacora/REGISTRO.md#70,si,"commits c5143cf 7:25 y fbb31fc 7:59 (cliente), a48fba0 8:01; por validar"
2026-08-26,2026-W35,2,E2-03,"Investigación de cierre solo lectura: body de Invoices_AddInvoice vs portal y SDK, PDFs de 1000/1001/2384/2386, censo de 58 no fiscales, diff prefactura 2384 vs remisión — adenda al #70",2.50,reconstruida,bitacora/REGISTRO.md#70,si,"commit 2ae6880 21:28; por validar"
2026-08-26,2026-W35,2,E2-03,"Experimentos autorizados en modo Write (folio compartido, 12 sondas de campo, DELETE), dump del APIM (98 ops), script de captura Playwright y adenda 2 al #70",2.50,reconstruida,bitacora/REGISTRO.md#70,si,"commits e7775e0 21:48 (cliente) y db16f62 00:10 del 27-ago; por validar"
2026-09-03,2026-W36,2,AJ-01,"Sesión 7:00 am 'Seguimiento PAF': datos para el tenant de Azure y conceptos de Acuntia (headcount); definición: ítem por código y concepto = título del ticket",0.50,reconstruida,correo Read AI 3-sep 7:28 am,si,"SIN registro en bitácora; por validar"
2026-09-03,2026-W36,2,E2-02,Sesión 7:00 am 'Seguimiento PAF': datos para el tenant de Azure y conceptos de Acuntia (headcount); definición: ítem por código y concepto = título del ticket,0.50,reconstruida,correo Read AI 3-sep 7:28 am,si,"SIN registro en bitácora; por validar; reclasificada de AJ-01 a E2-02 el 2-oct por Johann: es cotización contra el API de BIND, no integración con Jira"
2026-09-03,2026-W36,2,E2-02,"Catálogo de servicios de BIND en la emisión (GET /api/Services), mapeo categoría Jira → código configurable, selector de ítem, y caché de la bandeja invalidada al cotizar (visto en producción)",1.50,reconstruida,balam-plataforma commit ed4c819,si,"archivos 07:46–07:58 y 12:55 del 3-sep; por validar"
2026-09-03,2026-W36,1,E1-09,"Publicación de la plataforma en https://paputec.mx/balam/ como ambiente provisional: Docker (Postgres + API chiseled read-only), Apache/proxy/SPA, deploy.sh reproducible, seeder de admin, migración al arrancar",2.50,reconstruida,balam-plataforma/deploy/paputec/README.md,si,"archivos 08:36–12:57 del 3-sep; commit 03582af; por validar"
2026-09-03,2026-W36,3,E3-01,"Endurecimiento aplicado en la publicación: CSP/HSTS, rate limit por IP, bloqueo de login, contraseñas 12+, secretos solo en .env root, respaldos diarios con retención",1.00,reconstruida,balam-plataforma/deploy/paputec/README.md,si,"commit 03582af; por validar"
2026-09-03,2026-W36,3,E3-02,"Runbook de publicación, seguridad aplicada, operación y rollback (deploy/paputec/README.md)",0.50,reconstruida,balam-plataforma/deploy/paputec/README.md,si,"commit 03582af; por validar"
2026-09-03,2026-W36,2,AJ-01,"Sesión 1:30 pm 'Seguimiento PAF (2)': tres tickets de Pedro convertidos en cotizaciones reales desde la plataforma publicada; prueba con ticket de Linares",0.25,reconstruida,correo Read AI 3-sep 1:51 pm,si,"SIN registro en bitácora; por validar"
2026-09-03,2026-W36,2,E2-02,Sesión 1:30 pm 'Seguimiento PAF (2)': tres tickets de Pedro convertidos en cotizaciones reales desde la plataforma publicada; prueba con ticket de Linares,0.25,reconstruida,correo Read AI 3-sep 1:51 pm,si,"SIN registro en bitácora; por validar; reclasificada de AJ-01 a E2-02 el 2-oct por Johann: es cotización contra el API de BIND, no integración con Jira"
2026-09-07,2026-W37,2,E2-02,"Cotización en lote desde la bandeja: selección de tickets, pantalla cotizar-lote con dry-run y confirmación por ticket en serie, reglas de prellenado compartidas (cotizacion-prefill.ts)",2.00,reconstruida,balam-plataforma commit fbdbd18,si,"archivos 07:27–07:30 del 7-sep; por validar"
2026-09-09,2026-W37,2,E2-03,"Sesión 1:00 pm 'Seguimiento PAF - Prefactura' con Araceli y Pedro: prefactura creada en la UI de BIND ('Generar venta') mientras se capturan las peticiones (InvoiceDetails)",0.50,reconstruida,correo Read AI 9-sep 1:24 pm,si,"SIN registro en bitácora; por validar"
2026-09-09,2026-W37,2,E2-03,"Tras la sesión de la 1:00 pm: análisis de la captura de 'Generar venta' en la UI de BIND, prueba de folio (no se puede sobrescribir) y diseño del puente a la web app de BIND para la prefactura",1.25,reconstruida,transcript de Claude Code 65198403 (17:36–18:53); balam-plataforma/CLAUDE.md §Puente a la UI,si,Corte 2-oct: no entró al corte del 15-sep (el código se subió hasta el 29-sep en 4d6b3d8); por validar
2026-09-10,2026-W37,2,E2-03,"Sesión 1:00 pm con Pedro: análisis de la cotización 1078 (Acuntia) y captura de la petición de red de la prefactura para evaluar su réplica",0.25,reconstruida,correo Read AI 10-sep 1:21 pm,si,"SIN registro en bitácora; por validar"
2026-09-10,2026-W37,2,E2-03,"Preparación de la segunda sesión de captura: lista de lo que hay que recabar en DevTools (Generar venta sobre la 1078, Preserve log, Fetch/XHR)",0.25,reconstruida,transcript de Claude Code 65198403 (12:21),si,Corte 2-oct; por validar
2026-09-10,2026-W37,2,E2-03,"Contrato real de AddInvoiceTransaction (PrevInvoice/FiscalInvoice/IsCFDI + QuoteID + Number), sonda autorizada op #12 (remisión 1004 creada y cancelada), BindUiClient con login de formulario, endpoints prefactura-ui, vista de cotizaciones sin ticket y mensaje a Pedro con las alternativas",1.75,reconstruida,"transcript de Claude Code 65198403 (13:21–13:29, 14:15–15:49, 16:50–16:53); commit 4d6b3d8",si,Corte 2-oct; la sesión con Pedro (1:00–1:21) ya está en su fila; por validar
2026-09-15,2026-W38,1,E1-05,"Corte de horas 6-ago→15-sep (reconstrucción desde commits, correos e informes de reunión), Excel de avance, commits y publicación de ambos repositorios",0.75,reconstruida,planeacion/Seguimiento-horas.csv,si,"sesión asistida por IA; por validar"
2026-09-16,2026-W38,1,E1-05,"Excel de avance publicado en Google Sheets (Drive/Balam vía rclone, enlace compartible) y corte del 16-sep regenerado desde script",0.50,reconstruida,planeacion/generar_avance_2026_09_16.py,si,"sesión asistida por IA; por validar"
2026-09-29,2026-W40,2,E2-03,"Sesión 7:00 am 'Seguimiento PAF - Integración ERP' con Pedro: validación de accesos, Balam entrega login web a BIND con permisos de prefactura y error al cargar la solicitud al cotizar",0.50,reconstruida,correo Read AI 29-sep 7:23 am; REGISTRO #83,si,Corte 2-oct; por validar
2026-09-29,2026-W40,2,E2-03,Cierre de los huecos de bitácora #71–#83 desde el buzón (informes de Read AI y correos del 21-ago al 29-sep) e investigación de cierre con el SDK oficial de BIND (modelo de dos endpoints),0.75,reconstruida,bitacora/REGISTRO.md#71-83; commit 3de2739 08:06,si,"Corte 2-oct; otra máquina, inicio estimado al terminar la sesión (7:23); por validar; movida de E1-05 a E2-03 el 2-oct: documenta las sesiones de prefactura e investiga el SDK de BIND (regla 7)"
2026-09-29,2026-W40,2,E2-03,"Puente de prefactura con el usuario propio CTO: credenciales y API key nueva en user-secrets, app en modo escritura, PRIMERA PREFACTURA REAL desde la plataforma (folio 2467, 16:08), una sesión por usuario, API interno de la web app y contención de la escritura accidental (FillQuote: cotización 1079 Surtida)",1.50,reconstruida,"transcript de Claude Code 65198403 y 02f58158 (10:54–11:21, 13:24–13:39, 15:28–16:26); balam-plataforma/CLAUDE.md",si,"Corte 2-oct; dos sesiones en paralelo, contadas una sola vez; por validar"
2026-09-29,2026-W40,2,AJ-01,"Webhook de Jira FAC: análisis (leer la creación de tickets vs ADR-001), plan de 5 fases, Fase 1 por GET (token de Pedro con ADMINISTER), endpoint POST /api/jira/webhook con HMAC, idempotencia y relectura (38/38), túnel ngrok y piloto e2e: webhook id 1 + FAC-169 detectado en 14.4 s y borrado",1.00,reconstruida,"transcript de Claude Code 02f58158 (15:34–15:41, 17:57–18:14, 18:39–19:08); REGISTRO #84; commit d7238da",si,"Corte 2-oct; incluye plan, bitácora y push de 4d6b3d8 (rebase con 3 conflictos); por validar"
2026-09-29,2026-W40,2,AJ-01,"Cierre de la sesión: push de ambos repos, appsettings.Local portátil de los user-secrets y relevo a la máquina con acceso a paputec",0.50,reconstruida,"transcript de Claude Code 02f58158 (23:47–00:11); commits 8479e96, 5145801, e5912bf",si,Corte 2-oct; por validar
2026-09-30,2026-W40,2,AJ-01,"Webhook en producción (paputec): fusión de las dos bitácoras del 29-sep, publicación de 5145801 (116 pruebas, respaldo y rollback), secreto nuevo, PUT del webhook id 1 y prueba e2e con FAC-170 detectada en 1.05 s",0.50,reconstruida,"bitacora/REGISTRO.md#85; commits e88b5e1 00:19, 9351aa4 00:30, 7e50c71 00:31",si,Corte 2-oct; ≈12:10–12:35 am; por validar
2026-09-30,2026-W40,2,AJ-01,"Vista previa automática de la cotización por cada ticket detectado (cola, trabajador y port del prellenado; 147 pruebas), push y publicación; causa del 500 de BIND: la API key de arturo (la de cto responde 200)",0.50,reconstruida,"bitacora/REGISTRO.md#86; commits 1c37eaa 00:45, 287cb65, 085e903 00:55",si,Corte 2-oct; ≈12:35–1:00 am; por validar
2026-09-30,2026-W40,2,AJ-01,"API key de cto en producción (el sync vuelve tras 20 días), radiografía del formulario contra el catálogo, vista previa validada contra BIND real con FAC-171 y modal en vivo con 'Aceptar', campana y modo automático configurable (170 pruebas)",0.75,reconstruida,"bitacora/REGISTRO.md#87; commits aac7447 01:40, bdafb06 01:42",si,Corte 2-oct; ≈1:00–1:45 am; por validar
2026-09-30,2026-W40,2,AJ-01,"Push y publicación del modal en vivo, con el modo automático permitido en el servidor (sin activar)",0.25,reconstruida,"bitacora/REGISTRO.md#87 adenda; commits 29a354b, 8799348 06:55",si,Corte 2-oct; respaldo de las 6:53 am; por validar
2026-09-30,2026-W40,2,E2-03,"Puente de prefactura encendido en producción, 'Servicio de Enlace' → ítem 106 y relectura completa de cotizaciones (11 con estado atrasado), tras lo que Pedro vio a las 7:26 en el wizard de FAC-173",0.75,reconstruida,bitacora/REGISTRO.md#88,si,Corte 2-oct; 7:50–8:30 am; por validar
2026-09-30,2026-W40,2,E2-03,"Cuatro bugs reales del puente y de los importes (Total en cero, USD mostrado en pesos e IVA doble, post-proceso que escondía la prefactura, duplicados posibles) con pruebas que los reproducen (197), panel bajo la fila y sync de cotizaciones por barrido completo",1.25,reconstruida,"bitacora/REGISTRO.md#88; commits 597c8a2 21:16, 63af3d6 21:29",si,Corte 2-oct; 8:30–9:40 pm; por validar
2026-09-30,2026-W40,2,E2-03,"Credenciales del usuario CTO en el .env de producción (autorización de Johann), push y publicación de 597c8a2 con la migración QuoteSubtotal y verificación del primer ciclo del sync",0.25,reconstruida,"bitacora/REGISTRO.md#88 adenda; commits 4a219b5, 604cb8d 21:49",si,Corte 2-oct; 9:40–9:50 pm; por validar
2026-09-30,2026-W40,2,E2-03,"Timbrado desde la plataforma (POST /api/emision/timbrar-ui: EmitCFDI con dry-run, palabra TIMBRAR y verificación por lectura), estado incierto cerrado en los POST, consecutivo sin la serie B, uso CFDI del mismo cliente, 212 pruebas, recorrido en navegador, publicación y guion de la prueba del 1-oct",0.75,reconstruida,"bitacora/REGISTRO.md#89; commits f5b3743 22:40, 9874547 22:45",si,Corte 2-oct; 10:00–10:45 pm según respaldo (04:40 UTC) y último commit — el título del #89 dice 10:00 pm–12:50 am: Johann no recuerda la hora de cierre (2-oct): se queda la cota de la evidencia
2026-09-30,2026-W40,2,E2-03,"Llamada con Pedro (7:26 am): revisión del wizard de FAC-173 en producción — prefactura deshabilitada, totales en cero, campo de servicios y Arturo en la lista de aprobadores",0.25,validada,bitacora/REGISTRO.md#88,si,Corte 2-oct; ~15 min según Johann (2-oct)
2026-10-01,2026-W40,2,E2-04,Prueba completa con Balam en producción: ticket → cotización → prefactura; el timbrado (prefactura → CFDI) no se intentó y queda por probar,0.25,validada,planeacion/Guion-prueba-completa-2026-10-01.md,si,Corte 2-oct; ~20 min o menos según Johann (2-oct)
2026-10-02,2026-W40,2,E2-03,"Prefacturas en lote (Cotizaciones y bandeja → una prefactura por cotización, en serie, folios consecutivos con el API lento) y timbrado también en el detalle de la factura (213 pruebas, recorrido en navegador); push y publicación",1.00,reconstruida,"bitacora/REGISTRO.md#90; commits 73d1c2a 08:35, 4f3eb99, 4e90543 08:41",si,Corte 2-oct; 7:40–8:40 am; por validar
2026-10-02,2026-W40,1,E1-05,"Corte de horas 16-sep → 2-oct: pull de ambos repos, reconstrucción desde los transcripts de Claude Code, los commits y los informes de Read AI, y Excel de avance",0.75,confirmada,transcript de Claude Code a134588d (11:47–12:25); planeacion/generar_avance_2026_10_02.py,si,"Corte 2-oct; sesión asistida por IA; tiempo real. No se cobran la proyección de costos, la reclasificación ni los ajustes posteriores del Excel (~0.75 h, decisión de Johann 2-oct)"
2026-10-02,2026-W40,2,E2-04,"Manual de usuario en PDF (14 páginas): acceso y roles, consulta, solicitudes de Jira, ticket → cotización (aviso en vivo, pantalla y lote), prefactura (individual, emisión asistida y lote), timbrado, reglas y mensajes frecuentes, con capturas de la demo",0.50,confirmada,manual/Manual-usuario-Balam.pdf; transcript de Claude Code a134588d (12:25–12:55),si,Corte 2-oct; sesión asistida por IA; tiempo real de esta sesión
1 fecha semana etapa actividad_id actividad horas estado_hora fuente_evidencia facturable notas
51 2026-08-11 2026-W33 1 E1-05 Bitácora #62–#63 (merge de las definiciones, respuesta de Noé) y sondeo nocturno de FAC (molde inexistente, FAC-98) 0.75 reconstruida bitacora/REGISTRO.md#63 si commits f28fcc5, a2f338f; por validar
52 2026-08-11 2026-W33 2 E2-02 Capa de escritura mínima ticket→cotización: Quotes_NewQuote en BindClient, POST /api/emision/cotizacion con dry-run/confirmación, pantalla 'Generar cotización en BIND' y script para Balam (73/73 pruebas) 2.50 reconstruida bitacora/REGISTRO.md#64 si commits e854eda y a99eb8b (cliente, 21:28), 2fb230e (21:30); sesión nocturna; por validar
53 2026-08-12 2026-W33 2 E2-02 Madrugada y previo a la sesión: coreografía de la demo (Actualizar sin caché, Cotizar por fila, vista previa automática), sin reintentos en escrituras, tipo de cambio, prefill y selector de cliente 2.00 reconstruida bitacora/REGISTRO.md#64 si commits eafa49b 00:34, 1b062d2 01:33, 2be0432/8228c40 06:55 (cliente); por validar
54 2026-08-12 2026-W33 2 AJ-01 E2-02 Sesión acompañada de escritura Jira→BIND con Noé, Araceli y Pedro: formulario construido en vivo, decisión de que la plataforma cotiza, primer POST con 500 1.00 confirmada fuentes/2026-08-12 - Transcript - Sesion prueba de escritura Jira-BIND.txt si Transcript de 52 min; REGISTRO #65; relevo de Erika por Pedro Transcript de 52 min; REGISTRO #65; relevo de Erika por Pedro; reclasificada de AJ-01 a E2-02 el 2-oct por Johann: es cotización contra el API de BIND, no integración con Jira
55 2026-08-12 2026-W33 2 AJ-01 E2-02 Excel de campos y catálogos de la cotización BIND (67 servicios, 3 productos, 17 clientes) y correo a Pedro (CC Ara, Noé) 0.75 reconstruida Cotizacion-BIND-campos-y-catalogos.xlsx si correo 8:09 am; REGISTRO #66; por validar correo 8:09 am; REGISTRO #66; por validar; reclasificada de AJ-01 a E2-02 el 2-oct por Johann: es cotización contra el API de BIND, no integración con Jira
56 2026-08-20 2026-W34 1 E1-05 Coordinación con Pedro por WhatsApp (18–20 ago): 17 categorías, formulario modificado, ticket FAC-106 y convocatoria del 21 0.50 reconstruida bitacora/REGISTRO.md#67 si por validar
57 2026-08-20 2026-W34 1 E1-05 Cruce del buzón contra la bitácora (9 comunicaciones, 2 correcciones), registro #65 desde el transcript y guion de la sesión del 21-ago Cruce del buzón contra la bitácora (9 comunicaciones incorporadas, 2 correcciones) 2.00 1.00 reconstruida bitacora/REGISTRO.md#65 commit 5d846ec 23:02 si commits 5d846ec 23:02 → 072a3cd 23:52; por validar por validar; dividida el 2-oct: la otra hora (registro #65 y guion del 21-ago) pasó a E2-02
58 2026-08-20 2026-W34 2 AJ-01 E2-02 Sondeo de FAC-106 por API, diagnóstico del 500 (VATRate como fracción, VAT, PriceListID) y wizard cableado al formulario 'Cotización Bind', verificado e2e (74/74) Registro #65 de la sesión de cotización del 12-ago desde el transcript y guion de la sesión del 21-ago (aviso previo a Pedro, coreografía de escritura, plan B del 500, 8 preguntas, protocolo si falla) 1.50 1.00 reconstruida planeacion/Sondeo-FAC-106-y-payload-2026-08-20.md commits a03f81f 23:29, cabe0fe 23:50 y 072a3cd 23:52 si commits f839d0e 23:41 y eb04e23 23:47 (cliente); por validar por validar; dividida el 2-oct de la fila de gestión: documenta y prepara trabajo de cotización (regla 7)
59 2026-08-21 2026-08-20 2026-W34 2 AJ-01 E2-02 Sesión 7:00 am 'Seguimiento - PAF' con Noé, Araceli y Pedro: tres intentos de emisión (500) y pivote a las definiciones Sondeo de FAC-106 por API, diagnóstico del 500 (VATRate como fracción, VAT, PriceListID) y wizard cableado al formulario 'Cotización Bind', verificado e2e (74/74) 0.25 1.50 reconstruida bitacora/REGISTRO.md#69 planeacion/Sondeo-FAC-106-y-payload-2026-08-20.md si Informe Read AI recibido 7:16 am; por validar commits f839d0e 23:41 y eb04e23 23:47 (cliente); por validar; reclasificada de AJ-01 a E2-02 el 2-oct por Johann: es cotización contra el API de BIND, no integración con Jira
60 2026-08-21 2026-W34 2 AJ-01 E2-02 Investigación posterior: schema del portal, intentos 4–5, PRIMERA COTIZACIÓN REAL (folio 1026), VAT corregido como tasa, registro #69, correo 'Seguimiento FAC-106' a Noé y borrador del correo de causa Sesión 7:00 am 'Seguimiento - PAF' con Noé, Araceli y Pedro: tres intentos de emisión (500) y pivote a las definiciones 3.00 0.25 reconstruida bitacora/REGISTRO.md#69 si commits 46fad4c 10:15 (cliente) y d31747d 10:16; correo 7:32 am; por validar Informe Read AI recibido 7:16 am; por validar; reclasificada de AJ-01 a E2-02 el 2-oct por Johann: es cotización contra el API de BIND, no integración con Jira
61 2026-08-21 2026-W34 2 AJ-01 E2-02 Sesión 1:00 pm 'Seguimiento - PAF (2)': homologación de estados del flujo ticket→cotización→prefactura→factura; pendiente prueba de escritura en Jira Investigación posterior: schema del portal, intentos 4–5, PRIMERA COTIZACIÓN REAL (folio 1026), VAT corregido como tasa, registro #69, correo 'Seguimiento FAC-106' a Noé y borrador del correo de causa 0.50 3.00 reconstruida correo Read AI 21-ago 1:34 pm bitacora/REGISTRO.md#69 si SIN registro en bitácora; por validar commits 46fad4c 10:15 (cliente) y d31747d 10:16; correo 7:32 am; por validar; reclasificada de AJ-01 a E2-02 el 2-oct por Johann: es cotización contra el API de BIND, no integración con Jira
62 2026-08-21 2026-W34 2 E2-02 Sesión 1:00 pm 'Seguimiento - PAF (2)': homologación de estados del flujo ticket→cotización→prefactura→factura; pendiente prueba de escritura en Jira 0.50 reconstruida correo Read AI 21-ago 1:34 pm si SIN registro en bitácora; por validar; reclasificada de AJ-01 a E2-02 el 2-oct por Johann: es cotización contra el API de BIND, no integración con Jira
63 2026-08-23 2026-W34 2 E2-03 Camino cotización→prefactura real: Invoices_AddInvoice con candado, POST /api/emision/prefactura, wizard sin simulación (borrador + aprobación real), herencia de CFDIUse/ServiceID/PriceListID desde factura y cliente 3.00 reconstruida balam-plataforma commits a893fa9..a07fff6 si commits 22:15–23:40 del 23-ago (cliente); por validar
64 2026-08-24 2026-W35 2 E2-03 Sesión 7:00 am 'Seguimiento - PAF': validación del flujo cotización→prefactura (MXN/USD, IVA 16 %/0 %) y ajuste en vivo: false = remisión, columna BIND en la bandeja 1.00 reconstruida correo Read AI 24-ago 7:44 am si commit 77d3767 7:38 (cliente); SIN registro en bitácora; por validar
65 2026-08-25 2026-W35 1 E1-05 Correo a Noé con los requisitos de Azure (suscripción, grupo de recursos, Contributor) y lectura de su explicación remisión vs prefactura 0.50 reconstruida correo 25-ago 4:54 pm si por validar
68 2026-08-26 2026-W35 2 E2-03 Contención del incidente: confirmación de prefactura bloqueada (409), candado triple IsFiscalInvoice=false, API en ReadOnly, registro #70 0.75 reconstruida bitacora/REGISTRO.md#70 si commits c5143cf 7:25 y fbb31fc 7:59 (cliente), a48fba0 8:01; por validar
69 2026-08-26 2026-W35 2 E2-03 Investigación de cierre solo lectura: body de Invoices_AddInvoice vs portal y SDK, PDFs de 1000/1001/2384/2386, censo de 58 no fiscales, diff prefactura 2384 vs remisión — adenda al #70 2.50 reconstruida bitacora/REGISTRO.md#70 si commit 2ae6880 21:28; por validar
70 2026-08-26 2026-W35 2 E2-03 Experimentos autorizados en modo Write (folio compartido, 12 sondas de campo, DELETE), dump del APIM (98 ops), script de captura Playwright y adenda 2 al #70 2.50 reconstruida bitacora/REGISTRO.md#70 si commits e7775e0 21:48 (cliente) y db16f62 00:10 del 27-ago; por validar
71 2026-09-03 2026-W36 2 AJ-01 E2-02 Sesión 7:00 am 'Seguimiento PAF': datos para el tenant de Azure y conceptos de Acuntia (headcount); definición: ítem por código y concepto = título del ticket 0.50 reconstruida correo Read AI 3-sep 7:28 am si SIN registro en bitácora; por validar SIN registro en bitácora; por validar; reclasificada de AJ-01 a E2-02 el 2-oct por Johann: es cotización contra el API de BIND, no integración con Jira
72 2026-09-03 2026-W36 2 E2-02 Catálogo de servicios de BIND en la emisión (GET /api/Services), mapeo categoría Jira → código configurable, selector de ítem, y caché de la bandeja invalidada al cotizar (visto en producción) 1.50 reconstruida balam-plataforma commit ed4c819 si archivos 07:46–07:58 y 12:55 del 3-sep; por validar
73 2026-09-03 2026-W36 1 E1-09 Publicación de la plataforma en https://paputec.mx/balam/ como ambiente provisional: Docker (Postgres + API chiseled read-only), Apache/proxy/SPA, deploy.sh reproducible, seeder de admin, migración al arrancar 2.50 reconstruida balam-plataforma/deploy/paputec/README.md si archivos 08:36–12:57 del 3-sep; commit 03582af; por validar
74 2026-09-03 2026-W36 3 E3-01 Endurecimiento aplicado en la publicación: CSP/HSTS, rate limit por IP, bloqueo de login, contraseñas 12+, secretos solo en .env root, respaldos diarios con retención 1.00 reconstruida balam-plataforma/deploy/paputec/README.md si commit 03582af; por validar
75 2026-09-03 2026-W36 3 E3-02 Runbook de publicación, seguridad aplicada, operación y rollback (deploy/paputec/README.md) 0.50 reconstruida balam-plataforma/deploy/paputec/README.md si commit 03582af; por validar
76 2026-09-03 2026-W36 2 AJ-01 E2-02 Sesión 1:30 pm 'Seguimiento PAF (2)': tres tickets de Pedro convertidos en cotizaciones reales desde la plataforma publicada; prueba con ticket de Linares 0.25 reconstruida correo Read AI 3-sep 1:51 pm si SIN registro en bitácora; por validar SIN registro en bitácora; por validar; reclasificada de AJ-01 a E2-02 el 2-oct por Johann: es cotización contra el API de BIND, no integración con Jira
77 2026-09-07 2026-W37 2 E2-02 Cotización en lote desde la bandeja: selección de tickets, pantalla cotizar-lote con dry-run y confirmación por ticket en serie, reglas de prellenado compartidas (cotizacion-prefill.ts) 2.00 reconstruida balam-plataforma commit fbdbd18 si archivos 07:27–07:30 del 7-sep; por validar
78 2026-09-09 2026-W37 2 E2-03 Sesión 1:00 pm 'Seguimiento PAF - Prefactura' con Araceli y Pedro: prefactura creada en la UI de BIND ('Generar venta') mientras se capturan las peticiones (InvoiceDetails) 0.50 reconstruida correo Read AI 9-sep 1:24 pm si SIN registro en bitácora; por validar
79 2026-09-09 2026-W37 2 E2-03 Tras la sesión de la 1:00 pm: análisis de la captura de 'Generar venta' en la UI de BIND, prueba de folio (no se puede sobrescribir) y diseño del puente a la web app de BIND para la prefactura 1.25 reconstruida transcript de Claude Code 65198403 (17:36–18:53); balam-plataforma/CLAUDE.md §Puente a la UI si Corte 2-oct: no entró al corte del 15-sep (el código se subió hasta el 29-sep en 4d6b3d8); por validar
80 2026-09-10 2026-W37 2 E2-03 Sesión 1:00 pm con Pedro: análisis de la cotización 1078 (Acuntia) y captura de la petición de red de la prefactura para evaluar su réplica 0.25 reconstruida correo Read AI 10-sep 1:21 pm si SIN registro en bitácora; por validar
81 2026-09-10 2026-W37 2 E2-03 Preparación de la segunda sesión de captura: lista de lo que hay que recabar en DevTools (Generar venta sobre la 1078, Preserve log, Fetch/XHR) 0.25 reconstruida transcript de Claude Code 65198403 (12:21) si Corte 2-oct; por validar
82 2026-09-10 2026-W37 2 E2-03 Contrato real de AddInvoiceTransaction (PrevInvoice/FiscalInvoice/IsCFDI + QuoteID + Number), sonda autorizada op #12 (remisión 1004 creada y cancelada), BindUiClient con login de formulario, endpoints prefactura-ui, vista de cotizaciones sin ticket y mensaje a Pedro con las alternativas 1.75 reconstruida transcript de Claude Code 65198403 (13:21–13:29, 14:15–15:49, 16:50–16:53); commit 4d6b3d8 si Corte 2-oct; la sesión con Pedro (1:00–1:21) ya está en su fila; por validar
83 2026-09-15 2026-W38 1 E1-05 Corte de horas 6-ago→15-sep (reconstrucción desde commits, correos e informes de reunión), Excel de avance, commits y publicación de ambos repositorios 0.75 reconstruida planeacion/Seguimiento-horas.csv si sesión asistida por IA; por validar
84 2026-09-16 2026-W38 1 E1-05 Excel de avance publicado en Google Sheets (Drive/Balam vía rclone, enlace compartible) y corte del 16-sep regenerado desde script 0.50 reconstruida planeacion/generar_avance_2026_09_16.py si sesión asistida por IA; por validar
85 2026-09-29 2026-W40 2 E2-03 Sesión 7:00 am 'Seguimiento PAF - Integración ERP' con Pedro: validación de accesos, Balam entrega login web a BIND con permisos de prefactura y error al cargar la solicitud al cotizar 0.50 reconstruida correo Read AI 29-sep 7:23 am; REGISTRO #83 si Corte 2-oct; por validar
86 2026-09-29 2026-W40 2 E2-03 Cierre de los huecos de bitácora #71–#83 desde el buzón (informes de Read AI y correos del 21-ago al 29-sep) e investigación de cierre con el SDK oficial de BIND (modelo de dos endpoints) 0.75 reconstruida bitacora/REGISTRO.md#71-83; commit 3de2739 08:06 si Corte 2-oct; otra máquina, inicio estimado al terminar la sesión (7:23); por validar; movida de E1-05 a E2-03 el 2-oct: documenta las sesiones de prefactura e investiga el SDK de BIND (regla 7)
87 2026-09-29 2026-W40 2 E2-03 Puente de prefactura con el usuario propio CTO: credenciales y API key nueva en user-secrets, app en modo escritura, PRIMERA PREFACTURA REAL desde la plataforma (folio 2467, 16:08), una sesión por usuario, API interno de la web app y contención de la escritura accidental (FillQuote: cotización 1079 Surtida) 1.50 reconstruida transcript de Claude Code 65198403 y 02f58158 (10:54–11:21, 13:24–13:39, 15:28–16:26); balam-plataforma/CLAUDE.md si Corte 2-oct; dos sesiones en paralelo, contadas una sola vez; por validar
88 2026-09-29 2026-W40 2 AJ-01 Webhook de Jira FAC: análisis (leer la creación de tickets vs ADR-001), plan de 5 fases, Fase 1 por GET (token de Pedro con ADMINISTER), endpoint POST /api/jira/webhook con HMAC, idempotencia y relectura (38/38), túnel ngrok y piloto e2e: webhook id 1 + FAC-169 detectado en 14.4 s y borrado 1.00 reconstruida transcript de Claude Code 02f58158 (15:34–15:41, 17:57–18:14, 18:39–19:08); REGISTRO #84; commit d7238da si Corte 2-oct; incluye plan, bitácora y push de 4d6b3d8 (rebase con 3 conflictos); por validar
89 2026-09-29 2026-W40 2 AJ-01 Cierre de la sesión: push de ambos repos, appsettings.Local portátil de los user-secrets y relevo a la máquina con acceso a paputec 0.50 reconstruida transcript de Claude Code 02f58158 (23:47–00:11); commits 8479e96, 5145801, e5912bf si Corte 2-oct; por validar
90 2026-09-30 2026-W40 2 AJ-01 Webhook en producción (paputec): fusión de las dos bitácoras del 29-sep, publicación de 5145801 (116 pruebas, respaldo y rollback), secreto nuevo, PUT del webhook id 1 y prueba e2e con FAC-170 detectada en 1.05 s 0.50 reconstruida bitacora/REGISTRO.md#85; commits e88b5e1 00:19, 9351aa4 00:30, 7e50c71 00:31 si Corte 2-oct; ≈12:10–12:35 am; por validar
91 2026-09-30 2026-W40 2 AJ-01 Vista previa automática de la cotización por cada ticket detectado (cola, trabajador y port del prellenado; 147 pruebas), push y publicación; causa del 500 de BIND: la API key de arturo (la de cto responde 200) 0.50 reconstruida bitacora/REGISTRO.md#86; commits 1c37eaa 00:45, 287cb65, 085e903 00:55 si Corte 2-oct; ≈12:35–1:00 am; por validar
92 2026-09-30 2026-W40 2 AJ-01 API key de cto en producción (el sync vuelve tras 20 días), radiografía del formulario contra el catálogo, vista previa validada contra BIND real con FAC-171 y modal en vivo con 'Aceptar', campana y modo automático configurable (170 pruebas) 0.75 reconstruida bitacora/REGISTRO.md#87; commits aac7447 01:40, bdafb06 01:42 si Corte 2-oct; ≈1:00–1:45 am; por validar
93 2026-09-30 2026-W40 2 AJ-01 Push y publicación del modal en vivo, con el modo automático permitido en el servidor (sin activar) 0.25 reconstruida bitacora/REGISTRO.md#87 adenda; commits 29a354b, 8799348 06:55 si Corte 2-oct; respaldo de las 6:53 am; por validar
94 2026-09-30 2026-W40 2 E2-03 Puente de prefactura encendido en producción, 'Servicio de Enlace' → ítem 106 y relectura completa de cotizaciones (11 con estado atrasado), tras lo que Pedro vio a las 7:26 en el wizard de FAC-173 0.75 reconstruida bitacora/REGISTRO.md#88 si Corte 2-oct; 7:50–8:30 am; por validar
95 2026-09-30 2026-W40 2 E2-03 Cuatro bugs reales del puente y de los importes (Total en cero, USD mostrado en pesos e IVA doble, post-proceso que escondía la prefactura, duplicados posibles) con pruebas que los reproducen (197), panel bajo la fila y sync de cotizaciones por barrido completo 1.25 reconstruida bitacora/REGISTRO.md#88; commits 597c8a2 21:16, 63af3d6 21:29 si Corte 2-oct; 8:30–9:40 pm; por validar
96 2026-09-30 2026-W40 2 E2-03 Credenciales del usuario CTO en el .env de producción (autorización de Johann), push y publicación de 597c8a2 con la migración QuoteSubtotal y verificación del primer ciclo del sync 0.25 reconstruida bitacora/REGISTRO.md#88 adenda; commits 4a219b5, 604cb8d 21:49 si Corte 2-oct; 9:40–9:50 pm; por validar
97 2026-09-30 2026-W40 2 E2-03 Timbrado desde la plataforma (POST /api/emision/timbrar-ui: EmitCFDI con dry-run, palabra TIMBRAR y verificación por lectura), estado incierto cerrado en los POST, consecutivo sin la serie B, uso CFDI del mismo cliente, 212 pruebas, recorrido en navegador, publicación y guion de la prueba del 1-oct 0.75 reconstruida bitacora/REGISTRO.md#89; commits f5b3743 22:40, 9874547 22:45 si Corte 2-oct; 10:00–10:45 pm según respaldo (04:40 UTC) y último commit — el título del #89 dice 10:00 pm–12:50 am: Johann no recuerda la hora de cierre (2-oct): se queda la cota de la evidencia
98 2026-09-30 2026-W40 2 E2-03 Llamada con Pedro (7:26 am): revisión del wizard de FAC-173 en producción — prefactura deshabilitada, totales en cero, campo de servicios y Arturo en la lista de aprobadores 0.25 validada bitacora/REGISTRO.md#88 si Corte 2-oct; ~15 min según Johann (2-oct)
99 2026-10-01 2026-W40 2 E2-04 Prueba completa con Balam en producción: ticket → cotización → prefactura; el timbrado (prefactura → CFDI) no se intentó y queda por probar 0.25 validada planeacion/Guion-prueba-completa-2026-10-01.md si Corte 2-oct; ~20 min o menos según Johann (2-oct)
100 2026-10-02 2026-W40 2 E2-03 Prefacturas en lote (Cotizaciones y bandeja → una prefactura por cotización, en serie, folios consecutivos con el API lento) y timbrado también en el detalle de la factura (213 pruebas, recorrido en navegador); push y publicación 1.00 reconstruida bitacora/REGISTRO.md#90; commits 73d1c2a 08:35, 4f3eb99, 4e90543 08:41 si Corte 2-oct; 7:40–8:40 am; por validar
101 2026-10-02 2026-W40 1 E1-05 Corte de horas 16-sep → 2-oct: pull de ambos repos, reconstrucción desde los transcripts de Claude Code, los commits y los informes de Read AI, y Excel de avance 0.75 confirmada transcript de Claude Code a134588d (11:47–12:25); planeacion/generar_avance_2026_10_02.py si Corte 2-oct; sesión asistida por IA; tiempo real. No se cobran la proyección de costos, la reclasificación ni los ajustes posteriores del Excel (~0.75 h, decisión de Johann 2-oct)
102 2026-10-02 2026-W40 2 E2-04 Manual de usuario en PDF (14 páginas): acceso y roles, consulta, solicitudes de Jira, ticket → cotización (aviso en vivo, pantalla y lote), prefactura (individual, emisión asistida y lote), timbrado, reglas y mensajes frecuentes, con capturas de la demo 0.50 confirmada manual/Manual-usuario-Balam.pdf; transcript de Claude Code a134588d (12:25–12:55) si Corte 2-oct; sesión asistida por IA; tiempo real de esta sesión
+89 -1
View File
@@ -5,7 +5,7 @@ Fuente local de horas efectivamente trabajadas para el proyecto Balam. Se mantie
**Tarifa contractual:** $600 MXN/h + IVA
**Cadencia:** corte semanal; factura los viernes; pago a 30 días
**Datos tabulares:** [`Seguimiento-horas.csv`](Seguimiento-horas.csv)
**Última actualización:** 2026-09-16 (corte 6-ago → 16-sep reconstruido, por validar)
**Última actualización:** 2026-10-02 (corte 16-sep → 2-oct reconstruido, por validar)
## Reglas de captura
@@ -18,6 +18,94 @@ Fuente local de horas efectivamente trabajadas para el proyecto Balam. Se mantie
7. El esfuerzo real de una sesión incluye TODO su alrededor, no solo el desarrollo: el análisis/pensamiento previo para decidir qué se hace, la documentación de cada parte (bitácora, pendientes, investigación), y la comunicación asociada (cada correo, cada mensaje de WhatsApp, minutas y reportes). Definido por Johann el 6-ago.
8. Para proyectar el costo final del proyecto, las horas no comprimibles (sesiones con Balam, pruebas acompañadas, capacitación, demos, gestión y comunicación) son el piso, y el objetivo de cierre es ese piso y un poco más — no el piso pelón ni el rango completo de la propuesta. Los cortes ya reportados a Erika nunca se recalculan retroactivamente (ajustado por Johann el 6-ago).
## Corte al 2-oct (reconstruido, por validar)
Del 16-sep al 2-oct no se capturó nada en el CSV. El 2-oct se agregaron **15.25 h**, en 21 filas con la nota `Corte 2-oct`:
- 13.50 h `reconstruida`.
- 0.50 h `validada`: Johann dio la duración el 2-oct.
- 1.25 h `confirmada`: tiempo real de la sesión del 2-oct (solo lo que se cobra).
Las fuentes fueron:
- los registros de las sesiones de Claude Code de esta máquina (marcas de tiempo de cada mensaje; las sesiones paralelas se unieron para no contar doble);
- los commits de ambos repos;
- los rangos horarios que anota la bitácora (#83–#90);
- el informe de Read AI del 29-sep.
**Johann debe validar las horas `reconstruida`** antes de reportarlas. El corte del 16-sep (94.58 h) no se tocó.
| Etapa | Horas al corte | Rango | Avance | Estado |
|---|---:|---:|---:|---|
| Etapa 0 | **21.83 h** | 18–22 h | 100% | Cerrada y validada |
| Etapa 1 | **32.50 h** | 32–39 h | 97% | Capa de escritura completa; Azure entregado, falta desplegar (75%); CI/CD 50% |
| Etapa 2 (sin ajuste Jira) | **43.50 h** | 38–46 h | 97% | Cadena completa validada en real: Pedro timbró desde la plataforma el 2-oct ([#91](../bitacora/REGISTRO.md)); quedan ajustes de UI/UX (fila nueva) |
| » Ajuste Jira → BIND | **10.50 h** | 7–10 h | 100% | 0.5 h arriba del tope: webhook en producción, vista previa automática y modal "Aceptar" |
| Etapa 3 | **1.50 h** | 24–29 h | 13% | Solo cartera por moneda (25%), endurecimiento y runbook |
| **Total** | **109.83 h** | | 81% | 55.00 h desde el 6-ago: 52.25 `reconstruida` + 2.25 `confirmada` + 0.50 `validada` |
Movimientos entre etapas en este corte (el total no cambia): 7.75 h de cotización contra BIND salen del ajuste Jira hacia la Etapa 2, y 1.75 h de documentación técnica salen de la gestión de la Etapa 1 hacia la Etapa 2 (ver abajo).
**De dónde salen las 15.25 h:**
- **9/10-sep (3.25 h) — no entraron al corte del 15-sep.** El puente a la web app de BIND para la prefactura (`BindUiClient`, contrato de `AddInvoiceTransaction`, sonda op #12) se construyó en esta máquina y se quedó sin commit hasta el 29-sep (`4d6b3d8`). Por eso la reconstrucción del 15-sep, hecha en la otra máquina, no lo vio. Las dos sesiones con Pedro de esos días ya estaban en el CSV y no se duplican.
- **29-sep (4.25 h):** la sesión de las 7:00 con Pedro (login web a BIND) y el cierre de huecos de la bitácora. Luego, en esta máquina, la primera prefactura real (folio 2467) y el piloto del webhook. Esas dos sesiones corrieron en paralelo y se cuentan una sola vez.
- **30-sep (5.25 h), en la otra máquina:** el webhook pasa a producción, con la vista previa automática y el modal en vivo. Hubo una llamada de 15 min con Pedro a las 7:26. Después, el puente se encendió en producción con cuatro bugs corregidos y se construyó el timbrado.
- **1-oct (0.25 h):** prueba completa con Balam, de unos 20 min según Johann. Llegó a prefactura; el timbrado no se intentó.
- **2-oct (2.25 h):**
- Prefacturas en lote (1.00 h).
- Este corte en sí (0.75 h). La proyección de costos, la reclasificación y los ajustes posteriores del Excel (~0.75 h) **no se cobran** (decisión de Johann, 2-oct).
- El manual de usuario en PDF (0.50 h, `manual/Manual-usuario-Balam.pdf`).
- El acompañamiento por WhatsApp a Pedro mientras timbraba (3:28–3:46 pm) **no se cobra** (decisión de Johann, 2-oct).
- **Reparto CSV → filas del Excel** (`generar_avance_2026_10_02.py`; el script se detiene si no cuadra):
- E1-05 → seguimiento semanal
- E2-03 → conversión cotización→factura
- E2-04 → pruebas integrales + manual
- AJ-01 → disparador → cotización automática
- Además se movieron 0.25 h de "Creación de cotizaciones" a "Tipo de cambio" (el TC se resolvió el 12-ago dentro de ese bloque), para que ninguna fila cerrada quede con 0 h.
**Reclasificación del 2-oct (Johann):** las 7.75 h que el corte del 15-sep cargó al ajuste Jira (`AJ-01`) y que en realidad fueron cotización contra el API de BIND pasan a la Etapa 2 como `E2-02`, a la fila «Creación de cotizaciones». Son las sesiones acompañadas del 12-ago, las dos del 21-ago y las dos del 3-sep, el Excel de catálogos, el diagnóstico del 500 y la investigación hasta el folio 1026. Así quedan:
| Línea | Antes | Ahora | Rango |
|---|---:|---:|---:|
| Ajuste Jira → BIND | 18.25 h | **10.50 h** ($6,300) | 7–10 h |
| Disparador → cotización automática | 8.00 h | **3.50 h** (webhook, vista previa, modal) | 3–4 h |
| Trazabilidad | 4.75 h | **1.50 h** | 2–3 h |
| Etapa 2 | 34.00 h | **41.75 h** | 38–46 h |
| Creación de cotizaciones | 7.75 h | **15.50 h** | 5–6 h |
El total del proyecto no cambia. Lo que Balam aprobó y controla es el rango por etapa, y ninguna etapa queda rebasada. A nivel fila, «Creación de cotizaciones» y «Conversión cotización→factura» superan su estimado: el API de BIND no documentaba cómo cotizar ni permitía crear prefacturas.
**Fila de seguimiento y gestión (2-oct, Johann):** la fila «Demostración semanal (viernes) + reporte de horas» no tenía demostraciones: las sesiones con Pedro se cargan a las filas técnicas. Lo que tenía era gestión. Por eso:
- Se renombra a «Seguimiento y gestión: reportes de horas, bitácora y comunicación con Balam».
- Lo técnico se va a su fila (regla 7):
- 20-ago: 1.00 h del registro #65 y el guion de la sesión del 21-ago pasan a «Creación de cotizaciones». La fila del CSV se dividió según los commits: 5d846ec 23:02 para el cruce del buzón; a03f81f, cabe0fe y 072a3cd para lo de cotización.
- 29-sep: 0.75 h (registros #71–#83 e investigación del SDK de BIND para la prefactura) pasan a «Conversión».
- La fila queda en **8.00 h**, unos 40 minutos por semana en 12 semanas:
| Qué | Horas | Fechas y evidencia |
|---|---:|---|
| Cortes de horas y Excel de avance | 3.75 | 15-jul 0.50 · 16-jul 1.00 (Excel y mensaje a Erika) · 21-jul 0.25 · 15-sep 0.75 · 16-sep 0.50 (Google Sheets) · 2-oct 0.75 |
| Bitácora | 2.25 | 1-ago 0.50 (#59–#60, commit 2b862ee) · 11-ago 0.75 (#62–#63, commits f28fcc5 y a2f338f) · 20-ago 1.00 (cruce del buzón, commit 5d846ec) |
| Comunicación con Balam | 2.00 | 28-jul 0.25 (Azure a Erika) · 29-jul 0.75 (envío de la v1.3) · 20-ago 0.50 (WhatsApp con Pedro) · 25-ago 0.50 (correo de Azure a Noé) |
| **Total** | **8.00** | El estimado de 2–3 h de la propuesta era para demostraciones y reporte, no para la gestión de 12 semanas |
**Decisiones de Johann sobre el Excel (2-oct):**
- **Fechas reales.** Inicio = primer día trabajado. Fin = día en que se terminó el entregable según la bitácora y los commits; en las tareas en curso, el último día trabajado.
- **Orden.** Las filas van en orden cronológico dentro de cada etapa; el bloque del ajuste Jira → BIND sigue al frente de la Etapa 2.
- **Avance de los encabezados.** Es el promedio de sus filas ponderado por las horas estimadas (regla 4). Antes se calculaba con horas consumidas entre horas estimadas.
- **Al 100%.**
- Capa de escritura, Disparador → cotización automática, Creación de cotizaciones, Pruebas integrales + manual y Demostración semanal (al día).
- Conversión cotización→factura y Trazabilidad: se cerraron al 100% el 2-oct, cuando Pedro timbró desde la plataforma y completó todo el proceso. Los ajustes de UI/UX que quedan (botones «Ver en Jira» / «Ver en BIND» y las pantallas de Solicitudes, Cotizaciones y Facturas) no reabren esas filas: van en una fila nueva de la Etapa 2, «Ajustes de UI/UX de la emisión» (⚪ Próxima, 0%, 1–2 h estimadas), que deja la Etapa 2 en 97%.
- Con alcance ajustado: Catálogo de clientes (Balam eliminó la lista blanca el 6-jul), Detalle de factura (el XML se consulta en BIND), Exportación (queda CSV) y Tipo de cambio (sale del catálogo de monedas de BIND; el proceso del DOF sale del alcance).
- **Otros avances.**
- Azure sube a 75%: Balam ya dio el acceso, aunque falta volver a pedir la contraseña.
- CI/CD queda en 50%.
- Cobranza queda en 25%: la cartera con vencidos existe desde el 1-ago.
- Endurecimiento y documentación técnica se quedan como estaban.
**Sin registro de horas:**
- Timbrado del 30-sep (#89): se dejó en 0.75 h (10:00–10:45 pm según el respaldo y el último commit). Johann no recuerda si siguió hasta las 12:50 am, como dice el título del registro.
- Comunicación del 16 al 28-sep (convocatoria del 28-sep, credenciales en TXT, autorización escrita de Pedro para el webhook) y la fecha en que Balam dio el acceso a Azure.
- Las duraciones salen de la actividad dentro de las sesiones de trabajo. Una pausa de más de 20 minutos corta el bloque, así que el tiempo de revisar o pensar fuera de la sesión no aparece (regla 7). Johann puede subirlas.
## Corte al 16-sep (reconstruido, por validar)
Del 10-ago al 15-sep no se capturó nada en el CSV; el 15-sep se reconstruyeron **39.25 h** (más 0.50 h del 16-sep: Excel publicado en Google Sheets) con evidencia fechada (commits de ambos repos, correos, informes de reunión de Read AI y mtimes de archivos) y quedaron marcadas `reconstruida` (salvo la sesión del 12-ago, `confirmada` por transcript). **Johann debe validarlas** (o ajustarlas) antes de reportarlas; el corte del 6-ago (54.83 h) no se tocó.
+1
View File
@@ -0,0 +1 @@
Plan-actividades-avance-2026-07-27.xlsx
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

+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()))
+55
View File
@@ -4,6 +4,11 @@
# 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)
@@ -120,6 +125,56 @@ elif cmd == "crear-ticket":
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"
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

Binary file not shown.

After

Width:  |  Height:  |  Size: 108 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 261 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 248 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 245 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 235 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 257 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 206 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 170 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 256 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 125 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 157 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 163 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 189 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 212 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 123 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 158 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 144 KiB

+18
View File
@@ -0,0 +1,18 @@
@echo off
REM ============================================================
REM Arranque de n8n + ngrok
REM Replica las variables de entorno del flujo de PowerShell.
REM ============================================================
REM --- Variables de entorno para n8n ---
set "WEBHOOK_URL=https://deanne-victualless-gertie.ngrok-free.dev/"
set "N8N_SECURE_COOKIE=false"
set "NODE_FUNCTION_ALLOW_BUILTIN=fs,path,os,child_process"
set "NODE_FUNCTION_ALLOW_EXTERNAL=jimp,jsqr,puppeteer"
REM --- Abre ngrok en su propia ventana (tunel al puerto 5678) ---
start "ngrok" cmd /k ngrok http 5678
REM --- Arranca n8n en esta ventana ---
echo Iniciando n8n...
n8n start