feat(payroll): PC_AT default 730k + clean up Tax Deduction 2024 #2

Merged
tant merged 14 commits from :feat/TETRA-1328-vhrms-phase1-seed-salary into main 2026-04-20 19:29:31 +07:00
Collaborator

Summary

  • Set default 730.000đ cho Phụ cấp ăn trưa (PC_AT) trong Salary Structure 'Lương Chính thức' — convention thị trường VN. Luật 109/2025/QH15 đã bỏ trần 730k từ 15/06/2025 (miễn TNCN toàn bộ) nhưng mức này vẫn phổ biến; HR có thể override ở SSA.
  • Bỏ Vietnam Tax Deduction records 2024 (Personal 11M, Dependent 4.4M) — DN triển khai HRMS từ 2026 không cần lịch sử mức cũ. Nhất quán với greenfield-2026 đã áp cho Minimum Wage + Tax Bracket + Insurance Rate. Trang /desk/vietnam-tax-deduction giờ chỉ còn Personal 15.5M + Dependent 6.2M (NQ 110/2025/UBTVQH15, hiệu lực 01/01/2026).

Changes

  • setup/salary_structure.py — thêm amount: 730000 cho PC_AT trong _earnings_for_official().
  • patches/v0_5_0/set_pc_at_default_amount.py (new) — idempotent patch update PC_AT row trên site đã deploy (chỉ đụng khi amount = 0/NULL).
  • fixtures/__init__.py — xóa list deductions_2024, chỉ seed 2026.
  • doctype/vietnam_tax_deduction.json — update 2 description fields reflect mức 2026.
  • patches/v0_5_0/cleanup_tax_deductions_2024.py (new) — idempotent patch xóa 2 records 2024 trên site deploy (safe-guard: chỉ xóa khi amount khớp 11M/4.4M, tránh đè admin đã sửa tay).
  • patches.txt — đăng ký 2 patches mới.

Test plan

  • Local Frappe container: chạy cleanup_tax_deductions_2024 trên DB có seed cũ — xóa đúng 2 records 2024, còn lại 2 records 2026.
  • Rerun patch — idempotent, "No 2024 records to clean up".
  • get_personal_deduction() → 15.500.000đ; get_dependent_deduction() → 6.200.000đ.
  • Staging: chạy migrate sau merge, verify trang /desk/vietnam-tax-deduction chỉ còn 2 records 2026.
  • Staging: kiểm tra Salary Structure 'Lương Chính thức' hiện hữu → PC_AT row có amount 730.000đ.

Refs: TETRA-1326, TETRA-1328

## Summary - Set default 730.000đ cho Phụ cấp ăn trưa (PC_AT) trong Salary Structure 'Lương Chính thức' — convention thị trường VN. Luật 109/2025/QH15 đã bỏ trần 730k từ 15/06/2025 (miễn TNCN toàn bộ) nhưng mức này vẫn phổ biến; HR có thể override ở SSA. - Bỏ Vietnam Tax Deduction records 2024 (Personal 11M, Dependent 4.4M) — DN triển khai HRMS từ 2026 không cần lịch sử mức cũ. Nhất quán với greenfield-2026 đã áp cho Minimum Wage + Tax Bracket + Insurance Rate. Trang /desk/vietnam-tax-deduction giờ chỉ còn Personal 15.5M + Dependent 6.2M (NQ 110/2025/UBTVQH15, hiệu lực 01/01/2026). ## Changes - `setup/salary_structure.py` — thêm `amount: 730000` cho PC_AT trong `_earnings_for_official()`. - `patches/v0_5_0/set_pc_at_default_amount.py` (new) — idempotent patch update PC_AT row trên site đã deploy (chỉ đụng khi amount = 0/NULL). - `fixtures/__init__.py` — xóa list `deductions_2024`, chỉ seed 2026. - `doctype/vietnam_tax_deduction.json` — update 2 description fields reflect mức 2026. - `patches/v0_5_0/cleanup_tax_deductions_2024.py` (new) — idempotent patch xóa 2 records 2024 trên site deploy (safe-guard: chỉ xóa khi amount khớp 11M/4.4M, tránh đè admin đã sửa tay). - `patches.txt` — đăng ký 2 patches mới. ## Test plan - [x] Local Frappe container: chạy `cleanup_tax_deductions_2024` trên DB có seed cũ — xóa đúng 2 records 2024, còn lại 2 records 2026. - [x] Rerun patch — idempotent, "No 2024 records to clean up". - [x] `get_personal_deduction()` → 15.500.000đ; `get_dependent_deduction()` → 6.200.000đ. - [ ] Staging: chạy migrate sau merge, verify trang /desk/vietnam-tax-deduction chỉ còn 2 records 2026. - [ ] Staging: kiểm tra Salary Structure 'Lương Chính thức' hiện hữu → PC_AT row có amount 730.000đ. Refs: TETRA-1326, TETRA-1328
Add idempotent patch removing Frappe DB references to the removed
vhrms.vhrms.overrides.salary_slip module. Stale Property Setter rows
on Salary Slip caused ModuleNotFoundError when Tetra called get_doc,
breaking the Insurance Contribution Report (returned 0 employees).

