feat(1099): seed Form1099Box catalog and default subcategory mappings
Adds Form1099BoxSeed (NEC-1, MISC-1) and Form1099SubMappingSeed (6 service/rent subcategories), SeedForm1099BoxesAsync method with null-fill idempotency (never clobbers admin edits), and wires it into SeedAsync after SeedForm990ExpenseLinesAsync. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -137,6 +137,23 @@ public static class DbSeeder
|
||||
("Other", "Gifts", "24"),
|
||||
];
|
||||
|
||||
private static readonly (string Code, string En, string Zh, string FormType, int Sort)[] Form1099BoxSeed =
|
||||
[
|
||||
(Form1099.BoxNec1, "Nonemployee compensation", "非員工報酬", "1099-NEC", 1),
|
||||
(Form1099.BoxMisc1, "Rents", "租金", "1099-MISC", 2),
|
||||
];
|
||||
|
||||
// Only service/rent subcategories get a box. Everything else stays unmapped (not reportable).
|
||||
private static readonly (string GroupEn, string SubEn, string Code)[] Form1099SubMappingSeed =
|
||||
[
|
||||
("Personnel", "Honorarium", Form1099.BoxNec1),
|
||||
("Personnel", "Contract Labor", Form1099.BoxNec1),
|
||||
("Professional Services", "Legal", Form1099.BoxNec1),
|
||||
("Professional Services", "Accounting & Audit", Form1099.BoxNec1),
|
||||
("Professional Services", "Other Professional", Form1099.BoxNec1),
|
||||
("Facility", "Rent", Form1099.BoxMisc1),
|
||||
];
|
||||
|
||||
// One-time corrections for subcategories that were mapped to the WRONG line in an earlier
|
||||
// seed. The normal mapping loop below only fills NULLs, so it cannot fix an existing bad
|
||||
// value — this block does. Idempotent: each row fires only while the subcategory still holds
|
||||
@@ -375,6 +392,25 @@ public static class DbSeeder
|
||||
await db.SaveChangesAsync();
|
||||
}
|
||||
|
||||
public static async Task SeedForm1099BoxesAsync(AppDbContext db)
|
||||
{
|
||||
foreach (var (code, en, zh, formType, sort) in Form1099BoxSeed)
|
||||
if (!await db.Form1099Boxes.AnyAsync(b => b.BoxCode == code))
|
||||
db.Form1099Boxes.Add(new Form1099Box
|
||||
{ BoxCode = code, Name_en = en, Name_zh = zh, FormType = formType, SortOrder = sort, IsActive = true });
|
||||
await db.SaveChangesAsync();
|
||||
|
||||
var boxesByCode = await db.Form1099Boxes.ToDictionaryAsync(b => b.BoxCode, b => b.Id);
|
||||
var subs = await db.ExpenseSubCategories.Include(s => s.Group).ToListAsync();
|
||||
foreach (var (groupEn, subEn, code) in Form1099SubMappingSeed)
|
||||
{
|
||||
var sub = subs.FirstOrDefault(s => s.Group!.Name_en == groupEn && s.Name_en == subEn);
|
||||
if (sub is not null && sub.Form1099BoxId is null && boxesByCode.TryGetValue(code, out var boxId))
|
||||
sub.Form1099BoxId = boxId;
|
||||
}
|
||||
await db.SaveChangesAsync();
|
||||
}
|
||||
|
||||
public static async Task SeedChurchProfileAsync(AppDbContext db)
|
||||
{
|
||||
// Singleton row used by the disbursement module (issuer info + check counter).
|
||||
@@ -454,6 +490,7 @@ public static class DbSeeder
|
||||
await SeedMinistriesAsync(db);
|
||||
await SeedExpenseCategoriesAsync(db);
|
||||
await SeedForm990ExpenseLinesAsync(db);
|
||||
await SeedForm1099BoxesAsync(db);
|
||||
await SeedChurchProfileAsync(db);
|
||||
await SeedSiteSettingAsync(db);
|
||||
await SeedNotificationSettingAsync(db, config);
|
||||
|
||||
Reference in New Issue
Block a user