書類の電子化とスプレッドシートを用いた台帳テンプレート・GAS

# 書類の電子化とスプレッドシートを使った台帳テンプレート・GAS・Python

スプレッドシートのテンプレート(GASつき)
↓

https://docs.google.com/spreadsheets/d/1eJ4kodmz_KBV39le4vaSxSB_WMM_gnS0La95r_XURz8/edit?gid=86836940#gid=86836940

<summary>Google Apps Script（GAS）サンプルコード</summary>


```javascript
/**
 * ===============================================================
 *  logFolderToSheet  —— 必須 5 列だけ確保して台帳へ書込（省略なし版）
 * --------------------------------------------------------------
 *  必須列 : ['ファイル名','URL','fileId','作成日時','folderId']
 *  1) headerIndex で現在の列マップを取得
 *  2) 無い列だけ sheet.appendRow() で右端に追加（既存列は上書きしない）
 *  3) Drive 内ファイルを BATCH_SIZE (=50) 行ごとにバッチ書込
 *  4) 重複 fileId は Set 判定でスキップ
 *  5) onEdit/onOpen トリガーは禁止。時間主導や手動トリガーで実行
 * ---------------------------------------------------------------
 *  フォルダ ID  : 設定用!A2  または ScriptProperty 'folderId'
 *  MIME/拡張子 : 設定用!B2（".pdf" や "image/" 等）※空欄なら全ファイル
 * ===============================================================
 */
function logFolderToSheet() {

  /* ---------- 設定 ---------- */
  const REQUIRED   = ['ファイル名','URL','fileId','作成日時','folderId'];
  const SHEET_NAME = 'スキャン台帳';
  const BATCH_SIZE = 50;

  /* ---------- シート取得 & 必須列確保 ---------- */
  const ss    = SpreadsheetApp.getActiveSpreadsheet();
  const sheet = ss.getSheetByName(SHEET_NAME) || ss.insertSheet(SHEET_NAME);

  // 必須列チェック後は必ず最新ヘッダを取得
  let colMap = headerIndex(SHEET_NAME);
  const missing = REQUIRED.filter(c => colMap[c] === undefined);
  if (missing.length) {
    sheet.appendRow(missing);
  }
  // ここで強制再取得して確実に最新列順へ
  colMap = headerIndex(SHEET_NAME, undefined, { forceRefresh: true });

  /* ---------- フォルダ ID / フィルタ取得 ---------- */
  const folderId = getFolderId();
  if (!folderId) {
    SpreadsheetApp.getUi().alert('folderId が未設定です（設定用!A2 または ScriptProperty）。');
    return;
  }
  const filterStr = getFilterStr();                         // 拡張子 / MIME 部分文字列

  /* ---------- Drive 走査 ---------- */
  const folder = DriveApp.getFolderById(folderId);
  const files  = folder.getFiles();

  // 既存FileIdセット（重複排除）
  const lastRow = sheet.getLastRow();
  const idSet   = new Set(
      lastRow > 1
        ? sheet.getRange(2, colMap['fileId'] + 1, lastRow - 1).getValues().flat()
        : []
  );

  /* ---------- バッチ書込 ---------- */
  const buffer = [];
  while (files.hasNext()) {
    const f = files.next();
    if (idSet.has(f.getId())) continue;                      // 重複スキップ

    // フィルタ（拡張子 or MIME 部分一致）
    if (filterStr) {
      if (filterStr.startsWith('.')) {
        if (!f.getName().toLowerCase().endsWith(filterStr.toLowerCase())) continue;
      } else {
        if (!f.getMimeType().includes(filterStr)) continue;
      }
    }

    const row = new Array(Object.keys(colMap).length).fill('');
    row[colMap['ファイル名']] = f.getName();
    row[colMap['URL']]      = f.getUrl();
    row[colMap['fileId']]   = f.getId();
    row[colMap['作成日時']]   = Utilities.formatDate(
        f.getDateCreated(), Session.getScriptTimeZone(), 'yyyy-MM-dd HH:mm:ss');
    row[colMap['folderId']] = folderId;
    buffer.push(row);

    if (buffer.length >= BATCH_SIZE) {
      sheet.getRange(sheet.getLastRow() + 1, 1, buffer.length, row.length)
           .setValues(buffer);
      buffer.length = 0; // バッファクリア
    }
  }
  if (buffer.length) {
    sheet.getRange(sheet.getLastRow() + 1, 1, buffer.length, buffer[0].length)
         .setValues(buffer);
  }
}

/* ====================================================================
 *  サポート関数
 * ==================================================================== */

/**
 * headerIndex  —— ヘッダー ⇔ 列番号ユーティリティ（キャッシュ付）
 *   - 引数・振る舞いは README コメントを参照
 */
function headerIndex(sheetName, key, opts) {
  opts = Object.assign({ headerRow: 1, indexBase: 0, forceRefresh: false }, opts || {});
  const cacheKey = `hdr:${sheetName}:${opts.headerRow}`;

  // 1) 実行中メモリキャッシュ
  headerIndex._mem = headerIndex._mem || {};
  if (!opts.forceRefresh && headerIndex._mem[cacheKey]) {
    return _dispatch(headerIndex._mem[cacheKey], key, opts.indexBase);
  }

  // 2) ScriptCache（TTL 6h）
  const sc = CacheService.getScriptCache();
  let headers = null;
  if (!opts.forceRefresh) {
    const cached = sc.get(cacheKey);
    if (cached) headers = JSON.parse(cached);
  }

  // 3) シートから読み込み
  if (!headers) {
    const ss    = SpreadsheetApp.getActiveSpreadsheet();
    const sheet = ss.getSheetByName(sheetName);
    if (!sheet) throw new Error(`Sheet '${sheetName}' not found`);
    headers = sheet.getRange(opts.headerRow, 1, 1, sheet.getLastColumn()).getValues()[0];
    headerIndex._mem[cacheKey] = headers;
    sc.put(cacheKey, JSON.stringify(headers), 21600); // 6h
  }

  return _dispatch(headers, key, opts.indexBase);

  /* --- 内部ヘルパ --- */
  function _dispatch(arr, k, base) {
    const off = base === 1 ? 1 : 0;
    if (k === undefined) return arr.reduce((o, h, i) => (o[h] = i + off, o), {});
    if (typeof k === 'number') return arr[k - 1] ?? null;
    if (Array.isArray(k)) return k.reduce((o, h) => (o[h] = arr.indexOf(h) + off || null, o), {});
    if (typeof k === 'string') {
      const idx = arr.indexOf(k);
      return idx === -1 ? null : idx + off;
    }
    throw new Error('key must be undefined/string/number/string[]');
  }
}

/* ---------- フォルダ ID 取得 ---------- */
function getFolderId() {
  const ss   = SpreadsheetApp.getActiveSpreadsheet();
  const s2   = ss.getSheetByName('設定用');
  const idIn = s2 ? String(s2.getRange('A2').getValue()).trim() : '';
  return idIn || PropertiesService.getScriptProperties().getProperty('folderId') || '';
}

/* ---------- フィルタ文字列取得（拡張子 or MIME 部分） ---------- */
function getFilterStr() {
  const ss = SpreadsheetApp.getActiveSpreadsheet();
  const s2 = ss.getSheetByName('設定用');
  return s2 ? String(s2.getRange('B2').getValue()).trim() : '';
}
}
```

##  概要

大量の紙書類を電子化し、Googleスプレッドシートを用いた台帳管理からはじめたい方向け

##  対象: 業務でGoogle Workspace環境を使っており、現場の書類管理を効率化したい方。

## 制約
- 雇用契約書等の機密情報が対象

## 筆者の環境

- クラウド: Google Workspace（Google Drive, Google Spreadsheet, Google Apps Script）
- スキャナー: ScanSnap (流し込みスキャンができること)
- Adobe Acrobat Pro (ページの分割に使用)

---

## 書類電子化の流れ

1. **紙書類のスキャン**
    - ホチキスやクリップの除去、付箋の剥離、A3書類はA4変換等
    - スキャナーでPDF化

2. **Google Driveにアップロード**
    - フォルダ構成（例：年度・部署・個人フォルダごとなど）

3. **スプレッドシート台帳作成**
    - Driveのファイル情報を一覧管理
    - カラム例：

      | ファイル名 | URL | fileId |  作成日時 | folderId |
      |------------|-----|--------|-----------|----------|

4. **自動化（GAS）**
    - 指定したGoogle Drive内のフォルダからファイルリストを取得し、自動でスプレッドシートに書き出す
    - カラムの順序が変わっても動くスクリプト
    - 重複回避