# DrCr: Web-based double-entry bookkeeping framework # Copyright (C) 2022 Lee Yingtong Li (RunasSudo) # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Affero General Public License for more details. # # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . from flask import abort, redirect, render_template, request from .. import AMOUNT_DPS from ..database import db_session from ..webapp import app from .models import StatementLine, StatementLinePosting, StatementLineTransaction @app.route('/statement-lines') def statement_lines(): if 'account' in request.args: statement_lines = StatementLine.query.filter_by(source_account=request.args['account']).all() else: statement_lines = StatementLine.query.all() return render_template('statements/statement_lines.html', statement_lines=sorted(statement_lines, key=lambda l: l.dt)) @app.route('/statement-lines/charge', methods=['POST']) def statement_line_charge(): statement_line = db_session.get(StatementLine, request.form['line-id']) if not statement_line: abort(404) # Delete existing postings statement_line.delete_postings() transaction = StatementLineTransaction( dt=statement_line.dt, description=statement_line.description, postings=[ StatementLinePosting(statement_line=statement_line, account=statement_line.source_account, quantity=statement_line.quantity, commodity=statement_line.commodity), StatementLinePosting(account=request.form['charge-account'], quantity=-statement_line.quantity, commodity=statement_line.commodity) ] ) db_session.add(transaction) db_session.commit() return 'OK' @app.route('/statement-lines/edit-transaction', methods=['GET', 'POST']) def statement_line_edit_transaction(): statement_line = db_session.get(StatementLine, request.args['line-id']) if not statement_line: abort(404) if request.method == 'GET': if len(statement_line.postings) == 0: return render_template('statements/statement_line_edit_transaction.html', statement_line=statement_line, transaction=None) if len(statement_line.postings) > 1: # NYI raise Exception('Cannot display a StatementLine with >2 postings') # Get existing transaction return render_template('statements/statement_line_edit_transaction.html', statement_line=statement_line, transaction=statement_line.postings[0].transaction) # Delete existing postings statement_line.delete_postings() if len(request.form.getlist('charge-account')) == 1: # Simple transaction postings = [ StatementLinePosting(statement_line=statement_line, account=statement_line.source_account, quantity=statement_line.quantity, commodity=statement_line.commodity), StatementLinePosting(account=request.form['charge-account'], quantity=-statement_line.quantity, commodity=statement_line.commodity) ] else: # Complex transaction, multiple postings postings = [ StatementLinePosting(statement_line=statement_line, account=statement_line.source_account, quantity=statement_line.quantity, commodity=statement_line.commodity) ] for charge_account, charge_amount_str in zip(request.form.getlist('charge-account'), request.form.getlist('charge-amount')): charge_quantity = round(float(charge_amount_str) * (10**AMOUNT_DPS)) if statement_line.quantity >= 0: # If source is debit, charge is credit charge_quantity = -charge_quantity postings.append(StatementLinePosting(account=charge_account, quantity=charge_quantity, commodity=statement_line.commodity)) transaction = StatementLineTransaction( dt=statement_line.dt, description=request.form['description'], postings=postings ) transaction.assert_valid() db_session.add(transaction) db_session.commit() return redirect('/statement-lines') @app.route('/statement-lines/reconcile-transfer', methods=['POST']) def statement_line_reconcile_transfer(): line_ids = request.form.getlist('sel-line-id') if len(line_ids) != 2: raise Exception('Must select exactly 2 statement lines') line1 = db_session.get(StatementLine, line_ids[0]) line2 = db_session.get(StatementLine, line_ids[1]) # Check same amount if line1.quantity != -line2.quantity or line1.commodity != line2.commodity: raise Exception('Selected statement line debits/credits must equal') # Delete existing postings line1.delete_postings() line2.delete_postings() transaction = StatementLineTransaction( dt=line1.dt, description=line1.description, postings=[ StatementLinePosting(statement_line=line1, description=line1.description, account=line1.source_account, quantity=line1.quantity, commodity=line1.commodity), StatementLinePosting(statement_line=line2, description=line2.description, account=line2.source_account, quantity=line2.quantity, commodity=line2.commodity) ] ) db_session.add(transaction) db_session.commit() return redirect('/statement-lines')