[CLAUDE] App+Infra+FE-Admin: seed master data + MyDashboard widgets
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 2m48s

Task 1 — Seed master data unblock UAT/demo:
- DbInitializer.SeedDepartmentsAsync: 9 departments từ QT-TP-NCC.docx
  (PM/QS/CCM/PRO/FIN/ACT/EQU/HRA/BOD) — reference data không phải demo.
- DbInitializer.SeedDemoMasterDataAsync: 5 demo suppliers (NCC VLXD, NTP
  Xây dựng, TĐ Hoàng Nam, DV Clean, CĐT Vingroup — covers cả 5
  SupplierType) + 3 demo projects (FLOCK01, SkyGarden, Industrial).
  Chỉ seed nếu tables empty — respect admin's real data khi họ add.

Task 2 — Roles CRUD đã có sẵn trong UsersPage (Shield icon button mở
dialog gán 12 roles từ AppRoles.cs). Skip.

Task 3 — MyDashboard role-specific widgets:
- GetMyDashboardQuery (Reports): returns DraftsInProgress (tôi là
  Drafter + phase soạn thảo), PendingMyApproval (phase eligible role
  tôi + không phải tôi drafter), DueSoon 24h, Overdue, DraftsTotalValue.
- Endpoint GET /api/reports/my-dashboard.
- FE MyDashboardRow ở đầu DashboardPage: 4 card hover → navigate.
  Admin ẩn row nếu tất cả = 0 (ERP noise reduction).
  'Đang soạn thảo' + 'Chờ tôi duyệt' clickable → /contracts?filter=...
  (filter param để wire lần sau; row hiện chưa implement).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
pqhuy1987
2026-04-21 22:06:28 +07:00
parent 4690cc3a81
commit 6197c841bb
4 changed files with 226 additions and 1 deletions

View File

@ -5,6 +5,7 @@ using Microsoft.Extensions.Logging;
using SolutionErp.Domain.Contracts;
using SolutionErp.Domain.Forms;
using SolutionErp.Domain.Identity;
using SolutionErp.Domain.Master;
namespace SolutionErp.Infrastructure.Persistence;
@ -29,6 +30,8 @@ public static class DbInitializer
await SeedAdminAsync(userManager, logger);
await SeedMenuTreeAsync(db, logger);
await SeedAdminPermissionsAsync(db, roleManager, logger);
await SeedDepartmentsAsync(db, logger);
await SeedDemoMasterDataAsync(db, logger);
await SeedContractTemplatesAsync(db, logger);
await WarnDefaultAdminPasswordAsync(userManager, logger);
}
@ -145,6 +148,70 @@ public static class DbInitializer
}
}
// 9 departments từ QT-TP-NCC.docx — reference data, không phải demo.
// Mỗi role-based check trên workflow tham chiếu phòng ban này.
private static async Task SeedDepartmentsAsync(ApplicationDbContext db, ILogger logger)
{
var departments = new (string Code, string Name, string? Note)[]
{
("PM", "Ban Quản lý Dự án", "PM/PD: Giám đốc Dự án, Giám đốc Thi công"),
("QS", "Phòng Quantity Surveyor", "QS công trường — tính khối lượng + soạn HĐ"),
("CCM", "Phòng Kiểm soát Chi phí", "Cost Control Management — review HĐ trước khi BOD ký"),
("PRO", "Phòng Cung ứng", "Procurement — NCC vật tư / thầu phụ"),
("FIN", "Phòng Tài chính", "Financial — điều khoản thanh toán, bảo lãnh"),
("ACT", "Phòng Kế toán", "Accounting — thuế, hóa đơn"),
("EQU", "Phòng Thiết bị", "Equipment — thuê/mua máy móc"),
("HRA", "Phòng Nhân sự - Hành chính", "HRA/ISO — đóng dấu HĐ sau BOD ký"),
("BOD", "Ban Giám đốc", "Board of Directors — ký duyệt HĐ"),
};
var existingCodes = await db.Departments.Select(d => d.Code).ToListAsync();
var added = 0;
foreach (var (code, name, note) in departments)
{
if (existingCodes.Contains(code)) continue;
db.Departments.Add(new Department { Code = code, Name = name, Note = note });
added++;
}
if (added > 0)
{
await db.SaveChangesAsync();
logger.LogInformation("Seeded {Count} departments", added);
}
}
// Sample master data for UAT/demo. Gated by: only seed if tables are empty
// (respects admin's real data once they start adding).
private static async Task SeedDemoMasterDataAsync(ApplicationDbContext db, ILogger logger)
{
if (await db.Suppliers.AnyAsync() && await db.Projects.AnyAsync()) return;
if (!await db.Suppliers.AnyAsync())
{
db.Suppliers.AddRange(
new Supplier { Code = "NCC-VLXD", Name = "Công ty TNHH Vật liệu Xây dựng ABC", Type = SupplierType.NhaCungCap, TaxCode = "0100000001", Phone = "024 3888 1111", Email = "contact@vlxd-abc.vn", ContactPerson = "Nguyễn Văn An", Address = "123 Láng Hạ, Đống Đa, Hà Nội" },
new Supplier { Code = "NTP-XD", Name = "Công ty CP Xây dựng Thăng Long", Type = SupplierType.NhaThauPhu, TaxCode = "0100000002", Phone = "024 3888 2222", Email = "info@thanglong-xd.vn", ContactPerson = "Trần Thị Bình", Address = "45 Nguyễn Chí Thanh, Đống Đa, Hà Nội" },
new Supplier { Code = "TD-NEHOANG", Name = "Tổ đội Hoàng Nam", Type = SupplierType.ToDoi, Phone = "098 111 2233", ContactPerson = "Phạm Hoàng Nam", Address = "Cầu Giấy, Hà Nội", Note = "Tổ đội cốp pha 15 người" },
new Supplier { Code = "DV-CLEAN", Name = "Công ty TNHH Vệ sinh Công nghiệp Clean Pro", Type = SupplierType.DonViDichVu, TaxCode = "0100000004", Phone = "024 3888 4444", Email = "sales@cleanpro.vn", Address = "Trung Hòa, Cầu Giấy, Hà Nội" },
new Supplier { Code = "CDT-VIN", Name = "Tập đoàn Vingroup", Type = SupplierType.ChuDauTu, TaxCode = "0100109106", Phone = "024 3974 9999", Email = "contact@vingroup.net", Address = "7 Bằng Lăng 1, Vinhomes Riverside, Long Biên, Hà Nội", Note = "Chủ đầu tư - bypass CCM" }
);
logger.LogInformation("Seeded 5 demo suppliers");
}
if (!await db.Projects.AnyAsync())
{
var now = DateTime.UtcNow;
db.Projects.AddRange(
new Project { Code = "FLOCK01", Name = "Dự án FLOCK 01 — Khu đô thị Mỹ Đình", StartDate = now.AddMonths(-3), EndDate = now.AddMonths(18), BudgetTotal = 120_000_000_000m, Note = "Dự án mẫu — demo" },
new Project { Code = "SKYGARDEN", Name = "Sky Garden Residence", StartDate = now.AddMonths(-6), EndDate = now.AddMonths(12), BudgetTotal = 85_000_000_000m, Note = "Dự án mẫu — demo" },
new Project { Code = "INDUSTRIAL", Name = "Nhà máy Công nghiệp Yên Phong", StartDate = now.AddMonths(-1), EndDate = now.AddMonths(9), BudgetTotal = 45_000_000_000m, Note = "Dự án mẫu — demo" }
);
logger.LogInformation("Seeded 3 demo projects");
}
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/.