====== 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