From 2c8ad83d43f1ea5e4346b90744ca7ac433dd7284 Mon Sep 17 00:00:00 2001 From: gsinghpal Date: Sat, 30 May 2026 18:59:10 -0400 Subject: [PATCH] fix(fusion_clock): NFC clock-out shows gross worked time, not net-of-penalty MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- fusion_clock/__manifest__.py | 2 +- fusion_clock/controllers/clock_nfc_kiosk.py | 7 +++++-- .../static/src/js/fusion_clock_nfc_kiosk.js | 15 +++++++++++---- 3 files changed, 17 insertions(+), 7 deletions(-) diff --git a/fusion_clock/__manifest__.py b/fusion_clock/__manifest__.py index 793a1702..6516d91e 100644 --- a/fusion_clock/__manifest__.py +++ b/fusion_clock/__manifest__.py @@ -5,7 +5,7 @@ { 'name': 'Fusion Clock', - 'version': '19.0.3.11.4', + 'version': '19.0.3.11.5', 'category': 'Human Resources/Attendances', 'summary': 'Complete Employee T&A with Geofencing, Shifts, Penalties, Overtime, Kiosk, Dashboard & Payroll Export', 'description': """ diff --git a/fusion_clock/controllers/clock_nfc_kiosk.py b/fusion_clock/controllers/clock_nfc_kiosk.py index d9c44b3e..86731fad 100644 --- a/fusion_clock/controllers/clock_nfc_kiosk.py +++ b/fusion_clock/controllers/clock_nfc_kiosk.py @@ -367,7 +367,7 @@ class FusionClockNfcKiosk(http.Controller): 'employee_name': employee.name, 'employee_avatar_url': avatar_url, 'message': f'{employee.name} clocked in at {location.name}', - 'net_hours_today': 0.0, + 'worked_hours': 0.0, 'needs_photo': not employee.image_1920, } else: @@ -393,7 +393,10 @@ class FusionClockNfcKiosk(http.Controller): 'employee_name': employee.name, 'employee_avatar_url': avatar_url, '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, } diff --git a/fusion_clock/static/src/js/fusion_clock_nfc_kiosk.js b/fusion_clock/static/src/js/fusion_clock_nfc_kiosk.js index f016467b..1ce496ed 100644 --- a/fusion_clock/static/src/js/fusion_clock_nfc_kiosk.js +++ b/fusion_clock/static/src/js/fusion_clock_nfc_kiosk.js @@ -248,10 +248,17 @@ const action = payload.action === "clock_in" ? "CLOCKED IN" : "CLOCKED OUT"; let hoursLine = ""; if (payload.action === "clock_out") { - const mins = Math.round((payload.net_hours_today || 0) * 60); - const h = Math.floor(mins / 60); - const m = mins % 60; - hoursLine = `
Worked ${h}h ${m}m this shift
`; + // Gross clock-in → clock-out time. Adaptive units so short shifts + // (and quick tests) don't all read "0h 0m". + const totalSec = Math.round((payload.worked_hours || 0) * 3600); + 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 = `
Worked ${dur} this shift
`; } const time = new Date().toLocaleTimeString([], { hour: "2-digit", minute: "2-digit", hour12: true }); stateContainer.innerHTML = `