Arquitectura Tecnica
Zentto es un ERP modular multi-tenant, multi-pais y multi-almacen construido sobre una arquitectura de microservicios frontend con API centralizada y soporte dual de base de datos.
Stack tecnologico
Section titled “Stack tecnologico”| Capa | Tecnologia |
|---|---|
| API | Node.js + Express + TypeScript |
| Frontend | Next.js (App Router) + React + TypeScript |
| UI | MUI + MUI X (DataGrid, DatePicker) |
| State | TanStack Query + Zustand |
| Auth | JWT (API) + NextAuth (Frontend) |
| Validacion | Zod (API + Frontend) |
| BD primaria | SQL Server (mssql) |
| BD alternativa | PostgreSQL 16 (pg) |
| Cache | Redis (ioredis, opcional) |
| Contenedores | Docker + PM2 |
| CI/CD | GitHub Actions |
| Reverse proxy | Nginx |
Estructura del monorepo
Section titled “Estructura del monorepo”DatqBoxWeb/ web/ api/ # API Node + Express + TypeScript src/ app.ts # Registro de rutas index.ts # Entry point config/ # Variables de entorno db/ # Capa de abstraccion BD query.ts # callSp, callSpOut, callSpTx mssql.ts # Driver SQL Server pg.ts # Driver PostgreSQL middleware/ # JWT, RBAC, audit-trail, datetime modules/ # 60+ modulos de negocio _shared/ # Utilidades compartidas clientes/ inventario/ contabilidad/ ... sqlweb/ # Scripts SQL Server includes/sp/ # Stored Procedures (T-SQL) run_all.sql # Master script (SSMS) sqlweb-pg/ # Scripts PostgreSQL includes/sp/ # Functions (PL/pgSQL) run_all.sql # Master script (psql) modular-frontend/ # Monorepo micro-frontends apps/ # 15 micro-apps Next.js shell/ # App host principal ventas/ compras/ contabilidad/ inventario/ bancos/ nomina/ pos/ restaurante/ ecommerce/ auditoria/ crm/ manufactura/ flota/ logistica/ packages/ # Paquetes compartidos shared-api/ # Cliente API, tipos, formateo shared-auth/ # Autenticacion compartida shared-ui/ # Componentes UI reutilizables module-admin/ # Modulo administracion module-bancos/ # Modulo bancos module-contabilidad/ # Modulo contabilidad module-compras/ # Modulo compras module-crm/ # Modulo CRM module-ecommerce/ # Modulo ecommerce module-flota/ # Modulo flota module-inventario/ # Modulo inventario module-logistica/ # Modulo logistica module-manufactura/ # Modulo manufactura module-nomina/ # Modulo nomina module-auditoria/ # Modulo auditoria contracts/ openapi.yaml # Contrato API OpenAPI docker/ Dockerfile.api # Imagen API Dockerfile.frontend # Imagen Frontend (PM2) pm2.config.cjs # Ecosistema PM2 (puertos 3000-3010) .github/workflows/ deploy-api.yml # CI/CD API deploy-frontend.yml # CI/CD FrontendBase de datos dual
Section titled “Base de datos dual”Zentto soporta dos motores de base de datos en paralelo. El switch se controla con una sola variable de entorno:
DB_TYPE=sqlserver # Microsoft SQL ServerDB_TYPE=postgres # PostgreSQLLa API es identica sin importar el motor activo. Los servicios llaman callSp('usp_NombreDelSP', params) y el helper resuelve internamente que driver usar.
Conversion automatica de parametros
Section titled “Conversion automatica de parametros”La capa de abstraccion convierte automaticamente:
| Direccion | Transformacion |
|---|---|
| Parametros de entrada | PascalCase → p_snake_case (para PG) |
| Columnas de salida | p_snake_case → PascalCase (desde PG) |
| XML → JSON | HeaderXml → HeaderJson (PG usa JSONB, no XML) |
Tabla de traduccion SQL Server → PostgreSQL
Section titled “Tabla de traduccion SQL Server → PostgreSQL”| SQL Server | PostgreSQL |
|---|---|
CREATE PROCEDURE usp_X | CREATE OR REPLACE FUNCTION usp_x(...) RETURNS ... LANGUAGE plpgsql |
@Param INT | p_param INT |
NVARCHAR(n) | VARCHAR(n) |
BIT | BOOLEAN |
DATETIME / DATETIME2 | TIMESTAMP |
INT IDENTITY(1,1) | INT GENERATED ALWAYS AS IDENTITY |
SYSUTCDATETIME() | NOW() AT TIME ZONE 'UTC' |
ISNULL(x, d) | COALESCE(x, d) |
OPENJSON(...) | jsonb_array_elements(...) |
FOR JSON PATH | json_agg(row_to_json(...)) |
BEGIN TRY...CATCH | EXCEPTION WHEN OTHERS THEN |
MERGE...WHEN NOT MATCHED | INSERT...ON CONFLICT DO NOTHING/UPDATE |
Regla critica: paridad dual-DB
Section titled “Regla critica: paridad dual-DB”Todo cambio de base de datos DEBE implementarse en AMBOS directorios. No hay excepciones. Checklist obligatorio:
- Crear/modificar SP en
sqlweb/includes/sp/(SQL Server) - Crear/modificar funcion equivalente en
sqlweb-pg/includes/sp/(PostgreSQL) - Si es tabla nueva: DDL en ambos directorios
- Si es seed: seed en ambos directorios
- Actualizar
run_all.sqlde ambos motores si hay archivo nuevo - Probar con
DB_TYPE=sqlservery conDB_TYPE=postgres
Helpers de base de datos
Section titled “Helpers de base de datos”Los tres helpers en web/api/src/db/query.ts son la unica forma de interactuar con la BD:
callSp(spName, params)
Section titled “callSp(spName, params)”Ejecuta un stored procedure y retorna las filas resultantes como array.
const rows = await callSp<Customer>('usp_Master_Customer_List', { CompanyId: 1, Search: 'acme', Page: 1, PageSize: 50});callSpOut(spName, params, outputKeys)
Section titled “callSpOut(spName, params, outputKeys)”Ejecuta un SP con parametros de salida. Retorna { rows, output }.
const { rows, output } = await callSpOut<SalesDoc>( 'usp_AR_SalesDocument_List', { CompanyId: 1, Page: 1, PageSize: 50 }, ['TotalCount']);// output.TotalCount = 142callSpTx(operations)
Section titled “callSpTx(operations)”Ejecuta multiples SPs dentro de una transaccion atomica.
await callSpTx([ { sp: 'usp_AR_SalesDocument_Create', params: { ... } }, { sp: 'usp_Inv_Stock_Decrease', params: { ... } },]);Nomenclatura de Stored Procedures
Section titled “Nomenclatura de Stored Procedures”Formato: usp_[Schema]_[Entity]_[Action]
| Componente | Descripcion | Ejemplo |
|---|---|---|
usp_ | Prefijo obligatorio | - |
Schema | Esquema logico (ar, ap, acct, master…) | AR |
Entity | Entidad principal | SalesDocument |
Action | Operacion (List, Get, Create, Update, Void) | List |
Ejemplos completos:
usp_AR_SalesDocument_List— listar documentos de ventausp_Master_Customer_Create— crear clienteusp_Acct_JournalEntry_Post— contabilizar asiento
Patrones de salida
Section titled “Patrones de salida”Listas paginadas
Section titled “Listas paginadas”Las listas retornan un parametro de salida TotalCount para paginacion:
- SQL Server:
@TotalCount INT OUTPUT - PostgreSQL: columna
"TotalCount"enRETURNS TABLE
Escrituras
Section titled “Escrituras”Las operaciones de escritura retornan resultado y mensaje:
- SQL Server:
@Resultado INT OUTPUT, @Mensaje NVARCHAR(500) OUTPUT - PostgreSQL:
RETURNS TABLE("ok" BOOLEAN, "mensaje" VARCHAR)
Multi-tenant
Section titled “Multi-tenant”Zentto es multi-tenant por diseno. Cada tabla de negocio tiene:
CompanyId INT— identificador de empresaBranchId INT— identificador de sucursal
El scope activo se obtiene del token JWT y se inyecta automaticamente en cada consulta mediante getActiveScope().
Las tablas de configuracion viven en el schema cfg:
cfg.Company— empresas registradascfg.Branch— sucursales por empresacfg.AppSetting— configuraciones por empresa
Multi-pais
Section titled “Multi-pais”Zentto soporta multiples paises con reglas fiscales diferenciadas:
cfg.Company.CountryCode— codigo ISO del pais (VE, ES, CO, MX, US)- Plugins fiscales por pais en
web/api/src/modules/fiscal/ - Retenciones, libros fiscales y formatos de factura especificos por jurisdiccion
Paises actualmente soportados:
| Pais | Codigo | Funcionalidades fiscales |
|---|---|---|
| Venezuela | VE | ISLR, IVA, retenciones, impresoras fiscales |
| Espana | ES | Verifactu, SII |
| Colombia | CO | Facturacion electronica DIAN |
| Mexico | MX | CFDI |
| USA | US | Sales tax |
Multi-almacen
Section titled “Multi-almacen”El sistema soporta multiples almacenes con control granular:
inv.Warehouse— catalogo de almacenesWarehouseIden todas las operaciones de stock- Zonas y ubicaciones dentro de cada almacen (inventario avanzado)
- Transferencias entre almacenes
- Stock por ubicacion con trazabilidad de lotes y seriales
Fechas UTC-0
Section titled “Fechas UTC-0”Todas las fechas se almacenan en UTC-0. No hay excepciones.
- API: middleware
datetime.tsconvierte request → UTC, response → timezone local - Frontend: hook
useTimezone()+ funcionesformatDate/formatDateTime/toDateOnly - SQL:
SYSUTCDATETIME()(SQL Server) /NOW() AT TIME ZONE 'UTC'(PostgreSQL) GETDATE()esta prohibido — 0 ocurrencias en el codebase
Columnas de auditoria
Section titled “Columnas de auditoria”Toda tabla de negocio incluye columnas de auditoria:
| Columna | Tipo | Descripcion |
|---|---|---|
CreatedAt | TIMESTAMP | Fecha de creacion (UTC) |
UpdatedAt | TIMESTAMP | Fecha de ultima modificacion (UTC) |
CreatedBy | INT | UserId del creador |
UpdatedBy | INT | UserId del ultimo editor |
IsActive | BOOLEAN | Borrado logico |
Patron best-effort para integraciones
Section titled “Patron best-effort para integraciones”Las integraciones entre modulos (contabilidad, auditoria, notificaciones, inventario) usan el patron best-effort: nunca bloquean la operacion principal.
// Ejemplo: venta genera asiento contabletry { await generateAccountingEntry(saleData);} catch { // Best-effort: nunca bloquea la venta}Esto garantiza que:
- La operacion principal siempre se completa
- Un fallo en contabilidad/auditoria/notificaciones no afecta al usuario
- Los errores se registran silenciosamente para revision posterior
Modulos que usan best-effort:
- Contabilidad automatica (asientos desde ventas, compras, bancos, etc.)
- Auditoria (registro en
audit.AuditLog) - Notificaciones (envio via Zentto Notify)
- Movimientos de inventario (desde POS, manufactura, logistica)