【実務・中級編】【実務中級】マイルストーンタスクへの「終了日固定」制約一括適用:納品日管理の自動化ツール – Project VBA解析バイブル

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

Project VBAを掌握する極限の知見:マイルストーンの「納期固定」を自動化し、スケジュールの崩壊を防げ

プロジェクト管理において、最も恐ろしいのは「いつの間にか納期が後ろにズレている」ことだ。
タスクの遅延が連鎖し、最終的なマイルストーン(納品日)が計画から乖離していく。これは、MS Projectの計算エンジンが「柔軟性」を優先する性質を持っているからに他ならない。

多くの初級者は、マイルストーンの日付を直接書き換えるだけのスクリプトを書く。だが、それは設計として極めて脆弱だ。制約条件(Constraint)を適切に設定しなければ、後続タスクやリソースの変更によって、せっかく固定したはずの日付が再び計算エンジンによって上書きされてしまうからだ。

本記事では、特定のフラグに基づき、マイルストーンに対して「終了日固定制約」を論理的に一括適用する、プロフェッショナル仕様の自動化手法を伝授する。

1. なぜ「日付の書き換え」では不十分なのか

MS Projectの核心は、「制約条件(Constraint Type)」と「計算エンジン(Scheduling Engine)」の相互作用にある。

単に `Task.Finish = Date` と記述しても、そのタスクに「自由制約(As Soon As Possible)」が設定されていれば、前工程の遅延が発生した瞬間に、その日付は計算によって再決定される。これは「納期を守らせる」という目的において、致命的な欠陥だ。

実務で求められるのは、日付の書き換えではない。「この日付は動かしてはならない」という意志を、オブジェクトのプロパティとして刻み込むことだ。

具体的には、以下のプロパティを操作する必要がある。

  • `ConstraintType`: 制約の種類(`pjMustFinishOn` 等)を指定する。
  • `ConstraintDate`: 制約を適用する基準日を指定する。

2. プロダクション・レベルの設計思想

堅牢なツールを作るために、以下の3点を設計原則とする。

① フラグによるトリガー制御

全てのタスクに制約をかけるのは愚策だ。ユーザーが「これは納期固定だ」と判断したタスクに対し、特定のテキストフィールド(例:`Text1`)に「FIX」などのフラグを立てる運用にする。これにより、プログラムと人間の判断を分離する。

② エラーハンドリングと計算エンジンの制御

制約を強制すると、どうしても「前工程が終わっていないのに、この日に終わらなければならない」という制約の矛盾(Constraint Conflict)が発生する。これを無視して処理を進めると、プロジェクト全体の計算が狂う。エラーを検知し、ログに記録する仕組みが不可欠だ。

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

大量のタスクをループで回す際、一つ一つの変更に対してMS Projectが再計算を行うと、処理時間は指数関数的に増大する。`ScreenUpdating` の制御と、最後に一度だけ `Calculate` を実行する設計が鉄則だ。

3. 実装コード:マイルストーン納期固定エンジン

以下に、`Text1` に「FIX」と入力され、`Text2` に目標日付が入っているタスクに対し、強制的に `pjMustFinishOn`(その日に終了しなければならない)を適用するコードを示す。

Option Explicit

''' <summary>
''' マイルストーンの納期固定制約を一括適用する。
''' Text1 = "FIX" の場合に、Text2 の日付を終了制約として適用する。
''' </summary>
Public Sub ApplyMilestoneConstraints()
    Dim pj As Project
    Set pj = ActiveProject
    
    Dim t As Task
    Dim fixCount As Long: fixCount = 0
    Dim errorCount As Long: errorCount = 0
    Dim errorLog As String: errorLog = ""
    
    ' 1. パフォーマンス最適化: 画面更新と自動計算を一時停止
    ' ※ MS Project VBAでは ScreenUpdating は直接制御できない場合が多いが、
    '    計算モードの制御が重要となる。
    On Error GoTo ErrorHandler

    ' 2. ループ処理
    For Each t In pj.Tasks
        ' オブジェクトの存在確認(空行対策)
        If Not t Is Nothing Then
            
            ' 条件判定: Text1 が "FIX" かつ Text2 に日付が入っているか
            If t.Text1 = "FIX" And IsDate(t.Text2) Then
                
                Dim targetDate As Date
                targetDate = CDate(t.Text2)
                
                ' 3. 制約の適用
                ' pjMustFinishOn は「その日に終わらなければならない」という最も強い制約の一つ
                t.ConstraintType = pjMustFinishOn
                t.ConstraintDate = targetDate
                
                ' 念のため、Finish日付自体も明示的にセット(エンジンの整合性のため)
                t.Finish = targetDate
                
                fixCount = fixCount + 1
            End If
        End If
    Next t

    ' 4. 全体の再計算を実行
    pj.Calculate

    ' 5. 結果の通知
    MsgBox "処理が完了しました。" & vbCrLf & _
           "適用数: " & fixCount & " 件" & vbCrLf & _
           "エラー数: " & errorCount & " 件", vbInformation, "実行結果"
    
    Exit Sub

ErrorHandler:
    errorCount = errorCount + 1
    errorLog = errorLog & "Task: " & t.Name & " - Error: " & Err.Description & vbCrLf
    Resume Next
End Sub

''' <summary>
''' 日付文字列が有効な日付形式か判定するヘルパー関数
''' </summary>
Private Function IsDate(ByVal val As Variant) As Boolean
    If IsDate(val) Then
        IsDate = True
    Else
        IsDate = False
    End If
End Function

4. アーキテクトの視点:運用における注意点

このツールを実務に投入する際、エンジニアが考慮すべき「重み」が2つある。

制約の矛盾(Conflict)の管理

`pjMustFinishOn` を適用した際、前工程の終了日が指定日を過ぎていれば、MS Projectは警告を表示する。これはバグではない。「計画が破綻している」という事実をシステムが教えてくれているのだ。
自動化ツールを作成する際は、単にエラーを無視するのではなく、制約によって発生した「クリティカルパスのズレ」をレポートする機能(例:制約適用後に遅延が発生したタスクを抽出する)をセットで検討すべきだ。

外部データ(Excel/DB)との連携

実務では、納期リストはExcelや社内の管理データベースにあることが多い。
VBAから外部ファイルを参照する場合、`FileSystemObject` を用いてファイルの存在を確認し、`Excel.Application` を介してデータを読み込むことになるが、その際も「データの型変換」に細心の注意を払え。Excel上の日付が文字列として渡されるだけで、`ConstraintDate` への代入は失敗する。

結論

「動くコード」を書くのは誰にでもできる。しかし、「プロジェクトの整合性を守るコード」を書ける者は限られている。

マイルストーンの制御とは、単なるプロパティの操作ではない。プロジェクトの「約束」をシステムに定義する行為だ。オブジェクトのライフサイクルと、計算エンジンの挙動を深く理解し、制約という名の楔(くさび)を打ち込む。それこそが、真の業務自動化エンジニアが成すべき仕事である。

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