06/02 Update account balance adjustment

This commit is contained in:
2026-06-02 15:40:42 -04:00
parent 814ec90581
commit 4e47eddf70
3 changed files with 164 additions and 0 deletions
+56
View File
@@ -137,3 +137,59 @@ def delete(id):
db.session.commit() db.session.commit()
flash(f'Account "{account.name}" removed.', 'info') flash(f'Account "{account.name}" removed.', 'info')
return redirect(url_for('accounts.index')) return redirect(url_for('accounts.index'))
@accounts_bp.route('/<int:id>/adjust', methods=['GET', 'POST'])
@login_required
def adjust_balance(id):
account = db.get_or_404(Account, id)
calc_balance(account.id)
db.session.refresh(account)
current_balance = float(account.balance)
if request.method == 'POST':
from app.models.transaction import Transaction
from app.models.category import Category
from wtforms import ValidationError
try:
new_balance = float(request.form.get('new_balance', '').replace(',', ''))
except (ValueError, AttributeError):
flash('Invalid amount entered.', 'danger')
return redirect(url_for('accounts.adjust_balance', id=id))
difference = round(new_balance - current_balance, 2)
if difference == 0:
flash('Balance is already at that amount — no adjustment needed.', 'info')
return redirect(url_for('accounts.index',
tab='credit' if account.account_type == 'credit_card' else 'bank'))
txn_type = 'income' if difference > 0 else 'expense'
amount = abs(difference)
notes = request.form.get('notes', '').strip() or 'Manual balance adjustment'
txn = Transaction(
account_id=account.id,
transaction_type=txn_type,
amount=amount,
description='Balance Adjustment',
date=date.today(),
notes=notes,
)
db.session.add(txn)
db.session.commit()
calc_balance(account.id)
flash(
f'Balance adjusted by '
f'{"+" if difference > 0 else ""}{difference:,.2f}. '
f'New balance: {new_balance:,.2f}.',
'success'
)
return redirect(url_for('accounts.index',
tab='credit' if account.account_type == 'credit_card' else 'bank'))
return render_template('accounts/adjust.html',
account=account,
current_balance=current_balance)
+107
View File
@@ -0,0 +1,107 @@
{% extends "base.html" %}
{% block title %}Adjust Balance — {{ account.name }}{% endblock %}
{% block page_title %}Adjust Balance{% endblock %}
{% block content %}
<div class="row justify-content-center">
<div class="col-12 col-md-7 col-lg-5">
<div class="pcard mb-3" style="border-left: 4px solid {{ account.color }};">
<div class="d-flex align-items-center gap-2">
<div style="width:36px;height:36px;border-radius:9px;background:{{ account.color }}22;color:{{ account.color }};display:flex;align-items:center;justify-content:center;font-size:18px;">
<i class="bi {{ account.icon }}"></i>
</div>
<div>
<div style="font-weight:600;font-size:14px;">{{ account.name }}</div>
<div style="font-size:11px;color:var(--muted);">{{ account.account_type | replace('_',' ') | title }}</div>
</div>
</div>
<div class="mt-3 d-flex gap-3">
<div>
<div style="font-size:11px;color:var(--muted);font-weight:600;text-transform:uppercase;letter-spacing:.04em;">Current Balance</div>
<div class="mono {% if current_balance >= 0 %}text-income{% else %}text-expense{% endif %}" style="font-size:22px;font-weight:700;">
{{ current_balance | abs | currency }}{% if current_balance < 0 %} <span style="font-size:13px;font-weight:400;">(owed)</span>{% endif %}
</div>
</div>
</div>
</div>
<div class="pcard" style="border-top: 4px solid {{ account.color }};">
<p style="font-size:13px;color:var(--muted);">
Enter the correct balance. An adjustment transaction will be created for the difference to bring the account in line.
</p>
<form method="POST" novalidate id="adjustForm">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
<div class="mb-3">
<label class="form-label fw-medium" style="font-size:13px;">Correct Balance</label>
<div class="input-group">
<span class="input-group-text" style="font-size:13px;">{{ current_user.currency_symbol }}</span>
<input type="number" name="new_balance" id="newBalance" class="form-control"
step="0.01" placeholder="0.00" autofocus
style="font-size:15px;font-weight:600;">
</div>
</div>
<!-- Live difference preview -->
<div id="diffPreview" class="mb-3 p-3" style="border-radius:8px;background:#f8fafc;display:none;">
<div style="font-size:12px;color:var(--muted);margin-bottom:4px;">Adjustment that will be created</div>
<div id="diffText" style="font-size:14px;font-weight:600;"></div>
</div>
<div class="mb-4">
<label class="form-label fw-medium" style="font-size:13px;">Reason <span style="font-weight:400;color:var(--muted);">(optional)</span></label>
<input type="text" name="notes" class="form-control" style="font-size:13px;"
placeholder="e.g. Bank reconciliation, opening balance">
</div>
<div class="d-flex gap-2">
<button type="submit" class="btn btn-primary" id="submitBtn" disabled>Apply Adjustment</button>
<a href="{{ url_for('accounts.index', tab='credit' if account.account_type == 'credit_card' else 'bank') }}"
class="btn btn-outline-secondary">Cancel</a>
</div>
</form>
</div>
</div>
</div>
{% endblock %}
{% block extra_js %}
<script>
const currentBalance = {{ current_balance }};
const symbol = '{{ current_user.currency_symbol }}';
document.getElementById('newBalance').addEventListener('input', function () {
const val = parseFloat(this.value);
const preview = document.getElementById('diffPreview');
const diffText = document.getElementById('diffText');
const submitBtn = document.getElementById('submitBtn');
if (isNaN(val)) {
preview.style.display = 'none';
submitBtn.disabled = true;
return;
}
const diff = Math.round((val - currentBalance) * 100) / 100;
if (diff === 0) {
preview.style.background = '#f8fafc';
diffText.style.color = '#64748b';
diffText.textContent = 'No change — balance is already at this amount.';
submitBtn.disabled = true;
} else {
const isIncome = diff > 0;
preview.style.background = isIncome ? '#f0fdf4' : '#fef2f2';
diffText.style.color = isIncome ? '#10b981' : '#ef4444';
const sign = isIncome ? '+' : '-';
const absVal = Math.abs(diff).toFixed(2).replace(/\B(?=(\d{3})+(?!\d))/g, ',');
const type = isIncome ? 'Income (credit)' : 'Expense (debit)';
diffText.textContent = `${type}: ${sign}${symbol}${absVal}`;
submitBtn.disabled = false;
}
preview.style.display = 'block';
});
</script>
{% endblock %}
+1
View File
@@ -43,6 +43,7 @@
<button class="btn btn-sm" style="background:none;border:none;color:var(--muted);padding:2px 6px;" data-bs-toggle="dropdown"><i class="bi bi-three-dots"></i></button> <button class="btn btn-sm" style="background:none;border:none;color:var(--muted);padding:2px 6px;" data-bs-toggle="dropdown"><i class="bi bi-three-dots"></i></button>
<ul class="dropdown-menu dropdown-menu-end" style="font-size:13px;"> <ul class="dropdown-menu dropdown-menu-end" style="font-size:13px;">
<li><a class="dropdown-item" href="{{ url_for('accounts.edit', id=acct.id) }}"><i class="bi bi-pencil me-2"></i>Edit</a></li> <li><a class="dropdown-item" href="{{ url_for('accounts.edit', id=acct.id) }}"><i class="bi bi-pencil me-2"></i>Edit</a></li>
<li><a class="dropdown-item" href="{{ url_for('accounts.adjust_balance', id=acct.id) }}"><i class="bi bi-sliders me-2"></i>Adjust Balance</a></li>
<li><hr class="dropdown-divider"></li> <li><hr class="dropdown-divider"></li>
<li> <li>
<form method="POST" action="{{ url_for('accounts.delete', id=acct.id) }}" onsubmit="return confirm('Remove this account?')"> <form method="POST" action="{{ url_for('accounts.delete', id=acct.id) }}" onsubmit="return confirm('Remove this account?')">