0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

「作った人がいなくても回るGAS」を作る:設定シート・実行ログ・要確認・テストで引き渡せる受注取り込み

0
Last updated at Posted at 2026-11-03

はじめに

最近は、非エンジニアがAIを使ってGASを書く場面が増えました。一方で、止まっても気づかない、作った本人しか直せないといった形でつまずくケースも多く見かけます。そこで、まずエンジニアが土台となる型を作り、それを引き渡すという方式を考えました。

題材は、卸の担当者がメールで受け取る注文書です。15分ごとに Gmail のラベルを読み、注文の明細をスプレッドシートに書き出し、怪しい注文は推測せずに「要確認」に回して、まとめて1通メールで知らせます。顧客・商品・メールはすべて架空です。

現場の悩みと設計判断

現場でよくある悩み このリポジトリでの判断
しきい値や通知先を変えるのに、コードを開く必要がある 設定は 設定シート(キー・値・説明の3列)に置く。値はスクリプトが確かめ、間違いはふつうの言葉で知らせる
夜中に止まっても誰も気づかない 実行のたびに 実行ログ シートへ1行書く。失敗と要確認は メールで通知
推測で取り込んで、違う商品が出荷される 読み切れないもの(未登録のSKU、多すぎる数量、顧客コードなし、読めない行)があれば、注文ごと要確認 に回す。黙って捨てない
再実行や実行の重なりで、同じ注文が2回書かれる スクリプトロック で重なりを止め、書き込んでからラベルを付け替える。シートにあるメールIDは読み飛ばす
件数が多い日に6分の制限で止まる 古い順に読み、1回あたりの上限 を超えた分は次の実行に回す
作った人しか直せない TypeScript とテスト・CI、clasp で git から配布。運用ガイド と 引き継ぎチェックリスト を同梱

作り方:AIとの分担

AIコーディングアシスタント(Claude)と一緒に作りました。何を作るか、守る条件(架空データのみ・秘密情報を扱わない・非エンジニアが運用できること)を決め、結果を確かめたのは私です。設計の提案、コード、テスト、CI、ドキュメントはAIが担当しました。

全体像

ルールはすべて src/core に置き、GmailApp や SpreadsheetApp を一切呼びません。Apps Script とのやりとりは src/gas の薄い層だけです。こうしておくと、ルールを Vitest で手元でテストできます。

1. Apps Script を「窓口(ports)」の向こうに置く

実行の流れは、小さなインターフェースに対して書いています。

src/core/app.ts(抜粋)
export interface Ports {
  lock: { tryLock(timeoutMs: number): boolean; release(): void };
  settingsRows(): unknown[][];
  inbox: {
    list(label: string, limit: number): IncomingMessage[];
    relabel(threadIds: readonly string[], from: string, to: string): void;
  };
  sheet: {
    processedIds(): Set<string>;
    knownOrderNos(): Set<string>;
    knownSkus(): Set<string>;
    appendOrders(rows: readonly OrderRow[]): void;
    appendRunLog(entry: RunLogEntry): void;
    url(): string;
  };
  notify(to: readonly string[], subject: string, body: string): void;
  ownerEmail(): string;
  now(): Date;
}

本番では GmailApp などで実装し、テストでは偽物を渡します。テストでは「ロックが取れなければ何もしない」「設定が間違っていればメールを読む前に止まる」「例外が出てもロックを外す」といった、本番で試しにくい動きを確かめています。

2. 書き込んでからラベルを付け替える

一番気をつけたのは、途中で止まっても注文が二重にならない ことです。

src/core/app.ts(抜粋)
// Write first, relabel second: if the run dies in between, the next run sees the
// message IDs on the sheet and skips them instead of writing the orders twice.
if (plan.rows.length > 0) ports.sheet.appendOrders(plan.rows);
if (plan.doneThreadIds.length > 0) ports.inbox.relabel(plan.doneThreadIds, settings.inboxLabel, settings.doneLabel);

逆の順番だと、ラベルを付け替えた直後に止まった場合、その注文はどこにも書かれずに消えます。この順番なら、最悪でも次の実行で読み直し、シートにあるメールIDとして読み飛ばすだけです。

3. 推測しない:注文ごと要確認に回す

注文のうち1行でも怪しければ、その注文全体を要確認にします。半分だけ出荷するより、人が確かめるまで止めておくほうが安全だからです。

src/core/plan.ts(抜粋)
// If any part of an order is doubtful, the whole order goes to review:
// shipping half an order is worse than holding it for a person to check.
const lineReasons = lineRows.filter((r) => r.reasons !== "").map((r) => `${r.sku}: ${r.reasons}`);
const allReasons = [...orderReasons, ...lineReasons];
const needsReview = allReasons.length > 0;

読み取りは、安全なところだけ寛容にしています。全角の英数字(日本語入力のまま打たれたもの)、タブや x 区切り、あいさつ・署名・引用部分は受け入れ、読めない行は「○行目が読めません」として必ず理由に残します。

4. 設定はシートで、間違いはふつうの言葉で

設定シートの値は、スクリプトが毎回確かめます。

src/core/settings.ts(抜粋)
if (!Number.isInteger(n) || n < 1 || n > max) {
  errors.push(`"${key}" must be a whole number between 1 and ${max} (it is "${raw}").`);
  return fallback;
}

設定が間違っているときは、メールを1通も読まずに止まり、「注文は失われていません。設定シートの○○を直して、メニューから再実行してください」とメールで知らせます。止まり方まで含めて、非エンジニアが自分で直せることを目指しました。

5. TypeScript を GAS で動かすビルド

Apps Script はトップレベルの関数しか呼べないので、esbuild で1つのファイルにまとめたあと、公開する関数ごとに小さな関数を足しています。

scripts/build.mjs(抜粋)
const wrappers = exportsList
  .map((name) => `function ${name}() {\n  return ${GLOBAL}.${name}.apply(this, arguments);\n}`)
  .join("\n\n");

テストでは、できあがった dist/Code.js を Node の vm で読み込み、トリガーやメニューから呼ぶ関数が本当にあるかを確かめています。

テストとCI

Vitest で33件のテストを書きました。

  • 読み取り:全角、区切り文字、引用部分、読めない行、日付のチェック
  • 設定:既定値、不正な値の説明、通知先が空のとき
  • 計画:要確認の判定、二重取り込みの防止、1回の上限と次回への持ち越し
  • 実行:書き込みと付け替えの順番、ロック、設定エラー、例外時の通知
  • ビルド:GAS から呼べる関数がそろっていること

GitHub Actions で、lint・型チェック・テスト・ビルドを実行しています。テストは偽物のサービスを使うので、Gmail やシートそのものは確かめていません。src/gas を変えたときは、シートのコピーで「今すぐ実行」を試す運用にしています。

まとめ

  • ルールは純粋な TypeScript に置き、Apps Script は薄い窓口にする
  • 書き込んでからラベルを付け替え、途中で止まっても二重にしない
  • 怪しいものは推測せず、注文ごと要確認に回し、理由を残す
  • 設定はシートに置き、間違いはふつうの言葉で知らせる
  • 実行ログ・通知・運用ガイド・引き継ぎチェックリストまでを1セットにする

おわりに

型さえ引き渡しておけば、その後のメンテナンスや機能の追加は、非エンジニアがAIを使って自分たちで進められる時代になったと思います。テストとCIを最初から入れておいたのは、AIに直してもらったあとに、壊れていないかを確かめられるようにするためです。

リポジトリ:https://github.com/yahayuta/gas-business-automation

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?