【実務・中級編】【実務中級】Resourceの「カレンダー」をVBAで個別に上書きし、特定の担当者の休暇を反映させる – Project VBA解析バイブル

スポンサーリンク

【実務中級】MS Project VBAでリソース個別のカレンダー(例外日)を動的制御する極限の知見

プロジェクトマネジメントの現場において、リソースの稼働管理はスケジュール精度の命運を握る。汎用的なプロジェクトカレンダーとは別に、「特定のメンバーが急遽取得した有給休暇」や「突発的な出張による稼働除外日」を、何百人規模のリソースプールに対して手動でポチポチ設定していく……。そんな泥臭い作業を続けていないだろうか?

もしあなたが業務効率化ツールの開発者であれば、その手作業こそが「システムによる自動化」の最大のターゲットであると気づいているはずだ。

今回は、MS Project VBAにおける `Resource` オブジェクト、`Calendar` オブジェクト、そして例外日を司る `Exception` オブジェクトのライフサイクルと癖を徹底的に解説し、実務で絶対に破綻しない堅牢なカレンダー上書きロジックを伝授する。

1. なぜ「標準機能の手動設定」では実務で破綻するのか?

MS ProjectのGUI上でリソースの稼働日を変更する場合、[リソース情報]ダイアログを開き、[稼働時間]タブの変更を行う。しかし、これが数人ではなく「全社規模の300名」となるとどうなるか。

  • ヒューマンエラーの温床: 日付の入力ミス、期間の設定ミスがスケジュール全体を狂わせる。
  • メンテナンス性の欠如: 人事システムや勤怠管理システム(CSV/DB)からの変更差分が発生するたびに、手動では追従しきれなくなる。
  • 「ベースカレンダー」と「リソース個別カレンダー」の混同: ここがProject VBAの最大の罠である。リソースのカレンダーをいじる際、ベースカレンダー自体を書き換えてしまうと、他のリソースのスケジュールまで破壊される。「特定のリソースだけが持つ固有のカレンダー(またはベースのオーバーライド)」を正確にターゲットにしなければならない。

プロフェッショナルなエンジニアであれば、外部データ(CSVやExcel)をソースとして、VBAからAPI経由でアトミックに例外日を流し込む仕組みを構築すべきだ。

2. Projectオブジェクトモデルの急所:ResourceとCalendarの構造

VBAからリソースの例外日を操作する際、以下のオブジェクト階層を正確に把握しておく必要がある。

Application
└── ActiveProject
└── Resources (Collection)
└── Resource (Object)
└── BaseCalendar (Property) / Calendar (Object)
└── Exceptions (Collection)
└── Exception (Object)

押さえておくべき技術的ポイント

1. リソースには固有の「ベースカレンダー」が割り当てられている
通常、リソース(`Resource.BaseCalendar`)は「標準」などのベースカレンダーを継承している。
2. 個別の例外を追加するアプローチ
リソース個別に例外(休暇など)を持たせる場合、そのリソースが参照しているカレンダーオブジェクト(またはリソース自体のカレンダーコンテキスト)に対して `Exceptions.Add` メソッドを実行する。
3. カレンダー名の衝突回避
MS Projectでは、プロジェクト全体でカレンダー名が一意である必要がある。リソース固有のカレンダーをプログラムで動的生成・操作する場合、名前の重複によるランタイムエラー(Error 1100など)を完全にガードするロジックが不可欠である。

3. 【プロダクションコード】特定リソースの休暇を動的に上書きする堅牢なVBAプロシージャ

以下のコードは、外部のデータソース(今回はシミュレーションとしてハードコーディングまたは別シートを想定)から取得した「リソース名」と「休暇日(単日または期間)」をもとに、該当リソースのカレンダーに例外日を安全に追加する実務レベルのモジュールである。

エラーハンドリング、トランザクション的な視点、そして既存例外との重複チェック(または上書き)を考慮した実装になっている。

