# Plan: Fix and Simplify Car Booking Without Adding Customer Accounts

## Objective

Improve the car booking process while keeping the current guest booking model.

This plan does **not** add:

- Customer accounts
- Customer login
- Renter dashboard
- Global customer profile
- Cross-company customer sharing
- Saved customer identity

The goal is to make booking smoother, safer, and simpler while keeping each company’s customer data private.

---

## Current Direction

The system should continue using this structure:

```txt
Company
  └── Customer
        └── Reservation
```

A customer who books with Company A exists as a customer under Company A.

If the same person books with Company B, they become a separate customer under Company B.

This is the correct approach for now.

---

## Core Privacy Rule

Customer data must always belong to one company.

The system must never allow Company A to view, edit, search, or infer customer data from Company B.

Correct model:

```txt
Company A
  └── john@example.com

Company B
  └── john@example.com
```

These are two separate customer records.

Do **not** create a shared global customer record.

---

## Phase 1: Keep Guest Booking Only

### Decision

Do not build customer accounts now.

Booking should work without login.

The customer only submits the information needed to request a booking.

### Required Behavior

When a customer books a car:

1. Customer selects a car.
2. Customer enters basic booking and contact information.
3. Backend finds the car.
4. Backend derives the company from the car.
5. Backend finds or creates a customer under that company.
6. Backend creates a reservation under that company.

The customer should not need to create an account to make a booking.

---

## Phase 2: Simplify the Booking Form

### Problem

The current booking process asks for too much information too early.

That creates friction and makes the booking feel heavier than it needs to be.

### Fix

The first booking form should only ask for:

- Name
- Email
- Phone
- Pickup date
- Return date
- Pickup location
- Return location
- Optional message

### Move These Fields Out of the First Step

Do not require these during the initial booking request:

- Driver license
- License expiration date
- Government ID
- Passport
- Full address
- Document upload
- Payment details

These can be requested later during verification or approval.

### New Booking Flow

```txt
Step 1: Request Booking
  - Customer contact info
  - Trip dates
  - Pickup and return locations
  - Notes

Step 2: Company Reviews Request
  - Company checks availability
  - Company accepts, rejects, or asks for more details

Step 3: Verification If Needed
  - License
  - Address
  - Documents
  - Payment
```

This keeps the first action lightweight.

---

## Phase 3: Derive Company Ownership From the Car

### Problem

The frontend should not decide which company owns a reservation.

If the frontend sends `companyId`, it can be wrong, manipulated, or stale.

### Fix

The backend must derive `companyId` from the selected car.

Correct backend logic:

```ts
const car = await prisma.car.findUnique({
  where: { id: input.carId },
  select: {
    id: true,
    companyId: true,
  },
});

if (!car) {
  throw new Error("Car not found");
}

const companyId = car.companyId;
```

Then use that `companyId` for both the customer and reservation.

The frontend may send `carId`, but the backend decides the company.

---

## Phase 4: Find or Create Customer by Company and Email

### Required Rule

Customers should be unique inside each company, not globally.

Use:

```prisma
@@unique([companyId, email])
```

This allows:

```txt
john@example.com under Company A
john@example.com under Company B
```

without mixing their records.

### Booking Logic

```ts
const normalizedEmail = input.email.toLowerCase().trim();

const customer = await prisma.customer.upsert({
  where: {
    companyId_email: {
      companyId,
      email: normalizedEmail,
    },
  },
  update: {
    name: input.name,
    phone: input.phone,
  },
  create: {
    companyId,
    email: normalizedEmail,
    name: input.name,
    phone: input.phone,
  },
});
```

### Important

Do not use email alone to find a customer.

Bad:

```ts
where: {
  email: input.email
}
```

Good:

```ts
where: {
  companyId_email: {
    companyId,
    email: normalizedEmail
  }
}
```

---

## Phase 5: Create Reservation With Company Scope

