Ceci est une ancienne révision du document !
Table des matières
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
Étape suivante
Phase 2-K
Revenue Management & Tarification Dynamique
PricingRule DynamicPrice RevenueForecast RevenueSimulation CompetitorSnapshot MarketDemand
Cette phase couvrira :
- Sprint 10 Reporting
- Sprint 13 IA & Prévisions
- Sprint 17 Revenue Management
- Sprint 20 Enterprise Analytics