# The Mia! Accounting Project. # Author: imacat@mail.imacat.idv.tw (imacat), 2023/1/30 # 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 views for the account management. """ from urllib.parse import parse_qsl, urlencode import sqlalchemy as sa from flask import Blueprint, render_template, session, redirect, flash, \ url_for, request from werkzeug.datastructures import ImmutableMultiDict from accounting import db from accounting.locale import lazy_gettext from accounting.models import Account, BaseAccount from accounting.utils.cast import s from accounting.utils.flash_errors import flash_form_errors from accounting.utils.next_uri import inherit_next, or_next from accounting.utils.pagination import Pagination from accounting.utils.permission import can_view, has_permission, can_edit from accounting.utils.user import get_current_user_pk from .forms import AccountForm, sort_accounts_in, AccountReorderForm from .queries import get_account_query bp: Blueprint = Blueprint("account", __name__) """The view blueprint for the account management.""" @bp.get("", endpoint="list") @has_permission(can_view) def list_accounts() -> str: """Lists the accounts. :return: The account list. """ accounts: list[BaseAccount] = get_account_query() pagination: Pagination = Pagination[BaseAccount](accounts) return render_template("accounting/account/list.html", list=pagination.list, pagination=pagination) @bp.get("create", endpoint="create") @has_permission(can_edit) def show_add_account_form() -> str: """Shows the form to add an account. :return: The form to add an account. """ if "form" in session: form = AccountForm(ImmutableMultiDict(parse_qsl(session["form"]))) del session["form"] form.validate() else: form = AccountForm() return render_template("accounting/account/create.html", form=form) @bp.post("store", endpoint="store") @has_permission(can_edit) def add_account() -> redirect: """Adds an account. :return: The redirection to the account detail on success, or the account creation form on error. """ form = AccountForm(request.form) if not form.validate(): flash_form_errors(form) session["form"] = urlencode(list(request.form.items())) return redirect(inherit_next(url_for("accounting.account.create"))) account: Account = Account() form.populate_obj(account) db.session.add(account) db.session.commit() flash(s(lazy_gettext("The account is added successfully")), "success") return redirect(inherit_next(__get_detail_uri(account))) @bp.get("", endpoint="detail") @has_permission(can_view) def show_account_detail(account: Account) -> str: """Shows the account detail. :param account: The account. :return: The detail. """ return render_template("accounting/account/detail.html", obj=account) @bp.get("/edit", endpoint="edit") @has_permission(can_edit) def show_account_edit_form(account: Account) -> str: """Shows the form to edit an account. :param account: The account. :return: The form to edit the account. """ form: AccountForm if "form" in session: form = AccountForm(ImmutableMultiDict(parse_qsl(session["form"]))) del session["form"] form.validate() else: form = AccountForm(obj=account) return render_template("accounting/account/edit.html", account=account, form=form) @bp.post("/update", endpoint="update") @has_permission(can_edit) def update_account(account: Account) -> redirect: """Updates an account. :param account: The account. :return: The redirection to the account detail on success, or the account edit form on error. """ form = AccountForm(request.form) if not form.validate(): flash_form_errors(form) session["form"] = urlencode(list(request.form.items())) return redirect(inherit_next(url_for("accounting.account.edit", account=account))) with db.session.no_autoflush: form.populate_obj(account) if not account.is_modified: flash(s(lazy_gettext("The account was not modified.")), "success") return redirect(inherit_next(__get_detail_uri(account))) account.updated_by_id = get_current_user_pk() account.updated_at = sa.func.now() db.session.commit() flash(s(lazy_gettext("The account is updated successfully.")), "success") return redirect(inherit_next(__get_detail_uri(account))) @bp.post("/delete", endpoint="delete") @has_permission(can_edit) def delete_account(account: Account) -> redirect: """Deletes an account. :param account: The account. :return: The redirection to the account list on success, or the account detail on error. """ if not account.can_delete: flash(s(lazy_gettext("The account cannot be deleted.")), "error") return redirect(inherit_next(__get_detail_uri(account))) account.delete() sort_accounts_in(account.base_code, account.id) db.session.commit() flash(s(lazy_gettext("The account is deleted successfully.")), "success") return redirect(or_next(__get_list_uri())) @bp.get("bases/", endpoint="order") @has_permission(can_view) def show_account_order(base: BaseAccount) -> str: """Shows the order of the accounts under a same base account. :param base: The base account. :return: The order of the accounts under the base account. """ return render_template("accounting/account/order.html", base=base) @bp.post("bases/", endpoint="sort") @has_permission(can_edit) def sort_accounts(base: BaseAccount) -> redirect: """Reorders the accounts under a base account. :param base: The base account. :return: The redirection to the incoming account or the account list. The reordering operation does not fail. """ form: AccountReorderForm = AccountReorderForm(base) form.save_order() if not form.is_modified: flash(s(lazy_gettext("The order was not modified.")), "success") return redirect(or_next(__get_list_uri())) db.session.commit() flash(s(lazy_gettext("The order is updated successfully.")), "success") return redirect(or_next(__get_list_uri())) def __get_detail_uri(account: Account) -> str: """Returns the detail URI of an account. :param account: The account. :return: The detail URI of the account. """ return url_for("accounting.account.detail", account=account) def __get_list_uri() -> str: """Returns the account list URI. :return: The account list URI. """ return url_for("accounting.account.list")