38686-vm/docs/plans/2026-04-20-worker-lookup-design.md
Konrad du Plessis 60ee21dd61 Add Worker Lookup modal to payroll dashboard
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>
2026-04-20 14:15:04 +02:00

6.1 KiB

Worker Lookup Modal — Implementation Plan

For Claude: REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.

Goal: Add a "Worker Lookup" modal to the payroll dashboard that shows a comprehensive financial report card for any active worker — payable amount, loans, recent payments, sizing, and notes.

Architecture: New AJAX endpoint (worker_lookup_ajax) returns JSON with all worker data. Modal HTML + JS in the dashboard template dynamically renders the data using safe DOM methods (textContent, createElement). Worker names across all dashboard tabs become clickable links that open the modal. A "Worker Lookup" button in the header lets you search any active worker.

Tech Stack: Django views (JSON), Bootstrap 5 modal, vanilla JavaScript (matching existing patterns)


Task 1: Add the AJAX backend endpoint

Files:

  • Modify: core/views.py (add new view after preview_payslip at ~line 2155)
  • Modify: core/urls.py (add new URL pattern at ~line 54)

Step 1: Add URL pattern in core/urls.py

Add after the preview_payslip URL (line 51):

# Worker lookup — AJAX report card for a single worker (returns JSON)
path('payroll/worker-lookup/<int:worker_id>/', views.worker_lookup_ajax, name='worker_lookup_ajax'),

Step 2: Add worker_lookup_ajax view in core/views.py

Add after preview_payslip function (after line 2155). The view:

  1. Checks admin access (is_admin)
  2. Fetches the Worker object with all model fields (sizing, license, notes)
  3. Calculates Amount Payable:
    • Unpaid WorkLogs x daily_rate (same logic as preview_payslip lines 2069-2083)
    • Net pending adjustments (additive minus deductive)
  4. Outstanding Loans: Loan.filter(worker=worker, active=True).aggregate(Sum('remaining_balance'))
  5. Paid This Month: PayrollRecord.filter(worker=worker, date__year=now.year, date__month=now.month).aggregate(Sum('amount_paid'))
  6. Loans This Year: Loan.filter(worker=worker, date__year=now.year).aggregate(Sum('principal_amount'))
  7. Paid This Year: PayrollRecord.filter(worker=worker, date__year=now.year).aggregate(Sum('amount_paid'))
  8. Last Payslip: PayrollRecord.filter(worker=worker).order_by('-date').first() -> date + amount
  9. Last Loan Given: Loan.filter(worker=worker).order_by('-date').first() -> date + amount + reason
  10. Last Loan Repayment: PayrollAdjustment.filter(worker=worker, type='Loan Repayment', payroll_record__isnull=False).order_by('-date').first() -> date + amount
  11. Last Advance: PayrollAdjustment.filter(worker=worker, type='Advance Payment', payroll_record__isnull=False).order_by('-date').first() -> date + amount
  12. Active Loans list: Loan.filter(worker=worker, active=True).order_by('-date') -> type, principal, balance, date, reason
  13. Current Project: most recent WorkLog -> project name + count of logs on that project
  14. Team: get_worker_active_team(worker) -> team name

Step 3: Verify imports

Ensure Sum is imported from django.db.models at the top of views.py. Check for existing from django.db.models import ... line and add Sum if missing.


Task 2: Add the modal HTML to the dashboard template

Files:

  • Modify: core/templates/core/payroll_dashboard.html

Step 1: Add "Worker Lookup" button in the page header (line 15)

Add a new button in the header button group, before Batch Pay.

Step 2: Add the Worker Lookup modal HTML

Add after the previewPayslipModal (after line ~783). The modal contains:

  • Worker dropdown in header
  • A body div (#workerLookupBody) that gets populated by JS
  • Placeholder text as default content

Step 3: Pass active_workers_list from the view

In core/views.py, in the payroll_dashboard view, add to the context dict:

'active_workers_list': Worker.objects.filter(active=True).order_by('name'),

Task 3: Make worker names clickable across all tabs

Files:

  • Modify: core/templates/core/payroll_dashboard.html

Replace <strong>worker.name</strong> with clickable links using class worker-lookup-link and data-worker-id at:

  • Pending Payments tab (line 268)
  • Payment History tab (line 365)
  • Loans & Advances tab (line 432)

Task 4: Add JavaScript to fetch data and render the modal

Files:

  • Modify: core/templates/core/payroll_dashboard.html (JS section at bottom)

Step 1: Add the loadWorkerLookup(workerId) function

This function:

  1. Shows a loading spinner in #workerLookupBody
  2. Fetches /payroll/worker-lookup/<workerId>/ via fetch API
  3. Builds the report card using safe DOM methods (createElement, textContent — no innerHTML with user data)
  4. Renders sections: Identity, Quick Stats (4 cards), Recent Activity, Active Loans table, Paid This Year, Sizing & Info

Format currency as R X,XXX.XX using toLocaleString('en-ZA', {minimumFractionDigits: 2}).

Step 2: Add event listeners

  • Worker Lookup button click -> open modal with empty dropdown
  • Dropdown change -> call loadWorkerLookup(selectedId)
  • .worker-lookup-link click -> set dropdown value, load data, open modal

Task 5: Update CLAUDE.md

Files:

  • Modify: CLAUDE.md

  • Add Worker Lookup documentation to Development Workflow section

  • Add URL route to the URL Routes table

  • Update view count from "27 functions" to "28 functions"


Task 6: Test locally

  1. Start dev server: run_dev.bat
  2. Go to /payroll/ -> verify "Worker Lookup" button appears in header
  3. Click "Worker Lookup" -> modal opens with dropdown -> select a worker -> report card loads
  4. Click a worker name in Pending Payments -> modal opens with that worker's data
  5. Click a worker name in Payment History -> same
  6. Click a worker name in Loans & Advances -> same
  7. Switch workers via dropdown while modal is open -> data refreshes
  8. Verify all sections render correctly:
    • Quick stats show correct amounts
    • Recent activity shows dates and amounts (or "None")
    • Active loans table shows if worker has loans
    • Sizing and notes display at bottom
  9. Verify existing functionality is unbroken:
    • Preview payslip modal still works
    • Quick adjust button still works
    • Pay/Batch pay still works