# CR-055　共享池（共用額度）／預借現金／企業子額度——**語意凍結與計算式**

| 項目 | 內容 |
|---|---|
| 文件編號 | CR-055 |
| 版本 | **1.3** |
| 狀態 | **口徑已凍結**；**P4-1／P4-2／P4-3 全數落地並真機驗證**（§8／§10／§11）；**§6 維度已由 BEAM PMO 確認**（自然人帳戶級／公司戶歸戶級／不混算）；身分證 360 見 `CR-056` |
| 觸發 | BEAM PMO 2026-10-17 回覆 `CR-054 §9` Q-A～Q-E |
| 需求依據 | RFP `FR.md §2.4(1)`「歸戶、持卡人、卡片調額／查詢」；`CR-052` C-1(ii)／C-4；`CR-053` 維度模型 |
| 影響模組 | `limit-service`（檢核與查詢）、`authorization-service`（授權呼叫）、商品參數（`product-service`） |
| 關聯 | `CR-052`／`CR-053`／`CR-054` |

---

## 1. 口徑原文（BEAM PMO 2026-10-17，逐字保留要點）

> 「共享池的觀念是『**總額度的另一面說法－共用額度**』。如果某自然人/公司戶在某銀行的
> **總額度是 5 萬**，這個人在這家銀行有**三張卡**，一張額度 5 萬、一張 2 萬、另一張 3 萬，
> 他的**共用額度仍然是 5 萬**，而不是 5+2+3=10 萬！
>
> 對卡片級而言，若當初的額度 2 萬，**超刷的部分就走共享池**，但**共享池的上限就是 5 萬**，
> **就算其他兩張都沒刷，這一張兩萬額度的最多也只能刷到 5 萬**！
>
> 還有，**共享池是個參數**，2 萬那張卡**若不能走共享池，那他的上限就是當初核定的 2 萬**！」
>
> 「A-3：**會！**（一般消費走卡片額度、預借現金走共享池）——因為**共享池的觀念不在消費類型，
> 而是該卡能否享有共享池的權利**！」
>
> 「B：共享池是個參數，只有能夠適用共享池的商品，才會在他可以消費的類別裡面『消費』。
> 其**卡片級的『可用』仍是他原本的額度**。」
>
> 「C：預借現金是**總額度的某個百分比**所產生的可預借現金額度。**不是獨立額度**——
> 總額度 5 萬、評等 80% → 4 萬是預借現金上限，**總額度仍為 5 萬**，不是 5+4=9 萬！」
>
> 「D：企業子額度是**集團型總公司的總額度**，各分公司、部門、分公司總經理、高階主管、
> 採購單位可以有個別的子額度。D-2：**當然要**（子額度佔用扣減上層）。」
>
> 「E：**同意**（60 秒容忍、300 秒告警）。」

---

## 2. 精確語意模型（本文件為權威）

### 2.1 三個不同的「額度」概念（★ 不可混用）

| 概念 | 定義 | 現行承載 |
|---|---|---|
| **總額度**（shared pool 之上限） | 該客戶在本行之**共用上限**；**不是**各卡額度之和 | `account_limits` 之 `ACCOUNT` 維度（`permanent + temporary + shadow`） |
| **卡片名目額度** | 核卡時核給該卡的金額；**僅為該卡自身可用之上限**（不享共享池時） | `account_limits` 之 `CARD` 維度（`V11`） |
| **共享池權利** | 該卡**是否可動用總額度**（**參數**，商品級） | ★ 本 CR 新增：`account_limits.shared_pool_eligible`（卡片維度快照） |

> ★★ **關鍵**：共享池**不是額外池**，而是「**允許該卡用到總額度**」的權利。
> 因此 `總額度 ≠ Σ卡片額度`，且「享共享池」的卡其上限**不會**變成「卡額度 ＋ 總額度」。

### 2.2 可刷上限（授權檢核用，**風控底線**）

```
若 該卡享共享池（shared_pool_eligible = true）：
      可刷上限 = 總額度 − 帳戶已用 − 帳戶全部未釋放佔用        ← 即「帳戶可用」
否則：
      可刷上限 = 卡片名目額度 − 該卡未釋放佔用                 ← 卡額度硬上限
```

★ 兩者皆**下限 0**。授權時檢核「本次金額 ≤ 可刷上限」。

### 2.3 卡片級「可用」的呈現（查詢用）

BEAM PMO 明示：「其**卡片級的『可用』仍是他原本的額度**」——故查詢須**分開呈現**：

| 欄位 | 語意 |
|---|---|
| `availableAmount` | **卡片自身可用**＝卡片名目額度 − 該卡佔用（**不扣帳戶已用**）← 這是「原本的額度」 |
| `sharedPoolEligible` | 是否享共享池（參數快照） |
| `sharedPoolAvailableAmount` | **可動用總額度之剩餘**＝帳戶可用（僅 `eligible=true` 時有值；否則 `null`） |

> ★ 一個數字不足以表達：`availableAmount` 是「這張卡自己的錢」，
> `sharedPoolAvailableAmount` 是「這張卡還能向共用池借多少」。兩者**不可相加**。

