Saltar al contenido principal
Estás viendo el contrato actual (v1) de store.business_day_closed — un payload thin: identidad del cierre, timing, closedBy y una store mínima. Ya no arrastra las métricas agregadas del día, los breakdowns ni el detalle de anomalías — eso lo consultás por businessDayId cuando lo necesites.
store.business_day_closed dispara cuando el pipeline automático de cierre de día de Fire termina de construir el snapshot inmutable del día operativo de una tienda. El payload es thin: lleva la identidad del cierre (businessDayId), el timing, quién lo cerró y una ref mínima de tienda — lo suficiente para reaccionar a “el día cerró” y matchearlo de tu lado. Los agregados pesados (ventas, métricas, breakdowns por canal/pago, detalle de force-closed y cancelaciones) viven en el snapshot y se obtienen por businessDayIdno vienen embebidos en el evento. A diferencia de los eventos a nivel orden, este es un evento a nivel tienda: uno por tienda cerrada, no uno por orden.

Condición de disparo

Fire emite store.business_day_closed una vez por snapshot (storeId, businessDayDate), la primera vez que todo lo siguiente es verdadero:
  • La tienda tenía un día de negocio abierto (isBusinessDayOpen === true) para la fecha que se cierra
  • El pipeline de cierre (CloseBusinessDayService.execute()) completó todos los pasos sin error (force-close, cálculo de métricas, persistencia del snapshot)
  • No existe ya un snapshot para ese par (storeId, businessDayDate) (guard de idempotencia)
CoberturaUniversal — todos los países. Sin gating por país.
Llave de idempotenciaevent.id (también trigger.data.businessDayId)
Dispara más de una vezNo, salvo reintento. Un snapshot por (storeId, fecha).
OrigenPipeline cron (v1). El cierre manual aún no emite.
Latencia vs cierre realSíncrono — el evento se encola apenas saveSnapshot() retorna.

Qué hay en trigger.data

Un payload thin: identidad (businessDayId, businessDayDate, timezone, status), timing (openedAt, closedAt), el closedBy resuelto, y una ref mínima de store (uid, code, externalId, countryCode, timezone, currencyCode). Sin métricas, breakdowns ni arrays de anomalías — consultá el snapshot por businessDayId si necesitás el detalle.

Ejemplo — payload real (sanitizado)

{
  "event": {
    "id": "43214dd1-e260-4245-945d-5bbdfc4a54be",
    "type": "store.business_day_closed",
    "createdAt": "2026-05-18T21:59:01.306Z"
  },
  "data": {
    "businessDayId": "2c332116-79ec-4c63-a53d-8b87fff5a654",
    "businessDayDate": "2026-03-31",
    "timezone": "America/Sao_Paulo",
    "status": "CLOSED",
    "openedAt": "2026-03-31T09:00:02.55Z",
    "closedAt": "2026-05-18T21:58:09.882Z",
    "closedBy": {
      "uid": "00000000-0000-0000-0000-000000000000",
      "name": "Cierre Automático — Sandbox",
      "type": "system"
    },
    "store": {
      "uid": "a4019cad-bbac-4269-9f8d-f29654e92c45",
      "code": "BR-SP-001",
      "externalId": "200400500",
      "countryCode": "BR",
      "timezone": "America/Sao_Paulo",
      "currencyCode": "BRL"
    }
  },
  "_meta": { "executionId": "...", "flowId": "...", "flowName": "...", "attempt": 0, "triggerEntityId": "2c332116-79ec-4c63-a53d-8b87fff5a654" }
}

Referencia de data.*

Identidad y temporal

businessDayId
string
UUID del snapshot de día de negocio persistido en la base de Fire. También aparece como _meta.triggerEntityId. Usalo como llave de idempotencia de tu lado — los reenvíos del mismo cierre comparten el mismo businessDayId — y como llave de lookup para traer los agregados del día desde la API de snapshots de Fire cuando los necesites.
businessDayDate
string
Fecha calendario que representa el cierre, en la timezone de la tienda (ej. "2026-03-31"). No la fecha UTC en que disparó el evento.
timezone
string
Timezone IANA de la tienda (ej. "America/Sao_Paulo"). Usala para interpretar businessDayDate.
status
string
Siempre "CLOSED" para este evento. No hay otro valor posible.
openedAt
string | null
Timestamp ISO 8601 UTC de apertura del día. Puede ser null para tiendas que nunca abrieron explícitamente (data legacy); para tiendas cerradas por cron v1 siempre está presente.
closedAt
string
Timestamp ISO 8601 UTC en que el pipeline persistió el snapshot. Usalo para medir SLI/SLO en vez de event.createdAt (que es el momento del dispatch de la cola).

closedBy — identidad del operador

closedBy
object
Identidad del operador ya resuelta. Sin lookups en runtime downstream.

store — ref mínima de tienda

store
object
Una referencia mínima de tienda — identidad más país/timezone/moneda. El bloque store canónico completo (name, address, vendor, account, fiscal config) no viene acá; si lo necesitás, tomalo de los eventos order.completed de la misma tienda.

Ejemplo de handler

async function onStoreDayClosed(data) {
  const { businessDayId, businessDayDate, timezone, store, closedBy } = data;

  // 1. Idempotencia — saltar si ya procesamos este cierre
  const existing = await db.dayCloses.findUnique({ where: { businessDayId } });
  if (existing) return;

  // 2. Registrar el cierre (lean — identidad + ref de tienda)
  await db.dayCloses.create({
    data: {
      businessDayId,
      businessDayDate,
      timezone,
      storeExternalId: store.externalId,
      storeCode: store.code,
      countryCode: store.countryCode,
      currencyCode: store.currencyCode,
      closedByName: closedBy.name,
      closedByType: closedBy.type,
    },
  });

  // 3. ¿Necesitás los agregados del día (sales, metrics, anomalías)? Traelos por businessDayId.
  //    NO están en este evento — consultá la API de snapshots de Fire on demand.
  // const snapshot = await fire.businessDay.get(businessDayId);
}

Errores comunes

  • Las métricas del día NO están en este evento. sales, metrics, byChannel, byPaymentMethod, forceClosedOrders, closureStats, cancelledOrders y metadata ya no viajan inline. Si dependías de ellos, traé el snapshot por businessDayId.
  • snapshotId se renombró a businessDayId. Si parseabas data.snapshotId, cambiá a data.businessDayId (mismo valor — el id de la fila del snapshot).
  • closedBy.uid puede ser el nil UUID "00000000-0000-0000-0000-000000000000" — en cierres system donde la cuenta no configuró un system user. Detectá cierres system con closedBy.type === "system", no parseando el uid.
  • businessDayDate es la fecha calendario de la tienda, no UTC. Interpretala con timezone.
  • Los campos store.* pueden ser null ante configuración de tienda incompleta. No asumas non-null.

Eventos relacionados

order.completed

Dispara por cada orden al completarse. El bloque store completo vive acá.

order.cancelled

Evento por cancelación. Usalo para el detalle de cancelaciones en vez del payload de cierre.