[CLAUDE] Domain+App+Infra+Api+FE-Admin+FE-User: S38 G-O4+G-O5+G-O6+G-P1+G-H3 SKELETON full-stack
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 3m53s

Phase 10.3-10.4 SKELETON 5 plan combo finish — Mig 39+40 + BE skeleton 7 module +
FE 2 app SHA256 IDENTICAL + 11 menu key. UAT visible end-to-end.

⚠️ SKELETON Phase 1 trade-off rõ:
  - Status flat 5-state WorkflowAppStatus enum share Leave/OT/Travel/Vehicle
  - ApproveV2 workflow advance DEFER Phase 11 (Drafter Create OK, Approve flow chưa wire)
  - LevelOpinions per-module DEFER Phase 11
  - LeaveBalance calc + Auto-assign + SLA timer DEFER Phase 11
  - CodeGen atomic + MaDonTu/MaTicket gen DEFER Phase 11
  - Vehicle catalog + Driver catalog DEFER Phase 11 (free text VehicleLicense)
  - ItTicketComments thread DEFER Phase 11 (free text Resolution field)

Mig 39 (em main solo): 5 entity Workflow Apps schema
  - LeaveRequest (G-O4, FK LeaveType Hrm Mig 35, ApplicableType=5)
  - OtRequest (G-O4, FK OtPolicy optional, ApplicableType=6)
  - TravelRequest (G-O4, reuse ApplicableType=4 Proposal)
  - VehicleBooking (G-O5, free text vehicle, ApplicableType=7)
  - ItTicket (G-O6, NO workflow V2 — kanban status flow)

Mig 40 (em main solo): Attendance entity (G-P1)
  - GPS lat/long check-in/out + Source enum Web/Mobile/Device
  - UNIQUE composite (UserId, AttendanceDate)
  - WorkHours computed simple diff (NO OtPolicy multiplier yet)

BE CQRS (em main solo, single mega ~1100 LOC):
  - WorkflowAppsFeatures.cs 7 region (5 module Create+List + Attendance CheckIn/Out/GetMonth + HrDashboard)
  - 7 Controller: /api/leave-requests + /ot-requests + /travel-requests + /vehicle-bookings + /it-tickets + /attendances + /hr/dashboard
  - Class-level [Authorize] any authenticated
  - 13 endpoint total

