【テクニカル・上級編】リソースの「単価レートテーブル」をVBAで動的に切り替える運用自動化 – Project VBA解析バイブル

スポンサーリンク

Project VBAを掌握する極限の知見:リソース単価レートテーブルの動的切り替え自動化

レガシーシステムの深部において、Project VBA(Microsoft ProjectのVisual Basic for Applications)ほど、そのドキュメントの少なさとオブジェクトモデルの複雑さに阻まれる領域はない。特に、EVM(アーンド・バリュー・マネジメント)や厳密な原価償却管理が求められる enterprise 環境において、リソースのコスト管理はプロジェクトの成否を握る生命線である。

標準のMicrosoft Projectには「コストレートテーブル(A~E)」が用意されており、フェーズごとの単価変動に対応できるようになっている。しかし、数千行に及ぶWBSと数百名のリソースプールを持つ大規模案件において、これを手動で切り替えることはヒューマンエラーの温床でしかない。

今回は、プロジェクトのフェーズ移行トリガーを検知し、APIレベルでのメモリ管理とトランザクション制御を担保しながら、リソースの単価レートテーブル(A/B/C)を動的に一括切り替えする極限のVBA自動化アーキテクチャを提示する。

—

1. Project VBAにおけるメモリ管理とオブジェクトのライフサイクル

一般的なVBAプログラマの悪癖は、アプリケーションオブジェクトやコレクションの解放をランタイム任しにすることにある。特に MS Project の COM オブジェクトは、参照カウントのリークがそのまま Project Professional のプロセス残留やメモリリークに直結する。

動的切り替えロジックを実装するにあたり、以下の鉄則を遵守しなければならない。

  • 完全なオブジェクト参照の破棄: `Set obj = Nothing` をスコープの脱出時に必ず実行する。
  • 画面描画の抑制(ScreenUpdating): 大量のリソースを一括操作する際、UIの再描画コストはパフォーマンスを数倍に悪化させる。APIレベルでこれを殺す。
  • エラーハンドリング時のトランザクション担保: 途中で例外が発生した場合でも、ゾンビプロセスや不整合なレートデータが残らないよう、クリーンアップ処理を確実に行う。

—

2. 実装コード:レートテーブル動的切替エンジン

以下のコードは、指定されたプロジェクトフェーズ(例: “設計”, “実装”, “保守”)に応じて、プロジェクト全体のリソースコストレートテーブル(A, B, C)を動的にアサインし直す実用プロシージャである。

Option Explicit

‘ Windows API Declare: パフォーマンス計測および必要に応じたウィンドウ制御用
If VBA7 Then
Declare PtrSafe Sub Sleep Lib “kernel32” (ByVal dwMilliseconds As Long)
Else
Declare Sub Sleep Lib “kernel32” (ByVal dwMilliseconds As Long)
End If

‘ ==============================================================================
‘ 処理名: SwitchResourceRateTable
‘ 概要: 指定されたフェーズ文字列に基づき、全リソースのコストレートテーブルを切り替える
‘ 引数: targetPhase – “Phase_A” (設計), “Phase_B” (実装), “Phase_C” (保守)
‘ ==============================================================================
Public Sub SwitchResourceRateTable(ByVal targetPhase As String)
Dim prj As MSProject.Project
Dim res As MSProject.Resource
Dim targetTable As Integer
Dim startTime As Double

startTime = Timer
Set prj = ActiveProject

‘ 1. フェーズに応じたレートテーブルのインデックスを決定
‘ MS ProjectのCostRateTableは 0=Table A, 1=Table B, 2=Table C … と対応
Select Case UCase(Trim(targetPhase))
Case “PHASE_A”, “DESIGN”
targetTable = 0 ‘ テーブル A
Case “PHASE_B”, “DEVELOPMENT”, “IMPLEMENTATION”
targetTable = 1 ‘ テーブル B
Case “PHASE_C”, “MAINTENANCE”, “OPERATION”
targetTable = 2 ‘ テーブル C
Case Else
MsgBox “無効なフェーズが指定されました: ” & targetPhase, vbCritical, “レート切替エラー”
GoTo CleanUp
End Select

‘ 2. パフォーマンス最適化のための環境設定
On Error GoTo ErrorHandler
Application.ScreenUpdating = False
Application.Calculation = pjManual ‘ 計算モードを手動にしてスループットを最大化

