[CLAUDE] Domain+App+Api: 4 master catalogs cho Details (migration 10)
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 2m48s

User feedback: thêm Master Data cho phần Chi tiết (line items) — autocomplete
khi user nhập field thay vì gõ tay free text.

## 4 entities mới (Domain/Master/Catalogs/)

| Entity | Bảng | Dùng cho HĐ Detail |
|---|---|---|
| UnitOfMeasure | UnitsOfMeasure | Tất cả 7 type (DonViTinh) |
| MaterialItem | MaterialItems | NCC + Mua bán + Nguyên tắc NCC (MaSP/TenSP) |
| ServiceItem | ServiceItems | Dịch vụ + Nguyên tắc DV (MaDichVu/TenDichVu) |
| WorkItem | WorkItems | Thầu phụ + Giao khoán (HangMuc/MaCongViec) |

Common pattern: Code unique (filter IsDeleted=0) + Name + Category +
DefaultUnit + AuditableEntity (soft delete) + IsActive flag.

## Migration 10: AddMasterCatalogs

3-file rule (gotcha #17). Total DB: 32 → 36 tables. Apply LocalDB OK.

## Seed defaults (idempotent — skip per-table nếu có row)

- 20 UnitsOfMeasure: m2, m3, kg, tấn, lít, ngc (ngày công), giờ, gói, ...
- 15 MaterialItems demo: xi măng PCB40, cát vàng, đá 1x2, thép D10, gạch
  4 lỗ, sơn lót, ống PVC...
- 10 ServiceItems demo: vận chuyển, bảo trì, tư vấn, kiểm định, vệ sinh...
- 15 WorkItems demo: đào móng, đổ bê tông, xây tường, trát, lát gạch,
  sơn nước, lắp điện, lắp nước, thấm chống...

## CQRS (Application/Master/Catalogs/CatalogsFeatures.cs ~290 dòng)

Mỗi catalog 5 handlers (List filter q+category, Create với unique code
guard, Update, Delete soft). FluentValidation max length per spec EF.

## Controller (Api/Controllers/CatalogsController.cs)

13 endpoints:
- GET  /api/catalogs/{units|materials|services|work-items}
- POST /api/catalogs/{kind}                  (Admin role)
- PUT  /api/catalogs/{kind}/{id}             (Admin role)
- DELETE /api/catalogs/{kind}/{id}           (Admin role)

Read open cho mọi role (FE Details add form autocomplete cần list).

## Menu (5 mới)

- Catalogs (group, Master parent, order 24, "Library" icon)
  - CatalogUnits "Đơn vị tính" (Ruler)
  - CatalogMaterials "Vật tư / SP" (Package)
  - CatalogServices "Dịch vụ" (Wrench)
  - CatalogWorkItems "Hạng mục công việc" (ListChecks)

Admin auto-grant tất cả CRUD action (qua SeedAdminPermissionsAsync
loop MenuKeys.All).

## Build

dotnet build BE pass (0 error)

## Note

FE admin page CatalogsPage + datalist autocomplete trong Details form
sẽ ở commit kế tiếp.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
pqhuy1987
2026-04-23 12:21:39 +07:00
parent 51449d6b9d
commit e27c54702a
14 changed files with 3302 additions and 0 deletions

View File

@ -6,6 +6,7 @@ using SolutionErp.Domain.Contracts.Details;
using SolutionErp.Domain.Forms;
using SolutionErp.Domain.Identity;
using SolutionErp.Domain.Master;
using SolutionErp.Domain.Master.Catalogs;
using SolutionErp.Domain.Notifications;
namespace SolutionErp.Infrastructure.Persistence;
@ -18,6 +19,10 @@ public class ApplicationDbContext
public DbSet<Supplier> Suppliers => Set<Supplier>();
public DbSet<Project> Projects => Set<Project>();
public DbSet<Department> Departments => Set<Department>();
public DbSet<UnitOfMeasure> UnitsOfMeasure => Set<UnitOfMeasure>();
public DbSet<MaterialItem> MaterialItems => Set<MaterialItem>();
public DbSet<ServiceItem> ServiceItems => Set<ServiceItem>();
public DbSet<WorkItem> WorkItems => Set<WorkItem>();
public DbSet<MenuItem> MenuItems => Set<MenuItem>();
public DbSet<Permission> Permissions => Set<Permission>();
public DbSet<ContractTemplate> ContractTemplates => Set<ContractTemplate>();

View File

@ -0,0 +1,75 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using SolutionErp.Domain.Master.Catalogs;
namespace SolutionErp.Infrastructure.Persistence.Configurations;
// EF config cho 4 master catalogs (UnitsOfMeasure, MaterialItems,
// ServiceItems, WorkItems) — phục vụ autocomplete trong Details add form.
// Pattern: Code unique + IsDeleted query filter + IsActive flag soft-archive.
public class UnitOfMeasureConfiguration : IEntityTypeConfiguration<UnitOfMeasure>
{
public void Configure(EntityTypeBuilder<UnitOfMeasure> b)
{
b.ToTable("UnitsOfMeasure");
b.HasKey(x => x.Id);
b.Property(x => x.Code).HasMaxLength(20).IsRequired();
b.Property(x => x.Name).HasMaxLength(100).IsRequired();
b.Property(x => x.Description).HasMaxLength(500);
b.HasIndex(x => x.Code).IsUnique().HasFilter("[IsDeleted] = 0");
b.HasQueryFilter(x => !x.IsDeleted);
}
}
public class MaterialItemConfiguration : IEntityTypeConfiguration<MaterialItem>
{
public void Configure(EntityTypeBuilder<MaterialItem> b)
{
b.ToTable("MaterialItems");
b.HasKey(x => x.Id);
b.Property(x => x.Code).HasMaxLength(50).IsRequired();
b.Property(x => x.Name).HasMaxLength(200).IsRequired();
b.Property(x => x.Category).HasMaxLength(100);
b.Property(x => x.DefaultUnit).HasMaxLength(50);
b.Property(x => x.Specification).HasMaxLength(1000);
b.Property(x => x.OriginCountry).HasMaxLength(100);
b.HasIndex(x => x.Code).IsUnique().HasFilter("[IsDeleted] = 0");
b.HasIndex(x => x.Category);
b.HasQueryFilter(x => !x.IsDeleted);
}
}
public class ServiceItemConfiguration : IEntityTypeConfiguration<ServiceItem>
{
public void Configure(EntityTypeBuilder<ServiceItem> b)
{
b.ToTable("ServiceItems");
b.HasKey(x => x.Id);
b.Property(x => x.Code).HasMaxLength(50).IsRequired();
b.Property(x => x.Name).HasMaxLength(200).IsRequired();
b.Property(x => x.Category).HasMaxLength(100);
b.Property(x => x.DefaultUnit).HasMaxLength(50);
b.Property(x => x.Description).HasMaxLength(1000);
b.HasIndex(x => x.Code).IsUnique().HasFilter("[IsDeleted] = 0");
b.HasIndex(x => x.Category);
b.HasQueryFilter(x => !x.IsDeleted);
}
}
public class WorkItemConfiguration : IEntityTypeConfiguration<WorkItem>
{
public void Configure(EntityTypeBuilder<WorkItem> b)
{
b.ToTable("WorkItems");
b.HasKey(x => x.Id);
b.Property(x => x.Code).HasMaxLength(50).IsRequired();
b.Property(x => x.Name).HasMaxLength(200).IsRequired();
b.Property(x => x.Category).HasMaxLength(100);
b.Property(x => x.DefaultUnit).HasMaxLength(50);
b.Property(x => x.Description).HasMaxLength(1000);
b.HasIndex(x => x.Code).IsUnique().HasFilter("[IsDeleted] = 0");
b.HasIndex(x => x.Category);
b.HasQueryFilter(x => !x.IsDeleted);
}
}

View File

@ -7,6 +7,7 @@ using SolutionErp.Domain.Contracts;
using SolutionErp.Domain.Forms;
using SolutionErp.Domain.Identity;
using SolutionErp.Domain.Master;
using SolutionErp.Domain.Master.Catalogs;
namespace SolutionErp.Infrastructure.Persistence;
@ -35,6 +36,7 @@ public static class DbInitializer
await SeedDemoMasterDataAsync(db, logger);
await SeedContractTemplatesAsync(db, logger);
await SeedWorkflowDefinitionsAsync(db, logger);
await SeedCatalogsAsync(db, logger);
// Backfill mã HĐ cho HĐ legacy chưa có (sau khi đổi policy gen-tại-create).
// Idempotent: chỉ HĐ MaHopDong IS NULL được gen.
@ -44,6 +46,105 @@ public static class DbInitializer
await WarnDefaultAdminPasswordAsync(userManager, logger);
}
// 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)
{
// 1. UnitsOfMeasure (~17)
if (!await db.UnitsOfMeasure.AnyAsync())
{
var units = new[]
{
("m2", "Mét vuông"), ("m3", "Mét khối"), ("m", "Mét"), ("md", "Mét dài"),
("kg", "Kilogram"), ("tan", "Tấn"), ("g", "Gram"),
("l", "Lít"), ("ml", "Mililít"),
("cai", "Cái"), ("chiec", "Chiếc"), ("bo", "Bộ"), ("goi", "Gói"), ("hop", "Hộp"),
("ngc", "Ngày công"), ("h", "Giờ"), ("ca", "Ca"),
("nguoi", "Người"), ("luot", "Lượt"), ("lan", "Lần"),
};
foreach (var (code, name) in units)
db.UnitsOfMeasure.Add(new UnitOfMeasure { Code = code, Name = name });
await db.SaveChangesAsync();
logger.LogInformation("Seed {Count} UnitsOfMeasure", units.Length);
}
// 2. MaterialItems (~15 demo)
if (!await db.MaterialItems.AnyAsync())
{
var materials = new[]
{
("XM-PCB40", "Xi măng PCB40", "Xi măng", "kg"),
("XM-PCB30", "Xi măng PCB30", "Xi măng", "kg"),
("CAT-VANG", "Cát vàng đổ bê tông", "Cát đá", "m3"),
("CAT-DEN", "Cát đen xây trát", "Cát đá", "m3"),
("DA-1x2", "Đá 1x2", "Cát đá", "m3"),
("DA-4x6", "Đá 4x6", "Cát đá", "m3"),
("THEP-D6", "Thép cuộn phi 6", "Sắt thép", "kg"),
("THEP-D10", "Thép cây phi 10", "Sắt thép", "kg"),
("THEP-D14", "Thép cây phi 14", "Sắt thép", "kg"),
("THEP-D18", "Thép cây phi 18", "Sắt thép", "kg"),
("GACH-XANH", "Gạch xây 4 lỗ", "Gạch ngói", "cai"),
("GACH-DAT-NUNG", "Gạch đặc đất nung", "Gạch ngói", "cai"),
("SON-LOT", "Sơn lót chống kiềm", "Sơn", "l"),
("SON-NGOAI", "Sơn ngoại thất", "Sơn", "l"),
("ONG-PVC-90", "Ống PVC phi 90", "Vật tư cấp thoát nước", "m"),
};
foreach (var (code, name, cat, unit) in materials)
db.MaterialItems.Add(new MaterialItem { Code = code, Name = name, Category = cat, DefaultUnit = unit });
await db.SaveChangesAsync();
logger.LogInformation("Seed {Count} MaterialItems", materials.Length);
}
// 3. ServiceItems (~10 demo)
if (!await db.ServiceItems.AnyAsync())
{
var services = new[]
{
("VC-OTO", "Vận chuyển ô tô tải", "Vận chuyển", "ca"),
("VC-CAN-TRUC", "Cẩu tháp / cẩu tự hành", "Vận chuyển", "h"),
("BT-MAY", "Bảo trì máy móc thiết bị", "Bảo trì", "lan"),
("BT-DIEN", "Bảo trì hệ thống điện", "Bảo trì", "lan"),
("TV-THIET-KE", "Tư vấn thiết kế", "Tư vấn", "goi"),
("TV-GIAM-SAT", "Giám sát thi công", "Tư vấn", "ngc"),
("KIEM-DINH", "Kiểm định an toàn", "Kiểm định", "lan"),
("VS-CT", "Vệ sinh công trường", "Vệ sinh", "ca"),
("BV-CT", "Bảo vệ công trường", "Bảo vệ", "ngc"),
("THUE-MAY", "Thuê máy móc thiết bị", "Thuê thiết bị", "ca"),
};
foreach (var (code, name, cat, unit) in services)
db.ServiceItems.Add(new ServiceItem { Code = code, Name = name, Category = cat, DefaultUnit = unit });
await db.SaveChangesAsync();
logger.LogInformation("Seed {Count} ServiceItems", services.Length);
}
// 4. WorkItems (~15 demo)
if (!await db.WorkItems.AnyAsync())
{
var works = new[]
{
("DAO-MONG", "Đào móng công trình", "Phần thô", "m3"),
("DO-BTONG", "Đổ bê tông móng/cột/dầm", "Phần thô", "m3"),
("LAP-COT-THEP", "Lắp dựng cốt thép", "Phần thô", "kg"),
("LAP-COPPHA", "Lắp dựng cốp pha", "Phần thô", "m2"),
("XAY-TUONG", "Xây tường gạch", "Phần thô", "m3"),
("TRAT-TUONG", "Trát tường", "Hoàn thiện", "m2"),
("LAT-GACH-NEN", "Lát gạch nền", "Hoàn thiện", "m2"),
("OP-GACH-TUONG", "Ốp gạch tường", "Hoàn thiện", "m2"),
("SON-NUOC", "Sơn nước", "Hoàn thiện", "m2"),
("LAP-CUA", "Lắp cửa", "Hoàn thiện", "cai"),
("LAP-DIEN", "Lắp đặt hệ thống điện", "Cơ điện", "goi"),
("LAP-NUOC", "Lắp đặt hệ thống cấp thoát nước", "Cơ điện", "goi"),
("LAP-DIEU-HOA", "Lắp điều hòa", "Cơ điện", "cai"),
("THAM-CHONG", "Thấm chống thấm", "Hoàn thiện", "m2"),
("VC-PHE-THAI", "Vận chuyển phế thải", "Khác", "m3"),
};
foreach (var (code, name, cat, unit) in works)
db.WorkItems.Add(new WorkItem { Code = code, Name = name, Category = cat, DefaultUnit = unit });
await db.SaveChangesAsync();
logger.LogInformation("Seed {Count} WorkItems", works.Length);
}
}
private static async Task BackfillContractCodesAsync(
ApplicationDbContext db, IContractCodeGenerator codeGen, ILogger logger)
{
@ -236,6 +337,12 @@ public static class DbInitializer
(MenuKeys.Suppliers, "Nhà cung cấp", MenuKeys.Master, 21, "Building2"),
(MenuKeys.Projects, "Dự án", MenuKeys.Master, 22, "FolderKanban"),
(MenuKeys.Departments, "Phòng ban", MenuKeys.Master, 23, "Users"),
// 4 master catalog cho Details add form (autocomplete suggestion)
(MenuKeys.Catalogs, "Danh mục chi tiết", MenuKeys.Master, 24, "Library"),
(MenuKeys.CatalogUnits, "Đơn vị tính", MenuKeys.Catalogs, 1, "Ruler"),
(MenuKeys.CatalogMaterials,"Vật tư / SP", MenuKeys.Catalogs, 2, "Package"),
(MenuKeys.CatalogServices, "Dịch vụ", MenuKeys.Catalogs, 3, "Wrench"),
(MenuKeys.CatalogWorkItems,"Hạng mục công việc", MenuKeys.Catalogs, 4, "ListChecks"),
(MenuKeys.Contracts, "Hợp đồng", null, 30, "FileText"),
(MenuKeys.Forms, "Biểu mẫu", null, 40, "FileSpreadsheet"),
(MenuKeys.Reports, "Báo cáo", null, 50, "BarChart3"),

View File

@ -0,0 +1,168 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace SolutionErp.Infrastructure.Persistence.Migrations
{
/// <inheritdoc />
public partial class AddMasterCatalogs : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "MaterialItems",
columns: table => new
{
Id = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
Code = table.Column<string>(type: "nvarchar(50)", maxLength: 50, nullable: false),
Name = table.Column<string>(type: "nvarchar(200)", maxLength: 200, nullable: false),
Category = table.Column<string>(type: "nvarchar(100)", maxLength: 100, nullable: true),
DefaultUnit = table.Column<string>(type: "nvarchar(50)", maxLength: 50, nullable: true),
Specification = table.Column<string>(type: "nvarchar(1000)", maxLength: 1000, nullable: true),
OriginCountry = table.Column<string>(type: "nvarchar(100)", maxLength: 100, nullable: true),
IsActive = table.Column<bool>(type: "bit", nullable: false),
CreatedAt = table.Column<DateTime>(type: "datetime2", nullable: false),
UpdatedAt = table.Column<DateTime>(type: "datetime2", nullable: true),
CreatedBy = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
UpdatedBy = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
IsDeleted = table.Column<bool>(type: "bit", nullable: false),
DeletedAt = table.Column<DateTime>(type: "datetime2", nullable: true),
DeletedBy = table.Column<Guid>(type: "uniqueidentifier", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_MaterialItems", x => x.Id);
});
migrationBuilder.CreateTable(
name: "ServiceItems",
columns: table => new
{
Id = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
Code = table.Column<string>(type: "nvarchar(50)", maxLength: 50, nullable: false),
Name = table.Column<string>(type: "nvarchar(200)", maxLength: 200, nullable: false),
Category = table.Column<string>(type: "nvarchar(100)", maxLength: 100, nullable: true),
DefaultUnit = table.Column<string>(type: "nvarchar(50)", maxLength: 50, nullable: true),
Description = table.Column<string>(type: "nvarchar(1000)", maxLength: 1000, nullable: true),
IsActive = table.Column<bool>(type: "bit", nullable: false),
CreatedAt = table.Column<DateTime>(type: "datetime2", nullable: false),
UpdatedAt = table.Column<DateTime>(type: "datetime2", nullable: true),
CreatedBy = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
UpdatedBy = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
IsDeleted = table.Column<bool>(type: "bit", nullable: false),
DeletedAt = table.Column<DateTime>(type: "datetime2", nullable: true),
DeletedBy = table.Column<Guid>(type: "uniqueidentifier", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_ServiceItems", x => x.Id);
});
migrationBuilder.CreateTable(
name: "UnitsOfMeasure",
columns: table => new
{
Id = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
Code = table.Column<string>(type: "nvarchar(20)", maxLength: 20, nullable: false),
Name = table.Column<string>(type: "nvarchar(100)", maxLength: 100, nullable: false),
Description = 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),
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_UnitsOfMeasure", x => x.Id);
});
migrationBuilder.CreateTable(
name: "WorkItems",
columns: table => new
{
Id = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
Code = table.Column<string>(type: "nvarchar(50)", maxLength: 50, nullable: false),
Name = table.Column<string>(type: "nvarchar(200)", maxLength: 200, nullable: false),
Category = table.Column<string>(type: "nvarchar(100)", maxLength: 100, nullable: true),
DefaultUnit = table.Column<string>(type: "nvarchar(50)", maxLength: 50, nullable: true),
Description = table.Column<string>(type: "nvarchar(1000)", maxLength: 1000, nullable: true),
IsActive = table.Column<bool>(type: "bit", nullable: false),
CreatedAt = table.Column<DateTime>(type: "datetime2", nullable: false),
UpdatedAt = table.Column<DateTime>(type: "datetime2", nullable: true),
CreatedBy = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
UpdatedBy = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
IsDeleted = table.Column<bool>(type: "bit", nullable: false),
DeletedAt = table.Column<DateTime>(type: "datetime2", nullable: true),
DeletedBy = table.Column<Guid>(type: "uniqueidentifier", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_WorkItems", x => x.Id);
});
migrationBuilder.CreateIndex(
name: "IX_MaterialItems_Category",
table: "MaterialItems",
column: "Category");
migrationBuilder.CreateIndex(
name: "IX_MaterialItems_Code",
table: "MaterialItems",
column: "Code",
unique: true,
filter: "[IsDeleted] = 0");
migrationBuilder.CreateIndex(
name: "IX_ServiceItems_Category",
table: "ServiceItems",
column: "Category");
migrationBuilder.CreateIndex(
name: "IX_ServiceItems_Code",
table: "ServiceItems",
column: "Code",
unique: true,
filter: "[IsDeleted] = 0");
migrationBuilder.CreateIndex(
name: "IX_UnitsOfMeasure_Code",
table: "UnitsOfMeasure",
column: "Code",
unique: true,
filter: "[IsDeleted] = 0");
migrationBuilder.CreateIndex(
name: "IX_WorkItems_Category",
table: "WorkItems",
column: "Category");
migrationBuilder.CreateIndex(
name: "IX_WorkItems_Code",
table: "WorkItems",
column: "Code",
unique: true,
filter: "[IsDeleted] = 0");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "MaterialItems");
migrationBuilder.DropTable(
name: "ServiceItems");
migrationBuilder.DropTable(
name: "UnitsOfMeasure");
migrationBuilder.DropTable(
name: "WorkItems");
}
}
}

