Project

General

Profile

Actions

CR #838

open
RA

[CR-063] 會計科目缺口解除:GL 會計(科目表/傳票/事件規則)

CR #838: [CR-063] 會計科目缺口解除:GL 會計(科目表/傳票/事件規則)

Added by Redmine Admin 6 days ago. Updated 6 days ago.

Status:
In Progress
Priority:
P0-Urgent
Assignee:
-
Target version:
-
Start date:
Due date:
% Done:

0%

Estimated time:
Story Points:

Description

【缺口】GAP-001 G-05:會計科目未定義(clearing→posting/rewards→posting/dispute→posting 三條同步分錄未實作,SDD-003 §13/14/15);原判定為「業務事實不可自建」。
【來源】米粿 2026-10-20 提供 taiwan-bank-card-accounting-subjects.md(台灣本國銀行信用卡發卡與收單會計科目,962 行),已複製入 repo:docs/00-governance/reference/。
【規格先行】SCS-007 新增 §7.3 GL 會計(v1.9):五張表、科目代碼規則、三條同步分錄(REWARD_REDEEM/CLEARING_ISSUER_PRESENT/ACQ_CHARGEBACK)、六項設計要點;§10 新增 #14 揭露仍缺。★ 文件自身不一致已揭露:ACCT_CLASS 標 CHAR(2) 但值域含 OBS(3 字元),實作採 CHAR(3) 並保留完整值域。
【實作】posting-service:V11__gl_accounting.sql(GL_ACCOUNT/GL_JOURNAL_ENTRY/GL_JOURNAL_LINE/ACCT_EVENT_RULE/GL_ACCT_BALANCE + GL_JE_NO_SEQ);V12__gl_seed.sql 由 tools/gen_gl_seed.py 產生(科目 90 筆/事件規則 17 筆,不手打);JournalEntry/JournalLine/AccountRule + GlRepository + JournalEntryService(借貸平衡/冪等/沖銷以反轉傳票/未知事件 fail-closed)+ JdbcGlRepository + GlController(POST/GET/reversals)。
【結果】JournalEntryServiceTest 5 測全綠;GlRepositoryIntegrationTest(Testcontainers 真實 Oracle + Flyway)驗證三條分錄規則、傳票號格式、寫回查、冪等唯一鍵、沖銷回填與原傳票標記 R。

【進度(2026-10-20 更新,commit b00e161)】三條同步分錄全數接線:①rewards 點數兌換(SCS-010 §7.5,169 測)/②clearing 請款認列(SCS-013 §7.2 v1.5,56 測)/③dispute 爭議扣款(SCS-012 §7.6,58 測)。

