Keep results of last run of the day only - excel

My goal is to make a script that every time I press the button refreshes my investment value and then posts the date and the investment value on another sheet.
I managed the following code.
Sub Refresh()
ActiveWorkbook.RefreshAll
ActiveSheet.Range("A1").End(xlDown).Offset(1, 0).Value = Date
Worksheets("Portfolio").Range("B3").Copy
ActiveSheet.Range("B1").End(xlDown).Offset(1, 0).PasteSpecial Paste:=xlPasteValues
End Sub
If I hit the button more than once, I just want to keep the last entry of the day.
My idea was to solve it with an if else, but it ignores the if:
Sub Refresh()
ActiveWorkbook.RefreshAll
If ActiveSheet.Range("A1").End(xlDown).Offset(1, 0).Value = ActiveSheet.Range("A1").End(xlDown).Value Then
Worksheets("Portfolio").Range("B3").Copy
ActiveSheet.Range("B1").End(xlDown).PasteSpecial Paste:=xlPasteValues
Else
ActiveSheet.Range("A1").End(xlDown).Offset(1, 0).Value = Date
Worksheets("Portfolio").Range("B3").Copy
ActiveSheet.Range("B1").End(xlDown).Offset(1, 0).PasteSpecial Paste:=xlPasteValues
End If
End Sub

Update Worksheet
If you don't have a button or another control to run Refresh on the ActiveSheet, I would strongly suggest you define it like Protfolio is defined.
If the code is in the ActiveWorkbook, you should definitely use
ThisWorkbook instead, which refers to the workbook containing this
code.
The Code
Option Explicit
Sub Refresh()
' Define workbook ('wb').
Dim wb As Workbook
Set wb = ActiveWorkbook
' Do.
wb.RefreshAll
' Define Target Worksheet ('tgt').
Dim tgt As Worksheet
Set tgt = wb.ActiveSheet
' Define Old Date Cell ('cel').
Dim cel As Range
Set cel = tgt.Range("A1").End(xlDown)
' Check if Old Date Cell does not contain today's date.
If cel.Value <> Date Then
' Does not contain today's date:
' Define New Date Cell ('cel'), the cell below Old Date Cell.
Set cel = cel.Offset(1, 0)
' Write today's date to New Date Cell.
cel.Value = Date
Else
' Does contain today's date:
' Do nothing because Old Date Cell is becoming New Date Cell.
End If
' Define Source Worksheet ('src').
Dim src As Worksheet
Set src = wb.Worksheets("Portfolio")
' Write value from Source Cell to Target Cell.
' (Source Cell is next to New Date Cell).
cel.Offset(0, 1).Value = src.Range("B3").Value
End Sub

Related

How can I cut rows into another sheet then delete + shift for the next row without a loop freezing?

I'm working on having a production schedule sort due dates into different worksheets from a data dump sheet. I can get it working for one date manually, but I'd like it to loop for the entire range. When I try to loop it it gets stuck, I think it's because I'm deleting and shifting cells up in the groupBy sub.
Sub groupByDate()
'matches every date to the first cell, copies them onto a new sheet
'then deletes original range and shifts up for new top date
Dim day As Range
Dim due As Range
Set due = Range(Range("D29"), Range("D29").End(xlDown))
Dim cel As Range
For Each cel In due
'appends the range to move, if empty create range to move
If (cel.Value = due.Cells(1).Value) Then
If day1 Is Nothing Then
Set day = cel.EntireRow
Else
Set day = Union(day, cel.EntireRow)
End If
End If
Next
day.Copy
Sheets.Add.PasteSpecial
day.Delete Shift:=xlUp
End Sub
Here is my loop function:
Sub testLoop()
'trying to loop groupBy until dump range is empty
Dim cel As Range
Set cel = Range("D29")
Do While True
If cel.Value <> "" Then
groupByDate
Else
Exit Do
End If
Loop
End Sub
When you have one date Range("D29").End(xlDown) could be D1048576. Also the new sheet will become the active sheet and on second pass of loop after row 29 is deleted cel is undefined and needs to be reassigned with a next value.
Sub groupByDate()
'matches every date to the first cell, copies them onto a new sheet
'then deletes original range and shifts up for new top date
Dim day As Range, ue As Range, cel As Range
Dim ws As Worksheet
Set ws = ActiveSheet
Set due = ws.Range(ws.Range("D29"), ws.Range("D29").End(xlDown))
If due.Rows.Count > 1000000 Then Set due = ws.Range("D29") ' single date
For Each cel In due
'appends the range to move, if empty create range to move
If (cel.Value = due.Cells(1).Value) Then
If day Is Nothing Then
Set day = cel.EntireRow
Else
Set day = Union(day, cel.EntireRow)
End If
End If
Next
day.Copy
Sheets.Add.PasteSpecial
day.Delete Shift:=xlUp
ws.Activate ' return to active sheet
End Sub
Sub testLoop()
'trying to loop groupBy until dump range is empty
Dim cel As Range
Set cel = Range("D29")
Do While cel.Value <> ""
groupByDate0
Set cel = Range("D29")
Loop
End Sub

