251 lines
12 KiB
HTML
251 lines
12 KiB
HTML
{% extends 'base.html' %}
|
|
{% load humanize %}
|
|
|
|
{% block title %}Payroll Dashboard - Fox Fitt{% endblock %}
|
|
|
|
{% block content %}
|
|
<div class="container py-5">
|
|
<div class="d-flex justify-content-between align-items-center mb-4">
|
|
<h1 class="h2 fw-bold text-dark">Payroll Dashboard</h1>
|
|
<div>
|
|
<a href="{% url 'loan_list' %}" class="btn btn-outline-secondary me-2">Manage Loans</a>
|
|
<button class="btn btn-outline-primary" data-bs-toggle="modal" data-bs-target="#addAdjustmentModal">
|
|
+ Add Adjustment
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Analytics Section -->
|
|
<div class="row mb-5">
|
|
<!-- Outstanding Payments -->
|
|
<div class="col-md-4 mb-3">
|
|
<div class="card border-0 shadow-sm h-100 bg-warning bg-opacity-10">
|
|
<div class="card-body">
|
|
<h6 class="text-uppercase text-muted fw-bold small">Outstanding Payments</h6>
|
|
<div class="display-6 fw-bold text-dark">R {{ outstanding_total|intcomma }}</div>
|
|
<p class="text-muted small mb-0">Total pending (including adjustments)</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Recent Payments -->
|
|
<div class="col-md-4 mb-3">
|
|
<div class="card border-0 shadow-sm h-100 bg-success bg-opacity-10">
|
|
<div class="card-body">
|
|
<h6 class="text-uppercase text-muted fw-bold small">Recent Payments (2 Months)</h6>
|
|
<div class="display-6 fw-bold text-dark">R {{ recent_payments_total|intcomma }}</div>
|
|
<p class="text-muted small mb-0">Total paid out</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Project Costs -->
|
|
<div class="col-md-4 mb-3">
|
|
<div class="card border-0 shadow-sm h-100">
|
|
<div class="card-header bg-white fw-bold small text-uppercase text-muted">Project Costs (Active)</div>
|
|
<div class="card-body overflow-auto" style="max-height: 150px;">
|
|
{% if project_costs %}
|
|
<ul class="list-group list-group-flush">
|
|
{% for p in project_costs %}
|
|
<li class="list-group-item d-flex justify-content-between align-items-center px-0 py-2">
|
|
<span>{{ p.name }}</span>
|
|
<span class="fw-bold text-dark">R {{ p.cost|intcomma }}</span>
|
|
</li>
|
|
{% endfor %}
|
|
</ul>
|
|
{% else %}
|
|
<p class="text-muted small mb-0">No active project costs.</p>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Filter Tabs -->
|
|
<ul class="nav nav-pills mb-4">
|
|
<li class="nav-item">
|
|
<a class="nav-link {% if active_tab == 'pending' %}active{% endif %}" href="?status=pending">Pending Payments</a>
|
|
</li>
|
|
<li class="nav-item">
|
|
<a class="nav-link {% if active_tab == 'paid' %}active{% endif %}" href="?status=paid">Payment History</a>
|
|
</li>
|
|
<li class="nav-item">
|
|
<a class="nav-link {% if active_tab == 'all' %}active{% endif %}" href="?status=all">All Records</a>
|
|
</li>
|
|
</ul>
|
|
|
|
<!-- Pending Payments Table -->
|
|
{% if active_tab == 'pending' or active_tab == 'all' %}
|
|
{% if workers_data %}
|
|
<div class="card border-0 shadow-sm mb-5">
|
|
<div class="card-header bg-white fw-bold">Pending Payments</div>
|
|
<div class="card-body p-0">
|
|
<div class="table-responsive">
|
|
<table class="table table-hover align-middle mb-0">
|
|
<thead class="bg-light">
|
|
<tr>
|
|
<th class="ps-4">Worker Name</th>
|
|
<th>Breakdown</th>
|
|
<th>Net Payable</th>
|
|
<th class="text-end pe-4">Action</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{% for item in workers_data %}
|
|
<tr>
|
|
<td class="ps-4">
|
|
<div class="fw-bold text-dark">{{ item.worker.name }}</div>
|
|
<div class="small text-muted">ID: {{ item.worker.id_no }}</div>
|
|
{% if item.adjustments %}
|
|
<div class="mt-1">
|
|
{% for adj in item.adjustments %}
|
|
<span class="badge bg-secondary opacity-75 small">
|
|
{{ adj.get_type_display }}: R {{ adj.amount }}
|
|
</span>
|
|
{% endfor %}
|
|
</div>
|
|
{% endif %}
|
|
</td>
|
|
<td>
|
|
<div class="small">
|
|
<div>Work: {{ item.unpaid_count }} days (R {{ item.unpaid_amount|intcomma }})</div>
|
|
<div class="{% if item.adj_amount < 0 %}text-danger{% else %}text-success{% endif %}">
|
|
Adjustments: R {{ item.adj_amount|intcomma }}
|
|
</div>
|
|
</div>
|
|
</td>
|
|
<td class="fw-bold fs-5 {% if item.total_payable < 0 %}text-danger{% else %}text-success{% endif %}">
|
|
R {{ item.total_payable|intcomma }}
|
|
</td>
|
|
<td class="text-end pe-4">
|
|
{% if item.total_payable > 0 %}
|
|
<form action="{% url 'process_payment' item.worker.id %}" method="post">
|
|
{% csrf_token %}
|
|
<button type="submit" class="btn btn-sm btn-success"
|
|
onclick="return confirm('Confirm payment of R {{ item.total_payable }} to {{ item.worker.name }}? This will email the receipt.')">
|
|
Pay Now
|
|
</button>
|
|
</form>
|
|
{% else %}
|
|
<button class="btn btn-sm btn-secondary" disabled>Nothing to Pay</button>
|
|
{% endif %}
|
|
</td>
|
|
</tr>
|
|
{% endfor %}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% elif active_tab == 'pending' %}
|
|
<div class="alert alert-info shadow-sm mb-4">
|
|
<div class="d-flex align-items-center">
|
|
<div class="fs-4 me-3">🎉</div>
|
|
<div>
|
|
<strong>All caught up!</strong><br>
|
|
There are no outstanding payments for active workers.
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
{% endif %}
|
|
|
|
<!-- Payment History Table -->
|
|
{% if active_tab == 'paid' or active_tab == 'all' %}
|
|
{% if paid_records %}
|
|
<div class="card border-0 shadow-sm">
|
|
<div class="card-header bg-white fw-bold">Payment History</div>
|
|
<div class="card-body p-0">
|
|
<div class="table-responsive">
|
|
<table class="table table-hover align-middle mb-0">
|
|
<thead class="bg-light">
|
|
<tr>
|
|
<th class="ps-4">Date</th>
|
|
<th>Payslip ID</th>
|
|
<th>Worker</th>
|
|
<th>Net Amount</th>
|
|
<th class="text-end pe-4">Action</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{% for record in paid_records %}
|
|
<tr>
|
|
<td class="ps-4">{{ record.date|date:"M d, Y" }}</td>
|
|
<td class="text-muted">#{{ record.id|stringformat:"06d" }}</td>
|
|
<td>
|
|
<div class="fw-bold">{{ record.worker.name }}</div>
|
|
<div class="small text-muted">{{ record.worker.id_no }}</div>
|
|
</td>
|
|
<td class="fw-bold">R {{ record.amount|intcomma }}</td>
|
|
<td class="text-end pe-4">
|
|
<a href="{% url 'payslip_detail' record.id %}" class="btn btn-sm btn-outline-primary">
|
|
View Payslip
|
|
</a>
|
|
</td>
|
|
</tr>
|
|
{% endfor %}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% else %}
|
|
<div class="alert alert-light border shadow-sm">
|
|
No payment history found.
|
|
</div>
|
|
{% endif %}
|
|
{% endif %}
|
|
|
|
</div>
|
|
|
|
<!-- Add Adjustment Modal -->
|
|
<div class="modal fade" id="addAdjustmentModal" tabindex="-1" aria-hidden="true">
|
|
<div class="modal-dialog">
|
|
<div class="modal-content">
|
|
<div class="modal-header">
|
|
<h5 class="modal-title fw-bold">Add Payroll Adjustment</h5>
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
|
</div>
|
|
<form action="{% url 'add_adjustment' %}" method="POST">
|
|
{% csrf_token %}
|
|
<div class="modal-body">
|
|
<div class="mb-3">
|
|
<label class="form-label">Worker</label>
|
|
<select name="worker" class="form-select" required>
|
|
<option value="">Select a worker...</option>
|
|
{% for worker in all_workers %}
|
|
<option value="{{ worker.id }}">{{ worker.name }}</option>
|
|
{% endfor %}
|
|
</select>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label class="form-label">Type</label>
|
|
<select name="type" class="form-select" required>
|
|
{% for code, label in adjustment_types %}
|
|
<option value="{{ code }}">{{ label }}</option>
|
|
{% endfor %}
|
|
</select>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label class="form-label">Amount (R)</label>
|
|
<input type="number" name="amount" class="form-control" step="0.01" min="1" required>
|
|
<div class="form-text">For deductions, enter a positive number. It will be subtracted automatically.</div>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label class="form-label">Description</label>
|
|
<input type="text" name="description" class="form-control" placeholder="e.g. Public Holiday Bonus" required>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label class="form-label">Date</label>
|
|
<input type="date" name="date" class="form-control" value="{% now 'Y-m-d' %}">
|
|
</div>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button type="button" class="btn btn-light" data-bs-dismiss="modal">Cancel</button>
|
|
<button type="submit" class="btn btn-primary">Save Adjustment</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% endblock %} |