fix(fusion_clock): NFC clock-out shows gross worked time, not net-of-penalty
The result card showed x_fclk_net_hours = worked_hours − break − early-out penalty minutes. Tapping out before the scheduled end adds a 15-min early-out penalty to the break field, so short shifts clamped to 0 → "Worked 0h 0m". Show GROSS attendance.worked_hours (the actual clock-in → clock-out elapsed time) instead, and format adaptively (Xh Ym / Ym / Ys) so brief shifts and quick tests don't all read 0. Net-of-deductions stays in the payroll reports. Live as 19.0.3.11.5 (verified worked_hours computes correctly in the DB). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
{
|
{
|
||||||
'name': 'Fusion Clock',
|
'name': 'Fusion Clock',
|
||||||
'version': '19.0.3.11.4',
|
'version': '19.0.3.11.5',
|
||||||
'category': 'Human Resources/Attendances',
|
'category': 'Human Resources/Attendances',
|
||||||
'summary': 'Complete Employee T&A with Geofencing, Shifts, Penalties, Overtime, Kiosk, Dashboard & Payroll Export',
|
'summary': 'Complete Employee T&A with Geofencing, Shifts, Penalties, Overtime, Kiosk, Dashboard & Payroll Export',
|
||||||
'description': """
|
'description': """
|
||||||
|
|||||||
@@ -367,7 +367,7 @@ class FusionClockNfcKiosk(http.Controller):
|
|||||||
'employee_name': employee.name,
|
'employee_name': employee.name,
|
||||||
'employee_avatar_url': avatar_url,
|
'employee_avatar_url': avatar_url,
|
||||||
'message': f'{employee.name} clocked in at {location.name}',
|
'message': f'{employee.name} clocked in at {location.name}',
|
||||||
'net_hours_today': 0.0,
|
'worked_hours': 0.0,
|
||||||
'needs_photo': not employee.image_1920,
|
'needs_photo': not employee.image_1920,
|
||||||
}
|
}
|
||||||
else:
|
else:
|
||||||
@@ -393,7 +393,10 @@ class FusionClockNfcKiosk(http.Controller):
|
|||||||
'employee_name': employee.name,
|
'employee_name': employee.name,
|
||||||
'employee_avatar_url': avatar_url,
|
'employee_avatar_url': avatar_url,
|
||||||
'message': f'{employee.name} clocked out',
|
'message': f'{employee.name} clocked out',
|
||||||
'net_hours_today': round(attendance.x_fclk_net_hours or 0, 2),
|
# GROSS time between clock-in and clock-out (what the employee
|
||||||
|
# expects to see). x_fclk_net_hours subtracts break + early-out
|
||||||
|
# penalty minutes, which zeroed short shifts — that's for payroll.
|
||||||
|
'worked_hours': attendance.worked_hours or 0.0,
|
||||||
'needs_photo': not employee.image_1920,
|
'needs_photo': not employee.image_1920,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -248,10 +248,17 @@
|
|||||||
const action = payload.action === "clock_in" ? "CLOCKED IN" : "CLOCKED OUT";
|
const action = payload.action === "clock_in" ? "CLOCKED IN" : "CLOCKED OUT";
|
||||||
let hoursLine = "";
|
let hoursLine = "";
|
||||||
if (payload.action === "clock_out") {
|
if (payload.action === "clock_out") {
|
||||||
const mins = Math.round((payload.net_hours_today || 0) * 60);
|
// Gross clock-in → clock-out time. Adaptive units so short shifts
|
||||||
const h = Math.floor(mins / 60);
|
// (and quick tests) don't all read "0h 0m".
|
||||||
const m = mins % 60;
|
const totalSec = Math.round((payload.worked_hours || 0) * 3600);
|
||||||
hoursLine = `<div class="hours">Worked ${h}h ${m}m this shift</div>`;
|
const h = Math.floor(totalSec / 3600);
|
||||||
|
const m = Math.floor((totalSec % 3600) / 60);
|
||||||
|
const s = totalSec % 60;
|
||||||
|
let dur;
|
||||||
|
if (h > 0) dur = `${h}h ${m}m`;
|
||||||
|
else if (m > 0) dur = `${m}m`;
|
||||||
|
else dur = `${s}s`;
|
||||||
|
hoursLine = `<div class="hours">Worked ${dur} this shift</div>`;
|
||||||
}
|
}
|
||||||
const time = new Date().toLocaleTimeString([], { hour: "2-digit", minute: "2-digit", hour12: true });
|
const time = new Date().toLocaleTimeString([], { hour: "2-digit", minute: "2-digit", hour12: true });
|
||||||
stateContainer.innerHTML = `
|
stateContainer.innerHTML = `
|
||||||
|
|||||||
Reference in New Issue
Block a user