Every reservation must be linked to:

- `companyId`
- `customerId`
- `carId`

Recommended reservation creation:

```ts
const reservation = await prisma.reservation.create({
  data: {
    companyId,
    customerId: customer.id,
    carId: car.id,
    startDate: input.startDate,
    endDate: input.endDate,
    pickupLocation: input.pickupLocation,
    returnLocation: input.returnLocation,
    notes: input.notes,
    status: "PENDING",
  },
});
```

Initial reservation status should be:

```txt
PENDING
```

The company can later change it to:

```txt
APPROVED
REJECTED
CANCELLED
COMPLETED
```

---

## Phase 6: Enforce Company Privacy in Dashboard Queries

### Problem

If company dashboard routes fetch customer or reservation records only by ID, one company may accidentally access another company’s data.

That is a serious multi-tenant privacy bug.

### Fix

Every company-facing query must include `companyId`.

Bad:

```ts
await prisma.customer.findUnique({
  where: { id: customerId },
});
```

Good:

```ts
await prisma.customer.findFirst({
  where: {
    id: customerId,
    companyId: currentCompany.id,
  },
});
```

Bad:

```ts
await prisma.reservation.findUnique({
  where: { id: reservationId },
});
```

Good:

```ts
await prisma.reservation.findFirst({
  where: {
    id: reservationId,
    companyId: currentCompany.id,
  },
});
```

### Apply This Rule To

- Customers
- Reservations
- Cars
- Documents
- Notes
- Payments
- Messages
- Invoices
- Reviews

Anything owned by a company must be queried with company scope.

---

## Phase 7: Prevent Cross-Company Search

Company staff should never be able to search all customers globally.

Company customer search must always filter by the company.

Correct:

```ts
await prisma.customer.findMany({
  where: {
    companyId: currentCompany.id,
    OR: [
      { name: { contains: search, mode: "insensitive" } },
      { email: { contains: search, mode: "insensitive" } },
      { phone: { contains: search, mode: "insensitive" } },
    ],
  },
});
```

Incorrect:

```ts
await prisma.customer.findMany({
  where: {
    email: { contains: search },
  },
});
```

The second version searches across companies and should not exist.

---

## Phase 8: Improve Booking Status Feedback

After submission, show a clear confirmation page.

The customer should see:

- Booking reference
- Car name
- Pickup date
- Return date
- Status: Pending
- Company name
- Message: The company will review your request.

Do not leave the customer wondering whether the booking worked.

### Optional Confirmation Email

Send a confirmation email with:

- Booking reference
- Car details
- Dates
- Company name
- Status
- Support or contact information

No customer account required.

---

## Phase 9: Add Booking Reference

Add a public-safe booking reference to reservations.

Example:

```txt
BK-2026-8F3K2
```

This is useful for emails, support, and later booking lookup.

Recommended field:

```prisma
bookingReference String @unique
```

The reference should not expose database IDs.

Bad reference:

```txt
cm8x9customeridreservationid
```

Good reference:

```txt
BK-7H92KQ
```

---

## Phase 10: Optional Booking Lookup Without Account

If needed later, allow customers to check booking status without creating an account.

Use:

```txt
booking reference + email
```

Example:

```txt
Booking Reference: BK-7H92KQ
Email: john@example.com
```

The backend should only return limited information:

- Status
- Car name
- Company name
- Pickup date
- Return date
- Pickup location
- Return location

Do not expose:

- Internal notes
- Admin comments
- Risk flags
- Documents
- Payment metadata
- Other bookings
- Other company records

This gives convenience without building full accounts.

---

## Phase 11: Add Validation

Validate booking input on both frontend and backend.

### Required Validations

- Email must be valid.
- Phone must be valid enough for contact.
- Start date must be before end date.
- Start date cannot be in the past.
- Car must exist.
- Car must be active/listed.
- Car must belong to an active company.

### Date Validation

