[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:
pqhuy1987
2026-04-23 16:37:55 +07:00
parent a7ea6ad3d6
commit 2c6f0cabfb
19 changed files with 4884 additions and 1 deletions

View File

@ -8,6 +8,7 @@ using SolutionErp.Domain.Identity;
using SolutionErp.Domain.Master;
using SolutionErp.Domain.Master.Catalogs;
using SolutionErp.Domain.Notifications;
using SolutionErp.Domain.PurchaseEvaluations;
namespace SolutionErp.Infrastructure.Persistence;
@ -46,6 +47,17 @@ public class ApplicationDbContext
public DbSet<NguyenTacNccDetail> NguyenTacNccDetails => Set<NguyenTacNccDetail>();
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)
{
base.OnModelCreating(builder);

View File

@ -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);
}
}

View File

@ -8,6 +8,7 @@ using SolutionErp.Domain.Forms;
using SolutionErp.Domain.Identity;
using SolutionErp.Domain.Master;
using SolutionErp.Domain.Master.Catalogs;
using SolutionErp.Domain.PurchaseEvaluations;
namespace SolutionErp.Infrastructure.Persistence;
@ -37,6 +38,7 @@ public static class DbInitializer
await SeedDemoMasterDataAsync(db, logger);
await SeedContractTemplatesAsync(db, logger);
await SeedWorkflowDefinitionsAsync(db, logger);
await SeedPurchaseEvaluationWorkflowsAsync(db, logger);
await SeedCatalogsAsync(db, logger);
// 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
// đúng business: ThauPhu/GiaoKhoan đi với NTP/TĐ, NCC/MuaBan/NTNcc đi
// 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.Permissions, "Phân quyền", MenuKeys.System, 93, "KeyRound"),
(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
@ -809,6 +887,30 @@ public static class DbInitializer
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 added = 0;
foreach (var (key, label, parent, o, icon) in tree)

View File

@ -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");
}
}
}

View File

@ -1904,6 +1904,592 @@ namespace SolutionErp.Infrastructure.Persistence.Migrations
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 =>
{
b.HasOne("SolutionErp.Domain.Identity.Role", null)
@ -2135,6 +2721,106 @@ namespace SolutionErp.Infrastructure.Persistence.Migrations
.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 =>
{
b.Navigation("Approvals");
@ -2176,6 +2862,36 @@ namespace SolutionErp.Infrastructure.Persistence.Migrations
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
}
}