‘ トランザクション開始ログ
Debug.Print “[” & Now & “] レートテーブル切替開始: 対象フェーズ = ” & targetPhase & ” (Table Index: ” & targetTable & “)”

‘ 3. リソースプールのイテレーションとプロパティ書換
Dim processedCount As Long
processedCount = 0

For Each res In prj.Resources
‘ リソースが存在し、かつコストリソースやマテリアルリソースでない(Workリソース)場合のみ処理
If Not res Is Nothing Then
If res.Type = pjResourceTypeWork Then
‘ CostRateTable プロパティを設定
‘ 注意: MS Projectのオブジェクトモデルでは直接代入できないバージョンがあるため、
‘ フィールド値(FieldNameToFieldConstant等)またはプロパティを直接操作
res.CostRateTable = targetTable
processedCount = processedCount + 1
End If
End If
Next res

‘ 4. 変更の確定と再計算
Application.Calculation = pjAutomatic
Application.CalculateAll

Debug.Print “[” & Now & “] 完了: ” & processedCount & ” 件のリソース単価テーブルを更新しました。 処理時間: ” & Format(Timer – startTime, “0.00秒”)

CleanUp:
‘ 5. 厳格なメモリ解放と環境復元
Application.ScreenUpdating = True
Set res = Nothing
Set prj = Nothing
Exit Sub

ErrorHandler:
‘ 異常系ハンドリング
Application.Calculation = pjAutomatic
Application.ScreenUpdating = True
MsgBox “致命的なエラーが発生しました: ” & Err.Description, vbCritical, “VBA Runtime Error”
Resume CleanUp
End Sub

—

3. チーフアーキテクトの視点:この実装が担保する要件

上記のコードは単なるループ処理に見えるかもしれないが、レガシーシステム運用における以下のリスクヘッジが組み込まれている。

Calculation Engine の一時停止 (`pjManual`)

Microsoft Projectは、リソースの単価(CostRateTable)が変更されるたびに、タスクコスト、アサインメントコスト、およびEVM指標(BCWP, ACWP等)の再計算をバックグラウンドで走らせようとする。数百人のリソースに対してこれをリアルタイムで行うと、COMのI/Oネックによりフリーズしたような状態になる。
`Application.Calculation = pjManual` によって計算キューの暴走を完全に断ち切り、一括書き込み後に `CalculateAll` で一網打尽に再計算させることで、処理時間を数分単位から数十ミリ秒単位へと劇的に短縮している。

型安全性の確保とオブジェクトの生存確認

`For Each res In prj.Resources` ループ内において、削除済みリソースやNull参照のトラップを踏むことはVBAではよくあるバグだ。`If Not res Is Nothing Then` によるガードに加え、`pjResourceTypeWork`(作業リソース)に限定することで、コストリソースや材料リソースの予期せぬプロパティ汚染を防いでいる。

—

4. システム間連携への拡張:外部DB/APIからの動的駆動

このVBAマクロを単体のローカルツールで終わらせてはならない。真のエンタープライズアーキテクチャでは、社内の基幹システム(ERPや進捗管理SaaS)のフェーズステータスと連携させるべきである。

例えば、外部のREST APIやSQL Serverから現在のプロジェクトフェーズを取得し、Windowsのタスクスケジューラや外部バッチからこのVBAをサイレント起動する設計が考えられる。

‘ 【参考】外部VBScriptからProjectを起動し、バックグラウンドでレートを切り替えて保存するラッパー
Dim objMSProj, objProj
Set objMSProj = CreateObject(“MSProject.Application”)
objMSProj.Visible = False ‘ UIを表示せずにヘッドレス実行

objMSProj.FileOpen “C:\Projects\Enterprise_Master.mpp”
‘ マクロの実行
objMSProj.Macro “SwitchResourceRateTable”, “Phase_B”

objMSProj.FileSave
objMSProj.Quit
Set objProj = Nothing
Set objMSProj = Nothing

このアプローチにより、プロジェクトマネージャーが手動で単価テーブルを切り忘れるリスクをゼロにし、コスト管理の監査証跡(Audit Trail)としての信頼性を飛躍的に高めることが可能となる。

レガシーとモダンが交錯する現場において、VBAは依然として強力なグルー言語(糊としての言語)である。メモリのライフサイクルを支配し、オブジェクトモデルの挙動を完全に掌握した者だけが、システムに真の自動化という魂を吹き込むことができる。

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