【VBAリファレンス】Excel VBAで実現!「毎日棚卸し」による在庫削減の極意

スポンサーリンク

はじめに:なぜ「毎日棚卸し」が重要なのか?

多くの企業で、在庫管理は永遠の課題です。「売れるものだけ仕入れればいい」「適正在庫を保てばいい」といった理想論は、現実のビジネスシーンではなかなか実現できません。特に、小規模な店舗や部門、あるいは多品種少量生産のような状況では、日々の在庫変動が激しく、正確な把握が困難になりがちです。その結果、過剰在庫による保管コストの増大、陳腐化による廃棄ロスの発生、さらには品切れによる機会損失といった、様々な問題を引き起こします。

このような状況を打開する鍵となるのが、「毎日棚卸し」という考え方です。文字通り、毎日、全ての在庫品目を正確に数え、記録すること。一見、非効率で手間のかかる作業に思えるかもしれません。しかし、この地道な作業こそが、在庫の「見える化」を実現し、無駄を徹底的に削減するための最も強力な武器となります。

「毎日棚卸し」の真の目的は、単に在庫数を把握することだけではありません。日々の変動を追跡することで、売れ筋、死に筋、滞留在庫の兆候を早期に発見し、仕入れ計画の精度を劇的に向上させることができます。また、棚卸しの過程で、商品の配置や整理整頓も見直されるため、作業効率の向上にも繋がるのです。

しかし、手作業での棚卸しは、人的ミス、時間的制約、そして何よりも担当者のモチベーション維持という点で大きな壁にぶつかります。そこで、本記事では、Excel VBAを活用して「毎日棚卸し」を効率化し、在庫削減を強力に推進する方法について、具体的なアプローチとサンプルコードを交えて詳細に解説していきます。

Excel VBAで「毎日棚卸し」を自動化するメリット

Excel VBAを在庫管理、特に棚卸しプロセスに導入することで、以下のような多岐にわたるメリットを享受できます。

* **作業時間の劇的な短縮:** 手作業での集計や転記作業を自動化することで、棚卸しにかかる時間を大幅に削減できます。これにより、担当者は本来の業務により多くの時間を割くことが可能になります。
* **人的ミスの削減:** 数値の入力ミス、転記ミス、計算ミスといった、手作業にありがちなヒューマンエラーを排除できます。VBAコードが正確な処理を行うため、データの信頼性が向上します。
* **リアルタイムな在庫状況の把握:** 棚卸しデータを即座に集計・分析できるため、常に最新の在庫状況を把握できます。これにより、迅速な意思決定が可能になります。
* **分析機能の強化:** VBAを活用することで、単なる在庫数の集計に留まらず、売上データとの連携、回転率の計算、滞留在庫の抽出など、より高度な分析を容易に実現できます。
* **標準化されたプロセス:** VBAコードによって、棚卸しの手順が標準化されます。誰が担当しても一定の品質で棚卸しが実施できるようになり、属人化を防ぎます。
* **モチベーションの向上:** 面倒な作業が自動化されることで、担当者の負担が軽減され、モチベーションの維持に繋がります。また、データに基づいた客観的な改善が進むことで、達成感も得やすくなります。

「毎日棚卸し」を実現するためのExcel VBAサンプルコードと解説

ここでは、「毎日棚卸し」を実践するための基本的なExcel VBAコード例をいくつかご紹介します。これらのコードは、在庫管理シートへのデータ入力、集計、そして分析の各段階で活用できます。

1. 棚卸しデータの入力フォーム(UserForm)の作成

手作業での直接入力はミスを招きやすいため、UserFormを利用して入力インターフェースを設けるのが賢明です。

‘— UserFormのコード例 —
‘ UserFormの名前を「frmInventoryInput」とする

Private Sub cmdAdd_Click()
Dim wsData As Worksheet
Dim lastRow As Long

Set wsData = ThisWorkbook.Sheets(“在庫データ”) ‘ 在庫データシート名

‘ 空白行を探す
lastRow = wsData.Cells(Rows.Count, “A”).End(xlUp).Row + 1

