Appearance
Cấu hình chấm công đa đơn vị cho Daisy và Phương Nam
Version: 1.0 Date: 13/04/2026 Author: PO/BA Type: Enhancement Complexity: M Module: timekeeping, settings
Changelog
| Version | Date | Author | Thay đổi |
|---|---|---|---|
| 1.0 | 13/04/2026 | PO/BA | Initial |
Hướng dẫn đọc (RACI)
| Audience | Đọc sections | Trách nhiệm |
|---|---|---|
| PO/BA | A0 → Z → A5 → A10 | Approve scope, rule nghiệp vụ, assumptions |
| Tech Lead | A0 → Z → A5 → A7 | Approve boundary, blast radius, rollout |
| FE Admin | A0 → A5 | Thiết kế Settings Module và filter unit |
| FE Mobile | A0 → A5 | Thiết kế state hiển thị 2 mốc / 4 mốc |
| BE Dev | Z → A5 → A10 | Implement config model, runtime adapter, export logic |
| QA | A5 → A7 → A8 | Lập test matrix theo đơn vị, ca, rule phạt |
Executive Summary (TL;DR)
Feature này bổ sung lớp config chấm công theo đơn vị để Diva có thể hỗ trợ Daisy và Phương Nam mà không phá logic legacy của Diva hiện tại.
Giải pháp trọng tâm là thêm timekeeping_unit và các bảng config per unit cho ca làm việc, GPS, quota đơn, công chuẩn, tính công, OT, penalty, đồng thời chỉ mở rộng runtime ở những điểm không thể giải quyết bằng config thuần.
Milestones
| Milestone | Target | Owner | Điều kiện |
|---|---|---|---|
| Discovery + scope lock | T+2 ngày | PO/BA + TL | Chốt boundary config vs runtime |
| PRD sign-off | T+3 ngày | PO/BA | Locked Decision Log |
| UI/Dev/QA split specs | T+5 ngày | PO/BA + TL | PRD approved |
Trạng thái Sign-off
| Domain | Người | Status |
|---|---|---|
| Business | PO | Pending |
| Tech | Tech Lead | Pending |
| QA | QA Lead | Pending |
Pending Decisions
Không có Pending Decision critical ở mức PRD này. Các điểm nghiệp vụ chính cho Daisy và Phương Nam đã đủ rõ để đặc tả lớp config Day-1.
Backlog Phase 2 (Out-of-scope)
| # | Tính năng | Lý do defer |
|---|---|---|
| 1 | Auto-generate ca xoay theo tuần cho Daisy | Day-1 dùng import/thủ công, không build scheduling engine mới |
| 2 | Payroll tự động từ bảng công | Ngoài scope timekeeping config |
| 3 | Full remote attendance đa policy | Day-1 chỉ reuse flow remote hiện có cho nhóm nhỏ |
Z) Decision Log
| ID | Category | Quyết định | Lý do | Ngày | Status |
|---|---|---|---|---|---|
| DEC-001 | Technical | Dùng timekeeping_unit làm lớp boundary cho bài toán Daisy/Phương Nam | Tránh sửa boundary company/branch/department của toàn hệ thống | 13/04/2026 | Locked |
| DEC-002 | Scope | Chỉ thay đổi trong timekeeping, settings, mobile attendance, request timekeeping, export timekeeping | Giảm blast radius lên dashboard, salary, payroll legacy | 13/04/2026 | Locked |
| DEC-003 | Technical | Config phải được đọc từ DB, không hardcode Daisy/Phương Nam trong code | Đảm bảo thêm đơn vị mới không cần deploy lại | 13/04/2026 | Locked |
| DEC-004 | Business | Daisy và Phương Nam có policy khác nhau về ca, công chuẩn, penalty, OT, quota đơn | Cần config per unit thay vì dùng global hrm_master_data | 13/04/2026 | Locked |
| DEC-005 | Technical | Shift template tiếp tục là đơn vị cấu hình ca, nhưng được mở rộng thêm field điều khiển 2 mốc / 4 mốc và break policy | Reuse model hiện có, tránh tạo engine ca song song | 13/04/2026 | Locked |
| DEC-006 | Technical | Runtime logTimeKeeping và log_time_keeping_flag phải hỗ trợ segment_4 cho các ca cần 4 mốc | Không thể cover PN ca gãy và Daisy DV chỉ bằng config hiện tại | 13/04/2026 | Locked |
| DEC-007 | Business | Daisy dùng fixed workday (0 / 0.5 / 1.0); Phương Nam dùng hourly workday theo giờ thực tế | Rule tính công của 2 đơn vị khác nhau bản chất | 13/04/2026 | Locked |
| DEC-008 | Technical | Quota đơn đi trễ/quên chấm phải scope theo timekeeping_unit | Global count hiện tại sẽ làm sai khi nhiều đơn vị chạy chung tenant | 13/04/2026 | Locked |
| DEC-009 | Technical | GPS, branch mapping, remote allowance phải cấu hình per unit | Tránh user punch nhầm chi nhánh ngoài phạm vi đơn vị mình | 13/04/2026 | Locked |
| DEC-010 | Scope | PRD này tập trung vào lớp config + boundary runtime tối thiểu, không đặc tả toàn bộ rollout multi-company | Giữ tài liệu đúng câu hỏi “nên tạo thêm config như nào” | 13/04/2026 | Locked |
A) PRD
A0) Feature Overview
Ý tưởng cốt lõi
Hệ thống chấm công Diva hiện đã có nền tảng tốt cho ca 2 mốc, ca có break cố định, GPS, request timekeeping, working sheet và export. Tuy nhiên, toàn bộ rule hiện vẫn thiên về global config hoặc hardcode runtime, nên không đủ để chạy đồng thời Daisy và Phương Nam trên cùng shared tenant.
Feature này bổ sung một lớp config chấm công đa đơn vị để:
- reuse tối đa logic hiện tại,
- cô lập rule của từng đơn vị,
- và chỉ mở rộng runtime ở đúng các điểm hệ thống hiện chưa hỗ trợ bằng config.
Kiến trúc tổng thể
text
HR/Admin
-> Settings Module / Timekeeping Config
-> timekeeping_unit
-> unit assignment
-> shift config
-> GPS config
-> request quota config
-> workday config
-> OT config
-> penalty config
Staff Mobile / Admin Web
-> logTimeKeeping / request_working_schedule / working sheet / export
-> resolve current timekeeping_unit
-> load unit config + shift config
-> apply 2-punch or 4-punch runtime
-> calculate violation / workday / OT / penalty
Legacy Diva
-> unit_id = NULL or unit = Diva
-> keep existing semantics unless explicitly migratedMapping sang tài liệu chi tiết:
| Khối | PRD (FR) | UI Spec | Dev Spec |
|---|---|---|---|
| Unit boundary | FR-001, FR-002 | Settings tabs, filters | DB schema, resolver |
| Shift + punch mode | FR-003, FR-004 | Shift form, mobile CTA | Runtime state machine |
| Rule engine | FR-005, FR-006, FR-007, FR-008 | Config screens, export columns | Calc engine, scheduler |
| Scope & guardrails | FR-009, FR-010 | RBAC/filter behavior | permission + query scope |
Quyết định chính (tóm tắt theo nhóm)
| Nhóm | Quyết định | Ref |
|---|---|---|
| Boundary | Dùng timekeeping_unit, không sửa company boundary toàn hệ thống | DEC-001, DEC-002 |
| Config strategy | Toàn bộ rule per unit đọc từ DB, không hardcode | DEC-003, DEC-004 |
| Shift/runtime | Reuse shift template hiện có, mở rộng thêm điều khiển 2 mốc / 4 mốc | DEC-005, DEC-006 |
| Business rules | Daisy = fixed workday; PN = hourly workday | DEC-007 |
| Multi-tenant safety | Request quota, GPS, remote đều scope theo unit | DEC-008, DEC-009 |
Logic nghiệp vụ đặc biệt
| Chủ đề | Daisy | Phương Nam |
|---|---|---|
| Punch mode | VP 2 mốc, DV 4 mốc | Ca thường 2 mốc, ca gãy 4 mốc |
| Break policy | Có case break linh động 2h | Ca gãy break cố định |
| Tính công | Fixed workday | Hourly workday |
| OT | Theo role, rate riêng | Theo role, rate riêng, threshold 30p |
| Penalty | Có case trừ 0.5 công và phạt tiền | Chủ yếu phạt tiền |
| Quota request | Scope theo unit | Scope theo unit |
Bảng FR tóm tắt
| FR | Mô tả | Priority | Phase |
|---|---|---|---|
| FR-001 | Thêm timekeeping_unit và assignment | Must | 1 |
| FR-002 | Scope query/GPS/request/export theo unit | Must | 1 |
| FR-003 | Mở rộng shift config cho 2 mốc / 4 mốc | Must | 1 |
| FR-004 | Mở rộng runtime support 4 mốc | Must | 1 |
| FR-005 | Config công chuẩn và chế độ tính công | Must | 1 |
| FR-006 | Config penalty per unit | Must | 1 |
| FR-007 | Config OT per unit/role | Must | 1 |
| FR-008 | Config quota đơn/request rule | Must | 1 |
| FR-009 | Config GPS/remote/branch mapping | Must | 1 |
| FR-010 | Guardrails để không phá Diva legacy | Must | 1 |
Data Model tóm tắt
| Table | Loại | Mục đích |
|---|---|---|
timekeeping_unit | MỚI | Định nghĩa đơn vị chấm công |
timekeeping_unit_user | MỚI | Gán user vào unit |
timekeeping_unit_branch | MỚI | Gán branch hợp lệ cho unit |
timekeeping_unit_department | MỚI | Gán department trong phạm vi timekeeping |
timekeeping_workday_rule | MỚI | Config mode tính công, standard hours |
timekeeping_standard_workday_rule | MỚI | Config công chuẩn theo branch label |
timekeeping_penalty_rule | MỚI | Config rule phạt theo loại vi phạm |
timekeeping_ot_rule | MỚI | Config OT theo role/group |
timekeeping_request_rule | MỚI | Config quota và duration của request |
timekeeping_location_rule | MỚI | Config GPS, remote, radius |
time_slot_template | SỬA | Bổ sung field điều khiển punch/break policy |
time_slot_time_keeping | SỬA | Bổ sung segment_index, timekeeping_unit_id khi chạy 4 mốc |
Impact lên code hiện tại
diva-admin/src/modules/settings/components/shift-setting/WorkShiftForm.tsxdiva-admin/src/modules/settings/graphql/shift.graphqldiva-backend/services/ecommerce-api/action/log_time_keeping.godiva-backend/services/ecommerce-api/scheduler/log_time_keeping_flag.godiva-backend/pkg/store/user_salary.godiva-flutter/staff/lib/data/models/attendance.dartdiva-flutter/staff/lib/data/data_source/remote/attendance_repository/attendance_repository.impl.dart
Timeline
| Phase | Nội dung | Target |
|---|---|---|
| Phase 1 | PRD + scope lock | T+3 ngày |
| Phase 2 | UI Spec / Dev Spec / QA split | T+5 ngày |
| Phase 3 | Implementation | Theo plan riêng |
A1) Blueprint
| Field | Value |
|---|---|
| Feature | Cấu hình chấm công đa đơn vị cho Daisy và Phương Nam |
| Type | Enhancement |
| Platform | Web Admin, Staff Mobile, Hasura/Go backend |
| Module ảnh hưởng | timekeeping, settings, request_working_schedule, mobile attendance |
A2) Context
As-Is
- Shift hiện có các field
start_time,end_time,break_time,start_break_time,end_break_time,workday,roles. - Runtime mobile/backend hiện chủ yếu coi một ngày chấm công là 1 cặp
clock_in/clock_out. - Working sheet/admin/mobile đã đọc được dữ liệu nghỉ giữa ca ở mức template, nhưng chưa có state machine rõ ràng cho 4 mốc.
- Request count đang dựa vào PostgreSQL functions đọc global config trong
hrm_master_data. - Công chuẩn trong salary/timekeeping vẫn suy từ
branchLabelID, chưa cấu hình được cho từng đơn vị. - GPS kiểm tra theo branch chung, chưa biết “unit nào được quyền punch ở branch nào”.
To-Be
- Mỗi user chạy timekeeping theo 1
timekeeping_unitactive. - Mỗi
timekeeping_unitcó bộ config riêng cho:- punch mode / break policy,
- workday calculation,
- standard workday,
- penalty,
- OT,
- request quota,
- GPS/remote/branch scope.
- Shift vẫn là nguồn cấu hình ca chính, nhưng có thêm khả năng mô tả:
- ca 2 mốc,
- ca 4 mốc,
- break cố định,
- break linh động.
- Diva legacy tiếp tục chạy như cũ nếu chưa được map sang model mới.
A3) Goals & Success Metrics
| Goal | Metric | Target |
|---|---|---|
| Hỗ trợ rule Daisy và PN mà không hardcode | % rule read from DB config | 100% rule Day-1 |
| Cô lập dữ liệu/timekeeping policy theo đơn vị | % request/export filtered by unit | 100% |
| Hỗ trợ đúng ca 2 mốc và 4 mốc | Pilot scenario pass rate | 100% P0 |
| Không làm regression Diva legacy | P0 regression count | 0 |
| Giảm phụ thuộc Excel thủ công | Export đủ cột rule-based cho HR | 100% pilot |
A4) Personas
| Persona | Vai trò | JTBD | Frequency |
|---|---|---|---|
| HR Daisy | Quản trị công, rule phạt | Muốn tự cấu hình rule cho Daisy mà không cần dev sửa code | Hàng tuần/tháng |
| HR Phương Nam | Quản trị công, công chuẩn | Muốn cấu hình cách tính công theo giờ và quota đơn riêng | Hàng tuần/tháng |
| System Admin | Quản trị nền | Muốn thêm đơn vị mới bằng config, không tạo nhánh logic riêng | Theo rollout |
| Staff | Chấm công trên app | Muốn app hiển thị đúng nút và rule theo ca của mình | Hàng ngày |
| Tech Lead | Kiểm soát blast radius | Muốn thay đổi gói gọn trong timekeeping, không ảnh hưởng payroll/report legacy | Mỗi đợt release |
A5) Functional Requirements
FR-001: Timekeeping Unit Boundary (Ref: DEC-001, DEC-002)
Priority: Must | SCR: Settings / Unit Setup
AC:
- [ ] Hệ thống cho phép tạo
timekeeping_unittối thiểu choDiva,Daisy,Phuong Nam. - [ ] Mỗi user chỉ có tối đa
1 active timekeeping_unittại một thời điểm. - [ ] Unit chỉ dùng trong phạm vi timekeeping và request timekeeping.
- [ ] Không thay đổi semantics
branch,department,companyhiện có của toàn hệ thống.
FR-002: Scope Mapping Theo Unit (Ref: DEC-004, DEC-008, DEC-009)
Priority: Must | SCR: Settings / Unit Scope
AC:
- [ ] Admin map được branch hợp lệ cho từng unit.
- [ ] Admin map được department hợp lệ cho từng unit trong phạm vi timekeeping.
- [ ] Request count, working sheet, export và mobile attendance chỉ đọc dữ liệu thuộc unit active của user.
- [ ] Nếu user chưa được map unit thì hệ thống fallback legacy path hoặc chặn vào flow mới theo config rollout.
FR-003: Mở rộng Shift Config cho 2 Mốc / 4 Mốc (Ref: DEC-005)
Priority: Must | SCR: Shift Form
AC:
- [ ] Shift template có field
clocking_modevới giá trị tối thiểupair_2,segment_4. - [ ] Shift template có field
break_window_typevới giá trị tối thiểunone,fixed,flex. - [ ] Shift template có field
break_tolerance_minutes. - [ ] Shift template có field
standard_hoursđể phục vụ mode tính công theo giờ. - [ ] Daisy VP và PN ca thường cấu hình được bằng
pair_2. - [ ] Daisy DV và PN ca gãy cấu hình được bằng
segment_4.
FR-004: Runtime Hỗ trợ 4 Mốc (Ref: DEC-006)
Priority: Must | SCR: Mobile Attendance / Working Sheet
AC:
- [ ]
logTimeKeepingxác định được trạng tháivào ca,ra nghỉ,vào lại,ra vềkhi shift làsegment_4. - [ ]
log_time_keeping_flagtính late/early theo từng segment thay vì 1 cặp giờ vào/ra duy nhất. - [ ] Working sheet/admin history hiển thị được đầy đủ 4 mốc trong cùng ngày.
- [ ] Ca
pair_2vẫn giữ nguyên semantics hiện tại, không bị ảnh hưởng bởi flowsegment_4.
FR-005: Config Tính Công và Công Chuẩn (Ref: DEC-007)
Priority: Must | SCR: Workday Settings
AC:
- [ ] Hệ thống có
timekeeping_workday_rulevới tối thiểu 2 mode:fixed,hourly. - [ ] Hệ thống có
timekeeping_standard_workday_ruleđể xác định công chuẩn theobranch_label_idhoặc unit scope. - [ ] Daisy dùng
fixed: hỗ trợ 0 / 0.5 / 1.0 công. - [ ] Phương Nam dùng
hourly:workday = actual_hours / standard_hours. - [ ] Rule approved late/early keeps full workday cấu hình được cho PN.
FR-006: Config Penalty Theo Unit (Ref: DEC-003, DEC-004)
Priority: Must | SCR: Penalty Settings
AC:
- [ ] Hệ thống có bảng
timekeeping_penalty_ruletheounit_idvàviolation_type. - [ ] Mỗi rule hỗ trợ tối thiểu 3 mode:
money_per_minute,money_fixed,workday_fraction. - [ ] Rule hỗ trợ
free_allowance_per_month. - [ ] Rule hỗ trợ
allowance_pool = shared | individual. - [ ] Daisy cấu hình được case quên đầu/cuối ca trừ
0.5 workdayvà quên giữa trưa phạt tiền. - [ ] PN cấu hình được case quên chấm phạt
30.000đ/lỗi.
FR-007: Config OT Theo Unit/Role (Ref: DEC-004)
Priority: Must | SCR: OT Settings
AC:
- [ ] Hệ thống có
timekeeping_ot_ruletheounit_idvà role/group. - [ ] Rule hỗ trợ
minimum_ot_minutes. - [ ] Rule hỗ trợ
rate_per_hour. - [ ] PN cấu hình được threshold
30 phút, BS150.000đ/h, còn lại50.000đ/h. - [ ] Daisy cấu hình được BS
150.000đ/hvà nhóm còn lại theo rate Daisy.
FR-008: Config Quota và Validation của Request (Ref: DEC-008)
Priority: Must | SCR: Request Settings
AC:
- [ ] Hệ thống có
timekeeping_request_rulecho tối thiểu các loạilate_arrival_early_leave,forget_clock_in_out,remote. - [ ] Rule hỗ trợ
max_requests_per_month. - [ ] Rule hỗ trợ
max_duration_minutes. - [ ] Daisy cấu hình được max
60 phút/lầncho đơn đi trễ/về sớm. - [ ] Request count PostgreSQL functions phải đọc quota theo unit thay vì global
hrm_master_data.
FR-009: Config GPS / Remote / Branch Scope (Ref: DEC-009)
Priority: Must | SCR: Location Settings
AC:
- [ ] Hệ thống có
timekeeping_location_ruleper unit. - [ ] Rule hỗ trợ
gps_required,radius_meters,allow_remote. - [ ] Rule hỗ trợ whitelist branch được punch.
- [ ] Daisy Marketing ca 3 và nhóm nhỏ PN có thể được enable remote qua config phù hợp.
- [ ] Người dùng bị reject rõ lý do khi GPS ngoài phạm vi unit mình.
FR-010: Guardrails và Legacy Safety (Ref: DEC-002, DEC-010)
Priority: Must | SCR: N/A
AC:
- [ ] Không sửa dashboard/report/payroll ngoài scope timekeeping.
- [ ] Các query/filter mới phải optional theo
timekeeping_unit. - [ ] Diva legacy tiếp tục chạy được khi chưa migrate sang config mới.
- [ ] Toàn bộ logic mới có feature flag hoặc migration strategy rõ ràng để rollout theo unit.
A6) Assumptions
| ID | Assumption | Owner xác nhận |
|---|---|---|
| ASM-001 | Daisy và PN sẽ được map user vào unit trước khi pilot | PO + Ops |
| ASM-002 | Day-1 không yêu cầu thay thế toàn bộ module salary/payroll | PO |
| ASM-003 | Existing shift template tiếp tục là nền cấu hình ca chính | TL |
| ASM-004 | Các export hiện có sẽ được extend, không build module export mới từ đầu | TL + FE |
| ASM-005 | Có thể rollout từng unit, không bắt buộc bật đồng thời toàn tenant | PO + TL |
A7) Risks
| ID | Risk | Impact | Probability | Mitigation |
|---|---|---|---|---|
| RSK-001 | Config model quá ít field, sau này lại phải hardcode thêm | Cao | Trung bình | Thiết kế theo nhóm rule, không theo từng đơn vị cụ thể |
| RSK-002 | Runtime 4 mốc làm hỏng ca 2 mốc hiện tại | Cao | Trung bình | Tách path pair_2 và segment_4, regression suite riêng |
| RSK-003 | Scope unit bị lọt khiến count/request/export sai | Cao | Trung bình | Enforce unit_id ở DB function, query layer và export layer |
| RSK-004 | Công chuẩn/config workday va chạm logic salary legacy | Trung bình | Trung bình | Chỉ apply trong scope timekeeping feature; salary legacy cần mapping rõ |
| RSK-005 | HR cấu hình sai rule phạt/OT dẫn tới sai số liệu | Trung bình | Cao | Validation chặt, seed mặc định, preview rule trước khi save |
A8) Metrics (Post-launch)
| Metric | Cách đo | Target | Khi nào đo |
|---|---|---|---|
config_coverage_ratio | % rule Day-1 đọc từ config DB | 100% | Sau UAT |
legacy_regression_count | Số P0/P1 regression trên Diva cũ | 0 P0 | Pilot |
pn_daisy_punch_success_rate | % thao tác punch thành công | >= 99% | Pilot tuần 1 |
export_match_manual_check | % report khớp kiểm tay HR | 100% sample agreed | Pilot tuần 2 |
unit_scope_violation_count | Số case thấy dữ liệu ngoài unit | 0 | Pilot |
A9) Glossary
| Thuật ngữ (VI) | Thuật ngữ (EN) | Định nghĩa | Phân biệt với |
|---|---|---|---|
| Đơn vị chấm công | Timekeeping Unit | Lớp scope chỉ dùng cho timekeeping | Khác company/global tenant |
| Ca 2 mốc | 2-punch shift | Chỉ gồm vào ca và ra về | Khác ca 4 mốc |
| Ca 4 mốc | 4-punch shift | Gồm vào ca, ra nghỉ, vào lại, ra về | Khác break_time thuần ở template cũ |
| Break cố định | Fixed break | Khoảng nghỉ được chốt theo giờ cụ thể | Khác break linh động |
| Break linh động | Flexible break | Có khoảng nghỉ chuẩn nhưng cho phép lệch trong tolerance | Khác remote |
| Tính công cố định | Fixed workday | Chấm công quy đổi theo 0 / 0.5 / 1.0 | Khác tính theo giờ |
| Tính công theo giờ | Hourly workday | Quy đổi công dựa trên actual hours / standard hours | Khác OT hours |
| Pool miễn phạt | Allowance pool | Cách cộng dồn số lần miễn phạt giữa các loại vi phạm | Khác quota đơn |
RACI
| Deliverable | PO | TL | UI/UX | FE Dev | BE Dev | QA |
|---|---|---|---|---|---|---|
| PRD (file này) | A | C | I | I | I | I |
| UI Spec | C | I | A | R | I | I |
| Dev Spec | I | A | I | C | R | I |
| QA Test Plan | C | I | I | I | I | R |
R = Responsible, A = Accountable, C = Consulted, I = Informed
A10) Business Formulas
FORMULA-001: Tính công theo giờ cho Phương Nam
- Mô tả: Quy đổi ngày công theo giờ làm thực tế khi unit dùng mode
hourly. - Công thức:
workday = min(shift_workday, actual_hours / standard_hours) - Biến số:
actual_hours: tổng số giờ làm thực tế của các segment trong ngàystandard_hours: số giờ chuẩn/ngày của group áp dụngshift_workday: số công tối đa của ca
- Đơn vị: công
- Ví dụ: NV làm
7 giờ, chuẩn8 giờ->7/8 = 0.875 công - Edge cases:
- Có đơn approved theo policy “giữ full công” ->
workday = shift_workday actual_hours <= 0->0 công
- Có đơn approved theo policy “giữ full công” ->
FORMULA-002: Tổng phút vi phạm ca 2 mốc
- Mô tả: Tính tổng phút đi trễ/về sớm với ca
pair_2. - Công thức:
total_violation_minutes = late_minutes + early_minutes - Biến số:
late_minutes = max(0, actual_clock_in - scheduled_start)early_minutes = max(0, scheduled_end - actual_clock_out)
- Đơn vị: phút
- Ví dụ: vào trễ
10 phút, ra sớm5 phút-> tổng15 phút - Edge cases:
- Thiếu
clock_inhoặcclock_out-> không tính theo formula này, chuyển sang missing-punch rule
- Thiếu
FORMULA-003: Tổng phút vi phạm ca 4 mốc
- Mô tả: Tính vi phạm cho ca
segment_4theo từng segment sáng/chiều. - Công thức:
total_violation_minutes = seg0_late + seg0_early + seg1_late + seg1_early - Biến số:
seg0: vào ca -> ra nghỉseg1: vào lại -> ra vềbreak_tolerance_minutes: tolerance của break nếubreak_window_type = flex
- Đơn vị: phút
- Ví dụ: trễ đầu ca sáng
5 phút, vào lại chiều trễ12 phút-> tổng17 phút - Edge cases:
- PN ca gãy dùng
break_tolerance_minutes = 0 - Daisy BS/Phụ tá ca 2 có thể dùng tolerance
60
- PN ca gãy dùng
FORMULA-004: Penalty theo phút
- Mô tả: Tính tiền phạt cho rule
money_per_minute. - Công thức:
penalty_amount = violation_minutes * rate_per_minute - Biến số:
violation_minutes: tổng phút vi phạm sau khi apply exemptionrate_per_minute: đơn giá phạt/phút
- Đơn vị: VND
- Ví dụ:
12 phút * 10.000 = 120.000đ - Edge cases:
- Nếu vẫn nằm trong số lần miễn phạt ->
0đ
- Nếu vẫn nằm trong số lần miễn phạt ->
FORMULA-005: Penalty cố định theo lần
- Mô tả: Tính tiền phạt cho rule
money_fixed. - Công thức:
penalty_amount = violation_count_after_free * fixed_amount - Biến số:
violation_count_after_free: số lần vi phạm sau miễnfixed_amount: số tiền phạt mỗi lần
- Đơn vị: VND
- Ví dụ: quên chấm giữa trưa
1 lầnsau miễn, rule50.000đ/lần->50.000đ - Edge cases:
- Nếu
allowance_pool = shared, count phải tính trên pool chung
- Nếu
FORMULA-006: Trừ công theo vi phạm
- Mô tả: Tính số công bị trừ khi rule là
workday_fraction. - Công thức:
deducted_workday = violation_count_after_free * fraction - Biến số:
fraction: số công bị trừ mỗi lần, ví dụ0.5
- Đơn vị: công
- Ví dụ: quên chấm đầu ca
1 lần, rule0.5-> trừ0.5 công - Edge cases:
- Không trừ quá
shift_workdaycủa ngày đó
- Không trừ quá
FORMULA-007: Công chuẩn theo rule
- Mô tả: Tính công chuẩn tháng từ
timekeeping_standard_workday_rule. - Công thức:
standard_workday = rule(mode, month, branch_label) - Biến số:
mode:days_minus_sun,days_minus_sun_half_sat,fixed_26,fixed_custom
- Đơn vị: công
- Ví dụ: tháng có
30 ngày,4 CN, modedays_minus_sun->26 - Edge cases:
- Không có rule -> fallback
fixed_26hoặc rule mặc định đã thống nhất
- Không có rule -> fallback