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. |