Excel VBA: delete row after specific password is entered - excel

I have a sheet filled with booking numbers and associated data. In VBA I already have the code ready to delete a booking number with its associated row.
Some of these booking numbers shouldn't be allowed to be removed and I would like a password (tied to this booking) to be used in order to remove it.
Here is the delete code I have:
Private Sub btn_verwijderen_Click()
' try to retrieve the product by ID
Dim rngIdList As Range, rngId As Range
Set rngIdList = ActiveSheet.Range([B2], [B2].End(xlDown))
Set rngId = rngIdList.Find(Me.txtBookingnr, LookIn:=xlValues)
If rngId Is Nothing Then
' bookingnumber is not found
Exit Sub
Else
If MsgBox("you are about to delete: " & "Booking:" & Me.txtBookingnr & ". Are you sure?", vbYesNo) = vbYes Then
If MsgBox("You can't undo this process. Sure to delete?", vbYesNo) = vbYes Then
Sheets("Invoer").Range("Tabel133").Find(Me.txtBookingnr.Value).Delete
Sheets("Gastenlijst_vertrekkers").Columns(2).Find(Me.txtBookingnr, , , , 1).EntireRow.Delete
Sheets("Gastenlijst").Columns(2).Find(Me.txtBookingnr, , , , 1).EntireRow.Delete
MsgBox "Booking is deleted. Refresh update now (automatically)"
Call updatePlanning_Click
Call btn_cancel_Click
Else
MsgBox "Nothing changed"
End If
End If
End If
End Sub
Now, this code works for the rows that the user IS allowed to delete. But for the numbers the user CANNOT delete, should be protected by a password they need to fill in, in order to officially delete the row.
So for example:
Column B = Booking#
Column C = Initials
Column D = Surname
Column E = Checkin date
Column F = Checkout date
etc. etc. etc.
Let's say that Booking number: 1800123 is canceled and need to be removed from the list. I hit the button: Remove (btn_verwijderen) and I will get the first MsgBox. After hitting YES, there should be a new message asking for a password. After entering the correct password, the booking should be removed. BUT the password is tied to a specific booking.
Hopefully, some of you guys know how to achieve this.
Thanks in advance for your help.

Here is some pseudo code that illustrates one approach. In your main sub, you need to state what booking you want to delete. You then need to check to see if that booking requires a password.
The function Password will return either TRUE (okay to delete) or FALSE (not okay to delete) to your sub. Therefore, your delete test needs to be based around the output of this function, which is illustrated in Sub Example.
The Function Password output possibilities are as follows:
TRUE: If booking number to delete does not exist in the array var (no password required)
TRUE: If booking number to delete does exist in the array and the correct password is entered
FALSE: If booking number to delete does exist in the array and the wrong password is entered
I recomend you test this code out as is to understand how it works. Then build it into your main sub. You should really only need to modify the array values in the function. The bulk of the work will be building your delete statements around the result of this function
Notice I am passing the booking number 2 into the function manually. You need to swap the 2 for your variable booking number.
Option Explicit
Sub Example()
If Password(2) Then 'If function returns TRUE
MsgBox "Add Delete Code Here"
Else 'If function returns FALSE
MsgBox "Incorrect Password"
End If
End Sub
Private Function Password(Booking As Long) As Boolean
Dim var As Variant, i As Long, PW As String
var = [{1, 2, 3, 4, 5; "Password1", "Password2", "Password3", "Password4", "Password5"}]
For i = LBound(var, 1) To UBound(var, 2)
If Booking = var(1, i) Then
PW = Application.InputBox("Password required to delete booking: " & Booking, "Password Protected", Type:=2)
If PW = var(2, i) Then
Password = True
Exit Function
Else
Password = False
Exit Function
End If
End If
Next i
Password = True
End Function
Disclaimer
Anyone with VBA knowledge will be able to view any passwords you store using this method. You can tighten the security by password protecting your VBA Project, however, this is still not 100% dummy proof. There are ways, which can be found on this site, that go into great detail about how to break these passwords.

Related

VBA Inputbox to Prevent Decimals

