# Hub Bent Data Model

## Goal

Build a booking and fleet database that is:

- operationally correct for real rental work;
- flexible enough to grow without schema rewrites;
- simple enough to explain to operators and support.

This model is intentionally pragmatic. It is not a generic ERP and it is not a copy of the legacy DB.

## Core Principles

### 1. Sell by group, assign by vehicle

Public search and most bookings should reserve a `vehicle_group`, not a specific `vehicle`.

Why:

- one group can contain multiple real cars;
- a real car can go to service or be blocked unexpectedly;
- operators need reassignment freedom;
- contract and handover still need an exact car later.

Business flow:

1. website or operator books a `vehicle_group`
2. availability checks if at least one real `vehicle` can cover the slot
3. booking stays group-based until dispatch
4. operator assigns a concrete `vehicle`
5. contract is always tied to the assigned real car

### 2. Separate commercial catalog from real fleet

There are three different layers:

- `vehicle_model` = descriptive model
- `vehicle_group` = sellable group on site and in pricing
- `vehicle` = real asset with VIN, plate, mileage, provider, insurance

This is the main anti-chaos rule in the whole system.

### 3. Pricing is layered, not magical

Do not build a huge abstract rule engine in v1.

Pricing should be explicit and readable:

1. base daily rate by `vehicle_group` and `rate_band`
2. optional seasonal adjustment
3. pickup and dropoff fees
4. one-way fee
5. out-of-hours fee
6. extras
7. promo / discount
8. deposit and prepayment

### 4. Every booking stores a pricing snapshot

Rules can change later. Existing bookings must not recalculate silently.

That means:

- live pricing tables are used only to calculate a new quote
- accepted booking stores snapshot lines and totals inside `bookings.price_snapshot`

## Main Domains

### Identity

Tables:

- `users`

V1 role model:

- `admin`
- `operator`
- `manager`
- `accountant`

Start simple. Fine-grained permission matrices can come later.

### Locations

Tables:

- `locations`

Purpose:

- pickup / dropoff points
- airport handling
- office network
- service center
- delivery zone

Important fields:

- `name`
- `code`
- `type`
- `is_airport`
- `is_active`

### Providers

Tables:

- `providers`

Purpose:

- internal fleet
- partner fleet
- leased fleet source

Important fields:

- `name`
- `code`
- `type`
- contact fields
- `is_active`

### Catalog

Tables:

- `vehicle_groups`
- `vehicle_models`
- `vehicle_group_model`

`vehicle_groups` is the commercial layer.

Example:

- `Luxury Sedan`
- `Electric Sedan`
- `Compact SUV`

Important fields:

- `name`
- `slug`
- `category`
- `transmission`
- `fuel_type`
- `drive_type`
- `seats`
- `doors`
- `luggage_count`
- `is_premium`
- `sort_order`

`vehicle_models` is descriptive catalog content.

Example:

- `Mercedes-Benz S-Class`
- `Tesla Model 3`
- `Toyota Camry`

### Real Fleet

Tables:

- `vehicles`
- `vehicle_maintenance`
- `vehicle_documents`

Important `vehicles` fields:

- `internal_code`
- `provider_id`
- `vehicle_group_id`
- `vehicle_model_id`
- `current_location_id`
- `plate`
- `vin`
- `year`
- `status`
- `powertrain`
- `transmission`
- `color`
- `mileage`
- `energy_level`
- `condition_summary`
- `next_action_note`
- `image_url`
- insurance fields
- purchase date

Recommended statuses:

- `available`
- `reserved`
- `assigned`
- `on_rent`
- `prep`
- `charging`
- `maintenance`
- `blocked`

### Customers

Tables:

- `customers`

Important fields:

- first / last / full name
- email
- phone
- initials
- avatar color
- birth date
- nationality
- locale
- license number / country
- passport number
- notes

V1 intentionally keeps customer structure lean. Extra KYC and document tables can be added later.

### Bookings

Tables:

- `bookings`
- `booking_vehicle_assignments`
- `booking_extras`
- `booking_documents`
- `booking_activities`

Important `bookings` fields:

- `reference`
- `source`
- `status`
- `payment_status`
- `service_type`
- `vehicle_group_id`
- `customer_id`
- `pickup_location_id`
- `dropoff_location_id`
- `pickup_at`
- `dropoff_at`
- `flight_number`
- `comment`
- `currency`
- `base_total`
- `fees_total`
- `extras_total`
- `discount_total`
- `final_total`
- `prepayment_amount`
- `deposit_amount`
- `price_snapshot`

Recommended booking statuses:

- `pending`
- `upcoming`
- `active`
- `completed`
- `cancelled`

Recommended payment statuses:

- `unpaid`
- `partial`
- `paid`
- `refunded`

### Pricing

Tables:

- `rate_bands`
- `vehicle_group_rates`
- `season_rules`
- `location_fees`
- `extras`
- `promo_codes`

#### `rate_bands`

Dynamic in data, even if UI shows presets.

Default presets:

- `1-3`
- `4-6`
- `7-14`
- `15-29`
- `30+`

#### `vehicle_group_rates`

One row per:

- `vehicle_group`
- `rate_band`
- optional channel / date range

Stores:

- `daily_rate`
- `deposit_amount`
- `prepayment_type`
- `prepayment_value`

#### `season_rules`

Supports:

- increase / decrease
- percentage / fixed
- date range

#### `location_fees`

Supports:

- pickup fee
- dropoff fee
- one-way fee
- out-of-hours fee

#### `extras`

Supports:

- `per_day`
- `per_rental`

#### `promo_codes`

Supports:

- percentage discount
- fixed discount
- date range
- minimum days
- minimum total
- usage limit later if needed

## Price Calculation Order

Current recommended order:

1. detect rental duration in days
2. find matching `rate_band`
3. find active `vehicle_group_rate`
4. calculate rental base
5. apply season adjustment
6. apply pickup / dropoff / one-way / out-of-hours fees
7. apply extras
8. apply promo discount
9. calculate final total
10. calculate deposit and prepayment
11. save snapshot lines into booking

Important rule:

- frontend must not own pricing logic
- frontend sends inputs
- backend returns quote
- backend creates booking from the same pricing service

## Availability Rule

Availability is not “is this model free”.

Correct rule:

- search on `vehicle_group`
- count real `vehicles` inside the group
- exclude cars blocked by status
- exclude cars already assigned to overlapping bookings
- if at least one valid car remains, the group is sellable

This is why booking must point to `vehicle_group_id`, not `vehicle_id`.

## Assignment Rule

Vehicle assignment is a separate step.

Why:

- car can change before pickup
- operator may need to replace with similar or better car
- contract generation should use latest assigned vehicle

Table:

- `booking_vehicle_assignments`

This table also gives us reassignment history later.

## What Is Intentionally Not In V1

Not now:

- huge polymorphic notes system
- generic rules engine for every fee on earth
- deep RBAC matrix
- accounting ledger
- multi-contract engine
- telematics ingestion pipeline
- dynamic partner settlements

These can be added later without breaking the current shape.

## Current Local Implementation

Already implemented in the local Laravel backend:

- core fleet / booking / pricing tables
- lookup API
- overview API
- bookings list + details
- fleet list + details
- pricing overview API
- backend quote calculation for booking creation
- booking create endpoint
- vehicle create endpoint

This is enough to start building the real frontend around stable domain primitives instead of legacy screens.
