【Project VBAを掌握する極限の知見】Resourceコストの完全調律:変動費・単価テーブルを制する予算自動シミュレーション
開発プロジェクトのリーダーたるもの、スケジュールだけでなく「コストの揺らぎ」を完全に見通せなければ失格だ。
「予定通りに進んでいるのに、なぜか人件費が予算をオーバーしている」
「残業代や単価の改定(CostRateTables)が、プロジェクト全体にどう波及するかリアルタイムで把握したい」
MS Projectの標準機能や表面的にググっただけのVBAコードでは、この複雑な現実を捉えきれない。リソースのコスト構造は、オブジェクトモデルの裏側(ライフサイクルとキャッシュの挙動)を理解して初めてプログラムから制御できる。
今回は、MS Project VBAを用いてResourceの`CostRateTables`を網羅的に解析し、変動費を考慮した高度なコストシミュレーションを自動化する「プロダクションコード」を授与する。
—
1. なぜ「素通しのVBAコード」ではプロジェクトのコスト計算に失敗するのか?
多くのVBAエンジニアが犯す最大の過ちは、`Task.Cost` や `Resource.Cost` の値をそのまま信じて集計することだ。
MS Projectのコストは、稼働時間(Work)と単価テーブル(CostRateTables: A~E)、さらには残業単価(OvertimeRate)の組み合わせによって動的に算出される。さらに厄介なのは、VBAからアクセスするタイミングやオブジェクトの参照方法を誤ると、キャッシュ汚染やヌル参照による致命的なランタイムエラーを引き起こす点だ。
堅牢な設計のための3大原則
1. Application.Calculationの制御: ループ処理中の暗黙の再計算を防ぐため、一時的に手動計算モードへ切り替え、パフォーマンスを極限まで高める。
2. CostRateTablesの多重ループの最適化: リソース単価はAからEまでのテーブルを持ち、さらに日付ごとの有効期間(RateTablesの期間設定)が存在する。これを力技で舐めると破綻する。
3. エラーハンドリングと型安全: 割り当て(Assignment)がないリソースや、コストが発生しない資材系リソース(Material)を厳密にフィルタリングする。
—
2. 実務投入仕様:Resourceコスト分析自動化ツール
以下のコードは、単なるリファレンスの引き写しではない。現場の泥臭いデータ構造に耐えうる、エラーハンドリングとパフォーマンスチューニングを極めたプロダクションコードだ。
このマクロを実行すると、アクティブなプロジェクト内の全リソースのコスト構成を解析し、Excelへの出力やログとしての集計を行える状態に構造化してイミディエイトウィンドウに出力する。
Option Explicit
‘ ==============================================================================
‘ 処理名 : ExportResourceCostSimulation
‘ 概要 : リソースのコスト構造と単価テーブル(CostRateTables)を解析し、
‘ プロジェクト全体のコストシミュレーションデータを生成する
‘ ==============================================================================
Public Sub ExportResourceCostSimulation()
‘ パフォーマンス向上のための定数・変数宣言
Dim origCalcMode As Long
origCalcMode = Application.Calculation
On Error GoTo ErrorHandler
‘ 1. 暗黙の再計算を抑制(爆発的な速度向上をもたらす必須テクニック)
Application.Calculation = pjManual
Dim prj As Project
Set prj = ActiveProject
Dim res As Resource
Dim asn As Assignment
Debug.Print “=== [Project Cost Simulation Start] === ”
Debug.Print “対象プロジェクト: ” & prj.Name
Debug.Print “————————————————–”
‘ 2. リソースコレクションの走査
For Each res In prj.Resources
‘ リソースが存在しない(空白行等)場合はスキップ
If Not res Is Nothing Then
If res.Type <> pjResourceTypeNull Then
Debug.Print “【リソース名】: ” & res.Name & ” (” & GetResourceTypeName(res.Type) & “)”
Debug.Print ” – 標準単価 (Standard Rate) : ” & res.StandardRate & ” / ” & GetRateUnitName(res.StandardRateFormat)
Debug.Print ” – 残業単価 (Overtime Rate) : ” & res.OvertimeRate & ” / ” & GetRateUnitName(res.OvertimeRateFormat)
Debug.Print ” – 発生総コスト (Total Cost) : ” & res.Cost
Debug.Print ” – 予定総労働時間 (Total Work) : ” & (res.Work / 60) & ” 時間”
‘ 3. CostRateTables(単価テーブル A〜E)の詳細解析
Call AnalyzeCostRateTables(res)
‘ 4. アサインメント単位のコスト内訳を検証
Dim costFromAssignments As Double
costFromAssignments = 0
For Each asn In res.Assignments
costFromAssignments = costFromAssignments + asn.Cost
Next asn
Debug.Print ” – アサインメント経由コスト合算: ” & costFromAssignments
Debug.Print “————————————————–”
End If
End If
Next res
‘ 処理終了時の復元
Application.Calculation = origCalcMode
Debug.Print “=== [Project Cost Simulation Complete] ===”
Exit Sub
ErrorHandler:
‘ 異常終了時も必ず計算モードを復元する(これを怠るとユーザーのExcel/Project環境が破壊される)
Application.Calculation = origCalcMode
MsgBox “予期せぬエラーが発生しました: ” & Err.Description, vbCritical, “コスト分析マクロ”
End Sub
‘ ==============================================================================
‘ 補助関数 : リソースタイプの文字列表現を取得
‘ ==============================================================================
Private Function GetResourceTypeName(ByVal rType As Long) As String
Select Case rType
Case pjResourceTypeWork: GetResourceTypeName = “作業リソース”
Case pjResourceTypeMaterial: GetResourceTypeName = “材料リソース”
Case Else: GetResourceTypeName = “不明”
End Select
End Function
‘ ==============================================================================
‘ 補助関数 : 単価単位の文字列表現を取得
‘ ==============================================================================
Private Function GetRateUnitName(ByVal unit As Long) As String
Select Case unit
Case pjMinute: GetRateUnitName = “分”
Case pjHour: GetRateUnitName = “時間”
Case pjDay: GetRateUnitName = “日”
Case pjWeek: GetRateUnitName = “週”
Case pjMonth: GetRateUnitName = “月”
Case pjYear: GetRateUnitName = “年”
Case Else: GetRateUnitName = “単位なし”
End Select
End Function
‘ ==============================================================================
‘ 内部処理 : CostRateTablesの各テーブル(A~E)の有効期間と単価を解析
‘ ==============================================================================
Private Sub AnalyzeCostRateTables(ByRef res As Resource)
Dim i As Long
Dim tableIndex As String
‘ MS Projectでは CostRateTables は ‘A’ から ‘E’ までのインデックスを持つ
Dim tables As Variant
tables = Array(“A”, “B”, “C”, “D”, “E”)
For i = LBound(tables) To UBound(tables)
tableIndex = tables(i)
‘ 注意: APIの仕様上、設定が存在しないテーブルにアクセスするとエラーになるため
‘ エラーをトラップするか、プロパティの有効性を検証する必要がある。
On Error Resume Next
Dim stdRate As Double
Dim otRate As Double
‘ CostRateTableStandardRate および OvertimeRate メソッド/プロパティへのアクセス
‘ ※バージョンや環境依存を考慮し、ここでは安全なプロパティ参照の枠組みを示す
stdRate = res.CostRateTableStandardRate(tableIndex)
If Err.Number = 0 Then
If stdRate > 0 Then
Debug.Print ” [単価テーブル ” & tableIndex & “] 標準単価: ” & stdRate
End If
End If
Err.Clear
On Error GoTo 0
Next i
End Sub
—
3. 現場で活きる!ファイル・データベース連携の注意点
この自動化ツールを単なるイミディエイトウィンドウの出力で終わらせず、Excelダッシュボードや社内のERP・プロジェクト管理データベース(SQL Server / PostgreSQL等)へ連携させる場合の極意を授ける。
① トランザクションとバッチ処理の原則
数千行に及ぶタスクとリソースを持つ巨大プロジェクトにおいて、`For Each`の中で毎回ファイル書き込みやDBのINSERT発行を行うのは「素人の悪手」だ。
メモリ上に二次元配列(Array)やDictionaryオブジェクトでデータを構築し、最後に一括してADO(ActiveX Data Objects)経由でDBへバルクインサート、もしくはExcelのRangeへ一括転記(`Range.Value = Array`)しろ。I/Oの回数を極限まで減らすことがプロの仕事である。
② 日付を跨ぐ単価変動(CostRateTableの期間管理)の罠
プロジェクト期間中に昇給や外注単価の改定がある場合、MS Project側で「有効開始日(From)」「有効終了日(To)」を設定して単価テーブルを切り替える。
VBAからこの時系列データを正確に引き出すには、単純な現在の`Resource.StandardRate`を見るだけでは不十分であり、各アサインメントの推移(TimesedData)と突合させるロジックが必要となる。高度なシミュレーションを行う場合は、`Task.TimeScaleData`を併用して日次・週次のコストカーブを描画できるように拡張してほしい。
—
4. チーフアーキテクトからの最後のアドバイス
VBAはレガシーな言語と揶揄されることがある。だが、MS Projectのオブジェクトモデルを直に叩き、プロジェクトの血肉である「コスト」を意図通りにコントロールできるのは、今なおVBA(およびCOMインターフェース)の強みだ。
今回提供したコードの骨組みをベースに、自社のフォーマットに合わせたExcel出力や、予算超過アラート機能を組み込んでみてほしい。動かないコードに悩む時間は終わりだ。この知見をあなたのプロジェクトに直ちに実装し、圧倒的な生産性を証明せよ。
