MS Project VBAを極める:ResourceAssignmentの暗部と「Work」「ActualWork」同期の極意
開発プロジェクトのリーダー、あるいは大規模なリソース管理を任されたプロフェッショナルであれば、誰もが一度はMS Projectの工数管理の「歪み」に直面したことがあるはずだ。
「タスクは完了しているのに、ActualWork(実績工数)がWork(総工数)と一致しない」
「残余工数(RemainingWork)の自動再計算ロジックに邪魔されて、意図した実績値を書き込めない」
MS Projectのオブジェクトモデル、特に`ResourceAssignment`(リソースアサインメント)のライフサイクルと計算エンジンの挙動は、表向きのリファレンスだけを眺めていても絶対に理解できない。標準機能に任せきった進捗入力は、必ずデータ不整合という名の爆弾をプロジェクトに残す。
今回は、VBAを用いて`Work`と`ActualWork`を完全に掌握し、実務に耐えうる堅牢な工数同期・差異算出ロジックを構築する方法を伝授しよう。
—
1. なぜ「Work」と「ActualWork」の同期でつまずくのか?
初学者が陥る最大の罠は、`Assignment.ActualWork = x` と単純に代入すれば済む話だと思い込むことだ。
MS Projectの計算エンジンは、タスクの「固定単位(Fixed Units)」「固定期間(Fixed Duration)」「固定ワーク(Fixed Work)」のタスクタイプに応じて、実績値や残余値を勝手に再配分しようとする。さらに、タスクの進捗状況(`PercentComplete`)やステータス日付(`CurrentDate` / `StatusDate`)が絡み合うと、VBAから書き込んだ値がバックグラウンドで勝手に上書きされる現象が発生する。
堅牢な設計のための鉄則
1. タスクの計算モードを明示的に制御する:VBA実行中は、MS Projectの自動計算の挙動に依存せず、必要なプロパティをトップダウンで強制的に確定させる。
2. 「残余工数(Remaining Work)」との関係性を断ち切る:`Work`(総工数)は `ActualWork`(実績工数) + `RemainingWork`(残余工数) で構成される。実績を入れた瞬間に残余が自動削りされる挙動をロジック側でコントロールしなければならない。
—
2. 【プロダクションコード】堅牢な工数同期・差異算出エンジン
以下のコードは、アクティブなプロジェクト内の全アサインメントを走査し、「実績工数と総工数の乖離を自動調整しつつ、リアルタイムの差異(Variance)を算出・出力する」ためのプロシージャである。
エラーハンドリング、オブジェクトの解放、パフォーマンスを意識した画面描画の抑止(`ScreenUpdating`相当の制御)を網羅した、そのまま現場で使える実用コードだ。
Option Explicit
”’
”’
Public Sub SyncAndCalculateWorkVariance()
‘ パフォーマンス向上と予期せぬUI干渉を防ぐための設定
Dim originalCalcMode As Long
originalCalcMode = Application.Calculation
On Error GoTo ErrorHandler
‘ 自動計算をマニュアルモードに切り替え(バッチ処理の高速化と意図しない自動再計算の防止)
Application.Calculation = pjCalculationManual
Application.DisplayAlerts = False
Dim tsk As Task
Dim asn As ResourceAssignment
Dim processedCount As Long
processedCount = 0
‘ プロジェクト内の全タスクを走査
For Each tsk In ActiveProject.Tasks
‘ サマリータスク(要約タスク)や削除済みタスクはスキップ
If Not tsk Is Nothing Then
If Not tsk.Summary And tsk.Active Then
‘ タスクに紐付くリソースアサインメントを走査
For Each asn In tsk.Assignments
If Not asn.ResourceType = pjResourceTypeMaterial Then ‘ 資材リソースは除外(人間のみ対象)
‘ 【コアロジック】
‘ ここでは例として、「実績が入力されているが、WorkとActualWorkに乖離がある場合」
‘ または「特定のカスタムフィールドをトリガーに同期させる」ロジックを想定。
Dim currentWork As Double
Dim actualWork As Double
Dim remainingWork As Double
currentWork = asn.Work / 60 // 分単位から時間単位(Hours)へ換算のベース
actualWork = asn.ActualWork / 60
remainingWork = asn.RemainingWork / 60
‘ 1. 整合性チェックと自動調整ロジック
‘ 例:もし進捗が100%なのにActualWorkがWorkに満たない場合、WorkをActualWorkに合わせる(あるいはその逆)
If tsk.PercentComplete = 100 And actualWork < currentWork Then
' 完了タスクの過剰な工数を実績に切り詰める
asn.Work = asn.ActualWork
End If
' 2. 計画値(BaselineWork)との差異(Variance)を算出・カスタムフィールドへ書き出し
' ※BaselineWorkが設定されている前提
Dim baselineWork As Double
baselineWork = asn.BaselineWork / 60
If baselineWork > 0 Then
‘ 差異 = 実際の総工数 – 計画工数
Dim workVariance As Double
workVariance = (asn.Work / 60) – baselineWork
‘ 例として、カスタムテキストフィールド(Text1)または数値フィールド(Number1)に差異を格納
‘ MS ProjectのNumber11〜20あたりはカスタム用に最適
asn.Number11 = workVariance
End If
processedCount = processedCount + 1
End If
Next asn
End If
End If
Next tsk
‘ 変更を反映するために再計算を実行
Application.Calculation = pjCalculationAutomatic
MsgBox “工数の同期と差異算出が完了しました。” & vbCrLf & _
“処理されたアサインメント数: ” & processedCount, vbInformation, “工数管理自動化”
CleanUp:
‘ 状態を確実に復元
Application.Calculation = originalCalcMode
Application.DisplayAlerts = True
Exit Sub
ErrorHandler:
MsgBox “予期せぬエラーが発生しました。” & vbCrLf & _
“エラー番号: ” & Err.Number & vbCrLf & _
“エラー内容: ” & Err.Description, vbCritical, “致命的なエラー”
Resume CleanUp
End Sub
—
3. コードのキモ:プロが解説するアーキテクチャの解説
① `Application.Calculation` の手動化
MS Projectで数千行規模のタスクとアサインメントをループ処理する場合、1つのプロパティを書き換えるたびにプロジェクト全体のリスケジュール計算(Critical Pathの再計算など)が走り、爆発的な処理遅延を引き起こす。
処理の冒頭で `pjCalculationManual` に落とし、最後に自動計算に戻す手法は、大規模案件のVBAでは絶対的な必須要件である。
② 時間単位(Minutes vs Hours)の罠
MS Projectの内部データ構造において、`Work` や `ActualWork` などの工数系プロパティの生の値は「分(Minutes)」単位で保持されている。
画面上では「8H」と表示されていても、VBAで取得すると `480` という数値が返ってくる。これをそのまま計算式に組み込むと、単位のミスで大惨事になるため、コード内では必ず `/ 60` などの正規化を行っている点に注目してほしい。
③ サマリータスクとマテリアルリソースの排除
プロジェクトマネージャがよく犯すミスが、要約タスク(Summary Task)に対して直接リソースをアサインしようとしたり、VBAで要約タスクの工数を操作しようとすることだ。要約タスクの工数は子タスクの集計値(Read-only的性質を持つ)であるため、VBAから直接書き込もうとすると実行時エラーかデータ破損を引き起こす。
`If Not tsk.Summary And tsk.Active Then` というガード節は、こうしたトラブルを未然に防ぐための防壁である。
—
4. データベース連携・外部ファイル連携における注意点
このVBAツールを起点として、ExcelやSQL Serverなどの外部データベースへ工数実績を吐き出す、あるいはERPから実績を取り込むアーキテクチャを組む場合、以下の点に留意せよ。
1. GUIDによる一意制約の維持
タスク名やリソース名でデータを突き合わせるのはアマチュアのやり方だ。MS Projectが裏で持つ `Task.UniqueID` や `Resource.UniqueID`、そしてアサインメントの `Assignment.UniqueID` をキーとして外部DBとマッピングしなければ、同名タスクの混入によってデータが崩壊する。
2. 排他制御(ファイルロック)
複数人が同時にアクセスするネットワーク上の `.mpp` ファイルに対してVBAで一括書き込みを行うと、ファイルがロックされて同期に失敗する。外部連携を行う前段階として、必ずローカルへの一時コピー、あるいはProject Server / Project Online(PWA)環境であればPWAのAPI(またはCSOM)経由での設計を検討すべきだ。
—
総括
MS ProjectのVBAは、単なる「マクロによる省力化ツール」ではない。標準の計算エンジンがカバーしきれない企業独自のガバナンスや、厳密な工数管理ロジックを強制するための「強力なエンジニアリング・武器」である。
今回解説した `ResourceAssignment` のライフサイクルと工数同期のメカニズムを正確に理解し、実装に組み込むことで、あなたのプロジェクトの数字は常に「真実」を語るようになるはずだ。現場の信頼を勝ち取る堅牢なツールを、ぜひあなたの手で完成させてほしい。
