Files
HRM-System/docs/PLAN-biometric-shift-detection.md

3.5 KiB

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:

  • 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:

  • Add a helper method to filter out raw punches occurring within 60 seconds of the previous valid punch:
    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:

  • 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.