[CLAUDE] Domain+App+Infra+Api+FE-Admin+FE-User: S37 Mig 37 enum + Plan G-O3 Đề xuất full-stack
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 3m53s
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 3m53s
Phase 10.3 G-O3 Đề xuất (Proposal) — Mig 37 enum extend +5 values + Mig 38 Proposal schema + BE CQRS 8 endpoint + FE 2 app SHA256 IDENTICAL. Mig 37 (em main solo): extend ApprovalWorkflowApplicableType enum +5 values ProposalGeneral=4 / LeaveRequest=5 / OtRequest=6 / VehicleBooking=7 / ItTicket=8 cookie-cutter Mig 22 pattern (Up/Down empty — enum mức Domain). Mig 38 (em main solo): 4 entity Proposal (Code DX/YYYY/NNN) + ProposalAttachment + ProposalLevelOpinion (UNIQUE composite PEId+LevelId mirror PE Mig 26) + ProposalCodeSequence (Prefix PK atomic seq). 4 EF Config + 2 DbContext mod. BE CQRS (em main solo ~700 LOC ProposalFeatures.cs sau Implementer truncate phase exploration gotcha #53 5th + 529 Overload): - 4 Header handler (List paged + GetById detail + Create + UpdateDraft owner-OR-admin) - 4 Workflow handler (Submit gen MaDeXuat atomic + Approve UPSERT LevelOpinion advance + Reject + Return) - SERIALIZABLE transaction CodeGen - DTOs nested LevelOpinion với Step+Level metadata JOIN ProposalsController 8 endpoint /api/proposals (List/GetById/Create/Update/Submit/Approve/Reject/Return) class-level [Authorize] + handler-level owner-OR-admin guard. DbInitializer: SeedSampleProposalWorkflowV2Async ~40 LOC seed QT-DX-V2-001 IsUserSelectable=true NOT gated DemoSeed per gotcha #51. SeedMenuTreeAsync +4 row (Off_DeXuat sub-group + 3 leaf). FE 2 app (em main solo + Implementer 529 fail fallback): - types/proposal.ts × 2 SHA256 IDENTICAL 95607052ff1138f2 - ProposalsListPage.tsx × 2 IDENTICAL 603f0d9cf74cd09a — table 6 cột + Status badge + filter - ProposalCreatePage.tsx × 2 IDENTICAL 6aed3a76563dd576 — Form Header card - ProposalDetailPage.tsx × 2 IDENTICAL 3dc229ea8dcc9bc0 — 3 Section + WorkflowActions - Pattern 16-bis 8× cumulative (App.tsx + menuKeys + Layout staticMap 3 entry) Verify: - dotnet build PASS 0 error 2 warning pre-existing DocxRenderer - dotnet test 130/130 PASS baseline preserve - npm build × 2 PASS (fe-admin 14.72s + fe-user 6.40s) - SHA256 verify 4 file × 2 app all IDENTICAL Pattern reinforced cumulative S37: - Pattern 12-bis cross-module mirror 11× (PE V2 → Proposal V2 ApproveV2) - Pattern 16-bis 4-place mirror cross-app 8× - gotcha #53 5th occurrence Implementer mid-exploration truncation + 529 Overload 1× — em main solo fallback proven Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@ -47,6 +47,14 @@ public enum ApprovalWorkflowApplicableType
|
||||
DuyetNcc = 1, // PE module — Duyệt NCC (default test target)
|
||||
DuyetNccPhuongAn = 2, // PE — Duyệt NCC + Giải pháp
|
||||
Contract = 3, // HĐ general (any ContractType)
|
||||
|
||||
// Phase 10.3 Workflow Apps (S37 — Mig 37 enum extend). Cookie-cutter Mig 22 V2 pattern.
|
||||
// Mỗi module có 1 workflow per ApplicableType, admin Designer config + pin per draft.
|
||||
ProposalGeneral = 4, // G-O3 — Đề xuất (Proposal)
|
||||
LeaveRequest = 5, // G-O4 — Đơn nghỉ phép
|
||||
OtRequest = 6, // G-O4 — Đơn OT
|
||||
VehicleBooking = 7, // G-O5 — Đặt xe công
|
||||
ItTicket = 8, // G-O6 — Ticket CNTT
|
||||
}
|
||||
|
||||
// Bước = Phòng. 1 quy trình có nhiều bước theo Order.
|
||||
|
||||
@ -105,6 +105,12 @@ public static class MenuKeys
|
||||
public const string OffPhongHopManage = "Off_PhongHop_Manage"; // Quản lý phòng (Admin CRUD MeetingRoom)
|
||||
public const string OffPhongHopBook = "Off_PhongHop_Book"; // Đặt phòng (Create/Update/Cancel Booking)
|
||||
|
||||
// Phase 10.3 G-O3 (Mig 38 — S37 2026-05-28) — Đề xuất (Proposal) workflow V2.
|
||||
public const string OffDeXuat = "Off_DeXuat"; // sub-group Đề xuất
|
||||
public const string OffDeXuatList = "Off_DeXuat_List"; // Danh sách đề xuất
|
||||
public const string OffDeXuatCreate = "Off_DeXuat_Create"; // Tạo đề xuất mới
|
||||
public const string OffDeXuatInbox = "Off_DeXuat_Inbox"; // Inbox phê duyệt
|
||||
|
||||
public static readonly string[] PurchaseEvaluationTypeCodes =
|
||||
["DuyetNcc", "DuyetNccPhuongAn"];
|
||||
|
||||
@ -133,6 +139,7 @@ public static class MenuKeys
|
||||
HrmConfig, HrmConfigLeaveTypes, HrmConfigHolidays, HrmConfigShifts, HrmConfigOtPolicies, // Mig 35 — Phase 10.2 G-H2
|
||||
Off, OffDanhBa, // Phase 10.2 G-O1 — Văn phòng số
|
||||
OffPhongHop, OffPhongHopView, OffPhongHopManage, OffPhongHopBook, // Phase 10.2 G-O2 — Phòng họp
|
||||
OffDeXuat, OffDeXuatList, OffDeXuatCreate, OffDeXuatInbox, // Phase 10.3 G-O3 — Đề xuất
|
||||
System, Users, Roles, Permissions, MenuVisibility, Workflows, PeWorkflows,
|
||||
ApprovalWorkflowsV2, ApprovalWorkflowDuyetNccV2, ApprovalWorkflowDuyetNccPhuongAnV2, // Mig 22
|
||||
];
|
||||
|
||||
@ -10,3 +10,15 @@ public enum MeetingBookingStatus
|
||||
Cancelled = 2, // Đã huỷ
|
||||
Completed = 3, // Đã kết thúc (auto-set khi EndAt < Now via job/manual)
|
||||
}
|
||||
|
||||
// Phase 10.3 G-O3 (Mig 38 — S37 2026-05-28) — Trạng thái Đề xuất (Proposal).
|
||||
// 5-state mirror PE/Contract pattern (Nháp / Đã gửi duyệt / Trả lại / Từ chối / Đã duyệt).
|
||||
// Workflow V2 dynamic theo ApprovalWorkflow.ApplicableType=ProposalGeneral=4.
|
||||
public enum ProposalStatus
|
||||
{
|
||||
Nhap = 1, // Drafter đang soạn, chưa gửi duyệt
|
||||
DaGuiDuyet = 2, // Trong workflow approve flow (CurrentApprovalLevelOrder)
|
||||
TraLai = 3, // Approver trả về Drafter sửa → resubmit lại từ Cấp 1
|
||||
TuChoi = 4, // Terminal — không thể edit/resubmit
|
||||
DaDuyet = 5, // Terminal — workflow complete tất cả Cấp
|
||||
}
|
||||
|
||||
38
src/Backend/SolutionErp.Domain/Office/Proposal.cs
Normal file
38
src/Backend/SolutionErp.Domain/Office/Proposal.cs
Normal file
@ -0,0 +1,38 @@
|
||||
using SolutionErp.Domain.Common;
|
||||
|
||||
namespace SolutionErp.Domain.Office;
|
||||
|
||||
// Phase 10.3 G-O3 (Mig 38 — S37 2026-05-28) — Đề xuất (Proposal) aggregate root.
|
||||
// Mirror PE Plan B Mig 22-26 pattern cookie-cutter: Workflow V2 dynamic (ApplicableType=4) +
|
||||
// LevelOpinions UPSERT auto via ApproveV2Async + atomic CodeGen DX/YYYY/NNN.
|
||||
// Reference: PurchaseEvaluation.cs (PE module flagship V2 mirror source).
|
||||
public class Proposal : AuditableEntity
|
||||
{
|
||||
public string? MaDeXuat { get; set; } // Auto-gen "DX/YYYY/NNN" qua ProposalCodeSequence
|
||||
|
||||
public string Title { get; set; } = string.Empty; // "Đề xuất mua máy tính", "Đề xuất tăng ngân sách"
|
||||
public string? Description { get; set; } // Nội dung chi tiết (max 5000)
|
||||
|
||||
// Tổng số tiền dự kiến (đ). Optional — có đề xuất không số tiền (vd policy).
|
||||
public decimal? AmountEstimate { get; set; }
|
||||
|
||||
public ProposalStatus Status { get; set; } = ProposalStatus.Nhap;
|
||||
|
||||
public Guid? DepartmentId { get; set; } // Phòng ban đề xuất (org filter)
|
||||
public Guid DrafterUserId { get; set; } // User tạo đề xuất
|
||||
|
||||
// Workflow V2 pin (cookie-cutter PE Mig 22-23-24).
|
||||
// ApplicableType phải = ProposalGeneral=4 (Mig 37 enum extend).
|
||||
public Guid? ApprovalWorkflowId { get; set; }
|
||||
public int? CurrentApprovalLevelOrder { get; set; } // Cấp đang chờ (1-based) khi DaGuiDuyet
|
||||
|
||||
// SLA (mirror PE)
|
||||
public DateTime? SlaDeadline { get; set; }
|
||||
public bool SlaWarningSent { get; set; }
|
||||
|
||||
// Smart reject (mirror PE Mig 16)
|
||||
public ProposalStatus? RejectedFromStatus { get; set; }
|
||||
|
||||
public List<ProposalAttachment> Attachments { get; set; } = new();
|
||||
public List<ProposalLevelOpinion> LevelOpinions { get; set; } = new();
|
||||
}
|
||||
20
src/Backend/SolutionErp.Domain/Office/ProposalAttachment.cs
Normal file
20
src/Backend/SolutionErp.Domain/Office/ProposalAttachment.cs
Normal file
@ -0,0 +1,20 @@
|
||||
using SolutionErp.Domain.Common;
|
||||
|
||||
namespace SolutionErp.Domain.Office;
|
||||
|
||||
// Phase 10.3 G-O3 (Mig 38 — S37) — Attachment cho Đề xuất.
|
||||
// FK Cascade Proposal (xoá Đề xuất → wipe Attachments).
|
||||
// Mirror PurchaseEvaluationAttachment pattern.
|
||||
public class ProposalAttachment : AuditableEntity
|
||||
{
|
||||
public Guid ProposalId { get; set; }
|
||||
public Proposal Proposal { get; set; } = null!;
|
||||
|
||||
public string FileName { get; set; } = string.Empty; // Original file name display
|
||||
public string FilePath { get; set; } = string.Empty; // Server file path / blob URL
|
||||
public long FileSize { get; set; } // Bytes
|
||||
public string? MimeType { get; set; } // "application/pdf", "image/png"
|
||||
|
||||
public Guid UploadedByUserId { get; set; } // Drafter or approver tải lên
|
||||
public string UploadedByFullName { get; set; } = string.Empty; // denorm
|
||||
}
|
||||
@ -0,0 +1,13 @@
|
||||
namespace SolutionErp.Domain.Office;
|
||||
|
||||
// Phase 10.3 G-O3 (Mig 38 — S37) — Sequence generator cho mã Đề xuất (MaDeXuat).
|
||||
// Mirror PurchaseEvaluationCodeSequence pattern (Prefix string PK + LastSeq atomic).
|
||||
// Format: "DX/YYYY/NNN" — vd "DX/2026/001" → "DX/2026/002" → ...
|
||||
// Prefix = "DX/{YYYY}" cho từng năm. LastSeq reset đầu năm tự nhiên (key mới).
|
||||
// Update atomic qua SERIALIZABLE transaction trong CodeGen service.
|
||||
public class ProposalCodeSequence
|
||||
{
|
||||
public string Prefix { get; set; } = string.Empty; // PK — "DX/2026"
|
||||
public int LastSeq { get; set; }
|
||||
public DateTime UpdatedAt { get; set; }
|
||||
}
|
||||
@ -0,0 +1,28 @@
|
||||
using SolutionErp.Domain.ApprovalWorkflowsV2;
|
||||
using SolutionErp.Domain.Common;
|
||||
|
||||
namespace SolutionErp.Domain.Office;
|
||||
|
||||
// Phase 10.3 G-O3 (Mig 38 — S37) — Ý kiến cấp duyệt V2 dynamic cho Proposal.
|
||||
// Cookie-cutter mirror PurchaseEvaluationLevelOpinion (Mig 26).
|
||||
//
|
||||
// Mỗi row = 1 (Proposal × ApprovalWorkflowLevel). Service ApproveV2Async sau
|
||||
// khi approve thành công Cấp hiện tại sẽ UPSERT row này (latest-write-wins).
|
||||
// Reject (TraLai/TuChoi) KHÔNG sync.
|
||||
//
|
||||
// UNIQUE composite (ProposalId, LevelId) — 1 row / level / proposal.
|
||||
// FK Cascade Proposal (wipe khi xoá) + Restrict Level (admin xoá Level chặn).
|
||||
// SignedByUserId track actor thật (có thể Admin override) + denorm FullName.
|
||||
public class ProposalLevelOpinion : AuditableEntity
|
||||
{
|
||||
public Guid ProposalId { get; set; }
|
||||
public Guid ApprovalWorkflowLevelId { get; set; }
|
||||
|
||||
public string? Comment { get; set; } // max 2000 hoặc placeholder
|
||||
public DateTime SignedAt { get; set; }
|
||||
public Guid SignedByUserId { get; set; } // người ký thực sự (có thể Admin thay)
|
||||
public string SignedByFullName { get; set; } = string.Empty; // snapshot denorm
|
||||
|
||||
public Proposal? Proposal { get; set; }
|
||||
public ApprovalWorkflowLevel? Level { get; set; }
|
||||
}
|
||||
Reference in New Issue
Block a user