[CLAUDE] Domain+App+Infra+Api+FE-Admin+FE-User: S37 Mig 37 enum + Plan G-O3 Đề xuất full-stack
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 3m53s
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 3m53s
Phase 10.3 G-O3 Đề xuất (Proposal) — Mig 37 enum extend +5 values + Mig 38 Proposal schema + BE CQRS 8 endpoint + FE 2 app SHA256 IDENTICAL. Mig 37 (em main solo): extend ApprovalWorkflowApplicableType enum +5 values ProposalGeneral=4 / LeaveRequest=5 / OtRequest=6 / VehicleBooking=7 / ItTicket=8 cookie-cutter Mig 22 pattern (Up/Down empty — enum mức Domain). Mig 38 (em main solo): 4 entity Proposal (Code DX/YYYY/NNN) + ProposalAttachment + ProposalLevelOpinion (UNIQUE composite PEId+LevelId mirror PE Mig 26) + ProposalCodeSequence (Prefix PK atomic seq). 4 EF Config + 2 DbContext mod. BE CQRS (em main solo ~700 LOC ProposalFeatures.cs sau Implementer truncate phase exploration gotcha #53 5th + 529 Overload): - 4 Header handler (List paged + GetById detail + Create + UpdateDraft owner-OR-admin) - 4 Workflow handler (Submit gen MaDeXuat atomic + Approve UPSERT LevelOpinion advance + Reject + Return) - SERIALIZABLE transaction CodeGen - DTOs nested LevelOpinion với Step+Level metadata JOIN ProposalsController 8 endpoint /api/proposals (List/GetById/Create/Update/Submit/Approve/Reject/Return) class-level [Authorize] + handler-level owner-OR-admin guard. DbInitializer: SeedSampleProposalWorkflowV2Async ~40 LOC seed QT-DX-V2-001 IsUserSelectable=true NOT gated DemoSeed per gotcha #51. SeedMenuTreeAsync +4 row (Off_DeXuat sub-group + 3 leaf). FE 2 app (em main solo + Implementer 529 fail fallback): - types/proposal.ts × 2 SHA256 IDENTICAL 95607052ff1138f2 - ProposalsListPage.tsx × 2 IDENTICAL 603f0d9cf74cd09a — table 6 cột + Status badge + filter - ProposalCreatePage.tsx × 2 IDENTICAL 6aed3a76563dd576 — Form Header card - ProposalDetailPage.tsx × 2 IDENTICAL 3dc229ea8dcc9bc0 — 3 Section + WorkflowActions - Pattern 16-bis 8× cumulative (App.tsx + menuKeys + Layout staticMap 3 entry) Verify: - dotnet build PASS 0 error 2 warning pre-existing DocxRenderer - dotnet test 130/130 PASS baseline preserve - npm build × 2 PASS (fe-admin 14.72s + fe-user 6.40s) - SHA256 verify 4 file × 2 app all IDENTICAL Pattern reinforced cumulative S37: - Pattern 12-bis cross-module mirror 11× (PE V2 → Proposal V2 ApproveV2) - Pattern 16-bis 4-place mirror cross-app 8× - gotcha #53 5th occurrence Implementer mid-exploration truncation + 529 Overload 1× — em main solo fallback proven Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@ -102,6 +102,12 @@ public class ApplicationDbContext
|
||||
public DbSet<MeetingBooking> MeetingBookings => Set<MeetingBooking>();
|
||||
public DbSet<MeetingBookingAttendee> MeetingBookingAttendees => Set<MeetingBookingAttendee>();
|
||||
|
||||
// Phase 10.3 G-O3 (Mig 38 — S37) — Đề xuất + Attachment + LevelOpinion + CodeSequence.
|
||||
public DbSet<Proposal> Proposals => Set<Proposal>();
|
||||
public DbSet<ProposalAttachment> ProposalAttachments => Set<ProposalAttachment>();
|
||||
public DbSet<ProposalLevelOpinion> ProposalLevelOpinions => Set<ProposalLevelOpinion>();
|
||||
public DbSet<ProposalCodeSequence> ProposalCodeSequences => Set<ProposalCodeSequence>();
|
||||
|
||||
protected override void OnModelCreating(ModelBuilder builder)
|
||||
{
|
||||
base.OnModelCreating(builder);
|
||||
|
||||
@ -0,0 +1,27 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Metadata.Builders;
|
||||
using SolutionErp.Domain.Office;
|
||||
|
||||
namespace SolutionErp.Infrastructure.Persistence.Configurations;
|
||||
|
||||
// EF Mig 38 G-O3 (S37) — Attachment cho Đề xuất.
|
||||
// FK Cascade Proposal (wipe khi xoá Đề xuất).
|
||||
public class ProposalAttachmentConfiguration : IEntityTypeConfiguration<ProposalAttachment>
|
||||
{
|
||||
public void Configure(EntityTypeBuilder<ProposalAttachment> e)
|
||||
{
|
||||
e.ToTable("ProposalAttachments");
|
||||
|
||||
e.Property(x => x.FileName).HasMaxLength(500).IsRequired();
|
||||
e.Property(x => x.FilePath).HasMaxLength(1000).IsRequired();
|
||||
e.Property(x => x.MimeType).HasMaxLength(200);
|
||||
e.Property(x => x.UploadedByFullName).HasMaxLength(200).IsRequired();
|
||||
|
||||
e.HasOne(x => x.Proposal)
|
||||
.WithMany(p => p.Attachments)
|
||||
.HasForeignKey(x => x.ProposalId)
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
e.HasIndex(x => x.ProposalId);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,19 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Metadata.Builders;
|
||||
using SolutionErp.Domain.Office;
|
||||
|
||||
namespace SolutionErp.Infrastructure.Persistence.Configurations;
|
||||
|
||||
// EF Mig 38 G-O3 (S37) — Sequence generator cho mã Đề xuất.
|
||||
// Mirror PurchaseEvaluationCodeSequenceConfiguration pattern.
|
||||
// PK = Prefix (string) — "DX/2026". LastSeq tăng atomic.
|
||||
public class ProposalCodeSequenceConfiguration : IEntityTypeConfiguration<ProposalCodeSequence>
|
||||
{
|
||||
public void Configure(EntityTypeBuilder<ProposalCodeSequence> e)
|
||||
{
|
||||
e.ToTable("ProposalCodeSequences");
|
||||
|
||||
e.HasKey(x => x.Prefix);
|
||||
e.Property(x => x.Prefix).HasMaxLength(20); // "DX/2026" max 7 chars OK
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,31 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Metadata.Builders;
|
||||
using SolutionErp.Domain.Office;
|
||||
|
||||
namespace SolutionErp.Infrastructure.Persistence.Configurations;
|
||||
|
||||
// EF Mig 38 G-O3 (S37) — Đề xuất aggregate root.
|
||||
// Cookie-cutter mirror PurchaseEvaluationConfiguration pattern.
|
||||
public class ProposalConfiguration : IEntityTypeConfiguration<Proposal>
|
||||
{
|
||||
public void Configure(EntityTypeBuilder<Proposal> e)
|
||||
{
|
||||
e.ToTable("Proposals");
|
||||
|
||||
e.Property(x => x.MaDeXuat).HasMaxLength(50); // "DX/2026/001"
|
||||
e.Property(x => x.Title).HasMaxLength(300).IsRequired();
|
||||
e.Property(x => x.Description).HasMaxLength(5000);
|
||||
e.Property(x => x.AmountEstimate).HasColumnType("decimal(18,2)");
|
||||
e.Property(x => x.Status).HasConversion<int>();
|
||||
e.Property(x => x.RejectedFromStatus).HasConversion<int>();
|
||||
|
||||
// MaDeXuat optional UNIQUE (gen sau Submit lần đầu)
|
||||
e.HasIndex(x => x.MaDeXuat).IsUnique().HasFilter("[MaDeXuat] IS NOT NULL");
|
||||
|
||||
// Index Status + Drafter cho list filter/inbox
|
||||
e.HasIndex(x => x.Status);
|
||||
e.HasIndex(x => x.DrafterUserId);
|
||||
e.HasIndex(x => x.DepartmentId);
|
||||
e.HasIndex(x => x.ApprovalWorkflowId);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,31 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Metadata.Builders;
|
||||
using SolutionErp.Domain.Office;
|
||||
|
||||
namespace SolutionErp.Infrastructure.Persistence.Configurations;
|
||||
|
||||
// EF Mig 38 G-O3 (S37) — Ý kiến cấp duyệt V2 dynamic. UPSERT auto từ ApproveV2Async.
|
||||
// Cookie-cutter mirror PurchaseEvaluationLevelOpinionConfiguration (Mig 26).
|
||||
public class ProposalLevelOpinionConfiguration : IEntityTypeConfiguration<ProposalLevelOpinion>
|
||||
{
|
||||
public void Configure(EntityTypeBuilder<ProposalLevelOpinion> e)
|
||||
{
|
||||
e.ToTable("ProposalLevelOpinions");
|
||||
|
||||
e.Property(x => x.Comment).HasMaxLength(2000);
|
||||
e.Property(x => x.SignedByFullName).HasMaxLength(200).IsRequired();
|
||||
|
||||
e.HasOne(x => x.Proposal)
|
||||
.WithMany(p => p.LevelOpinions)
|
||||
.HasForeignKey(x => x.ProposalId)
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
e.HasOne(x => x.Level)
|
||||
.WithMany()
|
||||
.HasForeignKey(x => x.ApprovalWorkflowLevelId)
|
||||
.OnDelete(DeleteBehavior.Restrict);
|
||||
|
||||
e.HasIndex(x => new { x.ProposalId, x.ApprovalWorkflowLevelId }).IsUnique();
|
||||
e.HasIndex(x => x.ApprovalWorkflowLevelId);
|
||||
}
|
||||
}
|
||||
@ -132,6 +132,11 @@ public static class DbInitializer
|
||||
// Idempotent: skip nếu workflow QT-HD-V2-001 đã tồn tại.
|
||||
await SeedSampleContractWorkflowV2Async(db, userManager, logger);
|
||||
|
||||
// Phase 10.3 G-O3 (Mig 38 — S37 2026-05-28). Infrastructure seed sample workflow
|
||||
// ApplicableType=ProposalGeneral=4 cho Drafter Workspace dropdown.
|
||||
// NOT gated DemoSeed per gotcha #51 INFRASTRUCTURE seed.
|
||||
await SeedSampleProposalWorkflowV2Async(db, userManager, logger);
|
||||
|
||||
await WarnDefaultAdminPasswordAsync(userManager, logger);
|
||||
}
|
||||
|
||||
@ -239,6 +244,56 @@ public static class DbInitializer
|
||||
logger.LogInformation("Seeded sample ApprovalWorkflow V2 for Contract: QT-HD-V2-001 v01");
|
||||
}
|
||||
|
||||
// Phase 10.3 G-O3 (Mig 38 — S37) — Sample workflow Proposal cho UAT.
|
||||
// Mirror SeedSampleContractWorkflowV2Async pattern. INFRASTRUCTURE seed NOT gated.
|
||||
private static async Task SeedSampleProposalWorkflowV2Async(
|
||||
ApplicationDbContext db, UserManager<User> userManager, ILogger logger)
|
||||
{
|
||||
var hasAnyProposal = await db.ApprovalWorkflows
|
||||
.AnyAsync(w => w.ApplicableType == ApprovalWorkflowApplicableType.ProposalGeneral);
|
||||
if (hasAnyProposal) return;
|
||||
|
||||
var approver = await userManager.FindByEmailAsync("binh.le@solutions.com.vn");
|
||||
if (approver is null)
|
||||
{
|
||||
logger.LogWarning("SeedSampleProposalWorkflowV2Async: skip — approver binh.le@solutions.com.vn not found");
|
||||
return;
|
||||
}
|
||||
|
||||
var ccmDept = await db.Departments.FirstOrDefaultAsync(d => d.Code == "CCM");
|
||||
|
||||
var wf = new ApprovalWorkflow
|
||||
{
|
||||
Code = "QT-DX-V2-001",
|
||||
Version = 1,
|
||||
ApplicableType = ApprovalWorkflowApplicableType.ProposalGeneral,
|
||||
Name = "Quy trình duyệt Đề xuất mẫu V2",
|
||||
Description = "Sample seed UAT — 1 Bước Phòng CCM × 1 Cấp (Lê Văn Bình). Admin có thể clone tạo version mới qua Designer.",
|
||||
IsActive = true,
|
||||
IsUserSelectable = true,
|
||||
ActivatedAt = DateTime.UtcNow,
|
||||
};
|
||||
var step = new ApprovalWorkflowStep
|
||||
{
|
||||
ApprovalWorkflow = wf,
|
||||
Order = 1,
|
||||
Name = "Bước 1 - Phòng CCM",
|
||||
DepartmentId = ccmDept?.Id,
|
||||
};
|
||||
var level = new ApprovalWorkflowLevel
|
||||
{
|
||||
Step = step,
|
||||
Order = 1,
|
||||
Name = "Cấp 1",
|
||||
ApproverUserId = approver.Id,
|
||||
};
|
||||
wf.Steps.Add(step);
|
||||
step.Levels.Add(level);
|
||||
db.ApprovalWorkflows.Add(wf);
|
||||
await db.SaveChangesAsync();
|
||||
logger.LogInformation("Seeded sample ApprovalWorkflow V2 for Proposal: QT-DX-V2-001 v01");
|
||||
}
|
||||
|
||||
// Seed 4 master catalogs với defaults cho user nhập liệu Details. Idempotent:
|
||||
// skip per-table nếu đã có row (admin có thể đã thêm/sửa — không clobber).
|
||||
private static async Task SeedCatalogsAsync(ApplicationDbContext db, ILogger logger)
|
||||
@ -1507,6 +1562,11 @@ public static class DbInitializer
|
||||
(MenuKeys.OffPhongHopView, "Xem lịch", MenuKeys.OffPhongHop, 1, "CalendarDays"),
|
||||
(MenuKeys.OffPhongHopManage, "Quản lý phòng", MenuKeys.OffPhongHop, 2, "Building2"),
|
||||
(MenuKeys.OffPhongHopBook, "Đặt phòng", MenuKeys.OffPhongHop, 3, "CalendarPlus"),
|
||||
// Phase 10.3 G-O3 (Mig 38 — S37 2026-05-28). Sub-group "Đề xuất" + 3 leaf.
|
||||
(MenuKeys.OffDeXuat, "Đề xuất", MenuKeys.Off, 3, "FileSignature"),
|
||||
(MenuKeys.OffDeXuatList, "Danh sách", MenuKeys.OffDeXuat, 1, "List"),
|
||||
(MenuKeys.OffDeXuatCreate, "Tạo mới", MenuKeys.OffDeXuat, 2, "Plus"),
|
||||
(MenuKeys.OffDeXuatInbox, "Inbox duyệt", MenuKeys.OffDeXuat, 3, "Inbox"),
|
||||
};
|
||||
|
||||
// Per-type sub-menu under Contracts: 1 group + 3 leaves each
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,22 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace SolutionErp.Infrastructure.Persistence.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class ExtendApplicableTypeForWorkflowApps : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
5428
src/Backend/SolutionErp.Infrastructure/Persistence/Migrations/20260528082726_AddProposals.Designer.cs
generated
Normal file
5428
src/Backend/SolutionErp.Infrastructure/Persistence/Migrations/20260528082726_AddProposals.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,184 @@
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace SolutionErp.Infrastructure.Persistence.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class AddProposals : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.CreateTable(
|
||||
name: "ProposalCodeSequences",
|
||||
columns: table => new
|
||||
{
|
||||
Prefix = table.Column<string>(type: "nvarchar(20)", maxLength: 20, nullable: false),
|
||||
LastSeq = table.Column<int>(type: "int", nullable: false),
|
||||
UpdatedAt = table.Column<DateTime>(type: "datetime2", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_ProposalCodeSequences", x => x.Prefix);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "Proposals",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
|
||||
MaDeXuat = table.Column<string>(type: "nvarchar(50)", maxLength: 50, nullable: true),
|
||||
Title = table.Column<string>(type: "nvarchar(300)", maxLength: 300, nullable: false),
|
||||
Description = table.Column<string>(type: "nvarchar(max)", maxLength: 5000, nullable: true),
|
||||
AmountEstimate = table.Column<decimal>(type: "decimal(18,2)", nullable: true),
|
||||
Status = table.Column<int>(type: "int", nullable: false),
|
||||
DepartmentId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
|
||||
DrafterUserId = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
|
||||
ApprovalWorkflowId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
|
||||
CurrentApprovalLevelOrder = table.Column<int>(type: "int", nullable: true),
|
||||
SlaDeadline = table.Column<DateTime>(type: "datetime2", nullable: true),
|
||||
SlaWarningSent = table.Column<bool>(type: "bit", nullable: false),
|
||||
RejectedFromStatus = 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),
|
||||
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_Proposals", x => x.Id);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "ProposalAttachments",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
|
||||
ProposalId = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
|
||||
FileName = table.Column<string>(type: "nvarchar(500)", maxLength: 500, nullable: false),
|
||||
FilePath = table.Column<string>(type: "nvarchar(1000)", maxLength: 1000, nullable: false),
|
||||
FileSize = table.Column<long>(type: "bigint", nullable: false),
|
||||
MimeType = table.Column<string>(type: "nvarchar(200)", maxLength: 200, nullable: true),
|
||||
UploadedByUserId = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
|
||||
UploadedByFullName = table.Column<string>(type: "nvarchar(200)", maxLength: 200, 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_ProposalAttachments", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_ProposalAttachments_Proposals_ProposalId",
|
||||
column: x => x.ProposalId,
|
||||
principalTable: "Proposals",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "ProposalLevelOpinions",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
|
||||
ProposalId = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
|
||||
ApprovalWorkflowLevelId = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
|
||||
Comment = table.Column<string>(type: "nvarchar(2000)", maxLength: 2000, nullable: true),
|
||||
SignedAt = table.Column<DateTime>(type: "datetime2", nullable: false),
|
||||
SignedByUserId = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
|
||||
SignedByFullName = table.Column<string>(type: "nvarchar(200)", maxLength: 200, 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_ProposalLevelOpinions", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_ProposalLevelOpinions_ApprovalWorkflowLevels_ApprovalWorkflowLevelId",
|
||||
column: x => x.ApprovalWorkflowLevelId,
|
||||
principalTable: "ApprovalWorkflowLevels",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Restrict);
|
||||
table.ForeignKey(
|
||||
name: "FK_ProposalLevelOpinions_Proposals_ProposalId",
|
||||
column: x => x.ProposalId,
|
||||
principalTable: "Proposals",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_ProposalAttachments_ProposalId",
|
||||
table: "ProposalAttachments",
|
||||
column: "ProposalId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_ProposalLevelOpinions_ApprovalWorkflowLevelId",
|
||||
table: "ProposalLevelOpinions",
|
||||
column: "ApprovalWorkflowLevelId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_ProposalLevelOpinions_ProposalId_ApprovalWorkflowLevelId",
|
||||
table: "ProposalLevelOpinions",
|
||||
columns: new[] { "ProposalId", "ApprovalWorkflowLevelId" },
|
||||
unique: true);
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_Proposals_ApprovalWorkflowId",
|
||||
table: "Proposals",
|
||||
column: "ApprovalWorkflowId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_Proposals_DepartmentId",
|
||||
table: "Proposals",
|
||||
column: "DepartmentId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_Proposals_DrafterUserId",
|
||||
table: "Proposals",
|
||||
column: "DrafterUserId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_Proposals_MaDeXuat",
|
||||
table: "Proposals",
|
||||
column: "MaDeXuat",
|
||||
unique: true,
|
||||
filter: "[MaDeXuat] IS NOT NULL");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_Proposals_Status",
|
||||
table: "Proposals",
|
||||
column: "Status");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "ProposalAttachments");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "ProposalCodeSequences");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "ProposalLevelOpinions");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "Proposals");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -3623,6 +3623,227 @@ namespace SolutionErp.Infrastructure.Persistence.Migrations
|
||||
b.ToTable("MeetingRooms", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("SolutionErp.Domain.Office.Proposal", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("uniqueidentifier");
|
||||
|
||||
b.Property<decimal?>("AmountEstimate")
|
||||
.HasColumnType("decimal(18,2)");
|
||||
|
||||
b.Property<Guid?>("ApprovalWorkflowId")
|
||||
.HasColumnType("uniqueidentifier");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.Property<Guid?>("CreatedBy")
|
||||
.HasColumnType("uniqueidentifier");
|
||||
|
||||
b.Property<int?>("CurrentApprovalLevelOrder")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<DateTime?>("DeletedAt")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.Property<Guid?>("DeletedBy")
|
||||
.HasColumnType("uniqueidentifier");
|
||||
|
||||
b.Property<Guid?>("DepartmentId")
|
||||
.HasColumnType("uniqueidentifier");
|
||||
|
||||
b.Property<string>("Description")
|
||||
.HasMaxLength(5000)
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<Guid>("DrafterUserId")
|
||||
.HasColumnType("uniqueidentifier");
|
||||
|
||||
b.Property<bool>("IsDeleted")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<string>("MaDeXuat")
|
||||
.HasMaxLength(50)
|
||||
.HasColumnType("nvarchar(50)");
|
||||
|
||||
b.Property<int?>("RejectedFromStatus")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<DateTime?>("SlaDeadline")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.Property<bool>("SlaWarningSent")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<int>("Status")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("Title")
|
||||
.IsRequired()
|
||||
.HasMaxLength(300)
|
||||
.HasColumnType("nvarchar(300)");
|
||||
|
||||
b.Property<DateTime?>("UpdatedAt")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.Property<Guid?>("UpdatedBy")
|
||||
.HasColumnType("uniqueidentifier");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("ApprovalWorkflowId");
|
||||
|
||||
b.HasIndex("DepartmentId");
|
||||
|
||||
b.HasIndex("DrafterUserId");
|
||||
|
||||
b.HasIndex("MaDeXuat")
|
||||
.IsUnique()
|
||||
.HasFilter("[MaDeXuat] IS NOT NULL");
|
||||
|
||||
b.HasIndex("Status");
|
||||
|
||||
b.ToTable("Proposals", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("SolutionErp.Domain.Office.ProposalAttachment", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.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<string>("FileName")
|
||||
.IsRequired()
|
||||
.HasMaxLength(500)
|
||||
.HasColumnType("nvarchar(500)");
|
||||
|
||||
b.Property<string>("FilePath")
|
||||
.IsRequired()
|
||||
.HasMaxLength(1000)
|
||||
.HasColumnType("nvarchar(1000)");
|
||||
|
||||
b.Property<long>("FileSize")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<bool>("IsDeleted")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<string>("MimeType")
|
||||
.HasMaxLength(200)
|
||||
.HasColumnType("nvarchar(200)");
|
||||
|
||||
b.Property<Guid>("ProposalId")
|
||||
.HasColumnType("uniqueidentifier");
|
||||
|
||||
b.Property<DateTime?>("UpdatedAt")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.Property<Guid?>("UpdatedBy")
|
||||
.HasColumnType("uniqueidentifier");
|
||||
|
||||
b.Property<string>("UploadedByFullName")
|
||||
.IsRequired()
|
||||
.HasMaxLength(200)
|
||||
.HasColumnType("nvarchar(200)");
|
||||
|
||||
b.Property<Guid>("UploadedByUserId")
|
||||
.HasColumnType("uniqueidentifier");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("ProposalId");
|
||||
|
||||
b.ToTable("ProposalAttachments", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("SolutionErp.Domain.Office.ProposalCodeSequence", b =>
|
||||
{
|
||||
b.Property<string>("Prefix")
|
||||
.HasMaxLength(20)
|
||||
.HasColumnType("nvarchar(20)");
|
||||
|
||||
b.Property<int>("LastSeq")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<DateTime>("UpdatedAt")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.HasKey("Prefix");
|
||||
|
||||
b.ToTable("ProposalCodeSequences", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("SolutionErp.Domain.Office.ProposalLevelOpinion", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("uniqueidentifier");
|
||||
|
||||
b.Property<Guid>("ApprovalWorkflowLevelId")
|
||||
.HasColumnType("uniqueidentifier");
|
||||
|
||||
b.Property<string>("Comment")
|
||||
.HasMaxLength(2000)
|
||||
.HasColumnType("nvarchar(2000)");
|
||||
|
||||
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<bool>("IsDeleted")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<Guid>("ProposalId")
|
||||
.HasColumnType("uniqueidentifier");
|
||||
|
||||
b.Property<DateTime>("SignedAt")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.Property<string>("SignedByFullName")
|
||||
.IsRequired()
|
||||
.HasMaxLength(200)
|
||||
.HasColumnType("nvarchar(200)");
|
||||
|
||||
b.Property<Guid>("SignedByUserId")
|
||||
.HasColumnType("uniqueidentifier");
|
||||
|
||||
b.Property<DateTime?>("UpdatedAt")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.Property<Guid?>("UpdatedBy")
|
||||
.HasColumnType("uniqueidentifier");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("ApprovalWorkflowLevelId");
|
||||
|
||||
b.HasIndex("ProposalId", "ApprovalWorkflowLevelId")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("ProposalLevelOpinions", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("SolutionErp.Domain.PurchaseEvaluations.PurchaseEvaluation", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
@ -4887,6 +5108,36 @@ namespace SolutionErp.Infrastructure.Persistence.Migrations
|
||||
b.Navigation("Booking");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("SolutionErp.Domain.Office.ProposalAttachment", b =>
|
||||
{
|
||||
b.HasOne("SolutionErp.Domain.Office.Proposal", "Proposal")
|
||||
.WithMany("Attachments")
|
||||
.HasForeignKey("ProposalId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Proposal");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("SolutionErp.Domain.Office.ProposalLevelOpinion", b =>
|
||||
{
|
||||
b.HasOne("SolutionErp.Domain.ApprovalWorkflowsV2.ApprovalWorkflowLevel", "Level")
|
||||
.WithMany()
|
||||
.HasForeignKey("ApprovalWorkflowLevelId")
|
||||
.OnDelete(DeleteBehavior.Restrict)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("SolutionErp.Domain.Office.Proposal", "Proposal")
|
||||
.WithMany("LevelOpinions")
|
||||
.HasForeignKey("ProposalId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Level");
|
||||
|
||||
b.Navigation("Proposal");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("SolutionErp.Domain.PurchaseEvaluations.PurchaseEvaluation", b =>
|
||||
{
|
||||
b.HasOne("SolutionErp.Domain.ApprovalWorkflowsV2.ApprovalWorkflow", null)
|
||||
@ -5126,6 +5377,13 @@ namespace SolutionErp.Infrastructure.Persistence.Migrations
|
||||
b.Navigation("Attendees");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("SolutionErp.Domain.Office.Proposal", b =>
|
||||
{
|
||||
b.Navigation("Attachments");
|
||||
|
||||
b.Navigation("LevelOpinions");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("SolutionErp.Domain.PurchaseEvaluations.PurchaseEvaluation", b =>
|
||||
{
|
||||
b.Navigation("Approvals");
|
||||
|
||||
Reference in New Issue
Block a user