### 2.4 BEAM PMO 之 5 萬例子——**逐情境驗算**

設定：總額度 5 萬；三張卡名目 5／2／3 萬；帳戶已用 0、佔用 0。

| # | 情境 | 可刷上限（正解） | 現行系統 | 判定 |
|---|---|---|---|---|
| ① | 2 萬卡（**享**共享池）首次刷卡 | **5 萬**（總額度，不是 2+5=7） | 5 萬（看帳戶可用） | ✅ 一致 |
| ② | 同上，其他兩卡已刷 4 萬 | **1 萬**（5−4） | 1 萬 | ✅ 一致 |
| ③ | 2 萬卡（**不享**共享池）刷卡 | **2 萬**（卡額度硬上限） | **5 萬** | ❌ **超放 3 萬** |
| ④ | 同上，該卡已佔用 1.5 萬 | **0.5 萬** | 5 萬 | ❌ **超放** |

> ★★ 情境 ③／④ 即本 CR 要修的**真實缺陷**：現行 `hold()` 只檢核帳戶可用、
> **完全不看卡片名目額度**（`CARD` 維度僅供查詢）。→ 不享共享池的卡會用到總額度。

### 2.5 預借現金（P4-2）

```
預借現金額度上限 = 總額度 × ratio          （ratio 由商品／信用評等參數決定，例 80%）
預借現金可用     = min(預借上限 − 已預借金額, 帳戶可用)
```

★ **不是**獨立額度：總額度 5 萬、ratio 80% → 預借上限 4 萬，**總額度仍 5 萬**（非 9 萬）。
★ 與一般消費**共用**同一總額度（故 `已預借` 亦計入帳戶已用／佔用）。

### 2.6 企業子額度（P4-3）

* 上層：**集團總公司總額度**（`CORP_SUB` 之上層，對應 `subject_type=CORP_SUB` 之根）。
* 下層：分公司／部門／分公司總經理／高階主管／採購單位——各為一個 `CORP_SUB` 子主體。
* **子額度佔用扣減上層**（BEAM PMO 明示「當然要」）→ 上層可用須扣除所有下層佔用之和。
* ⚠️ 需要**組織樹**（誰是誰的上層）——現行**無**此資料結構 → P4-3 需另立資料模型。

---

## 3. 與現行模型之落差（實查）

| # | 落差 | 現況 | 影響 |
|---|---|---|---|
| **G-1** | **授權不看卡片額度** | `hold()` 僅檢核帳戶可用；`CARD` 維度只供查詢（`#A-285`） | ★★ **不享共享池的卡會超放**（§2.4 情境③／④） |
| **G-2** | 無「共享池權利」欄位 | 無 | 無法區分「享／不享」 |
| **G-3** | 查詢未分開呈現兩種可用 | `availableAmount` 為「卡額度 − 帳戶已用 − 卡佔用」（保守下界） | 與 BEAM PMO 語意不符（他要「卡額度 − 卡佔用」） |
| **G-4** | 無預借現金額度 | 無資料模型與參數 | P4-2 |
| **G-5** | 無企業組織樹 | `CORP_SUB` 僅值域預留 | P4-3 |
| **G-6** | 「總額度」維度待確認 | 現行 `ACCOUNT` 維度；BEAM PMO 稱「自然人在本行總額度」 | 見 §6 待確認 |

---

## 4. 參數設計（BEAM PMO：**商品（產品級）參數**）

| 參數鍵（`namespace=product`） | 值域 | 說明 |
|---|---|---|
| `limit.shared-pool-eligible` | `true`／`false` | 該商品之卡是否**可動用總額度**；**缺值 fail-closed**（不臆造） |
| `limit.cash-advance-ratio` | 0–100（%） | 預借現金額度佔**總額度**之比例（P4-2） |

★ **參數 → 卡片快照**（留痕原則）：設定卡片額度時，由呼叫端依商品參數帶入
`sharedPoolEligible` 並寫入 `CARD` 維度列；事後改參數**不改變**已設定之卡片語意。
⚠️ `limit-service` **無** `card→product` 對映（`CR-052 C-5`）→ 無法自行查商品參數，
故由呼叫端（BFF／card-service，知道 `product_code`）帶入。

---

## 5. 分批

| 批次 | 內容 | 狀態 |
|---|---|---|
| **P4-1** | 共享池權利欄位＋**授權卡片上限檢核**＋查詢分開呈現 | ✅ 本批落地（`V13`） |
| **P4-2** | 預借現金：`cash-advance-ratio` 參數＋額度計算＋查詢輸出（C-4） | ✅ 已落地（`V15`／`V16`；§10）。★ 授權端尚未送 `CASH_ADVANCE`（§10.5） |
| **P4-3** | 企業子額度：組織樹資料模型＋上層扣減 | ✅ 已落地（`V17`；§11）。★ 覆核流程與卡片綁定未接（§11.5） |

---

## 6. 維度確認（BEAM PMO 2026-10-17 已裁定，**本節為權威**）

