I have an issue with a vba code I developed - excel

I developed the vba code below to calculate cumulative production up to one date in the column "K" by adding amounts in the column "I" corresponding to dates less than the one in the column "K"
Option Explicit
Sub Gp()
Dim date2 As Double 'Same p/z production Dates'
Dim Gp As Double 'Cumulative production volumes"
Dim fraction As Double
Dim Daysmonth As Double
Dim Days As Double
Dim i As Integer, j As Integer
Gp = 0
j = 3
For i = 4 To 43
date2 = Cells(i, "K")
Do While (Cells(j, "A") < date2)
Gp = Gp + Cells(j, "I")
j = j + 1
Loop
Daysmonth = Cells(j, "A") - Cells(j - 1, "A")
Days = date2 - Cells(j - 1, "A")
fraction = Cells(j, "I") * Days / Daysmonth
Gp = Gp + fraction
Cells(i, "M") = Gp
Gp = (Gp + Cells(j, "I")) - fraction
j = j + 1
Next i
End Sub
This code takes the first cell of the "K" column and look all the dates less than date in the "A" column. Then it add all the amounts in the "I" column for those corresponding dates. Then if the date in the column "A" is in the same month and year of the date in the column "K" it will calculate the fraction of the amount corresponding to those days. Everything is alright until 8/14/80 when calculating the fraction it gives -17 instead of 14 days for 8/14/80 - 8/31/80. It gives me 6304593 and it should give me 6350518.484.
This is a picture of my answers in the column "M" and the right answers in the column "N". They are very similar but not the same for some dates
Calculated cumulative and right answers next to it

"when calculating the fraction it gives -17 instead of 14 days for 8/14/80 - 8/31/80"
Not sure where you are getting the 14 days. Since 8/31/80 is in the future it would be 17 days after 8/14/80. Based upon your formula of 8/14/80 - 8/31/80, it arrives at -17 which is correct.
If you are okay to include dates greater than 'date2', you could use the built in 'Abs()' function to always make the result positive and that should correct the calculation.
Days = Abs(date2 - Cells(j - 1, "A"))

Output of 8/14/80 - 8/31/80 should be -17 only , instead of 14 days. Just to check, I have tried your code and replaced the value -17 with 14 then output i am getting is 7255151.33 instead of 6350518.484.
I think fraction should not be calculated in this case(8/14/80 - 8/31/80) as the primary condition is date in column K(8/14/80) should be greater than Date in column A(8/31/80) which is failing in this case.
And if it is required then you can check if the output of date2 - Cells(j - 1, "A") is negative then replace it with Cells(j - 1, "A")-date2 to make the result positive.

Related

Adding the Last Few Features to my Scheduling Template Generator

Thanks to some wonderful people here on StackOverflow I have almost completed the code for my scheduling template generator! I just have three more things I would like to add that I am having some trouble with.
Current Breakdown: I have two sheets - "2 - Staff Listing" and "X - Template"
-The Values in in the staff listing sheet are a column of staff names beginning in Cell D9
-Then the Staff values are transferred to the template sheets starting in Cell B6
-There is a drop-down menu in the template sheet in Cell C2 where you can select which template you want to make just as an example it could read 5 Week or 5 Week with AM/PM
-Based on this Key I have it repeating each staff member's names X amount of times (5 in this case) and double that if AM/PM is selected so 10 times in this case
What I am hoping to still achieve:
-In the template sheet I would like column C to be the week number and column D to show AM/PM.
-So, for a 5 week AM/PM Template you would have Column C staring in cell C6 list Week 1, Week 1, Week 2, Week 2, up to Week 5. So, once for each week in the Key if it is AM/PM it should appear twice once for AM and once for PM.
-Then I would like it to list AM starting in cell D6 and then go PM and just keep repeating for the length of the names in column B. Additionally, if it is not an AM/PM template then I would like to hide column D.
Below is the code I currently have.
Sub populate_Template()
Worksheets("X - Template").Range("B6:K1000").ClearContents
Dim SourceData As Range
Set SourceData = Sheets("2 - Staff Listing").Range("D9")
Dim RepititionCell As Range
Set RepititionCell = Sheets("X - Template").Range("C2")
Dim Destination As Range
Set Destination = Sheets("X - Template").Range("B6")
Dim lr As Long
Dim arr As Variant
With SourceData.Parent
lr = .Cells(.Rows.count, SourceData.Column).End(xlUp).Row
arr = .Range(SourceData, .Cells(lr, SourceData.Column)).Value
End With
Dim repetition As Long
If Right(RepititionCell, 5) = "AM/PM" Then repetition = Split(RepititionCell.Value, " ")(0) * 2 Else repetition = Split(RepititionCell.Value, " ")(0)
Dim newarr() As String
ReDim newarr(1 To (lr - SourceData.Row + 1) * repetition, 1 To 1)
Dim count As Long
count = 0
Dim i As Long
For i = LBound(arr) To UBound(arr)
Dim y As Long
For y = 1 To repetition
count = count + 1
newarr(count, 1) = arr(i, 1)
Next y
Next i
Destination.Resize(UBound(newarr) - LBound(newarr) + 1).Value = newarr
End Sub
First you need to dim newarr to have three columns instead of one:
ReDim newarr(1 To (lr - SourceData.Row + 1) * repetition, 1 To 3)
Then your For y Next loop should fill in those other two columns. You can use the Mod operator to determine if you're on an odd or even row.
For y = 1 To repetition
Count = Count + 1
newarr(Count, 1) = arr(i, 1)
'if you're doubling up. You may want to put this in a Boolean variable
'to reduce code duplication
If Right(RepititionCell, 5) = "AM/PM" Then
'if it's an odd number
If y Mod 2 = 1 Then
newarr(Count, 2) = "Week " & (y + 1) / 2
newarr(Count, 3) = "AM"
Else 'even number
newarr(Count, 2) = "Week " & y / 2
newarr(Count, 3) = "PM"
End If
Else
'not doubling up, so y is the week number and nothing in column D
newarr(Count, 2) = "Week " & y
End If
Next y
Finally, you have to change your write line to account for the new columns
Destination.Resize(UBound(newarr, 1), UBound(newarr, 2)).Value = newarr
I got rid of the LBound part of the Resize because you're starting at 1 anyway. It's less robust if you happen to start your array at some other number. But between the likelihood that would happen and the improved readability, I think it's better.

