| Priority | Rule Name | Scope | Condition (Logic) | Output / Remark Pattern | Status |
|---|---|---|---|---|---|
| ▼ Group A No Work Times Recorded — C / D empty A1→A2→A2.5(FT)→A2.7(FT)→A2.7(PT)→A2.5(PT): If/ElseIf chain (mutually exclusive). PT employees are exempt from Rules 1, 2, and 2.5 (FT); they fall into 2.7(PT) (incomplete time — one of C/D filled) or 2.5(PT) (half-day marker, both C/D empty) where applicable. | |||||
| GRP A1 | Leave without reason (all empty)PT: ⊘ Skipped | WeekdaySaturday (5.5-day) |
(Weekday OR Saturday 5.5-day) AND FT AND • Start Time (C) = Empty • End Time (D) = Empty • Col G = Empty AND Col H = Empty |
DD-MMM & " - Leave without reason (weekday, no time in C/D, no leave type in G/H)" | Done |
| GRP A2 | Leave without reason (has text in G/H, no recognised leave code)PT: ⊘ Skipped | WeekdaySaturday (5.5-day) |
(Weekday OR Saturday 5.5-day) AND FT AND • C = Empty AND D = Empty • Col G or Col H has text (distinguishes from Rule 1) • upCombined (Col G + Col H) contains none of:AL, SL, NPL ← Col G dropdown values NPSL ← Col H only (Others path) PUBLIC HOLIDAY ← substring match |
DD-MMM & " - Leave without reason (weekday, no time in C/D, no leave code in G/H)" | Done |
| GRP A2.5 (FT) | Half-day leave without work hours (FT)PT: ⊘ Skipped (see 2.5 (PT)) | WeekdaySaturday (5.5-day) |
(Weekday OR Saturday 5.5-day) AND FT AND • Remarks contain "(AM)" or "(PM)" • C = Empty AND D = Empty (both) |
DD-MMM & " - Half-day leave but no work hours (expected work time in C/D for other half)" | Done |
| GRP A2.5 (PT) | Half-day leave without work hours (PT)PT: ★ Exclusive | Weekday |
isPartTime = True AND isWeekday AND • hasRemark = True • C = Empty AND D = Empty (both) • Remarks contain "(AM)" or "(PM)" |
DD-MMM & " - Half-day leave but no work hours (expected work time in C/D for other half)" | Done |
| GRP A2.7 (FT) | Incomplete time entry — one of start/end missing (FT)PT: ⊘ Skipped (see 2.7 (PT)) | WeekdaySaturday (5.5-day) |
(Weekday OR scheduled Saturday) AND FT AND • Exactly one of C or D is filled |
DD-MMM & " - Incomplete time entry (has start time in C but missing end time in D)" or DD-MMM & " - Incomplete time entry (has end time in D but missing start time in C)" |
Done |
| GRP A2.7 (PT) | Incomplete time entry — one of start/end missing (PT)PT: ★ Exclusive | All Days |
isPartTime = True AND • Exactly one of C or D is filled |
DD-MMM & " - Incomplete time entry (has start time in C but missing end time in D)" or DD-MMM & " - Incomplete time entry (has end time in D but missing start time in C)" |
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 presentPT: ✓ Applied | Weekday Saturday (5.5-day) |
(Weekday OR Saturday 5.5-day) AND • C filled AND D filled • Col G starts with "NPL" or "NPSL" (non-Others path) OR Col G = "OTHERS" AND Col H contains "NPSL" (exact case) • No "(AM)" or "(PM)" marker |
DD-MMM & " - 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)PT: ⊘ Skipped | Weekday |
Weekday AND • C filled AND D filled • actualHours > 0 (from C-D) • E NOT numeric (N/A, text, error) |
DD-MMM & " - Working hours missing C-D=" & Format(actualHours,"0.0") & "h E is not numeric (N/A)" | Done |
| GRP B4 | C/D vs E mismatchPT: ✓ Applied | Weekday |
Weekday AND • C filled AND D filled • E is numeric • E ≠ (D-C) × 24 |
DD-MMM & " - Working hours mismatch C-D=" & Format(actualHours,"0.0") & "h E=" & Format(workHours,"0.0") & "h" | Done |
| GRP B5 | Actual < expected hoursPT: ⊘ Skipped | Weekday |
Weekday AND • actualHours + tolerance(0.5) < expectedHours |
DD-MMM & " - Hours mismatch actual " & Format(actualHours,"0.0") & "h expected " & Format(expectedHours,"0.0") & "h" | Done |
| GRP B6 | OT not allowed (no OT eligibility)PT: ⊘ Skipped | Weekday |
Weekday AND • actualHours ≤ expectedHours + tolerance • OT (F) > 0 |
DD-MMM & " - 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)PT: ⊘ Skipped | Weekday |
Weekday AND • actualHours > expectedHours • If hrOTFreeHr = Y: calcOT = MAX(0, actual - expected - 1) If hrOTFreeHr = N: calcOT = actual - expected • calcOT ≠ OT in col F |
DD-MMM & " - 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" | 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)PT: ⊘ Skipped | Saturday |
Saturday AND 5.5-day employee AND • 0 < actualHours < 4 • actualHours < expectedHours • No recognised leave code in Col G or Col H (AL/SL/NPL from Col G; NPSL from Col H; PH/Public Holiday) |
DD-MMM & " - Sat leave without reason (5.5-day staff) actual " & Format(actualHours,"0.0") & "h expected " & Format(expectedHours,"0.0") & "h" | Done |
| GRP C11.5 | Saturday leave without AM/PM marker (5.5-day staff)PT: ⊘ Skipped | Saturday |
Saturday AND 5.5-day employee AND • Active leave type detected (effHasAL / effHasSL / effHasNPL) • No "(AM)" or "(PM)" marker in remarks |
DD-MMM & " - Saturday AL should specify (AM) or (PM)" DD-MMM & " - Saturday SL should specify (AM) or (PM)" DD-MMM & " - Saturday NPL should specify (AM) or (PM)" (3 warning variants — AL, SL, NPL only) |
Done |
| GRP C12 | Saturday OT after 5.5 days (4h+ only)PT: ⊘ Skipped | Saturday |
Sat AND 5.5-day AND • actualHours > 4 • calcOT = actualHours - 4 • calcOT ≠ OT in col F |
DD-MMM & " - 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 |
| ▼ 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)PT: ⊘ Skipped | 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) |
Weekend (Sun/Sat-5day): DD-MMM & " - Weekend hours mismatch C-D=" & Format(actualHours,"0.0") & "h E=" & Format(vE,"0.0") & "h F=" & Format(vF,"0.0") & "h" Public Holiday: DD-MMM & " - Public Holiday hours mismatch C-D=" & Format(actualHours,"0.0") & "h E=" & Format(vE,"0.0") & "h F=" & Format(vF,"0.0") & "h" |
Done |
| GRP D14 | Rest day no work, no issue (Sun/PH/Sat-5day)PT: ✓ Applied | 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)PT: ✓ Applied | Sunday/PH Saturday (5-day) |
(Sunday OR Public Holiday OR Saturday with 5-day staff) AND • Active leave type detected (effHasAL / effHasSL / effHasNPL) |
DD-MMM & " - AL marked on rest day (Sun/PH)" DD-MMM & " - SL on rest day (included in consecutive SL streak)" DD-MMM & " - NPL marked on rest day (Sun/PH)" (3 active leave types only — AL warning, SL informational, NPL warning) |
Done |
| GRP D16 | Others (Col G) informational remarkPT: ✓ Applied | All Days | Col G = "Others" (any employee, any day type) | DD-MMM & " - Others (Col G): '" & Col H value & "' - please review" | Done |
| ▼ Group E Part-Time (PT) Employees — Specific Outputs These outputs apply exclusively to employees with work schedule "PT" in the HR sheet. FT employees receive "N/A" for PT-specific columns. | |||||
| GRP EPT-1 | PT working hours & daysPT: ★ Exclusive | All Days |
isPartTime = True After day loop ends: • Read footer "TOTAL WORKING HOURS" col E → totalPTHours • Read footer "TOTAL WORKING DAYS" col E → totalPTDays |
Intermediate col B: totalPTHours Intermediate col C: totalPTDays (FT employees: both written as "N/A") |
Done |
| GRP EPT-2 | 468-hour statutory cap statusPT: ★ Exclusive | All Days |
isPartTime = True AND HR column "468" = "Y" |
Intermediate col P (pt_468_status): "✓ entitled" (Only written if HR 468 = Y AND isPartTime) |
Done |
intermediate - after scanningOne row is written per employee per run. Col P (pt_468_status) is written only for PT employees where HR 468 = Y. Col Q (total allowance) is the parsed numeric total from HR "other allowance" column. All other columns always written.
Column Schema (A–Q)
| 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 — read from timesheet footer row "TOTAL WORKING HOURS" col E (pre-calculated by Generator). Written as "N/A" for FT employees. |
| C | total working days (PT) | PT employees only — read from timesheet footer row "TOTAL WORKING DAYS" col E (pre-calculated by Generator). 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 scheduled 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). |
| 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 colour-highlighted inline (see table below). |
| N | issues | "Y" if any remarks exist, blank otherwise. Used by downstream processing to filter employees needing attention. |
| O | scan timestamp | Date/time of the most recent validation run for this employee (format: yyyy-mm-dd hh:mm). Always written; overwrites previous value. |
| P | pt_468_status | Written as "✓ entitled" only when isPartTime = True AND HR column "468" = "Y". Blank for all FT employees and PT employees without 468 flag. |
| Q | total allowance | Parsed numeric total from HR "other allowance" column (e.g. "Housing 500; Standby 200" → 700). Always written; overwrites previous value. |
Remarks Column (M) — Inline Colour Highlights
After all remarks are written, specific keywords are coloured within the cell text (character-level formatting, not cell background):
| Keyword | Colour | Trigger condition |
|---|---|---|
| SL > 3 days | Red | Monthly SL count exceeds 3 days |
| overtaking AL | Red | AL taken exceeds HR quota (no. of AL column in input (HR)) |
| overtaking SL | Red | SL taken exceeds HR quota (no. of SL column in input (HR)) |
| overtaking CL hours | Red | CL hours taken exceeds HR quota |
| Blank date/day | Red (entire line) | Inserted row with blank date/day detected — entire remark line coloured red |
| Others (Col G): | Orange (entire line) | Col G = "Others" — informational, HR to review Col H content (RGB 255,140,0) |
| Statutory Holiday: | Blue (entire line) | 468 PT employee SL on statutory holiday — HR to action full-pay (RGB 0,112,192) |
| SL gap across rest days | Black | SL streak spans across rest days — informational only, not an error (vbBlack) |
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 |