Apply custom accounts combobox to transaction editor

This commit is contained in:
RunasSudo 2024-04-06 02:00:30 +11:00
parent af2818b6ae
commit 64b49b00ff
Signed by: RunasSudo
GPG Key ID: 7234E476BF21C61A
3 changed files with 90 additions and 100 deletions

View File

@ -38,7 +38,7 @@ def journal():
@app.route('/journal/new-transaction', methods=['GET', 'POST']) @app.route('/journal/new-transaction', methods=['GET', 'POST'])
def journal_new_transaction(): def journal_new_transaction():
if request.method == 'GET': if request.method == 'GET':
return render_template('journal/journal_edit_transaction.html', transaction=None) return render_template('journal/journal_edit_transaction.html', transaction=None, all_accounts=all_accounts())
# New transaction # New transaction
transaction = Transaction( transaction = Transaction(
@ -73,7 +73,7 @@ def journal_edit_transaction():
abort(404) abort(404)
if request.method == 'GET': if request.method == 'GET':
return render_template('journal/journal_edit_transaction.html', transaction=transaction) return render_template('journal/journal_edit_transaction.html', transaction=transaction, all_accounts=all_accounts())
if request.form.get('action', None) == 'delete': if request.form.get('action', None) == 'delete':
# Delete transaction # Delete transaction

View File

@ -20,7 +20,7 @@
<path fill-rule="evenodd" d="M10 3a.75.75 0 01.55.24l3.25 3.5a.75.75 0 11-1.1 1.02L10 4.852 7.3 7.76a.75.75 0 01-1.1-1.02l3.25-3.5A.75.75 0 0110 3zm-3.76 9.2a.75.75 0 011.06.04l2.7 2.908 2.7-2.908a.75.75 0 111.1 1.02l-3.25 3.5a.75.75 0 01-1.1 0l-3.25-3.5a.75.75 0 01.04-1.06z" clip-rule="evenodd" /> <path fill-rule="evenodd" d="M10 3a.75.75 0 01.55.24l3.25 3.5a.75.75 0 11-1.1 1.02L10 4.852 7.3 7.76a.75.75 0 01-1.1-1.02l3.25-3.5A.75.75 0 0110 3zm-3.76 9.2a.75.75 0 011.06.04l2.7 2.908 2.7-2.908a.75.75 0 111.1 1.02l-3.25 3.5a.75.75 0 01-1.1 0l-3.25-3.5a.75.75 0 01.04-1.06z" clip-rule="evenodd" />
</svg> </svg>
</button> </button>
<ul class="hidden peer-focus:block absolute z-10 mt-1 max-h-60 w-full overflow-auto bg-white py-1 text-base shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none sm:text-sm"> <ul class="hidden peer-focus:block absolute z-20 mt-1 max-h-60 w-full overflow-auto bg-white py-1 text-base shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none sm:text-sm">
{% for account in all_accounts %} {% for account in all_accounts %}
<li class="group relative cursor-default select-none py-1 pl-3 pr-9 text-gray-900 hover:text-white hover:bg-indigo-600"> <li class="group relative cursor-default select-none py-1 pl-3 pr-9 text-gray-900 hover:text-white hover:bg-indigo-600">
<span class="combobox-text block truncate group-data-[selected=selected]:font-semibold">{{ account }}</span> <span class="combobox-text block truncate group-data-[selected=selected]:font-semibold">{{ account }}</span>

View File

@ -18,6 +18,80 @@
{% extends 'base.html' %} {% extends 'base.html' %}
{% block title %}{{ 'Edit' if transaction and transaction.id else 'New' }} transaction{% endblock %} {% block title %}{{ 'Edit' if transaction and transaction.id else 'New' }} transaction{% endblock %}
{# Macros for template posting rows as these are reused #}
{% macro template_dr() %}
<tr>
<td></td>
{#<td></td>#}
<td class="py-1 px-1" colspan="2">
<div class="relative flex">
<div class="relative flex flex-grow items-stretch shadow-sm">
<div class="absolute inset-y-0 left-0 flex items-center z-10">
<select class="h-full border-0 bg-transparent py-0 pl-2 pr-8 text-gray-900 focus:ring-2 focus:ring-inset focus:ring-indigo-600" name="sign" onchange="changeDrCr(this)">
<option value="dr" selected>Dr</option>
<option value="cr">Cr</option>
</select>
</div>
<div class="relative combobox w-full">
<input type="text" class="bordered-field pl-16 peer" name="account" autocomplete="off">
{% include 'components/accounts_combobox_inner.html' %}
</div>
</div>
<a class="relative -ml-px px-2 py-2 text-gray-500 hover:text-gray-700" href="#" onclick="addPosting(this);return false;">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-4 h-4">
<path stroke-linecap="round" stroke-linejoin="round" d="M12 4.5v15m7.5-7.5h-15" />
</svg>
</a>
</div>
</td>
<td class="amount-dr has-amount py-1 px-1">
<div class="relative shadow-sm">
<div class="pointer-events-none absolute inset-y-0 left-0 flex items-center pl-3">
<span class="text-gray-500">$</span>
</div>
<input type="number" class="bordered-field pl-7" name="amount" step="0.01" oninput="changeAmount(this)">
</div>
</td>
<td class="amount-cr py-1 pl-1"></td>
</tr>
{% endmacro %}
{% macro template_cr() %}
<tr>
<td></td>
{#<td></td>#}
<td class="py-1 px-1" colspan="2">
<div class="relative flex">
<div class="relative flex flex-grow items-stretch shadow-sm">
<div class="absolute inset-y-0 left-0 flex items-center z-10">
<select class="h-full border-0 bg-transparent py-0 pl-2 pr-8 text-gray-900 focus:ring-2 focus:ring-inset focus:ring-indigo-600" name="sign" onchange="changeDrCr(this)">
<option value="dr">Dr</option>
<option value="cr" selected>Cr</option>
</select>
</div>
<div class="relative combobox w-full">
<input type="text" class="bordered-field pl-16 peer" name="account" autocomplete="off">
{% include 'components/accounts_combobox_inner.html' %}
</div>
</div>
<a class="relative -ml-px px-2 py-2 text-gray-500 hover:text-gray-700" href="#" onclick="addPosting(this);return false;">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-4 h-4">
<path stroke-linecap="round" stroke-linejoin="round" d="M12 4.5v15m7.5-7.5h-15" />
</svg>
</a>
</div>
</td>
<td class="amount-dr py-1 px-1"></td>
<td class="amount-cr has-amount py-1 pl-1">
<div class="relative shadow-sm">
<div class="pointer-events-none absolute inset-y-0 left-0 flex items-center pl-3">
<span class="text-gray-500">$</span>
</div>
<input type="number" class="bordered-field pl-7" name="amount" step="0.01" oninput="changeAmount(this)">
</div>
</td>
</tr>
{% endmacro %}
{% block content %} {% block content %}
<h1 class="page-heading mb-4"> <h1 class="page-heading mb-4">
{{ 'Edit' if transaction and transaction.id else 'New' }} transaction {{ 'Edit' if transaction and transaction.id else 'New' }} transaction
@ -52,13 +126,16 @@
<td class="py-1 px-1" colspan="2"> <td class="py-1 px-1" colspan="2">
<div class="relative flex"> <div class="relative flex">
<div class="relative flex flex-grow items-stretch shadow-sm"> <div class="relative flex flex-grow items-stretch shadow-sm">
<div class="absolute inset-y-0 left-0 flex items-center"> <div class="absolute inset-y-0 left-0 flex items-center z-10">
<select class="h-full border-0 bg-transparent py-0 pl-2 pr-8 text-gray-900 focus:ring-2 focus:ring-inset focus:ring-indigo-600" name="sign" onchange="changeDrCr(this)"> <select class="h-full border-0 bg-transparent py-0 pl-2 pr-8 text-gray-900 focus:ring-2 focus:ring-inset focus:ring-indigo-600" name="sign" onchange="changeDrCr(this)">
<option value="dr"{% if posting.quantity >= 0 %} selected{% endif %}>Dr</option> <option value="dr"{% if posting.quantity >= 0 %} selected{% endif %}>Dr</option>
<option value="cr"{% if posting.quantity < 0 %} selected{% endif %}>Cr</option> <option value="cr"{% if posting.quantity < 0 %} selected{% endif %}>Cr</option>
</select> </select>
</div> </div>
<input type="text" class="bordered-field pl-16" name="account" value="{{ posting.account }}"> <div class="relative combobox w-full">
<input type="text" class="bordered-field pl-16 peer" name="account" value="{{ posting.account }}" autocomplete="off">
{% include 'components/accounts_combobox_inner.html' %}
</div>
</div> </div>
<a class="relative -ml-px px-2 py-2 text-gray-500 hover:text-gray-700" href="#" onclick="addPosting(this);return false;"> <a class="relative -ml-px px-2 py-2 text-gray-500 hover:text-gray-700" href="#" onclick="addPosting(this);return false;">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-4 h-4"> <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-4 h-4">
@ -91,68 +168,8 @@
</tr> </tr>
{% endfor %} {% endfor %}
{% else %} {% else %}
<tr> {{ template_dr() }}
<td></td> {{ template_cr() }}
{#<td></td>#}
<td class="py-1 px-1" colspan="2">
<div class="relative flex">
<div class="relative flex flex-grow items-stretch shadow-sm">
<div class="absolute inset-y-0 left-0 flex items-center">
<select class="h-full border-0 bg-transparent py-0 pl-2 pr-8 text-gray-900 focus:ring-2 focus:ring-inset focus:ring-indigo-600" name="sign" onchange="changeDrCr(this)">
<option value="dr" selected>Dr</option>
<option value="cr">Cr</option>
</select>
</div>
<input type="text" class="bordered-field pl-16" name="account">
</div>
<a class="relative -ml-px px-2 py-2 text-gray-500 hover:text-gray-700" href="#" onclick="addPosting(this);return false;">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-4 h-4">
<path stroke-linecap="round" stroke-linejoin="round" d="M12 4.5v15m7.5-7.5h-15" />
</svg>
</a>
</div>
</td>
<td class="amount-dr has-amount py-1 px-1">
<div class="relative shadow-sm">
<div class="pointer-events-none absolute inset-y-0 left-0 flex items-center pl-3">
<span class="text-gray-500">$</span>
</div>
<input type="number" class="bordered-field pl-7" name="amount" step="0.01" oninput="changeAmount(this)">
</div>
</td>
<td class="amount-cr py-1 pl-1"></td>
</tr>
<tr>
<td></td>
{#<td></td>#}
<td class="py-1 px-1" colspan="2">
<div class="relative flex">
<div class="relative flex flex-grow items-stretch shadow-sm">
<div class="absolute inset-y-0 left-0 flex items-center">
<select class="h-full border-0 bg-transparent py-0 pl-2 pr-8 text-gray-900 focus:ring-2 focus:ring-inset focus:ring-indigo-600" name="sign" onchange="changeDrCr(this)">
<option value="dr">Dr</option>
<option value="cr" selected>Cr</option>
</select>
</div>
<input type="text" class="bordered-field pl-16" name="account">
</div>
<a class="relative -ml-px px-2 py-2 text-gray-500 hover:text-gray-700" href="#" onclick="addPosting(this);return false;">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-4 h-4">
<path stroke-linecap="round" stroke-linejoin="round" d="M12 4.5v15m7.5-7.5h-15" />
</svg>
</a>
</div>
</td>
<td class="amount-dr py-1 px-1"></td>
<td class="amount-cr has-amount py-1 pl-1">
<div class="relative shadow-sm">
<div class="pointer-events-none absolute inset-y-0 left-0 flex items-center pl-3">
<span class="text-gray-500">$</span>
</div>
<input type="number" class="bordered-field pl-7" name="amount" step="0.01" oninput="changeAmount(this)">
</div>
</td>
</tr>
{% endif %} {% endif %}
</tbody> </tbody>
</table> </table>
@ -166,10 +183,13 @@
<input type="hidden" name="referrer" value="{{ request.referrer or '' }}"> <input type="hidden" name="referrer" value="{{ request.referrer or '' }}">
</form> </form>
{# Save HTML for template posting rows so we can access this from JS when required #}
<table style="display:none" id="template-dr">{{ template_dr() }}</table>
<table style="display:none" id="template-cr">{{ template_cr() }}</table>
{% endblock %} {% endblock %}
{% block scripts %} {% block scripts %}
{# TODO: This would be much easier with React or similar... #}
<script> <script>
function changeDrCr(el) { function changeDrCr(el) {
let trPosting = el.parentNode.parentNode.parentNode.parentNode.parentNode; let trPosting = el.parentNode.parentNode.parentNode.parentNode.parentNode;
@ -193,40 +213,9 @@
let trPosting = el.parentNode.parentNode.parentNode; let trPosting = el.parentNode.parentNode.parentNode;
let sign = trPosting.querySelector('select').value; // Use same sign as row clicked let sign = trPosting.querySelector('select').value; // Use same sign as row clicked
let inputAmount =
'<div class="relative shadow-sm">' +
'<div class="pointer-events-none absolute inset-y-0 left-0 flex items-center pl-3">' +
'<span class="text-gray-500">$</span>' +
'</div>' +
'<input type="number" class="bordered-field pl-7" name="amount" step="0.01" oninput="changeAmount(this)">' +
'</div>';
// Add new posting row // Add new posting row
let trNew = document.createElement('tr'); let trNew = document.createElement('tr');
trNew.innerHTML = trNew.innerHTML = document.getElementById(sign === 'dr' ? 'template-dr' : 'template-cr').querySelector('tr').innerHTML;
'<tr>' +
'<td></td>' +
'<td class="py-1 px-1" colspan="2">' +
'<div class="relative flex">' +
'<div class="relative flex flex-grow items-stretch shadow-sm">' +
'<div class="absolute inset-y-0 left-0 flex items-center">' +
'<select class="h-full border-0 bg-transparent py-0 pl-2 pr-8 text-gray-900 focus:ring-2 focus:ring-inset focus:ring-indigo-600" name="sign" onchange="changeDrCr(this)">' +
(sign === 'dr' ? '<option value="dr" selected>Dr</option><option value="cr">Cr</option>' : '<option value="dr">Dr</option><option value="cr" selected>Cr</option>') +
'</select>' +
'</div>' +
'<input type="text" class="bordered-field pl-16" name="account">' +
'</div>' +
'<a class="relative -ml-px px-2 py-2 text-gray-500 hover:text-gray-700" href="#" onclick="addPosting(this);return false;">' +
'<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-4 h-4">' +
'<path stroke-linecap="round" stroke-linejoin="round" d="M12 4.5v15m7.5-7.5h-15" />' +
'</svg>' +
'</a>' +
'</div>' +
'</td>' +
(sign === 'dr' ? ('<td class="amount-dr has-amount py-1 px-1">' + inputAmount + '</td>') : '<td class="amount-dr py-1 px-1"></td>') +
(sign === 'cr' ? ('<td class="amount-cr has-amount py-1 pl-1">' + inputAmount + '</td>') : '<td class="amount-cr py-1 pl-1"></td>') +
'</tr>';
trPosting.after(trNew); trPosting.after(trNew);
} }
@ -247,4 +236,5 @@
} }
} }
</script> </script>
<script src="{{ url_for('static', filename='js/combobox.js') }}"></script>
{% endblock %} {% endblock %}