Skip to content

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

VersionDateAuthorThay đổi
1.013/04/2026PO/BAInitial

Hướng dẫn đọc (RACI)

AudienceĐọc sectionsTrách nhiệm
PO/BAA0 → Z → A5 → A10Approve scope, rule nghiệp vụ, assumptions
Tech LeadA0 → Z → A5 → A7Approve boundary, blast radius, rollout
FE AdminA0 → A5Thiết kế Settings Module và filter unit
FE MobileA0 → A5Thiết kế state hiển thị 2 mốc / 4 mốc
BE DevZ → A5 → A10Implement config model, runtime adapter, export logic
QAA5 → A7 → A8Lậ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ợ DaisyPhương Namkhô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

MilestoneTargetOwnerĐiều kiện
Discovery + scope lockT+2 ngàyPO/BA + TLChốt boundary config vs runtime
PRD sign-offT+3 ngàyPO/BALocked Decision Log
UI/Dev/QA split specsT+5 ngàyPO/BA + TLPRD approved

Trạng thái Sign-off

DomainNgườiStatus
BusinessPOPending
TechTech LeadPending
QAQA LeadPending

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ăngLý do defer
1Auto-generate ca xoay theo tuần cho DaisyDay-1 dùng import/thủ công, không build scheduling engine mới
2Payroll tự động từ bảng côngNgoài scope timekeeping config
3Full remote attendance đa policyDay-1 chỉ reuse flow remote hiện có cho nhóm nhỏ

Z) Decision Log

IDCategoryQuyết địnhLý doNgàyStatus
DEC-001TechnicalDùng timekeeping_unit làm lớp boundary cho bài toán Daisy/Phương NamTránh sửa boundary company/branch/department của toàn hệ thống13/04/2026Locked
DEC-002ScopeChỉ thay đổi trong timekeeping, settings, mobile attendance, request timekeeping, export timekeepingGiảm blast radius lên dashboard, salary, payroll legacy13/04/2026Locked
DEC-003TechnicalConfig 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ại13/04/2026Locked
DEC-004BusinessDaisy và Phương Nam có policy khác nhau về ca, công chuẩn, penalty, OT, quota đơnCần config per unit thay vì dùng global hrm_master_data13/04/2026Locked
DEC-005TechnicalShift 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 policyReuse model hiện có, tránh tạo engine ca song song13/04/2026Locked
DEC-006TechnicalRuntime logTimeKeepinglog_time_keeping_flag phải hỗ trợ segment_4 cho các ca cần 4 mốcKhông thể cover PN ca gãy và Daisy DV chỉ bằng config hiện tại13/04/2026Locked
DEC-007BusinessDaisy 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ất13/04/2026Locked
DEC-008TechnicalQuota đơn đi trễ/quên chấm phải scope theo timekeeping_unitGlobal count hiện tại sẽ làm sai khi nhiều đơn vị chạy chung tenant13/04/2026Locked
DEC-009TechnicalGPS, branch mapping, remote allowance phải cấu hình per unitTránh user punch nhầm chi nhánh ngoài phạm vi đơn vị mình13/04/2026Locked
DEC-010ScopePRD 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-companyGiữ tài liệu đúng câu hỏi “nên tạo thêm config như nào”13/04/2026Locked

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

Mapping sang tài liệu chi tiết:

KhốiPRD (FR)UI SpecDev Spec
Unit boundaryFR-001, FR-002Settings tabs, filtersDB schema, resolver
Shift + punch modeFR-003, FR-004Shift form, mobile CTARuntime state machine
Rule engineFR-005, FR-006, FR-007, FR-008Config screens, export columnsCalc engine, scheduler
Scope & guardrailsFR-009, FR-010RBAC/filter behaviorpermission + query scope

Quyết định chính (tóm tắt theo nhóm)

NhómQuyết địnhRef
BoundaryDùng timekeeping_unit, không sửa company boundary toàn hệ thốngDEC-001, DEC-002
Config strategyToàn bộ rule per unit đọc từ DB, không hardcodeDEC-003, DEC-004
Shift/runtimeReuse shift template hiện có, mở rộng thêm điều khiển 2 mốc / 4 mốcDEC-005, DEC-006
Business rulesDaisy = fixed workday; PN = hourly workdayDEC-007
Multi-tenant safetyRequest quota, GPS, remote đều scope theo unitDEC-008, DEC-009

Logic nghiệp vụ đặc biệt

Chủ đềDaisyPhương Nam
Punch modeVP 2 mốc, DV 4 mốcCa thường 2 mốc, ca gãy 4 mốc
Break policyCó case break linh động 2hCa gãy break cố định
Tính côngFixed workdayHourly workday
OTTheo role, rate riêngTheo role, rate riêng, threshold 30p
PenaltyCó case trừ 0.5 công và phạt tiềnChủ yếu phạt tiền
Quota requestScope theo unitScope theo unit

