見出し画像

【過不足調整マクロ(2/2)】物品移動の調整を効率化してボタンひとつで!(その2:コードの作成手順)

当ブログで紹介するコードは筆者が独自に開発して自身の業務のみに使用しているものであり、会社の財産ではありません。また、コードや参考画像等の中で使用している単語や図表なども抽象化したものであり、業務で使用しているものではありません。
当記事ではNoteのシンタックスハイライトの仕様上、コメントアウトされたコードを「//」から始まる形で記述していますが、VBAでは本来「'」によって記述します。コードを準用する場合はご注意ください。
【参考】公式ドキュメント:コード内のコメント

 過不足調整マクロの紹介の第2回目です。前回の記事では解決したい課題とマクロの全体像について解説しました。まだご覧になっていない方は、下のリンクからどうぞ。

 当ブログは、ただ完成形のマクロを紹介するだけではなく、ご自身の目の前の業務を効率化するために読者様が自分でVBAを書いてマクロを作成できるようになって欲しいという目標をもって執筆しておりますので、第2回目の今回は、コードを完成させるまでの手順について解説します。

要件定義

 まずは、何をしたいかという目標を明確にします。
 あくまで自分が楽をするための小さな小さなプログラムを作るだけなので、あまり仰々しく考える必要はありませんが、必要な機能は何かを理解し、ゴールを明確にするようにしましょうということです。
 今回のゴールは「複数の部署の間での作業服の過不足調整案の作成を自動化したい」ですので、次はさらに小さな課題(タスク)に切り分けていきましょう。これを課題分解と呼びます。

課題分解

 効率化に適した Excelの様式を準備する

  1. データベースファーストの原則を遵守できている過不足のとりまとめシートを作成する

  2. 各課から回答をとりまとめる

プログラムの処理の手順を日本語で書いて整理する

  1. どのテーブルを扱うかを宣言する

  2. テーブルの上の行から調整の対象とするレコードを探していく

  3. 「不足」のレコードが見つかれば、さらにもう一度テーブルの上の行から対応相手の条件にマッチするレコードを探していく

  4. 対応相手が見つかればそれぞれに値を入れていく

  5. 最後の行まで処理が終わる

  6. またテーブルの上の行から順に見ていって、対応相手が見つかっていない行には「購入」や「保留」を入力する。


 これらのことを、VBEを開いてモジュール内にコメントとして書いていくと良いでしょう。こうすることで、どのような手順で処理を書いていくかを整理することができます。あとは、これらの処理をVBAに置き換えていく作業を繰り返すのです。

//どのテーブルを扱うかを宣言する

//テーブルの上の行から調整の対象とするレコードを探していく

//「不足」のレコードが見つかれば、さらにもう一度テーブルの上の行から対応相手の条件にマッチするレコードを探していく

//対応相手が見つかればそれぞれに値を入れていく

//最後の行まで処理が終わる

//またテーブルの上の行から順に見ていって、対応相手が見つかっていない行には「購入」や「保留」を入力する。

日本語で書いたプログラムの処理をVBAコードに置き換えていく

どのテーブルを扱うかを宣言する

 ここからVBAのコードを書いていきます。まずはおまじないの「Option Explicit」を記述し、Subプロシージャの宣言と、リストオブジェクトのインスタンス化によって、 Excelブック内のどのテーブルを扱うかの宣言をします。

Option Explicit

Sub Adjustmet()

    //どのテーブルを扱うかを宣言する
    Dim UniformList As ListObject: Set UniformList = Sheet1.ListObjects(1)

    //テーブルの上の行から調整の対象とするレコードを探していく

    //「不足」のレコードが見つかれば、さらにもう一度テーブルの上の行から対応相手の条件にマッチするレコードを探していく

    //対応相手が見つかればそれぞれに値を入れていく

    //最後の行まで処理が終わる

    //またテーブルの上の行から順に見ていって、対応相手が見つかっていない行には「購入」や「保留」を入力する。
    