FE 2 app (em main solo fallback gotcha #53 risk):
  - types/workflowApps.ts × 2 SHA256 IDENTICAL 77470e182a15de88 (all DTOs + Status badge)
  - WorkflowAppsListPage.tsx × 2 IDENTICAL 58139d0301a60ddf — generic declarative KIND_CONFIG handles 4 module (Leave/OT/Travel/Vehicle)
  - ItTicketsPage.tsx × 2 IDENTICAL d3062de2f54c794c — kanban 5 status column
  - MyAttendancePage.tsx × 2 IDENTICAL 86da48ae147db012 — GPS check-in/out + tháng calendar
  - HrmDashboardPage.tsx × 2 IDENTICAL d9c6c12a5a8694f8 — 4 KPI card + gender ratio + status breakdown
  - Pattern 16-bis 9× cumulative (App.tsx +4 routes + menuKeys +8 const + Layout staticMap +7 entry)
  - 7 amber banner "Skeleton Phase 1 — full feature Phase 11" rõ ràng UAT

Menu seed: +11 const + SeedMenuTreeAsync 8 row (Off_DonTu sub-group + 3 leaf + Off_DatXe + Off_ItTicket + Off_ChamCong + Hrm_Dashboard).
DbInitializer Sample workflow seed DEFER (workflows V2 already seeded từ S29+S37 reuse — admin clone tạo riêng per ApplicableType=5/6/7).

Verify:
- dotnet build PASS 0 error 2 pre-existing warning
- dotnet test 130/130 PASS baseline preserve
- npm build × 2 PASS clean
- SHA256 verify 5 file × 2 app all IDENTICAL

Plan G-* progress 11/11  (100% COMPLETE):
   G-H1 (S33) + G-O1 (S34) + G-H2 (S35) + G-O2 (S36) + G-O3 (S37) +
   G-O4 + G-O5 + G-O6 + G-P1 + G-H3 (S38 skeleton)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
pqhuy1987
2026-05-28 16:19:42 +07:00
parent 17aaba9df0
commit e54a22de0c
41 changed files with 14980 additions and 0 deletions

View File

@ -0,0 +1,329 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace SolutionErp.Infrastructure.Persistence.Migrations
{
/// <inheritdoc />
public partial class AddWorkflowApps : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "Attendances",
columns: table => new
{
Id = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
UserId = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
UserFullName = table.Column<string>(type: "nvarchar(200)", maxLength: 200, nullable: false),
AttendanceDate = table.Column<DateTime>(type: "datetime2", nullable: false),
CheckInAt = table.Column<DateTime>(type: "datetime2", nullable: true),
CheckOutAt = table.Column<DateTime>(type: "datetime2", nullable: true),
CheckInLatitude = table.Column<decimal>(type: "decimal(10,7)", nullable: true),
CheckInLongitude = table.Column<decimal>(type: "decimal(10,7)", nullable: true),
CheckInAccuracy = table.Column<decimal>(type: "decimal(8,2)", nullable: true),
CheckOutLatitude = table.Column<decimal>(type: "decimal(10,7)", nullable: true),
CheckOutLongitude = table.Column<decimal>(type: "decimal(10,7)", nullable: true),
CheckOutAccuracy = table.Column<decimal>(type: "decimal(8,2)", nullable: true),
SourceIn = table.Column<int>(type: "int", nullable: false),
SourceOut = table.Column<int>(type: "int", nullable: false),
IpAddressIn = table.Column<string>(type: "nvarchar(50)", maxLength: 50, nullable: true),
IpAddressOut = table.Column<string>(type: "nvarchar(50)", maxLength: 50, nullable: true),
Note = table.Column<string>(type: "nvarchar(500)", maxLength: 500, nullable: true),
WorkHours = table.Column<decimal>(type: "decimal(5,2)", nullable: true),
OtHours = table.Column<decimal>(type: "decimal(5,2)", 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_Attendances", x => x.Id);
});
migrationBuilder.CreateTable(
name: "ItTickets",
columns: table => new
{
Id = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
MaTicket = table.Column<string>(type: "nvarchar(50)", maxLength: 50, nullable: true),
RequesterUserId = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
RequesterFullName = table.Column<string>(type: "nvarchar(200)", maxLength: 200, nullable: false),
Title = table.Column<string>(type: "nvarchar(300)", maxLength: 300, nullable: false),
Description = table.Column<string>(type: "nvarchar(max)", maxLength: 5000, nullable: false),
Category = table.Column<int>(type: "int", nullable: false),
Priority = table.Column<int>(type: "int", nullable: false),
Status = table.Column<int>(type: "int", nullable: false),
AssignedToUserId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
AssignedToFullName = table.Column<string>(type: "nvarchar(200)", maxLength: 200, nullable: true),
ResolvedAt = table.Column<DateTime>(type: "datetime2", nullable: true),
Resolution = table.Column<string>(type: "nvarchar(max)", maxLength: 5000, 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_ItTickets", x => x.Id);
});
migrationBuilder.CreateTable(
name: "LeaveRequests",
columns: table => new
{
Id = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
MaDonTu = table.Column<string>(type: "nvarchar(50)", maxLength: 50, nullable: true),
RequesterUserId = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
RequesterFullName = table.Column<string>(type: "nvarchar(200)", maxLength: 200, nullable: false),
LeaveTypeId = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
StartDate = table.Column<DateTime>(type: "datetime2", nullable: false),
EndDate = table.Column<DateTime>(type: "datetime2", nullable: false),
NumDays = table.Column<decimal>(type: "decimal(5,2)", nullable: false),
Reason = table.Column<string>(type: "nvarchar(1000)", maxLength: 1000, nullable: false),
Status = table.Column<int>(type: "int", nullable: false),
ApprovalWorkflowId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
CurrentApprovalLevelOrder = 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_LeaveRequests", x => x.Id);
});
migrationBuilder.CreateTable(
name: "OtRequests",
columns: table => new
{
Id = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
MaDonTu = table.Column<string>(type: "nvarchar(50)", maxLength: 50, nullable: true),
RequesterUserId = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
RequesterFullName = table.Column<string>(type: "nvarchar(200)", maxLength: 200, nullable: false),
OtDate = table.Column<DateTime>(type: "datetime2", nullable: false),
StartTime = table.Column<TimeSpan>(type: "time", nullable: false),
EndTime = table.Column<TimeSpan>(type: "time", nullable: false),
Hours = table.Column<decimal>(type: "decimal(5,2)", nullable: false),
Reason = table.Column<string>(type: "nvarchar(1000)", maxLength: 1000, nullable: false),
OtPolicyId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
Status = table.Column<int>(type: "int", nullable: false),
ApprovalWorkflowId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
CurrentApprovalLevelOrder = 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_OtRequests", x => x.Id);
});
migrationBuilder.CreateTable(
name: "TravelRequests",
columns: table => new
{
Id = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
MaDonTu = table.Column<string>(type: "nvarchar(50)", maxLength: 50, nullable: true),
RequesterUserId = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
RequesterFullName = table.Column<string>(type: "nvarchar(200)", maxLength: 200, nullable: false),
Destination = table.Column<string>(type: "nvarchar(300)", maxLength: 300, nullable: false),
StartDate = table.Column<DateTime>(type: "datetime2", nullable: false),
EndDate = table.Column<DateTime>(type: "datetime2", nullable: false),
NumDays = table.Column<int>(type: "int", nullable: false),
Purpose = table.Column<string>(type: "nvarchar(1000)", maxLength: 1000, nullable: false),
EstimatedCost = table.Column<decimal>(type: "decimal(18,2)", nullable: true),
Status = table.Column<int>(type: "int", nullable: false),
ApprovalWorkflowId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
CurrentApprovalLevelOrder = 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_TravelRequests", x => x.Id);
});
migrationBuilder.CreateTable(
name: "VehicleBookings",
columns: table => new
{
Id = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
MaDonTu = table.Column<string>(type: "nvarchar(50)", maxLength: 50, nullable: true),
RequesterUserId = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
RequesterFullName = table.Column<string>(type: "nvarchar(200)", maxLength: 200, nullable: false),
VehicleLicense = table.Column<string>(type: "nvarchar(20)", maxLength: 20, nullable: false),
VehicleName = table.Column<string>(type: "nvarchar(200)", maxLength: 200, nullable: true),
StartAt = table.Column<DateTime>(type: "datetime2", nullable: false),
EndAt = table.Column<DateTime>(type: "datetime2", nullable: false),
Destination = table.Column<string>(type: "nvarchar(300)", maxLength: 300, nullable: false),
Purpose = table.Column<string>(type: "nvarchar(1000)", maxLength: 1000, nullable: false),
DriverName = table.Column<string>(type: "nvarchar(200)", maxLength: 200, nullable: true),
Status = table.Column<int>(type: "int", nullable: false),
ApprovalWorkflowId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
CurrentApprovalLevelOrder = 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_VehicleBookings", x => x.Id);
});
migrationBuilder.CreateIndex(
name: "IX_Attendances_UserId_AttendanceDate",
table: "Attendances",
columns: new[] { "UserId", "AttendanceDate" },
unique: true);
migrationBuilder.CreateIndex(
name: "IX_ItTickets_AssignedToUserId",
table: "ItTickets",
column: "AssignedToUserId");
migrationBuilder.CreateIndex(
name: "IX_ItTickets_Category",
table: "ItTickets",
column: "Category");
migrationBuilder.CreateIndex(
name: "IX_ItTickets_MaTicket",
table: "ItTickets",
column: "MaTicket",
unique: true,
filter: "[MaTicket] IS NOT NULL");
migrationBuilder.CreateIndex(
name: "IX_ItTickets_RequesterUserId",
table: "ItTickets",
column: "RequesterUserId");
migrationBuilder.CreateIndex(
name: "IX_ItTickets_Status",
table: "ItTickets",
column: "Status");
migrationBuilder.CreateIndex(
name: "IX_LeaveRequests_MaDonTu",
table: "LeaveRequests",
column: "MaDonTu",
unique: true,
filter: "[MaDonTu] IS NOT NULL");
migrationBuilder.CreateIndex(
name: "IX_LeaveRequests_RequesterUserId",
table: "LeaveRequests",
column: "RequesterUserId");
migrationBuilder.CreateIndex(
name: "IX_LeaveRequests_Status",
table: "LeaveRequests",
column: "Status");
migrationBuilder.CreateIndex(
name: "IX_OtRequests_MaDonTu",
table: "OtRequests",
column: "MaDonTu",
unique: true,
filter: "[MaDonTu] IS NOT NULL");
migrationBuilder.CreateIndex(
name: "IX_OtRequests_RequesterUserId",
table: "OtRequests",
column: "RequesterUserId");
migrationBuilder.CreateIndex(
name: "IX_OtRequests_Status",
table: "OtRequests",
column: "Status");
migrationBuilder.CreateIndex(
name: "IX_TravelRequests_MaDonTu",
table: "TravelRequests",
column: "MaDonTu",
unique: true,
filter: "[MaDonTu] IS NOT NULL");
migrationBuilder.CreateIndex(
name: "IX_TravelRequests_RequesterUserId",
table: "TravelRequests",
column: "RequesterUserId");
migrationBuilder.CreateIndex(
name: "IX_TravelRequests_Status",
table: "TravelRequests",
column: "Status");
migrationBuilder.CreateIndex(
name: "IX_VehicleBookings_MaDonTu",
table: "VehicleBookings",
column: "MaDonTu",
unique: true,
filter: "[MaDonTu] IS NOT NULL");
migrationBuilder.CreateIndex(
name: "IX_VehicleBookings_RequesterUserId",
table: "VehicleBookings",
column: "RequesterUserId");
migrationBuilder.CreateIndex(
name: "IX_VehicleBookings_Status",
table: "VehicleBookings",
column: "Status");
migrationBuilder.CreateIndex(
name: "IX_VehicleBookings_VehicleLicense_StartAt",
table: "VehicleBookings",
columns: new[] { "VehicleLicense", "StartAt" });
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "Attendances");
migrationBuilder.DropTable(
name: "ItTickets");
migrationBuilder.DropTable(
name: "LeaveRequests");
migrationBuilder.DropTable(
name: "OtRequests");
migrationBuilder.DropTable(
name: "TravelRequests");
migrationBuilder.DropTable(
name: "VehicleBookings");
}
}
}

