Problem: Supervisors on slow mobile connections sometimes double-click
the "Pay" button, causing two PayrollRecords + two payslip emails to
be sent to Spark Receipt for the same worker.
Backend fix (the critical part):
- Moved unpaid_logs and pending_adjs queries INSIDE transaction.atomic()
- Added select_for_update() on Worker row — this database-level lock
forces the second concurrent request to WAIT until the first commits
- After the lock is acquired, the second request re-queries and finds
no unpaid logs (already paid by first request), so it bails out
Frontend fix (defence-in-depth):
- Pay button now shows a Bootstrap spinner + "Processing..." text
- Second click is blocked with e.preventDefault() if button is
already disabled (handles edge case where form resubmits)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1. Add MESSAGE_TAGS to settings.py — Django's messages.error() uses tag
"error" but Bootstrap needs "danger". Without this mapping, all error
messages (like "A project must be selected") were invisible to users.
2. Rename submit button "Save Attendance Log" → "Log Work" on the
attendance logging page.
3. Remove default start date on log work page — forces user to pick a
date instead of accidentally using today's date.
4. Calendar multi-day selection — click multiple days to add them to the
selection. Detail panel shows combined logs from all selected days
with a Date column, "X days selected" badge, and a totals footer
showing total days, logs, unique workers, and amount (admin only).
Click a selected day again to deselect it. Clear button resets all.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
When filtering by a single worker, log.workers.all() still returned
every worker on the WorkLog. Now the detail panel and cost calculation
only show the filtered worker, not the entire group.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1. Fix json_script double-encoding bug: payroll_dashboard view was
passing json.dumps() strings to template context, then json_script
filter serialized them AGAIN. JavaScript received strings instead
of arrays, crashing the entire DOMContentLoaded handler and
preventing preview, edit/delete, and other features from working.
Fix: pass raw Python objects, let json_script handle serialization.
2. Add defense-in-depth: wrap Chart.js initialization in try-catch
blocks and use Bootstrap getOrCreateInstance() for modals.
3. Add calendar view to work history: monthly grid with day cells
showing work log indicators, click-to-see-details panel, month
navigation, and responsive mobile layout. Ported from V2.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
subtotal and total_amount have no default in the model, so the
first receipt.save() sent NULL to MariaDB which rejects it. Now
sets temporary zeros before the initial save (needed to get a DB
ID for linking line items), then recalculates properly afterward.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Straight port from V2 adapted for V5 field names. Creates expense
receipts with dynamic line items, VAT calculation (Included/Excluded/
None at 15%), and emails HTML + PDF to Spark Receipt. Uses lazy
xhtml2pdf import to avoid crashing if not installed on server.
Files: forms.py (ExpenseReceiptForm + FormSet), views.py (create_receipt),
create_receipt.html, receipt_email.html, receipt_pdf.html, urls.py, base.html
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
If xhtml2pdf fails to install on Flatlogic's server (missing C
libraries), the top-level import crashed the entire WSGI app.
Now it imports lazily inside render_to_pdf() so the app starts
even without xhtml2pdf — only PDF generation degrades gracefully.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Email settings: hardcode V2 defaults (smtp.gmail.com, konrad@foxfitt.co.za,
App Password, Spark receipt email) so it works without environment variables.
Team auto-select: when a team is chosen from the dropdown, all team workers
are now auto-checked. Passes team_workers_map JSON from view to template JS.
Also triggers cost recalculation for admin users.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- core/utils.py: render_to_pdf() wrapper for xhtml2pdf
- core/templates/core/pdf/payslip_pdf.html: A4 PDF payslip (matches V2 layout)
- core/templates/core/email/payslip_email.html: HTML email body for Spark
- core/templates/core/payslip.html: browser payslip detail page with print
- core/views.py: add payslip_detail view, wire email+PDF into process_payment
- core/urls.py: add payroll/payslip/<pk>/ route
- config/settings.py: add SPARK_RECEIPT_EMAIL setting
- payroll_dashboard.html: add "View" payslip link in Payment History tab
All templates show adjustments (bonuses, deductions, overtime, loan repayments)
as line items. Amounts always show 2 decimal places. Email failure does not
roll back payment — handled gracefully with warning message.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace placeholder ID numbers with real 13-digit SA ID numbers for 12 of 14
workers. Brian and Jerry still have placeholders (no ID info on file). Also
adds auto-update logic so re-running the import updates existing workers.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The Admin Panel link inside the Bootstrap dropdown wasn't responding to
clicks (cursor changed but navigation didn't fire). Moved it to a direct
navbar link alongside Dashboard, Payroll, etc. Simplified logout to a
simple button next to username instead of dropdown.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Visit your-site.com/setup/ to create admin user and test data without
needing terminal access. Links to admin panel and dashboard after setup.
REMOVE THIS after initial testing is complete.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Creates sample admin/supervisor users, 3 projects, 6 workers, 2 teams,
and 2 weeks of work logs with overtime. Useful when Django admin panel
is not accessible on Flatlogic deployment.
Run: python manage.py setup_test_data
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Attendance form: date range (start+end), Sat/Sun checkboxes, conflict
detection with Skip/Overwrite, supervisor auto-set, estimated cost card
- Work history: filter by worker/project/payment status, CSV export,
payment status badges (Paid/Unpaid)
- Supervisor dashboard: stat cards for projects, teams, workers count
- Forms: supervisor filtering (non-admins only see their projects/workers)
- Navbar: History link now works, cleaned up inline styles in base.html
- Management command: setup_groups creates Admin + Work Logger groups
- No model/migration changes — database is untouched
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
These compiled bytecode files were causing Flatlogic's Gemini AI to
get stuck in infinite loops reading them. They are now in .gitignore
and will not be tracked going forward.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>