I have Finally got some working code but Im getting a "type mismatch" error once the Module2 function finishes and I have no idea why.
If I step through it, it steps through "End function" on module2 then I get a Type Mismatch, but it does send the email. Any help would be great
This VBA code is in 3 parts.
1 Sub
Sub Workbook_open()
Call Module1.GetData
End Sub
2 Module 1
Public EmailAddress As String
Public CompanyNumber As String
Public Name As String
Public GroupComp As String
Function GetData()
Dim LastRow As String
Dim rng As Range
LastRow = Cells(Rows.Count, "K").End(xlUp).Row
For Each rng In Range("K2:K" + LastRow)
If Not rng.Value = vbNullString Then
Select Case rng.Value
Case 1
Case Is = "True"
Let EmailAddress = ActiveCell.Offset(0, -5).Value
Let CompanyNumber = ActiveCell.Offset(0, -9).Value
Let Name = ActiveCell.Offset(0, -8).Value
Let GroupComp = ActiveCell.Offset(0, -7).Value
Call Module2.Email(EmailAddress, CompanyNumber, Name, GroupsComp)
Case 2
Case Is = "False"
End Select
End If
Next
End Function
3 Module 2
Function Email()
'MsgBox (EmailAddress)
Set objMessage = CreateObject("CDO.Message")
objMessage.Subject = "Stuffl " & (GroupComp)
objMessage.From = "Department Name(Department#Email.com)"
objMessage.Cc = "Department Name(Department#Email.com)"
objMessage.To = (EmailAddress)
MsgBox (EmailAddress)
objMessage.TextBody = "TEST"
objMessage.Configuration.Fields.Item _
("http://schemas.microsoft.com/cdo/configuration/sendusing") = 2
objMessage.Configuration.Fields.Item _
("http://schemas.microsoft.com/cdo/configuration/smtpserver") = "x.x.x.x"
objMessage.Configuration.Fields.Item _
("http://schemas.microsoft.com/cdo/configuration/smtpserverport") = 25
objMessage.Configuration.Fields.Update
objMessage.Send
End Function
Change the Function Email() into
Function Email(emailaddress As String, companynumber As String, name As String, groupscomp As String)
You are not actually calling the Mail function , that's the problem i think
"i dont understand why I had to specify the variables again in the mail"
Whenever you pass values to a Sub or Function, you have to define that Sub or Function such that it is expecting to have values passed to it. So this won't work:
Sub Foo()
Dim i as Integer
i = 5
Call Bar(i)
End Sub
Sub Bar()
i = i + 2
End Sub
Because Bar() isn't expecting to have anything passed to it. This will work:
Sub Foo()
Dim i as Integer
i = 5
Call Bar(i)
End Sub
Sub Bar(i as Integer)
i = i + 2
End Sub
Because you have now told Bar to expect an integer to be passed to it.
Hope that helped.
Related
As you saw from the title I am getting error 1004. I am trying to make it iterate through cells B4 to B9 and at each one and if there is no sheet with the name in that cell it creates it and pastes the headers that are on the data entry page (C1:M3) and the data on that row from C to I onto the newly created sheet. If it does exist it looks at A1 of the sheet with that name and pastes the data into column B and the row that A1 specifies. And it does this for B4:B9 on each cell. Any help would be appreciated.
Function copyHeader(inputrange As String, inputsheet As String, outputcell As String, outputsheet As String)
Sheets(inputsheet).Range(inputrange).Copy Destination:=Sheets(outputsheet).Range(outputcell)
Application.CutCopyMode = False
Cells(1, 1).Value = 4 'probably better to make this dynamic
End Function
Function copyDetail(inputrange As String, inputsheet As String, outputcell As String, outputsheet As String)
Sheets(inputsheet).Range(inputrange).Copy Destination:=Sheets(outputsheet).Range(outputcell)
Application.CutCopyMode = False
Cells(1, 1).Value = 4 'probably better to make this dynamic
End Function
Function createTab(tabname As String)
Worksheets.Add.Name = tabname
End Function
Function shtExists(shtname As String) As Boolean
Dim sht As Worksheet
On Error GoTo ErrHandler:
Set sht = Sheets(shtname)
shtExists = True
ErrHandler:
If Err.Number = 9 Then
shtExists = False
End If
End Function
Public Function lastCell(Col As String)
With ActiveSheet
lastCell = .Cells(.Rows.Count, Col).End(xlUp).Row
End With
End Function
Sub AddData()
Dim teamname As String
Dim countery As Integer
Dim teamdata As String
Dim matchcounter As String
Dim resp As Boolean
Dim maxCounter As Integer
counter = 4
maxCounter = lastCell("B")
On Error GoTo eh
For counter = 4 To maxCounter
ThisWorkbook.Sheets("DataEntry").Select
teamdata = "C" & counter & ":" & "N" & counter
teamname = ThisWorkbook.Sheets("DataEntry").Range("B" & counter).Value
resp = shtExists(teamname)
If resp = False Then
createTab (teamname)
copyHeader "C1:M3", "DataEntry", "B1", teamname
matchcounter = CStr(Sheets(teamname).Range("A1").Value)
copyDetail teamdata, "DataEntry", "B" & matchcounter, teamname
ElseIf resp = True Then
copyDetail teamdata, "DataEntry", "B" & matchcounter, teamname
End If
Next counter
Worksheets("DataEntry").Activate
Done:
Exit Sub
eh:
MsgBox "The following error occurred: " & Err.Description & " " & Err.Number & " " & Err.Source
End Sub
Here is what my data entry sheet looks like:
https://i.stack.imgur.com/NYo0P.png
Here is what the sheets that I am creating for each team look like:
https://i.stack.imgur.com/JaBfX.png
I've mocked this up here and tweaked your code to get it working. It isn't necessarily how I'd do it normally, (I wouldn't bother storing the destination row in A1 for instance - I'd detect the bottom and add there) but it works and should
a) make sense to you and
b) work with your data structure.
Option Explicit
Sub copyHeader(inputrange As String, inputsheet As String, outputcell As String, outputsheet As String)
Sheets(inputsheet).Range(inputrange).Copy Destination:=Sheets(outputsheet).Range(outputcell)
Application.CutCopyMode = False
Cells(1, 1).Value = 4 'probably better to make this dynamic
End Sub
Sub copyDetail(inputrange As String, inputsheet As String, outputcell As String, outputsheet As String)
Sheets(inputsheet).Range(inputrange).Copy Destination:=Sheets(outputsheet).Range(outputcell)
Application.CutCopyMode = False
Sheets(outputsheet).Cells(1, 1).Value = Sheets(outputsheet).Cells(1, 1).Value + 1
End Sub
Sub createTab(tabname As String)
Worksheets.Add(After:=Worksheets(Worksheets.Count)).Name = tabname
End Sub
Function shtExists(shtname As String) As Boolean
Dim sht As Worksheet
On Error GoTo ErrHandler:
Set sht = Sheets(shtname)
shtExists = True
ErrHandler:
If Err.Number = 9 Then
shtExists = False
End If
End Function
Public Function lastCell(sht As Worksheet, Col As String)
With sht
lastCell = .Cells(.Rows.Count, Col).End(xlUp).Row
End With
End Function
Sub AddData()
Dim teamname As String
Dim counter As Integer
Dim teamdata As String
Dim matchcounter As String
Dim resp As Boolean
Dim maxCounter As Integer
Dim sourcesheet As Worksheet
counter = 4
Set sourcesheet = ThisWorkbook.Sheets("DataEntry")
maxCounter = lastCell(sourcesheet, "B")
On Error GoTo eh
For counter = 4 To maxCounter
sourcesheet.Select
teamdata = "C" & counter & ":" & "N" & counter
teamname = sourcesheet.Range("B" & counter).Value
resp = shtExists(teamname)
If resp = False Then
createTab (teamname)
copyHeader "C1:M3", sourcesheet.Name, "B1", teamname
matchcounter = CStr(Sheets(teamname).Range("A1").Value)
copyDetail teamdata, sourcesheet.Name, "B" & matchcounter, teamname
ElseIf resp = True Then
matchcounter = CStr(Sheets(teamname).Range("A1").Value)
copyDetail teamdata, sourcesheet.Name, "B" & matchcounter, teamname
End If
Next counter
Worksheets("DataEntry").Activate
Done:
Exit Sub
eh:
MsgBox "The following error occurred: " & Err.Description & " " & Err.Number & " " & Err.Source
End Sub
Can some body help me with the reason why below code is returning "sub or function not defined" when I attempt to run a module.
Sub Tokenize()
Dim txt As String
Dim i As Integer
Dim FullName As Variant
For Counter = 1 To 300
Set curCell = Worksheet("Sheet1").Cells(Counter, 1)
txt = curCell.Value
FullName = Split(txt, " ")
For i = 0 To UBound(FullName)
Cells(Counter, i + 1).Value = FullName(i)
Next i
Next Counter
End Sub
I suspect the error is in this line:
Set curCell = Worksheet("Sheet1").Cells(Counter, 1)
^^^
You should replace Worksheet with Worksheets (notice the additional s).
I am working with a module that involves many functions that are almost identical except for the sub function that is called within. (The arguments are the same, and the loops run the same.)
An example of such a function is the following:
Function Run1(lookupString) As Boolean
For i = 1 To nA
For j = 1 To nB
Checkbox = ThisWorkbook.Sheets(i).Cells(j,1)
If Checkbox = lookupString
RunLocation(Checkbox)
Run1 = True
Exit Function
End If
Next j
Next i
Run1 = False
End Function
I have other functions that are identical except for the call to "RunLocation" which is different inside the other functions. Is there a way to have just one function in this form but include the sub function that it calls as an argument?
Try something like this:
Function Run1(lookupString, procName As String) As Boolean
For i = 1 To nA
For j = 1 To nB
CheckBox = ThisWorkbook.Sheets(i).Cells(j, 1)
If CheckBox = lookupString Then
'RunLocation (CheckBox)
Application.Run "'" & ThisWorkbook.Name & _
"'!Module1." & subName, CheckBox
'Adust "Module1" to whatever is the name of the
' code module with the methods you want to run...
Run1 = True
Exit Function
End If
Next j
Next i
Run1 = False
End Function
EDIT: use of Evaluate, and an interesting way to use a UDF to directly update a worksheet (something normally tricky to do...)
'************* a few test methods to call *******************
'just return the value
Function DoIt(c As Range)
DoIt = "Value is " & c.Value
End Function
'change the value
Function DoIt2(c As Range)
c.Value = 33
DoIt2 = "Value is " & c.Value
End Function
'a sub instead of a function
Sub DoIt3(c1 As Range, c2 As Range)
c1.Value = c2.Value
c1.Interior.Color = IIf(c1.Value > 10, vbRed, vbYellow)
End Sub
'******************** end test methods ***********************
Sub Tester()
'A1=22
Debug.Print ActiveSheet.Evaluate("=DoIt(A1)") '>> Value is 22
Debug.Print ActiveSheet.Evaluate("=DoIt2(A1)") '>> Value is 33
ActiveSheet.Evaluate "DoIt3(A1,A2)" '>> Sets A1 to A2
End Sub
'######## run as a UDF, this actually changes the sheet! ##############
' changing value in src updates dest and set interior color
Function Tester2(dest, src)
dest.Parent.Evaluate "DoIt3(" & dest.Address(False, False) & "," _
& src.Address(False, False) & ")"
Tester2 = "Changed sheet!"
End Function
For some reason its not going to the next cell in the range to check the value.
Break down of what is meant to happen
Sub calls Modules1.Getdata
This Checks each row for a notification tag ("True/False"). If true it grabs CompanyNumber calls Module3.Check
Moduel3.Check takes the CompanyNumber checks another sheet/range for Samevalue (Go to next Iteration in Module1.Getdata) Next cell if blank, enter company number etc.
Hope that makes sense.
Sub
Sub Workbook_open()
Call Module1.GetData
End Sub
Module1.GetData
Public EmailAddress As String
Public CompanyNumber As String
Public Name As String
Public Comp As String
Public ID As Integer
Function GetData()
Dim LastRow As String
Dim rng As Range
Worksheets("DDregister").Activate
Range("K2").Select
LastRow = Cells(Rows.Count, "K").End(xlUp).Row
For Each rng In Range("K2:K" + LastRow)
If Not rng.Value = vbNullString Then
Worksheets("DDregister").Activate
Range("K2").Select
Select Case rng.Value
Case 1
Case Is = "True"
rng.Select
Let EmailAddress = ActiveCell.Offset(0, -5).Value
Let CompanyNumber = ActiveCell.Offset(0, -9).Value
Let Name = ActiveCell.Offset(0, -8).Value
Let Comp = ActiveCell.Offset(0, -7).Value
ID = ActiveCell.Offset(0, -10).Value
Call Module3.Check(EmailAddress, CompanyNumber, Name, Comp)
Case 2
Case Is = "False"
End Select
ElseIf rng.Value = vbNullString Then
ThisWorkbook.Save
Application.DisplayAlerts = True
'ThisWorkbook.Close
End If
Next
End Function
Module3.Check
Function Check(EmailAddress As String, CompanyNumber As String, Name As String, Comp As String)
Dim rngCheck As Range
Dim LastRowCheck As String
Dim NewRange As Range
Worksheets("Check").Activate
ActiveSheet.Range("B2").Select
LastRowCheck = Cells(Rows.Count, "B").End(xlDown).Row
For Each rngCheck In Range("B2:B" + LastRowCheck)
Select Case rngCheck.Value
Case 1
Case Is = CompanyNumber
'Go to next iteration
Case 2
Case Is = vbNullString
ActiveCell.Value = CompanyNumber
ActiveCell.Offset(0, 1).Value = "True"
ActiveCell.Offset(0, -1).Value = ID
Call Module2.Email(EmailAddress, CompanyNumber, Name, Comp)
Next
End Function
Module2.Email
Function Email(EmailAddress As String, CompanyNumber As String, Name As String, Comp As String)
Set objMessage = CreateObject("CDO.Message")
objMessage.Subject = "Subject " & (Comp)
objMessage.From = "EmailAddress#Address.com"
objMessage.Cc = "EmailAddress#Address.com"
objMessage.To = (EmailAddress)
'MsgBox (EmailAddress)
objMessage.TextBody = "Stuff"
objMessage.Configuration.Fields.Item _
("http://schemas.microsoft.com/cdo/configuration/sendusing") = 2
objMessage.Configuration.Fields.Item _
("http://schemas.microsoft.com/cdo/configuration/smtpserver") = "x.x.x.x"
objMessage.Configuration.Fields.Item _
("http://schemas.microsoft.com/cdo/configuration/smtpserverport") = 25
objMessage.Configuration.Fields.Update
objMessage.Send
End Function
Assuming "True" And "False" Are actually strings and not the boolean values I think GetData should look more like the following:
Sub GetData()
Dim LastRow As String
Dim rng As Range
Dim EmailAddress As String
Dim CompanyNumber As String
Dim Name As String
Dim Comp As String
Dim ID As Integer
Worksheets("DDregister").Activate
Range("K2").Select
Lastrow = Worksheets("DDregister").Cells(Rows.Count, "K").End(xlUp).Row
For Each rng In Range("K2:K" & LastRow)
Select Case rng.value
Case "True"
EmailAddress = Worksheets("DDregister").Cells(rng.Row,"F").Value
CompanyNumber = Worksheets("DDregister").Cells(rng.Row,"B").Value
Name = Worksheets("DDregister").Cells(rng.Row,"C").Value
Comp = Worksheets("DDregister").Cells(rng.Row,"D").Value
ID = Worksheets("DDregister").Cells(rng.Row,"A").Value
Call Module3.Check(EmailAddress, CompanyNumber, Name, Comp)
Case "False"
Case vbNullString
ThisWorkbook.Save
Application.DisplayAlerts = True
'ThisWorkbook.Close
End Select
Next rng
End Sub
Also this is a sub because it does not return anything and why have you put all of these routines in different modules? Since you are passing the values there is no reason to make them global by listing them outside the sub
P.S. I did not fix your other SELECT CASE Statement but it has similar issues. SELECT CASE syntax the way you are using it is as follows
SELECT CASE [expression]
CASE [condition]
CASE [condition]
CASE ELSE
END SELECT
How far is this from what you need? It all goes into a single standard module and is a complete replacement for you code:
Option Explicit
Public Enum DataRef
ID = 1
CompanyNumber = 2
Name = 3
Comp = 4
Email = 6
End Enum
Sub test()
Dim vData, vSubData
Dim lngRow As Long
With Worksheets("DDregister")
vData = .Range("A2:K" & .Cells(.Rows.Count, "K").End(xlUp).Row)
End With
If Len(vData(1, 11)) > 0 Then
For lngRow = LBound(vData) To UBound(vData)
If vData(lngRow, 11) = "True" Then
With Worksheets("Check").Columns(2)
If .Find(vData(lngRow, DataRef.CompanyNumber), , xlValues) Is Nothing Then
vSubData = Array(vData(lngRow, DataRef.ID), vData(lngRow, DataRef.CompanyNumber), "True")
.Cells(.Rows.Count, 1).End(xlUp).Offset(1, -1).Resize(, 3).Value = vSubData
SendEmail vData(lngRow, DataRef.Email), vData(lngRow, DataRef.Comp)
End If
End With
End If
Next lngRow
Else
ThisWorkbook.Save
End If
End Sub
Sub SendEmail(ByVal EmailAddress As String, ByVal Comp As String)
Dim objMessage As Object
Set objMessage = CreateObject("CDO.Message")
With objMessage
.Subject = "Subject " & Comp
.From = "EmailAddress#Address.com"
.Cc = "EmailAddress#Address.com"
.To = EmailAddress
.TextBody = "Stuff"
.Configuration.Fields.Item _
("http://schemas.microsoft.com/cdo/configuration/sendusing") = 2
.Configuration.Fields.Item _
("http://schemas.microsoft.com/cdo/configuration/smtpserver") = "x.x.x.x"
.Configuration.Fields.Item _
("http://schemas.microsoft.com/cdo/configuration/smtpserverport") = 25
.Configuration.Fields.Update
.Send
End With
End Sub
I have worked out a way to do it myself, I would really appreciate some feedback, as you might have guessed im new to coding lol
Sub GetData()
Dim LastRow As String
Dim rng As Range
Dim EmailAddress As String
Dim CompanyNumber As String
Dim Name As String
Dim Comp As String
Dim ID As Integer
Dim rngCheck As Range
Dim LastRowCheck As String
Dim TodayDate As Date
TodayDate = Date
Worksheets("DDregister").Activate
Range("K2").Select
LastRow = Cells(Rows.Count, "K").End(xlUp).Row
For Each rng In Range("K2:K" + LastRow)
Worksheets("DDregister").Activate
Select Case rng.Value
Case "True"
rng.Select
EmailAddress = ActiveCell.Offset(0, -5).Value
CompanyNumber = ActiveCell.Offset(0, -9).Value
Name = ActiveCell.Offset(0, -8).Value
Comp = ActiveCell.Offset(0, -7).Value
ID = ActiveCell.Offset(0, -10).Value
Worksheets("Check").Activate
Range("B2").Select
LastRowCheck = Cells(Rows.Count, "B").End(xlUp).Row
For Each rngCheck In Range("B2:B" & LastRowCheck)
Select Case True
Case ActiveCell.Value = CompanyNumber
ActiveCell.Offset(1, 0).Select
Exit For
End Select
If Not IsEmpty(ActiveCell.Value) Then
ActiveCell.Offset(1, 0).Select
ActiveCell.Select
End If
If ActiveCell.Value = "" Then
ActiveCell.Value = CompanyNumber
ActiveCell.Offset(0, 1).Value = "True"
ActiveCell.Offset(0, -1).Value = ID
ActiveCell.Offset(0, 2).Value = TodayDate
Call Email(EmailAddress, CompanyNumber, Name, Comp)
End If
Next rngCheck
Case "False"
Case vbNullString
Call Module2.MsgPopup
'CloseBookMsgBox = MsgBox("Do you want to Close the WorkBook", vbYesNo, "WhatsThis")
'
If Module2.MsgPopup = vbYes Then
ThisWorkbook.Save
ThisWorkbook.Close
'
ElseIf Module2.MsgPopup = vbNo Then
Cancel = "True"
MsgBox "Please make sure you save changes manually and close the work book!"
End If
If Cancel = "True" Then Exit Sub
End Select
Next rng
End Sub
Sub Email(EmailAddress As String, CompanyNumber As String, Name As String, Comp As String)
Set objMessage = CreateObject("CDO.Message")
objMessage.Subject = "stuff" & (Comp)
objMessage.From = "emailaddress"
objMessage.Cc = "emailaddress"
objMessage.to = EmailAddress
objMessage.TextBody = "stuff"
objMessage.Configuration.Fields.Item _
("http://schemas.microsoft.com/cdo/configuration/sendusing") = 2
objMessage.Configuration.Fields.Item _
("http://schemas.microsoft.com/cdo/configuration/smtpserver") = "x.x.x.x"
objMessage.Configuration.Fields.Item _
("http://schemas.microsoft.com/cdo/configuration/smtpserverport") = 25
objMessage.Configuration.Fields.Update
objMessage.Send
End Sub
As i will be runing a Scheduled task to execute this on open, I need to add a delayed msgbox as we will need to manual alter the document as well. so if the timeout period is reached i need to default to "no". Im attempting this in the below function (itsnt working atm)
Set objWshell = CreateObject(“WScript.Shell”)
Any help on this part would be great, currently telling me that "Object Required on this line ^. Even tho it is "set"
Public Function MsgPopup(Optional Prompt As String, Optional Buttons As VbMsgBoxStyle = vbOKOnly, Optional Title As String, Optional SecondsToWait As Long = 0) As VbMsgBoxResult
Dim objWshell As Object
Set objWshell = CreateObject(“WScript.Shell”)
MsgPopup = objWshell.Popup(Prompt, 20, "Do you want to Close the WorkBook", vbYesNo)
Set objWshell = Nothing
End Function
I'm looking to populate a combobox with only unique text values from a column. If a value in the column is empty (i.e. "") then it takes the value from the adjacent column to the left (still making sure it's not a duplicate).
I've embedded a Public Sub within the Userform module to add the items without duplicates:
Public Sub addIfUnique(CB As ComboBox, value As String)
If CB.ListCount = 0 Then GoTo doAdd
Dim i As Integer
For i = 0 To CB.ListCount - 1
If CB.List(i) = value Then Exit Sub
Next
doAdd:
CB.AddItem value
End Sub
However when I try to call the sub, it tells me an object is required. What I've got so far is as follows:
Worksheets("Scrapers").Activate
Range("M9").Activate
Dim intX As Integer
Dim value As String
push_lt_cbo.Clear
Do Until ActiveCell.Offset(0, -1).value = 0
If ActiveCell.value = "" Then
value = ActiveCell.Offset(0, -1).Text
Call addIfUnique((push_lt_cbo), (value))
Else
value = ActiveCell.Text
Call addIfUnique((CB), (value))
End If
Loop
Any help would be much appreciated!
LW
You're close:
Option Explicit 'Add this if you don't already have it
Private Sub UserForm_Initialize()
Worksheets("Scrapers").Activate
Range("M9").Activate
Dim intX As Integer
Dim value As String
push_lt_cbo.Clear
'Your loop will never end like this:
'Do Until ActiveCell.Offset(0, -1).value = 0
'Instead use a variable:
Dim rowOffset As Integer
rowOffset = 0
Do Until ActiveCell.Offset(rowOffset, -1).value = 0
'There was a lot of extra stuff here. Simplifying:
value = ActiveCell.Offset(rowOffset, -1).value
'Remove optional CALL keyword.
'Also remove paranthesis; they caused the error:
addIfUnique push_lt_cbo, value
'increment offset:
rowOffset = rowOffset + 1
Loop
End Sub
'Use 'msforms.ComboBox' to clarify.
Public Sub addIfUnique(CB As msforms.ComboBox, value As String)
If CB.ListCount = 0 Then GoTo doAdd
Dim i As Integer
For i = 0 To CB.ListCount - 1
If CB.List(i) = value Then Exit Sub
Next
doAdd:
CB.AddItem value
End Sub