Dynamic calendar and sum conditionally formatted cells

harieta

New Member
Joined
Mar 16, 2023
Messages
27
Office Version
  1. 365
  2. 2021
Platform
  1. Windows
Hi,
I'm working on dynamic calendar that has weekends and Bank Holidays conditionally formatted (highlighted)
I'm struggling with formula that would sum values each week depending if it's normal weekday, weekend or Bank Holiday

I have 4 conditional formatting rules in this order
grey out days that don't belong to current month
highlight bank holidays in orange
highlight weekends in green
highlight weekdays in white

I have been using VBA for summing conditional formatted cells

Function SumConditionColorCells(CellsRange As Range, ColorRng As Range)
Dim Bambo As Boolean
Dim dbw As String
Dim CFCELL As Range
Dim CF1 As Single
Dim CF2 As Double
Dim CF3 As Long
Bambo = False
For CF1 = 1 To CellsRange.FormatConditions.Count
If CellsRange.FormatConditions(CF1).Interior.ColorIndex = ColorRng.Interior.ColorIndex Then
Bambo = True
Exit For
End If
Next CF1
CF2 = 0
CF3 = 0
If Bambo = True Then
For Each CFCELL In CellsRange
dbw = CFCELL.FormatConditions(CF1).Formula1
dbw = Application.ConvertFormula(dbw, xlA1, xlR1C1)
dbw = Application.ConvertFormula(dbw, xlR1C1, xlA1, , ActiveCell.Resize(CellsRange.Rows.Count, CellsRange.Columns.Count).Cells(CF3 + 1))
If Evaluate(dbw) = True Then CF2 = CF2 + CFCELL.Value
CF3 = CF3 + 1
Next CFCELL
Else
SumConditionColorCells = ""
Exit Function
End If
SumConditionColorCells = CF2
End Function


now my issue:
helper column should only sum weekdays (exclude Bank Holidays) but even if I reference in formula to cell with white colour, it still takes value of cell that is coloured in orange (Bank Holiday)
is there another way to sum values of cells but their formatted colour? Does my udf needs amending?
my mini sheets shows manually entered values in columns BN to BR to show what I need the formula to calculate, with the ability to adapt to a different month (and different allocation of Bank Holiday in a week - Monday, Friday, Thursday)


Can you please guise where to start editing

Thank you


Book1
AUAVAWAXAYBFBGBHBIBJBKBLBMBNBOBPBQBRBSBTBUBVBWBX
1
220/03/202421/03/202422/03/202423/03/202424/03/202425/03/202426/03/202427/03/202428/03/202429/03/202430/03/202431/03/2024Total O/ThelperO/T @ flat rateO/T @ 1.5WeekendBank Holidaybank holidays
3536721.00807601/01/2024
429/03/2024
501/04/2024
606/05/2024
727/05/2024
826/08/2024
9
10
11
12
13
14
15
16
jan
Cell Formulas
RangeFormula
BF2:BL2BF2=SEQUENCE(,7,AY2+1,1)
BM3BM3=SUM(BF$3:BL$3)
BP3BP3=IF($C3=$D3,$N3,$N3-$O3)
Dynamic array formulas.
Cells with Conditional Formatting
CellConditionCell FormatStop If True
F2:BL60Expression=MONTH(F$2)<>MONTH($U$2)textNO
BF2:BL60Expression=ISNUMBER(VLOOKUP(BF$2,$BW$3:$BW$8,1,0))textNO
AF2:AL60,AS2:AY60,BF2:BL60,S2:Y60,F2:L60Expression=WEEKDAY(F$2,2)>5textNO
AF2:AL60,AS2:AY60,BF2:BL60,S2:Y60,F2:L60Expression=OR(WEEKDAY(F$2)=2,WEEKDAY(F$2)=3,WEEKDAY(F$2)=4,WEEKDAY(F$2)=5,WEEKDAY(F$2)=6)textNO
Cells with Data Validation
CellAllowCriteria
BF2:BL60Custom=MONTH(AZ2)<>MONTH($AY$2)
BM2Any value
BM3:BM60Custom=MONTH(BM2)<>MONTH($AY$2)
 