Bảng FR tóm tắt

FRMô tảPriorityPhase
FR-001Thêm timekeeping_unit và assignmentMust1
FR-002Scope query/GPS/request/export theo unitMust1
FR-003Mở rộng shift config cho 2 mốc / 4 mốcMust1
FR-004Mở rộng runtime support 4 mốcMust1
FR-005Config công chuẩn và chế độ tính côngMust1
FR-006Config penalty per unitMust1
FR-007Config OT per unit/roleMust1
FR-008Config quota đơn/request ruleMust1
FR-009Config GPS/remote/branch mappingMust1
FR-010Guardrails để không phá Diva legacyMust1

Data Model tóm tắt

TableLoạiMục đích
timekeeping_unitMỚIĐịnh nghĩa đơn vị chấm công
timekeeping_unit_userMỚIGán user vào unit
timekeeping_unit_branchMỚIGán branch hợp lệ cho unit
timekeeping_unit_departmentMỚIGán department trong phạm vi timekeeping
timekeeping_workday_ruleMỚIConfig mode tính công, standard hours
timekeeping_standard_workday_ruleMỚIConfig công chuẩn theo branch label
timekeeping_penalty_ruleMỚIConfig rule phạt theo loại vi phạm
timekeeping_ot_ruleMỚIConfig OT theo role/group
timekeeping_request_ruleMỚIConfig quota và duration của request
timekeeping_location_ruleMỚIConfig GPS, remote, radius
time_slot_templateSỬABổ sung field điều khiển punch/break policy
time_slot_time_keepingSỬABổ 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.tsx
  • diva-admin/src/modules/settings/graphql/shift.graphql
  • diva-backend/services/ecommerce-api/action/log_time_keeping.go
  • diva-backend/services/ecommerce-api/scheduler/log_time_keeping_flag.go
  • diva-backend/pkg/store/user_salary.go
  • diva-flutter/staff/lib/data/models/attendance.dart
  • diva-flutter/staff/lib/data/data_source/remote/attendance_repository/attendance_repository.impl.dart

Timeline

PhaseNội dungTarget
Phase 1PRD + scope lockT+3 ngày
Phase 2UI Spec / Dev Spec / QA splitT+5 ngày
Phase 3ImplementationTheo plan riêng

A1) Blueprint

FieldValue
FeatureCấu hình chấm công đa đơn vị cho Daisy và Phương Nam
TypeEnhancement
PlatformWeb Admin, Staff Mobile, Hasura/Go backend
Module ảnh hưởngtimekeeping, 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_unit active.
  • Mỗi timekeeping_unit có 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

GoalMetricTarget
Hỗ trợ rule Daisy và PN mà không hardcode% rule read from DB config100% rule Day-1
Cô lập dữ liệu/timekeeping policy theo đơn vị% request/export filtered by unit100%
Hỗ trợ đúng ca 2 mốc và 4 mốcPilot scenario pass rate100% P0
Không làm regression Diva legacyP0 regression count0
Giảm phụ thuộc Excel thủ côngExport đủ cột rule-based cho HR100% pilot

A4) Personas

PersonaVai tròJTBDFrequency
HR DaisyQuản trị công, rule phạtMuốn tự cấu hình rule cho Daisy mà không cần dev sửa codeHàng tuần/tháng
HR Phương NamQuản trị công, công chuẩnMuốn cấu hình cách tính công theo giờ và quota đơn riêngHàng tuần/tháng
System AdminQuản trị nềnMuốn thêm đơn vị mới bằng config, không tạo nhánh logic riêngTheo rollout
StaffChấm công trên appMuốn app hiển thị đúng nút và rule theo ca của mìnhHàng ngày
Tech LeadKiểm soát blast radiusMuốn thay đổi gói gọn trong timekeeping, không ảnh hưởng payroll/report legacyMỗ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_unit tối thiểu cho Diva, Daisy, Phuong Nam.
  • [ ] Mỗi user chỉ có tối đa 1 active timekeeping_unit tạ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, company hiệ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_mode với giá trị tối thiểu pair_2, segment_4.
  • [ ] Shift template có field break_window_type với giá trị tối thiểu none, 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:

  • [ ] logTimeKeeping xác định được trạng thái vào ca, ra nghỉ, vào lại, ra về khi shift là segment_4.
  • [ ] log_time_keeping_flag tí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_2 vẫn giữ nguyên semantics hiện tại, không bị ảnh hưởng bởi flow segment_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_rule với tối thiểu 2 mode: fixed, hourly.
  • [ ] Hệ thống có timekeeping_standard_workday_rule để xác định công chuẩn theo branch_label_id hoặ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_rule theo unit_idviolation_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 workday và 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_rule theo unit_id và role/group.
  • [ ] Rule hỗ trợ minimum_ot_minutes.
  • [ ] Rule hỗ trợ rate_per_hour.
  • [ ] PN cấu hình được threshold 30 phút, BS 150.000đ/h, còn lại 50.000đ/h.
  • [ ] Daisy cấu hình được BS 150.000đ/h và 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_rule cho tối thiểu các loại late_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ần cho đơ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_rule per 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