Refs: TETRA-1307

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Frappe HRMS Employee Separation doctype has no built-in "last working
day" field (only boarding_begins_on and resignation_letter_date).
Tetra's offboarding code has been writing dto.lastWorkingDay into
`separation_date`, which Frappe silently drops — so the stored value
was always lost and reads returned undefined, which made the
offboarding list render today's date for every record.

Introduce `custom_last_working_day` (Date, in_standard_filter=1) so
Tetra has a canonical place to store and query the last working day.
Fresh installs pick up the field via `setup/custom_fields.py`; existing
sites get it (plus a backfill from `custom_settlement_deadline`) via
the new v0_4_0 patch.

Refs: TETRA-1307

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Tạo 3 doctype phục vụ thưởng năng suất hằng tháng (TETRA-1087):
- Vietnam KPI Bonus Tier (child)
- Vietnam KPI Bonus Rule (parent + validator: bắt buộc tier 0% miễn BHXH
  theo K3 Đ30 TT 59/2015)
- Vietnam Monthly KPI Evaluation (submittable; on_submit tự tạo Employee
  Incentive với Salary Component TH_HS, payroll_date = cuối tháng đánh giá;
  on_cancel hủy Incentive)

Thêm Salary Component "Thưởng hiệu suất" (TH_HS, Earning, taxable, mapping
VAS account 6421) trong setup/salary_components.py. Patch v0_4_0/
seed_default_kpi_bonus_rule seed rule mặc định A(100%)/B(70%)/C(40%)/D(0%)
trần 5tr VND, idempotent.

Tách bạch tuyệt đối với Frappe Appraisal — không hook, không import. KPI
hằng tháng khác Appraisal 6 tháng/năm về mục đích (tính thưởng vs xét tăng
giảm lương, KRA, 360°). Đáp ứng contract API endpoints /api/v1/hrms/kpi-*
đã ship ở Tetra (TETRA-1089).

Refs: TETRA-1087

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Khi submit Employee Incentive, Frappe HRMS auto-tạo Additional Salary
linked qua ref_doctype/ref_docname. Frappe block cancel Incentive khi
còn link → on_cancel của Vietnam Monthly KPI Evaluation phải cancel
Additional Salary trước, mới cancel được Incentive.

Phát hiện qua browser smoke test: cancel evaluation HR-VKPI-2026-00221
trả 417 với "Cannot delete or cancel because Employee Incentive ... is
linked with Additional Salary HR-ADS-26-04-00001".

Refs: TETRA-1087

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Bối cảnh
--------
Tetra API TETRA-1089 đã ship DTO `salary_bands[]` trên Salary Grades endpoints
nhưng vhrms chưa có doctype/custom field hỗ trợ → Frappe drop silently. 20
Employee Grade hiện tại trên staging do seed thủ công bên ngoài code, chỉ có
`grade_category` Select Tech/Non-Tech + grade_level + increment_percentage,
không có child bands. Fresh install không có gì.

Thay đổi
--------
2 doctype mới:

1. `Vietnam Grade Category` — configurable thay cho Select hard-coded. HR có
   thể thêm Mgmt / Lao động phổ thông / ... mà không sửa code. Patch seed 2
   record mặc định "Tech" + "Non-Tech".
2. `Vietnam Salary Band` (child, istable=1) — 3 field khớp Tetra DTO contract:
   `band_number` Int ≥1, `base_amount` Currency ≥0, `effective_from` Date.

Custom fields trên Employee Grade qua `setup/custom_fields.py`:
  - vn_salary_grade_section + grade_category (Link → Vietnam Grade Category)
    + grade_level + increment_percentage (default 10%)
  - salary_bands_section + salary_bands (Table → Vietnam Salary Band)

Custom field `salary_band` (Int) trên Employee.

Validation:
  - Vietnam Salary Band.validate: band_number ≥1, base_amount ≥0, msgprint
    cảnh báo (không hard-fail) khi band_1 < lương tối thiểu vùng IV (3.7M
    fallback hoặc tra Vietnam Minimum Wage doctype nếu có).
  - Employee Grade.validate hook: bands không trùng band_number, msgprint
    cảnh báo nếu thiếu band_1.

Override Salary Structure Assignment.validate auto-fill `base` từ
`employee.grade.salary_bands[employee.salary_band].base_amount`. Tôn trọng
input thủ công (skip nếu base đã set), throw nếu band index không tồn tại
(tránh silent wrong base pay).