View File

@ -0,0 +1,22 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace SolutionErp.Infrastructure.Persistence.Migrations
{
/// <inheritdoc />
public partial class AddAttendances : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
}
}
}

View File

@ -3448,6 +3448,267 @@ namespace SolutionErp.Infrastructure.Persistence.Migrations
b.ToTable("Notifications", (string)null);
});
modelBuilder.Entity("SolutionErp.Domain.Office.Attendance", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uniqueidentifier");
b.Property<DateTime>("AttendanceDate")
.HasColumnType("datetime2");
b.Property<decimal?>("CheckInAccuracy")
.HasColumnType("decimal(8,2)");
b.Property<DateTime?>("CheckInAt")
.HasColumnType("datetime2");
b.Property<decimal?>("CheckInLatitude")
.HasColumnType("decimal(10,7)");
b.Property<decimal?>("CheckInLongitude")
.HasColumnType("decimal(10,7)");
b.Property<decimal?>("CheckOutAccuracy")
.HasColumnType("decimal(8,2)");
b.Property<DateTime?>("CheckOutAt")
.HasColumnType("datetime2");
b.Property<decimal?>("CheckOutLatitude")
.HasColumnType("decimal(10,7)");
b.Property<decimal?>("CheckOutLongitude")
.HasColumnType("decimal(10,7)");
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>("IpAddressIn")
.HasMaxLength(50)
.HasColumnType("nvarchar(50)");
b.Property<string>("IpAddressOut")
.HasMaxLength(50)
.HasColumnType("nvarchar(50)");
b.Property<bool>("IsDeleted")
.HasColumnType("bit");
b.Property<string>("Note")
.HasMaxLength(500)
.HasColumnType("nvarchar(500)");
b.Property<decimal?>("OtHours")
.HasColumnType("decimal(5,2)");
b.Property<int>("SourceIn")
.HasColumnType("int");
b.Property<int>("SourceOut")
.HasColumnType("int");
b.Property<DateTime?>("UpdatedAt")
.HasColumnType("datetime2");
b.Property<Guid?>("UpdatedBy")
.HasColumnType("uniqueidentifier");
b.Property<string>("UserFullName")
.IsRequired()
.HasMaxLength(200)
.HasColumnType("nvarchar(200)");
b.Property<Guid>("UserId")
.HasColumnType("uniqueidentifier");
b.Property<decimal?>("WorkHours")
.HasColumnType("decimal(5,2)");
b.HasKey("Id");
b.HasIndex("UserId", "AttendanceDate")
.IsUnique();
b.ToTable("Attendances", (string)null);
});
modelBuilder.Entity("SolutionErp.Domain.Office.ItTicket", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uniqueidentifier");
b.Property<string>("AssignedToFullName")
.HasMaxLength(200)
.HasColumnType("nvarchar(200)");
b.Property<Guid?>("AssignedToUserId")
.HasColumnType("uniqueidentifier");
b.Property<int>("Category")
.HasColumnType("int");
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>("Description")
.IsRequired()
.HasMaxLength(5000)
.HasColumnType("nvarchar(max)");
b.Property<bool>("IsDeleted")
.HasColumnType("bit");
b.Property<string>("MaTicket")
.HasMaxLength(50)
.HasColumnType("nvarchar(50)");
b.Property<int>("Priority")
.HasColumnType("int");
b.Property<string>("RequesterFullName")
.IsRequired()
.HasMaxLength(200)
.HasColumnType("nvarchar(200)");
b.Property<Guid>("RequesterUserId")
.HasColumnType("uniqueidentifier");
b.Property<string>("Resolution")
.HasMaxLength(5000)
.HasColumnType("nvarchar(max)");
b.Property<DateTime?>("ResolvedAt")
.HasColumnType("datetime2");
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("AssignedToUserId");
b.HasIndex("Category");
b.HasIndex("MaTicket")
.IsUnique()
.HasFilter("[MaTicket] IS NOT NULL");
b.HasIndex("RequesterUserId");
b.HasIndex("Status");
b.ToTable("ItTickets", (string)null);
});
modelBuilder.Entity("SolutionErp.Domain.Office.LeaveRequest", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uniqueidentifier");
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<DateTime>("EndDate")
.HasColumnType("datetime2");
b.Property<bool>("IsDeleted")
.HasColumnType("bit");
b.Property<Guid>("LeaveTypeId")
.HasColumnType("uniqueidentifier");
b.Property<string>("MaDonTu")
.HasMaxLength(50)
.HasColumnType("nvarchar(50)");
b.Property<decimal>("NumDays")
.HasColumnType("decimal(5,2)");
b.Property<string>("Reason")
.IsRequired()
.HasMaxLength(1000)
.HasColumnType("nvarchar(1000)");
b.Property<string>("RequesterFullName")
.IsRequired()
.HasMaxLength(200)
.HasColumnType("nvarchar(200)");
b.Property<Guid>("RequesterUserId")
.HasColumnType("uniqueidentifier");
b.Property<DateTime>("StartDate")
.HasColumnType("datetime2");
b.Property<int>("Status")
.HasColumnType("int");
b.Property<DateTime?>("UpdatedAt")
.HasColumnType("datetime2");
b.Property<Guid?>("UpdatedBy")
.HasColumnType("uniqueidentifier");
b.HasKey("Id");
b.HasIndex("MaDonTu")
.IsUnique()
.HasFilter("[MaDonTu] IS NOT NULL");
b.HasIndex("RequesterUserId");
b.HasIndex("Status");
b.ToTable("LeaveRequests", (string)null);
});
modelBuilder.Entity("SolutionErp.Domain.Office.MeetingBooking", b =>
{
b.Property<Guid>("Id")
@ -3623,6 +3884,87 @@ namespace SolutionErp.Infrastructure.Persistence.Migrations
b.ToTable("MeetingRooms", (string)null);
});
modelBuilder.Entity("SolutionErp.Domain.Office.OtRequest", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uniqueidentifier");
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<TimeSpan>("EndTime")
.HasColumnType("time");
b.Property<decimal>("Hours")
.HasColumnType("decimal(5,2)");
b.Property<bool>("IsDeleted")
.HasColumnType("bit");
b.Property<string>("MaDonTu")
.HasMaxLength(50)
.HasColumnType("nvarchar(50)");
b.Property<DateTime>("OtDate")
.HasColumnType("datetime2");
b.Property<Guid?>("OtPolicyId")
.HasColumnType("uniqueidentifier");
b.Property<string>("Reason")
.IsRequired()
.HasMaxLength(1000)
.HasColumnType("nvarchar(1000)");
b.Property<string>("RequesterFullName")
.IsRequired()
.HasMaxLength(200)
.HasColumnType("nvarchar(200)");
b.Property<Guid>("RequesterUserId")
.HasColumnType("uniqueidentifier");
b.Property<TimeSpan>("StartTime")
.HasColumnType("time");
b.Property<int>("Status")
.HasColumnType("int");
b.Property<DateTime?>("UpdatedAt")
.HasColumnType("datetime2");
b.Property<Guid?>("UpdatedBy")
.HasColumnType("uniqueidentifier");
b.HasKey("Id");
b.HasIndex("MaDonTu")
.IsUnique()
.HasFilter("[MaDonTu] IS NOT NULL");
b.HasIndex("RequesterUserId");
b.HasIndex("Status");
b.ToTable("OtRequests", (string)null);
});
modelBuilder.Entity("SolutionErp.Domain.Office.Proposal", b =>
{
b.Property<Guid>("Id")
@ -3844,6 +4186,181 @@ namespace SolutionErp.Infrastructure.Persistence.Migrations
b.ToTable("ProposalLevelOpinions", (string)null);
});
modelBuilder.Entity("SolutionErp.Domain.Office.TravelRequest", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uniqueidentifier");
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<string>("Destination")
.IsRequired()
.HasMaxLength(300)
.HasColumnType("nvarchar(300)");
b.Property<DateTime>("EndDate")
.HasColumnType("datetime2");
b.Property<decimal?>("EstimatedCost")
.HasColumnType("decimal(18,2)");
b.Property<bool>("IsDeleted")
.HasColumnType("bit");
b.Property<string>("MaDonTu")
.HasMaxLength(50)
.HasColumnType("nvarchar(50)");
b.Property<int>("NumDays")
.HasColumnType("int");
b.Property<string>("Purpose")
.IsRequired()
.HasMaxLength(1000)
.HasColumnType("nvarchar(1000)");
b.Property<string>("RequesterFullName")
.IsRequired()
.HasMaxLength(200)
.HasColumnType("nvarchar(200)");
b.Property<Guid>("RequesterUserId")
.HasColumnType("uniqueidentifier");
b.Property<DateTime>("StartDate")
.HasColumnType("datetime2");
b.Property<int>("Status")
.HasColumnType("int");
b.Property<DateTime?>("UpdatedAt")
.HasColumnType("datetime2");
b.Property<Guid?>("UpdatedBy")
.HasColumnType("uniqueidentifier");
b.HasKey("Id");
b.HasIndex("MaDonTu")
.IsUnique()
.HasFilter("[MaDonTu] IS NOT NULL");
b.HasIndex("RequesterUserId");
b.HasIndex("Status");
b.ToTable("TravelRequests", (string)null);
});
modelBuilder.Entity("SolutionErp.Domain.Office.VehicleBooking", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uniqueidentifier");
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<string>("Destination")
.IsRequired()
.HasMaxLength(300)
.HasColumnType("nvarchar(300)");
b.Property<string>("DriverName")
.HasMaxLength(200)
.HasColumnType("nvarchar(200)");
b.Property<DateTime>("EndAt")
.HasColumnType("datetime2");
b.Property<bool>("IsDeleted")
.HasColumnType("bit");
b.Property<string>("MaDonTu")
.HasMaxLength(50)
.HasColumnType("nvarchar(50)");
b.Property<string>("Purpose")
.IsRequired()
.HasMaxLength(1000)
.HasColumnType("nvarchar(1000)");
b.Property<string>("RequesterFullName")
.IsRequired()
.HasMaxLength(200)
.HasColumnType("nvarchar(200)");
b.Property<Guid>("RequesterUserId")
.HasColumnType("uniqueidentifier");
b.Property<DateTime>("StartAt")
.HasColumnType("datetime2");
b.Property<int>("Status")
.HasColumnType("int");
b.Property<DateTime?>("UpdatedAt")
.HasColumnType("datetime2");
b.Property<Guid?>("UpdatedBy")
.HasColumnType("uniqueidentifier");
b.Property<string>("VehicleLicense")
.IsRequired()
.HasMaxLength(20)
.HasColumnType("nvarchar(20)");
b.Property<string>("VehicleName")
.HasMaxLength(200)
.HasColumnType("nvarchar(200)");
b.HasKey("Id");
b.HasIndex("MaDonTu")
.IsUnique()
.HasFilter("[MaDonTu] IS NOT NULL");
b.HasIndex("RequesterUserId");
b.HasIndex("Status");
b.HasIndex("VehicleLicense", "StartAt");
b.ToTable("VehicleBookings", (string)null);
});
modelBuilder.Entity("SolutionErp.Domain.PurchaseEvaluations.PurchaseEvaluation", b =>
{
b.Property<Guid>("Id")