1. Catalog Import (Generadores de Precios):
- ImportacionCatalogo model for tracking import history
- POST /api/importar - Process Excel/CSV files
- GET /api/importar - List import history
- ImportadorCatalogo component with:
- Template download for materials, labor, equipment
- Auto unit mapping (PZA→PIEZA, M2→METRO_CUADRADO, etc.)
- FSR auto-calculation for labor imports
- Import history view
2. Progress Tracking (Control de Avance por Partida):
- AvancePartida model with quantity, percentage, amount tracking
- CRUD API endpoints for avances
- GET /api/avances/resumen - Summary per presupuesto
- ControlAvancePartidas component with:
- Global progress summary cards
- Per-partida progress table with bars
- Register progress dialog
- Auto-calculation of accumulated values
3. Bug fixes:
- ClientDate component to fix hydration mismatch errors
- Date formatting now consistent between server and client
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1148 lines
32 KiB
Plaintext
1148 lines
32 KiB
Plaintext
// Prisma schema for Construction Management System
|
|
|
|
generator client {
|
|
provider = "prisma-client-js"
|
|
}
|
|
|
|
datasource db {
|
|
provider = "postgresql"
|
|
url = env("DATABASE_URL")
|
|
}
|
|
|
|
// ============== ENUMS ==============
|
|
|
|
enum Role {
|
|
ADMIN
|
|
GERENTE
|
|
SUPERVISOR
|
|
CONTADOR
|
|
EMPLEADO
|
|
}
|
|
|
|
enum EstadoObra {
|
|
PLANIFICACION
|
|
EN_PROGRESO
|
|
PAUSADA
|
|
COMPLETADA
|
|
CANCELADA
|
|
}
|
|
|
|
enum EstadoTarea {
|
|
PENDIENTE
|
|
EN_PROGRESO
|
|
COMPLETADA
|
|
BLOQUEADA
|
|
}
|
|
|
|
enum EstadoGasto {
|
|
PENDIENTE
|
|
APROBADO
|
|
RECHAZADO
|
|
PAGADO
|
|
}
|
|
|
|
enum CategoriaGasto {
|
|
MATERIALES
|
|
MANO_DE_OBRA
|
|
EQUIPOS
|
|
SUBCONTRATISTAS
|
|
PERMISOS
|
|
TRANSPORTE
|
|
SERVICIOS
|
|
OTROS
|
|
}
|
|
|
|
enum TipoFactura {
|
|
EMITIDA
|
|
RECIBIDA
|
|
}
|
|
|
|
enum EstadoFactura {
|
|
PENDIENTE
|
|
PAGADA
|
|
VENCIDA
|
|
CANCELADA
|
|
}
|
|
|
|
enum TipoMovimiento {
|
|
ENTRADA
|
|
SALIDA
|
|
AJUSTE
|
|
}
|
|
|
|
enum UnidadMedida {
|
|
UNIDAD
|
|
METRO
|
|
METRO_CUADRADO
|
|
METRO_CUBICO
|
|
KILOGRAMO
|
|
TONELADA
|
|
LITRO
|
|
BOLSA
|
|
PIEZA
|
|
ROLLO
|
|
CAJA
|
|
HORA
|
|
JORNADA
|
|
VIAJE
|
|
LOTE
|
|
GLOBAL
|
|
}
|
|
|
|
enum CategoriaManoObra {
|
|
PEON
|
|
AYUDANTE
|
|
OFICIAL_ALBANIL
|
|
OFICIAL_FIERRERO
|
|
OFICIAL_CARPINTERO
|
|
OFICIAL_PLOMERO
|
|
OFICIAL_ELECTRICISTA
|
|
CABO
|
|
MAESTRO_OBRA
|
|
OPERADOR_EQUIPO
|
|
}
|
|
|
|
enum TipoEquipo {
|
|
MAQUINARIA_PESADA
|
|
MAQUINARIA_LIGERA
|
|
HERRAMIENTA_ELECTRICA
|
|
TRANSPORTE
|
|
}
|
|
|
|
enum TipoInsumoAPU {
|
|
MATERIAL
|
|
MANO_OBRA
|
|
EQUIPO
|
|
HERRAMIENTA_MENOR
|
|
}
|
|
|
|
// ============== MODELS ==============
|
|
|
|
model User {
|
|
id String @id @default(cuid())
|
|
email String @unique
|
|
password String
|
|
nombre String
|
|
apellido String
|
|
role Role @default(EMPLEADO)
|
|
telefono String?
|
|
activo Boolean @default(true)
|
|
empresaId String
|
|
empresa Empresa @relation(fields: [empresaId], references: [id])
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
// Relations
|
|
gastosCreados Gasto[] @relation("GastoCreador")
|
|
gastosAprobados Gasto[] @relation("GastoAprobador")
|
|
tareasAsignadas TareaObra[]
|
|
obrasSupervision Obra[] @relation("ObraSupervisor")
|
|
registrosAvance RegistroAvance[]
|
|
fotosSubidas FotoAvance[]
|
|
bitacorasRegistradas BitacoraObra[]
|
|
asistenciasRegistradas Asistencia[]
|
|
ordenesCreadas OrdenCompra[] @relation("OrdenCreador")
|
|
ordenesAprobadas OrdenCompra[] @relation("OrdenAprobador")
|
|
pushSubscriptions PushSubscription[]
|
|
notificaciones Notificacion[]
|
|
actividades ActividadLog[]
|
|
avancesRegistrados AvancePartida[] @relation("AvancesRegistrados")
|
|
avancesAprobados AvancePartida[] @relation("AvancesAprobados")
|
|
importaciones ImportacionCatalogo[]
|
|
|
|
@@index([empresaId])
|
|
@@index([email])
|
|
}
|
|
|
|
model Empresa {
|
|
id String @id @default(cuid())
|
|
nombre String
|
|
rfc String? @unique
|
|
direccion String?
|
|
telefono String?
|
|
email String?
|
|
logo String?
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
// Relations
|
|
usuarios User[]
|
|
obras Obra[]
|
|
materiales Material[]
|
|
empleados Empleado[]
|
|
subcontratistas Subcontratista[]
|
|
clientes Cliente[]
|
|
// APU Relations
|
|
categoriasTrabajo CategoriaTrabajoAPU[]
|
|
equiposMaquinaria EquipoMaquinaria[]
|
|
apus AnalisisPrecioUnitario[]
|
|
configuracionAPU ConfiguracionAPU?
|
|
importaciones ImportacionCatalogo[]
|
|
}
|
|
|
|
model Cliente {
|
|
id String @id @default(cuid())
|
|
nombre String
|
|
rfc String?
|
|
direccion String?
|
|
telefono String?
|
|
email String?
|
|
empresaId String
|
|
empresa Empresa @relation(fields: [empresaId], references: [id])
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
// Relations
|
|
obras Obra[]
|
|
accesos ClienteAcceso[]
|
|
|
|
@@index([empresaId])
|
|
}
|
|
|
|
// ============== PORTAL DE CLIENTES ==============
|
|
|
|
model ClienteAcceso {
|
|
id String @id @default(cuid())
|
|
email String @unique
|
|
password String? // Hash de contraseña (opcional si usa token)
|
|
token String? @unique // Token de acceso único
|
|
tokenExpira DateTime? // Expiración del token
|
|
activo Boolean @default(true)
|
|
ultimoAcceso DateTime?
|
|
|
|
// Permisos
|
|
verFotos Boolean @default(true)
|
|
verAvances Boolean @default(true)
|
|
verGastos Boolean @default(false)
|
|
verDocumentos Boolean @default(true)
|
|
descargarPDF Boolean @default(true)
|
|
|
|
// Relaciones
|
|
clienteId String
|
|
cliente Cliente @relation(fields: [clienteId], references: [id], onDelete: Cascade)
|
|
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
@@index([clienteId])
|
|
@@index([token])
|
|
}
|
|
|
|
model Obra {
|
|
id String @id @default(cuid())
|
|
nombre String
|
|
descripcion String?
|
|
direccion String
|
|
estado EstadoObra @default(PLANIFICACION)
|
|
fechaInicio DateTime?
|
|
fechaFinPrevista DateTime?
|
|
fechaFinReal DateTime?
|
|
porcentajeAvance Float @default(0)
|
|
presupuestoTotal Float @default(0)
|
|
gastoTotal Float @default(0)
|
|
imagenPortada String?
|
|
empresaId String
|
|
empresa Empresa @relation(fields: [empresaId], references: [id])
|
|
clienteId String?
|
|
cliente Cliente? @relation(fields: [clienteId], references: [id])
|
|
supervisorId String?
|
|
supervisor User? @relation("ObraSupervisor", fields: [supervisorId], references: [id])
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
// Relations
|
|
fases FaseObra[]
|
|
presupuestos Presupuesto[]
|
|
gastos Gasto[]
|
|
facturas Factura[]
|
|
movimientosInv MovimientoInventario[]
|
|
asignaciones AsignacionEmpleado[]
|
|
contratos ContratoSubcontratista[]
|
|
registrosAvance RegistroAvance[]
|
|
fotos FotoAvance[]
|
|
bitacoras BitacoraObra[]
|
|
asistencias Asistencia[]
|
|
ordenesCompra OrdenCompra[]
|
|
actividadesLog ActividadLog[]
|
|
|
|
@@index([empresaId])
|
|
@@index([estado])
|
|
@@index([clienteId])
|
|
}
|
|
|
|
model FaseObra {
|
|
id String @id @default(cuid())
|
|
nombre String
|
|
descripcion String?
|
|
orden Int
|
|
fechaInicio DateTime?
|
|
fechaFin DateTime?
|
|
porcentajeAvance Float @default(0)
|
|
obraId String
|
|
obra Obra @relation(fields: [obraId], references: [id], onDelete: Cascade)
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
// Relations
|
|
tareas TareaObra[]
|
|
fotos FotoAvance[]
|
|
|
|
@@index([obraId])
|
|
}
|
|
|
|
model TareaObra {
|
|
id String @id @default(cuid())
|
|
nombre String
|
|
descripcion String?
|
|
estado EstadoTarea @default(PENDIENTE)
|
|
prioridad Int @default(1)
|
|
fechaInicio DateTime?
|
|
fechaFin DateTime?
|
|
porcentajeAvance Float @default(0)
|
|
faseId String
|
|
fase FaseObra @relation(fields: [faseId], references: [id], onDelete: Cascade)
|
|
asignadoId String?
|
|
asignado User? @relation(fields: [asignadoId], references: [id])
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
@@index([faseId])
|
|
@@index([estado])
|
|
@@index([asignadoId])
|
|
@@index([faseId, estado])
|
|
}
|
|
|
|
model RegistroAvance {
|
|
id String @id @default(cuid())
|
|
descripcion String
|
|
porcentaje Float
|
|
fotos String[]
|
|
obraId String
|
|
obra Obra @relation(fields: [obraId], references: [id], onDelete: Cascade)
|
|
registradoPorId String
|
|
registradoPor User @relation(fields: [registradoPorId], references: [id])
|
|
createdAt DateTime @default(now())
|
|
|
|
@@index([obraId])
|
|
@@index([registradoPorId])
|
|
@@index([obraId, createdAt])
|
|
}
|
|
|
|
model Presupuesto {
|
|
id String @id @default(cuid())
|
|
nombre String
|
|
descripcion String?
|
|
version Int @default(1)
|
|
total Float @default(0)
|
|
aprobado Boolean @default(false)
|
|
obraId String
|
|
obra Obra @relation(fields: [obraId], references: [id], onDelete: Cascade)
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
// Relations
|
|
partidas PartidaPresupuesto[]
|
|
|
|
@@index([obraId])
|
|
}
|
|
|
|
model PartidaPresupuesto {
|
|
id String @id @default(cuid())
|
|
codigo String
|
|
descripcion String
|
|
unidad UnidadMedida
|
|
cantidad Float
|
|
precioUnitario Float
|
|
total Float
|
|
categoria CategoriaGasto
|
|
presupuestoId String
|
|
presupuesto Presupuesto @relation(fields: [presupuestoId], references: [id], onDelete: Cascade)
|
|
apuId String?
|
|
apu AnalisisPrecioUnitario? @relation(fields: [apuId], references: [id])
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
// Relations
|
|
gastos Gasto[]
|
|
avances AvancePartida[]
|
|
|
|
@@index([presupuestoId])
|
|
@@index([apuId])
|
|
}
|
|
|
|
model AvancePartida {
|
|
id String @id @default(cuid())
|
|
fecha DateTime @default(now())
|
|
cantidadEjecutada Float // Cantidad ejecutada en este registro
|
|
cantidadAcumulada Float // Cantidad acumulada hasta este registro
|
|
porcentajeAvance Float // Porcentaje de avance (0-100)
|
|
montoEjecutado Float // Monto correspondiente a este avance
|
|
montoAcumulado Float // Monto acumulado hasta este registro
|
|
notas String?
|
|
fotos String[]
|
|
|
|
partidaId String
|
|
partida PartidaPresupuesto @relation(fields: [partidaId], references: [id], onDelete: Cascade)
|
|
registradoPorId String
|
|
registradoPor User @relation("AvancesRegistrados", fields: [registradoPorId], references: [id])
|
|
aprobadoPorId String?
|
|
aprobadoPor User? @relation("AvancesAprobados", fields: [aprobadoPorId], references: [id])
|
|
aprobado Boolean @default(false)
|
|
fechaAprobacion DateTime?
|
|
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
@@index([partidaId])
|
|
@@index([registradoPorId])
|
|
@@index([fecha])
|
|
}
|
|
|
|
model Gasto {
|
|
id String @id @default(cuid())
|
|
concepto String
|
|
descripcion String?
|
|
monto Float
|
|
fecha DateTime
|
|
categoria CategoriaGasto
|
|
estado EstadoGasto @default(PENDIENTE)
|
|
comprobante String?
|
|
notas String?
|
|
obraId String
|
|
obra Obra @relation(fields: [obraId], references: [id], onDelete: Cascade)
|
|
partidaId String?
|
|
partida PartidaPresupuesto? @relation(fields: [partidaId], references: [id])
|
|
creadoPorId String
|
|
creadoPor User @relation("GastoCreador", fields: [creadoPorId], references: [id])
|
|
aprobadoPorId String?
|
|
aprobadoPor User? @relation("GastoAprobador", fields: [aprobadoPorId], references: [id])
|
|
fechaAprobacion DateTime?
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
@@index([obraId])
|
|
@@index([estado])
|
|
@@index([categoria])
|
|
@@index([fecha])
|
|
}
|
|
|
|
model Factura {
|
|
id String @id @default(cuid())
|
|
numero String
|
|
tipo TipoFactura
|
|
concepto String
|
|
monto Float
|
|
iva Float @default(0)
|
|
total Float
|
|
fechaEmision DateTime
|
|
fechaVencimiento DateTime?
|
|
fechaPago DateTime?
|
|
estado EstadoFactura @default(PENDIENTE)
|
|
archivo String?
|
|
obraId String
|
|
obra Obra @relation(fields: [obraId], references: [id], onDelete: Cascade)
|
|
proveedorNombre String?
|
|
proveedorRfc String?
|
|
clienteNombre String?
|
|
clienteRfc String?
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
@@index([obraId])
|
|
@@index([tipo])
|
|
@@index([estado])
|
|
}
|
|
|
|
model Material {
|
|
id String @id @default(cuid())
|
|
codigo String
|
|
nombre String
|
|
descripcion String?
|
|
unidad UnidadMedida
|
|
precioUnitario Float
|
|
stockMinimo Float @default(0)
|
|
stockActual Float @default(0)
|
|
ubicacion String?
|
|
activo Boolean @default(true)
|
|
empresaId String
|
|
empresa Empresa @relation(fields: [empresaId], references: [id])
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
// Relations
|
|
movimientos MovimientoInventario[]
|
|
itemsOrden ItemOrdenCompra[]
|
|
insumosAPU InsumoAPU[]
|
|
|
|
@@unique([codigo, empresaId])
|
|
@@index([empresaId])
|
|
@@index([nombre])
|
|
@@index([activo, empresaId])
|
|
}
|
|
|
|
model MovimientoInventario {
|
|
id String @id @default(cuid())
|
|
tipo TipoMovimiento
|
|
cantidad Float
|
|
motivo String?
|
|
materialId String
|
|
material Material @relation(fields: [materialId], references: [id])
|
|
obraId String?
|
|
obra Obra? @relation(fields: [obraId], references: [id])
|
|
createdAt DateTime @default(now())
|
|
|
|
@@index([materialId])
|
|
@@index([obraId])
|
|
@@index([tipo])
|
|
}
|
|
|
|
model Empleado {
|
|
id String @id @default(cuid())
|
|
nombre String
|
|
apellido String
|
|
documento String?
|
|
telefono String?
|
|
email String?
|
|
puesto String
|
|
salarioBase Float?
|
|
fechaIngreso DateTime
|
|
fechaBaja DateTime?
|
|
activo Boolean @default(true)
|
|
empresaId String
|
|
empresa Empresa @relation(fields: [empresaId], references: [id])
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
// Relations
|
|
asignaciones AsignacionEmpleado[]
|
|
jornadas JornadaTrabajo[]
|
|
asistencias Asistencia[]
|
|
|
|
@@index([empresaId])
|
|
}
|
|
|
|
model AsignacionEmpleado {
|
|
id String @id @default(cuid())
|
|
fechaInicio DateTime
|
|
fechaFin DateTime?
|
|
activo Boolean @default(true)
|
|
empleadoId String
|
|
empleado Empleado @relation(fields: [empleadoId], references: [id])
|
|
obraId String
|
|
obra Obra @relation(fields: [obraId], references: [id])
|
|
createdAt DateTime @default(now())
|
|
|
|
@@index([empleadoId])
|
|
@@index([obraId])
|
|
}
|
|
|
|
model JornadaTrabajo {
|
|
id String @id @default(cuid())
|
|
fecha DateTime
|
|
horasRegular Float @default(0)
|
|
horasExtra Float @default(0)
|
|
notas String?
|
|
empleadoId String
|
|
empleado Empleado @relation(fields: [empleadoId], references: [id])
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
@@index([empleadoId])
|
|
@@index([fecha])
|
|
@@index([empleadoId, fecha])
|
|
}
|
|
|
|
model Subcontratista {
|
|
id String @id @default(cuid())
|
|
nombre String
|
|
rfc String?
|
|
especialidad String
|
|
telefono String?
|
|
email String?
|
|
direccion String?
|
|
activo Boolean @default(true)
|
|
empresaId String
|
|
empresa Empresa @relation(fields: [empresaId], references: [id])
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
// Relations
|
|
contratos ContratoSubcontratista[]
|
|
|
|
@@index([empresaId])
|
|
}
|
|
|
|
model ContratoSubcontratista {
|
|
id String @id @default(cuid())
|
|
descripcion String
|
|
montoContratado Float
|
|
montoPagado Float @default(0)
|
|
fechaInicio DateTime
|
|
fechaFin DateTime?
|
|
estado String @default("ACTIVO")
|
|
subcontratistaId String
|
|
subcontratista Subcontratista @relation(fields: [subcontratistaId], references: [id])
|
|
obraId String
|
|
obra Obra @relation(fields: [obraId], references: [id])
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
@@index([subcontratistaId])
|
|
@@index([obraId])
|
|
}
|
|
|
|
// ============== ÓRDENES DE COMPRA ==============
|
|
|
|
enum EstadoOrdenCompra {
|
|
BORRADOR
|
|
PENDIENTE
|
|
APROBADA
|
|
ENVIADA
|
|
RECIBIDA_PARCIAL
|
|
RECIBIDA
|
|
CANCELADA
|
|
}
|
|
|
|
enum PrioridadOrden {
|
|
BAJA
|
|
NORMAL
|
|
ALTA
|
|
URGENTE
|
|
}
|
|
|
|
model OrdenCompra {
|
|
id String @id @default(cuid())
|
|
numero String // Número de orden (OC-001, etc.)
|
|
|
|
// Estado y prioridad
|
|
estado EstadoOrdenCompra @default(BORRADOR)
|
|
prioridad PrioridadOrden @default(NORMAL)
|
|
|
|
// Fechas
|
|
fechaEmision DateTime @default(now())
|
|
fechaRequerida DateTime? // Fecha en que se necesitan los materiales
|
|
fechaAprobacion DateTime?
|
|
fechaEnvio DateTime?
|
|
fechaRecepcion DateTime?
|
|
|
|
// Proveedor
|
|
proveedorNombre String
|
|
proveedorRfc String?
|
|
proveedorContacto String?
|
|
proveedorTelefono String?
|
|
proveedorEmail String?
|
|
proveedorDireccion String?
|
|
|
|
// Totales
|
|
subtotal Float @default(0)
|
|
descuento Float @default(0)
|
|
iva Float @default(0)
|
|
total Float @default(0)
|
|
|
|
// Condiciones
|
|
condicionesPago String? // Ej: "Contado", "Crédito 30 días"
|
|
tiempoEntrega String? // Ej: "3-5 días hábiles"
|
|
lugarEntrega String? // Dirección de entrega
|
|
|
|
// Notas
|
|
notas String? @db.Text
|
|
notasInternas String? @db.Text
|
|
|
|
// Relaciones
|
|
obraId String
|
|
obra Obra @relation(fields: [obraId], references: [id], onDelete: Cascade)
|
|
creadoPorId String
|
|
creadoPor User @relation("OrdenCreador", fields: [creadoPorId], references: [id])
|
|
aprobadoPorId String?
|
|
aprobadoPor User? @relation("OrdenAprobador", fields: [aprobadoPorId], references: [id])
|
|
|
|
// Items
|
|
items ItemOrdenCompra[]
|
|
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
@@unique([numero, obraId])
|
|
@@index([obraId])
|
|
@@index([estado])
|
|
@@index([fechaEmision])
|
|
}
|
|
|
|
model ItemOrdenCompra {
|
|
id String @id @default(cuid())
|
|
|
|
// Descripción del item
|
|
codigo String? // Código del material
|
|
descripcion String
|
|
unidad UnidadMedida
|
|
|
|
// Cantidades
|
|
cantidad Float
|
|
cantidadRecibida Float @default(0)
|
|
|
|
// Precios
|
|
precioUnitario Float
|
|
descuento Float @default(0)
|
|
subtotal Float
|
|
|
|
// Relaciones
|
|
ordenId String
|
|
orden OrdenCompra @relation(fields: [ordenId], references: [id], onDelete: Cascade)
|
|
materialId String? // Opcional: vincular con catálogo de materiales
|
|
material Material? @relation(fields: [materialId], references: [id])
|
|
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
@@index([ordenId])
|
|
@@index([materialId])
|
|
}
|
|
|
|
// ============== CONTROL DE ASISTENCIA ==============
|
|
|
|
enum TipoAsistencia {
|
|
PRESENTE
|
|
AUSENTE
|
|
RETARDO
|
|
PERMISO
|
|
INCAPACIDAD
|
|
VACACIONES
|
|
}
|
|
|
|
model Asistencia {
|
|
id String @id @default(cuid())
|
|
fecha DateTime @db.Date
|
|
|
|
// Estado de asistencia
|
|
tipo TipoAsistencia @default(PRESENTE)
|
|
|
|
// Registro de entrada
|
|
horaEntrada DateTime?
|
|
latitudEntrada Float?
|
|
longitudEntrada Float?
|
|
|
|
// Registro de salida
|
|
horaSalida DateTime?
|
|
latitudSalida Float?
|
|
longitudSalida Float?
|
|
|
|
// Horas trabajadas (calculadas)
|
|
horasTrabajadas Float?
|
|
horasExtra Float @default(0)
|
|
|
|
// Notas y observaciones
|
|
notas String?
|
|
motivoAusencia String?
|
|
|
|
// Relaciones
|
|
empleadoId String
|
|
empleado Empleado @relation(fields: [empleadoId], references: [id], onDelete: Cascade)
|
|
obraId String
|
|
obra Obra @relation(fields: [obraId], references: [id], onDelete: Cascade)
|
|
registradoPorId String
|
|
registradoPor User @relation(fields: [registradoPorId], references: [id])
|
|
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
@@unique([empleadoId, obraId, fecha])
|
|
@@index([empleadoId])
|
|
@@index([obraId])
|
|
@@index([fecha])
|
|
@@index([tipo])
|
|
}
|
|
|
|
// ============== BITÁCORA DE OBRA ==============
|
|
|
|
enum CondicionClima {
|
|
SOLEADO
|
|
NUBLADO
|
|
PARCIALMENTE_NUBLADO
|
|
LLUVIA_LIGERA
|
|
LLUVIA_FUERTE
|
|
TORMENTA
|
|
VIENTO_FUERTE
|
|
FRIO_EXTREMO
|
|
CALOR_EXTREMO
|
|
}
|
|
|
|
model BitacoraObra {
|
|
id String @id @default(cuid())
|
|
fecha DateTime @db.Date
|
|
|
|
// Condiciones climáticas
|
|
clima CondicionClima
|
|
temperaturaMin Float?
|
|
temperaturaMax Float?
|
|
condicionesExtra String? // Notas adicionales del clima
|
|
|
|
// Personal en obra
|
|
personalPropio Int @default(0)
|
|
personalSubcontrato Int @default(0)
|
|
personalDetalle String? // Descripción del personal
|
|
|
|
// Actividades del día
|
|
actividadesRealizadas String @db.Text
|
|
actividadesPendientes String? @db.Text
|
|
|
|
// Materiales
|
|
materialesUtilizados String? @db.Text
|
|
materialesRecibidos String? @db.Text
|
|
|
|
// Equipo y maquinaria
|
|
equipoUtilizado String? @db.Text
|
|
|
|
// Incidentes y observaciones
|
|
incidentes String? @db.Text
|
|
observaciones String? @db.Text
|
|
|
|
// Seguridad
|
|
incidentesSeguridad String? @db.Text
|
|
platicaSeguridad Boolean @default(false)
|
|
temaSeguridad String?
|
|
|
|
// Visitas
|
|
visitasInspeccion String? @db.Text
|
|
|
|
// Relaciones
|
|
obraId String
|
|
obra Obra @relation(fields: [obraId], references: [id], onDelete: Cascade)
|
|
registradoPorId String
|
|
registradoPor User @relation(fields: [registradoPorId], references: [id])
|
|
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
@@unique([obraId, fecha])
|
|
@@index([obraId])
|
|
@@index([fecha])
|
|
}
|
|
|
|
// ============== FOTOS DE AVANCE ==============
|
|
|
|
model FotoAvance {
|
|
id String @id @default(cuid())
|
|
url String // Ruta del archivo
|
|
thumbnail String? // Ruta de la miniatura
|
|
titulo String?
|
|
descripcion String?
|
|
fechaCaptura DateTime @default(now())
|
|
|
|
// Geolocalización
|
|
latitud Float?
|
|
longitud Float?
|
|
direccionGeo String? // Dirección obtenida por geocoding
|
|
|
|
// Metadatos
|
|
tamanio Int? // Tamaño en bytes
|
|
tipo String? // MIME type (image/jpeg, etc.)
|
|
ancho Int? // Width en pixels
|
|
alto Int? // Height en pixels
|
|
|
|
// Relaciones
|
|
obraId String
|
|
obra Obra @relation(fields: [obraId], references: [id], onDelete: Cascade)
|
|
faseId String?
|
|
fase FaseObra? @relation(fields: [faseId], references: [id])
|
|
subidoPorId String
|
|
subidoPor User @relation(fields: [subidoPorId], references: [id])
|
|
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
@@index([obraId])
|
|
@@index([faseId])
|
|
@@index([fechaCaptura])
|
|
@@index([subidoPorId])
|
|
@@index([obraId, fechaCaptura])
|
|
}
|
|
|
|
// ============== NOTIFICACIONES PUSH ==============
|
|
|
|
enum TipoNotificacion {
|
|
TAREA_ASIGNADA
|
|
TAREA_COMPLETADA
|
|
GASTO_PENDIENTE
|
|
GASTO_APROBADO
|
|
ORDEN_APROBADA
|
|
AVANCE_REGISTRADO
|
|
RECORDATORIO
|
|
ALERTA_INVENTARIO
|
|
GENERAL
|
|
}
|
|
|
|
model PushSubscription {
|
|
id String @id @default(cuid())
|
|
endpoint String @unique
|
|
p256dh String
|
|
auth String
|
|
activo Boolean @default(true)
|
|
|
|
// Preferencias de notificación
|
|
notifyTareas Boolean @default(true)
|
|
notifyGastos Boolean @default(true)
|
|
notifyOrdenes Boolean @default(true)
|
|
notifyAvances Boolean @default(true)
|
|
notifyAlertas Boolean @default(true)
|
|
|
|
userId String
|
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
@@index([userId])
|
|
}
|
|
|
|
model Notificacion {
|
|
id String @id @default(cuid())
|
|
tipo TipoNotificacion
|
|
titulo String
|
|
mensaje String
|
|
url String? // URL para navegar al hacer clic
|
|
leida Boolean @default(false)
|
|
enviada Boolean @default(false)
|
|
|
|
// Datos adicionales en JSON
|
|
metadata String? @db.Text
|
|
|
|
userId String
|
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
|
|
createdAt DateTime @default(now())
|
|
|
|
@@index([userId])
|
|
@@index([leida])
|
|
@@index([createdAt])
|
|
}
|
|
|
|
// ============== LOG DE ACTIVIDADES ==============
|
|
|
|
enum TipoActividad {
|
|
OBRA_CREADA
|
|
OBRA_ACTUALIZADA
|
|
OBRA_ESTADO_CAMBIADO
|
|
FASE_CREADA
|
|
TAREA_CREADA
|
|
TAREA_ASIGNADA
|
|
TAREA_COMPLETADA
|
|
TAREA_ESTADO_CAMBIADO
|
|
GASTO_CREADO
|
|
GASTO_APROBADO
|
|
GASTO_RECHAZADO
|
|
ORDEN_CREADA
|
|
ORDEN_APROBADA
|
|
ORDEN_ENVIADA
|
|
ORDEN_RECIBIDA
|
|
AVANCE_REGISTRADO
|
|
FOTO_SUBIDA
|
|
BITACORA_REGISTRADA
|
|
MATERIAL_MOVIMIENTO
|
|
USUARIO_ASIGNADO
|
|
COMENTARIO_AGREGADO
|
|
DOCUMENTO_SUBIDO
|
|
}
|
|
|
|
model ActividadLog {
|
|
id String @id @default(cuid())
|
|
tipo TipoActividad
|
|
descripcion String
|
|
detalles String? @db.Text // JSON con datos adicionales
|
|
|
|
// Entidad afectada
|
|
entidadTipo String? // "obra", "tarea", "gasto", etc.
|
|
entidadId String?
|
|
entidadNombre String?
|
|
|
|
// Contexto
|
|
obraId String?
|
|
obra Obra? @relation(fields: [obraId], references: [id], onDelete: SetNull)
|
|
|
|
// Usuario que realizó la acción
|
|
userId String?
|
|
user User? @relation(fields: [userId], references: [id], onDelete: SetNull)
|
|
|
|
// Empresa para filtrar
|
|
empresaId String
|
|
|
|
// Metadatos de IP/dispositivo (opcional)
|
|
ipAddress String?
|
|
userAgent String?
|
|
|
|
createdAt DateTime @default(now())
|
|
|
|
@@index([obraId])
|
|
@@index([userId])
|
|
@@index([empresaId])
|
|
@@index([tipo])
|
|
@@index([createdAt])
|
|
@@index([obraId, createdAt])
|
|
@@index([empresaId, createdAt])
|
|
}
|
|
|
|
// ============== ANÁLISIS DE PRECIOS UNITARIOS (APU) ==============
|
|
|
|
model CategoriaTrabajoAPU {
|
|
id String @id @default(cuid())
|
|
codigo String
|
|
nombre String
|
|
categoria CategoriaManoObra
|
|
salarioDiario Float
|
|
factorIMSS Float @default(0.2675)
|
|
factorINFONAVIT Float @default(0.05)
|
|
factorRetiro Float @default(0.02)
|
|
factorVacaciones Float @default(0.0411)
|
|
factorPrimaVac Float @default(0.0103)
|
|
factorAguinaldo Float @default(0.0411)
|
|
factorSalarioReal Float // Calculado: 1 + suma de factores
|
|
salarioReal Float // salarioDiario * FSR
|
|
activo Boolean @default(true)
|
|
empresaId String
|
|
empresa Empresa @relation(fields: [empresaId], references: [id])
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
// Relations
|
|
insumosAPU InsumoAPU[]
|
|
|
|
@@unique([codigo, empresaId])
|
|
@@index([empresaId])
|
|
@@index([categoria])
|
|
}
|
|
|
|
model EquipoMaquinaria {
|
|
id String @id @default(cuid())
|
|
codigo String
|
|
nombre String
|
|
tipo TipoEquipo
|
|
valorAdquisicion Float
|
|
vidaUtilHoras Float
|
|
valorRescate Float @default(0)
|
|
consumoCombustible Float? // Litros por hora
|
|
precioCombustible Float? // Precio por litro
|
|
factorMantenimiento Float @default(0.60)
|
|
costoOperador Float? // Costo por hora del operador
|
|
costoHorario Float // Calculado
|
|
activo Boolean @default(true)
|
|
empresaId String
|
|
empresa Empresa @relation(fields: [empresaId], references: [id])
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
// Relations
|
|
insumosAPU InsumoAPU[]
|
|
|
|
@@unique([codigo, empresaId])
|
|
@@index([empresaId])
|
|
@@index([tipo])
|
|
}
|
|
|
|
model AnalisisPrecioUnitario {
|
|
id String @id @default(cuid())
|
|
codigo String
|
|
descripcion String
|
|
unidad UnidadMedida
|
|
rendimientoDiario Float? // Cantidad de unidades por jornada
|
|
costoMateriales Float @default(0)
|
|
costoManoObra Float @default(0)
|
|
costoEquipo Float @default(0)
|
|
costoHerramienta Float @default(0)
|
|
costoDirecto Float @default(0)
|
|
porcentajeIndirectos Float @default(0)
|
|
costoIndirectos Float @default(0)
|
|
porcentajeUtilidad Float @default(0)
|
|
costoUtilidad Float @default(0)
|
|
precioUnitario Float @default(0)
|
|
activo Boolean @default(true)
|
|
empresaId String
|
|
empresa Empresa @relation(fields: [empresaId], references: [id])
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
// Relations
|
|
insumos InsumoAPU[]
|
|
partidas PartidaPresupuesto[]
|
|
|
|
@@unique([codigo, empresaId])
|
|
@@index([empresaId])
|
|
@@index([unidad])
|
|
}
|
|
|
|
model InsumoAPU {
|
|
id String @id @default(cuid())
|
|
tipo TipoInsumoAPU
|
|
descripcion String
|
|
unidad UnidadMedida
|
|
cantidad Float
|
|
desperdicio Float @default(0) // Porcentaje
|
|
cantidadConDesperdicio Float
|
|
rendimiento Float? // Para mano de obra: unidades por jornada
|
|
precioUnitario Float
|
|
importe Float
|
|
apuId String
|
|
apu AnalisisPrecioUnitario @relation(fields: [apuId], references: [id], onDelete: Cascade)
|
|
materialId String?
|
|
material Material? @relation(fields: [materialId], references: [id])
|
|
categoriaManoObraId String?
|
|
categoriaManoObra CategoriaTrabajoAPU? @relation(fields: [categoriaManoObraId], references: [id])
|
|
equipoId String?
|
|
equipo EquipoMaquinaria? @relation(fields: [equipoId], references: [id])
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
@@index([apuId])
|
|
@@index([tipo])
|
|
@@index([materialId])
|
|
@@index([categoriaManoObraId])
|
|
@@index([equipoId])
|
|
}
|
|
|
|
model ConfiguracionAPU {
|
|
id String @id @default(cuid())
|
|
porcentajeHerramientaMenor Float @default(3)
|
|
porcentajeIndirectos Float @default(8)
|
|
porcentajeUtilidad Float @default(10)
|
|
empresaId String @unique
|
|
empresa Empresa @relation(fields: [empresaId], references: [id])
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
}
|
|
|
|
// ============== IMPORTACIÓN DE CATÁLOGOS ==============
|
|
|
|
enum TipoImportacion {
|
|
MATERIALES
|
|
MANO_OBRA
|
|
EQUIPOS
|
|
}
|
|
|
|
enum EstadoImportacion {
|
|
PENDIENTE
|
|
PROCESANDO
|
|
COMPLETADA
|
|
ERROR
|
|
}
|
|
|
|
model ImportacionCatalogo {
|
|
id String @id @default(cuid())
|
|
tipo TipoImportacion
|
|
nombreArchivo String
|
|
estado EstadoImportacion @default(PENDIENTE)
|
|
registrosTotal Int @default(0)
|
|
registrosCreados Int @default(0)
|
|
registrosActualizados Int @default(0)
|
|
registrosError Int @default(0)
|
|
errores String? // JSON con detalles de errores
|
|
fuenteDatos String? // BIMSA, Proveedor, Manual, etc.
|
|
empresaId String
|
|
empresa Empresa @relation(fields: [empresaId], references: [id])
|
|
creadoPorId String
|
|
creadoPor User @relation(fields: [creadoPorId], references: [id])
|
|
createdAt DateTime @default(now())
|
|
completadoAt DateTime?
|
|
|
|
@@index([empresaId])
|
|
@@index([tipo])
|
|
@@index([estado])
|
|
}
|