Recompute running balances when required
This commit is contained in:
parent
e313758192
commit
16534fc755
@ -18,7 +18,7 @@ from flask import abort, redirect, render_template, request, url_for
|
|||||||
|
|
||||||
from .. import AMOUNT_DPS
|
from .. import AMOUNT_DPS
|
||||||
from ..database import db
|
from ..database import db
|
||||||
from ..models import Amount, Posting, Transaction, TrialBalancer, reporting_commodity
|
from ..models import Amount, Posting, Transaction, TrialBalancer, queue_invalidate_running_balances, reporting_commodity
|
||||||
from ..webapp import all_accounts, all_transactions, app
|
from ..webapp import all_accounts, all_transactions, app
|
||||||
from .models import BalanceAssertion
|
from .models import BalanceAssertion
|
||||||
from ..statements.models import StatementLineReconciliation
|
from ..statements.models import StatementLineReconciliation
|
||||||
@ -59,9 +59,12 @@ def journal_new_transaction():
|
|||||||
)
|
)
|
||||||
transaction.postings.append(posting)
|
transaction.postings.append(posting)
|
||||||
|
|
||||||
transaction.assert_valid()
|
# Invalidate future running balances
|
||||||
|
queue_invalidate_running_balances(account, transaction.dt)
|
||||||
|
|
||||||
|
transaction.assert_valid()
|
||||||
db.session.add(transaction)
|
db.session.add(transaction)
|
||||||
|
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
|
||||||
return redirect(request.form.get('referrer', '') or url_for('journal'))
|
return redirect(request.form.get('referrer', '') or url_for('journal'))
|
||||||
@ -104,6 +107,9 @@ def journal_edit_transaction():
|
|||||||
)
|
)
|
||||||
new_postings.append(posting)
|
new_postings.append(posting)
|
||||||
|
|
||||||
|
# Invalidate future running balances
|
||||||
|
queue_invalidate_running_balances(account, transaction.dt)
|
||||||
|
|
||||||
# Fix up reconciliations
|
# Fix up reconciliations
|
||||||
for old_posting in transaction.postings:
|
for old_posting in transaction.postings:
|
||||||
for reconciliation in StatementLineReconciliation.query.filter(StatementLineReconciliation.posting == old_posting):
|
for reconciliation in StatementLineReconciliation.query.filter(StatementLineReconciliation.posting == old_posting):
|
||||||
|
@ -70,15 +70,26 @@ class Posting(db.Model):
|
|||||||
|
|
||||||
transaction = db.relationship('Transaction', back_populates='postings')
|
transaction = db.relationship('Transaction', back_populates='postings')
|
||||||
|
|
||||||
def __init__(self, description=None, account=None, quantity=None, commodity=None):
|
def __init__(self, description=None, account=None, quantity=None, commodity=None, running_balance=None):
|
||||||
self.description = description
|
self.description = description
|
||||||
self.account = account
|
self.account = account
|
||||||
self.quantity = quantity
|
self.quantity = quantity
|
||||||
self.commodity = commodity
|
self.commodity = commodity
|
||||||
|
self.running_balance = running_balance
|
||||||
|
|
||||||
def amount(self):
|
def amount(self):
|
||||||
return Amount(self.quantity, self.commodity)
|
return Amount(self.quantity, self.commodity)
|
||||||
|
|
||||||
|
def queue_invalidate_running_balances(account, dt_from):
|
||||||
|
"""
|
||||||
|
Invalidate running_balances for Postings in the specified account, from the given date onwards
|
||||||
|
|
||||||
|
NOTE: Does not call db.session.commit()
|
||||||
|
"""
|
||||||
|
|
||||||
|
for posting in db.session.scalars(db.select(Posting).join(Posting.transaction).where((Transaction.dt >= dt_from) & (Posting.account == account))).all():
|
||||||
|
posting.running_balance = None
|
||||||
|
|
||||||
class Amount:
|
class Amount:
|
||||||
__slots__ = ['quantity', 'commodity']
|
__slots__ = ['quantity', 'commodity']
|
||||||
|
|
||||||
@ -204,6 +215,26 @@ class TrialBalancer:
|
|||||||
def from_cached(cls, start_date=None, end_date=None):
|
def from_cached(cls, start_date=None, end_date=None):
|
||||||
"""Obtain a TrialBalancer based on the cached running_balance"""
|
"""Obtain a TrialBalancer based on the cached running_balance"""
|
||||||
|
|
||||||
|
# First, recompute any running_balance if required
|
||||||
|
stale_accounts = db.session.scalars('SELECT DISTINCT account FROM postings WHERE running_balance IS NULL').all()
|
||||||
|
if stale_accounts:
|
||||||
|
# Get all relevant Postings in database in correct order
|
||||||
|
# FIXME: Recompute balances only from the last non-stale balance to be more efficient
|
||||||
|
postings = db.session.scalars(db.select(Posting).join(Posting.transaction).where(Posting.account.in_(stale_accounts)).order_by(Transaction.dt, Transaction.id)).all()
|
||||||
|
|
||||||
|
accounts = {}
|
||||||
|
|
||||||
|
for posting in postings:
|
||||||
|
if posting.account not in accounts:
|
||||||
|
accounts[posting.account] = Amount(0, reporting_commodity())
|
||||||
|
|
||||||
|
# FIXME: Handle commodities better (ensure compatible commodities)
|
||||||
|
accounts[posting.account].quantity += posting.amount().as_cost().quantity
|
||||||
|
|
||||||
|
posting.running_balance = accounts[posting.account].quantity
|
||||||
|
|
||||||
|
db.session.commit()
|
||||||
|
|
||||||
if start_date is not None:
|
if start_date is not None:
|
||||||
result_start_date = cls()
|
result_start_date = cls()
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user