feat: API Gateway (Express 5 + Prisma + Monday) y conexion de apps al API
- api-gateway: servicio Monday (retry 429/complejidad, paginacion cursor, column IDs por titulo), endpoints unidades/cotizaciones/clientes/planes, webhook con handshake challenge, Prisma SQLite dev, 55 tests - cotizador: inventario desde /api/unidades con fallback a seed - alta-clientes: POST /api/clientes + upload multipart de documentos - cobranza: persistencia via API con respaldo offline en localStorage - .env.example con board IDs del workspace Frasa-CAS
This commit is contained in:
121
api-gateway/prisma/migrations/20260821224530_init/migration.sql
Normal file
121
api-gateway/prisma/migrations/20260821224530_init/migration.sql
Normal file
@@ -0,0 +1,121 @@
|
||||
-- CreateTable
|
||||
CREATE TABLE "Cotizacion" (
|
||||
"id" TEXT NOT NULL PRIMARY KEY,
|
||||
"unidadCodigo" TEXT NOT NULL,
|
||||
"clienteNombre" TEXT NOT NULL,
|
||||
"clienteEmail" TEXT,
|
||||
"clienteTelefono" TEXT,
|
||||
"tipoPropuesta" TEXT NOT NULL,
|
||||
"acabados" BOOLEAN NOT NULL,
|
||||
"apartado" REAL NOT NULL,
|
||||
"descuento" REAL NOT NULL,
|
||||
"precioLista" REAL NOT NULL,
|
||||
"precioFinal" REAL NOT NULL,
|
||||
"enganche" REAL NOT NULL,
|
||||
"numMensualidades" INTEGER NOT NULL,
|
||||
"mensualidad" REAL,
|
||||
"pagoFinal" REAL NOT NULL,
|
||||
"mondayItemId" TEXT,
|
||||
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "Cliente" (
|
||||
"id" TEXT NOT NULL PRIMARY KEY,
|
||||
"nombreCompleto" TEXT NOT NULL,
|
||||
"curp" TEXT NOT NULL,
|
||||
"rfc" TEXT NOT NULL,
|
||||
"email" TEXT,
|
||||
"celular" TEXT,
|
||||
"proyecto" TEXT NOT NULL,
|
||||
"unidadCodigo" TEXT,
|
||||
"payloadJson" TEXT NOT NULL,
|
||||
"firmaPng" TEXT,
|
||||
"mondayContactoId" TEXT,
|
||||
"mondayApartadoId" TEXT,
|
||||
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "Documento" (
|
||||
"id" TEXT NOT NULL PRIMARY KEY,
|
||||
"clienteId" TEXT NOT NULL,
|
||||
"tipo" TEXT NOT NULL,
|
||||
"nombreArchivo" TEXT NOT NULL,
|
||||
"ruta" TEXT NOT NULL,
|
||||
"mime" TEXT NOT NULL,
|
||||
"tamano" INTEGER NOT NULL,
|
||||
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
CONSTRAINT "Documento_clienteId_fkey" FOREIGN KEY ("clienteId") REFERENCES "Cliente" ("id") ON DELETE CASCADE ON UPDATE CASCADE
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "PlanPago" (
|
||||
"id" TEXT NOT NULL PRIMARY KEY,
|
||||
"ventaRef" TEXT,
|
||||
"clienteNombre" TEXT NOT NULL,
|
||||
"clienteTelefono" TEXT,
|
||||
"unidadCodigo" TEXT NOT NULL,
|
||||
"proyecto" TEXT NOT NULL,
|
||||
"fechaInicio" DATETIME NOT NULL,
|
||||
"engancheTotal" REAL NOT NULL,
|
||||
"numPagos" INTEGER NOT NULL,
|
||||
"mensualidad" REAL NOT NULL,
|
||||
"tasaMoraMensual" REAL NOT NULL DEFAULT 0,
|
||||
"status" TEXT NOT NULL DEFAULT 'ACTIVO',
|
||||
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "Pago" (
|
||||
"id" TEXT NOT NULL PRIMARY KEY,
|
||||
"planId" TEXT NOT NULL,
|
||||
"numPago" INTEGER NOT NULL,
|
||||
"fechaVencimiento" DATETIME NOT NULL,
|
||||
"monto" REAL NOT NULL,
|
||||
"montoPagado" REAL NOT NULL DEFAULT 0,
|
||||
CONSTRAINT "Pago_planId_fkey" FOREIGN KEY ("planId") REFERENCES "PlanPago" ("id") ON DELETE CASCADE ON UPDATE CASCADE
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "AplicacionPago" (
|
||||
"id" TEXT NOT NULL PRIMARY KEY,
|
||||
"pagoId" TEXT NOT NULL,
|
||||
"fechaPago" DATETIME NOT NULL,
|
||||
"monto" REAL NOT NULL,
|
||||
"referencia" TEXT NOT NULL,
|
||||
CONSTRAINT "AplicacionPago_pagoId_fkey" FOREIGN KEY ("pagoId") REFERENCES "Pago" ("id") ON DELETE CASCADE ON UPDATE CASCADE
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "NotaPlan" (
|
||||
"id" TEXT NOT NULL PRIMARY KEY,
|
||||
"planId" TEXT NOT NULL,
|
||||
"fecha" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"texto" TEXT NOT NULL,
|
||||
CONSTRAINT "NotaPlan_planId_fkey" FOREIGN KEY ("planId") REFERENCES "PlanPago" ("id") ON DELETE CASCADE ON UPDATE CASCADE
|
||||
);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "Cotizacion_unidadCodigo_idx" ON "Cotizacion"("unidadCodigo");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "Cliente_curp_idx" ON "Cliente"("curp");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "Documento_clienteId_idx" ON "Documento"("clienteId");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "PlanPago_unidadCodigo_idx" ON "PlanPago"("unidadCodigo");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "Pago_planId_idx" ON "Pago"("planId");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "Pago_fechaVencimiento_idx" ON "Pago"("fechaVencimiento");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "AplicacionPago_pagoId_idx" ON "AplicacionPago"("pagoId");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "NotaPlan_planId_idx" ON "NotaPlan"("planId");
|
||||
3
api-gateway/prisma/migrations/migration_lock.toml
Normal file
3
api-gateway/prisma/migrations/migration_lock.toml
Normal file
@@ -0,0 +1,3 @@
|
||||
# Please do not edit this file manually
|
||||
# It should be added in your version-control system (e.g., Git)
|
||||
provider = "sqlite"
|
||||
123
api-gateway/prisma/schema.prisma
Normal file
123
api-gateway/prisma/schema.prisma
Normal file
@@ -0,0 +1,123 @@
|
||||
// FRASA API Gateway — persistencia local
|
||||
// Desarrollo: SQLite. Producción: Postgres 16 (cambiar provider y DATABASE_URL).
|
||||
|
||||
generator client {
|
||||
provider = "prisma-client-js"
|
||||
}
|
||||
|
||||
datasource db {
|
||||
provider = "sqlite" // prod: "postgresql"
|
||||
url = env("DATABASE_URL")
|
||||
}
|
||||
|
||||
model Cotizacion {
|
||||
id String @id @default(uuid())
|
||||
unidadCodigo String
|
||||
clienteNombre String
|
||||
clienteEmail String?
|
||||
clienteTelefono String?
|
||||
tipoPropuesta String // BANCARIO | INFONAVIT | CONTADO | SEMI_CONTADO
|
||||
acabados Boolean
|
||||
apartado Float // TODO prod: Decimal en Postgres
|
||||
descuento Float // fracción (0.02 = 2%)
|
||||
precioLista Float // TODO prod: Decimal en Postgres
|
||||
precioFinal Float
|
||||
enganche Float
|
||||
numMensualidades Int
|
||||
mensualidad Float? // null => N/A (Contado)
|
||||
pagoFinal Float
|
||||
mondayItemId String?
|
||||
createdAt DateTime @default(now())
|
||||
|
||||
@@index([unidadCodigo])
|
||||
}
|
||||
|
||||
model Cliente {
|
||||
id String @id @default(uuid())
|
||||
nombreCompleto String
|
||||
curp String
|
||||
rfc String
|
||||
email String?
|
||||
celular String?
|
||||
proyecto String
|
||||
unidadCodigo String?
|
||||
// Payload completo del alta como JSON serializado (SQLite no tiene Json nativo;
|
||||
// en Postgres puede migrarse a la columna Json sin cambiar el contrato).
|
||||
payloadJson String
|
||||
firmaPng String? // base64 opcional
|
||||
mondayContactoId String?
|
||||
mondayApartadoId String?
|
||||
createdAt DateTime @default(now())
|
||||
documentos Documento[]
|
||||
|
||||
@@index([curp])
|
||||
}
|
||||
|
||||
model Documento {
|
||||
id String @id @default(uuid())
|
||||
clienteId String
|
||||
cliente Cliente @relation(fields: [clienteId], references: [id], onDelete: Cascade)
|
||||
tipo String // INE | CONSTANCIA_RFC | CURP | COMPROBANTE_DOMICILIO | ACTA_NACIMIENTO | OTRO
|
||||
nombreArchivo String
|
||||
ruta String
|
||||
mime String
|
||||
tamano Int
|
||||
createdAt DateTime @default(now())
|
||||
|
||||
@@index([clienteId])
|
||||
}
|
||||
|
||||
model PlanPago {
|
||||
id String @id @default(uuid())
|
||||
ventaRef String?
|
||||
clienteNombre String
|
||||
clienteTelefono String?
|
||||
unidadCodigo String
|
||||
proyecto String
|
||||
fechaInicio DateTime // se normaliza a medianoche local
|
||||
engancheTotal Float // TODO prod: Decimal en Postgres
|
||||
numPagos Int
|
||||
mensualidad Float
|
||||
tasaMoraMensual Float @default(0)
|
||||
status String @default("ACTIVO") // ACTIVO | CANCELADO (LIQUIDADO se deriva)
|
||||
createdAt DateTime @default(now())
|
||||
notas NotaPlan[]
|
||||
pagos Pago[]
|
||||
|
||||
@@index([unidadCodigo])
|
||||
}
|
||||
|
||||
model Pago {
|
||||
id String @id @default(uuid())
|
||||
planId String
|
||||
plan PlanPago @relation(fields: [planId], references: [id], onDelete: Cascade)
|
||||
numPago Int
|
||||
fechaVencimiento DateTime
|
||||
monto Float
|
||||
montoPagado Float @default(0)
|
||||
aplicaciones AplicacionPago[]
|
||||
|
||||
@@index([planId])
|
||||
@@index([fechaVencimiento])
|
||||
}
|
||||
|
||||
model AplicacionPago {
|
||||
id String @id @default(uuid())
|
||||
pagoId String
|
||||
pago Pago @relation(fields: [pagoId], references: [id], onDelete: Cascade)
|
||||
fechaPago DateTime
|
||||
monto Float
|
||||
referencia String
|
||||
|
||||
@@index([pagoId])
|
||||
}
|
||||
|
||||
model NotaPlan {
|
||||
id String @id @default(uuid())
|
||||
planId String
|
||||
plan PlanPago @relation(fields: [planId], references: [id], onDelete: Cascade)
|
||||
fecha DateTime @default(now())
|
||||
texto String
|
||||
|
||||
@@index([planId])
|
||||
}
|
||||
Reference in New Issue
Block a user