[CLAUDE] Master: nạp master data thật từ Excel (62 dự án + 71 hạng mục + 3 NCC) + Project +4 cột (Mig 48)
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 4m33s
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 4m33s
Nạp master data công ty từ file Excel 'HẠNG MỤC CÔNG VIỆC DỰ ÁN': - 62 Projects (Mã + Năm; tên/CĐT/địa điểm/gói thầu cho ~6 dự án có chi tiết) - 71 WorkItems: Vật tư 16 · Thầu phụ 30 · MEP 9 · Thiết bị 16 - 3 Suppliers (TRUONGGIANG/TANPHU/TGN) Mig 48 AddProjectMasterFields: Project +4 cột nullable (Year/Investor/Location/Package) + ProjectFeatures DTO/Create/Update + ProjectsPage form ×2 app (SHA256 mirror). SeedRealMasterDataAsync per-code idempotent, UNGATED → reaches prod (coexist demo). FLOCK01 collision → skip (demo wins). Verify: build 0-err · test 216 PASS · runtime Dev proof (data landed, Investor col populates). Provenance: scripts/master-import-data.generated.md. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@ -18,7 +18,11 @@ public record ProjectDto(
|
||||
decimal? BudgetTotal,
|
||||
string? Note,
|
||||
DateTime CreatedAt,
|
||||
DateTime? UpdatedAt);
|
||||
DateTime? UpdatedAt,
|
||||
int? Year,
|
||||
string? Investor,
|
||||
string? Location,
|
||||
string? Package);
|
||||
|
||||
// ===================== LIST =====================
|
||||
public record ListProjectsQuery : PagedRequest, IRequest<PagedResult<ProjectDto>>;
|
||||
@ -44,7 +48,7 @@ public class ListProjectsQueryHandler(IApplicationDbContext db) : IRequestHandle
|
||||
var total = await query.CountAsync(ct);
|
||||
var items = await query
|
||||
.Skip((request.Page - 1) * request.PageSize).Take(request.PageSize)
|
||||
.Select(x => new ProjectDto(x.Id, x.Code, x.Name, x.StartDate, x.EndDate, x.ManagerUserId, x.BudgetTotal, x.Note, x.CreatedAt, x.UpdatedAt))
|
||||
.Select(x => new ProjectDto(x.Id, x.Code, x.Name, x.StartDate, x.EndDate, x.ManagerUserId, x.BudgetTotal, x.Note, x.CreatedAt, x.UpdatedAt, x.Year, x.Investor, x.Location, x.Package))
|
||||
.ToListAsync(ct);
|
||||
return new PagedResult<ProjectDto>(items, total, request.Page, request.PageSize);
|
||||
}
|
||||
@ -59,14 +63,15 @@ public class GetProjectQueryHandler(IApplicationDbContext db) : IRequestHandler<
|
||||
{
|
||||
var x = await db.Projects.AsNoTracking().FirstOrDefaultAsync(p => p.Id == request.Id, ct)
|
||||
?? throw new NotFoundException("Project", request.Id);
|
||||
return new ProjectDto(x.Id, x.Code, x.Name, x.StartDate, x.EndDate, x.ManagerUserId, x.BudgetTotal, x.Note, x.CreatedAt, x.UpdatedAt);
|
||||
return new ProjectDto(x.Id, x.Code, x.Name, x.StartDate, x.EndDate, x.ManagerUserId, x.BudgetTotal, x.Note, x.CreatedAt, x.UpdatedAt, x.Year, x.Investor, x.Location, x.Package);
|
||||
}
|
||||
}
|
||||
|
||||
// ===================== CREATE =====================
|
||||
public record CreateProjectCommand(
|
||||
string Code, string Name, DateTime? StartDate, DateTime? EndDate,
|
||||
Guid? ManagerUserId, decimal? BudgetTotal, string? Note) : IRequest<Guid>;
|
||||
Guid? ManagerUserId, decimal? BudgetTotal, string? Note,
|
||||
int? Year, string? Investor, string? Location, string? Package) : IRequest<Guid>;
|
||||
|
||||
public class CreateProjectCommandValidator : AbstractValidator<CreateProjectCommand>
|
||||
{
|
||||
@ -75,6 +80,9 @@ public class CreateProjectCommandValidator : AbstractValidator<CreateProjectComm
|
||||
RuleFor(x => x.Code).NotEmpty().MaximumLength(50);
|
||||
RuleFor(x => x.Name).NotEmpty().MaximumLength(200);
|
||||
RuleFor(x => x.BudgetTotal).GreaterThanOrEqualTo(0).When(x => x.BudgetTotal.HasValue);
|
||||
RuleFor(x => x.Investor).MaximumLength(250);
|
||||
RuleFor(x => x.Location).MaximumLength(500);
|
||||
RuleFor(x => x.Package).MaximumLength(300);
|
||||
RuleFor(x => x).Must(x => !x.StartDate.HasValue || !x.EndDate.HasValue || x.EndDate >= x.StartDate)
|
||||
.WithMessage("Ngày kết thúc phải sau ngày bắt đầu");
|
||||
}
|
||||
@ -91,6 +99,7 @@ public class CreateProjectCommandHandler(IApplicationDbContext db) : IRequestHan
|
||||
Code = request.Code, Name = request.Name,
|
||||
StartDate = request.StartDate, EndDate = request.EndDate,
|
||||
ManagerUserId = request.ManagerUserId, BudgetTotal = request.BudgetTotal, Note = request.Note,
|
||||
Year = request.Year, Investor = request.Investor, Location = request.Location, Package = request.Package,
|
||||
};
|
||||
db.Projects.Add(entity);
|
||||
await db.SaveChangesAsync(ct);
|
||||
@ -101,7 +110,8 @@ public class CreateProjectCommandHandler(IApplicationDbContext db) : IRequestHan
|
||||
// ===================== UPDATE =====================
|
||||
public record UpdateProjectCommand(
|
||||
Guid Id, string Code, string Name, DateTime? StartDate, DateTime? EndDate,
|
||||
Guid? ManagerUserId, decimal? BudgetTotal, string? Note) : IRequest;
|
||||
Guid? ManagerUserId, decimal? BudgetTotal, string? Note,
|
||||
int? Year, string? Investor, string? Location, string? Package) : IRequest;
|
||||
|
||||
public class UpdateProjectCommandValidator : AbstractValidator<UpdateProjectCommand>
|
||||
{
|
||||
@ -111,6 +121,9 @@ public class UpdateProjectCommandValidator : AbstractValidator<UpdateProjectComm
|
||||
RuleFor(x => x.Code).NotEmpty().MaximumLength(50);
|
||||
RuleFor(x => x.Name).NotEmpty().MaximumLength(200);
|
||||
RuleFor(x => x.BudgetTotal).GreaterThanOrEqualTo(0).When(x => x.BudgetTotal.HasValue);
|
||||
RuleFor(x => x.Investor).MaximumLength(250);
|
||||
RuleFor(x => x.Location).MaximumLength(500);
|
||||
RuleFor(x => x.Package).MaximumLength(300);
|
||||
RuleFor(x => x).Must(x => !x.StartDate.HasValue || !x.EndDate.HasValue || x.EndDate >= x.StartDate)
|
||||
.WithMessage("Ngày kết thúc phải sau ngày bắt đầu");
|
||||
}
|
||||
@ -131,6 +144,10 @@ public class UpdateProjectCommandHandler(IApplicationDbContext db) : IRequestHan
|
||||
entity.ManagerUserId = request.ManagerUserId;
|
||||
entity.BudgetTotal = request.BudgetTotal;
|
||||
entity.Note = request.Note;
|
||||
entity.Year = request.Year;
|
||||
entity.Investor = request.Investor;
|
||||
entity.Location = request.Location;
|
||||
entity.Package = request.Package;
|
||||
await db.SaveChangesAsync(ct);
|
||||
}
|
||||
}
|
||||
|
||||
@ -11,4 +11,8 @@ public class Project : AuditableEntity
|
||||
public Guid? ManagerUserId { get; set; } // PM — Giám đốc Dự án
|
||||
public decimal? BudgetTotal { get; set; } // Tổng ngân sách dự án (tham chiếu cho CCM check)
|
||||
public string? Note { get; set; }
|
||||
public int? Year { get; set; } // Năm
|
||||
public string? Investor { get; set; } // Chủ đầu tư
|
||||
public string? Location { get; set; } // Địa điểm
|
||||
public string? Package { get; set; } // Gói thầu
|
||||
}
|
||||
|
||||
@ -15,6 +15,9 @@ public class ProjectConfiguration : IEntityTypeConfiguration<Project>
|
||||
b.Property(x => x.Name).HasMaxLength(200).IsRequired();
|
||||
b.Property(x => x.BudgetTotal).HasPrecision(18, 2);
|
||||
b.Property(x => x.Note).HasMaxLength(1000);
|
||||
b.Property(x => x.Investor).HasMaxLength(250); // Chủ đầu tư
|
||||
b.Property(x => x.Location).HasMaxLength(500); // Địa điểm
|
||||
b.Property(x => x.Package).HasMaxLength(300); // Gói thầu
|
||||
|
||||
b.HasIndex(x => x.Code).IsUnique().HasFilter("[IsDeleted] = 0"); // Mig 47 (gotcha #57 EXT) — soft-deleted slot reusable, khớp HasQueryFilter !IsDeleted app-check
|
||||
|
||||
|
||||
@ -113,6 +113,9 @@ public static class DbInitializer
|
||||
await SeedPurchaseEvaluationWorkflowsAsync(db, logger);
|
||||
}
|
||||
await SeedCatalogsAsync(db, logger);
|
||||
// S55 (2026-06-09) — Master data thật của công ty (62 dự án + 71 hạng mục +
|
||||
// 3 NCC). NOT gated DemoSeed — coexist với demo data qua per-code idempotent.
|
||||
await SeedRealMasterDataAsync(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.
|
||||
@ -2219,6 +2222,213 @@ public static class DbInitializer
|
||||
await db.SaveChangesAsync();
|
||||
}
|
||||
|
||||
// S55 (2026-06-09) — Import master data thật của công ty (62 dự án + 71 hạng
|
||||
// mục công việc + 3 NCC) từ file Excel `scripts/master-import-data.generated.md`.
|
||||
// Data hardcode inline vì runtime KHÔNG đọc được scratch file đó.
|
||||
//
|
||||
// NOT gated DemoSeed:Disabled — đây là INFRASTRUCTURE master data thật (như
|
||||
// Roles/Departments/Catalogs), coexist với demo data qua per-code idempotent
|
||||
// (gotcha #51). Mirror pattern SeedDemoMasterDataAsync: load existing codes →
|
||||
// foreach skip-if-contains else Add → 1 SaveChanges.
|
||||
//
|
||||
// KNOWN collision: project "FLOCK01" cũng là demo project (SeedDemoMasterDataAsync)
|
||||
// → per-code idempotent SKIP nó (demo thắng). Chấp nhận được — FLOCK01 thật chỉ
|
||||
// có Code + Year. Không special-case.
|
||||
private static async Task SeedRealMasterDataAsync(ApplicationDbContext db, ILogger logger)
|
||||
{
|
||||
// ---------- PROJECTS (62) — (Code, Year, Name [=Code nếu blank], Package, Investor, Location) ----------
|
||||
var projects = new (string Code, int? Year, string? Name, string? Package, string? Investor, string? Location)[]
|
||||
{
|
||||
("APVN01", 2023, null, null, null, null),
|
||||
("AUS01", 2025, null, null, null, null),
|
||||
("BVN01", 2025, null, null, null, null),
|
||||
("CAL01", 2025, "Phân xưởng homecare 75 tấn/ngày và các hạng mục phụ trợ", null, "Công ty TNHH Calofic", "Lô C18-C24, Khu Công Nghiệp Hiệp Phước, Xã Hiệp Phước, Thành Phố Hồ Chí Minh"),
|
||||
("CET01", 2023, null, null, null, null),
|
||||
("CET02", 2023, null, null, null, null),
|
||||
("EQU01", null, null, null, null, null),
|
||||
("EVO01", null, null, null, null, null),
|
||||
("FLEMING01", null, null, null, null, null),
|
||||
("FLEMING01.24", null, null, null, null, null),
|
||||
("FLEMING01.25", null, null, null, null, null),
|
||||
("FLEMING03", 2023, null, null, null, null),
|
||||
("FLOCK01", 2024, null, null, null, null),
|
||||
("FLOCK01.25", 2024, null, null, null, null),
|
||||
("FLOCK01.26", 2024, null, null, null, null),
|
||||
("FLOCK02", 2024, null, null, null, null),
|
||||
("FLOCK02.25", 2024, null, null, null, null),
|
||||
("FLOCK03", 2026, null, null, null, null),
|
||||
("FLOCK04", 2026, null, null, null, null),
|
||||
("GCP01", null, null, null, null, null),
|
||||
("GO01", 2023, null, null, null, null),
|
||||
("GST01", 2024, null, null, null, null),
|
||||
("ILD01", null, null, null, null, null),
|
||||
("IML06", null, null, null, null, null),
|
||||
("MASAN01", null, null, null, null, null),
|
||||
("MIDEA01", null, "Nhà máy Midea", "Công trình xây dựng tầng lửng Nhà Máy 2 SDA Việt Nam Năm 2025", "Công Ty TNHH Midea Consumer Electric (Vietnam)", "Xưởng A2-1, A2-3, Lô A3, Đường NA2, Khu Công Nghiệp Thới Hòa, Phường Thới Hòa, Thành Phố Hồ Chí Minh"),
|
||||
("NTBA01", 2023, null, null, null, null),
|
||||
("NTBA01.25", 2025, null, null, null, null),
|
||||
("OLAM01", null, null, null, null, null),
|
||||
("OLAM02", null, null, null, null, null),
|
||||
("PMH01", null, null, null, null, null),
|
||||
("REDBULL01", 2021, null, null, null, null),
|
||||
("REDBULL03", 2021, null, null, null, null),
|
||||
("RMIT01", null, null, null, null, null),
|
||||
("SAM01", 2024, "Xây dựng nhà kho SAMHWA-VH", "Thi công chính", "Công ty TNHH SAMHWA-VH CO., LTD", "Lô số b-13, đường d2, Phường Long Đức, Xã An Phước, Huyện Long Thành, Tỉnh Đồng Nai"),
|
||||
("SEW01", 2023, null, null, null, null),
|
||||
("SHEICO01", null, null, null, null, null),
|
||||
("SHEICO01B", null, null, null, null, null),
|
||||
("SHEICO01C", null, null, null, null, null),
|
||||
("SHEICO03", null, null, null, null, null),
|
||||
("SHEICO03.ELEVATOR", null, null, null, null, null),
|
||||
("SHEICO03.MEP", null, null, null, null, null),
|
||||
("SHEICO04", null, null, null, null, null),
|
||||
("SHEICO04.14", null, null, null, null, null),
|
||||
("SHEICO04.15ME", null, null, null, null, null),
|
||||
("SHEICO04.16ME", null, null, null, null, null),
|
||||
("SHEICO04.17ME", null, null, null, null, null),
|
||||
("SHEICO04.9", null, null, null, null, null),
|
||||
("SOL01", null, null, null, null, null),
|
||||
("SOVI01", null, null, null, null, null),
|
||||
("TBA01", null, null, null, null, null),
|
||||
("TLB01", 2023, "Merlion Trelleborg", "Thiết Kế & Thi Công", "Công Ty TNHH Trelleborg Việt Nam", "Lô Số F2, Đường Số 2, Phường Phước Hòa, Thị Xã Phú Mỹ, Tỉnh Bà Rịa - Vũng Tàu"),
|
||||
("TTC01", null, null, null, null, null),
|
||||
("VLU04", null, null, null, null, null),
|
||||
("VLU04.1", null, null, null, null, null),
|
||||
("VP2024", null, null, null, null, null),
|
||||
("VPS01", null, null, null, null, null),
|
||||
("VRR01", null, null, null, null, null),
|
||||
("WFW01", null, null, null, null, null),
|
||||
("WPL01", null, null, null, null, null),
|
||||
("WPL02", null, null, null, null, null),
|
||||
("ZOTE01", 2025, "Zotefoams new manufacturing plant", "Building and building service", "Zotefoams PLC", "Changshin VJ2, Khu Công Nghiệp Lộc An - Bình Sơn, Xã Long Thành, Tỉnh Đồng Nai"),
|
||||
};
|
||||
|
||||
var existingProjectCodes = await db.Projects.Select(p => p.Code).ToListAsync();
|
||||
int addedProjects = 0;
|
||||
foreach (var (code, year, name, package, investor, location) in projects)
|
||||
{
|
||||
if (existingProjectCodes.Contains(code)) continue; // FLOCK01 collision → demo thắng, skip
|
||||
db.Projects.Add(new Project
|
||||
{
|
||||
Code = code,
|
||||
Name = string.IsNullOrWhiteSpace(name) ? code : name,
|
||||
Year = year,
|
||||
Package = package,
|
||||
Investor = investor,
|
||||
Location = location,
|
||||
});
|
||||
addedProjects++;
|
||||
}
|
||||
if (addedProjects > 0) logger.LogInformation("Seeded {Count} real projects (S55 import)", addedProjects);
|
||||
|
||||
// ---------- WORKITEMS (71) — (Code, Category, Name) ----------
|
||||
var workItems = new (string Code, string Category, string Name)[]
|
||||
{
|
||||
("VT-01", "Vật tư - Xây dựng", "Bê tông"),
|
||||
("VT-02", "Vật tư - Xây dựng", "Thép cây, Thép lưới hàn"),
|
||||
("VT-03", "Vật tư - Xây dựng", "Cọc ly tâm"),
|
||||
("VT-04", "Vật tư - Xây dựng", "Cống ly tâm"),
|
||||
("VT-05", "Vật tư - Xây dựng", "Cừ tràm"),
|
||||
("VT-06", "Vật tư - Xây dựng", "Kim loại ( rọ đầu cọc, V mạ kẽm, Vinox, bản mã…)"),
|
||||
("VT-07", "Vật tư - Xây dựng", "Nắp gang"),
|
||||
("VT-08", "Vật tư - Xây dựng", "Gạch xây, Xi măng, cát, đá, gạch trồng cỏ"),
|
||||
("VT-09", "Vật tư - Xây dựng", "Gạch trồng cỏ, gạch trang trí…"),
|
||||
("VT-10", "Vật tư - Xây dựng", "Gạch ốp lát"),
|
||||
("VT-11", "Vật tư - Xây dựng", "Ván khuôn"),
|
||||
("VT-12", "Vật tư - Xây dựng", "Sơn nước"),
|
||||
("VT-13", "Vật tư - Xây dựng", "Phụ gia (sika, vinkem, Gps…)"),
|
||||
("VT-14", "Vật tư - Xây dựng", "PVC, vải địa"),
|
||||
("VT-15", "Vật tư - Xây dựng", "Nẹp inox, nẹp nhựa"),
|
||||
("VT-16", "Vật tư - Xây dựng", "Khác"),
|
||||
("TP-01", "Thầu phụ - Xây dựng", "Thầu phụ cọc ( cơ, robot)"),
|
||||
("TP-02", "Thầu phụ - Xây dựng", "Thầu phụ cọc Khoan nhồi"),
|
||||
("TP-03", "Thầu phụ - Xây dựng", "Thầu phụ cọc CDM"),
|
||||
("TP-04", "Thầu phụ - Xây dựng", "Kết cấu thép"),
|
||||
("TP-05", "Thầu phụ - Xây dựng", "Cáp dự ứng lực"),
|
||||
("TP-06", "Thầu phụ - Xây dựng", "Panel"),
|
||||
("TP-07", "Thầu phụ - Xây dựng", "Nhôm kính, alu"),
|
||||
("TP-08", "Thầu phụ - Xây dựng", "Cửa thép, Cửa cuốn, sectional door"),
|
||||
("TP-09", "Thầu phụ - Xây dựng", "Cửa gỗ"),
|
||||
("TP-10", "Thầu phụ - Xây dựng", "Cơ khí hoàn thiện lan can, cầu thang"),
|
||||
("TP-11", "Thầu phụ - Xây dựng", "Dây cứu sinh"),
|
||||
("TP-12", "Thầu phụ - Xây dựng", "Nội thất"),
|
||||
("TP-13", "Thầu phụ - Xây dựng", "Vách trần thạch cao"),
|
||||
("TP-14", "Thầu phụ - Xây dựng", "Vách ngăn di động"),
|
||||
("TP-15", "Thầu phụ - Xây dựng", "Vách ngăn vệ sinh"),
|
||||
("TP-16", "Thầu phụ - Xây dựng", "Granit"),
|
||||
("TP-17", "Thầu phụ - Xây dựng", "Hoàn thiện sàn thảm, vinyl"),
|
||||
("TP-18", "Thầu phụ - Xây dựng", "Chống mối"),
|
||||
("TP-19", "Thầu phụ - Xây dựng", "Chống thấm"),
|
||||
("TP-20", "Thầu phụ - Xây dựng", "Sơn PU, Epoxy, liquid"),
|
||||
("TP-21", "Thầu phụ - Xây dựng", "Dock Leveler."),
|
||||
("TP-22", "Thầu phụ - Xây dựng", "Cẩu trục"),
|
||||
("TP-23", "Thầu phụ - Xây dựng", "Trạm cân"),
|
||||
("TP-24", "Thầu phụ - Xây dựng", "Thang máy, thang hàng, …"),
|
||||
("TP-25", "Thầu phụ - Xây dựng", "Sàn nâng."),
|
||||
("TP-26", "Thầu phụ - Xây dựng", "Landscape"),
|
||||
("TP-27", "Thầu phụ - Xây dựng", "Cổng xếp."),
|
||||
("TP-28", "Thầu phụ - Xây dựng", "Logo, Bảng hiệu."),
|
||||
("TP-29", "Thầu phụ - Xây dựng", "TP thi công Màng nhựa"),
|
||||
("TP-30", "Thầu phụ - Xây dựng", "Khác"),
|
||||
("MEP-01", "MEP", "MEP (Full)"),
|
||||
("MEP-02", "MEP", "Hệ thống trung thế"),
|
||||
("MEP-03", "MEP", "PCCC (Phòng cháy chữa cháy)."),
|
||||
("MEP-04", "MEP", "Hệ thống điện, điện nhẹ"),
|
||||
("MEP-05", "MEP", "Hệ thống HVAC, Utility (Thông gió & Điều hòa không khí & Phù trợ)."),
|
||||
("MEP-06", "MEP", "Hệ thống cấp thoát nước"),
|
||||
("MEP-07", "MEP", "Hệ thống xử lý nước thải"),
|
||||
("MEP-08", "MEP", "Hệ thống solar"),
|
||||
("MEP-09", "MEP", "Khác"),
|
||||
("TB-01", "Thiết bị", "Máy biến áp"),
|
||||
("TB-02", "Thiết bị", "Tủ điện"),
|
||||
("TB-03", "Thiết bị", "Tủ trung thế."),
|
||||
("TB-04", "Thiết bị", "Busway"),
|
||||
("TB-05", "Thiết bị", "Cáp điện"),
|
||||
("TB-06", "Thiết bị", "Thiết bị Utility (Máy nén khí, Hút bụi hút mùi, Boiler,...)"),
|
||||
("TB-07", "Thiết bị", "Máy lạnh"),
|
||||
("TB-08", "Thiết bị", "Ống thép, inox"),
|
||||
("TB-09", "Thiết bị", "Thang máng cáp"),
|
||||
("TB-10", "Thiết bị", "Ống PVC, HDPE"),
|
||||
("TB-11", "Thiết bị", "Thiết bị báo cháy"),
|
||||
("TB-12", "Thiết bị", "Đèn (đèn chiếu sáng, đèn năng lượng, đèn exit….)"),
|
||||
("TB-13", "Thiết bị", "Thiết bị chữa cháy (Sprinkler, Chữa cháy khí, FM200,…)"),
|
||||
("TB-14", "Thiết bị", "Bơm (bơm PCCC, Bơm nước…)"),
|
||||
("TB-15", "Thiết bị", "Bồn nước, Thiết bị vệ sinh"),
|
||||
("TB-16", "Thiết bị", "Khác"),
|
||||
};
|
||||
|
||||
var existingWorkItemCodes = await db.WorkItems.Select(w => w.Code).ToListAsync();
|
||||
int addedWorkItems = 0;
|
||||
foreach (var (code, category, name) in workItems)
|
||||
{
|
||||
if (existingWorkItemCodes.Contains(code)) continue;
|
||||
db.WorkItems.Add(new WorkItem { Code = code, Name = name, Category = category, IsActive = true });
|
||||
addedWorkItems++;
|
||||
}
|
||||
if (addedWorkItems > 0) logger.LogInformation("Seeded {Count} real work items (S55 import)", addedWorkItems);
|
||||
|
||||
// ---------- SUPPLIERS (3) — (Code, Name, Type, TaxCode, Phone, Email, Address, ContactPerson, Note) ----------
|
||||
var suppliers = new[]
|
||||
{
|
||||
new Supplier { Code = "TRUONGGIANG", Name = "CÔNG TY CỔ PHẦN NHÔM KÍNH VÀ ĐẦU TƯ TRƯỜNG GIANG", Type = SupplierType.NhaThauPhu, TaxCode = "0312 251 859", Phone = "08 2253 1381", Email = "viethq1985@gmail.com", Address = "Số 77/46A Đường Chuyên Dùng Chính, Phường Phú Thuận, TP.HCM", ContactPerson = "Mr. Việt", Note = "Loại: Nhôm kính, alu; NĐD: Ông: HOÀNG QUỐC VIỆT - GIÁM ĐỐC ĐIỀU HÀNH; TK: 100212127 Ngân hàng Eximbank - Chi Nhánh Hòa Bình; Trạng thái: ✅ Đang hoạt động" },
|
||||
new Supplier { Code = "TANPHU", Name = "CÔNG TY TNHH SẢN XUẤT THƯƠNG MẠI SƠN TÂN PHÚ", Type = SupplierType.NhaThauPhu, TaxCode = "0305497393", Phone = "0902 686468", Email = "thu@tanphupaint.com", Address = "Số 49/5A Đường số 7, Khu phố 53, P. Linh Xuân, Thành phố Hồ Chí Minh, Việt Nam", ContactPerson = "Ms Thư", Note = "Loại: Sơn PU, Epoxy, liquid; NĐD: Ông ĐINH PHƯỚC THỌ - GIÁM ĐỐC; TK: 8683107979 tại Ngân hàng BIDV - CN Nam Bình Dương; Trạng thái: ✅ Đang hoạt động" },
|
||||
new Supplier { Code = "TGN", Name = "CÔNG TY CỔ PHẦN SIÊU THỊ VLXD THẾ GIỚI NHÀ", Type = SupplierType.NhaCungCap, TaxCode = "3603497972", Phone = "028 36206000", Email = "duong.ltt@tgngroup.vn", Address = "46-48 Nguyễn Cơ Thạch, Phường An Khánh, Thành phố Hồ Chí Minh,Việt Nam", ContactPerson = "Ms. Dương", Note = "Loại: Bê tông; NĐD: Ông TRẦN ANH ĐIỀN - TỔNG GIÁM ĐỐC; TK: 114002641307 tại VietinBank CN KCN Biên Hòa; Trạng thái: ✅ Đang hoạt động" },
|
||||
};
|
||||
|
||||
var existingSupplierCodes = await db.Suppliers.Select(s => s.Code).ToListAsync();
|
||||
int addedSuppliers = 0;
|
||||
foreach (var s in suppliers)
|
||||
{
|
||||
if (existingSupplierCodes.Contains(s.Code)) continue;
|
||||
db.Suppliers.Add(s);
|
||||
addedSuppliers++;
|
||||
}
|
||||
if (addedSuppliers > 0) logger.LogInformation("Seeded {Count} real suppliers (S55 import)", addedSuppliers);
|
||||
|
||||
if (addedProjects + addedWorkItems + addedSuppliers > 0)
|
||||
await db.SaveChangesAsync();
|
||||
}
|
||||
|
||||
private static async Task SeedContractTemplatesAsync(ApplicationDbContext db, ILogger logger)
|
||||
{
|
||||
// Chỉ IsActive=true nếu file thực tế tồn tại trong wwwroot/templates/.
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,61 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace SolutionErp.Infrastructure.Persistence.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class AddProjectMasterFields : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "Investor",
|
||||
table: "Projects",
|
||||
type: "nvarchar(250)",
|
||||
maxLength: 250,
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "Location",
|
||||
table: "Projects",
|
||||
type: "nvarchar(500)",
|
||||
maxLength: 500,
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "Package",
|
||||
table: "Projects",
|
||||
type: "nvarchar(300)",
|
||||
maxLength: 300,
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<int>(
|
||||
name: "Year",
|
||||
table: "Projects",
|
||||
type: "int",
|
||||
nullable: true);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "Investor",
|
||||
table: "Projects");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "Location",
|
||||
table: "Projects");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "Package",
|
||||
table: "Projects");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "Year",
|
||||
table: "Projects");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -3484,9 +3484,17 @@ namespace SolutionErp.Infrastructure.Persistence.Migrations
|
||||
b.Property<DateTime?>("EndDate")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.Property<string>("Investor")
|
||||
.HasMaxLength(250)
|
||||
.HasColumnType("nvarchar(250)");
|
||||
|
||||
b.Property<bool>("IsDeleted")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<string>("Location")
|
||||
.HasMaxLength(500)
|
||||
.HasColumnType("nvarchar(500)");
|
||||
|
||||
b.Property<Guid?>("ManagerUserId")
|
||||
.HasColumnType("uniqueidentifier");
|
||||
|
||||
@ -3499,6 +3507,10 @@ namespace SolutionErp.Infrastructure.Persistence.Migrations
|
||||
.HasMaxLength(1000)
|
||||
.HasColumnType("nvarchar(1000)");
|
||||
|
||||
b.Property<string>("Package")
|
||||
.HasMaxLength(300)
|
||||
.HasColumnType("nvarchar(300)");
|
||||
|
||||
b.Property<DateTime?>("StartDate")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
@ -3508,6 +3520,9 @@ namespace SolutionErp.Infrastructure.Persistence.Migrations
|
||||
b.Property<Guid?>("UpdatedBy")
|
||||
.HasColumnType("uniqueidentifier");
|
||||
|
||||
b.Property<int?>("Year")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("Code")
|
||||
|
||||
Reference in New Issue
Block a user