Forum Discussion

tpayne's avatar
tpayne
Copper Contributor
Jan 06, 2026

Move based on value VBA

I have what I hope to be a fairly easy to solve problem in my VBA code.

I have a tab where each row is a different project and each project has a handful of tasks.  When the project is complete, the entire row moves to a completed projects tab.  My problem is that some of the formulas that make up the tasks update when moved and reference the incorrect cell.  I am trying to copy/paste values before the row moves from one tab to the other to no avail.  The formula that I am really trying to paste the value of is in column D. Could anyone help me out?  Thanks in advance!

 

Dim xRg As Range
    Dim xCell As Range
    Dim A As Long
    Dim B As Long
    Dim C As Long
    A = Worksheets("Deliverables").UsedRange.Rows.Count
    B = Worksheets("Complete").UsedRange.Rows.Count
    If B = 1 Then
       If Application.WorksheetFunction.CountA(Worksheets("Complete").UsedRange) = 0 Then B = 0
    End If
    Set xRg = Worksheets("Deliverables").Range("S1:S" & A)
    On Error Resume Next
    Application.ScreenUpdating = False
    For C = 1 To xRg.Count
        If CStr(xRg(C).Value) = "Complete" Then
                Sheets("Deliverables").Select
            xRg(C).EntireRow.Copy Destination:=Worksheets("Complete").Range("A" & B + 1)
            xRg(C).EntireRow.Delete
            If CStr(xRg(C).Value) = "Complete" Then
                C = C - 1
            End If
            B = B + 1
        End If
    Next
    Application.ScreenUpdating = True
    
    Sheets("Deliverables").Select
'
End Sub

2 Replies

  • VBasic2008's avatar
    VBasic2008
    Copper Contributor

    Move Matching Rows

     

    Sub ArchiveCompleteRecords()
        Const PROC_NAME As String = "ArchiveCompleteRecords"
        
        ' Here you choose whether to copy only values for the whole rows (True)
        ' or to copy using the Copy method as you did before and subsequently copy
        ' the value in column 'D' (False).
        Const OK_TO_COPY_VALUES_ONLY As Boolean = False
        
        ' Here you choose whether to delete records (True) or not (False).
        ' The latter is useful while developing and testing the code.
        Const OK_TO_DELETE_RECORDS As Boolean = False
        
        ' Reference the workbook.
        Dim wb As Workbook: Set wb = ThisWorkbook ' workbook containing this code
        ' If it isn't, reference it by its name or use 'ActiveWorkbook' instead.
        
        ' Reference the source sheet.
        Dim sws As Worksheet: Set sws = wb.Sheets("Deliverables")
        
        ' Reference the source range.
        Dim srg As Range
        With sws.UsedRange
            Set srg = sws.Range("A2", .Cells(.Cells.CountLarge))
        End With
        
        ' Retrieve the number of columns (the same for source and destination).
        Dim ColumnsCount As Long: ColumnsCount = srg.Columns.Count
        
        ' Reference the destination sheet.
        Dim dws As Worksheet: Set dws = wb.Sheets("Complete")
        
        ' Reference the last populated destination row range resized
        ' to the number of columns in the source range.
        Dim drrg As Range
        With dws.UsedRange
            Set drrg = dws.Range("A1").Offset(.Rows.Count - 1) _
                .Resize(, ColumnsCount)
        End With
        
        ' Turn off settings.
        Application.ScreenUpdating = False
        
        ' Declare variables.
        Dim surg As Range, srrg As Range, scell As Range, dRowOffset As Long
        
        ' Loop through the rows of the source range.
        For Each srrg In srg.Rows
            ' Reference the criteria cell.
            Set scell = srrg.Columns("S")
            ' Check if the value matches the criteria.
            If CStr(scell.Value) = "Complete" Then
                ' Adjust the next available destination row.
                dRowOffset = dRowOffset + 1 ' next row
                ' Copy.
                If OK_TO_COPY_VALUES_ONLY Then
                    ' Copy values only.
                    drrg.Offset(dRowOffset).Value = srrg.Value
                Else
                    ' Copy as you did before and copy the value of column 'D'.
                    srrg.Copy Destination:=drrg
                    drrg.Columns("D").Value = srrg.Columns("D").Value
                End If
                ' Combine the matching rows into a unioned range
                ' to be deleted in one go at the end.
                If surg Is Nothing Then
                    Set surg = srrg
                Else
                    Set surg = Union(surg, srrg)
                End If
            End If
        Next srrg
        
        ' Delete the archived source rows.
        Dim WereRecordsArchived As Boolean
        If Not surg Is Nothing Then
            If OK_TO_DELETE_RECORDS Then
                surg.Delete Shift:=xlShiftUp
            Else
                MsgBox "You were about to delete the following rows:" & vbLf _
                    & Join(Split(surg.Address(0, 0), ","), vbLf), vbInformation, _
                    PROC_NAME & " - Test Mode"
                'Debug.Print Join(Split(surg.Address(0, 0), ","), vbLf)
            End If
            WereRecordsArchived = True
        End If
        
        ' Turn on settings.
        Application.ScreenUpdating = True
        
        ' Activate the source sheet (not necessarry!?).
        'sws.Activate
        
        If WereRecordsArchived Then
            MsgBox "Complete records archived.", vbInformation, PROC_NAME
        Else
            MsgBox "The records had already been archived!", _
                vbExclamation, PROC_NAME
        End If
    
    End Sub

     

  • NikolinoDE's avatar
    NikolinoDE
    Platinum Contributor

    Simply copying the row causes formulas (like the one in column D) to re-point to the wrong cells on the destination sheet.

    It is the best-practice, production-ready version of your macro.
    It avoids Select, handles row deletion correctly, preserves your logic, and locks column D as values before moving the row so formulas never shift.

    Sub MoveCompletedProjects()
    
        Dim wsSrc As Worksheet
        Dim wsDst As Worksheet
        Dim lastSrcRow As Long
        Dim lastDstRow As Long
        Dim r As Long
    
        Application.ScreenUpdating = False
        Application.EnableEvents = False
        Application.Calculation = xlCalculationManual
    
        Set wsSrc = Worksheets("Deliverables")
        Set wsDst = Worksheets("Complete")
    
        ' Last used rows
        lastSrcRow = wsSrc.Cells(wsSrc.Rows.Count, "S").End(xlUp).Row
        lastDstRow = wsDst.Cells(wsDst.Rows.Count, "A").End(xlUp).Row
    
        ' Loop bottom-to-top to safely delete rows
        For r = lastSrcRow To 1 Step -1
            
            If wsSrc.Cells(r, "S").Value = "Complete" Then
                
                ' Lock column D as values BEFORE moving
                wsSrc.Cells(r, "D").Value = wsSrc.Cells(r, "D").Value
                
                ' Copy row to destination
                wsSrc.Rows(r).Copy Destination:=wsDst.Rows(lastDstRow + 1)
                
                ' Delete source row
                wsSrc.Rows(r).Delete
                
                lastDstRow = lastDstRow + 1
            End If
            
        Next r
    
    CleanExit:
        Application.ScreenUpdating = True
        Application.EnableEvents = True
        Application.Calculation = xlCalculationAutomatic
    
    End Sub

    No .Select or .Activate

    Bottom-to-top loop

    Formula-safe transfer

    Performance optimized

    Explicit worksheet references

     

    My answers are voluntary and without guarantee!

     

    Hope this will help you.

     

    Was the answer useful? Mark as best response and like it!

    This will help all forum participants.

Resources