Runtime Error 13 : Type mismatch - vba

This is my first time coding from scratch and a very dumb question indeed.
Why do i keep getting this error?
Runtime Error '13' : Type mismatch
My value in B1 is an integer
Private Sub Workbook_Open()
xdate = Worksheets("sheet1").Range("A1")
lsdate = DateAdd("d", -7, Date)
'MsgBox lsdate
If Day(lsdate) > Day(xdate) Then
Worksheets("sheet1").Range ("B1") * 1 = result
Else
Worksheets("sheet1").Range ("B1") * 1.07 = result
End If
Range("c1").Value = result
End Sub
I have further edit my code to the following but not able to get the condition to work
'To create the following condition
'If less than 7 days interest = 0%
'if 8 to 30 days interest = 7%
'if more than 31 days interest = 9%
Sub Workbook_Open()
For i = 1 To 3 'Rows.Count
xdate = Cells(i, 1)
'MsgBox Cells(i, 1)
nulldate = DateAdd("d", -7, Date)
irate7late = DateAdd("d", -8, Date)
irate7early = DateAdd("d", -30, Date)
If Day(nulldate) < Day(xdate) Then
result = Cells(i, 2) * 1
ElseIf Day(irate7early) <= Day(xdate) And Day(xdate) <= Day(irate7late) Then
'30/9/2015 20/10/2015 20/10/2015 22/10/2015
result = Cells(i, 2) * 1.07
ElseIf Day(irate7early) > Day(xdate) Then
result = Cells(i, 2) * 1.09
End If
Cells(i, 3).Value = result
Next i
End Sub

You seem pretty new at programming, so I'll explain plainly :
When you are trying to assign a value to a variable in almost every language, you use :
variable = value
So you can do a lot of operations on the value, which is on the right of the equals =.
BUT you canNOT do operations on the left of the equals = when you simply assigning a value to a variable. You can do almost any operations when you are testing 2 variables but you'll have a keyword like If or While at the start of the line of code.
So the issue with your code is that you reversed the order of the value and the variable and tried to do value * 1 = variable
Here is your corrected code (I indented it so that it can be read easily) :
Private Sub Workbook_Open()
xdate = Worksheets("sheet1").Range("A1")
lsdate = DateAdd("d", -7, Date)
'MsgBox lsdate
If Day(lsdate) > Day(xdate) Then
result = Worksheets("sheet1").Range ("B1") * 1
Else
result = Worksheets("sheet1").Range ("B1") * 1.07
End If
Sheets("Sheet_name").Range("c1").Value = result
End Sub
And you should always reference the sheet you are working with, because previously, your code didn't specify on which sheet the C1 was supposed to be.
So here, just change Sheet_name to whatever the name of your sheet is!

variables need to get the values from sheet. You are trying opposite way which is why you getting error.
Private Sub Workbook_Open()
xdate = Worksheets("sheet1").Range("A1")
lsdate = DateAdd("d", -7, Date)
'MsgBox lsdate
If Day(lsdate) > Day(xdate) Then
result = Worksheets("sheet1").Range("B1") * 1
Else
result = Worksheets("sheet1").Range("B1") * 1.07
End If
Range("c1").Value = result
End Sub
Sub test1()
Dim var1 As Variant
' If you need to get values from the range("D1") Then use the below code
var1 = Worksheets("sheet1").Range("D1").Value
End Sub

Related

Add month to previous cells date For each loop

All,
I have written a little procedure which I would like a for each loop to insert the current month into the first cell "01"/MM/YY and then add one month to the date as it goes through the loop. Using the example below;
K1 = 01/06/2018
L1 = 01/07/2018
M1 = 01/08/2018 etc
The code I am using is below - The error is on the DateAdd line.
Sub test()
Dim dt As date
dt = "01/" & Application.Text(Now(), "MM/YY")
Dim i As Double
i = 1
For Each c In Range("K1:XFD1")
If c.Value = "" Then Exit For
c.Value = dt
'change date to one months time
dt = DateAdd(m, i, dt)
i = i + 1
Next c
End Sub
Any help regarding this would be much appreciated.
Put "m" not m. m would be a variable. "m" is a literal string representing the argument being "month".
dt = DateAdd("m", i, dt)

