【実務・中級編】実務中級:プロジェクトテンプレート適用時の「制約条件クリーンアップ」自動化 – Project VBA解析バイブル

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

テンプレートの呪縛を解け:Project VBAによる「制約条件クリーンアップ」の極致

プロジェクト管理の自動化に取り組むエンジニアが、必ずと言っていいほど直面する「見えない壁」がある。それは、「テンプレートからプロジェクトを作成した瞬間、スケジュールが動かなくなる」という現象だ。

新しいプロジェクトの開始日を設定したはずなのに、タスクが古い日付に固執し、クリティカルパスが崩壊している。一見すると単純なバグのように見えるが、これはMS Projectのスケジューリングエンジンの仕様、すなわち「制約条件(Constraints)」のライフサイクルを理解していないことに起因する。

本稿では、テンプレートに潜む「過去の制約」を根絶し、プロジェクトを真に動的な状態へリセットするための、プロフェッショナルな設計思想と実装手法を伝授する。

1. なぜ「テンプレート適用」はスケジュールを破壊するのか

MS Projectにおけるタスクの挙動は、単なる「開始日」と「期間」だけで決まるのではない。その背後には、`ConstraintType` という強力な制約エンジンが控えている。

テンプレートを作成する際、多くのユーザーは「特定の開始日」を指定して保存してしまう。このとき、タスクには `pjTaskStartNoEarlierThan`(開始前禁止)などの制約が刻印される。テンプレートをコピーして新しいプロジェクトを作成しても、この「制約の刻印」はそのまま引き継がれる。

エンジンの衝突

新しいプロジェクトの開始日を2024年10月に設定したとしても、タスクに「2023年1月1日以降に開始」という制約が残っていれば、エンジンは「よし、2023年以降ならいつでもいいんだな。じゃあ、スケジュール計算の基準となる前工程との関係性は無視して、とりあえずこの日付に置こう」といった、意図しない計算(あるいは計算の停止)を行ってしまう。

これが、「制約条件のクリーンアップ」が必要な論理的根拠である。

2. アーキテクチャ設計:堅牢なクリーンアップ・エンジンの要件

単に `For Each task In Tasks` でループを回して制約を書き換えるだけのコードは、素人の書くコードだ。実務レベルのツールには、以下の3つの設計原則が求められる。

① 状態の確定性(Determinism)

タスクの「スケジューリングモード(手動/自動)」を無視して制約だけを変えても、計算エンジンは正しく動作しない。必ず「自動スケジューリング(Auto Scheduled)」への移行をセットで設計しなければならない。

② パフォーマンスの最適化

数千タスク規模のプロジェクトにおいて、オブジェクトへのアクセスは極めて重い。`ScreenUpdating` の制御はもちろん、不要な再計算(Recalculate)をループ内で発生させない制御が不可欠だ。

③ 例外への耐性

サマリータスク、マイルストーン、あるいは削除済みで `Nothing` になっているタスク。これらを適切にフィルタリングできないコードは、実行時に即座にクラッシュする。

3. プロダクション・コード:Constraint Reset Engine

以下に、実務でそのまま利用可能な、極めて堅牢な設計を用いたVBAコードを示す。このコードは、単なる「書き換え」ではなく、「プロジェクトの再定義」を行うためのものです。

Option Explicit

