Compare commits
25 Commits
5004269664
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| cbb29086cc | |||
| 980baf0a0a | |||
| 820b8d41c8 | |||
| bee2d02f50 | |||
| bebca73bfe | |||
| 117606d71d | |||
| c3e984944e | |||
| 388e772c40 | |||
| e057fb9127 | |||
| 3a66e5aee1 | |||
| 8da11ea164 | |||
| a2104f2a63 | |||
| 6daeeab72d | |||
| 4dca2ecc7c | |||
| 365c99acb6 | |||
| b358570443 | |||
| 8967fa2fed | |||
| fde0b9333b | |||
| 9ccfbe262e | |||
| 12006b5fa4 | |||
| 231a121327 | |||
| 082a89eb05 | |||
| f69d6104bd | |||
| 5f500d9a78 | |||
| b3e4673238 |
@@ -4,6 +4,22 @@ Thumbs.db
|
|||||||
.vscode/
|
.vscode/
|
||||||
.idea/
|
.idea/
|
||||||
|
|
||||||
|
# Secretos — NUNCA versionar (tokens de API de BIND y Jira, producción)
|
||||||
|
bind_token_api.txt
|
||||||
|
propuesta/token-jira.txt
|
||||||
|
*.env
|
||||||
|
|
||||||
|
# Repo de código del cliente (github.com/pedro-balam-itsm/BALAM) — clon local, tiene su propio git
|
||||||
|
balam-plataforma/
|
||||||
|
|
||||||
|
# Evidencia financiera sensible local — no subir al repositorio
|
||||||
|
Resumen de movimientos.pdf
|
||||||
|
|
||||||
|
# Respaldos de la BD local (pg_dump formato custom) — traen datos REALES de
|
||||||
|
# clientes de BIND; jamás al historial de git.
|
||||||
|
# Restaurar: docker exec -i balam-postgres-dev pg_restore -U balam -d balam --clean --if-exists < respaldos/<archivo>
|
||||||
|
respaldos/
|
||||||
|
|
||||||
# Python (skill proposal-pdf)
|
# Python (skill proposal-pdf)
|
||||||
__pycache__/
|
__pycache__/
|
||||||
*.pyc
|
*.pyc
|
||||||
|
|||||||
@@ -2,8 +2,8 @@
|
|||||||
|
|
||||||
> **Fuente de la verdad del proyecto.** Este repositorio concentra todo: propuesta, comunicaciones, fuentes y prototipos. Si pasa algo (llamada, correo, mensaje, decisión), se registra en la [bitácora](bitacora/). Empieza por aquí.
|
> **Fuente de la verdad del proyecto.** Este repositorio concentra todo: propuesta, comunicaciones, fuentes y prototipos. Si pasa algo (llamada, correo, mensaje, decisión), se registra en la [bitácora](bitacora/). Empieza por aquí.
|
||||||
|
|
||||||
**Estado:** 🟢 **Discovery en marcha (Etapa 0)** — contrato firmado (26-jun), kickoff realizado (1-jul), **primera sesión de Discovery realizada (6-jul: proceso de facturación mapeado end-to-end con Ara y Arturo)**. Cambios de reglas del 6-jul: **lista blanca eliminada** (recordatorios a todos) y **cotización BIND obligatoria**. Johann trabaja el **prototipo** con el flujo real. Bloqueadores: (1) **token BIND** — ya en gestión por Erika (6-jul; entrega esperada lun 6–mar 7 jul, por correo), además de repo y Azure; (2) confirmación de Balam a la propuesta v1.2 (enviada 2-jul) para emitir la **factura de 30 h**.
|
**Estado:** 🟢 **Etapa 0 en validación + Etapa 1 iniciada en local.** Discovery de facturación, envío y cobranza completo (6–7 jul); prototipo entregado el 10-jul y sesión de validación confirmada para el **23-jul, 7:00 pm**. Sesión de reglas/dudas de Etapa 1 confirmada para el **22-jul** con Arturo y CEO. Token BIND validado en solo lectura; repo GitHub autorizado por Noé y pendiente de invitación para `Johann-28`; Azure programado para la semana del 21-jul. La confirmación para emitir la **factura inicial de 30 h** sigue pendiente por viaje de Noé y CEO. Control local de horas iniciado.
|
||||||
_Última actualización: 2026-07-06._
|
_Última actualización: 2026-07-15._
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -21,8 +21,9 @@ Plataforma web para centralizar y automatizar **facturación y cobranza** de Bal
|
|||||||
| [`fuentes/`](fuentes/) | Material crudo: PRD, transcripciones de llamadas, `.eml`. Evidencia, no se edita. |
|
| [`fuentes/`](fuentes/) | Material crudo: PRD, transcripciones de llamadas, `.eml`. Evidencia, no se edita. |
|
||||||
| [`bind-api-sandbox/`](bind-api-sandbox/) | Prototipo técnico del cliente/mock de la API de BIND (TypeScript; el productivo será .NET). |
|
| [`bind-api-sandbox/`](bind-api-sandbox/) | Prototipo técnico del cliente/mock de la API de BIND (TypeScript; el productivo será .NET). |
|
||||||
| [`.claude/skills/proposal-pdf/`](.claude/skills/proposal-pdf/) | **Skill** que genera el PDF de la propuesta/cotización con diseño editorial (portada full-bleed, TOC con páginas reales, footers "Confidential"). Pipeline Chromium + 2 pasos. Setup y uso en su [SKILL.md](.claude/skills/proposal-pdf/SKILL.md) (incluye nota de Windows). |
|
| [`.claude/skills/proposal-pdf/`](.claude/skills/proposal-pdf/) | **Skill** que genera el PDF de la propuesta/cotización con diseño editorial (portada full-bleed, TOC con páginas reales, footers "Confidential"). Pipeline Chromium + 2 pasos. Setup y uso en su [SKILL.md](.claude/skills/proposal-pdf/SKILL.md) (incluye nota de Windows). |
|
||||||
| [`planeacion/`](planeacion/) | **Plan de ejecución:** [Plan-actividades.xlsx](planeacion/Plan-actividades.xlsx) (+ [`.md`](planeacion/Plan-actividades.md)) — actividades por etapa (0–3) con fechas, responsables y sesiones, para el seguimiento con Erika (Jira/Gantt). |
|
| [`planeacion/`](planeacion/) | **Plan y seguimiento:** [Excel oficial consolidado](planeacion/Plan-actividades-avance-2026-07-10-ajustado.xlsx), [plan legible](planeacion/Plan-actividades.md), [control de horas](planeacion/Seguimiento-horas.md) ([CSV](planeacion/Seguimiento-horas.csv)) y [hallazgos/ADRs de Etapa 0](planeacion/Hallazgos-y-decisiones-Etapa0.md). |
|
||||||
| [`marca/`](marca/) | **Tokens de marca** ([Marca-Balam.md](marca/Marca-Balam.md)) destilados del manual de imagen corporativa: color (Amarillo `#F7BD0C`, Café `#331F0E`), tipografía (Poppins) y uso del logo, para el prototipo y la UI. Manual crudo en [`fuentes/`](fuentes/). |
|
| [`marca/`](marca/) | **Tokens de marca** ([Marca-Balam.md](marca/Marca-Balam.md)) destilados del manual de imagen corporativa: color (Amarillo `#F7BD0C`, Café `#331F0E`), tipografía (Poppins) y uso del logo, para el prototipo y la UI. Manual crudo en [`fuentes/`](fuentes/). |
|
||||||
|
| [`prototipo/`](prototipo/) | **Prototipo visual navegable (Etapa 0):** [Prototipo-Etapa0.html](prototipo/Prototipo-Etapa0.html) — HTML autocontenido (doble clic) con el flujo real del Discovery 6-jul: Jira → cotización BIND → prefactura → validación humana → CFDI → envío por cliente + cobranza y bitácora. Marca Balam (café/amarillo, Poppins), datos ficticios. Para validar con Ara, Arturo y Pedro. |
|
||||||
|
|
||||||
## 3. Datos clave
|
## 3. Datos clave
|
||||||
|
|
||||||
@@ -42,7 +43,7 @@ Plataforma web para centralizar y automatizar **facturación y cobranza** de Bal
|
|||||||
|
|
||||||
**Comunicación:** canal de WhatsApp (ágil) + correo para evidencia formal. Gestión de avances en Jira.
|
**Comunicación:** canal de WhatsApp (ágil) + correo para evidencia formal. Gestión de avances en Jira.
|
||||||
|
|
||||||
## 4. Alcance y comercial (propuesta v1.1)
|
## 4. Alcance y comercial (propuesta v1.2)
|
||||||
|
|
||||||
- **MVP Fase 1:** integración BIND vía API · **emisión de facturas MXN (con IVA) / USD (sin IVA)** con dry-run + confirmación humana (timbra el PAC de BIND) · catálogo de clientes + lista blanca configurable _(al 6-jul: vacía — Ara decidió recordatorios a todos, sin excepciones)_ · cobranza operativa (aging + alertas internas) · dashboard · reportes CSV/XLSX · bitácora · multimoneda con TC DOF. **Flujo objetivo validado en Discovery (6-jul):** Jira (disparador) → **cotización BIND (obligatoria)** → prefactura → validación humana → CFDI → envío.
|
- **MVP Fase 1:** integración BIND vía API · **emisión de facturas MXN (con IVA) / USD (sin IVA)** con dry-run + confirmación humana (timbra el PAC de BIND) · catálogo de clientes + lista blanca configurable _(al 6-jul: vacía — Ara decidió recordatorios a todos, sin excepciones)_ · cobranza operativa (aging + alertas internas) · dashboard · reportes CSV/XLSX · bitácora · multimoneda con TC DOF. **Flujo objetivo validado en Discovery (6-jul):** Jira (disparador) → **cotización BIND (obligatoria)** → prefactura → validación humana → CFDI → envío.
|
||||||
- **Inversión:** **$67,200 – $81,600 MXN + IVA** · **6–7 semanas** (~20 h/sem) · tarifa **$600 MXN/h** · modelo Time & Materials con tope por etapa (monto final se confirma en Discovery).
|
- **Inversión:** **$67,200 – $81,600 MXN + IVA** · **6–7 semanas** (~20 h/sem) · tarifa **$600 MXN/h** · modelo Time & Materials con tope por etapa (monto final se confirma en Discovery).
|
||||||
@@ -70,15 +71,20 @@ Plataforma web para centralizar y automatizar **facturación y cobranza** de Bal
|
|||||||
| 2026-07-01 | **Kickoff realizado.** BIND: **token de Arturo** (solo consulta, se prueba primero); **Balam crea el repo** (GitHub privado) y **gestiona Azure** (Pedro+Noé); horas de Johann en **Jira** (corte lunes de Erika); Discovery **con Arturo + Araceli**. **Manual de marca recibido.** Abierto: **vincular la factura de 30 h** (contrato vs propuesta) antes de emitir — Johann espera correo de Balam. |
|
| 2026-07-01 | **Kickoff realizado.** BIND: **token de Arturo** (solo consulta, se prueba primero); **Balam crea el repo** (GitHub privado) y **gestiona Azure** (Pedro+Noé); horas de Johann en **Jira** (corte lunes de Erika); Discovery **con Arturo + Araceli**. **Manual de marca recibido.** Abierto: **vincular la factura de 30 h** (contrato vs propuesta) antes de emitir — Johann espera correo de Balam. |
|
||||||
| 2026-07-02 | **Propuesta v1.2 enviada por correo** (30 h de facturación inicial explícitas, condiciones comerciales en §3.3). En espera de confirmación para facturar. |
|
| 2026-07-02 | **Propuesta v1.2 enviada por correo** (30 h de facturación inicial explícitas, condiciones comerciales en §3.3). En espera de confirmación para facturar. |
|
||||||
| 2026-07-06 | **Discovery #1 realizado (proceso de facturación).** Flujo mapeado: Jira ITSM mandatorio → prefactura BIND → CFDI → envío por correo con particularidades por cliente. **Lista blanca ELIMINADA** (recordatorios a todos — decisión de Ara) y **cotización BIND obligatoria** como inicio del flujo. Validaciones de oro: PPD default (requerimiento SAT previo por PUE erróneo), IVA 16%/0% manual en BIND. Volumen real: ~55 facturas/mes de 4–6 clientes. Pendiente de Balam: Excel de particularidades de envío. |
|
| 2026-07-06 | **Discovery #1 realizado (proceso de facturación).** Flujo mapeado: Jira ITSM mandatorio → prefactura BIND → CFDI → envío por correo con particularidades por cliente. **Lista blanca ELIMINADA** (recordatorios a todos — decisión de Ara) y **cotización BIND obligatoria** como inicio del flujo. Validaciones de oro: PPD default (requerimiento SAT previo por PUE erróneo), IVA 16%/0% manual en BIND. Volumen real: ~55 facturas/mes de 4–6 clientes. Pendiente de Balam: Excel de particularidades de envío. |
|
||||||
|
| 2026-07-07 | **Discovery de cobranza completado.** Aging por factura, conciliación por folio, diferencias por fee y pagos registrados manualmente por el despacho. |
|
||||||
|
| 2026-07-10 | **Prototipo Etapa 0 entregado** por correo; Balam lo revisa internamente. |
|
||||||
|
| 2026-07-13 | Johann inicia trabajo local de Etapa 1 sin esperar la validación visual. La factura de 30 h sigue pendiente de visto bueno. |
|
||||||
|
| 2026-07-14 | Sesiones confirmadas: reglas/dudas Etapa 1 el 22-jul y validación del prototipo el 23-jul. |
|
||||||
|
| 2026-07-15 | Noé autoriza el repositorio privado de GitHub; invitación pendiente para `Johann-28`. Se inicia control local de horas. |
|
||||||
|
|
||||||
## 6. Próximos pasos
|
## 6. Próximos pasos
|
||||||
|
|
||||||
Ver detalle y responsables en [bitacora/PENDIENTES.md](bitacora/PENDIENTES.md). En corto:
|
Ver detalle y responsables en [bitacora/PENDIENTES.md](bitacora/PENDIENTES.md). En corto:
|
||||||
1. **Johann:** trabajar el **prototipo (Etapa 0)** con el flujo real del 6-jul (Jira → cotización BIND → prefactura → CFDI → envío) y sus validaciones (PPD default, IVA 16%/0%).
|
1. **Johann:** continuar backend local y publicar en cuanto llegue la invitación al repo.
|
||||||
2. **Balam:** entregar el **token de BIND** (bloqueador técnico, sin respuesta desde el kickoff), repo GitHub y Azure; **Ara+Arturo:** enviar el **Excel de particularidades de envío por cliente** (comprometido para el 6-jul).
|
2. **Johann:** validar el corte reconstruido de 30 h en [`Seguimiento-horas.csv`](planeacion/Seguimiento-horas.csv), capturar diariamente en adelante y conciliar con Jira cuando Pedro habilite el flujo.
|
||||||
3. **Johann:** esperar la confirmación de Balam a la propuesta v1.2 (enviada 2-jul) y entonces **emitir la factura de 30 h**; mientras, no facturar.
|
3. **Johann:** preparar la sesión del 22-jul (alta de cliente, fee, envíos y alcance Jira→BIND) y la validación del prototipo del 23-jul.
|
||||||
4. **Johann:** aclarar con Balam la expectativa de **recordatorios a clientes** (Anexo B, no MVP) antes de que se consolide como supuesto.
|
4. **Balam:** completar repo, Azure, Excel de particularidades de envío y confirmación para emitir la factura de 30 h.
|
||||||
5. **Johann:** configurar **Jira** (con Pedro), cambiar régimen fiscal; confirmar con Erika si la sesión del **martes 7-jul** sigue en pie.
|
5. **Johann/Balam:** cerrar explícitamente qué pertenece al MVP: Jira automático, recordatorios externos y escritura en BIND.
|
||||||
|
|
||||||
## 7. Riesgos / puntos abiertos
|
## 7. Riesgos / puntos abiertos
|
||||||
|
|
||||||
@@ -86,3 +92,5 @@ Ver detalle y responsables en [bitacora/PENDIENTES.md](bitacora/PENDIENTES.md).
|
|||||||
- **Conciliación**: depende de procesar PDFs bancarios (sin API directa) + involucrar a Arturo.
|
- **Conciliación**: depende de procesar PDFs bancarios (sin API directa) + involucrar a Arturo.
|
||||||
- **Solapamiento dashboard** con el Power BI existente de Pedro.
|
- **Solapamiento dashboard** con el Power BI existente de Pedro.
|
||||||
- **Límite de API BIND** (1 llave por usuario, 20K req/día) → documentar llaves y monitorear consumo.
|
- **Límite de API BIND** (1 llave por usuario, 20K req/día) → documentar llaves y monitorear consumo.
|
||||||
|
- **Integración Jira→BIND no incluida explícitamente:** requiere decisión de alcance, cuenta técnica de Jira, mapeo de campos y escritura autorizada en BIND.
|
||||||
|
- **Horas históricas reconstruidas:** corte de 30 h distribuido por actividad; solo 3.08 h tienen duración documental directa. Johann debe validar las otras 26.92 h antes de facturar.
|
||||||
|
|||||||
@@ -1,16 +1,23 @@
|
|||||||
# --- Configuración del sandbox de BIND ---
|
# --- Configuración del sandbox de BIND ---
|
||||||
#
|
#
|
||||||
# Por default el cliente apunta al mock local (puerto 4010).
|
# Por default el cliente apunta al mock local (puerto 4010).
|
||||||
# Cuando Pedro entregue el API key real, copia este archivo a .env y
|
# Para apuntar al API REAL: BIND_BASE_URL=https://api.bind.com.mx y pon el
|
||||||
# cambia BIND_BASE_URL a https://api.bind.com.mx + llena las credenciales.
|
# token en BIND_API_TOKEN (así lo nombra el correo de entrega de Pedro, 6-jul).
|
||||||
#
|
#
|
||||||
# Recuerda: BIND solo tiene PRODUCCIÓN. Cualquier llamada con base URL real
|
# Recuerda: BIND solo tiene PRODUCCIÓN. Cualquier llamada con base URL real
|
||||||
# afecta datos reales de Balam. Mantén MODE=read-only mientras no haya
|
# afecta datos reales de Balam. Mantén MODE=read-only mientras no haya
|
||||||
# autorización explícita para escribir.
|
# autorización explícita para escribir. El token NUNCA se versiona ni se
|
||||||
|
# imprime (este archivo .env está en .gitignore).
|
||||||
|
#
|
||||||
|
# Validado 6-jul-2026 (ver VALIDACION-API.md): la auth real es SOLO
|
||||||
|
# Authorization: Bearer <token> — la subscription key no se requiere.
|
||||||
|
|
||||||
BIND_BASE_URL=http://localhost:4010
|
BIND_BASE_URL=http://localhost:4010
|
||||||
|
# Token del API real (entregado por Pedro; usuario BIND de Arturo Rosas):
|
||||||
|
BIND_API_TOKEN=
|
||||||
|
# Credencial para el mock local (cualquier string no vacío funciona):
|
||||||
BIND_API_KEY=mock-bearer-token
|
BIND_API_KEY=mock-bearer-token
|
||||||
BIND_SUBSCRIPTION_KEY=mock-subscription-key
|
BIND_SUBSCRIPTION_KEY=
|
||||||
|
|
||||||
# read-only | dry-run | write
|
# read-only | dry-run | write
|
||||||
# - read-only: solo GET. Bloquea POST/PUT/PATCH/DELETE en el cliente.
|
# - read-only: solo GET. Bloquea POST/PUT/PATCH/DELETE en el cliente.
|
||||||
@@ -20,3 +27,6 @@ BIND_MODE=read-only
|
|||||||
|
|
||||||
# Puerto del mock server
|
# Puerto del mock server
|
||||||
MOCK_PORT=4010
|
MOCK_PORT=4010
|
||||||
|
|
||||||
|
# Presupuesto de peticiones del script de validación (src/validate-real-api.ts)
|
||||||
|
VALIDATION_BUDGET=120
|
||||||
|
|||||||
@@ -2,3 +2,4 @@ node_modules/
|
|||||||
dist/
|
dist/
|
||||||
.env
|
.env
|
||||||
*.log
|
*.log
|
||||||
|
validation-output/
|
||||||
|
|||||||
@@ -7,21 +7,31 @@ Está pensado para que tú (Johann), Pedro (Balam) o un futuro dev puedan:
|
|||||||
2. Probar el cliente tipado contra un mock que replica los headers, el formato OData y el rate-limit observable de BIND.
|
2. Probar el cliente tipado contra un mock que replica los headers, el formato OData y el rate-limit observable de BIND.
|
||||||
3. Cuando Pedro entregue las credenciales reales, **cambiar dos variables de entorno** y apuntar el mismo código a `https://api.bind.com.mx` sin reescribir nada.
|
3. Cuando Pedro entregue las credenciales reales, **cambiar dos variables de entorno** y apuntar el mismo código a `https://api.bind.com.mx` sin reescribir nada.
|
||||||
|
|
||||||
|
> ✅ **ACTUALIZACIÓN 6-jul-2026 — validación contra el API real EJECUTADA.**
|
||||||
|
> Con el token entregado por Pedro (usuario de Arturo Rosas) se corrió la validación
|
||||||
|
> técnica de solo lectura (117 peticiones GET). Resultados completos, tabla de
|
||||||
|
> cobertura de endpoints, inventario de campos y veredicto del saldo en
|
||||||
|
> **[VALIDACION-API.md](VALIDACION-API.md)**. Los hallazgos clave ya están
|
||||||
|
> reconciliados en el cliente (`types.real.ts`, `BindClient` con `idStyle`).
|
||||||
|
> El mock conserva el schema aproximado previo — sigue siendo útil para CI/demo,
|
||||||
|
> pero el contrato real es el de `types.real.ts`.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## TL;DR del API de BIND (lo que descubrí del discovery)
|
## TL;DR del API de BIND (reconciliado con la validación del 6-jul)
|
||||||
|
|
||||||
| Tema | Hallazgo |
|
| Tema | Hallazgo |
|
||||||
|---|---|
|
|---|---|
|
||||||
| **Base URL** | `https://api.bind.com.mx` |
|
| **Base URL** | `https://api.bind.com.mx` ✅ confirmado |
|
||||||
| **Estilo** | REST con sintaxis **OData v3** (filtros `$filter`, `$top`, `$skip`, `$orderby`, `$count`, IDs como `guid'...'`) |
|
| **Estilo** | REST con filtros OData v3 (`$filter`, `$top`≤100, `$skip`, `$orderby`). ⚠️ `$select` NO funciona (500); conteo total inaccesible; **GET por ID es REST `/{id}`, no `(guid'...')`** |
|
||||||
| **Auth** | Dos headers: `Authorization: Bearer <API_KEY>` + `Ocp-Apim-Subscription-Key: <SUBSCRIPTION_KEY>` (este último cuando aplica) |
|
| **Auth** | ✅ **Solo** `Authorization: Bearer <token>` — la `Ocp-Apim-Subscription-Key` no se requiere. ⚠️ Token inválido → **500** (no 401) |
|
||||||
| **Origen del API key** | Cuenta BIND → Perfil de usuario → pestaña *Integraciones* |
|
| **Origen del API key** | Cuenta BIND → Perfil de usuario → pestaña *Integraciones* (el de Balam salió del usuario de **Arturo Rosas**) |
|
||||||
| **Rate limit** | **20,000 peticiones / día** (confirmado por Noe, 25-may-2026) |
|
| **Rate limit** | **20,000 peticiones / día** — no observable en headers; llevar contador local |
|
||||||
| **Sandbox oficial** | **No existe.** BIND recomienda Postman contra producción → razón #1 de este sandbox |
|
| **Sandbox oficial** | **No existe.** Este mock local sigue siendo la única red de pruebas sin efecto fiscal |
|
||||||
| **Portal dev** | [developers.bind.com.mx](https://developers.bind.com.mx) (login requerido para ver schemas detallados) |
|
| **PAC para CFDI** | Integrado en BIND; el PDF del CFDI se descarga vía `GET /api/Invoices/{id}/pdf` ✅ |
|
||||||
| **PAC para CFDI** | Integrado dentro del propio BIND — la plataforma de Balam **no toca el SAT**, solo orquesta |
|
| **Recursos confirmados (200)** | `Invoices`, **`Clients`** (no Customers), **`Quotes`**, `Products`, `Currencies`, `Warehouses`, `Locations`, `Activities`, `PriceLists`, `Orders`, `Providers`, `Banks`, `BankAccounts`, `Users` |
|
||||||
| **Recursos confirmados** | `Activities`, `Customers`, `Products` (y según el discovery doc: `Invoices`, `Payments`, `Quotes` muy probables) |
|
| **Recursos que NO existen** | **`Payments`** (≈20 nombres probados → 404 — el acumulado pagado viene DENTRO de cada factura), `Customers`, `Series`, `CreditNotes`, `Companies`… |
|
||||||
|
| **Saldo por factura** | ✅ **Plan A operativo:** `Total − Payments − CreditNotes` en la misma fila de `/api/Invoices` (verificado aritméticamente) |
|
||||||
|
|
||||||
### Por qué un sandbox propio y no Postman
|
### Por qué un sandbox propio y no Postman
|
||||||
|
|
||||||
@@ -75,18 +85,26 @@ Stats del cliente
|
|||||||
{ requestsToday: 6, quota: 20000, remaining: 19994, mode: 'read-only' }
|
{ requestsToday: 6, quota: 20000, remaining: 19994, mode: 'read-only' }
|
||||||
```
|
```
|
||||||
|
|
||||||
### Apuntar a producción (cuando llegue el API key)
|
### Apuntar a producción (token real)
|
||||||
|
|
||||||
Solo cambiar variables de entorno — el código no se modifica:
|
Solo cambiar variables de entorno — el código no se modifica (el cliente detecta
|
||||||
|
el estilo de ID; contra el API real usa `/{id}`):
|
||||||
|
|
||||||
```powershell
|
```powershell
|
||||||
$env:BIND_BASE_URL = "https://api.bind.com.mx"
|
$env:BIND_BASE_URL = "https://api.bind.com.mx"
|
||||||
$env:BIND_API_KEY = "<key del perfil de usuario de Balam>"
|
$env:BIND_API_TOKEN = "<token del perfil de Arturo — NUNCA versionarlo>"
|
||||||
$env:BIND_SUBSCRIPTION_KEY = "<si aplica>"
|
|
||||||
$env:BIND_MODE = "read-only" # mantenlo así hasta tener autorización para escribir
|
$env:BIND_MODE = "read-only" # mantenlo así hasta tener autorización para escribir
|
||||||
npm run demo
|
npm run demo
|
||||||
```
|
```
|
||||||
|
|
||||||
|
> ⚠️ La demo fue escrita contra el schema del mock (`types.ts` aproximados);
|
||||||
|
> contra producción algunos escenarios no aplican (p. ej. `Customers` → 404 real).
|
||||||
|
> Para explorar el API real usa el script de validación:
|
||||||
|
>
|
||||||
|
> ```powershell
|
||||||
|
> npm run validate:real # solo GET, presupuesto de peticiones, reporte sanitizado
|
||||||
|
> ```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Mapeo a la arquitectura de Balam
|
## Mapeo a la arquitectura de Balam
|
||||||
@@ -151,9 +169,14 @@ Estos son los puntos del `03_Anexo_Tecnico_Integraciones_Discovery_Balam.docx` y
|
|||||||
|
|
||||||
## Limitaciones honestas de este sandbox
|
## Limitaciones honestas de este sandbox
|
||||||
|
|
||||||
- **El schema de `Invoice`, `Customer`, etc. es una aproximación** — está modelado a partir de la doc pública y del flujo que necesita Balam, no del SDK oficial. Cuando salga el primer `GET` real contra producción, hay que reconciliar nombres de campos (especialmente capitalización y campos opcionales).
|
- **El schema del MOCK (`types.ts`) sigue siendo la aproximación previa** — el contrato
|
||||||
- **El mock acepta cualquier Bearer**, solo valida que exista. No es un servidor de auth real, es un placeholder.
|
confirmado contra producción vive en **`src/client/types.real.ts`** y en
|
||||||
- **El parser de OData del mock solo cubre lo que el cliente genera** (`eq, ne, gt, lt, ge, le, and, or`, paréntesis). No soporta `contains`, `startswith`, funciones, lambdas. Suficiente para el MVP.
|
[VALIDACION-API.md](VALIDACION-API.md). Pendiente (opcional): regenerar el seed del
|
||||||
- **No reproduce la lógica de `$expand`** — si BIND lo soporta para traer `Lines` o `Customer` embebidos, hay que extender.
|
mock con los shapes reales para que la demo ejercite el contrato confirmado.
|
||||||
|
- **El mock acepta cualquier Bearer**, solo valida que exista. No es un servidor de auth real.
|
||||||
|
- **El mock implementa el GET por ID estilo OData `(guid'...')`** — el API real usa `/{id}`;
|
||||||
|
el cliente lo resuelve con `idStyle`, el mock quedó intacto.
|
||||||
|
- **El parser de OData del mock solo cubre lo que el cliente genera** (`eq, ne, gt, lt, ge, le, and, or`, paréntesis).
|
||||||
|
- **No reproduce la lógica de `$expand`** — y el API real ni siquiera soporta `$select`, así que el payload completo es la norma.
|
||||||
|
|
||||||
Cuando alguno de estos límites se vuelva una piedra en el zapato, se extiende. Hoy es deliberadamente mínimo.
|
Cuando alguno de estos límites se vuelva una piedra en el zapato, se extiende. Hoy es deliberadamente mínimo.
|
||||||
|
|||||||
@@ -0,0 +1,357 @@
|
|||||||
|
# Validación técnica de la API de BIND ERP — cuenta real (Balam)
|
||||||
|
|
||||||
|
> **Actividad:** "Validación técnica de la API de BIND" · Etapa 0 (Discovery)
|
||||||
|
> **Fecha de ejecución:** 6-jul-2026 · **Base URL:** `https://api.bind.com.mx`
|
||||||
|
> **Token:** entregado por Pedro el 6-jul (correo, [REGISTRO #29]) — generado con el usuario de **Arturo Rosas**, conforme al acuerdo del kickoff ([REGISTRO #22]). Vive en `bind-api-sandbox/.env` (`BIND_API_TOKEN`), fuera de git.
|
||||||
|
> **Método:** script [`src/validate-real-api.ts`](src/validate-real-api.ts) — **exclusivamente GET** (no existe código de escritura en el script), **117 de 120 peticiones** presupuestadas (límite real: 20K/día). Reporte crudo sanitizado en `validation-output/report.json` (gitignoreado).
|
||||||
|
> **Política de datos:** este documento contiene **solo estructura** — nombres de campos, tipos, formatos, conteos y códigos de estatus. Ningún valor real de Balam (nombres, RFCs, montos, folios, correos). Los ejemplos son inventados con el mismo shape.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Resumen ejecutivo
|
||||||
|
|
||||||
|
| Pregunta | Veredicto |
|
||||||
|
|---|---|
|
||||||
|
| ¿El token autentica? | ✅ Sí — `Authorization: Bearer` como único header. Sin subscription key. |
|
||||||
|
| ¿Alcance del token? | ✅ Cubre todos los recursos existentes que probamos (ningún 401/403 por recurso) — coherente con "cuenta mayor, todos los permisos" del kickoff. |
|
||||||
|
| ¿Se distingue prefactura de factura timbrada? | ✅ Sí — `UUID eq null` / `IsFiscalInvoice eq false` devuelven filas. |
|
||||||
|
| ¿PPD/PUE visible? | ✅ Sí, en el **detalle** por factura (`CFDIPaymentTerm`) — no en la lista. |
|
||||||
|
| **¿Saldo abierto por factura?** | ✅ **Plan A operativo:** `Total − Payments − CreditNotes`, todos campos de la **misma fila** de `/api/Invoices`. Verificado aritméticamente. |
|
||||||
|
| ¿Pagos individuales listables? | ❌ **No** — no existe recurso de pagos consultable (19 nombres probados → 404). El acumulado sí (`Invoices.Payments`). |
|
||||||
|
| ¿Cotizaciones consultables? | ✅ Sí (`/api/Quotes` + detalle) — pero **sin relación visible** cotización→factura. |
|
||||||
|
| ¿OData? | ⚠️ Parcial — `$filter/$top/$skip/$orderby` sí (sintaxis v3); `$select` y conteo total **no**. |
|
||||||
|
| ¿Multi-empresa? | ✅ Acotado a **una** empresa (la del usuario del token). Sin recurso `Companies` ni campo de empresa. |
|
||||||
|
| ¿PDF del CFDI por API? | ✅ `GET /api/Invoices/{id}/pdf` → `application/pdf`. |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1 · Autenticación
|
||||||
|
|
||||||
|
Esquema confirmado: **un solo header**.
|
||||||
|
|
||||||
|
```
|
||||||
|
GET https://api.bind.com.mx/api/{Recurso}
|
||||||
|
Authorization: Bearer <token>
|
||||||
|
Accept: application/json
|
||||||
|
```
|
||||||
|
|
||||||
|
- El `Ocp-Apim-Subscription-Key` que el sandbox contemplaba como posible **no es necesario** — no se envió en ninguna petición y todo funcionó.
|
||||||
|
|
||||||
|
| Escenario | Estatus | Cuerpo (estructura) |
|
||||||
|
|---|---|---|
|
||||||
|
| Token válido | `200` | Colección OData `{ value: [...] }` |
|
||||||
|
| Sin header Authorization | `401` | `{ "Message": "Authorization has been denied for this request." }` |
|
||||||
|
| Token corrupto/inválido | ⚠️ **`500`** | `{ "message": "API Key es inválida. \| Your API Key is invalid.", "code": "0" }` |
|
||||||
|
|
||||||
|
> **Hallazgo importante:** un token inválido responde **500, no 401/403**. El monitoreo de la plataforma **no puede fiarse del código HTTP** para distinguir "token revocado" de "error del servidor de BIND" — hay que inspeccionar el mensaje del body (`API Key es inválida`).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2 · Cobertura de endpoints (inventario)
|
||||||
|
|
||||||
|
Todos con `GET {recurso}?$top=1`. **No apareció ningún 401/403 por recurso**: con este token todo existe (200) o no existe (404) — no hay recursos "prohibidos" visibles.
|
||||||
|
|
||||||
|
### Responden 200
|
||||||
|
|
||||||
|
| Recurso | Método probado | Estatus | Notas |
|
||||||
|
|---|---|---|---|
|
||||||
|
| `Invoices` | GET lista / GET `/{id}` / GET `/{id}/pdf` / GET `/{id}/xml` | 200 | Colección OData. Detalle trae más campos que la lista (50 vs 32). |
|
||||||
|
| `Clients` | GET lista / GET `/{id}` | 200 | **Así se llaman los clientes** (no `Customers`). Detalle 28 campos vs 10 de lista. |
|
||||||
|
| `Quotes` | GET lista / GET `/{id}` | 200 | Cotizaciones. Detalle 45 campos con partidas `Items[]`. |
|
||||||
|
| `Products` | GET lista | 200 | 30 campos. Incluye `ChargeVAT`, `CurrencyCode`, unidad. |
|
||||||
|
| `Currencies` | GET lista | 200 | Catálogo: `ID`, `Name`, `Code` (3 letras), `ExchangeRate`. |
|
||||||
|
| `Warehouses` | GET lista | 200 | `ID`, `Name`, `LocationID`, `AvailableInOtherLoc`. 1 fila (Matriz). |
|
||||||
|
| `Locations` | GET lista | 200 | Sucursales/domicilios: `Name`, `Street`, `ZipCode`, `City`, `State`… 1 fila. |
|
||||||
|
| `Activities` | GET lista | 200 | Devuelve colección **vacía** en esta cuenta (0 filas). |
|
||||||
|
| `PriceLists` | GET lista | 200 | Listas de precios. |
|
||||||
|
| `Orders` | GET lista | 200 | Pedidos (no se profundizó — fuera del flujo MVP). |
|
||||||
|
| `Providers` | GET lista | 200 | Proveedores (fuera del flujo MVP). |
|
||||||
|
| `Banks` | GET lista | 200 | Catálogo bancario (relevante futuro: conciliación). |
|
||||||
|
| `BankAccounts` | GET lista | 200 | Cuentas bancarias de la empresa (ídem). |
|
||||||
|
| `Users` | GET lista | 200 | Usuarios BIND. No se analizó su shape (contiene datos personales, no prioritario). |
|
||||||
|
|
||||||
|
### Responden 404 (no existen con ese nombre)
|
||||||
|
|
||||||
|
| Grupo | Nombres probados → 404 |
|
||||||
|
|---|---|
|
||||||
|
| Clientes (alias) | `Customers` |
|
||||||
|
| **Pagos** | `Payments`, `Payment`, `ClientPayments`, `CustomerPayments`, `Incomes`, `Income`, `Deposits`, `Collections`, `PaymentComplements`, `Complements`, `CashReceipts`, `AccountsReceivable`, `Receivables`, `Cobros`, `Pagos`, `InvoicePayments`, `PaymentsReceived` |
|
||||||
|
| Pagos (sub-recurso) | `Invoices/{id}/Payments`, `Invoices/{id}/payments`, `Invoices/{id}/CreditNotes` |
|
||||||
|
| Cotizaciones (alias) | `Quotations`, `Cotizaciones` |
|
||||||
|
| Series | `Series`, `InvoiceSeries`, `Folios`, `DocumentSeries` (la serie es **campo** de la factura, no recurso) |
|
||||||
|
| Sucursales (alias) | `Branches`, `Sucursales` (lo real es `Locations`) |
|
||||||
|
| Otros | `CreditNotes`, `Taxes`, `Prices`, `SalesOrders`, `PurchaseOrders`, `Suppliers`, `Sellers`, `Employees`, `Companies`, `Expenses`, `Inventory`, `CFDI`, `CFDIs` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3 · Inventario de campos por recurso
|
||||||
|
|
||||||
|
Solo nombres, tipos y formatos observados (muestras de `$top=5`). `(≈corto/medio/largo)` = longitud aproximada del string; los valores reales nunca se persistieron.
|
||||||
|
|
||||||
|
### 3.1 `Invoices` — lista (32 campos)
|
||||||
|
|
||||||
|
| Campo | Tipo/formato | Nota |
|
||||||
|
|---|---|---|
|
||||||
|
| `ID` | guid | Clave para `GET /api/Invoices/{id}`. |
|
||||||
|
| `Serie` | string corto (a veces vacío) | ⚠️ En el detalle se llama **`Series`** (inconsistencia del API). |
|
||||||
|
| `Number` | integer | Folio interno. |
|
||||||
|
| `UUID` | guid | Folio fiscal del CFDI. **`null` en prefacturas.** |
|
||||||
|
| `Date` | datetime ISO | Fecha del documento. |
|
||||||
|
| `ExpirationDate` | datetime ISO | **Fecha de vencimiento** (esto alimenta el aging). No existe campo `DueDate`. |
|
||||||
|
| `ClientID` / `ClientName` | guid / string | Denormalizado en la propia fila. |
|
||||||
|
| `RFC` | string formato RFC | Del receptor. |
|
||||||
|
| `Cost`, `Subtotal`, `Discount`, `Total` | decimal | |
|
||||||
|
| `VAT`, `IEPS`, `ISRRet`, `VATRet` | decimal | Impuestos y retenciones. |
|
||||||
|
| `VATRate`, `VATRetRate` | decimal | Tasas (p. ej. `0.16`). |
|
||||||
|
| **`Payments`** | decimal | **Acumulado pagado de la factura** (ver §4). |
|
||||||
|
| **`CreditNotes`** | decimal | Acumulado de notas de crédito aplicadas. |
|
||||||
|
| `CurrencyID` | guid | FK a `Currencies` (la lista no trae el código — el detalle sí). |
|
||||||
|
| `ExchangeRate` | decimal | TC fijado al emitir. |
|
||||||
|
| `LocationID`, `WarehouseID`, `PriceListID` | guid | |
|
||||||
|
| `CFDIUse` | integer | ⚠️ Código **interno** (se observaron `3`, `23`), no la clave SAT (`G03`…). Falta tabla de mapeo. |
|
||||||
|
| `Comments` | string | Aquí ponen hoy el nº de ticket Jira (Discovery #27). |
|
||||||
|
| `PurchaseOrder` | string | Orden de compra. |
|
||||||
|
| `IsFiscalInvoice` | boolean | **`false` = prefactura** (sin timbrar). |
|
||||||
|
| `ShowIEPS` | boolean | |
|
||||||
|
| `Status` | integer | Ver semántica abajo. |
|
||||||
|
|
||||||
|
**Semántica de `Status` (mapeada contra el propio API, lista→detalle):**
|
||||||
|
|
||||||
|
| Código | Etiqueta (campo `Status` del detalle) |
|
||||||
|
|---|---|
|
||||||
|
| `0` | Activa |
|
||||||
|
| `1` | Pagada |
|
||||||
|
| `2` | Cancelada |
|
||||||
|
|
||||||
|
Se probaron códigos 3–5: sin filas (o no existen o no hay ejemplares). ⚠️ `Status` **no distingue** prefactura de timbrada — el discriminador fiable es `UUID eq null` / `IsFiscalInvoice eq false` (ambos filtros devuelven filas: **las prefacturas sí son visibles por API**).
|
||||||
|
|
||||||
|
### 3.2 `Invoices/{id}` — detalle (50 campos; los adicionales)
|
||||||
|
|
||||||
|
| Campo | Tipo/formato | Nota |
|
||||||
|
|---|---|---|
|
||||||
|
| `Series` | string | La lista lo llama `Serie`. |
|
||||||
|
| `Status` / `StatusCode` | string / integer | Etiqueta + código (p. ej. "Pagada" / `1`). |
|
||||||
|
| **`PaymentTerms`** | integer | **Días de crédito** de la factura. Solo en detalle. |
|
||||||
|
| **`CFDIPaymentTerm`** | string | ⚠️ **El método de pago SAT (PPD/PUE)** — se observó el literal "PAGO EN UNA SOLA EXHIBICIÓN" (=PUE). Puede venir vacío. Solo en detalle. |
|
||||||
|
| **`CFDIPaymentMethod`** | string | ⚠️ **La forma de pago SAT** (se observaron "Transferencia Electrónica de Fondos", "Por Definir"). Nomenclatura **invertida** respecto al SAT — ver hallazgos. |
|
||||||
|
| `CFDIAccountNumber` | string | Nº de cuenta (últimos dígitos), puede venir vacío. |
|
||||||
|
| `CurrencyName` | string 3 letras | Código de moneda (`MXN`/`USD`) — en el detalle es el código, no el nombre. |
|
||||||
|
| `ClientPhoneNumber`, `ClientContact` | string \| null | |
|
||||||
|
| `CreatedByID` / `CreatedByName` | guid / string | Quién creó el documento (auditoría). |
|
||||||
|
| `CreationDate` / `ApplicationDate` | datetime ISO | |
|
||||||
|
| `PriceListName`, `LocationName`, `WarehouseName` | string | Denormalizados. |
|
||||||
|
| `FiscalID` | guid | |
|
||||||
|
| `Address` | string largo | Dirección fiscal del receptor. |
|
||||||
|
| `Products` | array | Partidas de productos (vacío en la muestra — Balam factura servicios). |
|
||||||
|
| `Services` | array | **Partidas de servicios.** |
|
||||||
|
| `Services[].ID`, `Services[].ServiceID` | guid | |
|
||||||
|
| `Services[].IndexNumber` | integer | Orden de la partida. |
|
||||||
|
| `Services[].Name`, `Services[].Code` | string | Concepto (p. ej. el 029 "consultoría y servicios" del Discovery). |
|
||||||
|
| `Services[].Qty`, `Services[].Price` | decimal | |
|
||||||
|
| `Services[].VATRate` | decimal | **Tasa de IVA por partida** — habilita la validación 16 % / 0 %. |
|
||||||
|
| `Services[].Discount` | decimal | |
|
||||||
|
|
||||||
|
> El detalle **no** trae `CFDIUse` (solo la lista) ni un campo de saldo precalculado.
|
||||||
|
|
||||||
|
### 3.3 `Quotes` — lista (11 campos) y detalle (45)
|
||||||
|
|
||||||
|
**Lista:** `ID` (guid), `Number` (string), `CreationDate` (datetime), `ClientName`, `Locations` (string), `Comments`, `TotalOriginalCurrency` (decimal), `Currency` (nombre, p. ej. "Peso mexicano"), `Total` (decimal), `Status` (integer), `StatusText` (string).
|
||||||
|
|
||||||
|
**Semántica de `Quotes.Status`** (mapeada vía `$filter` + `StatusText` de la misma fila):
|
||||||
|
|
||||||
|
| Código | `StatusText` |
|
||||||
|
|---|---|
|
||||||
|
| `0` | Activa |
|
||||||
|
| `1` | Cancelada |
|
||||||
|
| `2` | Surtida |
|
||||||
|
|
||||||
|
**Detalle `Quotes/{id}` agrega:** `QuoteNumber`, `ClientID`/`ClientContact`/`ClientPhone`, `LocationName/ID`, `PriceListName/ID`, `EmployeeName/ID` (comercial que cotizó), `CurrencyCode` (3 letras), `ExchangeRate`, `Subtotal`, `Discount`, `IEPS`, `VAT`/`VATRate`, `ISR`/`ISRRate`, `VatRet`, `Total`, `BaseCurrency` (bool), `OriginalCurrencySubtotal`, `OriginalCurrencyDiscountAmount`, `IsPercentage` (bool), **`ContactEmails`**, `ExternalIDType` (int), `Comments`, y partidas **`Items[]`**: `ID`, `Code`, `ProductID`, `ProductName`, `Unit`, `Qty`, `Price`, `Amount`, `IEPS`, `VAT`, `IndexNumber`.
|
||||||
|
|
||||||
|
> ⚠️ **No hay campo que ligue la cotización con la factura generada** (ni `InvoiceID` en Quote, ni `QuoteID` en Invoice). "Surtida" dice que se convirtió, pero no *a qué* factura. Ver implicaciones (§9.2).
|
||||||
|
|
||||||
|
### 3.4 `Clients` — lista (10 campos) y detalle (28)
|
||||||
|
|
||||||
|
**Lista:** `ID` (guid), `Number` (int), `ClientName`, `LegalName`, `RFC`, `Email`, `Phone`, `NextContactDate`, `LocationID` (guid), `RegimenFiscal` (string).
|
||||||
|
|
||||||
|
**Detalle `Clients/{id}` agrega:**
|
||||||
|
|
||||||
|
| Campo | Tipo | Nota |
|
||||||
|
|---|---|---|
|
||||||
|
| `CommercialName` | string | |
|
||||||
|
| **`CreditDays`** | integer | **Días de crédito default del cliente** (los 30/45/90 del Discovery). |
|
||||||
|
| `CreditAmount` | decimal | Límite de crédito. |
|
||||||
|
| `PaymentMethod` | string | Forma de pago default (se observó "Efectivo"). |
|
||||||
|
| `PaymentTermType` | string | Puede venir vacío. |
|
||||||
|
| `Status` | string | "Activo"/… |
|
||||||
|
| `SalesContact` / `CreditContact` | string | Contactos comercial y de cobranza. |
|
||||||
|
| **`Loctaion` / `LoctaionID`** | string / guid | ⚠️ **Typo real del API** ("Loctaion", sic) — el cliente tipado debe usar el nombre con typo. |
|
||||||
|
| `PriceList` / `PriceListID` | string / guid | |
|
||||||
|
| `Email`, `Telephones` | string \| null | Correos configurados (los que usa el botón "enviar email" de BIND). |
|
||||||
|
| `AccountNumber`, `DefaultDiscount`, `ClientSource`, `Account` | varios | |
|
||||||
|
| `City`, `State`, `Addresses[]` | string / array | |
|
||||||
|
| `RegimenFiscal` | string | Régimen fiscal SAT. |
|
||||||
|
| `CreationDate` | datetime | |
|
||||||
|
|
||||||
|
> **No se observó** un campo "uso CFDI default por cliente" — el `CFDIUse` vive en la factura. La regla "gastos en general vs sin efectos fiscales" tendrá que derivarse de otra señal (p. ej. RFC extranjero/`XEXX010101000`, país, o configuración en la plataforma).
|
||||||
|
|
||||||
|
### 3.5 Catálogos
|
||||||
|
|
||||||
|
- **`Currencies`:** `ID` (guid), `Name`, `Code` (3 letras), `ExchangeRate` (decimal). 4 filas en la cuenta.
|
||||||
|
- **`Warehouses`:** `ID`, `Name` ("Matriz"), `LocationID`, `AvailableInOtherLoc` (bool). 1 fila — confirma el "hoy solo matriz" del Discovery.
|
||||||
|
- **`Locations`:** `ID`, `Name`, `Street`, `ExtNumber`, `IntNumber`, `ZipCode`, `Colonia`, `City`, `State`. 1 fila.
|
||||||
|
- **`Products`:** 30 campos, incl. `Code`, `Title`, `Cost`, `CostType(+Text)`, `CurrentInventory`, **`ChargeVAT`** (bool), `Unit`, `CurrencyID/Code`, `PricingType(+Text)`, `PurchaseType(+Text)`, `IEPSRate`, `Type(+Text)`, `SKU`, categorías.
|
||||||
|
|
||||||
|
### 3.6 Documentos del CFDI
|
||||||
|
|
||||||
|
| Endpoint | Estatus | Content-Type | Nota |
|
||||||
|
|---|---|---|---|
|
||||||
|
| `GET /api/Invoices/{id}/pdf` | 200 | `application/pdf` | **PDF real descargable por API** — insumo directo del módulo de envío. |
|
||||||
|
| `GET /api/Invoices/{id}/xml` | 200 | `application/json` | Responde 200 pero como JSON — probablemente envuelve el XML o una URL. El contenido se descartó por política de no persistir datos; **shape pendiente** (§10). |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4 · La pregunta del saldo — veredicto
|
||||||
|
|
||||||
|
**Plan A (operativo). No se necesita Plan B ni Plan C.**
|
||||||
|
|
||||||
|
- No existe un campo literal `Balance`/`Saldo`, **pero** cada fila de `/api/Invoices` trae `Total`, **`Payments`** (acumulado pagado) y **`CreditNotes`** (acumulado de notas de crédito):
|
||||||
|
|
||||||
|
$$\text{SaldoPorFactura} = \text{Total} - \text{Payments} - \text{CreditNotes}$$
|
||||||
|
|
||||||
|
- **Verificación aritmética (en memoria, sin persistir montos):** en 5/5 facturas con `Status=1` (Pagada), `Payments + CreditNotes ≈ Total` (diferencia < 0.01); en 5/5 con `Status=0` (Activa), el residual es positivo. La fórmula cuadra en ambas poblaciones.
|
||||||
|
- Es "Plan A" en el sentido operativo del riesgo de la propuesta: **una sola llamada a `/api/Invoices` basta** para calcular saldo y aging de toda la cartera — no hay que correlacionar una colección de pagos (Plan B) ni capturar nada a mano (Plan C).
|
||||||
|
- Matiz honesto: BIND no expone el número ya restado; la resta la hace la plataforma. El costo es cero (mismos campos, misma fila).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5 · Payments — el hallazgo duro
|
||||||
|
|
||||||
|
**No existe recurso consultable de pagos individuales.** Se probaron 17 nombres de colección y 3 sub-recursos (§2) — todos 404.
|
||||||
|
|
||||||
|
Lo que **sí** hay:
|
||||||
|
|
||||||
|
| Necesidad del MVP | ¿Cubierta? | Cómo |
|
||||||
|
|---|---|---|
|
||||||
|
| Saldo por factura | ✅ | `Total − Payments − CreditNotes` (§4). |
|
||||||
|
| ¿Factura pagada? | ✅ | `Status = 1` y/o residual ≈ 0. |
|
||||||
|
| Aging / vencimiento | ✅ | `ExpirationDate` + saldo. |
|
||||||
|
| **Fecha y monto de cada abono individual** | ❌ | No visible por API con este token. |
|
||||||
|
| Complementos de pago (REP) de facturas PPD | ❌ | Ningún recurso visible (`PaymentComplements`, `Complements` → 404). |
|
||||||
|
|
||||||
|
**Implicación:** el motor de cobranza puede detectar *que* una factura se pagó (transición de `Status`/residual entre sincronizaciones) y registrar el *timestamp de detección* en la plataforma, pero no la fecha valor del pago según BIND. Preguntar a Pedro/soporte BIND si existe un endpoint de pagos/REP no descubierto (la doc completa está tras login en developers.bind.com.mx) — ver §10.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6 · OData y paginación
|
||||||
|
|
||||||
|
| Mecanismo | ¿Funciona? | Evidencia |
|
||||||
|
|---|---|---|
|
||||||
|
| `$top` | ✅ con tope | `$top=100` → 200; **`$top=101` → 500**. |
|
||||||
|
| `$skip` | ✅ | `$top=1&$skip=1` devuelve la fila siguiente (verificado por ID). |
|
||||||
|
| `$orderby` | ✅ | `Date asc` → orden ascendente verificado. |
|
||||||
|
| `$filter eq` (int) | ✅ | `Status eq 1`, `CFDIUse eq 3` → 200. |
|
||||||
|
| `$filter eq null` | ✅ | `UUID eq null` → 200 con filas. |
|
||||||
|
| `$filter ge` + fecha | ✅ **sintaxis v3** | `Date ge datetime'2020-01-01T00:00:00'` → 200. |
|
||||||
|
| `$select` | ❌ | → **500**. No se pueden proyectar columnas; el payload siempre viene completo. |
|
||||||
|
| `$inlinecount=allpages` (v3) | ❌ | → 500. |
|
||||||
|
| `$count=true` (v4) | ⚠️ | → 200 pero **ignorado**: no devuelve conteo. |
|
||||||
|
| `odata.nextLink` | ❌ | Nunca apareció. |
|
||||||
|
|
||||||
|
**Paginación:** no hay `nextLink` ni conteo total ⇒ **paginación manual** con `$top=100&$skip=N` hasta recibir página corta. ⚠️ `GET` sin `$top` devuelve la colección completa en una respuesta (se observó con una colección de 41 filas) — con colecciones grandes es un riesgo de payload; **siempre** paginar. Sondeo por `$skip` (sin descargar): la colección histórica de `Invoices` supera las 1,000 filas.
|
||||||
|
|
||||||
|
**Rate limit:** no se observó **ningún header** de cuota (`X-RateLimit-*`, `Retry-After` en 200s) — el límite de 20K/día no es observable por request; hay que llevarlo con contador local (como ya hace `BindClient`).
|
||||||
|
|
||||||
|
**Estabilidad:** ~3 respuestas `500` transitorias en 117 peticiones (resueltas al primer retry). El retry con backoff **no es opcional** en producción. Nota: BIND usa 500 también para errores de sintaxis OData y token inválido — distinguir por body/contexto antes de reintentar a ciegas.
|
||||||
|
|
||||||
|
**GET por ID:** estilo **REST** — `GET /api/Invoices/{id}` → 200; el estilo OData `Invoices(guid'...')` → **404**. (El cliente del sandbox asumía el estilo OData; ya se corrigió.)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7 · Multi-empresa
|
||||||
|
|
||||||
|
- `Companies` → 404; **ningún** recurso expone campo `Company`/`Empresa`.
|
||||||
|
- `Locations` y `Warehouses` devuelven **1 fila** (Matriz).
|
||||||
|
- Conclusión: **el token está acotado a la empresa del usuario que lo generó** (Arturo → Balam). La distinción multi-empresa del portal Jira (Balam/Regiotour/Elmstone, Discovery #27) **no viaja a BIND por este token**: para facturar otras empresas se necesitaría una cuenta BIND distinta con su propio token. Anotado para el roadmap — coherente con dejar multi-empresa fuera del MVP.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 8 · Hallazgos inesperados
|
||||||
|
|
||||||
|
1. **Token inválido → 500** (no 401/403), con mensaje `"API Key es inválida"` en el body. El 401 solo aparece cuando *falta* el header.
|
||||||
|
2. **`$select` no funciona** (500): no se puede reducir payload por columnas.
|
||||||
|
3. **Conteo total inaccesible**: `$inlinecount` truena (500) y `$count=true` se ignora — el total solo se conoce paginando hasta el final.
|
||||||
|
4. **No hay recurso de pagos** (17 nombres → 404) — el acumulado vive dentro de la factura (§5).
|
||||||
|
5. **Nomenclatura CFDI invertida respecto al SAT:** `CFDIPaymentTerm` = *Método de pago* SAT (PPD/PUE); `CFDIPaymentMethod` = *Forma de pago* SAT (transferencia, efectivo…). Cablearlo al revés rompería la validación de oro PPD/PUE.
|
||||||
|
6. **`CFDIUse` es un código interno** (enteros `3`, `23`), no la clave SAT (`G03`, `S01`…). Se necesita la tabla de mapeo (pedir a Pedro o doc tras login).
|
||||||
|
7. **Typo real en el API:** el detalle de `Clients` trae `Loctaion`/`LoctaionID` (sic).
|
||||||
|
8. **Inconsistencias lista vs detalle:** `Serie` (lista) vs `Series` (detalle); `Status` int (lista) vs `Status` string + `StatusCode` int (detalle); `CurrencyID` (lista) vs `CurrencyName` con el código (detalle); PPD/PUE y días de crédito **solo** en el detalle.
|
||||||
|
9. **Prefacturas visibles** en la misma colección `Invoices` (`UUID` null / `IsFiscalInvoice` false) — no hay recurso separado.
|
||||||
|
10. **`Activities` existe pero está vacío** en esta cuenta (0 filas) — el recurso que la doc pública usa de ejemplo no tiene datos aquí.
|
||||||
|
11. **500 transitorios** ocasionales que se resuelven con retry inmediato.
|
||||||
|
12. **Higiene de secretos:** el `.txt` del token estaba en la raíz del repo sin gitignorear (no trackeado aún) — se agregó `bind_token_api.txt` y `*.env` al `.gitignore` raíz. Recomendación vigente: moverlo a un gestor de secretos y borrarlo del correo/disco.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 9 · Implicaciones para el MVP
|
||||||
|
|
||||||
|
Cruce contra el flujo objetivo del Discovery (#27): **Jira → cotización BIND → prefactura → validación humana → CFDI → envío**.
|
||||||
|
|
||||||
|
### 9.1 Lo que la API ya sostiene (solo lectura, hoy)
|
||||||
|
|
||||||
|
| Paso del flujo | Soporte confirmado |
|
||||||
|
|---|---|
|
||||||
|
| **Cotización** | `Quotes` legible con partidas, comercial (`EmployeeName`), moneda/TC y estatus (Activa/Cancelada/Surtida). La plataforma puede detectar cotizaciones nuevas y validar el prerequisito "cotización obligatoria" de Ara. |
|
||||||
|
| **Prefactura** | Listable vía `UUID eq null` / `IsFiscalInvoice eq false` → el dashboard "prefacturas pendientes de validación" es viable 100 % lectura. |
|
||||||
|
| **CFDI** | `UUID`, `Series`+`Number`, RFC, moneda, `ExchangeRate`, impuestos por partida (`Services[].VATRate`), uso CFDI (código), PPD/PUE (`CFDIPaymentTerm` en detalle), creador y fechas. |
|
||||||
|
| **Validaciones de oro** | • **PPD/PUE:** auditable por factura (detalle). La plataforma puede alertar "PUE detectado — ¿fue consciente?" apenas aparezca. • **IVA 16 %/0 %:** `VATRate` por partida + moneda + RFC → la regla "extranjero con IVA ≠ 0" (el error que Ara señaló en vivo) es detectable automáticamente. • **Días de crédito:** `Clients.CreditDays` (default) vs `PaymentTerms` (factura) — discrepancias detectables. |
|
||||||
|
| **Cobranza / aging** | `ExpirationDate` + saldo derivado (§4) + `Status` → aging y alertas internas sin recurso de pagos. |
|
||||||
|
| **Envío** | PDF real por API (`/{id}/pdf`) + correos del cliente (`Clients.Email`, `ContactEmails`) + las particularidades por cliente (Excel de Ara/Arturo) viven en la plataforma. |
|
||||||
|
|
||||||
|
### 9.2 Restricciones de diseño que impone lo encontrado
|
||||||
|
|
||||||
|
1. **Sync incremental obligatorio.** PPD/PUE y días de crédito viven en el **detalle** ⇒ 1 llamada por factura. Con ~55 facturas/mes es trivial, pero el histórico (>1,000) exige sincronizar por delta (`Date ge` la última corrida) y guardar en Postgres — nunca re-barrer todo el detalle.
|
||||||
|
2. **Trazabilidad cotización→factura la lleva la plataforma.** BIND no expone el vínculo; al orquestar la conversión (Etapa 2) la plataforma debe registrar el par `QuoteID→InvoiceID` en su propia BD (y/o convención en `Comments`, como hoy hacen con el ticket Jira).
|
||||||
|
3. **"Fecha de pago" = fecha de detección.** Sin pagos individuales, la plataforma registra cuándo *observó* el cambio a Pagada — suficiente para cobranza operativa; insuficiente para conciliación contable fina (que de todos modos es fase posterior).
|
||||||
|
4. **Catálogos internos a mapear:** `CFDIUse` (int→clave SAT) y códigos de `Status` no observados (3+). Confirmar con Pedro.
|
||||||
|
5. **Cliente HTTP:** paginar siempre (`$top=100`), retry en 500 transitorio, no usar `$select`, IDs estilo REST, contador local de cuota (sin headers de rate limit).
|
||||||
|
6. **Los complementos de pago (REP) no son visibles** — riesgo para el flujo PPD completo; escalar a Pedro (§10).
|
||||||
|
|
||||||
|
### 9.3 Presupuesto de peticiones (viabilidad del sync)
|
||||||
|
|
||||||
|
Escenario conservador: lista de facturas delta (1–2 req) + detalle solo de facturas nuevas/cambiadas (~3/día) + cotizaciones delta (1 req) + clientes delta (1 req) ⇒ **< 10 req por ciclo**. Con polling cada 15 min ≈ **~1,000 req/día**, 5 % del límite de 20K. Holgado.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 10 · Lo que quedó SIN validar y por qué
|
||||||
|
|
||||||
|
| Pendiente | Por qué no se validó | Cómo cerrarlo |
|
||||||
|
|---|---|---|
|
||||||
|
| **Escritura** (crear cotización/prefactura, convertir, emitir CFDI, cancelar) | **Prohibido en esta actividad**: cuenta de producción, sin sandbox, efecto fiscal. Regla dura de solo-GET. | Doc detallada tras login (developers.bind.com.mx) con Pedro; luego dry-run + confirmación humana en Etapa 2, empezando por un documento de prueba interno coordinado con Arturo. |
|
||||||
|
| Si la factura creada desde una cotización hereda alguna referencia a ésta | Requiere ejecutar la conversión (= escritura). | Mismo camino que el punto anterior; o preguntar a Arturo si la UI muestra el vínculo. |
|
||||||
|
| Shape real del `/{id}/xml` (¿XML embebido? ¿URL?) | El body se descartó por política de no persistir datos reales en esta corrida. | 1 GET dirigido leyendo solo las **claves** del JSON (sin valores), en la próxima sesión técnica. |
|
||||||
|
| Mapa completo `CFDIUse` interno → clave SAT | No hay catálogo expuesto; solo se observaron códigos `3` y `23`. | Pedir tabla a Pedro o doc tras login. |
|
||||||
|
| Códigos de `Status` > 2 (¿parciales, vencidas?) | Los filtros 3–5 no devolvieron filas: o no existen o no hay ejemplares en la cuenta. | Doc tras login; observar en operación. |
|
||||||
|
| **Complementos de pago (REP)** para PPD | Ningún recurso visible con los nombres probados. | **Crítico** — preguntar a Pedro/soporte BIND; el flujo PPD del MVP lo necesita al menos en lectura. |
|
||||||
|
| Rate limit real (20K/día) y comportamiento al agotarlo | No hay headers de cuota y agotar el límite adrede sería irresponsable en producción. | Aceptar el dato de Noe (20K) y llevar contador local. |
|
||||||
|
| Shape de `Users`, `Orders`, `Providers`, `Banks`, `BankAccounts`, `PriceLists` | Fuera del flujo del MVP; `Users` además contiene datos personales. | Cuando conciliación (Anexo B) lo requiera. |
|
||||||
|
| Webhooks / eventos push | No documentados públicamente; no sondeables por GET. | Preguntar a Pedro; mientras, polling incremental. |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Anexo · Reproducir la validación
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
cd bind-api-sandbox
|
||||||
|
# .env debe tener BIND_API_TOKEN (nunca se versiona; .gitignore lo cubre)
|
||||||
|
npm run validate:real # ronda 1: auth + inventario + shapes + OData + byId
|
||||||
|
npx tsx src/validate-real-api.ts --round2 # pagos, estatus, prefactura, detalles, paginación
|
||||||
|
npx tsx src/validate-real-api.ts --round3 # tope $top, literales CFDI, pdf/xml
|
||||||
|
npx tsx src/validate-real-api.ts --round4 # aritmética del saldo + xml
|
||||||
|
```
|
||||||
|
|
||||||
|
- Presupuesto acumulado entre rondas (`VALIDATION_BUDGET`, default 120). El script aborta al agotarlo.
|
||||||
|
- El reporte `validation-output/report.json` está sanitizado (solo estructura) y además gitignoreado por defensa en profundidad.
|
||||||
|
- El script es GET-only por construcción: no contiene ningún código capaz de emitir escrituras.
|
||||||
|
|
||||||
|
[REGISTRO #22]: ../bitacora/REGISTRO.md
|
||||||
|
[REGISTRO #29]: ../bitacora/REGISTRO.md
|
||||||
@@ -11,6 +11,7 @@
|
|||||||
"mock": "tsx src/mock-server/server.ts",
|
"mock": "tsx src/mock-server/server.ts",
|
||||||
"demo": "tsx src/demo.ts",
|
"demo": "tsx src/demo.ts",
|
||||||
"demo:prod": "BIND_BASE_URL=https://api.bind.com.mx tsx src/demo.ts",
|
"demo:prod": "BIND_BASE_URL=https://api.bind.com.mx tsx src/demo.ts",
|
||||||
|
"validate:real": "tsx src/validate-real-api.ts",
|
||||||
"typecheck": "tsc --noEmit"
|
"typecheck": "tsc --noEmit"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|||||||
@@ -6,9 +6,22 @@
|
|||||||
* - dry-run: loguea el request que se haría sin enviarlo (útil para revisar
|
* - dry-run: loguea el request que se haría sin enviarlo (útil para revisar
|
||||||
* un payload antes de aprobarlo manualmente).
|
* un payload antes de aprobarlo manualmente).
|
||||||
* - Retries con backoff exponencial en 429 y 5xx (no en 4xx fuera de 429).
|
* - Retries con backoff exponencial en 429 y 5xx (no en 4xx fuera de 429).
|
||||||
|
* El API real emite 500 transitorios ocasionales — confirmado 6-jul-2026.
|
||||||
* - Lleva contador local de requests para acercarse al límite de 20K/día
|
* - Lleva contador local de requests para acercarse al límite de 20K/día
|
||||||
* con visibilidad temprana (cuota real la valida el servidor).
|
* con visibilidad temprana (la cuota NO es observable en headers — confirmado).
|
||||||
* - Sin dependencias externas — usa fetch nativo de Node 20+.
|
* - Sin dependencias externas — usa fetch nativo de Node 20+.
|
||||||
|
*
|
||||||
|
* Reconciliado contra el API real (validación 6-jul-2026, ver VALIDACION-API.md):
|
||||||
|
* - Auth: SOLO `Authorization: Bearer` — el Ocp-Apim-Subscription-Key no se requiere.
|
||||||
|
* - GET por ID: estilo REST `/api/{Recurso}/{id}` (el estilo OData `(guid'...')`
|
||||||
|
* responde 404 en producción). El mock local sigue usando `(guid'...')`, por
|
||||||
|
* eso el estilo es configurable (`idStyle`).
|
||||||
|
* - Recursos reales: `Clients` (no Customers), `Quotes`, `Currencies`,
|
||||||
|
* `Warehouses`, `Locations`. NO existe recurso `Payments` — el acumulado
|
||||||
|
* pagado viene en cada factura (campo `Payments`).
|
||||||
|
* - `$select` NO funciona (500) y `$top` acepta máximo 100.
|
||||||
|
* - Token inválido → 500 con body "API Key es inválida" (no 401) — no
|
||||||
|
* reintentamos 500 cuyo body reporte api key inválida.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { buildQueryString, type ODataQuery } from "./odata.js";
|
import { buildQueryString, type ODataQuery } from "./odata.js";
|
||||||
@@ -20,14 +33,35 @@ import type {
|
|||||||
Payment,
|
Payment,
|
||||||
Product,
|
Product,
|
||||||
} from "./types.js";
|
} from "./types.js";
|
||||||
|
import type {
|
||||||
|
BindCollection,
|
||||||
|
ClientDetail,
|
||||||
|
ClientListItem,
|
||||||
|
CurrencyInfo,
|
||||||
|
InvoiceDetail,
|
||||||
|
InvoiceListItem,
|
||||||
|
LocationInfo,
|
||||||
|
QuoteDetail,
|
||||||
|
QuoteListItem,
|
||||||
|
WarehouseInfo,
|
||||||
|
} from "./types.real.js";
|
||||||
|
|
||||||
export type ClientMode = "read-only" | "dry-run" | "write";
|
export type ClientMode = "read-only" | "dry-run" | "write";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Estilo del GET por ID:
|
||||||
|
* - "rest": /api/Invoices/{id} → lo que el API REAL acepta (confirmado 6-jul-2026).
|
||||||
|
* - "odata": /api/Invoices(guid'{id}') → lo que implementa el mock local.
|
||||||
|
*/
|
||||||
|
export type IdStyle = "rest" | "odata";
|
||||||
|
|
||||||
export interface BindClientConfig {
|
export interface BindClientConfig {
|
||||||
baseUrl: string;
|
baseUrl: string;
|
||||||
apiKey: string;
|
apiKey: string;
|
||||||
subscriptionKey?: string;
|
subscriptionKey?: string;
|
||||||
mode?: ClientMode;
|
mode?: ClientMode;
|
||||||
|
/** Default "rest" (API real). Usa "odata" contra el mock local. */
|
||||||
|
idStyle?: IdStyle;
|
||||||
/** Máximo de reintentos para 429/5xx. */
|
/** Máximo de reintentos para 429/5xx. */
|
||||||
maxRetries?: number;
|
maxRetries?: number;
|
||||||
/** Logger opcional. Default: console. */
|
/** Logger opcional. Default: console. */
|
||||||
@@ -60,6 +94,7 @@ export class BindClient {
|
|||||||
private readonly apiKey: string;
|
private readonly apiKey: string;
|
||||||
private readonly subscriptionKey?: string;
|
private readonly subscriptionKey?: string;
|
||||||
private readonly mode: ClientMode;
|
private readonly mode: ClientMode;
|
||||||
|
private readonly idStyle: IdStyle;
|
||||||
private readonly maxRetries: number;
|
private readonly maxRetries: number;
|
||||||
private readonly logger: Pick<Console, "info" | "warn" | "error">;
|
private readonly logger: Pick<Console, "info" | "warn" | "error">;
|
||||||
private readonly fetchImpl: typeof fetch;
|
private readonly fetchImpl: typeof fetch;
|
||||||
@@ -72,19 +107,78 @@ export class BindClient {
|
|||||||
this.apiKey = cfg.apiKey;
|
this.apiKey = cfg.apiKey;
|
||||||
this.subscriptionKey = cfg.subscriptionKey;
|
this.subscriptionKey = cfg.subscriptionKey;
|
||||||
this.mode = cfg.mode ?? "read-only";
|
this.mode = cfg.mode ?? "read-only";
|
||||||
|
this.idStyle = cfg.idStyle ?? "rest";
|
||||||
this.maxRetries = cfg.maxRetries ?? 3;
|
this.maxRetries = cfg.maxRetries ?? 3;
|
||||||
this.logger = cfg.logger ?? console;
|
this.logger = cfg.logger ?? console;
|
||||||
this.fetchImpl = cfg.fetchImpl ?? globalThis.fetch;
|
this.fetchImpl = cfg.fetchImpl ?? globalThis.fetch;
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- Recursos del MVP --------------------------------------------------
|
private byId(resource: string, id: string): string {
|
||||||
|
return this.idStyle === "rest"
|
||||||
|
? `/api/${resource}/${id}`
|
||||||
|
: `/api/${resource}(guid'${id}')`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Recursos REALES confirmados (validación 6-jul-2026) ----------------
|
||||||
|
|
||||||
|
/** GET /api/Invoices — lista con acumulados Payments/CreditNotes (saldo = Total − ambos). */
|
||||||
|
invoiceList(query: ODataQuery = {}): Promise<BindCollection<InvoiceListItem>> {
|
||||||
|
return this.get<BindCollection<InvoiceListItem>>(`/api/Invoices${buildQueryString(query)}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** GET /api/Invoices/{id} — única fuente de PPD/PUE (CFDIPaymentTerm) y días de crédito. */
|
||||||
|
invoiceDetail(id: string): Promise<InvoiceDetail> {
|
||||||
|
return this.get<InvoiceDetail>(this.byId("Invoices", id));
|
||||||
|
}
|
||||||
|
|
||||||
|
/** GET /api/Clients — así se llaman los clientes en el API real (no Customers). */
|
||||||
|
clients(query: ODataQuery = {}): Promise<BindCollection<ClientListItem>> {
|
||||||
|
return this.get<BindCollection<ClientListItem>>(`/api/Clients${buildQueryString(query)}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** GET /api/Clients/{id} — trae CreditDays, contactos y (sic) Loctaion/LoctaionID. */
|
||||||
|
clientDetail(id: string): Promise<ClientDetail> {
|
||||||
|
return this.get<ClientDetail>(this.byId("Clients", id));
|
||||||
|
}
|
||||||
|
|
||||||
|
/** GET /api/Quotes — cotizaciones (0=Activa, 1=Cancelada, 2=Surtida). */
|
||||||
|
quotes(query: ODataQuery = {}): Promise<BindCollection<QuoteListItem>> {
|
||||||
|
return this.get<BindCollection<QuoteListItem>>(`/api/Quotes${buildQueryString(query)}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** GET /api/Quotes/{id} — partidas Items[]; SIN referencia a la factura generada. */
|
||||||
|
quoteDetail(id: string): Promise<QuoteDetail> {
|
||||||
|
return this.get<QuoteDetail>(this.byId("Quotes", id));
|
||||||
|
}
|
||||||
|
|
||||||
|
currencies(query: ODataQuery = {}): Promise<BindCollection<CurrencyInfo>> {
|
||||||
|
return this.get<BindCollection<CurrencyInfo>>(`/api/Currencies${buildQueryString(query)}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
warehouses(query: ODataQuery = {}): Promise<BindCollection<WarehouseInfo>> {
|
||||||
|
return this.get<BindCollection<WarehouseInfo>>(`/api/Warehouses${buildQueryString(query)}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
locations(query: ODataQuery = {}): Promise<BindCollection<LocationInfo>> {
|
||||||
|
return this.get<BindCollection<LocationInfo>>(`/api/Locations${buildQueryString(query)}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** GET /api/Invoices/{id}/pdf — devuelve el PDF binario del CFDI (insumo del módulo de envío). */
|
||||||
|
async invoicePdf(id: string): Promise<ArrayBuffer> {
|
||||||
|
return this.getBinary(`/api/Invoices/${id}/pdf`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Recursos de la era mock (types.ts aproximados) ----------------------
|
||||||
|
// El mock server sirve Customers/Payments con el schema aproximado previo a la
|
||||||
|
// validación. Se conservan para la demo local; NO usarlos contra producción
|
||||||
|
// (Customers → 404 real; Payments → 404 real — no existe el recurso).
|
||||||
|
|
||||||
customers(query: ODataQuery = {}): Promise<ODataCollection<Customer>> {
|
customers(query: ODataQuery = {}): Promise<ODataCollection<Customer>> {
|
||||||
return this.get<ODataCollection<Customer>>(`/api/Customers${buildQueryString(query)}`);
|
return this.get<ODataCollection<Customer>>(`/api/Customers${buildQueryString(query)}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
customer(id: string): Promise<Customer> {
|
customer(id: string): Promise<Customer> {
|
||||||
return this.get<Customer>(`/api/Customers(guid'${id}')`);
|
return this.get<Customer>(this.byId("Customers", id));
|
||||||
}
|
}
|
||||||
|
|
||||||
invoices(query: ODataQuery = {}): Promise<ODataCollection<Invoice>> {
|
invoices(query: ODataQuery = {}): Promise<ODataCollection<Invoice>> {
|
||||||
@@ -92,7 +186,7 @@ export class BindClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
invoice(id: string): Promise<Invoice> {
|
invoice(id: string): Promise<Invoice> {
|
||||||
return this.get<Invoice>(`/api/Invoices(guid'${id}')`);
|
return this.get<Invoice>(this.byId("Invoices", id));
|
||||||
}
|
}
|
||||||
|
|
||||||
payments(query: ODataQuery = {}): Promise<ODataCollection<Payment>> {
|
payments(query: ODataQuery = {}): Promise<ODataCollection<Payment>> {
|
||||||
@@ -133,6 +227,18 @@ export class BindClient {
|
|||||||
return this.request<T>("GET", path);
|
return this.request<T>("GET", path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** GET binario (PDF del CFDI). Cuenta contra la cuota como cualquier request. */
|
||||||
|
private async getBinary(path: string): Promise<ArrayBuffer> {
|
||||||
|
this.rolloverIfNewDay();
|
||||||
|
this.requestCount++;
|
||||||
|
const headers: Record<string, string> = { Authorization: `Bearer ${this.apiKey}` };
|
||||||
|
if (this.subscriptionKey) headers["Ocp-Apim-Subscription-Key"] = this.subscriptionKey;
|
||||||
|
const url = `${this.baseUrl}${path}`;
|
||||||
|
const res = await this.fetchImpl(url, { method: "GET", headers });
|
||||||
|
if (!res.ok) throw new BindApiError(res.status, url, await safeJson(res));
|
||||||
|
return res.arrayBuffer();
|
||||||
|
}
|
||||||
|
|
||||||
private async request<T>(method: string, path: string, body?: unknown): Promise<T> {
|
private async request<T>(method: string, path: string, body?: unknown): Promise<T> {
|
||||||
if (MUTATING.has(method) && this.mode === "read-only") {
|
if (MUTATING.has(method) && this.mode === "read-only") {
|
||||||
throw new BindReadOnlyViolation(method, path);
|
throw new BindReadOnlyViolation(method, path);
|
||||||
|
|||||||
@@ -0,0 +1,306 @@
|
|||||||
|
/**
|
||||||
|
* Tipos CONFIRMADOS contra el API real de BIND (validación del 6-jul-2026,
|
||||||
|
* cuenta Balam, solo lectura). Fuente: VALIDACION-API.md + validation-output/report.json.
|
||||||
|
*
|
||||||
|
* Conviven con types.ts (la aproximación que consume el mock server): el mock
|
||||||
|
* queda intacto; el código que apunte a producción debe tipar con ESTOS.
|
||||||
|
*
|
||||||
|
* Notas duras del API real:
|
||||||
|
* - Los clientes son `Clients` (no `Customers`); no existe recurso `Payments`.
|
||||||
|
* - GET por ID es estilo REST (`/api/Invoices/{id}`), NO OData `(guid'...')`.
|
||||||
|
* - La lista y el detalle de un mismo recurso difieren en campos y hasta en
|
||||||
|
* nombres (`Serie` vs `Series`; `Status` int vs string+`StatusCode`).
|
||||||
|
* - `CFDIPaymentTerm` = Método de pago SAT (PPD/PUE) y `CFDIPaymentMethod` =
|
||||||
|
* Forma de pago SAT — nomenclatura invertida respecto al SAT.
|
||||||
|
* - `Loctaion`/`LoctaionID` es un typo real del API en el detalle de Clients.
|
||||||
|
* - Saldo por factura = Total − Payments − CreditNotes (misma fila de lista).
|
||||||
|
*/
|
||||||
|
|
||||||
|
export type Guid = string;
|
||||||
|
export type IsoDateTime = string; // "2026-07-06T00:00:00" (sin zona en lo observado)
|
||||||
|
|
||||||
|
// ─── Invoices ───────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
/** Códigos de Invoices.Status confirmados vía filtros + detalle. */
|
||||||
|
export enum InvoiceStatusCode {
|
||||||
|
Activa = 0,
|
||||||
|
Pagada = 1,
|
||||||
|
Cancelada = 2,
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Fila de GET /api/Invoices (lista, 32 campos). */
|
||||||
|
export interface InvoiceListItem {
|
||||||
|
ID: Guid;
|
||||||
|
/** ⚠️ En el detalle este campo se llama `Series`. */
|
||||||
|
Serie: string;
|
||||||
|
Number: number;
|
||||||
|
/** Folio fiscal. `null` ⇒ prefactura (sin timbrar). */
|
||||||
|
UUID: Guid | null;
|
||||||
|
Date: IsoDateTime;
|
||||||
|
/** Vencimiento — insumo del aging. No existe `DueDate`. */
|
||||||
|
ExpirationDate: IsoDateTime;
|
||||||
|
ClientID: Guid;
|
||||||
|
ClientName: string;
|
||||||
|
RFC: string;
|
||||||
|
Cost: number;
|
||||||
|
Subtotal: number;
|
||||||
|
Discount: number;
|
||||||
|
VAT: number;
|
||||||
|
IEPS: number;
|
||||||
|
ISRRet: number;
|
||||||
|
VATRet: number;
|
||||||
|
Total: number;
|
||||||
|
/** Acumulado PAGADO de la factura (no es una colección). */
|
||||||
|
Payments: number;
|
||||||
|
/** Acumulado de notas de crédito aplicadas. */
|
||||||
|
CreditNotes: number;
|
||||||
|
CurrencyID: Guid;
|
||||||
|
LocationID: Guid;
|
||||||
|
WarehouseID: Guid;
|
||||||
|
PriceListID: Guid;
|
||||||
|
/** Código INTERNO de BIND (se observaron 3, 23) — no es la clave SAT (G03…). */
|
||||||
|
CFDIUse: number;
|
||||||
|
ExchangeRate: number;
|
||||||
|
VATRetRate: number;
|
||||||
|
Comments: string;
|
||||||
|
VATRate: number;
|
||||||
|
PurchaseOrder: string;
|
||||||
|
/** false ⇒ prefactura. */
|
||||||
|
IsFiscalInvoice: boolean;
|
||||||
|
ShowIEPS: boolean;
|
||||||
|
Status: InvoiceStatusCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Partida de servicios del detalle de factura. */
|
||||||
|
export interface InvoiceServiceLine {
|
||||||
|
ID: Guid;
|
||||||
|
IndexNumber: number;
|
||||||
|
ServiceID: Guid;
|
||||||
|
Name: string;
|
||||||
|
Code: string;
|
||||||
|
Qty: number;
|
||||||
|
Price: number;
|
||||||
|
/** Tasa de IVA por partida — habilita la validación 16 % MXN / 0 % extranjero. */
|
||||||
|
VATRate: number;
|
||||||
|
Discount: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** GET /api/Invoices/{id} (detalle, 50 campos). Campos exclusivos vs lista. */
|
||||||
|
export interface InvoiceDetail {
|
||||||
|
ID: Guid;
|
||||||
|
UUID: Guid | null;
|
||||||
|
/** ⚠️ La lista lo llama `Serie`. */
|
||||||
|
Series: string;
|
||||||
|
Number: number;
|
||||||
|
ClientID: Guid;
|
||||||
|
ClientName: string;
|
||||||
|
/** Días de crédito de la factura — solo en detalle. */
|
||||||
|
PaymentTerms: number;
|
||||||
|
/** Etiqueta legible ("Activa" | "Pagada" | "Cancelada"). */
|
||||||
|
Status: string;
|
||||||
|
StatusCode: InvoiceStatusCode;
|
||||||
|
ClientPhoneNumber: string | null;
|
||||||
|
ClientContact: string | null;
|
||||||
|
RFC: string;
|
||||||
|
CreatedByID: Guid;
|
||||||
|
CreatedByName: string;
|
||||||
|
CreationDate: IsoDateTime;
|
||||||
|
ApplicationDate: IsoDateTime;
|
||||||
|
PriceListID: Guid;
|
||||||
|
PriceListName: string;
|
||||||
|
LocationID: Guid;
|
||||||
|
LocationName: string;
|
||||||
|
WarehouseID: Guid;
|
||||||
|
WarehouseName: string;
|
||||||
|
/** ⚠️ FORMA de pago SAT (ej. "Transferencia Electrónica de Fondos", "Por Definir"). */
|
||||||
|
CFDIPaymentMethod: string;
|
||||||
|
/** ⚠️ MÉTODO de pago SAT — PPD/PUE (ej. "PAGO EN UNA SOLA EXHIBICIÓN"). Puede venir vacío. */
|
||||||
|
CFDIPaymentTerm: string;
|
||||||
|
CFDIAccountNumber: string;
|
||||||
|
/** Código de 3 letras ("MXN"/"USD") — a pesar del nombre. */
|
||||||
|
CurrencyName: string;
|
||||||
|
ExchangeRate: number;
|
||||||
|
PurchaseOrder: string;
|
||||||
|
FiscalID: Guid;
|
||||||
|
Address: string;
|
||||||
|
Comments: string;
|
||||||
|
Subtotal: number;
|
||||||
|
Discount: number;
|
||||||
|
VAT: number;
|
||||||
|
IEPS: number;
|
||||||
|
VATRet: number;
|
||||||
|
ISRRet: number;
|
||||||
|
Payments: number;
|
||||||
|
CreditNotes: number;
|
||||||
|
Products: unknown[]; // partidas de producto (vacío en Balam — facturan servicios)
|
||||||
|
Services: InvoiceServiceLine[];
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Saldo abierto por factura (Plan A operativo — ver VALIDACION-API.md §4). */
|
||||||
|
export function invoiceOpenBalance(inv: Pick<InvoiceListItem, "Total" | "Payments" | "CreditNotes">): number {
|
||||||
|
return inv.Total - inv.Payments - inv.CreditNotes;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ─── Clients ────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
/** Fila de GET /api/Clients (lista, 10 campos). */
|
||||||
|
export interface ClientListItem {
|
||||||
|
ID: Guid;
|
||||||
|
Number: number;
|
||||||
|
ClientName: string;
|
||||||
|
LegalName: string;
|
||||||
|
RFC: string;
|
||||||
|
Email: string | null;
|
||||||
|
Phone: string | null;
|
||||||
|
NextContactDate: IsoDateTime | null;
|
||||||
|
LocationID: Guid;
|
||||||
|
RegimenFiscal: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** GET /api/Clients/{id} (detalle, 28 campos). */
|
||||||
|
export interface ClientDetail {
|
||||||
|
ID: Guid;
|
||||||
|
RFC: string;
|
||||||
|
LegalName: string;
|
||||||
|
CommercialName: string;
|
||||||
|
/** Días de crédito default del cliente (30/45/90 del Discovery). */
|
||||||
|
CreditDays: number;
|
||||||
|
CreditAmount: number;
|
||||||
|
PaymentMethod: string;
|
||||||
|
CreationDate: IsoDateTime;
|
||||||
|
Status: string;
|
||||||
|
SalesContact: string;
|
||||||
|
CreditContact: string;
|
||||||
|
/** ⚠️ Typo REAL del API (sic). */
|
||||||
|
Loctaion: string;
|
||||||
|
/** ⚠️ Typo REAL del API (sic). */
|
||||||
|
LoctaionID: Guid;
|
||||||
|
Comments: string;
|
||||||
|
PriceList: string;
|
||||||
|
PriceListID: Guid;
|
||||||
|
PaymentTermType: string;
|
||||||
|
Email: string | null;
|
||||||
|
Telephones: string | null;
|
||||||
|
Number: number;
|
||||||
|
AccountNumber: string | null;
|
||||||
|
DefaultDiscount: number | null;
|
||||||
|
ClientSource: string;
|
||||||
|
Account: string;
|
||||||
|
City: string;
|
||||||
|
State: string;
|
||||||
|
Addresses: unknown[];
|
||||||
|
RegimenFiscal: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ─── Quotes ─────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
/** Códigos de Quotes.Status confirmados (StatusText de la misma fila). */
|
||||||
|
export enum QuoteStatusCode {
|
||||||
|
Activa = 0,
|
||||||
|
Cancelada = 1,
|
||||||
|
Surtida = 2,
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Fila de GET /api/Quotes (lista, 11 campos). */
|
||||||
|
export interface QuoteListItem {
|
||||||
|
ID: Guid;
|
||||||
|
Number: string;
|
||||||
|
CreationDate: IsoDateTime;
|
||||||
|
ClientName: string;
|
||||||
|
Locations: string;
|
||||||
|
Comments: string | null;
|
||||||
|
TotalOriginalCurrency: number;
|
||||||
|
/** Nombre ("Peso mexicano") — el código de 3 letras vive en el detalle. */
|
||||||
|
Currency: string;
|
||||||
|
Status: QuoteStatusCode;
|
||||||
|
Total: number;
|
||||||
|
StatusText: string | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Partida del detalle de cotización. */
|
||||||
|
export interface QuoteItem {
|
||||||
|
ID: Guid;
|
||||||
|
Code: string;
|
||||||
|
ProductID: Guid;
|
||||||
|
ProductName: string;
|
||||||
|
Unit: string;
|
||||||
|
Qty: number;
|
||||||
|
Price: number;
|
||||||
|
Amount: number;
|
||||||
|
IEPS: number;
|
||||||
|
VAT: number;
|
||||||
|
IndexNumber: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** GET /api/Quotes/{id} (detalle, 45 campos).
|
||||||
|
* ⚠️ NO trae referencia a la factura generada — la trazabilidad la lleva la plataforma. */
|
||||||
|
export interface QuoteDetail {
|
||||||
|
ID: Guid;
|
||||||
|
QuoteNumber: string;
|
||||||
|
ClientName: string;
|
||||||
|
ClientContact: string;
|
||||||
|
ClientID: Guid;
|
||||||
|
ClientPhone: string | null;
|
||||||
|
LocationName: string;
|
||||||
|
LocationID: Guid;
|
||||||
|
PriceListName: string;
|
||||||
|
PriceListID: Guid;
|
||||||
|
EmployeeName: string;
|
||||||
|
EmployeeID: Guid;
|
||||||
|
CurrencyCode: string;
|
||||||
|
ExchangeRate: number;
|
||||||
|
CreationDate: IsoDateTime;
|
||||||
|
Status: QuoteStatusCode;
|
||||||
|
StatusText: string | null;
|
||||||
|
Subtotal: number;
|
||||||
|
Discount: number;
|
||||||
|
IEPS: number;
|
||||||
|
VAT: number;
|
||||||
|
VATRate: number;
|
||||||
|
ISR: number;
|
||||||
|
ISRRate: number;
|
||||||
|
Total: number;
|
||||||
|
BaseCurrency: boolean;
|
||||||
|
Comments: string | null;
|
||||||
|
OriginalCurrencyDiscountAmount: number;
|
||||||
|
OriginalCurrencySubtotal: number;
|
||||||
|
IsPercentage: boolean;
|
||||||
|
ContactEmails: string | null;
|
||||||
|
ExternalIDType: number;
|
||||||
|
VatRet: number;
|
||||||
|
Items: QuoteItem[];
|
||||||
|
}
|
||||||
|
|
||||||
|
// ─── Catálogos ──────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
export interface CurrencyInfo {
|
||||||
|
ID: Guid;
|
||||||
|
Name: string;
|
||||||
|
Code: string; // "MXN", "USD"…
|
||||||
|
ExchangeRate: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface WarehouseInfo {
|
||||||
|
ID: Guid;
|
||||||
|
Name: string;
|
||||||
|
LocationID: Guid;
|
||||||
|
AvailableInOtherLoc: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface LocationInfo {
|
||||||
|
ID: Guid;
|
||||||
|
Name: string;
|
||||||
|
Street: string;
|
||||||
|
ExtNumber: string;
|
||||||
|
IntNumber: string;
|
||||||
|
ZipCode: string;
|
||||||
|
Colonia: string;
|
||||||
|
City: string;
|
||||||
|
State: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Respuesta de colección del API real: { value: [...] } sin count ni nextLink
|
||||||
|
* (el conteo total NO es accesible; paginar con $top=100 + $skip). */
|
||||||
|
export interface BindCollection<T> {
|
||||||
|
value: T[];
|
||||||
|
}
|
||||||
@@ -20,9 +20,14 @@ import { and, eq, ge, guid, lt } from "./client/odata.js";
|
|||||||
|
|
||||||
const cfg = {
|
const cfg = {
|
||||||
baseUrl: process.env.BIND_BASE_URL ?? "http://localhost:4010",
|
baseUrl: process.env.BIND_BASE_URL ?? "http://localhost:4010",
|
||||||
apiKey: process.env.BIND_API_KEY ?? "mock-bearer-token",
|
apiKey: process.env.BIND_API_TOKEN ?? process.env.BIND_API_KEY ?? "mock-bearer-token",
|
||||||
subscriptionKey: process.env.BIND_SUBSCRIPTION_KEY,
|
subscriptionKey: process.env.BIND_SUBSCRIPTION_KEY,
|
||||||
mode: (process.env.BIND_MODE as "read-only" | "dry-run" | "write") ?? "read-only",
|
mode: (process.env.BIND_MODE as "read-only" | "dry-run" | "write") ?? "read-only",
|
||||||
|
// El mock local implementa el GET por ID estilo OData (guid'...'); el API
|
||||||
|
// real usa estilo REST /{id} (validado 6-jul-2026 — ver VALIDACION-API.md §6).
|
||||||
|
idStyle: (/localhost|127\.0\.0\.1/.test(process.env.BIND_BASE_URL ?? "localhost")
|
||||||
|
? "odata"
|
||||||
|
: "rest") as "odata" | "rest",
|
||||||
};
|
};
|
||||||
|
|
||||||
const client = new BindClient(cfg);
|
const client = new BindClient(cfg);
|
||||||
|
|||||||
@@ -0,0 +1,857 @@
|
|||||||
|
/**
|
||||||
|
* Validación técnica de la API REAL de BIND ERP (producción, cuenta Balam).
|
||||||
|
* Actividad "Validación técnica de la API de BIND" — Etapa 0.
|
||||||
|
*
|
||||||
|
* REGLAS DURAS (no negociables):
|
||||||
|
* - SOLO LECTURA. Este archivo únicamente construye peticiones GET; no existe
|
||||||
|
* código capaz de emitir POST/PUT/PATCH/DELETE.
|
||||||
|
* - El token se lee de `.env` (BIND_API_TOKEN) y JAMÁS se imprime, se loguea
|
||||||
|
* ni se escribe en el reporte. El serializado final pasa por un scrub que
|
||||||
|
* además redacta cualquier patrón tipo RFC o email por si el sanitizador
|
||||||
|
* estructural dejara pasar algo.
|
||||||
|
* - El reporte solo conserva ESTRUCTURA: nombres de campos, tipos, formatos,
|
||||||
|
* conteos, códigos de estatus. Nunca valores reales (nombres, RFCs, montos,
|
||||||
|
* folios, correos).
|
||||||
|
* - Presupuesto duro de peticiones (default 120 < 150 acordado; el límite de
|
||||||
|
* BIND es 20K/día). Cada retry cuenta contra el presupuesto.
|
||||||
|
*
|
||||||
|
* Salida: validation-output/report.json (sanitizado; el folder está gitignoreado
|
||||||
|
* por defensa en profundidad) + resumen sanitizado en consola.
|
||||||
|
*
|
||||||
|
* Uso: npm run validate:real
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
||||||
|
import { dirname, join } from "node:path";
|
||||||
|
import { fileURLToPath } from "node:url";
|
||||||
|
|
||||||
|
const HERE = dirname(fileURLToPath(import.meta.url));
|
||||||
|
const ROOT = join(HERE, "..");
|
||||||
|
const OUT_DIR = join(ROOT, "validation-output");
|
||||||
|
|
||||||
|
// ─── Configuración ──────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
const env = loadEnv();
|
||||||
|
const BASE = (env.BIND_BASE_URL ?? process.env.BIND_BASE_URL ?? "https://api.bind.com.mx").replace(/\/+$/, "");
|
||||||
|
const TOKEN = env.BIND_API_TOKEN ?? env.BIND_API_KEY ?? process.env.BIND_API_TOKEN ?? "";
|
||||||
|
const BUDGET = Number(env.VALIDATION_BUDGET ?? 120);
|
||||||
|
const PACE_MS = 120; // pausa entre peticiones — gentileza con producción
|
||||||
|
|
||||||
|
function loadEnv(): Record<string, string> {
|
||||||
|
const out: Record<string, string> = {};
|
||||||
|
try {
|
||||||
|
const raw = readFileSync(join(ROOT, ".env"), "utf8");
|
||||||
|
for (const line of raw.split(/\r?\n/)) {
|
||||||
|
const m = /^\s*([A-Za-z_][A-Za-z0-9_]*)\s*=\s*(.*?)\s*$/.exec(line);
|
||||||
|
if (m && m[1] && m[2] !== undefined) out[m[1]] = m[2].replace(/^["']|["']$/g, "");
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
/* sin .env — se valida abajo */
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ─── Sonda HTTP (GET-only por construcción) ─────────────────────────────────
|
||||||
|
|
||||||
|
let used = 0;
|
||||||
|
|
||||||
|
interface Probe {
|
||||||
|
path: string;
|
||||||
|
status: number;
|
||||||
|
ok: boolean;
|
||||||
|
contentType: string | null;
|
||||||
|
headers: Record<string, string>; // allowlist no sensible
|
||||||
|
bodyShape: "array" | "odata-value" | "object" | "empty" | "non-json";
|
||||||
|
rowCount: number | null;
|
||||||
|
count: number | string | null; // odata.count viene como string en OData v3
|
||||||
|
nextLink: boolean;
|
||||||
|
errorSnippet?: string;
|
||||||
|
/** SOLO en memoria — nunca va al reporte. */
|
||||||
|
rows: unknown[] | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const HEADER_KEEP = /rate|limit|quota|remain|retry-after|dataserviceversion|odata-version|content-type|www-authenticate|apim/i;
|
||||||
|
|
||||||
|
async function probeGet(path: string, tokenOverride?: string | null): Promise<Probe> {
|
||||||
|
if (used >= BUDGET) throw new Error(`Presupuesto de ${BUDGET} peticiones agotado — abortando por seguridad.`);
|
||||||
|
const token = tokenOverride === undefined ? TOKEN : tokenOverride;
|
||||||
|
|
||||||
|
for (let attempt = 0; attempt < 2; attempt++) {
|
||||||
|
used++;
|
||||||
|
const headers: Record<string, string> = { Accept: "application/json" };
|
||||||
|
if (token) headers.Authorization = `Bearer ${token}`;
|
||||||
|
let res: Response;
|
||||||
|
try {
|
||||||
|
res = await fetch(`${BASE}${path}`, {
|
||||||
|
method: "GET", // ÚNICO método en todo el archivo
|
||||||
|
headers,
|
||||||
|
signal: AbortSignal.timeout(25_000),
|
||||||
|
});
|
||||||
|
} catch (err) {
|
||||||
|
await sleep(PACE_MS);
|
||||||
|
if (attempt === 0) continue;
|
||||||
|
return {
|
||||||
|
path, status: 0, ok: false, contentType: null, headers: {},
|
||||||
|
bodyShape: "empty", rowCount: null, count: null, nextLink: false,
|
||||||
|
errorSnippet: `network: ${scrub(String((err as Error).message)).slice(0, 120)}`, rows: null,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const keep: Record<string, string> = {};
|
||||||
|
res.headers.forEach((v, k) => { if (HEADER_KEEP.test(k)) keep[k] = v; });
|
||||||
|
|
||||||
|
const text = await res.text();
|
||||||
|
let json: unknown = null;
|
||||||
|
try { json = text ? JSON.parse(text) : null; } catch { /* non-json */ }
|
||||||
|
|
||||||
|
if ((res.status === 429 || res.status >= 500) && attempt === 0) {
|
||||||
|
const ra = Number(res.headers.get("Retry-After") ?? 2);
|
||||||
|
console.log(` ⏳ ${res.status} en ${path} — retry en ${ra}s`);
|
||||||
|
await sleep(Math.min(ra, 10) * 1000);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { rows, bodyShape, count, nextLink } = extractRows(json, text);
|
||||||
|
const probe: Probe = {
|
||||||
|
path, status: res.status, ok: res.ok,
|
||||||
|
contentType: res.headers.get("content-type"),
|
||||||
|
headers: keep, bodyShape,
|
||||||
|
rowCount: rows ? rows.length : null,
|
||||||
|
count, nextLink, rows,
|
||||||
|
};
|
||||||
|
if (!res.ok) {
|
||||||
|
const raw = typeof json === "object" && json !== null ? JSON.stringify(json) : text;
|
||||||
|
probe.errorSnippet = scrub(raw ?? "").slice(0, 300);
|
||||||
|
}
|
||||||
|
await sleep(PACE_MS);
|
||||||
|
return probe;
|
||||||
|
}
|
||||||
|
throw new Error("unreachable");
|
||||||
|
}
|
||||||
|
|
||||||
|
function extractRows(json: unknown, text: string): Pick<Probe, "rows" | "bodyShape" | "count" | "nextLink"> {
|
||||||
|
if (json === null) return { rows: null, bodyShape: text.trim() ? "non-json" : "empty", count: null, nextLink: false };
|
||||||
|
if (Array.isArray(json)) return { rows: json, bodyShape: "array", count: null, nextLink: false };
|
||||||
|
if (typeof json === "object") {
|
||||||
|
const o = json as Record<string, unknown>;
|
||||||
|
const value = o["value"];
|
||||||
|
if (Array.isArray(value)) {
|
||||||
|
const count = (o["odata.count"] ?? o["@odata.count"] ?? null) as number | string | null;
|
||||||
|
const nextLink = Boolean(o["odata.nextLink"] ?? o["@odata.nextLink"]);
|
||||||
|
return { rows: value, bodyShape: "odata-value", count, nextLink };
|
||||||
|
}
|
||||||
|
return { rows: [json], bodyShape: "object", count: null, nextLink: false };
|
||||||
|
}
|
||||||
|
return { rows: null, bodyShape: "non-json", count: null, nextLink: false };
|
||||||
|
}
|
||||||
|
|
||||||
|
// ─── Sanitizador estructural ────────────────────────────────────────────────
|
||||||
|
|
||||||
|
interface FieldInfo {
|
||||||
|
name: string;
|
||||||
|
types: string[];
|
||||||
|
formats: string[];
|
||||||
|
nullable: boolean;
|
||||||
|
enumValues?: string[];
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Campos cuyo VALOR es un código de proceso (no dato personal) y puede documentarse. */
|
||||||
|
const ENUM_FIELD = /status|estatus|type|tipo|method|metodo|use|uso|currency|moneda|cfdi|way/i;
|
||||||
|
/** Nunca documentar valores de campos que huelan a monto/cantidad aunque matcheen arriba. */
|
||||||
|
const ENUM_EXCLUDE = /total|amount|monto|price|cost|balance|exchange|sum|qty|quantity|rate|saldo/i;
|
||||||
|
/** Literales de catálogo SAT (PPD/PUE, uso CFDI) — seguros y valiosos; se permite más largo. */
|
||||||
|
const SAT_CATALOG_FIELD = /cfdi(use|paymentmethod|paymentterm)|paymentmethod|regimenfiscal/i;
|
||||||
|
|
||||||
|
function formatHint(v: unknown): string {
|
||||||
|
if (v === null || v === undefined) return "null";
|
||||||
|
if (typeof v === "boolean") return "boolean";
|
||||||
|
if (typeof v === "number") return Number.isInteger(v) ? "integer" : "decimal";
|
||||||
|
if (Array.isArray(v)) return "array";
|
||||||
|
if (typeof v === "object") return "object";
|
||||||
|
const s = String(v);
|
||||||
|
if (/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(s)) return "guid/uuid";
|
||||||
|
if (/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}/.test(s)) return "datetime-iso";
|
||||||
|
if (/^\d{4}-\d{2}-\d{2}$/.test(s)) return "date-iso";
|
||||||
|
if (/^\/Date\(-?\d+([+-]\d{4})?\)\/$/.test(s)) return "datetime-wcf(/Date(ms)/)";
|
||||||
|
if (/^[\w.+-]+@[\w-]+\.[\w.]+$/.test(s)) return "email(REDACTADO)";
|
||||||
|
if (/^[A-ZÑ&]{3,4}\d{6}[A-Z0-9]{3}$/.test(s)) return "rfc(REDACTADO)";
|
||||||
|
if (/^https?:\/\//.test(s)) return "url";
|
||||||
|
if (/^[A-Z]{3}$/.test(s)) return "code-3letras";
|
||||||
|
return `string(≈${lenBucket(s.length)})`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function lenBucket(n: number): string {
|
||||||
|
if (n === 0) return "vacío";
|
||||||
|
if (n <= 10) return "corto";
|
||||||
|
if (n <= 40) return "medio";
|
||||||
|
return "largo";
|
||||||
|
}
|
||||||
|
|
||||||
|
function safeEnumValue(fieldName: string, v: unknown): string | null {
|
||||||
|
if (ENUM_EXCLUDE.test(fieldName)) return null;
|
||||||
|
if (!ENUM_FIELD.test(fieldName)) return null;
|
||||||
|
// Números: solo enteros pequeños (códigos de catálogo), jamás montos/decimales.
|
||||||
|
if (typeof v === "number") return Number.isInteger(v) && Math.abs(v) < 1000 ? String(v) : null;
|
||||||
|
const maxLen = SAT_CATALOG_FIELD.test(fieldName) ? 60 : 14;
|
||||||
|
if (typeof v !== "string" || v.length === 0 || v.length > maxLen) return null;
|
||||||
|
const h = formatHint(v);
|
||||||
|
if (/REDACTADO|guid|datetime|date-iso|url/.test(h)) return null;
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Analiza filas y devuelve SOLO estructura. Recorre objetos anidados un nivel (Lines[].Campo). */
|
||||||
|
function analyzeRows(rows: unknown[], cap = 5): FieldInfo[] {
|
||||||
|
const acc = new Map<string, { types: Set<string>; formats: Set<string>; nullable: boolean; enums: Set<string> }>();
|
||||||
|
|
||||||
|
const visit = (obj: Record<string, unknown>, prefix: string) => {
|
||||||
|
for (const [k, v] of Object.entries(obj)) {
|
||||||
|
const name = prefix + k;
|
||||||
|
let rec = acc.get(name);
|
||||||
|
if (!rec) { rec = { types: new Set(), formats: new Set(), nullable: false, enums: new Set() }; acc.set(name, rec); }
|
||||||
|
if (v === null || v === undefined) { rec.nullable = true; rec.types.add("null"); continue; }
|
||||||
|
rec.types.add(Array.isArray(v) ? "array" : typeof v);
|
||||||
|
rec.formats.add(formatHint(v));
|
||||||
|
const ev = safeEnumValue(k, v);
|
||||||
|
if (ev !== null && rec.enums.size < 10) rec.enums.add(ev);
|
||||||
|
if (!prefix && Array.isArray(v) && typeof v[0] === "object" && v[0] !== null) {
|
||||||
|
visit(v[0] as Record<string, unknown>, `${k}[].`);
|
||||||
|
} else if (!prefix && typeof v === "object" && !Array.isArray(v)) {
|
||||||
|
visit(v as Record<string, unknown>, `${k}.`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
for (const row of rows.slice(0, cap)) {
|
||||||
|
if (typeof row === "object" && row !== null && !Array.isArray(row)) {
|
||||||
|
visit(row as Record<string, unknown>, "");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return [...acc.entries()].map(([name, r]) => {
|
||||||
|
const fi: FieldInfo = {
|
||||||
|
name,
|
||||||
|
types: [...r.types],
|
||||||
|
formats: [...r.formats],
|
||||||
|
nullable: r.nullable,
|
||||||
|
};
|
||||||
|
if (r.enums.size > 0) fi.enumValues = [...r.enums];
|
||||||
|
return fi;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function scrub(s: string): string {
|
||||||
|
let out = s;
|
||||||
|
if (TOKEN) out = out.replaceAll(TOKEN, "[TOKEN-REDACTADO]");
|
||||||
|
return out
|
||||||
|
.replace(/[A-ZÑ&]{3,4}\d{6}[A-Z0-9]{3}/g, "[RFC-REDACTADO]")
|
||||||
|
.replace(/[\w.+-]+@[\w-]+\.[\w.]+/g, "[EMAIL-REDACTADO]");
|
||||||
|
}
|
||||||
|
|
||||||
|
function sleep(ms: number): Promise<void> {
|
||||||
|
return new Promise((r) => setTimeout(r, ms));
|
||||||
|
}
|
||||||
|
|
||||||
|
// ─── Reporte ────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
interface InventoryEntry {
|
||||||
|
resource: string;
|
||||||
|
path: string;
|
||||||
|
status: number;
|
||||||
|
bodyShape: string;
|
||||||
|
rowCount: number | null;
|
||||||
|
note: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const report = {
|
||||||
|
meta: {
|
||||||
|
ranAt: new Date().toISOString(),
|
||||||
|
baseUrl: BASE,
|
||||||
|
tokenSource: ".env BIND_API_TOKEN (usuario BIND: Arturo Rosas, según correo de Pedro 6-jul-2026)",
|
||||||
|
budget: BUDGET,
|
||||||
|
requestsUsed: 0,
|
||||||
|
note: "Reporte sanitizado: solo estructura (campos, tipos, formatos, conteos, códigos). Sin valores reales.",
|
||||||
|
},
|
||||||
|
auth: {} as Record<string, unknown>,
|
||||||
|
inventory: [] as InventoryEntry[],
|
||||||
|
shapes: {} as Record<string, { rowsAnalyzed: number; totalCount: number | string | null; fields: FieldInfo[] }>,
|
||||||
|
odata: {} as Record<string, unknown>,
|
||||||
|
byId: {} as Record<string, unknown>,
|
||||||
|
multiCompany: {} as Record<string, unknown>,
|
||||||
|
balanceQuestion: {} as Record<string, unknown>,
|
||||||
|
headersObserved: {} as Record<string, string>,
|
||||||
|
paymentsHunt: {} as Record<string, unknown>,
|
||||||
|
statusSemantics: {} as Record<string, unknown>,
|
||||||
|
prefactura: {} as Record<string, unknown>,
|
||||||
|
pagination: {} as Record<string, unknown>,
|
||||||
|
seriesHunt: {} as Record<string, unknown>,
|
||||||
|
};
|
||||||
|
|
||||||
|
function projection(p: Probe): Omit<Probe, "rows"> {
|
||||||
|
const { rows: _rows, ...rest } = p;
|
||||||
|
return rest;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ─── Fases ──────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
const PRIORITY_RESOURCES = [
|
||||||
|
"Invoices", "Clients", "Customers", "Payments",
|
||||||
|
"Quotes", "Quotations", "Cotizaciones",
|
||||||
|
"Products", "Currencies", "Warehouses", "Locations", "Branches", "Sucursales", "Series",
|
||||||
|
];
|
||||||
|
|
||||||
|
const SECONDARY_RESOURCES = [
|
||||||
|
"Activities", "CreditNotes", "Taxes", "PriceLists", "Prices",
|
||||||
|
"Orders", "SalesOrders", "PurchaseOrders", "Providers", "Suppliers",
|
||||||
|
"Banks", "BankAccounts", "Sellers", "Employees", "Users",
|
||||||
|
"Companies", "Expenses", "Inventory", "CFDI", "CFDIs",
|
||||||
|
];
|
||||||
|
|
||||||
|
async function phaseAuth(): Promise<string> {
|
||||||
|
console.log("\n── Fase 1 · Autenticación");
|
||||||
|
// Endpoint de referencia barato. Products está documentado públicamente.
|
||||||
|
const ok = await probeGet("/api/Products?$top=1");
|
||||||
|
const noToken = await probeGet("/api/Products?$top=1", null);
|
||||||
|
const badToken = await probeGet("/api/Products?$top=1", "invalid-token-abc123");
|
||||||
|
report.auth = {
|
||||||
|
scheme: "Authorization: Bearer <token> (único header; sin Ocp-Apim-Subscription-Key)",
|
||||||
|
validToken: projection(ok),
|
||||||
|
missingToken: projection(noToken),
|
||||||
|
invalidToken: projection(badToken),
|
||||||
|
};
|
||||||
|
Object.assign(report.headersObserved, ok.headers);
|
||||||
|
console.log(` token válido → ${ok.status} · sin token → ${noToken.status} · token corrupto → ${badToken.status}`);
|
||||||
|
if (!ok.ok) {
|
||||||
|
console.log(" ⚠️ El token de .env NO autenticó contra /api/Products. Revisar antes de seguir.");
|
||||||
|
}
|
||||||
|
return ok.ok ? "ok" : "fail";
|
||||||
|
}
|
||||||
|
|
||||||
|
async function phaseInventory(): Promise<Map<string, Probe>> {
|
||||||
|
console.log("\n── Fase 2 · Inventario de recursos (GET {recurso}?$top=1)");
|
||||||
|
const results = new Map<string, Probe>();
|
||||||
|
for (const res of [...PRIORITY_RESOURCES, ...SECONDARY_RESOURCES]) {
|
||||||
|
let p = await probeGet(`/api/${res}?$top=1`);
|
||||||
|
let note = "";
|
||||||
|
// Algunos endpoints podrían rechazar $top — reintenta plano solo para prioritarios.
|
||||||
|
if (p.status === 400 && PRIORITY_RESOURCES.includes(res)) {
|
||||||
|
const plain = await probeGet(`/api/${res}`);
|
||||||
|
if (plain.ok) { p = plain; note = "existe pero rechaza $top=1 (400)"; }
|
||||||
|
else note = "400 con y sin $top";
|
||||||
|
}
|
||||||
|
if (p.status === 404) note ||= "no existe con este nombre";
|
||||||
|
if (p.status === 401 || p.status === 403) note ||= "sin permiso para este token";
|
||||||
|
if (p.ok) note ||= `responde ${p.bodyShape}`;
|
||||||
|
results.set(res, p);
|
||||||
|
report.inventory.push({
|
||||||
|
resource: res, path: p.path, status: p.status,
|
||||||
|
bodyShape: p.bodyShape, rowCount: p.rowCount, note,
|
||||||
|
});
|
||||||
|
Object.assign(report.headersObserved, p.headers);
|
||||||
|
console.log(` [${String(used).padStart(3)}/${BUDGET}] ${res.padEnd(15)} → ${p.status}${note ? ` (${note})` : ""}`);
|
||||||
|
}
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function phaseShapes(inventory: Map<string, Probe>): Promise<Map<string, unknown[]>> {
|
||||||
|
console.log("\n── Fase 3 · Inventario de campos ($top=5, solo estructura)");
|
||||||
|
const rowsByResource = new Map<string, unknown[]>();
|
||||||
|
const targets = [...PRIORITY_RESOURCES, "Companies", "CreditNotes", "Activities"]
|
||||||
|
.filter((r) => inventory.get(r)?.ok);
|
||||||
|
for (const res of targets) {
|
||||||
|
const p = await probeGet(`/api/${res}?$top=5`);
|
||||||
|
const rows = p.rows ?? [];
|
||||||
|
rowsByResource.set(res, rows);
|
||||||
|
report.shapes[res] = {
|
||||||
|
rowsAnalyzed: Math.min(rows.length, 5),
|
||||||
|
totalCount: p.count,
|
||||||
|
fields: analyzeRows(rows),
|
||||||
|
};
|
||||||
|
console.log(` [${String(used).padStart(3)}/${BUDGET}] ${res.padEnd(15)} → ${rows.length} filas analizadas, ${report.shapes[res].fields.length} campos`);
|
||||||
|
}
|
||||||
|
return rowsByResource;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function phaseOData(rowsByResource: Map<string, unknown[]>): Promise<void> {
|
||||||
|
console.log("\n── Fase 4 · Mecánica OData");
|
||||||
|
// Elige el mejor recurso disponible para las pruebas.
|
||||||
|
const resource = ["Invoices", "Products", "Clients", "Customers"].find((r) => rowsByResource.has(r));
|
||||||
|
if (!resource) { report.odata = { skipped: "ningún recurso disponible" }; return; }
|
||||||
|
const fields = report.shapes[resource]?.fields ?? [];
|
||||||
|
const idField = fields.find((f) => /^id$/i.test(f.name))?.name ?? fields.find((f) => f.formats.includes("guid/uuid"))?.name;
|
||||||
|
const dateField = fields.find((f) => f.formats.some((x) => x.startsWith("datetime")))?.name;
|
||||||
|
const numField = fields.find((f) => /integer|decimal/.test(f.formats.join()) && !/id/i.test(f.name))?.name;
|
||||||
|
const enumField = fields.find((f) => f.enumValues?.length);
|
||||||
|
|
||||||
|
const odata: Record<string, unknown> = { resourceUsed: resource, idField, dateField, numField };
|
||||||
|
|
||||||
|
// $top / $skip coherentes
|
||||||
|
const a = await probeGet(`/api/${resource}?$top=2`);
|
||||||
|
const b = await probeGet(`/api/${resource}?$top=1&$skip=1`);
|
||||||
|
if (idField && a.rows?.length === 2 && b.rows?.length === 1) {
|
||||||
|
const id = (r: unknown) => (r as Record<string, unknown>)[idField];
|
||||||
|
odata.topSkip = { works: id(a.rows[1]) === id(b.rows[0]), statuses: [a.status, b.status] };
|
||||||
|
} else {
|
||||||
|
odata.topSkip = { works: null, statuses: [a.status, b.status], note: "sin filas suficientes para comparar" };
|
||||||
|
}
|
||||||
|
|
||||||
|
// $orderby
|
||||||
|
if (dateField) {
|
||||||
|
const o = await probeGet(`/api/${resource}?$top=3&$orderby=${encodeURIComponent(`${dateField} asc`)}`);
|
||||||
|
let sorted: boolean | null = null;
|
||||||
|
if (o.rows && o.rows.length >= 2) {
|
||||||
|
const vals = o.rows.map((r) => String((r as Record<string, unknown>)[dateField] ?? ""));
|
||||||
|
sorted = vals.every((v, i) => i === 0 || v >= String(vals[i - 1]));
|
||||||
|
}
|
||||||
|
odata.orderby = { field: dateField, status: o.status, ascendingVerified: sorted };
|
||||||
|
}
|
||||||
|
|
||||||
|
// Conteo total: v3 ($inlinecount) vs v4 ($count)
|
||||||
|
const v3 = await probeGet(`/api/${resource}?$top=1&$inlinecount=allpages`);
|
||||||
|
const v4 = await probeGet(`/api/${resource}?$top=1&$count=true`);
|
||||||
|
odata.countMechanism = {
|
||||||
|
"v3 $inlinecount=allpages": { status: v3.status, countReturned: v3.count !== null, totalCount: v3.count },
|
||||||
|
"v4 $count=true": { status: v4.status, countReturned: v4.count !== null, totalCount: v4.count },
|
||||||
|
};
|
||||||
|
|
||||||
|
// $filter numérico
|
||||||
|
if (numField) {
|
||||||
|
const f = await probeGet(`/api/${resource}?$top=1&$filter=${encodeURIComponent(`${numField} ge 0`)}`);
|
||||||
|
odata.filterNumeric = { expr: `${numField} ge 0`, status: f.status, rows: f.rowCount };
|
||||||
|
}
|
||||||
|
|
||||||
|
// $filter por enum observado (código de proceso, no dato personal)
|
||||||
|
if (enumField?.enumValues?.[0] !== undefined) {
|
||||||
|
const isNum = /^\d+$/.test(enumField.enumValues[0]);
|
||||||
|
const lit = isNum ? enumField.enumValues[0] : `'${enumField.enumValues[0]}'`;
|
||||||
|
const f = await probeGet(`/api/${resource}?$top=1&$filter=${encodeURIComponent(`${enumField.name} eq ${lit}`)}`);
|
||||||
|
odata.filterEnum = { expr: `${enumField.name} eq ${lit}`, status: f.status, rows: f.rowCount };
|
||||||
|
}
|
||||||
|
|
||||||
|
// $filter por fecha: sintaxis v3 (datetime'...') vs v4 (literal ISO)
|
||||||
|
if (dateField) {
|
||||||
|
const fv3 = await probeGet(`/api/${resource}?$top=1&$filter=${encodeURIComponent(`${dateField} ge datetime'2020-01-01T00:00:00'`)}`);
|
||||||
|
let fv4: Probe | null = null;
|
||||||
|
if (!fv3.ok) fv4 = await probeGet(`/api/${resource}?$top=1&$filter=${encodeURIComponent(`${dateField} ge 2020-01-01T00:00:00Z`)}`);
|
||||||
|
odata.filterDate = {
|
||||||
|
"v3 datetime'...'": fv3.status,
|
||||||
|
...(fv4 ? { "v4 ISO literal": fv4.status } : {}),
|
||||||
|
verdict: fv3.ok ? "sintaxis OData v3" : fv4?.ok ? "sintaxis OData v4" : "ninguna funcionó",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// $select
|
||||||
|
if (idField) {
|
||||||
|
const s = await probeGet(`/api/${resource}?$top=1&$select=${idField}`);
|
||||||
|
odata.select = { status: s.status, works: s.ok };
|
||||||
|
// Página default (barata: solo IDs) — tamaño de página y nextLink
|
||||||
|
if (s.ok) {
|
||||||
|
const d = await probeGet(`/api/${resource}?$select=${idField}`);
|
||||||
|
odata.defaultPage = { rowsReturned: d.rowCount, nextLinkPresent: d.nextLink, totalCount: d.count, status: d.status };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
report.odata = odata;
|
||||||
|
console.log(` recurso de prueba: ${resource} · resultados en reporte`);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function phaseById(rowsByResource: Map<string, unknown[]>): Promise<void> {
|
||||||
|
console.log("\n── Fase 5 · GET por ID");
|
||||||
|
const resource = ["Invoices", "Clients", "Customers", "Products"].find((r) => rowsByResource.has(r) && (rowsByResource.get(r)?.length ?? 0) > 0);
|
||||||
|
if (!resource) { report.byId = { skipped: "sin filas para tomar un ID" }; return; }
|
||||||
|
const fields = report.shapes[resource]?.fields ?? [];
|
||||||
|
const idField = fields.find((f) => /^id$/i.test(f.name))?.name;
|
||||||
|
if (!idField) { report.byId = { skipped: "sin campo ID identificable" }; return; }
|
||||||
|
const firstId = String((rowsByResource.get(resource)![0] as Record<string, unknown>)[idField] ?? "");
|
||||||
|
if (!firstId) { report.byId = { skipped: "ID vacío" }; return; }
|
||||||
|
|
||||||
|
const odataStyle = await probeGet(`/api/${resource}(guid'${firstId}')`);
|
||||||
|
let restStyle: Probe | null = null;
|
||||||
|
if (!odataStyle.ok) restStyle = await probeGet(`/api/${resource}/${firstId}`);
|
||||||
|
report.byId = {
|
||||||
|
resource,
|
||||||
|
"odata (guid'...')": odataStyle.status,
|
||||||
|
...(restStyle ? { "rest (/{id})": restStyle.status } : {}),
|
||||||
|
verdict: odataStyle.ok ? "estilo OData key" : restStyle?.ok ? "estilo REST /{id}" : "ninguno funcionó",
|
||||||
|
// ¿El detalle trae más campos que la lista? (p.ej. Lines embebidas)
|
||||||
|
detailFieldCount: odataStyle.ok || restStyle?.ok
|
||||||
|
? analyzeRows((odataStyle.ok ? odataStyle : restStyle!).rows ?? []).length
|
||||||
|
: null,
|
||||||
|
listFieldCount: fields.length,
|
||||||
|
};
|
||||||
|
if (odataStyle.ok || restStyle?.ok) {
|
||||||
|
const detail = (odataStyle.ok ? odataStyle : restStyle!).rows ?? [];
|
||||||
|
report.shapes[`${resource}(detalle por ID)`] = {
|
||||||
|
rowsAnalyzed: detail.length,
|
||||||
|
totalCount: null,
|
||||||
|
fields: analyzeRows(detail),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
console.log(` ${resource} por ID → OData:${odataStyle.status}${restStyle ? ` / REST:${restStyle.status}` : ""}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
function phaseBalanceVerdict(): void {
|
||||||
|
const inv = report.shapes["Invoices"] ?? report.shapes["Invoices(detalle por ID)"];
|
||||||
|
if (!inv) { report.balanceQuestion = { verdict: "SIN VALIDAR — Invoices no accesible" }; return; }
|
||||||
|
const all = [
|
||||||
|
...(report.shapes["Invoices"]?.fields ?? []),
|
||||||
|
...(report.shapes["Invoices(detalle por ID)"]?.fields ?? []),
|
||||||
|
];
|
||||||
|
const balanceish = [...new Set(all.filter((f) => /balance|saldo|due|paid|pending|remain|credit|debt|payment/i.test(f.name)).map((f) => f.name))];
|
||||||
|
report.balanceQuestion = {
|
||||||
|
fieldsMatching: balanceish,
|
||||||
|
verdict: balanceish.length > 0
|
||||||
|
? "REVISAR nombres arriba — hay candidatos a saldo por factura (Plan A probable)"
|
||||||
|
: "Sin campo de saldo visible en Invoices (apunta a Plan B: total − pagos)",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function phaseMultiCompany(inventory: Map<string, Probe>): void {
|
||||||
|
const companies = inventory.get("Companies");
|
||||||
|
const companyFields: string[] = [];
|
||||||
|
for (const [res, shape] of Object.entries(report.shapes)) {
|
||||||
|
for (const f of shape.fields) {
|
||||||
|
if (/company|empresa/i.test(f.name)) companyFields.push(`${res}.${f.name}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
report.multiCompany = {
|
||||||
|
companiesEndpoint: companies ? { status: companies.status, rowCount: companies.rowCount } : "no sondeado",
|
||||||
|
companyLikeFields: companyFields,
|
||||||
|
note: "1 token = 1 usuario BIND (kickoff #22). Si Companies no existe o regresa 1 fila, el token está acotado a la empresa del usuario (Balam).",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// ─── Fases de ronda 2 (sondeos dirigidos) ─────────────────────────────
|
||||||
|
|
||||||
|
async function phasePaymentsHunt(): Promise<void> {
|
||||||
|
console.log("\n── Ronda 2 · Búsqueda del recurso de pagos");
|
||||||
|
const candidates = [
|
||||||
|
"Payment", "ClientPayments", "CustomerPayments", "Incomes", "Income",
|
||||||
|
"Deposits", "Collections", "PaymentComplements", "Complements", "CashReceipts",
|
||||||
|
"AccountsReceivable", "Receivables",
|
||||||
|
];
|
||||||
|
const found: Record<string, number> = {};
|
||||||
|
for (const c of candidates) {
|
||||||
|
const p = await probeGet(`/api/${c}?$top=1`);
|
||||||
|
found[c] = p.status;
|
||||||
|
console.log(` [${String(used).padStart(3)}/${BUDGET}] ${c.padEnd(20)} → ${p.status}`);
|
||||||
|
if (p.ok && p.rows) {
|
||||||
|
report.shapes[c] = { rowsAnalyzed: p.rows.length, totalCount: p.count, fields: analyzeRows(p.rows) };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Sub-recurso bajo factura: /api/Invoices/{id}/Payments
|
||||||
|
const inv = await probeGet("/api/Invoices?$top=1");
|
||||||
|
const invId = inv.rows?.[0] ? String((inv.rows[0] as Record<string, unknown>)["ID"] ?? "") : "";
|
||||||
|
const subProbes: Record<string, number> = {};
|
||||||
|
if (invId) {
|
||||||
|
for (const sub of ["Payments", "payments", "CreditNotes"]) {
|
||||||
|
const p = await probeGet(`/api/Invoices/${invId}/${sub}`);
|
||||||
|
subProbes[`Invoices/{id}/${sub}`] = p.status;
|
||||||
|
console.log(` [${String(used).padStart(3)}/${BUDGET}] Invoices/{id}/${sub.padEnd(12)} → ${p.status}`);
|
||||||
|
if (p.ok && p.rows?.length) {
|
||||||
|
report.shapes[`Invoices/{id}/${sub}`] = { rowsAnalyzed: p.rows.length, totalCount: p.count, fields: analyzeRows(p.rows) };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
report.paymentsHunt = {
|
||||||
|
collectionCandidates: found,
|
||||||
|
subResources: subProbes,
|
||||||
|
invoiceEmbeddedField: "Invoices.Payments es numérico (acumulado pagado) — ver shapes",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async function phaseStatusSemantics(): Promise<void> {
|
||||||
|
console.log("\n── Ronda 2 · Semántica de estatus");
|
||||||
|
// Quotes: la lista trae Status(int) + StatusText(string) en la misma fila — mapeo barato.
|
||||||
|
const quoteMap: Record<string, string> = {};
|
||||||
|
for (const code of [0, 1, 2, 3, 4, 5]) {
|
||||||
|
const p = await probeGet(`/api/Quotes?$top=1&$filter=${encodeURIComponent(`Status eq ${code}`)}`);
|
||||||
|
if (p.ok && p.rows?.length) {
|
||||||
|
const r = p.rows[0] as Record<string, unknown>;
|
||||||
|
quoteMap[String(code)] = String(r["StatusText"] ?? "(sin StatusText)");
|
||||||
|
} else if (!p.ok) {
|
||||||
|
quoteMap[String(code)] = `error ${p.status}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
console.log(` Quotes.Status → ${JSON.stringify(quoteMap)}`);
|
||||||
|
|
||||||
|
// Invoices: lista trae Status(int); el label vive en el detalle (Status string + StatusCode int).
|
||||||
|
const invoiceMap: Record<string, string> = {};
|
||||||
|
for (const code of [0, 1, 2, 3, 4, 5]) {
|
||||||
|
const list = await probeGet(`/api/Invoices?$top=1&$filter=${encodeURIComponent(`Status eq ${code}`)}`);
|
||||||
|
if (!list.ok || !list.rows?.length) continue;
|
||||||
|
const id = String((list.rows[0] as Record<string, unknown>)["ID"] ?? "");
|
||||||
|
if (!id) continue;
|
||||||
|
const det = await probeGet(`/api/Invoices/${id}`);
|
||||||
|
if (det.ok && det.rows?.length) {
|
||||||
|
const d = det.rows[0] as Record<string, unknown>;
|
||||||
|
invoiceMap[String(code)] = `${String(d["Status"] ?? "?")} (StatusCode=${String(d["StatusCode"] ?? "?")})`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
console.log(` Invoices.Status → ${JSON.stringify(invoiceMap)}`);
|
||||||
|
report.statusSemantics = {
|
||||||
|
quotesStatusToText: quoteMap,
|
||||||
|
invoicesStatusToLabel: invoiceMap,
|
||||||
|
note: "Labels vienen del propio API (campo StatusText / Status del detalle); son códigos de proceso, no datos personales.",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async function phasePrefactura(): Promise<void> {
|
||||||
|
console.log("\n── Ronda 2 · ¿Prefacturas visibles? (UUID null / IsFiscalInvoice false)");
|
||||||
|
const uuidNull = await probeGet(`/api/Invoices?$top=1&$filter=${encodeURIComponent("UUID eq null")}`);
|
||||||
|
const notFiscal = await probeGet(`/api/Invoices?$top=1&$filter=${encodeURIComponent("IsFiscalInvoice eq false")}`);
|
||||||
|
report.prefactura = {
|
||||||
|
"filter UUID eq null": { status: uuidNull.status, rows: uuidNull.rowCount },
|
||||||
|
"filter IsFiscalInvoice eq false": { status: notFiscal.status, rows: notFiscal.rowCount },
|
||||||
|
interpretation:
|
||||||
|
(uuidNull.rowCount ?? 0) > 0 || (notFiscal.rowCount ?? 0) > 0
|
||||||
|
? "Hay documentos sin timbrar visibles en /api/Invoices — prefactura distinguible vía UUID/IsFiscalInvoice"
|
||||||
|
: "Con los filtros probados no aparecieron prefacturas — posible que /api/Invoices solo exponga CFDI timbrados (validar en UI con Arturo)",
|
||||||
|
};
|
||||||
|
console.log(` UUID null → ${uuidNull.status}/${uuidNull.rowCount} filas · IsFiscalInvoice false → ${notFiscal.status}/${notFiscal.rowCount} filas`);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function phaseDetailShapes(): Promise<void> {
|
||||||
|
console.log("\n── Ronda 2 · Detalle por ID de Clients y Quotes");
|
||||||
|
for (const res of ["Clients", "Quotes"]) {
|
||||||
|
const list = await probeGet(`/api/${res}?$top=1`);
|
||||||
|
const id = list.rows?.[0] ? String((list.rows[0] as Record<string, unknown>)["ID"] ?? "") : "";
|
||||||
|
if (!id) continue;
|
||||||
|
const det = await probeGet(`/api/${res}/${id}`);
|
||||||
|
console.log(` [${String(used).padStart(3)}/${BUDGET}] ${res}/{id} → ${det.status} (${det.ok ? analyzeRows(det.rows ?? []).length : 0} campos)`);
|
||||||
|
if (det.ok && det.rows?.length) {
|
||||||
|
report.shapes[`${res}(detalle por ID)`] = {
|
||||||
|
rowsAnalyzed: det.rows.length,
|
||||||
|
totalCount: null,
|
||||||
|
fields: analyzeRows(det.rows),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function phasePagination(): Promise<void> {
|
||||||
|
console.log("\n── Ronda 2 · Paginación");
|
||||||
|
const plain = await probeGet("/api/Quotes"); // colección chica conocida; mide page size default
|
||||||
|
const top101 = await probeGet("/api/Quotes?$top=101");
|
||||||
|
report.pagination = {
|
||||||
|
"GET sin $top (Quotes)": { rows: plain.rowCount, nextLink: plain.nextLink, count: plain.count, status: plain.status },
|
||||||
|
"GET $top=101 (Quotes)": { rows: top101.rowCount, nextLink: top101.nextLink, status: top101.status },
|
||||||
|
note: "Si rows < total esperado y no hay nextLink, la paginación es por $top/$skip manual.",
|
||||||
|
};
|
||||||
|
console.log(` sin $top → ${plain.rowCount} filas (nextLink=${plain.nextLink}) · $top=101 → ${top101.rowCount} filas`);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function phaseSeriesHunt(): Promise<void> {
|
||||||
|
console.log("\n── Ronda 2 · Series de facturación");
|
||||||
|
const out: Record<string, number> = {};
|
||||||
|
for (const c of ["InvoiceSeries", "Folios", "DocumentSeries"]) {
|
||||||
|
const p = await probeGet(`/api/${c}?$top=1`);
|
||||||
|
out[c] = p.status;
|
||||||
|
if (p.ok && p.rows?.length) {
|
||||||
|
report.shapes[c] = { rowsAnalyzed: p.rows.length, totalCount: p.count, fields: analyzeRows(p.rows) };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
report.seriesHunt = out;
|
||||||
|
console.log(` ${JSON.stringify(out)}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Carga el reporte previo y purga enumValues de campos tipo monto (fuga corregida). */
|
||||||
|
function loadPreviousReport(): boolean {
|
||||||
|
try {
|
||||||
|
const prev = JSON.parse(readFileSync(join(OUT_DIR, "report.json"), "utf8")) as typeof report;
|
||||||
|
Object.assign(report, prev);
|
||||||
|
for (const shape of Object.values(report.shapes)) {
|
||||||
|
for (const f of shape.fields) {
|
||||||
|
if (f.enumValues && ENUM_EXCLUDE.test(f.name)) delete f.enumValues;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
} catch {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ─── Fases de ronda 3 (cabos sueltos) ───────────────────────────────────────
|
||||||
|
|
||||||
|
async function phaseCaps(): Promise<void> {
|
||||||
|
console.log("\n── Ronda 3 · Límites de $top y tamaño de colecciones");
|
||||||
|
const top100 = await probeGet("/api/Quotes?$top=100");
|
||||||
|
const skip100 = await probeGet("/api/Invoices?$top=1&$skip=100");
|
||||||
|
const skip1000 = await probeGet("/api/Invoices?$top=1&$skip=1000");
|
||||||
|
report.pagination = {
|
||||||
|
...(report.pagination as Record<string, unknown>),
|
||||||
|
"GET $top=100 (Quotes)": { status: top100.status, rows: top100.rowCount },
|
||||||
|
topCapVerdict: top100.ok ? "$top acepta hasta 100; 101 → 500" : `$top=100 también falla (${top100.status})`,
|
||||||
|
invoicesSizeBracket: {
|
||||||
|
"skip=100 devuelve fila": (skip100.rowCount ?? 0) > 0,
|
||||||
|
"skip=1000 devuelve fila": (skip1000.rowCount ?? 0) > 0,
|
||||||
|
note: "brackets aproximados del total de facturas históricas, sin descargar la colección",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
console.log(` $top=100 → ${top100.status} · skip100 → ${skip100.rowCount} · skip1000 → ${skip1000.rowCount}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function phaseCfdiLiterals(): Promise<void> {
|
||||||
|
console.log("\n── Ronda 3 · Literales CFDI (PPD/PUE, uso CFDI) — sanitizador corregido");
|
||||||
|
const list = await probeGet("/api/Invoices?$top=2");
|
||||||
|
const rows = list.rows ?? [];
|
||||||
|
const detailRows: unknown[] = [];
|
||||||
|
for (const r of rows) {
|
||||||
|
const id = String((r as Record<string, unknown>)["ID"] ?? "");
|
||||||
|
if (!id) continue;
|
||||||
|
const det = await probeGet(`/api/Invoices/${id}`);
|
||||||
|
if (det.ok && det.rows) detailRows.push(...det.rows);
|
||||||
|
}
|
||||||
|
if (detailRows.length) {
|
||||||
|
report.shapes["Invoices(detalle por ID)"] = {
|
||||||
|
rowsAnalyzed: detailRows.length,
|
||||||
|
totalCount: null,
|
||||||
|
fields: analyzeRows(detailRows),
|
||||||
|
};
|
||||||
|
const f = report.shapes["Invoices(detalle por ID)"].fields.find((x) => x.name === "CFDIPaymentMethod");
|
||||||
|
console.log(` CFDIPaymentMethod literales → ${JSON.stringify(f?.enumValues ?? [])}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function phasePaymentsHuntEs(): Promise<void> {
|
||||||
|
console.log("\n── Ronda 3 · Pagos: nombres en español y variantes finales");
|
||||||
|
const extra: Record<string, number> = {};
|
||||||
|
for (const c of ["Cobros", "Pagos", "InvoicePayments", "PaymentsReceived"]) {
|
||||||
|
const p = await probeGet(`/api/${c}?$top=1`);
|
||||||
|
extra[c] = p.status;
|
||||||
|
if (p.ok && p.rows?.length) {
|
||||||
|
report.shapes[c] = { rowsAnalyzed: p.rows.length, totalCount: p.count, fields: analyzeRows(p.rows) };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
report.paymentsHunt = { ...(report.paymentsHunt as Record<string, unknown>), spanishAndFinal: extra };
|
||||||
|
console.log(` ${JSON.stringify(extra)}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function phaseDocEndpoints(): Promise<void> {
|
||||||
|
console.log("\n── Ronda 3 · ¿Descarga de PDF/XML del CFDI? (solo estatus; el contenido se descarta)");
|
||||||
|
const list = await probeGet("/api/Invoices?$top=1");
|
||||||
|
const id = list.rows?.[0] ? String((list.rows[0] as Record<string, unknown>)["ID"] ?? "") : "";
|
||||||
|
const out: Record<string, unknown> = {};
|
||||||
|
if (id) {
|
||||||
|
for (const sub of ["pdf", "xml", "PDF", "cfdi"]) {
|
||||||
|
const p = await probeGet(`/api/Invoices/${id}/${sub}`);
|
||||||
|
out[`Invoices/{id}/${sub}`] = { status: p.status, contentType: p.contentType };
|
||||||
|
if (p.ok) break; // con uno confirmado basta
|
||||||
|
}
|
||||||
|
}
|
||||||
|
(report as Record<string, unknown>)["documentDownload"] = out;
|
||||||
|
console.log(` ${JSON.stringify(out)}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ─── Ronda 4: verificación aritmética del saldo (en memoria, sin persistir montos) ───
|
||||||
|
|
||||||
|
async function phaseBalanceArithmetic(): Promise<void> {
|
||||||
|
console.log("\n── Ronda 4 · Verificación del saldo: ¿Payments acumula lo pagado? (aritmética en memoria)");
|
||||||
|
const paid = await probeGet(`/api/Invoices?$top=5&$filter=${encodeURIComponent("Status eq 1")}`);
|
||||||
|
const active = await probeGet(`/api/Invoices?$top=5&$filter=${encodeURIComponent("Status eq 0")}`);
|
||||||
|
const near = (a: number, b: number) => Math.abs(a - b) < 0.01;
|
||||||
|
const summarize = (rows: unknown[]) =>
|
||||||
|
rows.map((r) => {
|
||||||
|
const o = r as Record<string, unknown>;
|
||||||
|
const total = Number(o["Total"] ?? NaN);
|
||||||
|
const pay = Number(o["Payments"] ?? NaN);
|
||||||
|
const cn = Number(o["CreditNotes"] ?? 0);
|
||||||
|
return { settled: near(pay + cn, total), residualPositive: total - pay - cn > 0.01 };
|
||||||
|
});
|
||||||
|
const paidChecks = summarize(paid.rows ?? []);
|
||||||
|
const activeChecks = summarize(active.rows ?? []);
|
||||||
|
report.balanceQuestion = {
|
||||||
|
...(report.balanceQuestion as Record<string, unknown>),
|
||||||
|
arithmetic: {
|
||||||
|
paidSample: { n: paidChecks.length, allSettled: paidChecks.every((c) => c.settled) },
|
||||||
|
activeSample: { n: activeChecks.length, allWithResidual: activeChecks.every((c) => c.residualPositive) },
|
||||||
|
formula: "SaldoPorFactura = Total − Payments − CreditNotes (campos de la MISMA fila de /api/Invoices)",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
console.log(
|
||||||
|
` pagadas: ${paidChecks.length} muestras, todas saldadas=${paidChecks.every((c) => c.settled)} · activas: ${activeChecks.length} muestras, todas con residual=${activeChecks.every((c) => c.residualPositive)}`,
|
||||||
|
);
|
||||||
|
|
||||||
|
// XML del CFDI (quedó sin probar en ronda 3 por el break temprano)
|
||||||
|
const id = paid.rows?.[0] ? String((paid.rows[0] as Record<string, unknown>)["ID"] ?? "") : "";
|
||||||
|
if (id) {
|
||||||
|
const xml = await probeGet(`/api/Invoices/${id}/xml`);
|
||||||
|
const doc = ((report as Record<string, unknown>)["documentDownload"] ?? {}) as Record<string, unknown>;
|
||||||
|
doc["Invoices/{id}/xml"] = { status: xml.status, contentType: xml.contentType };
|
||||||
|
(report as Record<string, unknown>)["documentDownload"] = doc;
|
||||||
|
console.log(` xml → ${xml.status} (${xml.contentType})`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ─── Main ───────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
async function main() {
|
||||||
|
const round2 = process.argv.includes("--round2");
|
||||||
|
const round3 = process.argv.includes("--round3");
|
||||||
|
const round4 = process.argv.includes("--round4");
|
||||||
|
console.log(`BIND API · validación técnica SOLO LECTURA${round2 ? " · RONDA 2" : round3 ? " · RONDA 3" : round4 ? " · RONDA 4" : ""}`);
|
||||||
|
console.log(`Base: ${BASE} · presupuesto: ${BUDGET} peticiones`);
|
||||||
|
if (!TOKEN) {
|
||||||
|
console.error("❌ No hay BIND_API_TOKEN en bind-api-sandbox/.env — abortando.");
|
||||||
|
process.exitCode = 1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
console.log("Token cargado desde .env (no se imprime).");
|
||||||
|
if (/localhost|127\.0\.0\.1/.test(BASE)) {
|
||||||
|
console.log("⚠️ Base URL apunta al mock local; esto NO valida producción.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (round2 || round3 || round4) {
|
||||||
|
if (!loadPreviousReport()) {
|
||||||
|
console.error("❌ --round2/--round3/--round4 requieren validation-output/report.json previo.");
|
||||||
|
process.exitCode = 1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
used = report.meta.requestsUsed; // presupuesto acumulado entre rondas
|
||||||
|
if (round2) {
|
||||||
|
await phasePaymentsHunt();
|
||||||
|
await phaseStatusSemantics();
|
||||||
|
await phasePrefactura();
|
||||||
|
await phaseDetailShapes();
|
||||||
|
await phasePagination();
|
||||||
|
await phaseSeriesHunt();
|
||||||
|
} else if (round3) {
|
||||||
|
await phaseCaps();
|
||||||
|
await phaseCfdiLiterals();
|
||||||
|
await phasePaymentsHuntEs();
|
||||||
|
await phaseDocEndpoints();
|
||||||
|
} else {
|
||||||
|
await phaseBalanceArithmetic();
|
||||||
|
}
|
||||||
|
phaseBalanceVerdict();
|
||||||
|
finish();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auth = await phaseAuth();
|
||||||
|
if (auth !== "ok") {
|
||||||
|
finish();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const inventory = await phaseInventory();
|
||||||
|
const rowsByResource = await phaseShapes(inventory);
|
||||||
|
await phaseOData(rowsByResource);
|
||||||
|
await phaseById(rowsByResource);
|
||||||
|
phaseBalanceVerdict();
|
||||||
|
phaseMultiCompany(inventory);
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
|
||||||
|
function finish() {
|
||||||
|
report.meta.requestsUsed = used;
|
||||||
|
mkdirSync(OUT_DIR, { recursive: true });
|
||||||
|
const serialized = scrub(JSON.stringify(report, null, 2));
|
||||||
|
writeFileSync(join(OUT_DIR, "report.json"), serialized, "utf8");
|
||||||
|
console.log(`\n✔ Reporte sanitizado escrito en validation-output/report.json`);
|
||||||
|
console.log(`✔ Peticiones usadas: ${used}/${BUDGET}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
main().catch((err) => {
|
||||||
|
console.error("Error fatal:", scrub(String(err?.message ?? err)));
|
||||||
|
finish();
|
||||||
|
process.exitCode = 1;
|
||||||
|
});
|
||||||
@@ -2,25 +2,67 @@
|
|||||||
|
|
||||||
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).
|
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-06 (Discovery realizado: proceso mapeado, lista blanca eliminada, cotización BIND obligatoria; token BIND en gestión por Erika — entrega esperada lun 6–mar 7 jul)._
|
_Última actualización: 2026-08-10 (Balam definió el flujo Jira→BIND en el correo del 7-ago: disparador = `En proceso de facturación`, todos los request types, monto/conceptos como campos de Jira, prueba acompañada sin `FACTEST`. Verificado por API el 10-ago: el campo `Monto sin IVA` ya existe, pero **los conceptos no tienen campo** y **10 de 69 tickets no traen datos estructurados**. Ver [REGISTRO #59](REGISTRO.md))._
|
||||||
|
|
||||||
|
## 🔥 Flujo Jira→BIND — abierto tras la definición del 7-ago
|
||||||
|
|
||||||
|
- [x] ~~🔥 **Johann — responder el correo del hilo de Jira**~~ → **Enviado el 10-ago.** Pide ticket de ejemplo, propone mié 12 / jue 13 a las 7:00 am, fija el alcance en la cotización y recuerda Azure. Texto en [REGISTRO #59](REGISTRO.md).
|
||||||
|
- [ ] 🔥 **Balam — levantar el TICKET DE EJEMPLO capturado "como debería ser"** (pedido para hoy/mañana en el correo del 10-ago). Tarea **dejada sin asignar a propósito** para que Balam la ruteé; en la práctica es de Arturo. Es el mecanismo elegido para resolver por evidencia, y no por correo, los huecos de abajo. ⚠️ **Riesgo: el precedente de latencia es de 8 días** (pregunta del 30-jul contestada el 7-ago). Si no hay respuesta para mañana al mediodía, empujar por WhatsApp con Erika, que es el canal que responde el mismo día.
|
||||||
|
- [ ] 🔥 **Balam/Noé — confirmar la sesión** (mié 12 o jue 13, 7:00 am). Sin fecha confirmada no hay prueba acompañada.
|
||||||
|
- [ ] 🔴 **Conceptos/partidas — se resuelve con el ticket de ejemplo.** El acuerdo del 4-ago resolvió el monto (`customfield_11556`) pero **no existe campo de conceptos en todo el sitio de Jira**. Si en el molde el desglose no cabe en ningún campo, hay que agregar uno; si no, la cotización va de una sola línea. **Sigue siendo el único bloqueo real para cerrar B7.**
|
||||||
|
- [ ] 🔴 **ACUNTIA — se resuelve con el segundo ticket de ejemplo.** `FAC-100` trae **un solo** `Monto sin IVA` (7,594.00 USD) para el ticket que históricamente representa 40–48 facturas. ¿Un ticket → una cotización? Ver [REGISTRO #55](REGISTRO.md) punto 6.
|
||||||
|
- [ ] ⚠️ **Tickets de `Automation for Jira` — convertido en SUPUESTO, ya no bloquea.** 10 de 69 no tienen request type ni un solo campo (incluido `FAC-91`, vivo en validación nacional). En el correo del 10-ago se asienta que **siguen tramitándose a mano** y que la automatización cubre solo los que entran por el portal. Si Balam objeta, la regla de Automation tendría que propagar los campos (trabajo de ellos). El molde capturado a mano no puede resolver esto.
|
||||||
|
- [ ] ⚠️ **Johann — decidir el destino de la cotización de la prueba en BIND.** El ejercicio escribe una **cotización real en BIND producción**. Anunciado en el correo, con dos salidas ofrecidas: cancelarla al terminar o apuntarla a un cliente de prueba. Definir cuál antes de la sesión.
|
||||||
|
- [ ] 🔴 **¿El comercial ya genera la cotización en BIND, o la genera la plataforma?** Contradicción abierta: el 6-jul Ara dijo que **el comercial la genera y la plataforma la convierte** a prefactura ([REGISTRO #27](REGISTRO.md)); el 4-ago Arturo dijo que **la plataforma la genera**; y el formulario de Jira sigue exigiendo **adjuntar la cotización**. Si ambas cosas ocurren, **quedan dos cotizaciones por ticket**. Se resuelve viendo qué adjunto pone Arturo en el ticket de ejemplo: cotización de BIND ya existente (→ convertir) o documento comercial (→ crear).
|
||||||
|
- [x] ~~**Alcance del ejercicio en vivo: ¿cotización o prefactura?**~~ → **Decidido (10-ago): termina en la COTIZACIÓN.** Es lo que contestó Arturo el 4-ago; la aprobación de Araceli va entre `En proceso de facturación` y `Facturado`, así que la prefactura pertenece después; Arturo pidió gradualismo explícito; y la cotización se cancela sin efecto fiscal mientras la prefactura es un registro en `Invoices` con `UUID = null`. La prefactura queda para la segunda sesión. Candados acordados: **cotización → prefactura → aprobación humana → timbrado** ([REGISTRO #55](REGISTRO.md)).
|
||||||
|
- [ ] ⚠️ **La transición automática depende de Azure.** En la prueba del miércoles el disparo es **manual** (pipeline `Draft→DryRun→Confirmed` de ADR-001 con flags apagados). Para que corra automática hace falta el ambiente publicado y corriendo continuo — otro argumento para insistir en el acceso a Azure.
|
||||||
|
- [ ] 🔥 **Johann — preparar el script contra el ticket de ejemplo** en cuanto Arturo lo levante, para llegar a la sesión con el flujo funcionando. Compartirlo con Balam **antes** de la sesión (protocolo del Bloque 6 de `../planeacion/Investigacion-API-Jira.md`).
|
||||||
|
- [ ] **Balam/Noé — fecha para la prueba acompañada de escritura.** Aceptado el formato el 7-ago, sin día propuesto por ellos. En el correo del 10-ago se proponen **mié 12 o jue 13-ago, 7:00 am o después de 6:00 pm** (30 min), sobre un ticket que cree Arturo, con el script compartido de antemano. **No habrá `FACTEST`.**
|
||||||
|
- [ ] **Balam — ¿nacional vs extranjero cambia la cotización** o solo el paquete de salida (PDF+XML vs PDF)? Hay dos estatus de validación y una aprobación por rama.
|
||||||
|
- [ ] **Balam/Pedro — migrar a cuenta de servicio** en vez de la cuenta personal de Pedro. Planteado el 29-jul, sin respuesta: hoy todo lo que escriba la plataforma queda firmado como Pedro y una rotación suya mata la integración.
|
||||||
|
- [ ] **Johann — levantar el sandbox Jira Cloud propio** (plan Free, proyecto JSM que replique el workflow de FAC). Con `FACTEST` descartado, es el único lugar donde se puede equivocar sin costo.
|
||||||
|
- [ ] ⚠️ **Johann — implementar la detección por changelog, no por estatus actual.** `FAC-100` estuvo **2m44s** en el estatus disparador; un poller de 15 min que consulte el estado actual se pierde la mayoría de los disparos. Refuerza el caso de pedir un webhook / regla de Automation a Pedro.
|
||||||
|
- [ ] **Johann — mover el token de Jira a user-secrets/Key Vault** y borrarlo de disco (ya está en `.gitignore:9`). Mismo pendiente que arrastra `bind_token_api.txt`.
|
||||||
|
|
||||||
## 🔴 Johann (proveedor) — inmediato
|
## 🔴 Johann (proveedor) — inmediato
|
||||||
|
|
||||||
- [x] ~~**Asistir al kickoff con Noé (mié 1-jul, 7:00 am)**~~ → **Hecho (1-jul).** Ver [REGISTRO #22](REGISTRO.md).
|
- [x] ~~**Asistir al kickoff con Noé (mié 1-jul, 7:00 am)**~~ → **Hecho (1-jul).** Ver [REGISTRO #22](REGISTRO.md).
|
||||||
- [x] ~~📅 **Asistir a la sesión "Proceso actual de facturación y cobranza" (lun 6-jul, 7:00 am)**~~ → **Hecha (6-jul).** Proceso mapeado end-to-end; lista blanca eliminada; cotización BIND obligatoria. Ver [REGISTRO #27](REGISTRO.md).
|
- [x] ~~📅 **Asistir a la sesión "Proceso actual de facturación y cobranza" (lun 6-jul, 7:00 am)**~~ → **Hecha (6-jul).** Proceso mapeado end-to-end; lista blanca eliminada; cotización BIND obligatoria. Ver [REGISTRO #27](REGISTRO.md).
|
||||||
- [ ] 🎨 **Trabajar el prototipo (Etapa 0)** reflejando el flujo real levantado el 6-jul: Jira (disparador) → cotización BIND → prefactura → validación humana → CFDI → envío. Incluir las validaciones de oro: **PPD default** (PUE consciente — ya les costó un requerimiento del SAT), **IVA 16% MXN / 0% extranjero** (BIND no lo automatiza), prefactura siempre antes de timbrar, alta de cliente como flujo aparte. Ver [REGISTRO #27](REGISTRO.md).
|
- [x] ~~🎨 **Preparar y entregar el prototipo de la Etapa 0**~~ → **Entregado por correo (10-jul)** a Erika + Noé, CC Pedro, con PDF y versión navegable. Refleja facturación, envío, cobranza y reglas del Discovery. Ver [REGISTRO #40](REGISTRO.md).
|
||||||
- [ ] Confirmar con Erika si la sesión del **martes 7-jul** sigue en pie — probablemente ya no es necesaria (las dudas quedaron resueltas el lunes). Ver [REGISTRO #27](REGISTRO.md).
|
- [x] ~~📧 **Enviar por correo el prototipo (imágenes) + comentarios**~~ → **Hecho (10-jul).** Balam respondió que lo revisaría internamente.
|
||||||
- [ ] ⚠️ **Aclarar expectativa de recordatorios a clientes:** lo que Ara/Arturo describieron el 6-jul son recordatorios automáticos **a clientes**, que están **diferidos al Anexo B** (el MVP trae alertas *internas*). Aclararlo pronto o anticipar que pidan ese módulo al cierre del MVP. Igual con la idea de **agentes observando Jira** (Nivel 3, fase posterior) y el deseo **multi-empresa** (Regiotour, Elmstone). Ver [REGISTRO #27](REGISTRO.md).
|
- [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.
|
||||||
- [x] ~~**Ajustar la propuesta** (30 h explícitas, sin "Etapa 0", términos comerciales en una sección)~~ → **Hecho (1-jul):** propuesta **v1.2** — se agregó el concepto **"Facturación inicial: 30 h / $18,000 + IVA"** (cubre Etapa 0 + inicio de Etapa 1; Etapa 0 se mantiene en 18–22 h) y se consolidó **§3.3 Condiciones comerciales**. Fuente MD actualizada; **falta regenerar el PDF** (lo hace el agente de PDF de Johann con el mismo cambio).
|
- [x] ~~📅 **Preparar y asistir a la sesión de reglas/dudas de Etapa 1 (mié 22-jul)**~~ → **Hecha (22-jul, 7 am):** alta de cliente/proveedor mapeada (Arturo en pantalla), fee = lo registra el despacho (plataforma solo detecta), Jira confirmado en el proceso (estatus final "resuelto"; validación PDF+XML nacional / PDF internacional). No se tocaron particularidades de envío ni PUE/PPD. Ver [REGISTRO #54](REGISTRO.md) y minuta en `../planeacion/Sesion-Etapa1-2026-07-22.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. **Aprovechar para meter la pregunta PUE/PPD que no se alcanzó el 22-jul.** Ver [REGISTRO #42](REGISTRO.md).
|
||||||
|
- [x] ~~📊 **Compartir con Erika el corte de avance de la Etapa 1 solicitado el 16-jul**~~ → **Hecho (16-jul, 8:40 pm):** Excel `Plan-actividades-avance-2026-07-16.xlsx` enviado por WhatsApp; Erika acusó recibo ("Ntp, gracias"). 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).
|
||||||
|
- [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: ¿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. **No se alcanzó a preguntar en la sesión del 22-jul** — reintentar el 23-jul (validación del prototipo) o en la sesión de API Jira con Pedro. Ver [REGISTRO #52](REGISTRO.md), [#54](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).
|
||||||
|
- [ ] 📊 **Exceles de cobranza (condicional):** si Balam valida que el prototipo representa bien su operación, ya no se requieren. Si pide ajustes, solicitar a Arturo estructura con datos ficticios de abiertas/canceladas/días vencidos, pago↔folio y estado de cuenta. **No confundir con el Excel de particularidades de envío**, que sigue pendiente. Ver [REGISTRO #44](REGISTRO.md).
|
||||||
|
- [ ] 🔑 **Dar seguimiento a si Pedro crea el usuario de solo lectura de BIND** que pidió Noé (mientras tanto Erika opera con el usuario de Arturo, sin bloqueo) — sigue sin resolverse una semana después (7-jul). Ver [REGISTRO #37](REGISTRO.md).
|
||||||
|
- [x] ~~🔧 **Decidir si probar endpoints de escritura (POST/PUT) de BIND**~~ → **Decidido (7-jul): NO todavía.** Contradice la instrucción de Noé de mantener solo-lectura mientras Pedro confirma el alcance del token; sin sandbox, el riesgo fiscal es real. Alternativa segura: catalogar operaciones de escritura desde el **portal de desarrolladores de BIND** (documentación), no probarlas en vivo. Ver [REGISTRO #36](REGISTRO.md).
|
||||||
|
- [x] ~~📧 **Responder el correo de Pedro**~~ → **Enviado (6-jul, ~3:28 pm):** acuse de recibo + usuario de Arturo documentado + arranque de trabajo con la API, sin comprometer entregas adicionales. Ver [REGISTRO #29](REGISTRO.md).
|
||||||
|
- [ ] 📧⚠️ **Responder el correo de Noé (importancia alta, 4:33 pm)** describiendo las **salvaguardas** ya operando: solo-lectura bloqueado en código, consultas acotadas, token en gestor de secretos fuera del repo, sin datos reales en documentos, escritura solo con autorización + candados de la propuesta. Borrador listo. **Confirmación informal ya dada por WhatsApp (6-jul, 5:01 pm) — falta la respuesta formal por correo para dejar constancia en el canal correcto.** Ver [REGISTRO #31](REGISTRO.md), [#33](REGISTRO.md).
|
||||||
|
- [ ] ⚠️ **No atar nada al token actual:** Pedro está revisando permisos y puede **reemplazarlo** por uno de un usuario nuevo de solo lectura — mantener el token como variable de entorno intercambiable (ya es así en el sandbox). Ver [REGISTRO #31](REGISTRO.md).
|
||||||
|
- [x] ~~🔑 **Validar la API de BIND con el token real**~~ → ✅ **Hecha (6-jul, adelantada un día):** 117 peticiones GET-only, Plan A del saldo CONFIRMADO, flujo MVP 100% sostenible en lectura, PDF por API. Hallazgo duro: **sin recurso de pagos/REP**. Documento: [`../bind-api-sandbox/VALIDACION-API.md`](../bind-api-sandbox/VALIDACION-API.md). Ver [REGISTRO #32](REGISTRO.md).
|
||||||
|
- [ ] 📨 **Escalar a Pedro las preguntas técnicas de la API** (juntas, esta semana): (1) ¿existe endpoint de **pagos individuales / complementos de pago (REP)** no documentado? — es el hueco más relevante para el flujo PPD, _y ganó caso de negocio el 7-jul: el despacho registra los pagos en BIND uno por uno a mano_; (2) **tabla de mapeo `CFDIUse`** interno → clave SAT; (3) shape del endpoint `/{id}/xml`; (4) ¿webhooks/eventos o solo polling?; (5) ⚠️ **qué token alimenta el Power BI** — el 7-jul Arturo mostró su Power BI conectado con **su** token, pero el kickoff acordó Ara=Power BI / Arturo=desarrollo; BIND emite 1 token por usuario, así que si ambos usos comparten el de Arturo, reemplazarlo por el usuario solo-lectura que planteó Noé rompería uno de los dos. Ver [REGISTRO #32](REGISTRO.md) §10, [#35](REGISTRO.md).
|
||||||
|
- [ ] 🔐 **Higiene del token:** moverlo a un gestor de secretos y **borrar `bind_token_api.txt`** de la raíz del repo y del correo/descargas (está gitignoreado, pero sigue en disco).
|
||||||
|
- [ ] ⚠️ **Aclarar expectativa de recordatorios a clientes:** lo que Ara/Arturo describieron el 6-jul son recordatorios automáticos **a clientes**, que están **diferidos al Anexo B** (el MVP trae alertas *internas*). Aclararlo pronto o anticipar que pidan ese módulo al cierre del MVP. Igual con la idea de **agentes observando Jira** (Nivel 3, fase posterior) y el deseo **multi-empresa** (Regiotour, Elmstone). **Reapareció el 7-jul:** quieren correos automáticos al cliente pasados 1–5 días de vencimiento ("ser proactivos"). Ver [REGISTRO #27](REGISTRO.md), [#35](REGISTRO.md).
|
||||||
|
- [x] ~~**Ajustar la propuesta** (30 h explícitas, sin "Etapa 0", términos comerciales en una sección)~~ → **Hecho (1-jul):** propuesta **v1.2** — se agregó el concepto **"Facturación inicial: 30 h / $18,000 + IVA"** (cubre Etapa 0 + inicio de Etapa 1; Etapa 0 se mantiene en 18–22 h) y se consolidó **§3.3 Condiciones comerciales**. PDF generado y enviado el 2-jul.
|
||||||
- [x] ~~📧 **Enviar el correo** (Noé, CC Pedro, Ara, Erika) con la propuesta v1.2 ajustada, vinculando la **facturación inicial de 30 h** al acuerdo~~ → **Hecho (2-jul, 5:39 PM):** enviado con `Propuesta-Balam.pdf` adjunto, sin esperar el correo de Erika+Paola. Ver [REGISTRO #26](REGISTRO.md).
|
- [x] ~~📧 **Enviar el correo** (Noé, CC Pedro, Ara, Erika) con la propuesta v1.2 ajustada, vinculando la **facturación inicial de 30 h** al acuerdo~~ → **Hecho (2-jul, 5:39 PM):** enviado con `Propuesta-Balam.pdf` adjunto, sin esperar el correo de Erika+Paola. Ver [REGISTRO #26](REGISTRO.md).
|
||||||
- [ ] 💸 **NO facturar** hasta que Balam confirme por correo el envío del 2-jul (Noé quiere que la factura quede vinculada al documento). Tras el OK, **emitir la factura** de las 30 h. Ver [REGISTRO #22](REGISTRO.md), [#26](REGISTRO.md).
|
- [ ] 💸 **NO facturar todavía.** Erika confirmó el 13-jul que recibió la propuesta v1.2; la validación para emitir sigue pendiente porque Noé y la CEO están fuera del país. Mantener la factura lista y emitir en cuanto llegue el visto bueno, con pago a 30 días. Ver [REGISTRO #41](REGISTRO.md).
|
||||||
- [ ] **Configurar Jira y aprender el flujo con Pedro** — reportar horas ahí (incluye material informativo); Erika hace el corte los lunes.
|
- [ ] **Configurar Jira y aprender el flujo con Pedro** — reportar horas ahí (incluye material informativo); Erika hace el corte los lunes. Mientras tanto usar `../planeacion/Seguimiento-horas.csv`.
|
||||||
- [ ] Usar los **tokens de marca** ([`../marca/Marca-Balam.md`](../marca/Marca-Balam.md): Amarillo `#F7BD0C` + Café `#331F0E` + Poppins) en el **prototipo** de la Etapa 0.
|
- [x] ~~Usar los **tokens de marca** en el prototipo~~ → **Hecho:** prototipo y capturas usan Amarillo `#F7BD0C`, Café `#331F0E` y Poppins.
|
||||||
- [x] ~~Preparar el Excel de actividades~~ → **Entregado:** Etapa 0 y 1 (29-jun) y **completo, 4 etapas (0–3)** con fechas tentativas (30-jun). En `../planeacion/Plan-actividades.xlsx`.
|
- [x] ~~Preparar el Excel de actividades~~ → **Entregado y consolidado:** la única versión operativa se conserva en `../planeacion/Plan-actividades-avance-2026-07-10-ajustado.xlsx`; contiene el corte reconstruido de 30 h al 15-jul.
|
||||||
- [x] ~~Proponer sesiones de Discovery~~ → **Hecho (29-jun):** propuestas y aceptadas; **Erika coordina las agendas** (intermediaria de sesiones).
|
- [x] ~~Proponer sesiones de Discovery~~ → **Hecho (29-jun):** propuestas y aceptadas; **Erika coordina las agendas** (intermediaria de sesiones).
|
||||||
- [ ] Cambiar régimen fiscal (en proceso) para poder facturar (CFDI semanal los viernes, pago a 30 días).
|
- [ ] Cambiar régimen fiscal (en proceso) para poder facturar (CFDI semanal los viernes, pago a 30 días).
|
||||||
- [ ] Confirmar **qué permisos exactos de Azure** necesita (crear App Service + PostgreSQL; no Global Admin) — coordinar con Pedro/Noé, que ahora gestionan Azure.
|
- [x] ~~Confirmar **qué permisos exactos de Azure** necesita~~ → **Definido:** acceso `Contributor` acotado a un grupo de recursos de Balam; no se requiere Global Admin ni una cuenta nueva. Solicitar durante la semana del 21-jul.
|
||||||
- [ ] **Arrancar el Discovery** una vez Balam entregue los accesos (el contrato ya está firmado).
|
- [x] ~~**Arrancar el Discovery** una vez Balam entregue los accesos~~ → **Hecho (6–7 jul):** facturación, envío y cobranza mapeados.
|
||||||
- [ ] (Opcional) Pedir a Balam **copia limpia del contrato**: la cláusula de Firma Electrónica de la última página quedó duplicada y aún dice "EL PATRÓN" (residuo de plantilla, bajo riesgo). Ver [REGISTRO #19](REGISTRO.md).
|
- [ ] (Opcional) Pedir a Balam **copia limpia del contrato**: la cláusula de Firma Electrónica de la última página quedó duplicada y aún dice "EL PATRÓN" (residuo de plantilla, bajo riesgo). Ver [REGISTRO #19](REGISTRO.md).
|
||||||
- [x] ~~Revisar y firmar el contrato de servicios~~ → **Hecho (26-jun):** revisado, negociados 2 ajustes (pago de horas al terminar + aceptación a 10 días) y **FIRMADO**. Ver [REGISTRO #18](REGISTRO.md), [#19](REGISTRO.md).
|
- [x] ~~Revisar y firmar el contrato de servicios~~ → **Hecho (26-jun):** revisado, negociados 2 ajustes (pago de horas al terminar + aceptación a 10 días) y **FIRMADO**. Ver [REGISTRO #18](REGISTRO.md), [#19](REGISTRO.md).
|
||||||
- [x] ~~Responder el correo de Noe (8-jun)~~ → **Hecho (10-jun):** aceptados los 4 ajustes y respondidas las 3 preguntas técnicas. Ver [REGISTRO #16](REGISTRO.md).
|
- [x] ~~Responder el correo de Noe (8-jun)~~ → **Hecho (10-jun):** aceptados los 4 ajustes y respondidas las 3 preguntas técnicas. Ver [REGISTRO #16](REGISTRO.md).
|
||||||
@@ -35,19 +77,22 @@ _Última actualización: 2026-07-06 (Discovery realizado: proceso mapeado, lista
|
|||||||
## 🟡 Balam
|
## 🟡 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).
|
- [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: entregar los ACCESOS para arrancar** — **token de Arturo** (BIND), **repositorio GitHub privado** (lo crea Pedro), **Azure** (Pedro+Noé), reglas de negocio + bancos (Arturo). **Es el bloqueador para iniciar el Discovery.**
|
- [ ] 🔑 **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] ~~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)).
|
- [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).
|
- [ ] **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] ~~**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).
|
||||||
- [ ] **Erika/Pedro:** entregar el **token de BIND** (del usuario de **Arturo**, cuenta mayor / todos los permisos); dejar el de **Ara** para el Power BI. **SOLICITADO internamente por Erika (6-jul, 10:26 am)** tras confirmar con Johann que solo se necesita el token; entrega esperada **martes 7-jul temprano** para la validación técnica (mar–mié del plan). Al recibirlo: **confirmar de qué usuario salió** (documentar cuál llave es para qué) y tratarlo como llave de **producción**. Ver [REGISTRO #28](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).
|
||||||
- [ ] **Pedro:** crear el **repositorio GitHub privado** para backend/frontend (Fase 1–2).
|
- [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:** 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:** 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).
|
- [ ] **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).
|
- [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).
|
||||||
- [ ] 📊 **Ara + Arturo:** enviar el **Excel de particularidades de envío por cliente** (destinatarios + adjuntos + nomenclatura de asunto; ej. CEMEX = Excel de horas con visto bueno + nomenclatura; Acuntia = .zip + estado de cuenta). **Se comprometieron a mandarlo hoy 6-jul.** Ver [REGISTRO #27](REGISTRO.md).
|
- [ ] 📊 **Ara + Arturo:** enviar el **Excel de particularidades de envío por cliente** (destinatarios + adjuntos + nomenclatura). Erika confirmó el 13-jul que continúa en preparación y que esperan entregarlo durante la semana. Ver [REGISTRO #41](REGISTRO.md).
|
||||||
- [ ] **Arturo:** definir el proceso para cuando el **cliente NO esté dado de alta en BIND** (alta de cliente como flujo aparte de la automatización) — pregunta que él mismo dejó abierta el 6-jul. Ver [REGISTRO #27](REGISTRO.md).
|
- [ ] **Arturo:** definir el proceso para cuando el **cliente NO esté dado de alta en BIND** — el ASIS del alta ya quedó mapeado en la sesión del 22-jul ([REGISTRO #54](REGISTRO.md)); **sigue abierta la decisión de MVP: ¿la plataforma solo detecta que falta el cliente o también lo crea?** (cruza con la estimación Jira→BIND de ~10 h). Ver [REGISTRO #27](REGISTRO.md), [#51](REGISTRO.md).
|
||||||
- [ ] **Erika + Paola:** revisar si el **contrato ya vincula las 30 h** de la Etapa 0; Johann ya se adelantó y envió la propuesta v1.2 ajustada (2-jul) — pendiente que Balam confirme por correo para que pueda facturar. Ver [REGISTRO #26](REGISTRO.md).
|
- [ ] 📤 **Arturo/Pedro:** enviar a Johann el **diagrama del flujo Jira de facturación** en tamaño legible (comprometido en la sesión del 22-jul; el screenshot era ilegible). Ver [REGISTRO #54](REGISTRO.md).
|
||||||
|
- [ ] 📅 **Erika:** agendar la **sesión con Pedro (+Arturo)** para explorar la **API de Jira** (conexión, campos, webhook), la **facturación recurrente** (~30 facturas/mes) y qué facturas son automáticas vs manuales — acordada en la sesión del 22-jul. Ver [REGISTRO #54](REGISTRO.md).
|
||||||
|
- [ ] **Erika + Paola / Noé:** confirmar la propuesta v1.2 para emitir las 30 h. Erika confirmó recepción; validación pendiente por viaje de Noé y CEO. Ver [REGISTRO #41](REGISTRO.md).
|
||||||
- [ ] **Arturo:** dar acceso/contexto de **bancos** (para conciliación). _Las reglas de negocio de facturación ya quedaron mapeadas en la sesión del 6-jul ([REGISTRO #27](REGISTRO.md)); bancos sigue pendiente._
|
- [ ] **Arturo:** dar acceso/contexto de **bancos** (para conciliación). _Las reglas de negocio de facturación ya quedaron mapeadas en la sesión del 6-jul ([REGISTRO #27](REGISTRO.md)); bancos sigue pendiente._
|
||||||
|
|
||||||
## ⚙️ Acordado (referencia, ya cerrado)
|
## ⚙️ Acordado (referencia, ya cerrado)
|
||||||
|
|||||||
@@ -8,6 +8,8 @@ Esta carpeta es el **registro vivo** del proyecto Balam. Cada vez que pase algo
|
|||||||
|---|---|
|
|---|---|
|
||||||
| [REGISTRO.md](REGISTRO.md) | **Log cronológico** de toda comunicación (correos, llamadas, mensajes). Entradas numeradas, más antigua arriba. |
|
| [REGISTRO.md](REGISTRO.md) | **Log cronológico** de toda comunicación (correos, llamadas, mensajes). Entradas numeradas, más antigua arriba. |
|
||||||
| [PENDIENTES.md](PENDIENTES.md) | **Acciones abiertas** (checklist). Lo que hay que hacer y quién. |
|
| [PENDIENTES.md](PENDIENTES.md) | **Acciones abiertas** (checklist). Lo que hay que hacer y quién. |
|
||||||
|
| [`../planeacion/Seguimiento-horas.md`](../planeacion/Seguimiento-horas.md) | Reglas, resumen y reconstrucción pendiente de horas efectivamente trabajadas. |
|
||||||
|
| [`../planeacion/Seguimiento-horas.csv`](../planeacion/Seguimiento-horas.csv) | Fuente tabular para conciliación con Jira y facturación. |
|
||||||
| `../fuentes/` | **Material crudo**: transcripciones, archivos `.eml`, PRD. No se edita; es evidencia. |
|
| `../fuentes/` | **Material crudo**: transcripciones, archivos `.eml`, PRD. No se edita; es evidencia. |
|
||||||
| `../README.md` | **Estado del proyecto** (resumen ejecutivo, datos clave, decisiones). Se actualiza cuando algo cambia el rumbo. |
|
| `../README.md` | **Estado del proyecto** (resumen ejecutivo, datos clave, decisiones). Se actualiza cuando algo cambia el rumbo. |
|
||||||
|
|
||||||
@@ -18,6 +20,7 @@ Esta carpeta es el **registro vivo** del proyecto Balam. Cada vez que pase algo
|
|||||||
3. **Registra la entrada** en [REGISTRO.md](REGISTRO.md) con la plantilla que corresponda (ver abajo). Usa el siguiente número consecutivo.
|
3. **Registra la entrada** en [REGISTRO.md](REGISTRO.md) con la plantilla que corresponda (ver abajo). Usa el siguiente número consecutivo.
|
||||||
4. **Mueve los pendientes** que surjan a [PENDIENTES.md](PENDIENTES.md).
|
4. **Mueve los pendientes** que surjan a [PENDIENTES.md](PENDIENTES.md).
|
||||||
5. Si cambió el **alcance, precio, plazo o una decisión clave**, actualiza también `../README.md`.
|
5. Si cambió el **alcance, precio, plazo o una decisión clave**, actualiza también `../README.md`.
|
||||||
|
6. Si hubo trabajo facturable, registra el tiempo real el mismo día en `../planeacion/Seguimiento-horas.csv`; no infieras horas desde el porcentaje de avance.
|
||||||
|
|
||||||
> Regla de oro: si no está en la bitácora, no pasó. Registrar toma 2 minutos y evita malentendidos caros.
|
> Regla de oro: si no está en la bitácora, no pasó. Registrar toma 2 minutos y evita malentendidos caros.
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
- **Pedro Alberto Ayala Elizondo** — Desarrollador / contacto técnico, Balam — `pedro.ayala@balamtalentoestrategico.com`
|
- **Pedro Alberto Ayala Elizondo** — Desarrollador / contacto técnico, Balam — `pedro.ayala@balamtalentoestrategico.com`
|
||||||
- **Paola** — Recursos Humanos, Balam — coordinó la firma del contrato (WhatsApp)
|
- **Paola** — Recursos Humanos, Balam — coordinó la firma del contrato (WhatsApp)
|
||||||
|
|
||||||
**Periodo:** 30 abr 2026 → 6 jul 2026 (última actualización: sesión de Discovery del proceso de facturación, 6-jul)
|
**Periodo:** 30 abr 2026 → 22 jul 2026 (última actualización: sesión de reglas Etapa 1 — alta de clientes, fee y flujo Jira, 22-jul)
|
||||||
**Orden:** cronológico (más antiguo arriba)
|
**Orden:** cronológico (más antiguo arriba)
|
||||||
|
|
||||||
---
|
---
|
||||||
@@ -386,7 +386,7 @@ Erika (PM, contacto principal) confirma que **ya pasaron los temas administrativ
|
|||||||
|
|
||||||
## 21 · Jun 29–30, 2026 — WhatsApp Johann ↔ Erika · plan de actividades entregado + kickoff agendado ⭐
|
## 21 · Jun 29–30, 2026 — WhatsApp Johann ↔ Erika · plan de actividades entregado + kickoff agendado ⭐
|
||||||
|
|
||||||
> Evidencia: `../fuentes/2026-06-29 - WhatsApp - Erika arranque del plan de actividades.md`. Plan: `../planeacion/Plan-actividades.xlsx` (+ `.md`).
|
> Evidencia: `../fuentes/2026-06-29 - WhatsApp - Erika arranque del plan de actividades.md`. Plan vigente consolidado: `../planeacion/Plan-actividades-avance-2026-07-10-ajustado.xlsx` (+ `.md`).
|
||||||
|
|
||||||
- Johann entrega el **plan de actividades** (Excel sencillo: actividad · fecha inicio–fin · responsable · apoyo de Balam): primero Etapa 0 y 1 (29-jun) y luego **enviado completo, las 4 etapas (0–3)** con fechas tentativas (**30-jun, 12:34**). Las sesiones quedan ubicadas por etapa; la Etapa 0–1 se mantiene idéntica a lo enviado el 29-jun.
|
- Johann entrega el **plan de actividades** (Excel sencillo: actividad · fecha inicio–fin · responsable · apoyo de Balam): primero Etapa 0 y 1 (29-jun) y luego **enviado completo, las 4 etapas (0–3)** con fechas tentativas (**30-jun, 12:34**). Las sesiones quedan ubicadas por etapa; la Etapa 0–1 se mantiene idéntica a lo enviado el 29-jun.
|
||||||
- **Erika confirma que será la intermediaria** de todas las sesiones ("lo que necesites me lo pides y yo coordino agendas").
|
- **Erika confirma que será la intermediaria** de todas las sesiones ("lo que necesites me lo pides y yo coordino agendas").
|
||||||
@@ -504,7 +504,7 @@ Johann envía la **propuesta v1.2** (30 h de facturación inicial explícitas, c
|
|||||||
|
|
||||||
> Participan: **Araceli Sánchez** (comercial/dirección), **Arturo Rosas Hernández** (administración/facturación), Erika Chávez y Johann. Noé era opcional y no asistió. Canal: Microsoft Teams (~1 h 5 min). Evidencia: `../fuentes/2026-07-06 Proceso actual de facturación y cobranz_ Transcript.txt`.
|
> Participan: **Araceli Sánchez** (comercial/dirección), **Arturo Rosas Hernández** (administración/facturación), Erika Chávez y Johann. Noé era opcional y no asistió. Canal: Microsoft Teams (~1 h 5 min). Evidencia: `../fuentes/2026-07-06 Proceso actual de facturación y cobranz_ Transcript.txt`.
|
||||||
|
|
||||||
**Resumen:** Primera sesión de Discovery (Etapa 0). Araceli y Arturo muestran en vivo el proceso completo de facturación: entrada por **Jira ITSM** (obligatoria desde hace ~1 mes) → validación de administración → **prefactura en BIND** → emisión de CFDI → **envío por correo con particularidades por cliente**. Se demostró el flujo real en BIND (crearon y cancelaron una prefactura en vivo). Dos reglas de negocio cambiaron en la propia sesión.
|
**Resumen:** Primera sesión de Discovery (Etapa 0). Araceli y Arturo muestran en vivo el proceso completo de **facturación**: entrada por **Jira ITSM** (obligatoria desde hace ~1 mes) → validación de administración → **prefactura en BIND** → emisión de CFDI → **envío por correo con particularidades por cliente**. Se demostró el flujo real en BIND (crearon y cancelaron una prefactura en vivo). Dos reglas de negocio cambiaron en la propia sesión. **La parte de cobranza NO se cubrió** — solo se tocaron la lista blanca y la idea de recordatorios; el proceso de seguimiento de pagos queda pendiente de mapear (cuando Johann preguntó si después seguía cobranza, Ara redirigió al proceso de envío de factura).
|
||||||
|
|
||||||
### El proceso actual, paso a paso
|
### El proceso actual, paso a paso
|
||||||
|
|
||||||
@@ -552,12 +552,12 @@ Johann envía la **propuesta v1.2** (30 h de facturación inicial explícitas, c
|
|||||||
> - **Jira como disparador del flujo** (agentes observando tickets/botones) se parece al "Nivel 3" (generación desde eventos) que estaba en fase posterior. La integración con la API de Jira no está cotizada en el MVP — vigilar ese límite de alcance cuando se diseñe el prototipo.
|
> - **Jira como disparador del flujo** (agentes observando tickets/botones) se parece al "Nivel 3" (generación desde eventos) que estaba en fase posterior. La integración con la API de Jira no está cotizada en el MVP — vigilar ese límite de alcance cuando se diseñe el prototipo.
|
||||||
> - **Se levantaron reglas de validación de oro para la plataforma:** PPD por default (PUE solo consciente — ya les costó un requerimiento del SAT) · IVA 16% MXN / 0% extranjero-sin efectos fiscales (BIND no lo automatiza) · prefactura SIEMPRE antes de timbrar · OC/cotización como prerequisito · alta de cliente como flujo aparte.
|
> - **Se levantaron reglas de validación de oro para la plataforma:** PPD por default (PUE solo consciente — ya les costó un requerimiento del SAT) · IVA 16% MXN / 0% extranjero-sin efectos fiscales (BIND no lo automatiza) · prefactura SIEMPRE antes de timbrar · OC/cotización como prerequisito · alta de cliente como flujo aparte.
|
||||||
> - **El token de BIND NO se tocó en la sesión** — sigue abierto desde el kickoff y el WhatsApp del 2-jul ([#25](#25--jul-2-2026--418441-pm--whatsapp-erika--johann--agendamateriales-para-la-sesión-del-6-jul)).
|
> - **El token de BIND NO se tocó en la sesión** — sigue abierto desde el kickoff y el WhatsApp del 2-jul ([#25](#25--jul-2-2026--418441-pm--whatsapp-erika--johann--agendamateriales-para-la-sesión-del-6-jul)).
|
||||||
> - La sesión del **martes 7-jul** probablemente ya no sea necesaria (Johann cerró diciendo que sus dudas quedaron resueltas) — confirmar con Erika.
|
> - **La cobranza quedó sin mapear** — la sesión del **martes 7-jul** (que Erika mencionó desde el 1-jul) sería el espacio natural para cubrirla: cómo dan seguimiento hoy a los pagos, quién persigue morosos, cómo concilian cuentas por cobrar, y de dónde saldría el aging. Proponérselo a Erika.
|
||||||
> - **Multi-empresa** (Regiotour, Elmstone) aparece como deseo de futuro de Ara — anotarlo para el roadmap, no para el MVP.
|
> - **Multi-empresa** (Regiotour, Elmstone) aparece como deseo de futuro de Ara — anotarlo para el roadmap, no para el MVP.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 28 · Jul 6, 2026 — 9:01–10:28 AM · WhatsApp Erika ↔ Johann · seguimiento al token de BIND ⭐
|
## 28 · Jul 6, 2026 — 9:01 AM–12:40 PM · WhatsApp Erika ↔ Johann · token de BIND ENTREGADO ⭐
|
||||||
|
|
||||||
> Evidencia: `../fuentes/2026-07-06 - WhatsApp - Erika sesion discovery y seguimiento token BIND.md`.
|
> Evidencia: `../fuentes/2026-07-06 - WhatsApp - Erika sesion discovery y seguimiento token BIND.md`.
|
||||||
|
|
||||||
@@ -565,12 +565,622 @@ Tras la sesión de Discovery (7–8 am, [#27](#27--jul-6-2026--700-am--llamada--
|
|||||||
|
|
||||||
- **9:01** — Sobre *"Entrega y validación de accesos (BIND, Azure, manual de marca) · lun 06 → mar 07 jul"*: *"¿solo necesitas el token tal cual?"* **Johann (9:53–9:54):** *"Sí, para BIND solo necesito el token tal cual. Lo de Azure no me urge hoy y el manual de marca ya lo tengo."*
|
- **9:01** — Sobre *"Entrega y validación de accesos (BIND, Azure, manual de marca) · lun 06 → mar 07 jul"*: *"¿solo necesitas el token tal cual?"* **Johann (9:53–9:54):** *"Sí, para BIND solo necesito el token tal cual. Lo de Azure no me urge hoy y el manual de marca ya lo tengo."*
|
||||||
- **10:26** — Erika: *"okey deja lo solicito"* → **el token queda formalmente solicitado dentro de Balam.**
|
- **10:26** — Erika: *"okey deja lo solicito"* → **el token queda formalmente solicitado dentro de Balam.**
|
||||||
- **10:28** — Sobre *"Validación técnica de la API de BIND con la cuenta real · mar 07 → mié 08 jul"*: *"¿se necesita una sesión?"* **Respuesta de Johann:** no — es trabajo propio con el token en mano (probar endpoints y compartir resumen de hallazgos); solo si algo no cuadra (permisos/cobertura) pediría 15–20 min con Pedro o Arturo. Pide el token **martes temprano** para cumplir las fechas del plan.
|
- **10:28–10:37** — Sobre *"Validación técnica de la API de BIND con la cuenta real · mar 07 → mié 08 jul"*: *"¿se necesita una sesión?"* **Johann (10:37):** *"No, esa validación la hago yo por mi cuenta ya teniendo el token."*
|
||||||
|
- **12:10–12:19** — **Token GENERADO** dentro de Balam: *"ya solicitaran el token ahorita te lo comparto"*. Erika desconoce si tiene vencimiento (según el kickoff: no caduca), comenta que lo tienen en un **.txt** y pregunta si sirve **por correo**. Johann (12:25): sí, por correo y .txt está bien; (12:27) pide que le confirmen **a qué usuario pertenece** el token para documentarlo.
|
||||||
|
- **12:40** — Erika: *"ya se compartió por correo"* → ✅ **TOKEN DE BIND ENTREGADO.**
|
||||||
|
|
||||||
> **Lectura estratégica:**
|
> **Lectura estratégica:**
|
||||||
> - **El bloqueador del token por fin tiene tracción:** Erika lo gestiona activamente contra el plan de actividades y ya lo **solicitó internamente** (10:26). Primera respuesta real al seguimiento que Johann venía haciendo desde el 2-jul ([#25](#25--jul-2-2026--418441-pm--whatsapp-erika--johann--agendamateriales-para-la-sesión-del-6-jul)).
|
> - **El bloqueador del token se resolvió el mismo día:** Johann lo empujó a las 9:01 vía el plan de actividades, Erika lo solicitó a las 10:26 y estaba entregado a las 12:40 — un día antes de la fecha del plan (mar 7-jul). Cierra el seguimiento que venía desde el 2-jul ([#25](#25--jul-2-2026--418441-pm--whatsapp-erika--johann--agendamateriales-para-la-sesión-del-6-jul)).
|
||||||
> - Que Erika trabaje sobre el plan de actividades confirma que **el Excel de Johann es el instrumento real de seguimiento** — mantenerlo al día paga.
|
> - Que Erika trabaje sobre el plan de actividades confirma que **el Excel de Johann es el instrumento real de seguimiento** — mantenerlo al día paga.
|
||||||
> - Pendiente de higiene: al recibir el token, **confirmar de qué usuario salió** (documentar cuál llave es para qué — acuerdo del kickoff) y tratarlo como **llave de producción** (sin sandbox).
|
> - **Quedan dos higienes:** (1) Balam debe confirmar **de qué usuario salió** el token (Johann lo pidió 12:27, Erika "okey" — el acuerdo del kickoff fue el de **Arturo** para dev); (2) tratarlo como **llave de producción** (sin sandbox): guardarlo en gestor de secretos, no dejarlo en el correo/.txt.
|
||||||
|
> - **Desbloquea la validación técnica de la API** (mar 7 – mié 8 del plan) — puede incluso adelantarse hoy.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 29 · Jul 6, 2026 — 12:38 PM · Correo · Pedro → Johann (CC Noe, Erika) · entrega formal del token de BIND ✅
|
||||||
|
|
||||||
|
> **Adjunto:** `bind_token_api.txt`. Evidencia: `../fuentes/2026-07-06 - Correo - Pedro entrega token BIND (usuario Arturo Rosas).md`.
|
||||||
|
|
||||||
|
Pedro entrega el **token de la API de BIND** por correo (es el envío que Erika anunció por WhatsApp a las 12:40, [#28](#28--jul-6-2026--901-am1240-pm--whatsapp-erika--johann--token-de-bind-entregado-)) y **confirma que fue generado con el usuario de Arturo Rosas** — conforme al acuerdo del kickoff (llave de Arturo para desarrollo; la de Ara para Power BI). Se ofrece para validar dudas directamente. Erika agradece en el hilo (12:39). **Johann acusa recibo (~3:28 pm):** toma nota del usuario de Arturo para documentarlo, confirma que ya puede comenzar a trabajar con la API y que cualquier duda la validará con Pedro — deliberadamente **sin comprometer entregas adicionales** (los hallazgos se integran al documento de cierre de la Etapa 0 ya planeado).
|
||||||
|
|
||||||
|
> **Lectura estratégica:**
|
||||||
|
> - Cierra **las dos puntas** del pendiente de accesos BIND: token entregado **y** usuario documentado. La entrega por correo (no WhatsApp) además deja la evidencia formal en el canal correcto.
|
||||||
|
> - Higiene inmediata: pasar el token del `.txt` a un **gestor de secretos** y no conservarlo en el buzón.
|
||||||
|
> - Dato colateral: la firma de Pedro lo identifica como **ITSM Analyst / Consultor de Gestión de Servicios TI** — coherente con que él montó el portal Jira ITSM del Discovery.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 30 · Jul 6, 2026 — 3:01 PM · WhatsApp Johann → Erika · propone sesión de cobranza (martes 7-jul)
|
||||||
|
|
||||||
|
> Evidencia: `../fuentes/2026-07-06 - WhatsApp - Erika sesion discovery y seguimiento token BIND.md` (bloque 7).
|
||||||
|
|
||||||
|
Johann plantea a Erika que la sesión de hoy solo alcanzó para **facturación y envío**, y que falta mapear **cobranza** (seguimiento de pagos y cuentas por cobrar). Propone el **martes 7-jul, 7am** — retomando la fecha que Erika misma había mencionado el 1-jul — o cuando se acomode en la semana. **Erika queda en revisar la agenda con las personas involucradas y confirmar.**
|
||||||
|
|
||||||
|
> **Lectura estratégica:**
|
||||||
|
> - Completa el Discovery pendiente de [#27](#27--jul-6-2026--700-am--llamada--discovery-proceso-actual-de-facturación-y-cobranza-) reutilizando la propia propuesta de Erika (bajo costo de coordinación). Interlocutor clave: **Arturo** (lleva CxC en BIND); Ara deseable por el contexto comercial.
|
||||||
|
> - Idealmente se confirma antes del **viernes 10** (validación del prototipo), para que la pantalla de Cobranza llegue respaldada por el proceso real y no como hipótesis.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 31 · Jul 6, 2026 — 4:33 PM · Correo · Noé → Johann, Pedro (CC Erika) · salvaguardas sobre el token ⚠️
|
||||||
|
|
||||||
|
> **Importancia alta.** Evidencia: `../fuentes/2026-07-06 - Correo - Pedro entrega token BIND (usuario Arturo Rosas).md` (respuesta de Noé en el hilo).
|
||||||
|
|
||||||
|
Noé responde al hilo de la entrega del token con dos instrucciones:
|
||||||
|
|
||||||
|
1. **A Johann:** mientras Pedro revisa si el acceso es de lectura o también de **escritura**, aplicar *"las salvaguardas necesarias para evitar alguna afectación operativa"*.
|
||||||
|
2. **A Pedro:** si es necesario, **crear un usuario específico de solo lectura** en BIND para esta etapa de exploración — *"la cuenta de Arturo tiene algunos privilegios elevados que creo que no serían necesarios en este momento"*.
|
||||||
|
|
||||||
|
**Implicación:** el token de Arturo **podría ser reemplazado** por uno de un usuario nuevo de solo lectura.
|
||||||
|
|
||||||
|
> **Lectura estratégica:**
|
||||||
|
> - La preocupación de Noé es **exactamente el diseño ya construido**: el sandbox opera en modo solo-lectura por defecto con bloqueo de escrituras en código, dry-run, cuota vigilada y token fuera del repo (`.gitignore` ya cubre `bind_token_api.txt` y `*.env`). Responder describiendo esas salvaguardas convierte un correo de riesgo en una oportunidad de confianza con el CTO.
|
||||||
|
> - Ojo operativo: si Pedro crea un usuario nuevo, **el token cambiará** — no invertir esfuerzo en atar nada al token actual; el cliente ya está parametrizado por variable de entorno, el cambio es trivial.
|
||||||
|
> - Coherente con la estrategia "lectura primero" de la propuesta: nadie está pidiendo frenar la validación, solo asegurar el perímetro.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 32 · Jul 6, 2026 — tarde · Validación técnica de la API de BIND con la cuenta real ⭐ ✅
|
||||||
|
|
||||||
|
> Actividad del plan "Validación técnica de la API de BIND" (mar 7 – mié 8) — **ejecutada un día antes**, el mismo día en que llegó el token. Evidencia completa: [`../bind-api-sandbox/VALIDACION-API.md`](../bind-api-sandbox/VALIDACION-API.md) (117 peticiones, exclusivamente GET, solo estructura — cero datos reales persistidos).
|
||||||
|
|
||||||
|
**Veredictos clave:**
|
||||||
|
|
||||||
|
1. ✅ **Plan A del saldo CONFIRMADO** — el riesgo central de la propuesta quedó resuelto a favor: cada fila de `Invoices` trae `Total`, `Payments` (acumulado) y `CreditNotes`; el saldo por factura es una resta local, verificada aritméticamente. **No se activa el Plan C (+6–8 h)** — la inversión apunta a la parte baja del rango.
|
||||||
|
2. ✅ **El flujo del MVP es 100% sostenible en lectura:** cotizaciones legibles (con partidas y comercial), prefacturas distinguibles (`UUID null` / `IsFiscalInvoice false`), PPD/PUE y días de crédito auditables (en el detalle), IVA por partida (habilita la validación 16%/0%), **PDF del CFDI descargable por API** (insumo del módulo de envío), aging viable con `ExpirationDate` + saldo.
|
||||||
|
3. ❌ **No existe recurso de pagos individuales ni de complementos de pago (REP)** — 20 nombres probados, todos 404. La cobranza operativa del MVP funciona igual (detecta pago por transición de estatus/residual), pero la fecha valor del pago y los REP del flujo PPD no son visibles. **Escalar a Pedro.**
|
||||||
|
4. ⚠️ **Sin vínculo cotización→factura en la API** — la trazabilidad la llevará la plataforma (registrar el par al orquestar la conversión).
|
||||||
|
5. ⚠️ **Rarezas a cablear con cuidado:** token inválido responde 500 (no 401); `$select` y conteos no funcionan (paginación manual con `$top=100`); nomenclatura CFDI **invertida** vs SAT (`CFDIPaymentTerm` = PPD/PUE); `CFDIUse` es código interno (falta tabla de mapeo); typo real del API (`Loctaion`); campos clave solo en el detalle → **sync incremental obligatorio**.
|
||||||
|
6. ✅ **Token acotado a una sola empresa** (la de Arturo → Balam) — multi-empresa confirmado fuera del alcance por API.
|
||||||
|
7. **Presupuesto de sync:** ~1,000 req/día con polling cada 15 min ≈ 5% del límite de 20K. Holgado.
|
||||||
|
|
||||||
|
**Preguntas que quedan para Pedro/doc (§10 del documento):** endpoint de pagos/REP no descubierto · tabla `CFDIUse` interno→clave SAT · shape del endpoint `/xml` · códigos de `Status` >2 · webhooks/eventos.
|
||||||
|
|
||||||
|
> **Lectura estratégica:**
|
||||||
|
> - **El Discovery técnico ya pagó:** el Plan A confirmado elimina la mayor incertidumbre de horas de la propuesta, y todo lo que el MVP necesita en lectura existe. La actividad del plan se cerró **anticipada** — buen dato para el corte de Erika del lunes.
|
||||||
|
> - El hueco de REP/pagos es real pero **no bloquea el MVP** (cobranza operativa funciona por transición de estatus); importa para el flujo PPD completo y para conciliación (Anexo B). Escalarlo con Pedro esta semana, idealmente junto con la tabla de `CFDIUse`.
|
||||||
|
> - Las salvaguardas prometidas a Noé ([#31](#31--jul-6-2026--433-pm--correo--noé--johann-pedro-cc-erika--salvaguardas-sobre-el-token-)) se cumplieron al pie: GET-only por construcción, presupuesto de peticiones, reporte sanitizado, token fuera de git.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 33 · Jul 6, 2026 — 4:34–5:03 PM · WhatsApp Erika → Johann · confirma operar solo-lectura (eco del correo de Noé)
|
||||||
|
|
||||||
|
> Evidencia: `../fuentes/2026-07-06 - WhatsApp - Erika sesion discovery y seguimiento token BIND.md` (bloque 8).
|
||||||
|
|
||||||
|
**Resumen:** Erika transmite por WhatsApp, en versión breve, la instrucción de Noé sobre salvaguardas del token ([#31](#31--jul-6-2026--433-pm--correo--noé--johann-pedro-cc-erika--salvaguardas-sobre-el-token-)): *"referente a lo del token que se te compartió, para evitar complicaciones operativas."* Johann confirma: *"por el momento estaré haciendo operaciones solo de lectura."* Erika agradece el entendimiento.
|
||||||
|
|
||||||
|
**Acción derivada:** confirmación informal, no sustituye la respuesta formal por correo a Noé (borrador listo, ver [PENDIENTES.md](PENDIENTES.md)) — conviene enviarla igual para dejar constancia en el canal formal, tal como pidió Noé.
|
||||||
|
|
||||||
|
> **Lectura estratégica:**
|
||||||
|
> - Erika actúa como puente entre la instrucción formal de Noé (correo, 4:33 pm) y Johann — coherente con su rol de intermediaria única.
|
||||||
|
> - El contenido no agrega nada nuevo a lo ya resuelto por diseño (sandbox solo-lectura); es una confirmación de bajo costo que sostiene la confianza mientras se prepara la respuesta formal.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 34 · Jul 6, 2026 — 6:36–6:40 PM · WhatsApp + convocatoria Outlook/Teams · confirma sesión de cobranza (martes 7-jul) ⭐
|
||||||
|
|
||||||
|
> Evidencia: `../fuentes/2026-07-06 - WhatsApp - Erika sesion discovery y seguimiento token BIND.md` (bloque 9).
|
||||||
|
|
||||||
|
**Resumen:** Erika envía la convocatoria **"Proceso actual de cobranza- Balam"**, martes **07/07/2026, 7:00–8:00 AM**, por Microsoft Teams. Organiza Erika Chávez; invitados **Johann, Araceli Sánchez Jiménez y Arturo Rosas Hernández** (CC: Noé Rocha) — cierra la propuesta que Johann hizo el mismo día a las 3:01 pm ([#30](#30--jul-6-2026--301-pm--whatsapp-johann--erika--propone-sesión-de-cobranza-martes-7-jul-)). Johann confirma recepción por WhatsApp (6:40 pm).
|
||||||
|
|
||||||
|
**Acción derivada:** asistir a la sesión el martes 7-jul, 7:00 am (agenda: cómo dan seguimiento hoy a pagos y cuentas por cobrar, quién persigue morosos, cómo concilian, de dónde saldría el aging — temas listados en la lectura estratégica de [#27](#27--jul-6-2026--700-am--llamada--discovery-proceso-actual-de-facturación-y-cobranza-)).
|
||||||
|
|
||||||
|
> **Lectura estratégica:**
|
||||||
|
> - **Cierra el hueco de Discovery que quedó abierto el 6-jul:** facturación y envío ya están mapeados ([#27](#27--jul-6-2026--700-am--llamada--discovery-proceso-actual-de-facturación-y-cobranza-)); cobranza se mapea el 7-jul, antes del viernes 10 (fecha objetivo para la validación del prototipo) — como Johann buscaba al proponerlo.
|
||||||
|
> - **Interlocutores correctos confirmados:** Arturo (lleva CxC en BIND) y Araceli (contexto comercial), igual que en la sesión anterior — buena señal de continuidad.
|
||||||
|
> - Convocatoria aún sin respuestas registradas ("4 sin respuesta") al momento de reenviarla — no es un riesgo en sí, Balam ya confirmó la sesión por WhatsApp.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 35 · Jul 7, 2026 — 7:00 AM · Llamada · Discovery: proceso actual de cobranza (sin grabación) ⭐
|
||||||
|
|
||||||
|
> Participan: **Arturo Rosas** (administración/CxC — mostró el proceso) y Johann; la convocatoria incluía también a Araceli y Erika (CC Noé). Canal: Microsoft Teams, 7:00–8:00 AM. **⚠️ Sin grabación** — evidencia: `../fuentes/2026-07-07 - Notas - Proceso actual de cobranza (sin grabacion).md` (notas de memoria de Johann, mismo día).
|
||||||
|
|
||||||
|
**Resumen:** Segunda sesión de Discovery — cierra el mapeo de **cobranza** que quedó pendiente el 6-jul ([#27](#27--jul-6-2026--700-am--llamada--discovery-proceso-actual-de-facturación-y-cobranza-)). Arturo mostró sus **Exceles de control** (facturas abiertas, canceladas, días de morosidad — el aging de facto) y el flujo real de un pago: el cliente avisa por **correo/mensaje con su estado de cuenta** → se comparte con el **despacho contable**, que **registra los pagos en BIND uno por uno** → un **Power BI** (conectado, según Arturo, con **su token**) muestra la cartera pero **no está al día**.
|
||||||
|
|
||||||
|
**El detalle que complica conciliar:**
|
||||||
|
- Un mismo pago puede cubrir **decenas de facturas** ("10 pesos divididos en 40 facturas" — consistente con el cliente que exige factura por colaborador, ~40).
|
||||||
|
- Los comprobantes llegan **todos con el mismo patrón de referencia** (tipo `num_referencia.pdf`) → la referencia no distingue facturas, **se concilia por folio**.
|
||||||
|
- Al recibir el dinero hay un **fee/comisión** → los montos **no cuadran exactos** contra el total facturado.
|
||||||
|
- Caso mostrado: Arturo pidió a **Acuntia** su relación de pagos; respondieron con un **Excel pago ↔ folio** — control de ambos lados, pero totales distintos por el fee.
|
||||||
|
- Casos límite: clientes que pagan **facturas viejas arrastradas** (aplicación fuera de orden) y facturas pagadas cuyo **registro va atrás de la realidad**.
|
||||||
|
|
||||||
|
**Lo que buscan:** ser **proactivos** — recordatorios configurables (pasados **1–5 días** de vencimiento, correo al cliente) o al menos visibilidad inmediata del atraso. Su evolución: eran **reactivos** ("ya hace rato que no me paga"), hoy son **activos**, quieren ser **proactivos**.
|
||||||
|
|
||||||
|
**Pendientes que surgieron:**
|
||||||
|
- [ ] Johann — **pedir a Arturo los Exceles**: control de cobranza (abiertas/canceladas/morosidad), el Excel tipo Acuntia (pago↔folio) y un estado de cuenta ejemplo.
|
||||||
|
- [ ] Johann — preguntar **cómo registran en BIND la diferencia por fee** (¿pago parcial con residual? ¿nota de crédito?).
|
||||||
|
- [ ] Johann/Pedro — **verificar qué token alimenta el Power BI** (ver lectura estratégica).
|
||||||
|
|
||||||
|
> **Lectura estratégica:**
|
||||||
|
> - **El diseño de cobranza del MVP queda respaldado por el proceso real:** el aging **por factura** con residual (Plan A: `Total − Payments − CreditNotes`, confirmado en [#32](#32--jul-6-2026--tarde--validación-técnica-de-la-api-de-bind-con-la-cuenta-real--)) cubre exactamente los casos que Arturo describió — pagos fuera de orden y facturas arrastradas se leen por factura, no por cliente.
|
||||||
|
> - **Regla de diseño nueva — tolerancia a fees:** "pagada" no puede exigir residual = 0 exacto; hace falta un **umbral configurable** para no mostrar como morosas facturas saldadas con comisión. Aplica también a la conciliación futura (Anexo B).
|
||||||
|
> - **El hueco de pagos/REP del API ([#32](#32--jul-6-2026--tarde--validación-técnica-de-la-api-de-bind-con-la-cuenta-real--)) ahora tiene caso de negocio:** el despacho registra pagos **a mano, uno por uno** — automatizar ese registro requeriría justo el endpoint que no apareció. Refuerza la escalación a Pedro.
|
||||||
|
> - ⚠️ **Discrepancia de tokens a verificar:** Arturo mostró el Power BI conectado con **su** token, pero el kickoff ([#22](#22--jul-1-2026--700-am--llamada--kickoff-del-proyecto-)) acordó **Ara = Power BI / Arturo = desarrollo**. BIND emite **1 token por usuario**: si Power BI y el desarrollo comparten el de Arturo, el reemplazo por un usuario solo-lectura que planteó Noé ([#31](#31--jul-6-2026--433-pm--correo--noé--johann-pedro-cc-erika--salvaguardas-sobre-el-token-)) **rompería uno de los dos**. Verificar con Pedro antes de cualquier cambio.
|
||||||
|
> - **Los recordatorios proactivos a clientes reaparecen** (tercera vez: propuesta original, sesión del 6-jul, hoy) — siguen siendo módulo del **Anexo B** (el MVP trae alertas internas + visibilidad). La aclaración de expectativas ya no puede esperar mucho.
|
||||||
|
> - **Actor nuevo en el mapa: el despacho contable** — no había aparecido en el Discovery de facturación; es quien toca BIND para los pagos.
|
||||||
|
> - Los **Exceles de Arturo son la especificación de facto de la pantalla de Cobranza** (columnas, buckets de morosidad que ya usan) — pedirlos con las salvaguardas de siempre: datos reales fuera del repo y de los documentos, solo estructura.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 36 · Jul 7, 2026 — 12:27 PM · WhatsApp Erika → Johann · seguimiento del pendiente de BIND (¿probar escritura?)
|
||||||
|
|
||||||
|
> Evidencia: `../fuentes/2026-07-07 - WhatsApp - Erika seguimiento BIND.md`.
|
||||||
|
|
||||||
|
**Resumen:** Erika revisa de nuevo el plan de actividades y pregunta por la línea **"Validación técnica de la API de BIND con la cuenta real"** (vigente hoy 7-jul y mañana 8-jul según el plan): *"johan respecto a este punto, es para hoy y mañana, todo bien, necesitas algo?"* — la misma actividad que Johann ya adelantó y cerró el 6-jul ([#32](#32--jul-6-2026--tarde--validación-técnica-de-la-api-de-bind-con-la-cuenta-real--)).
|
||||||
|
|
||||||
|
> ⚠️ A las 9:28 am Erika había respondido *"no"* a un mensaje previo que no está incluido en la evidencia disponible — contexto pendiente de aclarar.
|
||||||
|
|
||||||
|
**Acción derivada:** Johann evalúa si hace falta sondear los endpoints de **escritura (POST/PUT)** de BIND, ya que la validación de lectura cerró en #32. **Decisión:** no probarlos en vivo contra producción todavía — sigue vigente la instrucción de Noé de mantener el modo solo-lectura mientras Pedro confirma el alcance real del token ([#31](#31--jul-6-2026--433-pm--correo--noé--johann-pedro-cc-erika--salvaguardas-sobre-el-token-)), y no hay sandbox donde ensayar sin riesgo fiscal. Camino seguro en su lugar: revisar el **catálogo de operaciones del portal de desarrolladores de BIND** (ya referenciado por Noé el 25-may — incluye al menos `Activities_AddActivity`) para documentar qué escrituras existen, sin ejecutarlas. Respuesta a Erika: nada adicional urgente para las pruebas; lo único abierto es escalar con Pedro las preguntas técnicas ya listadas en [PENDIENTES.md](PENDIENTES.md) (endpoint de pagos/REP, tabla `CFDIUse`, shape de `/xml`, webhooks).
|
||||||
|
|
||||||
|
> **Lectura estratégica:**
|
||||||
|
> - Buena disciplina: la tentación de "ya que tenemos el token, probemos escritura" se descarta porque **contradice justo lo que Noé pidió resguardar** — probarlo ahora sería el peor momento (token de Arturo con privilegios elevados, posible reemplazo en curso).
|
||||||
|
> - El `BindClient` ya está diseñado para este escenario: modo `read-only` bloquea cualquier método mutante en código (`BindReadOnlyViolation`), y `addActivity()` queda estructuralmente listo pero inerte hasta que se active `dry-run`/`write` con autorización — los candados de la propuesta (dry-run + confirmación humana) siguen intactos.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 37 · Jul 7, 2026 — 3:56–5:49 PM · WhatsApp Erika ↔ Johann · seguimiento al usuario de lectura de BIND
|
||||||
|
|
||||||
|
> Evidencia: `../fuentes/2026-07-07 - WhatsApp - Erika seguimiento BIND.md` (bloque 3).
|
||||||
|
|
||||||
|
Erika confirma que, mientras se resuelve si Pedro crea el **usuario de solo lectura** que pidió Noé por correo ([#31](#31--jul-6-2026--433-pm--correo--noé--johann-pedro-cc-erika--salvaguardas-sobre-el-token-)), ella sigue operando en BIND con el **usuario de Arturo** a modo de lectura, sin que esto la bloquee. Pregunta si ese usuario de lectura se lo iban a crear. Johann aclara que **Noe le pidió a Pedro revisar si era necesario crearlo** — la decisión no depende de Erika. Erika, reconociendo que no está familiarizada con el tema, queda en revisarlo con el equipo técnico.
|
||||||
|
|
||||||
|
> **Lectura estratégica:**
|
||||||
|
> - Confirma que el reemplazo de token propuesto por Noé el 6-jul ([#31](#31--jul-6-2026--433-pm--correo--noé--johann-pedro-cc-erika--salvaguardas-sobre-el-token-)) **sigue sin resolverse una semana después** — Pedro no ha confirmado si crea el usuario nuevo. No es bloqueante hoy (Erika opera con el de Arturo sin problema), pero sigue abierto el riesgo señalado en [#35](#35--jul-7-2026--700-am--llamada--discovery-proceso-actual-de-cobranza-sin-grabación-): si el Power BI de Arturo comparte el mismo token que el de desarrollo, reemplazarlo rompería uno de los dos usos.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 38 · Jul 8, 2026 — 10:09 AM–10:55 AM · WhatsApp Erika ↔ Johann · datos ficticios para los Exceles de cobranza
|
||||||
|
|
||||||
|
> Evidencia: `../fuentes/2026-07-08 - WhatsApp - Erika datos ficticios cobranza y avance fase 0.md` (bloques 1–2).
|
||||||
|
|
||||||
|
Erika avisa que Balam está **validando internamente** si puede compartir la información de cobranza que Johann solicitó ([#35](#35--jul-7-2026--700-am--llamada--discovery-proceso-actual-de-cobranza-sin-grabación-), pendiente en PENDIENTES.md), por tratarse de información delicada. Johann ofrece una salida de bajo fricción: acepta que sean **datos ficticios**, ya que lo que necesita es la **estructura** que siguen, no los datos reales. Erika traslada la propuesta al equipo (10:50) y queda en avisar cuando tenga respuesta.
|
||||||
|
|
||||||
|
> **Lectura estratégica:**
|
||||||
|
> - Resuelve por adelantado la fricción de confidencialidad de los Exceles de cobranza pedidos en [#35](#35--jul-7-2026--700-am--llamada--discovery-proceso-actual-de-cobranza-sin-grabación-): en vez de esperar una validación legal/interna que podría demorar, Johann baja el estándar a "estructura, no datos reales" — coherente con la salvaguarda ya prometida a Noé de mantener datos reales fuera del repo y de los documentos.
|
||||||
|
> - Sin fecha comprometida para la respuesta de Balam; sigue siendo **no bloqueante** para el avance del prototipo.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 39 · Jul 9–10, 2026 · WhatsApp Erika ↔ Johann · avance de Fase 0 + prototipo sin sesión de validación agendada
|
||||||
|
|
||||||
|
> Evidencia: `../fuentes/2026-07-08 - WhatsApp - Erika datos ficticios cobranza y avance fase 0.md` (bloques 3–4).
|
||||||
|
|
||||||
|
**9-jul, 7:20 PM:** Erika pide a Johann un corte de **avance de las actividades de Etapa 0** ("cómo vamos"). Johann responde hasta las 9:10 PM preguntando por qué medio compartirlo, sin resolverlo esa noche.
|
||||||
|
|
||||||
|
**10-jul, 8:41–9:39 AM:** Erika insiste ("nomás dime qué actividades y % de avance"). Johann reconoce que **no confirmó la sesión de validación del prototipo** que él mismo se había puesto como meta para el **viernes 10-jul** (ver PENDIENTES.md): el día anterior no dio seguimiento y no quedó agendada la reunión. Ofrece, en su lugar, **compartir el prototipo por correo con sus propios comentarios**. Erika acepta y da instrucciones de destinatarios, **corrigiéndose dos veces en minutos**: primero pide copiar a Pedro, al Ing. Noé **y a Araceli** (9:00); a los 21 minutos corrige — sin Araceli (9:21–9:22); y pocos minutos después acota aún más — **solo a ella y al Ing. Noé, con Pedro en copia**, "nosotros se los pasamos internamente" (9:28). Johann confirma y, al cierre, retoma la pregunta pendiente del 9-jul sobre si el % de avance de Fase 0 se reporta por el mismo canal de WhatsApp — **sin respuesta aún al cierre de este tramo**.
|
||||||
|
|
||||||
|
> **Lectura estratégica:**
|
||||||
|
> - **Dos pendientes de Johann quedan expuestos por la falta de seguimiento propio:** (1) el reporte de avance/% de Fase 0 que Erika pidió desde el 9-jul, y (2) la sesión de validación del prototipo del viernes 10-jul (comprometida en PENDIENTES.md) que no se agendó a tiempo. Ambos se resuelven convergiendo en un solo entregable: **correo con el prototipo (imágenes) + avance de Fase 0**, en vez de una reunión.
|
||||||
|
> - **Lista de destinatarios del correo del prototipo, final:** Erika + Ing. Noé, **CC Pedro únicamente** — Araceli queda excluida (Balam decide compartirlo con ella internamente). Difiere del patrón habitual del hilo de correo principal, donde Araceli sí solía ir en copia — respetar esta instrucción explícita para este envío.
|
||||||
|
> - Sigue sin resolverse el % de avance de Fase 0 que se debe reportar — acción abierta de Johann.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 40 · Jul 10, 2026 · Correo · Johann → Erika, Noé (CC Pedro) · entrega del prototipo Etapa 0 ⭐
|
||||||
|
|
||||||
|
> Evidencia documental: `../prototipo/Correo-prototipo-Etapa0.md`. Adjunto: `../prototipo/Prototipo-Balam-Etapa0.pdf`.
|
||||||
|
|
||||||
|
Johann entrega por correo el prototipo navegable de facturación y cobranza, con PDF y acceso temporal a la versión web. El material usa datos ficticios y refleja el flujo levantado en Discovery: Jira → cotización BIND → prefactura → validación humana → CFDI → envío, además de cobranza por factura.
|
||||||
|
|
||||||
|
Balam responde que lo revisará internamente y compartirá comentarios. La retroalimentación queda pendiente; no se realizó la sesión en vivo prevista originalmente para ese día.
|
||||||
|
|
||||||
|
**Acción derivada:** mantener el entregable en validación y continuar únicamente con trabajo de Etapa 1 que no dependa de cambios visuales.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 41 · Jul 13, 2026 · WhatsApp Erika ↔ Johann · continuidad de Etapa 1, sesión con Arturo y factura de 30 h
|
||||||
|
|
||||||
|
> Evidencia: `../fuentes/2026-07-13 a 2026-07-15 - WhatsApp - Seguimiento prototipo sesiones accesos.md`.
|
||||||
|
|
||||||
|
Johann comunica que, mientras Balam revisa el prototipo, avanzará localmente con base de datos, autenticación/roles y cliente de consulta de BIND. Erika explica que la CEO se encuentra fuera del país y que la diferencia de horario dificulta la validación inmediata.
|
||||||
|
|
||||||
|
Johann pide coordinar una sesión con Arturo para cerrar alta de clientes nuevos, tratamiento del fee en BIND y Excel de particularidades de envío. Erika contactará a Arturo y confirma que el Excel continúa en preparación.
|
||||||
|
|
||||||
|
Sobre la propuesta v1.2 y la factura inicial de 30 h, Erika confirma que recibió el documento. La validación administrativa sigue pendiente porque la CEO y Noé están fuera del país por trabajo; Erika revisará cómo obtener el visto bueno.
|
||||||
|
|
||||||
|
> **Lectura estratégica:** no hay rechazo comercial ni bloqueo técnico total. Johann mantiene momentum en local, pero la factura aún no debe emitirse sin confirmación y las definiciones de negocio se recorren.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 42 · Jul 14, 2026 · WhatsApp Erika ↔ Johann · dos sesiones confirmadas para 22–23 jul
|
||||||
|
|
||||||
|
> Evidencia: `../fuentes/2026-07-13 a 2026-07-15 - WhatsApp - Seguimiento prototipo sesiones accesos.md`.
|
||||||
|
|
||||||
|
Erika confirma dos espacios distintos:
|
||||||
|
|
||||||
|
1. **Miércoles 22-jul:** sesión de dudas y definiciones de la Etapa 1 con Arturo y participación solicitada de la CEO.
|
||||||
|
2. **Jueves 23-jul, 7:00 pm:** sesión de validación del prototipo de la Etapa 0. Erika envía la convocatoria.
|
||||||
|
|
||||||
|
**Temas para el 22-jul:** alta de cliente nuevo, registro de diferencias por fee, particularidades de envío y definición de alcance de la posible automatización Jira → cotización BIND.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 43 · Jul 14–15, 2026 · WhatsApp Erika ↔ Johann · ajuste de accesos y repositorio GitHub autorizado
|
||||||
|
|
||||||
|
> Evidencia: `../fuentes/2026-07-13 a 2026-07-15 - WhatsApp - Seguimiento prototipo sesiones accesos.md`.
|
||||||
|
|
||||||
|
Johann aclara que para la Etapa 1 requiere un repositorio privado de GitHub de Balam y, posteriormente, acceso acotado a Azure. Se mantiene la reprogramación: repositorio primero; Azure durante la semana del 21-jul; CI/CD y secretos el 24-jul.
|
||||||
|
|
||||||
|
El 15-jul Erika confirma que Noé autorizó la creación del repositorio y solicita el usuario de GitHub. Johann comparte `Johann-28` (`https://github.com/Johann-28`).
|
||||||
|
|
||||||
|
**Estado:** repositorio autorizado; falta recibir la invitación y verificar permisos de escritura. Azure aún no bloquea el desarrollo local.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 44 · Jul 14, 2026 · WhatsApp Erika ↔ Johann · Excel de días vencidos condicionado a validación
|
||||||
|
|
||||||
|
> Evidencia: `../fuentes/2026-07-13 a 2026-07-15 - WhatsApp - Seguimiento prototipo sesiones accesos.md`.
|
||||||
|
|
||||||
|
Erika pregunta si todavía se requiere el Excel de control de cobranza con días vencidos. Johann aclara que, si Balam considera que la estructura del prototipo refleja correctamente su operación, ya no será necesario prepararlo; si detectan ajustes, seguirá siendo útil como referencia y puede contener datos ficticios.
|
||||||
|
|
||||||
|
**Impacto:** deja de ser un pendiente obligatorio y permanece condicionado a la retroalimentación del prototipo. Esto no sustituye el **Excel de particularidades de envío por cliente**, que Balam continúa preparando.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 45 · Jul 15, 2026 · Gestión interna · se inicia control local de horas
|
||||||
|
|
||||||
|
Mientras Pedro habilita el flujo de reporte en Jira, Johann crea un control local en `../planeacion/Seguimiento-horas.csv` y su guía en `../planeacion/Seguimiento-horas.md`.
|
||||||
|
|
||||||
|
Se cargan 3.08 h de sesiones con duración comprobable y se reconstruye retrospectivamente el resto del esfuerzo por actividad hasta un corte de **30 h**: **22 h de Etapa 0** y **8 h de Etapa 1**. Las 26.92 h reconstruidas quedan etiquetadas como estimadas y deben validarse contra memoria/evidencia y Jira antes de facturar; no se derivan del porcentaje de avance.
|
||||||
|
|
||||||
|
**Acción:** Johann debe validar el desglose reconstruido, capturar diariamente en adelante y conciliar con Jira cuando Balam otorgue acceso al flujo.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 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. **Esa misma noche (8:40 pm) Johann envía por WhatsApp el Excel `Plan-actividades-avance-2026-07-16.xlsx`; Erika acusa recibo a las 8:44 pm ("Ntp, gracias").**
|
||||||
|
|
||||||
|
**Acciones derivadas:**
|
||||||
|
|
||||||
|
- [x] ~~Johann — validar permisos de escritura en el repositorio de Balam~~ → **Comprobado (16-jul):** push exitoso del scaffold inicial (backend .NET 10 + frontend Angular 21) al repo de Balam.
|
||||||
|
- [ ] Johann — revisar el correo y los procedimientos de carga de facturas; documentar reglas o diferencias frente al Discovery.
|
||||||
|
- [x] ~~Johann — enviar a Erika el corte de avance de Etapa 1~~ → **Hecho (16-jul, 8:40 pm):** Excel enviado por WhatsApp con acuse de Erika.
|
||||||
|
|
||||||
|
**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.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 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.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 54 · Jul 22, 2026 — 7:00 AM · Llamada · Sesión de reglas Etapa 1: alta de clientes, fee bancario y flujo Jira ⭐
|
||||||
|
|
||||||
|
> Participan: Noe Rocha (sala de juntas), Arturo Rosas, Araceli (se suma ~min 21 para el tema del fee) y Johann. Erika no participó. Duración ~48 min. Transcripción: `../fuentes/2026-07-22 - Flujo para dar de alta clientes nuevos,_ Transcript.txt`. Agenda preparada en [`../planeacion/Sesion-Etapa1-2026-07-22.md`](../planeacion/Sesion-Etapa1-2026-07-22.md).
|
||||||
|
|
||||||
|
**Resumen:** Se resolvieron 3 de los temas de la agenda: **alta de cliente/proveedor en BIND** (demostrada en pantalla por Arturo), **tratamiento del fee bancario** (lo registra el despacho, no Balam) y **flujo Jira de facturación** (estatus, validaciones y confirmación de que Jira ya es parte del proceso). Quedaron fuera: particularidades de envío, la pregunta PUE/PPD y el cierre técnico (repo/Azure/Jira horas).
|
||||||
|
|
||||||
|
**1 · Alta de cliente/proveedor en BIND (ASIS, demostrado por Arturo):**
|
||||||
|
- **Mismo procedimiento** para clientes (módulo Ventas→Clientes) y proveedores (Compras→Proveedores). Botón **Agregar** → 2 pestañas: **Detalle** y **Direcciones**.
|
||||||
|
- **Detalle:** razón social, RFC, nombre comercial y categoría se toman de la **constancia de situación fiscal**; banco y CLABE del **estado de cuenta** del cliente. **Días y monto de crédito vienen de la negociación comercial** (no de un documento). Además: correo de comunicación, sucursal (solo existe matriz), teléfono, uso CFDI (mayoría "gastos en general", según constancia), régimen fiscal e **idioma de documentos** (p. ej. BICTEX → inglés). Clientes agregan: ventas esperadas, lista de precios, descuento pactado.
|
||||||
|
- **Direcciones:** país/estado/municipio/calle/CP, también de la constancia. Al guardar, BIND asigna un **ID interno consecutivo** (ej. 1016), exclusivo de la instancia de Balam.
|
||||||
|
- En **editar** se pueden adjuntar archivos: constancia, carátula bancaria, NDA, contrato marco — todo en un solo lugar.
|
||||||
|
- **El alta nace del área comercial:** cuando una propuesta pasa a firme, administración da de alta al cliente. Caso reciente: **Frisa**.
|
||||||
|
- **Extranjeros (ej. Acuntia):** documento legal/fiscal del país en lugar de constancia; BIND propone un **RFC genérico**; uso CFDI = **"sin efectos fiscales"**; la factura al extranjero genera **solo PDF, sin XML ni timbrado SAT** (se registra como gasto/ingreso extranjero sin obligaciones fiscales).
|
||||||
|
- No se hizo alta en vivo (producción, sin datos de prueba). **Arturo se comprometió a grabar y documentar** los procesos nuevos conforme ocurran (Frisa está por facturarse; hará manuales Word/PowerPoint).
|
||||||
|
|
||||||
|
**2 · Fee/comisión bancaria (con Araceli):**
|
||||||
|
- El fee **NO lo registra Balam: lo registra y deduce contablemente el despacho**, para que la factura quede saldada al 100% sin residual de deuda del cliente. Balam solo concilia estado de cuenta ↔ facturas pagadas.
|
||||||
|
- El fee **solo aplica a pagos internacionales que mueven dinero hacia México** (ej. Europa→Banorte; varía según banco emisor). Los **pagos nacionales cuadran a centavos**, sin fee. **BigTech** paga de banco americano a banco americano de Balam → sin fee.
|
||||||
|
- El monto del fee **no es predecible** (no hay reglas conocidas), pero **siempre viene marcado en el estado de cuenta** ("comisión", "IVA sobre comisión"). Por eso es crítico **pedir el estado de cuenta al cliente** — además Accent/Axios tiene **muchas facturas por el mismo monto** y sin estado de cuenta no se sabe cuál pagaron.
|
||||||
|
- **Regla para la plataforma:** el agente de conciliación solo debe **identificar la diferencia como fee bancario**; quién autoriza saldar la factura con diferencia es **el despacho**, a nivel contable. Hoy el caso vivo es Acuntia.
|
||||||
|
|
||||||
|
**3 · Jira (flujo de facturación) — confirmado que SÍ entra en la fórmula:**
|
||||||
|
- Noe reconoció explícitamente que el alcance inicial decía "sin Jira" pero **el proceso interno evolucionó y hoy todo pasa por Jira**; la integración se pone sobre la mesa como ajuste.
|
||||||
|
- **Space "Facturación"** separado (hay otros: administración general, GEDEX, RH), con un solo request type hoy (facturación adicional/recurrente) — el universo de tickets de facturación está cerrado y filtrable.
|
||||||
|
- **Flujo de estatus del ticket:** inicia → abierto → **proceso de facturación** (confirmar monto, cliente dado de alta, constancia vigente, cuenta bancaria) → **validación** (nacional: adjuntar **PDF + XML obligatorios**; internacional: **solo PDF**) → facturado → **resuelto**. El estatus que confirma facturación válida es **"resuelto"** (el anterior solo es validación en curso). Las validaciones nacieron de desaciertos operativos reales ("ya facturé" sin evidencia).
|
||||||
|
- **Facturación recurrente:** ~30 facturas/mes salen de un flujo recurrente por contratos establecidos — es **proceso de Balam ejecutado en BIND**, no capacidad nativa de BIND, y siempre con intervención humana (los montos pueden variar mes a mes). **Pedirá sesión aparte con Pedro** para entenderlo.
|
||||||
|
- **Forecast contractual a largo plazo:** BIND solo proyecta sobre lo ya facturado (por plazo de pago); Noe quisiera a futuro proyección por contrato — **explícitamente NO es para ahora**.
|
||||||
|
|
||||||
|
**4 · Escritura en BIND / prueba Jira→cotización:** Johann planteó probar el flujo ticket Jira → cotización BIND (cotización→prefactura→factura). Noe accedió a explorarlo pero con advertencia fuerte: **no hay ambiente de pruebas, es producción directa** — "muy, muy medida la prueba… para no hacernos harakiri". La escritura será acordada y controlada llegado el momento.
|
||||||
|
|
||||||
|
**Acuerdos / próximos pasos que anotó Noe:**
|
||||||
|
- [ ] Balam (Arturo/Pedro) — **enviar a Johann el diagrama del flujo Jira** en tamaño legible (el screenshot se veía ilegible en la llamada).
|
||||||
|
- [ ] Balam (Erika coordina) — **sesión con Pedro (+Arturo)** para: **API de Jira** (explorar conexión), facturación **recurrente** y facturas automáticas vs manuales.
|
||||||
|
- Confirmado: **mañana jueves 23-jul, sesión de validación del prototipo**.
|
||||||
|
|
||||||
|
> **Lectura estratégica:**
|
||||||
|
> - **Jira→BIND avanza:** Noe aceptó en voz alta que Jira ya es parte del proceso y encaminó API + sesión con Pedro — coherente con la estimación de ~10 h comunicada a Erika el 21-jul ([#51](REGISTRO.md)). El disparador de facturación es el estatus **"resuelto"**; para crear cotizaciones el candidato natural es un estatus previo (a confirmar con Pedro en la sesión de API).
|
||||||
|
> - **El fee simplifica el MVP de conciliación:** la plataforma NO registra ajustes contables — solo detecta y etiqueta la diferencia como fee bancario en pagos internacionales. El asiento es del despacho. Esto acota el alcance justo donde la agenda temía ambigüedad.
|
||||||
|
> - **Alta de clientes:** quedó mapeado el ASIS completo, pero **no se decidió** si la plataforma solo detecta clientes faltantes o también los crea — la pregunta del MVP sigue abierta y toca la estimación Jira→BIND ([#51](REGISTRO.md)): un ticket de un cliente inexistente en BIND cruza ambas piezas.
|
||||||
|
> - **No se tocaron:** la pregunta **PUE vs PPD** (1,374 facturas históricas todas PUE, [#52](REGISTRO.md)) — reintentarla el 23-jul o con Pedro—, las **particularidades de envío** (Excel sigue pendiente de Ara/Arturo) y el cierre técnico (Azure/Jira horas).
|
||||||
|
> - **Factura extranjera sin XML** confirma el diseño del prototipo (PDF-only para internacionales) y explica por qué la validación de Jira solo exige PDF en ese ramal.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 55 · Jul 24, 2026 — 11:00 AM · Llamada · Validación del prototipo Etapa 0 — APROBADO (queda 1 definición de proceso) ⭐
|
||||||
|
|
||||||
|
> Participan: Erika Chávez (abre), Johann (presenta), Noe Rocha, Araceli Sánchez, Arturo Rosas. Duración ~1 h 4 min. La sesión estaba agendada para el **jueves 23-jul 7 am** pero se corrió al **viernes 24-jul 7 am** porque Araceli (dueña del proceso) tuvo un tema personal (reagenda documentada en [#57](REGISTRO.md)). Transcripción: `../fuentes/2026 - 07 - 24 - Validación de prototipo etapa 0- proyec_ Transcript.txt`. Guion usado: [`../planeacion/Guion-validacion-prototipo-2026-07-23.md`](../planeacion/Guion-validacion-prototipo-2026-07-23.md).
|
||||||
|
|
||||||
|
**Veredicto: Etapa 0 validada.** Johann recorrió el PDF y luego el prototipo navegable (URL). Ara: *"Johann nos captó absolutamente todo lo indispensable y más… de momento no le pondría nada"*, *"Me encantó", "Sí a todo"*. Arturo: *"yo no le pondría nada más… esto es lo mínimo que necesitamos"*. Ambos aprobaron el MVP tal cual. **Queda pendiente UNA sola cosa del lado de Balam: redefinir el flujo de facturación en Jira** (ver abajo) antes de volver con Johann para hacer el MVP productivo. Erika confirmó que esto **no bloquea** a Johann, que ya está en Etapa 1.
|
||||||
|
|
||||||
|
**El único bloqueo real — definición de proceso (no de sistema):** hoy el CIS de Jira exige que la factura se haga **antes** de cerrar el ticket, es decir el proceso de facturación ya no arrancaría desde el sistema. Hay que decidir si la ejecución (prefactura) se **dispara desde la plataforma** (lo deseable, para que el sistema optimice) o se sigue haciendo antes y solo se presenta al sistema. **Arturo pidió explícitamente que sea PREFACTURA, no timbrado automático:** validación previa (nacional vs extranjera, montos, datos fiscales) para evitar cancelaciones que el SAT observa; *"para ir monitoreando la eficiencia de la IA. Tal vez en algún futuro daremos independencia completa"*. **Ara sale de viaje mié/jue/vie; propuso el martes 28-jul para esa sesión; Erika coordina.** Es prerrequisito para que Johann conecte el frontend a la escritura.
|
||||||
|
|
||||||
|
**Decisiones y aclaraciones que quedaron asentadas:**
|
||||||
|
1. **Cotización SIEMPRE, sin excepciones (Ara).** Incluso las **recurrentes** (contrato AXIANS 3–4 años) deben nacer de una cotización → prefactura/factura. Es un punto de control para cazar cambios (sueldos, viáticos, ajustes) y errores. *"El esfuerzo es el mismo"*: la cotización vive en BIND y se convierte a prefactura/factura con un clic. Noe lo cerró como **3 candados: cotización → prefactura → aprobación humana → timbrado.**
|
||||||
|
2. **Nada de agente de IA en este MVP** (Noe, muy vocal, lo repitió 3 veces): esto es **reglas de negocio + procesos + integración de sistemas** para homologar pantallas, no LLM. La IA llega después; el candidato claro para agente es **conciliación** (hoy 100% manual). Primero la base de proceso, *"si le metemos IA de inicio va a ser una fiesta"*. Johann alineado: el MVP asienta el proceso para montar los modelos encima.
|
||||||
|
3. **Roles:** la plataforma ya los soporta; por ahora Ara y Arturo ven todo, se acotan cuando entre un analista.
|
||||||
|
4. **Dashboard:** las 4 tarjetas son propuesta, todo customizable. *"Qué necesita atención"* se alimentaría de Jira + estatus de facturación. Posible acople con el Power BI de aging de Pedro (no duplicar reporteo).
|
||||||
|
5. **Fuente única = BIND (Vine) o Jira.** No hay fuente alterna. Cartera vencida sale de BIND; lo que no esté en BIND requeriría carga/capa manual.
|
||||||
|
6. **Un ticket = una factura (hoy).** Caso ACUNTIA/Accions: un solo ticket con un Excel de 40–48 facturas. Queda **abierto** si se maneja como ticket-por-factura o un ticket con múltiples hijos (definición de proceso, va con el punto del bloqueo).
|
||||||
|
7. **Todo nace en Jira — política de empresa:** *"Lo que no está en JIRA no se procesa."* Ni correo ni mensaje; incluso los correos se reenvían a una dirección que auto-crea el ticket.
|
||||||
|
8. **Envío con particularidades por cliente:** validado y les gustó (CEMEX bloqueado por faltar Excel de horas; ACUNTIA = zip con PDF+XML+estado de cuenta). El **módulo de configuración** de reglas de envío queda **cerrado/hardcodeado al inicio**; si cambian reglas, se cobra como horas de servicio. No se desarrolla ahora.
|
||||||
|
9. **NAFINSA / cadenas productivas (solo CEMEX):** facturas ya *programadas para pago* (p. ej. cae 24-sep, 120 días) que **no existen como estatus en BIND**. Quieren una **capa/pantalla extra "programado para pago"** para planeación financiera (Arturo: *"la cerecita del pastel"*). Idea de Ara: **ticket Jira automático cada 15 días** para revisar el portal Nafinsa, adjuntar Excel/pantallazos y alimentar cuentas por cobrar, haciendo match por folio. A evaluar con Johann.
|
||||||
|
10. **Fee:** tolerancia configurable por cliente (% o fijo); se muestra como *"pagada pero con tolerancia"*. (Consistente con [#54](REGISTRO.md): el asiento contable es del despacho, la plataforma solo detecta/etiqueta.)
|
||||||
|
11. **Recordatorios internos en el MVP:** D+1 → Arturo, D+15 → Araceli. Los **correos al cliente (D+5, D+30) NO son alcance** ("no está definida la regla"). Excepciones de seguimiento por cliente contempladas.
|
||||||
|
12. **Bitácora/auditoría:** *"Me encanta, sí a todo."*
|
||||||
|
|
||||||
|
**Observaciones que anotó Noe sobre el prototipo:** (a) un ticket rotulado "facturación nacional" era extranjera → pedía XML+PDF cuando extranjera es solo PDF (ajuste menor); (b) los estatus del prototipo son placeholders previos a explorar Jira ("listo para facturar" ≈ "facturado" en Jira real) y se adaptarán al flujo que Balam defina.
|
||||||
|
|
||||||
|
**Cierre y próximos pasos:**
|
||||||
|
- Balam hace **teamback** para separar *must* vs *nice-to-have* y define el flujo de facturación/prefacturación (**sesión martes 28-jul, Erika coordina**).
|
||||||
|
- Antes del **go-live** habrá una **prueba muy observada de prefacturación** (primera escritura real sobre BIND), paso a paso y validada.
|
||||||
|
- **Sesión con Pedro para el API de Jira** queda encaminada (se materializó el 27-jul, [#56](REGISTRO.md)).
|
||||||
|
- Johann: **montar el frontend**, conectarlo al backend ya construido (cliente BIND, auth, multi-tenant, BD) y hacer las pruebas.
|
||||||
|
|
||||||
|
> **Lectura estratégica:**
|
||||||
|
> - **Etapa 0 cerrada en la práctica** con validación entusiasta de los dueños del proceso (Ara y Arturo) y respaldo de Noe. El entregable de Etapa 0 quedó aprobado; el único hilo suelto es una **definición de proceso de Balam**, no una carencia del prototipo.
|
||||||
|
> - **"Cotización siempre" es una decisión firme** que simplifica la capa de escritura (B7): un solo camino cotización→prefactura→aprobación→timbre, sin ramas de excepción para recurrentes. Elimina ambigüedad que la agenda temía.
|
||||||
|
> - **PUE/PPD no se retomó** en esta sesión — sigue abierta desde [#52](REGISTRO.md) (1,374 facturas históricas todas PUE). Reintentarla con Arturo en la sesión del 28-jul o con el flujo Jira.
|
||||||
|
> - **Dos capacidades nuevas asomaron fuera del MVP core:** "programado para pago"/planeación financiera (Nafinsa) y el espacio "comercial" en Jira para generar cotizaciones. Ambas son candidatas a ampliación (horas extra), útiles para el pipeline comercial, pero **no deben inflar el MVP** — es justo lo que Noe pidió filtrar con el teamback.
|
||||||
|
> - **La prueba observada de prefacturación es el hito de riesgo** antes del go-live: es la primera escritura real sobre producción. Los candados acordados (dry-run, confirmación humana por factura, feature flag) son el seguro.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 56 · Jul 27, 2026 — mañana · Llamada · Sesión API de Jira con Pedro: token comprometido y bandeja "Facturación" (FAC) identificada ⭐
|
||||||
|
|
||||||
|
> Participan: Noe Rocha, Erika Chávez, Pedro Ayala (TI, dueño de Jira), Johann. Duración ~12 min. Transcripción: `../fuentes/2026-07-27 - API de conexión con JIRA Transcript.txt`. Es el Discovery de API que quedó encaminado el 22-jul ([#54](REGISTRO.md)) y ratificado el 24-jul ([#55](REGISTRO.md)).
|
||||||
|
|
||||||
|
**Objetivo (Noe):** el Discovery mostró que buena parte del proceso de facturación llega por Jira, conectividad que originalmente no se contempló y que ahora **sí será necesaria**. Pedro explica cómo se conecta hoy vía API.
|
||||||
|
|
||||||
|
**Lo que confirmó Pedro:**
|
||||||
|
- Usa el API de Jira para varios tableros de TI (limpieza de usuarios, aprobadores, conectores, SLAs). Funciones: crear y consultar actividades/tickets, aprobaciones, documentos adjuntos, cambio de estatus. **Es de lectura Y escritura — todo accionable** ("cualquier creación que se haría manual se puede hacer por API"). Cubre el CRUD que Johann necesita.
|
||||||
|
- **Token comprometido:** Pedro genera una API key llamada **"PAF" (Plataforma de Automatización Financiera)**, **expiración a 1 año — 27-jul-2027**. La envía **por correo en TXT** junto con la **documentación de desarrolladores de Atlassian** (informativa). Ya la tenía lista al cierre de la llamada.
|
||||||
|
- **Límites de consumo:** a diferencia de BIND (20K/día), Pedro cree que Jira **no tiene tope por día**; lo investigará en Atlassian y lo revisa con Johann. Noe quiere saber el límite de transacciones por escritura/consulta para planear la operación.
|
||||||
|
- Los tokens se generan desde la cuenta de Pedro, diferenciados por nombre; buena práctica rotarlos.
|
||||||
|
|
||||||
|
**Identificación de dónde consultar (aclaración importante):** no es "tablero" (eso es Power BI) sino el **Space** de Jira. Los tickets de facturación viven en la bandeja/space **"Facturación", con llave `FAC-`** + número. Otras bandejas: Administración General (AG), ITS (DITCM), Recursos Humanos (RH), Facturación (FAC), HD. **El API es global** (extrae de todas), pero la que importa es **Facturación**, que funciona como **bandeja "padre"**: procesos que se cierran en RH o Administración General **caen en Facturación**. Este es el insumo que Johann pidió el 24-jul para saber de dónde extraer los tickets.
|
||||||
|
|
||||||
|
**Próximos pasos acordados:**
|
||||||
|
- **Pedro** → enviar por correo: token TXT (PAF) + documentación Atlassian; investigar límites de consumo. → **CUMPLIDO el mismo día (ver seguimiento).**
|
||||||
|
- **Johann** → revisar la documentación de Jira/Atlassian; con la key, integrar la consulta al prototipo. Construir un **script de consulta** y también scripts de **creación/edición/eliminación** sobre un registro de prueba. Noe: no existe un CRUD previo por API; cuando llegue el momento se hace una **prueba en vivo** de creación. **Johann es el checkpoint** — avisa cuando necesite prueba o tenga dudas.
|
||||||
|
|
||||||
|
**Seguimiento — correo de Pedro del mismo día (27-jul, 7:40 am; CC Noe y Erika):** `../fuentes/2026-07-27 - Correo - Pedro medicion de consumo API Jira y token.md`. Johann acusó recibo ("Enterado, muchas gracias Pedro! Saludos").
|
||||||
|
- **No hay límite de volumen** ("X llamadas al mes") ni costo extra de licencia por usar la API. Hay **3 límites de velocidad en paralelo**, cualquiera devuelve **HTTP 429** (`RateLimit-Reason` indica cuál):
|
||||||
|
1. **Cuota por puntos/hora:** 1 punto base + 1 por objeto de dominio (issues, proyectos) o 2 por objeto de identidad (usuarios, grupos, roles); **las escrituras solo cobran el punto base**. Bolsa por defecto **65,000 puntos/hora**.
|
||||||
|
2. **Burst por segundo:** 100 req/s GET y POST, 50 PUT y DELETE, bucket por endpoint y por tenant. Consulta de clientes de service desk topada a **5/s**.
|
||||||
|
3. **Por issue en escrituras:** 20 ops/2 s y 100/30 s sobre un mismo ticket.
|
||||||
|
- **No existe dashboard de consumo** (limitante de Atlassian): consola de desarrolladores solo muestra el tier de apps propias; el detalle por token exige **Atlassian Guard Premium** (licencia aparte); las apps de Marketplace estiman por muestreo. **Única fuente confiable = headers de respuesta:** `X-RateLimit-Limit/Remaining/NearLimit` (NearLimit se activa con <20% de capacidad) y en 429 además `X-RateLimit-Reset`, `Retry-After`, `RateLimit-Reason`.
|
||||||
|
- **Token entregado** vía enlace de Google Drive (carpeta compartida). ⚠️ Tratar como credencial: mover a user-secrets/Key Vault, no dejar en texto plano.
|
||||||
|
|
||||||
|
> **Lectura estratégica:**
|
||||||
|
> - **Desbloqueo clave para B7/Jira→BIND:** con token de escritura a 1 año y la bandeja FAC identificada, se habilita tanto la lectura de tickets como la automatización Jira→cotización BIND estimada en ~10 h ([#51](REGISTRO.md)). El API confirmado como read+write despeja el mayor riesgo de la integración.
|
||||||
|
> - **"Facturación como bandeja padre"** es un dato de arquitectura relevante: consultar solo FAC puede no bastar si el disparador nace en RH/AG y cae en FAC — validar en las pruebas si conviene escuchar la bandeja padre o rastrear los procesos origen.
|
||||||
|
> - **Límite de consumo resuelto (correo 27-jul):** no hay tope mensual; 3 límites de velocidad (puntos 65k/h, burst/s, por-issue en escrituras) sin dashboard nativo → el sync debe **autorregularse leyendo los headers `X-RateLimit-*`** (pausar en NearLimit, respetar `Retry-After` en 429). **Las escrituras son baratas en puntos** (solo el base), lo que favorece la capa de escritura frente a las consultas de identidad (2 puntos c/u).
|
||||||
|
> - **Pendiente de proceso aún abierto:** qué estatus de Jira dispara qué (facturación vs cotización) sigue amarrado a la definición del martes 28-jul ([#55](REGISTRO.md)) — la conectividad ya está, la regla de negocio no.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 57 · Jul 21–27, 2026 · WhatsApp Erika ↔ Johann · coordinación de sesiones, envío del flujo de facturación y reagenda 23→24 jul
|
||||||
|
|
||||||
|
> Hilo de coordinación que **complementa** la parte de fondo del 21-jul ([#51](REGISTRO.md)) y enlaza la sesión de reglas del 22-jul ([#54](REGISTRO.md)), la validación del 24-jul ([#55](REGISTRO.md)) y la sesión de API de Jira del 27-jul ([#56](REGISTRO.md)). Transcripción completa (con imágenes inferidas): `../fuentes/2026-07-21 a 2026-07-27 - WhatsApp - Erika coordinacion sesiones validacion y API Jira.md`.
|
||||||
|
|
||||||
|
**21-jul (tarde):** Johann propuso compartir las horas por Excel mientras se habilita Jira; Erika pidió el formato **entregable + actividad + horas por etapa**. Erika cuestionó si lo de las cotizaciones ya estaba (📷 captura) → Johann aclaró que **crear cotizaciones sí está en el MVP** y que lo nuevo (Jira genere la cotización en BIND automáticamente) requeriría acceso técnico a Jira + escritura controlada en BIND. Erika confirmó por su cuenta que Jira dice **"fuera de alcance"**.
|
||||||
|
|
||||||
|
**22-jul:** Erika se disculpó por **no asistir a la sesión de reglas** de esa mañana ([#54]; avisó a los ingenieros para que la tomaran igual). Preguntó **dos veces si se ajustará la propuesta** por el cambio de la integración Jira → **compromiso de Johann: enviar el conteo de horas + la propuesta ajustada "entre hoy y mañana" (para el 23-jul).** Erika propuso la **sesión de API de Jira** con Noe y Pedro y confirmó que **ya envió por correo el flujo de facturación de Jira** (2:26 pm).
|
||||||
|
|
||||||
|
**23-jul (reagenda):** por un tema personal de **Araceli** (dueña del proceso), la **validación del prototipo** se movió de jueves 23-jul 7 am a **viernes 24-jul 7 am**. La **sesión de API de Jira** se movió de viernes 24-jul 7 am → 7 pm → finalmente **lunes 27-jul 7 am** por disponibilidad de Pedro. Erika preguntó si "esta actividad se acabó ayer" (📷 captura del plan) → Johann: sí, **falta la prueba de escritura en BIND** pero ya está.
|
||||||
|
|
||||||
|
**24-jul:** intercambio de arranque; se conectan a la sesión de validación ([#55]).
|
||||||
|
|
||||||
|
**27-jul (mañana):** Erika pidió **los avances de la Etapa 1**; Johann preguntó si **ya existe el Jira del proyecto** — justo antes de la sesión de API donde Pedro compromete el token PAF ([#56]).
|
||||||
|
|
||||||
|
**Pendientes que deja el hilo:**
|
||||||
|
- [ ] Johann — **enviar conteo de horas (Excel: entregable + actividad + horas por etapa) + propuesta ajustada** por la integración Jira→BIND. ⚠️ Comprometido para el 23-jul — **verificar si ya se envió.**
|
||||||
|
- [ ] Johann — **entregar avances de Etapa 1** solicitados el 27-jul.
|
||||||
|
- [ ] Conservar el **correo del flujo de facturación de Jira** (Erika, 22-jul) como evidencia/insumo.
|
||||||
|
|
||||||
|
> **Nota de imágenes:** las capturas no vienen en el volcado; se infieren tres adjuntos por los deícticos ("esto", "esta actividad") — cotizaciones (21-jul 12:58), "fuera de alcance" (21-jul 1:03, confianza media) y actividad del plan (23-jul 11:29). Detalle en el archivo de `fuentes`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 58 · Jul 27–28, 2026 · WhatsApp Erika ↔ Johann · avance de Etapa 1 entregado, Excel de horas para FACTURACIÓN y Jira aún sin licencia ⭐
|
||||||
|
|
||||||
|
> Continuación de [#57](REGISTRO.md). Registrado a partir de capturas de pantalla del hilo (4 imágenes). Cubre el envío del avance del 27-jul y los pedidos de Erika del 27 (tarde) y 28 (mañana).
|
||||||
|
|
||||||
|
**27-jul, mañana — avance de Etapa 1 entregado por mensaje:** Erika pidió los avances de Etapa 1 (📷 captura del plan, 7:11 am). Johann preguntó si ya existe **un Jira para el seguimiento de este proyecto** —es decir, dónde llevar sus actividades y horas, no la instancia a integrar— (7:14 am) → **respuesta de Erika (7:54 am): "aún estamos viendo lo de alguna licencia... pero estamos viendo una segunda opción y no Jira precisamente".** Johann ofreció trabajar en un Excel compartido; Erika contestó que le comentó a Pedro que **pudiera ser un Drive para subir la documentación, pero están en espera de la validación** (8:57 am).
|
||||||
|
|
||||||
|
> ⚠️ **Precisión importante:** este intercambio es sobre la **herramienta de gestión del proyecto** (dónde se registran actividades y horas de Johann), NO sobre la integración técnica. El Jira a integrar es la **instancia productiva de TI que administra Pedro** —bandeja FAC, token PAF a 1 año, API read+write confirmada ([#56](REGISTRO.md))— y ese no está en duda. Consecuencia práctica: **mientras no haya herramienta de gestión, el Excel de horas de Johann ES el sistema de seguimiento del proyecto**, lo que explica la insistencia de Erika en el desglose por etapa.
|
||||||
|
|
||||||
|
Johann envió el **avance por actividad en texto** (8:12 am), con 👍 de Erika y "gracias" (8:58 am):
|
||||||
|
> ✅ Sincronización de clientes — hecho y verificado · ✅ Sincronización de facturas/cotizaciones — hecho, 1,494 facturas cuadran · ✅ Modelo + migraciones + datos de prueba — hecho · ⏸️ Capa de escritura — en la junta del viernes comentaron que iban a definir el flujo de facturación; en cuanto quede, la implemento (Balam) · ⏳ Config. Azure (+ staging) — espera el acceso a Azure de Pedro/Noé (Balam) · ⏳ CI/CD + secretos — depende de Azure (Balam)
|
||||||
|
|
||||||
|
**27-jul, tarde — la fila de escritura y la sesión del proceso:** Erika preguntó (3:35 pm) si la capa de escritura *"es la que está pendiente de lo que se mencionó el viernes de que validarían el flujo"* → Johann confirmó. Erika: **"es que lo tengo al 100%"** (3:40 pm) → **Johann corrigió: "lo volví a marcar como pendiente porque se iba a validar el proceso"** y preguntó cuándo podrían tener la junta revisando el proceso validado (3:44–3:45 pm). Erika: le comentó a Arturo, y como **Ara sale de viaje**, mañana en la oficina ve cuándo **revisarlo internamente y después pasarlo con Johann**; queda en avisar (3:49 pm). ⚠️ **La sesión del 28-jul no quedó confirmada — se movió a revisión interna de Balam primero.**
|
||||||
|
|
||||||
|
**27-jul, 4:59 pm — pedido formal de horas:** *"johan un favor, me puedes pasar en un excel tus horas que llevas de cada etapa por favor"* → **Johann envió `Avance-Balam-2026-07-27.xlsx` a las 6:45 pm** (versión simplificada). Johann ofreció además pasar un Excel para las siguientes etapas; Erika (7:57 pm): *"Y las ya completas más que nada para ver cuántas son"*.
|
||||||
|
|
||||||
|
**28-jul, mañana — el propósito del Excel y dos pendientes:**
|
||||||
|
1. **(8:36 am) ⭐ "es para llevar el conteo de horas de cada etapa sobre todo para la FACTURACIÓN correspondiente de la misma."** → El Excel de horas es el insumo de facturación, no solo seguimiento.
|
||||||
|
2. **(9:12 am) "johann adicional, ¿pasaste la propuesta con lo adicional de JIRA que no estaba contemplado?"** → Johann (10:58 am): **"Aún no la comparto, te la paso a la brevedad."** ⚠️ Es el compromiso que viene arrastrándose desde el 22-jul ([#57](REGISTRO.md)).
|
||||||
|
3. **(9:27 am)** Sobre Azure: Johann preguntó si el acceso lo iba a entregar Noé o Pedro → Erika: **"Sip"** + *"deja pregunto"* (11:01 am). Johann pidió contexto de qué se requiere específicamente de Azure (11:19 am).
|
||||||
|
|
||||||
|
**Lecturas clave:**
|
||||||
|
> - **El Excel de horas es documento de facturación** (dicho por Erika, 28-jul 8:36 am). Cambia su naturaleza: ya no es reporte de avance, es sustento de cobro. Refuerza que las 41.58 h estén validadas y trazables.
|
||||||
|
> - **Erika creía la capa de escritura al 100%** y Johann la corrigió a pendiente. Buena decisión: si se hubiera dejado al 100%, la definición del proceso habría reabierto una actividad cerrada. Valida el criterio de reportar 40% y no más.
|
||||||
|
> - **Sin herramienta de gestión para el proyecto** (licencia en revisión; Erika evalúa una alternativa a Jira para ese fin). **No afecta la integración técnica** —esa va contra la instancia de TI de Pedro, ya disponible—, pero sí significa que **el Excel de horas de Johann es el registro oficial de seguimiento** y el insumo de facturación. Mantenerlo trazable y al día no es opcional.
|
||||||
|
> - **La propuesta ajustada por Jira sigue sin enviarse** (comprometida el 22-jul, re-preguntada el 28-jul). Es el pendiente comercial más viejo del hilo y ahora Erika lo pide por segunda vez.
|
||||||
|
> - **La sesión del flujo de facturación no está agendada:** Balam la revisa internamente primero (Ara de viaje). El desbloqueo de B7 se corre de fecha.
|
||||||
|
|
||||||
|
**Pendientes que deja el hilo:**
|
||||||
|
- [ ] ⚠️🔥 Johann — **enviar la propuesta/adenda ajustada por la integración Jira→BIND** (comprometida 22-jul, re-preguntada 28-jul 9:12 am). Estimación ya comunicada: ~10 h ([#51](REGISTRO.md)).
|
||||||
|
- [ ] Johann — **enviar el Excel de horas de las etapas siguientes** (2, 3) que Erika pidió el 27-jul 6:46 pm ("las ya completas más que nada para ver cuántas son").
|
||||||
|
- [ ] Balam/Erika — **avisar fecha de la sesión del flujo de facturación** tras su revisión interna. Desbloquea B7.
|
||||||
|
- [ ] Balam/Erika — **definir la herramienta de gestión del proyecto** (licencia en revisión). Mientras no exista, el Excel de Johann es el registro oficial.
|
||||||
|
- [ ] Balam/Pedro/Noé — **entregar el acceso a Azure**; Erika iba a preguntar (28-jul 11:01 am). Johann pidió el contexto de lo que se requiere.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 59 · Jul 30 – Ago 10, 2026 · Correo · Balam define el flujo Jira→BIND: disparador, campos y prueba acompañada ⭐🔥
|
||||||
|
|
||||||
|
> Hilo `RV: Seguimiento: medición de consumo API de Jira y accesos` (fuente original conservada localmente; no versionada porque contiene cabeceras y adjuntos completos del correo). Continúa [#56](REGISTRO.md). Es **la definición del flujo de facturación que estaba bloqueando B7 desde el 24-jul** ([#55](REGISTRO.md), [#58](REGISTRO.md)).
|
||||||
|
|
||||||
|
**Cronología (8 días de latencia):**
|
||||||
|
|
||||||
|
| Fecha | Movimiento |
|
||||||
|
|---|---|
|
||||||
|
| 30-jul 10:28–11:44 am | Pedro confirma su correo, **regenera el token** y comparte la URL `https://balam-jsm-temp.atlassian.net`. Cierra el 401 del 29-jul. |
|
||||||
|
| 30-jul 1:07 pm | **Johann pregunta las 4 definiciones** (disparador, alcance de request types, fuente de monto/conceptos, y FACTEST vs prueba acompañada) + reporta el hallazgo de que el monto no está en campos estructurados. |
|
||||||
|
| 3-ago 4:58 pm | Noé **escala internamente** a Araceli y Arturo (CC Pedro), reconociendo el cambio de propósito: *"el flujo de JIRA tenía un propósito claro, validar la actividad y la evidencia, ahora cambiar a ejecutar y automatizar actividades con el desarrollo"*. Entra **Araceli Sánchez** al hilo. |
|
||||||
|
| 3-ago 4:59 pm | Noé a Johann: *"Estamos definiendo para darte respuesta mañana mismo"*. |
|
||||||
|
| 4-ago 12:15 pm | **Arturo responde en verde** sobre los tres puntos + adjunta captura del formulario y del filtro de request types. |
|
||||||
|
| 7-ago 5:34 pm | Noé reenvía a Johann y **contesta la cuarta**: prueba acompañada. |
|
||||||
|
|
||||||
|
**Las 4 definiciones, cerradas:**
|
||||||
|
|
||||||
|
1. ⭐ **La cotización BIND se genera al entrar a `En proceso de facturación`.** Y: *"por el momento todo se tramitará como facturación normal, hasta el explorar el módulo de proyectos en BIND"* → **el caso de facturación recurrente queda fuera de alcance por ahora.**
|
||||||
|
2. ⭐ **Se observan TODOS los request types** de la bandeja de facturación, no solo `Facturación adicional`.
|
||||||
|
3. ⭐ **Monto y conceptos van como campos de Jira**, no del adjunto. Noé: *"resta rapidez, pero garantiza consistencia de datos"*. Arturo: *"de acuerdo, deben ser los campos de JIRA (omitir el campo recurrencia)"*.
|
||||||
|
4. ⭐ **No habrá proyecto `FACTEST`:** *"Preferimos una prueba acompañada sobre un ticket"* (Noé).
|
||||||
|
|
||||||
|
**Verificación por API el 10-ago (solo lectura; detalle en `../planeacion/Investigacion-API-Jira.md`):**
|
||||||
|
|
||||||
|
- ✅ **El acuerdo ya está implementado en Jira:** `Monto sin IVA` existe como `customfield_11556` (number/float, obligatorio) en el request type 83. Desapareció `Periodo de Incidencias`.
|
||||||
|
- 🔴 **Pero solo 2 de 69 tickets lo tienen poblado** — `FAC-100` y `FAC-101`, ambos creados el **4-ago**, el mismo día de la respuesta de Arturo. Sin histórico contra el cual validar el mapeo.
|
||||||
|
- 🔴 **`conceptos`/partidas NO existe como campo en todo el sitio.** El catálogo completo solo tiene `Monto sin IVA` (11556), `Monto con IVA` (11522, sin usar en el formulario) y `Total forms` (de JSM). **El acuerdo resolvió el monto pero no los conceptos.**
|
||||||
|
- 🔴 **"Todos los request types" incluye tickets sin request type:** el filtro de la bandeja muestra 6 opciones (las 4 del portal + `Empty` + `Emailed request`). **10 de 69 tickets no tienen request type ni un solo campo estructurado**; nacen de `Automation for Jira` desde HH/SA/IN/RH, con los datos codificados en el `summary`. **No son ruido:** `FAC-91` es uno de ellos y está vivo en `En validación nacional` (`[FACTURA COMPLETA - Staff Augmentation] Axians — RH-36`).
|
||||||
|
- 🔴 **El estatus disparador dura minutos.** `FAC-100` estuvo **2m44s** en `En proceso de facturación` (11:51:25 → 11:54:09 del 5-ago) y 25 min de punta a punta. Solo 1 de 69 tickets está parado ahí hoy. **Un poller que consulte el estatus actual se pierde la mayoría de los disparos → la detección por changelog pasa de preferencia a requisito.**
|
||||||
|
- ⭐ **Hallazgo nuevo: el workflow tiene aprobaciones de JSM**, con **Araceli Sánchez** como aprobadora en las ramas de validación nacional/extranjera (`customfield_10003`/`10025`). El paso a `Facturado` es una aprobación formal, no una transición simple. La plataforma no debe aprobar nunca.
|
||||||
|
- ✅ `FAC-100` cierra con `status = Facturado` **y** `resolution = Done` consistentes (resuelve la trampa documentada el 30-jul).
|
||||||
|
- ✅ Headers de rate limit confirmados: `X-RateLimit-Limit: 350`, `X-RateLimit-Remaining: 348`, más los estándar `RateLimit-Policy`/`RateLimit`. **El límite observado es 350, no los 100/s que mencionó Pedro** → leer el header, no hardcodear.
|
||||||
|
- ✅ El origen es **creación por Automation, no move entre proyectos**: sin cambios de `project`/`key` en los changelogs, con `issuelinks` al ticket origen. **Escuchar FAC basta.**
|
||||||
|
- ⚠️ `Facturación recurrente` sigue siendo obligatorio y se está llenando (`FAC-100`: `Si`, periodo `12`) pese al acuerdo de omitirlo.
|
||||||
|
- ⚠️ `Nombre del Cliente` es **ADF**, no string, y **no trae RFC** — la llave de match contra BIND queda en el nombre escrito a mano.
|
||||||
|
- ⚠️ **ACUNTIA ya tiene caso real:** `FAC-100` es `Julio/CONSULTORIA Y SERVICIOS (Eduardo Fiallo CEMEXUSA)/ACUNTIA`, `Helmstone`, USD, 45 días, **un solo `Monto sin IVA` de 7,594.00** — para el ticket que históricamente representa 40–48 facturas ([#55](REGISTRO.md) punto 6).
|
||||||
|
|
||||||
|
> **Lectura estratégica:**
|
||||||
|
> - **B7 queda desbloqueado en su mayor parte.** El disparador está definido y el campo de monto ya existe: se puede implementar lectura, detección por changelog, parser de ADF y mapeo. **Lo único que sigue bloqueado es el armado final de la cotización**, por la definición de conceptos.
|
||||||
|
> - **El acuerdo del 4-ago resolvió el monto y olvidó los conceptos.** Es el hueco más caro del hilo: sin partidas, la cotización BIND sale de una sola línea. Puede ser aceptable, pero tiene que ser una decisión explícita de Balam, no un default silencioso.
|
||||||
|
> - **"Todos los request types" no es implementable literalmente.** ~14% de los tickets no tiene datos con los que armar nada. Hay que acotar el alcance por escrito o pedir que la regla de Automation propague los campos — y eso es trabajo de Balam.
|
||||||
|
> - **Sin `FACTEST`, el sandbox propio deja de ser opcional.** Se escribirá en producción una sola vez, acompañado; todo el desarrollo previo tiene que ocurrir en un sitio Jira Cloud gratuito propio.
|
||||||
|
> - **8 días de latencia en la definición**, con el agravante de que la pregunta se hizo el 30-jul y el escalamiento interno no arrancó hasta el 3-ago. Suma al patrón ya documentado: la definición del flujo lleva bloqueando desde el 24-jul.
|
||||||
|
> - **Araceli entra al hilo como aprobadora**, tanto en el correo como en el workflow de Jira. Es un interlocutor nuevo para las definiciones que faltan.
|
||||||
|
> - **PUE/PPD gana evidencia:** hay tickets `Seguimiento del Ticket con pago anticipado:`, lo que sugiere casos PUE reales y refuerza la pregunta que lleva cuatro sesiones sin hacerse ([#52](REGISTRO.md)).
|
||||||
|
|
||||||
|
**Respuesta de Johann — ENVIADA el 10-ago, tarde.** Texto completo y borradores previos en `../planeacion/Correo-respuesta-Noe-2026-08-10.md`:
|
||||||
|
|
||||||
|
> Buenas tardes, espero se encuentren bien.
|
||||||
|
> Gracias por las definiciones, quedan claras y ya retomé el desarrollo, ya validé que el campo "Monto sin IVA" está creado y funcionando.
|
||||||
|
> Para aterrizar los detalles que faltan sería de ayuda que hoy o mañana se cree un ticket de ejemplo capturado "como debería ser", con todos los campos llenos tal como quieren que se capture de aquí en adelante. Contra ese molde llego a la sesión con el flujo ya funcionando.
|
||||||
|
> Para la sesión, ¿les funciona el miércoles 12 o el jueves 13, a las 7:00 am?
|
||||||
|
> El ejercicio termina en la cotización en BIND.
|
||||||
|
> Sigo pendiente del acceso a Azure para publicar el ambiente.
|
||||||
|
> Quedo atento. Saludos.
|
||||||
|
|
||||||
|
**Decisión de enfoque:** en lugar de resolver los tres huecos con otra ronda de correos, se pide **un ticket de ejemplo capturado "como debería ser"**. Balam ya está adoptando el llenado completo de campos, así que el molde resuelve por evidencia lo que la prosa tardaría dos semanas en definir, y permite preparar el script contra datos reales para llegar a la sesión con el flujo funcionando.
|
||||||
|
|
||||||
|
**La tarea se dejó deliberadamente sin asignar** ("sería de ayuda que se cree"), para que Balam la ruteé internamente: Arturo no estaba en el CC del correo de Noé, y Noé ya venía haciendo ese papel de router desde el escalamiento del 3-ago. Nombrar a un ausente habría sido peor que dejarlo abierto.
|
||||||
|
|
||||||
|
**Alcance del ejercicio fijado: termina en la COTIZACIÓN, no en la prefactura.** Razones: es lo que contestó Arturo el 4-ago; la aprobación de Araceli va entre `En proceso de facturación` y `Facturado`, así que la prefactura pertenece después; Arturo pidió gradualismo explícito el 24-jul; y una cotización se cancela sin efecto fiscal, mientras que en BIND la prefactura es un registro en `Invoices` con `UUID = null` y los shapes de los POST nunca se han probado. Respeta los tres candados de Noé: **cotización → prefactura → aprobación humana → timbrado** ([#55](REGISTRO.md)).
|
||||||
|
|
||||||
|
**Lo que el correo final dejó fuera y ahora se resuelve verbalmente en la sesión** (sin constancia escrita previa):
|
||||||
|
- Que el ejercicio **genera una cotización real en BIND producción** y que se cancela al terminar. **Anunciarlo al abrir la sesión, antes de ejecutar.**
|
||||||
|
- El compromiso de **compartir el script antes** (condición de Noé del 24-jul). Mandarlo por separado cuando esté listo.
|
||||||
|
- Que el **disparo es manual** en esta prueba (pipeline `Draft→DryRun→Confirmed` de ADR-001 con flags apagados) y que la versión automática **depende de Azure**.
|
||||||
|
- **ACUNTIA** y el **supuesto de los tickets de `Automation for Jira`**.
|
||||||
|
|
||||||
|
**Pendientes que deja el hilo:**
|
||||||
|
- [x] ~~🔥 Johann — **responder el correo**~~ → **Enviado el 10-ago** (texto arriba).
|
||||||
|
- [ ] 🔴 Balam/Arturo — **definir de dónde salen los conceptos/partidas** de la cotización.
|
||||||
|
- [ ] 🔴 Balam/Arturo — **decidir qué pasa con los tickets creados por Automation** (10 de 69, sin campos).
|
||||||
|
- [ ] 🔴 Balam/Arturo — **confirmar si ACUNTIA es un ticket → una cotización** o sigue siendo 40–48 facturas.
|
||||||
|
- [ ] Balam/Noé — **fecha para la prueba acompañada** de escritura.
|
||||||
|
- [ ] Balam — **¿nacional vs extranjero cambia la cotización** o solo el paquete de salida?
|
||||||
|
- [ ] Balam/Pedro — **cuenta de servicio** en vez de la cuenta personal de Pedro (planteado el 29-jul, sin respuesta).
|
||||||
|
- [ ] Johann — **levantar el sandbox Jira Cloud propio** (plan Free) para desarrollar sin tocar producción.
|
||||||
|
- [ ] Johann — mover el token de Jira a **user-secrets/Key Vault** y borrarlo de disco (ya está gitignoreado).
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -586,6 +1196,7 @@ Tras la sesión de Discovery (7–8 am, [#27](#27--jul-6-2026--700-am--llamada--
|
|||||||
- **Lista blanca cobranza:** ~~ACUNTIA + top 3, configurable~~ → **ELIMINADA (6-jul):** Ara decidió recordatorios a **todos** los clientes morosos, sin excepciones. (La capacidad configurable se conserva, hoy vacía.)
|
- **Lista blanca cobranza:** ~~ACUNTIA + top 3, configurable~~ → **ELIMINADA (6-jul):** Ara decidió recordatorios a **todos** los clientes morosos, sin excepciones. (La capacidad configurable se conserva, hoy vacía.)
|
||||||
- **Cotización BIND obligatoria (6-jul):** todo lo que se facture parte de una cotización en el ERP (requisito fijado por Ara).
|
- **Cotización BIND obligatoria (6-jul):** todo lo que se facture parte de una cotización en el ERP (requisito fijado por Ara).
|
||||||
- **Flujo actual:** Jira ITSM mandatorio (~1 mes) → validación administración (Arturo) → prefactura BIND → CFDI → envío por correo con particularidades por cliente (Excel pendiente de Ara/Arturo).
|
- **Flujo actual:** Jira ITSM mandatorio (~1 mes) → validación administración (Arturo) → prefactura BIND → CFDI → envío por correo con particularidades por cliente (Excel pendiente de Ara/Arturo).
|
||||||
|
- **Cobranza actual (mapeada 7-jul):** el cliente avisa el pago por correo/mensaje (estado de cuenta) → el **despacho contable** registra los pagos en BIND **uno por uno** → Power BI de cartera (desactualizado). Se concilia **por folio** (la referencia no distingue facturas); los **fees** generan diferencias contra el total. Quieren **recordatorios proactivos** (1–5 días de vencimiento).
|
||||||
- **Book = SaaS** (no interno).
|
- **Book = SaaS** (no interno).
|
||||||
- **Jira:** solo gestión de proyectos con clientes.
|
- **Jira:** solo gestión de proyectos con clientes.
|
||||||
- **Nube preferida:** Azure.
|
- **Nube preferida:** Azure.
|
||||||
@@ -601,20 +1212,25 @@ Tras la sesión de Discovery (7–8 am, [#27](#27--jul-6-2026--700-am--llamada--
|
|||||||
1. **Llamada del 19-may** (transcript aparte): Noe pidió textualmente *"no le queremos estar poniendo estrellitas al pino, nada más estrictamente lo que se necesita"*. Foco = facturación + conciliación.
|
1. **Llamada del 19-may** (transcript aparte): Noe pidió textualmente *"no le queremos estar poniendo estrellitas al pino, nada más estrictamente lo que se necesita"*. Foco = facturación + conciliación.
|
||||||
2. **Correo del 25-may**: Noe pide explícitamente que la **propuesta se recorte a "solo ERP BIND" primero**, dejando bancos/conciliación para fase posterior.
|
2. **Correo del 25-may**: Noe pide explícitamente que la **propuesta se recorte a "solo ERP BIND" primero**, dejando bancos/conciliación para fase posterior.
|
||||||
|
|
||||||
### Estado actual (al 6-jul-2026 — Discovery en marcha)
|
### Estado actual (al 15-jul-2026 — Etapa 0 en validación, Etapa 1 iniciada en local)
|
||||||
**Contrato FIRMADO (26-jun)**, **kickoff realizado (1-jul)** y **primera sesión de Discovery realizada (6-jul, 7am, con Ara + Arturo — [#27](#27--jul-6-2026--700-am--llamada--discovery-proceso-actual-de-facturación-y-cobranza-)).** El proceso actual quedó mapeado end-to-end: **Jira ITSM (mandatorio) → validación administración → prefactura BIND → CFDI → envío por correo con particularidades por cliente**. Dos reglas cambiaron en la sesión: **lista blanca eliminada** (recordatorios a todos) y **cotización BIND obligatoria** como inicio del flujo. Ara/Arturo deben enviar hoy el **Excel de particularidades de envío**; Johann trabaja el **prototipo** con el flujo real. **Propuesta v1.2 enviada (2-jul)** — en espera de confirmación de Balam para emitir la factura de 30 h. **Token de BIND sigue pendiente** (no se tocó en la sesión).
|
|
||||||
|
|
||||||
**Definido en el kickoff:** token **de Arturo** para BIND (solo consulta; se prueba primero) · **Balam crea el repo** (GitHub privado) y **gestiona Azure** (Pedro+Noé) · Johann reporta **horas en Jira** (Pedro le enseña; corte de Erika los lunes) · Discovery **con Arturo + Araceli** (Erika agenda). **Dos bloqueadores vivos:** (1) **accesos** (token BIND, Azure, repo) y (2) **emisión de la 1ª factura** — Johann ya envió la propuesta v1.2 ajustada (2-jul); **no factura hasta que Balam confirme por correo**.
|
**Contrato firmado, kickoff y Discovery completos.** El prototipo de la Etapa 0 fue entregado por correo el 10-jul y Balam lo revisa internamente. La sesión formal de validación quedó confirmada para el **jueves 23-jul a las 7:00 pm**.
|
||||||
|
|
||||||
|
Johann inició en local las actividades independientes de Etapa 1. La sesión para cerrar dudas y reglas quedó el **miércoles 22-jul** con Arturo y la CEO. Deben resolverse alta de cliente nuevo, tratamiento del fee, particularidades de envío y si la automatización **Jira → cotización BIND** se incorpora al alcance.
|
||||||
|
|
||||||
|
**Accesos:** token BIND y manual de marca recibidos; repositorio GitHub autorizado por Noé y en proceso de invitación para `Johann-28`; Azure programado para la semana del 21-jul; CI/CD y secretos separados para el 24-jul. Jira técnico no se necesita salvo que se apruebe la integración automática Jira→BIND.
|
||||||
|
|
||||||
|
**Comercial:** propuesta v1.2 recibida por Erika, pero la confirmación para emitir la factura de 30 h sigue pendiente mientras Noé y la CEO están fuera del país. No hay rechazo ni pago vencido.
|
||||||
|
|
||||||
|
**Seguimiento:** se abrió control local de horas en `../planeacion/Seguimiento-horas.csv`. El Excel de cobranza/días vencidos queda condicionado a la retroalimentación del prototipo; el Excel de particularidades de envío sigue pendiente de Balam.
|
||||||
|
|
||||||
**Términos vinculantes del contrato:** sin anticipo + firma previa (cumplida) · **facturación semanal los viernes** por horas efectivamente trabajadas, pago a 30 días · garantía 45 días · alcance MVP BIND-first · arranque condicionado a accesos + API de BIND con lectura/escritura.
|
**Términos vinculantes del contrato:** sin anticipo + firma previa (cumplida) · **facturación semanal los viernes** por horas efectivamente trabajadas, pago a 30 días · garantía 45 días · alcance MVP BIND-first · arranque condicionado a accesos + API de BIND con lectura/escritura.
|
||||||
|
|
||||||
**Calendario tentativo:** kickoff 1-jul · Etapa 0 (Discovery) sem del 6-jul · Etapa 1 13–24 jul · Etapa 2 27-jul–7-ago · Etapa 3 10–21 ago. Las fechas se confirman/afinan al cerrar el Discovery (dependen de los accesos).
|
**Calendario vigente:** Etapa 1 en local desde 13-jul · repo 15–16 jul · Azure semana del 21-jul · sesión de reglas 22-jul · validación prototipo 23-jul · CI/CD y secretos 24-jul. Etapas 2–3 se afinan después de esas validaciones.
|
||||||
|
|
||||||
**Acciones inmediatas de Johann:**
|
**Acciones inmediatas de Johann:** capturar/reconstruir horas reales; aceptar y validar el repositorio; continuar backend local; preparar las preguntas del 22-jul; cerrar hallazgos/ADRs tras las sesiones; mantener lista la factura sin emitirla todavía.
|
||||||
- **Asistir al kickoff (1-jul, 7am)** con material listo (agenda + lista de accesos a pedir + preguntas de Discovery).
|
|
||||||
- Cambiar régimen fiscal (para facturar) y confirmar permisos exactos de Azure.
|
|
||||||
|
|
||||||
**En espera de Balam:** entregar **accesos** (API BIND vía cuenta maestra ARA / llave de Arturo, Azure con Guajardo/Erika, manual de marca de Pedro) y reglas de negocio + bancos con Arturo. El **Discovery arranca** una vez recibidos.
|
**En espera de Balam:** invitación al repo, acceso Azure, flujo Jira para horas, Excel de particularidades de envío, definiciones de Arturo/CEO y confirmación para emitir la primera factura.
|
||||||
|
|
||||||
**Pendiente menor:** (opcional) pedir copia limpia del contrato — la cláusula de Firma Electrónica de la última página quedó duplicada y aún dice "EL PATRÓN" (residuo de plantilla, bajo riesgo).
|
**Pendiente menor:** (opcional) pedir copia limpia del contrato — la cláusula de Firma Electrónica de la última página quedó duplicada y aún dice "EL PATRÓN" (residuo de plantilla, bajo riesgo).
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,506 @@
|
|||||||
|
Validación de prototipo etapa 0- proyecto integración IA Balam
|
||||||
|
Fri, Jul 24, 2026
|
||||||
|
|
||||||
|
10:44 - Arturo Rosas Hernandez
|
||||||
|
Está tranquila, pero constante.
|
||||||
|
|
||||||
|
10:46 - Arturo Rosas Hernandez
|
||||||
|
No ha dejado de llover. No sé, por allá. Por aquí sí está. Mucho, mucho, mucha agua. Pero de a poquito. ¿Qué tal, Araceli?
|
||||||
|
|
||||||
|
10:57 - Unidentified Speaker
|
||||||
|
Buenos días.
|
||||||
|
|
||||||
|
10:58 - Unidentified Speaker
|
||||||
|
¿Cómo estás?
|
||||||
|
|
||||||
|
10:59 - Unidentified Speaker
|
||||||
|
Buenos días, Karol.
|
||||||
|
|
||||||
|
11:01 - Unidentified Speaker
|
||||||
|
Buenos días.
|
||||||
|
|
||||||
|
11:02 - Araceli Sanchez Jimenez
|
||||||
|
Ya, ya sé.
|
||||||
|
|
||||||
|
11:03 - Araceli Sanchez Jimenez
|
||||||
|
Buenos días.
|
||||||
|
|
||||||
|
11:04 - Araceli Sanchez Jimenez
|
||||||
|
Oigan, Noe. Ah, ya está. Bueno. Listo. Ya estamos todos listos. ¿Listo?
|
||||||
|
|
||||||
|
11:10 - Erika Chavez
|
||||||
|
Bueno, este, esta sesión es para ver lo de la validación de la etapa cero, que es el prototipo navegable que aquí mi compañero Johann realizó.
|
||||||
|
|
||||||
|
11:24 - Johann
|
||||||
|
Entonces, el foro es todo, Johann, adelante. De acuerdo, voy a compartir pantalla y voy a presentar el prototipo. Cualquier duda me pueden preguntar. Gracias. Aquí pueden ver mi pantalla.
|
||||||
|
|
||||||
|
11:40 - Unidentified Speaker
|
||||||
|
Sí, ya.
|
||||||
|
|
||||||
|
11:41 - Johann
|
||||||
|
Ok, les voy a presentar el PDF que les compartí, en donde hay capturas del prototipo y una pequeña descripción. Voy a ir explicando cómo es cada pantalla y el proceso que se reflejó en este prototipo. Entonces, este proceso consta de cinco pasos en cuanto a la facturación. Todo comienza en una solicitud en Jira. Después de la solicitud en Jira, se pasa a una cotización en Bind, pasa a prefactura y, antes del CFDI y la facturación, necesita una aprobación humana. Hay algunas reglas que se levantaron durante el Discovery, durante las sesiones, como, por ejemplo, el PPD por defecto, y PUE solo consciente. También la regla prefactura que siempre va a estar antes de timbrar, el IVA de 16% nacional, la cotización al principio de manera obligatoria y un recordatorio interno de monosías de los clientes. Esto es para el proceso de facturación.
|
||||||
|
|
||||||
|
12:54 - Noe Rocha
|
||||||
|
Estamos hablando del proceso de facturación, el primero que se está abordando como parte de la automatización que se está buscándose las diferentes fuentes de información en un punto único de ejecución.
|
||||||
|
|
||||||
|
13:12 - Unidentified Speaker
|
||||||
|
Así es.
|
||||||
|
|
||||||
|
13:13 - Johann
|
||||||
|
Comenzamos por esa pantalla que es la de iniciación. De esta manera, cada persona logueada verá las facturas que le corresponden y podrá ver todo lo relacionado a su cuenta.
|
||||||
|
|
||||||
|
13:30 - Araceli Sanchez Jimenez
|
||||||
|
Aquí ya tengo una pregunta. Aquí en el inicio de sesión, me imagino que hay un solo usuario. O sea, todos vamos a tener, bueno, o los que demos acceso aquí, vamos a ser administradores, ¿no? O sea, no va a haber privilegios. Es nada más, o sea, yo tengo y si yo entro o Juanito o Pedrito, vamos a ver absolutamente todos lo mismo.
|
||||||
|
|
||||||
|
13:57 - Johann
|
||||||
|
Aquí, por ejemplo, como a ustedes les convenga más, necesitan que un usuario tenga más acceso que otro, ya está preparada la plataforma para los roles. Ah, super. OK. Perfecto. Muchas gracias, Johann.
|
||||||
|
|
||||||
|
14:09 - Araceli Sanchez Jimenez
|
||||||
|
O sea, de momento, como somos nada más dos personas que llevamos todo este tema, digo, podemos ver todo. Pero yo nada más viendo a lo mejor en algún momento que a lo mejor tengamos un analista que vea cierto punto, pues no necesita ver todo lo demás, ¿verdad? Es nada más un tema de cultura general para saber qué Ahora comenzamos con este apartado del dashboard, en donde se muestran estos cuatro valores en forma de cartas y que se necesita atención.
|
||||||
|
|
||||||
|
14:42 - Johann
|
||||||
|
Entonces, en esta pantalla se busca que estén a la mano los datos, el resumen de los datos de las facturas y la cartilla de datos. Como por ejemplo, facturado en julio en pesos mexicanos y dólares, cuántas facturas emitidas hay y cuántas pendientes, cuánto, qué cantidad hay en cartera vencida. También aquí hay algunos ítems de qué se necesita atención y qué responsable hay y cuánto tiempo tiene de antigüedad. Además, aquí debajo se muestra en forma de dashboard, la facturación mensual en forma de gráfico de barras y facturas por cliente de mayor a menor. De esta manera se puede ver un resumen de los datos de forma resumida. Aquí habría que validar qué datos ustedes quieren ver. Esta es una propuesta de cómo se vería y algunos datos que podrían interesar. Pero ustedes me comentan qué datos son los que hoy día quieren ver, porque entiendo que también bien comentado que ya tenían un dashboard en Power BI con Pedro, entonces de pronto pudiéramos ahí acoplarlo en esta parte.
|
||||||
|
|
||||||
|
16:02 - Johann
|
||||||
|
Oye Johann, una pregunta.
|
||||||
|
|
||||||
|
16:03 - Araceli Sanchez Jimenez
|
||||||
|
Aquí en los datos que pones, ¿qué necesita atención? ¿Estos los jalaste de Jira? ¿Serían los tickets que los jalas de Jira? ¿O cómo determinas o sacas esta información? Sí, podría.
|
||||||
|
|
||||||
|
16:16 - Johann
|
||||||
|
Jira y el estatus del estado de facturación podría ser un criterio para mostrarlos aquí Ya.
|
||||||
|
|
||||||
|
16:22 - Unidentified Speaker
|
||||||
|
Ya, ok, ok, ok.
|
||||||
|
|
||||||
|
16:24 - Araceli Sanchez Jimenez
|
||||||
|
Y realmente, como tú dices, esto es customizable, ¿no? O sea, nosotros lo podemos decir, oye, Johann, esta información ponla aquí, por ahí, digo, ok, perfecto. Sí, justamente. Pero de entrada, en la propuesta, tú propones mostrarlo así. Así es.
|
||||||
|
|
||||||
|
16:41 - Johann
|
||||||
|
Si ustedes me comentan, de pronto, que estos datos no son los que les interesa ver y ocupan otros, o visualizarlos de otra manera, y lo acomodamos. Mi propuesta es ver en esa pantalla de Dashboard un resumen de los datos que ustedes quieran ver. Pero sí, como me comentas, cualquier detalle a cambiar lo ajustamos sin problema.
|
||||||
|
|
||||||
|
17:05 - Araceli Sanchez Jimenez
|
||||||
|
Oye, a lo mejor me estoy adelantando, pero, por ejemplo, aquí en el tema de la cartera vencida, en este tema nosotros tendremos que alimentarlo manual, o sea, bajar todos los estados se cuenta de VanNorte de manera diaria o cómo está alimentado?
|
||||||
|
|
||||||
|
17:25 - Johann
|
||||||
|
Si no está en Bind, ahorita mismo se puede sincronizar desde Bind, pero si no se puede sincronizar desde Bind, sí tendría que hacerse una carga al sistema.
|
||||||
|
|
||||||
|
17:41 - Noe Rocha
|
||||||
|
O sea, en este momento La cartera vencida es lo que informe Bind. Sí. Específicamente la fuente de información de esto es o es Bind o es Jira. No hay una fuente alterna.
|
||||||
|
|
||||||
|
18:00 - Araceli Sanchez Jimenez
|
||||||
|
Así es. Perfecto. Ya, ya entiendo. Ok.
|
||||||
|
|
||||||
|
18:04 - Unidentified Speaker
|
||||||
|
Gracias.
|
||||||
|
|
||||||
|
18:04 - Unidentified Speaker
|
||||||
|
Excelente.
|
||||||
|
|
||||||
|
18:05 - Johann
|
||||||
|
Después del Dashboard pasamos a esta pantalla de facturación. Aquí estamos en el módulo de facturación. Aquí venían las solicitudes de facturación que vienen desde Jira. Aquí cada registro corresponde a un ticket levantado en Jira, en donde mostramos la información y el estatus que hay en Jira. Y ahora, de esta manera, se pueden ver todos los tickets de Jira en un solo lugar, que es aquí en la plataforma. Y poder llevar el seguimiento desde aquí.
|
||||||
|
|
||||||
|
18:44 - Araceli Sanchez Jimenez
|
||||||
|
Oh, wow.
|
||||||
|
|
||||||
|
18:45 - Noe Rocha
|
||||||
|
Además aclarando que los status son status, no como aparecen actualmente en Jira, porque este prototipo fue antes de explorar los flujos de Jira. Porque, por ejemplo, ese status que dice listo para facturar en Jira es facturado, si mal no recuerdo. Nada más para hacer la observación, pero como prototipo, Esto es una propuesta y se va a adaptar a los flujos que nosotros definamos.
|
||||||
|
|
||||||
|
19:15 - Johann
|
||||||
|
Sí, justamente. Muchas gracias, Noe. Así es. Aquí en estatus iría reflejado el proceso que ustedes ya llevan.
|
||||||
|
|
||||||
|
19:23 - Araceli Sanchez Jimenez
|
||||||
|
Ok. Perfecto. De acuerdo.
|
||||||
|
|
||||||
|
19:25 - Johann
|
||||||
|
Entonces, aquí... Aquí a la derecha hay un botón de ver detalle. Y cuando se le da click, podemos nosotros ver de este ticket, así como se lleva algunos archivos adjuntos. Y desde aquí se va a poder iniciar la facturación. Y ahora, en esta parte, es en donde también necesitaremos ver el API key de Jira y toda esa parte para comenzar a sincronizar las facturas. Y ahora, cuando se tiene esta parte, y cuando ya se revisó el ticket de Jira, ahora sí, aquí también podemos ver este ticket desde Jira, si se quiere ver desde la plataforma de Jira. Y desde aquí tenemos la oportunidad de comenzar la facturación.
|
||||||
|
|
||||||
|
20:23 - Araceli Sanchez Jimenez
|
||||||
|
Yo aquí tengo una pregunta, es como de funcionamiento. Normalmente, o sea, levantamos un ticket en es Jira, un ticket por factura, no? Pero, por ejemplo, en el caso de Accions es un ticket y ese contiene un Excel y ese Excel tiene N cantidad. O sea, ese Excel puede tener un varia el número, pero 40, 45, 48 facturas. Pero no abrimos un ticket por las por Entonces, ¿tendría la guía la habilidad de decir OK, es un ticket, leer el archivo en Excel y decir OK? Entonces, ¿va a ser una factura por cada columna que haga?
|
||||||
|
|
||||||
|
21:05 - Johann
|
||||||
|
No, ahora mismo sería una factura por ticket.
|
||||||
|
|
||||||
|
21:09 - Araceli Sanchez Jimenez
|
||||||
|
OK.
|
||||||
|
|
||||||
|
21:09 - Noe Rocha1
|
||||||
|
Ahora mismo, a ver, ahora mismo, ¿cómo está el proceso, verdad? ¿Hay ciertos asegúnes que habría que ver cómo los vamos a manejar? Por ejemplo ese particular, en donde es un solo ticket con múltiples facturas. Entonces tendría que evaluar cómo hacerlo, si lo vamos a llevar a ticket por factura o un ticket con múltiples facturas, con múltiples hijos. Pero aquí por ejemplo hay un tema que me gustaría platicar con ustedes, porque el proceso como se definió Arturo y Araceli es que cuando Pone el proceso de GIRA para facturar como está en la CIS. La CIS de hoy es que la factura debe hacerse antes de que se cierre el ticket. Por lo tanto, no iniciaría el proceso de facturación desde el sistema, sino que desde antes se tiene que estar haciendo.
|
||||||
|
|
||||||
|
22:06 - Araceli Sanchez Jimenez
|
||||||
|
¿Sí me explicó? No. A ver, más lento.
|
||||||
|
|
||||||
|
22:09 - Noe Rocha
|
||||||
|
A ver un ejemplo. Arturo tiene un proceso de facturación. Ábrete, GIRA, por favor, para ver tus casos. Vamos a pasarlo muy bien. Gráfico. Voy a quitar la pantalla aquí tantito, Johann, Arturo. Entonces, por ejemplo, Arturo recibe un ticket. Ahorita vamos a ver el flujo. ¿Sí? Beta facturación, Arturo. Y esto sí tendríamos que definirlo, ¿verdad? Porque es una definición de proceso, no es una definición del sistema.
|
||||||
|
|
||||||
|
22:39 - Araceli Sanchez Jimenez
|
||||||
|
Ah, mira, justo esta es la que te digo. Tenemos la factura es un solo ticket, pero si tú le das clic son múltiples facturas.
|
||||||
|
|
||||||
|
22:50 - Noe Rocha
|
||||||
|
No, es que ahí yo veo un error porque dice facturación nacional y es extranjera.
|
||||||
|
|
||||||
|
22:57 - Unidentified Speaker
|
||||||
|
Ah, invalidación nacional.
|
||||||
|
|
||||||
|
22:58 - Noe Rocha
|
||||||
|
Entonces te va a pedir el xml y el pdf cuando solamente es el pdf. Bueno, independientemente de eso voy al punto que quiero explicar. Vámonos a cualquier flujo de facturación. ¿Facturación, Arturo?
|
||||||
|
|
||||||
|
23:12 - Unidentified Speaker
|
||||||
|
Dale clic donde dice facturado y dale ver flujo.
|
||||||
|
|
||||||
|
23:17 - Noe Rocha
|
||||||
|
Muy bien, y dale si quieres un zoom. Lo vamos recorriendo hacia arriba. Aquí me va a tratar de explicar gráficamente, a ver si me va a entender. Primero, se genera el ticket para la facturación. Por cualquier área que necesite un proceso o por un proceso ya automatizado de facturación, como son las recurrencias. Segundo, se pasa a un estatus de abierto. Después de eso se va al proceso de facturación. ¿Qué quiere decir esto? Que aquí en esta parte, en este proceso de facturación, ya se fue a Bain a hacer la factura. ¿Se explicó? Entonces, Si nos devolvemos a la pantalla de Johann, Johann tiene desde ahí que se haga la facturación.
|
||||||
|
|
||||||
|
24:13 - Araceli Sanchez Jimenez
|
||||||
|
Entonces... Oye, yo tengo una duda, porque por ejemplo en Vine hay una diferencia entre prefactura y factura. Entonces, en este caso, ¿es ya la factura en sí o es una prefactura? La diferencia es que no está timbrada. O sea, generalmente nosotros hacemos prefacturas para darle un doble Y ya una vez que le dijimos que sí, entonces ya la timbramos con el SAT. Aquí, en el proceso que ustedes están considerando en proceso de facturación, ¿ahí es ya facturarla, ya timbrarla?
|
||||||
|
|
||||||
|
24:44 - Noe Rocha
|
||||||
|
No, porque este proceso es lo que se va a definir con las reglas de negocio nuevas que vamos a hablar, que es el propósito de esto, ¿no? Es decir, ¿cómo la vamos a hacer? Porque si antes dijimos que el proceso de facturación de Jira era, aquí ya tengo que tener la facturación, entonces vamos a tener que hacer un ajuste. O lo hacemos aquí y luego se lo presentamos al sistema, lo cual no sería lo práctico porque la intención del sistema es que el sistema nos ayude a optimizar, a hacer las cosas, ¿no? O definimos que en esta parte del proceso de facturación sea donde se dispare esa ejecución ahora sí. En la pantalla de Johann. ¿Sí me explicó? No sé si me estoy explicando.
|
||||||
|
|
||||||
|
25:32 - Arturo Rosas Hernandez
|
||||||
|
Sí, sí, sí, estoy de acuerdo.
|
||||||
|
|
||||||
|
25:35 - Arturo Rosas Hernandez
|
||||||
|
Nada más que sí es muy importante. Bueno, como dices, ahorita lo estamos definiendo. Sí, sería muy importante que sea prefactura. ¿Para qué? Justo, justo, justo para continuar con este flujo, que es bueno en proceso de facturación. Sabemos que inicia en Bind, pero debe haber una validación de justo de lo que acabas de ver, no de que sí nacional y a lo mejor la generó extranjera o es extranjera y la genera nacional y después pues hacer una validación nacional extranjera y y revisar no revisar que los datos sean correctos que no haya ninguna inconsistencia de que los los el tema fiscal esté registrado correctamente los montos las descripciones etcétera sean todas correctas entonces pero sí que hay una validación previa y que no caigamos en incumplimiento porque se supone cuando generas demasiadas facturas y cancelas demasiadas facturas, también puede ser observado por el SAT. Entonces, evitar en la medida de lo posible la cancelación de facturas como tal. Por eso es necesario que lo que haga la herramienta sea para facturas, para poder revisar. Tal vez en algún futuro ya daremos independencia completa y pediremos que facture directamente. Pero por ahora, para ir monitoreando el uso o la eficiencia de la IA, si tendría que ser ProFacture.
|
||||||
|
|
||||||
|
26:57 - Noe Rocha
|
||||||
|
Es que recordemos algo, este flujo de Jira, no me quiero atorar mucho aquí porque hay más pantallas que explorar, pero sí tenemos que volvernos a Arturo y a Araceli para redefinir cómo va a quedar este flujo, porque dependiendo de cómo quede este flujo es cómo vamos a ejecutar en el modelo que Johann nos está poniendo sobre la mesa, pero no es algo ni que Johann ni mío que definimos es cómo el proceso lo vamos a definir para el uso dentro del área de facturación.
|
||||||
|
|
||||||
|
27:30 - Araceli Sanchez Jimenez
|
||||||
|
Ok, entonces si quieres lo dejemos pendiente y si quieres nada más, ya que Erika nos ayudé a coordinarlo, yo salgo de viaje otra vez el miércoles jueves y viernes no voy a estar, entonces si lo podemos ver el martes estaría perfecto porque el lunes ya lo tengo saturado, para que ya no... Porque de esto depende que Johann pueda seguir avanzando, ¿no?
|
||||||
|
|
||||||
|
27:54 - Noe Rocha
|
||||||
|
Va a avanzar en otras cosas, pero como tal, el prototipo en esta parte está saturado por esta definición de nosotros.
|
||||||
|
|
||||||
|
28:00 - Erika Chavez
|
||||||
|
Ok, de hecho Johann sigue, ya está en la etapa 1, este es lo de la etapa 0, el entregable, entonces en cuanto él ya le... Ya tiene indicaciones en cuanto a él le detenga algo, pues me va a levantar la mano, pero si esto no le detiene a Johann para seguir avanzando en la etapa 1. Sí.
|
||||||
|
|
||||||
|
28:18 - Unidentified Speaker
|
||||||
|
Gracias.
|
||||||
|
|
||||||
|
28:19 - Noe Rocha
|
||||||
|
Esta es una regla de negocio, regla de nosotros. Hay que definirla.
|
||||||
|
|
||||||
|
28:27 - Unidentified Speaker
|
||||||
|
Adelante, Johann.
|
||||||
|
|
||||||
|
28:29 - Johann
|
||||||
|
Aquí, de acuerdo. En esta parte de iniciar facturación, voy a compartir pantalla del prototipo navegable, que es este que les compartí. Entonces, en... Solicitud de gira, al abrir una solicitud, después...
|
||||||
|
|
||||||
|
28:50 - Arturo Rosas Hernandez
|
||||||
|
Creo que no vemos tu pantalla. Yo no la veo.
|
||||||
|
|
||||||
|
28:56 - Unidentified Speaker
|
||||||
|
Listo, ¿la pueden ver?
|
||||||
|
|
||||||
|
28:58 - Johann
|
||||||
|
Listo, ahora sí.
|
||||||
|
|
||||||
|
29:00 - Arturo Rosas Hernandez
|
||||||
|
Ahora sí, pero vemos a nosotros mismos. Listo.
|
||||||
|
|
||||||
|
29:04 - Noe Rocha
|
||||||
|
Este ya es el prototipo navegable.
|
||||||
|
|
||||||
|
29:07 - Noe Rocha
|
||||||
|
O sea, no es el PDF, es como tal el URL.
|
||||||
|
|
||||||
|
29:15 - Unidentified Speaker
|
||||||
|
Así es.
|
||||||
|
|
||||||
|
29:16 - Johann
|
||||||
|
Aquí estoy dentro de una facturación. Me fui aquí a solicitudes Jira y abrí un ticket de Jira. Ahora, aquí, al iniciar una facturación, se me abrirán estos pasos para completar desde la cotización hasta la aprobación humana que timbra la factura. Ahora, aquí, cada ticket tendrá una cotización. En Bind. Puede ser que se cree por parte comercial o del despacho, entiendo que era quien creaba estas cotizaciones o quien las quería, o el sistema puede crearlas automáticamente utilizando... Apenas se crea el ticket, por ejemplo, va a Bind y lo crea. Después se ligan, no? Ese ticket y esa cosa. Eso estaría genial.
|
||||||
|
|
||||||
|
30:15 - Araceli Sanchez Jimenez
|
||||||
|
Quisiéramos un ticket en gira porque, por ejemplo, ahorita ese Johann lo hacemos manual, o sea, yo generalmente este soy la parte comercial, entonces yo me meto y como ya están pre configurados los clientes, o sea, sí debe de haber un pre requisito, verdad que ya estén dados de alta en el RP, pero una vez así te vas a cotización y entonces yo escojo los diferentes rubros y ahí ya yo hago la cotización, que es algo muy rápido y ya con esa cotización generalmente ya en los tickets para facturación anexa esa cotización para que facturación nada más haga la búsqueda y pues la convierte en prefactura lo necesario, pero si con levantar el ticket, ya se genera la cotización en el sistema estaría más que genial.
|
||||||
|
|
||||||
|
30:55 - Arturo Rosas Hernandez
|
||||||
|
De hecho, perdón por la interrupción, pero de hecho ayer, justo ayer en la comida platicábamos Pedro y yo, que yo le decía que en algún momento se fuera preparando porque yo sugería que hubiera un espacio para comercial con su debido proceso en gira. Entonces, si ya tenemos facturación, si ya tenemos cuentas por pagar, cuentas por cobrar RH, etcétera, creo que vamos a tener que crear un espacio para comercial y en ese espacio de comerciales, donde subiríamos los tickets, es donde la IA, que habrá que irle poniendo un nombre a la IA para nombrarla como herramienta. Bueno, a la IA va a ir a buscar los tickets y de donde va a tocar tomar la información para generar la cotización. Eso justo estábamos, se lo prometo, estábamos platicándolo Pedro y yo ayer en la hora de comida. Entonces creo que habrá que considerarlo también como parte de la regla de negocio, como dice Noe. Para dejar todo ordenado.
|
||||||
|
|
||||||
|
31:51 - Noe Rocha
|
||||||
|
Disculpen porque lo mencionan mucho, pero sí quiero ser muy vocal de lo que estamos viendo. Este es un desarrollo y todavía no está dentro de este desarrollo, en dentro, enbebido en este desarrollo, agentes LLM. Lo que están son reglas de negocio de procesamiento de información e integración con diferentes sistemas. ¿Para qué? Para homologarlo a pantallas. Entonces sí quiero dejar muy claro, no es un agente, es un LLM que está haciendo todo tras BarbaLimna. Es un desarrollo que sí está pensado y sí lo hablamos con Johann, que en un cierto momento le íbamos a tener que meter LLMs, pero como esto todavía no está digamos que en el scope inicial todavía no está el agente. Sí está entendido que lo vamos Pero para esto, que es simplemente regla de negocio y proceso, no es IA.
|
||||||
|
|
||||||
|
32:50 - Araceli Sanchez Jimenez
|
||||||
|
Nada más quiero aclararlo. En esto, en todo lo que nos han explicado, o en esto, es de preparación, aprobación y emisión de CFDI.
|
||||||
|
|
||||||
|
33:00 - Arturo Rosas Hernandez
|
||||||
|
En general, en todo.
|
||||||
|
|
||||||
|
33:02 - Araceli Sanchez Jimenez
|
||||||
|
En general, yo fue lo que entendí también. Entonces esto no es IA.
|
||||||
|
|
||||||
|
33:08 - Noe Rocha
|
||||||
|
No, es desarrollo. Es desarrollo... Con integración de pantallas.
|
||||||
|
|
||||||
|
33:12 - Araceli Sanchez Jimenez
|
||||||
|
Ah, y yo pensé que esto era agente de IA. No, no, no, no.
|
||||||
|
|
||||||
|
33:17 - Noe Rocha
|
||||||
|
Esto es desarrollo a la medida que sí, sí va a tener un componente de IA, pero en este momento todavía no va a estar, porque esto se resuelve con reglas y con procesos. Todavía no es necesario meter un agente como tal para hacer, pues, todas las, todo esto. Sí, lo están mencionando mucho. Y nada más aclarando ese punto, Johann, sí tenemos la intención de meter agentes, pero en este momento es regla de negocio con procesos e integración de sistemas. ¿Por qué? Porque ahorita tenemos todo desvinculado y todo lo tenemos que hacer manualmente, con diferentes pantallas, diferentes procesos, diferentes cálculos. Pero esto, como tal, lo que va a venir a hacer es, va a pivotear todos los sistemas para hasta automatizarlo. Ok. A través de reglas y procesos. Sí. Muy bien. No sé si quede claro.
|
||||||
|
|
||||||
|
34:12 - Unidentified Speaker
|
||||||
|
Sí.
|
||||||
|
|
||||||
|
34:12 - Unidentified Speaker
|
||||||
|
Sí.
|
||||||
|
|
||||||
|
34:13 - Noe Rocha
|
||||||
|
Johann, ¿quieres complementar algo de lo que comenté?
|
||||||
|
|
||||||
|
34:17 - Johann
|
||||||
|
Sí, justamente en este MVP se busca asentar el proceso que ustedes tienen para en un futuro implementar los modelos ya sobre este sistema bien fundamental.
|
||||||
|
|
||||||
|
34:30 - Araceli Sanchez Jimenez
|
||||||
|
Esta es la base.
|
||||||
|
|
||||||
|
34:31 - Noe Rocha
|
||||||
|
Sí, necesitamos una base de un proceso bien claro de cómo va a ser, porque si le metemos IA de inicio va a ser una fiesta.
|
||||||
|
|
||||||
|
34:41 - Araceli Sanchez Jimenez
|
||||||
|
Y qué haría la IA que no hace, o sea, qué plus nos daría la IA sobre esto?
|
||||||
|
|
||||||
|
34:47 - Araceli Sanchez Jimenez
|
||||||
|
Ponme un ejemplo.
|
||||||
|
|
||||||
|
34:48 - Noe Rocha
|
||||||
|
A ver, Johann, ayúdame con esta pregunta, porque ahorita de inicio a mí lo único que lo tengo claro que a lo mejor cosas como una gente que yo le pueda mandar información para que me lo procese ya dentro del sistema, ya no que yo tenga que estar interviniendo todo el tiempo manualmente.
|
||||||
|
|
||||||
|
35:04 - Johann
|
||||||
|
Pero con una regla, con una regla de vas a hacer A, B, C, D, E, A, B, C en este sistema, por ejemplo, se pudieran implementar en esta parte de tomar decisiones o saber cuándo, por ejemplo, una cotización lleva mucho tiempo parada, por ejemplo, o tomar decisiones y dejar preparadas facturas para que Araceli y Arturo simplemente tomen la decisión final. En ese apartado de dejar listo para que Araceli y Arturo vean un listado de facturas o de consideraciones que, por ejemplo, puedan estar en el riesgo o en el área de este apartado tiene algo raro, en esa parte la IA nos podría ayudar a prevenir Esos casos. Y eso ahorraría tiempo, por ejemplo, para estar leyendo todas las facturas. Y podría presentarlos, por ejemplo, cada mañana en el dashboard, por ejemplo. O de pronto podemos meterlo en la parte de conciliación como extra. Aparte de esa...
|
||||||
|
|
||||||
|
36:41 - Noe Rocha
|
||||||
|
Ahí sí, por ejemplo, entraría directamente la IA. Oye, ¿cómo hacemos la conciliación ahora? A mano. Sácate un estado de cuenta. Fíjate cómo está Vine, ¿verdad Arturo? Y Concilio, ¿qué se pagó? Bueno, ahí sí metemos la idea, porque es un proceso hoy por hoy que no nos lo resuelve nada manualmente. O sea, todo lo tenemos manual y no hay un proceso que nos ayude muy claramente a machar las conciliaciones. Entonces ahí sí entra, ahí sí entra la parte de un agente. Y después ya con la raíz de la cara clara de es el camino, lo que tiene que hacer para diferentes pasos de facturación. El sistema ya como está hecho, ahora sí, el agente lo puede hacer más automatizado. Ejecútame los pasos de facturación. Básicamente, échatelo, no? Sin que tengamos que estar interviniendo pantalla por pantalla.
|
||||||
|
|
||||||
|
37:39 - Araceli Sanchez Jimenez
|
||||||
|
Perfecto, ya entendí.
|
||||||
|
|
||||||
|
37:40 - Noe Rocha
|
||||||
|
Pero parte de que tengamos claras las reglas, porque si no, sin reglas claras, un agente no es eficiente.
|
||||||
|
|
||||||
|
37:50 - Unidentified Speaker
|
||||||
|
Gracias.
|
||||||
|
|
||||||
|
37:50 - Noe Rocha
|
||||||
|
Ahora yo hago otra observación aquí, muy importante, porque se menciona que la cotización es necesaria para esto que estamos viendo aquí con Johann, el 1, 2, 3, 4 y 5. Pero por ejemplo, y aquí lo pongo solo en la mesa, las recurrencias no existe una cotización para cada factura. ¿A qué voy? Tenemos facturas que son recurrentes porque ya hay un acuerdo y un contrato, Johann. Significa que no tenemos que tener una cotización para facturar porque ya está vendido un proyecto. ¿Te acuerdas lo que te platicaba la sesión pasada? Oye, tengo un contrato de servicio con un cliente y. Pues ese contrato.
|
||||||
|
|
||||||
|
38:35 - Araceli Sanchez Jimenez
|
||||||
|
Como el de AXIANS que está pagando por cuatro años, tres años. Y esperemos que ganemos otro.
|
||||||
|
|
||||||
|
38:41 - Noe Rocha
|
||||||
|
Ahí sí tendríamos que tener. Sí, sí, correcto. Ahí sí tendríamos que tener.
|
||||||
|
|
||||||
|
38:45 - Araceli Sanchez Jimenez
|
||||||
|
tener una una excepción de que no si es recurrente no necesita una cotización si me explicó o a lo mejor que nunca bueno no sé ustedes saben pero a mí me gustaría que nunca hiciéramos excepciones o sea al final del día todo no ha sido una cotización aunque sea recurrente o no entonces podría siempre generarse la cotización y de la cotización siempre se pasa prefactura o factura bueno dependiendo de lo que definamos no Estoy de acuerdo con Ara, porque al final de cuentas son puntos de revisión, oportunidades para revisar y evitar errores o mitigar los riesgos de errores humanos o errores en las configuraciones, sobre todo al inicio, ¿no?
|
||||||
|
|
||||||
|
39:28 - Arturo Rosas Hernandez
|
||||||
|
Al inicio de la implementación de esta herramienta, pues sí estar monitoreando en cada fase, en cada paso, todo lo que suceda para asegurar que todo suceda de manera correcta y o, algunas veces, como en Big Brother, las reglas cambian. Entonces, no obviar que cada mes todo es igual, sino estar asegurando que cada mes, si hay algún cambio en la descripción, en los montos, en lo que sea, ese mes se corrija y se emita conforme el nuevo acuerdo, conforme las nuevas reglas.
|
||||||
|
|
||||||
|
40:09 - Noe Rocha
|
||||||
|
donde entraría algo que no sé si debe vivir aquí y lo pongo sobre la mesa para ver si es algo que más adelante lo ponemos. No ahorita porque si no, no vamos a salir nunca porque va a haber muchos supuestos. Pero por ejemplo, en videos pasados, si tú te acuerdas Arturo, teníamos los contratos. Y ese contrato decía, este contrato por un año vale 12 pesos. Entonces cada orden de compra que yo haga por este contrato me del debe contrato de descontar lo contar que ya se facturó y se pagó de tal forma que si al mes seis yo tengo que ya se facturaron seis pesos y se cobraron seis pesos no puedo cobrar más allá de otros seis pesos porque ese Más o menos, amigo, porque más o menos, porque justo con el ejemplo de Axios, o sea, las personas suben, bajan, los precios cambian, se ajustan, aumentos de sueldo, disminución, nuevos acuerdos, etc.
|
||||||
|
|
||||||
|
41:04 - Arturo Rosas Hernandez
|
||||||
|
Entonces, por eso diría sí, pero no por eso yo digo que sí o bueno, estoy de acuerdo con lo que dijo Ara, de que siempre generemos la conciliación, porque es una oportunidad para revisar de oye, para el empleado, para colaboradora eran 6 pesos, pero acuérdate que esta vez son 5 por RENECOSA o son 6 más 1 que es viáticos o son 6 más lo que sea, ¿no? Entonces, sí debes tener la oportunidad.
|
||||||
|
|
||||||
|
41:35 - Noe Rocha
|
||||||
|
Pero aquí entra mi pregunta, porque esto sería un trabajo adicional. ¿Significa que cada vez que se tenga que facturar vas a hacer una cotización, Araceli? Significa que, por ejemplo, vamos a poner el caso de AXS, para cerrar la pregunta. ¿Sería práctico que cada mes tendría que hacer la cotización?
|
||||||
|
|
||||||
|
41:58 - Araceli Sanchez Jimenez
|
||||||
|
Es lo mismo. Si tú haces una cotización, el esfuerzo es el mismo. Por ejemplo, no hacemos cotización, pero al final del día se captura a mano la factura. Entonces da lo mismo. Porque cuando tú haces una cotización, esa cotización es en el RP, nada más te vas a un menú y esa cotización la conviertes. Nada más le haces un clic a prefactura o a factura. Y cuando tú también, entonces tú nada más es un clic. Y por ejemplo, cuando tú vas a facturar, hace el capturas lo mismo y nada más le haces un clic y lo timbras. Entonces el esfuerzo es el mismo.
|
||||||
|
|
||||||
|
42:35 - Noe Rocha
|
||||||
|
Ah, entonces miren, fíjense que hay un dato importante.
|
||||||
|
|
||||||
|
42:38 - Araceli Sanchez Jimenez
|
||||||
|
No sé si tú lo estás viendo, Johann, pero por no sé si nos explicamos o quieren que les expliquemos porque el esfuerzo es lo mismo, o sea, por eso a mí me hace sentido que todo nazca de una cotización, porque todos los datos son todos los mismos y nada más la diferencia en la cotización que, por ejemplo, cuando es un tema comercial, yo nada más la cotización la convierto a PDF, pero cuando ya se vendió, esa cotización yo la subo y entonces Artur lo que hace es, oye, factúrala y baja, nos da un número de folio, se va el sistema, el número de folio, aparece la cotización y nada más le da un clic factura o prefactura.
|
||||||
|
|
||||||
|
43:20 - Noe Rocha
|
||||||
|
Entonces es lo mismo. Entonces aquí es un arreglo interesante. Si la cotización sirve para que se convierta en factura o prefactura, entonces en estos pasos hace sentido. Primero cotización, los datos, los conceptos y luego ya validado, ya conocido información, se va a prefactura, no a se va a ir a prefactura, se va a tener una aprobación humana que va a ser una... Esa aprobación y después, después ya se timbra. Entonces tenemos candado 1 cotización, candado 2 prefactura, candado 3 aprobación y ya al final pues la factura como tal. Entonces son 3 candados para validar que esa factura tiene que salir pues como tenga que salir, ¿verdad?
|
||||||
|
|
||||||
|
44:10 - Unidentified Speaker
|
||||||
|
De forma correcta.
|
||||||
|
|
||||||
|
44:12 - Noe Rocha
|
||||||
|
Sí. ¿Hace sentido ese flujo entonces?
|
||||||
|
|
||||||
|
44:15 - Unidentified Speaker
|
||||||
|
Sí.
|
||||||
|
|
||||||
|
44:16 - Noe Rocha
|
||||||
|
Ok, de acuerdo. Gracias. Valente, Johanna. Ok, de acuerdo.
|
||||||
|
|
||||||
|
44:20 - Johann
|
||||||
|
Ahora, en esa parte donde comentan, así como comentario extra, también para tenerlo presente, ¿va a haber cotizaciones y, bueno, facturas que no van a vivir en Gira? O si esas cotizaciones o esas facturas que no tienen cotización a día de hoy están reflejadas en Gira, porque ahorita este ejercicio comienza desde un ticket de Gira. Entonces, si hay alguna cotización. Que no esté en Gira, no va a ser reflejado, entonces pregunto para, por ejemplo, considerarlas aquí para la regla.
|
||||||
|
|
||||||
|
44:58 - Araceli Sanchez Jimenez
|
||||||
|
Si no, no, Johann, ya por regla y política de la empresa, los únicos y Lo que no está en JIRA no se procesa, ya sea en facturación, cobranza u otras solicitudes de la operación de la empresa. Entonces, ahora sí, el único punto de inicio en donde realmente se detonan los procesos es JIRA. Entonces, todo debe de estar contenido ahí para que no nos vayamos por otro. Nada más es JIRA. Sí, que no sea un correo, que no sea un mensaje. Si no está en JIRA, no es oficial. Sí, y de hecho, por ejemplo, en mi caso, que luego me llegan correos con facturas cosas así como para yo no capturar de manera manual y un ticket en gira y yo ya tengo una dirección establecida de correo yo lo reenvío ese correo aún y a una dirección de gira y giren automático me hace un ticket entonces todo está por girar ya aquí aquí entiendo muchas gracias Ahora, continuando con estos pasos, así como comentan, sí, aquí los candados serían primero la cotización, después la prefactura, la aprobación y por último el timbrado, que es lo que sucede cuando avanzamos con este proceso de facturar.
|
||||||
|
|
||||||
|
46:18 - Johann
|
||||||
|
Entonces, primero vemos los datos que tiene la factura. Aquí se selecciona el método de pago, está por defecto el PPD. Ajá. Los conceptos EIVA de esta factura, bueno, de esta cotización. Después, se ve una previsualización de la prefactura. Aquí se puede dejar y guardar para validarla después, o podemos enviarla directamente a aprobación. Y ahora cuando se envía a aprobación, aquí, por ejemplo, se puede dejar lista para que o Arturo o Araceli la aprueben desde su cuenta una vez se apruebe ya se puede timbrar, primero se aprueba y luego se timbra y después pueden o emitir otra factura o continuar y enviar la factura con los documentos para esta parte del correo te tendría que dar un buzón o algo por el estilo, ¿verdad? Sí, ocuparemos un buzón de.
|
||||||
|
|
||||||
|
47:33 - Noe Rocha
|
||||||
|
Sí. Así es.
|
||||||
|
|
||||||
|
47:35 - Araceli Sanchez Jimenez
|
||||||
|
Oye, Johann, yo por ejemplo, aquí ya ves que tenemos algunos asegúnes, es decir, hay algunos que nada más le mandamos la factura, es decir, le echa un rollito ahí el por ejemplo, en el en el tema de Accions, le mandamos las cuarenta y tantas facturas en una carpeta zip, o sea, no le mandamos un correo por factura, pero además de de mandárselas todas en un en un solo correo, nos pide el estado de cuenta de esas facturas, es decir, les mandamos así y entonces se le manda un archivo que eso también se se saca de del sistema de entonces veinte y cincuenta, estas son tuyas y corresponde veinte, veinte, Araceli Sánchez del Perú, no sé qué, cinco mil y algo, o sea, se les manda todo eso, o sea, además de la factura. Aquí, por ejemplo, nada más se adicionaría en la factura, y nosotros a este correo le podemos anexar cosas o.
|
||||||
|
|
||||||
|
48:45 - Johann
|
||||||
|
Sí, sí, justamente, por ejemplo, tenga sus propios requisitos y, por ejemplo, aquí en el ejemplo de Cemex, me parece envío bloqueado porque me faltan las horas, el Excel de horas.
|
||||||
|
|
||||||
|
48:59 - Araceli Sanchez Jimenez
|
||||||
|
Sí, órale, sí, me gusta. Yay, jubilados, Artur, ya. Ya. Bueno, está en la playa. Exactamente, así desde la playa, autorizar, enviar. Mira, pues de hecho ahí está, ¿eh?
|
||||||
|
|
||||||
|
49:13 - Noe Rocha
|
||||||
|
Ahí no menciona. ¿Dónde estaba el zip? Acuntia. Ahí viene, .zip, pdf.xml, más estado de cuenta. Ya lo menciona.
|
||||||
|
|
||||||
|
49:21 - Araceli Sanchez Jimenez
|
||||||
|
Andale, super, sí. O sea, decirlo con particularidad por cliente. Sí, Johann, y ya ves que aquí luego los clientes es como el Big Brother, las reglas cambian. Entonces, por ejemplo, si de repente nos piden otra cosa por sistema, se puede anexar esa o quitar o poner la nueva regla, ¿verdad?
|
||||||
|
|
||||||
|
49:41 - Johann
|
||||||
|
Ok, habría que desarrollar ese módulo de configuración? Al principio esto estaría cerrado, directo, cerrado, sí, pero si se puede, nada más que ahora mismo sería desarrollar el módulo.
|
||||||
|
|
||||||
|
49:52 - Araceli Sanchez Jimenez
|
||||||
|
Sí, no, no, y si no, pues te contactamos, nos cobras unas horas de servicio y nos haces el ajuste, ¿no? Sí. Ok, sí, digo, de momento yo creo que no es necesario desarrollar el otro de configuración porque realmente, pues, es pequeño, pero yo nada más porque luego nos cambian la y si sí, nada más saber que la podemos poner ahí, porque al final del día esto nos libera mucho, Johann, porque luego, de entrada, la curva de aprendizaje nos las hace muy laxa o muy soft porque por ejemplo a lo mejor ya le dimos la capacitación a alguien y lo quiere mandar y va a decir ah si cierto tengo que mandar a b c d entonces así quita también mucha vigilancia humana no o acompañamiento de cierta forma gracias así es ok entonces esta parte es todo el proceso por la parte de facturación.
|
||||||
|
|
||||||
|
50:55 - Johann
|
||||||
|
¿Aquí hay alguna duda? Porque seguiría después cobranza. No. Yo ninguna, no sé si ustedes.
|
||||||
|
|
||||||
|
51:02 - Noe Rocha
|
||||||
|
No, nada más las reglas, nada más arrañar un poco nosotros para la regla específica de lo que comentamos antes. Pero de ahí en fuera el módulo es bastante, creo que se ve muy visual e intuitivo.
|
||||||
|
|
||||||
|
51:19 - Unidentified Speaker
|
||||||
|
Sí.
|
||||||
|
|
||||||
|
51:20 - Araceli Sanchez Jimenez
|
||||||
|
Me encantó.
|
||||||
|
|
||||||
|
51:21 - Johann
|
||||||
|
De acuerdo, continúo. Ahora, esta parte de cobranza. En el módulo de cobranza vamos a tener las facturas abiertas y los estados de cuenta. Entonces, toda esta información del módulo de cobranza, aquí la podemos ver, tendrá los estados de cuentas asociados. Y ahora aquí, nosotros tenemos dos pantallas. En la primera vamos a ver las cuentas por cobrar. Y aquí se pretende que el segundo reemplazo, no reemplazo, pero que sea el equivalente al Excel que compartió Arturo, en donde pueden ver las facturas y cuánto tiempo llevan de morosidad por cliente.
|
||||||
|
|
||||||
|
52:17 - Johann
|
||||||
|
Johann, esto se alimenta de Vine, ¿verdad?
|
||||||
|
|
||||||
|
52:19 - Noe Rocha
|
||||||
|
O sea, si Vine no está conciliado en algún dato, pues no nos va a dar la información. Al día. Si primero no está en Vine. Porque la fuente oficial de información es Vine. Así es. Ok.
|
||||||
|
|
||||||
|
52:35 - Johann
|
||||||
|
Sí, justamente como mencionas, viene desde Vine.
|
||||||
|
|
||||||
|
52:38 - Arturo Rosas Hernandez
|
||||||
|
Aquí tengo una duda y es la parte en la finza. Digamos que están los estatus obvios, ¿no? Por pagar, pagada, cancelada o... No veo el resto de estatus. Ok, entonces, pero hay unas facturas especiales que digamos que ya están programadas para pago, que de cierta manera las estamos ahorita categorizando de otra manera, ¿no? Entonces ahorita justo unas facturas que ya están programadas para pago, es decir, hoy mismo me apareció una factura que dice que se va a pagar el 24 de septiembre. Esas le llamamos NAFINSA y las categorizamos con esa tipificación, NAFINSA. La pregunta es, para este módulo de cuentas por cobrar, ¿habría la posibilidad de categorizar de manera diferente este tipo Si no está en Bind, no.
|
||||||
|
|
||||||
|
53:43 - Noe Rocha
|
||||||
|
Si el estatus no existe en Bind, no. Pero ahora sí ya. Sigue tú Johann. ¿Qué Workaround podemos tener? Muchas gracias Noe.
|
||||||
|
|
||||||
|
53:54 - Johann
|
||||||
|
Así es como esta información se jala de Bind. Si no existe en Bind, nosotros habría que agregar una capa extra aquí en el sistema. A lo mejor añadirle o o un campo de estatus o agregárselo aquí.
|
||||||
|
|
||||||
|
54:12 - Noe Rocha
|
||||||
|
Nada más que ahí tendríamos que definir la regla Arturo. Hoy por hoy, ¿cómo se define el estatus de una factura con un estatus diferente en Vine? Para visualizarlo y ponerle esa capa que dice Johann, oye esto no existe en Vine. ¿Cómo lo queremos ver y cuál es la regla para tipificarlo? Para entonces ponerle sobre lo es una capa adicional, es decir, y estas facturas las necesito ver así, pero es una capa arriba de.
|
||||||
|
|
||||||
|
54:43 - Araceli Sanchez Jimenez
|
||||||
|
Si son solo las de Cemex, el único cliente que tiene cadenas productivas, que es Nafinsa y Cemex. Entonces, por ejemplo, cómo es el proceso de Cemex? Cuando un proveedor está dado de alta en cadenas productivas, nosotros vemos todo el proceso y entonces Cemex dice ok, ya está, se pasa a aprobar a pago y lugar de que nos se nos tenga y porque nos paga 120 días lo que hace es pasar esa factura ya autorizada pago a la finza y entonces nosotros en la finza vemos todas las facturas que ya están en firme en pago sí entonces las cadenas productivas bueno no sé si ustedes sepan pero bueno sino cadenas productivas es es un apoyo que se le da a los proveedores porque es decir si Yo necesito bajar ese dinero antes de los 120 días. Yo lo bajo y me cobran un interés respectivo diferente, dependiendo del banco el que yo decida. Verá, entonces al final del día es como una ayuda, un apoyo al proveedor de crisis. Oye, yo no puedo esperar 120 días, lo bajo ahí. Entonces todas las facturas que nos aparecen en la finza ya están en pago en firme. Entonces, pero, están en pago en firme, en Bain a nivel contable, no se registra hasta que cae en la cuenta. Entonces, este dato nosotros ni siquiera si lo compartimos al despacho contable, porque nos va a decir a mí de qué me sirve, o sea, a mí no me des algo que esté en el aire. Pero para nosotros, de manera interna, sí nos sirve, porque entonces sabemos que ya tenemos ahí en firme el pago de dichas facturas, y las que no aparecen en la finza, entonces sí empezamos a correr porque quiere decir que no están autorizadas. Pero nada más pasa eso para CEMEX. De ahí en fuera, para ningún cliente tenemos NAFINSA o cadenas productivas.
|
||||||
|
|
||||||
|
56:38 - Noe Rocha
|
||||||
|
Y ese dato, como nada más para ver y pensarlo con Johann. Estos son los datos de Vine. Y la capa siguiente para ver esto, ¿cómo les serviría o cómo lo ven hoy? ¿Cómo les funciona ver ese dato?
|
||||||
|
|
||||||
|
56:55 - Araceli Sanchez Jimenez
|
||||||
|
y a la mejor nosotros lo podríamos alimentar o manual o por ejemplo nosotros tenemos un portal, un usuario de una contraseña y ahí en ese portal le damos emics, le damos consultar y nos explica todas las facturas con el el cfdi y a cuántos días nos los van a pagar, de hecho nosotros tenemos ahí ya parece el día exacto 4, 3 de agosto, septiembre, entonces este nosotros lo que hacemos por eso le con Artur una capa adicional porque esas no están pendiente de pago o sea si están pendiente de pago pero ya están programadas pero no han caído en la cuenta a fin de cuentas no han caído no han caído entonces nada más yo como lo veo esta pantalla nos dice lo que ya cayó necesitarían otra pantalla para ver lo que está programado para pago cuentas estos son cuentas por cobrar estas son las que no han pagado, o las que no nos han pagado.
|
||||||
|
|
||||||
|
57:56 - Noe Rocha
|
||||||
|
Sí, las que no se han pagado. Esto solamente nos dice si está pagado o no está pagado. Y cuántos días tiene desde que no se ha pagado a la fecha. Como dice aquí, tiene de 1 a 30 días vencidas, de 30 a 60 días vencidas, etc. Esto no nos dice eso, pero no nos dice o no viene, y como no existe en Vine, también es necesario definirlo. ¿Podemos tener una pantalla para ver lo que ya está programado?
|
||||||
|
|
||||||
|
58:25 - Arturo Rosas Hernandez
|
||||||
|
Mi respuesta corta, si me permite interrumpirte, Ara, es sí. Sí, porque en algún momento tenemos que ser preventivos o podemos planear nuestras finanzas a futuro. Y poco a poco pudiéramos hacerlo con los clientes. Yo estoy pensando, me estoy imaginando un futuro Balam, que por cierto, ahí viene otra pregunta porque falta el resto de empresas, pero bueno, ahorita voy a enfocarme con Balam, estoy pensando que vamos a crecer en la cantidad de clientes, en la cantidad de facturación, etcétera, etcétera. Y tal vez esos clientes ya van a empezar a tener portales. O sea, caso Frisa, caso Frisa ya tiene su portal, ya tiene un portal de proveedores. Y entonces, si ya tiene el portal, ya nos va a decir o ya nos va a permitir saber cuándo están programadas nuestras facturas. Como con este, con estos casos que mencionara de ya, ya es seguro, o sea, ya es seguro. Que el 24 de septiembre me va a caer una factura y con eso nosotros podemos planear nuestras finanzas, es decir, si tomo del ahorro o tomo de un préstamo o no considero pago para esa semana o la considero para la siguiente o para ese mes o para el otro mes, o sea, poder planear nuestros pagos porque ahorita estamos siendo reactivos, estamos reaccionando a las necesidades financieras de la empresa Y sí, estamos adelantando en unos cuantos meses en el futuro, pero yo quisiera que pudiéramos adelantarnos un poco más, pero de manera por proceso. Que en algún momento alguien nos diga o una pantalla nos diga cuánto dinero tenemos programado en los siguientes meses y qué podemos hacer con ese dinero con base a la facturación que tenemos, a las cuentas por cobrar, qué tenemos, a los ingresos, etcétera. O sea, una planeación financiera. Y ese es el último toque, la cerecita del pastel, diré yo. Adelante, perdón.
|
||||||
|
|
||||||
|
1:00:23 - Noe Rocha
|
||||||
|
Entonces sería entonces, a mí se me ocurre lo siguiente, muy rápido, nada más antes de perder la idea. A mí se me ocurre lo siguiente, y dígame si les funciona, que de esto, y ya lo veo con Johann, se pueda seleccionar qué factura ya la tenemos por lo menos programada en un portal de algún cliente, este es el que sea, y que manualmente le podamos poner un estatus que diga programada y con la fecha, como para poder decidir mínimo, por lo menos, ya sé que el mes de julio me van a pagar el 50% de las facturas vencidas. Pero yo creo que eso sería para ponerlo en otra pantalla diferente a esta. Esta sería nada más como que, o me pagaste o no me pagaste. Y en la otra, qué es lo que sí tengo programado. No sé si le sirva.
|
||||||
|
|
||||||
|
1:01:13 - Araceli Sanchez Jimenez
|
||||||
|
Oigan, a mí se me ocurre algo. Yo pensando en que cada vez dependamos menos de nosotros. O sea, de nosotros meternos al portal así. También lo que puede hacer es, por ejemplo, que cada 15 días se genere un ticket en Jira automático de checar portal Nafinsa, no sé, desde Jira, ¿no? Entonces, ese checar que diga, oye, ¿sabes qué? Y que pongas un Excel y que a lo mejor se haga un ticket para que se vaya directamente a cuentas por cobrar. Entonces, al menos cada 15 días, porque tampoco es tanta la transaccionalidad que tenemos con SEMED, como para decir, oye, es que hay que checarlo diario, porque, o sea, no. Entonces, a lo mejor cada 15 días que nosotros se genere un ticket en automático, pongamos lo que nos aparezca en Afinsa, el pantallazo, los FDIs, y ya se cierre. Y ese ticket automático lo jalen ustedes y lo meten en cuentas por cobrar. Y puedan hacer el match, si me entiendes, porque es el número de folio. El número de folio que nos aparece en Afinsa es el mismo número de Entonces que tú digas, ah, mira, en Cemex está el follow 100 y está en Afinsa, ah, ok, entonces aquí, en automático, en Afinsa. No me gustaría que quedara tan manual porque se nos va a ir otra vez de las manos.
|
||||||
|
|
||||||
|
1:02:35 - Noe Rocha
|
||||||
|
¿Podría ser eso o no?
|
||||||
|
|
||||||
|
1:02:37 - Arturo Rosas Hernandez
|
||||||
|
Lo veo con Johann.
|
||||||
|
|
||||||
|
1:02:39 - Noe Rocha
|
||||||
|
Bueno. Sí.
|
||||||
|
|
||||||
|
1:02:40 - Unidentified Speaker
|
||||||
|
Gracias.
|
||||||
|
|
||||||
|
1:02:40 - Unidentified Speaker
|
||||||
|
Muy bien.
|
||||||
|
|
||||||
|
1:02:41 - Johann
|
||||||
|
Adelante, Johann.
|
||||||
|
|
||||||
|
1:02:42 - Johann
|
||||||
|
Ok. Entonces. En cada factura se ve el detalle de las facturas. También aquí tenemos consideradas la tolerancia por fees. Por ejemplo, si tienen una diferencia que sea considerada un fee, eso se tendría que configurar. Por ejemplo, si es el 5% o una cantidad así fija de fee por cliente, aquí se configuraría y entraría aquí en pagada pero con tolerancia. Esto es por esta pantalla y en la siguiente pantalla de pagos y recordatorios, aquí es para informar o para ver, por ejemplo, los pagos detectados que han ocurrido últimamente en Bind, por ejemplo, hoy. Y aquí se verían los pagos de las facturas que se han hecho hoy y el resultado que se ha hecho. También aquí vemos algunas reglas de recordatorio. Por ejemplo, todas las alertas por el momento serán internas. Aquí, por ejemplo, si lleva un día vencido de la factura, se le avisará a Arturo. Si lleva 15 días, ya se enviar un mensaje a Araceli y también por el momento no está desarrollado pero podría ser en un proceso posterior de si lleva cinco días se le envía un correo al cliente y a 30 otro correo al cliente.
|
||||||
|
|
||||||
|
1:04:26 - Araceli Sanchez Jimenez
|
||||||
|
¿No está configurado porque no es el alcance?
|
||||||
|
|
||||||
|
1:04:30 - Noe Rocha
|
||||||
|
No está definida la regla todavía.
|
||||||
|
|
||||||
|
1:04:34 - Araceli Sanchez Jimenez
|
||||||
|
Gracias.
|
||||||
|
|
||||||
|
1:04:34 - Johann
|
||||||
|
Y aquí abajito ya aparecerán las alertas internas, que son estas reglas de recordatorio.
|
||||||
|
|
||||||
|
1:04:42 - Johann
|
||||||
|
Entonces, por ejemplo, si estoy aquí como el usuario de Arturo, aquí me aparecerán todas las facturas que tengan más de un día vencidas.
|
||||||
|
|
||||||
|
1:04:55 - Araceli Sanchez Jimenez
|
||||||
|
OK.
|
||||||
|
|
||||||
|
1:04:55 - Johann
|
||||||
|
Y aquí, por ejemplo, esta parte iba para lo de las excepciones de seguimientos está pensado para por ejemplo si había un cliente que no se le quisiera enviar un correo porque era un cliente que ya pagaba pero también esta parte puede no estar ok gracias esto por esta parte y como último este módulo de auditoría Es una bitácora de los movimientos que se han hecho y qué usuario los hizo, a qué hora los hizo. Entonces, de esta forma, por ejemplo, se puede llevar un seguimiento de las facturas que se hicieron, las aprobaciones que se hicieron, las cotizaciones que se vincularon y todas las acciones que se hicieron dentro del sistema. Aquí se puede ver para llevar una transabilidad.
|
||||||
|
|
||||||
|
1:05:57 - Araceli Sanchez Jimenez
|
||||||
|
OK. Me encanta la idea. Sí a todo.
|
||||||
|
|
||||||
|
1:06:02 - Noe Rocha
|
||||||
|
Ahora, este MVP y lo importante de la definición del MVP, qué significa MVP? Y lo vuelvo a repetir, porque ya lo habíamos dicho hace tiempo, pero nada más para no perdernos. El MVP es lo mínimo necesario para operar. Yo sé que ahorita vimos muchos temas, muchos segundos, muchos detalles, pero yo sí los invitaría a decir sí, si el cielo, esto nos funciona para empezar a operar algo el día de hoy, lo ideal sería hacerlo. ¿Por qué? Porque ¿qué pasa? Cuando se le da una revisada a un sistema, empiezan a salir muchos hacegúnes, como ya lo hemos visto. Y en esos hacegúnes empezamos a sumarle, sumarle, sumarle. Y al final de cuentas, cuando llegamos ya al momento de querer operarlo, pues nos damos cuenta de que hay cosas que se empiezan a decir. Ah, ¿sabes qué? Esto no, esto sí, esto lo ocupaba así, esto ya no. Entonces El MVP tiene como propósito salir rápido, operar rápido y descubrir sobre eso lo que, si bien ya documentamos ahorita, cosas que a lo mejor ni siquiera habíamos visto porque no lo estamos operando. Entonces. Hay cosas que sí son de reglas, que sí, definitivamente las tenemos que definir para que queden dentro del sistema. Y hay otras cosas, son como los nice to have. Oye, estaría padre que tuviera esto. Entonces, si los nice to have ahorita, nos impiden o no serían impedimento para poder salir a operar con algo. Yo sí les pediría que hagamos team back, veamos los puntos que mencionamos en esta sesión y digamos qué es nice to have y qué es un must. Si es un must, volver con Johann o Johann, esto es must. Por favor, vamos a quitar, poner, subir, bajar, lo que tengamos que hacer. Y lo que no, podamos empezar a avanzar para que Johann en un tiempo corto nos pueda empezar a dar ya algo operativo. Y lo que sí pasa mucho es que ya cuando sales a operación con algo, ahí es cuando se descubren muchos asegúnes. Muchísimos más de los que estamos viendo ahorita, porque ahorita ni siquiera lo estamos operando, nada más lo estamos viendo.
|
||||||
|
|
||||||
|
1:08:12 - Araceli Sanchez Jimenez
|
||||||
|
¿Sale? Mira, yo tengo la respuesta rápida. La verdad que Johann nos captó absolutamente todo lo indispensable y más o sea a mí me parece fabuloso yo lo único que de mi lado queda es que revisemos el proceso de facturación prefacturación nada más para no hacer si revisamos ese proceso de mi lado está o sea está perfecto porque justo lo como ya habíamos tenido otras sesiones con Johann nos captó súper bien entonces para mí esto se me hace algo súper maravilloso yo de momento no le pondría nada más o sea es como y ya obviamente entiendo que ya después él montaría la IA para pues obviamente ya que nos dé otra analítica y otras cosas. Pero de momento a mí me parece excepcional, o sea, siento que tiene absolutamente todo. ¿Tú qué opinas, Artur?
|
||||||
|
|
||||||
|
1:09:00 - Arturo Rosas Hernandez
|
||||||
|
Sí, la verdad es que lo dijiste muy bien. Yo no le pondría nada más. Incluso el reporteo está muy bueno, que es lo que nos va a permitir dar visibilidad de lo que falta, lo que se hace, lo que no se hace, etcétera, incumplimientos o omisiones. Entonces, yo lo veo bien, como lo dice Noe, lo conecto con el MVP. Sí, esto es lo mínimo que necesitamos y obviamente esto va a ir creciendo con todo esto que dijimos y más porque sabemos cómo somos, ya nos conocemos para que nos invitan. Sí, le podemos agregar muchas cosas porque sí, seguramente hay muchas cosas por mejorar, pero también dependen mucho de nuestros procesos, ¿no? De los procesos, de cómo los estamos creando o cómo lo estamos mejorando día a día. Entonces, yo también lo dejaría así como está. Sobre todo el tema de la facturación. O sea, si el tema de facturación me va a permitir hacerlo en segundos y no en horas o en minutos y no en horas, yo voy y ya estoy de acuerdo. O sea, con que nos reduzca el tiempo que dedicamos a la facturación, que tampoco es mucho. Y al reporteo, está genial.
|
||||||
|
|
||||||
|
1:10:12 - Araceli Sanchez Jimenez
|
||||||
|
Sí, sobre todo como tú dices, yo creo que la consolidación, o sea, el tenerlo, porque vamos de uno a otro, y luego agarras el giri, y luego agarras esto, y luego el vine, y O luego sea, no son sé muchos qué, temas y y luego, luego abrimos el archivo de Excel de la cobranza y luego esto. Entonces, yo creo que aquí y además que tengamos los dos o una sola previsualización. Por ejemplo, yo cuando apruebo cosas me tengo que meter al portal de JIR en aprobación y no sé qué. Y aquí ya está todo consolidado. Entonces, no me tengo que. Y luego, por ejemplo, veo las aprobaciones y porque apruebo no nada más las de administración, sino otras. Entonces, cuando veo la lista de aprobaciones, siempre le doy prioridad a facturación y a pagos. Entonces, yo ando seleccionando. Entonces, aquí ya sé que si yo me meto en el portal, yo nada más priorizo esto, ¿no? O sea, desde un solo dashboard, porque en el Jira, pues, sí, yo tengo que, por los títulos me dejo llevar y, bueno, este sí, ahorita este lo veo y cosas así. Entonces, a mí me parece que todo está bien, nada más hagamos Timback para rechecar el flujo de facturación o prefacturación. Y con eso, Johann, muchas gracias. Creo que, este, le decía, no, de que tú tienes, yo veo que tú tienes, digo, tienes muchas cualidades, pero de la cualidad que tiene que ver con lo laboral y que yo he visto que tienen pocas personas, hablas poco y escuchas muchísimo. Y eso hace que nos entiendas mucho. Generalmente la gente te interrumpe. Mucho entonces yo tú eres o sea yo puedo estar hablando una hora y tú jamás me interrumpes y ya al final que termino oye y ya me haces entonces generalmente las personas que que son así tienen mucha capacidad de de percibir y de de atención y de entender y tú eres una de ellas muchas gracias nos entendiste aquí está el ejemplo y el bebé y el bebé balama ahí está de ejemplo exactamente entonces está perfecto yo la verdad lo veo super bien. Muchas gracias. Pues listo, Johann.
|
||||||
|
|
||||||
|
1:12:19 - Noe Rocha
|
||||||
|
Queda pendiente de nuestro lado nada más una parte de definición del proceso y ya para volver contigo y hacer este MVP ya algo productivo. Que, ojo, nada más Ara y Arturo, vamos a tener que hacer una prueba y esa prueba va a ser muy muy observada, muy segura de lo que sería un flujo normal para la prefacturación. Porque eso ya implicaría ir a escribir sobre Vine, pero eso ya lo haríamos sobre... Ahora sí queda, como dices ahora, bien plenito, ¿no? Bien, paso por paso, valida. Oye, sí está súper seguro de que lo que estamos haciendo esté funcionando y no está afectando a otra cosa.
|
||||||
|
|
||||||
|
1:13:07 - Araceli Sanchez Jimenez
|
||||||
|
Ok, sí, me late.
|
||||||
|
|
||||||
|
1:13:08 - Noe Rocha
|
||||||
|
Ese ya sería el final, ya cuando cuando tengamos toda la fase definida del proceso, antes de hacer un go live del sistema.
|
||||||
|
|
||||||
|
1:13:19 - Unidentified Speaker
|
||||||
|
Ok, perfecto.
|
||||||
|
|
||||||
|
1:13:20 - Noe Rocha
|
||||||
|
Johann, ¿comentarios?
|
||||||
|
|
||||||
|
1:13:21 - Johann
|
||||||
|
Muchas gracias, Arturo, Araceli, por sus palabras, las aprecio mucho. Justamente, me han servido mucho las explicaciones que me han dado. Me han dejado espacio para dudas. Entonces, ha sido muy valiosa la información que me han brindado. También cómo mostraban ejemplos, cómo traían casos que no eran tan comunes también para considerarlos, cómo explicaban el proceso porque lo vio en día a día, fue muy útil y fue muy cómodo para mí trabajarlo y entender qué les iba a servir. Me alegra mucho que esta solución, este MVP, les haya agradado, les haya sido de gusto y vamos a trabajar sobre ello. Entonces, pues, mis siguientes pasos serán ya montar o comenzar con el desarrollo de la parte visual. En ese tiempo, he estado trabajando en la parte que va por detrás. En el backend, sí. He estado trabajando en el cliente de cómo se va a conectar con Bind, en la parte de usuario, en la parte de soportar el multitenant, en la base de datos. Entonces, falta conectar a CloudFrontend y hacer las pruebas que comentan hoy. Entonces, esos, por mi parte, serán los siguientes pasos. Y con mucho gusto de seguir trabajando en este proyecto, en el Bebé Balán, como dicen.
|
||||||
|
|
||||||
|
1:14:45 - Araceli Sanchez Jimenez
|
||||||
|
Ya sé. Muy bien, Johann. Muchas gracias a todos.
|
||||||
|
|
||||||
|
1:14:48 - Noe Rocha
|
||||||
|
Ya tenemos que ir, porque la parte de gira, pues, ya habíamos, tenemos una sesión pendiente con Pedro, ¿no?, para hacer ese Discovery. Sí, es correcto. En un principio, pero ahora, dado la necesidad del proceso, hay que incluirlo. ¿Vale?
|
||||||
|
|
||||||
|
1:15:03 - Johann
|
||||||
|
Vale, ok.
|
||||||
|
|
||||||
|
1:15:04 - Araceli Sanchez Jimenez
|
||||||
|
Bueno, ya está. Gracias, gracias. Feliz viernes y igualmente. Gracias, bye. Bye.
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
# Correo — Pedro Ayala → Johann · entrega del token de BIND
|
||||||
|
|
||||||
|
**De:** Pedro Alberto Ayala Elizondo (pedro.ayala@balamtalentoestrategico.com)
|
||||||
|
**Para:** Johann · **CC:** Noe Rocha, Erika Chávez
|
||||||
|
**Fecha:** lun 6 jul 2026, 12:38 PM
|
||||||
|
**Adjunto:** `bind_token_api.txt`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
> Buenas tardes, Johan,
|
||||||
|
> Espero te encuentres bien.
|
||||||
|
>
|
||||||
|
> Te comparto en el archivo adjunto (.txt) el token de Bind que se necesitará para comenzar con la construcción del proyecto.
|
||||||
|
> Como dato este token fue generado con el usuario **Arturo Rosas**, cualquier duda o comentario podemos validarlo directamente.
|
||||||
|
>
|
||||||
|
> Quedo al pendiente si se necesita algo más.
|
||||||
|
>
|
||||||
|
> Saludos,
|
||||||
|
> Pedro Ayala — ITSM Analyst / Consultor de Gestión de Servicios TI
|
||||||
|
|
||||||
|
**Respuesta de Erika (12:39 PM, a Johann y Pedro, CC Noe):** "Gracias pedro. Cualquier cosa quedo a sus órdenes. Saludos!"
|
||||||
|
|
||||||
|
**Respuesta de Johann (~3:28 PM, a Erika y Pedro, CC Noe):**
|
||||||
|
|
||||||
|
> Buenas tardes, Pedro:
|
||||||
|
> Muchas gracias, recibido el token. Tomo nota de que se generó con el usuario de Arturo, así lo documento.
|
||||||
|
> Con esto ya puedo comenzar a trabajar con la API. Cualquier duda que me surja se las hago saber, gracias.
|
||||||
|
> Saludos,
|
||||||
|
> Johann
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Respuesta de Noé (4:33 PM, a Johann y Pedro, CC Erika — ⚠️ importancia alta):**
|
||||||
|
|
||||||
|
> Buenas tardes Johan,
|
||||||
|
>
|
||||||
|
> Pedro va a revisar si el acceso es de lectura o también de escritura, por lo que te pido mientras tanto **las salvaguardas necesarias para evitar alguna afectación operativa**.
|
||||||
|
>
|
||||||
|
> @Pedro Alberto Ayala Elizondo, si es necesario crea un usuario específico de solo lectura en Bind para esta etapa de exploración, si la única forma de dar lectura es crear un nuevo usuario y token adelante; la cuenta de Arturo tiene algunos privilegios elevados que creo que no serían necesario en este momento.
|
||||||
|
>
|
||||||
|
> Saludos.
|
||||||
|
|
||||||
|
**Notas:**
|
||||||
|
- Confirma que el token salió del **usuario de Arturo Rosas** — conforme al acuerdo del kickoff (token de Arturo para desarrollo; el de Ara para Power BI). Cierra el pendiente de documentar cuál llave es para qué.
|
||||||
|
- El token viaja en `.txt` adjunto al correo → pasarlo a **gestor de secretos** y no conservarlo en el buzón/descargas.
|
||||||
|
- Firma de Pedro revela su rol formal: **ITSM Analyst / Consultor de Gestión de Servicios TI** (coherente con que él construyó el portal Jira ITSM mostrado en el Discovery).
|
||||||
@@ -30,9 +30,58 @@ Erika revisa el plan de actividades (línea: **"Entrega y validación de accesos
|
|||||||
> **Johann** *(9:53)*: Sii, para BIND solo necesito el token tal cual
|
> **Johann** *(9:53)*: Sii, para BIND solo necesito el token tal cual
|
||||||
> **Johann** *(9:54)*: Lo de azure no me urge hoy y el manual de marca ya lo tengo, gracias:)
|
> **Johann** *(9:54)*: Lo de azure no me urge hoy y el manual de marca ya lo tengo, gracias:)
|
||||||
|
|
||||||
## Bloque 4 — 10:26–10:28 AM — Erika solicita el token y pregunta por la validación técnica
|
## Bloque 4 — 10:26–10:37 AM — Erika solicita el token y pregunta por la validación técnica
|
||||||
|
|
||||||
> **Erika** *(10:26, citando a Johann)*: okey deja lo solicito
|
> **Erika** *(10:26, citando a Johann)*: okey deja lo solicito
|
||||||
> **Erika** *(10:28)*: y sobre esto, se necesita una sesion?
|
> **Erika** *(10:28)*: y sobre esto, se necesita una sesion?
|
||||||
|
> **Johann** *(10:37)*: No, esa validación la hago yo por mi cuenta ya teniendo el token
|
||||||
|
> **Erika** *(10:37)*: ah okey
|
||||||
|
|
||||||
La segunda pregunta refiere a la línea del plan: **"Validación técnica de la API de BIND con la cuenta real · 2 días · mar 07/07 → mié 08/07"**. Respuesta de Johann: no se necesita sesión — es trabajo propio con el token en mano (probar endpoints y compartir resumen); solo si algo no cuadra (permisos/cobertura) pediría 15–20 min con Pedro o Arturo. Pide el token idealmente el martes temprano para cumplir las fechas del plan.
|
La pregunta de las 10:28 refiere a la línea del plan: **"Validación técnica de la API de BIND con la cuenta real · 2 días · mar 07/07 → mié 08/07"**.
|
||||||
|
|
||||||
|
## Bloque 5 — 12:10–12:19 PM — token generado; entrega por correo
|
||||||
|
|
||||||
|
> **Erika** *(12:10)*: johan ya solicitaran el token ahorita te lo comparto
|
||||||
|
> **Erika** *(12:10)*: desconozco si tiene vencimiento
|
||||||
|
> **Erika** *(12:18)*: me comentan que tienen el txt o que como lo necesitas
|
||||||
|
> **Erika** *(12:19)*: por correo te sirve?
|
||||||
|
|
||||||
|
El token ya fue generado dentro de Balam (lo tienen en un .txt); Erika pregunta el formato y ofrece enviarlo por correo. *(Nota: según el kickoff, los tokens de BIND no caducan — 1 por usuario, no caducable.)*
|
||||||
|
|
||||||
|
## Bloque 6 — 12:25–12:40 PM — token ENTREGADO por correo
|
||||||
|
|
||||||
|
> **Johann** *(12:25)*: Sí, por correo y .txt está bien
|
||||||
|
> **Erika** *(12:25)*: Okey
|
||||||
|
> **Johann** *(12:27)*: Muchas gracias, comentenme a qué usuario pertenece ese token para poder documentarlo porfavor
|
||||||
|
> **Erika** *(12:27)*: Okey
|
||||||
|
> **Erika** *(12:40)*: ya se compartio por correo
|
||||||
|
> **Erika** *(12:40)*: cualquier cosa me dices
|
||||||
|
> **Johann** *(2:33)*: De acuerdo, muchas gracias
|
||||||
|
> **Erika** *(2:33)*: de nada / me avisas cualquier cosa
|
||||||
|
|
||||||
|
**Token de BIND entregado por correo (12:40 PM).** Queda pendiente que Balam confirme **a qué usuario pertenece** (Johann lo pidió a las 12:27; Erika quedó en verlo).
|
||||||
|
|
||||||
|
## Bloque 7 — 3:01–3:02 PM — Johann propone sesión de cobranza (martes 7-jul)
|
||||||
|
|
||||||
|
> **Johann** *(3:01)*: oye y otra cosa, hoy nos alcanzó el tiempo para facturación y el envío, pero nos faltó ver la parte de cobranza (cómo le dan seguimiento a los pagos y cuentas por cobrar) / ¿Crees que se pueda mañana a las 7am como habías comentado? O cuando se les acomode esta semana
|
||||||
|
> **Erika** *(3:01)*: Deja revisar la agenda con las personas va
|
||||||
|
> **Johann** *(3:02)*: Va, muchas gracias
|
||||||
|
|
||||||
|
Erika queda en **revisar la agenda** con los involucrados y confirmar.
|
||||||
|
|
||||||
|
## Bloque 8 — 4:34–5:03 PM — salvaguardas sobre el token (eco del correo de Noé)
|
||||||
|
|
||||||
|
> **Erika** *(4:34)*: referente a lo del token que se te compartio
|
||||||
|
> **Erika** *(4:34)*: para evitar complicaciones operativas
|
||||||
|
> **Johann** *(5:01)*: Hola, listo, por el momento estaré haciendo operaciones solo de lectura
|
||||||
|
> **Erika** *(5:03)*: gracias por el entendimiento
|
||||||
|
|
||||||
|
Erika transmite por WhatsApp, en versión breve, la instrucción que Noé envió por correo a las 4:33 pm (ver REGISTRO #31). Johann confirma de forma informal que ya opera en modo solo-lectura.
|
||||||
|
|
||||||
|
## Bloque 9 — 6:36–6:40 PM — confirma el meet de la sesión de cobranza
|
||||||
|
|
||||||
|
> **Erika** *(6:36)*: Listo mande el meet de mañana
|
||||||
|
> **Erika** *(6:36)*: Si lo recibiste?
|
||||||
|
> **Johann** *(6:40)*: Listo, ya me llegó
|
||||||
|
|
||||||
|
Erika envía la convocatoria de Outlook/Teams **"Proceso actual de cobranza- Balam"**, martes 07/07/2026, 7:00–8:00 AM. Organiza Erika Chávez; invitados: Johann, Araceli Sánchez Jiménez, Arturo Rosas Hernández (CC: Noé Rocha) — los mismos perfiles que Johann sugirió el 6-jul a las 3:01 pm (bloque 7). Confirma la sesión propuesta en REGISTRO #30.
|
||||||
|
|||||||
@@ -0,0 +1,38 @@
|
|||||||
|
# Notas — Sesión "Proceso actual de cobranza - Balam" · 7 jul 2026, 7:00–8:00 AM
|
||||||
|
|
||||||
|
**Canal:** Microsoft Teams (convocatoria de Erika del 6-jul, ver REGISTRO #34). **⚠️ Sin grabación** — estas notas las reconstruyó Johann de memoria el mismo día. Tratarlas como memoria, no como transcripción: nada aquí es cita textual.
|
||||||
|
|
||||||
|
**Mostró el proceso:** Arturo Rosas (administración / cuentas por cobrar). Convocados además: Araceli Sánchez y Erika Chávez (CC Noé); la asistencia exacta no quedó registrada.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Exceles de control (mostrados en pantalla)
|
||||||
|
|
||||||
|
- Llevan Exceles con el control de **facturas abiertas**, **facturas canceladas** y **cuánto tiempo llevan de morosos** los clientes — es el aging de facto del proceso actual.
|
||||||
|
- Johann quedó en pedirle esos archivos a Arturo (no se compartieron en la sesión).
|
||||||
|
|
||||||
|
## Flujo actual de un pago
|
||||||
|
|
||||||
|
1. El cliente avisa el pago por **mensaje o correo**, adjuntando su **estado de cuenta**.
|
||||||
|
2. Ese estado de cuenta se comparte con el **despacho** (contable externo), que **registra los pagos en cuentas por cobrar (BIND) uno por uno**.
|
||||||
|
3. Tienen un **Power BI conectado — según mostró Arturo, con su token** — donde ven los gráficos de cartera, pero **no están al día**.
|
||||||
|
|
||||||
|
## Por qué conciliar es difícil
|
||||||
|
|
||||||
|
- Hay clientes cuyo pago **cubre decenas de facturas** a la vez ("10 pesos divididos en 40 facturas") — consistente con el cliente mayor que exige una factura por colaborador (~40, ver REGISTRO #27).
|
||||||
|
- Los avisos/comprobantes llegan **todos bajo el mismo nombre/patrón de referencia** (algo como `num_referencia.pdf`), así que la referencia bancaria **no distingue facturas** → **se concilia por folio** de factura.
|
||||||
|
- Los montos **no siempre cuadran exactos**: al recibir el dinero hay un **fee/comisión** que genera diferencias contra el total facturado — hay que tenerlo en consideración.
|
||||||
|
|
||||||
|
## Caso Acuntia (mostrado en vivo)
|
||||||
|
|
||||||
|
- Arturo pidió a Acuntia por correo la relación de pagos realizados; Acuntia respondió con un **Excel pago ↔ folio de factura**. El control existe de ambos lados, pero **los totales diferían por el fee**.
|
||||||
|
|
||||||
|
## Casos límite mencionados
|
||||||
|
|
||||||
|
- Clientes que pagan **facturas viejas que venían arrastrando** (la aplicación de pagos llega fuera de orden).
|
||||||
|
- Facturas ya pagadas cuyo **registro no está al día** (el estatus en sistema va atrás de la realidad).
|
||||||
|
|
||||||
|
## Lo que buscan
|
||||||
|
|
||||||
|
- Ser **proactivos**: poder establecer **recordatorios** — pasados **1–5 días** (de vencimiento), mandar un correo, o al menos que el atraso **esté visible**.
|
||||||
|
- Evolución en sus palabras: antes eran **reactivos** ("ya hace rato que no me paga"), hoy son **activos**, y quieren ser **proactivos**.
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
# WhatsApp — Erika Chávez (PM, Balam) ↔ Johann · seguimiento del pendiente de BIND
|
||||||
|
|
||||||
|
**Canal:** WhatsApp (+52 1 81 2353 5803) · **Fecha:** 7 jul 2026
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Bloque 1 — 9:28 AM
|
||||||
|
|
||||||
|
> **Erika:** no
|
||||||
|
|
||||||
|
*(Fragmento suelto — el mensaje o pregunta al que responde no está incluido en lo reenviado a Claude. Pendiente aclarar contexto.)*
|
||||||
|
|
||||||
|
## Bloque 2 — 12:27 PM — seguimiento a la línea de BIND en el plan de actividades
|
||||||
|
|
||||||
|
> **Erika** *(12:27)*: johan respecto a este punto, es para hoy y mañana, todo bien, necesitas algo?
|
||||||
|
|
||||||
|
Se refiere a la línea del plan de actividades **"Validación técnica de la API de BIND con la cuenta real"** (mar 07 → mié 08 jul) — la misma actividad que Johann ya adelantó y cerró el 6-jul (ver REGISTRO #32 y `../bind-api-sandbox/`).
|
||||||
|
|
||||||
|
## Bloque 3 — 3:56 PM–5:49 PM — seguimiento al usuario de lectura de BIND
|
||||||
|
|
||||||
|
> **Erika** *(3:56)*: okey
|
||||||
|
>
|
||||||
|
> **Erika** *(4:10)*: Hola Erika, todo bien, solo sería el usuario de lectura qué comentaba Noe pero mientras tanto estoy usando el usuario de Arturo a modo de lectura, no me bloquea por esa parte
|
||||||
|
> este usuario de lectura que comento el inge noé te lo iban a crear o como?
|
||||||
|
>
|
||||||
|
> **Johann** *(5:12)*: Noe le había comentado a Pedro que revisara si era necesario crear un usuario específico
|
||||||
|
>
|
||||||
|
> **Erika** *(5:23)*: ah okey
|
||||||
|
> deja lo reviso con ellos entonces, como no estoy familiarizada con eso por eso no le entiendo y una disculpa eh
|
||||||
|
>
|
||||||
|
> **Johann** *(5:37)*: Sii ntp, de igual manera no me bloquea
|
||||||
|
>
|
||||||
|
> **Erika** *(5:49)*: ah bueno esta bien
|
||||||
|
|
||||||
|
*(Nota: el saludo "Hola Erika" del mensaje de las 4:10 es un lapsus de Erika — dirigido a Johann.)*
|
||||||
|
|
||||||
|
Erika confirma que, mientras se resuelve si Pedro crea el usuario de solo lectura que pidió Noé (ver REGISTRO #31), ella sigue operando con el **usuario de Arturo** a modo de lectura, sin bloqueo. Johann aclara que la decisión de crear o no ese usuario específico quedó en manos de Pedro (por instrucción de Noé), no de Erika. Erika queda en darle seguimiento con el equipo técnico.
|
||||||
@@ -0,0 +1,75 @@
|
|||||||
|
# WhatsApp — Erika Chávez (PM, Balam) ↔ Johann · datos ficticios de cobranza y avance de Fase 0
|
||||||
|
|
||||||
|
**Canal:** WhatsApp (+52 1 81 2353 5803) · **Fechas:** 8–10 jul 2026
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Bloque 1 — 8-jul, 10:09 AM–10:16 AM — validación interna antes de compartir los Exceles de cobranza
|
||||||
|
|
||||||
|
> **Erika** *(10:09)*: Buenos días johan, espero y te encuentres bien
|
||||||
|
>
|
||||||
|
> **Erika** *(10:10)*: estan validando la informacion que solicitaste tecnicamente para poder compartirlo contigo ya que como sabras es informacion delicada, en cuanto me den luz verde te lo hago saber
|
||||||
|
>
|
||||||
|
> **Erika** *(10:10)*: gracias por el apoyo
|
||||||
|
>
|
||||||
|
> **Johann** *(10:13)*: Hola Erika buenos días
|
||||||
|
>
|
||||||
|
> **Johann** *(10:15)*: Sí sin problema, de igual manera si gustan pueden ser datos ficticios, me es útil la estructura que siguen
|
||||||
|
>
|
||||||
|
> **Erika** *(10:16)*: ah super, entonces deja se los comunico por si pudieran hacer un ejemplo ficticio y proporcionartelo para efecto de visualizar el prototipo va
|
||||||
|
>
|
||||||
|
> **Johann** *(10:24)*: Va
|
||||||
|
|
||||||
|
Se refiere a la solicitud de los **Exceles de cobranza** a Arturo (control de facturas abiertas/canceladas/morosidad, Excel tipo Acuntia pago↔folio, estado de cuenta ejemplo — ver REGISTRO #35 y PENDIENTES.md). Balam está internamente validando si puede compartir la información real por ser delicada. Johann ofrece una alternativa de bajo fricción: **datos ficticios**, ya que lo que necesita es la **estructura**, no los datos reales — coherente con la salvaguarda ya acordada de mantener datos reales fuera del repo/documentos.
|
||||||
|
|
||||||
|
## Bloque 2 — 8-jul, 10:50 AM — Balam pide internamente ejemplo ficticio
|
||||||
|
|
||||||
|
> **Erika** *(10:50)*: ya mande mensaje que si se les hace mejor mandar la estructura con datos ficticios solo para efecto de visualizar el prototipo de entregable de la fase 0
|
||||||
|
>
|
||||||
|
> **Erika** *(10:50)*: espero comentarios y te comento va
|
||||||
|
>
|
||||||
|
> **Erika** *(10:50)*: gracias por el apoyo
|
||||||
|
>
|
||||||
|
> **Johann** *(10:54)*: De acuerdo, muchas gracias
|
||||||
|
>
|
||||||
|
> **Erika** *(10:55)*: a ti
|
||||||
|
|
||||||
|
Erika traslada la propuesta de datos ficticios al equipo interno (Arturo/Araceli) y queda en avisar cuando tenga respuesta. Sin fecha comprometida.
|
||||||
|
|
||||||
|
## Bloque 3 — 9-jul, 7:20 PM–9-jul 9:10 PM — Erika pide avance de Fase 0
|
||||||
|
|
||||||
|
> **Erika** *(9-jul, 7:20 PM)*: Buenas tardes johan espero y te encuentres bien, me puedes pasar como vamos con las acts de la fase 0
|
||||||
|
>
|
||||||
|
> **Johann** *(9-jul, 9:10 PM)*: Hola buen día, sí claro, cómo te lo comparto?
|
||||||
|
|
||||||
|
Erika (como intermediaria de seguimiento del plan de actividades) pide un corte de avance de las actividades de **Etapa 0**. Johann responde ya entrada la noche, preguntando el medio preferido para compartirlo — queda sin resolver hasta el día siguiente.
|
||||||
|
|
||||||
|
## Bloque 4 — 10-jul, 8:41 AM–9:39 AM — instrucción de envío y disculpa por sesión de validación no agendada
|
||||||
|
|
||||||
|
> **Erika** *(8:41)*: Así nomas dime que acts y porcentaje de avance ntp
|
||||||
|
>
|
||||||
|
> **Johann** *(8:42)*: Hola buenos días
|
||||||
|
>
|
||||||
|
> **Johann** *(8:44)*: Disculpa, tengo preparado el prototipo visual y hoy estaba agendado para validarlo con ustedes, ayer ya no te recordé y ya no pudimos agendar una reunión una disculpa, si hoy no tienen disponibilidad para revisarlo pudiera compartirles el prototipo con mis comentarios si les funciona
|
||||||
|
>
|
||||||
|
> **Erika** *(8:59)*: Ntp
|
||||||
|
>
|
||||||
|
> **Erika** *(9:00)*: Si manda correo del prototipo con imágenes copia a Pedro al inge Noe y Araceli por favor
|
||||||
|
>
|
||||||
|
> **Johann** *(9:21)*: De acuerdo, te lo envío a ti verdad?
|
||||||
|
>
|
||||||
|
> **Erika** *(9:21)*: A mí y al inge y cc a Pedro nada más
|
||||||
|
>
|
||||||
|
> **Erika** *(9:22)*: A Araceli no
|
||||||
|
>
|
||||||
|
> **Erika** *(9:28)*: Es más
|
||||||
|
>
|
||||||
|
> **Erika** *(9:28)*: Solo a mí y al inge por favor
|
||||||
|
>
|
||||||
|
> **Erika** *(9:28)*: Nosotros se los pasamos internamente
|
||||||
|
>
|
||||||
|
> **Johann** *(9:39)*: De acuerdo
|
||||||
|
>
|
||||||
|
> **Johann** *(9:39)*: Esto te lo paso por aquí? *(pregunta sobre el % de avance de la Fase 0 pedido en el Bloque 3, aún sin responder)*
|
||||||
|
|
||||||
|
Johann reconoce que no confirmó la sesión de validación del prototipo del **viernes 10-jul** (la fecha objetivo que él mismo se había puesto — ver PENDIENTES.md) y, ante la falta de agenda, ofrece compartir el prototipo por correo con sus propios comentarios en vez de esperar a una reunión. Erika acepta y da instrucción de destinatarios, **cambiando de opinión dos veces en el mismo intercambio**: primero pide copiar a Pedro, Noé **y Araceli**; 20 segundos después corrige — sin Araceli; y a los pocos minutos vuelve a acotar más — **solo a Erika y al Ing. Noé, con Pedro en copia**, sin nadie más ("nosotros se los pasamos internamente"). Queda pendiente la respuesta de Johann sobre el % de avance de Fase 0 (pedido desde el 9-jul) — pregunta si lo reporta por este mismo canal.
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
# WhatsApp — Erika Chávez (PM, Balam) ↔ Johann · seguimiento de prototipo, sesiones y accesos
|
||||||
|
|
||||||
|
**Canal:** WhatsApp
|
||||||
|
**Periodo:** 13–15 jul 2026
|
||||||
|
**Evidencia:** capturas compartidas por Johann el 13, 14 y 15-jul.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 13-jul — prototipo, sesión con Arturo y primera factura
|
||||||
|
|
||||||
|
Johann informa que, mientras Balam revisa internamente el prototipo, avanzará con trabajo de backend de la Etapa 1 que no depende de esa validación: base de datos, autenticación/roles y cliente de consulta de BIND. Propone recibir retroalimentación el miércoles para incorporar ajustes sin detenerse.
|
||||||
|
|
||||||
|
Erika responde que la CEO está fuera del país y que la diferencia de horario dificulta validar el prototipo en ese momento. Johann confirma que seguirá trabajando y avisará si fuera necesario ajustar el orden de las actividades.
|
||||||
|
|
||||||
|
Johann pide confirmar una sesión con Arturo para cerrar tres puntos abiertos del Discovery:
|
||||||
|
|
||||||
|
- Flujo de alta de clientes nuevos.
|
||||||
|
- Registro en BIND de las diferencias por comisión/fee.
|
||||||
|
- Excel de particularidades de envío por cliente.
|
||||||
|
|
||||||
|
Erika se compromete a contactar a Arturo. Sobre el Excel de particularidades, confirma que continúa en preparación y que Balam espera entregarlo durante la semana.
|
||||||
|
|
||||||
|
Johann también retoma la propuesta v1.2 enviada el 2-jul y solicita confirmación para emitir la factura inicial de 30 horas. Erika confirma que vio la propuesta, pero explica que tanto la CEO como Noé están fuera del país por trabajo, con aproximadamente ocho horas de diferencia. Queda en revisar cómo pueden validar la emisión.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 14-jul — agenda de sesiones y dependencias de Etapa 1
|
||||||
|
|
||||||
|
Erika informa que Arturo puede reunirse el día que sea más conveniente para Johann. Johann propone martes o miércoles a las 7:00 am.
|
||||||
|
|
||||||
|
Posteriormente, Erika comunica que la CEO desea participar y que la sesión solicitada se moverá al **miércoles 22-jul**, cuando ella se encuentre de regreso en el país.
|
||||||
|
|
||||||
|
Erika propone una segunda sesión, distinta de la anterior, para la **validación del prototipo de la Etapa 0**, el **jueves 23-jul a las 7:00 pm**. Aclara que:
|
||||||
|
|
||||||
|
- La sesión del 22-jul corresponde a dudas y definiciones de la Etapa 1.
|
||||||
|
- La sesión del 23-jul corresponde a la validación del prototipo de la Etapa 0.
|
||||||
|
|
||||||
|
Johann confirma ambas y Erika envía la convocatoria del 23-jul.
|
||||||
|
|
||||||
|
En el seguimiento del plan, Johann aclara que para la Etapa 1 requiere:
|
||||||
|
|
||||||
|
- Un repositorio privado de GitHub creado por Balam, con acceso de escritura.
|
||||||
|
- Acceso acotado a Azure para la infraestructura posterior.
|
||||||
|
|
||||||
|
Se mantiene el ajuste previamente conversado: repositorio primero; Azure durante la semana del 21-jul; CI/CD y gestión de secretos como actividad separada.
|
||||||
|
|
||||||
|
Erika pregunta si aún se necesita la estructura del Excel de cobranza con días vencidos que se había solicitado a Arturo. Johann responde que, si Balam considera que la estructura del prototipo refleja correctamente su operación, ya no será necesario prepararlo; si identifican ajustes, sí servirá como referencia.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 15-jul — repositorio GitHub autorizado
|
||||||
|
|
||||||
|
Erika solicita el usuario de GitHub de Johann y confirma que Noé autorizó la creación del repositorio privado de Balam.
|
||||||
|
|
||||||
|
Johann comparte:
|
||||||
|
|
||||||
|
- **Usuario:** `Johann-28`
|
||||||
|
- **Perfil:** `https://github.com/Johann-28`
|
||||||
|
|
||||||
|
**Estado al cierre:** repositorio autorizado y en proceso de creación/invitación. Falta recibir y validar el acceso efectivo con permisos de escritura.
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
# 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”
|
||||||
|
|
||||||
|
**Johann · 8:40 pm** *(mismo día, en la noche)*
|
||||||
|
|
||||||
|
- Envía el archivo **`Plan-actividades-avance-2026-07-16.xlsx`** (11 kB).
|
||||||
|
- “Buenas noches, una disculpa por la hora”
|
||||||
|
|
||||||
|
**Erika · 8:44 pm**
|
||||||
|
|
||||||
|
- “Buenas noches johan”
|
||||||
|
- *(respondiendo al documento)* “Ntp, gracias”
|
||||||
|
|
||||||
|
## Acciones derivadas
|
||||||
|
|
||||||
|
1. ~~Validar que el acceso al repositorio privado permita lectura y escritura.~~ → **Comprobado el 16-jul:** push exitoso del scaffold inicial (`04d8388..1f7f098`).
|
||||||
|
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.~~ → **Hecho (16-jul, 8:40 pm):** Excel de avance enviado por WhatsApp; Erika acusó recibo.
|
||||||
|
|
||||||
|
## 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 (insumo para la sesión del 22-jul).
|
||||||
@@ -0,0 +1,112 @@
|
|||||||
|
# WhatsApp — Erika Chávez (PM, Balam) ↔ Johann · coordinación de sesiones (validación de prototipo + API Jira), envío del flujo de facturación y reagenda 23→24 jul
|
||||||
|
|
||||||
|
**Canal:** WhatsApp
|
||||||
|
**Fechas:** 2026-07-21 (tarde) a 2026-07-27 (mañana)
|
||||||
|
**Evidencia:** copia del chat compartida por Johann el 27-jul-2026.
|
||||||
|
**Relación con la bitácora:** continúa el hilo del 21-jul ([#51](../bitacora/REGISTRO.md)); cruza con la sesión de reglas del 22-jul ([#54](../bitacora/REGISTRO.md)), la validación del prototipo del 24-jul ([#55](../bitacora/REGISTRO.md)) y la sesión de API de Jira del 27-jul ([#56](../bitacora/REGISTRO.md)).
|
||||||
|
|
||||||
|
> **Nota sobre imágenes:** las capturas no se conservan en el volcado de texto; se infieren por el contexto y se marcan como **📷 [imagen inferida]**.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Transcripción
|
||||||
|
|
||||||
|
### Martes 21-jul (tarde) — horas por Excel y aclaración de cotizaciones/MVP
|
||||||
|
|
||||||
|
**Johann · 12:51 pm** — “Disculpa acerca de las horas, mientras se habilita el flujo de Jira, ¿te las comparto por Excel?”
|
||||||
|
**Erika · 12:52 pm** — “Sí por favor con el entregable y act y horas” · “De cada etapa por favor”
|
||||||
|
**Johann · 12:52 pm** — “De acuerdo”
|
||||||
|
|
||||||
|
**Erika · 12:58 pm** — “disculpa ¿no es esto lo de las cotizaciones?”
|
||||||
|
> 📷 **[imagen inferida — confianza alta]:** captura señalando el punto de cotizaciones (probablemente §1.4 de la propuesta o la fila del Excel).
|
||||||
|
|
||||||
|
**Johann · 1:00 pm** — “Sí pero desde el MVP”
|
||||||
|
**Johann · 1:01 pm** — “También identifiqué un punto que no aparece todavía en el Excel: durante el Discovery surgió la posibilidad de que los tickets de Jira generen automáticamente las cotizaciones en BIND. Para eso eventualmente necesitaríamos acceso técnico a Jira y permisos controlados de escritura en BIND. Esto es para que cuando se cree un ticket de Jira, se cree automáticamente la cotización en BIND.”
|
||||||
|
**Erika · 1:01 pm** *(citando “Sí pero desde el mvp”)* — “¿Qué es MVP?”
|
||||||
|
**Johann · 1:02 pm** — “Esta aplicación que estamos trabajando”
|
||||||
|
**Erika · 1:03 pm** — “Okey” · “ya vi que dice fuera de alcance”
|
||||||
|
> 📷 **[imagen inferida — confianza media]:** posible captura de la §1.3 (Jira “Fuera de alcance”).
|
||||||
|
|
||||||
|
**Johann · 1:04 pm** — “Okay, cualquier cosa me avisas”
|
||||||
|
|
||||||
|
### Miércoles 22-jul — disculpa por la sesión de reglas, propuesta a ajustar, agenda de API Jira y envío del flujo por correo
|
||||||
|
|
||||||
|
**Erika · 11:35 am** — “buenos días Johan, primero que nada una disculpa por no estar en la sesión de hoy, tuve un compromiso personal, sin embargo les avisé a los ingenieros mi ausencia para que pudieran tomar esa reunión importante contigo. Veo que se mencionó lo del ajuste de la integración del sistema Jira ¿vrdd?”
|
||||||
|
**Erika · 11:35 am** — “¿ajustarás la propuesta?”
|
||||||
|
**Erika · 11:35 am** — “y quedó pendiente de nuestro lado la revisión de la API con Jira y el flujo de facturación de Jira ¿vrdd?”
|
||||||
|
**Johann · 11:55–11:56 am** — “Hola buen día” · *(citando su disculpa)* “No hay problema” · *(citando lo del pendiente)* “Sí, así es”
|
||||||
|
**Erika · 11:58 am** — “Okey de acuerdo, deja valido la información correspondiente para mandarte lo solicitado”
|
||||||
|
**Erika · 11:59 am** *(citando “ajustarás la propuesta?”)* — “¿Se ajustará?”
|
||||||
|
**Johann · 12:05 pm** — “Sí, de igual manera entre hoy y mañana te comparto el conteo de horas y la propuesta ajustada”
|
||||||
|
|
||||||
|
**Erika · 1:21 pm** — “Johan, ¿pudieras tener una sesión el viernes a las 7 am para ver lo de la API de Jira junto con el Ing. Noe y mi compañero Pedro?” · “¿Cómo ves?”
|
||||||
|
**Johann · 1:22 pm** — “Sí, me parece bien”
|
||||||
|
**Erika · 1:22 pm** — “Va, deja la agendo, gracias”
|
||||||
|
|
||||||
|
**Erika · 2:26 pm** — “Johan, te confirmo: el correo del flujo de facturación de Jira ya lo mandé” · “Quedo al pendiente de cualquier duda o comentario :)”
|
||||||
|
**Johann · 2:40 pm** — “De acuerdo, muchas gracias :)”
|
||||||
|
**Erika · 2:47 pm** — “A ti, igual la sesión ya se agendó, la del viernes”
|
||||||
|
|
||||||
|
### Jueves 23-jul — reagenda por tema personal de Araceli; validación se mueve a viernes; API Jira se mueve a lunes
|
||||||
|
|
||||||
|
**Erika · 6:53 am** — “Buenos días Johan” · “¿Ya listo?”
|
||||||
|
**Johann · 6:53 am** — “Hola Erika, buenos días” · “¡Listo!”
|
||||||
|
**Erika · 6:54 am** — “¡Súper!” · “Igual comenzaremos con que expliques el prototipo y si hay dudas que se comenten” · “Ten a la mano el archivo que nos compartiste para que proyectes por favor”
|
||||||
|
**Johann · 6:55 am** — “¿Cuál archivo disculpa?”
|
||||||
|
**Erika · 6:55 am** — “El que nos enviaste”
|
||||||
|
**Johann · 6:55 am** — “Oh, el PDF con las capturas”
|
||||||
|
**Erika · 6:55 am** — “Sí”
|
||||||
|
|
||||||
|
**Erika · 7:00–7:01 am** — “Johan” · “Una disculpa, me van avisando que Araceli trae un tema personal y no podrá conectarse, y es de gran importancia que esté ella ya que es la del proceso completo” · “¿Podemos cambiar esta sesión a mañana a las 7 am?” · “Y la que tenías mañana conmigo de la API de Jira moverla a la tarde, no sé cuál sea tu disponibilidad”
|
||||||
|
**Johann · 7:01–7:02 am** — “Entiendo” · “¿En qué horario en la tarde podrían para lo de la API de Jira?”
|
||||||
|
**Erika · 7:02 am** — “A la hora que indiques” · “¿Puedes a las 4-5-6?”
|
||||||
|
**Johann · 7:03 am** — “¿Podría ser después de las 6:30 de casualidad?”
|
||||||
|
**Erika · 7:03 am** — “Sí claro, 6:30 a 7:30 va”
|
||||||
|
**Johann · 7:05 am** — “¿Un poco más cercano a las 7 habrá oportunidad? ¿O ya sería muy tarde?”
|
||||||
|
**Erika · 7:09 am** — “¿A las 7 entonces?”
|
||||||
|
**Johann · 7:10 am** — “Sí, a esa hora me quedaría súper”
|
||||||
|
**Erika · 7:11 am** — “Listo, nos vemos a las 7 am mañana para prototipo y en la tarde 7 pm para lo de la API” · “Gracias Johan y una disculpa”
|
||||||
|
**Johann · 7:27 am** — “Enterado, muchas gracias Erika”
|
||||||
|
|
||||||
|
**Erika · 9:45–9:46 am** — “Johan” · “¿Puedes el lunes a las 7 am?” · “Es que me comenta Pedro que lo puede mañana a esa hora”
|
||||||
|
**Johann · 9:56 am** — “Sí sin problema”
|
||||||
|
**Erika · 10:06 am** — “Súper” · “gracias”
|
||||||
|
**Erika · 10:08 am** — “Listo ya la moví”
|
||||||
|
**Johann · 10:09 am** — “Listo, ya la acepté”
|
||||||
|
**Erika · 10:23 am** — “gracias”
|
||||||
|
|
||||||
|
**Erika · 11:29 am** — “Johan ¿esta actividad se acabó ayer?”
|
||||||
|
> 📷 **[imagen inferida — confianza alta]:** captura de una actividad del Excel/plan (la de escritura/sync en BIND).
|
||||||
|
|
||||||
|
**Johann · 11:37 am** — “Sí, aún no he hecho pruebas de escritura en BIND, pero ya está”
|
||||||
|
**Erika · 11:37 am** — “Okey de acuerdo”
|
||||||
|
|
||||||
|
### Viernes 24-jul — arranque de la sesión de validación del prototipo
|
||||||
|
|
||||||
|
**Erika · 6:57 am** — “Buenos días Johan” · “¿Cómo estás?” · “¿Todo listo?”
|
||||||
|
**Johann · 6:57 am** — “Hola Erika, buenos días” · “Sii” · “Listo” · *(citando “como estas?”)* “Bien ¿y tú?”
|
||||||
|
**Erika · 6:57–6:59 am** — “Bien gracias” · “Pues ahorita le damos entonces va” · “Vamos a conectarnos”
|
||||||
|
**Johann · 6:58 am** — “Va”
|
||||||
|
**Erika · 7:00 am** — “Ya estamos”
|
||||||
|
*(→ inicia la sesión de validación del prototipo, [#55](../bitacora/REGISTRO.md))*
|
||||||
|
|
||||||
|
### Lunes 27-jul — solicitud de avances de Etapa 1
|
||||||
|
|
||||||
|
**Erika · 7:11 am** — “Buenos días Johan, disculpa ¿me puedes pasar los avances de la etapa 1 pls?”
|
||||||
|
**Johann · 7:13 am** — “Buenos días Erika, sin problema”
|
||||||
|
**Johann · 7:14 am** — “Disculpa, ¿de casualidad ya tenemos el Jira para este proyecto?”
|
||||||
|
*(→ este mismo día por la mañana ocurre la sesión de API de Jira con Pedro, [#56](../bitacora/REGISTRO.md), donde se compromete el token PAF)*
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Acciones derivadas
|
||||||
|
|
||||||
|
1. **Johann — enviar conteo de horas (Excel con entregable + actividad + horas por etapa) + propuesta ajustada** por la integración Jira→BIND. Comprometido “entre hoy y mañana” el 22-jul (es decir, para el 23-jul). ⚠️ **Verificar si ya se envió.**
|
||||||
|
2. **Johann — preparar y entregar los avances de la Etapa 1** solicitados por Erika el 27-jul (7:11 am).
|
||||||
|
3. **Conservar el correo del flujo de facturación de Jira** que Erika envió el 22-jul (2:26 pm) — insumo para la integración.
|
||||||
|
4. **Pregunta abierta de Johann (27-jul):** ¿ya existe el Jira del proyecto? → se atiende en la sesión de API del 27-jul ([#56](../bitacora/REGISTRO.md)): Pedro genera el token **PAF** (1 año).
|
||||||
|
|
||||||
|
## Observaciones
|
||||||
|
|
||||||
|
- **Saga de reagenda:** la **validación del prototipo** pasó de jueves 23-jul 7 am → **viernes 24-jul 7 am** (Araceli, dueña del proceso, tuvo un tema personal). La **sesión de API de Jira** pasó de viernes 24-jul 7 am → viernes 24-jul 7 pm → **lunes 27-jul 7 am** (por disponibilidad de Pedro).
|
||||||
|
- Erika reiteró dos veces la pregunta de si **se ajustará la propuesta** por el cambio de la integración Jira — es un pendiente comercial, no solo técnico.
|
||||||
@@ -0,0 +1,473 @@
|
|||||||
|
Flujo para dar de alta clientes nuevos, registros en BIND las diferencias por comisión/fee-Proyecto integraciones Balam
|
||||||
|
Wed, Jul 22, 2026
|
||||||
|
|
||||||
|
0:01 - Johann
|
||||||
|
¡Gracias por ver el vídeo! Buenos días, ¿cómo estás?
|
||||||
|
|
||||||
|
0:33 - Johann
|
||||||
|
¿Qué tal? Bien, ¿y tú?
|
||||||
|
|
||||||
|
0:37 - Unidentified Speaker
|
||||||
|
¿Qué tal todo?
|
||||||
|
|
||||||
|
0:39 - Conference Room (Noe Rocha) - Speaker 1
|
||||||
|
Bien, gracias a Dios. A ver, ya nada más faltaría... Este...
|
||||||
|
|
||||||
|
0:46 - Unidentified Speaker
|
||||||
|
¿Arturo?
|
||||||
|
|
||||||
|
0:47 - Unidentified Speaker
|
||||||
|
Y ahorita arrancamos. Va, sin problema.
|
||||||
|
|
||||||
|
0:50 - Unidentified Speaker
|
||||||
|
Bien.
|
||||||
|
|
||||||
|
0:51 - Johann
|
||||||
|
Sí, me comentan que estuvieron fuera del país.
|
||||||
|
|
||||||
|
0:56 - Conference Room (Noe Rocha) - Speaker 1
|
||||||
|
Sí, estuvimos con...
|
||||||
|
|
||||||
|
0:58 - Conference Room (Noe Rocha) - Speaker 1
|
||||||
|
algunos temas con clientes y sí nos enfrentamos bastantes días aquí de la oficina. Pero ya de vuelta para retomar actividades y avanzar con este tema que nos interesa mucho. ¿Y qué tal fue?
|
||||||
|
|
||||||
|
1:12 - Unidentified Speaker
|
||||||
|
¿A qué país fueron?
|
||||||
|
|
||||||
|
1:14 - Conference Room (Noe Rocha) - Speaker 1
|
||||||
|
Estuvimos en España con unos clientes de España y aprovechamos y dimos una vuelta allá a Polonia en Cracovia para conocer. En Europa Central Es muy bonito viajar por allá y conocer otras culturas, otras ciudades, otras lenguas. Los polacos no entienden nada. Lo bueno es que hablan muy bien inglés. Hablan muy bien inglés. Entonces te puedes entender bien.
|
||||||
|
|
||||||
|
1:46 - Unidentified Speaker
|
||||||
|
Sí, sí, sí.
|
||||||
|
|
||||||
|
1:48 - Conference Room (Noe Rocha) - Speaker 1
|
||||||
|
Sí, sí, sí. Así fue. Estuvo muy padre. Bastante recomendable. Bien lejos, está muy lejos.
|
||||||
|
|
||||||
|
1:56 - Johann
|
||||||
|
Sí, me imagino hasta el otro lado del océano.
|
||||||
|
|
||||||
|
1:59 - Conference Room (Noe Rocha) - Speaker 1
|
||||||
|
Sí, y luego todavía llegando al otro lado es, tríncate otras cuatro horas para llegar a esos lugares en vuelo, entonces, tres horas y media, cuatro horas, depende de dónde vayas. Pero sí, es impresionante, o sea, mucho bosque, mucho verde, muy bonito.
|
||||||
|
|
||||||
|
2:16 - Johann
|
||||||
|
Muy bonito.
|
||||||
|
|
||||||
|
2:17 - Unidentified Speaker
|
||||||
|
¿Qué tal, Arturo?
|
||||||
|
|
||||||
|
2:18 - Johann
|
||||||
|
Buenos días.
|
||||||
|
|
||||||
|
2:19 - Unidentified Speaker
|
||||||
|
Hola Arturo, buenos días.
|
||||||
|
|
||||||
|
2:22 - Unidentified Speaker
|
||||||
|
¿Dónde te escuchas Arturo?
|
||||||
|
|
||||||
|
2:25 - Unidentified Speaker
|
||||||
|
¿Ahí me escuchan?
|
||||||
|
|
||||||
|
2:27 - Arturo Rosas Hernandez
|
||||||
|
Ahí te escuchamos. Excelente. Buenos días, ¿cómo están? Todo muy bien.
|
||||||
|
|
||||||
|
2:34 - Conference Room (Noe Rocha) - Speaker 1
|
||||||
|
Bien, ¿y tú? Que bueno, que bueno, todo bien.
|
||||||
|
|
||||||
|
2:40 - Arturo Rosas Hernandez
|
||||||
|
Gusto saludarte Johann, ¿cómo estás?
|
||||||
|
|
||||||
|
2:44 - Arturo Rosas Hernandez
|
||||||
|
También, también, buenos días.
|
||||||
|
|
||||||
|
2:48 - Arturo Rosas Hernandez
|
||||||
|
Buenos días, buenos días.
|
||||||
|
|
||||||
|
2:49 - Unidentified Speaker
|
||||||
|
Bueno, pues listo.
|
||||||
|
|
||||||
|
2:51 - Arturo Rosas Hernandez
|
||||||
|
Pues empecemos, Johann.
|
||||||
|
|
||||||
|
2:52 - Conference Room (Noe Rocha) - Speaker 1
|
||||||
|
Yo sé que por ahí habías pedido un foro con Erika y era para ver flujos, ¿no? El flujo de clientes nuevos, los registros de Vine, las diferencias por comisión, los fideiproyectos. Esto supongo que para ajustar o documentar lo que necesitas dentro del propio desarrollo y entendimiento de cómo suceden las cosas, ¿verdad?
|
||||||
|
|
||||||
|
3:17 - Johann
|
||||||
|
Sí, justamente.
|
||||||
|
|
||||||
|
3:18 - Conference Room (Noe Rocha) - Speaker 1
|
||||||
|
Pues mira, aquí tenemos a Arturo, y un poquito escuchando también la conversación, aunque no va a estar en la presentación porque está en otros temas, está Araceli. Entonces, pues, si quieres, ve preguntando y aquí vamos aclarando con Arturo el ASIS, ¿no?, cómo está funcionando ahorita.
|
||||||
|
|
||||||
|
3:37 - Johann
|
||||||
|
Ok, de acuerdo, me parece bien. Ahora mismo, estaría bien si pudiéramos comenzar con cómo se da de alta un cliente en Bind. Los ejercicios que habíamos visto previamente eran cuando el cliente de Vine ya existía. Y esa parte ya me quedó claro y Arturo nos lo explicó muy bien. No sé si pudiéramos ver un ejemplo de cómo se crea.
|
||||||
|
|
||||||
|
4:02 - Conference Room (Noe Rocha) - Speaker 1
|
||||||
|
En la plataforma, directamente en Vine.
|
||||||
|
|
||||||
|
4:04 - Arturo Rosas Hernandez
|
||||||
|
Sí, creo que se cortó un poquito. Te cortas un poquito, Johann. Te preguntaba, Johann, ¿sería sobre la plataforma?
|
||||||
|
|
||||||
|
4:12 - Conference Room (Noe Rocha) - Speaker 1
|
||||||
|
O sea, ¿entrar a la plataforma y que desde principio a fin cómo se da un Dalton cliente, ¿verdad?
|
||||||
|
|
||||||
|
4:21 - Unidentified Speaker
|
||||||
|
En Bain.
|
||||||
|
|
||||||
|
4:22 - Unidentified Speaker
|
||||||
|
Sí.
|
||||||
|
|
||||||
|
4:23 - Conference Room (Noe Rocha) - Speaker 1
|
||||||
|
A ver, adelanta tú.
|
||||||
|
|
||||||
|
4:25 - Arturo Rosas Hernandez
|
||||||
|
Ok, déjenme para eso entonces les muestro mi pantalla y me confirman por favor cuando ya la aprenda.
|
||||||
|
|
||||||
|
4:34 - Unidentified Speaker
|
||||||
|
Ya, está bien.
|
||||||
|
|
||||||
|
4:36 - Arturo Rosas Hernandez
|
||||||
|
Ok, déjenme ver si me acuerdo, porque esto no sucede muy a menudo así que en el apartado de compras en el módulo de proveedores ahí existen todos los proveedores que tenemos dados de alta actualmente ahí los podemos consultar y me gustaría empezar con la opción de modificar entonces modificar existen dos opciones en esta pantalla que es la de editar, editar el producto como tal o simplemente eliminar. Y agregarlo.
|
||||||
|
|
||||||
|
5:15 - Conference Room (Noe Rocha) - Speaker 1
|
||||||
|
Sí, obviamente y agregarlo.
|
||||||
|
|
||||||
|
5:17 - Arturo Rosas Hernandez
|
||||||
|
Entonces, bueno, no sé qué tan importante sea la parte de modificar y eliminar, pero bueno, está aquí en esta pantalla. Y lo siguiente, la siguiente opción que nos da es obviamente exportar, importar o generar notas de crédito. Una lista de EFOS y el apartado de agregar. Entonces en el botón de agregar simplemente le damos clic y nos abre una siguiente pantalla que es la pantalla de agregar proveedor y nos pide dos pestañas de información. Una es la de detalle que incluye o pide todo el tema fiscal, por decirlo de alguna manera, y la dirección. Entonces hace poco justo dimos de alta la razón social de Frisa, entonces la información para esta pantalla de detalle se toma básicamente de la constancia de situación fiscal del cliente. Y el estado de cuenta. Exacto. Hasta aquí, hasta la parte de aquí, razón social, RFC, nombre comercial y categoría, sería la parte fiscal. Y lo siguiente es su estado de cuenta, como bien lo dijo Noe, su estado de cuenta bancaria, que sería el banco, y nada más. El banco es la cuenta clave porque los días de crédito y el monto de crédito vienen de la parte comercial, desde la parte de negociación comercial, de los acuerdos exactos, ahí la parte comercial decide o negocia con el cliente la cantidad de crédito o de días de créditos que se tendrá, el correo electrónico que se usará o se para la comunicación y la sucursal, digo aquí nosotros no tenemos sucursales todavía en BIND pero si en algún momento hubiera más de una sucursal aquí estaría la matriz como actualmente la tenemos que es la que utilizamos para todos y pudiera haber más sucursales de BALAM pues sí sobre todo de BALAM obviamente el teléfono, el teléfono de contacto o de comunicación y algunos comentarios también podemos definir la parte del lenguaje de los documentos si tenemos clientes que por ejemplo BICTEX si no mal recuerdo que la comunicación debe ser en inglés entonces para ese caso o para casos como ese elegiremos el formato o lenguaje inglés y le damos siguiente no sé si quieran que hagamos una una alta de prueba para que veamos el flujo, o solo así platicado es suficiente Johann?
|
||||||
|
|
||||||
|
8:32 - Conference Room (Noe Rocha) - Speaker 1
|
||||||
|
No sé si algo pase adicional, porque si no tenemos datos, supongo que algunos datos reales para esto. Tú dime Johann, si al menos esta primera vista te es suficiente para entender un poco ¿Cómo dar datos a un cliente? Se pide la información fiscal, su CFDI, luego su estado de cuenta. Con esa información, ahora sí volvemos aquí y aquí lo alimentamos y le damos de alta. Vamos a suponer que le dimos de alta a alguien. Vámonos a la opción de editar un usuario para ver qué datos aparecen. Digo, falta la parte de las direcciones.
|
||||||
|
|
||||||
|
9:13 - Arturo Rosas Hernandez
|
||||||
|
Ah, ok. Adelante de dirección. Obviamente, en la pestañita de direcciones pide el país. ¿A qué país corresponde? Si tenemos clientes, por ejemplo, en España, podríamos tener algún cliente en España o en Estados Unidos. Entonces, ahí hay que definirlo. El estado, el municipio, la localidad, la calle. Esta parte también viene de la constancia de situación fiscal. La colonia, el código postal, si tiene número exterior, si tiene número interior. El código de la colonia código es de opcional. La localidad. Y ahora sí, una vez que terminamos de llenar esta documentación, de esta información, le damos guardar. Y ahora sí, la pestaña anterior.
|
||||||
|
|
||||||
|
9:57 - Conference Room (Noe Rocha) - Speaker 1
|
||||||
|
Yo sumaría, Arturo, a lo que dices, es que agregar a clientes es lo mismo que agregar a proveedores, ¿verdad, Arturo?
|
||||||
|
|
||||||
|
10:06 - Arturo Rosas Hernandez
|
||||||
|
Sí, sí, correcto.
|
||||||
|
|
||||||
|
10:07 - Conference Room (Noe Rocha) - Speaker 1
|
||||||
|
Es el mismo procedimiento clientes como proveedores. Correcto. Te lo digo porque estaba viendo que había algunos proveedores también dado de alta y después de agregarlo supongo que lo que pasa es que te genera un ID de proveedor.
|
||||||
|
|
||||||
|
10:25 - Arturo Rosas Hernandez
|
||||||
|
Sí, entonces justo lo que dice Noe, la parte de agregar proveedores y agregar clientes, esta es la pestaña de agregar clientes, digo la pantalla, esa está en el módulo de ventas y clientes entonces la anterior es compras y proveedores y esta es venta y clientes ok la información como lo dijimos básicamente inicia en el botón de agregar también tiene las mismas dos pestañas detalle y direcciones básicamente se llena la misma información que mencionábamos hace un rato razón social nombre comercial y rfc Los días de crédito, el monto de crédito. Las ventas esperadas y los créditos negociados. Lista de precios, si existe lista de precios. El correo electrónico de comunicación. El número de cuenta. El descuento pactado, si existe un descuento pactado al inicio. Los teléfonos de contacto. Cómo se enteró de nosotros. Hasta ahorita este apartado no lo usamos. El uso SFDI. En algunos casos, Si tienen un CFDI específico, la mayoría de las veces todos aceptan gastos en general, pero depende de su constancia de situación fiscal. El régimen fiscal, este también viene dado por su constancia de situación fiscal y algunos comentarios finales. En el apartado de direcciones, este sí es idéntico al de proveedores, le damos guardar. Y hasta aquí, no sé si haya duda, Johann.
|
||||||
|
|
||||||
|
12:02 - Unidentified Speaker
|
||||||
|
OK.
|
||||||
|
|
||||||
|
12:03 - Johann
|
||||||
|
Hasta ahora me quedan claros los detalles que se tienen que dar de alta cuando se agrega un cliente y un proveedor. Igual, como comentan hoy, si es verdad que crear uno nuevo implicaría datos reales, ¿no? Entonces, con este proceso que la platicadito, me queda claro que datos son los que se piden.
|
||||||
|
|
||||||
|
12:28 - Arturo Rosas Hernandez
|
||||||
|
Sí, y apenas tiene unos cuantos días esta frisa, entonces hubiera sido buena oportunidad para guardarlo. Justo ayer platicábamos Sara y yo que voy a ir grabando algunos procesos que surjan como nuevos, entonces los voy a grabar y después estos tipos muy sencillos manualitos que compartí ahí con Eric para el tema de cómo generar las facturas de algunos clientes, algo parecido a hacer, ¿no? Pero grabarlo y luego generar un Word y tal vez un PowerPoint con algunas imágenes y así, hacer un tipo proceso, procedimiento muy sencillo, ir haciéndolo cada vez que se presente la oportunidad de procesos o de clientes nuevos, proveedores, facturación. O sea, Frisa ahorita justo ya vamos proceso de facturación ya tengo un caso entonces justo lo voy a grabar y lo voy a documentar para que en necesidades como esta pues ya se tenga la información y se hagan ejemplos reales bueno ok entonces una vez que se guarda la información ya podemos regresar al apartado de clientes que es básicamente como el de proveedores y aparece en listados todos los clientes que tenemos lo que decía que Freeza por aquí ya debe estar, si no mal recuerdo. Lo que está acá también es el último que registramos y justo lo que decía Noe, se le asigna un ID que es el 1016. Este ID es interno, es exclusivo de Vine y de Valamp. Si tuviéramos en algún momento Vine para diferentes empresas, pues no, serían IDs diferentes. Existen dos opciones, que es la opción de editar, como lo comentaba al inicio, en esta, si entramos, ahí vamos a ver toda la información que nosotros registramos, la parte de detalle, la parte de direcciones, la parte de los contactos, si hay adicionales, información adicional que se tenga la necesidad de agregar, y algunos archivos. Aquí podemos agregar la contancia de situación fiscal, la cuenta bancaria con la que se dio de alta, tal vez algún tema de NDA o algún contrato marco. Bueno, pues aquí lo podemos subir y quedaría ahí en un solo lugar. Desde aquí tenemos el módulo de acciones que en las diferentes pestañas, nos muestra opciones adicionales esta pantalla solamente es de visualización es editable hasta que le damos clic en el botón de editar ahí sí una vez que se cambie cualquier información pues nos dará la opción de guardarla al final antes de salir principalmente en la pestaña de detalles ahorita le voy a dar cancelar para no borrar no modificar nada que no quiera modificar y pues básicamente no sé si haya alguna duda alguna información adicional y yo nada más cuando se va a dar de alta un cliente o un proveedor pero en este caso el cliente nace primero de la propuesta comercial de la del acuerdo comercial que sigue con ese cliente y entonces si se les da se empieza a dar de alta pero primero viene del área comercial hoy vamos a tener un nuevo cliente o de evolución a esta propuesta a algo ya en firme entonces hay que empezar a darlo de alta ese es para los nacionales para los internacionales ese me imagino que no te ha tocado Arturo este pero creo que es algunos algunos datos son diferentes como el caso de Acuntia que es de europeo si no no me ha tocado pero bueno podemos ver el ejemplo de Acuntia también debería haber algún documento legal o fiscal del país o de la región en donde esté dado de alta el proveedor entonces igual pediríamos una un documento de este tipo similar al al RFC o la constancia de situación fiscal de la de la empresa pero es aterrizada a la el CFDI que dice sin efectos fiscales al ser una expresa extranjera.
|
||||||
|
|
||||||
|
17:08 - Unidentified Speaker
|
||||||
|
Correcto.
|
||||||
|
|
||||||
|
17:09 - Conference Room (Noe Rocha) - Speaker 1
|
||||||
|
Tiene una pequeña variación.
|
||||||
|
|
||||||
|
17:11 - Arturo Rosas Hernandez
|
||||||
|
Y el RFC te da un genérico, te da simplemente un consecutivo genérico y te lo propone la herramienta, entonces ahí no hay mucho que hacer. Aquí sí, lo importante, como decía Noe, es que el uso de CFDI sea sin efectos fiscales, porque esto no declara impuestos. O deduzca impuestos de ningún tipo, ni ESR ni van a nada. Adelante.
|
||||||
|
|
||||||
|
17:37 - Conference Room (Noe Rocha) - Speaker 1
|
||||||
|
Oye Arturo, no me queda duda, obviamente la factura se hace solamente PDF, no se genera XML ni se timbra nada ante el SAT, ¿verdad?
|
||||||
|
|
||||||
|
17:46 - Arturo Rosas Hernandez
|
||||||
|
Correcto, sí, qué buen comentario. Entonces, para los nacionales, una vez que generas una factura, pues te genera dos archivos, el PDF y el XML. En temas de fiscalía mexicana, el XML es el que realmente lee sobre todo los bueno cualquier sistema o los sistemas que se habían creado pero sobre todo el del SAT este es el que realmente lee la información que trae la información trae el código y es el que lee los sistemas del SAT y el pdf es simplemente una representación gráfica una imagen de lo que contiene el xml pero el xml es importante y el otro es solamente pues visual un documento visual que normalmente se usa para transacciones de personas de morales o físicas etcétera pero para los sistemas la lectura de los sistemas se hace a través del xml y porque lo menciono porque para para los proveedores nacionales por eso es sin efectos fiscales porque estos no le no se leen de ninguna forma entre sistemas o sea no genera código por decirlo de alguna manera sólo genera un archivo pdf y ese no hay forma de que lo lean los sistemas no simplemente se registra como gasto extranjero y no genera obligaciones fiscales. La información que se llena básicamente es esta, muy parecida a lo que se llena de lado mexicano, las direcciones pues también la dirección que viene contenida en el documento fiscal o legal que los puedan compartir, los contactos normalmente los que nos que tenemos. Este cliente no lo di de alta yo, entonces ahí a lo mejor faltaron algunas información. Pero hay que agregarlas. Pero sí es importante llenarlas. De hecho, que bueno, ahorita me la voy a anotar porque voy a revisar los contactos que una tenemos vez y voy de a ir llenando la información que se tenga y que no esté registrada aquí en la herramienta, básicamente.
|
||||||
|
|
||||||
|
19:49 - Conference Room (Noe Rocha) - Speaker 1
|
||||||
|
Muy bien.
|
||||||
|
|
||||||
|
19:50 - Conference Room (Noe Rocha) - Speaker 1
|
||||||
|
¿Qué más de esta parte de clientes, Johann, podrías ver? No, por esta parte ya entiendo mejor el proceso.
|
||||||
|
|
||||||
|
19:59 - Johann
|
||||||
|
Por esta parte de clientes sería todo. Mi siguiente pregunta, si es que ya no hay nada que contar en ¿Cómo es que se registra en Bind? ¿O qué se hace con esa comisión?
|
||||||
|
|
||||||
|
20:36 - Conference Room (Noe Rocha) - Speaker 1
|
||||||
|
Sí.
|
||||||
|
|
||||||
|
20:37 - Arturo Rosas Hernandez
|
||||||
|
Tengo un poco de duda, si quieres comentarlo, pero el proceso que creamos ya no nos permite ver a nosotros, o a mí, o a nosotros, o al puesto actual.
|
||||||
|
|
||||||
|
20:54 - Conference Room (Noe Rocha) - Speaker 1
|
||||||
|
A ver, si quieres, vuelvo a preguntar, Johann, solo para que ahora te escuche. A ver, dime.
|
||||||
|
|
||||||
|
21:04 - Johann
|
||||||
|
Bueno, ¿ahí me escuchó bien?
|
||||||
|
|
||||||
|
21:06 - Conference Room (Noe Rocha) - Speaker 2
|
||||||
|
Buenos días.
|
||||||
|
|
||||||
|
21:07 - Conference Room (Noe Rocha) - Speaker 2
|
||||||
|
¿Qué tal?
|
||||||
|
|
||||||
|
21:08 - Conference Room (Noe Rocha) - Speaker 1
|
||||||
|
Buenos días.
|
||||||
|
|
||||||
|
21:08 - Johann
|
||||||
|
¿Me escuchó bien? Sí. De acuerdo. Buenos días, Araceli. Buenos días. Aquí. Mi pregunta era, me comentaban que había momentos en donde los montos de factura no coincidían por una fee que ustedes pagaban, ¿cierto? Sí. Entonces, mi pregunta era, ¿qué sucedía con esa fee? Si se registraba en Pero eso ya no nos corresponde a nosotros.
|
||||||
|
|
||||||
|
21:33 - Conference Room (Noe Rocha) - Speaker 2
|
||||||
|
Quien lo registra es el despacho. Hace cuenta que nuestro proceso nada más es en hacer la conciliación y ahí siempre viene un FII y el FII depende del banco que lo mande. Por ejemplo, cuando es de Accent, si es cierto banco, nos cobra un FII. Pero, por ejemplo, si es un banco texano, nos cobra otro FII, aunque sea el depósito al mismo Banorte. Entonces, cuando se hace la conciliación, lo que nosotros pedimos es el estado de cuenta, por eso es importante, del lado del cliente, para saber cuáles facturas nos pagaron, porque nos llega un monto más o menos parecido sin el FII. Pero ese FII, quien lo hace a nivel contable y quien lo deduce y quien hace todo lo necesario, es el despacho. De nuestro lado no hay nada que hacer, No hay nada que conciliar. La conciliación nada más de nuestro lado viene del estado de cuenta de las facturas que pagaron con lo que nos depositó.
|
||||||
|
|
||||||
|
22:39 - Johann
|
||||||
|
Pero de esa parte nosotros no hacemos el asiento contable. Ok, entiendo. Entonces es el despacho quien registra este fee en BIM, ¿cierto?
|
||||||
|
|
||||||
|
22:50 - Conference Room (Noe Rocha) - Speaker 2
|
||||||
|
Exactamente. Ellos hacen un manejo contable para empatar y mapear y que no se nos vaya quedando un residual como como deuda del lado del cliente porque pues eso debería de ser, no? O sea, oye, no me pagaste los ciento cincuenta, me llegaron ciento cuarenta y dos, me debes ocho pesos. No, ellos hacen esa deducción para a nivel fiscal y contable. Siempre que digas o sabes que me pagaste el cien por ciento, aunque no haya sido así con un FI, verdad?
|
||||||
|
|
||||||
|
23:19 - Conference Room (Noe Rocha) - Speaker 2
|
||||||
|
Pero eso ya es por parte del despacho que son FIS bancarios.
|
||||||
|
|
||||||
|
23:23 - Conference Room (Noe Rocha) - Speaker 2
|
||||||
|
Exactamente.
|
||||||
|
|
||||||
|
23:24 - Conference Room (Noe Rocha) - Speaker 1
|
||||||
|
Sí, la transacción. Es lo que cobra el banco por mover el dinero de donde está a las cuentas mexicanas.
|
||||||
|
|
||||||
|
23:33 - Conference Room (Noe Rocha) - Speaker 2
|
||||||
|
Pero esa parte ya no nos corresponde a nosotros, Johann. Nada más si habría que, por ejemplo, cuando el agente de IA haga la conciliación, que identifique que ese es un fin bancario.
|
||||||
|
|
||||||
|
23:47 - Johann
|
||||||
|
¿Verdad?
|
||||||
|
|
||||||
|
23:48 - Unidentified Speaker
|
||||||
|
El proceso.
|
||||||
|
|
||||||
|
23:48 - Johann
|
||||||
|
El proceso. Ok, entiendo.
|
||||||
|
|
||||||
|
23:51 - Conference Room (Noe Rocha) - Speaker 1
|
||||||
|
sería el despacho que autoriza o por saldada una factura con con dicha diferencia exactamente a nivel contable a nivel contable digamos a nivel cuenta nosotros no vamos a poder vamos a tener a lo mejor al momento de la conciliación antes de llevar un tema contable vamos a ver la diferencia pero esa diferencia corresponde al fin no sabemos no tenemos los reyes exactos de cuánto va a ser el fin si es por un monto si es por una cantidad, si es porque ese día es el Día de la Virgen y se lo quiero cobrar más.
|
||||||
|
|
||||||
|
24:26 - Conference Room (Noe Rocha) - Speaker 2
|
||||||
|
Pero siempre viene marcado en el estado de cuenta. Por ejemplo, no es sorpresa en el sentido de que, por ejemplo, dice IVA sobre comisión, comisión de IVA. Yo lo digo porque la gente lo lee y sabe que es una comisión bancaria. Y nada más en los pagos que se reciben de moneda extranjera, es en donde tenemos un FII. Los pagos nacionales no hay ningún FII. Ahí sí nos tiene que cuadrar centavos, verdad? Y nada más en los extranjeros. Por eso en los extranjeros es súper importante que nos pasen en el estado de cuenta. El cliente, el cliente y por ejemplo, no siempre es así, porque por ejemplo, Axios nos paga a nuestra cuenta de Banorte de dólares. Pero por ejemplo, tenemos también la de Y hay otro cliente que se llama Big Tech, que nos paga directamente de un banco americano a otro banco americano. Ahí no nos quitan el fee, la comisión.
|
||||||
|
|
||||||
|
25:27 - Conference Room (Noe Rocha) - Speaker 1
|
||||||
|
O sea, la única forma que tenemos de saber el dato del fee en un pago internacional es pedirle el estado de cuenta al cliente.
|
||||||
|
|
||||||
|
25:38 - Conference Room (Noe Rocha) - Speaker 2
|
||||||
|
O por ejemplo, en Big Tech te hace match la factura con el deporte.
|
||||||
|
|
||||||
|
25:44 - Conference Room (Noe Rocha) - Speaker 1
|
||||||
|
¿Por qué? Porque ese es cuenta americana.
|
||||||
|
|
||||||
|
25:47 - Conference Room (Noe Rocha) - Speaker 2
|
||||||
|
Banco, banco americano.
|
||||||
|
|
||||||
|
25:48 - Conference Room (Noe Rocha) - Speaker 1
|
||||||
|
Pero en el caso específico del ingreso que es Europa a México. Ahí sí. Y ese fin no lo podemos identificar a menos de que nos manden del estado de cuenta.
|
||||||
|
|
||||||
|
25:59 - Conference Room (Noe Rocha) - Speaker 2
|
||||||
|
Se identifica porque en el estado de cuenta viene la comisión. Pero de todas maneras, acuérdate de que diga Johann aquí ya entramos en otro tema. En este caso de ACCIONS hay muchas facturas. Por el mismo monto. Entonces, por eso tenemos que pedir el estado adecuado.
|
||||||
|
|
||||||
|
26:19 - Johann
|
||||||
|
Para estar seguro de que la factura corresponde a qué monto. Exactamente. Entiendo. Esa parte ya me queda clara. Muchas gracias. Gracias. Entonces, este monto de fee se registra en Bain por parte del despacho.
|
||||||
|
|
||||||
|
26:39 - Conference Room (Noe Rocha) - Speaker 1
|
||||||
|
¿En los internacionales específicamente? Y para que no se obtenga un margen, una deuda por parte del cliente. Prácticamente esto lo vamos a ver más con este proveedor, con Acuntia, hoy por hoy. Puede pasar con otro cliente, sí, si tenemos otro proyecto que esté en la misma situación, que sea un banco europeo, que nos tenga que mover el dinero hasta México. Y ahí, evidentemente, pues va a venir la comisión.
|
||||||
|
|
||||||
|
27:08 - Johann
|
||||||
|
Ok. Listo. Muchas gracias por esa explicación. Ya me quedo más claro y voy a trabajar en el productivo también.
|
||||||
|
|
||||||
|
27:17 - Conference Room (Noe Rocha) - Speaker 1
|
||||||
|
Sí, y en el caso de los nacionales pues está identificado directamente en el estado de cuenta.
|
||||||
|
|
||||||
|
27:24 - Unidentified Speaker
|
||||||
|
No hay sorpresa.
|
||||||
|
|
||||||
|
27:26 - Johann
|
||||||
|
Ok, de acuerdo. Ahora, sí, por esta parte ya me quedo muy claro. Muchas gracias. Y otra cosa que me gustaría ver también es respecto a lo de Jira. Comentaba con Erika que durante las sesiones previas me mostraron el proceso y cómo es que de las solicitudes de Jira es donde nacían los tickets para una facturación. De esta manera, aunque no está la propuesta inicial, me gustaría ver la la posibilidad o proponer la solución de que desde Jira se carguen a Bind como cotizaciones, que es el primero de los pasos para llegar a hacer una factura ¿no? Porque sería cotización, prefactura y factura. Sí, yo entiendo eso perfectamente Johann.
|
||||||
|
|
||||||
|
28:23 - Conference Room (Noe Rocha) - Speaker 1
|
||||||
|
Cuando lo hablamos en su momento no estaba, no se tenía en uso Jira para el tema de las propuestas. Entonces sí, yo recuerdo perfectamente que habíamos dicho que inicialmente ni BUC ni GIRA iban a estar dentro del proceso. Lo que pasó en el tiempo es que evolucionó el proceso interno a que todo se fuera por GIRA, hoy por hoy. Entonces de lo que dijimos a lo que está pasando ahorita, hay una variante, que GIRA sí entra en la fórmula hoy por hoy. Para este tema de GIRA y explorar el tema de GIRA, cómo se va a conectar si hay un Si hay un API para extraer la información. No lo explotó yo, habría que verlo con Pedro. Y si quieres, en esta etapa que ahorita estamos de exploración, contemplarlo y ya me dices, oye, bueno, pues a lo mejor aquí va a tener que ser un pequeño ajuste y lo vemos, lo ponemos sobre la mesa, ¿sí?
|
||||||
|
|
||||||
|
29:16 - Johann
|
||||||
|
OK. Sí, me parece bien. Porque en ese caso, les quería preguntar acerca de, dentro de los tickets de gira, ¿cuál es el estatus que un ticket listo para llegar a ser facturación? Sí, mira.
|
||||||
|
|
||||||
|
29:32 - Conference Room (Noe Rocha) - Speaker 1
|
||||||
|
En teoría debe estar en completo. Hay un estatus muy específico de Jira que dice resuelto. Porque mira, aquí lo vemos. Por ejemplo, estos son todos los inicia, dale flujos un actuales. Poquito de zoom Arturo, ahí inicia, luego se va abierto y luego de abierto hacia abajo se va en proceso de facturación, ese proceso de facturación también se puede cancelar, o sea puede ser que ya no ya no ocurra nada y se cancela y ahí muere, pero vamos a seguir suponiendo que ya que se vuelve en factura.
|
||||||
|
|
||||||
|
30:10 - Conference Room (Noe Rocha) - Speaker 2
|
||||||
|
Dale para abajo.
|
||||||
|
|
||||||
|
30:11 - Conference Room (Noe Rocha) - Speaker 1
|
||||||
|
Luego se va a una validación, porque puede ser lo que vimos ahorita, es una validación para una factura nacional o es una factura extranjera. Y esas dos requieren dos procesos diferentes, como ya vimos ahorita. Y la otra nada más la pura factura. Después, ya que está en esa validación, qué tipo de moneda, qué tipo de facturación, en términos generales.
|
||||||
|
|
||||||
|
30:32 - Arturo Rosas Hernandez
|
||||||
|
Y ahora sí, se manda a facturar con todos esos preámbulos, con todos esos detalles, se van directamente a facturar en BIND aquí no dice que se facture en BIND simplemente que aquí todo lo que se tiene que facturar de ese periodo ya está digamos este ya lo tenemos claro que se tiene que ir a facturar para que quede facturado el sistema que después de esto aquí nosotros esto no cubre todo el flujo porque después esto sería a cuentas por cobrar pero digamos que hasta aquí llega el proceso ahorita al día de hoy de la facturación y por qué lo usamos en gira porque son tantos temas que de repente traemos que porque necesitamos tener una visualización de cuántas facturas hay que hacer y si ya están listas para facturar correcto y las evidencias es la documentación el histórico y las evidencias de que se facturó algo no y aparte las autorizaciones porque de repente pudiera suceder que una factura se genera por un peso cuando realmente tuvo que haber sido por 90 centavos y esa diferencia no se observó porque no hubo un flujo o un proceso de autorizaciones o revisiones entonces para eso JIRA nos sirve para crear un proceso estándar de a ver antes de que antes de que envíe esa validación nacional o a validación del pdf y xml mete la proceso de facturación y en este proceso de facturación es confirma el monto, confirma si el proveedor está dado de alta, si con su constancia de situación fiscal está vigente, si su cuenta bancaria, si tiene sus datos de cuenta bancaria o etcétera. Si tienes toda la información necesaria para facturar de manera correcta, incluido el monto que tienes que facturar.
|
||||||
|
|
||||||
|
32:20 - Conference Room (Noe Rocha) - Speaker 1
|
||||||
|
Y en teoría facturado es que ya lo hiciste en la plataforma y ya incluso en facturado ya subiste el xml.
|
||||||
|
|
||||||
|
32:28 - Arturo Rosas Hernandez
|
||||||
|
A ver no, No, no, no, estaba recopilando toda la información necesaria y ya estaba metiendo a Bind toda la información, pero resultó que decían que ya habían facturado, o sea, alguien tomó el ticket, dijo que ya había facturado, pero no había evidencia, no había evidencia de que había facturado, no había número de factura, ni cliente, no había un pdf, un xml, no había nada de evidencia y en los próximos procesos de revisión de que, oye, facturamos junio, oye, facturamos mayo, oye, ¿qué pasó en enero de dos mil veinticinco? No, pues, ¿quién sabe? Ah, bueno, no hay evidencia, entonces, nos dimos cuenta que teníamos que meter un proceso obligado de que se adjuntara un PDF, no podemos asegurar que se suba un PDF justo de la factura que se generó, o sea, no puedo asegurar que es la pero sí obligar a que suban un pdf y un xml ya si alguien no sube la factura correcta pues ya es una mentira muy grande pero la podemos detectar y para eso se metió esa validación y para lo internacional el pdf no obligado subir un pdf y obviamente tiene que ser de la factura generada esto se hizo yo nada más con lo que dice Arturo, porque si tuvimos desaciertos operativos en donde decían, es que ya facturé, y dices, pues dónde está la factura, no, pues no, ah no, pues es que sí facturé, pero facturé otra cosa, dices, o sea no, necesito que factures esto, entonces internamente necesitamos esta validación para decir, oye, ok, ya lo facturaste, ok, validame que efectivamente Aquí en Gira.
|
||||||
|
|
||||||
|
34:45 - Conference Room (Noe Rocha) - Speaker 1
|
||||||
|
Lo que hiciste en Bind. Puedes subirme los archivos a Gira. Porque lo vamos a validar, que efectivamente existe. Y ahora sí, ya podemos decir que está facturado correctamente. Entonces, a tu respuesta, ya para volver. ¿Qué estatus es el que tomamos para saber si algo ya está facturado? Facturado, que diga facturado. ¿Qué es el último status? El resuelto. Porque el anterior solamente es un proceso de validación, pero no nos dice, o sea, no nos confirmas si está bien todo lo que se hizo. Correcto. ¿Correcto?
|
||||||
|
|
||||||
|
35:32 - Johann
|
||||||
|
OK, correcto. Entiendo. Muchas gracias por este diagrama. Me queda ahora sí claro el flujo que se sigue y primero toman un ticket de Jira después hacen todo el proceso online y a la par están actualizando el ticket ¿verdad? El Jira ok de acuerdo ya me quedo claro disculpa Arturo ¿te podría pedir este diagrama?
|
||||||
|
|
||||||
|
36:01 - Arturo Rosas Hernandez
|
||||||
|
sí ¿Le saca un screen o...?
|
||||||
|
|
||||||
|
36:05 - Unidentified Speaker
|
||||||
|
Sí.
|
||||||
|
|
||||||
|
36:06 - Arturo Rosas Hernandez
|
||||||
|
Nada más que sale muy pequeño. La única forma es screenshot. Sale muy pequeño.
|
||||||
|
|
||||||
|
36:15 - Johann
|
||||||
|
El click derecho, ¿no habrá algo de copiar?
|
||||||
|
|
||||||
|
36:21 - Arturo Rosas Hernandez
|
||||||
|
Uy, se ve muy chiquito. Sí.
|
||||||
|
|
||||||
|
36:25 - Unidentified Speaker
|
||||||
|
Si no...
|
||||||
|
|
||||||
|
36:26 - Unidentified Speaker
|
||||||
|
Dale ahí donde dice...
|
||||||
|
|
||||||
|
36:30 - Conference Room (Noe Rocha) - Speaker 1
|
||||||
|
Porque también es interesante los comentarios. Hoy no, no se va a alcanzar a ver. Mira, si lo compartes. Dile a Pedro que si hay forma de verlo en una pantalla más grande. Si no, aquí en la oficina lo proyectamos en una pantalla más grande y de ahí se lo mandamos. Pero la respuesta sí, se te puede mandar. Sí, sí, sí te lo mandamos.
|
||||||
|
|
||||||
|
36:50 - Arturo Rosas Hernandez
|
||||||
|
Mejor te lo mandamos, porque se va a ver muy muy mal y no te va a servir de mucho, entonces mejor enviarlo en buen tamaño para que te sirva realmente para lo que necesites.
|
||||||
|
|
||||||
|
37:04 - Johann
|
||||||
|
De acuerdo, me parece excelente.
|
||||||
|
|
||||||
|
37:07 - Arturo Rosas Hernandez
|
||||||
|
Y ya nada más como perrequisito o como pre, que seguramente será algo que como platicábamos se puede incluir de Jira, tenemos spaces en Jira, ¿no? Entonces, ¿qué son los spaces? Déjame te lo enseño. Son diferentes módulos grandes que concentran actividades, ¿no? Actividades o tickets, tipos de tickets. Entonces, ¿por qué lo menciono? Porque está el request type, digo, el space facturación, y ahí es donde se concentra todo el tema de facturación, hay otro de administración general, otro de GEDEX y otro de recursos humanos, pero es importante mencionar que el de facturación tiene un un espacio específico, único, que es donde se concentra todo el tema de facturación, entonces creo que vale la pena mencionarlo para saber que no está revuelto con el resto de tickets, o sea que hay una forma de cerrar ese universo a un solo tipo de Que se llama, perdón, de SPACE, que se llama... FACTURACIÓN.
|
||||||
|
|
||||||
|
38:25 - Unidentified Speaker
|
||||||
|
FACTURACIÓN.
|
||||||
|
|
||||||
|
38:25 - Arturo Rosas Hernandez
|
||||||
|
Y con diferentes REQUEST TYPE. Pero ahorita solamente uno que es FACTIVACIÓN ADICIONAL, RECURRENTE. Podría haber RECURRENTE ADICIONAL o NUEVA. Pero de momento el tema de los REQUEST TYPE no cambia. Eso es para la facturación.
|
||||||
|
|
||||||
|
38:41 - Conference Room (Noe Rocha) - Speaker 1
|
||||||
|
Pero creo que en la sesión anterior platicaron con Araceli. Que también habían propuesto... Que salían de Gira, ¿no?
|
||||||
|
|
||||||
|
38:49 - Arturo Rosas Hernandez
|
||||||
|
¿Propuestas? ¿O no? ¿Propuestas?
|
||||||
|
|
||||||
|
38:51 - Conference Room (Noe Rocha) - Speaker 1
|
||||||
|
No, además vieron el puro flujo de facturación en la sesión anterior. Johannes, que yo no estuve, por eso pregunto.
|
||||||
|
|
||||||
|
38:59 - Johann
|
||||||
|
Sí, en la sesión anterior vimos el tema de cobranza.
|
||||||
|
|
||||||
|
39:04 - Johann
|
||||||
|
¿Cobranza en Gira?
|
||||||
|
|
||||||
|
39:05 - Conference Room (Noe Rocha) - Speaker 1
|
||||||
|
Ah, no, en Gira no vimos cobranza.
|
||||||
|
|
||||||
|
39:08 - Johann
|
||||||
|
Cuando estuvimos viendo Gira.
|
||||||
|
|
||||||
|
39:10 - Conference Room (Noe Rocha) - Speaker 1
|
||||||
|
La pura cobranza, sí. ¿Y el tema de Gira surgió hasta cuándo?
|
||||||
|
|
||||||
|
39:16 - Johann
|
||||||
|
Fue el primer paso dentro de la facturación, que fue una sesión antes.
|
||||||
|
|
||||||
|
39:21 - Conference Room (Noe Rocha) - Speaker 1
|
||||||
|
Ah, ya. ¿Y qué? ¿Hablaba de esto precisamente? ¿De este tema de facturación?
|
||||||
|
|
||||||
|
39:26 - Unidentified Speaker
|
||||||
|
Sí.
|
||||||
|
|
||||||
|
39:27 - Johann
|
||||||
|
Arturo me mostró un ejemplo de cómo es que o dónde es que se registra un ticket. Me mostró los campos que un ticket te pide para hacer una facturación y después ya se va al taller de y entiendo que se va a este apartado de facturación, y a partir de ahí es donde le dan seguimiento a una factura.
|
||||||
|
|
||||||
|
39:52 - Conference Room (Noe Rocha) - Speaker 1
|
||||||
|
Sí, correcto. Muy bien, aquí entonces lo único es que en esta etapa de descubrimiento, Johann, también te tendría que pasar el API para que exploraras la conexión, ¿verdad, Pedro?
|
||||||
|
|
||||||
|
40:03 - Johann
|
||||||
|
Sí, entonces con ese API yo vendría a buscar la información dentro del grupo de facturación. Y esas serían las facturas con las que se están trabajando.
|
||||||
|
|
||||||
|
40:16 - Conference Room (Noe Rocha) - Speaker 1
|
||||||
|
Con las que se trabajan. En teoría, si toda facturación se ejecuta aquí como proceso inicial y se maquila en Bind. Ok. Y algunas facturas son procesos manuales y otras son procesos ya automáticos de facturación. Que eso si quieres lo platicamos con Pedro. Yo creo que a lo mejor sería bueno tener una sesión adicional con Pedro de este tema, si te parece, para que entiendas cómo son la facturación. Hay un proceso que se llama facturación recurrente y eso sí debo explicártelo por separado, nada más para entender cómo nace, porque no es que alguien se meta y lo pida, sino es un proceso que ya está programado. Y por ejemplo Acuntia, la mayoría de las facturas de Acuntia Vienen automáticamente, ¿verdad Arturo? Como recurrentes.
|
||||||
|
|
||||||
|
41:04 - Arturo Rosas Hernandez
|
||||||
|
Sí, ya tenemos un flujo recurrente como de 30 facturas. Un poco menos. Más o menos. Entonces, en ese flujo de recurrencia, como sea, hay intervención humana. Porque los temas comerciales pueden variar. Es decir, de un mes a otro puede subir o bajar la facturación. No la cantidad de facturas, porque esa más o menos se mantiene constante. Pero a lo mejor el costo de una de esas facturas, el monto, puede variar de un mes a otro.
|
||||||
|
|
||||||
|
41:37 - Conference Room (Noe Rocha) - Speaker 1
|
||||||
|
Y esa recurrencia obedece que hay temas contractuales ya establecidos por un periodo con un proveedor, con un cliente. Entonces si yo ya tengo, es como la casa. Oye, estoy en una casa, tengo que pagar el agua, pues yo sé que mensualmente me va a llegar. Entonces mensualmente nosotros proceso de facturarle al cliente ese servicio que ya tenemos por contrato. Para no tener que hacerlo manual, ya lo hicimos en un proceso que se llama recurrencia. Y son esas facturas que comenta Arturo.
|
||||||
|
|
||||||
|
42:08 - Unidentified Speaker
|
||||||
|
Correcto. Ok.
|
||||||
|
|
||||||
|
42:09 - Unidentified Speaker
|
||||||
|
Entiendo.
|
||||||
|
|
||||||
|
42:09 - Johann
|
||||||
|
Pero eso no está en Vine.
|
||||||
|
|
||||||
|
42:11 - Conference Room (Noe Rocha) - Speaker 1
|
||||||
|
Eso lo hicimos aquí para ejecutarlo en Vine. Porque Vine no tiene, hasta donde sé, no tiene esa esa bondad de poder, oye, pues este es un contrato que vale, no sé, todo inventar. 100 pesos. Entonces, de estos 100 pesos, yo lo tengo que facturar mensualmente, no sé. Bueno, son 12 pesos y tengo que facturarle un peso por mes. Entonces, para mí estaría bien padre, te lo digo para mí, pero ahorita no estamos en ese punto. Es decir, ok, si yo sé que tiene que al final pagar 12 pesos de un contrato fijo para lo que es fijo, pues por mes me tendría que estar descontando un pesito, un pesito y se me va viendo bien. Pues todavía falta por cobrar esto y este es el efectivamente cobrado. ¿Para qué? Pues para saber que no nos estemos pasando o que está ocurriendo algo diferente en la facturación de lo que contractualmente se estableció en un acuerdo comercial. Si ya después eso cambia y hay adegregados o cosas nuevas o cosas que se agregaron, pues a lo mejor verlo por separado. Porque es importante, porque yo creo que eso también nos ayudaría a futuro y no es para ahorita, no es para meterle más leña a la situación. Que si ya tenemos un contrato y un acuerdo, pues también tengo una... ¿Cómo te lo diré? Yo ya sé cuánto es un monto a recibir durante cierto tiempo, para que ya en un tema de dinero o de movimientos fiscales o movimientos internos de lo que va a llegar, oye, ¿cuál va a ser la proyección? Ah, pues por lo menos esto, porque tenemos un contrato y esto nos da una visualización de tanto Entonces, no hemos llegado a ese punto, pero te lo platico porque probablemente después algo así busquemos explorar en todo lo que estamos haciendo.
|
||||||
|
|
||||||
|
43:54 - Unidentified Speaker
|
||||||
|
La proyección.
|
||||||
|
|
||||||
|
43:55 - Conference Room (Noe Rocha) - Speaker 1
|
||||||
|
Ahorita simplemente estamos tratando de resolver lo que tenemos. Es una forma más ágil. Pero ya una vez afinando la máquina, la proyección es importante. Y lo malo es que Vine, hasta donde yo sé, no tiene eso, ¿verdad Arturo?
|
||||||
|
|
||||||
|
44:10 - Arturo Rosas Hernandez
|
||||||
|
Sí te puede generar, pero depende de que alguien lo alimenta, entonces esa parte de alimentar pues si vendría de tus comentarios.
|
||||||
|
|
||||||
|
44:18 - Conference Room (Noe Rocha) - Speaker 1
|
||||||
|
Sí, pero a ver, por ejemplo un contrato que yo tengo por tres años con un cliente que va a ser fijo en teoría por tantos recursos, no me da esa proyección hoy por hoy. Sí, hoy por hoy no. No tengo forma de saber cuál es la proyección de un flujo de efectivo a lo largo de más de un año por lo menos. Es, al día, al corte y a lo que tú haces Arturo, es decir, oye, pues tenemos estas fracturas y el flujo es tanto porque es lo que me tiene que pagar. Es lo que me tiene que pagar.
|
||||||
|
|
||||||
|
44:54 - Arturo Rosas Hernandez
|
||||||
|
No es el acuerdo comercial que tenemos a largo plazo. Exacto. El forecast o el futuro te lo da, pero sobre lo facturado, es decir, oye, y el plazo de pago, es decir, tienes una factura a 30, 60, 90 días plazo de pago, pues entonces dices, esta factura que voy a cobrar 10 pesos me la van a pagar dentro de tres meses entonces para octubre para octubre me debería de caer el dinero no en mi cuenta bancaria entonces el forecast viene de ahí derivado de ahí pero en las cuentas por cobrar un forecast o un pronóstico a largo plazo como este que menciona 9 hoy en un contrato que se gestó o se negoció a un año a 12 24 36 meses No hay forma de declararlo en Vine.
|
||||||
|
|
||||||
|
45:40 - Arturo Rosas Hernandez
|
||||||
|
Y ahorita no es para eso, Johann.
|
||||||
|
|
||||||
|
45:42 - Conference Room (Noe Rocha) - Speaker 1
|
||||||
|
Nada más te lo platico porque a futuro quisiéramos ir afinando las cosas para ahora sí tener un forecast más real. No de lo inmediato, porque tenemos un forecast a corto plazo. Aunque tenemos contratos anuales o por más periodo, hoy por hoy no sabemos, más allá de lo que ya está en Vine por cobrar, qué es lo que esperamos recibir a lo largo del tiempo. ¿Sale? OK.
|
||||||
|
|
||||||
|
46:07 - Conference Room (Noe Rocha) - Speaker 1
|
||||||
|
Muy bien.
|
||||||
|
|
||||||
|
46:08 - Unidentified Speaker
|
||||||
|
¿Qué más, Johanna?
|
||||||
|
|
||||||
|
46:09 - Conference Room (Noe Rocha) - Speaker 1
|
||||||
|
OK.
|
||||||
|
|
||||||
|
46:09 - Johann
|
||||||
|
Por esa parte, ya me queda muy claro que esas eran las preguntas con las que venía el día de hoy. Entiendo que mañana hay una sesión para revisar y validar el prototipo. Sí, ya el prototipo con RSL. Excelente. Entonces, por el día de hoy, esas 3 preguntas acerca de los clientes, del fee. Esta parte de Gira ya me respondió muy bien. Muchas gracias. Y ya me quedó claro.
|
||||||
|
|
||||||
|
46:41 - Conference Room (Noe Rocha) - Speaker 1
|
||||||
|
Y con esto puedo seguir trabajando. JUAN MANUEL LUCERO. Muy bien, Johann. Aquí nomás estoy poniendo un penetre, mandar el flujo desde una visualización más amplia para que lo veas a detalle. Y sesión con Arturo, con Pedro, para explorar lo que es el API. Conexión en Jira. Y ya sobre eso, este, siguientes pasos para platicar.
|
||||||
|
|
||||||
|
47:08 - Johann
|
||||||
|
De acuerdo, ahora mismo estoy utilizando el token que me pasaron de Bind únicamente como lectura. Y desde Jira valdría la pena revisar esta parte de escritura para el ticket de Jira, pasarlo a cotización en Bind y hacer una prueba.
|
||||||
|
|
||||||
|
47:31 - Conference Room (Noe Rocha) - Speaker 1
|
||||||
|
Algo más controlado. Sí. Allí te pediría mucho cuidado Johann cuando lleguemos a esa instancia. Porque no tenemos ambiente de prueba. Es producción. Entonces, nada más ser muy cuidadosos de lo que vamos a hacer. Te lo pediría mucho. O sea, muy, muy medida la prueba de lo que vamos a hacer a escritura. Para no hacernos jaraquiri nosotros.
|
||||||
|
|
||||||
|
47:54 - Unidentified Speaker
|
||||||
|
¿Sale?
|
||||||
|
|
||||||
|
47:54 - Unidentified Speaker
|
||||||
|
Sale.
|
||||||
|
|
||||||
|
47:55 - Conference Room (Noe Rocha) - Speaker 1
|
||||||
|
Bueno, muy bien.
|
||||||
|
|
||||||
|
47:56 - Conference Room (Noe Rocha) - Speaker 1
|
||||||
|
Ya llegando el momento. Lo acordamos muy bien. No se nos puede salir de las manos de la plataforma. Porque en serio, se nos hace una fiesta. Y nos ha costado mucho tener el RP como lo tenemos. Entonces, listo. Sería todo. Gracias por tu tiempo, Johann. Muchas gracias a ustedes.
|
||||||
|
|
||||||
|
48:14 - Johann
|
||||||
|
Que tengan un buen día. Hasta luego. Gracias, bye.
|
||||||
|
|
||||||
|
48:17 - Unidentified Speaker
|
||||||
|
Bye.
|
||||||
@@ -0,0 +1,173 @@
|
|||||||
|
API de conexión con JIRA
|
||||||
|
Mon, Jul 27, 2026
|
||||||
|
|
||||||
|
0:03 - Noe Rocha
|
||||||
|
Hola Erika, buenos días. ¿Cómo estás? Bien, gracias. Todo muy bien, también. ¿Te confirmó Pedro? Sí. Vamos a esperar. Hola Johann, buenos días. Hola, ¿qué tal? Buenos días.
|
||||||
|
|
||||||
|
0:21 - Unidentified Speaker
|
||||||
|
Hola Erika, hola Pedro.
|
||||||
|
|
||||||
|
0:23 - Unidentified Speaker
|
||||||
|
Buenos días. Hola, buenos días.
|
||||||
|
|
||||||
|
0:27 - Pedro Alberto Ayala Elizondo
|
||||||
|
¿Qué tal? Buenos días.
|
||||||
|
|
||||||
|
0:31 - Noe Rocha
|
||||||
|
Bueno, pues ya estamos todos. Espero que hayan tenido un buen fin de semana. Caluroso, pero buen fin de semana. Bueno, a ver, como propósito del tema, dentro del descubrimiento que está haciendo Johann, Pedro, se ha visto que muchas partes del proceso, que en un principio no se habían especificado, están llegando a través de JIRA. Específicamente para temas de facturación. Como están haciendo de Jira, originalmente no habíamos contemplado la conectividad, pero ahorita ya vemos que va a ser necesario. Entonces, aquí lo que queremos ver contigo y que le expliques a Johann, bueno, más allá de cómo funciona Jira, sino cómo se conecta a través de un API Jira actualmente para extraer información y darle un poquito de vista de... No sé si quieras ver también el proceso ¿O nos vamos directamente a cómo funciona? ¿Cómo estamos haciendo hoy la conectividad a través del API? Sí, con la conectividad a través del API me es suficiente.
|
||||||
|
|
||||||
|
1:38 - Johann
|
||||||
|
Vale, ya está.
|
||||||
|
|
||||||
|
1:39 - Noe Rocha
|
||||||
|
Te dejo el micrófono, Pedro.
|
||||||
|
|
||||||
|
1:41 - Noe Rocha
|
||||||
|
Adelante, por favor. Muy bien.
|
||||||
|
|
||||||
|
1:43 - Pedro Alberto Ayala Elizondo
|
||||||
|
Bueno, con el API de Jira, hoy actualmente se está utilizando... Bueno, yo lo utilizo para varios tableros de servicio de TI. Entonces, yo lo utilizo para varios tableros de TI. Entonces, algunos los tengo para limpieza de usuarios, otros los hago para aprobadores, otros simplemente para conectores. Pero sus principales funciones, más que nada, es para crear y consultar actividades, tickets, el tema de aprobaciones, aprobaciones pendientes, el tema de documentos adjuntos. Sí tiene una muy amplia variedad en temas de extracción. En el que sí tiene mucho de dónde agarrar. Entonces, pues, no sé si tienes alguna pregunta en específico de la API, cómo se gestiona o cómo funciona. O si en verdad no sé si necesitas la llave como tal para generar una con alguna fecha de expiración en específica.
|
||||||
|
|
||||||
|
2:46 - Johann
|
||||||
|
Sí, una API key y también entender por ejemplo ahora que me mencionas que si tiene la posibilidad de extraer por ejemplo los ítems de un tablero y consultar el estatus y cambiar el estatus de esa tarea es es muy útil sí sí todo todo es es accionable es tanto de lectura como de edición entonces vas a poder hacer cualquier tipo de creación que se podría hacer manual pero se puede hacer de manera entonces te paso igual quieres alguna por reporte xt te funciona?
|
||||||
|
|
||||||
|
3:25 - Noe Rocha
|
||||||
|
disculpa con reporte txt?
|
||||||
|
|
||||||
|
3:28 - Pedro Alberto Ayala Elizondo
|
||||||
|
si osea con un documento txt con el api si perfecto dejeme entonces lo genero vamos a llamarle De momento vamos a llamarle... Le estamos llamando Plataforma de Automatización Financiera. Vamos a llamarlo PAF, el token, para que no haya... Vamos a hacerlo de un año de expiración. Hasta el... 27 de julio del 2027. Y te la comparto, ya la tengo, si quieres ahorita te la por correo. Excelente.
|
||||||
|
|
||||||
|
4:51 - Johann
|
||||||
|
No sé si quieren ver algo más, algo que tengan dudas del API en general.
|
||||||
|
|
||||||
|
4:57 - Pedro Alberto Ayala Elizondo
|
||||||
|
Yo creo que van a salir las dudas ya una vez estando en acción con la herramienta, empezar a extraer los datos, empezar a jugar con las consultas. Yo tengo una duda.
|
||||||
|
|
||||||
|
5:12 - Noe Rocha
|
||||||
|
El consumo de APIs es algo parecido a Vine, que te limita en cuanto a extracción?
|
||||||
|
|
||||||
|
5:19 - Pedro Alberto Ayala Elizondo
|
||||||
|
Déjeme ver, porque creo que sí tiene el consumo al lado, pero estaba viendo que no tiene como una página como la de Bind, que es Bind de desarrolladores. Entonces creo que sí tiene una que es Atlassian.
|
||||||
|
|
||||||
|
5:40 - Noe Rocha
|
||||||
|
Si quieres, nomás investiga y luego lo vemos con Johann. En este momento. Porque si voy a tener un límite de transacción por escritura o por consulta, entonces tendremos que ver cómo funciona esto sobre la operación.
|
||||||
|
|
||||||
|
5:58 - Pedro Alberto Ayala Elizondo
|
||||||
|
Sí, creo que no existe uno por día. O sea, por día no hay, por así decirlo.
|
||||||
|
|
||||||
|
6:05 - Noe Rocha
|
||||||
|
Y las APIs las generas desde tu cuenta o... Desde mi cuenta. Las puedes diferenciar, ¿verdad? Con nombre. Con el nombre.
|
||||||
|
|
||||||
|
6:15 - Pedro Alberto Ayala Elizondo
|
||||||
|
Sí, justo así las tengo diferenciadas. De hecho, una práctica sí es ir renovando constantemente, bueno, no constantemente, pero ir renovando en cierto punto los tokens para evitar ambigüedades. Entonces, todavía están ahí todas en la base de datos y van a seguir apareciendo.
|
||||||
|
|
||||||
|
6:34 - Unidentified Speaker
|
||||||
|
Ok.
|
||||||
|
|
||||||
|
6:34 - Noe Rocha
|
||||||
|
¿Qué otra consideración importante es ahí con el uso de la API? De Argentina, particularmente.
|
||||||
|
|
||||||
|
6:43 - Pedro Alberto Ayala Elizondo
|
||||||
|
Pues, como tal, es algo muy intuitivo. Es como cualquier extracción de datos mediante un API. Simplemente haciendo los comandos correctos mediante SQL o algún lenguaje, se pueden extraer los datos de manera clara. Yo, más que nada, lo utilizo para temas de SLA. Yo tengo unos SLA configurados en cada uno de los procesos que tenemos dentro de Jira. Entonces, tengo la configuración para que este, para que este API me esté arrastrando este tipo de información. Y también el tema de la facturación. ¿Con quién está la prover? ¿En qué estatus está? También han habido temas para cambiar estatus que se han podido lograr con el API.
|
||||||
|
|
||||||
|
7:29 - Unidentified Speaker
|
||||||
|
Ok.
|
||||||
|
|
||||||
|
7:29 - Noe Rocha
|
||||||
|
Muy bien. Se fue realmente bastante rápido. Entonces, ¿el acuerdo se lo mandas en un TXT? Sí, ya lo tengo.
|
||||||
|
|
||||||
|
7:37 - Pedro Alberto Ayala Elizondo
|
||||||
|
Y no sé si así como...
|
||||||
|
|
||||||
|
7:40 - Noe Rocha
|
||||||
|
hay una página de desarrolladores de Jira. Simplemente para temas de consulta que tenga que tener Johann sobre el uso.
|
||||||
|
|
||||||
|
7:51 - Unidentified Speaker
|
||||||
|
Ok.
|
||||||
|
|
||||||
|
7:52 - Pedro Alberto Ayala Elizondo
|
||||||
|
Sí, eso todo te lo incluyo ahorita en el correo como informativo. Va, de acuerdo.
|
||||||
|
|
||||||
|
8:00 - Unidentified Speaker
|
||||||
|
Muy bien.
|
||||||
|
|
||||||
|
8:01 - Noe Rocha
|
||||||
|
Johann, ya con esta información, ¿cuál sería su idea?
|
||||||
|
|
||||||
|
8:06 - Johann
|
||||||
|
¿Cuáles serían los siguientes pasos? Los siguientes pasos, de mi parte, yo revisaría la documentación de Jira. También, si me pudieran compartir pronto el nombre exacto del tablero en donde vamos a consultar los tickets, también sería útil para saber a dónde o de dónde extraer esos tickets. Entonces, yo investigo la documentación y con la piqui que me comparte Pedro, ya puedo integrarlo al prototipo que estamos haciendo.
|
||||||
|
|
||||||
|
8:35 - Noe Rocha
|
||||||
|
Pedro. Pero no sé si está identificable a través de la API. O con un nombre. Supongo que con el nombre del tablero.
|
||||||
|
|
||||||
|
8:46 - Pedro Alberto Ayala Elizondo
|
||||||
|
Pero el tablero se refiere al de Power BI.
|
||||||
|
|
||||||
|
8:50 - Noe Rocha
|
||||||
|
No, no. Es que el tablero...
|
||||||
|
|
||||||
|
8:53 - Pedro Alberto Ayala Elizondo
|
||||||
|
Corrígeme, Johann.
|
||||||
|
|
||||||
|
8:54 - Noe Rocha
|
||||||
|
Si te refieres más bien a la... Al Space? Sí, al Space. Así es.
|
||||||
|
|
||||||
|
9:01 - Pedro Alberto Ayala Elizondo
|
||||||
|
Ah, ok, ok. ¿Dónde están almacenados todos los tickets? De facturación. Sí, en la bandeja de facturación.
|
||||||
|
|
||||||
|
9:10 - Noe Rocha
|
||||||
|
Sí, porque hay varias bandejas. Entonces, para que no tengas que buscar en todas. Ser muy específico de a cuál nos referimos.
|
||||||
|
|
||||||
|
9:20 - Pedro Alberto Ayala Elizondo
|
||||||
|
Esas son las bandejas que hay por hoy. Administración General, ITS, Medilo, Recursos Humanos, Facturación. Y por hoy, donde se encuentra lo bueno es aquí.
|
||||||
|
|
||||||
|
9:31 - Noe Rocha
|
||||||
|
Ya. Y si, por ejemplo, cuando tú has hecho la revisión o has usado el API, ¿cómo identificas cuál bandeja es? Por la llave.
|
||||||
|
|
||||||
|
9:44 - Pedro Alberto Ayala Elizondo
|
||||||
|
Por la llave. Esta tiene llave AG, llave DITCM, este es RH, este es FAC y este es HD.
|
||||||
|
|
||||||
|
9:54 - Noe Rocha
|
||||||
|
Ok, entonces la llave para el tablero de facturación debería ser FC. Sí, FAC.
|
||||||
|
|
||||||
|
10:00 - Unidentified Speaker
|
||||||
|
FAC.
|
||||||
|
|
||||||
|
10:01 - Pedro Alberto Ayala Elizondo
|
||||||
|
FAC guión y el numerito. Y esos son todos los correspondientes al... Sí, lo bueno que la API de Jira es global, entonces vas a poder extraer información de todas las banderas, pero la bandeja que nos importa es facturación, pero es que hay ciertas cosas que pasan en recursos humanos que al momento de cerrarse en recursos humanos genera cosas en facturación, también en administración general. Haz de cuenta que facturación es como el, la bandeja padre y se va, es la que se alimenta de varios, de varios que vienen siendo las demás.
|
||||||
|
|
||||||
|
10:41 - Unidentified Speaker
|
||||||
|
Por procesos diferentes.
|
||||||
|
|
||||||
|
10:43 - Pedro Alberto Ayala Elizondo
|
||||||
|
Ajá.
|
||||||
|
|
||||||
|
10:44 - Noe Rocha
|
||||||
|
Que todas vienen cayendo en facturación. Muy bien.
|
||||||
|
|
||||||
|
10:49 - Pedro Alberto Ayala Elizondo
|
||||||
|
Bien, pero, eh, reviso.
|
||||||
|
|
||||||
|
10:51 - Noe Rocha
|
||||||
|
¿Qué más, Johann? Creo que con esto es suficiente.
|
||||||
|
|
||||||
|
10:57 - Johann
|
||||||
|
Igual, cuando comience a ser ya las pruebas, les comparto mis dudas, o si me hablan Hasta entonces voy a estar haciendo un script de consulta. Y si pudieran apoyarme, me gustaría también hacer un script de creación, eliminación y edición de un registro de prueba. Para ver que todo esté correcto y ya de ahí implementarlo.
|
||||||
|
|
||||||
|
11:29 - Noe Rocha
|
||||||
|
O sea ya, digamos, probar una creación de un documento, dices tú?
|
||||||
|
|
||||||
|
11:35 - Unidentified Speaker
|
||||||
|
Sí.
|
||||||
|
|
||||||
|
11:36 - Noe Rocha
|
||||||
|
Si te parece, ya cuando sea el momento de hacer la creación. Hacemos una prueba en vivo. Y veremos cómo funciona.
|
||||||
|
|
||||||
|
11:48 - Johann
|
||||||
|
Sí, sin problema. O también si tienen a la mano un script. Donde ya tengan un CRUD.
|
||||||
|
|
||||||
|
11:57 - Noe Rocha
|
||||||
|
Fíjate que no tenemos algo así, ¿verdad Pedro? A través de API no. No hemos tenido la necesidad de hacerlo. Bueno, si es el checkpoint, entonces tú nos dices, Johann, para hacer para las dudas o para alguna prueba en específico. ¿Te parece? Sí, me parece bien. Vale, pues. Bueno, pues entonces es todo. Gracias por su desmañanada.
|
||||||
|
|
||||||
|
12:29 - Pedro Alberto Ayala Elizondo
|
||||||
|
Buen inicio de semana. Igualmente. Igualmente. Bye. Muchas gracias.
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
# Correo — Pedro Ayala → Johann · medición de consumo de la API de Jira y entrega del token
|
||||||
|
|
||||||
|
**Canal:** Correo
|
||||||
|
**Fecha:** 2026-07-27, 07:40 am
|
||||||
|
**De:** Pedro Alberto Ayala Elizondo `<pedro.ayala@balamtalentoestrategico.com>`
|
||||||
|
**Para:** Johann · **CC:** Noe Rocha, Erika Chávez
|
||||||
|
**Asunto:** Seguimiento: medición de consumo API de Jira y accesos
|
||||||
|
**Relación:** cumple los compromisos de Pedro en la sesión de API de Jira del mismo día ([REGISTRO #56](../bitacora/REGISTRO.md)): entregar el token e investigar los límites de consumo.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Transcripción del correo
|
||||||
|
|
||||||
|
> Buenos días, Johan. Espero te encuentres bien.
|
||||||
|
>
|
||||||
|
> Gracias por la sesión y por tu tiempo. Te comparto el detalle de cómo se mide el consumo de la API de Jira, porque tiene un par de puntos que hay que tener claros.
|
||||||
|
>
|
||||||
|
> **Cómo funciona el límite:**
|
||||||
|
>
|
||||||
|
> No existe un límite de volumen del tipo "X llamadas por mes". El uso de la API tampoco genera costo adicional sobre la licencia. Lo que sí existen son los **límites de velocidad**, y actualmente son **tres independientes que operan en paralelo**:
|
||||||
|
>
|
||||||
|
> 1. **Cuota por puntos (por hora).** Cada llamada consume puntos según el trabajo que implica: 1 punto base más 1 punto por objeto de dominio (issues, proyectos) o 2 puntos por objeto de identidad (usuarios, grupos, roles). **Las escrituras solo cobran el punto base.** La bolsa por defecto es de **65,000 puntos por hora**.
|
||||||
|
> 2. **Burst por segundo.** Aplica a todo el tráfico, incluido el de API token. Los defaults son **100 request por segundo para GET y POST, y 50 para PUT y DELETE**, con un bucket independiente por endpoint y por tenant. Hay endpoints con límites propios más bajos; el más notable es el de consulta de clientes de un service desk, **restringido a 5 por segundo**.
|
||||||
|
> 3. **Límite por issue en escrituras.** **20 operaciones de escritura cada 2 segundos y 100 cada 30 segundos** sobre un mismo ticket.
|
||||||
|
>
|
||||||
|
> Cualquiera de los tres devuelve **HTTP 429**. El header **`RateLimit-Reason`** indica cuál se activó.
|
||||||
|
>
|
||||||
|
> **Sobre la página de desarrolladores para revisar consumo:** aquí la respuesta es que **no existe y es una limitante de Jira**.
|
||||||
|
>
|
||||||
|
> - No hay dashboard de consumo en la administración de Jira. No hay pantalla de administrador ni reporte nativo.
|
||||||
|
> - El Developer Console de Atlassian solo sirve si uno publica una app propia, y ahí únicamente se ve el tier asignado, no el consumo de la instancia.
|
||||||
|
> - El detalle de uso por API token requiere **Atlassian Guard Premium**, que es una licencia aparte.
|
||||||
|
> - Existen apps de terceros en Marketplace que estiman el consumo, pero trabajan por muestreo, no con telemetría real de Atlassian.
|
||||||
|
>
|
||||||
|
> La **única fuente confiable son los headers de respuesta**: `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-NearLimit` (que se activa cuando queda menos del 20% de capacidad), y en respuestas 429 también `X-RateLimit-Reset`, `Retry-After` y `RateLimit-Reason`.
|
||||||
|
>
|
||||||
|
> Te comparto el API token en el siguiente link de Google Drive:
|
||||||
|
>
|
||||||
|
> `https://drive.google.com/drive/folders/1J4GZcV7gndpXJ6qlXtcpK6ZiNZbvkM_0?usp=sharing`
|
||||||
|
>
|
||||||
|
> Si se presenta algún problema con el acceso, estoy al pendiente.
|
||||||
|
>
|
||||||
|
> Saludos,
|
||||||
|
|
||||||
|
## Respuesta de Johann
|
||||||
|
|
||||||
|
> Enterado, muchas gracias Pedro! Saludos
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Notas e implicaciones para el desarrollo
|
||||||
|
|
||||||
|
- **Cierra el pendiente de la sesión #56:** ya no hay incógnita sobre el límite. **No hay tope mensual** ni costo por uso; el diseño del sync debe respetar **límites de velocidad**, no un cupo diario como en BIND (20K/día).
|
||||||
|
- **El sync debe autorregularse leyendo los headers** `X-RateLimit-*`: pausar/reducir ritmo cuando `X-RateLimit-NearLimit` aparezca (<20% de capacidad) y respetar `Retry-After` ante un 429. No hay dashboard, así que la telemetría vive en las respuestas.
|
||||||
|
- **Las escrituras son baratas en puntos** (solo el punto base): favorece la capa de escritura (crear cotizaciones/tickets) frente a las consultas de identidad (2 puntos c/u).
|
||||||
|
- **Cuidado con el límite por-issue en escrituras** (20/2 s, 100/30 s por ticket): relevante si la automatización hiciera varias escrituras sobre el mismo ticket en ráfaga.
|
||||||
|
- ⚠️ **El token vive en el enlace de Google Drive** de arriba — tratar como credencial: descargarlo, moverlo a user-secrets / Key Vault y **no** dejarlo en texto plano en el repo ni en el disco. (El conector de Google Drive de claude.ai requiere autorización; el token no se consultó desde aquí.)
|
||||||
@@ -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.
|
||||||
@@ -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,69 @@
|
|||||||
|
# Corte de avance — Etapa 1
|
||||||
|
|
||||||
|
**Fecha:** 27-jul-2026 (corte de los lunes, formato pedido por Erika: entregable + actividad + horas por etapa)
|
||||||
|
**Etapa:** Plataforma base y sincronización con BIND
|
||||||
|
**Rango estimado contractual:** 32–39 h · **Horas registradas de Etapa 1:** 19.75 h (total del proyecto: 41.58 h)
|
||||||
|
**Adjunto:** `Plan-actividades-avance-2026-07-27.xlsx`
|
||||||
|
|
||||||
|
> Documento interno de Johann. El mensaje para Erika va abajo; el resto es sustento propio (no se comparte).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Mensaje breve para Erika (WhatsApp)
|
||||||
|
|
||||||
|
> Hola Erika, buen día. Te comparto el avance de la Etapa 1 al corte de hoy (Excel adjunto, con entregable · actividad · horas por etapa como lo pediste).
|
||||||
|
>
|
||||||
|
> El **núcleo de la plataforma ya está construido y probado contra producción**: autenticación con roles y bitácora de auditoría, arquitectura multi-tenant con aislamiento por RLS, el cliente de la API de BIND, la sincronización de clientes, facturas y cotizaciones, el modelo central de facturas con sus estados y la cartera separada por moneda. La sincronización del histórico ya corrió y cuadró: **1,494 facturas** con sus totales de cartera coincidiendo con BIND.
|
||||||
|
>
|
||||||
|
> Lo que sigue pendiente es, sobre todo, la **capa de escritura (cotización→factura), que dejé en pausa a propósito hasta que definan el flujo de facturación en la sesión del martes**, ya que de ahí dependen las reglas. También quedan por cerrar el afinado de datos de prueba y la parte de Azure/CI-CD, que depende de los accesos de su lado.
|
||||||
|
>
|
||||||
|
> En horas voy en **19.75 h de Etapa 1** (rango estimado 32–39 h), con **6 de los 8 entregables de la etapa ya terminados**. Con la key de Jira que nos pasó Pedro hoy y las definiciones del martes, arranco la integración con Jira y la capa de escritura. Cualquier duda quedo al pendiente. 🙌
|
||||||
|
|
||||||
|
*(Nota: el ajuste de la propuesta y el conteo de horas formal van por separado; este mensaje es solo el avance de Etapa 1.)*
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Sustento interno
|
||||||
|
|
||||||
|
### Terminado y verificado contra producción
|
||||||
|
- **Backend base:** autenticación con JWT propio, matriz de roles (Finanzas, Dirección, Operaciones, Administración) con policies, y **bitácora de auditoría universal** (logins, sync, transiciones, escritura). — *entregable contractual 1* ✅
|
||||||
|
- **Multi-tenant + RLS real de Postgres** (no solo filtro EF): políticas `tenant_isolation` fail-closed en las 8 tablas de negocio + interceptor de conexión. — *entregable 2* ✅ *(verificación de aislamiento local pendiente de rol de app no-superusuario; las políticas ya están correctas — ver REGISTRO #53)*
|
||||||
|
- **Cliente productivo de BIND** en C#: paginación OData, reintentos, control de cuota y errores; solo lectura por defecto. — *entregable 3* ✅
|
||||||
|
- **Sync de clientes** (full-scan barato + hash + detalle solo de nuevos/cambiados, normalización RFC/nombre, dedup por RFC exacto). — *entregable 5* ✅
|
||||||
|
- **Sync de facturas y cotizaciones** con estados normalizados, tipo de cambio y detección de pagos (fecha de detección, ADR-004). — *entregable 6* ✅
|
||||||
|
- **Modelo central de facturas con estados** (Emitida, Vigente, Vencida, Pagada, Cancelada) como función pura con pruebas. — *entregable 7* ✅
|
||||||
|
- **Cartera por moneda** (jamás suma monedas): `GET /api/cartera` verificado BD↔endpoint — MXN 17 abiertas $1,568,772.32 · USD 92 abiertas $320,379.06.
|
||||||
|
- **Sync histórico corrido y verificado** (jamás en vivo): 1,494 facturas + 43 cotizaciones, detalle 1,494/1,494, idempotencia comprobada, 0 pagos falsos. (REGISTRO #52)
|
||||||
|
- 26/26 pruebas unitarias verdes.
|
||||||
|
|
||||||
|
### En construcción / en pausa
|
||||||
|
- **Capa de escritura controlada (cotización→factura)** — *entregable 4*. **Al 40 %: diseño y modelo listos, implementación en pausa.** Ya hecho: entidad `WriteOperation` con el pipeline `Draft→DryRun→Confirmed` migrada, policy `PuedeOperarEscritura`, ADR-001, el diseño del `BindWriteClient` con flags apagados por default y la regla PPD/PUE (informada por el hallazgo de que 1,374/1,374 facturas del histórico son PUE). Falta el `BindWriteClient`, el pipeline implementado, los shapes reales de los POST y las pruebas. 🔴 **Pausa deliberada** hasta la definición del flujo de facturación (sesión martes 28-jul con Arturo/Araceli): de ahí salen las reglas de prefactura, PPD/PUE, alta de cliente y qué estatus de Jira dispara qué.
|
||||||
|
- **Scheduler + backfill** (sync programado cada 15 min) — el sync ya funciona en manual; falta automatizarlo.
|
||||||
|
- **Datos de prueba (seeder) + runbook** — *entregable 8*, **al 85 %**: modelo central de facturas con el `InvoiceStatusMapper` (13 pruebas) ✅, migraciones ✅; falta el seeder de datos ficticios y el runbook.
|
||||||
|
- **Suite de pruebas de flujos críticos** (integración) — pendiente.
|
||||||
|
|
||||||
|
### Dependencias externas (lado Balam)
|
||||||
|
- **Definición del flujo de facturación/prefacturación** — sesión martes 28-jul (Erika coordina). Desbloquea la capa de escritura.
|
||||||
|
- **Correo del flujo de facturación de Jira** (Erika lo envió 22-jul) — a incorporar.
|
||||||
|
- **Alcance Jira → cotización BIND al 90 %:** conectividad resuelta el 27-jul (token PAF con vigencia a 1 año, API confirmada de lectura **y** escritura, bandeja **Facturación (FAC)** identificada, límites de consumo aclarados por correo de Pedro: sin tope mensual, 3 límites de velocidad, autorregulación por headers `X-RateLimit-*`). Falta el 10 %: **qué estatus de Jira dispara qué** (amarrado a la definición del 28-jul) y validar si basta escuchar FAC o hay que rastrear los procesos que nacen en RH/Administración General y caen ahí.
|
||||||
|
- **Acceso a Azure + CI/CD** (Pedro/Noé) — condiciona el despliegue.
|
||||||
|
|
||||||
|
## Criterio de medición
|
||||||
|
- **Entregables contractuales:** **6 de 8 terminados** (1, 2, 3, 5, 6, 7). Los dos restantes van parciales y su avance se muestra en su propia fila del Excel, no promediado en el encabezado: el **8 al 85 %** (migraciones sí, seeder no) y el **4 al 40 %** (escritura: diseño y modelo listos, implementación en pausa por definición de Balam). → **El Excel reporta 75 % de Etapa 1**, contando solo entregables cerrados, que es lo verificable.
|
||||||
|
- **Horas de Etapa 1:** 19.75 h de un rango de 32–39 h (**~51–62 % del esfuerzo estimado**).
|
||||||
|
- Se reporta por **entregable terminado**, no por promedio ponderado de actividades. El corte anterior mostraba 82 % ponderado, que era optimista e indefendible: no distinguía entre trabajo hecho y trabajo bloqueado. "6 de 8" dice exactamente qué falta y de quién depende.
|
||||||
|
- La brecha entre 75 % de entregables y ~55 % de horas **no es un error**: refleja que las sesiones de desarrollo asistidas por IA produjeron el núcleo en mucho menos tiempo de reloj que la estimación manual (ver la nota en `Seguimiento-horas.md`).
|
||||||
|
|
||||||
|
## Horas: estado de la validación (resuelto el 27-jul)
|
||||||
|
- **41.58 h en total** — Etapa 0: 21.83 h · Etapa 1: 19.75 h.
|
||||||
|
- **Cero horas pendientes de validar.** 9.13 h con duración documentada (`confirmada`) y 32.45 h reconstruidas y confirmadas por Johann contra evidencia fechada (`validada`).
|
||||||
|
- Correcciones aplicadas en este corte:
|
||||||
|
- Se corrigió un error de formato del CSV que ocultaba **1.00 h** del 19-jul (entorno local).
|
||||||
|
- Se re-etiquetó el 10-jul: la mayor parte eran 6 commits de pulido del prototipo (E0-05), no el entregable de cierre (E0-06). Mismo total, etiquetas correctas.
|
||||||
|
- Se incorporaron **3.50 h** de trabajo real que no estaba cobrado: diseño del plan de Etapa 1 (2.00 h, `Plan-Etapa1.md`) y preparación de las sesiones del 22 y 24-jul (1.50 h, agenda y guion de validación).
|
||||||
|
- **Ajustes a la baja** en las horas reconstruidas de Etapa 0: análisis del Discovery de facturación 1.92 → 1.00 h, documentación del de cobranza 1.50 → 0.75 h, guion de validación 0.75 → 0.45 h. Las duraciones de sesión documentadas en transcript no se tocaron.
|
||||||
|
- Cada celda de horas del Excel se genera desde el CSV y el script **falla** si el detalle no cuadra con el total.
|
||||||
|
- **Etapa 0 cerró en 21.83 h, dentro del rango contractual de 18–22 h.**
|
||||||
|
|
||||||
|
## Punto abierto (no bloquea el envío)
|
||||||
|
- **Tono del mensaje:** el borrador enmarca la capa de escritura como "en pausa por la definición del 28-jul" — pone la pelota de su lado, y es verdad. Si prefieres suavizarlo, se ajusta.
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
# Borradores de seguimiento técnico
|
||||||
|
|
||||||
|
Mensajes preparados; no enviados. Actualizar si los temas se resuelven en la sesión del 22-jul.
|
||||||
|
|
||||||
|
## 1. Correo a Noé y Pedro — salvaguardas y dudas de BIND
|
||||||
|
|
||||||
|
**Asunto:** Balam · Salvaguardas activas y dudas técnicas de la API de BIND
|
||||||
|
|
||||||
|
Hola Noé, Pedro:
|
||||||
|
|
||||||
|
Confirmo que el acceso de BIND se ha utilizado exclusivamente para operaciones de lectura. El cliente bloquea cualquier operación de escritura, controla el número de solicitudes y mantiene el token fuera del repositorio. La validación se realizó sin persistir datos reales y los reportes conservan únicamente estructura y resultados técnicos.
|
||||||
|
|
||||||
|
Para cualquier prueba futura de escritura mantendremos los candados definidos en la propuesta: autorización previa, dry-run, confirmación humana y feature flag desactivado por defecto. Si determinan crear un usuario específico de solo lectura, el cambio de token es transparente porque se carga mediante configuración segura.
|
||||||
|
|
||||||
|
La validación confirmó que el MVP de consulta, aging y descarga de PDF es viable. Quedaron estas dudas para revisar con ustedes:
|
||||||
|
|
||||||
|
1. ¿Existe algún endpoint de pagos individuales o complementos de pago (REP)?
|
||||||
|
2. ¿Tienen la tabla que relaciona el código interno de `CFDIUse` con la clave SAT?
|
||||||
|
3. ¿Qué formato devuelve el endpoint `/{id}/xml`?
|
||||||
|
4. ¿BIND ofrece webhooks o eventos, o debemos operar únicamente con polling?
|
||||||
|
5. ¿Qué token alimenta actualmente el Power BI? Arturo mencionó el suyo y queremos evitar afectar esa integración si se reemplaza el token.
|
||||||
|
|
||||||
|
Quedo atento. Muchas gracias.
|
||||||
|
|
||||||
|
Saludos,
|
||||||
|
Johann Velázquez
|
||||||
|
|
||||||
|
## 2. WhatsApp a Erika/Pedro — flujo de horas en Jira
|
||||||
|
|
||||||
|
> Hola Erika, Pedro. Para mantener al día el corte de horas, ya inicié un control local por actividad. ¿Me ayudan a confirmar cómo debo registrar las horas en Jira y si necesito que me agreguen a algún proyecto o tablero específico? En cuanto tenga acceso concilio lo que ya llevo y continúo reportando ahí.
|
||||||
|
|
||||||
|
## 3. Confirmación al recibir el repositorio
|
||||||
|
|
||||||
|
> Listo, ya recibí la invitación y confirmé que tengo acceso de escritura al repositorio. Muchas gracias. Voy a publicar la estructura inicial del backend y les aviso cuando quede disponible.
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
# Correo — Respuesta a Noé/Arturo: ticket de ejemplo "como debería ser" + fecha de la prueba acompañada
|
||||||
|
|
||||||
|
Enviado · 10-ago-2026
|
||||||
|
|
||||||
|
Responder a todos sobre el hilo `RV: Seguimiento: medición de consumo API de Jira y accesos`
|
||||||
|
**Para:** Noé Rocha · **CC:** Arturo Rosas; Araceli Sánchez; Pedro Ayala; Erika Chávez
|
||||||
|
**Asunto:** (el del hilo, con `RE:`)
|
||||||
|
|
||||||
|
> ⚠️ **Agregar a Arturo y a Erika al CC:** Noé mandó su correo del 7-ago solo con Araceli y Pedro en copia, pero Arturo es quien captura y define. Erika coordina agendas.
|
||||||
|
|
||||||
|
**Contexto:** Noé reenvió el 7-ago las respuestas de Arturo del 4-ago, más la definición de que las pruebas serán acompañadas. Las cuatro preguntas del 30-jul quedan cerradas.
|
||||||
|
|
||||||
|
**Estrategia — versión mínima.** Solo cinco cosas: agradecer, pedir el ticket de ejemplo, proponer fecha, decir dónde termina el ejercicio y recordar Azure. Todo lo demás se resuelve en la sesión o con el molde. El ticket capturado "como debería ser" hace el trabajo que antes hacían tres párrafos de preguntas: si los conceptos no caben en ningún campo, se ve en el ejemplo.
|
||||||
|
|
||||||
|
**Lo que se sacó del correo y ahora hay que llevar A LA SESIÓN** (ninguno se pierde, pero ya no tienen registro escrito previo):
|
||||||
|
|
||||||
|
1. 🔴 **ACUNTIA** — que el molde no cubre si Arturo captura un caso normal. `FAC-100` trae un solo monto de 7,594.00 USD para el ticket que históricamente son 40–48 facturas. **Es el riesgo más grande de alcance y ya no está pidiéndose por escrito** → preguntarlo en la sesión, o mandar un correo aparte si quieres constancia.
|
||||||
|
2. ⚠️ **El supuesto de los tickets de `Automation for Jira`** (10 de 69, sin campos, `FAC-91` activo). Ya no queda asentado por escrito que siguen manuales, así que la carga de objetar no está de su lado. Plantearlo en la sesión.
|
||||||
|
3. **Conceptos/partidas** — se resuelve solo con el molde, pero conviene mirar el ejemplo con esto en mente antes de la sesión.
|
||||||
|
4. **PUE vs PPD** y **cuenta de servicio** — ya estaban diferidos a la sesión desde la versión anterior.
|
||||||
|
|
||||||
|
**Alcance del ejercicio — cotización, NO prefactura.** Razones: (1) es literalmente lo que Arturo contestó el 4-ago; (2) la aprobación de Araceli está entre `En proceso de facturación` y `Facturado`, así que la prefactura pertenece *después* — Noé lo cerró como tres candados: **cotización → prefactura → aprobación humana → timbrado** ([REGISTRO #55](../bitacora/REGISTRO.md)); (3) Arturo pidió gradualismo explícito (*"para ir monitoreando la eficiencia de la IA"*); (4) una cotización se cancela sin efecto fiscal, mientras que en BIND la prefactura **no es un recurso aparte** sino un registro en `Invoices` con `UUID = null`, y los *shapes* de los POST nunca se han probado.
|
||||||
|
|
||||||
|
**El disparo es manual en esta prueba.** No es un modo inventado para la ocasión: son las etapas del pipeline ya modelado `Draft → DryRun → Confirmed` (ADR-001) con los feature flags apagados. La versión automática es el mismo código con el flag encendido, y **depende de Azure** para correr continuo.
|
||||||
|
|
||||||
|
> ⚠️ **Contradicción pendiente que el ticket de ejemplo resolverá solo:** el 6-jul Ara describió que **el comercial genera la cotización en BIND** y la plataforma la *convierte* a prefactura ([REGISTRO #27](../bitacora/REGISTRO.md)); el 4-ago Arturo dijo que la plataforma la *genera*; y el formulario sigue exigiendo **adjuntar la cotización**. Si ambas cosas ocurren, **quedan dos cotizaciones por ticket**. El adjunto que Arturo ponga en el ejemplo lo dirá.
|
||||||
|
|
||||||
|
======================================================================
|
||||||
|
|
||||||
|
## VERSIÓN FINAL (la que Johann envía)
|
||||||
|
|
||||||
|
Buenas tardes, espero se encuentren bien.
|
||||||
|
|
||||||
|
Gracias por las definiciones, quedan claras y ya retomé el desarrollo, ya validé que el campo "Monto sin IVA" está creado y funcionando.
|
||||||
|
|
||||||
|
Para aterrizar los detalles que faltan sería de ayuda que Arturo cree hoy o mañana un ticket de ejemplo capturado "como debería ser", con todos los campos llenos tal como quieren que se capture de aquí en adelante. Contra ese molde llego a la sesión con el flujo ya funcionando.
|
||||||
|
|
||||||
|
Para la sesión, ¿les funciona el miércoles 12 o el jueves 13, a las 7:00 am?
|
||||||
|
|
||||||
|
El ejercicio termina en la cotización en BIND.
|
||||||
|
|
||||||
|
Sigo pendiente del acceso a Azure para publicar el ambiente.
|
||||||
|
|
||||||
|
Quedo atento.
|
||||||
|
|
||||||
|
Saludos.
|
||||||
|
|
||||||
|
======================================================================
|
||||||
|
|
||||||
|
**Lo que la versión final deja fuera y por tanto pasa a la sesión (verbal, sin constancia escrita previa):**
|
||||||
|
|
||||||
|
- **El destino de la cotización de prueba.** El correo ya no dice que el ejercicio genera una cotización real en BIND producción ni que se cancela al terminar. Anunciarlo al abrir la sesión, antes de ejecutar.
|
||||||
|
- **El compromiso de compartir el script antes.** Era la condición de Noé del 24-jul. Mandarlo igual por separado cuando esté listo.
|
||||||
|
- **La prefactura como paso siguiente** y los tres candados. Explicarlo en la sesión para que no esperen ver el flujo completo.
|
||||||
|
- **ACUNTIA** y **el supuesto de los tickets de `Automation for Jira`** (ver arriba).
|
||||||
|
- ✅ **El ticket de ejemplo ya tiene dueño nombrado** (Arturo). Solo falta **agregarlo al CC**: no venía en el correo de Noé del 7-ago.
|
||||||
@@ -0,0 +1,119 @@
|
|||||||
|
# Guion — Validación del prototipo Etapa 0 (jue 23-jul, 7:00 pm)
|
||||||
|
|
||||||
|
**Material:** `../prototipo/Prototipo-Balam-Etapa0.pdf` (11 páginas) + demo en paputec.mx/balam.
|
||||||
|
**Meta de la sesión:** que digan "sí, así opera Balam" pantalla por pantalla → cierra la Etapa 0.
|
||||||
|
**Regla de oro:** en cada pantalla, terminar con UNA pregunta de validación. No presentar; validar.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Apertura (2 min)
|
||||||
|
|
||||||
|
> "Gracias por el tiempo. Lo que van a ver es el proceso que ustedes me enseñaron en las sesiones con Araceli y Arturo, convertido en pantallas. Todo es dato ficticio. El objetivo de hoy es que me digan qué SÍ refleja su operación y qué corregimos — cada 'así no es' de hoy me ahorra semanas de desarrollo."
|
||||||
|
|
||||||
|
Aviso útil: "Ayer con Arturo y Araceli aprendí cosas nuevas (fee, flujo Jira); les voy a ir señalando dónde ya sé que hay ajustes."
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Pág. 2 · El flujo de principio a fin (3 min)
|
||||||
|
|
||||||
|
Recorre los 5 pasos: **Solicitud Jira → Cotización BIND → Prefactura → Aprobación humana → CFDI + Envío**.
|
||||||
|
|
||||||
|
> "Este es el mapa de todo. La automatización está donde ustedes la pidieron: después de Jira, y con validación humana antes de timbrar. Aprobar y timbrar son acciones separadas — nada se timbra solo."
|
||||||
|
|
||||||
|
**⚠️ AQUÍ VA LA PREGUNTA GRANDE (caja de reglas, primera línea "PPD por defecto"):**
|
||||||
|
> "Esta regla salió del Discovery: PPD por defecto porque cobran a crédito 30–90 días. Pero al sincronizar el histórico real encontré que las 1,374 facturas con método de pago son TODAS PUE, cero PPD. ¿Es política del despacho o práctica que quieren corregir? Cambia el default de la plataforma y toca el requerimiento del SAT que ya vivieron."
|
||||||
|
|
||||||
|
Anotar la respuesta — define B7 (capa de escritura).
|
||||||
|
|
||||||
|
- Validar también: "¿Recordatorios de morosidad a todos los clientes, sin excepciones — sigue firme?" (lo decidió Ara el 6-jul).
|
||||||
|
|
||||||
|
## Pág. 3 · Acceso (30 seg — no gastar tiempo)
|
||||||
|
|
||||||
|
> "Ingreso con cuenta interna, control de sesión y roles — cada quien ve lo suyo. Esto ya está construido en el backend real."
|
||||||
|
|
||||||
|
## Pág. 4 · Panel de control (4 min)
|
||||||
|
|
||||||
|
Señalar en orden: facturado del mes **separado MXN/USD (nunca se suman monedas)** · emitidas vs pendientes · cartera vencida · bandeja "Qué necesita atención".
|
||||||
|
|
||||||
|
> "La bandeja es el corazón: bloqueos por falta de cotización, facturas vencidas escaladas, prefacturas esperando aprobación, clientes nuevos sin cédula, facturas emitidas sin enviar. Cada línea tiene responsable y antigüedad."
|
||||||
|
|
||||||
|
Dato para credibilidad: "Esto ya no es dibujo — el backend real ya sincroniza la cartera de producción: 1,494 facturas, y los totales cuadran contra BIND al centavo."
|
||||||
|
|
||||||
|
**Pregunta:** "¿Son estos los 4 números que quieren ver al abrir? ¿Falta o sobra una tarjeta?" (Recordar: Pedro ya tiene Power BI de aging — esto es operación, no reporteo; no duplico lo suyo.)
|
||||||
|
|
||||||
|
## Pág. 5 · Solicitudes (Jira) (3 min)
|
||||||
|
|
||||||
|
> "La entrada del flujo: sin ticket, no se factura — tal como operan hoy. Filtros por estatus: por validar, listos, bloqueados, facturados."
|
||||||
|
|
||||||
|
Conectar con ayer:
|
||||||
|
> "Con lo que me mostró Arturo ayer, esto mapea directo a su space 'Facturación' y su flujo de estatus — donde 'resuelto' es el que confirma facturación válida. La sesión con Pedro para el API de Jira nos dirá si estos tickets entran solos o se capturan."
|
||||||
|
|
||||||
|
**Pregunta:** "¿Las columnas (tipo, cliente, moneda, monto, crédito, recurrente, estatus) son las que Arturo necesita para decidir qué facturar?"
|
||||||
|
|
||||||
|
## Pág. 6 · Detalle de la solicitud (3 min)
|
||||||
|
|
||||||
|
Señalar: datos del ticket, adjuntos (cotización, OC, horas con VoBo), doble check de administración, **cotización BIND vinculada como requisito**.
|
||||||
|
|
||||||
|
**Nota ⚠️ (fila "Cliente nuevo"):**
|
||||||
|
> "Ayer Arturo me mostró el alta completa en BIND. La decisión que les pido hoy: cuando el ticket trae un cliente que NO existe en BIND, ¿la plataforma solo lo detecta y avisa a administración, o también lo da de alta? Mi recomendación para el MVP: detectar y bloquear; el alta la hace administración en BIND como hoy."
|
||||||
|
|
||||||
|
Anotar decisión — cruza con la estimación de ~10 h de Jira→BIND.
|
||||||
|
|
||||||
|
## Pág. 7 · Preparación, aprobación y emisión (4 min)
|
||||||
|
|
||||||
|
> "Asistente de 5 pasos: cotización BIND (sin cotización no se puede continuar) → datos de factura → conceptos e IVA 16%/0% con alerta si no cuadra → prefactura en simulación, sin timbrar → aprobación humana. Aprobar NO timbra: son dos clics distintos, de dos momentos distintos."
|
||||||
|
|
||||||
|
Reforzar seguridad (le importa a Noé):
|
||||||
|
> "Y cuando llegue la escritura real: dry-run, confirmación humana por factura y feature flag apagado por defecto — como acordamos ayer, con todo el cuidado de que es producción."
|
||||||
|
|
||||||
|
**Pregunta:** "¿Quién aprueba en la vida real — siempre Arturo, o hay segundo aprobador?"
|
||||||
|
|
||||||
|
## Pág. 8 · Envío al cliente (3 min)
|
||||||
|
|
||||||
|
> "El proceso NO termina al timbrar: termina cuando el correo sale con los adjuntos correctos. Checklist bloqueante por cliente — ejemplo CEMEX: PDF+XML automático, Excel de horas con VoBo, asunto con su nomenclatura exacta. Si falta algo, el envío se bloquea — hoy un error de estos rebota la factura y con 90 días de crédito cada rebote empuja el cobro."
|
||||||
|
|
||||||
|
**Pregunta / pendiente:** "Para cargar las reglas reales necesito el Excel de particularidades por cliente que quedó con Ara y Arturo — ¿cómo va? Ya revisé los procedimientos que me mandaron el 16-jul; el Excel me completa destinatarios y nomenclaturas."
|
||||||
|
|
||||||
|
## Pág. 9 · Cuentas por cobrar (3 min)
|
||||||
|
|
||||||
|
> "El aging que hoy llevan en Excel, leído de BIND: por vencer, 1–30, 31–60, 61–90, +90 — MXN y USD siempre separados, el tipo de cambio DOF es solo informativo. Saldo y días de mora por factura."
|
||||||
|
|
||||||
|
Credibilidad: "Los números reales ya viven en el backend: la cartera sincronizada cuadra exacta contra BIND."
|
||||||
|
|
||||||
|
**Pregunta:** "¿Estos rangos de aging son los que usan? ¿Les sirve el corte por factura o necesitan también por cliente?"
|
||||||
|
|
||||||
|
## Pág. 10 · Pagos y recordatorios (4 min) — LA PANTALLA CON MÁS AJUSTES DE AYER
|
||||||
|
|
||||||
|
1. **Fee (caja "Tolerancia por comisión"):**
|
||||||
|
> "Ayer Araceli me aclaró el proceso real: el fee bancario lo registra y deduce EL DESPACHO — Balam no hace ese asiento. Entonces esta pantalla se ajusta: la plataforma DETECTA la diferencia en pagos internacionales, la ETIQUETA como probable fee y avisa; quien salda es el despacho. ¿De acuerdo con ese ajuste?"
|
||||||
|
|
||||||
|
2. **Fecha de detección:** "La fecha que muestro es la del registro en BIND (lo captura el despacho), no la fecha valor del banco — BIND no expone pagos individuales. ¿Les funciona así para el MVP?"
|
||||||
|
|
||||||
|
3. **Recordatorios (columna derecha) — poner el límite con suavidad:**
|
||||||
|
> "Ojo con esta parte: las alertas D+1 y el escalamiento D+15 son INTERNAS y van en el MVP. Los correos automáticos AL CLIENTE (D+5, D+30) están cotizados en el Anexo B — aquí los dejo visibles y preparados para activarse, pero no son parte de esta fase. Lo señalo para que no haya sorpresa al cierre."
|
||||||
|
|
||||||
|
## Pág. 11 · Bitácora (2 min)
|
||||||
|
|
||||||
|
> "Todo queda registrado: quién aprobó, quién emitió, qué override de IVA se hizo y por qué, con ticket Jira y folio BIND. Esto responde exactamente al problema que me contó Arturo ayer: 'dijeron que ya facturaron y no había evidencia'. Aquí la evidencia es automática. Y la bitácora ya existe en el backend real, no solo en el prototipo."
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Cierre (5 min)
|
||||||
|
|
||||||
|
1. **Pedir el veredicto explícito:**
|
||||||
|
> "¿Validan que este flujo representa su operación? Con su OK doy por cerrada la Etapa 0, aplico los ajustes que anotamos y el demo público lo doy de baja como quedamos."
|
||||||
|
|
||||||
|
2. **Recap de decisiones tomadas hoy** (leerlas en voz alta): PUE/PPD · alta de cliente (detectar vs crear) · fee = despacho confirmado · recordatorios internos vs Anexo B · rangos de aging.
|
||||||
|
|
||||||
|
3. **Siguientes pasos** (recordar compromisos de ayer): diagrama del flujo Jira en tamaño legible · sesión con Pedro (API Jira + facturación recurrente) · Excel de particularidades de envío · Azure esta semana.
|
||||||
|
|
||||||
|
4. **Si el ambiente lo permite, la comercial:**
|
||||||
|
> "Ing. Noé, ya de regreso del viaje — ¿pudieron revisar la propuesta v1.2 para lo de la facturación inicial de las 30 horas?"
|
||||||
|
|
||||||
|
## Chuleta de respuestas rápidas
|
||||||
|
|
||||||
|
- **"¿Y si queremos que también mande correos al cliente?"** → Está cotizado en Anexo B; la pantalla ya lo deja preparado. Lo activamos como ampliación cuando cierre el MVP.
|
||||||
|
- **"¿Esto ya jala con BIND de verdad?"** → La lectura sí: clientes, facturas, cotizaciones y cartera ya sincronizan de producción, en solo-lectura. La escritura va con los candados acordados.
|
||||||
|
- **"¿Cuándo estará?"** → Etapa 1 va adelantada (backend, auth, sync y seguridad por tenant ya construidos). Las fechas finas dependen de Azure y de las decisiones de hoy.
|
||||||
|
- **"¿Multiempresa / Regiotour?"** → Fuera del MVP (un token BIND por empresa); el diseño ya lo soporta a futuro.
|
||||||
|
- **"¿Forecast por contrato?"** → Lo dijo Noé ayer: no es para ahorita; queda anotado para una fase posterior.
|
||||||
@@ -0,0 +1,126 @@
|
|||||||
|
# Etapa 0 — Hallazgos y decisiones de arquitectura
|
||||||
|
|
||||||
|
**Proyecto:** Plataforma de Automatización Financiera · Balam
|
||||||
|
**Estado:** borrador para cerrar con la validación del prototipo
|
||||||
|
**Última actualización:** 2026-07-15
|
||||||
|
|
||||||
|
## 1. Resultado del Discovery
|
||||||
|
|
||||||
|
El flujo operativo confirmado es:
|
||||||
|
|
||||||
|
**Jira ITSM → cotización en BIND → prefactura → validación humana → CFDI → envío según reglas del cliente → cobranza**
|
||||||
|
|
||||||
|
Reglas de negocio principales:
|
||||||
|
|
||||||
|
- Toda factura debe partir de una cotización en BIND.
|
||||||
|
- La prefactura es obligatoria antes de timbrar.
|
||||||
|
- PPD es el método predeterminado; PUE requiere una decisión consciente.
|
||||||
|
- IVA esperado: 16 % en operación nacional y 0 % para operación extranjera sin efectos fiscales, sujeto a las excepciones fiscales que confirme Balam.
|
||||||
|
- El alta de un cliente nuevo es un flujo separado y requiere definición final de Arturo.
|
||||||
|
- Los recordatorios se desean para todos los clientes; los correos automáticos externos permanecen fuera del MVP original (Anexo B) hasta confirmar alcance.
|
||||||
|
- La cobranza se controla por factura y folio. Debe tolerar diferencias por comisiones/fees conforme a la regla que confirme Arturo.
|
||||||
|
|
||||||
|
## 2. Resultado de la validación de BIND
|
||||||
|
|
||||||
|
La prueba del 6-jul realizó 117 solicitudes exclusivamente GET contra la cuenta real, sin persistir datos de Balam.
|
||||||
|
|
||||||
|
Confirmado:
|
||||||
|
|
||||||
|
- Saldo por factura viable con `Total - Payments - CreditNotes`.
|
||||||
|
- Cotizaciones, prefacturas, facturas, clientes, impuestos, PPD/PUE, vencimientos y PDF son consultables.
|
||||||
|
- Aging y alertas internas son viables sin consultar pagos individuales.
|
||||||
|
- El volumen proyectado de sincronización consume aproximadamente 5 % del límite de 20,000 solicitudes diarias.
|
||||||
|
|
||||||
|
Restricciones:
|
||||||
|
|
||||||
|
- No se encontró endpoint de pagos individuales ni complementos de pago (REP).
|
||||||
|
- BIND no expone el vínculo cotización→factura; la plataforma deberá conservarlo.
|
||||||
|
- Algunos campos clave solo aparecen en el detalle de cada factura.
|
||||||
|
- No hay webhooks confirmados; el diseño base usará sincronización incremental por polling.
|
||||||
|
- El token está acotado a una sola empresa.
|
||||||
|
- La cuenta actual es de producción y puede tener privilegios elevados; no se autoriza escritura todavía.
|
||||||
|
|
||||||
|
Detalle técnico: [`../bind-api-sandbox/VALIDACION-API.md`](../bind-api-sandbox/VALIDACION-API.md).
|
||||||
|
|
||||||
|
## 3. Decisiones de arquitectura
|
||||||
|
|
||||||
|
### ADR-001 · Lectura primero y escritura controlada
|
||||||
|
|
||||||
|
**Decisión:** operar inicialmente en modo solo lectura. Toda escritura futura requiere autorización expresa, dry-run, confirmación humana y feature flag desactivado por defecto.
|
||||||
|
|
||||||
|
**Motivo:** Balam no cuenta con sandbox y una operación incorrecta puede tener efecto fiscal.
|
||||||
|
|
||||||
|
### ADR-002 · Sincronización incremental
|
||||||
|
|
||||||
|
**Decisión:** persistir en PostgreSQL el último punto de sincronización y consultar únicamente registros nuevos o modificados. Paginar siempre con lotes de hasta 100.
|
||||||
|
|
||||||
|
**Motivo:** BIND no ofrece conteos ni `nextLink`; PPD/PUE y días de crédito requieren consultas de detalle.
|
||||||
|
|
||||||
|
### ADR-003 · Trazabilidad propia
|
||||||
|
|
||||||
|
**Decisión:** almacenar las relaciones `ticket Jira → cotización BIND → factura BIND` en la base de datos de la plataforma.
|
||||||
|
|
||||||
|
**Motivo:** la API de BIND no expone de forma confiable el vínculo cotización→factura.
|
||||||
|
|
||||||
|
### ADR-004 · Detección de pagos
|
||||||
|
|
||||||
|
**Decisión:** para el MVP, detectar pagos mediante cambios de estatus y saldo residual. Registrar la fecha de detección, no presentarla como fecha valor.
|
||||||
|
|
||||||
|
**Motivo:** no se encontró un recurso de pagos/REP. La fecha contable real requerirá información adicional o una ampliación.
|
||||||
|
|
||||||
|
### ADR-005 · Separación por empresa
|
||||||
|
|
||||||
|
**Decisión:** el MVP opera únicamente con la empresa Balam asociada al token. Multiempresa queda fuera del alcance.
|
||||||
|
|
||||||
|
**Motivo:** cada empresa requiere su propia cuenta/token de BIND.
|
||||||
|
|
||||||
|
### ADR-006 · Integración Jira→BIND pendiente de alcance
|
||||||
|
|
||||||
|
**Decisión:** diseñar el modelo para conservar el identificador del ticket, pero no comprometer todavía la automatización que crea cotizaciones desde Jira.
|
||||||
|
|
||||||
|
**Motivo:** el Discovery confirmó a Jira como entrada del proceso, pero la integración automática no estaba incluida explícitamente en el MVP. Antes de desarrollarla se requiere:
|
||||||
|
|
||||||
|
- Confirmación de alcance.
|
||||||
|
- Cuenta técnica y acceso API/OAuth a Jira.
|
||||||
|
- Webhook o regla de automatización.
|
||||||
|
- Mapeo de campos y estatus disparador.
|
||||||
|
- Acceso controlado de escritura a BIND.
|
||||||
|
|
||||||
|
## 4. Preguntas que deben cerrarse
|
||||||
|
|
||||||
|
### Con Arturo / CEO — sesión del 22-jul
|
||||||
|
|
||||||
|
1. Flujo definitivo de alta de cliente nuevo.
|
||||||
|
2. Tratamiento en BIND de diferencias por comisión/fee.
|
||||||
|
3. Particularidades de envío por cliente.
|
||||||
|
4. Estatus de Jira que autoriza crear una cotización.
|
||||||
|
5. Campos obligatorios y manejo de tickets incompletos.
|
||||||
|
|
||||||
|
### Con Pedro / Noé
|
||||||
|
|
||||||
|
1. Endpoint de pagos individuales o REP.
|
||||||
|
2. Mapeo interno de `CFDIUse` a clave SAT.
|
||||||
|
3. Formato de respuesta de `/{id}/xml`.
|
||||||
|
4. Disponibilidad de webhooks en BIND.
|
||||||
|
5. Token utilizado por Power BI y decisión sobre usuario de solo lectura.
|
||||||
|
6. Permisos e identidad técnica para Jira, si se aprueba la integración.
|
||||||
|
|
||||||
|
## 5. Dependencias de infraestructura
|
||||||
|
|
||||||
|
| Dependencia | Estado al 15-jul | Impacto |
|
||||||
|
|---|---|---|
|
||||||
|
| Token BIND | Entregado; uso solo lectura | Desbloquea consultas y cliente BIND |
|
||||||
|
| Repositorio privado GitHub | Autorizado por Noé; invitación pendiente | Necesario para publicar backend/frontend |
|
||||||
|
| Azure | Programado para semana del 21-jul | No bloquea desarrollo local |
|
||||||
|
| CI/CD y secretos | Actividad separada para 24-jul | Depende de repo y Azure |
|
||||||
|
| Jira técnico | No solicitado todavía | Solo bloquea integración automática Jira→BIND |
|
||||||
|
|
||||||
|
## 6. Criterio de cierre de Etapa 0
|
||||||
|
|
||||||
|
La Etapa 0 se cierra cuando:
|
||||||
|
|
||||||
|
- Balam valida el prototipo o registra ajustes.
|
||||||
|
- Se incorporan las decisiones de la sesión del 22-jul.
|
||||||
|
- Se valida el prototipo en la sesión del 23-jul.
|
||||||
|
- Este documento se actualiza con las respuestas finales.
|
||||||
|
- Se emite un plan refinado con alcance explícito para Jira, recordatorios externos y escritura en BIND.
|
||||||
@@ -0,0 +1,411 @@
|
|||||||
|
# Investigación de la API de Jira — qué probar exactamente
|
||||||
|
|
||||||
|
**Fecha:** 30-jul-2026 · **Actualizado:** 10-ago-2026
|
||||||
|
**Propósito:** lista cerrada de pruebas y preguntas para habilitar la integración **Jira → cotización BIND** (estimada en ~10 h, [REGISTRO #51](../bitacora/REGISTRO.md)). Documento de trabajo interno; sirve también como briefing para el agente/chat especializado en la API de Jira.
|
||||||
|
|
||||||
|
**Estado validado (30-jul):**
|
||||||
|
- Sitio: `https://balam-jsm-temp.atlassian.net`. El token regenerado de Pedro, usado con Basic auth y `pedro.ayala@balamtalentoestrategico.com`, respondió **200** a `GET /rest/api/3/myself`.
|
||||||
|
- Facturación es el proyecto JSM **`FAC`** (`projectId: 10034`, `serviceDeskId: 35`), de tipo `service_desk` y estilo `classic` (company-managed).
|
||||||
|
- La búsqueda de FAC devolvió **66 tickets**. La API vigente es `GET /rest/api/3/search/jql`, con paginación por `nextPageToken`; el endpoint clásico `/rest/api/3/search` responde **410 Gone**.
|
||||||
|
- Estatus confirmados: `Open`, `En proceso de facturación`, `En espera por colaborador`, `En validación nacional`, `En validación extranjera`, `Facturado` y `Cancelado`. No se observó un estatus llamado “Resuelto”.
|
||||||
|
- Límites de consumo ya aclarados por Pedro (sin tope mensual; 3 límites de velocidad; telemetría solo en headers `X-RateLimit-*`).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ⭐ Decisiones de negocio recibidas (correo del 7-ago) y su verificación por API (10-ago)
|
||||||
|
|
||||||
|
Balam contestó las tres preguntas del 30-jul. Arturo respondió el **4-ago** (en verde sobre el correo de Noé), Noé lo reenvió el **7-ago**. Fuente: `RV_ Seguimiento_ medición de consumo API de Jira y accesos.eml`.
|
||||||
|
|
||||||
|
| Decisión de Balam | Quién / cuándo | Verificación por API (10-ago) |
|
||||||
|
|---|---|---|
|
||||||
|
| La cotización BIND **se genera al entrar a `En proceso de facturación`** | Arturo, 4-ago | ✅ Estatus y transición `Iniciar facturación` ya inventariados (Bloque 3) |
|
||||||
|
| **Todo se tramita como facturación normal**; el caso recurrente espera a que se explore el módulo de Proyectos de BIND | Arturo, 4-ago | ⚠️ El campo `Facturación recurrente` sigue siendo **obligatorio** y se está llenando (FAC-100: `Si`, periodo `12`) |
|
||||||
|
| Se observan **todos los request types** de la bandeja de facturación, no solo `Facturación adicional` | Arturo, 4-ago | 🔴 **10 de 69 tickets no tienen request type alguno** y ninguno trae campos de facturación (ver Bloque 1) |
|
||||||
|
| Monto y conceptos van **como campos de Jira**, no del adjunto; **omitir el campo de recurrencia** | Arturo, 4-ago | ⚠️ `Monto sin IVA` ya existe y funciona; **`conceptos` no existe como campo en todo el sitio** (ver Bloque 2) |
|
||||||
|
| Las pruebas de escritura son **prueba acompañada sobre un ticket**, no proyecto `FACTEST` | Noé, 7-ago | Se descarta el sandbox en la instancia de Balam → aplica el protocolo del Bloque 6 sin excepción |
|
||||||
|
|
||||||
|
**Lo que sigue abierto tras estas respuestas:** de dónde salen los **conceptos/partidas** de la cotización, qué hacer con los tickets sin campos estructurados, si nacional/extranjero cambia la cotización o solo el paquete de salida, **PUE vs PPD**, la fecha de la prueba acompañada y la migración a cuenta de servicio.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Bloque 0 — Acceso autenticado ✅
|
||||||
|
|
||||||
|
El 401 del 29-jul quedó explicado por la combinación de token previo y datos de conexión incompletos. Pedro confirmó el correo, regeneró el token y compartió el dominio del sitio.
|
||||||
|
|
||||||
|
**Configuración que funcionó:**
|
||||||
|
```text
|
||||||
|
GET https://balam-jsm-temp.atlassian.net/rest/api/3/myself
|
||||||
|
Authorization: Basic base64(pedro.ayala@balamtalentoestrategico.com:<token>)
|
||||||
|
Accept: application/json
|
||||||
|
```
|
||||||
|
|
||||||
|
El resultado fue **HTTP 200**. Por tanto, para esta integración se usará la API directa del sitio con Basic auth; no hay evidencia de que se requiera una ruta `api.atlassian.com/ex/jira/{cloudId}`.
|
||||||
|
|
||||||
|
**Prueba canónica de auth (la primera que debe correrse siempre):**
|
||||||
|
```
|
||||||
|
GET /rest/api/3/myself
|
||||||
|
```
|
||||||
|
Interpretación de la respuesta:
|
||||||
|
- **200** → auth OK; además devuelve `accountId` (identidad con la que la plataforma va a escribir — dato de gobierno, ver Bloque 7).
|
||||||
|
- **401** → credencial inválida/inactiva o esquema mal armado.
|
||||||
|
- **403** → credencial VÁLIDA pero sin permiso → problema de permisos, no de token. Distinguir 401 de 403 es el diagnóstico más barato que existe.
|
||||||
|
|
||||||
|
> ⚠️ **Registrar siempre los headers de la respuesta 401 completos.** Ahí vive la pista (`WWW-Authenticate`, `X-Seraph-LoginReason`, `X-Failure-Category`).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Bloque 1 — Topología: Jira Service Management confirmado ✅
|
||||||
|
|
||||||
|
FAC está confirmado como **Jira Service Management (JSM)**. Por ello la integración requiere dos familias de API:
|
||||||
|
|
||||||
|
| API | Para qué | Cuándo usarla |
|
||||||
|
|---|---|---|
|
||||||
|
| `/rest/api/3/...` | Issues genéricos: JQL, campos, transiciones, adjuntos, changelog | Lectura/sync, transiciones, trazabilidad |
|
||||||
|
| `/rest/servicedeskapi/...` | Requests de portal: request types, campos del formulario, aprobaciones, SLA, comentarios públicos vs internos | Crear tickets como los crea un humano; leer aprobaciones y SLA |
|
||||||
|
|
||||||
|
**Resultados:**
|
||||||
|
```
|
||||||
|
GET /rest/api/3/project/FAC
|
||||||
|
```
|
||||||
|
→ `projectTypeKey: service_desk`, `style: classic`, `id: 10034`.
|
||||||
|
|
||||||
|
```
|
||||||
|
GET /rest/servicedeskapi/servicedesk
|
||||||
|
```
|
||||||
|
→ FAC corresponde a `serviceDeskId: 35`.
|
||||||
|
|
||||||
|
Request types **publicados en el portal** (`GET /rest/servicedeskapi/servicedesk/35/requesttype`, `isLastPage: true`):
|
||||||
|
|
||||||
|
| requestTypeId | Nombre | Campos obligatorios |
|
||||||
|
|---:|---|---:|
|
||||||
|
| 84 | Bajas | 7 |
|
||||||
|
| 83 | Facturación adicional | 10 |
|
||||||
|
| 389 | Automatización | 1 (solo `summary`) |
|
||||||
|
| 12 | Facturación | 1 (solo `summary`) |
|
||||||
|
|
||||||
|
Contratos verificados el 10-ago con `/requesttype/{id}/field`:
|
||||||
|
|
||||||
|
| Request type | Hallazgo |
|
||||||
|
|---|---|
|
||||||
|
| **Bajas** (`84`) | Empresa origen, Summary, adjunto de Cálculo/VoBo de Finiquito, Nombre del Cliente, Nombre del Colaborador, Fecha de la Baja y Motivo de la Baja. |
|
||||||
|
| **Facturación adicional** (`83`) | Empresa origen, `MES / DESCRIPCIÓN / CLIENTE` (es el `summary`), Cliente Nuevo, Nombre del Cliente, adjunto de cotización/CSF, Tipo de Moneda, Días de Crédito, Facturación recurrente, Periodo de recurrencia y **`Monto sin IVA`**. Es el contrato más completo. ⚠️ **Cambió desde el 30-jul:** se agregó `Monto sin IVA` y desapareció `Periodo de Incidencias`. |
|
||||||
|
| **Automatización** (`389`) | Solo exige Summary. |
|
||||||
|
| **Facturación** (`12`) | Solo exige Summary. |
|
||||||
|
|
||||||
|
### 🔴 “Todos los request types” incluye tickets sin request type
|
||||||
|
|
||||||
|
La bandeja de FAC muestra en el filtro de la UI **6 de 6** opciones: las 4 de arriba más **`Empty`** y **`Emailed request`**. Ninguna de esas dos aparece en `servicedeskapi` porque no están publicadas en el portal. El conteo real sobre los **69 tickets** de FAC (10-ago):
|
||||||
|
|
||||||
|
| Request type | Tickets | c/Monto | c/Moneda | c/Cliente | c/Adjunto |
|
||||||
|
|---|---:|---:|---:|---:|---:|
|
||||||
|
| 83 — Facturación adicional | 48 | **2** | 48 | 48 | 48 |
|
||||||
|
| **(sin request type)** | **10** | 0 | 0 | 0 | 3 |
|
||||||
|
| 84 — Bajas | 6 | 0 | 0 | 6 | 6 |
|
||||||
|
| 389 — Automatización | 4 | 0 | 0 | 0 | 4 |
|
||||||
|
| 12 — Facturación | 1 | 0 | 0 | 0 | 1 |
|
||||||
|
|
||||||
|
Los **10 tickets sin request type** tienen `creator` y `reporter` = **`Automation for Jira`**: nacen de una regla de Automation desde otras bandejas (HH, SA, IN, RH), no del portal. Y **no son ruido**: `FAC-91` es uno de ellos, está vivo en `En validación nacional` y su summary es `[FACTURA COMPLETA - Staff Augmentation] Axians - Incident Manager — RH-36`. Es decir, **hay facturación real entrando por una vía que no tiene ni un solo campo estructurado** — los datos viajan codificados en el `summary` y en la `description` (ADF), más 2 adjuntos.
|
||||||
|
|
||||||
|
`Emailed request` hoy tiene 0 tickets, pero existe en la configuración: cualquier correo a la bandeja aterriza ahí, también sin campos.
|
||||||
|
|
||||||
|
**Implicación:** el acuerdo literal “todos los request types” significa que **~14% de los tickets no puede generar una cotización automática** con el diseño de campos estructurados. Hay dos salidas y conviene que Balam elija explícitamente: (a) acotar la automatización a los request types que sí traen campos y dejar los demás en tratamiento manual, o (b) que Automation for Jira propague los campos al crear el ticket en FAC. La opción (b) es trabajo del lado de Balam, no de la plataforma.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Bloque 2 — Modelo de datos del ticket FAC (el mapeo) 🔴
|
||||||
|
|
||||||
|
Esto es lo que decide si la integración es de 10 h o de 25. **La pregunta de fondo: ¿los datos de facturación vienen en campos estructurados o en texto libre?** Si vienen en texto libre, hay que negociar campos nuevos con Pedro o meter parsing frágil.
|
||||||
|
|
||||||
|
**Lo que la plataforma necesita de cada ticket para armar una cotización en BIND:**
|
||||||
|
|
||||||
|
| Dato requerido | Por qué | Qué verificar |
|
||||||
|
|---|---|---|
|
||||||
|
| Cliente | Match contra `Clients` de BIND (por RFC o nombre normalizado) | ¿Campo custom? ¿Lista desplegable? ¿Texto libre? ¿Trae RFC? |
|
||||||
|
| Monto y moneda | Cotización BIND; la cartera **jamás mezcla monedas** | ¿Campo numérico? ¿Viene la moneda separada o embebida en el texto? |
|
||||||
|
| Nacional vs internacional | Determina PDF+XML (nacional) vs solo PDF (extranjero) | ¿Se deduce del cliente o hay campo/etiqueta? |
|
||||||
|
| Concepto / descripción | Línea de la cotización | Ver formato (ver ⚠️ ADF abajo) |
|
||||||
|
| Tipo de solicitud | adicional / recurrente / baja / headhunting / staff augmentation | ¿Request type, issue type, o campo? |
|
||||||
|
| Folio `FAC-nnn` | Trazabilidad ticket ↔ cotización ↔ factura (persistir en el modelo) | Confirmar formato y estabilidad (ver Bloque 5, ⚠️ moves) |
|
||||||
|
| Solicitante / área | Auditoría | `reporter` vs `requester` de JSM (no son lo mismo) |
|
||||||
|
| Adjuntos | Constancia fiscal, Excel de horas (CEMEX), estado de cuenta | Cómo se descargan y con qué auth |
|
||||||
|
|
||||||
|
**Resultados de lectura:**
|
||||||
|
```
|
||||||
|
GET /rest/api/3/issue/FAC-98?expand=names,renderedFields
|
||||||
|
```
|
||||||
|
→ caso real de tipo Bajas: cliente Axians, colaborador, fecha y motivo de baja en campos estructurados; una evidencia adjunta; sin descripción. Los valores de texto enriquecido regresan como ADF.
|
||||||
|
|
||||||
|
### Mapeo de campos confirmado (10-ago)
|
||||||
|
|
||||||
|
| Campo de Jira | `fieldId` | Tipo | Valores | → BIND |
|
||||||
|
|---|---|---|---|---|
|
||||||
|
| Empresa origen | `customfield_11423` | option | `Balam`, `RegioTurk`, `Helmstone` | Emisor (multi-empresa) |
|
||||||
|
| MES / DESCRIPCIÓN / CLIENTE | `summary` | string | texto libre | Referencia / parseo de respaldo |
|
||||||
|
| Cliente Nuevo | `customfield_10052` | option | `Si`, `No` | Decide si hay que dar de alta el cliente |
|
||||||
|
| Nombre del Cliente | `customfield_10202` | **ADF** (textarea) | texto enriquecido | Match contra `Clients` de BIND |
|
||||||
|
| Tipo de Moneda | `customfield_10053` | option | `MXN`, `USD`, `OTRO` | Moneda de la cotización |
|
||||||
|
| Días de Crédito | `customfield_10054` | option | `30`, `45`, `NA` | Condiciones de pago |
|
||||||
|
| Facturación recurrente | `customfield_10552` | option | `Si`, `No` | Fuera de alcance por ahora (Arturo, 4-ago) |
|
||||||
|
| Periodo de recurrencia | `customfield_10553` | string | texto | Fuera de alcance por ahora |
|
||||||
|
| **Monto sin IVA** | **`customfield_11556`** | **number/float** | — | **Total de la cotización** |
|
||||||
|
| Approvers | `customfield_10003` | array/user | — | Gobierno (ver Bloque 3) |
|
||||||
|
|
||||||
|
⚠️ **`Nombre del Cliente` es ADF, no string.** El match contra BIND tiene que extraer texto plano de un documento estructurado, no leer un campo de texto. Nada garantiza que el nombre escrito a mano coincida con la razón social en BIND; **el campo no trae RFC**, que sería la llave confiable.
|
||||||
|
|
||||||
|
### 🔴 Dos huecos que el acuerdo del 4-ago no cierra
|
||||||
|
|
||||||
|
1. **`Monto sin IVA` existe pero está prácticamente vacío.** Solo **2 de 69** tickets lo tienen: `FAC-100` y `FAC-101`, ambos creados el **4-ago** — el mismo día en que Arturo contestó el correo. El campo se agregó en ese momento; los 67 tickets anteriores lo tienen en `null`. Es buena noticia (el acuerdo ya se implementó) con dos consecuencias: no hay histórico para validar el mapeo contra facturas reales, y la obligatoriedad solo aplica al crear por portal — un ticket nacido de Automation entra con `null` igual.
|
||||||
|
|
||||||
|
2. **`conceptos` / partidas no existe como campo en NINGUNA parte del sitio.** `GET /rest/api/3/field` filtrado por monto/concepto/partida/importe/precio/cantidad/RFC devuelve únicamente:
|
||||||
|
- `customfield_11556` — `Monto sin IVA` (en uso)
|
||||||
|
- `customfield_11522` — `Monto con IVA` (existe, **no está en el formulario 83**)
|
||||||
|
- `customfield_10031` — `Total forms` (de JSM, no es de facturación)
|
||||||
|
|
||||||
|
Con un solo monto agregado **no se pueden armar partidas**: la cotización BIND queda de una sola línea con la descripción del `summary`. Eso puede ser aceptable como decisión, pero **hay que tomarla explícitamente**, y no cubre el caso ACUNTIA. Que exista `Monto con IVA` sin usar además abre la pregunta de si el IVA lo calcula BIND o viene dado.
|
||||||
|
|
||||||
|
**El caso ACUNTIA ya tiene evidencia:** `FAC-100` es exactamente eso — summary `Julio/CONSULTORIA Y SERVICIOS (Eduardo Fiallo CEMEXUSA)/ACUNTIA`, `Helmstone`, `USD`, 45 días, `Monto sin IVA = 7594.00`, con 2 adjuntos. **Un solo monto para un ticket que históricamente representa 40–48 facturas** ([REGISTRO #55](../bitacora/REGISTRO.md) punto 6). El supuesto “un ticket → una cotización” necesita confirmarse contra este caso antes de codificar.
|
||||||
|
|
||||||
|
> ⚠️ **ADF (Atlassian Document Format).** En Jira Cloud v3, `description` y los comentarios **no son texto plano ni wiki markup: son JSON estructurado**. Hay que decidir ya si se parsea ADF o se pide `expand=renderedFields` (HTML). Y al **escribir** comentarios/descripciones hay que **construir ADF válido**, no mandar un string. Es la trampa que más tiempo cuesta si se descubre tarde.
|
||||||
|
|
||||||
|
> ⚠️ **Adjuntos:** confirmar si `content` (URL de descarga) requiere el mismo Basic auth y si redirige. Y aplica la misma regla que con BIND: los adjuntos traen **datos reales de clientes** (constancias, estados de cuenta) — no van a disco del repo ni a documentos.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Bloque 3 — Workflow, estatus y transiciones (el disparador) ✅🟡
|
||||||
|
|
||||||
|
**Disparador confirmado por Arturo el 4-ago: la cotización BIND se crea al entrar a `En proceso de facturación`.** El inventario técnico ya estaba levantado, así que la regla de negocio queda cerrada.
|
||||||
|
|
||||||
|
**Inventario confirmado:**
|
||||||
|
```
|
||||||
|
GET /rest/api/3/project/FAC/statuses → estatus por tipo de issue, con id y statusCategory
|
||||||
|
```
|
||||||
|
|
||||||
|
| Estatus | ID | Categoría |
|
||||||
|
|---|---:|---|
|
||||||
|
| Open | 1 | To Do |
|
||||||
|
| En proceso de facturación | 10305 | In Progress |
|
||||||
|
| Facturado | 10306 | Done |
|
||||||
|
| En espera por colaborador | 10339 | In Progress |
|
||||||
|
| Cancelado | 10372 | Done |
|
||||||
|
| En validación nacional | 10635 | In Progress |
|
||||||
|
| En validación extranjera | 10669 | In Progress |
|
||||||
|
|
||||||
|
**Transiciones de lectura confirmadas** (son contextuales al estatus actual):
|
||||||
|
|
||||||
|
| Ticket / estatus actual | transitionId | Destino |
|
||||||
|
|---|---:|---|
|
||||||
|
| FAC-98 / En proceso de facturación | 6 | En espera por colaborador |
|
||||||
|
| FAC-98 / En proceso de facturación | 8 | En validación nacional |
|
||||||
|
| FAC-98 / En proceso de facturación | 9 | En validación extranjera |
|
||||||
|
| FAC-98 / En proceso de facturación | 2 | Cancelado |
|
||||||
|
| FAC-91 / En validación nacional | 2 | Cancelado |
|
||||||
|
| FAC-89 / Cancelado | 12 | Open |
|
||||||
|
|
||||||
|
`FAC-97` ya está Facturado y no expone transiciones. Las respuestas consultadas no devolvieron campos obligatorios para las transiciones anteriores. Aun así, no se ejecutará ninguna transición sin la sesión acompañada.
|
||||||
|
|
||||||
|
**Diagrama de workflow compartido por Balam:** confirma la ruta completa y los nombres de transición:
|
||||||
|
|
||||||
|
```text
|
||||||
|
Open --[Iniciar facturación]--> En proceso de facturación
|
||||||
|
En proceso de facturación --[Pausar actividad]--> En espera por colaborador
|
||||||
|
En espera por colaborador --[Retomar actividad]--> En proceso de facturación
|
||||||
|
En proceso de facturación --[Validar documentación nacional]--> En validación nacional
|
||||||
|
En proceso de facturación --[Validar documentación extranjera]--> En validación extranjera
|
||||||
|
En validación nacional o extranjera --[Facturación completa]--> Facturado
|
||||||
|
En validación nacional o extranjera --[Rechazar actividad]--> En proceso de facturación
|
||||||
|
```
|
||||||
|
|
||||||
|
El diagrama también contempla cancelación desde los estados de trabajo y reapertura; la API confirmó al menos `Cancelado --[Reabrir Ticket]--> Open` en FAC-89. **No existe un estado “Resuelto” en el workflow.** Con la respuesta del 4-ago, la regla queda: **`Iniciar facturación` (Open → En proceso de facturación) dispara la cotización**; `Facturado` confirma que la factura se emitió.
|
||||||
|
|
||||||
|
### 🔴 Hallazgo crítico: el estatus disparador dura minutos, no horas
|
||||||
|
|
||||||
|
Recorrido real de `FAC-100` (4–5 ago), desde su changelog:
|
||||||
|
|
||||||
|
| Hora | Transición | Autor |
|
||||||
|
|---|---|---|
|
||||||
|
| 05-ago 11:51:25 | `Open` → **`En proceso de facturación`** | Arturo Rosas |
|
||||||
|
| 05-ago 11:54:09 | → `En validación extranjera` | Arturo Rosas |
|
||||||
|
| 05-ago 12:16:37 | → `Facturado` | **Araceli Sánchez** |
|
||||||
|
|
||||||
|
**El ticket estuvo 2 minutos 44 segundos en el estatus disparador**, y 25 minutos de punta a punta. Hoy solo **1 de los 69 tickets** está parado en `En proceso de facturación` (65 ya están `Facturado`).
|
||||||
|
|
||||||
|
**Consecuencia de diseño, no negociable:** un poller de 15 min que pregunte *“¿qué tickets están hoy en `En proceso de facturación`?”* **se pierde la mayoría de los disparos**. La detección **tiene que leer el changelog** y buscar el *cruce* de estado dentro de la ventana, exactamente como se resolvió la detección de pagos en BIND (ADR-004). Esto confirma y vuelve obligatorio lo que antes era una preferencia de arquitectura, y refuerza el caso de los webhooks (Bloque 4) para reducir la latencia.
|
||||||
|
|
||||||
|
### ⭐ Hallazgo nuevo: el workflow tiene aprobaciones de JSM
|
||||||
|
|
||||||
|
Ni FAC-98 ni el diagrama lo mostraban. Tanto `FAC-100` como `FAC-91` traen:
|
||||||
|
- `customfield_10003` **Approvers** = `Araceli Sánchez`
|
||||||
|
- `customfield_10025` **Approvals** = el estatus donde vive la aprobación (`En validación extranjera` / `En validación nacional`)
|
||||||
|
|
||||||
|
Es decir, **el paso a `Facturado` pasa por una aprobación formal de Araceli**, no por una transición simple. Implicaciones: (1) la plataforma **no debe intentar aprobar** — eso es decisión humana; (2) si algún día tuviera que transicionar a `Facturado`, la vía correcta es `/rest/servicedeskapi/request/{id}/approval`, no `/transitions`; (3) la aprobación es un buen punto de trazabilidad para saber quién autorizó cada factura.
|
||||||
|
|
||||||
|
⚠️ **Trampas:**
|
||||||
|
1. ~~**`Facturado` como estatus ≠ campo `resolution`.**~~ **Resuelto (10-ago):** en `FAC-100`, `status = Facturado` **y** `resolution = Done` **y** `resolutiondate` poblada, todo consistente. Ambas señales sirven; se usará el cruce de estatus en el changelog por coherencia con el resto del diseño.
|
||||||
|
2. **Las transiciones son contextuales**: `/transitions` solo devuelve las salidas del estado actual, y pueden tener **pantallas con campos obligatorios**. Probar con `expand=transitions.fields` para saber si transicionar por API exige llenar algo.
|
||||||
|
3. **`issuetype` de FAC es `admon`**, no “Service Request”. Cualquier JQL o creación por API debe usar ese nombre.
|
||||||
|
|
||||||
|
**Changelog = la fuente de la detección.** `expand=changelog` da cada cambio de estatus con timestamp y autor. Verificado en FAC-100 (`total: 6`) y FAC-91 (`total: 8`): vienen completos y sin paginar en tickets de este tamaño. Para tickets con mucho historial hay que usar `/rest/api/3/issue/{key}/changelog`, que sí pagina.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Bloque 4 — Estrategia de sincronización: polling vs webhooks 🟡
|
||||||
|
|
||||||
|
El scheduler de la plataforma ya existe (`BackgroundService` + `PeriodicTimer` a 15 min). La pregunta es qué le pega a Jira.
|
||||||
|
|
||||||
|
**a) Búsqueda por JQL — validado.**
|
||||||
|
El endpoint clásico `GET /rest/api/3/search` respondió **410 Gone**. La integración debe usar `GET /rest/api/3/search/jql`, con `nextPageToken`. La lectura de FAC recuperó 66 tickets con ese mecanismo.
|
||||||
|
|
||||||
|
JQL de delta a validar:
|
||||||
|
```
|
||||||
|
project = FAC AND updated >= "-20m" ORDER BY updated ASC
|
||||||
|
```
|
||||||
|
⚠️ **`updated` en JQL tiene granularidad de minuto**, no de segundo → el checkpoint necesita **ventana de solape** (igual que el re-barrido de facturas abiertas de BIND) o se pierden tickets en el borde.
|
||||||
|
|
||||||
|
Validar también: `fields=` para pedir solo lo necesario (menos payload, menos puntos), y el tope real de `maxResults`.
|
||||||
|
|
||||||
|
**b) Webhooks.** Tres caminos, con dueños distintos:
|
||||||
|
|
||||||
|
| Opción | Quién la configura | Nota |
|
||||||
|
|---|---|---|
|
||||||
|
| Webhook de sitio (admin) | **Pedro** (requiere admin de Jira) | El más limpio; exige endpoint público |
|
||||||
|
| **Regla de Jira Automation** con "Send web request" | **Pedro, sin escribir código** | La más realista a corto plazo; se puede acotar a FAC y a un estatus |
|
||||||
|
| Webhook dinámico vía OAuth app | Desarrollo | **Expiran a los 30 días**, hay que refrescarlos |
|
||||||
|
|
||||||
|
**Recomendación:** **arrancar con polling** y dejar el webhook para después. Ningún webhook sirve hasta que exista un endpoint público, y Azure sigue pendiente del lado de Balam. El polling además es reversible y no depende de que Pedro toque su instancia.
|
||||||
|
|
||||||
|
**c) Rate limits — headers verificados ✅ (10-ago).** La respuesta de `/rest/api/3/search/jql` sí los emite:
|
||||||
|
|
||||||
|
```text
|
||||||
|
X-RateLimit-Limit: 350
|
||||||
|
X-RateLimit-Remaining: 348
|
||||||
|
RateLimit-Policy: "jira-burst-based";q=100;w=1
|
||||||
|
RateLimit: "jira-burst-based";r=348;t=1
|
||||||
|
```
|
||||||
|
|
||||||
|
Tres notas: (1) la telemetría existe y el autorregulado por headers es viable, como se le dijo a Erika el 29-jul; (2) el límite observado es **350**, no los 100/s que mencionó Pedro — hay que leer el header y no hardcodear el número; (3) aparecen también los headers estándar `RateLimit-*` (RFC) además de los `X-RateLimit-*`, así que el cliente debe tolerar ambos. `X-RateLimit-NearLimit` no apareció porque no se llegó al 20% del umbral. No se provocó un 429 deliberadamente contra producción.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Bloque 5 — La bandeja padre: de dónde nacen realmente los tickets 🟡
|
||||||
|
|
||||||
|
Pedro dijo que procesos que **se cierran en RH o Administración General caen en Facturación**. Técnicamente eso puede ser cuatro cosas distintas, y cada una se sincroniza diferente:
|
||||||
|
|
||||||
|
| Mecanismo | Cómo detectarlo | Implicación |
|
||||||
|
|---|---|---|
|
||||||
|
| **Issue link** (RH-45 relacionado con FAC-89) | `issuelinks` en el issue | Escuchar FAC basta; el link da contexto |
|
||||||
|
| **Clon / creación por Automation** | `changelog` + campo `creator` (usuario de automation) | Escuchar FAC basta |
|
||||||
|
| **Subtarea / hijo** | `parent` / `subtasks` | Relevante para ACUNTIA (1 ticket ↔ 40 facturas) |
|
||||||
|
| **Move entre proyectos** | `changelog` con cambio del campo `project` | 🔴 **La llave del ticket CAMBIA** (RH-45 → FAC-89). Cualquier folio persistido se rompe |
|
||||||
|
|
||||||
|
**Resultado de la prueba (10-ago): es creación por Automation, no move. ✅**
|
||||||
|
|
||||||
|
Los 10 tickets sin request type tienen `creator` = `reporter` = **`Automation for Jira`**, y sus changelogs (revisados en FAC-91) **no muestran cambios de `project` ni de `key`**. El patrón es: la regla de Automation **crea** un ticket nuevo en FAC y lo **liga** al original con `issuelinks`. Ejemplos de summary, que llevan el origen embebido:
|
||||||
|
|
||||||
|
```text
|
||||||
|
FAC-91 [FACTURA COMPLETA - Staff Augmentation] Axians - Incident Manager — RH-36
|
||||||
|
FAC-1 Seguimiento del Ticket Cerrado: HH-1 - Prueba #1
|
||||||
|
FAC-28 Seguimiento del Ticket con pago anticipado: HH-8 - TLE
|
||||||
|
```
|
||||||
|
|
||||||
|
Se ven tres bandejas de origen: **HH** (headhunting), **SA** (staff augmentation), **IN**, y **RH**. Dos patrones de regla: `Seguimiento del Ticket Cerrado:` y `Seguimiento del Ticket con pago anticipado:` — el segundo probablemente implique PUE y toca la pregunta pendiente de PUE/PPD.
|
||||||
|
|
||||||
|
**Conclusiones para el modelo de datos:**
|
||||||
|
- **Escuchar FAC basta** para detectar el disparo; el origen se obtiene de `issuelinks` sin salir del proyecto.
|
||||||
|
- No hay evidencia de moves, así que la llave `FAC-nnn` **parece** estable. Aun así conviene **persistir el `issue.id` numérico** (FAC-100 = `17308`, FAC-91 = `14348`): es inmutable por diseño y el costo de guardarlo es cero.
|
||||||
|
- ⚠️ Estos tickets son la vía sin campos estructurados del Bloque 1. Si la automatización debe cubrirlos, **la regla de Automation de Balam tendría que propagar los campos** al crear el ticket en FAC.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Bloque 6 — Escritura (CRUD): qué probar, cómo aprobarlo y dónde 🔴
|
||||||
|
|
||||||
|
**Confirmado por Noé el 7-ago: prueba acompañada sobre un ticket acordado. No habrá proyecto `FACTEST`.** Eso cierra la pregunta pero **deja el riesgo intacto**: se escribirá en producción, y el token tiene permisos para crear, editar, transicionar, comentar, adjuntar e incluso borrar tickets en FAC. El protocolo de abajo pasa de recomendación a requisito, y el sandbox propio (ver recuadro al final del bloque) pasa de opcional a necesario para desarrollar sin tocar Balam.
|
||||||
|
|
||||||
|
**Protocolo obligatorio para cualquier prueba de escritura:**
|
||||||
|
1. Preparar un script idempotente y de una sola operación; sin bucles, sin búsquedas masivas y sin credenciales embebidas.
|
||||||
|
2. Compartir el script y el payload de ejemplo con Pedro/Arturo antes de la sesión; documentar endpoint, ticket destino, efecto esperado y reversión.
|
||||||
|
3. Acordar el ticket de prueba y una ventana de ejecución. Preferir un proyecto sandbox (`FACTEST`) o un ticket creado expresamente para la prueba.
|
||||||
|
4. Ejecutarlo acompañado, registrar el `HTTP status`, el `issue.id`/`key` y verificar el resultado por API.
|
||||||
|
5. No probar `DELETE`; no es una capacidad necesaria para la plataforma y no es reversible en producción.
|
||||||
|
|
||||||
|
**Operaciones a validar, en este orden (de menos a más invasivo):**
|
||||||
|
|
||||||
|
1. **Comentar** — `POST /rest/api/3/issue/{key}/comment` (cuerpo en ADF).
|
||||||
|
⚠️ En JSM hay **comentarios públicos (los ve el cliente) vs internos**: `POST /rest/servicedeskapi/request/{id}/comment` con `public: true|false`. **La plataforma debe escribir SIEMPRE internos.** Publicar por error un comentario visible al cliente es el error más caro y menos reversible de este bloque.
|
||||||
|
2. **Adjuntar** — `POST /rest/api/3/issue/{key}/attachments`.
|
||||||
|
⚠️ Exige el header `X-Atlassian-Token: no-check` y `multipart/form-data`. Sin ese header falla con un error que no explica nada. Es la vía para dejar el PDF/XML como evidencia en el ticket.
|
||||||
|
3. **Transicionar** — `POST /rest/api/3/issue/{key}/transitions` con el `transition.id` del Bloque 3.
|
||||||
|
4. **Crear** — decisión real de diseño: `POST /rest/api/3/issue` (crudo, se salta el request type y el ticket queda "raro" en el portal) vs `POST /rest/servicedeskapi/request` (respeta request type y se ve como uno normal). **Si es JSM, la segunda es la correcta.**
|
||||||
|
5. **Editar** — `PUT /rest/api/3/issue/{key}`.
|
||||||
|
6. **Borrar** — `DELETE /rest/api/3/issue/{key}`. Requiere permiso de admin de proyecto. **Probablemente ni se tenga ni convenga tenerlo**; en producción el borrado no debe ser una capacidad de la plataforma.
|
||||||
|
|
||||||
|
> 🔴 **Dónde probar.**
|
||||||
|
> ~~Pedir a Pedro un proyecto de pruebas `FACTEST`~~ → **descartado por Noé el 7-ago:** será prueba acompañada sobre un ticket. Queda entonces una sola vía de mitigación:
|
||||||
|
>
|
||||||
|
> **Crear un sitio propio y gratuito de Jira Cloud** (plan Free, hasta 10 usuarios) con un proyecto JSM que replique el workflow de FAC. Sirve para desarrollar el cliente, el parser de ADF, la paginación, las aprobaciones y las transiciones **sin tocar nada de Balam** ni depender de que respondan. Es el sandbox que BIND nunca tuvo. Contra producción solo se corre lectura y, al final, la única prueba acompañada.
|
||||||
|
>
|
||||||
|
> Con el `FACTEST` descartado, esto ya no es una alternativa: es el único lugar donde se puede equivocar sin costo.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Bloque 7 — Gobierno y seguridad 🟡
|
||||||
|
|
||||||
|
- ✅ ~~**`propuesta/token-jira.txt` NO está en `.gitignore`**~~ → **Resuelto:** ignorado en [`.gitignore:9`](../.gitignore). Sigue pendiente mover el token a user-secrets / Key Vault y borrarlo de disco. Mismo pendiente que arrastra `bind_token_api.txt`.
|
||||||
|
- **Cuenta de servicio vs cuenta personal de Pedro** (ya planteado en el borrador de correo del 29-jul): hoy todo lo que la plataforma escriba en Jira queda firmado con el nombre de Pedro, y una rotación suya deja la integración muerta. Verificar con `GET /rest/api/3/myself` con qué identidad se está actuando. **Sin respuesta al 10-ago.**
|
||||||
|
- 🔴 **Alcance de permisos:** validado con `GET /rest/api/3/mypermissions` para FAC. El token puede navegar, crear, editar, transicionar, comentar, adjuntar y borrar tickets. Es un alcance excesivo para producción; la integración no debe implementar borrado y conviene migrar a una cuenta de servicio con permisos mínimos.
|
||||||
|
- **Vigencia 27-jul-2027** → dejar registrado el vencimiento y el plan de rotación desde ahora.
|
||||||
|
- **Datos de clientes en adjuntos y descripciones** — misma regla que BIND: nunca a disco del repo ni a documentos compartidos.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Bloque 8 — Preguntas para Pedro / Balam (no se resuelven con la API)
|
||||||
|
|
||||||
|
**Cerradas con el correo del 7-ago:**
|
||||||
|
|
||||||
|
1. ~~**¿Puede crear un proyecto de pruebas `FACTEST`?**~~ → **No.** Prueba acompañada sobre un ticket (Noé, 7-ago).
|
||||||
|
2. ~~**¿Debe observarse `Facturación adicional` (83), `Facturación` (12) u otro?**~~ → **Todos los request types** de la bandeja (Arturo, 4-ago). ⚠️ Con la salvedad del Bloque 1: 10 tickets no tienen request type ni campos.
|
||||||
|
3. ~~**¿Cuál estatus dispara la cotización?**~~ → **`En proceso de facturación`** (Arturo, 4-ago).
|
||||||
|
4. ~~**¿Monto del adjunto o como campo?**~~ → **Campo de Jira.** Ya implementado (`customfield_11556`), omitiendo recurrencia (Arturo, 4-ago).
|
||||||
|
|
||||||
|
**Abiertas:**
|
||||||
|
|
||||||
|
5. 🔴 **¿De dónde salen los CONCEPTOS/partidas?** El acuerdo resolvió el monto pero no los conceptos, y **no existe campo para ellos en el sitio**. ¿Cotización de una sola línea con el `summary` como descripción, o se agrega un campo?
|
||||||
|
6. 🔴 **¿Qué pasa con los tickets creados por Automation for Jira** (10 de 69, sin ningún campo estructurado, incluido `FAC-91` que está vivo)? ¿Se acota la automatización o la regla de Automation propaga los campos?
|
||||||
|
7. 🔴 **ACUNTIA / `FAC-100`:** ¿un ticket con `Monto sin IVA` único debe generar **una** cotización, o sigue siendo el caso de 40–48 facturas?
|
||||||
|
8. **¿Nacional vs extranjero cambia la cotización** o solo el paquete de salida (PDF+XML vs PDF)? Hay dos estatus de validación distintos y una aprobación por cada rama.
|
||||||
|
9. **¿Quién puede crear webhooks o reglas de Automation** apuntando a una URL nuestra, y bajo qué proceso? Gana urgencia: el estatus disparador dura minutos (Bloque 3).
|
||||||
|
10. **PUE vs PPD** — la pregunta que no se ha alcanzado a hacer en cuatro sesiones ([REGISTRO #52](../bitacora/REGISTRO.md)). Los tickets `Seguimiento del Ticket con pago anticipado:` sugieren que sí hay casos PUE reales.
|
||||||
|
11. **Cuenta de servicio** en lugar de la cuenta personal de Pedro, y fecha para la prueba acompañada.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Orden de ataque sugerido
|
||||||
|
|
||||||
|
| # | Acción | Bloqueado por |
|
||||||
|
|---|---|---|
|
||||||
|
| 1 | ✅ Blindar el token (`.gitignore`) — falta secrets/Key Vault | — |
|
||||||
|
| 2 | ✅ Mapeo de campos y contratos de request type verificados (10-ago) | — |
|
||||||
|
| 3 | Implementar lectura con `/search/jql`, cursor y ventana de solape | — |
|
||||||
|
| 4 | **Detección por changelog del cruce a `En proceso de facturación`** | — (ya desbloqueado por el acuerdo del 4-ago) |
|
||||||
|
| 5 | Parser de ADF para `Nombre del Cliente` + match contra `Clients` de BIND | ⚠️ Sin RFC; definir tolerancia del match |
|
||||||
|
| 6 | Armado de la cotización BIND | 🔴 Definición de conceptos/partidas (pregunta 5) |
|
||||||
|
| 7 | Levantar un sandbox Jira propio (plan Free) | — (ya no depende de Balam) |
|
||||||
|
| 8 | Preparar y compartir el script de escritura mínimo para revisión | — |
|
||||||
|
| 9 | Ejecutar una única prueba de escritura acompañada | Fecha de Balam + script revisado + ticket acordado |
|
||||||
|
|
||||||
|
**Lo que puede avanzar ya:** el cliente de lectura, manejo de ADF, paginación por cursor, mapeo de campos, detección incremental por changelog y el sandbox propio. **Lo único que sigue bloqueado es el armado final de la cotización**, por la definición de conceptos. La escritura queda fuera del flujo automático hasta la sesión acompañada.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Briefing corto para desarrollo / revisión técnica
|
||||||
|
|
||||||
|
> Contexto validado: Jira Cloud JSM en `balam-jsm-temp.atlassian.net`; proyecto FAC company-managed (`projectId: 10034`, `serviceDeskId: 35`). La autenticación directa con Basic `email:token` funciona. El token tiene permisos de escritura amplios, pero **no se debe ejecutar ningún POST, PUT o DELETE fuera de una prueba acompañada y aprobada**.
|
||||||
|
>
|
||||||
|
> Para la capa de lectura, usar `GET /rest/api/3/search/jql`, paginar por `nextPageToken` y aplicar una ventana de solape al checkpoint de `updated`. No usar `/rest/api/3/search`: responde 410.
|
||||||
|
>
|
||||||
|
> Para JSM, usar `/rest/servicedeskapi` al consultar request types y su contrato de campos. FAC publica 4 request types: Bajas (84), Facturación adicional (83), Automatización (389) y Facturación (12); los cuatro contratos están verificados. **Facturación adicional (83) es el único con datos de facturación** e incluye `Monto sin IVA` (`customfield_11556`, number). **No existe campo de conceptos/partidas en el sitio.**
|
||||||
|
>
|
||||||
|
> **El disparador acordado es el cruce a `En proceso de facturación`, y dura minutos** (FAC-100: 2m44s). La detección debe leer el changelog y buscar el cruce dentro de la ventana; consultar el estatus actual no sirve. El `issuetype` es `admon`. El workflow tiene **aprobaciones de JSM** con Araceli como aprobadora en las ramas de validación nacional/extranjera: la plataforma nunca aprueba.
|
||||||
|
>
|
||||||
|
> **10 de 69 tickets nacen de `Automation for Jira`** desde HH/SA/IN/RH, sin request type y sin ningún campo estructurado; los datos van en el `summary` y la `description` (ADF), con el origen en `issuelinks`. Persistir el `issue.id` numérico además de la llave.
|
||||||
|
>
|
||||||
|
> Pendientes técnicos: parser de ADF para `Nombre del Cliente` (no trae RFC), armado de la cotización cuando se defina el tema de conceptos, y diseñar un script de escritura de una sola operación para revisión previa. El script debe ser idempotente, no incluir credenciales y ejecutarse únicamente en el sandbox propio o acompañado en el ticket de prueba acordado. **No habrá `FACTEST`.**
|
||||||
@@ -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).
|
||||||
@@ -1,37 +1,43 @@
|
|||||||
# Plan de actividades — Etapas 0 a 3
|
# Plan de actividades — Etapas 0 a 3
|
||||||
|
|
||||||
**Proyecto:** Plataforma de Automatización Financiera · Balam
|
**Proyecto:** Plataforma de Automatización Financiera · Balam
|
||||||
> Fechas tentativas — el arranque depende de la entrega de accesos por Balam; se confirman en el kickoff. Las etapas 2 y 3 son estimadas y se afinan al cerrar el Discovery (Etapa 0). Total ~6–7 semanas (6-jul → 21-ago).
|
> 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.
|
||||||
|
|
||||||
## Etapa 0
|
## Etapa 0
|
||||||
|
|
||||||
| Actividad | Inicio | Fin | Responsable | Apoyo de Balam |
|
| Actividad | Inicio | Fin | Responsable | Apoyo de Balam |
|
||||||
|---|---|---|---|---|
|
|---|---|---|---|---|
|
||||||
| Sesión de arranque (kickoff) con el Ing. Noé | 01/07/2026 | 01/07/2026 | Johann | ✅ Realizada (1-jul, 7am) — Noé, Pedro, Erika |
|
| Sesión de arranque (kickoff) con el Ing. Noé | 01/07/2026 | 01/07/2026 | Johann | ✅ Realizada (1-jul, 7am) — Noé, Pedro, Erika |
|
||||||
| Sesión de Discovery: proceso actual de facturación y cobranza | 06/07/2026 | 07/07/2026 | Johann | Sí — **Araceli + Arturo** (Erika agenda) |
|
| Sesión de Discovery: facturación, envío y cobranza | 06/07/2026 | 07/07/2026 | Johann | ✅ Realizada — Araceli + Arturo |
|
||||||
| Entrega y validación de accesos (token BIND de Arturo, Azure, repo) | 06/07/2026 | 07/07/2026 | Balam / Johann | Sí — token de Arturo, Azure (Pedro+Noé), repo. Manual de marca ✅ recibido (1-jul) |
|
| 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 | 07/07/2026 | 08/07/2026 | Johann | Apoyo: token de Arturo (solo consulta; probar primero) |
|
| 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 |
|
||||||
| Configuración de infraestructura en Azure (+ staging) | 08/07/2026 | 09/07/2026 | Johann | Apoyo: accesos de Azure (Pedro+Noé) |
|
| Prototipo visual navegable | 08/07/2026 | 10/07/2026 | Johann | ✅ Entregado por correo el 10-jul; validación pendiente |
|
||||||
| Repositorio + CI/CD + gestión de secretos | 08/07/2026 | 09/07/2026 | Johann | Sí — **Balam crea el repo** (GitHub privado) |
|
| Repositorio privado GitHub | 13/07/2026 | 16/07/2026 | Balam / Johann | Invitación recibida y acceso confirmado; falta validar permiso de escritura |
|
||||||
| Prototipo visual navegable (5–6 pantallas) | 08/07/2026 | 10/07/2026 | Johann | Marca: `../marca/Marca-Balam.md` (✅ recibida) |
|
| 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 |
|
||||||
| Sesión de validación del prototipo | 10/07/2026 | 10/07/2026 | Johann | Sí — Pedro, Araceli y Arturo |
|
| 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 | 10/07/2026 | Johann | — |
|
| Documento de hallazgos + ADRs + plan refinado | 10/07/2026 | 23/07/2026 | Johann | Borrador creado; cerrar tras las sesiones del 22–23 jul |
|
||||||
|
| Sesión de validación del prototipo | 23/07/2026 | 23/07/2026 | Johann | Confirmada a las 7:00 pm con CEO/equipo Balam |
|
||||||
|
|
||||||
**Entregable Etapa 0 (18–22 h): prototipo navegable + infraestructura Azure + repo/CI-CD + documento de hallazgos.**
|
**Entregable Etapa 0 (18–22 h): prototipo navegable + infraestructura Azure + repo/CI-CD + documento de hallazgos.**
|
||||||
|
|
||||||
## Etapa 1
|
## Etapa 1
|
||||||
|
|
||||||
|
> Plan detallado de ejecución: [Plan-Etapa1.md](Plan-Etapa1.md)
|
||||||
|
|
||||||
| Actividad | Inicio | Fin | Responsable | Apoyo de Balam |
|
| Actividad | Inicio | Fin | Responsable | Apoyo de Balam |
|
||||||
|---|---|---|---|---|
|
|---|---|---|---|---|
|
||||||
| Backend base: autenticación, roles y bitácora de auditoría | 13/07/2026 | 15/07/2026 | Johann | — |
|
| 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 |
|
||||||
| Arquitectura multi-tenant (tenant_id + RLS) | 14/07/2026 | 15/07/2026 | Johann | — |
|
| Arquitectura multi-tenant (tenant_id + RLS) | 14/07/2026 | 17/07/2026 | Johann | — |
|
||||||
| Sesión de reglas de negocio con Arturo | 13/07/2026 | 13/07/2026 | Johann | Sí — Arturo |
|
| Sesión de reglas y dudas de Etapa 1 | 22/07/2026 | 22/07/2026 | Johann | Confirmada con Arturo + CEO |
|
||||||
|
| Definición de alcance Jira → cotización BIND | 22/07/2026 | 23/07/2026 | Johann / Balam | Requiere confirmar si la integración automática entra al MVP antes de pedir token técnico |
|
||||||
| Cliente de la API de BIND (reintentos, OData, errores) | 15/07/2026 | 17/07/2026 | Johann | — |
|
| Cliente de la API de BIND (reintentos, OData, errores) | 15/07/2026 | 17/07/2026 | Johann | — |
|
||||||
| Capa de escritura controlada (cotización→factura) | 17/07/2026 | 22/07/2026 | Johann | — |
|
| Capa de escritura controlada (cotización→factura) | 17/07/2026 | 22/07/2026 | Johann | — |
|
||||||
| Sincronización de clientes | 20/07/2026 | 21/07/2026 | Johann | — |
|
| Sincronización de clientes | 20/07/2026 | 21/07/2026 | Johann | — |
|
||||||
| Sincronización de facturas y cotizaciones | 21/07/2026 | 23/07/2026 | Johann | — |
|
| Sincronización de facturas y cotizaciones | 21/07/2026 | 23/07/2026 | Johann | — |
|
||||||
| Modelo de facturas + migraciones + datos de prueba | 23/07/2026 | 24/07/2026 | Johann | — |
|
| Modelo de facturas + migraciones + datos de prueba | 23/07/2026 | 24/07/2026 | Johann | — |
|
||||||
| Demostración semanal (viernes) + reporte de horas | 10/07/2026 | 24/07/2026 | Johann | Sí — Erika/Pedro |
|
| Demostración semanal (viernes) + reporte de horas | 10/07/2026 | 24/07/2026 | Johann | Sí — Erika/Pedro; control local en `Seguimiento-horas.csv` mientras Jira se habilita |
|
||||||
|
|
||||||
**Entregable Etapa 1 (32–39 h): plataforma base que sincroniza clientes y facturas de BIND, con capa de escritura controlada.**
|
**Entregable Etapa 1 (32–39 h): plataforma base que sincroniza clientes y facturas de BIND, con capa de escritura controlada.**
|
||||||
|
|
||||||
@@ -41,7 +47,7 @@
|
|||||||
|---|---|---|---|---|
|
|---|---|---|---|---|
|
||||||
| Implementación productiva de las pantallas (Angular Material) + integración con el backend | 27/07/2026 | 31/07/2026 | Johann | — |
|
| Implementación productiva de las pantallas (Angular Material) + integración con el backend | 27/07/2026 | 31/07/2026 | Johann | — |
|
||||||
| Listado de facturas (filtros, búsqueda, paginación) + detalle con descarga PDF/XML | 28/07/2026 | 31/07/2026 | Johann | — |
|
| Listado de facturas (filtros, búsqueda, paginación) + detalle con descarga PDF/XML | 28/07/2026 | 31/07/2026 | Johann | — |
|
||||||
| Catálogo de clientes + lista blanca configurable (ACUNTIA + Top 3) | 03/08/2026 | 04/08/2026 | Johann | — |
|
| Catálogo de clientes + reglas de cobranza | 03/08/2026 | 04/08/2026 | Johann | Lista blanca actualmente vacía: recordatorios para todos |
|
||||||
| Creación de cotizaciones + conversión a factura vía API de BIND | 31/07/2026 | 04/08/2026 | Johann | — |
|
| Creación de cotizaciones + conversión a factura vía API de BIND | 31/07/2026 | 04/08/2026 | Johann | — |
|
||||||
| Emisión multimoneda MXN/USD + flujo con confirmación humana y dry-run | 04/08/2026 | 06/08/2026 | Johann | — |
|
| Emisión multimoneda MXN/USD + flujo con confirmación humana y dry-run | 04/08/2026 | 06/08/2026 | Johann | — |
|
||||||
| Tipo de cambio del DOF (proceso programado) | 05/08/2026 | 05/08/2026 | Johann | — |
|
| Tipo de cambio del DOF (proceso programado) | 05/08/2026 | 05/08/2026 | Johann | — |
|
||||||
@@ -57,7 +63,7 @@
|
|||||||
|---|---|---|---|---|
|
|---|---|---|---|---|
|
||||||
| Sesión: alcance del tablero vs el Power BI existente (con Pedro) | 10/08/2026 | 10/08/2026 | Johann | Sí — Pedro |
|
| Sesión: alcance del tablero vs el Power BI existente (con Pedro) | 10/08/2026 | 10/08/2026 | Johann | Sí — Pedro |
|
||||||
| Módulo de cobranza: antigüedad de cartera (30/60/90) + por vencer y vencidas | 10/08/2026 | 12/08/2026 | Johann | — |
|
| Módulo de cobranza: antigüedad de cartera (30/60/90) + por vencer y vencidas | 10/08/2026 | 12/08/2026 | Johann | — |
|
||||||
| Alertas internas configurables para Finanzas + aplicación de lista blanca | 12/08/2026 | 13/08/2026 | Johann | — |
|
| Alertas internas configurables para Finanzas | 12/08/2026 | 13/08/2026 | Johann | Correos automáticos al cliente requieren confirmar ampliación de alcance |
|
||||||
| Tablero directivo de cuentas por cobrar | 13/08/2026 | 15/08/2026 | Johann | — |
|
| Tablero directivo de cuentas por cobrar | 13/08/2026 | 15/08/2026 | Johann | — |
|
||||||
| Reportes operativos configurables (CSV/XLSX/PDF) | 17/08/2026 | 18/08/2026 | Johann | — |
|
| Reportes operativos configurables (CSV/XLSX/PDF) | 17/08/2026 | 18/08/2026 | Johann | — |
|
||||||
| Endurecimiento de seguridad + respaldos + plan de recuperación | 18/08/2026 | 19/08/2026 | Johann | — |
|
| Endurecimiento de seguridad + respaldos + plan de recuperación | 18/08/2026 | 19/08/2026 | Johann | — |
|
||||||
|
|||||||
@@ -0,0 +1,36 @@
|
|||||||
|
fecha,semana,etapa,actividad_id,actividad,horas,estado_hora,fuente_evidencia,facturable,notas
|
||||||
|
2026-07-01,2026-W27,0,E0-01,Sesión de arranque con Noé,1.00,confirmada,bitacora/REGISTRO.md#22,si,Sesión calendarizada de 7:00 a 8:00 am
|
||||||
|
2026-07-01,2026-W27,0,E0-01,Preparación de plan kickoff y materiales,1.00,validada,planeacion/Kickoff-2026-07-01.md,si,validada por Johann 27-jul
|
||||||
|
2026-07-06,2026-W28,0,E0-02,Discovery de facturación y envío,1.08,confirmada,bitacora/REGISTRO.md#27,si,Duración documentada: aproximadamente 1 h 5 min
|
||||||
|
2026-07-06,2026-W28,0,E0-02,Preparación y análisis de Discovery de facturación,1.00,validada,bitacora/REGISTRO.md#27,si,"Completa 2.08 h con la sesión; ajustada a la baja por Johann 27-jul"
|
||||||
|
2026-07-06,2026-W28,0,E0-03,Coordinación y validación de accesos BIND y marca,1.00,validada,bitacora/REGISTRO.md#28-31,si,Recepción documentación y salvaguardas; validada por Johann 27-jul
|
||||||
|
2026-07-06,2026-W28,0,E0-04,Validación técnica de API BIND,4.50,validada,bind-api-sandbox/VALIDACION-API.md,si,117 GET análisis y reporte técnico; validada por Johann 27-jul
|
||||||
|
2026-07-07,2026-W28,0,E0-02,Discovery de cobranza,1.00,confirmada,bitacora/REGISTRO.md#35,si,Sesión calendarizada de 7:00 a 8:00 am; sin grabación
|
||||||
|
2026-07-07,2026-W28,0,E0-02,Preparación y documentación de Discovery de cobranza,0.75,validada,fuentes/2026-07-07 - Notas - Proceso actual de cobranza (sin grabacion).md,si,"Sin grabación; notas escritas a mano; ajustada a la baja por Johann 27-jul"
|
||||||
|
2026-07-08,2026-W28,0,E0-05,Diseño y construcción del prototipo,3.00,validada,prototipo/Prototipo-Etapa0.html,si,Primera mitad del esfuerzo reconstruido; validada por Johann 27-jul
|
||||||
|
2026-07-09,2026-W28,0,E0-05,Ajustes de cobranza navegación y prototipo,3.00,validada,prototipo/Prototipo-Etapa0.html,si,Segunda mitad del esfuerzo reconstruido; validada por Johann 27-jul
|
||||||
|
2026-07-10,2026-W28,0,E0-05,"Pulido final del prototipo (bandeja, modal Edo. cuenta, login, capturas)",1.50,validada,prototipo/Prototipo-Etapa0.html,si,"6 commits 00:44-01:52 (b358570..8da11ea) + 9 capturas + logo oficial; validada por Johann 27-jul"
|
||||||
|
2026-07-10,2026-W28,0,E0-06,Entregable de cierre Etapa 0: PDF hallazgos plan refinado y correo,1.50,validada,prototipo/Correo-prototipo-Etapa0.md,si,PDF generado 10:43; hallazgos y plan refinado; validada por Johann 27-jul
|
||||||
|
2026-07-13,2026-W29,1,E1-01,Backend base autenticación roles y auditoría,2.00,validada,planeacion/Plan-actividades.md,si,validada por Johann 27-jul
|
||||||
|
2026-07-13,2026-W29,1,E1-02,Coordinación del repositorio GitHub,0.50,validada,bitacora/REGISTRO.md#43,si,Seguimiento de creación y acceso; validada por Johann 27-jul
|
||||||
|
2026-07-14,2026-W29,1,E1-01,Backend base autenticación roles y auditoría,1.50,validada,planeacion/Plan-actividades.md,si,Trabajo local; completa 3.50 h; validada por Johann 27-jul
|
||||||
|
2026-07-14,2026-W29,1,E1-03,Arquitectura multi-tenant,1.00,validada,planeacion/Plan-actividades.md,si,Diseño inicial tenant_id y RLS; validada por Johann 27-jul
|
||||||
|
2026-07-15,2026-W29,1,E1-03,Arquitectura multi-tenant,0.50,validada,planeacion/Plan-actividades.md,si,Completa 1.50 h; validada por Johann 27-jul
|
||||||
|
2026-07-15,2026-W29,1,E1-04,Cliente productivo de API BIND,2.00,validada,bind-api-sandbox/VALIDACION-API.md,si,Inicio de reintentos OData y manejo de errores; validada por Johann 27-jul
|
||||||
|
2026-07-15,2026-W29,1,E1-05,Seguimiento ajuste de calendario y corte de horas,0.50,validada,bitacora/REGISTRO.md#41-45,si,Actualización de plan y control; validada por Johann 27-jul
|
||||||
|
2026-07-16,2026-W29,1,E1-06,Scaffold backend .NET 10 + frontend Angular 21 y push al repo de Balam,2.00,validada,bitacora/REGISTRO.md#46,si,Commits 220942a..1f7f098; incluye CLAUDE.md del repo; validada por Johann 27-jul
|
||||||
|
2026-07-16,2026-W29,1,E1-05,Corte de avance Etapa 1 para Erika (Excel + mensaje),1.00,validada,planeacion/Avance-Etapa1-2026-07-16.md,si,Enviado 8:40 pm por WhatsApp con acuse; validada por Johann 27-jul
|
||||||
|
2026-07-19,2026-W29,1,E1-06,"Preparación de entorno local (clon del repo, SDK .NET 10, secrets, app corriendo)",1.00,validada,bitacora/REGISTRO.md#47,si,API y frontend verificados en local; 13/13 pruebas verdes; validada por Johann 27-jul
|
||||||
|
2026-07-19,2026-W29,1,E1-08,"Diseño del plan de ejecución de Etapa 1 (bloques B0-B10, ADRs, orden)",2.00,validada,planeacion/Plan-Etapa1.md,si,Arquitectura y decisiones previas a las sesiones de desarrollo; validada por Johann 27-jul
|
||||||
|
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
|
||||||
|
2026-07-21,2026-W30,1,E1-05,Preparación de la agenda de la sesión de reglas de Etapa 1,0.75,validada,planeacion/Sesion-Etapa1-2026-07-22.md,si,Agenda y preguntas para la sesión del 22-jul; validada por Johann 27-jul
|
||||||
|
2026-07-22,2026-W30,1,E1-05,"Sesión de reglas Etapa 1 con Noé, Arturo y Araceli (alta de clientes, fee, flujo Jira)",0.80,confirmada,bitacora/REGISTRO.md#54,si,Duración real ~48 min según transcript (7:00–7:48 am)
|
||||||
|
2026-07-23,2026-W30,0,E0-07,Preparación del guion de validación del prototipo,0.45,validada,planeacion/Guion-validacion-prototipo-2026-07-23.md,si,"Guion del recorrido de la demo del 24-jul; ajustada a la baja por Johann 27-jul"
|
||||||
|
2026-07-24,2026-W30,0,E0-07,"Sesión de validación del prototipo Etapa 0 con Noé, Arturo y Araceli — APROBADO",1.05,confirmada,bitacora/REGISTRO.md#55,si,"Duración ~1 h 4 min según transcript; Etapa 0 validada, queda 1 definición de proceso (flujo Jira)"
|
||||||
|
2026-07-27,2026-W31,1,E1-07,"Sesión de API de Jira con Pedro (token PAF, bandeja FAC)",0.20,confirmada,bitacora/REGISTRO.md#56,si,~12 min; token PAF comprometido (1 año) por correo; integración inicia tras definición del flujo 28-jul
|
||||||
|
@@ -0,0 +1,82 @@
|
|||||||
|
# Seguimiento de horas
|
||||||
|
|
||||||
|
Fuente local de horas efectivamente trabajadas para el proyecto Balam. Se mantiene mientras Johann obtiene acceso al flujo de Jira; después, Jira será el registro operativo y este archivo/CSV servirá para conciliación y facturación.
|
||||||
|
|
||||||
|
**Tarifa contractual:** $600 MXN/h + IVA
|
||||||
|
**Cadencia:** corte semanal; factura los viernes; pago a 30 días
|
||||||
|
**Datos tabulares:** [`Seguimiento-horas.csv`](Seguimiento-horas.csv)
|
||||||
|
**Última actualización:** 2026-07-27
|
||||||
|
|
||||||
|
## Reglas de captura
|
||||||
|
|
||||||
|
1. Registrar el tiempo el mismo día, en incrementos de 0.25 h.
|
||||||
|
2. Separar sesiones, desarrollo, análisis, documentación y gestión técnica.
|
||||||
|
3. Vincular cada registro con una actividad del plan y una evidencia cuando exista.
|
||||||
|
4. No convertir porcentajes de avance en horas: el avance mide entregable; las horas miden esfuerzo real.
|
||||||
|
5. No completar retrospectivamente una duración sin respaldo. Si solo se conoce la actividad, dejarla en la lista de reconstrucción hasta que Johann confirme el tiempo.
|
||||||
|
6. Al habilitarse Jira, conciliar ambos registros y evitar duplicados.
|
||||||
|
|
||||||
|
## Corte al 27-jul
|
||||||
|
|
||||||
|
| Etapa | Horas al corte | Rango estimado de la etapa | Estado |
|
||||||
|
|---|---:|---:|---|
|
||||||
|
| Etapa 0 | **21.83 h** | 18–22 h | Cerrada y **validada por Balam el 24-jul**; dentro del rango |
|
||||||
|
| Etapa 1 | **19.75 h** | 32–39 h | En curso; 6 de 8 entregables terminados |
|
||||||
|
| **Total** | **41.58 h** | | |
|
||||||
|
|
||||||
|
**Estado de validación de las horas (27-jul):**
|
||||||
|
|
||||||
|
| Estado | Horas | Significado |
|
||||||
|
|---|---:|---|
|
||||||
|
| `confirmada` | 9.13 h | Duración documentada (transcripts, sesiones calendarizadas, tiempo de sesión) |
|
||||||
|
| `validada` | 32.45 h | Reconstruida por actividad y **confirmada por Johann el 27-jul** contra evidencia fechada |
|
||||||
|
| **Total** | **41.58 h** | Sin horas pendientes de validar |
|
||||||
|
|
||||||
|
## Distribución al 27-jul
|
||||||
|
|
||||||
|
| Etapa | Actividad | ID | Horas |
|
||||||
|
|---|---|---|---:|
|
||||||
|
| 0 | Kickoff y preparación | E0-01 | 2.00 |
|
||||||
|
| 0 | Discovery de facturación, envío y cobranza | E0-02 | 3.83 |
|
||||||
|
| 0 | Accesos y salvaguardas | E0-03 | 1.00 |
|
||||||
|
| 0 | Validación técnica de la API de BIND | E0-04 | 4.50 |
|
||||||
|
| 0 | Prototipo navegable (incl. pulido final del 10-jul) | E0-05 | 7.50 |
|
||||||
|
| 0 | Entregable de cierre: PDF, hallazgos, plan y correo | E0-06 | 1.50 |
|
||||||
|
| 0 | Validación del prototipo con Balam (guion + sesión) | E0-07 | 1.50 |
|
||||||
|
| 1 | Backend base, auth, roles, bitácora y syncs (B0–B4) | E1-01 | 6.50 |
|
||||||
|
| 1 | Coordinación del repositorio | E1-02 | 0.50 |
|
||||||
|
| 1 | Arquitectura multi-tenant y RLS (B6) | E1-03 | 2.00 |
|
||||||
|
| 1 | Cliente productivo de la API de BIND | E1-04 | 2.00 |
|
||||||
|
| 1 | Gestión, cortes de avance y sesión de reglas | E1-05 | 3.30 |
|
||||||
|
| 1 | Entorno local, scaffold y Scalar | E1-06 | 3.25 |
|
||||||
|
| 1 | Sesión de API de Jira | E1-07 | 0.20 |
|
||||||
|
| 1 | Diseño del plan de ejecución de Etapa 1 | E1-08 | 2.00 |
|
||||||
|
| | **Total** | | **41.58** |
|
||||||
|
|
||||||
|
> **Ajustes a la baja aplicados por Johann el 27-jul:** el análisis del Discovery de facturación bajó de 1.92 a 1.00 h, la documentación del de cobranza de 1.50 a 0.75 h, y el guion de validación de 0.75 a 0.45 h. Las duraciones de sesión documentadas en transcript **no se tocaron**. Con esto Etapa 0 vuelve a quedar dentro del rango contractual de 18–22 h.
|
||||||
|
|
||||||
|
### Nota sobre las sesiones asistidas por IA
|
||||||
|
|
||||||
|
Los bloques B0–B6 de Etapa 1 (auth, RLS, cliente de BIND, sincronización de 1,494 facturas de producción) se ejecutaron en **3.50 h de tiempo de reloj** contra una estimación manual de 4–5.5 h *por bloque*. Se registra el tiempo efectivamente trabajado, conforme al contrato. El trabajo intelectual que hizo posible esa velocidad —diseño de arquitectura, decisión de ADRs y plan de ejecución— se registra por separado en **E1-08 (2.00 h)**.
|
||||||
|
|
||||||
|
Consecuencia a vigilar: **Etapa 1 cerrará por debajo del rango de 32–39 h.** Conviene reportar avance por entregable (6 de 8) y no solo por horas, para que la eficiencia no se lea como alcance faltante.
|
||||||
|
|
||||||
|
### Nota sobre el mapeo CSV → Excel
|
||||||
|
|
||||||
|
El CSV agrupa por **actividad contractual**; el Excel desglosa por **tarea de proyecto**, y no mapean 1:1. El reparto vive en `HORAS_POR_FILA` dentro de `generar_avance_2026_07_27.py`, con la trazabilidad comentada línea por línea, y el script **aborta** si la suma del detalle no cuadra con el CSV.
|
||||||
|
|
||||||
|
Dos casos que conviene recordar:
|
||||||
|
- **E1-06 (entorno, scaffold, Scalar — 3.25 h)** no tiene fila propia: se carga a "Backend base", que se renombró y amplió su rango a 8–9 h.
|
||||||
|
- **E1-08 (diseño del plan — 2.00 h)** se reparte en dos: **1.00 h** de diseño general va a "Arquitectura multi-tenant y diseño de la solución" (rango 3–4 h) y **1.00 h** del análisis de B7 —pipeline `Draft→DryRun→Confirm`, flags, regla PPD/PUE, ADR-001— va a la fila del **entregable 4 (capa de escritura)**, para que muestre el trabajo hecho aunque la implementación siga en pausa. La entidad `WriteOperation` y la policy `PuedeOperarEscritura` **no** se contabilizan ahí: se hicieron en B1/B2 y ya están en "Backend base" (evita doble conteo).
|
||||||
|
- La fila de **seguimiento semanal se queda solo con gestión y cortes de avance (1.75 h)**. Antes acumulaba 7.00 h de trabajo técnico bajo un nombre que no lo describía, lo que se leía como sobrecosto en una actividad trivial.
|
||||||
|
- La sesión **B4 (1.00 h)** se reparte entre "Sincronización de facturas" (0.50 h) y "Modelo de facturas" (0.50 h): produjo ambas cosas —el sync y el `InvoiceStatusMapper` con sus 13 pruebas más la migración `DetalleSyncPendiente`—, y antes el modelo aparecía al 85 % con 0 h.
|
||||||
|
|
||||||
|
**Criterio general:** ninguna fila debe mostrar avance con 0 h trabajadas. Las únicas excepciones legítimas son Azure y CI/CD, que están en 0 % y 0 h porque dependen de accesos del lado de Balam.
|
||||||
|
|
||||||
|
## Corte comercial
|
||||||
|
|
||||||
|
- La propuesta v1.2 contempla una **facturación inicial de 30 h ($18,000 + IVA)** que cubre Etapa 0 e inicio de Etapa 1.
|
||||||
|
- Al 27-jul hay **41.58 h trabajadas**, es decir **11.58 h por encima** de esa facturación inicial. A tarifa de $600/h son **$6,948 + IVA** aún no facturados.
|
||||||
|
- Todas las horas están validadas (ninguna pendiente de confirmar), por lo que el desglose ya es presentable para facturación.
|
||||||
|
- Pendiente: conciliar contra Jira cuando Pedro lo habilite, y confirmar con Balam si la segunda factura va por el excedente o por corte de etapa.
|
||||||
|
- La emisión de la factura inicial sigue pendiente de confirmación de Balam al correo del 2-jul.
|
||||||
@@ -0,0 +1,104 @@
|
|||||||
|
# Sesión de reglas y definiciones — Etapa 1
|
||||||
|
|
||||||
|
**Fecha:** miércoles 22-jul-2026
|
||||||
|
**Participantes esperados:** CEO, Arturo, Erika, Johann
|
||||||
|
**Objetivo:** cerrar decisiones que afectan el modelo de datos y el alcance técnico antes de implementar escritura e integración Jira→BIND.
|
||||||
|
|
||||||
|
## Resultado esperado
|
||||||
|
|
||||||
|
Al terminar deben quedar definidos:
|
||||||
|
|
||||||
|
1. Flujo de alta de un cliente nuevo.
|
||||||
|
2. Tratamiento de diferencias por comisión/fee.
|
||||||
|
3. Fuente y reglas de particularidades de envío.
|
||||||
|
4. Alcance de la automatización Jira→cotización BIND.
|
||||||
|
5. Datos y autorización necesarios para comenzar escritura controlada.
|
||||||
|
|
||||||
|
## Agenda propuesta (45–60 min)
|
||||||
|
|
||||||
|
### 1. Alta de cliente nuevo — 10 min
|
||||||
|
|
||||||
|
- ¿Quién crea actualmente al cliente en BIND?
|
||||||
|
- ¿Qué documentos y validaciones son obligatorios?
|
||||||
|
- ¿Qué estatus de Jira indica que el alta está autorizada?
|
||||||
|
- ¿La plataforma solo debe detectar que falta el cliente o también crearlo?
|
||||||
|
- ¿Qué ocurre si el RFC ya existe o los datos fiscales no coinciden?
|
||||||
|
|
||||||
|
**Decisión a registrar:** alcance exacto del MVP y responsable de cada paso.
|
||||||
|
|
||||||
|
### 2. Comisión/fee en cobranza — 10 min
|
||||||
|
|
||||||
|
- Cuando el pago recibido es menor por una comisión, ¿cómo se registra en BIND?
|
||||||
|
- ¿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.
|
||||||
|
|
||||||
|
### 3. Particularidades de envío — 10 min
|
||||||
|
|
||||||
|
- Confirmar destinatarios, adjuntos, nomenclatura del asunto y cuerpo por cliente.
|
||||||
|
- Definir quién mantiene estos datos y con qué frecuencia cambian.
|
||||||
|
- Confirmar si el Excel en preparación será la fuente inicial para cargarlos.
|
||||||
|
|
||||||
|
**Decisión a registrar:** estructura mínima del catálogo y responsable de mantenimiento.
|
||||||
|
|
||||||
|
### 4. Jira → cotización BIND — 15 min
|
||||||
|
|
||||||
|
Explicar que Jira es parte confirmada del proceso, pero la creación automática de cotizaciones no estaba incluida explícitamente en el alcance inicial.
|
||||||
|
|
||||||
|
- ¿La expectativa del MVP es solo conservar el folio de Jira o crear automáticamente la cotización en BIND?
|
||||||
|
- ¿Qué estatus o aprobación dispara la acción?
|
||||||
|
- ¿Qué tipos de ticket aplican: adicional, baja, headhunting, staff augmentation?
|
||||||
|
- ¿Qué campos son obligatorios y qué ocurre si falta alguno?
|
||||||
|
- ¿Debe haber una previsualización/confirmación humana antes de escribir en BIND?
|
||||||
|
|
||||||
|
Si Balam confirma la automatización, solicitar posteriormente:
|
||||||
|
|
||||||
|
- Cuenta técnica de Jira con acceso mínimo al proyecto.
|
||||||
|
- API token/OAuth y permiso para webhook o automatización.
|
||||||
|
- 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
|
||||||
|
|
||||||
|
- Confirmar si ya está listo el repositorio GitHub y el acceso de Johann.
|
||||||
|
- Confirmar fecha y responsable del acceso Azure.
|
||||||
|
- Confirmar cuándo Pedro habilitará el reporte de horas en Jira.
|
||||||
|
- Revisar las preguntas técnicas pendientes de BIND: pagos/REP, `CFDIUse`, XML, webhooks y token de Power BI.
|
||||||
|
|
||||||
|
## Límites que conviene explicitar
|
||||||
|
|
||||||
|
- Recordatorios automáticos enviados al cliente: Anexo B; el MVP contempla alertas internas.
|
||||||
|
- Multiempresa: fuera del MVP; cada empresa requiere token/cuenta BIND.
|
||||||
|
- Escritura en producción: solo con autorización, dry-run, confirmación humana y feature flag.
|
||||||
|
- Integración Jira automática: pendiente de confirmación de alcance.
|
||||||
|
|
||||||
|
## Minuta — sesión realizada (22-jul, 7:00 am, ~48 min)
|
||||||
|
|
||||||
|
> Participaron: Noe (sala), Arturo, Araceli (desde ~min 21), Johann. Erika no estuvo. Transcripción: `../fuentes/2026-07-22 - Flujo para dar de alta clientes nuevos,_ Transcript.txt`. Detalle completo en [REGISTRO #54](../bitacora/REGISTRO.md).
|
||||||
|
|
||||||
|
| Tema | Decisión | Responsable | Fecha |
|
||||||
|
|---|---|---|---|
|
||||||
|
| Alta de cliente | ASIS mapeado: mismo flujo clientes/proveedores, 2 pestañas (Detalle + Direcciones), datos de constancia fiscal + estado de cuenta; crédito viene de negociación comercial; extranjeros con RFC genérico y CFDI "sin efectos fiscales" (solo PDF, sin XML). **Pendiente decidir si la plataforma solo detecta o también crea el cliente.** | Arturo demostró; decisión MVP abierta | 22-jul |
|
||||||
|
| Fee/comisión | El fee bancario lo registra y deduce **el despacho contable**, no Balam. Solo aplica en pagos internacionales que mueven dinero a México; nacionales cuadran a centavos. La plataforma solo debe **identificar la diferencia como fee** (visible en el estado de cuenta del cliente). | Despacho (asiento) · Plataforma (detección) | 22-jul |
|
||||||
|
| Particularidades de envío | **No se tocó.** Excel sigue pendiente de Ara/Arturo. | Ara + Arturo | — |
|
||||||
|
| Jira→BIND | Noe confirmó que **Jira ya es parte del proceso** (evolucionó desde el alcance inicial) y puso el ajuste sobre la mesa. Space "Facturación"; estatus final que confirma facturación = **"resuelto"**; validación exige PDF+XML (nacional) o PDF (internacional). Sesión con Pedro para API de Jira + recurrentes. | Erika coordina sesión con Pedro | Por agendar |
|
||||||
|
| Escritura BIND | Explorarla con máxima cautela: **no hay sandbox, es producción**. Prueba Jira→cotización será acordada, medida y controlada llegado el momento. | Johann + Noe/Pedro | Tras sesión API Jira |
|
||||||
|
| Repo/Azure/Jira horas | **No se tocó.** | — | — |
|
||||||
|
| PUE vs PPD (hallazgo 21-jul) | **No se alcanzó a preguntar.** Reintentar el 23-jul o en la sesión con Pedro. | Johann | Pendiente |
|
||||||
|
|
||||||
|
**Compromisos de Balam salidos de la sesión:**
|
||||||
|
1. Enviar a Johann el **diagrama del flujo Jira** en tamaño legible (Arturo/Pedro).
|
||||||
|
2. Agendar **sesión con Pedro** (API Jira, facturación recurrente, automáticas vs manuales).
|
||||||
|
3. Arturo grabará/documentará los procesos nuevos conforme ocurran (caso Frisa en curso).
|
||||||
@@ -0,0 +1,288 @@
|
|||||||
|
"""Actualiza el plan oficial con el corte reconstruido de horas al 15-jul-2026."""
|
||||||
|
|
||||||
|
from copy import copy
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
import openpyxl
|
||||||
|
from openpyxl.styles import Alignment, Font, PatternFill
|
||||||
|
from openpyxl.utils import get_column_letter
|
||||||
|
|
||||||
|
|
||||||
|
PATH = Path(__file__).with_name("Plan-actividades-avance-2026-07-10-ajustado.xlsx")
|
||||||
|
|
||||||
|
|
||||||
|
def find_row(ws, task):
|
||||||
|
for row in range(2, ws.max_row + 1):
|
||||||
|
if ws.cell(row, 1).value == task:
|
||||||
|
return row
|
||||||
|
raise ValueError(f"No se encontró la tarea: {task}")
|
||||||
|
|
||||||
|
|
||||||
|
def copy_row_style(ws, source_row, target_row, max_col=11):
|
||||||
|
for col in range(1, max_col + 1):
|
||||||
|
source = ws.cell(source_row, col)
|
||||||
|
target = ws.cell(target_row, col)
|
||||||
|
if source.has_style:
|
||||||
|
target._style = copy(source._style)
|
||||||
|
target.number_format = source.number_format
|
||||||
|
target.alignment = copy(source.alignment)
|
||||||
|
|
||||||
|
|
||||||
|
workbook = openpyxl.load_workbook(PATH)
|
||||||
|
sheet = workbook["Plan"]
|
||||||
|
|
||||||
|
# Actividades separadas después del plan original. Las inserciones son idempotentes.
|
||||||
|
if not any(
|
||||||
|
sheet.cell(row, 1).value == "Definición de alcance Jira → cotización BIND"
|
||||||
|
for row in range(2, sheet.max_row + 1)
|
||||||
|
):
|
||||||
|
source_row = find_row(sheet, "Sesión de reglas de negocio con Arturo")
|
||||||
|
sheet.insert_rows(source_row + 1)
|
||||||
|
copy_row_style(sheet, source_row, source_row + 1)
|
||||||
|
values = [
|
||||||
|
"Definición de alcance Jira → cotización BIND",
|
||||||
|
"2 días",
|
||||||
|
"mié 22/07/26",
|
||||||
|
"jue 23/07/26",
|
||||||
|
None,
|
||||||
|
"Johan y Balam",
|
||||||
|
0,
|
||||||
|
]
|
||||||
|
for column, value in enumerate(values, start=1):
|
||||||
|
sheet.cell(source_row + 1, column, value)
|
||||||
|
|
||||||
|
if not any(
|
||||||
|
sheet.cell(row, 1).value == "CI/CD + gestión de secretos"
|
||||||
|
for row in range(2, sheet.max_row + 1)
|
||||||
|
):
|
||||||
|
source_row = find_row(sheet, "Repositorio + CI/CD + gestión de secretos")
|
||||||
|
sheet.insert_rows(source_row + 1)
|
||||||
|
copy_row_style(sheet, source_row, source_row + 1)
|
||||||
|
values = [
|
||||||
|
"CI/CD + gestión de secretos",
|
||||||
|
"1 día",
|
||||||
|
"vie 24/07/26",
|
||||||
|
"vie 24/07/26",
|
||||||
|
None,
|
||||||
|
"Johan y Pedro",
|
||||||
|
0,
|
||||||
|
]
|
||||||
|
for column, value in enumerate(values, start=1):
|
||||||
|
sheet.cell(source_row + 1, column, value)
|
||||||
|
|
||||||
|
# Calendario ajustado con los acuerdos del 13–15 de julio.
|
||||||
|
updates = {
|
||||||
|
"Sesión de validación del prototipo": (
|
||||||
|
"1 día",
|
||||||
|
"jue 23/07/26",
|
||||||
|
"jue 23/07/26",
|
||||||
|
"Johan y Balam",
|
||||||
|
),
|
||||||
|
"Documento de hallazgos + ADRs + plan refinado": (
|
||||||
|
"10 días",
|
||||||
|
"vie 10/07/26",
|
||||||
|
"jue 23/07/26",
|
||||||
|
"Johan",
|
||||||
|
),
|
||||||
|
"Sesión de reglas de negocio con Arturo": (
|
||||||
|
"1 día",
|
||||||
|
"mié 22/07/26",
|
||||||
|
"mié 22/07/26",
|
||||||
|
"Arturo y CEO",
|
||||||
|
),
|
||||||
|
"Backend base: autenticación, roles y bitácora de auditoría": (
|
||||||
|
"5 días",
|
||||||
|
"lun 13/07/26",
|
||||||
|
"vie 17/07/26",
|
||||||
|
"Johan",
|
||||||
|
),
|
||||||
|
"Repositorio + CI/CD + gestión de secretos": (
|
||||||
|
"4 días",
|
||||||
|
"lun 13/07/26",
|
||||||
|
"jue 16/07/26",
|
||||||
|
"Pedro y Noé",
|
||||||
|
),
|
||||||
|
"Arquitectura multi-tenant (tenant_id + RLS)": (
|
||||||
|
"4 días",
|
||||||
|
"mar 14/07/26",
|
||||||
|
"vie 17/07/26",
|
||||||
|
"Johan",
|
||||||
|
),
|
||||||
|
}
|
||||||
|
|
||||||
|
for task, (duration, start, end, resources) in updates.items():
|
||||||
|
row = find_row(sheet, task)
|
||||||
|
sheet.cell(row, 2, duration)
|
||||||
|
sheet.cell(row, 3, start)
|
||||||
|
sheet.cell(row, 4, end)
|
||||||
|
sheet.cell(row, 6, resources)
|
||||||
|
|
||||||
|
rules_row = find_row(sheet, "Sesión de reglas de negocio con Arturo")
|
||||||
|
sheet.cell(rules_row, 1, "Sesión de reglas y dudas de Etapa 1")
|
||||||
|
repo_row = find_row(sheet, "Repositorio + CI/CD + gestión de secretos")
|
||||||
|
sheet.cell(repo_row, 1, "Repositorio privado GitHub")
|
||||||
|
|
||||||
|
headers = {
|
||||||
|
8: "Horas al 15/07",
|
||||||
|
9: "Tipo de registro",
|
||||||
|
10: "Notas de horas",
|
||||||
|
11: "Rango estimado",
|
||||||
|
}
|
||||||
|
for column, value in headers.items():
|
||||||
|
cell = sheet.cell(1, column, value)
|
||||||
|
cell.fill = copy(sheet.cell(1, 1).fill)
|
||||||
|
cell.font = copy(sheet.cell(1, 1).font)
|
||||||
|
cell.alignment = Alignment(horizontal="center", vertical="center", wrap_text=True)
|
||||||
|
|
||||||
|
# Corte: 22 h de Etapa 0 + 8 h del inicio de Etapa 1 = 30 h.
|
||||||
|
hours = {
|
||||||
|
"Sesión de arranque (kickoff) con el Ing. Noé": (
|
||||||
|
2.00,
|
||||||
|
"Incluye preparación y sesión de 1 h",
|
||||||
|
),
|
||||||
|
"Sesión de Discovery: proceso actual de facturación y cobranza": (
|
||||||
|
5.50,
|
||||||
|
"Incluye preparación, sesiones y documentación de facturación/cobranza",
|
||||||
|
),
|
||||||
|
"Entrega y validación de accesos (BIND + manual de marca)": (
|
||||||
|
1.00,
|
||||||
|
"Coordinación, recepción y salvaguardas",
|
||||||
|
),
|
||||||
|
"Validación técnica de la API de BIND con la cuenta real": (
|
||||||
|
4.50,
|
||||||
|
"117 consultas GET, análisis y reporte técnico",
|
||||||
|
),
|
||||||
|
"Prototipo visual navegable": (
|
||||||
|
6.00,
|
||||||
|
"Diseño, construcción y ajustes con datos ficticios",
|
||||||
|
),
|
||||||
|
"Sesión de validación del prototipo": (0.00, "Agendada para el 23-jul"),
|
||||||
|
"Documento de hallazgos + ADRs + plan refinado": (
|
||||||
|
3.00,
|
||||||
|
"PDF/correo de entrega, hallazgos, ADRs y actualización del plan",
|
||||||
|
),
|
||||||
|
"Sesión de reglas y dudas de Etapa 1": (0.00, "Agendada para el 22-jul"),
|
||||||
|
"Definición de alcance Jira → cotización BIND": (
|
||||||
|
0.00,
|
||||||
|
"Se define con Balam el 22–23 jul",
|
||||||
|
),
|
||||||
|
"Backend base: autenticación, roles y bitácora de auditoría": (
|
||||||
|
3.50,
|
||||||
|
"Trabajo local del 13–15 jul",
|
||||||
|
),
|
||||||
|
"Repositorio privado GitHub": (0.50, "Coordinación de creación y acceso"),
|
||||||
|
"CI/CD + gestión de secretos": (0.00, "Programado para el 24-jul"),
|
||||||
|
"Arquitectura multi-tenant (tenant_id + RLS)": (
|
||||||
|
1.50,
|
||||||
|
"Diseño técnico inicial",
|
||||||
|
),
|
||||||
|
"Cliente de la API de BIND (reintentos, OData, errores)": (
|
||||||
|
2.00,
|
||||||
|
"Inicio del cliente productivo",
|
||||||
|
),
|
||||||
|
"Demostración semanal (viernes) + reporte de horas": (
|
||||||
|
0.50,
|
||||||
|
"Seguimiento, ajuste de calendario y corte de horas",
|
||||||
|
),
|
||||||
|
}
|
||||||
|
|
||||||
|
pending = {
|
||||||
|
"Sesión de validación del prototipo",
|
||||||
|
"Sesión de reglas y dudas de Etapa 1",
|
||||||
|
"Definición de alcance Jira → cotización BIND",
|
||||||
|
"CI/CD + gestión de secretos",
|
||||||
|
}
|
||||||
|
|
||||||
|
stage_rows = {0: [], 1: []}
|
||||||
|
current_stage = None
|
||||||
|
for row in range(2, sheet.max_row + 1):
|
||||||
|
task = sheet.cell(row, 1).value
|
||||||
|
if task == "Etapa 0":
|
||||||
|
current_stage = 0
|
||||||
|
sheet.cell(row, 11, "18–22 h")
|
||||||
|
continue
|
||||||
|
if task == "Etapa 1":
|
||||||
|
current_stage = 1
|
||||||
|
sheet.cell(row, 11, "32–39 h")
|
||||||
|
continue
|
||||||
|
if task == "Integracion de plataformas Balam":
|
||||||
|
sheet.cell(row, 11, "112–136 h")
|
||||||
|
continue
|
||||||
|
if task not in hours:
|
||||||
|
continue
|
||||||
|
value, note = hours[task]
|
||||||
|
sheet.cell(row, 8, value)
|
||||||
|
sheet.cell(row, 9, "Pendiente" if task in pending else "Reconstruida")
|
||||||
|
sheet.cell(row, 10, note)
|
||||||
|
if current_stage in stage_rows:
|
||||||
|
stage_rows[current_stage].append(row)
|
||||||
|
|
||||||
|
project_row = find_row(sheet, "Integracion de plataformas Balam")
|
||||||
|
stage0_row = find_row(sheet, "Etapa 0")
|
||||||
|
stage1_row = find_row(sheet, "Etapa 1")
|
||||||
|
sheet.cell(stage0_row, 8, f"=SUM({','.join(f'H{row}' for row in stage_rows[0])})")
|
||||||
|
sheet.cell(stage1_row, 8, f"=SUM({','.join(f'H{row}' for row in stage_rows[1])})")
|
||||||
|
sheet.cell(project_row, 8, f"=H{stage0_row}+H{stage1_row}")
|
||||||
|
|
||||||
|
summaries = {
|
||||||
|
project_row: "Corte reconstruido al 15-jul",
|
||||||
|
stage0_row: "22 h: límite superior estimado de la etapa",
|
||||||
|
stage1_row: "8 h consumidas al 15-jul; etapa aún en curso",
|
||||||
|
}
|
||||||
|
for row, note in summaries.items():
|
||||||
|
sheet.cell(row, 9, "Resumen")
|
||||||
|
sheet.cell(row, 10, note)
|
||||||
|
|
||||||
|
for row in range(2, sheet.max_row + 1):
|
||||||
|
sheet.cell(row, 8).number_format = '0.00 "h"'
|
||||||
|
for column in range(8, 12):
|
||||||
|
sheet.cell(row, column).alignment = Alignment(vertical="top", wrap_text=True)
|
||||||
|
if sheet.cell(row, 1).value in {
|
||||||
|
"Integracion de plataformas Balam",
|
||||||
|
"Etapa 0",
|
||||||
|
"Etapa 1",
|
||||||
|
}:
|
||||||
|
for column in range(8, 12):
|
||||||
|
sheet.cell(row, column).font = Font(bold=True, color="FFFFFF")
|
||||||
|
sheet.cell(row, column).fill = PatternFill("solid", fgColor="331F0E")
|
||||||
|
|
||||||
|
widths = {
|
||||||
|
1: 58,
|
||||||
|
2: 13,
|
||||||
|
3: 16,
|
||||||
|
4: 16,
|
||||||
|
5: 14,
|
||||||
|
6: 22,
|
||||||
|
7: 14,
|
||||||
|
8: 16,
|
||||||
|
9: 18,
|
||||||
|
10: 55,
|
||||||
|
11: 18,
|
||||||
|
}
|
||||||
|
for column, width in widths.items():
|
||||||
|
sheet.column_dimensions[get_column_letter(column)].width = width
|
||||||
|
|
||||||
|
sheet.freeze_panes = "A2"
|
||||||
|
sheet.auto_filter.ref = f"A1:K{sheet.max_row}"
|
||||||
|
sheet.sheet_properties.pageSetUpPr.fitToPage = True
|
||||||
|
sheet.page_setup.fitToWidth = 1
|
||||||
|
sheet.page_setup.fitToHeight = 0
|
||||||
|
sheet.oddFooter.center.text = (
|
||||||
|
"Horas reconstruidas al 15-jul-2026; validar contra Jira antes de facturar"
|
||||||
|
)
|
||||||
|
|
||||||
|
stage0_total = sum(hours[sheet.cell(row, 1).value][0] for row in stage_rows[0])
|
||||||
|
stage1_total = sum(hours[sheet.cell(row, 1).value][0] for row in stage_rows[1])
|
||||||
|
assert stage0_total == 22.0
|
||||||
|
assert stage1_total == 8.0
|
||||||
|
|
||||||
|
temporary = PATH.with_suffix(".tmp.xlsx")
|
||||||
|
workbook.save(temporary)
|
||||||
|
check = openpyxl.load_workbook(temporary, data_only=False)["Plan"]
|
||||||
|
assert check.max_column == 11
|
||||||
|
assert check.cell(find_row(check, "Etapa 0"), 8).value.startswith("=SUM(")
|
||||||
|
assert check.cell(find_row(check, "Etapa 1"), 8).value.startswith("=SUM(")
|
||||||
|
temporary.replace(PATH)
|
||||||
|
|
||||||
|
print(f"Actualizado: {PATH}")
|
||||||
|
print("Etapa 0: 22.00 h | Etapa 1: 8.00 h | Total al 15-jul: 30.00 h")
|
||||||
@@ -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,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)
|
||||||
@@ -0,0 +1,300 @@
|
|||||||
|
"""Genera Plan-actividades-avance-2026-07-27.xlsx a partir del del 21-jul.
|
||||||
|
|
||||||
|
Diferencia clave vs. versiones anteriores: las HORAS TRABAJADAS ya no se escriben a
|
||||||
|
mano — se leen de `Seguimiento-horas.csv` (fuente de la verdad) y se agregan por
|
||||||
|
actividad del plan mediante el mapa ACT_POR_TAREA. Así cada celda de horas del
|
||||||
|
Excel es trazable a una o varias líneas del CSV, y el total del encabezado de cada
|
||||||
|
etapa es la suma real de su detalle.
|
||||||
|
|
||||||
|
Corte al 27-jul: validación del prototipo (24-jul, APROBADO) y sesión de reglas
|
||||||
|
(22-jul) completadas; API de Jira (27-jul) recibida; capa de escritura EN PAUSA a
|
||||||
|
la espera de la definición del flujo de facturación (sesión 28-jul).
|
||||||
|
"""
|
||||||
|
import csv
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
from collections import defaultdict
|
||||||
|
from datetime import date
|
||||||
|
|
||||||
|
import openpyxl
|
||||||
|
from openpyxl.styles import Alignment, Font, PatternFill
|
||||||
|
|
||||||
|
BASE = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
SRC = os.path.join(BASE, "Plan-actividades-avance-2026-07-21.xlsx")
|
||||||
|
DST = os.path.join(BASE, "Plan-actividades-avance-2026-07-27.xlsx")
|
||||||
|
CSV_HORAS = os.path.join(BASE, "Seguimiento-horas.csv")
|
||||||
|
HOY = date(2026, 7, 27)
|
||||||
|
|
||||||
|
CAFE, CREMA, GRIS_B, AMARILLO = "331F0E", "FDF3D8", "D9D9D9", "F7BD0C"
|
||||||
|
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
|
||||||
|
|
||||||
|
C_PCT, C_BLQ, C_EST, C_TRAB, C_STAT = 7, 8, 9, 10, 11
|
||||||
|
|
||||||
|
# --- Horas por fila del Excel --------------------------------------------------
|
||||||
|
# Las actividades del CSV no mapean 1:1 con las filas del plan (el CSV agrupa por
|
||||||
|
# actividad contractual; el Excel desglosa por tarea de proyecto). Se reparten por
|
||||||
|
# fila usando la evidencia de la bitácora, de modo que cada fila muestre el trabajo
|
||||||
|
# que realmente le corresponde y la suma siga cuadrando con el CSV.
|
||||||
|
#
|
||||||
|
# Trazabilidad de Etapa 1 (19.75 h del CSV):
|
||||||
|
# E1-01 6.50 = backend base 3.50 (13-14 jul) + B0/B1 1.00 + B2 0.50 + B3 0.50 + B4 1.00
|
||||||
|
# E1-02 0.50 = repositorio GitHub
|
||||||
|
# E1-03 2.00 = multi-tenant 1.50 (14-15 jul) + B6 RLS 0.50
|
||||||
|
# E1-04 2.00 = cliente de BIND
|
||||||
|
# E1-05 3.30 = gestión 0.50 + corte 16-jul 1.00 + cierre 21-jul 0.25 + prep 0.75 + sesión 0.80
|
||||||
|
# E1-06 3.25 = scaffold 2.00 + entorno local 1.00 + Scalar 0.25
|
||||||
|
# E1-07 0.20 = sesión API de Jira
|
||||||
|
# E1-08 2.00 = diseño del plan de Etapa 1
|
||||||
|
HORAS_POR_FILA = {
|
||||||
|
# --- Etapa 0 (21.83 h) — una actividad del CSV por fila
|
||||||
|
# E0-02 3.83 = sesión facturación 1.08 + análisis 1.00 + sesión cobranza 1.00 + notas 0.75
|
||||||
|
# E0-07 1.50 = guion 0.45 + sesión de validación 1.05 (transcript: 1 h 4 min)
|
||||||
|
"Sesión de arranque (kickoff)": 2.00, # E0-01
|
||||||
|
"Sesión de Discovery": 3.83, # E0-02
|
||||||
|
"Entrega y validación de accesos": 1.00, # E0-03
|
||||||
|
"Validación técnica de la API de BIND": 4.50, # E0-04
|
||||||
|
"Prototipo visual navegable": 7.50, # E0-05
|
||||||
|
"Documento de hallazgos": 1.50, # E0-06
|
||||||
|
"Sesión de validación del prototipo": 1.50, # E0-07 (guion 0.45 + sesión 1.05)
|
||||||
|
# --- Etapa 1 (19.75 h)
|
||||||
|
# El trabajo de andamiaje (entorno local, scaffold, Scalar) y el diseño del plan
|
||||||
|
# no tienen fila propia en el plan original: se cargan a la fila técnica que les
|
||||||
|
# corresponde por naturaleza, no a la de seguimiento semanal.
|
||||||
|
"Repositorio privado GitHub": 0.50, # E1-02
|
||||||
|
"Backend base": 8.25, # E1-01 5.00 + E1-06 3.25 (entorno, scaffold, Scalar)
|
||||||
|
"Arquitectura multi-tenant": 3.00, # E1-03 2.00 (incl. B6 RLS) + E1-08 1.00 (diseño general)
|
||||||
|
"Cliente de la API de BIND": 2.00, # E1-04
|
||||||
|
"Sincronización de clientes": 0.50, # E1-01 parte: B3
|
||||||
|
# B4 produjo dos cosas: el sync de facturas/cotizaciones y el modelo central
|
||||||
|
# (InvoiceStatusMapper con 13 pruebas + migración DetalleSyncPendiente). Se
|
||||||
|
# reparte para que ninguna fila quede en 0 h marcada como avanzada.
|
||||||
|
"Sincronización de facturas": 0.50, # E1-01 parte: B4 (sync)
|
||||||
|
"Modelo de facturas": 0.50, # E1-01 parte: B4 (mapper + migraciones)
|
||||||
|
"Sesión de reglas y dudas": 1.55, # E1-05 parte: prep 0.75 + sesión 0.80
|
||||||
|
"Definición de alcance Jira": 0.20, # E1-07
|
||||||
|
"Demostración semanal": 1.75, # E1-05 resto: gestión 0.50 + corte 1.00 + cierre 0.25
|
||||||
|
# Análisis y diseño de B7 (pipeline Draft→DryRun→Confirm, flags, regla PPD/PUE,
|
||||||
|
# ADR-001): 1.00 h de E1-08, separada del diseño general para que la fila del
|
||||||
|
# entregable 4 muestre el trabajo que sí se hizo. La implementación sigue en pausa.
|
||||||
|
"Capa de escritura controlada": 1.00,
|
||||||
|
# --- Sin horas aún (trabajo no iniciado o dependiente de Balam)
|
||||||
|
"Configuración de infraestructura en Azure": 0.00,
|
||||||
|
"CI/CD": 0.00,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def horas_por_actividad(path):
|
||||||
|
"""Suma horas del CSV por actividad_id y por etapa."""
|
||||||
|
por_act = defaultdict(float)
|
||||||
|
por_etapa = defaultdict(float)
|
||||||
|
with open(path, encoding="utf-8", newline="") as fh:
|
||||||
|
rdr = csv.DictReader(fh)
|
||||||
|
for i, r in enumerate(rdr, start=2):
|
||||||
|
if not (r.get("fecha") or "").strip():
|
||||||
|
continue
|
||||||
|
if None in r or any(v is None for v in r.values()):
|
||||||
|
raise ValueError(
|
||||||
|
f"{os.path.basename(path)} línea {i}: número de campos inesperado "
|
||||||
|
f"(¿falta escapar una coma con comillas?)")
|
||||||
|
h = float(r["horas"])
|
||||||
|
por_act[r["actividad_id"]] += h
|
||||||
|
por_etapa[r["etapa"]] += h
|
||||||
|
return por_act, por_etapa
|
||||||
|
|
||||||
|
|
||||||
|
HORAS_ACT, HORAS_ETAPA = horas_por_actividad(CSV_HORAS)
|
||||||
|
TOTAL_CSV = sum(HORAS_ETAPA.values())
|
||||||
|
|
||||||
|
# % por entregables terminados (dato duro y defendible, no promedio ponderado).
|
||||||
|
# Etapa 1: 6 de 8 entregables contractuales TERMINADOS (1,2,3,5,6,7) = 75%. Los dos
|
||||||
|
# restantes van parciales y su avance se refleja en su propia fila (entregable 4 al
|
||||||
|
# 40%, entregable 8 al 85%), no promediado en el encabezado: la etapa se mide por
|
||||||
|
# entregable cerrado, que es lo verificable.
|
||||||
|
PCT_ETAPA = {"0": 1.0, "1": 0.75}
|
||||||
|
|
||||||
|
OVERRIDES = {
|
||||||
|
"Integracion de plataformas Balam": dict(pct=0.80, trab=TOTAL_CSV),
|
||||||
|
"Etapa 0": dict(pct=PCT_ETAPA["0"], trab=HORAS_ETAPA["0"]),
|
||||||
|
"Etapa 1": dict(pct=PCT_ETAPA["1"], trab=HORAS_ETAPA["1"]),
|
||||||
|
"Documento de hallazgos": dict(pct=1.0),
|
||||||
|
"Sesión de validación del prototipo": dict(pct=1.0, comienzo="vie 24/07/26",
|
||||||
|
fin="vie 24/07/26"),
|
||||||
|
# Estas dos filas absorben el andamiaje y el diseño del plan: se renombran y se
|
||||||
|
# amplía su rango estimado para que el nombre refleje el alcance que cubren.
|
||||||
|
"Backend base": dict(
|
||||||
|
nombre="Backend base: entorno, andamiaje, autenticación, roles y bitácora",
|
||||||
|
est="8–9"),
|
||||||
|
"Arquitectura multi-tenant": dict(
|
||||||
|
nombre="Arquitectura multi-tenant (tenant_id + RLS) y diseño de la solución",
|
||||||
|
est="3–4"),
|
||||||
|
"Demostración semanal": dict(est="2–3"),
|
||||||
|
# B7: diseño, ADR-001, entidad WriteOperation (migrada en B1) y policy
|
||||||
|
# PuedeOperarEscritura ya existen; falta BindWriteClient, el pipeline y los
|
||||||
|
# shapes de los POST (dependen de las reglas del 28-jul). Sin horas propias:
|
||||||
|
# las del modelo y la policy ya están cobradas en B1/B2 ("Backend base").
|
||||||
|
"Capa de escritura controlada": dict(pct=0.40),
|
||||||
|
"Sesión de reglas y dudas": dict(pct=1.0),
|
||||||
|
"Sincronización de clientes": dict(pct=1.0),
|
||||||
|
"Sincronización de facturas": dict(pct=1.0),
|
||||||
|
# Conectividad resuelta el 27-jul (token PAF a 1 año, API read+write, bandeja FAC,
|
||||||
|
# límites de consumo). Queda el 10 %: qué estatus dispara qué, amarrado al 28-jul.
|
||||||
|
"Definición de alcance Jira": dict(pct=0.90),
|
||||||
|
}
|
||||||
|
|
||||||
|
# Estatus a mano (gana sobre el semáforo automático): texto, fill, font, negrita
|
||||||
|
K_ESPECIAL = {
|
||||||
|
"Documento de hallazgos": ("✅ Completada", V_FILL, V_FONT, False),
|
||||||
|
"Sesión de validación del prototipo": ("✅ Validado (aprobado)", V_FILL, V_FONT, True),
|
||||||
|
"Demostración semanal": ("🟡 En curso (semanal)", A_FILL, A_FONT, False),
|
||||||
|
"Modelo de facturas": ("🟡 Modelo y migraciones ✓ · falta seeder", A_FILL, A_FONT, False),
|
||||||
|
"Capa de escritura controlada": ("🔴 Diseño listo · impl. en pausa (def. Balam 28-jul)",
|
||||||
|
R_FILL, R_FONT, True),
|
||||||
|
"Sesión de reglas y dudas": ("✅ Completada", V_FILL, V_FONT, False),
|
||||||
|
"Definición de alcance Jira": ("🟡 Conectividad lista · falta regla 28-jul",
|
||||||
|
A_FILL, A_FONT, False),
|
||||||
|
"Configuración de infraestructura en Azure": ("🟡 Pendiente (Balam)", A_FILL, A_FONT, False),
|
||||||
|
"CI/CD": ("🔴 Pendiente · dep. Azure", R_FILL, R_FONT, True),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
|
||||||
|
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")
|
||||||
|
|
||||||
|
|
||||||
|
wb = openpyxl.load_workbook(SRC)
|
||||||
|
ws = wb.active
|
||||||
|
|
||||||
|
# 1) Horas trabajadas desde el CSV + overrides de datos por prefijo de nombre
|
||||||
|
for row in range(2, ws.max_row + 1):
|
||||||
|
nombre = str(ws.cell(row=row, column=1).value or "").strip()
|
||||||
|
if not nombre:
|
||||||
|
continue
|
||||||
|
|
||||||
|
# 1a) horas por fila (reparto trazable al CSV; ver HORAS_POR_FILA)
|
||||||
|
for prefijo, h in HORAS_POR_FILA.items():
|
||||||
|
if nombre.startswith(prefijo):
|
||||||
|
ws.cell(row=row, column=C_TRAB, value=h)
|
||||||
|
break
|
||||||
|
|
||||||
|
# 1b) overrides de %, fechas, nombre y rango estimado
|
||||||
|
for prefijo, ov in OVERRIDES.items():
|
||||||
|
if nombre.startswith(prefijo):
|
||||||
|
if "pct" in ov: ws.cell(row=row, column=C_PCT, value=ov["pct"])
|
||||||
|
if "trab" in ov: ws.cell(row=row, column=C_TRAB, value=round(ov["trab"], 2))
|
||||||
|
if "comienzo" in ov: ws.cell(row=row, column=3, value=ov["comienzo"])
|
||||||
|
if "fin" in ov: ws.cell(row=row, column=4, value=ov["fin"])
|
||||||
|
if "est" in ov: ws.cell(row=row, column=C_EST, value=ov["est"])
|
||||||
|
break
|
||||||
|
|
||||||
|
# 2) Recalcula el semáforo con HOY=27-jul (mismas reglas que el del 21-jul)
|
||||||
|
for row in range(2, ws.max_row + 1):
|
||||||
|
nombre = str(ws.cell(row=row, column=1).value or "").strip()
|
||||||
|
if not nombre or nombre.startswith("✅") or nombre.startswith("▬"):
|
||||||
|
continue
|
||||||
|
es_etapa = nombre.startswith("Etapa") or nombre.startswith("Integracion")
|
||||||
|
pct = ws.cell(row=row, column=C_PCT).value or 0
|
||||||
|
fin = fecha(ws.cell(row=row, column=4).value)
|
||||||
|
ini = fecha(ws.cell(row=row, column=3).value)
|
||||||
|
stat = ws.cell(row=row, column=C_STAT)
|
||||||
|
|
||||||
|
if es_etapa:
|
||||||
|
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 en la celda de %
|
||||||
|
pcel = ws.cell(row=row, column=C_PCT)
|
||||||
|
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)
|
||||||
|
pcel.number_format = "0%"
|
||||||
|
ws.cell(row=row, column=C_TRAB).number_format = "0.00"
|
||||||
|
|
||||||
|
# 3) Overrides finos de estatus (bloqueos y dependencias)
|
||||||
|
for row in range(2, ws.max_row + 1):
|
||||||
|
nombre = str(ws.cell(row=row, column=1).value or "").strip()
|
||||||
|
for prefijo, (txt, fill, font, bold) in K_ESPECIAL.items():
|
||||||
|
if nombre.startswith(prefijo):
|
||||||
|
stat = ws.cell(row=row, column=C_STAT)
|
||||||
|
stat.value = txt
|
||||||
|
pinta(stat, fill, font, bold)
|
||||||
|
break
|
||||||
|
|
||||||
|
# 4) Renombra las filas que absorbieron alcance (al final: los pasos 2-3 buscan por
|
||||||
|
# el nombre original)
|
||||||
|
for row in range(2, ws.max_row + 1):
|
||||||
|
nombre = str(ws.cell(row=row, column=1).value or "").strip()
|
||||||
|
for prefijo, ov in OVERRIDES.items():
|
||||||
|
if "nombre" in ov and nombre.startswith(prefijo):
|
||||||
|
ws.cell(row=row, column=1, value=ov["nombre"])
|
||||||
|
break
|
||||||
|
|
||||||
|
# 5) Encabezado de la columna de estatus
|
||||||
|
ws.cell(row=1, column=C_STAT, value=f"Estatus (hoy {HOY.day}-jul)")
|
||||||
|
|
||||||
|
# 6) Reescribe leyenda/notas (borra las viejas)
|
||||||
|
for row in range(ws.max_row, 1, -1):
|
||||||
|
v = str(ws.cell(row=row, column=1).value or "")
|
||||||
|
if v.startswith(("✅ Completada ·", "▬ La línea roja", "Núcleo de la Etapa 1",
|
||||||
|
"🔴 La capa de escritura", "API de Jira:", "Horas:")):
|
||||||
|
for col in range(1, C_STAT + 1):
|
||||||
|
ws.cell(row=row, column=col).value = None
|
||||||
|
|
||||||
|
ley = ws.max_row + 2
|
||||||
|
notas = [
|
||||||
|
("✅ Completada · 🟡 En curso · 🔴 Vencida/Pendiente · ⚪ Próxima", G_FONT, True),
|
||||||
|
(f"Horas: {HORAS_ETAPA['0']:.2f} h de Etapa 0 y {HORAS_ETAPA['1']:.2f} h de Etapa 1 "
|
||||||
|
f"({TOTAL_CSV:.2f} h en total), conforme al control local de horas trabajadas. "
|
||||||
|
f"Etapa 1 estimada en 32–39 h.", CAFE, False),
|
||||||
|
("Etapa 1: 6 de 8 entregables contractuales terminados — autenticación y roles con "
|
||||||
|
"bitácora, multi-tenant con RLS, cliente de BIND, sincronización de clientes, "
|
||||||
|
"sincronización de facturas y cotizaciones, y modelo de facturas con estados y "
|
||||||
|
"cartera por moneda. Verificado contra producción: 1,494 facturas sincronizadas "
|
||||||
|
"con la cartera cuadrada.", CAFE, False),
|
||||||
|
("🔴 La capa de escritura (cotización→factura) queda EN PAUSA a la espera de la "
|
||||||
|
"definición del flujo de facturación (sesión del martes 28-jul con Arturo y Araceli).",
|
||||||
|
"9C0006", False),
|
||||||
|
("Pendientes menores: datos de prueba (seeder) y verificación del aislamiento RLS "
|
||||||
|
"con rol no-superusuario. Azure y CI/CD dependen de accesos del lado de Balam.", CAFE, False),
|
||||||
|
("API de Jira: token recibido el 27-jul; la integración inicia una vez definido el flujo.",
|
||||||
|
CAFE, False),
|
||||||
|
]
|
||||||
|
for i, (txt, color, bold) in enumerate(notas):
|
||||||
|
c = ws.cell(row=ley + i, column=1, value=txt)
|
||||||
|
c.font = Font(italic=True, size=9, color=color, bold=bold)
|
||||||
|
|
||||||
|
ws.freeze_panes = "A2"
|
||||||
|
ws.oddFooter.center.text = (f"Avance al 27-jul-2026 · {TOTAL_CSV:.2f} h trabajadas "
|
||||||
|
"conforme al control local (pendiente conciliar con Jira)")
|
||||||
|
|
||||||
|
wb.save(DST)
|
||||||
|
|
||||||
|
# --- Verificación: el detalle debe cuadrar con el encabezado de cada etapa -----
|
||||||
|
print("OK ->", DST)
|
||||||
|
print(f" Etapa 0: {HORAS_ETAPA['0']:.2f} h · Etapa 1: {HORAS_ETAPA['1']:.2f} h "
|
||||||
|
f"· TOTAL {TOTAL_CSV:.2f} h")
|
||||||
|
suma_detalle = sum(HORAS_POR_FILA.values())
|
||||||
|
ok = abs(suma_detalle - TOTAL_CSV) < 0.01
|
||||||
|
print(f" Suma del detalle mostrado: {suma_detalle:.2f} h "
|
||||||
|
f"({'cuadra con el CSV' if ok else f'NO CUADRA (CSV {TOTAL_CSV:.2f})'})")
|
||||||
|
if not ok:
|
||||||
|
raise SystemExit("El detalle por fila no cuadra con el CSV — revisar HORAS_POR_FILA.")
|
||||||
@@ -0,0 +1,175 @@
|
|||||||
|
"""Genera Avance-Balam-2026-07-27.xlsx — versión SIMPLE para enviar a Erika.
|
||||||
|
|
||||||
|
Se construye desde el Excel interno (`Plan-actividades-avance-2026-07-27.xlsx`),
|
||||||
|
que a su vez lee las horas de `Seguimiento-horas.csv`. Así los dos archivos nunca
|
||||||
|
se contradicen: este es una vista reducida, no una segunda fuente de datos.
|
||||||
|
|
||||||
|
Formato pedido por Erika el 21-jul: entregable + actividad + horas por etapa.
|
||||||
|
Se quitan del interno: rangos estimados, bloques del plan (B1/B4/B9), predecesoras
|
||||||
|
y duración en días — ruido para quien solo da seguimiento.
|
||||||
|
"""
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
from datetime import date
|
||||||
|
|
||||||
|
import openpyxl
|
||||||
|
from openpyxl.styles import Alignment, Border, Font, PatternFill, Side
|
||||||
|
from openpyxl.utils import get_column_letter
|
||||||
|
|
||||||
|
BASE = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
SRC = os.path.join(BASE, "Plan-actividades-avance-2026-07-27.xlsx")
|
||||||
|
DST = os.path.join(BASE, "Avance-Balam-2026-07-27.xlsx")
|
||||||
|
HOY = date(2026, 7, 27)
|
||||||
|
|
||||||
|
# Marca Balam
|
||||||
|
CAFE, AMARILLO, CREMA = "331F0E", "F7BD0C", "FDF3D8"
|
||||||
|
V_FILL, V_FONT = "C6EFCE", "006100" # verde
|
||||||
|
A_FILL, A_FONT = "FFEB9C", "9C6500" # amarillo
|
||||||
|
R_FILL, R_FONT = "FFC7CE", "9C0006" # rojo
|
||||||
|
GRIS = "595959"
|
||||||
|
BORDE = "D9D9D9"
|
||||||
|
|
||||||
|
# Columnas del origen
|
||||||
|
O_NOMBRE, O_INI, O_FIN, O_PCT, O_TRAB, O_STAT = 1, 3, 4, 7, 10, 11
|
||||||
|
|
||||||
|
thin = Side(style="thin", color=BORDE)
|
||||||
|
box = Border(left=thin, right=thin, top=thin, bottom=thin)
|
||||||
|
|
||||||
|
|
||||||
|
def limpia_fecha(txt):
|
||||||
|
"""'vie 24/07/26' -> '24/07/2026'."""
|
||||||
|
m = re.search(r"(\d{2})/(\d{2})/(\d{2})", str(txt or ""))
|
||||||
|
return f"{m.group(1)}/{m.group(2)}/20{m.group(3)}" if m else ""
|
||||||
|
|
||||||
|
|
||||||
|
def es_etapa(nombre):
|
||||||
|
return nombre.startswith(("Etapa", "Integracion"))
|
||||||
|
|
||||||
|
|
||||||
|
# --- Lee el interno -----------------------------------------------------------
|
||||||
|
wsrc = openpyxl.load_workbook(SRC).active
|
||||||
|
filas = []
|
||||||
|
for row in range(2, wsrc.max_row + 1):
|
||||||
|
nombre = str(wsrc.cell(row=row, column=O_NOMBRE).value or "").strip()
|
||||||
|
if not nombre or nombre.startswith(("✅", "▬", "Horas:", "Etapa 1:", "🔴", "Pendientes",
|
||||||
|
"API de Jira", "Núcleo")):
|
||||||
|
continue
|
||||||
|
filas.append({
|
||||||
|
"nombre": nombre,
|
||||||
|
"ini": limpia_fecha(wsrc.cell(row=row, column=O_INI).value),
|
||||||
|
"fin": limpia_fecha(wsrc.cell(row=row, column=O_FIN).value),
|
||||||
|
"pct": wsrc.cell(row=row, column=O_PCT).value or 0,
|
||||||
|
"trab": wsrc.cell(row=row, column=O_TRAB).value,
|
||||||
|
"stat": str(wsrc.cell(row=row, column=O_STAT).value or "").strip(),
|
||||||
|
"etapa": es_etapa(nombre),
|
||||||
|
})
|
||||||
|
|
||||||
|
# --- Construye el simple ------------------------------------------------------
|
||||||
|
wb = openpyxl.Workbook()
|
||||||
|
ws = wb.active
|
||||||
|
ws.title = "Avance"
|
||||||
|
|
||||||
|
ws["A1"] = "Proyecto Balam · Plataforma de Automatización Financiera"
|
||||||
|
ws["A1"].font = Font(bold=True, size=14, color=CAFE)
|
||||||
|
ws["A2"] = f"Avance al {HOY.day}-jul-2026"
|
||||||
|
ws["A2"].font = Font(size=10, italic=True, color=GRIS)
|
||||||
|
|
||||||
|
ENC = ["Actividad", "Inicio", "Fin", "Avance", "Horas", "Estatus"]
|
||||||
|
ANCHOS = [52, 12, 12, 10, 9, 34]
|
||||||
|
FILA_ENC = 4
|
||||||
|
|
||||||
|
for i, (txt, ancho) in enumerate(zip(ENC, ANCHOS), start=1):
|
||||||
|
c = ws.cell(row=FILA_ENC, column=i, value=txt)
|
||||||
|
c.font = Font(bold=True, size=10, color=CAFE)
|
||||||
|
c.fill = PatternFill("solid", fgColor=AMARILLO)
|
||||||
|
c.alignment = Alignment(horizontal="center", vertical="center")
|
||||||
|
c.border = box
|
||||||
|
ws.column_dimensions[get_column_letter(i)].width = ancho
|
||||||
|
|
||||||
|
r = FILA_ENC + 1
|
||||||
|
for f in filas:
|
||||||
|
fill_etapa = PatternFill("solid", fgColor=CREMA) if f["etapa"] else None
|
||||||
|
|
||||||
|
c = ws.cell(row=r, column=1, value=f["nombre"])
|
||||||
|
c.font = Font(bold=f["etapa"], size=10, color=CAFE)
|
||||||
|
c.alignment = Alignment(vertical="center", wrap_text=True,
|
||||||
|
indent=0 if f["etapa"] else 1)
|
||||||
|
|
||||||
|
for col, val in ((2, f["ini"]), (3, f["fin"])):
|
||||||
|
cc = ws.cell(row=r, column=col, value=val)
|
||||||
|
cc.font = Font(size=9, color=GRIS)
|
||||||
|
cc.alignment = Alignment(horizontal="center", vertical="center")
|
||||||
|
|
||||||
|
# Avance
|
||||||
|
cp = ws.cell(row=r, column=4, value=f["pct"])
|
||||||
|
cp.number_format = "0%"
|
||||||
|
cp.alignment = Alignment(horizontal="center", vertical="center")
|
||||||
|
if f["pct"] >= 1:
|
||||||
|
cp.font = Font(bold=True, size=10, color=V_FONT)
|
||||||
|
cp.fill = PatternFill("solid", fgColor=V_FILL)
|
||||||
|
elif f["pct"] > 0:
|
||||||
|
cp.font = Font(bold=True, size=10, color=A_FONT)
|
||||||
|
cp.fill = PatternFill("solid", fgColor=A_FILL)
|
||||||
|
else:
|
||||||
|
cp.font = Font(size=10, color=GRIS)
|
||||||
|
|
||||||
|
# Horas
|
||||||
|
ch = ws.cell(row=r, column=5, value=f["trab"])
|
||||||
|
ch.number_format = "0.00"
|
||||||
|
ch.font = Font(bold=f["etapa"], size=10, color=CAFE)
|
||||||
|
ch.alignment = Alignment(horizontal="center", vertical="center")
|
||||||
|
|
||||||
|
# Estatus
|
||||||
|
cs = ws.cell(row=r, column=6, value=f["stat"])
|
||||||
|
cs.alignment = Alignment(horizontal="left", vertical="center", wrap_text=True)
|
||||||
|
cs.font = Font(size=9, color=GRIS)
|
||||||
|
if f["stat"].startswith("✅"):
|
||||||
|
cs.font = Font(size=9, color=V_FONT); cs.fill = PatternFill("solid", fgColor=V_FILL)
|
||||||
|
elif f["stat"].startswith("🟡"):
|
||||||
|
cs.font = Font(size=9, color=A_FONT); cs.fill = PatternFill("solid", fgColor=A_FILL)
|
||||||
|
elif f["stat"].startswith("🔴"):
|
||||||
|
cs.font = Font(size=9, bold=True, color=R_FONT); cs.fill = PatternFill("solid", fgColor=R_FILL)
|
||||||
|
|
||||||
|
for col in range(1, 7):
|
||||||
|
ws.cell(row=r, column=col).border = box
|
||||||
|
if fill_etapa and col in (1, 2, 3):
|
||||||
|
ws.cell(row=r, column=col).fill = fill_etapa
|
||||||
|
r += 1
|
||||||
|
|
||||||
|
# --- Notas --------------------------------------------------------------------
|
||||||
|
tot = next((f["trab"] for f in filas if f["nombre"].startswith("Integracion")), 0)
|
||||||
|
e0 = next((f["trab"] for f in filas if f["nombre"].startswith("Etapa 0")), 0)
|
||||||
|
e1 = next((f["trab"] for f in filas if f["nombre"].startswith("Etapa 1")), 0)
|
||||||
|
|
||||||
|
notas = [
|
||||||
|
("", False),
|
||||||
|
(f"Horas trabajadas: {e0:.2f} h en Etapa 0 y {e1:.2f} h en Etapa 1 "
|
||||||
|
f"({tot:.2f} h en total).", True),
|
||||||
|
("Etapa 1: 6 de los 8 entregables terminados — autenticación y roles con bitácora, "
|
||||||
|
"multi-tenant, cliente de la API de BIND, sincronización de clientes, "
|
||||||
|
"sincronización de facturas y cotizaciones, y modelo de facturas con estados y "
|
||||||
|
"cartera por moneda. Verificado contra producción: 1,494 facturas sincronizadas "
|
||||||
|
"con la cartera cuadrada.", False),
|
||||||
|
("La capa de escritura (cotización→factura) está en pausa a la espera de la "
|
||||||
|
"definición del flujo de facturación (sesión del martes 28-jul).", False),
|
||||||
|
("Azure y CI/CD esperan la configuración de los recursos del lado de Balam.", False),
|
||||||
|
]
|
||||||
|
for txt, bold in notas:
|
||||||
|
if txt:
|
||||||
|
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")
|
||||||
|
ws.merge_cells(start_row=r, start_column=1, end_row=r, end_column=6)
|
||||||
|
ws.row_dimensions[r].height = 28 if len(txt) > 110 else 14
|
||||||
|
r += 1
|
||||||
|
|
||||||
|
ws.freeze_panes = f"A{FILA_ENC + 1}"
|
||||||
|
ws.sheet_view.showGridLines = False
|
||||||
|
ws.print_area = f"A1:F{r}"
|
||||||
|
ws.page_setup.orientation = "landscape"
|
||||||
|
ws.page_setup.fitToWidth = 1
|
||||||
|
ws.sheet_properties.pageSetUpPr.fitToPage = True
|
||||||
|
|
||||||
|
wb.save(DST)
|
||||||
|
print("OK ->", DST)
|
||||||
|
print(f" {len(filas)} filas · Etapa 0 {e0:.2f} h · Etapa 1 {e1:.2f} h · total {tot:.2f} h")
|
||||||
@@ -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`).
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
# Correo — Prototipo navegable Etapa 0 (para validación)
|
||||||
|
|
||||||
|
Enviado · 10-jul-2026
|
||||||
|
Adjunto: Prototipo-Balam-Etapa0.pdf
|
||||||
|
|
||||||
|
Para: Erika Chávez; Ing. Noé Rocha
|
||||||
|
CC: Pedro Ayala
|
||||||
|
(sin Araceli, según indicó Erika el 10-jul)
|
||||||
|
|
||||||
|
**Respuesta de Balam:** revisión interna en curso; sesión de validación confirmada para el 23-jul-2026 a las 7:00 pm.
|
||||||
|
|
||||||
|
Asunto: Balam · Prototipo navegable de la Etapa 0 (para validación)
|
||||||
|
|
||||||
|
======================================================================
|
||||||
|
|
||||||
|
Hola Erika, Ing. Noé:
|
||||||
|
|
||||||
|
Como lo comentamos, les comparto el prototipo navegable de la plataforma de facturación y cobranza, correspondiente al entregable de la Etapa 0 (Discovery).
|
||||||
|
|
||||||
|
El prototipo refleja el proceso real que levantamos en las sesiones con Araceli y Arturo: solicitud en Jira, cotización BIND, prefactura, aprobación humana, CFDI y envío al cliente; más la parte de cobranza (antigüedad de saldos por factura con tolerancia a comisiones, pagos y recordatorios). También incorpora las reglas de validación que surgieron en el Discovery: PPD por defecto, IVA 16%/0%, prefactura siempre antes de timbrar y la cotización BIND como punto de partida obligatorio.
|
||||||
|
|
||||||
|
Lo pueden revisar de dos formas:
|
||||||
|
|
||||||
|
1. PDF adjunto: recorrido pantalla por pantalla con una breve descripción de cada una.
|
||||||
|
|
||||||
|
2. Versión navegable (por si quieren recorrerla ustedes mismos): paputec.mx/balam, entran directo. Es un entorno temporal de demostración con datos 100% ficticios, sin conexión a BIND ni a datos reales; lo doy de baja al cerrar la validación.
|
||||||
|
|
||||||
|
Toda la información que ven (clientes, montos, folios) es ilustrativa, únicamente para visualizar el flujo.
|
||||||
|
|
||||||
|
Quedo muy atento a sus comentarios; si les resulta más cómodo, con gusto agendamos una sesión corta para recorrerlo juntos y afinar detalles.
|
||||||
|
|
||||||
|
Saludos cordiales,
|
||||||
|
Johann Velázquez
|
||||||
|
After Width: | Height: | Size: 135 KiB |
|
After Width: | Height: | Size: 545 KiB |
|
After Width: | Height: | Size: 521 KiB |
|
After Width: | Height: | Size: 403 KiB |
|
After Width: | Height: | Size: 375 KiB |
|
After Width: | Height: | Size: 645 KiB |
|
After Width: | Height: | Size: 561 KiB |
|
After Width: | Height: | Size: 704 KiB |
|
After Width: | Height: | Size: 606 KiB |
|
After Width: | Height: | Size: 5.2 KiB |