# 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<AttendanceCountsDto> 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<AttendanceCounts>` → 呼叫 `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 測試。）
