Secure Timestamp - VBA - vba

I want to create a secure timestamp on my Excel sheet. The VBA I am using will automatically add the current users user name, the time, and the date when a user puts information into column A. So if the users puts something into cell A1 then B1 automatically gets filled with their username and C1 gets filled with the time and date. The only problem is that this method isn’t secure because the user could alter the information after it is automatically populated. I would like to add code to this VBA so it will lock all three cells after the information is populated.
I was planning on using the Protect Sheet feature and only allowing users to “Select unlocked cells” So if the VBA could auto lock the cells then the users would not be able to alter the information.
Moreover I have used Me.Unprotect before changing cells and Me.Protect after that still it is not working
Any help would be much appreciated!

Assume that we start with all the cells on the worksheet unlocked and the sheet password-protected with the password:
6LgSdHjc2uOssv0e1LDI
The following Event Macro will:
unprotect the workbook
detect entries in column A
place the username in column B and the date/timestamp in column C
lock the entries in columns A,B,C
re-protect the worksheet.
This goes in the Worksheet code area:
Private Sub Worksheet_Change(ByVal Target As Range)
Dim A As Range, MyPass As String, sh As Worksheet
Dim unit As Range
Set A = Range("A:A")
MyPass = "6LgSdHjc2uOssv0e1LDI"
Set sh = ActiveSheet
If Intersect(Target, A) Is Nothing Then Exit Sub
Set unit = Union(Target, Target.Offset(0, 1), Target.Offset(0, 2))
Application.EnableEvents = False
sh.Unprotect (MyPass)
unit.Locked = False
Target.Offset(0, 1) = Environ("Username")
Target.Offset(0, 2) = Now()
unit.Locked = True
sh.Protect (MyPass)
Application.EnableEvents = True
End Sub
Because it is worksheet code, it is very easy to install and automatic to use:
right-click the tab name near the bottom of the Excel window
select View Code - this brings up a VBE window
paste the stuff in and close the VBE window
If you have any concerns, first try it on a trial worksheet.
If you save the workbook, the macro will be saved with it.
If you are using a version of Excel later then 2003, you must save
the file as .xlsm rather than .xlsx
To remove the macro:
bring up the VBE windows as above
clear the code out
close the VBE window
To learn more about macros in general, see:
http://www.mvps.org/dmcritchie/excel/getstarted.htm
and
http://msdn.microsoft.com/en-us/library/ee814735(v=office.14).aspx
To learn more about Event Macros (worksheet code), see:
http://www.mvps.org/dmcritchie/excel/event.htm
Macros must be enabled for this to work!

Related

VBA - copy specific rows from one sheet into another sheet when user closes file

I'm new to VBA and I'm struggling a lot with a file I want to build.
I have a main Sheet that in a simple way looks like this (starting at column B - A is an empty column):
main
This is a simplified version just for the example. The first table of the sheet varies from B13 to O92, the second varies from B104 to O114 but some of those rows might be empty.
My goal is to join rows with content from the first area with rows with content from the second area in a different sheet (Sheet1), add to the left a column with 1s and "Cell 0" (content of cell B1). Using the example, the result would be something like this:
Sheet1
Sheet1 will stay hidden as I'm using it as a source of information to a different file. In fact, I may not need the 1s column if I find a way to copy information in a different way - I'm doing it like this (wsSource is Sheet1):
lRow = wsSource.Columns("A").Find(1, SearchDirection:=xlPrevious, LookIn:=xlValues, LookAt:=xlWhole).Row
wsSource.Range("B1:N" & lRow).Copy
I was trying to do it so Sheet1 is "emptied" when the file is opened and edited when file is closed - so that if new rows are added or information updated, it gets into Sheet1 every time.
I've tried several stuff I found online but couldn't make it to work. My main problem is adding the specified rows one after the others but I'm also struggling to reset Sheet1 every time the file is opened and automatically running the macro when file is closed.
Any help would be really appreciated.
Hopefully this will get you started. Both subs need to be pasted in VBE under ThisWorkBook rather module or sheet(n).
The first sub will execute when the workbook is open.
Are you sure you want to clear your sheet under these circumstances?
You will never have access to your table (without workarounds) since it will clear when opening every time.
If this is what you need, see the below method to clear a dynamic range (Col A - D down to last cell used in Col A) on Sheet1 every time the workbook that houses this code is opened.
Private Sub Workbook_Open()
Dim ws As Worksheet: Set ws = ThisWorkbook.Sheets("Sheet1")
Dim ClearRange As Range
Application.ScreenUpdating = False
With ws
.Visible = xlSheetVisible
.Range("A1:D" & .Range("A" & .Rows.Count).End(xlUp).Row).ClearContents
.Visible = xlSheetHidden
End With
Application.ScreenUpdating = True
End Sub
Next, the below sub will only execute before the book is closing. This is where the bulk of your code will go to build your table.
Private Sub Workbook_BeforeClose(Cancel As Boolean)
Application.ScreenUpdating = False
ThisWorkbook.Sheets(1).Visible = xlSheetVisible
'Code goes here
ThisWorkbook.Sheets(1).Visible = xlSheetHidden
Application.ScreenUpdating = True
End Sub
You will need to qualify your objects (Ranges, Cells, etc.) directly (which sheet) since the code will not be housed in a sheet. The first sub uses a With block to qualify ranges and the second sub directly qualifies all objects with ThisWorkbook.Sheets(1).
Lastly, I recommend writing your code to build your table inside a module (qualify ranges!) so you can test and debug without needing to close/open your book continuously. Once you have the code working in a module, you can copy and paste the code directly into the 2nd sub here and you are good to go!

