Softex CelwareTech Blog
Google Apps Script2026-06-17

GASで日付プレフィックスと日内連番の管理番号を自動採番する

Google Apps Scriptで、基準日yyyyMMddと日内連番を組み合わせた管理番号を、既存行の最大値とLockServiceで安全に採番する方法を解説します。

GASGoogleスプレッドシート採番LockService管理番号

はじめに

商品、伝票、受付データなどに、日付を含む管理番号を付けたいことがあります。

たとえば、2026年6月17日の1件目を2026061701、2件目を2026061702のように採番する形式です。

手入力でも作れますが、入力ミスや重複が起きやすくなります。Google Apps ScriptGoogleスプレッドシートへ保存するなら、登録処理の中で自動採番しておくと安全です。

この記事で解決できること

  • yyyyMMdd + 日内連番形式の管理番号を作る
  • 今日の日付ではなく、入力された基準日から採番する
  • 既存番号の最大連番を見て続きの番号を付ける
  • 同時送信時の連番衝突をLockServiceで防ぐ

採番の考え方

基本は、既存の管理番号列を走査し、同じ日付プレフィックスの最大連番を探します。

2026061701
2026061702
2026061801

基準日が2026-06-17なら、プレフィックスは20260617です。この日付で始まる番号だけを見て、最大連番02の次として03を作ります。

実装コード

管理番号を生成する関数

function generateMgmtNumber_(sheet, keyCol, dateStr, lastRow) {
  var prefix = String(dateStr).replace(/\D/g, '').slice(0, 8);
  var max = 0;

  if (prefix.length !== 8) {
    throw new Error('採番用の日付が正しくありません: ' + dateStr);
  }

  if (lastRow >= 2) {
    var values = sheet
      .getRange(2, keyCol, lastRow - 1, 1)
      .getDisplayValues();

    for (var i = 0; i < values.length; i++) {
      var value = String(values[i][0] || '').trim();

      if (value.length >= 10 && value.slice(0, 8) === prefix) {
        var seq = parseInt(value.slice(8), 10);

        if (!isNaN(seq) && seq > max) {
          max = seq;
        }
      }
    }
  }

  var next = max + 1;
  var seqStr = next < 100 ? ('0' + next).slice(-2) : String(next);

  return prefix + seqStr;
}

replace(/\D/g, '')2026-06-172026/06/17から数字だけを取り出し、先頭8桁をyyyyMMddとして使います。

連番は2桁ゼロ埋めにしています。100件を超えた場合は100101のように桁あふれを許容する形です。

書き込み処理をLockServiceで囲む

複数人が同時に送信するWebアプリでは、2つの処理が同じ最大番号を読んで、同じ次番号を作ってしまう可能性があります。

採番から書き込みまでをLockServiceで囲み、同時実行を直列化します。

function addRow(payload) {
  var lock = LockService.getScriptLock();
  lock.waitLock(20000);

  try {
    var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('仕入');
    var lastRow = findLastDataRow_(sheet, 1);
    var mgmtNumber = generateMgmtNumber_(sheet, 1, payload.purchaseDate, lastRow);

    sheet
      .getRange(lastRow + 1, 1, 1, 4)
      .setValues([[
        mgmtNumber,
        payload.purchaseDate,
        payload.productName,
        new Date()
      ]]);

    return {
      managementNumber: mgmtNumber
    };
  } finally {
    lock.releaseLock();
  }
}

waitLock(20000)は、最大20秒待ってロックを取得する指定です。ロックを取った後は、必ずfinallyで解放します。

最終行は主キー列基準で見る

シートに数式列があると、getLastRow()が想定より下の行を返すことがあります。採番や追記位置は、管理番号列のような主キー列を基準に見ると安定します。

function findLastDataRow_(sheet, keyCol) {
  var lastRow = sheet.getLastRow();

  if (lastRow < 2) {
    return 1;
  }

  var values = sheet.getRange(2, keyCol, lastRow - 1, 1).getDisplayValues();

  for (var i = values.length - 1; i >= 0; i--) {
    if (String(values[i][0] || '').trim()) {
      return i + 2;
    }
  }

  return 1;
}

この関数は、2行目以降の主キー列を下から見て、最後に値がある行を返します。データがなければヘッダー行の1を返すため、次の追記先は2行目になります。

クライアント側で採番プレビューを出す

登録前に管理番号の見込み値を表示したい場合は、初期データとして既存番号一覧を渡しておき、ブラウザ側でプレビューできます。

function previewMgmt(dateStr, mgmtNumbers) {
  var prefix = String(dateStr).replace(/\D/g, '').slice(0, 8);
  var max = 0;

  mgmtNumbers.forEach(function(value) {
    value = String(value || '').trim();

    if (value.length >= 10 && value.slice(0, 8) === prefix) {
      var seq = parseInt(value.slice(8), 10);

      if (!isNaN(seq) && seq > max) {
        max = seq;
      }
    }
  });

  var next = max + 1;
  return prefix + (next < 100 ? ('0' + next).slice(-2) : String(next));
}

ただし、これはあくまでプレビューです。確定番号は、必ずサーバー側でLockServiceを使って採番します。

注意点

  • 採番の基準日は「今日固定」ではなく、入力された日付を使うかを先に決める
  • 既存番号は表示値で取得すると、先頭ゼロや文字列形式を扱いやすい
  • 採番から書き込みまでを1つのロック内で行う
  • 100件超の桁あふれを許容するか、3桁固定にするかは業務ルールで決める
  • クライアント側のプレビュー番号を、そのまま保存値として信用しない

関連記事

まとめ

日付プレフィックスと日内連番の管理番号は、既存番号の最大値を見て次番号を作ると、途中に削除行があっても続きの番号を採れます。

複数人が使うGAS Webアプリでは、採番と書き込みをLockServiceで囲み、クライアント側の番号はプレビュー扱いにするのが安全です。

この技術で業務改善しませんか?

Excel VBA・GAS・Webアプリで業務の自動化ツールを開発しています。 「こんなことできる?」というご相談だけでもお気軽にどうぞ。

無料相談はこちら →