Compare commits
3 Commits
e25855450c
...
56fa6f3633
Author | SHA1 | Date | |
---|---|---|---|
56fa6f3633 | |||
97893a9c12 | |||
541086d346 |
@ -18,7 +18,7 @@ from flask import render_template, url_for
|
|||||||
|
|
||||||
from drcr.models import AccountConfiguration, Posting, Transaction, TrialBalancer, reporting_commodity
|
from drcr.models import AccountConfiguration, Posting, Transaction, TrialBalancer, reporting_commodity
|
||||||
from drcr.database import db
|
from drcr.database import db
|
||||||
from drcr.webapp import eofy_date
|
from drcr.webapp import eofy_date, sofy_date
|
||||||
import drcr.plugins
|
import drcr.plugins
|
||||||
|
|
||||||
from . import views # Load routes
|
from . import views # Load routes
|
||||||
@ -49,25 +49,32 @@ def plugin_init():
|
|||||||
drcr.plugins.transaction_providers.append(make_tax_transactions)
|
drcr.plugins.transaction_providers.append(make_tax_transactions)
|
||||||
|
|
||||||
@assert_aud
|
@assert_aud
|
||||||
def make_tax_transactions(start_date=None, end_date=None):
|
def make_tax_transactions(transactions, start_date=None, end_date=None, api_stage_until=None):
|
||||||
|
if api_stage_until is not None and api_stage_until < 300:
|
||||||
|
return transactions
|
||||||
|
|
||||||
# Get EOFY date
|
# Get EOFY date
|
||||||
dt = eofy_date()
|
dt = eofy_date()
|
||||||
|
|
||||||
if (start_date is not None and start_date > dt) or (end_date is not None and end_date < dt):
|
if (start_date is not None and start_date > dt) or (end_date is not None and end_date < dt):
|
||||||
return []
|
return transactions
|
||||||
|
|
||||||
report = tax_summary_report()
|
# Get trial balance
|
||||||
|
balancer = TrialBalancer.from_cached(start_date=sofy_date(), end_date=dt)
|
||||||
|
balancer.apply_transactions(transactions)
|
||||||
|
|
||||||
|
report = tax_summary_report(balancer)
|
||||||
tax_amount = report.by_id('total_tax').amount - report.by_id('offsets').amount
|
tax_amount = report.by_id('total_tax').amount - report.by_id('offsets').amount
|
||||||
|
|
||||||
# Estimated tax payable
|
# Estimated tax payable
|
||||||
transactions = [Transaction(
|
transactions.append(Transaction(
|
||||||
dt=dt,
|
dt=dt,
|
||||||
description='Estimated income tax',
|
description='Estimated income tax',
|
||||||
postings=[
|
postings=[
|
||||||
Posting(account='Income Tax', quantity=tax_amount.quantity, commodity=reporting_commodity()),
|
Posting(account='Income Tax', quantity=tax_amount.quantity, commodity=reporting_commodity()),
|
||||||
Posting(account='Income Tax Control', quantity=-tax_amount.quantity, commodity=reporting_commodity())
|
Posting(account='Income Tax Control', quantity=-tax_amount.quantity, commodity=reporting_commodity())
|
||||||
]
|
]
|
||||||
)]
|
))
|
||||||
|
|
||||||
# Mandatory study loan repayment
|
# Mandatory study loan repayment
|
||||||
loan_repayment = report.by_id('loan_repayment').amount
|
loan_repayment = report.by_id('loan_repayment').amount
|
||||||
@ -81,9 +88,6 @@ def make_tax_transactions(start_date=None, end_date=None):
|
|||||||
]
|
]
|
||||||
))
|
))
|
||||||
|
|
||||||
# Get trial balance
|
|
||||||
balancer = TrialBalancer.from_cached()
|
|
||||||
|
|
||||||
accounts = dict(sorted(balancer.accounts.items()))
|
accounts = dict(sorted(balancer.accounts.items()))
|
||||||
|
|
||||||
# Get account configurations
|
# Get account configurations
|
||||||
|
@ -82,10 +82,7 @@ def study_loan_repayment(year, taxable_income, rfb_grossedup):
|
|||||||
return Amount(rate * repayment_income.quantity, reporting_commodity())
|
return Amount(rate * repayment_income.quantity, reporting_commodity())
|
||||||
|
|
||||||
@assert_aud
|
@assert_aud
|
||||||
def tax_summary_report():
|
def tax_summary_report(balancer):
|
||||||
# Get trial balance
|
|
||||||
balancer = TrialBalancer.from_cached(start_date=sofy_date(), end_date=eofy_date())
|
|
||||||
|
|
||||||
accounts = dict(sorted(balancer.accounts.items()))
|
accounts = dict(sorted(balancer.accounts.items()))
|
||||||
|
|
||||||
# Get account configurations
|
# Get account configurations
|
||||||
|
@ -16,10 +16,11 @@
|
|||||||
|
|
||||||
from flask import redirect, render_template, request, url_for
|
from flask import redirect, render_template, request, url_for
|
||||||
|
|
||||||
from drcr.models import AccountConfiguration, Amount, Posting, Transaction, reporting_commodity
|
from drcr.models import AccountConfiguration, Amount, Posting, Transaction, TrialBalancer, reporting_commodity
|
||||||
from drcr.database import db
|
from drcr.database import db
|
||||||
from drcr.plugins import render_plugin_template
|
from drcr.plugins import render_plugin_template
|
||||||
from drcr.webapp import all_accounts, app, eofy_date
|
from drcr.transactions import api_transactions
|
||||||
|
from drcr.webapp import all_accounts, app, eofy_date, sofy_date
|
||||||
|
|
||||||
from .models import CGTAsset, CGTCostAdjustment
|
from .models import CGTAsset, CGTCostAdjustment
|
||||||
from .reports import tax_summary_report
|
from .reports import tax_summary_report
|
||||||
@ -237,5 +238,9 @@ def cgt_assets():
|
|||||||
@app.route('/tax/summary')
|
@app.route('/tax/summary')
|
||||||
@assert_aud
|
@assert_aud
|
||||||
def tax_summary():
|
def tax_summary():
|
||||||
report = tax_summary_report()
|
# Get trial balance
|
||||||
|
balancer = TrialBalancer.from_cached(start_date=sofy_date(), end_date=eofy_date())
|
||||||
|
balancer.apply_transactions(api_transactions(api_stage_until=299))
|
||||||
|
|
||||||
|
report = tax_summary_report(balancer)
|
||||||
return render_template('report.html', report=report)
|
return render_template('report.html', report=report)
|
||||||
|
@ -19,7 +19,8 @@ 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, queue_invalidate_running_balances, reporting_commodity
|
from ..models import Amount, Posting, Transaction, TrialBalancer, queue_invalidate_running_balances, reporting_commodity
|
||||||
from ..webapp import all_accounts, all_transactions, app
|
from ..transactions import all_transactions
|
||||||
|
from ..webapp import all_accounts, app
|
||||||
from .models import BalanceAssertion
|
from .models import BalanceAssertion
|
||||||
from ..statements.models import StatementLineReconciliation
|
from ..statements.models import StatementLineReconciliation
|
||||||
|
|
||||||
|
@ -17,7 +17,8 @@
|
|||||||
from flask import url_for
|
from flask import url_for
|
||||||
|
|
||||||
from .models import AccountConfiguration, Amount, TrialBalancer, reporting_commodity
|
from .models import AccountConfiguration, Amount, TrialBalancer, reporting_commodity
|
||||||
from .webapp import all_transactions, api_transactions, eofy_date, sofy_date
|
from .transactions import all_transactions, api_transactions
|
||||||
|
from .webapp import eofy_date, sofy_date
|
||||||
|
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
|
|
||||||
|
72
drcr/transactions.py
Normal file
72
drcr/transactions.py
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
# DrCr: Web-based double-entry bookkeeping framework
|
||||||
|
# Copyright (C) 2022–2024 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 <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
from .database import db
|
||||||
|
from .models import Transaction
|
||||||
|
from .plugins import transaction_providers
|
||||||
|
from .statements.models import StatementLine
|
||||||
|
|
||||||
|
def limit_query_dt(query, field, start_date=None, end_date=None):
|
||||||
|
"""Helper function to limit the query between the start and end dates"""
|
||||||
|
|
||||||
|
if start_date and end_date:
|
||||||
|
return query.where((field >= start_date) & (field <= end_date))
|
||||||
|
if start_date:
|
||||||
|
return query.where(field >= start_date)
|
||||||
|
if end_date:
|
||||||
|
return query.where(field <= end_date)
|
||||||
|
return query
|
||||||
|
|
||||||
|
def all_transactions(start_date=None, end_date=None, api_stage_until=None, join_postings=True):
|
||||||
|
"""Return all transactions, including from DB and API"""
|
||||||
|
|
||||||
|
transactions = db_transactions(start_date=start_date, end_date=end_date, join_postings=join_postings)
|
||||||
|
transactions.extend(api_transactions(start_date=start_date, end_date=end_date, api_stage_until=api_stage_until))
|
||||||
|
return transactions
|
||||||
|
|
||||||
|
def db_transactions(start_date=None, end_date=None, join_postings=True):
|
||||||
|
"""Return only transactions from DB"""
|
||||||
|
|
||||||
|
# All Transactions in database between start_date and end_date
|
||||||
|
query = db.select(Transaction)
|
||||||
|
query = limit_query_dt(query, Transaction.dt, start_date, end_date)
|
||||||
|
if join_postings:
|
||||||
|
query = query.options(db.selectinload(Transaction.postings))
|
||||||
|
|
||||||
|
transactions = db.session.scalars(query).all()
|
||||||
|
return transactions
|
||||||
|
|
||||||
|
def api_transactions(start_date=None, end_date=None, api_stage_until=None):
|
||||||
|
"""Return only transactions from API"""
|
||||||
|
|
||||||
|
# Stage 0: DB transactions
|
||||||
|
# Stage 100: Ordinary transactions
|
||||||
|
# Stage 200: Closing entries prior to tax
|
||||||
|
# Stage 300: Tax entries
|
||||||
|
# Stage 400: Closing entries after tax
|
||||||
|
|
||||||
|
transactions = []
|
||||||
|
|
||||||
|
# Unreconciled StatementLines
|
||||||
|
query = db.select(StatementLine).where(StatementLine.reconciliation == None)
|
||||||
|
query = limit_query_dt(query, StatementLine.dt, start_date, end_date)
|
||||||
|
transactions.extend(line.into_transaction() for line in db.session.scalars(query).all())
|
||||||
|
|
||||||
|
# Plugins
|
||||||
|
for transaction_provider in transaction_providers:
|
||||||
|
transactions = transaction_provider(transactions, start_date=start_date, end_date=end_date, api_stage_until=api_stage_until)
|
||||||
|
|
||||||
|
return transactions
|
@ -20,7 +20,8 @@ from .database import db
|
|||||||
from .models import AccountConfiguration, Amount, Balance, Posting, TrialBalancer, reporting_commodity
|
from .models import AccountConfiguration, Amount, Balance, Posting, TrialBalancer, reporting_commodity
|
||||||
from .plugins import account_kinds, advanced_reports, data_sources
|
from .plugins import account_kinds, advanced_reports, data_sources
|
||||||
from .reports import balance_sheet_report, income_statement_report
|
from .reports import balance_sheet_report, income_statement_report
|
||||||
from .webapp import all_transactions, api_transactions, app
|
from .transactions import all_transactions, api_transactions
|
||||||
|
from .webapp import app
|
||||||
|
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from itertools import groupby
|
from itertools import groupby
|
||||||
|
@ -23,8 +23,7 @@ from flask_sqlalchemy.record_queries import get_recorded_queries
|
|||||||
|
|
||||||
from .database import db
|
from .database import db
|
||||||
from .models import Amount, Metadata, Transaction, reporting_commodity
|
from .models import Amount, Metadata, Transaction, reporting_commodity
|
||||||
from .plugins import init_plugins, transaction_providers
|
from .plugins import init_plugins
|
||||||
from .statements.models import StatementLine
|
|
||||||
|
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
import time
|
import time
|
||||||
@ -32,51 +31,10 @@ import time
|
|||||||
app.config['SQLALCHEMY_RECORD_QUERIES'] = app.debug
|
app.config['SQLALCHEMY_RECORD_QUERIES'] = app.debug
|
||||||
db.init_app(app)
|
db.init_app(app)
|
||||||
|
|
||||||
def limit_query_dt(query, field, start_date=None, end_date=None):
|
|
||||||
"""Helper function to limit the query between the start and end dates"""
|
|
||||||
|
|
||||||
if start_date and end_date:
|
|
||||||
return query.where((field >= start_date) & (field <= end_date))
|
|
||||||
if start_date:
|
|
||||||
return query.where(field >= start_date)
|
|
||||||
if end_date:
|
|
||||||
return query.where(field <= end_date)
|
|
||||||
return query
|
|
||||||
|
|
||||||
def all_transactions(start_date=None, end_date=None, join_postings=True):
|
|
||||||
"""Return all transactions, including from DB and API"""
|
|
||||||
|
|
||||||
# All Transactions in database between start_date and end_date
|
|
||||||
query = db.select(Transaction)
|
|
||||||
query = limit_query_dt(query, Transaction.dt, start_date, end_date)
|
|
||||||
if join_postings:
|
|
||||||
query = query.options(db.selectinload(Transaction.postings))
|
|
||||||
|
|
||||||
transactions = db.session.scalars(query).all()
|
|
||||||
|
|
||||||
transactions.extend(api_transactions(start_date=start_date, end_date=end_date))
|
|
||||||
|
|
||||||
return transactions
|
|
||||||
|
|
||||||
def api_transactions(start_date=None, end_date=None):
|
|
||||||
"""Return only transactions from API"""
|
|
||||||
|
|
||||||
transactions = []
|
|
||||||
|
|
||||||
# Unreconciled StatementLines
|
|
||||||
query = db.select(StatementLine).where(StatementLine.reconciliation == None)
|
|
||||||
query = limit_query_dt(query, StatementLine.dt, start_date, end_date)
|
|
||||||
transactions.extend(line.into_transaction() for line in db.session.scalars(query).all())
|
|
||||||
|
|
||||||
# Plugins
|
|
||||||
for transaction_provider in transaction_providers:
|
|
||||||
transactions.extend(transaction_provider(start_date=start_date, end_date=end_date))
|
|
||||||
|
|
||||||
return transactions
|
|
||||||
|
|
||||||
def all_accounts():
|
def all_accounts():
|
||||||
# TODO: Can this be cached?
|
"""Return all accounts in alphabetical order"""
|
||||||
return sorted(list(set(p.account for t in all_transactions() for p in t.postings)))
|
|
||||||
|
return db.session.scalars('SELECT DISTINCT account FROM postings ORDER BY account').all()
|
||||||
|
|
||||||
def eofy_date():
|
def eofy_date():
|
||||||
"""Get the datetime for the end of the financial year"""
|
"""Get the datetime for the end of the financial year"""
|
||||||
|
Loading…
Reference in New Issue
Block a user