How to add Date every day to my first blank row

I would like every day, when I open excel, that my excel would add a today's date to it. I have this code, but it isn't working, sometimes it does what it's supposed to and sometimes it skips a line, any help please?
Sub Stretching()
'This procedure will run each time you open the workbook
'Specify the required worksheet name in double quotes
Dim ws As Worksheet
Set ws = Sheets("Stretching")
'Get the last row number filled with a value in Column A
Dim lastRow As Long
lastRow = Cells(Rows.Count, 1).End(xlUp).Row
'Check if the last entered date is the same as the current date, if so, exit
'You need this check to see if you close the workbook then open it on the same day
'so that the code does not enter the same date again in a new row.
If ws.Cells(lastRow, 1).Value = Date Then Exit Sub
'Fill a new row in Column A with the current date
If IsEmpty(Cells(lastRow, 1)) Then
ws.Cells(lastRow, 1).Value = Date
Else
ws.Cells(lastRow, 1).Offset(1, 0).Value = Date
End If
End Sub
Some suggestions on your code:
Fully qualifying the ranges help you avoiding inconsistent results. This means, you can be running the procedure when an active sheet is different than the one you are targeting, and this line: Cells(Rows.Count, 1).End(xlUp).Row would return a different "last row" than the one you'd expect
Also try to use variable names that are easily understandable. For example, ws vs targetSheet
Please try this code and let me know if it works:
Public Sub Stretching()
'This procedure will run each time you open the workbook
'Specify the required worksheet name in double quotes
Dim targetSheet As Worksheet
Set targetSheet = ThisWorkbook.Sheets("Stretching")
'Get the last row number filled with a value in Column A
Dim lastRow As Long
lastRow = targetSheet.Cells(targetSheet.Rows.Count, 1).End(xlUp).Row
'Check if the last entered date is the same as the current date, if so, exit
'You need this check to see if you close the workbook then open it on the same day
'so that the code does not enter the same date again in a new row.
If targetSheet.Cells(lastRow, 1).Value = Date Then Exit Sub
'Fill a new row in Column A with the current date
If IsEmpty(targetSheet.Cells(lastRow, 1)) Then
targetSheet.Cells(lastRow, 1).Value = Date
Else
targetSheet.Cells(lastRow, 1).Offset(1, 0).Value = Date
End If
End Sub

How to copy data between two defined dates to new worksheet using VBA?

