【入門編】【上級者向け】複数プロジェクトのカスタムフィールド定義を統合管理するマスターツール – Project VBA解析バイブル

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

【Project VBAの極致】複数プロジェクトのカスタムフィールドを統治する「マスター同期エンジン」の設計思想

こんにちは。現場のコードの海を泳ぎ、数々のプロジェクト崩壊を救ってきたチーフアーキテクトです。

今日は、Microsoft Projectを使ってポートフォリオ管理(複数のプロジェクトを横断した管理)を行っている時に、必ずと言っていいほど直面する「データの不一致」という巨大な壁を、VBAという武器を使ってどう乗り越えるか、という話をしましょう。

「マクロの記録」から一歩踏み出そうとしているあなた。あるいは、「なんとなく動くコード」を書いているけれど、もっと大規模なシステムを構築したいと考えているあなた。

この記事は、単なるコードの書き方ではなく、「仕組みを設計する」というエンジニアの醍醐味を伝えるためのものです。ここをクリアすれば、あなたのProject VBAのスキルは、単なる「自動化」から「統治(ガバナンス)」へと昇華しますよ。

1. なぜ、カスタムフィールドの「不一致」は死を招くのか?

想像してみてください。あなたは全社的なプロジェクト進捗レポートを作ろうとしています。

  • プロジェクトAでは、コストセンターを `Text1` に入れている。
  • プロジェクトBでは、コストセンターを `Text3` に入れている。
  • プロジェクトCでは、そもそも `Text1` に別のデータが入っている。

これでは、集計ツール(Power BIやExcelなど)でデータを統合しようとした瞬間に、地獄が始まります。これが、ポートフォリオ管理における「メタデータの不整合」です。

大規模な組織では、カスタムフィールドの定義(名前、型、計算式)を、全プロジェクトで完全に統一しなければなりません。これを手作業で行うのは不可能です。そこで必要になるのが、「マスター定義に基づいた同期エンジン」なのです。

2. 極限の知見:`CustomField` オブジェクトの本質

まず、技術的な基礎を固めましょう。Project VBAにおいて、カスタムフィールドを操作する主役は `CustomField` オブジェクトです。

多くの人が陥る間違いは、「セルに値を書き込むこと」だけが自動化だと思っていることです。しかし、真のマスターエンジニアは「フィールドの定義(器)」そのものを操作します。

図解:カスタムフィールドの構造

[ Project File ]
+-- [ CustomFields Collection ] <-- ここを操作する!
+-- [ CustomField Object ] |-- Name: "コストセンター" |-- Type: pjCustomFieldTypeText (テキスト型) |-- Formula: "[Cost] * 0.1" (計算式) |-- FieldID: 1 (内部的な識別子)

この「器(定義)」がバラバラだと、中身がどれだけ正しくても、データとしては機能しません。

3. 実装:マスター同期エンジンの設計図

それでは、実際に「マスターとなる定義リスト」を持ち、複数のプロジェクトファイルに対して、その定義を強制的に適用するコードを書いていきましょう。

今回は、シンプルかつ拡張性の高い、「定義情報を配列(またはコレクション)で管理し、各ファイルに流し込む」という設計思想で実装します。

実装コード:CustomField Sync Engine

Option Explicit

' ===============================================================================
' モジュール名: MasterSyncEngine
' 概要: 複数のプロジェクトファイルに対し、カスタムフィールドの定義を強制同期する
' 著: Legendary Chief Architect
' ===============================================================================

Sub SynchronizeCustomFields()
    Dim targetFiles As Variant
    Dim filePath As Variant
    Dim proj As Project
    
    ' 1. 同期対象のファイルリスト(本来はフォルダ内のファイルを検索するロジックにする)
    targetFiles = Array("C:\Projects\Project_A.mpp", "C:\Projects\Project_B.mpp")
    
    On Error GoTo ErrorHandler
    
    ' 画面更新を停止してパフォーマンスを向上(エンジニアの嗜みです)
    Application.ScreenUpdating = False

    For Each filePath In targetFiles
        Debug.Print "--- Processing: " & filePath & " ---"
        
        ' プロジェクトを開く
        Set proj = MSProject.Applications.Open(filePath)
        
        ' 2. カスタムフィールドの定義を適用
        ' ここで「マスター定義」を呼び出す
        ApplyMasterDefinitions proj
        
        ' 保存して閉じる
        proj.Save
        proj.Close
        
        Debug.Print "Successfully synchronized."
    Next filePath

    Application.ScreenUpdating = True
    MsgBox "全てのプロジェクトの同期が完了しました!", vbInformation
    Exit Sub

ErrorHandler:
    Application.ScreenUpdating = True
    MsgBox "エラーが発生しました。ファイルパスや定義式を確認してください。" & vbCrLf & _
           "Error: " & Err.Description, vbCritical
End Sub

