【VBA】見積書・納品書をテンプレートから自動作成する方法(コピペOK)

VBA
スポンサーリンク
スポンサーリンク

完成イメージ(Before / After)

Before(実行前)

「一覧表」シートにデータが入っている。「テンプレート」シートは空のひな形。


【一覧表シート】
| A(顧客名)  | B(品目)          | C(数量) | D(単価)  | E(備考)   |
|-------------|-------------------|----------|-----------|------------|
| 山田商事     | Webサイト制作       | 1        | 300000    | 初回案件    |
| 鈴木工業     | システム保守(月額)  | 12       | 50000     | 年間契約    |
| 田中建設     | サーバー構築         | 1        | 500000    |            |

After(実行後)

一覧表の各行から見積書シートが自動作成され、PDFとして保存される。


C:\見積書出力\
  ├── EST-001_山田商事_見積書.pdf  ← 自動生成
  ├── EST-002_鈴木工業_見積書.pdf  ← 自動生成
  └── EST-003_田中建設_見積書.pdf  ← 自動生成

実行前の準備

バックアップを取る

マクロを実行する前に、Excelファイルをコピーしてバックアップを取ること。テンプレートシートの複製やデータ転記を行うため、万が一に備える。

Excelをマクロ有効ブック(.xlsm)で保存する

  1. Excelブックを開く
  2. 「ファイル」→「名前を付けて保存」
  3. ファイルの種類を 「Excel マクロ有効ブック (*.xlsm)」 に変更して保存

.xlsx のままだとマクロが保存されない。必ず .xlsm にすること。

シートを2つ用意する

このマクロには以下の2つのシートが必要。

1. 「一覧表」シート — データを入力するシート

A B C D E
1 顧客名 品目 数量 単価 備考
2 山田商事 Webサイト制作 1 300000 初回案件
3 鈴木工業 システム保守(月額) 12 50000 年間契約
  • 1行目はヘッダー(データは2行目から)
  • A列: 顧客名、B列: 品目、C列: 数量、D列: 単価、E列: 備考

2. 「テンプレート」シート — 見積書のひな形

