【サークル出展者必見!】Googleフォームを使ってPDF化した領収書をメールで送付する方法

サークル出展した際、購入してくれた方から「領収書をもらえますか?」と尋ねられることは少なくありません
紙の領収書を出すのも一つの手ですが、次のお客さんがいる場合、お待たせしてしまうことになります

そこで、今回は「Googleフォームを使って領収書を自動発行する方法」について解説していきたいと思います!

がすぴょん

この間、ゲムマや同人イベントに参加している友人からこんな相談を受けたんだよ

最近、イベントに参加した際に「領収書をもらえますか?」と聞かれることが増えた。毎回一人で参加しているため、売り子もおらず、領収書を書く手間が惜しい。お客さんにQRコードを読み取ってもらい、必要項目を入力してもらうことで、領収書をPDF化してメールで送れるようにしたい。

こーすけ先生

なるほど!
GASを使えば簡単にできるよ!

本記事の内容
  • Googleフォームの回答内容をGASで取得して、Googleドキュメントで作成したテンプレートに取得した値を落とし込む
  • 自動作成したGoogleドキュメントをPDF変換する
  • 変換したPDFを添付ファイルとして相手先にメールを送信する

この記事を読めば、Googleフォームの回答から購入者宛にPDF化した領収書をメール送付することができます!めちゃくちゃ便利なので、是非読んでいってください!

こーすけ先生

初めての場合、GASで作るのは大変かもしれないけど、一回作ればそれをベースに何度も使えるから頑張って作ってみよう!

この記事の執筆者について
  • GASの人
  • ITベンダSEとして12年勤務する中で民間、金融、官公庁の現場を一通り経験
  • 足立区役所にて、ICT戦略推進担当課に課長職として3年間勤務
  • 2024年5月に「株式会社4U」を創業
GASなら任せろ!

​GASを極めたい方や、業務の効率化を図りたい方は、ぜひこの記事を読んください!
難しいことはGASに任せて、我々人間は楽しちゃいましょう!

こーすけ先生

当ブログでは実際に仕事でGASを扱っている私が、GASの魅力について徹底的に取り上げていきます!

目次

事前準備

本記事で取り組む演習
  • Googleフォームを作成&回答
  • Googleドキュメントで、欠席届のテンプレートを作成する
  • PDFを保存するためのフォルダを作成
こーすけ先生

事前準備の説明が不要な方は【STEP2】までジャンプしてください!

今回は商品が1つだった場合のシンプルな領収書の作り方を説明します!

【事前準備①】Googleフォームを作成する

スクロールできます
項目名 タイプ必須
メールアドレス(領収書データの送信先)テキスト
お名前(領収書に記載)テキスト
購入部数ラジオボタン
こーすけ先生

参考に私が作成したフォームは以下となります!

がすぴょん

フォームを作成したら回答してみてね!
回答0件だとエラーになってしまうよ

【事前準備②】Googleドキュメントを作成する

こーすけ先生

それでは、次にGoogleドキュメントで領収書のテンプレートを作成しましょう!

こーすけ先生

今回、こちらの「領収書テンプレート」を使って演習します!
必要に応じてコピーして使ってください!

{{}}(可変文字部)について

がすぴょん

この{{date}}みたいに波括弧で囲んであるのは何なの?

こーすけ先生

波括弧の部分は「可変文字部」といって、{{}}内を入力内容に書き換える魔法のような存在なんだ!詳しくは、以前の記事で説明しているよ!
是非読んでみてね!

【事前準備③】PDF出力場所の作成

こーすけ先生

Googleフォームの回答をGoogleドキュメントに反映し、PDFに変換するため、その出力先を作成しておきましょう!

Googleフォームはコンテナバインド型で開くため、IDは不要ですが、注文書テンプレートPDF出力場所のIDは事前に取得しておきましょう!

がすぴょん

IDは緑色の四角で囲った部分だよ!

【お題】Googleフォームの回答者に領収書をPDFで自動発行する

ひとつひとつのサービスをつなぎ合わせるイメージ
本記事で取り組む演習
  • Googleフォームの回答内容をGASで取得して、Googleドキュメントで作成したテンプレートに取得した値を落とし込む
  • GoogleドキュメントをPDF変換する
  • 変換したPDFを添付ファイルとして相手先にメールを送信する
こーすけ先生

