[CLAUDE] Domain+Infra: Migration 16 — 2-stage dept approval + smart reject schema
Chunk A của feature 2-stage department approval (Phase 9). 3 ràng buộc gộp 1 migration để rollback atomic. Schema changes: 1. Smart reject (3 ALTER): - Contracts.RejectedFromPhase int NULL - PurchaseEvaluations.RejectedFromPhase int NULL - Budgets.RejectedFromPhase int NULL Lưu phase nguồn khi reject để Drafter trình lại quay về đúng phase (skip phase trung gian đã duyệt) thay vì đi tuần tự từ DangSoanThao. 2. Bypass per-user (1 ALTER): - Users.CanBypassReview bit NOT NULL DEFAULT 0 Khi true → NV được duyệt thay TPB (skip Stage Review, đẩy thẳng Stage Confirm). Audit qua DepartmentApproval.IsBypassed=true. 3. 2-stage dept approval (3 CREATE TABLE): - ContractDepartmentApprovals - PurchaseEvaluationDepartmentApprovals - BudgetDepartmentApprovals Schema (chung): - Id, *Id FK Cascade, PhaseAtApproval int, DepartmentId FK - Stage enum (1=Review NV, 2=Confirm TPB) - ApproverUserId, ApproverRoleSnapshot, Comment, ApprovedAt - IsBypassed bit (mark NV bypass) - AuditableEntity (CreatedAt/By/UpdatedAt/By/IsDeleted/...) Indexes: - UNIQUE (TargetId, PhaseAtApproval, DepartmentId, Stage) - Single: TargetId, DepartmentId, ApproverUserId Files: - Domain: 4 entity update (Contract/PE/Budget/User add field) + 1 enum mới ApprovalStage + 3 entity DepartmentApproval mới - Infrastructure: 3 EntityConfiguration update + 1 file mới DepartmentApprovalsConfiguration với 3 config classes - IApplicationDbContext: thêm 3 DbSet - ApplicationDbContext: thêm 3 DbSet - Migration 16: 3 file (.cs + Designer.cs + Snapshot.cs) — 3-file rule Verify: - Build pass (2 warning DocxRenderer cũ, không liên quan) - 77 unit test pass (54 Domain + 23 Infra) — Domain policy chưa update, test pass nguyên không regression Note: KHÔNG khác PurchaseEvaluationDepartmentOpinion (Migration 15) — Opinion là sign-off block "Ý kiến 4 phòng ban" trên header phiếu. DepartmentApproval mới là 2-stage approval workflow per phase. Tổng sau Migration 16: 55 bảng (52+3), 16 migration. Chunk B-E sẽ implement Application + FE + Tests. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@ -31,6 +31,7 @@ public interface IApplicationDbContext
|
|||||||
DbSet<ContractAttachment> ContractAttachments { get; }
|
DbSet<ContractAttachment> ContractAttachments { get; }
|
||||||
DbSet<ContractCodeSequence> ContractCodeSequences { get; }
|
DbSet<ContractCodeSequence> ContractCodeSequences { get; }
|
||||||
DbSet<ContractChangelog> ContractChangelogs { get; }
|
DbSet<ContractChangelog> ContractChangelogs { get; }
|
||||||
|
DbSet<ContractDepartmentApproval> ContractDepartmentApprovals { get; }
|
||||||
DbSet<Notification> Notifications { get; }
|
DbSet<Notification> Notifications { get; }
|
||||||
DbSet<WorkflowTypeAssignment> WorkflowTypeAssignments { get; }
|
DbSet<WorkflowTypeAssignment> WorkflowTypeAssignments { get; }
|
||||||
DbSet<WorkflowDefinition> WorkflowDefinitions { get; }
|
DbSet<WorkflowDefinition> WorkflowDefinitions { get; }
|
||||||
@ -59,12 +60,14 @@ public interface IApplicationDbContext
|
|||||||
DbSet<PurchaseEvaluationWorkflowStepApprover> PurchaseEvaluationWorkflowStepApprovers { get; }
|
DbSet<PurchaseEvaluationWorkflowStepApprover> PurchaseEvaluationWorkflowStepApprovers { get; }
|
||||||
DbSet<PurchaseEvaluationCodeSequence> PurchaseEvaluationCodeSequences { get; }
|
DbSet<PurchaseEvaluationCodeSequence> PurchaseEvaluationCodeSequences { get; }
|
||||||
DbSet<PurchaseEvaluationDepartmentOpinion> PurchaseEvaluationDepartmentOpinions { get; }
|
DbSet<PurchaseEvaluationDepartmentOpinion> PurchaseEvaluationDepartmentOpinions { get; }
|
||||||
|
DbSet<PurchaseEvaluationDepartmentApproval> PurchaseEvaluationDepartmentApprovals { get; }
|
||||||
|
|
||||||
// Module Ngân sách (Phase 7)
|
// Module Ngân sách (Phase 7)
|
||||||
DbSet<Budget> Budgets { get; }
|
DbSet<Budget> Budgets { get; }
|
||||||
DbSet<BudgetDetail> BudgetDetails { get; }
|
DbSet<BudgetDetail> BudgetDetails { get; }
|
||||||
DbSet<BudgetApproval> BudgetApprovals { get; }
|
DbSet<BudgetApproval> BudgetApprovals { get; }
|
||||||
DbSet<BudgetChangelog> BudgetChangelogs { get; }
|
DbSet<BudgetChangelog> BudgetChangelogs { get; }
|
||||||
|
DbSet<BudgetDepartmentApproval> BudgetDepartmentApprovals { get; }
|
||||||
|
|
||||||
Task<int> SaveChangesAsync(CancellationToken cancellationToken = default);
|
Task<int> SaveChangesAsync(CancellationToken cancellationToken = default);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -23,7 +23,12 @@ public class Budget : AuditableEntity
|
|||||||
public DateTime? SlaDeadline { get; set; }
|
public DateTime? SlaDeadline { get; set; }
|
||||||
public bool SlaWarningSent { get; set; }
|
public bool SlaWarningSent { get; set; }
|
||||||
|
|
||||||
|
// Smart reject (Phase 9 — Migration 16): Phase nguồn khi reject. Drafter
|
||||||
|
// sửa lại + trình lại → quay về RejectedFromPhase thay vì DangSoanThao.
|
||||||
|
public BudgetPhase? RejectedFromPhase { get; set; }
|
||||||
|
|
||||||
public List<BudgetDetail> Details { get; set; } = new();
|
public List<BudgetDetail> Details { get; set; } = new();
|
||||||
public List<BudgetApproval> Approvals { get; set; } = new();
|
public List<BudgetApproval> Approvals { get; set; } = new();
|
||||||
public List<BudgetChangelog> Changelogs { get; set; } = new();
|
public List<BudgetChangelog> Changelogs { get; set; } = new();
|
||||||
|
public List<BudgetDepartmentApproval> DepartmentApprovals { get; set; } = new();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,20 @@
|
|||||||
|
using SolutionErp.Domain.Common;
|
||||||
|
|
||||||
|
namespace SolutionErp.Domain.Budgets;
|
||||||
|
|
||||||
|
// 2-stage department approval cho Budget workflow (Phase 9 — Migration 16).
|
||||||
|
// Mirror schema ContractDepartmentApproval / PurchaseEvaluationDepartmentApproval.
|
||||||
|
public class BudgetDepartmentApproval : AuditableEntity
|
||||||
|
{
|
||||||
|
public Guid BudgetId { get; set; }
|
||||||
|
public int PhaseAtApproval { get; set; } // snapshot BudgetPhase int
|
||||||
|
public Guid DepartmentId { get; set; }
|
||||||
|
public ApprovalStage Stage { get; set; } // 1=Review (NV), 2=Confirm (TPB)
|
||||||
|
public Guid ApproverUserId { get; set; }
|
||||||
|
public string? ApproverRoleSnapshot { get; set; }
|
||||||
|
public string? Comment { get; set; }
|
||||||
|
public DateTime ApprovedAt { get; set; }
|
||||||
|
public bool IsBypassed { get; set; }
|
||||||
|
|
||||||
|
public Budget? Budget { get; set; }
|
||||||
|
}
|
||||||
13
src/Backend/SolutionErp.Domain/Common/ApprovalStage.cs
Normal file
13
src/Backend/SolutionErp.Domain/Common/ApprovalStage.cs
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
namespace SolutionErp.Domain.Common;
|
||||||
|
|
||||||
|
// 2-stage department approval (Phase 9 — Migration 16).
|
||||||
|
// Mỗi phòng ban (Department) duyệt 1 phase qua 2 cấp:
|
||||||
|
// - Review: NV.<Dept> duyệt (cấp 1)
|
||||||
|
// - Confirm: TPB.<Dept> duyệt (cấp 2)
|
||||||
|
// Khi user.CanBypassReview=true → NV được skip Review, đẩy thẳng Confirm
|
||||||
|
// (audit qua field IsBypassed=true trong DepartmentApproval row).
|
||||||
|
public enum ApprovalStage
|
||||||
|
{
|
||||||
|
Review = 1,
|
||||||
|
Confirm = 2,
|
||||||
|
}
|
||||||
@ -25,10 +25,16 @@ public class Contract : AuditableEntity
|
|||||||
public bool SlaWarningSent { get; set; } // Flag để không gửi warning 2 lần
|
public bool SlaWarningSent { get; set; } // Flag để không gửi warning 2 lần
|
||||||
public Guid? BudgetId { get; set; } // Reference Budget (Phase 7) — đối chiếu chi phí HĐ vs ngân sách
|
public Guid? BudgetId { get; set; } // Reference Budget (Phase 7) — đối chiếu chi phí HĐ vs ngân sách
|
||||||
|
|
||||||
|
// Smart reject (Phase 9 — Migration 16): Phase nguồn khi reject. Drafter
|
||||||
|
// sửa lại + trình lại → quay về RejectedFromPhase thay vì DangSoanThao
|
||||||
|
// tuần tự lại từ đầu. Null khi chưa từng reject hoặc đã trình lại xong.
|
||||||
|
public ContractPhase? RejectedFromPhase { get; set; }
|
||||||
|
|
||||||
public List<ContractApproval> Approvals { get; set; } = new();
|
public List<ContractApproval> Approvals { get; set; } = new();
|
||||||
public List<ContractComment> Comments { get; set; } = new();
|
public List<ContractComment> Comments { get; set; } = new();
|
||||||
public List<ContractAttachment> Attachments { get; set; } = new();
|
public List<ContractAttachment> Attachments { get; set; } = new();
|
||||||
public List<ContractChangelog> Changelogs { get; set; } = new();
|
public List<ContractChangelog> Changelogs { get; set; } = new();
|
||||||
|
public List<ContractDepartmentApproval> DepartmentApprovals { get; set; } = new();
|
||||||
|
|
||||||
// Per-type details — chỉ 1 collection có data tương ứng với Type. Backend
|
// Per-type details — chỉ 1 collection có data tương ứng với Type. Backend
|
||||||
// logic dispatch theo Contract.Type để load đúng bảng. KHÔNG load eager
|
// logic dispatch theo Contract.Type để load đúng bảng. KHÔNG load eager
|
||||||
|
|||||||
@ -0,0 +1,27 @@
|
|||||||
|
using SolutionErp.Domain.Common;
|
||||||
|
|
||||||
|
namespace SolutionErp.Domain.Contracts;
|
||||||
|
|
||||||
|
// 2-stage department approval cho HĐ workflow (Phase 9 — Migration 16).
|
||||||
|
// Mỗi phase × phòng ban có max 2 row: Stage=Review (NV duyệt) + Stage=Confirm
|
||||||
|
// (TPB duyệt). Workflow service guard: chỉ transition khi đã có Stage=Confirm.
|
||||||
|
//
|
||||||
|
// User.CanBypassReview=true → NV insert thẳng Stage=Confirm (IsBypassed=true).
|
||||||
|
//
|
||||||
|
// UNIQUE (ContractId, PhaseAtApproval, DepartmentId, Stage) — 1 phase × 1 phòng
|
||||||
|
// × 1 stage = 1 row duy nhất. UPDATE in-place khi user đổi ý (audit qua
|
||||||
|
// ContractChangelog).
|
||||||
|
public class ContractDepartmentApproval : AuditableEntity
|
||||||
|
{
|
||||||
|
public Guid ContractId { get; set; }
|
||||||
|
public int PhaseAtApproval { get; set; } // snapshot ContractPhase int (Phase tại lúc approve)
|
||||||
|
public Guid DepartmentId { get; set; }
|
||||||
|
public ApprovalStage Stage { get; set; } // 1=Review (NV), 2=Confirm (TPB)
|
||||||
|
public Guid ApproverUserId { get; set; }
|
||||||
|
public string? ApproverRoleSnapshot { get; set; } // VD "NV.CCM" / "TPB.CCM" — denorm cho audit readable
|
||||||
|
public string? Comment { get; set; }
|
||||||
|
public DateTime ApprovedAt { get; set; }
|
||||||
|
public bool IsBypassed { get; set; } // true nếu NV bypass (User.CanBypassReview=true)
|
||||||
|
|
||||||
|
public Contract? Contract { get; set; }
|
||||||
|
}
|
||||||
@ -15,4 +15,9 @@ public class User : IdentityUser<Guid>
|
|||||||
// cho admin/system user không thuộc dept cụ thể.
|
// cho admin/system user không thuộc dept cụ thể.
|
||||||
public Guid? DepartmentId { get; set; }
|
public Guid? DepartmentId { get; set; }
|
||||||
public string? Position { get; set; } // vd "Trưởng phòng CCM", "QS công trường", "Phó GĐ"
|
public string? Position { get; set; } // vd "Trưởng phòng CCM", "QS công trường", "Phó GĐ"
|
||||||
|
|
||||||
|
// 2-stage department approval (Phase 9 — Migration 16): khi true, NV
|
||||||
|
// được quyền duyệt thay TPB (skip Stage Review, đẩy thẳng Stage Confirm).
|
||||||
|
// Mặc định false (an toàn). Admin set ở UserManager UI.
|
||||||
|
public bool CanBypassReview { get; set; }
|
||||||
}
|
}
|
||||||
|
|||||||
@ -28,6 +28,10 @@ public class PurchaseEvaluation : AuditableEntity
|
|||||||
public Guid? ContractId { get; set; } // FK Contracts — set khi user gen HĐ từ phiếu
|
public Guid? ContractId { get; set; } // FK Contracts — set khi user gen HĐ từ phiếu
|
||||||
public Guid? BudgetId { get; set; } // FK Budget (Phase 7) — đối chiếu báo giá vs ngân sách gói thầu
|
public Guid? BudgetId { get; set; } // FK Budget (Phase 7) — đối chiếu báo giá vs ngân sách gói thầu
|
||||||
|
|
||||||
|
// Smart reject (Phase 9 — Migration 16): Phase nguồn khi reject. Drafter
|
||||||
|
// sửa lại + trình lại → quay về RejectedFromPhase thay vì đi tuần tự.
|
||||||
|
public PurchaseEvaluationPhase? RejectedFromPhase { get; set; }
|
||||||
|
|
||||||
public List<PurchaseEvaluationSupplier> Suppliers { get; set; } = new();
|
public List<PurchaseEvaluationSupplier> Suppliers { get; set; } = new();
|
||||||
public List<PurchaseEvaluationDetail> Details { get; set; } = new();
|
public List<PurchaseEvaluationDetail> Details { get; set; } = new();
|
||||||
public List<PurchaseEvaluationQuote> Quotes { get; set; } = new();
|
public List<PurchaseEvaluationQuote> Quotes { get; set; } = new();
|
||||||
@ -35,4 +39,5 @@ public class PurchaseEvaluation : AuditableEntity
|
|||||||
public List<PurchaseEvaluationChangelog> Changelogs { get; set; } = new();
|
public List<PurchaseEvaluationChangelog> Changelogs { get; set; } = new();
|
||||||
public List<PurchaseEvaluationAttachment> Attachments { get; set; } = new();
|
public List<PurchaseEvaluationAttachment> Attachments { get; set; } = new();
|
||||||
public List<PurchaseEvaluationDepartmentOpinion> DepartmentOpinions { get; set; } = new();
|
public List<PurchaseEvaluationDepartmentOpinion> DepartmentOpinions { get; set; } = new();
|
||||||
|
public List<PurchaseEvaluationDepartmentApproval> DepartmentApprovals { get; set; } = new();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,26 @@
|
|||||||
|
using SolutionErp.Domain.Common;
|
||||||
|
|
||||||
|
namespace SolutionErp.Domain.PurchaseEvaluations;
|
||||||
|
|
||||||
|
// 2-stage department approval cho PE workflow (Phase 9 — Migration 16).
|
||||||
|
// Mirror schema ContractDepartmentApproval — pattern thống nhất cho cả 3
|
||||||
|
// module (HĐ / PE / Budget).
|
||||||
|
//
|
||||||
|
// LƯU Ý: KHÁC `PurchaseEvaluationDepartmentOpinion` (Migration 15) — Opinion
|
||||||
|
// là sign-off block "Ý kiến 4 phòng ban" (Phê duyệt/CCM/MuaHàng/SM-PM) trên
|
||||||
|
// header phiếu. DepartmentApproval là 2-stage approval per phase trong
|
||||||
|
// workflow chính.
|
||||||
|
public class PurchaseEvaluationDepartmentApproval : AuditableEntity
|
||||||
|
{
|
||||||
|
public Guid PurchaseEvaluationId { get; set; }
|
||||||
|
public int PhaseAtApproval { get; set; } // snapshot PurchaseEvaluationPhase int
|
||||||
|
public Guid DepartmentId { get; set; }
|
||||||
|
public ApprovalStage Stage { get; set; } // 1=Review (NV), 2=Confirm (TPB)
|
||||||
|
public Guid ApproverUserId { get; set; }
|
||||||
|
public string? ApproverRoleSnapshot { get; set; }
|
||||||
|
public string? Comment { get; set; }
|
||||||
|
public DateTime ApprovedAt { get; set; }
|
||||||
|
public bool IsBypassed { get; set; }
|
||||||
|
|
||||||
|
public PurchaseEvaluation? PurchaseEvaluation { get; set; }
|
||||||
|
}
|
||||||
@ -35,6 +35,7 @@ public class ApplicationDbContext
|
|||||||
public DbSet<ContractAttachment> ContractAttachments => Set<ContractAttachment>();
|
public DbSet<ContractAttachment> ContractAttachments => Set<ContractAttachment>();
|
||||||
public DbSet<ContractCodeSequence> ContractCodeSequences => Set<ContractCodeSequence>();
|
public DbSet<ContractCodeSequence> ContractCodeSequences => Set<ContractCodeSequence>();
|
||||||
public DbSet<ContractChangelog> ContractChangelogs => Set<ContractChangelog>();
|
public DbSet<ContractChangelog> ContractChangelogs => Set<ContractChangelog>();
|
||||||
|
public DbSet<ContractDepartmentApproval> ContractDepartmentApprovals => Set<ContractDepartmentApproval>();
|
||||||
public DbSet<Notification> Notifications => Set<Notification>();
|
public DbSet<Notification> Notifications => Set<Notification>();
|
||||||
public DbSet<WorkflowTypeAssignment> WorkflowTypeAssignments => Set<WorkflowTypeAssignment>();
|
public DbSet<WorkflowTypeAssignment> WorkflowTypeAssignments => Set<WorkflowTypeAssignment>();
|
||||||
public DbSet<WorkflowDefinition> WorkflowDefinitions => Set<WorkflowDefinition>();
|
public DbSet<WorkflowDefinition> WorkflowDefinitions => Set<WorkflowDefinition>();
|
||||||
@ -60,12 +61,14 @@ public class ApplicationDbContext
|
|||||||
public DbSet<PurchaseEvaluationWorkflowStepApprover> PurchaseEvaluationWorkflowStepApprovers => Set<PurchaseEvaluationWorkflowStepApprover>();
|
public DbSet<PurchaseEvaluationWorkflowStepApprover> PurchaseEvaluationWorkflowStepApprovers => Set<PurchaseEvaluationWorkflowStepApprover>();
|
||||||
public DbSet<PurchaseEvaluationCodeSequence> PurchaseEvaluationCodeSequences => Set<PurchaseEvaluationCodeSequence>();
|
public DbSet<PurchaseEvaluationCodeSequence> PurchaseEvaluationCodeSequences => Set<PurchaseEvaluationCodeSequence>();
|
||||||
public DbSet<PurchaseEvaluationDepartmentOpinion> PurchaseEvaluationDepartmentOpinions => Set<PurchaseEvaluationDepartmentOpinion>();
|
public DbSet<PurchaseEvaluationDepartmentOpinion> PurchaseEvaluationDepartmentOpinions => Set<PurchaseEvaluationDepartmentOpinion>();
|
||||||
|
public DbSet<PurchaseEvaluationDepartmentApproval> PurchaseEvaluationDepartmentApprovals => Set<PurchaseEvaluationDepartmentApproval>();
|
||||||
|
|
||||||
// Module Ngân sách (Phase 7) — 4 bảng: Budget header + Details + Approvals + Changelogs.
|
// Module Ngân sách (Phase 7) — 4 bảng: Budget header + Details + Approvals + Changelogs.
|
||||||
public DbSet<Budget> Budgets => Set<Budget>();
|
public DbSet<Budget> Budgets => Set<Budget>();
|
||||||
public DbSet<BudgetDetail> BudgetDetails => Set<BudgetDetail>();
|
public DbSet<BudgetDetail> BudgetDetails => Set<BudgetDetail>();
|
||||||
public DbSet<BudgetApproval> BudgetApprovals => Set<BudgetApproval>();
|
public DbSet<BudgetApproval> BudgetApprovals => Set<BudgetApproval>();
|
||||||
public DbSet<BudgetChangelog> BudgetChangelogs => Set<BudgetChangelog>();
|
public DbSet<BudgetChangelog> BudgetChangelogs => Set<BudgetChangelog>();
|
||||||
|
public DbSet<BudgetDepartmentApproval> BudgetDepartmentApprovals => Set<BudgetDepartmentApproval>();
|
||||||
|
|
||||||
protected override void OnModelCreating(ModelBuilder builder)
|
protected override void OnModelCreating(ModelBuilder builder)
|
||||||
{
|
{
|
||||||
|
|||||||
@ -15,6 +15,7 @@ public class BudgetConfiguration : IEntityTypeConfiguration<Budget>
|
|||||||
b.Property(x => x.TenNganSach).HasMaxLength(500).IsRequired();
|
b.Property(x => x.TenNganSach).HasMaxLength(500).IsRequired();
|
||||||
b.Property(x => x.Description).HasMaxLength(2000);
|
b.Property(x => x.Description).HasMaxLength(2000);
|
||||||
b.Property(x => x.Phase).HasConversion<int>();
|
b.Property(x => x.Phase).HasConversion<int>();
|
||||||
|
b.Property(x => x.RejectedFromPhase).HasConversion<int?>();
|
||||||
b.Property(x => x.TongNganSach).HasPrecision(18, 2);
|
b.Property(x => x.TongNganSach).HasPrecision(18, 2);
|
||||||
|
|
||||||
b.HasIndex(x => x.MaNganSach).IsUnique().HasFilter("[MaNganSach] IS NOT NULL");
|
b.HasIndex(x => x.MaNganSach).IsUnique().HasFilter("[MaNganSach] IS NOT NULL");
|
||||||
|
|||||||
@ -14,6 +14,7 @@ public class ContractConfiguration : IEntityTypeConfiguration<Contract>
|
|||||||
b.Property(x => x.MaHopDong).HasMaxLength(100);
|
b.Property(x => x.MaHopDong).HasMaxLength(100);
|
||||||
b.Property(x => x.Type).HasConversion<int>();
|
b.Property(x => x.Type).HasConversion<int>();
|
||||||
b.Property(x => x.Phase).HasConversion<int>();
|
b.Property(x => x.Phase).HasConversion<int>();
|
||||||
|
b.Property(x => x.RejectedFromPhase).HasConversion<int?>();
|
||||||
b.Property(x => x.GiaTri).HasPrecision(18, 2);
|
b.Property(x => x.GiaTri).HasPrecision(18, 2);
|
||||||
b.Property(x => x.TenHopDong).HasMaxLength(500);
|
b.Property(x => x.TenHopDong).HasMaxLength(500);
|
||||||
b.Property(x => x.NoiDung).HasMaxLength(2000);
|
b.Property(x => x.NoiDung).HasMaxLength(2000);
|
||||||
|
|||||||
@ -0,0 +1,94 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore.Metadata.Builders;
|
||||||
|
using SolutionErp.Domain.Budgets;
|
||||||
|
using SolutionErp.Domain.Contracts;
|
||||||
|
using SolutionErp.Domain.PurchaseEvaluations;
|
||||||
|
|
||||||
|
namespace SolutionErp.Infrastructure.Persistence.Configurations;
|
||||||
|
|
||||||
|
// 3 bảng *DepartmentApprovals (Phase 9 — Migration 16) — 2-stage approval per
|
||||||
|
// phase × phòng ban cho HĐ / PE / Budget.
|
||||||
|
//
|
||||||
|
// UNIQUE (TargetId, PhaseAtApproval, DepartmentId, Stage) — 1 phase × 1 phòng
|
||||||
|
// × 1 stage = 1 row duy nhất. UPDATE in-place khi user đổi ý → audit qua
|
||||||
|
// *Changelog.
|
||||||
|
//
|
||||||
|
// FK Cascade theo target (xóa HĐ/PE/Budget → xóa approval rows).
|
||||||
|
|
||||||
|
public class ContractDepartmentApprovalConfiguration
|
||||||
|
: IEntityTypeConfiguration<ContractDepartmentApproval>
|
||||||
|
{
|
||||||
|
public void Configure(EntityTypeBuilder<ContractDepartmentApproval> b)
|
||||||
|
{
|
||||||
|
b.ToTable("ContractDepartmentApprovals");
|
||||||
|
b.HasKey(x => x.Id);
|
||||||
|
|
||||||
|
b.Property(x => x.Stage).HasConversion<int>();
|
||||||
|
b.Property(x => x.ApproverRoleSnapshot).HasMaxLength(100);
|
||||||
|
b.Property(x => x.Comment).HasMaxLength(1000);
|
||||||
|
|
||||||
|
b.HasIndex(x => new { x.ContractId, x.PhaseAtApproval, x.DepartmentId, x.Stage })
|
||||||
|
.IsUnique()
|
||||||
|
.HasDatabaseName("UX_ContractDeptApprovals_Contract_Phase_Dept_Stage");
|
||||||
|
b.HasIndex(x => x.ContractId);
|
||||||
|
b.HasIndex(x => x.DepartmentId);
|
||||||
|
b.HasIndex(x => x.ApproverUserId);
|
||||||
|
|
||||||
|
b.HasOne(x => x.Contract)
|
||||||
|
.WithMany(c => c.DepartmentApprovals)
|
||||||
|
.HasForeignKey(x => x.ContractId)
|
||||||
|
.OnDelete(DeleteBehavior.Cascade);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class PurchaseEvaluationDepartmentApprovalConfiguration
|
||||||
|
: IEntityTypeConfiguration<PurchaseEvaluationDepartmentApproval>
|
||||||
|
{
|
||||||
|
public void Configure(EntityTypeBuilder<PurchaseEvaluationDepartmentApproval> b)
|
||||||
|
{
|
||||||
|
b.ToTable("PurchaseEvaluationDepartmentApprovals");
|
||||||
|
b.HasKey(x => x.Id);
|
||||||
|
|
||||||
|
b.Property(x => x.Stage).HasConversion<int>();
|
||||||
|
b.Property(x => x.ApproverRoleSnapshot).HasMaxLength(100);
|
||||||
|
b.Property(x => x.Comment).HasMaxLength(1000);
|
||||||
|
|
||||||
|
b.HasIndex(x => new { x.PurchaseEvaluationId, x.PhaseAtApproval, x.DepartmentId, x.Stage })
|
||||||
|
.IsUnique()
|
||||||
|
.HasDatabaseName("UX_PEDeptApprovals_PE_Phase_Dept_Stage");
|
||||||
|
b.HasIndex(x => x.PurchaseEvaluationId);
|
||||||
|
b.HasIndex(x => x.DepartmentId);
|
||||||
|
b.HasIndex(x => x.ApproverUserId);
|
||||||
|
|
||||||
|
b.HasOne(x => x.PurchaseEvaluation)
|
||||||
|
.WithMany(c => c.DepartmentApprovals)
|
||||||
|
.HasForeignKey(x => x.PurchaseEvaluationId)
|
||||||
|
.OnDelete(DeleteBehavior.Cascade);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class BudgetDepartmentApprovalConfiguration
|
||||||
|
: IEntityTypeConfiguration<BudgetDepartmentApproval>
|
||||||
|
{
|
||||||
|
public void Configure(EntityTypeBuilder<BudgetDepartmentApproval> b)
|
||||||
|
{
|
||||||
|
b.ToTable("BudgetDepartmentApprovals");
|
||||||
|
b.HasKey(x => x.Id);
|
||||||
|
|
||||||
|
b.Property(x => x.Stage).HasConversion<int>();
|
||||||
|
b.Property(x => x.ApproverRoleSnapshot).HasMaxLength(100);
|
||||||
|
b.Property(x => x.Comment).HasMaxLength(1000);
|
||||||
|
|
||||||
|
b.HasIndex(x => new { x.BudgetId, x.PhaseAtApproval, x.DepartmentId, x.Stage })
|
||||||
|
.IsUnique()
|
||||||
|
.HasDatabaseName("UX_BudgetDeptApprovals_Budget_Phase_Dept_Stage");
|
||||||
|
b.HasIndex(x => x.BudgetId);
|
||||||
|
b.HasIndex(x => x.DepartmentId);
|
||||||
|
b.HasIndex(x => x.ApproverUserId);
|
||||||
|
|
||||||
|
b.HasOne(x => x.Budget)
|
||||||
|
.WithMany(c => c.DepartmentApprovals)
|
||||||
|
.HasForeignKey(x => x.BudgetId)
|
||||||
|
.OnDelete(DeleteBehavior.Cascade);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -14,6 +14,7 @@ public class PurchaseEvaluationConfiguration : IEntityTypeConfiguration<Purchase
|
|||||||
b.Property(x => x.MaPhieu).HasMaxLength(100);
|
b.Property(x => x.MaPhieu).HasMaxLength(100);
|
||||||
b.Property(x => x.Type).HasConversion<int>();
|
b.Property(x => x.Type).HasConversion<int>();
|
||||||
b.Property(x => x.Phase).HasConversion<int>();
|
b.Property(x => x.Phase).HasConversion<int>();
|
||||||
|
b.Property(x => x.RejectedFromPhase).HasConversion<int?>();
|
||||||
b.Property(x => x.TenGoiThau).HasMaxLength(500).IsRequired();
|
b.Property(x => x.TenGoiThau).HasMaxLength(500).IsRequired();
|
||||||
b.Property(x => x.DiaDiem).HasMaxLength(500);
|
b.Property(x => x.DiaDiem).HasMaxLength(500);
|
||||||
b.Property(x => x.MoTa).HasMaxLength(2000);
|
b.Property(x => x.MoTa).HasMaxLength(2000);
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,231 @@
|
|||||||
|
using System;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace SolutionErp.Infrastructure.Persistence.Migrations
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class AddTwoStageDeptApprovalAndSmartReject : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.AddColumn<bool>(
|
||||||
|
name: "CanBypassReview",
|
||||||
|
table: "Users",
|
||||||
|
type: "bit",
|
||||||
|
nullable: false,
|
||||||
|
defaultValue: false);
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<int>(
|
||||||
|
name: "RejectedFromPhase",
|
||||||
|
table: "PurchaseEvaluations",
|
||||||
|
type: "int",
|
||||||
|
nullable: true);
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<int>(
|
||||||
|
name: "RejectedFromPhase",
|
||||||
|
table: "Contracts",
|
||||||
|
type: "int",
|
||||||
|
nullable: true);
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<int>(
|
||||||
|
name: "RejectedFromPhase",
|
||||||
|
table: "Budgets",
|
||||||
|
type: "int",
|
||||||
|
nullable: true);
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "BudgetDepartmentApprovals",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
|
||||||
|
BudgetId = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
|
||||||
|
PhaseAtApproval = table.Column<int>(type: "int", nullable: false),
|
||||||
|
DepartmentId = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
|
||||||
|
Stage = table.Column<int>(type: "int", nullable: false),
|
||||||
|
ApproverUserId = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
|
||||||
|
ApproverRoleSnapshot = table.Column<string>(type: "nvarchar(100)", maxLength: 100, nullable: true),
|
||||||
|
Comment = table.Column<string>(type: "nvarchar(1000)", maxLength: 1000, nullable: true),
|
||||||
|
ApprovedAt = table.Column<DateTime>(type: "datetime2", nullable: false),
|
||||||
|
IsBypassed = table.Column<bool>(type: "bit", 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),
|
||||||
|
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_BudgetDepartmentApprovals", x => x.Id);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_BudgetDepartmentApprovals_Budgets_BudgetId",
|
||||||
|
column: x => x.BudgetId,
|
||||||
|
principalTable: "Budgets",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "ContractDepartmentApprovals",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
|
||||||
|
ContractId = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
|
||||||
|
PhaseAtApproval = table.Column<int>(type: "int", nullable: false),
|
||||||
|
DepartmentId = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
|
||||||
|
Stage = table.Column<int>(type: "int", nullable: false),
|
||||||
|
ApproverUserId = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
|
||||||
|
ApproverRoleSnapshot = table.Column<string>(type: "nvarchar(100)", maxLength: 100, nullable: true),
|
||||||
|
Comment = table.Column<string>(type: "nvarchar(1000)", maxLength: 1000, nullable: true),
|
||||||
|
ApprovedAt = table.Column<DateTime>(type: "datetime2", nullable: false),
|
||||||
|
IsBypassed = table.Column<bool>(type: "bit", 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),
|
||||||
|
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_ContractDepartmentApprovals", x => x.Id);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_ContractDepartmentApprovals_Contracts_ContractId",
|
||||||
|
column: x => x.ContractId,
|
||||||
|
principalTable: "Contracts",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "PurchaseEvaluationDepartmentApprovals",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
|
||||||
|
PurchaseEvaluationId = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
|
||||||
|
PhaseAtApproval = table.Column<int>(type: "int", nullable: false),
|
||||||
|
DepartmentId = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
|
||||||
|
Stage = table.Column<int>(type: "int", nullable: false),
|
||||||
|
ApproverUserId = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
|
||||||
|
ApproverRoleSnapshot = table.Column<string>(type: "nvarchar(100)", maxLength: 100, nullable: true),
|
||||||
|
Comment = table.Column<string>(type: "nvarchar(1000)", maxLength: 1000, nullable: true),
|
||||||
|
ApprovedAt = table.Column<DateTime>(type: "datetime2", nullable: false),
|
||||||
|
IsBypassed = table.Column<bool>(type: "bit", 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),
|
||||||
|
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_PurchaseEvaluationDepartmentApprovals", x => x.Id);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_PurchaseEvaluationDepartmentApprovals_PurchaseEvaluations_PurchaseEvaluationId",
|
||||||
|
column: x => x.PurchaseEvaluationId,
|
||||||
|
principalTable: "PurchaseEvaluations",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_BudgetDepartmentApprovals_ApproverUserId",
|
||||||
|
table: "BudgetDepartmentApprovals",
|
||||||
|
column: "ApproverUserId");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_BudgetDepartmentApprovals_BudgetId",
|
||||||
|
table: "BudgetDepartmentApprovals",
|
||||||
|
column: "BudgetId");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_BudgetDepartmentApprovals_DepartmentId",
|
||||||
|
table: "BudgetDepartmentApprovals",
|
||||||
|
column: "DepartmentId");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "UX_BudgetDeptApprovals_Budget_Phase_Dept_Stage",
|
||||||
|
table: "BudgetDepartmentApprovals",
|
||||||
|
columns: new[] { "BudgetId", "PhaseAtApproval", "DepartmentId", "Stage" },
|
||||||
|
unique: true);
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_ContractDepartmentApprovals_ApproverUserId",
|
||||||
|
table: "ContractDepartmentApprovals",
|
||||||
|
column: "ApproverUserId");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_ContractDepartmentApprovals_ContractId",
|
||||||
|
table: "ContractDepartmentApprovals",
|
||||||
|
column: "ContractId");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_ContractDepartmentApprovals_DepartmentId",
|
||||||
|
table: "ContractDepartmentApprovals",
|
||||||
|
column: "DepartmentId");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "UX_ContractDeptApprovals_Contract_Phase_Dept_Stage",
|
||||||
|
table: "ContractDepartmentApprovals",
|
||||||
|
columns: new[] { "ContractId", "PhaseAtApproval", "DepartmentId", "Stage" },
|
||||||
|
unique: true);
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_PurchaseEvaluationDepartmentApprovals_ApproverUserId",
|
||||||
|
table: "PurchaseEvaluationDepartmentApprovals",
|
||||||
|
column: "ApproverUserId");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_PurchaseEvaluationDepartmentApprovals_DepartmentId",
|
||||||
|
table: "PurchaseEvaluationDepartmentApprovals",
|
||||||
|
column: "DepartmentId");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_PurchaseEvaluationDepartmentApprovals_PurchaseEvaluationId",
|
||||||
|
table: "PurchaseEvaluationDepartmentApprovals",
|
||||||
|
column: "PurchaseEvaluationId");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "UX_PEDeptApprovals_PE_Phase_Dept_Stage",
|
||||||
|
table: "PurchaseEvaluationDepartmentApprovals",
|
||||||
|
columns: new[] { "PurchaseEvaluationId", "PhaseAtApproval", "DepartmentId", "Stage" },
|
||||||
|
unique: true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "BudgetDepartmentApprovals");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "ContractDepartmentApprovals");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "PurchaseEvaluationDepartmentApprovals");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "CanBypassReview",
|
||||||
|
table: "Users");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "RejectedFromPhase",
|
||||||
|
table: "PurchaseEvaluations");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "RejectedFromPhase",
|
||||||
|
table: "Contracts");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "RejectedFromPhase",
|
||||||
|
table: "Budgets");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -169,6 +169,9 @@ namespace SolutionErp.Infrastructure.Persistence.Migrations
|
|||||||
b.Property<Guid>("ProjectId")
|
b.Property<Guid>("ProjectId")
|
||||||
.HasColumnType("uniqueidentifier");
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
|
b.Property<int?>("RejectedFromPhase")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
b.Property<DateTime?>("SlaDeadline")
|
b.Property<DateTime?>("SlaDeadline")
|
||||||
.HasColumnType("datetime2");
|
.HasColumnType("datetime2");
|
||||||
|
|
||||||
@ -314,6 +317,77 @@ namespace SolutionErp.Infrastructure.Persistence.Migrations
|
|||||||
b.ToTable("BudgetChangelogs", (string)null);
|
b.ToTable("BudgetChangelogs", (string)null);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("SolutionErp.Domain.Budgets.BudgetDepartmentApproval", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
|
b.Property<DateTime>("ApprovedAt")
|
||||||
|
.HasColumnType("datetime2");
|
||||||
|
|
||||||
|
b.Property<string>("ApproverRoleSnapshot")
|
||||||
|
.HasMaxLength(100)
|
||||||
|
.HasColumnType("nvarchar(100)");
|
||||||
|
|
||||||
|
b.Property<Guid>("ApproverUserId")
|
||||||
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
|
b.Property<Guid>("BudgetId")
|
||||||
|
.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<DateTime?>("DeletedAt")
|
||||||
|
.HasColumnType("datetime2");
|
||||||
|
|
||||||
|
b.Property<Guid?>("DeletedBy")
|
||||||
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
|
b.Property<Guid>("DepartmentId")
|
||||||
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
|
b.Property<bool>("IsBypassed")
|
||||||
|
.HasColumnType("bit");
|
||||||
|
|
||||||
|
b.Property<bool>("IsDeleted")
|
||||||
|
.HasColumnType("bit");
|
||||||
|
|
||||||
|
b.Property<int>("PhaseAtApproval")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<int>("Stage")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("UpdatedAt")
|
||||||
|
.HasColumnType("datetime2");
|
||||||
|
|
||||||
|
b.Property<Guid?>("UpdatedBy")
|
||||||
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("ApproverUserId");
|
||||||
|
|
||||||
|
b.HasIndex("BudgetId");
|
||||||
|
|
||||||
|
b.HasIndex("DepartmentId");
|
||||||
|
|
||||||
|
b.HasIndex("BudgetId", "PhaseAtApproval", "DepartmentId", "Stage")
|
||||||
|
.IsUnique()
|
||||||
|
.HasDatabaseName("UX_BudgetDeptApprovals_Budget_Phase_Dept_Stage");
|
||||||
|
|
||||||
|
b.ToTable("BudgetDepartmentApprovals", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("SolutionErp.Domain.Budgets.BudgetDetail", b =>
|
modelBuilder.Entity("SolutionErp.Domain.Budgets.BudgetDetail", b =>
|
||||||
{
|
{
|
||||||
b.Property<Guid>("Id")
|
b.Property<Guid>("Id")
|
||||||
@ -438,6 +512,9 @@ namespace SolutionErp.Infrastructure.Persistence.Migrations
|
|||||||
b.Property<Guid>("ProjectId")
|
b.Property<Guid>("ProjectId")
|
||||||
.HasColumnType("uniqueidentifier");
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
|
b.Property<int?>("RejectedFromPhase")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
b.Property<DateTime?>("SlaDeadline")
|
b.Property<DateTime?>("SlaDeadline")
|
||||||
.HasColumnType("datetime2");
|
.HasColumnType("datetime2");
|
||||||
|
|
||||||
@ -701,6 +778,77 @@ namespace SolutionErp.Infrastructure.Persistence.Migrations
|
|||||||
b.ToTable("ContractComments", (string)null);
|
b.ToTable("ContractComments", (string)null);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("SolutionErp.Domain.Contracts.ContractDepartmentApproval", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
|
b.Property<DateTime>("ApprovedAt")
|
||||||
|
.HasColumnType("datetime2");
|
||||||
|
|
||||||
|
b.Property<string>("ApproverRoleSnapshot")
|
||||||
|
.HasMaxLength(100)
|
||||||
|
.HasColumnType("nvarchar(100)");
|
||||||
|
|
||||||
|
b.Property<Guid>("ApproverUserId")
|
||||||
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
|
b.Property<string>("Comment")
|
||||||
|
.HasMaxLength(1000)
|
||||||
|
.HasColumnType("nvarchar(1000)");
|
||||||
|
|
||||||
|
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<bool>("IsBypassed")
|
||||||
|
.HasColumnType("bit");
|
||||||
|
|
||||||
|
b.Property<bool>("IsDeleted")
|
||||||
|
.HasColumnType("bit");
|
||||||
|
|
||||||
|
b.Property<int>("PhaseAtApproval")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<int>("Stage")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("UpdatedAt")
|
||||||
|
.HasColumnType("datetime2");
|
||||||
|
|
||||||
|
b.Property<Guid?>("UpdatedBy")
|
||||||
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("ApproverUserId");
|
||||||
|
|
||||||
|
b.HasIndex("ContractId");
|
||||||
|
|
||||||
|
b.HasIndex("DepartmentId");
|
||||||
|
|
||||||
|
b.HasIndex("ContractId", "PhaseAtApproval", "DepartmentId", "Stage")
|
||||||
|
.IsUnique()
|
||||||
|
.HasDatabaseName("UX_ContractDeptApprovals_Contract_Phase_Dept_Stage");
|
||||||
|
|
||||||
|
b.ToTable("ContractDepartmentApprovals", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("SolutionErp.Domain.Contracts.Details.DichVuDetail", b =>
|
modelBuilder.Entity("SolutionErp.Domain.Contracts.Details.DichVuDetail", b =>
|
||||||
{
|
{
|
||||||
b.Property<Guid>("Id")
|
b.Property<Guid>("Id")
|
||||||
@ -1598,6 +1746,9 @@ namespace SolutionErp.Infrastructure.Persistence.Migrations
|
|||||||
b.Property<int>("AccessFailedCount")
|
b.Property<int>("AccessFailedCount")
|
||||||
.HasColumnType("int");
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<bool>("CanBypassReview")
|
||||||
|
.HasColumnType("bit");
|
||||||
|
|
||||||
b.Property<string>("ConcurrencyStamp")
|
b.Property<string>("ConcurrencyStamp")
|
||||||
.IsConcurrencyToken()
|
.IsConcurrencyToken()
|
||||||
.HasColumnType("nvarchar(max)");
|
.HasColumnType("nvarchar(max)");
|
||||||
@ -2222,6 +2373,9 @@ namespace SolutionErp.Infrastructure.Persistence.Migrations
|
|||||||
b.Property<Guid>("ProjectId")
|
b.Property<Guid>("ProjectId")
|
||||||
.HasColumnType("uniqueidentifier");
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
|
b.Property<int?>("RejectedFromPhase")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
b.Property<Guid?>("SelectedSupplierId")
|
b.Property<Guid?>("SelectedSupplierId")
|
||||||
.HasColumnType("uniqueidentifier");
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
@ -2451,6 +2605,77 @@ namespace SolutionErp.Infrastructure.Persistence.Migrations
|
|||||||
b.ToTable("PurchaseEvaluationCodeSequences", (string)null);
|
b.ToTable("PurchaseEvaluationCodeSequences", (string)null);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("SolutionErp.Domain.PurchaseEvaluations.PurchaseEvaluationDepartmentApproval", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
|
b.Property<DateTime>("ApprovedAt")
|
||||||
|
.HasColumnType("datetime2");
|
||||||
|
|
||||||
|
b.Property<string>("ApproverRoleSnapshot")
|
||||||
|
.HasMaxLength(100)
|
||||||
|
.HasColumnType("nvarchar(100)");
|
||||||
|
|
||||||
|
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<DateTime?>("DeletedAt")
|
||||||
|
.HasColumnType("datetime2");
|
||||||
|
|
||||||
|
b.Property<Guid?>("DeletedBy")
|
||||||
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
|
b.Property<Guid>("DepartmentId")
|
||||||
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
|
b.Property<bool>("IsBypassed")
|
||||||
|
.HasColumnType("bit");
|
||||||
|
|
||||||
|
b.Property<bool>("IsDeleted")
|
||||||
|
.HasColumnType("bit");
|
||||||
|
|
||||||
|
b.Property<int>("PhaseAtApproval")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<Guid>("PurchaseEvaluationId")
|
||||||
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
|
b.Property<int>("Stage")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("UpdatedAt")
|
||||||
|
.HasColumnType("datetime2");
|
||||||
|
|
||||||
|
b.Property<Guid?>("UpdatedBy")
|
||||||
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("ApproverUserId");
|
||||||
|
|
||||||
|
b.HasIndex("DepartmentId");
|
||||||
|
|
||||||
|
b.HasIndex("PurchaseEvaluationId");
|
||||||
|
|
||||||
|
b.HasIndex("PurchaseEvaluationId", "PhaseAtApproval", "DepartmentId", "Stage")
|
||||||
|
.IsUnique()
|
||||||
|
.HasDatabaseName("UX_PEDeptApprovals_PE_Phase_Dept_Stage");
|
||||||
|
|
||||||
|
b.ToTable("PurchaseEvaluationDepartmentApprovals", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("SolutionErp.Domain.PurchaseEvaluations.PurchaseEvaluationDepartmentOpinion", b =>
|
modelBuilder.Entity("SolutionErp.Domain.PurchaseEvaluations.PurchaseEvaluationDepartmentOpinion", b =>
|
||||||
{
|
{
|
||||||
b.Property<Guid>("Id")
|
b.Property<Guid>("Id")
|
||||||
@ -2904,6 +3129,17 @@ namespace SolutionErp.Infrastructure.Persistence.Migrations
|
|||||||
b.Navigation("Budget");
|
b.Navigation("Budget");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("SolutionErp.Domain.Budgets.BudgetDepartmentApproval", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("SolutionErp.Domain.Budgets.Budget", "Budget")
|
||||||
|
.WithMany("DepartmentApprovals")
|
||||||
|
.HasForeignKey("BudgetId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("Budget");
|
||||||
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("SolutionErp.Domain.Budgets.BudgetDetail", b =>
|
modelBuilder.Entity("SolutionErp.Domain.Budgets.BudgetDetail", b =>
|
||||||
{
|
{
|
||||||
b.HasOne("SolutionErp.Domain.Budgets.Budget", "Budget")
|
b.HasOne("SolutionErp.Domain.Budgets.Budget", "Budget")
|
||||||
@ -2959,6 +3195,17 @@ namespace SolutionErp.Infrastructure.Persistence.Migrations
|
|||||||
b.Navigation("Contract");
|
b.Navigation("Contract");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("SolutionErp.Domain.Contracts.ContractDepartmentApproval", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("SolutionErp.Domain.Contracts.Contract", "Contract")
|
||||||
|
.WithMany("DepartmentApprovals")
|
||||||
|
.HasForeignKey("ContractId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("Contract");
|
||||||
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("SolutionErp.Domain.Contracts.Details.DichVuDetail", b =>
|
modelBuilder.Entity("SolutionErp.Domain.Contracts.Details.DichVuDetail", b =>
|
||||||
{
|
{
|
||||||
b.HasOne("SolutionErp.Domain.Contracts.Contract", "Contract")
|
b.HasOne("SolutionErp.Domain.Contracts.Contract", "Contract")
|
||||||
@ -3128,6 +3375,17 @@ namespace SolutionErp.Infrastructure.Persistence.Migrations
|
|||||||
b.Navigation("PurchaseEvaluation");
|
b.Navigation("PurchaseEvaluation");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("SolutionErp.Domain.PurchaseEvaluations.PurchaseEvaluationDepartmentApproval", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("SolutionErp.Domain.PurchaseEvaluations.PurchaseEvaluation", "PurchaseEvaluation")
|
||||||
|
.WithMany("DepartmentApprovals")
|
||||||
|
.HasForeignKey("PurchaseEvaluationId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("PurchaseEvaluation");
|
||||||
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("SolutionErp.Domain.PurchaseEvaluations.PurchaseEvaluationDepartmentOpinion", b =>
|
modelBuilder.Entity("SolutionErp.Domain.PurchaseEvaluations.PurchaseEvaluationDepartmentOpinion", b =>
|
||||||
{
|
{
|
||||||
b.HasOne("SolutionErp.Domain.PurchaseEvaluations.PurchaseEvaluation", "PurchaseEvaluation")
|
b.HasOne("SolutionErp.Domain.PurchaseEvaluations.PurchaseEvaluation", "PurchaseEvaluation")
|
||||||
@ -3212,6 +3470,8 @@ namespace SolutionErp.Infrastructure.Persistence.Migrations
|
|||||||
|
|
||||||
b.Navigation("Changelogs");
|
b.Navigation("Changelogs");
|
||||||
|
|
||||||
|
b.Navigation("DepartmentApprovals");
|
||||||
|
|
||||||
b.Navigation("Details");
|
b.Navigation("Details");
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -3225,6 +3485,8 @@ namespace SolutionErp.Infrastructure.Persistence.Migrations
|
|||||||
|
|
||||||
b.Navigation("Comments");
|
b.Navigation("Comments");
|
||||||
|
|
||||||
|
b.Navigation("DepartmentApprovals");
|
||||||
|
|
||||||
b.Navigation("DichVuDetails");
|
b.Navigation("DichVuDetails");
|
||||||
|
|
||||||
b.Navigation("GiaoKhoanDetails");
|
b.Navigation("GiaoKhoanDetails");
|
||||||
@ -3265,6 +3527,8 @@ namespace SolutionErp.Infrastructure.Persistence.Migrations
|
|||||||
|
|
||||||
b.Navigation("Changelogs");
|
b.Navigation("Changelogs");
|
||||||
|
|
||||||
|
b.Navigation("DepartmentApprovals");
|
||||||
|
|
||||||
b.Navigation("DepartmentOpinions");
|
b.Navigation("DepartmentOpinions");
|
||||||
|
|
||||||
b.Navigation("Details");
|
b.Navigation("Details");
|
||||||
|
|||||||
Reference in New Issue
Block a user