106 lines
3.7 KiB
Python
106 lines
3.7 KiB
Python
from flask import Blueprint, render_template, redirect, url_for, flash, request, jsonify
|
|
from flask_login import login_required
|
|
from flask_wtf import FlaskForm
|
|
from wtforms import StringField, SelectField, TextAreaField, SubmitField
|
|
from wtforms.validators import DataRequired, Length, Optional
|
|
from app.extensions import db
|
|
from app.models.account import Account
|
|
from app.services.account_service import calc_balance
|
|
|
|
accounts_bp = Blueprint('accounts', __name__, url_prefix='/accounts')
|
|
|
|
ACCOUNT_TYPES = [
|
|
('checking', 'Checking'),
|
|
('savings', 'Savings'),
|
|
('cash', 'Cash'),
|
|
('credit_card', 'Credit Card'),
|
|
('crypto', 'Crypto Wallet'),
|
|
('investment', 'Investment'),
|
|
('other', 'Other'),
|
|
]
|
|
|
|
ACCOUNT_ICONS = [
|
|
('bi-bank', 'Bank'),
|
|
('bi-wallet2', 'Wallet'),
|
|
('bi-cash-stack', 'Cash'),
|
|
('bi-credit-card', 'Credit Card'),
|
|
('bi-currency-bitcoin', 'Crypto'),
|
|
('bi-graph-up', 'Investment'),
|
|
('bi-safe', 'Safe'),
|
|
]
|
|
|
|
ACCOUNT_COLORS = [
|
|
'#4F81C7', '#10B981', '#F59E0B', '#EF4444',
|
|
'#8B5CF6', '#EC4899', '#06B6D4', '#64748B',
|
|
]
|
|
|
|
|
|
class AccountForm(FlaskForm):
|
|
name = StringField('Account Name', validators=[DataRequired(), Length(1, 100)])
|
|
account_type = SelectField('Type', choices=ACCOUNT_TYPES, validators=[DataRequired()])
|
|
color = StringField('Color', default='#4F81C7')
|
|
icon = StringField('Icon', default='bi-bank')
|
|
notes = TextAreaField('Notes', validators=[Optional(), Length(max=500)])
|
|
submit = SubmitField('Save')
|
|
|
|
|
|
@accounts_bp.route('/')
|
|
@login_required
|
|
def index():
|
|
accounts = Account.query.filter_by(is_active=True).order_by(Account.name).all()
|
|
# Recalc balances on page load
|
|
for a in accounts:
|
|
calc_balance(a.id)
|
|
accounts = Account.query.filter_by(is_active=True).order_by(Account.name).all()
|
|
return render_template('accounts/index.html', accounts=accounts)
|
|
|
|
|
|
@accounts_bp.route('/new', methods=['GET', 'POST'])
|
|
@login_required
|
|
def new():
|
|
form = AccountForm()
|
|
if form.validate_on_submit():
|
|
account = Account(
|
|
name=form.name.data.strip(),
|
|
account_type=form.account_type.data,
|
|
color=form.color.data or '#4F81C7',
|
|
icon=form.icon.data or 'bi-bank',
|
|
notes=form.notes.data,
|
|
balance=0,
|
|
)
|
|
db.session.add(account)
|
|
db.session.commit()
|
|
flash(f'Account "{account.name}" created.', 'success')
|
|
return redirect(url_for('accounts.index'))
|
|
return render_template('accounts/form.html', form=form, title='New Account',
|
|
colors=ACCOUNT_COLORS, icons=ACCOUNT_ICONS)
|
|
|
|
|
|
@accounts_bp.route('/<int:id>/edit', methods=['GET', 'POST'])
|
|
@login_required
|
|
def edit(id):
|
|
account = db.get_or_404(Account, id)
|
|
form = AccountForm(obj=account)
|
|
if form.validate_on_submit():
|
|
account.name = form.name.data.strip()
|
|
account.account_type = form.account_type.data
|
|
account.color = form.color.data or account.color
|
|
account.icon = form.icon.data or account.icon
|
|
account.notes = form.notes.data
|
|
db.session.commit()
|
|
flash(f'Account "{account.name}" updated.', 'success')
|
|
return redirect(url_for('accounts.index'))
|
|
return render_template('accounts/form.html', form=form, title='Edit Account',
|
|
account=account, colors=ACCOUNT_COLORS, icons=ACCOUNT_ICONS)
|
|
|
|
|
|
@accounts_bp.route('/<int:id>/delete', methods=['POST'])
|
|
@login_required
|
|
def delete(id):
|
|
account = db.get_or_404(Account, id)
|
|
# Soft delete
|
|
account.is_active = False
|
|
db.session.commit()
|
|
flash(f'Account "{account.name}" removed.', 'info')
|
|
return redirect(url_for('accounts.index'))
|