update documentation
This commit is contained in:
+402
@@ -0,0 +1,402 @@
|
||||
## Docker Environments
|
||||
|
||||
Three Docker environments are available:
|
||||
|
||||
- `Dockerfile.dev` with `docker-compose.dev.yml`
|
||||
- `Dockerfile.test` with `docker-compose.test.yml`
|
||||
- `Dockerfile.production` with `docker-compose.production.yml`
|
||||
- `docker-compose.pgmanage.yml` for a standalone pgManage container
|
||||
- `docker-compose.portainer.production.yml` for a standalone production Portainer deployment behind Traefik
|
||||
|
||||
### Development
|
||||
|
||||
Use the full dev stack for local work with hot reload and bundled Postgres and Redis:
|
||||
|
||||
```bash
|
||||
docker compose -f docker-compose.dev.yml --profile full up --build
|
||||
```
|
||||
|
||||
Services:
|
||||
|
||||
- marketplace: `http://localhost:3000`
|
||||
- dashboard: `http://localhost:3000/dashboard`
|
||||
- admin: `http://localhost:3000/admin`
|
||||
- public-site: `http://localhost:3003`
|
||||
- api: `http://localhost:4000`
|
||||
- pgAdmin: `http://localhost:5050`
|
||||
|
||||
Each dev app now runs in its own container and can be started independently with a profile tag:
|
||||
|
||||
```bash
|
||||
docker compose -f docker-compose.dev.yml --profile api up --build
|
||||
docker compose -f docker-compose.dev.yml --profile marketplace up --build
|
||||
docker compose -f docker-compose.dev.yml --profile dashboard up --build
|
||||
docker compose -f docker-compose.dev.yml --profile admin up --build
|
||||
docker compose -f docker-compose.dev.yml --profile public-site up --build
|
||||
docker compose -f docker-compose.dev.yml --profile tools up --build
|
||||
docker compose -f docker-compose.dev.yml --profile api --profile marketplace --profile dashboard --profile admin up --build
|
||||
```
|
||||
|
||||
Notes:
|
||||
|
||||
- `api` starts `postgres`, `redis`, and `migrate` automatically through dependencies.
|
||||
- frontend profiles also start `api` and its dependencies automatically.
|
||||
- `tools` starts only `pgadmin` plus its required `postgres` dependency.
|
||||
|
||||
On startup, Docker now waits for Postgres to become healthy, runs a one-shot `migrate` service, and only then starts the selected app container. For development, that bootstrap runs `db:generate` every time, but `db:deploy` and `db:seed` only the first time for a persisted dev database, so your local data survives rebuilds and normal restarts.
|
||||
|
||||
Uploaded assets handled by the API, including vehicle photos and company branding images, are stored in the named Docker volume `api_uploads_dev` at `/var/lib/rentaldrivego/storage` inside the container. They stay available across normal container rebuilds and restarts.
|
||||
|
||||
Default dev platform administrator:
|
||||
|
||||
- email: `admin@rentaldrivego.com`
|
||||
- password: `changeme123`
|
||||
|
||||
If you intentionally want a fresh dev bootstrap:
|
||||
|
||||
```bash
|
||||
docker compose -f docker-compose.dev.yml down -v
|
||||
```
|
||||
|
||||
If you want to keep the database and only apply new schema changes manually:
|
||||
|
||||
```bash
|
||||
docker compose -f docker-compose.dev.yml run --rm migrate sh -c "npm run db:deploy"
|
||||
```
|
||||
|
||||
pgAdmin dev login:
|
||||
|
||||
- email: `admin@rentaldrivego.local`
|
||||
- email: `admin@rentaldrivego.dev`
|
||||
- password: `admin`
|
||||
|
||||
pgAdmin opens with the dev Postgres server pre-registered as `RentalDriveGo Dev DB`.
|
||||
|
||||
pgAdmin Postgres connection:
|
||||
|
||||
- host: `postgres`
|
||||
- port: `5432`
|
||||
- database: `rentaldrivego`
|
||||
- username: `postgres`
|
||||
- password: `password`
|
||||
|
||||
### Standalone pgManage
|
||||
|
||||
If you want a standalone Postgres management UI without starting the full development stack:
|
||||
|
||||
```bash
|
||||
docker compose -f docker-compose.pgmanage.yml up -d
|
||||
```
|
||||
|
||||
It publishes `http://localhost:8000` with a standard Docker port mapping and persists its data in the named Docker volume `pgmanage_data`.
|
||||
From inside the container, connect to the local Postgres service through `host.docker.internal:5432`.
|
||||
|
||||
### Test
|
||||
|
||||
Use the test stack to run repeatable containerized verification:
|
||||
|
||||
```bash
|
||||
docker compose -f docker-compose.test.yml up --build --abort-on-container-exit
|
||||
```
|
||||
|
||||
The test container runs:
|
||||
|
||||
- `npm run db:deploy`
|
||||
- `npm run db:generate`
|
||||
- `npm run type-check`
|
||||
- `npm run build`
|
||||
- `npm run test:api:integration`
|
||||
|
||||
### Production
|
||||
|
||||
The production stack runs behind **Traefik** (reverse proxy + automatic HTTPS via Let's Encrypt). All services communicate over a private Docker network (`internal`). Traefik reaches public-facing services via a separate `traefik-proxy` network.
|
||||
|
||||
#### 1. Point DNS to your server
|
||||
|
||||
Add an A record for every subdomain to your server's public IP before deploying so Let's Encrypt can issue certificates:
|
||||
|
||||
| Subdomain | Service |
|
||||
|---|---|
|
||||
| `rentaldrivego.ma` | marketplace and public site |
|
||||
| `api.rentaldrivego.ma` | API |
|
||||
| `pgmanage.rentaldrivego.ma` | pgManage (DB admin) |
|
||||
| `portainer.rentaldrivego.ma` | Portainer |
|
||||
|
||||
#### 2. Install Docker and clone the repo
|
||||
|
||||
```bash
|
||||
# Install Docker (if not already installed)
|
||||
curl -fsSL https://get.docker.com | sh
|
||||
|
||||
git clone <repo-url> rentaldrivego
|
||||
cd rentaldrivego
|
||||
```
|
||||
|
||||
#### 3. Create the shared Traefik network
|
||||
|
||||
Only needs to be done once per server. If it already exists this is a no-op.
|
||||
|
||||
```bash
|
||||
docker network create traefik-proxy
|
||||
```
|
||||
|
||||
#### 4. Configure environment variables
|
||||
|
||||
```bash
|
||||
cp .env.docker.production.example .env.docker.production
|
||||
```
|
||||
|
||||
Open `.env.docker.production` and fill in every value. The minimum required secrets are:
|
||||
|
||||
| Variable | What to set |
|
||||
|---|---|
|
||||
| `POSTGRES_PASSWORD` | Strong random password |
|
||||
| `JWT_SECRET` | Long random string (e.g. `openssl rand -hex 64`) |
|
||||
| `ACME_EMAIL` | Your email for Let's Encrypt notifications |
|
||||
| `RESEND_API_KEY` | Resend API key (or configure SMTP vars instead) |
|
||||
|
||||
Production now derives `DATABASE_URL` inside the app container from `POSTGRES_HOST`, `POSTGRES_PORT`, `POSTGRES_DB`, `POSTGRES_USER`, and `POSTGRES_PASSWORD` when `DATABASE_URL_FROM_POSTGRES=true`. That avoids Prisma auth failures when the database password contains reserved URL characters such as `@`, `:`, or `/`.
|
||||
|
||||
The example file uses `rentaldrivego.ma` for the marketplace and public site. The dashboard and admin panel are routed under that same host at `/dashboard` and `/admin`.
|
||||
Set `PORTAINER_DOMAIN=portainer.rentaldrivego.ma` in `.env.docker.production` to expose Portainer through Traefik.
|
||||
|
||||
#### 4a. Configure the registry for CI deploys
|
||||
|
||||
If you want GitLab CI to build once and deploy by pulling a prebuilt image on the VPS, configure one of these two modes in GitLab `Settings > CI/CD > Variables`:
|
||||
|
||||
- Built-in GitLab Container Registry:
|
||||
Enable the project registry and use GitLab's default `CI_REGISTRY`, `CI_REGISTRY_USER`, `CI_REGISTRY_PASSWORD`, and `CI_REGISTRY_IMAGE` variables.
|
||||
- Explicit registry variables:
|
||||
Set `REGISTRY_HOST`, `REGISTRY_USER`, `REGISTRY_PASSWORD`, and `REGISTRY_IMAGE` yourself. This works for GitLab Registry, Docker Hub, GHCR, or another OCI registry.
|
||||
|
||||
Example explicit values for GitLab's hosted registry:
|
||||
|
||||
```text
|
||||
REGISTRY_HOST=gitlab.rentaldrivego.ma:5050
|
||||
REGISTRY_IMAGE=gitlab.rentaldrivego.ma:5050/rental_car/car_management_system
|
||||
REGISTRY_USER=<deploy-user-or-token-name>
|
||||
REGISTRY_PASSWORD=<personal-access-token-or-deploy-token>
|
||||
```
|
||||
|
||||
The production compose file now reads `APP_IMAGE` and `APP_VERSION` for pull-based deploys. The GitLab deploy job injects those values automatically. If you want to pull manually on the server, you can also set `APP_IMAGE` in `.env.docker.production`.
|
||||
|
||||
The CI pipeline publishes and deploys from the GitLab default branch (`$CI_DEFAULT_BRANCH`). If you change your release branch, update the repository default branch in GitLab instead of hard-coding branch names in `.gitlab-ci.yml`.
|
||||
|
||||
If you use `docker:dind` on a self-hosted GitLab runner, the runner's Docker executor must be started with `privileged = true`. Without that, `docker:dind` often logs AppArmor or `/sys/kernel/security` mount errors and can become unreliable even when the job container still starts.
|
||||
|
||||
#### 5. Start Traefik
|
||||
|
||||
Traefik must be running before the app stack so it can wire up routes at startup.
|
||||
|
||||
```bash
|
||||
docker compose -f traefik.yaml up -d
|
||||
```
|
||||
|
||||
#### 6. Build and start the app stack
|
||||
|
||||
```bash
|
||||
npm run docker:prod:up
|
||||
```
|
||||
|
||||
Or use the new helper scripts if you want to start one production container at a time:
|
||||
|
||||
```bash
|
||||
npm run docker:prod:start:traefik
|
||||
npm run docker:prod:start:postgres
|
||||
npm run docker:prod:start:redis
|
||||
npm run docker:prod:start:api
|
||||
npm run docker:prod:start:marketplace
|
||||
npm run docker:prod:start:dashboard
|
||||
npm run docker:prod:start:admin
|
||||
npm run docker:prod:start:frontends
|
||||
npm run docker:prod:start:pgmanage
|
||||
npm run docker:prod:start:portainer
|
||||
npm run docker:prod:start:all
|
||||
npm run docker:prod:start:api && npm run docker:prod:start:frontends
|
||||
```
|
||||
|
||||
Docker will:
|
||||
1. Build the monorepo image
|
||||
2. Start all app services (`api`, `marketplace`, `dashboard`, `admin`, `pgmanage`)
|
||||
|
||||
Traefik automatically picks up the containers and provisions TLS certificates. Services are live at their `https://` URLs within ~30 seconds.
|
||||
|
||||
#### 6a. Deploy Portainer
|
||||
|
||||
Portainer is deployed separately from the main app stack and reuses the shared `traefik-proxy` network managed by Traefik.
|
||||
|
||||
```bash
|
||||
npm run docker:prod:start:portainer
|
||||
```
|
||||
|
||||
Equivalent raw Docker Compose command:
|
||||
|
||||
```bash
|
||||
docker compose -p rentaldrivego-portainer-prod --env-file .env.docker.production -f docker-compose.portainer.production.yml up -d
|
||||
```
|
||||
|
||||
Uploaded assets handled by the API, including vehicle photos and company branding images, are persisted in the named Docker volume `api_uploads`, mounted inside the API container at `/var/lib/rentaldrivego/storage`. Rebuilding or redeploying the API no longer clears those files as long as that volume is kept.
|
||||
|
||||
The production helper scripts and the GitLab deploy job now explicitly create the Docker volume `${DOCKER_PROD_PROJECT_NAME:-rentaldrivego-prod}_api_uploads` before starting or redeploying the API stack, so a fresh server bootstrap does not accidentally start the API without its upload storage volume.
|
||||
|
||||
#### Updating after a code change
|
||||
|
||||
Pull the latest code and rebuild only the changed service:
|
||||
|
||||
```bash
|
||||
git pull
|
||||
docker compose -p rentaldrivego-prod --env-file .env.docker.production -f docker-compose.production.yml up --build -d --no-deps <service>
|
||||
# e.g. to redeploy only the API:
|
||||
docker compose -p rentaldrivego-prod --env-file .env.docker.production -f docker-compose.production.yml up --build -d --no-deps api
|
||||
```
|
||||
|
||||
To rebuild everything:
|
||||
|
||||
```bash
|
||||
npm run docker:prod:up
|
||||
```
|
||||
|
||||
#### Apply database migrations without downtime
|
||||
|
||||
```bash
|
||||
docker compose -p rentaldrivego-prod --env-file .env.docker.production -f docker-compose.production.yml run --rm api npm run db:deploy
|
||||
```
|
||||
|
||||
#### Create the first production admin
|
||||
|
||||
The repo includes a seed that creates the first `SUPER_ADMIN` if that email does not already exist.
|
||||
|
||||
```bash
|
||||
docker compose -p rentaldrivego-prod --env-file .env.docker.production -f docker-compose.production.yml run --rm \
|
||||
-e ADMIN_SEED_EMAIL=rentaldrivego@gmail.com \
|
||||
-e ADMIN_SEED_PASSWORD='Qwerty00!@#$%' \
|
||||
-e ADMIN_SEED_FIRST_NAME=Super \
|
||||
-e ADMIN_SEED_LAST_NAME=Admin \
|
||||
api npm run db:seed:admin
|
||||
```
|
||||
|
||||
Then sign in at `https://rentaldrivego.ma/admin/login` and create any additional admin users from the admin panel.
|
||||
|
||||
#### View logs
|
||||
|
||||
```bash
|
||||
# All services
|
||||
npm run docker:prod:logs
|
||||
|
||||
# Single service
|
||||
npm run docker:prod:logs:api
|
||||
```
|
||||
|
||||
#### Backup production data
|
||||
|
||||
Create a timestamped backup directory under `./backups`:
|
||||
|
||||
```bash
|
||||
npm run docker:prod:backup
|
||||
```
|
||||
|
||||
Or choose a different parent directory:
|
||||
|
||||
```bash
|
||||
bash scripts/docker-prod-backup.sh /srv/rentaldrivego-backups
|
||||
```
|
||||
|
||||
Each backup contains:
|
||||
|
||||
- `postgres.dump` — logical PostgreSQL backup in custom format
|
||||
- `api-uploads.tar.gz` — uploaded files from `/var/lib/rentaldrivego/storage`
|
||||
- `traefik-letsencrypt.tar.gz` — Traefik ACME certificate state, when available
|
||||
- `volumes/*.tar.gz` — raw Docker volume archives for the default production volumes
|
||||
- `manifest.txt` — basic metadata
|
||||
|
||||
By default, the backup also archives these named production volumes when they exist:
|
||||
|
||||
- `${DOCKER_PROD_PROJECT_NAME:-rentaldrivego-prod}_api_uploads`
|
||||
- `${DOCKER_PROD_PROJECT_NAME:-rentaldrivego-prod}_pgmanage_prod_data`
|
||||
- `${DOCKER_PROD_PROJECT_NAME:-rentaldrivego-prod}_postgres_prod_data`
|
||||
- `${DOCKER_PROD_PROJECT_NAME:-rentaldrivego-prod}_redis_prod_data`
|
||||
|
||||
To include extra named volumes from the same server, pass them as additional arguments:
|
||||
|
||||
```bash
|
||||
bash scripts/docker-prod-backup.sh /srv/rentaldrivego-backups \
|
||||
gitlab-l3gq_gitlab-config \
|
||||
gitlab-l3gq_gitlab-data \
|
||||
gitlab-l3gq_gitlab-logs \
|
||||
pgmanage_data \
|
||||
traefik-gcjk_portainer_data \
|
||||
traefik-gcjk_traefik-letsencrypt
|
||||
```
|
||||
|
||||
You can also provide extra volume names through `DOCKER_EXTRA_BACKUP_VOLUMES`.
|
||||
|
||||
#### Restore production data
|
||||
|
||||
Restore is destructive: it overwrites the production database, uploaded files, and, when present in the backup, Traefik ACME state.
|
||||
|
||||
```bash
|
||||
bash scripts/docker-prod-restore.sh ./backups/rentaldrivego-prod-YYYYMMDDTHHMMSSZ --yes
|
||||
```
|
||||
|
||||
The restore script:
|
||||
|
||||
1. Stops public app services
|
||||
2. Restores the PostgreSQL dump
|
||||
3. Replaces uploaded files
|
||||
4. Restores Traefik ACME state if the archive exists
|
||||
5. Restores raw volume archives from `volumes/` except the ones already covered by the database and upload restore steps
|
||||
6. Starts Traefik and the production stack again
|
||||
|
||||
If the backup contains raw archives for volumes used by other stacks such as GitLab or Portainer, stop those containers before running restore. The script will refuse to overwrite a volume that is currently attached to a running container.
|
||||
|
||||
Before running restore on a live server, take a fresh backup first.
|
||||
|
||||
#### Stop the stack
|
||||
|
||||
```bash
|
||||
# Stop containers but keep volumes (data is preserved)
|
||||
npm run docker:prod:down
|
||||
|
||||
# Stop and delete all data (destructive — irreversible)
|
||||
docker compose -p rentaldrivego-prod --env-file .env.docker.production -f docker-compose.production.yml down -v
|
||||
```
|
||||
|
||||
#### pgManage (DB admin UI)
|
||||
|
||||
pgManage is available at `https://pgmanage.rentaldrivego.ma`. To connect to the production database, add a connection inside pgManage with:
|
||||
|
||||
- **Host:** `localhost`
|
||||
- **Port:** `5432`
|
||||
- **Database:** `rentaldrivego`
|
||||
- **Username:** `postgres`
|
||||
- **Password:** value of `POSTGRES_PASSWORD` from `.env.docker.production`
|
||||
|
||||
#### Portainer
|
||||
|
||||
Portainer is available at `https://portainer.rentaldrivego.ma`.
|
||||
|
||||
Use this command to deploy or update it:
|
||||
|
||||
```bash
|
||||
npm run docker:prod:start:portainer
|
||||
```
|
||||
|
||||
### Notes
|
||||
|
||||
- The production image builds the whole monorepo once, then each service overrides its runtime command.
|
||||
- The dev compose file bind-mounts the repo and keeps `node_modules` in a named volume.
|
||||
- `API_INTERNAL_URL` is used for server-side container-to-container calls, while `NEXT_PUBLIC_API_URL` is used by the browser.
|
||||
- The Dockerfiles activate the repo's pinned `npm@10.5.0` with `corepack` before install so container builds do not depend on the npm version bundled with the base image.
|
||||
- The dev compose stack stores Postgres data in `postgres_dev_data` and the bootstrap marker in `postgres_bootstrap_state`, so `up --build` does not reseed an existing local database.
|
||||
- If you need database schema updates inside Docker, run:
|
||||
|
||||
```bash
|
||||
docker compose -f docker-compose.dev.yml run --rm migrate
|
||||
```
|
||||
|
||||
If a cached base image still fails during `npm ci`, refresh it and rebuild without cache:
|
||||
|
||||
```bash
|
||||
docker pull node:20-bookworm
|
||||
docker compose -f docker-compose.dev.yml build --no-cache dashboard
|
||||
```
|
||||
@@ -0,0 +1,15 @@
|
||||
# infra
|
||||
docker compose -f docker-compose.dev.yml up -d postgres
|
||||
docker compose -f docker-compose.dev.yml up -d redis
|
||||
|
||||
# app services
|
||||
docker compose -f docker-compose.dev.yml --profile api up --build
|
||||
docker compose -f docker-compose.dev.yml --profile marketplace up --build
|
||||
docker compose -f docker-compose.dev.yml --profile dashboard up --build
|
||||
docker compose -f docker-compose.dev.yml --profile admin up --build
|
||||
|
||||
# tools
|
||||
docker compose -f docker-compose.dev.yml --profile tools up --build
|
||||
|
||||
# full stack
|
||||
docker compose -f docker-compose.dev.yml --profile full up --build
|
||||
@@ -1,459 +1,83 @@
|
||||
# API Refactor Task List
|
||||
# API Refactor Status
|
||||
|
||||
This document turns the current API refactor direction into a concrete task list for this repo.
|
||||
This file replaces the older pre-refactor task list that referenced route paths and module boundaries no longer present in the codebase.
|
||||
|
||||
Scope:
|
||||
- modularize route and service structure
|
||||
- strengthen validation boundaries
|
||||
- add meaningful API test coverage
|
||||
Source of truth:
|
||||
|
||||
Non-goals:
|
||||
- rewrite the API framework
|
||||
- switch ORM or database
|
||||
- redesign product behavior
|
||||
- `apps/api/src/app.ts`
|
||||
- `apps/api/src/modules/*`
|
||||
- `apps/api/src/http/*`
|
||||
|
||||
## Current State
|
||||
## Refactor State
|
||||
|
||||
Main pressure points in the current API:
|
||||
- large route files with mixed concerns:
|
||||
- `apps/api/src/routes/reservations.ts`
|
||||
- `apps/api/src/routes/site.ts`
|
||||
- `apps/api/src/routes/marketplace.ts`
|
||||
- `apps/api/src/routes/admin.ts`
|
||||
- business logic split inconsistently between `routes/` and `services/`
|
||||
- no real API test runner wired in `apps/api/package.json`
|
||||
- ad hoc request parsing and response/error shaping across route files
|
||||
- direct Prisma access from route handlers in many domains
|
||||
The API has already been refactored into the modular structure the old task list was aiming for.
|
||||
|
||||
## Target Structure
|
||||
Current structure includes:
|
||||
|
||||
Target layout after refactor:
|
||||
- `modules/*` for route/service/repo/presenter grouping
|
||||
- `http/errors` for centralized error types and middleware
|
||||
- `http/validate` for request parsing helpers
|
||||
- `http/respond` for success helpers
|
||||
- module-level tests and integration tests under `apps/api/src/tests`
|
||||
|
||||
```text
|
||||
apps/api/src/
|
||||
modules/
|
||||
customers/
|
||||
customer.routes.ts
|
||||
customer.schemas.ts
|
||||
customer.service.ts
|
||||
customer.repo.ts
|
||||
customer.presenter.ts
|
||||
customer.test.ts
|
||||
vehicles/
|
||||
companies/
|
||||
reservations/
|
||||
marketplace/
|
||||
site/
|
||||
http/
|
||||
errors/
|
||||
middleware/
|
||||
respond/
|
||||
validate/
|
||||
lib/
|
||||
services/
|
||||
```
|
||||
The older checklist items that referenced:
|
||||
|
||||
Rules for the target shape:
|
||||
- route files parse input, call services, and return responses only
|
||||
- services own business rules and transaction boundaries
|
||||
- repos own Prisma access
|
||||
- schemas own `params`, `query`, and `body` validation
|
||||
- presenters own response DTO shaping
|
||||
- `apps/api/src/routes/*.ts`
|
||||
- missing test tooling
|
||||
- missing shared validation helpers
|
||||
- missing shared response helpers
|
||||
|
||||
## Phase 1: Foundation
|
||||
are no longer accurate and should not be used as active work items.
|
||||
|
||||
### 1.1 Add API test tooling
|
||||
## Remaining Follow-Up Work
|
||||
|
||||
Tasks:
|
||||
- add `vitest` to the repo
|
||||
- add `supertest` for HTTP integration tests
|
||||
- add API test scripts to:
|
||||
- `apps/api/package.json`
|
||||
- root `package.json`
|
||||
- add a basic test config under `apps/api`
|
||||
- decide on test DB bootstrap using existing Docker test stack in `docker-compose.test.yml`
|
||||
These are the only meaningful refactor follow-ups still worth tracking at a high level.
|
||||
|
||||
Acceptance criteria:
|
||||
- `npm run test --workspace @rentaldrivego/api` exists
|
||||
- one smoke test can boot the Express app and hit `/health`
|
||||
- one authenticated test can hit a protected route
|
||||
### 1. Keep OpenAPI coverage aligned with route growth
|
||||
|
||||
### 1.2 Centralize HTTP errors
|
||||
The API now has:
|
||||
|
||||
Tasks:
|
||||
- create `apps/api/src/http/errors/`
|
||||
- add app error classes:
|
||||
- `AppError`
|
||||
- `ValidationError`
|
||||
- `NotFoundError`
|
||||
- `ConflictError`
|
||||
- `ForbiddenError`
|
||||
- `UnauthorizedError`
|
||||
- add a single Express error middleware
|
||||
- register it in `apps/api/src/index.ts`
|
||||
- Swagger UI at `/docs`
|
||||
- OpenAPI JSON at `/api/v1/openapi.json`
|
||||
|
||||
Acceptance criteria:
|
||||
- route files stop hand-shaping most error JSON
|
||||
- thrown app errors become consistent `{ error, message, statusCode }` responses
|
||||
But the OpenAPI document does not yet mirror every newer route group perfectly. Continue updating:
|
||||
|
||||
### 1.3 Centralize request parsing helpers
|
||||
- `apps/api/src/swagger/openapi.ts`
|
||||
- route schemas
|
||||
- module docs
|
||||
|
||||
Tasks:
|
||||
- create `apps/api/src/http/validate/`
|
||||
- add helpers for:
|
||||
- `parseBody`
|
||||
- `parseQuery`
|
||||
- `parseParams`
|
||||
- standardize `zod` parse error mapping to `400`
|
||||
whenever new endpoints are added.
|
||||
|
||||
Acceptance criteria:
|
||||
- new/updated route files no longer call `schema.parse(req.body)` inline repeatedly
|
||||
- validation failures follow one shared response shape
|
||||
### 2. Expand integration test coverage
|
||||
|
||||
### 1.4 Centralize response helpers
|
||||
Integration tests exist, but the heaviest workflows still benefit from deeper coverage:
|
||||
|
||||
Tasks:
|
||||
- create `apps/api/src/http/respond/`
|
||||
- add helpers for:
|
||||
- `ok`
|
||||
- `created`
|
||||
- `noContent`
|
||||
- use the same success envelope conventions across refactored modules
|
||||
- subscription billing transitions
|
||||
- marketplace reservation intake
|
||||
- public booking and payment initialization
|
||||
- reservation inspection and close flows
|
||||
- admin billing operations
|
||||
|
||||
Acceptance criteria:
|
||||
- refactored routes send responses through shared helpers
|
||||
### 3. Reduce remaining direct Prisma orchestration in large services
|
||||
|
||||
## Phase 2: First Safe Module Extractions
|
||||
The route layer is already thin in the current API. The next cleanup target is inside larger service files where orchestration still mixes:
|
||||
|
||||
Goal:
|
||||
- refactor smaller route files first to establish patterns before touching reservation flows
|
||||
- multi-step workflow logic
|
||||
- transaction boundaries
|
||||
- some direct Prisma writes
|
||||
|
||||
### 2.1 Customers module
|
||||
especially in:
|
||||
|
||||
Files in scope:
|
||||
- `apps/api/src/routes/customers.ts`
|
||||
- `apps/api/src/services/licenseValidationService.ts`
|
||||
- customer-related Prisma access
|
||||
- reservation flows
|
||||
- subscription/billing flows
|
||||
- admin billing flows
|
||||
|
||||
Tasks:
|
||||
- create:
|
||||
- `modules/customers/customer.routes.ts`
|
||||
- `modules/customers/customer.schemas.ts`
|
||||
- `modules/customers/customer.service.ts`
|
||||
- `modules/customers/customer.repo.ts`
|
||||
- `modules/customers/customer.presenter.ts`
|
||||
- move all Prisma reads/writes out of the route file
|
||||
- move license image upload orchestration into the service
|
||||
- standardize customer DTO output
|
||||
### 4. Keep docs aligned with disabled flows
|
||||
|
||||
Tests to add:
|
||||
- list customers
|
||||
- create customer
|
||||
- update customer
|
||||
- validate license
|
||||
- approve license
|
||||
- upload license image
|
||||
Some legacy surfaces still exist as placeholders or schema remnants, for example:
|
||||
|
||||
Acceptance criteria:
|
||||
- `routes/customers.ts` becomes a thin delegator or is replaced by module route registration
|
||||
- no direct Prisma calls remain in the customer route layer
|
||||
- disabled Clerk webhook endpoint
|
||||
- legacy `clerkUserId` field on `Employee`
|
||||
- disabled renter signup/login API endpoints
|
||||
|
||||
### 2.2 Vehicles module
|
||||
|
||||
Files in scope:
|
||||
- `apps/api/src/routes/vehicles.ts`
|
||||
- `apps/api/src/services/vehicleAvailabilityService.ts`
|
||||
- `apps/api/src/lib/storage.ts`
|
||||
|
||||
Tasks:
|
||||
- extract module files for vehicles
|
||||
- isolate upload/photo operations from route code
|
||||
- isolate availability query logic from route formatting
|
||||
- create presenter functions for dashboard-facing vehicle responses
|
||||
|
||||
Tests to add:
|
||||
- create vehicle
|
||||
- update vehicle
|
||||
- upload photos
|
||||
- delete photo
|
||||
- availability check
|
||||
|
||||
Acceptance criteria:
|
||||
- vehicle upload and availability logic are both service-owned
|
||||
|
||||
### 2.3 Companies module
|
||||
|
||||
Files in scope:
|
||||
- `apps/api/src/routes/companies.ts`
|
||||
|
||||
Tasks:
|
||||
- extract company brand/profile subdomain flows into a module
|
||||
- isolate brand asset upload logic
|
||||
- separate subdomain availability checks from route code
|
||||
|
||||
Tests to add:
|
||||
- get company profile
|
||||
- update company profile
|
||||
- upload logo
|
||||
- upload hero image
|
||||
- subdomain check
|
||||
|
||||
Acceptance criteria:
|
||||
- no upload/storage branching remains in the route handler
|
||||
|
||||
## Phase 3: High-Value Reservation Refactor
|
||||
|
||||
This is the biggest and highest-risk slice.
|
||||
|
||||
### 3.1 Split reservations into sub-services
|
||||
|
||||
Files in scope:
|
||||
- `apps/api/src/routes/reservations.ts`
|
||||
- `apps/api/src/services/additionalDriverService.ts`
|
||||
- `apps/api/src/services/insuranceService.ts`
|
||||
- `apps/api/src/services/pricingRuleService.ts`
|
||||
- `apps/api/src/services/licenseValidationService.ts`
|
||||
- `apps/api/src/services/invoicePdfService.ts`
|
||||
|
||||
Target internal split:
|
||||
- `reservation.service.ts`
|
||||
- `reservation.lifecycle.service.ts`
|
||||
- `reservation.pricing.service.ts`
|
||||
- `reservation.insurance.service.ts`
|
||||
- `reservation.additional-driver.service.ts`
|
||||
- `reservation.document.service.ts`
|
||||
- `reservation.inspection.service.ts`
|
||||
- `reservation.presenter.ts`
|
||||
- `reservation.repo.ts`
|
||||
|
||||
Tasks:
|
||||
- extract create/update/confirm/check-in/check-out/close flows into dedicated service methods
|
||||
- move `serializeReservationForDashboard` into a presenter
|
||||
- move document number generation out of route code
|
||||
- keep Prisma transactions in the service layer only
|
||||
- remove route-local helper sprawl from `reservations.ts`
|
||||
|
||||
Tests to add:
|
||||
- create reservation success
|
||||
- create reservation conflict
|
||||
- confirm reservation
|
||||
- check-in reservation
|
||||
- check-out reservation
|
||||
- close reservation
|
||||
- add additional driver requiring approval
|
||||
- pricing rule application
|
||||
- insurance application
|
||||
|
||||
Acceptance criteria:
|
||||
- reservation route file becomes orchestration-only
|
||||
- all state transitions are covered by integration tests
|
||||
|
||||
## Phase 4: Public-Surface Modules
|
||||
|
||||
These routes should move after internal domains are stabilized.
|
||||
|
||||
### 4.1 Marketplace module
|
||||
|
||||
Files in scope:
|
||||
- `apps/api/src/routes/marketplace.ts`
|
||||
|
||||
Tasks:
|
||||
- extract search/filter/offer validation into module files
|
||||
- unify database-unavailable handling
|
||||
- separate presenter logic from business logic
|
||||
|
||||
Tests to add:
|
||||
- list cities
|
||||
- search vehicles
|
||||
- fetch vehicle details
|
||||
- validate offer / promo path
|
||||
|
||||
### 4.2 Site module
|
||||
|
||||
Files in scope:
|
||||
- `apps/api/src/routes/site.ts`
|
||||
|
||||
Tasks:
|
||||
- extract public company site flows
|
||||
- isolate booking flow for public site reservations
|
||||
- unify maintenance-mode / database-unavailable behavior
|
||||
|
||||
Tests to add:
|
||||
- fetch site brand
|
||||
- fetch public vehicle list
|
||||
- availability check
|
||||
- create booking request
|
||||
- payment-init guard paths
|
||||
|
||||
Acceptance criteria:
|
||||
- public route handlers become small and deterministic
|
||||
|
||||
## Phase 5: Cross-Cutting Cleanup
|
||||
|
||||
### 5.1 Authentication and role boundaries
|
||||
|
||||
Files in scope:
|
||||
- `apps/api/src/middleware/requireCompanyAuth.ts`
|
||||
- `apps/api/src/middleware/requireRenterAuth.ts`
|
||||
- `apps/api/src/middleware/requireAdminAuth.ts`
|
||||
- `apps/api/src/middleware/requireRole.ts`
|
||||
- `apps/api/src/middleware/requireTenant.ts`
|
||||
- `apps/api/src/middleware/requireSubscription.ts`
|
||||
|
||||
Tasks:
|
||||
- standardize auth failure responses
|
||||
- document what request context each middleware guarantees
|
||||
- remove duplicate assumptions in route files
|
||||
|
||||
Acceptance criteria:
|
||||
- auth and permission expectations are explicit and shared
|
||||
|
||||
### 5.2 Upload policy standardization
|
||||
|
||||
Files in scope:
|
||||
- `apps/api/src/routes/vehicles.ts`
|
||||
- `apps/api/src/routes/companies.ts`
|
||||
- `apps/api/src/routes/customers.ts`
|
||||
- `apps/api/src/lib/storage.ts`
|
||||
- `docker-compose.production.yml`
|
||||
- `docker-compose.dev.yml`
|
||||
|
||||
Tasks:
|
||||
- create shared upload policy helpers for image uploads
|
||||
- standardize max file size, mime validation, and error responses
|
||||
- centralize folder path rules for stored uploads
|
||||
- keep runtime uploads out of the API source tree and out of the image filesystem
|
||||
- require all uploaded files to be written to the configured storage root mounted from a Docker volume
|
||||
- verify the API serves uploads from the volume-backed storage root only
|
||||
- document the storage contract clearly:
|
||||
- app code may generate URLs and read/write through the storage abstraction
|
||||
- Docker volumes own persistence
|
||||
- rebuilds and redeploys must not delete uploaded files
|
||||
|
||||
Acceptance criteria:
|
||||
- upload endpoints share one validation policy
|
||||
- uploaded files are not stored inside `apps/api/src`, `apps/api/dist`, or any other API-local runtime path
|
||||
- production uploads persist in the named Docker volume mounted into the API container
|
||||
- dev Docker uploads persist in the dev named Docker volume mounted into the API container
|
||||
- storage behavior is environment-configured through the storage root, not hardcoded to an app-relative folder
|
||||
|
||||
### 5.3 Remove route-local DTO drift
|
||||
|
||||
Tasks:
|
||||
- define presenter functions for each major domain
|
||||
- stop returning raw Prisma records where API shape is intended to be stable
|
||||
|
||||
Acceptance criteria:
|
||||
- DTO shape changes are isolated to presenters
|
||||
|
||||
## Testing Plan
|
||||
|
||||
### Priority 0: smoke coverage
|
||||
|
||||
Add first:
|
||||
- `/health`
|
||||
- authenticated customer list
|
||||
- authenticated vehicle list
|
||||
- reservation create
|
||||
- site availability
|
||||
|
||||
### Priority 1: critical business flows
|
||||
|
||||
Add next:
|
||||
- reservation state changes
|
||||
- uploads
|
||||
- payments init guards
|
||||
- license approval paths
|
||||
|
||||
### Priority 2: regression coverage
|
||||
|
||||
Add later:
|
||||
- analytics
|
||||
- notifications
|
||||
- admin routes
|
||||
- subscriptions
|
||||
|
||||
### Test types
|
||||
|
||||
Use:
|
||||
- unit tests for pure business logic
|
||||
- integration tests for route + middleware + DB behavior
|
||||
- a few contract-style tests for public endpoints
|
||||
|
||||
Avoid:
|
||||
- brittle snapshots
|
||||
- tests that assert Prisma internal shapes directly unless repo-level persistence behavior is the actual concern
|
||||
|
||||
## Suggested Execution Order
|
||||
|
||||
1. add test tooling and smoke tests
|
||||
2. add shared error middleware and validation helpers
|
||||
3. refactor customers
|
||||
4. refactor vehicles
|
||||
5. refactor companies
|
||||
6. refactor reservations in slices
|
||||
7. refactor marketplace
|
||||
8. refactor site
|
||||
9. refactor admin, notifications, analytics, subscriptions, payments
|
||||
|
||||
## Deliverables Checklist
|
||||
|
||||
- [x] API test runner added
|
||||
- [x] shared error middleware added
|
||||
- [x] shared validation helpers added
|
||||
- [x] shared response helpers added
|
||||
- [x] customers module extracted
|
||||
- [x] vehicles module extracted
|
||||
- [x] companies module extracted
|
||||
- [x] reservations module extracted
|
||||
- [x] marketplace module extracted
|
||||
- [x] site module extracted
|
||||
- [ ] upload validation standardized
|
||||
- [x] auth/role middleware behavior documented
|
||||
- [ ] critical booking flows covered by integration tests
|
||||
|
||||
|
||||
## Status Update
|
||||
|
||||
Completed in the workspace:
|
||||
- phases 1 through 5 are implemented
|
||||
- the originally listed phase 9 domains are modularized, and the remaining route surface was also cleared for:
|
||||
- `team`
|
||||
- `offers`
|
||||
- `notifications`
|
||||
- `analytics`
|
||||
- `subscriptions`
|
||||
- `payments`
|
||||
- `admin`
|
||||
- `auth.*`
|
||||
- `webhooks`
|
||||
- `apps/api/src/routes/` has been cleared and route registration now points at module routers
|
||||
- shared HTTP helpers are in place under:
|
||||
- `apps/api/src/http/errors/`
|
||||
- `apps/api/src/http/validate/`
|
||||
- `apps/api/src/http/respond/`
|
||||
- the Docker-backed API integration workflow is wired and passing
|
||||
|
||||
Current automated integration coverage includes:
|
||||
- health
|
||||
- customers
|
||||
- vehicles
|
||||
- reservations
|
||||
- payments
|
||||
- subscriptions
|
||||
- notifications
|
||||
- admin
|
||||
|
||||
Still intentionally open:
|
||||
- upload policy standardization from section `5.2`
|
||||
- any remaining Priority 1 integration gaps not yet covered by the current suite, especially upload and license-approval paths if they are still required as dedicated HTTP flows
|
||||
|
||||
## Completion Criteria
|
||||
|
||||
The refactor is considered successful when:
|
||||
- route files are thin and mostly declarative
|
||||
- business rules live in services, not route handlers
|
||||
- Prisma access lives in repos or well-defined service boundaries
|
||||
- request validation is explicit for `params`, `query`, and `body`
|
||||
- core booking, customer, and upload flows have automated coverage
|
||||
- API error and success envelopes are consistent across modules
|
||||
Those should stay clearly marked in docs so design specs do not drift back toward removed implementations.
|
||||
|
||||
@@ -1,57 +1,41 @@
|
||||
## Project Design Audit
|
||||
# Documentation Audit
|
||||
|
||||
Date: 2026-05-01
|
||||
Date: 2026-05-26
|
||||
|
||||
### What I checked
|
||||
## Purpose
|
||||
|
||||
- Compared `project_design` team-related source stubs against the live implementation in:
|
||||
- `apps/dashboard/src/components/team/*`
|
||||
- `apps/dashboard/src/app/(dashboard)/dashboard/team/page.tsx`
|
||||
- `apps/api/src/routes/team.ts`
|
||||
- `apps/api/src/routes/webhooks.ts`
|
||||
- `apps/api/src/services/teamService.ts`
|
||||
- `apps/api/src/middleware/requireRole.ts`
|
||||
- Scanned the repo for obvious page- and feature-level gaps against:
|
||||
- `project_design/README.md`
|
||||
- `project_design/FEATURES.md`
|
||||
- `project_design/PAGES.md`
|
||||
- `project_design/advanced-features.md`
|
||||
This note records the cleanup applied to the `docs/project-design` folder after comparing it with the live codebase.
|
||||
|
||||
### Added to the project
|
||||
## Drift That Was Removed
|
||||
|
||||
- Copied the design documentation into `docs/project-design/`
|
||||
- Copied the design source stubs into `docs/project-design/stubs/`
|
||||
- Copied `project_design/rentalcardrive.png` into `apps/public-site/public/rentalcardrive.png`
|
||||
The older design docs included several features or assumptions that are not active in the current implementation:
|
||||
|
||||
### Confirmed as already implemented in the app code
|
||||
- Clerk-based employee auth and webhooks
|
||||
- Clerk-based team invite acceptance
|
||||
- renter self-service signup/login as an active user flow
|
||||
- renter email verification as an active flow
|
||||
- a separate white-label company public-site frontend app
|
||||
- multi-currency SaaS subscription checkout beyond `MAD`
|
||||
- marketing routes such as `/about`, `/contact`, and `/blog`
|
||||
- outdated API file paths under `apps/api/src/routes/*`
|
||||
|
||||
- Team API route exists: `apps/api/src/routes/team.ts`
|
||||
- Clerk invitation webhook route exists: `apps/api/src/routes/webhooks.ts`
|
||||
- Team service exists: `apps/api/src/services/teamService.ts`
|
||||
- Role middleware exists: `apps/api/src/middleware/requireRole.ts`
|
||||
- Dashboard team UI exists:
|
||||
- `apps/dashboard/src/app/(dashboard)/dashboard/team/page.tsx`
|
||||
- `apps/dashboard/src/components/team/InviteModal.tsx`
|
||||
- `apps/dashboard/src/components/team/EditMemberModal.tsx`
|
||||
- `apps/dashboard/src/components/team/PermissionsMatrix.tsx`
|
||||
- `apps/dashboard/src/hooks/useTeam.ts`
|
||||
## Current Documentation Rule
|
||||
|
||||
These are not exact copies of the `project_design` stubs, but they cover the same implementation area and appear to be the working versions.
|
||||
The `docs/project-design` files should describe only one of two things:
|
||||
|
||||
### Obvious gaps against the design docs
|
||||
1. current implemented behavior
|
||||
2. explicit future plans, clearly labeled as plans
|
||||
|
||||
- Public-site pages from the page spec are missing or not present as app routes:
|
||||
- `/pricing`
|
||||
- `/about`
|
||||
- `/blog`
|
||||
- Renter account routes described in the page spec are not present as standalone routes:
|
||||
- `/renter/notifications`
|
||||
- `/renter/saved-companies`
|
||||
- `/renter/profile`
|
||||
- The marketplace renter dashboard exists, but it is not split into the separate pages described in `project_design/PAGES.md`.
|
||||
- The design document should not be treated as "fully implemented" yet. There are implemented areas, but the repo still has page-level and spec-level gaps.
|
||||
They should not describe removed systems as if they are still live.
|
||||
|
||||
### Notes
|
||||
## Current References
|
||||
|
||||
- The git worktree already had many unrelated local changes before this audit. I did not overwrite or revert them.
|
||||
- This audit is a practical coverage check, not a formal end-to-end verification of every feature in `project_design/FEATURES.md`.
|
||||
Use these documents as the current implementation references:
|
||||
|
||||
- `api-routes.md`
|
||||
- `schema.md`
|
||||
- `FEATURES.md`
|
||||
- `PAGES.md`
|
||||
- `INTEGRATION.md`
|
||||
|
||||
Use the execution-plan documents only as future/planning material, not as evidence that a feature is already shipped.
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
+118
-276
@@ -1,310 +1,152 @@
|
||||
# Features & Priorities — RentalDriveGo
|
||||
# Features — Current Product Scope
|
||||
|
||||
## MVP (Phase 1) — Must Ship Before Launch
|
||||
This document lists the features that are active in the current codebase.
|
||||
|
||||
### Subscription & Billing
|
||||
- [x] Company signup → plan selection → payment via AmanPay or PayPal
|
||||
- [x] 14-day free trial (no payment required until trial ends)
|
||||
- [x] Company status: PENDING → TRIALING → ACTIVE / PAST_DUE / SUSPENDED
|
||||
- [x] Subscription gate middleware on all dashboard API routes
|
||||
- [x] Trial / past-due banners in dashboard
|
||||
- [x] Suspended account blocks dashboard; public site stays up
|
||||
- [x] Manual renewal flow: company pays via AmanPay (card/cash/e-wallet) or PayPal
|
||||
- [x] Invoice history (AmanPay transaction IDs or PayPal capture IDs)
|
||||
- [x] Supported currencies: MAD, USD, EUR
|
||||
Source of truth:
|
||||
|
||||
### Tenant Isolation (Fleet Management)
|
||||
- [x] Every Prisma query scoped by `companyId`
|
||||
- [x] `requireTenant` middleware attaches `companyId` from authenticated employee
|
||||
- [x] Vehicle, customer, reservation IDs validated against `companyId` before any operation
|
||||
- [x] No cross-company data exposure — enforced at API level, not just UI
|
||||
- [x] Add / edit / archive vehicles
|
||||
- [x] Vehicle photos (Cloudinary)
|
||||
- [x] Published / unpublished toggle (controls marketplace + public site visibility)
|
||||
- [x] Vehicle categories, specs, features list
|
||||
- `apps/marketplace`
|
||||
- `apps/dashboard`
|
||||
- `apps/admin`
|
||||
- `apps/api`
|
||||
- `packages/database/prisma/schema.prisma`
|
||||
|
||||
### Offers / Promotions System
|
||||
- [x] Create offer (%, fixed amount, free day, special rate)
|
||||
- [x] Valid from / until dates
|
||||
- [x] Applicable to: all vehicles / by category / specific vehicles
|
||||
- [x] Optional promo code + max redemptions
|
||||
- [x] Minimum rental days
|
||||
- [x] `isPublic` flag — show on global marketplace
|
||||
- [x] `isFeatured` flag — featured carousel placement (plan-gated: GROWTH+)
|
||||
- [x] Offer applied at booking (discount calculated, stored on Reservation)
|
||||
- [x] Offer performance stats (redemptions, revenue impact)
|
||||
- [x] Offer banner image upload
|
||||
## Active Applications
|
||||
|
||||
### Global Marketplace (RentalDriveGo.com/explore)
|
||||
- [x] Featured offers carousel on /explore home
|
||||
- [x] Search vehicles by city, dates, category, price range
|
||||
- [x] Company cards (logo, rating, offer badge)
|
||||
- [x] Company marketplace profile page (vehicles + offers + reviews)
|
||||
- [x] Vehicle detail page (photos, specs, redirect CTA to company site)
|
||||
- [x] Cross-company availability check
|
||||
- [x] Filter by category, transmission, price, features
|
||||
- [x] Marketplace listing toggle per company (opt out)
|
||||
The current workspace contains four active apps:
|
||||
|
||||
### Renter Account System
|
||||
- [x] Renter signup / login (own auth, separate from Employee/Clerk)
|
||||
- [x] Email verification
|
||||
- [x] Renter profile (name, phone, locale, currency)
|
||||
- [x] Cross-company reservation history in /renter/dashboard
|
||||
- [x] Save / unsave companies
|
||||
- [x] Guest booking (no account required, but prompted to sign up after)
|
||||
- [x] When renter books, auto-create Customer record in that company's CRM
|
||||
- `apps/marketplace`
|
||||
- `apps/dashboard`
|
||||
- `apps/admin`
|
||||
- `apps/api`
|
||||
|
||||
### Online Reservations (all sources)
|
||||
- [x] Booking from dashboard (employee creates manually)
|
||||
- [x] Booking from company public site (BookingSource: PUBLIC_SITE)
|
||||
- [x] Booking from global marketplace (BookingSource: MARKETPLACE)
|
||||
- [x] Reservation lifecycle: DRAFT → CONFIRMED → ACTIVE → COMPLETED
|
||||
- [x] Offer / promo code applied at booking step
|
||||
- [x] Availability check before confirming
|
||||
- [x] Booking confirmation (number, summary, company contact)
|
||||
- [x] Cancel with reason + `cancelledBy` (COMPANY / RENTER / SYSTEM)
|
||||
There is no separate frontend app for a white-label company public site in this repo at the moment.
|
||||
|
||||
### Branding & White-Label Public Site
|
||||
- [x] Company display name, logo, tagline, hero image
|
||||
- [x] Primary brand color picker
|
||||
- [x] Subdomain: `{slug}.RentalDriveGo.com` (claimed during onboarding)
|
||||
- [x] Real-time subdomain availability check
|
||||
- [x] Vercel wildcard domain routing (`*.RentalDriveGo.com`)
|
||||
- [x] Company public site: home with offers, vehicle listing, vehicle detail, booking, confirmation
|
||||
- [x] Offers page on public site
|
||||
- [x] "Powered by RentalDriveGo" badge (removable on Pro)
|
||||
- [x] Site language + currency preference
|
||||
## Active Platform Features
|
||||
|
||||
### Payments — Rental (AmanPay + PayPal)
|
||||
- [x] Company connects their own AmanPay merchant account (Merchant ID + Secret Key)
|
||||
- [x] Company connects their own PayPal business account
|
||||
- [x] Collect payment at booking (marketplace redirect + public site)
|
||||
- [x] AmanPay widget: national card, international card, cash (3000+ points), e-wallet
|
||||
- [x] PayPal buttons component (standard checkout)
|
||||
- [x] If no payment method configured: "Contact to reserve" + pay on pickup flow
|
||||
- [x] Payment status per reservation
|
||||
- [x] Refund on cancellation (via AmanPay or PayPal API)
|
||||
- [x] Guest checkout (no renter account required)
|
||||
### Company signup and employee access
|
||||
|
||||
### Notifications — Full System
|
||||
- [x] **Email** (Resend) — all notification types, rich HTML templates
|
||||
- [x] **SMS** (Twilio) — confirmations, reminders, payment alerts
|
||||
- [x] **WhatsApp** (Twilio WhatsApp API) — confirmations, reminders (key for AR/FR)
|
||||
- [x] **In-App** (Socket.io + Redis) — real-time bell icon in dashboard + renter site
|
||||
- [x] **Push** (Firebase FCM) — renter PWA push notifications
|
||||
- [x] `Notification` table — logs every delivery attempt per channel
|
||||
- [x] `NotificationPreference` table — per-user opt-in/out per type + channel
|
||||
- [x] Preferences UI: matrix toggle (notification type × channel)
|
||||
- [x] All company events: new booking, cancelled, payment received, trial ending, maintenance due, offer expiring, new review
|
||||
- [x] All renter events: confirmed, 24h reminder, 2h reminder, vehicle ready, return reminder, cancelled, refund, new offer from saved company, review request
|
||||
- [x] Scheduled notifications via cron (reminders, trial ending, maintenance due)
|
||||
- Company signup through the dashboard sign-up flow backed by `POST /auth/company/signup`
|
||||
- Employee sign-in with local email/password auth
|
||||
- Employee password reset flow
|
||||
- Team invitation flow that creates a pending employee and sends a reset-password invite link
|
||||
- Employee roles: `OWNER`, `MANAGER`, `AGENT`
|
||||
|
||||
### Reviews
|
||||
- [x] Renter submits review (only after reservation COMPLETED)
|
||||
- [x] 1–5 stars overall + vehicle + service
|
||||
- [x] Text comment
|
||||
- [x] Company can reply to review
|
||||
- [x] Reviews shown on marketplace company profile
|
||||
- [x] Average rating on company card + brand settings
|
||||
### Multi-tenant company operations
|
||||
|
||||
### Customer CRM
|
||||
- [x] Auto-created from any booking source
|
||||
- [x] Company-scoped view (never cross-company)
|
||||
- [x] Linked to global Renter account (if booked as logged-in renter)
|
||||
- [x] Rental history, notes, flag/blacklist
|
||||
- Company-scoped vehicles, customers, reservations, offers, payments, complaints, and notifications
|
||||
- API tenant isolation through employee auth plus `companyId` scoping
|
||||
- Company profile, brand, contract settings, insurance policies, pricing rules, and accounting settings
|
||||
- Public API key generation/regeneration for company integrations
|
||||
|
||||
### Auth & Onboarding
|
||||
- [x] Company employee auth via Clerk
|
||||
- [x] Renter auth via own JWT (separate)
|
||||
- [x] Onboarding step 2: branding + subdomain
|
||||
- [x] Onboarding step 4: "Your vehicles now appear on the RentalDriveGo marketplace"
|
||||
- [x] Role system: OWNER / MANAGER / AGENT
|
||||
### Dashboard operations
|
||||
|
||||
- Dashboard home with KPI cards and booking-source analytics
|
||||
- Fleet management
|
||||
- Vehicle photo uploads
|
||||
- Vehicle publish/unpublish
|
||||
- Vehicle status management
|
||||
- Vehicle maintenance logs
|
||||
- Vehicle calendar blocks
|
||||
- Reservation list, detail, create, update, confirm, check-in, check-out, close, extend, cancel
|
||||
- Reservation pickup/dropoff inspections
|
||||
- Reservation photo uploads
|
||||
- Additional-driver approval workflow
|
||||
- Online reservation intake queue
|
||||
- Customer CRM
|
||||
- Offer management
|
||||
- Team management
|
||||
- Company billing page for rental payments
|
||||
- Contract generation/viewing
|
||||
- Reviews and complaints management
|
||||
- Notification inbox and preferences
|
||||
- Subscription management
|
||||
|
||||
### Platform Admin Panel
|
||||
- [x] `AdminUser` model: email, password (bcrypt), role (SUPER_ADMIN/ADMIN/SUPPORT/FINANCE/VIEWER), 2FA (TOTP), last login tracking
|
||||
- [x] `AdminPermission` model: per-admin resource+action overrides on top of role defaults
|
||||
- [x] `AuditLog` model: every admin action logged (action, resource, before/after JSON, IP, note)
|
||||
- [x] Admin JWT auth (separate from Clerk and renter JWT) — 8h expiry
|
||||
- [x] TOTP 2FA: setup with QR code, verify, enforce for ADMIN+ roles
|
||||
- [x] Role hierarchy: VIEWER < FINANCE < SUPPORT < ADMIN < SUPER_ADMIN
|
||||
- [x] Admin role access matrix: 17 dashboard features × 5 roles
|
||||
- [x] `admin.RentalDriveGo.com` separate app (Next.js)
|
||||
- [x] **Company management**: list, search/filter, create, edit, suspend, reactivate, delete
|
||||
- [x] **Company staff management**: list employees per company, add employee, edit (name/email/role), deactivate/reactivate, reset password
|
||||
- [x] **Impersonation**: ADMIN+ can log in as any company employee (Clerk impersonation session), with dashboard banner
|
||||
- [x] **Subscription override**: force status, change plan, add free period with auto-revert
|
||||
- [x] Manual invoice mark-as-paid
|
||||
- [x] **Renter management**: list, search, view history, block/unblock
|
||||
- [x] **Audit log UI**: searchable, filterable by admin/action/company, CSV export
|
||||
- [x] Admin user management (SUPER_ADMIN only): create/edit/deactivate admins, set roles
|
||||
- [x] Platform metrics: MRR, churn, active companies, marketplace stats, notification delivery rates
|
||||
- [x] Prisma seed creates first SUPER_ADMIN from env vars
|
||||
- [x] Employee role control matrix (OWNER/MANAGER/AGENT) with 17 feature permissions documented
|
||||
### Marketplace and public platform
|
||||
|
||||
---
|
||||
- Public marketing homepage
|
||||
- Public pricing page
|
||||
- Public features page
|
||||
- Public marketplace/explore flow
|
||||
- Explore company profile pages under `/explore/[slug]`
|
||||
- Public review submission page via review token
|
||||
- Public legal/app policy pages
|
||||
- Public platform content loaded from `/site/platform/*` API endpoints
|
||||
|
||||
### Subscription and billing
|
||||
|
||||
### Advanced Rental Operations (Phase 1)
|
||||
- SaaS trial and subscription lifecycle
|
||||
- Subscription status handling including `TRIALING`, `ACTIVE`, `PAYMENT_PENDING`, `PAST_DUE`, `SUSPENDED`, `CANCELLED`, `EXPIRED`, `PAUSED`, `UNPAID`
|
||||
- Plan pricing loaded from DB or fallback config
|
||||
- AmanPay and PayPal provider support for SaaS subscription checkout
|
||||
- Subscription invoice history
|
||||
- Platform billing accounts, billing invoices, billing events, refunds, credit notes, and tax records
|
||||
|
||||
#### Vehicle Damage Inspection
|
||||
- [x] Interactive top-down SVG damage map in check-in/check-out workflow
|
||||
- [x] 22 clickable zones (hood, roof, doors, fenders, bumpers, wheels, windshield, mirrors, interior, undercarriage)
|
||||
- [x] 5 severity levels per zone: SCRATCH, DENT, CRACK, MISSING, OTHER — each shown in distinct color
|
||||
- [x] Optional note per damaged zone
|
||||
- [x] Optional damage photos upload (Cloudinary)
|
||||
- [x] `DamageReport` model — one per check-in, one per check-out (`@@unique([reservationId, type])`)
|
||||
- [x] Fuel level and odometer recorded at each inspection
|
||||
- [x] Customer acknowledgement timestamp + name
|
||||
- [x] Damage map rendered in PDF contract (static SVG with color markers) — CHECKIN + CHECKOUT side by side
|
||||
### Rental payments
|
||||
|
||||
#### Insurance Policies
|
||||
- [x] `InsurancePolicy` model: name, type (CDW, SCDW, THEFT, THIRD_PARTY, FULL, BASIC, ROADSIDE, PERSONAL, CUSTOM)
|
||||
- [x] Three charge types: PER_DAY, PER_RENTAL (flat), PERCENTAGE_OF_RENTAL
|
||||
- [x] Required vs optional flag — required policies auto-apply, cannot be opted out
|
||||
- [x] `ReservationInsurance` junction: snapshot of policy terms locked at booking time
|
||||
- [x] `insuranceTotal` cached on Reservation for fast invoice generation
|
||||
- [x] Insurance line items in customer invoice with charge type label
|
||||
- [x] Insurance shown on rental contract
|
||||
- AmanPay payment initialization and webhook handling
|
||||
- PayPal payment initialization and capture handling
|
||||
- Manual rental payment recording
|
||||
- Reservation refund support for successful online payments
|
||||
- Payment status tracking on reservations
|
||||
|
||||
#### Second / Additional Driver
|
||||
- [x] `AdditionalDriver` model: full personal details + license info
|
||||
- [x] Charge types: FREE, PER_DAY, FLAT
|
||||
- [x] Company configures default charge in `ContractSettings`
|
||||
- [x] License validation applied to additional drivers (same 3-month rule)
|
||||
- [x] Additional driver(s) listed on contract + invoice line item (if charged)
|
||||
- [x] Employee can approve/deny flagged additional driver license
|
||||
### Notifications
|
||||
|
||||
#### Driver License Validation
|
||||
- [x] `licenseExpiry`, `licenseIssuedAt`, `licenseCountry`, `licenseNumber`, `licenseCategory` fields on Customer
|
||||
- [x] `LicenseStatus` enum: PENDING, VALID, EXPIRING, APPROVED, DENIED, EXPIRED
|
||||
- [x] **3-month rule**: if license expires within 90 days → status=EXPIRING → requires employee approval
|
||||
- [x] **Expired**: status=EXPIRED → automatic flag → employee must explicitly approve or deny before confirmation
|
||||
- [x] License validation badge on customer record (green/amber/red)
|
||||
- [x] Warning banner on reservation detail when customer has EXPIRING or EXPIRED license
|
||||
- [x] Approve / Deny buttons for OWNER and MANAGER roles only
|
||||
- [x] Approval logged: who approved, when, and note
|
||||
- Email notifications
|
||||
- SMS notifications
|
||||
- WhatsApp notifications
|
||||
- In-app notifications
|
||||
- Notification templates and per-channel preferences
|
||||
- Notification history for company users
|
||||
|
||||
#### Age-Based & Experience-Based Pricing Rules
|
||||
- [x] `PricingRule` model: configurable per company
|
||||
- [x] Conditions: AGE_LESS_THAN, AGE_GREATER_THAN, LICENSE_YEARS_LESS_THAN, LICENSE_YEARS_GREATER_THAN
|
||||
- [x] **Default rule: Under-25 surcharge** (+15% of base rental, configurable)
|
||||
- [x] **Default rule: 5+ years experience discount** (−5%, configurable)
|
||||
- [x] Adjustment types: PERCENTAGE, FLAT_PER_DAY, FLAT_TOTAL
|
||||
- [x] Rules applied to primary driver AND all additional drivers (each rule applied once even if multiple drivers qualify)
|
||||
- [x] Rules shown as line items on invoice (surcharges positive, discounts negative)
|
||||
- [x] Snapshot of applied rules stored in `Reservation.pricingRulesApplied` JSON
|
||||
### Reservation-adjacent advanced operations
|
||||
|
||||
#### Fuel / Gasoline Policy (Structured)
|
||||
- [x] `FuelPolicyType` enum: FULL_TO_FULL, FULL_TO_EMPTY, SAME_TO_SAME, PREPAID, FREE
|
||||
- [x] Multilingual policy description auto-generated from type (EN/FR/AR)
|
||||
- [x] Optional custom note added by company
|
||||
- [x] Fuel level recorded at check-in AND check-out via `DamageReport.fuelLevel`
|
||||
- [x] Policy printed on contract in customer's language
|
||||
- Insurance policy configuration
|
||||
- Reservation insurance snapshots
|
||||
- Additional-driver charging rules
|
||||
- Driver license validation and approval states
|
||||
- Pricing rules and reservation-time pricing-rule snapshots
|
||||
- Damage inspections and damage points
|
||||
- Contract settings for fuel policy, tax, numbering, and additional-driver behavior
|
||||
- Reporting and export-oriented accounting defaults
|
||||
|
||||
#### Full Invoice Price Breakdown
|
||||
- [x] Line items: base rental × days, per insurance policy, additional driver (if charged), pricing rule surcharges, pricing rule discounts, security deposit (refundable label)
|
||||
- [x] Category labels per line: RENTAL, INSURANCE, ADDITIONAL_DRIVER, SURCHARGE, DISCOUNT, DEPOSIT
|
||||
- [x] Tax/VAT line (if `ContractSettings.showTax = true`)
|
||||
- [x] Subtotal → discounts → surcharges → insurance → tax → deposit → GRAND TOTAL
|
||||
- [x] All amounts formatted in company's currency and locale
|
||||
### Admin app
|
||||
|
||||
#### Billing Period Reporting (Accountant Export)
|
||||
- [x] Reports page at `/dashboard/reports`
|
||||
- [x] Preset periods: This Week, This Month, This Year, Custom range
|
||||
- [x] Summary cards: total bookings, base revenue, discounts given, insurance collected, additional driver revenue, net collected
|
||||
- [x] Full reservations table: contract#, invoice#, customer, vehicle, plate, dates, days, base, discount, insurance, additional driver, pricing adjustments, total, payment status, source
|
||||
- [x] Export as CSV (one click) — for import into accounting software (QuickBooks, Sage, etc.)
|
||||
- [x] Report generated on-demand from DB — no pre-computed tables
|
||||
- Admin login and password reset
|
||||
- Admin dashboard
|
||||
- Company management
|
||||
- Renter management
|
||||
- Admin-user management
|
||||
- Audit-log views
|
||||
- Billing operations
|
||||
- Pricing configuration and promotions
|
||||
- Notification review
|
||||
- Marketplace/site config management
|
||||
|
||||
---
|
||||
## Present But Not Active Product Features
|
||||
|
||||
## Phase 2 — Post-Launch
|
||||
These exist partially in code or schema, but they are not active end-user product flows and should not be treated as shipped features.
|
||||
|
||||
### Fleet Enhancements
|
||||
- [ ] Maintenance log with cost tracking
|
||||
- [ ] Maintenance due alerts (date + mileage)
|
||||
- [ ] Vehicle availability calendar view in dashboard
|
||||
- Renter self-service signup
|
||||
- Renter self-service login
|
||||
- Renter email verification flow
|
||||
- Clerk-based employee auth
|
||||
- Clerk webhooks
|
||||
- Clerk invitation acceptance
|
||||
- Admin impersonation through Clerk sessions
|
||||
- Multi-currency SaaS checkout beyond `MAD`
|
||||
- Separate white-label company-site frontend app
|
||||
|
||||
### CRM Enhancements
|
||||
- [ ] Repeat customer detection + discount
|
||||
- [ ] Customer communication log (emails sent)
|
||||
- [ ] Customer lifetime value stat
|
||||
## Explicitly Out Of Scope For Current Docs
|
||||
|
||||
### Analytics
|
||||
- [ ] Revenue chart (day/week/month)
|
||||
- [ ] Fleet utilization rate + underperforming vehicles alert
|
||||
- [ ] Offer ROI analysis (revenue gained vs discounted)
|
||||
- [ ] Marketplace impressions per vehicle
|
||||
- [ ] CSV export
|
||||
The following old design ideas should not be treated as active features unless code is added later:
|
||||
|
||||
### Renter Experience
|
||||
- [ ] Renter "Continue with Google" auth
|
||||
- [ ] Favorite vehicles across companies
|
||||
- [ ] Renter profile completion % indicator
|
||||
- [ ] Booking modification request (change dates)
|
||||
- marketing blog
|
||||
- standalone about/contact page set
|
||||
- Google renter auth
|
||||
- automatic custom-domain provisioning
|
||||
- Zapier/webhook marketplace integrations beyond the current API/webhook handlers
|
||||
|
||||
### Public Site Enhancements
|
||||
- [ ] About page with company description + team photo
|
||||
- [ ] Google Maps embed for pickup location
|
||||
- [ ] "Request to book" mode (no immediate payment)
|
||||
- [ ] WhatsApp floating button
|
||||
## Notes
|
||||
|
||||
---
|
||||
|
||||
## Phase 3 — Growth
|
||||
|
||||
### Custom Domain (Pro)
|
||||
- [ ] Company enters `cars.acme.com` in /dashboard/brand
|
||||
- [ ] Vercel API auto-provisions domain
|
||||
- [ ] DNS instructions shown
|
||||
- [ ] SSL automatic
|
||||
|
||||
### Marketplace Enhancements
|
||||
- [ ] City/region pages (`/explore/cities/paris`)
|
||||
- [ ] Renter 2FA
|
||||
- [ ] Sponsored listing (pay-per-click for marketplace placement)
|
||||
- [ ] AI-powered vehicle recommendations based on renter history
|
||||
|
||||
### Advanced Offers
|
||||
- [ ] Flash sales (limited time countdown timer)
|
||||
- [ ] Loyalty program (repeat renter discounts)
|
||||
- [ ] Bundle offers (car + extras)
|
||||
|
||||
### Integrations
|
||||
- [ ] Google Calendar sync
|
||||
- [ ] QuickBooks export
|
||||
- [ ] Zapier webhooks
|
||||
|
||||
### Documents — Rental Contracts & Customer Bills
|
||||
- [x] `ContractSettings` model: per-company legal name, RC/ICE number, tax ID, custom terms, fuel/deposit/damage policies, additional clauses, signature block, footer notes, invoice tax settings, numbering prefixes
|
||||
- [x] `contractNumber` + `invoiceNumber` fields on Reservation — assigned once via atomic DB transaction, never reset
|
||||
- [x] `checkInFuelLevel` / `checkOutFuelLevel` fields for contract accuracy
|
||||
- [x] **Rental Contract PDF** — branded with company logo + name + primary color, contains: customer details, vehicle data + cover photo, rental dates/locations, financial table (rate × days − discount + deposit), all policy clauses, full T&Cs, signature lines (company + customer)
|
||||
- [x] **Customer Invoice PDF** — branded, contains: company legal info + tax ID, bill-to block, line items table (vehicle, deposit, tax if configured), totals (subtotal, discount, tax, grand total), payment details (AmanPay/PayPal transaction ID, paid date), PAID/PENDING stamp
|
||||
- [x] **Both documents support EN / FR / AR** — all labels, date formats, and currency format per company locale
|
||||
- [x] **On-demand generation** — PDFs are NEVER stored. Generated fresh from DB data on every request. No Cloudinary PDF upload.
|
||||
- [x] **Streaming PDF response** — `Content-Type: application/pdf`, `Content-Disposition: inline` (view in browser tab) or `attachment` (download)
|
||||
- [x] **Print from browser** — user opens PDF in new tab and prints via native browser print dialog
|
||||
- [x] **Email with PDF attachment** — Resend sends email with both documents as attached PDFs
|
||||
- [x] **Send contract + invoice together** — single API call generates both and attaches both to one email
|
||||
- [x] **Custom recipient email** — override the customer's email address when sending (for forwarding)
|
||||
- [x] **Renter self-download** — renter can download their own contract/invoice from `/renter/dashboard`
|
||||
- [x] **Settings preview** — preview contract/invoice PDF with real company branding + sample data before finalizing settings
|
||||
- [x] **Automatic triggers**: invoice sent on payment confirmed; contract sent on check-in completed
|
||||
|
||||
### Multi-location per company
|
||||
- [ ] Multiple pickup/return locations
|
||||
- [ ] Location-aware vehicle assignment
|
||||
- [ ] Distance filter on marketplace
|
||||
|
||||
---
|
||||
|
||||
## Out of Scope
|
||||
|
||||
- GPS / telematics
|
||||
- Driver license OCR / ID verification
|
||||
- Insurance products
|
||||
- Fuel management
|
||||
- Peer-to-peer (this is B2B SaaS + B2C marketplace, not P2P)
|
||||
- Native mobile app (PWA covers push notifications)
|
||||
- The database still contains legacy fields such as `Employee.clerkUserId`, but those fields are no longer evidence of active Clerk integration.
|
||||
- Some renter-facing `/renter/*` pages exist in the marketplace app, but the auth entrypoints they depend on are not active. They are therefore not listed as active product scope here.
|
||||
|
||||
+108
-149
@@ -1,182 +1,141 @@
|
||||
# Staff Management — Integration Guide
|
||||
# Team Management Integration — Current Implementation
|
||||
|
||||
## 1. Register the routes in your Express app
|
||||
This document describes the current team-management integration in the live codebase.
|
||||
|
||||
```ts
|
||||
// apps/api/src/index.ts (or server.ts)
|
||||
Source of truth:
|
||||
|
||||
import express from 'express'
|
||||
import teamRouter from './routes/team'
|
||||
import webhookRouter from './routes/webhooks'
|
||||
- `apps/api/src/modules/team/team.routes.ts`
|
||||
- `apps/api/src/services/teamService.ts`
|
||||
- `apps/dashboard/src/hooks/useTeam.ts`
|
||||
- `apps/dashboard/src/app/(dashboard)/team/page.tsx`
|
||||
- `apps/dashboard/src/app/reset-password/*`
|
||||
|
||||
const app = express()
|
||||
## Current Model
|
||||
|
||||
// Webhook MUST use raw body — register BEFORE express.json()
|
||||
app.use(
|
||||
'/webhooks',
|
||||
express.raw({ type: 'application/json' }),
|
||||
webhookRouter
|
||||
)
|
||||
Team management is local to RentalDriveGo. It no longer depends on Clerk.
|
||||
|
||||
// All other routes use JSON
|
||||
app.use(express.json())
|
||||
The live flow is:
|
||||
|
||||
// Team routes — mounted under /api/v1/team (matches api-routes.md)
|
||||
app.use('/api/v1/team', teamRouter)
|
||||
1. an owner invites a staff member from the dashboard
|
||||
2. the API creates a pending `Employee`
|
||||
3. the API generates a password-reset token
|
||||
4. an email is sent with a reset-password link
|
||||
5. the invited employee sets a password through the dashboard reset-password page
|
||||
6. the employee can then sign in through the standard employee login flow
|
||||
|
||||
There is no webhook step in the active implementation.
|
||||
|
||||
## Backend Integration
|
||||
|
||||
The team router is already mounted by the main API app in `apps/api/src/app.ts` under:
|
||||
|
||||
- `/api/v1/team`
|
||||
|
||||
The public webhook placeholder under `/api/v1/webhooks/clerk` is intentionally disabled and should not be used for team onboarding.
|
||||
|
||||
### Team endpoints
|
||||
|
||||
- `GET /api/v1/team`
|
||||
- `GET /api/v1/team/stats`
|
||||
- `POST /api/v1/team/invite`
|
||||
- `PATCH /api/v1/team/:id/role`
|
||||
- `POST /api/v1/team/:id/deactivate`
|
||||
- `POST /api/v1/team/:id/reactivate`
|
||||
- `DELETE /api/v1/team/:id`
|
||||
|
||||
### Auth and authorization
|
||||
|
||||
These routes are protected by:
|
||||
|
||||
- employee JWT auth
|
||||
- tenant resolution
|
||||
- subscription guard
|
||||
- role checks inside the router and service
|
||||
|
||||
Important rules in the current implementation:
|
||||
|
||||
- only the `OWNER` can invite, change roles, deactivate/reactivate, or remove team members
|
||||
- invited team members cannot be created with the `OWNER` role
|
||||
- the account owner cannot be deactivated or removed through team endpoints
|
||||
|
||||
## Invite Email Flow
|
||||
|
||||
Invitations are generated in `apps/api/src/services/teamService.ts`.
|
||||
|
||||
Current behavior:
|
||||
|
||||
- a pending employee row is created
|
||||
- `passwordResetToken` and `passwordResetExpiresAt` are populated
|
||||
- the invite email links directly to the dashboard reset-password page
|
||||
|
||||
Expected URL shape:
|
||||
|
||||
```text
|
||||
{DASHBOARD_URL}/reset-password?token=...
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. Install the Clerk webhook (Clerk Dashboard → Webhooks)
|
||||
|
||||
1. Go to **clerk.com** → your app → **Webhooks** → **Add endpoint**
|
||||
2. URL: `https://api.RentalDriveGo.com/webhooks/clerk`
|
||||
3. Events to subscribe: `user.created`
|
||||
4. Copy the **Signing Secret** and add to `.env`:
|
||||
Relevant environment variables:
|
||||
|
||||
```env
|
||||
CLERK_WEBHOOK_SECRET=whsec_xxxxxxxxxxxxxxxxxxxxx
|
||||
DASHBOARD_URL=https://your-host/dashboard
|
||||
NEXT_PUBLIC_DASHBOARD_URL=https://your-host/dashboard
|
||||
NEXT_PUBLIC_API_URL=https://your-host/api/v1
|
||||
```
|
||||
|
||||
5. Install `svix` for signature verification:
|
||||
## Dashboard Integration
|
||||
|
||||
```bash
|
||||
npm install svix
|
||||
```
|
||||
The team UI lives here:
|
||||
|
||||
---
|
||||
- `apps/dashboard/src/app/(dashboard)/team/page.tsx`
|
||||
- `apps/dashboard/src/hooks/useTeam.ts`
|
||||
- `apps/dashboard/src/components/team/InviteModal.tsx`
|
||||
- `apps/dashboard/src/components/team/EditMemberModal.tsx`
|
||||
- `apps/dashboard/src/components/team/PermissionsMatrix.tsx`
|
||||
|
||||
## 3. Environment variables needed
|
||||
The dashboard consumes the team API directly through `apiFetch(...)`.
|
||||
|
||||
```env
|
||||
# Clerk
|
||||
CLERK_SECRET_KEY=sk_live_...
|
||||
CLERK_WEBHOOK_SECRET=whsec_...
|
||||
### Dashboard page behavior
|
||||
|
||||
# Frontend
|
||||
NEXT_PUBLIC_API_URL=https://api.RentalDriveGo.com/api/v1
|
||||
- loads members from `/team`
|
||||
- loads summary counts from `/team/stats`
|
||||
- opens invite/edit flows through local modals
|
||||
- updates local state after invite, role change, deactivate/reactivate, and removal
|
||||
|
||||
# Dashboard URL (used in invite redirect)
|
||||
DASHBOARD_URL=https://app.RentalDriveGo.com
|
||||
```
|
||||
## Password Setup / Invite Acceptance
|
||||
|
||||
---
|
||||
The active invite-acceptance mechanism is the dashboard reset-password flow, not a Clerk callback flow.
|
||||
|
||||
## 4. Add the page to the Next.js dashboard app
|
||||
Relevant pages:
|
||||
|
||||
```
|
||||
apps/dashboard/
|
||||
├── app/
|
||||
│ └── dashboard/
|
||||
│ └── team/
|
||||
│ └── page.tsx ← copy frontend/pages/team.tsx here
|
||||
├── components/
|
||||
│ └── team/
|
||||
│ ├── InviteModal.tsx ← copy from frontend/components/team/
|
||||
│ ├── EditMemberModal.tsx
|
||||
│ └── PermissionsMatrix.tsx
|
||||
└── hooks/
|
||||
└── useTeam.ts ← copy from frontend/hooks/
|
||||
```
|
||||
- `apps/dashboard/src/app/reset-password/page.tsx`
|
||||
- `apps/dashboard/src/app/reset-password/ResetPasswordPageClient.tsx`
|
||||
|
||||
Add the route to your dashboard sidebar navigation:
|
||||
This means:
|
||||
|
||||
```tsx
|
||||
// components/Sidebar.tsx — add to nav items
|
||||
{ href: '/dashboard/team', label: 'Team', icon: UsersIcon }
|
||||
```
|
||||
- there is no `__clerk_ticket`
|
||||
- there is no Clerk redirect callback
|
||||
- there is no employee activation webhook
|
||||
|
||||
---
|
||||
Invite completion is simply password setup using the token created by the API.
|
||||
|
||||
## 5. requireRole middleware placement
|
||||
## Request Typing
|
||||
|
||||
The `requireRole` middleware is already applied inside the team router.
|
||||
Only expose it if you need it elsewhere (e.g., `PATCH /pricing-rules` → `requireRole('MANAGER')`):
|
||||
The request object is extended in the API so team routes can rely on:
|
||||
|
||||
```ts
|
||||
import { requireRole } from './middleware/requireRole'
|
||||
- `req.employee`
|
||||
- `req.company`
|
||||
- `req.companyId`
|
||||
|
||||
// Example: license approval endpoint (OWNER or MANAGER only)
|
||||
router.post(
|
||||
'/customers/:id/approve-license',
|
||||
requireRole('MANAGER'),
|
||||
approveLicenseHandler
|
||||
)
|
||||
```
|
||||
Those values are attached by the company auth and tenant middleware before team handlers run.
|
||||
|
||||
---
|
||||
## What Was Removed
|
||||
|
||||
## 6. Invite acceptance flow (frontend)
|
||||
These older integration assumptions are no longer valid:
|
||||
|
||||
When a user clicks the magic link in their invitation email, Clerk redirects them to:
|
||||
- Clerk webhooks
|
||||
- Clerk invitation acceptance
|
||||
- Clerk `user.created` activation flow
|
||||
- `/webhooks/clerk` as a required part of team onboarding
|
||||
- `@clerk/nextjs` integration in the dashboard team flow
|
||||
|
||||
```
|
||||
https://app.RentalDriveGo.com/onboarding/accept-invite?__clerk_ticket=...
|
||||
```
|
||||
|
||||
Create this page in your Next.js app:
|
||||
|
||||
```tsx
|
||||
// app/onboarding/accept-invite/page.tsx
|
||||
'use client'
|
||||
import { useEffect } from 'react'
|
||||
import { useClerk } from '@clerk/nextjs'
|
||||
import { useRouter } from 'next/navigation'
|
||||
|
||||
export default function AcceptInvite() {
|
||||
const { handleRedirectCallback } = useClerk()
|
||||
const router = useRouter()
|
||||
|
||||
useEffect(() => {
|
||||
handleRedirectCallback({}).then(() => {
|
||||
// Clerk webhook fires user.created → activates Employee record in DB
|
||||
// Redirect to dashboard after a short delay
|
||||
setTimeout(() => router.replace('/dashboard'), 1000)
|
||||
})
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<div className="flex items-center justify-center min-h-screen">
|
||||
<p className="text-sm text-zinc-500">Setting up your account…</p>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. Prisma type extensions (optional, for `req.employee`)
|
||||
|
||||
To get TypeScript to recognize `req.companyId` and `req.employee` on Express requests,
|
||||
add a type declaration file:
|
||||
|
||||
```ts
|
||||
// types/express.d.ts
|
||||
import { Employee, Company } from '@prisma/client'
|
||||
|
||||
declare global {
|
||||
namespace Express {
|
||||
interface Request {
|
||||
companyId: string
|
||||
company: Company
|
||||
employee: Employee
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 8. Summary of files delivered
|
||||
|
||||
| File | Purpose |
|
||||
|------|---------|
|
||||
| `backend/services/teamService.ts` | All business logic — invite, update role, deactivate, remove, Clerk webhook handler |
|
||||
| `backend/routes/team.ts` | Express router — all `/team` endpoints |
|
||||
| `backend/routes/webhooks.ts` | Clerk webhook listener — activates employee on invite acceptance |
|
||||
| `backend/middleware/requireRole.ts` | Role-rank guard middleware — reusable across all routers |
|
||||
| `frontend/hooks/useTeam.ts` | React data hook — fetch, invite, updateRole, deactivate, reactivate, remove |
|
||||
| `frontend/pages/team.tsx` | Full `/dashboard/team` Next.js page |
|
||||
| `frontend/components/team/InviteModal.tsx` | Invite new member modal with role picker |
|
||||
| `frontend/components/team/EditMemberModal.tsx` | Edit role, deactivate, reactivate, remove modal with confirmation step |
|
||||
| `frontend/components/team/PermissionsMatrix.tsx` | Static role × feature permissions table |
|
||||
If this functionality returns in the future, it should be documented as a new integration rather than assumed from this file.
|
||||
|
||||
@@ -0,0 +1,739 @@
|
||||
# Notification Language and Localization Policy
|
||||
|
||||
## 1. Objective
|
||||
|
||||
The notification system must send all customer-facing notifications in the language selected by the user during account creation.
|
||||
|
||||
Supported initial languages:
|
||||
|
||||
```text
|
||||
EN
|
||||
AR
|
||||
FR
|
||||
```
|
||||
|
||||
Language mapping:
|
||||
|
||||
| User Selection | Locale Code | Notification Language |
|
||||
|---|---|---|
|
||||
| `EN` | `en` | English |
|
||||
| `AR` | `ar` | Arabic |
|
||||
| `FR` | `fr` | French |
|
||||
|
||||
This applies to account creation, workspace creation, invitations, trial notifications, subscription status notifications, billing notifications, invoice notifications, payment failure notifications, refunds, credits, in-app notifications, email notifications, SMS notifications if enabled, and customer-facing webhook labels if applicable.
|
||||
|
||||
Internal admin alerts may use the internal team’s default language unless configured otherwise.
|
||||
|
||||
---
|
||||
|
||||
## 2. Source of Truth for Notification Language
|
||||
|
||||
The user’s selected language at account creation must be stored and used as the primary language preference.
|
||||
|
||||
### Required User Field
|
||||
|
||||
```sql
|
||||
ALTER TABLE users
|
||||
ADD COLUMN preferred_language TEXT NOT NULL DEFAULT 'en';
|
||||
```
|
||||
|
||||
Allowed values:
|
||||
|
||||
```text
|
||||
en
|
||||
ar
|
||||
fr
|
||||
```
|
||||
|
||||
### Account Creation Requirement
|
||||
|
||||
During account creation, the user must select or confirm a language.
|
||||
|
||||
Example payload:
|
||||
|
||||
```json
|
||||
{
|
||||
"email": "user@example.com",
|
||||
"name": "Example User",
|
||||
"preferred_language": "ar"
|
||||
}
|
||||
```
|
||||
|
||||
Rules:
|
||||
|
||||
- `preferred_language` is required at account creation.
|
||||
- If not provided, default to `en`.
|
||||
- The selected language must be saved on the user profile.
|
||||
- The selected language must be used for all customer-facing notifications.
|
||||
- Users may update their preferred language later from account settings.
|
||||
- Updating the language affects future notifications only, not historical notifications.
|
||||
|
||||
---
|
||||
|
||||
## 3. Language Resolution Policy
|
||||
|
||||
When sending a notification, resolve the language in this order:
|
||||
|
||||
```text
|
||||
recipient user preferred_language
|
||||
workspace default_language
|
||||
billing account preferred_language
|
||||
organization default_language
|
||||
system default_language
|
||||
```
|
||||
|
||||
Default:
|
||||
|
||||
```text
|
||||
en
|
||||
```
|
||||
|
||||
Example:
|
||||
|
||||
```text
|
||||
User preferred_language = ar
|
||||
Workspace default_language = fr
|
||||
System default_language = en
|
||||
|
||||
Selected notification language = ar
|
||||
```
|
||||
|
||||
The recipient’s own language wins. Not the workspace. Not the billing account. Not a hardcoded backend default.
|
||||
|
||||
---
|
||||
|
||||
## 4. Workspace and Billing Language Fields
|
||||
|
||||
In addition to the user’s language, store default language on workspace and billing account.
|
||||
|
||||
### Workspace Field
|
||||
|
||||
```sql
|
||||
ALTER TABLE workspaces
|
||||
ADD COLUMN default_language TEXT NOT NULL DEFAULT 'en';
|
||||
```
|
||||
|
||||
### Billing Account Field
|
||||
|
||||
```sql
|
||||
ALTER TABLE billing_accounts
|
||||
ADD COLUMN preferred_language TEXT NOT NULL DEFAULT 'en';
|
||||
```
|
||||
|
||||
Purpose:
|
||||
|
||||
| Field | Purpose |
|
||||
|---|---|
|
||||
| `users.preferred_language` | Primary language for direct user notifications |
|
||||
| `workspaces.default_language` | Fallback for workspace-level notifications |
|
||||
| `billing_accounts.preferred_language` | Fallback for finance/billing notifications |
|
||||
| `system.default_language` | Final fallback |
|
||||
|
||||
---
|
||||
|
||||
## 5. Template Localization Policy
|
||||
|
||||
Every customer-facing notification template must exist in all supported languages.
|
||||
|
||||
Required locales:
|
||||
|
||||
```text
|
||||
en
|
||||
ar
|
||||
fr
|
||||
```
|
||||
|
||||
### Template Table Update
|
||||
|
||||
```sql
|
||||
CREATE TABLE notification_templates (
|
||||
id UUID PRIMARY KEY,
|
||||
|
||||
template_key TEXT NOT NULL,
|
||||
category TEXT NOT NULL,
|
||||
channel TEXT NOT NULL,
|
||||
|
||||
locale TEXT NOT NULL,
|
||||
|
||||
subject TEXT,
|
||||
body TEXT NOT NULL,
|
||||
|
||||
required_variables JSONB,
|
||||
optional_variables JSONB,
|
||||
|
||||
version INTEGER NOT NULL DEFAULT 1,
|
||||
is_active BOOLEAN NOT NULL DEFAULT TRUE,
|
||||
|
||||
created_at TIMESTAMP NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMP NOT NULL DEFAULT NOW(),
|
||||
|
||||
UNIQUE(template_key, channel, locale, version)
|
||||
);
|
||||
```
|
||||
|
||||
Preferred template structure:
|
||||
|
||||
```text
|
||||
template_key = account.created.email
|
||||
locale = en
|
||||
|
||||
template_key = account.created.email
|
||||
locale = ar
|
||||
|
||||
template_key = account.created.email
|
||||
locale = fr
|
||||
```
|
||||
|
||||
Do not encode the locale into the template key if the table already has a `locale` column. Duplicating state is how small mistakes become expensive folklore.
|
||||
|
||||
---
|
||||
|
||||
## 6. Template Lookup Policy
|
||||
|
||||
When rendering a notification:
|
||||
|
||||
```text
|
||||
1. Resolve recipient language.
|
||||
2. Find active template for template_key + channel + resolved locale.
|
||||
3. If unavailable, fall back to English.
|
||||
4. If English template is unavailable, fail notification creation and alert admins.
|
||||
```
|
||||
|
||||
Lookup flow:
|
||||
|
||||
```text
|
||||
event received
|
||||
↓
|
||||
resolve recipient
|
||||
↓
|
||||
resolve recipient language
|
||||
↓
|
||||
find localized template
|
||||
↓
|
||||
render template
|
||||
↓
|
||||
send notification
|
||||
```
|
||||
|
||||
Fallback rules:
|
||||
|
||||
| Condition | Action |
|
||||
|---|---|
|
||||
| `ar` template exists | Send Arabic notification |
|
||||
| `fr` template exists | Send French notification |
|
||||
| Requested locale missing | Fall back to English |
|
||||
| English fallback missing | Fail and alert internal admins |
|
||||
| Template variables missing | Fail and alert internal admins |
|
||||
|
||||
Fallback to English is allowed only as a safety net. It should be monitored as a product defect.
|
||||
|
||||
---
|
||||
|
||||
## 7. Arabic Language Requirements
|
||||
|
||||
Arabic notifications must support right-to-left rendering.
|
||||
|
||||
For locale:
|
||||
|
||||
```text
|
||||
ar
|
||||
```
|
||||
|
||||
Apply:
|
||||
|
||||
```html
|
||||
<html lang="ar" dir="rtl">
|
||||
```
|
||||
|
||||
or for partial rendering:
|
||||
|
||||
```html
|
||||
<div lang="ar" dir="rtl">
|
||||
...
|
||||
</div>
|
||||
```
|
||||
|
||||
Arabic requirements:
|
||||
|
||||
- Use RTL layout for email and in-app notifications.
|
||||
- Align Arabic text to the right.
|
||||
- Use Arabic translations for all customer-facing labels.
|
||||
- Use localized date formatting where possible.
|
||||
- Keep invoice numbers, amounts, and technical IDs readable.
|
||||
- Avoid mixing English UI labels into Arabic notifications unless they are brand or product names.
|
||||
- Test Arabic templates separately.
|
||||
|
||||
---
|
||||
|
||||
## 8. French Language Requirements
|
||||
|
||||
For locale:
|
||||
|
||||
```text
|
||||
fr
|
||||
```
|
||||
|
||||
Apply:
|
||||
|
||||
```html
|
||||
<html lang="fr">
|
||||
```
|
||||
|
||||
French requirements:
|
||||
|
||||
- Use French templates for all customer-facing messages.
|
||||
- Use localized date formatting.
|
||||
- Use localized money formatting where appropriate.
|
||||
- Avoid partial English/French mixed messages.
|
||||
- Keep plan names and product names unchanged unless officially translated.
|
||||
|
||||
---
|
||||
|
||||
## 9. English Language Requirements
|
||||
|
||||
For locale:
|
||||
|
||||
```text
|
||||
en
|
||||
```
|
||||
|
||||
Apply:
|
||||
|
||||
```html
|
||||
<html lang="en">
|
||||
```
|
||||
|
||||
English is the default fallback language.
|
||||
|
||||
---
|
||||
|
||||
## 10. Localized Formatting Policy
|
||||
|
||||
Notifications should localize:
|
||||
|
||||
- Subject lines
|
||||
- Body text
|
||||
- Button labels
|
||||
- Status labels
|
||||
- Dates
|
||||
- Currency display
|
||||
- Invoice labels
|
||||
- Subscription status labels
|
||||
- Error messages
|
||||
- Call-to-action text
|
||||
|
||||
Date formatting examples:
|
||||
|
||||
| Locale | Display |
|
||||
|---|---|
|
||||
| `en` | June 15, 2026 |
|
||||
| `fr` | 15 juin 2026 |
|
||||
| `ar` | ١٥ يونيو ٢٠٢٦ |
|
||||
|
||||
Currency formatting should respect both billing currency and locale.
|
||||
|
||||
Example for USD:
|
||||
|
||||
| Locale | Display |
|
||||
|---|---|
|
||||
| `en` | $1,200.00 |
|
||||
| `fr` | 1 200,00 $US |
|
||||
| `ar` | ١٬٢٠٠٫٠٠ US$ |
|
||||
|
||||
Do not store localized amounts as the source of truth. Store money as integer minor units and format at render time.
|
||||
|
||||
---
|
||||
|
||||
## 11. Localized Subscription Status Labels
|
||||
|
||||
### English
|
||||
|
||||
| Internal Status | English Label |
|
||||
|---|---|
|
||||
| `trialing` | Trial active |
|
||||
| `active` | Active |
|
||||
| `payment_pending` | Payment pending |
|
||||
| `past_due` | Payment overdue |
|
||||
| `suspended` | Suspended |
|
||||
| `canceled` | Canceled |
|
||||
| `expired` | Expired |
|
||||
|
||||
### French
|
||||
|
||||
| Internal Status | French Label |
|
||||
|---|---|
|
||||
| `trialing` | Essai actif |
|
||||
| `active` | Actif |
|
||||
| `payment_pending` | Paiement en attente |
|
||||
| `past_due` | Paiement en retard |
|
||||
| `suspended` | Suspendu |
|
||||
| `canceled` | Annulé |
|
||||
| `expired` | Expiré |
|
||||
|
||||
### Arabic
|
||||
|
||||
| Internal Status | Arabic Label |
|
||||
|---|---|
|
||||
| `trialing` | الفترة التجريبية نشطة |
|
||||
| `active` | نشط |
|
||||
| `payment_pending` | الدفع قيد الانتظار |
|
||||
| `past_due` | الدفع متأخر |
|
||||
| `suspended` | معلّق |
|
||||
| `canceled` | ملغى |
|
||||
| `expired` | منتهي |
|
||||
|
||||
---
|
||||
|
||||
## 12. Localized Invoice Labels
|
||||
|
||||
### English
|
||||
|
||||
| Internal Invoice Status | English Label |
|
||||
|---|---|
|
||||
| `draft` | Preparing |
|
||||
| `open` | Due |
|
||||
| `payment_pending` | Payment processing |
|
||||
| `paid` | Paid |
|
||||
| `partially_paid` | Partially paid |
|
||||
| `past_due` | Past due |
|
||||
| `void` | Canceled |
|
||||
| `uncollectible` | Contact support |
|
||||
| `refunded` | Refunded |
|
||||
| `partially_refunded` | Partially refunded |
|
||||
|
||||
### French
|
||||
|
||||
| Internal Invoice Status | French Label |
|
||||
|---|---|
|
||||
| `draft` | En préparation |
|
||||
| `open` | À payer |
|
||||
| `payment_pending` | Paiement en cours |
|
||||
| `paid` | Payée |
|
||||
| `partially_paid` | Partiellement payée |
|
||||
| `past_due` | En retard |
|
||||
| `void` | Annulée |
|
||||
| `uncollectible` | Contacter le support |
|
||||
| `refunded` | Remboursée |
|
||||
| `partially_refunded` | Partiellement remboursée |
|
||||
|
||||
### Arabic
|
||||
|
||||
| Internal Invoice Status | Arabic Label |
|
||||
|---|---|
|
||||
| `draft` | قيد الإعداد |
|
||||
| `open` | مستحقة الدفع |
|
||||
| `payment_pending` | الدفع قيد المعالجة |
|
||||
| `paid` | مدفوعة |
|
||||
| `partially_paid` | مدفوعة جزئياً |
|
||||
| `past_due` | متأخرة |
|
||||
| `void` | ملغاة |
|
||||
| `uncollectible` | تواصل مع الدعم |
|
||||
| `refunded` | مستردة |
|
||||
| `partially_refunded` | مستردة جزئياً |
|
||||
|
||||
---
|
||||
|
||||
## 13. Notification Creation Logic
|
||||
|
||||
When an event creates a notification, include the resolved locale.
|
||||
|
||||
### Notification Record Update
|
||||
|
||||
```sql
|
||||
ALTER TABLE notifications
|
||||
ADD COLUMN locale TEXT NOT NULL DEFAULT 'en';
|
||||
```
|
||||
|
||||
Example notification record:
|
||||
|
||||
```json
|
||||
{
|
||||
"event_type": "invoice.finalized",
|
||||
"recipient_email": "finance@example.com",
|
||||
"recipient_role": "billing_owner",
|
||||
"channel": "email",
|
||||
"template_key": "invoice.finalized.email",
|
||||
"locale": "fr",
|
||||
"status": "pending"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 14. Notification Rendering Pseudocode
|
||||
|
||||
```ts
|
||||
type SupportedLocale = "en" | "ar" | "fr";
|
||||
|
||||
function resolveNotificationLocale(recipient, workspace, billingAccount): SupportedLocale {
|
||||
return (
|
||||
recipient.preferredLanguage ||
|
||||
workspace.defaultLanguage ||
|
||||
billingAccount.preferredLanguage ||
|
||||
"en"
|
||||
);
|
||||
}
|
||||
|
||||
async function createNotification(event, recipient) {
|
||||
const locale = resolveNotificationLocale(
|
||||
recipient.user,
|
||||
event.workspace,
|
||||
event.billingAccount
|
||||
);
|
||||
|
||||
const template = await findTemplate({
|
||||
templateKey: event.templateKey,
|
||||
channel: event.channel,
|
||||
locale
|
||||
});
|
||||
|
||||
const fallbackTemplate = await findTemplate({
|
||||
templateKey: event.templateKey,
|
||||
channel: event.channel,
|
||||
locale: "en"
|
||||
});
|
||||
|
||||
const selectedTemplate = template || fallbackTemplate;
|
||||
|
||||
if (!selectedTemplate) {
|
||||
throw new Error("Missing notification template");
|
||||
}
|
||||
|
||||
return {
|
||||
eventType: event.type,
|
||||
recipientEmail: recipient.email,
|
||||
channel: event.channel,
|
||||
templateKey: event.templateKey,
|
||||
locale: selectedTemplate.locale,
|
||||
renderedContent: renderTemplate(selectedTemplate, event.payload)
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 15. Account Creation Flow Update
|
||||
|
||||
During account creation:
|
||||
|
||||
```text
|
||||
user selects language
|
||||
↓
|
||||
system stores preferred_language
|
||||
↓
|
||||
account.created event emitted
|
||||
↓
|
||||
notification system resolves preferred_language
|
||||
↓
|
||||
localized welcome notification sent
|
||||
```
|
||||
|
||||
### Account Creation API Update
|
||||
|
||||
```http
|
||||
POST /accounts
|
||||
```
|
||||
|
||||
Request:
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "Example User",
|
||||
"email": "user@example.com",
|
||||
"password": "secure_password",
|
||||
"preferred_language": "ar"
|
||||
}
|
||||
```
|
||||
|
||||
Response:
|
||||
|
||||
```json
|
||||
{
|
||||
"account_id": "acct_123",
|
||||
"user_id": "user_123",
|
||||
"preferred_language": "ar"
|
||||
}
|
||||
```
|
||||
|
||||
Validation:
|
||||
|
||||
```text
|
||||
preferred_language must be one of: en, ar, fr
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 16. Notification Preference Update
|
||||
|
||||
Language preference should be separate from notification category preferences.
|
||||
|
||||
Example user settings:
|
||||
|
||||
```text
|
||||
preferred_language = ar
|
||||
invoice.email.enabled = true
|
||||
billing.email.enabled = true
|
||||
onboarding.email.enabled = false
|
||||
```
|
||||
|
||||
Rules:
|
||||
|
||||
- Language controls the language of notifications.
|
||||
- Preferences control whether allowed notifications are sent.
|
||||
- Critical billing, invoice, payment, and security notifications still follow mandatory delivery rules.
|
||||
- Changing language does not unsubscribe the user from notifications.
|
||||
- Changing language affects future notifications only.
|
||||
|
||||
---
|
||||
|
||||
## 17. Testing Requirements
|
||||
|
||||
### Unit Tests
|
||||
|
||||
Test:
|
||||
|
||||
```text
|
||||
User with preferred_language=en receives English template
|
||||
User with preferred_language=ar receives Arabic template
|
||||
User with preferred_language=fr receives French template
|
||||
Missing Arabic template falls back to English
|
||||
Missing French template falls back to English
|
||||
Missing English fallback fails notification creation
|
||||
Arabic email renders with dir="rtl"
|
||||
French email renders with lang="fr"
|
||||
English email renders with lang="en"
|
||||
```
|
||||
|
||||
### Integration Tests
|
||||
|
||||
Test:
|
||||
|
||||
```text
|
||||
Account created with EN → welcome email sent in English
|
||||
Account created with AR → welcome email sent in Arabic
|
||||
Account created with FR → welcome email sent in French
|
||||
Invoice finalized for AR billing owner → Arabic invoice notification
|
||||
Payment failed for FR billing owner → French payment failure notification
|
||||
Subscription suspended for EN admin → English suspension notification
|
||||
```
|
||||
|
||||
### End-to-End Tests
|
||||
|
||||
Test:
|
||||
|
||||
```text
|
||||
User creates account and selects Arabic
|
||||
↓
|
||||
Arabic welcome email is sent
|
||||
↓
|
||||
User starts trial
|
||||
↓
|
||||
Arabic trial notification is sent
|
||||
↓
|
||||
Invoice is generated
|
||||
↓
|
||||
Arabic invoice notification is sent
|
||||
↓
|
||||
Payment fails
|
||||
↓
|
||||
Arabic payment failure notification is sent
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 18. Monitoring Requirements
|
||||
|
||||
Track localization metrics:
|
||||
|
||||
```text
|
||||
notifications_sent_by_locale
|
||||
notifications_failed_by_locale
|
||||
template_missing_by_locale
|
||||
fallback_to_english_count
|
||||
rtl_rendering_test_failures
|
||||
language_preference_update_count
|
||||
```
|
||||
|
||||
Alert on:
|
||||
|
||||
```text
|
||||
Missing Arabic template
|
||||
Missing French template
|
||||
Fallback-to-English spike
|
||||
Arabic rendering failure
|
||||
Localized template variable error
|
||||
```
|
||||
|
||||
Fallback-to-English should be treated as a defect, not a harmless convenience.
|
||||
|
||||
---
|
||||
|
||||
## 19. Updated Notification Localization Section
|
||||
|
||||
Replace the earlier notification localization section with this stricter version.
|
||||
|
||||
### Notification Localization
|
||||
|
||||
Notification templates must support the user’s selected account language.
|
||||
|
||||
Supported locales:
|
||||
|
||||
```text
|
||||
en
|
||||
ar
|
||||
fr
|
||||
```
|
||||
|
||||
Template lookup order:
|
||||
|
||||
```text
|
||||
recipient user preferred_language
|
||||
workspace default_language
|
||||
billing account preferred_language
|
||||
system default_language
|
||||
```
|
||||
|
||||
The language selected during account creation is the primary source of truth for user-facing notifications.
|
||||
|
||||
Arabic notifications must render in RTL mode.
|
||||
|
||||
French and English notifications must use localized date, currency, and status labels.
|
||||
|
||||
Fallback to English is allowed only when a localized template is missing, and every fallback must create an internal alert.
|
||||
|
||||
---
|
||||
|
||||
## 20. Final Localization Policy
|
||||
|
||||
Use this as the baseline rule:
|
||||
|
||||
```text
|
||||
The language selected by the user during account creation is the primary language for all customer-facing notifications.
|
||||
Supported languages are English, Arabic, and French.
|
||||
English uses locale en.
|
||||
Arabic uses locale ar and must render right-to-left.
|
||||
French uses locale fr.
|
||||
Each customer-facing notification template must exist in en, ar, and fr.
|
||||
If a localized template is missing, the system may fall back to English, but must log and alert the missing localization.
|
||||
Billing, invoice, subscription, and account notifications must use the recipient’s preferred language.
|
||||
Language preference is separate from notification opt-in preferences.
|
||||
Changing language affects future notifications only.
|
||||
Internal admin alerts may use the internal default language unless configured otherwise.
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 21. Definition of Done
|
||||
|
||||
The notification localization system is complete when:
|
||||
|
||||
- Account creation stores `preferred_language`.
|
||||
- Supported languages are limited to `en`, `ar`, and `fr`.
|
||||
- Notification records store the resolved locale.
|
||||
- Email templates exist in English, Arabic, and French.
|
||||
- In-app notification templates exist in English, Arabic, and French.
|
||||
- Arabic templates render correctly in RTL.
|
||||
- French templates render with French labels and formatting.
|
||||
- Dates and currency are localized during rendering.
|
||||
- Missing localized templates fall back to English and create alerts.
|
||||
- Tests cover account, subscription, billing, invoice, and payment notifications for all supported languages.
|
||||
+179
-316
@@ -1,316 +1,179 @@
|
||||
# Pages Specification — RentalDriveGo
|
||||
|
||||
---
|
||||
|
||||
## LAYER 1A — Marketing Site (RentalDriveGo.com)
|
||||
|
||||
### `/` — Home
|
||||
Sections:
|
||||
1. Hero — "Run your rental car business like a pro" + CTA
|
||||
2. Two-column value: "For rental companies" / "For renters"
|
||||
3. Social proof bar
|
||||
4. Pain points (for companies)
|
||||
5. Features grid — fleet, photos, offers, site, CRM, analytics
|
||||
6. **Marketplace callout** — "Your vehicle photos appear on the RentalDriveGo global marketplace automatically"
|
||||
7. How it works — 3 steps
|
||||
8. Pricing teaser (AmanPay / PayPal accepted badges shown)
|
||||
9. Testimonials
|
||||
10. Final CTA
|
||||
|
||||
### `/pricing` — Pricing
|
||||
- Three plan cards (Starter / Growth / Pro) with monthly/annual toggle
|
||||
- **Payment method icons:** AmanPay logo + PayPal logo
|
||||
- "Pay securely with AmanPay (cards, cash, e-wallet) or PayPal"
|
||||
- Supported currencies: MAD, USD, EUR
|
||||
- FAQ section
|
||||
|
||||
### `/features`, `/about`, `/contact`, `/blog` — standard
|
||||
|
||||
---
|
||||
|
||||
## LAYER 1B — Global Marketplace (RentalDriveGo.com/explore)
|
||||
|
||||
> **Design principle:** This is a DISCOVERY layer. No booking form, no payment form.
|
||||
> Every CTA leads to the company's own site.
|
||||
|
||||
### `/explore` — Marketplace Home
|
||||
|
||||
Sections:
|
||||
|
||||
1. **Search Hero**
|
||||
- Headline: "Find your next rental — from trusted local companies"
|
||||
- Search bar: [City / Location] [Pick-up date] [Return date] [Search]
|
||||
- Popular cities quick links
|
||||
|
||||
2. **Featured Offers Carousel**
|
||||
- "🔥 Current Deals" header
|
||||
- Horizontal scroll of offer cards:
|
||||
- Vehicle photo (from company upload)
|
||||
- Company logo + name
|
||||
- Offer title + discount badge (e.g. "20% OFF")
|
||||
- Valid until date
|
||||
- **"View Deal →"** → links to `/explore/[slug]#offers`
|
||||
|
||||
3. **All Available Vehicles**
|
||||
- Grid of vehicle cards showing ALL published vehicles from ALL companies:
|
||||
- **Vehicle photo** (first photo from `Vehicle.photos[]`)
|
||||
- Make, model, year, category badge
|
||||
- Company logo + name (clickable)
|
||||
- Daily rate + currency
|
||||
- Active offer badge (if applicable, e.g. "20% OFF until July 31")
|
||||
- Star rating from reviews
|
||||
- **"Book at [Company Name] →"** — on click: redirect to `{slug}.RentalDriveGo.com/book?vehicleId=X&from=Y&to=Z&ref=marketplace`
|
||||
- Filter panel: category, transmission, fuel type, price range, city, offer only
|
||||
- Sort: price (low-high, high-low), rating, newest
|
||||
|
||||
4. **Browse by Category**
|
||||
- Economy · Compact · SUV · Luxury · Van · Truck · Electric
|
||||
|
||||
5. **Browse by Company**
|
||||
- Company cards: logo, name, city, rating, vehicle count, active offer badge
|
||||
- **"View Fleet →"** → `/explore/[slug]`
|
||||
|
||||
6. **How It Works** (for first-time visitors)
|
||||
- 3 steps: 1. Browse vehicles → 2. Click to go to company site → 3. Book & pay directly
|
||||
|
||||
### `/explore?...` — Search Results
|
||||
- Same vehicle card grid, filtered by search params
|
||||
- No booking happens here — every card links to company site with pre-filled params
|
||||
- Availability indicator on each card ("3 available for your dates" vs "Check availability")
|
||||
|
||||
### `/explore/[slug]` — Company Marketplace Profile
|
||||
This is the company's listing on the RentalDriveGo marketplace (RentalDriveGo-styled, not their branded site).
|
||||
|
||||
Sections:
|
||||
1. Company header — logo, display name, city/country, rating, phone, "Save" heart
|
||||
2. **Current Offers section** — offer cards with discount badges
|
||||
3. **Available Vehicles grid** — all published vehicles with photos
|
||||
- Each card: photo(s), specs, daily rate, offer badge, **"Book at [Company Name] →"** redirect button
|
||||
4. **Reviews** — renter reviews with star ratings + company replies
|
||||
5. Contact info — email, phone, WhatsApp link (opens `wa.me/...`)
|
||||
6. "Visit their website →" link to `{slug}.RentalDriveGo.com`
|
||||
|
||||
### `/explore/[slug]/vehicles/[id]` — Vehicle Detail (Marketplace)
|
||||
- **Photo gallery** (all `Vehicle.photos[]` uploaded by company)
|
||||
- Full specs: make, model, year, color, category, seats, transmission, fuel, features
|
||||
- Daily rate + active offer badge + calculated total for selected dates
|
||||
- Availability calendar (shows blocked dates — unavailable for booking)
|
||||
- Company info sidebar: logo, name, rating, phone, WhatsApp
|
||||
- **BIG CTA: "Book at [Company Name] →"**
|
||||
- Redirects to: `{slug}.RentalDriveGo.com/book?vehicleId=[id]&from=[startDate]&to=[endDate]&ref=marketplace`
|
||||
- Opens in same tab
|
||||
|
||||
### No `/explore/[slug]/book` page
|
||||
There is no booking flow on the marketplace. Booking happens entirely on the company's site.
|
||||
|
||||
---
|
||||
|
||||
## LAYER 1B — Renter Account
|
||||
|
||||
### `/renter/signup`, `/renter/login`
|
||||
Standard auth pages. Login with Google optional.
|
||||
|
||||
### `/renter/dashboard` — My Rentals
|
||||
- Cross-company reservations list (tabs: Upcoming / Active / Past / Cancelled)
|
||||
- Each card: company logo, vehicle photo, dates, status, "View" → links to company site booking detail
|
||||
|
||||
### `/renter/notifications` — Notification Inbox
|
||||
- All in-app notifications (new booking, reminders, offers, etc.)
|
||||
|
||||
### `/renter/saved-companies`
|
||||
- Saved companies with "New Offer" badges if they have active offers
|
||||
- "View Fleet →" links to marketplace profile
|
||||
|
||||
### `/renter/profile`
|
||||
- Personal info, locale, currency preference
|
||||
- Notification preferences matrix (Email / SMS / WhatsApp / Push per event type)
|
||||
|
||||
---
|
||||
|
||||
## LAYER 2 — Company Dashboard (app.RentalDriveGo.com)
|
||||
|
||||
### Auth
|
||||
|
||||
#### `/signup` — Signup Wizard
|
||||
1. Account (name, email, password)
|
||||
2. Company (name, country, phone)
|
||||
3. Plan selection (Starter / Growth / Pro + Monthly / Annual + currency: MAD / USD / EUR)
|
||||
4. Payment:
|
||||
- **Option A: Pay with AmanPay** → shows AmanPay widget (card, cash, e-wallet)
|
||||
- **Option B: Pay with PayPal** → redirect to PayPal, return here
|
||||
- AmanPay and PayPal logos shown prominently
|
||||
- "Supported: Visa, Mastercard, CMI, cash at 3000+ points, PayPal"
|
||||
|
||||
#### `/onboarding` — 4-Step Wizard
|
||||
1. Company profile (address, phone)
|
||||
2. Branding (logo, color, subdomain)
|
||||
3. **Add first vehicle + upload photos** — emphasize: "Photos appear on the global marketplace"
|
||||
4. Launch: "Your site is live at [slug].RentalDriveGo.com" + "Your vehicles are now visible on RentalDriveGo.com/explore"
|
||||
|
||||
### Dashboard Pages
|
||||
|
||||
#### `/dashboard` — Home
|
||||
- Subscription status banner (trial / past-due / renewal due)
|
||||
- KPI cards + active offers widget + booking source breakdown
|
||||
|
||||
#### `/dashboard/fleet` — Fleet Management
|
||||
- Vehicle list with status + published/unpublished badge
|
||||
- **Photos column** — thumbnail of first photo, "0 photos" warning badge if none uploaded
|
||||
- Vehicle detail:
|
||||
- **Photos section at top** — drag-drop upload area
|
||||
- "Photos appear on marketplace" callout
|
||||
- Upload multiple photos (Cloudinary)
|
||||
- Reorder photos (first = cover image)
|
||||
- Delete photos
|
||||
- Specs + maintenance + reservation history
|
||||
|
||||
#### `/dashboard/offers` — Offers
|
||||
- Create / manage promotional offers
|
||||
- `isPublic` toggle: "Show on global marketplace"
|
||||
- `isFeatured` toggle: "Feature on explore homepage" (GROWTH+ plan badge)
|
||||
|
||||
#### `/dashboard/reservations`
|
||||
- Source filter includes MARKETPLACE (bookings from renter redirected from explore)
|
||||
|
||||
#### `/dashboard/billing` — Billing & Plan
|
||||
- Current plan + status
|
||||
- **"Renew Subscription"** button (manual renewal — AmanPay or PayPal)
|
||||
- Invoice history (with AmanPay transaction IDs or PayPal capture IDs)
|
||||
- Payment methods accepted: AmanPay badge + PayPal badge
|
||||
|
||||
#### `/dashboard/settings` — Settings
|
||||
- **Payments tab** ← UPDATED:
|
||||
- "Your rental payment methods" section (what renters use to pay YOU)
|
||||
- AmanPay section: Merchant ID input + Secret Key input + "Test connection" button
|
||||
- PayPal section: PayPal business email input + "Connect PayPal" button
|
||||
- Active payment methods shown with green/grey status dots
|
||||
- "If no payment method configured, renters can only make reservation requests (pay on pickup)"
|
||||
|
||||
#### `/dashboard/notifications` — Notification Center
|
||||
- Inbox tab (in-app notification log)
|
||||
- Preferences tab (Email / SMS / In-App / Push matrix per event type)
|
||||
|
||||
---
|
||||
|
||||
## LAYER 3 — Company Public Site ({slug}.RentalDriveGo.com)
|
||||
|
||||
> All booking and payment happens here. Company's brand. Company's AmanPay/PayPal.
|
||||
|
||||
### `/` — Home
|
||||
- Company header: logo + name + nav
|
||||
- Hero: tagline + hero image + "Browse Our Vehicles" CTA
|
||||
- **Active Offers section** — offer banners with countdown (valid until X days)
|
||||
- Vehicle grid (all published vehicles with photos)
|
||||
- Footer: contact + social + "Powered by RentalDriveGo" (Pro removes)
|
||||
|
||||
### `/vehicles/[id]` — Vehicle Detail
|
||||
- Photo gallery (all company-uploaded photos, full lightbox)
|
||||
- Specs + features list
|
||||
- Daily rate + active offer badge
|
||||
- Availability calendar
|
||||
- "Book Now" CTA
|
||||
|
||||
### `/offers` — All Offers
|
||||
- All active offers with banner images, descriptions, discount badges, valid until
|
||||
- "Book with this offer" → pre-fills booking form with offer applied
|
||||
|
||||
### `/book` — Booking Flow
|
||||
|
||||
The booking form accepts a `?vehicleId=X&from=Y&to=Z&ref=marketplace&offerId=O` querystring.
|
||||
When `ref=marketplace` is present, the reservation is tagged as source=MARKETPLACE.
|
||||
|
||||
**Step 1 — Vehicle & Dates**
|
||||
- Vehicle card (pre-filled if from marketplace redirect or offer click)
|
||||
- Date range picker
|
||||
- Active offer applied (if applicable) + promo code input
|
||||
- Price breakdown: base rate − discount = total
|
||||
|
||||
**Step 2 — Your Details**
|
||||
- First name, last name, email, phone (required)
|
||||
- Driver license (optional, company-configurable)
|
||||
- Renter account login prompt (optional — "Save this booking to your RentalDriveGo account")
|
||||
|
||||
**Step 3 — Payment**
|
||||
- Order summary card
|
||||
- **Payment method selection:**
|
||||
- **AmanPay tab** (if company has AmanPay configured):
|
||||
- AmanPay widget loads inline (card national, card international, cash)
|
||||
- Powered by AmanPay + M2T + PCI-DSS badge
|
||||
- **PayPal tab** (if company has PayPal configured):
|
||||
- PayPal buttons component
|
||||
- If only one method: show directly without tabs
|
||||
- If none: "Contact us to reserve" + company phone/WhatsApp
|
||||
- "Confirm & Pay" / "Confirm Reservation"
|
||||
|
||||
### `/book/confirmation` — Booking Confirmed
|
||||
- Booking reference number
|
||||
- Full summary
|
||||
- Company contact info
|
||||
- "Download receipt" (PDF, optional)
|
||||
- "Track this booking" → link to renter account
|
||||
- Notifications already sent automatically
|
||||
|
||||
### `/contact` — Contact
|
||||
- Contact form → email via Resend to company
|
||||
|
||||
---
|
||||
|
||||
## Admin Panel (admin.RentalDriveGo.com — internal)
|
||||
|
||||
> Separate app from the company dashboard. Own auth (JWT + TOTP 2FA). Only RentalDriveGo staff.
|
||||
|
||||
### Auth
|
||||
- `/admin/login` — email + password + optional 2FA code field (shown if 2FA enabled)
|
||||
- `/admin/login/2fa` — TOTP entry step
|
||||
|
||||
### `/admin` — Overview Dashboard
|
||||
- Platform KPI cards: Total Companies, Active, Trialing, Suspended, MRR, Churn
|
||||
- Recent signups feed
|
||||
- Quick links to all sections
|
||||
|
||||
### `/admin/companies` — Company List
|
||||
- Search (name, email, slug), filter (plan, status, country), sort (name, MRR, created)
|
||||
- Table: Logo, Name, Plan badge, Status badge, Vehicle count, Employee count, MRR, Actions
|
||||
- Actions: View | Edit | Suspend/Reactivate | Delete
|
||||
- Bulk: Suspend selected, Export CSV
|
||||
|
||||
### `/admin/companies/:id` — Company Detail
|
||||
- Tabs: Overview | Staff | Subscription | Activity | Settings
|
||||
- **Overview**: brand info, legal info, payment accounts status
|
||||
- **Staff tab**:
|
||||
- Employee table: name, email, role badge, status (Active/Deactivated), last login
|
||||
- Actions per employee: Edit | Change Role | Deactivate/Reactivate | Reset Password | Impersonate
|
||||
- "Add Employee" button → modal: name, email, role → sends Clerk invitation
|
||||
- Role dropdown: OWNER / MANAGER / AGENT with permission matrix tooltip
|
||||
- **Subscription tab**:
|
||||
- Current plan, status, billing period, last payment, next renewal
|
||||
- Invoice history table
|
||||
- Override controls: force status, change plan, mark invoice paid, add free period
|
||||
- **Activity tab**: recent reservations, offers, audit events for this company
|
||||
|
||||
### `/admin/renters` — Renter Accounts
|
||||
- Search by name, email, phone
|
||||
- Filter: blocked / unblocked
|
||||
- Table: name, email, phone, joined, reservation count, blocked badge
|
||||
- Actions: View | Block/Unblock
|
||||
|
||||
### `/admin/metrics` — Platform Analytics
|
||||
- MRR trend chart
|
||||
- New companies per week
|
||||
- Subscription breakdown by plan (donut chart)
|
||||
- Marketplace: impressions, bookings, top companies
|
||||
- Notification delivery rates per channel
|
||||
|
||||
### `/admin/users` — Admin User Management (SUPER_ADMIN only)
|
||||
- List of admin accounts: name, email, role, 2FA status, last login
|
||||
- Create admin: name, email, role → password emailed
|
||||
- Edit role | Deactivate | View audit trail
|
||||
|
||||
### `/admin/audit-logs` — Audit Log
|
||||
- Searchable, filterable (by admin, action, company, date range)
|
||||
- Columns: Timestamp, Admin, Action, Target, Before/After (expandable), IP, Note
|
||||
- Export CSV button
|
||||
|
||||
### `/admin/settings` — Admin Panel Settings
|
||||
- Admin account: update own name, email, password
|
||||
- 2FA: setup QR code, verify, enable/disable
|
||||
# Pages and Route Inventory — Current Apps
|
||||
|
||||
This document lists the pages that are actually present in the current frontend apps.
|
||||
|
||||
Source of truth:
|
||||
|
||||
- `apps/marketplace/src/app`
|
||||
- `apps/dashboard/src/app`
|
||||
- `apps/admin/src/app`
|
||||
|
||||
## Marketplace App
|
||||
|
||||
App:
|
||||
|
||||
- `apps/marketplace`
|
||||
|
||||
### Public routes
|
||||
|
||||
- `/`
|
||||
- `/pricing`
|
||||
- `/features`
|
||||
- `/explore`
|
||||
- `/explore/[slug]`
|
||||
- `/review`
|
||||
- `/company-workspace`
|
||||
- `/platform-operations`
|
||||
- `/sign-in`
|
||||
- `/app-privacy-en`
|
||||
- `/app-privacy-fr`
|
||||
- `/app-privacy-ar`
|
||||
- `/app-tc-en`
|
||||
- `/app-tc-fr`
|
||||
- `/app-tc-ar`
|
||||
- `/footer/[slug]`
|
||||
|
||||
### Active route behavior
|
||||
|
||||
- `/` is the public marketing home.
|
||||
- `/pricing` consumes platform pricing from `/site/platform/pricing`.
|
||||
- `/explore` is the public marketplace search/discovery page.
|
||||
- `/explore/[slug]` is the company marketplace profile page.
|
||||
- `/review` is the review submission page driven by a reservation review token.
|
||||
|
||||
### Not present in the current marketplace app
|
||||
|
||||
- `/about`
|
||||
- `/contact`
|
||||
- `/blog`
|
||||
- `/explore/[slug]/vehicles/[id]`
|
||||
- a full company public-site booking frontend under its own app
|
||||
|
||||
### Renter routes present in code but not active product entrypoints
|
||||
|
||||
These routes exist in the marketplace app:
|
||||
|
||||
- `/renter/dashboard`
|
||||
- `/renter/notifications`
|
||||
- `/renter/profile`
|
||||
- `/renter/saved-companies`
|
||||
- `/renter/sign-in`
|
||||
- `/renter/sign-up`
|
||||
|
||||
However:
|
||||
|
||||
- `/renter/sign-in` redirects to the dashboard sign-in page
|
||||
- `/renter/sign-up` redirects to the dashboard sign-in page
|
||||
- API renter signup/login endpoints are disabled
|
||||
|
||||
Because of that, the renter self-service area should be treated as dormant or incomplete, not as an active supported user flow.
|
||||
|
||||
## Dashboard App
|
||||
|
||||
App:
|
||||
|
||||
- `apps/dashboard`
|
||||
|
||||
Base path:
|
||||
|
||||
- `/dashboard`
|
||||
|
||||
### Public/auth routes
|
||||
|
||||
- `/dashboard/sign-in`
|
||||
- `/dashboard/sign-up`
|
||||
- `/dashboard/forgot-password`
|
||||
- `/dashboard/reset-password`
|
||||
- `/dashboard/onboarding`
|
||||
- `/dashboard/onboarding/accept-invite`
|
||||
|
||||
### Private company workspace routes
|
||||
|
||||
- `/dashboard`
|
||||
- `/dashboard/fleet`
|
||||
- `/dashboard/fleet/[id]`
|
||||
- `/dashboard/reservations`
|
||||
- `/dashboard/reservations/new`
|
||||
- `/dashboard/reservations/[id]`
|
||||
- `/dashboard/online-reservations`
|
||||
- `/dashboard/customers`
|
||||
- `/dashboard/offers`
|
||||
- `/dashboard/team`
|
||||
- `/dashboard/reports`
|
||||
- `/dashboard/subscription`
|
||||
- `/dashboard/billing`
|
||||
- `/dashboard/contracts`
|
||||
- `/dashboard/contracts/[id]`
|
||||
- `/dashboard/reviews`
|
||||
- `/dashboard/complaints`
|
||||
- `/dashboard/notifications`
|
||||
- `/dashboard/settings`
|
||||
|
||||
### Route responsibilities
|
||||
|
||||
- `/dashboard` shows KPIs and booking-source analytics.
|
||||
- `/dashboard/fleet*` manages vehicles, maintenance, and calendar blocks.
|
||||
- `/dashboard/reservations*` manages the booking lifecycle and inspection workflows.
|
||||
- `/dashboard/online-reservations` handles public/marketplace booking intake.
|
||||
- `/dashboard/customers` is the company CRM view.
|
||||
- `/dashboard/offers` manages promotions.
|
||||
- `/dashboard/team` manages employees.
|
||||
- `/dashboard/reports` shows analytics/reporting views.
|
||||
- `/dashboard/subscription` manages SaaS plan state and invoices.
|
||||
- `/dashboard/billing` manages rental payment collection and balances.
|
||||
- `/dashboard/contracts*` opens rental contracts.
|
||||
- `/dashboard/reviews` and `/dashboard/complaints` handle post-rental follow-up.
|
||||
- `/dashboard/notifications` shows notification inbox/history/preferences.
|
||||
- `/dashboard/settings` manages brand, domains, policies, insurance, pricing, and accounting settings.
|
||||
|
||||
## Admin App
|
||||
|
||||
App:
|
||||
|
||||
- `apps/admin`
|
||||
|
||||
### Public routes
|
||||
|
||||
- `/`
|
||||
- `/login`
|
||||
- `/forgot-password`
|
||||
- `/reset-password`
|
||||
- `/auth-redirect`
|
||||
|
||||
### Private admin dashboard routes
|
||||
|
||||
- `/dashboard`
|
||||
- `/dashboard/companies`
|
||||
- `/dashboard/companies/[id]`
|
||||
- `/dashboard/renters`
|
||||
- `/dashboard/admin-users`
|
||||
- `/dashboard/audit-logs`
|
||||
- `/dashboard/billing`
|
||||
- `/dashboard/pricing`
|
||||
- `/dashboard/notifications`
|
||||
- `/dashboard/site-config`
|
||||
- `/dashboard/containers`
|
||||
|
||||
### Route responsibilities
|
||||
|
||||
- `/dashboard` is the admin overview.
|
||||
- `/dashboard/companies*` manages tenant companies.
|
||||
- `/dashboard/renters` inspects and blocks/unblocks renter accounts.
|
||||
- `/dashboard/admin-users` manages admin operators.
|
||||
- `/dashboard/audit-logs` reviews admin activity.
|
||||
- `/dashboard/billing` manages platform billing operations.
|
||||
- `/dashboard/pricing` edits platform pricing, features, and promotions.
|
||||
- `/dashboard/notifications` inspects platform notifications.
|
||||
- `/dashboard/site-config` edits marketplace/site configuration content.
|
||||
|
||||
## Deliberately Not Listed
|
||||
|
||||
This document excludes:
|
||||
|
||||
- API endpoints
|
||||
- dormant backend-only concepts that do not have an active frontend route
|
||||
- design-spec routes that are not present in the current app trees
|
||||
|
||||
For backend route inventory, use:
|
||||
|
||||
- `docs/project-design/api-routes.md`
|
||||
|
||||
+329
-431
@@ -1,498 +1,396 @@
|
||||
# API Routes Inventory — RentalDriveGo (Complete)
|
||||
# API Architecture and Route Design
|
||||
|
||||
Base URL: `https://api.RentalDriveGo.com/api/v1`
|
||||
This document explains how the API is structured today, how requests move through the stack, and what each route group is responsible for.
|
||||
|
||||
## Middleware Legend
|
||||
- 🔒 `requireCompanyAuth` — valid Clerk JWT (Employee)
|
||||
- 🏢 `requireTenant` — attaches `req.company` + `req.companyId` + `req.employee`
|
||||
- 💳 `requireSubscription` — blocks SUSPENDED/PENDING companies
|
||||
- 🎫 `requireRenterAuth` — valid Renter JWT
|
||||
- 🔑 `requireApiKey` — company public API key in `x-api-key` header
|
||||
- 👑 `requireAdmin` — RentalDriveGo super-admin role
|
||||
Source of truth for the runtime wiring:
|
||||
|
||||
> **Tenant isolation rule:** Every 🏢 route must use `where: { companyId: req.companyId }` in every query.
|
||||
- `apps/api/src/app.ts`
|
||||
- `apps/api/src/modules/*`
|
||||
- `apps/api/src/middleware/*`
|
||||
- `apps/api/src/swagger/openapi.ts`
|
||||
|
||||
---
|
||||
## Runtime Overview
|
||||
|
||||
## Auth — Company Employees
|
||||
The API is an Express application mounted under `/api/v1` with a few non-versioned utility endpoints:
|
||||
|
||||
| Method | Path | Auth | Description |
|
||||
|--------|------|------|-------------|
|
||||
| POST | `/auth/company/signup` | — | Create Clerk user + Company + AmanPay/PayPal customer |
|
||||
| POST | `/auth/company/verify-email` | — | Verify email token |
|
||||
- `GET /health` returns process health.
|
||||
- `GET /docs` serves Swagger UI.
|
||||
- `GET /api/v1/openapi.json` returns the generated OpenAPI document.
|
||||
- `/storage/*` serves uploaded assets such as logos, hero images, vehicle photos, and reservation/customer documents.
|
||||
|
||||
*(Clerk handles login/logout/session refresh on the frontend)*
|
||||
The app boot sequence in `apps/api/src/app.ts` is intentionally ordered:
|
||||
|
||||
---
|
||||
1. CORS is applied first.
|
||||
2. storage guards are applied before static serving so private customer license files are never anonymously retrievable.
|
||||
3. Swagger UI is mounted before Helmet so the UI assets are not blocked by CSP.
|
||||
4. webhook routes that require raw payload handling are mounted before `express.json()`.
|
||||
5. Helmet, request logging, JSON parsing, and the module routers are mounted.
|
||||
6. the centralized error middleware converts validation, Prisma, and application errors into consistent JSON responses.
|
||||
|
||||
## Auth — Renters
|
||||
## Request Lifecycle
|
||||
|
||||
| Method | Path | Auth | Description |
|
||||
|--------|------|------|-------------|
|
||||
| POST | `/auth/renter/signup` | — | Create renter account |
|
||||
| POST | `/auth/renter/login` | — | Email + password → JWT |
|
||||
| POST | `/auth/renter/logout` | 🎫 | Invalidate token |
|
||||
| POST | `/auth/renter/verify-email` | — | Email verification token |
|
||||
| POST | `/auth/renter/forgot-password` | — | Send reset email |
|
||||
| POST | `/auth/renter/reset-password` | — | Reset with token |
|
||||
| GET | `/auth/renter/me` | 🎫 | Get renter profile |
|
||||
| PATCH | `/auth/renter/me` | 🎫 | Update profile (name, phone, locale, currency) |
|
||||
| POST | `/auth/renter/me/fcm-token` | 🎫 | Register FCM push token |
|
||||
Most internal company routes follow the same pattern:
|
||||
|
||||
---
|
||||
1. Express router receives the request.
|
||||
2. Zod schemas validate `req.params`, `req.query`, and `req.body` through `parseParams`, `parseQuery`, and `parseBody`.
|
||||
3. auth middleware resolves the caller identity.
|
||||
4. tenant middleware resolves the company record.
|
||||
5. subscription middleware blocks suspended or pending companies where required.
|
||||
6. role middleware enforces employee or admin permissions.
|
||||
7. the route handler calls a service function.
|
||||
8. the service applies business rules and calls a repo or Prisma directly.
|
||||
9. a presenter may normalize the payload for frontend consumption.
|
||||
10. `ok()` or `created()` wraps successful responses in `{ "data": ... }`.
|
||||
|
||||
## Subscriptions (AmanPay/PayPal subscription — RentalDriveGo collects)
|
||||
Common exceptions:
|
||||
|
||||
| Method | Path | Auth | Description |
|
||||
|--------|------|------|-------------|
|
||||
| GET | `/subscriptions/plans` | — | List plans + prices (public) |
|
||||
| GET | `/subscriptions/me` | 🔒🏢 | Current subscription |
|
||||
| POST | `/subscriptions/checkout` | 🔒🏢 | Create AmanPay/PayPal Checkout session |
|
||||
| POST | `/subscriptions/portal` | 🔒🏢 | Open AmanPay/PayPal Customer Portal |
|
||||
| GET | `/subscriptions/invoices` | 🔒🏢💳 | Invoice history |
|
||||
| POST | `/subscriptions/webhook` | — (sig) | AmanPay/PayPal subscription webhook |
|
||||
- `GET /health` and `GET /api/v1/docs` return raw JSON, not the `{ data }` envelope.
|
||||
- webhook endpoints return minimal receipt payloads.
|
||||
- some public endpoints return graceful fallback responses if the database is unavailable.
|
||||
|
||||
---
|
||||
## Security and Access Model
|
||||
|
||||
## Companies
|
||||
There are four main access patterns.
|
||||
|
||||
| Method | Path | Auth | Description |
|
||||
|--------|------|------|-------------|
|
||||
| GET | `/companies/me` | 🔒🏢 | Company profile |
|
||||
| PATCH | `/companies/me` | 🔒🏢💳 | Update profile |
|
||||
| GET | `/companies/me/brand` | 🔒🏢 | Brand settings |
|
||||
| PATCH | `/companies/me/brand` | 🔒🏢💳 | Update branding |
|
||||
| POST | `/companies/me/brand/logo` | 🔒🏢💳 | Upload logo |
|
||||
| POST | `/companies/me/brand/hero` | 🔒🏢💳 | Upload hero image |
|
||||
| POST | `/companies/me/brand/subdomain/check` | 🔒🏢💳 | Check subdomain availability |
|
||||
| POST | `/companies/me/brand/custom-domain` | 🔒🏢💳 | Add custom domain (Pro) |
|
||||
| GET | `/companies/me/brand/custom-domain/status` | 🔒🏢💳 | Domain verification status |
|
||||
| DELETE | `/companies/me/brand/custom-domain` | 🔒🏢💳 | Remove custom domain |
|
||||
| GET | `/companies/me/api-key` | 🔒🏢💳 | Get public API key |
|
||||
| POST | `/companies/me/api-key/regenerate` | 🔒🏢💳 | Regenerate API key |
|
||||
| GET | `/companies/me/stripe-connect` | 🔒🏢💳 | Connect status |
|
||||
| POST | `/companies/me/stripe-connect` | 🔒🏢💳 | Start Connect OAuth |
|
||||
| POST | `/companies/me/stripe-connect/webhook` | — (sig) | Connect events |
|
||||
### Employee JWT
|
||||
|
||||
---
|
||||
`requireCompanyAuth` validates a Bearer token with `type === "employee"`, loads the employee, and attaches:
|
||||
|
||||
## Team
|
||||
- `req.employee`
|
||||
- `req.company`
|
||||
- `req.companyId`
|
||||
|
||||
| Method | Path | Auth | Description |
|
||||
|--------|------|------|-------------|
|
||||
| GET | `/team` | 🔒🏢💳 | List employees |
|
||||
| POST | `/team/invite` | 🔒🏢💳 | Invite by email |
|
||||
| PATCH | `/team/:id/role` | 🔒🏢💳 | Change role |
|
||||
| DELETE | `/team/:id` | 🔒🏢💳 | Remove employee |
|
||||
This is the default for dashboard/company management routes.
|
||||
|
||||
---
|
||||
### Tenant Resolution
|
||||
|
||||
## Vehicles (Company fleet — strictly isolated)
|
||||
`requireTenant` reloads the company from `req.companyId` and guarantees the tenant context exists. Company-scoped modules then query by `companyId`.
|
||||
|
||||
| Method | Path | Auth | Description |
|
||||
|--------|------|------|-------------|
|
||||
| GET | `/vehicles` | 🔒🏢💳 | List `?status=&category=&published=` |
|
||||
| POST | `/vehicles` | 🔒🏢💳 | Add vehicle (checks plan limit) |
|
||||
| GET | `/vehicles/:id` | 🔒🏢💳 | Detail (validates companyId) |
|
||||
| PATCH | `/vehicles/:id` | 🔒🏢💳 | Update |
|
||||
| DELETE | `/vehicles/:id` | 🔒🏢💳 | Archive |
|
||||
| POST | `/vehicles/:id/photos` | 🔒🏢💳 | Upload photos (Cloudinary) |
|
||||
| DELETE | `/vehicles/:id/photos/:idx` | 🔒🏢💳 | Remove photo |
|
||||
| PATCH | `/vehicles/:id/publish` | 🔒🏢💳 | Toggle published |
|
||||
| GET | `/vehicles/:id/availability` | 🔒🏢💳 | `?startDate=&endDate=` |
|
||||
| GET | `/vehicles/:id/reservations` | 🔒🏢💳 | Reservation history |
|
||||
| GET | `/vehicles/:id/maintenance` | 🔒🏢💳 | Maintenance log |
|
||||
| POST | `/vehicles/:id/maintenance` | 🔒🏢💳 | Add maintenance entry |
|
||||
### Subscription Guard
|
||||
|
||||
---
|
||||
`requireSubscription` blocks company routes when the company status is `PENDING` or `SUSPENDED`. Public site and marketplace routes are intentionally not behind this guard.
|
||||
|
||||
## Offers (Company promotional offers)
|
||||
### Role-Based Access Control
|
||||
|
||||
| Method | Path | Auth | Description |
|
||||
|--------|------|------|-------------|
|
||||
| GET | `/offers` | 🔒🏢💳 | List all offers `?active=&public=` |
|
||||
| POST | `/offers` | 🔒🏢💳 | Create offer |
|
||||
| GET | `/offers/:id` | 🔒🏢💳 | Offer detail |
|
||||
| PATCH | `/offers/:id` | 🔒🏢💳 | Update offer |
|
||||
| DELETE | `/offers/:id` | 🔒🏢💳 | Delete offer |
|
||||
| POST | `/offers/:id/activate` | 🔒🏢💳 | Activate offer |
|
||||
| POST | `/offers/:id/deactivate` | 🔒🏢💳 | Deactivate offer |
|
||||
| POST | `/offers/:id/feature` | 🔒🏢💳 | Toggle featured (plan-gated) |
|
||||
| GET | `/offers/:id/stats` | 🔒🏢💳 | Redemption stats, revenue impact |
|
||||
Employee roles are hierarchical:
|
||||
|
||||
---
|
||||
- `OWNER`
|
||||
- `MANAGER`
|
||||
- `AGENT`
|
||||
|
||||
## Reservations (Company view — includes all sources)
|
||||
Admin roles are hierarchical:
|
||||
|
||||
| Method | Path | Auth | Description |
|
||||
|--------|------|------|-------------|
|
||||
| GET | `/reservations` | 🔒🏢💳 | List `?status=&vehicleId=&source=&startDate=&endDate=` |
|
||||
| POST | `/reservations` | 🔒🏢💳 | Create (dashboard) |
|
||||
| GET | `/reservations/:id` | 🔒🏢💳 | Detail |
|
||||
| PATCH | `/reservations/:id` | 🔒🏢💳 | Update |
|
||||
| POST | `/reservations/:id/confirm` | 🔒🏢💳 | Confirm + charge |
|
||||
| POST | `/reservations/:id/checkin` | 🔒🏢💳 | Check in `{ mileage }` |
|
||||
| POST | `/reservations/:id/checkout` | 🔒🏢💳 | Check out + complete `{ mileage }` |
|
||||
| POST | `/reservations/:id/cancel` | 🔒🏢💳 | Cancel `{ reason }` |
|
||||
| POST | `/reservations/:id/no-show` | 🔒🏢💳 | Mark no-show |
|
||||
| POST | `/reservations/:id/reply-review` | 🔒🏢💳 | Reply to renter review |
|
||||
- `SUPER_ADMIN`
|
||||
- `ADMIN`
|
||||
- `SUPPORT`
|
||||
- `FINANCE`
|
||||
- `VIEWER`
|
||||
|
||||
---
|
||||
### Renter JWT
|
||||
|
||||
## Customers (Company CRM)
|
||||
`requireRenterAuth` validates a Bearer token with `type === "renter"` and attaches `req.renterId`.
|
||||
|
||||
| Method | Path | Auth | Description |
|
||||
|--------|------|------|-------------|
|
||||
| GET | `/customers` | 🔒🏢💳 | List `?q=&flagged=` |
|
||||
| POST | `/customers` | 🔒🏢💳 | Create |
|
||||
| GET | `/customers/:id` | 🔒🏢💳 | Detail + reservation history |
|
||||
| PATCH | `/customers/:id` | 🔒🏢💳 | Update |
|
||||
| POST | `/customers/:id/flag` | 🔒🏢💳 | Flag `{ reason }` |
|
||||
| DELETE | `/customers/:id/flag` | 🔒🏢💳 | Remove flag |
|
||||
`optionalRenterAuth` is used on marketplace routes so public reads still work without a token.
|
||||
|
||||
---
|
||||
### API Key
|
||||
|
||||
## Payments (AmanPay/PayPal rental — rental revenue)
|
||||
`requireApiKey` accepts a company public API key in `x-api-key`. This is the low-friction integration path for public company-site style calls.
|
||||
|
||||
| Method | Path | Auth | Description |
|
||||
|--------|------|------|-------------|
|
||||
| GET | `/payments` | 🔒🏢💳 | List payments |
|
||||
| GET | `/payments/:id` | 🔒🏢💳 | Payment detail |
|
||||
| POST | `/payments/:id/refund` | 🔒🏢💳 | Issue refund |
|
||||
| POST | `/payments/webhook` | — (sig) | AmanPay/PayPal rental webhook |
|
||||
## Implementation Pattern
|
||||
|
||||
---
|
||||
The API codebase is organized per module. The most common pattern is:
|
||||
|
||||
## Analytics
|
||||
- `*.routes.ts`: route definitions and middleware composition
|
||||
- `*.schemas.ts`: Zod input validation
|
||||
- `*.service.ts`: business rules and orchestration
|
||||
- `*.repo.ts`: Prisma access helpers
|
||||
- `*.presenter.ts`: response shaping
|
||||
|
||||
| Method | Path | Auth | Description |
|
||||
|--------|------|------|-------------|
|
||||
| GET | `/analytics/summary` | 🔒🏢💳 | KPIs `?period=7d|30d|90d` |
|
||||
| GET | `/analytics/revenue` | 🔒🏢💳 | Revenue time series |
|
||||
| GET | `/analytics/vehicles` | 🔒🏢💳 | Per-vehicle performance |
|
||||
| GET | `/analytics/utilization` | 🔒🏢💳 | Fleet utilization |
|
||||
| GET | `/analytics/offers` | 🔒🏢💳 | Offer performance + redemptions |
|
||||
| GET | `/analytics/sources` | 🔒🏢💳 | Booking source breakdown |
|
||||
The `vehicles` module is representative:
|
||||
|
||||
---
|
||||
- `vehicle.routes.ts` mounts auth, tenant, and subscription guards once for the router.
|
||||
- route handlers validate input and call service functions.
|
||||
- `vehicle.service.ts` contains business rules such as publish-state normalization and availability calculation.
|
||||
- `vehicle.repo.ts` owns the Prisma queries and enforces tenant filters in reads.
|
||||
- `vehicle.presenter.ts` shapes the response returned to clients.
|
||||
|
||||
## Notifications — Company
|
||||
Some modules split specialized workflows into additional services instead of one large file. The reservations module is the clearest example:
|
||||
|
||||
| Method | Path | Auth | Description |
|
||||
|--------|------|------|-------------|
|
||||
| GET | `/notifications/company` | 🔒🏢💳 | In-app notification log `?unread=true` |
|
||||
| POST | `/notifications/company/:id/read` | 🔒🏢💳 | Mark as read |
|
||||
| POST | `/notifications/company/read-all` | 🔒🏢💳 | Mark all read |
|
||||
| GET | `/notifications/company/preferences` | 🔒🏢💳 | Get notification preferences |
|
||||
| PATCH | `/notifications/company/preferences` | 🔒🏢💳 | Update preferences per type+channel |
|
||||
- `reservation.service.ts` handles CRUD and list behavior
|
||||
- `reservation.lifecycle.service.ts` handles confirm/checkin/checkout/close/extend/cancel transitions
|
||||
- `reservation.inspection.service.ts` handles pickup/dropoff inspections
|
||||
- `reservation.additional-driver.service.ts` handles approval workflows
|
||||
- `reservation.photo.service.ts` handles pickup/dropoff photo uploads
|
||||
- `reservation.document.service.ts` generates contract and billing payloads
|
||||
|
||||
---
|
||||
## Route Groups
|
||||
|
||||
## Notifications — Renter
|
||||
The table below describes the current route groups mounted in `app.ts`.
|
||||
|
||||
| Method | Path | Auth | Description |
|
||||
|--------|------|------|-------------|
|
||||
| GET | `/notifications/renter` | 🎫 | Renter in-app notifications |
|
||||
| POST | `/notifications/renter/:id/read` | 🎫 | Mark as read |
|
||||
| POST | `/notifications/renter/read-all` | 🎫 | Mark all read |
|
||||
| GET | `/notifications/renter/preferences` | 🎫 | Get preferences |
|
||||
| PATCH | `/notifications/renter/preferences` | 🎫 | Update preferences |
|
||||
| Base path | Access | Purpose | Notes |
|
||||
| --- | --- | --- | --- |
|
||||
| `/health` | Public | Process health | Non-versioned utility endpoint |
|
||||
| `/docs` | Public | Swagger UI | Backed by `openapi.ts` |
|
||||
| `/api/v1/openapi.json` | Public | OpenAPI JSON | Generated from Zod schemas and manual path entries |
|
||||
| `/api/v1/auth/company` | Public | Company signup | `complete-signup` and `verify-email` now return disabled errors |
|
||||
| `/api/v1/auth/employee` | Public plus employee JWT | Employee login, password reset, profile, language | Dashboard/staff authentication path |
|
||||
| `/api/v1/auth/renter` | Renter JWT for profile routes | Renter profile and push token | `signup` and `login` are currently disabled in this codebase |
|
||||
| `/api/v1/admin` | Admin JWT | Platform operations | Includes company admin, billing, subscriptions, pricing, audit, and admin-user management |
|
||||
| `/api/v1/marketplace` | Public, optional renter JWT | Marketplace discovery and reservation intake | Designed for discovery and lead capture across companies |
|
||||
| `/api/v1/site` | Public | White-label company site APIs | Drives each company-branded booking site |
|
||||
| `/api/v1/subscriptions` | Mixed | Plan listing, webhooks, subscription lifecycle | Public, webhook, and authenticated sub-routers share the same prefix |
|
||||
| `/api/v1/vehicles` | Employee JWT + tenant + subscription | Fleet management | Includes photos, status, calendar blocks, maintenance, and availability |
|
||||
| `/api/v1/reservations` | Employee JWT + tenant + subscription | Reservation operations | Central booking lifecycle for internal staff |
|
||||
| `/api/v1/team` | Employee JWT + tenant + subscription | Employee/team management | Owner-controlled invite, role, activation, removal |
|
||||
| `/api/v1/customers` | Employee JWT + tenant + subscription | Company CRM and license handling | Includes protected license-image retrieval and approval flows |
|
||||
| `/api/v1/offers` | Employee JWT + tenant + subscription | Promotional offers | Company marketing and promo codes |
|
||||
| `/api/v1/analytics` | Employee JWT + tenant + subscription | Dashboard metrics and reports | Includes summary, dashboard, source, and report routes |
|
||||
| `/api/v1/notifications` | Employee or renter JWT depending on route | Notification inbox and preferences | Separate company and renter surfaces |
|
||||
| `/api/v1/companies` | Employee JWT + tenant + subscription | Company profile and settings | Brand, domains, contract settings, insurance policies, pricing rules, accounting, API key |
|
||||
| `/api/v1/payments` | Mixed | Rental payment webhooks and company payment actions | Webhooks are public; operational routes are company-authenticated |
|
||||
| `/api/v1/reviews` | Employee JWT + tenant + subscription | Review moderation | Company-side review visibility and replies |
|
||||
| `/api/v1/complaints` | Employee JWT + tenant + subscription | Complaint handling | Internal complaint tracking |
|
||||
| `/api/v1/webhooks` | Public | External webhook receivers | Currently includes `/clerk` placeholder |
|
||||
|
||||
---
|
||||
## Functional Breakdown by Module
|
||||
|
||||
## Renter — Marketplace Actions
|
||||
### Company and employee onboarding
|
||||
|
||||
| Method | Path | Auth | Description |
|
||||
|--------|------|------|-------------|
|
||||
| GET | `/renter/reservations` | 🎫 | All reservations (cross-company) |
|
||||
| GET | `/renter/reservations/:id` | 🎫 | Reservation detail |
|
||||
| POST | `/renter/reservations/:id/cancel` | 🎫 | Cancel `{ reason }` |
|
||||
| GET | `/renter/saved-companies` | 🎫 | Saved companies |
|
||||
| POST | `/renter/saved-companies/:slug` | 🎫 | Save company |
|
||||
| DELETE | `/renter/saved-companies/:slug` | 🎫 | Unsave company |
|
||||
| POST | `/renter/reviews` | 🎫 | Submit review (completed reservation only) |
|
||||
| GET | `/renter/reviews` | 🎫 | Renter's review history |
|
||||
- `POST /api/v1/auth/company/signup` creates the company tenant, owner employee, default subscription state, and related baseline records in one transaction.
|
||||
- The signup flow generates a unique company slug, hashes the owner password, starts a 90-day trial window, and sends account-created notifications.
|
||||
- Employee auth supports login, forgot-password, reset-password, profile read, and language updates.
|
||||
|
||||
---
|
||||
### Company profile and business configuration
|
||||
|
||||
## Global Marketplace API (public, optional renter auth)
|
||||
The `companies` module owns tenant-level settings:
|
||||
|
||||
| Method | Path | Auth | Description |
|
||||
|--------|------|------|-------------|
|
||||
| GET | `/marketplace/offers` | opt 🎫 | Featured + recent offers across all companies |
|
||||
| GET | `/marketplace/companies` | opt 🎫 | Company list `?city=&rating=&hasOffer=` |
|
||||
| GET | `/marketplace/search` | opt 🎫 | Search vehicles `?city=&startDate=&endDate=&category=&maxPrice=` |
|
||||
| GET | `/marketplace/[slug]` | opt 🎫 | Company marketplace profile |
|
||||
| GET | `/marketplace/[slug]/vehicles` | opt 🎫 | Published vehicles (no internal fields) |
|
||||
| GET | `/marketplace/[slug]/offers` | opt 🎫 | Active public offers |
|
||||
| POST | `/marketplace/[slug]/book` | opt 🎫 | Book (renter auth preferred, guest allowed) |
|
||||
| GET | `/marketplace/[slug]/reviews` | opt 🎫 | Company reviews |
|
||||
| POST | `/marketplace/offers/:code/validate` | opt 🎫 | Validate promo code |
|
||||
- company identity
|
||||
- brand settings and asset upload
|
||||
- subdomain/custom-domain configuration
|
||||
- contract settings used by rental documents
|
||||
- insurance policies
|
||||
- pricing rules
|
||||
- accounting settings
|
||||
- public API key generation/regeneration
|
||||
|
||||
---
|
||||
This module is where most long-lived company configuration lives.
|
||||
|
||||
## Company Public Site API (slug-resolved, no user auth)
|
||||
### Fleet management
|
||||
|
||||
> Used by SSR pages on `{slug}.RentalDriveGo.com`. Never subscription-gated.
|
||||
The `vehicles` module handles:
|
||||
|
||||
| Method | Path | Auth | Description |
|
||||
|--------|------|------|-------------|
|
||||
| GET | `/site/:slug/brand` | 🔑 | Branding for SSR |
|
||||
| GET | `/site/:slug/vehicles` | 🔑 | Published vehicles |
|
||||
| GET | `/site/:slug/vehicles/:id` | 🔑 | Vehicle detail + availability |
|
||||
| GET | `/site/:slug/offers` | 🔑 | Active offers |
|
||||
| POST | `/site/:slug/availability` | 🔑 | Date range availability check |
|
||||
| POST | `/site/:slug/book` | 🔑 | Create booking + payment |
|
||||
| POST | `/site/:slug/book/validate-code` | 🔑 | Validate promo code |
|
||||
| GET | `/site/:slug/booking/:id` | 🔑 | Booking status (confirmation page) |
|
||||
| POST | `/site/:slug/contact` | 🔑 | Contact form → email to company |
|
||||
- CRUD for vehicles
|
||||
- publish/unpublish
|
||||
- explicit status updates
|
||||
- photo upload and deletion
|
||||
- availability checks
|
||||
- monthly vehicle calendar events
|
||||
- manual calendar blocks
|
||||
- maintenance logs
|
||||
|
||||
---
|
||||
Uploads are persisted through the storage service, then surfaced under `/storage/*`.
|
||||
|
||||
## Admin (RentalDriveGo internal)
|
||||
### Reservations and rental workflow
|
||||
|
||||
| Method | Path | Auth | Description |
|
||||
|--------|------|------|-------------|
|
||||
| GET | `/admin/companies` | 👑 | All companies |
|
||||
| GET | `/admin/companies/:id` | 👑 | Company detail + subscription |
|
||||
| PATCH | `/admin/companies/:id/status` | 👑 | Force status |
|
||||
| GET | `/admin/offers/featured` | 👑 | All featured offers |
|
||||
| GET | `/admin/metrics` | 👑 | Platform MRR, churn, signups |
|
||||
| GET | `/admin/notifications/stats` | 👑 | Delivery rates per channel |
|
||||
The reservation aggregate is the center of the operational API.
|
||||
|
||||
---
|
||||
Important transitions:
|
||||
|
||||
## Standard Response Formats
|
||||
1. create draft reservation
|
||||
2. confirm reservation after license checks
|
||||
3. check in and move the reservation to `ACTIVE`
|
||||
4. check out and mark the reservation `COMPLETED`
|
||||
5. close the reservation operationally after post-rental tasks are done
|
||||
|
||||
The lifecycle service also handles:
|
||||
|
||||
- extensions with conflict checks
|
||||
- cancellations
|
||||
- vehicle status synchronization
|
||||
- review token generation
|
||||
- review request email dispatch on close
|
||||
|
||||
The reservations module also owns:
|
||||
|
||||
- contract and billing payload generation
|
||||
- pickup/dropoff inspections
|
||||
- additional-driver approval
|
||||
- pickup/dropoff photo upload
|
||||
|
||||
### Customer CRM and license compliance
|
||||
|
||||
Customers are company-scoped CRM records, even when a renter identity also exists. The `customers` module supports:
|
||||
|
||||
- customer CRUD
|
||||
- flag/unflag flows
|
||||
- license image upload and protected retrieval
|
||||
- license validation and manager approval
|
||||
|
||||
This is intentionally separate from renter identity because one renter may interact with multiple companies while each company still needs its own operational customer record.
|
||||
|
||||
### Marketplace and public site
|
||||
|
||||
The public surface is split in two modules on purpose.
|
||||
|
||||
`marketplace` is the cross-company discovery layer:
|
||||
|
||||
- featured/public offers
|
||||
- marketplace cities
|
||||
- listed companies
|
||||
- vehicle search
|
||||
- marketplace reservation intake
|
||||
- review submission by token
|
||||
- company public pages under `/:slug`
|
||||
|
||||
`site` is the white-label company booking API:
|
||||
|
||||
- company brand payload
|
||||
- published vehicle catalog
|
||||
- booking options
|
||||
- date-range availability
|
||||
- promo validation
|
||||
- booking creation
|
||||
- payment initialization
|
||||
- booking lookup
|
||||
- company contact form
|
||||
|
||||
Both modules deliberately include graceful degradation paths when the database is unavailable so public pages can fail softer than internal operations.
|
||||
|
||||
### Payments
|
||||
|
||||
Rental payments are company-side operational payments, distinct from SaaS subscription billing.
|
||||
|
||||
The `payments` module handles:
|
||||
|
||||
- AmanPay and PayPal rental webhooks
|
||||
- listing payments by company or reservation
|
||||
- initializing online charges
|
||||
- capturing PayPal orders
|
||||
- recording manual payments
|
||||
- refunding successful online payments
|
||||
|
||||
Payment success updates both the payment record and the reservation paid amount.
|
||||
|
||||
### Subscriptions and SaaS billing
|
||||
|
||||
The `subscriptions` module owns the platform billing lifecycle:
|
||||
|
||||
- public plan/provider/feature listing
|
||||
- company subscription read endpoints
|
||||
- trial start
|
||||
- checkout
|
||||
- plan changes
|
||||
- cancellation/resume/reactivation
|
||||
- provider webhooks
|
||||
- admin overrides
|
||||
|
||||
It also exposes background-job style functions for:
|
||||
|
||||
- trial expiration
|
||||
- payment-pending timeout
|
||||
- past-due timeout
|
||||
- suspension timeout
|
||||
- period-end cancellation
|
||||
|
||||
### Notifications
|
||||
|
||||
Notifications are multi-channel and multi-audience:
|
||||
|
||||
- company inbox
|
||||
- renter inbox
|
||||
- unread counts
|
||||
- preferences per type and channel
|
||||
- notification history
|
||||
|
||||
Templates and preference records live in the database; delivery orchestration lives in `services/notificationService.ts`.
|
||||
|
||||
### Admin surface
|
||||
|
||||
The admin router is broad because it covers platform operations across multiple domains:
|
||||
|
||||
- admin authentication and 2FA
|
||||
- company listing, inspection, status changes, deletion, impersonation
|
||||
- renter blocking/unblocking
|
||||
- platform metrics
|
||||
- notification inspection
|
||||
- audit log access
|
||||
- admin-user and permission management
|
||||
- billing account and billing invoice operations
|
||||
- pricing config, plan features, and promotions
|
||||
- subscription overrides and extensions
|
||||
- marketplace homepage configuration
|
||||
|
||||
This is the only route group allowed to work across tenants.
|
||||
|
||||
## Important Workflow Examples
|
||||
|
||||
### 1. Company signup
|
||||
|
||||
`POST /api/v1/auth/company/signup`
|
||||
|
||||
- validates the payload with Zod
|
||||
- checks for duplicate company and owner email addresses
|
||||
- generates a unique slug
|
||||
- hashes the owner password
|
||||
- creates the tenant, owner employee, and initial subscription state in one transaction
|
||||
- sends localized account-created notifications
|
||||
|
||||
### 2. Public booking from a company site
|
||||
|
||||
`POST /api/v1/site/:slug/book`
|
||||
|
||||
- resolves the company from the slug
|
||||
- verifies vehicle availability for the requested date range
|
||||
- upserts the company-scoped customer record
|
||||
- computes total days and base rental price
|
||||
- applies promo code discount if present
|
||||
- applies pricing rules
|
||||
- stores a draft reservation
|
||||
- attaches reservation insurance snapshots and additional-driver snapshots
|
||||
- triggers license validation flags
|
||||
|
||||
Payment is then started with `POST /api/v1/site/:slug/booking/:id/pay`.
|
||||
|
||||
### 3. Reservation lifecycle after booking
|
||||
|
||||
- reservation starts in `DRAFT`
|
||||
- staff confirms it after license compliance checks
|
||||
- vehicle status moves to `RESERVED`
|
||||
- checkin moves reservation to `ACTIVE` and vehicle to `RENTED`
|
||||
- checkout moves reservation to `COMPLETED`, records mileage, and generates a review token
|
||||
- close marks the operational workflow complete and can send the review request email
|
||||
|
||||
## Error Handling and Response Shape
|
||||
|
||||
The API standardizes error handling in `http/errors/errorMiddleware.ts`.
|
||||
|
||||
Special handling exists for:
|
||||
|
||||
- Zod validation errors -> `400 validation_error`
|
||||
- Prisma `P2025` -> `404 not_found`
|
||||
- Prisma `P2002` -> `409 conflict`
|
||||
- custom `AppError` subclasses -> explicit status and machine-readable code
|
||||
|
||||
Successful route handlers usually return:
|
||||
|
||||
```json
|
||||
// Success
|
||||
{ "data": { ... } }
|
||||
|
||||
// Paginated
|
||||
{ "data": [...], "total": 47, "page": 1, "pageSize": 20, "totalPages": 3 }
|
||||
|
||||
// Error
|
||||
{ "error": "vehicle_not_found", "message": "Vehicle not found or access denied", "statusCode": 404 }
|
||||
|
||||
// Subscription error
|
||||
{ "error": "subscription_suspended", "message": "...", "statusCode": 402, "billingUrl": "https://..." }
|
||||
{
|
||||
"data": {}
|
||||
}
|
||||
```
|
||||
|
||||
## Common Query Params
|
||||
`page`, `pageSize` (max 100), `sortBy`, `sortOrder` (asc|desc), `q` (search)
|
||||
Common deviations:
|
||||
|
||||
---
|
||||
- `/health`
|
||||
- `/api/v1/docs`
|
||||
- webhook receipt payloads
|
||||
- file downloads such as admin invoice PDFs
|
||||
|
||||
## Payment Routes — AmanPay + PayPal
|
||||
## Documentation Artifacts
|
||||
|
||||
| Method | Path | Auth | Description |
|
||||
|--------|------|------|-------------|
|
||||
| POST | `/payments/amanpay-webhook` | — (HMAC sig) | AmanPay event webhook (subscriptions + rentals) |
|
||||
| POST | `/payments/paypal/create-order` | 🔒🏢 or 🎫 | Create PayPal order (subscription or rental) |
|
||||
| POST | `/payments/paypal/capture` | 🔒🏢 or 🎫 | Capture PayPal order after approval |
|
||||
| POST | `/payments/paypal/refund` | 🔒🏢💳 | Refund PayPal capture |
|
||||
| GET | `/payments` | 🔒🏢💳 | Payment history (AmanPay + PayPal combined) |
|
||||
| GET | `/payments/:id` | 🔒🏢💳 | Payment detail |
|
||||
| POST | `/payments/:id/refund` | 🔒🏢💳 | Refund (AmanPay or PayPal auto-detected) |
|
||||
There are two documentation layers in the codebase:
|
||||
|
||||
---
|
||||
1. runtime API docs from Swagger/OpenAPI at `/docs` and `/api/v1/openapi.json`
|
||||
2. this markdown document, which explains design decisions, route grouping, and request flow
|
||||
|
||||
## Documents — Contracts & Invoices (On-Demand PDF Generation)
|
||||
|
||||
> **Design rule:** PDFs are NEVER stored. Every request hits the DB, assembles data, renders PDF via `@react-pdf/renderer`, and streams the result. Reference numbers (`contractNumber`, `invoiceNumber`) ARE stored in the DB and assigned once via atomic transaction.
|
||||
|
||||
| Method | Path | Auth | Description |
|
||||
|--------|------|------|-------------|
|
||||
| GET | `/documents/reservations/:id/contract` | 🔒🏢💳 | Generate + stream contract PDF. `?download=true` forces file download vs inline view. |
|
||||
| GET | `/documents/reservations/:id/invoice` | 🔒🏢💳 | Generate + stream invoice PDF. |
|
||||
| POST | `/documents/reservations/:id/send` | 🔒🏢💳 | Generate + email PDF(s) as attachment. Body: `{ type: "contract"|"invoice"|"both", email?: string }` |
|
||||
| GET | `/documents/settings` | 🔒🏢💳 | Get company's contract settings (terms, prefixes, tax config) |
|
||||
| PATCH | `/documents/settings` | 🔒🏢💳 | Update contract settings. Sequence counters are protected from API reset. |
|
||||
| GET | `/documents/settings/preview-contract` | 🔒🏢💳 | Preview contract PDF with sample data (for settings page preview) |
|
||||
| GET | `/documents/settings/preview-invoice` | 🔒🏢💳 | Preview invoice PDF with sample data |
|
||||
| GET | `/renter/reservations/:id/contract` | 🎫 | Renter downloads their own contract (renterId must match) |
|
||||
| GET | `/renter/reservations/:id/invoice` | 🎫 | Renter downloads their own invoice |
|
||||
|
||||
---
|
||||
|
||||
## Damage Inspections
|
||||
|
||||
| Method | Path | Auth | Description |
|
||||
|--------|------|------|-------------|
|
||||
| POST | `/reservations/:id/inspections` | 🔒🏢💳 | Create check-in or check-out inspection (with damage points) |
|
||||
| GET | `/reservations/:id/inspections` | 🔒🏢💳 | Get both inspections (checkin + checkout) |
|
||||
| GET | `/reservations/:id/inspections/checkin` | 🔒🏢💳 | Check-in inspection detail |
|
||||
| GET | `/reservations/:id/inspections/checkout` | 🔒🏢💳 | Check-out inspection detail |
|
||||
|
||||
---
|
||||
|
||||
## Billing (per-reservation line items)
|
||||
|
||||
| Method | Path | Auth | Description |
|
||||
|--------|------|------|-------------|
|
||||
| GET | `/reservations/:id/billing` | 🔒🏢💳 | Get full itemized billing breakdown (JSON — for invoice preview) |
|
||||
| PATCH | `/reservations/:id/billing` | 🔒🏢💳 | Add damage charges and extras to reservation |
|
||||
|
||||
---
|
||||
|
||||
## Accounting Reports
|
||||
|
||||
| Method | Path | Auth | Description |
|
||||
|--------|------|------|-------------|
|
||||
| GET | `/accounting/settings` | 🔒🏢💳 | Get company accounting settings |
|
||||
| PATCH | `/accounting/settings` | 🔒🏢💳 | Update accounting settings (period, accountant, taxes, fuel policy) |
|
||||
| GET | `/accounting/report` | 🔒🏢💳 | Get accounting report as JSON `?period=MONTHLY&from=&to=` |
|
||||
| GET | `/accounting/report/pdf` | 🔒🏢💳 | Stream accounting report PDF |
|
||||
| GET | `/accounting/report/csv` | 🔒🏢💳 | Stream CSV file |
|
||||
| POST | `/accounting/report/send` | 🔒🏢💳 | Email report to accountant `{ period, from, to, format }` |
|
||||
| GET | `/accounting/report/periods` | 🔒🏢💳 | List available periods with labels and date ranges |
|
||||
|
||||
---
|
||||
|
||||
## Admin Panel Routes (admin.RentalDriveGo.com → api.RentalDriveGo.com/admin)
|
||||
|
||||
> All require admin JWT. Role hierarchy: VIEWER < FINANCE < SUPPORT < ADMIN < SUPER_ADMIN
|
||||
|
||||
### Admin Auth
|
||||
| Method | Path | Role | Description |
|
||||
|--------|------|------|-------------|
|
||||
| POST | `/admin/auth/login` | — | Login `{ email, password, totpCode? }` |
|
||||
| POST | `/admin/auth/logout` | VIEWER | Invalidate token |
|
||||
| GET | `/admin/auth/me` | VIEWER | Current admin profile |
|
||||
| PATCH | `/admin/auth/me` | VIEWER | Update name/password |
|
||||
| POST | `/admin/auth/2fa/setup` | VIEWER | Generate TOTP secret + QR code |
|
||||
| POST | `/admin/auth/2fa/verify` | VIEWER | Verify and enable 2FA |
|
||||
|
||||
### Admin User Management (SUPER_ADMIN only)
|
||||
| Method | Path | Role | Description |
|
||||
|--------|------|------|-------------|
|
||||
| GET | `/admin/users` | SUPER_ADMIN | List admin users |
|
||||
| POST | `/admin/users` | SUPER_ADMIN | Create admin user |
|
||||
| PATCH | `/admin/users/:id` | SUPER_ADMIN | Edit name, role |
|
||||
| POST | `/admin/users/:id/deactivate` | SUPER_ADMIN | Deactivate admin |
|
||||
| POST | `/admin/users/:id/reset-password` | SUPER_ADMIN | Force password reset |
|
||||
| PATCH | `/admin/users/:id/permissions` | SUPER_ADMIN | Override permissions |
|
||||
|
||||
### Company Management (SUPPORT+)
|
||||
| Method | Path | Role | Description |
|
||||
|--------|------|------|-------------|
|
||||
| GET | `/admin/companies` | SUPPORT | List all companies |
|
||||
| POST | `/admin/companies` | SUPPORT | Create company |
|
||||
| GET | `/admin/companies/:id` | SUPPORT | Company detail |
|
||||
| PATCH | `/admin/companies/:id` | SUPPORT | Edit company |
|
||||
| POST | `/admin/companies/:id/suspend` | SUPPORT | Suspend `{ reason }` |
|
||||
| POST | `/admin/companies/:id/reactivate` | SUPPORT | Reactivate |
|
||||
| DELETE | `/admin/companies/:id` | ADMIN | Delete company |
|
||||
| PATCH | `/admin/companies/:id/subscription` | SUPPORT | Override subscription |
|
||||
| POST | `/admin/companies/:id/invoices/:invoiceId/mark-paid` | ADMIN | Manual mark paid |
|
||||
|
||||
### Company Staff Management (SUPPORT+)
|
||||
| Method | Path | Role | Description |
|
||||
|--------|------|------|-------------|
|
||||
| GET | `/admin/companies/:id/employees` | SUPPORT | List employees |
|
||||
| POST | `/admin/companies/:id/employees` | SUPPORT | Add employee |
|
||||
| GET | `/admin/companies/:id/employees/:empId` | SUPPORT | Employee detail |
|
||||
| PATCH | `/admin/companies/:id/employees/:empId` | SUPPORT | Edit employee |
|
||||
| PATCH | `/admin/companies/:id/employees/:empId/role` | SUPPORT | Change role |
|
||||
| POST | `/admin/companies/:id/employees/:empId/deactivate` | SUPPORT | Deactivate |
|
||||
| POST | `/admin/companies/:id/employees/:empId/reactivate` | SUPPORT | Reactivate |
|
||||
| POST | `/admin/companies/:id/employees/:empId/reset-password` | SUPPORT | Send reset |
|
||||
| POST | `/admin/companies/:id/employees/:empId/impersonate` | ADMIN | Generate impersonation token |
|
||||
|
||||
### Renters, Metrics, Audit
|
||||
| Method | Path | Role | Description |
|
||||
|--------|------|------|-------------|
|
||||
| GET | `/admin/renters` | SUPPORT | List renters |
|
||||
| PATCH | `/admin/renters/:id` | SUPPORT | Update renter |
|
||||
| POST | `/admin/renters/:id/block` | SUPPORT | Block `{ reason }` |
|
||||
| POST | `/admin/renters/:id/unblock` | SUPPORT | Unblock |
|
||||
| GET | `/admin/metrics` | FINANCE | Platform KPIs |
|
||||
| GET | `/admin/metrics/subscriptions` | FINANCE | Plan breakdown |
|
||||
| GET | `/admin/audit-logs` | ADMIN | Paginated audit log |
|
||||
| GET | `/admin/audit-logs/export` | ADMIN | CSV export |
|
||||
|
||||
---
|
||||
|
||||
## Advanced Features — New Routes
|
||||
|
||||
### Damage Reports
|
||||
|
||||
| Method | Path | Auth | Description |
|
||||
|--------|------|------|-------------|
|
||||
| GET | `/reservations/:id/damage-reports` | 🔒🏢💳 | Get check-in + check-out damage reports |
|
||||
| POST | `/reservations/:id/damage-reports` | 🔒🏢💳 | Save damage report. Body: `{ type: "CHECKIN"|"CHECKOUT", damages: DamageZone[], fuelLevel, mileage, photos[], inspectedBy, customerSignedAt, customerName }` |
|
||||
| PATCH | `/reservations/:id/damage-reports/:type` | 🔒🏢💳 | Update a damage report (before finalizing) |
|
||||
|
||||
### Insurance Policies (Company configuration)
|
||||
|
||||
| Method | Path | Auth | Description |
|
||||
|--------|------|------|-------------|
|
||||
| GET | `/insurance-policies` | 🔒🏢💳 | List company's insurance policies |
|
||||
| POST | `/insurance-policies` | 🔒🏢💳 | Create policy |
|
||||
| PATCH | `/insurance-policies/:id` | 🔒🏢💳 | Update policy |
|
||||
| DELETE | `/insurance-policies/:id` | 🔒🏢💳 | Deactivate policy |
|
||||
| GET | `/reservations/:id/insurances` | 🔒🏢💳 | Insurance selections for a reservation |
|
||||
| POST | `/reservations/:id/insurances` | 🔒🏢💳 | Apply insurance(s) to reservation. Body: `{ policyIds: string[] }` |
|
||||
|
||||
### Additional Drivers
|
||||
|
||||
| Method | Path | Auth | Description |
|
||||
|--------|------|------|-------------|
|
||||
| GET | `/reservations/:id/additional-drivers` | 🔒🏢💳 | List additional drivers |
|
||||
| POST | `/reservations/:id/additional-drivers` | 🔒🏢💳 | Add additional driver. Body: `{ firstName, lastName, driverLicense, licenseExpiry, dateOfBirth, ... }` |
|
||||
| PATCH | `/reservations/:id/additional-drivers/:did` | 🔒🏢💳 | Update driver info |
|
||||
| DELETE | `/reservations/:id/additional-drivers/:did` | 🔒🏢💳 | Remove additional driver |
|
||||
| POST | `/reservations/:id/additional-drivers/:did/approve-license` | 🔒🏢💳 (OWNER/MANAGER) | Approve flagged license. Body: `{ decision: "APPROVE"|"DENY", note }` |
|
||||
|
||||
### Driver License Validation
|
||||
|
||||
| Method | Path | Auth | Description |
|
||||
|--------|------|------|-------------|
|
||||
| POST | `/customers/:id/validate-license` | 🔒🏢💳 | Validate license expiry + flag if <3 months. Returns `{ status, daysUntilExpiry, requiresApproval }` |
|
||||
| POST | `/customers/:id/approve-license` | 🔒🏢💳 (OWNER/MANAGER) | Approve or deny flagged license. Body: `{ decision: "APPROVE"|"DENY", note }` |
|
||||
|
||||
### Pricing Rules
|
||||
|
||||
| Method | Path | Auth | Description |
|
||||
|--------|------|------|-------------|
|
||||
| GET | `/pricing-rules` | 🔒🏢💳 | List company's pricing rules |
|
||||
| POST | `/pricing-rules` | 🔒🏢💳 | Create rule (age surcharge, license discount, etc.) |
|
||||
| PATCH | `/pricing-rules/:id` | 🔒🏢💳 | Update rule |
|
||||
| DELETE | `/pricing-rules/:id` | 🔒🏢💳 | Deactivate rule |
|
||||
| POST | `/pricing-rules/preview` | 🔒🏢💳 | Preview rules applied to a hypothetical booking. Body: `{ customerId, additionalDrivers, dailyRate, totalDays }` |
|
||||
|
||||
### Financial Reporting (Accountant Export)
|
||||
|
||||
| Method | Path | Auth | Description |
|
||||
|--------|------|------|-------------|
|
||||
| GET | `/reports/financial` | 🔒🏢💳 | Financial report. `?period=WEEK|MONTH|YEAR&startDate=&endDate=&format=JSON|CSV` |
|
||||
| GET | `/reports/financial/summary` | 🔒🏢💳 | Aggregate totals only (for dashboard KPIs) |
|
||||
|
||||
### Admin Panel Routes
|
||||
|
||||
Base path: `/api/v1/admin/` — requires `requireAdminAuth` middleware (separate JWT from company auth)
|
||||
|
||||
| Method | Path | Role | Description |
|
||||
|--------|------|------|-------------|
|
||||
| POST | `/admin/auth/login` | — | Admin login |
|
||||
| GET | `/admin/auth/me` | Any | Admin profile |
|
||||
| GET | `/admin/companies` | Any | All companies `?q=&status=&plan=` |
|
||||
| POST | `/admin/companies` | ADMIN+ | Create company |
|
||||
| GET | `/admin/companies/:id` | Any | Full company detail |
|
||||
| PATCH | `/admin/companies/:id` | ADMIN+ | Edit company profile |
|
||||
| PATCH | `/admin/companies/:id/status` | ADMIN+ | Change status |
|
||||
| PATCH | `/admin/companies/:id/plan` | ADMIN+ | Change plan (bypasses payment) |
|
||||
| DELETE | `/admin/companies/:id` | SUPER_ADMIN | Hard delete |
|
||||
| POST | `/admin/companies/:id/impersonate` | ADMIN+ | Get 30-min impersonation token |
|
||||
| GET | `/admin/companies/:id/employees` | Any | List employees |
|
||||
| POST | `/admin/companies/:id/employees` | ADMIN+ | Add employee |
|
||||
| PATCH | `/admin/companies/:id/employees/:eid` | ADMIN+ | Edit employee |
|
||||
| PATCH | `/admin/companies/:id/employees/:eid/role` | ADMIN+ | Change role |
|
||||
| DELETE | `/admin/companies/:id/employees/:eid` | ADMIN+ | Deactivate |
|
||||
| POST | `/admin/companies/:id/employees/:eid/reset-password` | ADMIN+ | Trigger reset email |
|
||||
| GET | `/admin/metrics` | Any | Platform MRR, churn, signups |
|
||||
| GET | `/admin/admins` | SUPER_ADMIN | List admin users |
|
||||
| POST | `/admin/admins` | SUPER_ADMIN | Create admin |
|
||||
| PATCH | `/admin/admins/:id/role` | SUPER_ADMIN | Change admin role |
|
||||
| DELETE | `/admin/admins/:id` | SUPER_ADMIN | Deactivate admin |
|
||||
| GET | `/admin/audit-log` | SUPER_ADMIN | Audit log `?adminId=&targetType=&startDate=&endDate=` |
|
||||
The OpenAPI file is useful for request/response contracts, but it is not yet a perfect mirror of every newer route. This document should be updated together with `app.ts`, route files, and the OpenAPI generator whenever route groups change.
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
The seed script skips creation if the email already exists. The
|
||||
most flexible approach is a direct API call (if you have a token)
|
||||
or a one-liner script. Here are both options:
|
||||
|
||||
Option 1 — via API (requires an existing admin to be logged in
|
||||
first):
|
||||
|
||||
curl -s -X POST http://localhost:4000/api/v1/admin/admins \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "Authorization: Bearer <your_admin_token>" \
|
||||
-d '{
|
||||
"email": "newadmin@example.com",
|
||||
"firstName": "First",
|
||||
"lastName": "Last",
|
||||
"role": "ADMIN",
|
||||
"password": "yourpassword123"
|
||||
}'
|
||||
|
||||
Option 2 — directly in the database (no token needed, works any
|
||||
time):
|
||||
|
||||
DATABASE_URL="postgresql://postgres:password@localhost:5432/renta
|
||||
ldrivego" node -e "
|
||||
const { PrismaClient } =
|
||||
require('./packages/database/generated');
|
||||
const bcrypt = require('./node_modules/bcryptjs');
|
||||
const p = new PrismaClient();
|
||||
const EMAIL = 'newadmin@example.com';
|
||||
const PASSWORD = 'yourpassword123';
|
||||
const FIRST = 'First';
|
||||
const LAST = 'Last';
|
||||
const ROLE = 'SUPER_ADMIN'; // SUPER_ADMIN | ADMIN | SUPPORT |
|
||||
FINANCE | VIEWER
|
||||
bcrypt.hash(PASSWORD, 12).then(hash =>
|
||||
p.adminUser.create({ data: { email: EMAIL, firstName: FIRST,
|
||||
lastName: LAST, passwordHash: hash, role: ROLE, isActive: true }
|
||||
})
|
||||
).then(a => { console.log('Created:', a.email, a.role);
|
||||
p.\$disconnect(); })
|
||||
.catch(e => { console.error('Error:', e.message);
|
||||
p.\$disconnect(); });
|
||||
"
|
||||
|
||||
From inside Docker:
|
||||
|
||||
docker exec rentaldrivego-dev-api-1 node -e "
|
||||
const { PrismaClient } =
|
||||
require('/app/packages/database/generated');
|
||||
const bcrypt = require('/app/node_modules/bcryptjs');
|
||||
const p = new PrismaClient();
|
||||
bcrypt.hash('yourpassword123', 12).then(hash =>
|
||||
p.adminUser.create({ data: { email: 'newadmin@example.com',
|
||||
firstName: 'First', lastName: 'Last', passwordHash: hash, role:
|
||||
'SUPER_ADMIN', isActive: true } })
|
||||
).then(a => { console.log('Created:', a.email, a.role);
|
||||
p.\$disconnect(); })
|
||||
.catch(e => { console.error('Error:', e.message);
|
||||
p.\$disconnect(); });
|
||||
"
|
||||
|
||||
Replace email, password, firstName, lastName, and role as needed.
|
||||
Available roles: SUPER_ADMIN, ADMIN, SUPPORT, FINANCE, VIEWER.
|
||||
@@ -0,0 +1,699 @@
|
||||
# Contrat de Location de Véhicule
|
||||
|
||||
**Contrat n° :** [●]
|
||||
**Date de signature :** [●]
|
||||
**Lieu de signature :** [Ville, Maroc]
|
||||
|
||||
---
|
||||
|
||||
## 1. Parties au contrat
|
||||
|
||||
Entre les soussignés :
|
||||
|
||||
### Le Loueur / Agence
|
||||
|
||||
**Nom commercial :** [●]
|
||||
**Raison sociale :** [●]
|
||||
**Forme juridique :** [●]
|
||||
**RC n° :** [●]
|
||||
**ICE n° :** [●]
|
||||
**IF n° :** [●]
|
||||
**Agrément / Autorisation d’exercice n° :** [●]
|
||||
**Date de délivrance de l’agrément :** [●]
|
||||
**Autorité délivrante :** [●]
|
||||
**Adresse :** [●]
|
||||
**Téléphone :** [●]
|
||||
**Email :** [●]
|
||||
**Représenté par :** [Nom, prénom, fonction]
|
||||
|
||||
Ci-après désigné **« le Loueur »**,
|
||||
|
||||
Et :
|
||||
|
||||
### Le Locataire / Conducteur principal
|
||||
|
||||
**Nom :** [●]
|
||||
**Prénom :** [●]
|
||||
**Date de naissance :** [●]
|
||||
**Nationalité :** [●]
|
||||
**CIN / Passeport n° :** [●]
|
||||
**Adresse complète :** [●]
|
||||
**Téléphone :** [●]
|
||||
**Email :** [●]
|
||||
|
||||
Ci-après désigné **« le Locataire »**.
|
||||
|
||||
Le Loueur et le Locataire sont ensemble désignés **« les Parties »**.
|
||||
|
||||
---
|
||||
|
||||
## 2. Responsable légal et conformité réglementaire du Loueur
|
||||
|
||||
Le Loueur déclare exercer l’activité de location de véhicules sans chauffeur conformément à la réglementation marocaine applicable.
|
||||
|
||||
Le Loueur reconnaît que son activité doit respecter les textes mentionnés dans la section **Références légales et réglementaires applicables** du présent document.
|
||||
|
||||
La personne responsable de l’activité est :
|
||||
|
||||
**Nom et prénom :** [●]
|
||||
**Qualité :** [Dirigeant / Actionnaire / Salarié]
|
||||
**Pièce d’identité n° :** [●]
|
||||
**Qualification / diplôme / expérience :** [●]
|
||||
**Téléphone :** [●]
|
||||
**Email :** [●]
|
||||
|
||||
Cette personne est responsable du suivi des contrats, de l’entretien des véhicules, de leur conformité aux normes de sécurité routière et du respect des obligations administratives applicables.
|
||||
|
||||
Le Loueur déclare notamment :
|
||||
|
||||
- disposer des autorisations administratives requises pour l’exercice de l’activité ;
|
||||
- être régulièrement immatriculé auprès des administrations compétentes ;
|
||||
- disposer d’un siège social ou local professionnel déclaré ;
|
||||
- respecter les obligations sociales, fiscales et administratives applicables ;
|
||||
- exploiter une flotte conforme aux seuils et conditions réglementaires applicables ;
|
||||
- maintenir les véhicules loués en état conforme aux normes de sécurité routière.
|
||||
|
||||
---
|
||||
|
||||
## 3. Permis de conduire
|
||||
|
||||
Le Locataire déclare être titulaire d’un permis de conduire valide :
|
||||
|
||||
**Numéro du permis :** [●]
|
||||
**Catégorie :** [B / autre]
|
||||
**Date de délivrance :** [●]
|
||||
**Date d’expiration :** [●]
|
||||
**Pays de délivrance :** [●]
|
||||
**Permis international, le cas échéant :** [Oui / Non, n° ●]
|
||||
|
||||
Le Locataire garantit que son permis est valide pendant toute la durée de location et qu’il n’est frappé d’aucune suspension, annulation ou restriction incompatible avec la conduite du véhicule loué.
|
||||
|
||||
---
|
||||
|
||||
## 4. Conducteur(s) autorisé(s)
|
||||
|
||||
Le véhicule ne peut être conduit que par le Locataire et, le cas échéant, par les conducteurs additionnels expressément mentionnés ci-dessous :
|
||||
|
||||
### Conducteur additionnel 1
|
||||
|
||||
**Nom et prénom :** [●]
|
||||
**CIN / Passeport :** [●]
|
||||
**Permis n° :** [●]
|
||||
**Catégorie :** [●]
|
||||
**Date de délivrance :** [●]
|
||||
**Téléphone :** [●]
|
||||
|
||||
Tout conducteur non déclaré est interdit. En cas d’accident, vol, infraction ou dommage causé par un conducteur non autorisé, le Locataire assume l’entière responsabilité des conséquences financières, civiles, pénales et assurantielles.
|
||||
|
||||
---
|
||||
|
||||
## 5. Véhicule loué
|
||||
|
||||
Le Loueur met à disposition du Locataire le véhicule suivant :
|
||||
|
||||
**Marque :** [●]
|
||||
**Modèle :** [●]
|
||||
**Année :** [●]
|
||||
**Couleur :** [●]
|
||||
**Immatriculation :** [●]
|
||||
**Numéro de châssis :** [●]
|
||||
**Propriétaire du véhicule :** [Loueur / Autre agence / Société de financement / Autre]
|
||||
**Justificatif d’exploitation si le véhicule appartient à un tiers :** [Contrat / Autorisation / Mandat / Autre]
|
||||
**Type de motorisation :** [Thermique / Hybride / Électrique]
|
||||
**Date de première mise en circulation :** [●]
|
||||
**Âge du véhicule à la date de location :** [●]
|
||||
**Véhicule conforme aux limites réglementaires d’exploitation :** [Oui / Non]
|
||||
**Kilométrage au départ :** [●] km
|
||||
**Niveau de carburant au départ :** [●]
|
||||
**Carte grise :** [Oui / Non]
|
||||
**Assurance :** [Oui / Non]
|
||||
**Visite technique :** [Oui / Non / Non applicable]
|
||||
|
||||
Un état des lieux contradictoire au départ et au retour est annexé au présent contrat et en fait partie intégrante.
|
||||
|
||||
Le Loueur déclare que le véhicule mis à disposition est conforme aux conditions réglementaires d’exploitation applicables à sa catégorie, notamment en matière d’âge, d’immatriculation, d’assurance, d’entretien et de sécurité routière.
|
||||
|
||||
|
||||
---
|
||||
|
||||
## 6. Entretien et conformité du véhicule
|
||||
|
||||
Le Loueur déclare que le véhicule est régulièrement entretenu, assuré, immatriculé et conforme aux normes de sécurité routière applicables.
|
||||
|
||||
Le Locataire reconnaît avoir reçu un véhicule en état apparent de fonctionnement, sous réserve des observations mentionnées dans l’état des lieux de départ.
|
||||
|
||||
En cas d’anomalie mécanique, voyant d’alerte, bruit anormal, crevaison, panne ou incident susceptible d’affecter la sécurité, le Locataire doit cesser l’utilisation du véhicule dès que les conditions de sécurité le permettent et informer immédiatement le Loueur.
|
||||
|
||||
---
|
||||
|
||||
## 7. Durée de location
|
||||
|
||||
La location commence le :
|
||||
|
||||
**Date :** [●]
|
||||
**Heure :** [●]
|
||||
**Lieu de prise en charge :** [●]
|
||||
|
||||
La location prend fin le :
|
||||
|
||||
**Date :** [●]
|
||||
**Heure :** [●]
|
||||
**Lieu de restitution :** [●]
|
||||
|
||||
Toute prolongation doit être acceptée préalablement par écrit par le Loueur. À défaut, le véhicule sera considéré comme conservé sans autorisation, et le Loueur pourra facturer les jours supplémentaires, pénalités de retard et frais éventuels.
|
||||
|
||||
---
|
||||
|
||||
## 8. Prix de location et modalités de paiement
|
||||
|
||||
Le prix de location est fixé comme suit :
|
||||
|
||||
| Désignation | Montant |
|
||||
|---|---:|
|
||||
| Tarif journalier HT | [●] MAD |
|
||||
| Nombre de jours | [●] |
|
||||
| Sous-total HT | [●] MAD |
|
||||
| TVA applicable | [●] % |
|
||||
| Montant TVA | [●] MAD |
|
||||
| Total TTC | [●] MAD |
|
||||
| Conducteur additionnel | [●] MAD |
|
||||
| Livraison / récupération | [●] MAD |
|
||||
| Siège enfant | [●] MAD |
|
||||
| GPS / accessoires | [●] MAD |
|
||||
| Autres frais | [●] MAD |
|
||||
|
||||
**Montant total à payer TTC : [●] MAD**
|
||||
|
||||
**Mode de paiement :** [Espèces / carte bancaire / virement / chèque / autre]
|
||||
**Date de paiement :** [●]
|
||||
**Référence de paiement :** [●]
|
||||
|
||||
Le Locataire reconnaît avoir reçu une information claire sur le prix total TTC avant la signature du contrat.
|
||||
|
||||
---
|
||||
|
||||
## 9. Dépôt de garantie / caution
|
||||
|
||||
Le Locataire verse au Loueur un dépôt de garantie de :
|
||||
|
||||
**Montant : [●] MAD**
|
||||
|
||||
**Forme de la caution :** [Préautorisation bancaire / espèces / chèque / autre]
|
||||
**Date de constitution :** [●]
|
||||
**Conditions de libération :** [●]
|
||||
|
||||
Le dépôt de garantie garantit notamment :
|
||||
|
||||
- les dommages non couverts par l’assurance ;
|
||||
- la franchise d’assurance ;
|
||||
- les kilomètres supplémentaires ;
|
||||
- le carburant manquant ;
|
||||
- les frais de nettoyage exceptionnel ;
|
||||
- les contraventions, amendes, péages ou frais administratifs ;
|
||||
- les retards de restitution ;
|
||||
- la perte de documents, clés, accessoires ou équipements.
|
||||
|
||||
La caution sera restituée après vérification de l’état du véhicule, du kilométrage, du carburant, des éventuelles infractions et des sommes restant dues.
|
||||
|
||||
---
|
||||
|
||||
## 10. Assurance
|
||||
|
||||
Le véhicule est assuré auprès de :
|
||||
|
||||
**Compagnie d’assurance :** [●]
|
||||
**Numéro de police :** [●]
|
||||
**Type de couverture :** [Responsabilité civile / Tous risques / autre]
|
||||
**Franchise applicable :** [●] MAD
|
||||
**Assistance :** [Oui / Non]
|
||||
**Numéro d’assistance :** [●]
|
||||
|
||||
Le Locataire reconnaît avoir été informé des garanties, exclusions et franchises applicables.
|
||||
|
||||
Sont notamment exclus de la couverture, sauf stipulation contraire de l’assureur :
|
||||
|
||||
- conduite par une personne non autorisée ;
|
||||
- conduite sans permis valide ;
|
||||
- conduite sous l’emprise d’alcool, stupéfiants ou substances interdites ;
|
||||
- usage du véhicule hors des voies autorisées ;
|
||||
- participation à des courses, essais ou compétitions ;
|
||||
- négligence grave ;
|
||||
- fausse déclaration ;
|
||||
- absence de déclaration d’accident dans les délais ;
|
||||
- vol avec clés laissées dans le véhicule ;
|
||||
- transport illicite de personnes ou de marchandises.
|
||||
|
||||
---
|
||||
|
||||
## 11. Kilométrage
|
||||
|
||||
**Kilométrage inclus :** [●] km par jour / [●] km pour toute la durée de location.
|
||||
**Kilométrage au départ :** [●] km.
|
||||
**Kilométrage au retour :** [●] km.
|
||||
|
||||
Tout kilomètre supplémentaire sera facturé au tarif de :
|
||||
|
||||
**[●] MAD TTC par kilomètre supplémentaire.**
|
||||
|
||||
Le kilométrage fait foi sur la base du compteur du véhicule, constaté dans l’état des lieux de départ et de retour.
|
||||
|
||||
---
|
||||
|
||||
## 12. Utilisation du véhicule
|
||||
|
||||
Le Locataire s’engage à utiliser le véhicule avec prudence, conformément à sa destination normale et à la législation marocaine.
|
||||
|
||||
Il est interdit notamment :
|
||||
|
||||
- de sous-louer le véhicule ;
|
||||
- de transporter des marchandises dangereuses ou illicites ;
|
||||
- d’utiliser le véhicule pour des activités illégales ;
|
||||
- de participer à des compétitions ou essais sportifs ;
|
||||
- de tracter un autre véhicule sans autorisation écrite ;
|
||||
- de circuler hors du territoire marocain sans autorisation écrite du Loueur ;
|
||||
- de modifier le véhicule ou ses équipements ;
|
||||
- de fumer dans le véhicule si l’agence l’interdit ;
|
||||
- de transporter un nombre de passagers supérieur à celui autorisé.
|
||||
|
||||
Le Locataire est responsable des infractions au Code de la route commises pendant la période de location.
|
||||
|
||||
---
|
||||
|
||||
## 13. Restitution du véhicule
|
||||
|
||||
Le Locataire doit restituer le véhicule à la date, à l’heure et au lieu prévus au contrat, dans l’état où il lui a été remis, sous réserve de l’usure normale.
|
||||
|
||||
Le véhicule doit être restitué avec :
|
||||
|
||||
- le même niveau de carburant qu’au départ ;
|
||||
- les papiers du véhicule ;
|
||||
- les clés ;
|
||||
- les accessoires et équipements remis ;
|
||||
- un état de propreté normal ;
|
||||
- aucun dommage nouveau non déclaré.
|
||||
|
||||
En cas de retard, le Loueur pourra facturer :
|
||||
|
||||
- [●] MAD par heure de retard ; ou
|
||||
- une journée supplémentaire au tarif contractuel au-delà de [●] heures de retard.
|
||||
|
||||
En cas de carburant manquant, les frais seront facturés selon le coût réel augmenté de frais de service de [●] MAD.
|
||||
|
||||
En cas de salissure excessive, le Loueur pourra facturer des frais de nettoyage de [●] MAD à [●] MAD selon l’état du véhicule.
|
||||
|
||||
---
|
||||
|
||||
## 14. État des lieux départ et retour
|
||||
|
||||
Les Parties établissent un état des lieux au départ et au retour. Celui-ci peut être accompagné de photographies datées du véhicule, du compteur, du carburant, de la carrosserie, de l’intérieur, des pneus, des vitres et des équipements.
|
||||
|
||||
Les éléments à contrôler comprennent notamment :
|
||||
|
||||
- Kilométrage compteur ;
|
||||
- Niveau de carburant ;
|
||||
- Carrosserie ;
|
||||
- Pare-chocs ;
|
||||
- Rétroviseurs ;
|
||||
- Phares et feux ;
|
||||
- Pare-brise et vitres ;
|
||||
- Pneus et jantes ;
|
||||
- Intérieur et sièges ;
|
||||
- Tableau de bord ;
|
||||
- Roue de secours ;
|
||||
- Outillage ;
|
||||
- Documents du véhicule ;
|
||||
- Clés et accessoires.
|
||||
|
||||
Tout dommage constaté au retour et non mentionné dans l’état des lieux de départ pourra être facturé au Locataire, sous réserve des garanties d’assurance applicables.
|
||||
|
||||
---
|
||||
|
||||
## 15. Accident, panne, vol ou sinistre
|
||||
|
||||
En cas d’accident, panne, vol, tentative de vol, incendie ou tout autre sinistre, le Locataire doit immédiatement :
|
||||
|
||||
- informer le Loueur par téléphone et par écrit ;
|
||||
- prévenir les autorités compétentes si nécessaire ;
|
||||
- établir un constat amiable en cas d’accident ;
|
||||
- obtenir un procès-verbal ou document officiel en cas de vol, blessure, délit ou dommage important ;
|
||||
- ne pas abandonner le véhicule sans autorisation du Loueur ;
|
||||
- ne pas reconnaître de responsabilité sans accord préalable du Loueur ou de l’assureur.
|
||||
|
||||
Le Locataire doit transmettre au Loueur tous les documents utiles dans un délai maximum de **48 heures** à compter du sinistre.
|
||||
|
||||
En cas de non-respect de cette procédure, le Locataire pourra être tenu responsable des conséquences financières, notamment si l’assureur refuse sa garantie.
|
||||
|
||||
---
|
||||
|
||||
## 16. Responsabilité du Locataire
|
||||
|
||||
Le Locataire est responsable :
|
||||
|
||||
- des dommages causés au véhicule pendant la période de location ;
|
||||
- des dommages causés aux tiers dans les limites prévues par la loi et l’assurance ;
|
||||
- des amendes, contraventions, frais de fourrière, péages et frais administratifs ;
|
||||
- des frais liés à une mauvaise utilisation du véhicule ;
|
||||
- des pertes de clés, documents ou accessoires ;
|
||||
- des dommages non couverts par l’assurance ;
|
||||
- de la franchise prévue au contrat d’assurance.
|
||||
|
||||
Le Locataire reste responsable jusqu’à la restitution effective du véhicule au Loueur et la signature de l’état des lieux de retour.
|
||||
|
||||
---
|
||||
|
||||
## 17. Infractions, amendes et frais administratifs
|
||||
|
||||
Toutes les infractions commises pendant la durée de location sont à la charge du Locataire.
|
||||
|
||||
Le Loueur pourra communiquer l’identité du Locataire aux autorités compétentes et facturer au Locataire :
|
||||
|
||||
- le montant des amendes ;
|
||||
- les frais de dossier ;
|
||||
- les frais de fourrière ;
|
||||
- les frais de notification ;
|
||||
- tout coût lié au traitement administratif de l’infraction.
|
||||
|
||||
**Frais administratifs par infraction : [●] MAD TTC.**
|
||||
|
||||
---
|
||||
|
||||
## 18. Annulation, non-présentation et résiliation
|
||||
|
||||
En cas d’annulation par le Locataire :
|
||||
|
||||
- plus de [●] heures avant le départ : [conditions de remboursement] ;
|
||||
- moins de [●] heures avant le départ : [conditions] ;
|
||||
- non-présentation : [conditions].
|
||||
|
||||
Le Loueur peut résilier immédiatement le contrat, sans indemnité pour le Locataire, en cas de :
|
||||
|
||||
- fausse déclaration ;
|
||||
- permis invalide ;
|
||||
- défaut de paiement ;
|
||||
- utilisation interdite du véhicule ;
|
||||
- conduite dangereuse ;
|
||||
- non-respect grave du présent contrat ;
|
||||
- suspicion légitime de fraude ou d’usage illicite.
|
||||
|
||||
Dans ce cas, le Locataire doit restituer immédiatement le véhicule.
|
||||
|
||||
---
|
||||
|
||||
## 19. Indisponibilité administrative, réglementaire ou technique
|
||||
|
||||
En cas d’indisponibilité du véhicule ou d’impossibilité d’exécuter la location pour une raison administrative, réglementaire, technique ou de sécurité, le Loueur pourra proposer au Locataire un véhicule équivalent, sous réserve de disponibilité.
|
||||
|
||||
À défaut de véhicule équivalent accepté par le Locataire, les sommes payées au titre de la période non exécutée seront remboursées, sans préjudice des droits légalement reconnus aux Parties.
|
||||
|
||||
---
|
||||
|
||||
## 20. Données personnelles
|
||||
|
||||
Le Locataire autorise le Loueur à collecter et conserver les données nécessaires à l’exécution du présent contrat, notamment son identité, ses coordonnées, son permis de conduire, les informations de paiement, les documents liés au véhicule, les états des lieux et les données relatives aux éventuels incidents.
|
||||
|
||||
Ces données sont utilisées pour :
|
||||
|
||||
- l’exécution du contrat ;
|
||||
- la facturation ;
|
||||
- la gestion des sinistres ;
|
||||
- la gestion des infractions ;
|
||||
- les obligations comptables, fiscales et légales ;
|
||||
- la défense des droits du Loueur en cas de litige.
|
||||
|
||||
Le Loueur s’engage à protéger les données personnelles du Locataire, à limiter leur accès aux personnes habilitées et à les conserver uniquement pendant la durée nécessaire aux finalités prévues et aux obligations légales applicables.
|
||||
|
||||
---
|
||||
|
||||
## 21. Documents annexés
|
||||
|
||||
Les documents suivants sont annexés au présent contrat :
|
||||
|
||||
- Copie CIN ou passeport du Locataire ;
|
||||
- Copie du permis de conduire ;
|
||||
- Copie permis international, le cas échéant ;
|
||||
- État des lieux de départ ;
|
||||
- État des lieux de retour ;
|
||||
- Photos du véhicule au départ ;
|
||||
- Photos du véhicule au retour ;
|
||||
- Copie de la carte grise ;
|
||||
- Copie de l’attestation d’assurance ;
|
||||
- Justificatif autorisant l’exploitation du véhicule par le Loueur, si le véhicule n’appartient pas directement au Loueur ;
|
||||
- Reçu de paiement ;
|
||||
- Justificatif de dépôt de garantie ;
|
||||
- Conditions générales de location, le cas échéant.
|
||||
- Liste des références légales et réglementaires applicables ;
|
||||
|
||||
Les annexes font partie intégrante du présent contrat.
|
||||
|
||||
---
|
||||
|
||||
## 22. Loi applicable et juridiction compétente
|
||||
|
||||
Le présent contrat est soumis au droit marocain.
|
||||
|
||||
En cas de litige relatif à sa validité, son interprétation, son exécution ou sa résiliation, les Parties s’efforceront de trouver une solution amiable.
|
||||
|
||||
À défaut d’accord amiable, le litige sera porté devant le tribunal compétent du ressort de :
|
||||
|
||||
**[Ville du siège de l’agence / tribunal compétent]**
|
||||
|
||||
---
|
||||
|
||||
## 23. Déclaration finale
|
||||
|
||||
Le Locataire déclare :
|
||||
|
||||
- avoir lu et compris le présent contrat ;
|
||||
- avoir reçu toutes les informations utiles avant signature ;
|
||||
- avoir vérifié l’état du véhicule au départ ;
|
||||
- être titulaire d’un permis valide ;
|
||||
- s’engager à respecter les conditions du présent contrat ;
|
||||
- accepter les prix, caution, franchises, pénalités et frais indiqués.
|
||||
|
||||
**Fait à :** [●]
|
||||
**Le :** [●]
|
||||
**En deux exemplaires originaux.**
|
||||
|
||||
Chaque page du présent contrat doit être paraphée par les deux Parties.
|
||||
|
||||
### Signature du Loueur
|
||||
|
||||
**Nom :** [●]
|
||||
**Signature et cachet :**
|
||||
|
||||
<br><br>
|
||||
|
||||
### Signature du Locataire
|
||||
|
||||
**Nom :** [●]
|
||||
**Signature :**
|
||||
|
||||
<br><br>
|
||||
|
||||
---
|
||||
|
||||
# Annexe 1 — État des lieux de départ
|
||||
|
||||
**Contrat n° :** [●]
|
||||
**Date :** [●]
|
||||
**Heure :** [●]
|
||||
**Lieu :** [●]
|
||||
|
||||
**Véhicule :** [Marque / Modèle]
|
||||
**Immatriculation :** [●]
|
||||
**Kilométrage départ :** [●] km
|
||||
**Carburant départ :** [●]
|
||||
|
||||
| Élément | État au départ | Observations |
|
||||
|---|---|---|
|
||||
| Carrosserie avant | Bon / Rayé / Endommagé | [●] |
|
||||
| Carrosserie arrière | Bon / Rayé / Endommagé | [●] |
|
||||
| Côté droit | Bon / Rayé / Endommagé | [●] |
|
||||
| Côté gauche | Bon / Rayé / Endommagé | [●] |
|
||||
| Pare-brise | Bon / Impact / Fissure | [●] |
|
||||
| Vitres | Bon / Endommagé | [●] |
|
||||
| Pneus | Bon / Usé / Endommagé | [●] |
|
||||
| Jantes | Bon / Rayé / Endommagé | [●] |
|
||||
| Intérieur | Bon / Taché / Endommagé | [●] |
|
||||
| Sièges | Bon / Taché / Déchiré | [●] |
|
||||
| Tableau de bord | Bon / Endommagé | [●] |
|
||||
| Climatisation | Fonctionne / Ne fonctionne pas | [●] |
|
||||
| Feux | Fonctionnent / Défaut | [●] |
|
||||
| Roue de secours | Présente / Absente | [●] |
|
||||
| Outillage | Présent / Absent | [●] |
|
||||
| Carte grise | Présente / Absente | [●] |
|
||||
| Assurance | Présente / Absente | [●] |
|
||||
| Clés | [Nombre] | [●] |
|
||||
|
||||
**Photos prises au départ :** Oui / Non
|
||||
**Nombre de photos :** [●]
|
||||
|
||||
**Signature du Loueur :**
|
||||
|
||||
<br><br>
|
||||
|
||||
**Signature du Locataire :**
|
||||
|
||||
<br><br>
|
||||
|
||||
---
|
||||
|
||||
# Annexe 2 — État des lieux de retour
|
||||
|
||||
**Contrat n° :** [●]
|
||||
**Date :** [●]
|
||||
**Heure :** [●]
|
||||
**Lieu :** [●]
|
||||
|
||||
**Kilométrage retour :** [●] km
|
||||
**Kilométrage parcouru :** [●] km
|
||||
**Kilométrage inclus :** [●] km
|
||||
**Kilométrage supplémentaire :** [●] km
|
||||
**Montant km supplémentaires :** [●] MAD
|
||||
|
||||
**Carburant retour :** [●]
|
||||
**Carburant manquant :** Oui / Non
|
||||
**Montant carburant :** [●] MAD
|
||||
|
||||
| Élément | État au retour | Nouveau dommage ? | Observations |
|
||||
|---|---|---|---|
|
||||
| Carrosserie avant | Bon / Rayé / Endommagé | Oui / Non | [●] |
|
||||
| Carrosserie arrière | Bon / Rayé / Endommagé | Oui / Non | [●] |
|
||||
| Côté droit | Bon / Rayé / Endommagé | Oui / Non | [●] |
|
||||
| Côté gauche | Bon / Rayé / Endommagé | Oui / Non | [●] |
|
||||
| Pare-brise | Bon / Impact / Fissure | Oui / Non | [●] |
|
||||
| Vitres | Bon / Endommagé | Oui / Non | [●] |
|
||||
| Pneus | Bon / Usé / Endommagé | Oui / Non | [●] |
|
||||
| Jantes | Bon / Rayé / Endommagé | Oui / Non | [●] |
|
||||
| Intérieur | Bon / Taché / Endommagé | Oui / Non | [●] |
|
||||
| Sièges | Bon / Taché / Déchiré | Oui / Non | [●] |
|
||||
| Documents | Complets / Manquants | Oui / Non | [●] |
|
||||
| Clés | Restituées / Manquantes | Oui / Non | [●] |
|
||||
|
||||
## Frais constatés au retour
|
||||
|
||||
| Frais | Montant |
|
||||
|---|---:|
|
||||
| Dommages | [●] MAD |
|
||||
| Franchise assurance | [●] MAD |
|
||||
| Kilométrage supplémentaire | [●] MAD |
|
||||
| Carburant | [●] MAD |
|
||||
| Nettoyage | [●] MAD |
|
||||
| Retard | [●] MAD |
|
||||
| Autres frais | [●] MAD |
|
||||
|
||||
**Total à retenir sur la caution : [●] MAD**
|
||||
**Solde de caution à restituer : [●] MAD**
|
||||
|
||||
**Photos prises au retour :** Oui / Non
|
||||
**Nombre de photos :** [●]
|
||||
|
||||
**Signature du Loueur :**
|
||||
|
||||
<br><br>
|
||||
|
||||
**Signature du Locataire :**
|
||||
|
||||
<br><br>
|
||||
|
||||
---
|
||||
|
||||
# Checklist pratique avant signature
|
||||
|
||||
- Ne laisser aucun champ `[●]` vide dans la version signée.
|
||||
- Faire parapher chaque page par les deux Parties.
|
||||
- Joindre la copie CIN ou passeport du Locataire.
|
||||
- Joindre la copie du permis de conduire.
|
||||
- Photographier le compteur et le niveau de carburant au départ et au retour.
|
||||
- Photographier toutes les faces du véhicule au départ et au retour.
|
||||
- Écrire clairement le montant de la caution.
|
||||
- Mentionner la compagnie d’assurance, le numéro de police et la franchise.
|
||||
- Conserver une preuve de paiement.
|
||||
- Conserver les états des lieux signés.
|
||||
|
||||
---
|
||||
|
||||
# Note importante
|
||||
|
||||
Ce modèle est un document pratique destiné à structurer une location de voiture au Maroc. Il doit être adapté à l’activité réelle du Loueur, à ses conditions d’assurance, à son statut fiscal et aux règles applicables. Pour un usage commercial régulier, une validation par un juriste ou avocat marocain est recommandée.
|
||||
|
||||
|
||||
---
|
||||
|
||||
# Références légales et réglementaires applicables
|
||||
|
||||
Les références suivantes sont indiquées à titre de base juridique générale pour l’activité de location de véhicules sans chauffeur au Maroc. Elles doivent être vérifiées et actualisées selon les textes officiels en vigueur, notamment le Bulletin Officiel et les circulaires ou cahiers des charges applicables.
|
||||
|
||||
## Textes principaux relatifs à la location de véhicules
|
||||
|
||||
| Référence | Objet | Utilité pour le présent contrat |
|
||||
|---|---|---|
|
||||
| **Dahir n° 1-63-260 du 12 novembre 1963** | Transports par véhicules automobiles sur route | Cadre général applicable au transport routier et aux activités connexes. |
|
||||
| **Décret n° 2.69.351 du 4 avril 1970** | Conditions d’exploitation des voitures louées sans chauffeur | Texte spécifique encadrant l’exploitation des véhicules loués sans chauffeur. |
|
||||
| **Cahier des charges relatif à la location de voitures sans chauffeur, tel qu’en vigueur** | Conditions administratives, techniques et professionnelles de l’activité | Référence pratique pour l’agrément, la flotte, le responsable d’activité, l’âge des véhicules, les obligations administratives et les sanctions. |
|
||||
|
||||
## Circulation, sécurité routière et véhicules
|
||||
|
||||
| Référence | Objet | Utilité pour le présent contrat |
|
||||
|---|---|---|
|
||||
| **Loi n° 52-05 portant Code de la route** | Règles de circulation, infractions, sécurité routière, véhicules et conducteurs | Encadre l’usage du véhicule, les infractions, la responsabilité du conducteur et les obligations de sécurité. |
|
||||
| **Dahir n° 1-10-07 du 11 février 2010** | Promulgation de la Loi n° 52-05 portant Code de la route | Texte de promulgation du Code de la route. |
|
||||
|
||||
## Assurance, responsabilité et obligations contractuelles
|
||||
|
||||
| Référence | Objet | Utilité pour le présent contrat |
|
||||
|---|---|---|
|
||||
| **Loi n° 17-99 portant Code des assurances** | Assurance automobile, garanties, exclusions, franchises et responsabilité assurantielle | Encadre les assurances liées au véhicule loué, les garanties, les exclusions et les franchises. |
|
||||
| **Dahir des Obligations et des Contrats du 12 août 1913** | Règles générales des contrats, obligations, responsabilité civile, consentement et inexécution | Base générale du contrat de location et de la responsabilité des Parties. |
|
||||
|
||||
## Droit commercial et structure de l’agence
|
||||
|
||||
| Référence | Objet | Utilité pour le présent contrat |
|
||||
|---|---|---|
|
||||
| **Loi n° 15-95 formant Code de commerce** | Activité commerciale, commerçants, obligations commerciales, registre de commerce | Applicable à l’activité commerciale du Loueur. |
|
||||
| **Loi n° 5-96** | Sociétés à responsabilité limitée, sociétés en nom collectif, sociétés en commandite simple, sociétés en commandite par actions et sociétés en participation | Applicable si le Loueur est constitué sous l’une de ces formes, notamment SARL. |
|
||||
| **Loi n° 17-95 relative aux sociétés anonymes** | Sociétés anonymes | Applicable si le Loueur est constitué sous forme de société anonyme. |
|
||||
| **Loi n° 69-21 relative aux délais de paiement** | Délais de paiement entre professionnels | Applicable notamment aux locations ou relations commerciales B2B. |
|
||||
|
||||
## Protection du consommateur, données personnelles et signature électronique
|
||||
|
||||
| Référence | Objet | Utilité pour le présent contrat |
|
||||
|---|---|---|
|
||||
| **Loi n° 31-08 édictant des mesures de protection du consommateur** | Information du consommateur, transparence des prix, clauses abusives, droits du client | Applicable lorsque le Locataire agit comme consommateur. |
|
||||
| **Loi n° 09-08 relative à la protection des personnes physiques à l’égard du traitement des données à caractère personnel** | Collecte, traitement, conservation et protection des données personnelles | Applicable aux données du Locataire : CIN, passeport, permis, coordonnées, signatures, documents et photos. |
|
||||
| **Loi n° 53-05 relative à l’échange électronique de données juridiques** | Documents électroniques, signature électronique et valeur juridique des échanges numériques | Applicable si le contrat est signé, transmis ou conservé électroniquement. |
|
||||
|
||||
## Obligations sociales, fiscales et administratives
|
||||
|
||||
| Référence | Objet | Utilité pour le présent contrat |
|
||||
|---|---|---|
|
||||
| **Dahir n° 1-72-184 relatif au régime de sécurité sociale** | Régime CNSS et obligations sociales | Pertinent pour la conformité sociale du Loueur lorsqu’il emploie du personnel. |
|
||||
| **Loi n° 47-06 relative à la fiscalité des collectivités locales** | Fiscalité locale, taxe professionnelle et obligations locales | Pertinente pour les obligations fiscales locales de l’agence. |
|
||||
| **Code Général des Impôts** | TVA, impôt sur les sociétés, impôt sur le revenu, facturation et obligations fiscales | Pertinent pour la facturation, les déclarations fiscales et le traitement de la TVA. |
|
||||
|
||||
## Clause de prudence juridique
|
||||
|
||||
Le présent contrat doit être interprété et appliqué conformément aux textes marocains en vigueur à la date de signature.
|
||||
|
||||
Lorsque le présent contrat mentionne le **cahier des charges relatif à la location de voitures sans chauffeur**, cette référence vise le cahier des charges applicable tel qu’en vigueur, y compris ses modifications, circulaires d’application ou textes complémentaires.
|
||||
|
||||
En cas de contradiction entre le présent contrat et une disposition légale ou réglementaire impérative, la disposition légale ou réglementaire prévaut. Les autres clauses du contrat demeurent applicables dans la mesure permise par la loi.
|
||||
|
||||
|
||||
---
|
||||
|
||||
# Checklist interne de conformité du Loueur
|
||||
|
||||
Cette checklist est destinée au Loueur et ne remplace pas les documents administratifs obligatoires.
|
||||
|
||||
- Agrément / autorisation d’exercice disponible.
|
||||
- Responsable réglementaire désigné.
|
||||
- Casier judiciaire conforme, si exigé.
|
||||
- Société inscrite à la CNSS, si applicable.
|
||||
- Siège social ou local professionnel justifié.
|
||||
- Capital social conforme au seuil applicable.
|
||||
- Flotte conforme au seuil réglementaire applicable.
|
||||
- Âge des véhicules conforme selon motorisation.
|
||||
- Documents de propriété ou d’exploitation des véhicules disponibles.
|
||||
- Assurance et visite technique valides pour chaque véhicule.
|
||||
- Procédure de déclaration en cas de suspension ou cessation d’activité.
|
||||
@@ -0,0 +1,694 @@
|
||||
# Review Management Process After Rental Completion
|
||||
|
||||
## 1. Purpose
|
||||
|
||||
The review management process helps the rental company collect customer feedback after each completed rental, improve service quality, recover unhappy customers, and increase positive public reviews.
|
||||
|
||||
The goal is not only to collect high ratings. The goal is to identify service problems, fix weak operations, and encourage satisfied customers to share their experience publicly.
|
||||
|
||||
---
|
||||
|
||||
## 2. When the Review Process Starts
|
||||
|
||||
The review process starts only after the rental is fully closed.
|
||||
|
||||
A rental is considered closed when:
|
||||
|
||||
- The vehicle has been returned
|
||||
- The return inspection is completed
|
||||
- The final invoice has been sent
|
||||
- The deposit has been released or adjusted
|
||||
- Any damage, billing, or dispute case is closed or clearly documented
|
||||
- The customer has received the final receipt
|
||||
|
||||
Do not send a review request before the final billing is clear. Customers should not be asked for a review while they are still waiting for a deposit update, refund, or charge explanation.
|
||||
|
||||
---
|
||||
|
||||
## 3. Main Review Process Flow
|
||||
|
||||
### Step 1: Close the Rental
|
||||
|
||||
Before sending a review request, staff must confirm:
|
||||
|
||||
- Vehicle returned
|
||||
- Final charges completed
|
||||
- Receipt sent
|
||||
- Deposit status updated
|
||||
- Any customer issue notes added
|
||||
- Vehicle status updated
|
||||
|
||||
Once complete, the booking is marked as:
|
||||
|
||||
**Rental Closed**
|
||||
|
||||
This status triggers the review process.
|
||||
|
||||
---
|
||||
|
||||
### Step 2: Check Review Eligibility
|
||||
|
||||
Before sending a review request, the system or staff must confirm that the customer is eligible.
|
||||
|
||||
A review request should be sent only if:
|
||||
|
||||
- Rental status is **Closed**
|
||||
- Final invoice has been sent
|
||||
- No open dispute exists
|
||||
- No active complaint exists
|
||||
- No major damage case is open
|
||||
- Customer has not opted out of messages
|
||||
- Customer has a valid phone number or email address
|
||||
|
||||
A review request should be paused if:
|
||||
|
||||
- Customer has an open damage dispute
|
||||
- Customer has an unpaid balance
|
||||
- Customer has an active complaint
|
||||
- Customer was charged for major damage
|
||||
- Rental is under insurance investigation
|
||||
- Customer requested no further messages
|
||||
|
||||
---
|
||||
|
||||
### Step 3: Send Review Request
|
||||
|
||||
Send the review request within:
|
||||
|
||||
**2 to 6 hours after rental closure**
|
||||
|
||||
This timing is soon enough that the customer remembers the experience, but not so immediate that it feels automated and careless.
|
||||
|
||||
The message should include:
|
||||
|
||||
- Customer name
|
||||
- Thank-you message
|
||||
- Short review request
|
||||
- Review link
|
||||
- Support contact in case there was a problem
|
||||
|
||||
---
|
||||
|
||||
## 4. Review Channels
|
||||
|
||||
The company should define where customer reviews are collected.
|
||||
|
||||
Recommended review channels:
|
||||
|
||||
- Google Business Profile
|
||||
- Trustpilot
|
||||
- Facebook
|
||||
- Yelp, if relevant
|
||||
- Internal company review form
|
||||
- App store review, if using a mobile app
|
||||
|
||||
Recommended simple setup:
|
||||
|
||||
- Satisfied customers are sent to a public review link
|
||||
- Unsatisfied customers are routed to internal support first
|
||||
|
||||
The company should not fake reviews, pressure customers, or hide legitimate negative feedback. The process should encourage honest reviews and give unhappy customers a clear path to resolution.
|
||||
|
||||
---
|
||||
|
||||
## 5. Customer Rating Filter
|
||||
|
||||
Before sending customers to a public review page, the company may use a short internal satisfaction question.
|
||||
|
||||
Example question:
|
||||
|
||||
**How was your rental experience?**
|
||||
|
||||
Options:
|
||||
|
||||
- Excellent
|
||||
- Good
|
||||
- Okay
|
||||
- Bad
|
||||
- Very bad
|
||||
|
||||
Routing rules:
|
||||
|
||||
| Customer Response | Action |
|
||||
|---|---|
|
||||
| Excellent | Send public review link |
|
||||
| Good | Send public review link |
|
||||
| Okay | Ask for internal feedback |
|
||||
| Bad | Create support case |
|
||||
| Very bad | Create urgent support case |
|
||||
|
||||
This helps the company recover unhappy customers before the issue becomes a public complaint.
|
||||
|
||||
---
|
||||
|
||||
## 6. Review Request Timing
|
||||
|
||||
| Time After Rental Closure | Action |
|
||||
|---|---|
|
||||
| 2 to 6 hours | Send first review request |
|
||||
| 48 hours | Send reminder if no response |
|
||||
| 7 days | Send final reminder |
|
||||
| After 7 days | Stop messaging |
|
||||
|
||||
Maximum number of review messages:
|
||||
|
||||
**3 total**
|
||||
|
||||
Do not keep messaging customers after the final reminder.
|
||||
|
||||
---
|
||||
|
||||
## 7. Review Message Rules
|
||||
|
||||
A good review request should be:
|
||||
|
||||
- Short
|
||||
- Polite
|
||||
- Personalized
|
||||
- Easy to act on
|
||||
- Sent at the right time
|
||||
- Focused on one clear link
|
||||
|
||||
Avoid review messages that are:
|
||||
|
||||
- Too long
|
||||
- Begging for 5 stars
|
||||
- Sent too many times
|
||||
- Sent before billing is closed
|
||||
- Sent during a dispute
|
||||
- Written like spam
|
||||
|
||||
Do not say:
|
||||
|
||||
> Please give us 5 stars.
|
||||
|
||||
Better wording:
|
||||
|
||||
> Your feedback helps us improve and helps other customers choose us.
|
||||
|
||||
---
|
||||
|
||||
## 8. Review Request Templates
|
||||
|
||||
### SMS Template
|
||||
|
||||
Hi {{customer_name}}, thank you for renting with {{company_name}}. We hope everything went smoothly. Please take 30 seconds to review your experience: {{review_link}}
|
||||
|
||||
If anything was not right, reply here and our team will help.
|
||||
|
||||
---
|
||||
|
||||
### Email Template
|
||||
|
||||
**Subject:** How was your rental experience?
|
||||
|
||||
Hi {{customer_name}},
|
||||
|
||||
Thank you for renting with {{company_name}}.
|
||||
|
||||
We hope your experience was smooth from pickup to return. Please take a moment to review us here:
|
||||
|
||||
{{review_link}}
|
||||
|
||||
If something was not right, reply to this email and our team will review it.
|
||||
|
||||
Thank you,
|
||||
{{company_name}}
|
||||
|
||||
---
|
||||
|
||||
### Reminder Template
|
||||
|
||||
Hi {{customer_name}}, just a quick reminder. Your feedback helps us improve our rental service. You can review your experience here: {{review_link}}
|
||||
|
||||
---
|
||||
|
||||
## 9. Handling Positive Reviews
|
||||
|
||||
When a customer leaves a positive review, staff should:
|
||||
|
||||
- Thank the customer publicly
|
||||
- Keep the reply short
|
||||
- Mention the customer’s experience if appropriate
|
||||
- Avoid copying the same reply every time
|
||||
- Tag the booking as positive feedback
|
||||
|
||||
Example public reply:
|
||||
|
||||
> Thank you for your review. We are glad your rental experience went smoothly and appreciate you choosing us.
|
||||
|
||||
If the review mentions a staff member, the manager should be notified.
|
||||
|
||||
Positive reviews should be tracked by:
|
||||
|
||||
- Branch
|
||||
- Vehicle
|
||||
- Staff member
|
||||
- Booking channel
|
||||
- Rental type
|
||||
|
||||
---
|
||||
|
||||
## 10. Handling Negative Reviews
|
||||
|
||||
Negative reviews must be handled quickly and calmly.
|
||||
|
||||
Target response time:
|
||||
|
||||
**Within 24 hours**
|
||||
|
||||
Internal steps:
|
||||
|
||||
1. Create a complaint case.
|
||||
2. Link the complaint to the rental agreement.
|
||||
3. Review pickup photos.
|
||||
4. Review return photos.
|
||||
5. Review invoice and charges.
|
||||
6. Review staff notes.
|
||||
7. Contact the customer privately.
|
||||
8. Offer a fair solution if the company made a mistake.
|
||||
9. Reply publicly with a professional response.
|
||||
10. Record the final outcome.
|
||||
|
||||
Public reply rules:
|
||||
|
||||
- Stay calm
|
||||
- Acknowledge the concern
|
||||
- Do not argue
|
||||
- Do not expose customer details
|
||||
- Invite the customer to private support
|
||||
- Show that the company takes feedback seriously
|
||||
|
||||
Example public reply:
|
||||
|
||||
> We are sorry to hear your experience did not meet expectations. We take this seriously and would like to review the rental details with you. Please contact our support team at {{contact_info}} so we can investigate and help resolve this.
|
||||
|
||||
---
|
||||
|
||||
## 11. Complaint Severity Levels
|
||||
|
||||
### Level 1: Minor Issue
|
||||
|
||||
Examples:
|
||||
|
||||
- Long wait time
|
||||
- Car not perfectly clean
|
||||
- Staff communication issue
|
||||
- Confusing instructions
|
||||
|
||||
Action:
|
||||
|
||||
- Apologize
|
||||
- Record feedback
|
||||
- Offer small goodwill if appropriate
|
||||
- Inform branch manager
|
||||
|
||||
---
|
||||
|
||||
### Level 2: Serious Issue
|
||||
|
||||
Examples:
|
||||
|
||||
- Incorrect billing
|
||||
- Deposit delay
|
||||
- Vehicle mechanical problem
|
||||
- Wrong car category
|
||||
- Poor staff behavior
|
||||
|
||||
Action:
|
||||
|
||||
- Manager review required
|
||||
- Contact customer within 24 hours
|
||||
- Investigate records
|
||||
- Offer correction or compensation if valid
|
||||
|
||||
---
|
||||
|
||||
### Level 3: Critical Issue
|
||||
|
||||
Examples:
|
||||
|
||||
- Safety issue
|
||||
- Accident handling failure
|
||||
- Major billing dispute
|
||||
- Legal threat
|
||||
- Fraud accusation
|
||||
- Discrimination complaint
|
||||
|
||||
Action:
|
||||
|
||||
- Immediate manager escalation
|
||||
- Preserve all records
|
||||
- Stop automated review reminders
|
||||
- Senior manager or owner handles case
|
||||
- Legal or insurance review if needed
|
||||
|
||||
---
|
||||
|
||||
## 12. Internal Feedback Form
|
||||
|
||||
The company may use an internal form before sending customers to public review platforms.
|
||||
|
||||
Recommended questions:
|
||||
|
||||
1. How was the booking process?
|
||||
2. How was the pickup experience?
|
||||
3. Was the car clean?
|
||||
4. Was the car in good condition?
|
||||
5. Was the return process easy?
|
||||
6. Was pricing clear?
|
||||
7. Would you rent from us again?
|
||||
8. What should we improve?
|
||||
|
||||
Recommended score scale:
|
||||
|
||||
| Score | Meaning |
|
||||
|---|---|
|
||||
| 1 | Very bad |
|
||||
| 2 | Bad |
|
||||
| 3 | Okay |
|
||||
| 4 | Good |
|
||||
| 5 | Excellent |
|
||||
|
||||
---
|
||||
|
||||
## 13. Review Data to Track
|
||||
|
||||
The company should track:
|
||||
|
||||
- Number of review requests sent
|
||||
- Number of reviews received
|
||||
- Review response rate
|
||||
- Average review score
|
||||
- Number of positive reviews
|
||||
- Number of negative reviews
|
||||
- Main complaint categories
|
||||
- Branch performance
|
||||
- Staff performance
|
||||
- Vehicle-related complaints
|
||||
- Billing-related complaints
|
||||
- Deposit-related complaints
|
||||
- Pickup delay complaints
|
||||
- Return delay complaints
|
||||
|
||||
A monthly review report should show:
|
||||
|
||||
- Top 5 problems
|
||||
- Top 5 positive mentions
|
||||
- Best-performing branch
|
||||
- Worst-performing branch
|
||||
- Repeat complaint patterns
|
||||
- Corrective actions taken
|
||||
|
||||
---
|
||||
|
||||
## 14. Feedback Categories
|
||||
|
||||
Every review or complaint should be tagged under one main category:
|
||||
|
||||
- Booking
|
||||
- Pickup
|
||||
- Vehicle cleanliness
|
||||
- Vehicle condition
|
||||
- Staff service
|
||||
- Pricing
|
||||
- Deposit
|
||||
- Insurance
|
||||
- Damage claim
|
||||
- Return process
|
||||
- Communication
|
||||
- Roadside assistance
|
||||
- Billing
|
||||
- Other
|
||||
|
||||
This makes patterns easier to identify and fix.
|
||||
|
||||
Examples:
|
||||
|
||||
- Many pickup complaints = staffing or preparation issue
|
||||
- Many deposit complaints = unclear deposit communication
|
||||
- Many cleanliness complaints = weak cleaning control
|
||||
- Many billing complaints = unclear pricing or invoice process
|
||||
|
||||
---
|
||||
|
||||
## 15. Staff Responsibilities
|
||||
|
||||
### Rental Agent
|
||||
|
||||
Responsible for:
|
||||
|
||||
- Closing rental properly
|
||||
- Confirming customer contact details
|
||||
- Adding notes about any issue
|
||||
- Marking rental ready for review request
|
||||
|
||||
---
|
||||
|
||||
### Customer Support
|
||||
|
||||
Responsible for:
|
||||
|
||||
- Monitoring reviews
|
||||
- Replying to simple feedback
|
||||
- Creating complaint cases
|
||||
- Contacting unhappy customers
|
||||
|
||||
---
|
||||
|
||||
### Branch Manager
|
||||
|
||||
Responsible for:
|
||||
|
||||
- Reviewing negative feedback
|
||||
- Approving compensation
|
||||
- Coaching staff
|
||||
- Fixing branch-level problems
|
||||
|
||||
---
|
||||
|
||||
### Owner or Senior Manager
|
||||
|
||||
Responsible for:
|
||||
|
||||
- Reviewing monthly review report
|
||||
- Handling serious complaints
|
||||
- Updating company policy
|
||||
- Tracking reputation score
|
||||
|
||||
---
|
||||
|
||||
## 16. Review Response Standards
|
||||
|
||||
### Positive Review Response
|
||||
|
||||
Target response time:
|
||||
|
||||
**Within 3 business days**
|
||||
|
||||
Tone:
|
||||
|
||||
- Thankful
|
||||
- Short
|
||||
- Professional
|
||||
|
||||
---
|
||||
|
||||
### Negative Review Response
|
||||
|
||||
Target response time:
|
||||
|
||||
**Within 24 hours**
|
||||
|
||||
Tone:
|
||||
|
||||
- Calm
|
||||
- Responsible
|
||||
- Not defensive
|
||||
- No private customer details
|
||||
|
||||
---
|
||||
|
||||
### False or Abusive Review
|
||||
|
||||
If a review appears fake, abusive, or unrelated:
|
||||
|
||||
1. Take a screenshot.
|
||||
2. Check if the reviewer matches a customer.
|
||||
3. Report the review to the platform if it violates rules.
|
||||
4. Reply professionally if needed.
|
||||
5. Do not insult the reviewer.
|
||||
|
||||
Example reply:
|
||||
|
||||
> We cannot locate a rental under this name, but we take feedback seriously. Please contact us at {{contact_info}} so we can review this properly.
|
||||
|
||||
---
|
||||
|
||||
## 17. Compensation Rules
|
||||
|
||||
Compensation should be controlled and approved based on severity.
|
||||
|
||||
Possible compensation options:
|
||||
|
||||
- Apology only
|
||||
- Discount on next rental
|
||||
- Partial refund
|
||||
- Cleaning fee refund
|
||||
- Upgrade on next rental
|
||||
- Delivery fee refund
|
||||
- Deposit correction
|
||||
- Full refund, only in serious cases
|
||||
|
||||
Manager approval should be required for:
|
||||
|
||||
- Refunds above a set amount
|
||||
- Free rental days
|
||||
- Damage fee removal
|
||||
- Public complaint compensation
|
||||
- Legal threat situations
|
||||
|
||||
Compensation should be fair, documented, and linked to the actual issue.
|
||||
|
||||
---
|
||||
|
||||
## 18. Automation Rules
|
||||
|
||||
The review system should be automated but controlled.
|
||||
|
||||
### Automatic Review Request Trigger
|
||||
|
||||
Send a review request when:
|
||||
|
||||
- Rental status = Closed
|
||||
- Final invoice sent
|
||||
- No open dispute
|
||||
- No active complaint
|
||||
- Customer has valid phone or email
|
||||
- Customer has not opted out
|
||||
|
||||
---
|
||||
|
||||
### Automatic Reminder Trigger
|
||||
|
||||
Send a reminder when:
|
||||
|
||||
- No review received after 48 hours
|
||||
- No complaint opened
|
||||
- Customer has not opted out
|
||||
|
||||
---
|
||||
|
||||
### Stop Automation When
|
||||
|
||||
Stop review automation when:
|
||||
|
||||
- Customer replies with complaint
|
||||
- Review has already been submitted
|
||||
- Dispute is opened
|
||||
- Customer opts out
|
||||
- Staff manually pauses request
|
||||
|
||||
---
|
||||
|
||||
## 19. Simple Review Workflows
|
||||
|
||||
### Normal Happy Customer
|
||||
|
||||
1. Rental closed
|
||||
2. Review request sent
|
||||
3. Customer leaves review
|
||||
4. Company replies
|
||||
5. Review recorded
|
||||
6. No further action
|
||||
|
||||
---
|
||||
|
||||
### No Response
|
||||
|
||||
1. Rental closed
|
||||
2. First request sent
|
||||
3. No response after 48 hours
|
||||
4. Reminder sent
|
||||
5. No response after 7 days
|
||||
6. Final reminder sent
|
||||
7. Stop
|
||||
|
||||
---
|
||||
|
||||
### Unhappy Customer
|
||||
|
||||
1. Rental closed
|
||||
2. Customer gives low internal rating or bad public review
|
||||
3. Complaint case created
|
||||
4. Manager reviews rental
|
||||
5. Customer contacted
|
||||
6. Issue resolved or documented
|
||||
7. Public reply posted
|
||||
8. Root cause logged
|
||||
|
||||
---
|
||||
|
||||
### Dispute Customer
|
||||
|
||||
1. Rental closed with dispute
|
||||
2. Review request paused
|
||||
3. Dispute handled first
|
||||
4. Manager decides whether review request should be sent after resolution
|
||||
|
||||
---
|
||||
|
||||
## 20. Key Rules
|
||||
|
||||
The review process should follow these rules:
|
||||
|
||||
- Never send a review request before the final invoice.
|
||||
- Never send a review request during an open dispute.
|
||||
- Send the first request within 2 to 6 hours after closure.
|
||||
- Send no more than 3 total review messages.
|
||||
- Respond to negative reviews within 24 hours.
|
||||
- Track every complaint by category.
|
||||
- Use feedback to fix operations.
|
||||
- Do not argue publicly.
|
||||
- Do not ask directly for fake or forced 5-star reviews.
|
||||
- Stop messaging customers who opt out.
|
||||
|
||||
---
|
||||
|
||||
## 21. Minimum Setup for a Small Rental Company
|
||||
|
||||
A small rental company can manage the process with:
|
||||
|
||||
- Rental management system
|
||||
- Google review link
|
||||
- SMS or email tool
|
||||
- Complaint spreadsheet or CRM
|
||||
- Monthly review report
|
||||
- Staff member assigned to monitor reviews daily
|
||||
|
||||
Minimum tracking fields:
|
||||
|
||||
| Field | Example |
|
||||
|---|---|
|
||||
| Booking ID | R-1029 |
|
||||
| Customer Name | John Smith |
|
||||
| Return Date | 2026-05-25 |
|
||||
| Review Request Sent | Yes |
|
||||
| Review Score | 5 |
|
||||
| Complaint? | No |
|
||||
| Category | Pickup |
|
||||
| Staff Owner | Sarah |
|
||||
| Status | Closed |
|
||||
|
||||
---
|
||||
|
||||
## 22. Best Operating Rule
|
||||
|
||||
The review process should be simple:
|
||||
|
||||
**Ask every eligible customer.
|
||||
Pause disputed customers.
|
||||
Respond fast.
|
||||
Fix repeated problems.**
|
||||
|
||||
A review system that only collects praise is vanity. A review system that identifies problems and fixes them is management.
|
||||
+777
-979
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,695 @@
|
||||
# Simple and Robust Car Rental Process
|
||||
|
||||
## Purpose
|
||||
|
||||
This document defines a simple, robust operating process for a rental car company. It covers the full rental lifecycle from booking to vehicle return.
|
||||
|
||||
The process is designed to be easy for staff to follow, fast for customers, and strong enough to protect the company from payment issues, damage disputes, missing vehicles, and unclear responsibilities.
|
||||
|
||||
## Core Process
|
||||
|
||||
The rental operation should be built around four control points:
|
||||
|
||||
1. Booking
|
||||
2. Vehicle Preparation
|
||||
3. Pickup
|
||||
4. Return
|
||||
|
||||
Everything in the process should support one of these goals:
|
||||
|
||||
- Confirm the customer is eligible to rent.
|
||||
- Confirm the vehicle is available and ready.
|
||||
- Secure payment and deposit before release.
|
||||
- Document vehicle condition at pickup and return.
|
||||
- Close the rental clearly and fairly.
|
||||
|
||||
---
|
||||
|
||||
# 1. Booking
|
||||
|
||||
## Goal
|
||||
|
||||
The goal of booking is to confirm who the customer is, what they need, and whether the company can safely rent to them.
|
||||
|
||||
## Required Customer Information
|
||||
|
||||
Collect the following information for every booking:
|
||||
|
||||
- Full customer name
|
||||
- Phone number
|
||||
- Email address
|
||||
- Driver's license details
|
||||
- Pickup date and time
|
||||
- Return date and time
|
||||
- Pickup location
|
||||
- Return location
|
||||
- Vehicle class
|
||||
- Payment method
|
||||
- Insurance choice
|
||||
- Special requests, if any
|
||||
|
||||
Special requests may include:
|
||||
|
||||
- Child seat
|
||||
- GPS device
|
||||
- Additional driver
|
||||
- One-way rental
|
||||
- Airport pickup
|
||||
- After-hours return
|
||||
- Electric vehicle request
|
||||
|
||||
## Booking Checks
|
||||
|
||||
Before confirming a booking, staff or the system must check:
|
||||
|
||||
- Vehicle availability
|
||||
- Driver age eligibility
|
||||
- Driver's license validity
|
||||
- Deposit requirement
|
||||
- Payment method validity
|
||||
- Previous unpaid balance
|
||||
- Customer blacklist or restriction status
|
||||
|
||||
If any check fails, the booking must not be confirmed until the issue is reviewed by a manager.
|
||||
|
||||
## Booking Confirmation
|
||||
|
||||
After approval, send the customer a confirmation by email or SMS.
|
||||
|
||||
The confirmation must include:
|
||||
|
||||
- Booking number
|
||||
- Pickup date, time, and location
|
||||
- Return date, time, and location
|
||||
- Vehicle class
|
||||
- Estimated total price
|
||||
- Deposit amount
|
||||
- Required documents
|
||||
- Fuel or charging rule
|
||||
- Late return rule
|
||||
- Cancellation rule
|
||||
- Company contact information
|
||||
|
||||
## Booking Checklist
|
||||
|
||||
- [ ] Customer details collected
|
||||
- [ ] License details collected
|
||||
- [ ] Vehicle availability confirmed
|
||||
- [ ] Price confirmed
|
||||
- [ ] Deposit amount confirmed
|
||||
- [ ] Insurance choice recorded
|
||||
- [ ] Special requests recorded
|
||||
- [ ] Confirmation sent to customer
|
||||
|
||||
---
|
||||
|
||||
# 2. Vehicle Preparation
|
||||
|
||||
## Goal
|
||||
|
||||
The goal of vehicle preparation is to make sure the car is ready before the customer arrives.
|
||||
|
||||
## Vehicle Assignment
|
||||
|
||||
Assign a specific vehicle to the booking before pickup whenever possible.
|
||||
|
||||
The assigned vehicle must match:
|
||||
|
||||
- Booked vehicle class
|
||||
- Required seating capacity
|
||||
- Transmission preference, if applicable
|
||||
- Fuel or electric vehicle preference, if applicable
|
||||
- Special requests, if applicable
|
||||
|
||||
If the exact vehicle is unavailable, staff may assign an equal or higher-class vehicle according to company policy.
|
||||
|
||||
## Vehicle Readiness Checklist
|
||||
|
||||
Before pickup, staff must check:
|
||||
|
||||
- [ ] Vehicle is clean inside
|
||||
- [ ] Vehicle is clean outside
|
||||
- [ ] Mileage recorded
|
||||
- [ ] Fuel or battery level recorded
|
||||
- [ ] Tires checked visually
|
||||
- [ ] No dashboard warning lights
|
||||
- [ ] Lights working
|
||||
- [ ] Registration document available
|
||||
- [ ] Insurance document available
|
||||
- [ ] Keys available
|
||||
- [ ] Requested accessories installed or included
|
||||
- [ ] Pickup photos taken
|
||||
- [ ] Existing damage recorded
|
||||
|
||||
## Vehicle Statuses
|
||||
|
||||
Use simple vehicle statuses only:
|
||||
|
||||
| Status | Meaning |
|
||||
|---|---|
|
||||
| Available | Vehicle can be booked. |
|
||||
| Reserved | Vehicle is assigned to a future booking. |
|
||||
| Ready | Vehicle is prepared for pickup. |
|
||||
| On Rent | Vehicle is currently with a customer. |
|
||||
| Returned | Vehicle has been returned but not yet processed. |
|
||||
| Needs Cleaning | Vehicle requires cleaning before reuse. |
|
||||
| Needs Maintenance | Vehicle requires mechanical service. |
|
||||
| Damage Review | Vehicle has possible damage that must be reviewed. |
|
||||
| Blocked | Vehicle cannot be rented. |
|
||||
|
||||
## Preparation Checklist
|
||||
|
||||
- [ ] Booking reviewed
|
||||
- [ ] Vehicle assigned
|
||||
- [ ] Vehicle cleaned
|
||||
- [ ] Mileage recorded
|
||||
- [ ] Fuel or battery recorded
|
||||
- [ ] Vehicle photos taken
|
||||
- [ ] Documents inside vehicle
|
||||
- [ ] Keys ready
|
||||
- [ ] Accessories ready
|
||||
- [ ] Vehicle status changed to Ready
|
||||
|
||||
---
|
||||
|
||||
# 3. Pickup
|
||||
|
||||
## Goal
|
||||
|
||||
The goal of pickup is to verify the customer, secure payment, document the vehicle, and release the car quickly.
|
||||
|
||||
A standard pickup should normally take 10 to 15 minutes.
|
||||
|
||||
## Required Customer Documents
|
||||
|
||||
The customer must provide:
|
||||
|
||||
- Valid driver's license
|
||||
- Government ID, if required
|
||||
- Accepted payment card
|
||||
- Proof of insurance, if using personal insurance
|
||||
- Booking confirmation, if needed
|
||||
|
||||
The name on the driver's license should match the booking and payment method unless manager approval is given.
|
||||
|
||||
## Pickup Steps
|
||||
|
||||
1. Find the booking in the system.
|
||||
2. Verify the customer's identity.
|
||||
3. Verify the driver's license.
|
||||
4. Confirm return date, time, and location.
|
||||
5. Confirm vehicle class and assigned vehicle.
|
||||
6. Process rental payment.
|
||||
7. Authorize or collect deposit.
|
||||
8. Confirm insurance choice.
|
||||
9. Walk around the vehicle with the customer.
|
||||
10. Take pickup photos.
|
||||
11. Record mileage.
|
||||
12. Record fuel or battery level.
|
||||
13. Record existing damage.
|
||||
14. Have the customer sign the rental agreement.
|
||||
15. Give the customer the keys and emergency contact information.
|
||||
16. Change vehicle status to On Rent.
|
||||
|
||||
## Pickup Inspection
|
||||
|
||||
The pickup inspection must include:
|
||||
|
||||
- Front of vehicle
|
||||
- Rear of vehicle
|
||||
- Left side
|
||||
- Right side
|
||||
- Roof, if visible
|
||||
- Windshield
|
||||
- Windows
|
||||
- Mirrors
|
||||
- Tires
|
||||
- Wheels
|
||||
- Interior seats
|
||||
- Dashboard
|
||||
- Trunk
|
||||
- Fuel or charging area
|
||||
|
||||
Photos must be uploaded to the rental record before the vehicle is released.
|
||||
|
||||
## Rental Agreement Requirements
|
||||
|
||||
The rental agreement must include:
|
||||
|
||||
- Customer name
|
||||
- Authorized drivers
|
||||
- Vehicle make, model, color, and plate number
|
||||
- Pickup date and time
|
||||
- Expected return date and time
|
||||
- Pickup and return locations
|
||||
- Starting mileage
|
||||
- Starting fuel or battery level
|
||||
- Rental rate
|
||||
- Deposit amount
|
||||
- Insurance choice
|
||||
- Mileage limit, if applicable
|
||||
- Fuel or charging policy
|
||||
- Late return policy
|
||||
- Damage policy
|
||||
- Cleaning policy
|
||||
- Smoking policy
|
||||
- Toll and fine responsibility
|
||||
- Accident reporting process
|
||||
- Customer signature
|
||||
- Staff signature
|
||||
|
||||
## Pickup Checklist
|
||||
|
||||
- [ ] Booking found
|
||||
- [ ] Customer ID verified
|
||||
- [ ] Driver's license verified
|
||||
- [ ] Return details confirmed
|
||||
- [ ] Payment completed
|
||||
- [ ] Deposit authorized or collected
|
||||
- [ ] Insurance choice confirmed
|
||||
- [ ] Vehicle inspected with customer
|
||||
- [ ] Pickup photos uploaded
|
||||
- [ ] Mileage recorded
|
||||
- [ ] Fuel or battery level recorded
|
||||
- [ ] Existing damage recorded
|
||||
- [ ] Agreement signed
|
||||
- [ ] Keys given
|
||||
- [ ] Emergency contact provided
|
||||
- [ ] Vehicle status changed to On Rent
|
||||
|
||||
---
|
||||
|
||||
# 4. During the Rental
|
||||
|
||||
## Goal
|
||||
|
||||
The goal during the rental period is to handle changes, incidents, and support requests without confusion.
|
||||
|
||||
## Customer Support
|
||||
|
||||
The customer must be able to contact the company for:
|
||||
|
||||
- Rental extension
|
||||
- Accident
|
||||
- Breakdown
|
||||
- Flat tire
|
||||
- Lost key
|
||||
- Vehicle issue
|
||||
- Return location question
|
||||
- Payment issue
|
||||
|
||||
## Rental Extension Process
|
||||
|
||||
Before approving an extension, staff must:
|
||||
|
||||
1. Check vehicle availability.
|
||||
2. Confirm the new return date and time.
|
||||
3. Calculate additional charges.
|
||||
4. Process additional payment or authorization.
|
||||
5. Update the rental agreement.
|
||||
6. Send written confirmation to the customer.
|
||||
|
||||
Verbal-only extensions are not allowed.
|
||||
|
||||
## Accident Process
|
||||
|
||||
If the customer reports an accident, staff must instruct the customer to:
|
||||
|
||||
- Stop safely.
|
||||
- Call emergency services if needed.
|
||||
- Take photos of all vehicles and damage.
|
||||
- Collect other driver details, if applicable.
|
||||
- Get a police report if required.
|
||||
- Contact the rental company immediately.
|
||||
- Avoid driving the vehicle if it is unsafe.
|
||||
|
||||
Company staff must:
|
||||
|
||||
- Open an incident record.
|
||||
- Collect photos and documents.
|
||||
- Contact the insurer if needed.
|
||||
- Arrange towing if needed.
|
||||
- Arrange a replacement vehicle if approved.
|
||||
- Change vehicle status if the vehicle is no longer rentable.
|
||||
|
||||
## Breakdown Process
|
||||
|
||||
If the vehicle breaks down, staff must:
|
||||
|
||||
1. Confirm the customer's location.
|
||||
2. Confirm customer safety.
|
||||
3. Ask about warning lights or symptoms.
|
||||
4. Contact roadside assistance.
|
||||
5. Arrange towing if required.
|
||||
6. Arrange a replacement vehicle if approved.
|
||||
7. Record the incident.
|
||||
8. Mark the vehicle as Needs Maintenance or Blocked.
|
||||
|
||||
---
|
||||
|
||||
# 5. Return
|
||||
|
||||
## Goal
|
||||
|
||||
The goal of the return process is to close the rental fairly, document the condition of the vehicle, and prepare the car for the next customer.
|
||||
|
||||
## Return Steps
|
||||
|
||||
1. Find the rental agreement.
|
||||
2. Record actual return time.
|
||||
3. Record return mileage.
|
||||
4. Record fuel or battery level.
|
||||
5. Inspect the exterior.
|
||||
6. Inspect the interior.
|
||||
7. Compare condition with pickup photos.
|
||||
8. Check keys and accessories.
|
||||
9. Check for customer belongings.
|
||||
10. Take return photos.
|
||||
11. Calculate final charges.
|
||||
12. Release or adjust the deposit.
|
||||
13. Send final receipt.
|
||||
14. Change vehicle status.
|
||||
|
||||
## Return Inspection
|
||||
|
||||
The return inspection must include:
|
||||
|
||||
- Exterior damage
|
||||
- Interior damage
|
||||
- Glass condition
|
||||
- Tire condition
|
||||
- Wheel condition
|
||||
- Fuel or battery level
|
||||
- Mileage
|
||||
- Smoking odor
|
||||
- Pet hair
|
||||
- Stains
|
||||
- Trash
|
||||
- Missing accessories
|
||||
- Warning lights
|
||||
- Keys and key fobs
|
||||
|
||||
The return inspection should be completed with the customer present whenever possible.
|
||||
|
||||
## Damage Found at Return
|
||||
|
||||
If new damage is found, staff must:
|
||||
|
||||
1. Compare pickup photos with return photos.
|
||||
2. Show the customer the damage.
|
||||
3. Take clear photos of the damage.
|
||||
4. Record the damage location and description.
|
||||
5. Ask the customer to sign the damage report.
|
||||
6. Escalate to a manager if the customer disputes the damage.
|
||||
7. Keep the vehicle in Damage Review status until resolved.
|
||||
|
||||
If the customer refuses to sign, staff must write:
|
||||
|
||||
> Customer refused to sign damage acknowledgment.
|
||||
|
||||
The staff member must still complete the damage report and upload evidence.
|
||||
|
||||
## Final Charges
|
||||
|
||||
Final billing may include:
|
||||
|
||||
- Base rental charge
|
||||
- Extra rental time
|
||||
- Late return fee
|
||||
- Extra mileage fee
|
||||
- Fuel refill fee
|
||||
- EV recharge fee
|
||||
- Cleaning fee
|
||||
- Smoking fee
|
||||
- Damage charge
|
||||
- Lost key charge
|
||||
- Missing accessory charge
|
||||
- Toll charges
|
||||
- Traffic fines
|
||||
- Administration fees
|
||||
|
||||
All charges must be supported by the rental agreement, system records, or photo evidence.
|
||||
|
||||
## Deposit Handling
|
||||
|
||||
If there are no additional charges:
|
||||
|
||||
- Close the rental.
|
||||
- Release the deposit hold.
|
||||
- Send the final receipt.
|
||||
|
||||
If there are additional charges:
|
||||
|
||||
- Deduct approved charges.
|
||||
- Send an itemized invoice.
|
||||
- Attach evidence if needed.
|
||||
- Release any remaining deposit.
|
||||
|
||||
## Return Checklist
|
||||
|
||||
- [ ] Rental agreement found
|
||||
- [ ] Return time recorded
|
||||
- [ ] Mileage recorded
|
||||
- [ ] Fuel or battery level checked
|
||||
- [ ] Exterior inspected
|
||||
- [ ] Interior inspected
|
||||
- [ ] Pickup photos reviewed
|
||||
- [ ] Return photos taken
|
||||
- [ ] Keys returned
|
||||
- [ ] Accessories returned
|
||||
- [ ] Lost property checked
|
||||
- [ ] Final charges calculated
|
||||
- [ ] Deposit released or adjusted
|
||||
- [ ] Receipt sent
|
||||
- [ ] Vehicle status updated
|
||||
|
||||
---
|
||||
|
||||
# 6. Post-Return Processing
|
||||
|
||||
## Lost Property Check
|
||||
|
||||
Staff must check:
|
||||
|
||||
- Glove box
|
||||
- Center console
|
||||
- Door pockets
|
||||
- Under seats
|
||||
- Trunk
|
||||
- Seat pockets
|
||||
- Charging cable area
|
||||
|
||||
Any found item must be logged with:
|
||||
|
||||
- Date found
|
||||
- Vehicle plate number
|
||||
- Rental agreement number
|
||||
- Item description
|
||||
- Staff name
|
||||
- Storage location
|
||||
- Customer notification status
|
||||
|
||||
## Cleaning
|
||||
|
||||
After return, assign the vehicle to the correct cleaning level:
|
||||
|
||||
- Light cleaning
|
||||
- Standard cleaning
|
||||
- Deep cleaning
|
||||
- Smoke treatment
|
||||
- Pet hair removal
|
||||
- Stain removal
|
||||
|
||||
If extra cleaning is required, staff must take photos before cleaning.
|
||||
|
||||
## Maintenance Check
|
||||
|
||||
Staff must check for:
|
||||
|
||||
- Dashboard warning lights
|
||||
- Tire pressure issues
|
||||
- Fluid leaks
|
||||
- Brake issues
|
||||
- Unusual sounds
|
||||
- Service due alerts
|
||||
- EV charging issues
|
||||
|
||||
After cleaning and maintenance review, update the vehicle status to one of the following:
|
||||
|
||||
- Available
|
||||
- Needs Cleaning
|
||||
- Needs Maintenance
|
||||
- Damage Review
|
||||
- Blocked
|
||||
|
||||
---
|
||||
|
||||
# 7. Simple Policy Set
|
||||
|
||||
The company must define clear written policies for:
|
||||
|
||||
- Cancellation
|
||||
- No-show
|
||||
- Late pickup
|
||||
- Late return
|
||||
- Fuel return
|
||||
- EV battery return
|
||||
- Mileage limit
|
||||
- Extra mileage
|
||||
- Insurance
|
||||
- Damage
|
||||
- Smoking
|
||||
- Pets
|
||||
- Cleaning
|
||||
- Lost keys
|
||||
- Tolls
|
||||
- Traffic fines
|
||||
- Unauthorized drivers
|
||||
- Accidents
|
||||
- Breakdowns
|
||||
- Deposit release timing
|
||||
|
||||
Each policy should be written in plain language and shown to the customer before pickup.
|
||||
|
||||
---
|
||||
|
||||
# 8. Staff Roles
|
||||
|
||||
## Rental Agent
|
||||
|
||||
The rental agent is responsible for:
|
||||
|
||||
- Processing bookings
|
||||
- Verifying customer documents
|
||||
- Explaining rental terms
|
||||
- Processing payments
|
||||
- Completing pickup inspections
|
||||
- Completing return inspections
|
||||
- Updating vehicle status
|
||||
|
||||
## Fleet Staff
|
||||
|
||||
Fleet staff are responsible for:
|
||||
|
||||
- Cleaning vehicles
|
||||
- Fueling or charging vehicles
|
||||
- Moving vehicles
|
||||
- Checking readiness
|
||||
- Reporting damage or maintenance issues
|
||||
|
||||
## Manager
|
||||
|
||||
The manager is responsible for:
|
||||
|
||||
- Approving exceptions
|
||||
- Handling disputes
|
||||
- Reviewing damage claims
|
||||
- Approving high-risk rentals
|
||||
- Monitoring daily operations
|
||||
- Reviewing overdue vehicles
|
||||
|
||||
---
|
||||
|
||||
# 9. System Requirements
|
||||
|
||||
The rental company should use one central rental system for:
|
||||
|
||||
- Bookings
|
||||
- Customer records
|
||||
- Vehicle records
|
||||
- Availability
|
||||
- Payments
|
||||
- Deposits
|
||||
- Rental agreements
|
||||
- Pickup photos
|
||||
- Return photos
|
||||
- Damage records
|
||||
- Invoices
|
||||
- Vehicle statuses
|
||||
|
||||
## Core System Rule
|
||||
|
||||
Each rental must have:
|
||||
|
||||
- One booking
|
||||
- One customer record
|
||||
- One assigned vehicle
|
||||
- One rental agreement
|
||||
- One final invoice
|
||||
|
||||
If staff need to check several different places to understand one rental, the system is too complicated.
|
||||
|
||||
---
|
||||
|
||||
# 10. Non-Negotiable Controls
|
||||
|
||||
The following controls must never be skipped:
|
||||
|
||||
- Valid driver's license check
|
||||
- Payment before vehicle release
|
||||
- Deposit before vehicle release
|
||||
- Signed rental agreement
|
||||
- Pickup photos
|
||||
- Return photos
|
||||
- Mileage record at pickup and return
|
||||
- Fuel or battery record at pickup and return
|
||||
- Written insurance choice
|
||||
- Written extension approval
|
||||
- Damage evidence before charging customer
|
||||
|
||||
These controls protect the company from avoidable losses and disputes.
|
||||
|
||||
---
|
||||
|
||||
# 11. Standard Operating Flow
|
||||
|
||||
## Before Pickup
|
||||
|
||||
1. Customer books vehicle.
|
||||
2. System confirms availability.
|
||||
3. Staff assigns vehicle.
|
||||
4. Vehicle is cleaned and checked.
|
||||
5. Customer receives pickup reminder.
|
||||
|
||||
## At Pickup
|
||||
|
||||
1. Staff verifies customer.
|
||||
2. Staff verifies license.
|
||||
3. Payment and deposit are processed.
|
||||
4. Vehicle is inspected.
|
||||
5. Agreement is signed.
|
||||
6. Keys are released.
|
||||
7. Vehicle status becomes On Rent.
|
||||
|
||||
## During Rental
|
||||
|
||||
1. Company supports customer if needed.
|
||||
2. Extensions are handled in writing.
|
||||
3. Accidents and breakdowns are recorded.
|
||||
4. Overdue rentals are monitored.
|
||||
|
||||
## At Return
|
||||
|
||||
1. Vehicle is returned.
|
||||
2. Mileage and fuel or battery are recorded.
|
||||
3. Vehicle is inspected.
|
||||
4. Final charges are calculated.
|
||||
5. Deposit is released or adjusted.
|
||||
6. Receipt is sent.
|
||||
7. Vehicle is cleaned and reset.
|
||||
|
||||
---
|
||||
|
||||
# 12. Operating Principle
|
||||
|
||||
The company should design the process so a new employee can follow it using checklists, not memory.
|
||||
|
||||
The process should rely on:
|
||||
|
||||
- Clear vehicle statuses
|
||||
- Short checklists
|
||||
- Photo evidence
|
||||
- Written confirmations
|
||||
- Manager approval for exceptions
|
||||
|
||||
A simple rental process is not a weak process. It is a controlled process with fewer places for mistakes to hide.
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 1.1 MiB |
Reference in New Issue
Block a user