[CLAUDE] PurchaseEvaluation: demo seed 4 phieu + MaPhieu atomic sequence + Pe_* perm defaults

Polish session tiep cua PE module skeleton (commit 2c6f0ca..3990066):
3 task A (MISSING in MVP) khac STATUS.md In Progress:

1. Demo PE data seed (SeedDemoPurchaseEvaluationsAsync)
   - 4 phieu varied A/B x phase: A-001 DangSoanThao (mo), A-002
     ChoCEODuyetNCC (winner+9 quotes), A-003 DaDuyet (chua tao HD,
     PaymentTerms JSON), B-001 ChoDuAn (5-step giua chung).
   - Idempotent: skip-if-[DEMO]-exists.
   - Approval history dung policy A (3-step) hoac B (5-step).

2. MaPhieu atomic sequence — Migration 13
   - Format PE/{YYYY}/{TypeLetter}/{Seq:D3} (vd PE/2026/A/001).
   - PurchaseEvaluationCodeSequence entity (Prefix PK).
   - IPurchaseEvaluationCodeGenerator + impl SERIALIZABLE
     transaction (mirror ContractCodeGenerator 1:1).
   - Replace Random.Shared trong CreatePurchaseEvaluationCommandHandler.
   - Migration AddPurchaseEvaluationCodeSequences (1 bang).

3. Pe_* permission defaults
   - SeedPurchaseEvaluationPermissionDefaultsAsync — 7 role business x 9 menu key.
   - Drafter/DeptManager/Procurement: R+C+U; CostControl/PM/Director/AuthorizedSigner: R+U.
   - DeptManager them Delete (xoa nhap).
   - Idempotent per-(roleId x menuKey).

Build: 0 error, 2 warning (pre-existing DocxRenderer).

Files: 4 new + 8 modified (1 migration + entity + generator + DI + 2 ctx + 2 features).

Resolves: STATUS.md In Progress §A — 3 item PE MISSING.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
pqhuy1987
2026-04-24 10:41:17 +07:00
parent 0048a2e83a
commit c48ac2116d
16 changed files with 3621 additions and 24 deletions

View File

@ -56,6 +56,7 @@ public interface IApplicationDbContext
DbSet<PurchaseEvaluationWorkflowDefinition> PurchaseEvaluationWorkflowDefinitions { get; }
DbSet<PurchaseEvaluationWorkflowStep> PurchaseEvaluationWorkflowSteps { get; }
DbSet<PurchaseEvaluationWorkflowStepApprover> PurchaseEvaluationWorkflowStepApprovers { get; }
DbSet<PurchaseEvaluationCodeSequence> PurchaseEvaluationCodeSequences { get; }
Task<int> SaveChangesAsync(CancellationToken cancellationToken = default);
}

View File

@ -39,7 +39,8 @@ public class CreatePurchaseEvaluationCommandValidator : AbstractValidator<Create
public class CreatePurchaseEvaluationCommandHandler(
IApplicationDbContext db,
ICurrentUser currentUser,
IPurchaseEvaluationWorkflowService workflow) : IRequestHandler<CreatePurchaseEvaluationCommand, Guid>
IPurchaseEvaluationWorkflowService workflow,
IPurchaseEvaluationCodeGenerator codeGen) : IRequestHandler<CreatePurchaseEvaluationCommand, Guid>
{
public async Task<Guid> Handle(CreatePurchaseEvaluationCommand request, CancellationToken ct)
{
@ -67,8 +68,9 @@ public class CreatePurchaseEvaluationCommandHandler(
workflow.GetPhaseSla(PurchaseEvaluationPhase.DangSoanThao) ?? TimeSpan.FromDays(3)),
};
// Auto-gen MaPhieu đơn giản PE-YYYYMM-XXXX (4 digit random) — format sau
entity.MaPhieu = $"PE-{DateTime.UtcNow:yyyyMM}-{Random.Shared.Next(1000, 9999)}";
// Atomic MaPhieu sequence — format PE/{YYYY}/{TypeLetter}/{Seq:D3}
// (mirror IContractCodeGenerator pattern, transaction SERIALIZABLE).
entity.MaPhieu = await codeGen.GenerateAsync(entity, ct);
db.PurchaseEvaluations.Add(entity);

View File

@ -18,3 +18,15 @@ public interface IPurchaseEvaluationWorkflowService
TimeSpan? GetPhaseSla(PurchaseEvaluationPhase phase);
}
// Atomic sequence generator cho mã PE (MaPhieu) — mirror IContractCodeGenerator.
// Format: PE/{YYYY}/{TypeLetter}/{Seq:D3}
// - YYYY = năm hiện tại (UTC)
// - TypeLetter = "A" (DuyetNcc) / "B" (DuyetNccPhuongAn)
// - Seq = 3 chữ số tăng dần per (year × type)
// VD: PE/2026/A/001, PE/2026/A/002, PE/2026/B/001
public interface IPurchaseEvaluationCodeGenerator
{
// Gen mã phiếu atomic. Transaction SERIALIZABLE để tránh race condition.
Task<string> GenerateAsync(PurchaseEvaluation evaluation, CancellationToken ct = default);
}