Custom currency format based on another cell value

In cell A1 i might have one of the following currencies "EUR", "USD" or "RON".
In cell B1 i have the following custom cell format: "EUR" * 0.00"/mt"
Can anyone help in telling me how can i set the format in cell B1 to adapt taking into account the value from A1. The code must run only at workbook.open
Thank you in advance!
Place the following Event Macro in the worksheet code area:
Private Sub Worksheet_Change(ByVal Target As Range)
Dim A1 As Range, B1 As Range, BaseFormat As String
Dim temp As String
Set A1 = Range("A1")
Set B1 = Range("B1")
BaseFormat = """EUR"" * 0.00""/mt"""
If Intersect(A1, Target) Is Nothing Then Exit Sub
temp = A1.Value
B1.NumberFormat = Replace(BaseFormat, "EUR", temp)
End Sub
Because it is worksheet code, it is very easy to install and automatic to use:
right-click the tab name near the bottom of the Excel window
select View Code - this brings up a VBE window
paste the stuff in and close the VBE window
If you have any concerns, first try it on a trial worksheet.
If you save the workbook, the macro will be saved with it.
If you are using a version of Excel later then 2003, you must save
the file as .xlsm rather than .xlsx
To remove the macro:
bring up the VBE windows as above
clear the code out
close the VBE window
To learn more about macros in general, see:
http://www.mvps.org/dmcritchie/excel/getstarted.htm
and
http://msdn.microsoft.com/en-us/library/ee814735(v=office.14).aspx
To learn more about Event Macros (worksheet code), see:
http://www.mvps.org/dmcritchie/excel/event.htm
Macros must be enabled for this to work!
(to run this only at workbook open, have the Workbook Open macro re-assert the value in A1 and then disable events)

Excel basic timestamp using VBA

I'm creating a very basic excel workbook for my own personal record, and what I'm wanting to do is having a record timestamp(in column B) whenever I add/edit the value of column A. As I've never dealt with VBA before, but with other programming languages, can someone point me into a right direction to start with?
I'm thinking of opening the macros to write a sub, then using a for loop to screen the whole columns and set the timestamp using TODAY() by seeing which cells in colA have been changed. But I reckon this will be a very inefficient method (as whole worksheet screening is required), I wonder what's the proper way of doing this? Thank you.
Include the following event macro in the worksheet code area:
Private Sub Worksheet_Change(ByVal Target As Range)
Dim A As Range, L As Long, r As Range, rInt As Range
Set A = Range("A:A")
Set rInt = Intersect(A, Target)
If rInt Is Nothing Then Exit Sub
Application.EnableEvents = False
For Each r In rInt
r.Offset(0, 1) = Now()
Next r
Application.EnableEvents = True
End Sub
Because it is worksheet code, it is very easy to install and automatic to use:
right-click the tab name near the bottom of the Excel window
select View Code - this brings up a VBE window
paste the stuff in and close the VBE window
If you have any concerns, first try it on a trial worksheet.
If you save the workbook, the macro will be saved with it.
If you are using a version of Excel later then 2003, you must save
the file as .xlsm rather than .xlsx
To remove the macro:
bring up the VBE windows as above
clear the code out
close the VBE window
To learn more about macros in general, see:
http://www.mvps.org/dmcritchie/excel/getstarted.htm
and
http://msdn.microsoft.com/en-us/library/ee814735(v=office.14).aspx
To learn more about Event Macros (worksheet code), see:
http://www.mvps.org/dmcritchie/excel/event.htm
Macros must be enabled for this to work!

Lock rows in Excel using VBA

I have an Excel sheet with columns A to F to be filled out by different users. Once one row is completed another user (control user) enteres "done" in column G. As soon as the user entered "done" in coulmn G, I want a VBA script to lock the entire row (column A to G) so that no one can change any of that row entries any longer. Is that possible using VBA scripting?
We must start with all cells un-protected and the sheet Locked
Enter the following Event macro in the worksheet code area:
Private Sub Worksheet_Change(ByVal Target As Range)
Dim N As Long
N = Target.Row
If Intersect(Target, Range("G:G")) Is Nothing Then Exit Sub
If Target.Text <> "Done" Then Exit Sub
ActiveSheet.Unprotect
Range("A" & N & ":G" & N).Locked = True
ActiveSheet.Protect
End Sub
Because it is worksheet code, it is very easy to install and automatic to use:
right-click the tab name near the bottom of the Excel window
select View Code - this brings up a VBE window
paste the stuff in and close the VBE window
If you have any concerns, first try it on a trial worksheet.
If you save the workbook, the macro will be saved with it.
If you are using a version of Excel later then 2003, you must save
the file as .xlsm rather than .xlsx
To remove the macro:
bring up the VBE windows as above
clear the code out
close the VBE window
To learn more about macros in general, see:
http://www.mvps.org/dmcritchie/excel/getstarted.htm
and
http://msdn.microsoft.com/en-us/library/ee814735(v=office.14).aspx
To learn more about Event Macros (worksheet code), see:
http://www.mvps.org/dmcritchie/excel/event.htm
Macros must be enabled for this to work!

