Files
ROLAC/docs/superpowers/specs/2026-06-24-expense-990-functional-expenses-design.md
Chris Chen b51f22cfba docs: expand 990 expense-line catalog and add categories to cover gaps
Add 990 lines 5/8/11b/11c/20 to the catalog and new natural categories
(Personnel officer comp + pension, Missions foreign support, Printing
advertising, plus Professional Services / Information Technology /
Finance & Banking groups) so the category tree covers the common Part IX
lines instead of dumping uncovered lines into 24.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-24 18:30:49 -07:00

12 KiB
Raw Permalink Blame History

子專案 A — 支出 990 化(Functional Expenses / Part IX)設計

日期: 2026-06-24 狀態: Draft(待 user review 範圍: 僅子專案 A。1099 收款人(B)、收入端 Part VIII(C)為獨立 spec,不在此。


1. 目標與背景

教會依 IRC §6033(a)(3)(A) 免於申報 Form 990,但本系統要做到 990 查帳就緒(audit-readiness):在 IRS 檢查時,能依需求產出等同 Form 990 Part IX — Statement of Functional Expenses 的功能性費用表及佐證明細。

Part IX 本質是一個矩陣:

                       Program | Mgmt & General | Fundraising | Total
自然費用行(990 line)
  7  Salaries & wages    ...        ...             ...        ...
  9  Employee benefits   ...        ...             ...        ...
  10 Payroll taxes       ...        ...             ...        ...
  16 Occupancy           ...        ...             ...        ...
  ...
  24 Other expenses      ...        ...             ...        ...
  ─────────────────────────────────────────────────────────────
  Total                  ...        ...             ...        ...

現況:FinanceDashboardService 已能按 Ministry → CategoryGroup → SubCategory 彙總(Paid+Approved 口徑),但沒有功能別維度,自然類別也未對應 990 行。本子專案在現有兩條軸之上疊一層「990 對照」,不重寫分類樹。

設計原則

  • 維持現有兩條軸:Ministry = 功能別來源;ExpenseCategoryGroup → ExpenseSubCategory = 自然科目。
  • 990 對照以映射欄位 + 參考表實現(資料驅動,與 RolePermission / Categories 同風格)。
  • 單筆支出單一功能別(direct-charge),不做跨功能比例分攤。
  • 向後相容:新增欄位皆 nullable,既有資料不破。

2. 資料模型變更

2.1 功能別(Part IX 三欄)

功能別值域(字串,沿用 codebase 用 stringType/Status 的慣例): "Program" | "ManagementGeneral" | "Fundraising"

Ministry(新增欄位)

欄位 型別 說明
DefaultFunctionalClass varchar(20) NOT NULL DEFAULT 'Program' 該事工支出的預設功能別

Expense(新增欄位)

欄位 型別 說明
FunctionalClass varchar(20)? 覆寫;null = 繼承 Ministry

有效功能別(報表計算用):

EffectiveFunctionalClass = Expense.FunctionalClass
                        ?? Ministry.DefaultFunctionalClass
                        ?? "Program"   // 最終保底

Ministry 預設 seed:Administration → ManagementGeneral,其餘 9 個事工 → Program。目前無 Fundraising 事工(教會少見),需要時用單筆覆寫。

2.2 990 Part IX 行目錄 + 映射

新表 Form990ExpenseLine(參考表 / 資料驅動)

欄位 型別 說明
Id int PK
LineCode varchar(10) NOT NULL UNIQUE 990 行編號,如 "7""11g""16""24"
Name_en varchar(200) NOT NULL 如 "Other salaries and wages"
Name_zh varchar(200)?
SortOrder int NOT NULL 報表列順序
IsActive bool NOT NULL DEFAULT true

繼承 AuditableEntity(與其他類別表一致)。

映射欄位(兩處,nullable):

  • ExpenseSubCategory.Form990LineId(int? FK → Form990ExpenseLine.Id)— 主要映射
  • ExpenseCategoryGroup.Form990LineId(int? FK)— 大類預設(便利用)

有效 990 行:

EffectiveLine = SubCategory.Form990LineId
             ?? Group.Form990LineId
             ?? <line "24 Other expenses">   // 保底,確保無漏列

映射必須能下到子項目層:Personnel 大類底下 Salary→line 7、Payroll Taxes→line 10、Benefits→line 9 是三個不同 990 行,大類層無法表達。

seed 的 990 行子集(教會常用):

LineCode Name_en Name_zh
1 Grants to domestic organizations 對國內機構之捐贈
2 Grants to domestic individuals 對國內個人之捐贈
3 Grants to foreign organizations/individuals 對國外之捐贈
5 Compensation of current officers / key employees 主要職員/負責人薪酬
7 Other salaries and wages 薪資
8 Pension plan accruals and contributions 退休金提撥
9 Other employee benefits 員工福利
10 Payroll taxes 薪資稅
11b Legal fees 法律服務費
11c Accounting fees 會計與審計費
11g Other fees for services (non-employee) 其他勞務報酬(非員工)
12 Advertising and promotion 廣告與推廣
13 Office expenses 辦公費用
14 Information technology 資訊科技
16 Occupancy 場地佔用
17 Travel 差旅
19 Conferences, conventions, and meetings 會議與研習
20 Interest 利息
22 Depreciation 折舊(本子專案不映射,留行供未來資本化使用)
23 Insurance 保險
24 Other expenses 其他費用

現有子項目 → 990 行的預設映射 seed:

子項目(大類) 990 行
Personnel > Salary & Wages 7
Personnel > Payroll Taxes 10
Personnel > Employee Benefits 9
Personnel > Workers Compensation 9
Personnel > Honorarium 11g
Personnel > Contract Labor 11g
Personnel > Staff Training 19
Facility > Rent 16
Facility > Utilities 16
Facility > Property Insurance 23
Facility > Decoration 24
Training > Course Fees 19
Training > Conference 19
Training > Books 24
Training > Travel 17
Missions > Travel 17
Missions > Offering Transfer 1
Missions > Missionary Support 1
Benevolence > Emergency Aid 2
Benevolence > Condolence Gifts 2
Benevolence > Visit Expenses 2
Consumables > Office Supplies 13
Consumables > Batteries / Accessories / Cleaning Supplies 24
Printing > Bulletins / Order of Service 13
Printing > Posters 12
Materials > Curriculum Printing(見 §2.3 13
Materials > Craft Supplies / Copyright & Licensing 24
Food & Beverage > 全部子項目 24
Equipment > 全部子項目 24
Other > Miscellaneous 24

大類層 Form990LineId 一律 seed 為 24(保底),確保未細映的子項目仍落在 line 24。

2.3 類別清理(互斥化)

只用改名解決真正的歧義(不需搬移既有支出),把同名收斂成唯一含義:

現況 問題 解法
Food & Beverage > Consumables 與大類 Consumables 同名 改名 → "Disposable Tableware" / 一次性餐具
Materials > Printing 與大類 Printing 同名 改名 → "Curriculum Printing" / 教材印刷
Training > TravelMissions > Travel 同名但父類不同 不算衝突,維持原樣;兩者都映射到 990 line 17,報表自動合併

改名落地方式:

  • 新安裝:更新 DbSeeder 的 seed 字串。
  • 既有 DB:一次性資料 migration,依 (GroupId, 舊 Name_en) 定位後更新 Name_en/Name_zh(seed 採 insert-if-not-exists,不會自動改既有列,故需 migration)。

SubCategoryId 搬移,既有 Expense 不受影響。

2.4 新增類別(補齊 990 行覆蓋)

現有 11 大類對某些 990 行無對應(會讓那些行恆為 0 或被硬塞 line 24)。補上以下類別讓自然樹真正覆蓋常用 990 行。所有新增皆走 DbSeeder,並更新 DB_SCHEMA.md。

加進現有大類

大類 新子項目 990 行
Personnel 人事 Officer / Key Employee Compensation 主要職員薪酬 5
Personnel 人事 Retirement / Pension 退休金 8
Missions 宣教 Foreign Missions Support 國外宣教支援 3
Printing 印刷 Advertising & Promotion 廣告推廣 12

Personnel 既有 Salary & Wages 維持 line 7(一般員工);牧師等 officer/key employee 薪酬改記新子項目 → line 5。 Missions 既有 Missionary Support / Offering Transfer 視為國內/未分 → line 1;國外走新子項目 → line 3。

新增大類

新大類 子項目 990 行
Professional Services 專業服務 Legal 法律服務 11b
Accounting & Audit 會計與審計 11c
Other Professional 其他專業服務 11g
Information Technology 資訊科技 Software & Subscriptions 軟體與訂閱 14
Website & Hosting 網站與主機 14
Internet & Telecom 網路與電信 14
Finance & Banking 財務與銀行 Interest 利息支出 20
Bank & Processing Fees 銀行/金流手續費 24

大類數由 11 → 14(新增 Professional Services、Information Technology、Finance & Banking)。新大類 Form990LineId 預設仍 seed 為 24(保底),實際映射在子項目層。


3. 報表層

新服務 Form990ReportService(與 FinanceDashboardService 並列,讀取為主)。

Task<FunctionalExpenseStatementDto> GetFunctionalExpenseStatementAsync(
    DateOnly? from, DateOnly? to);
  • 支出口徑沿用 FinanceDashboardService 既有約定:Status == "Paid" || "Approved",選用 ExpenseDate 區間。
  • 對每筆支出計算 EffectiveFunctionalClassEffectiveLine,彙總成矩陣。

FunctionalExpenseStatementDto

Rows: [ { LineCode, Name_en, Name_zh,
          Program, ManagementGeneral, Fundraising, Total } ]   // 依 SortOrder
ColumnTotals: { Program, ManagementGeneral, Fundraising, GrandTotal }
UnmappedExpenseCount: int   // 落到保底 line 24 的「未明確映射」筆數,提示待補映射

UnmappedExpenseCount 讓財務知道哪些還沒細映(治理用),但金額仍計入 line 24,不漏帳。

匯出: 沿用既有報表/DevExpress 管道,輸出可交付會計師的表格(PDF/試算表)。


4. 前端(Angularadmin)

沿用既有 portal 慣例(UserPortalComponent 導覽、unified header、Kendo UI、表單版面用 Tailwind utilities、行動裝置友善 hidden md:block + md:hidden 卡片)。

  1. Part IX 報表頁:Kendo Grid 矩陣(列=990 行,欄=三功能別+Total),年度/區間篩選,雙語,行動裝置卡片版;UnmappedExpenseCount 以提示列顯示;匯出鈕。
  2. 支出表單(expense-form-dialog):新增 FunctionalClass 下拉(可空=繼承事工);Kendo DropdownList 設 [valuePrimitive]="true"
  3. 類別維護頁(expense-categories-page):每個大類/子項目可設 Form990LineId(990 行下拉)。
  4. 事工維護:DefaultFunctionalClass 下拉。

5. 測試

沿用既有測試模式(ExpenseServiceTests 等;受測元件用 inline template,Edge via CHROME_BIN,以 --include 縮限)。

  • EffectiveFunctionalClass 解析:覆寫優先、否則繼承事工、再保底 Program。
  • EffectiveLine 解析:子項目優先、否則大類、再保底 line 24。
  • 矩陣彙總:多筆跨功能別/跨行正確加總;欄合計與總計一致。
  • 保底行為:未映射子項目進 line 24 且 UnmappedExpenseCount 正確。
  • 狀態口徑:僅 Paid+Approved 計入;區間篩選正確。

6. Migration / 落地

EF Core code-first:

  1. 新表 Form990ExpenseLines
  2. 新欄 Ministries.DefaultFunctionalClassExpenses.FunctionalClassExpenseSubCategories.Form990LineIdExpenseCategoryGroups.Form990LineId(後二者建 FK)。
  3. 資料 migration:類別改名(§2.3)。
  4. DbSeeder:seed Form990ExpenseLine、子項目→行的預設映射、Ministry 預設功能別、更新改名後的 seed 字串。

DB_SCHEMA.md 同步更新(新表 + 新欄 + §8 備注)。


7. 不在此範圍(已知缺口)

  • 資本化 / 折舊(line 22):line 已 seed 但不映射;Equipment 購置暫入 line 24。需要時另開。
  • 1099 收款人追蹤:子專案 B。
  • 收入端 Part VIII:子專案 C。
  • 跨功能比例分攤:本系統維持單筆單一功能別,不支援拆分。

8. 驗收標準

  1. 可在報表頁選定年度,產出 Part IX 矩陣(三功能別 × 990 行),欄合計與總計正確,且金額等於同口徑(Paid+Approved)的支出總額。
  2. 變更某事工 DefaultFunctionalClass 或某筆 FunctionalClass 後,報表對應欄位即時反映。
  3. 未細映的子項目金額落在 line 24,且 UnmappedExpenseCount 正確顯示。
  4. 類別樹中不再有「同父同名」歧義;Training/Missions > Travel 維持並正確合併至 line 17。
  5. 既有支出資料不因本次變更而遺失或錯置。