Thanks. Which formula is generating the #VALUE! error? Which version of Excel is being used?
 
Upvote 0

Excel Facts

VLOOKUP to Left?
Use =VLOOKUP(A2,CHOOSE({1,2},$Z$1:$Z$99,$Y$1:$Y$99),2,False) to lookup Y values to left of Z values.
Hi Kirk,

I tried in Office 2021 and 365
I have created the array for weekday, weekend and Bank holiday and I have added a formula to helper and o/t@1.75 o/t @2.25 cell - this is where i have value error

overtime.xlsm
DEFGHIJKLMNOPQRSTUVWXYZAAABACADAEAFAGAHAIAJAKALAMANAOAPAQARASATAUAVAWAX
3
4Contract hoursThuFriSatSunMonTueWedThuFriSatSunMonTueWedThuFriSatSunMonTueWedThuFriSatSunMonTueWedThu1111100
5   0102030405060708091011121314151617181920212223242526272829     
628.005.005.005.005.005.005.00#REF!this is week 1
7#VALUE!this is week 2
8
finished
Cell Formulas
RangeFormula
AQ4AQ4=NETWORKDAYS.INTL(+L$5#,+L$5#,"0000011",BH)
AR4:AW4AR4=NETWORKDAYS.INTL(+F$5#,+F$5#,"0000011",BH)
E5E5=DATEVALUE(1&A2&B2)-WEEKDAY(DATEVALUE(1&A2&B2),3)
F5:AO5F5=E5+1
AQ6AQ6=SUMPRODUCT(E6:K6,NETWORKDAYS.INTL(+L$4#,+L$4#,"0000011",BH))
AQ7AQ7=SUMPRODUCT(L6:R6,NETWORKDAYS.INTL(+L$5#,+L$5#,"0000011",BH))
Named Ranges
NameRefers ToCells
BH=data!$A$16:$A$23AQ6:AQ7, AQ4:AW4
Cells with Conditional Formatting
CellConditionCell FormatStop If True
E4:AO50Expression=MONTH(E$5)<>MONTH($U$5)textYES
E4:AO50Expression=WEEKDAY(E$5,2)>5textNO
E4:AI50,AJ4:AO4,AM6:AO50,G5:AO5Expression=ISNUMBER(VLOOKUP(E$4,data!$A$15:$A$23,1,0))textNO
 
Upvote 0
Okay, this shows why being able to see a snapshot of your worksheet is so important...I wouldn't have known otherwise. Let's look at your formulas that are returning an error:
Excel Formula:
SUMPRODUCT(L6:R6,NETWORKDAYS.INTL(+L$5#,+L$5#,"0000011",BH))
I see that you must have a named range called BH, so I created one named the same in my worksheet and populated it with a few dates. Beyond that, there is another significant issue:
The NETWORKDAYS.INTL function has a start and end date, and right now, this is set to +L$5# for both start and end dates. The # symbol means to take the entire array that spills from the source formula in cell L$5, but if we look at cell L$5, we see that 1) it isn't the starting point---the source---for a spilling array, and 2) it isn't even part of a spilling array. So this syntax is incorrect in this case. What you have in H5 is a date starting point, and then you construct subsequent dates by dragging a formula across the row. A spilling array, on the other hand, would have a single source formula in H5 that would form the entire array of dates, and all of the dates would spill across the row without having to drag a formula into each cell.

You've adjusted the range reference for overtime hours correctly ((E6:K6 for week 1 and L6:R6 for week 2). Note that each of these is 1 row by 7 columns. The SUMPRODUCT function that they are passed into will want another range reference/array whose dimensions are the same (1 row by 7 columns). So we can fix these formulas several ways. If you want to do this manually. , see examples in AQ6 and AQ7. Alternatively, you could specify the entire range for dates (35 columns wide) and the entire range for hours (also 35 columns wide), and then use a formula to extract only the groups of 7 that matter for each week. One formula that will do this is:
Excel Formula:
SEQUENCE(,7,(AT6-1)*7+1)
...which creates a 7 column wide array, beginning at (AT6-1)*7+1, where AT6 is the cell address with the week number of interest. For week 1 (AT6=1), we get (1-1)*7+1, which is 0*7+1 or just 1. So the array would be {1,2,3,4,5,6,7}. For week 2, we get (2-1)*7+1, which is 1*7+1, or 8, so the array would be {8.9.10,11,12,13,14}.

This array of column numbers needed for each week can be passed to the INDEX function (or the CHOOSECOLS function) to extract the relevant subsets of dates and hours for each week. The formula in AS6 shows this, but you'll see that there is quite a bit of redundancy, as the same column array is needed three times, and this makes the formula messier, more difficult to understand, and more prone to editing errors. An alternative is the wrap these formula components inside a LET function and define a working variable called "cols" (the array of relevant column indexes).. It is defined just once, and then "cols" can be used multiple times where needed. Similarly, the array of dates for each week is used twice, so we can define it one and assign it to the "dts" working variable, and then use it where needed. So the formula in AS7 incorporates these ideas, but it otherwise the functional equivalent of the AS6 formula.

This very issue is why I asked so many questions about your process. You mentioned several times that processing was done monthly, so I thought you could easily apply a spilling array for the entire month and compute the relevant sums for O/T on weekdays, weekends, and holidays. But its more complicated than that: you process monthly in arrears, but you need to evaluate by week, and then consider within the weeks only those days that actually belong to the month of interest. I'm attempting to revise the formulas to do this. The simplest would be to create a 42 column wide (more on this in the next paragraph) helper table that could be hidden. And that

I wanted to comment to something you mentioned earlier...something about needing 5 weeks for each month. Wouldn't you need 6 weeks in some cases? For example a 30 or 31 day month that begins on Sunday, the last day of "week 1" would be followed by 28 more days in weeks 2 through 5, for a total of 29 days up to that point...and then 1 or 2 more days would spill into the 6th week.
 
Upvote 0
I guess I accidentally posted before I was done the next-to-last paragraph in my last post. Picking up there...
The simplest would be to create a 42 column wide (more on this in the next paragraph) helper table that could be hidden. And that helper table would be used by formulas similar to what we've discussed here to process the weekday overtime hours by week. I'd like to do this without a helper table, but that requires some additional steps to handle correctly inside a formula...so I'm still looking at that option.

And here are the formulas I referred to, as well as the date spilled from each of the INDEX/SEQUENCE formulas:
MrExcel_20240109b.xlsx
AQARASATAUAVAWAXAYAZBA
5WeekDates
613this is week 113145348453494535045351453524535345354
715this is week 215245355453564535745358453594536045361
8345362453634536445365453664536745368
9445369453704537145372453734537445375
10545376453774537845379453804538145382
Sheet4
Cell Formulas
RangeFormula
AQ6AQ6=SUMPRODUCT(E6:K6,NETWORKDAYS.INTL(+E5:K5,+E5:K5,"0000011",BH))
AQ7AQ7=SUMPRODUCT(L6:R6,NETWORKDAYS.INTL(+L5:R5,+L5:R5,"0000011",BH))
AS6AS6=SUMPRODUCT(INDEX($E$6:$AM$6,,SEQUENCE(,7,(AT6-1)*7+1)),NETWORKDAYS.INTL(INDEX($E$5:$AM$5,,SEQUENCE(,7,(AT6-1)*7+1)),INDEX($E$5:$AM$5,,SEQUENCE(,7,(AT6-1)*7+1)),"0000011",BH))
AS7AS7=LET(cols,SEQUENCE(,7,(AT7-1)*7+1), dts, INDEX($E$5:$AM$5,,cols), SUMPRODUCT(INDEX($E$6:$AM$6,,cols),NETWORKDAYS.INTL(dts,dts,"0000011",BH)))
AU6:BA10AU6=INDEX($E$5:$AM$5,,SEQUENCE(,7,(AT6-1)*7+1))
Dynamic array formulas.
Named Ranges
NameRefers ToCells
BH=Sheet4!$BD$2:$BD$7AS6:AS7, AQ6:AQ7
 
Upvote 0
oh gosh, I think it's a lot for my little head to comprehend :) There was me thinking it's an easy task

I wanted to comment to something you mentioned earlier...something about needing 5 weeks for each month. Wouldn't you need 6 weeks in some cases? For example a 30 or 31 day month that begins on Sunday, the last day of "week 1" would be followed by 28 more days in weeks 2 through 5, for a total of 29 days up to that point...and then 1 or 2 more days would spill into the 6th week.
I found out that i need more than 5 weeks after my draft when I was looking at all months and run out of columns :) thank you

I see that you must have a named range called BH, so I created one named the same in my worksheet and populated it with a few dates.
yes I have another tab with months listed (for drop down list) and table named BH that has listed all Bank Holidays

So we can fix these formulas several ways. If you want to do this manually. , see examples in AQ6 and AQ7.
I think I can do that one, at least I now know where this formula comes from


With the second version, am I correct to assume the array AU6:BU10 changes automatically each month as it refers to cells E5 : EO5

But its more complicated than that: you process monthly in arrears, but you need to evaluate by week, and then consider within the weeks only those days that actually belong to the month of interest.
Unfortunately, yes. I know it makes it messy and overcalculated but this ensures accuracy when paying and I wouldn't want to give those spreadsheet out knowing this is "cutting corners" by simplifying and just looking on the data as whole month.

With regards to formula in AS7 it looks so advanced that I wouldn't even know where to start to understand elements of it.

You've been kind enough to work with me on my problem, thanks a million!. Whichever way you think it would work I am extremely grateful
 
Upvote 0
I'm sorry to have kept you waiting...I set this aside to think about the weekday adjustments by week for non-FTE employees. I've also revised the approach taken for most of the quantities, and I am presenting an option to consider that keeps most of the data on the main sheet, rather than separate monthly tabs.

The main worksheet in envisioned as a multi-month, or perhaps year-long or multi-year, table whose column headings (dates) are an array formed from a single formula. Currently, that formula assumes the data log will begin on the Monday belonging to data start date specified in cell H1. The reason for this is to preserve overtime hour entries for the entire week, even if the first day(s) of that week belong to a prior year or month. Additionally, the user specifies (in cell H2) the number of months that should be covered by the date array. The date array-generating formula determines the Sunday after the last day of the nth month, again to preserve all overtime hour entries for that last week, even if the last day(s) of that week belong to the next year or month. This is done by the formula in cell M5, which spills the dates array to the right:

The months that are found in the data log are listed on a separate Lists worksheet, using a formula that spills all of them dynamically in mmm-yyyy text format, and this can be used by a drop down selector on the main worksheet using Data Validation, with the Lists worksheet cell address referenced as the source.

The Lists worksheet is also used to maintain a list of bank holidays, beginning in cell A2. Because you will need to add dates to this list, its length might change, so rather than trying to maintain a range reference for a changing list length, I would do one of two things: either enter bank holiday dates in a dedicated table and then use structured references to specify the bank holiday date column name, or declare an overly large fixed range and form a dynamic list of bank holiday dates. I've taken the latter approach, specifying a large fixed range reference of Lists!$A$2:$A$100 and assigning this range to a variable called "HolidayRng". Later in the formula, this range, which contains dates and blank cells, is filtered down to display only the array of dates.

The relationship between the contractual weekly workload (in hours) and that of a full time equivalent position (also in hours) is shown in columns C and D for each row of data. These cells are assigned to variables ContractWorkload and FTEWorkload in the main formula. For convenience, another formula component inside the main LET formula assigns a variable called "ShortFTE_WkD_hrs"...a somewhat long variable name representing the difference---the shortage---between an FTE and the employee's contractual workload.

The overtime hours data on each row begins in the same column as the first date, so a dynamic array of O/T hours is created with the formula. For O/T data on row 6, the formula appears like this:
Data, INDEX(6:6,,COLUMN(Dates)) ...where the entire range of O/T data on that row is assigned to a variable called Data. The advantage in using a dynamic array is to accommodate a changing data table size without needing to revise the data range reference.

The user sets a query month in cell B4. The month-year choice is offered through a drop down list described earlier. Recall that this choice is text having the format mmm-yyyy, so the text is immediately subjected to a mathematical operation where we add 0 to it, which at first does not seem to make sense, but this causes Excel to consider mmm-yyyy as a number---a date---and automatically makes the interpretation that mmm-yyyy is the first day of whatever month and year are chosen. This date value (a number) is assigned to the variable qryMo (short for query month).
MrExcel_20240221.xlsx
ABCDEFGHIJKLMANAOAPAQARASATAUAVAWAXAYAZBABBBCBDBEBFBGBHBIBJBKBLBMBNBOBPBQBRBSBTBUBV
11/1/2024Data Begin Mo-YrData Log
24Num Mos
3ISOWeek1155555556666666777777788888889999999
4Query Month®Feb-2024Month SumsDay# of Wk1212345671234567123456712345671234567
5NameEmployee IDContract (h/wk)FT Equiv (h/wk)Total O/TWeekdays O/T @ FlatWeekdays O/T @ 1.5WeekendBank HolidayDouble Check TotalDate1/1/20241/2/20241/29/20241/30/20241/31/20242/1/20242/2/20242/3/20242/4/20242/5/20242/6/20242/7/20242/8/20242/9/20242/10/20242/11/20242/12/20242/13/20242/14/20242/15/20242/16/20242/17/20242/18/20242/19/20242/20/20242/21/20242/22/20242/23/20242/24/20242/25/20242/26/20242/27/20242/28/20242/29/20243/1/20243/2/20243/3/2024
6Marie10028373017823301143211111112342
7Employee2101373713009127121302475275255524752752552475275252475275
8Employee310222375635111956532673137321121235423124
OTlog
Cell Formulas
RangeFormula
L3:EG3L3=ISOWEEKNUM(L5#)
L4:EG4L4=WEEKDAY(L5#,2)
L5:EG5L5=LET(MonOfFirst,WORKDAY.INTL($H$1+1,-1,"0111111"), NumDays, WORKDAY.INTL(EDATE($H$1,$H$2)-2,1,"1111110")-MonOfFirst+1, MonOfFirst+SEQUENCE(,NumDays,0))
E6:J8E6=LET(Dates, $L$5#, Data, INDEX(6:6,,COLUMN(Dates)),ContractWorkload, $C6, FTEWorkload, $D6, HolidayRng, Lists!$A$2:$A$100, qryMo, $B$4 + 0, RateFactor, {1,1.5,1.75,2.25}, commentRateFactor, {"flat rate","weekday","weekend","bank holiday"}, Holidays, FILTER(HolidayRng, HolidayRng <> ""), ShortFTE_WkD_hrs, FTEWorkload - ContractWorkload, MoFullWks, FILTER(VSTACK(Dates, Data), (Dates >= WORKDAY.INTL(qryMo + 1, -1, "0111111")) * (Dates <= WORKDAY.INTL(EOMONTH(qryMo, 0) - 1, 1, "1111110"))), FW_dts, CHOOSEROWS(MoFullWks, 1), FW_hrs, CHOOSEROWS(MoFullWks, 2), qMoFW_dts, EOMONTH(FW_dts,0)=EOMONTH(qryMo,0), MoWkE_OT, SUM(FILTER(FW_hrs, qMoFW_dts * (WEEKDAY(FW_dts,2)>5))), MoBH_OT, SUM(FILTER(FW_hrs, qMoFW_dts * ISNUMBER(MATCH(FW_dts,Holidays,0)),0)), FW_WkD_hrs, IF(NETWORKDAYS.INTL(FW_dts, FW_dts, "0000011", Holidays), FW_hrs, 0), commentFW_WkD_hrs,{"overwrites with 0 hrs for all WkE & BH"}, revFW_WkD_hrs, LET(a, WRAPROWS(FW_WkD_hrs, 7), b, ShortFTE_WkD_hrs, c, SCANBYROW(0, a, LAMBDA(x,y, x + y)), d, c - b, e, IF(c > d, d, c), f, IF(e > a, a, e), g, IF(f > 0, f, 0), TOROW(g)), MoWkD_OT, SUM(FILTER(revFW_WkD_hrs, qMoFW_dts)), MoWkD_Flat, SUM(FILTER(FW_WkD_hrs, qMoFW_dts)) - MoWkD_OT, MoAll_OT,SUM(MoWkD_Flat, MoWkD_OT, MoWkE_OT, MoBH_OT), MoAll_OTcheck,SUM(FILTER(FW_hrs,qMoFW_dts)), FinalOutput, HSTACK(MoAll_OT,MoWkD_Flat, MoWkD_OT, MoWkE_OT, MoBH_OT, MoAll_OTcheck), FinalOutput)
Dynamic array formulas.
Cells with Conditional Formatting
CellConditionCell FormatStop If True
L3:ZZ12Expression=AND(ISNUMBER(L$5),WEEKDAY(L$5,2)>5)textNO
L3:ZZ12Expression=ISNUMBER(MATCH(L$5,Lists!$A$2:$A$100,0))textNO
Cells with Data Validation
CellAllowCriteria
B4List=Lists!$B$2#

MrExcel_20240221.xlsx
AB
1Bank HolidaysMoYr List
22/19/2024Jan-2024
33/29/2024Feb-2024
44/1/2024Mar-2024
55/6/2024Apr-2024
65/27/2024May-2024
71/1/2024
81/15/2024
92/2/2024
Lists
Cell Formulas
RangeFormula
B2:B6B2=UNIQUE(TRANSPOSE(TEXT(OTlog!L5#,"mmm-yyyy")))
Dynamic array formulas.

The trickiest part of the main formula involves splitting the array of full weeks that cover the query month into individual weeks that can be analyzed to determine when the FTE hours short have been accounted for. That component of the main formula is set off within a separate LET function, and in there is an unusual function called SCANBYROW. The methodology used for the week-by-week evaluation requires an array of the cumulative sum of O/T hours for each full week. Ideally this could be accomplished with a BYROW LAMBDA helper function, where SCAN processes each array element and adds it to the accumulating sum, but BYROW will return only a single value, not an array...and we need the array of accumulating sequential sums. A convenient way to obtain this relies on a custom LAMBDA function called SCANBYROW, created by @tboulden and posted to this forum:

To use the function as I have, the function needs to be created by adding the name SCANBYROW via the Name Manager (or Advanced Formula Environment) and then pasting the following into the "Refers to" block (Name Manger) or "Function definition" (AFE):
Excel Formula:
=LET(
    rows_,ROWS(array),
    cols_,COLUMNS(array),
    row_thunks,
        BYROW(array,
            LAMBDA(row_,
                LAMBDA(SCAN(initial_value,row_,fn))
            )
        ),
    MAKEARRAY(rows_,cols_,
        LAMBDA(i,j,
            INDEX(
                INDEX(row_thunks,i,1)(),
                1,
                j
            )
        )
    )
)
Once that is done, SCANBYROW is available like any other function. It has three arguments: SCANBYROW(initial_value, array, fn). We seed the initial value with 0 (1st argument); pass in a row of weekday O/T hours (where the row represents a full week) (2nd argument), and then define the LAMBDA function to generate an accumulating sum...LAMBDA(x,y, x + y)...(3rd argument). And the SCANBYROW function builds the desired output array from individual results returned by the BYROW function. This process is repeated for each of the full weeks that cover the entire query month.
 
Upvote 0

Forum statistics

Threads
1,215,073
Messages
6,122,977
Members
449,095
Latest member
Mr Hughes

We've detected that you are using an adblocker.

We have a great community of people providing Excel help here, but the hosting costs are enormous. You can help keep this site running by allowing ads on MrExcel.com.
Allow Ads at MrExcel

Which adblocker are you using?

Disable AdBlock

Follow these easy steps to disable AdBlock

1)Click on the icon in the browser’s toolbar.
2)Click on the icon in the browser’s toolbar.
2)Click on the "Pause on this site" option.
Go back

Disable AdBlock Plus

Follow these easy steps to disable AdBlock Plus

1)Click on the icon in the browser’s toolbar.
2)Click on the toggle to disable it for "mrexcel.com".
Go back

Disable uBlock Origin

Follow these easy steps to disable uBlock Origin

1)Click on the icon in the browser’s toolbar.
2)Click on the "Power" button.
3)Click on the "Refresh" button.
Go back

Disable uBlock

Follow these easy steps to disable uBlock

1)Click on the icon in the browser’s toolbar.
2)Click on the "Power" button.
3)Click on the "Refresh" button.
Go back
Back
Top