38156-vm/core/templates/core/create_receipt.html
2026-02-04 13:33:29 +00:00

249 lines
11 KiB
HTML

{% extends 'base.html' %}
{% load static %}
{% block content %}
<div class="container mt-5">
<div class="row justify-content-center">
<div class="col-lg-8">
<div class="card shadow-lg border-0">
<div class="card-header text-white py-3" style="background-color: var(--primary-color); border-top-left-radius: 1rem; border-top-right-radius: 1rem;">
<h4 class="mb-0 fw-bold"><i class="fas fa-file-invoice-dollar me-2"></i>Receipt Generator</h4>
</div>
<div class="card-body p-4">
<form method="post" id="receiptForm">
{% csrf_token %}
<!-- Header Info -->
<div class="row g-3 mb-4">
<div class="col-md-6">
<label class="form-label fw-bold text-secondary">Date</label>
{{ form.date }}
</div>
<div class="col-md-6">
<label class="form-label fw-bold text-secondary">Vendor Name</label>
{{ form.vendor }}
<div class="form-text text-muted small"><i class="fas fa-info-circle"></i> Will appear as the main title on the receipt.</div>
</div>
<div class="col-md-6">
<label class="form-label fw-bold text-secondary">Payment Method</label>
{{ form.payment_method }}
</div>
<div class="col-12">
<label class="form-label fw-bold text-secondary">Description</label>
{{ form.description }}
</div>
</div>
<hr class="text-muted opacity-25">
<!-- Line Items -->
<div class="d-flex justify-content-between align-items-center mb-3">
<h5 class="fw-bold text-dark 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 form in items %}
<div class="item-row row g-2 align-items-center mb-2">
{{ form.id }}
<div class="col-7">
{{ form.product }}
</div>
<div class="col-4">
<div class="input-group">
<span class="input-group-text bg-light border-end-0">R</span>
{{ form.amount }}
</div>
</div>
<div class="col-1 text-center">
{% if items.can_delete %}
<div class="form-check d-none">
{{ 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 class="text-muted opacity-25 mt-4">
<!-- VAT and Totals -->
<div class="row">
<div class="col-md-6 mb-3 mb-md-0">
<label class="form-label d-block fw-bold text-secondary mb-2">VAT Configuration (15%)</label>
<div class="card bg-light border-0 p-3">
{% 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>
<div class="col-md-6">
<div class="p-3 rounded" style="background-color: #f8fafc; border: 1px solid #e2e8f0;">
<div class="d-flex justify-content-between mb-2">
<span class="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 class="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 border-top pt-2 mt-2">
<span class="h5 mb-0 fw-bold">Total:</span>
<span class="h5 mb-0" style="color: var(--accent-color);">R <span id="display-total">0.00</span></span>
</div>
</div>
</div>
</div>
<div class="d-grid mt-5">
<button type="submit" class="btn btn-accent btn-lg shadow-sm">
<i class="fas fa-paper-plane me-2"></i>Create & Send Receipt
</button>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
<!-- Empty Form Template for JS -->
<div id="empty-form" class="d-none">
<div class="item-row row g-2 align-items-center mb-2">
<div class="col-7">
{{ items.empty_form.product }}
</div>
<div class="col-4">
<div class="input-group">
<span class="input-group-text bg-light border-end-0">R</span>
{{ items.empty_form.amount }}
</div>
</div>
<div class="col-1 text-center">
<div class="form-check d-none">
{{ items.empty_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>
</div>
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
const itemsContainer = document.getElementById('items-container');
const addItemBtn = document.getElementById('add-item');
const totalForms = document.getElementById('id_items-TOTAL_FORMS');
const emptyFormHtml = document.getElementById('empty-form').innerHTML;
// Elements for calculation
const displaySubtotal = document.getElementById('display-subtotal');
const displayVat = document.getElementById('display-vat');
const displayTotal = document.getElementById('display-total');
const vatRadios = document.querySelectorAll('input[name="vat_type"]');
function updateCalculations() {
let sum = 0;
const amounts = document.querySelectorAll('.item-row:not(.deleted) .item-amount');
amounts.forEach(input => {
const val = parseFloat(input.value) || 0;
sum += val;
});
let vat = 0;
let total = 0;
let subtotal = 0;
// Find selected VAT type
let vatType = 'INCLUDED'; // Default
vatRadios.forEach(r => {
if (r.checked) vatType = r.value;
});
if (vatType === 'INCLUDED') {
// Reverse calc: Total is Sum. VAT is part of it.
// Amount = Base + VAT
// Base = Amount / 1.15
// VAT = Amount - Base
total = sum;
const base = total / 1.15;
vat = total - base;
subtotal = base;
} else if (vatType === 'EXCLUDED') {
// Forward calc: Sum is Subtotal. Add VAT on top.
subtotal = sum;
vat = subtotal * 0.15;
total = subtotal + vat;
} else {
// NONE
subtotal = sum;
vat = 0;
total = sum;
}
displaySubtotal.textContent = subtotal.toFixed(2);
displayVat.textContent = vat.toFixed(2);
displayTotal.textContent = total.toFixed(2);
}
// Add Row
addItemBtn.addEventListener('click', function() {
const formIdx = parseInt(totalForms.value);
const newHtml = emptyFormHtml.replace(/__prefix__/g, formIdx);
const newRow = document.createElement('div');
newRow.innerHTML = newHtml; // wrapper
// Unwrap logic to append cleanly
while (newRow.firstChild) {
itemsContainer.appendChild(newRow.firstChild);
}
totalForms.value = formIdx + 1;
updateCalculations();
});
// Delete Row (Event Delegation)
itemsContainer.addEventListener('click', function(e) {
if (e.target.closest('.delete-row')) {
const row = e.target.closest('.item-row');
const deleteCheckbox = row.querySelector('input[name$="-DELETE"]');
if (deleteCheckbox) {
deleteCheckbox.checked = true;
row.classList.add('d-none', 'deleted');
} else {
row.remove();
}
updateCalculations();
}
});
// Input Changes
itemsContainer.addEventListener('input', function(e) {
if (e.target.classList.contains('item-amount')) {
updateCalculations();
}
});
// VAT Change
vatRadios.forEach(r => {
r.addEventListener('change', updateCalculations);
});
// Initial Calc
updateCalculations();
});
</script>
{% endblock %}