今までの記事一覧
Googleフォーム×GASで実現する業務自動化シリーズ全記事まとめ目次
前提
この記事は、フォーム回答を保存しているスプレッドシート側のGASを前提にしています。
トリガーは以下を設定してください。
- フォーム送信時に実行する関数:
onFormSubmit - イベントのソース:スプレッドシートから
- イベントの種類:フォーム送信時
スプレッドシート構成
スプレッドシート「集計シート」をこの図のように設定しています。
- A1:D6 セル:商品マスタ(商品コードや現在の残数が入っている)
- 11行目以下:フォームから送信された注文の受付内容が記録される
- 図の赤字の内容の数式が入っている
※以前作ったこの記事の数式をそのまま生かしています。ただし、変更フォームによって在庫不足による注文数の修正が発生したときのために、有効注文数の数式を少しだけ変更し=IF(ISNUMBER(修正数のセル), 修正数のセル, 注文数のセル)としておきます。こうすることで、『修正数』の欄に修正後の確定数を書き込むだけで、有効注文数も、商品マスタの残数も、シート側で全自動で連動して計算されるようになります。
おさらい
前回は、GASを使ってGoogleフォームの受付を停止したり再開したりする処理を作りました。
あとはトリガーで動かすだけ.....でもその前に.....
毎朝の受付を開始する前に、前日の注文データをどう扱うか考える必要があります。
集計シートをそのまま使い続けると、前日のデータが残ってしまいます。
そこで、1日の終わりに注文履歴シートへ退避し、集計シートをクリアする運用にしたいと思います。
今回のミッション
- スプレッドシート上にあらかじめ「注文履歴」シートを作成しておく。
1行目の項目は「集計シート」のデータをそのままコピペできるよう、このような項目にしておくといいでしょう。
A1. 日時
B1. メールアドレス
C1. 氏名
D1. 注文数_ハンバーグ弁当
E1. 修正数_ハンバーグ弁当
F1. 有効注文数_ハンバーグ弁当
・
・
・
M1. 変更フォームの事前入力URL
「注文数_商品名」「修正数_商品名」「有効注文数_商品名」にしておくと後で集計やピボットテーブルを作成するときに便利です。
- フォームからの注文を受け付ける「集計シート」の注文データを「注文履歴」シートに追加する。
- 「集計シート」のデータ部分(11行目以降)をクリアする。
※「集計シート」のデータをクリアすることで、シート上の関数によって自動的に残数がリセットされます。
※「集計シート」だとネーミングがややこしいので「日次注文」などに名前を変えてもいいかもしれません。その場合はGASコードでもこのように変えてください。
const sheet_summary = ss.getSheetByName("日次注文");//"集計シート"から変更
この記事ではこのまま「集計シート」とします。
今回のキモはコレ!
データ範囲を変数化
「集計シート」の11行目以降にフォームからのデータが入るので、11行目から最終行までの範囲を変数 dataRange に格納します。データ範囲を変数化しておくことで、「データの取得」と「データのクリア」の両方で使い回せるため便利です。
- データ取得時(11行目から取得するため、行数は
最終行 - 10となります)
if (summaryLastRow >= 11) {
const summaryLastCol = sheet_summary.getLastColumn();
//11行目から最終行(行数は summaryLastRow - 10)
const dataRange = sheet_summary.getRange(11, 1, summaryLastRow - 10, summaryLastCol);
const data = dataRange.getValues();
- データクリア時
dataRange.clearContent();
サブ関数 updateFormOptions() を活用
以前の記事で作ったサブ関数 updateFormOptions() をそのまま使います。
このサブ関数は、シートの商品マスタから残数を抽出してフォームを更新するものです。シート上のデータをクリアすると、数式によってシート上の残数がリセットされます。その状態で updateFormOptions() を実行することで、自動的にフォーム上の選択肢・残数も初期状態へとリセットされる仕組みです。また、このサブ関数内では残数に応じてフォームの受付停止・再開も自動で行っています。
※このように汎用的なサブ関数を作っておくと、コードの重複を減らせるだけでなく、修正が必要になった場合も1か所を直すだけで済みます。
GASコード
メイン関数
//★グローバルスコープ
const form = FormApp.openById("1******"); // 注文フォームの編集用IDを入れる
const corForm = FormApp.openById("1######"); // 変更フォームの編集用IDを入れる
function summaryCopy() {
const ss = SpreadsheetApp.getActiveSpreadsheet();
// 1. 集計シートの注文データを注文履歴シートにコピー
//集計シートの最終行と最終列取得し、注文内容を変数dataに入れる
const sheet_summary = ss.getSheetByName("集計シート");
const summaryLastRow = sheet_summary
.getRange(sheet_summary.getMaxRows(), 1)
.getNextDataCell(SpreadsheetApp.Direction.UP)
.getRow();
//11行目以降に注文データがある場合(注文がある場合)
if (summaryLastRow >= 11) {
const summaryLastCol = sheet_summary.getLastColumn();
const dataRange = sheet_summary.getRange(11, 1, summaryLastRow - 10, summaryLastCol);
const data = dataRange.getValues();
//注文履歴シートの最終行以降に変数dataの内容を入れる
const sheet_history = ss.getSheetByName("注文履歴");
const historyLastRow = sheet_history.getLastRow();
sheet_history.getRange(historyLastRow + 1, 1, data.length, data[0].length).setValues(data);
// 2. 集計シートの注文データを消去する
dataRange.clearContent();
}
// 3. フォームの残数表示・受付状態をリセット
//以前作った【サブ関数】(引数を省略すると、自動で itemMaster を取得するように作ってある)
updateFormOptions();
// 4. 締め切りトリガーをセット
//次回以降実装予定
}
サブ関数
function updateFormOptions(itemMaster) {
//手動更新等で itemMaster が渡されない場合はここで取得
if (!itemMaster) {
const sheet_summary = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("集計シート");
//シートから手動で更新した場合(スマホからも含む)は、念のためスプレッドシートを強制再計算させる
SpreadsheetApp.flush();
// B1から右方向のデータ終端(列数取得用)
const summaryLastCol = sheet_summary.getRange("B1").getNextDataCell(SpreadsheetApp.Direction.NEXT).getColumn();
// 上部で宣言された引数の変数(itemMaster)にそのままデータを代入する
itemMaster = sheet_summary.getRange(1, 1, 6, summaryLastCol).getValues();
}
// マスタの列数(B列〜最終列)を取得
const summaryLastCol = itemMaster[0].length;
const formDescription = ["【現在の残数状況】"];
let sumStockNum = 0; //商品全部の残数を足したもの
// 2. 商品ごとにループ処理(B列=インデックス1 から開始)
for (let col = 1; col < summaryLastCol; col++) {
const productName = itemMaster[0][col]; // 1行目:商品名
const questionId = itemMaster[2][col]; // 3行目:フォームの質問ID
const stockNum = Number(itemMaster[5][col]); // 6行目:最新の残数
// 質問IDが空欄の場合はスキップ(エラー防止)
if (!questionId) continue;
try {
sumStockNum += stockNum; //商品全部の残数を足していく
// 3. 質問IDを元に、フォームから対象のプルダウン(ListItem)を取得
const listItem = form.getItemById(questionId).asListItem();
// 4. 残数に応じた選択肢(choices)を動的に組み立てる
const choices = []; //フォームの選択肢を入れる配列
if (stockNum <= 0) {
// 在庫がない場合は「0」にする(万が一マイナスになっても表示は0)
choices.push("0");
} else {
// 在庫がある場合は 1 〜 残数 までの数字を選択肢にする
// (0個も選択肢に入れたい場合は i = 0 からスタート)
for (let i = 1; i <= stockNum; i++) {
choices.push(String(i));
}
}
// 5. フォームのプルダウン選択肢を更新
listItem.setChoiceValues(choices);
// 質問のタイトルやヘルプテキスト(説明欄)も更新したい場合はここで変更可能
listItem.setHelpText(`残りあと ${stockNum} 個です`);
// 6. フォームタイトル下の説明欄を更新
formDescription.push(productName + ":" + stockNum);
} catch (e) {
Logger.log(`質問ID [${questionId}] の更新中にエラーが発生しました: ${e.message}`);
}
}
// フォームタイトル下の説明欄を書き換え
const formDescriptionText = formDescription.join("\n");
form.setDescription(formDescriptionText); //注文フォーム
corForm.setDescription(formDescriptionText + "\n ※元々のご自身の注文数にこの数を足したものが、注文可能数です。"); //変更フォーム
if (sumStockNum === 0) { //もし商品全部の残数(sumStockNum)が0だったらフォーム受付停止
if (form.isAcceptingResponses() === true) { // フォームがまだ受け付けていたら
form.setCustomClosedFormMessage("本日の注文受付は【完売】のため終了いたしました。またのご利用をお待ちしております!");
form.setAcceptingResponses(false); // 新規受付を停止
}
} else {
//フォームが受付停止になっていたら再開
if (form.isAcceptingResponses() === false) {
form.setAcceptingResponses(true);
}
//【サブ関数】売り切れ商品を売り切れセクションに移動
moveSoldoutItem(itemMaster)
}
return (formDescriptionText);
}
動作確認
それでは実行してみましょう。
この状態のスプレッドシートから開始します。注文は3件入っています。残数はすべて0になっているため、フォームは受付停止している状態です。
実行すると、集計シートの11行目以下がクリアされ、残数は初期値(10個)に戻っています。
こちらは「注文履歴」シートです。2行目以下にデータが転記されています。
フォームは受付再開されています。残数もリセットされています。
まとめ
今回は、日次集計用の「集計シート」をリセットし、「注文履歴」シートにデータを蓄積すると同時に、フォームの残数も自動でリセットする処理を作りました。
「注文履歴」シートへデータを蓄積することで、月次の集計や、ピボットテーブルを使った商品別・利用者別の集計などもしやすくなりました。
あとはこの処理をトリガーで毎日自動実行できるように設定すれば完成です。
次回はトリガーもGASで自動設定する方法を解説していきたいと思います。
今回の成果コードは、GASコード を参照してください。
次回の記事はこちら
Googleフォーム×在庫管理!日次運用③特定日時に発動するトリガーを毎日自動作成する
