見積書や請求書、毎回テンプレートを開いて数字を手入力していませんか。
件数が少ないうちは気にならなくても、月に何十件と発生すると「転記ミス」「作業時間の圧迫」「担当者が休むと誰も作れない」という問題が起きやすくなります。
この記事では、Excel VBAを使って見積書・請求書の作成を自動化する方法を、業務システム開発の現場で培った考え方をベースに、VBA初心者の方でも再現できるレベルまで落とし込んで解説します。
VBA自動化で得られること
- 作業時間の削減:明細が10行あっても数秒で転記が完了する
- 転記ミスの防止:手入力によるコピペミス・桁ズレがなくなる
- 属人化の解消:マクロを実行するだけなので、担当者が不在でも誰でも作成できる
全体の仕組み(設計の考え方)
今回作るマクロの流れはシンプルに3ステップです。
- 入力シートに顧客名・品名・数量・単価を入力する
- マクロが見積書テンプレートへ自動転記し、合計金額を計算する
- PDFとして自動保存する(ファイル名も自動生成)
この「入力 → 転記 → 出力」という型は、見積書に限らず請求書・発注書など多くの帳票作成に応用できます。

サンプルコード
入力シート(シート名「入力」)はB1セルに顧客名、3行目以降に「品名/数量/単価」を入力する想定です。テンプレート側(シート名「見積書テンプレート」)は事前にレイアウトを組んでおきます(C3に顧客名、C4に発行日、8〜18行目が明細欄、19行目が合計欄)。

Sub CreateEstimate()
Const DETAIL_FIRST_ROW As Long = 8 ' テンプレート側の明細開始行
Const DETAIL_LAST_ROW As Long = 18 ' テンプレート側の明細最終行(19行目は合計欄)
Dim wsInput As Worksheet
Dim wsTemplate As Worksheet
Dim lastRow As Long
Dim i As Long
Dim itemCount As Long
Dim outputRow As Long
Dim total As Double
Dim customerName As String
Dim wbPath As String
Dim savePath As String
Dim fileName As String
On Error GoTo ErrHandler
Set wsInput = ThisWorkbook.Worksheets("入力")
Set wsTemplate = ThisWorkbook.Worksheets("見積書テンプレート")
' 入力チェック
customerName = wsInput.Range("B1").Value
If customerName = "" Then
MsgBox "顧客名が入力されていません。", vbExclamation
Exit Sub
End If
' 明細の件数チェック(テンプレートの明細欄は8~18行目の11行分)
lastRow = wsInput.Cells(wsInput.Rows.Count, "A").End(xlUp).Row
itemCount = 0
For i = 3 To lastRow
If wsInput.Cells(i, "A").Value = "" Then Exit For
itemCount = itemCount + 1
Next i
If itemCount = 0 Then
MsgBox "明細が入力されていません。入力シートの3行目以降に品名・数量・単価を入力してください。", vbExclamation
Exit Sub
End If
If itemCount > DETAIL_LAST_ROW - DETAIL_FIRST_ROW + 1 Then
MsgBox "明細は" & (DETAIL_LAST_ROW - DETAIL_FIRST_ROW + 1) & "行までです(現在" & itemCount & "行)。", vbExclamation
Exit Sub
End If
' 保存先チェック(OneDrive/SharePoint上で開いているとURL形式のパスになり、
' Dir/MkDir/PDF出力が「ファイル名または番号が不正です」でエラーになるため)
wbPath = ThisWorkbook.Path
If wbPath = "" Or Left(wbPath, 4) = "http" Then
MsgBox "このファイルはOneDrive/SharePoint上で開かれているか、未保存の状態です。" & vbCrLf & _
"一度ローカルフォルダ(例:C:\見積書)にコピーして保存してから実行してください。", vbCritical
Exit Sub
End If
' 前回の転記内容を消去(明細が前回より少ないと古い行が残るため)
wsTemplate.Range("B" & DETAIL_FIRST_ROW & ":F" & DETAIL_LAST_ROW).ClearContents
wsTemplate.Range("F19").ClearContents
' 表示形式(数量は3桁区切り、単価・金額・合計は3桁区切り+「円」)
wsTemplate.Range("D" & DETAIL_FIRST_ROW & ":D" & DETAIL_LAST_ROW).NumberFormat = "#,##0"
wsTemplate.Range("E" & DETAIL_FIRST_ROW & ":F" & DETAIL_LAST_ROW).NumberFormat = "#,##0""円"""
wsTemplate.Range("F19").NumberFormat = "#,##0""円"""
wsTemplate.Range("C3:C4").HorizontalAlignment = xlLeft ' 顧客名・発行日は左寄せ
' 顧客名・発行日を転記
wsTemplate.Range("C3").Value = customerName
wsTemplate.Range("C4").Value = Format(Date, "yyyy/mm/dd")
' 明細をループで転記
outputRow = DETAIL_FIRST_ROW
total = 0
For i = 3 To 2 + itemCount
wsTemplate.Cells(outputRow, "B").Value = wsInput.Cells(i, "A").Value ' 品名
wsTemplate.Cells(outputRow, "D").Value = wsInput.Cells(i, "B").Value ' 数量
wsTemplate.Cells(outputRow, "E").Value = wsInput.Cells(i, "C").Value ' 単価
wsTemplate.Cells(outputRow, "F").Value = wsInput.Cells(i, "B").Value * wsInput.Cells(i, "C").Value ' 金額
total = total + wsTemplate.Cells(outputRow, "F").Value
outputRow = outputRow + 1
Next i
wsTemplate.Range("F19").Value = total ' 合計欄(B19の「合計」と同じ行)
' PDF出力(ファイル名を「顧客名_見積書_日付」で自動生成)
savePath = wbPath & "\見積書\"
If Dir(savePath, vbDirectory) = "" Then MkDir savePath
fileName = savePath & customerName & "_見積書_" & Format(Date, "yyyymmdd") & ".pdf"
wsTemplate.ExportAsFixedFormat Type:=xlTypePDF, fileName:=fileName
MsgBox "見積書を作成しました:" & fileName, vbInformation
Exit Sub
ErrHandler:
MsgBox "エラーが発生しました:" & Err.Description, vbCritical
End Sub
コードのポイント
- 転記の前に、前回の明細と合計を ClearContents で消している。これがないと、明細が前回より少ないときに古い行がPDFに残る
- 先に明細の件数を数え、0行のときやテンプレートの明細欄(11行分)に収まらないときはメッセージを出して止める
- 明細欄の範囲は定数(DETAIL_FIRST_ROW / DETAIL_LAST_ROW)にまとめているので、テンプレートの行数を変えたときはここを直すだけでよい
- NumberFormat で単価・金額・合計を「50,000円」のように3桁区切り+円で表示し、HorizontalAlignment で顧客名・発行日を左寄せにしている
- On Error GoTo ErrHandler でエラー時にメッセージを表示し、原因不明のまま処理が止まる事態を防ぐ
- ThisWorkbook.Path が http で始まっていないか(OneDrive/SharePoint上で開いていないか)を事前にチェックしている。これがないとPDF出力の直前で原因の分かりにくいエラーになる(詳細は次項)
マクロを実行すると、見積書テンプレート側に顧客名・発行日・明細・合計が自動転記されます。

