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

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

【Project VBA 掌握】テンプレート汚染を排除せよ:スケジュール計算エンジンを正常化する「制約条件クリーンアップ」の極意

プロジェクト管理の現場で、よくある「悲劇」を知っているだろうか?

過去の成功プロジェクトからタスクリストをコピーし、新しいプロジェクトのテンプレートとして流用する。一見、効率的な手法に見える。しかし、その中身を精査したとき、多くのプロジェクトマネージャーは絶望することになる。

「なぜだ? 前提条件(Predecessors)は正しく設定されているはずなのに、タスクがスケジュール通りに動かない。日付が固定されていて、クリティカルパスが全く計算されない……」

その原因は、見えない場所に潜んでいる。「制約条件(Constraints)」の残存だ。

今回は、MS Projectの心臓部である「スケジュール計算エンジン」を正常に機能させるために不可欠な、制約条件の自動クリーンアップについて、設計思想からプロダクションレベルのコード実装までを徹底的に解説する。

1. なぜ「制約条件」がプロジェクトを破壊するのか

MS Projectの本質は、単なる「日付のリスト」ではない。タスク間の依存関係に基づき、日付を動的に再計算する「動的スケジュール計算エンジン」である。

このエンジンが自由自在に動くためには、タスクが「ASAP(できるだけ早く)」の状態、つまり `pjTaskConstraintNone` である必要がある。

しかし、過去のプロジェクトからタスクをコピーしたり、特定のタスクを「Must Start On(○月○日に開始しなければならない)」として設定していた場合、その制約(Constraint)という名の枷(かせ)も一緒にコピーされてしまう。

制約条件の「毒性」

制約条件(特に `Must Start On` や `Must Finish On`)が残っていると、以下の事態を招く。

1. 連鎖反応の停止: 前行のタスクが遅延しても、制約のあるタスクは動かない。結果、スケジュール全体が嘘をつき始める。
2. スラック(余裕)の消失: 計算エンジンが制約を優先するため、本来存在するはずのフロート(余裕)が不自然にゼロ、あるいは負の値として表示される。
3. リソース平準化の失敗: 制約に縛られたタスクは、リソースの空き状況に関わらずその日付に居座り続けるため、リソースレベル化が機能しない。

テンプレート適用時、我々エンジニアが成すべきは、タスクの内容をコピーすることではなく、「計算エンジンが自由に動けるクリーンな器」を再構築することなのだ。

2. 設計思想:堅牢なクリーンアップ・アルゴリズム

単に「全ての制約を消す」というコードを書けばいいわけではない。実務レベルのツールとして、以下の3点を設計に組み込む必要がある。

① オブジェクトの存在チェック(Null参照の回避)

MS Projectの `Tasks` コレクションには、削除されたタスクや空のインデックスが含まれることがある。`If Not t Is Nothing` のチェックを怠れば、実行時に即座にランタイムエラーでクラッシュする。

② 自動スケジュールの強制(Auto Scheduledへの変換)

制約を消しても、タスクが「手動スケジューリング(Manually Scheduled)」のままであれば、計算エンジンは動かない。制約の削除と同時に、タスクを「自動スケジューリング」へと強制的にシフトさせる必要がある。

③ 計算負荷の最適化

数千タスク規模のプロジェクトにおいて、一つ一つの制約を書き換えるたびに再計算(Recalculation)が走ると、処理時間は指数関数的に増大する。計算モードを一時的に「手動」に切り替え、一括処理後に再計算させるのがプロの作法だ。

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

以下に、そのまま実務の標準モジュールとして組み込める、堅牢なVBAコードを提示する。

Option Explicit

''' <summary>
''' プロジェクト内の全タスクから不要な制約条件を排除し、
''' スケジュール計算エンジンを正常化する。
''' </summary>
Public Sub PurifyProjectConstraints()
    Dim t As Task
    Dim cleanCount As Long
    Dim manualCount As Long
    Dim errorCount As Long
    
    ' 1. パフォーマンス最適化: 計算モードを一時的に停止
    ' これにより、ループ中の不必要な再計算を防ぎ、実行速度を劇的に向上させる
    Application.Calculation = pjCalculationManual
    
    On Error GoTo ErrorHandler
    
    cleanCount = 0
    manualCount = 0
    errorCount = 0

    Debug.Print "--- Constraint Cleanup Started ---"

    ' 2. Tasksコレクションの走査
    ' Tasksコレクションは、空の要素を含む可能性があるため、Nothingチェックが必須
    For Each t In ActiveProject.Tasks
        If Not t Is Nothing Then
            
            ' --- 制約条件のクリーンアップ ---
            ' pjTaskConstraintNone (ASAP) 以外の制約がある場合、リセットする
            If t.ConstraintType <> pjTaskConstraintNone Then
                t.ConstraintType = pjTaskConstraintNone
                cleanCount = cleanCount + 1
            End If
            
            ' --- スケジュールモードの強制 ---
            ' 制約を消しても「手動」ではエンジンは動かない。
            ' すべてのタスクを「自動スケジューリング」へ移行させる。
            If t.Manual = True Then
                t.Manual = False
                manualCount = manualCount + 1
            End If
            
        End If
    Next t

    ' 3. 後処理: 計算モードを自動に戻し、一括再計算を実行
    Application.Calculation = pjCalculationAutomatic
    ActiveProject.Calculate

    ' 結果のレポート
    MsgBox "クリーンアップ完了", vbInformation, "Success"
    Debug.Print "Cleaned Constraints: " & cleanCount
    Debug.Print "Converted to Auto: " & manualCount
    Debug.Print "--- Cleanup Finished ---"
    
    Exit Sub

ErrorHandler:
    ' エラー発生時は計算モードを必ず自動に戻す(戻さないとMS Projectが固まったようになる)
    Application.Calculation = pjCalculationAutomatic
    MsgBox "致命的なエラーが発生しました: " & Err.Description, vbCritical, "Error"
    errorCount = errorCount + 1
    Debug.Print "Error encountered: " & Err.Description
End Sub

4. アーキテクトの視点:さらなる高度化に向けて

このスクリプトは「テンプレート適用時」の第一段階として完璧だが、実務ではさらに踏み込んだ要件が出てくるだろう。

データベース連携時の注意点

もし、このスクリプトを外部のSQL ServerやExcelデータベースからタスクをインポートするワークフローの一部として組み込むなら、「インポート直後」にこのクリーンアップを叩くように設計せよ。データソース側で日付が固定されている場合、インポートした瞬間に「制約付きタスク」としてMS Projectに認識されるからだ。

「意図的な制約」をどう扱うか

全ての制約を消すと、プロジェクトマネージャーが意図的に設定した「このタスクだけは絶対に○日以降に開始させたい(Start No Earlier Than)」といった制約まで破壊してしまう。

これを防ぐには、「特定のフラグ(Flag1〜Flag28)が立っているタスクはスキップする」というロジックを追加すべきだ。

  • `If t.Flag1 = False Then t.ConstraintType = pjTaskConstraintNone`

といった形で、業務ルールに基づいた「例外処理」を設計に組み込むのが、真に使いやすいツールの条件である。

結論

テンプレートのコピペは「データのコピー」ではなく、「ロジックの継承」である。
しかし、そのロジックに古い制約という名のノイズが混じっていれば、プロジェクトの未来は予測不能なものとなる。

VBAを書くときは、単に「動くコード」を書くのではない。「対象となるオブジェクト(この場合はスケジュール計算エンジン)が、最も健全な状態で動作するための環境を整えるコード」を書け。

それが、エンジニアリングの本質だ。

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