''' <summary>
''' プロジェクトの制約条件を完全にクリーンアップし、
''' 全タスクを「可能な限り早く(ASAP)」かつ「自動スケジューリング」の状態へリセットする。
''' </summary>
Public Sub ResetProjectConstraints()
    Dim t As Task
    Dim proj As Project
    Dim startTime As Double
    Dim taskCount As Long
    Dim cleanedCount As Long
    
    Set proj = ActiveProject
    
    ' --- パフォーマンス最適化の開始 ---
    ' 画面更新と再計算を停止し、計算コストを最小限に抑える
    Application.ScreenUpdating = False
    Application.Calculation = pjCalculationManual
    
    On Error GoTo ErrorHandler
    
    ' 統計用
    taskCount = proj.Tasks.Count
    cleanedCount = 0
    
    Debug.Print "--- Constraint Cleanup Started ---"
    
    ' プロジェクトの開始日を基準として保持(必要に応じて利用)
    ' proj.ProjectStart はプロジェクト全体の基準日
    
    For Each t In proj.Tasks
        ' 1. オブジェクトの存在確認(削除済みタスクのハンドリング)
        If Not t Is Nothing Then
            
            ' 2. サマリータスクは制約を持たせないのが設計の定石
            ' ただし、要件によりサマリー自体に制約を持たせる場合は除外する
            If Not t.Summary Then
                
                ' 3. 制約条件の強制リセット
                ' pjAsSoonAsPossible (ASAP) を設定することで、
                ' 過去の「Start No Earlier Than」等の呪縛を解く
                If t.ConstraintType <> pjAsSoonAsPossible Then
                    t.ConstraintType = pjAsSoonAsPossible
                End If
                
                ' 4. スケジューリングモードの強制変更
                ' 手動スケジューリング(pjManual)だと、制約を変えても日付が動かない
                If t.Manual = True Then
                    t.Manual = False ' 自動スケジューリングへ切り替え
                End If
                
                cleanedCount = cleanedCount + 1
            End If
        End If
    Next t

    ' --- 仕上げの工程 ---
    ' 全ての制約を解除した後、一度だけエンジンに再計算を命じる
    proj.Calculate
    
    MsgBox "クリーンアップ完了" & vbCrLf & _
           "処理タスク数: " & cleanedCount & " / " & taskCount, vbInformation, "Success"

CleanExit:
    ' --- 環境の復元 ---
    Application.Calculation = pjCalculationAutomatic
    Application.ScreenUpdating = True
    Debug.Print "--- Constraint Cleanup Finished ---"
    Exit Sub

ErrorHandler:
    MsgBox "致命的なエラーが発生しました: " & Err.Description, vbCritical, "Error"
    Resume CleanExit
End Sub

コードの解説:なぜこの書き方なのか

1. `pjCalculationManual` の活用:
ループ内でタスクのプロパティ(`ConstraintType` や `Manual`)を変更するたびに、MS Projectはスケジュールを再計算しようとする。これは指数関数的なパフォーマンス低下を招く。最初に `Manual` モードへ切り替え、最後に一括で `Calculate` するのが、プロの鉄則だ。
2. `If Not t Is Nothing Then`:
`Tasks` コレクションには、削除されたタスクの「抜け穴」が存在することがある。これを確認せずにプロパティにアクセスするのは、地雷原を全力疾走するようなものだ。
3. `t.Manual = False`:
ここが最も重要だ。制約を `pjAsSoonAsPossible` にしても、タスクが「手動(Manual)」であれば、ユーザーが入力した固定日付が優先され続け、スケジュールは一切動かない。「制約の解除」と「自動化の強制」は、コインの表裏である。

4. 実務への統合:データベース・外部連携時の注意点

このクリーンアップ処理を、外部のExcel管理表やSQLデータベースからプロジェクトを生成するワークフローに組み込む場合、以下の点に注意せよ。

  • データの整合性(Data Integrity):

外部DBから「開始日」を流し込む際、DB側の制約(例:日付のフォーマット)とMS Projectの `Date` 型の不一致によるエラーを考慮せよ。必ず `CDate()` 等で型を明示的に変換し、異常値(1899/12/30など)を弾くロジックを挟むこと。

  • トランザクションの概念:

大量のタスクを生成し、その後にクリーンアップを行う設計にするならば、途中でエラーが起きた際に「中途半端な制約状態のプロジェクト」が残らないよう、エラーハンドリング内でプロジェクトを閉じる、あるいはロールバック(以前の状態を保持したバックアップからの復元)を検討すべきだ。

結論

テンプレートを利用した自動化において、最も避けるべきは「動いているように見えて、実は計算が止まっている」状態だ。

制約条件を制御することは、単なる日付の書き換えではない。プロジェクトの「意思決定エンジン」を、過去の遺物から解放し、現在の計画へと同期させる儀式である。 この設計思想を理解し、堅牢なコードを実装することで、君の作るツールは真にプロフェッショナルなものへと昇華する。

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