Added the balance sheet.

This commit is contained in:
依瑪貓 2023-03-07 16:56:44 +08:00
parent 1813ce0cfa
commit 436a4c367f
7 changed files with 830 additions and 0 deletions

View File

@ -0,0 +1,489 @@
# The Mia! Accounting Flask Project.
# Author: imacat@mail.imacat.idv.tw (imacat), 2023/3/4
# 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.
"""The balance sheet.
"""
import csv
import typing as t
from decimal import Decimal
from io import StringIO
from urllib.parse import urlparse, ParseResult, parse_qsl, urlencode, \
urlunparse
import sqlalchemy as sa
from flask import url_for, render_template, Response, request
from accounting import db
from accounting.models import Currency, BaseAccount, Account, Transaction, \
JournalEntry
from accounting.utils.txn_types import TransactionType
from .option_link import OptionLink
from .period import Period
from .period_choosers import BalanceSheetPeriodChooser
from .report_chooser import ReportChooser
from .report_type import ReportType
class BalanceSheetAccount:
"""An account in the balance sheet."""
def __init__(self, account: Account, amount: Decimal, url: str):
"""Constructs an account in the balance sheet.
:param account: The account.
:param amount: The amount.
:param url: The URL to the ledger of the account.
"""
self.account: Account = account
"""The account."""
self.amount: Decimal = amount
"""The amount of the account."""
self.url: str = url
"""The URL to the ledger of the account."""
class BalanceSheetSubsection:
"""A subsection in the balance sheet."""
def __init__(self, title: BaseAccount):
"""Constructs a subsection in the balance sheet.
:param title: The title account.
"""
self.title: BaseAccount = title
"""The title account."""
self.accounts: list[BalanceSheetAccount] = []
"""The accounts in the subsection."""
@property
def total(self) -> Decimal:
"""Returns the total of the subsection.
:return: The total of the subsection.
"""
return sum([x.amount for x in self.accounts])
class BalanceSheetSection:
"""A section in the balance sheet."""
def __init__(self, title: BaseAccount):
"""Constructs a section in the balance sheet.
:param title: The title account.
"""
self.title: BaseAccount = title
"""The title account."""
self.subsections: list[BalanceSheetSubsection] = []
"""The subsections in the section."""
@property
def total(self) -> Decimal:
"""Returns the total of the section.
:return: The total of the section.
"""
return sum([x.total for x in self.subsections])
class CSVHalfRow:
"""A half row in the CSV balance sheet."""
def __init__(self, title: str | None, amount: Decimal | None):
"""The constructs a half row in the CSV balance sheet.
:param title: The title.
:param amount: The amount.
"""
self.title: str | None = title
"""The title."""
self.amount: Decimal | None = amount
"""The amount."""
class CSVRow:
"""A row in the CSV balance sheet."""
def __init__(self):
"""Constructs a row in the CSV balance sheet."""
self.asset_title: str | None = None
"""The title of the asset."""
self.asset_amount: Decimal | None = None
"""The amount of the asset."""
self.liability_title: str | None = None
"""The title of the liability."""
self.liability_amount: Decimal | None = None
"""The amount of the liability."""
@property
def values(self) -> list[str | Decimal | None]:
"""Returns the values of the row.
:return: The values of the row.
"""
return [self.asset_title, self.asset_amount,
self.liability_title, self.liability_amount]
class BalanceSheetPageParams:
"""The HTML parameters of the balance sheet."""
def __init__(self, currency: Currency,
period: Period,
has_data: bool,
assets: BalanceSheetSection,
liabilities: BalanceSheetSection,
owner_s_equity: BalanceSheetSection):
"""Constructs the HTML parameters of the balance sheet.
:param currency: The currency.
:param period: The period.
:param has_data: True if there is any data, or False otherwise.
:param assets: The assets.
:param liabilities: The liabilities.
:param owner_s_equity: The owner's equity.
"""
self.currency: Currency = currency
"""The currency."""
self.period: Period = period
"""The period."""
self.has_data: bool = has_data
"""True if there is any data, or False otherwise."""
self.assets: BalanceSheetSection = assets
"""The assets."""
self.liabilities: BalanceSheetSection = liabilities
"""The liabilities."""
self.owner_s_equity: BalanceSheetSection = owner_s_equity
"""The owner's equity."""
self.period_chooser: BalanceSheetPeriodChooser \
= BalanceSheetPeriodChooser(currency)
"""The period chooser."""
self.report_chooser: ReportChooser \
= ReportChooser(ReportType.BALANCE_SHEET,
currency=currency,
period=period)
"""The report chooser."""
self.txn_types: t.Type[TransactionType] = TransactionType
"""The transaction types."""
@property
def csv_uri(self) -> str:
uri: str = request.full_path if request.query_string \
else request.path
uri_p: ParseResult = urlparse(uri)
params: list[tuple[str, str]] = parse_qsl(uri_p.query)
params = [x for x in params if x[0] != "as"]
params.append(("as", "csv"))
parts: list[str] = list(uri_p)
parts[4] = urlencode(params)
return urlunparse(parts)
@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.balance-sheet-default",
currency=currency)
return url_for("accounting.report.balance-sheet",
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()]
class BalanceSheet:
"""The balance sheet."""
def __init__(self, currency: Currency, period: Period):
"""Constructs a balance sheet.
:param currency: The currency.
:param period: The period.
"""
self.currency: Currency = currency
"""The currency."""
self.period: Period = period
"""The period."""
self.__set_data()
def __set_data(self) -> None:
"""Queries and sets assets, the liabilities, and the owner's equity
sections in the balance sheet.
:return: The assets, the liabilities, and the owner's equity sections.
"""
balances: list[BalanceSheetAccount] = self.__query_balances()
titles: list[BaseAccount] = BaseAccount.query\
.filter(BaseAccount.code.in_({"1", "2", "3"})).all()
subtitles: list[BaseAccount] = BaseAccount.query\
.filter(BaseAccount.code.in_({x.account.base_code[:2]
for x in balances})).all()
sections: dict[str, BalanceSheetSection] \
= {x.code: BalanceSheetSection(x) for x in titles}
subsections: dict[str, BalanceSheetSubsection] \
= {x.code: BalanceSheetSubsection(x) for x in subtitles}
for subsection in subsections.values():
sections[subsection.title.code[0]].subsections.append(subsection)
for balance in balances:
subsections[balance.account.base_code[:2]].accounts.append(balance)
self.__has_data: bool = len(balances) > 0
self.__assets: BalanceSheetSection = sections["1"]
self.__liabilities: BalanceSheetSection = sections["2"]
self.__owner_s_equity: BalanceSheetSection = sections["3"]
def __query_balances(self) -> list[BalanceSheetAccount]:
"""Queries and returns the balances.
:return: The balances.
"""
sub_conditions: list[sa.BinaryExpression] \
= [Account.base_code.startswith(x) for x in {"1", "2", "3"}]
conditions: list[sa.BinaryExpression] \
= [JournalEntry.currency_code == self.currency.code,
sa.or_(*sub_conditions)]
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_balance: sa.Select \
= sa.select(Account.id, Account.base_code, Account.no,
balance_func)\
.join(Transaction).join(Account)\
.filter(*conditions)\
.group_by(Account.id, Account.base_code, Account.no)\
.order_by(Account.base_code, Account.no)
account_balances: list[sa.Row] \
= db.session.execute(select_balance).all()
account_by_id: dict[int, Account] \
= {x.id: x for x in Account.query
.filter(sa.or_(Account.id.in_({x.id for x in account_balances}),
Account.base_code == "3351",
Account.base_code == "3353")).all()}
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)
balances: list[BalanceSheetAccount] \
= [BalanceSheetAccount(account=account_by_id[x.id],
amount=x.balance,
url=get_url(account_by_id[x.id]))
for x in account_balances]
self.__add_accumulated(balances, list(account_by_id.values()))
self.__add_current_period(balances, list(account_by_id.values()))
for balance in balances:
if not balance.account.base_code.startswith("1"):
balance.amount = -balance.amount
return balances
def __add_accumulated(self, balances: list[BalanceSheetAccount],
accounts: list[Account]) -> None:
"""Adds the accumulated profit or loss to the balances.
:param balances: The accounts on the balance sheet.
:param accounts: The accounts.
:return: None.
"""
code: str = "3351-001"
amount: Decimal | None = self.__query_accumulated()
url: str = url_for("accounting.report.income-statement",
currency=self.currency, period=self.period.before)
self.__add_owner_s_equity(balances, accounts, code, amount, url)
def __query_accumulated(self) -> Decimal | None:
"""Queries and returns the accumulated profit or loss.
:return: The accumulated profit or loss.
"""
if self.period.start is None:
return None
conditions: list[sa.BinaryExpression] \
= [JournalEntry.currency_code == self.currency.code,
Transaction.date < self.period.start]
conditions.extend([sa.not_(Account.base_code.startswith(x))
for x in {"1", "2"}])
balance_func: sa.Function = sa.func.sum(sa.case(
(JournalEntry.is_debit, JournalEntry.amount),
else_=-JournalEntry.amount)).label("balance")
select_balance: sa.Select = sa.select(balance_func)\
.join(Transaction).join(Account).filter(*conditions)
return db.session.scalar(select_balance)
def __add_current_period(self, balances: list[BalanceSheetAccount],
accounts: list[Account]) -> None:
"""Adds the accumulated profit or loss to the balances.
:param balances: The accounts on the balance sheet.
:param accounts: The accounts.
:return: None.
"""
code: str = "3353-001"
amount: Decimal | None = self.__query_currency_period()
url: str = url_for("accounting.report.income-statement",
currency=self.currency, period=self.period)
self.__add_owner_s_equity(balances, accounts, code, amount, url)
def __query_currency_period(self) -> Decimal | None:
"""Queries and returns the net income or loss for current period.
:return: The net income or loss for current period.
"""
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)
conditions.extend([sa.not_(Account.base_code.startswith(x))
for x in {"1", "2"}])
balance_func: sa.Function = sa.func.sum(sa.case(
(JournalEntry.is_debit, JournalEntry.amount),
else_=-JournalEntry.amount)).label("balance")
select_balance: sa.Select = sa.select(balance_func)\
.join(Transaction).join(Account).filter(*conditions)
return db.session.scalar(select_balance)
@staticmethod
def __add_owner_s_equity(balances: list[BalanceSheetAccount],
accounts: list[Account],
code: str,
amount: Decimal | None,
url: str):
"""Adds an owner's equity balance.
:param balances: The accounts on the balance sheet.
:param accounts: The accounts.
:param code: The code of the account to add.
:param amount: The amount.
:return: None.
"""
# There is an existing balance.
balance_by_code: dict[str, BalanceSheetAccount] \
= {x.account.code: x for x in balances}
if code in balance_by_code:
balance: BalanceSheetAccount = balance_by_code[code]
balance.url = url
if amount is not None:
balance.amount = balance.amount + amount
return
# Add a new balance
if amount is None:
return
account_by_code: dict[str, Account] = {x.code: x for x in accounts}
balances.append(BalanceSheetAccount(
account=account_by_code[code],
amount=amount,
url=url))
def csv(self) -> Response:
"""Returns the report as CSV for download.
:return: The response of the report for download.
"""
filename: str = "balance-sheet-{currency}-{period}.csv"\
.format(currency=self.currency.code, period=self.period.spec)
rows: list[CSVRow] = self.__get_csv_rows()
with StringIO() as fp:
writer = csv.writer(fp)
writer.writerows([x.values for x in rows])
fp.seek(0)
response: Response = Response(fp.read(), mimetype="text/csv")
response.headers["Content-Disposition"] \
= f"attachment; filename={filename}"
return response
def __get_csv_rows(self) -> list[CSVRow]:
"""Composes and returns the CSV rows.
:return: The CSV rows.
"""
asset_rows: list[CSVHalfRow] = self.__section_csv_rows(self.__assets)
liability_rows: list[CSVHalfRow] = []
liability_rows.extend(self.__section_csv_rows(self.__liabilities))
liability_rows.append(CSVHalfRow("Total", self.__liabilities.total))
liability_rows.append(CSVHalfRow(None, None))
liability_rows.extend(self.__section_csv_rows(self.__owner_s_equity))
liability_rows.append(CSVHalfRow("Total", self.__owner_s_equity.total))
rows: list[CSVRow] = [CSVRow() for _ in
range(max(len(asset_rows), len(liability_rows)))]
for i in range(len(rows)):
if i < len(asset_rows):
rows[i].asset_title = asset_rows[i].title
rows[i].asset_amount = asset_rows[i].amount
if i < len(liability_rows) and liability_rows[i].title is not None:
rows[i].liability_title = liability_rows[i].title
rows[i].liability_amount = liability_rows[i].amount
total: CSVRow = CSVRow()
total.asset_title = "Total"
total.asset_amount = self.__assets.total
total.liability_title = "Total"
total.liability_amount \
= self.__liabilities.total + self.__owner_s_equity.total
rows.append(total)
return rows
@staticmethod
def __section_csv_rows(section: BalanceSheetSection) -> list[CSVHalfRow]:
"""Gathers the CSV rows for a section.
:param section: The section.
:return: The CSV rows for the section.
"""
rows: list[CSVHalfRow] \
= [CSVHalfRow(section.title.title.title(), None)]
for subsection in section.subsections:
rows.append(CSVHalfRow(f" {subsection.title.title.title()}", None))
for account in subsection.accounts:
rows.append(CSVHalfRow(f" {str(account.account).title()}",
account.amount))
return rows
def html(self) -> str:
"""Composes and returns the report as HTML.
:return: The report as HTML.
"""
params: BalanceSheetPageParams = BalanceSheetPageParams(
currency=self.currency,
period=self.period,
has_data=self.__has_data,
assets=self.__assets,
liabilities=self.__liabilities,
owner_s_equity=self.__owner_s_equity)
return render_template("accounting/report/balance-sheet.html",
report=params)

