Batch Pay: new button on payroll dashboard lets admins pay multiple
workers at once using team pay schedules. Shows preview modal with
eligible workers, then processes all payments in one click.
Fix: "Split at Pay Date" now uses cutoff_date (end of last completed
period) instead of current period end. This includes ALL overdue work
across completed periods, not just one period.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Enable selective payment of work logs and adjustments instead of
all-or-nothing. The preview modal now shows checkboxes on every item
(all checked by default) with dynamic net pay recalculation.
Teams can be configured with a pay frequency (weekly/fortnightly/monthly)
and anchor start date. When set, a "Split at Pay Date" button appears
that auto-unchecks items outside the current pay period.
Key changes:
- Team model: add pay_frequency and pay_start_date fields
- preview_payslip: return IDs, dates, and pay period info in JSON
- process_payment: accept optional selected_log_ids/selected_adj_ids
- Preview modal JS: checkboxes, recalcNetPay(), Split button, Pay Selected
- Backward compatible: existing Pay button still processes everything
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
When hovering over a bar in the Cost by Project chart, the tooltip
now shows the total for that month across all projects at the bottom.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Same wages/additions/deductions breakdown as the home dashboard,
now also shown on the Payroll Dashboard stat card.
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>
Work History:
- Worker names now display as rounded pill badges instead of comma-
separated text, making them easier to scan (both server-rendered
list view and JS calendar detail view)
Payroll Dashboard:
- New "By Worker" toggle on the Monthly Payroll chart card
- Dropdown to select an active worker with payment history
- Stacked bar chart shows monthly breakdown: base pay, overtime,
bonuses (positive), deductions, loan repayments, advances (negative)
- All data pre-computed server-side with 2 aggregate queries and
embedded as JSON — switching workers is instant, no AJAX needed
- Only workers with actual payment history appear in the dropdown
- Legend items auto-hide when a component has no data for that worker
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Root cause: V5 was missing required attributes that V2 had on the Add
Adjustment form. When a user submitted without selecting a project (for
types that require one), the server rejected it with messages.error()
but the error was invisible before the MESSAGE_TAGS fix. Combined with
no client-side validation for workers, the form would silently create
0 adjustments or redirect with no visible feedback.
Fixes:
- Add required attribute to Project select (toggles off for Loan types)
- Add client-side validation: blocks submit if no workers selected
- Add backend validation: returns error if no workers in POST data
- Add "Select All" / "Clear" links for worker checkboxes (matches V2)
- Add "X worker(s) selected" counter for visual feedback
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
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. 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>
- 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>