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"),
|
("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
|
// 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
|
// 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
|
// 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();
|
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)
|
public static async Task SeedChurchProfileAsync(AppDbContext db)
|
||||||
{
|
{
|
||||||
// Singleton row used by the disbursement module (issuer info + check counter).
|
// Singleton row used by the disbursement module (issuer info + check counter).
|
||||||
@@ -454,6 +490,7 @@ public static class DbSeeder
|
|||||||
await SeedMinistriesAsync(db);
|
await SeedMinistriesAsync(db);
|
||||||
await SeedExpenseCategoriesAsync(db);
|
await SeedExpenseCategoriesAsync(db);
|
||||||
await SeedForm990ExpenseLinesAsync(db);
|
await SeedForm990ExpenseLinesAsync(db);
|
||||||
|
await SeedForm1099BoxesAsync(db);
|
||||||
await SeedChurchProfileAsync(db);
|
await SeedChurchProfileAsync(db);
|
||||||
await SeedSiteSettingAsync(db);
|
await SeedSiteSettingAsync(db);
|
||||||
await SeedNotificationSettingAsync(db, config);
|
await SeedNotificationSettingAsync(db, config);
|
||||||
|
|||||||
Reference in New Issue
Block a user