203 lines
11 KiB
HTML
203 lines
11 KiB
HTML
{% extends 'base.html' %}
|
|
{% load i18n %}
|
|
|
|
{% block content %}
|
|
<div class="container-fluid py-4">
|
|
<div class="row justify-content-center">
|
|
<div class="col-lg-10">
|
|
<div class="d-flex justify-content-between align-items-center mb-4">
|
|
<div>
|
|
<nav aria-label="breadcrumb">
|
|
<ol class="breadcrumb mb-1">
|
|
<li class="breadcrumb-item"><a href="{% url 'accounting_dashboard' %}">{% trans "Accounting" %}</a></li>
|
|
<li class="breadcrumb-item"><a href="{% url 'journal_entries' %}">{% trans "Journal Entries" %}</a></li>
|
|
<li class="breadcrumb-item active">{% trans "New Manual Entry" %}</li>
|
|
</ol>
|
|
</nav>
|
|
<h2 class="mb-0">{% trans "New Manual Journal Entry" %}</h2>
|
|
</div>
|
|
</div>
|
|
|
|
<form method="post" id="journal-form">
|
|
{% csrf_token %}
|
|
<div class="card border-0 shadow-sm mb-4">
|
|
<div class="card-body p-4">
|
|
<div class="row g-3">
|
|
<div class="col-md-3">
|
|
<label class="form-label">{% trans "Date" %}</label>
|
|
{{ form.date }}
|
|
</div>
|
|
<div class="col-md-3">
|
|
<label class="form-label">{% trans "Reference" %}</label>
|
|
{{ form.reference }}
|
|
</div>
|
|
<div class="col-md-6">
|
|
<label class="form-label">{% trans "Description" %}</label>
|
|
{{ form.description }}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="card border-0 shadow-sm">
|
|
<div class="table-responsive">
|
|
<table class="table table-hover align-middle mb-0" id="items-table">
|
|
<thead class="bg-light">
|
|
<tr>
|
|
<th style="width: 40%;">{% trans "Account" %}</th>
|
|
<th style="width: 20%;">{% trans "Type" %}</th>
|
|
<th style="width: 30%;">{% trans "Amount" %}</th>
|
|
<th style="width: 10%;"></th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<tr class="item-row">
|
|
<td>
|
|
<select name="account[]" class="form-select select2-account" required>
|
|
<option value="">{% trans "Select Account" %}</option>
|
|
{% for acc in accounts %}
|
|
<option value="{{ acc.id }}">{{ acc.code }} - {% if LANGUAGE_CODE == 'ar' %}{{ acc.name_ar }}{% else %}{{ acc.name_en }}{% endif %}</option>
|
|
{% endfor %}
|
|
</select>
|
|
</td>
|
|
<td>
|
|
<select name="type[]" class="form-select item-type" required>
|
|
<option value="debit">{% trans "Debit" %}</option>
|
|
<option value="credit">{% trans "Credit" %}</option>
|
|
</select>
|
|
</td>
|
|
<td>
|
|
<div class="input-group">
|
|
<input type="number" name="amount[]" class="form-control item-amount" step="0.001" min="0" required>
|
|
<span class="input-group-text">{{ global_settings.currency_symbol }}</span>
|
|
</div>
|
|
</td>
|
|
<td class="text-center">
|
|
<button type="button" class="btn btn-outline-danger btn-sm remove-row"><i class="bi bi-trash"></i></button>
|
|
</td>
|
|
</tr>
|
|
<tr class="item-row">
|
|
<td>
|
|
<select name="account[]" class="form-select select2-account" required>
|
|
<option value="">{% trans "Select Account" %}</option>
|
|
{% for acc in accounts %}
|
|
<option value="{{ acc.id }}">{{ acc.code }} - {% if LANGUAGE_CODE == 'ar' %}{{ acc.name_ar }}{% else %}{{ acc.name_en }}{% endif %}</option>
|
|
{% endfor %}
|
|
</select>
|
|
</td>
|
|
<td>
|
|
<select name="type[]" class="form-select item-type" required>
|
|
<option value="debit">{% trans "Debit" %}</option>
|
|
<option value="credit" selected>{% trans "Credit" %}</option>
|
|
</select>
|
|
</td>
|
|
<td>
|
|
<div class="input-group">
|
|
<input type="number" name="amount[]" class="form-control item-amount" step="0.001" min="0" required>
|
|
<span class="input-group-text">{{ global_settings.currency_symbol }}</span>
|
|
</div>
|
|
</td>
|
|
<td class="text-center">
|
|
<button type="button" class="btn btn-outline-danger btn-sm remove-row"><i class="bi bi-trash"></i></button>
|
|
</td>
|
|
</tr>
|
|
</tbody>
|
|
<tfoot class="bg-light">
|
|
<tr>
|
|
<td colspan="4">
|
|
<button type="button" class="btn btn-outline-primary btn-sm" id="add-row">
|
|
<i class="bi bi-plus-lg"></i> {% trans "Add Line" %}
|
|
</button>
|
|
</td>
|
|
</tr>
|
|
<tr class="fw-bold">
|
|
<td class="text-end">{% trans "Totals" %}:</td>
|
|
<td class="text-end">{% trans "Debit" %}: <span id="total-debit">0.000</span></td>
|
|
<td class="text-end">{% trans "Credit" %}: <span id="total-credit">0.000</span></td>
|
|
<td></td>
|
|
</tr>
|
|
<tr>
|
|
<td colspan="4" class="text-center py-2" id="balance-message">
|
|
<span class="badge bg-danger">{% trans "Out of Balance" %}</span>
|
|
</td>
|
|
</tr>
|
|
</tfoot>
|
|
</table>
|
|
</div>
|
|
<div class="card-footer bg-white p-4 border-top d-flex justify-content-end gap-2">
|
|
<a href="{% url 'journal_entries' %}" class="btn btn-light">{% trans "Cancel" %}</a>
|
|
<button type="submit" class="btn btn-primary px-4" id="submit-btn" disabled>{% trans "Create Entry" %}</button>
|
|
</div>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
const table = document.getElementById('items-table').getElementsByTagName('tbody')[0];
|
|
const addBtn = document.getElementById('add-row');
|
|
const totalDebitSpan = document.getElementById('total-debit');
|
|
const totalCreditSpan = document.getElementById('total-credit');
|
|
const balanceMessage = document.getElementById('balance-message');
|
|
const submitBtn = document.getElementById('submit-btn');
|
|
|
|
function updateTotals() {
|
|
let totalDebit = 0;
|
|
let totalCredit = 0;
|
|
|
|
document.querySelectorAll('.item-row').forEach(row => {
|
|
const type = row.querySelector('.item-type').value;
|
|
const amount = parseFloat(row.querySelector('.item-amount').value) || 0;
|
|
|
|
if (type === 'debit') totalDebit += amount;
|
|
else totalCredit += amount;
|
|
});
|
|
|
|
totalDebitSpan.textContent = totalDebit.toFixed(3);
|
|
totalCreditSpan.textContent = totalCredit.toFixed(3);
|
|
|
|
const balanced = totalDebit > 0 && Math.abs(totalDebit - totalCredit) < 0.001;
|
|
|
|
if (balanced) {
|
|
balanceMessage.innerHTML = '<span class="badge bg-success">{% trans "Balanced" %}</span>';
|
|
submitBtn.disabled = false;
|
|
} else {
|
|
balanceMessage.innerHTML = '<span class="badge bg-danger">{% trans "Out of Balance" %}</span>';
|
|
submitBtn.disabled = true;
|
|
}
|
|
}
|
|
|
|
addBtn.addEventListener('click', function() {
|
|
const firstRow = document.querySelector('.item-row');
|
|
const newRow = firstRow.cloneNode(true);
|
|
newRow.querySelector('.item-amount').value = '';
|
|
table.appendChild(newRow);
|
|
|
|
newRow.querySelector('.remove-row').addEventListener('click', function() {
|
|
if (document.querySelectorAll('.item-row').length > 2) {
|
|
newRow.remove();
|
|
updateTotals();
|
|
}
|
|
});
|
|
|
|
newRow.querySelector('.item-type').addEventListener('change', updateTotals);
|
|
newRow.querySelector('.item-amount').addEventListener('input', updateTotals);
|
|
});
|
|
|
|
document.querySelectorAll('.remove-row').forEach(btn => {
|
|
btn.addEventListener('click', function() {
|
|
if (document.querySelectorAll('.item-row').length > 2) {
|
|
btn.closest('.item-row').remove();
|
|
updateTotals();
|
|
}
|
|
});
|
|
});
|
|
|
|
document.querySelectorAll('.item-type').forEach(el => el.addEventListener('change', updateTotals));
|
|
document.querySelectorAll('.item-amount').forEach(el => el.addEventListener('input', updateTotals));
|
|
});
|
|
</script>
|
|
{% endblock %}
|