I am trying to connect to workbooks to track orders. One workbook(Book1) displays the order#, total qty and current qty(completed so far) alongside a list of the weeks orders in the production office. The other workbook(Book2) is at the workstation for the operator to enter the new order number and current quantity as parts are completed.
The first half of the code works fine. It successfully updates the order# and pastes it to Book1 from Book2. What I am having trouble accomplishing is updating the cell in the "status" column of the table with the corresponding order# that was just pasted into the workbook to either a 1 or 2. I have the table formatted to where a blank cell is red(order not active), 1 = yellow(order is open) and 2 = green(order complete).
I tried the code below using an "IF" off of the Order Count being 0 because it will reset before the pasting of the new Order#. NOTE: orders may not be completed in the order they are listed so it has to be some type of lookup. I can't just find the last empty cell in the "status" column.
Update* FIGURED IT OUT! Code below now works in case anyone elses comes across this thread!
Thank you to everyone in the comments below.
Private Sub CommandButton1_Click()
Dim wbEntry As Workbook
Set wbEntry = ThisWorkbook
Dim wbCount As Workbook
Set wbCount = Workbooks("MO# Count.xlsm")
wbEntry.Sheets("Sheet1").Range("B3").Copy
wbCount.Activate
wbCount.Worksheets("Golf Cart").Range("V5").Select
ActiveCell.PasteSpecial xlPasteValues
Dim Fnd As Range
Set Fnd = Sheets("Golf Cart").Range("A:A").Find(Sheets("Golf Cart").Range("V5").Value, , , xlWhole, , , False, , False)
If Not Fnd Is Nothing Then
Set Fnd = Fnd.Offset(0, 2)
End If
Fnd.Value = 1
ActiveWorkbook.Save
wbEntry.Activate
Application.CutCopyMode = False
wbEntry.ActiveSheet.Range("H2").Select
End Sub
If you write What:="MO" then you are searching literally for the text "MO" and not for the value in the variable MO. To use the variable you must write What:=MO
For every Range object specify in which workbook/worksheet it is or Excel might assume another worksheet than you thought.
Only search in Column A wsGolf.Columns("A").Find(…), if you search in Cells you search in all cells and of course you will always find what you are looking for in Range("V5") but that's not the result you want.
Check if Find was successfull: If FoundRef Is Nothing Then. You can only Offset from the found cell if one was actually found.
Never use .Select, .Activate or ActiveSheet always specify worksheets by their name for all Range and Cells objects etc. See How to avoid using Select in Excel VBA.
So something like below should help you:
Option Explicit
Private Sub CommandButton1_Click()
Dim wbEntry As Workbook
Set wbEntry = ThisWorkbook
Dim wbCount As Workbook
Set wbCount = Workbooks("MO# Count.xlsm")
wbEntry.Worksheets("Sheet1").Range("B3").Copy
Dim wsGolf As Worksheet
Set wsGolf = wbCount.Worksheets("Golf Cart")
wsGolf.Range("V5").PasteSpecial xlPasteValues
Dim MO As Range
Set MO = wsGolf.Range("V5")
Dim FoundRef As Range
Set FoundRef = wsGolf.Columns("A").Find(What:=MO, After:=wsGolf.Range("A1"), LookIn:=xlValues, LookAt:=xlPart, _
SearchOrder:=xlByRows, SearchDirection:=xlNext, MatchCase:=False, SearchFormat:=False)
If FoundRef Is Nothing Then
MsgBox "'" & wsGolf.Range("V5") & "' was not found.", vbCritical
Exit Sub
End If
FoundRef.Offset(ColumnOffset:=2).Value = 1
End Sub
Related
MY EXCEL FILE
Two worksheets: Report and Leaving
One Named Range: Leavers (list of full names in the Leaving worksheet, column A, some names are red and others are orange).
MY GOAL
I want my macro to find in the Report worksheet (specifically in columns G to M) all the names that are part of the Leavers named range and apply the matching font color to each cell that was found.
MY CODE (SO FAR...)
This code could help by searching them one by one but it doesn't change much from doing it manually with Ctrl + F one by one. I could not find another way around it. Feel free to offer better alternatives, codes and solutions.
Dim Sh As Worksheet
Dim Found As Range
Dim Nme As String
Dim Adr1 As String
Nme = Application.InputBox("Enter Name to search", "Test")
Set Sh = Sheets("Sheet1")
With Sh.Range("A2:A")
Set Found = .Find(What:=Nme, After:=.Range("A2"), _
LookIn:=xlValues, lookat:=xlWhole, SearchOrder:=xlNext, _
MatchCase:=False, SearchFormat:=False)
If Not Found Is Nothing Then
Adr1 = Found.Address
Else
MsgBox "Name could not be found"
Exit Sub
End If
Do
Found.Interior.ColorIndex = 4
Set Found = .FindNext(Found)
Loop Until Found Is Nothing Or Found.Address = Adr1
End With
End Sub
Try this:
I tried to stick with some of your existing code but I had to make some changes.
You need to loop through your first range (I've used "G2:M1000" here on Sheet1 which I guess is your report page?)
You can't use a range like "A2:A" in your find routine, so again I've arbitrarily used a 1000 limit: "A2:A1000"
You were using interior cell colour, not font colour, I've changed this but if you did mean interior colour then just swap it back
I'm not using "Exit Sub" since this will stop everything running the first time it encounters a blank cell / no matching name.
Sub eh()
Dim rng As Range: Set rng = Sheet1.Range("G2:M1000")
Dim v As Range
Dim c As Variant
Dim Found As Range
Dim Nme As String
Dim Adr1 As String
For Each v In rng
Nme = v.Value2
If Nme <> "" Then
Set Found = Sheet2.Range("A2:A1000").Find(What:=Nme, After:=Sheet2.Range("A2"), LookIn:=xlValues, lookat:=xlWhole, SearchOrder:=xlNext, MatchCase:=False, SearchFormat:=False)
If Not Found Is Nothing Then
v.Font.Color = Found.Font.Color
End If
End If
Next v
End Sub
I tried a different approach, I have a subprocedure to loop through the range of the leavers, and when I find a value i give it to another procedure to look for that value in the report range. I am copying all the format of the cells, you can change it to just copy the font color. Also you should check for the ends of each range, to optimize and also this code would loop several times for the same name if the same name repeats in the leavers range, that could be improved.
Sub select_name() 'Select every cell in the leavers range
Dim leaving_range As Range
Dim row_leaving As Range
Set leaving_range = Sheets("Leaving").Range("A2:A10") 'The leavers list, check for the end of the range
For Each row_leaving In leaving_range.Rows
If row_leaving.Cells(1, 1).Text <> "" Then
row_leaving.Cells(1, 1).Copy 'I am gonna copy all the format, you change it to copy just the font color
Call look_for_name(row_leaving.Cells(1, 1).Text)
End If
Next
End Sub
Sub look_for_name(name_to_find As String) 'look for a name in the report range and change the format
Dim report_range As Range
Dim row_report As Range
Set report_range = Sheets("Report").Range("G1:M5") 'the report range where the names are to be found
For Each row_report In report_range.Rows
For Each cell In row_report.Cells
If cell.Value = name_to_find Then
cell.PasteSpecial Paste:=xlPasteFormats
End If
Next
Next
End Sub
I am still quite new to VBA and decided I would try and teach myself the Range.FindNext method. Unfortunately, I am not very successful so far.
What I am trying to do, is to copy all rows with a specific search term in them to a new sheet (could be anything, therefore declared as a Variant). What is important, is that the search term might only be part of the cell's value, hence I am using xlPart in my Range.Find method.
Here the example data from my ActiveWorkbook.ActiveSheet:
Date Name Numbers
12.04.2012 Marla 45653
13.04.2017 Peter 23545
27.04.1985 Bertrud 46932
16.08.2020 Peterson 46764
15.09.2014 Marcos 32465
21.06.2010 Peter Pan 23452
31.08.2013 Bernard 12321
So, when looking for "Peter", I should be getting rows 3, 5 and 7 in a new sheet. This is the code I wrote for this:
Option Explicit
Dim wsMain, wsNew As Worksheet
Dim rgAll, rgSearchTermFind As Range
Dim varSearchTerm As Variant
Dim lngLastRow, lngLastColumn As Long
Dim firstAddress As String
Public Sub FindAndCopy()
'I have an InputBox for the user to determine the varSearchTerm, but for this example:
varSearchTerm = "Peter"
Set wsMain = ActiveWorkbook.ActiveSheet
Set wsNew = Sheets.Add(After:=Worksheets(Sheets.Count))
Call FindLast(wsMain) 'This is a separate sub I wrote to find the last row & column
With wsMain
Set rgAll = .Range(.Cells(1, 1), .Cells(lngLastRow, lngLastColumn))
End With
With rgAll
Set rgSearchTermFind = .Find(What:=varSearchTerm, _
LookIn:=xlValues, _
LookAt:=xlPart, _
SearchOrder:=xlNext, _
MatchCase:=False)
If Not rgSearchTermFind Is Nothing Then
firstAddress = rgSearchTermFind.Address
Do
'Copy row to new sheet
If Application.WorksheetFunction.CountA(wsNew.Cells) <> 0 Then
Call FindLast(wsNew) 'This is a separate sub I wrote to find the last row & column
wsMain.Range(rgSearchTermFind.Address).EntireRow.Copy _
Destination:=wsNew.Cells(lngLastRow + 1, 1)
Else
wsMain.Range(rgSearchTermFind.Address).EntireRow.Copy _
Destination:=wsNew.Cells(1, 1)
End If
'Find next occurrence of search term
Set rgSearchTermFind = .FindNext(rgSearchTermFind)
Loop Until rgSearchTermFind.Address = firstAddress
Else
'Code here to execute if search term could not be found
End If
End With
End Sub
When running this code, the initial Range.Find method finds Peter in B3, but the Range.FindNext then finds "Bertrud" in B4 and copies it. This happens for each cell in the range, leaving me at the end with the table copied three times in the new sheet (due to there being three columns).
What am I doing wrong? Any help will be much appreciated.
Okay, so my problem is the following. What I'm basically doing is that I have a patientfile, on sheet1, I have some basic information of each patient. Column A of that page is manually edited. It is our "main page". We have 25 rooms, and usually all rooms are filled. So when we get a new patient, it will be entered on the line of where an old patient was.
On sheet2 I have extended information on each patient. The patientname is taken from sheet1 and after that comes extended information of the patient. Sheet2 can be sorted in different ways, for example, last name of patient, room number etc. So the patients won't always be in the same order as they are on sheet 1.
To explain what I want is the following: Whenever a patient gets discharged, I want the extended information of that patient cleared in sheet2, as it needs to be "reset" for the information of the new patient.
Below are images of what I mean:
Before new patient comes in
Sheet1 Sheet2
New patient comes in
Patient6 was replaced with Patient12 on sheet1, thus on sheet2 the extra information for Patient6 (which now stands with Patient12) was removed.
Sheet1 Sheet2
Like this, extended information for patient12 can be added again, without risking that extend information of the previous patient sticks around and suddenly belongs to patient12
Like these images, the same goes for all other cells in sheet1.
Private Sub Worksheet_Change(ByVal Target As Range)
Dim oSheet1 As Worksheet
Dim oSheet2 As Worksheet
Dim oLookFor As Range
Dim oFound As Range
If Not Intersect(Target, Columns(1)) Is Nothing Then
Set oSheet1 = ThisWorkbook.Worksheets("Blad1")
Set oSheet2 = ThisWorkbook.Worksheets("Blad2")
Set oLookFor = oSheet1.Range("A1")
Set oFound = oSheet2.Columns(1).Find(what:=oLookFor.Value, LookIn:=xlValues, lookat:=xlWhole, MatchCase:=False)
If Not oFound Is Nothing Then
oFound.Range("B" & Target.Row & ":D" & Target.Row).ClearContents
Else
MsgBox oLookFor.Value & " not found on sheet " & oSheet2.Name
End If
End If
End Sub
The above code, whether it be with .Columns(1) or .Range(A:A) will only work for 1 cell, because the oLookFor is set to just look at sheet1 A1.
That's where I miss the part which I can't figure out.
If the patient that gets switched is in cell A1, then sure, the code does what it has to do. It looks at the patients name in A1, searches this name and removes the extra information in sheet2. But what I now need to add, that if a patient's name in cell A3 changes, search the new name in sheet2 and remove the extra information. Same goes for the other cells.
ALSO: it is never the case that ALL patients change at once, it goes one by one.
I'm guessing for it to work, I would have to check which values in cells A1 to A5 actually change. Then only for the cell that changes lookup the value in sheet2 and clear the corresponding row. But I really have no idea how to set this up...
Update
Been fooling around myself some more. By combining the code of #Dschuli and some standard example on how to use a For Each cell statement I've managed to make it work :) Below is the code which does what I want:
Option Explicit
Private Sub Worksheet_Change(ByVal Target As Range)
Dim oSearchArea As Range
Dim oSheet2 As Worksheet
Dim oLookUpArea As Range
Dim oFound As Range
Set oSearchArea = Intersect(Target, Target.Parent.Range("A1:A5"))
Set oSheet2 = ThisWorkbook.Worksheets("Blad2")
Set oLookUpArea = oSheet2.Columns(1)
If Not oSearchArea Is Nothing Then
Application.EnableEvents = False 'pervent triggering another change event
Dim Cel As Range
For Each Cel In oSearchArea.Cells
Set oFound = oLookUpArea.Find(what:=Cel.Value, LookIn:=xlValues, lookat:=xlWhole, MatchCase:=False)
If Not oFound Is Nothing Then
oFound.Columns("B:D").ClearContents
Else
MsgBox Cel.Value & " not found on sheet " & oSheet2.Name
End If
Next Cel
Application.EnableEvents = True 'don't forget to re-enable events in the end
End If
End Sub
but I have 25 values it has to look at.
You have got most of what you wanted. As for the last part, set your range accordingly as shown below and then use that as I hinted in the comment above.
Is this what you are trying?
Option Explicit
Private Sub Worksheet_Change(ByVal Target As Range)
Dim rng As Range
'~~> This is the range which you want to capture
'~~> amend it as per your requirements
Set rng = Range("A1,D1,E4,G8,H14")
If Not Intersect(Target, rng) Is Nothing Then
'
'~~> Do what you want
'
MsgBox Target.Address
End If
End Sub
On a side note, since you are working with Worksheet_Change, I would recommend you give a glance at This Thread as well.
A shot at your problem - assuming you always look/search in column 1 ("A") and you're target area does not contain any blank cells.
Version 3 now as stated in the comment below.
Private Sub Worksheet_Change(ByVal Target As Range) 'Version 3
Dim oSheet1 As Worksheet
Dim oSheet2 As Worksheet
Dim oSensitiveArea As Range
Dim oLookUpArea As Range
Dim relevantChanges As Range
Dim oFound As Range
Dim oLookFor As Range
Dim columnsToClear As String
Set oSheet1 = ThisWorkbook.Worksheets("Blad1")
Set oSheet2 = ThisWorkbook.Worksheets("Blad2")
'Define the ranges that the event procedure should react on and where to look for
'In this case its the first column of the two sheets
Set oSensitiveArea = oSheet1.Columns(1)
Set oLookUpArea = oSheet2.Columns(1)
columnsToClear = "B:D"
Set relevantChanges = Intersect(Target, oSensitiveArea)
If Not relevantChanges Is Nothing Then
For Each oLookFor In relevantChanges.Cells
If Len(oLookFor.Value) = 0 Then Exit For 'Stop the loop if a blank cell (length = 0) is encountered
Set oFound = oLookUpArea.Find(what:=oLookFor.Value, LookIn:=xlValues, lookat:=xlWhole, MatchCase:=False)
If Not oFound Is Nothing Then
oFound.EntireRow.Columns(columnsToClear).ClearContents
Else
MsgBox oLookFor.Value & " not found on sheet " & oSheet2.Name
End If
Next oLookFor
End If
End Sub
I am struggling to find an answer to this one...
Each month I am provided with a spreadsheet full of clients data that is a raw extract from some sort of CRM software and that data is a mess. Some cells are merged, some are not. When you unmerge the whole sheet, you end up with data that is meant for one column randomly spread across 3 columns and mixed with another data, ie email addresses are spread across 3 columns and mixed with postcodes.
What I'd like to be able to do is search for cells within columns S, T and U that contain "#" and move (not copy) the whole email address to column V on the same row.
How can I achieve that?
You can achieve this with the following formula into V1:
=INDEX(S1:U1,MATCH(TRUE,NOT(ISERROR(SEARCH("#",S1:U1))),0))
The formula needs to be entered as array formula, i.e. pressing Ctrl-Shift-Enter.
Press Alt+F11 to open the Visual Basic Editor, and then click Insert, Module. Paste this in. Or, just download the example file here. Then under View/Macros, this movemail() routine will be there. Run it.
I take check, money order, paypal, bitcoin... :-) j/j Enjoy.
Sub moveemail()
Dim ws As Worksheet
Dim thisCell As Range, nextCell As Range, lookAt As Range
Dim foundAt As String, lookFor As String
Dim lastRow As Long
lookFor = "#"
On Error GoTo Err
'get last populated cell
Set ws = Application.ActiveSheet
With ws
If WorksheetFunction.CountA(Cells) > 0 Then
lastRow = Cells.Find(what:="*", SearchOrder:=xlByRows, _
SearchDirection:=xlPrevious).Row
End If
End With
' go cell by cell looking for # from row 1 to row 10000
Set lookAt = ActiveSheet.Range("S1:U" & lastRow)
Set thisCell = lookAt.Find(what:=lookFor, LookIn:=xlValues, lookAt:=xlPart, SearchDirection:=xlNext)
If Not thisCell Is Nothing Then
Set nextCell = thisCell
Do
Set thisCell = lookAt.FindNext(After:=thisCell)
If Not thisCell Is Nothing Then
foundAt = thisCell.Address
thisCell.Copy Range("V" & thisCell.Row)
thisCell.ClearContents
Else
Exit Do
End If
If thisCell.Address = nextCell.Address Then Exit Do
Loop
Else
'MsgBox SearchString & " not Found"
Exit Sub
End If
Err:
'MsgBox Err.Number
Exit Sub
End Sub
Before anybody says anything, I have looked through several of the posts relating to this similar idea (going by different search criterial and then modifying it) but I can't get the macro to work. This is probably due to my lack of programming knowledge!
All I want to do is, search for an email address in WORKSHEET 1 and if it finds it, copy the whole row to the next free row in WORKSHEET 2.
I'm using Excel 2003 (yes I'm an old stick-in-the-mud!).
Actually I think you're a smart person; personally I detest 2007/2010's user interface for many reasons.
To answer your question, see whether this make sense. (It's quick and dirty so it isn't bullet-proofed. It should give you a starting point, though.)
Sub FindAndCopyEmailAddress()
Dim vnt_Input As Variant
Dim rng_Found As Excel.Range
Dim wks1 As Excel.Worksheet, wks2 As Excel.Worksheet
Dim rng_target As Excel.Range
Dim l_FreeRow As Long
'Check that the sheets are there, and get a reference to
'them. Change the sheet names if they're different in yours.
On Error Resume Next
Set wks1 = ThisWorkbook.Worksheets("Sheet1")
Set wks2 = ThisWorkbook.Worksheets("Sheet2")
'If a runtime error occurs, jump to the line marked
'ErrorHandler to display the details before exiting the
'procedure.
On Error GoTo ErrorHandler
'Creating a message to tell *which* one is missing is left as an exercise
'for the reader, if you wish to.
If wks1 Is Nothing Or wks2 Is Nothing Then
Err.Raise vbObjectError + 20000, , "Cannot find sheet1 or 2"
End If
'Get the e-mail address that you want to find.
'You don't HAVE to use an InputBox; you could, for instance,
'pick it up from the contents of another cell; that's up
'to you.
vnt_Input = InputBox("Please enter the address that you're looking for", "Address Copier")
'If the user cancels the input box, exit the program.
'Do the same if there's no entry.
'Rather than exiting immediately we jump to the label
'ExitPoint so that all references are cleaned up.
'Perhaps unnecessary, but I prefer good housekeeping.
If vnt_Input = "" Then GoTo ExitPoint
'Find the range containing the e-mail address, if there is one.
'wks1.Cells essentially means "Look in all of the cells in the sheet
'that we assigned to the wks1 variable above". You don't have to be
'on that sheet to do this, you can be in any sheet of the workbook.
Set rng_Found = wks1.Cells.Find(What:=vnt_Input, After:=ActiveCell, _
LookIn:=xlFormulas, LookAt:=xlPart, SearchOrder:=xlByRows, _
SearchDirection:=xlNext, MatchCase:=False, SearchFormat:=False)
'The range will be Nothing if the address is not found. In that case, exit.
If rng_Found Is Nothing Then
MsgBox "Cannot find that address."
GoTo ExitPoint
End If
'Find the last free row in sheet2
'The .Row property tells us where the used range starts,
'the .Rows property tells us how many to add on to that to
'find the first free one.
'The only slight problem is that if there are no cells at
'all used in sheet 2, this will return row 2 rather than row
'1, but in practice that may not matter.
'(I wouldn't be surprised if you want headings anyway.)
l_FreeRow = wks2.UsedRange.Row + wks2.UsedRange.Rows.Count
'Make sure that the row is not greater than the number
'of rows on the sheet.
If l_FreeRow > wks2.Rows.Count Then
Err.Raise vbObjectError + 20000, , "No free rows on sheet " & wks2.Name
End If
'Set a range reference to the target.
'This will be the first free row, column 1 (column A).
Set rng_target = wks2.Cells(l_FreeRow, 1)
'Now copy the entire row that contains the e-mail address
'to the target that we identified above. Note that we DON'T need
'to select either the source range or the target range to do this; in fact
'doing so would just slow the code down.
rng_Found.EntireRow.Copy rng_target
'We always leave the procedure at this point so that we can clear
'all of the object variables (sheets, ranges, etc).
ExitPoint:
On Error Resume Next
Set rng_Found = Nothing
Set wks1 = Nothing
Set wks2 = Nothing
Set rng_target = Nothing
On Error GoTo 0
Exit Sub
ErrorHandler:
MsgBox "Error " & Err.Number & vbCrLf & Err.Description
Resume ExitPoint
End Sub
I have put together the following code which will look at the contents of a range of cells and copy the rows of cells that contain certain strings, "#" in this case, to a new row of a target workbook.
Dim srcWorkbook As Workbook
Dim destWorkbook As Workbook
Dim srcWorksheet As Worksheet
Dim destWorksheet As Worksheet
Dim SearchRange As Range
Dim destPath As String
Dim destname As String
Dim destsheet As String
Set srcWorkbook = ActiveWorkbook
Set srcWorksheet = ActiveSheet
destPath = "C:\test\"
destname = "dest.xlsm"
destsheet = "Sheet1"
'SET THIS TO YOUR DESTINATION WORBOOK Path/Workbook Name/Worksheet Name
On Error Resume Next
Set destWorkbook = Workbooks(destname)
If Err.Number <> 0 Then
Err.Clear
Set wbTarget = Workbooks.Open(destPath & destname)
CloseIt = True
End If
'THIS OPENS THE DESTINATION WORKBOOK IF IT IS CLOSED
For Each c In Range("A1:A100").Cells
'SET THIS RANGE TO THE CELLS YOU WANT TO CHECK FOR EMAIL
If InStr(c, "#") > 0 Then
'SET THE CALCULATION FOR DETERMINING AN EMAIL ADDRESS HERE (Currently it just checks for an # symbol)
c.EntireRow.Copy
destWorkbook.Activate
destWorkbook.Sheets(1).Range("A" & Rows.Count).End(xlUp).Offset(1).EntireRow.Select
'THIS FINDS AND SELECTS THE NEXT EMPTY ROW ON THE DESTINATION SHEET
Selection.PasteSpecial Paste:=xlPasteAll, Operation:=xlNone, SkipBlanks:= _
False, Transpose:=False
srcWorkbook.Activate
End If
Next
Apologies if i have messed up the code tags, I'm new to the site :)
This Code should be a lot simpler for doing the copy on the same workbook, I'm leaving my last answer there just incase you need it to work across workbooks as well :)
For Each c In Range("A1:A100").Cells
'SET THIS RANGE TO THE CELLS YOU WANT TO CHECK FOR EMAIL
If InStr(c, "#") > 0 Then
'SET THE CALCULATION FOR DETERMINING AN EMAIL ADDRESS HERE (Currently it just checks for an # symbol)
c.EntireRow.Copy Destination:=ActiveWorkbook.Worksheets("Sheet2").Range("A" & Rows.Count).End(xlUp).Offset(1)
End If
Next