Add Advance Payment type to payroll adjustments. Admins can give workers partial advances against earned wages — each advance creates a payslip sent to Spark immediately, while the remaining balance stays on the dashboard. Multiple advances allowed. Final payment deducts all advances automatically. Also refactored type-grouping logic into ADDITIVE_TYPES/DEDUCTIVE_TYPES constants (was duplicated 15 times across views). Includes migration 0016 (choices-only, run: python manage.py migrate) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
150 lines
7.2 KiB
HTML
150 lines
7.2 KiB
HTML
{% extends 'base.html' %}
|
|
{% load humanize %}
|
|
|
|
{% block title %}Payslip #{{ record.id }} - Fox Fitt{% endblock %}
|
|
|
|
{% block content %}
|
|
<div class="container py-5">
|
|
<div class="d-print-none mb-4">
|
|
<a href="{% url 'payroll_dashboard' %}?status=paid" class="btn btn-outline-secondary me-2">← Back to Payment History</a>
|
|
<button onclick="window.print()" class="btn btn-primary">Print Payslip</button>
|
|
</div>
|
|
|
|
<div class="card border-0 shadow-lg" id="payslip-card">
|
|
<div class="card-body p-5">
|
|
<!-- Header -->
|
|
<div class="row mb-5 border-bottom pb-4 align-items-center">
|
|
<div class="col-md-6">
|
|
<h6 class="text-uppercase text-muted fw-bold small mb-1">Payment To Beneficiary:</h6>
|
|
<h2 class="fw-bold text-dark mb-0 text-uppercase">{{ record.worker.name }}</h2>
|
|
<p class="text-muted small mb-0">Payslip No. #{{ record.id|stringformat:"06d" }}</p>
|
|
</div>
|
|
<div class="col-md-6 text-md-end mt-3 mt-md-0">
|
|
<h3 class="fw-bold text-uppercase text-secondary mb-1">Payslip</h3>
|
|
<div class="fw-bold">{{ record.date|date:"F j, Y" }}</div>
|
|
<div class="text-muted small">Payer: Fox Fitt</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Worker Details -->
|
|
<div class="row mb-5">
|
|
<div class="col-md-6">
|
|
<h6 class="text-uppercase text-muted fw-bold small mb-3">Beneficiary Details:</h6>
|
|
<h4 class="fw-bold">{{ record.worker.name }}</h4>
|
|
<p class="mb-0">ID Number: <strong>{{ record.worker.id_no }}</strong></p>
|
|
<p class="mb-0">Phone: {{ record.worker.phone_no }}</p>
|
|
</div>
|
|
<div class="col-md-6 text-md-end mt-4 mt-md-0">
|
|
<h6 class="text-uppercase text-muted fw-bold small mb-3">Net Payable Amount:</h6>
|
|
<div class="display-6 fw-bold text-dark">R {{ record.amount|intcomma }}</div>
|
|
<p class="text-success small fw-bold mt-2"><i class="fas fa-check-circle me-1"></i> PAID</p>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Work Details -->
|
|
<h6 class="text-uppercase text-muted fw-bold small mb-3">Work Log Details (Attendance)</h6>
|
|
<div class="table-responsive mb-4">
|
|
<table class="table table-bordered mb-0">
|
|
<thead class="table-light">
|
|
<tr>
|
|
<th>Date</th>
|
|
<th>Project</th>
|
|
<th>Notes</th>
|
|
<th class="text-end">Amount</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{% for log in logs %}
|
|
<tr>
|
|
<td>{{ log.date|date:"M d, Y" }}</td>
|
|
<td>{{ log.project.name }}</td>
|
|
<td>{{ log.notes|default:"-"|truncatechars:50 }}</td>
|
|
<td class="text-end">R {{ record.worker.day_rate|intcomma }}</td>
|
|
</tr>
|
|
{% empty %}
|
|
<tr>
|
|
<td colspan="4" class="text-center text-muted">No work logs in this period.</td>
|
|
</tr>
|
|
{% endfor %}
|
|
</tbody>
|
|
<tfoot class="table-light">
|
|
<tr>
|
|
<td colspan="3" class="text-end fw-bold">Base Pay Subtotal</td>
|
|
<td class="text-end fw-bold">R {{ base_pay|intcomma }}</td>
|
|
</tr>
|
|
</tfoot>
|
|
</table>
|
|
</div>
|
|
|
|
<!-- Adjustments -->
|
|
{% if adjustments %}
|
|
<h6 class="text-uppercase text-muted fw-bold small mb-3 mt-4">Adjustments (Bonuses, Deductions, Loans)</h6>
|
|
<div class="table-responsive mb-4">
|
|
<table class="table table-bordered mb-0">
|
|
<thead class="table-light">
|
|
<tr>
|
|
<th>Date</th>
|
|
<th>Type</th>
|
|
<th>Description</th>
|
|
<th class="text-end">Amount</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{% for adj in adjustments %}
|
|
<tr>
|
|
<td>{{ adj.date|date:"M d, Y" }}</td>
|
|
<td>
|
|
<span class="badge bg-secondary text-uppercase">{{ adj.get_type_display }}</span>
|
|
</td>
|
|
<td>{{ adj.description }}</td>
|
|
<td class="text-end {% if adj.type == 'DEDUCTION' or adj.type == 'LOAN_REPAYMENT' or adj.type == 'ADVANCE' %}text-danger{% else %}text-success{% endif %}">
|
|
{% if adj.type == 'DEDUCTION' or adj.type == 'LOAN_REPAYMENT' or adj.type == 'ADVANCE' %}
|
|
- R {{ adj.amount|intcomma }}
|
|
{% else %}
|
|
+ R {{ adj.amount|intcomma }}
|
|
{% endif %}
|
|
</td>
|
|
</tr>
|
|
{% endfor %}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
{% endif %}
|
|
|
|
<!-- Grand Total -->
|
|
<div class="row justify-content-end mt-4">
|
|
<div class="col-md-5">
|
|
<table class="table table-sm border-0">
|
|
<tr>
|
|
<td class="text-end border-0 text-muted">Base Pay:</td>
|
|
<td class="text-end border-0" width="120">R {{ base_pay|intcomma }}</td>
|
|
</tr>
|
|
{% if adjustments %}
|
|
<tr>
|
|
<td class="text-end border-0 text-muted">Adjustments Net:</td>
|
|
<td class="text-end border-0">
|
|
{% if adjustments_net >= 0 %}
|
|
+ R {{ adjustments_net|intcomma }}
|
|
{% else %}
|
|
- R {{ adjustments_net|stringformat:".2f"|slice:"1:"|intcomma }}
|
|
{% endif %}
|
|
</td>
|
|
</tr>
|
|
{% endif %}
|
|
<tr class="border-top border-dark">
|
|
<td class="text-end border-0 fw-bold fs-5">Net Payable:</td>
|
|
<td class="text-end border-0 fw-bold fs-5">R {{ record.amount|intcomma }}</td>
|
|
</tr>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Footer -->
|
|
<div class="text-center text-muted small mt-5 pt-4 border-top">
|
|
<p>This is a computer-generated document and does not require a signature.</p>
|
|
<p>Payer: Fox Fitt © 2026</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% endblock %} |