List of 1st and 3rd Mondays in Excel

In Excel, I'd like a list of the 1st and 3rd Mondays in each month, for say the next six months after today.
Using a formula-based approach, I can find the 1st or 3rd Monday after today. I got stuck when trying to populate the list after that. Particularly, a challenge is to account for months with 5 Mondays in them. The formulas get very complex very quickly!
This is the solution I have so far:
Private Sub Auto_Open()
Dim i, mday As Integer
Dim row, col As Integer
Dim d As Date
Dim min As Integer
min = 14 'Minimum set up period
col = 1 'Column to put results in
row = 2 'Row to start from
d = Date + min + 9 - Weekday(Date) 'Find Monday after Minimum set up period
Worksheets("options").Activate
For i = 1 To 26 'Results up to 26 weeks in future
mday = day(d)
If (mday - 7 <= 0) Then 'First week
Cells(row, col).Value = d
row = row + 1
ElseIf (mday - 14 > 0 And mday - 21 <= 0) Then 'Third week
Cells(row, col).Value = d
row = row + 1
End If
d = d + 7 '+1 week
Next
Worksheets("New Starter").Activate
End Sub
Try this:
On A1 place the first day of one month. On A2 place: =EOMONTH(A1,0)+1
On B1 place =A1+MOD(8-WEEKDAY(A22,2),7).
On C1 place =B1+14
Drag an drop down.
The first column is taking the first day of one month and consecutive month, the column Bis returning the first monday and colunm C is returning the third monday of that month.
If you are looking for the 5th monday:
On D1 place: =IF(A1+MOD(8-WEEKDAY(A22,2),7)+28<EOMONTH(A1,0)+1,A1+MOD(8-WEEKDAY(A22,2),7)+28,"")

Type Mismatch error adding 10 Seconds to the previous cell's value

