Project VBAを掌握する極限の知見:リソース単価テーブル(A〜E)の動的切り替え自動化アーキテクチャ
プロジェクトマネジメントにおいて、コスト管理の精度はプロジェクトの生死を分ける。
特に、フェーズや作業内容、あるいは外部要因(インフレ、契約変更、時間外労働など)に応じて、リソースの単価(コストレート)を動的に切り替える必要がある現場は多い。
標準機能のMicrosoft Project(以下、MSP)GUI上で、リソースシートを開き、個々のリソースの「コストレート」タブからレートA〜Eを手動で切り替える?
……そんな非効率な作業をまだエンジニアにやらせているのか。数十、数百の人員を抱えるエンタープライズ案件において、手動オペレーションは「ヒューマンエラーの温床」でしかない。
今回は、Project VBAを駆使して、プロジェクトのフェーズや条件に応じてリソースのコストレートテーブル(A〜E)を一括かつアトミックに切り替える堅牢な自動化アーキテクチャを伝授する。
—
なぜ「愚直なプロパティ操作」では破綻するのか?
VBA初心者が陥りがちな罠が、`Resource.CostRateTable` プロパティへの安易な代入だ。
「指定したリソースのテーブルを ‘B’ に変えればいいんだろう」と、以下のようなコードを書く。
‘ 【アンチパターン】これでは実務で使い物にならない
Dim r As Resource
For Each r In ActiveProject.Resources
If Not r Is Nothing Then
r.CostRateTable = “B” ‘ エラーまたは意図しない挙動の元凶
If
Next r
このアプローチが実務で通用しない理由は以下の通りだ。
1. グローバルとローカルのコンテキスト欠如:
コストレートテーブルの適用は、プロジェクト全体の一括変更なのか、特定の「タスクへの割り当て(Assignment)」単位の変更なのかを厳密に区別する必要がある。リソースマスター側のデフォルトテーブルを変えるのか、既存の割り当てコストに遡って適用するのかで挙動が全く異なる。
2. 存在しないテーブル指定によるランタイムエラー:
MSPではレートA〜Eまでが定義可能だが、未定義のテーブル(例: “F”)を指定したり、大文字小文字の揺れによって予期せぬエラー(実行時エラー)が発生する。
3. パフォーマンスの劣化:
`For Each` によるオブジェクトの直接走査は、大規模プロジェクト(数千行のリソース・タスク)において深刻なパフォーマンス低下を引き起こす。
プロのアーキテクトが書くコードは、「状態の検証(Validation)」「トランザクション的な一括処理」「エラーハンドリング」が完璧に担保されていなければならない。
—
堅牢なコストレートテーブル切替アーキテクチャの設計思想
今回構築するマクロの要件定義は以下の通りだ。
- 入力の抽象化:切り替えたいレートテーブル(”A”, “B”, “C”, “D”, “E”)を引数として受け取る。
- 安全性(Type Safety & Validation):不正な文字列が渡された場合、即座に処理を中断し、ユーザーに警告を発する。
- スコープの明確化:プロジェクト内の全アクティブリソースに対し、指定されたコストレートテーブルを強制適用する。
- ロギングとフィードバック:処理件数と結果をイミディエイトウインドウおよびメッセージボックスで明確に通知する。
—
プロダクションコード:リソースレート一括切替モジュール
以下のコードを、MSPのVBAエディタ(標準モジュール)にそのまま貼り付けて即座に使用できる。実務の厳格な現場に耐えうるよう、防御的コード(Defensive Code)で武装している。
Option Explicit
‘ ==============================================================================
‘ 模块名: ModCostRateManager
‘ 概要: プロジェクト内の全リソースのコストレートテーブルを動的に一括切替する
‘ 著作権: Project VBA Architecture Team
‘ ==============================================================================
Public Sub ApplyCostRateTableToAllResources()
Dim targetTable As String
‘ 1. ユーザーからの入力を取得(ダイアログによる対話型インターフェース)
targetTable = UCase(Trim(InputBox( _
“適用するコストレートテーブルを選択してください。” & vbCrLf & _
“有効な値: A, B, C, D, E”, _
“コストレートテーブル一括切替”, “A”)))
‘ キャンセル時の処理
If targetTable = “” Then Exit Sub
‘ 2. 入力値の厳格なバリデーション(境界値チェック)
If Not IsValidRateTable(targetTable) Then
MsgBox “エラー: 無効なコストレートテーブルが指定されました。” & vbCrLf & _
” ‘A’ から ‘E’ のアルファベットを入力してください。”, _
vbCritical, “入力エラー”
Exit Sub
End If
‘ 3. 実行確認
If MsgBox(“プロジェクト内の全リソースのコストレートテーブルを [” & targetTable & “] に変更します。” & vbCrLf & _
“よろしいですか?”, vbYesNo + vbQuestion, “確認”) = vbNo Then
Exit Sub
End If
‘ 4. メイン処理の実行(トランザクション風ラッパー)
On Error GoTo ErrorHandler
‘ 画面描画を停止してパフォーマンスを劇的に向上させる(極限の最適化)
Application.ScreenUpdating = False
Dim updatedCount As Long
updatedCount = ExecuteRateTableUpdate(targetTable)
‘ 画面描画の復元
Application.ScreenUpdating = True
‘ 5. 完了通知
MsgBox “処理が正常に完了しました。” & vbCrLf & _
“更新されたリソース数: ” & updatedCount & ” 件”, _
vbInformation, “完了”
Exit Sub
ErrorHandler:
Application.ScreenUpdating = True
MsgBox “予期せぬエラーが発生しました。” & vbCrLf & _
“Error No: ” & Err.Number & vbCrLf & _
“Description: ” & Err.Description, _
vbCritical, “致命的エラー”
End Sub
‘ ——————————————————————————
‘ Private関数: テーブル名の妥当性検証
‘ ——————————————————————————
Private Function IsValidRateTable(ByVal tblName As String) As Boolean
Select Case tblName
Case “A”, “B”, “C”, “D”, “E”
IsValidRateTable = True
Case Else
IsValidRateTable = False
End Select
End Function
‘ ——————————————————————————
‘ Private関数: 実際の更新処理を実行するエンジン
‘ ——————————————————————————
Private Function ExecuteRateTableUpdate(ByVal tblName As String) As Long
Dim r As Resource
Dim count As Long
count = 0
‘ アクティブプロジェクトの存在確認
If ActiveProject Is Nothing Then
Err.Raise 9999, , “アクティブなプロジェクトが開かれていません。”
End If
‘ リソースコレクションの走査
For Each r In ActiveProject.Resources
‘ リソースが有効(Nullではない、かつコストリソースや名前が空でない等)か判定
If Not r Is Nothing Then
‘ 取得したリソースが実在するものか(IDが0より大きい)
If r.ID > 0 Then
‘ コストレートテーブルを設定
‘ ※MSPのオブジェクトモデルでは CostRateTable プロパティに文字列を渡す
r.CostRateTable = tblName
count = count + 1
End If
End If
Next r
ExecuteRateTableUpdate = count
End Function
—
コードの解説と実務適用のポイント
1. `Application.ScreenUpdating = False` による圧倒的な高速化
Project VBAにおいて、GUIの再描画は極めて重い処理である。数千件のリソースを持つスケジュールでこれを有効にしたままループを回すと、数秒〜数分のフリーズを引き起こす。
処理の冒頭で描画を止め、最後に復元するイディオムは、大規模案件を扱うプロのエンジニアにとって常識である。
2. `r.ID > 0` によるダミー行の排除
MSPのリソースシートの末尾には、常に新規入力を待つ「空行(プレースホルダー)」が存在する。これをオブジェクトとして取得してしまうと、意図しないエラーや不要なプロパティ書き込みが発生する。
`r.ID > 0` というガード条件を入れることで、実在する有効なリソースのみを確実に処理の対象とする。
3. 外部データベース・Excel連携への拡張性
今回のコードは対話型(InputBox)だが、実務の現場では「進捗管理Excelファイル」や「ERPシステムからのCSVエクスポート」から現在のフェーズや適用すべきレートを動的に読み込ませるケースが多い。
`ExecuteRateTableUpdate` 関数を独立させておけば、以下のように外部データと容易に連携できる。
‘ 外部からフェーズ情報を受け取ってサイレント実行する例
Public Sub API_SetRateTable(ByVal phaseName As String)
Dim targetTable As String
‘ フェーズに応じたレートテーブルの決定ロジック
Select Case phaseName
Case “設計フェーズ”: targetTable = “A”
Case “開発フェーズ”: targetTable = “B”
Case “テストフェーズ”: targetTable = “C”
Case Else: targetTable = “A” ‘ デフォルト
End Select
Application.ScreenUpdating = False
ExecuteRateTableUpdate targetTable
Application.ScreenUpdating = True
End Sub
—
結び:ツールに振り回されるな、ツールを支配しろ
現場のプロジェクトマネージャーやPMOが求めるのは、「動くコード」ではなく「絶対にバグらない仕組み」だ。
今回紹介したコストレートテーブルの動的切り替えマクロは、単なる手抜きのための自動化ではない。人間の手作業という最大の脆弱性を排除し、プロジェクトの原価管理の信頼性を極限まで高めるための堅牢なガバナンスツールである。
君たちの手でこのアーキテクチャを現場に導入し、無駄な手作業を根絶やしにしてほしい。
