Excel Automatically Numbering Sheets that Have the Same Name - vba

hope everyone is doing good! I have a quick question concerning excel and sheet names. Currently I have a macro assigned to a forum control button on my template invoice page that when clicked will make a duplicate sheet named after cell E7 (the project name) + the word "Invoice" at the end of it. But since there will be more than one invoice per project, I would like the macro to include some code where if it finds a duplicate name, it will automatically start numbering them from two. So for example, I use the macro to create "Project A Invoice". If I use it again to create another one, I want it to be named "Project A Invoice(2)" automatically instead of giving me an error message. Here's what I have so far:
Sub invoice_export_test()
Dim sName As String
Dim wks As Worksheet
Worksheets("Invoice").Copy after:=Sheets(Worksheets.Count)
Set wks = ActiveSheet
Do While sName <> wks.Name
sName = Range("E7") + " Invoice"
wks.Name = sName
On Error GoTo 0
Loop
Range("C7:D7").Select
With Selection.Validation
.Delete
.Add Type:=xlValidateInputOnly, AlertStyle:=xlValidAlertStop, Operator _
:=xlBetween
.IgnoreBlank = True
.InCellDropdown = True
.IMEMode = xlIMEModeNoControl
.ShowInput = True
.ShowError = True
End With
ActiveSheet.Shapes.Range(Array("Button 1")).Select
Selection.Delete
Set wks = Nothing
End Sub
Everything after the loop is irrelevant to this question (its there to delete the forum control button and data validation), but I included it just in case. The code is probably messy since I am not too experienced with VBA and used a few tutorials to work this out, so please forgive me for that haha.
Thank you in advanced for your time/help!

Create a couple functions to help yourself out:
Function GetUniqueName(strProject As String) As String
' If this is the first time it's being used, just return it without a number...
If Not SheetNameExists(strProject & " Invoice") Then
GetUniqueName = strProject & " Invoice"
Exit Function
End If
' Otherwise, suffix the sheet name with a number, starting at 2...
Dim i As Long, strName As String
i = 1
Do
i = i + 1
strName = strProject & " Invoice (" & i & ")"
Loop While SheetNameExists(strName)
GetUniqueName = strName
End Function
Function SheetNameExists(strName As String) As Boolean
Dim sh As Worksheet
For Each sh In Worksheets
If StrComp(sh.Name, strName, vbTextCompare) = 0 Then
SheetNameExists = True
Exit Function
End If
Next
End Function
Then you can change your code from:
Dim sName As String
Dim wks As Worksheet
Worksheets("Invoice").Copy after:=Sheets(Worksheets.Count)
Set wks = ActiveSheet
Do While sName <> wks.Name
sName = Range("E7") + " Invoice"
wks.Name = sName
On Error GoTo 0
Loop
To:
Worksheets("Invoice").Copy after:=Sheets(Worksheets.Count)
Worksheets(Worksheets.Count).Name = GetUniqueName(Range("E7"))

Related

VBA - Code is duplicating paste into column not specified in code

