Registra la recuperación técnica del 1-ago (#61), refresca pendientes, borrador de status para Erika y script de pruebas Jira de lectura
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -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.
|
||||
|
||||
|
||||
Executable
+99
@@ -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-<fecha>. 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 <nombre> <ruta> — 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
|
||||
Reference in New Issue
Block a user