| 客戶型態 | 總額度維度 | 本批實作 |
|---|---|---|
| **自然人** | **帳戶級**（同一人在本行該帳戶之總額度；不論幾張主卡／附卡） | ✅ **已相符**（`ACCOUNT` 維度） |
| **公司戶** | **歸戶級**（集團總公司之總額度；分公司／部門／主管／採購單位為子額度） | ⏳ P4-3（需組織樹） |

### 6.1 ★★ 個人與公司額度**不混算**（BEAM PMO 2026-10-17 例子）

> 張三在 A 銀行個人帳戶總額度 **5 萬**；Z 公司在 A 銀行公司戶總額度 **100 萬**，
> 張三持 Z 的差旅卡 10 萬、採購卡 30 萬。
> **張三在 A 銀行的帳戶總額度仍是 5 萬**——個人與公司**各自獨立**。

**落地含義**：

* 個人卡之共享池上限＝**個人帳戶**可用（不因持有公司卡而提高）。
* 公司卡之共享池上限＝**公司戶**（歸戶）可用。
* 同一自然人跨二者時，**兩邊額度不得相加、不得互相扣減**。

### 6.2 但「以人為視角」要查得到（`CR-056`）

BEAM PMO 同時要求：**由身分證字號查詢時，要能帶出張三在 Z 公司的兩張卡**。

★ 這與「額度不混算」**不衝突**：額度分開算，但**查詢視角**要能跨帳戶彙整。
→ 已開 **`CR-056`**。盤點揭露硬缺口：`parties` 綁 `account_id`（同一人跨帳戶為不同 party 記錄）、
身分證僅有**加密欄**與**遮罩欄**（遮罩不可作比較鍵）→ **無 `id_no_hash` 則跨帳戶 360 查詢不可行**。
第一步（`GET /api/v1/cards?accountId|partyId`）已落地；`id_no_hash` 屬**資安決策**，待 BEAM PMO 裁定（`CR-056 §4`）。

### 6.3 採購卡之「限資產類消費」

BEAM PMO 例中採購卡「只能限制在資產類的消費」＝**用途限制（MCC 白名單）**。
現行可由 `special_limits.scope_ref` 承載（`CR-035 §2.1`），但該表為**帳戶層**；
若客戶要求**卡片層**用途限制，需另立資料模型（本 CR 不動，記錄待辦）。

---

## 8. P4-1 執行結果（2026-10-17，`#A-289`）

| 項目 | 落地 | 真機證據 |
|---|---|---|
| 共享池權利欄位 | `V13`：`account_limits.shared_pool_eligible`（`DEFAULT 1` 向後相容） | 真 Oracle 實查欄位存在 |
| 調額單留痕 | `V14`：`limit_adjustments.shared_pool_eligible`（可空；僅 CARD 維度） | 同上 |
| **授權卡片上限檢核** | `hold()`：不享共享池 → 檢查「卡額度 − 卡佔用」；並與帳戶可用**取較嚴者** | ★ 卡額度 200,000、帳戶 10,000,000：刷 150,000 **通過**；再刷 100,000 **拒絕**（`CC-LIM-1001`，訊息含「不享共享池…卡可用=50000」） |
| 查詢分開呈現 | `availableAmount`＝卡額度−卡佔用；`sharedPoolEligible`；`sharedPoolAvailableAmount`（僅 eligible 有值） | 不享共享池：`available=50,000`、`pool=null`；享共享池：`available=100,000`、`pool=9,650,000`（＝10,000,000−200,000−150,000） |
| 參數 → 快照 | 調額請求帶 `sharedPoolEligible`，核准/生效時落地 CARD 列 | 設定 `false` 後查詢回 `false` |

**★ 修正之真實缺陷（本批最大價值）**：原 `hold()` **完全不看卡片名目額度**——不享共享池的卡
可一路刷到總額度（例：總額度 5 萬、卡額度 2 萬 → 可刷 5 萬＝**超放 3 萬**）。現已擋下。

**★ 實作期發現之第二個缺陷（已修，勿重犯）**：初版於**申請時**即建立 CARD 列（為寫入權利），
但若該申請**待覆核**，卡片會出現「名目額度 0」之列 → `hold()` 以「卡可用 0」**拒絕該卡所有交易**。
→ 已改為「權利記在調額單、**核准/直接生效時**才建列」；並以既有測試
（`cardAdjustmentOverThresholdRequiresApprovalAndKeepsDimension` 斷言「待覆核期間不得先建列」）鎖住。

**★ 測試**：`limit-service` **202 測 0 失敗**（新增 4 例：不享共享池被拒／享共享池可動用總額度／
無 CARD 列向後相容／卡額度與帳戶可用取較嚴）；契約 **112 端點／0 錯誤**、`verify_contract_dto` 不符 0、
`validate_sql` 違規 0、三掃描器 0、Mermaid 0。

**★ 仍未做（誠實揭露）**：
* **P4-2 預借現金**：需「已預借」來源與 `cash-advance-ratio` 參數（比率基數為**總額度**）。
* **P4-3 企業子額度**：需組織樹資料模型（現行無）。
* **§6 之待確認**：「總額度」維度為帳戶級或歸戶/當事人級——本批採**帳戶級**（保守：不會超放，只會少給共享額度）。

