Sync data and row insertions/deletions on multiple tabs

gravanoc

Active Member
Joined
Oct 20, 2015
Messages
346
Office Version
  1. 365
Platform
  1. Windows
  2. Mobile
Workbook can be downloaded here.

Scenario: 10 people are using a shared workbook. There are 10 tabs, of which 9 are considered Biller tabs & 1 is the Master tab. The Master tab is just the aggregate of all the Biller tabs, minus the extra header rows. All tabs are using a giant table for all the input data.
The intended task list (not in any order) is:

1) Any of the 9 biller agents enter data on their respective tab, and this is automatically synced to the Master.
2) Likewise, if the Master tab updates something on its tab that is within a specific biller's domain, then that biller tab will sync the new data in the correct cell.

The above steps are not a problem AFAIK, and seem to work fine with my code. The next steps are what have been a problem.

3) Either the Master or a biller tab insert a row. This can happen a few different ways.
a) The Insert Row command
b) Placing cursor in first non-table, blank cell that is underneath last row with data in it, then inputting some data will automatically create a new row.
c) Pressing the Tab key when the cursor is in the last column of the last row of table data, thereby creating a new row.
Regardless how it is done, the counterpart tab must sync and add the row in the correct place. This happens sometimes, but has random failures that I haven't yet pinpointed.

4) Not as important, but deleting rows and having that mirror would also be nice.

Unless someone would like to take a look at my workbook, I will just post the code here. I've tried to remove steps that aren't relevant, but it is very segmented.

The first module runs after workbook is opened and the procedure is called from there. One problem with syncing inserted/deleted rows is knowing how many to add or delete at a time (even though in practice it should just be one).
VBA Code:
Public Sub InitSettings()

    Dim ws(0 To 9) As Worksheet
    Dim rg(1 To 9) As Range
    Dim i As Long
   
    EventStop
    FindLastCell

    For i = 0 To 9
        Set ws(i) = Sheets(Sheets(i + 1).Name)
        oldRowCount(i) = ws(i).UsedRange.Rows.Count
    Next

    EventStart

End Sub

The above also calls EventStop (obvious reasons) procedure & FindLastCell, which does that on each tab to help calculate and reform UsedRange row count.

VBA Code:
Sub FindLastCell()

    Dim ws(0 To 9) As Worksheet
    Dim lCell(0 To 9) As Range
   
    For i = 0 To 9
   
        Set ws(i) = Sheets(Sheets(i + 1).Name)
        Set lCell(i) = ws(i).Range("D1048576").End(xlUp).offset(0, 18)
        MakeLastCell ws(i), lCell(i)
       
    Next
   
    UsedRangeRowCount

End Sub

Sub MakeLastCell(ByVal Sht As Worksheet, lCell As Range)
'David McRitchie, http://www.mvps.org/dmcritchie/excel/lastcell.htm
    Dim x As Integer
    Dim str As String
    Dim xlong As Long, clong As Long, rlong As Long
   
    On Error GoTo 0

    str = lCell.Address
    Sht.Range(lCell.row + 1 & ":" & Sht.Cells.Rows.Count).Delete
    xlong = Sht.UsedRange.Rows.Count 'Filters can interfere
   
    Sht.Range(Sht.Cells(1, lCell.Column + 1), _
    Sht.Cells(Sht.Cells.Rows.Count, Sht.Cells.Columns.Count)).Delete
   
    Beep
   
    xlong = Sht.UsedRange.Rows.Count + Sht.UsedRange.Columns.Count 'Filters can interfere
    rlong = Sht.Cells.SpecialCells(xlLastCell).row
    clong = Sht.Cells.SpecialCells(xlLastCell).Column
    If rlong <= lCell.row And clong <= lCell.Column Then Exit Sub
    ActiveWorkbook.Save
    xlong = Sht.UsedRange.Rows.Count + Sht.UsedRange.Columns.Count 'Filters can interfere
    rlong = Sht.Cells.SpecialCells(xlLastCell).row
    clong = Sht.Cells.SpecialCells(xlLastCell).Column
    If rlong <= lCell.row And clong <= lCell.Column Then Exit Sub
    MsgBox "Sorry, Have failed to make " & str & " your last cell"
   
End Sub

Public Sub UsedRangeRowCount()

    Dim ws(0 To 9) As Worksheet
    Dim rg(1 To 9) As Range
    Dim i As Long

    For i = 0 To 9

        Set ws(i) = Sheets(Sheets(i + 1).Name)
        oldRowCount(i) = ws(i).UsedRange.Rows.Count

    Next

End Sub

After finding the last cell, the second procedure destroys excess UsedRange rows & columns so that my count is accurate.
That's the initialization, but it also runs after other triggers as will be apparent in the following.

VBA Code:
Private Sub Workbook_SheetChange(ByVal Sh As Object, ByVal Target As Range)
   
    EventStop

    Brancher Target

    EventStart

End Sub


