倉管同事在「出貨單」把狀態改成「已出貨」,出貨日卻常常忘了填。希望狀態一變,同一列的出貨日自己出現。
這件事不用人按按鈕,也不用排程:Apps Script 有一種簡單觸發器 onEdit,有人改了儲存格,Google 就會自動呼叫它。
先看流程
程式要回答的只有一題:這次被改的那一格,是不是我要處理的那一格? 不是就什麼都不做。
先準備
- 「出貨單」分頁:
A1:F1是表頭,E欄是狀態(用「插入 → 下拉式選單」設成「待出貨」「已出貨」),F欄是出貨日。 - 在這份試算表選「擴充功能 → Apps Script」,把下面三個區塊貼進去並儲存。
onEdit是固定名稱的簡單觸發器,不需要另外建立觸發器。- 要測試時,到試算表實際改一格 E 欄才會觸發。在 Apps Script 編輯器直接按「執行」
onEdit,e會是undefined,只會看到錯誤訊息,不代表程式有問題。
區塊一:把會變的東西放在最上面
/**
* @OnlyCurrentDoc
*/
const SHEET_NAME = "出貨單";
const FIRST_DATA_ROW = 2; // 第 1 列是表頭
const STATUS_COL = 5; // E 欄:狀態
const DATE_COL = 6; // F 欄:出貨日
const DONE_TEXT = "已出貨";- 最上面的
@OnlyCurrentDoc註解,宣告這支程式只會用到目前這份試算表。 - 分頁名稱、欄位位置、關鍵字集中在這幾行,之後換情境只改這裡。
- 欄位用數字:
E是第 5 欄、F是第 6 欄。 - 第 1 列是表頭,所以資料從第 2 列開始。
區塊二:判斷要不要動
// 只在「出貨單」資料列的 E 欄被單格改成「已出貨」,而且 F 欄還是空的時候才回 true
function shouldStampShipDate(info, currentDate) {
const isTarget = info.sheetName === SHEET_NAME && info.row >= FIRST_DATA_ROW && info.col === STATUS_COL;
const isSingleCell = info.numRows === 1 && info.numColumns === 1;
const isEmptyDate = currentDate === "" || currentDate === null || currentDate === undefined;
return isTarget && isSingleCell && info.value === DONE_TEXT && isEmptyDate;
}- 輸入是一包描述「這次編輯」的資料
info,加上 F 欄現有的值;輸出只有true或false。 - 它不碰試算表,所以可以在 Google 以外直接測。
- 六個條件要同時成立:在「出貨單」、第 2 列以後、E 欄、只改一格、新值是「已出貨」、F 欄是空的。
區塊三:onEdit 取資料、寫入
// 簡單觸發器:使用者編輯儲存格後,Google 會自動呼叫,並傳入事件物件 e
function onEdit(e) {
const range = e.range;
const sheet = range.getSheet();
const info = {
sheetName: sheet.getName(),
row: range.getRow(),
col: range.getColumn(),
numRows: range.getNumRows(),
numColumns: range.getNumColumns(),
value: e.value,
};
const dateCell = sheet.getRange(info.row, DATE_COL);
if (shouldStampShipDate(info, dateCell.getValue())) {
dateCell.setValue(new Date());
}
}e是 Google 傳進來的事件物件,e.range是被改的範圍,e.value是改完的值。- 先組出
info,再讀同一列 F 欄現有的值,交給判斷函式。 - 只有回傳
true,才對那一格setValue(new Date())。 new Date()是日期加時間。F 欄若沒有設定格式,儲存格通常會顯示日期加時間;想只顯示日期,先把 F 欄設成日期格式(格式 → 數字 → 日期)。之後要用日期比對或統計時,用INT()取出整數日期。
事件物件 e 有什麼
| 屬性 | 內容 | 這篇用到嗎 |
|---|---|---|
e.range | 被編輯的儲存格範圍 | 用到,取得分頁、列、欄、大小 |
e.value | 編輯後的值,只有改單一儲存格時才有 | 用到 |
e.oldValue | 編輯前的值,同樣只有單格;原本空白時是 undefined | 沒用到 |
e.source | 綁定的試算表 | 沒用到 |
e.user | 目前使用者,受安全限制,不一定取得 | 沒用到 |
為什麼要排除多格與覆蓋
- 多格貼上:一次貼上很多格時,
e.value不會有值,也分不出每一格原本是什麼。保守的做法是整批不動,需要日期時要手動補。 - 已有日期:狀態從「已出貨」改回「待出貨」再選回「已出貨」時,如果直接填日期,原本的出貨日會被換成今天。F 欄有值就不寫,可以保留第一次的紀錄。
- 清空:按 Delete 清掉狀態時,新值不會是「已出貨」,自然不會動。
- 程式自己寫的不會再觸發:腳本用
setValue寫 F 欄,官方文件說明這不會再觸發onEdit,所以不會無限循環。
前後對照
下表是示意用的「出貨單」改之前的樣子。假設把 S002 的狀態改成「已出貨」(這張表不會執行程式,改了也不會出現日期):
| 列 / 欄 | A | B | C | D | E | F | G |
|---|---|---|---|---|---|---|---|
| 1 | 訂單編號 | 客戶 | 品項 | 數量 | 狀態 | 出貨日 | |
| 2 | S001 | 王小明 | 咖啡豆 | 2 | 已出貨 | 2026/10/16 | |
| 3 | S002 | 李小華 | 濾紙 | 5 | 待出貨 | ||
| 4 | S003 | 陳大文 | 手沖壺 | 1 | 待出貨 | ||
| 5 | S004 | 林小美 | 磨豆機 | 1 | 待出貨 | ||
| 6 | S005 | 張三 | 咖啡豆 | 3 | 待出貨 | ||
| 7 | |||||||
| 8 |
可直接編輯儲存格 • 重新整理即還原
在真正的試算表裡,改完後 S002 的 F 欄會自動出現當天日期(下表是 2026/10/17 當天、F 欄已設成日期格式的結果;沒設格式時會多顯示時間),S001 原本的日期不變;這張表只是預先寫好的結果,不會自己跑程式:
| 列 / 欄 | A | B | C | D | E | F |
|---|---|---|---|---|---|---|
| 1 | 訂單編號 | 客戶 | 品項 | 數量 | 狀態 | 出貨日 |
| 2 | S001 | 王小明 | 咖啡豆 | 2 | 已出貨 | 2026/10/16 |
| 3 | S002 | 李小華 | 濾紙 | 5 | 已出貨 | 2026/10/17 |
| 4 | S003 | 陳大文 | 手沖壺 | 1 | 待出貨 | |
| 5 | S004 | 林小美 | 磨豆機 | 1 | 待出貨 | |
| 6 | S005 | 張三 | 咖啡豆 | 3 | 待出貨 |
示範試算表
換成你的情境
- 分頁名稱:
SHEET_NAME。 - 狀態在哪一欄、日期要填哪一欄:
STATUS_COL、DATE_COL。 - 關鍵字:
DONE_TEXT,改成「已完成」「已寄出」都可以。 - 表頭佔幾列:
FIRST_DATA_ROW。
改成「已完成」填完成日
同一個出貨單,現在要在 E 欄變成「已完成」時,把完成日填在 G 欄(第 7 欄)。先預測:要改哪幾個常數?哪幾個不用改?(E 欄的下拉選單另外要加「已完成」。)
延伸閱讀
這支程式只處理用下拉選單改單一儲存格狀態的情境;貼上多格、清空、改其他欄與其他分頁,都不會寫入日期。簡單觸發器不能使用需要授權的服務(例如寄信),執行時間上限 30 秒。
接下來可以
相關文章
查看所有文章資料驗證入門:下拉式選單、數字範圍、核取方塊怎麼設
從「資料」>「資料驗證」開始,一步一步設定下拉式選單(手動輸入選項)、數字介於某個範圍、核取方塊,說明拒絕輸入與顯示警告的差別,以及規則管不到舊資料與貼上的內容。介面字樣對照 Google 說明中心。
日期與時間入門:日期序號、DATE、TODAY 與文字日期
從日期序號開始,認識 DATE、TODAY、NOW、YEAR、MONTH、DAY,學會日期加減、分辨文字日期和真日期,並搞清楚顯示格式和儲存格的值是兩回事。
Apps Script 時間觸發器:每週一早上自動寄出值班表
用時間觸發器讓試算表每週一自動寄出值班表:先算出這一週的週一、找到對應的列、再寄信;也示範安裝與清理觸發器,重複安裝只留一個。附授權與執行身分說明。
留言討論
共 0 則留言