I am trying to develop a tool that will help standardize a description catalog of products. I want to have an input box prompt a user to enter a size. I want to encourage size entries like "5-1/2" and prevent users from entering "5.5". Ideally, if the size was entered with a decimal and not a dash with a fraction, I want a message box to pop up saying they can not do that. It would then need to re-show the input box.
Here is what I have -
Private Sub CS_Other_Click()
Unload Me
Sheets("Fill In").Activate
Worksheets("Fill In").Range("C2").NumberFormat = "#"
Dim other_casing_size As Variant
other_casing_size = InputBox("Fill in the casing size. Syntax MUST be in the form of X-X/X", "New Casing Size")
Range("C2") = other_casing_size
I just dont know the code to prevent an entry with decimals. Even better, if i knew how to code an exact syntax to include or exclude anything I wanted that would be perfect.
Thanks
A while loop, which checks the input string for a dot or comma would work quite ok, I guess:
Sub TestMe()
Dim inputString As String
Dim inputNumeric As Boolean
inputString = InputBox("Please, enter a number!")
inputNumeric = isNumeric(Evaluate(inputString))
Do While InStr(1, inputString, ".") Or _
InStr(1, inputString, ",") Or _
Not inputNumeric
If Not CBool(inputNumeric) Then
MsgBox "You tried to cancel or entered empty value!"
Exit Do
End If
MsgBox "Please, do not write dot or comma!"
inputString = InputBox("Please, enter a number!")
inputNumeric = isNumeric(Evaluate(inputString))
Loop
End Sub
The isNumeric() checks the input for being able to be converted to numeric. Thus 5-1/2 should be ok.
Concerning cancellation or entering empty value from the InputBox() - it really depends on the business logic of the "app", but in the case above - there is a msgbox and it exits the loop.
Write a separate function responsible for that prompt, and use it e.g. like this:
Dim casingSize As String
If GetCasingSize(casingSize) Then
ActiveSheet.Range("C2").Value = casingSize
End If
The function needs to return a Boolean for this to work - it returns True if the input is valid, False if there's no valid input to work with (e.g. prompt was cancelled). What makes this work, is passing the result as a ByRef argument, like this:
Public Function GetCasingSize(ByRef outResult As String) As Boolean
Do
Dim raw As Variant
raw = InputBox("Casing size?")
If VarType(raw) = vbBoolean Then
'handle cancelled prompt:
Exit Do
End If
If ValidateFractional(raw) Then
'handle valid input:
outResult = CStr(raw)
GetCasingSize = True
Exit Do
End If
'handle invalid input:
If MsgBox("The value '" & raw & "' is not valid. Try again?", vbYesNo) = vbNo Then
Exit Do
End If
Loop
End Function
Note the ValidateFractional function is its own concern - a separate, Private function would work, but I'd recommend making it Public, and unit-testing it to make sure it works as intended given a wide variety of edge-case inputs - and having it in a separate function means the logic in GetCasingSize doesn't need to change if the validation needs to be fine-tuned; for example this naive implementation uses the Like operator and would work for 5-1/4, but not for e.g. 15-5/8:
Public Function ValidateFractional(ByVal value As String) As Boolean
ValidateFractional = value Like "#[-]#/#"
End Function
Using Regular Expressions for this would probably be a good idea.

How to reduce IF statements for multiple option buttons

I have a UserForm which lets the user input a count of product defects into a textbox. This is done as part of monthly reporting, so I have option buttons to select the Month (12 options). There are also option buttons for selecting Product Type. The code basically evaluates what options are selected and copies the textbox values (defect counts) into specific cells in another spreadsheet (for reporting purposes). Not all TextBoxes are required to have values entered by the User.
You can check out a screenshot of the UserForm https://imgur.com/a/6QefjCp.
As you can see from the code, I'm using a bunch of IF statements to perform the decision making - I would like to reduce the length of this code, but I don't know where to start.
I have never really used VBA prior to this, so haven't really attempted a solution. In its current state, the code works flawlessly. Just looking to reduce and clean-up.
Private Sub OKButton_Click() 'This is the button the user clicks to finalize
'the data entry
'Calling the Product type modules
Call Product1Module
Call Product2Module
Call Product3Module
End Sub
Sub Product1Module() 'All product modules will look almost exactly like this
'except the cell ranges will be different
If UserForm.Product1Button.Value = True Then 'Checking for Product1 Option button
If UserForm.JANButton.Value = True Then
'Record value to textbox if JAN is selected
Sheets("Sheet1").Range("B1107").Value = UserForm.TextBox1.Value
Sheets("Sheet1").Range("B1115").Value = UserForm.TextBox2.Value
Sheets("Sheet1").Range("B1108").Value = UserForm.TextBox3.Value
Sheets("Sheet1").Range("B1116").Value = UserForm.TextBox4.Value
Sheets("Sheet1").Range("B1109").Value = UserForm.TextBox5.Value
Sheets("Sheet1").Range("B1117").Value = UserForm.TextBox6.Value
Sheets("Sheet1").Range("B1111").Value = UserForm.TextBox7.Value
ElseIf UserForm.FEBButton.Value = True Then
Sheets("Sheet1").Range("C1107").Value = UserForm.TextBox1.Value
Sheets("Sheet1").Range("C1115").Value = UserForm.TextBox2.Value
Sheets("Sheet1").Range("C1108").Value = UserForm.TextBox3.Value
Sheets("Sheet1").Range("C1116").Value = UserForm.TextBox4.Value
Sheets("Sheet1").Range("C1109").Value = UserForm.TextBox5.Value
Sheets("Sheet1").Range("C1117").Value = UserForm.TextBox6.Value
Sheets("Sheet1").Range("C1111").Value = UserForm.TextBox7.Value
...
End If
End If
End Sub
Give each of your option buttons a Tag property value - e.g. make JANButton.Tag be "B", then make FeBButton.Tag be "C", etc.
Then you can do this:
Dim targetColumn As String
Select Case True
Case UserForm.JANButton
targetColumn = UserForm.JANButton.Tag
Case UserForm.FEBButton
targetColumn = UserForm.FEBButton.Tag
'...
End Select
With Worksheets("Sheet1") '<~ which workbook is that in? whatever is active?
.Range(targetColumn & "1107").Value = UserForm.TextBox1.Value
.Range(targetColumn & "1115").Value = UserForm.TextBox2.Value
'...
End With