getting string output of 1900 from the date input vba

I am trying to keep the date of some event occurrence. I am having trouble with the way my data has been defined. Instead of showing the actual date the output is showing in 1900 format (like 10/1/1900)
Here is my code:
Dim Arr_rate As Date
Dim D(528) As Date
for i = 3 to 500
Arr_rate = -3.7 * Log(Rnd) '<~~ Arrival interval
D(2) = CDate(1 / 1 / 2006)
D(i) = D(i - 1) + Arr_rate
Worksheets("A").Cells(i, "E").Value = Cdate(D(i))
next i
I tried to have the sdate format in my code but it will not give me the numbers in a proper format (1/1/2006). How can I print in the proper format? I have read some post in stackover flow but was not helpful!
Without quotes 1\1\12006 is just a mathematical expression and not a date.
Sub test()
Dim Arr_rate As Date
Dim D(528) As Date
For i = 3 To 500
Arr_rate = -3.7 * Log(Rnd) '<~~ Arrival interval
'<~~ Using Cdate, then use string. Otherwise 1 / 1 / 2006 evaluates to 4.98504486540379E-04
D(2) = CDate("1 / 1 / 2006")
D(i) = D(i - 1) + Arr_rate
Worksheets("Sheet1").Cells(i, "E").Value = CDate(D(i))
Next i
End Sub

A function returns a MsgBox 10 times?

Found a function on Excelguru which I changed a few things in and gonna edit some more. The idea is to use this to register worked hours and minutes.
There is one thing in this I don't understand: if I type the wrong time in the column reff I get a msg that its wrong, but it wont disappear unless I click it 10 times. I cant see what Im doing wrong. The entire code is posted and Im grateful for any help.
Use his function as part of the formula in the sheet like: TimeValue($E2;$F2;"16:00";"18:00";B2;9;C2)
Function TimeValue(FromTime As String, ToTime As String, StartTime As String, StopTime As String, Optional Weekday As String, Optional Daynr As Integer, Optional Holiday As String)
Dim x As Long
Dim F As Double
Dim T As Double
Dim Start As Double
Dim Stopp As Double
Dim Min As Long
Dim Day As Integer
Dim OverMid As Boolean
Select Case LCase(Weekday)
Case "mandag"
Day = 1
Case "tirsdag"
Day = 2
Case "onsdag"
Day = 3
Case "torsdag"
Day = 4
Case "fredag"
Day = 5
Case "lordag"
Day = 6
Case "sondag"
Day = 7
Case "x"
Day = 8
Case Else
Day = 0
End Select
OverMid = False
If LCase(Holiday) = "x" Then Day = 8
If Len(FromTime) = 0 Or Len(ToTime) = 0 Then
Exit Function
End If
If Len(FromTime) <> 5 Then
MsgBox ("Use format TT:MM - From time is wrong:" & FromTime)
Exit Function
End If
If Len(ToTime) <> 5 Then
MsgBox ("Use format TT:MM - To time is wrong:" & ToTime)
Exit Function
End If
F = Val(Left(FromTime, 2)) * 60 + Val(Right(FromTime, 2))
T = Val(Left(ToTime, 2)) * 60 + Val(Right(ToTime, 2))
Start = Val(Left(StartTime, 2)) * 60 + Val(Right(StartTime, 2))
Stopp = Val(Left(StopTime, 2)) * 60 + Val(Right(StopTime, 2))
If T = 0 Then T = 24 * 60
If T < F Then
T = T + 24 * 60
OverMid = True
End If
If Stopp = 0 Then Stopp = 24 * 60
For x = F + 1 To T
If x > Start And x <= Stopp Then
Min = Min + 1
End If
Next x
If OverMid = True Then
For x = 0 To Val(Left(ToTime, 2)) * 60 + Val(Right(ToTime, 2))
If x > Start And x <= Stopp Then
Min = Min + 1
End If
Next x
End If
'If weekday is set, equal to day
If Daynr <> 0 Then
If Daynr <> 9 Then
If Day <> Daynr Then Min = 0
End If
If Daynr = 9 And (Day > 5) Then
Min = 0
End If
End If
TimeValue = Min / 60
End Function
And the sub in the sheets
Private Sub Worksheet_Change(ByVal Target As Range)
Dim streng As String
Dim k As Long
Dim r As Long
k = Target.Column
r = Target.Row
If Cells(1, k) = "P" Then
If Cells(r, k) = "x" Then
Cells(r, 4) = "x"
Else
Cells(r, 4) = ""
End If
End If
End Sub
Message boxes really don't belong in UDFs (VBA functions meant to be used as spreadsheet functions).
Instead of the message box you could use code like:
If Len(FromTime) <> 5 Then
TimeValue = "Error! Use format TT:MM - From time is wrong:" & FromTime
Exit Function
Or perhaps:
If Len(FromTime) <> 5 Then
TimeValue = CVErr(xlErrValue)
Exit Function
This later will cause #VALUE! to display in the cell. Include enough documentation in your spreadsheet so that users can interpret such error values.

