38686-vm/core/templates/core/create_receipt.html
Konrad du Plessis 82c1906607 Redesign UI with premium orange theme, sidebar nav, and bottom tab bar
Replace the green accent with a warm orange/amber palette and switch to a
dark-first design. Add a fixed sidebar for desktop navigation and a bottom
tab bar for mobile, replacing the top navbar. Cards now use glass-morphism
with left accent bars, buttons use orange gradients, and decorative glow
effects add depth. All 8 page templates updated, both light and dark modes
tested across desktop and mobile viewports.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-20 18:40:00 +02:00

269 lines
12 KiB
HTML

{% extends 'base.html' %}
{% block title %}Create Receipt | FoxFitt{% endblock %}
{% block content %}
<!-- === CREATE EXPENSE RECEIPT ===
Single-page form for recording business expenses.
- Dynamic line items (add/remove rows with JavaScript)
- Live VAT calculation (Included / Excluded / None)
- On submit: saves to database + emails HTML + PDF to Spark Receipt -->
<div class="container py-4">
<div class="row justify-content-center">
<div class="col-lg-10 col-xl-8">
<!-- Page header -->
<div class="d-flex justify-content-between align-items-center mb-4">
<h1 class="page-title"><i class="fas fa-receipt me-2" style="color: var(--accent);"></i>Create Expense Receipt</h1>
<a href="{% url 'home' %}" class="btn btn-outline-secondary btn-sm">
<i class="fas fa-arrow-left fa-sm me-1"></i> Back
</a>
</div>
<div class="card">
<div class="card-body p-4">
<form method="post" id="receipt-form">
{% csrf_token %}
<!-- === RECEIPT HEADER FIELDS === -->
<div class="row g-3 mb-4">
<div class="col-md-6">
<label class="form-label fw-semibold">Date</label>
{{ form.date }}
</div>
<div class="col-md-6">
<label class="form-label fw-semibold">Vendor Name</label>
{{ form.vendor_name }}
<small style="color: var(--text-tertiary);">
<i class="fas fa-info-circle"></i> Will appear as the main title on the receipt.
</small>
</div>
<div class="col-md-6">
<label class="form-label fw-semibold">Payment Method</label>
{{ form.payment_method }}
</div>
<div class="col-12">
<label class="form-label fw-semibold">Description</label>
{{ form.description }}
</div>
</div>
<hr style="border-color: var(--border-default);">
<!-- === LINE ITEMS === -->
<div class="d-flex justify-content-between align-items-center mb-3 mt-4">
<h5 class="fw-bold m-0">Items</h5>
<button type="button" id="add-item" class="btn btn-sm btn-outline-secondary rounded-pill">
<i class="fas fa-plus me-1"></i> Add Line
</button>
</div>
{{ items.management_form }}
<div id="items-container">
{% for item_form in items %}
<div class="item-row row g-2 align-items-center mb-2">
{{ item_form.id }}
<div class="col-12 col-md-7">
{{ item_form.product_name }}
</div>
<div class="col-10 col-md-4">
<div class="input-group">
<span class="input-group-text">R</span>
{{ item_form.amount }}
</div>
</div>
<div class="col-2 col-md-1 text-center">
{% if items.can_delete %}
<div class="form-check d-none">
{{ item_form.DELETE }}
</div>
<button type="button" class="btn btn-outline-danger btn-sm delete-row rounded-circle" title="Remove">
<i class="fas fa-times"></i>
</button>
{% endif %}
</div>
</div>
{% endfor %}
</div>
<hr style="border-color: var(--border-default);">
<!-- === VAT + TOTALS === -->
<div class="row mt-4">
<!-- VAT radio buttons -->
<div class="col-md-6 mb-3 mb-md-0">
<label class="form-label d-block fw-semibold mb-2">VAT Configuration (15%)</label>
<div class="p-3 rounded" style="background: var(--bg-inset); border: 1px solid var(--border-default);">
{% for radio in form.vat_type %}
<div class="form-check mb-2">
{{ radio.tag }}
<label class="form-check-label" for="{{ radio.id_for_label }}">
{{ radio.choice_label }}
</label>
</div>
{% endfor %}
</div>
</div>
<!-- Live totals -->
<div class="col-md-6">
<label class="form-label d-block fw-semibold mb-2">Receipt Totals</label>
<div class="p-3 rounded" style="background: var(--bg-inset); border: 1px solid var(--border-default);">
<div class="d-flex justify-content-between mb-2">
<span style="color: var(--text-secondary);">Subtotal (Excl. VAT):</span>
<span class="fw-bold">R <span id="display-subtotal">0.00</span></span>
</div>
<div class="d-flex justify-content-between mb-2">
<span style="color: var(--text-secondary);">VAT (15%):</span>
<span class="fw-bold">R <span id="display-vat">0.00</span></span>
</div>
<div class="d-flex justify-content-between pt-2 mt-2" style="border-top: 1px solid var(--border-default);">
<span class="h5 mb-0 fw-bold">Total:</span>
<span class="h5 mb-0" style="color: var(--accent); font-family: 'Poppins', sans-serif;">
R <span id="display-total">0.00</span>
</span>
</div>
</div>
</div>
</div>
<!-- Submit -->
<div class="text-end mt-4">
<button type="submit" class="btn btn-accent btn-lg">
<i class="fas fa-paper-plane me-2"></i> Create & Send Receipt
</button>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
<!-- === JavaScript: Dynamic line items + live VAT calculation === -->
<script>
(function() {
'use strict';
var itemsContainer = document.getElementById('items-container');
var addItemBtn = document.getElementById('add-item');
var totalForms = document.querySelector('#id_line_items-TOTAL_FORMS');
var displaySubtotal = document.getElementById('display-subtotal');
var displayVat = document.getElementById('display-vat');
var displayTotal = document.getElementById('display-total');
var vatRadios = document.querySelectorAll('input[name="vat_type"]');
// === ADD NEW LINE ITEM ===
addItemBtn.addEventListener('click', function() {
var formIdx = parseInt(totalForms.value);
var row = document.createElement('div');
row.className = 'item-row row g-2 align-items-center mb-2';
var hiddenId = document.createElement('input');
hiddenId.type = 'hidden';
hiddenId.name = 'line_items-' + formIdx + '-id';
hiddenId.id = 'id_line_items-' + formIdx + '-id';
row.appendChild(hiddenId);
var prodCol = document.createElement('div');
prodCol.className = 'col-12 col-md-7';
var prodInput = document.createElement('input');
prodInput.type = 'text';
prodInput.name = 'line_items-' + formIdx + '-product_name';
prodInput.className = 'form-control';
prodInput.placeholder = 'Item Name';
prodInput.id = 'id_line_items-' + formIdx + '-product_name';
prodCol.appendChild(prodInput);
row.appendChild(prodCol);
var amtCol = document.createElement('div');
amtCol.className = 'col-10 col-md-4';
var inputGroup = document.createElement('div');
inputGroup.className = 'input-group';
var prefix = document.createElement('span');
prefix.className = 'input-group-text';
prefix.textContent = 'R';
var amtInput = document.createElement('input');
amtInput.type = 'number';
amtInput.name = 'line_items-' + formIdx + '-amount';
amtInput.className = 'form-control item-amount';
amtInput.step = '0.01';
amtInput.placeholder = '0.00';
amtInput.id = 'id_line_items-' + formIdx + '-amount';
inputGroup.appendChild(prefix);
inputGroup.appendChild(amtInput);
amtCol.appendChild(inputGroup);
row.appendChild(amtCol);
var delCol = document.createElement('div');
delCol.className = 'col-2 col-md-1 text-center';
var delBtn = document.createElement('button');
delBtn.type = 'button';
delBtn.className = 'btn btn-outline-danger btn-sm delete-row rounded-circle';
delBtn.title = 'Remove';
var delIcon = document.createElement('i');
delIcon.className = 'fas fa-times';
delBtn.appendChild(delIcon);
delCol.appendChild(delBtn);
row.appendChild(delCol);
itemsContainer.appendChild(row);
totalForms.value = formIdx + 1;
updateCalculations();
});
// === DELETE LINE ITEM ===
itemsContainer.addEventListener('click', function(e) {
var deleteBtn = e.target.closest('.delete-row');
if (!deleteBtn) return;
var row = deleteBtn.closest('.item-row');
var deleteCheckbox = row.querySelector('input[name$="-DELETE"]');
if (deleteCheckbox) {
deleteCheckbox.checked = true;
row.classList.add('d-none', 'deleted');
} else {
row.remove();
}
updateCalculations();
});
// === LIVE AMOUNT CHANGES ===
itemsContainer.addEventListener('input', function(e) {
if (e.target.classList.contains('item-amount')) updateCalculations();
});
vatRadios.forEach(function(radio) {
radio.addEventListener('change', updateCalculations);
});
// === VAT CALCULATION ===
function updateCalculations() {
var sum = 0;
document.querySelectorAll('.item-row:not(.deleted) .item-amount').forEach(function(input) {
sum += parseFloat(input.value) || 0;
});
var vatType = 'None';
vatRadios.forEach(function(r) { if (r.checked) vatType = r.value; });
var subtotal = 0, vat = 0, total = 0;
if (vatType === 'Included') {
total = sum; subtotal = total / 1.15; vat = total - subtotal;
} else if (vatType === 'Excluded') {
subtotal = sum; vat = subtotal * 0.15; total = subtotal + vat;
} else {
subtotal = sum; total = sum;
}
displaySubtotal.textContent = subtotal.toFixed(2);
displayVat.textContent = vat.toFixed(2);
displayTotal.textContent = total.toFixed(2);
}
updateCalculations();
})();
</script>
{% endblock %}