generator client { provider = "prisma-client" output = "../src/generated/prisma" } datasource db { provider = "postgresql" } enum UserRole { OWNER CE_MANAGER CE_MEMBER TOURIST ADMIN } enum CarbetStatus { DRAFT PUBLISHED ARCHIVED } enum MediaType { PHOTO VIDEO } enum AvailabilityScope { PUBLIC CE_ONLY } enum AvailabilityBlockReason { NONE CE_BLOCKED WEEKEND_BLOCKED } enum BookingStatus { PENDING CONFIRMED CANCELLED COMPLETED } enum PaymentStatus { PENDING AUTHORIZED SUCCEEDED FAILED REFUNDED } enum SubscriptionStatus { TRIAL ACTIVE PAST_DUE CANCELED } enum AccessType { ROAD_AND_RIVER RIVER_ONLY } enum TransportMode { OWNER_PROVIDES SELF_ARRANGE PARTNER_PROVIDER } model Organization { id String @id @default(cuid()) name String slug String @unique description String? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt members User[] @@index([name]) } model User { id String @id @default(cuid()) email String @unique passwordHash String firstName String lastName String phone String? role UserRole organizationId String? avatarUrl String? isActive Boolean @default(true) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt organization Organization? @relation(fields: [organizationId], references: [id], onDelete: SetNull) carbets Carbet[] @relation("CarbetOwner") bookings Booking[] @relation("BookingTenant") reviews Review[] @relation("ReviewAuthor") subscriptions Subscription[] @@index([organizationId]) @@index([role]) } model Carbet { id String @id @default(cuid()) ownerId String title String slug String @unique description String river String latitude Decimal @db.Decimal(9, 6) longitude Decimal @db.Decimal(9, 6) embarkPoint String // Pirogue : obligatoire pour RIVER_ONLY, optionnelle pour ROAD_AND_RIVER // (estimation pour ceux qui veulent quand même venir en pirogue). pirogueDurationMin Int? accessType AccessType @default(ROAD_AND_RIVER) // Détails d'accès route pour ROAD_AND_RIVER (GPS, distance, type de piste). roadAccessNote String? capacity Int // Prix par nuit pour le carbet entier (toute capacité). En euros. nightlyPrice Decimal @db.Decimal(10, 2) @default(0) // Contraintes séjour (plugin min-stay). null = pas de contrainte. minStayNights Int? maxStayNights Int? minCapacity Int? // Contraintes saisonnières (plugin seasonality). JSON libre, schéma type : // { closedInLowWater: bool, closedSeasons: ["WET"|"DRY"|"LOW_WATER"][], note: string } seasonalConstraints Json? // Plugin pirogue-providers : qui organise le transport ? transportMode TransportMode? pirogueProviderId String? status CarbetStatus @default(DRAFT) lastBookedAt DateTime? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt owner User @relation("CarbetOwner", fields: [ownerId], references: [id], onDelete: Restrict) pirogueProvider PirogueProvider? @relation(fields: [pirogueProviderId], references: [id], onDelete: SetNull) amenities CarbetAmenity[] media Media[] availabilities Availability[] bookings Booking[] reviews Review[] subscriptions Subscription[] @@index([ownerId]) @@index([status]) @@index([river]) @@index([accessType]) @@index([pirogueProviderId]) } model PirogueProvider { id String @id @default(cuid()) name String contactEmail String? contactPhone String? rivers String[] @default([]) pricingNote String? description String? active Boolean @default(true) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt carbets Carbet[] @@index([active]) } model Amenity { id String @id @default(cuid()) key String @unique label String description String? createdAt DateTime @default(now()) carbets CarbetAmenity[] } model CarbetAmenity { carbetId String amenityId String carbet Carbet @relation(fields: [carbetId], references: [id], onDelete: Cascade) amenity Amenity @relation(fields: [amenityId], references: [id], onDelete: Cascade) @@id([carbetId, amenityId]) @@index([amenityId]) } model Media { id String @id @default(cuid()) carbetId String type MediaType s3Key String s3Url String sortOrder Int @default(0) createdAt DateTime @default(now()) carbet Carbet @relation(fields: [carbetId], references: [id], onDelete: Cascade) @@index([carbetId, sortOrder]) } model Availability { id String @id @default(cuid()) carbetId String startDate DateTime endDate DateTime scope AvailabilityScope @default(PUBLIC) blockReason AvailabilityBlockReason @default(NONE) isAvailable Boolean @default(true) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt carbet Carbet @relation(fields: [carbetId], references: [id], onDelete: Cascade) @@index([carbetId]) @@index([scope, blockReason]) @@index([startDate, endDate]) } model Booking { id String @id @default(cuid()) carbetId String tenantId String startDate DateTime endDate DateTime guestCount Int status BookingStatus @default(PENDING) amount Decimal @db.Decimal(10, 2) currency String @default("EUR") paymentStatus PaymentStatus @default(PENDING) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt carbet Carbet @relation(fields: [carbetId], references: [id], onDelete: Restrict) tenant User @relation("BookingTenant", fields: [tenantId], references: [id], onDelete: Restrict) review Review? @@index([carbetId]) @@index([tenantId]) @@index([status, paymentStatus]) @@index([startDate, endDate]) } model Subscription { id String @id @default(cuid()) ownerId String carbetId String provider String providerSubId String? @unique status SubscriptionStatus @default(TRIAL) startedAt DateTime @default(now()) renewedAt DateTime? canceledAt DateTime? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt owner User @relation(fields: [ownerId], references: [id], onDelete: Restrict) carbet Carbet @relation(fields: [carbetId], references: [id], onDelete: Cascade) @@index([ownerId]) @@index([carbetId]) @@index([status]) } model Review { id String @id @default(cuid()) bookingId String @unique carbetId String authorId String rating Int comment String? hostResponse String? hostRespondedAt DateTime? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt booking Booking @relation(fields: [bookingId], references: [id], onDelete: Cascade) carbet Carbet @relation(fields: [carbetId], references: [id], onDelete: Restrict) author User @relation("ReviewAuthor", fields: [authorId], references: [id], onDelete: Restrict) @@index([carbetId]) @@index([authorId]) } model Plugin { key String @id name String description String category String version String @default("0.1.0") enabled Boolean @default(false) config Json @default("{}") migrationsApplied String[] @default([]) installedAt DateTime @default(now()) updatedAt DateTime @updatedAt lastEnabledAt DateTime? lastDisabledAt DateTime? @@index([category]) @@index([enabled]) } model ContentPage { slug String lang String @default("fr") title String body String // 'general' (about, faq, ...) ou 'legal' (cgv, mentions, ...) category String @default("general") published Boolean @default(true) lastEditedBy String? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt @@id([slug, lang]) @@index([slug]) @@index([category]) @@index([published]) } model AuditLog { id String @id @default(cuid()) scope String event String target String? actorEmail String? details Json @default("{}") createdAt DateTime @default(now()) @@index([scope]) @@index([event]) @@index([actorEmail]) @@index([createdAt]) } model Setting { key String @id value Json @default("{}") updatedAt DateTime @updatedAt updatedBy String? } model Translation { key String lang String value String updatedAt DateTime @updatedAt updatedBy String? @@id([key, lang]) @@index([lang]) }