Piece of Code works as expected only when run multiple times

Currently I am debugging a piece of code. Currently my code works as intended it assigns a date of to the finaldate variable then looks in the code to delete all dates that are higher than the finaldate variable. Only problem is that the sub procedure needs to be run multiple times in order for this to take effect. For instance when I run through it once it removes about half of the dates, run through it again and it does the same, I usually F5 it about 5 times to confirm its complete. While this is fine in debugging I need to know this will work perfectly everytime.
Sub Remove_Unecessary_Data_1()
Dim ALLCs As Worksheet
Dim DS As Worksheet
Dim finaldate As Date
Set DS = Sheets("Data Summary")
Set ALLCs = Sheets("Asset LLC (Input)")
ALLCs.Select
For y = 1 To 40
If InStr(1, Cells(13, y), "Timestamp of Execution") Then
finaldate = ALLCs.Cells(50, y)
End If
Next
ALLCs.Select
For u = 1 To 40
If InStr(1, Cells(13, u), "Start Date") Then
For p = 2 To 69584
If Cells(p + 14, u) > finaldate Then
Cells(p + 14, u).EntireRow.Delete
End If
Next
End If
Next
end sub
EDIT: Sample Data
Cells(50,y) = 1/12/15
finaldate = Cells(50,Y)
the column headed Start date contains dates that range anywhere from 1/05/15 to 1/30/15.
When working properly all dates after 1/12/15 should have their entire row eliminated.
When deleting rows, you have to work your way from bottom to top, otherwise you end up skipping rows.
For example, you have:
Line 1
>Line 2
Line 3
Line 4
When your code deletes, Line 2, what was "Row" 3 now becomes "Row "2, but you code moves on to see Line 4. Your data now looks like this:
Line 1
Line 3
>Line 4
If you change this bit of your code:
For p = 2 To 69584
If Cells(p + 14, u) > finaldate Then
Cells(p + 14, u).EntireRow.Delete
End If
Next
to this:
For p = 69598 to 16 step - 1
If Cells(p, u) > finaldate Then
Cells(p, u).EntireRow.Delete
End If
Next
Everything should be fine.
*Note: I adjusted your start & end points up by 14, and removed the + 14 from the Cells() reference. No sense in doing the extra math in there...
When you delete a row using:
Cells(p + 14, u).EntireRow.Delete
the row below the deleted row moves up to occupy that space. If that row contains a date that should be deleted it will be ignored because the counter automatically moves onto the next row. For example, say we wish to delete any rows with C or D in the Data column:
Row Number Data
1 A
2 B
3 C
4 D
5 E
becomes:
Row Number Data
1 A
2 B
3 D
4 E
Tthe row counter moves onto 4 without checking the new value in 3 so the D will not be deleted.
You can solve this by changing your If...Then statement to a Do...While loop:
Sub Remove_Unecessary_Data_1()
Dim ALLCs As Worksheet
Dim DS As Worksheet
Dim finaldate As Date
Set DS = Sheets("Data Summary")
Set ALLCs = Sheets("Asset LLC (Input)")
ALLCs.Select
For y = 1 To 40
If InStr(1, Cells(13, y), "Timestamp of Execution") Then
finaldate = ALLCs.Cells(50, y)
End If
Next
ALLCs.Select
For u = 1 To 40
If InStr(1, Cells(13, u), "Start Date") Then
For p = 2 To 69584
Do While (Cells(p + 14, u) > finaldate)
Cells(p + 14, u).EntireRow.Delete
Loop
Next
End If
Next
End sub
This should keep checking that cell after it has deleted the previous row to ensure the replacement row should not be deleted also.
The fact that you delete a row while going increasingly in row's number, you will miss to analyze every rows right after the one you just delete because it (rows(i+1)) has become the rows(i) and yet you increased with the next.
Here is your code taking that into account (and got rid of the useless Select)
Sub Remove_Unecessary_Data_1()
Dim ALLCs As Worksheet, _
DS As Worksheet, _
FinalDate As Date
Set DS = Sheets("Data Summary")
Set ALLCs = Sheets("Asset LLC (Input)")
For y = 1 To 40
If InStr(1, ALLCs.Cells(13, y), "Timestamp of Execution") Then
FinalDate = ALLCs.Cells(50, y)
End If
Next
For u = 1 To 40
If InStr(1, ALLCs.Cells(13, u), "Start Date") Then
For p = 69584 To 2 Step -1
If Cells(p + 14, u) > FinalDate Then
Cells(p + 14, u).EntireRow.Delete
End If
Next
End If
Next
End Sub

