The same car gets promised to two customers for the same weekend, and nobody notices until the second customer shows up for the keys. In car and scooter rental, this almost always happens because the schedule lives in a notebook, a group chat, or a spreadsheet several people edit at once.
This guide is for rental owners and developers evaluating vehicle rental management source code. It covers the modules you need, how a system should prevent double bookings, a sound table design, running a Laravel project locally, and a checklist to use before buying any codebase.
The Operational Problems
- Double bookings. Without a single source of truth, two staff members can accept the same vehicle for overlapping dates.
- Unclear vehicle status. Whether a unit is rented out, in the shop, or ready often depends on who happens to be in the yard.
- Scattered payments. Deposits, balances, late fees, and extras end up in different places.
- Slow reporting. The owner waits for a manual tally to learn which vehicles actually earn money.
Core Modules
A modern rental management system typically covers vehicle records, bookings, customer management, vehicle status (available or rented), transaction and financial reports, and a monitoring dashboard. The flow looks like this:
- Vehicles store plate, make, model, daily rate, and condition.
- Customers store identity and contact details, often with a scanned driver's license.
- Bookings have a start and end time. Before saving, the system must check for overlaps.
- Vehicle status changes at pickup and return.
- Payments are recorded per booking, including late fees.
- Reports and the dashboard show revenue, utilization per vehicle, and upcoming bookings.
The Key Technical Piece: Preventing Overlaps
Two date ranges overlap when the new start is before the existing end and the new end is after the existing start. This rule is surprisingly easy to get wrong, so read it in the actual code.
SELECT COUNT(*) FROM bookings
WHERE vehicle_id = ?
AND status IN ('reserved','active')
AND start_at < ? -- new booking end_at
AND end_at > ?; -- new booking start_at
In Laravel, wrap it in a transaction with a row lock so two simultaneous saves cannot both succeed:
use Illuminate\Support\Facades\DB;
DB::transaction(function () use ($data) {
$vehicle = Vehicle::whereKey($data['vehicle_id'])->lockForUpdate()->firstOrFail();
$clash = Booking::where('vehicle_id', $vehicle->id)
->whereIn('status', ['reserved', 'active'])
->where('start_at', '<', $data['end_at'])
->where('end_at', '>', $data['start_at'])
->exists();
abort_if($clash, 422, 'Vehicle is already booked for those dates.');
Booking::create($data);
});
Without lockForUpdate(), two requests arriving at nearly the same moment can both read "no clash" and both insert. Locking the vehicle row forces the second request to wait.
Recommended Table Design
| Table | Key columns | Notes |
|---|---|---|
| vehicles | plate_number, make, model, daily_rate, condition | Unique plate_number |
| customers | name, phone, license_number | Restrict access to ID data |
| bookings | vehicle_id, customer_id, start_at, end_at, status, total | Composite index on (vehicle_id, start_at, end_at) |
| payments | booking_id, amount, type, paid_at | Deposit, balance, late fee |
| vehicle_logs | vehicle_id, event, odometer, note | Pickup, return, maintenance |
Derive vehicle status from active bookings rather than relying only on a column someone must remember to update. For modeling basics, see designing a simple ERD.
Running It Locally
- Install PHP 8.2+, Composer, and MySQL.
- Run the standard setup:
composer install
cp .env.example .env
php artisan key:generate
php artisan migrate --seed
php artisan storage:link
php artisan serve
- Test the critical scenario: book vehicle A from the 10th to the 12th, then try booking it again from the 11th to the 13th. A good system rejects the second booking.
- Test the boundary: the 12th to the 14th right after the 10th to the 12th. Depending on your pickup rules, this should normally be allowed.
Common Customizations
- Weekend or seasonal pricing: add a rate rules table instead of overwriting the daily rate.
- Chauffeur packages: link drivers to bookings and reuse the same overlap check for driver availability.
- Online payments: integrate a gateway such as Stripe or PayPal; the general pattern is shown in integrating a payment gateway in Laravel.
- Automatic late fees: compute from the actual return time versus
end_at.
Common Mistakes and Fixes
- Storing dates without times. Rentals are often billed per 24 hours. Use datetime columns.
- Cancelled bookings still blocking the calendar. Only count active statuses in the overlap check.
- Inconsistent time zones. Set
APP_TIMEZONEto your business location. - License scans in a public folder. Store them on a private disk and serve them through an authorized route.
Reading the Dashboard: Utilization
The most useful number for a rental owner is not total revenue but utilization: the share of days in a month that a vehicle is actually rented. For example, if vehicle B is out for 18 days in a 30-day month, its utilization is 60 percent. A vehicle rented for only 6 days (20 percent) might be better sold or moved into a discount package. These figures are illustrative, but the reasoning holds even for a fleet of three.
If the bundled dashboard does not show it, compute utilization from the bookings table by summing completed booking durations per vehicle over a period and dividing by the number of days. You can schedule this report with Laravel's scheduler and email it at the start of every month.
Handover Checklist
Good software also supports the work in the yard. Make sure each pickup and return records the following in vehicle_logs:
- Odometer reading out and in.
- Fuel level out and in.
- Photos of the vehicle from all four sides.
- Any new damage and the agreed charge.
- The staff member who handed over and received the vehicle.
Buyer Checklist
- Overlap checks run on the server, not only in JavaScript.
- Vehicle status matches active bookings.
- Partial payments (deposit and balance) are supported.
- Financial reports filter by period and by vehicle.
- Laravel and PHP versions are clear and supported.
- A demo, install docs, and a clear license are provided.
- Customer identity data requires login to view.
Limitations
Ready-made code does not handle hosting, backups, or server security for you. GPS tracking, a customer mobile app, or digital contract signing are usually not included. Treat the code as a foundation that saves you from rebuilding the standard parts, then budget time to encode your own rental rules.
For a concrete example, GudangCode lists a modern vehicle rental management system; assess it with the checklist above.