c77d0a8e89
Build & Push / Build & Push Docker Image (push) Failing after 10m39s
Replace storefront naming across source, tests, docs, config, and production scripts. Rename the legacy top-level app directory and Carplace component files, remove duplicate storefront startup scripts, and refresh the lockfile.
779 lines
18 KiB
Markdown
779 lines
18 KiB
Markdown
# Database Schema and Table Relationships
|
|
|
|
This document explains the current Prisma schema, how the data is partitioned, and how the main tables relate to each other.
|
|
|
|
Source of truth:
|
|
|
|
- `packages/database/prisma/schema.prisma`
|
|
|
|
## Design Principles
|
|
|
|
The schema is built around a multi-tenant SaaS model with three distinct concerns:
|
|
|
|
1. company operations for running a rental business
|
|
2. renter-facing booking and review flows
|
|
3. platform-level SaaS billing and administration
|
|
|
|
Important design choices:
|
|
|
|
- `Company` is the tenant root for nearly every business table.
|
|
- operational data and platform billing data are kept separate.
|
|
- reservation-time snapshots are stored explicitly so later changes to policies or prices do not rewrite history.
|
|
- some user concepts intentionally exist twice:
|
|
- `Renter` is a cross-company identity
|
|
- `Customer` is a company-owned CRM record
|
|
- status changes are modeled with enums rather than free-text fields where the workflow matters.
|
|
|
|
## High-Level Domain Map
|
|
|
|
```mermaid
|
|
erDiagram
|
|
Company ||--o| Subscription : has
|
|
Company ||--o| BrandSettings : has
|
|
Company ||--o| ContractSettings : has
|
|
Company ||--o| AccountingSettings : has
|
|
Company ||--o{ Employee : employs
|
|
Company ||--o{ Vehicle : owns
|
|
Company ||--o{ Offer : publishes
|
|
Company ||--o{ Customer : manages
|
|
Company ||--o{ Reservation : receives
|
|
Company ||--o{ RentalPayment : collects
|
|
Company ||--o{ InsurancePolicy : defines
|
|
Company ||--o{ PricingRule : defines
|
|
Company ||--o{ BillingAccount : bills
|
|
Company ||--o{ BillingInvoice : invoices
|
|
Company ||--o{ SubscriptionInvoice : tracks
|
|
Company ||--o{ Notification : receives
|
|
Company ||--o{ Complaint : tracks
|
|
|
|
Vehicle ||--o{ Reservation : booked_in
|
|
Vehicle ||--o{ MaintenanceLog : has
|
|
Vehicle ||--o{ VehicleCalendarBlock : blocked_by
|
|
Offer ||--o{ OfferVehicle : applies_to
|
|
Vehicle ||--o{ OfferVehicle : offered_in
|
|
|
|
Customer ||--o{ Reservation : books
|
|
Renter ||--o{ Reservation : owns
|
|
Reservation ||--o{ RentalPayment : paid_by
|
|
Reservation ||--o{ ReservationInsurance : includes
|
|
Reservation ||--o{ AdditionalDriver : adds
|
|
Reservation ||--o{ DamageInspection : inspected_by
|
|
Reservation ||--o{ DamageReport : reported_by
|
|
Reservation ||--o{ ReservationPhoto : photographed_by
|
|
Reservation ||--o| Review : reviewed_as
|
|
Reservation ||--o{ Complaint : disputed_in
|
|
|
|
InsurancePolicy ||--o{ ReservationInsurance : snapshotted_into
|
|
Renter ||--o{ Review : writes
|
|
Review ||--o{ Complaint : escalates_to
|
|
|
|
AdminUser ||--o{ AdminPermission : has
|
|
AdminUser ||--o{ AuditLog : writes
|
|
```
|
|
|
|
## Core Tenant Root
|
|
|
|
### `Company`
|
|
|
|
`Company` is the anchor row for tenant isolation.
|
|
|
|
Key fields:
|
|
|
|
- identity: `id`, `name`, `slug`, `email`, `phone`
|
|
- public/integration: `apiKey`
|
|
- SaaS state: `status`, `subscriptionPaymentRef`
|
|
- address: `address` JSON
|
|
|
|
Key relations:
|
|
|
|
- 1:1 with `Subscription`
|
|
- 1:1 with `BrandSettings`
|
|
- 1:1 with `ContractSettings`
|
|
- 1:1 with `AccountingSettings`
|
|
- 1:N with `Employee`, `Vehicle`, `Offer`, `Customer`, `Reservation`, `RentalPayment`
|
|
- 1:N with billing tables such as `BillingAccount`, `BillingInvoice`, `SubscriptionInvoice`
|
|
- 1:N with `InsurancePolicy`, `PricingRule`, `Notification`, `Complaint`
|
|
|
|
Important uniqueness:
|
|
|
|
- `slug`
|
|
- `email`
|
|
- `apiKey`
|
|
|
|
## SaaS Subscription and Billing
|
|
|
|
There are two billing layers in the schema:
|
|
|
|
1. legacy provider-oriented subscription records
|
|
2. richer billing-account and billing-invoice records for platform finance operations
|
|
|
|
### `Subscription`
|
|
|
|
One row per company.
|
|
|
|
Purpose:
|
|
|
|
- current plan
|
|
- billing period
|
|
- lifecycle state
|
|
- trial dates
|
|
- renewal windows
|
|
- suspension and cancellation timing
|
|
|
|
Relations:
|
|
|
|
- belongs to one `Company`
|
|
- has many `SubscriptionInvoice`
|
|
- has many `SubscriptionEvent`
|
|
- can be referenced by many `BillingInvoice`
|
|
- can be referenced by many `BillingEvent`
|
|
|
|
### `SubscriptionEvent`
|
|
|
|
Append-only event log for the subscription lifecycle.
|
|
|
|
Examples of what it represents:
|
|
|
|
- trial started
|
|
- checkout created
|
|
- payment received
|
|
- plan changed
|
|
- grace period extended
|
|
- subscription suspended
|
|
|
|
### `SubscriptionInvoice`
|
|
|
|
Legacy or provider-facing subscription payment records.
|
|
|
|
Purpose:
|
|
|
|
- capture AmanPay or PayPal payment references
|
|
- track amount, currency, due date, paid date, and failure/void state
|
|
- optionally bridge to the richer `BillingInvoice` model via `billingInvoiceId`
|
|
|
|
### `PaymentAttempt`
|
|
|
|
Legacy payment-attempt rows tied to `SubscriptionInvoice`.
|
|
|
|
This is narrower than the newer billing payment tables and exists for provider-level retry/failure tracking.
|
|
|
|
### `BillingAccount`
|
|
|
|
The primary finance entity for a company at the platform level.
|
|
|
|
Purpose:
|
|
|
|
- billing identity and address
|
|
- invoice terms
|
|
- default currency
|
|
- tax flags
|
|
- default payment method
|
|
- dunning pause state
|
|
- external provider customer ID
|
|
|
|
Relations:
|
|
|
|
- belongs to one `Company`
|
|
- has many `BillingPaymentMethod`
|
|
- has many `BillingInvoice`
|
|
- has many `BillingPaymentIntent`
|
|
- has many `BillingPaymentAttempt`
|
|
- has many `BillingCreditBalance`, `BillingCreditLedgerEntry`, `BillingCreditNote`, `BillingRefund`, `BillingEvent`
|
|
|
|
### `BillingPaymentMethod`
|
|
|
|
Stored billing method metadata for the company account.
|
|
|
|
Examples:
|
|
|
|
- card brand/last4/expiration
|
|
- ACH/bank/manual invoice style billing methods
|
|
|
|
One billing account can have many payment methods; one may be selected as default.
|
|
|
|
### `BillingInvoice`
|
|
|
|
The main platform invoice table.
|
|
|
|
Purpose:
|
|
|
|
- invoice numbering and sequencing
|
|
- totals and tax math
|
|
- status transitions
|
|
- billing snapshot fields such as billing name and email
|
|
- finance/admin metadata
|
|
|
|
Relations:
|
|
|
|
- belongs to one `BillingAccount`
|
|
- belongs to one `Company`
|
|
- optionally belongs to one `Subscription`
|
|
- optionally has a linked legacy `SubscriptionInvoice`
|
|
- has many `BillingInvoiceLineItem`
|
|
- has many `BillingPaymentIntent`
|
|
- has many `BillingPaymentAttempt`
|
|
- has many `BillingTaxRecord`
|
|
- has many `BillingCreditNote`
|
|
- has many `BillingRefund`
|
|
- has many `BillingEvent`
|
|
|
|
### Supporting billing tables
|
|
|
|
- `BillingInvoiceLineItem`: immutable invoice lines with type, quantity, unit amount, and optional service period.
|
|
- `BillingPaymentIntent`: payment-intent level state for a billing invoice.
|
|
- `BillingPaymentAttempt`: actual collection attempts against an invoice.
|
|
- `BillingCreditBalance`: running credit balance per billing account and currency.
|
|
- `BillingCreditLedgerEntry`: credit ledger movement history.
|
|
- `BillingCreditNote`: credit documents issued against an invoice.
|
|
- `BillingRefund`: refund records for billing invoices.
|
|
- `BillingTaxRecord`: tax breakdown rows for an invoice.
|
|
- `BillingEvent`: append-only finance/subscription/company event log.
|
|
|
|
## Branding and Company Configuration
|
|
|
|
### `BrandSettings`
|
|
|
|
One-to-one with `Company`.
|
|
|
|
Purpose:
|
|
|
|
- public display name, tagline, logo, favicon, hero image
|
|
- theme colors
|
|
- subdomain and custom domain
|
|
- public contact and social metadata
|
|
- default locale and currency
|
|
- company-owned payment provider settings for renter payments
|
|
- carplace visibility and rating
|
|
- homepage/menu JSON configuration
|
|
|
|
Important uniqueness:
|
|
|
|
- `companyId`
|
|
- `subdomain`
|
|
- `customDomain`
|
|
|
|
### `ContractSettings`
|
|
|
|
One-to-one with `Company`.
|
|
|
|
Purpose:
|
|
|
|
- rental contract legal copy
|
|
- invoice/footer text
|
|
- fuel and late-fee policy
|
|
- tax settings
|
|
- numbering prefixes and running sequences
|
|
- additional-driver charging policy
|
|
|
|
This table is used when generating operational rental documents.
|
|
|
|
### `AccountingSettings`
|
|
|
|
One-to-one with `Company`.
|
|
|
|
Purpose:
|
|
|
|
- reporting period
|
|
- fiscal-year start month
|
|
- accounting contact
|
|
- automatic report settings
|
|
- preferred export format
|
|
|
|
### `PricingRule`
|
|
|
|
Company-owned pricing rule definitions applied to reservations.
|
|
|
|
Purpose:
|
|
|
|
- define surcharge or discount logic
|
|
- express the rule type, condition, and adjustment
|
|
|
|
These rules are copied into reservation snapshots through `Reservation.pricingRulesApplied` and `Reservation.pricingRulesTotal`.
|
|
|
|
### `InsurancePolicy`
|
|
|
|
Company-defined upsell or mandatory protection products.
|
|
|
|
Purpose:
|
|
|
|
- define policy name and type
|
|
- define flat, percent, or per-day charges
|
|
- mark required or optional coverage
|
|
|
|
These are also snapshotted into reservations.
|
|
|
|
## Staff, Fleet, and Commercial Catalog
|
|
|
|
### `Employee`
|
|
|
|
Company staff records.
|
|
|
|
Purpose:
|
|
|
|
- operational user identity
|
|
- role assignment
|
|
- preferred language
|
|
- password-reset support
|
|
- activation state
|
|
|
|
Implementation note:
|
|
|
|
- `clerkUserId` still exists as a required unique field in the schema, but Clerk is no longer an active auth dependency in this project.
|
|
- the field is currently populated with local/generated identifiers so existing schema constraints remain satisfied.
|
|
|
|
Relations:
|
|
|
|
- belongs to one `Company`
|
|
- has many `Notification`
|
|
- has many `NotificationPreference`
|
|
|
|
Important uniqueness:
|
|
|
|
- `clerkUserId`
|
|
|
|
### `Vehicle`
|
|
|
|
The core fleet entity.
|
|
|
|
Purpose:
|
|
|
|
- static vehicle information
|
|
- public listing content
|
|
- daily rate and status
|
|
- pickup/dropoff location rules
|
|
|
|
Relations:
|
|
|
|
- belongs to one `Company`
|
|
- has many `Reservation`
|
|
- has many `MaintenanceLog`
|
|
- has many `VehicleCalendarBlock`
|
|
- participates in many-to-many offers through `OfferVehicle`
|
|
|
|
Soft deletion is implemented operationally by setting:
|
|
|
|
- `status = OUT_OF_SERVICE`
|
|
- `isPublished = false`
|
|
|
|
### `MaintenanceLog`
|
|
|
|
One-to-many from `Vehicle`.
|
|
|
|
Purpose:
|
|
|
|
- maintenance type and description
|
|
- cost, mileage, performed date
|
|
- next due date or mileage
|
|
|
|
### `VehicleCalendarBlock`
|
|
|
|
One-to-many from `Vehicle`.
|
|
|
|
Purpose:
|
|
|
|
- manual or maintenance blocks on the reservation calendar
|
|
- date-range unavailability independent of reservations
|
|
|
|
### `Offer`
|
|
|
|
Company-defined promotional offers.
|
|
|
|
Purpose:
|
|
|
|
- percentage/fixed/free-day/special-rate discount logic
|
|
- validity window
|
|
- promo code
|
|
- public and featured visibility
|
|
- redemption caps
|
|
|
|
Relations:
|
|
|
|
- belongs to one `Company`
|
|
- has many `Reservation`
|
|
- many-to-many with `Vehicle` through `OfferVehicle`
|
|
|
|
### `OfferVehicle`
|
|
|
|
Join table between `Offer` and `Vehicle`.
|
|
|
|
Composite primary key:
|
|
|
|
- `(offerId, vehicleId)`
|
|
|
|
## Renter Identity, Company CRM, and Reservation Aggregate
|
|
|
|
The schema intentionally separates global renter identity from company-local customer records.
|
|
|
|
### `Renter`
|
|
|
|
Cross-company end-user identity.
|
|
|
|
Purpose:
|
|
|
|
- login identity
|
|
- app preferences and verification state
|
|
- push token
|
|
- reservation and review linkage across companies
|
|
|
|
Relations:
|
|
|
|
- has many `Reservation`
|
|
- has many `Review`
|
|
- has many `Notification`
|
|
- has many `NotificationPreference`
|
|
- has many saved companies through `RenterSavedCompany`
|
|
|
|
### `RenterSavedCompany`
|
|
|
|
Join table that stores renter bookmarks.
|
|
|
|
Composite primary key:
|
|
|
|
- `(renterId, companyId)`
|
|
|
|
### `Customer`
|
|
|
|
Company-owned CRM record for a person renting from that company.
|
|
|
|
Purpose:
|
|
|
|
- renter contact and profile data as understood by that company
|
|
- notes and flag state
|
|
- driver-license metadata
|
|
- approval and compliance state
|
|
- company-specific reservation history
|
|
|
|
Relations:
|
|
|
|
- belongs to one `Company`
|
|
- has many `Reservation`
|
|
- has many `Complaint`
|
|
|
|
Important uniqueness:
|
|
|
|
- `(companyId, email)`
|
|
|
|
### `Reservation`
|
|
|
|
This is the central transactional aggregate for rental operations.
|
|
|
|
Core relations:
|
|
|
|
- belongs to one `Company`
|
|
- belongs to one `Vehicle`
|
|
- belongs to one `Customer`
|
|
- optionally belongs to one `Renter`
|
|
- optionally belongs to one `Offer`
|
|
|
|
Core business fields:
|
|
|
|
- booking source
|
|
- date range
|
|
- pickup/return locations
|
|
- pricing fields: `dailyRate`, `discountAmount`, `totalDays`, `totalAmount`, `depositAmount`
|
|
- payment summary fields: `paymentStatus`, `paidAmount`
|
|
- lifecycle fields: `status`, `checkedInAt`, `checkedOutAt`
|
|
- cancellation fields
|
|
- contract and invoice numbering
|
|
- review token and review reminder timestamps
|
|
- extras and pricing-rule snapshots
|
|
|
|
The reservation is the parent of most rental workflow records.
|
|
|
|
### Reservation child tables
|
|
|
|
- `RentalPayment`: individual payment rows for the reservation.
|
|
- `ReservationInsurance`: reservation-time snapshots of selected insurance products.
|
|
- `AdditionalDriver`: reservation-time records for extra drivers and approval status.
|
|
- `DamageReport`: pickup/dropoff summary damage records with photos and signatures.
|
|
- `DamageInspection`: structured inspection record per reservation and inspection type.
|
|
- `DamagePoint`: individual annotated damage points tied to one inspection.
|
|
- `ReservationPhoto`: ad hoc pickup/dropoff photo attachments.
|
|
- `Review`: at most one review per reservation.
|
|
- `Complaint`: operational or post-rental dispute record, optionally linked to reservation, review, and customer.
|
|
|
|
This snapshot pattern is important. The reservation stores what was actually sold and reviewed at the time of booking, not just pointers to mutable company configuration.
|
|
|
|
## Reservation-Adjacent Tables in Detail
|
|
|
|
### `RentalPayment`
|
|
|
|
Operational payment rows for rentals, not SaaS billing.
|
|
|
|
Purpose:
|
|
|
|
- amount, currency, type, status
|
|
- AmanPay or PayPal provider references
|
|
- paid timestamp
|
|
|
|
Relations:
|
|
|
|
- belongs to one `Company`
|
|
- belongs to one `Reservation`
|
|
|
|
### `ReservationInsurance`
|
|
|
|
Snapshot bridge between `Reservation` and `InsurancePolicy`.
|
|
|
|
It copies:
|
|
|
|
- policy name
|
|
- policy type
|
|
- charge type
|
|
- charge value
|
|
- total charge
|
|
|
|
That prevents later edits to the base insurance policy from mutating historical reservation pricing.
|
|
|
|
### `AdditionalDriver`
|
|
|
|
Reservation child rows for additional drivers.
|
|
|
|
Purpose:
|
|
|
|
- identity and license details
|
|
- charge model
|
|
- approval requirement and approval outcome
|
|
- expiry flags
|
|
|
|
### `DamageReport`
|
|
|
|
One row per reservation and report type.
|
|
|
|
Composite uniqueness:
|
|
|
|
- `(reservationId, type)`
|
|
|
|
Stores:
|
|
|
|
- summary damages JSON
|
|
- photo URLs
|
|
- fuel level
|
|
- mileage
|
|
- inspection metadata
|
|
|
|
### `DamageInspection`
|
|
|
|
Structured pickup/dropoff inspection entity.
|
|
|
|
Composite uniqueness:
|
|
|
|
- `(reservationId, type)`
|
|
|
|
Stores:
|
|
|
|
- fuel and mileage
|
|
- general condition notes
|
|
- customer agreement state
|
|
- inspector identity
|
|
|
|
### `DamagePoint`
|
|
|
|
Granular diagram point rows tied to `DamageInspection`.
|
|
|
|
Stores:
|
|
|
|
- diagram view
|
|
- x/y coordinates
|
|
- damage type and severity
|
|
- free-text description
|
|
- whether the damage is pre-existing
|
|
|
|
### `ReservationPhoto`
|
|
|
|
Simple photo attachments tied to a reservation with type `PICKUP` or `DROPOFF`.
|
|
|
|
### `Review`
|
|
|
|
One review per reservation.
|
|
|
|
Important uniqueness:
|
|
|
|
- `reservationId`
|
|
|
|
Purpose:
|
|
|
|
- overall, vehicle, and service ratings
|
|
- comment
|
|
- moderation/publication flags
|
|
- company reply data
|
|
- optional feedback category
|
|
|
|
Relations:
|
|
|
|
- optionally linked to `Renter`
|
|
- can have many `Complaint`
|
|
|
|
### `Complaint`
|
|
|
|
Company-scoped issue-tracking table.
|
|
|
|
Can be linked to:
|
|
|
|
- a reservation
|
|
- a review
|
|
- a customer
|
|
|
|
This supports both operational complaints and post-review escalations.
|
|
|
|
## Notifications and Messaging
|
|
|
|
### `Notification`
|
|
|
|
A notification can target one of three audiences:
|
|
|
|
- company-level
|
|
- employee-level
|
|
- renter-level
|
|
|
|
Purpose:
|
|
|
|
- delivery type
|
|
- title/body/data payload
|
|
- channel
|
|
- locale
|
|
- send/read/failure state
|
|
- provider message ID
|
|
|
|
### `NotificationTemplate`
|
|
|
|
Reusable message content keyed by:
|
|
|
|
- template key
|
|
- channel
|
|
- locale
|
|
- version
|
|
|
|
### `NotificationPreference`
|
|
|
|
Per-recipient opt-in/out rows.
|
|
|
|
The uniqueness rules are intentionally split:
|
|
|
|
- unique per `(employeeId, notificationType, channel)`
|
|
- unique per `(renterId, notificationType, channel)`
|
|
|
|
## Admin and Platform Governance
|
|
|
|
### `AdminUser`
|
|
|
|
Platform operator account.
|
|
|
|
Purpose:
|
|
|
|
- role
|
|
- password hash
|
|
- 2FA state
|
|
- login tracking
|
|
- password reset support
|
|
|
|
Relations:
|
|
|
|
- has many `AdminPermission`
|
|
- has many `AuditLog`
|
|
|
|
### `AdminPermission`
|
|
|
|
Resource/action override rows for an admin user.
|
|
|
|
Important uniqueness:
|
|
|
|
- `(adminUserId, resource)`
|
|
|
|
### `AuditLog`
|
|
|
|
Immutable log of admin-side actions.
|
|
|
|
Stores:
|
|
|
|
- actor
|
|
- action and resource
|
|
- optional company and resource IDs
|
|
- before/after JSON
|
|
- IP, user agent, and note
|
|
|
|
## Pricing and Platform Content Configuration
|
|
|
|
### `PricingConfig`
|
|
|
|
Platform-managed plan pricing rows.
|
|
|
|
Important uniqueness:
|
|
|
|
- `(plan, billingPeriod)`
|
|
|
|
### `PlanFeature`
|
|
|
|
Platform-managed feature labels per plan, ordered by `sortOrder`.
|
|
|
|
### `PricingPromotion`
|
|
|
|
Platform-managed promotion codes for SaaS pricing.
|
|
|
|
Purpose:
|
|
|
|
- code and description
|
|
- discount type/value
|
|
- applicable plans/periods
|
|
- usage limits
|
|
- validity window
|
|
|
|
## Relationship Summary by Cardinality
|
|
|
|
### One-to-one
|
|
|
|
- `Company` -> `Subscription`
|
|
- `Company` -> `BrandSettings`
|
|
- `Company` -> `ContractSettings`
|
|
- `Company` -> `AccountingSettings`
|
|
|
|
### One-to-many
|
|
|
|
- `Company` -> `Employee`, `Vehicle`, `Offer`, `Customer`, `Reservation`, `RentalPayment`
|
|
- `Company` -> `BillingAccount`, `BillingInvoice`, `SubscriptionInvoice`, `InsurancePolicy`, `PricingRule`, `Notification`, `Complaint`
|
|
- `Vehicle` -> `Reservation`, `MaintenanceLog`, `VehicleCalendarBlock`
|
|
- `Reservation` -> `RentalPayment`, `ReservationInsurance`, `AdditionalDriver`, `DamageReport`, `DamageInspection`, `ReservationPhoto`, `Complaint`
|
|
- `BillingAccount` -> `BillingPaymentMethod`, `BillingInvoice`, `BillingPaymentIntent`, `BillingPaymentAttempt`, `BillingCreditBalance`, `BillingCreditLedgerEntry`, `BillingCreditNote`, `BillingRefund`, `BillingEvent`
|
|
|
|
### Many-to-many through join tables
|
|
|
|
- `Offer` <-> `Vehicle` through `OfferVehicle`
|
|
- `Renter` <-> `Company` through `RenterSavedCompany`
|
|
|
|
## Indexing and Uniqueness Rules Worth Remembering
|
|
|
|
Operationally important uniqueness constraints:
|
|
|
|
- `Company.slug`
|
|
- `Company.email`
|
|
- `Company.apiKey`
|
|
- `BrandSettings.subdomain`
|
|
- `BrandSettings.customDomain`
|
|
- `Employee.clerkUserId`
|
|
- `Customer(companyId, email)`
|
|
- `Reservation.contractNumber`
|
|
- `Reservation.invoiceNumber`
|
|
- `Reservation.reviewToken`
|
|
- `Review.reservationId`
|
|
- `OfferVehicle(offerId, vehicleId)`
|
|
- `RenterSavedCompany(renterId, companyId)`
|
|
- `ReservationInsurance(reservationId, insurancePolicyId)`
|
|
- `DamageReport(reservationId, type)`
|
|
- `DamageInspection(reservationId, type)`
|
|
- `PricingConfig(plan, billingPeriod)`
|
|
- `NotificationPreference` employee and renter uniqueness pairs
|
|
|
|
## Practical Reading Guide
|
|
|
|
If you need to understand the schema quickly, read it in this order:
|
|
|
|
1. `Company`
|
|
2. `Subscription`, `BillingAccount`, `BillingInvoice`
|
|
3. `Employee`, `Vehicle`, `Offer`
|
|
4. `Customer`, `Renter`
|
|
5. `Reservation` and its child tables
|
|
6. `Notification*`
|
|
7. `Admin*`, `AuditLog`, `Pricing*`
|
|
|
|
That order matches how the application is structured in the API: tenant first, operations second, finance/admin last.
|