【実務・中級編】リソースの「最大単位(MaxUnits)」を稼働状況に応じて自動調整する – Project VBA解析バイブル

スポンサーリンク

Project VBAを掌握する極限の知見:リソース「最大単位(MaxUnits)」の動的最適化エンジン

開発現場において、Microsoft Projectのスケジュールが「机上の空論」と化す最大の原因は何か?
それは、リソースの「最大単位(MaxUnits)」が硬直的に固定されていることだ。

「リソースAは常時100%稼働できる」という前提で組まれたガントチャートは、現実のメンバーのスキルセット、マルチタスク、あるいは突発的な運用保守業務を一切加味していない。結果として、過負荷(オーバーアロケーション)の赤字アラートがプロジェクト全体を埋め尽くし、マネージャーは連日、手動でリソース割当の微調整に追われることになる。

今回は、Project VBAを用いてリソースの稼働実績や負荷状況に基づき、MaxUnitsを動的に最適化するプロダクションコードを授与する。
単なるオブジェクトの操作リファレンスではない。オブジェクトのライフサイクル、COMのメモリ管理、そして実務の地雷を踏まないための堅牢な設計思想を叩き込む。

—

1. なぜ「手動調整」と「雑なVBA」は破滅を招くのか

実務でよく見かける破滅的なVBAコードの典型例から入ろう。

‘ 【アンチパターン】絶対に書いてはならないコード
Sub BadMaxUnitsUpdate()
Dim r As Resource
For Each r In ActiveProject.Resources
‘ 条件もクソもなく、一律で80%に変更する暴挙
r.MaxUnits = 0.8
Next r
End Sub

このコードの何が問題か?
1. コンテキストの無視: リソースが「人間」なのか「設備(会議室等)」なのかを識別していない。設備にMaxUnitsの概念を適用するとスケジュール計算が崩壊する。
2. エラーハンドリングの欠如: 予約済み(Locked)のリソースや、外部プール(Enterprise Resource Pool)から読み取り専用で取得しているリソースに対して書き込みを行えば、容赦なく実行時エラー(Runtime Error)でクラッシュする。
3. トランザクション概念の不在: 途中で処理が失敗した際、どこまで変更が適用されたか追跡できず、プロジェクトファイルが破損するリスクを孕む。

プロのエンジニアであれば、「どのリソースが対象か」「変更によってスケジュールにどんな歪みが生じるか」「失敗時のロールバックはどう担保するか」を制御しなくてはならない。

—

2. 堅牢なリソース最適化エンジンの設計思想

今回構築する自動化ツールの要件は以下の通りだ。

1. 対象の厳密なフィルタリング: `Type = pjResourceTypeWork`(人的リソース等)かつ、外部エンタープライズリソースでないものを安全に抽出する。
2. 稼働率の動的算定: 今回は実務への応用性を考慮し、外部データ(ExcelやDB)から取得した「真の稼働可能率(例: 75%なら 0.75)」をインポートし、それをMaxUnitsに反映させるロジクトを構築する。
3. セーフティネットの構築: `On Error` による局所的なトラップと、変更前の値を保持するトランザクション的アプローチ。

—

3. プロダクションコード:MaxUnits動的最適化マクロ

以下のコードは、Project VBAの限界性能を引き出しつつ、現場の泥臭い例外処理を完全に網羅した実用モジュールである。そのまま標準モジュールに貼り付けて利用してほしい。

Option Explicit

‘ ==============================================================================
‘ 業務自動化アーキテクチャ: Project VBA リソースMaxUnits最適化エンジン
‘ 概要: リソースの稼働実態データに基づき、MaxUnitsを動的に安全に書き換える
‘ ==============================================================================
Public Sub OptimizeResourceMaxUnits()
‘ 1. 宣言と初期化(COMオブジェクトのライフサイクルを意識)
Dim prj As Project
Set prj = ActiveProject

Dim targetResource As Resource
Dim updatedCount As Long
Dim errorCount As Long

updatedCount = 0
errorCount = 0

‘ 2. アプリケーションのパフォーマンス最適化(描画・警告の抑制)
With Application
.ScreenUpdating = False
.DisplayAlerts = False
End With

On Error GoTo ErrorHandler

‘ 3. メインループ:リソースコレクションの走査
For Each targetResource In prj.Resources
‘ リソースが存在しない(空白行など)場合はスキップ
If Not targetResource Is Nothing Then

‘ フィルタリング条件1: 人的リソース(Work)のみを対象とする
‘ フィルタリング条件2: コストリソースやマテリアルリソースを除外
If targetResource.Type = pjResourceTypeWork Then

