====== Phase 2 — Prisma Schema ======
===== Objectif =====
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 :
* PostgreSQL
* Prisma Client
* DTO
* Entités NestJS
* OpenAPI
* SDK TypeScript
* Migrations SQL
Le schéma doit refléter l'intégralité des domaines définis dans les 20 sprints.
----
====== Objectifs techniques ======
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
----
====== Ordre de construction recommandé ======
Ne jamais commencer par les 80 tables.
Construire par couches.
----
===== Couche 1 — Fondations =====
Construire en premier :
Tenant
User
Role
Permission
UserRole
RolePermission
Ces tables sont nécessaires partout.
----
===== Couche 2 — Référentiels =====
Créer :
Address
Country
Language
Currency
Timezone
----
===== Couche 3 — Catalogue =====
Créer :
Property
PropertyType
PropertyMedia
PropertyFeature
PropertyAvailability
PropertyRate
----
===== Couche 4 — Réservations =====
Créer :
Reservation
ReservationGuest
ReservationStatus
ReservationEvent
----
===== Couche 5 — Contrats =====
Créer :
Contract
ContractTemplate
ContractSignature
Document
----
===== Couche 6 — Paiements =====
Créer :
Payment
Invoice
Refund
AccountingEntry
----
===== Couche 7 — CRM =====
Créer :
Lead
Customer
Activity
Task
Pipeline
----
===== Couche 8 — Propriétaires =====
Créer :
Owner
OwnerProperty
OwnerDocument
----
===== Couche 9 — Notifications =====
Créer :
Notification
NotificationTemplate
Message
Campaign
----
===== Couche 10 — Administration =====
Créer :
AuditLog
FeatureFlag
CustomField
Workflow
----
===== Couche 11 — IA =====
Créer :
AiConversation
AiMessage
KnowledgeDocument
AutomationRule
----
===== Couche 12 — OTA =====
Créer :
Channel
ChannelConnection
PropertyDistribution
ChannelReservation
----
===== Couche 13 — Revenue Management =====
Créer :
PricingRule
DynamicPrice
RevenueSimulation
CompetitorSnapshot
----
===== Couche 14 — Sécurité =====
Créer :
Consent
SecurityPolicy
Risk
ComplianceAudit
----
===== Couche 15 — Internationalisation =====
Créer :
Translation
CurrencyRate
Region
EnterpriseLicense
----
====== Organisation du dossier Prisma ======
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
----
====== Configuration initiale ======
===== schema.prisma =====
Version minimale :
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
----
====== Première implémentation ======
===== Étape 1 =====
Créer :
prisma/models/tenant.prisma
----
===== Tenant =====
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 =====
enum TenantStatus {
ACTIVE
SUSPENDED
TRIAL
ARCHIVED
}
----
====== Étape 2 ======
Créer :
prisma/models/user.prisma
----
===== User =====
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[]
}
----
===== Role =====
model Role {
id String @id @default(uuid())
code String @unique
name String
userRoles UserRole[]
permissions RolePermission[]
}
----
===== Permission =====
model Permission {
id String @id @default(uuid())
code String @unique
name String
rolePermissions RolePermission[]
}
----
===== UserRole =====
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])
}
----
===== RolePermission =====
model RolePermission {
roleId String
permissionId String
role Role @relation(
fields:[roleId],
references:[id]
)
permission Permission @relation(
fields:[permissionId],
references:[id]
)
@@id([roleId, permissionId])
}
----
====== Génération ======
Après chaque bloc important :
npx prisma format
----
===== Validation =====
npx prisma validate
----
===== Génération Client =====
npx prisma generate
----
===== Migration =====
npx prisma migrate dev \
--name init_security
----
====== Vérification ======
Ouvrir :
npx prisma studio
Vérifier la présence :
Tenant
User
Role
Permission
UserRole
RolePermission
----
====== Phase 2-A — Fondation complète (Tenant + RBAC + Propriétés de sécurité) ======
Vous avez raison.
La version précédente contient :
* Tenant
* User
* Role
* Permission
* UserRole
* RolePermission
mais il manque plusieurs éléments indispensables pour un véritable système Enterprise :
* propriétés du compte utilisateur
* préférences utilisateur
* sessions
* refresh tokens
* MFA
* audit de connexion
* catalogue complet des permissions
Sans ces éléments, le Sprint 1 (Authentification) et le Sprint 19 (Sécurité Enterprise) ne pourront pas être implémentés proprement.
----
====== Modèle User complet ======
===== User =====
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[]
}
----
====== Préférences utilisateur ======
===== UserPreference =====
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]
)
}
----
====== Gestion des sessions ======
===== UserSession =====
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]
)
}
----
====== Refresh Tokens ======
===== RefreshToken =====
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]
)
}
----
====== Historique des connexions ======
===== LoginHistory =====
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]
)
}
----
====== MFA ======
===== UserMfaMethod =====
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]
)
}
----
====== RBAC Enterprise ======
===== Role =====
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[]
}
----
===== Permission =====
model Permission {
id String @id @default(uuid())
code String @unique
name String
description String?
module String
createdAt DateTime @default(now())
rolePermissions RolePermission[]
}
----
====== Catalogue initial des permissions ======
===== Auth =====
AUTH_LOGIN
AUTH_REGISTER
AUTH_RESET_PASSWORD
AUTH_MANAGE_USERS
----
===== Users =====
USER_READ
USER_CREATE
USER_UPDATE
USER_DELETE
----
===== Properties =====
PROPERTY_READ
PROPERTY_CREATE
PROPERTY_UPDATE
PROPERTY_DELETE
PROPERTY_PUBLISH
----
===== Reservations =====
RESERVATION_READ
RESERVATION_CREATE
RESERVATION_UPDATE
RESERVATION_CANCEL
----
===== Contracts =====
CONTRACT_READ
CONTRACT_CREATE
CONTRACT_SIGN
----
===== Payments =====
PAYMENT_READ
PAYMENT_CREATE
PAYMENT_REFUND
----
===== CRM =====
CRM_READ
CRM_WRITE
CRM_EXPORT
----
===== Administration =====
ADMIN_READ
ADMIN_WRITE
ADMIN_AUDIT
ADMIN_TENANT
----
====== Rôles système ======
===== SUPER_ADMIN =====
Accès complet plateforme.
----
===== TENANT_ADMIN =====
Administration d'agence.
----
===== AGENCY_MANAGER =====
Gestion opérationnelle.
----
===== OWNER =====
Extranet propriétaire.
----
===== AGENT =====
Collaborateur.
----
===== CUSTOMER =====
Client final.
----
====== Indexes recommandés ======
===== User =====
@@index([tenantId])
@@index([email])
@@index([active])
----
===== LoginHistory =====
@@index([userId])
@@index([createdAt])
----
===== UserSession =====
@@index([userId])
@@index([expiresAt])
----
====== Architecture du domaine Property ======
===== Entités =====
Property
PropertyType
PropertyStatus
PropertyAddress
PropertyFeature
PropertyMedia
PropertyAvailability
PropertyRate
PropertyOwner
----
====== Property ======
===== Table principale =====
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[]
}
----
====== PropertyType ======
===== Référentiel =====
model PropertyType {
id String @id @default(uuid())
code String @unique
name String
properties Property[]
}
----
===== Valeurs =====
HOUSE
APARTMENT
VILLA
STUDIO
LOFT
CHALET
COTTAGE
MOBILE_HOME
----
====== PropertyAddress ======
===== Adresse du bien =====
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]
)
}
----
====== PropertyFeature ======
===== Équipements =====
model PropertyFeature {
id String @id @default(uuid())
propertyId String
featureCode String
featureValue String?
property Property @relation(
fields:[propertyId],
references:[id]
)
}
----
===== Exemples =====
POOL
WIFI
PARKING
AIR_CONDITIONING
TERRACE
SEA_VIEW
PET_ALLOWED
----
====== PropertyMedia ======
===== Médias =====
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]
)
}
----
===== MediaType =====
IMAGE
VIDEO
VIRTUAL_TOUR
DOCUMENT
----
====== PropertyAvailability ======
===== Calendrier =====
model PropertyAvailability {
id String @id @default(uuid())
propertyId String
startDate DateTime
endDate DateTime
status AvailabilityStatus
property Property @relation(
fields:[propertyId],
references:[id]
)
}
----
===== Enum =====
enum AvailabilityStatus {
AVAILABLE
RESERVED
BLOCKED
MAINTENANCE
}
----
====== PropertyRate ======
===== Tarification =====
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]
)
}
----
====== PropertyOwner ======
===== Association propriétaire =====
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])
}
----
====== Indexes ======
===== Property =====
@@index([tenantId])
@@index([propertyTypeId])
@@index([published])
@@index([active])
@@index([title])
@@index([slug])
----
===== PropertyAvailability =====
@@index([propertyId])
@@index([startDate])
@@index([endDate])
----
===== PropertyRate =====
@@index([propertyId])
@@index([startDate])
@@index([endDate])
----
====== Domaine Owner ======
===== Architecture =====
Owner
OwnerAddress
OwnerDocument
OwnerBankAccount
PropertyOwner
----
====== Owner ======
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[]
}
----
====== OwnerAddress ======
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]
)
}
----
====== OwnerDocument ======
model OwnerDocument {
id String @id @default(uuid())
ownerId String
documentType String
fileUrl String
createdAt DateTime @default(now())
owner Owner @relation(
fields:[ownerId],
references:[id]
)
}
----
===== Document Types =====
IDENTITY
TAX_DOCUMENT
MANDATE
BANK_DETAILS
INSURANCE
----
====== OwnerBankAccount ======
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]
)
}
----
====== Domaine Reservation ======
===== Architecture =====
Reservation
ReservationGuest
ReservationStatusHistory
ReservationEvent
ReservationPricing
----
====== Reservation ======
===== Table centrale =====
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?
}
----
====== ReservationStatus ======
enum ReservationStatus {
DRAFT
PENDING
CONFIRMED
SIGNED
PAID
CHECKED_IN
COMPLETED
CANCELLED
}
----
====== ReservationSource ======
enum ReservationSource {
WEBSITE
BACKOFFICE
AIRBNB
BOOKING
VRBO
API
}
----
====== ReservationGuest ======
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]
)
}
----
====== ReservationPricing ======
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]
)
}
----
====== ReservationEvent ======
model ReservationEvent {
id String @id @default(uuid())
reservationId String
eventType String
payload Json?
createdAt DateTime @default(now())
reservation Reservation @relation(
fields:[reservationId],
references:[id]
)
}
----
===== Event Types =====
CREATED
CONFIRMED
SIGNED
PAID
CHECK_IN
CHECK_OUT
CANCELLED
----
====== ReservationStatusHistory ======
model ReservationStatusHistory {
id String @id @default(uuid())
reservationId String
previousStatus ReservationStatus?
newStatus ReservationStatus
changedAt DateTime @default(now())
reservation Reservation @relation(
fields:[reservationId],
references:[id]
)
}
----
====== Relations à ajouter ======
===== Property =====
Ajouter :
reservations Reservation[]
----
===== Tenant =====
Ajouter :
owners Owner[]
reservations Reservation[]
----
====== Indexes ======
===== Owner =====
@@index([tenantId])
@@index([email])
@@index([active])
----
===== Reservation =====
@@index([tenantId])
@@index([propertyId])
@@index([status])
@@index([checkInDate])
@@index([checkOutDate])
@@index([reference])
----
===== ReservationGuest =====
@@index([reservationId])
----
====== Validation ======
npx prisma format
npx prisma validate
npx prisma generate
----
====== Migration ======
npx prisma migrate dev \
--name owners_and_reservations
----
====== État actuel du schéma ======
À 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
----
====== Phase 2-E — Contrats, Documents, Paiements & Facturation ======
===== Objectif =====
Construire le premier flux métier complet de la plateforme :
Client
↓
Réservation
↓
Contrat
↓
Signature
↓
Paiement
↓
Facture
↓
Comptabilité
Cette phase couvre :
* Sprint 5 — Contrats & Signature Électronique
* Sprint 6 — Paiements & Facturation
----
====== Prérequis ======
Avant cette phase, le schéma doit déjà contenir :
Tenant
User
Role
Permission
Owner
Property
Reservation
----
====== Domaine Client ======
Avant les contrats et paiements, il faut définir le client.
----
===== Customer =====
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[]
}
----
===== CustomerAddress =====
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]
)
}
----
===== CustomerDocument =====
model CustomerDocument {
id String @id @default(uuid())
customerId String
documentType String
fileUrl String
createdAt DateTime @default(now())
customer Customer @relation(
fields:[customerId],
references:[id]
)
}
----
====== Mise à jour Reservation ======
===== Ajouter =====
customerId String?
----
===== Relation =====
customer Customer? @relation(
fields:[customerId],
references:[id]
)
----
====== Domaine Contrats ======
===== Architecture =====
Contract
ContractTemplate
ContractVersion
ContractSignature
----
====== Contract ======
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[]
}
----
===== ContractStatus =====
enum ContractStatus {
DRAFT
GENERATED
SENT
VIEWED
SIGNED
CANCELLED
}
----
====== ContractTemplate ======
model ContractTemplate {
id String @id @default(uuid())
code String @unique
name String
content String
active Boolean @default(true)
createdAt DateTime @default(now())
contracts Contract[]
}
----
====== ContractVersion ======
model ContractVersion {
id String @id @default(uuid())
contractId String
versionNumber Int
pdfUrl String
createdAt DateTime @default(now())
contract Contract @relation(
fields:[contractId],
references:[id]
)
}
----
====== ContractSignature ======
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]
)
}
----
====== Domaine Documents ======
===== Document =====
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]
)
}
----
====== Domaine Paiements ======
===== Architecture =====
Payment
PaymentTransaction
Refund
PaymentMethod
----
====== Payment ======
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[]
}
----
===== PaymentStatus =====
enum PaymentStatus {
PENDING
AUTHORIZED
PAID
FAILED
CANCELLED
REFUNDED
}
----
====== PaymentTransaction ======
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]
)
}
----
====== Refund ======
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]
)
}
----
====== Facturation ======
===== Invoice =====
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]
)
}
----
===== InvoiceStatus =====
enum InvoiceStatus {
DRAFT
ISSUED
PAID
PARTIALLY_PAID
CANCELLED
}
----
====== Comptabilité ======
===== AccountingEntry =====
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]
)
}
----
====== Indexes ======
===== Contract =====
@@index([reservationId])
@@index([status])
@@index([contractNumber])
----
===== Payment =====
@@index([reservationId])
@@index([status])
@@index([paymentReference])
----
===== Invoice =====
@@index([reservationId])
@@index([invoiceNumber])
@@index([status])
----
====== Validation ======
npx prisma format
npx prisma validate
npx prisma generate
----
====== Migration ======
npx prisma migrate dev \
--name contracts_payments_invoices
----
====== État du noyau métier ======
À ce stade, le schéma couvre :
Tenant
User
RBAC
Owner
Property
Customer
Reservation
Contract
Document
Payment
Invoice
Accounting
----
====== Flux métier désormais couvert ======
Owner
↓
Property
↓
Customer
↓
Reservation
↓
Contract
↓
Signature
↓
Payment
↓
Invoice
↓
Accounting
----
====== Phase 2-F — CRM & Relation Client ======
===== Objectif =====
Mettre en place le CRM intégré de la plateforme.
Contrairement à un CRM externe, ce module est directement connecté aux :
* Clients
* Réservations
* Contrats
* Paiements
* Propriétaires
* Campagnes marketing
Le CRM devient ainsi la vue 360° du client.
----
====== Architecture CRM ======
===== Domaines =====
Lead
Opportunity
Pipeline
PipelineStage
Activity
Task
CustomerNote
Tag
Segment
CustomerTag
LeadSource
----
====== Vue d'ensemble ======
Lead
↓
Qualification
↓
Opportunity
↓
Reservation
↓
Customer
----
====== Lead ======
===== Prospect =====
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[]
}
----
===== LeadStatus =====
enum LeadStatus {
NEW
QUALIFIED
CONTACTED
PROPOSAL
WON
LOST
ARCHIVED
}
----
====== LeadSource ======
===== Origine du prospect =====
model LeadSource {
id String @id @default(uuid())
code String @unique
name String
leads Lead[]
}
----
===== Valeurs =====
WEBSITE
PHONE
EMAIL
FACEBOOK
INSTAGRAM
GOOGLE
AIRBNB
BOOKING
REFERRAL
----
====== Pipeline ======
===== Pipeline commercial =====
model Pipeline {
id String @id @default(uuid())
tenantId String
name String
active Boolean @default(true)
stages PipelineStage[]
opportunities Opportunity[]
}
----
====== PipelineStage ======
model PipelineStage {
id String @id @default(uuid())
pipelineId String
name String
position Int
probability Int
pipeline Pipeline @relation(
fields:[pipelineId],
references:[id]
)
}
----
===== Exemple =====
Nouveau
Qualification
Proposition
Négociation
Gagné
Perdu
----
====== Opportunity ======
===== Opportunité commerciale =====
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]
)
}
----
===== OpportunityStatus =====
enum OpportunityStatus {
OPEN
WON
LOST
CANCELLED
}
----
====== Activity ======
===== Historique des interactions =====
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]
)
}
----
===== ActivityType =====
enum ActivityType {
CALL
EMAIL
MEETING
SMS
NOTE
TASK
SYSTEM
}
----
====== Task ======
===== Tâches CRM =====
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]
)
}
----
===== TaskPriority =====
enum TaskPriority {
LOW
MEDIUM
HIGH
URGENT
}
----
===== TaskStatus =====
enum TaskStatus {
OPEN
IN_PROGRESS
DONE
CANCELLED
}
----
====== CustomerNote ======
===== Notes internes =====
model CustomerNote {
id String @id @default(uuid())
customerId String
authorUserId String
content String
createdAt DateTime @default(now())
customer Customer @relation(
fields:[customerId],
references:[id]
)
}
----
====== Tag ======
===== Classification =====
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[]
}
----
====== CustomerTag ======
===== Relation N:N =====
model CustomerTag {
customerId String
tagId String
customer Customer @relation(
fields:[customerId],
references:[id]
)
tag Tag @relation(
fields:[tagId],
references:[id]
)
@@id([customerId, tagId])
}
----
====== Segment ======
===== Segmentation marketing =====
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]
)
}
----
====== Relations à ajouter ======
===== Customer =====
Ajouter :
activities Activity[]
notes CustomerNote[]
tags CustomerTag[]
----
===== Tenant =====
Ajouter :
leads Lead[]
opportunities Opportunity[]
pipelines Pipeline[]
tasks Task[]
segments Segment[]
----
====== Indexes ======
===== Lead =====
@@index([tenantId])
@@index([status])
@@index([email])
@@index([assignedToUserId])
----
===== Opportunity =====
@@index([tenantId])
@@index([status])
@@index([expectedCloseDate])
----
===== Task =====
@@index([assignedToUserId])
@@index([status])
@@index([dueDate])
----
====== Validation ======
npx prisma format
npx prisma validate
npx prisma generate
----
====== Migration ======
npx prisma migrate dev \
--name crm_and_customer_relationship
----
====== Fonctionnalités désormais couvertes ======
===== Sprint 8 =====
Prospects
Pipeline
Relances
Activités
Tâches
Segmentation
Historique client
Vue 360°
----
====== Nouveau flux métier ======
Lead
↓
Qualification
↓
Opportunity
↓
Reservation
↓
Contract
↓
Payment
↓
Customer Loyalty
----
====== État global du schéma ======
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
----
====== Phase 2-G — Notifications, Messagerie & Communication ======
===== Objectif =====
Mettre en place le centre de communication unifié de la plateforme.
Ce domaine couvre :
* Notifications Back Office
* Notifications Client
* Notifications Propriétaire
* Emails transactionnels
* SMS transactionnels
* Messagerie interne
* Conversations liées aux réservations
* Historique des communications
Ce domaine sera utilisé par :
Reservations
Contracts
Payments
CRM
Owner Portal
Administration
----
====== Architecture ======
===== Modèles =====
Notification
NotificationTemplate
NotificationPreference
Conversation
ConversationParticipant
Message
EmailLog
SmsLog
----
====== Notification ======
===== Centre de notifications =====
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])
}
----
===== NotificationType =====
enum NotificationType {
SYSTEM
RESERVATION
CONTRACT
PAYMENT
CRM
OWNER
SECURITY
MARKETING
}
----
===== NotificationStatus =====
enum NotificationStatus {
UNREAD
READ
ARCHIVED
}
----
====== NotificationTemplate ======
===== Modèles de notification =====
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])
}
----
===== NotificationChannel =====
enum NotificationChannel {
IN_APP
EMAIL
SMS
PUSH
}
----
====== NotificationPreference ======
===== Préférences utilisateur =====
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])
}
----
====== Conversation ======
===== Fil de discussion =====
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])
}
----
===== ConversationStatus =====
enum ConversationStatus {
OPEN
CLOSED
ARCHIVED
}
----
====== ConversationParticipant ======
===== Participants =====
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
])
}
----
====== Message ======
===== Message =====
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])
}
----
====== EmailLog ======
===== Historique Emails =====
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])
}
----
===== EmailStatus =====
enum EmailStatus {
PENDING
SENT
DELIVERED
OPENED
FAILED
BOUNCED
}
----
====== SmsLog ======
===== Historique SMS =====
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])
}
----
===== SmsStatus =====
enum SmsStatus {
PENDING
SENT
DELIVERED
FAILED
}
----
====== Relations à ajouter ======
===== User =====
notifications Notification[]
notificationPreferences NotificationPreference[]
conversationParticipants
ConversationParticipant[]
messages Message[]
----
===== Customer =====
conversationParticipants
ConversationParticipant[]
messages Message[]
----
===== Owner =====
conversationParticipants
ConversationParticipant[]
messages Message[]
----
===== Reservation =====
conversations Conversation[]
----
===== Tenant =====
notifications Notification[]
notificationTemplates NotificationTemplate[]
conversations Conversation[]
emailLogs EmailLog[]
smsLogs SmsLog[]
----
====== Validation ======
npx prisma format
npx prisma validate
npx prisma generate
----
====== Migration ======
npx prisma migrate dev \
--name notifications_messaging
----
====== Fonctionnalités couvertes ======
===== Sprint 9 =====
Notifications temps réel
Centre de notifications
Messagerie propriétaire
Messagerie client
Messagerie réservation
Emails transactionnels
SMS transactionnels
Préférences utilisateur
----
====== État du schéma ======
Après Phase 2-G :
≈ 60 modèles Prisma
avec les domaines :
Core
Security
Properties
Owners
Customers
Reservations
Contracts
Payments
CRM
Notifications
Messaging
----
====== Phase 2-H — Campagnes Marketing & Automatisation ======
===== Objectif =====
Mettre en place la couche marketing et automatisation de la plateforme.
Ce domaine permettra :
* Campagnes Email
* Campagnes SMS
* Segmentation avancée
* Marketing Automation
* Relances automatiques
* Nurturing prospects
* Fidélisation clients
* Déclencheurs métiers
Cette phase finalise :
Sprint 8 CRM
Sprint 9 Communication
Sprint 13 Automatisation & IA
----
====== Architecture ======
===== Modèles =====
Campaign
CampaignRecipient
CampaignExecution
MarketingSegment
MarketingEvent
AutomationRule
AutomationExecution
----
====== Campaign ======
===== Campagne Marketing =====
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])
}
----
===== CampaignType =====
enum CampaignType {
EMAIL
SMS
MIXED
PUSH
WORKFLOW
}
----
===== CampaignStatus =====
enum CampaignStatus {
DRAFT
SCHEDULED
RUNNING
COMPLETED
PAUSED
CANCELLED
}
----
====== CampaignRecipient ======
===== Destinataires =====
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])
}
----
===== CampaignRecipientStatus =====
enum CampaignRecipientStatus {
PENDING
SENT
OPENED
CLICKED
FAILED
UNSUBSCRIBED
}
----
====== CampaignExecution ======
===== Exécution =====
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])
}
----
===== ExecutionStatus =====
enum ExecutionStatus {
PENDING
RUNNING
COMPLETED
FAILED
}
----
====== MarketingSegment ======
===== Segmentation avancée =====
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])
}
----
===== Exemples =====
CLIENTS_FIDELES
CLIENTS_INACTIFS
PROPRIETAIRES_ACTIFS
PROSPECTS_CHAUDS
RESERVATIONS_30J
ANNIVERSAIRES
----
====== MarketingEvent ======
===== Événements Marketing =====
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])
}
----
===== MarketingEventType =====
enum MarketingEventType {
EMAIL_OPEN
EMAIL_CLICK
SMS_SENT
PAGE_VISIT
FORM_SUBMIT
LEAD_CREATED
RESERVATION_CREATED
CONTRACT_SIGNED
PAYMENT_COMPLETED
}
----
====== AutomationRule ======
===== Règle métier =====
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])
}
----
===== Exemples =====
ReservationConfirmed
↓
SendEmail
----------------
PaymentReceived
↓
GenerateInvoice
----------------
LeadCreated
↓
AssignSalesAgent
----
====== AutomationExecution ======
===== Historique =====
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])
}
----
====== Relations à ajouter ======
===== Tenant =====
campaigns Campaign[]
marketingSegments MarketingSegment[]
marketingEvents MarketingEvent[]
automationRules AutomationRule[]
----
===== Customer =====
campaignRecipients CampaignRecipient[]
marketingEvents MarketingEvent[]
----
===== Lead =====
campaignRecipients CampaignRecipient[]
marketingEvents MarketingEvent[]
----
===== User =====
createdCampaigns Campaign[]
@relation("CampaignCreator")
----
====== Validation ======
npx prisma format
npx prisma validate
npx prisma generate
----
====== Migration ======
npx prisma migrate dev \
--name marketing_automation
----
====== Fonctionnalités couvertes ======
===== CRM =====
Lead Nurturing
Relances
Segmentation
Scoring
Qualification
----
===== Communication =====
Campagnes Email
Campagnes SMS
Suivi Ouvertures
Suivi Clics
Désabonnement
----
===== Automatisation =====
Déclencheurs
Workflows
Actions Automatiques
Historisation
----
====== État du schéma ======
Après Phase 2-H :
≈ 67 modèles Prisma
avec les domaines :
Core
Security
Properties
Owners
Customers
Reservations
Contracts
Payments
CRM
Messaging
Marketing
Automation
----
====== Phase 2-I — Administration, Audit & Gouvernance ======
===== Objectif =====
Mettre en place la couche transverse de gouvernance de la plateforme.
Cette couche doit permettre :
* Audit complet des actions
* Historisation métier
* Activation dynamique de fonctionnalités
* Workflows métiers configurables
* Gouvernance des données
* Traçabilité réglementaire
* Préparation RGPD / ISO27001 / SOC2
Cette phase prépare directement :
Sprint 10 — Reporting
Sprint 11 — Administration
Sprint 19 — Gouvernance & Conformité
----
====== Architecture ======
===== Modèles =====
AuditLog
EntityHistory
FeatureFlag
Workflow
WorkflowStep
WorkflowInstance
WorkflowExecution
----
====== AuditLog ======
===== Journal d'audit =====
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])
}
----
===== AuditAction =====
enum AuditAction {
CREATE
UPDATE
DELETE
RESTORE
LOGIN
LOGOUT
EXPORT
IMPORT
EXECUTE
APPROVE
REJECT
}
----
====== EntityHistory ======
===== Historisation métier =====
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])
}
----
====== FeatureFlag ======
===== Activation dynamique =====
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])
}
----
===== Exemples =====
AI_ASSISTANT
OWNER_PORTAL_V2
OTA_SYNC
REVENUE_MANAGEMENT
ADVANCED_REPORTING
----
====== Workflow ======
===== Définition d'un workflow =====
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])
}
----
====== WorkflowStep ======
===== Étapes du workflow =====
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])
}
----
===== Exemple =====
ReservationApproval
↓
ManagerApproval
↓
ContractGeneration
↓
PaymentValidation
↓
Completed
----
====== WorkflowInstance ======
===== Exécution métier =====
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])
}
----
===== WorkflowInstanceStatus =====
enum WorkflowInstanceStatus {
RUNNING
WAITING
APPROVED
REJECTED
COMPLETED
CANCELLED
}
----
====== WorkflowExecution ======
===== Historique des étapes =====
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])
}
----
===== WorkflowExecutionStatus =====
enum WorkflowExecutionStatus {
PENDING
APPROVED
REJECTED
SKIPPED
COMPLETED
}
----
====== Relations à ajouter ======
===== Tenant =====
auditLogs AuditLog[]
entityHistories EntityHistory[]
featureFlags FeatureFlag[]
workflows Workflow[]
----
===== User =====
auditLogs AuditLog[]
entityHistories EntityHistory[]
workflowExecutions WorkflowExecution[]
----
====== Cas d'usage métiers ======
===== Réservation =====
Reservation Created
↓
AuditLog
↓
Workflow Instance
↓
Manager Approval
↓
Contract Generation
----
===== Paiement =====
Payment Refunded
↓
AuditLog
↓
History Snapshot
↓
Compliance Trace
----
===== Administration =====
Feature Enabled
↓
AuditLog
↓
History
↓
Reporting
----
====== Validation ======
npx prisma format
npx prisma validate
npx prisma generate
----
====== Migration ======
npx prisma migrate dev \
--name administration_audit_governance
----
====== Fonctionnalités couvertes ======
===== Administration =====
Feature Flags
Workflows
Validation
Approbation
----
===== Audit =====
Journalisation
Traçabilité
Historisation
Conformité
----
===== Gouvernance =====
Contrôle
Supervision
Rétention
Preuve d'audit
----
====== État du schéma ======
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
----
====== Phase 2-J — OTA & Channel Manager ======
===== Objectif =====
Construire le moteur de distribution multicanal de la plateforme.
Ce domaine permettra :
* Synchronisation Airbnb
* Synchronisation Booking.com
* Synchronisation Vrbo
* Synchronisation Abritel
* Publication automatique des biens
* Synchronisation des disponibilités
* Synchronisation des tarifs
* Synchronisation des réservations
* Gestion des erreurs de synchronisation
Cette phase couvre :
Sprint 12 — OTA & Distribution
Sprint 14 — API & Partenaires
Sprint 18 — Channel Manager Enterprise
----
====== Architecture ======
===== Modèles =====
Channel
ChannelConnection
PropertyDistribution
ChannelReservation
SyncExecution
SyncError
----
====== Channel ======
===== Catalogue OTA =====
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[]
}
----
===== ChannelType =====
enum ChannelType {
OTA
DIRECT
PARTNER
API
MARKETPLACE
}
----
===== Valeurs initiales =====
AIRBNB
BOOKING
VRBO
ABRITEL
EXPEDIA
DIRECT_WEBSITE
----
====== ChannelConnection ======
===== Connexion OTA =====
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])
}
----
====== PropertyDistribution ======
===== Publication d'un bien =====
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])
}
----
===== DistributionStatus =====
enum DistributionStatus {
DRAFT
PENDING
PUBLISHED
SUSPENDED
ERROR
ARCHIVED
}
----
====== ChannelReservation ======
===== Réservation OTA =====
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])
}
----
====== SyncExecution ======
===== Historique des synchronisations =====
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])
}
----
===== SyncType =====
enum SyncType {
PROPERTY_EXPORT
AVAILABILITY_EXPORT
RATE_EXPORT
RESERVATION_IMPORT
FULL_SYNC
}
----
===== SyncStatus =====
enum SyncStatus {
PENDING
RUNNING
COMPLETED
PARTIAL_SUCCESS
FAILED
}
----
====== SyncError ======
===== Gestion des erreurs =====
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])
}
----
====== Relations à ajouter ======
===== Tenant =====
channelConnections ChannelConnection[]
propertyDistributions PropertyDistribution[]
channelReservations ChannelReservation[]
syncExecutions SyncExecution[]
----
===== Property =====
propertyDistributions PropertyDistribution[]
----
===== Reservation =====
channelReservations ChannelReservation[]
----
====== Synchronisations couvertes ======
===== Disponibilités =====
PropertyAvailability
↓
OTA
----
===== Tarifs =====
PropertyRate
↓
OTA
----
===== Réservations =====
OTA
↓
ChannelReservation
↓
Reservation
----
===== Publication =====
Property
↓
PropertyDistribution
↓
Airbnb
Booking
Vrbo
Abritel
----
====== Validation ======
npx prisma format
npx prisma validate
npx prisma generate
----
====== Migration ======
npx prisma migrate dev \
--name ota_channel_manager
----
====== Fonctionnalités couvertes ======
===== Sprint 12 =====
Publication OTA
Synchronisation calendrier
Synchronisation tarifs
Import réservations
----
===== Sprint 14 =====
Partenaires
API externes
Marketplace
----
===== Sprint 18 =====
Channel Manager Enterprise
Multi-OTA
Monitoring synchronisations
Gestion erreurs
----
====== État du schéma ======
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
----
====== Phase 2-K — Revenue Management & Tarification Dynamique ======
===== Objectif =====
Construire le moteur de Revenue Management de la plateforme.
Ce domaine permettra :
* Tarification dynamique
* Yield Management
* Prévisions de revenus
* Analyse de la demande
* Analyse concurrentielle
* Optimisation automatique des prix
* Simulation financière
* Aide à la décision
Cette phase couvre :
Sprint 10 — Reporting
Sprint 13 — IA & Prévisions
Sprint 17 — Revenue Management
Sprint 20 — Enterprise Analytics
----
====== Architecture ======
===== Modèles =====
PricingRule
DynamicPrice
RevenueForecast
RevenueSimulation
CompetitorSnapshot
MarketDemand
----
====== PricingRule ======
===== Règle tarifaire =====
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])
}
----
===== Exemples =====
Occupation > 80%
↓
+15%
----------------
Weekend
↓
+10%
----------------
Haute saison
↓
+25%
----
====== DynamicPrice ======
===== Prix calculé =====
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])
}
----
====== RevenueForecast ======
===== Prévisions =====
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])
}
----
====== RevenueSimulation ======
===== Simulation =====
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])
}
----
===== Exemple =====
Prix +10%
↓
Occupation -3%
↓
CA +6%
----
====== CompetitorSnapshot ======
===== Veille concurrentielle =====
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])
}
----
====== MarketDemand ======
===== Indicateurs marché =====
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])
}
----
====== Relations à ajouter ======
===== Property =====
pricingRules PricingRule[]
dynamicPrices DynamicPrice[]
revenueForecasts RevenueForecast[]
revenueSimulations RevenueSimulation[]
competitorSnapshots CompetitorSnapshot[]
----
===== Tenant =====
pricingRules PricingRule[]
dynamicPrices DynamicPrice[]
revenueForecasts RevenueForecast[]
revenueSimulations RevenueSimulation[]
marketDemands MarketDemand[]
----
====== Cas d'usage ======
===== Tarification dynamique =====
Occupation
+
Saisonnalité
+
Concurrence
+
Demande
↓
Prix optimal
----
===== Prévision =====
Historique réservations
↓
Forecast IA
↓
Revenus prévus
----
===== Simulation =====
Nouveau tarif
↓
Simulation
↓
Impact CA
----
====== Validation ======
npx prisma format
npx prisma validate
npx prisma generate
----
====== Migration ======
npx prisma migrate dev \
--name revenue_management
----
====== Fonctionnalités couvertes ======
===== Revenue Management =====
Yield Management
Dynamic Pricing
Optimisation tarifaire
----
===== Analytics =====
Forecast
Simulation
Benchmark concurrence
Demand Analytics
----
===== IA =====
Aide à la décision
Prévisions
Recommandations
----
====== État du schéma ======
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
----
====== Phase 2-L — Sécurité Enterprise & Conformité ======
===== Objectif =====
Construire la couche Enterprise de sécurité, conformité et gouvernance des données.
Cette phase permettra :
* Conformité RGPD
* Gestion des consentements
* Gestion des risques
* Gouvernance des données
* Classification des données
* Rétention réglementaire
* Gestion des incidents de sécurité
* Préparation ISO 27001
* Préparation SOC2
* Préparation NIS2
Cette phase couvre :
Sprint 19
Governance
Compliance
Enterprise Security
----
====== Architecture ======
===== Modèles =====
Consent
SecurityPolicy
Risk
ComplianceAudit
SecurityIncident
DataClassification
RetentionPolicy
----
====== Consent ======
===== Gestion des consentements =====
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])
}
----
===== ConsentType =====
enum ConsentType {
GDPR
COOKIES
MARKETING_EMAIL
MARKETING_SMS
PROFILING
THIRD_PARTY_SHARING
}
----
====== SecurityPolicy ======
===== Politiques de sécurité =====
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])
}
----
===== Exemples =====
PASSWORD_POLICY
SESSION_POLICY
MFA_POLICY
RETENTION_POLICY
ACCESS_CONTROL_POLICY
----
====== Risk ======
===== Registre des risques =====
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])
}
----
===== RiskCategory =====
enum RiskCategory {
SECURITY
COMPLIANCE
OPERATIONAL
FINANCIAL
LEGAL
TECHNICAL
}
----
===== RiskStatus =====
enum RiskStatus {
IDENTIFIED
ASSESSED
MITIGATED
ACCEPTED
CLOSED
}
----
====== ComplianceAudit ======
===== Audits réglementaires =====
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])
}
----
===== ComplianceAuditType =====
enum ComplianceAuditType {
GDPR
ISO27001
SOC2
NIS2
INTERNAL
}
----
===== ComplianceAuditStatus =====
enum ComplianceAuditStatus {
PLANNED
RUNNING
COMPLETED
CLOSED
}
----
====== SecurityIncident ======
===== Gestion des incidents =====
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])
}
----
===== IncidentSeverity =====
enum IncidentSeverity {
LOW
MEDIUM
HIGH
CRITICAL
}
----
===== IncidentStatus =====
enum IncidentStatus {
OPEN
INVESTIGATING
MITIGATED
RESOLVED
CLOSED
}
----
====== DataClassification ======
===== Classification des données =====
model DataClassification {
id String @id @default(uuid())
code String @unique
name String
description String?
level ClassificationLevel
active Boolean @default(true)
createdAt DateTime @default(now())
}
----
===== ClassificationLevel =====
enum ClassificationLevel {
PUBLIC
INTERNAL
CONFIDENTIAL
RESTRICTED
}
----
===== Exemples =====
Client
→ CONFIDENTIAL
----------------
Paiement
→ RESTRICTED
----------------
Catalogue public
→ PUBLIC
----
====== RetentionPolicy ======
===== Politique de conservation =====
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])
}
----
===== Exemples =====
AuditLog
3650 jours
----------------
LoginHistory
365 jours
----------------
MarketingEvent
1095 jours
----------------
Consent
1825 jours
----
====== Relations à ajouter ======
===== Tenant =====
consents Consent[]
securityPolicies SecurityPolicy[]
risks Risk[]
complianceAudits ComplianceAudit[]
securityIncidents SecurityIncident[]
retentionPolicies RetentionPolicy[]
----
===== User =====
consents Consent[]
ownedRisks Risk[]
reportedIncidents SecurityIncident[]
----
===== Customer =====
consents Consent[]
----
====== Validation ======
npx prisma format
npx prisma validate
npx prisma generate
----
====== Migration ======
npx prisma migrate dev \
--name enterprise_security_compliance
----
====== Fonctionnalités couvertes ======
===== RGPD =====
Consentements
Traçabilité
Conservation
Export
Suppression
----
===== Sécurité =====
Politiques
Incidents
MFA
Gestion des risques
----
===== Gouvernance =====
Classification
Rétention
Audit
Conformité
----
====== État du schéma ======
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
----
====== Phase 2-M — Internationalisation & Multi-régions ======
===== Objectif =====
Transformer la plateforme en solution SaaS internationale capable de gérer :
* Plusieurs langues
* Plusieurs devises
* Plusieurs pays
* Plusieurs fuseaux horaires
* Plusieurs régions d'exploitation
* Plusieurs marques
* Plusieurs réseaux d'agences
* Plusieurs juridictions
Cette phase couvre :
Sprint 15 — Réseau d'agences
Sprint 20 — Internationalisation
Enterprise SaaS
Commercialisation internationale
----
====== Architecture ======
===== Modèles =====
Country
Currency
Language
Timezone
CurrencyRate
Translation
Region
EnterpriseLicense
----
====== Country ======
===== Référentiel pays =====
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[]
}
----
===== Exemples =====
FR
BE
CH
ES
IT
DE
UK
US
CA
----
====== Currency ======
===== Référentiel devises =====
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[]
}
----
===== Exemples =====
EUR
USD
GBP
CHF
CAD
----
====== Language ======
===== Référentiel langues =====
Norme ISO 639.
model Language {
code String @id
name String
nativeName String
active Boolean @default(true)
createdAt DateTime @default(now())
translations Translation[]
}
----
===== Exemples =====
fr
en
de
es
it
nl
----
====== Timezone ======
===== Fuseaux horaires =====
model Timezone {
id String @id
displayName String
utcOffset String
active Boolean @default(true)
}
----
===== Exemples =====
Europe/Paris
Europe/London
America/New_York
America/Montreal
Asia/Tokyo
----
====== CurrencyRate ======
===== Taux de change =====
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])
}
----
====== Translation ======
===== Traductions =====
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])
}
----
===== Exemples =====
property.title
reservation.confirm
payment.invoice
----
====== Region ======
===== Régions d'exploitation =====
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
])
}
----
===== Exemples =====
FR_OCCITANIE
FR_PACA
FR_IDF
ES_CATALUNYA
US_FLORIDA
----
====== EnterpriseLicense ======
===== Licence SaaS Enterprise =====
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])
}
----
===== EnterpriseEdition =====
enum EnterpriseEdition {
COMMUNITY
PROFESSIONAL
BUSINESS
ENTERPRISE
ENTERPRISE_PLUS
}
----
====== Évolutions du modèle Tenant ======
===== Tenant =====
Ajouter :
countryCode String?
currencyCode String?
languageCode String?
timezoneId String?
enterpriseLicense EnterpriseLicense?
----
===== Relations =====
country Country?
currency Currency?
language Language?
timezone Timezone?
----
====== Évolutions du modèle Property ======
===== Property =====
Ajouter :
currencyCode String?
----
===== Relation =====
currency Currency?
----
====== Évolutions du modèle Reservation ======
===== Reservation =====
Ajouter :
currencyCode String?
exchangeRate Decimal?
----
====== Cas d'usage ======
===== Multi-langues =====
FR
↓
EN
↓
DE
↓
ES
----
===== Multi-devises =====
EUR
↓
USD
↓
GBP
↓
CHF
----
===== Multi-régions =====
France
↓
Occitanie
↓
Agence
↓
Biens
----
===== Réseau international =====
Tenant
↓
Multi-marques
↓
Multi-pays
↓
Multi-régions
----
====== Validation ======
npx prisma format
npx prisma validate
npx prisma generate
----
====== Migration ======
npx prisma migrate dev \
--name internationalization_multiregion
----
====== Fonctionnalités couvertes ======
===== Internationalisation =====
Multi-langues
Multi-devises
Multi-fuseaux
----
===== Réseau =====
Multi-pays
Multi-régions
Multi-agences
----
===== Enterprise =====
Licensing
Commercialisation
SaaS mondial
----
====== État du schéma ======
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
----
====== Phase 2-M — Internationalisation & Multi-régions ======
===== Objectif =====
Transformer la plateforme en solution SaaS internationale capable de gérer :
* Plusieurs langues
* Plusieurs devises
* Plusieurs pays
* Plusieurs fuseaux horaires
* Plusieurs régions d'exploitation
* Plusieurs marques
* Plusieurs réseaux d'agences
* Plusieurs juridictions
Cette phase couvre :
Sprint 15 — Réseau d'agences
Sprint 20 — Internationalisation
Enterprise SaaS
Commercialisation internationale
----
====== Architecture ======
===== Modèles =====
Country
Currency
Language
Timezone
CurrencyRate
Translation
Region
EnterpriseLicense
----
====== Country ======
===== Référentiel pays =====
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[]
}
----
===== Exemples =====
FR
BE
CH
ES
IT
DE
UK
US
CA
----
====== Currency ======
===== Référentiel devises =====
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[]
}
----
===== Exemples =====
EUR
USD
GBP
CHF
CAD
----
====== Language ======
===== Référentiel langues =====
Norme ISO 639.
model Language {
code String @id
name String
nativeName String
active Boolean @default(true)
createdAt DateTime @default(now())
translations Translation[]
}
----
===== Exemples =====
fr
en
de
es
it
nl
----
====== Timezone ======
===== Fuseaux horaires =====
model Timezone {
id String @id
displayName String
utcOffset String
active Boolean @default(true)
}
----
===== Exemples =====
Europe/Paris
Europe/London
America/New_York
America/Montreal
Asia/Tokyo
----
====== CurrencyRate ======
===== Taux de change =====
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])
}
----
====== Translation ======
===== Traductions =====
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])
}
----
===== Exemples =====
property.title
reservation.confirm
payment.invoice
----
====== Region ======
===== Régions d'exploitation =====
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
])
}
----
===== Exemples =====
FR_OCCITANIE
FR_PACA
FR_IDF
ES_CATALUNYA
US_FLORIDA
----
====== EnterpriseLicense ======
===== Licence SaaS Enterprise =====
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])
}
----
===== EnterpriseEdition =====
enum EnterpriseEdition {
COMMUNITY
PROFESSIONAL
BUSINESS
ENTERPRISE
ENTERPRISE_PLUS
}
----
====== Évolutions du modèle Tenant ======
===== Tenant =====
Ajouter :
countryCode String?
currencyCode String?
languageCode String?
timezoneId String?
enterpriseLicense EnterpriseLicense?
----
===== Relations =====
country Country?
currency Currency?
language Language?
timezone Timezone?
----
====== Évolutions du modèle Property ======
===== Property =====
Ajouter :
currencyCode String?
----
===== Relation =====
currency Currency?
----
====== Évolutions du modèle Reservation ======
===== Reservation =====
Ajouter :
currencyCode String?
exchangeRate Decimal?
----
====== Cas d'usage ======
===== Multi-langues =====
FR
↓
EN
↓
DE
↓
ES
----
===== Multi-devises =====
EUR
↓
USD
↓
GBP
↓
CHF
----
===== Multi-régions =====
France
↓
Occitanie
↓
Agence
↓
Biens
----
===== Réseau international =====
Tenant
↓
Multi-marques
↓
Multi-pays
↓
Multi-régions
----
====== Validation ======
npx prisma format
npx prisma validate
npx prisma generate
----
====== Migration ======
npx prisma migrate dev \
--name internationalization_multiregion
----
====== Fonctionnalités couvertes ======
===== Internationalisation =====
Multi-langues
Multi-devises
Multi-fuseaux
----
===== Réseau =====
Multi-pays
Multi-régions
Multi-agences
----
===== Enterprise =====
Licensing
Commercialisation
SaaS mondial
----
====== État du schéma ======
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
----
====== Phase 2-N — IA, Knowledge Base & Automatisation Avancée ======
===== Objectif =====
Construire la couche d'intelligence artificielle de la plateforme.
Cette couche permettra :
* Assistant IA interne
* Recherche sémantique
* Base de connaissances
* Recommandations intelligentes
* Analyse documentaire
* Automatisation avancée
* Aide à la décision
* Support opérationnel
Cette phase finalise :
Sprint 13 — IA & Automatisation
Sprint 20 — Enterprise Analytics
----
====== Architecture ======
===== Modèles =====
AiConversation
AiMessage
KnowledgeDocument
KnowledgeChunk
AutomationScenario
AutomationExecution
Recommendation
----
====== AiConversation ======
===== Conversations IA =====
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])
}
----
===== Exemples =====
Analyse réservations
Prévision revenus
Recherche client
Analyse contrat
Support utilisateur
----
====== AiMessage ======
===== Messages IA =====
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])
}
----
===== AiRole =====
enum AiRole {
SYSTEM
USER
ASSISTANT
TOOL
}
----
====== KnowledgeDocument ======
===== Base documentaire =====
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])
}
----
===== KnowledgeSourceType =====
enum KnowledgeSourceType {
DOCUMENT
CONTRACT
FAQ
WEBSITE
POLICY
PROCEDURE
}
----
====== KnowledgeChunk ======
===== Découpage vectoriel =====
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])
}
----
====== AutomationScenario ======
===== Automatisations avancées =====
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])
}
----
===== Exemples =====
ReservationConfirmed
↓
SendContract
↓
CreateTask
↓
NotifyOwner
----------------
PaymentReceived
↓
GenerateInvoice
↓
NotifyCustomer
----
====== AutomationExecution ======
===== Historique d'exécution =====
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])
}
----
====== Recommendation ======
===== Recommandations IA =====
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])
}
----
===== RecommendationType =====
enum RecommendationType {
PRICE_OPTIMIZATION
CUSTOMER_RETENTION
LEAD_CONVERSION
REVENUE_FORECAST
PROPERTY_IMPROVEMENT
RISK_ALERT
AUTOMATION
}
----
====== Relations à ajouter ======
===== Tenant =====
aiConversations AiConversation[]
knowledgeDocuments KnowledgeDocument[]
automationScenarios AutomationScenario[]
recommendations Recommendation[]
----
===== User =====
aiConversations AiConversation[]
----
===== Document =====
knowledgeDocuments KnowledgeDocument[]
----
====== Cas d'usage ======
===== Assistant Agence =====
"Quels biens sont sous-performants ?"
↓
Analyse IA
↓
Recommandations
----
===== Assistant Commercial =====
"Nouveaux leads prioritaires"
↓
Scoring
↓
Liste qualifiée
----
===== Assistant Revenue =====
"Optimiser les tarifs"
↓
Forecast
↓
Recommandations
----
===== Recherche documentaire =====
Question
↓
RAG
↓
KnowledgeDocument
↓
KnowledgeChunk
↓
Réponse
----
====== Validation ======
npx prisma format
npx prisma validate
npx prisma generate
----
====== Migration ======
npx prisma migrate dev \
--name ai_knowledge_automation
----
====== Fonctionnalités couvertes ======
===== IA =====
Assistant conversationnel
Recherche sémantique
Analyse métier
Aide à la décision
----
===== Knowledge Base =====
FAQ
Procédures
Contrats
Documentation
----
===== Automatisation =====
Workflows avancés
Scénarios
Actions automatiques
----
====== État final du schéma ======
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
----
====== Fin de la Phase 2 ======
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