進捗表の日付ミスを集計前に発見|GASで開始日・終了日・状態をdry-run確認

進捗表の日付ミスを集計前に発見|GASで開始日・終了日・状態をdry-run確認

ツールおじ|GoogleアカウントをAIエージェント化

ツールおじ|GoogleアカウントをAIエージェント化

終了日が開始日より前なのに、そのまま進捗表を集計してしまった。個人制作や副業の予定をシートで管理していると、こんな入力ミスが紛れ込みます。今回は、タイムラインを作る前に、空欄・日付・状態・重複をまとめて確認する無料のGASコードを紹介します。

まずは「作る」前に入力を点検する

この例はProjectsシートのA〜D列を読み取り、確認が必要な行番号と理由だけを実行ログへ出します。セルの書き換え、シート作成、予定登録、メール送信、外部APIへの送信は行いません。dry-runは、変更せずに確認する実行のことです。

タイムラインの描画や自動修正は対象外です。画像は進捗タイムラインのテーマを示したもので、この記事のコードが作る画面ではありません。Googleスプレッドシートと、そのファイルに紐づくApps Scriptを使います。

準備するシートと3行の例

元ファイルをコピーし、シート名をProjectsにします。A1〜D1の見出しは次のとおり固定してください。1行につき1つのプロジェクトを扱い、同じ名前を複数行に分けない前提です。

プロジェクト名	開始日	終了日	進捗状況
記事素材の準備	2026-09-09	2026-09-12	未着手
サンプル動画	2026-09-15	2026-09-10	進行中
説明文の見直し	2026-09-10	2026-09-13	作業中

B・C列の日付は、表示をyyyy-mm-dd(例:2026-09-09)に揃えます。「表示形式→数字→カスタム日時」で設定するか、書式なしテキストとして入力してください。コードはセルの表示文字列を検査するため、2026/9/9や9月9日という表示は確認対象になります。

進捗状況は「未着手」「進行中」「完了」「保留」の4種類です。上の例では3行目が日付の前後関係、4行目が状態の表記で確認対象になります。氏名・顧客名を使わず、架空のデータから試してください。

コピーして使える読み取り専用コード

/** @OnlyCurrentDoc */
function auditProjectTimelineFree() {
  const ss = SpreadsheetApp.getActiveSpreadsheet();
  if (!ss) throw new Error('シートに紐づくApps Scriptから実行してください。');
  const sheet = ss.getSheetByName('Projects');
  if (!sheet) throw new Error('Projectsシートが見つかりません。');
  const count = sheet.getLastRow() - 1;
  if (count < 1 || count > 200) {
    throw new Error('対象は1〜200行です。コピーしたシートで件数を確認してください。');
  }
  const data = sheet.getRange(1, 1, count + 1, 4).getDisplayValues();
  const report = checkProjectTimelineRows_(data);
  console.log(JSON.stringify(report));
  return report;
}

function checkProjectTimelineRows_(data) {
  const expected = ['プロジェクト名', '開始日', '終了日', '進捗状況'];
  if (!Array.isArray(data) || data.length < 2 || data.length > 201 ||
      !Array.isArray(data[0]) || data[0].length !== 4 ||
      !expected.every((name, i) => data[0][i] === name)) {
    throw new Error('A1:D1の見出し・列順・対象行数を確認してください。');
  }
  const rows = data.slice(1).map(row => {
    if (!Array.isArray(row) || row.length !== 4) {
      throw new Error('各行は4列で入力してください。');
    }
    return row.map(value => String(value == null ? '' : value).trim());
  });
  const names = new Map();
  rows.forEach(row => {
    if (row[0]) names.set(row[0], (names.get(row[0]) || 0) + 1);
  });
  const issues = [];
  let checked = 0;
  let skippedBlankRows = 0;
  rows.forEach((row, index) => {
    if (row.every(value => value === '')) {
      skippedBlankRows++;
      return;
    }
    checked++;
    const reasons = [];
    if (row.some(value => value === '')) reasons.push('必須項目の空欄');
    if (!validTimelineDate_(row[1]) || !validTimelineDate_(row[2])) {
      reasons.push('日付の形式・実在日を確認');
    } else if (row[1] > row[2]) {
      reasons.push('開始日が終了日より後');
    }
    if (!['未着手', '進行中', '完了', '保留'].includes(row[3])) {
      reasons.push('進捗状況が許可値以外');
    }
    if (row[0] && names.get(row[0]) > 1) reasons.push('プロジェクト名の重複');
    if (reasons.length) issues.push({ row: index + 2, reasons: reasons });
  });
  return {
    dryRun: true,
    checked: checked,
    skippedBlankRows: skippedBlankRows,
    ok: checked - issues.length,
    needsReview: issues.length,
    issues: issues
  };
}

function validTimelineDate_(value) {
  if (!/^\d{4}-\d{2}-\d{2}$/.test(value)) return false;
  const [year, month, day] = value.split('-').map(Number);
  if (year < 1900 || year > 2100 || month < 1 || month > 12) return false;
  const leap = year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0);
  const days = [31, leap ? 29 : 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
  return day >= 1 && day <= days[month - 1];
}

