こんにちは!先輩エンジニアのボクです。
マクロの記録ボタンをポチポチ押すだけのステージは、もう卒業しましたね。おめでとうございます!
さて、今回はMicrosoft Project VBAの真髄とも言えるテーマ――「リソースの稼働率を考慮したタスクの自動スケジューリングエンジン」の構築に挑みます。
「担当者の手が空いている隙間を狙って、自動でタスクをねじ込みたい」
「残業地獄にならないように、リソースのキャパシティ(最大単位)を計算してスケジュールを組ませたい」
現場でこんな無茶振りを受けたら、普通は頭を抱えますよね。でも大丈夫。Project VBAのオブジェクトモデルと、リソースのライフサイクルをしっかり理解すれば、あなた専属の優秀なAIスケジューラーを数分で組み上げることができます。
ここをクリアすれば、Project VBAの基本と「実務で使える自動化」の感覚はバッチリ掴めます。一緒に、ワンランク上のエンジニアへステップアップしましょう!
—
1. なぜ「リソース稼働率の自動化」は難しいのか?(基本概念の整理)
まず、Excel VBAとProject VBAの決定的な違いを知っておいてください。
Excelは「ただのマス目の集まり」ですが、Projectは「生き物(エンジン)」です。タスク(Task)にリソース(Resource)を割り当てると、Projectの内部カレンダーが勝手に稼働日や労働時間を計算し、ガントチャートの線を動かします。
[マニュアル操作の世界]
タスク作成 ➔ リソース割り当て ➔ 「うわ、この人パンクしてる!」 ➔ 手動で日程を後ろにずらす(絶望)
[今回目指す自動化の世界]
リソースの空き状況をVBAでスキャン ➔ 空いているスロットを特定 ➔ タスクの開始日を自動アサイン ➔ 完結!
VBAでこれをやる時の最大の罠は、「人間が目で見ているスケジュール」と「Projectが裏で持っているリソース・アサインメントのデータ」にタイムラグがあることです。
オブジェクトのライフサイクルを無視してガシガシコードを書くと、「あれ?スケジュールが更新されない…」というエラーの沼にハマります。
—
2. 実装の設計図:簡易スケジューリングエンジンのアルゴリズム
今回作成するマクロの動きはこうです。
1. ターゲットのタスクを特定する(例:「詳細設計の作成」)。
2. 担当リソースを特定する(例:「Aさん」)。
3. Aさんの現在の割り当て状況(Assignment)を確認し、いつ手が空くかを算出する。
4. 空きが見つかったら、その日付をタスクの`Start`(開始日)に代入する。
それでは、実際のコードを見ていきましょう。開発現場でそのままコピペして動かせるように、丁寧にコメントを入れています。
—
3. 実装コード:リソース考慮型タスク配置マクロ
以下のコードを、ProjectのVBAエディタ(`Alt + F11`)の標準モジュールに貼り付けてください。
Sub AutoScheduleTaskByResourceCapacity()
‘ =====================================================================
‘ テーマ: リソースの稼働率を考慮したタスクの自動スケジューリング
‘ 解説: 指定したタスクに対し、担当リソースの空き状況を加味して
‘ 自動的に開始日を再設定する簡易エンジン
‘ =====================================================================
Dim tsk As Task
Dim res As Resource
Dim tgtTaskName As String
Dim tgtResourceName As String
Dim candidateDate As Date
‘ — 1. ターゲットの設定(実際の運用では引数やセルから取得します) —
tgtTaskName = “詳細設計の作成”
tgtResourceName = “Aさん”
‘ — 2. タスクとリソースのオブジェクト取得 —
On Error GoTo ErrorHandler
Set tsk = FindTaskByName(tgtTaskName)
Set res = FindResourceByName(tgtResourceName)
If tsk Is Nothing Then
MsgBox “対象のタスクが見つかりませんでした: ” & tgtTaskName, vbCritical
Exit Sub
End If
If res is Nothing Then
MsgBox “対象のリソースが見つかりませんでした: ” & tgtResourceName, vbCritical
Exit Sub
End If
‘ — 3. リソースの空き状況(簡易ロジック)を考慮した日程計算 —
‘ ここではデモとして「現在設定されているプロジェクトの開始日、
‘ または既存アサインの終了日の翌日」を候補日とします。
candidateDate = CalculateNextAvailableDate(res)
‘ — 4. タスクへの適用とプロジェクトの再計算 —
‘ タスクにリソースがまだ割り当てられていない場合はアサインを追加
Dim isAssigned As Boolean
isAssigned = False
Dim asn As Assignment
For Each asn In tsk.Assignments
If asn.ResourceName = tgtResourceName Then
isAssigned = True
Exit For
End If
Next asn
If Not isAssigned Then
tsk.Resources.Add (tgtResourceName)
End If
‘ 開始日を自動設定
tsk.Start = candidateDate
‘ Projectのエンジンに再計算を強制(超重要!)
Application.Calculate
MsgBox “タスク「” & tsk.Name & “」を ” & tgtResourceName & ” の稼働を考慮し、” & _
Format(candidateDate, “yyyy/mm/dd”) & ” からに再スケジュールしました!”, vbInformation
Exit Sub
ErrorHandler:
MsgBox “予期せぬエラーが発生しました: ” & Err.Description, vbCritical
End Sub
‘ =====================================================================
‘ 補助関数: 名前からタスクを検索する
‘ =====================================================================
Private Function FindTaskByName(taskName As String) As Task
Dim t As Task
For Each t In ActiveProject.Tasks
If Not t Is Nothing Then
If t.Name = taskName Then
Set FindTaskByName = t
Exit Function
End If
End If
Next t
Set FindTaskByName = Nothing
End Function
‘ =====================================================================
‘ 補助関数: 名前からリソースを検索する
‘ =====================================================================
Private Function FindResourceByName(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 FindResourceByName = r
Exit Function
End If
End If
Next r
Set FindResourceByName = Nothing
End Function
‘ =====================================================================
‘ 補助関数: リソースの空き日程を算出するロジック(簡易版)
‘ =====================================================================
Private Function CalculateNextAvailableDate(res As Resource) As Date
Dim latestEnd As Date
Dim asn As Assignment
latestEnd = ActiveProject.ProjectStart
‘ 該当リソースが持つ全アサインメントを走査し、最も遅い終了日を探す
For Each asn In res.Assignments
If Not asn.Task.Milestone Then
If asn.Finish > latestEnd Then
latestEnd = asn.Finish
End If
End If
Next asn
‘ 最後のタスクが終わる日の翌日を空き日とする(簡易ロジック)
CalculateNextAvailableDate = DateAdd(“d”, 1, latestEnd)
End Function
—
4. 初学者が絶対にハマる「落とし穴」と対策
このコードを動かす上で、初心者が必ずと言っていいほど躓くポイントを先回りして解説しておきますね。
① `For Each t In ActiveProject.Tasks` でのエラー
Projectのタスクリストには、途中に「削除されたタスクの空きスロット」が含まれていることがあります。そのため、`If Not t Is Nothing Then` というヌルチェックをサボると、容赦なく「オブジェクト変数が設定されていません」エラー(実行エラー 91)が爆誕します。
ボクらのコードではしっかり対策済みなので安心してくださいね。
② 計算を反映させるための `Application.Calculate`
コードで `tsk.Start = …` と日付を書き換えても、Projectの画面や内部データが即座に同期されない場合があります。
VBAでスケジュールをいじった後は、必ず `Application.Calculate` を呼ぶこと。これが、裏のエンジンに「おい、スケジュール再計算しろよ!」と号令をかける呪文です。これを忘れると、データが宙ぶらりんになります。
—
5. さらに実務で活かすためのヒント
今回のコードは「最後に終わるタスクの翌日を空き日とする」というシンプルなものですが、実務では「リソースのオーバーアロケーション(過負荷:MaxUnits超え)」を検知するプロパティ(`res.Overallocated`)と組み合わせることで、もっと本格的なスケジューリングエンジンに進化させることができます。
「この人にこれ以上仕事を振るとパンクするから、次の週に回そう」という判断をVBAに肩代わりさせることができれば、あなたの残業時間は劇的に減るはずです。
ここをクリアできれば、Project VBAの基本はもうバッチリ。自信を持って、自分のプロジェクトにこのコードを組み込んでみてください。
分からないことがあれば、いつでも先輩エンジニアのボクを頼ってくださいね。応援しています!