I am trying to copy data from a "Data" worksheet into a already created worksheet called "DateData". I want the user to be able to enter in a Start Date ("L15") and End Date ("L16") in a separate worksheet called "No Entry". On a button click...Then the data in "Data" worksheet is pulled into the "DateData" Worksheet, only including records between those dates (including the start and entry date). I hope that makes sense haha
I have tried the below but keep getting errors. The first being a "Sort method of Range class failed 1004". The code below also doesn't use the preset worksheet to copy data but creates a sheet at the end of all worksheets (which I don't want).
The "Data" worksheet has titles all in row 1 and data starts from A2 onwards...It has 19 columns of titles (so data filled) and the date that I want it looking for is in column G..G1=Title, G2 = Date starts. Date format = dd/mm/yyyy
How would I go about doing this? Any help would be so grateful. Thank you
Private Sub CommandButton2_Click()
Application.ScreenUpdating = False
Dim StartDate, EndDate As Date
Dim MainWorksheet As Worksheet
StartDate = Sheets("NoEntry").Range("L15").Value
EndDate = Sheets("NoEntry").Range("L16").Value
Set MainWorksheet = Worksheets("Data")
MainWorksheet.Activate
Range("G1").CurrentRegion.Sort key1:=Range("G1"), order1:=xlAscending, Header:=xlYes
Range("G1").CurrentRegion.AutoFilter Field:=7, Criteria1:=">=" & StartDate, Operator:=xlAnd,
Criteria2:="<=" & EndDate
ActiveSheet.AutoFilter.Range.Copy
Worksheets.Add after:=Worksheets(Worksheets.Count)
ActiveSheet.Paste
Selection.Columns.AutoFit
Range("G1").Select
MainWorksheet.Activate
Selection.AutoFilter
Sheets("NoEntry").Activate
End Sub
"DateData"
"Data"
So as you can see from the "Data" worksheet I have sorted the data but because it has blanks they are at the bottom (as in there are no dates in the G column for it). This was before validation so this happened
And what copies over onto the "DateData" worksheet is only the records with blank dates.
Sorry for the black filled records as they are private information. I hope that makes sense.
First, see How to avoid using Select in Excel VBA to learn how to avoid using select in your code. There is almost no necessary case in using it.
See below notes for the code I provide (now tested!).
1) You are having an issue where a worksheet is being added and you are not aware how/ why and you are uncertain of your destination for your data. To overcome this, it is a common practice to explicitly define your worksheet objects. This makes it easier for you to understand, while also allowing for less scope for error. I have qualified the worksheets as wsData for “Data worksheet”, wsDate for “DateData worksheet” and wsNoEntry for “No Entry worksheet”. Do you see how easy it is to understand now?
2) Make sure that the dates in your data set are stored as “Date” type values. You can do this under the number formatting ribbon.
3) I have chosen to use an array to loop through. Depending on how big your data set is, this will be a much faster way to loop through to get the start and end date
4) This approach assumes your data is sorted by the Date column (G)
Sub CopyDataUsingDateRange()
Application.ScreenUpdating = False
Dim wsData As Worksheet, wsDate As Worksheet, wsNoEntry As Worksheet
Dim dSDate As Date, dEDate As Date
Dim lRowStart As Long, lRowEnd As Long
Dim aData() As Variant
Dim i As Long
'set the worksheet objects
Set wsData = ThisWorkbook.Sheets("Data")
Set wsDate = ThisWorkbook.Sheets("DateData")
Set wsNoEntry = ThisWorkbook.Sheets("No Entry")
'required variables
dSDate = wsNoEntry.Range("L15").Value
dEDate = wsNoEntry.Range("L16").Value
'set the array - you can make this dynamic!
aData = wsData.Range("A1:Z1000").Value
'for loop to find start
For i = 1 To 1000
If aData(i, 7) = dSDate Then
lRowStart = i
Debug.Print "Start row = " & lRowStart
Exit For
End If
Next i
'now loop backwards to find end date
For i = 1000 To 1 Step -1
If aData(i, 7) = dEDate Then
lRowEnd = i
Debug.Print "End row = " & lRowEnd
Exit For
End If
Next i
'now we have start and end dates
'going to use copy/ paste for simplicity
wsData.Range("A" & lRowStart, "Z" & lRowEnd).Copy
'paste in date sheet
wsDate.Range("A1").PasteSpecial Paste:=xlPasteValues
'clear clipboard
Application.CutCopyMode = False
Application.ScreenUpdating = True
End Sub
Hope this helps, mostly with understanding so you can leverage for future use!
Consider avoiding the use of constant .Select and .Activate. Instead, manage processes with Set variables or in a With context. Additionally, the filter copy method needs to be handled differently namely on visible and non-blank cell results of filtered worksheet.
Dim StartDate As Date, EndDate As Date
Dim MainWorksheet As Worksheet, NewWorkSheet As Worksheet
StartDate = Sheets("NoEntry").Range("L15").Value
EndDate = Sheets("NoEntry").Range("L16").Value
Set MainWorksheet = Worksheets("Data")
With MainWorksheet
' SORT RANGE
.Range("G1").CurrentRegion.Sort key1:=.Range("F1"), order1:=xlAscending, Header:=xlYes
Set NewWorkSheet = Worksheets.Add(after:=Worksheets(Worksheets.Count))
With .Range("$A:$G")
' SORT RANGE
.AutoFilter Field:=7, Criteria1:=">=" & StartDate, Operator:=xlAnd, _
Criteria2:="<=" & EndDate
' COPY VISIBLE AND NON-BLANK CELLS TO NEW WORKSHEET
Application.Intersect(.SpecialCells(xlCellTypeVisible), _
.SpecialCells(xlCellTypeConstants)).Copy _
Destination:=NewWorkSheet.Range("A1")
End With
' REMOVE FILTER
.Cells.AutoFilter
End With
Sheets("NoEntry").Activate
Set MainWorksheet = Nothing: Set NewWorkSheet = Nothing

I want to modify the code so that macro runs as soon as the workbook is opened

