[CLAUDE] Workflow: LeaveBalance business logic — trừ phép khi duyệt + số dư (Phase 11 P11-B)
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 4m8s

Số dư phép theo (User × LeaveType × Year) + trừ tự động khi đơn nghỉ duyệt cuối.
Policy: cho phép vượt số dư (âm) + cảnh báo (anh main chốt), tích hợp vào trang đơn nghỉ.

Schema (Mig 42 AddLeaveBalances — pure additive, 1 bảng):
- LeaveBalance: UserId + LeaveTypeId + Year + EntitledDays + UsedDays + AdjustmentDays.
  UNIQUE (UserId,LeaveTypeId,Year), FK LeaveType Restrict, decimal(5,2).
  Remaining = Entitled + Adjustment − Used (computed, không store).

Deduction hook (ApproveLeaveRequestHandler nhánh terminal DaDuyet — exactly-once):
- Upsert LeaveBalance(RequesterUserId, LeaveTypeId, StartDate.Year), auto-create từ
  LeaveType.DaysPerYear, UsedDays += NumDays. Guard Status!=DaGuiDuyet chặn re-approve.

FK invariant guard (em main thêm sau test reveal FK risk):
- Create + UpdateDraft validate LeaveTypeId tồn tại (AnyAsync) → ConflictException.
  Đóng cửa vào — bogus type không thể tới deduction FK insert (tránh 500 kẹt đơn).

CQRS LeaveBalanceFeatures.cs: GetMy (self, lazy merge active LeaveType) + GetUser (admin)
  + AdjustLeaveBalance (admin upsert carry-over). Controller [Authorize] + admin Roles=Admin.
Embed: GetLeaveRequestByIdHandler trả balance NGƯỜI TẠO (approver xem thấy đúng).
FE: WorkflowAppDetailPage ×2 — block "Số dư phép" + cảnh báo vượt khi kind=leave (SHA256 identical).

Tests (+11, 130→154 PASS): deduction single/multi-level/accumulate/negative-allowed/
  reject-return-no-deduct + lazy-merge + adjust upsert + Create guard bogus→Conflict.
  Cũng repair 2 test S42 terminal FK-fail (template BuildLeave +seed LeaveType).

Verify: build 0 error · 154 test · FE ×2 · reviewer Max PASS (deduction exactly-once +
  FK invariant fully closed, 2 minor concurrency/comment defer).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
pqhuy1987
2026-05-30 11:10:44 +07:00
parent 0db5e1fdc9
commit 82d7fcff4d
21 changed files with 7356 additions and 10 deletions

View File

@ -0,0 +1,27 @@
using SolutionErp.Domain.Common;
namespace SolutionErp.Domain.Hrm;
// Phase 11 P11-B Wave 1 (Mig 42 — S43 2026-05-30) — Số dư phép theo năm.
// Track quota phép từng NV × LoạiPhép × Năm. Trừ phép tự động khi đơn nghỉ
// phép duyệt cuối (ApproveLeaveRequestHandler terminal branch UPSERT UsedDays).
//
// Remaining = EntitledDays + AdjustmentDays UsedDays (COMPUTED ở DTO, KHÔNG store).
// UNIQUE (UserId, LeaveTypeId, Year) — 1 row mỗi NV mỗi loại mỗi năm.
public class LeaveBalance : AuditableEntity
{
public Guid UserId { get; set; }
public Guid LeaveTypeId { get; set; }
public int Year { get; set; }
// Phân bổ năm — mặc định lấy từ LeaveType.DaysPerYear lúc tạo row.
public decimal EntitledDays { get; set; }
// Đã dùng — cộng dồn NumDays mỗi đơn nghỉ phép duyệt cuối.
public decimal UsedDays { get; set; }
// Admin carry-over / điều chỉnh (dồn phép năm trước, thưởng phép...). Default 0.
public decimal AdjustmentDays { get; set; }
public LeaveType? LeaveType { get; set; }
}