Compare commits

...

6 Commits

19 changed files with 74 additions and 60 deletions

View File

@ -13,12 +13,21 @@ module for the Flask_ applications.
Install Install
======= =======
Install the latest source from the Install ``mia-accounting`` with ``pip``.
`Mia! Accounting repository`_.
:: ::
pip install git+https://github.com/imacat/mia-accounting.git pip install mia-accounting
Usage
=====
This needs to be done. Currently, you can refer to the test site
located in the test directory on the `Mia! Accounting repository`_.
The test site is running as the
`live demonstration for Mia! Accounting`_.
Copyright Copyright
@ -48,3 +57,4 @@ Authors
.. _Flask: https://flask.palletsprojects.com .. _Flask: https://flask.palletsprojects.com
.. _Mia! Accounting repository: https://github.com/imacat/mia-accounting .. _Mia! Accounting repository: https://github.com/imacat/mia-accounting
.. _live demonstration for Mia! Accounting: https://accounting.imacat.idv.tw

View File

@ -47,7 +47,6 @@ def init_app(app: Flask, user_utils: UserUtilityInterface,
init_user_utils(user_utils) init_user_utils(user_utils)
bp: Blueprint = Blueprint("accounting", __name__, bp: Blueprint = Blueprint("accounting", __name__,
url_prefix=url_prefix,
template_folder="templates", template_folder="templates",
static_folder="static") static_folder="static")
@ -84,9 +83,9 @@ def init_app(app: Flask, user_utils: UserUtilityInterface,
journal_entry.init_app(app, bp) journal_entry.init_app(app, bp)
from . import report from . import report
report.init_app(app, bp) report.init_app(app, url_prefix)
from . import option from . import option
option.init_app(bp) option.init_app(bp)
app.register_blueprint(bp) app.register_blueprint(bp, url_prefix=url_prefix)

View File

@ -77,6 +77,7 @@ def get_selectable_original_line_items(
.options(selectinload(JournalEntryLineItem.currency), .options(selectinload(JournalEntryLineItem.currency),
selectinload(JournalEntryLineItem.account), selectinload(JournalEntryLineItem.account),
selectinload(JournalEntryLineItem.journal_entry)).all() selectinload(JournalEntryLineItem.journal_entry)).all()
line_items.reverse()
for line_item in line_items: for line_item in line_items:
line_item.net_balance = line_item.amount \ line_item.net_balance = line_item.amount \
if net_balances[line_item.id] is None \ if net_balances[line_item.id] is None \

View File

@ -235,4 +235,4 @@ def __get_default_page_uri() -> str:
:return: The URI for the default page. :return: The URI for the default page.
""" """
return url_for("accounting.report.default") return url_for("accounting-report.default")

View File

@ -17,14 +17,14 @@
"""The report management. """The report management.
""" """
from flask import Flask, Blueprint from flask import Flask
def init_app(app: Flask, bp: Blueprint) -> None: def init_app(app: Flask, url_prefix: str) -> None:
"""Initialize the application. """Initialize the application.
:param app: The Flask application. :param app: The Flask application.
:param bp: The blueprint of the accounting application. :param url_prefix: The URL prefix of the accounting application.
:return: None. :return: None.
""" """
from .converters import PeriodConverter, IncomeExpensesAccountConverter from .converters import PeriodConverter, IncomeExpensesAccountConverter
@ -32,4 +32,4 @@ def init_app(app: Flask, bp: Blueprint) -> None:
app.url_map.converters["ieAccount"] = IncomeExpensesAccountConverter app.url_map.converters["ieAccount"] = IncomeExpensesAccountConverter
from .views import bp as report_bp from .views import bp as report_bp
bp.register_blueprint(report_bp, url_prefix="/reports") app.register_blueprint(report_bp, url_prefix=url_prefix)

View File

@ -68,9 +68,9 @@ class ReportChooser:
"""The title of the current report.""" """The title of the current report."""
self.is_search: bool = active_report == ReportType.SEARCH self.is_search: bool = active_report == ReportType.SEARCH
"""Whether the current report is the search page.""" """Whether the current report is the search page."""
self.__reports.append(self.__journal)
self.__reports.append(self.__ledger)
self.__reports.append(self.__income_expenses) self.__reports.append(self.__income_expenses)
self.__reports.append(self.__ledger)
self.__reports.append(self.__journal)
self.__reports.append(self.__trial_balance) self.__reports.append(self.__trial_balance)
self.__reports.append(self.__income_statement) self.__reports.append(self.__income_statement)
self.__reports.append(self.__balance_sheet) self.__reports.append(self.__balance_sheet)
@ -80,28 +80,6 @@ class ReportChooser:
if self.is_search: if self.is_search:
self.current_report = gettext("Search") self.current_report = gettext("Search")
@property
def __journal(self) -> OptionLink:
"""Returns the journal.
:return: The journal.
"""
return OptionLink(gettext("Journal"), journal_url(self.__period),
self.__active_report == ReportType.JOURNAL,
fa_icon="fa-solid fa-book")
@property
def __ledger(self) -> OptionLink:
"""Returns the ledger.
:return: The ledger.
"""
return OptionLink(gettext("Ledger"),
ledger_url(self.__currency, self.__account,
self.__period),
self.__active_report == ReportType.LEDGER,
fa_icon="fa-solid fa-clipboard")
@property @property
def __income_expenses(self) -> OptionLink: def __income_expenses(self) -> OptionLink:
"""Returns the income and expenses log. """Returns the income and expenses log.
@ -118,6 +96,28 @@ class ReportChooser:
self.__active_report == ReportType.INCOME_EXPENSES, self.__active_report == ReportType.INCOME_EXPENSES,
fa_icon="fa-solid fa-money-bill-wave") fa_icon="fa-solid fa-money-bill-wave")
@property
def __ledger(self) -> OptionLink:
"""Returns the ledger.
:return: The ledger.
"""
return OptionLink(gettext("Ledger"),
ledger_url(self.__currency, self.__account,
self.__period),
self.__active_report == ReportType.LEDGER,
fa_icon="fa-solid fa-clipboard")
@property
def __journal(self) -> OptionLink:
"""Returns the journal.
:return: The journal.
"""
return OptionLink(gettext("Journal"), journal_url(self.__period),
self.__active_report == ReportType.JOURNAL,
fa_icon="fa-solid fa-book")
@property @property
def __trial_balance(self) -> OptionLink: def __trial_balance(self) -> OptionLink:
"""Returns the trial balance. """Returns the trial balance.

View File

@ -34,8 +34,8 @@ def journal_url(period: Period) \
:return: The URL of the journal. :return: The URL of the journal.
""" """
if period.is_default: if period.is_default:
return url_for("accounting.report.journal-default") return url_for("accounting-report.journal-default")
return url_for("accounting.report.journal", period=period) return url_for("accounting-report.journal", period=period)
def ledger_url(currency: Currency, account: Account, period: Period) \ def ledger_url(currency: Currency, account: Account, period: Period) \
@ -48,9 +48,9 @@ def ledger_url(currency: Currency, account: Account, period: Period) \
:return: The URL of the ledger. :return: The URL of the ledger.
""" """
if period.is_default: if period.is_default:
return url_for("accounting.report.ledger-default", return url_for("accounting-report.ledger-default",
currency=currency, account=account) currency=currency, account=account)
return url_for("accounting.report.ledger", return url_for("accounting-report.ledger",
currency=currency, account=account, currency=currency, account=account,
period=period) period=period)
@ -67,11 +67,11 @@ def income_expenses_url(currency: Currency, account: CurrentAccount,
if currency.code == default_currency_code() \ if currency.code == default_currency_code() \
and account.code == options.default_ie_account_code \ and account.code == options.default_ie_account_code \
and period.is_default: and period.is_default:
return url_for("accounting.report.default") return url_for("accounting-report.default")
if period.is_default: if period.is_default:
return url_for("accounting.report.income-expenses-default", return url_for("accounting-report.income-expenses-default",
currency=currency, account=account) currency=currency, account=account)
return url_for("accounting.report.income-expenses", return url_for("accounting-report.income-expenses",
currency=currency, account=account, currency=currency, account=account,
period=period) period=period)
@ -84,9 +84,9 @@ def trial_balance_url(currency: Currency, period: Period) -> str:
:return: The URL of the trial balance. :return: The URL of the trial balance.
""" """
if period.is_default: if period.is_default:
return url_for("accounting.report.trial-balance-default", return url_for("accounting-report.trial-balance-default",
currency=currency) currency=currency)
return url_for("accounting.report.trial-balance", return url_for("accounting-report.trial-balance",
currency=currency, period=period) currency=currency, period=period)
@ -98,9 +98,9 @@ def income_statement_url(currency: Currency, period: Period) -> str:
:return: The URL of the income statement. :return: The URL of the income statement.
""" """
if period.is_default: if period.is_default:
return url_for("accounting.report.income-statement-default", return url_for("accounting-report.income-statement-default",
currency=currency) currency=currency)
return url_for("accounting.report.income-statement", return url_for("accounting-report.income-statement",
currency=currency, period=period) currency=currency, period=period)
@ -112,7 +112,7 @@ def balance_sheet_url(currency: Currency, period: Period) -> str:
:return: The URL of the balance sheet. :return: The URL of the balance sheet.
""" """
if period.is_default: if period.is_default:
return url_for("accounting.report.balance-sheet-default", return url_for("accounting-report.balance-sheet-default",
currency=currency) currency=currency)
return url_for("accounting.report.balance-sheet", return url_for("accounting-report.balance-sheet",
currency=currency, period=period) currency=currency, period=period)

View File

@ -30,7 +30,7 @@ from .reports import Journal, Ledger, IncomeExpenses, TrialBalance, \
IncomeStatement, BalanceSheet, Search IncomeStatement, BalanceSheet, Search
from .template_filters import format_amount from .template_filters import format_amount
bp: Blueprint = Blueprint("report", __name__) bp: Blueprint = Blueprint("accounting-report", __name__)
"""The view blueprint for the reports.""" """The view blueprint for the reports."""
bp.add_app_template_filter(format_amount, "accounting_report_format_amount") bp.add_app_template_filter(format_amount, "accounting_report_format_amount")

View File

@ -316,6 +316,10 @@ a.accounting-report-table-row {
} }
/* The description editor */ /* The description editor */
.accounting-description-editor-buttons {
max-height: 7rem;
overflow-y: scroll;
}
.accounting-description-editor-buttons .btn { .accounting-description-editor-buttons .btn {
margin-bottom: 0.3rem; margin-bottom: 0.3rem;
} }

View File

@ -28,7 +28,7 @@ First written: 2023/1/26
</span> </span>
<ul class="dropdown-menu"> <ul class="dropdown-menu">
<li> <li>
<a class="dropdown-item {% if request.endpoint and request.endpoint.startswith("accounting.report.") %} active {% endif %}" href="{{ url_for("accounting.report.default") }}"> <a class="dropdown-item {% if request.endpoint and request.endpoint.startswith("accounting-report.") %} active {% endif %}" href="{{ url_for("accounting-report.default") }}">
<i class="fa-solid fa-book"></i> <i class="fa-solid fa-book"></i>
{{ A_("Reports") }} {{ A_("Reports") }}
</a> </a>

View File

@ -23,6 +23,6 @@ First written: 2023/2/25
{% block header %}{% block title %}{{ A_("Add a New Cash Disbursement Journal Entry") }}{% endblock %}{% endblock %} {% block header %}{% block title %}{{ A_("Add a New Cash Disbursement Journal Entry") }}{% endblock %}{% endblock %}
{% block back_url %}{{ request.args.get("next") or url_for("accounting.report.default") }}{% endblock %} {% block back_url %}{{ request.args.get("next") or url_for("accounting-report.default") }}{% endblock %}
{% block action_url %}{{ url_for("accounting.journal-entry.store", journal_entry_type=journal_entry_type) }}{% endblock %} {% block action_url %}{{ url_for("accounting.journal-entry.store", journal_entry_type=journal_entry_type) }}{% endblock %}

View File

@ -181,10 +181,10 @@ First written: 2023/2/28
</div> </div>
{# The suggested accounts #} {# The suggested accounts #}
<div class="mt-3"> <div class="mt-3 accounting-description-editor-buttons">
<button id="accounting-description-editor-{{ description_editor.debit_credit }}-account-confirmed" class="btn btn-primary mb-1 d-none" type="button"></button> <button id="accounting-description-editor-{{ description_editor.debit_credit }}-account-confirmed" class="btn btn-primary mb-1 d-none" type="button"></button>
{% for account in description_editor.accounts %} {% for account in description_editor.accounts %}
<button class="btn btn-outline-primary mb-1 d-none accounting-description-editor-{{ description_editor.debit_credit }}-account {% if account.is_need_offset %} accounting-account-is-need-offset {% endif %}" type="button" data-code="{{ account.code }}" data-text="{{ account }}"> <button class="btn btn-outline-primary d-none accounting-description-editor-{{ description_editor.debit_credit }}-account {% if account.is_need_offset %} accounting-account-is-need-offset {% endif %}" type="button" data-code="{{ account.code }}" data-text="{{ account }}">
{{ account }} {{ account }}
</button> </button>
{% endfor %} {% endfor %}

View File

@ -26,7 +26,7 @@ First written: 2023/2/26
{% block content %} {% block content %}
<div class="mb-3 accounting-toolbar"> <div class="mb-3 accounting-toolbar">
<a class="btn btn-primary" role="button" href="{{ url_for("accounting.report.default")|accounting_or_next }}"> <a class="btn btn-primary" role="button" href="{{ url_for("accounting-report.default")|accounting_or_next }}">
<i class="fa-solid fa-circle-chevron-left"></i> <i class="fa-solid fa-circle-chevron-left"></i>
<span class="d-none d-md-inline">{{ A_("Back") }}</span> <span class="d-none d-md-inline">{{ A_("Back") }}</span>
</a> </a>

View File

@ -31,7 +31,7 @@ First written: 2023/2/26
{% block content %} {% block content %}
<div class="mb-3 accounting-toolbar"> <div class="mb-3 accounting-toolbar">
<a class="btn btn-primary" role="button" href="{{ url_for("accounting.report.default")|accounting_or_next }}"> <a class="btn btn-primary" role="button" href="{{ url_for("accounting-report.default")|accounting_or_next }}">
<i class="fa-solid fa-circle-chevron-left"></i> <i class="fa-solid fa-circle-chevron-left"></i>
<span class="d-none d-md-inline">{{ A_("Back") }}</span> <span class="d-none d-md-inline">{{ A_("Back") }}</span>
</a> </a>

View File

@ -23,6 +23,6 @@ First written: 2023/2/25
{% block header %}{% block title %}{{ A_("Add a New Cash Receipt Journal Entry") }}{% endblock %}{% endblock %} {% block header %}{% block title %}{{ A_("Add a New Cash Receipt Journal Entry") }}{% endblock %}{% endblock %}
{% block back_url %}{{ request.args.get("next") or url_for("accounting.report.default") }}{% endblock %} {% block back_url %}{{ request.args.get("next") or url_for("accounting-report.default") }}{% endblock %}
{% block action_url %}{{ url_for("accounting.journal-entry.store", journal_entry_type=journal_entry_type) }}{% endblock %} {% block action_url %}{{ url_for("accounting.journal-entry.store", journal_entry_type=journal_entry_type) }}{% endblock %}

View File

@ -23,6 +23,6 @@ First written: 2023/2/25
{% block header %}{% block title %}{{ A_("Add a New Transfer Journal Entry") }}{% endblock %}{% endblock %} {% block header %}{% block title %}{{ A_("Add a New Transfer Journal Entry") }}{% endblock %}{% endblock %}
{% block back_url %}{{ request.args.get("next") or url_for("accounting.report.default") }}{% endblock %} {% block back_url %}{{ request.args.get("next") or url_for("accounting-report.default") }}{% endblock %}
{% block action_url %}{{ url_for("accounting.journal-entry.store", journal_entry_type=journal_entry_type) }}{% endblock %} {% block action_url %}{{ url_for("accounting.journal-entry.store", journal_entry_type=journal_entry_type) }}{% endblock %}

View File

@ -19,7 +19,7 @@ search-modal.html: The search modal
Author: imacat@mail.imacat.idv.tw (imacat) Author: imacat@mail.imacat.idv.tw (imacat)
First written: 2023/3/8 First written: 2023/3/8
#} #}
<form action="{{ url_for("accounting.report.search") }}" method="get" role="search" aria-labelledby="accounting-search-modal-label"> <form action="{{ url_for("accounting-report.search") }}" method="get" role="search" aria-labelledby="accounting-search-modal-label">
<div class="modal fade" id="accounting-search-modal" tabindex="-1" aria-labelledby="accounting-search-modal-label" aria-hidden="true"> <div class="modal fade" id="accounting-search-modal" tabindex="-1" aria-labelledby="accounting-search-modal-label" aria-hidden="true">
<div class="modal-dialog"> <div class="modal-dialog">
<div class="modal-content"> <div class="modal-content">

View File

@ -118,7 +118,7 @@ First written: 2023/3/8
</button> </button>
{% endif %} {% endif %}
{% if use_search %} {% if use_search %}
<form class="btn btn-primary d-flex input-group" action="{{ url_for("accounting.report.search") }}" method="get" role="search" aria-labelledby="accounting-toolbar-search-label"> <form class="btn btn-primary d-flex input-group" action="{{ url_for("accounting-report.search") }}" method="get" role="search" aria-labelledby="accounting-toolbar-search-label">
<input id="accounting-toolbar-search" class="form-control form-control-sm" type="search" name="q" value="{{ request.args.q }}" placeholder=" " required="required"> <input id="accounting-toolbar-search" class="form-control form-control-sm" type="search" name="q" value="{{ request.args.q }}" placeholder=" " required="required">
<label id="accounting-toolbar-search-label" for="accounting-toolbar-search" class="input-group-text"> <label id="accounting-toolbar-search-label" for="accounting-toolbar-search" class="input-group-text">
<button type="submit"> <button type="submit">

View File

@ -35,7 +35,7 @@ from testlib_journal_entry import NON_EMPTY_NOTE, EMPTY_NOTE, \
PREFIX: str = "/accounting/journal-entries" PREFIX: str = "/accounting/journal-entries"
"""The URL prefix for the journal entry management.""" """The URL prefix for the journal entry management."""
RETURN_TO_URI: str = "/accounting/reports" RETURN_TO_URI: str = "/accounting"
"""The URL to return to after the operation.""" """The URL to return to after the operation."""