【実務・中級編】実務中級:サブプロジェクト統合時の制約条件競合を自動解消するVBAスクリプト – Project VBA解析バイブル

スポンサーリンク
スポンサーリンク

「制約条件の地獄」から脱却せよ:サブプロジェクト統合時のスケジュール崩壊を防ぐ、極限の自動調整設計

プロジェクト管理の現場において、MS Projectの「サブプロジェクト統合」は、強力な武器であると同時に、スケジュールを破壊する時限爆弾でもある。

複数のプロジェクトを一つのマスタープロジェクトに統合した瞬間、ガントチャートが予期せぬ挙動を見せ、タスクが数ヶ月先に吹き飛んだり、逆にクリティカルパスが消失したりする。これはバグではない。「制約条件(Constraints)」と「スケジュール計算エンジン」の不整合が生んだ、設計上の必然的な帰結だ。

多くの開発者は、力技で「すべての制約をASAP(可能な限り早く)に書き換える」という安易なコードを書く。しかし、それはプロの仕事ではない。実務における制約には「意味」がある。それを無視した自動化は、プロジェクトの整合性を根底から破壊する。

本記事では、マスタープロジェクトへの統合時に発生する制約条件の競合を、ロジカルかつ安全に解消するための、プロダクションレベルの設計思想と実装手法を伝授する。

1. なぜスケジュールは「崩壊」するのか

MS Projectの計算エンジンは、以下の2つの要素の相互作用で動いている。

1. 論理関係(Dependencies): タスク間の「FS(終了後開始)」などのリンク。
2. 制約条件(Constraints): 「○月○日に開始しなければならない(Must Start On)」といった、論理関係を無視して強制的に日付を固定する命令。

サブプロジェクトを統合する際、サブプロジェクト側で設定されていた「Must Start On(強制開始日)」が、マスタープロジェクト側の論理関係(先行タスクの終了日など)と矛盾した場合、エンジンはどちらか一方を優先せざるを得ない。その結果、無理やり引き延ばされたタスクがドミノ倒しのようにスケジュールを狂わせるのだ。

我々が目指すべきは、「強制的な制約(Hard Constraint)」を「論理的な境界(Soft Constraint)」へと、文脈を維持したまま変換することである。

2. 設計思想:HardからSoftへの「昇格」

堅牢な自動調整ツールの設計には、以下の3ステップが必要となる。

Step 1: 制約の検知と分類

すべてのタスクを対象にするのではなく、`ConstraintType` を走査し、`pjTaskMustStartOn` や `pjTaskMustFinishOn` といった「Must系」の制約を持つタスクのみを抽出する。

Step 2: 制約の緩和(Softening)

「Must Start On(必ずこの日に開始)」を、そのまま「ASAP(制約なし)」に変えてはいけない。それは、本来守るべきデッドラインを放棄することを意味する。
代わりに、「Start No Earlier Than(これより前には開始できない)」へと変換する。これにより、「その日以降なら、論理関係に従って動ける」という柔軟性をエンジンに与えることができる。

Step 3: 計算エンジンの制御

タスクを一つ書き換えるたびにスケジュールを再計算させてはいけない。これはパフォーマンスの致命的な低下を招くだけでなく、不完全な状態での計算による二次的なエラーを引き起こす。

3. プロダクションコード:Constraint Resolver

以下に、実務でそのまま利用可能な、保守性と堅牢性を兼ね備えたVBAコードを示す。

Option Explicit

