Adds a new CLAUDE.md section documenting the display/DB gap that
Path A of the UX Polish Pass creates: user sees 'Loan' / 'Advance'
/ 'Advance Repaid' while DB stores 'New Loan' / 'Advance Payment'
/ 'Advance Repayment'. Includes a lookup table, the rule for when
to use which (DB for logic, display for templates), and the failure
symptom so future Claude sessions don't chase ghost filters.
Ships BEFORE the rename so the doc is searchable from minute one.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Three additions from this session's work:
1. Django ORM gotcha — PayrollAdjustment project double-attribution.
Documents the Coalesce pattern that solved the Apr 2026 perf-pass
double-count bug on Overtime adjustments.
2. Payroll dashboard query-count baselines — target ranges for /
and the four /payroll/ tabs after the perf pass, plus the
"spotting a regression" heuristic (>50% jump = N+1 reintroduced).
3. Profiling locally — Django Debug Toolbar — what it is, how it's
triple-gated, how to use it for N+1 hunting. Flags that the
package is already in requirements.txt so future sessions don't
need to install it.
Net: +35 lines, three new sections, no deletions.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Final whole-impl review catch on the Perf Quick-Wins Pass. Step 3
said "the mtime of the collected copy under staticfiles/ stays the
same, so the token doesn't bump." That's backwards — the token is
read from static/css/custom.css (the SOURCE file), so editing the
source DOES bump the token and Cloudflare correctly misses on the
next request. What actually breaks is the VM's response to the miss:
Apache serves stale bytes from staticfiles/ because collectstatic
hasn't refreshed the collected copy. New URL, old bytes behind it.
Rewording makes the causal chain correct so future Gemini/Claude
debugging "CSS change deployed but old file still shows" reaches
the right conclusion (run collectstatic on the VM) via the right
reasoning.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Code-review followups on 16d4399:
- CLAUDE.md's "When CSS changes don't appear" diagnostic steps
were written for the old per-request token. Under mtime-based
caching, a stable ?v= number is the healthy expected state,
not a broken one. Rewrote steps 1 + 3 so someone debugging
a real production CSS issue gets the right advice.
- Dropped unused `original = cp._compute_cache_bust_token` line
in test_token_falls_back_if_file_missing - it misled readers
into thinking the function itself was patched. Added a one-
line comment clarifying the monkey-patch is path-only.
Tests: still 68/68.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
deployment_timestamp was int(time.time()) per-request, giving every
page load a new ?v=... query string on custom.css. Cloudflare treats
each unique URL as a new resource, so the CSS was fetched from the VM
on every page load — 64 KB over the wire per navigation.
Token now tied to static/css/custom.css mtime. The URL only changes
when the CSS actually changes, so Cloudflare can hold the file for
its full 4h TTL. Degraded-mode fallback preserves today's behaviour
if the file isn't on disk.
3 new CacheBustTokenTests; all 68 tests pass.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Audit revealed several stale / missing items:
1. Wrong CSS selector for light theme — said `:root.light`, actual is
`[data-theme="light"]`. Task 2 of Adjustments caught this in the
implementer's self-review; the doc didn't get updated. Now correct.
2. `_report_config_modal (partial)` removed from templates list — the
file was deleted in commit 1d00a3a (retire modal).
3. `_adjustment_row.html` added to templates list — new partial, shared
by flat + grouped views on the Adjustments tab.
4. `format_tags.py` now lists all 5 filters: money, money_abs, type_slug,
url_replace, dictlookup (was just 'money').
5. New narrative paragraphs for:
- Inline Filters on /report/ (pill popovers, cross-filter, JSON gotcha)
- Adjustments tab (filter pills, badge palette, group-by, bulk delete)
- _delete_adjustment_with_cascade helper (shared by single+bulk)
- Pill-popover filter pattern (.adj-hidden-inputs + OK-rewrites-inputs)
6. Two new schema name-drifts: PayrollRecord.amount_paid (not total_amount
/ days_worked); Loan.principal_amount (not principal). Both bit an
implementer this session when writing test fixtures.
7. Two new Coding Style rules in the top section:
- Multi-line {# #} template comments are INVALID — use {% comment %}
(bit us 4× in this session). With caveat that literal {# or #} can't
appear inside a {% comment %} block either.
- Duplicate id= attributes silently steal event handlers — grep before
assigning (caught adjSelectAll collision between table header + modal).
Now 707 lines, 24 sections. Future sessions should have the context to
avoid the mistakes this session made.
Captures the 11-task implementation, 5 deviations (biggest: the
CP1 pivot from Choices.js chip-multiselect to popover-checkbox
filter UX after Konrad flagged the chip pattern as intrusive),
14 new adjustments-tab tests, and total code churn (~+1400 lines).
CLAUDE.md URL Routes table gains two rows so future sessions
surface /payroll/?status=adjustments and the bulk-delete endpoint.
Feature ready for final whole-feature code review + batched push.
Five focused updates from the Apr 22-23 bug-fix + gitignore session:
1. Fix stale supervisor-picker queryset doc: it was showing the pre-fix
Q(is_staff)|Q(is_superuser)|Q(groups__name='Work Logger') filter.
Since commit 0ceceeb the queryset is just User.objects.filter(is_active=True).
2. Update "How to add a new supervisor" step 2: Work Logger group
membership is no longer required for picker visibility — optional now.
3. Add "Schema name-drifts to remember" block near Key Models. Three
recurring gotchas that burned four subagent tasks across two sessions:
- PayrollAdjustment.description (not reason)
- log.adjustments_by_work_log (not payrolladjustment_set)
- log.overtime_amount (not log.overtime)
4. Add canonical test-command one-liner to the Commands section:
USE_SQLITE=true DJANGO_DEBUG=true python manage.py test core.tests -v 2
5. Add "Django ORM gotcha" subsection documenting the M2M filter +
values().annotate(Sum()) inflation bug and the id__in subquery fix
pattern (refs commit f1e246c, ReportContextFilterInflationTests).
No code changes; no test impact.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Problem: every time collectstatic ran on the VM, Flatlogic's web UI
detected the modified files in staticfiles/ and auto-committed them
with a generic "Ver XX.YY" message (e.g. "Ver 30.04 Fix reports and
add Supervisor"), pushing the result to gitea but not GitHub. Every
push of CSS/JS changes triggered a reconciliation dance. See the
"Ver 30.04" divergence resolved by commit e0d2c74 for the most recent
example — that was the 3rd or 4th recurrence of this exact pattern.
Fix:
1. Add staticfiles/ to .gitignore
2. Untrack all 627 currently-tracked files via `git rm -r --cached`
3. Document the change in CLAUDE.md (Project Structure, Static Assets,
and a new "NOT tracked in git" subsection)
Deploy consequence: the NEXT pull on the VM will delete
staticfiles/ from the working tree (because git sees those files
removed from the tree). Gemini MUST run `collectstatic --noinput`
IMMEDIATELY after `git pull` to repopulate from source, then
restart the service. Brief window of 404s on static assets is
acceptable at this scale (seconds).
After this change: collectstatic output lives on the VM's filesystem
but outside git's view, so Flatlogic's UI has nothing to auto-commit.
The recurring divergence pattern is permanently eliminated.
No runtime code changes — all 28 tests still pass.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Documents three things that came out of today's Phase 2 deploy session
and weren't previously written down:
1. Static Assets & Cache-Busting (new section): explains that production
traffic goes through Cloudflare with 4h edge cache; the
`deployment_timestamp` template variable is what breaks stale caches;
and why `request.timestamp` must never be used (the silent-default-to-1.0
bug that ate a couple of hours).
2. Environment Variables: inline notes for each var. Most important new
fact is that DEFAULT_FROM_EMAIL is now optional — falls back to
EMAIL_HOST_USER if unset (prevents the "Invalid address ''" failure
mode on outbound mail). Also documents that .env lives at BASE_DIR.parent
on Flatlogic and can only be edited via Gemini/shell.
3. Flatlogic Deployment: collectstatic isn't auto-run, django-dev.service
runs manage.py runserver (dev server in prod — known but works at this
scale), Cloudflare sits in front, VM has two git remotes (github +
gitea) that must stay in sync, VM-local safety branches for rollback,
and the "pick one write path" workflow rule to avoid divergence.
No code changes — documentation only.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
New docs/FEATURES.md covers all 15 feature areas: dashboard, attendance,
work history, payroll, payments, adjustments, loans, worker lookup, worker
management, team schedules, receipts, emails/PDFs, auth, exports, and
deployment tools. CLAUDE.md updated with accurate line count.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
New AJAX endpoint (worker_lookup_ajax) returns a comprehensive financial
report card for any active worker. Modal shows: amount payable, outstanding
loans, paid this month/year, loans this year, recent activity, active loans
table, current project + days, PPE sizing, drivers license, and notes.
Worker names across all dashboard tabs are now clickable links that open
the modal. Header button with searchable dropdown for quick access.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
New fields: shoe_size, overall_top_size, pants_size, tshirt_size,
has_drivers_license (boolean), drivers_license (file upload).
Admin organised into 3 fieldsets. CSV export updated with new columns.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- views.py now has 27 functions (~2470 lines)
- Document the Quick Adjust button on pending payments rows
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Replace "Exclude workers with loans" checkbox with dropdown
(All Workers / With loans only / Without loans) in batch pay modal,
matching the pending payments table filter style
- Fix radio button visual state when switching between
"Until Last Paydate" and "Pay All" modes (set checked after DOM append)
- Update CLAUDE.md with pending table filter and overdue badge docs
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Flatlogic doesn't always run migrations on Pull Latest. Added note
about using /run-migrate/ to fix "Unknown column" errors after deploy.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Document the new split payslip feature, team pay schedule fields,
pay period calculation helpers, and backward-compatible process_payment.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Advances are now treated as immediate payments (not pending salary items):
- Auto-creates PayrollRecord + sends payslip email at creation time
- Auto-creates Advance Repayment adjustment for next salary cycle
- Validates worker has unpaid work logs (otherwise use New Loan)
- Requires project selection for cost tracking
- Partial repayment converts advance to regular loan
- Admin can edit auto-repayment amount before payday
- Negative net pay warning in preview modal
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Redesign Advance Payments to work like loans with tracked balances:
- Add loan_type field to Loan model ('loan' or 'advance')
- Move Advance Payment from DEDUCTIVE to ADDITIVE types (worker receives money)
- Add new Advance Repayment type for deducting from future salary
- Create/edit/delete handlers mirror New Loan behavior for advances
- Loans & Advances tab with type badges and filter buttons
Enhance Payslip Preview modal into "Worker Payment Hub":
- Show outstanding loans & advances with balances in preview
- Inline repayment form per loan (amount pre-filled, note, Deduct button)
- AJAX add_repayment_ajax endpoint creates adjustment without page reload
- Modal auto-refreshes after repayment showing updated net pay
- New refreshPreview() JS function enables re-fetching after AJAX
Other changes:
- Rename History to Work History in navbar
- Advance-specific payslip layout for pure advance payments
- Fix JS noProjectTypes to hide Project field for advance types
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>