【テクニカル・上級編】ResourceAssignmentオブジェクトの「Work」と「ActualWork」をVBAで同期させる工数管理術 – Project VBA解析バイブル

スポンサーリンク

Project VBAを掌握する極限の知見:ResourceAssignmentの工数同期とリアルタイム差異算出の極意

プロジェクトマネジメントの現場において、Microsoft ProjectのVBAを用いた工数管理の自動化は、単なる定型作業の効率化ではない。それは、プロジェクトの「生命線」である計画工数(Work)と実績工数(ActualWork)の乖離を、データ整合性を担保しながら極限までリアルタイムに制御する高度なエンジニアリング領域である。

本稿では、`ResourceAssignment`オブジェクトが内包するタスク・リソース間の多重構造、およびVBAランタイムにおけるメモリ管理の罠を完全に看破し、現場で即座に稼働する堅牢な工数同期・差異算出ロジックを解説する。

1. MS Projectオブジェクトモデルの深層:`ResourceAssignments`の罠

MS Projectのオブジェクト階層において、`ResourceAssignment`は「どのリソースが、どのタスクに、どれだけの時間割り当てられているか」を定義する中継点である。

多くの開発者が陥る最初の罠は、タスク側(`Task.Work`)とアサインメント側(`ResourceAssignment.Work`)の双方向更新における「カスケード更新の競合」だ。単一リソースのアサインであればタスク側の変更が自動伝播するが、複数リソース(マルチアサイン)環境下では、VBAから直接`ResourceAssignment`のプロパティを操作しなければ、正確な工数配分コントロールは不可能となる。

さらに、VBAのCOMオブジェクト参照は、適切に解放を行わないとMS Projectのプロセス内にメモリリーク(ゴーストプロセス)を残す。特に数千行規模の大規模工程表をループ処理する際、オブジェクト変数の解放漏れは致命的なパフォーマンス低下を引き起こす。

2. 実装アーキテクチャ:WorkとActualWorkの同期・差異算出ロジック

以下のコードは、アクティブなプロジェクト内の全アサインメントを走査し、実績工数(`ActualWork`)の入力状態に応じて計画工数(`Work`)を動的に同期させ、カスタムフィールドを用いてリアルタイムの差異(Variance)を算出・保持するプロダクション品質のマクロである。

このコードでは、予期せぬエラー発生時にもCOMオブジェクトのメモリリークを防ぐため、徹底した参照の破棄(`Set obj = Nothing`)を行っている。

Option Explicit

‘ 定数定義: カスタムフィールドのインデックス (例: Text11, Number11を使用)
Const FIELD_VARIANCE_NOTE As Integer = pjCustomTaskText1
Const FIELD_CALC_VARIANCE As Integer = pjCustomTaskCost1

Sub SyncResourceWorkAndCalculateVariance()
‘ —————————————————————–
‘ 処理名: SyncResourceWorkAndCalculateVariance
‘ 概要 : ResourceAssignmentのWorkとActualWorkを同期し、差異を算出する
‘ 備考 : 巨大プロジェクトでのパフォーマンス維持のため画面描画を抑制
‘ —————————————————————–

Dim tsk As Task
Dim assn As ResourceAssignment
Dim totalWork As Double
Dim totalActualWork As Double
Dim dblVariance As Double

‘ パフォーマンス最適化: 画面描画と自動計算を一時停止
Application.ScreenUpdating False
On Error GoTo ErrorHandler

‘ プロジェクト内の全タスクをイテレート
For Each tsk In ActiveProject.Tasks
If Not tsk Is Nothing Then
‘ サマリータスク(プロジェクトや親工程)を除外する場合のガード
If Not tsk.Summary Then
totalWork = 0
totalActualWork = 0

‘ タスクに紐付く全リソースアサインメントを走査
If tsk.ResourceAssignments.Count > 0 Then
For Each assn In tsk.ResourceAssignments

‘ 【コアロジック】
‘ 実績工数が入力されている場合、未消化分の工数を実績に寄せる、
‘ あるいはActualWorkをベースにWorkを強制同期させるビジネスロジック
If assn.ActualWork > 0 Then
‘ 例:実績が計画を超過している場合、計画工数を実績値まで引き上げる(必要に応じたポリシー調整)
If assn.ActualWork > assn.Work Then
assn.Work = assn.ActualWork
End If
End If

‘ 集計用変数への加算(単位は分で保持されるため必要に応じて変換)
totalWork = totalWork + assn.Work
totalActualWork = totalActualWork + assn.ActualWork

‘ ループ内での個別アサインメントオブジェクトの解放
Set assn = Nothing
Next assn
End If

‘ 差異算出 (計画工数 – 実績工数)
‘ 正の値: 順調(残工数あり), 負の値: 超過
dblVariance = totalWork – totalActualWork

‘ カスタムフィールドへの書き込み(プロジェクトレポートとの連携用)
‘ ※MinutesToText関数を用いて人間が読みやすい形式に変換して格納
tsk.SetField FIELD_VARIANCE_NOTE, MinutesToText(dblVariance)
tsk.SetField FIELD_CALC_VARIANCE, dblVariance

End If
End If
Set tsk = Nothing
Next tsk

CleanUp:
‘ 画面描画と自動計算を復元
Application.ScreenUpdating True
MsgBox “工数の同期および差異の算出が完了しました。”, vbInformation, “システム通知”
Exit Sub

ErrorHandler:
‘ 異常系ハンドリング
MsgBox “予期せぬエラーが発生しました。” & vbCrLf & _
“Error No: ” & Err.Number & vbCrLf & _
“Description: ” & Err.Description, vbCritical, “致命的エラー”

‘ エラー時も確実に環境を復元
Resume CleanUp
End Sub

3. シニアエンジニアのための極限最適化テクニック

上記のコードをさらにエンタープライズ環境(数万タスク規模のプラント管理や大規模ITインフラ構築案件)に適用する場合、以下のアーキテクチャ上の配慮が不可欠となる。

A. イベントドリブンとバッチ処理のハイブリッド化

`Project_Calculate` イベントや `Project_Change` イベントの内部で上記の全件走査を行うのは自殺行為である。イベントトリガーでは変更されたタスクID(`Target.ID`)のみをキャプチャし、影響範囲(Affected Sub-tree)だけに絞ったピンポイントなアサインメント同期を実行するべきだ。

B. カレンダー(Working Time)の考慮

`ResourceAssignment.Work` は「分(Minutes)」単位で格納される。単純な四則演算を行うと、MS Project内部のカレンダー設定(土日祝日、変則労働時間)との間で微小なズレ(端数)が生じる。
厳密な工数管理が求められる環境では、MS Project標準のパース関数を通すか、API経由でカレンダー例外日を考慮した工数換算ロジックを挟む必要がある。

総括

VBAは、その手軽さゆえに「とりあえず動くコード」が乱立しがちだが、Microsoft Projectという巨大なスケジューリング・エンジンの下では、オブジェクトのライフサイクルと内部計算メカニズムを理解した者だけが堅牢なシステムを構築できる。

ここに示した知見とコードは、単なる省力化ツールではない。プロジェクトの真の進捗を暴き出し、リスクを早期に検知するための「強力なインフラ」である。現場のコードベースに直ちに組み込み、その圧倒的な処理速度とデータ整合性を体感してほしい。

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