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
@@ -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:
|
||||
|
||||
@@ -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>
|
||||
|
After Width: | Height: | Size: 417 KiB |
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
|
After Width: | Height: | Size: 363 KiB |
@@ -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": {}
|
||||
}
|
||||
@@ -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": {}
|
||||
}
|
||||
@@ -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": {}
|
||||
}
|
||||
@@ -2,13 +2,24 @@
|
||||
|
||||
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-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)._
|
||||
_Última actualización: **2026-10-15** — ⭐ **Azure + CI/CD**: plataforma en https://balam-paf-staging.azurewebsites.net, pipeline de GitHub Actions por OIDC (build verde; falta el rol de despliegue), PostgreSQL 17, Log Analytics, Application Insights, Key Vault y Storage creados; traspaso de la BD ensayado y pendiente ([REGISTRO #92](REGISTRO.md)). Anterior: **2026-10-02** — ⭐ **prefacturas en lote** (Cotizaciones y bandeja → una prefactura por cotización, en serie; timbrado también desde el detalle de la factura), **publicadas en paputec a las 8:40 AM** (`73d1c2a`) ([REGISTRO #90](REGISTRO.md)). Anterior: **2026-10-01, madrugada** — ⭐🔥 **el TIMBRADO entra a la plataforma** (prefactura → CFDI por el puente, con dry-run, palabra TIMBRAR, verificación por lectura y bloqueo de reenvío), la cadena completa queda revisada (estado incierto de los POST cerrado, consecutivo sin la serie B, uso CFDI del mismo cliente), probada (212 + 10 pruebas, recorrido completo en navegador) y **publicada** (`f5b3743`) para la **prueba completa del 1-oct con Balam**; guion en `planeacion/` ([REGISTRO #89](REGISTRO.md)). Anterior: **2026-09-30, noche** — 🔥 **el puente de prefactura queda ENCENDIDO en producción** (sin credenciales del usuario `CTO` en el servidor: bloqueo del control de permisos, decisión de Johann pendiente), «Servicio de Enlace» mapeado al 106 y 11 cotizaciones con estado atrasado corregidas; lo que Pedro vio a las 7:26 (totales en cero, botones que "no llevan a ninguna parte", "servicios", Arturo) tenía detrás **cuatro bugs reales**, corregidos, probados (197 + 10 pruebas) y **publicados en paputec a las 9:44 PM** (`597c8a2`, migración `QuoteSubtotal`); las credenciales del usuario `CTO` entraron al servidor a las 9:41 PM con autorización de Johann: el puente queda completo ([REGISTRO #88](REGISTRO.md)). Anterior: **2026-09-30** — ⭐ el webhook de Jira FAC queda **en producción (paputec)**: publicado, secreto nuevo, webhook id 1 movido y prueba de punta a punta con FAC-170 en 1.05 s ([REGISTRO #85](REGISTRO.md)); y la vista previa automática de la cotización queda **publicada** ([#86](REGISTRO.md)). 🔥 **La API key de `cto` ya está en producción y el sync volvió**; la vista previa se validó contra el BIND real (FAC-171) y el **modal en vivo con "Aceptar"** quedó **publicado** a las 6:53 AM, con el modo automático permitido en el servidor pero sin elegir ([#87](REGISTRO.md)). Además, fusión de las dos sesiones paralelas del 29-sep: la del webhook pasa a ser el [#84](REGISTRO.md) (nació como #71 en la otra máquina). Anterior: **2026-09-29** — ⭐ Balam entrega **login web a BIND** con permisos de prefactura; endpoint interno de la UI confirmado (*Generar Venta → Prefactura*, petición `InvoiceDetails`) y el modelo de dos endpoints corroborado con el SDK oficial ([REGISTRO #83](REGISTRO.md)). **Huecos de registro cerrados:** las sesiones del 21-ago al 10-sep se reconstruyeron desde el buzón ([#71](REGISTRO.md)–[#82](REGISTRO.md)). Anterior: **2026-09-15** — corte de horas 10-ago→15-sep reconstruido y huecos de registro listados (seis sesiones sin bitácora). Anterior: **2026-08-21, mediodía** — ⭐ primera cotización real emitida (folio 1026; la 2.ª, corregida, pendiente de confirmar). Anterior: **2026-08-20, noche.** Cruce completo del buzón contra la bitácora (MCP ms365): se incorporaron 9 comunicaciones faltantes y se corrigieron 2 errores de registro. **Hay sesión mañana viernes 21-ago, 7:00 am** con Noé, Ara y Pedro sobre el formulario modificado y `FAC-106`. Ver [REGISTRO #65](REGISTRO.md)–[#68](REGISTRO.md)._
|
||||
|
||||
## ☁️ 15-oct — Azure + CI/CD: lo que falta para cerrar la Etapa 1 ([REGISTRO #92](REGISTRO.md), runbook en `balam-plataforma/deploy/azure/README.md`)
|
||||
|
||||
- [ ] 🔑 **Balam (la única solicitud pendiente):** rol **Website Contributor** sobre la web app `balam-paf-staging` para la identidad `id-balam-paf-staging-github` (principalId `ebb12547-c96d-4993-9c57-2cc1bd7abaa0`). En cuanto exista, cada push a `main` publica solo. *Opcional en el mismo mensaje:* "Role Based Access Control Administrator" acotado a Key Vault Secrets Officer/User, Storage Blob Data Contributor, Website Contributor, Monitoring Reader y Log Analytics Reader, para no volver a pedir roles.
|
||||
- [ ] 🌐 **Johann — red de la BD nueva** (`psql-balam-paf-staging`, hoy con el acceso público apagado): encenderlo con firewall solo a las IPs de salida del App Service (comandos en el README) o red privada (requiere `Microsoft.Network`, que tendría que registrar Balam).
|
||||
- [ ] 🗄️ **Johann — traspaso de la BD**, en una ventana sin operación y **después de los comentarios de Pedro de la prueba del 15-oct**: detener el sitio de Azure y el API de paputec → `pg_dump` de paputec → crear `balam_app` y la base → `pg_restore` como `balam_app` → comparar conteos → configuración del sitio (cadena nueva, `Db__MigrateOnStartup=true`, `Sync__SchedulerIntervalMinutes=15`, `APPLICATIONINSIGHTS_CONNECTION_STRING`) → dejar paputec apagado. Las contraseñas generadas están solo en la máquina Windows (`%USERPROFILE%\.balam-secretos\`); en otra máquina, fijar unas nuevas.
|
||||
- [ ] 🔁 **Webhook de Jira** tras el traspaso: reenviar `/balam/api/jira/webhook` desde Apache de paputec a Azure (sin tocar el Jira de Balam) o `PUT` del webhook id 1 con autorización de Balam.
|
||||
- [ ] 🧹 Tras el traspaso: quitar `paputec-pg-ca.crt` de `empaquetar.sh`, borrar la regla temporal del firewall y pasar los secretos a Key Vault cuando haya roles.
|
||||
- [ ] 💰 Decidir si se baja el tope de Log Analytics de 1 a 0.3 GB/día (peor caso ≈ 63 → ≈ 10 USD/mes; el uso normal cabe en los 5 GB gratis).
|
||||
- [ ] ⏱️ Reconstruir el corte de horas del 3 al 15-oct (incluye la sesión de Azure del 15-oct, ≈3 h). La Etapa 1 lleva 32.50 de 32–39 h y el total 109.83 de 112–136 h (más 7–10 del ajuste Jira): avisar por escrito a Balam antes de comprometer la Etapa 3.
|
||||
- [x] ~~☁️ Azure: Balam ya dio el acceso; volver a pedir la contraseña~~ → **15-oct:** acceso verificado (Contributor en el RG, proveedores registrados) y plataforma desplegada ([#92](REGISTRO.md)).
|
||||
|
||||
## ⭐ 2-oct — prefacturas en lote ([REGISTRO #90](REGISTRO.md))
|
||||
|
||||
- [x] ~~🚀 **Johann — decidir push + publicación de `73d1c2a`**~~ → **HECHO el 2-oct, 8:40 AM** (respaldo, imagen `pre-lote-20261002`, publicado; [#90](REGISTRO.md)).
|
||||
- [ ] 🧹 Imagen de rollback `balam-api:pre-lote-20261002` en paputec: borrarla con las demás `pre-*` cuando la versión lleve unos días estable.
|
||||
- [ ] 📝 **Registrar el resultado de la prueba completa del 1-oct** (¿se timbró? folio, UUID, qué devolvió `EmitCFDI`, qué se canceló) en el #91.
|
||||
- [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))
|
||||
@@ -93,6 +104,9 @@ _Última actualización: **2026-10-02** — ⭐ **prefacturas en lote** (Cotizac
|
||||
- [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))
|
||||
|
||||
@@ -1985,6 +1985,74 @@ La bandeja permite marcar varios tickets (solo los que aún no tienen cotizació
|
||||
|
||||
---
|
||||
|
||||
## 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)
|
||||
|
||||
@@ -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>
|
||||
|
After Width: | Height: | Size: 56 KiB |
|
After Width: | Height: | Size: 64 KiB |
|
After Width: | Height: | Size: 49 KiB |
|
After Width: | Height: | Size: 101 KiB |
|
After Width: | Height: | Size: 77 KiB |
|
After Width: | Height: | Size: 75 KiB |
|
After Width: | Height: | Size: 101 KiB |
|
After Width: | Height: | Size: 59 KiB |
|
After Width: | Height: | Size: 113 KiB |
@@ -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"
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
|
@@ -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ó.
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
Plan-actividades-avance-2026-07-27.xlsx
|
||||
@@ -0,0 +1,622 @@
|
||||
"""Genera Avance-Balam-2026-10-02.xlsx a partir del corte del 16-sep.
|
||||
|
||||
Horas — corte del viernes 2-oct: todo lo nuevo en `Seguimiento-horas.csv`
|
||||
lleva la nota "Corte 2-oct":
|
||||
- 3.25 h del 9/10-sep que no entraron al corte del 15-sep: el puente a la
|
||||
web app de BIND para la prefactura vivía sin commit en esta máquina (se
|
||||
subió el 29-sep en 4d6b3d8); fuente: transcripts de Claude Code.
|
||||
- 29-sep: sesión con Pedro (login web a BIND), primera prefactura real
|
||||
(folio 2467) y webhook de Jira FAC (piloto con FAC-169).
|
||||
- 30-sep: webhook en producción, vista previa automática, modal en vivo,
|
||||
llamada con Pedro (7:26), puente encendido en producción con cuatro bugs
|
||||
corregidos y timbrado.
|
||||
- 1-oct: prueba completa con Balam (llegó a prefactura; timbrado sin probar).
|
||||
- 2-oct: prefacturas en lote, manual de usuario y este corte. Ese día Pedro
|
||||
timbró desde la plataforma (cadena completa con CFDI), con lo que se cierran
|
||||
«Conversión» y «Trazabilidad»; el acompañamiento por WhatsApp no se cobra, y del
|
||||
corte solo se cobran 0.75 h (no la proyección de costos ni la reclasificación).
|
||||
- La fila "Demostración semanal + reporte de horas" pasa a llamarse "Seguimiento y
|
||||
gestión"; lo técnico que traía (20-ago 1.00 h, 29-sep 0.75 h) va a su fila.
|
||||
- Hoja 2 "Desglose de horas": cada registro del CSV con fecha, etapa, actividad,
|
||||
horas, monto, estado y evidencia, más un resumen por etapa que cuadra con la hoja 1.
|
||||
Quedan ajustes de UI/UX de esas pantallas, como fila nueva de la Etapa 2.
|
||||
|
||||
Avance y fechas — decisiones de Johann del 2-oct:
|
||||
- Fin = día en que se terminó el entregable según bitácora y commits (las
|
||||
tareas en curso: último día trabajado). Los encabezados de etapa toman el
|
||||
primer Inicio y el último Fin de sus filas.
|
||||
- Dentro de cada etapa las filas van en orden cronológico (Inicio, luego Fin);
|
||||
las no iniciadas al final. El bloque del ajuste Jira → BIND se queda al
|
||||
frente de la Etapa 2 (decisión del 29-jul), con sus filas también en orden.
|
||||
- El avance de cada encabezado es el promedio de sus filas ponderado por las
|
||||
horas estimadas (regla 4: el avance mide entregable, no horas). La Etapa 2
|
||||
excluye las filas del ajuste, igual que sus horas.
|
||||
- Cierres con alcance ajustado: lista blanca (Balam la eliminó el 6-jul), XML
|
||||
(se consulta en BIND), XLSX (queda CSV) y tipo de cambio del DOF (se toma
|
||||
del catálogo de monedas de BIND).
|
||||
- Estatus simples: una línea de estado y viñetas cortas en cada fila; sin
|
||||
bloque de notas debajo de la tabla (el detalle vive en Seguimiento-horas.md).
|
||||
El corte del 16-sep no se toca.
|
||||
"""
|
||||
import csv
|
||||
import os
|
||||
from collections import defaultdict
|
||||
from copy import copy
|
||||
from datetime import datetime
|
||||
|
||||
import openpyxl
|
||||
from openpyxl.styles import Alignment, Border, Font, PatternFill, Side
|
||||
|
||||
BASE = os.path.dirname(os.path.abspath(__file__))
|
||||
SRC = os.path.join(BASE, "Avance-Balam-2026-09-16.xlsx")
|
||||
DST = os.path.join(BASE, "Avance-Balam-2026-10-02.xlsx")
|
||||
CSV_HORAS = os.path.join(BASE, "Seguimiento-horas.csv")
|
||||
|
||||
CAFE, GRIS = "331F0E", "595959"
|
||||
V_FILL, V_FONT = "C6EFCE", "006100"
|
||||
A_FILL, A_FONT = "FFEB9C", "9C6500"
|
||||
|
||||
C_NOM, C_INI, C_FIN, C_PCT, C_EST, C_TRAB, C_STAT = 1, 2, 3, 4, 5, 6, 7
|
||||
CORTE_ANTERIOR = 94.58 # corte del 16-sep
|
||||
MARCA = "Corte 2-oct" # nota de las filas nuevas del CSV (incluye fechas del 9/10-sep)
|
||||
SIN = "—"
|
||||
|
||||
# --- CSV: totales por etapa y por actividad -----------------------------------
|
||||
por_act, por_etapa, nuevas_act = defaultdict(float), defaultdict(float), defaultdict(float)
|
||||
nuevas, desde_6ago = 0.0, 0.0
|
||||
with open(CSV_HORAS, encoding="utf-8", newline="") as fh:
|
||||
for r in csv.DictReader(fh):
|
||||
if not (r.get("fecha") or "").strip():
|
||||
continue
|
||||
h = float(r["horas"])
|
||||
por_act[r["actividad_id"]] += h
|
||||
por_etapa[r["etapa"]] += h
|
||||
if MARCA in r["notas"]:
|
||||
nuevas += h
|
||||
nuevas_act[r["actividad_id"]] += h
|
||||
if r["fecha"] > "2026-08-06":
|
||||
desde_6ago += h
|
||||
|
||||
E0, E1, E3 = por_etapa["0"], por_etapa["1"], por_etapa["3"]
|
||||
AJ = por_act["AJ-01"]
|
||||
E2 = por_etapa["2"] - AJ
|
||||
TOTAL = E0 + E1 + E2 + AJ + E3
|
||||
assert abs(TOTAL - CORTE_ANTERIOR - nuevas) < 0.01, "El CSV no arranca del corte del 16-sep"
|
||||
|
||||
# Reparto CSV → filas del Excel (mismo criterio que el corte del 15-sep):
|
||||
FILA_POR_ACT = {
|
||||
"E1-05": "Demostración semanal", # gestión, bitácora, cortes
|
||||
"E2-03": "Conversión cotización→factura", # prefactura, timbrado, lote
|
||||
"E2-04": "Pruebas integrales", # prueba completa del 1-oct + manual de usuario
|
||||
"AJ-01": "Disparador por estatus", # webhook, vista previa, modal
|
||||
}
|
||||
sin_fila = set(nuevas_act) - set(FILA_POR_ACT)
|
||||
assert not sin_fila, f"Actividades nuevas sin fila en el Excel: {sin_fila}"
|
||||
DELTAS_TRAB = defaultdict(float)
|
||||
for a, h in nuevas_act.items():
|
||||
DELTAS_TRAB[FILA_POR_ACT[a]] += h
|
||||
# Reasignación entre filas (suma cero): el tipo de cambio se resolvió el 12-ago dentro del
|
||||
# bloque de cotización (CSV 2026-08-12, E2-02: "tipo de cambio, prefill y selector de cliente").
|
||||
DELTAS_TRAB["Creación de cotizaciones desde la plataforma"] -= 0.25
|
||||
DELTAS_TRAB["Tipo de cambio del DOF"] += 0.25
|
||||
# Reclasificación del 2-oct (decisión de Johann, suma cero): las 7.75 h de cotización contra el API
|
||||
# de BIND que el corte del 15-sep cargó al ajuste Jira pasan a la Etapa 2 (en el CSV, AJ-01 → E2-02).
|
||||
# Disparador −4.50: sondeo de FAC-106 y diagnóstico del 500 (20-ago 1.50), investigación hasta el folio 1026 (21-ago 3.00)
|
||||
# Trazabilidad −3.25: sesión 12-ago 1.00, Excel de catálogos 0.75, sesiones 21-ago 0.75 y 3-sep 0.75
|
||||
DELTAS_TRAB["Disparador por estatus"] -= 4.50
|
||||
DELTAS_TRAB["Trazabilidad ticket"] -= 3.25
|
||||
DELTAS_TRAB["Creación de cotizaciones desde la plataforma"] += 7.75
|
||||
# 20-ago: de las 2.00 h de "bitácora", 1.00 h fue el registro #65 de la sesión de cotización y el guion del 21-ago
|
||||
# (commits a03f81f, cabe0fe, 072a3cd) → se dividió en el CSV (E1-05 1.00 + E2-02 1.00) y aquí sigue a su fila.
|
||||
DELTAS_TRAB["Demostración semanal"] -= 1.00
|
||||
DELTAS_TRAB["Creación de cotizaciones desde la plataforma"] += 1.00
|
||||
assert abs(sum(DELTAS_TRAB.values()) - nuevas) < 0.01, (
|
||||
f"El reparto ({sum(DELTAS_TRAB.values()):.2f}) no suma las {nuevas:.2f} h nuevas del CSV")
|
||||
|
||||
HORAS_ENCABEZADO = {
|
||||
"Integracion de plataformas Balam": TOTAL,
|
||||
"Etapa 0": E0,
|
||||
"Etapa 1": E1,
|
||||
"Etapa 2": E2,
|
||||
"» Ajuste dentro de la Etapa 2": AJ,
|
||||
"Etapa 3": E3,
|
||||
}
|
||||
ETAPAS = ("Etapa 0", "Etapa 1", "Etapa 2", "Etapa 3")
|
||||
AJUSTE = "» Ajuste dentro de la Etapa 2"
|
||||
AJUSTE_HIJOS = ("Cliente de la API de Jira", "Disparador por estatus", "Trazabilidad ticket")
|
||||
|
||||
# --- Fechas reales de las filas que cambian (dd/mm/yyyy) -----------------------
|
||||
FECHAS_REALES = {
|
||||
"Demostración semanal": ("15/07/2026", "02/10/2026"), # corte del 2-oct
|
||||
"Capa de escritura controlada": ("19/07/2026", "30/09/2026"), # diseño 19-jul → timbrado 30-sep
|
||||
"Disparador por estatus": ("29/09/2026", "30/09/2026"), # webhook (29-sep) → vista previa y modal (30-sep)
|
||||
"Trazabilidad ticket": ("10/08/2026", "02/10/2026"), # definiciones 10-ago → Pedro completa la cadena con CFDI 2-oct
|
||||
"Creación de cotizaciones desde la plataforma": ("06/08/2026", "30/09/2026"), # total desde BIND corregido 30-sep
|
||||
"Conversión cotización→factura": ("06/08/2026", "02/10/2026"), # puente 9/10-sep → lote 2-oct
|
||||
"Tipo de cambio del DOF": ("12/08/2026", "12/08/2026"), # TC del catálogo de monedas de BIND
|
||||
"Pruebas integrales": ("30/09/2026", "02/10/2026"), # recorrido e2e 30-sep, Balam 1-oct, manual 2-oct
|
||||
"Módulo de cobranza": ("21/07/2026", "01/08/2026"), # cartera por moneda (B4) → vista de vencidas
|
||||
}
|
||||
|
||||
# --- Avance de las filas que cambian -----------------------------------------
|
||||
AVANCE = {
|
||||
"Demostración semanal": 1.0,
|
||||
"Capa de escritura controlada": 1.0,
|
||||
"Configuración de infraestructura en Azure": 0.75,
|
||||
"CI/CD": 0.5,
|
||||
"Definición de alcance Jira": 1.0, # decía "falta el estatus disparador", viejo desde el 7-ago (REGISTRO #62)
|
||||
"Disparador por estatus": 1.0,
|
||||
"Trazabilidad ticket": 1.0, # 2-oct: Pedro timbró desde la plataforma, proceso completo
|
||||
"Detalle de factura": 1.0,
|
||||
"Catálogo de clientes": 1.0,
|
||||
"Tipo de cambio del DOF": 1.0,
|
||||
"Creación de cotizaciones desde la plataforma": 1.0,
|
||||
"Conversión cotización→factura": 1.0, # 2-oct: primer CFDI timbrado desde la plataforma
|
||||
"Exportación de facturas": 1.0,
|
||||
"Pruebas integrales": 1.0,
|
||||
"Ajustes de UI/UX de la emisión": 0.0,
|
||||
"Módulo de cobranza": 0.25,
|
||||
}
|
||||
|
||||
# --- Estatus: una línea de estado y viñetas cortas (todas las filas de detalle) --
|
||||
OK, CURSO, PROX = "✅ Completada", "🟡 En curso", "⚪ Próxima"
|
||||
ESTATUS = {
|
||||
# Etapa 0
|
||||
"Sesión de arranque (kickoff)": [OK],
|
||||
"Entrega y validación de accesos": [OK],
|
||||
"Validación técnica de la API de BIND": [OK],
|
||||
"Sesión de Discovery": [OK],
|
||||
"Prototipo visual navegable": [OK],
|
||||
"Documento de hallazgos": [OK],
|
||||
"Sesión de validación del prototipo": ["✅ Aprobado por Balam (24-jul)"],
|
||||
# Etapa 1
|
||||
"Repositorio privado GitHub": [OK],
|
||||
"Backend base": [OK],
|
||||
"Arquitectura multi-tenant": [OK],
|
||||
"Cliente de la API de BIND": [OK],
|
||||
"Demostración semanal": ["✅ Al día",
|
||||
"Cortes de horas y Excel de avance (último: 2-oct)",
|
||||
"Bitácora y comunicación con Balam",
|
||||
"Las sesiones con Pedro van en las filas técnicas"],
|
||||
"Sincronización de clientes": [OK],
|
||||
"Capa de escritura controlada": [OK,
|
||||
"Vista previa + confirmación humana + auditoría",
|
||||
"Cotización (21-ago), prefactura (29-sep) y timbrado (30-sep)",
|
||||
"Timbrado validado en real por Balam (2-oct)"],
|
||||
"Sesión de reglas y dudas": [OK],
|
||||
"Sincronización de facturas": [OK],
|
||||
"Modelo de facturas": [OK],
|
||||
"Definición de alcance Jira": [OK,
|
||||
"v1.3 aprobada por Noé (30-jul)",
|
||||
"Disparador y campos definidos por Balam (7-ago)"],
|
||||
"Configuración de infraestructura en Azure": [CURSO,
|
||||
"Balam ya dio el acceso",
|
||||
"Falta: volver a pedir la contraseña y desplegar",
|
||||
"Mientras tanto corre en paputec.mx (staging)"],
|
||||
"CI/CD": [CURSO,
|
||||
"Despliegue reproducible con respaldo y rollback",
|
||||
"Secretos fuera del repositorio",
|
||||
"Falta: el pipeline en Azure"],
|
||||
# Etapa 2 · ajuste Jira → BIND
|
||||
"Cliente de la API de Jira": [OK,
|
||||
"Bandeja y detalle de tickets FAC en vivo",
|
||||
"Adjuntos descargables"],
|
||||
"Trazabilidad ticket": [OK,
|
||||
"Cadena ticket → cotización → prefactura → CFDI visible",
|
||||
"Pedro completó el proceso con timbrado (2-oct)",
|
||||
"El tablero se alimenta en la Etapa 3"],
|
||||
"Disparador por estatus": [OK,
|
||||
"Webhook de Jira en producción (30-sep)",
|
||||
"Ticket detectado en ~1 s, cotización lista para aceptar",
|
||||
"De Balam: 7 categorías sin ítem en BIND"],
|
||||
# Etapa 2
|
||||
"Andamiaje del frontend": [OK],
|
||||
"Listado de facturas": [OK],
|
||||
"Detalle de factura": [OK,
|
||||
"Descarga del PDF",
|
||||
"El XML se consulta en BIND"],
|
||||
"Catálogo de clientes": [OK,
|
||||
"Catálogo y búsqueda",
|
||||
"Lista blanca eliminada por Balam (6-jul)"],
|
||||
"Exportación de facturas": [OK,
|
||||
"Facturas y clientes a CSV (abre en Excel)"],
|
||||
"Layout base": [OK],
|
||||
"Creación de cotizaciones desde la plataforma": [OK,
|
||||
"Individual y en lote",
|
||||
"Vista previa + confirmación humana"],
|
||||
"Conversión cotización→factura": [OK,
|
||||
"Prefactura validada con Balam (1-oct)",
|
||||
"Prefacturas en lote (2-oct)",
|
||||
"Pedro timbró desde la plataforma (2-oct)"],
|
||||
"Tipo de cambio del DOF": [OK,
|
||||
"Se toma del catálogo de monedas de BIND"],
|
||||
"Pruebas integrales": [OK,
|
||||
"Prueba completa con Balam (1-oct)",
|
||||
"Timbrado real desde la plataforma (2-oct)",
|
||||
"213 pruebas automáticas",
|
||||
"Manual de usuario en PDF (2-oct)"],
|
||||
# Etapa 3
|
||||
"Módulo de cobranza": [CURSO,
|
||||
"Cartera por moneda con vencidos",
|
||||
"Falta: rangos 30/60/90 y vista por vencer"],
|
||||
"Endurecimiento de seguridad": [CURSO,
|
||||
"Rate limit y bloqueo de login",
|
||||
"CSP/HSTS y respaldos diarios"],
|
||||
"Documentación técnica": [CURSO,
|
||||
"Runbook de publicación, operación y rollback"],
|
||||
"Alertas internas": [PROX],
|
||||
"Tablero directivo": [PROX],
|
||||
"Reporte operativo": [PROX],
|
||||
"Sesión de capacitación": [PROX],
|
||||
"Soporte posterior": ["⚪ Incluido en el cierre, sin costo"],
|
||||
"Ajustes de UI/UX de la emisión": [PROX,
|
||||
"Botones «Ver en Jira» / «Ver en BIND»",
|
||||
"Pantallas de Solicitudes, Cotizaciones y Facturas"],
|
||||
}
|
||||
ANCHO_ESTATUS = 46 # ancho de la columna G (caracteres)
|
||||
|
||||
|
||||
def texto_estatus(lineas):
|
||||
return "\n".join([lineas[0]] + [f"• {l}" for l in lineas[1:]])
|
||||
|
||||
|
||||
def alto_fila(*textos_y_anchos):
|
||||
"""Alto en puntos para que quepa el texto envuelto (fuente 9-10 pt)."""
|
||||
renglones = 1
|
||||
for texto, ancho in textos_y_anchos:
|
||||
n = sum(max(1, -(-len(l) // max(1, int(ancho * 1.05)))) for l in str(texto or "").split("\n"))
|
||||
renglones = max(renglones, n)
|
||||
return max(15, 12 * renglones + 4)
|
||||
|
||||
|
||||
def pinta(celda, fill, font_color, bold=False, size=9, align="left"):
|
||||
celda.fill = PatternFill("solid", fgColor=fill)
|
||||
celda.font = Font(color=font_color, bold=bold, size=size)
|
||||
celda.alignment = Alignment(horizontal=align, vertical="center", wrap_text=True)
|
||||
|
||||
|
||||
def fecha(v):
|
||||
return datetime.strptime(v, "%d/%m/%Y") if isinstance(v, str) and v.count("/") == 2 else None
|
||||
|
||||
|
||||
def estimadas(v):
|
||||
"""Punto medio de 'Horas estimadas' ('2', '4–5', '1.5–2'); 0 si no aplica ('—')."""
|
||||
if isinstance(v, (int, float)):
|
||||
return float(v)
|
||||
partes = [p for p in str(v or "").replace(" ", "").split("–") if p]
|
||||
try:
|
||||
nums = [float(p) for p in partes]
|
||||
except ValueError:
|
||||
return 0.0
|
||||
return sum(nums) / len(nums) if nums else 0.0
|
||||
|
||||
|
||||
wb = openpyxl.load_workbook(SRC)
|
||||
ws = wb.active
|
||||
|
||||
ws["A2"] = ("Plan y avance al 2-oct-2026 · webhook de Jira FAC en producción con vista previa y modal 'Aceptar', "
|
||||
"prefactura por la web app de BIND, timbrado desde la plataforma y prefacturas en lote · horas del "
|
||||
"10-ago al 2-oct reconstruidas, pendientes de validar")
|
||||
ws["A2"].font = Font(size=10, italic=True, color=GRIS)
|
||||
|
||||
# Notas del corte anterior: se limpian y se reescriben abajo.
|
||||
primera_nota = None
|
||||
for row in range(5, ws.max_row + 1):
|
||||
v = str(ws.cell(row=row, column=C_NOM).value or "")
|
||||
if v.startswith("✅ Completada ·"):
|
||||
primera_nota = row
|
||||
if primera_nota and row >= primera_nota:
|
||||
for rango in list(ws.merged_cells.ranges):
|
||||
if rango.min_row == row:
|
||||
ws.unmerge_cells(str(rango))
|
||||
ws.cell(row=row, column=C_NOM).value = None
|
||||
tope = primera_nota if primera_nota else ws.max_row + 1
|
||||
|
||||
# Fila nueva (Johann, 2-oct): ajustes de UI/UX pendientes de la Etapa 2, al final de la etapa. Toma el
|
||||
# formato de «Pruebas integrales», que en el corte del 16-sep era una fila ⚪ sin iniciar.
|
||||
UIUX = "Ajustes de UI/UX de la emisión: «Ver en Jira» / «Ver en BIND» y pantallas de Solicitudes, Cotizaciones y Facturas"
|
||||
assert not any(str(ws.cell(row=r, column=C_NOM).value or "").startswith("Ajustes de UI/UX") for r in range(5, tope))
|
||||
modelo = next(r for r in range(5, tope) if str(ws.cell(row=r, column=C_NOM).value or "").startswith("Pruebas integrales"))
|
||||
nueva = next(r for r in range(modelo + 1, tope) if str(ws.cell(row=r, column=C_NOM).value or "").startswith("Etapa 3"))
|
||||
ws.insert_rows(nueva)
|
||||
for c, valor in enumerate((UIUX, SIN, SIN, 0, "1–2", SIN, None), start=1):
|
||||
cel = ws.cell(row=nueva, column=c, value=valor)
|
||||
cel._style = copy(ws.cell(row=modelo, column=c)._style)
|
||||
tope += 1
|
||||
filas_tabla = [r for r in range(5, tope) if str(ws.cell(row=r, column=C_NOM).value or "").strip()]
|
||||
assert not any(rg.min_row in filas_tabla for rg in ws.merged_cells.ranges), "Hay celdas combinadas en la tabla"
|
||||
|
||||
|
||||
def nombre_de(row):
|
||||
return str(ws.cell(row=row, column=C_NOM).value or "").strip()
|
||||
|
||||
|
||||
def encabezado_de(nombre):
|
||||
return next((p for p in HORAS_ENCABEZADO if nombre.startswith(p)), None)
|
||||
|
||||
|
||||
# --- 1. Valores de las filas de detalle ----------------------------------------
|
||||
aplicado = defaultdict(set)
|
||||
for row in filas_tabla:
|
||||
nombre = nombre_de(row)
|
||||
if nombre.startswith(AJUSTE) and "(en aprobación, v1.3)" in nombre:
|
||||
ws.cell(row=row, column=C_NOM, value=nombre.replace("(en aprobación, v1.3)", "(aprobado en la v1.3, 30-jul)"))
|
||||
if encabezado_de(nombre):
|
||||
continue
|
||||
for prefijo, (ini, fin) in FECHAS_REALES.items():
|
||||
if nombre.startswith(prefijo):
|
||||
ws.cell(row=row, column=C_INI, value=ini)
|
||||
ws.cell(row=row, column=C_FIN, value=fin)
|
||||
aplicado["fechas"].add(prefijo)
|
||||
for prefijo, delta in DELTAS_TRAB.items():
|
||||
if nombre.startswith(prefijo):
|
||||
actual = ws.cell(row=row, column=C_TRAB).value
|
||||
actual = float(actual) if isinstance(actual, (int, float)) else 0.0
|
||||
c = ws.cell(row=row, column=C_TRAB, value=round(actual + delta, 2))
|
||||
c.number_format = "0.00"
|
||||
aplicado["horas"].add(prefijo)
|
||||
for prefijo, pct in AVANCE.items():
|
||||
if nombre.startswith(prefijo):
|
||||
p = ws.cell(row=row, column=C_PCT, value=pct)
|
||||
p.number_format = "0%"
|
||||
pinta(p, V_FILL if pct >= 1 else A_FILL, V_FONT if pct >= 1 else A_FONT, bold=True, size=10,
|
||||
align="center")
|
||||
aplicado["avance"].add(prefijo)
|
||||
for prefijo, lineas in ESTATUS.items():
|
||||
if nombre.startswith(prefijo):
|
||||
cel = ws.cell(row=row, column=C_STAT, value=texto_estatus(lineas))
|
||||
if lineas[0].startswith("✅"):
|
||||
pinta(cel, V_FILL, V_FONT)
|
||||
elif lineas[0].startswith("🟡"):
|
||||
pinta(cel, A_FILL, A_FONT)
|
||||
else: # ⚪ conserva su formato; solo envuelve el texto
|
||||
cel.alignment = Alignment(horizontal="left", vertical="center", wrap_text=True)
|
||||
ws.row_dimensions[row].height = alto_fila((nombre, 56), (cel.value, ANCHO_ESTATUS))
|
||||
aplicado["estatus"].add(prefijo)
|
||||
break
|
||||
for col in (C_INI, C_FIN):
|
||||
cel = ws.cell(row=row, column=col)
|
||||
cel.alignment = Alignment(horizontal="center", vertical="center")
|
||||
cel.font = Font(size=9, color=GRIS) if cel.value == SIN else Font(size=9)
|
||||
|
||||
sin_estatus = [nombre_de(r) for r in filas_tabla if not encabezado_de(nombre_de(r))
|
||||
and not any(nombre_de(r).startswith(p) for p in ESTATUS)]
|
||||
assert not sin_estatus, f"Filas sin estatus: {sin_estatus}"
|
||||
for clave, esperado in (("horas", DELTAS_TRAB), ("fechas", FECHAS_REALES), ("avance", AVANCE), ("estatus", ESTATUS)):
|
||||
faltan = set(esperado) - aplicado[clave]
|
||||
assert not faltan, f"Filas no encontradas en el Excel ({clave}): {faltan}"
|
||||
|
||||
# La fila "Demostración semanal (viernes) + reporte de horas" no tiene demostraciones: las sesiones con Pedro
|
||||
# están en las filas técnicas. Se renombra para que diga lo que contiene (Johann, 2-oct).
|
||||
GESTION = "Seguimiento y gestión: reportes de horas, bitácora y comunicación con Balam"
|
||||
renombradas = 0
|
||||
for row in filas_tabla:
|
||||
if nombre_de(row).startswith("Demostración semanal"):
|
||||
ws.cell(row=row, column=C_NOM, value=GESTION)
|
||||
renombradas += 1
|
||||
assert renombradas == 1
|
||||
|
||||
# --- 2. Grupos: etapa → (bloque del ajuste) + filas ----------------------------
|
||||
grupos = [] # [(fila_encabezado, [filas]), ...]; el ajuste es su propio grupo dentro de la Etapa 2
|
||||
for row in filas_tabla:
|
||||
enc = encabezado_de(nombre_de(row))
|
||||
if enc:
|
||||
grupos.append((row, []))
|
||||
else:
|
||||
grupos[-1][1].append(row)
|
||||
# Las filas del ajuste aparecen justo después de su encabezado; las que siguen son de la Etapa 2.
|
||||
for i, (enc_row, hijos) in enumerate(grupos):
|
||||
if nombre_de(enc_row).startswith(AJUSTE):
|
||||
propios = [r for r in hijos if nombre_de(r).startswith(AJUSTE_HIJOS)]
|
||||
resto = [r for r in hijos if r not in propios]
|
||||
assert len(propios) == len(AJUSTE_HIJOS), "No se encontraron las filas del ajuste"
|
||||
grupos[i] = (enc_row, propios)
|
||||
grupos.insert(i + 1, ("etapa2-resto", resto))
|
||||
break
|
||||
|
||||
|
||||
def clave_cronologica(row):
|
||||
ini, fin = fecha(ws.cell(row=row, column=C_INI).value), fecha(ws.cell(row=row, column=C_FIN).value)
|
||||
return (ini is None, ini or datetime.max, fin or datetime.max, row)
|
||||
|
||||
|
||||
def promedio(filas):
|
||||
pesos = [(estimadas(ws.cell(row=r, column=C_EST).value), ws.cell(row=r, column=C_PCT).value or 0) for r in filas]
|
||||
total = sum(p for p, _ in pesos)
|
||||
return round(sum(p * float(v) for p, v in pesos) / total, 2) if total else 0.0
|
||||
|
||||
|
||||
def rango_fechas(filas):
|
||||
inis = [fecha(ws.cell(row=r, column=C_INI).value) for r in filas]
|
||||
fins = [fecha(ws.cell(row=r, column=C_FIN).value) for r in filas]
|
||||
inis, fins = [d for d in inis if d], [d for d in fins if d]
|
||||
return (min(inis).strftime("%d/%m/%Y") if inis else SIN, max(fins).strftime("%d/%m/%Y") if fins else SIN)
|
||||
|
||||
|
||||
# --- 3. Encabezados: horas del CSV, avance ponderado y fechas de sus filas ------
|
||||
hojas = {} # encabezado → filas de detalle que lo componen (para avance y fechas)
|
||||
etapa_actual = None
|
||||
for enc_row, hijos in grupos:
|
||||
if enc_row == "etapa2-resto":
|
||||
hojas[etapa_actual]["avance"] += hijos
|
||||
hojas[etapa_actual]["fechas"] += hijos
|
||||
continue
|
||||
enc = encabezado_de(nombre_de(enc_row))
|
||||
hojas[enc] = {"row": enc_row, "avance": list(hijos), "fechas": list(hijos)}
|
||||
if enc in ETAPAS:
|
||||
etapa_actual = enc
|
||||
elif enc == AJUSTE:
|
||||
hojas[etapa_actual]["fechas"] += hijos # el ajuste cuenta para las fechas de la Etapa 2, no para su avance
|
||||
todas = [r for g in hojas.values() for r in g["avance"]]
|
||||
hojas["Integracion de plataformas Balam"]["avance"] = todas
|
||||
hojas["Integracion de plataformas Balam"]["fechas"] = todas
|
||||
|
||||
for enc, g in hojas.items():
|
||||
row, pct = g["row"], promedio(g["avance"])
|
||||
ini, fin = rango_fechas(g["fechas"])
|
||||
ws.cell(row=row, column=C_INI, value=ini)
|
||||
ws.cell(row=row, column=C_FIN, value=fin)
|
||||
c = ws.cell(row=row, column=C_TRAB, value=round(HORAS_ENCABEZADO[enc], 2))
|
||||
c.number_format = "0.00"
|
||||
p = ws.cell(row=row, column=C_PCT, value=pct)
|
||||
p.number_format = "0%"
|
||||
pinta(p, V_FILL if pct >= 1 else A_FILL, V_FONT if pct >= 1 else A_FONT, bold=True, size=10, align="center")
|
||||
g["pct"] = pct
|
||||
|
||||
# --- 4. Orden cronológico dentro de cada grupo ----------------------------------
|
||||
orden = []
|
||||
for enc_row, hijos in grupos:
|
||||
if enc_row != "etapa2-resto":
|
||||
orden.append(enc_row)
|
||||
orden += sorted(hijos, key=clave_cronologica)
|
||||
assert sorted(orden) == filas_tabla
|
||||
|
||||
foto = {r: ([(ws.cell(row=r, column=c).value, copy(ws.cell(row=r, column=c)._style)) for c in range(1, 8)],
|
||||
ws.row_dimensions[r].height) for r in filas_tabla}
|
||||
for destino, origen in zip(filas_tabla, orden):
|
||||
celdas, alto = foto[origen]
|
||||
for c, (valor, estilo) in enumerate(celdas, start=1):
|
||||
cel = ws.cell(row=destino, column=c)
|
||||
cel.value = valor
|
||||
cel._style = copy(estilo)
|
||||
ws.row_dimensions[destino].height = alto
|
||||
|
||||
# --- 5. Cuadre: las filas de detalle suman el total del CSV ----------------------
|
||||
detalle = 0.0
|
||||
for row in filas_tabla:
|
||||
v = ws.cell(row=row, column=C_TRAB).value
|
||||
if not encabezado_de(nombre_de(row)) and isinstance(v, (int, float)):
|
||||
detalle += float(v)
|
||||
assert abs(detalle - TOTAL) < 0.01, f"Las filas suman {detalle:.2f} h y el CSV {TOTAL:.2f} h"
|
||||
|
||||
PCT = {enc: g["pct"] for enc, g in hojas.items()}
|
||||
# Sin notas debajo de la tabla (Johann, 2-oct): el estatus de cada fila lo dice todo.
|
||||
for r in range(tope, ws.max_row + 1):
|
||||
ws.row_dimensions[r].height = None
|
||||
ws.column_dimensions["G"].width = ANCHO_ESTATUS
|
||||
r = filas_tabla[-1]
|
||||
|
||||
ws.print_area = f"A1:G{r}"
|
||||
|
||||
# --- Hoja 2: desglose de horas, registro por registro (Johann, 2-oct: "quiero poder justificar todo") ----
|
||||
TARIFA = 600
|
||||
AMARILLO, CREMA = "F7BD0C", "FDF3D8"
|
||||
ACTIVIDADES = {
|
||||
"E0-01": "Kickoff y preparación", "E0-02": "Discovery de facturación y cobranza",
|
||||
"E0-03": "Accesos y salvaguardas", "E0-04": "Validación técnica del API de BIND",
|
||||
"E0-05": "Prototipo navegable", "E0-06": "Entregable de cierre de la Etapa 0",
|
||||
"E0-07": "Validación del prototipo con Balam",
|
||||
"E1-01": "Backend base, autenticación, roles y sincronización", "E1-02": "Repositorio",
|
||||
"E1-03": "Arquitectura multi-tenant y RLS", "E1-04": "Cliente del API de BIND",
|
||||
"E1-05": "Seguimiento y gestión", "E1-06": "Entorno local y andamiaje",
|
||||
"E1-07": "Sesión del API de Jira", "E1-08": "Diseño y planeación",
|
||||
"E1-09": "Publicación provisional (paputec.mx)",
|
||||
"E2-01": "Pantallas de consulta", "E2-02": "Creación de cotizaciones",
|
||||
"E2-03": "Prefactura y timbrado", "E2-04": "Pruebas integrales y manual",
|
||||
"AJ-01": "Integración Jira → BIND",
|
||||
"E3-01": "Endurecimiento de seguridad", "E3-02": "Documentación técnica",
|
||||
}
|
||||
ESTADOS = {"confirmada": "Documentada", "validada": "Validada", "reconstruida": "Reconstruida"}
|
||||
ETAPA_NOMBRE = {"0": "Etapa 0", "1": "Etapa 1", "2": "Etapa 2", "3": "Etapa 3"}
|
||||
|
||||
with open(CSV_HORAS, encoding="utf-8", newline="") as fh:
|
||||
registros = [x for x in csv.DictReader(fh) if (x.get("fecha") or "").strip()]
|
||||
sin_nombre = {x["actividad_id"] for x in registros} - set(ACTIVIDADES)
|
||||
assert not sin_nombre, f"Actividades sin nombre en el desglose: {sin_nombre}"
|
||||
|
||||
|
||||
def etapa_de(x):
|
||||
return "Ajuste Jira → BIND" if x["actividad_id"] == "AJ-01" else ETAPA_NOMBRE[x["etapa"]]
|
||||
|
||||
|
||||
if "Desglose de horas" in wb.sheetnames:
|
||||
del wb["Desglose de horas"]
|
||||
wd = wb.create_sheet("Desglose de horas")
|
||||
wd.sheet_view.showGridLines = False
|
||||
for col, ancho in zip("ABCDEFGH", (11, 18, 30, 72, 8, 12, 14, 46)):
|
||||
wd.column_dimensions[col].width = ancho
|
||||
|
||||
wd["A1"] = "Proyecto Balam · Desglose de horas trabajadas"
|
||||
wd["A1"].font = Font(size=14, bold=True, color=CAFE)
|
||||
wd["A2"] = (f"Cada registro con su fecha, actividad y evidencia · tarifa ${TARIFA}/h + IVA · corte al 2-oct-2026 · "
|
||||
"Documentada = duración comprobada (transcript o sesión) · Validada = confirmada por Johann · "
|
||||
"Reconstruida = armada con evidencia fechada, pendiente de validar")
|
||||
wd["A2"].font = Font(size=9, italic=True, color=GRIS)
|
||||
wd["A2"].alignment = Alignment(wrap_text=True, vertical="top")
|
||||
wd.merge_cells("A2:H2")
|
||||
wd.row_dimensions[2].height = 28
|
||||
|
||||
borde = Border(bottom=Side(style="thin", color="E4E2DC"))
|
||||
|
||||
|
||||
def encabezado(fila, textos):
|
||||
for c, t in enumerate(textos, start=1):
|
||||
cel = wd.cell(row=fila, column=c, value=t)
|
||||
cel.font = Font(size=10, bold=True, color=CAFE)
|
||||
cel.fill = PatternFill("solid", fgColor=AMARILLO)
|
||||
cel.alignment = Alignment(horizontal="center", vertical="center", wrap_text=True)
|
||||
wd.row_dimensions[fila].height = 20
|
||||
|
||||
|
||||
# Resumen por etapa (cuadra con la hoja principal)
|
||||
encabezado(4, ["Etapa", "", "Registros", "", "Horas", "Monto (MXN)", "", ""])
|
||||
orden_etapas = ["Etapa 0", "Etapa 1", "Etapa 2", "Ajuste Jira → BIND", "Etapa 3"]
|
||||
esperado = {"Etapa 0": E0, "Etapa 1": E1, "Etapa 2": E2, "Ajuste Jira → BIND": AJ, "Etapa 3": E3}
|
||||
fila = 5
|
||||
for et in orden_etapas:
|
||||
regs = [x for x in registros if etapa_de(x) == et]
|
||||
horas = sum(float(x["horas"]) for x in regs)
|
||||
assert abs(horas - esperado[et]) < 0.01, f"{et}: desglose {horas:.2f} h vs hoja principal {esperado[et]:.2f} h"
|
||||
for c, v in ((1, et), (3, len(regs)), (5, round(horas, 2)), (6, round(horas * TARIFA, 2))):
|
||||
cel = wd.cell(row=fila, column=c, value=v)
|
||||
cel.font = Font(size=10, color=CAFE)
|
||||
cel.border = borde
|
||||
wd.cell(row=fila, column=5).number_format = "0.00"
|
||||
wd.cell(row=fila, column=6).number_format = '"$"#,##0.00'
|
||||
fila += 1
|
||||
for c, v in ((1, "Total"), (3, len(registros)), (5, round(TOTAL, 2)), (6, round(TOTAL * TARIFA, 2))):
|
||||
cel = wd.cell(row=fila, column=c, value=v)
|
||||
cel.font = Font(size=10, bold=True, color=CAFE)
|
||||
cel.fill = PatternFill("solid", fgColor=CREMA)
|
||||
wd.cell(row=fila, column=5).number_format = "0.00"
|
||||
wd.cell(row=fila, column=6).number_format = '"$"#,##0.00'
|
||||
|
||||
# Detalle, en orden cronológico
|
||||
cab = fila + 3
|
||||
encabezado(cab, ["Fecha", "Etapa", "Actividad", "Qué se hizo", "Horas", "Monto (MXN)", "Estado", "Evidencia"])
|
||||
fila = cab + 1
|
||||
for x in registros:
|
||||
h = float(x["horas"])
|
||||
valores = (datetime.strptime(x["fecha"], "%Y-%m-%d"), etapa_de(x), ACTIVIDADES[x["actividad_id"]],
|
||||
x["actividad"], h, round(h * TARIFA, 2), ESTADOS.get(x["estado_hora"], x["estado_hora"]),
|
||||
x["fuente_evidencia"])
|
||||
for c, v in enumerate(valores, start=1):
|
||||
cel = wd.cell(row=fila, column=c, value=v)
|
||||
cel.font = Font(size=9, color=CAFE)
|
||||
cel.alignment = Alignment(vertical="top", wrap_text=True,
|
||||
horizontal="center" if c in (1, 7) else ("right" if c in (5, 6) else "left"))
|
||||
cel.border = borde
|
||||
wd.cell(row=fila, column=1).number_format = "dd/mm/yyyy"
|
||||
wd.cell(row=fila, column=5).number_format = "0.00"
|
||||
wd.cell(row=fila, column=6).number_format = '"$"#,##0.00'
|
||||
if x["estado_hora"] == "reconstruida":
|
||||
wd.cell(row=fila, column=7).font = Font(size=9, color=A_FONT)
|
||||
renglones = max(-(-len(x["actividad"]) // 80), -(-len(x["fuente_evidencia"]) // 50), -(-len(ACTIVIDADES[x["actividad_id"]]) // 32), 1)
|
||||
wd.row_dimensions[fila].height = 12 * renglones + 4
|
||||
fila += 1
|
||||
assert abs(sum(float(x["horas"]) for x in registros) - TOTAL) < 0.01
|
||||
|
||||
wd.freeze_panes = f"A{cab + 1}"
|
||||
wd.auto_filter.ref = f"A{cab}:H{fila - 1}"
|
||||
wd.page_setup.orientation = "landscape"
|
||||
wd.page_setup.fitToWidth = 1
|
||||
wd.page_setup.fitToHeight = 0
|
||||
wd.sheet_properties.pageSetUpPr.fitToPage = True
|
||||
wd.print_title_rows = f"{cab}:{cab}"
|
||||
wb.active = 0
|
||||
wb.save(DST)
|
||||
print("OK ->", DST)
|
||||
print(f" E0 {E0:.2f} · E1 {E1:.2f} · E2 {E2:.2f} · Jira {AJ:.2f} · E3 {E3:.2f} · TOTAL {TOTAL:.2f} h "
|
||||
f"({nuevas:.2f} nuevas)")
|
||||
print(" avance:", " · ".join(f"{k} {v:.0%}" for k, v in PCT.items()))
|
||||
print(" reparto:", ", ".join(f"{k} {v:+.2f}" for k, v in DELTAS_TRAB.items()))
|
||||
@@ -0,0 +1,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
|
||||