LotusNotes 8.5 - Adding a row to a table with a button

I am an intern and learning LotusNotes currently, so am not very fluent with it yet.
My question is, how can I program an action button to add a row to an existing table in LotusNotes 8.5?
I have tried the following code, but it has not worked for me,
Sub Click(Source As Button)
Dim uiw As New NotesUIWorkspace
Dim uidoc As NotesUIDocument
Set uidoc = uiw.CurrentDocument
Dim doc As NotesDocument
Set doc = uidoc.Document
Dim Body As NotesRichTextItem
Set body = doc.GetFirstItem("Body")
If body Is Nothing Then
Msgbox "Click on the Reset the demo action to create the table first."
End If
Dim rows As Integer, rownumber As Integer, numrowstoadd As Integer
Dim strrownumber As String, strrowstoadd As String
Dim rtnav As NotesRichTextNavigator
Set rtnav = body.CreateNavigator
Dim rttable As NotesRichTextTable
Set rttable = rtnav.GetFirstElement(RTELEM_TYPE_TABLE)
If rttable Is Nothing Then
Msgbox "No table created - use the Reset the demo action first."
Else
rows=rttable.RowCount
strrowstoadd = Inputbox("Enter the number of rows to add.")
If Isnumeric( strrowstoadd ) Then
numrowstoAdd = Cint(strrowstoAdd)
If numrowstoAdd <= 0 Then
Msgbox "Enter a number greater than zero."
Exit Sub
End If
Else
Msgbox ("Enter a integer number only.")
Exit Sub
End If
strrownumber = Inputbox("Enter the number corresponding to the row to start adding at, no greater than " & rows & ".")
If Isnumeric( strrownumber ) Then
rownumber = Cint(strrownumber)
If rownumber < 0 Or rownumber > rows Then
Msgbox ("You entered too high a number or a number less than zero, try again.")
Exit Sub
End If
Else
Msgbox ("Enter a integer number only.")
Exit Sub
End If
Call rttable.AddRow(numrowstoadd, rownumber)
End If
doc.save True, True
uidoc.Close
Call uiw.EditDocument(False,doc)
End Sub
Any help would be great. Thanks!
Without taking a detailed look at your code, I believe the fundamental problem you are facing is most likely the fact that the NotesRichText class is part of what we call the "back end classes" for Notes. That means that it is one of the objects that represents the in-memory version of data from an NSF file in its storage format, and this is not the same as the "front end classes". Those are objects that represent the data that the user sees and edits. You can tell the front end from back end classes by the prefix NotesUI for all the front end classes.
The thing is, the objects in the front end classes and back end are kept synchronized except for rich text, and what that means is that changes that you make to NotesRichText objects do occur in memory, and the are saved to the NSF file if you call NotesDocument.save(), but they are not reflected in what you see on the screen until you do something to reload the front end data from the back end. Here's a link to a wiki page that demonstrates a technique for doing that.
You wrote "but it has not worked for me". I tried your code and it works. I suggest you just few changes in order to make it work better:
close the doc before working with the table in the RT (back end) just before Dim Body As NotesRichTextItem
uidoc.save 'to save any change done
doc.saveoptions = "0"'to avoid do you want to save
uidoc.Close True
in place of the 3 last lines:
doc.Save True, True
Call uiw.EditDocument(True,doc)
NB you have to add Exit Sub after "Click on the Reset the demo action to create the table first" and after "No table created"

How to dynamically obtain numeric part of userform control name - VBA