I'm using the below code to copy column B in combinedWorkbook to column B in ThisWorkbook but when running the macro it seems to paste column B into column C of ThisWorkbook as well as pasting into column B. I've stepped through the code and it works fine. This seems very strange and would be grataeful with any help on why it's also pasting into column C in ThisWorkbook.
Sub ImportWriteOffs()
Dim filter As String
Dim caption As String
Dim combinedFilename As String
Dim combinedWorkbook As Workbook
' Open BRAM Report Source Data
MsgBox ("Select 'SRMF0035 BRAM Pre Repurchase'")
filter = "Text files (*.*),*.*"
caption = "Select 'SRMF0035 BRAM Pre Repurchase'"
combinedFilename = Application.GetOpenFilename(filter, , caption)
If combinedFilename <> "False" Then
Set combinedWorkbook = Application.Workbooks.Open(combinedFilename)
Else
MsgBox "No file was uploaded", vbExclamation
GoTo LastLine
End If
If combinedWorkbook.Worksheets(1).Range("D7").Value = "Periodic Insurance" Then
' Copy and Paste into working file
Sheets("Tabular Version").Select
Range("B10:B100000").Select
Selection.Copy
ThisWorkbook.Activate
Sheets("Input - Write offs").Select
Range("B10:B100000").Select
ActiveSheet.Paste
Application.CutCopyMode = False
combinedWorkbook.Close False
' Delete last row
ThisWorkbook.Activate
Sheets("Input - Write offs").Select
Range("B10").Select
Selection.End(xlDown).Select
Selection.EntireRow.Delete
Else
MsgBox "Incorrect File Selected"
combinedWorkbook.Close False
Exit Sub
End If
LastLine:
End Sub
You can try this. Notice that you do not need to .Select a cell to copy it. It defeats the purpose of VBA! Just get right to the point: State the range and copy it. No need to select.
Also, no need for GoTo as mentioned by the infamous #ashleedawg, just Exit Sub when needed.
Sub ImportWriteOffs()
Dim ws As Worksheet: Set ws = ThisWorkbook.Sheets("Input - Write offs")
Dim filter As String, caption As String, combinedFilename As String
Dim combinedWorkbook As Workbook, ws2 as Worksheet
MsgBox ("Select 'SRMF0035 BRAM Pre Repurchase'")
filter = "Text files (*.*),*.*"
caption = "Select 'SRMF0035 BRAM Pre Repurchase'"
combinedFilename = Application.GetOpenFilename(filter, , caption)
If combinedFilename <> "False" Then
Set combinedWorkbook = Application.Workbooks.Open(combinedFilename)
Set ws2 = combinedWorkbook.Sheets("Tabular Version")
Else
MsgBox "No file was uploaded", vbExclamation
Exit Sub
End If
If combinedWorkbook.Worksheets(1).Range("D7") = "Periodic Insurance" Then
ws2.Range("B10:B" & ws2.Range("B" & ws.Rows.Count).End(xlUp).Row - 1).Copy
ws.Range("B10").PasteSpecial xlPasteValues
ws.Range("B10").PasteSpecial xlPasteFormats
combinedWorkbook.Close False
Else
MsgBox "Incorrect File Selected"
combinedWorkbook.Close False
End If
End Sub
This is happening because the select is actually using a relative reference. But it would be clearer what you want to do if you used Cells instead:
For r = 10 to 10000
ActiveWorkbook.Worksheets("Input - Write-offs").Cells(r, 2) = combinedWorkbook.Worksheets("Tabular Version").Cells(r, 2)
Next
You can implement something similar for deleting the last row, if you are so inclined.

Remove text after a space across multiple columns in Excel using VBA

I have a list of items' monthly prices and the price columns have both the amount and currencies. I am trying to remove the currencies across multiple columns.
Sample data sets:
I wrote below macro to remove text after space of cells in A1:E6:
Sub RemoveCurrency()
Dim cell As Range
Dim withCurrency As String
Dim PriceOnly As String
For Each cell In Worksheets("Sheet1").Range("A1:E6")
withCurrency = cell.Value
PriceOnly = Left(withCurrency, InStr(withCurrency, " ") - 1)
cell.Value = PriceOnly
Next cell
End Sub
After I ran it, it gives me a VB run-time error '5':
Invalid procedure call or argument
Can someone help to debug my VBA code?
Am I right, reading "to remove text after space" as "including this space"? If so:
Dim sStr$
' ...
For Each cell In Worksheets("Sheet1").Range("A1:E6")
' ...
sStr = cell.Value
If LenB(sStr) Then cell.Value = Split(sStr, " ")(0)
'or
cell.Value = Split(sStr & " ", " ")(0)
' ...
Next
' I'm not sure which is faster... but LenB seems to me more realiable
This code is much longer/less efficient than yours, but it won't give you an error when it runs out of cells to edit, at least (it's a module I had already written in the past for nearly the exact same purpose). Alternatively, yours should work (but still error at the end) if you update the range to start at the cell where your currency amount starts. You can read more about VBA ranges in the MSDN Documentation.
It requires that you add a VBA reference to "Microsoft VBScript Regular Expressions 5.5" if you don't already have it checked. You can do that in Tools -> References.
Public Sub TruncateIDs()
Dim strPattern As String: strPattern = "[A-Z]{1,3}"
Dim strReplace As String: strReplace = ""
Dim regEx As New RegExp
Dim strInput As String
Dim MyRange As Range
Set MyRange = ActiveSheet.Range("B3:E6")
ActiveSheet.Range("B3").Select
Do While strPattern <> ""
strInput = ActiveCell.Value
With regEx
.Global = True
.MultiLine = True
.IgnoreCase = False
.Pattern = strPattern
End With
If regEx.Test(strInput) Then
ActiveCell = regEx.Replace(strInput, strReplace)
ActiveCell.Offset(1, 0).Select
ElseIf ActiveCell = "" Then
ActiveCell.Offset(-4, 1).Select
If ActiveCell <> "" Then
strInput = ActiveCell.Value
ActiveCell = regEx.Replace(strInput, strReplace)
ActiveCell.Offset(1, 0).Select
Else
Exit Do
End If
Else
Exit Do
End If
Loop
End Sub