Option Explicit

‘ ==============================================================================
‘ モジュール名: ModResourceCalendarManager
‘ 概要 : 指定されたリソースのカレンダーに特定の休暇(例外日)を動的に追加する
‘ 著者 : 首席VBAアーキテクト
‘ ==============================================================================

Public Sub ApplyResourceExceptionExample()
Dim targetResourceName As String
targetResourceName = “開発 太郎” ‘ 対象となるリソース名

Dim exceptionStart As Date
Dim exceptionFinish As Date
exceptionStart = #10/15/2023# ‘ 休暇開始日
exceptionFinish = #10/17/2023# ‘ 休暇終了日(単日の場合は同日を指定)

Dim exceptionName As String
exceptionName = “特別夏季休暇(自動設定)”

‘ メイン処理の実行
Call UpdateResourceCalendarException(targetResourceName, exceptionStart, exceptionFinish, exceptionName)
End Sub

”’

”’ 指定リソースのカレンダーに例外日(非稼働日)を追加する
”’

”’ リソース名 ”’ 開始日 ”’ 終了日 ”’ 例外の名称 Public Sub UpdateResourceCalendarException(ByVal resName As String, ByVal startDate As Date, ByVal finishDate As Date, ByVal excName As String)

‘ エラーハンドリングの有効化(MS ProjectのCOM例外をトラップ)
On Error GoTo ErrorHandler

Dim tgtResource As Resource
Set tgtResource = GetResourceByName(resName)

If tgtResource Is Nothing Then
MsgBox “指定されたリソースが見つかりません: ” & resName, vbCritical, “リソースエラー”
Exit Sub
End If

‘ リソースに紐付くカレンダーオブジェクトを取得
‘ 注意: Resourceオブジェクト自体は直接Exceptionsを持たないため、BaseCalendarプロパティを経由するか、
‘ プロジェクト全体のCalendarsコレクションからリソース名に紐づくものを特定する。
‘ ※MS Projectでは通常、リソース名と同名の個別カレンダーを持つか、ベースカレンダーを共有する。
‘ ここでは「リソース固有のベースカレンダー」を取得・操作するアプローチをとる。

Dim targetCal As Calendar
Dim calName As String
calName = tgtResource.BaseCalendar

‘ もしリソース専用のカレンダーが存在しない(標準を共有している等)場合、
‘ リソース名ベースのカスタムカレンダーを新規作成してアサインし直す設計が安全。
‘ 今回はシンプルに既存の割り当て先カレンダー(BaseCalendar)に対して例外を追加する実装とする。
‘ (※共有カレンダーの場合、他のメンバーにも影響するため実務では専用カレンダーの分離を推奨)

Set targetCal = ActiveProject.Calendars(calName)

‘ 既に同じ期間・名称の例外が存在するかチェックし、重複登録を防ぐ(冪等性の担保)
If ExistsException(targetCal, startDate, finishDate) Then
MsgBox “指定された期間の例外は既に登録されています。”, vbInformation, “スキップ”
Exit Sub
End If

‘ 例外日の追加
‘ 構文: Calendar.Exceptions.Add(Type, Start, Finish, Name, [Shift1Start], [Shift1Finish], …)
‘ pjExceptionNonWorking は非稼働日を指定する列挙体
targetCal.Exceptions.Add _
Type:=pjExceptionNonWorking, _
Start:=startDate, _
Finish:=finishDate, _
Name:=excName

MsgBox “[” & resName & “]” & ” のカレンダーに例外日を正常に設定しました。” & vbCrLf & _
“期間: ” & Format(startDate, “yyyy/mm/dd”) & ” ~ ” & Format(finishDate, “yyyy/mm/dd”), _
vbInformation, “処理成功”

Exit Sub

ErrorHandler:
MsgBox “予期せぬエラーが発生しました。” & vbCrLf & _
“エラー番号: ” & Err.Number & vbCrLf & _
“詳細: ” & Err.Description, vbCritical, “致命的エラー”
End Sub

