# CR-036　8-6 之「繳款截止日」精確化——改以**帳單出帳事件**校正

| 項目 | 內容 |
|---|---|
| 文件編號 | CR-036 |
| 版本 | **1.0** |
| 狀態 | ✅ **已裁定並執行完畢** |
| 提出日 | 2026-10-02 |
| 提出者 | WorkBuddy AI（開發團隊） |
| 緣起 | BEAM PMO：「**「精確的繳款截止日」<- 是的，麻煩您直接執行！**」 |
| 前案 | `CR-035 §2.4`（8-6／8-7）——該處之迄日為**推算值**（`DERIVED`），本 CR 收斂之 |
| 關聯 | `BRD-001 §5.4 BR-04-02`、`BRD-004 POC-8 8-6`、`SAD-002 §3.2`、`SAD-004 §5.2／§5.3`、`SCS-002 §7.12`、`SCS-011` |
| 嚴重度 | 🟡 中——影響「調高至**繳款截止日** + 7 日」之**正確性**（推算值可能與實際截止日**相差近一個帳單週期**） |

---

## 1. 問題：推算值為何不精確

`CR-035 §2.4` 之 8-6 於**請款入帳**時觸發，迄日以「入帳日 ＋ 帳單週期(25) ＋ 7 日」**推算**。

**誤差來源**：真正的繳款截止日 ＝「**該筆稅款所屬帳單**之帳單日 ＋ 25 天」。
而稅款落在**哪一期**帳單，取決於「入帳日**是否已過**該歸戶之帳單日」——
推算式一律以「入帳日」當作帳單日 → **最長可高估近一個帳單週期**（若入帳日恰在帳單日之後）。

**為何不能直接算**：`ACCOUNTS.STATEMENT_DAY`（帳單日）在 `CC_PARTY`，
而 `limit-service` 之 schema 為 `CC_LIM` —— **跨 schema 讀取為架構級禁止**（`SAD-003`／`SQL-07`）。

---

## 2. ★★ 關鍵洞察：精確值**在原理上**只有帳單出帳後才存在

在帳單出帳之前，「該筆稅款屬於哪一期帳單」**尚未確定**——故**任何**在請款當下算出的值都是猜測。

→ **正解不是「算得更準」，而是「在精確值出現時校正」**。

---

## 3. 裁定：兩階段（調高 → 校正）

| 階段 | 觸發 | 動作 | `due_date_source` |
|---|---|---|---|
| **① 調高** | `posting.transaction.posted.v1`（帶 `authorizationId`） | `temporary_amount += 金額`；`raise_until` 以推算值**先給** | `DERIVED` |
| **② 校正** | **`statement.statement.issued.v1`** | 以帳單之**權威 `paymentDueDate`** 覆寫 `raise_until = paymentDueDate + 7`；並同步 `account_limits.temp_end_date` | **`STATEMENT`** |

### 3.1 ★ 校正之歸屬條件（避免套錯期別）

> **只有「在該筆調高**之後**才出帳」的帳單，才是承載該筆稅款的帳單。**

故校正條件為：`status='OPEN'` **且** `due_date_source='DERIVED'` **且** `raised_on ≤ 帳單出帳日`。
- 若帳單在調高**之前**已出帳（`raised_on > 出帳日`）→ **略過**（該期不承載此筆；下一期才是）。
- 校正後標記 `STATEMENT` → **不重複校正**（冪等）。

**「帳單出帳日」之來源**：事件信封之 **`occurredAt`**（該事件即「出帳」之事實）——
**不需**由 `paymentDueDate − 25` 反推（那會把 25 天慣例**硬編**進程式）。

### 3.2 契約：**無需變更**

`statement.statement.issued.v1` 之 payload **早已包含**所需欄位
（`statementId`／`accountId`／`statementMonth`／`totalAmount`／`minimumDue`／**`paymentDueDate`**／`currency`）
——**不需**為本 CR 增欄，亦**不升版**。

### 3.3 `temp_end_date` 之同步

校正後，`account_limits.temp_end_date` ＝ 該歸戶**所有 OPEN 串動紀錄**之 `raise_until` **最大值**
（**取最晚者**：不得因校正某一筆而**縮短**另一筆之窗口）。

---

## 4. 架構變更：新增一組事件訂閱

| 文件 | 變更 |
|---|---|
| `SAD-004 §5.2` | `statement.statement.issued.v1` 之消費者 **`notification` → `notification` ＋ `limit`** |
| `SAD-002 §3.2` | `limit-service` 之「事件（訂閱）」補 `statement.statement.issued.v1` |

**理由**：8-6 之迄日語意即「**帳單之繳款截止日** ＋ 7 日」——
帳單為 `statement-service` 之**權威**，`limit-service` 作為消費者**維護其所需之最小事實**
（僅取 `accountId`／`paymentDueDate`／`occurredAt`），符合 `SAD-003` CQRS 讀側原則，
**不新增任何同步呼叫、不開放任何 ACL**。

---

## 5. 實作範圍

1. `SpecialLimitTempAdjustmentRepository`：新增 `findOpenDerivedRaisedOnOrBefore(accountId, date)`
   與 `applyDueDateCorrection(id, until)`（**只改 `raise_until`／`due_date_source`**，
   不動 `raised_amount`／`recovered_amount`）。
2. `LimitRepository`：新增 `updateTemporaryEnd(accountId, currency, until)`。
3. `SpecialLimitTempAdjustmentService#correctDueDate(accountId, statementIssuedOn, paymentDueDate)`。
4. `StatementIssuedListener`（新 Kafka 消費者，主題 `cc.statement.statement.issued`）。
5. 規格：`SCS-002 §7.13`；`SAD-004 §5.2`；`SAD-002 §3.2`。
6. 測試與真機驗證。

**不做**：`limit-service` 不快取「最近一期帳單」之狀態——本校正為**事件驅動之單次覆寫**，
不需維護讀模型（**無新表**）。

---

## 6. 已知限制（誠實揭露）

1. **若該歸戶長期不出帳**（如停用），`raise_until` 將**停留在推算值**（`DERIVED`）——
   可由 `due_date_source` 辨識並人工處理。
2. **8-7 之回收不動日期**：回收只調降 `temporary_amount`；`temp_end_date` 於全部回收後
   可能殘留（不影響金額，僅影響窗口語意）——已列 `SCS-002 §10`。
3. **多幣別**：校正以串動紀錄自身之 `currency` 為準（同 8-7 之作法）；
   繳款／帳單事件之 `currency` 若為 `null` 亦不影響（**不臆測**）。
4. **僅「首次出帳」校正**：若同一筆因故跨兩期帳單，本設計取**第一期**（`raised_on` 之後的
   第一份帳單）——與「稅款落在第一期帳單」之會計慣例一致。

---

## 7. 修訂記錄

| 版本 | 日期 | 修訂內容 |
|---|---|---|
| 1.0 | 2026-10-02 | 初版：診斷推算值之誤差來源與「跨 schema 不可讀」之限制；★ **關鍵洞察**：精確值在原理上只有帳單出帳後才存在 → 裁定採**兩階段（調高 → 校正）**；訂定**校正歸屬條件**（`raised_on ≤ 出帳日`）；確認**契約無需變更**；載明架構變更（`SAD-004 §5.2` 新增 `limit` 為 `statement.issued.v1` 之消費者）；實作範圍與四項已知限制 |
