「日付」と「リソース」の衝突を乗りこなす —— Project VBAで制御するスケジュールの真実
こんにちは。プロジェクトの複雑なパズルを解き明かす準備はできていますか?
マクロの記録ボタンを押し、記録されたコードを眺めて「ふむふむ、動いているな」と満足する。それは素晴らしい第一歩です。しかし、実務の現場で求められるのは、単なる「操作の自動化」ではありません。
「リソースが足りないけれど、納期(制約条件)は動かせない。この矛盾をどう解決するか?」
この、プロジェクトマネジメントにおける「究極のジレンマ」に、プログラムの力で論理的な答えを出すこと。それこそが、真の自動化エンジニアが目指すべき領域です。
今日は、Microsoft Project VBAにおける「制約条件(Constraints)」と「リソース平準化(Resource Leveling)」の衝突を、どのようにスマートに解決するか。その極限の知見を伝授します。
1. なぜ、スケジュールは「破綻」するのか?
プロジェクトを管理していると、必ずと言っていいほど以下の2つの力が衝突します。
1. 制約条件(Constraint)の力
「このタスクは絶対に〇月〇日に終わらせなければならない(Must Finish On)」という、日付に対する強力な縛りです。
2. リソース平準化(Leveling)の力
「担当者の作業量が限界を超えているから、タスクを後ろにずらして調整しよう」という、現実的なリソースの制約です。
衝突のメカニズム
平準化エンジンが「リソース不足だからタスクを3日後ろにずらそう」と判断したとき、そのタスクに「Must Finish On(〇月〇日に必ず終了)」という制約がかかっていたらどうなるでしょうか?
エンジンは身動きが取れなくなります。
制約(ルール)を守ろうとすれば、リソース不足(現実)を無視することになり、スケジュールは「過負荷(Overallocated)」のまま放置されるか、あるいは計算エラーのような不自然な状態に陥ります。
これを解決するには、「制約を緩める(ASAPにする)」か、「リソースの優先順位を上げる」かの判断を、VBAによるロジックで行う必要があるのです。
2. 掌握すべきプロパティ:`ConstraintType`
ここをクリアすれば、Project VBAの基本はバッチリですよ。
この問題を解決するために、私たちが操るべきは `Task` オブジェクトの `ConstraintType` プロパティです。
Projectには、以下のような制約の種類があります(一部抜粋):
| 定数 (VBA) | 意味 | 強さ |
|---|---|---|
| `pjTaskConstraintAsSoonAsPossible` | できるだけ早く (ASAP) | 極小 (平準化に従う) |
| `pjTaskConstraintStartNoEarlierThan` | ~より前には開始しない | 中 |
| `pjTaskConstraintMustStartOn` | ~に必ず開始する | 強 |
| `pjTaskConstraintMustFinishOn` | ~に必ず終了する | 強 |
実務的な自動化ツールを作るなら、「強すぎる制約(Must系)を、平準化が効く制約(ASAP系)に動的に書き換える」というロジックが非常に強力な武器になります。
3. 実践:制約条件を自動緩和する解決エンジン
それでは、具体的なコードを見ていきましょう。
このコードは、「リソースが過負荷になっているタスクのうち、強すぎる制約(Must系)を持っているものを見つけ出し、平準化を優先するために制約を『できるだけ早く(ASAP)』へ自動的に変更する」というものです。
Option Explicit
''' <summary>
''' リソース平準化を妨げる「強すぎる制約」を検出し、自動的に緩和する
''' </summary>
Sub ResolveConstraintConflicts()
Dim t As Task
Dim proj As Project
Dim conflictCount As Integer
Set proj = ActiveProject
conflictCount = 0
' 高速化のためのテクニック:画面更新や再計算を一時停止する(大規模プロジェクトでは必須)
' ただし、Project VBAではCalculationの制御がパフォーマンスの鍵を握ります
On Error GoTo ErrorHandler
Debug.Print "--- スケジュール整合性チェック開始 ---"
' 1. プロジェクト内の全タスクをループ
For Each t In proj.Tasks
' 空のタスク(削除済みなど)はスキップ
If Not t Is Nothing Then
' 2. 【判定ロジック】
' リソースが過負荷(Overallocated)であり、
' かつ、制約が「Must(必ず~)」という強いものかチェック
If t.ResourceOverallocated = True Then
Select Case t.ConstraintType
' 「必ず終了」という強い制約がある場合
Case pjTaskConstraintMustFinishOn, pjTaskConstraintMustStartOn
Debug.Print "衝突検知: タスク [" & t.Name & "] はリソース過負荷かつ強制制約があります。"
' 3. 【解決策】制約を「できるだけ早く(ASAP)」に変更
' これにより、平準化エンジンがタスクを動かせるようになる
t.ConstraintType = pjTaskConstraintAsSoonAsPossible
conflictCount = conflictCount + 1
Debug.Print " -> 解決策: 制約を ASAP (As Soon As Possible) に緩和しました。"
' その他の制約(開始不可など)も必要に応じてここに追加可能
End Select
End If
End If
Next t
' 4. 変更を反映させるために再計算を実行
proj.Calculate
MsgBox "処理が完了しました。" & vbCrLf & _
"緩和したタスク数: " & conflictCount & " 件", vbInformation, "完了"
Exit Sub
ErrorHandler:
MsgMsgBox "エラーが発生しました: " & Err.Description, vbCritical
End Sub
コードの解説:プロの視点
1. `t.ResourceOverallocated` の活用:
単に日付を見るのではなく、Projectのエンジンが「この人は無理していますよ」と判定したフラグを直接参照しています。これが最も確実なトリガーです。
2. `Select Case` による精密な選別:
すべての制約を緩めてしまうと、プロジェクトのコントロールを失います。`Must` 系(強制)だけを狙い撃ちにするのが、プロの「加減」です。
3. `proj.Calculate` のタイミング:
ループの中で毎回 `Calculate` を呼んではいけません。これはパフォーマンスを劇的に低下させ、最悪の場合、無限ループのような挙動を引き起こします。「変更をすべて終えてから、最後に一回」。これが鉄則です。
4. 陥りやすいエラーと回避策
この領域を扱う際、初心者が必ずと言っていいほどハマるポイントが2つあります。
① 「無限ループ」の罠
制約を変更したことで、依存関係(プリセッサー)が連鎖的に動き、別のタスクが過負荷になることがあります。
これを考慮せずに「過負荷がなくなるまで繰り返す」というコードを書くと、計算が終わらなくなります。
対策: 「一度のループで緩和できるのはここまで」という、回数制限や条件の明確化が重要です。
② 「意図しないスケジュールの崩壊」
すべての `Must` 制約を解除してしまうと、クライアントとの約束(納期)が勝手に書き換わってしまいます。
対策: ツールを実行する前に、必ず「制約を緩和してもよいタスク」に特定のフラグ(例えば、テキスト欄に “RELAX” と入力しておくなど)を立て、そのフラグがあるタスクだけを対象にする、といった安全装置を組み込みましょう。
最後に:エンジニアとしてのマインドセット
Project VBAを使いこなすということは、単にプログラムを書くことではありません。「プロジェクトの論理構造」を理解し、それをコードという言語で翻訳することです。
制約とリソースの衝突を解決するツールは、単なる便利機能ではなく、プロジェクトの「健全性」を守るための防衛策になります。
「マクロの記録」から一歩踏み出し、今回のような「オブジェクトの性質(ConstraintType)」に基づいたロジックを組み立てられるようになれば、あなたはもう、立派な自動化エンジニアです。
さあ、次はあなたのプロジェクトで、このロジックを試してみてください。応援していますよ!
