Esta guía describe cómo habilitar la sincronización de tablas adicionales del POS (p. ej. staff_users, prep_queue, inventory_items) entre Supabase y la base SQLite usada por los servicios offline. Los pasos se realizan principalmente en el repositorio XocoCafe (fuente de datos) y se consumen desde Xoco-POS (endpoints).
Archivo a modificar: ../XocoCafe/schema.sqlite.sql.
- Busca la sección correspondiente al dominio (staff, inventario, cola, etc.) y agrega las columnas relevantes manteniendo los tipos de SQLite (
TEXT,INTEGER,REAL,NUMERIC,BOOLEANsimuladas comoINTEGER). - Mantén los nombres exactamente como en Supabase. El script de sync compara los nombres para mapear registros y evita sincronizar columnas inexistentes.
- Define llaves primarias y columnas de marcas de tiempo (
createdAt,updatedAt) en el orden real de Supabase. Ejemplo mínimo:
CREATE TABLE IF NOT EXISTS staff_users (
id TEXT PRIMARY KEY,
email TEXT NOT NULL,
role TEXT NOT NULL,
branchId TEXT,
firstNameEncrypted TEXT,
lastNameEncrypted TEXT,
createdAt TEXT NOT NULL,
updatedAt TEXT NOT NULL
);- Cuando agregues tablas nuevas (p. ej.
inventory_movements), crea los índices necesarios si los endpoints los requieren (CREATE INDEX ...). SQLite soporta los índices simples usados para filtrados locales.
Archivo: ../XocoCafe/scripts/sync-supabase-sqlite.mjs.
- Ubica el arreglo
SYNC_TABLES. Cada entrada debe exponer:{ name: 'staff_users', pk: 'id', updatedColumn: 'updatedAt' }
namedebe coincidir con el nombre real de la tabla en Supabase.pkes la columna utilizada para laON CONFLICTlocal; agregaido el identificador correspondiente.updatedColumndebe existir tanto en Supabase como en SQLite y representar la última actualización (updatedAtocreatedAtcuando no existaupdatedAt). Esta columna permite al script hacer sincronizaciones incrementales.- Para tablas con dependencias (ej.
prep_queuedepende deorder_items), registra primero la tabla base para evitar referencias huérfanas durante la primera corrida.
- En el repositorio
XocoCafe, instala dependencias (npm install) si es la primera vez. - Exporta las variables requeridas (
SUPABASE_SERVICE_KEY,SUPABASE_URL,SQLITE_PATH, etc.). Normalmente ya están definidas en.env.local. - Corre
npm run sync:sqlite. El script:- Lee
schema.sqlite.sqly recrea el archivoapp.db(si cambió el esquema). - Itera por
SYNC_TABLES, descarga datos de Supabase en bloques y los inserta en SQLite. - Reporta columnas faltantes y las ignora automáticamente para evitar fallas.
- Lee
- Repite la ejecución cada vez que agregues columnas nuevas para validar que no existan errores.
- Importa el cliente local en endpoints o servicios:
import { sqlite } from '@/lib/sqlite';. - Para lecturas, usa helpers genéricos:
const staff = await sqlite.all<StaffUser>('SELECT * FROM staff_users WHERE role = ?', ['barista']);. - Para escrituras u operaciones offline-first usa
sqlite.runosqlite.exec. Si el endpoint necesita seguir funcionando offline, aplica la estrategia write-through: escribe primero en Supabase y, tras el éxito, replica el cambio en SQLite (o al revés si el flujo es offline → online con reintentos). - Mantén Supabase como fuente de verdad. Las mutaciones locales deben reconciliarse con Supabase en el mismo endpoint para evitar divergencias.
- Actualmente el script deja que Supabase gane cuando hay conflictos sobre la
pk. SQLite se sobrescribe con los datos remotos. - Si se necesita lógica especial (inventario, conteos críticos), encapsula la regla antes de
supabase.from(...).upserten el servicio correspondiente. Ejemplo: validar queinventory_items.stockno sea negativo y ajustar la cantidad antes de confirmar el upsert remoto. - Sólo agrega flags o columnas de conflicto si existen en ambos lados; de lo contrario, la columna quedará siempre vacía en SQLite.
- ¿La tabla existe en
schema.sqlite.sqlcon todas sus columnas y tipos compatibles? - ¿Tiene
PRIMARY KEYy las marcascreatedAt/updatedAt(ocreated_atsi aplica)? - ¿Registraste la entrada correspondiente en
SYNC_TABLES? - ¿Ejecutaste
npm run sync:sqlitey verificaste que no se reportan columnas faltantes? - ¿Actualizaste los endpoints/servicios para leer desde SQLite (
sqlite.all/sqlite.run)? - ¿La lógica write-through mantiene a Supabase como autoridad y replica los cambios locales?
Sigue estos pasos para cada tabla faltante (staff_users, prep_queue, inventory_items, inventory_movements, etc.) y repite el proceso cada que se agreguen columnas nuevas para mantener los datos del POS sincronizados.
Cuando no haya conexión con Supabase, las mutaciones se persisten en SQLite dentro de pos_pending_queue. No sincronices esta tabla con Supabase: es exclusiva de la app local y guarda la serie de operaciones (upsert, insert, delete) que deben reintentarse.
- El job se dispara al inicio de cada request crítico (por ejemplo
/api/orders) medianteflushPendingOperations. Se procesan hastaPOS_PENDING_SYNC_BATCHregistros por corrida (por defecto20). - Si un reintento vuelve a fallar por red, el estado se marca como offline y se respeta un intervalo (
SUPABASE_RETRY_DELAY_MS, 30s por defecto) antes de volver a intentarlo. - Una vez que todas las operaciones pendientes terminan sin errores,
markSupabaseHealthyrestaura la prioridad de Supabase y las nuevas escrituras se hacen directo en la nube (los pendientes se limpian automáticamente).
Usa node scripts/pending-queue.mjs para inspeccionar o limpiar pos_pending_queue sin abrir el archivo SQLite manualmente:
node scripts/pending-queue.mjs list— muestra los registros pendientes (comando por defecto).node scripts/pending-queue.mjs show <id>— imprime el payload JSON de una operación específica.node scripts/pending-queue.mjs clear <id>— elimina la operación indicada.node scripts/pending-queue.mjs clear all --force— limpia toda la cola (requiere--forcepara evitar accidentes).
Esto te permite validar qué pedidos se reintentarán y descartar pruebas antes de volver a levantar el servidor local.
Esto garantiza que los pedidos creados offline no se pierdan y que Supabase retome el control apenas la red vuelva sin intervención manual.