Project VBAの深淵へようこそ:サブプロジェクト統合時の「制約条件競合」を自動で鎮める技術
こんにちは。現場の自動化を支えるエンジニアの皆さん。
マクロの記録機能を使って「やりたいことの断片」を形にできるようになってきた頃でしょうか。それとも、もっと複雑なロジックを組もうとして、プロジェクトのスケジュールが意図せず跳ね上がり、途方に暮れてしまった経験があるでしょうか?
今日は、MS Projectを扱う実務者なら必ず一度は直面する、非常にエキサイティングで、かつ厄介な問題についてお話しします。
テーマは「サブプロジェクト統合時の制約条件(Constraint)の自動解消」です。
「マクロの記録」から一歩踏み出し、「プロジェクトの計算エンジンがどう動いているか」という本質的な視点を持つための、極めて実践的なレッスンを始めましょう。
1. なぜ、統合した瞬間にスケジュールが「爆発」するのか?
複数のサブプロジェクトを一つのマスタープロジェクトにまとめる作業は、プロジェクト管理の要です。しかし、単純に結合(Insert Subproject)しただけで、全体のクリティカルパスが崩壊したり、タスクが数ヶ月先へ吹き飛んだりすることがあります。
その犯人は、「制約条件(Constraint)」の衝突です。
制約条件の「硬さ」を理解する
MS Projectのタスクには、それぞれ「いつ動けるか」というルールが設定されています。これをVBAの世界では `ConstraintType` と呼びます。
- ASAP (As Soon As Possible / 可能限り早く):
制約なし。前工程が終わればすぐ動ける。もっとも「柔軟」で、スケジュール計算エンジンが最も得意とする状態です。
- Must Start On (指定開始日):
「絶対にこの日に始めろ」という命令。これは「硬い制約(Hard Constraint)」と呼ばれます。
【ここがトラブルの本質】
サブプロジェクト側で「Must Start On」が設定されているタスクをマスターに統合すると、そのタスクはマスター側の論理的なつながり(先行・後続関係)を無視して、その日付に居座ろうとします。
その結果、後続タスクが無理やり押し出され、スケジュール全体が歪んでしまうのです。
2. 戦略:エンジンの「柔軟性」を取り戻す
プロフェッショナルな自動化エンジニアは、力ずくで日付を書き換えることはしません。そうではなく、「計算エンジンが自由に動けるように、制約の硬さを調整する」というアプローチを取ります。
今回のスクリプトの設計思想はこうです:
1. 統合された全タスクを走査する。
2. 「Must Start On」などの「硬い制約」を持つタスクを特定する。
3. それらを「ASAP(制約なし)」に書き換え、論理的なつながりに基づいて再計算させる。
4. ただし、勝手に変えていいのか不安な人のために、変更したタスクをログに残す。
3. 実践:制約条件自動解消スクリプト
それでは、魂を込めて書いたコードを見ていきましょう。このコードは、単なる「書き換え」ではなく、パフォーマンスと安全性を考慮した設計になっています。
Option Explicit
''' <summary>
''' サブプロジェクト統合時に発生する硬い制約条件を検知し、
''' ASAP(制約なし)へ自動変換してスケジュールの整合性を確保する。
''' </summary>
Sub ResolveConstraintConflicts()
Dim t As Task
Dim countChanged As Integer
Dim startTime As Double
' パフォーマンス最適化: 計算モードを「手動」に切り替える
' ループ内で毎回再計算が走ると、プロジェクトが巨大な場合にフリーズします。
' これが「オブジェクトのライフサイクル」を理解する第一歩です。
Application.Calculation = pjManual
countChanged = 0
startTime = Timer
Debug.Print "--- 制約条件解消プロセス開始 ---"
' プロジェクト内の全タスクをループ
For Each t In ActiveProject.Tasks
' タスクが存在しない(空行など)場合はスキップ
If Not t Is Nothing Then
' 【重要】制約条件の判定
' pjTaskMustStartOn (Must Start On) や
' pjTaskStartNoEarlierThan (開始指定) など、
' スケジュールの柔軟性を奪う条件をチェックします。
Select Case t.ConstraintType
Case pjTaskMustStartOn, pjTaskMustFinishOn, pjTaskStartNoEarlierThan
' ログ出力(イミディエイトウィンドウ)
Debug.Print "変更対象: [" & t.Name & "] 現在の制約: " & GetConstraintName(t.ConstraintType)
' 制約を「ASAP (可能限り早く)」に変更
' これにより、タスクは論理的な先行関係に従うようになります。
t.ConstraintType = pjTaskConstraintAsSoonAsPossible
' 変更したことを視覚的にわかりやすくするため、テキストを付与(任意)
' t.Text1 = "Constraint Resolved by VBA"
countChanged = countChanged + 1
Case Else
' 制約が柔軟な場合は何もしない
End Select
End If
Next t
' 計算モードを「自動」に戻し、一括でスケジュールを再計算させる
Application.Calculation = pjAutomatic
' 結果報告
Debug.Print "--- プロセス完了 ---"
Debug.Print "変更したタスク数: " & countChanged
Debug.Print "実行時間: " & Round(Timer - startTime, 2) & " 秒"
MsgBox "制約条件の解消が完了しました。" & vbCrLf & _
"変更数: " & countChanged & " 件", vbInformation, "自動化エンジン実行完了"
End Sub
''' <summary>
''' 制約タイプを人間が読める形式の文字列に変換する補助関数
''' </summary>
Private Function GetConstraintName(ByVal cType As Long) As String
Select Case cType
Case pjTaskConstraintAsSoonAsPossible: GetConstraintName = "ASAP"
Case pjTaskConstraintMustStartOn: GetConstraintName = "Must Start On"
Case pjTaskConstraintStartNoEarlierThan: GetConstraintName = "Start No Earlier Than"
Case pjTaskConstraintStartNoLaterThan: GetConstraintName = "Start No Later Than"
Case pjTaskConstraintMustFinishOn: GetConstraintName = "Must Finish On"
Case Else: GetConstraintName = "Other/Flexible"
End Select
End Function
4. ここがプロの分かれ道:エンジニアの知見
このコードをコピーして動かすだけで満足してはいけません。伝説的なアーキテクトを目指すなら、以下の3点を意識してください。
① `Application.Calculation` の制御
コードの中に `pjManual`(手動計算)と `pjAutomatic`(自動計算)の切り替えを入れましたね。
もしこれを入れずに、ループの中でタスクを書き換えると、「1タスク書き換えるたびに、MS Projectがプロジェクト全体を再計算する」という地獄のような挙動になります。タスクが1,000個あれば、1,000回再計算が走るのです。
「計算の重み」を知ることは、大規模な業務自動化において最も重要なスキルの一つです。
② `If Not t Is Nothing` の重要性
MS Projectの `Tasks` コレクションには、ユーザーが削除したタスクの「跡地(Nothing)」が含まれることがあります。これを確認せずに `t.Name` にアクセスしようとすると、`Object variable or With block variable not set` というエラーでプログラムが止まってしまいます。
「例外的な状態」を事前に潰しておく。これがプロの作法です。
③ 制約の「緩和」は「破壊」ではない
このスクリプトは、制約を消しているのではなく、「論理的なつながり(Predecessors/Successors)に従う権利をタスクに与えている」のです。
もし、どうしてもその日付を守らなければならないタスクがある場合は、そのタスクだけを `Select Case` の対象外にする、あるいは「特定のフラグが立っているものだけ処理する」といったロジックを追加して、カスタマイズしていきましょう。
最後に
いかがでしたか?
「マクロの記録」で得られるのは「操作の記録」ですが、「VBAのプログラミング」で得られるのは「プロジェクトの意思決定の自動化」です。
制約条件を制御できるようになると、あなたは単なる「作業者」から、プロジェクトの構造を設計する「アーキテクト」へと進化します。
一歩ずつ、着実に。
このコードが、あなたの業務を劇的に変える一助となることを願っています。
ここをクリアすれば、Project VBAの基本はバッチリですよ。次はもっと複雑な、リソース平準化の自動化に挑戦してみましょう!
