# Offering Session — 顯示與修改主日參加人數 Date: 2026-06-24 ## Goal 在 finance/offering-session 的 **Recent Sessions** 表格中,每筆 session 顯示該日的「主日參加人數」總數,並新增一個 Action 可修改該日的參加人數。 ## Confirmed Decisions 1. **資料來源** — 沿用既有 `MealAttendance`。主日參加人數 = 該日 `AdultCount + YouthCount + KidCount`。修改 Action 改的是同一筆 `MealAttendance` 紀錄。 2. **顯示** — 後端 join,DTO 加 nullable 欄位;該日無紀錄時顯示 `—`。 3. **編輯介面** — Kendo Dialog 分別編輯三個分類(成人/青年/兒童),總數即時計算。 4. **Action 擺放** — 依既有慣例,Date 欄位可點擊觸發 View;View 與「修改主日人數」都進右鍵 context menu(沿用 expense-categories 範式)。 ## Key Constraint `AttendanceHub.SetCount` 只作用在 `ServiceDay`(本週日),無法改任意日期。因此編輯過去某場 session 的日期,**必須走新的 REST 端點**,不可用 SignalR。 ## Backend Changes ### 1. DTO `API/ROLAC.API/DTOs/Giving/OfferingSessionListItemDto.cs` - 新增 `public int? SundayAttendanceCount { get; set; }`(nullable:該日無 attendance row 為 null)。 ### 2. OfferingSessionService.GetPaged - 取得當頁 sessions 後,以這些 `SessionDate` 一次查 `MealAttendance`(單一 set-based 查詢,依日期分組求和),把總數填入各 DTO 的 `SundayAttendanceCount`;無紀錄者留 null。 - 不可對每筆 session 各發一次查詢(避免 N+1)。 ### 3. IMealAttendanceService + MealAttendanceService - 新增 `Task SetCountsAsync(DateOnly date, int adult, int youth, int kid)`。 - 沿用既有 clamp-at-zero 語意,一次寫三欄並回傳 `AttendanceCountsDto`;該日無 row 則建立(沿用 `GetOrCreateAsync` 的建立邏輯)。 ### 4. MealAttendanceController - 新增 `PUT /api/meal-attendance/{date}`,`[Authorize]`(與既有 `GetRange` 一致),body `{ adult, youth, kid }` → 回傳 `AttendanceCountsDto`。 - **Optional(plan 階段決定)**:若 `date == ServiceDay`,順手透過 `AttendanceHub` 廣播 `ReceiveCounts`,讓正在跑的即時計數器同步。預設先不做,避免增加耦合。 ## Frontend Changes ### 1. Model `APP/.../giving/models/giving.model.ts` - `OfferingSessionListItemDto` 加 `sundayAttendanceCount?: number | null`。 ### 2. Attendance API service `APP/.../meal-attendance/services/meal-attendance-api.service.ts` - 新增 `setCounts(date: string, counts: { adult: number; youth: number; kid: number }): Observable` → 呼叫 `PUT /api/meal-attendance/{date}`。 ### 3. offering-session-page component `APP/.../giving/pages/offering-session-page/offering-session-page.component.{ts,html}` - imports 加 `ContextMenuModule`、`DialogsModule`、`InputsModule`(NumericTextBox)。 - Recent Sessions grid: - 在 "Lines" 後新增欄 `Attendance · 主日人數`,顯示 `s.sundayAttendanceCount ?? '—'`。 - 移除原本獨立的 "View" 按鈕欄;改為 `(cellClick)`:`event.type === 'contextmenu'` → 開 context menu;否則 `openView`。Date 欄加可點擊樣式(`clickable-rows`)。 - `kendo-contextmenu`,items:`View`、`修改主日人數`。 - 修改主日人數 Dialog: - 開啟時以 `mealAttendanceApi.getRange(date, date)` 取該日 breakdown 預填(無紀錄則三欄為 0)。 - 三個 `kendo-numerictextbox`(成人/青年/兒童,min 0),即時顯示總數。 - Save → `setCounts(date, …)`;成功後就地把該列 `sundayAttendanceCount` 更新為三者之和。Cancel 關閉。 ## Permissions PUT 端點用 `[Authorize]`,與既有 `GetRange` 一致。 ## Out of Scope - Recent Sessions grid 目前尚無手機卡片版;本次只新增欄位與 action,不一併重構 mobile 版面(可另開 task)。 ## Testing - Backend:`SetCountsAsync` 對負值 clamp 為 0、該日無 row 時建立;`GetPaged` 正確帶入 attendance 總數且無 N+1。 - Frontend:dialog 總數計算(成人+青年+兒童)與存檔後就地更新該列。(前端測試環境較脆弱,採最小範圍 inline-template 測試。)