‘ 各コントロールからデータを取得し、シートに書き込む
wsData.Cells(lastRow, “A”).Value = Me.txtProductID.Value ‘ 商品ID
wsData.Cells(lastRow, “B”).Value = Me.txtProductName.Value ‘ 商品名
wsData.Cells(lastRow, “C”).Value = Me.txtQuantity.Value ‘ 数量
wsData.Cells(lastRow, “D”).Value = Format(Now, “yyyy/mm/dd HH:mm:ss”) ‘ 入力日時

‘ 入力フィールドをクリア
Me.txtProductID.Value = “”
Me.txtProductName.Value = “”
Me.txtQuantity.Value = “”
Me.txtProductID.SetFocus ‘ 次の入力のためにフォーカスを戻す

MsgBox “データが追加されました。”, vbInformation
End Sub

Private Sub cmdClose_Click()
Unload Me ‘ フォームを閉じる
End Sub

‘— フォームの初期化 —
Private Sub UserForm_Initialize()
‘ 必要に応じて、商品マスタなどから商品IDや商品名のリストを読み込む処理を記述
End Sub

**解説:**
* `frmInventoryInput` という名前のUserFormを作成します。
* `txtProductID`, `txtProductName`, `txtQuantity` といったTextBoxコントロールを配置します。
* 「追加」ボタン (`cmdAdd`) をクリックすると、入力されたデータが「在庫データ」シートの最終行に追加されます。
* 入力日時も自動的に記録されるようにしています。
* 「閉じる」ボタン (`cmdClose`) でフォームを閉じます。
* `UserForm_Initialize` イベントでは、商品IDのドロップダウンリストなどを設定すると、さらに便利になります。

2. 在庫集計マクロ

棚卸しデータを集計し、現在の在庫数を把握するマクロです。

Sub AggregateInventory()
Dim wsData As Worksheet
Dim wsSummary As Worksheet
Dim dataRange As Range
Dim lastRowData As Long
Dim lastRowSummary As Long
Dim i As Long
Dim dict As Object ‘ Dictionaryオブジェクトを使用

Set wsData = ThisWorkbook.Sheets(“在庫データ”) ‘ 在庫データシート名
Set wsSummary = ThisWorkbook.Sheets(“在庫サマリー”) ‘ 在庫サマリーシート名

‘ Dictionaryオブジェクトの生成
Set dict = CreateObject(“Scripting.Dictionary”)

‘ 在庫データシートの最終行を取得
lastRowData = wsData.Cells(Rows.Count, “A”).End(xlUp).Row

‘ データシートから全データをループ処理
‘ ヘッダー行をスキップするため、2行目から開始
For i = 2 To lastRowData
Dim productID As String
Dim quantity As Long

productID = wsData.Cells(i, “A”).Value ‘ 商品ID
quantity = CLng(wsData.Cells(i, “C”).Value) ‘ 数量 (数値に変換)

‘ Dictionaryに商品IDが存在しない場合、新規登録
If Not dict.Exists(productID) Then
dict.Add productID, quantity
Else
‘ 既に存在する場合、数量を加算
dict(productID) = dict(productID) + quantity
End If
Next i

‘ 在庫サマリーシートの既存データをクリア (ヘッダー行は残す)
lastRowSummary = wsSummary.Cells(Rows.Count, “A”).End(xlUp).Row
If lastRowSummary >= 2 Then
wsSummary.Range(“A2:C” & lastRowSummary).ClearContents
End If

‘ Dictionaryの内容を在庫サマリーシートに書き出す
Dim key As Variant
Dim rowNum As Long
rowNum = 2 ‘ 書き込み開始行 (ヘッダーの次)

For Each key In dict.Keys
wsSummary.Cells(rowNum, “A”).Value = key ‘ 商品ID
‘ 商品名を取得 (ここでは単純に、最初の登録時のものを取得する例)
‘ より正確には、商品マスタシートから取得する処理を記述
wsSummary.Cells(rowNum, “B”).Value = wsData.Cells(Application.Match(key, wsData.Columns(“A”), 0), “B”).Value ‘ 商品名
wsSummary.Cells(rowNum, “C”).Value = dict(key) ‘ 合計数量
rowNum = rowNum + 1
Next key

‘ 必要に応じて、サマリーシートをソートする処理などを追加
MsgBox “在庫集計が完了しました。”, vbInformation

End Sub

