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 <noreply@anthropic.com>
This commit is contained in:
Chris Chen
2026-06-25 17:46:31 -07:00
parent 82096e7e6f
commit 402826ee3d
2 changed files with 17 additions and 4 deletions
@@ -11,6 +11,8 @@ public class ExpenseSubCategoryDto
public bool IsActive { get; set; } public bool IsActive { get; set; }
public int? Form990LineId { get; set; } public int? Form990LineId { get; set; }
public string? Form990LineCode { get; set; } public string? Form990LineCode { get; set; }
public int? Form1099BoxId { get; set; }
public string? Form1099BoxCode { get; set; }
} }
public class ExpenseCategoryGroupDto public class ExpenseCategoryGroupDto
@@ -22,6 +24,8 @@ public class ExpenseCategoryGroupDto
public bool IsActive { get; set; } public bool IsActive { get; set; }
public int? Form990LineId { get; set; } public int? Form990LineId { get; set; }
public string? Form990LineCode { get; set; } public string? Form990LineCode { get; set; }
public int? Form1099BoxId { get; set; }
public string? Form1099BoxCode { get; set; }
public List<ExpenseSubCategoryDto> SubCategories { get; set; } = []; public List<ExpenseSubCategoryDto> SubCategories { get; set; } = [];
} }
@@ -31,6 +35,7 @@ public class CreateExpenseGroupRequest
[MaxLength(200)] public string? Name_zh { get; set; } [MaxLength(200)] public string? Name_zh { get; set; }
public int SortOrder { get; set; } public int SortOrder { get; set; }
public int? Form990LineId { get; set; } public int? Form990LineId { get; set; }
public int? Form1099BoxId { get; set; }
} }
public class UpdateExpenseGroupRequest : CreateExpenseGroupRequest public class UpdateExpenseGroupRequest : CreateExpenseGroupRequest
{ {
@@ -44,6 +49,7 @@ public class CreateExpenseSubCategoryRequest
[MaxLength(200)] public string? Name_zh { get; set; } [MaxLength(200)] public string? Name_zh { get; set; }
public int SortOrder { get; set; } public int SortOrder { get; set; }
public int? Form990LineId { get; set; } public int? Form990LineId { get; set; }
public int? Form1099BoxId { get; set; }
} }
public class UpdateExpenseSubCategoryRequest : CreateExpenseSubCategoryRequest public class UpdateExpenseSubCategoryRequest : CreateExpenseSubCategoryRequest
{ {
@@ -25,25 +25,32 @@ public class ExpenseCategoryService : IExpenseCategoryService
var lineCodes = await _db.Form990ExpenseLines.AsNoTracking() var lineCodes = await _db.Form990ExpenseLines.AsNoTracking()
.ToDictionaryAsync(l => l.Id, l => l.LineCode); .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 return groups.Select(g => new ExpenseCategoryGroupDto
{ {
Id = g.Id, Name_en = g.Name_en, Name_zh = g.Name_zh, Id = g.Id, Name_en = g.Name_en, Name_zh = g.Name_zh,
SortOrder = g.SortOrder, IsActive = g.IsActive, SortOrder = g.SortOrder, IsActive = g.IsActive,
Form990LineId = g.Form990LineId, Form990LineId = g.Form990LineId,
Form990LineCode = g.Form990LineId.HasValue ? lineCodes.GetValueOrDefault(g.Form990LineId.Value) : null, 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 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, Id = s.Id, GroupId = s.GroupId, Name_en = s.Name_en, Name_zh = s.Name_zh,
SortOrder = s.SortOrder, IsActive = s.IsActive, SortOrder = s.SortOrder, IsActive = s.IsActive,
Form990LineId = s.Form990LineId, Form990LineId = s.Form990LineId,
Form990LineCode = s.Form990LineId.HasValue ? lineCodes.GetValueOrDefault(s.Form990LineId.Value) : null, 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(),
}).ToList(); }).ToList();
} }
public async Task<int> CreateGroupAsync(CreateExpenseGroupRequest r) public async Task<int> 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); _db.ExpenseCategoryGroups.Add(g);
await _db.SaveChangesAsync(); await _db.SaveChangesAsync();
return g.Id; return g.Id;
@@ -53,7 +60,7 @@ public class ExpenseCategoryService : IExpenseCategoryService
{ {
var g = await _db.ExpenseCategoryGroups.FindAsync(id) var g = await _db.ExpenseCategoryGroups.FindAsync(id)
?? throw new KeyNotFoundException($"ExpenseCategoryGroup {id} not found."); ?? 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(); await _db.SaveChangesAsync();
} }
@@ -69,7 +76,7 @@ public class ExpenseCategoryService : IExpenseCategoryService
{ {
var exists = await _db.ExpenseCategoryGroups.AnyAsync(g => g.Id == r.GroupId); var exists = await _db.ExpenseCategoryGroups.AnyAsync(g => g.Id == r.GroupId);
if (!exists) throw new KeyNotFoundException($"ExpenseCategoryGroup {r.GroupId} not found."); 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); _db.ExpenseSubCategories.Add(s);
await _db.SaveChangesAsync(); await _db.SaveChangesAsync();
return s.Id; return s.Id;
@@ -79,7 +86,7 @@ public class ExpenseCategoryService : IExpenseCategoryService
{ {
var s = await _db.ExpenseSubCategories.FindAsync(id) var s = await _db.ExpenseSubCategories.FindAsync(id)
?? throw new KeyNotFoundException($"ExpenseSubCategory {id} not found."); ?? 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(); await _db.SaveChangesAsync();
} }