Skip to content

feat: demos/overtime.prg — Overtime Tracking demo (#46) - #54

Merged
DDecoene merged 1 commit into
release/v1.2.0from
feature/46-overtime-demo
Jul 9, 2026
Merged

feat: demos/overtime.prg — Overtime Tracking demo (#46)#54
DDecoene merged 1 commit into
release/v1.2.0from
feature/46-overtime-demo

Conversation

@DDecoene

@DDecoene DDecoene commented Jul 9, 2026

Copy link
Copy Markdown
Owner

Closes #46. Depends on #43 (TIME), #44 (WEEK), #45 (grid validation), #52 (DATEADD) — all merged.

What

An Overtime Tracker example app, and the showcase for this release's engine work:

  • TIME(15) columns for every clock field — the grid rejects 08:07 inline as you type (BROWSE per-cell validation hook #45).
  • WEEK() shows the ISO week number of the week you're opening.
  • DATEADD() walks the week's Monday through Friday to fill each WORKDATE.

Five linked tables (EMPLOYEES, SCHEDULEDAYS, TIMESHEET, WEEKSUMMARY, LEAVETAKEN) across five work areas with relations into EMPLOYEES. Each employee has their own weekly schedule, so standard hours are a real per-employee sum (S001 = 40.00 h/wk, S002 = 31.25 h/wk), not a flat 40. Overtime is banked per week and drawn down as leave; the balance is computed live from the source rows, so re-editing a week can't leave a stale running total behind.

Menu: add employee · edit schedule (BROWSE) · open/prep week (auto-creates the 5 days from the schedule, then BROWSEs them) · recalculate week · register leave · overtime balance · report · CSV export · table tour.

Two engine constraints, verified before writing the program

The issue's spec assumed both of these worked. They don't, and I checked rather than discovered it in the demo:

  1. SUM … FOR and SET FILTER are spliced raw into SQL and cannot see a memory variable — SUM OVERTIME FOR EMPID == m_emp fails with no such column: M_EMP. The crm.prg idiom works only because its condition is a literal (STAGE == "Won"). So the per-employee balance accumulates in a DO WHILE/IF loop. It is still computed live with no stored balance field, which is the property the issue actually wanted. Same reason the prep-week screen browses the whole timesheet instead of SET FILTER TO WEEKDATE == m_week.
  2. Field references only resolve where the row cache is primed (IF, DO WHILE condition, @ SAY) — not in ?, STORE or a bare REPLACE … WITH <expr>. REPLACE WH WITH VAL(SUBSTR(TIMEIN,1,2)) at the top level silently writes 0. All the time arithmetic therefore lives inside loop bodies.

Times are HH:MM text, so worked hours convert to minutes first: (TIMEOUT - TIMEIN) - (BEND - BSTART), rounded to 2 dp.

A bug the demo would have shipped with

My first draft gated first-run seeding on LEAVETAKEN being empty, mirroring crm.prg's use of CONTACTS. But leave legitimately stays empty until someone registers some — so every DO overtime would have re-dropped TIMESHEET and WEEKSUMMARY, destroying the user's data. The gate is now EMPLOYEES, which the seed always fills; there's a comment explaining why. Covered by driving DO overtime twice and asserting the rows survive.

Assistant parity

Programs → Run Overtime demo (DO overtime), alongside the CRM and Inventory launchers. Also added to the splash screen and HELP.

Test plan

Suites run serially (they share data/).

  • npx tsc --noEmit clean.
  • npm test379/379 vitest. tests/DemoSchemas.test.ts (the golden pins from Test hardening: strict CREATE TABLE parser, demo schema pins, untested WS message types #50) failed the moment the new tables appeared, exactly as designed, and now pins all five.
  • npx playwright test94/94 (was 84). New tests/overtime.spec.ts (9): menu renders; seeds two employees on different schedules; prep-week derives Mon–Fri with DATEADD and shows ISO week 28; the schedule grid rejects an off-quarter TIME(15) edit and accepts 08:15; recalculation asserts the actual numbers (4×8.00 + 10.00 = 42.00 worked, 40.00 standard, +2.00 overtime); balance = 2.00 banked − 1.50 leave = 0.50; the leave form rejects 1.3 as not a quarter-hour step; the grouped report renders; CSV downloads as weeksummary.csv.

Per the project's assert-the-value rule, the specs assert the computed figures in the surface they render in (#form-view), not just that a keyword appeared. Friday's overtime is entered through the validated grid, so the demo's showcase path is the one under test.

Docs

CHANGELOG.md, README.md (demo list), CLAUDE.md (demos tree, roadmap, test counts), new demos/reports/overtimebyemp.json.

An overtime tracker, and the showcase for this release's engine work:
TIME(15) columns validated per-cell as you type in BROWSE, WEEK() for
the ISO week number, and DATEADD() to walk a week's Monday to Friday.

Five linked tables (EMPLOYEES, SCHEDULEDAYS, TIMESHEET, WEEKSUMMARY,
LEAVETAKEN) across five work areas with relations into EMPLOYEES.
Each employee has their own weekly schedule, so standard hours are a
real per-employee sum rather than a flat 40. Overtime is banked per
week and drawn down as leave; the balance is computed live from the
source rows, so re-editing a week cannot leave a stale running total.

Two engine constraints shaped the implementation, both verified before
writing the program:

- SUM ... FOR and SET FILTER are spliced raw into SQL and cannot see a
  memory variable, so the per-employee balance accumulates in a
  DO WHILE / IF loop rather than the crm.prg SUM ... FOR ... TO idiom,
  whose condition is a literal.
- Field references only resolve where the row cache is primed (IF,
  DO WHILE, @ SAY), which is why the time arithmetic lives inside the
  loop body.

Times are HH:MM text, so worked hours convert to minutes first:
(TIMEOUT - TIMEIN) - (BEND - BSTART), rounded to two decimals.

Seeds a grouped report and is reachable from the splash screen, HELP,
and the Assistant (Programs -> Run Overtime demo). The DemoSchemas
golden pins from #50 caught the new tables immediately and now cover
them.
@DDecoene
DDecoene merged commit 5d06863 into release/v1.2.0 Jul 9, 2026
2 checks passed
@DDecoene
DDecoene deleted the feature/46-overtime-demo branch July 9, 2026 19:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant