2095cb542a
FASE 1 — schema:
- Cliente.email opcional; nuevo telefonoNorm (E.164, único) para matchear por WhatsApp
- TipoServicio.jefeMecanicoId opcional (trabajos per-jefe; globales legacy quedan null)
- Turno.autoId/kilometros opcionales (reservas por WhatsApp sin auto/km)
- Nuevo modelo ConversacionWhatsapp (estado del bot por teléfono)
- admin/turnos/[id]: no permite COMPLETADO sin auto+km; Sheets tolera email nulo
FASE 3 — panel de trabajos del jefe:
- API jefe/servicios (GET/POST) y jefe/servicios/[id] (PATCH/DELETE), scoped al jefe
- DELETE desactiva si el trabajo ya tiene turnos asociados
- Página /jefe/servicios (alta/listado/activar/editar/eliminar) + nav Turnos|Trabajos
- Dashboard del jefe resiliente a turnos sin auto/km ("a completar")
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
139 lines
3.9 KiB
Plaintext
139 lines
3.9 KiB
Plaintext
generator client {
|
|
provider = "prisma-client-js"
|
|
}
|
|
|
|
datasource db {
|
|
provider = "postgresql"
|
|
}
|
|
|
|
model Cliente {
|
|
id String @id @default(cuid())
|
|
email String? @unique
|
|
nombre String
|
|
apellido String
|
|
telefono String
|
|
telefonoNorm String? @unique // teléfono en formato E.164, usado para matchear al que escribe por WhatsApp
|
|
createdAt DateTime @default(now())
|
|
|
|
autos Auto[]
|
|
turnos Turno[]
|
|
magicTokens MagicToken[]
|
|
conversaciones ConversacionWhatsapp[]
|
|
}
|
|
|
|
model Auto {
|
|
id String @id @default(cuid())
|
|
clienteId String
|
|
patente String @unique
|
|
marca String
|
|
modelo String
|
|
anio Int
|
|
nroChasis String?
|
|
createdAt DateTime @default(now())
|
|
|
|
cliente Cliente @relation(fields: [clienteId], references: [id])
|
|
turnos Turno[]
|
|
historial HistorialServicio[]
|
|
}
|
|
|
|
model TipoServicio {
|
|
id String @id @default(cuid())
|
|
nombre String
|
|
duracionHoras Float
|
|
activo Boolean @default(true)
|
|
jefeMecanicoId String? // trabajo cargado por un jefe mecánico puntual; null = servicio global (legacy)
|
|
|
|
jefeMecanico JefeMecanico? @relation(fields: [jefeMecanicoId], references: [id])
|
|
turnos Turno[]
|
|
historial HistorialServicio[]
|
|
}
|
|
|
|
model Turno {
|
|
id String @id @default(cuid())
|
|
clienteId String
|
|
autoId String?
|
|
tipoServicioId String
|
|
jefeMecanicoId String
|
|
fechaInicio DateTime
|
|
fechaFin DateTime
|
|
kilometros Int?
|
|
estado EstadoTurno @default(PENDIENTE)
|
|
comentario String?
|
|
createdAt DateTime @default(now())
|
|
|
|
cliente Cliente @relation(fields: [clienteId], references: [id])
|
|
auto Auto? @relation(fields: [autoId], references: [id])
|
|
tipoServicio TipoServicio @relation(fields: [tipoServicioId], references: [id])
|
|
jefeMecanico JefeMecanico @relation(fields: [jefeMecanicoId], references: [id])
|
|
}
|
|
|
|
model JefeMecanico {
|
|
id String @id @default(cuid())
|
|
nombre String
|
|
email String @unique
|
|
passwordHash String
|
|
slotsParalelos Int @default(1)
|
|
activo Boolean @default(true)
|
|
createdAt DateTime @default(now())
|
|
|
|
turnos Turno[]
|
|
servicios TipoServicio[]
|
|
}
|
|
|
|
model HistorialServicio {
|
|
id String @id @default(cuid())
|
|
autoId String
|
|
adminId String
|
|
tipoServicioId String?
|
|
descripcion String
|
|
observaciones String?
|
|
fechaRealizado DateTime
|
|
createdAt DateTime @default(now())
|
|
|
|
auto Auto @relation(fields: [autoId], references: [id])
|
|
admin Admin @relation(fields: [adminId], references: [id])
|
|
tipoServicio TipoServicio? @relation(fields: [tipoServicioId], references: [id])
|
|
}
|
|
|
|
model Admin {
|
|
id String @id @default(cuid())
|
|
email String @unique
|
|
nombre String
|
|
passwordHash String
|
|
createdAt DateTime @default(now())
|
|
|
|
historial HistorialServicio[]
|
|
}
|
|
|
|
model MagicToken {
|
|
id String @id @default(cuid())
|
|
clienteId String
|
|
token String @unique
|
|
expiresAt DateTime
|
|
usado Boolean @default(false)
|
|
createdAt DateTime @default(now())
|
|
|
|
cliente Cliente @relation(fields: [clienteId], references: [id])
|
|
}
|
|
|
|
enum EstadoTurno {
|
|
PENDIENTE
|
|
CONFIRMADO
|
|
CANCELADO
|
|
COMPLETADO
|
|
}
|
|
|
|
// Estado de la conversación del bot de WhatsApp, por número de teléfono.
|
|
// El webhook es stateless: acá se guarda en qué paso está y las selecciones parciales.
|
|
model ConversacionWhatsapp {
|
|
id String @id @default(cuid())
|
|
telefono String @unique // wa_id del cliente (E.164, sin '+')
|
|
paso String @default("INICIO") // paso de la máquina de estados
|
|
datos Json @default("{}") // selecciones parciales (mecánico, trabajo, slot, alta en curso, etc.)
|
|
clienteId String?
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
cliente Cliente? @relation(fields: [clienteId], references: [id])
|
|
}
|