310 lines
16 KiB
Markdown
310 lines
16 KiB
Markdown
# Parked / deferred work
|
||
|
||
> Updated 15 May 2026. A small index of features that are designed,
|
||
> half-built, blocked on input, or pending an operator step. When a
|
||
> fresh session opens, glance here first to see what's already on
|
||
> the workbench.
|
||
|
||
---
|
||
|
||
## ⏸ Paused — ready to execute (not started, not pushed)
|
||
|
||
### Site-progress logging — rebuild from scratch (parked)
|
||
|
||
The SiteReport / "Log Today's Work" feature was **removed 17 May
|
||
2026** (Konrad's call — work mix shifting, scope uncertain). The
|
||
post-attendance flow now simply returns to the dashboard. A future
|
||
rebuild starts fresh — see
|
||
`docs/plans/2026-05-17-site-report-removed-capture.md` (preserves the
|
||
schema-as-Python pattern, recovery pointers, and the now-superseded
|
||
`2026-05-15-post-attendance-flow-v2-*` prior thinking, which stays on
|
||
disk).
|
||
|
||
**Removal status (local only, HARD STOP — not pushed):** Tasks 1-3
|
||
done — model/table/UI/routes deleted, migration
|
||
`0018_delete_sitereport` drops `core_sitereport`, suite **193 OK**.
|
||
Un-pushed pending Konrad's local verification (destructive migration
|
||
on the daily-use attendance path).
|
||
|
||
---
|
||
|
||
## ✅ Shipped & live — Manager / Salaried bundle (17 May 2026)
|
||
|
||
### Manager / Salaried Pay (+ pay-type filter + Salary auto-scope + Pay Salary quick action)
|
||
|
||
**Status:** Brainstormed + designed + planned + **fully
|
||
implemented**, every task two-stage code-reviewed, **PUSHED and
|
||
DEPLOYED**. The 36-commit bundle `d7015b9..4c25011` is live on
|
||
production (`https://foxlog.flatlogic.app/`) and **Konrad verified it
|
||
"all working well" on 17 May 2026**. `origin/ai-dev` HEAD `80d96d7`
|
||
== prod (only delta over `4c25011` is doc breadcrumbs). Migrations
|
||
`0016`/`0017` applied; `static/css/custom.css` collected. **209/209
|
||
tests green.** Design/plan docs: `2026-05-15-manager-salaried-pay-*`,
|
||
`2026-05-16-managers-paytype-filter-*`,
|
||
`2026-05-16-salary-autoscope-picker-*`,
|
||
`2026-05-16-pay-salary-quick-action-*` (all under `docs/plans/`).
|
||
Nothing pending on this bundle — fully done.
|
||
|
||
**What it does:** Lets a manager / salaried worker be paid a fixed
|
||
monthly amount without ever logging attendance. A new
|
||
`Worker.pay_type` (`'daily'` default | `'fixed'`) marks managers;
|
||
they're excluded from attendance + absence pickers ONLY (NOT payroll
|
||
modals / TeamForm — they must stay payable), and paid via a new
|
||
`Salary` `PayrollAdjustment` type (project-required; "Pay
|
||
Immediately" → isolated PayrollRecord like New Loan; unpaid nets via
|
||
`_process_single_payment`). The report shows a separate per-project
|
||
"Management / Salaried Cost" line — never merged into WorkLog-derived
|
||
daily labour cost, so all existing money math is provably unchanged.
|
||
"Manager / Salaried" is a Path-A display-only label (model stays
|
||
`Worker`). Migrations `0016_worker_pay_type`, `0017_alter_payrolladjustment_type`.
|
||
|
||
**To resume:** the push is done; the remaining step is the **Flatlogic
|
||
VM deploy** (ordered steps in the Status block above — migrate +
|
||
collectstatic + restart-last). After Gemini/Konrad deploys, verify on
|
||
`https://foxlog.flatlogic.app/` using the manual checklists in the four
|
||
design docs, then update this file's "Production status" + the CLAUDE.md
|
||
"What's mid-flight" breadcrumb to "fully caught up at `4c25011`". No
|
||
code changes pending — implementation is complete and reviewed.
|
||
|
||
Now also includes a display-only pay-type filter (design
|
||
`docs/plans/2026-05-16-managers-paytype-filter-design.md`, plan
|
||
`docs/plans/2026-05-16-managers-paytype-filter-plan.md`): a
|
||
`/workers/?pay_type=fixed` filter + dropdown and a "Managers only"
|
||
client-side toggle on the Add-Adjustment modal picker. Shipped in
|
||
the 36-commit push `d7015b9..4c25011`.
|
||
|
||
Also now: setting the Add-Adjustment **type = Salary** auto-scopes
|
||
the picker — pay-type filter → "Managers only", daily rows hidden,
|
||
and any selected daily worker auto-unticked (UI guard so a `Salary`
|
||
can never silently target a daily worker). Design
|
||
`docs/plans/2026-05-16-salary-autoscope-picker-design.md`, plan
|
||
`docs/plans/2026-05-16-salary-autoscope-picker-plan.md`. Shipped in
|
||
the 36-commit push `d7015b9..4c25011`.
|
||
|
||
Also now: a home-dashboard admin Quick Actions tile **"Pay Salary"**
|
||
deep-links `/payroll/?action=pay-salary` and auto-opens the existing
|
||
Pay Salary modal (param stripped after; no re-pop on refresh). Design
|
||
`docs/plans/2026-05-16-pay-salary-quick-action-design.md`, plan
|
||
`docs/plans/2026-05-16-pay-salary-quick-action-plan.md`. Shipped in
|
||
the 36-commit push `d7015b9..4c25011`.
|
||
|
||
#### Follow-ups from code review (parked, out of scope for this feature)
|
||
|
||
1. **Atomicity hardening (cross-cutting, pre-existing).**
|
||
`add_adjustment`'s three immediate-payment branches — `New Loan`,
|
||
`Advance Payment`, and now `Salary` — create a `PayrollAdjustment`
|
||
+ `PayrollRecord` and call `_send_payslip_email` WITHOUT a
|
||
wrapping `transaction.atomic()`, and `_send_payslip_email`
|
||
re-raises on email failure. A mid-sequence failure can therefore
|
||
orphan a payment row / 500 after commit. This is **PRE-EXISTING**
|
||
(Salary just consistently matches the New Loan / Advance pattern,
|
||
it did not introduce the gap) — flagged by Task 5 code review as a
|
||
separate hardening ticket, **NOT a Manager/Salaried defect**.
|
||
Recommended future cross-cutting fix: wrap each immediate-branch
|
||
body in `transaction.atomic()` and swallow/log email failures the
|
||
way `process_payment` already does. Out of scope for this feature.
|
||
2. **Pre-existing flaky test.** `AbsenceListViewTests` (in
|
||
`core/tests.py`) has an `assertContains`/`assertNotContains`
|
||
against the `/absences/` list HTML that intermittently failed
|
||
~1-in-3 in one run during this feature's execution, then passed on
|
||
rerun; 3+ subsequent full-suite runs were clean. A reviewer's
|
||
isolation analysis concluded it's pre-existing environmental
|
||
nondeterminism (proper Django `TestCase` transactional isolation;
|
||
the Manager/Salaried tests cannot perturb it). Low priority:
|
||
investigate a possible date/locale or response-rendering
|
||
nondeterminism in that test. **NOT introduced by this feature.**
|
||
|
||
> Note: the **Site-progress logging — rebuild from scratch** parked
|
||
> entry above is unaffected by this Manager/Salaried entry (it was
|
||
> superseded separately by the 17 May 2026 SiteReport removal).
|
||
|
||
---
|
||
|
||
## Production status — ✅ fully caught up (15 May 2026)
|
||
|
||
Production (`https://foxlog.flatlogic.app/`) is deployed at
|
||
`origin/ai-dev` HEAD **`1d224bc`**. Everything is live: the original
|
||
SiteReport + Absences feature, all UX polish, the team filters, the
|
||
dashboard/report number audit (18 findings), and the
|
||
template-comment + subline-clarity fixes. (The 17 May Manager/
|
||
Salaried bundle has since shipped too — see the "Shipped & live"
|
||
section above.)
|
||
|
||
**In-progress local-only work (HARD STOP — not pushed):** the
|
||
SiteReport / "Log Today's Work" removal — Tasks 1-3 done, suite
|
||
**193 OK**, migration `0018_delete_sitereport` drops the table.
|
||
Un-pushed pending Konrad's local verify; see the "Site-progress
|
||
logging — rebuild from scratch (parked)" entry above + the capture
|
||
doc `docs/plans/2026-05-17-site-report-removed-capture.md`.
|
||
|
||
**Deploy-ordering lesson learned (now in CLAUDE.md):** production
|
||
runs `DEBUG=False` → Django's cached template loader holds compiled
|
||
templates in memory until the process restarts. The 14 commits of
|
||
template fixes were briefly "invisible" because the service was
|
||
restarted *before* the code reached the target commit. The fix was
|
||
simply to restart AGAIN once `git log` confirmed the right commit.
|
||
Correct order is always: `git reset --hard github/ai-dev` → migrate
|
||
(if needed) → collectstatic (if `static/` changed) → restart
|
||
**last**. See the "DEPLOY ORDERING" bullet in CLAUDE.md's
|
||
Flatlogic Deployment section.
|
||
|
||
---
|
||
|
||
## 🧊 Backburner — journal/voice features (separate offline track)
|
||
|
||
> **Decision (15 May 2026):** Konrad parked the entire journal /
|
||
> voice-transcript line of work. It's more complex than the rest of
|
||
> the app and will be built + tested **offline, on a separate
|
||
> track**, NOT mixed into `ai-dev`. **Do NOT start either of these
|
||
> in `ai-dev` or in any normal feature session.** They are not
|
||
> "blocked waiting on an answer" — they are deliberately deferred
|
||
> until Konrad chooses to spin up a dedicated offline effort.
|
||
|
||
**Important — nothing is in the working app.** Verified 15 May 2026:
|
||
zero `JournalEntry` model / views / URLs / templates anywhere in
|
||
`core/`, zero Letterly / webhook / `@csrf_exempt` code on `ai-dev`,
|
||
latest migration is `0015_absence_project`. There is no dead code,
|
||
no feature flag, no migration to reverse. "Removing" these was a
|
||
no-op — they were never built into the running app. The working
|
||
app (`ai-dev` + production) is completely clean of journal/voice
|
||
code.
|
||
|
||
### Phase A.2 — Manual JournalEntry web UI (deferred)
|
||
|
||
Designed in the Site Work Logging brainstorm (`~/.claude/plans/
|
||
prancy-painting-brook.md`, local). A `JournalEntry` model + manual
|
||
entry form/list/edit, admin-only. Open question if/when resumed:
|
||
**Q7 "Who is Vi?"** — drives whether `JournalEntry` needs a
|
||
`recipient`/`audience` field. Default if never answered: no
|
||
recipient field, all-admin-readable. ~1 hour scope. When resumed,
|
||
build on the offline track first, not directly in `ai-dev`.
|
||
|
||
### Phase B — Letterly inbound webhook (deferred)
|
||
|
||
Designed in the same brainstorm. A `@csrf_exempt` view at
|
||
`/webhooks/letterly/` that creates `JournalEntry` rows from voice
|
||
transcripts, with HMAC body verification + shared-secret URL token.
|
||
Open question if/when resumed: **Q5 — a sample Letterly webhook
|
||
payload** (does it include the recording user / project metadata /
|
||
audio URL / custom fields?). ~3-4 hours scope + one-off Letterly
|
||
account setup. This was always meant to live on the `integrations`
|
||
branch, never `ai-dev` — and now it's offline-track until Konrad
|
||
revisits.
|
||
|
||
---
|
||
|
||
## Small polish follow-ups from the Absences feature
|
||
|
||
> All 7 cleared on 15 May 2026 (commit `d1d3e15`). Kept the section
|
||
> header in place because more polish items will accumulate from
|
||
> future code reviews — when they do, append here. The shipped items
|
||
> are summarized under "Recently shipped" below.
|
||
|
||
---
|
||
|
||
## Defaulted (not blocking — flag if you disagree)
|
||
|
||
The Q9 (photos on site reports) and Q4 (per-project metric
|
||
templates) defaults from the Site Work Logging brainstorm are now
|
||
**obsolete** — the SiteReport feature was removed 17 May 2026. Both
|
||
were open design considerations for that feature; they are captured
|
||
(along with the rest of the design rationale) in
|
||
`docs/plans/2026-05-17-site-report-removed-capture.md` for whoever
|
||
rebuilds site-progress logging from scratch.
|
||
|
||
---
|
||
|
||
## Recently shipped (for context, so a fresh session knows what just landed)
|
||
|
||
- **Dashboard + report numbers audit** (commits `18c75b2` → `c02edce`,
|
||
15 May 2026): Konrad spotted that the home dashboard's "Paid This
|
||
Month" actually showed the last 60 days. A thorough audit found
|
||
17 more issues across the home dashboard, payroll dashboard, and
|
||
report. All 18 fixed in 7 commits: calendar-month math, outstanding
|
||
totals aligned between dashboards, `Project.name` dedupe (was
|
||
silently merging same-named projects), `timezone.localdate()`
|
||
replacing `timezone.now().date()` (SAST drift fix), off-by-one
|
||
on "last 7 / 60 days" windows, dead `year_projects`/`year_teams`
|
||
context removed, `_company_cost_velocity` rewritten as single SQL
|
||
aggregate, signed/coloured adjustment amounts on report tables
|
||
(Bonus vs Deduction now visually distinct), "Outstanding Now"
|
||
hero card subline now shows scope when filters active, dual
|
||
daily_rate code paths documented in CLAUDE.md. 160 → 173 tests
|
||
(+13 regression).
|
||
- **Absence-feature polish pass** (commit `d1d3e15`, 15 May 2026):
|
||
Seven small cleanups in one focused commit. (1) Deleted unused
|
||
`AbsenceQuickForm` (Round C made it obsolete). (2) Extracted
|
||
`_build_team_workers_map(user)` helper using `Prefetch(..., to_attr=...)`,
|
||
fixing N+1 on both `attendance_log` and `absence_log` (one query
|
||
instead of one-per-team). (3) `absence_list` now calls the existing
|
||
`_user_can_log_absences` helper. (4) Removed undefined
|
||
`var(--badge-neutral-bg)` CSS wrapper — fallback hex was always
|
||
winning anyway. (5) `conflicting_worklogs()` rewritten as a single
|
||
query with Python-side pair-set filtering — 25-query → 2-query for
|
||
a 5×5 form. (6) Extracted `_apply_absence_filters(qs, request)`
|
||
shared by `absence_list` + `absence_export_csv` so adding a filter
|
||
param now touches one place, not two. (7) `site_report_detail.html`
|
||
paid-check icon uses `text-success` instead of background-color
|
||
variable as foreground (WCAG contrast fix). 157/157 tests still
|
||
green.
|
||
- **Day name in Work Log Payroll modal header** (commit `70fa085`,
|
||
15 May 2026): The modal opened from /history/ now shows
|
||
"Friday, 15 May 2026" instead of "2026-05-15". Pure server-side
|
||
`strftime` on the already-loaded `log.date` — zero overhead.
|
||
- **Team filters on `/workers/` and `/history/`** (commits `4b57cff`,
|
||
`398a5b2`, 15 May 2026): Both pages now have a Team dropdown in
|
||
their filter row. `/workers/?team=<id>` filters by Team.workers
|
||
M2M membership (who's CURRENTLY on the team); `/history/?team=<id>`
|
||
filters by WorkLog.team FK (logs TAGGED with the team when
|
||
created — different semantics, intentional). Both accept
|
||
`team=none` for the "no team assigned" / "ad-hoc work log" case.
|
||
CSV export at `/history/export/` honours the same param.
|
||
+8 regression tests.
|
||
- **Absences UX polish** (commits `4368e53`, `02c6d4d`, 15 May 2026):
|
||
Two production-found bugs fixed. (1) Absence-form team filter was
|
||
hiding ALL workers when a team was selected — switched the JS to
|
||
read `<input name="workers">[value]` directly instead of going
|
||
through `data-worker-id` (proven attendance-form pattern). (2)
|
||
Reasons multi-checkbox dropdown on `/absences/` was rendering
|
||
behind the table — lifted the filter card with
|
||
`position: relative; z-index: 10` so the whole card sits above
|
||
its sibling table card in the stacking order. See the Coding
|
||
Style section in CLAUDE.md for both gotchas.
|
||
- **Worker Absences feature** (commits `bf6f0a5` → `27fe05e`,
|
||
14 May 2026): Complete absence-tracking system. 8 reason choices,
|
||
optional project FK (auto-attributes paid-absence Bonus
|
||
adjustments to the project), date-range logging with weekend
|
||
toggles, supervisor scoping (admin sees all; supervisors see
|
||
their teams). Standalone `/absences/log/` form + "Submit + Log
|
||
Absences" shortcut on attendance form. List/edit/delete + CSV
|
||
export. Worker-detail "Absences" tab with YTD totals. Dashboard
|
||
alert card "X absent in last 7 days". Bidirectional cascade with
|
||
PayrollAdjustment via `_sync_absence_payroll_adjustment` helper
|
||
(single-chokepoint design, transaction.atomic-wrapped). 12
|
||
commits pushed to `origin/ai-dev`. Test count: 85 → 149 (+64
|
||
tests). Migrations `0013_add_site_report`, `0014_add_absence`,
|
||
`0015_absence_project` are LIVE on production as of late 14 May
|
||
2026.
|
||
- **Phase A.1 — SiteReport** (commit `864ae72`, 14 May 2026):
|
||
Model, migration `0013_add_site_report.py`, form, two-step flow
|
||
from attendance log, 16 new tests. `CLAUDE.md` updated with model
|
||
summary + `site_report_schema.py` pattern section.
|
||
- **Pastel soft-fill payroll action buttons** (commit `6c6ade9`,
|
||
24 Apr 2026): unified Worker Lookup / Batch Pay / Add Adjustment
|
||
/ Price Overtime treatment.
|
||
- **Path A display-only rename** (UX Polish Pass, Apr 2026):
|
||
"New Loan" / "Advance Payment" / "Advance Repayment" display as
|
||
"Loan" / "Advance" / "Advance Repaid" — but **DB values are
|
||
unchanged**. See "UI-vs-DB naming drift" section in `CLAUDE.md`
|
||
before writing any filter/comparison on `PayrollAdjustment.type`.
|
||
|
||
---
|
||
|
||
## Original Site Work Logging brainstorm
|
||
|
||
The full Q1-Q13 question list and design rationale lives in
|
||
`~/.claude/plans/prancy-painting-brook.md` (local — deliberately
|
||
not committed to the repo, since the parts that matter have been
|
||
absorbed into `CLAUDE.md` and this file). If Konrad answers Q5 or
|
||
Q7 later, refer back to that file for the original framing.
|