VBA_Processing a value as 29160012040000TZ - vba

I created a couple of user forms which operate a data in separate report workbook. My script can successfully proceed a value in digit type. Unfortunately the circumstances have changed and now it has to work with a Serial Numbers as: 29160012040000TZ. With that new value script after starting the Sub, open a report, but it never enter into a 'with' statement. It doesn't look for a value or doing something else. Just open a report workbook and freeze.
Below you can see the code lines where issue is present and a little description:
Single_PHA is a text window in User Form where user can enter a a value, proceeding value is 29160012040000TZ
Private Sub Wydaj_button_Click()
Workbooks.Open Filename:="N:\ENGINEERING\1. ENGINEERS\Mateusz Skorupka\PHA_Cleaning_report_path\PHA_CLEANING_REPORT.xlsm", ReadOnly:=False
Dim REPORT As Workbook
Set REPORT = Application.Workbooks("PHA_CLEANING_REPORT.xlsm")
Set TABLE = REPORT.Worksheets("Main_table")
...
With TABLE.Range("A1")
If Single_PHA = True Then
If Not IsError(Application.Match(Single_PHA.Value, .Range("A:A"), 0)) Then
Single_PHA_row = TABLE.Range("A:A").Find(What:=Single_PHA.Value, LookIn:=xlValues).Row
.Offset(Single_PHA_row - 1, 4).Value = Date
REPORT.Close SaveChanges:=True
Single_PHA.Value = ""
Exit Sub
Else
MsgBox "Numer seryjny głowicy nie istnieje w bazie"
REPORT.Close SaveChanges:=False
Exit Sub
End If
End If
End With
In VBA I don't know how to open something like debugger or make the print instruction which would show me how the variables look on specific steps.
I am not sure if VBA read the value as 29160012040000TZ as string. I tried to declare at the beginning a variable as Single_PHA_STR as String and the proceed it as just text, but no wins there:
Dim Single_PHA_STR As String
...
With TABLE.Range("A1")
If Single_PHA = True Then
Single_PHA_STR = Str(Single_PHA.Value)
If Not IsError(Application.Match(Single_PHA_STR, .Range("A:A"), 0)) Then
Single_PHA_row = TABLE.Range("A:A").Find(What:=Single_PHA_STR, LookIn:=xlValues).Row
.Offset(Single_PHA_row - 1, 4).Value = Date
REPORT.Close SaveChanges:=True
Single_PHA.Value = ""
Exit Sub
Else
MsgBox "Numer seryjny głowicy nie istnieje w bazie"
REPORT.Close SaveChanges:=False
Exit Sub
End If
End If
End With
I noticed that if in VBA IDE I write a bold value 29160012040000TZ, I get an error
Expected line number or label or statement or end of statement
and the value is highlighted in red.
Could someone help me in that field and explain the nature of issues:
To reproduce a situation you can create a simply user form with one TextBox and one CommandButton. In the same worksheet as user form in a column A put a values: 29160012040000 and 29160012042027IR
Then make a sub which execute after double click on command button with code:
Private Sub CommandButton1_Click()
With Worksheets("Sheet1").Range("A1")
If Text_box1 = True Then
If Not IsError(Application.Match(Text_box1.Value, .Range("A:A"), 0)) Then
Text_box1_row = Worksheets("Sheet1").Range("A:A").Find(What:=Text_box1.Value, LookIn:=xlValues).Row
.Offset(Text_box1_row - 1, 4).Value = Date
Text_box1.Value = ""
Exit Sub
Else
MsgBox "PHA SN not exist in a database"
Exit Sub
End If
End If
End With
End Sub
Then try to input in a UserForm's TextBox a value = 29160012040000 and you will see that script successfully filled a forth column in row with current date. Then try to input a value 29160012042027IR and you will see that nothing happened. Script don't proceed that value at all.
So that is my issue and question indeed. How to process a value with letters at the end like: 29160012042027IR : )
I also tried to focus a script statement on one specific cell in which is a text value "29160012042027IR" that which I input into a UserForm TextBox. Looking with a debugger both of variables in if statement have the same text value, but still script miss that statement and go to else instructions : (
I mean abut: If Range("A3").Text = Text_box1.Text Then
When I change a statement for "If Range("A3").Value = Text_box1.Value Then" the same thing happen.
Private Sub CommandButton1_Click()
With Worksheets("Sheet1").Range("A:A")
If Text_box1 = True Then
If Range("A3").Text = Text_box1.Text Then
Text_box1_row = Worksheets("Arkusz1").Range("A:A").Find(What:=Text_box1.Value, LookIn:=xlWhole).Row
.Offset(Text_box1_row - 1, 4).Value = Date
Text_box1.Value = ""
Exit Sub
Else
MsgBox "PHA SN not exist in a database"
Exit Sub
End If
Else
MsgBox "Other loop"
End If
End With
End Sub
IMPORTANT NOTICE:
I found the main issue. I made wrong if condition, it should be:
If Single_PHA <> "" Then previously I have got: If Single_PHA = True Then, and there the results is a value not the boolean type.
Everything works. Thank everyone very much for help.
Topic is ready to be closed.
PS: thank you Tom for suggestion and tip with debugger: )