End Sub

 Withステートメントを宣言し、このテーブルに対してオブジェクト名の修飾を繰り返すことなく一連のステートメントを実行できるようにし、コードの可読性を高めます。

【参考】公式ドキュメント:Withステートメント

Option Explicit

Sub Adjustmet()

    //どのテーブルを扱うかを宣言する
    Dim UniformList As ListObject: Set UniformList = Sheet1.ListObjects(1)

    With UniformList

        //テーブルの上の行から調整の対象とするレコードを探していく

        //「不足」のレコードが見つかれば、さらにもう一度テーブルの上の行から対応相手の条件にマッチするレコードを探していく

        //対応相手が見つかればそれぞれに値を入れていく

        //最後の行まで処理が終わる

        //またテーブルの上の行から順に見ていって、対応相手が見つかっていない行には「購入」や「保留」を入力する。

    End With

End Sub

テーブルの上の行から調整の対象とするレコードを探していく

 「a」という数値の変数の宣言と、For文によるループを作成します。

Dim a As Integer

For a = 2 To .ListRows.Count + 1

変数「a」はループの中で行番号として扱います。初期値を2とし、それが1ずつ増えていくので、シートの2行目から、2行目、3行目、4行目と上から順に処理していく流れになります。
 ループが終わるのは、「UniformList.ListRows.Count + 1」とし、テーブルの最終行まで処理が終わった時ということにします。

【参考】公式ドキュメント:ListRows.Countプロパティ

モジュールの中に書き込むと以下のようになります。

Option Explicit

Sub Adjustmet()

    //どのテーブルを扱うかを宣言する
    Dim UniformList As ListObject: Set UniformList = Sheet1.ListObjects(1)

    With UniformList
        Dim a As Integer

        //テーブルの上の行から調整の対象とするレコードを探していく
        For a = 2 To .ListRows.Count + 1

            //「不足」のレコードが見つかれば、さらにもう一度テーブルの上の行から対応相手の条件にマッチするレコードを探していく

            //対応相手が見つかればそれぞれに値を入れていく

            //最後の行まで処理が終わる

        End For

        //またテーブルの上の行から順に見ていって、対応相手が見つかっていない行には「購入」や「保留」を入力する。

    End With
    
End Sub

「不足」のレコードが見つかれば、さらにもう一度テーブルの上の行から対応相手の条件にマッチするレコードを探していく

a行目5列目の値が「不足」であり、その対応相手がまだ決定していない場合、a行目のレコードの対応相手を見つけるという処理を行うため、「.Range(a, 5).value = "不足"」と、「.Range(a, 7) = Empty」という2つの命題が共に真であるときの処理を記す IF文を作成します。

//不足のレコードが見つかったとき
If .Range(a, 5).Value = "不足" And .Range(a, 7) = Empty Then

    //さらにもう一度テーブルの上の行から対応相手の条件にマッチするレコードを探す

End If

 もう一度テーブルの1番上の行から順にループを回すため、「b」という数値の変数宣言を行い、IF文の中に変数「b」が2からテーブルの行数と同じ数値までの間循環するループを作成します。
 For文の中において、変数「b」は「a」と同じく番号として扱います。

Dim b As Integer
//不足のレコードが見つかったとき
If .Range(a, 5).Value = "不足" And .Range(a, 7) = Empty Then

    //もう一度テーブルの上の行から対応相手の条件にマッチするレコードを探す
    For b = 2 To .ListRows.Count + 1
        //対応相手が見つかればそれぞれに値を入れていく
    End For

End If

 これらをモジュールに書き込むと、以下のようになります。

Option Explicit