''' <summary>
''' サブプロジェクト統合時に発生する「強制制約(Must系)」を
''' 「開始可能制約(SNET/FNLT系)」へ安全に変換し、スケジュール崩壊を防ぐ。
''' </summary>
Public Sub ResolveConstraintConflicts()
    Dim t As Task
    Dim conflictCount As Long
    Dim startTime As Double
    
    ' パフォーマンス最適化:計算モードをマニュアルに変更
    ' これを行わないと、タスク更新のたびに全工程が再計算され、処理が指数関数的に遅くなる
    Application.Calculation = pjManual
    
    On Error GoTo ErrorHandler
    
    conflictCount = 0
    startTime = Timer
    
    Debug.Print "--- Constraint Resolution Started ---"

    ' 全タスクをループ(サブプロジェクト内のタスクも含む)
    For Each t In ActiveProject.Tasks
        ' 空のタスク行をスキップ
        If Not t Is Nothing Then
            
            ' 制約条件の判定
            Select Case t.ConstraintType
                
                ' Case 1: Must Start On (強制開始日) -> Start No Earlier Than (開始可能日) へ変換
                Case pjTaskMustStartOn
                    t.ConstraintType = pjStartNoEarlierThan
                    conflictCount = conflictCount + 1
                    Debug.Print "Converted [Must Start On] -> [SNET]: " & t.Name
                
                ' Case 2: Must Finish On (強制終了日) -> Finish No Later Than (終了可能日) へ変換
                Case pjTaskMustFinishOn
                    t.ConstraintType = pjFinishNoLaterThan
                    conflictCount = conflictCount + 1
                    Debug.Print "Converted [Must Finish On] -> [FNLT]: " & t.Name
                
                ' Case 3: Must Start On (強制開始日) かつ 以前の制約
                ' 必要に応じて、その他のHard Constraintを追加していく
                Case pjTaskStartOn
                     ' Start On は「その日に開始」という強い制約。
                     ' 統合時は SNET に落とすのが定石。
                     t.ConstraintType = pjStartNoEarlierThan
                     conflictCount = conflictCount + 1
                     
            End Select
        End If
    Next t

    ' 最後に一度だけ再計算を実行
    Application.Calculation = pjAutomatic
    
    MsgBox "処理完了" & vbCrLf & _
           "調整したタスク数: " & conflictCount & vbCrLf & _
           "実行時間: " & Format(Timer - startTime, "0.00") & " 秒", vbInformation, "Success"
    
    Exit Sub

ErrorHandler:
    ' エラー発生時は必ず計算モードを自動に戻す(これを行わないと、ユーザーのMS Projectが壊れたように見える)
    Application.Calculation = pjAutomatic
    MsgBox "致命的なエラーが発生しました: " & Err.Description, vbCritical, "Error"
End Sub

4. アーキテクトの視点:実務への実装における注意点

このスクリプトを単なる「コピペ」で終わらせてはいけない。システムとして組み込む際は、以下の3点を考慮せよ。

① 外部データベースとの同期(Single Source of Truth)

もし、制約条件(デッドライン)を外部のERPやデータベースで管理している場合、VBAで `ConstraintType` を書き換えた後に、「どの制約を緩和したのか」というログをDBに書き戻す設計が必要だ。
「いつ、どのタスクの制約を、どのロジックで緩和したか」が追跡できなければ、プロジェクト監査において致命的な欠陥となる。

② ユーザーへの「意図」の確認

完全に自動化するのではなく、統合直後に「以下のタスクの制約を緩和しました。よろしいですか?」というダイアログ、あるいは「緩和対象リスト」をExcel等に書き出して確認させるプロセスを挟むのが、現場で信頼されるツールの条件である。

③ オブジェクトのライフサイクルとメモリ管理

大規模プロジェクト(数万タスク規模)の場合、`For Each t In ActiveProject.Tasks` はメモリを大量に消費する。もしさらに高速化を求めるなら、`Task` オブジェクトを直接操作するのではなく、一旦配列に情報を格納するか、ActiveX経由でのAPI呼び出しを検討すべきだが、通常のプロジェクト規模であれば上記の `pjManual` による制御だけで十分なパフォーマンスが得られる。

結論

優れた自動化エンジニアは、ツールで「問題を消し去る」のではない。「問題を管理可能な状態に変換する」のだ。

「Must」を「No Earlier Than」に変える。この小さな、しかし本質的な設計思想の差が、プロジェクトの整合性を守り、エンジニアとしてのあなたの価値を決定づける。

Happy Coding. And keep your schedules logical.

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