IDAssumptionOwner xác nhận
ASM-001Daisy và PN sẽ được map user vào unit trước khi pilotPO + Ops
ASM-002Day-1 không yêu cầu thay thế toàn bộ module salary/payrollPO
ASM-003Existing shift template tiếp tục là nền cấu hình ca chínhTL
ASM-004Các export hiện có sẽ được extend, không build module export mới từ đầuTL + FE
ASM-005Có thể rollout từng unit, không bắt buộc bật đồng thời toàn tenantPO + TL

A7) Risks

IDRiskImpactProbabilityMitigation
RSK-001Config model quá ít field, sau này lại phải hardcode thêmCaoTrung bìnhThiết kế theo nhóm rule, không theo từng đơn vị cụ thể
RSK-002Runtime 4 mốc làm hỏng ca 2 mốc hiện tạiCaoTrung bìnhTách path pair_2segment_4, regression suite riêng
RSK-003Scope unit bị lọt khiến count/request/export saiCaoTrung bìnhEnforce unit_id ở DB function, query layer và export layer
RSK-004Công chuẩn/config workday va chạm logic salary legacyTrung bìnhTrung bìnhChỉ apply trong scope timekeeping feature; salary legacy cần mapping rõ
RSK-005HR cấu hình sai rule phạt/OT dẫn tới sai số liệuTrung bìnhCaoValidation chặt, seed mặc định, preview rule trước khi save

A8) Metrics (Post-launch)

MetricCách đoTargetKhi nào đo
config_coverage_ratio% rule Day-1 đọc từ config DB100%Sau UAT
legacy_regression_countSố P0/P1 regression trên Diva cũ0 P0Pilot
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 HR100% sample agreedPilot tuần 2
unit_scope_violation_countSố case thấy dữ liệu ngoài unit0Pilot

A9) Glossary

Thuật ngữ (VI)Thuật ngữ (EN)Định nghĩaPhân biệt với
Đơn vị chấm côngTimekeeping UnitLớp scope chỉ dùng cho timekeepingKhác company/global tenant
Ca 2 mốc2-punch shiftChỉ gồm vào ca và ra vềKhác ca 4 mốc
Ca 4 mốc4-punch shiftGồm vào ca, ra nghỉ, vào lại, ra vềKhác break_time thuần ở template cũ
Break cố địnhFixed breakKhoảng nghỉ được chốt theo giờ cụ thểKhác break linh động
Break linh độngFlexible breakCó khoảng nghỉ chuẩn nhưng cho phép lệch trong toleranceKhác remote
Tính công cố địnhFixed workdayChấm công quy đổi theo 0 / 0.5 / 1.0Khác tính theo giờ
Tính công theo giờHourly workdayQuy đổi công dựa trên actual hours / standard hoursKhác OT hours
Pool miễn phạtAllowance poolCách cộng dồn số lần miễn phạt giữa các loại vi phạmKhác quota đơn

RACI

DeliverablePOTLUI/UXFE DevBE DevQA
PRD (file này)ACIIII
UI SpecCIARII
Dev SpecIAICRI
QA Test PlanCIIIIR

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ày
    • standard_hours: số giờ chuẩn/ngày của group áp dụng
    • shift_workday: số công tối đa của ca
  • Đơn vị: công
  • Ví dụ: NV làm 7 giờ, chuẩn 8 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

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ớm 5 phút -> tổng 15 phút
  • Edge cases:
    • Thiếu clock_in hoặc clock_out -> không tính theo formula này, chuyển sang missing-punch rule

FORMULA-003: Tổng phút vi phạm ca 4 mốc

  • Mô tả: Tính vi phạm cho ca segment_4 theo 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ếu break_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ổng 17 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

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 exemption
    • rate_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 ->

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ễn
    • fixed_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ần sau miễn, rule 50.000đ/lần -> 50.000đ
  • Edge cases:
    • Nếu allowance_pool = shared, count phải tính trên pool chung

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, rule 0.5 -> trừ 0.5 công
  • Edge cases:
    • Không trừ quá shift_workday của ngày đó

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, mode days_minus_sun -> 26
  • Edge cases:
    • Không có rule -> fallback fixed_26 hoặc rule mặc định đã thống nhất