The macro is about locking columns containing all dates except column with today's date. I have coded it in each sheet and is exactly similar in all the sheets. Macro runs when the data in any cell is changed. But I want macro to run when the workbook is opened.
I tried to code it in 'this workbook' but I can not figure out how to do it. I also tried to do it in 'module' but could not.
Private Sub Worksheet_Change(ByVal Target As Excel.Range)
'vps
Dim x As Long
x = 7
ThisWorkbook.ActiveSheet.Unprotect Password:="123456"
ThisWorkbook.ActiveSheet.Cells.Locked = False
Do Until IsEmpty(Cells(5, x))
If Cells(5, x) <> Date Then
Columns(x).Locked = True
End If
x = x + 1
Loop
ThisWorkbook.ActiveSheet.Protect Password:="123456"
End Sub
I want the macro to run when the workbook is opened and not only when the data in cell is changed.
Lock Cells on Workbook Open
Features
To run the following (or any) macro when a workbook opens, you have
to use its Workbook_Open event. In VBA double-click ThisWorkbook. In
the first drop down choose Workbook and in the second choose Open.
For each worksheet in the workbook containing this macro, unprotects
it and unlocks all cells. Then checks a specified row range for dates
and locks cells of entire columns where today's date was not found.
Finally protects the worksheet.
Additionally the Interior color will be different in cells where
today's date was found.
Links
Workbook Download (Dropbox)
The Code
Standard Module e.g Module1
Sub ProtectPrevious()
Const cRow As Long = 5 ' Date Row Number
Const cFirstC As Variant = 7 ' First Column Letter/Number e.g. 7 or "G"
Const cToday As Long = 6 ' Today Cell ColorIndex e.g. 6 is Yellow
Const cDays As Long = 15 ' Other Days ColorIndex e.g. 15 is some Gray
Dim ws As Worksheet ' Current Worksheet
Dim LastC As Long ' Last Column Number
Dim j As Integer ' Column Counter
For Each ws In ThisWorkbook.Worksheets
With ws
' Prepare for processing.
.Unprotect Password:="123456"
.Cells.Locked = False
' When there is no data in Date Row, continue with next worksheet.
If .Rows(cRow).Find("*", .Cells(cRow, _
.Columns.Count), -4123, , 1) Is Nothing Then Exit For
' Calculate Last Column Number
LastC = .Rows(cRow).Find("*", , -4123, , 1, 2).Column
' Remove formatting from other day(s) in Date Row.
With .Range(.Cells(cRow, cFirstC), .Cells(cRow, LastC))
.Interior.ColorIndex = cDays
End With
' Loop through columns: from First Column to Last Column.
For j = cFirstC To LastC
If .Cells(cRow, j) <> Date Then
.Columns(j).Locked = True
Else
' Apply formatting to 'today' in Date Row.
With .Cells(cRow, j)
.Interior.ColorIndex = cToday
End With
End If
Next
.Protect Password:="123456"
End With
Next
End Sub
ThisWorkbook
Private Sub Workbook_Open()
ProtectPrevious
End Sub

Excel: Populate Data Across Multiple Worksheets

