- 省時
- 減錯
每月退信自動對回客戶,認不出的不硬猜
把退信對回客戶名單,每月產出台灣、海外與名單快照三個分頁,完成後用 Slack 通知。
背景與使用技術(4 項)
每個月寄給客戶的帳單信,總有一批寄不到而被退回。退信上沒有客戶名稱,唯一的線索是寫給人看的信件主旨,而且主旨有中英兩種格式。現在每月自動撈取上個月的退信、從主旨抽出客戶名稱、比對客戶名單,產出台灣與海外兩個分頁的月報,完成後以 Slack 通知。
誰在用
負責追帳單、請客戶更新聯絡資訊的營收營運團隊
原本怎麼做
每個月約有 150 封帳單信被退回,代表那位客戶收不到帳單,得有人找出是哪一家、去取得可用的信箱。退信不說是誰,唯一線索是主旨,同一位客戶在不同月份的寫法也不一致。台灣與海外的主旨格式不同,一條規則讀不了兩種。人工比對會漏,也會重複算,而且認錯比漏掉更糟:把 A 客戶的退信算到 B 客戶頭上。
前後對比
量測:實測=有計時或有紀錄;估計=喜特先生推估;客戶自述=客戶回報;規格=設計上的固定條件;待補=尚未取得數字。
流程一眼看懂
退信郵件
人工看主旨比對
找出客戶、取得可用信箱
每天同步客戶名單
月初撈上月退信
比對客戶對照表
產出月報(3 個分頁)
Slack 通知
流程示意:依本案既有做法整理,非客戶實際畫面。
做了什麼
- 名單與報表分離:客戶資料天天變,報表一個月才跑一次,名單在背景同步。
- 地區在查詢層分開:兩組條件各對應一種主旨格式,不必逐封再判斷。
- 認不出就標 #N/A 並統計筆數,不硬猜。
- 每次新建一份試算表,不覆蓋既有資料。
- 執行接近時間上限時主動收手,並附上已建好的試算表連結。
程式碼與實作細節收在頁面下方,需要時再展開。
你的情況像這樣嗎?
- 你每個月要從一堆郵件裡人工找出是哪些客戶。
- 不同地區的格式不一樣,一條規則處理不了。
- 寧可標示「不確定」,也不能把 A 客戶算到 B 客戶頭上。
更多成果與運作條件
20 分鐘 → 1 分鐘(估計)
每月約 150 筆退信的處理時間
3 個分頁
台灣退信、海外退信與客戶名單快照,一次產出,供查核對不上的原因
趕在 30 天保存期限內
寄信服務只保留 30 天的寄送紀錄,所以固定月初執行,把當月退信留成自己的紀錄
以上為本案既有成果與運作條件;估計值依原始資料標示,其他專案需另行評估,並非通用成效保證。
退信月報的欄位
退信對回客戶名稱與 Branch ID;對不上就標 #N/A,留給同事查核。
畫面示意・範例資料,非客戶實際截圖。手機可左右滑動查看。
挑戰:原本遇到的困難
每個月約有一百五十封帳單信會被退回。退信代表那位客戶收不到帳單,得有人找出是哪一家,去取得可用的信箱。
- 退信不說是誰:唯一線索是主旨,同一位客戶在不同月份的寫法也不一致
- 兩種主旨格式:台灣是中文、客戶名稱在最前;海外是英文、客戶名稱在句尾。一條規則讀不了兩種
- 人工比對會漏,也會重複算
- 認錯比漏掉更糟:名單會變成打給客戶的電話,把 A 客戶的退信算到 B 客戶頭上
所以這套流程要守住的是:認出來的,一定是對的。
完整解決方案與設計取捨
拆成兩支各自排程的流程。updateCustomerList() 每天把客戶系統的最新名單同步進主試算表;processMailList() 於月初執行,撈上個月的退信、比對客戶對照表,產出一份新的月報試算表,含「台灣退信」「海外退信」兩個分頁與一份客戶名單快照,完成後以 Slack 通知。
核心設計有五點:
- 名單與報表分離:客戶資料天天變,報表一個月才跑一次。名單在背景同步,月初那次才不會用到過期資料
- 地區在查詢層分開:兩組查詢條件各自對應一種主旨格式,不必對每封信再判斷
- 認不出就標 #N/A:客戶名稱抽取或對照失敗時留下記號並統計筆數,不硬猜
- 每次新建一份試算表:不覆蓋既有資料,同一個月重跑只會多一份檔案
- 接近時間上限就收手:執行快到六分鐘上限時主動停止,並附上已建好的試算表連結
技術與實作細節
展開完整實作說明
每個月初,營收營運團隊(負責追帳單、請客戶更新聯絡資訊的同事)需要一份能直接打電話的名單。以前要有人登進寄信服務(公司寄帳單信用的服務)後台,把上個月的退信一筆筆撈出來,讀主旨、判斷是哪一位客戶,再貼進試算表。撈資料不難,難的是「對回客戶」這一步。
退信上為什麼看不出是哪一家
退信本身只是一個事實:某封信寄不到,被退回來。退信的欄位裡沒有客戶名稱,唯一藏著客戶名稱的地方是信件主旨。
主旨是寫給人看的,而且有兩種長相。台灣客戶收到的是中文主旨,客戶名稱擺在最前面,後面接帳單期間的日期;海外客戶收到的是一句英文,客戶名稱擺在句子的最後。人看哪一種都能一眼認出客戶名稱,程式在沒有人先把「怎麼讀這行字」寫成規則之前,兩種都認不出來。
圖:示意主旨,客戶名稱為範例。台灣的客戶名稱在第一個日期之前,海外的客戶名稱在 for 之後。
同一位客戶在不同月份、不同分支的主旨裡,寫法還常常不完全一致。所以人工比對會有兩種失誤:一種是漏掉,一種是把同一位客戶重複算成好幾家。
最難收拾的是第三種:認錯。這份名單最後會變成打給客戶的電話,把 A 客戶的退信算到 B 客戶頭上,錯誤就會一路帶進與客戶的對話。所以這套流程要守住的是「認出來的都是對的」,寧可漏判。
從主旨切出客戶名稱:兩種格式兩條規則
台灣主旨的客戶名稱是第一個日期之前的那一段,用一條 regex(正規表達式,用符號描述文字格式的比對語法)切出來。海外主旨的客戶名稱是 for 之後的全部,另外用一條規則處理。兩條規則各自都很乾淨,互換使用就全部錯,所以分開寫。兩種都抓不到時回傳空字串,寧可漏判,也不把不確定的結果寫進報表。
示意的程式如下,客戶名稱與主旨皆為範例:
const extractCustomerName_ = (subject) => {
if (!subject) return '';
const twMatch = subject.match(/^(.*?)\s\d{2}-/);
if (twMatch) return twMatch[1].trim();
const overseaMatch = subject.match(/Here's your invoice for (.+)$/);
if (overseaMatch) return overseaMatch[1].trim();
return '';
};地區在查詢條件裡就分開
台灣那一組查詢,用中文月份對照表(一月到十二月)拼出主旨的搜尋字串,只撈上個月的台灣帳單退信;海外那一組直接比對 Here's your invoice 開頭。同一支腳本、兩組查詢,地區就分開了,不必對每封信再做一次判斷。
報表輸出也保留這個切分,變成「台灣退信」與「海外退信」兩個分頁。兩份名單的跟進方式不同,比對不到的客戶名稱會標成 #N/A,不會硬猜。
分支代號要去掉第一個字
對照表是用計費系統(訂閱帳務服務)的客戶名單建成的 Map:key 是公司名稱,value 是客戶 ID。客戶 ID 前面帶一個前綴字元,真正拿去跟進的分支代號是去掉首字元之後的部分,也就是 customerId.substring(1)。原本這件事靠試算表的 XLOOKUP 加 MID 公式完成,現在改在程式裡一次算好。
對不到的標成 #N/A,並統計筆數。這個數字本身就是線索:它指出下次該補哪些名稱進對照表。
圖:示意資料,客戶名稱皆為範例。選取的 B4 是 #N/A,代表對照表沒有這個名稱,要補進名單。
同一位客戶只跟進一次:重複分支標黃
同一位客戶在一個月內退好幾封信很常見,但營收營運團隊只需要聯絡一次。Branch ID 欄掛了一條條件式格式:用 COUNTIF 數同一個值出現幾次,超過一次就標成黃底(#FFD966)。
示意:F 欄為 Branch ID,這條規則套用在 F2 起的每一列
=COUNTIF($F:$F, F2)>1撞到六分鐘上限,怎麼收手?
Apps Script 一次執行的上限是六分鐘。這支流程要依序撈台灣、撈海外、比對、寫入多個分頁,很容易撞到上限。每個吃重的階段開始前,都會呼叫 checkExecutionTime_ 檢查剩餘時間;不夠就主動停下,並把已建好的試算表網址附在錯誤訊息裡。中途停下,也不會留下一份沒人看得懂的半成品。
查寄信服務時,哪三個地方會踩到
寄信服務的寄送活動查詢語法有三個地方沒講就會踩到:
- 模糊比對要自己補上 %,否則 LIKE 對不到任何東西
- 要篩某一把 API key 寄出的信,得用 api_key_id(一串識別碼)
- 用 status="not_delivered" 一次涵蓋 bounce 與 drop,不必分開查
當時的錯誤是 LIKE 條件沒有補上 %,查詢回傳的是空結果。查詢本身沒有報錯,看起來就像上個月沒有退信,所以一開始沒有人懷疑查詢條件。
分頁也要處理。寄信服務單頁最多 1000 筆,要以前一頁最後一筆的 last_event_time 當作 next_token 往後翻;計費系統的客戶每頁 100 筆。每翻一頁之間停 1500 毫秒(Utilities.sleep(1500)),避免撞到速率限制。所以某個月退信多到一頁裝不下時,資料仍會完整拉回來,不會悄悄停在第一頁。
歸檔留給人,重跑只多一份檔案
月報完成後,歸檔刻意留給人做。通知裡附上資料夾連結,收到通知的人把檔案拖進共用資料夾。接下來要用這份名單的本來就是同一個人,多寫一段搬檔的程式不划算。
月報也沒有防重跑機制:每次執行都建一份新試算表,不覆蓋既有資料,同一個月重跑只會多一份檔案,刪掉即可,不值得為它另外維護一份執行狀態。
完成後會發送 Slack 私訊通知,內容包含台灣與海外的筆數、總筆數,以及試算表連結與 Drive 資料夾連結。寄信服務的寄送活動只保留 30 天,過期就查不到,所以月報固定在月初跑,資料消失前把當月退信抄成自己留得住的紀錄。
上線後還沒量到的事
- 20 分鐘降到 1 分鐘是估計值,尚未實測
- 對照表未命中的筆數,逐月趨勢尚未整理
- 把未命中的名稱補進名單後,下個月的命中率尚未實測