''' <summary>
''' プロジェクトに対してマスター定義を適用するコアロジック
''' </summary>
''' <param name="targetProj">対象となるプロジェクトオブジェクト</param>
Private Sub ApplyMasterDefinitions(ByRef targetProj As Project)
    ' ※注意: ここでは Task(タスク)用のカスタムフィールドを定義します。
    ' Resource(リソース)用の場合は、対象のコレクションを変える必要があります。

    ' --- 定義1: コストセンター (Text1を使用) ---
    ' 既存の定義があるか確認し、なければ作成、あれば上書きする思想
    UpdateFieldDefinition targetProj, "Text1", "コストセンター", pjCustomFieldTypeText, ""
    
    ' --- 定義2: 優先度フラグ (Flag1を使用) ---
    UpdateFieldDefinition targetProj, "Flag1", "重要プロジェクト判定", pjCustomFieldTypeFlag, "[% Complete] > 50"

    ' --- 定義3: 予算計算用 (Number1を使用) ---
    UpdateFieldDefinition targetProj, "Number1", "調整後予算", pjCustomFieldTypeNumber, "[Cost] * 1.05"
    
End Sub

''' <summary>
''' 個別のフィールド定義を安全に更新する低レイヤー関数
''' </summary>
Private Sub UpdateFieldDefinition(ByRef proj As Project, _
                                 ByVal fieldID As String, _
                                 ByVal newName As String, _
                                 ByVal fieldType As Long, _
                                 ByVal formula As String)
    
    Dim cf As CustomField
    Dim found As Boolean
    found = False
    
    ' 1. 既存のフィールドが存在するかチェック
    ' ProjectのCustomFieldsコレクションをループして探す
    For Each cf In proj.CustomFields
        ' fieldID(Text1, Flag1等)が一致するか確認
        ' ※実際には内部的なIDで判定するのが最も確実ですが、ここでは分かりやすさ優先
        If cf.Name = fieldID Or IsFieldIDMatch(cf, fieldID) Then
            ' 既存フィールドが見つかった場合、定義を更新
            cf.Name = newName
            cf.Type = fieldType
            cf.Formula = formula
            found = True
            Exit For
        End If
    Next cf
    
    ' 2. 見つからなかった場合は、新規作成(エラー回避のため慎重に)
    ' ※Project VBAでは、既存の「器」に対して名前や型を書き換えるのが基本です
    If Not found Then
        ' ここで新規作成のロジックを入れる(既存のText1などは必ず存在するため、基本はUpdateでOK)
        Debug.Print "Warning: Field " & fieldID & " not found. Manual intervention may be required."
    End If
End Sub

''' <summary>
''' フィールドIDの照合ロジック(Text1などの文字列と内部IDの橋渡し)
''' </summary>
Private Function IsFieldIDMatch(cf As CustomField, targetID As String) As Boolean
    ' 簡略化のため、ここではNameプロパティの比較を行っていますが、
    ' 本格的なエンジンでは内部的なIndexやTypeを利用します。
    ' 実運用では、Text1, Text2... という名前がカスタムフィールドのNameに入っているか確認します。
    IsFieldIDMatch = (cf.Name = targetID)
End Function

4. プロフェッショナルの視点:陥りやすい罠と回避策

このコードを動かす際、初心者が必ずと言っていいほど踏む「地雷」がいくつかあります。

① オブジェクトのスコープ(Scope)の混同

Project VBAには、`Task` 用のカスタムフィールドと `Resource` 用のカスタムフィールドが存在します。
`proj.CustomFields` はこれらを混在して管理しているように見えますが、計算式(Formula)が参照できるオブジェクトは異なります。

  • Taskフィールドに `[Resource Name]` という式を書くと、エラーになります。
  • Resourceフィールドに `[Task Name]` は書けません。

「どのオブジェクトに対して、どの式を投げているか」を常に意識してください。

② 計算式(Formula)の構文エラー

`cf.Formula = “[Cost] * 1.05″` のような文字列を代入する際、1文字でもタイポ(打ち間違い)があると、その瞬間に実行時エラーでプログラムが止まります。
対策: マスター定義をコード内に直書きせず、Excelシートや外部のJSONファイルに持たせ、そこから読み込む設計にすると、メンテナンス性が劇的に向上します。

③ パフォーマンスの重み

大量のプロジェクト(100個以上など)をループで回す場合、`Application.ScreenUpdating = False` は必須です。これがないだけで、処理時間は数倍に膨れ上がります。また、`proj.Save` を行う前に、必ず「本当に書き換えて良いか」のバリデーション(検証)を挟むのが、プロの仕事です。

5. まとめ:ここから先へ

今回のツールは、単に「名前を変える」だけのものではありません。「組織全体のデータ構造を、プログラムによって強制的に統治する」という思想に基づいています。

1. 定義の集約: どこに何が書いてあるか一箇所で管理する。
2. 自動適用: 手作業を排除し、全ファイルに一律に適用する。
3. 検証: 正しく適用されたかをログで確認する。

これができれば、あなたはもう「マクロを書く人」ではなく、「業務プロセスを設計するエンジニア」です。

まずは、自分の手元にある小さなプロジェクトを対象に、このコードを試してみてください。エラーが出たら、それは成長のチャンスです。エラーメッセージを読み解き、オブジェクトのライフサイクルを理解していく。その積み重ねが、あなたを伝説のエンジニアへと導きます。

応援していますよ。頑張ってくださいね。

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