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
Étape suivante
Phase 2-H
Campagnes Marketing & Automatisation
Campaign CampaignRecipient CampaignExecution MarketingSegment MarketingEvent AutomationRule AutomationExecution
Cette phase permettra de finaliser complètement :
- Sprint 8 CRM
- Sprint 9 Communication
- Sprint 13 Automatisation & IA