Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/Carlos-Gnd/FERRED-Inventario-y-Ventas/llms.txt

Use this file to discover all available pages before exploring further.

The SyncService runs a background loop every 30 seconds that drains the local sync queue to Supabase when online. Every data mutation logs a pending entry that is picked up and applied to the remote database.

Ciclo de sincronización

SyncService.start() is called once during server bootstrap. It runs an initial cycle immediately and then schedules subsequent runs with setInterval.
1

SyncService.start()

Called once at startup. Fires the first run() immediately and schedules repeating calls every 30 seconds (INTERVAL_MS = 30_000).
2

checkConnectivity()

Pings Prisma with SELECT 1. Returns true if the remote database is reachable, false otherwise. If offline, the cycle exits early — no sync attempt is made.
3

pushPendientes()

Reads up to 50 pending entries from the local sync_log table, applies each to Supabase via aplicarOperacion(), and marks entries as SINCRONIZADO or increments the retry counter on failure.
sync.service.ts
const INTERVAL_MS = 30_000;

export const SyncService = {
  start() {
    void this.run();
    setInterval(() => void this.run(), INTERVAL_MS);
  },

  async run() {
    const online = await this.checkConnectivity();
    if (!online) return;
    await this.pushPendientes();
  },
};

logPendiente()

Every mutation (create, update, delete) calls logPendiente() to durably record the change before it is applied remotely. The function always writes to the local SQLite sync_log table first, then attempts to mirror the entry in the remote SyncLog table if the service is online.
sync.service.ts
export async function logPendiente(
  tabla: string,
  operacion: 'CREATE' | 'UPDATE' | 'DELETE',
  payload: object,
  usuarioId?: number
): Promise<void>
ParameterTypeDescription
tablastringTarget table name. Must be in TABLAS_PERMITIDAS.
operacion'CREATE' | 'UPDATE' | 'DELETE'The type of mutation.
payloadobjectThe record data to sync. Scalar fields only.
usuarioIdnumber (optional)The user who performed the mutation, for audit purposes.
If the remote write fails (e.g., the connection drops mid-request), the error is logged to the console and the local entry is still queued for the next sync cycle.

Tablas sincronizadas

Only tables in the TABLAS_PERMITIDAS set are accepted by logPendiente() and aplicarOperacion(). Attempting to sync any other table throws an error that is recorded in the sync entry.
TableDescription
productoProduct catalog
categoriaProduct categories
usuarioBranch users
stockSucursalPer-branch stock levels
facturaDteElectronic invoices (DTE)
detalleVentaSale line items
proveedorSuppliers
recepcionMercanciaMerchandise receipts
detalleRecepcionReceipt line items

pushPendientes()

pushPendientes() reads up to 50 entries with status = 'PENDIENTE' and fewer than MAX_INTENTOS (5) attempts, then processes each one sequentially.
sync.service.ts
async pushPendientes() {
  const pendientes = leerPendientesLocal(50).filter(
    (log) => log.intentos < MAX_INTENTOS
  );
  if (!pendientes.length) return;

  let ok = 0;
  for (const log of pendientes) {
    try {
      const payload = JSON.parse(log.payload);
      await this.aplicarOperacion(log.tabla, log.operacion, payload);
      marcarSincronizado(log.id);
      ok++;
    } catch (err: any) {
      console.error(`Error sync SQLite ${log.id}:`, err.message);
      marcarError(log.id, err.message, MAX_INTENTOS);
    }
  }

  if (ok > 0) cache.clear();
},
After any successful sync, the in-memory OfflineCache is fully cleared so that subsequent reads reflect the up-to-date remote state.

Estados del SyncLog

StatusDescription
PENDIENTEThe entry has not yet been applied to Supabase. It will be picked up in the next sync cycle.
SINCRONIZADOThe entry was successfully applied to Supabase. sinc_en is stamped with the UTC timestamp of completion.
ERRORThe entry exhausted all MAX_INTENTOS (5) retry attempts. It will no longer be retried and must be resolved manually.
The transition from PENDIENTE to ERROR happens inside marcarError() in sync.local.ts. Each failed attempt increments intentos. When intentos >= MAX_INTENTOS, the status is set to ERROR; otherwise it remains PENDIENTE and will be retried in the next cycle.

GET /api/inventario/sync-pendientes

This endpoint returns a real-time count of sync log entries grouped by status for the authenticated user’s scope. Admins see counts across all users; non-admin users see only their own entries.
response
{
  "pendientes": 3,
  "sincronizados": 142,
  "errores": 0,
  "online": true,
  "sucursalId": 2
}
Poll GET /api/inventario/sync-pendientes from the admin dashboard to monitor sync health across branches. A non-zero errores count indicates entries that require manual intervention — check the sync_log table in SQLite and the SyncLog table in Supabase for the recorded error messages.

Build docs developers (and LLMs) love