Added trial balance.
This commit is contained in:
parent
480e2d2d8f
commit
cca43c68a6
@ -159,3 +159,22 @@ class IncomeExpensesPeriodChooser(PeriodChooser):
|
|||||||
return url_for("accounting.report.income-expenses",
|
return url_for("accounting.report.income-expenses",
|
||||||
currency=self.currency, account=self.account,
|
currency=self.currency, account=self.account,
|
||||||
period=period)
|
period=period)
|
||||||
|
|
||||||
|
|
||||||
|
class TrialBalancePeriodChooser(PeriodChooser):
|
||||||
|
"""The trial balance period chooser."""
|
||||||
|
|
||||||
|
def __init__(self, currency: Currency):
|
||||||
|
"""Constructs the trial balance period chooser."""
|
||||||
|
self.currency: Currency = currency
|
||||||
|
"""The currency."""
|
||||||
|
first: Transaction | None \
|
||||||
|
= Transaction.query.order_by(Transaction.date).first()
|
||||||
|
super().__init__(None if first is None else first.date)
|
||||||
|
|
||||||
|
def _url_for(self, period: Period) -> str:
|
||||||
|
if period.is_default:
|
||||||
|
return url_for("accounting.report.trial-balance-default",
|
||||||
|
currency=self.currency)
|
||||||
|
return url_for("accounting.report.trial-balance",
|
||||||
|
currency=self.currency, period=period)
|
||||||
|
@ -67,6 +67,7 @@ class ReportChooser:
|
|||||||
self.__reports.append(self.__journal)
|
self.__reports.append(self.__journal)
|
||||||
self.__reports.append(self.__ledger)
|
self.__reports.append(self.__ledger)
|
||||||
self.__reports.append(self.__income_expenses)
|
self.__reports.append(self.__income_expenses)
|
||||||
|
self.__reports.append(self.__trial_balance)
|
||||||
for report in self.__reports:
|
for report in self.__reports:
|
||||||
if report.is_active:
|
if report.is_active:
|
||||||
self.current_report = report.title
|
self.current_report = report.title
|
||||||
@ -113,6 +114,20 @@ class ReportChooser:
|
|||||||
return OptionLink(gettext("Income and Expenses"), url,
|
return OptionLink(gettext("Income and Expenses"), url,
|
||||||
self.__active_report == ReportType.INCOME_EXPENSES)
|
self.__active_report == ReportType.INCOME_EXPENSES)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def __trial_balance(self) -> OptionLink:
|
||||||
|
"""Returns the trial balance.
|
||||||
|
|
||||||
|
:return: The trial balance.
|
||||||
|
"""
|
||||||
|
url: str = url_for("accounting.report.trial-balance-default",
|
||||||
|
currency=self.__currency) \
|
||||||
|
if self.__period.is_default \
|
||||||
|
else url_for("accounting.report.trial-balance",
|
||||||
|
currency=self.__currency, period=self.__period)
|
||||||
|
return OptionLink(gettext("Trial Balance"), url,
|
||||||
|
self.__active_report == ReportType.TRIAL_BALANCE)
|
||||||
|
|
||||||
def __iter__(self) -> t.Iterator[OptionLink]:
|
def __iter__(self) -> t.Iterator[OptionLink]:
|
||||||
"""Returns the iteration of the reports.
|
"""Returns the iteration of the reports.
|
||||||
|
|
||||||
|
@ -22,6 +22,7 @@ from abc import ABC, abstractmethod
|
|||||||
from datetime import date
|
from datetime import date
|
||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
|
|
||||||
|
from accounting.locale import gettext
|
||||||
from accounting.models import JournalEntry, Transaction, Account, Currency
|
from accounting.models import JournalEntry, Transaction, Account, Currency
|
||||||
|
|
||||||
|
|
||||||
@ -169,3 +170,39 @@ class IncomeExpensesRow(ReportRow):
|
|||||||
"Expense": self.expense,
|
"Expense": self.expense,
|
||||||
"Balance": self.balance,
|
"Balance": self.balance,
|
||||||
"Note": self.note}
|
"Note": self.note}
|
||||||
|
|
||||||
|
|
||||||
|
class TrialBalanceRow(ReportRow):
|
||||||
|
"""A row in the trial balance."""
|
||||||
|
|
||||||
|
def __init__(self, account: Account | None = None,
|
||||||
|
balance: Decimal | None = None):
|
||||||
|
"""Constructs the row in the trial balance.
|
||||||
|
|
||||||
|
:param account: The account.
|
||||||
|
:param balance: The balance.
|
||||||
|
"""
|
||||||
|
self.is_total: bool = False
|
||||||
|
"""Whether this is the total row."""
|
||||||
|
self.account: Account | None = account
|
||||||
|
"""The date."""
|
||||||
|
self.debit: Decimal | None = None
|
||||||
|
"""The debit amount."""
|
||||||
|
self.credit: Decimal | None = None
|
||||||
|
"""The credit amount."""
|
||||||
|
self.url: str | None = None
|
||||||
|
"""The URL."""
|
||||||
|
if balance is not None:
|
||||||
|
if balance > 0:
|
||||||
|
self.debit = balance
|
||||||
|
if balance < 0:
|
||||||
|
self.credit = -balance
|
||||||
|
|
||||||
|
def as_dict(self) -> dict[str, t.Any]:
|
||||||
|
if self.is_total:
|
||||||
|
return {"Account": gettext("Total"),
|
||||||
|
"Debit": self.debit,
|
||||||
|
"Credit": self.credit}
|
||||||
|
return {"Account": str(self.account).title(),
|
||||||
|
"Debit": self.debit,
|
||||||
|
"Credit": self.credit}
|
||||||
|
@ -28,3 +28,5 @@ class ReportType(Enum):
|
|||||||
"""The ledger."""
|
"""The ledger."""
|
||||||
INCOME_EXPENSES: str = "income-expenses"
|
INCOME_EXPENSES: str = "income-expenses"
|
||||||
"""The income and expenses."""
|
"""The income and expenses."""
|
||||||
|
TRIAL_BALANCE: str = "trial-balance"
|
||||||
|
"""The trial balance."""
|
||||||
|
@ -36,9 +36,10 @@ from accounting.utils.txn_types import TransactionType
|
|||||||
from .option_link import OptionLink
|
from .option_link import OptionLink
|
||||||
from .period import Period
|
from .period import Period
|
||||||
from .period_choosers import PeriodChooser, JournalPeriodChooser, \
|
from .period_choosers import PeriodChooser, JournalPeriodChooser, \
|
||||||
LedgerPeriodChooser, IncomeExpensesPeriodChooser
|
LedgerPeriodChooser, IncomeExpensesPeriodChooser, TrialBalancePeriodChooser
|
||||||
from .report_chooser import ReportChooser
|
from .report_chooser import ReportChooser
|
||||||
from .report_rows import ReportRow, JournalRow, LedgerRow, IncomeExpensesRow
|
from .report_rows import ReportRow, JournalRow, LedgerRow, IncomeExpensesRow, \
|
||||||
|
TrialBalanceRow
|
||||||
from .report_type import ReportType
|
from .report_type import ReportType
|
||||||
|
|
||||||
T = t.TypeVar("T", bound=ReportRow)
|
T = t.TypeVar("T", bound=ReportRow)
|
||||||
@ -629,3 +630,135 @@ class IncomeExpenses(JournalEntryReport[IncomeExpensesRow]):
|
|||||||
return [OptionLink(str(x), get_url(x), x.id == self.account.id)
|
return [OptionLink(str(x), get_url(x), x.id == self.account.id)
|
||||||
for x in Account.query.filter(Account.id.in_(in_use))
|
for x in Account.query.filter(Account.id.in_(in_use))
|
||||||
.order_by(Account.base_code, Account.no).all()]
|
.order_by(Account.base_code, Account.no).all()]
|
||||||
|
|
||||||
|
|
||||||
|
class TrialBalance(JournalEntryReport[TrialBalanceRow]):
|
||||||
|
"""The trial balance."""
|
||||||
|
|
||||||
|
def __init__(self, currency: Currency, period: Period):
|
||||||
|
"""Constructs a trial balance.
|
||||||
|
|
||||||
|
:param currency: The currency.
|
||||||
|
:param period: The period.
|
||||||
|
"""
|
||||||
|
super().__init__(period)
|
||||||
|
self.currency: Currency = currency
|
||||||
|
"""The currency."""
|
||||||
|
self.total_row: TrialBalanceRow | None = None
|
||||||
|
"""The total row."""
|
||||||
|
|
||||||
|
def get_rows(self) -> list[TrialBalanceRow]:
|
||||||
|
rows: list[TrialBalanceRow] = self.__query_balances()
|
||||||
|
self.__populate_url(rows)
|
||||||
|
total_row: TrialBalanceRow = self.__get_total_row(rows)
|
||||||
|
rows.append(total_row)
|
||||||
|
return rows
|
||||||
|
|
||||||
|
def __query_balances(self) -> list[TrialBalanceRow]:
|
||||||
|
"""Queries and returns the balances.
|
||||||
|
|
||||||
|
:return: The balances.
|
||||||
|
"""
|
||||||
|
conditions: list[sa.BinaryExpression] \
|
||||||
|
= [JournalEntry.currency_code == self.currency.code]
|
||||||
|
if self.period.start is not None:
|
||||||
|
conditions.append(Transaction.date >= self.period.start)
|
||||||
|
if self.period.end is not None:
|
||||||
|
conditions.append(Transaction.date <= self.period.end)
|
||||||
|
balance_func: sa.Function = sa.func.sum(sa.case(
|
||||||
|
(JournalEntry.is_debit, JournalEntry.amount),
|
||||||
|
else_=-JournalEntry.amount)).label("balance")
|
||||||
|
select_trial_balance: sa.Select \
|
||||||
|
= sa.select(JournalEntry.account_id, balance_func)\
|
||||||
|
.join(Transaction)\
|
||||||
|
.filter(*conditions)\
|
||||||
|
.group_by(JournalEntry.account_id)
|
||||||
|
balances: list[sa.Row] = db.session.execute(select_trial_balance).all()
|
||||||
|
accounts: dict[int, Account] \
|
||||||
|
= {x.id: x for x in Account.query
|
||||||
|
.filter(Account.id.in_([x.account_id for x in balances])).all()}
|
||||||
|
return [TrialBalanceRow(accounts[x.account_id], x.balance)
|
||||||
|
for x in balances]
|
||||||
|
|
||||||
|
def __populate_url(self, rows: list[TrialBalanceRow]) -> None:
|
||||||
|
"""Populates the URL of the trial balance rows.
|
||||||
|
|
||||||
|
:param rows: The trial balance rows.
|
||||||
|
:return: None.
|
||||||
|
"""
|
||||||
|
def get_url(account: Account) -> str:
|
||||||
|
"""Returns the ledger URL of an account.
|
||||||
|
|
||||||
|
:param account: The account.
|
||||||
|
:return: The ledger URL of the account.
|
||||||
|
"""
|
||||||
|
if self.period.is_default:
|
||||||
|
return url_for("accounting.report.ledger-default",
|
||||||
|
currency=self.currency, account=account)
|
||||||
|
return url_for("accounting.report.ledger",
|
||||||
|
currency=self.currency, account=account,
|
||||||
|
period=self.period)
|
||||||
|
|
||||||
|
for row in rows:
|
||||||
|
row.url = get_url(row.account)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def __get_total_row(rows: list[TrialBalanceRow]) -> TrialBalanceRow:
|
||||||
|
"""Composes the total row.
|
||||||
|
|
||||||
|
:param rows: The rows.
|
||||||
|
:return: None.
|
||||||
|
"""
|
||||||
|
row: TrialBalanceRow = TrialBalanceRow()
|
||||||
|
row.is_total = True
|
||||||
|
row.debit = sum([x.debit for x in rows if x.debit is not None])
|
||||||
|
row.credit = sum([x.credit for x in rows if x.credit is not None])
|
||||||
|
return row
|
||||||
|
|
||||||
|
def populate_rows(self, rows: list[T]) -> None:
|
||||||
|
pass
|
||||||
|
|
||||||
|
@property
|
||||||
|
def csv_field_names(self) -> list[str]:
|
||||||
|
return ["Account", "Debit", "Credit"]
|
||||||
|
|
||||||
|
@property
|
||||||
|
def csv_filename(self) -> str:
|
||||||
|
return f"trial-balance-{self.period.spec}.csv"
|
||||||
|
|
||||||
|
@property
|
||||||
|
def period_chooser(self) -> PeriodChooser:
|
||||||
|
return TrialBalancePeriodChooser(self.currency)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def report_chooser(self) -> ReportChooser:
|
||||||
|
return ReportChooser(ReportType.TRIAL_BALANCE, period=self.period)
|
||||||
|
|
||||||
|
def as_html_page(self) -> str:
|
||||||
|
pagination: Pagination = Pagination[TrialBalanceRow](self.rows)
|
||||||
|
rows: list[TrialBalanceRow] = pagination.list
|
||||||
|
if len(rows) > 0 and rows[-1].is_total:
|
||||||
|
self.total_row = rows[-1]
|
||||||
|
rows = rows[:-1]
|
||||||
|
return render_template("accounting/report/trial-balance.html",
|
||||||
|
list=rows, pagination=pagination, report=self)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def currency_options(self) -> list[OptionLink]:
|
||||||
|
"""Returns the currency options.
|
||||||
|
|
||||||
|
:return: The currency options.
|
||||||
|
"""
|
||||||
|
def get_url(currency: Currency):
|
||||||
|
if self.period.is_default:
|
||||||
|
return url_for("accounting.report.trial-balance-default",
|
||||||
|
currency=currency)
|
||||||
|
return url_for("accounting.report.trial-balance",
|
||||||
|
currency=currency, period=self.period)
|
||||||
|
|
||||||
|
in_use: set[str] = set(db.session.scalars(
|
||||||
|
sa.select(JournalEntry.currency_code)
|
||||||
|
.group_by(JournalEntry.currency_code)).all())
|
||||||
|
return [OptionLink(str(x), get_url(x), x.code == self.currency.code)
|
||||||
|
for x in Currency.query.filter(Currency.code.in_(in_use))
|
||||||
|
.order_by(Currency.code).all()]
|
||||||
|
@ -22,7 +22,7 @@ from flask import Blueprint, request, Response
|
|||||||
from accounting.models import Currency, Account
|
from accounting.models import Currency, Account
|
||||||
from accounting.utils.permission import has_permission, can_view
|
from accounting.utils.permission import has_permission, can_view
|
||||||
from .period import Period
|
from .period import Period
|
||||||
from .reports import Journal, Ledger, IncomeExpenses
|
from .reports import Journal, Ledger, IncomeExpenses, TrialBalance
|
||||||
|
|
||||||
bp: Blueprint = Blueprint("report", __name__)
|
bp: Blueprint = Blueprint("report", __name__)
|
||||||
"""The view blueprint for the reports."""
|
"""The view blueprint for the reports."""
|
||||||
@ -148,3 +148,43 @@ def __get_income_expenses_list(currency: Currency, account: Account,
|
|||||||
if "as" in request.args and request.args["as"] == "csv":
|
if "as" in request.args and request.args["as"] == "csv":
|
||||||
return report.as_csv_download()
|
return report.as_csv_download()
|
||||||
return report.as_html_page()
|
return report.as_html_page()
|
||||||
|
|
||||||
|
|
||||||
|
@bp.get("trial-balance/<currency:currency>",
|
||||||
|
endpoint="trial-balance-default")
|
||||||
|
@has_permission(can_view)
|
||||||
|
def get_default_trial_balance_list(currency: Currency) -> str | Response:
|
||||||
|
"""Returns the trial balance in the default period.
|
||||||
|
|
||||||
|
:param currency: The currency.
|
||||||
|
:return: The trial balance in the default period.
|
||||||
|
"""
|
||||||
|
return __get_trial_balance_list(currency, Period.get_instance())
|
||||||
|
|
||||||
|
|
||||||
|
@bp.get("trial-balance/<currency:currency>/<period:period>",
|
||||||
|
endpoint="trial-balance")
|
||||||
|
@has_permission(can_view)
|
||||||
|
def get_trial_balance_list(currency: Currency, period: Period) \
|
||||||
|
-> str | Response:
|
||||||
|
"""Returns the trial balance.
|
||||||
|
|
||||||
|
:param currency: The currency.
|
||||||
|
:param period: The period.
|
||||||
|
:return: The trial balance in the period.
|
||||||
|
"""
|
||||||
|
return __get_trial_balance_list(currency, period)
|
||||||
|
|
||||||
|
|
||||||
|
def __get_trial_balance_list(currency: Currency, period: Period) \
|
||||||
|
-> str | Response:
|
||||||
|
"""Returns the trial balance.
|
||||||
|
|
||||||
|
:param currency: The currency.
|
||||||
|
:param period: The period.
|
||||||
|
:return: The trial balance in the period.
|
||||||
|
"""
|
||||||
|
report: TrialBalance = TrialBalance(currency, period)
|
||||||
|
if "as" in request.args and request.args["as"] == "csv":
|
||||||
|
return report.as_csv_download()
|
||||||
|
return report.as_html_page()
|
||||||
|
@ -127,6 +127,41 @@ td.accounting-amount {
|
|||||||
font-weight: bolder;
|
font-weight: bolder;
|
||||||
font-style: italic;
|
font-style: italic;
|
||||||
}
|
}
|
||||||
|
.accounting-report-card {
|
||||||
|
padding: 2em 1.5em;
|
||||||
|
margin: 1em;
|
||||||
|
background-color: #F8F9FA;
|
||||||
|
box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19);
|
||||||
|
}
|
||||||
|
.accounting-report-card h2 {
|
||||||
|
border-bottom: thick double slategray;
|
||||||
|
}
|
||||||
|
.accounting-report-row {
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
a.accounting-report-row:hover {
|
||||||
|
color: inherit;
|
||||||
|
}
|
||||||
|
.accounting-trial-balance-row.accounting-trial-balance-header {
|
||||||
|
border-bottom: thick double slategray;
|
||||||
|
font-weight: bolder;
|
||||||
|
font-size: 1.2rem;
|
||||||
|
}
|
||||||
|
.accounting-trial-balance-row.accounting-trial-balance-header .accounting-amount {
|
||||||
|
font-style: normal;
|
||||||
|
}
|
||||||
|
.accounting-trial-balance-row > div {
|
||||||
|
width: 50%;
|
||||||
|
}
|
||||||
|
.accounting-trial-balance-row .accounting-amount {
|
||||||
|
width: 50%;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
.accounting-trial-balance-row.accounting-trial-balance-total {
|
||||||
|
border-top: thick double slategray;
|
||||||
|
font-weight: bolder;
|
||||||
|
font-size: 1.2rem;
|
||||||
|
}
|
||||||
|
|
||||||
/* The Material Design text field (floating form control in Bootstrap) */
|
/* The Material Design text field (floating form control in Bootstrap) */
|
||||||
.accounting-material-text-field {
|
.accounting-material-text-field {
|
||||||
|
155
src/accounting/templates/accounting/report/trial-balance.html
Normal file
155
src/accounting/templates/accounting/report/trial-balance.html
Normal file
@ -0,0 +1,155 @@
|
|||||||
|
{#
|
||||||
|
The Mia! Accounting Flask Project
|
||||||
|
trial-balance.html: The trial balance
|
||||||
|
|
||||||
|
Copyright (c) 2023 imacat.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
|
||||||
|
Author: imacat@mail.imacat.idv.tw (imacat)
|
||||||
|
First written: 2023/3/5
|
||||||
|
#}
|
||||||
|
{% extends "accounting/base.html" %}
|
||||||
|
|
||||||
|
{% block accounting_scripts %}
|
||||||
|
<script src="{{ url_for("accounting.static", filename="js/material-fab-speed-dial.js") }}"></script>
|
||||||
|
<script src="{{ url_for("accounting.static", filename="js/period-chooser.js") }}"></script>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block header %}{% block title %}{{ _("Trial Balance of %(currency)s %(period)s", currency=report.currency, period=report.period.desc|title) }}{% endblock %}{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
|
||||||
|
<div class="btn-group mb-2 d-none d-md-inline-flex">
|
||||||
|
{% if accounting_can_edit() %}
|
||||||
|
<div class="btn-group" role="group">
|
||||||
|
<button type="button" class="btn btn-primary dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false">
|
||||||
|
<i class="fa-solid fa-plus"></i>
|
||||||
|
{{ A_("New") }}
|
||||||
|
</button>
|
||||||
|
<ul class="dropdown-menu">
|
||||||
|
<li>
|
||||||
|
<a class="dropdown-item" href="{{ url_for("accounting.transaction.create", txn_type=report.txn_types.CASH_EXPENSE)|accounting_append_next }}">
|
||||||
|
{{ A_("Cash Expense") }}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a class="dropdown-item" href="{{ url_for("accounting.transaction.create", txn_type=report.txn_types.CASH_INCOME)|accounting_append_next }}">
|
||||||
|
{{ A_("Cash Income") }}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a class="dropdown-item" href="{{ url_for("accounting.transaction.create", txn_type=report.txn_types.TRANSFER)|accounting_append_next }}">
|
||||||
|
{{ A_("Transfer") }}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
{% with report_chooser = report.report_chooser %}
|
||||||
|
{% include "accounting/report/include/report-chooser.html" %}
|
||||||
|
{% endwith %}
|
||||||
|
<div class="btn-group">
|
||||||
|
<button type="button" class="btn btn-primary dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false">
|
||||||
|
<i class="fa-solid fa-money-bill-wave"></i>
|
||||||
|
{{ report.currency.name|title }}
|
||||||
|
</button>
|
||||||
|
<ul class="dropdown-menu">
|
||||||
|
{% for currency in report.currency_options %}
|
||||||
|
<li>
|
||||||
|
<a class="dropdown-item {% if currency.is_active %} active {% endif %}" href="{{ currency.url }}">
|
||||||
|
{{ currency.title|title }}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<button class="btn btn-primary" type="button" data-bs-toggle="modal" data-bs-target="#accounting-period-chooser-modal">
|
||||||
|
<i class="fa-solid fa-calendar-day"></i>
|
||||||
|
{{ report.period.desc|title }}
|
||||||
|
</button>
|
||||||
|
<a class="btn btn-primary" role="button" href="{{ report.csv_uri }}">
|
||||||
|
<i class="fa-solid fa-download"></i>
|
||||||
|
{{ A_("Download") }}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% with txn_types = report.txn_types %}
|
||||||
|
{% include "accounting/include/add-txn-material-fab.html" %}
|
||||||
|
{% endwith %}
|
||||||
|
|
||||||
|
<div class="btn-group btn-actions mb-3 d-md-none">
|
||||||
|
{% with report_chooser = report.report_chooser %}
|
||||||
|
{% include "accounting/report/include/report-chooser.html" %}
|
||||||
|
{% endwith %}
|
||||||
|
<div class="btn-group">
|
||||||
|
<button type="button" class="btn btn-primary dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false">
|
||||||
|
<i class="fa-solid fa-money-bill-wave"></i>
|
||||||
|
</button>
|
||||||
|
<ul class="dropdown-menu">
|
||||||
|
{% for currency in report.currency_options %}
|
||||||
|
<li>
|
||||||
|
<a class="dropdown-item {% if currency.is_active %} active {% endif %}" href="{{ currency.url }}">
|
||||||
|
{{ currency.title|title }}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<button class="btn btn-primary" type="button" data-bs-toggle="modal" data-bs-target="#accounting-period-chooser-modal">
|
||||||
|
<i class="fa-solid fa-calendar-day"></i>
|
||||||
|
{{ A_("Period") }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% with period = report.period, period_chooser = report.period_chooser %}
|
||||||
|
{% include "accounting/report/include/period-chooser.html" %}
|
||||||
|
{% endwith %}
|
||||||
|
|
||||||
|
{% if list %}
|
||||||
|
<div class="accounting-report-card">
|
||||||
|
<div class="d-none d-sm-flex justify-content-center mb-3">
|
||||||
|
<h2 class="text-center">{{ _("Trial Balance of %(currency)s %(period)s", currency=report.currency, period=report.period.desc|title) }}</h2>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="list-group accounting-list-group-stripped accounting-list-group-hover">
|
||||||
|
<div class="list-group-item d-flex justify-content-between accounting-report-row accounting-trial-balance-row accounting-trial-balance-header">
|
||||||
|
<div>{{ A_("Account") }}</div>
|
||||||
|
<div class="d-flex justify-content-between">
|
||||||
|
<div class="accounting-amount">{{ A_("Debit") }}</div>
|
||||||
|
<div class="accounting-amount">{{ A_("Credit") }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% for item in list %}
|
||||||
|
<a class="list-group-item d-flex justify-content-between accounting-report-row accounting-trial-balance-row" href="{{ item.url }}">
|
||||||
|
<div>{{ item.account|title }}</div>
|
||||||
|
<div class="d-flex justify-content-between">
|
||||||
|
<div class="accounting-amount">{{ "" if item.debit is none else item.debit|accounting_format_amount }}</div>
|
||||||
|
<div class="accounting-amount">{{ "" if item.credit is none else item.credit|accounting_format_amount }}</div>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
{% endfor %}
|
||||||
|
<div class="list-group-item d-flex justify-content-between accounting-report-row accounting-trial-balance-row accounting-trial-balance-total">
|
||||||
|
<div>{{ A_("Total") }}</div>
|
||||||
|
<div class="d-flex justify-content-between">
|
||||||
|
<div class="accounting-amount">{{ report.total_row.debit|accounting_format_amount }}</div>
|
||||||
|
<div class="accounting-amount">{{ report.total_row.credit|accounting_format_amount }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% else %}
|
||||||
|
<p>{{ A_("There is no data.") }}</p>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% endblock %}
|
Loading…
Reference in New Issue
Block a user