From 7f86497314b3fc7f425b35f32a0c0811b03d25af Mon Sep 17 00:00:00 2001 From: Johann Date: Sat, 1 Aug 2026 20:41:07 -0600 Subject: [PATCH] =?UTF-8?q?Corte=20de=20horas=20al=201-ago=20(52.58=20h):?= =?UTF-8?q?=20captura=20validada=20del=2028-jul=E2=86=921-ago,=20Excel=20d?= =?UTF-8?q?e=20avance=20regenerado=20desde=20script=20(la=20versi=C3=B3n?= =?UTF-8?q?=20enviada=20a=20Erika=20queda=20respaldada=20localmente=20en?= =?UTF-8?q?=20respaldos/)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Fable 5 --- planeacion/Avance-Balam-2026-07-28.xlsx | Bin 9975 -> 9975 bytes planeacion/Avance-Balam-2026-08-01.xlsx | Bin 0 -> 10286 bytes planeacion/Seguimiento-horas.csv | 9 + planeacion/generar_avance_2026_08_01.py | 214 ++++++++++++++++++++++++ 4 files changed, 223 insertions(+) create mode 100644 planeacion/Avance-Balam-2026-08-01.xlsx create mode 100644 planeacion/generar_avance_2026_08_01.py diff --git a/planeacion/Avance-Balam-2026-07-28.xlsx b/planeacion/Avance-Balam-2026-07-28.xlsx index e81ae1445690b937c9de0f778b903f7f5862730f..754c54fda5d79c099e6f8c23ff6593d573af0b4e 100644 GIT binary patch delta 437 zcmezF``wo>z?+#xgn@y9gW>iP#@LB``#C^#PwkRx6R#@N`yDpmX^T%iwYT)vArsd~ zz1bFxEk{%YwrhOcmfh|{DylpwM9CmnE z7BFj)!c&OIONGgdVAkXYrH2rqRAn!y&}J1Yc#uy1s$vdNXrO8fVHB!5K^WUrp;Dhz UO(7!MYL*a2hMEh6u|drp0C4}o00000 delta 437 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 wJpT{4jQnr#|1s= 1: + pinta(p, V_FILL, V_FONT, bold=True, size=10, align="center") + elif pct > 0: + pinta(p, A_FILL, A_FONT, bold=True, size=10, align="center") + break + + for prefijo, delta in DELTAS_TRAB.items(): + if nombre.startswith(prefijo): + actual = ws.cell(row=row, column=C_TRAB).value + actual = float(actual) if isinstance(actual, (int, float)) else 0.0 + c = ws.cell(row=row, column=C_TRAB, value=round(actual + delta, 2)) + c.number_format = "0.00" + aplicado.add(prefijo) + break + + for prefijo, (pct, txt, fill, fcolor, bold) in FILAS.items(): + if nombre.startswith(prefijo): + if pct is not None: + p = ws.cell(row=row, column=C_PCT, value=pct) + p.number_format = "0%" + if pct >= 1: + pinta(p, V_FILL, V_FONT, bold=True, size=10, align="center") + elif pct > 0: + pinta(p, A_FILL, A_FONT, bold=True, size=10, align="center") + else: + p.font = Font(size=10, color=GRIS) + s = ws.cell(row=row, column=C_STAT, value=txt) + pinta(s, fill, fcolor, bold) + break + +faltan = set(DELTAS_TRAB) - aplicado +assert not faltan, f"Filas no encontradas en el Excel: {faltan}" + +# --- Notas: se reemplaza el bloque del 28-jul --------------------------------- +primera_nota = None +for row in range(5, ws.max_row + 1): + v = str(ws.cell(row=row, column=1).value or "") + if v.startswith("✅ Completada ·"): + primera_nota = row + if primera_nota and row >= primera_nota: + ws.cell(row=row, column=1).value = None + +notas = [ + ("✅ Completada · 🟡 En curso · 🔴 Pendiente/bloqueada · ⚪ Próxima · " + "(adelantada) = ejecutada antes de su fecha plan", True), + (f"Horas trabajadas al corte del sábado 1-ago: Etapa 0 {E0:.2f} h (cerrada y validada) · " + f"Etapa 1 {E1:.2f} h (rango 32–39) · Etapa 2 {E2:.2f} h · ajuste Jira {AJ:.2f} h " + f"(de 7–10 aprobadas) · total {TOTAL:.2f} h. Base de facturación a $600/h + IVA.", True), + ("Etapa 1: 7 de 8 entregables contractuales terminados. El 1-ago se cerraron el " + "seeder de datos de demostración, la verificación real del aislamiento RLS, el " + "sincronizador automático (cada 15 min) y la suite de pruebas de integración " + "(61 pruebas). Solo queda la capa de escritura, en pausa por la definición del " + "flujo de facturación.", False), + ("Etapa 2: el frontend de CONSULTA quedó adelantado al 1-ago (inicio de sesión por " + "roles, cartera por moneda, listado de facturas con filtros/búsqueda/paginación, " + "detalle con descarga del PDF del CFDI y catálogo de clientes con exportación). " + "Las filas de emisión (cotizaciones y conversión) siguen sujetas al flujo definido.", False), + ("Integración Jira → BIND (7–10 h, v1.3 aprobada por el Ing. Noé el 30-jul): el " + "cliente técnico de lectura quedó construido y probado; el disparador y la " + "trazabilidad esperan las definiciones de negocio (request type, estatus " + "disparador y origen de monto/conceptos) y la prueba de escritura acompañada.", False), + ("En espera de Balam: acceso a Azure (grupo de recursos con Contributor — con el " + "Ing. Noé desde el 28-jul; bloquea ambiente, staging y CI/CD), fecha de la sesión " + "del flujo de facturación, y las tres definiciones de la integración Jira.", False), +] +r = primera_nota if primera_nota else ws.max_row + 2 +for txt, bold in notas: + c = ws.cell(row=r, column=1, value=txt) + c.font = Font(italic=True, size=9, color=CAFE, bold=bold) + c.alignment = Alignment(wrap_text=True, vertical="top") + try: + ws.merge_cells(start_row=r, start_column=1, end_row=r, end_column=7) + except ValueError: + pass # ya estaba fusionada desde el archivo origen + ws.row_dimensions[r].height = 28 if len(txt) > 120 else 14 + r += 1 + +ws.print_area = f"A1:G{r}" +wb.save(DST) + +print("OK ->", DST) +print(f" E0 {E0:.2f} · E1 {E1:.2f} · E2 {E2:.2f} · Jira {AJ:.2f} · TOTAL {TOTAL:.2f} h " + f"· avance global {PCT_GLOBAL:.0%}")