Payroll / Timesheet — Kiến trúc & dữ liệu
Timesheet export — server-side CSV qua jsReport
- Browser chỉ gửi danh sách id + mode; không chứa column definition, định dạng giá trị hay logic leave-overlap.
- Payload tách bạch id dòng ca và id dòng leave-only — hệ thống không suy loại record từ giá trị id.
- Dataset backend tự dựng, pre-format toàn bộ (date, time range, hour total 2 số thập phân) trước khi render.
- Có một template jsReport với
namekhớpReportCodecố định cho Timesheet CSV export.
Ví dụ thực tế: Export dòng ca rostered 09:00–17:00. → Browser không định dạng. → Cell Rostered Time = 09:00–17:00, Rostered Hours = 8.00 do backend dựng sẵn.
Scope & quyền khi export
- BU/org scope của caller resolve từ server-side context, không từ payload.
- Id — dòng ca hay leave-only — nằm ngoài scope của caller → reject toàn bộ request, không thu hẹp kết quả.
- Thiếu Timesheet permission (quyền xem Approved tab) → reject, không đánh dấu dòng exported.
- Nội dung export bằng dữ liệu lưu trữ, không phải state màn hình của caller.
Ví dụ thực tế: Caller scope không phủ org B nhưng request có dòng thuộc org B. → Không trả file thiếu dòng. → Cả request reject, không dòng nào được đánh dấu exported.
Đánh dấu exported
- Mark
exportedxảy ra trong cùng request export, chỉ sau khi render thành công. - Dòng ca → đánh trên
Shift.Exported; dòng leave-only → đánh trênEmployeeAbsence.Exported. - Render fail (ví dụ Reporting Server lỗi) → request fail, không dòng nào được đánh dấu.
- Un-export giữ nguyên, xóa cờ
exportedcho cả hai loại dòng.
Ví dụ thực tế: Selection 1 dòng ca + 1 dòng leave-only, render thành công. → Cả hai record đánh dấu cùng lúc. → Shift.Exported = true và EmployeeAbsence.Exported = true trong một round-trip.
Column contract — separated mode
Thứ tự cột trong mode separated (header row luôn có):
| # | Cột |
|---|---|
| 1–13 | Date, Name, Org, Tracking Code (ca), Note, Rostered Time, Rostered Hours, Clocked Time, Clocked Hours, Actual Time, Actual Hours, To Pay Time, To Pay Hours |
| 14–15 | Leave Hours, rồi ngay Tracking Code (leave) |
- Cột leave đúng 2 cột
Leave Hours→Tracking Code; không cóLeave Tracking Code. - Value escape theo RFC 4180 (comma, quote, newline) để không hỏng row.
- Dòng leave-only dùng đúng bộ cột này:
Date/Nametừ absence,Orgtrống, cột giờ ca trống,Leave Hours= giờ của absence,Tracking Code= code của leave. - File chỉ chứa các dòng được chọn, thứ tự theo grid.
Ví dụ thực tế: Dòng có note Late, rescheduled. → CSV không được vỡ cột. → Note được quote, row vẫn parse đúng số field.
Column contract — grouped mode
Thứ tự cột trong mode grouped (header row luôn có): Name, Rostered Hours, Clocked Hours, Actual Hours, To Pay Hours, Leave Hours.
- Gộp theo staff identity, không theo tên hiển thị; 2 staff trùng tên vẫn là 2 record riêng.
- Bỏ cột mức ca (
Date,Org,Tracking Code,Note, mọi cột time range) thay vì emit rỗng. - Mỗi cột giờ = tổng theo staff, format 2 số thập phân; dòng không có giá trị đóng góp
0. Leave Hours= tổng leave-overlap của các dòng đã chọn; staff không có overlap →0.00.- Không có cột leave
Tracking Codetrong mode này (code không cộng được); contract leave columns vẫn ràng buộc modeseparated. - Record sort theo tên staff.
Ví dụ thực tế: Selection 7 dòng của 3 staff (2 người trùng tên). → Không gộp nhầm thành 1. → CSV có header + đúng 3 record; giờ của 2 người trùng tên không cộng chung.
Leave columns — nguồn dữ liệu
Leave Hours= giờ giao (overlap) giữa interval leave và interval ca — không phảiEmployeeAbsencetotal hours.- Cột leave
Tracking Code= tracking code của leave overlap — không phải shift tracking code. - Nhiều leave trùng một ca → gộp vào cùng 1 shift row; các giá trị giữ thứ tự overlap start time.
- Shift không có leave overlap → cột leave theo empty-value convention.
Ví dụ thực tế: Ca 09:00–17:00, leave 07:00–11:00 hours = 4, code AL. → Leave Hours = 2 (overlap), code AL. → Không dùng 4 (total) hay code ca OT.
Guardrails
- Export request phải mang id chứ không phải row data; row data trong payload bị bỏ qua.
- Scope và quyền kiểm tra ở server; id ngoài scope → reject toàn bộ.
- Mark
exportedluôn gắn với render thành công; không có request riêng cho việc đánh dấu. - Trạng thái timesheet của leave-only row không bao giờ đổi lifecycle duyệt của leave.
- Mọi giá trị export phải được backend dựng; browser không chứa logic định dạng/leave-overlap.
Dữ liệu chính
| Nhóm | Dữ liệu |
|---|---|
| Payroll settings | BusinessUnitSetting.DefaultPaidHoursOptionId (0/1). |
| Ca & link | Shift, Shift.OrgEmployeeId, OrgEmployee. |
| Trạng thái dòng | Shift.TimesheetStatusId / Shift.Exported; EmployeeAbsence.TimesheetStatusId / EmployeeAbsence.Exported. |
| Leave | EmployeeAbsence (Date, TimeStart/TimeEnd, tracking code). |
| Mã phân loại | TrackingCode / ShiftTrackingCode (TypeId Leave/Shift). |
| Template | jsReport template khớp ReportCode Timesheet CSV. |
Định dạng attendance export
Service report serialize ngày theo ISO và giờ theo đồng hồ 24 giờ trước khi đưa dataset cho jsReport. Manual export và Report Subscription dùng cùng đường generate Roll Sheet nên giữ cùng format.
Ví dụ thực tế: Cùng một khoảng ngày chạy từ admin và scheduler. → Hai file có cùng ngày ISO/giờ 24 giờ vì dùng chung report service.