【実務・中級編】タスクの「制約条件」を無視して依存関係を強制的に優先させる一括設定スクリプト – Project VBA解析バイブル

スポンサーリンク

Project VBAを掌握する極限の知見:制約の呪縛を断ち、タスクの依存関係を完全制御せよ

開発プロジェクトの現場において、Microsoft Project(以下、MSP)は強力なWBSエンジンである。しかし、実務で他部署から上がってくる工程表や、場当たり的に手動編集されたスケジュールデータは、往々にして「負債」を抱えている。

その最大の元凶が、ユーザーが無意識に設定する「制約条件(Constraint Type)」だ。

「開始日固定」「指定日以降に開始」といった制約条件は、プロジェクトマネジメントの観点ではスケジュールを固定化するアンチパターンになり得る。特に、タスク間に依存関係(先行・後続関係)が張られているにもかかわらず、強い制約条件が残っていると、MSPのエンジンは依存関係による自動計算をフリーズさせ、スケジュール全体の整合性を破壊する。

今回は、Project VBAのオブジェクトモデルの深層を突く。無数にあるタスクの制約条件を強制的に「制約なし(As Soon As Possible / できるだけ早く)」へと一括変換し、依存関係を再計算させるプロダクションレベルの自動化スクリプトを授けよう。

1. 現場の現実:なぜ「制約条件」が依存関係を殺すのか

MSPのタスクには、デフォルトで「できるだけ早く(ASAP)」という制約がかかっている。これにより、先行タスクの完了日に連動して後続タスクの開始日が自動シフトする。

しかし、現場の担当者は往々にして以下のような操作を行う。

  • ガントチャート上でバーをマウスで直接ドラッグ&ドロップした
  • 過去の進捗遅れを隠すために「開始日」を直接カレンダーから選んで上書きした

これを行った瞬間、MSPは内部的に「指定日 〇月〇日以降に開始(SNET)」などの硬い制約条件を付与する。この状態に陥ると、後からどれだけ正確な先行タスク(Predecessors)を設定しても、制約条件が優先されるため、依存関係に基づく自動スケジュール計算が機能しなくなるのだ。

数千行に及ぶ巨大なWBSで、これが何百箇所も発生しているファイルを想像してほしい。手動で一つずつ「制約なし」に戻すなど、エンジニアの仕事ではない。VBAで一網打尽にするべきだ。

2. アーキテクチャ設計:バグを生まない堅牢なVBAコードの要件

Project VBAを扱う上で、素人が書いたコードは簡単にアプリケーションをハングアップさせるか、メモリリークを引き起こす。以下の設計思想を厳守すること。

① `ActiveProject` への依存を断つ

グローバルな `ActiveProject` を安易に参照するな。現在アクティブなウィンドウが切り替わるリスクを排除するため、処理対象のプロジェクトは明示的にオブジェクト変数(`Project`)に格納してスコープを限定する。

② イベントと画面描画の完全凍結(パフォーマンスの極限化)

数千件のタスクに対して `.ConstraintType = pjAsSoonAsPossible` をループで叩くとき、画面描画(ScreenUpdating)や計算イベントが走ると、処理時間が数十倍に跳ね上がる。
Project VBAには直接的な `ScreenUpdating` プロパティはないが、計算モードを手動(Manual)に切り替えることで、MSPの重い再計算エンジンをループ中だけ完全に沈黙させる。

③ 読み取り専用タスク・サマリータスクへの配慮

プロジェクトのルートサマリーや、すでに完了したタスク、あるいは外部プロジェクトへのリンクタスクに対して無謀なプロパティ変更を行うと、ランタイムエラー(Error 1100: その操作はこのタスクでは許可されていません)が発生する。エラーハンドリングと対象外条件のフィルタリングは必須である。

3. プロダクションコード:制約強制解除&依存関係再計算ツール

以下のコードを、MSPのVBAエディタ(`ThisProject` または標準モジュール)に配置し実行せよ。実務の現場でそのまま耐えうる、堅牢かつ高速な設計を施している。

Option Explicit

‘ ==============================================================================
‘ 処理名 : 健全化エンジン:全タスク制約解除&依存関係再計算ツール
‘ 概要 : プロジェクト内のすべての有効タスクに対し、強制的に制約条件を
‘ 「できるだけ早く (ASAP)」に変更し、依存関係の自動計算を復活させる。
‘ ==============================================================================
Public Sub ForceClearConstraintsAndRecalculate()
Dim prj As Project
Set prj = ActiveProject

‘ プロジェクトが開かれていない場合のガード
If prj Is Nothing Then
MsgBox “有効なプロジェクトが開かれていません。”, vbCritical, “致命的エラー”
Exit Sub
End If

‘ 実行確認
Dim confirm As VbMsgBoxResult
confirm = MsgBox(“この操作は、すべてのタスクの制約条件を「できるだけ早く(ASAP)」に強制変更し、” & vbCrLf & _
“依存関係に基づくスケジュールを再計算します。” & vbCrLf & vbCrLf & _
“実行してもよろしいですか?”, _
vbYesNo + vbExclamation, “制約強制解除エンジン”)
If confirm = vbNo Then Exit Sub

