# Car Rental Management System — Implementation Plan

> Status: **DRAFT for review** · Date: 2026-06-26
> Baseline: Spring Boot 3.3.4 · Java 17 · Thymeleaf · PostgreSQL · Spring Security (admin + customer chains, Google OAuth2)
> Purpose: turn the current customer-booking + admin-CRUD app into the full multi-party platform described in the feature spec.

---

## 1. Scope summary

The current app covers ~25–30% of the spec (customer-facing booking + basic admin car/booking CRUD).
This plan adds the four missing pillars — **Driver, Vendor, Billing/Settlement, Reporting** — plus the
**Organization/config** foundation and enrichment of Fleet, Customer, and Booking.

Current coverage vs target:

| Area | Now | Target |
|---|---|---|
| Organization / Config | ~20% | Org profile, staff users + permissions, tax/currency/commission config, category masters |
| Fleet & Car | ~45% | + year, colour, ownership, feature tags, hourly/monthly rates, documents+expiry, maintenance |
| Driver | 0% | full module |
| Vendor | 0% | full module + vendor login + settlement |
| Customer | ~50% | + categories (Individual/Company/Corporate), GST/billing |
| Trip Booking | ~40% | + with-driver, hours/months, tax & driver charges, invoice |
| Billing/Settlement | ~5% | invoices, commission, settlements, profit split |
| Reporting | 0% | filtered reports + Excel/PDF export, dashboards |
| Customer page | ~55% | richer filters + driver option |

---

## 2. Target architecture

- **Single Spring Boot modular-monolith** (no microservices). Package-by-feature:
  `org`, `fleet`, `driver`, `vendor`, `customer`, `booking`, `billing`, `reporting`, `web`, `security`, `config`.