Googleフォームの方であらかじめ回答しておくのを忘れないようにしてくださいね!
(回答がないとエラーになってしまうため)

まずは全体のコードを確認

// 欠席届テンプレートファイル
const DOC_TEMPLATE = DriveApp.getFileById('〇〇〇〇〇〇(自分のドキュメントID)');
// PDF出力先
const PDF_OUTDIR = DriveApp.getFolderById('〇〇〇〇〇〇(自分のフォルダID)');


function gaslog_formSubmit(e) {
  let itemResponses;
  // フォームの回答をイベントオブジェクトまたはフォーム自身から取得する。
  if (e !== undefined) {
    itemResponses = e.response.getItemResponses();
  } else {
    const wFormRes = FormApp.getActiveForm().getResponses();
    itemResponses =  wFormRes[wFormRes.length-1].getItemResponses();
  }

  // 回答をもとに領収書を作成する 
  let wFileRtn = createGDoc(itemResponses);
  // PDF変換してファイルIDを取得する
  let wPdfId = createPdf(wFileRtn[0], wFileRtn[1]);
  // PDF変換したあとは元ファイルを削除する
  DriveApp.getFileById(wFileRtn[0]).setTrashed(true);
  
  // 今回はPDFファイルを添付してメールを送信する
  sendEmailEx(
    e !== undefined ? itemResponses[0].getResponse() : '〇〇〇〇@gmail.com(自分のメールアドレス)'
    , '【自動送信メール】領収書の送信'
    , `領収書のPDFを送付致します。`
    , {attachments: DriveApp.getFileById(wPdfId).getBlob()}
  );
  DriveApp.getFileById(wPdfId).setTrashed(true);
}

function createGDoc(itemResponses) {
  // テンプレートファイルをコピーする
  const wCopyFile = DOC_TEMPLATE.makeCopy()
        , wCopyFileId = wCopyFile.getId()
        , wCopyDoc = DocumentApp.openById(wCopyFileId); // コピーしたファイルをGoogleドキュメントとして開く
  let wCopyDocBody = wCopyDoc.getBody()                 // Googleドキュメント内の本文を取得する
      , today = dayjs.dayjs();

  // 以降はGoogleフォームの回答をマッピングする
  itemResponses.forEach(function(itemResponse){
    switch (itemResponse.getItem().getTitle()) {
      case 'お名前(領収書に記載)':
        wCopyDocBody = wCopyDocBody.replaceText(`{{name}}`, itemResponse.getResponse());
        break;

      case '購入部数':
        let unitPrice = PropertiesService.getScriptProperties().getProperty('PRODUCT_VALUE')
            , quantity = Number(itemResponse.getResponse());
        wCopyDocBody = wCopyDocBody.replaceText(`{{price}}`, unitPrice*quantity);
        break;

      default:
        break;

    }
  });
  wCopyDoc.saveAndClose();
  // ファイル名を変更する
  let fileName = 'イベント領収書:ガスログ_'+today.format('YYYYMMDD');
  wCopyFile.setName(fileName);
  // コピーしたファイルIDとファイル名を返却する(あとでこのIDをもとにPDFに変換するため)
  return [wCopyFileId, fileName];
}

function createPdf(docId, fileName){
  // PDF変換するためのベースURLを作成する
  let wUrl = `https://docs.google.com/document/d/${docId}/export?exportFormat=pdf`;

  // headersにアクセストークンを格納する
  let wOtions = {
    headers: {
      'Authorization': `Bearer ${ScriptApp.getOAuthToken()}`
    }
  }; 
  // PDFを作成する
  let wBlob = UrlFetchApp.fetch(wUrl, wOtions).getBlob().setName(fileName + '.pdf');

  //PDFを指定したフォルダに保存する
  return PDF_OUTDIR.createFile(wBlob).getId();
}

function sendEmailEx(_recipient, _subject, _body, _option) {
  // 引数の内容でメールを下書き保存する
  const mailDraft = GmailApp.createDraft(_recipient, _subject, _body, _option);
  // 下書き保存したメールIDから下書きを取得し、メール送信を依頼する
  GmailApp.getDraft(mailDraft.getId()).send();
}
こーすけ先生

今回は今まで扱ったことがない要素があるので、順を追って解説していきますね!

【STEP1】Googleフォームからコンテナバインド型でスクリプトエディタを開く

こーすけ先生