”’

”’ リソース名からResourceオブジェクトを安全に検索する
”’

Private Function GetResourceByName(ByVal resName As String) As Resource
Dim r As Resource
For Each r In ActiveProject.Resources
If Not r Is Nothing Then
If r.Name = resName Then
Set GetResourceByName = r
Exit Function
End If
End If
Next r
Set GetResourceByName = Nothing
End Function

”’

”’ カレンダーに同一期間の例外が既に登録されているか判定する(二重登録防止)
”’

Private Function ExistsException(ByVal cal As Calendar, ByVal targetStart As Date, ByVal targetFinish As Date) As Boolean
Dim exc As Exception
Dim exists As Boolean
exists = False

On Error Resume Next
For Each exc In cal.Exceptions
If Not exc Is Nothing Then
‘ 日付の重複判定(簡易ロジック)
If exc.Start <= targetFinish And exc.Finish >= targetStart Then
exists = True
Exit For
End If
End If
Next exc
On Error GoTo 0

ExistsException = exists
End Function

4. チーフアーキテクトが教える「現場で絶対にハマる罠」と回避策

上記のコードを実務の現場(特に数千行のWBSを持つ大規模プロジェクト)に導入する際、以下の「現場の泥臭い罠」に直面することがある。プロとしてこれらをどう回避すべきかを伝授しよう。

罠1:共有ベースカレンダーを書き換えてしまうことの恐怖

先ほどのコードで `tgtResource.BaseCalendar` を取得したが、もしそのリソースが「標準」という名前のベースカレンダーを他の50人のメンバーと共有していた場合、そのカレンダーに例外を追加するとプロジェクト全体の「標準」カレンダーが汚染され、関係のないメンバーまで休暇扱いになってしまう。

【解決策】
実務において個別の休暇を反映させる場合、以下の設計アプローチをとるのが鉄則である。
1. リソース専用のベースカレンダー(例: `Res_開発太郎`)が存在するか確認する。
2. 存在しない場合は、標準カレンダーを元にして新規にベースカレンダーを作成する(`ActiveProject.Calendars.Add “Res_開発太郎”, “標準”`)。
3. 該当リソースの `BaseCalendar` プロパティをその専用カレンダーに書き換える。
4. その専用カレンダーに対して `Exceptions.Add` を実行する。

このひと手間を加えることで、他のリソースへの影響を完全に遮断し、クリーンなリソース管理が実現できる。

罠2:パフォーマンスの劣化(ループ処理の重み)

数百人分の休暇データをデータベースやExcelから一括読み込みし、毎回のループで `ActiveProject.Resources` や `Calendars` を全件走査(O(N^2)の計算量)すると、処理が数分間フリーズすることがある。

【解決策】

  • 検索対象のリソースやカレンダーの参照を一度Dictionaryオブジェクト(`Scripting.Dictionary`)にキャッシュし、メモリ上でO(1)の高速ルックアップを実現する。
  • 画面描画の更新を一時停止する(`Application.ScreenUpdating = False` ――もっとも、MS ProjectのVBAではExcelほど効果が顕著ではないが、イベントや不要な再計算を抑制する配慮は有効)。

5. おわりに

Project VBAは、正しく飼い慣らせば手作業による数時間の苦行を「0.5秒のワンクリック」に変える強力な武器となる。

今回解説したカレンダーと例外オブジェクトの制御は、単なるAPIのリファレンス書き写しではなく、「他のリソースへの波及を防ぐスコープ管理」「二重登録を防ぐ冪等性」という実務に耐えうる堅牢な設計思想に基づいている。

あなたの開発するツールにこの知見を組み込み、プロジェクトマネージャーたちを「手動メンテ地獄」から解放してやってほしい。それこそが、真の業務自動化エンジニアの仕事である。

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