【入門編】Project VBAで「進捗率」を自動計算する:実績工数と残工数から算出する独自ロジックの実装 – Project VBA解析バイブル

スポンサーリンク

こんにちは、プロジェクト管理の深淵なる世界へようこそ!伝説的なチーフアーキテクトとして、皆さんの業務自動化の旅をナビゲートできることを光栄に思います。

「Projectの標準機能だけでは、どうも進捗率がしっくりこないんだよな…」

そう感じているあなた、きっとこの記事がその疑問を解消し、Project VBAの新たな扉を開く鍵となるでしょう。今日は、Project VBAの基礎の基礎から、オブジェクトの魂に触れるような実践的なコードまで、一歩ずつ、しかし確実に皆さんのスキルアップをサポートしていきます。

Project VBAで「進捗率」を自動計算する:実績工数と残工数から算出する独自ロジックの実装

プロジェクトの進捗管理は、その成否を分ける非常に重要な要素です。Microsoft Projectには「進捗率 (%)」や「物理的達成率 (%)」といった標準フィールドがありますが、これらが常に皆さんの現場感覚と一致するとは限りません。

例えば、「実績工数と残工数の合計に対する実績工数の割合」という、より直感的で実態に即した進捗率を求めたい場面はありませんか?

この記事では、まさにそのニーズに応えるべく、Project VBAを使って独自の進捗率を計算し、カスタムフィールドにその結果を書き戻す方法を、優しく、そして本質的に解説していきます。ここをクリアすれば、Project VBAの基本はバッチリですよ!

なぜProjectの標準進捗率だけでは不十分な場合があるのか?

Projectの標準的な「進捗率 (%)」は、多くの場合、タスクの期間と実績期間に基づいて計算されます。しかし、これは「どれだけの期間が経過したか」を示すものであり、「どれだけの作業が完了したか」を正確に反映しないことがあります。

例えば、10日間のタスクで、5日経過した時点で進捗が50%と表示されても、実際にはまだ半分も作業が終わっていない、なんてことはよくありますよね。

そこで、今回私たちが目指すのは、「実績工数(Actual Work)」と「残存工数(Remaining Work)」をベースにした、よりリアルな進捗率です。

$$
\text{独自進捗率} = \frac{\text{実績工数}}{\text{実績工数} + \text{残存工数}} \times 100
$$

この計算式は、実際に投下された労力と、これから投下されるべき労力のバランスから、現在の作業完了度を数値化するもので、非常に強力な指標となり得ます。

Project VBAの全体像を掴む:オブジェクトモデルという「地図」

VBAでProjectを操作するということは、Projectが持つ「オブジェクト」という構成要素を理解し、それらを操作するということです。Projectの世界は、以下のような階層構造を持つオブジェクトの集合体として捉えることができます。

  • Application: Projectアプリケーション全体を司る最上位のオブジェクト。
  • Project: 開いている個々のプロジェクトファイル(.mpp)を表すオブジェクト。`Application` の中に複数存在し得ます。
  • Task: プロジェクト内の個々のタスクを表すオブジェクト。`Project` の中に複数存在し得ます。
  • Resource: プロジェクトに割り当てられたリソース(人、設備など)を表すオブジェクト。これも `Project` の中に複数存在し得ます。

まるで木構造を想像してください。`Application` という大きな幹があり、そこから `Project` という太い枝が伸び、さらに `Task` や `Resource` という葉っぱや実がぶら下がっている、といったイメージです。

今回の私たちの目標は、`Application` → `ActiveProject` → `Tasks` というルートを辿り、個々の `Task` オブジェクトにアクセスして情報を取得し、カスタムフィールドに書き込むことです。