- **Three security filter chains** (extends today's pattern):
  1. `/admin/**` → staff (ROLE_ADMIN, ROLE_STAFF) — existing admin chain, expanded
  2. `/vendor/**` → ROLE_VENDOR — **new** vendor portal chain
  3. everything else → customers + Google OAuth (existing customer chain)
- **Persistence**: PostgreSQL + JPA. Move `ddl-auto=update` → **Flyway migrations** (versioned schema; required once money/settlement tables exist).
- **Money**: always `BigDecimal`, scale 2, currency from config. Never `double`.
- **Exports**: Apache POI (Excel), OpenPDF or Flying Saucer (HTML→PDF) for invoices/statements/reports.
- **Server-side rendering** stays Thymeleaf; add small JS for dynamic price calc on the customer page.

---

## 3. Role & permission model

Replace the 2-value `Role` enum with a richer model:

```
Role (enum):  ADMIN, STAFF, VENDOR, CUSTOMER
Permission (enum): MANAGE_FLEET, MANAGE_DRIVERS, MANAGE_VENDORS, MANAGE_BOOKINGS,
                   MANAGE_BILLING, VIEW_REPORTS, MANAGE_CONFIG, MANAGE_USERS
```

- `ADMIN` = all permissions. `STAFF` = a configurable subset (stored per-user).
- `VENDOR` = scoped to own data only (own cars, own trips, own settlements).
- Authorization: method-level `@PreAuthorize("hasAuthority('MANAGE_FLEET')")` on services/controllers.

---

## 4. Domain model (entities & key fields)

New entities in **bold**; modified existing in _italics_.

### 4.1 Organization / Config
- **Organization** — name, legalName, logoUrl, address, gstNumber, contactEmail, phone, currencyCode
- **SystemConfig** (singleton) — currencyCode, defaultTaxPercent, defaultCommissionPercent,
  defaultDriverDailyCharge, invoicePrefix, settlementCycle (WEEKLY/MONTHLY)
- **Category** (generic master) — `type` (VEHICLE / DRIVER / CUSTOMER), name, description, active
  → replaces hard-coded enums so categories are admin-configurable
- _User_ — add `permissions` (set), `vendor` (nullable FK for vendor-portal users)

### 4.2 Fleet
- _Car_ — add: `yearOfRegistration`, `colour`, `ownership` (OWNED / VENDOR), `vendor` (nullable FK),
  `hourlyRate`, `dailyRate` (rename pricePerDay), `monthlyRate`, `unitsTotal`,
  `featureTags` (AC, GPS, … as a set/element-collection), `status` (ACTIVE / MAINTENANCE / RETIRED)
- **CarDocument** — car FK, docType (RC / INSURANCE / PERMIT / POLLUTION), fileUrl, issuedDate, expiryDate
- **CarPhoto** — car FK, url, sortOrder (gallery instead of single imageUrl)

### 4.3 Driver
- **Driver** — name, phone, licenceNumber, licenceExpiry, yearsExperience, category (FK Category),
  employmentType (EMPLOYED / VENDOR), vendor (nullable FK), hourlyCharge, dailyCharge, monthlyCharge,
  status (AVAILABLE / ON_TRIP / OFF), photoUrl
- **DriverDocument** — driver FK, docType, fileUrl, expiryDate
- Trip history derived from `Booking` where `driver = X`

### 4.4 Vendor
- **Vendor** — name, contactPerson, phone, email, address, gstNumber,
  bankAccount/settlementDetails, commissionPercent (nullable → overrides global), active
- Vendor login = a `User` with ROLE_VENDOR linked to the Vendor
- **VendorSettlement** — vendor FK, periodStart, periodEnd, grossRevenue, commissionAmount,
  driverCharges, netPayable, status (DRAFT / FINALISED / PAID), generatedAt

### 4.5 Customer
- _User_ (customer) — add: `customerCategory` (INDIVIDUAL / COMPANY / CORPORATE),
  `companyName`, `gstNumber`, `billingAddress`, `contactPersons` (for company/corporate)

### 4.6 Booking & Billing
- _Booking_ — add: `driver` (nullable FK), `withDriver` (bool), `rentalUnit` (HOUR / DAY / MONTH),
  `quantity` (units of the period), `vehicleCharge`, `driverCharge`, `taxPercent`, `taxAmount`,
  `commissionPercent`, `commissionAmount`, `revenueOwner` (OWNED / VENDOR), `vendor` (nullable FK)
- **Invoice** — booking FK, invoiceNumber, issuedAt, lineItems, subtotal, tax, total, pdfUrl
- **CommissionRule** — scope (GLOBAL / VENDOR / VEHICLE), refId, percent
  (resolution order: vehicle → vendor → global)

### 4.7 ER overview (relationships)
```
Organization 1─* User
User *─1 Vendor (vendor-portal users)            Vendor 1─* Car
Vendor 1─* Driver                                Car 1─* CarDocument / CarPhoto
Category 1─* Car / Driver / User(customer)       Driver 1─* DriverDocument
Customer(User) 1─* Booking                       Car 1─* Booking
Driver 1─* Booking (optional)                    Booking 1─1 Invoice
Vendor 1─* Booking (revenue attribution)         Vendor 1─* VendorSettlement
```

---

## 5. Calculation rules (single source of truth)

```
periodCount   = ceil(duration in chosen unit)            // HOUR / DAY / MONTH
vehicleCharge = rate(unit) × periodCount × units
driverCharge  = withDriver ? driverRate(unit) × periodCount : 0
subtotal      = vehicleCharge + driverCharge
taxAmount     = subtotal × taxPercent/100
total         = subtotal + taxAmount

// Vendor settlement (per vendor trip)
commissionPercent = resolve(vehicle → vendor → global)
commissionAmount  = vehicleCharge × commissionPercent/100
netVendorPayable  = vehicleCharge − commissionAmount + (vendorDriverCharge − orgDriverMargin)

// Owned vs vendor profit split tracked via Booking.revenueOwner
```
All money `BigDecimal`, `RoundingMode.HALF_UP`, scale 2, currency from `SystemConfig`.

---

## 6. Screen inventory

**Admin / Staff (`/admin`)**
- Dashboard (KPIs + owned-vs-vendor split), Fleet (list/form + docs/photos/maintenance),
  Drivers (list/form/profile + trip history), Vendors (list/form + settlements),
  Customers (list/detail), Bookings (list/detail/assign-driver/invoice),
  Billing & Settlements, Reports (filter + export), Settings (org, config, categories, staff & permissions)

**Vendor portal (`/vendor`)** — new
- Login, Dashboard (own trips/revenue), My Cars, My Drivers, Trips (period filter),
  Settlements (view + export PDF/Excel)

**Customer page (public)**
- Home, Car listing **with filters** (category, date, seating, transmission, price, driver option) + live price,
  Car detail, Booking (with/without driver + category), Register/Login, My Bookings + invoices

---

## 7. Phased delivery plan

Each phase is independently shippable and ends with a working app + tests.

### Phase 0 — Foundations (enablers)
- Add Flyway; convert current schema to `V1__baseline.sql`; turn off `ddl-auto`
- Expand `Role` + add `Permission`; method security
- `Organization` + `SystemConfig` + `Category` master; Settings screens
- Replace hard-coded category enums with `Category` lookups (keep enums as seed data)

### Phase 1 — Fleet enrichment
- Car: year, colour, ownership, feature tags, hourly/daily/monthly rates, units, status(maintenance)
- CarDocument + CarPhoto with expiry tracking + expiry warnings on dashboard
- Maintenance blocking in availability logic

### Phase 2 — Driver module
- Driver entity + documents + categories + charges + availability
- Driver list/form/profile + trip history; driver-availability check

### Phase 3 — Vendor module
- Vendor onboarding + ROLE_VENDOR login + vendor filter chain (`/vendor/**`)
- Vendor dashboard (own cars/drivers/trips); commission config (global/vendor/vehicle)

### Phase 4 — Booking upgrades
- With/without driver + driver category assignment
- Hourly/daily/monthly periods; tax + driver-charge calc; revenue attribution
- Driver double-booking prevention; invoice generation (PDF)

### Phase 5 — Billing & settlement
- Invoices/receipts for customers
- Vendor settlement aggregation per cycle + statement (Excel + PDF) + profit split

### Phase 6 — Reporting
- Filtered reports (date, vehicle, category, driver, vendor, customer category, ownership, status)
- Excel + PDF export; summary dashboard (owned vs vendor); vendor-wise & driver-wise reports

### Phase 7 — Customer page polish
- Full filter set + live cost-by-duration; with-driver booking flow; responsive QA

---

## 8. Dependencies to add
- `flyway-core` + `flyway-database-postgresql`
- `org.apache.poi:poi-ooxml` (Excel)
- `com.github.librepdf:openpdf` **or** `org.xhtmlrenderer:flying-saucer-pdf` (PDF from Thymeleaf)
- (already present: web, security, oauth2-client, data-jpa, validation, thymeleaf, postgresql, lombok)

---

## 9. Cross-cutting concerns
- **Migrations**: every schema change via Flyway; never edit a shipped migration.
- **Validation**: bean validation + show `th:errors` on all admin/vendor forms (current admin forms lack this).
- **Auditing**: `createdAt/updatedAt/createdBy` via JPA auditing on money-bearing entities.
- **Performance**: fix N+1 in booking/report queries with fetch-joins; add pagination to all admin tables.
- **Files**: documents/photos need storage — decide local disk vs S3-compatible (config-driven path).
- **Testing**: service-layer unit tests for all calc rules; `@WebMvcTest` for access control per chain.
- **Seed data**: keep `DataInitializer` for org/config/admin + demo vendor/driver/cars.

---

## 10. Decisions (LOCKED — 2026-06-26, recommended defaults accepted)
1. **File storage** → **local filesystem** (config-driven path) for v1.
2. **PDF library** → **Flying Saucer** (Thymeleaf HTML/CSS → PDF).
3. **Tenancy** → **single organization** per install.
4. **Staff permissions** → **per-user permission checkboxes** (granular `Permission` set).
5. **Settlement cycle** → **monthly** default.
6. **Bookings** → **day/month** for v1 (hourly *rate fields* stored, hourly *booking flow* deferred).

### Deviation from §7
- **Flyway is deferred** to a later hardening phase. During active development the app keeps
  `spring.jpa.hibernate.ddl-auto=update`, which additively creates the new tables/columns without
  disrupting the running PostgreSQL database. Flyway baseline will be introduced before production.

> Plan locked. Implementation started with Phase 0 — Foundations.
