5
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初心者】売場改善報告書の作成を自動化してみた|写真を撮るだけでPDF化する仕組みづくりに挑戦

5
Posted at

はじめに

私は小売業で複数店舗を担当しており、

・店舗チーフやパートスタッフの教育

・本部施策や徹底事項の実施確認

・売場づくりの指導や水平展開

などの業務を普段行っています。今回は、そんな日々の業務の中で感じていた「売場改善報告書づくりの手間」を減らすために、人生で初めてGoogle Apps Script(GAS)に挑戦した話です。

店舗を巡回していると、

「本部で決めた売場になっていないな」

「この商品の見せ方、少し変えた方が良さそうだな」

「この成功事例、他店舗にも展開したいな」

と思うことがよくあります。

そのため、必要に応じて売場を修正したり、改善提案を行ったりすることがあります。

そして、その内容を本部や上司へ報告するのも大切な仕事のひとつです。

今回は、そんな日々の業務の中で感じていた「売場改善報告書づくりの手間」を減らすために、人生で初めてGoogle Apps Script(GAS)に挑戦した話です。

売場を直すより、報告書を作る方が大変だった

店舗巡回中、本部施策が正しく展開されていない場合があります。

例えば、

  • POPの位置が違う
  • 商品の並び順が違う
  • 売場の見せ方がもったいない

などです。

その場合は、その場で売場を修正します。しかし問題はその後でした。
改善内容を報告するため、

  • Before写真
  • After写真
  • 作業コメント

を資料にまとめることがあります。

これまでの報告フローは

  • Beforeの売り場を撮影

  • 売り場を手直し

  • Afterの売り場を撮影

  • PCでExcelやPowerPointを立ち上げる

  • 写真を取り込み、貼り付け、サイズ調整

  • コメントを入力

  • ファイルを保存して上司へ送信

image.png
※これまでの報告書イメージ

地味だけど面倒な作業

特に大変だったのが写真の貼り付けです。

サイズを合わせる、配置を整える、少しズレる、また直す。

報告書1件なら大したことはありません。

でも、これが何件も重なると意外と時間を取られます。

気付けば、「売場を直す時間より報告書を作る時間の方が長いんじゃないか?」 と思うこともありました。

写真を撮るだけで報告書が完成したら?

ある日ふと思いました。

「写真を撮ってコメントを書くだけで報告書ができたら楽なのでは?」今回実現したかったのは、とてもシンプルです。

やりたかったこと

  • スマホやPCで簡単に完結
  • Before写真を登録
  • After写真を登録
  • コメントを入力
  • PDFを自動生成

事務所へ戻ってPowerPointやExcelで作成しなくても良い。
そんな仕組みを目指しました。

完成イメージ

image.png

そもそもGASって何?

今回使ったのはGoogle Apps Script(GAS)です。
Googleが提供しているサービス同士を連携させたり、自動化したりできる仕組みです。

正直に言うと、私は今回初めて触りました。「名前は聞いたことあるけど難しそう」というレベルです。

それでも、
Googleフォーム

Googleドライブ

GAS

PDF作成

という流れならできそうだと思い、挑戦してみることにしました。

AIに相談しながら作ってみた

今回も頼ったのはAIです。

まずは、「こんな仕組みを作りたい」という内容を整理して相談しました。

image.png

すると、

必要な手順やサンプルコードを提案してくれました。

その時は、「思ったより簡単にできるかもしれない」 と思っていました。

image.png
※AIが返してくれた内容の一部を抜粋しています。

現実はそんなに甘くなかった

実際に作り始めると、すぐに壁にぶつかりました。

コードを貼り付ける。

エラー。

修正する。

またエラー。

別の方法を試す。

またエラー。

前回のPower Automateの記事と同じ展開です(笑)

特に苦労したところ

  • 写真の反映
  • PDFレイアウト
  • 画像配置

AIが教えてくれるコードをそのまま貼れば完成。

そんな簡単な話ではありませんでした。

「本当にできるのか...」

途中で何度も思いました。

でも、

エラー内容を調べる

AIに聞く

修正する

試す

これを繰り返していくうちに、少しずつ前に進めました。

3日後、ついに完成

試行錯誤を続けること約3日。

