|
15 | 15 | // local está en un estado (cerrado / con tal horario) que HubRise nunca |
16 | 16 | // confirmó -> estado incoherente entre Folvy y la plataforma. |
17 | 17 | // |
18 | | -// Ventana de revisión de 20 min (solape sobre el cron de 15 min, para no |
19 | | -// perder filas entre corridas). No dedupea entre corridas (deuda menor, |
20 | | -// mismo criterio que el original: más ruidoso que perder un fallo en silencio). |
| 18 | +// (C) brand.closure_mode (Fase B, Cap. B) — cierres de marca OLVIDADOS: sin |
| 19 | +// resume_at (indefinido) hace más de 24h, o con resume_at ya vencido pero |
| 20 | +// closure_mode aún 'paused'. Ver checkStaleBrandClosures. |
| 21 | +// |
| 22 | +// Ventana de revisión de 20 min para (A)/(B) (solape sobre el cron de 15 min, |
| 23 | +// para no perder filas entre corridas). (C) no usa ventana: es estado actual, |
| 24 | +// no eventos recientes. No dedupea entre corridas (deuda menor, mismo |
| 25 | +// criterio que el original: más ruidoso que perder un fallo en silencio). |
21 | 26 | // |
22 | 27 | // Deploy: --no-verify-jwt (inocua; sin params externos, solo lee y alerta). |
23 | 28 |
|
@@ -103,6 +108,68 @@ async function checkAvailabilityPushLog( |
103 | 108 | return { failures: rows.length, stuck: stuck.length, soldOutFailed: soldOutFailed.length }; |
104 | 109 | } |
105 | 110 |
|
| 111 | +// (C) Cap. B — cierres de marca OLVIDADOS. Dos casos, distintos de gravedad: |
| 112 | +// · INDEFINIDO (resume_at null) más de INDEFINITE_CLOSURE_ALERT_HOURS: sin |
| 113 | +// expires_at en el push a HubRise, esa marca NO se reabre sola — riesgo |
| 114 | +// real de quedar cerrada para siempre si nadie se acuerda. |
| 115 | +// · VENCIDO (resume_at ya pasado, brand.closure_mode aún 'paused'): HubRise |
| 116 | +// ya reabrió esos SKUs solo (expires_at) — closed_brands ya lo oculta en |
| 117 | +// el indicador ambiental — pero el semáforo de Folvy sigue "mintiendo" |
| 118 | +// hasta que alguien reabra a mano; aviso de limpieza, no de fallo real. |
| 119 | +// SIN dedupe entre corridas (mismo criterio que hubrise-callback-ensure: más |
| 120 | +// ruidoso que olvidarlo en silencio) — se resuelve solo en cuanto se reabre. |
| 121 | +const INDEFINITE_CLOSURE_ALERT_HOURS = 24; |
| 122 | + |
| 123 | +async function checkStaleBrandClosures( |
| 124 | + sb: ReturnType<typeof createClient>, |
| 125 | +): Promise<{ indefinite: number; expired: number }> { |
| 126 | + const { data: closed, error } = await sb |
| 127 | + .from("brand") |
| 128 | + .select("id, name, account_id, closure_resume_at, closure_set_at, closure_reason") |
| 129 | + .eq("closure_mode", "paused") |
| 130 | + .limit(200); |
| 131 | + |
| 132 | + if (error) { |
| 133 | + console.error("availability-watchdog: error consultando brand (cierres de marca)", error); |
| 134 | + return { indefinite: 0, expired: 0 }; |
| 135 | + } |
| 136 | + |
| 137 | + const rows = closed ?? []; |
| 138 | + if (rows.length === 0) return { indefinite: 0, expired: 0 }; |
| 139 | + |
| 140 | + const now = Date.now(); |
| 141 | + const indefiniteCutoff = now - INDEFINITE_CLOSURE_ALERT_HOURS * 60 * 60 * 1000; |
| 142 | + |
| 143 | + const indefinite = rows.filter((b) => |
| 144 | + !b.closure_resume_at && b.closure_set_at && new Date(b.closure_set_at as string).getTime() < indefiniteCutoff); |
| 145 | + const expired = rows.filter((b) => |
| 146 | + !!b.closure_resume_at && new Date(b.closure_resume_at as string).getTime() < now); |
| 147 | + |
| 148 | + if (indefinite.length === 0 && expired.length === 0) return { indefinite: 0, expired: 0 }; |
| 149 | + |
| 150 | + const lines: string[] = []; |
| 151 | + if (indefinite.length > 0) { |
| 152 | + lines.push(`⚠️ ${indefinite.length} marca(s) cerrada(s) INDEFINIDAMENTE hace más de ${INDEFINITE_CLOSURE_ALERT_HOURS}h — sin expires_at, NO se reabren solas en HubRise:`); |
| 153 | + for (const b of indefinite.slice(0, 15)) { |
| 154 | + lines.push(` - ${b.name} · cuenta ${b.account_id} · cerrada desde ${b.closure_set_at} · motivo: ${b.closure_reason ?? "(sin motivo)"}`); |
| 155 | + } |
| 156 | + lines.push(""); |
| 157 | + } |
| 158 | + if (expired.length > 0) { |
| 159 | + lines.push(`${expired.length} marca(s) con cierre YA VENCIDO pero brand.closure_mode sigue 'paused' — HubRise ya las reabrió sola(s), es limpieza de Folvy, no fallo de plataforma:`); |
| 160 | + for (const b of expired.slice(0, 15)) { |
| 161 | + lines.push(` - ${b.name} · cuenta ${b.account_id} · debía reabrir en ${b.closure_resume_at}`); |
| 162 | + } |
| 163 | + } |
| 164 | + |
| 165 | + await raiseAlert( |
| 166 | + `Cierres de marca sin resolver (${indefinite.length + expired.length})`, |
| 167 | + lines.join("\n"), |
| 168 | + "brand-closure", |
| 169 | + ); |
| 170 | + return { indefinite: indefinite.length, expired: expired.length }; |
| 171 | +} |
| 172 | + |
106 | 173 | // (B) Cap. C/D — location_status_log (cerrar/reabrir local, horario semanal). |
107 | 174 | async function checkLocationStatusLog( |
108 | 175 | sb: ReturnType<typeof createClient>, since: string, |
@@ -143,16 +210,19 @@ Deno.serve(async (req: Request) => { |
143 | 210 | const sb = createClient(SUPABASE_URL, SERVICE_ROLE_KEY, { auth: { persistSession: false } }); |
144 | 211 | const since = new Date(Date.now() - WINDOW_MINUTES * 60_000).toISOString(); |
145 | 212 |
|
146 | | - const [availability, locationStatus] = await Promise.all([ |
| 213 | + const [availability, locationStatus, staleBrandClosures] = await Promise.all([ |
147 | 214 | checkAvailabilityPushLog(sb, since), |
148 | 215 | checkLocationStatusLog(sb, since), |
| 216 | + checkStaleBrandClosures(sb), |
149 | 217 | ]); |
150 | 218 |
|
151 | | - const ok = availability.failures === 0 && locationStatus.failures === 0; |
| 219 | + const ok = availability.failures === 0 && locationStatus.failures === 0 |
| 220 | + && staleBrandClosures.indefinite === 0 && staleBrandClosures.expired === 0; |
152 | 221 | return json({ |
153 | 222 | ok, |
154 | 223 | checked_since: since, |
155 | 224 | availability_push_log: availability, |
156 | 225 | location_status_log: locationStatus, |
| 226 | + brand_closures: staleBrandClosures, |
157 | 227 | }, ok ? 200 : 207); |
158 | 228 | }); |
0 commit comments