I have 2 small lists of combo boxes. The first list is labled Type(1-5). The second list is labled Products(1-5). I want to populate each PRODUCT box pending on the selection made in the corresponding TYPE box. I am currently doing the following...
Private Sub Type1_Change()
NavComboPropChange
End Sub
Sub NavComboPropChange()
If BaseActiveControl.Name = "AVM" Then
= Worksheets("Setup").Range("AVM").Value
ElseIf BaseActiveControl.Name = "Appraisal" Then
= Worksheets("Setup").Range("APPRAISAL").Value
Else
= Worksheets("Setup").Range("TITLES").Value
End If
End Sub
BaseActiveControl.name grabs the root control element currently selected. Before the equal sign in the IF, ElseIf, Else sequence would be the product name and the corresponding value.
To restate my question though, I want to know how I can grab the numeric part of the control name to use in conjunction with the product box name.
I found the solution using this route.
Sub NavComboPropChange()
Dim myString As String
myString = Right(BaseActiveControl.Name, Len(BaseActiveControl.Name) - 4)
If BaseActiveControl.Value = "AVM" Then
Controls("Products" & myString).List = Worksheets("Setup").Range("AVM").Value
ElseIf BaseActiveControl.Value = "Appraisal" Then
Controls("Products" & myString).List = Worksheets("Setup").Range("APPRAISAL").Value
Else
Controls("Products" & myString).List = Worksheets("Setup").Range("TITLES").Value
End If
End Sub

Excel VBA Textbox resets to 0 when i press a button

i have a userform with 2 textboxes, 2 labels and a log in button.
On my excel sheet i have a sort of database with id, name, pin and balance.
the problem is whenever i click the login button my ID textbox resets its value to 0, but my pin textbox works fine!
i will paste my complete code:
Dim ID As Integer
Dim PIN As Integer
Dim PINField As String
Dim Balance As Double
Dim Attempts As Integer
Dim BalanceField As String
Private Sub btnLogin_Click()
txtID.Text = ID
Call SetId
Call Authenticate
End Sub
Sub Authenticate()
If txtPin.Text = PIN Then
Call Welcome
ElseIf Attempts > 3 Then
Call Bye
Else
lblWelcome.Caption = "Wrong Pin"
lblWelcome.ForeColor = RGB(255, 0, 0)
Attempts = Attempts + 1
End If
End Sub
Sub SetId()
PINField = "C" & Str(ID)
PINField = Replace(PINField, " ", "")
MsgBox (PINField)
BalanceField = "D" & Str(ID)
BalanceField = Replace(BalanceField, " ", "")
MsgBox (BalanceField)
End Sub
Sub Welcome()
MsgBox ("Login Successful. Welcome")
End Sub
Sub Bye()
MsgBox ("Max Pin Attempts reached. Contact Your Bank")
Unload frmLogin
End Sub
The reason it does this is because you are using a variable which has no value. Since it is an Integer it returns 0.
I'm guessing you probably actually want to have ID = txtID.Text - that is, take the value of the txtID textbox and store the value in the ID variable.
This will probably error though because the Text property of a textbox is a String. You will need to use ID = CInt(txtID.Text). You should also do some checking to make sure that txtID.Text evaluates to an Integer before assignment.
Please make sure there's no reset for the txtID anywhere in the code that you have not shown here. Looking at your code, it doesn't say anything how you are setting values to either ID or PIN... You said it's working fine for PIN, so it makes me very curious...
It could be the case Nick pointed out given this is a Form with textboxes allowing people to enter ID and PIN.. And then you are comparing it against PIN. But what are you comparing against? As you said you have a database kind of a structure in the sheet. You need to assing ID and PIN using it.
Here is the visualization I have for your Sheet, which is my best blind guess:
User needs to enter a value via the Form into txtID. That number is infact the cell number for column C which contains the relevant PIN. Then you compare that PIN with the txtPIN value. Next return the balance from column D based on that PIN.
Try this:
Private Sub btnLogin_Click()
If txtID.Text <> "" Or txtID.value > 0 or txtPIN.Text <> "" Then
ID = CInt(txtID.Text)
Call SetID
Call Authentication
Else
MsgBox "ID and PIN" can't be empty!"
End If
End Sub
Sub Authenticate()
If CInt(txtPin.Text) = PIN Then '-- here
Call Welcome
'-- idealy Blance can be shown at this point...
ElseIf Attempts > 3 Then
Call Bye
Else
lblWelcome.Caption = "Wrong Pin"
lblWelcome.ForeColor = RGB(255, 0, 0)
Attempts = Attempts + 1
End If
End Sub
Sub SetId()
PIN = CInt(Trim(Sheets(1).Range("C" & ID).value))
'-- NOT sure why you are showing this PIN here since you want to authenticate...?
MsgBox PIN
BalanceField = Sheets(1).Range("D" & ID).value
BalanceField = Trim(BalanceField) '--here
'-- doesn't make sense to show Balance before authentication...
MsgBox BalanceField
End Sub
Trim is clearner and faster than Replace..

Resources