テスト実行後にGoogleドライブを開くと、そこにはPDFが生成されていました。

思わず声が出ました。 「できたーーー!」

本当に嬉しかったです。

たった1枚のPDFです。

でも、自分で考えた仕組みが動いた瞬間でした。

完成した仕組み

GAS動画.gif

image.png

作成したコード

コードを開く

/**
 * フォーム送信時に実行されるメイン関数(公開用サンプル)
 */
function onFormSubmit(e) {
  let sheet;
  let row;
  
  if (e && e.range) {
    sheet = e.range.getSheet();
    row = e.range.getRow();
  } else {
    sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
    row = sheet.getLastRow();
    Logger.log("手動で実行されました。最新の行(" + row + "行目)を処理します。");
  }

  if (row <= 1) {
    Logger.log("処理できるデータ行がありません。");
    return;
  }

  const headers = sheet.getRange(1, 1, 1, sheet.getLastColumn()).getValues()[0];
  const rowValues = sheet.getRange(row, 1, 1, sheet.getLastColumn()).getValues()[0];
  
  let beforeCell = "";
  let afterCell = "";
  let storeName = "";
  let commentParts = [];

  for (let i = 0; i < headers.length; i++) {
    const headerName = headers[i].toString().trim();
    const val = rowValues[i] ? rowValues[i].toString().trim() : "";
    
    if (!val) continue;

    if (val.includes("drive.google.com")) {
      if (!beforeCell) beforeCell = val;
      else if (!afterCell) afterCell = val;
    } 
    else if (headerName.includes("") || val.includes("")) {
      storeName = val;
    } 
    else if (headerName !== "タイムスタンプ") {
      commentParts.push(val);
    }
  }

  function getBase64Image(cellValue, label) {
    if (!cellValue) return null;
    const url = cellValue.split(",")[0].trim();
    const match = url.match(/id=([a-zA-Z0-9_-]+)/);
    if (match && match[1]) {
      try {
        const file = DriveApp.getFileById(match[1]);
        const blob = file.getBlob();
        return `data:${blob.getContentType()};base64,${Utilities.base64Encode(blob.getBytes())}`;
      } catch(err) {
        Logger.log(label + "の画像データ取得に失敗しました: " + err.message);
        return null;
      }
    }
    return null;
  }

  const beforeBase64 = getBase64Image(beforeCell, "Before画像");
  const afterBase64 = getBase64Image(afterCell, "After画像");

  if (!beforeBase64 || !afterBase64) {
    Logger.log("【終了】画像が解析できなかったため、PDF生成を停止しました。");
    return;
  }

  // 🔒 セキュリティのため公開用にフォルダーIDを書き換えています
  const folderId = "★ここに保存先のGoogleドライブのフォルダIDを入力★";
  const folder = DriveApp.getFolderById(folderId);

  const timestamp = Utilities.formatDate(new Date(), "Asia/Tokyo", "yyyy/MM/dd HH:mm");
  const timestampFile = Utilities.formatDate(new Date(), "Asia/Tokyo", "yyyyMMdd_HHmmss");
  
  const rawComment = commentParts.join("\n\n");
  const formattedComment = rawComment ? rawComment.replace(/\n/g, '<br>') : "(記述なし)";

  // HTML組み立て
  const html = `
  <html>
  <head>
    <style>
      @page { size: A4 landscape; margin: 3mm 4mm; }
      body { font-family: 'Helvetica Neue', Arial, 'Hiragino Kaku Gothic ProN', sans-serif; margin: 0; padding: 0; color: #333; line-height: 1.2; }
      
      .header-container { width: 100%; position: relative; text-align: center; margin-bottom: 1px; padding: 2px 0; }
      .header-title { font-size: 22px; font-weight: bold; color: #1e293b; display: inline-block; }
      .meta { position: absolute; right: 4px; bottom: 4px; font-size: 10px; color: #64748b; }
      
      table { width: 100%; border-collapse: collapse; table-layout: fixed; margin-bottom: 2px; }
      .col-img { width: 48%; text-align: center; } 
      .col-arrow { width: 4%; text-align: center; vertical-align: middle; }
      td { padding: 0; vertical-align: top; }
      
      h3 { margin: 0 0 1px 0; font-size: 13px; color: #475569; font-weight: bold; text-align: center; text-transform: uppercase; }
      
      .img-container { width: 100%; height: 480px; display: table; background: #fff; }
      .img-cell { display: table-cell; vertical-align: middle; text-align: center; }
      img { max-width: 100%; max-height: 480px; object-fit: contain; border: 1px solid #cbd5e1; border-radius: 4px; }
      
      .arrow { font-size: 32px; font-weight: bold; color: #ef4444; line-height: 480px; }
      
      .comment-box { 
        border: 1px solid #cbd5e1; 
        border-radius: 4px; 
        padding: 8px 12px; 
        background: #f8fafc; 
        font-size: 13px; 
        line-height: 1.4; 
        text-align: left; 
        margin-top: 2px;
        height: 180px; 
        max-height: 200px;
        overflow: hidden; 
        box-sizing: border-box;
      }
      .comment-title { font-size: 11px; font-weight: bold; color: #475569; margin-bottom: 4px; display: block; border-bottom: 1px solid #e2e8f0; padding-bottom: 2px; }
    </style>
  </head>
  <body>
    <div class="header-container">
      <div class="header-title">売り場変更報告 【${storeName || '未入力'}】</div>
      <div class="meta">作成日時:${timestamp}</div>
    </div>
    <table>
      <tr>
        <td class="col-img">
          <h3>BEFORE</h3>
          <div class="img-container"><div class="img-cell"><img src="${beforeBase64}"></div></div>
        </td>
        <td class="col-arrow"><div class="arrow">➡</div></td>
        <td class="col-img">
          <h3>AFTER</h3>
          <div class="img-container"><div class="img-cell"><img src="${afterBase64}"></div></div>
        </td>
      </tr>
      <tr>
        <td colspan="3">
          <div class="comment-box">
            <span class="comment-title">【コメント】</span>${formattedComment}
          </div>
        </td>
      </tr>
    </table>
  </body>
  </html>
  `;

  const blob = Utilities.newBlob(html, "text/html", "report.html");
  const pdf = blob.getAs("application/pdf");
  const pdfFile = folder.createFile(pdf);
  pdfFile.setName(`売り場変更報告_${storeName || '名称未設定'}_${timestampFile}.pdf`);
  
  Logger.log("PDFの生成に成功しました: " + pdfFile.getName());
}

