Using VBA to calculate cell values in rows based on criteria - excel

I have a problem I hope I can get some help with. In a summary report I need to use date criterias: today's date compared to months in B1:M1 (all cells are date formatted using a userdefined date format to only display the monthname) to sum the rows of data only if an account number is listed in column A. (pls. see below example)
I.E. if todays date is Feb. 7th the VBA code should loop through all rows and only sum the numbers for January and february where an account # is present (it must be in VBA)
Here is what I have so far:
Sub Test()
Dim today, lastdayinmonth As Date
Dim i, ii As Integer
Dim months As Range
today = DateSerial(Year(Date), Month(Date), Day(Date))
lastdayinmonth = DateSerial(Year(Date), Month(Date) + 1, 0)
months = Sheet2.Range("B2:M2")
If idag <= lastdayinmonth Then
For i = 3 To 20
If Not IsEmpty(Sheet2.Range("B" & i)) Then
End If
Next ii
End If
End Sub

Try this code, please. It works based on the assumption that your columns header are Date formatted (no matter if they show only month...), and the sum will be returned in Imediate Window:
Sub TestSumMonth()
Dim arrM As Variant, i As Long, j As Long
Dim nSum As Long, lastRow As Long, sh As Worksheet
Set sh = sheet2
lastRow = sh.Range("A" & sh.Rows.count).End(xlUp).Row
arrM = sh.Range("A1:M" & lastRow).Value
sh.Range("O2:O" & lastRow).Interior.ColorIndex = xlNone ' clear the existing interior color
For i = 1 To UBound(arrM, 1)
If arrM(i, 1) <> Empty Then
nSum = 0
For j = 2 To UBound(arrM, 2)
If Month(Date) >= Month(arrM(1, j)) Then
nSum = nSum + arrM(i, j)
If Month(Date) = Month(arrM(1, j)) Then
With sh.Range("O" & i)
.Value = nSum
.Interior.Color = vbYellow ' interior colored in yellow
End With
Exit For
End If
End If
Next j
End If
Next i
End Sub
The code firstly clears "O:O" range interior color, then returns the sum on the appropriate row of this column and colors the cell interior in yellow...
Now, it would summarize all the passed month values plus the active month.

Related

VBA - Loop wont add dates for start of the month