Patch `v0_4_0/setup_salary_grade_scaffolding`:
  - Seed 2 Vietnam Grade Category mặc định
  - Trigger `create_custom_fields(get_custom_fields(), ignore_validate=True)`
    để bổ sung field mới (idempotent — site cũ có grade_category Select sẽ
    được upgrade thành Link, dữ liệu cột "Tech"/"Non-Tech" giữ nguyên)

Helper `_seed_bands_for_grade(grade_name, bands)` whitelisted để
scripts/hrms/seed_salary_grades.py upsert child bands sạch qua bench execute.

Tests
-----
13 unit tests cho validate + parent hook + override auto-fill (mock-based,
chạy được trong bench environment).

Compliance
----------
KPI hằng tháng (TETRA-1087) tách riêng — file mới không chạm Appraisal
namespace nào. Grep `appraisal` trên toàn bộ file mới = 0 matches.

Refs: TETRA-1085, TETRA-1089

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Thêm 3 salary components riêng cho OT breakdown theo Đ98 BLLĐ 45/2019:
  OT_WD (Tăng ca ngày thường 150%), OT_WE (Tăng ca cuối tuần 200%),
  OT_HL (Tăng ca ngày lễ 300%)
- is_tax_applicable=0 cho tất cả OT components theo Luật Thuế TNCN 109/2025
  (hiệu lực 01/01/2026 — OT miễn 100% thuế TNCN cho cá nhân cư trú)
- Migration idempotent: flip is_tax_applicable=1 → 0 cho OT_PAY hiện có
- VAS account 6421 cho 3 components mới

Refs: TETRA-1264
Theo Điều 46 BLLĐ 45/2019, số năm tính trợ cấp thôi việc mới = tổng
thời gian làm việc − thời gian đóng BHTN − thời gian đã nhận trợ
cấp thôi việc trước đó. Trước patch này vhrms chưa có chỗ lưu trữ
giai đoạn đã được BHTN / công ty cũ chi trả; Tetra
`calculateSeverance()` chỉ khấu trừ BHTN nên rehired employees
nhận trùng.

