fix inventory

This commit is contained in:
root
2026-06-11 11:06:32 -04:00
parent 9483750161
commit c91fa2ce4d
53 changed files with 2936 additions and 12666 deletions
@@ -1,83 +0,0 @@
# API Refactor Status
This file replaces the older pre-refactor task list that referenced route paths and module boundaries no longer present in the codebase.
Source of truth:
- `apps/api/src/app.ts`
- `apps/api/src/modules/*`
- `apps/api/src/http/*`
## Refactor State
The API has already been refactored into the modular structure the old task list was aiming for.
Current structure includes:
- `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`
The older checklist items that referenced:
- `apps/api/src/routes/*.ts`
- missing test tooling
- missing shared validation helpers
- missing shared response helpers
are no longer accurate and should not be used as active work items.
## Remaining Follow-Up Work
These are the only meaningful refactor follow-ups still worth tracking at a high level.
### 1. Keep OpenAPI coverage aligned with route growth
The API now has:
- Swagger UI at `/docs`
- OpenAPI JSON at `/api/v1/openapi.json`
But the OpenAPI document does not yet mirror every newer route group perfectly. Continue updating:
- `apps/api/src/swagger/openapi.ts`
- route schemas
- module docs
whenever new endpoints are added.
### 2. Expand integration test coverage
Integration tests exist, but the heaviest workflows still benefit from deeper coverage:
- subscription billing transitions
- marketplace reservation intake
- public booking and payment initialization
- reservation inspection and close flows
- admin billing operations
### 3. Reduce remaining direct Prisma orchestration in large services
The route layer is already thin in the current API. The next cleanup target is inside larger service files where orchestration still mixes:
- multi-step workflow logic
- transaction boundaries
- some direct Prisma writes
especially in:
- reservation flows
- subscription/billing flows
- admin billing flows
### 4. Keep docs aligned with disabled flows
Some legacy surfaces still exist as placeholders or schema remnants, for example:
- disabled Clerk webhook endpoint
- legacy `clerkUserId` field on `Employee`
- disabled renter signup/login API endpoints
Those should stay clearly marked in docs so design specs do not drift back toward removed implementations.
-41
View File
@@ -1,41 +0,0 @@
# Documentation Audit
Date: 2026-05-26
## Purpose
This note records the cleanup applied to the `docs/project-design` folder after comparing it with the live codebase.
## Drift That Was Removed
The older design docs included several features or assumptions that are not active in the current implementation:
- 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/*`
## Current Documentation Rule
The `docs/project-design` files should describe only one of two things:
1. current implemented behavior
2. explicit future plans, clearly labeled as plans
They should not describe removed systems as if they are still live.
## Current References
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
-166
View File
@@ -1,166 +0,0 @@
# Cookie Policy
**Platform:** RentalDriveGo
**Domain:** rentaldrivego.ma
**Last updated:** 2026-05-23
---
## 1. What Are Cookies
Cookies are small text files that a website stores on your device when you visit. They allow the site to remember information about your visit — such as your preferred language or whether you are signed in — so you do not have to re-enter it every time.
RentalDriveGo uses cookies only for the purposes described in this document. We do not use cookies to track your activity across third-party websites, serve advertisements, or build behavioural profiles.
---
## 2. Types of Cookies We Use
We use two categories of cookies: **strictly necessary** and **preference/functional**.
We do not use analytics cookies, advertising cookies, or third-party tracking cookies.
---
## 3. Cookie Details
### 3.1 Authentication Cookie
| Field | Value |
|---|---|
| **Name** | `employee_session` |
| **Category** | Strictly necessary |
| **Duration** | 8 hours (session) |
| **Scope** | All pages (`path=/`) |
| **Third-party** | No |
**Purpose:** This cookie is set when an employee signs in to the RentalDriveGo workspace. Admins receive a separate `admin_session` cookie. It stores a cryptographically signed token (JWT) that proves your identity to the platform. Without this cookie, the dashboard and protected API endpoints cannot verify who you are and will redirect you to the sign-in page.
**When it is set:** On a successful sign-in.
**When it is removed:** When you sign out, or automatically after 8 hours of inactivity.
**Can you opt out?** No. This cookie is required for the platform to function. Blocking it will prevent you from signing in.
---
### 3.2 Language Preference Cookie
| Field | Value |
|---|---|
| **Name** | `rentaldrivego-language` |
| **Category** | Functional / preference |
| **Duration** | 1 year |
| **Scope** | All pages (`path=/`) |
| **Third-party** | No |
**Purpose:** Stores your chosen display language so the interface appears in your preferred language on every visit across all parts of the platform (public marketplace, dashboard, and admin panel). Supported values are English (`en`), French (`fr`), and Arabic (`ar`).
**When it is set:** When you change the language using the language selector, or automatically on first visit based on your browser's language settings.
**Can you opt out?** You can block this cookie. If you do, the platform will fall back to a default language and you may need to select your language on every visit.
---
### 3.3 Theme Preference Cookie
| Field | Value |
|---|---|
| **Name** | `rentaldrivego-theme` |
| **Category** | Functional / preference |
| **Duration** | 1 year |
| **Scope** | All pages (`path=/`) |
| **Third-party** | No |
**Purpose:** Stores your preferred colour scheme — light mode or dark mode. This cookie is read immediately when a page loads (before the interface is drawn) to apply the correct theme without any visible flash of the wrong colours.
**When it is set:** When you toggle the light/dark mode switch.
**Can you opt out?** You can block this cookie. If you do, the platform will fall back to your operating system's colour scheme preference and the theme may reset on every visit.
---
### 3.4 Per-User Scoped Preference Cookies
| Field | Value |
|---|---|
| **Name** | `rentaldrivego-language--{userID}`, `rentaldrivego-theme--{userID}` |
| **Category** | Functional / preference |
| **Duration** | 1 year |
| **Scope** | All pages (`path=/`) |
| **Third-party** | No |
**Purpose:** When you are signed in, the platform saves your language and theme preferences under a cookie that is tied to your account identifier. This allows multiple employees who share a device (for example, in a shared office environment) to each maintain independent preferences. The `{userID}` portion is derived from the authentication token and does not contain personal information beyond an internal account identifier.
**When it is set:** On every language or theme change while you are signed in.
**Can you opt out?** You can block these cookies. If you do, your preferences will revert to the shared or default settings each time you sign in on a shared device.
---
### 3.5 Legacy Preference Cookies (Transitional)
| Field | Value |
|---|---|
| **Names** | `dashboard-language`, `marketplace-language` |
| **Category** | Functional / preference |
| **Duration** | 1 year |
| **Scope** | All pages (`path=/`) |
| **Third-party** | No |
**Purpose:** These cookies exist for backward compatibility with older versions of the platform. They store the same language preference as `rentaldrivego-language` but were scoped to individual sub-applications. New code always reads `rentaldrivego-language` first and only falls back to these if the primary cookie is absent.
**Planned removal:** These cookies will be removed once all users have transitioned to the unified preference system. They do not store any additional personal data.
---
## 4. What We Do Not Do
- We do **not** use cookies to track you across other websites.
- We do **not** share cookie data with advertisers or data brokers.
- We do **not** use session-replay or behavioural analytics cookies.
- We do **not** set cookies from third-party domains on our pages.
---
## 5. Cookie Consent
**Strictly necessary cookies** (section 3.1) do not require your consent because they are essential to provide the service you have requested.
**Functional / preference cookies** (sections 3.23.5) are used solely to remember your choices and improve your experience. They do not process personal data for marketing or tracking purposes. Under most privacy regulations (including GDPR), these may be set without explicit consent when used exclusively to fulfil user-requested functionality. Where local law requires explicit consent, a consent prompt will be displayed on your first visit.
---
## 6. How to Manage or Delete Cookies
You can control cookies through your browser settings. Common options include:
- **Block all cookies** — the platform will work but you will need to re-enter preferences on every visit and you will not be able to sign in.
- **Block third-party cookies** — this will have no effect on RentalDriveGo as we do not use third-party cookies.
- **Delete cookies on close** — you will be signed out and your preferences will reset each time you close the browser.
- **Delete specific cookies** — use your browser's developer tools (Application → Cookies) to remove individual cookies by name.
Browser-specific instructions:
| Browser | Settings location |
|---|---|
| Chrome | Settings → Privacy and security → Cookies and other site data |
| Firefox | Settings → Privacy & Security → Cookies and Site Data |
| Safari | Settings → Privacy → Manage Website Data |
| Edge | Settings → Cookies and site permissions → Cookies and site data |
---
## 7. Security
The authentication cookies (`employee_session` and `admin_session`) are signed, HttpOnly, and expire after the configured session window. They use `Secure` in production and are not readable by JavaScript. We recommend using the platform on trusted devices only and signing out when you are finished.
---
## 8. Changes to This Policy
We may update this Cookie Policy when we add new features or change how existing cookies work. The "Last updated" date at the top of this document will reflect any changes. Significant changes will be communicated through the platform or by email.
---
## 9. Contact
If you have questions about this Cookie Policy or how we handle your data, please contact us at:
**Email:** rentaldrivego@gmail.com
**Website:** https://rentaldrivego.ma
-152
View File
@@ -1,152 +0,0 @@
# Features — Current Product Scope
This document lists the features that are active in the current codebase.
Source of truth:
- `apps/marketplace`
- `apps/dashboard`
- `apps/admin`
- `apps/api`
- `packages/database/prisma/schema.prisma`
## Active Applications
The current workspace contains four active apps:
- `apps/marketplace`
- `apps/dashboard`
- `apps/admin`
- `apps/api`
There is no separate frontend app for a white-label company public site in this repo at the moment.
## Active Platform Features
### Company signup and employee access
- 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`
### Multi-tenant company operations
- 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
### 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
### 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
- 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
### Rental payments
- 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
### Notifications
- Email notifications
- SMS notifications
- WhatsApp notifications
- In-app notifications
- Notification templates and per-channel preferences
- Notification history for company users
### Reservation-adjacent advanced operations
- 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
### Admin app
- 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
These exist partially in code or schema, but they are not active end-user product flows and should not be treated as shipped features.
- 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
## Explicitly Out Of Scope For Current Docs
The following old design ideas should not be treated as active features unless code is added later:
- marketing blog
- standalone about/contact page set
- Google renter auth
- automatic custom-domain provisioning
- Zapier/webhook marketplace integrations beyond the current API/webhook handlers
## Notes
- 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.
@@ -0,0 +1,46 @@
# Finance Plan Implementation Notes
This implementation keeps the existing financial endpoints and PayPal-related code intact, then adds the missing additive finance lifecycle pieces from the plan.
## Added
- Finance lifecycle migration: follow-up notes, balance carryforwards, installment plans/installments, payment-installment allocations, notification logs/preferences, receipt sequences, and carryforward payment allocations.
- Parent payment follow-up reporting with CSV export and write-only metadata actions for notes, contacted status, promise-to-pay, and resolution.
- Prior-year balance carryforward preview, draft creation, approval, posting to a new-year invoice, waiver, adjustment, report, and CSV export.
- Installment plan creation, activation, cancellation, overdue/due reporting, and payment allocation.
- Finance notification logging endpoints for receipts, statements, overdue reminders, installment reminders, and notification log search.
- Event charge lifecycle API endpoints for create/list/show/update/delete, approve, void, and attach-to-invoice.
- Finance config flags for legacy ledger mode, carryforward behavior, payment allocation policy, and tuition calculator version.
## Safety posture
- Existing finance routes were not removed.
- Existing PayPal models/controllers/routes were not removed.
- Existing invoice/payment math remains the source for read models.
- Carryforward posting creates a separate new-year invoice instead of mutating prior-year invoices.
- Follow-up notes do not modify invoice balances.
- Installment allocation writes to additive allocation tables and installment balances only.
- Notifications currently log database events instead of sending real external email/SMS, because inboxes are not test benches, despite humanity's tireless effort to prove otherwise.
## Key files changed or added
- `database/migrations/2026_06_04_230000_add_finance_lifecycle_tables.php`
- `config/finance.php`
- `app/Services/Finance/ParentPaymentFollowUpService.php`
- `app/Services/Finance/PriorYearBalanceCarryforwardService.php`
- `app/Services/Finance/InstallmentPlanService.php`
- `app/Services/Finance/FinanceNotificationLogService.php`
- `app/Http/Controllers/Api/Finance/BalanceCarryforwardController.php`
- `app/Http/Controllers/Api/Finance/InstallmentPlanController.php`
- `app/Http/Controllers/Api/Finance/FinanceNotificationController.php`
- `app/Http/Controllers/Api/Finance/EventChargeController.php`
- `app/Http/Controllers/Api/Finance/FinancialController.php`
- `routes/api.php`
- New finance request classes under `app/Http/Requests/Finance/`
- New finance lifecycle models under `app/Models/`
## Not fully implemented by design
- Centralized ledger replacement is still behind the future phase. That is intentional. Replacing accounting math without dry-runs is how systems become folklore.
- External mail/SMS sending is not activated. The implementation logs notification intent first.
- Parent-facing UI pages are not included because this package is the Laravel API.
-141
View File
@@ -1,141 +0,0 @@
# Team Management Integration — Current Implementation
This document describes the current team-management integration in the live codebase.
Source of truth:
- `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/*`
## Current Model
Team management is local to RentalDriveGo. It no longer depends on Clerk.
The live flow is:
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=...
```
Relevant environment variables:
```env
DASHBOARD_URL=https://your-host/dashboard
NEXT_PUBLIC_DASHBOARD_URL=https://your-host/dashboard
NEXT_PUBLIC_API_URL=https://your-host/api/v1
```
## Dashboard Integration
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`
The dashboard consumes the team API directly through `apiFetch(...)`.
### Dashboard page behavior
- 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
## Password Setup / Invite Acceptance
The active invite-acceptance mechanism is the dashboard reset-password flow, not a Clerk callback flow.
Relevant pages:
- `apps/dashboard/src/app/reset-password/page.tsx`
- `apps/dashboard/src/app/reset-password/ResetPasswordPageClient.tsx`
This means:
- 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.
## Request Typing
The request object is extended in the API so team routes can rely on:
- `req.employee`
- `req.company`
- `req.companyId`
Those values are attached by the company auth and tenant middleware before team handlers run.
## What Was Removed
These older integration assumptions are no longer valid:
- 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
If this functionality returns in the future, it should be documented as a new integration rather than assumed from this file.
@@ -0,0 +1,162 @@
# Inventory System Improvement — Implementation Summary
## Overview
This implementation applies the first three sprints of the inventory improvement plan:
1. **Stabilize Inventory Data** — Make movement ledger the source of truth
2. **Movement and Audit Cleanup** — Append-only ledger with void/correction support
3. **Procurement Integration** — Link purchase orders to inventory movements
4. **Low-Stock and Reorder Workflow** — New endpoints and features
---
## Changes Made
### Phase 1: Stabilize Inventory Data
#### 1. Quantity removed from item updates
- **`app/Http/Requests/Inventory/InventoryItemUpdateRequest.php`**: Removed `quantity` from validatable fields. Stock changes must go through movements (adjust, audit, etc.).
- **`app/Services/Inventory/InventoryItemService.php`**: `filterItemData()` now only includes `quantity` during creation (`$lockedType === null`), not during updates.
- **`app/Http/Requests/Inventory/InventoryItemStoreRequest.php`**: Strengthened validation — `type` uses `in:classroom,book,office,kitchen`, `condition` uses `in:good,needs_repair,need_replace,cannot_find`, `category_id` checks `exists:inventory_categories,id`.
#### 2. Append-only movement ledger with void/correction
New migration: **`2026_06_11_074503_add_inventory_movement_audit_fields.php`**
Adds to `inventory_movements`:
- `voided_at` (timestamp) — when the movement was voided
- `voided_by` (bigint) — user who voided it
- `void_reason` (varchar 255) — reason for voiding
- `corrects_movement_id` (bigint) — links a correction to the original movement
- `source_type` / `source_id` (varchar/bigint) — polymorphic source reference
**`app/Models/InventoryMovement.php`**: Added fillable fields, casts, scopes (`notVoided`, `voided`), and relationships (`voidedBy`, `correctsMovement`, `corrections`).
**`app/Services/Inventory/InventoryMovementService.php`**:
| Method | Status | Notes |
|--------|--------|-------|
| `create()` | ⚠️ Deprecated | Still works, but marked `@deprecated`. Use `recordMovement()` instead. |
| `update()` | ⚠️ Deprecated | Still works. Prevents editing voided movements. |
| `delete()` | ⚠️ Deprecated | Prevents deleting movements that have corrections. |
| `bulkDelete()` | ⚠️ Deprecated | Same prevention for corrections. |
| `recordMovement()` | ✅ Primary method | Append-only, creates `initial` movement automatically. |
| `voidMovement()` | ✅ NEW | Marks original as voided + creates reverse correction. |
| `correctMovement()` | ✅ NEW | Adds an adjusting entry linked to the original. |
| `recalcQuantity()` | ✅ Updated | Excludes voided movements from sum calculations. |
| `reconcile()` | ✅ NEW | Compares stored quantity vs movement sum, reports discrepancies. |
#### 3. Reconciliation command
**`app/Console/Commands/InventoryReconcile.php`**: `php artisan inventory:reconcile` with `--school-year` and `--fix` options.
### Phase 2: Movement and Audit Cleanup
#### Void and correction API endpoints
**`app/Http/Controllers/Api/Inventory/InventoryMovementController.php`**:
- `POST movements/{id}/void` — requires `reason`
- `POST movements/{id}/correct` — requires `qty_change` and `reason`
Both require authentication and return appropriate success/error responses.
### Phase 3: Procurement Integration
#### 1. inventory_item_id on purchase_order_items
New migration: **`2026_06_11_074642_add_inventory_item_id_to_purchase_order_items.php`**
Adds `inventory_item_id` (unsigned int, nullable) to `purchase_order_items` with an index. This allows linking PO items to inventory items.
**`app/Models/PurchaseOrderItem.php`**: Added `inventory_item_id` to `$fillable`.
#### 2. PurchaseOrderReceiveService links to inventory
**`app/Services/PurchaseOrders/PurchaseOrderReceiveService.php`**:
- Injects `InventoryMovementService`
- On receiving PO items with `inventory_item_id` set, creates an inventory movement via `recordMovement()`
- Continues to update legacy `supplies.qty_on_hand` for backward compatibility
### Phase 4: Low-Stock and Reorder Workflow
#### New database fields
New migration: **`2026_06_11_074621_add_inventory_item_reorder_fields.php`**
Adds to `inventory_items`:
- `preferred_supplier_id`, `last_purchase_price`, `average_unit_cost`
- `reorder_point`, `reorder_quantity`, `lead_time_days`
- `barcode`, `qr_code`, `asset_tag`
**`app/Models/InventoryItem.php`**: Added fillable fields, casts, relationships (`preferredSupplier`), and scopes (`lowStock`, `outOfStock`).
**`app/Http/Resources/Inventory/InventoryItemResource.php`**: Exposes all new fields.
#### New API endpoints
**`app/Http/Controllers/Api/Inventory/InventoryController.php`**:
| Method | Endpoint | Description |
|--------|----------|-------------|
| `lowStock()` | `GET inventory/low-stock` | Lists items where `quantity <= reorder_point` |
| `reorderSuggestions()` | `GET inventory/reorder-suggestions` | Calculates suggested order quantities |
| `reorderRequest()` | `POST inventory/items/{id}/reorder-request` | Single item reorder info |
| `stockStatus()` | `GET inventory/stock-status` | All items with status: ok/low_stock/out_of_stock |
| `dashboard()` | `GET inventory/dashboard` | Summary counts by type, low stock, out of stock, etc. |
All routes are registered under `api/v1/inventory/` in `routes/api.php`.
---
## Database Migrations
| Migration | Description |
|-----------|-------------|
| `2026_06_11_074503_add_inventory_movement_audit_fields` | Adds void/correction fields to movements |
| `2026_06_11_074621_add_inventory_item_reorder_fields` | Adds reorder/supplier/barcode fields to items |
| `2026_06_11_074642_add_inventory_item_id_to_purchase_order_items` | Links PO items to inventory items |
---
## Key Backward Compatibility Notes
1. **Existing endpoints remain unchanged.** `GET/POST/PATCH/DELETE inventory/items`, `inventory/movements`, and all summary/distribution endpoints continue to work with the same payloads.
2. **Old `create/update/delete` movement methods** work as before, but are marked `@deprecated`. They still enforce new guards (e.g., cannot edit voided movements, cannot delete movements with corrections).
3. **Purchase order receiving** continues to update `supplies.qty_on_hand` for legacy support. The new inventory movement is added alongside, not instead.
4. **Voided movements** are excluded from quantity calculations by default. Existing reports that call `recalcQuantity()` automatically benefit.
---
## Console Commands
```bash
# Check for discrepancies between stored quantity and movement sum
php artisan inventory:reconcile
# With --fix to auto-correct discrepancies
php artisan inventory:reconcile --fix
# For a specific school year
php artisan inventory:reconcile --school-year=2025-2026
```
---
## API Reference
### New Endpoints
```
GET /api/v1/inventory/low-stock
GET /api/v1/inventory/reorder-suggestions
POST /api/v1/inventory/items/{id}/reorder-request
GET /api/v1/inventory/stock-status?type=book
GET /api/v1/inventory/dashboard?school_year=2025-2026
POST /api/v1/inventory/movements/{id}/void { reason: "..." }
POST /api/v1/inventory/movements/{id}/correct { qty_change: -5, reason: "..." }
```
@@ -1,739 +0,0 @@
# 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 teams default language unless configured otherwise.
---
## 2. Source of Truth for Notification Language
The users 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 recipients 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 users 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 users 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 recipients 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
View File
@@ -1,179 +0,0 @@
# 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`
@@ -0,0 +1,90 @@
# Strong Grading Implementation Notes
## Safety rule
This patch is additive and backward-compatible. Existing historical semester scores remain displayed from stored `semester_scores` values and are labeled as legacy by metadata. The new strong-grading machinery is present, but strong-mode finalization only activates when `strong_grading_enabled` is enabled through configuration.
## What changed
### Legacy display protection
`semester_scores` now supports:
- `calculation_mode`
- `calculation_policy_version`
- `snapshot_id`
Existing rows are backfilled as:
- `calculation_mode = legacy`
- `calculation_policy_version = legacy_v1`
No historical score is recalculated by this migration.
### Score safety columns
The score tables now support:
- `max_points`
- `status`
- `excused_reason`
- `locked_at`
- `locked_by`
Existing numeric score rows are marked `scored`. Existing blank rows are marked `pending`, not `missing`, so old data is not punished by the new policy.
### Validation
A central `ScoreValueValidator` rejects impossible scores before storage. The default max remains `100` to preserve old behavior until item-level max points are configured.
### Attendance grace
Attendance now names the one-absence grace policy explicitly instead of hiding it in `(total_days + 1 - absences) / total_days`. The result is intentionally equivalent.
### Strong-mode lock validation
`GradingLockService` now checks the policy mode before locking. In legacy mode, behavior remains compatible. In strong mode, pending scores and invalid score ranges block locking.
### Snapshot infrastructure
`semester_score_snapshots` and `SemesterScoreSnapshotService` were added. Strong-mode finalized scores can store calculation inputs and outputs for auditability.
### Display resolver
`GradeCalculationDisplayResolver` resolves whether a row should be shown as `Legacy Calculation` or `Strong Calculation`.
## Configuration gates
Strong mode is off by default.
Suggested configuration values:
```text
strong_grading_enabled = false
strong_grading_class_sections = *
```
When enabled:
```text
strong_grading_enabled = true
strong_grading_class_sections = 12,15,20
```
or all sections:
```text
strong_grading_class_sections = *
```
## What is intentionally not done yet
This patch does not globally replace the legacy PTAP formula. That would change grade math. The legacy formula remains the default until strong grading is intentionally activated for a class section.
This patch does not silently recalculate historical records. That would be reckless, so naturally we avoided it.
## Verification
PHP syntax checks passed for the modified and new PHP files.
PHPUnit could not run in this sandbox because the PHP runtime is missing required extensions: `dom`, `mbstring`, `xml`, and `xmlwriter`.
@@ -1,179 +0,0 @@
# Security Vulnerability Fix Report
**Date:** 2026-05-22
**Scope:** API (`apps/api`) — automated scan + manual review
**Branch:** `develop`
---
## Summary
A full security review of the API codebase identified 3 confirmed, high-confidence vulnerabilities. All 3 were fixed in the same session. A separate frontend scan (dashboard, admin, marketplace, public-site) found no confirmed vulnerabilities above the reporting threshold.
| # | Severity | Category | Status |
|---|----------|----------|--------|
| [VF-01](#vf-01--payment-credential-exposure) | High | Credential / Data Exposure | Fixed |
| [VF-02](#vf-02--webhook-signature-bypass) | High | Authentication Bypass | Fixed |
| [VF-03](#vf-03--idor-on-vehicle-maintenance-logs) | Medium-High | IDOR / Tenant Isolation | Fixed |
---
## VF-01 — Payment Credential Exposure
**Severity:** High
**Confidence:** 9/10
**Category:** Credential / Data Exposure
### Description
`GET /api/v1/companies/me/brand` returned the full `BrandSettings` database row, including the live payment gateway credentials `amanpaySecretKey`, `amanpayMerchantId`, `paypalEmail`, and `paypalMerchantId` in the JSON response. The endpoint had no role guard, so any authenticated employee — including the lowest-privilege `AGENT` role — could call it and receive the secret key in plaintext.
### Exploit Scenario
An `AGENT`-role employee calls `GET /api/v1/companies/me/brand` with a valid session token. The response body includes `amanpaySecretKey`. The attacker uses the merchant ID and secret key to interact directly with the AmanPay payment gateway on behalf of the company, initiating fraudulent charges or refunds outside the application.
### Root Cause
`presentBrand()` in `company.presenter.ts` was a transparent passthrough (`return brand`). The DB query had no `select` clause, so Prisma returned every column. The public-facing site module correctly stripped these fields (evidenced by explicit test assertions), but the authenticated dashboard endpoint did not.
### Fix
**File:** `apps/api/src/modules/companies/company.presenter.ts`
`presentBrand()` now destructures and drops all four credential fields before returning. The caller receives boolean presence indicators (`amanpayConfigured`, `paypalConfigured`) instead of the raw secrets.
```diff
export function presentBrand(brand: any) {
- return brand
+ if (!brand) return brand
+ const { amanpaySecretKey, amanpayMerchantId, paypalEmail, paypalMerchantId, ...safe } = brand
+ return {
+ ...safe,
+ amanpayConfigured: !!(amanpayMerchantId && amanpaySecretKey),
+ paypalConfigured: !!(paypalEmail || paypalMerchantId),
+ }
}
```
---
## VF-02 — Webhook Signature Bypass
**Severity:** High
**Confidence:** 8/10
**Category:** Authentication Bypass / Payment Fraud
### Description
Both payment and subscription webhook endpoints for AmanPay and PayPal used a short-circuit AND guard for signature verification:
```ts
if (amanpay.isConfigured() && !amanpay.verifyWebhookSignature(rawBody, signature)) {
return res.status(401).json({ error: 'invalid_signature' })
}
await service.handleAmanpayWebhook(req.body) // executed unconditionally
```
When `isConfigured()` returns `false` (i.e., credentials are absent or set to placeholder defaults), the entire guard short-circuits and the webhook handler executes on any unauthenticated request. The endpoints had no IP allowlist and were explicitly placed before `requireCompanyAuth`.
This affected 4 endpoints:
- `POST /api/v1/payments/webhooks/amanpay`
- `POST /api/v1/payments/webhooks/paypal`
- `POST /api/v1/subscriptions/webhooks/amanpay`
- `POST /api/v1/subscriptions/webhooks/paypal`
### Exploit Scenario
On any staging or misconfigured production environment where AmanPay credentials are not set, an attacker posts `{"transaction_id": "<known_id>", "status": "PAID"}` to `/api/v1/payments/webhooks/amanpay`. The handler finds the pending payment by `transactionId`, marks it succeeded, and unlocks the reservation — with no money changing hands. A valid `amanpayTransactionId` can leak through receipts, logs, or API responses visible to renters.
The same attack against `/api/v1/subscriptions/webhooks/amanpay` activates a paid subscription tier for free.
### Fix
**Files:** `apps/api/src/modules/payments/payment.routes.ts`, `apps/api/src/modules/subscriptions/subscription.routes.ts`
Inverted the guard logic: an unconfigured provider now immediately returns `401` instead of passing through. Applied to all 4 endpoints.
```diff
-if (amanpay.isConfigured() && !amanpay.verifyWebhookSignature(rawBody, signature)) {
+if (!amanpay.isConfigured() || !amanpay.verifyWebhookSignature(rawBody, signature)) {
return res.status(401).json({ error: 'invalid_signature' })
}
```
```diff
-if (paypal.isConfigured() && !isValid) return res.status(401).json({ error: 'invalid_signature' })
+if (!paypal.isConfigured() || !isValid) return res.status(401).json({ error: 'invalid_signature' })
```
---
## VF-03 — IDOR on Vehicle Maintenance Logs
**Severity:** Medium-High
**Confidence:** 9/10
**Category:** Insecure Direct Object Reference (IDOR) / Tenant Isolation Bypass
### Description
`GET /api/v1/vehicles/:id/maintenance` was protected by `requireCompanyAuth` and `requireTenant`, making `req.companyId` available. However, `req.companyId` was never forwarded to the service or repository. The database query used only `vehicleId` with no tenant filter:
```ts
// vehicle.repo.ts
prisma.maintenanceLog.findMany({ where: { vehicleId } })
```
An authenticated employee from Company A could fetch the full maintenance history of any vehicle belonging to any other company by knowing its UUID. The write path (`POST /:id/maintenance`) already performed the correct ownership check using `repo.findById(id, companyId)` — the read path did not.
### Exploit Scenario
An employee of Company A calls `GET /api/v1/vehicles/<vehicleId_from_company_B>/maintenance`. Vehicle UUIDs can leak through marketplace listings, shared bookings, or support interactions. The response contains the complete maintenance history of Company B's vehicle — including service records, mileage data, and operational schedules — across tenant boundaries.
### Fix
**Files:** `apps/api/src/modules/vehicles/vehicle.routes.ts`, `apps/api/src/modules/vehicles/vehicle.service.ts`
`req.companyId` is now threaded through to `getMaintenanceLogs`, which performs the same ownership pre-check already used by the write path. A cross-tenant request returns `404`.
```diff
// vehicle.routes.ts
-const logs = await service.getMaintenanceLogs(id)
+const logs = await service.getMaintenanceLogs(id, req.companyId)
```
```diff
// vehicle.service.ts
-export async function getMaintenanceLogs(id: string) {
- return repo.findMaintenanceLogs(id)
+export async function getMaintenanceLogs(id: string, companyId: string) {
+ const vehicle = await repo.findById(id, companyId)
+ if (!vehicle) throw new NotFoundError('Vehicle not found')
+ return repo.findMaintenanceLogs(id)
}
```
---
## Frontend Scan Results
A full scan of all four frontend applications (dashboard, admin, marketplace, public-site) was performed. No vulnerabilities above the reporting threshold (confidence ≥ 8/10) were confirmed.
### Hardening Recommendations (not vulnerabilities)
Two items were identified as best-practice improvements:
1. **Admin auth-redirect `next` param** (`apps/admin/src/app/auth-redirect/page.tsx`) — Validate the `next` query parameter is a relative path (starts with `/`, does not contain `://`) before passing it to `router.replace`. The router itself does not perform cross-origin navigation, but defense-in-depth is recommended.
2. **`postMessage` wildcard origin + missing `frame-ancestors` CSP** (`apps/dashboard/src/app/sign-in/[[...sign-in]]/SignInPageClient.tsx`, `apps/dashboard/next.config.js`) — Replace `targetOrigin: '*'` with the marketplace origin, and add `Content-Security-Policy: frame-ancestors 'self' <marketplace-origin>` to prevent the sign-in page from being embeddable by arbitrary third-party domains.
---
## Files Changed
| File | Change |
|------|--------|
| `apps/api/src/modules/companies/company.presenter.ts` | Strip payment credentials in `presentBrand()` |
| `apps/api/src/modules/payments/payment.routes.ts` | Fix webhook signature bypass (AmanPay + PayPal) |
| `apps/api/src/modules/subscriptions/subscription.routes.ts` | Fix webhook signature bypass (AmanPay + PayPal) |
| `apps/api/src/modules/vehicles/vehicle.routes.ts` | Pass `companyId` to maintenance log service |
| `apps/api/src/modules/vehicles/vehicle.service.ts` | Add ownership check in `getMaintenanceLogs` |
File diff suppressed because it is too large Load Diff
-396
View File
@@ -1,396 +0,0 @@
# API Architecture and Route Design
This document explains how the API is structured today, how requests move through the stack, and what each route group is responsible for.
Source of truth for the runtime wiring:
- `apps/api/src/app.ts`
- `apps/api/src/modules/*`
- `apps/api/src/middleware/*`
- `apps/api/src/swagger/openapi.ts`
## Runtime Overview
The API is an Express application mounted under `/api/v1` with a few non-versioned utility endpoints:
- `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.
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.
## Request Lifecycle
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": ... }`.
Common exceptions:
- `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
There are four main access patterns.
### Employee JWT
`requireCompanyAuth` validates a Bearer token with `type === "employee"`, loads the employee, and attaches:
- `req.employee`
- `req.company`
- `req.companyId`
This is the default for dashboard/company management routes.
### Tenant Resolution
`requireTenant` reloads the company from `req.companyId` and guarantees the tenant context exists. Company-scoped modules then query by `companyId`.
### 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.
### Role-Based Access Control
Employee roles are hierarchical:
- `OWNER`
- `MANAGER`
- `AGENT`
Admin roles are hierarchical:
- `SUPER_ADMIN`
- `ADMIN`
- `SUPPORT`
- `FINANCE`
- `VIEWER`
### Renter JWT
`requireRenterAuth` validates a Bearer token with `type === "renter"` and attaches `req.renterId`.
`optionalRenterAuth` is used on marketplace routes so public reads still work without a token.
### API Key
`requireApiKey` accepts a company public API key in `x-api-key`. This is the low-friction integration path for public company-site style calls.
## Implementation Pattern
The API codebase is organized per module. The most common pattern is:
- `*.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
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.
Some modules split specialized workflows into additional services instead of one large file. The reservations module is the clearest example:
- `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
The table below describes the current route groups mounted in `app.ts`.
| 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
### Company and employee onboarding
- `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
The `companies` module owns tenant-level settings:
- 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.
### Fleet management
The `vehicles` module handles:
- 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/*`.
### Reservations and rental workflow
The reservation aggregate is the center of the operational API.
Important transitions:
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
{
"data": {}
}
```
Common deviations:
- `/health`
- `/api/v1/docs`
- webhook receipt payloads
- file downloads such as admin invoice PDFs
## Documentation Artifacts
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
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.
@@ -1,62 +0,0 @@
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.
@@ -1,699 +0,0 @@
# 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 dexercice n° :** [●]
**Date de délivrance de lagré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 lactivité 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 lactivité est :
**Nom et prénom :** [●]
**Qualité :** [Dirigeant / Actionnaire / Salarié]
**Pièce didentité n° :** [●]
**Qualification / diplôme / expérience :** [●]
**Téléphone :** [●]
**Email :** [●]
Cette personne est responsable du suivi des contrats, de lentretien 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 lexercice de lactivité ;
- être régulièrement immatriculé auprès des administrations compétentes ;
- disposer dun 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 dun permis de conduire valide :
**Numéro du permis :** [●]
**Catégorie :** [B / autre]
**Date de délivrance :** [●]
**Date dexpiration :** [●]
**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 quil nest frappé daucune 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 daccident, vol, infraction ou dommage causé par un conducteur non autorisé, le Locataire assume lentiè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 dexploitation 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 dexploitation :** [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 dexploitation applicables à sa catégorie, notamment en matière d’âge, dimmatriculation, dassurance, dentretien 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 danomalie mécanique, voyant dalerte, bruit anormal, crevaison, panne ou incident susceptible daffecter la sécurité, le Locataire doit cesser lutilisation 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 lassurance ;
- la franchise dassurance ;
- 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 dassurance :** [●]
**Numéro de police :** [●]
**Type de couverture :** [Responsabilité civile / Tous risques / autre]
**Franchise applicable :** [●] MAD
**Assistance :** [Oui / Non]
**Numéro dassistance :** [●]
Le Locataire reconnaît avoir été informé des garanties, exclusions et franchises applicables.
Sont notamment exclus de la couverture, sauf stipulation contraire de lassureur :
- conduite par une personne non autorisée ;
- conduite sans permis valide ;
- conduite sous lemprise dalcool, 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 daccident 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 sengage à 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 ;
- dutiliser 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 lagence linterdit ;
- 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, à lheure et au lieu prévus au contrat, dans l’état où il lui a été remis, sous réserve de lusure normale.
Le véhicule doit être restitué avec :
- le même niveau de carburant quau 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 linté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 dassurance applicables.
---
## 15. Accident, panne, vol ou sinistre
En cas daccident, 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 daccident ;
- 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 lassureur.
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 lassureur 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 lassurance ;
- 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 lassurance ;
- de la franchise prévue au contrat dassurance.
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 lidentité 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 linfraction.
**Frais administratifs par infraction : [●] MAD TTC.**
---
## 18. Annulation, non-présentation et résiliation
En cas dannulation 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 dusage illicite.
Dans ce cas, le Locataire doit restituer immédiatement le véhicule.
---
## 19. Indisponibilité administrative, réglementaire ou technique
En cas dindisponibilité du véhicule ou dimpossibilité dexé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 à lexé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 :
- lexé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 sengage à 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 lattestation dassurance ;
- Justificatif autorisant lexploitation du véhicule par le Loueur, si le véhicule nappartient 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 sefforceront de trouver une solution amiable.
À défaut daccord amiable, le litige sera porté devant le tribunal compétent du ressort de :
**[Ville du siège de lagence / 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 dun permis valide ;
- sengager à 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 dassurance, 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é à lactivité réelle du Loueur, à ses conditions dassurance, à 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 lactivité 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 dexploitation des voitures louées sans chauffeur | Texte spécifique encadrant lexploitation des véhicules loués sans chauffeur. |
| **Cahier des charges relatif à la location de voitures sans chauffeur, tel quen vigueur** | Conditions administratives, techniques et professionnelles de lactivité | Référence pratique pour lagrément, la flotte, le responsable dactivité, 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 lusage 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 lagence
| 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 à lactivité 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 lune 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 lorsquil 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 lagence. |
| **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 quen vigueur, y compris ses modifications, circulaires dapplication 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 dexercice 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 dexploitation des véhicules disponibles.
- Assurance et visite technique valides pour chaque véhicule.
- Procédure de déclaration en cas de suspension ou cessation dactivité.
@@ -0,0 +1,866 @@
# Inventory System Improvement Plan
## Project Context
This document outlines a practical improvement plan for the inventory system in the `alrahma_sunday_school_api` project.
The current backend already includes a meaningful inventory module with items, categories, movements, classroom audits, book distribution, summaries, suppliers, supplies, and purchase-order receiving. However, the system has several architectural and operational gaps that should be fixed before adding more features.
The main goal is to make inventory data trustworthy, auditable, and operationally useful.
---
## Current Inventory Capabilities
The project currently includes the following inventory-related capabilities:
- Inventory item CRUD
- Inventory category CRUD
- Inventory movement tracking
- Stock adjustment
- Classroom item audit
- Teacher book distribution
- Inventory summaries by type
- Global inventory summary reports
- Supplier management
- Supply categories
- Supplies and supply transactions
- Purchase order receiving
- Inventory service tests
Relevant areas of the codebase include:
- `app/Models/InventoryItem.php`
- `app/Models/InventoryMovement.php`
- `app/Models/InventoryCategory.php`
- `app/Services/Inventory/InventoryItemService.php`
- `app/Services/Inventory/InventoryMovementService.php`
- `app/Services/Inventory/InventoryTeacherDistributionService.php`
- `app/Services/Inventory/InventorySummaryService.php`
- `app/Http/Controllers/Api/Inventory/*`
- `app/Http/Requests/Inventory/*`
- `database/migrations/*inventory*`
- `app/Services/PurchaseOrders/PurchaseOrderReceiveService.php`
---
## Main Problems
### 1. Inventory and Supplies Are Split
The system has both:
- `inventory_items` / `inventory_movements`
- `supplies` / `supply_transactions`
Purchase order receiving updates `supplies.qty_on_hand`, while inventory reports appear to rely mainly on `inventory_items` and `inventory_movements`.
This creates two separate sources of truth. That is dangerous because purchased supplies may not appear correctly in inventory reports.
### 2. Quantity Can Drift From Movements
`inventory_items.quantity` is stored directly, but it is also recalculated from inventory movements.
This can work only if all stock changes go through the movement ledger. Currently, item updates appear capable of modifying quantity directly, which can cause inventory drift.
The required invariant is:
```text
inventory_items.quantity = sum(inventory_movements.qty_change)
```
The system should enforce this rule aggressively.
### 3. Movement History Is Too Editable
Inventory movements can be updated or deleted.
For a real inventory system, this weakens auditability. Historical stock movements should generally be append-only. Corrections should be handled through reversal or correction movements, not by editing or deleting previous records.
### 4. No Low-Stock or Reorder Workflow
The current system tracks quantity but does not appear to include:
- Reorder point
- Reorder quantity
- Low-stock alerts
- Out-of-stock status
- Preferred supplier per item
- Automatic reorder suggestions
This forces admins to manually notice shortages.
### 5. Book Distribution Is Not a Full Assignment Lifecycle
The project has teacher book distribution, but the lifecycle is incomplete.
Missing capabilities include:
- Return book
- Mark book as damaged
- Mark book as lost
- Exchange book
- Track who currently has which book/item
- Prevent duplicate active assignment cleanly
The system can record that items were distributed, but it does not fully answer where those items are now.
### 6. No Structured Location Tracking
Inventory has categories and item types, but no strong location model.
Missing location support includes:
- Storage room
- Classroom
- Shelf/bin
- Teacher possession
- Office/kitchen location
- Transfer between locations
Quantity without location is only partially useful.
### 7. Classroom Audits Are Too Narrow
Classroom audits support condition checks such as good, needs repair, needs replacement, or missing. However, condition tracking should be expanded to other item types, especially books and durable assets.
### 8. Authorization Needs to Be Stronger
Inventory mutation actions should have explicit permissions.
Sensitive actions include:
- Stock adjustment
- Movement correction
- Item deletion/archive
- Purchase order receiving
- Supplier management
- Book distribution
- Inventory reports
- Cost visibility
Authentication alone is not enough for inventory operations.
---
# Recommended Improvement Plan
## Phase 1: Stabilize Inventory Data
### 1. Make the Movement Ledger the Source of Truth
Inventory quantity should only change through movements.
Rules:
- `inventory_items.quantity` should be treated as a cached value.
- Normal item updates should not allow direct quantity changes.
- Initial quantity may be set during item creation.
- Future changes must go through movement creation.
- A reconciliation command should compare stored quantity against movement totals.
Suggested command:
```bash
php artisan inventory:reconcile --school-year=2026-2027
```
Acceptance criteria:
- Updating item metadata cannot change quantity.
- Every quantity change creates an inventory movement.
- Reconciliation can detect and report quantity drift.
---
### 2. Replace Movement Editing and Deleting With Corrections
Inventory movements should become append-only.
Instead of updating or deleting a movement, create a correction movement.
Suggested new fields:
```php
voided_at
voided_by
void_reason
corrects_movement_id
source_type
source_id
```
Example correction:
```text
Original movement: out -5
Correction movement: adjust +5
```
Acceptance criteria:
- Normal users cannot hard-delete movements.
- Corrections preserve original records.
- Reports can exclude voided movements by default.
- Audit mode can show full movement history.
---
### 3. Improve Validation
Inventory request validation should be stricter.
Recommended validation rules:
- `type` must be one of the allowed inventory types.
- `condition` must be one of the allowed conditions.
- `category_id` must exist.
- Initial quantity must be an integer greater than or equal to zero.
- `semester` must use allowed values.
- `school_year` should match a valid format or existing school year record.
- `grade_min` must be less than or equal to `grade_max`.
Acceptance criteria:
- Invalid inventory types fail validation.
- Invalid categories fail validation.
- Negative quantities are rejected.
- Invalid grade ranges are rejected.
---
## Phase 2: Unify Procurement and Inventory
### 4. Merge or Link Supplies With Inventory Items
The system must decide whether `supplies` are separate from inventory or part of inventory.
Recommended approach:
- Treat supplies as inventory items.
- Link purchase order items to `inventory_items`.
- Receiving a purchase order should create an inventory movement.
Target flow:
```text
Purchase Order Received
Inventory Movement Created
Inventory Item Quantity Updated
Inventory Summary Reflects Purchase
```
Suggested receiving movement:
```php
InventoryMovementService::recordMovement(
itemId: $inventoryItemId,
qtyChange: $toReceive,
type: 'in',
reason: 'Purchase order received',
note: 'PO ' . $purchaseOrder->po_number,
performedBy: $userId
);
```
Acceptance criteria:
- Receiving a purchase order increases the correct inventory item quantity.
- Inventory reports include purchase order receipts.
- `supplies` and `inventory_items` no longer represent disconnected stock worlds.
---
### 5. Add Supplier and Reorder Fields to Inventory Items
Suggested fields:
```php
preferred_supplier_id
last_purchase_price
average_unit_cost
reorder_point
reorder_quantity
lead_time_days
```
These fields allow the system to answer:
- What needs to be reordered?
- From which supplier?
- How much should be ordered?
- How much will it likely cost?
- How long will it take to arrive?
Acceptance criteria:
- Items can have preferred suppliers.
- Items can define low-stock thresholds.
- Reorder reports can use these fields.
---
## Phase 3: Add Missing Operational Features
### 6. Add Low-Stock and Reorder Workflow
New endpoints:
```http
GET /api/v1/inventory/low-stock
GET /api/v1/inventory/reorder-suggestions
POST /api/v1/inventory/items/{id}/reorder-request
```
Basic reorder logic:
```text
if current_quantity <= reorder_point:
suggest reorder_quantity
```
Acceptance criteria:
- Dashboard shows low-stock items.
- Admins can view reorder suggestions.
- Reorder requests can be generated from low-stock items.
---
### 7. Add Location Tracking
Suggested tables:
```php
inventory_locations
inventory_item_locations
```
Example `inventory_locations` fields:
```php
id
name
type
class_section_id
responsible_user_id
notes
```
Example `inventory_item_locations` fields:
```php
id
item_id
location_id
quantity
condition
updated_at
```
Suggested movement fields:
```php
from_location_id
to_location_id
```
Supported movement examples:
- Receive into storage
- Transfer from storage to classroom
- Transfer from classroom to teacher
- Return from teacher to storage
- Mark item missing from classroom
Acceptance criteria:
- Total quantity across locations equals item quantity.
- Admins can see where each item is located.
- Transfers create inventory movements.
---
### 8. Add Inventory Assignment Lifecycle
Create a proper assignment table:
```php
inventory_assignments
```
Suggested fields:
```php
id
item_id
student_id
teacher_id
class_section_id
quantity
status
assigned_at
returned_at
assigned_by
returned_by
condition_out
condition_in
notes
school_year
semester
```
Suggested statuses:
```text
assigned
returned
lost
damaged
replaced
```
New endpoints:
```http
POST /api/v1/inventory/assignments
POST /api/v1/inventory/assignments/{id}/return
POST /api/v1/inventory/assignments/{id}/mark-lost
POST /api/v1/inventory/assignments/{id}/mark-damaged
GET /api/v1/inventory/assignments
```
Assignment flow:
1. Create assignment.
2. Create inventory movement out.
3. Prevent duplicate active assignment for the same item and student.
Return flow:
1. Mark assignment as returned.
2. Create inventory movement in.
3. Capture return condition.
Acceptance criteria:
- Admins can see who currently has each assigned item.
- Returned books increase available stock.
- Lost and damaged items are tracked.
---
### 9. Add Barcode and QR Support
Add fields to inventory items:
```php
barcode
qr_code
asset_tag
```
For individually tracked assets, add:
```php
inventory_item_units
```
Suggested fields:
```php
id
item_id
asset_tag
barcode
serial_number
status
condition
location_id
```
Suggested statuses:
```text
available
assigned
lost
damaged
retired
```
Use individual units for books and durable assets. Use aggregate quantities for consumables.
Acceptance criteria:
- Durable assets can be scanned individually.
- Consumables remain quantity-based.
- The system does not force every small consumable item to be tracked individually.
---
## Phase 4: Reporting and Audit
### 10. Add Inventory Dashboard
Dashboard should include:
- Total items by type
- Low-stock count
- Out-of-stock count
- Items needing repair
- Missing/lost items
- Books assigned by class
- Recent movements
- Pending reorder requests
- Reconciliation variance count
New endpoint:
```http
GET /api/v1/inventory/dashboard
```
---
### 11. Add Inventory Reports
Recommended report endpoints:
```http
GET /api/v1/inventory/reports/stock-on-hand
GET /api/v1/inventory/reports/movement-ledger
GET /api/v1/inventory/reports/assigned-items
GET /api/v1/inventory/reports/missing-damaged
GET /api/v1/inventory/reports/reorder
GET /api/v1/inventory/reports/year-end
```
Useful filters:
- School year
- Semester
- Type
- Category
- Location
- Class section
- Teacher
- Student
- Movement type
- Date range
Export options:
```http
?format=csv
?format=pdf
```
Acceptance criteria:
- Reports can be filtered by date and category.
- Admins can export CSV.
- PDF export can be added after CSV is stable.
---
### 12. Add Audit Logs for Sensitive Actions
Sensitive actions should be logged.
Actions to log:
- Stock adjustment
- Movement correction
- Item deletion/archive
- Purchase order receiving
- Assignment return
- Assignment lost/damaged update
- Category deletion
- Supplier changes
Suggested audit fields:
```php
actor_id
action
entity_type
entity_id
before_json
after_json
ip_address
user_agent
created_at
```
Acceptance criteria:
- Admins can see who changed stock and why.
- No silent stock-changing operation exists.
- Deleted inventory records are archived when possible.
---
## Phase 5: Permissions and Approval Workflow
### 13. Add Inventory Permissions
Suggested permissions:
```text
inventory.view
inventory.item.create
inventory.item.update
inventory.item.archive
inventory.stock.adjust
inventory.stock.correct
inventory.stock.audit
inventory.book.distribute
inventory.assignment.return
inventory.supplier.manage
inventory.purchase.receive
inventory.report.view
inventory.cost.view
```
Acceptance criteria:
- Inventory actions use explicit permissions.
- Stock adjustment is not available to every authenticated user.
- Cost visibility can be restricted separately.
---
### 14. Add Approval Workflow for Large Adjustments
Create:
```php
inventory_adjustment_requests
```
Suggested fields:
```php
id
item_id
requested_qty_change
reason
status
requested_by
approved_by
approved_at
rejected_reason
```
Suggested statuses:
```text
pending
approved
rejected
```
Rules:
- Small adjustments can be immediate for users with permission.
- Large adjustments require approval.
- Approval creates the real inventory movement.
Acceptance criteria:
- Large adjustments do not change stock immediately.
- Approval and rejection are logged.
- Pending adjustments appear in dashboard.
---
## Phase 6: Frontend and Admin Screens
The backend has inventory capability, but the project should add or improve admin screens for inventory operations.
Recommended screens:
1. Inventory dashboard
2. Inventory item list with filters
3. Inventory item detail page
4. Movement ledger
5. Add/edit item form
6. Stock adjustment modal
7. Assignment and return screen
8. Teacher book distribution screen
9. Low-stock and reorder screen
10. Supplier management screen
11. Purchase receiving screen
12. Reports and exports screen
13. Audit and reconciliation screen
UX rules:
- Show on-hand, assigned, available, damaged, missing, and reorder status.
- Every stock-changing action must require a reason.
- Dangerous actions should preview the resulting quantity.
- Item detail pages should show movement history.
- Low-stock items should be visible without manual searching.
---
# Recommended Implementation Order
## Sprint 1: Data Integrity
- Remove direct quantity updates from item update logic.
- Strengthen request validation.
- Add reconciliation command.
- Add tests for quantity/movement consistency.
- Restrict hard deletion of movements.
## Sprint 2: Movement and Audit Cleanup
- Add movement correction fields.
- Add void/correction flow.
- Add audit logs.
- Add explicit permission checks for inventory mutation.
## Sprint 3: Procurement Integration
- Link purchase order receiving to inventory movements.
- Decide how `supplies` maps to `inventory_items`.
- Add supplier and reorder fields.
- Add reorder reports.
## Sprint 4: Assignments and Returns
- Add `inventory_assignments` table.
- Refactor book distribution to create assignments.
- Add return/lost/damaged flows.
- Add assignment reports.
## Sprint 5: Locations
- Add location tables.
- Add transfer movements.
- Add stock-by-location report.
- Add location filters to inventory screens and reports.
## Sprint 6: Dashboard and Reports
- Add inventory dashboard endpoint.
- Add low-stock endpoint.
- Add stock-on-hand report.
- Add movement ledger report.
- Add missing/damaged report.
- Add assignment report.
- Add CSV export.
---
# Missing Feature Backlog
| Feature | Priority | Reason |
|---|---:|---|
| Ledger-only stock changes | Critical | Prevents stock corruption |
| Reconciliation command | Critical | Detects existing quantity drift |
| Movement correction instead of delete | Critical | Preserves audit trail |
| Strong permissions | Critical | Prevents unauthorized stock mutation |
| Procurement integration | High | Purchase receiving must affect inventory |
| Low-stock alerts | High | Prevents shortages |
| Reorder workflow | High | Turns shortages into action |
| Item assignment and return | High | Required for books and student materials |
| Location tracking | High | Shows where inventory actually is |
| Damaged/lost lifecycle | High | Supports accountability |
| Barcode/QR support | Medium | Improves audits and distribution |
| Inventory dashboard | Medium | Makes inventory easier to operate |
| CSV/PDF reports | Medium | Supports administration and accountability |
| Approval workflow | Medium | Controls risky stock changes |
| Individual item units | Medium | Useful for books and durable assets |
---
# First Code Changes to Make
## 1. Update `InventoryItemService::update()`
Remove quantity from normal editable fields.
Quantity should only change through:
- Item creation
- Stock adjustment
- Inventory movement creation
- Purchase order receiving
- Assignment return
- Audit correction
## 2. Refactor `InventoryMovementService`
Move toward append-only ledger behavior.
Replace or deprecate:
```php
update()
delete()
bulkDelete()
```
Add:
```php
recordMovement()
voidMovement()
correctMovement()
```
## 3. Update `PurchaseOrderReceiveService`
Receiving a purchase order should create an inventory movement, not only a supply transaction.
Recommended target behavior:
```php
InventoryMovementService::recordMovement(
itemId: $inventoryItemId,
qtyChange: $toReceive,
type: 'in',
reason: 'Purchase order received',
note: 'PO ' . $purchaseOrder->po_number,
performedBy: $userId
);
```
## 4. Add New Migrations
Recommended migrations:
```text
add_inventory_item_reorder_fields
add_inventory_movement_audit_fields
create_inventory_locations
create_inventory_item_locations
create_inventory_assignments
create_inventory_adjustment_requests
create_inventory_item_units
```
---
# Final Recommendation
Do not start by adding more endpoints. The project already has enough endpoint-shaped objects wandering around.
The first priority is to make inventory data reliable.
Recommended order:
1. Protect the stock invariant.
2. Stop editable/deletable movement history.
3. Unify procurement with inventory movements.
4. Add assignment and return tracking.
5. Add locations and low-stock workflows.
6. Build dashboard and reporting on top.
This turns the inventory system from a set of stock-related APIs into an actual operational system that admins can trust.
@@ -1,694 +0,0 @@
# 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 customers 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.
-778
View File
@@ -1,778 +0,0 @@
# Database Schema and Table Relationships
This document explains the current Prisma schema, how the data is partitioned, and how the main tables relate to each other.
Source of truth:
- `packages/database/prisma/schema.prisma`
## Design Principles
The schema is built around a multi-tenant SaaS model with three distinct concerns:
1. company operations for running a rental business
2. renter-facing booking and review flows
3. platform-level SaaS billing and administration
Important design choices:
- `Company` is the tenant root for nearly every business table.
- operational data and platform billing data are kept separate.
- reservation-time snapshots are stored explicitly so later changes to policies or prices do not rewrite history.
- some user concepts intentionally exist twice:
- `Renter` is a cross-company identity
- `Customer` is a company-owned CRM record
- status changes are modeled with enums rather than free-text fields where the workflow matters.
## High-Level Domain Map
```mermaid
erDiagram
Company ||--o| Subscription : has
Company ||--o| BrandSettings : has
Company ||--o| ContractSettings : has
Company ||--o| AccountingSettings : has
Company ||--o{ Employee : employs
Company ||--o{ Vehicle : owns
Company ||--o{ Offer : publishes
Company ||--o{ Customer : manages
Company ||--o{ Reservation : receives
Company ||--o{ RentalPayment : collects
Company ||--o{ InsurancePolicy : defines
Company ||--o{ PricingRule : defines
Company ||--o{ BillingAccount : bills
Company ||--o{ BillingInvoice : invoices
Company ||--o{ SubscriptionInvoice : tracks
Company ||--o{ Notification : receives
Company ||--o{ Complaint : tracks
Vehicle ||--o{ Reservation : booked_in
Vehicle ||--o{ MaintenanceLog : has
Vehicle ||--o{ VehicleCalendarBlock : blocked_by
Offer ||--o{ OfferVehicle : applies_to
Vehicle ||--o{ OfferVehicle : offered_in
Customer ||--o{ Reservation : books
Renter ||--o{ Reservation : owns
Reservation ||--o{ RentalPayment : paid_by
Reservation ||--o{ ReservationInsurance : includes
Reservation ||--o{ AdditionalDriver : adds
Reservation ||--o{ DamageInspection : inspected_by
Reservation ||--o{ DamageReport : reported_by
Reservation ||--o{ ReservationPhoto : photographed_by
Reservation ||--o| Review : reviewed_as
Reservation ||--o{ Complaint : disputed_in
InsurancePolicy ||--o{ ReservationInsurance : snapshotted_into
Renter ||--o{ Review : writes
Review ||--o{ Complaint : escalates_to
AdminUser ||--o{ AdminPermission : has
AdminUser ||--o{ AuditLog : writes
```
## Core Tenant Root
### `Company`
`Company` is the anchor row for tenant isolation.
Key fields:
- identity: `id`, `name`, `slug`, `email`, `phone`
- public/integration: `apiKey`
- SaaS state: `status`, `subscriptionPaymentRef`
- address: `address` JSON
Key relations:
- 1:1 with `Subscription`
- 1:1 with `BrandSettings`
- 1:1 with `ContractSettings`
- 1:1 with `AccountingSettings`
- 1:N with `Employee`, `Vehicle`, `Offer`, `Customer`, `Reservation`, `RentalPayment`
- 1:N with billing tables such as `BillingAccount`, `BillingInvoice`, `SubscriptionInvoice`
- 1:N with `InsurancePolicy`, `PricingRule`, `Notification`, `Complaint`
Important uniqueness:
- `slug`
- `email`
- `apiKey`
## SaaS Subscription and Billing
There are two billing layers in the schema:
1. legacy provider-oriented subscription records
2. richer billing-account and billing-invoice records for platform finance operations
### `Subscription`
One row per company.
Purpose:
- current plan
- billing period
- lifecycle state
- trial dates
- renewal windows
- suspension and cancellation timing
Relations:
- belongs to one `Company`
- has many `SubscriptionInvoice`
- has many `SubscriptionEvent`
- can be referenced by many `BillingInvoice`
- can be referenced by many `BillingEvent`
### `SubscriptionEvent`
Append-only event log for the subscription lifecycle.
Examples of what it represents:
- trial started
- checkout created
- payment received
- plan changed
- grace period extended
- subscription suspended
### `SubscriptionInvoice`
Legacy or provider-facing subscription payment records.
Purpose:
- capture AmanPay or PayPal payment references
- track amount, currency, due date, paid date, and failure/void state
- optionally bridge to the richer `BillingInvoice` model via `billingInvoiceId`
### `PaymentAttempt`
Legacy payment-attempt rows tied to `SubscriptionInvoice`.
This is narrower than the newer billing payment tables and exists for provider-level retry/failure tracking.
### `BillingAccount`
The primary finance entity for a company at the platform level.
Purpose:
- billing identity and address
- invoice terms
- default currency
- tax flags
- default payment method
- dunning pause state
- external provider customer ID
Relations:
- belongs to one `Company`
- has many `BillingPaymentMethod`
- has many `BillingInvoice`
- has many `BillingPaymentIntent`
- has many `BillingPaymentAttempt`
- has many `BillingCreditBalance`, `BillingCreditLedgerEntry`, `BillingCreditNote`, `BillingRefund`, `BillingEvent`
### `BillingPaymentMethod`
Stored billing method metadata for the company account.
Examples:
- card brand/last4/expiration
- ACH/bank/manual invoice style billing methods
One billing account can have many payment methods; one may be selected as default.
### `BillingInvoice`
The main platform invoice table.
Purpose:
- invoice numbering and sequencing
- totals and tax math
- status transitions
- billing snapshot fields such as billing name and email
- finance/admin metadata
Relations:
- belongs to one `BillingAccount`
- belongs to one `Company`
- optionally belongs to one `Subscription`
- optionally has a linked legacy `SubscriptionInvoice`
- has many `BillingInvoiceLineItem`
- has many `BillingPaymentIntent`
- has many `BillingPaymentAttempt`
- has many `BillingTaxRecord`
- has many `BillingCreditNote`
- has many `BillingRefund`
- has many `BillingEvent`
### Supporting billing tables
- `BillingInvoiceLineItem`: immutable invoice lines with type, quantity, unit amount, and optional service period.
- `BillingPaymentIntent`: payment-intent level state for a billing invoice.
- `BillingPaymentAttempt`: actual collection attempts against an invoice.
- `BillingCreditBalance`: running credit balance per billing account and currency.
- `BillingCreditLedgerEntry`: credit ledger movement history.
- `BillingCreditNote`: credit documents issued against an invoice.
- `BillingRefund`: refund records for billing invoices.
- `BillingTaxRecord`: tax breakdown rows for an invoice.
- `BillingEvent`: append-only finance/subscription/company event log.
## Branding and Company Configuration
### `BrandSettings`
One-to-one with `Company`.
Purpose:
- public display name, tagline, logo, favicon, hero image
- theme colors
- subdomain and custom domain
- public contact and social metadata
- default locale and currency
- company-owned payment provider settings for renter payments
- marketplace visibility and rating
- homepage/menu JSON configuration
Important uniqueness:
- `companyId`
- `subdomain`
- `customDomain`
### `ContractSettings`
One-to-one with `Company`.
Purpose:
- rental contract legal copy
- invoice/footer text
- fuel and late-fee policy
- tax settings
- numbering prefixes and running sequences
- additional-driver charging policy
This table is used when generating operational rental documents.
### `AccountingSettings`
One-to-one with `Company`.
Purpose:
- reporting period
- fiscal-year start month
- accounting contact
- automatic report settings
- preferred export format
### `PricingRule`
Company-owned pricing rule definitions applied to reservations.
Purpose:
- define surcharge or discount logic
- express the rule type, condition, and adjustment
These rules are copied into reservation snapshots through `Reservation.pricingRulesApplied` and `Reservation.pricingRulesTotal`.
### `InsurancePolicy`
Company-defined upsell or mandatory protection products.
Purpose:
- define policy name and type
- define flat, percent, or per-day charges
- mark required or optional coverage
These are also snapshotted into reservations.
## Staff, Fleet, and Commercial Catalog
### `Employee`
Company staff records.
Purpose:
- operational user identity
- role assignment
- preferred language
- password-reset support
- activation state
Implementation note:
- `clerkUserId` still exists as a required unique field in the schema, but Clerk is no longer an active auth dependency in this project.
- the field is currently populated with local/generated identifiers so existing schema constraints remain satisfied.
Relations:
- belongs to one `Company`
- has many `Notification`
- has many `NotificationPreference`
Important uniqueness:
- `clerkUserId`
### `Vehicle`
The core fleet entity.
Purpose:
- static vehicle information
- public listing content
- daily rate and status
- pickup/dropoff location rules
Relations:
- belongs to one `Company`
- has many `Reservation`
- has many `MaintenanceLog`
- has many `VehicleCalendarBlock`
- participates in many-to-many offers through `OfferVehicle`
Soft deletion is implemented operationally by setting:
- `status = OUT_OF_SERVICE`
- `isPublished = false`
### `MaintenanceLog`
One-to-many from `Vehicle`.
Purpose:
- maintenance type and description
- cost, mileage, performed date
- next due date or mileage
### `VehicleCalendarBlock`
One-to-many from `Vehicle`.
Purpose:
- manual or maintenance blocks on the reservation calendar
- date-range unavailability independent of reservations
### `Offer`
Company-defined promotional offers.
Purpose:
- percentage/fixed/free-day/special-rate discount logic
- validity window
- promo code
- public and featured visibility
- redemption caps
Relations:
- belongs to one `Company`
- has many `Reservation`
- many-to-many with `Vehicle` through `OfferVehicle`
### `OfferVehicle`
Join table between `Offer` and `Vehicle`.
Composite primary key:
- `(offerId, vehicleId)`
## Renter Identity, Company CRM, and Reservation Aggregate
The schema intentionally separates global renter identity from company-local customer records.
### `Renter`
Cross-company end-user identity.
Purpose:
- login identity
- app preferences and verification state
- push token
- reservation and review linkage across companies
Relations:
- has many `Reservation`
- has many `Review`
- has many `Notification`
- has many `NotificationPreference`
- has many saved companies through `RenterSavedCompany`
### `RenterSavedCompany`
Join table that stores renter bookmarks.
Composite primary key:
- `(renterId, companyId)`
### `Customer`
Company-owned CRM record for a person renting from that company.
Purpose:
- renter contact and profile data as understood by that company
- notes and flag state
- driver-license metadata
- approval and compliance state
- company-specific reservation history
Relations:
- belongs to one `Company`
- has many `Reservation`
- has many `Complaint`
Important uniqueness:
- `(companyId, email)`
### `Reservation`
This is the central transactional aggregate for rental operations.
Core relations:
- belongs to one `Company`
- belongs to one `Vehicle`
- belongs to one `Customer`
- optionally belongs to one `Renter`
- optionally belongs to one `Offer`
Core business fields:
- booking source
- date range
- pickup/return locations
- pricing fields: `dailyRate`, `discountAmount`, `totalDays`, `totalAmount`, `depositAmount`
- payment summary fields: `paymentStatus`, `paidAmount`
- lifecycle fields: `status`, `checkedInAt`, `checkedOutAt`
- cancellation fields
- contract and invoice numbering
- review token and review reminder timestamps
- extras and pricing-rule snapshots
The reservation is the parent of most rental workflow records.
### Reservation child tables
- `RentalPayment`: individual payment rows for the reservation.
- `ReservationInsurance`: reservation-time snapshots of selected insurance products.
- `AdditionalDriver`: reservation-time records for extra drivers and approval status.
- `DamageReport`: pickup/dropoff summary damage records with photos and signatures.
- `DamageInspection`: structured inspection record per reservation and inspection type.
- `DamagePoint`: individual annotated damage points tied to one inspection.
- `ReservationPhoto`: ad hoc pickup/dropoff photo attachments.
- `Review`: at most one review per reservation.
- `Complaint`: operational or post-rental dispute record, optionally linked to reservation, review, and customer.
This snapshot pattern is important. The reservation stores what was actually sold and reviewed at the time of booking, not just pointers to mutable company configuration.
## Reservation-Adjacent Tables in Detail
### `RentalPayment`
Operational payment rows for rentals, not SaaS billing.
Purpose:
- amount, currency, type, status
- AmanPay or PayPal provider references
- paid timestamp
Relations:
- belongs to one `Company`
- belongs to one `Reservation`
### `ReservationInsurance`
Snapshot bridge between `Reservation` and `InsurancePolicy`.
It copies:
- policy name
- policy type
- charge type
- charge value
- total charge
That prevents later edits to the base insurance policy from mutating historical reservation pricing.
### `AdditionalDriver`
Reservation child rows for additional drivers.
Purpose:
- identity and license details
- charge model
- approval requirement and approval outcome
- expiry flags
### `DamageReport`
One row per reservation and report type.
Composite uniqueness:
- `(reservationId, type)`
Stores:
- summary damages JSON
- photo URLs
- fuel level
- mileage
- inspection metadata
### `DamageInspection`
Structured pickup/dropoff inspection entity.
Composite uniqueness:
- `(reservationId, type)`
Stores:
- fuel and mileage
- general condition notes
- customer agreement state
- inspector identity
### `DamagePoint`
Granular diagram point rows tied to `DamageInspection`.
Stores:
- diagram view
- x/y coordinates
- damage type and severity
- free-text description
- whether the damage is pre-existing
### `ReservationPhoto`
Simple photo attachments tied to a reservation with type `PICKUP` or `DROPOFF`.
### `Review`
One review per reservation.
Important uniqueness:
- `reservationId`
Purpose:
- overall, vehicle, and service ratings
- comment
- moderation/publication flags
- company reply data
- optional feedback category
Relations:
- optionally linked to `Renter`
- can have many `Complaint`
### `Complaint`
Company-scoped issue-tracking table.
Can be linked to:
- a reservation
- a review
- a customer
This supports both operational complaints and post-review escalations.
## Notifications and Messaging
### `Notification`
A notification can target one of three audiences:
- company-level
- employee-level
- renter-level
Purpose:
- delivery type
- title/body/data payload
- channel
- locale
- send/read/failure state
- provider message ID
### `NotificationTemplate`
Reusable message content keyed by:
- template key
- channel
- locale
- version
### `NotificationPreference`
Per-recipient opt-in/out rows.
The uniqueness rules are intentionally split:
- unique per `(employeeId, notificationType, channel)`
- unique per `(renterId, notificationType, channel)`
## Admin and Platform Governance
### `AdminUser`
Platform operator account.
Purpose:
- role
- password hash
- 2FA state
- login tracking
- password reset support
Relations:
- has many `AdminPermission`
- has many `AuditLog`
### `AdminPermission`
Resource/action override rows for an admin user.
Important uniqueness:
- `(adminUserId, resource)`
### `AuditLog`
Immutable log of admin-side actions.
Stores:
- actor
- action and resource
- optional company and resource IDs
- before/after JSON
- IP, user agent, and note
## Pricing and Platform Content Configuration
### `PricingConfig`
Platform-managed plan pricing rows.
Important uniqueness:
- `(plan, billingPeriod)`
### `PlanFeature`
Platform-managed feature labels per plan, ordered by `sortOrder`.
### `PricingPromotion`
Platform-managed promotion codes for SaaS pricing.
Purpose:
- code and description
- discount type/value
- applicable plans/periods
- usage limits
- validity window
## Relationship Summary by Cardinality
### One-to-one
- `Company` -> `Subscription`
- `Company` -> `BrandSettings`
- `Company` -> `ContractSettings`
- `Company` -> `AccountingSettings`
### One-to-many
- `Company` -> `Employee`, `Vehicle`, `Offer`, `Customer`, `Reservation`, `RentalPayment`
- `Company` -> `BillingAccount`, `BillingInvoice`, `SubscriptionInvoice`, `InsurancePolicy`, `PricingRule`, `Notification`, `Complaint`
- `Vehicle` -> `Reservation`, `MaintenanceLog`, `VehicleCalendarBlock`
- `Reservation` -> `RentalPayment`, `ReservationInsurance`, `AdditionalDriver`, `DamageReport`, `DamageInspection`, `ReservationPhoto`, `Complaint`
- `BillingAccount` -> `BillingPaymentMethod`, `BillingInvoice`, `BillingPaymentIntent`, `BillingPaymentAttempt`, `BillingCreditBalance`, `BillingCreditLedgerEntry`, `BillingCreditNote`, `BillingRefund`, `BillingEvent`
### Many-to-many through join tables
- `Offer` <-> `Vehicle` through `OfferVehicle`
- `Renter` <-> `Company` through `RenterSavedCompany`
## Indexing and Uniqueness Rules Worth Remembering
Operationally important uniqueness constraints:
- `Company.slug`
- `Company.email`
- `Company.apiKey`
- `BrandSettings.subdomain`
- `BrandSettings.customDomain`
- `Employee.clerkUserId`
- `Customer(companyId, email)`
- `Reservation.contractNumber`
- `Reservation.invoiceNumber`
- `Reservation.reviewToken`
- `Review.reservationId`
- `OfferVehicle(offerId, vehicleId)`
- `RenterSavedCompany(renterId, companyId)`
- `ReservationInsurance(reservationId, insurancePolicyId)`
- `DamageReport(reservationId, type)`
- `DamageInspection(reservationId, type)`
- `PricingConfig(plan, billingPeriod)`
- `NotificationPreference` employee and renter uniqueness pairs
## Practical Reading Guide
If you need to understand the schema quickly, read it in this order:
1. `Company`
2. `Subscription`, `BillingAccount`, `BillingInvoice`
3. `Employee`, `Vehicle`, `Offer`
4. `Customer`, `Renter`
5. `Reservation` and its child tables
6. `Notification*`
7. `Admin*`, `AuditLog`, `Pricing*`
That order matches how the application is structured in the API: tenant first, operations second, finance/admin last.
Binary file not shown.
@@ -1,695 +0,0 @@
# 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.