【Project VBA極限講座】サブプロジェクト統合時の「スケジュール崩壊」を防ぐ:制約条件の自動最適化ロジック
「サブプロジェクトをマスタープロジェクトに統合した瞬間、ガントチャートがバラバラに崩れてしまった……」
そんな経験はありませんか?
せっかく作り込んだサブプロジェクトのスケジュールが、統合した途端に予期せぬ日付へ飛び、クリティカルパスが狂ってしまう。これは、Project VBAを使い始めた中級者が必ずと言っていいほど直面する「制約条件(Constraint)の衝突」という壁です。
マクロの記録機能で「ボタンを押す動作」を記録する段階は、もう卒業しましょう。
今日は、Projectの計算エンジンがどのように動いているのかという本質に触れながら、統合時の制約条件をスマートに自動解消する、プロフェッショナルなスクリプトの書き方を伝授します。
ここをクリアすれば、あなたは「ただのマクロ使い」から「プロジェクト・アーキテクト」へと進化できるはずですよ。
1. なぜスケジュールは「崩壊」するのか?
結論から言いましょう。原因は「制約条件(Constraint)」と「依存関係(Dependency)」の混同にあります。
依存関係(Dependency)
「タスクAが終わったら、タスクBを始める」という、タスク同士の論理的な繋がりです。これはスケジュールの「流れ」を作ります。
制約条件(Constraint)
「タスクAは、絶対に6月1日に開始しなければならない(Must Start On)」という、日付に対する強力な「縛り」です。
サブプロジェクトを統合する際、個別のプロジェクト内で設定されていた「Must Start On(強制開始日)」などの強い制約が残っていると、マスタープロジェクト全体の論理的な流れ(依存関係)を無視して、そのタスクを無理やり固定しようとします。
その結果、「依存関係による計算結果」と「制約条件による固定値」がケンカを起こし、スケジュールが不自然に伸びたり、逆に無理な圧縮がかかったりするのです。
2. 戦略:制約条件を「緩める」自動化
実務における最も賢明なアプローチは、統合時に「強い制約(Hard Constraints)」を「緩い制約(Soft Constraints = ASAP/ALAP)」に自動変換することです。
- Before: 「Must Start On(強制開始日)」 $\rightarrow$ スケジュールを破壊する原因
- After: 「As Soon As Possible(可能な限り早く)」 $\rightarrow$ 依存関係に従って動く、柔軟な状態
これをVBAで、全タスクをスキャンしながら一括処理していきます。
3. 実践:制約条件自動解消スクリプト
それでは、実際に使えるコードを見ていきましょう。このコードは、単に書き換えるだけでなく、実行速度を落とさないための「エンジニアの作法」を盛り込んでいます。
Option Explicit
''' <summary>
''' サブプロジェクト統合時に発生する強い制約条件を、
''' 依存関係に従う「可能な限り早く(ASAP)」へと自動変換する。
''' </summary>
Sub ResolveConstraintConflicts()
Dim t As Task
Dim countChanged As Long
Dim startTime As Double
' パフォーマンス最適化の儀式
' 画面更新を止め、計算モードを「手動」にすることで、
' タスクを一つ書き換えるたびに再計算が走るのを防ぎます。
Application.ScreenUpdating = False
Application.Calculation = pjManual
startTime = Timer
countChanged = 0
On Error GoTo ErrorHandler
' プロジェクト内の全タスクをループ処理
' Note: Taskオブジェクトのライフサイクルを意識し、Nothingチェックを入れるのがプロの作法です
For Each t In ActiveProject.Tasks
If Not t Is Nothing Then
' 【ここが核心】制約条件の種類を判定
' pjTaskConstraintMustStartOn (強制開始日)
' pjTaskConstraintMustFinishOn (強制終了日)
' pjTaskConstraintStartNoEarlierThan (開始日以降) などの「強い制約」を狙い撃ちします
Select Case t.ConstraintType
Case pjTaskConstraintMustStartOn, _
pjTaskConstraintMustFinishOn, _
pjTaskConstraintStartNoEarlierThan, _
pjTaskConstraintFinishNoLaterThan
' 制約を「可能な限り早く (As Soon As Possible)」へ書き換え
t.ConstraintType = pjTaskConstraintAsSoonAsPossible
countChanged = countChanged + 1
Case Else
' その他の制約(既にASAPなものなど)は何もしない
End Select
End If
Next t
' 処理の終了処理
' 計算モードを「自動」に戻すことで、変更を反映させます
Application.Calculation = pjAutomatic
Application.ScreenUpdating = True
MsgBox "処理が完了しました。" & vbCrLf & _
"変更されたタスク数: " & countChanged & " 件" & vbCrLf & _
"実行時間: " & Round(Timer - startTime, 2) & " 秒", vbInformation, "完了"
Exit Sub
ErrorHandler:
' エラーが発生した場合は、計算モードを必ず自動に戻して終了させる
Application.Calculation = pjAutomatic
Application.ScreenUpdating = True
MsgBox "エラーが発生しました: " & Err.Description, vbCritical, "エラー"
End Sub
4. コードの深淵:なぜこう書くのか?
このコードには、初学者が「マクロの記録」では決して辿り着けない、3つの重要なエッセンスが詰まっています。
① パフォーマンスの重みを知る(`Application.Calculation`)
Project VBAにおいて、最もコストが高い処理は「スケジュールの再計算」です。
もし、1,000個のタスクに対して、一つずつ制約を書き換えて、その都度Projectが「えーっと、全体のスケジュールはどうなるかな?」と再計算していたら……処理時間は数分に及ぶでしょう。
`Application.Calculation = pjManual` とすることで、「作業中は黙っていろ、最後にまとめて計算しろ」とエンジンに命令しているのです。これがプロのスピードを生みます。
② `Select Case` による精密なターゲティング
全ての制約を消去してしまうのは危険です。ユーザーが意図的に設定した「開始日以降(Start No Earlier Than)」などは残したい場合もあります。
`Select Case` を使い、「スケジュールを壊す原因となる特定の制約タイプ」だけをピンポイントで狙い撃ちする。これが、システムの整合性を守る設計思想です。
③ オブジェクトの生存確認(`If Not t Is Nothing`)
Projectのタスクリストには、削除されたタスクの残骸や、特殊な空行が含まれることがあります。
`Nothing`(空っぽの状態)のオブジェクトに対してプロパティにアクセスしようとすると、プログラムは即座にクラッシュします。この一行を入れるだけで、スクリプトの堅牢性は劇的に向上します。
5. まとめ:次の一歩へ
いかがでしたか?
「マクロの記録」は、あくまで「あなたの操作のコピー」に過ぎません。
しかし、今回学んだような「オブジェクトの状態を見て、論理的に判断し、計算エンジンを制御する」というアプローチは、まさに「プログラムによる自動化」そのものです。
ここをクリアすれば、Project VBAの基本はバッチリですよ。
次は、このスクリプトを応用して、「特定の条件を満たすタスクだけに、自動でリソースを割り当てる」といった、より高度なロジックに挑戦してみてください。
君のコードが、複雑なプロジェクトを支える強力なエンジンになる日を楽しみにしています。