Related

My VBA for loop in MS word 2016 is not working

I having trouble with the following code in MS word VBA
For i = 6 To ActiveDocument.Tables(2).Rows.Count
z = Len(ActiveDocument.Tables(2).Cell(i, 2).Range.Text) - 2
x = Len(ActiveDocument.Tables(2).Cell(i, 3).Range.Text) - 2
b = ActiveDocument.Tables(2).Cell(i, 5).Range.ContentControls.Item(1).ShowingPlaceholderText
If (z = 0 And x = 0) Then
If b = True Then
MsgBox "Please do error 1!"
If vbOK Then
Exit Sub
End If
Else
MsgBox "Please do error 2!"
If vbOK Then
Exit Sub
End If
End If
Else
If b = True Then
MsgBox "Please do error 3!"
If vbOK Then
Exit Sub
End If
Else
Confirm = MsgBox("Are you sure to submit?", vbYesNo, "Confirmation")
If Confirm = vbNo Then
Exit Sub
End If
End If
End If
Next i
The for loop won't go into the second line to check if z or x is having value or not
I doubt moving Next i would have solved anything. This code is riddled with badness.
My impression is that your code is intended to check three columns in a table (from rows 6 downwards) - this appears to be a consistency check.
Naming. z, x and b are not very descriptive. Using names like lengthCol2, lengthCol3 and hasPlaceHolderText will help you follow your logic more closely.
Use Option Explicit. Always.
You use a standard MsgBox call, which by default only has a single button ("OK"). The MsgBox is a blocking code element, so the macro will not progress until the user has clicked "OK".
vbOK is an enumerated value (value = 1). So If vbOK then always comes out true. Always. You appear to be seeking some sort of user input, but you are not clear on what that input is.
Address these simple steps gives us:
For i = 6 To ActiveDocument.Tables(2).Rows.Count
lengthCol2 = Len(ActiveDocument.Tables(2).Cell(i, 2).Range.Text) - 2
lengthCol3 = Len(ActiveDocument.Tables(2).Cell(i, 3).Range.Text) - 2
hasPlaceHolderText = ActiveDocument.Tables(2).Cell(i, 5).Range.ContentControls.Item(1).ShowingPlaceholderText
If (lengthCol2 = 0 And lengthCol3 = 0) Then
If hasPlaceHolderText = True Then
MsgBox "Please do error 1!"
Exit Sub
Else
MsgBox "Please do error 2!"
Exit Sub
End If
Else
If hasPlaceHolderText = True Then
MsgBox "Please do error 3!"
Exit Sub
Else
Confirm = MsgBox("Are you sure to submit?", vbYesNo, "Confirmation")
If Confirm = vbNo Then
Exit Sub
End If
End If
End If
Next i
Your logic is negative-biased - that is, intending to find reasons not to do something than to do something. Positive-biased logic is usually easier to understand and maintain - the coder's intent is clearer.
Rewording the logic gives us:
For i = 6 To ActiveDocument.Tables(2).Rows.Count
lengthCol2 = Len(ActiveDocument.Tables(2).Cell(i, 2).Range.Text) - 2
lengthCol3 = Len(ActiveDocument.Tables(2).Cell(i, 3).Range.Text) - 2
hasPlaceHolderText = ActiveDocument.Tables(2).Cell(i, 5).Range.ContentControls.Item(1).ShowingPlaceholderText
If (lengthCol2 > 0 OR lengthCol3 > 0) AND hasPlaceHolderText Then
Confirm = MsgBox("Are you sure to submit?", vbYesNo, "Confirmation")
If Confirm = vbYes Then
'Do submission code here - or call the submission procedure
End If ' Just do nothing if they say "No" - this is what your current code does.
Else
' The next line could be used instead of the nested IF-the-else statements following.
'MsgBox " Table contents are not valid, please ensure columns 2,3 and 5 are completed"
If hasPlaceHolderText then
If (lengthCol2 = 0 And lengthCol3 = 0) Then
MsgBox "Please do error 1!"
Else
MsgBox "Please do error 2!"
EndIF
Else
MsgBox "Please do error 3!"
End If
End If
Next i
Note that in your logic, either Column 2 or Column 3 can be empty and (as long as placeholder text not being shown) you document is ready for submission. Perhaps you meant AND instead of OR (i.e. all columns should be filled).
There is still one problem. Your loop. As currently written, you loop over the logic, thus you ask the user to either check errors or submit the document x number of times based on error checking in each row. But, just moving the Next i does not solve the problem because the only results that are retained are those in the last row. In other words, all the previous rows could be bad/invalid, but you would still be able to submit.
We can fix this last bit by creating cumulative logic. In other words, we track the errors in a short loop, then go into the main logic. This seems to be a little more complex but it really is relative straight forwards. But, we do need more Booleans to make it work.
Dim rowsOK as Boolean
'explicit initialisation - I am working on a positive bias here.
rowsOK = True
For i = 6 To ActiveDocument.Tables(2).Rows.Count
Dim lengthCol2OK as Boolean ' Use these just to make the logic clearer and the code cleaner
Dim lengthCol3OK as Boolean
Dim hasPlaceHolderTextOK as Boolean
lengthCol2OK = Len(ActiveDocument.Tables(2).Cell(i, 2).Range.Text) > 2
lengthCol3OK = Len(ActiveDocument.Tables(2).Cell(i, 3).Range.Text) > 2
hasPlaceHolderTextOK = ActiveDocument.Tables(2).Cell(i, 5).Range.ContentControls.Item(1).ShowingPlaceholderText
rowsOK = rowsOK And ((lengthCol2OK Or lengthCol3OK) And hasPlaceHolderTextOK) ' Note: Using "Or" here as per original code logic
' Extra logic could go here to message the user if any of the above are false.
Next i
If rowsOK Then
Confirm = MsgBox("Are you sure to submit?", vbYesNo, "Confirmation")
If Confirm = vbYes Then
'Do submission code here - or call the submission procedure
End If ' Just do nothing if they say "No" - this is what your current code does.
Else
MsgBox " Table contents are not valid, please ensure columns 2,3 and 5 are completed"
End If
However, this logic works on all the rows, so identifying individual row errors in not possible in the main loop. You could work extra logic in the For-Next loop to message the user for errors.
Now the code is maintainable, and more likely does what you want.
Key points:
Use Option Explicit. This prevents typos and ensures that you are using variables in they way you intend.
Use meaningful variable names. Makes it easier to follow what you want to do.
Don't confuse enumerated values with returns from functions. Don't confuse constants with variables.
Take some time to review your logic chains to ensure they do what you want to do, rather than not do what you don't want to do. The latter has greater chance of missing a non-valid path.