実際どれくらい楽になったのか

Before

  • 写真整理
  • PowerPoint編集
  • PDF化

約20〜30分

After

  • 写真撮影(アップロード)
  • コメント入力

約5分

効果

報告書作成に使っていた時間を大幅に削減できました。
浮いた時間を、

  • 店舗巡回
  • 売場改善指導
  • スタッフ教育

など、本来やるべき業務に時間が使えるようになりました。

初心者だからこそ感じたこと

今回改めて思ったことがあります。

それは、 現場の困りごとは、現場にいる人が一番知っている ということです。

私はエンジニアではありません。

GASも今回が初めてでした。

それでも、「面倒だな」と思っていた作業を見直し、少しずつ調べながら形にすることができました。

AIは魔法ではない

今回わかったことがあります。

AIは便利です。

でも、AIに聞けば一発で完成するわけではありません。

試す。

失敗する。

調べる。

また試す。

結局はその繰り返しでした。

でも、その過程で学んだことはとても大きかったです。

今後の展望

今回作った仕組みはまだ試作品です。

今後は、

  • Teams通知との連携
  • 会議資料向けにレイアウト改善
  • 他の方に使ってもらい、ブラッシュアップする

なども進めていきたいと思っています。

おわりに

今回作った仕組みは、まだ完成形ではありません。

もっと改善したい部分もあります。

それでも、

Before写真を撮る

売場を修正する

After写真を撮る

コメントを書く

PDFが完成する

ここまでできた時は本当に嬉しかったです。

デジタルが苦手な私でも、調べながら形にすることができました。

だからこそ、「自分には難しそうだな」と思っている人ほど、一度挑戦してみてほしいです。

私もまだ勉強中ですが、これからも現場で感じた「面倒」を少しずつ改善していこうと思います。

最後まで読んでいただき、ありがとうございました。

もし「こうするともっと良くなるよ!」というアイデアがあれば、ぜひコメントで教えてください。

5
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
5
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?