---

## 10. P4-2 預借現金（2026-10-17，`#A-296`）

BEAM PMO 指示「P4-2 預借現金…請執行」。**語意完全依 §2.5 凍結值實作，未另行解釋**：

```
預借現金額度上限 = 總額度 × ratio / 100          （向下取整）
預借現金可用     = min(預借上限 − 已預借金額, 有效可用)
```

### 10.1 落地

| 項目 | 內容 |
|---|---|
| `V15` | `account_limits.cash_advance_ratio NUMBER(3)`（可空、`CHECK 0–100`）＋`limit_adjustments.cash_advance_ratio`（同，供調額單留痕） |
| `V16` | **擴充 `limit_holds.hold_type` 值域**加入 `CASH_ADVANCE`（見 §10.3 之真實缺口） |
| 領域 | `AccountLimit.cashAdvanceLimit(today)`＝`totalLimit × ratio / 100`；`cashAdvanceConfigured()`；比例 0–100 於**記憶體建構亦檢核**（不靠 DDL 唯一防線） |
| 查詢 | `GET /instruments/{id}/limits` 增 `cashAdvanceRatio`／`cashAdvanceLimitAmount`／`cashAdvanceUsedAmount`／`cashAdvanceAvailableAmount` |
| 授權 | `hold(holdType='CASH_ADVANCE')` 先檢**預借子上限**，再走既有「帳戶可用」檢核（兩者皆須通過） |
| 參數 → 快照 | 調額請求帶 `cashAdvanceRatio`，於**核准/直接生效**時落地 CARD 列（比照 `V14`，**申請時不建列**） |

### 10.2 三個刻意的語意決定

| 決定 | 理由 |
|---|---|
| **`null` ≠ `0`** | `null`＝**未設定** → 預借**一律拒絕**（fail-closed，不臆造 80% 這種「舉例值」）；`0`＝**明確禁止**（上限 0，訊息走「額度不足」）。兩者必須可區分，否則「還沒設」與「不准用」會混為一談 |
| **「有效可用」取較嚴者** | 享共享池 → 帳戶可用；不享共享池 → **卡片自身可用**。若一律用帳戶可用，不享共享池的卡就能用預借繞過卡片硬上限（＝`§3 G-1` 的缺陷以另一種形式復活） |
| **預借佔用進同一張 `limit_holds`** | `CR-052 Q-C C-3` 建議「共用，以 `hold_type` 區分」——避免兩套佔用表導致總額度被重複使用 |

### 10.3 ★★ 實作期攔下的真實缺口（值域未涵蓋）

`V1` 基線之 `ck_holds_hold_type` 值域為 `('GENERAL','SPECIAL','INSTALLMENT')`，
**沒有** `CASH_ADVANCE` → 真機測試第一次執行即 `ORA-02290`（違反檢查約束）。
**這不是測試寫錯，是語意與 DDL 不一致**：§2.5 要求「已預借」可被識別，而唯一可識別的位置就是 `hold_type`。

**修法**：`V16` 動態定位該**系統命名**約束（`SYS_C…` 名稱隨環境不同，不可寫死）後卸除，
再以**具名** `ck_holds_hold_type` 重建。**不改成「無 CHECK」**——值域即行為契約，
放寬為任意字串會讓打錯字靜默變成新型別。

### 10.4 真機驗證（Testcontainers 真 Oracle ＋真 Flyway）

| 情境 | 結果 |
|---|---|
| 未設定比例 → 預借 | **拒絕**，訊息「預借現金未開放（該卡未設定預借現金比例，fail-closed）」；查詢四欄皆 `null` |
| 卡額度 200,000、ratio 80% | 上限 **160,000**（**不是** 360,000）；預借 100,000 後：已預借 100,000、可用 **60,000** |
| ★ 預借佔用是否計入帳戶佔用 | **是**：帳戶 `holdAmount=100,000`、`availableAmount=900,000`（與一般消費共用總額度） |
| 帳戶可用 1,000,000 但預借上限 160,000 → 預借 170,000 | **拒絕**（錯誤訊息帶上限 160000，供客服判讀） |
| ratio `0` | 上限 0、可用 0；預借 1,000 → 拒絕（走「額度不足」而非「未開放」） |
| 不享共享池（卡 200,000／ratio 20%） | 上限 40,000；卡可用 100,000 → 可用取 **40,000**；預借 50,000 → 拒絕（**卡額度充足仍被預借上限擋下**） |
| 不享共享池且卡可用 30,000（< 上限 40,000） | 可用取 **30,000**（較嚴者） |
| 調額單帶 `cashAdvanceRatio=60` | 生效後 CARD 列 `ratio=60`、上限 120,000（200,000×60%） |

**★ 測試**：`CashAdvanceLimitIntegrationTest` **7 例全通過**（真 Oracle、零替身）。

### 10.5 誠實揭露