**解説:**
* `AggregateInventory` サブプロシージャです。
* `Scripting.Dictionary` オブジェクトを使用し、商品IDをキーとして在庫数量を効率的に集計します。これにより、同じ商品IDのデータが複数あっても、正確に合計されます。
* 「在庫データ」シートからデータを読み込み、「在庫サマリー」シートに集計結果を出力します。
* 集計前に「在庫サマリー」シートの既存データをクリアすることで、常に最新の集計結果を表示できるようにしています。
* 商品名の取得は、ここでは「在庫データ」シートから最初に見つかったものを取得していますが、実際には商品マスタシートを参照する方がより堅牢です。

3. 滞留在庫の抽出マクロ

一定期間以上動きのない在庫(滞留在庫)を抽出するマクロの例です。

Sub ExtractStagnantInventory()
Dim wsSummary As Worksheet
Dim wsStagnant As Worksheet
Dim lastRowSummary As Long
Dim lastRowStagnant As Long
Dim i As Long
Dim lastSaleDate As Date
Dim daysThreshold As Long

Set wsSummary = ThisWorkbook.Sheets(“在庫サマリー”) ‘ 在庫サマリーシート名
Set wsStagnant = ThisWorkbook.Sheets(“滞留在庫リスト”) ‘ 滞留在庫リストシート名

daysThreshold = 90 ‘ 滞留とみなす日数 (例: 90日)

‘ 滞留在庫リストシートの既存データをクリア (ヘッダー行は残す)
lastRowStagnant = wsStagnant.Cells(Rows.Count, “A”).End(xlUp).Row
If lastRowStagnant >= 2 Then
wsStagnant.Range(“A2:D” & lastRowStagnant).ClearContents
End If

‘ 在庫サマリーシートの最終行を取得
lastRowSummary = wsSummary.Cells(Rows.Count, “A”).End(xlUp).Row

‘ 在庫サマリーシートをループ処理
‘ ヘッダー行をスキップするため、2行目から開始
For i = 2 To lastRowSummary
Dim productID As String
Dim productName As String
Dim currentStock As Long
Dim daysSinceLastSale As Long

productID = wsSummary.Cells(i, “A”).Value
productName = wsSummary.Cells(i, “B”).Value
currentStock = CLng(wsSummary.Cells(i, “C”).Value)

‘ 最終販売日の取得 (ここでは、在庫データシートから最新の販売日を取得する想定)
‘ 実際には、販売履歴シートなど、より詳細なデータから取得する必要があります。
‘ ここでは仮に、在庫データシートの最終更新日を「最終棚卸日」として利用する例を示します。
‘ より正確な「最終販売日」を取得するには、別途集計が必要です。
‘ 例: 最終販売日 = Application.WorksheetFunction.Max(wsData.Columns(“D”).SpecialCells(xlCellTypeConstants).Cells.Offset(0, -3).Value) ‘ 商品IDがA列、日付がD列の場合
‘ この部分は、実際のデータ構造に合わせてカスタマイズが必要です。

‘ 仮の処理:最終棚卸日(在庫データシートの最終更新日時)から計算
‘ より正確には、販売履歴データと連携させる必要があります。
‘ ここでは、単純化のため、商品IDに対応する最終入力日を仮定します。
‘ 実際には、商品ごとに「最終販売日」を記録・管理することが望ましいです。
Dim lastEntryDate As Date
On Error Resume Next ‘ エラーが発生しても処理を続行
lastEntryDate = Application.WorksheetFunction.Max(wsData.Columns(“D”).SpecialCells(xlCellTypeConstants)) ‘ 全体の最終入力日時
On Error GoTo 0 ‘ エラーハンドリングを元に戻す

‘ 滞留日数の計算(最終販売日からの経過日数)
‘ ここで、lastEntryDateが「最終販売日」として機能すると仮定します。
‘ 実際には、販売履歴データから該当商品の最終販売日を検索する必要があります。
daysSinceLastSale = DateDiff(“d”, lastEntryDate, Now) ‘ 現在の日付との差

‘ 在庫があり、かつ一定期間以上動きがない場合
If currentStock > 0 And daysSinceLastSale > daysThreshold Then
lastRowStagnant = wsStagnant.Cells(Rows.Count, “A”).End(xlUp).Row + 1
wsStagnant.Cells(lastRowStagnant, “A”).Value = productID
wsStagnant.Cells(lastRowStagnant, “B”).Value = productName
wsStagnant.Cells(lastRowStagnant, “C”).Value = currentStock
wsStagnant.Cells(lastRowStagnant, “D”).Value = lastEntryDate ‘ 最終入力日(仮)
wsStagnant.Cells(lastRowStagnant, “E”).Value = daysSinceLastSale ‘ 滞留日数
End If
Next i

