excelvba

Excel VBA- Delete Multiple Cells in a Table for Tracking Changes


The following code works so long as you only delete one cell at a time. If I select multiple cells at hit delete, I get an error 1004 Method 'Undo' of object '_Application' failed.

NOTE: This is an extension of Excel VBA- Loop Through Target Cells in a Table for Tracking Changes

Private Sub Worksheet_Change(ByVal Target As Range)

    Dim wsLog As Worksheet, oList1 As ListObject, oList2 As ListObject
    Dim rng As Range, c As Range, sCol As String
    Dim oldValue, sTo As String, sFrom As String
    Dim r As Long
    
    On Error GoTo myerror
    Application.EnableEvents = False
   
    Set oList1 = Me.ListObjects(1)
    Set oList2 = Me.ListObjects(2)
    Set rng = Union(oList2.DataBodyRange.Columns("A:C"), _
                    oList2.DataBodyRange.Columns("E:AR"))
   
    Set wsLog = ThisWorkbook.Sheets("Change Log")
    With wsLog
        r = .Cells(.Rows.Count, 2).End(xlUp).Row
        
        For Each c In Target
            If Intersect(c, rng) Is Nothing Then
                ' do nothing
                
            Else
                ' column header
                If c.Column - oList2.DataBodyRange.Column + 1 <= 3 Then
                    sCol = Intersect(c.EntireColumn, oList2.HeaderRowRange).Value
                    
                Else
                    sCol = Intersect(c.EntireColumn, oList1.DataBodyRange.Rows(1)).Value
                End If
               
               
                Application.Undo
                oldValue = c
                Application.Undo
                
                
                ' so that empty values are easier to read versus **** in .Cells(r, 8)
                If c.Value = "" Then
                    sTo = "EMPTY"
                    sFrom = oldValue
                    
                ElseIf oldValue = 0 Then
                   sTo = c.Value
                   sFrom = "EMPTY"
                   
                ElseIf oldValue <> 0 Then
                   sTo = c.Value
                   sFrom = oldValue
                End If
                
                ' prevent logging changes when deleting empty cell
                If sTo = "EMPTY" And sFrom = "" Then
                    'do nothing
                    
                Else
                    'log it
                    r = r + 1
                    .Cells(r, 2) = Me.Name
                    .Cells(r, 3) = sCol ' column name
                    .Cells(r, 4) = Environ("username")
                    .Cells(r, 5) = Format(Now(), "DD MMMM")
                    If c.Column - oList2.DataBodyRange.Column + 1 <= 1 Then
                        .Cells(r, 6) = sTo
                        .Cells(r, 6).NumberFormat = "m/d/yyyy"
                    
                    Else
                        .Cells(r, 6) = sTo
                        .Cells(r, 6).NumberFormat = "$#,##0.00_);($#,##0.00)"
                    End If
                    .Cells(r, 7) = sFrom
                    .Cells(r, 8) = "Was changed to **" & sTo & "** from **" _
                        & sFrom & "** by " & Environ("username") & " on" & " " & _
                        Format(Now(), "DD MMMM, YYYY @ H:MM:ss")
                End If
            End If
            
        Next
        
        .Columns("B:H").AutoFit
    End With
    
myerror:
    Application.EnableEvents = True
    If Err.Number Then MsgBox Err.Number & " " & Err.Description
    
End Sub

I discovered the issue is the following part of the code:

                    If c.Column - oList2.DataBodyRange.Column + 1 <= 1 Then
                        .Cells(r, 6) = sTo
                        .Cells(r, 6).NumberFormat = "m/d/yyyy"
                    
                    Else
                        .Cells(r, 6) = sTo
                        .Cells(r, 6).NumberFormat = "$#,##0.00_);($#,##0.00)"
                    End If

If I remove the If statement then I can delete multiple cells at once.

                    'log it
                    r = r + 1
                    .Cells(r, 2) = Me.Name
                    .Cells(r, 3) = sCol ' column name
                    .Cells(r, 4) = Environ("username")
                    .Cells(r, 5) = Format(Now(), "DD MMMM")
                    .Cells(r, 6) = sTo
                    .Cells(r, 7) = sFrom
                    .Cells(r, 8) = "Was changed to **" & sTo & "** from **" _
                        & sFrom & "** by " & Environ("username") & " on" & " " & _
                        Format(Now(), "DD MMMM, YYYY @ H:MM:ss")

The whole reason I want to add this If statement is because the first column in oList2 are dates so if the change occurs in column 1, I want the before/ after changes to be formatted in a date. All other changes should be formatted as currency.


Solution

  • Change the format for cells in column 6 after the For Each c In Target loop.

    With wsLog
            Dim rStart As Long, rEnd As Long
            r = .Cells(.Rows.Count, 2).End(xlUp).Row
            rStart = r + 1
            
            For Each c In Target
                If Intersect(c, rng) Is Nothing Then
                    ' do nothing
                Else
                
                    ' column header
                    If c.Column - oList2.DataBodyRange.Column + 1 <= 3 Then
                        sCol = Intersect(c.EntireColumn, oList2.HeaderRowRange).Formula
                    Else
                         sCol = Intersect(c.EntireColumn, oList1.DataBodyRange.Rows(1)).Formula
                    End If
                   
                    Application.Undo
                    oldvalue = c
                    Application.Undo
                    
                    If c.Value = "" Then
                        sTo = "EMPTY"
                        sFrom = oldvalue
                    ElseIf oldvalue = 0 Then
                       sTo = c.Value
                       sFrom = "EMPTY"
                    ElseIf oldvalue <> 0 Then
                       sTo = c.Value
                       sFrom = oldvalue
                    End If
                    
                    'log it
                    r = r + 1
                    .Cells(r, 2) = Me.Name
                    .Cells(r, 3) = sCol ' column name
                    .Cells(r, 4) = Environ("username")
                    .Cells(r, 5) = Format(Now(), "DD MMMM")
                    .Cells(r, 6) = sTo
                    .Cells(r, 7) = sFrom
                    .Cells(r, 8) = "Was changed to **" & sTo & "** from **" _
                            & sFrom & "** by " & Environ("username") & " on" & " " & _
                            Format(Now(), "DD MMMM, YYYY @ H:MM:ss")
                    .Cells(r, 9) = c.Column - oList2.DataBodyRange.Column + 1
                 
                End If
            Next
            rEnd = r
            
            ' change format
            For r = rStart To rEnd
                If .Cells(r, 9) <= 1 Then
                    .Cells(r, 6).NumberFormat = "m/d/yyyy"
                Else
                    .Cells(r, 6).NumberFormat = "$#,##0.00_);($#,##0.00)"
                End If
                .Cells(r, 9).Clear
            Next
            
            .Columns("B:F").AutoFit
        End With