From b450bd3c39b2a3a910218808427bcb33f5631daf Mon Sep 17 00:00:00 2001 From: Konrad du Plessis Date: Thu, 23 Apr 2026 15:34:09 +0200 Subject: [PATCH] =?UTF-8?q?feat(adjustments):=20Adjustments=20tab=20?= =?UTF-8?q?=E2=80=94=20nav=20+=20filter=20bar=20+=20flat=20table=20+=20row?= =?UTF-8?q?=20actions?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reuses existing modals (#editAdjustmentModal, delete confirm flow) — zero new JS for row actions. Choices.js lazy-inits only when the tab is active. Stats row scoped to filter set. Subquery pattern on team filter (CLAUDE.md). Group-by + bulk-delete + cross-filter come in Tasks 5/6/7. --- core/templates/core/_adjustment_row.html | 131 ++++++++++++ core/templates/core/payroll_dashboard.html | 219 +++++++++++++++++++++ 2 files changed, 350 insertions(+) create mode 100644 core/templates/core/_adjustment_row.html diff --git a/core/templates/core/_adjustment_row.html b/core/templates/core/_adjustment_row.html new file mode 100644 index 0000000..d07d830 --- /dev/null +++ b/core/templates/core/_adjustment_row.html @@ -0,0 +1,131 @@ +{# === _adjustment_row.html === + Single used by BOTH the flat Adjustments view and (later) the grouped view. + Context: + - `adj` — a PayrollAdjustment instance + - `additive_types` — list of type labels that are additive (used to decide + whether the amount should be prefixed with + or - in the display) + Row actions differ based on whether the adjustment has already been paid: + - Paid -> single [View Payslip] icon button + - Unpaid -> three buttons: [Preview][Edit][x] + (these reuse the existing modals on the dashboard — no new JS) +#} +{% load format_tags %} + + + {# --- Bulk-select checkbox --- #} + {# Paid rows show a disabled checkbox so the column stays aligned; #} + {# only unpaid rows can be bulk-selected for deletion (feature comes in Task 6). #} + + {% if adj.payroll_record %} + + {% else %} + + {% endif %} + + + {# --- Date --- #} + {{ adj.date|date:"d M Y" }} + + {# --- Worker name (clickable link to the worker profile page) --- #} + + + {{ adj.worker.name }} + + + + {# --- Type badge (colour comes from the .badge-type- CSS class) --- #} + {{ adj.type }} + + {# --- Amount (sign reflects additive vs deductive) --- #} + + {% if adj.type in additive_types %} + +R {{ adj.amount|money }} + {% else %} + −R {{ adj.amount|money }} + {% endif %} + + + {# --- Project (clickable if present, dash if missing) --- #} + + {% if adj.project %} + + {{ adj.project.name }} + + {% else %}{% endif %} + + + {# --- Team (worker's first team, if any — many workers are unteamed) --- #} + + {% with team=adj.worker.teams.first %} + {% if team %}{{ team.name }}{% else %}{% endif %} + {% endwith %} + + + {# --- Description (truncated; full text shown in a hover tooltip) --- #} + + {% if adj.description %} + + {{ adj.description|truncatechars:40 }} + + {% else %}{% endif %} + + + {# --- Status: Paid #N (links to the payslip) or Unpaid badge --- #} + + {% if adj.payroll_record %} + + Paid #{{ adj.payroll_record.id }} + + {% else %} + Unpaid + {% endif %} + + + {# --- Row actions (eye + pen + x for unpaid; eye only for paid) --- #} + + {% if adj.payroll_record %} + {# PAID: view payslip only #} + + + + {% else %} + {# UNPAID: preview + edit + delete #} + {# Preview button — class .preview-payslip-btn is already wired up in the + main dashboard JS (opens the preview modal for this worker). #} + + {# Edit button — class .adjustment-badge is already wired up in the + main dashboard JS (populates + opens #editAdjustmentModal). We reuse + it here so no new JS is needed for editing. #} + + {# Delete button — opens the existing #deleteConfirmModal directly + (short-circuits the edit modal's usual two-step flow). #} + + {% endif %} + + diff --git a/core/templates/core/payroll_dashboard.html b/core/templates/core/payroll_dashboard.html index f3eee51..2166a19 100644 --- a/core/templates/core/payroll_dashboard.html +++ b/core/templates/core/payroll_dashboard.html @@ -1,5 +1,6 @@ {% extends 'base.html' %} {% load static %} +{% load format_tags %} {% block title %}Payroll Dashboard | FoxFitt{% endblock %} @@ -265,6 +266,14 @@ Loans & Advances + {# === ADJUSTMENTS TAB LINK === #} + {# Task 4: flat table view of every payroll adjustment with filters, #} + {# bulk actions, and row edit/delete. See _adjustment_row.html. #} + {# =============================================== #} @@ -539,6 +548,164 @@ {% endif %} + {# =============================================== #} + {# === ADJUSTMENTS TAB === #} + {# =============================================== #} + {# Flat, filterable table of every PayrollAdjustment in the system. #} + {# Filters run server-side via GET params (type, worker, team, status, date range). #} + {# Row actions reuse the existing Edit / Delete / Preview modals so no new JS is needed. #} + {% if active_tab == 'adjustments' %} + + {# --- Sticky filter bar (Choices.js enhances the multi-selects below) --- #} +
+
+ + + {# --- Type multi-select (Bonus / Overtime / etc.) --- #} +
+ + +
+ + {# --- Workers multi-select (cross-filtered by Teams in Task 7) --- #} +
+ + +
+ + {# --- Teams multi-select --- #} +
+ + +
+ + {# --- Status single-select (Unpaid / Paid / All) --- #} +
+ + +
+ + {# --- Date range --- #} +
+ + +
+
+ + +
+ + {# --- Sort state (column-header clicks will set these via JS in Task 9) --- #} + + + + {# --- Apply / Clear buttons --- #} +
+ + Clear +
+
+
+ + {# --- Stats row (scoped to the currently-filtered set) --- #} +
+ {{ adj_total_count }} adjustment{{ adj_total_count|pluralize }} + · + {{ adj_unpaid_count }} unpaid (R {{ adj_unpaid_sum|money }}) + · + +R {{ adj_additive_sum|money }} net additive + · + −R {{ adj_deductive_sum|money }} net deductive +
+ + {# --- Flat table of adjustments --- #} + {% if adj_page.object_list %} +
+
+
+ + + + + + + + + + + + + + + + + {% for adj in adj_page.object_list %} + {% include 'core/_adjustment_row.html' with adj=adj additive_types=additive_types %} + {% endfor %} + +
+ + Date Worker TypeAmount ProjectTeamDescriptionStatus Actions
+
+
+
+ + {# --- Pagination --- #} + {% if adj_page.has_other_pages %} + + {% endif %} + + {% else %} + {# Simple placeholder empty state — visual polish comes in Task 10 #} +
+
+

No adjustments match these filters.

+
+ {% endif %} + + {% endif %} + {# ================================================================== #} @@ -903,6 +1070,19 @@ {{ project_chart_json|json_script:"projectChartJson" }} {{ worker_chart_json|json_script:"workerChartJson" }} +{# === CHOICES.JS CDN — loaded only when the Adjustments tab is active === #} +{# Used by the Type / Workers / Teams multi-select filters. If the CDN fails #} +{# the