[CLAUDE] Move nested-type menu → fe-user; Admin workflow config page
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 2m41s
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 2m41s
User clarified: menu loại HĐ 3-level (Danh sách/Thao tác/Duyệt) thuộc
fe-user. Admin có page riêng để config quy trình per loại HĐ.
fe-admin Layout:
- filterForAdmin() drops Ct_* entries (hide nested type menu).
- Admin sidebar giờ về lại đơn giản: Dashboard / Master / Hợp đồng
(leaf) / Forms / Reports / System.
fe-user Layout:
- Dynamic menu tree từ /menus/me (thay fixed USER_MENU hardcoded).
- Recursive MenuNodeRenderer (top-level expanded, nested collapsed).
- resolvePath user-specific: Ct_*_List → /my-contracts?type=X,
Ct_*_Create → /contracts/new?type=X, Ct_*_Pending → /inbox?type=X.
- filterForUser drops admin-only entries (Master/System/Forms/Reports).
- Static USER_FIXED_TOP prepends "Hộp thư" leaf → /inbox.
- MyContractsPage + InboxPage đọc ?type=X param, filter client-side.
Workflow config (Admin side):
- Domain: WorkflowTypeAssignment entity (ContractType → PolicyName
override). Registry.ForContractWithOverrides() prefer DB override
else default.
- Infrastructure: EF config + migration AddWorkflowTypeAssignments,
unique index trên ContractType. ContractWorkflowService load
overrides dict mỗi transition. ContractFeatures load overrides khi
build WorkflowSummaryDto.
- Application: GetWorkflowAdminOverviewQuery returns 7 types × current
policy + available policies. SetWorkflowAssignmentCommand validate
policy name tồn tại; nếu = default thì delete override (no stale row).
- Api: GET /api/workflows + PUT /api/workflows/{contractType}
với policy "Workflows.Read" + "Workflows.Update".
- Menu: new key `Workflows` dưới System, label "Quy trình HĐ".
- FE /system/workflows: 7 card per type, dropdown Standard/SkipCcm +
'Đã override' badge khi khác default, phase sequence timeline,
explanation banner ở top. Iteration 2 note: admin-authored custom
policies.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@ -117,23 +117,40 @@ public static class WorkflowPolicies
|
||||
|
||||
public static class WorkflowPolicyRegistry
|
||||
{
|
||||
// Mapping contract type → policy. Tuned to the real business from
|
||||
// QT-TP-NCC.docx: formal NTP/NCC/Giao khoán need full CCM review; service /
|
||||
// purchase / framework contracts skip CCM.
|
||||
public static WorkflowPolicy For(ContractType type) => type switch
|
||||
// Available policy names — extend when admin-authored custom policies
|
||||
// are added in iteration 2.
|
||||
public static readonly string[] AvailablePolicyNames = ["Standard", "SkipCcm"];
|
||||
|
||||
public static WorkflowPolicy ByName(string name) => name switch
|
||||
{
|
||||
ContractType.HopDongThauPhu => WorkflowPolicies.Standard,
|
||||
ContractType.HopDongGiaoKhoan => WorkflowPolicies.Standard,
|
||||
ContractType.HopDongNhaCungCap => WorkflowPolicies.Standard,
|
||||
ContractType.HopDongDichVu => WorkflowPolicies.SkipCcm,
|
||||
ContractType.HopDongMuaBan => WorkflowPolicies.SkipCcm,
|
||||
ContractType.HopDongNguyenTacNCC => WorkflowPolicies.SkipCcm,
|
||||
ContractType.HopDongNguyenTacDichVu => WorkflowPolicies.SkipCcm,
|
||||
"SkipCcm" => WorkflowPolicies.SkipCcm,
|
||||
_ => WorkflowPolicies.Standard,
|
||||
};
|
||||
|
||||
// Instance-level bypass flag overrides the default: if a contract has
|
||||
// Default mapping per contract type — used when DB override missing.
|
||||
// Matches business from QT-TP-NCC.docx.
|
||||
public static string DefaultPolicyNameFor(ContractType type) => type switch
|
||||
{
|
||||
ContractType.HopDongThauPhu or ContractType.HopDongGiaoKhoan or ContractType.HopDongNhaCungCap => "Standard",
|
||||
_ => "SkipCcm",
|
||||
};
|
||||
|
||||
public static WorkflowPolicy For(ContractType type) => ByName(DefaultPolicyNameFor(type));
|
||||
|
||||
// Instance-level bypass flag overrides the policy — if a contract has
|
||||
// BypassProcurementAndCCM=true, always use SkipCcm regardless of type.
|
||||
public static WorkflowPolicy ForContract(Contract contract) =>
|
||||
contract.BypassProcurementAndCCM ? WorkflowPolicies.SkipCcm : For(contract.Type);
|
||||
|
||||
// DB-aware resolver: prefer assignment, else default. Pass in the
|
||||
// assignments dict once per request (cached from DB).
|
||||
public static WorkflowPolicy ForContractWithOverrides(
|
||||
Contract contract,
|
||||
IReadOnlyDictionary<ContractType, string>? assignments)
|
||||
{
|
||||
if (contract.BypassProcurementAndCCM) return WorkflowPolicies.SkipCcm;
|
||||
if (assignments is not null && assignments.TryGetValue(contract.Type, out var policyName))
|
||||
return ByName(policyName);
|
||||
return For(contract.Type);
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,13 @@
|
||||
using SolutionErp.Domain.Common;
|
||||
|
||||
namespace SolutionErp.Domain.Contracts;
|
||||
|
||||
// Admin-configurable: which WorkflowPolicy applies to each ContractType.
|
||||
// Seed with hardcoded defaults (see DbInitializer); admin can switch via
|
||||
// /system/workflows UI without code changes. Later iteration will let admin
|
||||
// define custom policies with bespoke phase/role/SLA.
|
||||
public class WorkflowTypeAssignment : BaseEntity
|
||||
{
|
||||
public ContractType ContractType { get; set; }
|
||||
public string PolicyName { get; set; } = string.Empty; // "Standard" | "SkipCcm" (extensible)
|
||||
}
|
||||
@ -16,6 +16,7 @@ public static class MenuKeys
|
||||
public const string Users = "Users";
|
||||
public const string Roles = "Roles";
|
||||
public const string Permissions = "Permissions";
|
||||
public const string Workflows = "Workflows";
|
||||
|
||||
// Per-contract-type menu groups + 3 action leaves each.
|
||||
// Key format: Ct_<TypeCode>[_<Action>] — prefix `Ct_` distinguishes from
|
||||
@ -35,7 +36,7 @@ public static class MenuKeys
|
||||
Dashboard,
|
||||
Master, Suppliers, Projects, Departments,
|
||||
Contracts, Forms, Reports,
|
||||
System, Users, Roles, Permissions,
|
||||
System, Users, Roles, Permissions, Workflows,
|
||||
];
|
||||
|
||||
public static readonly string[] Actions = ["Read", "Create", "Update", "Delete"];
|
||||
|
||||
Reference in New Issue
Block a user