From 15cdfe6f92e04447f981eb0b2853fdafdc32df4d Mon Sep 17 00:00:00 2001 From: Chris Chen Date: Fri, 29 May 2026 18:21:52 -0700 Subject: [PATCH] feat(expense): add expense, category, and monthly-statement DTOs --- .../DTOs/Expense/ExpenseCategoryDtos.cs | 45 ++++++++++++++ API/ROLAC.API/DTOs/Expense/ExpenseDtos.cs | 59 +++++++++++++++++++ .../DTOs/Expense/MonthlyStatementDtos.cs | 35 +++++++++++ 3 files changed, 139 insertions(+) create mode 100644 API/ROLAC.API/DTOs/Expense/ExpenseCategoryDtos.cs create mode 100644 API/ROLAC.API/DTOs/Expense/ExpenseDtos.cs create mode 100644 API/ROLAC.API/DTOs/Expense/MonthlyStatementDtos.cs diff --git a/API/ROLAC.API/DTOs/Expense/ExpenseCategoryDtos.cs b/API/ROLAC.API/DTOs/Expense/ExpenseCategoryDtos.cs new file mode 100644 index 0000000..0d235b3 --- /dev/null +++ b/API/ROLAC.API/DTOs/Expense/ExpenseCategoryDtos.cs @@ -0,0 +1,45 @@ +using System.ComponentModel.DataAnnotations; +namespace ROLAC.API.DTOs.Expense; + +public class ExpenseSubCategoryDto +{ + public int Id { get; set; } + public int GroupId { get; set; } + public string Name_en { get; set; } = ""; + public string? Name_zh { get; set; } + public int SortOrder { get; set; } + public bool IsActive { get; set; } +} + +public class ExpenseCategoryGroupDto +{ + public int Id { get; set; } + public string Name_en { get; set; } = ""; + public string? Name_zh { get; set; } + public int SortOrder { get; set; } + public bool IsActive { get; set; } + public List SubCategories { get; set; } = []; +} + +public class CreateExpenseGroupRequest +{ + [Required, MaxLength(200)] public string Name_en { get; set; } = ""; + [MaxLength(200)] public string? Name_zh { get; set; } + public int SortOrder { get; set; } +} +public class UpdateExpenseGroupRequest : CreateExpenseGroupRequest +{ + public bool IsActive { get; set; } = true; +} + +public class CreateExpenseSubCategoryRequest +{ + [Required] public int GroupId { get; set; } + [Required, MaxLength(200)] public string Name_en { get; set; } = ""; + [MaxLength(200)] public string? Name_zh { get; set; } + public int SortOrder { get; set; } +} +public class UpdateExpenseSubCategoryRequest : CreateExpenseSubCategoryRequest +{ + public bool IsActive { get; set; } = true; +} diff --git a/API/ROLAC.API/DTOs/Expense/ExpenseDtos.cs b/API/ROLAC.API/DTOs/Expense/ExpenseDtos.cs new file mode 100644 index 0000000..f816e42 --- /dev/null +++ b/API/ROLAC.API/DTOs/Expense/ExpenseDtos.cs @@ -0,0 +1,59 @@ +using System.ComponentModel.DataAnnotations; +namespace ROLAC.API.DTOs.Expense; + +public class ExpenseListItemDto +{ + public int Id { get; set; } + public string Type { get; set; } = ""; + public string Status { get; set; } = ""; + public decimal Amount { get; set; } + public string Description { get; set; } = ""; + public int MinistryId { get; set; } + public string MinistryName { get; set; } = ""; + public int CategoryGroupId { get; set; } + public string CategoryGroupName { get; set; } = ""; + public int SubCategoryId { get; set; } + public string SubCategoryName { get; set; } = ""; + public string? VendorName { get; set; } + public int? MemberId { get; set; } + public string? MemberName { get; set; } + public string ExpenseDate { get; set; } = ""; // yyyy-MM-dd + public bool HasReceipt { get; set; } +} + +public class ExpenseDto : ExpenseListItemDto +{ + public string? CheckNumber { get; set; } + public string? Notes { get; set; } + public string? ReviewNotes { get; set; } + public string? SubmittedBy { get; set; } + public DateTimeOffset? SubmittedAt { get; set; } + public DateTimeOffset? ReviewedAt { get; set; } + public DateTimeOffset? PaidAt { get; set; } +} + +public class CreateExpenseRequest +{ + [Required] public string Type { get; set; } = "StaffReimbursement"; // VendorPayment|StaffReimbursement + [Required] public int MinistryId { get; set; } + [Required] public int CategoryGroupId { get; set; } + [Required] public int SubCategoryId { get; set; } + [Range(0.01, 9_999_999)] public decimal Amount { get; set; } + [Required, MaxLength(500)] public string Description { get; set; } = ""; + [MaxLength(200)] public string? VendorName { get; set; } + public int? MemberId { get; set; } // ignored for self-service (server uses caller) + [MaxLength(50)] public string? CheckNumber { get; set; } + [Required] public DateOnly ExpenseDate { get; set; } + public string? Notes { get; set; } +} +public class UpdateExpenseRequest : CreateExpenseRequest { } + +public class RejectExpenseRequest +{ + [MaxLength(500)] public string? ReviewNotes { get; set; } +} +public class PayExpenseRequest +{ + [MaxLength(50)] public string? CheckNumber { get; set; } + public DateOnly? PaidAt { get; set; } +} diff --git a/API/ROLAC.API/DTOs/Expense/MonthlyStatementDtos.cs b/API/ROLAC.API/DTOs/Expense/MonthlyStatementDtos.cs new file mode 100644 index 0000000..45ce036 --- /dev/null +++ b/API/ROLAC.API/DTOs/Expense/MonthlyStatementDtos.cs @@ -0,0 +1,35 @@ +using System.ComponentModel.DataAnnotations; +namespace ROLAC.API.DTOs.Expense; + +public class MonthlyStatementDto +{ + public int Id { get; set; } + public int Year { get; set; } + public int Month { get; set; } + public decimal OpeningBalance { get; set; } + public decimal TotalGiving { get; set; } + public decimal TotalOtherIncome { get; set; } + public decimal TotalExpenses { get; set; } + public decimal CalculatedClosingBalance { get; set; } + public decimal BankStatementBalance { get; set; } + public decimal Difference { get; set; } + public string? Notes { get; set; } + public bool IsFinalized { get; set; } +} + +public class CreateMonthlyStatementRequest +{ + [Range(2000, 2100)] public int Year { get; set; } + [Range(1, 12)] public int Month { get; set; } + public decimal OpeningBalance { get; set; } + public decimal TotalOtherIncome { get; set; } + public decimal BankStatementBalance { get; set; } + public string? Notes { get; set; } +} +public class UpdateMonthlyStatementRequest +{ + public decimal OpeningBalance { get; set; } + public decimal TotalOtherIncome { get; set; } + public decimal BankStatementBalance { get; set; } + public string? Notes { get; set; } +}