From c3e984944ecfa469aa3502035d7767f221c0bb01 Mon Sep 17 00:00:00 2001 From: Johann Date: Wed, 22 Jul 2026 00:06:54 -0600 Subject: [PATCH] =?UTF-8?q?Cierre=2021-jul:=20B4=20con=20sync=20hist=C3=B3?= =?UTF-8?q?rico=20verificado=20y=20B6=20aplicado;=20horas,=20bit=C3=A1cora?= =?UTF-8?q?=20y=20Excel=20de=20avance=20al=20d=C3=ADa?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- bitacora/PENDIENTES.md | 4 +- bitacora/REGISTRO.md | 81 +++++++++ planeacion/Agenda-semana-2026-07-20.md | 57 +++++++ .../Plan-actividades-avance-2026-07-21.xlsx | Bin 0 -> 8328 bytes planeacion/Seguimiento-horas.csv | 10 ++ planeacion/Sesion-Etapa1-2026-07-22.md | 8 + planeacion/generar_avance_2026_07_21.py | 159 ++++++++++++++++++ 7 files changed, 318 insertions(+), 1 deletion(-) create mode 100644 planeacion/Agenda-semana-2026-07-20.md create mode 100644 planeacion/Plan-actividades-avance-2026-07-21.xlsx create mode 100644 planeacion/generar_avance_2026_07_21.py diff --git a/bitacora/PENDIENTES.md b/bitacora/PENDIENTES.md index cc61b6d..c3bef15 100644 --- a/bitacora/PENDIENTES.md +++ b/bitacora/PENDIENTES.md @@ -2,7 +2,7 @@ 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-07-19 (avance de Etapa 1 ENTREGADO a Erika el 16-jul por WhatsApp con acuse; acceso de escritura al repo COMPROBADO con push del scaffold; quedan por revisar los procedimientos por cliente antes de la sesión del 22-jul; validación del prototipo el 23-jul)._ +_Última actualización: 2026-07-21 noche (B4 TERMINADO con sync histórico verificado — 1,494 facturas, cartera MXN/USD cuadrada; B6 código completo y migración RLS aplicada, falta el rol local `balam_app` para verificar el aislamiento; quedan por revisar los procedimientos por cliente antes de la sesión del 22-jul; validación del prototipo el 23-jul)._ ## 🔴 Johann (proveedor) — inmediato @@ -18,6 +18,8 @@ _Última actualización: 2026-07-19 (avance de Etapa 1 ENTREGADO a Erika el 16-j - [x] ~~🔐 **Validar permiso de escritura en el repositorio privado de Balam**~~ → **Comprobado (16-jul):** push exitoso del scaffold inicial (backend .NET 10 + Angular 21, commits `220942a..1f7f098`) a `pedro-balam-itsm/BALAM`. Ver [REGISTRO #46](REGISTRO.md). - [ ] ⏱️ **Validar el corte reconstruido de 30 h** en `../planeacion/Seguimiento-horas.csv`: 22 h de Etapa 0 + 8 h de Etapa 1. Solo 3.08 h corresponden a sesiones con duración comprobable; las otras 26.92 h están marcadas como reconstruidas. Ajustarlas si la memoria/evidencia indica otra distribución y después conciliar con Jira. Ver [REGISTRO #45](REGISTRO.md). - [ ] 📝 **Cerrar el documento de hallazgos + ADRs** (`../planeacion/Hallazgos-y-decisiones-Etapa0.md`) después de las sesiones del 22–23 jul. +- [ ] 🔐 **Decidir/aplicar el rol local `balam_app` para que RLS aplique en desarrollo:** la política RLS de B6 está aplicada, pero el usuario `balam` del contenedor es superuser (BYPASSRLS) y la ignora. Falta: `CREATE ROLE balam_app LOGIN NOSUPERUSER NOBYPASSRLS` + `REASSIGN OWNED` + apuntar la cadena de conexión de desarrollo (appsettings.Development y DesignTimeDbContextFactory) a ese rol + repetir la verificación psql. En Azure no existe este hueco (el usuario de app no es superuser). Ver [REGISTRO #53](REGISTRO.md). +- [ ] ⚠️ **Preguntar a Arturo (sesión 22-jul): ¿por qué TODO el histórico se factura PUE?** 1,374/1,374 facturas con método de pago = PUE, cero PPD, aunque cobran a crédito 30–90 días. Toca la regla "PPD default" de la capa de escritura (B7) y el requerimiento del SAT que ellos mismos sufrieron. Ver [REGISTRO #52](REGISTRO.md). - [x] ~~📅 **Proponer a Erika usar la sesión del martes 7-jul para COBRANZA**~~ → **Propuesto (6-jul, 3:01 pm) y CONFIRMADO (6-jul, 6:36 pm):** convocatoria de Teams enviada para el martes 7-jul, 7:00–8:00 am, con Araceli y Arturo (CC Noé). Ver [REGISTRO #34](REGISTRO.md). - [x] ~~📅 **Asistir a la sesión de cobranza (mar 7-jul, 7:00 am)**~~ → **Hecha (7-jul).** Sin grabación. Ver [REGISTRO #35](REGISTRO.md). - [x] ~~📝 **Documentar el proceso de cobranza** de la sesión del 7-jul (sin grabación)~~ → **Hecho (7-jul):** reconstruido de memoria el mismo día. Notas en `../fuentes/2026-07-07 - Notas - Proceso actual de cobranza (sin grabacion).md`; entrada [REGISTRO #35](REGISTRO.md). diff --git a/bitacora/REGISTRO.md b/bitacora/REGISTRO.md index f8d3bcd..f0cc544 100644 --- a/bitacora/REGISTRO.md +++ b/bitacora/REGISTRO.md @@ -846,6 +846,87 @@ Erika solicita un corte del avance de la Etapa 1. Johann se compromete a prepara --- +## 47 · Jul 19, 2026 · Gestión interna · demo del 18-jul no realizada; entorno local montado en la Mac + +La **demo semanal planeada para el viernes 18-jul no ocurrió** (confirmado por Johann el 19-jul). Causa señalada: la validación del prototipo de la Etapa 0 sigue pendiente (quedó para el jue 23-jul) y eso ha retrasado el avance de la etapa. Los días 17–18 jul no registran trabajo en `../planeacion/Seguimiento-horas.csv`. La próxima demo queda apuntada al **viernes 24-jul** (cierre de Etapa 1), donde también conviene comunicar que la capa de escritura se entrega en dry-run (ver nota en `../planeacion/Agenda-semana-2026-07-20.md`). + +El mismo 19-jul se montó el entorno local en la Mac: repo del cliente clonado en `balam-plataforma/` (convención de `../planeacion/Plan-Etapa1.md`), SDK .NET 10 instalado en usuario, token cargado en user-secrets, API corriendo (`/health` y `/api/bind/status` verificados, modo ReadOnly, 13/13 pruebas verdes) y frontend Angular servido. Se creó la agenda por día de la semana 20–24 jul (`../planeacion/Agenda-semana-2026-07-20.md`) y se actualizó el CSV de horas con los cortes del 16 y 19 jul. + +--- + +## 48 · Jul 19, 2026 — noche · Desarrollo · B0+B1 completados (adelantados del lunes) + Scalar UI + +Trabajo adelantado la noche del domingo siguiendo `../planeacion/Plan-Etapa1.md` al pie, con asistencia de IA (Claude Code). Tiempo real registrado en `../planeacion/Seguimiento-horas.csv` (~1.25 h de sesión — el plan estimaba 4–5.5 h manuales; el registro es de horas efectivamente trabajadas, conforme al contrato). + +**B0 — Preparación local:** `docker-compose.yml` con Postgres 17 en puerto **5438** (5433–5437 ocupados por otros proyectos locales — difiere del 5433 que asume el plan en la otra máquina); cadena de conexión en `appsettings.Development.json`; `DesignTimeDbContextFactory`; referencia Infrastructure → Integrations.Bind; `dotnet-ef` 10.0.10. + +**B1 — Fundación de datos:** entidades `Tenant`, `Client`, `Invoice` (modelo central: `Uuid` null ⇒ prefactura, `OpenBalance`, `CfdiPaymentTermRaw`, `PaidDetectedAtUtc`), `Quote`, `QuoteInvoiceLink` (ADR-003, con `JiraTicketKey` ADR-006), `InvoiceStatusChange` (ADR-004, fecha de detección), `WriteOperation` (pipeline Draft→DryRun→Confirmed, ADR-001); `TenantId` en `SyncCheckpoint`/`AuditLogEntry`; `BalamDbContext` → `IdentityDbContext` con llaves Guid; convenciones fechas BIND sin zona vs `*Utc` timestamptz y `decimal(18,2)`/`ExchangeRate(18,6)`; migración `Inicial` aplicada (16 tablas); `DbSeeder` idempotente verificado: tenant Balam (GUID fijo), 4 roles, 5 usuarios ficticios `*@balam.dev` (password en user-secrets `Seed:DefaultPassword`). + +**Extra:** UI del OpenAPI con **Scalar** en `/scalar/v1` (solo Development). Estado verificado: API arriba con BD, 13/13 pruebas verdes, seed comprobado por consulta directa a Postgres. **Cambios sin commitear** en el repo del cliente — commit pendiente de Johann. + +**Efecto en la agenda:** el lunes 20 queda libre para gestión + arrancar B2 (auth JWT + roles + bitácora) directamente. + +--- + +## 49 · Jul 19, 2026 — noche · Desarrollo · B2 completado (adelantado del lunes): auth JWT + policies + bitácora + +Continuación de la sesión nocturna asistida por IA (~0.5 h reales registradas en el CSV). Con esto el trabajo técnico planeado para el lunes 20 queda hecho; el lunes queda solo la gestión + arrancar B3 (sync de clientes). + +**B2 conforme al plan:** `POST /api/auth/login` (Identity + JWT propio, sin `MapIdentityApi`, sin refresh en E1) y `GET /api/auth/me`; `Jwt:SigningKey` en user-secrets con fail-fast; policies nombradas (`PuedeVerCartera`, `PuedeDispararSync`, `PuedeVerBitacora`, `PuedeOperarEscritura`) con matriz provisional hasta la sesión del 22-jul; roles como constantes (`BalamRoles`); bitácora en dos piezas — `AuditMiddleware` (mutantes + 401/403) e `IAuditLogger` explícito — y `GET /api/audit` (Direccion/Administracion). Decisión aplicada del plan: **BD obligatoria con fail-fast**. + +**Verificado end-to-end contra el API corriendo:** login fallido 401 (auditado una sola vez), login Finanzas 200 con token, `/me` 200, `/me` sin token 401, `/api/audit` con Finanzas 403 (auditado), con Dirección 200 mostrando toda la secuencia. 13/13 pruebas verdes. + +Repo del cliente: 3 commits locales (`5631f55` Scalar, `6264ea3` B0+B1, `4617070` B2) — **push pendiente por decisión de Johann** (no se hace push sin su orden explícita). + +--- + +## 50 · Jul 19, 2026 — noche · Desarrollo · B3 completado: sync de clientes CONTRA PRODUCCIÓN + hallazgo de datos + +Cierre de la sesión nocturna (~0.5 h reales más). **Primer dato real de BIND viviendo en la plataforma:** 16 clientes sincronizados con detalle (días de crédito 0/30/45/60/90), checkpoint registrado y ciclo auditado en bitácora. + +**Hallazgo de producción:** hay clientes con `LocationID`/`LoctaionID` **null** — caso que la validación del 6-jul no encontró. Se hicieron nullable todos los GUIDs de referencia de los modelos BIND (commit `fb0f066`); los IDs propios siguen no-nulos. Vale mencionarlo a Pedro junto con las 5 preguntas técnicas. + +**B3 conforme al plan:** full-scan de lista + `SourceHash`, detalle solo nuevos/cambiados con tope por ciclo, normalización RFC/nombre, dedup por RFC exacto excluyendo genéricos — verificado en real: el único RFC repetido es `XEXX010101000` (2 clientes extranjeros) y quedó correctamente FUERA del dedup. Idempotencia comprobada: segundo run = 16 sin cambio, 1 sola petición. Consumo total del día: ~42 peticiones de 20,000. + +`POST /api/sync/run` (Finanzas/Administración; Operaciones→403 verificado) y `GET /api/sync/status`. 13/13 pruebas verdes. Repo: **5 commits locales, push pendiente por decisión de Johann.** + +**Estado de bloques al cierre del domingo: B0 ✅ · B1 ✅ · B2 ✅ · B3 ✅** — el lunes queda gestión + B4 (sync de facturas/cotizaciones + cartera, el bloque más grande) con margen de un día completo vs. el plan. + +--- + +## 51 · Jul 21, 2026 · WhatsApp Erika ↔ Johann · estimación Jira→BIND comunicada (10 h) y alcance confirmado por Erika ⭐ + +Erika pidió en la mañana la estimación del cambio nuevo "para tenerla a la mano" antes de su sesión con directores. Secuencia clave: + +1. **Johann comunicó: ~10 h de desarrollo, a confirmar el miércoles 22 en la sesión con Arturo** (primero cerrar reglas del flujo y validar la escritura de BIND). Quedó por escrito que "no estaba contemplado al inicio; es una mejora que surgió en el Discovery". +2. Erika retó con la §1.4 de la propuesta ("¿no es esto lo de las cotizaciones?"). Aclaración que quedó asentada: **crear cotizaciones manualmente desde la plataforma SÍ está en el MVP** (capa de escritura); lo nuevo es que **los tickets de Jira las generen automáticamente**. Erika encontró ella misma la §1.3 donde Jira dice "Fuera de alcance" y lo confirmó con 👍. Erika lo valida internamente con los directores. +3. Erika preguntó por 2 actividades vencidas en fechas del Excel (backend base y multi-tenant); Johann respondió "esas ya quedaron". Backend base ✅ real (B2); multi-tenant al ~80% — **hacer B6 (RLS real) de inmediato para que la afirmación quede cubierta.** +4. **Nuevo compromiso:** compartir las horas semanalmente por Excel mientras Pedro habilita Jira, **con entregable + actividad + horas de cada etapa** (formato pedido por Erika, lunes de corte). + +--- + +## 52 · Jul 21, 2026 — noche · Desarrollo · B4 completado: sync de facturas/cotizaciones + cartera por moneda + SYNC HISTÓRICO ⭐ + +Sesión nocturna asistida por IA (~1 h real registrada en el CSV). **La plataforma ya tiene la cartera completa de producción viviendo en Postgres.** + +**B4 conforme al plan:** `InvoiceStatusMapper` como **función pura en Domain con 13 pruebas xUnit** (precedencia Cancelada→Pagada→Emitida→Vencida→Vigente; "hoy" en hora MX vía `MexicoTime`; la frontera Emitida/Vigente se ajusta en minutos si la sesión del 22 la cambia). `InvoiceSyncService` con ventana única (delta por `Date` + re-barrido de abiertas viejas, solape 1 día, cursor que solo avanza al completar la pasada) y `QuoteSyncService` con sonda del filtro `CreationDate` + fallback a full-scan. Detección de pagos ADR-004: transición → `InvoiceStatusChanges` con fecha de DETECCIÓN; **facturas nuevas NO generan transición** (el histórico fotografía, no detecta). Campo nuevo `DetailSyncedAtUtc` (migración `DetalleSyncPendiente`) para tope de detalle 50/ciclo + backfill B5. Endpoints: `GET /api/cartera` (por moneda, jamás suma monedas; primer uso de `PuedeVerCartera`), `GET /api/invoices|clients|quotes`. Normalización PPD/PUE desde la etiqueta larga de BIND. + +**Sync histórico corrido y verificado esta noche (jamás en demo):** 1,494 facturas y 43 cotizaciones, **detalle 1,494/1,494 (0 pendientes)**, idempotencia comprobada (2ª pasada = 100% sinCambio), `InvoiceStatusChanges` = 0 (cero pagos falsos), 24.5 min, ~1,700 peticiones (~8.5% de cuota; cierre del día ~1,750 de 20,000). **Cartera verificada BD ↔ endpoint exacto:** MXN 17 abiertas $1,568,772.32 · USD 92 abiertas $320,379.06. + +**Hallazgos de producción:** (1) el filtro `CreationDate` de Quotes SÍ funciona (pendiente §10 de VALIDACION-API resuelto de facto); (2) ⚠️ **las 1,374 facturas con método de pago traen PUE — ninguna PPD** (120 con el campo vacío). Todo el histórico se factura "pago en una sola exhibición" aunque cobran a crédito 30–90 días — preguntar a Arturo en la sesión del 22 (toca la regla PPD-default de la capa de escritura y el riesgo SAT que ellos mismos reportaron). + +Commit `2e1ae82`. 26/26 pruebas verdes. **7 commits locales, push pendiente por decisión de Johann.** + +--- + +## 53 · Jul 21, 2026 — noche · Desarrollo · B6: RLS real de Postgres (contractual) — código completo; verificación local pendiente de rol de app + +Cierre de la sesión (~0.5 h real). **B6 conforme al plan:** `ITenantOwned` en las 8 entidades de negocio; `ICurrentTenantProvider` + `FixedTenantProvider` (ADR-005: tenant único); `TenantConnectionInterceptor` (`set_config('app.tenant_id',…)` en cada conexión, vías sync y async); filtros globales EF por entidad como segunda capa; migración `RlsPorTenant` a mano con `ENABLE/FORCE ROW LEVEL SECURITY` + política `tenant_isolation` (USING + WITH CHECK, fail-closed sin GUC) en las 8 tablas. Migración **aplicada** (catálogo confirma `relrowsecurity=t, relforcerowsecurity=t` en las 8); app end-to-end igual que antes (login, cartera, sync). Commit `fedb376`. + +**⚠️ Verificación de aislamiento local INCOMPLETA:** la prueba psql devolvió filas sin GUC y con tenant ajeno porque `balam` (usuario bootstrap del contenedor) es **superuser con BYPASSRLS** — FORCE somete al *owner*, pero ningún candado somete a un superuser. El fix (pendiente de decisión de Johann, quedó en PENDIENTES): crear rol local `balam_app` (LOGIN, NOSUPERUSER, NOBYPASSRLS), transferirle la propiedad y apuntar la cadena de conexión de desarrollo a ese rol — replica cómo será Azure, donde el usuario de la app tampoco es superuser. Las políticas en sí son correctas; el hueco es del entorno local, no del código. + +--- + ## Resumen ejecutivo del hilo (para contexto rápido) ### Datos duros confirmados por Balam diff --git a/planeacion/Agenda-semana-2026-07-20.md b/planeacion/Agenda-semana-2026-07-20.md new file mode 100644 index 0000000..6dced80 --- /dev/null +++ b/planeacion/Agenda-semana-2026-07-20.md @@ -0,0 +1,57 @@ +# Agenda por día — semana del 20 al 24 de julio 2026 + +Cierre de la Etapa 1. Cruza los compromisos externos del Excel enviado a Erika el 16-jul +(`Plan-actividades-avance-2026-07-16.xlsx`), los bloques técnicos B0–B9 de [Plan-Etapa1.md](Plan-Etapa1.md) +y los pendientes de gestión de [PENDIENTES.md](../bitacora/PENDIENTES.md). Quedan ~24–31 h de construcción. + +> ⚠️ Desfase a comunicar en la demo del viernes: el Excel compromete la "capa de escritura controlada" +> al mar 22, pero por ADR-001 está bloqueada hasta después de la sesión del mié 22. Se entrega jue 23 +> en modo dry-run con flags apagados. El "modelo de facturas + migraciones" (Excel: 23–24 jul) en +> realidad se construye primero (B1, lun 20) porque todo el sync depende de él. + +## Lunes 20 — fundación de datos + gestión (día de corte de Erika) + +**Gestión (primera hora):** +- [ ] 📧 Enviar el correo a Noé + Pedro: salvaguardas + 5 dudas técnicas del API (borrador 1 de [Borradores-seguimiento-tecnico.md](Borradores-seguimiento-tecnico.md)). Le da a Pedro 2 días para investigar antes de la sesión del miércoles. +- [ ] 💬 WhatsApp a Erika/Pedro: flujo de horas en Jira (borrador 2) — lunes es su día de corte. +- [ ] 💬 Recordatorio a Erika: acceso Azure programado esta semana (condiciona el CI/CD del viernes). +- [ ] ⏱️ Validar las horas reconstruidas en [Seguimiento-horas.csv](Seguimiento-horas.csv) (26.92 h estimadas) y confirmar/ajustar las filas del 16–19 jul. +- [ ] 📄 Revisar los procedimientos de carga de facturas por cliente (correo de Erika 16-jul); volcar diferencias vs Discovery/prototipo en el punto 3 de [Sesion-Etapa1-2026-07-22.md](Sesion-Etapa1-2026-07-22.md). + +**Técnico:** ✅ **B0, B1, B2 y B3 quedaron ADELANTADOS el domingo 19 en la noche** (ver [REGISTRO #48–50](../bitacora/REGISTRO.md)): Postgres 17 local (5438), modelo migrado y sembrado, auth completa con matriz verificada, y **sync de clientes corriendo contra BIND real** (16 clientes, idempotente, dedup correcto). Hallazgo: GUIDs null en producción → modelos corregidos; mencionarlo a Pedro. 5 commits locales — **push pendiente de decisión de Johann**. El lunes queda: gestión en la mañana + **B4** (sync de facturas/cotizaciones + estados + cartera, 4.5–5.5 h) — con un día completo de colchón vs. el plan. + +## Martes 21 — seguridad y primer sync real (~6–8 h) + +- B2 Auth JWT + 4 roles + policies + bitácora de auditoría (4–5 h). +- B3 Sync de clientes con normalización y dedup por RFC (2.5–3.5 h) — el Excel lo compromete terminando hoy. +- Seguimiento a Pedro/Noé: arranque de la configuración de Azure (de su lado; no bloquea lo local). + +## Miércoles 22 — sesión de definiciones + sync de facturas + +✅ **B4 y B6 quedaron ADELANTADOS la noche del martes 21** (ver [REGISTRO #52–53](../bitacora/REGISTRO.md)): sync de facturas/cotizaciones con histórico completo verificado (1,494 facturas, cartera MXN/USD cuadrada contra BIND), mapper de estados puro con pruebas, y RLS aplicado (falta solo el rol local `balam_app` para la verificación de aislamiento). Hallazgo para la sesión: **todo el histórico es PUE, cero PPD** — preguntar a Arturo. + +- 📅 **Sesión de reglas Etapa 1 (Arturo + CEO).** Llevar [Sesion-Etapa1-2026-07-22.md](Sesion-Etapa1-2026-07-22.md) con la minuta lista y los hallazgos de los procedimientos por cliente. Salidas esperadas: alta de cliente, fee, envío, alcance Jira→BIND, frontera Emitida/Vigente, autorización de escritura. +- Post-sesión: registrar la minuta y actualizar [Hallazgos-y-decisiones-Etapa0.md](Hallazgos-y-decisiones-Etapa0.md) (ADRs afectados). +- Ajustar `InvoiceStatusMapper` con la frontera Emitida/Vigente definida en la sesión (es función pura: minutos). +- Restante técnico de la semana: B5 scheduler, B7 (post-autorización), B8/B9, CI/CD viernes. + +## Jueves 23 — escritura controlada + validación del prototipo + +- Cerrar B4 si quedó cola; **correr el sync inicial del histórico en la noche — jamás en vivo en demo**. +- B7 Capa de escritura controlada Draft→DryRun→Confirm con flags apagados y cero llamadas reales (3–4 h), ya con las definiciones del miércoles. +- Avanzar el cierre del documento de hallazgos + ADRs (el Excel lo compromete hoy). +- 📅 **7:00 pm — Validación del prototipo Etapa 0.** Llevar la lista de decisiones que requieren visto bueno y la separación MVP vs Anexo B (recordatorios a clientes = Anexo B; multiempresa = fuera; agentes Jira = Nivel 3). + +## Viernes 24 — robustez, CI/CD y cierre de etapa + +- B5 Scheduler 15 min con guarda de cuota + backfill (2–3 h) · B6 RLS real de Postgres (1.5–2.5 h, contractual, no recortable). +- B8 pruebas de flujos críticos + B9 seeder + runbook (recortables a lo mínimo si aprieta). +- CI/CD + gestión de secretos con Pedro (según acceso Azure). +- 📅 **Demo semanal + reporte de horas.** Meta: login + roles + sync real en Postgres + cartera. Comunicar ahí el ajuste de la capa de escritura (dry-run, pendiente de autorización para escritura real). +- Registrar cierre en bitácora y actualizar Excel de avance para Erika. + +## Si aprieta el tiempo (recortes ya identificados en el plan, ≈ −5 h) + +`GET /api/audit` y middleware post-demo (−1) · dedup solo RFC exacto (−0.5) · quotes full-scan simple (−0.5) · +equivalente MXN (−0.5) · backfill con tope fijo (−0.5) · B7 sin modelos provisionales si el 22 cambia el alcance (−1) · +seeder mínimo (−0.5). La demo del viernes exige como mínimo: login + roles + sync + cartera. diff --git a/planeacion/Plan-actividades-avance-2026-07-21.xlsx b/planeacion/Plan-actividades-avance-2026-07-21.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..0886a2986fc9d7923c6889d9397db32d0ba7ed29 GIT binary patch literal 8328 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 MZz19!{_F1l0Jg>?vj6}9 literal 0 HcmV?d00001 diff --git a/planeacion/Seguimiento-horas.csv b/planeacion/Seguimiento-horas.csv index 147aefd..8080e4d 100644 --- a/planeacion/Seguimiento-horas.csv +++ b/planeacion/Seguimiento-horas.csv @@ -17,3 +17,13 @@ fecha,semana,etapa,actividad_id,actividad,horas,estado_hora,fuente_evidencia,fac 2026-07-15,2026-W29,1,E1-03,Arquitectura multi-tenant,0.50,reconstruida,planeacion/Plan-actividades.md,si,"Completa 1.50 h" 2026-07-15,2026-W29,1,E1-04,Cliente productivo de API BIND,2.00,reconstruida,bind-api-sandbox/VALIDACION-API.md,si,"Inicio de reintentos OData y manejo de errores" 2026-07-15,2026-W29,1,E1-05,Seguimiento ajuste de calendario y corte de horas,0.50,reconstruida,bitacora/REGISTRO.md#41-45,si,"Actualización de plan y control" +2026-07-16,2026-W29,1,E1-06,Scaffold backend .NET 10 + frontend Angular 21 y push al repo de Balam,2.00,reconstruida,bitacora/REGISTRO.md#46,si,"Commits 220942a..1f7f098; incluye CLAUDE.md del repo; validar duración" +2026-07-16,2026-W29,1,E1-05,Corte de avance Etapa 1 para Erika (Excel + mensaje),1.00,reconstruida,planeacion/Avance-Etapa1-2026-07-16.md,si,"Enviado 8:40 pm por WhatsApp con acuse; validar duración" +2026-07-19,2026-W29,1,E1-06,Preparación de entorno local (clon del repo, SDK .NET 10, secrets, app corriendo),1.00,reconstruida,bitacora/REGISTRO.md,si,"API y frontend verificados en local; 13/13 pruebas verdes; validar duración" +2026-07-19,2026-W29,1,E1-06,Scalar UI para el OpenAPI del backend,0.25,confirmada,bitacora/REGISTRO.md#48,si,"Sesión asistida por IA; /scalar/v1 verificado" +2026-07-19,2026-W29,1,E1-01,B0+B1: Postgres 17 local + modelo de datos + Identity + migración Inicial + seed,1.00,confirmada,bitacora/REGISTRO.md#48,si,"Sesión asistida por IA (noche del domingo); 16 tablas migradas y siembra verificada; tiempo real de sesión" +2026-07-19,2026-W29,1,E1-01,B2: login JWT + policies por rol + bitácora de auditoría,0.50,confirmada,bitacora/REGISTRO.md#49,si,"Sesión asistida por IA; matriz 401/403/200 verificada end-to-end; tiempo real de sesión" +2026-07-19,2026-W29,1,E1-01,B3: sync de clientes contra BIND real + fix de modelos (GUIDs null),0.50,confirmada,bitacora/REGISTRO.md#50,si,"Sesión asistida por IA; 16 clientes sincronizados, idempotencia verificada; tiempo real de sesión" +2026-07-21,2026-W30,1,E1-01,B4: sync de facturas/cotizaciones + estados + cartera por moneda + sync histórico,1.00,confirmada,bitacora/REGISTRO.md#52,si,"Sesión asistida por IA; 1494 facturas y 43 cotizaciones sincronizadas de producción; tiempo real de sesión" +2026-07-21,2026-W30,1,E1-03,B6: RLS real de Postgres por tenant (migración + interceptor + filtros EF),0.50,confirmada,bitacora/REGISTRO.md#53,si,"Sesión asistida por IA; verificación manual en psql; tiempo real de sesión" +2026-07-21,2026-W30,1,E1-05,Cierre de sesión: Excel de avance regenerado + bitácora + control de horas,0.25,confirmada,bitacora/REGISTRO.md#52-53,si,"Sesión asistida por IA; tiempo real de sesión" diff --git a/planeacion/Sesion-Etapa1-2026-07-22.md b/planeacion/Sesion-Etapa1-2026-07-22.md index a5ddb1f..db04e05 100644 --- a/planeacion/Sesion-Etapa1-2026-07-22.md +++ b/planeacion/Sesion-Etapa1-2026-07-22.md @@ -32,6 +32,7 @@ Al terminar deben quedar definidos: - ¿Pago parcial con saldo residual, nota de crédito, gasto/comisión o ajuste contable? - ¿Existe una tolerancia fija o cambia por cliente/banco? - ¿Quién autoriza considerar una factura como saldada con diferencia? +- 🆕 **Hallazgo del sync histórico (21-jul):** las 1,374 facturas con método de pago registran **PUE — ninguna PPD** — aunque cobran a crédito 30–90 días. ¿Es política deliberada del despacho o práctica a corregir? Impacta la regla "PPD default" de la futura capa de escritura y el requerimiento del SAT que ya sufrieron. **Decisión a registrar:** fórmula de estado de cobranza y si el umbral es global o por cliente. @@ -60,6 +61,13 @@ Si Balam confirma la automatización, solicitar posteriormente: - Identificadores del sitio/proyecto y mapeo de campos. - Autorización de escritura controlada en BIND. +**Estimación a comunicar (en dos partes, no dar una sola cifra global):** + +1. *Definición del flujo + accesos:* esta misma semana, un par de horas. +2. *Desarrollo:* **~10 horas, sujeto a validar la API de escritura de BIND** — sin sandbox, endpoints de escritura no documentados y producción directa (por eso lleva dry-run + confirmación humana). Frase preparada: "La definición del flujo y los accesos los resolvemos esta semana. El desarrollo lo estimo en unas 10 horas, sujeto a lo que encontremos en la API de escritura de BIND, que aún no está documentada y es producción directa — por eso va con dry-run y confirmación humana antes de escribir nada." + +⚠️ No comprometer el número de desarrollo como cerrado hasta tener las reglas del punto 1 (alta de cliente) — si el ticket puede traer un cliente que no existe en BIND, las dos piezas se tocan y el alcance cambia. + **Decisión a registrar:** incluido, diferido o sujeto a estimación/cambio de alcance. ### 5. Cierre técnico — 10 min diff --git a/planeacion/generar_avance_2026_07_21.py b/planeacion/generar_avance_2026_07_21.py new file mode 100644 index 0000000..26d41bf --- /dev/null +++ b/planeacion/generar_avance_2026_07_21.py @@ -0,0 +1,159 @@ +"""Genera Plan-actividades-avance-2026-07-21.xlsx desde el del 16-jul. +Columnas nuevas: Bloque · Horas estimadas (rango) · Horas trabajadas · Estatus. +Semáforo: verde=completada, amarillo=en curso, rojo=vencida, gris=próxima. +Línea roja gruesa = HOY (21-jul): lo de arriba debería estar hecho o en curso.""" +import re +from datetime import date +import openpyxl +from openpyxl.styles import Font, Alignment, PatternFill, Border, Side + +SRC = "/Users/johann/Desktop/Proyectos personales/BALAM/planeacion/Plan-actividades-avance-2026-07-16.xlsx" +DST = "/Users/johann/Desktop/Proyectos personales/BALAM/planeacion/Plan-actividades-avance-2026-07-21.xlsx" +HOY = date(2026, 7, 21) + +AMARILLO, CAFE = "F7BD0C", "331F0E" +CREMA, GRIS_B = "FDF3D8", "D9D9D9" +V_FILL, V_FONT = "C6EFCE", "006100" # verde +A_FILL, A_FONT = "FFEB9C", "9C6500" # amarillo +R_FILL, R_FONT = "FFC7CE", "9C0006" # rojo +G_FILL, G_FONT = "F2F2F2", "595959" # gris + +# prefijo -> (bloque, estimadas_rango, trabajadas, nuevo_pct o None) +MAPA = { + "Integracion de plataformas Balam": ("", "50–61", 38.0, 0.65), + "Etapa 0": ("", "18–22", 22.0, None), + "Sesión de arranque (kickoff)": ("—", "2", 2.0, None), + "Sesión de Discovery": ("—", "4–5", 5.5, None), + "Entrega y validación de accesos": ("—", "1", 1.0, None), + "Validación técnica de la API": ("—", "3–4", 4.5, None), + "Prototipo visual navegable": ("—", "5–6", 6.0, None), + "Documento de hallazgos": ("—", "2–3", 3.0, None), + "Sesión de validación del prototipo": ("—", "1", 0.0, None), + "Etapa 1": ("", "32–39", 16.0, 0.8), + "Demostración semanal": ("—", "2–3", 1.5, None), + "Repositorio privado GitHub": ("—", "1", 0.5, None), + "Backend base": ("B2", "6–7", 7.25, 1.0), + "Arquitectura multi-tenant": ("B1 + B6", "2–3", 2.0, 1.0), + "Cliente de la API de BIND": ("—", "2–3", 2.0, None), + "Capa de escritura controlada": ("B7", "3–4", 0.0, None), + "Sincronización de clientes": ("B3", "2.5–3", 0.5, 1.0), + "Configuración de infraestructura en Azure": ("—", "1", 0.0, None), + "Sincronización de facturas": ("B4", "4.5–5.5", 1.0, 1.0), + "Sesión de reglas y dudas": ("—", "1", 0.0, None), + "Definición de alcance Jira": ("—", "1", 0.0, None), + "Modelo de facturas": ("B1 + B4 + B9", "3.5–4.5", 1.0, 0.85), + "CI/CD": ("—", "1.5–2", 0.0, None), +} + +def fecha(celda): + m = re.search(r"(\d{2})/(\d{2})/(\d{2})", str(celda or "")) + return date(2000 + int(m.group(3)), int(m.group(2)), int(m.group(1))) if m else None + +wb = openpyxl.load_workbook(SRC) +ws = wb.active + +C_BLQ, C_EST, C_TRAB, C_STAT = 8, 9, 10, 11 +for col, titulo in [(C_BLQ, "Bloque (plan interno)"), (C_EST, "Horas estimadas"), + (C_TRAB, "Horas trabajadas"), (C_STAT, f"Estatus (hoy {HOY.day}-jul)")]: + ws.cell(row=1, column=col, value=titulo) + +thin = Side(style="thin", color=GRIS_B) +borde = Border(left=thin, right=thin, top=thin, bottom=thin) + +def pinta(celda, fill, font_color, bold=False): + celda.fill = PatternFill("solid", fgColor=fill) + celda.font = Font(color=font_color, bold=bold, size=10) + celda.alignment = Alignment(horizontal="center", vertical="center") + +fila_hoy = None # primera actividad de Etapa 1 que arranca DESPUÉS de hoy +en_etapa1 = False + +for row in range(2, ws.max_row + 1): + nombre = str(ws.cell(row=row, column=1).value or "").strip() + if not nombre: + continue + if nombre.startswith("Etapa 1"): + en_etapa1 = True + + for prefijo, (blq, est, trab, pct) in MAPA.items(): + if nombre.startswith(prefijo): + ws.cell(row=row, column=C_BLQ, value=blq) + ws.cell(row=row, column=C_EST, value=est) + ws.cell(row=row, column=C_TRAB, value=trab) + if pct is not None: + ws.cell(row=row, column=7, value=pct) + break + + es_etapa = nombre.startswith("Etapa") or nombre.startswith("Integracion") + pct = ws.cell(row=row, column=7).value or 0 + ini, fin = fecha(ws.cell(row=row, column=3).value), fecha(ws.cell(row=row, column=4).value) + stat = ws.cell(row=row, column=C_STAT) + + if es_etapa: + ws.cell(row=row, column=C_STAT, value="") + elif pct >= 1: + stat.value = "✅ Completada"; pinta(stat, V_FILL, V_FONT) + elif fin and fin < HOY: + stat.value = "🔴 Vencida"; pinta(stat, R_FILL, R_FONT, bold=True) + elif ini and ini <= HOY: + stat.value = "🟡 En curso"; pinta(stat, A_FILL, A_FONT) + elif pct > 0: + stat.value = "🟢 Adelantada"; pinta(stat, V_FILL, V_FONT) + else: + stat.value = "⚪ Próxima"; pinta(stat, G_FILL, G_FONT) + + # semáforo también en la celda de % + pcel = ws.cell(row=row, column=7) + if not es_etapa: + if pct >= 1: pinta(pcel, V_FILL, V_FONT, bold=True) + elif pct > 0: pinta(pcel, A_FILL, A_FONT, bold=True) + elif fin and fin < HOY: pinta(pcel, R_FILL, R_FONT, bold=True) + + if en_etapa1 and fila_hoy is None and not es_etapa and ini and ini > HOY: + fila_hoy = row + +# estilo general: encabezado marca, filas de etapa, bordes, formatos +head_fill = PatternFill("solid", fgColor=AMARILLO) +etapa_fill = PatternFill("solid", fgColor=CREMA) +for row in range(1, ws.max_row + 1): + nombre = str(ws.cell(row=row, column=1).value or "") + es_etapa = nombre.startswith("Etapa") or nombre.startswith("Integracion") + for col in range(1, C_STAT + 1): + c = ws.cell(row=row, column=col) + c.border = borde + if row == 1: + c.fill = head_fill + c.font = Font(bold=True, color=CAFE, size=11) + c.alignment = Alignment(horizontal="center", vertical="center", wrap_text=True) + elif es_etapa and nombre: + c.fill = etapa_fill + c.font = Font(bold=True, color=CAFE) + if row > 1: + ws.cell(row=row, column=7).number_format = "0%" + ws.cell(row=row, column=C_TRAB).number_format = "0.0" + for col in (C_BLQ, C_EST, C_TRAB): + ws.cell(row=row, column=col).alignment = Alignment(horizontal="center") + +# línea de HOY: borde rojo grueso arriba de la primera actividad futura de Etapa 1 +if fila_hoy: + rojo = Side(style="thick", color="C00000") + for col in range(1, C_STAT + 1): + c = ws.cell(row=fila_hoy, column=col) + c.border = Border(left=c.border.left, right=c.border.right, + top=rojo, bottom=c.border.bottom) + +# leyenda +ley = ws.max_row + 2 +ws.cell(row=ley, column=1, + value="✅ Completada · 🟡 En curso · 🔴 Vencida (fecha plan pasada, <100%) · ⚪ Próxima").font = Font(italic=True, size=9, color=G_FONT) +ws.cell(row=ley + 1, column=1, + value=f"▬ La línea roja gruesa marca HOY ({HOY.day}-jul): lo de arriba debería estar terminado o en curso.").font = Font(italic=True, size=9, color="C00000") + +for letra, ancho in {"A": 48, "B": 9, "C": 12, "D": 12, "E": 12, "F": 22, "G": 12, + "H": 15, "I": 14, "J": 14, "K": 20}.items(): + ws.column_dimensions[letra].width = ancho +ws.freeze_panes = "A2" +ws.oddFooter.center.text = "Avance al 21-jul-2026 · Horas conforme al control local (pendiente conciliar con Jira)" + +wb.save(DST) +print("OK ->", DST, "| línea de hoy en fila:", fila_hoy)