WBSの崩壊を許すな:VBAによる階層・WBS番号の完全同期と自動修復の極意
プロジェクトマネジメントにおいて、Microsoft Project(以下、Project)のWBS(Work Breakdown Structure)とアウトライン階層(インデント)の整合性は、プロジェクトの生命線である。
しかし、現場の実務はどうだろうか。
現場の担当者がエクセル感覚でタスクを行単位でコピー&ペーストし、場当たり的にインデントをいじり、タスクを挿入・削除する。その結果、何が起きるか。
- アウトラインレベル(インデント)とWBS番号(OutlineNumber)が完全に乖離する。
- 親タスクの集計期間(サマリータスク)が狂い、スケジュール全体のクリティカルパスが崩壊する。
- 進捗報告会の直前に「なぜかWBS番号が飛んでいる」ことに気づき、手作業で修正する地獄絵図が展開される。
手動での修正など論外だ。数千行規模のスケジュールにおいて、人間が目視でWBSの整合性を維持するなど、ヒューマンエラーの温床でしかない。
今回は、Project VBAのオブジェクトモデルを熟知したアーキテクトだけが知る、「タスクのインデントレベルを基準として、WBS番号とアウトライン構造を強制的に同期・再構築するプロダクションコード」を授けよう。
—
1. 現場でなぜWBSの不整合が起きるのか?(根本原因の分析)
Projectのタスクには、構造を決定する上で重要な2つのプロパティが存在する。
1. `Level` (OutlineNumber / OutlineLevel): タスクの階層の深さ(インデント)。
2. `WBS`: 表示上のWBSコード(例: `1.2.3`)。
Projectのデフォルト挙動として、タスクを挿入したり移動したりすると、ある程度は自動的にWBS番号が振り直される。しかし、以下のような「雑な操作」が加わると、エンジンが追従しきれずに内部インデックスが破損する。
- サマリータスクの直下に、誤って同一階層のタスクをねじ込む。
- マクロや外部連携ツール(Excel等)から、`Name`やカスタムフィールドだけを強引に流し込む。
- タスクの削除時にアウトラインのツリー構造が正しく閉じられなかった場合。
この状態を放置すると、Projectのスケジュールエンジンが誤作動を起こし、後続の依存関係(Predecessors/Successors)の計算にも悪影響を及ぼす。だからこそ、「VBAによって強制的にインデントレベルを正とし、WBS番号を数学的に再計算・上書きする仕組み」を常備しておかねばならない。
—
2. 堅牢なWBS自動修復ロジックの設計思想
今回構築するスクリプトの設計方針は以下の3点だ。
- 単一の真実(Single Source of Truth): インデントの深さ(`OutlineLevel`)を「絶対的な正」とする。
- 配列バースト処理(ではないが…): Project VBAではExcelのようなメモリ上の一括配列操作(`Value = Array`)が効かない。そのため、タスクコレクションへのアクセスを最小限にし、トランザクション的に処理する。
- 安全性の担保: ユーザーが意図しないデータの破損を防ぐため、実行前に確認とエラーハンドリングを徹底する。
実装上のトラップと回避策
Project VBAにおいて、タスクをループ処理しながら動的にアウトラインを変更すると、コレクションのインデックスがズレて暴走する。そのため、「上から下へのシーケンシャルな走査」と「各レベルのカウンター管理」を厳密に行う必要がある。
—
3. 【プロダクションコード】WBS完全同期・修復マクロ
以下のコードをProjectの標準モジュールに貼り付けてほしい。実務の現場でそのまま稼働する、エンタープライズ品質のコードだ。
Option Explicit
‘ ==============================================================================
‘ 処理名 : RepairWBSAndOutlineStructure
‘ 概要 : タスクのインデントレベル(OutlineLevel)を検証し、
‘ 正しいWBS番号の体系を再構築・同期する
‘ 著者 : Enterprise VBA Architect
‘ ==============================================================================
Public Sub RepairWBSAndOutlineStructure()
‘ 1. エラーハンドリングの初期化
On Error GoTo ErrorHandler
‘ 2. アクティブプロジェクトの存在確認
If ActiveProject Is Nothing Then
MsgBox “有効なプロジェクトが開かれていません。”, vbCritical, “WBS修復エラー”
Exit Sub
End If
‘ 3. 処理実行の確認(大規模プロジェクトでの誤爆防止)
Dim confirm As VbMsgBoxResult
confirm = MsgBox(“現在のタスク構造を基に、WBS番号とアウトラインの整合性を強制修復します。” & vbCrLf & _
“実行前に必ずプロジェクトファイルのバックアップを取得してください。” & vbCrLf & vbCrLf & _
“処理を実行しますか?”, vbYesNo + vbExclamation, “WBS修復の確認”)
If confirm = vbNo Then Exit Sub
‘ パフォーマンス向上のための画面描画停止
Application.ScreenUpdating = False
Dim t As Task
Dim levelCounters() As Integer
Dim currentLevel As Integer
Dim maxLevel As Integer
‘ プロジェクト内の最大アウトラインレベルを想定して配列を初期化(通常は10階層あれば十分)
maxLevel = 20
ReDim levelCounters(1 To maxLevel)
Dim i As Integer
For i = 1 To maxLevel
levelCounters(i) = 0
Next i
Dim repairedCount As Long
repairedCount = 0
‘ 4. タスクの走査とWBS番号の再構築
For Each t In ActiveProject.Tasks
‘ 削除済みタスクや空行はスキップ
If Not t Is Nothing Then
If t.Text10 <> “DELETED” And t.Name <> “” Then
currentLevel = t.OutlineLevel
‘ 階層の境界チェック
If currentLevel < 1 Then currentLevel = 1
If currentLevel > maxLevel Then currentLevel = maxLevel
‘ カウンターのインクリメント
levelCounters(currentLevel) = levelCounters(currentLevel) + 1
‘ 下位レベルのカウンターをリセット
For i = currentLevel + 1 To maxLevel
levelCounters(i) = 0
Next i
‘ WBS文字列の組み立て(例: 1.2.1)
Dim newWBS As String
newWBS = “”
For i = 1 To currentLevel
If levelCounters(i) > 0 Then
If newWBS = “” Then
newWBS = CStr(levelCounters(i))
Else
newWBS = newWBS & “.” & CStr(levelCounters(i))
End If
End If
Next i
‘ プロジェクトの標準WBSフィールドへ上書き
‘ ※Projectの仕様上、WBSは自動採番モードだと上書きできないため、
‘ 事前にプロジェクトオプションを確認するか、カスタムWBSフィールドに退避させる設計も有効。
‘ ここでは強制的にWBSプロパティへ代入を試みる。
On Error Resume Next
t.WBS = newWBS
If Err.Number <> 0 Then
‘ 自動WBSコード体系が競合している場合のフォールバックとしてカスタムWBSに書き込む例
t.Text1 = newWBS
Err.Clear
End If
On Error GoTo ErrorHandler
repairedCount = repairedCount + 1
End If
End If
Next t
‘ 画面描画の復元
Application.ScreenUpdating = True
‘ 5. 完了通知
MsgBox “WBSの修復が完了しました。” & vbCrLf & _
“処理対象タスク数: ” & repairedCount & ” 件”, vbInformation, “修復完了”
Exit Sub
ErrorHandler:
Application.ScreenUpdating = True
MsgBox “予期せぬエラーが発生しました。” & vbCrLf & _
“エラー番号: ” & Err.Number & vbCrLf & _
“エラー内容: ” & Err.Description, vbCritical, “致命的エラー”
End Sub
—
4. コードの解説とアーキテクトの視点
このコードが他の凡庸なスクリプトと一線を画す理由は、以下の設計思想に基づいているからだ。
① 配列による階層カウンター管理 (`levelCounters()`)
WBSの採番は、ツリー構造の深さに応じて親の番号を引き継ぎ、自身の番号をインクリメントし、子階層に入ったときにリセットされる必要がある。
このコードでは、`levelCounters(currentLevel)` という動的配列を使い、現在のインデントレベルに応じたカウンターを厳密に保持している。これにより、何階層深くても、何千行あっても、O(N)の計算量で一瞬にして正しいWBS文字列を生成できる。
② Projectの「WBS自動採番ロック」への対策
Microsoft Projectは、標準設定で「プロジェクトの新規作成時にWBSコードを自動生成する」機能が有効になっている。この状態のままVBAから `t.WBS = …` を実行すると、エラーになるか、次の瞬間にProjectのエンジンによって上書きされてしまう。
そのため、コード内では `On Error Resume Next` を挟み、標準WBSプロパティへの書き込みが拒絶された場合のフォールバック(`Text1`などのカスタムテキストフィールドへの退避)を考慮した堅牢な設計にしている。
※実運用では、プロジェクト側のWBSオプションを「手動」に固定するか、カスタムフィールドをレポート用WBSとして利用することを強く推奨する。
③ パフォーマンスの最適化 (`ScreenUpdating`)
数千行規模のタスクを持つプロジェクトファイルで、ループ内でGUIを再描画させると、処理が数分単位でフリーズする。`Application.ScreenUpdating = False` を挟むことで、バックグラウンドで一気に処理を完了させ、実務上のストレスを完全に排除している。
—
5. 実務運用における注意点(データベース連携・共有ファイル)
このマクロをチーム全体、あるいは企業全体のPMO(プロジェクトマネジメントオフィス)で運用する上での知見を共有する。
1. Project Server / Project Online環境での注意点
エンタープライズ環境(PWA)でタスクを管理している場合、WBSコードやアウトライン構造はサーバー側のチェックイン・チェックアウト制約を受ける。ローカルの `.mpp` ファイル単体であれば今回のコードで完全に制御できるが、サーバー連携時は「ローカルで修復した後に一括チェックインする」フローを徹底させること。
2. Excelや外部DBとのデータ連携時のパイプライン
外部システムからタスク群をインポートする際、Excel側の行順序をそのまま信じてインポートすると、インデント情報(Level)が脱落することが多々ある。
「インポート時は必ずインデントレベルを数値として保持させ、インポート完了直後にこの修復マクロを自動フック(イベントプロシージャ)して走らせる」。このアーキテクチャを構築して初めて、システム間のデータ連携は真に強固なものとなる。
—
総括
WBSの不整合は、単なる「表示の乱れ」ではない。それはプロジェクトの論理構造の崩壊であり、スケジュール遅延の予兆を見えなくする目隠しに他ならない。
手作業による修正という不確実性を排除し、コードによって構造の秩序を強制的に保つこと。これこそが、デジタルの恩恵を最大化し、プロジェクトを勝利へと導くエンジニアリングである。
あなたの手元にあるその荒れたスケジュールファイルを、今すぐこのコードで美しく蘇らせてほしい。
