Every month, HR repeats the same routine: copy fingerprint machine data into a spreadsheet, match it against leave forms, count late arrivals by hand, and send the summary to payroll. Field staff and remote branches send photos of paper sheets instead. The process is slow, error-prone, and easy to game.
A web-based attendance app moves clock-in to a phone or browser and keeps everything in one database. This guide covers the modules you typically need, the data flow, a healthy table design, how to validate location in Laravel, installation, and what to check before adopting ready-made attendance source code.
Problems It Solves
- Buddy punching. A coworker clocks in for someone who has not arrived.
- Distributed teams. Field staff and branches cannot share one office terminal.
- Manual consolidation. Attendance, leave, and overtime live in different places.
- Late payroll. Salaries wait for the attendance summary.
Core Modules and Data Flow
A mature web attendance system usually includes geolocation and selfie check-in, shift scheduling, online leave requests with approval, daily and monthly attendance reports including lateness, and payroll integration.
- HR defines office locations (coordinates and radius) and shifts.
- An employee opens the app, the browser asks for location permission, and the employee takes a selfie.
- The server checks distance from the office and compares the time with the shift.
- Check-in and check-out are stored with coordinates, photo, and status.
- Leave requests go to a manager for approval.
- At period end, attendance feeds into payroll.
Recommended Table Design
| Table | Key columns | Notes |
|---|---|---|
| employees | id, user_id, employee_no, name, office_id, shift_id | One login per employee |
| offices | id, name, latitude, longitude, radius_m | Supports multiple sites |
| shifts | id, name, start_time, end_time, grace_minutes | Grace period per shift |
| attendances | id, employee_id, date, check_in, check_out, lat, lng, photo_path, status | Unique per employee per date |
| leave_requests | id, employee_id, type, start_date, end_date, reason, status, approved_by | pending, approved, rejected |
Store photos on a private disk, not in public, and serve them through a route that checks permissions. Faces and locations are personal data. For relationship design basics, see designing a simple ERD.
Validate Location on the Server
The most common mistake is checking the radius only in JavaScript. Anyone can alter what the browser sends, so repeat the calculation server-side. A Haversine example for Laravel 11:
// app/Support/Geo.php
namespace App\Support;
class Geo
{
public static function distanceMeters(float $lat1, float $lng1, float $lat2, float $lng2): float
{
$r = 6371000; // earth radius in meters
$dLat = deg2rad($lat2 - $lat1);
$dLng = deg2rad($lng2 - $lng1);
$a = sin($dLat / 2) ** 2
+ cos(deg2rad($lat1)) * cos(deg2rad($lat2)) * sin($dLng / 2) ** 2;
return $r * 2 * atan2(sqrt($a), sqrt(1 - $a));
}
}
// in the check-in controller
$data = $request->validate([
'lat' => ['required', 'numeric', 'between:-90,90'],
'lng' => ['required', 'numeric', 'between:-180,180'],
'photo' => ['required', 'image', 'max:2048'],
]);
$office = $employee->office;
$distance = Geo::distanceMeters($data['lat'], $data['lng'], $office->latitude, $office->longitude);
if ($distance > $office->radius_m) {
return back()->withErrors(['lat' => 'You are about ' . round($distance) . ' m from the office.']);
}
Indoor phone GPS can drift by tens of meters. A radius of 50–150 meters is usually more practical than 10 meters, which rejects people standing in the lobby. Test on site before settling on a value. Browsers also only expose location on HTTPS pages, so check your certificate first if location fails on a test server.
Anti-Fraud Options
| Method | Stops | Weakness |
|---|---|---|
| Geolocation + radius | Clocking in from home | GPS spoofing apps |
| Selfie | Buddy punching | Needs manual or random review |
| Office network (IP) restriction | Off-site check-ins | Unsuitable for field teams |
| One device per account | Account sharing | Friction when phones change |
Geolocation plus selfie is enough for most small and mid-sized businesses. Automatic face recognition adds rigor but also cost and complexity.
Lateness and Monthly Summaries
Calculate lateness at check-in and store it, rather than recomputing on every report:
$limit = Carbon::parse($shift->start_time)->addMinutes($shift->grace_minutes);
$status = now()->format('H:i:s') > $limit->format('H:i:s') ? 'late' : 'on_time';
For overnight shifts, store the shift date explicitly in the date column instead of relying on the check-in timestamp. This is the most common bug in attendance systems.
Connecting Attendance to Payroll
The biggest payoff comes when attendance feeds salary calculations directly. A few rules keep that integration clean:
- Lock the period. Once HR approves a month, mark it locked so records cannot change silently after payday.
- Provide a per-employee summary: days present, late, on leave, and absent. Payroll reads this summary, not thousands of raw rows.
- Log corrections. When an admin edits a record, store who changed it and why.
For example, if a company deducts a meal allowance for each late day, the summary turns that into a single multiplication, and every figure traces back to a dated record.
Local Installation
- Install PHP 8.2+, Composer, and MySQL.
- Extract the code and run
composer install. - Copy
.env.exampleto.env, set the database, then runphp artisan key:generate. - Run migrations and seeders as documented.
- Run
php artisan storage:linkif photos use the public disk. - To test on a phone, use an HTTPS tunnel because geolocation requires a secure context.
When you deploy, follow deploying a Laravel app to shared hosting.
Customizing Safely
- Add columns such as department through new migrations.
- Move business rules like grace periods into a settings table so HR can change them.
- Schedule archiving of old photos with the Laravel scheduler.
- Use Git and a staging server. More in customizing source code without breaking it.
Evaluation Checklist
- Radius is validated on the server, not only in the browser.
- Selfies are stored privately and visible only to authorized users.
- Multiple offices with different radii are supported.
- Shifts are configurable, including overnight ones.
- Leave requests have an approval flow and history.
- Reports filter by employee, department, and period.
- Attendance can be exported or passed to payroll.
- Install docs state PHP and Laravel versions.
Limitations
Web attendance depends on employees' phones and connectivity, so define a fallback, such as an admin-recorded entry with a note, for broken phones or dead zones. GPS spoofing exists; geolocation reduces fraud but does not eliminate it. Finally, tell employees openly what is collected (location at check-in and a photo) and why. Transparency matters for acceptance and, in many countries, for privacy law compliance.
The GudangCode catalog also includes Laravel HR and attendance apps if you want to compare their structure against this checklist.