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.

Ferred sigue una arquitectura hexagonal (Ports & Adapters) en el backend Express y un modelo offline-first en la capa de datos. Cada sucursal tiene su propia base de datos SQLite que actúa como fuente de verdad local. Cuando hay conectividad, el SyncService propaga los cambios pendientes a Supabase (PostgreSQL) en la nube. El frontend React corre dentro de un proceso Electron que también inicia el servidor Express de forma embebida, por lo que la aplicación funciona completa sin necesidad de acceso a internet ni de ningún servidor externo.

Estructura del monorepo

El repositorio usa pnpm workspaces con tres aplicaciones principales:
PaqueteRutaDescripción
rendererapps/rendererReact + Vite + Tailwind + Zustand. Interfaz de usuario del POS.
serverapps/serverExpress.js con arquitectura hexagonal. Incluye adaptadores HTTP, base de datos, sync y DTE.
electronapps/electronWrapper de escritorio. Inicia el servidor Express como proceso hijo e inyecta SQLITE_PATH y BRANCH_ID.

Diagrama de arquitectura

┌─────────────────────────────────────────────────────────┐
│                    ELECTRON (Desktop)                    │
│  ┌─────────────────────┐   ┌───────────────────────┐   │
│  │   Renderer Process  │   │    Main Process       │   │
│  │   React + Vite      │◄──│    main.js            │   │
│  │   Tailwind + Zustand│   │    preload.js         │   │
│  └────────┬────────────┘   └──────────┬────────────┘   │
│           │ Axios /api                │ IPC             │
│  ┌────────▼───────────────────────────▼────────────┐   │
│  │              Express.js Server                   │   │
│  │  ┌────────────────────────────────────────────┐  │   │
│  │  │  ADAPTERS                                  │  │   │
│  │  │  http/ · db/ · sync/ · printer/ · dte/     │  │   │
│  │  └────────────────────────────────────────────┘  │   │
│  └──────────────────────┬──────────────────────────┘   │
│              ┌───────────▼──────────┐                  │
│              │    SQLite local      │                  │
│              │  (una por sucursal)  │                  │
│              └──────────────────────┘                  │
└──────────────────────┬──────────────────────────────────┘
                       │ HTTPS (cuando hay internet)
              ┌────────▼─────────────┐
              │   Supabase (PgSQL)   │
              │   + Railway API      │
              └──────────────────────┘

Arquitectura hexagonal — ports & adapters

El núcleo de negocio (casos de uso, entidades de dominio) no depende de ningún framework ni base de datos. Los adaptadores implementan los puertos definidos por el dominio.

adapters/http

Rutas Express y middleware JWT. Cada recurso tiene su propio archivo de rutas: auth.routes, ventas.routes, dte.routes, sync.routes, etc. El middleware jwtMiddleware protege todos los endpoints excepto /api/auth y /health.

adapters/db

Dos implementaciones de persistencia en paralelo: Prisma para Supabase/PostgreSQL (operaciones en línea y sync) y better-sqlite3 para el SQLite local por sucursal (operaciones offline). El cliente SQLite se inicializa en initSqlite() al arrancar el servidor.

adapters/sync

SyncService ejecuta pushPendientes() cada 30 segundos. Lee hasta 50 registros de sync_log con estado PENDIENTE y los aplica en Supabase vía Prisma. OfflineCache guarda respuestas en memoria con TTL de 5 minutos para evitar lecturas redundantes cuando no hay internet.

adapters/dte

Adaptador para la API del Ministerio de Hacienda de El Salvador (apitest.dtes.mh.gob.sv en sandbox). Si las credenciales DTE no están configuradas, las facturas se generan en modo simulado localmente sin llamar al API externo.

Flujo de datos offline-first

Toda operación de escritura sigue este camino, independientemente de si hay conexión:
  1. El adaptador HTTP recibe la petición y ejecuta la lógica de negocio.
  2. El resultado se persiste en SQLite local (ferred_branch{BRANCH_ID}.db).
  3. Se llama a logPendiente(tabla, operacion, payload), que escribe un registro en la tabla sync_log con estado PENDIENTE.
  4. Si en ese momento hay internet, logPendiente también crea el registro en el syncLog remoto de Supabase vía Prisma.
  5. Si no hay internet, el registro queda solo en SQLite local hasta que SyncService lo detecte en el próximo ciclo de 30 segundos.
Escritura local


SQLite (sync_log: PENDIENTE)

     │  SyncService cada 30 s

pushPendientes() ──► Supabase (PostgreSQL)
     │                    │
     │                    └─► marcarSincronizado(id)

     └─► marcarError(id) si falla (máx. 5 intentos)

Pipeline de sincronización

PasoFunciónDescripción
1logPendienteLocalInserta en sync_log de SQLite con status PENDIENTE.
2SyncService.checkConnectivityPrueba SELECT 1 contra Supabase vía Prisma para determinar si hay internet.
3SyncService.pushPendientesLee hasta 50 registros pendientes con menos de 5 intentos fallidos.
4SyncService.aplicarOperacionAplica CREATE, UPDATE o DELETE en Supabase usando el modelo Prisma correspondiente.
5marcarSincronizadoActualiza el registro en sync_log a SINCRONIZADO y registra el timestamp.
6marcarErrorIncrementa el contador de intentos. Tras 5 fallos, cambia el status a ERROR.

Despliegue en producción

Ferred usa tres servicios de nube complementarios: Netlify sirve el frontend estático (rama main con deploy automático), Railway aloja el servidor Express en contenedor (también rama main, deploy automático), y Supabase provee PostgreSQL 15 siempre activo como base de datos centralizada. Cada instalación de escritorio Electron corre su propio Express embebido y su propio SQLite, independiente de Railway.
EntornoURLServicio
Frontendhttps://ferred.netlify.appNetlify (CDN estático)
Backend APIhttps://server-production-3252.up.railway.appRailway (contenedor Node.js)
Base de datosProyecto Supabase (privado)Supabase PostgreSQL 15
Desktop localhttp://127.0.0.1:3001 (embebido)Express dentro de Electron

Build docs developers (and LLMs) love