fix(hrms): TETRA-1653 PC_DT/PC_BB miễn TNCN + PC_LAN đóng BHXH theo Luật 109/2025 + 41/2024 #130

Merged
kienta merged 3 commits from kienta/vhrms:fix/TETRA-1653-pc-dt-bb-lan-flags into develop 2026-05-06 19:11:16 +07:00
Collaborator

Summary

3 Salary Component bị seed sai cờ TNCN/BHXH so với pháp lý hiện hành (audit master data sau khi fix PC_LAN TNCN ở TETRA-1645):

  • PC_DT (Phụ cấp điện thoại) is_tax_applicable=1 — sai. Phụ cấp khoán điện thoại theo quy chế DN miễn TNCN trong định mức (Luật 109/2025 + TT 111/2013 Đ.2.2.đ.4).
  • PC_BB (Phụ cấp bờ-biển) is_tax_applicable=1 — sai. DN trả theo hệ số phụ cấp khu vực NĐ 76/2019/NĐ-CP cho địa bàn ven biển → nhóm phụ cấp khu vực miễn TNCN tại Luật 109/2025/QH15 Đ.3.2.b.
  • PC_LAN (Phụ cấp lặn) custom_is_pay_insurance=0 — sai. Lặn là CV đặc thù theo TT 11/2020 → diện đóng BHXH theo Luật BHXH 41/2024 Đ.31 (eff 2026, override CV 1198 cũ).

Refs: TETRA-1653

Changes

  • setup/salary_components.py: flip cờ master data + update descriptions; extend backfill TNCN abbr set ({OT_*, PC_LAN} → thêm PC_BB, PC_DT); thêm backfill BHXH block mới flip custom_is_pay_insurance 0→1 cho PC_LAN (logic ngược chiều, dùng set để dễ extend).
  • patches/v0_8_0/seed_missing_allowance_welfare_components.py: flip seed PC_BB tax + PC_LAN BHXH cho fresh install + cập nhật header docstring.
  • tests/test_seed_components.py: cập nhật EXPECTED_COMPONENTS + tách test_og_no_insurance thành test_pc_lan_pays_insurance (Luật 41/2024) + test_pc_bb_no_insurance (regression guard).
  • tests/test_payroll_deductions.py: mock _TAX_FLAGS["Phụ cấp điện thoại"] 1→0 + recompute expected PIT base 34,318,182 → 33,818,182 (PC_DT 500k chuyển taxable → exempt).

Test plan

  • python3 -m unittest vhrms.vhrms.tests.test_seed_components — pass (per-module)
  • python3 -m unittest vhrms.vhrms.tests.test_payroll_deductions — pass (per-module)
  • Verify post-merge trên staging:
    • frappe.db.get_value("Salary Component", "Phụ cấp điện thoại", "is_tax_applicable") == 0
    • frappe.db.get_value("Salary Component", "Phụ cấp bờ-biển", "is_tax_applicable") == 0
    • frappe.db.get_value("Salary Component", "Phụ cấp lặn", "custom_is_pay_insurance") == 1

Note: Khi chạy combined python3 -m unittest test_seed_components test_payroll_deductions có 11 false-failures ở test_strips_missing_columns do pre-existing test stub collision — cũng repro trên develop, không phải regression của PR này. Tracked: TETRA-1655.

Out of scope

  • Phần vượt trần (PC_DT định mức quy chế, PC_AT 730k/tháng, PC_DH_M1..M4 5–15% LCB) — sẽ tạo follow-up khi cần model cap/formula riêng.
  • PC_BB BHXH: nếu C&B sau xác nhận trả cố định gắn vị trí làm việc → cần custom_is_pay_insurance=1 theo Luật 41/2024 Đ.31, ticket riêng.
  • payroll/vietnam_calculations.py & pit_calculator.py & BHXH calculator — đọc cờ từ DB, không cần sửa logic.
  • Patches v0_5_0 (lịch sử) không sửa retroactively — backfill ở setup file đảm nhận migration.

🤖 Generated with Claude Code