| 項 | 說明 |
|---|---|
| **`CASH_ADVANCE` 之判定靠呼叫端** | 本服務不判斷「這筆是不是預借現金」——`holdType` 由授權端（`authorization-service`）依交易類型帶入。若上游帶錯，檢核即失效。**目前授權端尚未帶此值**（見下） |
| **授權端尚未送 `holdType=CASH_ADVANCE`** | `authorization-service` 之授權流程目前一律送 `GENERAL`（交易類型 → 預借現金之對映屬其職責）。→ 本批完成的是**限額能力**，**尚未**在授權路徑生效。列為後續（需 `CR` 補 `authorization-service` 之交易類型對映） |
| 預借比率來源 | 由呼叫端依商品參數 `limit.cash-advance-ratio` 帶入（本服務無 card→product 對映，`CR-052 C-5`）；**商品參數本身尚未建立**（`product-service` 之參數清單未含此鍵） |

---

## 11. P4-3 企業子額度（2026-10-17，`#A-296`）

BEAM PMO 指示「P4-3 企業子額度（需組織樹）…請執行」。**語意依 §2.6 凍結值**：

```
上層可用 = 上層總額度 − 上層已用 − 上層自身佔用 − **所有下層佔用之和**（下限 0）
```

### 11.1 資料模型（`V17`）

| 表／欄 | 內容 |
|---|---|
| `corp_units` | `id`／`unit_no`（唯一）／`unit_name`／`parent_unit_id`（根為 `NULL`）／`root_unit_id`（反正規化）／`depth`／`settlement_account_id`／`status` |
| `limit_holds.corp_unit_id` | 佔用歸屬企業單位（可空）；與 `instrument_id` **並存不互斥** |
| 額度列 | `account_limits` 之 `subject_type='CORP_SUB'`、`subject_id = corp_units.id` |

**★ 為何要獨立一張表，不在 `account_limits` 上硬加 `parent_id`**：
① 組織樹是**主體結構**、額度是**掛在結構上的數字**，生命週期不同（單位可先建、額度後核）；
② 同一單位未來可能有多幣別額度列（`account_limits` 唯一鍵含 `currency`），
樹只能有一份，否則多幣別會出現「多棵不一致的樹」。

**★ `settlement_account_id`（額度歸屬帳戶）為何必要**：`account_limits.account_id` 為 `NOT NULL`，
`CORP_SUB` 列亦須有歸屬帳戶。企業額度在實務上歸屬**企業戶結算帳戶** → 由呼叫端於建立根單位時提供，
子單位**繼承**；**不由系統臆造**（`CR-053 D-4` 同類原則）。

**★ 單位不得實體刪除**：有歷史額度與佔用 → 僅能以 `status='INACTIVE'` 停用（本版尚未提供停用端點）。

### 11.2 端點

| 端點 | 說明 |
|---|---|
| `POST /api/v1/corp-units` | 建根／子單位（`unitNo` 冪等；已存在回既有、**不覆寫**） |
| `GET /api/v1/corp-units/{unitId}/limit` | 查額度：**同時回「自身佔用」與「下層佔用」**——只回一個總數會讓客服無法解釋「上層為何可用變少」 |
| `PUT /api/v1/corp-units/{unitId}/limit` | 設定總額度（**風控核定**端點） |
| `POST /api/v1/corp-units/{unitId}/holds` | 建立佔用（**檢核走上層鏈**） |

### 11.3 三個容易做錯的點（本批刻意處理）

| 陷阱 | 後果 | 本批做法 |
|---|---|---|
| 只檢核**自身**可用 | N 個子單位各自把子額度花光 → 上層被**超放 N 倍** | 檢核走**上層鏈**（自身→父→…→根），任一層不足即拒絕 |
| 把「自身佔用」算進「下層之和」 | 同一筆佔用被扣**兩次** → 可用被低估一倍 | 兩個查詢分開（`corpUnitHolds` vs `descendantCorpHolds`），下層**不含自身** |
| 遞迴方向寫反 | `CONNECT BY` 回**空集合且不報錯** → 靜默低估下層佔用 | 以真機整合測試鎖住（三層樹，根＝750,000） |

### 11.4 真機驗證（Testcontainers 真 Oracle ＋真遞迴 SQL）

三層樹：根 1,000,000 → 分公司 300,000 → 部門 100,000

| 動作 | 驗證 |
|---|---|
| 分公司佔用 200,000、部門佔用 50,000 | 分公司：自身 200,000、下層 50,000、**可用 50,000** |
| 根查詢 | 自身 0、**下層 250,000**、**可用 750,000**（遞迴兩層都算到） |
| 葉節點（部門） | 下層 0、可用 50,000 |
| 根 100,000／分公司 80,000／部門 50,000；分公司花光 80,000 後部門再借 30,000 | **拒絕**，訊息指出**不足之層＝B-003（分公司）**——否則維運不知往上要調誰 |
| 子單位未帶額度 | 列存在但 0 元 → 佔用**拒絕**（不當 0 元放行、也不當無限大） |
| 重複 `unitNo` 建立 | 回既有、名稱與額度**皆不覆寫** |

**★ 測試**：`CorpLimitIntegrationTest` **6 例全通過**。

### 11.5 誠實揭露