![Project VBA オブジェクトモデルの概念図](https://i.imgur.com/example_project_vba_object_model.png){:width=”600px”}
概念図:Project VBAの主要オブジェクトの階層構造

準備作業:カスタムフィールドの追加

VBAで計算した結果を書き込むには、Projectにあらかじめ「受け皿」を用意しておく必要があります。それが「カスタムフィールド」です。

1. Projectを開く: 進捗率を計算したいプロジェクトファイルを開きます。
2. リボンの「プロジェクト」タブをクリックします。
3. 「カスタムフィールド」ボタンをクリックします。
4. 「タスク」を選択し、「種類」を「数値」に設定します。
5. リストの中から未使用のフィールド(例: 「数値1」)を選択し、「フィールド名の変更」をクリックします。
6. 「進捗率(独自)」など、分かりやすい名前を付けて「OK」をクリックします。
7. 再度「OK」をクリックしてカスタムフィールドの設定を閉じます。

これで、「進捗率(独自)」という数値型のカスタムフィールドが、各タスクに用意されました。このフィールドの内部的なIDは「数値1」なので、VBAからは `PjTaskNumber1` として参照することになります。

VBAコードの実装:魂を込めた自動化ロジック

それでは、いよいよVBAコードを書いていきましょう。開発タブの「Visual Basic」をクリックしてVBE(Visual Basic Editor)を開いてください。

コードの格納場所について

VBAコードは、`ThisProject` モジュールに書くこともできますが、汎用性や保守性を考えると「標準モジュール」に追加することをお勧めします。

1. VBEのプロジェクトエクスプローラーで、対象のプロジェクトを選択します。
2. 「挿入」メニューから「標準モジュール」を選択します。
3. 新しく追加されたモジュール(例: `Module1`)をダブルクリックし、以下のコードを貼り付けてください。

VBAコード例:実績工数と残工数から独自進捗率を計算する

‘====================================================================================================
‘ プロジェクトVBA:独自進捗率計算モジュール
‘ 作成者: 伝説のチーフアーキテクト
‘ 最終更新日: 2023-10-27
‘ 目的: 各タスクの実績工数と残存工数に基づき、独自の進捗率を計算しカスタムフィールドに設定する。
‘====================================================================================================

Option Explicit ‘ 変数の宣言を強制し、記述ミスによるエラーを早期発見するための鉄則

Sub CalculateCustomProgressPercentage()

‘ パフォーマンス向上のための設定
‘ 大規模なプロジェクトでは、これらの設定が劇的な速度向上をもたらします。
‘ オブジェクトのライフサイクルを意識した堅牢な自動化の第一歩です。
Application.ScreenUpdating = False ‘ 画面描画を停止し、処理速度を向上させます
Application.Calculation = pjManual ‘ 計算モードを手動に設定し、不要な再計算を抑制します

Dim objTask As MSProject.Task ‘ 現在処理中のタスクオブジェクトを格納する変数
Dim lngActualWorkMinutes As Long ‘ 実績工数(分単位)を格納する変数
Dim lngRemainingWorkMinutes As Long ‘ 残存工数(分単位)を格納する変数
Dim lngTotalWorkMinutes As Long ‘ 合計工数(分単位)を格納する変数
Dim dblCalculatedProgress As Double ‘ 計算された進捗率を格納する変数

On Error GoTo ErrorHandler ‘ エラー発生時に指定のErrorHandlerへジャンプ

‘ 現在開いているアクティブなプロジェクトを対象とします。
‘ ApplicationオブジェクトからActiveProjectへの参照は、VBAにおけるProject操作の基本です。
With Application.ActiveProject

‘ プロジェクト内の全てのタスクをループ処理します。
‘ .Tasksコレクションは、Projectオブジェクトが持つ全タスクの集合体です。
For Each objTask In .Tasks

‘ サマリータスク(親タスク)は個別の進捗計算の対象外とするのが一般的です。
‘ サマリータスクの進捗は子タスクの進捗によって自動的に集計されるためです。
If Not objTask.Summary Then

‘ オブジェクトが正しく参照されているかを確認することは非常に重要です。
‘ オブジェクトのライフサイクルを意識し、Null参照エラーを避けます。
If Not objTask Is Nothing Then

‘ 実績工数と残存工数を分単位で取得します。
‘ MS Projectの工数データはDurationオブジェクトとして扱われ、
‘ .TotalMinutesプロパティで分単位の数値を取得できます。
‘ これは、Durationオブジェクトの内部表現を知り尽くした者のみが使えるテクニックです。
lngActualWorkMinutes = objTask.ActualWork.TotalMinutes
lngRemainingWorkMinutes = objTask.RemainingWork.TotalMinutes

‘ 合計工数を計算します。
lngTotalWorkMinutes = lngActualWorkMinutes + lngRemainingWorkMinutes

‘ ゼロ除算を避けるための堅牢なチェック。
‘ 合計工数が0の場合、進捗率は0とします。
If lngTotalWorkMinutes > 0 Then
dblCalculatedProgress = (CDbl(lngActualWorkMinutes) / CDbl(lngTotalWorkMinutes)) 100
Else
dblCalculatedProgress = 0 ‘ 工数がないタスクは進捗0%
End If

‘ 計算された進捗率を、事前に用意したカスタムフィールドに書き込みます。
‘ PjTaskNumber1 は「数値1」カスタムフィールドのIDです。
‘ あなたが用意したカスタムフィールドが「数値2」であれば PjTaskNumber2 を指定します。
‘ SetFieldメソッドは、フィールドIDを指定して値を設定する標準的な方法です。
objTask.SetField FieldID:=pjTaskNumber1, Value:=dblCalculatedProgress

End If ‘ If Not objTask Is Nothing
End If ‘ If Not objTask.Summary

Next objTask ‘ 次のタスクへ

End With ‘ With Application.ActiveProject

MsgBox “独自進捗率の計算と設定が完了しました!”, vbInformation ‘ 完了メッセージ

GoTo CleanExit ‘ 正常終了時はエラーハンドラをスキップ

ErrorHandler:
‘ エラー発生時の処理
‘ エラー番号と説明をメッセージボックスで表示し、問題の特定を助けます。
MsgBox “エラーが発生しました: ” & Err.Number & ” – ” & Err.Description, vbCritical
‘ エラーが発生しても、リソースは適切に解放されるべきです。

CleanExit:
‘ 処理の終了時に元の設定に戻します。
‘ Application.ScreenUpdatingをTrueに戻し、Application.CalculationをpjAutoに戻すことで、
‘ アプリケーションが正常な状態に復帰します。
Application.ScreenUpdating = True
Application.Calculation = pjAuto

‘ オブジェクトの解放(For Eachループでは通常不要ですが、意識は常に持つべきです)
‘ Set objTask = Nothing ‘ 必要であればここで明示的に解放

End Sub

コードの解説:一歩踏み込んだ理解のために

  • `Option Explicit`: これはVBAを書く上での鉄則です。変数の宣言を強制することで、タイプミスなどのバグを未然に防ぎます。
  • `Application.ScreenUpdating = False` / `Application.Calculation = pjManual`: 大規模なプロジェクトでマクロを実行する際、これらの設定は非常に重要です。画面の描画や自動計算を一時的に停止することで、処理速度が飛躍的に向上します。処理終了時には必ず `True` や `pjAuto` に戻すことを忘れないでください。オブジェクトの負荷を軽減する、チーフアーキテクトならではの配慮です。
  • `Dim objTask As MSProject.Task`: `MSProject.Task` 型で変数を宣言しています。特定の型を指定することで、VBAはより効率的にメモリを管理し、IntelliSense(入力補完)も効くようになります。
  • `On Error GoTo ErrorHandler`: 堅牢なプログラムにはエラーハンドリングが必須です。予期せぬエラーが発生してもプログラムが突然終了せず、適切なメッセージを表示してくれます。
  • `With Application.ActiveProject`: `Application.ActiveProject` オブジェクトに対する一連の操作を簡潔に記述するための構文です。コードの可読性が向上します。
  • `For Each objTask In .Tasks`: プロジェクト内の全てのタスクを一つずつ処理するためのループです。`Tasks` は `Project` オブジェクトが持つコレクション(タスクの集まり)です。
  • `If Not objTask.Summary Then`: サマリータスク(子タスクをまとめる親タスク)は、それ自体で実績工数や残存工数を持つわけではありません。子タスクの集計結果として表示されるため、ここでは個別の計算対象から除外しています。
  • `If Not objTask Is Nothing Then`: これはオブジェクト指向プログラミングにおける基本的な堅牢性チェックです。`objTask` が実際に有効なオブジェクトを参照しているかを確認します。もし参照が切れていたり、存在しないオブジェクトを参照しようとしたりすると、実行時エラーが発生します。オブジェクトのライフサイクルを意識したプログラミングの基本中の基本です。
  • `objTask.ActualWork.TotalMinutes` / `objTask.RemainingWork.TotalMinutes`: Projectの工数データは、ただの数値ではありません。`Duration`(期間)という特殊なオブジェクトとして扱われます。`TotalMinutes` プロパティを使うことで、その期間を分単位の数値として正確に取得できます。これにより、単位の不整合による計算ミスを防ぐことができます。
  • `CDbl()`: 計算の際に整数型ではなく浮動小数点数型 (`Double`) にキャストしています。これにより、小数点以下の精度を保った正確な割り算が可能になります。
  • `If lngTotalWorkMinutes > 0 Then`: ゼロ除算エラーを防ぐためのチェックです。合計工数がゼロのタスク(作業が全くないタスク)に対して割り算を試みると、エラーでプログラムが停止してしまいます。
  • `objTask.SetField FieldID:=pjTaskNumber1, Value:=dblCalculatedProgress`: これがカスタムフィールドへの書き込みの肝です。
  • `SetField` メソッドは、指定したフィールドに値を設定するためのものです。
  • `FieldID` には、どのカスタムフィールドに書き込むかを識別する定数を指定します。`pjTaskNumber1` は「タスクの数値1」フィールドを指します。あなたが用意したカスタムフィールドが「数値2」であれば `pjTaskNumber2` を指定してください。
  • `Value` には、計算した進捗率 `dblCalculatedProgress` を渡します。

コードの実行と注意点

1. Projectファイルの保存: マクロを実行する前に、必ずプロジェクトファイルを保存してください。VBAによる変更はUndo(元に戻す)できない場合があります。
2. マクロの実行:

  • Projectのリボンの「開発」タブをクリックします。
  • 「マクロ」ボタンをクリックします。
  • リストから「`CalculateCustomProgressPercentage`」を選択し、「実行」をクリックします。

3. 結果の確認:

  • 計算が完了すると、「独自進捗率の計算と設定が完了しました!」というメッセージボックスが表示されます。
  • Projectのガントチャートビューなどで、テーブルに「進捗率(独自)」フィールドを追加して、計算結果を確認してください。

Project VBAをさらに深掘りするために

今回の実装は、Project VBAの強力な機能のほんの一部です。ここからさらに知識を深め、より高度な自動化を実現するためのヒントをいくつかご紹介します。

  • オブジェクトブラウザの活用 (F2キー): VBEで `F2` キーを押すと、Projectが持つ全てのオブジェクト、プロパティ、メソッドを探索できる「オブジェクトブラウザ」が開きます。これは、Project VBAの「辞書」であり「地図」です。
  • エラーハンドリングの強化: `On Error GoTo ErrorHandler` は良い出発点ですが、特定のエラーコードに対して異なる処理を行うなど、より詳細なエラーハンドリングを学ぶことで、さらに堅牢なコードが書けるようになります。
  • ユーザーフォームの作成: 入力ダイアログや設定画面をVBAで作成し、マクロの使い勝手を向上させることができます。
  • 他のオブジェクトとの連携: `Resource` オブジェクトや `Assignment` オブジェクト(タスクとリソースの割り当て)を操作することで、リソース管理の自動化も可能です。

まとめ:この一歩がプロジェクト管理を変える

Project VBAを学ぶことは、単にコードを書くこと以上の価値があります。それは、Projectという強力なツールを、あなたの手で自由にカスタマイズし、「あなたのプロジェクト管理」に最適化することを意味します。

今回ご紹介した「独自の進捗率計算」は、その第一歩に過ぎません。しかし、この小さな一歩が、あなたのプロジェクト管理を劇的に変え、より正確で、より効率的な意思決定を可能にするでしょう。

恐れることなく、この自動化の道を歩み続けてください。私が提唱する「極限の知見」とは、表面的な機能だけでなく、その背後にあるオブジェクトの振る舞いや、パフォーマンスへの影響までをも理解し、最適な解を導き出すことです。今日の学びが、その深淵への入口となることを願っています。

さあ、次なる自動化のチャレンジで、あなたのプロジェクトをさらに進化させましょう!

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