Payroll / Timesheet — Khái niệm cốt lõi
| Khái niệm | Nghĩa |
|---|---|
| Payroll settings | Cấu hình theo BU trong Finance Settings; lưu cơ sở giờ trả công ở BusinessUnitSetting.DefaultPaidHoursOptionId. |
| Paid hours basis | Cơ sở giờ tính công: Rostered Hours (giờ xếp ca, value 0) hoặc Actual Hours (giờ thực tế chấm công, value 1). |
| OrgEmployee | Link staff–site active; Shift.OrgEmployeeId tham chiếu tới link này. |
| Shift | Ca làm việc; là nguồn của shift row trên Timesheet, mang TimesheetStatusId và Exported. |
| EmployeeAbsence | Nghỉ phép; một Date + TimeStart/TimeEnd; là nguồn của leave-only row khi approved và không trùng ca. |
| Shift row | Dòng timesheet từ một Shift, có đầy đủ cột giờ ca. |
| Leave-only row | Dòng timesheet từ một approved EmployeeAbsence vào ngày không có ca; mang trạng thái timesheet riêng. |
| Timesheet state | Cặp trạng thái TimesheetStatusId + cờ Exported, tồn tại trên cả Shift lẫn EmployeeAbsence với cùng shape. |
| TrackingCode | Mã phân loại (TypeId = Leave / Shift); nguồn của cột Tracking Code trong export. |
| Leave overlap hours | Giờ giao nhau giữa interval leave và interval ca; giá trị của cột Leave Hours trong export. |
| ReportCode | Tên template jsReport cố định cho Timesheet CSV export. |
| jsReport Reporting Server | Server render báo cáo; nguồn duy nhất dựng file CSV export. |
Payroll settings — cơ sở giờ trả công theo BU
- Lưu trên
BusinessUnitSettingquaDefaultPaidHoursOptionId; chỉ ghi được qua route/api/BusinessUnitSetting/Payrollcó quyềnSettingFinance. - Map label ↔ giá trị backend cố định:
Rostered Hours=0,Actual Hours=1. - BU chưa có row
BusinessUnitSetting→ load không lỗi, chọn mặc địnhRostered Hours(0). - Giá trị khác
0/1khi save → bị reject hoặc normalize theo validation contract. - Tab chỉ hiện khi BU bật
Staff Roster(quyềnSTAFF_ROSTER); route bảo vệ cùng quy tắc.
Ví dụ thực tế: Admin chọn Actual Hours rồi Save. → Lưu value 1, không phải label. → DefaultPaidHoursOptionId = 1 ghi vào BusinessUnitSetting; load lại vẫn hiện Actual Hours.
OrgEmployee link — tự sửa khi tạo ca
- Tạo ca mới (clock-in) hoặc ca kế tiếp (clock-out) → đảm bảo tồn tại
OrgEmployeeactive choEmployeeId+OrgIdcủa ca. - Thiếu → backend tự tạo và gán
Shift.OrgEmployeeId; đã có → tái dùng, không tạo trùng. - Employee/org không active trong BU → reject toàn bộ, không tạo
OrgEmployee, không tạo ca. - Frontend Staff Attendance không cần tự tạo
OrgEmployee; signature, tracking code, clock event, timesheet status và giờ ca giữ nguyên.
Ví dụ thực tế: Clock-out tạo ca kế tiếp, ca nguồn có EmployeeId/OrgId nhưng OrgEmployeeId không active. → Không để ca lưu với link hỏng. → Backend resolve/tạo OrgEmployee active gán vào ca mới.
Timesheet state — trạng thái trên từng dòng
ShiftvàEmployeeAbsencecùng mang shape trạng thái:TimesheetStatusId+Exported, cả hai optional.- Absence được tạo trước capability này → hành xử như dòng draft, chưa exported.
- Trạng thái timesheet độc lập với lifecycle duyệt của leave: approve/un-approve dòng không đổi status/approver/time approve của leave, và ngược lại.
- Leave lifecycle chưa approved (pending/rejected) → không xuất hiện thành dòng timesheet dù timesheet state thế nào.
Ví dụ thực tế: Admin approve dòng leave-only trên Timesheet. → Không kéo theo duyệt leave. → EmployeeAbsence.TimesheetStatusId đổi sang approved; status/approver/time approve của leave giữ nguyên.
Leave-only row — dòng nghỉ không ca
- Chỉ sinh khi approved
EmployeeAbsencerơi vào ngày không có ca rostered; leave trùng ca → không có row riêng, nằm trong cột leave của shift row. - Row key ổn định, suy tất định từ source absence id; không đụng shift row key; lặp lại ở mọi lần load cùng date range.
- Server resolve bản ghi đích từ row type + absence id field, không đoán type từ row key.
- Lọc theo status tab như shift row; absence không có timesheet status = draft default.
- Action: approve/un-approve (không ghi paid-time/duration), export/un-export; đã exported → khóa chọn.
Ví dụ thực tế: Load lại cùng leave-only row với date range hẹp hơn. → Row key phải giống lần trước để selection không lệch. → Key suy từ absence id cố định, không phụ thuộc thứ tự/độ dài kết quả.
Export mode — separated vs grouped
separated(mặc định): một CSV record cho mỗi dòng timesheet được chọn; giữ nguyên cột theo thứ tự export cũ.grouped: một CSV record cho mỗi staff (theo identity, không theo tên), gộp tổng giờ các dòng đã chọn; bỏ cột mức ca.- Cả hai mode cùng render từ dataset re-query giống nhau → báo cùng số giờ cho cùng selection.
- Yêu cầu không có mode → xử lý như
separated.
Ví dụ thực tế: Cùng 3 ca của 1 staff: To Pay Hours = 4.00, 3.50, 2.00. → Hai file phải khớp. → grouped báo 9.50 = tổng 3 giá trị trong separated.