| 項 | 說明 |
|---|---|
| **企業額度之「申請→覆核」未接** | `PUT /corp-units/{id}/limit` 是**核定端點**（風控用），非客戶申請路徑。`LimitAdjustmentService` 目前僅由 `instrumentId` 判斷 CARD 維度，**尚未**支援 `CORP_SUB` 之覆核流程 |
| **不發射 `limit.hold.created.v1`** | 該事件消費端（授權投影）語意為**卡片授權佔用**，payload 無企業單位維度；照發會讓投影出現「無載體的佔用」→ 污染卡片級可用額度。故企業額度佔用**不進**事件流，以本服務即時查詢為權威 |
| **停用端點未做** | `status='INACTIVE'` 之值域已在 DDL，但無 API；停用後既有佔用如何處置（釋放？保留？）需客戶口徑 |
| **未與卡片綁定** | 企業卡消費目前**不會**自動產生 `corp_unit_id` 之佔用（需 `authorization-service` 帶企業單位識別）；本批完成的是**額度模型與檢核能力** |


---

## 12. 授權路徑生效（2026-10-18，`#A-297`）

BEAM PMO 指示「授權路徑還沒生效 ← 請完成」。§10.5／§11.5 揭露的兩個缺口，本批補上。

### 12.1 缺口一：授權端一律送 `GENERAL`（P4-2 的限額能力從未觸發）

| 項目 | 內容 |
|---|---|
| 問題 | `LimitPort.hold(...)` **不帶型態**，`RemoteLimitPort` 固定用 `HoldRequest.general` → `hold_type` 永遠 `GENERAL` → `CASH_ADVANCE` 的子上限檢核**一次都沒跑過** |
| 修法 | `LimitPort.hold` 增 `holdType`（與 `instrumentId`）；`AuthorizeUseCase` 以 `txType` 對映：`CASH_ADVANCE`→`CASH_ADVANCE`、`INSTALLMENT`→`INSTALLMENT`、**其餘（含未知）→`GENERAL`** |
| ★ 未知一律 GENERAL 之意義 | **不是放行**：金額檢核（卡片上限／帳戶可用／預借上限）與型態無關，一律執行；型態只決定「要不要多一道子上限」 |
| 測試 | `AuthorizeUseCaseTest` 新增 2 例（`CASH_ADVANCE`→`CASH_ADVANCE`；`PURCHASE`→`GENERAL`），並斷言 `instrumentId` 確實傳遞 |

### 12.2 ★ 順帶修掉一個更嚴重的缺口：`instrumentId` 從未傳給額度服務

`LimitPort.hold(...)` 原本**連 `instrumentId` 都沒有** → `limit_holds.instrument_id` **永遠為空**。
這正是 `CR-054 §3.3` 量到「34/34 筆佔用無載體」的真正原因之一（不只是「歷史資料」）——
**新交易也不會帶**。後果：卡片級已用／可用只能取下界，`holdAttribution` 永遠 `PARTIAL`。

本批一併補上：授權帶 `card.instrumentId()` → 真機實查佔用列 `INSTRUMENT_ID` 有值。

### 12.3 缺口二：企業卡消費不帶企業單位（P4-3 在授權上等於沒生效）

| 項目 | 內容 |
|---|---|
| 設計選擇 | **由 `limit-service` 自行解析**（`V19` `corp_unit_instruments`），**不**要求授權服務帶 `corpUnitId` |
| 理由 | 授權服務**沒有**（也不該有）企業組織資料；若要求它帶，就得在授權服務複製組織樹或每次多打一次同步查詢。放在額度域：授權只要照常帶 `instrumentId` → **授權服務零改動、零額外往返** |
| 端點 | `POST /api/v1/corp-units/{unitId}/instruments`（一卡一單位；已綁其他單位回 `CC-LIM-1003`，**不靜默改綁**） |
| 佔用行為 | `hold()` 解析所屬單位 → 先跑**上層鏈檢核**（任一層不足即拒絕）→ 佔用列同時寫 `instrument_id` 與 `corp_unit_id` |

### 12.4 真機驗證（`#A-297`；真 Oracle ＋ 真 Kafka ＋ 真 Keycloak）

| 步驟 | 結果 |
|---|---|
| 發卡（8082） | `cardId=0dbe10fb…`、`411111******0335` |
| Kafka `cc.card.card.issued` → limit-service | 自動建立 CARD **權利列**（`PRODUCT_DEFAULT`、金額 0、`shared_pool_eligible=1`） |
| 查額度 | `cardLimitConfigured=false`、`permanentAmount=0`、`sharedPoolAvailableAmount=1,000,000` |
| **佔用 30,000（未綁企業單位）** | **成功**，DB `corp_unit_id=NULL`、`instrument_id` **有值** |
| 建企業單位（額度 200,000）＋**綁定該卡** | 綁定成功 |
| **佔用 100,000（授權只帶 `instrumentId`）** | **成功**，DB `corp_unit_id=0A838A7E…`（★ 自動歸屬） |
| 企業額度查詢 | 自身佔用 100,000、可用 100,000（200,000 − 100,000） |

★ 測試資料已清理（`corp_units` 0、`limit_holds` 回 34、`account_limits` 回 18）。

