跳到主要內容跳到頁尾
作品
  • 減錯
  • 省時

撥款通知信的每個日期,都能指回合約

依合約規則產出撥款通知預覽,寄送由人完成;合作方發票上傳後另由 OCR 與規則核對。

2026/07
3 種合作方案、8 封信、3 條收件規則
案例閱讀約 3 分鐘
背景與使用技術(4 項)

每個結算月要寄給合作方的撥款通知信,兩個日期都得照合約條文逐一數工作日,還要記得補假。改成依方案與狀態分支的產生器,8 封信一次產齊,日期由程式算好並在產出檔案前先跑驗算。合作方回傳的發票則經上傳網頁與 OCR 核對金額、統一編號與辨識信心度,三條規則全過才算收件。

Node.jsGoogle Apps ScriptOCR合約自動化

誰在用

負責撥款通知與發票回收的財務人員

原本怎麼做

撥款通知信上的日期是合作方對帳的依據,合約把它們綁死:通知寄出後 7 個工作日內開立發票,再對到下一個約定撥款日。每寄一封信都得翻一次合約、逐日數工作日,還要記得當年哪些日子是補假。合作方案有三種,每種又有不同狀況,展開是 8 封內容不同的信,過去每月手寫一輪。發票回收的金額、統一編號與掃描清晰度,全靠人工逐張比對。

前後對比

通知信改造前8 封內容不同的信,每月手寫一輪改造後同一個產生器依方案與狀態輸出,改文案只需改一處規格
日期計算改造前翻合約、逐日數工作日、記補假改造後合約條文寫成常數,程式算好寫進信;產出檔案前跑 9 條驗算規格
發票核對改造前金額、統編、掃描清晰度人工逐張比對改造後OCR 後套 3 條規則,全部通過才收件規格
OCR 費用改造前上傳網址公開,每次都呼叫按次計費的服務改造後同一個專屬連結六小時內最多 20 次,超過就不再呼叫規格

量測:實測=有計時或有紀錄;估計=喜特先生推估;客戶自述=客戶回報;規格=設計上的固定條件;待補=尚未取得數字。

流程一眼看懂

改造前
  1. 翻合約數工作日

  2. 手寫 8 種信

  3. 人工比對回收的發票

改造後
  1. 合約條文寫成常數

  2. 產生器算日期並驗算

  3. 預覽,人工寄送

  4. 合作方專屬連結上傳發票

  5. OCR 加 3 條規則收件

流程示意:依本案既有做法整理,非客戶實際畫面。

做了什麼

  1. 合約條文常數化:每個日期都能指回合約的某一條。
  2. 產出即預覽:產生靜態網頁給人檢視,寄送仍由人在信件系統完成。
  3. 產出檔案前自我驗算:9 條驗算涵蓋跨年、補假與各方案的位移,改壞了當場失敗。
  4. 三條規則收件:金額相符、統一編號相符、辨識信心度不為「低」。
  5. 限流先於 OCR,避免公開網址被拿來燒費用。

程式碼與實作細節收在頁面下方,需要時再展開。

你的情況像這樣嗎?

  • 信上的日期要指回合約,算錯一天就是一來一往的確認。
  • 同一種信有好幾種版本,每個月要重寫一輪。
  • 要收合作方的發票或憑證,金額和統編得逐張人工核對。

更多成果與運作條件

  • 8 封信一次產齊

    方案與狀態的所有組合由同一支產生器輸出,改文案只需改一處

  • 9 條驗算先於產出檔案

    日期算式被改壞時當場失敗,不會產出錯誤日期的信

  • 3 條 OCR 規則

    金額、統一編號、辨識信心度,三條全過才算收件

  • 0 個外部套件:產生器只用 Node.js 內建模組,不需要 npm 安裝

以上為本案既有成果與運作條件;估計值依原始資料標示,其他專案需另行評估,並非通用成效保證。

挑戰:原本遇到的困難

撥款通知信上的日期是合作方對帳的依據,不能自己挑。合約把它們綁死:通知寄出後 7 個工作日內開立發票,再對到下一個約定撥款日(每月 10 日,遇假日順延)。每寄一封信都得翻一次合約、逐日數工作日,還要記得當年哪些日子是補假。

  • 通知信長相不只一種:合作方案分三種,每種又有正常撥款、只提醒、需另收補償金等狀況,組合展開是 8 封內容不同的信,過去每月手寫一輪
  • 通知寄出日依方案不同:同一句「7 個工作日加下一個 10 日」,換一個方案,結果落在不同的月份
  • 算錯的代價高:合作方手上沒有合約可以自己驗算,日期錯了就是一來一往的確認
  • 發票回收要核對:金額、統一編號與掃描清晰度原本全靠人工逐張比對;而上傳網址是公開的,每次又會呼叫按次計費的 OCR 服務

所以目標不是「跑得動」,而是信上每一個日期都能指回合約的某一條。