## Summary 3 Salary Component bị seed sai cờ TNCN/BHXH so với pháp lý hiện hành (audit master data sau khi fix PC_LAN TNCN ở TETRA-1645): - **PC_DT** (Phụ cấp điện thoại) `is_tax_applicable=1` — sai. Phụ cấp khoán điện thoại theo quy chế DN miễn TNCN trong định mức (Luật 109/2025 + TT 111/2013 Đ.2.2.đ.4). - **PC_BB** (Phụ cấp bờ-biển) `is_tax_applicable=1` — sai. DN trả theo hệ số phụ cấp khu vực NĐ 76/2019/NĐ-CP cho địa bàn ven biển → nhóm phụ cấp khu vực miễn TNCN tại Luật 109/2025/QH15 Đ.3.2.b. - **PC_LAN** (Phụ cấp lặn) `custom_is_pay_insurance=0` — sai. Lặn là CV đặc thù theo TT 11/2020 → diện đóng BHXH theo Luật BHXH 41/2024 Đ.31 (eff 2026, override CV 1198 cũ). Refs: TETRA-1653 ## Changes - **`setup/salary_components.py`**: flip cờ master data + update descriptions; extend backfill TNCN abbr set (`{OT_*, PC_LAN}` → thêm `PC_BB`, `PC_DT`); thêm backfill BHXH block mới flip `custom_is_pay_insurance` 0→1 cho PC_LAN (logic ngược chiều, dùng set để dễ extend). - **`patches/v0_8_0/seed_missing_allowance_welfare_components.py`**: flip seed PC_BB tax + PC_LAN BHXH cho fresh install + cập nhật header docstring. - **`tests/test_seed_components.py`**: cập nhật `EXPECTED_COMPONENTS` + tách `test_og_no_insurance` thành `test_pc_lan_pays_insurance` (Luật 41/2024) + `test_pc_bb_no_insurance` (regression guard). - **`tests/test_payroll_deductions.py`**: mock `_TAX_FLAGS["Phụ cấp điện thoại"]` 1→0 + recompute expected PIT base 34,318,182 → 33,818,182 (PC_DT 500k chuyển taxable → exempt). ## Test plan - [x] `python3 -m unittest vhrms.vhrms.tests.test_seed_components` — pass (per-module) - [x] `python3 -m unittest vhrms.vhrms.tests.test_payroll_deductions` — pass (per-module) - [ ] Verify post-merge trên staging: - `frappe.db.get_value("Salary Component", "Phụ cấp điện thoại", "is_tax_applicable")` == 0 - `frappe.db.get_value("Salary Component", "Phụ cấp bờ-biển", "is_tax_applicable")` == 0 - `frappe.db.get_value("Salary Component", "Phụ cấp lặn", "custom_is_pay_insurance")` == 1 > **Note:** Khi chạy combined `python3 -m unittest test_seed_components test_payroll_deductions` có 11 false-failures ở `test_strips_missing_columns` do **pre-existing test stub collision** — cũng repro trên `develop`, không phải regression của PR này. Tracked: TETRA-1655. ## Out of scope - Phần vượt trần (PC_DT định mức quy chế, PC_AT 730k/tháng, PC_DH_M1..M4 5–15% LCB) — sẽ tạo follow-up khi cần model cap/formula riêng. - PC_BB BHXH: nếu C&B sau xác nhận trả cố định gắn vị trí làm việc → cần `custom_is_pay_insurance=1` theo Luật 41/2024 Đ.31, ticket riêng. - `payroll/vietnam_calculations.py` & `pit_calculator.py` & BHXH calculator — đọc cờ từ DB, không cần sửa logic. - Patches v0_5_0 (lịch sử) không sửa retroactively — backfill ở setup file đảm nhận migration. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
3 Salary Component bị seed sai cờ TNCN/BHXH so với pháp lý hiện hành
(audit master data sau khi fix PC_LAN TNCN ở TETRA-1645):

- PC_DT (Phụ cấp điện thoại) `is_tax_applicable=1` — sai vì phụ cấp
  khoán điện thoại theo quy chế DN miễn TNCN trong định mức (Luật
  109/2025 + TT 111/2013 Đ.2.2.đ.4).
- PC_BB (Phụ cấp bờ-biển) `is_tax_applicable=1` — sai vì DN trả theo
  hệ số phụ cấp khu vực NĐ 76/2019/NĐ-CP cho địa bàn ven biển, thuộc
  nhóm phụ cấp khu vực miễn TNCN tại Luật 109/2025/QH15 Đ.3.2.b
  (C&B confirmed căn cứ NĐ 76/2019).
- PC_LAN (Phụ cấp lặn) `custom_is_pay_insurance=0` — sai vì lặn là CV
  đặc thù theo TT 11/2020, thuộc diện đóng BHXH theo Luật BHXH 41/2024
  Đ.31 (eff 2026, override CV 1198 cũ).

Cả 3 đều sai ngay trong happy path (DN cấp trong khung pháp lý), không
phải edge case vượt trần. Phần vượt trần (PC_DT định mức quy chế,
PC_AT 730k/tháng, PC_DH_M1..M4 5–15% LCB) ngoài scope — sẽ tạo
follow-up tickets khi cần model cap/formula riêng.

