[CLAUDE] Domain+Infra: PurchaseEvaluation module — 10 bảng + 2 workflow seed (migration 12)
Module Duyệt NCC (tiền-HĐ): phiếu trình duyệt so sánh giá N NCC × M hạng mục trước khi ký HĐ. 2 quy trình: A DuyetNcc (3-step: Purchasing→CCM→CEO), B DuyetNccPhuongAn (5-step: Purchasing→DựÁn→CCM→CEO PA→CEO NCC). Domain (7 core + 3 workflow admin): - PurchaseEvaluation (header, AuditableEntity, pin WorkflowDefinitionId, SelectedSupplierId, PaymentTerms JSON, ContractId? FK kế thừa) - PurchaseEvaluationSupplier (N:M Phiếu × Supplier + contact + payment term) - PurchaseEvaluationDetail (hạng mục + ngân sách, group A.I/A.II/...) - PurchaseEvaluationQuote (báo giá per NCC per hạng mục + IsSelected) - PurchaseEvaluationApproval (workflow history, reuse ApprovalDecision) - PurchaseEvaluationChangelog (audit log, reuse ChangelogAction) - PurchaseEvaluationAttachment (file upload — báo giá NCC + spec...) - PurchaseEvaluationWorkflowDefinition/Step/StepApprover (config y như HĐ, tách table riêng vì Phase là PurchaseEvaluationPhase enum riêng) Policy: - PurchaseEvaluationPolicy record + PurchaseEvaluationPolicies.NccOnly/ NccWithPlan (default hardcoded) + FromDefinition(def) build runtime policy từ DB admin-authored. Default SLA: soạn 3d, step 1-2d, CEO 1d. EF: 10 configurations với index phase+isDeleted, SupplierId, ProjectId, SlaDeadline, WorkflowDefinitionId, ContractId. UX index (PeId, SupplierId) + (DetailId, SupplierId). HasQueryFilter soft delete cho header. Migration 12 AddPurchaseEvaluations tạo 10 bảng. Idempotent seed: - SeedMenuTreeAsync +13 menu item (Pe_* root + 2 group + 6 action leaf + PeWorkflows root + 2 admin leaf) - SeedPurchaseEvaluationWorkflowsAsync seed QT-DN-A-v01 + QT-DN-B-v01
This commit is contained in:
@ -6,6 +6,7 @@ using SolutionErp.Domain.Identity;
|
|||||||
using SolutionErp.Domain.Master;
|
using SolutionErp.Domain.Master;
|
||||||
using SolutionErp.Domain.Master.Catalogs;
|
using SolutionErp.Domain.Master.Catalogs;
|
||||||
using SolutionErp.Domain.Notifications;
|
using SolutionErp.Domain.Notifications;
|
||||||
|
using SolutionErp.Domain.PurchaseEvaluations;
|
||||||
|
|
||||||
namespace SolutionErp.Application.Common.Interfaces;
|
namespace SolutionErp.Application.Common.Interfaces;
|
||||||
|
|
||||||
@ -44,5 +45,17 @@ public interface IApplicationDbContext
|
|||||||
DbSet<NguyenTacNccDetail> NguyenTacNccDetails { get; }
|
DbSet<NguyenTacNccDetail> NguyenTacNccDetails { get; }
|
||||||
DbSet<NguyenTacDvDetail> NguyenTacDvDetails { get; }
|
DbSet<NguyenTacDvDetail> NguyenTacDvDetails { get; }
|
||||||
|
|
||||||
|
// Module Duyệt NCC (tiền-HĐ) — 7 core + 3 workflow config
|
||||||
|
DbSet<PurchaseEvaluation> PurchaseEvaluations { get; }
|
||||||
|
DbSet<PurchaseEvaluationSupplier> PurchaseEvaluationSuppliers { get; }
|
||||||
|
DbSet<PurchaseEvaluationDetail> PurchaseEvaluationDetails { get; }
|
||||||
|
DbSet<PurchaseEvaluationQuote> PurchaseEvaluationQuotes { get; }
|
||||||
|
DbSet<PurchaseEvaluationApproval> PurchaseEvaluationApprovals { get; }
|
||||||
|
DbSet<PurchaseEvaluationChangelog> PurchaseEvaluationChangelogs { get; }
|
||||||
|
DbSet<PurchaseEvaluationAttachment> PurchaseEvaluationAttachments { get; }
|
||||||
|
DbSet<PurchaseEvaluationWorkflowDefinition> PurchaseEvaluationWorkflowDefinitions { get; }
|
||||||
|
DbSet<PurchaseEvaluationWorkflowStep> PurchaseEvaluationWorkflowSteps { get; }
|
||||||
|
DbSet<PurchaseEvaluationWorkflowStepApprover> PurchaseEvaluationWorkflowStepApprovers { get; }
|
||||||
|
|
||||||
Task<int> SaveChangesAsync(CancellationToken cancellationToken = default);
|
Task<int> SaveChangesAsync(CancellationToken cancellationToken = default);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -42,13 +42,35 @@ public static class MenuKeys
|
|||||||
// → mở /system/workflows/{typeCode} (filter theo type thay vì tab).
|
// → mở /system/workflows/{typeCode} (filter theo type thay vì tab).
|
||||||
public static string WorkflowTypeLeaf(string typeCode) => $"Wf_{typeCode}";
|
public static string WorkflowTypeLeaf(string typeCode) => $"Wf_{typeCode}";
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// Module Duyệt NCC (tiền-HĐ) — Pe_* prefix. 2 EvaluationType:
|
||||||
|
// DuyetNcc (A, NccOnly 3-step) + DuyetNccPhuongAn (B, NccWithPlan 5-step).
|
||||||
|
// Mỗi type có 3 action leaf (Danh sách / Thao tác / Duyệt) + 1 group.
|
||||||
|
// Workflow admin cho PE ở /system/pe-workflows/:typeCode.
|
||||||
|
// ============================================================
|
||||||
|
public const string PurchaseEvaluations = "PurchaseEvaluations"; // root group
|
||||||
|
public const string PeWorkflows = "PeWorkflows"; // workflow admin root
|
||||||
|
|
||||||
|
public static readonly string[] PurchaseEvaluationTypeCodes =
|
||||||
|
["DuyetNcc", "DuyetNccPhuongAn"];
|
||||||
|
|
||||||
|
public static string PurchaseEvaluationGroup(string typeCode) => $"Pe_{typeCode}";
|
||||||
|
public static string PurchaseEvaluationList(string typeCode) => $"Pe_{typeCode}_List";
|
||||||
|
public static string PurchaseEvaluationCreate(string typeCode) => $"Pe_{typeCode}_Create";
|
||||||
|
public static string PurchaseEvaluationPending(string typeCode) => $"Pe_{typeCode}_Pending";
|
||||||
|
|
||||||
|
// Workflow admin leaf per PE type — dưới PeWorkflows, click leaf mở
|
||||||
|
// /system/pe-workflows/{typeCode}
|
||||||
|
public static string PeWorkflowTypeLeaf(string typeCode) => $"PeWf_{typeCode}";
|
||||||
|
|
||||||
public static readonly string[] All =
|
public static readonly string[] All =
|
||||||
[
|
[
|
||||||
Dashboard,
|
Dashboard,
|
||||||
Master, Suppliers, Projects, Departments,
|
Master, Suppliers, Projects, Departments,
|
||||||
Catalogs, CatalogUnits, CatalogMaterials, CatalogServices, CatalogWorkItems,
|
Catalogs, CatalogUnits, CatalogMaterials, CatalogServices, CatalogWorkItems,
|
||||||
Contracts, Forms, Reports,
|
Contracts, Forms, Reports,
|
||||||
System, Users, Roles, Permissions, Workflows,
|
PurchaseEvaluations,
|
||||||
|
System, Users, Roles, Permissions, Workflows, PeWorkflows,
|
||||||
];
|
];
|
||||||
|
|
||||||
public static readonly string[] Actions = ["Read", "Create", "Update", "Delete"];
|
public static readonly string[] Actions = ["Read", "Create", "Update", "Delete"];
|
||||||
|
|||||||
@ -0,0 +1,36 @@
|
|||||||
|
using SolutionErp.Domain.Common;
|
||||||
|
|
||||||
|
namespace SolutionErp.Domain.PurchaseEvaluations;
|
||||||
|
|
||||||
|
// Aggregate root cho phiếu Duyệt NCC — module tiền-HĐ.
|
||||||
|
// Sau khi phê duyệt xong (Phase=DaDuyet) user click "Tạo HĐ từ phiếu" →
|
||||||
|
// kế thừa Details/Quotes sang Contract + ContractDetails.
|
||||||
|
public class PurchaseEvaluation : AuditableEntity
|
||||||
|
{
|
||||||
|
public string? MaPhieu { get; set; } // Auto-gen khi create (format tính sau)
|
||||||
|
public PurchaseEvaluationType Type { get; set; }
|
||||||
|
public PurchaseEvaluationPhase Phase { get; set; } = PurchaseEvaluationPhase.DangSoanThao;
|
||||||
|
|
||||||
|
public string TenGoiThau { get; set; } = string.Empty; // "Cung cấp bê tông"
|
||||||
|
public Guid ProjectId { get; set; } // Dự án (FK Projects)
|
||||||
|
public Guid? DepartmentId { get; set; }
|
||||||
|
public Guid? DrafterUserId { get; set; } // QS/NV.PB soạn
|
||||||
|
public string? DiaDiem { get; set; } // Lô K, KCN Lộc An...
|
||||||
|
public string? MoTa { get; set; }
|
||||||
|
|
||||||
|
public Guid? WorkflowDefinitionId { get; set; } // Pinned at create — config y như HĐ
|
||||||
|
public DateTime? SlaDeadline { get; set; }
|
||||||
|
public bool SlaWarningSent { get; set; }
|
||||||
|
|
||||||
|
public Guid? SelectedSupplierId { get; set; } // NCC thắng — null tới khi DaDuyet
|
||||||
|
public string? PaymentTerms { get; set; } // JSON {tamUng, thanhToanTam, quyetToan, baoHanh, hanMucCongNo, danhGia}
|
||||||
|
|
||||||
|
public Guid? ContractId { get; set; } // FK Contracts — set khi user gen HĐ từ phiếu
|
||||||
|
|
||||||
|
public List<PurchaseEvaluationSupplier> Suppliers { get; set; } = new();
|
||||||
|
public List<PurchaseEvaluationDetail> Details { get; set; } = new();
|
||||||
|
public List<PurchaseEvaluationQuote> Quotes { get; set; } = new();
|
||||||
|
public List<PurchaseEvaluationApproval> Approvals { get; set; } = new();
|
||||||
|
public List<PurchaseEvaluationChangelog> Changelogs { get; set; } = new();
|
||||||
|
public List<PurchaseEvaluationAttachment> Attachments { get; set; } = new();
|
||||||
|
}
|
||||||
@ -0,0 +1,18 @@
|
|||||||
|
using SolutionErp.Domain.Common;
|
||||||
|
using SolutionErp.Domain.Contracts; // reuse ApprovalDecision enum
|
||||||
|
|
||||||
|
namespace SolutionErp.Domain.PurchaseEvaluations;
|
||||||
|
|
||||||
|
// Lịch sử phê duyệt — giống ContractApproval pattern.
|
||||||
|
public class PurchaseEvaluationApproval : BaseEntity
|
||||||
|
{
|
||||||
|
public Guid PurchaseEvaluationId { get; set; }
|
||||||
|
public PurchaseEvaluationPhase FromPhase { get; set; }
|
||||||
|
public PurchaseEvaluationPhase ToPhase { get; set; }
|
||||||
|
public Guid? ApproverUserId { get; set; } // null = system SLA auto
|
||||||
|
public ApprovalDecision Decision { get; set; }
|
||||||
|
public string? Comment { get; set; }
|
||||||
|
public DateTime ApprovedAt { get; set; }
|
||||||
|
|
||||||
|
public PurchaseEvaluation? PurchaseEvaluation { get; set; }
|
||||||
|
}
|
||||||
@ -0,0 +1,25 @@
|
|||||||
|
using SolutionErp.Domain.Common;
|
||||||
|
|
||||||
|
namespace SolutionErp.Domain.PurchaseEvaluations;
|
||||||
|
|
||||||
|
public enum PurchaseEvaluationAttachmentPurpose
|
||||||
|
{
|
||||||
|
QuoteDocument = 1, // File báo giá NCC gửi (PDF/xlsx)
|
||||||
|
RequirementSpec = 2, // Bản vẽ/yêu cầu kỹ thuật kèm theo
|
||||||
|
DecisionExport = 3, // Bản phiếu duyệt đã export
|
||||||
|
Other = 99,
|
||||||
|
}
|
||||||
|
|
||||||
|
public class PurchaseEvaluationAttachment : BaseEntity
|
||||||
|
{
|
||||||
|
public Guid PurchaseEvaluationId { get; set; }
|
||||||
|
public Guid? PurchaseEvaluationSupplierId { get; set; } // Null nếu không gắn với NCC cụ thể
|
||||||
|
public string FileName { get; set; } = string.Empty;
|
||||||
|
public string StoragePath { get; set; } = string.Empty;
|
||||||
|
public long FileSize { get; set; }
|
||||||
|
public string ContentType { get; set; } = string.Empty;
|
||||||
|
public PurchaseEvaluationAttachmentPurpose Purpose { get; set; }
|
||||||
|
public string? Note { get; set; }
|
||||||
|
|
||||||
|
public PurchaseEvaluation? PurchaseEvaluation { get; set; }
|
||||||
|
}
|
||||||
@ -0,0 +1,33 @@
|
|||||||
|
using SolutionErp.Domain.Common;
|
||||||
|
using SolutionErp.Domain.Contracts; // reuse ChangelogAction enum
|
||||||
|
|
||||||
|
namespace SolutionErp.Domain.PurchaseEvaluations;
|
||||||
|
|
||||||
|
// Audit log unified cho mọi thay đổi trên phiếu — Header / Supplier / Detail
|
||||||
|
// / Quote / Workflow / Attachment. Populate tương tự ContractChangelog qua
|
||||||
|
// IPurchaseEvaluationChangelogService.
|
||||||
|
public class PurchaseEvaluationChangelog : BaseEntity
|
||||||
|
{
|
||||||
|
public Guid PurchaseEvaluationId { get; set; }
|
||||||
|
public PurchaseEvaluation? PurchaseEvaluation { get; set; }
|
||||||
|
|
||||||
|
public PurchaseEvaluationEntityType EntityType { get; set; }
|
||||||
|
public Guid? EntityId { get; set; }
|
||||||
|
public ChangelogAction Action { get; set; }
|
||||||
|
public PurchaseEvaluationPhase? PhaseAtChange { get; set; }
|
||||||
|
public Guid? UserId { get; set; }
|
||||||
|
public string? UserName { get; set; }
|
||||||
|
public string? Summary { get; set; }
|
||||||
|
public string? FieldChangesJson { get; set; }
|
||||||
|
public string? ContextNote { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum PurchaseEvaluationEntityType
|
||||||
|
{
|
||||||
|
Header = 1,
|
||||||
|
Supplier = 2,
|
||||||
|
Detail = 3,
|
||||||
|
Quote = 4,
|
||||||
|
Workflow = 5,
|
||||||
|
Attachment = 6,
|
||||||
|
}
|
||||||
@ -0,0 +1,24 @@
|
|||||||
|
using SolutionErp.Domain.Common;
|
||||||
|
|
||||||
|
namespace SolutionErp.Domain.PurchaseEvaluations;
|
||||||
|
|
||||||
|
// Hạng mục so sánh giá + ngân sách (Excel III + "CHI TIẾT SO SÁNH").
|
||||||
|
// Tách Quotes ra PurchaseEvaluationQuote để normalize N NCC × M hạng mục.
|
||||||
|
public class PurchaseEvaluationDetail : BaseEntity
|
||||||
|
{
|
||||||
|
public Guid PurchaseEvaluationId { get; set; }
|
||||||
|
public string GroupCode { get; set; } = string.Empty; // "A.I", "A.II", "A.III", "A.IV"
|
||||||
|
public string GroupName { get; set; } = string.Empty; // "Bê tông", "Phụ gia", "Bơm bê tông", "Vận chuyển"
|
||||||
|
public string? ItemCode { get; set; } // "DMCCC0001"
|
||||||
|
public string NoiDung { get; set; } = string.Empty; // "Concrete M100"
|
||||||
|
public string? DonViTinh { get; set; } // "m3"
|
||||||
|
public decimal KhoiLuongNganSach { get; set; }
|
||||||
|
public decimal KhoiLuongThiCong { get; set; }
|
||||||
|
public decimal DonGiaNganSach { get; set; } // Chưa VAT
|
||||||
|
public decimal ThanhTienNganSach { get; set; } // = KL × Đơn giá (hoặc nhập tay)
|
||||||
|
public int Order { get; set; }
|
||||||
|
public string? GhiChu { get; set; }
|
||||||
|
|
||||||
|
public PurchaseEvaluation? PurchaseEvaluation { get; set; }
|
||||||
|
public List<PurchaseEvaluationQuote> Quotes { get; set; } = new();
|
||||||
|
}
|
||||||
@ -0,0 +1,20 @@
|
|||||||
|
namespace SolutionErp.Domain.PurchaseEvaluations;
|
||||||
|
|
||||||
|
// State machine cho phiếu Duyệt NCC (tiền-HĐ). 2 workflow khác nhau cùng
|
||||||
|
// share state space này — A (DuyetNcc) dùng subset, B (DuyetNccPhuongAn)
|
||||||
|
// dùng full.
|
||||||
|
//
|
||||||
|
// A: DangSoanThao → ChoPurchasing → ChoCCM → ChoCEODuyetNCC → DaDuyet
|
||||||
|
// B: DangSoanThao → ChoPurchasing → ChoDuAn → ChoCCM → ChoCEODuyetPA → ChoCEODuyetNCC → DaDuyet
|
||||||
|
// Cả 2: từ DangSoanThao có thể → TuChoi; từ mọi phase duyệt reject → DangSoanThao.
|
||||||
|
public enum PurchaseEvaluationPhase
|
||||||
|
{
|
||||||
|
DangSoanThao = 1,
|
||||||
|
ChoPurchasing = 2,
|
||||||
|
ChoDuAn = 3, // chỉ B
|
||||||
|
ChoCCM = 4,
|
||||||
|
ChoCEODuyetPA = 5, // chỉ B (duyệt phương án trước)
|
||||||
|
ChoCEODuyetNCC = 6, // chung cả A & B — duyệt chọn đơn vị
|
||||||
|
DaDuyet = 7, // terminal thành công
|
||||||
|
TuChoi = 99, // terminal từ chối
|
||||||
|
}
|
||||||
@ -0,0 +1,203 @@
|
|||||||
|
using SolutionErp.Domain.Contracts; // WorkflowApproverKind
|
||||||
|
using SolutionErp.Domain.Identity;
|
||||||
|
|
||||||
|
namespace SolutionErp.Domain.PurchaseEvaluations;
|
||||||
|
|
||||||
|
// Policy record cho phiếu Duyệt NCC — mirror WorkflowPolicy của HĐ nhưng
|
||||||
|
// dùng PurchaseEvaluationPhase enum. 2 default policy hardcoded (A/B)
|
||||||
|
// phục vụ seed + fallback khi admin chưa author định nghĩa DB.
|
||||||
|
public sealed record PurchaseEvaluationPolicy(
|
||||||
|
string Name,
|
||||||
|
string Description,
|
||||||
|
IReadOnlyDictionary<(PurchaseEvaluationPhase From, PurchaseEvaluationPhase To), string[]> Transitions,
|
||||||
|
IReadOnlyDictionary<PurchaseEvaluationPhase, TimeSpan?> PhaseSla,
|
||||||
|
IReadOnlyList<PurchaseEvaluationPhase> ActivePhases,
|
||||||
|
IReadOnlyDictionary<(PurchaseEvaluationPhase From, PurchaseEvaluationPhase To), string[]>? UserTransitions = null)
|
||||||
|
{
|
||||||
|
public bool HasPhase(PurchaseEvaluationPhase phase) => ActivePhases.Contains(phase);
|
||||||
|
|
||||||
|
public bool IsTransitionAllowed(
|
||||||
|
PurchaseEvaluationPhase from, PurchaseEvaluationPhase to,
|
||||||
|
IReadOnlyList<string> actorRoles, Guid? actorUserId = null)
|
||||||
|
{
|
||||||
|
if (!Transitions.TryGetValue((from, to), out var roles)) return false;
|
||||||
|
if (actorRoles.Any(r => roles.Contains(r))) return true;
|
||||||
|
|
||||||
|
if (actorUserId is null) return false;
|
||||||
|
if (UserTransitions is null) return false;
|
||||||
|
if (!UserTransitions.TryGetValue((from, to), out var userIds)) return false;
|
||||||
|
return userIds.Contains(actorUserId.Value.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
public IReadOnlyList<PurchaseEvaluationPhase> NextPhasesFrom(PurchaseEvaluationPhase from) =>
|
||||||
|
Transitions.Keys.Where(k => k.From == from).Select(k => k.To).Distinct().ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class PurchaseEvaluationPolicies
|
||||||
|
{
|
||||||
|
private static readonly Dictionary<PurchaseEvaluationPhase, TimeSpan?> DefaultSla = new()
|
||||||
|
{
|
||||||
|
[PurchaseEvaluationPhase.DangSoanThao] = TimeSpan.FromDays(3),
|
||||||
|
[PurchaseEvaluationPhase.ChoPurchasing] = TimeSpan.FromDays(2),
|
||||||
|
[PurchaseEvaluationPhase.ChoDuAn] = TimeSpan.FromDays(2),
|
||||||
|
[PurchaseEvaluationPhase.ChoCCM] = TimeSpan.FromDays(2),
|
||||||
|
[PurchaseEvaluationPhase.ChoCEODuyetPA] = TimeSpan.FromDays(1),
|
||||||
|
[PurchaseEvaluationPhase.ChoCEODuyetNCC] = TimeSpan.FromDays(1),
|
||||||
|
[PurchaseEvaluationPhase.DaDuyet] = null,
|
||||||
|
[PurchaseEvaluationPhase.TuChoi] = null,
|
||||||
|
};
|
||||||
|
|
||||||
|
// A — DuyetNcc (3 step thực + Drafter soạn): Drafter → Purchasing → CCM → CEO
|
||||||
|
public static readonly PurchaseEvaluationPolicy NccOnly = new(
|
||||||
|
Name: "NccOnly",
|
||||||
|
Description: "Duyệt NCC — 3 step (Purchasing → CCM → CEO). Không cần duyệt phương án.",
|
||||||
|
Transitions: new Dictionary<(PurchaseEvaluationPhase, PurchaseEvaluationPhase), string[]>
|
||||||
|
{
|
||||||
|
[(PurchaseEvaluationPhase.DangSoanThao, PurchaseEvaluationPhase.ChoPurchasing)] = [AppRoles.Drafter, AppRoles.DeptManager],
|
||||||
|
[(PurchaseEvaluationPhase.DangSoanThao, PurchaseEvaluationPhase.TuChoi)] = [AppRoles.Drafter, AppRoles.DeptManager],
|
||||||
|
|
||||||
|
[(PurchaseEvaluationPhase.ChoPurchasing, PurchaseEvaluationPhase.ChoCCM)] = [AppRoles.Procurement],
|
||||||
|
[(PurchaseEvaluationPhase.ChoPurchasing, PurchaseEvaluationPhase.DangSoanThao)] = [AppRoles.Procurement],
|
||||||
|
|
||||||
|
[(PurchaseEvaluationPhase.ChoCCM, PurchaseEvaluationPhase.ChoCEODuyetNCC)] = [AppRoles.CostControl],
|
||||||
|
[(PurchaseEvaluationPhase.ChoCCM, PurchaseEvaluationPhase.DangSoanThao)] = [AppRoles.CostControl],
|
||||||
|
|
||||||
|
[(PurchaseEvaluationPhase.ChoCEODuyetNCC, PurchaseEvaluationPhase.DaDuyet)] = [AppRoles.Director, AppRoles.AuthorizedSigner],
|
||||||
|
[(PurchaseEvaluationPhase.ChoCEODuyetNCC, PurchaseEvaluationPhase.DangSoanThao)] = [AppRoles.Director, AppRoles.AuthorizedSigner],
|
||||||
|
},
|
||||||
|
PhaseSla: DefaultSla,
|
||||||
|
ActivePhases:
|
||||||
|
[
|
||||||
|
PurchaseEvaluationPhase.DangSoanThao,
|
||||||
|
PurchaseEvaluationPhase.ChoPurchasing,
|
||||||
|
PurchaseEvaluationPhase.ChoCCM,
|
||||||
|
PurchaseEvaluationPhase.ChoCEODuyetNCC,
|
||||||
|
PurchaseEvaluationPhase.DaDuyet,
|
||||||
|
PurchaseEvaluationPhase.TuChoi,
|
||||||
|
]);
|
||||||
|
|
||||||
|
// B — DuyetNccPhuongAn (5 step thực + Drafter): Drafter → Purchasing → Dự án → CCM → CEO(PA) → CEO(NCC)
|
||||||
|
public static readonly PurchaseEvaluationPolicy NccWithPlan = new(
|
||||||
|
Name: "NccWithPlan",
|
||||||
|
Description: "Duyệt NCC + Phương án — 5 step (Purchasing → Dự án → CCM → CEO duyệt PA → CEO duyệt NCC).",
|
||||||
|
Transitions: new Dictionary<(PurchaseEvaluationPhase, PurchaseEvaluationPhase), string[]>
|
||||||
|
{
|
||||||
|
[(PurchaseEvaluationPhase.DangSoanThao, PurchaseEvaluationPhase.ChoPurchasing)] = [AppRoles.Drafter, AppRoles.DeptManager],
|
||||||
|
[(PurchaseEvaluationPhase.DangSoanThao, PurchaseEvaluationPhase.TuChoi)] = [AppRoles.Drafter, AppRoles.DeptManager],
|
||||||
|
|
||||||
|
[(PurchaseEvaluationPhase.ChoPurchasing, PurchaseEvaluationPhase.ChoDuAn)] = [AppRoles.Procurement],
|
||||||
|
[(PurchaseEvaluationPhase.ChoPurchasing, PurchaseEvaluationPhase.DangSoanThao)] = [AppRoles.Procurement],
|
||||||
|
|
||||||
|
[(PurchaseEvaluationPhase.ChoDuAn, PurchaseEvaluationPhase.ChoCCM)] = [AppRoles.ProjectManager],
|
||||||
|
[(PurchaseEvaluationPhase.ChoDuAn, PurchaseEvaluationPhase.DangSoanThao)] = [AppRoles.ProjectManager],
|
||||||
|
|
||||||
|
[(PurchaseEvaluationPhase.ChoCCM, PurchaseEvaluationPhase.ChoCEODuyetPA)] = [AppRoles.CostControl],
|
||||||
|
[(PurchaseEvaluationPhase.ChoCCM, PurchaseEvaluationPhase.DangSoanThao)] = [AppRoles.CostControl],
|
||||||
|
|
||||||
|
[(PurchaseEvaluationPhase.ChoCEODuyetPA, PurchaseEvaluationPhase.ChoCEODuyetNCC)] = [AppRoles.Director, AppRoles.AuthorizedSigner],
|
||||||
|
[(PurchaseEvaluationPhase.ChoCEODuyetPA, PurchaseEvaluationPhase.DangSoanThao)] = [AppRoles.Director, AppRoles.AuthorizedSigner],
|
||||||
|
|
||||||
|
[(PurchaseEvaluationPhase.ChoCEODuyetNCC, PurchaseEvaluationPhase.DaDuyet)] = [AppRoles.Director, AppRoles.AuthorizedSigner],
|
||||||
|
[(PurchaseEvaluationPhase.ChoCEODuyetNCC, PurchaseEvaluationPhase.DangSoanThao)] = [AppRoles.Director, AppRoles.AuthorizedSigner],
|
||||||
|
},
|
||||||
|
PhaseSla: DefaultSla,
|
||||||
|
ActivePhases:
|
||||||
|
[
|
||||||
|
PurchaseEvaluationPhase.DangSoanThao,
|
||||||
|
PurchaseEvaluationPhase.ChoPurchasing,
|
||||||
|
PurchaseEvaluationPhase.ChoDuAn,
|
||||||
|
PurchaseEvaluationPhase.ChoCCM,
|
||||||
|
PurchaseEvaluationPhase.ChoCEODuyetPA,
|
||||||
|
PurchaseEvaluationPhase.ChoCEODuyetNCC,
|
||||||
|
PurchaseEvaluationPhase.DaDuyet,
|
||||||
|
PurchaseEvaluationPhase.TuChoi,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class PurchaseEvaluationPolicyRegistry
|
||||||
|
{
|
||||||
|
public static readonly string[] AvailablePolicyNames = ["NccOnly", "NccWithPlan"];
|
||||||
|
|
||||||
|
public static PurchaseEvaluationPolicy ByName(string name) => name switch
|
||||||
|
{
|
||||||
|
"NccWithPlan" => PurchaseEvaluationPolicies.NccWithPlan,
|
||||||
|
_ => PurchaseEvaluationPolicies.NccOnly,
|
||||||
|
};
|
||||||
|
|
||||||
|
public static string DefaultPolicyNameFor(PurchaseEvaluationType type) => type switch
|
||||||
|
{
|
||||||
|
PurchaseEvaluationType.DuyetNccPhuongAn => "NccWithPlan",
|
||||||
|
_ => "NccOnly",
|
||||||
|
};
|
||||||
|
|
||||||
|
public static PurchaseEvaluationPolicy For(PurchaseEvaluationType type) =>
|
||||||
|
ByName(DefaultPolicyNameFor(type));
|
||||||
|
|
||||||
|
public static PurchaseEvaluationPolicy ForEvaluation(PurchaseEvaluation ev) =>
|
||||||
|
For(ev.Type);
|
||||||
|
|
||||||
|
// Build policy from persisted admin-authored definition (mirror
|
||||||
|
// WorkflowPolicyRegistry.FromDefinition for HĐ).
|
||||||
|
public static PurchaseEvaluationPolicy FromDefinition(PurchaseEvaluationWorkflowDefinition def)
|
||||||
|
{
|
||||||
|
var steps = def.Steps.OrderBy(s => s.Order).ToList();
|
||||||
|
var transitions = new Dictionary<(PurchaseEvaluationPhase From, PurchaseEvaluationPhase To), string[]>();
|
||||||
|
var userTransitions = new Dictionary<(PurchaseEvaluationPhase From, PurchaseEvaluationPhase To), string[]>();
|
||||||
|
var sla = new Dictionary<PurchaseEvaluationPhase, TimeSpan?>();
|
||||||
|
var activePhases = new List<PurchaseEvaluationPhase>();
|
||||||
|
|
||||||
|
PurchaseEvaluationPhase? prev = null;
|
||||||
|
foreach (var s in steps)
|
||||||
|
{
|
||||||
|
activePhases.Add(s.Phase);
|
||||||
|
sla[s.Phase] = s.SlaDays is int d ? TimeSpan.FromDays(d) : null;
|
||||||
|
|
||||||
|
var roles = s.Approvers
|
||||||
|
.Where(a => a.Kind == WorkflowApproverKind.Role)
|
||||||
|
.Select(a => a.AssignmentValue)
|
||||||
|
.Distinct()
|
||||||
|
.ToArray();
|
||||||
|
var hasUserKind = s.Approvers.Any(a => a.Kind == WorkflowApproverKind.User);
|
||||||
|
if (roles.Length == 0 && !hasUserKind) roles = [AppRoles.DeptManager];
|
||||||
|
|
||||||
|
var userIds = s.Approvers
|
||||||
|
.Where(a => a.Kind == WorkflowApproverKind.User)
|
||||||
|
.Select(a => a.AssignmentValue)
|
||||||
|
.Distinct()
|
||||||
|
.ToArray();
|
||||||
|
|
||||||
|
if (prev is not null)
|
||||||
|
{
|
||||||
|
transitions[(prev.Value, s.Phase)] = roles;
|
||||||
|
if (userIds.Length > 0) userTransitions[(prev.Value, s.Phase)] = userIds;
|
||||||
|
|
||||||
|
// Reject path back to Drafter (common pattern)
|
||||||
|
if (prev.Value != PurchaseEvaluationPhase.DangSoanThao && s.Phase != PurchaseEvaluationPhase.DangSoanThao)
|
||||||
|
{
|
||||||
|
transitions.TryAdd((s.Phase, PurchaseEvaluationPhase.DangSoanThao), roles);
|
||||||
|
if (userIds.Length > 0)
|
||||||
|
userTransitions.TryAdd((s.Phase, PurchaseEvaluationPhase.DangSoanThao), userIds);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
prev = s.Phase;
|
||||||
|
}
|
||||||
|
// First step có thể reject to TuChoi
|
||||||
|
if (steps.Count > 0)
|
||||||
|
transitions.TryAdd((steps[0].Phase, PurchaseEvaluationPhase.TuChoi),
|
||||||
|
[AppRoles.Drafter, AppRoles.DeptManager]);
|
||||||
|
|
||||||
|
// Terminal states always available
|
||||||
|
if (!activePhases.Contains(PurchaseEvaluationPhase.TuChoi))
|
||||||
|
activePhases.Add(PurchaseEvaluationPhase.TuChoi);
|
||||||
|
if (!activePhases.Contains(PurchaseEvaluationPhase.DaDuyet))
|
||||||
|
activePhases.Add(PurchaseEvaluationPhase.DaDuyet);
|
||||||
|
|
||||||
|
return new PurchaseEvaluationPolicy(
|
||||||
|
Name: $"{def.Code}-v{def.Version:D2}",
|
||||||
|
Description: def.Description ?? def.Name,
|
||||||
|
Transitions: transitions,
|
||||||
|
PhaseSla: sla,
|
||||||
|
ActivePhases: activePhases,
|
||||||
|
UserTransitions: userTransitions.Count > 0 ? userTransitions : null);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,20 @@
|
|||||||
|
using SolutionErp.Domain.Common;
|
||||||
|
|
||||||
|
namespace SolutionErp.Domain.PurchaseEvaluations;
|
||||||
|
|
||||||
|
// Báo giá N NCC × M hạng mục. 1 row = 1 NCC chào 1 hạng mục.
|
||||||
|
// IsSelected cho per-item winner (nếu user chọn mỗi hạng mục 1 NCC khác nhau);
|
||||||
|
// PurchaseEvaluation.SelectedSupplierId là winner tổng thể (trường hợp 1 NCC thắng toàn bộ).
|
||||||
|
public class PurchaseEvaluationQuote : BaseEntity
|
||||||
|
{
|
||||||
|
public Guid PurchaseEvaluationDetailId { get; set; }
|
||||||
|
public Guid PurchaseEvaluationSupplierId { get; set; } // FK PurchaseEvaluationSuppliers
|
||||||
|
public decimal BgVat { get; set; } // Báo giá NCC gửi (đã VAT)
|
||||||
|
public decimal ChuaVat { get; set; } // Chưa VAT
|
||||||
|
public decimal ThanhTien { get; set; } // = KL × ChuaVat (tính sẵn)
|
||||||
|
public bool IsSelected { get; set; } // NCC được chọn cho hạng mục này
|
||||||
|
public string? Note { get; set; }
|
||||||
|
|
||||||
|
public PurchaseEvaluationDetail? Detail { get; set; }
|
||||||
|
public PurchaseEvaluationSupplier? Supplier { get; set; }
|
||||||
|
}
|
||||||
@ -0,0 +1,22 @@
|
|||||||
|
using SolutionErp.Domain.Common;
|
||||||
|
|
||||||
|
namespace SolutionErp.Domain.PurchaseEvaluations;
|
||||||
|
|
||||||
|
// N:M giữa PurchaseEvaluation × Supplier (master). Lưu contact + payment
|
||||||
|
// term per NCC (file Excel section E + cột header "TGN-30 ngày" /
|
||||||
|
// "Tiến Phát" / ... ngay dưới bảng II). Note chứa yellow chip:
|
||||||
|
// "ĐÃ CHỐT SO SÁNH LẦN 1/2", "ĐÀM PHÁN THÊM".
|
||||||
|
public class PurchaseEvaluationSupplier : BaseEntity
|
||||||
|
{
|
||||||
|
public Guid PurchaseEvaluationId { get; set; }
|
||||||
|
public Guid SupplierId { get; set; } // FK Suppliers master
|
||||||
|
public string? DisplayName { get; set; } // Override nếu khác Supplier.Name (vd kèm term: "TGN-30 ngày")
|
||||||
|
public string? ContactName { get; set; }
|
||||||
|
public string? ContactEmail { get; set; }
|
||||||
|
public string? ContactPhone { get; set; }
|
||||||
|
public string? PaymentTermText { get; set; } // Free text: "30 ngày", "45 ngày", "300tr"
|
||||||
|
public string? Note { get; set; } // Chip trạng thái so sánh
|
||||||
|
public int Order { get; set; } // Thứ tự cột trong bảng so sánh
|
||||||
|
|
||||||
|
public PurchaseEvaluation? PurchaseEvaluation { get; set; }
|
||||||
|
}
|
||||||
@ -0,0 +1,10 @@
|
|||||||
|
namespace SolutionErp.Domain.PurchaseEvaluations;
|
||||||
|
|
||||||
|
// 2 quy trình theo flowchart QT chọn NTP/NCC:
|
||||||
|
// A — NccOnly (3 step): Purchasing → CCM → CEO
|
||||||
|
// B — NccWithPlan (5 step): Purchasing → Dự án → CCM → CEO (PA) → CEO (NCC)
|
||||||
|
public enum PurchaseEvaluationType
|
||||||
|
{
|
||||||
|
DuyetNcc = 1, // A
|
||||||
|
DuyetNccPhuongAn = 2, // B
|
||||||
|
}
|
||||||
@ -0,0 +1,46 @@
|
|||||||
|
using SolutionErp.Domain.Common;
|
||||||
|
using SolutionErp.Domain.Contracts; // reuse WorkflowApproverKind
|
||||||
|
|
||||||
|
namespace SolutionErp.Domain.PurchaseEvaluations;
|
||||||
|
|
||||||
|
// Versioned workflow definition cho module Duyệt NCC — pattern giống HĐ
|
||||||
|
// nhưng tách table riêng vì Phase là PurchaseEvaluationPhase enum (không
|
||||||
|
// phải ContractPhase). Admin có UI /system/pe-workflows/:typeCode tương
|
||||||
|
// tự /system/workflows/:typeCode.
|
||||||
|
//
|
||||||
|
// Invariant: AT MOST ONE IsActive=true per PurchaseEvaluationType tại 1
|
||||||
|
// thời điểm. PurchaseEvaluation.WorkflowDefinitionId pin tại create →
|
||||||
|
// phiếu cũ không bị ảnh hưởng khi admin active version mới.
|
||||||
|
public class PurchaseEvaluationWorkflowDefinition : BaseEntity
|
||||||
|
{
|
||||||
|
public string Code { get; set; } = string.Empty; // "QT-DN-A" / "QT-DN-B" default
|
||||||
|
public int Version { get; set; }
|
||||||
|
public PurchaseEvaluationType EvaluationType { get; set; }
|
||||||
|
public string Name { get; set; } = string.Empty;
|
||||||
|
public string? Description { get; set; }
|
||||||
|
public bool IsActive { get; set; }
|
||||||
|
public DateTime? ActivatedAt { get; set; }
|
||||||
|
|
||||||
|
public List<PurchaseEvaluationWorkflowStep> Steps { get; set; } = new();
|
||||||
|
}
|
||||||
|
|
||||||
|
public class PurchaseEvaluationWorkflowStep : BaseEntity
|
||||||
|
{
|
||||||
|
public Guid PurchaseEvaluationWorkflowDefinitionId { get; set; }
|
||||||
|
public int Order { get; set; }
|
||||||
|
public PurchaseEvaluationPhase Phase { get; set; }
|
||||||
|
public string Name { get; set; } = string.Empty;
|
||||||
|
public int? SlaDays { get; set; }
|
||||||
|
|
||||||
|
public PurchaseEvaluationWorkflowDefinition? Definition { get; set; }
|
||||||
|
public List<PurchaseEvaluationWorkflowStepApprover> Approvers { get; set; } = new();
|
||||||
|
}
|
||||||
|
|
||||||
|
public class PurchaseEvaluationWorkflowStepApprover : BaseEntity
|
||||||
|
{
|
||||||
|
public Guid PurchaseEvaluationWorkflowStepId { get; set; }
|
||||||
|
public WorkflowApproverKind Kind { get; set; } // reuse Role/User enum từ Contract
|
||||||
|
public string AssignmentValue { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
public PurchaseEvaluationWorkflowStep? Step { get; set; }
|
||||||
|
}
|
||||||
@ -8,6 +8,7 @@ using SolutionErp.Domain.Identity;
|
|||||||
using SolutionErp.Domain.Master;
|
using SolutionErp.Domain.Master;
|
||||||
using SolutionErp.Domain.Master.Catalogs;
|
using SolutionErp.Domain.Master.Catalogs;
|
||||||
using SolutionErp.Domain.Notifications;
|
using SolutionErp.Domain.Notifications;
|
||||||
|
using SolutionErp.Domain.PurchaseEvaluations;
|
||||||
|
|
||||||
namespace SolutionErp.Infrastructure.Persistence;
|
namespace SolutionErp.Infrastructure.Persistence;
|
||||||
|
|
||||||
@ -46,6 +47,17 @@ public class ApplicationDbContext
|
|||||||
public DbSet<NguyenTacNccDetail> NguyenTacNccDetails => Set<NguyenTacNccDetail>();
|
public DbSet<NguyenTacNccDetail> NguyenTacNccDetails => Set<NguyenTacNccDetail>();
|
||||||
public DbSet<NguyenTacDvDetail> NguyenTacDvDetails => Set<NguyenTacDvDetail>();
|
public DbSet<NguyenTacDvDetail> NguyenTacDvDetails => Set<NguyenTacDvDetail>();
|
||||||
|
|
||||||
|
public DbSet<PurchaseEvaluation> PurchaseEvaluations => Set<PurchaseEvaluation>();
|
||||||
|
public DbSet<PurchaseEvaluationSupplier> PurchaseEvaluationSuppliers => Set<PurchaseEvaluationSupplier>();
|
||||||
|
public DbSet<PurchaseEvaluationDetail> PurchaseEvaluationDetails => Set<PurchaseEvaluationDetail>();
|
||||||
|
public DbSet<PurchaseEvaluationQuote> PurchaseEvaluationQuotes => Set<PurchaseEvaluationQuote>();
|
||||||
|
public DbSet<PurchaseEvaluationApproval> PurchaseEvaluationApprovals => Set<PurchaseEvaluationApproval>();
|
||||||
|
public DbSet<PurchaseEvaluationChangelog> PurchaseEvaluationChangelogs => Set<PurchaseEvaluationChangelog>();
|
||||||
|
public DbSet<PurchaseEvaluationAttachment> PurchaseEvaluationAttachments => Set<PurchaseEvaluationAttachment>();
|
||||||
|
public DbSet<PurchaseEvaluationWorkflowDefinition> PurchaseEvaluationWorkflowDefinitions => Set<PurchaseEvaluationWorkflowDefinition>();
|
||||||
|
public DbSet<PurchaseEvaluationWorkflowStep> PurchaseEvaluationWorkflowSteps => Set<PurchaseEvaluationWorkflowStep>();
|
||||||
|
public DbSet<PurchaseEvaluationWorkflowStepApprover> PurchaseEvaluationWorkflowStepApprovers => Set<PurchaseEvaluationWorkflowStepApprover>();
|
||||||
|
|
||||||
protected override void OnModelCreating(ModelBuilder builder)
|
protected override void OnModelCreating(ModelBuilder builder)
|
||||||
{
|
{
|
||||||
base.OnModelCreating(builder);
|
base.OnModelCreating(builder);
|
||||||
|
|||||||
@ -0,0 +1,205 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore.Metadata.Builders;
|
||||||
|
using SolutionErp.Domain.PurchaseEvaluations;
|
||||||
|
|
||||||
|
namespace SolutionErp.Infrastructure.Persistence.Configurations;
|
||||||
|
|
||||||
|
public class PurchaseEvaluationConfiguration : IEntityTypeConfiguration<PurchaseEvaluation>
|
||||||
|
{
|
||||||
|
public void Configure(EntityTypeBuilder<PurchaseEvaluation> b)
|
||||||
|
{
|
||||||
|
b.ToTable("PurchaseEvaluations");
|
||||||
|
b.HasKey(x => x.Id);
|
||||||
|
|
||||||
|
b.Property(x => x.MaPhieu).HasMaxLength(100);
|
||||||
|
b.Property(x => x.Type).HasConversion<int>();
|
||||||
|
b.Property(x => x.Phase).HasConversion<int>();
|
||||||
|
b.Property(x => x.TenGoiThau).HasMaxLength(500).IsRequired();
|
||||||
|
b.Property(x => x.DiaDiem).HasMaxLength(500);
|
||||||
|
b.Property(x => x.MoTa).HasMaxLength(2000);
|
||||||
|
b.Property(x => x.PaymentTerms).HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.HasIndex(x => x.MaPhieu).IsUnique().HasFilter("[MaPhieu] IS NOT NULL");
|
||||||
|
b.HasIndex(x => new { x.Phase, x.IsDeleted });
|
||||||
|
b.HasIndex(x => x.ProjectId);
|
||||||
|
b.HasIndex(x => x.SlaDeadline);
|
||||||
|
b.HasIndex(x => x.WorkflowDefinitionId);
|
||||||
|
b.HasIndex(x => x.ContractId);
|
||||||
|
|
||||||
|
b.HasMany(x => x.Suppliers).WithOne(s => s.PurchaseEvaluation).HasForeignKey(s => s.PurchaseEvaluationId).OnDelete(DeleteBehavior.Cascade);
|
||||||
|
b.HasMany(x => x.Details).WithOne(d => d.PurchaseEvaluation).HasForeignKey(d => d.PurchaseEvaluationId).OnDelete(DeleteBehavior.Cascade);
|
||||||
|
b.HasMany(x => x.Approvals).WithOne(a => a.PurchaseEvaluation).HasForeignKey(a => a.PurchaseEvaluationId).OnDelete(DeleteBehavior.Cascade);
|
||||||
|
b.HasMany(x => x.Changelogs).WithOne(c => c.PurchaseEvaluation).HasForeignKey(c => c.PurchaseEvaluationId).OnDelete(DeleteBehavior.Cascade);
|
||||||
|
b.HasMany(x => x.Attachments).WithOne(a => a.PurchaseEvaluation).HasForeignKey(a => a.PurchaseEvaluationId).OnDelete(DeleteBehavior.Cascade);
|
||||||
|
// Quotes không FK trực tiếp tới PurchaseEvaluation (đi qua Detail) —
|
||||||
|
// nhưng collection navigation có nên cần config riêng bên dưới.
|
||||||
|
|
||||||
|
b.HasQueryFilter(x => !x.IsDeleted);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class PurchaseEvaluationSupplierConfiguration : IEntityTypeConfiguration<PurchaseEvaluationSupplier>
|
||||||
|
{
|
||||||
|
public void Configure(EntityTypeBuilder<PurchaseEvaluationSupplier> b)
|
||||||
|
{
|
||||||
|
b.ToTable("PurchaseEvaluationSuppliers");
|
||||||
|
b.HasKey(x => x.Id);
|
||||||
|
|
||||||
|
b.Property(x => x.DisplayName).HasMaxLength(200);
|
||||||
|
b.Property(x => x.ContactName).HasMaxLength(200);
|
||||||
|
b.Property(x => x.ContactEmail).HasMaxLength(200);
|
||||||
|
b.Property(x => x.ContactPhone).HasMaxLength(50);
|
||||||
|
b.Property(x => x.PaymentTermText).HasMaxLength(200);
|
||||||
|
b.Property(x => x.Note).HasMaxLength(500);
|
||||||
|
|
||||||
|
b.HasIndex(x => new { x.PurchaseEvaluationId, x.SupplierId }).IsUnique();
|
||||||
|
b.HasIndex(x => x.SupplierId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class PurchaseEvaluationDetailConfiguration : IEntityTypeConfiguration<PurchaseEvaluationDetail>
|
||||||
|
{
|
||||||
|
public void Configure(EntityTypeBuilder<PurchaseEvaluationDetail> b)
|
||||||
|
{
|
||||||
|
b.ToTable("PurchaseEvaluationDetails");
|
||||||
|
b.HasKey(x => x.Id);
|
||||||
|
|
||||||
|
b.Property(x => x.GroupCode).HasMaxLength(50).IsRequired();
|
||||||
|
b.Property(x => x.GroupName).HasMaxLength(200).IsRequired();
|
||||||
|
b.Property(x => x.ItemCode).HasMaxLength(100);
|
||||||
|
b.Property(x => x.NoiDung).HasMaxLength(500).IsRequired();
|
||||||
|
b.Property(x => x.DonViTinh).HasMaxLength(50);
|
||||||
|
b.Property(x => x.GhiChu).HasMaxLength(1000);
|
||||||
|
b.Property(x => x.KhoiLuongNganSach).HasPrecision(18, 4);
|
||||||
|
b.Property(x => x.KhoiLuongThiCong).HasPrecision(18, 4);
|
||||||
|
b.Property(x => x.DonGiaNganSach).HasPrecision(18, 2);
|
||||||
|
b.Property(x => x.ThanhTienNganSach).HasPrecision(18, 2);
|
||||||
|
|
||||||
|
b.HasIndex(x => new { x.PurchaseEvaluationId, x.Order });
|
||||||
|
|
||||||
|
b.HasMany(x => x.Quotes).WithOne(q => q.Detail).HasForeignKey(q => q.PurchaseEvaluationDetailId).OnDelete(DeleteBehavior.Cascade);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class PurchaseEvaluationQuoteConfiguration : IEntityTypeConfiguration<PurchaseEvaluationQuote>
|
||||||
|
{
|
||||||
|
public void Configure(EntityTypeBuilder<PurchaseEvaluationQuote> b)
|
||||||
|
{
|
||||||
|
b.ToTable("PurchaseEvaluationQuotes");
|
||||||
|
b.HasKey(x => x.Id);
|
||||||
|
|
||||||
|
b.Property(x => x.BgVat).HasPrecision(18, 2);
|
||||||
|
b.Property(x => x.ChuaVat).HasPrecision(18, 2);
|
||||||
|
b.Property(x => x.ThanhTien).HasPrecision(18, 2);
|
||||||
|
b.Property(x => x.Note).HasMaxLength(500);
|
||||||
|
|
||||||
|
b.HasIndex(x => new { x.PurchaseEvaluationDetailId, x.PurchaseEvaluationSupplierId }).IsUnique();
|
||||||
|
|
||||||
|
// Quote → Supplier (restrict — không xóa Supplier-row khỏi phiếu nếu còn quote)
|
||||||
|
b.HasOne(x => x.Supplier)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey(x => x.PurchaseEvaluationSupplierId)
|
||||||
|
.OnDelete(DeleteBehavior.Restrict);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class PurchaseEvaluationApprovalConfiguration : IEntityTypeConfiguration<PurchaseEvaluationApproval>
|
||||||
|
{
|
||||||
|
public void Configure(EntityTypeBuilder<PurchaseEvaluationApproval> b)
|
||||||
|
{
|
||||||
|
b.ToTable("PurchaseEvaluationApprovals");
|
||||||
|
b.HasKey(x => x.Id);
|
||||||
|
|
||||||
|
b.Property(x => x.FromPhase).HasConversion<int>();
|
||||||
|
b.Property(x => x.ToPhase).HasConversion<int>();
|
||||||
|
b.Property(x => x.Decision).HasConversion<int>();
|
||||||
|
b.Property(x => x.Comment).HasMaxLength(1000);
|
||||||
|
|
||||||
|
b.HasIndex(x => new { x.PurchaseEvaluationId, x.ApprovedAt });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class PurchaseEvaluationChangelogConfiguration : IEntityTypeConfiguration<PurchaseEvaluationChangelog>
|
||||||
|
{
|
||||||
|
public void Configure(EntityTypeBuilder<PurchaseEvaluationChangelog> b)
|
||||||
|
{
|
||||||
|
b.ToTable("PurchaseEvaluationChangelogs");
|
||||||
|
b.HasKey(x => x.Id);
|
||||||
|
|
||||||
|
b.Property(x => x.EntityType).HasConversion<int>();
|
||||||
|
b.Property(x => x.Action).HasConversion<int>();
|
||||||
|
b.Property(x => x.PhaseAtChange).HasConversion<int>();
|
||||||
|
b.Property(x => x.UserName).HasMaxLength(200);
|
||||||
|
b.Property(x => x.Summary).HasMaxLength(500);
|
||||||
|
b.Property(x => x.ContextNote).HasMaxLength(2000);
|
||||||
|
b.Property(x => x.FieldChangesJson).HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.HasIndex(x => new { x.PurchaseEvaluationId, x.CreatedAt });
|
||||||
|
b.HasIndex(x => new { x.PurchaseEvaluationId, x.EntityType });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class PurchaseEvaluationAttachmentConfiguration : IEntityTypeConfiguration<PurchaseEvaluationAttachment>
|
||||||
|
{
|
||||||
|
public void Configure(EntityTypeBuilder<PurchaseEvaluationAttachment> b)
|
||||||
|
{
|
||||||
|
b.ToTable("PurchaseEvaluationAttachments");
|
||||||
|
b.HasKey(x => x.Id);
|
||||||
|
|
||||||
|
b.Property(x => x.FileName).HasMaxLength(255).IsRequired();
|
||||||
|
b.Property(x => x.StoragePath).HasMaxLength(500).IsRequired();
|
||||||
|
b.Property(x => x.ContentType).HasMaxLength(100).IsRequired();
|
||||||
|
b.Property(x => x.Purpose).HasConversion<int>();
|
||||||
|
b.Property(x => x.Note).HasMaxLength(500);
|
||||||
|
|
||||||
|
b.HasIndex(x => x.PurchaseEvaluationId);
|
||||||
|
b.HasIndex(x => x.PurchaseEvaluationSupplierId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class PurchaseEvaluationWorkflowDefinitionConfiguration : IEntityTypeConfiguration<PurchaseEvaluationWorkflowDefinition>
|
||||||
|
{
|
||||||
|
public void Configure(EntityTypeBuilder<PurchaseEvaluationWorkflowDefinition> e)
|
||||||
|
{
|
||||||
|
e.ToTable("PurchaseEvaluationWorkflowDefinitions");
|
||||||
|
e.Property(x => x.Code).HasMaxLength(100).IsRequired();
|
||||||
|
e.Property(x => x.Name).HasMaxLength(200).IsRequired();
|
||||||
|
e.Property(x => x.Description).HasMaxLength(1000);
|
||||||
|
e.Property(x => x.EvaluationType).HasConversion<int>();
|
||||||
|
|
||||||
|
e.HasIndex(x => new { x.Code, x.Version }).IsUnique();
|
||||||
|
e.HasIndex(x => new { x.EvaluationType, x.IsActive });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class PurchaseEvaluationWorkflowStepConfiguration : IEntityTypeConfiguration<PurchaseEvaluationWorkflowStep>
|
||||||
|
{
|
||||||
|
public void Configure(EntityTypeBuilder<PurchaseEvaluationWorkflowStep> e)
|
||||||
|
{
|
||||||
|
e.ToTable("PurchaseEvaluationWorkflowSteps");
|
||||||
|
e.Property(x => x.Name).HasMaxLength(200).IsRequired();
|
||||||
|
e.Property(x => x.Phase).HasConversion<int>();
|
||||||
|
|
||||||
|
e.HasOne(x => x.Definition)
|
||||||
|
.WithMany(d => d.Steps)
|
||||||
|
.HasForeignKey(x => x.PurchaseEvaluationWorkflowDefinitionId)
|
||||||
|
.OnDelete(DeleteBehavior.Cascade);
|
||||||
|
|
||||||
|
e.HasIndex(x => new { x.PurchaseEvaluationWorkflowDefinitionId, x.Order });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class PurchaseEvaluationWorkflowStepApproverConfiguration : IEntityTypeConfiguration<PurchaseEvaluationWorkflowStepApprover>
|
||||||
|
{
|
||||||
|
public void Configure(EntityTypeBuilder<PurchaseEvaluationWorkflowStepApprover> e)
|
||||||
|
{
|
||||||
|
e.ToTable("PurchaseEvaluationWorkflowStepApprovers");
|
||||||
|
e.Property(x => x.Kind).HasConversion<int>();
|
||||||
|
e.Property(x => x.AssignmentValue).HasMaxLength(100).IsRequired();
|
||||||
|
|
||||||
|
e.HasOne(x => x.Step)
|
||||||
|
.WithMany(s => s.Approvers)
|
||||||
|
.HasForeignKey(x => x.PurchaseEvaluationWorkflowStepId)
|
||||||
|
.OnDelete(DeleteBehavior.Cascade);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -8,6 +8,7 @@ using SolutionErp.Domain.Forms;
|
|||||||
using SolutionErp.Domain.Identity;
|
using SolutionErp.Domain.Identity;
|
||||||
using SolutionErp.Domain.Master;
|
using SolutionErp.Domain.Master;
|
||||||
using SolutionErp.Domain.Master.Catalogs;
|
using SolutionErp.Domain.Master.Catalogs;
|
||||||
|
using SolutionErp.Domain.PurchaseEvaluations;
|
||||||
|
|
||||||
namespace SolutionErp.Infrastructure.Persistence;
|
namespace SolutionErp.Infrastructure.Persistence;
|
||||||
|
|
||||||
@ -37,6 +38,7 @@ public static class DbInitializer
|
|||||||
await SeedDemoMasterDataAsync(db, logger);
|
await SeedDemoMasterDataAsync(db, logger);
|
||||||
await SeedContractTemplatesAsync(db, logger);
|
await SeedContractTemplatesAsync(db, logger);
|
||||||
await SeedWorkflowDefinitionsAsync(db, logger);
|
await SeedWorkflowDefinitionsAsync(db, logger);
|
||||||
|
await SeedPurchaseEvaluationWorkflowsAsync(db, logger);
|
||||||
await SeedCatalogsAsync(db, logger);
|
await SeedCatalogsAsync(db, logger);
|
||||||
|
|
||||||
// Backfill mã HĐ cho HĐ legacy chưa có (sau khi đổi policy gen-tại-create).
|
// Backfill mã HĐ cho HĐ legacy chưa có (sau khi đổi policy gen-tại-create).
|
||||||
@ -317,6 +319,79 @@ public static class DbInitializer
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Seed default workflow v01 cho 2 PurchaseEvaluationType (A NccOnly / B
|
||||||
|
// NccWithPlan) — mirror SeedWorkflowDefinitionsAsync của HĐ. Admin có
|
||||||
|
// thể tạo version mới qua /system/pe-workflows/:typeCode designer.
|
||||||
|
private static async Task SeedPurchaseEvaluationWorkflowsAsync(ApplicationDbContext db, ILogger logger)
|
||||||
|
{
|
||||||
|
var typeLabels = new Dictionary<PurchaseEvaluationType, (string Code, string Name)>
|
||||||
|
{
|
||||||
|
[PurchaseEvaluationType.DuyetNcc] = ("QT-DN-A", "Quy trình Duyệt NCC"),
|
||||||
|
[PurchaseEvaluationType.DuyetNccPhuongAn] = ("QT-DN-B", "Quy trình Duyệt NCC - Phương Án"),
|
||||||
|
};
|
||||||
|
|
||||||
|
var phaseNames = new Dictionary<PurchaseEvaluationPhase, string>
|
||||||
|
{
|
||||||
|
[PurchaseEvaluationPhase.DangSoanThao] = "Soạn thảo",
|
||||||
|
[PurchaseEvaluationPhase.ChoPurchasing] = "Purchasing tổng hợp",
|
||||||
|
[PurchaseEvaluationPhase.ChoDuAn] = "Dự án kiểm tra",
|
||||||
|
[PurchaseEvaluationPhase.ChoCCM] = "CCM kiểm tra ngân sách",
|
||||||
|
[PurchaseEvaluationPhase.ChoCEODuyetPA] = "CEO duyệt phương án",
|
||||||
|
[PurchaseEvaluationPhase.ChoCEODuyetNCC] = "CEO duyệt chọn đơn vị",
|
||||||
|
[PurchaseEvaluationPhase.DaDuyet] = "Đã duyệt",
|
||||||
|
};
|
||||||
|
|
||||||
|
var added = 0;
|
||||||
|
foreach (var (type, info) in typeLabels)
|
||||||
|
{
|
||||||
|
var alreadyExists = await db.PurchaseEvaluationWorkflowDefinitions
|
||||||
|
.AnyAsync(w => w.EvaluationType == type);
|
||||||
|
if (alreadyExists) continue;
|
||||||
|
|
||||||
|
var policy = PurchaseEvaluationPolicyRegistry.For(type);
|
||||||
|
var def = new PurchaseEvaluationWorkflowDefinition
|
||||||
|
{
|
||||||
|
Code = info.Code,
|
||||||
|
Version = 1,
|
||||||
|
EvaluationType = type,
|
||||||
|
Name = $"{info.Name} (v01)",
|
||||||
|
Description = policy.Description,
|
||||||
|
IsActive = true,
|
||||||
|
ActivatedAt = DateTime.UtcNow,
|
||||||
|
Steps = policy.ActivePhases
|
||||||
|
.Where(p => p != PurchaseEvaluationPhase.TuChoi && p != PurchaseEvaluationPhase.DaDuyet)
|
||||||
|
.Select((p, idx) =>
|
||||||
|
{
|
||||||
|
var roles = policy.Transitions
|
||||||
|
.Where(t => t.Key.To == p)
|
||||||
|
.SelectMany(t => t.Value)
|
||||||
|
.Distinct()
|
||||||
|
.ToList();
|
||||||
|
return new PurchaseEvaluationWorkflowStep
|
||||||
|
{
|
||||||
|
Order = idx + 1,
|
||||||
|
Phase = p,
|
||||||
|
Name = phaseNames.GetValueOrDefault(p, p.ToString()),
|
||||||
|
SlaDays = policy.PhaseSla.GetValueOrDefault(p) is TimeSpan s ? (int?)s.Days : null,
|
||||||
|
Approvers = roles.Select(r => new PurchaseEvaluationWorkflowStepApprover
|
||||||
|
{
|
||||||
|
Kind = WorkflowApproverKind.Role,
|
||||||
|
AssignmentValue = r,
|
||||||
|
}).ToList(),
|
||||||
|
};
|
||||||
|
})
|
||||||
|
.ToList(),
|
||||||
|
};
|
||||||
|
db.PurchaseEvaluationWorkflowDefinitions.Add(def);
|
||||||
|
added++;
|
||||||
|
}
|
||||||
|
if (added > 0)
|
||||||
|
{
|
||||||
|
await db.SaveChangesAsync();
|
||||||
|
logger.LogInformation("Seeded {Count} PE workflow definitions (v01)", added);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Map ContractType → preferred supplier code (đa dạng dữ liệu demo theo
|
// Map ContractType → preferred supplier code (đa dạng dữ liệu demo theo
|
||||||
// đúng business: ThauPhu/GiaoKhoan đi với NTP/TĐ, NCC/MuaBan/NTNcc đi
|
// đúng business: ThauPhu/GiaoKhoan đi với NTP/TĐ, NCC/MuaBan/NTNcc đi
|
||||||
// với NCC, DichVu/NTDv đi với DV).
|
// với NCC, DichVu/NTDv đi với DV).
|
||||||
@ -786,6 +861,9 @@ public static class DbInitializer
|
|||||||
(MenuKeys.Roles, "Vai trò", MenuKeys.System, 92, "Shield"),
|
(MenuKeys.Roles, "Vai trò", MenuKeys.System, 92, "Shield"),
|
||||||
(MenuKeys.Permissions, "Phân quyền", MenuKeys.System, 93, "KeyRound"),
|
(MenuKeys.Permissions, "Phân quyền", MenuKeys.System, 93, "KeyRound"),
|
||||||
(MenuKeys.Workflows, "Quy trình HĐ", MenuKeys.System, 94, "GitBranch"),
|
(MenuKeys.Workflows, "Quy trình HĐ", MenuKeys.System, 94, "GitBranch"),
|
||||||
|
// Module Duyệt NCC (tiền-HĐ)
|
||||||
|
(MenuKeys.PurchaseEvaluations, "Quy trình chọn Thầu phụ - NCC", null, 25, "ClipboardCheck"),
|
||||||
|
(MenuKeys.PeWorkflows, "Quy trình Duyệt NCC", MenuKeys.System, 95, "GitCompareArrows"),
|
||||||
};
|
};
|
||||||
|
|
||||||
// Per-type sub-menu under Contracts: 1 group + 3 leaves each
|
// Per-type sub-menu under Contracts: 1 group + 3 leaves each
|
||||||
@ -809,6 +887,30 @@ public static class DbInitializer
|
|||||||
tree.Add((MenuKeys.WorkflowTypeLeaf(code), label, MenuKeys.Workflows, wfOrder++, "FileText"));
|
tree.Add((MenuKeys.WorkflowTypeLeaf(code), label, MenuKeys.Workflows, wfOrder++, "FileText"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Pe_* group per PurchaseEvaluationType + 3 action leaves each
|
||||||
|
var peTypeLabels = new Dictionary<string, string>
|
||||||
|
{
|
||||||
|
["DuyetNcc"] = "Quy trình Duyệt NCC",
|
||||||
|
["DuyetNccPhuongAn"] = "Quy trình Duyệt NCC - Phương Án",
|
||||||
|
};
|
||||||
|
var peOrder = 1;
|
||||||
|
foreach (var code in MenuKeys.PurchaseEvaluationTypeCodes)
|
||||||
|
{
|
||||||
|
var label = peTypeLabels.GetValueOrDefault(code, code);
|
||||||
|
tree.Add((MenuKeys.PurchaseEvaluationGroup(code), label, MenuKeys.PurchaseEvaluations, peOrder++, "FileCheck"));
|
||||||
|
tree.Add((MenuKeys.PurchaseEvaluationList(code), "Danh sách", MenuKeys.PurchaseEvaluationGroup(code), peOrder++, "List"));
|
||||||
|
tree.Add((MenuKeys.PurchaseEvaluationCreate(code), "Thao tác", MenuKeys.PurchaseEvaluationGroup(code), peOrder++, "Plus"));
|
||||||
|
tree.Add((MenuKeys.PurchaseEvaluationPending(code),"Duyệt", MenuKeys.PurchaseEvaluationGroup(code), peOrder++, "CheckCircle2"));
|
||||||
|
}
|
||||||
|
|
||||||
|
// PE workflow admin leaves dưới `PeWorkflows`
|
||||||
|
var peWfOrder = 96;
|
||||||
|
foreach (var code in MenuKeys.PurchaseEvaluationTypeCodes)
|
||||||
|
{
|
||||||
|
var label = peTypeLabels.GetValueOrDefault(code, code);
|
||||||
|
tree.Add((MenuKeys.PeWorkflowTypeLeaf(code), label, MenuKeys.PeWorkflows, peWfOrder++, "FileCheck"));
|
||||||
|
}
|
||||||
|
|
||||||
var existingKeys = await db.MenuItems.Select(m => m.Key).ToListAsync();
|
var existingKeys = await db.MenuItems.Select(m => m.Key).ToListAsync();
|
||||||
var added = 0;
|
var added = 0;
|
||||||
foreach (var (key, label, parent, o, icon) in tree)
|
foreach (var (key, label, parent, o, icon) in tree)
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,455 @@
|
|||||||
|
using System;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace SolutionErp.Infrastructure.Persistence.Migrations
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class AddPurchaseEvaluations : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "PurchaseEvaluations",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
|
||||||
|
MaPhieu = table.Column<string>(type: "nvarchar(100)", maxLength: 100, nullable: true),
|
||||||
|
Type = table.Column<int>(type: "int", nullable: false),
|
||||||
|
Phase = table.Column<int>(type: "int", nullable: false),
|
||||||
|
TenGoiThau = table.Column<string>(type: "nvarchar(500)", maxLength: 500, nullable: false),
|
||||||
|
ProjectId = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
|
||||||
|
DepartmentId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
|
||||||
|
DrafterUserId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
|
||||||
|
DiaDiem = table.Column<string>(type: "nvarchar(500)", maxLength: 500, nullable: true),
|
||||||
|
MoTa = table.Column<string>(type: "nvarchar(2000)", maxLength: 2000, nullable: true),
|
||||||
|
WorkflowDefinitionId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
|
||||||
|
SlaDeadline = table.Column<DateTime>(type: "datetime2", nullable: true),
|
||||||
|
SlaWarningSent = table.Column<bool>(type: "bit", nullable: false),
|
||||||
|
SelectedSupplierId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
|
||||||
|
PaymentTerms = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||||
|
ContractId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
|
||||||
|
CreatedAt = table.Column<DateTime>(type: "datetime2", nullable: false),
|
||||||
|
UpdatedAt = table.Column<DateTime>(type: "datetime2", nullable: true),
|
||||||
|
CreatedBy = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
|
||||||
|
UpdatedBy = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
|
||||||
|
IsDeleted = table.Column<bool>(type: "bit", nullable: false),
|
||||||
|
DeletedAt = table.Column<DateTime>(type: "datetime2", nullable: true),
|
||||||
|
DeletedBy = table.Column<Guid>(type: "uniqueidentifier", nullable: true)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_PurchaseEvaluations", x => x.Id);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "PurchaseEvaluationWorkflowDefinitions",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
|
||||||
|
Code = table.Column<string>(type: "nvarchar(100)", maxLength: 100, nullable: false),
|
||||||
|
Version = table.Column<int>(type: "int", nullable: false),
|
||||||
|
EvaluationType = table.Column<int>(type: "int", nullable: false),
|
||||||
|
Name = table.Column<string>(type: "nvarchar(200)", maxLength: 200, nullable: false),
|
||||||
|
Description = table.Column<string>(type: "nvarchar(1000)", maxLength: 1000, nullable: true),
|
||||||
|
IsActive = table.Column<bool>(type: "bit", nullable: false),
|
||||||
|
ActivatedAt = table.Column<DateTime>(type: "datetime2", nullable: true),
|
||||||
|
CreatedAt = table.Column<DateTime>(type: "datetime2", nullable: false),
|
||||||
|
UpdatedAt = table.Column<DateTime>(type: "datetime2", nullable: true),
|
||||||
|
CreatedBy = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
|
||||||
|
UpdatedBy = table.Column<Guid>(type: "uniqueidentifier", nullable: true)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_PurchaseEvaluationWorkflowDefinitions", x => x.Id);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "PurchaseEvaluationApprovals",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
|
||||||
|
PurchaseEvaluationId = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
|
||||||
|
FromPhase = table.Column<int>(type: "int", nullable: false),
|
||||||
|
ToPhase = table.Column<int>(type: "int", nullable: false),
|
||||||
|
ApproverUserId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
|
||||||
|
Decision = table.Column<int>(type: "int", nullable: false),
|
||||||
|
Comment = table.Column<string>(type: "nvarchar(1000)", maxLength: 1000, nullable: true),
|
||||||
|
ApprovedAt = table.Column<DateTime>(type: "datetime2", nullable: false),
|
||||||
|
CreatedAt = table.Column<DateTime>(type: "datetime2", nullable: false),
|
||||||
|
UpdatedAt = table.Column<DateTime>(type: "datetime2", nullable: true),
|
||||||
|
CreatedBy = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
|
||||||
|
UpdatedBy = table.Column<Guid>(type: "uniqueidentifier", nullable: true)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_PurchaseEvaluationApprovals", x => x.Id);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_PurchaseEvaluationApprovals_PurchaseEvaluations_PurchaseEvaluationId",
|
||||||
|
column: x => x.PurchaseEvaluationId,
|
||||||
|
principalTable: "PurchaseEvaluations",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "PurchaseEvaluationAttachments",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
|
||||||
|
PurchaseEvaluationId = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
|
||||||
|
PurchaseEvaluationSupplierId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
|
||||||
|
FileName = table.Column<string>(type: "nvarchar(255)", maxLength: 255, nullable: false),
|
||||||
|
StoragePath = table.Column<string>(type: "nvarchar(500)", maxLength: 500, nullable: false),
|
||||||
|
FileSize = table.Column<long>(type: "bigint", nullable: false),
|
||||||
|
ContentType = table.Column<string>(type: "nvarchar(100)", maxLength: 100, nullable: false),
|
||||||
|
Purpose = table.Column<int>(type: "int", nullable: false),
|
||||||
|
Note = table.Column<string>(type: "nvarchar(500)", maxLength: 500, nullable: true),
|
||||||
|
CreatedAt = table.Column<DateTime>(type: "datetime2", nullable: false),
|
||||||
|
UpdatedAt = table.Column<DateTime>(type: "datetime2", nullable: true),
|
||||||
|
CreatedBy = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
|
||||||
|
UpdatedBy = table.Column<Guid>(type: "uniqueidentifier", nullable: true)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_PurchaseEvaluationAttachments", x => x.Id);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_PurchaseEvaluationAttachments_PurchaseEvaluations_PurchaseEvaluationId",
|
||||||
|
column: x => x.PurchaseEvaluationId,
|
||||||
|
principalTable: "PurchaseEvaluations",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "PurchaseEvaluationChangelogs",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
|
||||||
|
PurchaseEvaluationId = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
|
||||||
|
EntityType = table.Column<int>(type: "int", nullable: false),
|
||||||
|
EntityId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
|
||||||
|
Action = table.Column<int>(type: "int", nullable: false),
|
||||||
|
PhaseAtChange = table.Column<int>(type: "int", nullable: true),
|
||||||
|
UserId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
|
||||||
|
UserName = table.Column<string>(type: "nvarchar(200)", maxLength: 200, nullable: true),
|
||||||
|
Summary = table.Column<string>(type: "nvarchar(500)", maxLength: 500, nullable: true),
|
||||||
|
FieldChangesJson = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||||
|
ContextNote = table.Column<string>(type: "nvarchar(2000)", maxLength: 2000, nullable: true),
|
||||||
|
CreatedAt = table.Column<DateTime>(type: "datetime2", nullable: false),
|
||||||
|
UpdatedAt = table.Column<DateTime>(type: "datetime2", nullable: true),
|
||||||
|
CreatedBy = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
|
||||||
|
UpdatedBy = table.Column<Guid>(type: "uniqueidentifier", nullable: true)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_PurchaseEvaluationChangelogs", x => x.Id);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_PurchaseEvaluationChangelogs_PurchaseEvaluations_PurchaseEvaluationId",
|
||||||
|
column: x => x.PurchaseEvaluationId,
|
||||||
|
principalTable: "PurchaseEvaluations",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "PurchaseEvaluationDetails",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
|
||||||
|
PurchaseEvaluationId = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
|
||||||
|
GroupCode = table.Column<string>(type: "nvarchar(50)", maxLength: 50, nullable: false),
|
||||||
|
GroupName = table.Column<string>(type: "nvarchar(200)", maxLength: 200, nullable: false),
|
||||||
|
ItemCode = table.Column<string>(type: "nvarchar(100)", maxLength: 100, nullable: true),
|
||||||
|
NoiDung = table.Column<string>(type: "nvarchar(500)", maxLength: 500, nullable: false),
|
||||||
|
DonViTinh = table.Column<string>(type: "nvarchar(50)", maxLength: 50, nullable: true),
|
||||||
|
KhoiLuongNganSach = table.Column<decimal>(type: "decimal(18,4)", precision: 18, scale: 4, nullable: false),
|
||||||
|
KhoiLuongThiCong = table.Column<decimal>(type: "decimal(18,4)", precision: 18, scale: 4, nullable: false),
|
||||||
|
DonGiaNganSach = table.Column<decimal>(type: "decimal(18,2)", precision: 18, scale: 2, nullable: false),
|
||||||
|
ThanhTienNganSach = table.Column<decimal>(type: "decimal(18,2)", precision: 18, scale: 2, nullable: false),
|
||||||
|
Order = table.Column<int>(type: "int", nullable: false),
|
||||||
|
GhiChu = table.Column<string>(type: "nvarchar(1000)", maxLength: 1000, nullable: true),
|
||||||
|
CreatedAt = table.Column<DateTime>(type: "datetime2", nullable: false),
|
||||||
|
UpdatedAt = table.Column<DateTime>(type: "datetime2", nullable: true),
|
||||||
|
CreatedBy = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
|
||||||
|
UpdatedBy = table.Column<Guid>(type: "uniqueidentifier", nullable: true)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_PurchaseEvaluationDetails", x => x.Id);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_PurchaseEvaluationDetails_PurchaseEvaluations_PurchaseEvaluationId",
|
||||||
|
column: x => x.PurchaseEvaluationId,
|
||||||
|
principalTable: "PurchaseEvaluations",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "PurchaseEvaluationSuppliers",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
|
||||||
|
PurchaseEvaluationId = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
|
||||||
|
SupplierId = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
|
||||||
|
DisplayName = table.Column<string>(type: "nvarchar(200)", maxLength: 200, nullable: true),
|
||||||
|
ContactName = table.Column<string>(type: "nvarchar(200)", maxLength: 200, nullable: true),
|
||||||
|
ContactEmail = table.Column<string>(type: "nvarchar(200)", maxLength: 200, nullable: true),
|
||||||
|
ContactPhone = table.Column<string>(type: "nvarchar(50)", maxLength: 50, nullable: true),
|
||||||
|
PaymentTermText = table.Column<string>(type: "nvarchar(200)", maxLength: 200, nullable: true),
|
||||||
|
Note = table.Column<string>(type: "nvarchar(500)", maxLength: 500, nullable: true),
|
||||||
|
Order = table.Column<int>(type: "int", nullable: false),
|
||||||
|
CreatedAt = table.Column<DateTime>(type: "datetime2", nullable: false),
|
||||||
|
UpdatedAt = table.Column<DateTime>(type: "datetime2", nullable: true),
|
||||||
|
CreatedBy = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
|
||||||
|
UpdatedBy = table.Column<Guid>(type: "uniqueidentifier", nullable: true)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_PurchaseEvaluationSuppliers", x => x.Id);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_PurchaseEvaluationSuppliers_PurchaseEvaluations_PurchaseEvaluationId",
|
||||||
|
column: x => x.PurchaseEvaluationId,
|
||||||
|
principalTable: "PurchaseEvaluations",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "PurchaseEvaluationWorkflowSteps",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
|
||||||
|
PurchaseEvaluationWorkflowDefinitionId = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
|
||||||
|
Order = table.Column<int>(type: "int", nullable: false),
|
||||||
|
Phase = table.Column<int>(type: "int", nullable: false),
|
||||||
|
Name = table.Column<string>(type: "nvarchar(200)", maxLength: 200, nullable: false),
|
||||||
|
SlaDays = table.Column<int>(type: "int", nullable: true),
|
||||||
|
CreatedAt = table.Column<DateTime>(type: "datetime2", nullable: false),
|
||||||
|
UpdatedAt = table.Column<DateTime>(type: "datetime2", nullable: true),
|
||||||
|
CreatedBy = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
|
||||||
|
UpdatedBy = table.Column<Guid>(type: "uniqueidentifier", nullable: true)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_PurchaseEvaluationWorkflowSteps", x => x.Id);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_PurchaseEvaluationWorkflowSteps_PurchaseEvaluationWorkflowDefinitions_PurchaseEvaluationWorkflowDefinitionId",
|
||||||
|
column: x => x.PurchaseEvaluationWorkflowDefinitionId,
|
||||||
|
principalTable: "PurchaseEvaluationWorkflowDefinitions",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "PurchaseEvaluationQuotes",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
|
||||||
|
PurchaseEvaluationDetailId = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
|
||||||
|
PurchaseEvaluationSupplierId = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
|
||||||
|
BgVat = table.Column<decimal>(type: "decimal(18,2)", precision: 18, scale: 2, nullable: false),
|
||||||
|
ChuaVat = table.Column<decimal>(type: "decimal(18,2)", precision: 18, scale: 2, nullable: false),
|
||||||
|
ThanhTien = table.Column<decimal>(type: "decimal(18,2)", precision: 18, scale: 2, nullable: false),
|
||||||
|
IsSelected = table.Column<bool>(type: "bit", nullable: false),
|
||||||
|
Note = table.Column<string>(type: "nvarchar(500)", maxLength: 500, nullable: true),
|
||||||
|
PurchaseEvaluationId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
|
||||||
|
CreatedAt = table.Column<DateTime>(type: "datetime2", nullable: false),
|
||||||
|
UpdatedAt = table.Column<DateTime>(type: "datetime2", nullable: true),
|
||||||
|
CreatedBy = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
|
||||||
|
UpdatedBy = table.Column<Guid>(type: "uniqueidentifier", nullable: true)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_PurchaseEvaluationQuotes", x => x.Id);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_PurchaseEvaluationQuotes_PurchaseEvaluationDetails_PurchaseEvaluationDetailId",
|
||||||
|
column: x => x.PurchaseEvaluationDetailId,
|
||||||
|
principalTable: "PurchaseEvaluationDetails",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_PurchaseEvaluationQuotes_PurchaseEvaluationSuppliers_PurchaseEvaluationSupplierId",
|
||||||
|
column: x => x.PurchaseEvaluationSupplierId,
|
||||||
|
principalTable: "PurchaseEvaluationSuppliers",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Restrict);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_PurchaseEvaluationQuotes_PurchaseEvaluations_PurchaseEvaluationId",
|
||||||
|
column: x => x.PurchaseEvaluationId,
|
||||||
|
principalTable: "PurchaseEvaluations",
|
||||||
|
principalColumn: "Id");
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "PurchaseEvaluationWorkflowStepApprovers",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
|
||||||
|
PurchaseEvaluationWorkflowStepId = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
|
||||||
|
Kind = table.Column<int>(type: "int", nullable: false),
|
||||||
|
AssignmentValue = table.Column<string>(type: "nvarchar(100)", maxLength: 100, nullable: false),
|
||||||
|
CreatedAt = table.Column<DateTime>(type: "datetime2", nullable: false),
|
||||||
|
UpdatedAt = table.Column<DateTime>(type: "datetime2", nullable: true),
|
||||||
|
CreatedBy = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
|
||||||
|
UpdatedBy = table.Column<Guid>(type: "uniqueidentifier", nullable: true)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_PurchaseEvaluationWorkflowStepApprovers", x => x.Id);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_PurchaseEvaluationWorkflowStepApprovers_PurchaseEvaluationWorkflowSteps_PurchaseEvaluationWorkflowStepId",
|
||||||
|
column: x => x.PurchaseEvaluationWorkflowStepId,
|
||||||
|
principalTable: "PurchaseEvaluationWorkflowSteps",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_PurchaseEvaluationApprovals_PurchaseEvaluationId_ApprovedAt",
|
||||||
|
table: "PurchaseEvaluationApprovals",
|
||||||
|
columns: new[] { "PurchaseEvaluationId", "ApprovedAt" });
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_PurchaseEvaluationAttachments_PurchaseEvaluationId",
|
||||||
|
table: "PurchaseEvaluationAttachments",
|
||||||
|
column: "PurchaseEvaluationId");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_PurchaseEvaluationAttachments_PurchaseEvaluationSupplierId",
|
||||||
|
table: "PurchaseEvaluationAttachments",
|
||||||
|
column: "PurchaseEvaluationSupplierId");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_PurchaseEvaluationChangelogs_PurchaseEvaluationId_CreatedAt",
|
||||||
|
table: "PurchaseEvaluationChangelogs",
|
||||||
|
columns: new[] { "PurchaseEvaluationId", "CreatedAt" });
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_PurchaseEvaluationChangelogs_PurchaseEvaluationId_EntityType",
|
||||||
|
table: "PurchaseEvaluationChangelogs",
|
||||||
|
columns: new[] { "PurchaseEvaluationId", "EntityType" });
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_PurchaseEvaluationDetails_PurchaseEvaluationId_Order",
|
||||||
|
table: "PurchaseEvaluationDetails",
|
||||||
|
columns: new[] { "PurchaseEvaluationId", "Order" });
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_PurchaseEvaluationQuotes_PurchaseEvaluationDetailId_PurchaseEvaluationSupplierId",
|
||||||
|
table: "PurchaseEvaluationQuotes",
|
||||||
|
columns: new[] { "PurchaseEvaluationDetailId", "PurchaseEvaluationSupplierId" },
|
||||||
|
unique: true);
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_PurchaseEvaluationQuotes_PurchaseEvaluationId",
|
||||||
|
table: "PurchaseEvaluationQuotes",
|
||||||
|
column: "PurchaseEvaluationId");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_PurchaseEvaluationQuotes_PurchaseEvaluationSupplierId",
|
||||||
|
table: "PurchaseEvaluationQuotes",
|
||||||
|
column: "PurchaseEvaluationSupplierId");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_PurchaseEvaluations_ContractId",
|
||||||
|
table: "PurchaseEvaluations",
|
||||||
|
column: "ContractId");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_PurchaseEvaluations_MaPhieu",
|
||||||
|
table: "PurchaseEvaluations",
|
||||||
|
column: "MaPhieu",
|
||||||
|
unique: true,
|
||||||
|
filter: "[MaPhieu] IS NOT NULL");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_PurchaseEvaluations_Phase_IsDeleted",
|
||||||
|
table: "PurchaseEvaluations",
|
||||||
|
columns: new[] { "Phase", "IsDeleted" });
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_PurchaseEvaluations_ProjectId",
|
||||||
|
table: "PurchaseEvaluations",
|
||||||
|
column: "ProjectId");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_PurchaseEvaluations_SlaDeadline",
|
||||||
|
table: "PurchaseEvaluations",
|
||||||
|
column: "SlaDeadline");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_PurchaseEvaluations_WorkflowDefinitionId",
|
||||||
|
table: "PurchaseEvaluations",
|
||||||
|
column: "WorkflowDefinitionId");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_PurchaseEvaluationSuppliers_PurchaseEvaluationId_SupplierId",
|
||||||
|
table: "PurchaseEvaluationSuppliers",
|
||||||
|
columns: new[] { "PurchaseEvaluationId", "SupplierId" },
|
||||||
|
unique: true);
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_PurchaseEvaluationSuppliers_SupplierId",
|
||||||
|
table: "PurchaseEvaluationSuppliers",
|
||||||
|
column: "SupplierId");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_PurchaseEvaluationWorkflowDefinitions_Code_Version",
|
||||||
|
table: "PurchaseEvaluationWorkflowDefinitions",
|
||||||
|
columns: new[] { "Code", "Version" },
|
||||||
|
unique: true);
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_PurchaseEvaluationWorkflowDefinitions_EvaluationType_IsActive",
|
||||||
|
table: "PurchaseEvaluationWorkflowDefinitions",
|
||||||
|
columns: new[] { "EvaluationType", "IsActive" });
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_PurchaseEvaluationWorkflowStepApprovers_PurchaseEvaluationWorkflowStepId",
|
||||||
|
table: "PurchaseEvaluationWorkflowStepApprovers",
|
||||||
|
column: "PurchaseEvaluationWorkflowStepId");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_PurchaseEvaluationWorkflowSteps_PurchaseEvaluationWorkflowDefinitionId_Order",
|
||||||
|
table: "PurchaseEvaluationWorkflowSteps",
|
||||||
|
columns: new[] { "PurchaseEvaluationWorkflowDefinitionId", "Order" });
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "PurchaseEvaluationApprovals");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "PurchaseEvaluationAttachments");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "PurchaseEvaluationChangelogs");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "PurchaseEvaluationQuotes");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "PurchaseEvaluationWorkflowStepApprovers");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "PurchaseEvaluationDetails");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "PurchaseEvaluationSuppliers");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "PurchaseEvaluationWorkflowSteps");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "PurchaseEvaluations");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "PurchaseEvaluationWorkflowDefinitions");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1904,6 +1904,592 @@ namespace SolutionErp.Infrastructure.Persistence.Migrations
|
|||||||
b.ToTable("Notifications", (string)null);
|
b.ToTable("Notifications", (string)null);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("SolutionErp.Domain.PurchaseEvaluations.PurchaseEvaluation", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
|
b.Property<Guid?>("ContractId")
|
||||||
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("datetime2");
|
||||||
|
|
||||||
|
b.Property<Guid?>("CreatedBy")
|
||||||
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("DeletedAt")
|
||||||
|
.HasColumnType("datetime2");
|
||||||
|
|
||||||
|
b.Property<Guid?>("DeletedBy")
|
||||||
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
|
b.Property<Guid?>("DepartmentId")
|
||||||
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
|
b.Property<string>("DiaDiem")
|
||||||
|
.HasMaxLength(500)
|
||||||
|
.HasColumnType("nvarchar(500)");
|
||||||
|
|
||||||
|
b.Property<Guid?>("DrafterUserId")
|
||||||
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
|
b.Property<bool>("IsDeleted")
|
||||||
|
.HasColumnType("bit");
|
||||||
|
|
||||||
|
b.Property<string>("MaPhieu")
|
||||||
|
.HasMaxLength(100)
|
||||||
|
.HasColumnType("nvarchar(100)");
|
||||||
|
|
||||||
|
b.Property<string>("MoTa")
|
||||||
|
.HasMaxLength(2000)
|
||||||
|
.HasColumnType("nvarchar(2000)");
|
||||||
|
|
||||||
|
b.Property<string>("PaymentTerms")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<int>("Phase")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<Guid>("ProjectId")
|
||||||
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
|
b.Property<Guid?>("SelectedSupplierId")
|
||||||
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("SlaDeadline")
|
||||||
|
.HasColumnType("datetime2");
|
||||||
|
|
||||||
|
b.Property<bool>("SlaWarningSent")
|
||||||
|
.HasColumnType("bit");
|
||||||
|
|
||||||
|
b.Property<string>("TenGoiThau")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(500)
|
||||||
|
.HasColumnType("nvarchar(500)");
|
||||||
|
|
||||||
|
b.Property<int>("Type")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("UpdatedAt")
|
||||||
|
.HasColumnType("datetime2");
|
||||||
|
|
||||||
|
b.Property<Guid?>("UpdatedBy")
|
||||||
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
|
b.Property<Guid?>("WorkflowDefinitionId")
|
||||||
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("ContractId");
|
||||||
|
|
||||||
|
b.HasIndex("MaPhieu")
|
||||||
|
.IsUnique()
|
||||||
|
.HasFilter("[MaPhieu] IS NOT NULL");
|
||||||
|
|
||||||
|
b.HasIndex("ProjectId");
|
||||||
|
|
||||||
|
b.HasIndex("SlaDeadline");
|
||||||
|
|
||||||
|
b.HasIndex("WorkflowDefinitionId");
|
||||||
|
|
||||||
|
b.HasIndex("Phase", "IsDeleted");
|
||||||
|
|
||||||
|
b.ToTable("PurchaseEvaluations", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("SolutionErp.Domain.PurchaseEvaluations.PurchaseEvaluationApproval", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
|
b.Property<DateTime>("ApprovedAt")
|
||||||
|
.HasColumnType("datetime2");
|
||||||
|
|
||||||
|
b.Property<Guid?>("ApproverUserId")
|
||||||
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
|
b.Property<string>("Comment")
|
||||||
|
.HasMaxLength(1000)
|
||||||
|
.HasColumnType("nvarchar(1000)");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("datetime2");
|
||||||
|
|
||||||
|
b.Property<Guid?>("CreatedBy")
|
||||||
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
|
b.Property<int>("Decision")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<int>("FromPhase")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<Guid>("PurchaseEvaluationId")
|
||||||
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
|
b.Property<int>("ToPhase")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("UpdatedAt")
|
||||||
|
.HasColumnType("datetime2");
|
||||||
|
|
||||||
|
b.Property<Guid?>("UpdatedBy")
|
||||||
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("PurchaseEvaluationId", "ApprovedAt");
|
||||||
|
|
||||||
|
b.ToTable("PurchaseEvaluationApprovals", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("SolutionErp.Domain.PurchaseEvaluations.PurchaseEvaluationAttachment", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
|
b.Property<string>("ContentType")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(100)
|
||||||
|
.HasColumnType("nvarchar(100)");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("datetime2");
|
||||||
|
|
||||||
|
b.Property<Guid?>("CreatedBy")
|
||||||
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
|
b.Property<string>("FileName")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(255)
|
||||||
|
.HasColumnType("nvarchar(255)");
|
||||||
|
|
||||||
|
b.Property<long>("FileSize")
|
||||||
|
.HasColumnType("bigint");
|
||||||
|
|
||||||
|
b.Property<string>("Note")
|
||||||
|
.HasMaxLength(500)
|
||||||
|
.HasColumnType("nvarchar(500)");
|
||||||
|
|
||||||
|
b.Property<Guid>("PurchaseEvaluationId")
|
||||||
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
|
b.Property<Guid?>("PurchaseEvaluationSupplierId")
|
||||||
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
|
b.Property<int>("Purpose")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<string>("StoragePath")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(500)
|
||||||
|
.HasColumnType("nvarchar(500)");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("UpdatedAt")
|
||||||
|
.HasColumnType("datetime2");
|
||||||
|
|
||||||
|
b.Property<Guid?>("UpdatedBy")
|
||||||
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("PurchaseEvaluationId");
|
||||||
|
|
||||||
|
b.HasIndex("PurchaseEvaluationSupplierId");
|
||||||
|
|
||||||
|
b.ToTable("PurchaseEvaluationAttachments", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("SolutionErp.Domain.PurchaseEvaluations.PurchaseEvaluationChangelog", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
|
b.Property<int>("Action")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<string>("ContextNote")
|
||||||
|
.HasMaxLength(2000)
|
||||||
|
.HasColumnType("nvarchar(2000)");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("datetime2");
|
||||||
|
|
||||||
|
b.Property<Guid?>("CreatedBy")
|
||||||
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
|
b.Property<Guid?>("EntityId")
|
||||||
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
|
b.Property<int>("EntityType")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<string>("FieldChangesJson")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<int?>("PhaseAtChange")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<Guid>("PurchaseEvaluationId")
|
||||||
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
|
b.Property<string>("Summary")
|
||||||
|
.HasMaxLength(500)
|
||||||
|
.HasColumnType("nvarchar(500)");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("UpdatedAt")
|
||||||
|
.HasColumnType("datetime2");
|
||||||
|
|
||||||
|
b.Property<Guid?>("UpdatedBy")
|
||||||
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
|
b.Property<Guid?>("UserId")
|
||||||
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
|
b.Property<string>("UserName")
|
||||||
|
.HasMaxLength(200)
|
||||||
|
.HasColumnType("nvarchar(200)");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("PurchaseEvaluationId", "CreatedAt");
|
||||||
|
|
||||||
|
b.HasIndex("PurchaseEvaluationId", "EntityType");
|
||||||
|
|
||||||
|
b.ToTable("PurchaseEvaluationChangelogs", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("SolutionErp.Domain.PurchaseEvaluations.PurchaseEvaluationDetail", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("datetime2");
|
||||||
|
|
||||||
|
b.Property<Guid?>("CreatedBy")
|
||||||
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
|
b.Property<decimal>("DonGiaNganSach")
|
||||||
|
.HasPrecision(18, 2)
|
||||||
|
.HasColumnType("decimal(18,2)");
|
||||||
|
|
||||||
|
b.Property<string>("DonViTinh")
|
||||||
|
.HasMaxLength(50)
|
||||||
|
.HasColumnType("nvarchar(50)");
|
||||||
|
|
||||||
|
b.Property<string>("GhiChu")
|
||||||
|
.HasMaxLength(1000)
|
||||||
|
.HasColumnType("nvarchar(1000)");
|
||||||
|
|
||||||
|
b.Property<string>("GroupCode")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(50)
|
||||||
|
.HasColumnType("nvarchar(50)");
|
||||||
|
|
||||||
|
b.Property<string>("GroupName")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(200)
|
||||||
|
.HasColumnType("nvarchar(200)");
|
||||||
|
|
||||||
|
b.Property<string>("ItemCode")
|
||||||
|
.HasMaxLength(100)
|
||||||
|
.HasColumnType("nvarchar(100)");
|
||||||
|
|
||||||
|
b.Property<decimal>("KhoiLuongNganSach")
|
||||||
|
.HasPrecision(18, 4)
|
||||||
|
.HasColumnType("decimal(18,4)");
|
||||||
|
|
||||||
|
b.Property<decimal>("KhoiLuongThiCong")
|
||||||
|
.HasPrecision(18, 4)
|
||||||
|
.HasColumnType("decimal(18,4)");
|
||||||
|
|
||||||
|
b.Property<string>("NoiDung")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(500)
|
||||||
|
.HasColumnType("nvarchar(500)");
|
||||||
|
|
||||||
|
b.Property<int>("Order")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<Guid>("PurchaseEvaluationId")
|
||||||
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
|
b.Property<decimal>("ThanhTienNganSach")
|
||||||
|
.HasPrecision(18, 2)
|
||||||
|
.HasColumnType("decimal(18,2)");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("UpdatedAt")
|
||||||
|
.HasColumnType("datetime2");
|
||||||
|
|
||||||
|
b.Property<Guid?>("UpdatedBy")
|
||||||
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("PurchaseEvaluationId", "Order");
|
||||||
|
|
||||||
|
b.ToTable("PurchaseEvaluationDetails", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("SolutionErp.Domain.PurchaseEvaluations.PurchaseEvaluationQuote", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
|
b.Property<decimal>("BgVat")
|
||||||
|
.HasPrecision(18, 2)
|
||||||
|
.HasColumnType("decimal(18,2)");
|
||||||
|
|
||||||
|
b.Property<decimal>("ChuaVat")
|
||||||
|
.HasPrecision(18, 2)
|
||||||
|
.HasColumnType("decimal(18,2)");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("datetime2");
|
||||||
|
|
||||||
|
b.Property<Guid?>("CreatedBy")
|
||||||
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
|
b.Property<bool>("IsSelected")
|
||||||
|
.HasColumnType("bit");
|
||||||
|
|
||||||
|
b.Property<string>("Note")
|
||||||
|
.HasMaxLength(500)
|
||||||
|
.HasColumnType("nvarchar(500)");
|
||||||
|
|
||||||
|
b.Property<Guid>("PurchaseEvaluationDetailId")
|
||||||
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
|
b.Property<Guid?>("PurchaseEvaluationId")
|
||||||
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
|
b.Property<Guid>("PurchaseEvaluationSupplierId")
|
||||||
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
|
b.Property<decimal>("ThanhTien")
|
||||||
|
.HasPrecision(18, 2)
|
||||||
|
.HasColumnType("decimal(18,2)");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("UpdatedAt")
|
||||||
|
.HasColumnType("datetime2");
|
||||||
|
|
||||||
|
b.Property<Guid?>("UpdatedBy")
|
||||||
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("PurchaseEvaluationId");
|
||||||
|
|
||||||
|
b.HasIndex("PurchaseEvaluationSupplierId");
|
||||||
|
|
||||||
|
b.HasIndex("PurchaseEvaluationDetailId", "PurchaseEvaluationSupplierId")
|
||||||
|
.IsUnique();
|
||||||
|
|
||||||
|
b.ToTable("PurchaseEvaluationQuotes", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("SolutionErp.Domain.PurchaseEvaluations.PurchaseEvaluationSupplier", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
|
b.Property<string>("ContactEmail")
|
||||||
|
.HasMaxLength(200)
|
||||||
|
.HasColumnType("nvarchar(200)");
|
||||||
|
|
||||||
|
b.Property<string>("ContactName")
|
||||||
|
.HasMaxLength(200)
|
||||||
|
.HasColumnType("nvarchar(200)");
|
||||||
|
|
||||||
|
b.Property<string>("ContactPhone")
|
||||||
|
.HasMaxLength(50)
|
||||||
|
.HasColumnType("nvarchar(50)");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("datetime2");
|
||||||
|
|
||||||
|
b.Property<Guid?>("CreatedBy")
|
||||||
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
|
b.Property<string>("DisplayName")
|
||||||
|
.HasMaxLength(200)
|
||||||
|
.HasColumnType("nvarchar(200)");
|
||||||
|
|
||||||
|
b.Property<string>("Note")
|
||||||
|
.HasMaxLength(500)
|
||||||
|
.HasColumnType("nvarchar(500)");
|
||||||
|
|
||||||
|
b.Property<int>("Order")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<string>("PaymentTermText")
|
||||||
|
.HasMaxLength(200)
|
||||||
|
.HasColumnType("nvarchar(200)");
|
||||||
|
|
||||||
|
b.Property<Guid>("PurchaseEvaluationId")
|
||||||
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
|
b.Property<Guid>("SupplierId")
|
||||||
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("UpdatedAt")
|
||||||
|
.HasColumnType("datetime2");
|
||||||
|
|
||||||
|
b.Property<Guid?>("UpdatedBy")
|
||||||
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("SupplierId");
|
||||||
|
|
||||||
|
b.HasIndex("PurchaseEvaluationId", "SupplierId")
|
||||||
|
.IsUnique();
|
||||||
|
|
||||||
|
b.ToTable("PurchaseEvaluationSuppliers", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("SolutionErp.Domain.PurchaseEvaluations.PurchaseEvaluationWorkflowDefinition", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("ActivatedAt")
|
||||||
|
.HasColumnType("datetime2");
|
||||||
|
|
||||||
|
b.Property<string>("Code")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(100)
|
||||||
|
.HasColumnType("nvarchar(100)");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("datetime2");
|
||||||
|
|
||||||
|
b.Property<Guid?>("CreatedBy")
|
||||||
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
|
b.Property<string>("Description")
|
||||||
|
.HasMaxLength(1000)
|
||||||
|
.HasColumnType("nvarchar(1000)");
|
||||||
|
|
||||||
|
b.Property<int>("EvaluationType")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<bool>("IsActive")
|
||||||
|
.HasColumnType("bit");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(200)
|
||||||
|
.HasColumnType("nvarchar(200)");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("UpdatedAt")
|
||||||
|
.HasColumnType("datetime2");
|
||||||
|
|
||||||
|
b.Property<Guid?>("UpdatedBy")
|
||||||
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
|
b.Property<int>("Version")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("Code", "Version")
|
||||||
|
.IsUnique();
|
||||||
|
|
||||||
|
b.HasIndex("EvaluationType", "IsActive");
|
||||||
|
|
||||||
|
b.ToTable("PurchaseEvaluationWorkflowDefinitions", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("SolutionErp.Domain.PurchaseEvaluations.PurchaseEvaluationWorkflowStep", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("datetime2");
|
||||||
|
|
||||||
|
b.Property<Guid?>("CreatedBy")
|
||||||
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(200)
|
||||||
|
.HasColumnType("nvarchar(200)");
|
||||||
|
|
||||||
|
b.Property<int>("Order")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<int>("Phase")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<Guid>("PurchaseEvaluationWorkflowDefinitionId")
|
||||||
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
|
b.Property<int?>("SlaDays")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("UpdatedAt")
|
||||||
|
.HasColumnType("datetime2");
|
||||||
|
|
||||||
|
b.Property<Guid?>("UpdatedBy")
|
||||||
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("PurchaseEvaluationWorkflowDefinitionId", "Order");
|
||||||
|
|
||||||
|
b.ToTable("PurchaseEvaluationWorkflowSteps", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("SolutionErp.Domain.PurchaseEvaluations.PurchaseEvaluationWorkflowStepApprover", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
|
b.Property<string>("AssignmentValue")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(100)
|
||||||
|
.HasColumnType("nvarchar(100)");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("datetime2");
|
||||||
|
|
||||||
|
b.Property<Guid?>("CreatedBy")
|
||||||
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
|
b.Property<int>("Kind")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<Guid>("PurchaseEvaluationWorkflowStepId")
|
||||||
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("UpdatedAt")
|
||||||
|
.HasColumnType("datetime2");
|
||||||
|
|
||||||
|
b.Property<Guid?>("UpdatedBy")
|
||||||
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("PurchaseEvaluationWorkflowStepId");
|
||||||
|
|
||||||
|
b.ToTable("PurchaseEvaluationWorkflowStepApprovers", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<System.Guid>", b =>
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<System.Guid>", b =>
|
||||||
{
|
{
|
||||||
b.HasOne("SolutionErp.Domain.Identity.Role", null)
|
b.HasOne("SolutionErp.Domain.Identity.Role", null)
|
||||||
@ -2135,6 +2721,106 @@ namespace SolutionErp.Infrastructure.Persistence.Migrations
|
|||||||
.OnDelete(DeleteBehavior.Restrict);
|
.OnDelete(DeleteBehavior.Restrict);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("SolutionErp.Domain.PurchaseEvaluations.PurchaseEvaluationApproval", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("SolutionErp.Domain.PurchaseEvaluations.PurchaseEvaluation", "PurchaseEvaluation")
|
||||||
|
.WithMany("Approvals")
|
||||||
|
.HasForeignKey("PurchaseEvaluationId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("PurchaseEvaluation");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("SolutionErp.Domain.PurchaseEvaluations.PurchaseEvaluationAttachment", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("SolutionErp.Domain.PurchaseEvaluations.PurchaseEvaluation", "PurchaseEvaluation")
|
||||||
|
.WithMany("Attachments")
|
||||||
|
.HasForeignKey("PurchaseEvaluationId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("PurchaseEvaluation");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("SolutionErp.Domain.PurchaseEvaluations.PurchaseEvaluationChangelog", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("SolutionErp.Domain.PurchaseEvaluations.PurchaseEvaluation", "PurchaseEvaluation")
|
||||||
|
.WithMany("Changelogs")
|
||||||
|
.HasForeignKey("PurchaseEvaluationId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("PurchaseEvaluation");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("SolutionErp.Domain.PurchaseEvaluations.PurchaseEvaluationDetail", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("SolutionErp.Domain.PurchaseEvaluations.PurchaseEvaluation", "PurchaseEvaluation")
|
||||||
|
.WithMany("Details")
|
||||||
|
.HasForeignKey("PurchaseEvaluationId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("PurchaseEvaluation");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("SolutionErp.Domain.PurchaseEvaluations.PurchaseEvaluationQuote", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("SolutionErp.Domain.PurchaseEvaluations.PurchaseEvaluationDetail", "Detail")
|
||||||
|
.WithMany("Quotes")
|
||||||
|
.HasForeignKey("PurchaseEvaluationDetailId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasOne("SolutionErp.Domain.PurchaseEvaluations.PurchaseEvaluation", null)
|
||||||
|
.WithMany("Quotes")
|
||||||
|
.HasForeignKey("PurchaseEvaluationId");
|
||||||
|
|
||||||
|
b.HasOne("SolutionErp.Domain.PurchaseEvaluations.PurchaseEvaluationSupplier", "Supplier")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("PurchaseEvaluationSupplierId")
|
||||||
|
.OnDelete(DeleteBehavior.Restrict)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("Detail");
|
||||||
|
|
||||||
|
b.Navigation("Supplier");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("SolutionErp.Domain.PurchaseEvaluations.PurchaseEvaluationSupplier", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("SolutionErp.Domain.PurchaseEvaluations.PurchaseEvaluation", "PurchaseEvaluation")
|
||||||
|
.WithMany("Suppliers")
|
||||||
|
.HasForeignKey("PurchaseEvaluationId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("PurchaseEvaluation");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("SolutionErp.Domain.PurchaseEvaluations.PurchaseEvaluationWorkflowStep", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("SolutionErp.Domain.PurchaseEvaluations.PurchaseEvaluationWorkflowDefinition", "Definition")
|
||||||
|
.WithMany("Steps")
|
||||||
|
.HasForeignKey("PurchaseEvaluationWorkflowDefinitionId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("Definition");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("SolutionErp.Domain.PurchaseEvaluations.PurchaseEvaluationWorkflowStepApprover", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("SolutionErp.Domain.PurchaseEvaluations.PurchaseEvaluationWorkflowStep", "Step")
|
||||||
|
.WithMany("Approvers")
|
||||||
|
.HasForeignKey("PurchaseEvaluationWorkflowStepId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("Step");
|
||||||
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("SolutionErp.Domain.Contracts.Contract", b =>
|
modelBuilder.Entity("SolutionErp.Domain.Contracts.Contract", b =>
|
||||||
{
|
{
|
||||||
b.Navigation("Approvals");
|
b.Navigation("Approvals");
|
||||||
@ -2176,6 +2862,36 @@ namespace SolutionErp.Infrastructure.Persistence.Migrations
|
|||||||
|
|
||||||
b.Navigation("Permissions");
|
b.Navigation("Permissions");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("SolutionErp.Domain.PurchaseEvaluations.PurchaseEvaluation", b =>
|
||||||
|
{
|
||||||
|
b.Navigation("Approvals");
|
||||||
|
|
||||||
|
b.Navigation("Attachments");
|
||||||
|
|
||||||
|
b.Navigation("Changelogs");
|
||||||
|
|
||||||
|
b.Navigation("Details");
|
||||||
|
|
||||||
|
b.Navigation("Quotes");
|
||||||
|
|
||||||
|
b.Navigation("Suppliers");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("SolutionErp.Domain.PurchaseEvaluations.PurchaseEvaluationDetail", b =>
|
||||||
|
{
|
||||||
|
b.Navigation("Quotes");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("SolutionErp.Domain.PurchaseEvaluations.PurchaseEvaluationWorkflowDefinition", b =>
|
||||||
|
{
|
||||||
|
b.Navigation("Steps");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("SolutionErp.Domain.PurchaseEvaluations.PurchaseEvaluationWorkflowStep", b =>
|
||||||
|
{
|
||||||
|
b.Navigation("Approvers");
|
||||||
|
});
|
||||||
#pragma warning restore 612, 618
|
#pragma warning restore 612, 618
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user