View File

@ -197,3 +197,22 @@ class IncomeStatementPeriodChooser(PeriodChooser):
currency=self.currency)
return url_for("accounting.report.income-statement",
currency=self.currency, period=period)
class BalanceSheetPeriodChooser(PeriodChooser):
"""The balance sheet period chooser."""
def __init__(self, currency: Currency):
"""Constructs the balance sheet 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.balance-sheet-default",
currency=self.currency)
return url_for("accounting.report.balance-sheet",
currency=self.currency, period=period)

View File

@ -70,6 +70,7 @@ class ReportChooser:
self.__reports.append(self.__income_expenses)
self.__reports.append(self.__trial_balance)
self.__reports.append(self.__income_statement)
self.__reports.append(self.__balance_sheet)
for report in self.__reports:
if report.is_active:
self.current_report = report.title
@ -147,6 +148,20 @@ class ReportChooser:
return OptionLink(gettext("Income Statement"), url,
self.__active_report == ReportType.INCOME_STATEMENT)
@property
def __balance_sheet(self) -> OptionLink:
"""Returns the balance sheet.
:return: The balance sheet.
"""
url: str = url_for("accounting.report.balance-sheet-default",
currency=self.__currency) \
if self.__period.is_default \
else url_for("accounting.report.balance-sheet",
currency=self.__currency, period=self.__period)
return OptionLink(gettext("Balance Sheet"), url,
self.__active_report == ReportType.BALANCE_SHEET)
def __iter__(self) -> t.Iterator[OptionLink]:
"""Returns the iteration of the reports.

