feat: add Warehouses + Integrations modules, /admin/configuracion page
- Schema v0.3: Warehouse + Integration models (db push applied) - WarehousesModule: CRUD, set-default, multi-warehouse support - IntegrationsModule: 26 keys across 7 groups (payment, notifications, customs, courier, marketplace, compliance, warehouse) - AuthService: suite address pulled from default Warehouse in DB (env fallback) - AuthModule: imports WarehousesModule - Seed: creates default warehouse from WAREHOUSE_ADDRESS_* env vars - Web: /admin/configuracion page (3 tabs: Bodegas, Integraciones, Estado APIs) - Web: admin layout adds Configuracion nav link - api.ts: warehouses + integrations client methods
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
// Moraworld Imports — Schema v0.2 (Fase 0–1)
|
||||
// Moraworld Imports — Schema v0.3 (Fase 2 — Bodegas + Integraciones)
|
||||
// Multi-tenant por tenant_id en todas las tablas de negocio
|
||||
// Sincronizado con documentacion.html v1.1
|
||||
|
||||
@@ -90,6 +90,8 @@ model Tenant {
|
||||
preAlerts PreAlert[]
|
||||
tariffs Tariff[]
|
||||
b2bRequests B2BRequest[]
|
||||
warehouses Warehouse[]
|
||||
integrations Integration[]
|
||||
}
|
||||
|
||||
// ─── Usuarios ────────────────────────────────────────────────
|
||||
@@ -344,3 +346,57 @@ model AuditLog {
|
||||
@@index([tenantId, createdAt])
|
||||
@@index([userId])
|
||||
}
|
||||
|
||||
// ─── Bodegas (multi-bodega, doc §07 + §12) ───────────────────
|
||||
|
||||
/// Bodega física. Cada tenant puede tener varias; una es la default.
|
||||
model Warehouse {
|
||||
id String @id @default(cuid())
|
||||
tenantId String
|
||||
name String // ej: "Bodega NJ — City of Orange"
|
||||
street String // ej: "150 N Day St"
|
||||
city String // ej: "City of Orange"
|
||||
state String // ej: "NJ"
|
||||
zip String // ej: "07050"
|
||||
country String @default("US")
|
||||
phone String?
|
||||
email String?
|
||||
contactName String?
|
||||
/// Horario de operación (texto libre)
|
||||
schedule String?
|
||||
/// Capacidad aproximada (m²)
|
||||
capacityM2 Decimal? @db.Decimal(10, 2)
|
||||
isDefault Boolean @default(false)
|
||||
isActive Boolean @default(true)
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
tenant Tenant @relation(fields: [tenantId], references: [id], onDelete: Cascade)
|
||||
|
||||
@@index([tenantId])
|
||||
}
|
||||
|
||||
// ─── Integraciones (API keys por tenant) ─────────────────────
|
||||
|
||||
/// Configuración de integraciones externas (API keys, endpoints, webhooks).
|
||||
/// El valor se almacena en texto plano en dev; en prod debe cifrarse (AES-256).
|
||||
model Integration {
|
||||
id String @id @default(cuid())
|
||||
tenantId String
|
||||
/// Clave identificadora: ej. "whatsapp_token", "senae_endpoint", "stripe_secret"
|
||||
key String
|
||||
/// Valor (API key, URL, token — cifrar en prod)
|
||||
value String?
|
||||
/// Descripción legible del campo
|
||||
label String?
|
||||
/// Grupo de integración: "payment", "notifications", "customs", "courier", "warehouse"
|
||||
group String?
|
||||
isActive Boolean @default(false)
|
||||
updatedAt DateTime @updatedAt
|
||||
updatedBy String?
|
||||
|
||||
tenant Tenant @relation(fields: [tenantId], references: [id], onDelete: Cascade)
|
||||
|
||||
@@unique([tenantId, key])
|
||||
@@index([tenantId, group])
|
||||
}
|
||||
|
||||
@@ -50,6 +50,30 @@ async function main() {
|
||||
});
|
||||
console.log("✓ Tarifa base configurada ($3.50/lb, 2% seguro, 0.5% FODINFA, 15% IVA)");
|
||||
|
||||
// ─── Bodega predeterminada (doc §07 + §12) ────────────────
|
||||
const warehouseCount = await prisma.warehouse.count({ where: { tenantId: tenant.id } });
|
||||
if (warehouseCount === 0) {
|
||||
await prisma.warehouse.create({
|
||||
data: {
|
||||
tenantId: tenant.id,
|
||||
name: "Bodega NJ — City of Orange",
|
||||
street: process.env.WAREHOUSE_ADDRESS_STREET ?? "150 N Day St",
|
||||
city: process.env.WAREHOUSE_ADDRESS_CITY ?? "City of Orange",
|
||||
state: process.env.WAREHOUSE_ADDRESS_STATE ?? "NJ",
|
||||
zip: process.env.WAREHOUSE_ADDRESS_ZIP ?? "07050",
|
||||
country: process.env.WAREHOUSE_ADDRESS_COUNTRY ?? "US",
|
||||
phone: "+1-555-0100",
|
||||
contactName: "Operaciones Moraworld NJ",
|
||||
schedule: "Lun–Vie 8:00–17:00 ET",
|
||||
isDefault: true,
|
||||
isActive: true,
|
||||
},
|
||||
});
|
||||
console.log("✓ Bodega predeterminada creada: 150 N Day St, City of Orange, NJ 07050");
|
||||
} else {
|
||||
console.log("→ Bodega predeterminada ya existe");
|
||||
}
|
||||
|
||||
// ─── Usuarios de prueba (uno por cada rol del §06) ────────
|
||||
|
||||
const usersToSeed: Array<{
|
||||
|
||||
Reference in New Issue
Block a user