From 8d320edfa7b45d6a227bf1a356607e7be0c842ba Mon Sep 17 00:00:00 2001 From: Johann Date: Sat, 1 Aug 2026 20:21:02 -0600 Subject: [PATCH] =?UTF-8?q?Registra=20la=20recuperaci=C3=B3n=20t=C3=A9cnic?= =?UTF-8?q?a=20del=201-ago=20(#61),=20refresca=20pendientes,=20borrador=20?= =?UTF-8?q?de=20status=20para=20Erika=20y=20script=20de=20pruebas=20Jira?= =?UTF-8?q?=20de=20lectura?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Fable 5 --- bitacora/PENDIENTES.md | 9 +- bitacora/REGISTRO.md | 24 +++++ planeacion/Borradores-seguimiento-tecnico.md | 16 ++++ planeacion/pruebas-jira-lectura.sh | 99 ++++++++++++++++++++ 4 files changed, 145 insertions(+), 3 deletions(-) create mode 100755 planeacion/pruebas-jira-lectura.sh diff --git a/bitacora/PENDIENTES.md b/bitacora/PENDIENTES.md index ecc98b2..de637d2 100644 --- a/bitacora/PENDIENTES.md +++ b/bitacora/PENDIENTES.md @@ -2,15 +2,18 @@ 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-08-01 (propuesta v1.3 con Jira→BIND enviada 29-jul y APROBADA por Noé 30-jul [#60]; Excel de etapas `Avance-Balam-2026-07-28.xlsx` entregado 29-jul; acceso API Jira resuelto 30-jul con token regenerado de Pedro; Azure escalado a Noé — requisitos ya comunicados: grupo de recursos + Contributor, sin Global Admin)._ +_Última actualización: 2026-08-01, noche ([#61]: E1 técnica cerrada — B5 scheduler, B6 RLS verificado, B8 suite de integración, B9 seeder demo; frontend de lectura de E2 completo (login→cartera→facturas→detalle+PDF→clientes+CSV); cliente Jira de lectura con pruebas; 16 commits pusheados al repo del cliente. Siguen bloqueados por Balam: Azure, sesión del flujo (B7) y decisiones Jira)._ ## 🔴 Johann (proveedor) — inmediato - [x] ~~⚠️🔥 **Enviar la propuesta/adenda ajustada por la integración Jira→BIND**~~ → **Hecho (29-jul):** v1.3 enviada por WhatsApp (8:18 am) y por correo (3:56 pm) a Noé, CC Ara/Erika/Pedro. **APROBADA por Noé el 30-jul:** "De acuerdo con la propuesta actualizada". Alcance: 7–10 h ($4,200–$6,000 MXN + IVA); total primera etapa 119–146 h. Ver [REGISTRO #59](REGISTRO.md), [#60](REGISTRO.md). - [x] ~~📊 **Enviar el Excel de horas de las etapas siguientes (2, 3)**~~ → **Hecho (29-jul, 8:18 am):** `Avance-Balam-2026-07-28.xlsx` por WhatsApp; 👍 de Erika. Ver [REGISTRO #59](REGISTRO.md). - [x] ~~🔑 **Destrabar el acceso a la API de Jira (401 del 29-jul)**~~ → **Resuelto (30-jul):** Pedro confirmó el correo del apikey (`pedro.ayala@...`), regeneró el token y `GET /myself` respondió 200. Ver `../planeacion/Investigacion-API-Jira.md` (Bloque 0). -- [ ] 🌱 **Seeder de datos de prueba (B9):** sigue pendiente aunque a Erika se le reportó como hecho el 27-jul dentro de "Modelo + migraciones + datos de prueba". Cerrarlo antes de que se pida una demo sobre datos limpios. -- [ ] 🧾 **Integración Jira→BIND — preparar las pruebas aprobables:** con el alcance aprobado (30-jul), avanzar los bloques de `../planeacion/Investigacion-API-Jira.md`; las pruebas de escritura requieren aprobación explícita previa. +- [x] ~~🌱 **Seeder de datos de prueba (B9)**~~ → **Hecho (1-ago):** `DevDataSeeder` opt-in con datos ficticios (5 estados × MXN/USD × PUE/PPD). El desfase con lo reportado a Erika el 27-jul quedó saldado. Ver [REGISTRO #61](REGISTRO.md). +- [ ] 🔑 **Copiar `propuesta/token-jira.txt` a esta máquina** (quedó en la otra computadora) y correr `../planeacion/pruebas-jira-lectura.sh` — completa los bloques 2/3/4/5 (solo lectura) de `../planeacion/Investigacion-API-Jira.md`. +- [ ] 🧪 **Crear el sandbox Jira Cloud Free propio** (decidido 1-ago) para desarrollar escritura (transiciones/comentarios/creación) sin tocar FAC; la prueba en la instancia real sigue reservada a la sesión acompañada que autorizó Noé. +- [ ] ⏱️ **Capturar horas 28-jul→1-ago en el CSV** (propuesta preparada, validar montos) y **regenerar `Avance-Balam` desde script** — la copia en disco del 28-jul está desincronizada (re-guardado manual). +- [ ] 📅 **Demo del viernes 7-ago:** material listo — frontend (login→cartera→facturas→detalle+PDF→clientes), seeder demo como respaldo si BIND falla, scheduler en vivo. - [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). diff --git a/bitacora/REGISTRO.md b/bitacora/REGISTRO.md index 78006a4..4d0c205 100644 --- a/bitacora/REGISTRO.md +++ b/bitacora/REGISTRO.md @@ -1149,6 +1149,30 @@ Johann envió el **avance por actividad en texto** (8:12 am), con 👍 de Erika --- +## 61 · Ago 1, 2026 · Desarrollo · Recuperación del plan: E1 técnica cerrada (B5/B6/B8/B9), frontend de lectura de E2 completo y cliente Jira ⭐ + +> Sesión de trabajo del sábado 1-ago para poner el proyecto donde el plan del 28-jul dice que debería estar, y adelantar todo lo que no depende de Balam. 9 commits nuevos en `pedro-balam-itsm/BALAM` (pusheados junto con los 7 que faltaban de B0–B6 — **el repo del cliente ya refleja todo el trabajo**). + +**Entorno (bloqueos propios resueltos):** el backend no compilaba en esta máquina (faltaba apuntar al SDK .NET 10 de `~/.dotnet`); Docker apagado. Resuelto y documentado en el CLAUDE.md del repo. `bind_token_api.txt` borrado del disco (el token vive solo en user-secrets). + +**Etapa 1 — deuda técnica cerrada (lo no bloqueado):** +- **B9 · `DevDataSeeder`** ✅ — datos demo 100% ficticios opt-in (`Seed:DevData=true`): 5 clientes (con caso de dedup), 11 facturas cubriendo los 5 estados × MXN/USD × PUE/PPD, 3 cotizaciones y 2 vínculos con ticket ficticio. Idempotente, fechas relativas a "hoy" MX. **Cierra el entregable 8 y el pendiente "se reportó como hecho el 27-jul".** Runbook en CLAUDE.md. +- **B6 · verificación RLS real** ✅ — rol `balam_app` (NOSUPERUSER/NOBYPASSRLS) creado y con ownership de las tablas; la connection string de desarrollo ya lo usa. Verificado fail-closed por psql: sin GUC 0 filas, INSERT rechazado. Script de init para volúmenes nuevos en `backend/db/init/`. +- **B5 · scheduler** ✅ — `BackgroundService` + `PeriodicTimer` cada 15 min (configurable; ≤0 lo apaga) con guardas de solape y de cuota (<10% restante salta el ciclo). **Primer ciclo real contra BIND detectó el movimiento desde el 27-jul: 3 facturas nuevas, 46 pagos, 2 cotizaciones nuevas.** +- **B8 · `Balam.Api.Tests`** ✅ — 25 pruebas de integración con el API completo contra Postgres real (`balam_test`, recreada por corrida): matriz rol→policy (401/403/200), login sin filtración de correos, cartera multimoneda contra la fixture demo, paginación/filtros, PDF demo sin tocar BIND. **Total de la solución: 61/61 pruebas verdes.** + +**Etapa 2 — recuperadas las filas vencidas (29-31 jul) y adelantadas las de la semana próxima (solo lectura, nada bloqueado):** andamiaje Angular (sesión JWT en señales, interceptor, guards por rol espejo de las policies, login) · shell con identidad de marca y estado del sync · cartera por moneda **sin total combinado** · listado de facturas con búsqueda/filtros/paginación/orden del lado del servidor (el API se extendió para ello) · detalle con desglose y **descarga de PDF vía proxy** (el token de BIND nunca llega al navegador) · catálogo de clientes con dedup visible y **export CSV** (facturas y clientes). Verificado end-to-end con login real por el proxy de desarrollo. **Fuera de alcance por bloqueo:** creación de cotizaciones y conversión (flujo sin validar por Balam), XML (shape sin confirmar con Pedro), tipo de cambio DOF (diferido: la captura al facturar depende de B7). + +**Integración Jira — todo lo posible sin Balam:** proyecto `Balam.Integrations.Jira` espejo del cliente BIND (Basic auth email:token, `search/jql` con `nextPageToken` — el clásico da 410—, changelog, transiciones con `expand=transitions.fields`, servicedeskapi, rate limit leído de headers `X-RateLimit-*`), con los tres modos ReadOnly/DryRun/Write y **read-only por default** (FAC es producción); 10 pruebas con `FakeHandler`. Los modelos persisten el **id numérico inmutable** (la llave FAC-nnn cambia con un move — Bloque 5). Registro condicional en el API (+`/api/jira/status`). ⚠️ **Las pruebas de lectura contra FAC real quedaron bloqueadas EN ESTA MÁQUINA: `propuesta/token-jira.txt` no existe aquí (quedó en la otra computadora, gitignoreado).** Script listo para correrlas en un comando: `planeacion/pruebas-jira-lectura.sh`. + +**Pendientes que deja la sesión:** +- [ ] Johann — **copiar `propuesta/token-jira.txt` a esta máquina** y correr `planeacion/pruebas-jira-lectura.sh` (bloques 2/3/4/5 de la investigación, solo lectura). +- [ ] Johann — **crear el sandbox Jira Cloud Free propio** (decidido 1-ago) para desarrollar transiciones/comentarios/creación sin tocar FAC. +- [ ] Johann — **capturar las horas del 28-jul→1-ago** en el CSV (propuesta preparada, pendiente de validación) y regenerar el Excel de avance desde el script (la copia en disco está desincronizada — re-guardado manual del 28-jul). +- [ ] Johann — demo del viernes 7-ago: ya hay material (frontend + seeder demo + scheduler). + +--- + ## Resumen ejecutivo del hilo (para contexto rápido) ### Datos duros confirmados por Balam diff --git a/planeacion/Borradores-seguimiento-tecnico.md b/planeacion/Borradores-seguimiento-tecnico.md index 5b6aa0e..2d31f06 100644 --- a/planeacion/Borradores-seguimiento-tecnico.md +++ b/planeacion/Borradores-seguimiento-tecnico.md @@ -32,3 +32,19 @@ Johann Velázquez ## 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. + +## 4. WhatsApp a Erika — status lunes 3-ago + lo que falta de Balam (preparado 1-ago) + +> Hola Erika, buen día. Te comparto avance del fin de semana: +> +> ✅ Cierre técnico de la Etapa 1 (lo que no estaba bloqueado): sincronización automática cada 15 min, datos de demostración, y suite de pruebas del API (61 pruebas en verde). +> ✅ Adelanté el frontend de la Etapa 2 (parte de consulta): inicio de sesión por roles, cartera por moneda, listado de facturas con filtros y búsqueda, detalle con descarga del PDF del CFDI y catálogo de clientes con exportación a Excel/CSV. Todo el código ya está en el repositorio de Balam. +> ✅ La base técnica de la integración con Jira (lectura) quedó lista y probada. +> +> Para seguir avanzando solo me falta de su lado: +> 1. ☁️ El acceso a Azure que quedó con el inge Noé (grupo de recursos con Contributor) — es lo que libera el ambiente de pruebas y el despliegue. +> 2. 📅 La fecha de la sesión del flujo de facturación — desbloquea la capa de escritura (cotización→factura) y las pantallas de emisión. +> 3. 🧾 Tres definiciones de la integración Jira→BIND (pueden salir en una sola sesión con Pedro y Arturo): ¿qué tipo de solicitud se automatiza (Facturación o Facturación adicional)?, ¿qué estatus dispara la cotización?, y ¿de dónde tomamos monto y conceptos del ticket? +> +> Si te parece, el viernes les muestro todo funcionando en la demo semanal. + diff --git a/planeacion/pruebas-jira-lectura.sh b/planeacion/pruebas-jira-lectura.sh new file mode 100755 index 0000000..1604496 --- /dev/null +++ b/planeacion/pruebas-jira-lectura.sh @@ -0,0 +1,99 @@ +#!/usr/bin/env bash +# Pruebas de SOLO LECTURA contra la instancia real de Jira (FAC) — Bloques 2, +# 3 (inventario), 4 y 5 de Investigacion-API-Jira.md. Ninguna llamada muta nada. +# +# Requisitos: el token en propuesta/token-jira.txt (gitignoreado) o en $JIRA_TOKEN. +# Uso: ./pruebas-jira-lectura.sh [directorio_salida] +# La salida VA FUERA DEL REPO por default (los tickets traen datos reales de +# clientes): /tmp/jira-probes-. Al repo solo se llevan los hallazgos +# agregados, transcritos a mano a Investigacion-API-Jira.md. +set -euo pipefail + +SITE="https://balam-jsm-temp.atlassian.net" +EMAIL="pedro.ayala@balamtalentoestrategico.com" +REPO_DIR="$(cd "$(dirname "$0")/.." && pwd)" +TOKEN="${JIRA_TOKEN:-$(cat "$REPO_DIR/propuesta/token-jira.txt" 2>/dev/null || true)}" +if [ -z "$TOKEN" ]; then + echo "ERROR: token no encontrado (propuesta/token-jira.txt o \$JIRA_TOKEN)." >&2 + exit 1 +fi + +OUT="${1:-/tmp/jira-probes-$(date +%Y%m%d-%H%M%S)}" +mkdir -p "$OUT" +AUTH="$EMAIL:$TOKEN" + +req() { # req — guarda body y headers, imprime status + rate limit + local name="$1" path="$2" + local hdr="$OUT/$name.headers" body="$OUT/$name.json" + local status + status=$(curl -sS -u "$AUTH" -H "Accept: application/json" \ + -D "$hdr" -o "$body" -w "%{http_code}" "$SITE$path") + local rate + rate=$(grep -i "x-ratelimit" "$hdr" | tr -d '\r' | paste -sd' ' - || true) + echo "[$status] $name ${rate:-(sin headers de rate limit)}" +} + +echo "== Bloque 0 · auth ==" +req myself "/rest/api/3/myself" + +echo "== Bloque 4 · búsqueda vigente, fields acotados y tope de maxResults ==" +req search-recientes "/rest/api/3/search/jql?jql=$(python3 -c 'import urllib.parse;print(urllib.parse.quote("project = FAC ORDER BY updated DESC"))')&maxResults=10&fields=summary,status,updated,created,issuetype,reporter" +req search-tope "/rest/api/3/search/jql?jql=$(python3 -c 'import urllib.parse;print(urllib.parse.quote("project = FAC"))')&maxResults=5000&fields=id" +req search-delta "/rest/api/3/search/jql?jql=$(python3 -c 'import urllib.parse;print(urllib.parse.quote("project = FAC AND updated >= \"-20m\" ORDER BY updated ASC"))')&fields=updated" + +echo "== Bloque 5 · origen de los tickets (changelog de los 8 más recientes) ==" +python3 - "$OUT" <<'PY' +import json, sys, subprocess, os +out = sys.argv[1] +data = json.load(open(f"{out}/search-recientes.json")) +keys = [(i["id"], i["key"]) for i in data.get("issues", [])][:8] +open(f"{out}/ids-muestra.txt", "w").write("\n".join(f"{i} {k}" for i, k in keys)) +print("muestra:", ", ".join(k for _, k in keys)) +PY +while read -r id key; do + req "issue-$key" "/rest/api/3/issue/$id?expand=changelog&fields=summary,status,issuetype,created,reporter,attachment,description,resolution" +done < "$OUT/ids-muestra.txt" + +echo "== Bloque 3 · transiciones con campos obligatorios (2 tickets de la muestra) ==" +head -2 "$OUT/ids-muestra.txt" | while read -r id key; do + req "transitions-$key" "/rest/api/3/issue/$id/transitions?expand=transitions.fields" +done + +echo "== Bloque 2 · request types faltantes (12 Facturación, 389 Automatización) ==" +req requesttype-12 "/rest/servicedeskapi/servicedesk/35/requesttype/12/field" +req requesttype-389 "/rest/servicedeskapi/servicedesk/35/requesttype/389/field" + +echo "== Resumen para transcribir a Investigacion-API-Jira.md ==" +python3 - "$OUT" <<'PY' +import json, sys, glob, os +out = sys.argv[1] + +def load(n): + try: return json.load(open(f"{out}/{n}.json")) + except Exception: return None + +tope = load("search-tope") +if tope: print(f"- maxResults efectivo con 5000 pedidos: {len(tope.get('issues', []))} issues devueltos; isLast={tope.get('isLast')}") +delta = load("search-delta") +if delta is not None: print(f"- delta 20m: {len(delta.get('issues', []))} tickets actualizados en la ventana") + +moves = 0 +for f in sorted(glob.glob(f"{out}/issue-*.json")): + d = load(os.path.basename(f)[:-5]) + if not d: continue + key = d.get("key", "?") + fields = d.get("fields", {}) + hist = (d.get("changelog") or {}).get("histories", []) or (d.get("changelog") or {}).get("values", []) + key_changes = [i for h in hist for i in h.get("items", []) if i.get("field") in ("Key", "project")] + if key_changes: moves += 1 + att = fields.get("attachment") or [] + desc = fields.get("description") + desc_len = len(json.dumps(desc)) if desc else 0 + total_hist = (d.get("changelog") or {}).get("total", len(hist)) + print(f"- {key}: type={ (fields.get('issuetype') or {}).get('name') } · status={(fields.get('status') or {}).get('name')} · " + f"resolution={(fields.get('resolution') or {}).get('name') if fields.get('resolution') else None} · " + f"adjuntos={len(att)} · descripcion_adf={desc_len}B · changelog(total={total_hist}, en_expand={len(hist)})" + f"{' · ⚠️ MOVE/KEY-CHANGE detectado' if key_changes else ''}") +print(f"- tickets con move/cambio de llave en la muestra: {moves}") +print(f"\nBodies completos en: {out} (NO copiar al repo: contienen datos reales)") +PY