VBA Worksheet Data extraction to search for multiple values

I am tasked with pulling two specific rows of data from monthly sheets in a workbook.
Current code, using MyVal and a search box, is only compatible with one search. How can I change the code & searchbox function to be compatible with multiple searches?
Current code looks like this:
Sub Set_Hyper()
' Object variables
Dim wks As Excel.Worksheet
Dim rCell As Excel.Range
Dim fFirst As String
' {i} will act as our counter
Dim i As Long
' Use an input box to type in the search criteria
Dim MyVal As String
MyVal = InputBox("What are you searching for", "Search-Box", "")
' if we don't have anything entered, then exit the procedure
If MyVal = "" Then Exit Sub
Application.ScreenUpdating = False
Application.DisplayAlerts = False
' Add a heading to the sheet with the specified search value
With Cells(1, 1)
.Value = "Found " & MyVal & " in the Link below:"
.EntireColumn.AutoFit
.HorizontalAlignment = xlCenter
End With
i = 2
' Begin looping:
' We are checking all the Worksheets in the Workbook
For Each wks In ActiveWorkbook.Worksheets
If wks.Name <> "Data" Then
' We are checking all cells, we don't need the SpecialCells method
' the Find method is fast enough
With wks.Range("A:A")
' Using the find method is faster:
' Here we are checking column "A" that only have {myVal} explicitly
Set rCell = .Find(MyVal, , , xlWhole, xlByColumns, xlNext, False)
' If something is found, then we keep going
If Not rCell Is Nothing Then
' Store the first address
fFirst = rCell.Address
Do
' Link to each cell with an occurence of {MyVal}
rCell.Hyperlinks.Add Cells(i, 1), "", "'" & wks.Name & "'!" & rCell.Address
wks.Range("B" & rCell.Row & ":R" & rCell.Row).Copy Destination:=Cells(i, 2)
Set rCell = .FindNext(rCell)
i = i + 1 'Increment our counter
Loop While Not rCell Is Nothing And rCell.Address <> fFirst
End If
End With
End If
Next wks
' Explicitly clear memory
Set rCell = Nothing
' If no matches were found, let the user know
If i = 2 Then
MsgBox "The value {" & MyVal & "} was not found on any sheet", 64, "No Matches"
Cells(1, 1).Value = ""
End If
' Reset application settings
Application.ScreenUpdating = True
Application.DisplayAlerts = True
End Sub
I'm thinking what you could do is create a UserForm with the following controls:
A text box
A Listbox
A button to add text to the listbox
Another button to run the VBA
The Textbox can hold the search string(s). You can make an event when you click the button to do the following:
1) Add the text from textbox to the listbox. Lookup the AddItem method to do this.
2) Clear the text box contents, so a new value can be added.
Once that's added you can add another for loop around your code to go through each item added to the listbox. That way you can do multiple searches based on what was added.
Hopefully this helps :)

Runtime error 424: New version & Old Version

