From 3debfb1122f6e5f5f158e78a3f96cd543d6dff2c Mon Sep 17 00:00:00 2001 From: RunasSudo Date: Sat, 24 Dec 2022 13:22:18 +1100 Subject: [PATCH] Allow charging statement line to multiple accounts --- drcr/models.py | 13 +++++ drcr/statements/models.py | 9 ++++ drcr/statements/views.py | 26 ++++++++-- .../statement_line_edit_transaction.html | 47 +++++++++++++++---- drcr/templates/statement_lines.html | 6 +-- drcr/webapp.py | 2 +- 6 files changed, 85 insertions(+), 18 deletions(-) diff --git a/drcr/models.py b/drcr/models.py index f4900cb..e70e989 100644 --- a/drcr/models.py +++ b/drcr/models.py @@ -21,6 +21,16 @@ class Transaction: self.dt = dt self.description = description self.postings = postings or [] + + def assert_valid(self): + """Assert that debits equal credits, and commodities are compatible""" + + if any(p.commodity != '$' for p in self.postings): + # FIXME: Allow non-$ commodities + raise AssertionError('Transaction contains multiple commodities') + + if sum(p.quantity for p in self.postings) != 0: + raise AssertionError('Transaction debits and credits do not balance') class Posting: def __init__(self, description=None, account=None, quantity=None, commodity=None): @@ -44,3 +54,6 @@ class Amount: def format(self): return '{0}{1:,.{dps}f}'.format(self.commodity, self.quantity / (10**AMOUNT_DPS), dps=AMOUNT_DPS) + + def quantity_string(self): + return '{:.{dps}f}'.format(self.quantity / (10**AMOUNT_DPS), dps=AMOUNT_DPS) diff --git a/drcr/statements/models.py b/drcr/statements/models.py index f713e69..780021d 100644 --- a/drcr/statements/models.py +++ b/drcr/statements/models.py @@ -54,6 +54,15 @@ class StatementLine(Base): Posting(account=unclassified_name, quantity=-self.quantity, commodity=self.commodity) ] ) + + def is_complex(self): + if len(self.postings) > 1: + return True + + if any(len(p.transaction.postings) > 2 for p in self.postings): + return True + + return False class StatementLineTransaction(Base, Transaction): __tablename__ = 'statement_line_transactions' diff --git a/drcr/statements/views.py b/drcr/statements/views.py index a41d467..e08bf53 100644 --- a/drcr/statements/views.py +++ b/drcr/statements/views.py @@ -16,6 +16,7 @@ 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 @@ -74,14 +75,31 @@ def statement_line_edit_transaction(): # Queue for deletion db_session.delete(posting.transaction) - transaction = StatementLineTransaction( - dt=statement_line.dt, - description=request.form['description'], - 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() diff --git a/drcr/templates/statement_line_edit_transaction.html b/drcr/templates/statement_line_edit_transaction.html index 0634b70..0696b7f 100644 --- a/drcr/templates/statement_line_edit_transaction.html +++ b/drcr/templates/statement_line_edit_transaction.html @@ -34,7 +34,7 @@ - + {{ statement_line.source_account }} {{ statement_line.dt.strftime('%Y-%m-%d') }} {{ statement_line.description }} @@ -59,26 +59,27 @@ {# FIXME: Customise date, etc. #} - {# FIXME: Complex transactions #} {{ statement_line.dt.strftime('%Y-%m-%d') }} - + + {# Source line #} - {{ 'Dr' if statement_line.quantity >= 0 else 'Cr' }} {{ statement_line.source_account }} - {{ statement_line.amount().format() if statement_line.quantity >= 0 else '' }} - {{ (statement_line.amount()|abs).format() if statement_line.quantity < 0 else '' }} + {{ 'Dr' if statement_line.quantity >= 0 else 'Cr' }} {{ statement_line.source_account }} {##} + {% if statement_line.quantity >= 0 %}{{ statement_line.amount().format() }}{% else %}{% endif %} + {% if statement_line.quantity < 0 %}{{ (statement_line.amount()|abs).format() }}{% else %}{% endif %} - + + {# Charge line #} - {{ 'Cr' if statement_line.quantity >= 0 else 'Dr' }} - {{ (statement_line.amount()|abs).format() if statement_line.quantity < 0 else '' }} - {{ statement_line.amount().format() if statement_line.quantity >= 0 else '' }} + {{ 'Cr' if statement_line.quantity >= 0 else 'Dr' }} + {% if statement_line.quantity < 0 %}{{ (statement_line.amount()|abs).format() }}{% else %}{% endif %} + {% if statement_line.quantity >= 0 %}{{ statement_line.amount().format() }}{% else %}{% endif %} @@ -88,4 +89,30 @@ + + {% endblock %} diff --git a/drcr/templates/statement_lines.html b/drcr/templates/statement_lines.html index 43f1dd9..679e178 100644 --- a/drcr/templates/statement_lines.html +++ b/drcr/templates/statement_lines.html @@ -40,10 +40,10 @@ {{ line.source_account }} {{ line.dt.strftime('%Y-%m-%d') }} {{ line.description }} - {% if line.postings|length > 1 %} - - {% elif line.postings|length == 0 %} + {% if line.postings|length == 0 %} Unclassified + {% elif line.is_complex() %} + (Complex) {% else %} {{ line.postings[0].transaction.charge_account(line.source_account) }} {% endif %} diff --git a/drcr/webapp.py b/drcr/webapp.py index 598fdda..e2afe63 100644 --- a/drcr/webapp.py +++ b/drcr/webapp.py @@ -36,7 +36,7 @@ def all_transactions(): @app.route('/general-ledger') def general_ledger(): - return render_template('general_ledger.html', transactions=all_transactions()) + return render_template('general_ledger.html', transactions=sorted(all_transactions(), key=lambda t: t.dt)) @app.route('/trial-balance') def trial_balance():