Files
WYGIWYH/app/templates/accounts/fragments/account_reconciliation.html
T
Herculino Trotta fcd666946f feat(accounts): reconcile balances against a date or reference month cutoff
Credit cards often have paid transactions dated after the invoice being
closed, which made reconciliation against all paid transactions wrong.

The reconciliation panel now lets you balance against all paid
transactions (previous behavior), paid transactions up to a date, or
paid transactions up to a reference month. The adjustment transaction is
dated at the cutoff so later reconciliations with the same or a later
cutoff include it. An invalid cutoff blocks the reconciliation instead
of silently falling back.
2026-10-31 17:56:36 -03:00

113 lines
5.4 KiB
HTML

{% extends 'extends/offcanvas.html' %}
{% load currency_display %}
{% load i18n %}
{% load crispy_forms_tags %}
{% block title %}{% translate 'Account Reconciliation' %}{% endblock %}
{% block body %}
<form hx-post="{% url 'account_reconciliation' %}">
{% csrf_token %}
{{ form.management_form }}
{# Picking a cutoff shows its field and re-fetches only the accounts list, with balances computed against it #}
<div class="mb-3"
hx-get="{% url 'account_reconciliation' %}"
hx-trigger="change"
hx-target="#reconciliation-accounts"
hx-select="#balanceAccordionFlush"
hx-include="this"
_="on change
set mode to (the first <input[name='cutoff-mode']:checked/> in me).value
for wrapper in <[data-cutoff-mode]/> in me
if wrapper's @data-cutoff-mode is mode
remove .hidden from wrapper
else
add .hidden to wrapper
end
end">
<div class="join w-full mb-3" role="group" aria-label="{{ cutoff.mode.label }}">
{% for value, label in cutoff.fields.mode.choices %}
<input type="radio"
class="join-item btn btn-primary btn-outline w-1/3"
name="{{ cutoff.mode.html_name }}"
id="{{ cutoff.mode.html_name }}_{{ forloop.counter }}"
value="{{ value }}"
aria-label="{{ label }}"
{% if value == cutoff.selected_mode %}checked{% endif %}>
{% endfor %}
</div>
<div data-cutoff-mode="date" {% if cutoff.selected_mode != "date" %}class="hidden"{% endif %}>
{{ cutoff.date|as_crispy_field }}
</div>
<div data-cutoff-mode="month" {% if cutoff.selected_mode != "month" %}class="hidden"{% endif %}>
{{ cutoff.month|as_crispy_field }}
</div>
</div>
{# Swapped rows bring fresh selects; the offcanvas only initializes TomSelect on first load #}
<div id="reconciliation-accounts"
_="on htmx:afterSettle
for x in <select/> in me
if not x.tomselect
remove .select from x
js(x) TomSelect(x) end
end
end">
<div class="join join-vertical w-full" id="balanceAccordionFlush">
{% for form in form.forms %}
<c-ui.components.collapse>
<c-slot name="title">
{% if form.account_group %}<span class="badge badge-primary badge-outline me-2">{{ form.account_group.name }}</span>{% endif %}{{ form.account_name }}
</c-slot>
<c-slot name="content">
<div class="fieldset">
<span class="fieldset-legend">{% translate 'Current balance' %}</span>
<div data-amount="{{ form.current_balance|floatformat:"-40u" }}"
data-decimal-places="{{ form.currency_decimal_places }}"
id="amount-{{ forloop.counter0 }}" class="text-base">
<c-amount.display
:amount="form.current_balance"
:prefix="form.currency_prefix"
:suffix="form.currency_suffix"
:decimal_places="form.currency_decimal_places"
color="auto"></c-amount.display>
</div>
</div>
<div>
{% crispy form %}
</div>
<div class="fieldset">
<span class="fieldset-legend">{% translate 'Difference' %}</span>
<div class="text-base"
_="on input from #id_form-{{ forloop.counter0 }}-new_balance
set original_amount to parseFloat('{{ form.current_balance|floatformat:"-40u" }}')
then set prefix to '{{ form.currency_prefix }}'
then set suffix to '{{ form.currency_suffix }}'
then set decimal_places to {{ form.currency_decimal_places }}
then call parseLocaleNumber(#id_form-{{ forloop.counter0 }}-new_balance.value)
then set new_amount to result
then set diff to (Math.round((new_amount - original_amount) * Math.pow(10, decimal_places))) / Math.pow(10, decimal_places)
then set format_new_amount to
Intl.NumberFormat(
undefined,
{
minimumFractionDigits: decimal_places,
maximumFractionDigits: decimal_places,
roundingMode: 'trunc'
}
).format(diff)
then set formatted_string to `${prefix}${format_new_amount}${suffix}`
then put formatted_string into me if diff else put '-' into me">-</div>
</div>
</c-slot>
</c-ui.components.collapse>
{% endfor %}
</div>
</div>
<div class="mt-3">
<div>
<input type="submit" name="submit" value="{% translate 'Reconcile balances' %}" class="btn btn-primary w-full" id="submit-id-submit">
</div>
</div>
</form>
{% endblock %}