View File

@ -1408,6 +1408,249 @@ namespace SolutionErp.Infrastructure.Persistence.Migrations
b.ToTable("Users", (string)null);
});
modelBuilder.Entity("SolutionErp.Domain.Master.Catalogs.MaterialItem", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uniqueidentifier");
b.Property<string>("Category")
.HasMaxLength(100)
.HasColumnType("nvarchar(100)");
b.Property<string>("Code")
.IsRequired()
.HasMaxLength(50)
.HasColumnType("nvarchar(50)");
b.Property<DateTime>("CreatedAt")
.HasColumnType("datetime2");
b.Property<Guid?>("CreatedBy")
.HasColumnType("uniqueidentifier");
b.Property<string>("DefaultUnit")
.HasMaxLength(50)
.HasColumnType("nvarchar(50)");
b.Property<DateTime?>("DeletedAt")
.HasColumnType("datetime2");
b.Property<Guid?>("DeletedBy")
.HasColumnType("uniqueidentifier");
b.Property<bool>("IsActive")
.HasColumnType("bit");
b.Property<bool>("IsDeleted")
.HasColumnType("bit");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(200)
.HasColumnType("nvarchar(200)");
b.Property<string>("OriginCountry")
.HasMaxLength(100)
.HasColumnType("nvarchar(100)");
b.Property<string>("Specification")
.HasMaxLength(1000)
.HasColumnType("nvarchar(1000)");
b.Property<DateTime?>("UpdatedAt")
.HasColumnType("datetime2");
b.Property<Guid?>("UpdatedBy")
.HasColumnType("uniqueidentifier");
b.HasKey("Id");
b.HasIndex("Category");
b.HasIndex("Code")
.IsUnique()
.HasFilter("[IsDeleted] = 0");
b.ToTable("MaterialItems", (string)null);
});
modelBuilder.Entity("SolutionErp.Domain.Master.Catalogs.ServiceItem", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uniqueidentifier");
b.Property<string>("Category")
.HasMaxLength(100)
.HasColumnType("nvarchar(100)");
b.Property<string>("Code")
.IsRequired()
.HasMaxLength(50)
.HasColumnType("nvarchar(50)");
b.Property<DateTime>("CreatedAt")
.HasColumnType("datetime2");
b.Property<Guid?>("CreatedBy")
.HasColumnType("uniqueidentifier");
b.Property<string>("DefaultUnit")
.HasMaxLength(50)
.HasColumnType("nvarchar(50)");
b.Property<DateTime?>("DeletedAt")
.HasColumnType("datetime2");
b.Property<Guid?>("DeletedBy")
.HasColumnType("uniqueidentifier");
b.Property<string>("Description")
.HasMaxLength(1000)
.HasColumnType("nvarchar(1000)");
b.Property<bool>("IsActive")
.HasColumnType("bit");
b.Property<bool>("IsDeleted")
.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.HasKey("Id");
b.HasIndex("Category");
b.HasIndex("Code")
.IsUnique()
.HasFilter("[IsDeleted] = 0");
b.ToTable("ServiceItems", (string)null);
});
modelBuilder.Entity("SolutionErp.Domain.Master.Catalogs.UnitOfMeasure", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uniqueidentifier");
b.Property<string>("Code")
.IsRequired()
.HasMaxLength(20)
.HasColumnType("nvarchar(20)");
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")
.HasMaxLength(500)
.HasColumnType("nvarchar(500)");
b.Property<bool>("IsDeleted")
.HasColumnType("bit");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(100)
.HasColumnType("nvarchar(100)");
b.Property<DateTime?>("UpdatedAt")
.HasColumnType("datetime2");
b.Property<Guid?>("UpdatedBy")
.HasColumnType("uniqueidentifier");
b.HasKey("Id");
b.HasIndex("Code")
.IsUnique()
.HasFilter("[IsDeleted] = 0");
b.ToTable("UnitsOfMeasure", (string)null);
});
modelBuilder.Entity("SolutionErp.Domain.Master.Catalogs.WorkItem", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uniqueidentifier");
b.Property<string>("Category")
.HasMaxLength(100)
.HasColumnType("nvarchar(100)");
b.Property<string>("Code")
.IsRequired()
.HasMaxLength(50)
.HasColumnType("nvarchar(50)");
b.Property<DateTime>("CreatedAt")
.HasColumnType("datetime2");
b.Property<Guid?>("CreatedBy")
.HasColumnType("uniqueidentifier");
b.Property<string>("DefaultUnit")
.HasMaxLength(50)
.HasColumnType("nvarchar(50)");
b.Property<DateTime?>("DeletedAt")
.HasColumnType("datetime2");
b.Property<Guid?>("DeletedBy")
.HasColumnType("uniqueidentifier");
b.Property<string>("Description")
.HasMaxLength(1000)
.HasColumnType("nvarchar(1000)");
b.Property<bool>("IsActive")
.HasColumnType("bit");
b.Property<bool>("IsDeleted")
.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.HasKey("Id");
b.HasIndex("Category");
b.HasIndex("Code")
.IsUnique()
.HasFilter("[IsDeleted] = 0");
b.ToTable("WorkItems", (string)null);
});
modelBuilder.Entity("SolutionErp.Domain.Master.Department", b =>
{
b.Property<Guid>("Id")