### 12.5 仍未做（誠實揭露）

| 項 | 說明 |
|---|---|
| **Stand-in 上送仍不帶 `instrumentId`** | 本地讀模型（`CC_AUTH.LIMIT_HOLDS`）無該欄；自行回填等於臆造 → 該類佔用在權威端仍無載體歸屬（如實揭露） |
| **商品參數 `limit.cash-advance-ratio` 未建** | 故真機之預借檢核仍需呼叫端明確帶比例；`limit.shared-pool-eligible` 亦未建（走系統預設，見 `CR-057 §9.3`） |
| 企業卡「改綁」 | 目前**禁止**（回 `CC-LIM-1003`）；改綁流程（含既有佔用如何處置）需客戶口徑 |
| 企業額度「申請→覆核」 | 仍未接 `LimitAdjustmentService`（`PUT /corp-units/{id}/limit` 為核定端點） |


---

## 13. 商品參數落地與消費端接線（2026-10-18，`#A-298`）

BEAM PMO「依照您的建議推進」→ 把 §4 設計的參數**真的建起來並接上消費端**。

### 13.1 建了什麼、刻意不建什麼

| 參數 | 處置 | 理由 |
|---|---|---|
| `product/limit.shared-pool-eligible` | **建**（`product-service V3`，值 `true`，效期 2026-01-01 起） | 與 `V13` 之 `shared_pool_eligible DEFAULT 1` **完全一致** → 建了不改變任何行為，只是把「系統隱含預設」升級為**顯式、可稽核、可覆寫**的參數（原本只寫在 DDL 的 DEFAULT 裡，沒人看得到） |
| `product/limit.cash-advance-ratio` | **刻意不建** | 這是**授信政策**（預借現金額度佔總額度幾成）。`§2.5` 的 80% 只是**舉例** → 填任何數字都是**臆造政策**。留空＝未設定＝預借不可用（fail-closed），待客戶核定 |
| `product/limit.approval-threshold-minor` | **不種** | 未設時 `limit-service` 以組態預設 500,000 fail-closed 降級（`CR-045 §7.14 A4`），已有明確預設 → 再種一份會產生「兩個真相」 |

### 13.2 消費端接線（★ 沒有這一步，參數建了也不會生效）

| 參數 | 消費端 | 行為 |
|---|---|---|
| `shared-pool-eligible` | `CardIssuedProjectionService`（`#A-297` 已接） | 發卡事件 → CARD 權利列之 `shared_pool_eligible` |
| `cash-advance-ratio` | **`LimitAdjustmentService.resolveCashAdvanceRatio`（本批新增）** | 卡片級調額**未帶**比例時自動取參；優先序：呼叫端明確帶入 → 商品參數 → 未設定（`null`，預借不可用）。值域外（非數字／超出 0–100）視同未設定並記 WARN，**不得靜默截斷** |

### 13.3 ★★ 真機攔下的真實缺陷（本批最大價值）

真機驗證時發現：**調額核准後 `cardLimitConfigured` 仍是 `false`**（金額已寫入 200,000）。

| 項目 | 內容 |
|---|---|
| 根因 | `applyAdjustedAmount` 只更新金額，**沒把 `limit_source` 由 `PRODUCT_DEFAULT` 翻成 `MANUAL`** |
| 後果 | `limitConfigured()` 恆為 false → `hold()` **永遠不套卡片上限** → 已核定額度的卡仍可刷到帳戶總額度 |
| 嚴重度 | **等於 `#A-286` 修掉的「不享共享池之卡超放」缺陷，在「先發卡、後核定」的卡片上靜默復活** |
| 修法 | 三個 `UPDATE`（永久／臨時／彈性）一律加 `limit_source = 'MANUAL'`——套用調額即代表**人工核定** |
| 回歸測試 | `CashAdvanceLimitIntegrationTest.核定後須翻為人工核定`：權利列 → 核准 → 斷言 `cardLimitConfigured=true` |

★ 教訓：**「金額寫進去了」≠「語意對了」**。同一張表上並存「金額」與「金額的來源」時，
只更新金額會留下**自相矛盾的列**（有 200,000 卻說「未核定」），而這種矛盾**不會報錯**。

### 13.4 真機驗證（真 Oracle＋真 Kafka＋真 product-service）

| 步驟 | 結果 |
|---|---|
| 參數改 `limit.shared-pool-eligible=false` | 權威值已變 |
| 投遞發卡事件（真 Kafka） | CARD 權利列：`PRODUCT_DEFAULT｜金額 0｜shared_pool_eligible=**0**`（★ 參數被實際消費） |
| 參數 `limit.cash-advance-ratio=80` | 已建立 |
| 卡片級調額（**不帶**比例） | CARD 列 `cash_advance_ratio=80`、上限 `160,000`（200,000×80%）→ ★ 參數被實際消費 |

腳本：`ops/local/e2e/limit_param_loop_e2e.sh`。

### 13.5 ★ 待裁定：業務日之語意（**本批未擅自改**）

真機驗證時撞到一個**跨服務的真實問題**：

