昨天完成結算快照後,今天終於開始處理這個專案一開始就想解決的問題:知道誰欠誰多少錢之後,實際應該拿出哪些紙鈔和硬幣?例如應付 150 元的人只有兩張一百元,收款人剛好有一枚五十元,就可以先付 200 元,再找回 50 元。這個問題需要同時考慮付款與找零雙方的庫存,不能只把應付金額拆成常見面額。
我把今天的範圍定為淨額抵銷後只剩一位付款人、一位收款人的情況。帳本可以有其他成員,但如果有三位以上仍有應收應付,系統會明確顯示多人交付尚待下一階段處理。因為一個人可能必須先收到零錢,才能在下一筆拿出來付款,多人問題還牽涉順序,不能把幾筆各自可行的結果直接拼成整體方案。
計算前,系統會重新檢查成員資格以及快照是否仍有效。應收應付來自每個人的代墊總額減去自己的分攤總額,不會把每張帳單都當成獨立欠款。若所有淨額都抵銷,直接顯示不需要交付;若有人修改現金、帳目或成員,舊快照不能繼續拿來計算,必須重新核對、確認與鎖定。
找零演算法使用有限庫存的動態規劃。對每個可能的金額,記錄能否湊出以及最少需要多少張或枚,並保存對應的面額組合。付款人的每個可行交付金額,都會配對收款人能否湊出差額。例如付款 200、應付 150,就要求收款人能從自己的原有庫存拿出 50。程式不會假設有無限枚十元,也不會借用未參與者的錢包。
「最合適」也需要有明確定義。今天先減少交付方向:剛好付清的一次交付優先於付款後還要找零的兩次交付;方向數相同時,再減少双方合計的紙鈔與硬幣數量。若仍相同,採固定搜尋順序決定結果。因此即使四枚十元比一枚五十元加找回一枚十元的張枚數更多,應付四十元時仍會優先選直接交四枚十元。這是產品選擇,不是所有人都認同的唯一標準。
如果付款人的現金總額不足,系統直接說明目前庫存無解;總額足夠但雙方無法湊出付款與找零,也會回報沒有可行方案。另一方面,若完整可能付款範圍超過 20,000 元,今天的版本會回報搜尋未完成,而不宣稱無解。只有完整搜尋了支援範圍,才會把結果稱為目前兩人、原有庫存条件下的最佳方案,並不宣稱多人全域最佳。
介面加在結算頁下方的「付多少、找多少」。只有有效快照時才能按下計算,結果用兩個步驟說明誰給誰多少,以及實際拿出的面額。如果不需要找零,就只顯示剛好付款。這裡也補充了隱私界線:完整錢包仍不公開,但為了實際交付,付款與收款雙方能看到方案中使用的面額。與這筆交付無關的其他成員,即使在同一本帳本裡,也不能查看這些交付面額。
今天通過 56 項嵌入式 PostgreSQL 檢查、13 項結算與找零模擬介面檢查,以及 8 項共同帳單回歸檢查,共 77 項。SQL 檢查中有一項以 81 組小庫存進行完整枚舉,逐一比對每個可達金額的最少張枚數,以及輸出組合沒有超出庫存。其他案例包含付 200 找 50、精確付款優先、面額不足、現金總額不足、搜尋上限、淨額抵銷、多人狀態、失效快照和存取權限。
今天完成的是程式、離線驗證與文章,新的 SQL 還沒有套用到 Supabase,也尚未完成找零功能的真實雙帳號操作。這次試算不會扣除現金、不會改變付款狀態,更不代表錢已經交付。下一天會接多人交付順序與逐步庫存驗證,讓方案不只最後加總正確,也能依照每一步實際執行。