Sub Adjustmet()

    //どのテーブルを扱うかを宣言する
    Dim UniformList As ListObject: Set UniformList = Sheet1.ListObjects(1)

    With UniformList
        Dim a As Integer
        Dim b As Integer

        //テーブルの上の行から調整の対象とするレコードを探していく
        For a = 2 To .ListRows.Count + 1
            //不足のレコードが見つかったとき
            If .Range(a, 5).Value = "不足" And .Range(a, 7) = Empty Then
                //もう一度テーブルの上の行から対応相手の条件にマッチするレコードを探す
                For b = 2 To .ListRows.Count + 1
                    //対応相手が見つかればそれぞれに値を入れていく
                End For
            End If
            //最後の行まで処理が終わる
        End For

        //またテーブルの上の行から順に見ていって、対応相手が見つかっていない行には「購入」や「保留」を入力する。

    End With

End Sub

対応相手が見つかればそれぞれに値を入れていく

変数「b」を用いたループ内では、b行目のレコードが次の5つの条件を満たす場合のIF文を作成します。

  1. 「対応相手」がまだ見つかっていない =空欄

  2. 「過不足」が「余剰」である

  3. 変数「a」によるループで見つけたレコードとは所属が異なっている

  4. 変数「a」によるループで見つけたレコードと作業服の種類が同じである

  5. 変数「a」によるループで見つけたレコードと作業服のサイズが同じである

IF文の中では、次のとおりそれぞれのレコードに値を入力する処理を記述します。

  • 「不足」のレコードの「対応相手」列には、「b」ループで発見したレコードの「所属名」の値 +「から」という文字列を入力する

  • 「余剰」のレコードの「対応相手」列には、「a」ループでで発見したレコードの「所属名」の値+「へ」という文字列を入力する

  • 両レコードの「パス」列に同じ数字を入力する

 さらに、a行目のレコード(「不足」のレコード)の対応相手としてb行目のレコード(「余剰」のレコード)が見つかったということは、変数「b」によるループは離脱し、変数「a」によるループに戻り、次の「不足」のレコードを探しにいきます。「Exit For」というステートメントによりループを離脱します。

//対応相手がみつかったとき
If _
.Range(b, 7).Value = Empty And _
.Range(b, 5).Value = "余剰" And _
.Range(b, 4).Value <> .Range(a, 4).Value And _
.Range(b, 2).Value = .Range(a, 2).Value And _
.Range(b, 3).Value = .Range(a, 3).Value _
Then
    //両レコードの「対応相手」列に値を入れる
    .Range(a, 7).Value = .Range(b, 4).Value + "から"
    .Range(b, 7).Value = .Range(a, 4).Value + "へ"
    //両レコードの「パス」列に同じ数値を入れる
    .Range(a, 8).Value = a
    .Range(b, 8).Value = a
    //変数「b」によるループを離脱する
    Exit For
End If

ここまでをモジュールに記述すると以下のとおりになります。

Option Explicit

Sub Adjustmet()

//どのテーブルを扱うかを宣言する
    Dim UniformList As ListObject: Set UniformList = Sheet1.ListObjects(1)

    With UniformList
        Dim a As Integer
        Dim b As Integer

        //テーブルの上の行から調整の対象とするレコードを探していく
        For a = 2 To .ListRows.Count + 1
            //不足のレコードが見つかったとき
            If .Range(a, 5).Value = "不足" And .Range(a, 7) = Empty Then
                //もう一度テーブルの上の行から対応相手の条件にマッチするレコードを探す
                For b = 2 To .ListRows.Count + 1
                //対応相手がみつかったとき
                    If _
                    .Range(b, 7).Value = Empty And _
                    .Range(b, 5).Value = "余剰" And _
                    .Range(b, 4).Value <> .Range(a, 4).Value And _
                    .Range(b, 2).Value = .Range(a, 2).Value And _
                    .Range(b, 3).Value = .Range(a, 3).Value _
                    Then
                        //両レコードの「対応相手」列に値を入れる
                        .Range(a, 7).Value = .Range(b, 4).Value + "から"
                        .Range(b, 7).Value = .Range(a, 4).Value + "へ"
                        //両レコードの「パス」列に同じ数値を入れる
                        .Range(a, 8).Value = a
                        .Range(b, 8).Value = a
                        //変数「b」によるループを離脱する
                        Exit For
                    End If
                End For
            End If
            //最後の行まで処理が終わる
        End For

        //またテーブルの上の行から順に見ていって、対応相手が見つかっていない行には「購入」や「保留」を入力する。

    End With
