feat: demos/overtime.prg — Overtime Tracking demo (#46) - #54
Merged
Conversation
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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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 rejects08:07inline 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 eachWORKDATE.Five linked tables (
EMPLOYEES,SCHEDULEDAYS,TIMESHEET,WEEKSUMMARY,LEAVETAKEN) across five work areas with relations intoEMPLOYEES. 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:
SUM … FORandSET FILTERare spliced raw into SQL and cannot see a memory variable —SUM OVERTIME FOR EMPID == m_empfails withno such column: M_EMP. Thecrm.prgidiom works only because its condition is a literal (STAGE == "Won"). So the per-employee balance accumulates in aDO WHILE/IFloop. 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 ofSET FILTER TO WEEKDATE == m_week.IF,DO WHILEcondition,@ SAY) — not in?,STOREor a bareREPLACE … 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:MMtext, 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
LEAVETAKENbeing empty, mirroringcrm.prg's use ofCONTACTS. But leave legitimately stays empty until someone registers some — so everyDO overtimewould have re-droppedTIMESHEETandWEEKSUMMARY, destroying the user's data. The gate is nowEMPLOYEES, which the seed always fills; there's a comment explaining why. Covered by drivingDO overtimetwice 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 andHELP.Test plan
Suites run serially (they share
data/).npx tsc --noEmitclean.npm test— 379/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 test— 94/94 (was 84). Newtests/overtime.spec.ts(9): menu renders; seeds two employees on different schedules; prep-week derives Mon–Fri withDATEADDand shows ISO week 28; the schedule grid rejects an off-quarterTIME(15)edit and accepts08: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 rejects1.3as not a quarter-hour step; the grouped report renders; CSV downloads asweeksummary.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), newdemos/reports/overtimebyemp.json.