using Microsoft.AspNetCore.Identity; using Microsoft.EntityFrameworkCore; using ROLAC.API.Entities; namespace ROLAC.API.Data; public static class DbSeeder { private static readonly (string En, string Zh, int Sort)[] GivingCategorySeed = [ ("Tithe", "什一奉獻", 1), ("General Offering", "一般奉獻", 2), ("Special Offering", "特別奉獻", 3), ("Building Fund", "建堂基金", 4), ("Mission", "宣教奉獻", 5), ]; private static readonly (string En, string Zh, int Sort)[] MinistrySeed = [ ("Administration", "行政", 1), ("Preaching", "講道", 2), ("Emcee", "司會", 3), ("Worship", "敬拜", 4), ("PPT/Media", "PPT/影音", 5), ("Sound", "音控", 6), ("Facility", "場地組", 7), ("Hospitality", "招待", 8), ("Children", "兒牧", 9), ("Catering", "餐飲", 10), ]; // (GroupEn, GroupZh, Sort, SubItems[(SubEn, SubZh)]) private static readonly (string En, string Zh, int Sort, (string En, string Zh)[] Subs)[] ExpenseCategorySeed = [ ("Equipment", "設備", 1, [("Purchase","購置"),("Rental","租借"),("Maintenance & Repair","維修")]), ("Consumables", "消耗品", 2, [("Batteries","電池"),("Accessories","配件"),("Cleaning Supplies","清潔用品"),("Office Supplies","文具")]), ("Food & Beverage", "餐飲", 3, [("Catering","出餐費用"),("Food Ingredients","食材採購"),("Utensils","器具"),("Consumables","消耗品")]), ("Training", "培訓", 4, [("Course Fees","課程費用"),("Books","書籍"),("Conference","研討會"),("Travel","差旅")]), ("Materials", "教材", 5, [("Printing","印刷費用"),("Craft Supplies","手工材料"),("Copyright & Licensing","版權購買")]), ("Facility", "場地", 6, [("Rent","場地租金"),("Utilities","水電"),("Property Insurance","財產保險"),("Decoration","裝飾")]), ("Printing", "印刷", 7, [("Bulletins","週報"),("Order of Service","程序單"),("Posters","海報")]), ("Missions", "宣教", 8, [("Offering Transfer","奉獻轉帳"),("Missionary Support","宣教士支援"),("Travel","差旅")]), ("Benevolence", "關懷救助", 9, [("Emergency Aid","急難救助"),("Condolence Gifts","慰問禮品"),("Visit Expenses","探訪費用")]), ("Other", "其他", 10, [("Miscellaneous","雜支")]), ("Personnel", "人事", 11, [("Salary & Wages","薪資"),("Payroll Taxes","薪資稅費"),("Employee Benefits","員工福利"),("Workers Compensation","勞工保險"),("Honorarium","酬庸"),("Staff Training","同工進修"),("Contract Labor","外包勞務")]), ]; private static readonly (string Name, string Description)[] Roles = [ ("super_admin", "System administrator — full access"), ("pastor", "Pastor — full member and financial overview"), ("board_member", "Board member — church governance"), ("coworker_chair", "Coworker chair — coordinates ministry leaders"), ("ministry_leader", "Ministry leader — scoped to own ministry"), ("district_leader", "District leader — manages multiple cell groups"), ("cell_leader", "Cell leader — scoped to own cell group"), ("coworker", "Coworker — general worker in assigned ministry"), ("finance", "Finance — manages giving and expense reports"), ("secretary", "Secretary — manages member data and scheduling"), ("worship_leader", "Worship leader — manages song library and setlists (Phase deferred)"), ("member", "Member — views own profile and service roster"), ("visitor", "Visitor — public pages only"), ]; public static async Task SeedRolesAsync(RoleManager roleManager) { foreach (var (name, description) in Roles) { if (!await roleManager.RoleExistsAsync(name)) { await roleManager.CreateAsync(new AppRole { Name = name, Description = description, }); } } } public static async Task SeedGivingCategoriesAsync(AppDbContext db) { foreach (var (en, zh, sort) in GivingCategorySeed) { if (!await db.GivingCategories.AnyAsync(c => c.Name_en == en)) { db.GivingCategories.Add(new GivingCategory { Name_en = en, Name_zh = zh, SortOrder = sort, IsActive = true, // Audit fields are stamped by AuditSaveChangesInterceptor on save. }); } } await db.SaveChangesAsync(); } public static async Task SeedMinistriesAsync(AppDbContext db) { foreach (var (en, zh, sort) in MinistrySeed) { if (!await db.Ministries.AnyAsync(m => m.Name_en == en)) db.Ministries.Add(new Ministry { Name_en = en, Name_zh = zh, SortOrder = sort, IsActive = true }); } await db.SaveChangesAsync(); } public static async Task SeedExpenseCategoriesAsync(AppDbContext db) { foreach (var (gEn, gZh, gSort, subs) in ExpenseCategorySeed) { var group = await db.ExpenseCategoryGroups.FirstOrDefaultAsync(g => g.Name_en == gEn); if (group is null) { group = new ExpenseCategoryGroup { Name_en = gEn, Name_zh = gZh, SortOrder = gSort, IsActive = true }; db.ExpenseCategoryGroups.Add(group); await db.SaveChangesAsync(); // assign group.Id } var sub = 1; foreach (var (sEn, sZh) in subs) { if (!await db.ExpenseSubCategories.AnyAsync(s => s.GroupId == group.Id && s.Name_en == sEn)) db.ExpenseSubCategories.Add(new ExpenseSubCategory { GroupId = group.Id, Name_en = sEn, Name_zh = sZh, SortOrder = sub, IsActive = true }); sub++; } } await db.SaveChangesAsync(); } public static async Task SeedChurchProfileAsync(AppDbContext db) { // Singleton row used by the disbursement module (issuer info + check counter). if (!await db.ChurchProfiles.AnyAsync()) { db.ChurchProfiles.Add(new ChurchProfile { Name = "River Of Life Christian Church", City = "Arcadia", State = "CA", NextCheckNumber = 1001, }); await db.SaveChangesAsync(); } } /// /// Seeds roles and (in Development) the default admin account. /// Called once on application startup after migrations have been applied. /// public static async Task SeedAsync(IServiceProvider services) { var roleManager = services.GetRequiredService>(); var userManager = services.GetRequiredService>(); var env = services.GetRequiredService(); await SeedRolesAsync(roleManager); var db = services.GetRequiredService(); await SeedGivingCategoriesAsync(db); await SeedMinistriesAsync(db); await SeedExpenseCategoriesAsync(db); await SeedChurchProfileAsync(db); if (env.IsDevelopment()) await SeedAdminUserAsync(userManager); } /// /// Creates a super_admin test account for local development. /// DO NOT call this in production — remove or guard with IsDevelopment(). /// Credentials: admin@rolac.org / Admin1234! /// public static async Task SeedAdminUserAsync(UserManager userManager) { const string adminEmail = "admin@rolac.org"; const string adminPassword = "Admin1234!"; if (await userManager.FindByEmailAsync(adminEmail) is null) { var admin = new AppUser { UserName = adminEmail, Email = adminEmail, EmailConfirmed = true, IsActive = true, LanguagePreference = "en", CreatedAt = DateTime.UtcNow, }; var result = await userManager.CreateAsync(admin, adminPassword); if (result.Succeeded) await userManager.AddToRoleAsync(admin, "super_admin"); } } }