報價單顯示「已寄出」,客戶收件匣卻是空的:ERP 寄信的假成功與五步驗收
2026 年 8 月 13 日,一個客戶站的專案經理在正式環境測試「寄出客戶報價」:收件人填自己的信箱,按下確定,畫面顯示「已寄出客戶報價,訂單進入『待客戶確認』」,單子的站點也真的往前推了一格。但收件匣什麼都沒有。這篇記錄那封信去了哪裡、為什麼系統會說它寄出去了,以及我們當天怎麼改,最後附一張驗收任何一套 ERP 寄信功能都能用的五步測試。
先講結論
- 那封信沒有寄出,被寫成一個
.eml檔案放在伺服器上。這是給開發測試用的備援,在正式站被當成了「寄信成功」。 - 真正的傷害不在少一封信,而在業務會在「待客戶確認」站等一個永遠不會來的回覆,系統還留下一筆「已寄出」的進度備註背書。
- 修法:沒有設定郵件伺服器時,寄信一律失敗並說出原因;失敗時整筆交易回滾,站點不推進、不寫寄出時間、不留備註。
- 「寄出成功」在我們系統裡的意思是郵件伺服器收下了這封信,不等於進了對方收件匣。這個界線寫清楚,比假裝做得到更重要。
- 驗收任何 ERP 的寄信功能,至少要做「拔掉設定再寄一次」這一步。只測成功路徑的話,這個問題測不出來。
一、那封信去了哪裡
拓 ERP 決定「用什麼管道寄信」時,會依序找三個地方的郵件伺服器(SMTP)設定:伺服器的環境變數、系統設定裡的郵件伺服器資料表、以及系統參數。三個都沒有的時候,原本的程式會退到第四層:把整封信寫成一個 .eml 檔案,存在伺服器的資料目錄裡。
第四層的原始用途很正當。本機開發與自動化測試不該真的寄信給任何人,寫成檔案讓測試程式打開來檢查內容剛好。程式碼註解裡也寫著「除錯用」。
問題是這個客戶站在正式環境上從來沒有設過郵件伺服器,所以每一封信都安靜地掉進了第四層。
二、為什麼系統說它寄出去了
寫檔成功時,那一層回傳的是「沒有錯誤」。報價單寄出的流程只檢查一件事:寄信有沒有回錯誤。沒有,就接著做後面三件:
- 寫入寄出時間與收件人;
- 把訂單站點推進到「待客戶確認」;
- 在進度紀錄留下一筆「報價單已寄給某某」。
每一步單獨看都沒有寫錯。錯在「寫成檔案」與「寄出去了」用的是同一個回傳值,呼叫端分不出來。換句話說,系統沒有說謊的意圖,它只是沒有被設計成能說「我沒寄」。
三、假成功比明確失敗糟在哪
如果當時畫面跳出「寄信失敗」,專案經理會去找設定,最多損失十分鐘。假成功的成本是延後出現的,而且會擴散:
- 業務會等。站點寫著「待客戶確認」,業務的合理反應是等客戶回覆。客戶那邊什麼都沒收到,也不會回。
- 紀錄會替錯誤背書。進度備註寫著「已寄給某某」,日後有人追問,翻紀錄的結論會是「我們寄了,是客戶沒看」。
- 問題要等到人發現才會浮現。這次是專案經理寄給自己才看出來。如果收件人一直是客戶,可能要等到客戶打電話問「報價呢」。
所以這張單排在同一批另外五張待修單前面,當天處理。
四、當天的修法
修正在同一天完成,核心只有一條規則:三層設定都沒有時,寄信直接失敗,畫面顯示白話的原因(未設定郵件伺服器,請先到設定填寫),不再寫檔。
站點與寄信綁在一起這件事,原本其實已經做到了。拓 ERP 每個請求是一筆資料庫交易,成功才寫入;寄出時間、站點推進、進度備註本來就在同一筆交易裡。缺的只是「寄信真的會失敗」。寄信一失敗,整筆回滾,三件事一件都不會留下。
開發與測試仍然需要寫檔模式,所以改成要明確打開:設定 TUOERP_MAIL_OUTBOX=1 才會寫檔。本機開發環境與 CI 的端對端測試都加上了這個變數;兩份正式環境的部署設定都沒有,預設就是嚴格模式。
驗證方式是另外啟動一台沒開寫檔模式的伺服器,照票面的案例實際按一次:錯誤訊息正確、寄出時間是空的、單子留在報價草稿、進度紀錄零筆。既有的「寄信失敗保留草稿」測試也補了一條斷言:失敗時不可以留下「已寄出」的備註。
這個改動有一個部署時要先講清楚的副作用:其他沒設郵件伺服器的站,按寄出會從「靜靜沒收到」變成「當場報錯」。對系統來說這是修正,對使用者來說是一個沒見過的錯誤畫面。
五、設好了還是寄不出去:寄件人預設值
修這張單時順便發現另一個問題。正式環境的部署設定原本只帶了郵件伺服器的主機、埠號、帳號、密碼四個值,沒有帶寄件人。沒設寄件人時,系統預設用 no-reply@localhost。
多數郵件服務看到寄件人網域是 localhost,會直接退信或丟進垃圾信匣。也就是說,就算客戶把郵件伺服器設好了,信還是可能寄不到,只是換成另一種失敗。部署設定補上寄件人欄位之後,這條路才算真的通。
9 月初我們又加了一層:使用者可以連結自己的 Gmail,寄報價時用業務本人的信箱寄出(OAuth 授權,系統不存密碼)。本人授權失效時退回全站設定;全站也沒設,就照第四節的規則報錯。
六、「寄出成功」到底保證了什麼
修完之後,「寄出成功」的意思是:我們設定的郵件伺服器接受了這封信。這是 SMTP 協定能給的保證,但它不保證以下幾件事:
- 對方的郵件伺服器之後沒有退信;
- 信沒有被分到垃圾信匣;
- 收件人打開了。
退信是事後由對方的郵件伺服器寄回寄件人信箱,不會回到 ERP 的畫面上。所以用業務本人的 Gmail 寄有一個附帶好處:退信會回到業務自己的收件匣。用 no-reply 類的系統信箱寄,退信就沒有人看。
所以畫面上的用詞維持「已寄出」,沒有寫成「已送達」。跟第二節是同一個教訓:系統能確認的範圍到哪,畫面就只說到哪。
七、驗收 ERP 寄信功能的五步測試
不論哪一套系統,寄報價、寄發票、寄對帳單的功能都可以用這五步驗收。前兩步大家都會做,第三步之後才是這次踩到的地方:
- 寄給自己。確認收得到、主旨與附件正確。
- 看寄件人。收到的信寄件人是誰?是公司網域、業務本人,還是
no-reply@某某?後者的退信沒人收。 - 拔掉設定再寄一次。請廠商在測試環境把郵件伺服器設定清空,再按寄出。畫面應該報錯,單據狀態應該不動。如果顯示成功,就是這篇講的問題。
- 填錯密碼再寄一次。錯誤訊息要能讓非工程師看懂下一步該做什麼。
- 寄給一個不存在的信箱。系統會顯示成功(這是正常的),然後去寄件人信箱看退信有沒有回來、回到誰那裡。
第三步最容易被跳過,因為它要求廠商刻意弄壞自己的系統。但正式環境的設定一定會在某一天缺掉或過期,到那天系統怎麼反應,只有這一步測得出來。
常見問題
ERP 顯示報價單已寄出,客戶卻說沒收到,先查什麼?
先查三件事:系統到底有沒有設定郵件伺服器、寄件人是哪個信箱、寄件人信箱有沒有收到退信。如果系統在沒有郵件伺服器時仍回報成功,信可能根本沒有離開伺服器;如果寄件人是 no-reply 或 localhost 這類網域,信多半被對方退回或判為垃圾信。
寄信失敗時,訂單狀態應該怎麼處理?
寄信失敗時,寄出時間、狀態推進與「已寄出」的紀錄都不應該留下。拓 ERP 把這三件事放在同一筆資料庫交易裡,寄信一失敗整筆回滾,單子留在報價草稿,使用者會看到白話的錯誤原因。
系統顯示寄出成功,代表客戶一定收到了嗎?
不代表。寄出成功只表示我們設定的郵件伺服器接受了這封信。對方伺服器之後仍可能退信,信也可能被分進垃圾信匣。退信會寄回寄件人信箱,所以用業務本人的信箱寄出,退信才有人看得到。
驗收 ERP 寄信功能時最容易漏掉哪一步?
把郵件伺服器設定清空再寄一次。正確的反應是畫面報錯、單據狀態不動;如果顯示成功,系統就有假成功的問題。這一步要請廠商在測試環境刻意弄壞設定,所以最常被跳過。
想看看這些在實際系統裡長什麼樣?
拓 ERP 把進銷存、會計四表、401 申報所需明細與電子發票處理放在同一套資料上。申請試用可取得 15 天完整功能的體驗環境。