Reject:

- Return date before pickup date.
- Same-day invalid ranges if not allowed.
- Past pickup dates.

### Availability Validation

Before creating a reservation, check whether the car already has an overlapping approved or pending reservation.

Example logic:

```ts
const overlappingReservation = await prisma.reservation.findFirst({
  where: {
    carId: car.id,
    status: {
      in: ["PENDING", "APPROVED"],
    },
    startDate: {
      lt: input.endDate,
    },
    endDate: {
      gt: input.startDate,
    },
  },
});

if (overlappingReservation) {
  throw new Error("Car is not available for the selected dates");
}
```

This avoids double booking.

---

## Phase 12: Add Tests

Add tests for the booking flow and company privacy.

### Test 1: Booking creates company-scoped customer

Given:

```txt
Company A
Car A belongs to Company A
Customer books Car A
```

Expected:

```txt
Customer is created under Company A
Reservation is created under Company A
```

### Test 2: Same email can book with two companies

Given:

```txt
john@example.com books with Company A
john@example.com books with Company B
```

Expected:

```txt
Two customer records exist
One under Company A
One under Company B
```

### Test 3: Company cannot view another company’s customer

Given:

```txt
Customer belongs to Company B
Company A user tries to fetch that customer
```

Expected:

```txt
404 or 403
```

### Test 4: Company cannot view another company’s reservation

Given:

```txt
Reservation belongs to Company B
Company A user tries to fetch it
```

Expected:

```txt
404 or 403
```

### Test 5: Backend ignores frontend companyId

Given:

```txt
Frontend sends carId for Company A
Frontend also sends companyId for Company B
```

Expected:

```txt
Reservation is created under Company A
```

The backend must trust the car ownership, not the frontend payload.

### Test 6: Double booking is blocked

Given:

```txt
Car has an approved reservation from June 10 to June 15
Customer tries to book June 12 to June 14
```

Expected:

```txt
Booking is rejected
```

---

## Phase 13: Implementation Order

1. Audit current booking form.
2. Classify fields as required now, optional now, move to verification, or remove.
3. Simplify the frontend booking form.
4. Update booking API to accept `carId` and derive `companyId` from the car.
5. Normalize email before lookup or storage.
6. Find or create customer using `companyId + email`.
7. Create reservation with `companyId + customerId + carId`.
8. Set initial reservation status to `PENDING`.
9. Audit company dashboard routes.
10. Add `companyId` filtering to every company-owned query.
11. Prevent cross-company customer and reservation search.
12. Add reservation availability check.
13. Add booking confirmation page.
14. Add confirmation email if email infrastructure exists.
15. Add booking reference if not already present.
16. Add tests for booking creation, company isolation, and double-booking prevention.
17. Deploy carefully.
18. Run migration only if schema changes are needed.

---

## What Not To Change Now

Do not add:

- Renter model usage
- Customer login
- Customer password
- Customer dashboard
- Shared profile
- Cross-company booking history
- Saved documents
- Saved payment methods

Do not change customer uniqueness to global email.

Do not let companies search customers outside their own company.

Do not trust `companyId` from the frontend.

---

## Acceptance Criteria

This work is complete when:

- A customer can book without creating an account.
- The booking form is shorter and easier to complete.
- Customers are created per company using `companyId + email`.
- The same email can exist under multiple companies.
- Reservations are always tied to the correct company.
- The backend derives `companyId` from `carId`.
- Company dashboards cannot access another company’s customers.
- Company dashboards cannot access another company’s reservations.
- Overlapping bookings are blocked.
- Booking confirmation is clear.
- Sensitive information is not collected too early.

---

## Final Recommendation

Keep the system guest-based for now.

Fix the booking process by simplifying the form, enforcing company-scoped data access, deriving company ownership from the car, and preventing double bookings.

This gives the product a cleaner booking process without adding customer accounts before they are actually needed.
