Repository navigation
[feature] 交易對象別名與合併 - #87
Merged
Merged
Conversation
✅ SonarQube Quality Gate passed — pathorsAI_internal0 open issues on this PR. |
Contributor
Author
|
@yui AI check 沒問題,可以 review/merge |
yui0303
approved these changes
Oct 12, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
問題
src/lib/wise-sync.ts的resolveParties),查無就自動新建。deleted_at IS NULL:被軟刪除的交易對象還是會吃到新匯入的交易,結果那些交易掛在看不到的對象底下,畫面上等於消失。Facebk *26qx2aj6e4、Grab* A-9p4ckuhgwjnpav),或同一家有多種寫法(ElevenLabs/Elevenlabs.io、GitHub/Github, Inc.)。之前沒有辦法把它們併起來,也沒有辦法讓之後的匯入自動歸到同一個。設計
別名
parties.aliases(migration 0033)text[] NOT NULL DEFAULT '{}'。一般別名 = 不分大小寫、名稱完全相同才算;以*結尾 = 前綴比對(Facebk *、Grab*)。matchParty(src/lib/party-aliases.ts):名稱完全相同 > 別名完全相同 > 最長的前綴別名,同分取 id 最小的。前後空白不算;只有*的別名會被丟掉(會吃掉所有名稱)。src/lib/party-resolve.ts)一律帶organization_id與deleted_at IS NULL,只撈「名稱(lower/btrim)在清單裡」或「有設別名」的列,然後交給matchParty。共用同一套比對的地方
grep 過所有
insert(parties)與parties.name的名稱查詢,換成共用函式:wise-sync.ts,原本的resolveParties刪掉)create_transaction(partyName)與bulk_create_transactions(tools-transactions.ts,原本的getOrCreateParty/resolvePartyNames也有同樣「沒過濾已刪除」的問題)mutations.ts的getOrCreateParty/resolvePartyName,同樣沒過濾已刪除)matchPartyId:先比統編(唯一才算),名稱那步改用matchParty(原本只認唯一的完全相同名稱)simpany-issue/simpany-salary/vendor-billing用到parties.name的地方都是「依 id 取名稱顯示」,不是名稱比對,沒動。合併
mergeParties(orgId, targetId, sourceIds[])src/lib/party-merge.ts:驗證與編排(純邏輯,DB 由 deps 注入);src/lib/party-merge-deps.ts:Neon 實作。planPartyMerge再比一次 row 的組織,第二道防線)。db.batch)。依序:organization_id,含已軟刪除的列:transactions.party_id、projects.client_party_id、subscriptions.customer_party_id、contracts.customer_party_id、billing_items.customer_party_id、invoices.party_id、vendor_billing_accounts.party_id、recurring_expenses.party_idvendor_billing_accounts.meta.matchPartyIds(jsonb 裡的 party id,不是 FK 但一樣會留著指向已刪除對象):來源換成目標、去重,只動這個鍵aliases+= 每個來源的名稱與別名(不分大小寫去重、跳過跟目標名稱相同的)REFERENCES parties(id)的:links(0007)與receivables(0003)已在 0008 / 0010 刪表;invoice_drafts.payload是 2 小時過期的 jsonb 草稿,沒動。MCP
merge_parties:{ targetId: number, sourceIds: number[], organizationId? }→{ id, name, mergedIds, mergedNames, aliases, moved: { transactions, projects, subscriptions, contracts, billingItems, invoices, vendorBillingAccounts, vendorBillingMatchParties, recurringExpenses } }。標destructiveHint: true(沒有 unmerge),描述裡要求先跟使用者確認。activity log 走 handler 的集中紀錄(update/party/ 目標 id)。create_party/update_party可帶aliases: string[](update 是整串取代、[]清空);list_parties/get_party回傳aliases。create_party/delete_party都沒有 owner/admin 限制),沒另加。SERVER_VERSION1.11.0 → 1.12.0;docs/mcp.md補上別名與合併。網頁
updateParty只在表單有帶aliases欄位時才寫,避免舊表單一存就清空。Migration 0033⚠️
migrations/0033_party_aliases.sql:ALTER TABLE parties ADD COLUMN aliases text[] NOT NULL DEFAULT '{}'。Forward-only、additive,Run AFTER 0032。必須在部署前跑:
listParties/getParty/ 所有名稱比對都會 select 這一欄,欄位不存在時交易對象頁、MCP 與 Wise 同步都會壞。沒有加索引:比對要做前綴 + 不分大小寫,GIN(text[]) 幫不上忙;候選查詢走既有的
idx_parties_org,每個組織頂多數百筆。docs/deployment.md已補說明。測試
bun run test:220 pass / 0 fail(新增party-aliases.test.ts、party-merge.test.ts、party-merge-deps.test.ts)*的別名被忽略party-avatar-store的測法):別名合併與去重、matchPartyIds只改同組織、目標在來源裡 / 空清單 / 非整數 / 已刪除 / 別組織都擋、查詢租戶條件被改壞時第二道還擋得住organization_id、目標 / 來源都要求deleted_at IS NULLtsc --noEmit、bun run lint、bun run build全過facebk *zzz經前綴別名Facebk *對到既有對象;New Co/NEW CO同批只建一筆meta.matchPartyIds換成目標且billingAccountId保留;來源軟刪除、目標別名正確division by zero中止,目標別名沒被改