Run time error 424 Object Required working with UserForm

I'm trying to link a user form I built in VBA editor for MS Excel 2010 to the data in an excel worksheet, but I'm getting a
run-time error 424: Object required.
I referred to the active worksheet explicitly in my code to try and remedy this, but it still comes up with the same error. My code is:
Private Sub GetData()
Dim r As Long
r = ActiveSheet.Range("B2").Value
If IsNumeric(RowNumber.Text) Then
r = CLng(RowNumber.Text)
Else
ClearData
MsgBox "Invalid row number"
Exit Sub
End If
If r > 1 And r <= LastRow Then
cboFilterResultId.Text = FormatNumber(Cells(r, 1), 0)
txtFolderPaths.Text = Cells(r, 2)
txtFileName.Text = Cells(r, 3)
txtDeletedDate.Text = Cells(r, 4)
txtReason.Text = Cells(r, 5)
txtcboAdd.Text = Cells(r, 6)
txtcboView.Text = Cells(r, 7)
txtcboChange.Text = Cells(r, 8)
DisableSave
ElseIf r = 1 Then
ClearData
Else
ClearData
MsgBox "Invalid row number"
End If
End Sub
Where RowNumber is a textbox where the user can enter the row number for the data they want.
Please help!
I rarely use ActiveSheet just in case that isn't the sheet I'm after. Generally better to be explicit which sheet you're referring to:
r=ThisWorkbook.WorkSheets("Sheet1").Range("B2")
Right, pulling data from a worksheet to a userform... as you haven't said which line your error occurs on and you haven't given us the code for ClearData or DisableSave I'll start from scratch.
Example Form Design
I create a blank userform and add three text boxes and a spin button to it:
txtRowNumber holds the row number that the data is pulled from.
TextBox1 and TextBox2 will hold my sample values.
In the Tag property of TextBox1 I enter 1 and in the Tag property of TextBox2 I enter 2. These are the column numbers that the data will be pulled from.
In reality I usually add extra stuff, for example, 8;CPER;REQD. I'd then use some code to pull it apart so it pastes in column 8, must have a percentage and is a required entry.
spnButton is used to quickly move up or down a row.
We'll need two procedures to populate the form from the given row number and to clear all controls on the form (ready for the next row to be brought in).
Any textbox or combobox that has something in it's Tag property is cleared:
Private Sub ClearForm()
Dim frmCtrl As Control
For Each frmCtrl In Me.Controls
If frmCtrl.Tag <> "" Then
Select Case TypeName(frmCtrl)
Case "TextBox", "ComboBox"
frmCtrl.Value = Null
Case Else
'Do nothing.
End Select
End If
Next frmCtrl
End Sub
Any control that has a Tag value (it's assumed the value is correct) is populated from the specified RowNumber and column (from the Tag value). The value is always taken from the sheet called MyDataSheet in the workbook containing the VBA code (ThisWorkbook) no matter which is currently active:
Private Sub PopulateForm(RowNumber As Long)
Dim frmCtrl As Control
For Each frmCtrl In Me.Controls
With frmCtrl
If .Tag <> "" Then
.Value = ThisWorkbook.Worksheets("MyDataSheet").Cells(RowNumber, CLng(.Tag))
End If
End With
Next frmCtrl
End Sub
Whenever txtRowNumber changes the form should update with values from the indicated row. To do this we'll need to clear the form of current data and then repopulate it:
Private Sub txtRowNumber_Change()
ClearForm
PopulateForm CLng(Me.txtRowNumber)
End Sub
The spin button should increase/decrease the value in .txtRowNumber. I've added checks that it doesn't go below 1. You should also add checks that it doesn't go higher than the last populated row.
Private Sub spnButton_SpinDown()
With Me
.txtRowNumber = CLng(.txtRowNumber) + 1
End With
End Sub
Private Sub spnButton_SpinUp()
With Me
If CLng(.txtRowNumber) > 1 Then
.txtRowNumber = CLng(.txtRowNumber) - 1
End If
End With
End Sub
Finally, the form should be populated when it is first opened:
Private Sub UserForm_Initialize()
With Me
.txtRowNumber = 2
.spnButton = .txtRowNumber
PopulateForm .txtRowNumber
End With
End Sub

Excel VBA UserForm 'OK'

Does anyone know how to make a userform function in the same way as the Message Box 'ok' button? I'll explain.
I'm detecting errors in a column in a spreadsheet. When an error is found, a message box pops up as follows:
MsgBox "Please enter valid data"
When I select "OK" it goes to the next error in the column. This is great, except of course a message box is modal, which freezes the application. I want the user to be able to edit the data and then move to the next error. So, I designed a userform, which can be non-modal. Great, except I want the macro to advance to the next error. It will do that IF the user corrects the error. If they do not, it just stays at that error cell.
I know WHY this happens. My userform 'Next' button just calls the macro which finds the first error. But what I want to know is if there is a way around this.
Error checking starts at row 19 because that is where user input data starts.
I'm including a link to the spreadsheet here. Module 1 'NextValidationError' works great and proceeds to the next error. Module 14 just hangs at the error until it is resolved. I'd like it to be able to skip.
https://www.dropbox.com/s/yqko5kj19pnauc9/Transparency%20Data%20Input%20Sheet%20for%20Indirect%20Spend%20V7%2009212016%20v2%200.xlsm?dl=0
Can anyone give me advice on how to make module 14 proceed as module 1?
Something like this:
Dim r_start As Long
Sub CheckNames()
Dim r As Long
'Dim emptyRow As Boolean
If r_start = 0 Then r_start = 19
With ActiveSheet
For r = r_start To 5000
'Checks entire row for data. User may skip rows when entering data.
If WorksheetFunction.CountA(.Range(.Cells(r, 1), .Cells(r, 33))) > 0 Then
If ((.Cells(r, 2) = "") <> (.Cells(r, 3) = "")) Or _
((.Cells(r, 2) = "") = (.Cells(r, 4) = "")) Then
MsgBox "Please fill in First and Last Name or HCO in Row " & r & "."
End If
End If
Next
End With
End Sub
Unless I'm mis-reading your code you can combine your two checks with Or.
You will need some method to reset r_start when the user is done checking (if the form stays open after that).
EDIT: here's a very basic example.
UserForm1 has two buttons - "Next" and "Close"
Code for "next" is just:
Private Sub CommandButton1_Click()
ShowErrors
End Sub
In a regular module:
Dim r_start As Long
'this kicks off the checking process
Sub StartChecking()
r_start = 0
UserForm1.Show vbModeless
ShowErrors
End Sub
'a simple example validation...
Sub ShowErrors()
Dim c As Range, r As Long
If r_start = 0 Then r_start = 9
For r = r_start To 200
With ActiveSheet.Rows(r)
If Not IsNumeric(.Cells(1).Value) Then
UserForm1.lblMsg.Caption = "Cell " & .Cells(1).Address() & " is not numeric!"
r_start = r + 1
Exit Sub
End If
End With
Next r
r_start = 0
UserForm1.lblMsg.Caption = "No more errors"
End Sub

Edge cases in IsNumeric- is this overthinking it

I have code which looks like this:
Select Case IsNumeric(somevariable)
Case True
Resume Next
Case False
Call notnum
Else
Call MyErrorHandler
End Select
Is this overthinking it? Is there a chance IsNumeric will return something other than True or False here or is this bad programming practice?
Don't need the else as it will be true or false however, just a note the Else should be Case Else (moot point though as you are about to delete it)
Based on this though I wouldn't use a case for only 2 options:
If IsNumeric(somevariable) then
Resume Next
Else
Call MyErrorHandler
End if
Edit: Here is how error checking works:
Sub SheetError()
Dim MySheet As String
On Error GoTo ErrorCheck
MySheet = ActiveSheet.name
Sheets.Add
ActiveSheet.name = MySheet
MsgBox "I continued the code"
ActiveSheet.name = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
MsgBox "I will never get to here in the code"
End
ErrorCheck:
If Err.Description = "Cannot rename a sheet to the same name as another sheet, a referenced object library or a workbook referenced by Visual Basic." Then
Resume Next
Else
MsgBox "Error I am not designed to deal with"
End If
End Sub
Copy and paste this module to your personal workbook or to a new workbook and run it, step through line by line using F8 to see how it is actually dealing with the error.
From OP's comment I'm not using my error handler. I want to do stuff with the hopefully numeric output
Sub demo()
Dim inputs As Variant
inputs = InputBox("Prompt", "Title", "Default")
If Not IsNumeric(inputs) Then
notnum
Else
' Do what you want with numeric input inside the Else
End If
' Maybe do more stuff irrespective of input
End Sub
Sub notnum()
' do not numeric stuff here
End Sub
Or if you want to keep prompting for numeric input until the users gets it right or cancels
Sub demo2()
Dim inputs As Variant
Do
inputs = InputBox("Enter something Numeric", "Title", "Default")
Loop Until IsNumeric(inputs) Or inputs = vbNullString
If Not inputs = vbNullString Then
' Do wht you want with numeric input inside the Else
End If
' Maybe do more stuff irrespective of input
End Sub
Input box can have different type of input validation. Try this
something = Application.InputBox("Pls Insert the Number", Type:=1)
If something = False Then Exit Sub
'Type:=0 A formula
'Type:=1 A number
'Type:=2 Text (a string)
'Type:=4 A logical value (True or False)
'Type:=8 A cell reference, as a Range object
'Type:=16 An error value, such as #N/A
'Type:=64 An array of values

Evaluate excel concatenate formula only once

I am building a template/form in excel that will be used by different people on different computers to fill in some information and then send it by email to me.
When the template is being filled I need to assign an unique ID number to a field along with other info(kind of like a request ID). I am generating this unique ID by using
CONCATENATE("NER-";DEC2HEX(RANDBETWEEN(0;4294967295);8))
This formula serves me good for the task at hand.
My challenge is to evaluate this formula only one time in the template and then keep it the same when I open the file once it gets to me. Something along the lines of a time stamp. I have already looked into some methods but I cannot seem to get it to work.
I have tried making use of:
Private Sub Worksheet_Change(ByVal Target As Excel.Range)
With Target
If .Count > 1 Then Exit Sub
If Not Intersect(Range("A2:A10"), .Cells) Is Nothing Then
Application.EnableEvents = False
If IsEmpty(.Value) Then
.Offset(0, 1).ClearContents
Else
With .Offset(0, 1)
.NumberFormat = "dd mmm yyyy hh:mm:ss"
.Value = Now
End With
End If
Application.EnableEvents = True
End If
End With
End Sub
But I do not know how to integrate my concatenate function into the code. I am also not extremely sure if this will keep my unique value untouched when I open the template on my computer.
I would guess that a method that would limit my iterations in the entire sheet would also serve me good.
You could generate and store the ID right when the user first opens the workbook/template, placing this code in the ' ThisWorbook module:
Private Sub Workbook_Open()
'ID already set?
If Sheet1.Range("A2").Value <> "" Then Exit Sub
'Prevent that ID is generated on your machine
If Environ$("Username") = "YOURUSERNAME" Then Exit Sub
'Store ID
Sheet1.Range("A2").Value = _
"NER-" & [DEC2HEX(RANDBETWEEN(0,4294967295),8)]
End Sub