diff --git a/planeacion/Avance-Balam-2026-08-06b.xlsx b/planeacion/Avance-Balam-2026-08-06b.xlsx new file mode 100644 index 0000000..59327fa Binary files /dev/null and b/planeacion/Avance-Balam-2026-08-06b.xlsx differ diff --git a/planeacion/Seguimiento-horas.csv b/planeacion/Seguimiento-horas.csv index 6148c77..cafae99 100644 --- a/planeacion/Seguimiento-horas.csv +++ b/planeacion/Seguimiento-horas.csv @@ -46,3 +46,4 @@ fecha,semana,etapa,actividad_id,actividad,horas,estado_hora,fuente_evidencia,fac 2026-08-06,2026-W32,2,AJ-01,"Sondeo de customfields en FAC (IDs capturados y documentados), token a user-secrets y endpoints de lectura /api/jira/solicitudes (+converter de fechas de Jira)",0.50,confirmada,planeacion/Investigacion-API-Jira.md,si,"Sesión asistida por IA; tiempo real de sesión; commits 4ce5640 (privado) y e2c2d18 (cliente)" 2026-08-06,2026-W32,2,AJ-01,"Bandeja de solicitudes y detalle de ticket fieles al prototipo leyendo FAC en vivo (estatus reales, solo lectura) + adjuntos descargables vía proxy",0.50,confirmada,bitacora/PENDIENTES.md,si,"Sesión asistida por IA; tiempo real de sesión; commits e2c2d18 y 76ad7b1 (cliente)" 2026-08-06,2026-W32,2,E2-01,"Shell con el chrome del prototipo aplicado a toda la app (sidebar con grupos y logo oficial, breadcrumb, chips de sync y módulos próximamente)",0.25,confirmada,prototipo/Prototipo-Etapa0.html,si,"Sesión asistida por IA; tiempo real de sesión; commit e2c2d18 (cliente)" +2026-08-06,2026-W32,2,E2-01,"Wizard de emisión asistida (5 pasos, solo UI) fiel al prototipo: cotizaciones BIND reales seleccionables, datos de factura con PPD/PUE y alerta SAT, IVA por regla con totales reactivos, prefactura simulada con marca de agua y aprobación humana visual; timbrado bloqueado hasta B7",1.00,confirmada,prototipo/Prototipo-Etapa0.html,si,"Sesión asistida por IA; tiempo real de sesión; commits 2164f6c, 90f7633 y 0600f2f (cliente)" diff --git a/planeacion/generar_avance_2026_08_06b.py b/planeacion/generar_avance_2026_08_06b.py new file mode 100644 index 0000000..feb7983 --- /dev/null +++ b/planeacion/generar_avance_2026_08_06b.py @@ -0,0 +1,192 @@ +"""Genera Avance-Balam-2026-08-06b.xlsx a partir del corte de la tarde del 6-ago. + +Segundo corte del jueves 6-ago (sesión vespertina, previa a la demo del vie +7-ago): el wizard de emisión asistida quedó construido en la plataforma como +interfaz navegable de simulación — 5 pasos fieles al prototipo aprobado, con +las cotizaciones reales de BIND y el ticket FAC vivo (solo lectura), prefactura +simulada con marca de agua y aprobación humana visual. El timbrado queda +deshabilitado hasta que Balam autorice la escritura (B7). El corte de la +mañana (Avance-Balam-2026-08-06.xlsx) no se toca. + +Las horas se leen de `Seguimiento-horas.csv` (fuente de la verdad, corte +54.83 h); el script aborta si el reparto por fila no cuadra con el CSV. +""" +import csv +import os +from collections import defaultdict + +import openpyxl +from openpyxl.styles import Alignment, Font, PatternFill + +BASE = os.path.dirname(os.path.abspath(__file__)) +SRC = os.path.join(BASE, "Avance-Balam-2026-08-06.xlsx") +DST = os.path.join(BASE, "Avance-Balam-2026-08-06b.xlsx") +CSV_HORAS = os.path.join(BASE, "Seguimiento-horas.csv") + +CAFE, GRIS = "331F0E", "595959" +V_FILL, V_FONT = "C6EFCE", "006100" +A_FILL, A_FONT = "FFEB9C", "9C6500" +R_FILL, R_FONT = "FFC7CE", "9C0006" +G_FILL, G_FONT = "F2F2F2", "595959" + +C_PCT, C_EST, C_TRAB, C_STAT = 4, 5, 6, 7 + +# --- CSV: totales por etapa y por actividad ----------------------------------- +por_act, por_etapa = defaultdict(float), defaultdict(float) +with open(CSV_HORAS, encoding="utf-8", newline="") as fh: + for r in csv.DictReader(fh): + if not (r.get("fecha") or "").strip(): + continue + por_act[r["actividad_id"]] += float(r["horas"]) + por_etapa[r["etapa"]] += float(r["horas"]) + +E0, E1 = por_etapa["0"], por_etapa["1"] +AJ = por_act["AJ-01"] # ajuste Jira→BIND (vive dentro de la etapa 2 del CSV) +E2 = por_etapa["2"] - AJ # Etapa 2 propiamente (frontend) +TOTAL = E0 + E1 + E2 + AJ +assert abs(TOTAL - sum(por_etapa.values())) < 0.01 +PCT_GLOBAL = round(TOTAL / ((119 + 146) / 2), 2) + +# --- Reparto por fila del delta corte matutino→vespertino (1.00 h nueva) ------- +# Trazabilidad: +# E2-01 +1.00 (wizard de emisión asistida, 5 pasos en simulación) +# → fila "Conversión cotización→factura" (el wizard ES su interfaz; +# la conversión real sigue bloqueada por B7) +DELTAS_TRAB = { + "Conversión cotización→factura": 1.00, +} +assert abs(sum(DELTAS_TRAB.values()) - 1.00) < 0.01, "El reparto no suma la 1.00 h nueva" + +ENCABEZADOS = { + "Integracion de plataformas Balam": (TOTAL, PCT_GLOBAL), + "Etapa 0": (E0, 1.0), + "Etapa 1": (E1, 0.875), + "Etapa 2": (E2, 0.55), + "» Ajuste dentro de la Etapa 2": (AJ, 0.55), + "Etapa 3": (None, 0), +} + +# (pct, estatus, fill, font, bold) — solo lo que cambió respecto al corte matutino. +FILAS = { + "Conversión cotización→factura": (0.4, "🟡 Interfaz completa en simulación (6-ago): wizard de 5 pasos con " + "cotizaciones y tickets reales, prefactura simulada y aprobación humana " + "visual · el timbrado real espera la autorización de escritura (B7)", + A_FILL, A_FONT, False), +} + + +def pinta(celda, fill, font_color, bold=False, size=9, align="left"): + celda.fill = PatternFill("solid", fgColor=fill) + celda.font = Font(color=font_color, bold=bold, size=size) + celda.alignment = Alignment(horizontal=align, vertical="center", wrap_text=True) + + +wb = openpyxl.load_workbook(SRC) +ws = wb.active + +ws["A2"] = ("Plan y avance al 6-ago-2026 (2.º corte del día) · lectura de la bandeja Jira (FAC) " + "completa y wizard de emisión asistida en simulación; timbrado y escritura en espera de Balam") +ws["A2"].font = Font(size=10, italic=True, color=GRIS) + +# Las notas del corte anterior van fusionadas A:G y algunas empiezan con +# "Etapa 1: …", que colisiona con los prefijos de encabezado: se localizan y +# limpian ANTES del bucle principal, y este se detiene donde empezaban. +primera_nota = None +for row in range(5, ws.max_row + 1): + v = str(ws.cell(row=row, column=1).value or "") + if v.startswith("✅ Completada ·"): + primera_nota = row + if primera_nota and row >= primera_nota: + for rango in list(ws.merged_cells.ranges): + if rango.min_row == row: + ws.unmerge_cells(str(rango)) + ws.cell(row=row, column=1).value = None +tope = primera_nota if primera_nota else ws.max_row + 1 + +aplicado = set() +for row in range(5, tope): + nombre = str(ws.cell(row=row, column=1).value or "").strip() + if not nombre: + continue + + for prefijo, (trab, pct) in ENCABEZADOS.items(): + if nombre.startswith(prefijo): + if trab is not None: + c = ws.cell(row=row, column=C_TRAB, value=round(trab, 2)) + c.number_format = "0.00" + p = ws.cell(row=row, column=C_PCT, value=pct) + p.number_format = "0%" + if pct >= 1: + pinta(p, V_FILL, V_FONT, bold=True, size=10, align="center") + elif pct > 0: + pinta(p, A_FILL, A_FONT, bold=True, size=10, align="center") + break + + for prefijo, delta in DELTAS_TRAB.items(): + if nombre.startswith(prefijo): + actual = ws.cell(row=row, column=C_TRAB).value + actual = float(actual) if isinstance(actual, (int, float)) else 0.0 + c = ws.cell(row=row, column=C_TRAB, value=round(actual + delta, 2)) + c.number_format = "0.00" + aplicado.add(prefijo) + break + + for prefijo, (pct, txt, fill, fcolor, bold) in FILAS.items(): + if nombre.startswith(prefijo): + if pct is not None: + p = ws.cell(row=row, column=C_PCT, value=pct) + p.number_format = "0%" + if pct >= 1: + pinta(p, V_FILL, V_FONT, bold=True, size=10, align="center") + elif pct > 0: + pinta(p, A_FILL, A_FONT, bold=True, size=10, align="center") + else: + p.font = Font(size=10, color=GRIS) + s = ws.cell(row=row, column=C_STAT, value=txt) + pinta(s, fill, fcolor, bold) + break + +faltan = set(DELTAS_TRAB) - aplicado +assert not faltan, f"Filas no encontradas en el Excel: {faltan}" + +# --- Notas: se escribe el bloque vespertino donde estaba el matutino ---------- +notas = [ + ("✅ Completada · 🟡 En curso · 🔴 Pendiente/bloqueada · ⚪ Próxima · " + "(adelantada) = ejecutada antes de su fecha plan", True), + (f"Horas trabajadas al 2.º corte del jueves 6-ago: Etapa 0 {E0:.2f} h (cerrada y validada) · " + f"Etapa 1 {E1:.2f} h (rango 32–39) · Etapa 2 {E2:.2f} h · ajuste Jira {AJ:.2f} h " + f"(de 7–10 aprobadas) · total {TOTAL:.2f} h. Base de facturación a $600/h + IVA.", True), + ("Etapa 1: 7 de 8 entregables contractuales terminados. Solo queda la capa de " + "escritura, en pausa por la definición del flujo de facturación.", False), + ("Integración Jira → BIND (7–10 h, v1.3 aprobada por el Ing. Noé el 30-jul): la " + "LECTURA quedó completa el 6-ago — la plataforma muestra la bandeja de solicitudes " + "y el detalle de cada ticket de FAC en vivo (solo lectura), con los campos del " + "formulario mapeados y los adjuntos descargables. El disparador de cotización y la " + "trazabilidad esperan las definiciones de negocio (request type, estatus disparador " + "y origen de monto/conceptos) y la prueba de escritura acompañada.", False), + ("Etapa 2: al frontend de consulta y al chrome del prototipo se sumó el wizard de " + "emisión asistida (6-ago): 5 pasos sobre cotizaciones y tickets reales, con regla " + "PPD/PUE y advertencia SAT, IVA automático, prefactura simulada y aprobación humana " + "visual. El timbrado queda deshabilitado hasta que Balam autorice la escritura (B7).", False), + ("En espera de Balam: acceso a Azure (Contributor — con el Ing. Noé desde el " + "28-jul), fecha de la sesión del flujo de facturación, y las tres definiciones de " + "la integración Jira.", False), +] +r = primera_nota if primera_nota else ws.max_row + 2 +for txt, bold in notas: + c = ws.cell(row=r, column=1, value=txt) + c.font = Font(italic=True, size=9, color=CAFE, bold=bold) + c.alignment = Alignment(wrap_text=True, vertical="top") + try: + ws.merge_cells(start_row=r, start_column=1, end_row=r, end_column=7) + except ValueError: + pass # ya estaba fusionada desde el archivo origen + ws.row_dimensions[r].height = 28 if len(txt) > 120 else 14 + r += 1 + +ws.print_area = f"A1:G{r}" +wb.save(DST) + +print("OK ->", DST) +print(f" E0 {E0:.2f} · E1 {E1:.2f} · E2 {E2:.2f} · Jira {AJ:.2f} · TOTAL {TOTAL:.2f} h " + f"· avance global {PCT_GLOBAL:.0%}")