[CLAUDE] Workflow: State machine 5 trạng thái — Trả lại = Phase riêng
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 3m17s
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 3m17s
Session 17 spec: chốt 5 trạng thái phiếu PE/HĐ/Budget theo state diagram:
Nháp ─trình──► Đã gửi duyệt ─approve cấp cuối──► Đã duyệt (terminal)
├─ Trả lại ────────► Trả lại
└─ Từ chối ────────► Từ chối (terminal)
Trả lại ──Drafter sửa+gửi lại──► Đã gửi duyệt (chạy LẠI từ đầu)
Khác Mig 21 (Session 16): bỏ smart-reject jump-back. Trả lại = Phase
RIÊNG (TraLai=98), không revert về DangSoanThao + không jump-back step.
Drafter từ TraLai gửi lại như case Nháp — workflow chạy lại từ Cấp 1
Bước 1 (Option A diagram chốt với user).
BE Domain:
- ContractPhase + TraLai = 98
- BudgetPhase + TraLai = 98
- PurchaseEvaluationPhase: TraLai=98 đổi từ [LEGACY deprecated] thành
primary state. Comment update enum docs cho cả 3.
BE Policy (PE/HĐ/Budget):
- Reject transitions trỏ về TraLai (thay DangSoanThao)
- Mirror entry transitions: TraLai → next phase (cho Drafter resubmit)
- ActivePhases thêm TraLai
- FromDefinition mirror: TraLai → step.Phase + reject → TraLai
- DefaultSla cho TraLai = same as DangSoanThao
BE Service (PE + Contract):
- Reject branch: target=TuChoi giữ; else set Phase=TraLai, clear
CurrentWorkflowStepIndex=null
- Bỏ ResumeAfterReject branch + RejectedAtStepIndex/RejectedFromPhase
assignment (DB column giữ deprecated cho data cũ)
- Drafter trình branch: từ DangSoanThao HOẶC TraLai → ChoDuyet, init
CurrentWorkflowStepIndex=0 (cùng entry point, chạy lại từ đầu)
- Notification: TraLai when fromPhase=ChoDuyet → "bị trả lại"
- Budget Handler: simplify reject → TraLai, bỏ smart-reject + isResuming
BE Tests update:
- WorkflowPolicyTests: Standard_RejectFromCCM → TraLai (rename + assert)
+ Standard_TraLai_To_DangGopY_Allowed_For_Drafter (new)
- PurchaseEvaluationPolicyTests: BothPolicies_RejectFromCCM → TraLai
+ BothPolicies_TraLai_To_ChoPurchasing_AllowedForDrafter (new theory)
- BudgetPolicyTests: Default_CostControl_ChoCCM_To_TraLai (rename)
+ ActivePhases All6States (was All5) + NextPhasesFrom_TraLai (new)
+ NextPhasesFrom_ChoCEO_Includes_DaDuyet_And_TraLai (rename)
- 77 → 81 test pass (+4 tests TraLai entry point)
FE rename "Bản nháp" → "Nháp" (cả 2 app + types):
- types/purchaseEvaluation.ts: PurchaseEvaluationPhaseLabel 1=Nháp,
10=Đã gửi duyệt. PeDisplayStatus.BanNhap → Nhap (key + value).
PhaseLabel/Color cho TraLai update active.
- types/contracts.ts: +ChoDuyet=10, +TraLai=98 const + label/color.
Phase 2 'Đang soạn thảo' → 'Nháp'.
- types/budget.ts: +TraLai=98 const + label/color. Phase 1 → 'Nháp'.
- PeListPanel + PurchaseEvaluationsListPage filter dropdown: Nhap +
TraLai map đúng phase value.
BE label maps update consistent:
- ContractExcelExporter PhaseLabel: DangSoanThao → "Nháp" + add ChoDuyet/
TraLai entries.
- PeWorkflowAdminFeatures + WorkflowAdminFeatures PhaseLabels: same.
Verify: dotnet test 81 pass · npm build × 2 app pass · BE 0 error.
Field RejectedAtStepIndex/RejectedFromPhase giữ DB column (nullable,
không set value mới). Cleanup migration sau.
This commit is contained in:
@ -45,11 +45,11 @@ public class BudgetPolicyTests
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Default_CostControl_ChoCCM_To_DangSoanThao_Allowed()
|
||||
public void Default_CostControl_ChoCCM_To_TraLai_Allowed()
|
||||
{
|
||||
// Trả về Drafter
|
||||
// Session 17 spec: Trả lại = Phase riêng (TraLai), không revert DangSoanThao
|
||||
BudgetPolicies.Default
|
||||
.IsTransitionAllowed(BudgetPhase.ChoCCM, BudgetPhase.DangSoanThao,
|
||||
.IsTransitionAllowed(BudgetPhase.ChoCCM, BudgetPhase.TraLai,
|
||||
[AppRoles.CostControl])
|
||||
.Should().BeTrue();
|
||||
}
|
||||
@ -93,11 +93,12 @@ public class BudgetPolicyTests
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Default_ActivePhases_Includes_All5States()
|
||||
public void Default_ActivePhases_Includes_All6States()
|
||||
{
|
||||
// Session 17: thêm TraLai = Phase riêng cho Trả lại
|
||||
BudgetPolicies.Default.ActivePhases.Should().BeEquivalentTo(new[]
|
||||
{
|
||||
BudgetPhase.DangSoanThao, BudgetPhase.ChoCCM,
|
||||
BudgetPhase.DangSoanThao, BudgetPhase.TraLai, BudgetPhase.ChoCCM,
|
||||
BudgetPhase.ChoCEO, BudgetPhase.DaDuyet, BudgetPhase.TuChoi,
|
||||
});
|
||||
}
|
||||
@ -111,11 +112,21 @@ public class BudgetPolicyTests
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Default_NextPhasesFrom_ChoCEO_Includes_DaDuyet_And_DangSoanThao()
|
||||
public void Default_NextPhasesFrom_TraLai_Includes_ChoCCM_And_TuChoi()
|
||||
{
|
||||
// Drafter từ TraLai gửi lại = entry point thứ 2 (mirror DangSoanThao)
|
||||
var next = BudgetPolicies.Default.NextPhasesFrom(BudgetPhase.TraLai);
|
||||
next.Should().Contain(BudgetPhase.ChoCCM);
|
||||
next.Should().Contain(BudgetPhase.TuChoi);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Default_NextPhasesFrom_ChoCEO_Includes_DaDuyet_And_TraLai()
|
||||
{
|
||||
var next = BudgetPolicies.Default.NextPhasesFrom(BudgetPhase.ChoCEO);
|
||||
next.Should().Contain(BudgetPhase.DaDuyet);
|
||||
next.Should().Contain(BudgetPhase.DangSoanThao);
|
||||
next.Should().Contain(BudgetPhase.TraLai);
|
||||
next.Should().Contain(BudgetPhase.TuChoi);
|
||||
}
|
||||
|
||||
// SLA — chống regression khi đổi phase deadline accidentally
|
||||
|
||||
@ -90,12 +90,23 @@ public class WorkflowPolicyTests
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Standard_RejectFromCCM_GoesBackToDraft()
|
||||
public void Standard_RejectFromCCM_GoesTo_TraLai()
|
||||
{
|
||||
// Session 17 spec: Trả lại = Phase riêng (TraLai), không revert DangSoanThao
|
||||
WorkflowPolicies.Standard
|
||||
.IsTransitionAllowed(ContractPhase.DangKiemTraCCM, ContractPhase.DangSoanThao,
|
||||
.IsTransitionAllowed(ContractPhase.DangKiemTraCCM, ContractPhase.TraLai,
|
||||
[AppRoles.CostControl])
|
||||
.Should().BeTrue("CCM có quyền reject về Drafter");
|
||||
.Should().BeTrue("CCM có quyền reject về TraLai");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Standard_TraLai_To_DangGopY_Allowed_For_Drafter()
|
||||
{
|
||||
// Drafter từ TraLai gửi lại = entry point thứ 2 (mirror DangSoanThao → DangGopY)
|
||||
WorkflowPolicies.Standard
|
||||
.IsTransitionAllowed(ContractPhase.TraLai, ContractPhase.DangGopY,
|
||||
[AppRoles.Drafter])
|
||||
.Should().BeTrue("Drafter resubmit từ Trả lại");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
||||
@ -131,17 +131,33 @@ public class PurchaseEvaluationPolicyTests
|
||||
[Theory]
|
||||
[InlineData(nameof(PurchaseEvaluationPolicies.NccOnly))]
|
||||
[InlineData(nameof(PurchaseEvaluationPolicies.NccWithPlan))]
|
||||
public void BothPolicies_RejectFromCCM_GoesBackTo_DangSoanThao(string policyName)
|
||||
public void BothPolicies_RejectFromCCM_GoesTo_TraLai(string policyName)
|
||||
{
|
||||
// Session 17 spec: Trả lại = Phase riêng (TraLai), không revert DangSoanThao
|
||||
var policy = policyName == nameof(PurchaseEvaluationPolicies.NccOnly)
|
||||
? PurchaseEvaluationPolicies.NccOnly
|
||||
: PurchaseEvaluationPolicies.NccWithPlan;
|
||||
|
||||
policy.IsTransitionAllowed(PurchaseEvaluationPhase.ChoCCM, PurchaseEvaluationPhase.DangSoanThao,
|
||||
policy.IsTransitionAllowed(PurchaseEvaluationPhase.ChoCCM, PurchaseEvaluationPhase.TraLai,
|
||||
[AppRoles.CostControl])
|
||||
.Should().BeTrue();
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(nameof(PurchaseEvaluationPolicies.NccOnly))]
|
||||
[InlineData(nameof(PurchaseEvaluationPolicies.NccWithPlan))]
|
||||
public void BothPolicies_TraLai_To_ChoPurchasing_AllowedForDrafter(string policyName)
|
||||
{
|
||||
// Drafter từ TraLai gửi lại = entry point thứ 2 (mirror DangSoanThao)
|
||||
var policy = policyName == nameof(PurchaseEvaluationPolicies.NccOnly)
|
||||
? PurchaseEvaluationPolicies.NccOnly
|
||||
: PurchaseEvaluationPolicies.NccWithPlan;
|
||||
|
||||
policy.IsTransitionAllowed(PurchaseEvaluationPhase.TraLai, PurchaseEvaluationPhase.ChoPurchasing,
|
||||
[AppRoles.Drafter])
|
||||
.Should().BeTrue();
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(nameof(PurchaseEvaluationPolicies.NccOnly))]
|
||||
[InlineData(nameof(PurchaseEvaluationPolicies.NccWithPlan))]
|
||||
|
||||
Reference in New Issue
Block a user