別怕!水電材料採購懶人整合表——庫存、付款條件、時間成本一把抓
做過工程現場或幫忙管採購的朋友一定懂,每天材料進進出出,付款條件還五花八門,採購人員還常常被叫去對庫存,時間都在碎裂的 Excel、LINE、電話中流掉。我之前幫朋友協助場務,就碰過現場找不到最新庫存、老闆急問還要翻半天的窘境。有時表單填完還要自己算付款日或比價,超費時!這個工具,就是來幫大家省事,讓你用 Google 表單,一次搞定材料採購、庫存整合、付款狀態,還幫你記錄每次操作所花時間,採購人員超有感!
這段程式碼直接複製就能用!
只要填寫材料資料、庫存變動、付款方式和工作時間,表單自動整理統計,省去來回比對的時間。
// === 水電材料採購整合工具 ===
function doGet(e) {
var html = [];
html.push('<div style="max-width:550px;margin:30px auto;padding:20px;'
+ 'background:#f4f8fc;border-radius:12px;">');
html.push('<h2 style="color:#2367a0;">材料採購&庫存一站式填報</h2>');
html.push('<form id="matForm">');
// 材料名稱
html.push('<label>材料名稱:<input type="text" name="matName" required></label><br>');
// 單位
html.push('<label>單位:<input type="text" name="unit" style="width:50px;"></label><br>');
// 數量(正可為採購,負為領用)
html.push('<label>數量(進為+,領出為-)<input type="number"'
+ 'name="qty" required></label><br>');
// 單價
html.push('<label>單價(元):<input type="number" name="price" min="0" step="1"></label><br>');
// 供應商
html.push('<label>供應商:<input type="text" name="supplier"></label><br>');
// 付款條件
html.push('<label>付款條件:<select name="payTerm">'
+ '<option value="現金">現金</option>'
+ '<option value="月結30天">月結30天</option>'
+ '<option value="票期60天">票期60天</option>'
+ '<option value="其他">其他</option>'
+ '</select></label><br>');
// 預計付款日自動顯示
html.push('<label>採購日期:<input type="date" name="buyDate" required></label><br>');
// 採購人
html.push('<label>採購人:<input type="text" name="buyer"></label><br>');
// 處理時間(分鐘)
html.push('<label>這次花了幾分鐘填單?<input type="number" name="min" min="1" required></label><br>');
// 備註
html.push('<label>備註:<input type="text" name="note" style="width:300px;"></label><br>');
html.push('<button type="submit">提交/新增</button>');
html.push('</form>');
// 統計庫存+付款狀態
html.push('<hr><div id="stat"><em>載入庫存中...</em></div>');
// refresh btn
html.push('<button onclick="window.location.reload()" '
+ 'style="margin-top:12px;">手動更新</button>');
html.push('<script>'
+ 'document.getElementById("matForm").onsubmit=function(e){'
+ 'e.preventDefault();var f=new FormData(this),o={};'
+ 'for(var [k,v]of f.entries())o[k]=v;'
+ 'fetch("?submit=1",{method:"POST",body:JSON.stringify(o)})'
+ '.then(x=>x.text()).then(()=>{alert("已新增!");window.location.reload()});};'
+ 'fetch("?show=1").then(x=>x.json()).then(function(arr){'
+ 'var tb="<table border=1 cellpadding=4><tr>'
+ "<th>材料</th><th>庫存</th><th>未付金額</th><th>最早付款日</th></tr>";'
+ 'arr.forEach(function(row){tb+="<tr><td>"+row[0]'
+ '"+</td><td>"+row[1]+"</td><td>"+row[2]'
+ '"+</td><td>"+row[3]+"</td></tr>";});'
+ 'tb+="</table>";document.getElementById("stat").innerHTML=tb;});'
+ '</script>');
html.push('</div>');
return HtmlService.createHtmlOutput(html.join(""));
}
function doPost(e) {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sh = ss.getSheetByName('採購資料')||ss.insertSheet('採購資料');
var data = JSON.parse(e.postData.contents);
var keys = ["材料名稱","單位","數量","單價","供應商",
"付款條件","採購日期","採購人","花費分鐘","備註"];
// 如果首列不存在就補上
if (sh.getLastRow()==0) sh.appendRow(keys);
var row = [data.matName||"", data.unit||"", Number(data.qty)||0, Number(data.price)||0,
data.supplier||"", data.payTerm||"", data.buyDate||"", data.buyer||"", Number(data.min)||0, data.note||""];
sh.appendRow(row);
return ContentService.createTextOutput('ok');
}
function doGetShow_() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sh = ss.getSheetByName('採購資料');
if (!sh) return ContentService.createTextOutput(JSON.stringify([]));
var dat = sh.getDataRange().getValues();
if (dat.length<2) return ContentService.createTextOutput(JSON.stringify([]));
var inv = {}; // 材料對應 {庫存,未付,最早付款日}
for (var i=1;i<dat.length;i++) {
var r = dat[i], mat = r[0], qty = Number(r[2])||0, amt = Number(r[3])||0, pay=r[5], dt=r[6];
if (!inv[mat]) inv[mat]=[0,0,null];
inv[mat][0] += qty;
// 假設領用就不算付款(只+採購且付款條件非現金)
if (qty>0 && pay!="現金") {
inv[mat][1] += amt*qty;
// 算付款日
var pd = new Date(dt);
if (pay.indexOf("30")>-1) pd.setDate(pd.getDate()+30);
else if (pay.indexOf("60")>-1) pd.setDate(pd.getDate()+60);
// 取最早付款日
var dstr = pd.toISOString().slice(0,10);
if (!inv[mat][2]||dstr<inv[mat][2]) inv[mat][2]=dstr;
}
}
// 輸出格式 [[材料,庫存,未付金額,最早付款日],...]
var res = [];
for (var k in inv) res.push([k, inv[k][0], inv[k][1], inv[k][2]||"-"]);
return ContentService.createTextOutput(JSON.stringify(res));
}
// 讓?show=1抓統計
function doGet(e){
if (e && e.parameter && e.parameter.show=="1") return doGetShow_();
return doGet_main();
}
function doGet_main(){ return doGet(); }
// END
超簡單!六步驟搞定部署
放心跟著來,一步步超穩!
- 打開 Apps Script 編輯器
先開啟 Google 試算表,然後在最上方選單找到「擴充功能」→「Apps Script」點下去。通常「擴充功能」在畫面中間偏右的位置。
這時會跳出新分頁,就是 Apps Script 的編輯器啦。
⚠️ 我自己公司帳號有時候沒權限進,這種要用自己私人帳號比較快,不然就請 IT 幫忙開權限。
- 全部清空貼上程式碼
看到編輯器中間那片大白,按 Ctrl+A 選全部,再按 Delete 刪掉。接著複製上面的程式碼(記得全選,不然容易漏),Ctrl+V 貼進去。
這樣「function myFunction()」就會消失,變成一大串新內容。
⚠️ 很多人只貼了一半,結果執行錯誤,建議貼好前後都檢查一下!
- 存檔專案
點左上角那個藍色磁碟片圖示(或直接 Ctrl+S),第一次存會跳出「請命名專案」,隨便取個名字即可。
存完檔右邊應該會閃一下,表示寫入成功。
⚠️ 我之前常常一忙忘存,直接去部署,結果沒反應,要重來一次才會好,超阿雜。
- 部署成網頁應用程式
看右上角,點「部署」藍色按鈕,選「新增部署作業」,接著會跳出設定小視窗:
1. 點左下齒輪選「網頁應用程式」
2. 執行身分選「我」
3. 誰可以存取選「任何人」
4. 點「部署」
這樣流程走完就快好了。
⚠️ 記得「誰可以存取」一定要選「任何人」,不然現場同事用不到會一直反映。
- 授權警告一條龍
會看到紅色警告頁,先別怕,這只是 Google 提醒你還沒驗證(自己寫的都這樣)。
點「進階」→「前往(不安全)」→「允許」。
完成後會自動回來 Apps Script 畫面。
⚠️ 第一次用 Apps Script 幾乎都會卡這裡。我在論壇看超多人問,其實就是權限提醒,不是什麼病毒,放心按「允許」沒事的!
- 拿網址直接用,想改要重新部署
成功授權後,系統會出現一串網址(https://script.google.com/...),複製下來,丟瀏覽器打開,就會看到剛剛的採購填報工具啦。
⚠️ 記得,每次有改程式碼都要重新部署一次,否則打開還是舊的喔!我一開始就被這個小陷阱搞得找不到原因。
⚠️ 關於紅色授權警告不用怕
這個紅色授權畫面常常嚇到新手,其實只要是自己寫的 Apps Script、還沒經過 Google 官方審核,都會跳這個畫面。它只是標準安全機制,提醒你這程式有權限動用 Google 文件,不代表危險。如果你是自己貼上/自己寫的,點「進階」→「允許」就對了。我有問過做 IT 朋友,大家都說只要來源確定沒問題,這步驟是必經的,不用擔心資料外洩!
這工具能怎麼用?來兩個真實例子!
1. 阿明是工地主任,平常每天都要對倉庫的庫存,還要定期匯報材料採購金額、未付金額。用這工具,他每天用手機記錄材料進出,下班時一點按鈕,統計表自動更新,省掉手算和反覆確認的時間。
2. 小美是採購人員,常被老闆臨時問「最近買的電線還剩幾捲?有哪幾筆還沒付款?」。以前要翻 Excel 找記錄,現在直接打開工具,查一查材料名馬上知道庫存和未付金額,還能說出下次付款日。連花了多少時間處理,她都能記錄給老闆看——從此工作有據可依,再也不怕臨時追問啦!