Unfortunately for my employer, none of my network engineering courses included advanced Excel formula programming. Needless to say, I know nothing about Excel save for basic SUM and COUNT formula commands.
My employer has an Excel workbook with multiple worksheets within it representing each month of the calendar year. We want to be able to have a "total" worksheet in the workbook that reflects all data across the entire workbook in each column/row.
An example for the sake of clarity:
In the worksheet "May_2013", column A is labeled "DATE". Cell A2 contains the data "MAY-1".
In the worksheet "June_2013", column A is labeled "DATE". Cell A2 contains the data "JUNE-1".
In the worksheet "Total", column A is labeled "DATE". We want cells A2 to reflect "MAY-1" and A3 to reflect "JUNE-1".
We want to do this for all worksheets, columns A-Q, rows 2-33 and populate a master sheet at the very end containing all data in all worksheets in their corresponding columns.
Is this possible?
Here are two VBA solutions. The first does this:
Check if a sheet "totals" exists. Create it if it does not
Copy the first row (A to Q) of first sheet to "totals"
Copy block A2:Q33 to "totals" sheet starting at row 2
Repeat for all other sheets, appending 32 rows lower each time
The second shows how to do some manipulation of the column data before copying: for each column it applies the WorksheetFunction.Sum(), but you could replace that with any other aggregating function that you would like to use. It then copies the result (one row per sheet) to the "totals" sheet.
Both solutions are in the workbook you can download from this site. Run the macros with , and pick the appropriate one from the list of options that shows up. You can edit the code by invoking the VBA editor with .
Sub aggregateRaw()
Dim thisSheet, newSheet As Worksheet
Dim sheetCount As Integer
Dim targetRange As Range
sheetCount = ActiveWorkbook.Sheets.Count
' add a new sheet at the end:
If Not worksheetExists("totals") Then
Set newSheet = ActiveWorkbook.Sheets.Add(after:=Sheets(sheetCount))
newSheet.Name = "totals"
Else
Set newSheet = ActiveWorkbook.Sheets("totals")
End If
Set targetRange = newSheet.[A1]
' if you want to clear the sheet before copying data, uncomment this line:
' newSheet.UsedRange.Delete
' assuming you want to copy the headers, and that they are the same
' on all sheets, you can copy them to the "totals" sheet like this:
ActiveWorkbook.Sheets(1).Range("1:1").Copy targetRange
Set targetRange = targetRange.Offset(1, 0) ' down a row
' copy blocks of data from A2 to Q33 into the "totals" sheet
For Each ws In ActiveWorkbook.Worksheets
If ws.Name <> newSheet.Name Then
ws.Range("A2", "Q33").Copy targetRange
Set targetRange = targetRange.Offset(32, 0) ' down 32 rows
End If
Next ws
End Sub
Sub aggregateTotal()
Dim thisSheet, newSheet As Worksheet
Dim sheetCount As Integer
Dim targetRange As Range
Dim columnToSum As Range
sheetCount = ActiveWorkbook.Sheets.Count
' add a new sheet at the end:
If Not worksheetExists("totals") Then
Set newSheet = ActiveWorkbook.Sheets.Add(after:=Sheets(sheetCount))
newSheet.Name = "totals"
Else
Set newSheet = Sheets("totals")
End If
' assuming you want to copy the headers, and that they are the same
' on all sheets, you can copy them to the "totals" sheet like this:
Set targetRange = newSheet.[A1]
ActiveWorkbook.Sheets(1).Range("A1:Q1").Copy targetRange
Set targetRange = targetRange.Offset(1, 0) ' down a row
For Each ws In ActiveWorkbook.Worksheets
' don't copy data from "total" sheet to "total" sheet...
If ws.Name <> newSheet.Name Then
' copy the month label
ws.[A2].Copy targetRange
' get the sum of the coluns:
Set columnToSum = ws.[B2:B33]
For colNum = 2 To 17 ' B to Q
targetRange.Offset(0, colNum - 1).Value = WorksheetFunction.Sum(columnToSum.Offset(0, colNum - 2))
Next colNum
Set targetRange = targetRange.Offset(1, 0) ' next row in output
End If
Next ws
End Sub
Function worksheetExists(wsName)
' adapted from http://www.mrexcel.com/forum/excel-questions/3228-visual-basic-applications-check-if-worksheet-exists.html
worksheetExists = False
On Error Resume Next
worksheetExists = (Sheets(wsName).Name <> "")
On Error GoTo 0
End Function
Final(?) edit:
If you want this script to run automatically every time someone makes a change to the workbook, you can capture the SheetChange event by adding code to the workbook. You do this as follows:
open the Visual Basic editor ()
In the project explorer (left hand side of the screen), expand the VBAProject
Right-click on "ThisWorkbook", and select "View Code"
In the window that opens, copy/paste the following lines of code:
Private Sub Workbook_SheetChange(ByVal Sh As Object, ByVal Target As Range)
' handle errors gracefully:
On Error GoTo errorHandler
' turn off screen updating - no annoying "flashing"
Application.ScreenUpdating = False
' don't respond to events while we are updating:
Application.EnableEvents = False
' run the same sub as before:
aggregateRaw
' turn screen updating on again:
Application.ScreenUpdating = True
' turn event handling on again:
Application.EnableEvents = True
Exit Sub ' if we encountered no errors, we are now done.
errorHandler:
Application.EnableEvents = True
Application.ScreenUpdating = True
' you could add other code here... for example by uncommenting the next two lines
' MsgBox "Something is wrong ... " & Err.Description
' Err.Clear
End Sub
Kindly use RDBMerge add-in which will combine the data from different worksheet and create a master sheet for you. Please see the below link for more details.
http://duggisjobstechnicalstuff.blogspot.in/2013/03/how-to-merge-all-excel-worksheets-with.html
Download RDBMerge
You can use the indirect function to reference the sheet name. In the image below this function takes the header name (B37) and uses it as the sheet reference. All you have to do is choose the correct "total cell" which I made "A1" in "MAY_2013". I put an image below to show you my reference name as well as tab name

Resources