06/02 Update functionalities

This commit is contained in:
2026-06-02 15:28:31 -04:00
parent c429de7bcd
commit 814ec90581
3 changed files with 62 additions and 7 deletions
+20 -1
View File
@@ -1,10 +1,13 @@
from datetime import date
from flask import Blueprint, render_template, redirect, url_for, flash, request, jsonify from flask import Blueprint, render_template, redirect, url_for, flash, request, jsonify
from flask_login import login_required from flask_login import login_required
from flask_wtf import FlaskForm from flask_wtf import FlaskForm
from wtforms import StringField, SelectField, TextAreaField, SubmitField from wtforms import StringField, SelectField, TextAreaField, SubmitField
from wtforms.validators import DataRequired, Length, Optional from wtforms.validators import DataRequired, Length, Optional
from sqlalchemy import func, extract
from app.extensions import db from app.extensions import db
from app.models.account import Account from app.models.account import Account
from app.models.transaction import Transaction
from app.services.account_service import calc_balance from app.services.account_service import calc_balance
accounts_bp = Blueprint('accounts', __name__, url_prefix='/accounts') accounts_bp = Blueprint('accounts', __name__, url_prefix='/accounts')
@@ -63,11 +66,27 @@ def index():
credit_accounts = [a for a in all_accounts if a.account_type == 'credit_card'] credit_accounts = [a for a in all_accounts if a.account_type == 'credit_card']
accounts = credit_accounts if tab == 'credit' else bank_accounts accounts = credit_accounts if tab == 'credit' else bank_accounts
# Monthly charges per credit card (current month expenses)
today = date.today()
monthly_charges = {}
if credit_accounts:
rows = db.session.query(
Transaction.account_id,
func.sum(Transaction.amount)
).filter(
Transaction.transaction_type == 'expense',
extract('year', Transaction.date) == today.year,
extract('month', Transaction.date) == today.month,
Transaction.account_id.in_([a.id for a in credit_accounts])
).group_by(Transaction.account_id).all()
monthly_charges = {row[0]: float(row[1]) for row in rows}
return render_template('accounts/index.html', return render_template('accounts/index.html',
accounts=accounts, accounts=accounts,
tab=tab, tab=tab,
bank_count=len(bank_accounts), bank_count=len(bank_accounts),
credit_count=len(credit_accounts)) credit_count=len(credit_accounts),
monthly_charges=monthly_charges)
@accounts_bp.route('/new', methods=['GET', 'POST']) @accounts_bp.route('/new', methods=['GET', 'POST'])
+8
View File
@@ -241,6 +241,14 @@ def transfer():
form.from_account_id.choices = _account_choices() form.from_account_id.choices = _account_choices()
form.to_account_id.choices = _account_choices() form.to_account_id.choices = _account_choices()
if request.method == 'GET':
prefill_to = request.args.get('to_account_id', '')
prefill_desc = request.args.get('description', '')
if prefill_to:
form.to_account_id.data = prefill_to
if prefill_desc:
form.description.data = prefill_desc
if form.validate_on_submit(): if form.validate_on_submit():
if form.from_account_id.data == form.to_account_id.data: if form.from_account_id.data == form.to_account_id.data:
flash('Source and destination accounts must be different.', 'warning') flash('Source and destination accounts must be different.', 'warning')
+34 -6
View File
@@ -27,6 +27,8 @@
{% for acct in accounts %} {% for acct in accounts %}
<div class="col-12 col-md-6 col-xl-4"> <div class="col-12 col-md-6 col-xl-4">
<div class="pcard" style="border-left: 4px solid {{ acct.color }};"> <div class="pcard" style="border-left: 4px solid {{ acct.color }};">
<!-- Header: icon + name + menu -->
<div class="d-flex justify-content-between align-items-start"> <div class="d-flex justify-content-between align-items-start">
<div class="d-flex align-items-center gap-2"> <div class="d-flex align-items-center gap-2">
<div style="width:36px;height:36px;border-radius:9px;background:{{ acct.color }}22;color:{{ acct.color }};display:flex;align-items:center;justify-content:center;font-size:18px;"> <div style="width:36px;height:36px;border-radius:9px;background:{{ acct.color }}22;color:{{ acct.color }};display:flex;align-items:center;justify-content:center;font-size:18px;">
@@ -52,33 +54,59 @@
</div> </div>
</div> </div>
{% if tab == 'credit' %}
<!-- Amount Owed + Monthly Charges -->
{% set owed = [0, -acct.balance] | max %}
{% set charges = monthly_charges.get(acct.id, 0) %}
<div class="d-flex gap-3 mt-3">
<div style="flex:1;background:#fff5f5;border-radius:8px;padding:10px 12px;">
<div style="font-size:10px;color:#991b1b;font-weight:600;text-transform:uppercase;letter-spacing:.04em;margin-bottom:2px;">Amount Owed</div>
<div class="mono {% if owed > 0 %}text-expense{% else %}" style="color:#10b981;{% endif %}font-size:20px;font-weight:700;">
{% if owed > 0 %}-{% endif %}{{ owed | currency }}
</div>
</div>
<div style="flex:1;background:#fafafa;border-radius:8px;padding:10px 12px;">
<div style="font-size:10px;color:var(--muted);font-weight:600;text-transform:uppercase;letter-spacing:.04em;margin-bottom:2px;">This Month</div>
<div class="mono text-expense" style="font-size:20px;font-weight:700;">
{% if charges > 0 %}-{% endif %}{{ charges | currency }}
</div>
</div>
</div>
{% endif %}
{% if acct.notes %} {% if acct.notes %}
<div style="font-size:12px;color:var(--muted);margin-top:10px;">{{ acct.notes }}</div> <div style="font-size:12px;color:var(--muted);margin-top:10px;">{{ acct.notes }}</div>
{% endif %} {% endif %}
<!-- Action buttons -->
{% if tab == 'credit' %} {% if tab == 'credit' %}
<div class="d-flex gap-2 mt-3"> <div class="d-flex gap-2 mt-3">
<a href="{{ url_for('transactions.transfer', to_account_id=acct.id, description='Credit Card Payment — ' ~ acct.name) }}"
class="btn btn-sm flex-fill btn-danger" style="font-size:12px;">
<i class="bi bi-send me-1"></i>Pay Card
</a>
<a href="{{ url_for('transactions.index', tab='expense', account_id=acct.id) }}" <a href="{{ url_for('transactions.index', tab='expense', account_id=acct.id) }}"
class="btn btn-sm flex-fill" style="font-size:11px;background:#fee2e2;color:#991b1b;border:none;"> class="btn btn-sm flex-fill btn-outline-secondary" style="font-size:12px;">
<i class="bi bi-arrow-up-circle me-1"></i>Charges <i class="bi bi-list-ul me-1"></i>Charges
</a> </a>
<a href="{{ url_for('transactions.new', type='expense') }}" <a href="{{ url_for('transactions.new', type='expense') }}"
class="btn btn-sm flex-fill" style="font-size:11px;background:#f1f5f9;color:#475569;border:none;"> class="btn btn-sm" style="font-size:12px;background:#f1f5f9;color:#475569;border:none;" title="Add Charge">
<i class="bi bi-plus-lg me-1"></i>Add Charge <i class="bi bi-plus-lg"></i>
</a> </a>
</div> </div>
{% else %} {% else %}
<div class="d-flex gap-2 mt-3"> <div class="d-flex gap-2 mt-3">
<a href="{{ url_for('transactions.index', tab='expense', account_id=acct.id) }}" <a href="{{ url_for('transactions.index', tab='expense', account_id=acct.id) }}"
class="btn btn-sm flex-fill" style="font-size:11px;background:#f1f5f9;color:#475569;border:none;"> class="btn btn-sm flex-fill btn-outline-secondary" style="font-size:12px;">
<i class="bi bi-list-ul me-1"></i>Transactions <i class="bi bi-list-ul me-1"></i>Transactions
</a> </a>
<a href="{{ url_for('transactions.new', type='income') }}" <a href="{{ url_for('transactions.new', type='income') }}"
class="btn btn-sm flex-fill" style="font-size:11px;background:#d1fae5;color:#065f46;border:none;"> class="btn btn-sm flex-fill" style="font-size:12px;background:#d1fae5;color:#065f46;border:none;">
<i class="bi bi-plus-lg me-1"></i>Add Income <i class="bi bi-plus-lg me-1"></i>Add Income
</a> </a>
</div> </div>
{% endif %} {% endif %}
</div> </div>
</div> </div>
{% endfor %} {% endfor %}