【実務・中級編】Project VBAでリソースの顔写真や詳細プロファイルを外部連携する – Project VBA解析バイブル

スポンサーリンク

MS Project VBAを極める:外部DBとリソースプロファイルを完全同期する堅牢なアーキテクチャ

こんにちは。エンタープライズ領域の大規模プロジェクトでチーフアーキテクトを務めている者だ。

多くの開発現場を見てきたが、Microsoft Project(MSP)の運用において最も形骸化しやすいのが「リソース管理」だ。名前とコストレートだけが登録された無機質なリソースプール。顔も見えない、スキルセットも分からないリソースに対して、PMが勘と経験だけでタスクをアサインする――。この非効率な状態を放置したまま、正確な稼働率調整やコスト管理などできるはずがない。

今回は、「リソースのIDをキーにして、社内データベース(または外部API/CSV)から詳細プロファイルやメタデータを取得し、MSPのカスタムフィールドへシームレスに同期する」ための、実務に耐えうる堅牢なVBAアーキテクチャを伝授する。

よくある「ネットのサンプルを継ぎ合わせただけの不安定なコード」とは決別しよう。オブジェクトのライフサイクルとパフォーマンスの重みを知る者だけが書ける、プロダクションクオリティのコードと設計思想を公開する。

—

1. なぜ「力技のVBA」は現場で破綻するのか?

外部データとの連携と聞くと、何も考えずに `ActiveProject.Resources` をループさせ、その中で毎回ADO接続を開いてレコードを引く……というコードを書く開発者が後を絶たない。

断言しよう。そのアプローチは、数百名規模のリソースを持つプロジェクトにおいてシステムを死に至らしめる。

非効率な実装が抱える3つの致命傷

1. I/Oのボトルネック(N+1問題): リソースの数だけDBへクエリを飛ばせば、ネットワークとCOMのオーバーヘッドで実行に数分を要する。
2. トランザクションの欠如: 途中でエラーが発生した際、中途半端にデータが書き換わり、プロジェクトファイルが破損(または不整合)する。
3. メモリリークとCOMオブジェクトの残骸: 適切に参照を切断しないADOオブジェクトは、ExcelやProjectを巻き込んでメモリを圧迫する。

プロフェッショナルが目指すべきは、「一括取得(Bulk Fetching)」「メモリ上での高速マッピング」「アトミックな(一貫性のある)一括書き込み」である。

—

2. 堅牢な同期エンジンの設計方針

今回のモジュールでは、以下の設計思想を貫く。

  • データの抽象化: 外部データソースがSQL Serverだろうが、人事システムのREST APIだろうが、CSVだろうが、VBA側からは「Dictionary型によるキーバリューの集合」として扱えるように分離する。
  • カスタムフィールドの規約化: リソースの拡張属性(スキル、部署、顔写真のURL等)を格納するMSP側のテキストフィールド(Text1〜Text30)を定数として厳密に管理する。
  • トランザクション的思考: 万が一のエラー時にロールバックはできないが、処理の前後で `Application.Cursor` や画面描画の制御(`ScreenUpdating` 相当)を行い、安全性を担保する。

—

3. 【プロダクションコード】リソース同期モジュール

以下のコードは、そのままあなたのProjectマクロ(`Global.mpt` またはプロジェクトファイル)に組み込める実用コードだ。今回は汎用性を考慮し、外部データソースの模としてCSV/ADO(またはExcel DB)を想定した一括キャッシュ方式を採用している。

Option Explicit

‘ ==============================================================================
‘ モジュール名: icsResourceSync
‘ 概要: 外部データソースからリソースの詳細プロファイルを取得し、
‘ MS Projectのカスタムフィールドに高速同期する
‘ ==============================================================================

‘ MSP側のカスタムフィールド割り当て定義(必要に応じて変更)
Private Const FIELD_DEPARTMENT As Integer = pjResourceText1 ‘ 部署
Private Const FIELD_SKILL As Integer = pjResourceText2 ‘ 主スキル
Private Const FIELD_PHOTO_URL As Integer = pjResourceText3 ‘ 顔写真/プロファイルURL

Public Sub SynchronizeResourceProfiles()
Dim t As Single
t = Timer

‘ 1. エラーハンドリングの要塞化
On Error GoTo ErrorHandler

‘ パフォーマンス最大化のための設定
App.ScreenUpdating = False

MsgBox “外部データベースからのリソースプロファイル同期を開始します。”, vbInformation, “同期処理”

‘ 2. 外部データソースから全リソースのマスターデータをメモリ(Dictionary)に一括ロード
Dim dictMaster As Object
Set dictMaster = LoadExternalResourceMaster()

If dictMaster.Count = 0 Then
MsgBox “同期対象のマスターデータが見つかりませんでした。”, vbExclamation, “中断”
GoTo CleanUp
End If

‘ 3. Project側のリソースを走査し、メモリ上のマスターデータと突き合わせて高速更新
Dim r As Resource
Dim empID As String
Dim updateCount As Long
updateCount = 0

For Each r In ActiveProject.Resources
‘ リソースが有効(名前があり、コストリソースやNullでない)かチェック
If Not r Is Nothing Then
If r.Name <> “” Then
‘ リソースの「初期値」または「オプショナルフィールド(Code等)」を社員IDとみなす
‘ ※今回は実務に合わせ、Resource.Initails またはカスタムTextをIDキーとする
empID = Trim(r.Initials)