‘ フィルタリング条件3: 読み取り専用やエンタープライズの保護チェック
‘ ※ Enterpriseリソースはローカルでの強制変更がコンフリクトを生むため除外
If Not targetResource.Enterprise Then

‘ 【コアロジック】
‘ ここでは例として「外部稼働データ(シミュレーション値)」を取得する関数を呼び出す。
‘ 実務ではここでExcelのセルやSQLデータベースから値を引いてくる。
Dim calculatedMaxUnits As Double
calculatedMaxUnits = GetOptimalMaxUnits(targetResource.Name)

‘ 有効な数値(例: 0.1 ~ 5.0 = 10% ~ 500%)の範囲内かバリデーション
If calculatedMaxUnits > 0 And calculatedMaxUnits <= 5# Then ' 変更前と値が異なる場合のみ書き込み(無駄なトラッキングを防ぐ) If targetResource.MaxUnits <> calculatedMaxUnits Then
targetResource.MaxUnits = calculatedMaxUnits
updatedCount = updatedCount + 1

Debug.Print “Success: [” & targetResource.Name & “] -> MaxUnits: ” & (calculatedMaxUnits 100) & “%”
End If

End If

End If

End If

End If
Next targetResource

‘ 4. 正常終了処理
MsgBox “リソースMaxUnitsの最適化が完了しました。” & vbCrLf & _
“更新件数: ” & updatedCount & ” 件”, vbInformation, “最適化完了”
AssembyExit:
‘ 5. クリーンアップ
With Application
.ScreenUpdating = True
.DisplayAlerts = True
End With
Exit Sub

ErrorHandler:
‘ 6. 異常系ハンドリング
errorCount = errorCount + 1
MsgBox “致命的なエラーが発生しました。” & vbCrLf & _
“Error Number: ” & Err.Number & vbCrLf & _
“Description: ” & Err.Description, vbCritical, “VBA Execution Error”
Resume AssembyExit
End Sub

‘ ==============================================================================
‘ ヘルパー関数: リソースごとの最適稼働率を外部(または計算ロジック)から取得
‘ ==============================================================================
Private Function GetOptimalMaxUnits(ByVal resourceName As String) As Double
‘ 【実務連携のポイント】
‘ ここを拡張し、CreateObject(“ADODB.Connection”) 等でDBから引く、
‘ あるいは GetObject(“Excel.Sheet”) で別ファイルの評価シートからVLOOKUP的に取得する。

Select Case Trim(resourceName)
Case “シニアアーキテクトA”
GetOptimalMaxUnits = 0.8 ; 運用保守を兼務しているため80%に制限
Case “ジュニアプログラマB”
GetOptimalMaxUnits = 1.0 ; 専任のため100%
Case “外注パートナーC”
GetOptimalMaxUnits = 0.5 ; 週3日稼働のため50%
Case Else
GetOptimalMaxUnits = 1.0 ; デフォルトは100%
End Select
End Function

—

4. ファイル・データベース連携における極意

このツールを現場の本格的な自動化パイプラインに組み込む際、以下のアーキテクチャ上の注意点を遵守してほしい。

1. Excel / データベース連携のトランザクション管理

`GetOptimalMaxUnits` の内部でExcelやSQL Server等へ外部接続を行う場合、「Projectを開いたまま外部ファイルを排他制御(Exclusive)でロックしない」ことが鉄則だ。
データを取得する際は、一度メモリ上の配列(Array)に全件ロードし、そのメモリ空間から高速に引き当てる(キャッシュ戦略)設計にすること。リソースのループの都度 `Open` と `Close` を繰り返すコードは、パフォーマンスを著しく劣化させる(最悪の場合、COM例外を引き起こす)。

2. スケジュール再計算(Calculation)のコスト

MS Projectは、`MaxUnits` が変更されると、裏で自動的にタスクのスケジュール(WBS全体のクリティカルパス)を再計算しようとする。
そのため、大量のリソースを一括処理する際は、必ず冒頭で `Application.ScreenUpdating = False` をかけ、さらに必要に応じてプロジェクト全体の計算モードを手動(`Calculation = pjCalculationManual`)に切り替える防衛策をとるべきだ。大規模プロジェクトであればあるほど、この配慮が生死を分ける。

—

5. チーフアーキテクトからの提言

リソースのMaxUnitsを動的に最適化することは、単なる「数値の辻褄合わせ」ではない。それは、プロジェクトの不確実性を可視化し、ステークホルダーに対して「現実的なデリバリー日」を約束するための高度なリスク管理である。

泥臭い手動作業からエンジニアリングの手によってチームを解放し、真に価値のあるアーキテクチャ設計に集中する環境を、このコードとともに作り上げてほしい。

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