How to combine/copy multiple workbooks into one workbook.

How to combine/copy multiple workbooks into one workbook.

A lab billing macro example

Here is another useful macro for copying data from multiple workbooks into one workbook. At the end of the month billing needs a report with all the months charges listed. We have been creating a workbook daily with the day’s charges listed. Now we have to collate all those workbooks into one workbook. Again it’s a tedious process in need of automation.

To work this macro needs a sheet to copy to. I take the easy road and create a workbook name MasterWorkbook. I run the macro and it copies all the month’s workbooks to the MasterWorkbook. Then I save MasterWorkbook with a new name denoting the month. this way the original remains blank for next month. To work all the workbooks/sheets need to be in one folder. It copies all data from all workbooks in that folder to one.

How to get a list of workbooks in a folder:

We have gathered all the month’s files in one folder. The first step is to get a list of all the excel workbooks in this folder.

Sub AggregateReports()
    'Get a folder full of reports and put them all into one sheet.
    'Get the folder from user.
    On Error GoTo ErrorHandler

    Dim fldr As FileDialog
    Dim strFolder As String
    Set fldr = Application.FileDialog(msoFileDialogFolderPicker)
    With fldr
        .Title = "Select the Folder where all the daily reports reside"
        .AllowMultiSelect = False
        .InitialFileName = strPath
        If .Show <> 0 Then
            strFolder = .SelectedItems(1)
        Else 'Cancelled
            strFolder = ""
        End If
    End With

    Debug.Print "Folder was: " & strFolder
    Application.ScreenUpdating = False

We have the folder name now we need to get a list of only the excel files within that folder. We do this with the old Dir command. If you recall in MS-Dos dir used to list all the files in a folder. It’s almost the same in vba, except Dir returns the name of the first file and must be called again to get the next.

    'Get all filenames within folder.
    Dim Files() As String
    ReDim Files(-1 To -1)
    ThisWB = ThisWorkbook.Name
    Filename = Dir(strFolder & "\*.xls", vbNormal)
    Do Until Filename = ""
        If Filename <> ThisWB Then
            If UBound(Files) = -1 Then
                ReDim Files(0 To 0)
                If Filename = "" Then
                Else
                    Files(0) = Filename
                End If
            Else
                ReDim Preserve Files(0 To UBound(Files) + 1)
                Files(UBound(Files)) = Filename
            End If

            Debug.Print "Added: " & Filename
        Else
            Debug.Print "Excluded this workbook: " & Filename
        End If
        Filename = Dir()
    Loop

We now have a list of files but sadly they are in no particular order. A simple sort of the files will ensure that when we loop through them (as long as you have named them all in the same format with a date) the days will get copied in order.

How to do a quick sort in excel vba:

Private Sub QuickSort(strArray() As String, intBottom As Integer, intTop As Integer)
    Dim strPivot As String, strTemp As String
    Dim intBottomTemp As Integer, intTopTemp As Integer

    intBottomTemp = intBottom
    intTopTemp = intTop

    strPivot = strArray((intBottom + intTop) \ 2)

    While (intBottomTemp <= intTopTemp)

        'comparison of the values is a descending sort
        While (strArray(intBottomTemp) < strPivot And intBottomTemp < intTop)
            intBottomTemp = intBottomTemp + 1
        Wend

        While (strPivot < strArray(intTopTemp) And intTopTemp > intBottom)
            intTopTemp = intTopTemp - 1
        Wend

        If intBottomTemp < intTopTemp Then
            strTemp = strArray(intBottomTemp)
            strArray(intBottomTemp) = strArray(intTopTemp)
            strArray(intTopTemp) = strTemp
        End If

        If intBottomTemp <= intTopTemp Then
            intBottomTemp = intBottomTemp + 1
            intTopTemp = intTopTemp - 1
        End If

    Wend

    'the function calls itself until everything is in good order
    If (intBottom < intTopTemp) Then QuickSort strArray, intBottom, intTopTemp
    If (intBottomTemp < intTop) Then QuickSort strArray, intBottomTemp, intTop

End Sub

This is a great and simple quicksort function but how do we use it? It’s simple:

    'NOTE: We are still in sub AggregateReports here:
    'Files come in all willy nilly, sort them by date
    QuickSort Files, LBound(Files), UBound(Files)

    Dim intX As Integer
    For intX = LBound(Files) To UBound(Files)
        Debug.Print "After Sort: " & Files(intX)
    Next intX

Now we have our list of files we just need to loop through it and copy them to the MasterWorkbook:

    'NOTE: we are still in sub AggregateReports here:
    Dim wkb As Workbook
    Dim intFinalRow As Integer
    Dim intRow As Integer
    Dim blnHeaderDone As Boolean

    For intX = LBound(Files) To UBound(Files)
            Set wkb = Workbooks.Open(Filename:=strFolder & "\" & Files(intX))
            wkb.Activate
            With wkb.Sheets(1)
                Set LastCell = .Cells.SpecialCells(xlCellTypeLastCell)
                .Activate
                intFinalRow = Range("A65536").End(xlUp).Row  '
                For intRow = 2 To intFinalRow
                    If blnHeaderDone = False Then
                        'We only want to copy the header row once so we do it here and set variable to True
                        Rows(intRow - 1).EntireRow.Copy (Sheets(1).Range("a65536").End(xlUp).Offset(0, 0))
                        blnHeaderDone = True
                    End If
                    Rows(intRow).EntireRow.Copy (Sheets(1).Range("a65536").End(xlUp).Offset(1, 0))
                    If intRow = 2 Then 'This add's a 'Begin' comment to the first cell of that day.
                        Sheets(1).Range("a65536").End(xlUp).Offset(0, 7).AddComment ("Day " & CStr(intX) & " Begin: " & Files(intX))
                    End If
                    If intRow = intFinalRow Then 'Add end
                        Sheets(1).Range("a65536").End(xlUp).Offset(0, 7).AddComment ("Day " & CStr(intX) & " End: " & Files(intX))
                    End If

                Next intRow

            End With
            wkb.Close False

    Next intX 'next file.
    Application.ScreenUpdating = True

    Exit Sub
ErrorHandler:
    Debug.Print Err.Number & " " & Err.Description & " " & Erl
    MsgBox Err.Number & " " & Err.Description & " " & Erl
End Sub

And voila! Another 30 minutes saved!

You can download the workbook with macro here: MasterWorkbook

Leave a Reply

Your email address will not be published. Required fields are marked *