Public Sub Brancher(ByVal Target As Range)

    Dim allSht(0 To 9) As Worksheet
    Dim newRowCount(0 To 9) As Long
    Dim i As Long
    Dim rowIns As Long
    Dim insOnly As Boolean
    Dim addBiller As Boolean
   
    Set allSht(0) = Sheets("Master")
    insOnly = False

    For i = 0 To 9

        Set allSht(i) = Sheets(Sheets(i + 1).Name)
        newRowCount(i) = allSht(i).UsedRange.Rows.Count

    Next

    For i = 0 To 9

        If newRowCount(i) <> oldRowCount(i) Then

            rowIns = newRowCount(i) - oldRowCount(i)

        End If

    Next


    If rowIns < 0 Then insOnly = True

    If Target.Parent Is allSht(0) Then

'         AuxUpdate updates biller tabs
        oldRowCount(Target.Parent.Index) = newRowCount(Target.Parent.Index) + rowIns
        AuxUpdate allSht(0), Target, rowIns, insOnly, addBiller

    Else

'         MasterUpdate updates only the Master tab
        If Target.Column = 1 And Target.Cells.Count < 1000 Then

            addBiller = True
            Target.offset(0, 3).Value = Target.Parent.Name

        End If
        oldRowCount(0) = newRowCount(0) + rowIns
        MasterUpdate allSht(0), Target, rowIns, insOnly, addBiller

    End If

End Sub

Public Sub MasterUpdate(ByVal mSht As Worksheet, ByVal Target As Range, rowIns As Long, insOnly As Boolean, addBiller As Boolean)

    Dim tSht As Worksheet
   
    Dim outRng(0 To 8) As Range
    Dim outputRng As Range
   
    Dim row As Long
    Dim col As Long
    Dim offset As Long
    Dim rowCh As Long
   
    Dim colLet As String
    Dim nameStr(0 To 8) As String
   
    Set tSht = Target.Parent
   
    For i = 0 To 8
   
        nameStr(i) = Sheets(i + 2).Name
       
        If i = 0 Then
       
            Set outRng(i) = mSht.Range("D2")
           
        Else
       
            Set outRng(i) = mSht.Cells.Find(What:=nameStr(i), after:=outRng(i - 1), LookIn:=xlFormulas, lookat:= _
                    xlPart, searchorder:=xlByRows, searchdirection:=xlNext, MatchCase:=False)
   
        End If
       
    Next
   
    row = Target.row
    col = Target.Column
    colLet = Number2Letter(col)
   
    Select Case tSht.Name
   
        Case nameStr(0)
       
            offset = row + outRng(0).row
           
        Case nameStr(1)

            offset = row + outRng(1).row
           
        Case nameStr(2)
       
            offset = row + outRng(2).row
       
        Case nameStr(3)
       
            offset = row + outRng(3).row
       
        Case nameStr(4)
       
            offset = row + outRng(4).row
       
        Case nameStr(5)
       
            offset = row + outRng(5).row
       
        Case nameStr(6)
       
            offset = row + outRng(6).row
       
        Case nameStr(7)
       
            offset = row + outRng(7).row
       
        Case nameStr(8)
       
            offset = row + outRng(8).row

    End Select
   
    offset = offset - 2
    Set outputRng = mSht.Range(colLet & (offset))
    rowCh = rowIns
   
    Do While rowIns > 0
       
        outputRng.EntireRow.Insert Shift:=xlUp
        rowIns = rowIns - 1

    Loop
   
    Do While rowIns < 0
       
        outputRng.EntireRow.Delete Shift:=xlUp
        rowIns = rowIns + 1
       
    Loop

    If insOnly Then GoTo EndSec

    If Target.Cells.Count > 1 Then
   
        If Target.Cells.Count > 1000 Then Exit Sub
       
        c = Target.Columns.Count
        r = Target.Rows.Count
       
       
        For i = 1 To r
       
            For j = 1 To c
           
                outputRng.Cells(i, j).Value = Target.Cells(i, j).Value
               
            Next
           
        Next
   
    Else
       
        Set outputRng = outputRng.offset(-rowCh, 0)
        outputRng.Value = Target.Value
        If addBiller Then
       
            mSht.Range("D" & outputRng.row).Value = tSht.Name
            FindLastCell
           
        End If
       
    End If
   
EndSec:
   
End Sub