スクリプトエディタの開き方が分かる方は【STEP2】にジャンプしてください!

がすぴょん

作成したGoogleフォームの「︙」をクリックして「〈〉スクリプトエディタ」から、コンテナバインド型でエディタを開こう!

がすぴょん

エディタが表示されたら「▷実行」をクリック!

がすぴょん

プロジェクトを開始する時、必ず出てくる画面だよ
「権限を確認」をクリックしよう!

がすぴょん

自分のアカウントを選択して…

がすぴょん

「詳細」をクリックして、「無題のプロジェクト(安全ではないページ)に移動」をクリックすると…

がすぴょん

こんなカンジの画面が出たら「許可」をクリックしよう!

こーすけ先生

これでGASを書く準備が整ったよ!
早速、試しに色々書いていこう!

GASは[実行]ボタンをクリックした際に、各クラスに対するアクセス権限がまだ承認されていない場合、それを促すダイアログが表示されます
これは、GASも自身のアカウントで作成しているとはいえ、GASから見ると別のGoogleサービスを扱うことになるため、本当によいかどうかを確認するものとなります

【STEP2】GASエディタを開き、「Day.js」をライブラリに追加する

STEP
Googleドライブにアクセスし、左上の [+ 新規] ボタンをクリック
STEP
[その他] の中にある [Google Apps Script] を選択
STEP
エディタが開く
がすぴょん

GASのエディタを開くことができたよ!

Day.jsをライブラリに追加する

STEP
メニューバーから「+(ライブラリを追加)」をクリック
STEP
出てきたダイアログのスクリプトIDに以下のIDを入力し、「検索」をクリック

1ShsRhHc8tgPy5wGOzUvgEhOedJUQD53m-gd8lG2MOgs-dXC_aCZn9lFB

STEP
「追加」をクリックすると、GAS上で「Day.jsライブラリ」を使用できるようになる

上記ダイアログのID欄にデフォルトは「dayjs」と表示されていますが、ここを変更すると、プロジェクト内でdayjsライブラリにアクセスするための文字列を変更できます。が、通常あまりやりません。

【STEP2】の補足説明

let today = dayjs.dayjs();

//中略

wCopyDoc.saveAndClose();
let fileName = 'イベント領収書:ガスログ_'+today.format('YYYYMMDD');
wCopyFile.setName(fileName);
こーすけ先生

dayjsライブラリを使って現在の日付を取得し、「イベント領収書:ガスログ_YYYYMMDD(本日の日付)」というファイル名になるようにしています

今回も一時的に保存するGoogleドキュメントのファイル名に日付を自動入力したかったので、「Day.js」を使いました

こーすけ先生

「Day.jsについて知見を深めたい!」という方はこちらから読んでみてください!

【STEP3】Googleフォームで回答送信時にGASが動くようトリガーを紐づける

こーすけ先生

左側のメニュー内の「トリガー」をクリック

こーすけ先生

右下にある「+トリガーを追加」をクリック

トリガーの設定画面において、「イベントのソースを選択」を[フォームから]に、「イベントの種類を選択」を[フォーム送信時]に設定してください

こーすけ先生

この設定を行うことで、Googleフォームから回答があった際に、回答者宛に自動でメールが送信されるよ!

こーすけ先生

「もっと詳しく知りたい!」という方はこちらの記事を参考にしてください

【STEP4】「プロジェクトの設定」から金額を設定する※重要

こーすけ先生

左側のメニュー内にある歯車マーク「プロジェクトの設定」をクリック!

こーすけ先生

一番下までスクロールし、「プロパティ欄」に金額(単価)を設定します

      case '購入部数':
        let unitPrice = PropertiesService.getScriptProperties().getProperty('PRODUCT_VALUE')
            , quantity = Number(itemResponse.getResponse());
        wCopyDocBody = wCopyDocBody.replaceText(`{{price}}`, unitPrice*quantity);
        break;
こーすけ先生

PRODUCT_VALUEという値を「1000」に設定することでスクリプト内でこの金額が適用されています

スクリプトプロパティから商品の単価(PRODUCT_VALUE)を取得し、単価と購入数(回答)を掛け合わせて合計金額を計算し、{{price}}プレースホルダーを置き換えています

こーすけ先生