セル 内容 書式設定(推奨)
B2 (見積番号が入る) 文字列
B3 (見積日が入る) 日付(yyyy/mm/dd)
B4 (有効期限が入る) 日付(yyyy/mm/dd)
B6 (宛先が入る) 文字列
B10 (品目が入る) 文字列
C10 (数量が入る) 数値(#,##0)
D10 (単価が入る) 通貨(\\#,##0)
E10 (金額が入る) 通貨(\\#,##0)
E14 (小計が入る) 通貨(\\#,##0)
E15 (消費税が入る) 通貨(\\#,##0)
E16 (合計が入る) 通貨(\\#,##0)
B18 (備考が入る) 文字列

テンプレートシートには罫線やタイトル(「御見積書」など)を事前に設定しておく。セルの書式設定(通貨表示 \#,##0、日付表示 yyyy/mm/dd)もテンプレート側で済ませておくと仕上がりがきれい。

テンプレートの印刷設定(用紙サイズ・余白・印刷範囲)は事前に設定しておくこと。設定方法は 【VBA】印刷設定を自動化する方法 を参照。


手順(コピペ → 実行まで約5分)

VBE(コードを書く画面)を開く

Alt + F11 キーを押すとVBE(Visual Basic Editor)が開く。

一般的にはAlt + F11で開けるが、企業のセキュリティ設定でVBAが無効化されている場合は、IT部門に確認すること。

標準モジュールを挿入する

  1. VBEのメニュー「挿入」→「標準モジュール」をクリック
  2. 右側に白い画面(コードウィンドウ)が表示される

コードを貼り付けて実行する

  1. 下の「コード(最小版)」をコピーして、コードウィンドウに貼り付ける
  2. Alt + F8 を押す(または VBE上で F5
  3. 「CreateEstimate」を選択して「実行」
  4. 処理が完了すると、メッセージボックスが表示される

コード(最小版)– 1件の見積書をテンプレートから作成

まずはこれだけで動く。「一覧表」シートの2行目(1件目)のデータを「テンプレート」シートに転記して、見積書シートを1枚作成する。


Sub CreateEstimate()

    Dim wsList As Worksheet
    Dim wsTemplate As Worksheet
    Dim wsNew As Worksheet
    Dim customerName As String
    Dim itemName As String
    Dim quantity As Long
    Dim unitPrice As Long
    Dim amount As Long
    Dim subtotal As Long
    Dim tax As Long
    Dim total As Long
    Dim estNo As String

    ' --- シートを取得 ---
    Set wsList = ThisWorkbook.Worksheets("一覧表")
    Set wsTemplate = ThisWorkbook.Worksheets("テンプレート")

    ' --- 一覧表の2行目からデータを取得 ---
    customerName = wsList.Cells(2, 1).Value   ' A2: 顧客名
    itemName = wsList.Cells(2, 2).Value       ' B2: 品目
    quantity = CLng(wsList.Cells(2, 3).Value)  ' C2: 数量
    unitPrice = CLng(wsList.Cells(2, 4).Value) ' D2: 単価

    ' --- 金額を計算 ---
    amount = quantity * unitPrice
    subtotal = amount
    tax = Int(subtotal * 0.1)     ' 消費税(10%)小計に対して計算
    total = subtotal + tax

    ' --- 見積番号を生成 ---
    estNo = "EST-001"

    ' --- テンプレートシートをコピー(末尾に追加) ---
    wsTemplate.Copy After:=Sheets(Sheets.Count)
    Set wsNew = ActiveSheet
    wsNew.Name = estNo

    ' --- データを転記 ---
    wsNew.Range("B2").Value = estNo                        ' 見積番号
    wsNew.Range("B3").Value = Date                         ' 見積日(当日)
    wsNew.Range("B4").Value = DateAdd("m", 1, Date)        ' 有効期限(1ヶ月後)
    wsNew.Range("B6").Value = customerName & " 御中"       ' 宛先
    wsNew.Range("B10").Value = itemName                    ' 品目
    wsNew.Range("C10").Value = quantity                    ' 数量
    wsNew.Range("D10").Value = unitPrice                   ' 単価
    wsNew.Range("E10").Value = amount                      ' 金額
    wsNew.Range("E14").Value = subtotal                    ' 小計
    wsNew.Range("E15").Value = tax                         ' 消費税
    wsNew.Range("E16").Value = total                       ' 合計
    wsNew.Range("B18").Value = wsList.Cells(2, 5).Value    ' 備考

    MsgBox "見積書を作成しました。" & vbCrLf & _
           "見積番号:" & estNo & vbCrLf & _
           "顧客名:" & customerName, vbInformation

End Sub

コードの動作:

  1. 「一覧表」シートの2行目から顧客名・品目・数量・単価を取得
  2. 金額(数量×単価)、消費税(小計の10%)、合計を計算
  3. 「テンプレート」シートをコピーして新しいシートを作成
  4. 見積番号・日付・宛先・明細・金額を転記
  5. 完了メッセージを表示

ポイント: 消費税は Int(subtotal * 0.1) で小計に対して1回だけ計算する。明細ごとに計算すると端数のズレが起きることがある。

自分はこの最小版で「テンプレートにデータが自動で入る」のを見たとき、手入力に戻れないと思った。

テンプレートへのデータ転記の仕組みをもっと詳しく知りたい場合は 【VBA】一覧表からExcelテンプレートに差し込み印刷する方法 を参照。


コード(実務版)– 一覧表から複数の見積書を一括作成→PDF保存

業務で使うなら、一覧表の全行から見積書を一括で作成してPDF保存したい。連番の自動採番、PDF出力先フォルダの自動作成、エラースキップ付き。

PDF出力の詳しいパラメータについては 【VBA】ExcelファイルをPDFに一括変換する方法 で解説している。

出力フォルダ構成:


C:\見積書出力\
  ├── EST-001_山田商事_見積書.pdf
  ├── EST-002_鈴木工業_見積書.pdf
  └── EST-003_田中建設_見積書.pdf

Sub CreateEstimateBatch()

    Dim wsList As Worksheet
    Dim wsTemplate As Worksheet
    Dim wsNew As Worksheet
    Dim fso As Object
    Dim outputPath As String
    Dim customerName As String
    Dim itemName As String
    Dim quantity As Long
    Dim unitPrice As Long
    Dim amount As Long
    Dim subtotal As Long
    Dim tax As Long
    Dim total As Long
    Dim estNo As String
    Dim pdfName As String
    Dim lastRow As Long
    Dim row As Long
    Dim seqNo As Long
    Dim fileCount As Long
    Dim errCount As Long
    Dim taxRate As Double

    ' --- ★ ここを自分の出力先フォルダに書き換える ---
    outputPath = "C:\見積書出力\"

    ' --- 消費税率(変更時はここを修正) ---
    taxRate = 0.1

    ' フォルダパスの末尾に \ がなければ追加
    If Right(outputPath, 1) <> "\" Then outputPath = outputPath & "\"

    ' 出力先フォルダがなければ作成
    Set fso = CreateObject("Scripting.FileSystemObject")
    If Not fso.FolderExists(outputPath) Then
        fso.CreateFolder outputPath
    End If
    Set fso = Nothing

    ' --- シートを取得 ---
    Set wsList = ThisWorkbook.Worksheets("一覧表")
    Set wsTemplate = ThisWorkbook.Worksheets("テンプレート")

    ' --- データの最終行を取得 ---
    lastRow = wsList.Cells(wsList.Rows.Count, 1).End(xlUp).row
    If lastRow < 2 Then
        MsgBox "一覧表にデータがありません。", vbExclamation
        Exit Sub
    End If

    ' 画面更新を停止(高速化)
    Application.ScreenUpdating = False
    Application.DisplayAlerts = False

    fileCount = 0
    errCount = 0
    seqNo = 0

    ' --- 一覧表の各行を処理 ---
    For row = 2 To lastRow

        ' 顧客名が空なら行をスキップ
        If Trim(wsList.Cells(row, 1).Value) = "" Then GoTo NextRow

        On Error GoTo ErrHandler

        ' --- データを取得 ---
        customerName = wsList.Cells(row, 1).Value
        itemName = wsList.Cells(row, 2).Value
        quantity = CLng(wsList.Cells(row, 3).Value)
        unitPrice = CLng(wsList.Cells(row, 4).Value)

        ' --- 金額を計算 ---
        amount = quantity * unitPrice
        subtotal = amount
        tax = Int(subtotal * taxRate)
        total = subtotal + tax

        ' --- 連番を採番 ---
        seqNo = seqNo + 1
        estNo = "EST-" & Format(seqNo, "000")

        ' --- テンプレートシートをコピー ---
        wsTemplate.Copy After:=Sheets(Sheets.Count)
        Set wsNew = ActiveSheet
        wsNew.Name = estNo

        ' --- データを転記 ---
        wsNew.Range("B2").Value = estNo
        wsNew.Range("B3").Value = Date
        wsNew.Range("B4").Value = DateAdd("m", 1, Date)
        wsNew.Range("B6").Value = customerName & " 御中"
        wsNew.Range("B10").Value = itemName
        wsNew.Range("C10").Value = quantity
        wsNew.Range("D10").Value = unitPrice
        wsNew.Range("E10").Value = amount
        wsNew.Range("E14").Value = subtotal
        wsNew.Range("E15").Value = tax
        wsNew.Range("E16").Value = total
        wsNew.Range("B18").Value = wsList.Cells(row, 5).Value

        ' --- PDFとして保存 ---
        pdfName = estNo & "_" & customerName & "_見積書.pdf"
        wsNew.ExportAsFixedFormat _
            Type:=xlTypePDF, _
            Filename:=outputPath & pdfName, _
            Quality:=xlQualityStandard, _
            IncludeDocProperties:=False, _
            IgnorePrintAreas:=False, _
            OpenAfterPublish:=False

        fileCount = fileCount + 1
        On Error GoTo 0

NextRow:
    Next row

    ' 画面更新を再開
    Application.ScreenUpdating = True
    Application.DisplayAlerts = True

    MsgBox "完了" & vbCrLf & _
           "作成:" & fileCount & " 件" & vbCrLf & _
           "エラー:" & errCount & " 件" & vbCrLf & _
           "出力先:" & outputPath, vbInformation

    Exit Sub

ErrHandler:
    errCount = errCount + 1

    ' コピーしたシートがあれば削除
    If Not wsNew Is Nothing Then
        Application.DisplayAlerts = False
        wsNew.Delete
        Application.DisplayAlerts = True
        Set wsNew = Nothing
    End If

    ' Application 設定を確実に復帰
    Application.ScreenUpdating = True
    Application.DisplayAlerts = True

    On Error GoTo 0
    Resume NextRow

End Sub

追加ポイント:

  • 一覧表の全行を For ループで処理(空行はスキップ)
  • 連番を EST-001, EST-002, … で自動採番(Format(seqNo, "000") でゼロ埋め)
  • 有効期限を見積日の1ヶ月後に自動設定
  • 各見積書をPDFとして出力先フォルダに保存
  • 出力先フォルダがなければ FSO で自動作成
  • エラーが出た行はスキップして次へ進む
  • 消費税率を taxRate 変数で管理(税率変更時に1箇所だけ修正すればOK)
  • エラーハンドラで Application.ScreenUpdating / DisplayAlerts を確実に復帰

作成した見積書PDFをメールに添付して送信したい場合は 【VBA】Excelファイルをメールに自動添付して送信する方法 を参照。


よくある落とし穴5選

自分が初めてVBAで見積書を自動作成したとき、消費税の計算を明細ごとに Int(金額 * 0.1) で処理していた。ところが端数の丸めが合計行と明細行でズレていて、取引先から「合計金額が1円違うんですが」と指摘された。原因は明細ごとに消費税を切り捨てるか、小計に対して消費税を計算するかの違い。以来、消費税は小計に対して1回だけ計算するようにしている。

# 症状 原因 対策
1 消費税が合計と明細で1円ズレる 明細行ごとに Int(金額 * 税率) で切り捨てていて、小計に対する税額と不一致になっている 消費税は小計に対して1回だけ計算する。tax = Int(subtotal * taxRate)
2 PDFが白紙で出力される テンプレートシートに印刷範囲が設定されていない テンプレートの印刷範囲を事前に設定しておく。印刷設定の自動化 も参考に
3 見積番号が過去の案件と重複する コードが毎回 EST-001 から採番するため、既存の番号と被る 実務では採番開始番号を管理用セルに記録しておくか、既存の最大番号を取得して +1 する
4 一覧表の空行でエラーが出る 空行のセルを CLng() で変換しようとして型変換エラー 顧客名が空の行はスキップする処理を入れる(実務版コードでは対応済み)
5 テンプレートのレイアウト変更後にコードが動かない セル位置(B2, B6, E14 など)がハードコードされているため、行や列を追加するとズレる テンプレートのレイアウトを変更したら、コード内のセル位置も修正する。またはセルに名前を付けて Range("見積番号") で参照する方法もある

FAQ

Q1: 納品書にも使えますか?

テンプレートシートの見出しを「御納品書」に変えて、番号のプレフィックスを DLV- にするだけで納品書にも使える。コード内の estNo = "EST-"estNo = "DLV-" に変更し、テンプレートシート名を「テンプレート_納品書」などにすればよい。

Q2: 明細行が複数ある見積書を作りたい

この記事のコードは「1顧客=1品目」のシンプルな構成。明細行が複数ある場合は、一覧表を顧客ごとにグループ化して、明細行を For ループで B10〜B13 などに転記する。構成が複雑になるため、一覧表からテンプレートに差し込み印刷する方法 も参考にしてほしい。

Q3: 消費税率を変えたい

実務版コードの taxRate = 0.1 を変更するだけ。たとえば軽減税率8%なら taxRate = 0.08 に変える。品目によって税率が異なる場合は、一覧表に「税率」列を追加してそこから読み取る方式に変更する。

Q4: 作成した見積書をメールで送りたい

【VBA】Excelファイルをメールに自動添付して送信する方法 を参照。PDF保存後のファイルパスを .Attachments.Add に渡せば添付できる。一覧表にメールアドレス列を追加すれば、見積書作成→PDF保存→メール送信を一気に自動化できる。

Q5: 見積書をExcelファイル(.xlsx)として保存したい

PDF保存の代わりに、テンプレートをコピーして新しいブックとして保存する方法がある。wsTemplate.Copy(引数なし)で新しいブックが開くので、ActiveWorkbook.SaveAs Filename:=パス, FileFormat:=xlOpenXMLWorkbook で保存する。


まとめ

この記事で、一覧表のデータから見積書・納品書をテンプレートで自動作成できるようになった。

  • 最小版:一覧表の1件目 → テンプレートに転記して見積書シートを作成
  • 実務版:一覧表の全件 → 連番採番+消費税計算+PDF一括保存

重要なのは、消費税は小計に対して1回だけ計算すること。明細ごとに切り捨てると端数がズレる。まず1件だけテスト作成してから本番実行するのがおすすめ。

関連記事

次にやりたくなること


もっとカスタマイズしたい場合

「複数明細行の見積書を作りたい」「見積書→納品書→請求書を連動させたい」「見積書作成と同時にメール送信もしたい」など、業務に合わせたカスタマイズが必要な場合は、ココナラで相談できます。

相談時に以下の情報があるとスムーズです:

  • Excel のバージョン / OS
  • テンプレートのレイアウト(スクリーンショット)
  • 一覧表の列構成
  • 見積番号の採番ルール(既存の番号体系がある場合)

コメント

タイトルとURLをコピーしました