End Sub

 ここまででマクロを実行してみると、対応相手があるレコードについては、「対応相手」列及び「パス」列に何らかの値が入ります。

対応相手があるレコードについては、「対応相手」列及び「パス」列に何らかの値が入った状態

対応相手が見つかっていない行には「購入」や「保留」を入力する

最後に、対応相手がなかったレコードに値を入れる処理を記述します。また新しいループを回すので、「c」という数値の変数を宣言します。このときには変数「a」によるループは終了しているので、「a」を再利用してもいいのですが、頭の中で整理がつきやすいので、新たな変数を使うこととします。

  • 「不足」で対応相手なし → 「対応相手」列に「購入」

  • 「余剰」で対応相手なし → 「対応相手」列に「保留」

Dim c As Integer
//対応相手が見つかっていない行には「購入」や「保留」を入力する
For c = 2 To .ListRows.Count + 1
      If .Range(c, 5).Value = "不足" And .Range(c, 7) = Empty Then
        .Range(c, 7).Value = "購入"
    ElseIf .Range(c, 5).Value = "余剰" And .Range(c, 7) = Empty Then
        .Range(c, 7).Value = "保留"
    End If
Next c

完成したVBAコードと実行結果

これで全ての処理が記述できました。

Option Explicit

Sub Adjustmet()

//どのテーブルを扱うかを宣言する
    Dim UniformList As ListObject: Set UniformList = Sheet1.ListObjects(1)

    With UniformList
        Dim a As Integer
        Dim b As Integer

        //テーブルの上の行から調整の対象とするレコードを探していく
        For a = 2 To .ListRows.Count + 1
            //不足のレコードが見つかったとき
            If .Range(a, 5).Value = "不足" And .Range(a, 7) = Empty Then
                //もう一度テーブルの上の行から対応相手の条件にマッチするレコードを探す
                For b = 2 To .ListRows.Count + 1
                //対応相手がみつかったとき
                    If _
                    .Range(b, 7).Value = Empty And _
                    .Range(b, 5).Value = "余剰" And _
                    .Range(b, 4).Value <> .Range(a, 4).Value And _
                    .Range(b, 2).Value = .Range(a, 2).Value And _
                    .Range(b, 3).Value = .Range(a, 3).Value _
                    Then
                        //両レコードの「対応相手」列に値を入れる
                        .Range(a, 7).Value = .Range(b, 4).Value + "から"
                        .Range(b, 7).Value = .Range(a, 4).Value + "へ"
                        //両レコードの「パス」列に同じ数値を入れる
                        .Range(a, 8).Value = a
                        .Range(b, 8).Value = a
                        //変数「b」によるループを離脱する
                        Exit For
                    End If
                End For
            End If
            //最後の行まで処理が終わる
        End For
        //対応相手が見つかっていない行には「購入」や「保留」を入力する
        For c = 2 To .ListRows.Count + 1
            If .Range(c, 5).Value = "不足" And .Range(c, 7) = Empty Then
                .Range(c, 7).Value = "購入"
            ElseIf .Range(c, 5).Value = "余剰" And .Range(c, 7) = Empty Then
                .Range(c, 7).Value = "保留"
            End If
        Next c
    End With
End Sub

 完成したマクロの処理結果が以下のとおりです。

完成したマクロによる処理結果

 以上が、過不足調整マクロの課題分解からコードの完成までのプロセスです。解決したい課題とマクロの全体像について解説した前回との2回構成で解説させていただきましたが、いかがでしたでしょうか。
 ここで紹介したマクロを参考に、ご自身の業務用のマクロを作成して効率化を実現していただければ、筆者としては非常に嬉しい限りです。
 お読みいただきありがとうございました。


参考文献

公式ドキュメント

書籍


この記事が参加している募集

この記事が気に入ったらサポートをしてみませんか?