View File

@ -32,3 +32,5 @@ class ReportType(Enum):
"""The trial balance."""
INCOME_STATEMENT: str = "income-statement"
"""The income statement."""
BALANCE_SHEET: str = "balance-sheet"
"""The balance sheet."""

View File

@ -21,6 +21,7 @@ from flask import Blueprint, request, Response
from accounting.models import Currency, Account
from accounting.utils.permission import has_permission, can_view
from .balance_sheet import BalanceSheet
from .period import Period
from .reports import Journal, Ledger, IncomeExpenses, TrialBalance, \
IncomeStatement
@ -229,3 +230,43 @@ def __get_income_statement_list(currency: Currency, period: Period) \
if "as" in request.args and request.args["as"] == "csv":
return report.csv()
return report.html()
@bp.get("balance-sheet/<currency:currency>",
endpoint="balance-sheet-default")
@has_permission(can_view)
def get_default_balance_sheet_list(currency: Currency) -> str | Response:
"""Returns the balance sheet in the default period.
:param currency: The currency.
:return: The balance sheet in the default period.
"""
return __get_balance_sheet_list(currency, Period.get_instance())
@bp.get("balance-sheet/<currency:currency>/<period:period>",
endpoint="balance-sheet")
@has_permission(can_view)
def get_balance_sheet_list(currency: Currency, period: Period) \
-> str | Response:
"""Returns the balance sheet.
:param currency: The currency.
:param period: The period.
:return: The balance sheet in the period.
"""
return __get_balance_sheet_list(currency, period)
def __get_balance_sheet_list(currency: Currency, period: Period) \
-> str | Response:
"""Returns the balance sheet.
:param currency: The currency.
:param period: The period.
:return: The balance sheet in the period.
"""
report: BalanceSheet = BalanceSheet(currency, period)
if "as" in request.args and request.args["as"] == "csv":
return report.csv()
return report.html()