実行と結果の見方

1. コピーしたProjectsシートで「拡張機能→Apps Script」を開き、コードを貼り付けて保存します。

2. 関数一覧でauditProjectTimelineFreeを選んで実行します。初回は対象ファイルと実行アカウントを確認してアクセスを許可します。身に覚えのない権限要求や警告が出た場合は進めないでください。

3. Apps Scriptの「実行ログ」を開きます。上の3行を入力した場合、checkedは3、okは1、needsReviewは2です。issuesにはシートの3行目と4行目の確認理由が表示されます。プロジェクト名や日付の値はログへ出しません。

4. 3行目の開始日を2026-09-09、4行目の状態を「進行中」に手動で直し、もう一度実行します。needsReviewが0になることと、実行によってシートが変更されていないことを確認します。

確認できること・できないこと

日付は1900〜2100年の実在日を確認します。うるう年でない年の2月29日や4月31日、開始日と終了日の逆転を見つけます。同日開始・同日終了は許容します。日付だけを扱うため、時刻や所要時間は評価しません。

完全な空行は除外し、その件数をskippedBlankRowsへ出します。一部だけ空欄の行は確認対象です。同名のプロジェクトが複数あれば、該当する全行を確認対象にします。別工程を意図的に複数行で管理している表には、そのまま適用しないでください。

okは、このコードで定義した入力条件を通ったという意味です。作業の完了、期限内の達成、担当者の負荷、工程間の依存関係までは判断しません。日付と状態が整っていても、予定内容の妥当性はご自身で確認してください。

エラー時と元に戻す方法

Projectsがない、見出しや列順が違う、対象が0行または200行を超える場合は、検査を止めます。別列だけに値が入った行も行数に影響するため、コピー環境の不要行を確認してください。上限を超える場合は、元ファイルを消さず検証用コピーで対象を分けます。

スクリプトはデータを変更しないため、実行を取り消す復元処理はありません。人が入力を修正した分はスプレッドシートの元に戻す操作や変更履歴で復元できます。ログには行番号と理由が残るため、スクリプトとシートの共有先も必要な範囲に絞ってください。

GASの利用上限や組織の管理設定により実行できない場合があります。今回の例には有料API呼び出しは含みません。コードの検証はローカルの入力テストと読み書きの模擬確認で行っています。ご自身のGoogle環境では、まず上の3行で動作を確かめてください。

予定の入力を整えたら、公開前の準備も整理する

まずは入力ミスを見つけ、内容を直して再確認するところまで、この無料コードだけで試せます。商品制作の予定に加え、サムネ・納品ファイル・公開導線なども商品ごとに管理したい方には、別の有料テンプレート「個人クリエイター公開準備チェックGAS Pro」があります。今回の監査コードの拡張版ではなく、公開準備を整理するための商品です。

GasおじのTips商品一覧

※学習・検証用の例です。Google公式・Google公認の商品ではありません。


あなたも記事の投稿・販売を
始めてみませんか?

Tipsなら簡単に記事を販売できます!
登録無料で始められます!

Tipsなら、無料ですぐに記事の販売をはじめることができます Tipsの詳細はこちら
 

この記事のライター

ツールおじ|GoogleアカウントをAIエージェント化

おう、Gasおじだ。 AIツールとSaaSに散々突っ込んで遠回りした結果、最後に強かったのは「今あるGoogleアカウント」だった。 今ではAIエージェントであらゆるタスクを自動化して無双してるぞ。 GAS、スプレッドシート、フォーム、Gmail、AIエージェントを組み合わせれば、個人でも小さな会社でも、手作業だらけの業務はかなり自動化できる。 ここでは机上のAI論じゃなく、現場で動くテンプレ、コード、手順だけを置いていくぞ。 高いツールを増やす前に、まずお前さんのGoogleアカウントを仕事するエージェントに変えようじゃないか。

このライターが書いた他の記事

  • Gmailの定型連絡を「送信せず」まとめて下書き化する|Gmail下書きテンプレGAS Pro

    ¥2,980
    1 %獲得
    (29 円相当)
  • あとで見るURLを二重登録しない|GASで読書・動画ウォッチリストをdry-run監査

  • カレンダーの二重登録を作成前に発見|GASで予定候補をdry-run監査

関連のおすすめ記事

  • Gmailの定型連絡を「送信せず」まとめて下書き化する|Gmail下書きテンプレGAS Pro

    ¥2,980
    1 %獲得
    (29 円相当)
    ツールおじ|GoogleアカウントをAIエージェント化

    ツールおじ|GoogleアカウントをAIエージェント化

  • 【Python】 Con-Que 【データベース接続】

    ¥3,000
    1 %獲得
    (30 円相当)
    GoGorilla

    GoGorilla

  • USBメモリで持ち運べるLinux Mint 22.1と永続的な書き込み領域の作成法【Rufus だけで作成する方法を初心者向けに徹底解説】

    ¥500
    1 %獲得
    (5 円相当)
    SmartSenior

    SmartSenior