Thay đổi:
- setup/salary_components.py: flip cờ master data + update descriptions
  + extend backfill block TNCN abbr set ({OT_*, PC_LAN} → thêm PC_BB,
  PC_DT) + thêm block backfill BHXH mới flip custom_is_pay_insurance
  0→1 cho PC_LAN (logic ngược chiều, dùng set để dễ extend nếu PC_BB
  BHXH cần follow-up).
- patches/v0_8_0/seed_missing_allowance_welfare_components.py: flip
  seed value PC_BB tax + PC_LAN BHXH cho fresh install ra đúng từ đầu
  + cập nhật header docstring.
- tests/test_seed_components.py: EXPECTED_COMPONENTS table + tách
  test_og_no_insurance thành test_pc_lan_pays_insurance (assert =1,
  viện dẫn Luật 41/2024) + test_pc_bb_no_insurance (assert =0, regression
  guard ngăn drift).
- tests/test_payroll_deductions.py: mock _TAX_FLAGS["Phụ cấp điện
  thoại"] 1→0 (mirror DB state) + recompute expected PIT base
  34_318_182 → 33_818_182 trong scenario test (PC_DT 500k chuyển từ
  taxable → exempt) + cập nhật docstring/comments.

PIT calculator + BHXH calculator đọc cờ thẳng từ DB → không cần sửa
logic. Patches v0_5_0 (lịch sử) không sửa retroactively — backfill
block ở setup file đảm nhận migration. PC_BB BHXH note: nếu C&B sau
xác nhận trả cố định gắn vị trí làm việc → cần custom_is_pay_insurance=1
theo Luật 41/2024 Đ.31, ticket riêng.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Author
Collaborator

Code Review — TETRA-1653

Verdict: APPROVED

Quality cao, tuân pattern TETRA-1264/TETRA-1645, tests verify math chắc chắn.

Strengths

  • Pattern consistency — TNCN backfill abbr set mở rộng đúng: {OT_*, PC_LAN, PC_BB, PC_DT} (line 643). Comment trace cả 4 ticket nguồn.
  • BHXH backfill block mới (line 661–678) — logic rõ ràng, dùng set để dễ extend dù hiện chỉ có 1 element (PC_LAN). Forward-looking comment đề cập case PC_BB nếu C&B confirm sau.
  • Test math kiểm chứng tốt: PIT base 34,318,182 → 33,818,182 = đúng -500k (PC_DT chuyển exempt). Cả 2 approach (gross−exempt và Σ taxable) đều update chéo, đảm bảo invariant.
  • Test split hợp lý: test_og_no_insurance (PC_LAN+PC_BB chung) → tách test_pc_lan_pays_insurance (=1, Luật 41/2024) + test_pc_bb_no_insurance (=0). Tránh ambiguity sau khi PC_LAN đảo chiều.
  • Description cite cả nguồn pháp lý + assumption kinh doanh — vd PC_BB: "C&B đã xác nhận DN trả theo NĐ 76/2019" → audit trail tốt.

Findings

# Severity File Issue
1 🟡 Major vhrms/vhrms/setup/salary_components.py:213-220 Comment cảnh báo "Phần vượt định mức quy chế chịu thuế (chưa model trên Frappe — needs cap)" — nhưng is_tax_applicable=0 sẽ miễn thuế PC_DT toàn bộ bất kể số tiền. Nếu admin nhập PC_DT > định mức DN, hệ thống sẽ silently miss tax. Đã acknowledge out-of-scope, OK; flag cho QA verify scenarios.
2 🟢 Minor vhrms/vhrms/setup/salary_components.py:670 BHXH migration flip chỉ check current_bhxh == 0. Nếu DB value là None (column mới thêm, record cũ chưa init), điều kiện False → không flip. Same pattern với TNCN flip (line 645) → parity acceptable. Có thể normalize giống ensure_law_109_pit_exemption (line 727-732, coerce None→0).
3 🟢 Minor (test infra) Không có test mới cho BHXH migration block (line 661-678) — gap tương tự TETRA-1645 cho TNCN block. Static-check trong test_pit_exemption_law_109_2025.py có thể extend cover BHXH side. Out of scope cho ticket này.

Suggestions

  • Verify scenario PC_BB: claim "DN trả theo NĐ 76/2019/NĐ-CP cho địa bàn ven biển" là business assumption. NĐ 76/2019 list các xã/huyện cụ thể "có điều kiện kinh tế-xã hội đặc biệt khó khăn". QA/C&B nên confirm các site ngoài khơi/giàn khoan của DN nằm trong danh sách đó.
  • Follow-up cap modeling: PC_DT (định mức quy chế DN), PC_AT (730k/tháng TT 26/2016), PC_DH_M1..M4 (5-15% LCB) — cùng class "miễn trong khung, chịu trên trần". Có thể tạo Epic cho cap/formula model.

Blockers

None.

Next Step

Có thể merge. Findings #2/#3 nên defer; #1 cần QA aware về behavior chính xác.

Known issues (out of scope)

  • Pre-existing test stub collision khi run combined test files → tracked: TETRA-1655. Không phải regression của PR này.
# Code Review — TETRA-1653 ## Verdict: ✅ APPROVED Quality cao, tuân pattern TETRA-1264/TETRA-1645, tests verify math chắc chắn. ## Strengths - **Pattern consistency** — TNCN backfill abbr set mở rộng đúng: `{OT_*, PC_LAN, PC_BB, PC_DT}` (line 643). Comment trace cả 4 ticket nguồn. - **BHXH backfill block mới** (line 661–678) — logic rõ ràng, dùng `set` để dễ extend dù hiện chỉ có 1 element (PC_LAN). Forward-looking comment đề cập case PC_BB nếu C&B confirm sau. - **Test math kiểm chứng tốt**: PIT base 34,318,182 → 33,818,182 = đúng -500k (PC_DT chuyển exempt). Cả 2 approach (gross−exempt và Σ taxable) đều update chéo, đảm bảo invariant. - **Test split hợp lý**: `test_og_no_insurance` (PC_LAN+PC_BB chung) → tách `test_pc_lan_pays_insurance` (=1, Luật 41/2024) + `test_pc_bb_no_insurance` (=0). Tránh ambiguity sau khi PC_LAN đảo chiều. - **Description cite cả nguồn pháp lý + assumption kinh doanh** — vd PC_BB: "C&B đã xác nhận DN trả theo NĐ 76/2019" → audit trail tốt. ## Findings | # | Severity | File | Issue | |---|----------|------|-------| | 1 | 🟡 Major | `vhrms/vhrms/setup/salary_components.py:213-220` | Comment cảnh báo "Phần vượt định mức quy chế chịu thuế (chưa model trên Frappe — needs cap)" — nhưng `is_tax_applicable=0` sẽ miễn thuế PC_DT **toàn bộ** bất kể số tiền. Nếu admin nhập PC_DT > định mức DN, hệ thống sẽ silently miss tax. Đã acknowledge out-of-scope, OK; flag cho QA verify scenarios. | | 2 | 🟢 Minor | `vhrms/vhrms/setup/salary_components.py:670` | BHXH migration flip chỉ check `current_bhxh == 0`. Nếu DB value là `None` (column mới thêm, record cũ chưa init), điều kiện False → không flip. Same pattern với TNCN flip (line 645) → parity acceptable. Có thể normalize giống `ensure_law_109_pit_exemption` (line 727-732, coerce None→0). | | 3 | 🟢 Minor | (test infra) | Không có test mới cho BHXH migration block (line 661-678) — gap tương tự TETRA-1645 cho TNCN block. Static-check trong `test_pit_exemption_law_109_2025.py` có thể extend cover BHXH side. Out of scope cho ticket này. | ## Suggestions * **Verify scenario PC_BB**: claim "DN trả theo NĐ 76/2019/NĐ-CP cho địa bàn ven biển" là business assumption. NĐ 76/2019 list các xã/huyện cụ thể "có điều kiện kinh tế-xã hội đặc biệt khó khăn". QA/C&B nên confirm các site ngoài khơi/giàn khoan của DN nằm trong danh sách đó. * **Follow-up cap modeling**: PC_DT (định mức quy chế DN), PC_AT (730k/tháng TT 26/2016), PC_DH_M1..M4 (5-15% LCB) — cùng class "miễn trong khung, chịu trên trần". Có thể tạo Epic cho cap/formula model. ## Blockers None. ## Next Step Có thể merge. Findings #2/#3 nên defer; #1 cần QA aware về behavior chính xác. ## Known issues (out of scope) - Pre-existing test stub collision khi run combined test files → tracked: TETRA-1655. Không phải regression của PR này.
kienta merged commit 6c8e500174 into develop 2026-05-06 19:11:16 +07:00
kienta deleted branch fix/TETRA-1653-pc-dt-bb-lan-flags 2026-05-06 19:12:13 +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!130
No description provided.