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

146 lines
6.1 KiB
Markdown

# 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):
```python
# 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:
```python
'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