| Priority | Rule Name | Scope | Condition (Logic) | Output / Remark Pattern | Status |
|---|---|---|---|---|---|
| ▼ Group A No Work Times Recorded — C / D empty A1→A2→A2.5: If/ElseIf chain (mutually exclusive); A2.1 = implicit exemption inside A2. Remarks scans Col G + Col H together. | |||||
| GRP A1 | Leave without reason (all empty) | Weekday |
Weekday AND • Start Time (C) = Empty • End Time (D) = Empty • Remarks (H) = Empty |
dispDate & " Leave without reason (weekday, no time in C/D, no remarks in H)" | Done |
| GRP A2 | Leave without reason (missing time, no AL/SL/PH) | Weekday |
Weekday AND • (C = Empty OR D = Empty) • Remarks (H) NOT contain: AL/SL/CL/ML/PL/NPL/NPSL/Public Holiday |
dispDate & " Leave without reason (weekday, no time in C/D, no AL/SL/Public Holiday in remarks)" | Done |
| GRP A2.1 | Special paid leave (CL/ML/PL) | Weekday Saturday PH |
(Weekday OR Sat OR PH) AND • C = Empty AND D = Empty • actualHours = 0 • Remarks contain: CL or ML or PL |
(No remark - valid paid leave CL/ML/PL) | Done |
| GRP A2.5 | Half-day leave without work hours | Weekday |
Weekday AND • Remarks contain "(AM)" or "(PM)" • C = Empty AND D = Empty |
dispDate & " - Half-day leave but no work hours (expected work time in C/D for other half)" | Done |
| ▼ Group B Work Times Present — C & D both filled Each rule is an independent parallel check — not an If/ElseIf chain. | |||||
| GRP B2.6 | Full-day NPL/NPSL with work hours present | Weekday Saturday (5.5-day) |
(Weekday OR Saturday 5.5-day) AND • C filled AND D filled • Remarks contain "NPL" or "NPSL" • No "(AM)" or "(PM)" marker in remarks |
dispDate & " - Full-day NPL/NPSL but work hours present (C-D=" & Format(roundedActual,"0.0") & "h)" | Done |
| GRP B3 | Working hours not numeric (E is N/A) | Weekday |
Weekday AND • C filled AND D filled • actualHours > 0 (from C-D) • E NOT numeric (N/A, text, error) |
dispDate & " Working hours missing C-D=" & Format(actualHours,"0.0") & "h E is not numeric (N/A)" | Done |
| GRP B4 | C/D vs E mismatch | Weekday |
Weekday AND • C filled AND D filled • E is numeric • E ≠ (D-C) × 24 |
dispDate & " Working hours mismatch C-D=" & Format(actualHours,"0.0") & "h E=" & Format(workHours,"0.0") & "h" | Done |
| GRP B5 | Actual < expected hours | Weekday |
Weekday AND • actualHours + tolerance(0.5) < expectedHours |
dispDate & " Hours mismatch actual " & Format(actualHours,"0.0") & "h expected " & Format(expectedHours,"0.0") & "h" | Done |
| GRP B6 | OT not allowed (no OT eligibility) | Weekday |
Weekday AND • actualHours ≤ expectedHours + tolerance • OT (G) > 0 |
dispDate & " OT not allowed actual " & Format(actualHours,"0.0") & "h expected " & Format(expectedHours,"0.0") & "h OT(col F)=" & Format(otFromCol,"0.0") & "h" | Done |
| GRP B7 | OT miscalculation (1-hour buffer) | Weekday |
Weekday AND • actualHours > expectedHours • calcOT = MAX(0, actual - expected - 1) • calcOT ≠ OT in col F |
dispDate & " OT miscalculation actual " & Format(actualHours,"0.0") & "h expected " & Format(expectedHours,"0.0") & "h OT(col F)=" & Format(otFromCol,"0.0") & "h OT(calc)=" & Format(calcOT,"0.0") & "h (OT = hours - 1)" | Done |
| GRP B8 | Late coming (time-based) | Weekday |
Weekday AND • C filled AND D filled • startTime > scheduledStartTime + 15min |
dispDate & " Late coming start " & Format(startTime,"hh:nn") | Pending |
| GRP B9 | Early leave (time-based) | Weekday |
Weekday AND • C filled AND D filled • endTime < scheduledEndTime - 15min |
dispDate & " Early leave end " & Format(endTime,"hh:nn") | Pending |
| GRP B10 | Short hours (possible early leave/late coming) | Weekday |
Weekday AND • C filled AND D filled • actualHours + tolerance < expectedHours |
dispDate & " Working hours less than standard (possible early leave/late coming) actual " & Format(actualHours,"0.0") & "h expected " & Format(expectedHours,"0.0") & "h" | Done |
| ▼ Group C Saturday — 5.5-day Staff Only Applies only when HR flag "5.5 days (Y/N)" = Y. 5-day staff Saturday falls into Group D instead. | |||||
| GRP C11 | Saturday leave without reason (5.5-day staff) | Saturday |
Saturday AND 5.5-day employee AND • 0 < actualHours ≤ 4 • actualHours < expectedHours • Remarks NOT contain leave types |
dispDate & " - Sat leave without reason (5.5-day staff) actual " & Format(actualHours,"0.0") & "h expected " & Format(expectedHours,"0.0") & "h" | Done |
| GRP C12 | Saturday OT after 5.5 days (4h+ only) | Saturday |
Part A: Sat AND 5.5-day AND • C/D filled, weeklyDays>5.5, hrs≥4 Part B: Sat AND 5.5-day AND • actualHours > 4 • calcOT = actualHours - 4 • calcOT ≠ OT in col F |
Part A: dispDate & " Saturday OT hours=" & Format(actualHours,"0.0") & "h (after 5.5 days rule)" Part B: dispDate & " - Sat OT miscalculation (5.5-day staff) actual " & Format(actualHours,"0.0") & "h OT(col F)=" & Format(otFromCol,"0.0") & "h OT(calc)=" & Format(satOT,"0.0") & "h (OT = hours - 4)" |
Done |
| GRP C11.5 | Saturday leave without AM/PM marker (5.5-day staff) | Saturday |
Saturday AND 5.5-day employee AND • Remarks contain any leave type: AL/SL/CL/ML/PL/NPL/NPSL/COMPASSION • No "(AM)" or "(PM)" marker in remarks |
dispDate & " - Saturday AL should specify (AM) or (PM)" dispDate & " - Saturday SL should specify (AM) or (PM)" dispDate & " - Saturday CL should specify (AM) or (PM)" dispDate & " - Saturday ML should specify (AM) or (PM)" dispDate & " - Saturday PL should specify (AM) or (PM)" dispDate & " - Saturday NPL should specify (AM) or (PM)" dispDate & " - Saturday NPSL should specify (AM) or (PM)" dispDate & " - Saturday COMPASSION should specify (AM) or (PM)" (8 warning variants, one per leave type) |
Done |
| ▼ Group D Rest Day — Sunday / Public Holiday / Saturday (5-day staff) 5-day staff Saturday treated as rest day (same as Sun/PH). 5.5-day staff Saturday handled by Group C. | |||||
| GRP D13 | Rest day hours mismatch (Sun/PH/Sat-5day) | Sunday/PH Saturday (5-day) |
(Sunday OR Public Holiday OR Saturday with 5-day staff) AND • At least one of C-D / E / F filled • NOT (C-D = E = F) |
Version 1: dispDate & " Public Holiday hours mismatch C-D=" & Format(actualHours,"0.0") & "h E=" & Format(vE,"0.0") & "h F=" & Format(vF,"0.0") & "h" Version 2: dispDate & " - Public Holiday hours mismatch C-D=" & Format(actualHours,"0.0") & "h E=" & Format(CDbl(colE),"0.0") & "h" |
Done |
| GRP D14 | Rest day no work, no issue (Sun/PH/Sat-5day) | Sunday/PH Saturday (5-day) |
(Sunday OR Public Holiday OR Saturday with 5-day staff) AND • C = Empty • D = Empty |
(No remark) | Done |
| GRP D15 | Leave marked on rest day (warning) | Sunday/PH Saturday (5-day) |
(Sunday OR Public Holiday OR Saturday with 5-day staff) AND • Remarks contain any leave type: AL/SL/CL/ML/PL/NPL/NPSL/ COMPASSION |
dispDate & " - AL marked on rest day (Sun/PH)" dispDate & " - SL marked on rest day (Sun/PH)" dispDate & " - CL marked on rest day (Sun/PH)" dispDate & " - ML marked on rest day (Sun/PH)" dispDate & " - PL marked on rest day (Sun/PH)" dispDate & " - NPL marked on rest day (Sun/PH)" dispDate & " - COMPASSION marked on rest day (Sun/PH)" (7 different messages based on leave type) |
Done |
intermediate - after scanningOne row is written per employee per run. Columns A–N are always written; special annotations (red highlights) are applied afterwards.
Column Schema (A–N)
| Col | Header | Description |
|---|---|---|
| A | employee ID | Employee code (e.g. A0012). Row is matched by this key on subsequent runs — updated in-place if already exists. |
| B | total working hours (PT) | PT employees only — sum of all C–D hours for the month. Written as "N/A" for FT employees. |
| C | total working days (PT) | PT employees only — count of days with any recorded hours. Written as "N/A" for FT employees. |
| D | 468 | Reserved column (468-hour statutory cap tracking). Currently written as "N/A". |
| E | OT - weekdays/sat (hours) | Total OT hours accumulated on weekdays and 5.5-day Saturdays for the month. |
| F | OT - sun/PH (hours) | Total OT hours accumulated on Sundays, Public Holidays, and 5-day-staff Saturdays. |
| G | no. of AL taken (days) | Count of AL day-equivalents taken (full-day = 1, half-day = 0.5). Validated against HR quota; overtaking AL highlighted red in remarks. |
| H | no. of SL taken (days) | Count of SL day-equivalents taken (full-day = 1, half-day = 0.5). Validated against HR quota; overtaking SL highlighted red in remarks. |
| I | 4 consecutive SL (Y/N) | "Y" if 4 or more consecutive SL days detected, "N" otherwise. "Y" displayed in red font (RGB 255,0,0); "N" resets to automatic colour. |
| J | no. of NPL/NPSL taken (days) | Count of NPL/NPSL day-equivalents taken (full-day = 1, half-day = 0.5). |
| K | CL hours used (compensation leave) | Total CL hours deducted. Uses actual C–D hours when available; falls back to HR paid-hours (Sat default: 4h). Validated against hrCLCurrent balance; overtaking CL hours highlighted red in remarks. |
| L | status | "completed" — no issues found. "needs review" — one or more remarks generated. |
| M | remarks | All validation messages for the employee, newline-separated. "No issue" when clean. Certain keywords are highlighted red inline: SL > 3 days, overtaking AL, overtaking SL, overtaking CL hours. |
| N | issues | "Y" if any remarks exist, blank otherwise. Used by downstream processing to filter employees needing attention. |
Remarks Column (M) — Inline Red Highlights
After all remarks are written, specific keywords are coloured red within the cell text (character-level formatting, not cell background):
| Keyword | Trigger condition |
|---|---|
| SL > 3 days | Monthly SL count exceeds 3 days |
| overtaking AL | AL taken exceeds HR quota (no. of AL column in input (HR)) |
| overtaking SL | SL taken exceeds HR quota (no. of SL column in input (HR)) |
| overtaking CL hours | CL hours used exceeds hrCLCurrent balance from input (HR) |
Each validation run appends one row per issue to timesheet - validation log.xlsx in the same source folder. The file and "issue log" sheet are auto-created if missing. SharePoint URL paths are supported. The log is append-only — it preserves full run history and never overwrites previous results.
| Column | Description |
|---|---|
| A — Run Timestamp | Date/time the validation ran (format: yyyy-mm-dd hh:mm:ss) |
| B — Employee ID | Employee code parsed from filename (e.g. A0012) |
| C — Employee Name | Full name read from timesheet cell C6 |
| D — Year | Timesheet year |
| E — Month | 3-letter month code (e.g. JAN) |
| F — Date | Date extracted from the remark line (text before first " - "); blank for "No issue" rows |
| G — Message | Remark text after the date prefix; "No issue" when no errors detected |
| H — Severity |
"Warning" — message contains "rest day" or "should specify""Error" — all other issuesBlank — "No issue" rows |