I'm completely baffled...this macro looks at a Range, draws a number with Rnd then creates a vlookup to bring back a quote and author every time I open up my workbook (should one apply).
This error just began this evening, but only on today's versions. I am able to open up older versions and run the code just as expected.
Below is "Today's" latest copy and produces the Runtime error, with the break happening on the line defining the string quote:
Private Sub Workbook_Open()
Dim sht As Object
Dim RandNumb As Integer
Dim quote As String
Dim author As String
Dim ws As Worksheet
Set ws = Worksheets("Home")
'Make "Home" Sheet visible and select
ws.Visible = True
'Search for all sheets not named "Home" and hide them
For Each sht In Worksheets
If sht.Name <> "Home" Then
sht.Visible = xlSheetHidden
End If
Next sht
'Create random number, then vlookup based off number
RandNumb = Int((56 - 1 + 1) * Rnd + 1)
quote = Application.WorksheetFunction.VLookup(RandNumb, Sheet3.Range("ba101:bc465"), 2, False)
author = Application.WorksheetFunction.VLookup(RandNumb, Sheet3.Range("ba101:bc465"), 3, False)
If quote <> Empty Then
MsgBox quote & vbNewLine & vbNewLine & " - " & author, vbOKOnly, "Quote of the day"
End If
End Sub
While the version from 2/6 works just fine:
Private Sub Workbook_Open()
Dim sht As Object
Dim RandNumb As Integer
Dim quote As String
Dim author As String
Dim ws As Worksheet
Set ws = Worksheets("Home")
'Make "Home" Sheet visible and select
ws.Visible = True
ws.Select
Range("A1").Select
'Search for all sheets not named "Home" and hide them
For Each sht In Worksheets
If sht.Name <> "Home" Then
sht.Visible = xlSheetHidden
End If
Next sht
'Create random number, then vlookup based off number
RandNumb = Int((56 - 1 + 1) * Rnd + 1)
quote = Application.WorksheetFunction.VLookup(RandNumb, Sheet3.Range("ba101:bc465"), 2, False)
author = Application.WorksheetFunction.VLookup(RandNumb, Sheet3.Range("ba101:bc465"), 3, False)
If quote <> Empty Then
MsgBox quote & vbNewLine & vbNewLine & " - " & author, vbOKOnly, "Quote of the day"
End If
End Sub
These codes look no different to me. Even when I copy the version from 2/6 and put it in "Today's" I continue to receive the error. Help please.
This was solved by #Rory; I had carelessly changed the name of the sheet but not in the code.

Need to copy certain data over workbooks

new to VBA here. I've been stuck on this problem for a while now:
Essentially, I need to create a macro that copies over specific data from one sheet to another, that is up to the user to specify. The catch is that while all the data is in one column (B), not all rows of the column have relevant entries; some are blank and some have other data that I don't want.
Only entries that begin with 4 numbers are wanted. I can't seem to get how the iterated copy-pasting works; what I've come up with is as follows:
'defining input
Dim dater As Date
dater = Range("B2")
If dater = False Then
MsgBox "Date not specified"
Exit Sub
End If
Dim sheetin As String
sheetin = Range("B5")
If sheetin = "" Then
MsgBox "Input Sheet not specified"
Exit Sub
End If
Dim wbin As String
wbin = Range("B4")
If wbin = "" Then
MsgBox "Input workbook not specified"
Exit Sub
End If
Dim sheetout As String
sheetout = Range("B9")
If sheetout = "" Then
MsgBox "Output Sheet not specified"
Exit Sub
End If
Dim wbout As String
wbout = Range("B8")
If wbout = "" Then
MsgBox "Output Workbook not specified"
Exit Sub
End If
Windows(wbout).Activate
Dim sh As Worksheet, existx As Boolean
For Each sh In Worksheets
If sh.Name Like sheetout Then existx = True: Exit For
Next
If existx = True Then
If Sheets(sheetout).Visible = False Then Sheets(sheetout).Visible = True
Else
Sheets.Add.Name = CStr(sheetout)
End If
'copy pasting values
Windows(wbin).Activate
Sheets(sheetin).Select
'specify maximum row
iMaxRow = 500
For iRow = 1 To iMaxRow
With Worksheets(sheetin).Cells(iRow, 2)
'Check that cell is not empty.
If .Value = "####*" Then
.Copy Destination:=Workbooks(wbout).Worksheets(sheetout).Range("A" & i)
'Else do nothing.
End If
End With
Next iRow
End Sub
Subsequently i'll have to match data to these entries that have been copied over but I figure once i get the hang of how to do iterated stuff it shouldn't be too much of a problem. But right now i'm really stuck... Please help!
It looks like it should work, except for that part :
With Worksheets(sheetin).Cells(iRow, 2)
If .Value = "####*" Then
.Copy Destination:=Workbooks(wbout).Worksheets(sheetout).Range("A" & i)
End If
End With
The third line contains an unknown variable : i.
You need to define it to contain the number of the line to which you're copying. For example, if you want to copy to the first available line, try this :
Set wsOut = Workbooks(wbout).Worksheets(sheetout)
With Worksheets(sheetin).Cells(iRow, 2)
If .Value = "####*" Then
i = wsOut.Cells(wsOut.Rows.Count, 1).End(xlUp).Row + 1
.Copy Destination:=wsOut.Range("A" & i)
End If
End With