I am trying to create a macro that as well as many other things will loop through the dates in column and, compare it to the "due date" and show the amount of dates that is left
Unfortunately the macro that I wrote doesn't give the desired outcome and gives me some huge number instead.
my code for that part so far is:
Sub calcDays()
Dim dueDate As Date
Dim macroLastRow As Long
Dim macroWs As Worksheet
Set macroWs = ThisWorkbook.Worksheets("Macro")
macroLastRow = macroWs.Range("A" & Rows.Count).End(xlUp).Row
dueDate = Range("B1").Value
For i = 2 To macroLastRow
Cells(i, 2).Value = DateDiff("d", i, dueDate)
Next i
End Sub
This is what I get after I run the macro:
I hope that you guys will be able to advise! Thanks a mil in advance!
There is no value assigned to invoiceDate. You may replace it with Cells(i, 1).Value and that will do the trick.
I also would recommend you to switch on the setting "Require Variable Declaration". You may find the way to do it here in the answer.
Also, discard my edit to your post for problem to remain.
Related
I have a small tracker program I am building in Excel VBA. I have a userform that I keep up throughout the day, inputting my tasks/data via an 'Add' button. At the end of the day, I click the 'Post' button, and it saves the data to my worksheets where appropriate.
Thought I had it finished and working correctly, but now apparently my sub to select the correct column based on the day's date is not working, and I'm not sure why, as it had been working perfectly throughout development.
This piece is vital, as my other functions to post the day's data rely on this. I've read a lot of other posts about how to do this (selecting a column based on current date), but none have explained why mine isn't working. Trying to become a better coder, and not just a copy/paste coder, so thought I would try asking here. Copy/Paste usually gets me into these messes, where I'm using tools/functions that work, but I don't know why, and can't troubleshoot/debug on my own.
My total project involves 5 worksheets, but this sub should only involve 2 of them. "Tasks" and "Data Tracker", both of which have a row of dates.
Below is the sub in question.
Public Sub currentDate()
'sub to assign current date to global values
Set rng - Range("H2:HZ2")
Set myDate = rng.Find(What:=Int(Date), LookIn:=xlFormulas)
End Sub
If I step through it, Date is pulling the correct date, and xlFormulas shows a value of -4123 (I don't even know if that matters)..
(UPDATE) so apparently, this morning, it decided to work perfectly. facepalm Any clues?
(UPDATE) so, per usual, I try adding features as I fix something else, so this took a bit more researching to solve, but #Super-Symmetry pointed me in the right direction! As noted in a comment down below, I changed my date headers in the two sheets to be more of a "start date + n" situation. Although his suggestion of using xlValue instead of xlFormula was on the right track, Find. was still having trouble with date vs serial. Ultimately this is what I got to work:
Public Sub currentDate()
'sub to assign current date to global values
'load the date range
Set rng = Worksheets("Tasks").Range("H2:HZ2")
'load the values in the range
dateArray = Range("H2:HZ2").Value
Dim day As Variant 'object to load dateArray
Dim loc As Integer 'matches date with cell location
'converting the date to serial
For Each day In dateArray
day = CLng(day)
loc = loc + 1
If day = Date Then 'we found the right column
Set myDate = rng(loc)
'selects the correct cell
If ActiveSheet.name = "Data Tracker" Then 'adjust the row
Cells(myDate.Row + 3, myDate.Column).Select
Else 'sheet must be Tasks
Cells(myDate.Row + 2, myDate.Column).Select
End If
Exit Sub
End If
Next
End Sub
It's not elegant, but it works.. please feel free to educate me if you have any cleaner ways to do this!
Try changing Int(Date) to CLng(Date)
Public Sub currentDate()
'sub to assign current date to global values
Dim rng As Range, myDate As Range
Set rng = Range("H2:HZ2")
Set myDate = rng.Find(What:=CLng(Date), LookIn:=xlValues)
End Sub
Say that I have a spreadsheet that allows users to put in some metadata like the following:
Date range start: mm/dd/yyyy
Date range end: mm/dd/yyyy
Mondays: (y/n)
Tuesdays: (y/n)
Wednesdays: (y/n)
Thursdays: (y/n)
Fridays: (y/n)
Based on that, I want to generate a list of dates in the format of mm/dd/yyyy that starts on 4/1/2019, ends on 4/30/2019 and only includes dates of the day that was indicated with a y.
So if a user put in start date = 04/01/2019, end date = 04/30/2019, y for just Mondays and Wednesdays, the list would look like:
04/01/2019
04/03/2019
04/08/2019
04/10/2019
04/15/2019
04/17/2019
04/22/2019
04/24/2019
04/29/2019
Couldn't find an Excel function to start with. I don't know VBA but imagine it would be possible to do this with that.
If I wanted to write this in Python by using an add-in like Pyxll, would everyone with a copy of the Excel file be required to install Pyxll?
#simplycoding: VBA doesn't have a function or suroutine ready to do what you are looking for right out of the box. However, you can compose your own ones based on whatever VBA has on offer, which is a lot.
This is my startup scenario:
I wrote, tested and commented the following SUB in some 20 minutes. Believe me when I say I'm not a first row VBA coder.
Sub DateList()
'(C) Antonio Rodulfo, 2019
Dim dEnd, dStart, dCounter As Date
Dim iaDays(1 To 5) As Integer
Dim iCounter, iRow As Integer
' Indent your sentences properly
' Reading parameters
dStart = Cells(2, 1).Value
dEnd = Cells(2, 2)
For iCounter = 1 To 5
If Cells(2, 2 + iCounter) = "y" Then
iaDays(iCounter) = 1
Else
iaDays(iCounter) = 0
End If
Next iCounter
' Here's where the list of dates will start
iRow = 4
' I process the dates: Excel stores dates in its own
' coding, which we can use to run a for..next loop
For dCounter = dStart To dEnd
' Weekday(datecode,type) returns the day of week
' type 2 means the week starts with monday being day 1
iCounter = Weekday(dCounter, 2)
' The sub only sets dates for working days
' monday to friday
If iCounter <= 5 Then
' date must be set if found "y" in the matching day
If iaDays(iCounter) = 1 Then
' I like setting the proper numberformat so
' that no surprises are found when reading it
Cells(iRow, 1).NumberFormat = "dd/mmm/yyyy"
Cells(iRow, 1) = dCounter
' Increasing iRow sets the focus on next row
iRow = iRow + 1
End If
End If
' Adding the index name to the next sentence helps
' tracking the structures
Next dCounter
End Sub
I always recommend using Option Explicit when coding. It can seem annoying at first, but it will help you a lot when testing and debugging your code.
Good luck!
I was challenged by your request to not use VBA at all, so I was playing around with some Excel functions and here's what I would like you to try.
I used your metadata in the following cells:
OPTION 1 - easy
Enter the following Array function into cell D1 (Ctrl+Shift+Enter) and drag it all the way to the right (e.g. up to cell AW1):
=IF(AND(ISNUMBER(MATCH(WEEKDAY($B$1+COLUMN()-4,2),--($B$3:$B$7="y")*(ROW($B$3:$B$7)-2),0)),($B$1+COLUMN()-4)<=$B$2),$B$1+COLUMN()-4,"")
All dates that are between Start & End dates and for the required weekdays are displayed. However, this option does not prevent the gaps, so you will have lots of blank columns in between.
OPTION 2 - complete
Enter the following Array function into cell D2 (Ctrl+Shift+Enter) and drag it all the way to the right:
=IFERROR(SUMPRODUCT(SMALL(($B$1+ROW($B$1:$B$30)-1)*(IF(ISNUMBER(MATCH(WEEKDAY($B$1+ROW($B$1:$B$30)-1,2),(--($B$3:$B$7="y")*(ROW($B$3:$B$7)-2)),0)),1,0)),30-COUNT(MATCH(WEEKDAY($B$1+ROW($B$1:$B$30)-1,2),(--($B$3:$B$7="y")*(ROW($B$3:$B$7)-2)),0))+COLUMN()-3)),"")
As you can see, this option displays all "proper" dates next to each other, without any gaps.
Hope you will find this helpful - I had lots of fun playing around with the second option and I'm sure it can be still improved.
Take care,
Justyna
I am working on a userform to record counts at certain times of the day.
I automatically add the date and day to the first two fields. There are different fields for one day of the week compared to the others, hence the if statement:
Private Sub UserForm_Initialize()
If Format(Date, "ddd") <> "Sat" Then
DateWkd.Value = Format(Date, "mm/dd/yy")
DayWkd.Value = Format(Date, "ddd")
Else
DateSat.Value = Format(Date, "mm/dd")
DaySat.Value = Format(Date, "ddd")
End If
End Sub
Data will be submitted at different times of the day.
How do I find if the last row's day value equals today's day to update the row, or create a new row if the date doesn't match?
Will write out a little bit more as comments aren't great for code.
In general, you should appropriately qualify references, so in this case with using your userform, you will need to specify the sheet/etc.
Dim lr as Long, varDay as Long
varDay = 1 'assumes using ColumnA, but you could make this a Find() function if necessary
With Sheets("Data")
lr = .Cells( .Rows.Count, varDay).End(xlUp).Row
If DateTextBox.Value <> .Cells(lr,varDay).Value Then 'Not sure if you want just Date (todays' date, no time) or the value in your input for the comparison (gave arbitrary name for textbox example)
'Do your thing
End if
End With
This would be in your command button for entering data, to determine where it would go. If you need to pull data from the sheet on initialize, you would then set textbox.value = .cell references... note that these two situations are not within the same module.
Could you please someone can help me on how to change the date formatting cause i use the following code and instead of today's date it gives me august date. I mean it takes the date of my laptop (08-sep-2015) and it returns on the excel cell (09-Aug-2015).
Private Sub UserForm_Initialize()
'testing variable
'Dim LValue As String
'LValue = Format(#4/17/2004#, "Long Date")
'close testing variable
Me.tbDate = Date
'fill combobox
For Each cell In [cartridges]
Me.cmbCartridges.AddItem cell
Next cell
End Sub
The part that i exclude form the code is a test i made for a solution i found over the internet.
Also the text box that i want to present the date has the following code
ssheet.Cells(nr, 1) = CDate(Me.tbDate)
I found this and sorted my issue. Now everything its working like a charm.
Me.tbDate = Format(Date, "dd/mm/yyyy")
I resolved my issue with this code. I intentionally formated the cell the wrong way (excel default) and then formated it as I needed.
For j = 0 To LISTBOXNAME.ListCount - 1
LISTBOXNAME.List(j) = Format(DateValue(LISTBOXNAME.List(j)), "m/d/yyyy")
LISTBOXNAME.List(j) = Format(DateValue(LISTBOXNAME.List(j)), "dd.mm.yyyy")
Next j
So, right now I have this excel sheet where there is a last revision date. I have named this column "LastRevisionDate". And then I have a column named "RevisionFrequency" . The "RevisionFrequency" contains a drop-down menu consisting of terms, "Annually", "Bi-Annually"(2 times in a year), "Semi-Annually", and "Quarterly". And then I have a column where it states the "NextRevisionDate". So I want to create a VBA code that would calculate the NextRevisionDate from the LastRevisionDate and the RevisionFrequency.
For example. Say in column "A" i have the RevisionFrequency to be "Bi-annually" And the last revision date was Mar-14 in column "B", then I would want the NextRevisionDate in column "C" to state Mar,Sep .Thats basically saying that the item gets revised twice a year. So I would want to create a macro where Column "C" is based off the RevisionFrequency and LastRevisionDate. I realize I could do this with a formula, but I have new items being added constantly so I do not want to keep copying formulas into each cell. Also for some items, they do not need revision, I would also like to have a blank cell if there is no LastRevisionDate.
So far, I have this updated code:
Private Sub Worksheet_Change(ByVal Target As Range)
Dim ws As Worksheet
Set ws = Sheets(1)
If Not Intersect(Target, ws.Range("LastCalDate").Value) Is Nothing Then
Dim Lastdate As Date
Dim DueDate As Variant
Dim Frequency As String
Dim R As Variant
Dim C As Variant
Dim R1 As Variant
Dim C1 As Variant
Dim R2 As Variant
Dim C2 As Variant
R = Range("LastCalDate").Row
C = Range("LastCalDate").Column
R1 = Range("CalDueDate").Row
C1 = Range("CalDueDate").Column
R2 = Range("CalFrequency").Row
C2 = Range("CalFrequency").Column
Lastdate = Cells(R, C).Value 'Last Cal Date
DueDate = Cells(R1, C1).Value 'Cal Due Date
Frequency = Cells(R2, C2)
If Frequency = "Annually" Then
DueDate = DateAdd("mmm", 12, Lastdate)
End If
If Frequency = "Semi-Annually" Then
DueDate = DateAdd("mmm", 6, Lastdate)
End If
If Frequency = "Quarterly" Then
DueDate = DateAdd("mmm", 3, Lastdate)
End If
End Sub
“Am I just overcomplicating my code?”
That is the wrong question. The first question is not “What can I do?” but “What do my users want?”
If you start by saying “I can produce wooden clogs”, you may never learn they want soft slippers. You may not be able to produce soft slippers but you can probably produce something a lot better than wooden clogs if know it is required. Start with the design of the ideal product then cut it down to what is practical.
From your description, I visualise something like this:
You may have many other columns and these three columns may be in different positions; it does not matter, we will restrict ourselves these columns for now.
It sounds to me as though you have two requirements and an issue:
You have a worksheet where the values in the Next Revision Date column may be unreliable or missing. You require a macro that will run down the existing worksheet and enter correct values into the Next Revision Date column.
You have a requirement to set the values in the Next Revision Date column automatically as new rows are added of existing Revision Frequencies and Last Revision Dates are amended. This could be achieved by running macro 1 or using the Worksheet Change event, as you suggest. There may be other approaches but I will not address this requirement.
If you look at the last three rows of my example worksheet, you will notice the day of the month in the Next Revision Date column is not the same as that in the Last Revision Date. This is because I converted the value in the Frequency column to 3, 6 or 12 and added that number of months to the Last Revision Date. In the last three rows the new month does not have as many days as the old and the VBA function has, for example, converted 30 February to 2 March. Is this the effect you require? I have included code to bring the date back to the “correct” month. Often the most difficult task in macro design is identifying all these exceptions and specifying how they are to be handled.
I will only consider macro 1 first since you can use it for both requirements while you are design and implementing macro 2. If you run into problems with macro 2, ask a new question. You can ask as many questions as you like – providing they are good questions – but they should only be one issue per question.
You need a macro that will step down every row of the worksheet. If you are using an online tutorial or you have bought a book on Excel VBA, you may find a suitable example there. If you are using neither an online tutorial nor a book, please start. It will not take long to master the basics of Excel VBA and the time spent learning the basics will quickly repay itself. Trying to search the web for code when you do not know the basics is very difficult.
If your tutorial/book does not tell you how to step down every row of the worksheet, try searching SO for “[excel-vba] find last row of worksheet”. There are lots of variations of this question so you should have no difficulty in finding something suitable. You do not have to do so on this occasion because I show you how below but I believe this is the best way of using this site. Break your requirement down into little steps and then search for a question relevant to each step.
Below is a simple macro 1. Study my code and come back with questions if necessary. However, the more you can understand on your own, the faster you will develop.
Welcome to the joys of programming.
Option Explicit
' Using constants for values that may change makes your code easier to
' understand and easier to maintain.
Const ColFrequency As Long = 1
Const ColLastRevisionDate As Long = 2
Const ColNextRevisionDate As Long = 3
Const RowDataFirst As Long = 2
Sub FixNextRevisionDate()
Dim DateLastCrnt As Date
Dim DateNextCrnt As Date
Dim NumMonthsToStep As Long
Dim RowCrnt As Long
Dim RowLast As Long
' Replace "Data" with the name of your worksheet
With Worksheets("Data")
' This is the most popular method of finding the last row but it will
' not work in every situation. I believe it is appropriate for your
' current requirement but suggest you look for questions that describe
' other methods and which explain why they might be better.
RowLast = .Cells(Rows.Count, ColFrequency).End(xlUp).Row
For RowCrnt = RowDataFirst To RowLast
' Convert Frequency to 3, 6 or 12
' I have used the LCase function to allow for inconsistent use of
' upper and lower case
Select Case LCase(.Cells(RowCrnt, ColFrequency).Value)
Case "annually"
NumMonthsToStep = 12
Case "bi-annually"
NumMonthsToStep = 6
Case "semi-annually"
NumMonthsToStep = 6
Case "quarterly"
NumMonthsToStep = 3
Case Else
' Unknown frequency. never assume the worksheet is correct
' if an error will cause your macro to fail.
' This is an easy way to highlight faulty values for user
' attention.
With .Cells(RowCrnt, ColFrequency)
.Interior.Color = RGB(255, 0, 0)
NumMonthsToStep = 0
End With
End Select
If NumMonthsToStep <> 0 Then
' Had valid frequency
If IsDate(.Cells(RowCrnt, ColLastRevisionDate).Value) Then
' Value in Last Revision Date column is a date
DateLastCrnt = .Cells(RowCrnt, ColLastRevisionDate).Value
' Calculate next date by adding NumMonthsToStep
DateNextCrnt = DateSerial(Year(DateLastCrnt), _
Month(DateLastCrnt) + NumMonthsToStep, _
Day(DateLastCrnt))
' You may not want this but it shows how to do it if you do
If Day(DateNextCrnt) < Day(DateLastCrnt) Then
DateNextCrnt = DateSerial(Year(DateNextCrnt), _
Month(DateNextCrnt), _
0)
End If
With .Cells(RowCrnt, ColNextRevisionDate)
.Value = DateNextCrnt
' Replace with date format of your choice
.NumberFormat = "d mmm yy"
End With
Else
' The Last Revision Date is not a date
With .Cells(RowCrnt, ColLastRevisionDate)
.Interior.Color = RGB(255, 0, 0)
End With
End If
End If
Next
End With
End Sub