> `limit-service` 之 `LocalDate.now(clock)` 取的是 **UTC 日期**。台北時間 00:00–08:00 之間，
> UTC 日期仍是「昨天」→ 取參時會拿到**昨天的版本**（本次驗證因此一開始取到舊值 `true`）。

| 選項 | 說明 |
|---|---|
| A（建議） | 額度域之**業務日**統一改為 `Asia/Taipei`（`Clock.system(ZoneId.of("Asia/Taipei"))`）——銀行業務日不應隨 UTC 跳動 |
| B | 維持 UTC，並要求參數生效日一律提前一天設定（**治標，且易忘**） |

★ 未擅自改：`Clock` 影響本服務**所有**日期語意（臨時額度起訖、覆核門檻版本、預借比例版本），
屬跨功能影響 → 需 BEAM PMO 裁定後另批處理。

### 13.6 工具教訓（省下後人半小時）

| 坑 | 事實 |
|---|---|
| Flyway 的歷史表在 Oracle 是**引號小寫識別名** | `cc_product."flyway_schema_history"`，欄名亦為 `"installed_rank"`／`"success"`——用大寫會 `ORA-00904` |
| `COMMENT ON TABLE` 不可用相鄰字串字面值 | Oracle 不自動串接 → `ORA-03046`（本批首版即踩，`V3` 因此失敗一次；已改為說明寫在註解） |
| Kafka CLI 不在 PATH | 容器內為 `/opt/kafka/bin/kafka-console-producer.sh` |
| bash 變數後接中文全角字元 | `$VAR（` 會被吃進變數名 → `unbound variable`；一律 `${VAR}`（第二次踩） |

---

## 9. 修訂紀錄

| 版本 | 日期 | 內容 |
|---|---|---|
| **1.5** | 2026-10-18 | **商品參數落地＋消費端接線（`#A-298`）**：`product-service V3` 建 `limit.shared-pool-eligible=true`（與 `V13` 預設一致，行為中性）；**刻意不建** `cash-advance-ratio`（授信政策待客戶核定）。新增 `LimitAdjustmentService.resolveCashAdvanceRatio`（未帶比例時自動取參）。★★ **真機攔下缺陷**：調額核准未把 `limit_source` 翻為 `MANUAL` → `cardLimitConfigured` 恆 false → **卡片上限永不檢核（超放缺陷靜默復活）**；已修＋回歸測試。★ 另揭露**業務日走 UTC** 之跨服務問題（待裁定，未擅自改） |
| **1.4** | 2026-10-18 | **授權路徑生效（`#A-297`）**：① `LimitPort.hold` 增 `holdType`＋`instrumentId`，`AuthorizeUseCase` 以 `txType` 對映型態（`CASH_ADVANCE`→`CASH_ADVANCE`，未知→`GENERAL`）；② ★ **順帶修掉「`instrumentId` 從未傳給額度服務」**（`limit_holds.instrument_id` 永遠為空之真因）；③ `V19` `corp_unit_instruments` ＋ `POST /corp-units/{id}/instruments`，`hold()` 自動解析企業單位並跑上層鏈檢核。真機：發卡→權利列→未核定仍可佔用→綁定後佔用**自動歸屬**企業單位 |
| **1.3** | 2026-10-17 | **P4-2／P4-3 落地（`#A-296`）**：預借現金（`V15` 比例欄＋`V16` **值域擴充——真機攔下 `ORA-02290` 之真實缺口**；上限＝總額度×ratio、可用＝min(上限−已預借, 有效可用)；`null`≠`0`；7 例真機）；企業子額度（`V17` 組織樹＋`limit_holds.corp_unit_id`；**上層可用扣減所有下層佔用**；檢核走上層鏈並指出不足之層；6 例真機）。★ 揭露：授權端尚未送 `CASH_ADVANCE`、企業額度覆核流程未接、企業卡未綁 `corp_unit_id` |
| **1.2** | 2026-10-17 | **§6 維度確認**（BEAM PMO）：自然人＝**帳戶級**、公司戶＝**歸戶級**；**個人與公司額度不混算**（附張三／Z 公司例）；併記錄採購卡用途限制與 `CR-056` 身分證 360 之缺口 |
| 1.1 | 2026-10-17 | **P4-1 落地（`#A-289`）**：`V13` 共享池權利欄＋`V14` 調額單留痕；**授權卡片上限檢核**（修正「不享共享池之卡超放」）；查詢分開呈現（卡可用 vs 共享池剩餘）；真機驗證四情境。★ 併揭露實作期第二缺陷（申請時建列會使待覆核卡片被鎖）與其修正 |
| 1.0 | 2026-10-17 | 開立並凍結 BEAM PMO 口徑：共享池＝**總額度之共用權利**（非額外池）；三概念分離（總額度／卡片名目額度／共享池權利）；可刷上限與查詢呈現之精確公式；**5 萬例子逐情境驗算**並揭露現行「不享共享池之卡會超放」（G-1）；預借現金為總額度百分比（非獨立額度）；企業子額度扣減上層且需組織樹。P4-1 落地、P4-2／P4-3 分批 |