つまずきやすいポイントとエラー対処
サンプルコードのまま実行した場合、エラーは「エラーが発生しました:(エラー内容)」の形で表示されます。
エラー内容の部分で、下の表を探してください。
| エラー | よくある原因 | 対処 |
|---|---|---|
| 実行時エラー '9'(インデックスが有効範囲にありません) | シート名のタイプミス、シートが存在しない | Worksheets("入力") のシート名がタブの表示名と完全一致しているか確認 |
| 実行時エラー '13'(型が一致しません) | 数量・単価のセルに数値以外(空白や文字列)が入っている | 転記前に IsNumeric() で入力チェックを入れる |
| PDF出力でエラーになる | 保存先フォルダが存在しない、ファイルが開いたままになっている | Dir() でフォルダ有無を確認してから MkDir、保存前に同名ファイルを閉じているか確認 |
| 実行時エラー '52'(ファイル名または番号が不正です) | ブックをOneDrive/SharePoint上で開いていて ThisWorkbook.Path がURL形式(https://...)になっている | OneDrive/SharePoint管理外のローカルフォルダにコピーしてから実行する。サンプルコードでは Left(wbPath, 4) = "http" であらかじめ検知してメッセージを出している |
いずれも「決め打ちで進めず、実行前に状態をチェックする」のが対処の基本です。特にOneDrive同期フォルダで作業している方は多いため、最後の一件は見落としがちな落とし穴です。
応用編:さらに効率化するには
- Pythonでの自動化:openpyxl や pandas を使えば、同様の処理をPythonでも実装できます。複数ファイルの一括処理や他システムとの連携が必要になったら、Python移行を検討する価値があります。VBAとPythonどちらを学ぶべきか迷ったらVBAとPython、業務自動化にはどちらを学ぶべきか比較も参考にしてください。
- 生成AIツールの活用:見積書の文面(備考欄の定型文など)を生成AIツールで下書きし、VBAで転記する組み合わせも実務では有効です。
テンプレートを無料配布しています
この記事のマクロを組み込んだExcelファイルを、そのまま使える形で配布しています。
コードを1行ずつ写す手間を省きたい方、まず動くものを触ってみたい方はこちらからどうぞ。
まとめ
見積書・請求書の自動化は、いきなり完璧な仕組みを作る必要はありません。まずは今回のような「入力 → 転記 → 出力」の最小構成を自分の業務に合わせて1つ作ってみることが、自動化の第一歩になります。
VBAの構文を基礎から体系的に押さえたい場合はExcel VBA初心者がまず覚えるべき基本構文まとめを、複数シートに分かれたデータを1つにまとめたい場合は複数シートのデータを1つにまとめるVBAマクロの作り方を参考にしてください。コードを自分で書くのが難しい場合は、ChatGPTにVBAコードを書かせて業務を自動化する方法も参考にしてください。プログラミングを使わずに生成AIを業務効率化に使いたい場合は生成AIを使った業務効率化ネタ5選もあわせてご覧ください。