購入部数の回答が「1」だと「1,000円」、回答が「2」だと「2,000円」…といったカンジで、スクリプトプロパティから設定した単価を掛けた金額がドキュメントファイルの{{price}}内に反映されるよ

金額を自由記述式にすると、ユーザーが不正に金額を変えることができてしまうため、単価を設定した上で、選択式にしています

【STEP5】Googleフォームの回答を取得し、ドキュメント内を書き換える

function createGDoc(itemResponses) {
  // テンプレートファイルをコピーする
  const wCopyFile = DOC_TEMPLATE.makeCopy()
        , wCopyFileId = wCopyFile.getId()
        , wCopyDoc = DocumentApp.openById(wCopyFileId); // コピーしたファイルをGoogleドキュメントとして開く
  let wCopyDocBody = wCopyDoc.getBody()                 // Googleドキュメント内の本文を取得する
      , today = dayjs.dayjs();

  // 以降はGoogleフォームの回答をマッピングする
  itemResponses.forEach(function(itemResponse){
    switch (itemResponse.getItem().getTitle()) {
      case 'お名前(領収書に記載)':
        wCopyDocBody = wCopyDocBody.replaceText(`{{name}}`, itemResponse.getResponse());
        break;

      case '購入部数':
        let unitPrice = PropertiesService.getScriptProperties().getProperty('PRODUCT_VALUE')
            , quantity = Number(itemResponse.getResponse());
        wCopyDocBody = wCopyDocBody.replaceText(`{{price}}`, unitPrice*quantity);
        break;

      default:
        break;

    }
  });

自分が設定した質問文を一語一句違わず、case '〇〇〇':と「〇〇〇」の中に入れよう!

こーすけ先生

switch文を利用することによって、「〇〇」という質問に対しての回答をGoogleドキュメント本文中の{{可変文字部}}部分に置き換える作業をしているよ!

itemResponses.forEach(function(itemResponse){
    switch (itemResponse.getItem().getTitle()) {
      case 'お名前(領収書に記載)':
        wCopyDocBody = wCopyDocBody.replaceText(`{{name}}`, itemResponse.getResponse());
        break;

      case '購入部数':
        let unitPrice = PropertiesService.getScriptProperties().getProperty('PRODUCT_VALUE')
            , quantity = Number(itemResponse.getResponse());
        wCopyDocBody = wCopyDocBody.replaceText(`{{price}}`, unitPrice*quantity);
        break;

      default:
        break;
    }

itemResponse.getResponse()を用いることで、Googleフォームの回答内容を取得し、テンプレートドキュメント内の{{name}}や、{{price}}等の可変文字部をユーザーが回答した内容に置き換えます

こーすけ先生

今回の例だと、「お名前(領収書に記載)」と「購入部数」の質問に対して、ユーザーが入力した回答内容を取得し、テンプレートドキュメント内の対応する{{可変文字部}}を置き換えます

【STEP6】 PDF化した領収書を添付ファイルとして相手先にメールを送信する

// 欠席届テンプレートファイル
const DOC_TEMPLATE = DriveApp.getFileById('〇〇〇〇〇〇(自分のドキュメントID)');
// PDF出力先
const PDF_OUTDIR = DriveApp.getFolderById('〇〇〇〇〇〇(自分のフォルダID)');


function gaslog_formSubmit(e) {
  let itemResponses;
  // フォームの回答をイベントオブジェクトまたはフォーム自身から取得する。
  if (e !== undefined) {
    itemResponses = e.response.getItemResponses();
  } else {
    const wFormRes = FormApp.getActiveForm().getResponses();
    itemResponses =  wFormRes[wFormRes.length-1].getItemResponses();
  }

  // 回答をもとに領収書を作成する 
  let wFileRtn = createGDoc(itemResponses);
  // PDF変換してファイルIDを取得する
  let wPdfId = createPdf(wFileRtn[0], wFileRtn[1]);
  // PDF変換したあとは元ファイルを削除する
  DriveApp.getFileById(wFileRtn[0]).setTrashed(true);
  
  // 今回はPDFファイルを添付してメールを送信する
  sendEmailEx(
    e !== undefined ? itemResponses[0].getResponse() : '〇〇〇〇@gmail.com(自分のメールアドレス)'
    , '【自動送信メール】領収書の送信'
    , `領収書のPDFを送付致します。`
    , {attachments: DriveApp.getFileById(wPdfId).getBlob()}
  );
  DriveApp.getFileById(wPdfId).setTrashed(true);
}

function createGDoc(itemResponses) {
  // テンプレートファイルをコピーする
  const wCopyFile = DOC_TEMPLATE.makeCopy()
        , wCopyFileId = wCopyFile.getId()
        , wCopyDoc = DocumentApp.openById(wCopyFileId); // コピーしたファイルをGoogleドキュメントとして開く
  let wCopyDocBody = wCopyDoc.getBody()                 // Googleドキュメント内の本文を取得する
      , today = dayjs.dayjs();

  // 以降はGoogleフォームの回答をマッピングする
  itemResponses.forEach(function(itemResponse){
    switch (itemResponse.getItem().getTitle()) {
      case 'お名前(領収書に記載)':
        wCopyDocBody = wCopyDocBody.replaceText(`{{name}}`, itemResponse.getResponse());
        break;

      case '購入部数':
        let unitPrice = PropertiesService.getScriptProperties().getProperty('PRODUCT_VALUE')
            , quantity = Number(itemResponse.getResponse());
        wCopyDocBody = wCopyDocBody.replaceText(`{{price}}`, unitPrice*quantity);
        break;

      default:
        break;

    }
  });
  wCopyDoc.saveAndClose();
  // ファイル名を変更する
  let fileName = 'イベント領収書:ガスログ_'+today.format('YYYYMMDD');
  wCopyFile.setName(fileName);
  // コピーしたファイルIDとファイル名を返却する(あとでこのIDをもとにPDFに変換するため)
  return [wCopyFileId, fileName];
}

function createPdf(docId, fileName){
  // PDF変換するためのベースURLを作成する
  let wUrl = `https://docs.google.com/document/d/${docId}/export?exportFormat=pdf`;

  // headersにアクセストークンを格納する
  let wOtions = {
    headers: {
      'Authorization': `Bearer ${ScriptApp.getOAuthToken()}`
    }
  }; 
  // PDFを作成する
  let wBlob = UrlFetchApp.fetch(wUrl, wOtions).getBlob().setName(fileName + '.pdf');

  //PDFを指定したフォルダに保存する
  return PDF_OUTDIR.createFile(wBlob).getId();
}

function sendEmailEx(_recipient, _subject, _body, _option) {
  // 引数の内容でメールを下書き保存する
  const mailDraft = GmailApp.createDraft(_recipient, _subject, _body, _option);
  // 下書き保存したメールIDから下書きを取得し、メール送信を依頼する
  GmailApp.getDraft(mailDraft.getId()).send();
}
こーすけ先生

sendEmailEx は、指定された受信者、件名、本文、およびオプションでメールの下書きを作成し、その下書きを取得してメールを送信するものです!

このような設定を行うことで、Googleフォームの回答者宛に自動で領収書をメール送付することができます

こんなカンジで置いてくれたみたい
こーすけ先生

今回はこれでおしまいです!
お疲れ様でした

まとめ

今回は「GASを用いてGoogleフォームの回答を取得し、回答者宛に自動で領収書を送付しよう!」というテーマで解説を行いました

ぜひ、皆さんも実業務での活用に取り組んでみてください
引き続き、GASを楽しんでいきましょう!!

こーすけ先生

ありがたいことに今年の5月に起業してから忙しくさせてもらっていて技術ブログを更新する時間がまったく取れなかったよ

がすぴょん

更新3か月くらい空いちゃったけど、なんだかんだ毎日100人は見に来てくれて本当にありがたいよね

こーすけ先生

今、「食をDXの力で盛り上げたい」という思いから
テイクアウト専門情報サイトを構築中です!
ひとまずXアカウントを開設したのでフォローしてくれると嬉しいな

こーすけ先生

「ToGoナビ」のリリース告知記事はこちら

こーすけ先生

おかげさまで今年5月に起業しました!
GASやGoogleサービス、プログラミング全般のご相談承ります!

この記事が気に入ったら
いいね または フォローしてね!

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

GASの人。ITベンダSEとして12年勤務し、民間、金融、官公庁の現場を一通り経験済。html、css、JavaScript、Java、PHPも分かります。最近は専らGASで小規模アプリケーションを頻繁に作成しています。GASのことなら何でもお任せあれ!現在は公務員として働きながら、起業に向けて着々と準備中です!

コメント

コメントする

CAPTCHA


目次