Skip to content

[feature] 訂閱可綁定合約,訂閱表與合約列可互相看到 - #64

Merged
YJack0000 merged 1 commit into
mainfrom
feat/subscription-contract-link
Sep 7, 2026
Merged

[feature] 訂閱可綁定合約,訂閱表與合約列可互相看到#64
YJack0000 merged 1 commit into
mainfrom
feat/subscription-contract-link

Conversation

@YJack0000

Copy link
Copy Markdown
Contributor

為什麼

subscriptions(週期性月費)與 contracts 是兩張各自獨立的表,訂閱只掛得到專案(project_id),掛不到合約。但週期性費用多半是合約裡談好的條件 —— 合約寫「每月維運費 30,000」,系統裡卻只有一張孤兒訂閱,回頭要問「這筆月費是哪張合約來的」沒有任何欄位答得出來,只能靠方案名稱猜。請款看板上的訂閱期別也因此永遠是 contractId: null,跟 billing_items 那側的合約欄位對不起來。

一次性分期走 billing_items.contract_id,週期性月費走新的 subscriptions.contract_id,兩邊語意一致。刻意維持選填 —— 既有訂閱不見得對得到合約,也有純口頭約定的月費。

改了什麼

Schemasubscriptions.contract_id(選填,FK → contracts.id)+ idx_subscriptions_contract

Queriessrc/db/queries.ts

  • listSubscriptions / getSubscription leftJoin contracts,多回 contractId / contractTitle
  • listBillingBoard 的訂閱列不再硬寫 contractId: null / contractTitle: null,改由訂閱綁定的合約帶出來。
  • 新增 listSubscriptionsByContract(orgId):一次查完再依合約分組(合約頁每張合約各查一次就是 N+1)。

Mutationssrc/db/mutations.ts

  • subscriptionValues / subscriptionColumnscontractIdcreateSubscription / updateSubscriptionunownedRefError 一併檢查合約歸屬,別的 org 的合約 id 塞不進來。
  • 多一個 optRefNum():Radix Select 不接受空字串當值,「不綁定」那一項送的是 "none"(比照 employees 的 userId),用 num() 解會變成 NaN

Web UI

  • 訂閱表單多一個「合約」下拉(專案下拉的下一格),選項標籤是 合約標題 — 客戶名,因為同一客戶常有多張標題相近的約。可清空(「不綁定合約」)。
  • 訂閱列表多一欄「合約」。
  • 合約的編輯視窗裡多一區「綁定的訂閱」(名稱、金額 + 週期、狀態),沒有綁任何訂閱就整塊不出現。

MCP

  • create_subscription / update_subscription 收選填 contractId,以 assertInOrg 驗歸屬;update_subscription 明確傳 contractId: null 可解除綁定(optNumber 會把 null 與「沒帶」都收斂成 undefined,所以另外判一次)。
  • list_subscriptions 多回 contractId / contractTitleSUBSCRIPTION_ROW / SUBSCRIPTION_LIST_ROW 兩個 outputSchema 同步補欄位,structured output 驗證才過得了。
  • docs/mcp.md 補一行。

i18nsubscriptions.list.columns.contractsubscriptions.newDialog.contract.*contracts.linkedSubscriptions.*(zh-TW / en 各一份)。

Schema change

⚠️ migrations/0022_subscription_contract.sql merge 後要手動套到 prod。

  • Forward-only、additive:只加一個可為 NULL 的欄位與一個 index,不動既有資料,不需要 backfill
  • 沒跑 db:pull / db:push,也沒對任何資料庫套用過。

驗證

  • bun run lint 通過(無輸出)。
  • bunx tsc --noEmitsrc/ 零錯誤。
  • bun run build 通過。

@YJack0000
YJack0000 requested a review from yui0303 September 7, 2026 16:01
@github-actions

github-actions Bot commented Sep 7, 2026

Copy link
Copy Markdown

❌ SonarQube Quality Gate ERROR — pathorsAI_internal

failed condition value threshold
new_duplicated_lines_density 3.81356 ≤ 3
new_violations 1 ≤ 0

1 open issue on this PR:

  • CRITICAL typescript:S3776 — Refactor this function to reduce its Cognitive Complexity from 17 to the 15 allowed. (src/lib/mcp/tools-client.ts:307)

@yui0303 yui0303 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approved per request.

@YJack0000
YJack0000 merged commit 3e107f9 into main Sep 7, 2026
1 check failed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants