Copy the code below into a regular module in your sample file and run sub Main.
These are the correct formulas IMHIO...
Andreas.
Option Explicit
Option Compare Text
Private Const SubTotalFormula = "=SUBTOTAL(9, @Range)"
Private Const TotalFormula = "=SUM(@Range)"
Sub Main()
Dim F As Range, T As Range
Dim RTotal As Range, OTotal As Range, GTotal As Range
Dim All As Range, Area As Range
Set GTotal = Cells.Find("Grand Total", LookIn:=xlValues, LookAt:=xlWhole)
ClearAllTotals GTotal
'Find the Revenue section
Set F = Range("A:A").Find("Revenue", LookIn:=xlValues, LookAt:=xlPart)
Set T = Range("A:A").Find("Revenue", SearchDirection:=xlPrevious)
Set All = ProcessSections(Range(F, T), RTotal)
MyFillRight All, GTotal
'All other sections are below the Revenue section
Set F = T.Offset(1)
Set T = Range("A" & Rows.Count).End(xlUp)
Set All = ProcessSections(Range(F, T), OTotal)
MyFillRight All, GTotal
'Process Profit/Loss
Const PLFormula = "=@R-@O"
Dim Formula As String
Formula = PLFormula
Formula = Replace(Formula, "@R", RTotal.Address(0, 0))
Formula = Replace(Formula, "@O", OTotal.Address(0, 0))
Set All = T.Offset(, 1)
All.Formula = Formula
MyFillRight All, GTotal
'Process the year totals
Set F = Range("A" & GTotal.Row).End(xlToRight)
Set T = GTotal.Offset(, -1)
Set All = ProcessYears(Range(F, T))
'Process the grand total
For Each Area In Intersect(All.EntireRow, GTotal.EntireColumn)
Area.Formula = Replace(TotalFormula, "@Range", Intersect(All.EntireColumn, Area.EntireRow).Address(0, 0))
Next
End Sub
Private Sub MyFillRight(ByRef All As Range, ByRef Till As Range)
'Just to keep code simple
Dim Area As Range
For Each Area In All.Areas
Area.Resize(, Till.Column - All.Column + 1).FillRight
Next
End Sub
Private Sub MyUnion(ByRef Dest As Range, ByRef This As Range)
'Just to keep code simple
If Dest Is Nothing Then Set Dest = This Else Set Dest = Union(Dest, This)
End Sub
Private Function ProcessSections(ByVal Where As Range, ByRef Total As Range) As Range
Dim SubTotal As Range, SubTotals As Range
Dim FirstAddress As String
Dim F As Range, T As Range, Here As Range
'Find the first sub total row
Set Here = Where.Find("*Total", LookIn:=xlValues, LookAt:=xlWhole)
'Remember this row
FirstAddress = Here.Address
Do
'The start is the same content without "Total" anywhere above
Set F = Where.Find(Trim(Left(Here, Len(Here) - 5)), LookIn:=xlValues, LookAt:=xlWhole, SearchDirection:=xlPrevious)
'Refer to the data cells
Set F = Intersect(Where.Offset(, 1), F.Offset(1).EntireRow)
Set T = Intersect(Where.Offset(, 1), Here.Offset(-1).EntireRow)
Set SubTotal = Intersect(Where.Offset(, 1), Here.EntireRow)
'Create the formula
SubTotal.Formula = Replace(SubTotalFormula, "@Range", Range(F, T).Address(0, 0))
'Remember this cell
MyUnion SubTotals, SubTotal
MyUnion ProcessSections, SubTotal
'Assume a Total below
Set Total = SubTotal.Offset(1)
'Is it there?
If Intersect(Where, Total.EntireRow) Like "Total\*" Then
'Remember this cell
MyUnion ProcessSections, Total
'Create the formula
Total.Formula = Replace(TotalFormula, "@Range", SubTotals.Address(0, 0))
End If
'Find the next sub total row
Set Here = Where.Find("\*Total", Here, LookIn:=xlValues, LookAt:=xlWhole, SearchDirection:=xlNext)
Loop Until Here.Address = FirstAddress
End Function
Private Sub ClearAllTotals(ByRef GrandTotal As Range)
Dim FirstAddress As String
Dim Where As Range, Here As Range, F As Range, T As Range
'Clear all total rows
Set Where = Range("A:A")
Set Here = Where.Find("Total", LookIn:=xlValues, LookAt:=xlPart)
FirstAddress = Here.Address
Do
Set F = Here.Offset(, 1)
Set T = F.Offset(, Columns.Count - F.Column).End(xlToLeft)
If T.Column >= F.Column Then Range(F, T).ClearContents
Set Here = Where.Find("Total", Here, LookIn:=xlValues, LookAt:=xlPart)
Loop Until Here.Address = FirstAddress
'Clear all total columns
Set Where = GrandTotal.EntireRow
Set Here = Where.Find("Total", LookIn:=xlValues, LookAt:=xlPart)
FirstAddress = Here.Address
Do
Set F = Here.Offset(1)
Set T = F.Offset(Rows.Count - F.Row).End(xlUp)
If T.Row >= F.Row Then Range(F, T).ClearContents
Set Here = Where.Find("Total", Here, LookIn:=xlValues, LookAt:=xlPart)
Loop Until Here.Address = FirstAddress
End Sub
Private Function ProcessYears(ByVal Where As Range) As Range
Dim F As Range, T As Range, Here As Range, R As Range, Total As Range
Dim FirstAddress As String
'Find the first year total column
Set Here = Where.Find("*Total", LookIn:=xlValues, LookAt:=xlWhole)
FirstAddress = Here.Address
'The column on the right is the next data cell
Set F = Where.Cells(1)
Do
'In each row
For Each R In Range(Here.Offset(1), Here.Offset(Rows.Count - Here.Row).End(xlUp))
'Skip over our formulas
If R.HasFormula Then GoTo Skip
'Do we have a numbric value on the left side?
If Not IsNumeric(R.Offset(, -1)) Then GoTo Skip
If R.Offset(, -1) = "" Then GoTo Skip
'Yes, this is the place for the formula
Set Total = Intersect(Range(F, Here.Offset(, -1)).EntireColumn, R.EntireRow)
'Remember this cell
MyUnion ProcessYears, R
'Create the formula
R.Formula = Replace(TotalFormula, "@Range", Total.Address(0, 0))
Skip:
Next
'The next column on the right is the next data cell
Set F = Here.Offset(, 1)
'Find the next year total column
Set Here = Where.Find("*Total", Here, LookIn:=xlValues, LookAt:=xlWhole, SearchDirection:=xlNext)
Loop Until Here.Address = FirstAddress
End Function