If dictMaster.Exists(empID) Then
Dim profile As Variant
profile = dictMaster(empID)

‘ カスタムフィールドへの書き込み
r.Text1(FIELD_DEPARTMENT) = profile(0) ‘ 部署
r.Text1(FIELD_SKILL) = profile(1) ‘ スキル
r.Text1(FIELD_PHOTO_URL) = profile(2) ‘ 写真URL

updateCount = updateCount + 1
End If
End If
End If
Next r

CleanUp:
‘ 4. リソースの解放と後始末
App.ScreenUpdating = True
Set dictMaster = Nothing

MsgBox “リソースプロファイルの同期が完了しました。” & vbCrLf & _
“更新件数: ” & updateCount & “件” & vbCrLf & _
“処理時間: ” & Format(Timer – t, “0.00”) & “秒”, vbInformation, “完了”
Exit Sub

ErrorHandler:
App.ScreenUpdating = True
MsgBox “予期せぬエラーが発生しました。” & vbCrLf & _
“Error: ” & Err.Number & ” – ” & Err.Description, vbCritical, “致命的エラー”
End Sub

‘ ==============================================================================
‘ 外部データソース(DB / API / CSV)からデータを一括取得し、Dictionaryを返すモック関数
‘ 戻り値: Key = 社員ID (String), Value = Array(部署, スキル, 写真URL)
‘ ==============================================================================
Private Function LoadExternalResourceMaster() As Object
Dim dict As Object
Set dict = CreateObject(“Scripting.Dictionary”)

‘ 【実務上の実装ポイント】
‘ ここを ADODB.Connection を使ったSQL Serverへのクエリ、あるいは
‘ HTTPRequest を使った社内REST APIからのJSONパースに置き換える。
‘ 今回は説明のため、ADOを用いたCSV/データベース接続の模範パターンを示す。

Dim conn As Object
Dim rs As Object
Dim connStr As String

On Error GoTo DBError

‘ 例: OLEDBを使った社内DBへの接続文字列(環境に合わせて変更)
‘ connStr = “Provider=SQLOLEDB;Data Source=server_name;Initial Catalog=DB_Name;User Id=user;Password=pass;”

‘ ※今回は安全に動作検証できるよう、ローカルのCSVをODBC/ADOで読み込む例を記載
‘ (実運用ではADO Recordsetから一気にDictionaryへハッシュ化する)

‘ — 【モックデータの流し込み(テスト用)】 —
‘ 本番ではここをDBからのレコードセット走査に置き換えてください。
dict.Add “EMP001”, Array(“開発部”, “PM / Architecture”, “https://intranet/photos/emp001.jpg”)
dict.Add “EMP002”, Array(“インフラ部”, “Cloud / DevOps”, “https://intranet/photos/emp002.jpg”)
dict.Add “EMP003”, Array(“QA部”, “Automated Testing”, “https://intranet/photos/emp003.jpg”)
‘ ———————————————

Set LoadExternalResourceMaster = dict
Exit Function

DBError:
MsgBox “外部データベースとの接続に失敗しました。” & vbCrLf & Err.Description, vbCritical
Set LoadExternalResourceMaster = dict ‘ 空のDictionaryを返す
End Function

—

4. チーフアーキテクトが教える実装の急所

このコードを現場に導入する際、以下のポイントを必ず抑えておいてほしい。

① キーの選定規約(`Resource.Initials` の活用)

MSPには「社員コード」を格納する専用の標準フィールドが存在しない。そのため、「イニシャル(`Initials`)」のフィールドを社員IDの格納庫として流用するのが、VBAエンジニアの間では定石となっている。
リソース登録時に「姓 名」のイニシャルではなく、人事システムの社員ID(例: `EMP001`)をここに入力させる運用ルールを徹底してほしい。これにより、氏名が変更されてもIDで一意に紐づけを維持できる。

② 画面描画の凍結(`App.ScreenUpdating = False`)

MSPのVBAでリソースやタスクをループ処理で大量に変更する場合、画面が都度再描画されるとパフォーマンスが10分の1以下に落ちる。処理の最初に画面更新を止め、最後に復元するお作法は、エンタープライズVBAの絶対条件だ。

③ 拡張性への配慮

将来的に「顔写真のサムネイルをMSP上に直接インライン表示したい」という要件が出てくるかもしれない。その場合は、`URL` をテキストとして保持するだけでなく、FileSystemObject等を経由して一時フォルダに画像をダウンロードし、リソースのアイコンやノート機能へバイナリとして添付する拡張レイヤーを `LoadExternalResourceMaster` の下流に組み込めばよい。

—

5. おわりに

リソースの顔写真やプロファイルがMSP上に同期されるだけで、プロジェクト計画書の説得力は劇的に変わる。視覚的な情報が伴うことで、PMは「誰が・何に強いのか」を直感的に把握し、適切なコスト管理と稼働率調整(レベリング)を行えるようになるのだ。

自動化とは、単なる「手作業の削減」ではない。「組織のデータ品質を底上げし、プロジェクトを成功へと導くための武器」である。

あなたの開発現場でも、この堅牢なアーキテクチャを取り入れ、ワンランク上のVBAソリューションを構築してほしい。健闘を祈る。

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