Construire le schéma Prisma qui deviendra la source de vérité unique du système.
À partir de ce schéma seront générés :
Le schéma doit refléter l'intégralité des domaines définis dans les 20 sprints.
Le schéma Prisma doit supporter :
Multi-tenant Audit Historisation Soft Delete RBAC CRM Catalogue Réservations Paiements Contrats OTA IA Revenue Management Internationalisation Sécurité Enterprise
Ne jamais commencer par les 80 tables.
Construire par couches.
Construire en premier :
Tenant User Role Permission UserRole RolePermission
Ces tables sont nécessaires partout.
Créer :
Address Country Language Currency Timezone
Créer :
Property PropertyType PropertyMedia PropertyFeature PropertyAvailability PropertyRate
Créer :
Reservation ReservationGuest ReservationStatus ReservationEvent
Créer :
Contract ContractTemplate ContractSignature Document
Créer :
Payment Invoice Refund AccountingEntry
Créer :
Lead Customer Activity Task Pipeline
Créer :
Owner OwnerProperty OwnerDocument
Créer :
Notification NotificationTemplate Message Campaign
Créer :
AuditLog FeatureFlag CustomField Workflow
Créer :
AiConversation AiMessage KnowledgeDocument AutomationRule
Créer :
Channel ChannelConnection PropertyDistribution ChannelReservation
Créer :
PricingRule DynamicPrice RevenueSimulation CompetitorSnapshot
Créer :
Consent SecurityPolicy Risk ComplianceAudit
Créer :
Translation CurrencyRate Region EnterpriseLicense
Au lieu d'un unique fichier géant :
prisma/ ├── schema.prisma │ ├── models/ │ │ ├── tenant.prisma │ ├── user.prisma │ ├── property.prisma │ ├── reservation.prisma │ ├── contract.prisma │ ├── payment.prisma │ ├── crm.prisma │ ├── owner.prisma │ ├── notification.prisma │ ├── audit.prisma │ ├── ai.prisma │ ├── channel.prisma │ ├── revenue.prisma │ ├── security.prisma │ └── localization.prisma
Version minimale :
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
Créer :
prisma/models/tenant.prisma
model Tenant {
id String @id @default(uuid())
code String @unique
name String
status TenantStatus
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
users User[]
properties Property[]
}
enum TenantStatus {
ACTIVE
SUSPENDED
TRIAL
ARCHIVED
}
Créer :
prisma/models/user.prisma
model User {
id String @id @default(uuid())
tenantId String
email String @unique
passwordHash String
firstName String
lastName String
active Boolean @default(true)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
tenant Tenant @relation(
fields:[tenantId],
references:[id]
)
roles UserRole[]
}
model Role {
id String @id @default(uuid())
code String @unique
name String
userRoles UserRole[]
permissions RolePermission[]
}
model Permission {
id String @id @default(uuid())
code String @unique
name String
rolePermissions RolePermission[]
}
model UserRole {
userId String
roleId String
assignedAt DateTime @default(now())
user User @relation(
fields:[userId],
references:[id]
)
role Role @relation(
fields:[roleId],
references:[id]
)
@@id([userId, roleId])
}
model RolePermission {
roleId String
permissionId String
role Role @relation(
fields:[roleId],
references:[id]
)
permission Permission @relation(
fields:[permissionId],
references:[id]
)
@@id([roleId, permissionId])
}
Après chaque bloc important :
npx prisma format
npx prisma validate
npx prisma generate
npx prisma migrate dev \
--name init_security
Ouvrir :
npx prisma studio
Vérifier la présence :
Tenant User Role Permission UserRole RolePermission
Vous avez raison.
La version précédente contient :
mais il manque plusieurs éléments indispensables pour un véritable système Enterprise :
Sans ces éléments, le Sprint 1 (Authentification) et le Sprint 19 (Sécurité Enterprise) ne pourront pas être implémentés proprement.
model User {
id String @id @default(uuid())
tenantId String
email String @unique
passwordHash String
firstName String
lastName String
phone String?
avatarUrl String?
languageCode String?
timezoneCode String?
active Boolean @default(true)
emailVerified Boolean @default(false)
lastLoginAt DateTime?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
deletedAt DateTime?
tenant Tenant @relation(
fields:[tenantId],
references:[id]
)
roles UserRole[]
sessions UserSession[]
refreshTokens RefreshToken[]
preferences UserPreference?
loginHistory LoginHistory[]
mfaMethods UserMfaMethod[]
}
model UserPreference {
id String @id @default(uuid())
userId String @unique
theme String?
language String?
timezone String?
notifications Json?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
user User @relation(
fields:[userId],
references:[id]
)
}
model UserSession {
id String @id @default(uuid())
userId String
ipAddress String?
userAgent String?
country String?
city String?
lastActivityAt DateTime
expiresAt DateTime
revokedAt DateTime?
createdAt DateTime @default(now())
user User @relation(
fields:[userId],
references:[id]
)
}
model RefreshToken {
id String @id @default(uuid())
userId String
tokenHash String
expiresAt DateTime
revokedAt DateTime?
createdAt DateTime @default(now())
user User @relation(
fields:[userId],
references:[id]
)
}
model LoginHistory {
id String @id @default(uuid())
userId String
success Boolean
ipAddress String?
userAgent String?
country String?
city String?
createdAt DateTime @default(now())
user User @relation(
fields:[userId],
references:[id]
)
}
model UserMfaMethod {
id String @id @default(uuid())
userId String
methodType String
secret String?
enabled Boolean @default(true)
createdAt DateTime @default(now())
user User @relation(
fields:[userId],
references:[id]
)
}
model Role {
id String @id @default(uuid())
code String @unique
name String
description String?
systemRole Boolean @default(false)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
userRoles UserRole[]
permissions RolePermission[]
}
model Permission {
id String @id @default(uuid())
code String @unique
name String
description String?
module String
createdAt DateTime @default(now())
rolePermissions RolePermission[]
}
AUTH_LOGIN AUTH_REGISTER AUTH_RESET_PASSWORD AUTH_MANAGE_USERS
USER_READ USER_CREATE USER_UPDATE USER_DELETE
PROPERTY_READ PROPERTY_CREATE PROPERTY_UPDATE PROPERTY_DELETE PROPERTY_PUBLISH
RESERVATION_READ RESERVATION_CREATE RESERVATION_UPDATE RESERVATION_CANCEL
CONTRACT_READ CONTRACT_CREATE CONTRACT_SIGN
PAYMENT_READ PAYMENT_CREATE PAYMENT_REFUND
CRM_READ CRM_WRITE CRM_EXPORT
ADMIN_READ ADMIN_WRITE ADMIN_AUDIT ADMIN_TENANT
Accès complet plateforme.
Administration d'agence.
Gestion opérationnelle.
Extranet propriétaire.
Collaborateur.
Client final.
@@index([tenantId]) @@index([email]) @@index([active])
@@index([userId]) @@index([createdAt])
@@index([userId]) @@index([expiresAt])
Property PropertyType PropertyStatus PropertyAddress PropertyFeature PropertyMedia PropertyAvailability PropertyRate PropertyOwner
model Property {
id String @id @default(uuid())
tenantId String
propertyTypeId String
code String @unique
reference String?
title String
slug String @unique
description String?
shortDescription String?
maxGuests Int
bedrooms Int
bathrooms Int
area Decimal? @db.Decimal(10,2)
floor Int?
constructionYear Int?
checkInTime String?
checkOutTime String?
active Boolean @default(true)
published Boolean @default(false)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
deletedAt DateTime?
tenant Tenant @relation(
fields:[tenantId],
references:[id]
)
propertyType PropertyType @relation(
fields:[propertyTypeId],
references:[id]
)
address PropertyAddress?
features PropertyFeature[]
media PropertyMedia[]
availabilities PropertyAvailability[]
rates PropertyRate[]
owners PropertyOwner[]
reservations Reservation[]
}
model PropertyType {
id String @id @default(uuid())
code String @unique
name String
properties Property[]
}
HOUSE APARTMENT VILLA STUDIO LOFT CHALET COTTAGE MOBILE_HOME
model PropertyAddress {
id String @id @default(uuid())
propertyId String @unique
addressLine1 String
addressLine2 String?
postalCode String
city String
state String?
countryCode String
latitude Decimal? @db.Decimal(10,7)
longitude Decimal? @db.Decimal(10,7)
property Property @relation(
fields:[propertyId],
references:[id]
)
}
model PropertyFeature {
id String @id @default(uuid())
propertyId String
featureCode String
featureValue String?
property Property @relation(
fields:[propertyId],
references:[id]
)
}
POOL WIFI PARKING AIR_CONDITIONING TERRACE SEA_VIEW PET_ALLOWED
model PropertyMedia {
id String @id @default(uuid())
propertyId String
fileName String
fileUrl String
mediaType String
position Int
isCover Boolean @default(false)
createdAt DateTime @default(now())
property Property @relation(
fields:[propertyId],
references:[id]
)
}
IMAGE VIDEO VIRTUAL_TOUR DOCUMENT
model PropertyAvailability {
id String @id @default(uuid())
propertyId String
startDate DateTime
endDate DateTime
status AvailabilityStatus
property Property @relation(
fields:[propertyId],
references:[id]
)
}
enum AvailabilityStatus {
AVAILABLE
RESERVED
BLOCKED
MAINTENANCE
}
model PropertyRate {
id String @id @default(uuid())
propertyId String
startDate DateTime
endDate DateTime
nightlyRate Decimal @db.Decimal(10,2)
weekendRate Decimal? @db.Decimal(10,2)
cleaningFee Decimal? @db.Decimal(10,2)
securityDeposit Decimal? @db.Decimal(10,2)
currencyCode String
property Property @relation(
fields:[propertyId],
references:[id]
)
}
model PropertyOwner {
propertyId String
ownerId String
ownershipRate Decimal @db.Decimal(5,2)
property Property @relation(
fields:[propertyId],
references:[id]
)
owner Owner @relation(
fields:[ownerId],
references:[id]
)
@@id([propertyId, ownerId])
}
@@index([tenantId]) @@index([propertyTypeId]) @@index([published]) @@index([active]) @@index([title]) @@index([slug])
@@index([propertyId]) @@index([startDate]) @@index([endDate])
@@index([propertyId]) @@index([startDate]) @@index([endDate])
Owner OwnerAddress OwnerDocument OwnerBankAccount PropertyOwner
model Owner {
id String @id @default(uuid())
tenantId String
code String @unique
companyName String?
firstName String?
lastName String?
email String
phone String?
mobile String?
taxIdentifier String?
vatNumber String?
active Boolean @default(true)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
deletedAt DateTime?
tenant Tenant @relation(
fields:[tenantId],
references:[id]
)
address OwnerAddress?
documents OwnerDocument[]
bankAccounts OwnerBankAccount[]
properties PropertyOwner[]
}
model OwnerAddress {
id String @id @default(uuid())
ownerId String @unique
addressLine1 String
addressLine2 String?
postalCode String
city String
state String?
countryCode String
owner Owner @relation(
fields:[ownerId],
references:[id]
)
}
model OwnerDocument {
id String @id @default(uuid())
ownerId String
documentType String
fileUrl String
createdAt DateTime @default(now())
owner Owner @relation(
fields:[ownerId],
references:[id]
)
}
IDENTITY TAX_DOCUMENT MANDATE BANK_DETAILS INSURANCE
model OwnerBankAccount {
id String @id @default(uuid())
ownerId String
iban String
bic String?
accountHolder String
active Boolean @default(true)
owner Owner @relation(
fields:[ownerId],
references:[id]
)
}
Reservation ReservationGuest ReservationStatusHistory ReservationEvent ReservationPricing
model Reservation {
id String @id @default(uuid())
tenantId String
propertyId String
customerId String?
reference String @unique
status ReservationStatus
checkInDate DateTime
checkOutDate DateTime
nights Int
adults Int
children Int
infants Int
totalGuests Int
notes String?
source ReservationSource
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
cancelledAt DateTime?
tenant Tenant @relation(
fields:[tenantId],
references:[id]
)
property Property @relation(
fields:[propertyId],
references:[id]
)
guests ReservationGuest[]
events ReservationEvent[]
statusHistory ReservationStatusHistory[]
pricing ReservationPricing?
}
enum ReservationStatus {
DRAFT
PENDING
CONFIRMED
SIGNED
PAID
CHECKED_IN
COMPLETED
CANCELLED
}
enum ReservationSource {
WEBSITE
BACKOFFICE
AIRBNB
BOOKING
VRBO
API
}
model ReservationGuest {
id String @id @default(uuid())
reservationId String
firstName String
lastName String
birthDate DateTime?
email String?
phone String?
isPrimary Boolean @default(false)
reservation Reservation @relation(
fields:[reservationId],
references:[id]
)
}
model ReservationPricing {
id String @id @default(uuid())
reservationId String @unique
nightlyAmount Decimal @db.Decimal(10,2)
cleaningFee Decimal @db.Decimal(10,2)
touristTax Decimal @db.Decimal(10,2)
discountAmount Decimal @db.Decimal(10,2)
totalAmount Decimal @db.Decimal(10,2)
currencyCode String
reservation Reservation @relation(
fields:[reservationId],
references:[id]
)
}
model ReservationEvent {
id String @id @default(uuid())
reservationId String
eventType String
payload Json?
createdAt DateTime @default(now())
reservation Reservation @relation(
fields:[reservationId],
references:[id]
)
}
CREATED CONFIRMED SIGNED PAID CHECK_IN CHECK_OUT CANCELLED
model ReservationStatusHistory {
id String @id @default(uuid())
reservationId String
previousStatus ReservationStatus?
newStatus ReservationStatus
changedAt DateTime @default(now())
reservation Reservation @relation(
fields:[reservationId],
references:[id]
)
}
Ajouter :
reservations Reservation[]
Ajouter :
owners Owner[] reservations Reservation[]
@@index([tenantId]) @@index([email]) @@index([active])
@@index([tenantId]) @@index([propertyId]) @@index([status]) @@index([checkInDate]) @@index([checkOutDate]) @@index([reference])
@@index([reservationId])
npx prisma format npx prisma validate npx prisma generate
npx prisma migrate dev \
--name owners_and_reservations
À ce stade, le noyau fonctionnel est enfin présent :
Tenant User Role Permission Owner Property Reservation
avec :
Catalogue Immobilier Propriétaires Réservations RBAC Multi-tenant
Construire le premier flux métier complet de la plateforme :
Client ↓ Réservation ↓ Contrat ↓ Signature ↓ Paiement ↓ Facture ↓ Comptabilité
Cette phase couvre :
Avant cette phase, le schéma doit déjà contenir :
Tenant User Role Permission Owner Property Reservation
Avant les contrats et paiements, il faut définir le client.
model Customer {
id String @id @default(uuid())
tenantId String
customerNumber String @unique
firstName String
lastName String
email String
phone String?
mobile String?
birthDate DateTime?
nationality String?
languageCode String?
active Boolean @default(true)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
deletedAt DateTime?
tenant Tenant @relation(
fields:[tenantId],
references:[id]
)
address CustomerAddress?
documents CustomerDocument[]
reservations Reservation[]
}
model CustomerAddress {
id String @id @default(uuid())
customerId String @unique
addressLine1 String
addressLine2 String?
postalCode String
city String
state String?
countryCode String
customer Customer @relation(
fields:[customerId],
references:[id]
)
}
model CustomerDocument {
id String @id @default(uuid())
customerId String
documentType String
fileUrl String
createdAt DateTime @default(now())
customer Customer @relation(
fields:[customerId],
references:[id]
)
}
customerId String?
customer Customer? @relation( fields:[customerId], references:[id] )
Contract ContractTemplate ContractVersion ContractSignature
model Contract {
id String @id @default(uuid())
tenantId String
reservationId String
templateId String
contractNumber String @unique
status ContractStatus
signedAt DateTime?
generatedAt DateTime?
pdfUrl String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
tenant Tenant @relation(
fields:[tenantId],
references:[id]
)
reservation Reservation @relation(
fields:[reservationId],
references:[id]
)
template ContractTemplate @relation(
fields:[templateId],
references:[id]
)
signatures ContractSignature[]
versions ContractVersion[]
}
enum ContractStatus {
DRAFT
GENERATED
SENT
VIEWED
SIGNED
CANCELLED
}
model ContractTemplate {
id String @id @default(uuid())
code String @unique
name String
content String
active Boolean @default(true)
createdAt DateTime @default(now())
contracts Contract[]
}
model ContractVersion {
id String @id @default(uuid())
contractId String
versionNumber Int
pdfUrl String
createdAt DateTime @default(now())
contract Contract @relation(
fields:[contractId],
references:[id]
)
}
model ContractSignature {
id String @id @default(uuid())
contractId String
signerName String
signerEmail String
signedAt DateTime?
providerReference String?
status String
contract Contract @relation(
fields:[contractId],
references:[id]
)
}
model Document {
id String @id @default(uuid())
tenantId String
documentType String
fileName String
fileUrl String
mimeType String
fileSize Int
uploadedAt DateTime @default(now())
tenant Tenant @relation(
fields:[tenantId],
references:[id]
)
}
Payment PaymentTransaction Refund PaymentMethod
model Payment {
id String @id @default(uuid())
tenantId String
reservationId String
paymentReference String @unique
status PaymentStatus
amount Decimal @db.Decimal(10,2)
currencyCode String
paidAt DateTime?
createdAt DateTime @default(now())
tenant Tenant @relation(
fields:[tenantId],
references:[id]
)
reservation Reservation @relation(
fields:[reservationId],
references:[id]
)
transactions PaymentTransaction[]
refunds Refund[]
}
enum PaymentStatus {
PENDING
AUTHORIZED
PAID
FAILED
CANCELLED
REFUNDED
}
model PaymentTransaction {
id String @id @default(uuid())
paymentId String
gateway String
gatewayReference String
status String
amount Decimal @db.Decimal(10,2)
createdAt DateTime @default(now())
payment Payment @relation(
fields:[paymentId],
references:[id]
)
}
model Refund {
id String @id @default(uuid())
paymentId String
amount Decimal @db.Decimal(10,2)
reason String?
refundedAt DateTime?
payment Payment @relation(
fields:[paymentId],
references:[id]
)
}
model Invoice {
id String @id @default(uuid())
tenantId String
reservationId String
invoiceNumber String @unique
issueDate DateTime
dueDate DateTime
amountExclTax Decimal @db.Decimal(10,2)
taxAmount Decimal @db.Decimal(10,2)
amountInclTax Decimal @db.Decimal(10,2)
status InvoiceStatus
pdfUrl String?
createdAt DateTime @default(now())
reservation Reservation @relation(
fields:[reservationId],
references:[id]
)
tenant Tenant @relation(
fields:[tenantId],
references:[id]
)
}
enum InvoiceStatus {
DRAFT
ISSUED
PAID
PARTIALLY_PAID
CANCELLED
}
model AccountingEntry {
id String @id @default(uuid())
tenantId String
entryDate DateTime
accountCode String
label String
debit Decimal @db.Decimal(12,2)
credit Decimal @db.Decimal(12,2)
reference String?
createdAt DateTime @default(now())
tenant Tenant @relation(
fields:[tenantId],
references:[id]
)
}
@@index([reservationId]) @@index([status]) @@index([contractNumber])
@@index([reservationId]) @@index([status]) @@index([paymentReference])
@@index([reservationId]) @@index([invoiceNumber]) @@index([status])
npx prisma format npx prisma validate npx prisma generate
npx prisma migrate dev \
--name contracts_payments_invoices
À ce stade, le schéma couvre :
Tenant User RBAC Owner Property Customer Reservation Contract Document Payment Invoice Accounting
Owner ↓ Property ↓ Customer ↓ Reservation ↓ Contract ↓ Signature ↓ Payment ↓ Invoice ↓ Accounting
Mettre en place le CRM intégré de la plateforme.
Contrairement à un CRM externe, ce module est directement connecté aux :
Le CRM devient ainsi la vue 360° du client.
Lead Opportunity Pipeline PipelineStage Activity Task CustomerNote Tag Segment CustomerTag LeadSource
Lead ↓ Qualification ↓ Opportunity ↓ Reservation ↓ Customer
model Lead {
id String @id @default(uuid())
tenantId String
leadNumber String @unique
firstName String
lastName String
email String?
phone String?
company String?
sourceId String?
status LeadStatus
score Int @default(0)
estimatedBudget Decimal? @db.Decimal(10,2)
expectedCheckIn DateTime?
expectedCheckOut DateTime?
notes String?
assignedToUserId String?
convertedAt DateTime?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
tenant Tenant @relation(
fields:[tenantId],
references:[id]
)
source LeadSource? @relation(
fields:[sourceId],
references:[id]
)
opportunities Opportunity[]
activities Activity[]
}
enum LeadStatus {
NEW
QUALIFIED
CONTACTED
PROPOSAL
WON
LOST
ARCHIVED
}
model LeadSource {
id String @id @default(uuid())
code String @unique
name String
leads Lead[]
}
WEBSITE PHONE EMAIL FACEBOOK INSTAGRAM GOOGLE AIRBNB BOOKING REFERRAL
model Pipeline {
id String @id @default(uuid())
tenantId String
name String
active Boolean @default(true)
stages PipelineStage[]
opportunities Opportunity[]
}
model PipelineStage {
id String @id @default(uuid())
pipelineId String
name String
position Int
probability Int
pipeline Pipeline @relation(
fields:[pipelineId],
references:[id]
)
}
Nouveau Qualification Proposition Négociation Gagné Perdu
model Opportunity {
id String @id @default(uuid())
tenantId String
leadId String
pipelineId String
stageId String
title String
amount Decimal? @db.Decimal(10,2)
probability Int
expectedCloseDate DateTime?
status OpportunityStatus
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
tenant Tenant @relation(
fields:[tenantId],
references:[id]
)
lead Lead @relation(
fields:[leadId],
references:[id]
)
pipeline Pipeline @relation(
fields:[pipelineId],
references:[id]
)
}
enum OpportunityStatus {
OPEN
WON
LOST
CANCELLED
}
model Activity {
id String @id @default(uuid())
tenantId String
leadId String?
customerId String?
activityType ActivityType
subject String
description String?
occurredAt DateTime
createdByUserId String
createdAt DateTime @default(now())
tenant Tenant @relation(
fields:[tenantId],
references:[id]
)
lead Lead? @relation(
fields:[leadId],
references:[id]
)
customer Customer? @relation(
fields:[customerId],
references:[id]
)
}
enum ActivityType {
CALL
EMAIL
MEETING
SMS
NOTE
TASK
SYSTEM
}
model Task {
id String @id @default(uuid())
tenantId String
title String
description String?
assignedToUserId String
dueDate DateTime?
completedAt DateTime?
priority TaskPriority
status TaskStatus
createdAt DateTime @default(now())
tenant Tenant @relation(
fields:[tenantId],
references:[id]
)
}
enum TaskPriority {
LOW
MEDIUM
HIGH
URGENT
}
enum TaskStatus {
OPEN
IN_PROGRESS
DONE
CANCELLED
}
model CustomerNote {
id String @id @default(uuid())
customerId String
authorUserId String
content String
createdAt DateTime @default(now())
customer Customer @relation(
fields:[customerId],
references:[id]
)
}
model Tag {
id String @id @default(uuid())
tenantId String
code String @unique
name String
color String?
tenant Tenant @relation(
fields:[tenantId],
references:[id]
)
customerTags CustomerTag[]
}
model CustomerTag {
customerId String
tagId String
customer Customer @relation(
fields:[customerId],
references:[id]
)
tag Tag @relation(
fields:[tagId],
references:[id]
)
@@id([customerId, tagId])
}
model Segment {
id String @id @default(uuid())
tenantId String
code String @unique
name String
description String?
rules Json
active Boolean @default(true)
tenant Tenant @relation(
fields:[tenantId],
references:[id]
)
}
Ajouter :
activities Activity[] notes CustomerNote[] tags CustomerTag[]
Ajouter :
leads Lead[] opportunities Opportunity[] pipelines Pipeline[] tasks Task[] segments Segment[]
@@index([tenantId]) @@index([status]) @@index([email]) @@index([assignedToUserId])
@@index([tenantId]) @@index([status]) @@index([expectedCloseDate])
@@index([assignedToUserId]) @@index([status]) @@index([dueDate])
npx prisma format npx prisma validate npx prisma generate
npx prisma migrate dev \
--name crm_and_customer_relationship
Prospects Pipeline Relances Activités Tâches Segmentation Historique client Vue 360°
Lead ↓ Qualification ↓ Opportunity ↓ Reservation ↓ Contract ↓ Payment ↓ Customer Loyalty
Le noyau métier couvre désormais :
RBAC Utilisateurs Propriétaires Catalogue Réservations Contrats Paiements Facturation CRM
soit environ :
35 à 40 modèles Prisma
Mettre en place le centre de communication unifié de la plateforme.
Ce domaine couvre :
Ce domaine sera utilisé par :
Reservations Contracts Payments CRM Owner Portal Administration
Notification NotificationTemplate NotificationPreference Conversation ConversationParticipant Message EmailLog SmsLog
model Notification {
id String @id @default(uuid())
tenantId String
userId String
type NotificationType
title String
content String
status NotificationStatus
entityType String?
entityId String?
readAt DateTime?
createdAt DateTime @default(now())
tenant Tenant @relation(
fields:[tenantId],
references:[id]
)
user User @relation(
fields:[userId],
references:[id]
)
@@index([tenantId])
@@index([userId])
@@index([status])
@@index([createdAt])
}
enum NotificationType {
SYSTEM
RESERVATION
CONTRACT
PAYMENT
CRM
OWNER
SECURITY
MARKETING
}
enum NotificationStatus {
UNREAD
READ
ARCHIVED
}
model NotificationTemplate {
id String @id @default(uuid())
tenantId String
code String
name String
subject String?
content String
channel NotificationChannel
active Boolean @default(true)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
tenant Tenant @relation(
fields:[tenantId],
references:[id]
)
@@unique([tenantId, code])
}
enum NotificationChannel {
IN_APP
EMAIL
SMS
PUSH
}
model NotificationPreference {
id String @id @default(uuid())
userId String
notificationType NotificationType
emailEnabled Boolean @default(true)
smsEnabled Boolean @default(false)
pushEnabled Boolean @default(false)
inAppEnabled Boolean @default(true)
user User @relation(
fields:[userId],
references:[id]
)
@@unique([userId, notificationType])
}
model Conversation {
id String @id @default(uuid())
tenantId String
reservationId String?
subject String
status ConversationStatus
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
tenant Tenant @relation(
fields:[tenantId],
references:[id]
)
reservation Reservation? @relation(
fields:[reservationId],
references:[id]
)
participants ConversationParticipant[]
messages Message[]
@@index([tenantId])
@@index([reservationId])
}
enum ConversationStatus {
OPEN
CLOSED
ARCHIVED
}
model ConversationParticipant {
conversationId String
userId String?
customerId String?
ownerId String?
joinedAt DateTime @default(now())
conversation Conversation @relation(
fields:[conversationId],
references:[id]
)
user User? @relation(
fields:[userId],
references:[id]
)
customer Customer? @relation(
fields:[customerId],
references:[id]
)
owner Owner? @relation(
fields:[ownerId],
references:[id]
)
@@id([
conversationId,
userId,
customerId,
ownerId
])
}
model Message {
id String @id @default(uuid())
conversationId String
senderUserId String?
senderCustomerId String?
senderOwnerId String?
content String
sentAt DateTime @default(now())
readAt DateTime?
conversation Conversation @relation(
fields:[conversationId],
references:[id]
)
senderUser User? @relation(
fields:[senderUserId],
references:[id]
)
senderCustomer Customer? @relation(
fields:[senderCustomerId],
references:[id]
)
senderOwner Owner? @relation(
fields:[senderOwnerId],
references:[id]
)
@@index([conversationId])
@@index([sentAt])
}
model EmailLog {
id String @id @default(uuid())
tenantId String
templateId String?
recipientEmail String
subject String
provider String?
providerMessageId String?
status EmailStatus
errorMessage String?
sentAt DateTime?
createdAt DateTime @default(now())
tenant Tenant @relation(
fields:[tenantId],
references:[id]
)
template NotificationTemplate?
@relation(
fields:[templateId],
references:[id]
)
@@index([tenantId])
@@index([recipientEmail])
@@index([status])
}
enum EmailStatus {
PENDING
SENT
DELIVERED
OPENED
FAILED
BOUNCED
}
model SmsLog {
id String @id @default(uuid())
tenantId String
recipientPhone String
content String
provider String?
providerMessageId String?
status SmsStatus
errorMessage String?
sentAt DateTime?
createdAt DateTime @default(now())
tenant Tenant @relation(
fields:[tenantId],
references:[id]
)
@@index([tenantId])
@@index([recipientPhone])
@@index([status])
}
enum SmsStatus {
PENDING
SENT
DELIVERED
FAILED
}
notifications Notification[] notificationPreferences NotificationPreference[] conversationParticipants ConversationParticipant[] messages Message[]
conversationParticipants ConversationParticipant[] messages Message[]
conversationParticipants ConversationParticipant[] messages Message[]
conversations Conversation[]
notifications Notification[] notificationTemplates NotificationTemplate[] conversations Conversation[] emailLogs EmailLog[] smsLogs SmsLog[]
npx prisma format npx prisma validate npx prisma generate
npx prisma migrate dev \
--name notifications_messaging
Notifications temps réel Centre de notifications Messagerie propriétaire Messagerie client Messagerie réservation Emails transactionnels SMS transactionnels Préférences utilisateur
Après Phase 2-G :
≈ 60 modèles Prisma
avec les domaines :
Core Security Properties Owners Customers Reservations Contracts Payments CRM Notifications Messaging
Mettre en place la couche marketing et automatisation de la plateforme.
Ce domaine permettra :
Cette phase finalise :
Sprint 8 CRM Sprint 9 Communication Sprint 13 Automatisation & IA
Campaign CampaignRecipient CampaignExecution MarketingSegment MarketingEvent AutomationRule AutomationExecution
model Campaign {
id String @id @default(uuid())
tenantId String
code String
name String
description String?
campaignType CampaignType
status CampaignStatus
startDate DateTime?
endDate DateTime?
createdByUserId String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
tenant Tenant @relation(
fields:[tenantId],
references:[id]
)
createdByUser User? @relation(
"CampaignCreator",
fields:[createdByUserId],
references:[id]
)
recipients CampaignRecipient[]
executions CampaignExecution[]
@@unique([tenantId, code])
@@index([tenantId])
@@index([status])
}
enum CampaignType {
EMAIL
SMS
MIXED
PUSH
WORKFLOW
}
enum CampaignStatus {
DRAFT
SCHEDULED
RUNNING
COMPLETED
PAUSED
CANCELLED
}
model CampaignRecipient {
id String @id @default(uuid())
campaignId String
customerId String?
leadId String?
email String?
phone String?
status CampaignRecipientStatus
sentAt DateTime?
openedAt DateTime?
clickedAt DateTime?
unsubscribedAt DateTime?
campaign Campaign @relation(
fields:[campaignId],
references:[id]
)
customer Customer? @relation(
fields:[customerId],
references:[id]
)
lead Lead? @relation(
fields:[leadId],
references:[id]
)
@@index([campaignId])
@@index([status])
}
enum CampaignRecipientStatus {
PENDING
SENT
OPENED
CLICKED
FAILED
UNSUBSCRIBED
}
model CampaignExecution {
id String @id @default(uuid())
campaignId String
startedAt DateTime
completedAt DateTime?
recipientsCount Int
successCount Int
failedCount Int
status ExecutionStatus
campaign Campaign @relation(
fields:[campaignId],
references:[id]
)
@@index([campaignId])
@@index([status])
}
enum ExecutionStatus {
PENDING
RUNNING
COMPLETED
FAILED
}
model MarketingSegment {
id String @id @default(uuid())
tenantId String
code String
name String
description String?
rules Json
active Boolean @default(true)
createdAt DateTime @default(now())
tenant Tenant @relation(
fields:[tenantId],
references:[id]
)
@@unique([tenantId, code])
}
CLIENTS_FIDELES CLIENTS_INACTIFS PROPRIETAIRES_ACTIFS PROSPECTS_CHAUDS RESERVATIONS_30J ANNIVERSAIRES
model MarketingEvent {
id String @id @default(uuid())
tenantId String
customerId String?
leadId String?
eventType MarketingEventType
payload Json?
occurredAt DateTime
tenant Tenant @relation(
fields:[tenantId],
references:[id]
)
customer Customer? @relation(
fields:[customerId],
references:[id]
)
lead Lead? @relation(
fields:[leadId],
references:[id]
)
@@index([tenantId])
@@index([eventType])
@@index([occurredAt])
}
enum MarketingEventType {
EMAIL_OPEN
EMAIL_CLICK
SMS_SENT
PAGE_VISIT
FORM_SUBMIT
LEAD_CREATED
RESERVATION_CREATED
CONTRACT_SIGNED
PAYMENT_COMPLETED
}
model AutomationRule {
id String @id @default(uuid())
tenantId String
code String
name String
description String?
triggerEvent String
conditions Json?
actions Json
active Boolean @default(true)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
tenant Tenant @relation(
fields:[tenantId],
references:[id]
)
executions AutomationExecution[]
@@unique([tenantId, code])
@@index([tenantId])
@@index([active])
}
ReservationConfirmed ↓ SendEmail ---------------- PaymentReceived ↓ GenerateInvoice ---------------- LeadCreated ↓ AssignSalesAgent
model AutomationExecution {
id String @id @default(uuid())
automationRuleId String
status ExecutionStatus
entityType String?
entityId String?
startedAt DateTime
completedAt DateTime?
errorMessage String?
automationRule AutomationRule @relation(
fields:[automationRuleId],
references:[id]
)
@@index([automationRuleId])
@@index([status])
}
campaigns Campaign[] marketingSegments MarketingSegment[] marketingEvents MarketingEvent[] automationRules AutomationRule[]
campaignRecipients CampaignRecipient[] marketingEvents MarketingEvent[]
campaignRecipients CampaignRecipient[] marketingEvents MarketingEvent[]
createdCampaigns Campaign[]
@relation("CampaignCreator")
npx prisma format npx prisma validate npx prisma generate
npx prisma migrate dev \
--name marketing_automation
Lead Nurturing Relances Segmentation Scoring Qualification
Campagnes Email Campagnes SMS Suivi Ouvertures Suivi Clics Désabonnement
Déclencheurs Workflows Actions Automatiques Historisation
Après Phase 2-H :
≈ 67 modèles Prisma
avec les domaines :
Core Security Properties Owners Customers Reservations Contracts Payments CRM Messaging Marketing Automation
Mettre en place la couche transverse de gouvernance de la plateforme.
Cette couche doit permettre :
Cette phase prépare directement :
Sprint 10 — Reporting Sprint 11 — Administration Sprint 19 — Gouvernance & Conformité
AuditLog EntityHistory FeatureFlag Workflow WorkflowStep WorkflowInstance WorkflowExecution
Toutes les actions sensibles doivent être tracées.
model AuditLog {
id String @id @default(uuid())
tenantId String
userId String?
entityType String
entityId String
action AuditAction
oldValues Json?
newValues Json?
ipAddress String?
userAgent String?
correlationId String?
createdAt DateTime @default(now())
tenant Tenant @relation(
fields:[tenantId],
references:[id]
)
user User? @relation(
fields:[userId],
references:[id]
)
@@index([tenantId])
@@index([entityType])
@@index([entityId])
@@index([action])
@@index([createdAt])
}
enum AuditAction {
CREATE
UPDATE
DELETE
RESTORE
LOGIN
LOGOUT
EXPORT
IMPORT
EXECUTE
APPROVE
REJECT
}
Permet de reconstruire l'état d'une entité à n'importe quelle date.
model EntityHistory {
id String @id @default(uuid())
tenantId String
entityType String
entityId String
version Int
snapshot Json
createdByUserId String?
createdAt DateTime @default(now())
tenant Tenant @relation(
fields:[tenantId],
references:[id]
)
createdByUser User? @relation(
fields:[createdByUserId],
references:[id]
)
@@unique([
entityType,
entityId,
version
])
@@index([tenantId])
@@index([entityType])
@@index([entityId])
}
Permet de déployer progressivement des fonctionnalités.
model FeatureFlag {
id String @id @default(uuid())
tenantId String?
code String @unique
name String
description String?
enabled Boolean @default(false)
configuration Json?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
tenant Tenant? @relation(
fields:[tenantId],
references:[id]
)
@@index([tenantId])
@@index([enabled])
}
AI_ASSISTANT OWNER_PORTAL_V2 OTA_SYNC REVENUE_MANAGEMENT ADVANCED_REPORTING
model Workflow {
id String @id @default(uuid())
tenantId String
code String
name String
description String?
entityType String
active Boolean @default(true)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
tenant Tenant @relation(
fields:[tenantId],
references:[id]
)
steps WorkflowStep[]
instances WorkflowInstance[]
@@unique([tenantId, code])
@@index([tenantId])
@@index([entityType])
}
model WorkflowStep {
id String @id @default(uuid())
workflowId String
code String
name String
position Int
approverRoleCode String?
automatic Boolean @default(false)
configuration Json?
workflow Workflow @relation(
fields:[workflowId],
references:[id]
)
@@unique([
workflowId,
code
])
@@index([workflowId])
@@index([position])
}
ReservationApproval ↓ ManagerApproval ↓ ContractGeneration ↓ PaymentValidation ↓ Completed
model WorkflowInstance {
id String @id @default(uuid())
workflowId String
entityType String
entityId String
currentStepId String?
status WorkflowInstanceStatus
startedAt DateTime @default(now())
completedAt DateTime?
workflow Workflow @relation(
fields:[workflowId],
references:[id]
)
currentStep WorkflowStep?
@relation(
fields:[currentStepId],
references:[id]
)
executions WorkflowExecution[]
@@index([workflowId])
@@index([entityType])
@@index([entityId])
@@index([status])
}
enum WorkflowInstanceStatus {
RUNNING
WAITING
APPROVED
REJECTED
COMPLETED
CANCELLED
}
model WorkflowExecution {
id String @id @default(uuid())
workflowInstanceId String
workflowStepId String
executedByUserId String?
status WorkflowExecutionStatus
comments String?
executedAt DateTime @default(now())
workflowInstance WorkflowInstance @relation(
fields:[workflowInstanceId],
references:[id]
)
workflowStep WorkflowStep @relation(
fields:[workflowStepId],
references:[id]
)
executedByUser User? @relation(
fields:[executedByUserId],
references:[id]
)
@@index([workflowInstanceId])
@@index([workflowStepId])
@@index([executedAt])
}
enum WorkflowExecutionStatus {
PENDING
APPROVED
REJECTED
SKIPPED
COMPLETED
}
auditLogs AuditLog[] entityHistories EntityHistory[] featureFlags FeatureFlag[] workflows Workflow[]
auditLogs AuditLog[] entityHistories EntityHistory[] workflowExecutions WorkflowExecution[]
Reservation Created ↓ AuditLog ↓ Workflow Instance ↓ Manager Approval ↓ Contract Generation
Payment Refunded ↓ AuditLog ↓ History Snapshot ↓ Compliance Trace
Feature Enabled ↓ AuditLog ↓ History ↓ Reporting
npx prisma format npx prisma validate npx prisma generate
npx prisma migrate dev \
--name administration_audit_governance
Feature Flags Workflows Validation Approbation
Journalisation Traçabilité Historisation Conformité
Contrôle Supervision Rétention Preuve d'audit
Après Phase 2-I :
≈ 74 modèles Prisma
avec les domaines :
Core Security Properties Owners Customers Reservations Contracts Payments CRM Messaging Marketing Automation Governance
Construire le moteur de distribution multicanal de la plateforme.
Ce domaine permettra :
Cette phase couvre :
Sprint 12 — OTA & Distribution Sprint 14 — API & Partenaires Sprint 18 — Channel Manager Enterprise
Channel ChannelConnection PropertyDistribution ChannelReservation SyncExecution SyncError
Référentiel des plateformes connectables.
model Channel {
id String @id @default(uuid())
code String @unique
name String
channelType ChannelType
active Boolean @default(true)
apiDocumentationUrl String?
logoUrl String?
createdAt DateTime @default(now())
connections ChannelConnection[]
distributions PropertyDistribution[]
}
enum ChannelType {
OTA
DIRECT
PARTNER
API
MARKETPLACE
}
AIRBNB BOOKING VRBO ABRITEL EXPEDIA DIRECT_WEBSITE
Une agence peut posséder plusieurs connexions.
model ChannelConnection {
id String @id @default(uuid())
tenantId String
channelId String
connectionName String
accountIdentifier String?
apiKey String?
apiSecret String?
refreshToken String?
configuration Json?
active Boolean @default(true)
lastSyncAt DateTime?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
tenant Tenant @relation(
fields:[tenantId],
references:[id]
)
channel Channel @relation(
fields:[channelId],
references:[id]
)
propertyDistributions PropertyDistribution[]
syncExecutions SyncExecution[]
@@index([tenantId])
@@index([channelId])
@@index([active])
}
Permet d'associer un bien à un canal de distribution.
model PropertyDistribution {
id String @id @default(uuid())
tenantId String
propertyId String
channelId String
channelConnectionId String
externalPropertyId String?
externalListingId String?
published Boolean @default(false)
publicationStatus DistributionStatus
publishedAt DateTime?
lastSyncAt DateTime?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
tenant Tenant @relation(
fields:[tenantId],
references:[id]
)
property Property @relation(
fields:[propertyId],
references:[id]
)
channel Channel @relation(
fields:[channelId],
references:[id]
)
channelConnection ChannelConnection @relation(
fields:[channelConnectionId],
references:[id]
)
reservations ChannelReservation[]
@@unique([
propertyId,
channelConnectionId
])
@@index([tenantId])
@@index([propertyId])
@@index([channelId])
@@index([publicationStatus])
}
enum DistributionStatus {
DRAFT
PENDING
PUBLISHED
SUSPENDED
ERROR
ARCHIVED
}
Historise le lien entre la réservation interne et la réservation OTA.
model ChannelReservation {
id String @id @default(uuid())
tenantId String
reservationId String
propertyDistributionId String
externalReservationId String
externalStatus String?
importedAt DateTime @default(now())
lastSyncAt DateTime?
reservation Reservation @relation(
fields:[reservationId],
references:[id]
)
propertyDistribution PropertyDistribution @relation(
fields:[propertyDistributionId],
references:[id]
)
tenant Tenant @relation(
fields:[tenantId],
references:[id]
)
@@unique([
propertyDistributionId,
externalReservationId
])
@@index([tenantId])
@@index([reservationId])
@@index([importedAt])
}
Chaque synchronisation OTA doit être historisée.
model SyncExecution {
id String @id @default(uuid())
tenantId String
channelConnectionId String
syncType SyncType
status SyncStatus
startedAt DateTime
completedAt DateTime?
processedCount Int @default(0)
successCount Int @default(0)
errorCount Int @default(0)
createdAt DateTime @default(now())
tenant Tenant @relation(
fields:[tenantId],
references:[id]
)
channelConnection ChannelConnection @relation(
fields:[channelConnectionId],
references:[id]
)
errors SyncError[]
@@index([tenantId])
@@index([channelConnectionId])
@@index([syncType])
@@index([status])
@@index([startedAt])
}
enum SyncType {
PROPERTY_EXPORT
AVAILABILITY_EXPORT
RATE_EXPORT
RESERVATION_IMPORT
FULL_SYNC
}
enum SyncStatus {
PENDING
RUNNING
COMPLETED
PARTIAL_SUCCESS
FAILED
}
Permet d'analyser les échecs OTA.
model SyncError {
id String @id @default(uuid())
syncExecutionId String
errorCode String?
errorMessage String
entityType String?
entityId String?
payload Json?
occurredAt DateTime @default(now())
syncExecution SyncExecution @relation(
fields:[syncExecutionId],
references:[id]
)
@@index([syncExecutionId])
@@index([errorCode])
@@index([occurredAt])
}
channelConnections ChannelConnection[] propertyDistributions PropertyDistribution[] channelReservations ChannelReservation[] syncExecutions SyncExecution[]
propertyDistributions PropertyDistribution[]
channelReservations ChannelReservation[]
PropertyAvailability ↓ OTA
PropertyRate ↓ OTA
OTA ↓ ChannelReservation ↓ Reservation
Property ↓ PropertyDistribution ↓ Airbnb Booking Vrbo Abritel
npx prisma format npx prisma validate npx prisma generate
npx prisma migrate dev \
--name ota_channel_manager
Publication OTA Synchronisation calendrier Synchronisation tarifs Import réservations
Partenaires API externes Marketplace
Channel Manager Enterprise Multi-OTA Monitoring synchronisations Gestion erreurs
Après Phase 2-J :
≈ 80 modèles Prisma
avec les domaines :
Core Security Properties Owners Customers Reservations Contracts Payments CRM Messaging Marketing Automation Governance OTA
Construire le moteur de Revenue Management de la plateforme.
Ce domaine permettra :
Cette phase couvre :
Sprint 10 — Reporting Sprint 13 — IA & Prévisions Sprint 17 — Revenue Management Sprint 20 — Enterprise Analytics
PricingRule DynamicPrice RevenueForecast RevenueSimulation CompetitorSnapshot MarketDemand
Définit les règles d'ajustement automatique.
model PricingRule {
id String @id @default(uuid())
tenantId String
propertyId String?
code String
name String
description String?
priority Int @default(100)
active Boolean @default(true)
conditions Json
actions Json
validFrom DateTime?
validTo DateTime?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
tenant Tenant @relation(
fields:[tenantId],
references:[id]
)
property Property? @relation(
fields:[propertyId],
references:[id]
)
@@unique([tenantId, code])
@@index([tenantId])
@@index([propertyId])
@@index([active])
@@index([priority])
}
Occupation > 80% ↓ +15% ---------------- Weekend ↓ +10% ---------------- Haute saison ↓ +25%
Prix final appliqué à une date donnée.
model DynamicPrice {
id String @id @default(uuid())
tenantId String
propertyId String
pricingDate DateTime
basePrice Decimal @db.Decimal(12,2)
adjustedPrice Decimal @db.Decimal(12,2)
occupancyFactor Decimal? @db.Decimal(5,2)
seasonalityFactor Decimal? @db.Decimal(5,2)
demandFactor Decimal? @db.Decimal(5,2)
competitorFactor Decimal? @db.Decimal(5,2)
generatedAt DateTime @default(now())
tenant Tenant @relation(
fields:[tenantId],
references:[id]
)
property Property @relation(
fields:[propertyId],
references:[id]
)
@@unique([
propertyId,
pricingDate
])
@@index([tenantId])
@@index([propertyId])
@@index([pricingDate])
}
Prévision de revenus futurs.
model RevenueForecast {
id String @id @default(uuid())
tenantId String
propertyId String?
forecastDate DateTime
forecastPeriodStart DateTime
forecastPeriodEnd DateTime
expectedRevenue Decimal @db.Decimal(14,2)
expectedOccupancy Decimal @db.Decimal(5,2)
confidenceLevel Decimal @db.Decimal(5,2)
modelVersion String?
generatedAt DateTime @default(now())
tenant Tenant @relation(
fields:[tenantId],
references:[id]
)
property Property? @relation(
fields:[propertyId],
references:[id]
)
@@index([tenantId])
@@index([propertyId])
@@index([forecastDate])
}
Analyse de scénarios.
model RevenueSimulation {
id String @id @default(uuid())
tenantId String
propertyId String?
name String
assumptions Json
projectedRevenue Decimal @db.Decimal(14,2)
projectedOccupancy Decimal @db.Decimal(5,2)
createdByUserId String?
createdAt DateTime @default(now())
tenant Tenant @relation(
fields:[tenantId],
references:[id]
)
property Property? @relation(
fields:[propertyId],
references:[id]
)
createdByUser User? @relation(
fields:[createdByUserId],
references:[id]
)
@@index([tenantId])
@@index([propertyId])
}
Prix +10% ↓ Occupation -3% ↓ CA +6%
Capture des prix concurrents.
model CompetitorSnapshot {
id String @id @default(uuid())
tenantId String
propertyId String
competitorName String
competitorPropertyId String?
snapshotDate DateTime
nightlyRate Decimal @db.Decimal(12,2)
occupancy Decimal? @db.Decimal(5,2)
source String?
createdAt DateTime @default(now())
tenant Tenant @relation(
fields:[tenantId],
references:[id]
)
property Property @relation(
fields:[propertyId],
references:[id]
)
@@index([tenantId])
@@index([propertyId])
@@index([snapshotDate])
}
Mesure de la demande.
model MarketDemand {
id String @id @default(uuid())
tenantId String
regionCode String
demandDate DateTime
demandIndex Decimal @db.Decimal(5,2)
occupancyIndex Decimal @db.Decimal(5,2)
averageDailyRate Decimal @db.Decimal(12,2)
source String?
createdAt DateTime @default(now())
tenant Tenant @relation(
fields:[tenantId],
references:[id]
)
@@index([tenantId])
@@index([regionCode])
@@index([demandDate])
}
pricingRules PricingRule[] dynamicPrices DynamicPrice[] revenueForecasts RevenueForecast[] revenueSimulations RevenueSimulation[] competitorSnapshots CompetitorSnapshot[]
pricingRules PricingRule[] dynamicPrices DynamicPrice[] revenueForecasts RevenueForecast[] revenueSimulations RevenueSimulation[] marketDemands MarketDemand[]
Occupation + Saisonnalité + Concurrence + Demande ↓ Prix optimal
Historique réservations ↓ Forecast IA ↓ Revenus prévus
Nouveau tarif ↓ Simulation ↓ Impact CA
npx prisma format npx prisma validate npx prisma generate
npx prisma migrate dev \
--name revenue_management
Yield Management Dynamic Pricing Optimisation tarifaire
Forecast Simulation Benchmark concurrence Demand Analytics
Aide à la décision Prévisions Recommandations
Après Phase 2-K :
≈ 86 modèles Prisma
avec les domaines :
Core Security Properties Owners Customers Reservations Contracts Payments CRM Messaging Marketing Automation Governance OTA Revenue Management
Construire la couche Enterprise de sécurité, conformité et gouvernance des données.
Cette phase permettra :
Cette phase couvre :
Sprint 19 Governance Compliance Enterprise Security
Consent SecurityPolicy Risk ComplianceAudit SecurityIncident DataClassification RetentionPolicy
Traçabilité complète des consentements utilisateurs.
model Consent {
id String @id @default(uuid())
tenantId String
customerId String?
userId String?
consentType ConsentType
granted Boolean
version String
source String?
ipAddress String?
userAgent String?
grantedAt DateTime
revokedAt DateTime?
createdAt DateTime @default(now())
tenant Tenant @relation(
fields:[tenantId],
references:[id]
)
customer Customer? @relation(
fields:[customerId],
references:[id]
)
user User? @relation(
fields:[userId],
references:[id]
)
@@index([tenantId])
@@index([customerId])
@@index([userId])
@@index([consentType])
}
enum ConsentType {
GDPR
COOKIES
MARKETING_EMAIL
MARKETING_SMS
PROFILING
THIRD_PARTY_SHARING
}
Configuration centralisée.
model SecurityPolicy {
id String @id @default(uuid())
tenantId String?
code String @unique
name String
description String?
configuration Json
active Boolean @default(true)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
tenant Tenant? @relation(
fields:[tenantId],
references:[id]
)
@@index([tenantId])
@@index([active])
}
PASSWORD_POLICY SESSION_POLICY MFA_POLICY RETENTION_POLICY ACCESS_CONTROL_POLICY
model Risk {
id String @id @default(uuid())
tenantId String
code String
title String
description String?
category RiskCategory
probability Int
impact Int
score Int
mitigationPlan String?
ownerUserId String?
status RiskStatus
identifiedAt DateTime
reviewedAt DateTime?
createdAt DateTime @default(now())
tenant Tenant @relation(
fields:[tenantId],
references:[id]
)
ownerUser User? @relation(
fields:[ownerUserId],
references:[id]
)
@@unique([tenantId, code])
@@index([tenantId])
@@index([status])
@@index([score])
}
enum RiskCategory {
SECURITY
COMPLIANCE
OPERATIONAL
FINANCIAL
LEGAL
TECHNICAL
}
enum RiskStatus {
IDENTIFIED
ASSESSED
MITIGATED
ACCEPTED
CLOSED
}
model ComplianceAudit {
id String @id @default(uuid())
tenantId String
auditType ComplianceAuditType
scope String?
status ComplianceAuditStatus
auditor String?
findings Json?
recommendations Json?
startedAt DateTime
completedAt DateTime?
createdAt DateTime @default(now())
tenant Tenant @relation(
fields:[tenantId],
references:[id]
)
@@index([tenantId])
@@index([auditType])
@@index([status])
}
enum ComplianceAuditType {
GDPR
ISO27001
SOC2
NIS2
INTERNAL
}
enum ComplianceAuditStatus {
PLANNED
RUNNING
COMPLETED
CLOSED
}
model SecurityIncident {
id String @id @default(uuid())
tenantId String
code String
title String
description String?
severity IncidentSeverity
status IncidentStatus
detectedAt DateTime
resolvedAt DateTime?
reportedByUserId String?
rootCause String?
correctiveActions String?
createdAt DateTime @default(now())
tenant Tenant @relation(
fields:[tenantId],
references:[id]
)
reportedByUser User? @relation(
fields:[reportedByUserId],
references:[id]
)
@@unique([tenantId, code])
@@index([tenantId])
@@index([severity])
@@index([status])
}
enum IncidentSeverity {
LOW
MEDIUM
HIGH
CRITICAL
}
enum IncidentStatus {
OPEN
INVESTIGATING
MITIGATED
RESOLVED
CLOSED
}
model DataClassification {
id String @id @default(uuid())
code String @unique
name String
description String?
level ClassificationLevel
active Boolean @default(true)
createdAt DateTime @default(now())
}
enum ClassificationLevel {
PUBLIC
INTERNAL
CONFIDENTIAL
RESTRICTED
}
Client → CONFIDENTIAL ---------------- Paiement → RESTRICTED ---------------- Catalogue public → PUBLIC
model RetentionPolicy {
id String @id @default(uuid())
tenantId String?
code String @unique
entityType String
retentionDays Int
archiveBeforeDelete Boolean @default(true)
active Boolean @default(true)
createdAt DateTime @default(now())
tenant Tenant? @relation(
fields:[tenantId],
references:[id]
)
@@index([tenantId])
@@index([entityType])
}
AuditLog 3650 jours ---------------- LoginHistory 365 jours ---------------- MarketingEvent 1095 jours ---------------- Consent 1825 jours
consents Consent[] securityPolicies SecurityPolicy[] risks Risk[] complianceAudits ComplianceAudit[] securityIncidents SecurityIncident[] retentionPolicies RetentionPolicy[]
consents Consent[] ownedRisks Risk[] reportedIncidents SecurityIncident[]
consents Consent[]
npx prisma format npx prisma validate npx prisma generate
npx prisma migrate dev \
--name enterprise_security_compliance
Consentements Traçabilité Conservation Export Suppression
Politiques Incidents MFA Gestion des risques
Classification Rétention Audit Conformité
Après Phase 2-L :
≈ 93 modèles Prisma
avec les domaines :
Core Security Properties Owners Customers Reservations Contracts Payments CRM Messaging Marketing Automation Governance OTA Revenue Management Enterprise Security
Transformer la plateforme en solution SaaS internationale capable de gérer :
Cette phase couvre :
Sprint 15 — Réseau d'agences Sprint 20 — Internationalisation Enterprise SaaS Commercialisation internationale
Country Currency Language Timezone CurrencyRate Translation Region EnterpriseLicense
Norme ISO 3166.
model Country {
code String @id
iso3 String @unique
name String
nativeName String?
phonePrefix String?
euMember Boolean @default(false)
active Boolean @default(true)
createdAt DateTime @default(now())
regions Region[]
}
FR BE CH ES IT DE UK US CA
Norme ISO 4217.
model Currency {
code String @id
numericCode String?
name String
symbol String
decimalPlaces Int @default(2)
active Boolean @default(true)
createdAt DateTime @default(now())
rates CurrencyRate[]
}
EUR USD GBP CHF CAD
Norme ISO 639.
model Language {
code String @id
name String
nativeName String
active Boolean @default(true)
createdAt DateTime @default(now())
translations Translation[]
}
fr en de es it nl
model Timezone {
id String @id
displayName String
utcOffset String
active Boolean @default(true)
}
Europe/Paris Europe/London America/New_York America/Montreal Asia/Tokyo
Historisation des taux.
model CurrencyRate {
id String @id @default(uuid())
fromCurrencyCode String
toCurrencyCode String
rate Decimal @db.Decimal(18,8)
rateDate DateTime
source String?
createdAt DateTime @default(now())
fromCurrency Currency
@relation(
"FromCurrency",
fields:[fromCurrencyCode],
references:[code]
)
toCurrency Currency
@relation(
"ToCurrency",
fields:[toCurrencyCode],
references:[code]
)
@@unique([
fromCurrencyCode,
toCurrencyCode,
rateDate
])
@@index([rateDate])
}
Permet l'internationalisation dynamique.
model Translation {
id String @id @default(uuid())
languageCode String
namespace String
translationKey String
translationValue String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
language Language @relation(
fields:[languageCode],
references:[code]
)
@@unique([
languageCode,
namespace,
translationKey
])
@@index([namespace])
}
property.title reservation.confirm payment.invoice
Permet la gestion géographique.
model Region {
id String @id @default(uuid())
countryCode String
code String
name String
active Boolean @default(true)
createdAt DateTime @default(now())
country Country @relation(
fields:[countryCode],
references:[code]
)
@@unique([
countryCode,
code
])
}
FR_OCCITANIE FR_PACA FR_IDF ES_CATALUNYA US_FLORIDA
Gestion des déploiements internationaux.
model EnterpriseLicense {
id String @id @default(uuid())
tenantId String
licenseKey String @unique
edition EnterpriseEdition
maxUsers Int?
maxProperties Int?
maxAgencies Int?
validFrom DateTime
validTo DateTime?
active Boolean @default(true)
features Json?
createdAt DateTime @default(now())
tenant Tenant @relation(
fields:[tenantId],
references:[id]
)
@@index([tenantId])
@@index([active])
}
enum EnterpriseEdition {
COMMUNITY
PROFESSIONAL
BUSINESS
ENTERPRISE
ENTERPRISE_PLUS
}
Ajouter :
countryCode String? currencyCode String? languageCode String? timezoneId String? enterpriseLicense EnterpriseLicense?
country Country? currency Currency? language Language? timezone Timezone?
Ajouter :
currencyCode String?
currency Currency?
Ajouter :
currencyCode String? exchangeRate Decimal?
FR ↓ EN ↓ DE ↓ ES
EUR ↓ USD ↓ GBP ↓ CHF
France ↓ Occitanie ↓ Agence ↓ Biens
Tenant ↓ Multi-marques ↓ Multi-pays ↓ Multi-régions
npx prisma format npx prisma validate npx prisma generate
npx prisma migrate dev \
--name internationalization_multiregion
Multi-langues Multi-devises Multi-fuseaux
Multi-pays Multi-régions Multi-agences
Licensing Commercialisation SaaS mondial
Après Phase 2-M :
≈ 101 modèles Prisma
avec les domaines :
Core Security Properties Owners Customers Reservations Contracts Payments CRM Messaging Marketing Automation Governance OTA Revenue Management Enterprise Security Internationalization
Transformer la plateforme en solution SaaS internationale capable de gérer :
Cette phase couvre :
Sprint 15 — Réseau d'agences Sprint 20 — Internationalisation Enterprise SaaS Commercialisation internationale
Country Currency Language Timezone CurrencyRate Translation Region EnterpriseLicense
Norme ISO 3166.
model Country {
code String @id
iso3 String @unique
name String
nativeName String?
phonePrefix String?
euMember Boolean @default(false)
active Boolean @default(true)
createdAt DateTime @default(now())
regions Region[]
}
FR BE CH ES IT DE UK US CA
Norme ISO 4217.
model Currency {
code String @id
numericCode String?
name String
symbol String
decimalPlaces Int @default(2)
active Boolean @default(true)
createdAt DateTime @default(now())
rates CurrencyRate[]
}
EUR USD GBP CHF CAD
Norme ISO 639.
model Language {
code String @id
name String
nativeName String
active Boolean @default(true)
createdAt DateTime @default(now())
translations Translation[]
}
fr en de es it nl
model Timezone {
id String @id
displayName String
utcOffset String
active Boolean @default(true)
}
Europe/Paris Europe/London America/New_York America/Montreal Asia/Tokyo
Historisation des taux.
model CurrencyRate {
id String @id @default(uuid())
fromCurrencyCode String
toCurrencyCode String
rate Decimal @db.Decimal(18,8)
rateDate DateTime
source String?
createdAt DateTime @default(now())
fromCurrency Currency
@relation(
"FromCurrency",
fields:[fromCurrencyCode],
references:[code]
)
toCurrency Currency
@relation(
"ToCurrency",
fields:[toCurrencyCode],
references:[code]
)
@@unique([
fromCurrencyCode,
toCurrencyCode,
rateDate
])
@@index([rateDate])
}
Permet l'internationalisation dynamique.
model Translation {
id String @id @default(uuid())
languageCode String
namespace String
translationKey String
translationValue String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
language Language @relation(
fields:[languageCode],
references:[code]
)
@@unique([
languageCode,
namespace,
translationKey
])
@@index([namespace])
}
property.title reservation.confirm payment.invoice
Permet la gestion géographique.
model Region {
id String @id @default(uuid())
countryCode String
code String
name String
active Boolean @default(true)
createdAt DateTime @default(now())
country Country @relation(
fields:[countryCode],
references:[code]
)
@@unique([
countryCode,
code
])
}
FR_OCCITANIE FR_PACA FR_IDF ES_CATALUNYA US_FLORIDA
Gestion des déploiements internationaux.
model EnterpriseLicense {
id String @id @default(uuid())
tenantId String
licenseKey String @unique
edition EnterpriseEdition
maxUsers Int?
maxProperties Int?
maxAgencies Int?
validFrom DateTime
validTo DateTime?
active Boolean @default(true)
features Json?
createdAt DateTime @default(now())
tenant Tenant @relation(
fields:[tenantId],
references:[id]
)
@@index([tenantId])
@@index([active])
}
enum EnterpriseEdition {
COMMUNITY
PROFESSIONAL
BUSINESS
ENTERPRISE
ENTERPRISE_PLUS
}
Ajouter :
countryCode String? currencyCode String? languageCode String? timezoneId String? enterpriseLicense EnterpriseLicense?
country Country? currency Currency? language Language? timezone Timezone?
Ajouter :
currencyCode String?
currency Currency?
Ajouter :
currencyCode String? exchangeRate Decimal?
FR ↓ EN ↓ DE ↓ ES
EUR ↓ USD ↓ GBP ↓ CHF
France ↓ Occitanie ↓ Agence ↓ Biens
Tenant ↓ Multi-marques ↓ Multi-pays ↓ Multi-régions
npx prisma format npx prisma validate npx prisma generate
npx prisma migrate dev \
--name internationalization_multiregion
Multi-langues Multi-devises Multi-fuseaux
Multi-pays Multi-régions Multi-agences
Licensing Commercialisation SaaS mondial
Après Phase 2-M :
≈ 101 modèles Prisma
avec les domaines :
Core Security Properties Owners Customers Reservations Contracts Payments CRM Messaging Marketing Automation Governance OTA Revenue Management Enterprise Security Internationalization
Construire la couche d'intelligence artificielle de la plateforme.
Cette couche permettra :
Cette phase finalise :
Sprint 13 — IA & Automatisation Sprint 20 — Enterprise Analytics
AiConversation AiMessage KnowledgeDocument KnowledgeChunk AutomationScenario AutomationExecution Recommendation
Historisation des échanges avec l'assistant.
model AiConversation {
id String @id @default(uuid())
tenantId String
userId String
title String?
modelName String
contextType String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
tenant Tenant @relation(
fields:[tenantId],
references:[id]
)
user User @relation(
fields:[userId],
references:[id]
)
messages AiMessage[]
@@index([tenantId])
@@index([userId])
@@index([createdAt])
}
Analyse réservations Prévision revenus Recherche client Analyse contrat Support utilisateur
model AiMessage {
id String @id @default(uuid())
conversationId String
role AiRole
content String
tokenCount Int?
createdAt DateTime @default(now())
conversation AiConversation
@relation(
fields:[conversationId],
references:[id]
)
@@index([conversationId])
@@index([createdAt])
}
enum AiRole {
SYSTEM
USER
ASSISTANT
TOOL
}
Documents utilisés par l'IA.
model KnowledgeDocument {
id String @id @default(uuid())
tenantId String
documentId String?
title String
sourceType KnowledgeSourceType
sourceReference String?
content String?
metadata Json?
indexed Boolean @default(false)
indexedAt DateTime?
createdAt DateTime @default(now())
tenant Tenant @relation(
fields:[tenantId],
references:[id]
)
document Document? @relation(
fields:[documentId],
references:[id]
)
chunks KnowledgeChunk[]
@@index([tenantId])
@@index([indexed])
}
enum KnowledgeSourceType {
DOCUMENT
CONTRACT
FAQ
WEBSITE
POLICY
PROCEDURE
}
Préparation RAG.
model KnowledgeChunk {
id String @id @default(uuid())
knowledgeDocumentId String
chunkIndex Int
content String
embeddingId String?
metadata Json?
createdAt DateTime @default(now())
knowledgeDocument KnowledgeDocument
@relation(
fields:[knowledgeDocumentId],
references:[id]
)
@@unique([
knowledgeDocumentId,
chunkIndex
])
@@index([knowledgeDocumentId])
}
Version métier évoluée de :
AutomationRule
model AutomationScenario {
id String @id @default(uuid())
tenantId String
code String
name String
description String?
triggerType String
configuration Json
active Boolean @default(true)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
tenant Tenant @relation(
fields:[tenantId],
references:[id]
)
executions AutomationExecution[]
@@unique([tenantId, code])
@@index([tenantId])
@@index([active])
}
ReservationConfirmed ↓ SendContract ↓ CreateTask ↓ NotifyOwner ---------------- PaymentReceived ↓ GenerateInvoice ↓ NotifyCustomer
Extension du modèle existant.
model AutomationExecution {
id String @id @default(uuid())
automationScenarioId String?
automationRuleId String?
status ExecutionStatus
entityType String?
entityId String?
executionContext Json?
startedAt DateTime
completedAt DateTime?
errorMessage String?
createdAt DateTime @default(now())
automationScenario AutomationScenario?
@relation(
fields:[automationScenarioId],
references:[id]
)
automationRule AutomationRule?
@relation(
fields:[automationRuleId],
references:[id]
)
@@index([automationScenarioId])
@@index([automationRuleId])
@@index([status])
}
Aide à la décision.
model Recommendation {
id String @id @default(uuid())
tenantId String
entityType String
entityId String
recommendationType RecommendationType
title String
description String
confidenceScore Decimal @db.Decimal(5,2)
accepted Boolean?
acceptedAt DateTime?
createdAt DateTime @default(now())
tenant Tenant @relation(
fields:[tenantId],
references:[id]
)
@@index([tenantId])
@@index([entityType])
@@index([recommendationType])
@@index([confidenceScore])
}
enum RecommendationType {
PRICE_OPTIMIZATION
CUSTOMER_RETENTION
LEAD_CONVERSION
REVENUE_FORECAST
PROPERTY_IMPROVEMENT
RISK_ALERT
AUTOMATION
}
aiConversations AiConversation[] knowledgeDocuments KnowledgeDocument[] automationScenarios AutomationScenario[] recommendations Recommendation[]
aiConversations AiConversation[]
knowledgeDocuments KnowledgeDocument[]
"Quels biens sont sous-performants ?" ↓ Analyse IA ↓ Recommandations
"Nouveaux leads prioritaires" ↓ Scoring ↓ Liste qualifiée
"Optimiser les tarifs" ↓ Forecast ↓ Recommandations
Question ↓ RAG ↓ KnowledgeDocument ↓ KnowledgeChunk ↓ Réponse
npx prisma format npx prisma validate npx prisma generate
npx prisma migrate dev \
--name ai_knowledge_automation
Assistant conversationnel Recherche sémantique Analyse métier Aide à la décision
FAQ Procédures Contrats Documentation
Workflows avancés Scénarios Actions automatiques
Après Phase 2-N :
≈ 108 à 112 modèles Prisma
répartis sur :
Core Security RBAC Properties Owners Customers Reservations Contracts Payments Accounting CRM Messaging Marketing Automation Governance OTA Revenue Management Enterprise Security Internationalization Artificial Intelligence
Le schéma Prisma couvre désormais l'intégralité des sprints :
Sprint 1 → Sprint 20
et constitue la base pour :
Phase 3 OpenAPI 3.1 complet ↓ DTO NestJS ↓ SDK TypeScript ↓ Génération Backend NestJS ↓ Génération Frontend NextJS ↓ Monorepo Enterprise 4.0