この記事でわかること
- 在庫マスタシートから発注点を下回った商品を自動で抽出できる
- 抽出した商品を発注書テンプレートに自動転記し、PDF出力できる
- 在庫マスタ+発注書テンプレートの2シート構成で、1つのブックで在庫管理〜発注書作成まで完結する
対象: Excel 2016以降 / Microsoft 365、Windows 10/11
—
どんな場面で使う?
- 月初・週次の定期発注 — 在庫を確認して発注書を作る作業が毎回発生している場合。ボタン1つで完了する
- 消耗品・備品の補充管理 — コピー用紙や文具など、在庫が減るたびに手作業で発注書を書いている場合
- 複数拠点の在庫を一括チェック — 拠点ごとの在庫マスタを用意すれば、それぞれの発注書を一気に作れる
- 発注履歴の自動記録 — PDFで出力するため、いつ・何を・何個発注したかがファイル名の日付で追えるようになる
- 担当者の引き継ぎ負荷を軽減 — 発注作業がマクロに集約されるので、新しい担当者にも「このボタンを押すだけ」と伝えれば済む
—
完成イメージ(Before / After)
Before(手作業):
- 在庫マスタを目視で確認し、発注点以下の商品をピックアップ
- 発注書テンプレートに商品コード・商品名・数量を手で転記
- 印刷 or PDF保存
After(VBAで自動化):
- ボタンを押す(またはAlt+F8で実行)
- 在庫マスタから発注点以下の商品を自動抽出
- 発注書テンプレートに自動転記+PDF出力
所要時間: コピペ → 実行まで約10分
—
自分も以前、在庫表を目視で確認して発注点を下回った商品を手でピックアップし、発注書に転記していた。20品目以上あると正直しんどくて、転記ミスや発注漏れが月に1〜2回は起きていた。VBAで自動化してからは、ボタン1つで発注書が完成するようになり、転記ミスがゼロになった。同じように在庫管理と発注作業を手作業で回している人が、この記事でサクッと自動化できればうれしい。
在庫マスタ+発注書テンプレートの2シート構成で、発注書の作成をボタン1つで自動化する。
帳票自動化の基本パターンは Excelで請求書を自動作成する方法 でも解説している。請求書を自動化したことがある人は、同じ考え方で発注書にも応用できる。
—
実行前の準備
バックアップを取る
実務版コードは発注書シートにデータを書き込む(前回データは上書き)。必ずファイルのコピーを別フォルダに保存してから実行する。
Excelをマクロ有効ブック(.xlsm)で保存する
拡張子が .xlsx のままだとマクロが保存できない。
- 「ファイル」→「名前を付けて保存」
- ファイルの種類を「Excelマクロ有効ブック (*.xlsm)」に変更
- 保存
—
シート構成
シート1: 在庫マスタ(シート名: “在庫マスタ”)
| A列(商品コード) | B列(商品名) | C列(現在庫) | D列(発注点) | E列(発注数量) |
|---|---|---|---|---|
| P-001 | コピー用紙 A4 | 15 | 20 | 50 |
| P-002 | ボールペン 黒 | 80 | 30 | 100 |
| P-003 | クリアファイル | 5 | 10 | 30 |
| P-004 | 付箋 75mm | 3 | 5 | 20 |
| P-005 | ホッチキス針 | 25 | 10 | 50 |
- 1行目はヘッダー
- C列「現在庫」が D列「発注点」以下の商品が発注対象
シート2: 発注書テンプレート(シート名: “発注書”)
| セル | 内容 | 備考 |
|---|---|---|
| B2 | 発注日 | マクロで自動入力(yyyy/mm/dd形式) |
| B3 | 発注番号 | マクロで自動採番(PO-2026-0001形式) |
| B4 | 発注先 | 手入力 or 固定値 |
| A6〜E6 | ヘッダー行 | No. / 商品コード / 商品名 / 数量 / 備考 |
| A7以降 | データ転記エリア | マクロが自動で書き込む |
在庫マスタをテーブル形式で管理するとデータの追加・削除が楽になる。詳しくは テーブル形式のデータをVBAで操作する方法 を参照。
—
手順(コピペ → 実行まで約10分)
VBE(コードを書く画面)を開く
- Excelで
Alt + F11を押す
標準モジュールを挿入する
- VBEのメニュー →「挿入」→「標準モジュール」
コードを貼り付けて実行する
- コードウィンドウに、下のコードをそのままコピペする
Alt + F8→ マクロ名を選んで「実行」
ボタンに割り当てれば毎回Alt+F8を押さなくて済む。方法は マクロをボタン1つで実行する方法 を参照。
—
コード(最小版)– 発注点以下の商品を抽出してメッセージ表示
まずは在庫マスタから発注点以下の商品を抽出し、MsgBoxで一覧表示するだけの最小版。シートへの書き込みはしないので安全に動作確認できる。
'============================================================
' ■ 発注点以下の商品を抽出(最小版)
' → 在庫マスタから現在庫 <= 発注点の商品をMsgBoxで表示
' ※ 数値以外のデータが入っているとエラーになる。
' 実務版ではIsNumericでガード済み。
'============================================================
Sub CheckReorderItems()
Dim wsMaster As Worksheet
Set wsMaster = Worksheets("在庫マスタ")
'--- 最終行を取得
Dim lastRow As Long
lastRow = wsMaster.Cells(wsMaster.Rows.Count, 1).End(xlUp).Row
If lastRow < 2 Then
MsgBox "在庫マスタにデータがありません。", vbExclamation
Exit Sub
End If
'--- 発注対象を抽出
Dim msg As String
Dim cnt As Long
Dim r As Long
For r = 2 To lastRow
Dim stockQty As Long '← 現在庫(C列)
Dim reorderPt As Long '← 発注点(D列)
stockQty = CLng(wsMaster.Cells(r, 3).Value)
reorderPt = CLng(wsMaster.Cells(r, 4).Value)
If stockQty <= reorderPt Then
cnt = cnt + 1
msg = msg & wsMaster.Cells(r, 1).Value & " " & _
wsMaster.Cells(r, 2).Value & _
"(在庫: " & stockQty & " / 発注点: " & reorderPt & ")" & vbCrLf
End If
Next r
'--- 結果表示
If cnt = 0 Then
MsgBox "発注が必要な商品はありません。", vbInformation
Else
MsgBox "発注が必要な商品: " & cnt & " 件" & vbCrLf & vbCrLf & msg, vbInformation
End If
End Sub
コードの処理フロー解説
このコードは3つのブロックで構成されている。
- 最終行の取得とデータ存在チェック —
Cells.End(xlUp).Rowで在庫マスタの最終行を取得し、データが1行もなければ早期終了する。この「データがなければ即終了」のパターンは、空データで後続処理がエラーになるのを防ぐための定番テクニック。最終行を取得する方法でも詳しく解説している。
- 発注対象の抽出ループ — 2行目からデータ行をループし、C列(現在庫)とD列(発注点)を比較する。
CLng()で数値変換しているのは、セルの値が文字列型になっている場合に正しく比較できるようにするため。ただし最小版ではIsNumericチェックを省略しているので、数値以外が入っているとエラーになる点に注意。
- 結果のメッセージ表示 — 発注対象が0件なら「必要なし」、1件以上なら商品一覧を
MsgBoxで表示する。vbCrLfで改行しながら文字列を結合することで、見やすい一覧表示を実現している。
書き換えポイント
| 変数 | 説明 | 初期値 |
|---|---|---|
Worksheets("在庫マスタ") |
在庫マスタのシート名 | "在庫マスタ" |
Cells(r, 3) |
現在庫の列番号 | 3(C列) |
Cells(r, 4) |
発注点の列番号 | 4(D列) |
—
コード(実務版)– 発注書シートに転記+PDF出力(エラー処理付き)
この仕組みを作ってからは、月初の在庫チェック→発注書作成が15分で終わるようになった。以前は半日かかっていたので、空いた時間で他の業務に集中できている。
※ 発注書シートの前回データは上書きされます。実行前にバックアップを取ってください。
'============================================================
' ■ 発注書自動作成(実務版)
' → 在庫マスタから発注点以下の商品を発注書シートに転記+PDF出力
' → 在庫マスタシート: 商品コード(A)/商品名(B)/現在庫(C)/発注点(D)/発注数量(E)
' → 発注書シート: B2=発注日, B3=発注番号, A7以降=データ転記エリア
'============================================================
Sub CreatePurchaseOrder()
On Error GoTo ErrHandler
Application.ScreenUpdating = False
'--- ★書き換えポイント(シート名・列番号・行番号) ---
Const MASTER_SHEET As String = "在庫マスタ"
Const ORDER_SHEET As String = "発注書"
'在庫マスタの列番号
Const COL_CODE As Long = 1 'A列: 商品コード
Const COL_NAME As Long = 2 'B列: 商品名
Const COL_STOCK As Long = 3 'C列: 現在庫
Const COL_REORDER_PT As Long = 4 'D列: 発注点
Const COL_ORDER_QTY As Long = 5 'E列: 発注数量
'発注書の転記開始行
Const ORDER_START_ROW As Long = 7
'--- ★ここまで ---
Dim wsMaster As Worksheet
Dim wsOrder As Worksheet
Set wsMaster = Worksheets(MASTER_SHEET)
Set wsOrder = Worksheets(ORDER_SHEET)
'--- 在庫マスタの最終行を取得
Dim lastRow As Long
lastRow = wsMaster.Cells(wsMaster.Rows.Count, COL_CODE).End(xlUp).Row
If lastRow < 2 Then
MsgBox "在庫マスタにデータがありません。", vbExclamation
GoTo CleanUp
End If
'--- 発注書の前回データをクリア(A7以降)
Dim clearLastRow As Long
clearLastRow = wsOrder.Cells(wsOrder.Rows.Count, 1).End(xlUp).Row
If clearLastRow >= ORDER_START_ROW Then
wsOrder.Range(wsOrder.Cells(ORDER_START_ROW, 1), _
wsOrder.Cells(clearLastRow, 5)).ClearContents
End If
'--- 発注日・発注番号を自動入力
wsOrder.Range("B2").Value = Format(Date, "yyyy/mm/dd")
'発注番号: "PO-年-連番" 形式(B3の既存値から連番を+1)
'初回実行時はB3が空 → PO-yyyy-0001から開始
Dim prevNo As String
Dim newSeq As Long
prevNo = CStr(wsOrder.Range("B3").Value)
If prevNo Like "PO-####-####" Then
newSeq = CLng(Right(prevNo, 4)) + 1
Else
newSeq = 1
End If
wsOrder.Range("B3").Value = "PO-" & Format(Date, "yyyy") & "-" & Format(newSeq, "0000")
'--- 在庫マスタをループし、発注点以下の商品を転記
Dim writeRow As Long
writeRow = ORDER_START_ROW
Dim cnt As Long
Dim r As Long
For r = 2 To lastRow
Dim stockQty As Long
Dim reorderPt As Long
'数値変換(文字列混入ガード)
If IsNumeric(wsMaster.Cells(r, COL_STOCK).Value) And _
IsNumeric(wsMaster.Cells(r, COL_REORDER_PT).Value) Then
stockQty = CLng(wsMaster.Cells(r, COL_STOCK).Value)
reorderPt = CLng(wsMaster.Cells(r, COL_REORDER_PT).Value)
Else
'数値でないセルはスキップ(備考にメモ)
wsOrder.Cells(writeRow, 5).Value = "※数値変換エラー: 行" & r
writeRow = writeRow + 1
GoTo NextRow
End If
'--- 発注点以下なら転記
If stockQty <= reorderPt Then
cnt = cnt + 1
wsOrder.Cells(writeRow, 1).Value = cnt 'No.
wsOrder.Cells(writeRow, 2).Value = wsMaster.Cells(r, COL_CODE).Value '商品コード
wsOrder.Cells(writeRow, 3).Value = wsMaster.Cells(r, COL_NAME).Value '商品名
wsOrder.Cells(writeRow, 4).Value = wsMaster.Cells(r, COL_ORDER_QTY).Value '発注数量
writeRow = writeRow + 1
End If
NextRow:
Next r
'--- 発注対象が0件の場合
If cnt = 0 Then
MsgBox "発注が必要な商品はありません。", vbInformation
GoTo CleanUp
End If
'--- PDF出力
Dim pdfPath As String
'未保存ブック(ThisWorkbook.Pathが空)の場合はデスクトップに出力
If ThisWorkbook.Path = "" Then
pdfPath = CreateObject("WScript.Shell").SpecialFolders("Desktop") & _
"\発注書_" & Format(Date, "yyyymmdd") & ".pdf"
Else
pdfPath = ThisWorkbook.Path & "\発注書_" & Format(Date, "yyyymmdd") & ".pdf"
End If
'出力先フォルダの存在確認
Dim pdfFolder As String
pdfFolder = Left(pdfPath, InStrRev(pdfPath, "\"))
If Dir(pdfFolder, vbDirectory) = "" Then
MsgBox "PDF出力先のフォルダが見つかりません。" & vbCrLf & _
pdfFolder, vbExclamation
GoTo CleanUp
End If
wsOrder.ExportAsFixedFormat _
Type:=xlTypePDF, _
Filename:=pdfPath, _
Quality:=xlQualityStandard, _
IncludeDocProperties:=False, _
OpenAfterPublish:=False
MsgBox cnt & " 件の商品を発注書に転記し、PDFを出力しました。" & vbCrLf & _
"保存先: " & pdfPath, vbInformation
CleanUp:
Application.ScreenUpdating = True
Exit Sub
ErrHandler:
Application.ScreenUpdating = True
MsgBox "エラーが発生しました。" & vbCrLf & _
"エラー番号: " & Err.Number & vbCrLf & _
"内容: " & Err.Description, vbCritical
End Sub
書き換えポイント
| 定数 | 説明 | 初期値 |
|---|---|---|
MASTER_SHEET |
在庫マスタのシート名 | "在庫マスタ" |
ORDER_SHEET |
発注書のシート名 | "発注書" |
COL_CODE 〜 COL_ORDER_QTY |
在庫マスタの各列番号 | 1〜5 |
ORDER_START_ROW |
発注書の転記開始行 | 7 |
pdfPath |
PDF出力先のパス | ブックと同じフォルダ(未保存時はデスクトップ) |
コードの処理フロー解説
実務版コードは大きく5つのブロックに分かれている。なぜこの順番で処理するのかを理解しておくと、自社のテンプレートに合わせたカスタマイズがしやすくなる。
- 定数定義と初期化 — 列番号やシート名を
Constでコード先頭にまとめている。なぜ定数化するかというと、後から列構成が変わったときに修正箇所を1か所に集約できるから。ハードコーディング(コード中に直接数値を埋め込むこと)はバグの温床になるので、実務コードでは必ず定数を使うクセをつけておくとよい。
- 前回データのクリア — 発注書シートの転記エリアを
ClearContentsで消す。これを忘れると前回の発注データと今回のデータが混在して、二重発注の原因になる。自分もこのクリア処理を入れ忘れて、同じ商品を2回発注してしまったことがある。
- 発注番号の自動採番 — B3セルの既存番号から連番を +1 する仕組み。
Like "PO-####-####"でフォーマットチェックしているので、手で番号を消してしまっても初回のPO-yyyy-0001からリスタートできる。
- 在庫マスタのループと転記 —
IsNumericで数値チェックを入れているのがポイント。CSVから取り込んだデータは文字列型になっていることがあり、そのままCLng()に渡すとエラーになる。数値でないセルは備考欄にエラーメッセージを残してスキップするので、処理が途中で止まらない。
- PDF出力 —
ExportAsFixedFormatでシートをPDFに変換する。未保存ブック(ThisWorkbook.Pathが空)のときはデスクトップに出力するフォールバック処理が入っている。出力先フォルダの存在確認もDir()で行っているので、フォルダが消えていても安全に処理が止まる。
エラー処理の詳細は エラー処理(On Error)で止まらないマクロを作る方法 を参照。PDF出力の技術要素は ExcelファイルをPDFに一括変換する方法 でも解説している。
—
よくある落とし穴5選
1. 発注点の列と発注数量の列を取り違えて、異常な発注書が出力される
自分もこれでやらかした。在庫マスタの「発注点」を発注数量として転記してしまい、発注数が異常に少ない発注書を上司に提出してしまった。「これ本当にこの数でいいの?」と聞かれて冷や汗をかいた。列番号をコードの先頭に定数で定義しておけば、こういうミスは防げる。
| # | 症状 | 原因 | 対策 |
|---|---|---|---|
| 1 | 発注数量が異常に多い/少ない | 列番号のハードコーディングで列を取り違えた | Constで列番号を定数化し、コード先頭にまとめる |
2. 在庫マスタの数値が文字列型で比較が正しく動かない
| # | 症状 | 原因 | 対策 |
|---|---|---|---|
| 2 | 発注点以下なのに抽出されない | CSV取り込みなどでセルの値が文字列型になっている | CLng() / CDbl()で数値変換。IsNumeric()で事前チェック |
3. 前回の発注書データが残ったまま新しいデータが追加される
| # | 症状 | 原因 | 対策 |
|---|---|---|---|
| 3 | 前回分と今回分のデータが混在する | 転記前に前回データをクリアしていない | 転記処理の前にClearContentsで前回データを消す |
4. PDF出力先のフォルダが存在せずエラーになる
| # | 症状 | 原因 | 対策 |
|---|---|---|---|
| 4 | 実行時エラー 1004 | 指定したフォルダパスが存在しない | Dir()でフォルダの存在を確認。なければMkDirで作成 |
5. 在庫マスタに空白行があると最終行の取得がずれる
| # | 症状 | 原因 | 対策 |
|---|---|---|---|
| 5 | 途中のデータが抽出されない | Cells.End(xlUp)が空白行の手前で止まる | データに空白行を入れない運用にするか、UsedRangeで全範囲を走査する |
VBAで在庫管理の数量がマイナスになるエラーの対処法
「在庫数を更新したら現在庫がマイナスになってしまう」という場合、原因は出庫数量が現在庫を上回っているのにチェックなしで減算していることだ。対策として、減算前に If stockQty - outQty < 0 Then の条件分岐を入れ、マイナスになる場合はMsgBoxで警告を出して処理を中断する。在庫マスタに「安全在庫」列を追加して、安全在庫を下回る操作を拒否する仕組みにしておくと、運用ミスをさらに減らせる。
VBAで発注書が自動作成できないときの対処法
「マクロを実行したのに発注書シートに何も転記されない」という場合、原因はシート名の不一致か、在庫マスタのC列・D列が文字列型になっていて数値比較が正しく動いていないことが多い。まずシート見出しをダブルクリックして名前が「在庫マスタ」「発注書」と完全一致しているか確認する。それでも動かないなら、C列・D列を選択して「データ」→「区切り位置」→「完了」で数値に変換してから再実行する。自分もCSVから貼り付けたデータでこの現象に遭遇して、30分ほど原因を探したことがある。
VBAで発注書のPDF出力が失敗するときの対処法
「ExportAsFixedFormatで実行時エラー1004が出る」という場合、原因はPDF出力先のフォルダパスが存在しないか、ファイル名に使えない文字(/や:など)が含まれていることだ。Dir()で出力先フォルダの存在を事前に確認し、なければMkDirで作成する処理を追加するのが安全。また、未保存ブック(ThisWorkbook.Pathが空文字列)の場合はデスクトップに出力するフォールバックを入れておくと、どの環境でもエラーなく動作する。
---
FAQ
Q1: 発注書のフォーマットを自社のテンプレートに合わせたい
発注書シートのレイアウトは自由に変更できる。コード内の ORDER_START_ROW(転記開始行)と、wsOrder.Cells(writeRow, 列番号) の列番号を自社のテンプレートに合わせて調整する。
見積書や納品書のテンプレートも同じ考え方で作れる。見積書・納品書をテンプレートから自動作成する方法 を参照。
Q2: 発注点を商品ごとに変えられる?
在庫マスタのD列に商品ごとの発注点を設定すれば、商品ごとに異なるしきい値で判定できる。コードの reorderPt = CLng(wsMaster.Cells(r, COL_REORDER_PT).Value) で行ごとに発注点を読み取っているので、そのまま対応可能。
Q3: PDF出力ではなく、印刷したい場合は?
wsOrder.ExportAsFixedFormat の部分を wsOrder.PrintOut に置き換える。印刷範囲やページ設定は wsOrder.PageSetup で事前に設定する。
Q4: 発注書に発注番号を自動採番する仕組みを変えたい
実務版コードでは「PO-年-連番」形式(例: PO-2026-0001)で採番している。フォーマットを変えたい場合は Format(newSeq, "0000") の部分を調整する。たとえば3桁なら "000"、接頭辞を変えるなら "PO-" の部分を書き換える。
Q5: 在庫マスタの行が1000行以上ある場合、動作が遅くなる?
1000行程度なら体感で数秒で完了する。それ以上の場合は、在庫マスタのデータを配列に一括読み込みしてから処理するとさらに速い。高速化の詳細は 画面更新・再計算を止めてマクロを高速化する方法 を参照。
---
まとめ
- 在庫マスタ+発注書テンプレートの2シート構成で、発注書の自動作成が1つのブックで完結する
- 最小版で動作確認 → 実務版でPDF出力まで自動化、の2段階で進められる
- 列番号はConstで定数化しておくと、列の取り違えミスを防げる
- エラー処理とClearContents(前回データクリア)を入れておけば安心して運用できる
この仕組みの本質は「マスタデータから条件に合う行を抽出して別シートに転記する」というパターン。このパターンさえ覚えれば、発注書に限らず請求書・見積書・納品書など、あらゆる帳票の自動化に応用できる。まずは最小版で在庫チェックの仕組みを作り、動作が確認できたら実務版に切り替えてPDF出力まで自動化するのがおすすめの進め方。
関連記事
- Excelで請求書を自動作成する方法 -- 帳票自動化の基本パターン。発注書と同じ考え方で請求書も作れる
- 見積書・納品書をテンプレートから自動作成する方法 -- テンプレート転記+連番採番の共通技術
- Excelで勤怠管理表を自動化する方法 -- Excel業務テンプレート自動化の別パターン
- テーブル形式のデータをVBAで操作する方法 -- 在庫マスタをテーブル化して管理しやすくする
- ExcelファイルをPDFに一括変換する方法 -- PDF出力の技術要素をさらに深く知りたい場合
---
次にやりたくなること
- Excelで請求書を自動作成する方法: 発注書の次は、受け取った商品の請求書処理も自動化したい場合に
- 見積書・納品書をテンプレートから自動作成する方法: 発注書と同じ仕組みで見積書・納品書も自動生成したい場合に


コメント