fix: refactor ZKTeco API receiver to group by day and use correct AttendanceRecord table, and limit agent to last 15 days
This commit is contained in:
@@ -46,60 +46,84 @@ class AttendanceSyncController extends Controller
|
||||
return response()->json(['error' => 'Branch not found'], 404);
|
||||
}
|
||||
|
||||
// 3. Process the logs (same logic as BiometricAttendanceController)
|
||||
// 3. Process the logs (match BiometricAttendanceController logic)
|
||||
// Ensure we handle arrays with 'recordTime' or 'timestamp'
|
||||
$formattedAttendances = [];
|
||||
foreach ($attendances as $record) {
|
||||
$empCode = $record['deviceUserId'] ?? $record['id'] ?? null;
|
||||
$punchTime = $record['recordTime'] ?? $record['timestamp'] ?? null;
|
||||
$uid = $record['userSn'] ?? $record['uid'] ?? null;
|
||||
|
||||
if ($empCode && $punchTime) {
|
||||
$formattedAttendances[] = [
|
||||
'id' => $uid,
|
||||
'emp_code' => (string) $empCode,
|
||||
'punch_time' => $punchTime,
|
||||
'branch_id' => $branch->id
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
$groupedAttendances = collect($formattedAttendances)->groupBy(function ($item) {
|
||||
return $item['emp_code'] . '_' . date('Y-m-d', strtotime($item['punch_time']));
|
||||
});
|
||||
|
||||
$processed = 0;
|
||||
$inserted = 0;
|
||||
|
||||
foreach ($attendances as $record) {
|
||||
foreach ($groupedAttendances as $key => $dayEntries) {
|
||||
$processed++;
|
||||
// Typical payload from zklib-js: { userSn: 1, deviceUserId: '1', recordTime: '2023-10-14 08:30:00' }
|
||||
$empCode = $record['deviceUserId'] ?? $record['id'] ?? null;
|
||||
$punchTime = $record['recordTime'] ?? $record['timestamp'] ?? null;
|
||||
|
||||
if (!$empCode || !$punchTime) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Find employee by emp_code inside this branch's company
|
||||
$employee = \App\Models\Employee::where('emp_code', $empCode)
|
||||
$sorted = $dayEntries->sortBy('punch_time');
|
||||
$firstEntry = $sorted->first();
|
||||
$lastEntry = $sorted->last();
|
||||
|
||||
// Note: Since this is an API, there is no Auth::user(). We use the Branch's creator.
|
||||
// BiometricAttendanceController uses whereIn('created_by', getCompanyAndUsersId())
|
||||
// We just use where('created_by', $branch->created_by) or similar.
|
||||
|
||||
$employee = \App\Models\Employee::with('user')
|
||||
->where('created_by', $branch->created_by)
|
||||
->where('biometric_emp_id', $firstEntry['emp_code'])
|
||||
->first();
|
||||
|
||||
if (!$employee) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Parse date and time
|
||||
$ts = strtotime($punchTime);
|
||||
$date = date('Y-m-d', $ts);
|
||||
$time = date('H:i:s', $ts);
|
||||
|
||||
// Check if record already exists to prevent duplicates
|
||||
$exists = AttendanceEmployee::where('employee_id', $employee->id)
|
||||
->where('date', $date)
|
||||
->where('clock_in', $time)
|
||||
->exists();
|
||||
|
||||
if (!$exists) {
|
||||
AttendanceEmployee::create([
|
||||
'employee_id' => $employee->id,
|
||||
'date' => $date,
|
||||
'status' => 'Present',
|
||||
'clock_in' => $time,
|
||||
'clock_out' => '00:00:00',
|
||||
'late' => '00:00:00',
|
||||
'early_leaving' => '00:00:00',
|
||||
'overtime' => '00:00:00',
|
||||
'total_rest' => '00:00:00',
|
||||
'created_by' => $branch->created_by,
|
||||
]);
|
||||
$inserted++;
|
||||
|
||||
if ($employee && $sorted->count() > 1) {
|
||||
$attedanceDate = date('Y-m-d', strtotime($firstEntry['punch_time']));
|
||||
$clockInTime = date('H:i:s', strtotime($firstEntry['punch_time']));
|
||||
$clockOutTime = date('H:i:s', strtotime($lastEntry['punch_time']));
|
||||
|
||||
$exists = \App\Models\AttendanceRecord::where('employee_id', $employee->user_id)
|
||||
->where('date', $attedanceDate)
|
||||
->exists();
|
||||
|
||||
if (!$exists) {
|
||||
$shift = \App\Models\Shift::where('id', $employee->shift_id)->where('status', 'active')->first()
|
||||
?? \App\Models\Shift::where('created_by', $branch->created_by)->where('status', 'active')->first();
|
||||
|
||||
$policy = \App\Models\AttendancePolicy::where('id', $employee->attendance_policy_id)->where('status', 'active')->first()
|
||||
?? \App\Models\AttendancePolicy::where('created_by', $branch->created_by)->where('status', 'active')->first();
|
||||
|
||||
$attendance = new \App\Models\AttendanceRecord();
|
||||
$attendance->employee_id = $employee->user_id;
|
||||
$attendance->biometric_id = $firstEntry['id'];
|
||||
$attendance->shift_id = $shift?->id;
|
||||
$attendance->attendance_policy_id = $policy?->id;
|
||||
$attendance->date = $attedanceDate;
|
||||
$attendance->clock_in = $clockInTime;
|
||||
$attendance->clock_out = $clockOutTime;
|
||||
$attendance->created_by = $branch->created_by;
|
||||
$attendance->save();
|
||||
|
||||
$attendance->fresh();
|
||||
$attendance->processAttendance();
|
||||
|
||||
$inserted++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
'message' => "Processed {$processed} records, Inserted {$inserted} new attendances.",
|
||||
'message' => "Processed {$processed} unique daily records, Inserted {$inserted} new attendances.",
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user