[CLAUDE] Domain+App+Infra: Plan B Chunk C — Mig 33 ContractLevelOpinions cookie-cutter mirror PE Mig 26
- Domain/Contracts/ContractLevelOpinion.cs (NEW entity mirror PE — AuditableEntity, 4 field core + 2 nav) - Domain/Contracts/Contract.cs (+LevelOpinions nav collection) - Migrations/20260522052240_AddContractLevelOpinions.cs (3-file rule: .cs + .Designer.cs + Snapshot) - Configurations/ContractLevelOpinionConfiguration.cs (NEW separate file, mirror PE pattern) - IApplicationDbContext.cs + ApplicationDbContext.cs (+DbSet<ContractLevelOpinion>) UNIQUE composite (ContractId, ApprovalWorkflowLevelId) — 1 row per HĐ × Level. FK Cascade Contract + Restrict ApprovalWorkflowLevel. SignedByUserId KHÔNG nav (denorm SignedByFullName tránh cascade khi xoá user). Mirror PE Mig 26 pattern (S19 2026-05-09) EXACT — UPSERT row khi Approver duyệt qua Service ApproveV2Async (Plan B Chunk B em main138469dđã có TODO marker). Em main sẽ add UPSERT block sau Chunk C done (Chunk D). Verify: - dotnet build PASS 0 err (2 pre-existing warn DocxRenderer unrelated) - dotnet ef database update PASS (Mig 33 applied SolutionErp_Dev + _Design) - dotnet test 111/111 PASS (58 Domain + 53 Infra — no regression) Plan B chain (6 chunks): - A158898e8✅ ContractApprovalWorkflowV2 entity scaffold - A2a85e437✅ Contract.ApprovalWorkflowId + ContractConfiguration FK - B138469d✅ ContractWorkflowService ApproveV2Async skeleton + TODO LevelOpinion UPSERT - C (this) ✅ ContractLevelOpinions entity + Mig 33 + config + DbSet - D FE Workspace V2 (Implementer, pending) - E FE Section 5 V2 (Implementer, pending) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@ -48,6 +48,12 @@ public class Contract : AuditableEntity
|
||||
public List<ContractApproval> Approvals { get; set; } = new();
|
||||
public List<ContractComment> Comments { get; set; } = new();
|
||||
public List<ContractAttachment> Attachments { get; set; } = new();
|
||||
|
||||
// Plan B Chunk C (S29 — Mig 33, 2026-05-22) — Ý kiến cấp duyệt V2 dynamic
|
||||
// cookie-cutter mirror PE Mig 26. UPSERT auto từ ApproveV2Async (Plan B
|
||||
// Chunk D em main wire). Section 5 FE render dynamic theo flow.steps[].levels[].
|
||||
// HĐ V1 (WorkflowDefinitionId) KHÔNG dùng.
|
||||
public List<ContractLevelOpinion> LevelOpinions { get; set; } = new();
|
||||
public List<ContractChangelog> Changelogs { get; set; } = new();
|
||||
public List<ContractDepartmentApproval> DepartmentApprovals { get; set; } = new();
|
||||
|
||||
|
||||
@ -0,0 +1,36 @@
|
||||
using SolutionErp.Domain.ApprovalWorkflowsV2;
|
||||
using SolutionErp.Domain.Common;
|
||||
|
||||
namespace SolutionErp.Domain.Contracts;
|
||||
|
||||
// "Ý kiến cấp duyệt" V2 cho HĐ — sign-off DYNAMIC theo workflow ApprovalWorkflowV2
|
||||
// (Mig 22-25 + Plan B Mig 32). Cookie-cutter mirror PE Mig 26
|
||||
// `PurchaseEvaluationLevelOpinion` (S19 2026-05-09).
|
||||
//
|
||||
// Mỗi row = 1 (Contract × ApprovalWorkflowLevel). Service `ApproveV2Async` sau khi
|
||||
// approve thành công Cấp hiện tại sẽ UPSERT row này (Plan B Chunk D em main wire):
|
||||
// Comment = approval.Comment ?? "(duyệt — không ý kiến)"
|
||||
// SignedAt = clock.UtcNow
|
||||
// SignedByUserId = actor.Id (NV chính chủ HOẶC Admin override)
|
||||
// SignedByFullName = actor.FullName (denorm — tránh user bị xóa/đổi tên)
|
||||
//
|
||||
// Reject (Trả lại / Từ chối) KHÔNG sync (vì không phải sign-off của level đó).
|
||||
// Khi user resubmit từ TraLai → workflow chạy lại từ Cấp 1, opinion cũ bị
|
||||
// OVERWRITE bằng UPSERT mới (latest-write-wins).
|
||||
//
|
||||
// Section 5 FE detect V2 qua `contract.approvalWorkflowId != null` → render dynamic
|
||||
// theo flow.steps[].levels[]. HĐ V1 legacy (WorkflowDefinitionId set) → fallback
|
||||
// không có ý kiến dynamic (giống PE V1 behavior).
|
||||
public class ContractLevelOpinion : AuditableEntity
|
||||
{
|
||||
public Guid ContractId { get; set; }
|
||||
public Guid ApprovalWorkflowLevelId { get; set; }
|
||||
|
||||
public string? Comment { get; set; } // ý kiến (max 2000) hoặc placeholder "(duyệt — không ý kiến)"
|
||||
public DateTime SignedAt { get; set; } // luôn có khi UPSERT (Service set khi Approve)
|
||||
public Guid SignedByUserId { get; set; } // người ký thực sự (có thể là Admin thay NV)
|
||||
public string SignedByFullName { get; set; } = string.Empty; // snapshot tên — denorm
|
||||
|
||||
public Contract? Contract { get; set; }
|
||||
public ApprovalWorkflowLevel? Level { get; set; }
|
||||
}
|
||||
Reference in New Issue
Block a user