Etapa 1: plan de ejecución, corte de avance del 16-jul y fuentes
- Plan-Etapa1.md: plan detallado por fases/bloques (B0-B9) con horas, decisiones de arquitectura y lo bloqueado hasta el 22-jul - Avance-Etapa1-2026-07-16.md + Excel de avance ordenado por fechas (con script generador y de ordenamiento) - WhatsApp 16-jul: invitación al Git recibida, procedimientos de clientes enviados por correo, Erika pide avance de Etapa 1 - Actualiza PENDIENTES y REGISTRO
This commit is contained in:
@@ -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-15 (prototipo entregado y con validación agendada para el 23-jul; sesión de reglas de Etapa 1 el 22-jul; repo GitHub autorizado y pendiente de invitación; control local de horas iniciado; factura de 30 h aún pendiente de confirmación)._
|
||||
_Última actualización: 2026-07-16 (prototipo con validación agendada para el 23-jul; sesión de reglas de Etapa 1 el 22-jul; invitación al repo GitHub recibida y acceso de escritura por validar; procedimientos por cliente recibidos por correo y pendientes de revisión; avance de Etapa 1 solicitado por Erika)._
|
||||
|
||||
## 🔴 Johann (proveedor) — inmediato
|
||||
|
||||
@@ -13,6 +13,9 @@ _Última actualización: 2026-07-15 (prototipo entregado y con validación agend
|
||||
- [x] ~~📊 **Responder a Erika el % de avance de Fase 0**~~ → **Atendido mediante el Excel de avance ajustado y entrega del prototipo.** La validación final sigue abierta.
|
||||
- [ ] 📅 **Preparar y asistir a la sesión de reglas/dudas de Etapa 1 (mié 22-jul)** con Arturo + CEO: alta de cliente nuevo, fee en BIND, particularidades de envío, estatus disparador de Jira y confirmación de alcance Jira→BIND. Ver [REGISTRO #42](REGISTRO.md).
|
||||
- [ ] 📅 **Preparar y asistir a la validación del prototipo (jue 23-jul, 7:00 pm).** Llevar decisiones que requieren visto bueno y separar MVP de Anexo B. Ver [REGISTRO #42](REGISTRO.md).
|
||||
- [ ] 📊 **Compartir con Erika el corte de avance de la Etapa 1 solicitado el 16-jul.** Usar cifras verificables: 8 h registradas de 32–39 h; cliente BIND terminado con 13/13 pruebas; scaffold base parcial; base de datos, autenticación/roles, sincronización y cartera en construcción. Ver [REGISTRO #46](REGISTRO.md).
|
||||
- [ ] 📄 **Revisar los procedimientos de carga de facturas por cliente enviados por correo el 16-jul.** Conservar correo/adjuntos como evidencia y contrastar destinatarios, adjuntos, asunto, cuerpo y nomenclatura contra el Discovery y el prototipo. Ver [REGISTRO #46](REGISTRO.md).
|
||||
- [ ] 🔐 **Validar permiso de escritura en el repositorio privado de Balam.** Johann confirmó que puede acceder, pero falta comprobar `clone/push` o el nivel de permiso antes de mover el trabajo local. 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.
|
||||
- [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).
|
||||
@@ -52,14 +55,14 @@ _Última actualización: 2026-07-15 (prototipo entregado y con validación agend
|
||||
## 🟡 Balam
|
||||
|
||||
- [x] ~~**Balam:** enviar el documento/contrato de firma~~ → **Hecho (25-jun):** contrato enviado vía Paola (RH); ajustado y **firmado el 26-jun**. Ver [REGISTRO #18](REGISTRO.md), [#19](REGISTRO.md).
|
||||
- [ ] 🔑 **Balam: completar accesos de Etapa 1** — token BIND ✅; manual de marca ✅; repo GitHub autorizado (falta invitación y validar escritura); Azure programado para semana del 21-jul. Ya no bloquea el Discovery, pero sí despliegue/CI-CD.
|
||||
- [ ] 🔑 **Balam: completar accesos de Etapa 1** — token BIND ✅; manual de marca ✅; invitación al repo GitHub recibida y acceso confirmado informalmente (falta validar escritura); Azure programado para semana del 21-jul. Ya no bloquea el desarrollo local, pero Azure sí condiciona despliegue/CI-CD.
|
||||
- [x] ~~Erika (PM): pedir el plan de actividades~~ → **Recibido (30-jun).** Erika es la **intermediaria de todas las sesiones**, agendó el **kickoff (1-jul, 7am)** y monta el **tablero Kanban en Jira**. Ver [REGISTRO #21](REGISTRO.md).
|
||||
- [x] ~~**Noe:** formalizar por correo~~ → **Hecho:** aclaraciones (8-jun, [#15](REGISTRO.md)) y **luz verde + redacción del documento de firma** (16-jun, [#17](REGISTRO.md)).
|
||||
- [ ] **Pedro + Erika:** armar el **tablero de seguimiento en Jira** y revisarlo juntos (instruido formalmente por Noe el 16-jun).
|
||||
- [x] ~~**Pedro:** enviar **manual de marca**~~ → **Hecho (1-jul):** enviado por correo. Tokens en [`../marca/Marca-Balam.md`](../marca/Marca-Balam.md). Ver [REGISTRO #23](REGISTRO.md).
|
||||
- [x] ~~**Erika:** entregar el **token de BIND**~~ → ✅ **ENTREGADO por correo (6-jul, 12:40 pm)**, mismo día en que se solicitó. Ver [REGISTRO #28](REGISTRO.md).
|
||||
- [x] ~~**Balam:** confirmar **a qué usuario pertenece el token**~~ → ✅ **Confirmado (6-jul, 12:38 pm):** Pedro lo entregó por correo (`bind_token_api.txt`) indicando que fue generado con el **usuario de Arturo Rosas** — conforme al kickoff (Arturo=dev, Ara=Power BI). Ver [REGISTRO #29](REGISTRO.md).
|
||||
- [ ] **Pedro/Balam:** completar la creación del **repositorio GitHub privado** y agregar a `Johann-28` con permisos de escritura. Noé ya autorizó la creación el 15-jul. Ver [REGISTRO #43](REGISTRO.md).
|
||||
- [ ] **Pedro/Balam:** confirmar permisos de **escritura** para `Johann-28` en el repositorio GitHub privado. Pedro envió la invitación el 15-jul y Johann confirmó acceso el 16-jul; falta una prueba efectiva de escritura. Ver [REGISTRO #43](REGISTRO.md), [#46](REGISTRO.md).
|
||||
- [ ] **Pedro:** enseñar a Johann el **flujo de Jira** (reporte de horas como los demás consultores).
|
||||
- [ ] **Pedro + Noé:** **configurar Azure** y permisos (Noé otorga donde Pedro tiene limitantes).
|
||||
- [x] ~~**Erika:** **coordinar la sesión de Discovery con Arturo + Araceli**~~ → **Hecho:** agendada (1-jul) y **realizada (6-jul, 7am)**. Ver [REGISTRO #24](REGISTRO.md), [#27](REGISTRO.md).
|
||||
|
||||
@@ -828,6 +828,24 @@ Se cargan 3.08 h de sesiones con duración comprobable y se reconstruye retrospe
|
||||
|
||||
---
|
||||
|
||||
## 46 · Jul 16, 2026 · WhatsApp · Erika ↔ Johann · invitación Git, procedimientos y solicitud de avance
|
||||
|
||||
> Evidencia: `../fuentes/2026-07-16 - WhatsApp - Erika invitacion Git procedimientos y avance Etapa1.md`.
|
||||
|
||||
Erika informa que Pedro envió el 15-jul la invitación al repositorio privado y pide validar el acceso. Johann confirma por WhatsApp que ya puede entrar; queda pendiente comprobar que también tenga permisos de escritura. Erika envía por correo los procedimientos de carga de facturas por cliente y Johann confirma recepción.
|
||||
|
||||
Erika solicita un corte del avance de la Etapa 1. Johann se compromete a prepararlo y compartirlo por WhatsApp.
|
||||
|
||||
**Acciones derivadas:**
|
||||
|
||||
- [ ] Johann — validar permisos de escritura en el repositorio de Balam.
|
||||
- [ ] Johann — revisar el correo y los procedimientos de carga de facturas; documentar reglas o diferencias frente al Discovery.
|
||||
- [ ] Johann — enviar a Erika el corte de avance de Etapa 1.
|
||||
|
||||
**Estado técnico comprobado al preparar el corte:** repositorio local limpio; solución .NET 10 y cliente BIND compilando; 13/13 pruebas del cliente BIND aprobadas; endpoints base `/health` y `/api/bind/status`. La base de datos, autenticación/roles, sincronización y endpoints de cartera siguen en construcción.
|
||||
|
||||
---
|
||||
|
||||
## Resumen ejecutivo del hilo (para contexto rápido)
|
||||
|
||||
### Datos duros confirmados por Balam
|
||||
|
||||
+57
@@ -0,0 +1,57 @@
|
||||
# WhatsApp — Erika Chávez (PM, Balam) ↔ Johann · invitación Git, procedimientos y avance de Etapa 1
|
||||
|
||||
**Canal:** WhatsApp
|
||||
**Fecha:** 2026-07-16
|
||||
**Evidencia:** captura compartida por Johann el 16-jul-2026.
|
||||
|
||||
## Transcripción
|
||||
|
||||
**Erika · 12:10–12:16 pm**
|
||||
|
||||
- “buenas tardes johan”
|
||||
- “ayer mi compañero pedro te envio la invitacion para el GIT”
|
||||
- “puedes revisar si ya puedes acceder”
|
||||
- “adicional me acaban de liberar los procedimientos de los clientes, ahorita te lo mando por correo”
|
||||
|
||||
**Johann · 12:24 pm**
|
||||
|
||||
- “Buenas tardes”
|
||||
- “De acuerdo, si ya pude acceder al repositorio, muchas gracias”
|
||||
- “Enterado”
|
||||
|
||||
**Erika · 12:32 pm**
|
||||
|
||||
- “gracias y quedo a tus ordenes”
|
||||
- “me pudieras pasar el avance que se tiene de la etapa 1 porfa”
|
||||
|
||||
**Johann · 12:54 pm**
|
||||
|
||||
- “Sí, sin problema, lo preparo y te lo comparto por aquí”
|
||||
|
||||
**Erika · 12:55–1:02 pm**
|
||||
|
||||
- “gracias”
|
||||
- “ya envie el correo con los procedimientos de cargas de factura de los clientes puedes validarlo si llego el correo por favor”
|
||||
|
||||
**Johann · 1:03 pm**
|
||||
|
||||
- “Recibido, gracias”
|
||||
|
||||
**Erika · 1:28–1:29 pm**
|
||||
|
||||
- “a ti, quedo al pendiente de los avances de la etapa 1”
|
||||
- “y si necesitas algo mas de nuestro lado me avisas va”
|
||||
|
||||
**Johann · 1:31 pm**
|
||||
|
||||
- “va, en cuanto los tenga te los comparto”
|
||||
|
||||
## Acciones derivadas
|
||||
|
||||
1. Validar que el acceso al repositorio privado permita lectura y escritura.
|
||||
2. Revisar los procedimientos de carga de facturas enviados por correo y contrastarlos con el flujo levantado en Discovery.
|
||||
3. Compartir con Erika un corte verificable del avance de la Etapa 1.
|
||||
|
||||
## Observación
|
||||
|
||||
En el chat Johann confirmó que ya podía acceder al repositorio y que recibió el correo. Aún falta conservar el correo o sus adjuntos como evidencia y realizar la revisión de contenido.
|
||||
@@ -0,0 +1,50 @@
|
||||
# Corte de avance — Etapa 1
|
||||
|
||||
**Fecha:** 16-jul-2026
|
||||
**Etapa:** Plataforma base y sincronización con BIND
|
||||
**Rango estimado contractual:** 32–39 h
|
||||
|
||||
## Mensaje breve para Erika
|
||||
|
||||
Hola Erika. Te comparto el Excel actualizado con el avance de la Etapa 1 al día de hoy.
|
||||
|
||||
Como puntos principales, el cliente de consulta de BIND ya quedó terminado e integrado en C#, con las salvaguardas acordadas y 13 pruebas automatizadas aprobadas. La estructura base de la plataforma tiene avance parcial y continúo con el modelo de datos, autenticación/roles, separación por tenant y sincronización de clientes y facturas.
|
||||
|
||||
En el corte tengo 8 h registradas de esta etapa, cuyo rango estimado es de 32–39 h. En el Excel marqué 20% de avance para la Etapa 1: un entregable está terminado y la plataforma base tiene avance parcial.
|
||||
|
||||
Ya recibí los procedimientos que me enviaste. Los voy a contrastar con el flujo levantado en las sesiones para incorporar las reglas por cliente. También validaré que el acceso al repositorio incluya permisos de escritura; si requiero algo adicional de su lado te aviso.
|
||||
|
||||
**Adjunto:** `Plan-actividades-avance-2026-07-16.xlsx`
|
||||
|
||||
## Sustento interno
|
||||
|
||||
### Terminado y comprobado
|
||||
|
||||
- Solución backend .NET 10 y estructura frontend Angular 21.
|
||||
- Cliente productivo de BIND en C#, solo lectura por defecto.
|
||||
- Paginación OData, reintentos, control local de cuota y errores conocidos.
|
||||
- `GET /health`.
|
||||
- `GET /api/bind/status`.
|
||||
- **13/13 pruebas automatizadas aprobadas el 16-jul-2026.**
|
||||
|
||||
### En construcción
|
||||
|
||||
- Modelo de datos PostgreSQL y migración inicial.
|
||||
- Autenticación y matriz de roles.
|
||||
- Aislamiento multi-tenant y RLS.
|
||||
- Sincronización incremental de clientes, cotizaciones y facturas.
|
||||
- Endpoints de clientes, facturas, cotizaciones y cartera.
|
||||
- Scheduler, bitácora operativa y capa de escritura controlada.
|
||||
|
||||
### Dependencias externas
|
||||
|
||||
- Validar permiso efectivo de escritura en el repositorio privado.
|
||||
- Revisar los procedimientos por cliente recibidos por correo.
|
||||
- Cerrar reglas de alta de cliente, fee, envío y Jira→BIND en la sesión del 22-jul.
|
||||
- Recibir acceso acotado a Azure para despliegue y CI/CD.
|
||||
|
||||
## Criterio de medición
|
||||
|
||||
- **Horas registradas:** 8.00 h de un rango de 32–39 h (**20.5–25% del esfuerzo estimado**).
|
||||
- **Entregables:** 1 de 8 terminado; plataforma base con avance parcial.
|
||||
- No se reporta un porcentaje global único porque mezclar horas consumidas con entregables terminados produciría una cifra engañosa.
|
||||
@@ -0,0 +1,128 @@
|
||||
# Plan de ejecución — Etapa 1 · Plataforma base y sincronización con BIND
|
||||
|
||||
**Proyecto:** Plataforma de Automatización Financiera · Balam
|
||||
**Ventana:** 13–24 jul 2026 (semanas 2–3) · **Rango contractual:** 32–39 h · $19,200–$23,400 + IVA
|
||||
**Corte de referencia:** 16-jul-2026 · 8.00 h registradas → restan ~24–31 h
|
||||
**Repo de código:** `github.com/pedro-balam-itsm/BALAM` (clonado en `balam-plataforma/`)
|
||||
|
||||
> Documento interno de Johann. NO compartir con Balam ni commitear en el repo del cliente.
|
||||
> Fuentes: propuesta v1.2 (§Etapa 1, L142–157), ADR-001…006 (`Hallazgos-y-decisiones-Etapa0.md`),
|
||||
> `bind-api-sandbox/VALIDACION-API.md`, `Seguimiento-horas.md`, REGISTRO #40–#45.
|
||||
|
||||
## 1. Estado al 16-jul — entregables contractuales
|
||||
|
||||
Objetivo contractual: *"Plataforma funcional que ingiere clientes y facturas de BIND vía API, los normaliza, mantiene su trazabilidad y habilita la capa de escritura controlada."*
|
||||
|
||||
| # | Entregable (propuesta L148–155) | Estado |
|
||||
|---|---|---|
|
||||
| 1 | Backend con autenticación, roles (Finanzas, Dirección, Operaciones, Administración) y bitácora de auditoría universal | ❌ Solo existe la entidad `AuditLogEntry` |
|
||||
| 2 | Arquitectura multi-tenant (`tenant_id` + RLS), sin alta autoservicio | ❌ |
|
||||
| 3 | Cliente de la API de BIND (reintentos, OData, límites, errores) | ✅ **Terminado** — C#, 13 pruebas xUnit, salvaguardas del sandbox |
|
||||
| 4 | Capa de escritura controlada (cotización→factura con dry-run + confirmación + feature flag + bitácora) | ❌ Bloqueada hasta el 22-jul (solo backend; la UI es Etapa 2) |
|
||||
| 5 | Sincronización programada de clientes (normalización y deduplicación) | ❌ |
|
||||
| 6 | Sincronización programada de facturas y cotizaciones (estado normalizado, tipo de cambio, paginación) | ❌ |
|
||||
| 7 | Modelo central de facturas con estados (Emitida, Vigente, Vencida, Pagada, Cancelada) | ❌ |
|
||||
| 8 | Migraciones versionadas y datos de prueba | ❌ |
|
||||
|
||||
Avance parcial no contractual ya hecho: solución .NET 10 compilando (Api/Domain/Infrastructure/Integrations.Bind), `BalamDbContext` con `SyncCheckpoint`/`AuditLogEntry`, endpoints `/health` y `/api/bind/status`, frontend Angular 21 + Material con tema de marca, CLAUDE.md del repo con reglas duras y salvaguardas.
|
||||
|
||||
## 2. Calendario e hitos externos
|
||||
|
||||
| Fecha | Hito |
|
||||
|---|---|
|
||||
| jue 17-jul | Fin de la ventana planeada de backend base + multi-tenant |
|
||||
| **vie 18-jul** | **Demo semanal (30 min, Pedro + gerencia):** meta = login + roles + sync real de BIND en Postgres + endpoint de cartera |
|
||||
| sem 21-jul | Acceso Azure (Pedro + Noé) — no bloquea trabajo local |
|
||||
| **mié 22-jul** | **Sesión de definiciones Etapa 1** (Arturo + CEO): alta de cliente, fee, particularidades de envío, alcance Jira→BIND. Agenda en [Sesion-Etapa1-2026-07-22.md](Sesion-Etapa1-2026-07-22.md) |
|
||||
| jue 23-jul 7 pm | Validación del prototipo de Etapa 0 |
|
||||
| vie 24-jul | CI/CD + gestión de secretos · demo y cierre de etapa |
|
||||
|
||||
## 3. Plan de construcción — fases y bloques
|
||||
|
||||
Dependencias: B1 → (B2, B3); B3 → B4; B4 → B5; B1 → B6; B2+B1 → B7; todo → B8/B9. B6 y B7 son paralelizables.
|
||||
**Mínimo para la demo del 18-jul:** B0 + B1 + B2-núcleo + B3 + B4 (≈ 14–15 h desde el 16-jul).
|
||||
|
||||
### F1 (16-jul) — Fundación de datos
|
||||
|
||||
- **B0 · Preparación local (0.5–1 h):** `docker-compose.yml` con Postgres 17 (puerto 5433 para no chocar); cadena de conexión en `appsettings.Development.json`; `DesignTimeDbContextFactory`; referencia Infrastructure → Integrations.Bind. Docker 29.x ya instalado; `dotnet-ef` 10.0.4 global (invocarlo desde PowerShell — el PATH de git-bash no lo ve).
|
||||
- **B1 · Modelo de datos + Identity + migración inicial + seed (3.5–4.5 h):**
|
||||
- Entidades nuevas en `Balam.Domain/Entities/`: `Tenant`, `Client` (Id = GUID de BIND; `NormalizedRfc`, `NormalizedName`, `DuplicateOfClientId`, `SourceHash`, `DetailSyncedAtUtc`), `Invoice` (modelo central: `Uuid` null ⇒ prefactura, `OpenBalance` recalculado en sync, `CfdiPaymentTermRaw` PPD/PUE del detalle, `PaidDetectedAtUtc`, `LastKnownPlatformStatus`), `Quote`, `QuoteInvoiceLink` (ADR-003; incluye `JiraTicketKey` — ADR-006), `InvoiceStatusChange` (ADR-004), `WriteOperation` (nace aquí para no re-migrar).
|
||||
- Extender `SyncCheckpoint` y `AuditLogEntry` con `TenantId`; índice único `(TenantId, Resource)`.
|
||||
- `BalamDbContext` → `IdentityDbContext<BalamUser, IdentityRole<Guid>, Guid>` (`BalamUser` vive en Infrastructure; Domain queda puro).
|
||||
- Convenciones ANTES de la primera migración: fechas de BIND (sin zona) → `timestamp without time zone`; timestamps propios `*Utc` → `timestamptz`; montos `decimal(18,2)`, `ExchangeRate (18,6)`.
|
||||
- Migración `Inicial` + `DbSeeder` idempotente: tenant Balam (GUID fijo), 4 roles, 5 usuarios ficticios `*@balam.dev` (password vía `Seed:DefaultPassword`; jamás datos reales).
|
||||
|
||||
### F2 (17-jul) — Seguridad, bitácora y primer sync real
|
||||
|
||||
- **B2 · Auth + roles + bitácora (4–5 h):** ASP.NET Core Identity + JWT propio (`AddIdentityCore` + `AddJwtBearer`; NO `MapIdentityApi` — emite tokens opacos, el contrato dice JWT). `POST /api/auth/login`, `GET /api/auth/me`. Roles sin acentos como identificador (`Finanzas`, `Direccion`, `Operaciones`, `Administracion`). Policies nombradas (`PuedeVerCartera`, `PuedeDispararSync`, `PuedeVerBitacora`, `PuedeOperarEscritura`). `Jwt:SigningKey` en user-secrets/Key Vault con fail-fast (mismo patrón que `Bind:ApiKey`). **Sin refresh tokens en E1** (se difieren a Etapa 2 con la UI). Bitácora universal en dos piezas — NO interceptor de `SaveChanges` (inundaría con los upserts del sync): `AuditMiddleware` (peticiones mutantes + 401/403) e `IAuditLogger` explícito (logins, resumen de cada ciclo de sync, transiciones de estado, TODA operación de escritura). `GET /api/audit` (Direccion/Administracion).
|
||||
- **B3 · Sync de clientes (2.5–3.5 h):** la lista `Clients` NO tiene campo de fecha filtrable ⇒ full-scan barato de lista cada ciclo (~1–3 páginas con `PaginateAsync`) + comparación por `SourceHash` + **detalle solo para nuevos/cambiados** (ahí viven `CreditDays` y contactos). Normalización RFC/nombre (mayúsculas, sin acentos, sin sufijos societarios). Dedup MVP: marcar `DuplicateOfClientId` por `NormalizedRfc` exacto **excluyendo RFCs genéricos** (`XAXX010101000`/`XEXX010101000`); sin fuzzy matching. `POST /api/sync/run` (manual, para la demo) + `GET /api/sync/status`.
|
||||
|
||||
### F3 (18-jul am) — Cierre de demo
|
||||
|
||||
- **B4 · Sync de facturas/cotizaciones + estados + cartera (4.5–5.5 h):**
|
||||
- Facturas: delta `$filter=Date ge datetime'{cursor}'` + **re-barrido de la ventana de facturas abiertas** (los pagos caen en facturas viejas que el delta por fecha no atrapa) + detalle (1 GET c/u) SOLO nuevas/cambiadas con tope `Sync:MaxDetailPerCycle` (50). Nunca re-barrer todo el detalle (histórico > 1,000 filas).
|
||||
- Estados de plataforma como **función pura con pruebas** (`InvoiceStatusMapper`): Cancelada (`BindStatus=2`) → Pagada (`BindStatus=1` o saldo ≤ 0.01 con abonos) → Vencida (timbrada, saldo > 0, `ExpirationDate` < hoy MX) → Vigente (timbrada, en plazo) → Emitida (prefactura: `Uuid` null). ⚠️ Frontera Emitida/Vigente a confirmar el 22-jul — por eso es función pura, cambiarla cuesta minutos. Vencida/Vigente se computan en la consulta (dependen de "hoy"); lo persistido es `LastKnownPlatformStatus` solo para detectar transiciones.
|
||||
- Detección de pagos (ADR-004): transición → fila en `InvoiceStatusChanges` con **fecha de DETECCIÓN** (nunca presentarla como fecha valor) + `PaidDetectedAtUtc`.
|
||||
- Cotizaciones: delta por `CreationDate` con fallback a full-scan (filtro no validado contra producción); partidas `Items[]` NO se persisten en E1.
|
||||
- Moneda: catálogo `Currencies` (4 filas, 1 GET/ciclo) → `CurrencyCode` sin pedir detalle.
|
||||
- Endpoints: `GET /api/cartera` (agregados **por moneda — nunca sumar monedas entre sí**), `GET /api/invoices?status=&clientId=`, `GET /api/clients?search=` (marca duplicados), `GET /api/quotes`.
|
||||
- **El sync inicial del histórico se corre el jueves 17 en la noche — jamás en vivo en la demo** (en demo solo el incremental, <10 req).
|
||||
|
||||
### F4 (21-jul) — Robustez
|
||||
|
||||
- **B5 · Scheduler + backfill (2–3 h):** `BackgroundService` + `PeriodicTimer` cada 15 min (NO Quartz/Hangfire: un solo job, estado en checkpoints, sin dashboard). `SemaphoreSlim(1,1)` anti-solape; guarda de cuota vía `BindQuotaTracker` (si `Remaining < Sync:QuotaReserve` = 500, el ciclo se salta y se audita). Backfill gradual del detalle histórico por ciclo. Presupuesto validado: <10 req/ciclo ≈ 1,000 req/día = 5 % de la cuota. Nota Azure: requiere Always On + instancia única.
|
||||
- **B6 · RLS + tenancy (1.5–2.5 h):** RLS **real** de Postgres (compromiso contractual, no basta el filtro EF): migración con `ALTER TABLE ... ENABLE/FORCE ROW LEVEL SECURITY` + política `tenant_isolation` con `current_setting('app.tenant_id', true)::uuid` y `WITH CHECK`, por tabla de negocio; `TenantConnectionInterceptor` (`set_config` al abrir conexión); filtro global EF (`ITenantOwned` + `ICurrentTenantProvider` con tenant fijo del seed) como segunda capa. Fuera: CRUD de tenants, resolución por request, RLS sobre tablas Identity.
|
||||
|
||||
### F5 (22-jul, después de la sesión) — Escritura controlada
|
||||
|
||||
- **B7 · Capa de escritura controlada (3–4 h):** pipeline `Draft → DryRun → Confirm` persistido en `WriteOperations`, todo auditado. `BindWriteClient : BindClient` (hereda `RequestAsync` protected y sus salvaguardas). Flags `BindWrite:Enabled=false` y `BindWrite:AllowConvertToInvoice=false` por default. PPD default; PUE solo con `"confirmoPue": true` explícito (un PUE erróneo ya les costó un requerimiento del SAT). **CERO llamadas reales a BIND** — shapes de los POST provisionales (TODO hasta leer la doc del portal con Pedro); la conversión mock registra `QuoteInvoiceLink`. `Bind:Mode=Write` queda documentado como bloqueado hasta autorización expresa (ADR-001).
|
||||
|
||||
### F6 (23–24 jul) — Cierre
|
||||
|
||||
- **B8 · Suite de pruebas de flujos críticos (2.5–3.5 h):** nuevo proyecto `tests/Balam.Api.Tests` (xUnit + `WebApplicationFactory` + **SQLite in-memory**; NO EF InMemory — no valida FKs/únicos; NO Testcontainers — exige red). Cobertura contractual: matriz de roles 401/403/200, sync (delta/checkpoint/detección de pago), multimoneda (cartera no mezcla), salvaguardas de escritura (flag/modo/bitácora). Costo aceptado: RLS y `timestamptz` son Postgres-specific → verificación manual en runbook.
|
||||
- **B9 · Datos de prueba + runbook (1–2 h):** `DevDataSeeder` ficticio multimoneda/multi-estado (entregable 8 y respaldo de demo si BIND fallara); runbook: compose up → run (migra+siembra) → login → sync en vivo → cartera → bitácora → `/api/bind/status`.
|
||||
|
||||
## 4. Presupuesto de horas
|
||||
|
||||
| Bloque | Horas | ¿Recortable si aprieta? |
|
||||
|---|---|---|
|
||||
| B0 Preparación local | 0.5–1 | No |
|
||||
| B1 Modelo + Identity + migración + seed | 3.5–4.5 | No (fundacional) |
|
||||
| B2 Auth + roles + bitácora | 4–5 | `GET /api/audit` y middleware → post-demo (−1) |
|
||||
| B3 Sync clientes + dedup | 2.5–3.5 | Dedup solo RFC exacto (−0.5) |
|
||||
| B4 Sync facturas/cotizaciones + cartera | 4.5–5.5 | Equivalente MXN (−0.5); quotes full-scan simple (−0.5) |
|
||||
| B5 Scheduler + backfill | 2–3 | Backfill con tope fijo (−0.5) |
|
||||
| B6 RLS + tenancy | 1.5–2.5 | **No recortable a cero** (contractual) |
|
||||
| B7 Escritura controlada (dry-run) | 3–4 | Sin modelos BIND provisionales si el 22-jul cambia el alcance (−1) |
|
||||
| B8 Suite de pruebas | 2.5–3.5 | Solo casos redundantes |
|
||||
| B9 Datos de prueba + runbook + colchón | 1–2 | Seeder mínimo (−0.5) |
|
||||
| **Total** | **25–31** (nominal ~27.5) | Recortes identificados ≈ −5 h |
|
||||
|
||||
Primer recorte si el viernes se complica: bitácora consultable y sync de cotizaciones se posponen al lunes 21 — la demo exige login + roles + sync + cartera.
|
||||
|
||||
## 5. Decisiones de arquitectura (registro)
|
||||
|
||||
1. **Scheduler:** `BackgroundService` + `PeriodicTimer`, no Quartz/Hangfire — un solo job, estado ya persistido en `SyncCheckpoints` (ADR-002), cero esquema extra.
|
||||
2. **RLS en dos capas:** políticas reales de Postgres (compromiso "tenant_id + RLS") + filtro global EF como defensa; sin resolución de tenant por request (ADR-005: tenant único).
|
||||
3. **Identity + JWT sin refresh tokens** en E1; se agregan en Etapa 2 con la UI Angular.
|
||||
4. **Bitácora:** middleware HTTP + `IAuditLogger` explícito; NO interceptor EF (el primer sync insertaría > 1,000 entradas de ruido).
|
||||
5. **Pruebas con SQLite in-memory**; RLS/`timestamptz` quedan a verificación manual documentada (candidato a job de integración cuando exista CI el 24-jul).
|
||||
6. **Trampas resueltas por convención:** fechas Npgsql (BIND sin zona), "hoy" con `America/Mexico_City`, `partial class Program` para `WebApplicationFactory`, BD obligatoria con fail-fast.
|
||||
|
||||
## 6. Bloqueado y fuera de alcance
|
||||
|
||||
**Bloqueado hasta la sesión del 22-jul (no adelantar):** escritura real a BIND (ADR-001); reglas de alta de cliente, fee y particularidades de envío; alcance Jira→BIND (ADR-006 — solo se persiste el folio); semántica exacta Emitida/Vigente; mapeo `CFDIUse`→clave SAT.
|
||||
|
||||
**Fuera de la Etapa 1:** UI (Etapa 2); refresh tokens (E2); recordatorios a clientes (Anexo B); multiempresa (ADR-005); fecha valor de pagos/REP (ADR-004 — solo fecha de detección); aging 30/60/90 y tablero (Etapa 3); conciliación bancaria; CI/CD y Azure (actividades propias, 21–24 jul); envío de PDF (aunque `InvoicePdfAsync` ya existe).
|
||||
|
||||
## 7. Riesgos y pendientes de gestión
|
||||
|
||||
**Riesgos técnicos:** primera corrida del sync en vivo (mitigado: jueves noche); filtro `CreationDate` de Quotes sin validar (fallback full-scan desde el día uno); repo visible al cliente (solo datos ficticios, passwords de seed configurables, sin co-autoría en commits).
|
||||
|
||||
**Gestión que toca la etapa:**
|
||||
- Correo formal a Noé con las salvaguardas (borrador listo en [Borradores-seguimiento-tecnico.md](Borradores-seguimiento-tecnico.md)).
|
||||
- 5 preguntas técnicas a Pedro: pagos/REP, tabla `CFDIUse`→SAT, shape de `/{id}/xml`, webhooks vs polling, token del Power BI.
|
||||
- Revisar los **procedimientos de carga de facturas por cliente** que Erika envió por correo el 16-jul (insumo para particularidades de envío).
|
||||
- Borrar `bind_token_api.txt` del disco (el token ya vive en user-secrets).
|
||||
- Registrar horas diario en [Seguimiento-horas.csv](Seguimiento-horas.csv) y conciliar con Jira cuando Pedro lo habilite.
|
||||
- Preparar la sesión del 22-jul con [Sesion-Etapa1-2026-07-22.md](Sesion-Etapa1-2026-07-22.md).
|
||||
Binary file not shown.
Binary file not shown.
@@ -1,7 +1,7 @@
|
||||
# Plan de actividades — Etapas 0 a 3
|
||||
|
||||
**Proyecto:** Plataforma de Automatización Financiera · Balam
|
||||
> Fechas tentativas. Este documento incorpora los ajustes acordados al 15-jul y se mantiene sincronizado con el [Excel oficial consolidado](Plan-actividades-avance-2026-07-10-ajustado.xlsx). Las etapas 2 y 3 se afinan al validar el prototipo y cerrar las definiciones de la Etapa 1. Total estimado: ~6–7 semanas.
|
||||
> Fechas tentativas. Este documento incorpora los ajustes y el corte de avance al 16-jul y se mantiene sincronizado con el [Excel oficial consolidado](Plan-actividades-avance-2026-07-16.xlsx). Las etapas 2 y 3 se afinan al validar el prototipo y cerrar las definiciones de la Etapa 1. Total estimado: ~6–7 semanas.
|
||||
>
|
||||
> Las horas reales se registran en [`Seguimiento-horas.csv`](Seguimiento-horas.csv); el porcentaje de avance no sustituye ese registro.
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
| Accesos iniciales: token BIND + manual de marca | 01/07/2026 | 06/07/2026 | Balam / Johann | ✅ Entregados y validados |
|
||||
| Validación técnica de la API de BIND con la cuenta real | 06/07/2026 | 06/07/2026 | Johann | ✅ Realizada: 117 GET, sin escritura |
|
||||
| Prototipo visual navegable | 08/07/2026 | 10/07/2026 | Johann | ✅ Entregado por correo el 10-jul; validación pendiente |
|
||||
| Repositorio privado GitHub | 13/07/2026 | 16/07/2026 | Balam / Johann | Noé autorizó su creación; usuario `Johann-28`; invitación pendiente |
|
||||
| Repositorio privado GitHub | 13/07/2026 | 16/07/2026 | Balam / Johann | Invitación recibida y acceso confirmado; falta validar permiso de escritura |
|
||||
| Configuración de infraestructura en Azure (+ staging) | 21/07/2026 | 23/07/2026 | Johann | Apoyo: acceso acotado de Azure (Pedro+Noé); no bloquea trabajo local |
|
||||
| CI/CD + gestión de secretos | 24/07/2026 | 24/07/2026 | Johann | Depende de repo y Azure |
|
||||
| Documento de hallazgos + ADRs + plan refinado | 10/07/2026 | 23/07/2026 | Johann | Borrador creado; cerrar tras las sesiones del 22–23 jul |
|
||||
@@ -24,6 +24,8 @@
|
||||
|
||||
## Etapa 1
|
||||
|
||||
> Plan detallado de ejecución: [Plan-Etapa1.md](Plan-Etapa1.md)
|
||||
|
||||
| Actividad | Inicio | Fin | Responsable | Apoyo de Balam |
|
||||
|---|---|---|---|---|
|
||||
| Backend base: autenticación, roles y bitácora de auditoría | 13/07/2026 | 17/07/2026 | Johann | Trabajo local; publicar al recibir el repositorio |
|
||||
|
||||
@@ -0,0 +1,98 @@
|
||||
"""Genera el Excel de avance que se compartirá con Erika el 16-jul-2026."""
|
||||
|
||||
from copy import copy
|
||||
from pathlib import Path
|
||||
from shutil import copy2
|
||||
|
||||
import openpyxl
|
||||
|
||||
|
||||
DIRECTORY = Path(__file__).parent
|
||||
SOURCE = DIRECTORY / "Plan-actividades-avance-2026-07-10-ajustado.xlsx"
|
||||
TARGET = DIRECTORY / "Plan-actividades-avance-2026-07-16.xlsx"
|
||||
|
||||
|
||||
def find_row(sheet, task):
|
||||
for row in range(2, sheet.max_row + 1):
|
||||
if sheet.cell(row, 1).value == task:
|
||||
return row
|
||||
raise ValueError(f"No se encontró la actividad: {task}")
|
||||
|
||||
|
||||
copy2(SOURCE, TARGET)
|
||||
workbook = openpyxl.load_workbook(TARGET)
|
||||
sheet = workbook["Plan"]
|
||||
|
||||
sheet.cell(1, 8, "Horas al 16/07")
|
||||
|
||||
updates = {
|
||||
"Integracion de plataformas Balam": (
|
||||
0.20,
|
||||
"Corte de avance al 16-jul; Etapa 0 en validación y Etapa 1 en curso",
|
||||
),
|
||||
"Etapa 0": (
|
||||
0.91,
|
||||
"22 h registradas; prototipo entregado y validación agendada para el 23-jul",
|
||||
),
|
||||
"Etapa 1": (
|
||||
0.20,
|
||||
"8 h registradas; 1 de 8 entregables terminado y plataforma base parcial",
|
||||
),
|
||||
"Backend base: autenticación, roles y bitácora de auditoría": (
|
||||
0.30,
|
||||
"API, health check y DbContext base listos; autenticación, roles y auditoría pendientes",
|
||||
),
|
||||
"Repositorio privado GitHub": (
|
||||
0.90,
|
||||
"Invitación recibida y acceso confirmado el 16-jul; falta validar permiso de escritura",
|
||||
),
|
||||
"Arquitectura multi-tenant (tenant_id + RLS)": (
|
||||
0.25,
|
||||
"Diseño técnico inicial; entidades tenant y políticas RLS pendientes",
|
||||
),
|
||||
"Cliente de la API de BIND (reintentos, OData, errores)": (
|
||||
1.00,
|
||||
"Terminado: cliente C# solo lectura, paginación, reintentos y cuota; 13/13 pruebas aprobadas",
|
||||
),
|
||||
"Modelo de facturas + migraciones + datos de prueba": (
|
||||
0.10,
|
||||
"Base operativa con SyncCheckpoint y AuditLog; modelo de facturas y migración pendientes",
|
||||
),
|
||||
"Demostración semanal (viernes) + reporte de horas": (
|
||||
0.50,
|
||||
"Corte del 16-jul preparado; control local mientras se habilita Jira",
|
||||
),
|
||||
}
|
||||
|
||||
for task, (progress, note) in updates.items():
|
||||
row = find_row(sheet, task)
|
||||
sheet.cell(row, 7, progress)
|
||||
sheet.cell(row, 10, note)
|
||||
|
||||
stage1_row = find_row(sheet, "Etapa 1")
|
||||
sheet.cell(stage1_row, 8, "=SUM(H12:H24)")
|
||||
|
||||
for row in range(2, sheet.max_row + 1):
|
||||
sheet.cell(row, 7).number_format = "0%"
|
||||
|
||||
sheet.oddFooter.center.text = (
|
||||
"Avance al 16-jul-2026 · Horas reconstruidas pendientes de conciliación con Jira"
|
||||
)
|
||||
|
||||
# Conserva los estilos y usa el nombre de archivo como título impreso.
|
||||
sheet.oddHeader.center.text = "Plan de actividades y avance · Proyecto Balam"
|
||||
for cell in sheet[1]:
|
||||
cell._style = copy(cell._style)
|
||||
|
||||
temporary = TARGET.with_suffix(".tmp.xlsx")
|
||||
workbook.save(temporary)
|
||||
|
||||
check = openpyxl.load_workbook(temporary, data_only=False)["Plan"]
|
||||
assert check.cell(find_row(check, "Etapa 1"), 7).value == 0.20
|
||||
assert check.cell(
|
||||
find_row(check, "Cliente de la API de BIND (reintentos, OData, errores)"), 7
|
||||
).value == 1.00
|
||||
assert check.cell(find_row(check, "Etapa 1"), 8).value == "=SUM(H12:H24)"
|
||||
|
||||
temporary.replace(TARGET)
|
||||
print(f"Generado: {TARGET}")
|
||||
@@ -0,0 +1,113 @@
|
||||
"""Ordena las actividades del Excel de avance por fecha de Comienzo, dentro de
|
||||
cada etapa, conservando estilos, formatos, fórmulas de resumen y encabezados.
|
||||
|
||||
Estructura del plan:
|
||||
fila 2 = Integración (resumen maestro) -> fija
|
||||
fila 3 = Etapa 0 (encabezado) -> fija
|
||||
filas 4-10 = tareas de Etapa 0 -> se ordenan por Comienzo
|
||||
fila 11 = Etapa 1 (encabezado) -> fija
|
||||
filas 12-24 = tareas de Etapa 1 -> se ordenan por Comienzo
|
||||
|
||||
Las celdas de fecha son texto ("mié 01/07/26"), así que se parsea el dd/mm/yy
|
||||
para ordenar cronológicamente (no alfabéticamente, que ordenaría por día de
|
||||
semana). En empate de Comienzo, se desempata por Fin y luego por el orden
|
||||
original (estable), para no barajar tareas que arrancan el mismo día.
|
||||
"""
|
||||
|
||||
import re
|
||||
from copy import copy
|
||||
from pathlib import Path
|
||||
|
||||
import openpyxl
|
||||
|
||||
DIRECTORY = Path(__file__).parent
|
||||
TARGET = DIRECTORY / "Plan-actividades-avance-2026-07-16.xlsx"
|
||||
|
||||
# Grupos de filas de tareas a ordenar (rango inclusivo). Los encabezados
|
||||
# (2, 3, 11) quedan fuera y no se mueven.
|
||||
GROUPS = [(4, 10), (12, 24)]
|
||||
|
||||
DATE_RE = re.compile(r"(\d{2})/(\d{2})/(\d{2})")
|
||||
|
||||
|
||||
def sort_key_from_cell(value):
|
||||
"""Devuelve (aaaa, mm, dd) desde 'mié 01/07/26'; None si no hay fecha."""
|
||||
if value is None:
|
||||
return (9999, 99, 99)
|
||||
m = DATE_RE.search(str(value))
|
||||
if not m:
|
||||
return (9999, 99, 99)
|
||||
dd, mm, yy = (int(g) for g in m.groups())
|
||||
return (2000 + yy, mm, dd)
|
||||
|
||||
|
||||
def snapshot_row(sheet, row, max_col):
|
||||
"""Captura valor + estilo + formato de cada celda de la fila, y el alto."""
|
||||
cells = []
|
||||
for col in range(1, max_col + 1):
|
||||
c = sheet.cell(row, col)
|
||||
cells.append(
|
||||
{
|
||||
"value": c.value,
|
||||
"style": copy(c._style),
|
||||
"number_format": c.number_format,
|
||||
}
|
||||
)
|
||||
height = sheet.row_dimensions[row].height
|
||||
return {"cells": cells, "height": height}
|
||||
|
||||
|
||||
def restore_row(sheet, row, snap, max_col):
|
||||
for col in range(1, max_col + 1):
|
||||
c = sheet.cell(row, col)
|
||||
data = snap["cells"][col - 1]
|
||||
c.value = data["value"]
|
||||
c._style = copy(data["style"])
|
||||
c.number_format = data["number_format"]
|
||||
if snap["height"] is not None:
|
||||
sheet.row_dimensions[row].height = snap["height"]
|
||||
|
||||
|
||||
def main():
|
||||
wb = openpyxl.load_workbook(TARGET)
|
||||
sh = wb["Plan"]
|
||||
max_col = sh.max_column
|
||||
|
||||
for start, end in GROUPS:
|
||||
snaps = [snapshot_row(sh, r, max_col) for r in range(start, end + 1)]
|
||||
# Orden estable: por (Comienzo, Fin) usando el índice original como
|
||||
# tercer criterio implícito de estabilidad de sorted().
|
||||
order = sorted(
|
||||
range(len(snaps)),
|
||||
key=lambda i: (
|
||||
sort_key_from_cell(snaps[i]["cells"][2]["value"]), # col C Comienzo
|
||||
sort_key_from_cell(snaps[i]["cells"][3]["value"]), # col D Fin
|
||||
),
|
||||
)
|
||||
reordered = [snaps[i] for i in order]
|
||||
for offset, snap in enumerate(reordered):
|
||||
restore_row(sh, start + offset, snap, max_col)
|
||||
|
||||
tmp = TARGET.with_suffix(".tmp.xlsx")
|
||||
wb.save(tmp)
|
||||
|
||||
# Verificación: cada grupo queda no-decreciente por Comienzo.
|
||||
check = openpyxl.load_workbook(tmp)["Plan"]
|
||||
for start, end in GROUPS:
|
||||
prev = (0, 0, 0)
|
||||
for r in range(start, end + 1):
|
||||
k = sort_key_from_cell(check.cell(r, 3).value)
|
||||
assert k >= prev, f"Fila {r} fuera de orden: {k} < {prev}"
|
||||
prev = k
|
||||
# Los encabezados de etapa siguen en su lugar.
|
||||
assert check.cell(3, 1).value == "Etapa 0"
|
||||
assert check.cell(11, 1).value == "Etapa 1"
|
||||
# Las fórmulas de resumen se conservan.
|
||||
assert str(check.cell(11, 8).value).startswith("=SUM")
|
||||
|
||||
tmp.replace(TARGET)
|
||||
print("Ordenado correctamente:", TARGET.name)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,72 @@
|
||||
# Plan: Documento "Plan-Etapa1.md" — plan fiel y completo de la Etapa 1
|
||||
|
||||
## Contexto
|
||||
|
||||
Johann necesita el plan de trabajo de la **Etapa 1 — "Plataforma base y sincronización con BIND"** (32–39 h contractuales, semanas 2–3, ventana 13–24 jul). Hoy 16-jul solo 1 de los 8 entregables contractuales está terminado (el cliente API de BIND, portado a C# con 13 pruebas) y hay 8 h registradas de la etapa; quedan ~24–31 h y la demo semanal del **viernes 18-jul** es el compromiso más cercano. El usuario pidió un plan "lo más fiel posible" a toda la documentación (propuesta comercial v1.2, ADRs de Etapa 0, VALIDACION-API.md, bitácora, seguimiento de horas) — se revisó todo con 2 agentes de exploración y 1 de diseño.
|
||||
|
||||
**Decisión del usuario:** generar SOLO el documento (no arrancar implementación todavía), guardado en el repo privado de gestión `../balam` (nunca en el repo del cliente `balam-plataforma` — regla del CLAUDE.md de ese repo).
|
||||
|
||||
## Qué se hará
|
||||
|
||||
1. **Crear `planeacion/Plan-Etapa1.md`** en `c:\Users\JohannVelazquez\Desktop\Personal\balam` con el contenido detallado abajo.
|
||||
2. **Agregar 1 línea de referencia** en `planeacion/Plan-actividades.md` (sección Etapa 1) apuntando al nuevo documento: `> Plan detallado de ejecución: [Plan-Etapa1.md](Plan-Etapa1.md)`.
|
||||
3. Nada más: sin commits al repo del cliente, sin código. (El repo bitácora tiene cambios sin commitear del usuario; NO commitear nada ahí tampoco, solo dejar los archivos.)
|
||||
|
||||
## Contenido del documento Plan-Etapa1.md
|
||||
|
||||
Estructura y contenido esencial (redactado en español MX, estilo de los docs existentes de `planeacion/`):
|
||||
|
||||
### §1 Resumen y estado (al 16-jul)
|
||||
- Entregable contractual de la etapa (propuesta L142–157): plataforma que ingiere clientes/facturas de BIND, los normaliza, mantiene trazabilidad y habilita capa de escritura controlada. 32–39 h · $19,200–$23,400.
|
||||
- Horas: 8.00 h registradas (reconstruidas) → quedan ~24–31 h. Tabla de los 8 entregables contractuales con estado real (solo cliente BIND ✅; scaffold/DbContext/entidades semilla ✅ como avance parcial; resto ❌).
|
||||
- Hechos del repo de código: solución .NET 10 compilando, 13 pruebas verdes, API con /health y /api/bind/status, Angular 21 con tema de marca.
|
||||
|
||||
### §2 Calendario e hitos externos
|
||||
| Fecha | Hito |
|
||||
|---|---|
|
||||
| jue 17-jul | Fin de ventana planeada de backend base + multi-tenant |
|
||||
| vie 18-jul | **Demo semanal (30 min, Pedro + gerencia)** — meta: login+roles, sync real de BIND en Postgres, endpoint de cartera |
|
||||
| sem 21-jul | Acceso Azure (Pedro+Noé) — no bloquea trabajo local |
|
||||
| mié 22-jul | **Sesión de definiciones Etapa 1** (Arturo + CEO): alta cliente, fee, envío, alcance Jira→BIND |
|
||||
| jue 23-jul 7pm | Validación del prototipo Etapa 0 |
|
||||
| vie 24-jul | CI/CD + secretos · demo/cierre de etapa |
|
||||
|
||||
### §3 Plan de construcción por bloques (B0–B9, fases F1–F6)
|
||||
Incluir el plan técnico completo producido en el diseño (resumido aquí; el documento lo lleva íntegro):
|
||||
|
||||
- **F1 (16-jul) — B0 Preparación (0.5–1 h) + B1 Modelo de datos (3.5–4.5 h):** docker-compose Postgres 17 local; entidades `Tenant`, `Client`, `Invoice` (modelo central con estados), `Quote`, `QuoteInvoiceLink` (ADR-003), `InvoiceStatusChange` (ADR-004), `WriteOperation`; extender `SyncCheckpoint`/`AuditLogEntry` con TenantId; `BalamDbContext` → `IdentityDbContext<BalamUser,…>`; convenciones de fechas Npgsql (BIND sin zona → `timestamp without time zone`) y decimales; migración `Inicial` + `DbSeeder` idempotente (tenant Balam, 4 roles, 5 usuarios ficticios).
|
||||
- **F2 (17-jul) — B2 Auth+roles+bitácora (4–5 h) + B3 Sync clientes (2.5–3.5 h):** ASP.NET Core Identity + JWT propio (sin MapIdentityApi, sin refresh tokens en E1); roles `Finanzas/Direccion/Operaciones/Administracion` (sin acentos como identificador); policies nombradas + matriz de acceso por endpoint; `AuditMiddleware` (mutantes + 401/403) + `IAuditLogger` explícito (login, ciclos de sync, escritura) — NO interceptor EF (inundaría con upserts del sync); sync de clientes: full-scan barato de lista (sin campo fecha filtrable) + `SourceHash` + detalle solo nuevos/cambiados + normalización RFC/nombre + dedup por RFC exacto excluyendo genéricos (`DuplicateOfClientId`).
|
||||
- **F3 (18-jul am) — B4 Sync facturas/cotizaciones + cartera (4.5–5.5 h) → DEMO:** delta `Date ge` desde checkpoint + re-barrido de ventana de facturas abiertas (los pagos caen en facturas viejas) + detalle solo nuevas/cambiadas con tope por ciclo; mapeo de estados plataforma (Emitida=prefactura, Vigente, Vencida=derivada con fecha MX, Pagada, Cancelada) como **función pura con pruebas** (frontera Emitida/Vigente a confirmar el 22-jul); detección de pagos por transición → `InvoiceStatusChange` con fecha de DETECCIÓN (ADR-004); endpoints `GET /api/cartera` (agregados POR MONEDA, nunca mezclarlas), `/api/invoices`, `/api/clients`, `/api/quotes`, `POST /api/sync/run`; **sync inicial del histórico (>1,000 facturas) se corre el jueves en la noche, nunca en vivo**.
|
||||
- **F4 (21-jul) — B5 Scheduler (2–3 h) + B6 RLS (1.5–2.5 h):** `BackgroundService` + `PeriodicTimer` 15 min (NO Quartz/Hangfire — un solo job, estado en checkpoints), `SemaphoreSlim` anti-solape, guarda de cuota vía `BindQuotaTracker` (reserva configurable), backfill gradual de detalle histórico; RLS REAL de Postgres (contractual): políticas `tenant_isolation` por tabla con `current_setting('app.tenant_id')` + `FORCE` + `WITH CHECK`, `TenantConnectionInterceptor` con `set_config`, más filtro global EF (`ITenantOwned`) como segunda capa; fuera: CRUD tenants, resolución por request.
|
||||
- **F5 (22-jul, post-sesión) — B7 Capa de escritura controlada (3–4 h):** pipeline `Draft → DryRun → Confirm` persistido en `WriteOperations`, todo auditado; `BindWriteClient : BindClient` (reusa `RequestAsync` protected y sus salvaguardas); feature flags `BindWrite:Enabled=false` default; PPD default, PUE solo con confirmación explícita (salvaguarda 4); CERO llamadas reales a BIND — shapes provisionales marcados TODO hasta doc del portal con Pedro; conversión mock registra `QuoteInvoiceLink`.
|
||||
- **F6 (23–24 jul) — B8 Suite de pruebas (2.5–3.5 h) + B9 datos de prueba + runbook (1–2 h):** nuevo proyecto `tests/Balam.Api.Tests` con `WebApplicationFactory` + **SQLite in-memory** (no EF InMemory — no valida FKs/únicos; no Testcontainers — exige red/Docker en tests); cobertura contractual: matriz de roles, sync (delta/checkpoint/detección de pago), multimoneda, salvaguardas de escritura; `DevDataSeeder` ficticio multimoneda/multi-estado (respaldo de demo); runbook de demo.
|
||||
|
||||
### §4 Presupuesto de horas
|
||||
Tabla B0–B9 (nominal ~27.5 h, rango 25–31) + recortes identificados (≈ −5 h): audit endpoint y quotes-sync pueden moverse post-demo; dedup solo RFC; backfill mínimo. B6 (RLS) NO recortable a cero por ser contractual.
|
||||
|
||||
### §5 Decisiones de arquitectura (con porqués, citables)
|
||||
Scheduler BackgroundService vs Quartz; RLS real en dos capas; Identity+JWT sin refresh tokens (diferidos a Etapa 2 con la UI); bitácora middleware+servicio vs interceptor EF; SQLite en pruebas con costo aceptado (RLS/timestamptz se verifican manual en runbook); trampas Npgsql (fechas), "hoy" con zona México, `partial class Program`.
|
||||
|
||||
### §6 Bloqueado hasta el 22-jul y fuera de alcance
|
||||
- Bloqueado: escritura real a BIND (ADR-001), reglas de alta de cliente/fee/envío, alcance Jira→BIND, semántica exacta Emitida/Vigente, mapeo `CFDIUse`→SAT.
|
||||
- Fuera de Etapa 1: UI (E2), refresh tokens (E2), recordatorios a clientes (Anexo B), multiempresa (ADR-005), fecha valor de pagos/REP (ADR-004), aging/tablero (E3), conciliación, CI/CD+Azure (actividades propias 21–24 jul), envío de PDF.
|
||||
|
||||
### §7 Riesgos y gestión (no técnico, en el mismo doc)
|
||||
- Riesgos: primera corrida de sync en vivo (mitigado: jueves noche); filtro `CreationDate` de Quotes no validado (fallback full-scan); repo visible al cliente (solo datos ficticios, sin co-autoría en commits).
|
||||
- Pendientes de gestión que tocan la etapa: correo formal a Noé (salvaguardas — borrador listo), 5 preguntas técnicas a Pedro (REP/pagos, `CFDIUse`, `/xml`, webhooks, token Power BI), borrar `bind_token_api.txt` del disco, registrar horas diario en `Seguimiento-horas.csv`, preparar sesión 22-jul (ya existe `Sesion-Etapa1-2026-07-22.md` — referenciarla).
|
||||
|
||||
## Archivos a crear/modificar
|
||||
|
||||
| Archivo | Acción |
|
||||
|---|---|
|
||||
| `c:\Users\JohannVelazquez\Desktop\Personal\balam\planeacion\Plan-Etapa1.md` | Crear (documento completo) |
|
||||
| `c:\Users\JohannVelazquez\Desktop\Personal\balam\planeacion\Plan-actividades.md` | Agregar 1 línea de referencia en la sección Etapa 1 |
|
||||
|
||||
## Verificación
|
||||
|
||||
1. El documento existe, abre bien en Markdown y sus tablas renderizan (revisión visual).
|
||||
2. Cifras contractuales cuadran con la propuesta: 32–39 h, entregables L148–155, roles y stack (verificar contra `propuesta/00 - PROPUESTA-COMERCIAL.md`).
|
||||
3. Cifras de horas cuadran con `Seguimiento-horas.md` (8.00 h E1, 30 h totales).
|
||||
4. Fechas cuadran con `bitacora/REGISTRO.md` #42–#43 (22-jul, 23-jul, Azure sem 21, CI/CD 24).
|
||||
5. El enlace desde `Plan-actividades.md` navega al nuevo documento.
|
||||
6. Confirmar que NADA se escribió en el repo del cliente (`balam-plataforma` limpio: `git -C ...balam-plataforma status`).
|
||||
Reference in New Issue
Block a user