From fe8d47c676a32bf25db8cd815783e3b5d60f3b07 Mon Sep 17 00:00:00 2001 From: RunasSudo Date: Sat, 9 Nov 2024 18:14:25 +1100 Subject: [PATCH] Don't hard code reporting commodity --- austax/__init__.py | 18 +++++----- austax/models.py | 10 +++--- austax/reports.py | 33 +++++++++++-------- austax/templates/cgt_adjustments_edit.html | 2 +- .../templates/cgt_adjustments_multinew.html | 2 +- austax/util.py | 31 +++++++++++++++++ austax/views.py | 11 +++++-- drcr/journal/views.py | 4 +-- drcr/models.py | 28 +++++++++++----- drcr/reports.py | 6 ++-- drcr/statements/importers/ofx1.py | 6 ++-- drcr/statements/importers/ofx2.py | 6 ++-- .../journal/balance_assertions_edit.html | 2 +- .../journal/journal_edit_transaction.html | 9 ++--- drcr/templates/report.html | 2 +- drcr/views.py | 8 ++--- drcr/webapp.py | 8 +++-- 17 files changed, 124 insertions(+), 62 deletions(-) create mode 100644 austax/util.py diff --git a/austax/__init__.py b/austax/__init__.py index 0507caa..4cd4bcc 100644 --- a/austax/__init__.py +++ b/austax/__init__.py @@ -16,13 +16,14 @@ from flask import render_template, url_for -from drcr.models import AccountConfiguration, Posting, Transaction, TrialBalancer +from drcr.models import AccountConfiguration, Posting, Transaction, TrialBalancer, reporting_commodity from drcr.database import db from drcr.webapp import eofy_date import drcr.plugins +from . import views # Load routes from .reports import tax_summary_report -from . import views +from .util import assert_aud def plugin_init(): drcr.plugins.data_sources.append(('cgt_adjustments', 'CGT adjustments')) @@ -47,6 +48,7 @@ def plugin_init(): drcr.plugins.transaction_providers.append(make_tax_transactions) +@assert_aud def make_tax_transactions(): report = tax_summary_report() tax_amount = report.by_id('total_tax').amount @@ -59,8 +61,8 @@ def make_tax_transactions(): dt=dt, description='Estimated income tax', postings=[ - Posting(account='Income Tax', quantity=tax_amount.quantity, commodity='$'), - Posting(account='Income Tax Control', quantity=-tax_amount.quantity, commodity='$') + Posting(account='Income Tax', quantity=tax_amount.quantity, commodity=reporting_commodity()), + Posting(account='Income Tax Control', quantity=-tax_amount.quantity, commodity=reporting_commodity()) ] )] @@ -71,8 +73,8 @@ def make_tax_transactions(): dt=dt, description='Mandatory study loan repayment payable', postings=[ - Posting(account='HELP', quantity=loan_repayment.quantity, commodity='$'), # FIXME: Correct account - Posting(account='Income Tax Control', quantity=-loan_repayment.quantity, commodity='$') + Posting(account='HELP', quantity=loan_repayment.quantity, commodity=reporting_commodity()), # FIXME: Correct account + Posting(account='Income Tax Control', quantity=-loan_repayment.quantity, commodity=reporting_commodity()) ] )) @@ -94,8 +96,8 @@ def make_tax_transactions(): dt=dt, description='PAYG withheld amounts', postings=[ - Posting(account='Income Tax Control', quantity=accounts[account_name].quantity, commodity='$'), - Posting(account=account_name, quantity=-accounts[account_name].quantity, commodity='$') + Posting(account='Income Tax Control', quantity=accounts[account_name].quantity, commodity=reporting_commodity()), + Posting(account=account_name, quantity=-accounts[account_name].quantity, commodity=reporting_commodity()) ] )) diff --git a/austax/models.py b/austax/models.py index fbdc032..445fbd0 100644 --- a/austax/models.py +++ b/austax/models.py @@ -1,5 +1,5 @@ # DrCr: Web-based double-entry bookkeeping framework -# Copyright (C) 2022–2023 Lee Yingtong Li (RunasSudo) +# 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 @@ -37,20 +37,20 @@ class CGTAsset(Amount): return self.commodity[:self.commodity.index('{')].strip() def cost_adjustment(self): - return Amount(sum(a.cost_adjustment for a in self.cost_adjustments), '$') + return Amount(sum(a.cost_adjustment for a in self.cost_adjustments), reporting_commodity()) def cost_adjustment_brought_forward(self): date1 = eofy_date() date1 = date1.replace(year=date1.year - 1) - return Amount(sum(a.cost_adjustment for a in self.cost_adjustments if a.dt <= date1), '$') + return Amount(sum(a.cost_adjustment for a in self.cost_adjustments if a.dt <= date1), reporting_commodity()) def cost_adjustment_current_period(self): date1 = eofy_date() date1 = date1.replace(year=date1.year - 1) date2 = eofy_date() - return Amount(sum(a.cost_adjustment for a in self.cost_adjustments if a.dt > date1 and a.dt <= date2), '$') + return Amount(sum(a.cost_adjustment for a in self.cost_adjustments if a.dt > date1 and a.dt <= date2), reporting_commodity()) def gain(self): return self.disposal_value - (self.as_cost() + self.cost_adjustment()) @@ -73,4 +73,4 @@ class CGTCostAdjustment(db.Model): return CGTAsset(self.quantity, self.commodity, self.account, self.acquisition_date) def cost_adjustment_amount(self): - return Amount(self.cost_adjustment, '$') + return Amount(self.cost_adjustment, reporting_commodity()) diff --git a/austax/reports.py b/austax/reports.py index df84664..744f425 100644 --- a/austax/reports.py +++ b/austax/reports.py @@ -16,20 +16,23 @@ from drcr import AMOUNT_DPS from drcr.database import db -from drcr.models import AccountConfiguration, Amount, Metadata, Transaction, TrialBalancer +from drcr.models import AccountConfiguration, Amount, Metadata, Transaction, TrialBalancer, reporting_commodity from drcr.reports import Calculated, Report, Section, Spacer, Subtotal, entries_for_kind from drcr.webapp import eofy_date, sofy_date from .tax_tables import base_tax, medicare_levy_threshold, medicare_levy_surcharge_single, repayment_rates, fbt_grossup +from .util import assert_aud +@assert_aud def base_income_tax(year, taxable_income): """Get the amount of base income tax""" for i, (upper_limit, flat_amount, marginal_rate) in enumerate(base_tax[year]): if upper_limit is None or taxable_income.quantity <= upper_limit * (10**AMOUNT_DPS): lower_limit = base_tax[year][i - 1][0] or 0 - return Amount(flat_amount * (10**AMOUNT_DPS) + marginal_rate * (taxable_income.quantity - lower_limit * (10**AMOUNT_DPS)), '$') + return Amount(flat_amount * (10**AMOUNT_DPS) + marginal_rate * (taxable_income.quantity - lower_limit * (10**AMOUNT_DPS)), reporting_commodity()) +@assert_aud def lito(taxable_income, total_tax): """Get the amount of low income tax offset""" @@ -38,34 +41,37 @@ def lito(taxable_income, total_tax): # FIXME: This will not work if we implement multiple non-refundable tax offsets if total_tax.quantity <= 70000: return total_tax - return Amount(70000, '$') + return Amount(70000, reporting_commodity()) if taxable_income.quantity <= 4500000: - return Amount(70000 - 0.05 * (taxable_income.quantity - 3750000), '$') + return Amount(70000 - 0.05 * (taxable_income.quantity - 3750000), reporting_commodity()) if taxable_income.quantity <= 6666700: - return Amount(32500 - int(0.015 * (taxable_income.quantity - 4500000)), '$') + return Amount(32500 - int(0.015 * (taxable_income.quantity - 4500000)), reporting_commodity()) - return Amount(0, '$') + return Amount(0, reporting_commodity()) +@assert_aud def medicare_levy(year, taxable_income): lower_threshold, upper_threshold = medicare_levy_threshold[year] if taxable_income.quantity < lower_threshold * 100: - return Amount(0, '$') + return Amount(0, reporting_commodity()) if taxable_income.quantity < upper_threshold * 100: # Medicare levy is 10% of the amount above the lower threshold - return Amount((taxable_income - lower_threshold * 100) * 0.1, '$') + return Amount((taxable_income - lower_threshold * 100) * 0.1, reporting_commodity()) # Normal Medicare levy - return Amount(int(taxable_income.quantity * 0.02), '$') + return Amount(int(taxable_income.quantity * 0.02), reporting_commodity()) +@assert_aud def medicare_levy_surcharge(year, taxable_income, rfb_grossedup): mls_income = taxable_income + rfb_grossedup for i, (upper_limit, rate) in enumerate(medicare_levy_surcharge_single[year]): if upper_limit is None or mls_income.quantity <= upper_limit * (10**AMOUNT_DPS): - return Amount(rate * mls_income.quantity, '$') + return Amount(rate * mls_income.quantity, reporting_commodity()) +@assert_aud def study_loan_repayment(year, taxable_income, rfb_grossedup): """Get the amount of mandatory study loan repayment""" @@ -73,8 +79,9 @@ def study_loan_repayment(year, taxable_income, rfb_grossedup): for upper_limit, rate in repayment_rates[year]: if upper_limit is None or repayment_income.quantity <= upper_limit * (10**AMOUNT_DPS): - return Amount(rate * repayment_income.quantity, '$') + return Amount(rate * repayment_income.quantity, reporting_commodity()) +@assert_aud def tax_summary_report(): # Get trial balance balancer = TrialBalancer() @@ -182,12 +189,12 @@ def tax_summary_report(): entries=[ Calculated( 'Taxable value of reportable fringe benefits', - lambda _: -sum((e.amount for e in entries_for_kind(account_configurations, accounts, 'austax.rfb')), Amount(0, '$')), + lambda _: -sum((e.amount for e in entries_for_kind(account_configurations, accounts, 'austax.rfb')), Amount(0, reporting_commodity())), id='rfb_taxable' ), Calculated( 'Grossed-up value', - lambda _: Amount(report.by_id('rfb_taxable').amount.quantity * fbt_grossup[eofy_date().year], '$'), + lambda _: Amount(report.by_id('rfb_taxable').amount.quantity * fbt_grossup[eofy_date().year], reporting_commodity()), id='rfb_grossedup' ) ], diff --git a/austax/templates/cgt_adjustments_edit.html b/austax/templates/cgt_adjustments_edit.html index 9aa6510..d81ddba 100644 --- a/austax/templates/cgt_adjustments_edit.html +++ b/austax/templates/cgt_adjustments_edit.html @@ -55,7 +55,7 @@
- $ + {{ reporting_commodity }}
diff --git a/austax/templates/cgt_adjustments_multinew.html b/austax/templates/cgt_adjustments_multinew.html index aeaaefb..f209fe8 100644 --- a/austax/templates/cgt_adjustments_multinew.html +++ b/austax/templates/cgt_adjustments_multinew.html @@ -64,7 +64,7 @@
- $ + {{ reporting_commodity }}
diff --git a/austax/util.py b/austax/util.py new file mode 100644 index 0000000..79ccf46 --- /dev/null +++ b/austax/util.py @@ -0,0 +1,31 @@ +# 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 . + +from drcr.models import reporting_commodity + +import functools + +def assert_aud(f): + """Wrap a function to assert that the reporting_commodity is $""" + + @functools.wraps(f) + def wrapper(*args, **kwargs): + if reporting_commodity() != '$': + raise Exception('austax requires reporting_commodity to be $') + + return f(*args, **kwargs) + + return wrapper diff --git a/austax/views.py b/austax/views.py index abc4459..8855e15 100644 --- a/austax/views.py +++ b/austax/views.py @@ -23,11 +23,13 @@ from drcr.webapp import all_accounts, app, eofy_date from .models import CGTAsset, CGTCostAdjustment from .reports import tax_summary_report +from .util import assert_aud from datetime import datetime from math import copysign @app.route('/tax/cgt-adjustments') +@assert_aud def cgt_adjustments(): adjustments = db.select(CGTCostAdjustment).order_by(CGTCostAdjustment.dt.desc(), CGTCostAdjustment.account, CGTCostAdjustment.id.desc()) if 'account' in request.args: @@ -43,6 +45,7 @@ def cgt_adjustments(): return render_plugin_template('austax', 'cgt_adjustments.html', cgt_adjustments=adjustments) @app.route('/tax/cgt-adjustments/new', methods=['GET', 'POST']) +@assert_aud def cgt_adjustment_new(): if request.method == 'GET': return render_plugin_template('austax', 'cgt_adjustments_edit.html', adjustment=None, all_accounts=all_accounts()) @@ -63,6 +66,7 @@ def cgt_adjustment_new(): return redirect(url_for('cgt_adjustments')) @app.route('/tax/cgt-adjustments/edit', methods=['GET', 'POST']) +@assert_aud def cgt_adjustment_edit(): if request.method == 'GET': return render_plugin_template('austax', 'cgt_adjustments_edit.html', adjustment=db.session.get(CGTCostAdjustment, request.args['id']), all_accounts=all_accounts()) @@ -83,6 +87,7 @@ def cgt_adjustment_edit(): return redirect(url_for('cgt_adjustments')) @app.route('/tax/cgt-adjustments/multi-new', methods=['GET', 'POST']) +@assert_aud def cgt_adjustment_multinew(): if request.method == 'GET': return render_plugin_template( @@ -172,6 +177,7 @@ def cgt_adjustment_multinew(): return redirect(url_for('cgt_adjustments')) @app.route('/tax/cgt-assets') +@assert_aud def cgt_assets(): # Find all CGT asset accounts cgt_accounts = [] @@ -192,7 +198,7 @@ def cgt_assets(): assets = [] for posting in cgt_postings: - if posting.commodity == '$': + if posting.commodity == reporting_commodity(): # FIXME: Detect this better continue @@ -212,7 +218,7 @@ def cgt_assets(): asset.disposal_date = posting.transaction.dt # Calculate disposal value by searching for matching asset postings - asset.disposal_value = Amount(0, '$') + asset.disposal_value = Amount(0, reporting_commodity()) for other_posting in posting.transaction.postings: if posting != other_posting and 'drcr.asset' in account_configurations.get(other_posting.account, []): asset.disposal_value.quantity += other_posting.amount().as_cost().quantity @@ -229,6 +235,7 @@ def cgt_assets(): return render_plugin_template('austax', 'cgt_assets.html', assets=assets, eofy_date=eofy_date()) @app.route('/tax/summary') +@assert_aud def tax_summary(): report = tax_summary_report() return render_template('report.html', report=report) diff --git a/drcr/journal/views.py b/drcr/journal/views.py index 4ba4fa7..f8c4e40 100644 --- a/drcr/journal/views.py +++ b/drcr/journal/views.py @@ -18,7 +18,7 @@ from flask import abort, redirect, render_template, request, url_for from .. import AMOUNT_DPS from ..database import db -from ..models import Amount, Posting, Transaction, TrialBalancer +from ..models import Amount, Posting, Transaction, TrialBalancer, reporting_commodity from ..webapp import all_accounts, all_transactions, app from .models import BalanceAssertion from ..statements.models import StatementLineReconciliation @@ -158,7 +158,7 @@ def balance_assertions_new(): description=request.form['description'], account=request.form['account'], quantity=quantity, - commodity='$' + commodity=reporting_commodity() ) db.session.add(assertion) db.session.commit() diff --git a/drcr/models.py b/drcr/models.py index 1da7b7f..1ba222c 100644 --- a/drcr/models.py +++ b/drcr/models.py @@ -1,5 +1,5 @@ # DrCr: Web-based double-entry bookkeeping framework -# Copyright (C) 2022–2023 Lee Yingtong Li (RunasSudo) +# 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 @@ -19,6 +19,7 @@ from markupsafe import Markup from . import AMOUNT_DPS from .database import db +import functools from itertools import groupby class Transaction(db.Model): @@ -86,7 +87,7 @@ class Amount: if ' ' not in amount_str: # Default commodity quantity = round(float(amount_str) * (10**AMOUNT_DPS)) - return Amount(quantity, '$') # TODO: Customisable default commodity + return Amount(quantity, reporting_commodity()) quantity_str = amount_str[:amount_str.index(' ')] quantity = round(float(quantity_str) * (10**AMOUNT_DPS)) @@ -118,7 +119,7 @@ class Amount: if commodity not in ('non_reporting', 'force', 'hide'): raise ValueError('Invalid commodity reporting option') - if (self.commodity == '$' and commodity in ('non_reporting', 'force')) or commodity == 'hide': + if (self.commodity == reporting_commodity() and commodity in ('non_reporting', 'force')) or commodity == 'hide': return Markup('{:,.{dps}f}'.format(self.quantity / (10**AMOUNT_DPS), dps=AMOUNT_DPS).replace(',', ' ')) elif len(self.commodity) == 1: return Markup('{0}{1:,.{dps}f}'.format(self.commodity, self.quantity / (10**AMOUNT_DPS), dps=AMOUNT_DPS).replace(',', ' ')) @@ -139,7 +140,7 @@ class Amount: return Markup('{}{}'.format(link, text, space)) def quantity_string(self): - if self.commodity == '$': + if self.commodity == reporting_commodity(): return '{:.{dps}f}'.format(self.quantity / (10**AMOUNT_DPS), dps=AMOUNT_DPS) elif len(self.commodity) == 1: return '{0}{1:.{dps}f}'.format(self.commodity, self.quantity / (10**AMOUNT_DPS), dps=AMOUNT_DPS) @@ -149,7 +150,7 @@ class Amount: def as_cost(self): """Convert commodity to reporting currency in cost basis""" - if self.commodity == '$': + if self.commodity == reporting_commodity(): return self # TODO: Refactor this @@ -158,10 +159,10 @@ class Amount: cost = float(self.commodity[self.commodity.index('{{')+2:self.commodity.index('}}')]) if self.quantity < 0: cost = -cost - return Amount(round(cost * (10**AMOUNT_DPS)), '$') + return Amount(round(cost * (10**AMOUNT_DPS)), reporting_commodity()) elif '{' in self.commodity: cost = float(self.commodity[self.commodity.index('{')+1:self.commodity.index('}')]) - return Amount(round(cost * self.quantity), '$') # FIXME: Custom reporting currency + return Amount(round(cost * self.quantity), reporting_commodity()) # FIXME: Custom reporting currency else: raise Exception('No cost base for commodity {}'.format(self.commodity)) @@ -199,7 +200,7 @@ class TrialBalancer: for transaction in transactions: for posting in transaction.postings: if posting.account not in self.accounts: - self.accounts[posting.account] = Amount(0, '$') + self.accounts[posting.account] = Amount(0, reporting_commodity()) # FIXME: Handle commodities better self.accounts[posting.account].quantity += posting.amount().as_cost().quantity @@ -217,7 +218,7 @@ class TrialBalancer: return if destination_account not in self.accounts: - self.accounts[destination_account] = Amount(0, '$') # FIXME: Other commodities + self.accounts[destination_account] = Amount(0, reporting_commodity()) # FIXME: Handle commodities self.accounts[destination_account].quantity += self.accounts[source_account].quantity @@ -246,6 +247,9 @@ class AccountConfiguration(db.Model): return kinds +# ---------------- +# Metadata helpers + class Metadata(db.Model): __tablename__ = 'metadata' @@ -257,3 +261,9 @@ class Metadata(db.Model): @staticmethod def get(key): return Metadata.query.filter_by(key=key).one().value + +@functools.cache # Very poor performance if result is not cached! +def reporting_commodity(): + """Get the native reporting commodity""" + + return Metadata.get('reporting_commodity') diff --git a/drcr/reports.py b/drcr/reports.py index 0d07090..73c93da 100644 --- a/drcr/reports.py +++ b/drcr/reports.py @@ -1,5 +1,5 @@ # DrCr: Web-based double-entry bookkeeping framework -# Copyright (C) 2022–2023 Lee Yingtong Li (RunasSudo) +# 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 @@ -16,7 +16,7 @@ from flask import url_for -from .models import AccountConfiguration, Amount, TrialBalancer +from .models import AccountConfiguration, Amount, TrialBalancer, reporting_commodity from .webapp import all_transactions, eofy_date, sofy_date from datetime import datetime, timedelta @@ -110,7 +110,7 @@ class Subtotal: if self.floor: amount = (amount // self.floor) * self.floor - self.amount = Amount(amount, '$') + self.amount = Amount(amount, reporting_commodity()) class Calculated(Entry): def __init__(self, text=None, calc=None, **kwargs): diff --git a/drcr/statements/importers/ofx1.py b/drcr/statements/importers/ofx1.py index b937020..fa55bff 100644 --- a/drcr/statements/importers/ofx1.py +++ b/drcr/statements/importers/ofx1.py @@ -1,5 +1,5 @@ # DrCr: Web-based double-entry bookkeeping framework -# Copyright (C) 2022–2023 Lee Yingtong Li (RunasSudo) +# 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 @@ -16,7 +16,7 @@ import lxml.etree as ET -from ..models import StatementLine +from ..models import StatementLine, reporting_commodity from datetime import datetime from io import StringIO @@ -51,7 +51,7 @@ def import_ofx1(file): dt=datetime.strptime(date, '%Y-%m-%d'), description=description, quantity=round(float(amount)*100), - commodity='$' + commodity=reporting_commodity() )) return imported_statement_lines diff --git a/drcr/statements/importers/ofx2.py b/drcr/statements/importers/ofx2.py index 5dbc5fb..9813a44 100644 --- a/drcr/statements/importers/ofx2.py +++ b/drcr/statements/importers/ofx2.py @@ -1,5 +1,5 @@ # DrCr: Web-based double-entry bookkeeping framework -# Copyright (C) 2022–2023 Lee Yingtong Li (RunasSudo) +# 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 @@ -14,7 +14,7 @@ # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . -from ..models import StatementLine +from ..models import StatementLine, reporting_commodity from datetime import datetime from io import StringIO @@ -55,7 +55,7 @@ def import_ofx2(file): dt=datetime.strptime(date, '%Y-%m-%d'), description=description, quantity=round(float(amount)*100), - commodity='$' + commodity=reporting_commodity() )) return imported_statement_lines diff --git a/drcr/templates/journal/balance_assertions_edit.html b/drcr/templates/journal/balance_assertions_edit.html index 57bf901..2aa9bcb 100644 --- a/drcr/templates/journal/balance_assertions_edit.html +++ b/drcr/templates/journal/balance_assertions_edit.html @@ -42,7 +42,7 @@
- $ + {{ reporting_commodity }}
{# TODO: Display existing credit assertion as credit, not as negative debit #} diff --git a/drcr/templates/journal/journal_edit_transaction.html b/drcr/templates/journal/journal_edit_transaction.html index b090276..931b641 100644 --- a/drcr/templates/journal/journal_edit_transaction.html +++ b/drcr/templates/journal/journal_edit_transaction.html @@ -47,7 +47,8 @@
- $ + {# FIXME: Gracefully handle when the reporting commodity is not a single character #} + {{ reporting_commodity }}
@@ -84,7 +85,7 @@
- $ + {{ reporting_commodity }}
@@ -148,7 +149,7 @@
- $ + {{ reporting_commodity }}
@@ -159,7 +160,7 @@
- $ + {{ reporting_commodity }}
diff --git a/drcr/templates/report.html b/drcr/templates/report.html index 42747c5..3d5fc92 100644 --- a/drcr/templates/report.html +++ b/drcr/templates/report.html @@ -67,7 +67,7 @@ - $  + {{ reporting_commodity }}  diff --git a/drcr/views.py b/drcr/views.py index eea17a6..c91ade8 100644 --- a/drcr/views.py +++ b/drcr/views.py @@ -17,7 +17,7 @@ from flask import redirect, render_template, request, url_for from .database import db -from .models import AccountConfiguration, Amount, Balance, Posting, TrialBalancer +from .models import AccountConfiguration, Amount, Balance, Posting, TrialBalancer, reporting_commodity from .plugins import account_kinds, advanced_reports, data_sources from .reports import balance_sheet_report, income_statement_report from .webapp import all_transactions, app @@ -72,8 +72,8 @@ def trial_balance(): balancer = TrialBalancer() balancer.apply_transactions(all_transactions()) - total_dr = Amount(sum(v.quantity for v in balancer.accounts.values() if v.quantity > 0), '$') - total_cr = Amount(sum(v.quantity for v in balancer.accounts.values() if v.quantity < 0), '$') + total_dr = Amount(sum(v.quantity for v in balancer.accounts.values() if v.quantity > 0), reporting_commodity()) + total_cr = Amount(sum(v.quantity for v in balancer.accounts.values() if v.quantity < 0), reporting_commodity()) return render_template('trial_balance.html', accounts=dict(sorted(balancer.accounts.items())), total_dr=total_dr, total_cr=total_cr) @@ -105,7 +105,7 @@ def account_transactions(): # Pre-compute running totals # There can be more than one posting per account per transaction, so track the running total at the level of individual postings running_totals = {} - running_total = Amount(0, '$') + running_total = Amount(0, reporting_commodity()) for transaction in sorted(transactions, key=lambda t: t.dt): for posting in transaction.postings: if posting.account == request.args['account']: diff --git a/drcr/webapp.py b/drcr/webapp.py index 87436ae..8b2324f 100644 --- a/drcr/webapp.py +++ b/drcr/webapp.py @@ -1,5 +1,5 @@ # DrCr: Web-based double-entry bookkeeping framework -# Copyright (C) 2022–2023 Lee Yingtong Li (RunasSudo) +# 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 @@ -22,7 +22,7 @@ app.config.from_file('config.toml', load=toml.load) from flask_sqlalchemy.record_queries import get_recorded_queries from .database import db -from .models import Metadata, Transaction +from .models import Metadata, Transaction, reporting_commodity from .plugins import init_plugins, transaction_providers from .statements.models import StatementLine @@ -84,6 +84,10 @@ def initdb(): # FIXME: Need to init metadata +@app.context_processor +def add_reporting_commodity(): + return dict(reporting_commodity=reporting_commodity()) + if app.debug: @app.before_request def before_request():