I would like to write to a cell the previous cells time value + 10 seconds.
I have tried several approaches after a lot of googling, however below is what I started with and what I would like to understand is why this doesn't work - as in my head it is logical.
The cell data is in the special format DD:MM:YYYY HH:MM:SS - which is a reason this may not work, however if I add + (10 / (3600 * 24)) to the cell manually then it successfully adds on 10 seconds.
Dates are stored as custom and show up as 24/09/2018 08:41:09.
Public Sub Add_Row()
Dim Row As Variant
Dim LR As Long
Dim x As Integer
Dim y As Integer
LR = ActiveSheet.Range("B" & Rows.Count).End(xlUp).Row 'Counts number of rows
x = 1
Row = 1
Do Until x = LR
If Cells(Row, 2).Value <= 1 Then 'If the value of the cell is less than or equal to one do nothing and increment
Row = Row + 1
x = x + 1
Else
y = Cells(Row, 2).Value - 1 'Need a variable for the number of rows we require based on how many missed points we have
For k = 1 To y
ActiveSheet.Rows(Row).Insert Shift:=xlDown
Cells(Row, 1).Value = Cells(Row - 1, 1).Value + (10 / (3600 * 24))
Next
Row = Row + y + 1
x = x + 1
End If
Loop
End Sub
MUltiplying 3600 by 24 in VBA will give an overflow error, because the max value of a 16 bit integer (the default type for the result when both the input numbers are integers) is 32767. You can either use a "#" which will tell VBA that you want to treat the result as a double, like so:
Cells(Row, 1).Value = Cells(Row - 1, 1).Value + (10 / (3600# * 24))
OR you can use "#12:00:10 AM#" which represents 10 seconds, rather than attempting to calculate it, like so:
Cells(Row, 1).Value = Cells(Row - 1, 1).Value + #12:00:10 AM#
Hope this helps.

VBA count columns and copy them

Below code hide a certain number of rows(depending on the number of the week we are in) and shows only the cell corresponding to the current week number and cells corresponding to the future week numbers.
Dim test As String
test = Format(Now, "yyyy", vbMonday) & KW(Now)
For k = 3 To lastColumn
ThisWorkbook.Worksheets(PlanningTableNameUG).Columns(k).ColumnWidth = cWidth
If ThisWorkbook.Worksheets(PlanningTableNameUG).Cells(1, k).Value = test Then
today = True
On Error Resume Next
ThisWorkbook.Worksheets(PlanningTableNameUG).Columns(k - 1).Ungroup
On Error GoTo 0
ThisWorkbook.Worksheets(PlanningTableNameUG).Columns(k - 1).Group
End If
If Not today Then
On Error Resume Next
ThisWorkbook.Worksheets(PlanningTableNameUG).Columns(k).Ungroup
On Error GoTo 0
ThisWorkbook.Worksheets(PlanningTableNameUG).Columns(k).Group
ThisWorkbook.Worksheets(PlanningTableNameUG).Columns(k).Hidden = True
If Hidden = True Then
ThisWorkbook.Worksheets(PlanningTableNameUG).Columns(k).Group.Copy
ThisWorkbook.Worksheets(PlanningTableNameUG).Columns(k).Group.Insert Shift:=xlToRight
End If
Else
ThisWorkbook.Worksheets(PlanningTableNameUG).Columns(k).Hidden = False
End If
Next k
' calculate the week number
Function KW(d As Date) As Integer
Dim Tag As Long
Tag = DateSerial(Year(d + (8 - Weekday(d)) Mod 7 - 3), 1, 1)
KW = (d - Tag - 3 + (Weekday(Tag) + 1) Mod 7) \ 7 + 1
End Function
Now, I need to count how many columns were hidden and add the exact numbers of columns, example:
columns:
1,2,3,4,5,6,7,8,9,10 (column 4 is corresponding to today week number)
I hide: 1,2,3 because there are in the past weeks, and want to add 11,12, 13, together with the week number corresponding for them, but not more than 1 year from current date.
Count of the weeks starts at the begging of the current year.
When hiding the columns you could add an integer variable that increases by one then use this variable to add that many new columns.
Then to add the week number, use the last column with the week number on it and add one to it for each of the new columns...
To count hidden columns in used range of ThisWorkbook.Worksheets(PlanningTableNameUG):
Dim col As Range
Dim cnt As Long
For Each col In ThisWorkbook.Worksheets(PlanningTableNameUG).UsedRange
Debug.Print col.EntireColumn.Hidden
cnt = cnt - col.EntireColumn.Hidden
Next col
EntireColumn.Hidden returns True if is hidden. True is -1 in VBA, that is why i deduct it from the counter to get positive value.

Subtract Amount From Cell Until It Reaches 0 multiple reminding amount

My formula below subtracts a cell until it reaches zero, and moves to the next one. The subtraction is based on the value “B”. Each time the formula comes across the value “B”, this action is performed.
Question: I have been trying to advance this to formulae, in that each time “B” is found that cell is minuses until zero and those amount multiple by the adjacent price.
could you please provide me with a formula which does this ?
Example: when it comes across the first B the full value of 100 x 10 will be multiplied and the reminder 50 will be multiplied by 15 i.e. 50 x 15 price of the next A. These values will be summed.
=MAX(SUMIF($A$2:A2,"A",$B$2:B2)-SUMIF($A$2:$A$10,"B",$B$2:$B$10),0)
The reminder of the 50 is coming from the difference between the B 150 - A 100 , which leaves 50 to be still absorbed .
Further Calculation for explanation:
Apologies thats meant to say calculation of 6000
Your question is still very unclear. What does "My formula below subtracts a cell until it reaches zero" mean? Also, as OldUgly pointed out, it seems that you are ignoring the second A. Since we can't understand each other, take a look at the code below and try to rewrite it yourself to fit your needs. It assumes the data is in a sheet named "Data", and that there is a button (Button1) to run the code.
Dim lLastRow As Long
Dim i As Integer
Dim QtyNumberA, QtyNumberB, QtyNumberRem As Integer
Sub Button1_Click()
lLastRow = Worksheets("Data").Cells(2, 1).End(xlDown).Row 'Rows with data, starting 2nd row (titles in the first)
QtyNumberA = 0 'Variable for storing quantities of A
QtyNumberB = 0 'Variable for storing quantities of B
QtyNumberRem = 0 'Variable for storing quantities remaining
For i = 2 To lLastRow 'scan all rows with data
If (Worksheets("Data").Cells(i, 1).Value = "A") Then
QtyNumberA = QtyNumberA + Worksheets("Data").Cells(i, 2).Value
ElseIf (Worksheets("Data").Cells(i, 1).Value = "B") Then
QtyNumberB = QtyNumberB + Worksheets("Data").Cells(i, 2).Value
QtyNumberRem = QtyNumberA - QtyNumberB
Worksheets("Data").Cells(i, 6) = QtyNumberRem
End If
Next
End Sub

Resources