Public Sub AuxUpdate(ByVal mSht As Worksheet, ByVal Target As Range, rowIns As Long, insOnly As Boolean, addBiller As Boolean)

    Dim outSht As Worksheet
   
    Dim outRng(0 To 8) As Range
   
    Dim row As Long
    Dim col As Long
    Dim offset As Long
    Dim rowCh As Long
   
    Dim colLet As String
    Dim shtName As String
    Dim nameStr(0 To 8) As String

    For i = 0 To 8
   
        nameStr(i) = Sheets(i + 2).Name
       
        If i = 0 Then
       
            Set outRng(i) = mSht.Range("D2")
           
        Else
       
            Set outRng(i) = mSht.Cells.Find(What:=nameStr(i), after:=outRng(i - 1), LookIn:=xlFormulas, lookat:= _
                    xlPart, searchorder:=xlByRows, searchdirection:=xlNext, MatchCase:=False)
   
        End If
       
    Next

    row = Target.row
   
    Select Case row
   
        Case Is < outRng(1).row
       
            shtName = nameStr(0)
           
        Case Is < outRng(2).row
       
            shtName = nameStr(1)
           
        Case Is < outRng(3).row
       
            shtName = nameStr(2)
           
        Case Is < outRng(4).row
       
            shtName = nameStr(3)
           
        Case Is < outRng(5).row
       
            shtName = nameStr(4)
           
        Case Is < outRng(6).row
       
            shtName = nameStr(5)
           
        Case Is < outRng(7).row
       
            shtName = nameStr(6)
           
        Case Is < outRng(8).row
       
            shtName = nameStr(7)
           
        Case Else
       
            shtName = nameStr(8)
           
    End Select
   
'    shtName = mSht.Range("D" & row).Value
'    If shtName = "" Then shtName = mSht.Range("D" & row).offset(1, 0).Value
    Set outSht = Sheets(shtName)
   
    col = Target.Column
    colLet = Number2Letter(col)
   
    Select Case outSht.Name
   
        Case nameStr(0)
       
            offset = row - outRng(0).row
           
        Case nameStr(1)

            offset = row - outRng(1).row
           
        Case nameStr(2)
       
            offset = row - outRng(2).row
       
        Case nameStr(3)
       
            offset = row - outRng(3).row
       
        Case nameStr(4)
       
            offset = row - outRng(4).row
       
        Case nameStr(5)
       
            offset = row - outRng(5).row
       
        Case nameStr(6)
       
            offset = row - outRng(6).row
       
        Case nameStr(7)
       
            offset = row - outRng(7).row
       
        Case nameStr(8)
       
            offset = row - outRng(8).row
             
    End Select
   
    offset = offset + 2
   
    Set outputRng = outSht.Range(colLet & (offset))
    rowCh = rowIns
   
     Do While rowIns > 0
       
        outputRng.EntireRow.Insert Shift:=xlUp
        rowIns = rowIns - 1
       
    Loop
   
    Do While rowIns < 0
       
        outputRng.EntireRow.Delete Shift:=xlUp
        rowIns = rowIns + 1
       
    Loop

    If insOnly Then GoTo EndSec

    If Target.Cells.Count > 1 Then
   
        ' Probably inserted an entire row, so end sub
        If Target.Cells.Count > 1000 Then GoTo EndSec
   
        c = Target.Columns.Count
        r = Target.Rows.Count
       
       
        For i = 1 To r
       
            For j = 1 To c
           
                outputRng.Cells(i, j).Value = Target.Cells(i, j).Value
               
            Next
           
        Next
   
    Else
       
        Set outputRng = outputRng.offset(-rowCh, 0)
        outputRng.Value = Target.Value
       
    End If
   
EndSec:

End Sub

Anyway, the brancher procedure determines which other procedure to run, depending on whether the Master or a biller tab needs updating. Most of the code within either the MasterUpdate or AuxUpdate is similar, intended to keep track of row insertions/deletions, and also to keep my counts accurate for future iterations involving oldRowCount or newRowCount (derived from UsedRange).

That, I believe, is the most relevant parts. I will upload my workbook to my Drive as well.

Workbook can be downloaded here.
 

Excel Facts

Best way to learn Power Query?
Read M is for (Data) Monkey book by Ken Puls and Miguel Escobar. It is the complete guide to Power Query.

Forum statistics

Threads
1,214,427
Messages
6,119,419
Members
448,895
Latest member
omarahmed1

We've detected that you are using an adblocker.

We have a great community of people providing Excel help here, but the hosting costs are enormous. You can help keep this site running by allowing ads on MrExcel.com.
Allow Ads at MrExcel

Which adblocker are you using?

Disable AdBlock

Follow these easy steps to disable AdBlock

1)Click on the icon in the browser’s toolbar.
2)Click on the icon in the browser’s toolbar.
2)Click on the "Pause on this site" option.
Go back

Disable AdBlock Plus

Follow these easy steps to disable AdBlock Plus

1)Click on the icon in the browser’s toolbar.
2)Click on the toggle to disable it for "mrexcel.com".
Go back

Disable uBlock Origin

Follow these easy steps to disable uBlock Origin

1)Click on the icon in the browser’s toolbar.
2)Click on the "Power" button.
3)Click on the "Refresh" button.
Go back

Disable uBlock

Follow these easy steps to disable uBlock

1)Click on the icon in the browser’s toolbar.
2)Click on the "Power" button.
3)Click on the "Refresh" button.
Go back
Back
Top