MsgBox “滞留在庫の抽出が完了しました。”, vbInformation
End Sub

**解説:**
* `ExtractStagnantInventory` サブプロシージャです。
* 「在庫サマリー」シートから現在の在庫状況を取得します。
* **重要:** このコード例では、「最終販売日」の取得方法を簡略化しています。実際には、販売履歴データと連携し、各商品が最後に販売された日付を正確に特定する必要があります。ここでは、「在庫データ」シートの最終入力日時を仮の「最終販売日」として使用していますが、これはあくまで例示であり、実運用ではより精緻なデータ管理が求められます。
* `daysThreshold` で指定した日数以上、在庫の動きがない商品を「滞留在庫」として「滞留在庫リスト」シートに抽出します。
* 抽出されたデータには、商品ID、商品名、現在の在庫数、最終入力日(仮)、滞留日数が記録されます。

実務アドバイス:Excel VBAで「毎日棚卸し」を成功させるためのポイント

Excel VBAを導入するだけでは、在庫削減は達成できません。以下に、成功のための実践的なアドバイスをまとめました。

* **目的の明確化と共有:** なぜ毎日棚卸しをするのか、その目的(在庫削減、コスト削減、機会損失防止など)を明確にし、関係者全員で共有することが重要です。
* **データ入力の徹底と正確性:** VBAで自動化しても、元となるデータが不正確では意味がありません。入力担当者への教育と、入力ルールの徹底が不可欠です。可能であれば、バーコードリーダーやハンディターミナルとの連携も検討しましょう。
* **システム連携の検討:** Excel VBAは強力ですが、限界もあります。もし、基幹システムや販売管理システムなどがあれば、それらとのデータ連携を検討することで、より高度で効率的な在庫管理が可能になります。例えば、販売データから自動的に在庫を引当てる、といった仕組みです。
* **分析結果の活用と改善サイクルの確立:** 棚卸し・集計・分析はあくまで手段です。抽出された滞留在庫や、売れ筋・死に筋のデータを基に、仕入れ計画の見直し、販売促進策の実施、不採算商品の見切りなど、具体的なアクションに繋げることが最も重要です。PDCAサイクルを回し続けましょう。
* **定期的なVBAコードの見直しと改善:** ビジネス環境は常に変化します。在庫管理のルールや扱う商品が変わることもあります。それに合わせて、VBAコードも定期的に見直し、改善していく必要があります。
* **担当者の負担軽減とモチベーション維持:** VBA導入の最大のメリットは、担当者の負担軽減です。このメリットを最大限に活かし、担当者が「毎日棚卸し」を継続できるような環境を整えましょう。成功事例の共有や、データに基づいた貢献を評価することも有効です。
* **バックアップ体制の構築:** 万が一のデータ消失に備え、定期的なバックアップは必須です。

まとめ:Excel VBAで在庫管理の質を劇的に向上させる

「エクセル雑感在庫を減らせ!毎日棚卸ししろ!」というスローガンは、単なる掛け声ではなく、在庫管理における本質を突いています。しかし、その実践は容易ではありません。

本記事で解説したように、Excel VBAを適切に活用することで、「毎日棚卸し」にかかる負担を大幅に軽減し、データの正確性とリアルタイム性を向上させることが可能です。UserFormによる入力補助、Dictionaryオブジェクトを用いた効率的な集計、そして滞留在庫の抽出といった具体的なVBAの応用例は、皆様の在庫管理業務改善の強力な一助となるはずです。

重要なのは、VBAを導入すること自体が目的ではなく、それによって得られる「正確な在庫状況の把握」を基盤として、具体的な在庫削減アクションに繋げていくことです。日々の地道な棚卸しと、それを支えるVBAによる効率化、そしてデータに基づいた継続的な改善活動こそが、過剰在庫という名の「雑感在庫」を徹底的に減らし、企業の利益を最大化する王道と言えるでしょう。

ぜひ、本記事の内容を参考に、Excel VBAを活用した「毎日棚卸し」による在庫削減に挑戦してみてください。

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