Attendance: detect rest day in enrich → render 休息 instead of 缺勤
When roster says the shift rests on a given weekday (e.g. Sunday for S2/S3), the employee wasn't scheduled to work. Import still marks the record status_code='missing' because no shift times were available, but the right user-facing label is '休息' not '缺勤'. Enrich now: if status_code='missing' and get_shift_schedule_full returns None (shift defined no schedule for that weekday), override to status_code='rest' / status_text='休息'. Added '休息' to the basic-status vocabulary: dropdown option, filter condition, StatusBadge (slate grey + coffee icon), and row highlight.
This commit is contained in:
+13
-1
@@ -126,6 +126,16 @@ def enrich_attendance_with_leave(records, db):
|
|||||||
if not r.date:
|
if not r.date:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
# Rest day detection: roster says the shift rests on this weekday
|
||||||
|
# (e.g. Sunday for S2/S3) but raw status is 'missing'. Don't flag as 缺勤
|
||||||
|
# when the employee wasn't scheduled to work.
|
||||||
|
if (r.status_code or '').lower() == 'missing' and r.shift_code and r.weekday:
|
||||||
|
shift_info = get_shift_schedule_full(r.shift_code, r.weekday)
|
||||||
|
if shift_info is None:
|
||||||
|
r.status_code = 'rest'
|
||||||
|
r.status_text = '休息'
|
||||||
|
continue
|
||||||
|
|
||||||
if r.date in holidays_by_date:
|
if r.date in holidays_by_date:
|
||||||
h = holidays_by_date[r.date]
|
h = holidays_by_date[r.date]
|
||||||
# Check roster: if shift's public_holiday == 'Yes', employee works on holidays
|
# Check roster: if shift's public_holiday == 'Yes', employee works on holidays
|
||||||
@@ -1106,7 +1116,7 @@ async def attendance_status_texts(
|
|||||||
raw status_text variant (e.g. '異常-遲到39分'). Keeping it server-driven
|
raw status_text variant (e.g. '異常-遲到39分'). Keeping it server-driven
|
||||||
so client and server agree on the filter vocabulary.
|
so client and server agree on the filter vocabulary.
|
||||||
"""
|
"""
|
||||||
return ['正常', '異常', '缺勤', '公假', '請假']
|
return ['正常', '異常', '缺勤', '公假', '請假', '休息']
|
||||||
|
|
||||||
@app.get("/api/attendance/stats")
|
@app.get("/api/attendance/stats")
|
||||||
async def attendance_stats(
|
async def attendance_stats(
|
||||||
@@ -1335,6 +1345,8 @@ async def attendance_records(
|
|||||||
for tag in ('SL', 'AL', 'CL'))]
|
for tag in ('SL', 'AL', 'CL'))]
|
||||||
elif status_text in ('normal', '正常'):
|
elif status_text in ('normal', '正常'):
|
||||||
rows = [r for r in all_rows if (r.status_code or '').lower() == 'normal']
|
rows = [r for r in all_rows if (r.status_code or '').lower() == 'normal']
|
||||||
|
elif status_text in ('rest', '休息'):
|
||||||
|
rows = [r for r in all_rows if (r.status_code or '').lower() == 'rest']
|
||||||
else:
|
else:
|
||||||
# Fallback to raw status_text substring search
|
# Fallback to raw status_text substring search
|
||||||
rows = [r for r in all_rows
|
rows = [r for r in all_rows
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { CheckCircle, AlertCircle, CalendarX, Palmtree, Briefcase } from 'lucide-react'
|
import { CheckCircle, AlertCircle, CalendarX, Palmtree, Briefcase, Coffee } from 'lucide-react'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reusable status badge for attendance records.
|
* Reusable status badge for attendance records.
|
||||||
@@ -74,6 +74,11 @@ export default function StatusBadge({ code = '', text = '', size = 'sm' }) {
|
|||||||
return wrap(badge(<CalendarX size={12} />, '缺勤', 'bg-gray-200 text-gray-700'))
|
return wrap(badge(<CalendarX size={12} />, '缺勤', 'bg-gray-200 text-gray-700'))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 休息 (rest day — roster says not scheduled, employee didn't clock in)
|
||||||
|
if (c === 'rest' || t === '休息') {
|
||||||
|
return wrap(badge(<Coffee size={12} />, '休息', 'bg-slate-100 text-slate-600'))
|
||||||
|
}
|
||||||
|
|
||||||
// 公假 (holiday)
|
// 公假 (holiday)
|
||||||
if (c === 'holiday' || t.includes('公假') || t.includes('🏖️')) {
|
if (c === 'holiday' || t.includes('公假') || t.includes('🏖️')) {
|
||||||
return wrap(badge(<Palmtree size={12} />, '公假', 'bg-indigo-100 text-indigo-700'))
|
return wrap(badge(<Palmtree size={12} />, '公假', 'bg-indigo-100 text-indigo-700'))
|
||||||
|
|||||||
@@ -99,6 +99,7 @@ export default function AttendanceList() {
|
|||||||
return 'bg-orange-50 hover:bg-orange-100'
|
return 'bg-orange-50 hover:bg-orange-100'
|
||||||
}
|
}
|
||||||
if (c === 'missing') return 'bg-gray-100 hover:bg-gray-200'
|
if (c === 'missing') return 'bg-gray-100 hover:bg-gray-200'
|
||||||
|
if (c === 'rest') return 'bg-slate-50 hover:bg-slate-100'
|
||||||
if (c === 'holiday') return 'bg-indigo-50 hover:bg-indigo-100'
|
if (c === 'holiday') return 'bg-indigo-50 hover:bg-indigo-100'
|
||||||
if (['al', 'sl', 'cl', 'mixed_leave'].includes(c)) return 'bg-violet-50 hover:bg-violet-100'
|
if (['al', 'sl', 'cl', 'mixed_leave'].includes(c)) return 'bg-violet-50 hover:bg-violet-100'
|
||||||
return 'hover:bg-slate-50'
|
return 'hover:bg-slate-50'
|
||||||
|
|||||||
Reference in New Issue
Block a user