VBA make named range arguments to a custom function refer to both a single cell and a range of cells

The percentrank function can take the same named range for both of its agruments like:
=percentrank(inputrange,inputrange)
It treats the first argument as a range of cells for calculating percent cutoffs, and uses the second argument to return a single cell's value to rank amongst the cutoffs. The single cell is determined by the row in which the function is entered/called from.
I want to recreate this functionality and use the second range reference to find a single cells value (based on the row the function is entered into). Here's what I have:
Public Function QUARTILE_RANK(DataRange As Range, RefCell As Range)
If RefCell <> vbNullString Then
q1 = Application.WorksheetFunction.Quartile(DataRange, 1)
q2 = Application.WorksheetFunction.Quartile(DataRange, 2)
q3 = Application.WorksheetFunction.Quartile(DataRange, 3)
q4 = Application.WorksheetFunction.Quartile(DataRange, 4)
If (RefCell <= q1) Then QUARTILE_RANK = 1
If (RefCell > q1) And (RefCell <= q2) Then QUARTILE_RANK = 2
If (RefCell > q2) And (RefCell <= q3) Then QUARTILE_RANK = 3
If (RefCell > q3) Then QUARTILE_RANK = 4
Else
QUARTILE_RANK = vbNullString
End If
End Function
If I pass it the same named range for both arguments, it sees both ranges as arrays of cells. I want it to treat the first argument that way, but find a single cell value using the second argument. That is, I want it to mirror the functionality of the PERCENTRANK function.
EDIT:
Sean Chesire's input helped me get it working. I'd welcome any suggestions for improvement as I'm sure this same function has been written by others.
Here's the final form:
Public Function QUARTILE_RANK(DataRange As Range, RefRange As Range)
Dim refCell As Range
If RefRange.Rows.Count > 1 Then
Set refCell = RefRange.Cells(Application.Caller.Row, 1)
Else
Set refCell = RefRange ' maybe they only passed a cell reference
End If
If refCell <> vbNullString Then
q1 = Application.WorksheetFunction.Quartile(DataRange, 1)
q2 = Application.WorksheetFunction.Quartile(DataRange, 2)
q3 = Application.WorksheetFunction.Quartile(DataRange, 3)
q4 = Application.WorksheetFunction.Quartile(DataRange, 4)
If (refCell <= q1) Then QUARTILE_RANK = 1
If (refCell > q1) And (refCell <= q2) Then QUARTILE_RANK = 2
If (refCell > q2) And (refCell <= q3) Then QUARTILE_RANK = 3
If (refCell > q3) Then QUARTILE_RANK = 4
Else
QUARTILE_RANK = vbNullString
End If
End Function
this will tell you which cell the formula was called from:
Function testit() As String
testit = Application.Caller.Parent.Parent.Name & " " & Application.Caller.Parent.Name & " " & Application.Caller.Address
End Function
You should then be able to test the ranges given, and work out which value should be referenced in the other array
=LOOKUP(PERCENTRANK(vals,vals),{0,0.25,0.5,0.75},{1,2,3,4})