Allow reconciling two statement lines together as a transfer between accounts

This commit is contained in:
RunasSudo 2022-12-24 16:16:47 +11:00
parent 801d70c6e9
commit 77d23cb7b4
Signed by: RunasSudo
GPG Key ID: 7234E476BF21C61A
3 changed files with 82 additions and 47 deletions

View File

@ -17,7 +17,7 @@
from sqlalchemy import Column, DateTime, ForeignKey, Integer, String from sqlalchemy import Column, DateTime, ForeignKey, Integer, String
from sqlalchemy.orm import relationship from sqlalchemy.orm import relationship
from ..database import Base from ..database import Base, db_session
from ..models import Amount, Posting, Transaction from ..models import Amount, Posting, Transaction
class StatementLine(Base): class StatementLine(Base):
@ -63,6 +63,13 @@ class StatementLine(Base):
return True return True
return False return False
def delete_postings(self):
"""Delete existing StatementLineTransactions with postings for this StatementLine"""
for posting in self.postings:
# TODO: Will be wonky if transaction covers multiple StatementLines
db_session.delete(posting.transaction)
class StatementLineTransaction(Base, Transaction): class StatementLineTransaction(Base, Transaction):
__tablename__ = 'statement_line_transactions' __tablename__ = 'statement_line_transactions'

View File

@ -37,14 +37,7 @@ def statement_line_charge():
abort(404) abort(404)
# Delete existing postings # Delete existing postings
if len(statement_line.postings) > 0: statement_line.delete_postings()
for posting in statement_line.postings:
if len(posting.transaction.postings) > 2:
# Complex posting
raise Exception('Cannot automatically delete a StatementLineTransaction with >2 postings')
# Queue for deletion
db_session.delete(posting.transaction)
transaction = StatementLineTransaction( transaction = StatementLineTransaction(
dt=statement_line.dt, dt=statement_line.dt,
@ -78,10 +71,7 @@ def statement_line_edit_transaction():
return render_template('statements/statement_line_edit_transaction.html', statement_line=statement_line, transaction=statement_line.postings[0].transaction) return render_template('statements/statement_line_edit_transaction.html', statement_line=statement_line, transaction=statement_line.postings[0].transaction)
# Delete existing postings # Delete existing postings
if len(statement_line.postings) > 0: statement_line.delete_postings()
for posting in statement_line.postings:
# Queue for deletion
db_session.delete(posting.transaction)
if len(request.form.getlist('charge-account')) == 1: if len(request.form.getlist('charge-account')) == 1:
# Simple transaction # Simple transaction
@ -113,3 +103,33 @@ def statement_line_edit_transaction():
db_session.commit() db_session.commit()
return redirect('/statement-lines') 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')

View File

@ -22,41 +22,49 @@
<div class="container"> <div class="container">
<h1 class="h2 mt-4 mb-4">Statement lines</h1> <h1 class="h2 mt-4 mb-4">Statement lines</h1>
<table class="table"> <form method="POST">
<thead> <div class="mb-2">
<tr> <button type="submit" class="btn btn-outline-secondary" formaction="/statement-lines/reconcile-transfer">Reconcile selected as transfer</button>
<th>Source account</th> </div>
<th>Date</th>
<th>Description</th> <table class="table">
<th>Charged to</th> <thead>
<th>Dr</th> <tr>
<th>Cr</th> <th></th>
<th>Balance</th> <th>Source account</th>
</tr> <th>Date</th>
</thead> <th>Description</th>
<tbody> <th>Charged to</th>
{% for line in statement_lines %} <th>Dr</th>
<tr data-line-id="{{ line.id }}"> <th>Cr</th>
<td>{{ line.source_account }}</td> <th>Balance</th>
<td>{{ line.dt.strftime('%Y-%m-%d') }}</td>
<td>{{ line.description }}</td>
<td class="charge-account">
{% if line.postings|length == 0 %}
<a href="#" class="text-danger" onclick="classifyLine({{ line.id }});return false;">Unclassified</a>
{% elif line.is_complex() %}
<i>(Complex)</i>
{% else %}
<a href="#" class="text-body" onclick="classifyLine({{ line.id }});return false;">{{ line.postings[0].transaction.charge_account(line.source_account) }}</a>
{% endif %}
<a href="/statement-lines/edit-transaction?line-id={{ line.id }}" class="text-muted"><i class="bi bi-pencil"></i></a>
</td>
<td>{{ line.amount().format() if line.quantity >= 0 else '' }}</td>
<td>{{ (line.amount()|abs).format() if line.quantity < 0 else '' }}</td>
<td>{{ line.balance or '' }}</td>
</tr> </tr>
{% endfor %} </thead>
</tbody> <tbody>
</table> {% for line in statement_lines %}
<tr data-line-id="{{ line.id }}">
<td><input type="checkbox" name="sel-line-id" value="{{ line.id }}"></td>
<td>{{ line.source_account }}</td>
<td>{{ line.dt.strftime('%Y-%m-%d') }}</td>
<td>{{ line.description }}</td>
<td class="charge-account">
{% if line.postings|length == 0 %}
<a href="#" class="text-danger" onclick="classifyLine({{ line.id }});return false;">Unclassified</a>
{% elif line.is_complex() %}
<i>(Complex)</i>
{% else %}
<a href="#" class="text-body" onclick="classifyLine({{ line.id }});return false;">{{ line.postings[0].transaction.charge_account(line.source_account) }}</a>
{% endif %}
<a href="/statement-lines/edit-transaction?line-id={{ line.id }}" class="text-muted"><i class="bi bi-pencil"></i></a>
</td>
<td>{{ line.amount().format() if line.quantity >= 0 else '' }}</td>
<td>{{ (line.amount()|abs).format() if line.quantity < 0 else '' }}</td>
<td>{{ line.balance or '' }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</form>
</div> </div>
<script> <script>