【仍缺(不假綠)】

  1. 端到端真機驗證受阻:POST /api/v1/clearing/runs 受服務間 ACL 保護(ACL 已放行 cc-clearing-service,仍缺 M2M 權杖)→ 三條分錄皆未真機驗證。
  2. 結算/交割分錄未接:NCCC_SETTLE_ISSUER(貸方=現金/同業存款)來源文件未給科目,V12 種子 fail-closed 略過——外部會計輸入(決策 D-034)。
  3. 零費用組織(feeTotal=0 且 totalAmount>0)→ CC-POST-4000 → 整批清算回滾(刻意大聲失敗,決策 D-035/SCS-013 §10 #7)。
  4. 多幣別(FX_RATE/匯兌損益)未實作(SCS-013 §10 #8)。
  5. GL_ACCT_BALANCE 日終結轉未實作(SCS-007 §10 #14)。

→ 依假綠判例 G3,本議題不結案(有殘項)。

RA Updated by Redmine Admin 6 days ago Actions #1

【接線①完成(2026-10-20)】點數兌換 → GL 分錄(同步)。
依據:SDD-003 §13「Rewards → Posting 為同步呼叫且在寫事件之前」+ CR-030 §2 #16「事件不得重做同步已做之寫入」。
規格:SCS-010 v1.11 新增 §7.5。
實作:GlPostingPort(domain)+ PostingGlHttpClient(POST /api/v1/gl/journal-entries;posting.service.url 預設 http://localhost:8098);RedemptionService 於 redemptionRepository.save/writeLedgerEntries 之後、outbox.append 之前呼叫;金額=pointsUsed × 點值(分,與 REWARD_ACCRUAL 同基礎);sourceRefNo=redemptionNo 供 GL 端冪等;非 2xx 或連線失敗 → CC-PLT-5005 → 交易回滾(fail-closed)。
測試:RedemptionServiceTest 新增 2 例(2,500 點 × 100 分 = 250,000 分;GL 失敗不發射事件)+手寫替身(無 Mockito);rewards 全量 169 測 0 失敗。
【仍缺】clearing→posting/dispute→posting 尚未接線;GL_ACCT_BALANCE 日終結轉;多幣別 FX/匯兌損益。
【已知落差】cashValue(實際給付)與點值之差額(兌換損益)未定義,本期以點值入帳。

RA Updated by Redmine Admin 6 days ago Actions #2

【接線②(clearing→posting)preflight:發現需先擴充 GL 規則模型,暫不接線(不做錯帳)】
文件 §9.1 之 CLEARING_ISSUER_PRESENT 為「1 借 + 2 貸」且金額不同:借 13027-ISS-01(總額)、貸 23000-ISS-01(淨額)、貸 42066-ISS-01(交換費)。
現行 GL 模型(ACCT_EVENT_RULE 一列=一組 DR/CR;JournalEntryService 對所有規則套用同一金額)無法正確表達此分錄——強行接線會把交換費也記成總額,造成借貸不平或費用虛增(假帳)。
→ 需先做一個增量:①規則支援 AMOUNT_BASIS(NET/FEE)多列;②PostJournalRequest 支援 per-basis 金額;③服務逐列取對應金額,缺 basis → fail-closed;④測試(3 行平衡、缺 basis 拒絕)。
★ 此為「寧可停下來,不做錯帳」之決定(反假綠)。
dispute→posting(ACQ_CHARGEBACK:13097-ACQ-02 → 13097-ACQ-01)為單一借貸對,不受此限,可先接。

RA Updated by Redmine Admin 6 days ago Actions #3

【接線③完成(2026-10-20)】爭議扣款 → GL 分錄(同步)。
規格:SCS-012 新增 §7.6。事件 ACQ_CHARGEBACK(BIZ_SCOPE=ACQ);借 13097-ACQ-02/貸 13097-ACQ-01(由 posting 端 ACCT_EVENT_RULE 決定,dispute 不硬編碼科目)。
呼叫時點:DisputeService.submitChargeback 之 repository.update 之後;sourceRefNo=disputeNo 供冪等。
失敗:fail-closed(非 2xx/連線失敗 → CC-PLT-5005 → 交易回滾)。
測試:DisputeServiceTest +2(同步寫入/GL 失敗拋出)+ FakeGlPostingPort(無 Mockito);dispute 全量 58 測 0 失敗。
【仍缺】clearing→posting(需 per-basis 金額擴充)、GL_ACCT_BALANCE 日終結轉、多幣別 FX。

RA Updated by Redmine Admin 6 days ago Actions #4

【A 完成(2026-10-20)】GL 支援 per-basis 金額——解鎖 clearing 之「1 借 2 貸且金額不同」分錄。
實作:PostJournalRequest 新增可選 basisAmounts;JournalEntryService.resolveAmount 逐規則依 AMOUNT_BASIS 取金額,缺基礎 → CC-POST-4000 fail-closed(不用總額硬套);產生器 gen_gl_seed.py 新增 MULTI_BASIS,CLEARING_ISSUER_PRESENT 拆 NET(23000-ISS-01)/FEE(42066-ISS-01)兩列。
測試:JournalEntryServiceTest +1(2 借 2 貸、借貸各 100,000 平衡;缺 FEE 基礎 → fail-closed)+整合測試改驗 NET/FEE 兩列;posting 目標測試 BUILD SUCCESSFUL。
【下一步】接線② clearing→posting:SettlementService 以 NET=total-fee、FEE=fee 呼叫。

RA Updated by Redmine Admin 6 days ago Actions #5

【交接(2026-10-20)】接線② 之完整續接包已寫入 git:cardcore/HANDOVER.md §3.B「續接包:#A-363」——含已驗證事實(per-basis 已支援/種子已含 NET+FEE 兩列/落點為 ClearingRunService.execute() 且 OrgAggregate 已有 totalAmount()與feeTotal()/勿用 settle())、照做清單(約 5 檔)、驗收標準(4 行分錄借貸各=totalAmount;缺 FEE → CC-POST-4000)。接手者照清單做即可,不必重新勘查。

RA Updated by Redmine Admin 6 days ago Actions #6

【接線②完成(2026-10-20)】清算請款 → GL 分錄(同步)。CR-063 三條同步分錄至此全數接線(①rewards 點數兌換/②clearing 請款認列/③dispute 爭議扣款)。

規格:SCS-013 → v1.5 新增 §7.2「清算請款之 GL 分錄(同步)」;併更正 §7 #3 之入帳時點、§8 改列同步 fail-closed、§10 #5 改列「請款已接/交割未接」+新增 #7(零費用 fail-closed)/#8(多幣別)。決策 D-034/D-035。

★ 更正上一筆 journal(#1276)之「下一步」:落點不是 SettlementService.settle()——settle() 語意為交割(NCCC_SETTLE_ISSUER,貸方=現金/同業存款,來源文件未給具體科目,V12 種子已 fail-closed 略過)。請款認列(CLEARING_ISSUER_PRESENT)依 SDD-003 §14「比對 → 產生會計分錄 → 寫事件」落在 ClearingRunService.execute()。

實作(6 檔):clr/domain/port/GlPostingPort(postIssuerPresent:runNo/orgCode/netMinor/feeMinor/businessDate);clr/infrastructure/client/PostingGlHttpClient(POST /api/v1/gl/journal-entries;事件 CLEARING_ISSUER_PRESENT/BIZ_SCOPE=ISS/sourceRefNo = runNo + ":" + orgCode;必帶 basisAmounts={NET: 總額−費用, FEE: 費用};posting.service.url 預設 http://localhost:8098);ClearingRunService 建構子加埠,於聚合之後、outbox 之前逐組織呼叫,totalAmount<=0 之組織略過。

配套:SAD-002 §5 之 clearing → posting 由 A 更正為 S(同步 HTTP),重跑 tools/gen_service_acl.py --write → posting-service 已放行 cc-clearing-service(原 SCS-013 §10 #5 之待辦)。

失敗語意:fail-closed——非 2xx/連線失敗 → CC-PLT-5005 → execute() 交易整體回滾(不留「清算已完成、會計未認列」)。

測試(跑測試+讀報告,非掃字串):FakeGlPostingPort(手寫替身,無 Mockito)+ ClearingApplicationServiceTest +3(逐組織 NET/FEE:500,000−12,500=487,500/200,000−5,000=195,000;零金額略過;GL 失敗拋出);clearing 全量 56 測 0 失敗(報告 9 個測試類,failures=0/errors=0/skipped=0);scan_mockdata/scan_faketest 於本模組 0 命中;check_memory.py ✅。傳票形狀(4 行=2 借 2 貸、借貸各=總額)由 posting 側 JournalEntryServiceTest.multiBasisEntry 把關——本服務僅驗發出的內容,不重複驗對方會計語意。

【仍缺/不假綠】

  1. 端到端真機驗證仍受阻:POST /api/v1/clearing/runs 受服務間 ACL 保護,M2M 權杖仍未取得(同 SCS-013 §7.1 既有阻塞)→ 不宣稱已驗證。
  2. 結算/交割分錄未接(NCCC_SETTLE_ISSUER 無科目,屬外部會計輸入,不得臆造)。
  3. 零費用組織(feeTotal=0 且 totalAmount>0)→ posting 之 resolveAmount 要求 basis>0 → CC-POST-4000 → 整批回滾。刻意採大聲失敗(不臆造費用、不靜默略過分錄),解除待 posting 支援「零額基礎略過該列」(SCS-013 §10 #7)。
  4. 多幣別(FX_RATE/匯兌損益)未實作(§10 #8);GL_ACCT_BALANCE 日終結轉未實作(SCS-007 §10 #14)。

RA Updated by Redmine Admin 6 days ago Actions #7

【內容同步(2026-10-20)】描述之「仍缺」已過時(三條同步分錄已全數接線),本筆更新為最新事實。

進度:CR-063 三條同步分錄全數接線——①rewards 點數兌換(SCS-010 §7.5,rewards 全量 169 測)/②clearing 請款認列(SCS-013 §7.2 v1.5,clearing 全量 56 測)/③dispute 爭議扣款(SCS-012 §7.6,dispute 全量 58 測)。commit b00e161(main)。

仍缺(不假綠,逐條可追溯)

  1. 端到端真機驗證受阻:POST /api/v1/clearing/runs 受服務間 ACL 保護——ACL 已放行 cc-clearing-service(SAD-002 §5 A→S 後重跑 gen_service_acl.py),仍缺合法 M2M 權杖 → 三條分錄皆未真機驗證,不宣稱已驗證。
  2. 結算/交割分錄未接:NCCC_SETTLE_ISSUER(貸方=現金/同業存款)來源文件未給具體科目,V12 種子已 fail-closed 略過——屬外部會計輸入,不得臆造(決策 D-034)。
  3. 零費用組織(feeTotal=0 且 totalAmount>0)→ posting 之 resolveAmount 要求 basis>0 → CC-POST-4000 → 整批清算回滾;刻意採大聲失敗(決策 D-035,SCS-013 §10 #7)。
  4. 多幣別(FX_RATE/匯兌損益)未實作(SCS-013 §10 #8)。
  5. GL_ACCT_BALANCE 日終結轉未實作(SCS-007 §10 #14)。

→ 依假綠判例 G3「有殘項一律不得 CLOSE」,本議題維持未結案,狀態轉 In Progress。

RA Updated by Redmine Admin 6 days ago Actions #8

三條同步分錄已全數接線(commit b00e161),惟仍有 5 項殘缺(真機驗證受阻/交割分錄未接/零費用 fail-closed/多幣別/日終結轉)——依假綠判例 G3「有殘項一律不得 CLOSE」,故不結案,轉 In Progress 續追。

RA Updated by Redmine Admin 6 days ago Actions #9

【端到端真機驗證完成(2026-10-20,#A-364)】三條同步分錄之 clearing → posting 已於真機閉環;過程中抓到並修正一個真缺陷。

腳本:ops/local/e2e/clearing_gl_e2e.sh(新增,可重入)——24 項檢查全綠(fail-closed:有失敗即退出碼 1)。

驗證內容(每項以查權威表為準,不以日誌字串判讀)

  • V1:無權杖 POST /api/v1/clearing/runs → 401(證明服務間 ACL 生效,非假開放)。
  • V2:Keycloak client_credentials 取得權杖(azp=cc-dispute-service,clearing 之 ACL 唯一允許呼叫者)。★ 前一窗「權杖長度 0」判定為代理未繞過之 502 假陽性(本地 curl 須 --noproxy '*')。
  • V3:人工種入 CC_CLR 2 批次 3 明細(VISA 500,000/12,500、MASTERCARD 200,000/5,000)——檔解析器不在範圍(SCS-013 §10 #1)。
  • V4:帶權杖呼叫 → 202;CLEARING_RUNS:MATCHED/tx=3/total=700,000。
  • V5–V6:CC_POSTING.GL_JOURNAL_ENTRY 查得 2 張傳票(CLR20261020000007:VISA/:MASTERCARD),各 4 行——借 13027-ISS-01(487,500+12,500/195,000+5,000)、貸 23000-ISS-01(NET)+42066-ISS-01(FEE);ΣDR=ΣCR=500,000/200,000。
  • V7:同冪等鍵重送 → 同 runId、傳票仍 2 張(GL 冪等成立)。
  • 併查 outbox:DB 查得 clearing.batch.matched(SCS-013 §7.1 之「同交易寫 outbox」經真機查證;Kafka 發布未單獨驗證)。

★ 真缺陷(已修,D-036):三支 GL client(rewards/dispute/clearing)之 posting.service.url 預設埠誤植 8098(實為 report-service;posting-service 是 8087)。若未修正,真機呼叫必然「連不上 → fail-closed → 整批回滾」。單元測試以手寫替身測不出埠錯誤——這是「反假綠必須真機」的活例證。三服務已重建 jar(check_artifacts_fresh 0 過期)並重跑測試(clearing 56/dispute 58/rewards 169,0 失敗)。

揭露範圍(不假綠):①清算明細為人工種入(檔解析器不在範圍);②涵蓋 clearing→posting 同步分錄,不含結算(交割)分錄與多幣別;③SCS-013 §10 新增 #9:GL 為遠端交易,本端回滾不回滾對方已提交之分錄(孤兒分錄風險,屬 Saga 範疇,本期不做)。

殘缺不變:交割分錄(待客戶給現金/同業存款科目)、零費用 fail-closed、多幣別、日終結轉。本議題維持 In Progress、不結案(G3)。

RA Updated by Redmine Admin 6 days ago Actions #10

【交割分錄「待科目模板」備妥(2026-10-20,#A-365)】G-05(交割之現金/同業存款科目)到貨後當日可閉環——程式、工具、規格、測試已全部就位。

分錄設計(定案,SCS-013 §7.3 v1.7):事件 NCCC_SETTLE_ISSUER/ISS;借 23000-ISS-01(已知——§7.2 認列時貸記之淨額負債,交割清償)/貸待 G-05(現金/同業存款,§9.1 L710 僅敘述無代碼);金額=淨額(與負債餘額對應);sourceRefNo = runNo + ":SETTLE:" + orgCode + ":" + settlementId(GL UNIQUE 冪等);fail-closed。

已備妥:

  1. GlPostingPort.postSettlement+PostingGlHttpClient 實作(clearing)。
  2. 啟用開關 settlement.gl.enabled(預設 false)——★ 關鍵設計:規則未種前若接線即啟用,每次 settle() 會因 CC-POST-4001(查無規則)整批回滾、破壞既有結算功能;開關分離「程式就緒」與「會計啟用」。
  3. gen_gl_seed.py 新增 EXTERNAL_ACCOUNT_SLOTS(借已填/貸 None 待填)+ --check-pending(退出碼 1=待填);重跑冪等已驗證(G-05 未填時產出與現狀完全相同)。
  4. 測試:交割三例(flag 關→不發 GL/flag 開→逐組織呼叫金額=淨額 487,500/195,000/GL 失敗拋出)——clearing 全量 59 測 0 失敗。

§7.3.1 四步 runbook(科目到貨後):①科目碼填入 EXTERNAL_ACCOUNT_SLOTS.credit(格式檢核+--check-pending 轉 0)→ ②重跑產生器(⚠️ V12 已套用環境須以 V13 追加,不得改既有 checksum)→ ③settlement.gl.enabled=true+真機驗證(借 23000-ISS-01/貸新科目、金額=淨額、冪等)→ ④回寫 GAP-001 G-05 結案+本 journal。

範圍揭露:不含收單側交割(ACQ_MERCH_SETTLE/ACQ_NCCC_SETTLE 同有「現金/存款」待科目問題)——待 G-05 一併提供後另立增量。

Actions

Also available in: PDF Atom