完整解決方案與設計取捨

把合約條文拆成兩張對照表(通知寄出日、撥款月位移)放在程式開頭,產生器依方案與狀態分支組出 8 封信,日期由程式算好後直接寫進信件文字。產出檔案之前先跑 9 條 assert(程式內建的斷言檢查)驗算日期式。發票則由每家合作方各一組專屬識別碼(token)的上傳網頁接收,經 OCR(圖片辨識文字)擷取欄位後套用三條規則,全部通過才收件。

核心設計

  1. 合約條文常數化:通知日與撥款月位移的對應寫在常數旁,註解標出合約出處
  2. 產出即預覽:產生靜態 HTML 交給人檢視,寄送仍由人在信件系統完成
  3. 產出檔案前自我驗算:9 條 assertion 涵蓋跨年、補假與各方案的位移
  4. 三條規則收件:金額完全相符、統一編號相符、辨識信心度不為「低」
  5. 限流先於 OCR:同一 token 六小時內最多 20 次,超過就不再呼叫 OCR

技術與實作細節

展開完整實作說明

合約條文寫在常數旁邊

NOTICE_DAY(通知寄出日)與 PAY_MONTH_OFFSET(撥款落在結算月之後第幾個月)是把合約條文搬進程式的兩張表。寫入前,已把三份合約裡「明細通知寄出日」「發票開立期限」「撥款日」的條文逐字對過一遍,再把對應關係註解在表格旁。之後跟法務對條文時,讀註解就夠,不必回頭翻整份合約。

合約沒寫清楚的地方也記在同一處。「7 個工作日」是否包含通知當天,合約未明定,目前採隔日起算,並在註解中標明這是假設,不是定案。

順延邏輯與假日表

假日表是寫死在程式裡的一份日期清單(Set),涵蓋 2026–2027 兩年的政府公告國定假日與補假。銀行營業日的判斷同時看週末與假日表;撥款日從約定的 10 日起每次加一天,直到遇到營業日為止。

這張表沒有接任何行事曆 API,超出涵蓋年份之後撥款日就會算錯,程式碼中有註解直接點出這一點。順延演算法本身與資料來源無關,將來要換成從別處讀取假日時,替換的只有這個常數。

八封信共用一組版面函式

八封信的問候語、金額表格、發票資訊區塊與防詐提醒大量重複。若各自維護八個範本,改一次版面就得改八處,很容易漏掉其中一封。因此版面拆成一組基礎函式(段落、表格、列、提示框、按鈕),兩種信件函式只負責以不同順序把它們組起來。

README 明訂不手改產出的 HTML,因為重跑會被覆蓋,文案一律改產生器後重新執行。

產出檔案前的九條驗算

執行時,腳本會在產出檔案前以 Node 內建的 assert 跑九條驗算,涵蓋撥款日順延(含跨年進位與國慶日補假等邊界)、各方案的撥款月位移,以及發票開立期限的算式。九條全部通過才寫入檔案。

這組驗算沒有覆蓋率報告,它只做一件事:日期算式被改壞時當場失敗,而不是默默產出一個錯誤的日期。

預覽與列印,寄送留給人

產生器會把 logo 與字型編碼成文字(base64)後直接嵌入單一頁面的 index.html,斷網也能檢視;另外產出逐信一頁的列印版,交由無介面瀏覽器(不開視窗、在背景運作的瀏覽器)轉成 PDF。

寄送刻意不放在這支工具裡。信一寄出就是對外的付款承諾,中間保留一道人工檢視比較穩妥。

發票上傳:token 與三條規則

合作方沒有系統帳號,身分只靠每家一組的專屬 token。網頁與實際處理上傳的函式各自驗證一次 token,因為處理函式可以被直接呼叫,只驗網頁那一層等於沒驗。缺 token、token 錯誤、或查無當期資料,一律回覆同一句話,避免錯誤訊息被拿來試探其他合作方的 token。上傳的檔案只在單次請求的記憶體中驗證,不寫入雲端硬碟。

OCR 服務本身會給出「是否通過」的判斷,但那是為內部報帳設計的,與「合作方開出的發票是否正確」不是同一件事,所以不採用,改由三條規則判定:金額必須完全相符、統一編號須對得上公司、辨識信心度不得為「低」。任一條不過就退回,並說明原因。

限流卡在呼叫 OCR 之前。OCR 按次計費,而公開網址任何人都能打,所以以 token 為 key,六小時最多 20 次。這個計數的目的是擋住帳單,不是做稽核用的精確紀錄,因此允許它不是絕對精準。

說明

目前上傳網頁已部署,但尚未發給任何一家合作方。每家合作方的專屬 token 與當期應開立金額,仍由財務人員維護在共用試算表中;這也是結算依據目前唯一的人工環節,合作方主檔、合約條款與實際入座資料尚未串接。

延伸閱讀

有類似的情境想討論

帶著現有流程與去識別範例,討論適合的做法。