- BuildEbitdaBridge.bas: waterfall bridge tab (2026E->2027 AOP) from Slide 13 - clean_names_xml.py: strip junk defined names via direct XML surgery (Excel save corrupts this workbook's query tables/pivot caches) - CleanDefinedNames.bas: skip all _xl* reserved names; copy survivors to clipboard Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
180 lines
7.6 KiB
QBasic
180 lines
7.6 KiB
QBasic
Attribute VB_Name = "BuildEbitdaBridge"
|
|
Option Explicit
|
|
|
|
' ============================================================
|
|
' BuildEbitdaBridge
|
|
' Builds an EBITDA bridge (waterfall) tab walking Growscape
|
|
' Management Adj. EBITDA from 2026E (Slide 13 col Q) to
|
|
' 2027 AOP (Slide 13 col V), decomposed by P&L driver line.
|
|
'
|
|
' Bars are LIVE formulas referencing 'Slide 13', so the tab
|
|
' updates on recalc. Stacked-column technique (Base/Down/Up/
|
|
' Total) for version-proof formatting. Re-runnable: rebuilds
|
|
' the sheet each time.
|
|
'
|
|
' EBITDA(42) = NetSales(23) - Materials(25) - Labor(26)
|
|
' - VarOH(27) - Fixed(31) - Distribution(35)
|
|
' - Freight(36) - SG&A(40) - Other(41)
|
|
' Bar = +d(NetSales) ; -d(each cost). Sum = V42 - Q42.
|
|
' ============================================================
|
|
Sub BuildEbitdaBridge()
|
|
Const SRC As String = "Slide 13" ' source P&L tab
|
|
Const OUT As String = "EBITDA Bridge" ' output tab
|
|
Const C1 As String = "Q" ' 2026E (start) column on SRC
|
|
Const C2 As String = "V" ' 2027 AOP (end) column on SRC
|
|
|
|
Dim wb As Workbook: Set wb = ThisWorkbook
|
|
Dim ws As Worksheet
|
|
|
|
' --- guard: source must exist ---
|
|
On Error Resume Next
|
|
Set ws = wb.Worksheets(SRC)
|
|
On Error GoTo 0
|
|
If ws Is Nothing Then
|
|
MsgBox "Sheet '" & SRC & "' not found.", vbExclamation: Exit Sub
|
|
End If
|
|
|
|
' --- (re)create output sheet ---
|
|
Application.DisplayAlerts = False
|
|
On Error Resume Next
|
|
wb.Worksheets(OUT).Delete
|
|
On Error GoTo 0
|
|
Application.DisplayAlerts = True
|
|
Set ws = wb.Worksheets.Add(After:=wb.Worksheets(wb.Worksheets.Count))
|
|
ws.Name = OUT
|
|
|
|
' --- driver definition: label, SRC row, sign ( +1 sales, -1 cost ) ---
|
|
Dim lbl() As Variant, rw() As Variant, sgn() As Variant
|
|
lbl = Array("Net Sales", "Materials", "Labor", "Var Overhead", _
|
|
"Fixed Costs", "Distribution", "Freight", "SG&A", "Other")
|
|
rw = Array(23, 25, 26, 27, 31, 35, 36, 40, 41)
|
|
sgn = Array(1, -1, -1, -1, -1, -1, -1, -1, -1)
|
|
|
|
' --- header ---
|
|
ws.Range("B2").Value = "EBITDA Bridge - 2026E to 2027 AOP"
|
|
ws.Range("B2").Font.Bold = True: ws.Range("B2").Font.Size = 14
|
|
ws.Range("B3").Value = "Growscape, Management Adj. EBITDA ($ in 000s)"
|
|
ws.Range("B3").Font.Italic = True
|
|
|
|
Dim hRow As Long: hRow = 5
|
|
ws.Cells(hRow, 2).Value = "Step"
|
|
ws.Cells(hRow, 3).Value = "Value"
|
|
ws.Cells(hRow, 4).Value = "Base"
|
|
ws.Cells(hRow, 5).Value = "Decrease"
|
|
ws.Cells(hRow, 6).Value = "Increase"
|
|
ws.Cells(hRow, 7).Value = "Total"
|
|
ws.Cells(hRow, 8).Value = "Cumulative"
|
|
ws.Range(ws.Cells(hRow, 2), ws.Cells(hRow, 8)).Font.Bold = True
|
|
|
|
Dim r As Long, i As Long, prevH As Long
|
|
Dim firstData As Long, lastData As Long
|
|
firstData = hRow + 1
|
|
|
|
' ---- START anchor: 2026E EBITDA (Q42) ----
|
|
r = firstData
|
|
ws.Cells(r, 2).Value = "2026E EBITDA"
|
|
ws.Cells(r, 3).Formula = "='" & SRC & "'!" & C1 & "42"
|
|
ws.Cells(r, 4).Value = 0 ' Base
|
|
ws.Cells(r, 5).Value = 0 ' Decrease
|
|
ws.Cells(r, 6).Value = 0 ' Increase
|
|
ws.Cells(r, 7).Formula = "=C" & r ' Total
|
|
ws.Cells(r, 8).Formula = "=C" & r ' Cumulative
|
|
|
|
' ---- driver steps ----
|
|
For i = LBound(lbl) To UBound(lbl)
|
|
r = firstData + 1 + i
|
|
prevH = r - 1
|
|
ws.Cells(r, 2).Value = lbl(i)
|
|
' delta contribution to EBITDA, signed
|
|
If sgn(i) = 1 Then
|
|
ws.Cells(r, 3).Formula = "='" & SRC & "'!" & C2 & rw(i) & "-'" & SRC & "'!" & C1 & rw(i)
|
|
Else
|
|
ws.Cells(r, 3).Formula = "=-('" & SRC & "'!" & C2 & rw(i) & "-'" & SRC & "'!" & C1 & rw(i) & ")"
|
|
End If
|
|
ws.Cells(r, 4).Formula = "=IF(C" & r & ">=0,H" & prevH & ",H" & prevH & "+C" & r & ")" ' Base
|
|
ws.Cells(r, 5).Formula = "=IF(C" & r & "<0,-C" & r & ",0)" ' Decrease
|
|
ws.Cells(r, 6).Formula = "=IF(C" & r & ">=0,C" & r & ",0)" ' Increase
|
|
ws.Cells(r, 7).Value = 0 ' Total
|
|
ws.Cells(r, 8).Formula = "=H" & prevH & "+C" & r ' Cumulative
|
|
Next i
|
|
|
|
' ---- END anchor: 2027 AOP EBITDA (V42) ----
|
|
r = r + 1
|
|
lastData = r
|
|
ws.Cells(r, 2).Value = "2027 AOP EBITDA"
|
|
ws.Cells(r, 3).Formula = "='" & SRC & "'!" & C2 & "42"
|
|
ws.Cells(r, 4).Value = 0
|
|
ws.Cells(r, 5).Value = 0
|
|
ws.Cells(r, 6).Value = 0
|
|
ws.Cells(r, 7).Formula = "=C" & r
|
|
ws.Cells(r, 8).Formula = "=C" & r
|
|
|
|
' ---- tie-out check: cumulative before end anchor must equal V42 ----
|
|
ws.Cells(lastData + 2, 2).Value = "Tie-out (should be 0):"
|
|
ws.Cells(lastData + 2, 3).Formula = "=H" & (lastData - 1) & "-C" & lastData
|
|
ws.Cells(lastData + 2, 2).Font.Italic = True
|
|
|
|
' ---- number formats ----
|
|
ws.Range(ws.Cells(firstData, 3), ws.Cells(lastData, 8)).NumberFormat = "#,##0;(#,##0)"
|
|
ws.Cells(lastData + 2, 3).NumberFormat = "#,##0;(#,##0)"
|
|
ws.Columns("B").ColumnWidth = 18
|
|
ws.Columns("C:H").ColumnWidth = 11
|
|
|
|
' ============================================================
|
|
' Chart: stacked column waterfall
|
|
' ============================================================
|
|
Dim co As ChartObject, ch As Chart
|
|
Set co = ws.ChartObjects.Add(Left:=ws.Cells(5, 10).Left, Top:=ws.Cells(5, 10).Top, _
|
|
Width:=620, Height:=340)
|
|
Set ch = co.Chart
|
|
ch.ChartType = xlColumnStacked
|
|
|
|
Dim catRng As Range, baseRng As Range, decRng As Range, incRng As Range, totRng As Range
|
|
Set catRng = ws.Range(ws.Cells(firstData, 2), ws.Cells(lastData, 2))
|
|
Set baseRng = ws.Range(ws.Cells(firstData, 4), ws.Cells(lastData, 4))
|
|
Set decRng = ws.Range(ws.Cells(firstData, 5), ws.Cells(lastData, 5))
|
|
Set incRng = ws.Range(ws.Cells(firstData, 6), ws.Cells(lastData, 6))
|
|
Set totRng = ws.Range(ws.Cells(firstData, 7), ws.Cells(lastData, 7))
|
|
|
|
Do While ch.SeriesCollection.Count > 0
|
|
ch.SeriesCollection(1).Delete
|
|
Loop
|
|
|
|
Dim s As Series
|
|
' 1) Base (invisible riser)
|
|
Set s = ch.SeriesCollection.NewSeries
|
|
s.Name = "Base": s.Values = baseRng: s.XValues = catRng
|
|
s.Format.Fill.Visible = msoFalse: s.Format.Line.Visible = msoFalse
|
|
' 2) Decrease (red)
|
|
Set s = ch.SeriesCollection.NewSeries
|
|
s.Name = "Decrease": s.Values = decRng: s.XValues = catRng
|
|
s.Format.Fill.ForeColor.RGB = RGB(192, 0, 0)
|
|
s.HasDataLabels = True: s.DataLabels.NumberFormat = "#,##0;(#,##0)"
|
|
' 3) Increase (green)
|
|
Set s = ch.SeriesCollection.NewSeries
|
|
s.Name = "Increase": s.Values = incRng: s.XValues = catRng
|
|
s.Format.Fill.ForeColor.RGB = RGB(112, 173, 71)
|
|
s.HasDataLabels = True: s.DataLabels.NumberFormat = "#,##0;(#,##0)"
|
|
' 4) Total (dark blue) — start/end anchors
|
|
Set s = ch.SeriesCollection.NewSeries
|
|
s.Name = "Total": s.Values = totRng: s.XValues = catRng
|
|
s.Format.Fill.ForeColor.RGB = RGB(31, 78, 121)
|
|
s.HasDataLabels = True: s.DataLabels.NumberFormat = "#,##0;(#,##0)"
|
|
|
|
ch.ChartGroups(1).GapWidth = 30
|
|
ch.ChartGroups(1).Overlap = 100
|
|
ch.HasTitle = True
|
|
ch.ChartTitle.Text = "EBITDA Bridge: 2026E to 2027 AOP (Growscape, $000s)"
|
|
ch.HasLegend = False
|
|
|
|
' drop the "Base" out of any axis clutter; keep gridlines light
|
|
On Error Resume Next
|
|
ch.Axes(xlValue).MajorGridlines.Format.Line.ForeColor.RGB = RGB(217, 217, 217)
|
|
On Error GoTo 0
|
|
|
|
ws.Range("B2").Select
|
|
MsgBox "EBITDA Bridge built." & vbCrLf & _
|
|
"Check the tie-out cell (C" & (lastData + 2) & ") = 0." & vbCrLf & _
|
|
"Recalc (Ctrl+Alt+F9) if values look stale.", vbInformation
|
|
End Sub
|