エクセルを使って請求書や見積書を手入力で作成している人は少なくありません。
このとき、膨大な量の請求書を手入力で作成するとなると、記載ミスを引き起こす可能性があります。また、量が多いために計算間違いをしたりしてしまうことがあります。
しかし、このようなミスはエクセルマクロVBAを活用することで大きく改善することができます。
そこで、請求書や見積書をエクセルマクロVBAで作成する方法を解説します。
・請求書はPDFとして作成し、所定フォルダへ保存
・請求書の締め日も調整可能(これは事前入力で対応)
・マクロ入りテンプレートダウンロード可能
このページで紹介しているマクロはダウンロード可能です。
ページ下部にフォームがありますので、ぜひお仕事でご活用ください。
目次
- 1 エクセルマクロで請求書を自動作成!取引先別に複数シートをPDF化
- 2 エクセルマクロで請求書を自動作成するメリット
- 3 エクセル関数での請求書作成はお勧めしない理由
- 4 VBA入りのエクセルファイルをダウンロード
- 5 VBA作成前の2つの事前準備
- 6 VBAのプログラムソース解説
- 6.1 プログラム0|変数宣言の指定
- 6.2 プログラム1|プログラム開始
- 6.3 プログラム2|FileSystemObjectの設定
- 6.4 プログラム3|新しいフォルダの名前を生成
- 6.5 プログラム4|プログラム3で生成した名前のフォルダが既に存在しなければ新しいフォルダを作成
- 6.6 プログラム5|新しいエクセルファイルを作成
- 6.7 プログラム6|新しいエクセルファイルのA1~D1にヘッダー情報を入力
- 6.8 プログラム7|VBAが保存されているエクセルの「Sheet1」を取得
- 6.9 プログラム8|ws1の最終行を取得
- 6.10 プログラム9|ws1のデータを取得
- 6.11 プログラム10|取引先のリストを作成(重複削除)
- 6.12 プログラム11|変数設定
- 6.13 プログラム12|取引先ごとに処理を実行
- 6.14 プログラム13|同フォルダの「template.xlsx」を開く
- 6.15 プログラム14|「template.xlsx」で指定した対象期間を設定
- 6.16 プログラム15|値を初期化
- 6.17 プログラム16|myrange1の内、対象取引先のものだけを処理
- 6.18 プログラム17|対象期間に含まれるものだけを処理
- 6.19 プログラム18|対象情報を「template.xlsx」へ出力
- 6.20 プログラム19|「template.xlsx」の必要箇所でセル結合し罫線を引く
- 6.21 プログラム20|合計金額を足す
- 6.22 プログラム21|「template.xlsx」の行を一つ足す
- 6.23 プログラム22|請求書IDを取得
- 6.24 プログラム23|「template.xlsx」の請求書のヘッダー情報を出力
- 6.25 プログラム24|「template.xlsx」のシート名を取引先名称に変更
- 6.26 プログラム25|「template.xlsx」をPDFとして保存
- 6.27 プログラム26|「template.xlsx」を新しいエクセルとして保存して閉じる
- 6.28 プログラム27|プログラム5で作成したエクセルに情報を出力
- 6.29 プログラム28|プログラム27でデータを入力したエクセルを保存して閉じる
- 6.30 プログラム29|プログラム終了
- 7 Excel VBAについて詳しく理解したいなら
- 8 VBAを自分で書けるようになる
エクセルマクロで請求書を自動作成!取引先別に複数シートをPDF化
このページでは以下のようなデータ一覧をもとに、PDFの請求書を作成するVBAプログラムを紹介します。
上記の取引データをもとに、取引先別に請求書を作成します。
作成に必要な事前準備は以下です。
1. 請求書テンプレートに必要データを入力
2. 取引データ一覧を取得
3. VBAプログラムを実行
このページでは紹介している請求書マクロをダウンロードできるようにしています。
興味がある人はダウンロードしてご活用ください。
1. 請求書テンプレートに必要データを入力
このページで紹介するVBAプログラムでは、上記のような請求書テンプレートにデータを入力していきます。
「template.xlsx」という名前でフォルダに保存してあります。
「template.xlsx」の請求書に記載する情報のなかには、いくつかの不変情報があるため事前に入力しておきます。
事前入力すべき項目1:会社情報
会社情報を入力しておきます。
ここでは、以下の情報をテンプレートに入力しておきます。
住所 XXXXXXX
電話番号 XXXXXXX
担当者 XXXXXXX
上記の○○、XXXXXXに関しては、必要な情報を入力しておきます。
事前入力すべき項目2:銀行口座情報
支払先の銀行口座番号は不変情報なので、事前に入力しておきます。
事前入力すべき項目3:取引期間(締め日)
請求書として対象とする期間を入力します。当然ですが、数値を入れないとエラーが出るので、注意です。
5行目:期間終了:年(J5)、月(K5)、日(L5)
未入力のままVBAプログラムを実行すると、エラーが出て途中で処理が止まります。
なおエクセルの背景が灰色になっている通り、この部分は印刷されません。
2. 取引データ一覧を取得
このページでは、上記のような取引データ一覧をもとに請求書を自動作成します。
当然ですが取引データ一覧に入力されているデータが異なると、このページで紹介するプログラムでは正しく請求書が作成されません。
必要に応じてVBAプログラムの修正が必要になりますので、注意が必要です。
3. VBAプログラムを実行
請求書作成ボタンを押すとVBAプログラムが実行されます。
以下のように請求書が自動作成されます。
取引先ごとにフォルダに自動作成されます。
ボタンにVBAプログラムを登録
上記の画像では、VBAプログラムをボタンに登録しています。
ボタンにVBAプログラムを登録することで、ボタンを押下しプログラムを実行することができます。
ボタンをVBAプログラムを設定したい場合は、以下で動画も交えて設定方法を紹介しているので、そちらをご覧ください。
エクセルマクロで請求書を自動作成するメリット
このページでは、エクセルマクロで請求書や見積書を自動作成する方法を紹介しています。
これを使うと、下記の3つのメリットがあります。
No | メリット | 内容 |
1 | 記載ミスが減る | すべてプログラミングで自動処理されるため、ヒューマンエラーがなくなる |
2 | 大量に書類を作成可能 | ボタン一つで一度に複数の処理ができ、大量の処理ができる |
3 | 保管先指定や名称統一が可能 | プログラミングで決まった名称や場所に指定して保管される |
それでは上記のメリットについて具体的に説明していきます。
メリット1|記載ミスが減る
エクセルVBAを利用して請求書を作成すると記載ミスが少なくなります。
エクセルマクロを使わない場合、請求書の日付を間違えたり、顧客の氏名が違ってしまったりします。
しかしエクセルマクロを使う場合、プログラムに間違いがない限り記載ミスはありません。
基本は顧客マスターから抽出するため、顧客の氏名は正しいものが入力されます。
このようにエクセルVBAを利用し請求書や見積書を作成した場合はあらゆる記載ミスが少なくなります。
請求書のような資料は正確な記載が重要な作業です。よってエクセルVBAのような仕組みを利用し記載ミスを減らしていくことが必要となってきます。
メリット2|大量の書類を自動作成できる
エクセルVBAを利用すると大量の書類を自動で簡易的に作成できるようになります。
エクセルマクロを使わない場合
・印刷するときも内容を変えながら印刷しなければいけない
・複数ページの処理が困難
エクセルマクロを使う場合
・「内容を変える」→「印刷する」をワンクリックでできる
・複数ページも自動的に処理できる
請求書は宛先を変えたり、金額を変えたりして何回も編集や印刷をする必要があり、時間や手間がかかりがちです。
特に複数ページにわたる処理は時間がかかります。
しかしながら、エクセルVBAで請求書を作成すると編集や印刷をプログラムが自動的に実行されます。
そのため、面倒な編集や印刷も必要がなくなります。
複数ページの処理も自動的に実行しますので、大幅な作業効率の改善につながります。
メリット3|保管先指定や名称統一が可能
エクセルVBAはプログラムによって保管先や名称を固定できます。
そのため、社内全体で保管先と名称が統一できます。
エクセルマクロを使わない場合
・ファイルの管理ができなくなる(ファイルの保管先が担当者にしかわからない)
エクセルマクロを使う場合
・請求書の保管先を指定できる
このようにエクセルVBAを利用すると適切なファイル管理ができます。
そのため「あの請求書や見積書のファイルはどこにいった?」というようなありがちなミスもなくなります。
エクセル関数での請求書作成はお勧めしない理由
エクセル関数でも請求書作成をできると考える人もいるかもしれません。
しかし、実はエクセルの関数には落とし穴があります。
なぜなら、関数による自動計算や計算式は処理が重たくなるためです。処理が重たくなると下記のような弊害がでてきます。
・処理が重く、作業効率が悪くなる
・式が壊れると気づきにくく、修復しにくい
最も大きなリスクは、請求書のデータが蓄積してくると、エクセルファイルの破損の危険が高まることです。
そのため、安心して請求書作成を自動化したいならエクセルVBAでの処理をおすすめします。
以下のリンクで、エクセルVBAとエクセル関数についてメリットとデメリットを紹介しています。
ぜひ合わせて読んでみてください。
VBA入りのエクセルファイルをダウンロード
以下で紹介しているVBAプログラムをそのまま使いたい人は、以下のフォームからダウンロードできます。
登録したメールアドレスへ「VBAプログラムが含まれたエクセル」を送信します。
本プログラムの内容をそのまま使用可能です。ぜひお仕事にお役立てください。
VBA作成前の2つの事前準備
ExcelでVBAを使うためにはいくつかの準備が必要です。
具体的には以下です。
2. フォルダとファイルを扱うための事前準備
上記の2つに関して、以下で説明します。
1. ExcelでVBAを使うための事前準備
Excelで、以下の2つの準備をします。
2. 開発タブを追加
保存ファイルの拡張子変更、Excelの基本設定変更の2つです。
2つともで難しい作業はなく、それぞれ1分もあれば設定変更可能です。
上記に関しては、以下の記事で解説をしています。
もしVBAを使うための準備段階に不安がある人は上記をご覧ください。
この内容は以下の動画で紹介しています。
入門エクセルマクロの使い方|マクロ作成から実行までを徹底解説
文字や画像だけで分かりづらい人は上記の動画をご覧ください。
2. フォルダとファイルを扱うための事前準備
VBAでフォルダやファイルを操作するためのプログラミングに入る前に、VBAの設定を変更しておく必要があります。
実はVBAのデフォルト設定の場合、フォルダやファイル操作はできません。
なぜなら、参照設定が変更だからです。参照設定とは、機能拡張させることです。
参照設定を変更することで、フォルダとファイルを操作できるようになります。
この記事で紹介するプログラムを使えるようにするには、以下の参照設定を変更します。
「Microsoft Scripting Runtime」にチェックを入れることで、添付ファイルを保存するフォルダを作成することができるようになります。
VBAの参照設定の方法
以下、具体的な設定方法を紹介します。
手順2.Microsoft Scripting Runtimeのライブラリにチェックを入れて、OKをクリック
詳細はこちらの画像の通りです。
手順1.VBEを開いて頂いて、「ツール」→「参照設定」
手順2.Microsoft Scripting Runtimeのライブラリにチェックを入れて、OKをクリック
上記の設定をしていないと、本事例で紹介しているプログラムは動作しません。
必ずチェックを入れるようにします。
それでは、以下でプログラムについて詳細を説明します。
VBAのプログラムソース解説
今回紹介するプログラムの概要は以下です。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 |
'プログラム0|変数宣言の指定 Option Explicit 'プログラム1|プログラム開始 Sub CreateSeikyusho() 'プログラム2|FileSystemObjectの設定 Dim fs As FileSystemObject Set fs = New FileSystemObject 'プログラム3|新しいフォルダの名前を生成 Dim newfoldername As String newfoldername = Format(Now, "YYYY-MM-DD") & "_請求書リスト" 'プログラム4|プログラム3で生成した名前のフォルダが既に存在しなければ新しいフォルダを作成 Dim newfolderpath As String newfolderpath = ThisWorkbook.Path & "\" & newfoldername If fs.FolderExists(newfolderpath) = False Then fs.CreateFolder newfolderpath End If 'プログラム5|新しいエクセルファイルを作成 Dim newbook As Workbook Set newbook = Workbooks.Add Dim newfilename As String newfilename = "00_" & newfoldername & ".xlsx" newbook.SaveAs filename:=newfolderpath & "\" & newfilename 'プログラム6|新しいエクセルファイルのA1~D1にヘッダー情報を入力 Dim newsheet As Worksheet Set newsheet = newbook.Worksheets(1) newsheet.Range("A1").Value = "No" newsheet.Range("B1").Value = "取引先名称" newsheet.Range("C1").Value = "期間" newsheet.Range("D1").Value = "PDF保管フォルダ" 'プログラム7|VBAが保存されているエクセルの「Sheet1」を取得 Dim ws1 As Worksheet Set ws1 = ThisWorkbook.Worksheets("Sheet1") 'プログラム8|ws1の最終行を取得 Dim cmax As Long cmax = ws1.Range("A65536").End(xlUp).Row 'プログラム9|ws1のデータを取得 Dim myrange1 As Variant myrange1 = ws1.Range("A2:E" & cmax).Value 'プログラム10|取引先のリストを作成(重複削除) Dim dic As New dictionary Set dic = CreateObject("Scripting.Dictionary") Dim torihiki_list() As String ReDim torihiki_list(0) Dim i As Long For i = LBound(myrange1) + 1 To UBound(myrange1) If Not dic.Exists(myrange1(i, 5)) Then dic.Add myrange1(i, 5), myrange1(i, 5) torihiki_list(UBound(torihiki_list)) = myrange1(i, 5) ReDim Preserve torihiki_list(UBound(torihiki_list) + 1) Debug.Print (myrange1(i, 5)) End If Next 'プログラム11|変数設定 Dim seikyusyo_id As String Dim templatebook As Workbook Dim templatesheet As Worksheet Dim startdate As Date, enddate As Date, hiduke As Date Dim goukei As Long, gyo As Long Dim k As Long 'プログラム12|取引先ごとに処理を実行 For k = LBound(torihiki_list) To UBound(torihiki_list) - 1 'プログラム13|同フォルダの「template.xlsx」を開く Set templatebook = Workbooks.Open(filename:=ThisWorkbook.Path & "\template.xlsx") Set templatesheet = templatebook.Worksheets("テンプレート") 'プログラム14|「template.xlsx」で指定した対象期間を設定 startdate = templatesheet.Range("J4").Value & "/" & templatesheet.Range("K4").Value & "/" & templatesheet.Range("L4").Value enddate = templatesheet.Range("J5").Value & "/" & templatesheet.Range("K5").Value & "/" & templatesheet.Range("L5").Value 'プログラム15|値を初期化 goukei = 0 gyo = 14 'プログラム16|myrange1の内、対象取引先のものだけを処理 For i = LBound(myrange1) + 1 To UBound(myrange1) If myrange1(i, 5) = torihiki_list(k) Then 'プログラム17|対象期間に含まれるものだけを処理 hiduke = myrange1(i, 3) If startdate <= hiduke <= enddate Then 'プログラム18|対象情報を「template.xlsx」へ出力 With templatesheet .Range("B" & gyo).Value = myrange1(i, 1) .Range("C" & gyo).Value = myrange1(i, 2) .Range("F" & gyo).Value = Format(myrange1(i, 3), "yyyy/mm/dd") .Range("G" & gyo).Value = Format(myrange1(i, 4), "#,##0") End With 'プログラム19|「template.xlsx」の必要箇所でセル結合し罫線を引く templatesheet.Range(Cells(gyo, 3), Cells(gyo, 5)).Merge templatesheet.Range(Cells(gyo, 2), Cells(gyo, 7)).Borders.LineStyle = 1 'プログラム20|合計金額を足す goukei = goukei + myrange1(i, 4) 'プログラム21|「template.xlsx」の行を一つ足す gyo = gyo + 1 End If End If Next 'プログラム22|請求書IDを取得 seikyusyo_id = Format(Now, "YYYYMMDD") & "_" & torihiki_list(k) Debug.Print "請求書ID:" & seikyusyo_id 'プログラム23|「template.xlsx」の請求書のヘッダー情報を出力 With templatesheet .Range("B4").Value = torihiki_list(k) .Range("B6").Value = startdate & "~" & enddate & "の請求書" .Range("C9").Value = Format(goukei, "#,##0") .Range("G3").Value = seikyusyo_id .Range("G4").Value = Format(Now, "YYYY/MM/DD") .Range("C11").Value = Format(DateAdd("d", 15, Now), "yyyy/mm/dd") End With Debug.Print "セルB4:" & torihiki_list(k) Debug.Print "セルB6:" & startdate & "~" & enddate & "の請求書" Debug.Print "セルC9:" & Format(goukei, "#,##0") Debug.Print "セルG3:" & seikyusyo_id Debug.Print "セルG4:" & Format(Now, "YYYY/MM/DD") Debug.Print "セルC11:" & Format(DateAdd("d", 15, Now), "yyyy/mm/dd") 'プログラム24|「template.xlsx」のシート名を取引先名称に変更 templatesheet.Name = torihiki_list(k) 'プログラム25|「template.xlsx」をPDFとして保存 Dim pdf_path As String pdf_path = newfolderpath & "\" & seikyusyo_id & "_report.pdf" templatesheet.ExportAsFixedFormat 0, pdf_path 'プログラム26|「template.xlsx」を新しいエクセルとして保存して閉じる Dim filename As String filename = newfolderpath & "\" & seikyusyo_id & ".xlsx" templatesheet.SaveAs filename:=filename templatebook.Close 'プログラム27|プログラム5で作成したエクセルに情報を出力 With newsheet .Range("A2").Offset(k, 0).Value = k + 1 .Range("B2").Offset(k, 0).Value = seikyusyo_id .Range("C2").Offset(k, 0).Value = startdate & "~" & enddate .Range("D2").Offset(k, 0).Value = pdf_path End With Next 'プログラム28|プログラム27でデータを入力したエクセルを保存して閉じる newbook.Save newbook.Close 'プログラム29|プログラム終了 End Sub |
プログラム0|変数宣言の指定
1 |
Option Explicit |
「Option Explicit」とは、変数宣言を強制するためのものです。
これを入れておくと、変数を定義していない場合、エラーが出ます。
つまり、「Option Explicit」を入力しておくことで、たとえば「Dim i」をあらかじめ入力しないと、「i」という変数を使えません。
もし「Option Explicit」を入力しているのに、「Dim i」を書かずに「i = 1」と書くと、エラーメッセージが表示されます。
実は、この機能はあくまでオプションです。「Option Explicit」を入力しなくても、プログラムは動きます。
しかし、これを入れておくことで、変数の誤記によるエラーを防止することができます。
結果的に、プログラム作成速度が上がるので、「Option Explicit」を入力することを習慣化することをオススメします。
プログラム1|プログラム開始
1 |
Sub CreateSeikyusho() |
「Sub CreateSeikyusho()」のプログラムを開始することを意味します。
VBAではプロシージャという単位でプログラムを呼び出します。
プロシージャの構文は下記となっています。
1 2 3 |
Sub プロシージャ名() '実行プログラム End Sub |
「Sub」で始まり「End Sub」で終わります。
「Sub XXXX」の「XXXX」の部分がプロシージャ名です。
このプロシージャ名はあらゆる文字(アルファベット、ひらがな、漢字、数字など)が使用可能です。
ただし、プロシージャ名の先頭は数字を入れるとエラーとなります。
あとで見たときに、「何のプログラムだったのか?」とならないようにするためです。
なお、「()」の中には何も記入されていません。これは引数なしという意味です。なお、プログラム2-1では引数を受け取ってプログラムを実行します。
初心者の内は、引数ということが分からなくてもVBAプログラムを書くことは可能です。
興味があれば、「VBA 引数」で検索して調べてみてください。
プログラム2|FileSystemObjectの設定
1 2 |
Dim fs As FileSystemObject Set fs = New Scripting.FileSystemObject |
FileSystemObjectは、ファイルやフォルダを操作するときに使うオブジェクトです。
本事例ではFileSystemObjectをfsという変数として扱います。
変数fsは定義するだけではなく、「Set fs = New Scripting.FileSystemObject」と記入して使えるようになります。
これを忘れてしまうことが多いので、注意が必要です。
FileSystemObjectに関する注意点
FileSystemObjectを使用するためには、参照設定でMicrosoft Scripting Runtimeを設定に入れておく必要があります。
設定を忘れると動作しませんので、事前に設定を変更しておく必要があります。
なお、FileSystemObject以外にもフォルダを操作する方法はあります。しかしFileSystemObjectを使えば、ファイルとフォルダを両方操作できるので、覚えることが少なくて済みます。
細かいことを覚えるのが面倒な人は、ファイルやフォルダを操作するときはFileSystemObjectを使うと覚えておくといいです。
プログラム3|新しいフォルダの名前を生成
1 2 |
Dim newfoldername As String newfoldername = Format(Now, "YYYY-MM-DD") & "_請求書リスト" |
newfoldernameという文字列型の変数に値を設定します。
Debug.Printで検証してみます。
1 2 3 4 5 |
Dim newfoldername As String newfoldername = Format(Now, "YYYY-MM-DD") & "_請求書リスト" Debug.Print "newfoldername:" & newfoldername >>>newfoldername:2021-05-16_請求書リスト |
上記ではDebug.Printで、上記のような文字列を取得できます。
次のプログラムで、この名前を使ってフォルダを作成します。
プログラム4|プログラム3で生成した名前のフォルダが既に存在しなければ新しいフォルダを作成
1 2 3 4 5 |
Dim newfolderpath As String newfolderpath = ThisWorkbook.Path & "\" & newfoldername If fs.FolderExists(newfolderpath) = False Then fs.CreateFolder newfolderpath End If |
VBAを実行しているエクセル(ThisWorkbook)と同じフォルダにnewfoldernameで設定した名前でフォルダを作成します。
プログラム5|新しいエクセルファイルを作成
1 2 3 4 5 |
Dim newbook As Workbook Set newbook = Workbooks.Add Dim newfilename As String newfilename = "00_" & newfoldername & ".xlsx" newbook.SaveAs filename:=newfolderpath & "\" & newfilename |
新しいエクセルファイルを作成して、プログラム4で作成したフォルダに保存します。
新しいエクセルファイルの名前をDebug.Printでチェックしてみます。
1 2 3 4 5 6 7 8 |
Dim newbook As Workbook Set newbook = Workbooks.Add Dim newfilename As String newfilename = "00_" & newfoldername & ".xlsx" newbook.SaveAs filename:=newfolderpath & "\" & newfilename Debug.Print "newfilename:" & newfilename >>>newfilename:00_2021-05-16_請求書リスト.xlsx |
上記の名前で新しいエクセルファイルを保存します。
プログラム6|新しいエクセルファイルのA1~D1にヘッダー情報を入力
1 2 3 4 5 6 |
Dim newsheet As Worksheet Set newsheet = newbook.Worksheets(1) newsheet.Range("A1").Value = "No" newsheet.Range("B1").Value = "取引先名称" newsheet.Range("C1").Value = "期間" newsheet.Range("D1").Value = "PDF保管フォルダ" |
新しい作成したエクセルの左から1番目(一番左)にあるシートをnewsheetとして取得します。
newsheetで指定した新しいエクセルのA1,B1,C1,D1に値を入力します。
B1:取引先名称
C1:期間
D1:PDF保管フォルダ
プログラム7|VBAが保存されているエクセルの「Sheet1」を取得
1 2 |
Dim ws1 As Worksheet Set ws1 = ThisWorkbook.Worksheets("Sheet1") |
VBAが保存されているエクセルのシート「Sheet1」をws1として扱います。
プログラム8|ws1の最終行を取得
1 2 |
Dim cmax As Long cmax = ws1.Range("A65536").End(xlUp).Row |
Sheet1(ws1)の最終行を取得します。A75までデータが入っているので、cmax=75となります。
実際にDebug.Printでチェックしてみます。
1 2 3 4 5 |
Dim cmax As Long cmax = ws1.Range("A65536").End(xlUp).Row Debug.Print "cmax:" & cmax >>>cmax:75 |
Sheet1の最終行を使って、エクセルの値として取得するデータ範囲を決定します。
プログラム9|ws1のデータを取得
1 2 |
Dim myrange1 As Variant myrange1 = ws1.Range("A2:E" & cmax).Value |
Sheet1(ws1)のA2:E75の範囲をmyrange1として取得します。
cmax=75のため、E75まで取得するデータ範囲となります。
プログラム10|取引先のリストを作成(重複削除)
1 2 3 4 5 6 7 8 9 10 11 12 |
Dim dic As New dictionary Set dic = CreateObject("Scripting.Dictionary") Dim torihiki_list() As String ReDim torihiki_list(0) Dim i As Long For i = LBound(myrange1) + 1 To UBound(myrange1) If Not dic.Exists(myrange1(i, 5)) Then dic.Add myrange1(i, 5), myrange1(i, 5) torihiki_list(UBound(torihiki_list)) = myrange1(i, 5) ReDim Preserve torihiki_list(UBound(torihiki_list) + 1) End If Next |
配列を使って、A列の重複を削除したリストを作成します。
実際にDebug.Printで検証してみます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
Dim dic As New dictionary Set dic = CreateObject("Scripting.Dictionary") Dim torihiki_list() As String ReDim torihiki_list(0) Dim i As Long For i = LBound(myrange1) + 1 To UBound(myrange1) If Not dic.Exists(myrange1(i, 5)) Then dic.Add myrange1(i, 5), myrange1(i, 5) torihiki_list(UBound(torihiki_list)) = myrange1(i, 5) ReDim Preserve torihiki_list(UBound(torihiki_list) + 1) Debug.Print (myrange1(i, 5)) 'ここの値をDebug.Printでチェック End If Next >>>愛媛不動産 >>>茨城信託 >>>愛知製本 >>>茨城物産 >>>愛知販売 |
これでtorihiki_listの配列に[“愛媛不動産”,”茨城信託”,”愛知製本”,”茨城物産”,”愛知販売”]のデータを取得できました。
このtorihiki_listを使って処理を行っていきます。
プログラム11|変数設定
1 2 3 4 5 6 |
Dim seikyusyo_id As String Dim templatebook As Workbook Dim templatesheet As Worksheet Dim startdate As Date, enddate As Date, hiduke As Date Dim goukei As Long, gyo As Long Dim k As Long |
プログラム12以降で使用する変数を設定します。
プログラム12|取引先ごとに処理を実行
1 |
For k = LBound(torihiki_list) To UBound(torihiki_list) - 1 |
プログラム10で取得したtorihiki_listの値を順々に処理していきます。
torihiki_listに格納されている値をDebug.Printで確認します。
1 2 3 4 5 6 7 8 9 |
For k = LBound(torihiki_list) To UBound(torihiki_list) - 1 Debug.Print k, torihiki_list(k) Next >>>0 愛媛不動産 >>>1 茨城信託 >>>2 愛知製本 >>>3 茨城物産 >>>4 愛知販売 |
プログラム10でも確認しましたが、上記の値が入っていることがチェックできました。
プログラム13|同フォルダの「template.xlsx」を開く
1 2 |
Set templatebook = Workbooks.Open(filename:=ThisWorkbook.Path & "\template.xlsx") Set templatesheet = templatebook.Worksheets("テンプレート") |
「template.xlsx」をtemplatebook、「template.xlsx」の「Sheet1」をtemplatesheetとして扱います。
このtemplatesheetにデータを入力していきます。
プログラム14|「template.xlsx」で指定した対象期間を設定
1 2 |
startdate = templatesheet.Range("J4").Value & "/" & templatesheet.Range("K4").Value & "/" & templatesheet.Range("L4").Value enddate = templatesheet.Range("J5").Value & "/" & templatesheet.Range("K5").Value & "/" & templatesheet.Range("L5").Value |
年:セルJ4
月:セルK4
日:セルL4
年:セルJ5
月:セルK5
日:セルL5
この事例ではstartdate、enddateとして以下の値が入力されています。
1 2 3 4 5 6 7 |
startdate = templatesheet.Range("J4").Value & "/" & templatesheet.Range("K4").Value & "/" & templatesheet.Range("L4").Value enddate = templatesheet.Range("J5").Value & "/" & templatesheet.Range("K5").Value & "/" & templatesheet.Range("L5").Value Debug.Print "startdate:" & startdate Debug.Print "enddate:" & enddate >>>startdate:2020/07/01 >>>enddate:2020/07/31 |
上記の値を取得して、startdate(期間開始)とenddate(期間終了)の日付を取得します。
なお、これらの値は事前に入力しておきます。
プログラム15|値を初期化
1 2 |
goukei = 0 gyo = 14 |
変数goukeiとgyoを上記の値で初期化します。
gyo:請求書の詳細情報を14行目から入力するため14で初期化
プログラム16|myrange1の内、対象取引先のものだけを処理
1 2 |
For i = LBound(myrange1) + 1 To UBound(myrange1) If myrange1(i, 5) = torihiki_list(k) Then |
myrange1(プログラム9)で取得したデータの中で、対象となる取引先のものだけを処理します。
myrange1(i,5)は、ws1のE列i行目の値を指します。このmyrange1(i,5)がtorihiki_list(k)と合致するデータのみを対象とします。
プログラム17|対象期間に含まれるものだけを処理
1 2 |
hiduke = myrange1(i, 3) If startdate <= hiduke <= enddate Then |
myrange(i,3)をhidukeとして取得します。これはws1のC列の値をhidukeとして取得することと同じです。
期間内に入っていることを検証
hidukeがプログラム14で設定したstartdate,enddateの期間内に含まれるかどうかを検証します。
stardateは「2020/07/01」でenddateは「2020/07/31」なので、これらを不等式でつなぐことで、hidukeが期間内に含まれることを確認することができます。
プログラム18|対象情報を「template.xlsx」へ出力
1 2 3 4 5 6 |
With templatesheet .Range("B" & gyo).Value = myrange1(i, 1) .Range("C" & gyo).Value = myrange1(i, 2) .Range("F" & gyo).Value = Format(myrange1(i, 3), "yyyy/mm/dd") .Range("G" & gyo).Value = Format(myrange1(i, 4), "#,##0") End With |
請求書テンプレートファイルである「template.xlsx」の14行目からデータを入力していきます。
C列:摘要:myrange1(i,2)→ws1のB列
F列:納品日(yyyy/mm/ddのFormat)
G列:取引金額(3桁ごとに「,」が入るFormat)
上記のデータを入力していきます。
プログラム19|「template.xlsx」の必要箇所でセル結合し罫線を引く
1 2 |
templatesheet.Range(Cells(gyo, 3), Cells(gyo, 5)).Merge templatesheet.Range(Cells(gyo, 2), Cells(gyo, 7)).Borders.LineStyle = 1 |
C列~E列をセル結合し、B列~G列に罫線を引きます。
プログラム20|合計金額を足す
1 |
goukei = goukei + myrange1(i, 4) |
myrange(i,4)に格納されている取引金額を、変数goukeiで累算していきます。
このgoukeiは請求書のヘッダー情報として後半のプログラムで使用します。
プログラム21|「template.xlsx」の行を一つ足す
1 2 3 4 |
gyo = gyo + 1 End If End If Next |
gyoに1加算していきます。
gyoは請求書の詳細データを入力する行を指しています。
ここで1加算することで、新しいデータが一つ下の行に入力されていくようにしています。
なおデータが全部入力されると、以下のように請求書の詳細情報が埋まります。
プログラム22|請求書IDを取得
1 |
seikyusyo_id = Format(Now, "YYYYMMDD") & "_" & torihiki_list(k) |
seikyusyo_idで請求書IDを作成します。請求書IDは請求書を管理に使えるので、作成しておくと後々重宝します。
seikyusyo_idは「YYYYMMDD_取引先名称」としています。以下のDebug.Printでチェックしてみます。
1 2 3 4 |
seikyusyo_id = Format(Now, "YYYYMMDD") & "_" & torihiki_list(k) Debug.Print "請求書ID:" & seikyusyo_id >>>請求書ID:20210516_愛媛不動産 |
上記のように請求書IDを作成します。
プログラム23|「template.xlsx」の請求書のヘッダー情報を出力
1 2 3 4 5 6 7 8 |
With templatesheet .Range("B4").Value = torihiki_list(k) .Range("B6").Value = startdate & "~" & enddate & "の請求書" .Range("C9").Value = Format(goukei, "#,##0") .Range("G3").Value = seikyusyo_id .Range("G4").Value = Format(Now, "YYYY/MM/DD") .Range("C11").Value = Format(DateAdd("d", 15, Now), "yyyy/mm/dd") End With |
請求書のヘッダー情報を入力していきます。
ヘッダー情報としては以下を設定します。
セルB6:請求期間:2020/07/01~2020/07/31の請求書
セルC9:合計金額:620,000
セルG3:請求書ID:20210516_愛媛不動産
セルG4:請求日:2021/05/16
セルC11:お支払い期限:2021/05/31 ←ここでは請求日+15日で設定している
実際にDebug.Printでチェックしてみます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
With templatesheet .Range("B4").Value = torihiki_list(k) .Range("B6").Value = startdate & "~" & enddate & "の請求書" .Range("C9").Value = Format(goukei, "#,##0") .Range("G3").Value = seikyusyo_id .Range("G4").Value = Format(Now, "YYYY/MM/DD") .Range("C11").Value = Format(DateAdd("d", 15, Now), "yyyy/mm/dd") End With Debug.Print "セルB4:" & torihiki_list(k) Debug.Print "セルB6:" & startdate & "~" & enddate & "の請求書" Debug.Print "セルC9:" & Format(goukei, "#,##0") Debug.Print "セルG3:" & seikyusyo_id Debug.Print "セルG4:" & Format(Now, "YYYY/MM/DD") Debug.Print "セルC11:" & Format(DateAdd("d", 15, Now), "yyyy/mm/dd") >>>セルB4:愛媛不動産 >>>セルB6:2020/07/01~2020/07/31の請求書 >>>セルC9:620,000 >>>セルG3:20210516_愛媛不動産 >>>セルG4:2021/05/16 >>>セルC11:2021/05/31 |
プログラム24|「template.xlsx」のシート名を取引先名称に変更
1 |
templatesheet.Name = torihiki_list(k) |
シート名を取引先名称に変更します。
プログラム25|「template.xlsx」をPDFとして保存
1 2 3 |
Dim pdf_path As String pdf_path = newfolderpath & "\" & seikyusyo_id & "_report.pdf" templatesheet.ExportAsFixedFormat 0, pdf_path |
作成した請求書をPDFとして保存します。
保存先はプログラム4で作成したフォルダです。
プログラム26|「template.xlsx」を新しいエクセルとして保存して閉じる
1 2 3 4 |
Dim filename As String filename = newfolderpath & "\" & seikyusyo_id & ".xlsx" templatesheet.SaveAs filename:=filename templatebook.Close |
請求書として作成したエクセルファイルもプログラム4で作成したフォルダに保存して閉じます。
すべての請求書に対して処理が終わると、以下のようにPDFとエクセルがフォルダに保存されます。
プログラム27|プログラム5で作成したエクセルに情報を出力
1 2 3 4 5 6 7 |
With newsheet .Range("A2").Offset(k, 0).Value = k + 1 .Range("B2").Offset(k, 0).Value = seikyusyo_id .Range("C2").Offset(k, 0).Value = startdate & "~" & enddate .Range("D2").Offset(k, 0).Value = pdf_path End With Next |
プログラム5で作成したエクセルへデータを出力します。
ここには作成した請求書の一覧を出力します。
ここで敢えて一覧で表示している理由は、このままメール送信を想定しているからです。
作成した請求書は送り先があります。基本的にはメール送信を行うはずのため、請求書PDFのフルパスを入力しているのです。
個別にメールを送信するVBAプログラムは以下で紹介しています。
上記のページと組み合わせることで、さらに効率的に作業を行うことができます。
ぜひ参考にしてみてください。
プログラム28|プログラム27でデータを入力したエクセルを保存して閉じる
1 2 |
newbook.Save newbook.Close |
プログラム27で作成したエクセルを上書き保存して閉じます。
プログラム29|プログラム終了
1 |
End Sub |
プログラム1と対になるプログラムで、プログラム終了させるための記述です。
「End Sub」を読み込むと、プログラムが終了します。
プログラムの解説はここまでです。
Excel VBAについて詳しく理解したいなら
VBAを活用すると、仕事を効率化できる幅を広げることができます。
たとえば私が実際にVBAを活用して効率化してきた作業は以下の記事で紹介しています。
興味がある人は以下の記事もご覧ください。
動画でも解説しています。
エクセルマクロVBAで出来ることを15の事例で紹介|日常業務をラクにするヒントを見つけよう!
(音声が小さいので、ボリュームを上げてご覧いただければと思います)
VBAの勉強方法
私はプログラミング初心者からVBAを勉強を始めて少しずつレベルアップしていきました。
成長の過程は以下で紹介しています。
学習の過程では、意識すべきポイントがあります。
特に独学の場合だと、勉強を優先してしまい、肝心な実践を疎かにしがちです。
私の経験では、実践から逆算した勉強が必要だと考えています。
目指すべきは会社でお金をもらいながら勉強することです。
要はVBAを仕事の一つとして上司に認めてもらうのです。
そうすればわざわざ高いお金を払って勉強をする必要がなくなります。
しかも作業を自動化して、会社やチームに貢献しつつ、自らのスキルアップできます。
そのために必要な考え方を以下で紹介しています。
とはいえ、プログラミング初心者でVBAについて知識ゼロの人もいるはずです。
いきなり会社でVBAで使うことさえ、とてつもなくハードルが高く見えてしまうものです。
その場合は、VBAの基本について学ぶ必要があります。
たとえば車の運転も慣れてしまえば、たいしたことではありません。
しかし教習所で運転の基本を学び、免許を取得することで、公道で運転できるようになります。
VBAも同じです。VBAに免許はありませんが、まずは基本を学ばないことには会社で使えるレベルにはなりません。
実際に私もプログラミング初心者のときは、動画を見たり書籍を読んだりして勉強しました。
今はオンラインの教材で無料で学習できるものも多いです。
上記のリンクでは、私の経験から勉強にオススメの教材を紹介しています。
興味がある人はご覧ください。
VBAを自分で書けるようになる
さて、本記事で紹介したマクロを利用すれば、作業の自動化が可能になります。
しかしデメリットもあります。それはカスタムできないことです。
なぜなら、色々な要望が増えるからです。
この動画を見たとき、「もっと○○ができるのでは?」や「ここはなんとかならないのか」と感じる人は少なくないはずです。
例えば、「他の条件を付け加えたい」や「日付毎に条件を変えたい」といった要望が出るかもしれません。
このような要望を満たすには、マクロを勉強して自力でマクロを編集できるようになる必要があります。
もし、自力でマクロを編集できるようになれば、今より仕事の効率はグッと上がります。
実際、私も自力でマクロを書けるようになってからは、仕事の生産性が一気に上がりました。
他の人が30分~1時間かけて行う仕事が、ボタン一つで終わらせることができるようになったのです。
その結果、周囲からの信頼も増し、仕事で高い評価を得られるようになりました。
ただ、要望に応えるようになるためには、マクロを学ぶ必要があります。
まずは無料でマクロを勉強してみる
ウェブや書籍で勉強すれば、マクロを習得できると考えている人は少なくありません。
しかし、仕事で使えるマクロを習得したいなら、仕事で使える部分に特化した教材で学ぶことをお勧めします。
なぜなら、ウェブや書籍には仕事に関係しない部分まで提供していることが多いからです。
例えば、マクロ初心者なのに配列を学ぼうとする人がいます。実は配列なしでも仕事で使えるマクロを書くことは可能です。
しかし、マクロ初心者ほど「全ての知識が必要だ」と考えて、無駄な学習に時間を使ってしまうのです。詳しくは、こちらの記事で紹介しています。
そこで、私がお勧めするのは仕事に直結するマクロ教材です。とくにお勧めするのは、こちらの無料オンライン動画です。
なぜなら、仕事に直結する部分に絞って、エクセルマクロを学ぶことができるからです。
マクロの作り方・考え方から解説しているので、教材をしっかり学べばここで紹介したマクロをゼロから書けるようになります。
マクロ初心者が、仕事に直結したいマクロを学ぶなら、まずはこちらの無料オンライン動画を試すのがいいです。
興味がある人は、まずは無料でエクセルマクロの勉強を始めてみてください。
もっと学びたいと感じたら、さらに深く勉強をしてみることをお勧めします。