I have a macro that filters data by each unique value in column A and then adds lines for any missing dates. The Macro will only add the missing dates for the start of the month to the first group. The rest of the missing dates are added to all groups without any issues.
I think the issue is the 'If I = 2 then prevcell = start_date'. Is there any way to fix this so each time the macro filters it adds the missing dates at the start of the group even when not in line 2?
'Sub Macro1()
Dim aNames As Variant, Itm As Variant
With Range("A1", Range("A" & Rows.Count).End(xlUp))
.AdvancedFilter Action:=xlFilterInPlace, Unique:=False
aNames = .Offset(1).Resize(.Rows.Count - 1).SpecialCells(xlVisible).Value
For Each Itm In aNames
.AutoFilter Field:=1, Criteria1:=Itm
'Do whatever you want with an individual name here
Call Macro2
Next Itm
.AutoFilter
End With
Sub Macro2()
Dim wks As Worksheet, ssh As Worksheet
Set wks = Worksheets("NAV_REPORT_FSIGLOB1")
Set ssh = Worksheets("SUMMARY")
Dim lastRow As Long, start_date As Date, end_date As Date, curcell As Date
lastRow = wks.Range("D2").End(xlDown).Row
start_date = ssh.Range("A2") - 1
end_date = ssh.Range("B2")
With wks.Cells(lastRow, 4)
If .Value < end_date Then
.EntireRow.Copy
.EntireRow.Insert xlShiftDown
lastRow = lastRow + 1
.Value = end_date
End If
End With
For i = lastRow To 2 Step -1
curcell = wks.Cells(i, 4).Value
If i = lastRow Then curcell = end_date
prevcell = wks.Cells(i - 1, 4).Value
If i = 2 Then prevcell = start_date
Do Until curcell - 1 <= prevcell
wks.Rows(i).Copy
wks.Rows(i).Insert xlShiftDown
curcell = wks.Cells(i + 1, 4) - 1
wks.Cells(i, 4).Value = curcell
Loop
Next i`
The answer below assumes that all your three questions here, here and here is actually the same which have the same expected result.
I have a macro that filters data by each unique value in column A
The code below is not involving a unique value and a filtering.
The start and end date would need updated monthly so needs to be
easily changed e.g. cells A2 and B2 on the "Summary" worksheet.
The code will involve a start date (dtS) value and end date value (dtE).
adds lines for any missing dates
The row addition regarding the dtS will happen only if the first value in column D is bigger then dtS. If the first value in column D is smaller than the dtS then it does nothing
The row addition regarding the dtE will happen only if the last value in column D is smaller then dtE. If the last value in column D is bigger than the dtE then it does nothing.
before running the sub :
In a condition where dtS = "02-Oct-22": dtE = "20-Oct-22", If yellow column D value is 2 Oct 22, then no process is performed. If blue column D value is 23 Oct 22, then no process is performed.
The expected result :
yellow will be 2 rows, where column D value is from 2 to 3 Oct 22.
orange will be 3 rows, where column D value is from 8 to 10 Oct 22.
green will be 5 rows, where column D value is from 13 to 17 Oct 22.
blue will be 3 rows, where column D value is from 18 to 20 Oct 22.
There will be three section in the code.
The first is a loop which already mentioned in the answer of your question here.
The second and the third is additional code to involve the dtS and dtE.
Sub test()
Dim dtS As Date: Dim dtE As Date
Dim c As Range: Dim dif As Integer
Set c = Range("D2")
dtS = "02-Oct-22": dtE = "20-Oct-22"
'same code in https://stackoverflow.com/questions/75172779/vba-code-help-need-to-add-a-line-for-each-missing-date-and-copy-data-from-cell/75180868#75180868
Do While c.Offset(1, 0).Value <> ""
dif = DateDiff("d", c.Value, c.Offset(1, 0).Value)
If dif > 1 Then
With c.Offset(1, -3)
.EntireRow.Copy
Range(.Cells, .Offset(dif - 2, 0)).Insert Shift:=xlDown
End With
c.AutoFill Destination:=Range(c, c.Offset(dif - 1, 0)), Type:=xlFillDefault
Set c = c.Offset(dif, 0)
Else
Set c = c.Offset(1, 0)
End If
Loop
'check the dtS in column D first value
Set c = Range("D2")
If dtS < CDate(c.Value) Then
dif = DateDiff("d", dtS, c.Value): c.Value = dtS
With c.Offset(0, -3)
.EntireRow.Copy
Range(.Cells, .Offset(dif - 1, 0)).Insert Shift:=xlDown
End With
Set c = Range("D2")
c.AutoFill Destination:=c.Resize(dif + 1, 1), Type:=xlFillDefault
End If
'check the dtE in column D last value
Set c = Range("D2").End(xlDown)
If dtE > CDate(c.Value) Then
addr = c.Address: dif = DateDiff("d", c.Value, dtE)
With c.Offset(0, -3)
.EntireRow.Copy
Range(.Cells, .Offset(dif - 1, 0)).Insert Shift:=xlDown
End With
With Range(addr)
.AutoFill Destination:=.Resize(dif + 1, 1), Type:=xlFillDefault
End With
End If
End Sub
Again, the sub above is based on guessing from all of your three questions. Because in your first question, you don't mentioned about start date and end date at all. In the second question you mentioned :
Could someone help with rewriting the code so it adds all dates that
are missing between a start and end date. The start and end date would
need updated monthly so needs to be easily changed e.g. cells A2 and
B2 on the "Summary" worksheet
And in this question, you mentioned ONLY about the start date.
add the missing dates for the start of the month
Please note that the answer is based on the data in your second question, where your data is something like this
which your expected result is like this

Create a days of month using a date as parameter

I have a date in cell A1 for example: 12/08/22
I want create a list with all days of the month (1-31) in column E using the month and year of the cell A1 as parameter.
Sub TESTEEEEEEE()
Dim r As Range, i As String, j As Long
i = Range("A1").Offset(0, 1).Value = Left(cell.Value, 3)
'k = ????
j = 31
For Each r In Range("E1:E31")
r.NumberFormat = "#"
r.Value = Format(DateSerial(k, i, j), "dd/m/yy")
j = j + 1
Next r
End Sub
I'm stucked in how to extract the the month and year. I was trying using the position of the characteres as parameter, but i'm not getting it work.
i should extract the 4,5 and characterer returning 08 (ok, the code is wrong i was making some tests).
k should extract the 7,8 charachter returning 22.
Someone can help me?
Please, try using the next way. It does not need iteration:
Sub testCreateWholeMonth()
Dim D As Date, lastD As Long
D = Range("A1").value
lastD = Day(WorksheetFunction.EoMonth(DateSerial(Year(D), Month(D), 1), 0))
With Range("E1:E" & lastD)
.value = Evaluate("date(" & Year(D) & "," & Month(D) & ",row(1:" & lastD & "))")
.NumberFormat = "mm.dd.yyyy"
End With
End Sub
You are probably after calendar functions like YEAR, MONTH, EOMONTH
Sub DebugDate()
Dim rg As Range
Set rg = Range("A1") ' should contain a date
Dim dt As Date
dt = rg.Value
Debug.Print Year(dt), Month(dt), CDate(WorksheetFunction.EoMonth(dt, 0))
' End of month not using worksheet function EOMONTH
Debug.Print DateSerial(Year(dt), Month(dt) + 1, 1) - 1
End Sub
Further reading on How to create a calendar with VBA

Check, Select and Change specific parts of a Date in VBA

So I have a list of dates that I am trying to search through and check if they need to be corrected or not. The yellow highlighted cells are examples of changes needed to be made. Wether the date needs fixing or not I want the result of the code to place it in the "Date Fixed" column as shown in the first cell. If the date is not listed as the 30/31st of a month or the 1st then I need to change the day part of the date to either the beginning or end of the month. I have written what I thought would work but I keep receiving a Run Time Error 11 code. Any ideas on how to fix this and keep going through all the dates?
Private Sub FormatDate_Click()
Dim myrow As Integer
Dim startrow As Integer
Dim Dates As Date
Dim Datesfixed As Date
Dim dateTwo As Date
Dim dateEnd As Date
myrow = 2
startrow = 2
Dates = Cells(myrow, 2)
Datesfixed = Cells(myrow, 3)
dateTwo = mm / 1 / yyyy
dateEnd = mm / 31 / yyyy
Do Until Cells(myrow, 1) = ""
If Dates = dateTwo Or dateEnd Then
Datesfixed = Dates
ElseIf Dates <> dateTwo Or dateEnd Then
Dates = dateTwo
myrow = myrow + 1
End If
myrow = myrow + 1
Loop
myrow = 2
startrow = 2
End Sub
Try something like this:
Private Sub FormatDate_Click()
Dim c As Range, dt, d As Long, m As Long, y As Long, dLast As Long
Set c = ActiveSheet.Range("B2") 'first date
Do While Len(c.Value) > 0
dt = c.Value
d = Day(dt) 'extract the parts of the date
m = Month(dt)
y = Year(dt)
dLast = Day(DateAdd("m", 1, DateSerial(y, m, 1)) - 1) 'last day of the month
If d <> 1 And d <> dLast Then
c.Offset(0, 1) = DateSerial(y, m, dLast) 'set to last day of the month
End If
Set c = c.Offset(1, 0) 'next date
Loop
End Sub

VBA code to list all months between a range of dates

I am new to VBA in Excel. I have looked through through the forum, but have not found an answer for my specific date VBA I am looking for. I have three date ranges in excel cells per row of data elements representing testing dates. Each of the three ranges has a start date and an end date columns A-F.
For each row of test date ranges, I would one cell in column G to calculate the month and year "MMMYY" for any months covered in any of the three date ranges. If the date ranges over three months, the resulting cell would list all three months.
Any help would be greatly appreciated. Thank you in advance.
Marc
Calculated VBA column G
A B C D E F G
1 T1 Start T1 End T2 Start T2 End T3 Start T3 End Months
2 02Nov20 16Nov20 17Nov20 19Nov20 02Nov20 1Jan21 Nov20
Dec20
Jan21
3 28Oct19 15Nov19 28Oct19 01Nov19 28Oct19 1Nov19 Oct20
Nov20
4 20Jul20 21Aug20 Jul20
Aug20
5 11Sep20 29Sep20 20Sep20 22Sep20 20Sep20 Sep20
Here is a macro that outputs ALL of the included month/year.
In order to find the data table, I used the .CurrentRegion property of the cell that contains T1 Start. Because of this, if the output were adjacent to the table, the second run would include that column. Accordingly, I wrote the results one column over (and hid the intervening column. But you could make any number of changes in determining the source table size if that is undesireable.
I also was not certain, from your screenshot, if the Dates were "real Excel Dates" formatted to look like ddmmmyy (except for Column F in your text table) or if they are strings. So there is code to account for the different things I see. Code could be simplified if the data is in a known format.
The output is text strings and the column is formatted as text. If you want the output to be real dates formatted as mmmyy, then code will need to be added so Excel does not erroneously convert 2 digit years to day of the month.
Be sure to read the notes in the macro, as it will help you understand what's going on.
Option Explicit
Sub mthList()
Dim cM As Collection
Dim rg As Range, dStart As Date, dEnd As Date
Dim vSrc As Variant, vRes As Variant
Dim i As Long, J As Long, K As Long
Dim d1 As Double, d2 As Double 'start and end dates
Dim WS As Worksheet, rRes As Range
'Find the table and read it into VBA array
Set WS = ThisWorkbook.Worksheets("Sheet1")
With WS
Set rg = .Cells.Find(what:="T1 Start", after:=.Cells(.Rows.Count, .Columns.Count), _
LookIn:=xlFormulas, lookat:=xlWhole, searchorder:=xlByRows, searchdirection:=xlNext, MatchCase:=False)
If rg Is Nothing Then
MsgBox "No Data Table"
Exit Sub
End If
vSrc = rg.CurrentRegion
ReDim vRes(1 To UBound(vSrc, 1), 1 To 1)
End With
'Collect all the included dates
'Convert date strings to real dates if they are strings
For i = 2 To UBound(vSrc, 1)
Set cM = New Collection
For J = 1 To UBound(vSrc, 2) Step 2 'can have N pairs of dates
If vSrc(i, J) <> "" Then
d1 = theDate(vSrc(i, J)) ' need to make sure this is a date and not a text string
If vSrc(i, J + 1) = "" Then
d2 = d1
Else
d2 = theDate(vSrc(i, J + 1))
End If
On Error Resume Next 'remove duplicates since Collection cannot have two entries with same key
For K = d1 To d2
cM.Add Format(K, "mmmyy"), Format(K, "mmmyy")
Next K
On Error GoTo 0
End If
Next J
'Output the data to results array
For K = 1 To cM.Count
vRes(i, 1) = vRes(i, 1) & vbLf & cM(K)
Next K
vRes(i, 1) = Mid(vRes(i, 1), 2)
Next i
'write the results
'formatting is optional, and Styles may not work with non-English versions
Set rRes = rg.Offset(0, rg.CurrentRegion.Columns.Count + 1)
Set rRes = rRes.Resize(UBound(vRes, 1), UBound(vRes, 2))
With rRes
.EntireColumn.Clear
.EntireColumn.NumberFormat = "#"
.Value = vRes
.WrapText = True
.EntireRow.AutoFit
.EntireColumn.AutoFit
.Style = "output"
.Offset(0, -1).EntireColumn.Hidden = True
End With
With rg.CurrentRegion
.VerticalAlignment = xlCenter
.HorizontalAlignment = xlCenter
.Style = "Input"
End With
End Sub
Private Function theDate(d) As Double
If Not IsDate(d) Then
theDate = CDate(Left(d, Len(d) - 5) & " " & Mid(d, Len(d) - 4, 3) & " " & Right(d, 2))
Else
theDate = d
End If
End Function
EDIT:
To use my algorithm as a function, just need to remove all that stuff with regard to finding the table and writing results back to the worksheet:
Option Explicit
Function mthList(rg As Range) As String
Dim cM As Collection
Dim dStart As Date, dEnd As Date
Dim vSrc As Variant
Dim I As Long, J As Long, K As Long
Dim d1 As Double, d2 As Double 'start and end dates
Dim S As String
'Collect all the included dates
'Convert date strings to real dates if they are strings
vSrc = rg
Set cM = New Collection
For J = 1 To UBound(vSrc, 2) Step 2 'can have N pairs of dates
If vSrc(1, J) <> "" Then
d1 = theDate(vSrc(1, J)) ' need to make sure this is a date and not a text string
If vSrc(1, J + 1) = "" Then
d2 = d1
Else
d2 = theDate(vSrc(1, J + 1))
End If
On Error Resume Next 'remove duplicates since Collection cannot have two entries with same key
For K = d1 To d2
cM.Add Format(K, "mmmyy"), Format(K, "mmmyy")
Next K
On Error GoTo 0
End If
Next J
'Output the data to a string
For K = 1 To cM.Count
S = S & vbLf & cM(K)
Next K
mthList = Mid(S, 2)
End Function
Private Function theDate(d) As Double
If Not IsDate(d) Then
theDate = CDate(Left(d, Len(d) - 5) & " " & Mid(d, Len(d) - 4, 3) & " " & Right(d, 2))
Else
theDate = d
End If
End Function
As said in the comments one could use a dictionary
Function listMthYear(rg As Range) As String
' Goto Tools/Reference and check Microsoft Scripting Runtime
Dim dict As Dictionary
Set dict = New Dictionary
Dim sngCell As Range
For Each sngCell In rg
If IsDate(sngCell.Value) Then
Dim mth As Long
Dim yr As Long
Dim dte As Date
dte = sngCell.Value
mth = VBA.Month(dte)
yr = VBA.year(dte)
dte = VBA.DateSerial(yr, mth, 1)
' This will create an unique entry in the dictionary if not already created
dict(dte) = dte
End If
Next sngCell
Dim output As Variant, i As Long
output = dict.Keys
For i = LBound(output) To UBound(output)
output(i) = Format(output(i), "MMMYY")
Next i
listMthYear = Join(output, vbLf)
End Function
You could use the function as an UDF or like that
Sub TestIt()
Dim rg As Range
Set rg = Range("A3:E3")
MsgBox listMthYear(rg)
End Sub

Splitting date time column in excel vba to two separate columns gives different results depending on whether dd part is greater than 12 or no

I use the following code:
Sub SplitDateTime()
Dim dDate As Date
Dim dTime As Date
Dim x As Integer
MsgBox "Split Date Time"
For x = 2 To 21
'Sets the date in the cell of the first column
dDate = Cells(x, 2)
dTime = Cells(x, 2)
Cells(x, 2).Value = Format(dDate, "dd/mm/yyyy")
'Sets the time in the cell of the second column
Cells(x, 3).Value = Format(dTime, "hh:mm")
Next x
When this is run I get the following:
Notice that the 2nd to the 6th row of dates weren't converted properly. It seems that these rows were changed to mm/dd/yyyy instead of dd/mm/yyyy (they were originally 12/01/2019...), possibly because the conversion is inconsistent, and then the time was inserted as 00:00. Has anyone any suggestions on how to correct this?
This is the original:
Original columns, the first column was subsequently deleted
Something like the following should work:
Sub SplitDateTime()
Dim dDate As Date
Dim dTime As Date
Dim x As Integer
MsgBox "Split Date Time"
For x = 2 To 21
'Sets the date in the cell of the first column
dDate = Cells(x, 2)
With Cells(x, 2)
.Value = Int(dDate)
.NumberFormat = "dd/mm/yy"
End With
'Sets the time in the cell of the second column
With Cells(x, 3)
.Value = dDate - Int(dDate)
.NumberFormat = "hh:mm"
End With
Next x
End Sub

Resources