Sixième Étoile — Documentation

Key Relationships

Foreign-key graph, multi-tenancy chain, and important nullable / optional relations.

This page documents the primary relationships between models. Understanding these links is essential before writing queries or API handlers.

Multi-tenancy: the organizationId chain

Every VTC business model carries a mandatory organizationId (FK to Organization). API handlers enforce row-level isolation by always filtering on the active organization from the authenticated session.

Organization
├── Contact (organizationId)
├── Quote (organizationId)
│   └── QuoteLine (quoteId)
├── Mission (organizationId)
│   └── MissionTransition (missionId)
│   └── MissionExpense (missionId)
├── Invoice (organizationId)
│   └── InvoiceLine (invoiceId)
│   └── InvoicePayment (invoiceId)
├── Order (organizationId)
├── Vehicle (organizationId)
├── Driver (organizationId)
├── PricingZone (organizationId)
└── … all other VTC models

Contact → commercial documents

A Contact is the anchor for all commercial activity:

Contact
├── quotes[]          — all quotes for this contact
├── invoices[]        — all invoices for this contact
├── orders[]          — all orders (dossiers) for this contact
├── endCustomers[]    — individual passengers under this partner/agency
├── partnerContract   — 1:1 commercial contract (PARTNER/AGENCY only)
└── portalUsers[]     — agency portal accounts (AGENCY only)

An EndCustomer (individual passenger within an agency) can be attributed to a Quote via Quote.endCustomerId, bypassing the parent agency contact for personalized communication.

Quote → lines → missions

Quote
├── lines[]           — ordered list of QuoteLine items
│   └── missions[]    — operational Mission(s) spawned per dispatchable line
└── missions[]        — all missions for this quote (convenience relation)

Each QuoteLine with dispatchable = true can spawn one or more Mission records. The link is Mission.quoteLineId → QuoteLine.id.

Mission.quoteId — nullable

Mission.quoteId is nullable. There are two kinds of missions:

KindquoteIdDescription
Commercial missionSetCreated from a QuoteLine, invoiceable
Internal tasknullCreated directly by the operator (staff transfer, depot move, etc.) — not linked to a quote, excluded from invoice generation

When writing queries that join MissionQuote, always use a LEFT JOIN or include: { quote: true } — never assume quoteId is present.

Quote → Invoice

A Quote can have multiple Invoice records (e.g. deposit + balance, or supplement invoices). The link is Invoice.quoteId → Quote.id (nullable — standalone invoices exist).

Quote (status=ACCEPTED)
└── invoices[]
    ├── Invoice (STANDARD)
    ├── Invoice (SUPPLEMENT)
    └── Invoice (CREDIT_NOTE)

Order grouping

An Order acts as a "dossier" grouping:

Order
├── quotes[]     — one or more Quote records
├── missions[]   — all operational Missions for the order
└── invoices[]   — all Invoice records for the order

Mission.ref (e.g. ORD-2026-001-01) encodes the order reference and the mission sequence number within that order.

Fleet relations

Vehicle ──────────── VehicleCategory
    │                     │
    └── OperatingBase     └── ZoneRoute / ExcursionPackage / DispoPackage
                               (Method 1 pricing grids per category)

A Vehicle must belong to both a VehicleCategory and an OperatingBase. The pricing engine uses the vehicle's base coordinates for deadhead distance calculation.

Driver licensing & compliance

Driver
├── driverLicenses[]          — DriverLicense (M:N with LicenseCategory)
├── driverCalendarEvents[]    — absences and unavailability periods
├── driverRSECounters[]       — daily RSE driving/amplitude/rest counters
└── driverLocations           — latest GPS position (1:1)

OrganizationLicenseRule defines the RSE thresholds per LicenseCategory (max daily driving, amplitude, rest, break intervals). The compliance engine reads these dynamically — no hardcoded limits.

PartnerContract

A PartnerContract (1:1 with Contact) holds commercial grid assignments for partner/agency contacts:

PartnerContract
├── zoneRoutes[]           — ZoneRoute entries via PartnerContractZoneRoute (M:N)
├── excursionPackages[]    — ExcursionPackage entries (M:N)
└── dispoPackages[]        — DispoPackage entries (M:N)

Each junction table (PartnerContractZoneRoute, etc.) carries an optional overridePrice — when set, the partner-specific price takes precedence over the catalog price.

Agency portal

Contact (type=AGENCY)
├── portalUsers[]           — AgencyPortalUser (M:1 with User)
│   ├── delegationsAsSource[]    — StaffDelegation (coverage handoff FROM this user)
│   └── delegationsAsDelegate[]  — StaffDelegation (coverage handoff TO this user)
└── clientDelegationsAsAgency[]  — ClientDelegation (per-client ownership transfers)

StaffDelegation models "while staff A is away, staff B covers all their clients". ClientDelegation models "this specific end-client is owned by this specific staff member". They are distinct models — do not confuse them.

Tracking chain

Mission
└── trackingTokens[]      — TrackingToken (public magic links)
    ├── messages[]         — TrackingMessage (customer messages)
    └── notifications[]    — TrackingNotification (delay alerts, rate-limited)

Mission
└── driverLocations[]     — DriverLocation (current position)
└── locationHistory[]     — DriverLocationHistory (breadcrumb trail)

A TrackingToken expires automatically: the API checks expiresAt on every request and computes it as Mission.actualDropoffAt + Organization.gracePeriodMinutes.

Activity & audit log

Organization
└── activities[]           — Activity (immutable business-level event log)
Organization
└── auditLogs[]            — AuditLog (API-level developer audit — keys, webhooks)
Quote
├── statusAuditLogs[]      — QuoteStatusAuditLog (status transitions)
└── quoteNotesAuditLogs[]  — QuoteNotesAuditLog (notes edits)
Driver
└── complianceAuditLogs[]  — ComplianceAuditLog (RSE decisions: APPROVED/BLOCKED/WARNING)

MissionAuditLog was unified into Activity in Epic 74 (migration 20260420000000_activity_unification). The legacy table is preserved as mission_audit_log_legacy and is not exposed via Prisma.

Was this page helpful?

On this page