鎖を解き放て:Project VBAによる制約条件の完全排除とASAPスケジュールへの強制リセット
プロジェクトマネジメントの現場において、MS Projectのスケジュールが「動かない」ほど絶望的な状況はない。
タスクを動かそうとしても、なぜか特定の日付に張り付いて動かない。クリティカルパスが計算と合致しない。原因の多くは、現場の人間が「とりあえず」で設定した、制約条件(Constraints)の蓄積にある。Must Start On, Start No Earlier Than… こうした制約は、スケジュールの柔軟性を奪う「鎖」だ。
本記事では、複雑化したプロジェクトを、開始日基準の最短スケジュール(ASAP: As Soon As Possible)へと一瞬で解き放つ、堅牢なエンジニアリング手法を伝授する。単なる「コードの書き方」ではない。オブジェクトの挙動を支配するための「設計思想」を語ろう。
1. なぜ、初心者のコードは「壊れる」のか
多くの初心者は、以下のようなコードを書く。
' 避けるべき、脆弱な実装例
Sub BadReset()
Dim t As Task
For Each t In ActiveProject.Tasks
If Not t Is Nothing Then
t.ConstraintType = pjConstraintNone ' 制約を消すだけ
End If
Next t
End Sub
これでは、プロフェッショナルの現場では通用しない。なぜか?
1. 計算エンジンの無視: 制約を消しただけでは、MS Projectの計算エンジンが「いつ」再計算を行うかが不安定だ。
2. エラーハンドリングの欠如: `Tasks` コレクションには、削除済みのタスクや、特殊なオブジェクトが混在することがある。`Nothing` チェックだけでは不十分だ。
3. パフォーマンスの欠如: 大規模プロジェクト(数千タスク)において、画面更新を制御せずにループを回すのは、リソースの無駄遣いである。
4. 状態の不整合: 制約を消しても、タスクの「開始日」自体に「手入力された値」が残っている場合がある。これは制約とは別の「手動スケジュール」状態を引き起こす。
2. アーキテクチャ設計:堅牢なリセット・プロセス
真に実務で使えるツールを作るには、以下の3つのレイヤーで設計を考えるべきだ。
Layer 1: 環境制御(Environment Control)
計算負荷を下げるため、`ScreenUpdating` を停止し、プロジェクトの計算モードを一時的に「手動」に切り替える。これにより、ループ中の再計算によるオーバーヘッドを極限まで削る。
Layer 2: 状態遷移(State Transition)
制約(Constraint)を排除するだけでなく、タスクのスケジュールモードを「自動(Auto Scheduled)」へと強制的に遷移させる。これにより、制約の有無に関わらず、先行・後続関係に基づくASAP計算が有効になる。
Layer 3: 整合性検証(Integrity Verification)
処理終了後、計算エンジンを強制的に「自動」で再起動させ、プロジェクト全体の整合性を保証する。
3. プロダクション・コード:Project ASAP Reset Engine
以下に、私が実務で推奨する、保守性と堅牢性を極限まで高めたコードを示す。これをそのまま、あるいはプロジェクトの文脈に合わせて調整して使用してほしい。
Option Explicit
''' <summary>
''' プロジェクト内の全タスクの制約条件を解除し、
''' プロジェクト開始日基準のASAPスケジュールへリセットする。
''' </summary>
Public Sub ResetProjectToASAP()
Dim pj As MSProject.Application
Set pj = MSProject
Dim t As MSProject.Task
Dim taskCount As Long
Dim successCount As Long
Dim errorCount As Long
' --- 1. 環境の最適化 (Performance Optimization) ---
On Error GoTo ErrorHandler
pj.ScreenUpdating = False
' 計算モードを「手動」にすることで、ループ中の再計算負荷を最小化する
pj.Calculation = pjManual
' --- 2. 実行前確認 (Safety Check) ---
' 誤操作防止のため、ユーザーに最終確認を促す(実運用ではログ出力に置き換えることも検討)
If MsgBox("全タスクの制約を解除し、ASAPスケジュールへリセットしますか?" & vbCrLf & _
"この操作は取り消せません。", vbQuestion + vbYesNo, "実行確認") <> vbYes Then
Exit Sub
End If
' --- 3. メイン・ロジック (Core Logic) ---
taskCount = pj.ActiveProject.Tasks.Count
successCount = 0
errorCount = 0
For Each t In pj.ActiveProject.Tasks
' タスクが存在しない(削除済み等)場合はスキップ
If Not t Is Nothing Then
On Error Resume Next ' 個別のタスクエラーで全体を止めない
' 制約を「なし(None)」に設定
t.ConstraintType = pjConstraintNone
' スケジュールモードを「自動(Auto Scheduled)」に強制
' これにより、制約なしの状態でのASAP計算が正しく機能する
t.Manual = False
If Err.Number = 0 Then
successCount = successCount + 1
Else
errorCount = errorCount + 1
Debug.Print "Error on Task: " & t.Name & " - " & Err.Description
End If
On Error GoTo ErrorHandler ' エラーハンドリングを戻す
End If
Next t
' --- 4. 整合性の復元 (Engine Synchronization) ---
' 計算モードを「自動」に戻し、全タスクの再計算を強制実行
pj.Calculation = pjAutomatic
pj.ScreenUpdating = True
MsgBox "リセット完了" & vbCrLf & _
"処理タスク数: " & taskCount & vbCrLf & _
"成功: " & successCount & vbCrLf & _
"失敗: " & errorCount, vbInformation, "完了"
Exit Sub
ErrorHandler:
' 異常事態が発生した場合は、必ず環境を復元して終了する
pj.ScreenUpdating = True
pj.Calculation = pjAutomatic
MsgBox "致命的なエラーが発生しました: " & Err.Description, vbCritical, "System Error"
End Sub
4. エンジニアとしての警告:外部連携とデータ整合性
このマクロを「業務システム」の一部として組み込む場合、以下の2点に細心の注意を払わなければならない。
① データベース・外部ファイル連携の罠
もし、このMS ProjectファイルがSQL Serverなどのデータベースと同期されている、あるいは他のExcelファイルから値を参照している場合、この一括更新は「大量のトランザクション」を発生させる。
- 対策: データベースへの書き込み(Sync)が走る前にこのマクロを実行するか、あるいは同期プロセスを一時的に無効化する設計にせよ。一気に数千行の変更がデータベースに飛ぶと、ロック競合やタイムアウトの原因となる。
② 「制約の削除」は「情報の破壊」でもある
制約(Constraint)は、時に「物理的な制約(例:機材がこの日にしか入らない)」を表現するために意図的に設定されている。
- 設計思想: このマクロを「汎用ツール」として配布する場合、必ず「制約を消して良いタスク」と「消してはいけないタスク」を分けるフラグ(例:カスタムフィールドの「Reset対象」フラグ)を導入することを検討せよ。「何でも消す」のは、エンジニアリングではなく、ただの破壊だ。
結論
MS Project VBAにおける真のマスターとは、単にコードが書ける者ではない。オブジェクトのライフサイクルを理解し、計算エンジンの挙動を制御し、システム全体の副作用を予測できる者を指す。
今回のコードは、その第一歩だ。この堅牢なテンプレートを基に、君自身の業務に合わせた「最強の自動化ツール」を構築してほしい。
