Project VBAを掌握する極限の知見:リソース割り当て優先度を自動制御し、平準化エンジンを掌中へ収める方法
開発現場のリーダー諸君。Microsoft Projectを用いたリソース管理において、こんな絶望を味わったことはないか?
「重要度の低い定常タスクに優秀なリソースが張り付いたせいで、キーストーンとなるクリティカルパスのタスクが遅延している」
「リソース平準化を実行したはいいが、システムが勝手に判断した優先順位でスケジュールがズタズタになり、結局手動で修正する羽目になった」
Project標準の「リソース平準化(Resource Leveling)」は強力だが、その挙動の裏側にある「優先順位(Priority)」の概念を理解し、手動でコントロールすることを怠れば、アルゴリズムは単なる「お騒がせな自動化ツール」に成り下がる。
今回は、タスクの重要度や属性に応じてリソース割り当ての優先順位(Assignment Priority / Task Priority)をVBAで動的に算出し、完璧な平準化ロジックの土台を築く極限のテクニックを授けよう。
—
1. なぜ標準機能や安易なマクロでは破綻するのか?
多くのエンジニアが犯す最大のミスは、単に `Task.Priority` を一律でいじったり、オブジェクトのライフサイクルを無視してガリガリとプロパティを書き換えたりすることだ。
Project VBAにおいて、タスク(Task)とリソース割り当て(Assignment)は、データ構造上「多対多(Many-to-Many)」の複雑な関係性を持っている。1つのタスクに複数のリソースがアサインされている場合、タスク自体の優先度を変更するだけでは、リソース競合が発生した際の「誰を優先し、誰を後回しにするか」というミクロな制御ができない。
さらに、Projectのオブジェクトモデルは非常に重い。画面描画(ScreenUpdating)や計算モード(Calculation)を制御せずにループを回せば、数千行規模のスケジュールにおいて実行完了をコーヒーブレイクどころかランチまで待たされることになる。
プロダクション環境に耐えうるコードとは、「トランザクション的な一括処理」「エラーハンドリングの網羅」「ビジネスロジックの数式化」の3つが完璧に調和しているものである。
—
2. 優先度自動設定のアーキテクチャ設計
今回構築するロジックの要件はこうだ:
1. 多次元評価によるスコアリング: タスクの「クリティカルパスフラグ」「残り日数」「カスタムフィールドによる重要度」を掛け合わせ、0〜1000の優先度を算出。
2. 高速トランザクション処理: 画面描画と自動計算を完全に停止し、メモリ上で一気に処理を流し込む。
3. 割り当てレベルの制御: タスク単位だけでなく、必要に応じて `Assignment` オブジェクト単位で優先度を調停する。
—
3. 【プロダクションコード】リソース優先度自動調停エンジン
以下のコードは、実務の現場でそのまま組み込める堅牢なVBAモジュールだ。コピペして、あなたのProjectファイルの標準モジュールに貼り付けてほしい。
Option Explicit
‘ ==============================================================================
‘ ódulo名: clsResourcePriorityController
‘ 概要: タスクの重要度とクリティカルパス状態に基づき、リソース割り当ての
‘ 優先順位を動的に最適化するプロダクションコード
‘ ==============================================================================
Public Sub AutoOptimizeResourcePriorities()
Dim startTime As Double
startTime = Timer
‘ 1. パフォーマンス最大化のための環境退避と設定
Dim originalCalcMode As Long
originalcalcMode = Application.Calculation
On Error GoTo ErrorHandler
‘ 自動計算と画面描画を停止(爆発的な速度向上をもたらす必須の儀式)
Application.Calculation = pjCalculationManual
Application.ScreenUpdating = False
Dim t As Task
Dim assn As Assignment
Dim calculatedPriority As Integer
Dim processedCount As Long
processedCount = 0
Debug.Print “[INFO] リソース優先度自動最適化プロセスを開始します…”
‘ 2. アクティブプロジェクトの全タスクを走査
For Each t In ActiveProject.Tasks
‘ 予約語やマイルストーン、完了済みタスクはスキップして無駄な演算を排除
If Not t Is Nothing Then
If t.Summary = False And t.Milestone = False And t.PercentComplete < 100 Then
' ==============================================================
' 優先度算定アルゴリズム(ビジネスロジック)
' 基底スコア(100) + クリティカル加算(500) + 早期期限加算(残日数に応じた重み付け)
' ==============================================================
calculatedPriority = CalculateDynamicPriority(t)
' タスク自体の優先度を更新
t.Priority = calculatedPriority
' タスクに紐付くすべてのリソース割り当て(Assignment)に対しても優先度を伝播
For Each assn In t.Assignments
If Not assn Is Nothing Then
' 割り当て側の優先度も同期させることで、平準化エンジンの精度を極限まで高める
assn.Priority = calculatedPriority
End If
Next assn
processedCount = processedCount + 1
End If
End If
Next t
' 3. 変更を確定させるための再計算
Application.Calculation = originalcalcMode
ActiveProject.Calculate
Application.ScreenUpdating = True
Debug.Print "[SUCCESS] 最適化完了。処理対象タスク数: " & processedCount & " / 処理時間: " & Format(Timer - startTime, "0.00秒")
MsgBox "リソース優先度の自動最適化が正常に完了しました。" & vbCrLf & "処理件数: " & processedCount & "件", vbInformation, "最適化完了"
Exit Sub
ErrorHandler:
' 異常系ハンドリング:確実に環境を復元する
Application.Calculation = originalcalcMode
Application.ScreenUpdating = True
MsgBox "予期せぬエラーが発生しました。" & vbCrLf & _
"エラー番号: " & Err.Number & vbCrLf & _
"エラー内容: " & Err.Description, vbCritical, "致命的エラー"
Debug.Print "[ERROR] 予期せぬエラーが発生しました: " & Err.Description
End Sub
' ==============================================================================
' 補助関数: タスクの属性から動的優先度を算出する
' ==============================================================================
Private Function CalculateDynamicPriority(ByVal t As Task) As Integer
Dim basePriority As Integer
basePriority = 500 ' デフォルトミドル
' 要件1: クリティカルパス上のタスクは最優先(+400)
If t.Critical Then
basePriority = basePriority + 400
End If
' 要件2: 残り日数(Remaining Duration)が短い逼迫したタスクにボーナス加算
' ※ProjectのDurationは分単位で格納されるため、日換算(480分 = 1日)で評価
Dim remainingDays As Double
remainingDays = t.RemainingDuration / 480
If remainingDays <= 3 And remainingDays > 0 Then
basePriority = basePriority + 100 ‘ 直近3日以内のタスクはブースト
ElseIf remainingDays <= 0 Then
basePriority = 0 ' 期限超過は最低優先度(または要対応として例外処理)
End If
' 境界値制御(ProjectのPriorityは 0 から 1000 の整数値を取る)
If basePriority > 1000 Then basePriority = 1000
If basePriority < 0 Then basePriority = 0
CalculateDynamicPriority = basePriority
End Function
---
4. チーフアーキテクトが教える実装上の急所(陷りやすい罠)
このコードを現場に導入する際、以下のポイントを必ずチームメンバーに周知してほしい。
① `Assignment.Priority` と `Task.Priority` の非対称性
ProjectのUI上ではタスクの優先度を変更すると自動的に割り当て側も追従するように見えるが、大規模なスケジュールや外部DB(Project Server / Project Online)と連携している環境では、明示的に `Assignment` オブジェクト側をループして叩く必要がある。この一手間を惜しむと、平準化を実行した際に「タスクの順序は正しいのに、リソースの奪い合いで変な割り当てになる」という怪奇現象に悩まされることになる。
② エラーハンドリングにおける状態の凍結解除
VBAで `ScreenUpdating = False` や `Calculation = xlCalculationManual`(Projectの場合は `pjCalculationManual`)を使う際、途中でエラー落ちするとユーザーのProjectアプリケーション全体が「自動計算しない・画面描画しない」というゾンビ状態に取り残される。今回のコードのように `On Error GoTo ErrorHandler` を必ず記述し、イミディエイトウィンドウやメッセージボックスの前に必ず環境を復元する防壁を張るのがプロの作法だ。
③ データベース連携時のトランザクション
もしこのマクロを基幹の進捗管理DBやERPからエクスポートされたCSV/SQL Serverのデータと組み合わせて動かす場合、「DBからの値インポート ➔ 優先度自動算出マクロの実行 ➔ リソース平準化のバッチ実行」というパイプラインを構築することになる。この際、タスクUID(`UniqueID`)をキーとして一意性を担保し、名前(Name)でマッチングするような愚行は絶対に避けるべきだ。
—
5. おわりに:ツールに振り回されるな、ツールを飼い慣らせ
リソース平準化は、人間の思考の代わりをしてくれる魔法の杖ではない。あくまで「設定されたルールと優先順位に従って数学的にリソースをシフトする冷徹なエンジン」に過ぎない。
だからこそ、そのエンジンに与える「優先順位」という名の燃料を、我々エンジニアがロジカルに、美しくコントロールしなければならないのだ。
今回のコードをベースに、自社のプロジェクト管理の要件に合わせたカスタムフィールド(Costあふれフラグ、顧客重要度など)を `CalculateDynamicPriority` 関数に組み込んでみてほしい。あなたの管理するプロジェクトから、リソース競合による無駄な遅延が綺麗に消え去ることを約束しよう。