Thêm child doctype `Vietnam Prior Severance Record` (istable=1) với
các field: previous_employer, start_date, end_date, years_covered,
amount_paid, payment_date, notes. Gắn vào Employee qua custom Table
field `prior_severance_records` (section "Lịch sử trợ cấp thôi
việc"). Patch `v0_4_0/add_prior_severance_tracking` idempotent sync
field cho site đã migrate.

HR nhập record qua Frappe Desk khi onboard nhân viên tái tuyển
dụng; Tetra `calculateSeverance()` đọc qua REST + khấu trừ trong
công thức (xem PR tetra TETRA-1293 riêng).
- Thêm 10 Salary Component mới:
  + Lương cơ bản (LCB) vào create_vietnam_salary_components
    (fix bug create_overtime_type() fail do link tới LCB chưa tồn tại)
  + 5 phụ cấp không đóng BHXH: PC_AT, PC_XX, PC_DT, PC_NO, PC_TP
    (ăn trưa/xăng xe/điện thoại/nhà ở/trang phục — theo CV 1198/CTL&BHXH-BHXH)
  + 4 mức phụ cấp độc hại: PC_DH_M1-M4
    (0.1-0.4 × LCS, khung 5-15% lương theo NĐ 145/2020 Đ103 + TT 26/2022)

- Thay Salary Structure "Cơ cấu lương Việt Nam" (1 template) bằng 2 template:
  + "Lương Chính thức" (LCB + 5 phụ cấp, 9 deductions)
  + "Lương Thử việc" (LCB × 0.85 theo BLLĐ Đ26, 9 deductions)
  + Cả 2 auto-submit (docstatus=1)

- Biểu thuế TNCN theo Luật 109/2025 (xem TETRA-1251):
  + PIT formula: 7 bậc → 5 bậc (5/10/20/30/35%)
  + Giảm trừ: 11tr/4.4tr → 15.5tr/6.2tr
  + Vietnam Tax Deduction 2026: effective 2026-07-01 → 2026-01-01
  + Seed Income Tax Slab "Biểu thuế lũy tiến TNCN 2026 (5 bậc)"

- 5 migration patches v0_5_0 cho site upgrade 0.3.2 → 0.5.0 (idempotent)
- Fix after_install hook: handle existing Company case
  (Company tạo trước khi install vhrms → on_company_created không fire retroactively)
- Bump version 0.3.2 → 0.5.0

Refs: TETRA-1328
Relates: TETRA-1251

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Bỏ seed 4 records Decree 74/2024 (wages_2024) khỏi fixtures/__init__.py —
  DN triển khai mới từ đầu 2026 không cần lịch sử lương tối thiểu cũ.
- Đổi effective_from của 4 records NĐ 293/2025 từ 2026-07-01 → 2026-01-01,
  đồng bộ với Luật TNCN 2025 và các quy định tiền lương 2026.
- Thêm patch v0_5_0/cleanup_minimum_wages_2026 cho site upgrade:
  xóa records NĐ 74/2024 + re-date NĐ 293/2025.

Refs: TETRA-1328

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Update fixtures/import_tax_brackets(): seed 5 bậc mới (effective 2026-01-01)
  theo Luật TNCN 2025 thay vì 7 bậc cũ (effective 2024-01-01):
    Bậc 1: 0-10M, 5%
    Bậc 2: 10-30M, 10%
    Bậc 3: 30-60M, 20%
    Bậc 4: 60-100M, 30%
    Bậc 5: 100M+, 35%
- Thêm patch v0_5_0/cleanup_tax_brackets_2026 cho site upgrade:
  xóa 7 bậc cũ (2024-01-01) + seed 5 bậc mới (2026-01-01).

Refs: TETRA-1328
Relates: TETRA-1251

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Fix Trade Union NLĐ: 1.0% → 0.5% theo QĐ 61/QĐ-TLĐ (hiệu lực 01/07/2025,
  giảm đoàn phí công đoàn).
- Update effective_from của toàn bộ 4 loại (BHXH/BHYT/BHTN/Trade Union) từ
  2024-01-01 → 2025-07-01 đồng bộ với Luật BHXH 2024 + NĐ 158/2025.
- Các rate khác giữ nguyên (theo luật hiện hành, đúng như cũ):
    BHXH  NLĐ 8%    DN 17.5%  (hưu trí/tử tuất + ốm đau/thai sản + TNLĐ-BNN)
    BHYT  NLĐ 1.5%  DN 3%
    BHTN  NLĐ 1%    DN 1%
    Tổng: NLĐ 10.5% + DN 21.5% = 32%.
- Thêm patch v0_5_0/cleanup_insurance_rates_2025 cho site upgrade.

Refs: TETRA-1328

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Fix silent-fail trong fresh install: khi 'bench install-app vhrms' chạy patches
trước lúc doctype đồng bộ ổn định, 2 patch v0_4_0 (setup_salary_grade_scaffolding,
seed_default_kpi_bonus_rule) bị đánh dấu 'applied' nhưng insert fail silent →
site mới không có Grade Category / KPI Bonus Rule.

Cùng pattern đã fix cho Income Tax Slab:
- Extract logic sang setup/grade_category.py + setup/kpi_bonus_rule.py.
- Gọi từ after_install + after_migrate hooks (state stable).
- Patch v0_4_0 refactor thành delegate sang setup function (giữ path upgrade cho
  site cài 0.3.2 lên 0.5.0).

Refs: TETRA-1328
Relates: TETRA-1085, TETRA-1087

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Set amount mặc định 730.000đ cho Phụ cấp ăn trưa trong Salary Structure
'Lương Chính thức' — convention thị trường VN. Luật 109/2025/QH15 đã bỏ
trần 730k của TT 26/2016 từ 15/06/2025 (miễn TNCN toàn bộ), nhưng mức
này vẫn phổ biến. HR có thể override ở SSA nếu công ty áp mức khác.

- setup/salary_structure.py: thêm amount: 730000 cho PC_AT trong
  _earnings_for_official() → áp cho fresh install.
- patches/v0_5_0/set_pc_at_default_amount.py: idempotent patch update
  row PC_AT trên site đã deploy (chỉ đụng khi amount = 0/NULL).
- patches.txt: đăng ký patch mới.

Refs: TETRA-1328

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
DN triển khai HRMS từ 2026 không cần lịch sử mức giảm trừ gia cảnh cũ
theo NQ 954/2020. Nhất quán với greenfield-2026 đã áp dụng cho Minimum
Wage + Tax Bracket + Insurance Rate. Trang /desk/vietnam-tax-deduction
giờ chỉ còn 2 dòng: Personal 15.500.000đ + Dependent 6.200.000đ (NQ
110/2025/UBTVQH15, hiệu lực 01/01/2026).

- fixtures/__init__.py: xóa deductions_2024, chỉ seed 2026.
- doctype JSON: cập nhật description fields reflect mức hiện hành.
- patches/v0_5_0/cleanup_tax_deductions_2024.py: idempotent patch xóa
  records 2024 trên site đã deploy; safe-guard chỉ xóa khi amount
  khớp 11M/4.4M (tránh đè admin đã sửa tay).
- patches.txt: đăng ký patch mới.

Đã test trên local: patch xóa đúng 2 records, rerun idempotent,
get_personal_deduction/get_dependent_deduction trả 15.5M/6.2M.

Refs: TETRA-1326

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
tant merged commit 70dda95d65 into main 2026-04-20 19:29:31 +07:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
tant/vhrms!2
No description provided.