# PLAN: Shift-Aware Biometric Routing and Double-Punch Protection This plan documents the integration of shift-aware punch classification (In/Out routing for single punches) and the prevention of duplicate check-ins (double-punch filtering). --- ## 🏗️ Target Files 1. **Backend Controller:** `app/Http/Controllers/BiometricAttendanceController.php` 2. **Frontend UI Page:** `resources/js/pages/hr/biometric-attendance/index.tsx` --- ## 🛠️ Proposed Implementation Steps ### Phase 1: Main Dashboard UI Updates (Frontend) Modify [index.tsx](file:///Users/dvapp/Documents/HRM/resources/js/pages/hr/biometric-attendance/index.tsx): - Remove the `Total Entries` column from the `columns` array. - Ensure the table renders clean and responsive rows without the total entries column. ### Phase 2: Double-Punch Filtering Logic (Backend) Modify [BiometricAttendanceController.php](file:///Users/dvapp/Documents/HRM/app/Http/Controllers/BiometricAttendanceController.php): - Add a helper method to filter out raw punches occurring within 60 seconds of the previous valid punch: ```php private static function filterDoublePunches($dayEntries) { $sorted = $dayEntries->sortBy('punch_time'); $filtered = collect(); foreach ($sorted as $entry) { if ($filtered->isEmpty()) { $filtered->push($entry); } else { $lastValid = $filtered->last(); $diffInSeconds = $entry->punch_time->diffInSeconds($lastValid->punch_time); if ($diffInSeconds > 60) { $filtered->push($entry); } } } return $filtered; } ``` ### Phase 3: Shift-Aware Single Punch Routing (Backend) Modify [BiometricAttendanceController.php](file:///Users/dvapp/Documents/HRM/app/Http/Controllers/BiometricAttendanceController.php): - Refactor the grouping map in `index` and the processing loop in `syncAll` to: 1. Filter out double-punches first using `filterDoublePunches`. 2. If the remaining entries count is exactly **1**: - Check if the employee has a shift. - If so, calculate the expected shift `start_time` and `end_time` (accounting for night shifts) on that work date. - Calculate the distance from the punch to the shift start and shift end. - If the punch is closer to the shift **end time**, set `clock_out` as the punch time, and `clock_in` as `null` (or `-`). - Otherwise, set `clock_in` as the punch time, and `clock_out` as `null`. - If no shift is found, default to treating the single punch as `clock_in`. 3. If the remaining entries count is **2 or more**: - Treat the earliest punch as `clock_in` and the latest as `clock_out`. --- ## 🏁 Verification Checklist - [ ] **Verify Column Removal:** Open the main biometric attendance page and check that the "Total Entries" column is removed. - [ ] **Verify Double-Punch Filtering:** Push mock data containing two punches for the same employee 30 seconds apart (e.g. `08:30:00` and `08:30:30`). Verify that the dashboard groups this as a single punch at `08:30:00` and treats it as a Clock In. - [ ] **Verify Shift-Aware Routing (Clock In):** Push a single punch at `08:45:00` for an employee with a `09:00 - 18:00` shift. Verify that the system assigns it as **Clock In**. - [ ] **Verify Shift-Aware Routing (Clock Out):** Push a single punch at `17:45:00` for the same employee. Verify that the system assigns it as **Clock Out** and leaves Clock In blank. - [ ] **Vite compilation:** Run `npm run build` to verify React compilation. - [ ] **PHP syntax check:** Run `php -l` on the controller.