How do i insert a new blank cell before current cell that has just been populated

I have a two (very long) TO-DO lists- one going across and the other going down.
What i want to achieve is for a blank cell to appear at the start of the list instead of having to scroll to the end of the lists to enter a new item.
So then when i have entered an item in a cell and hit enter, i want the cell just populated to move down the list (or across if i hit tab) and a new empty cell to appear at the start of the list.
It would be useful for the new blank cell to be pre-populated with the current date but that is not essential.
Thanks for your help.
NOT FOR POINTS.
Piggy-backing on Gary's answer, the mistake is that you set A to Range("C4:C6"). What happens is, when you enter data into any of C4, C5, and C6, they are all moved to the right because of A.Insert, which refers to all the cells assigned to A.
The trick here is to fully qualify your requirements for Target. Let's say you have a table from B1:E3, like below:
Now, let's say you want to move row 1 if you enter something into A1, row 2 if A2, etc. The following macro should do it (notice the difference with Gary's macro):
Private Sub Worksheet_Change(ByVal Target As Range)
Dim QualifyingRange As Range
'Dim OrigRng As String
Set QualifyingRange = Range("A1:A3")
If Intersect(Target, QualifyingRange) Is Nothing Then Exit Sub
Application.EnableEvents = False
'OrigRng = Target.Address
Target.Insert Shift:=xlToRight, CopyOrigin:=xlFormatFromLeftOrAbove
'Range(OrigRng).Value = Date
Application.EnableEvents = True
End Sub
What is the difference in the above? Very simple but very important. When a Worksheet_Change is in a sheet's code, every time you do a valid change to the sheet, the macro fires. The range you just edited will be known to the macro as Target. Now, usually, if you don't declare what the qualifications for Target are, the Worksheet_Change macro just fires indiscriminately. How do we qualify Target properly then?
We use Intersect. First, we declare a range of cells that we want to track. These cells, when changed, should fire the macro. Otherwise, macro is kaput. This line: If Intersect(Target, QualifyingRange) Is Nothing Then Exit Sub basically reads: If Target is not inside my desired range, then nothing happens.
This is the reason why I declared A1:A3 as my QualifyingRange. This way, if my change is to any of the cells above, the macro will fire. HOWEVER, .Insert should not be applied to the whole range but to Target alone. This is because if we do QualifyingRange.Insert, every time a change is detected in any cells in A1:A3, all three rows will move. This is what happened when you set A to three cells and kept A.Insert.
Hopefully, this clears up the confusion. Let us know if this helps.
Here is a partial solution. The following event macro monitors entry to cell A1 . Once you have entered a value in A1, the macro "pushed" the values in column A down by one. This means that value you just entered has been pushed down to A2 and A1 is empty:
Private Sub Worksheet_Change(ByVal Target As Range)
Dim A As Range
Set A = Range("A1")
If Intersect(A, Target) Is Nothing Then Exit Sub
Application.EnableEvents = False
A.Insert Shift:=xlDown, CopyOrigin:=xlFormatFromLeftOrAbove
Application.EnableEvents = True
End Sub
Because it is worksheet code, it is very easy to install and automatic to use:
right-click the tab name near the bottom of the Excel window
select View Code - this brings up a VBE window
paste the stuff in and close the VBE window
If you have any concerns, first try it on a trial worksheet.
If you save the workbook, the macro will be saved with it.
If you are using a version of Excel later then 2003, you must save
the file as .xlsm rather than .xlsx
To remove the macro:
bring up the VBE windows as above
clear the code out
close the VBE window
To learn more about macros in general, see:
http://www.mvps.org/dmcritchie/excel/getstarted.htm
and
http://msdn.microsoft.com/en-us/library/ee814735(v=office.14).aspx
To learn more about Event Macros (worksheet code), see:
http://www.mvps.org/dmcritchie/excel/event.htm
Macros must be enabled for this to work!
EDIT#1
To push across rather than down:
Private Sub Worksheet_Change(ByVal Target As Range)
Dim A As Range
Set A = Range("A1")
If Intersect(A, Target) Is Nothing Then Exit Sub
Application.EnableEvents = False
A.Insert Shift:=xlToRight, CopyOrigin:=xlFormatFromLeftOrAbove
Application.EnableEvents = True
End Sub
To handle multiple cells, you must specify which cells get pushed across and which cells get pushed down.