View File

@ -207,6 +207,27 @@ a.accounting-report-table-row {
.accounting-income-statement-total:last-child {
margin-bottom: 0;
}
.accounting-balance-sheet-section, .accounting-balance-sheet-total {
font-size: 1.2rem;
font-weight: bolder;
}
.accounting-balance-sheet-section {
border-bottom: thick double darkslategray;
}
.accounting-balance-sheet-total {
border-top: thick double darkslategray;
}
.accounting-balance-sheet-subtotal {
font-size: 1.1rem;
font-weight: bolder;
border-top: thick double darkslategray;
}
.accounting-balance-sheet-account {
margin-left: 0.5rem;
}
.accounting-balance-sheet-total .accounting-amount, .accounting-balance-sheet-subtotal, .accounting-amount {
font-style: italic;
}
/* The accounting report */
.accounting-mobile-journal-credit {

View File

@ -0,0 +1,243 @@
{#
The Mia! Accounting Flask Project
income-statement.html: The income statement
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/7
#}
{% 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 %}{{ _("Balance Sheet of %(currency)s %(period)s", currency=report.currency.name|title, 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 }}
</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 }}
</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 report.has_data %}
<div class="accounting-sheet">
<div class="d-none d-sm-flex justify-content-center mb-3">
<h2 class="text-center">{{ _("Balance Sheet of %(currency)s %(period)s", currency=report.currency.name|title, period=report.period.desc|title) }}</h2>
</div>
<div class="row accounting-report-table accounting-balance-sheet-table">
<div class="col-sm-6">
{% if report.assets.subsections %}
<div class="accounting-report-table-row accounting-balance-sheet-section">
<div>{{ report.assets.title.title|title }}</div>
</div>
<div class="accounting-report-table-body">
{% for subsection in report.assets.subsections %}
<div class="accounting-report-table-row accounting-balance-sheet-subsection">
<div>
<span class="d-none d-md-inline">{{ subsection.title.code }}</span>
{{ subsection.title.title|title }}
</div>
</div>
{% for account in subsection.accounts %}
<a class="d-flex justify-content-between accounting-report-table-row accounting-balance-sheet-account" href="{{ account.url }}">
<div>
<span class="d-none d-md-inline">{{ account.account.code }}</span>
{{ account.account.title|title }}
</div>
<div class="accounting-amount">{{ account.amount|accounting_format_amount }}</div>
</a>
{% endfor %}
{% endfor %}
</div>
<div class="d-md-none d-flex justify-content-between accounting-report-table-row accounting-balance-sheet-total">
<div>{{ A_("Total") }}</div>
<div class="accounting-amount">{{ report.assets.total|accounting_format_amount }}</div>
</div>
{% endif %}
</div>
<div class="col-sm-6">
{% if report.liabilities.subsections %}
<div class="accounting-report-table-row accounting-balance-sheet-section">
<div>{{ report.liabilities.title.title|title }}</div>
</div>
<div class="accounting-report-table-body">
{% for subsection in report.liabilities.subsections %}
<div class="accounting-report-table-row accounting-balance-sheet-subsection">
<div>
<span class="d-none d-md-inline">{{ subsection.title.code }}</span>
{{ subsection.title.title|title }}
</div>
</div>
{% for account in subsection.accounts %}
<a class="d-flex justify-content-between accounting-report-table-row accounting-balance-sheet-account" href="{{ account.url }}">
<div>
<span class="d-none d-md-inline">{{ account.account.code }}</span>
{{ account.account.title|title }}
</div>
<div class="accounting-amount">{{ account.amount|accounting_format_amount }}</div>
</a>
{% endfor %}
{% endfor %}
</div>
<div class="d-flex justify-content-between accounting-report-table-row accounting-balance-sheet-subtotal">
<div>{{ A_("Total") }}</div>
<div class="accounting-amount">{{ report.liabilities.total|accounting_format_amount }}</div>
</div>
{% endif %}
{% if report.owner_s_equity.subsections %}
<div class="accounting-report-table-row accounting-balance-sheet-section">
<div>{{ report.owner_s_equity.title.title|title }}</div>
</div>
<div class="accounting-report-table-body">
{% for subsection in report.owner_s_equity.subsections %}
<div class="accounting-report-table-row accounting-balance-sheet-subsection">
<div>
<span class="d-none d-md-inline">{{ subsection.title.code }}</span>
{{ subsection.title.title|title }}
</div>
</div>
{% for account in subsection.accounts %}
<a class="d-flex justify-content-between accounting-report-table-row accounting-balance-sheet-account" href="{{ account.url }}">
<div>
<span class="d-none d-md-inline">{{ account.account.code }}</span>
{{ account.account.title|title }}
</div>
<div class="accounting-amount">{{ account.amount|accounting_format_amount }}</div>
</a>
{% endfor %}
{% endfor %}
</div>
<div class="d-flex justify-content-between accounting-report-table-row accounting-balance-sheet-subtotal">
<div>{{ A_("Total") }}</div>
<div class="accounting-amount">{{ report.owner_s_equity.total|accounting_format_amount }}</div>
</div>
{% endif %}
<div class="d-md-none d-flex justify-content-between accounting-report-table-row accounting-balance-sheet-total">
<div>{{ A_("Total") }}</div>
<div class="accounting-amount">{{ (report.liabilities.total + report.owner_s_equity.total)|accounting_format_amount }}</div>
</div>
</div>
</div>
<div class="row d-none d-md-flex">
<div class="col-sm-6">
<div class="d-flex justify-content-between accounting-balance-sheet-total">
<div>{{ A_("Total") }}</div>
<div class="accounting-amount">{{ report.assets.total|accounting_format_amount }}</div>
</div>
</div>
<div class="col-sm-6">
<div class="d-flex justify-content-between accounting-balance-sheet-total">
<div>{{ A_("Total") }}</div>
<div class="accounting-amount">{{ (report.liabilities.total + report.owner_s_equity.total)|accounting_format_amount }}</div>
</div>
</div>
</div>
</div>
{% else %}
<p>{{ A_("There is no data.") }}</p>
{% endif %}
{% endblock %}