Don't hard code reporting commodity

This commit is contained in:
RunasSudo 2024-11-09 18:14:25 +11:00
parent e5cce0fd4b
commit b425643980
Signed by: RunasSudo
GPG Key ID: 7234E476BF21C61A
17 changed files with 126 additions and 64 deletions

View File

@ -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())
]
))

View File

@ -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
@ -15,7 +15,7 @@
# along with this program. If not, see <https://www.gnu.org/licenses/>.
from drcr.database import db
from drcr.models import Amount
from drcr.models import Amount, reporting_commodity
from drcr.webapp import eofy_date
class CGTAsset(Amount):
@ -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())

View File

@ -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'
)
],

View File

@ -55,7 +55,7 @@
<label for="cost_adjustment" class="block text-gray-900 pr-4">Cost adjustment</label>
<div class="relative shadow-sm">
<div class="pointer-events-none absolute inset-y-0 left-0 flex items-center pl-3">
<span class="text-gray-500">$</span>
<span class="text-gray-500">{{ reporting_commodity }}</span>
</div>
<input type="number" class="bordered-field pl-7" name="cost_adjustment" id="cost_adjustment" step="0.01" value="{{ adjustment.cost_adjustment_amount().quantity_string() if adjustment else '' }}" placeholder="0.00">
</div>

View File

@ -64,7 +64,7 @@
<label for="cost_adjustment" class="block text-gray-900 pr-4">Total cost adjustment</label>
<div class="relative shadow-sm">
<div class="pointer-events-none absolute inset-y-0 left-0 flex items-center pl-3">
<span class="text-gray-500">$</span>
<span class="text-gray-500">{{ reporting_commodity }}</span>
</div>
<input type="number" class="bordered-field pl-7" name="cost_adjustment" id="cost_adjustment" step="0.01" value="{{ cost_adjustment or '' }}" placeholder="0.00">
</div>

31
austax/util.py Normal file
View File

@ -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 <https://www.gnu.org/licenses/>.
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

View File

@ -16,18 +16,20 @@
from flask import redirect, render_template, request, url_for
from drcr.models import AccountConfiguration, Amount, Posting, Transaction
from drcr.models import AccountConfiguration, Amount, Posting, Transaction, reporting_commodity
from drcr.database import db
from drcr.plugins import render_plugin_template
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)

View File

@ -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()

View File

@ -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(',', '&#x202F;'))
elif len(self.commodity) == 1:
return Markup('{0}{1:,.{dps}f}'.format(self.commodity, self.quantity / (10**AMOUNT_DPS), dps=AMOUNT_DPS).replace(',', '&#x202F;'))
@ -139,7 +140,7 @@ class Amount:
return Markup('<a href="{}" class="hover:text-blue-700 hover:underline">{}</a>{}'.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')

View File

@ -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):

View File

@ -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

View File

@ -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 <https://www.gnu.org/licenses/>.
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

View File

@ -42,7 +42,7 @@
<label for="amount" class="block text-gray-900 pr-4">Balance</label>
<div class="relative shadow-sm">
<div class="pointer-events-none absolute inset-y-0 left-0 flex items-center pl-3">
<span class="text-gray-500">$</span>
<span class="text-gray-500">{{ reporting_commodity }}</span>
</div>
{# TODO: Display existing credit assertion as credit, not as negative debit #}
<input type="number" class="bordered-field pl-7 pr-16" name="amount" step="0.01" value="{{ assertion.balance().quantity_string() if assertion else '' }}" placeholder="0.00">

View File

@ -47,7 +47,8 @@
<td class="amount-dr has-amount py-1 px-1">
<div class="relative shadow-sm">
<div class="pointer-events-none absolute inset-y-0 left-0 flex items-center pl-3">
<span class="text-gray-500">$</span>
{# FIXME: Gracefully handle when the reporting commodity is not a single character #}
<span class="text-gray-500">{{ reporting_commodity }}</span>
</div>
<input type="text" class="bordered-field pl-7" name="amount" oninput="changeAmount(this)">
</div>
@ -84,7 +85,7 @@
<td class="amount-cr has-amount py-1 pl-1">
<div class="relative shadow-sm">
<div class="pointer-events-none absolute inset-y-0 left-0 flex items-center pl-3">
<span class="text-gray-500">$</span>
<span class="text-gray-500">{{ reporting_commodity }}</span>
</div>
<input type="text" class="bordered-field pl-7" name="amount" oninput="changeAmount(this)">
</div>
@ -148,7 +149,7 @@
<td class="amount-dr has-amount py-1 px-1">
<div class="relative shadow-sm">
<div class="pointer-events-none absolute inset-y-0 left-0 flex items-center pl-3">
<span class="text-gray-500">$</span>
<span class="text-gray-500">{{ reporting_commodity }}</span>
</div>
<input type="text" class="bordered-field pl-7" name="amount" value="{{ posting.amount().quantity_string() }}" oninput="changeAmount(this)">
</div>
@ -159,7 +160,7 @@
<td class="amount-cr has-amount py-1 pl-1">
<div class="relative shadow-sm">
<div class="pointer-events-none absolute inset-y-0 left-0 flex items-center pl-3">
<span class="text-gray-500">$</span>
<span class="text-gray-500">{{ reporting_commodity }}</span>
</div>
<input type="text" class="bordered-field pl-7" name="amount" value="{{ (posting.amount()|abs).quantity_string() }}" oninput="changeAmount(this)">
</div>

View File

@ -67,7 +67,7 @@
<thead>
<tr class="border-b border-gray-300">
<th></th>
<th class="py-0.5 pl-1 text-gray-900 font-semibold text-end">$&nbsp;</th>
<th class="py-0.5 pl-1 text-gray-900 font-semibold text-end">{{ reporting_commodity }}&nbsp;</th>
</tr>
</thead>
<tbody>

View File

@ -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']:

View File

@ -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():