‘ 処理開始のログとパフォーマンス最適化
Dim startTime As Double
startTime = Timer

On Error GoTo ErrorHandler

‘ 計算モードを一時的に手動(またはバックグラウンド)へ移行し、無駄な再計算を抑制
‘ ※ProjectオブジェクトのCalculation属性を制御
Application.Calculation = pjManual

Dim t As Task
Dim targetCount As Long
Dim modifiedCount As Long
targetCount = 0
modifiedCount = 0

‘ トランザクション的な処理(Undoスタックの保護)
AppActivate Application.Caption

‘ タスクループの高速化
‘ ※マイルストーンやサマリータスク、外部タスクを除外せず一括処理するが、
‘ 読み取り専用やアーティファクトは弾く
For Each t in prj.Tasks
If Not t Is Nothing Then
‘ 削除されたタスクやプレースホルダーを除外
If Not t.ExternalTask And Not t.IsNull Then
targetCount = targetCount + 1

‘ すでに「できるだけ早く」以外(固定日やSNETなど)に設定されている場合のみ変更
If t.ConstraintType <> pjAsSoonAsPossible Then
‘ サマリータスクであっても制約を持つ場合があるため安全に適用
t.ConstraintType = pjAsSoonAsPossible
modifiedCount = modifiedCount + 1
End If
End If
End If
Next t

‘ 計算モードを自動に戻し、依存関係を元に一括再計算を実行
Application.Calculation = pjAutomatic

‘ 強制再計算(プロジェクト全体)
prj.UpdateProject

‘ 終了通知
Dim elapsedTime As Double
elapsedTime = Timer – startTime

MsgBox “処理が完了しました。” & vbCrLf & vbCrLf & _
“・走査タスク数: ” & targetCount & vbCrLf & _
“・制約を解除したタスク数: ” & modifiedCount & vbCrLf & _
“・処理時間: ” & Format(elapsedTime, “0.00”) & ” 秒”, _
vbInformation, “実行完了”

Exit Sub

ErrorHandler:
‘ 異常終了時のリカバリ
Application.Calculation = pjAutomatic
MsgBox “予期せぬエラーが発生しました。” & vbCrLf & _
“Error No: ” & Err.Number & vbCrLf & _
“Description: ” & Err.Description, vbCritical, “エラー発生”
End Sub

4. コードの深層解説:なぜこの実装なのか

① `t.IsNull` と `t.ExternalTask` のガード

MSPの `Tasks` コレクションは、途中で行が削除された場合に「空のオブジェクト(Null Task)」を内包することがある。これをそのまま `t.ConstraintType` にアクセスさせると、容赦なくエラーが飛ぶ。`If Not t Is Nothing And Not t.IsNull Then` は、Project VBAにおける極めて重要な防御的プログラミングのイディオムである。

② `Application.Calculation` の制御

MSPは、1つのタスクのプロパティが変更されるたびに、影響範囲のネットワークダイアグラムを再計算しようとする。数千行のWBSでこれをやると、処理が数分単位でフリーズする。
一度計算を `pjManual` に落とし、メモリ上で一気にプロパティ書き換えを行った後、最後に `pjAutomatic` に戻して `prj.UpdateProject` を叩くことで、処理時間を劇的に短縮(数秒レベルへ)している。

5. データベースや外部ファイル連携時の注意点

このスクリプトを、Excel等の外部データソースからのインポート処理や、Project Server / Project Online(PWA)のエンタープライズ環境と組み合わせて使用する場合、以下の罠に注意せよ。

  • エンタープライズ・グローバルテンプレートの制約

サーバー側のカスタムフィールドや、強制的なガバナンスポリシーによって「特定の制約条件以外を禁止している」環境では、VBAからの強制変更がサーバー側でロールバックされるか、チェックイン時にエラーとなる。スタンドアロンの `.mpp` ファイル、またはローカル環境でのクレンジング作業として使用するのが定石である。

  • 実績入力済み(Actuals)タスクへの配慮

すでに「実績開始日」や「実績作業時間」が入力されている進捗済みのタスクに対してASAP制約を強制すると、過去の実績データとスケジュールエンジンがコンフリクトを起こす場合がある。厳密な運用をするならば、`t.PercentComplete = 0` (未着手)のタスクに限定して実行する条件分岐を付加するとより安全だ。

総括

WBSのスケジュールが汚染される最大の原因は、「人間の手動による局所的な日付け上書き」と「それを野放しにするツール設計」にある。

今回提供したスクリプトは、単なる便利マクロではない。「プロジェクトの依存関係という数学的モデルを、人間の我流の介入から奪還する」ための強力な統制ツールである。

現場のエンジニアやプロジェクトマネージャーが、スケジュールのジレンマに頭を抱えている現場があれば、このコードを静かに差し出し、一瞬でプロジェクトを本来の美しい連動状態へと復元させてみせるといい。それこそが、真の業務自動化エンジニアの仕事である。

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