From 402826ee3df6dc62b9fb7095fb06db2d42d3fb22 Mon Sep 17 00:00:00 2001 From: Chris Chen Date: Thu, 25 Jun 2026 17:46:31 -0700 Subject: [PATCH] feat(1099): round-trip Form1099BoxId through expense category DTOs/service Mirror Form990LineId: add Form1099BoxId + Form1099BoxCode to all four category DTOs (response + request, group + sub); load a boxCodes lookup dictionary in GetAllAsync and project it; set/copy the field in CreateGroupAsync, UpdateGroupAsync, CreateSubCategoryAsync, and UpdateSubCategoryAsync. All 4 category-service unit tests pass. Co-Authored-By: Claude Opus 4.8 --- API/ROLAC.API/DTOs/Expense/ExpenseCategoryDtos.cs | 6 ++++++ API/ROLAC.API/Services/ExpenseCategoryService.cs | 15 +++++++++++---- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/API/ROLAC.API/DTOs/Expense/ExpenseCategoryDtos.cs b/API/ROLAC.API/DTOs/Expense/ExpenseCategoryDtos.cs index 78e1250..6efcc5b 100644 --- a/API/ROLAC.API/DTOs/Expense/ExpenseCategoryDtos.cs +++ b/API/ROLAC.API/DTOs/Expense/ExpenseCategoryDtos.cs @@ -11,6 +11,8 @@ public class ExpenseSubCategoryDto public bool IsActive { get; set; } public int? Form990LineId { get; set; } public string? Form990LineCode { get; set; } + public int? Form1099BoxId { get; set; } + public string? Form1099BoxCode { get; set; } } public class ExpenseCategoryGroupDto @@ -22,6 +24,8 @@ public class ExpenseCategoryGroupDto public bool IsActive { get; set; } public int? Form990LineId { get; set; } public string? Form990LineCode { get; set; } + public int? Form1099BoxId { get; set; } + public string? Form1099BoxCode { get; set; } public List SubCategories { get; set; } = []; } @@ -31,6 +35,7 @@ public class CreateExpenseGroupRequest [MaxLength(200)] public string? Name_zh { get; set; } public int SortOrder { get; set; } public int? Form990LineId { get; set; } + public int? Form1099BoxId { get; set; } } public class UpdateExpenseGroupRequest : CreateExpenseGroupRequest { @@ -44,6 +49,7 @@ public class CreateExpenseSubCategoryRequest [MaxLength(200)] public string? Name_zh { get; set; } public int SortOrder { get; set; } public int? Form990LineId { get; set; } + public int? Form1099BoxId { get; set; } } public class UpdateExpenseSubCategoryRequest : CreateExpenseSubCategoryRequest { diff --git a/API/ROLAC.API/Services/ExpenseCategoryService.cs b/API/ROLAC.API/Services/ExpenseCategoryService.cs index ae6bb87..90967b0 100644 --- a/API/ROLAC.API/Services/ExpenseCategoryService.cs +++ b/API/ROLAC.API/Services/ExpenseCategoryService.cs @@ -25,25 +25,32 @@ public class ExpenseCategoryService : IExpenseCategoryService var lineCodes = await _db.Form990ExpenseLines.AsNoTracking() .ToDictionaryAsync(l => l.Id, l => l.LineCode); + var boxCodes = await _db.Form1099Boxes.AsNoTracking() + .ToDictionaryAsync(b => b.Id, b => b.BoxCode); + return groups.Select(g => new ExpenseCategoryGroupDto { Id = g.Id, Name_en = g.Name_en, Name_zh = g.Name_zh, SortOrder = g.SortOrder, IsActive = g.IsActive, Form990LineId = g.Form990LineId, Form990LineCode = g.Form990LineId.HasValue ? lineCodes.GetValueOrDefault(g.Form990LineId.Value) : null, + Form1099BoxId = g.Form1099BoxId, + Form1099BoxCode = g.Form1099BoxId.HasValue ? boxCodes.GetValueOrDefault(g.Form1099BoxId.Value) : null, SubCategories = subs.Where(s => s.GroupId == g.Id).Select(s => new ExpenseSubCategoryDto { Id = s.Id, GroupId = s.GroupId, Name_en = s.Name_en, Name_zh = s.Name_zh, SortOrder = s.SortOrder, IsActive = s.IsActive, Form990LineId = s.Form990LineId, Form990LineCode = s.Form990LineId.HasValue ? lineCodes.GetValueOrDefault(s.Form990LineId.Value) : null, + Form1099BoxId = s.Form1099BoxId, + Form1099BoxCode = s.Form1099BoxId.HasValue ? boxCodes.GetValueOrDefault(s.Form1099BoxId.Value) : null, }).ToList(), }).ToList(); } public async Task CreateGroupAsync(CreateExpenseGroupRequest r) { - var g = new ExpenseCategoryGroup { Name_en = r.Name_en, Name_zh = r.Name_zh, SortOrder = r.SortOrder, IsActive = true, Form990LineId = r.Form990LineId }; + var g = new ExpenseCategoryGroup { Name_en = r.Name_en, Name_zh = r.Name_zh, SortOrder = r.SortOrder, IsActive = true, Form990LineId = r.Form990LineId, Form1099BoxId = r.Form1099BoxId }; _db.ExpenseCategoryGroups.Add(g); await _db.SaveChangesAsync(); return g.Id; @@ -53,7 +60,7 @@ public class ExpenseCategoryService : IExpenseCategoryService { var g = await _db.ExpenseCategoryGroups.FindAsync(id) ?? throw new KeyNotFoundException($"ExpenseCategoryGroup {id} not found."); - g.Name_en = r.Name_en; g.Name_zh = r.Name_zh; g.SortOrder = r.SortOrder; g.IsActive = r.IsActive; g.Form990LineId = r.Form990LineId; + g.Name_en = r.Name_en; g.Name_zh = r.Name_zh; g.SortOrder = r.SortOrder; g.IsActive = r.IsActive; g.Form990LineId = r.Form990LineId; g.Form1099BoxId = r.Form1099BoxId; await _db.SaveChangesAsync(); } @@ -69,7 +76,7 @@ public class ExpenseCategoryService : IExpenseCategoryService { var exists = await _db.ExpenseCategoryGroups.AnyAsync(g => g.Id == r.GroupId); if (!exists) throw new KeyNotFoundException($"ExpenseCategoryGroup {r.GroupId} not found."); - var s = new ExpenseSubCategory { GroupId = r.GroupId, Name_en = r.Name_en, Name_zh = r.Name_zh, SortOrder = r.SortOrder, IsActive = true, Form990LineId = r.Form990LineId }; + var s = new ExpenseSubCategory { GroupId = r.GroupId, Name_en = r.Name_en, Name_zh = r.Name_zh, SortOrder = r.SortOrder, IsActive = true, Form990LineId = r.Form990LineId, Form1099BoxId = r.Form1099BoxId }; _db.ExpenseSubCategories.Add(s); await _db.SaveChangesAsync(); return s.Id; @@ -79,7 +86,7 @@ public class ExpenseCategoryService : IExpenseCategoryService { var s = await _db.ExpenseSubCategories.FindAsync(id) ?? throw new KeyNotFoundException($"ExpenseSubCategory {id} not found."); - s.GroupId = r.GroupId; s.Name_en = r.Name_en; s.Name_zh = r.Name_zh; s.SortOrder = r.SortOrder; s.IsActive = r.IsActive; s.Form990LineId = r.Form990LineId; + s.GroupId = r.GroupId; s.Name_en = r.Name_en; s.Name_zh = r.Name_zh; s.SortOrder = r.SortOrder; s.IsActive = r.IsActive; s.Form990LineId = r.Form990LineId; s.Form1099BoxId = r.Form1099BoxId; await _db.SaveChangesAsync(); }