Added the TransactionTypeEnum in the new "accounting.utils.txn_types" module to remove the dependency from the "accounting.report" module to the "accounting.transaction" module.

This commit is contained in:
2023-03-04 19:30:04 +08:00
parent 7d412b20d7
commit 9833bac6e4
8 changed files with 94 additions and 79 deletions

View File

@ -24,8 +24,7 @@ from werkzeug.routing import BaseConverter
from accounting import db
from accounting.models import Transaction
from accounting.transaction.dispatcher import TransactionType, \
TXN_TYPE_DICT
from accounting.utils.txn_types import TransactionTypeEnum
class TransactionConverter(BaseConverter):
@ -56,24 +55,26 @@ class TransactionTypeConverter(BaseConverter):
"""The transaction converter to convert the transaction type ID from and to
the corresponding transaction type in the routes."""
def to_python(self, value: str) -> TransactionType:
def to_python(self, value: str) -> TransactionTypeEnum:
"""Converts a transaction ID to a transaction.
:param value: The transaction ID.
:return: The corresponding transaction.
"""
txn_type: TransactionType | None = TXN_TYPE_DICT.get(value)
type_dict: dict[str, TransactionTypeEnum] \
= {x.value: x for x in TransactionTypeEnum}
txn_type: TransactionTypeEnum | None = type_dict.get(value)
if txn_type is None:
abort(404)
return txn_type
def to_url(self, value: TransactionType) -> str:
def to_url(self, value: TransactionTypeEnum) -> str:
"""Converts a transaction type to its ID.
:param value: The transaction type.
:return: The ID.
"""
return str(value.ID)
return str(value.value)
class DateConverter(BaseConverter):

View File

@ -24,15 +24,14 @@ from flask import render_template, request, abort
from flask_wtf import FlaskForm
from accounting.models import Transaction
from accounting.template_globals import default_currency_code
from accounting.utils.txn_types import TransactionTypeEnum
from .forms import TransactionForm, IncomeTransactionForm, \
ExpenseTransactionForm, TransferTransactionForm
from accounting.template_globals import default_currency_code
class TransactionType(ABC):
"""An abstract transaction type."""
ID: str = ""
"""The transaction type ID."""
CHECK_ORDER: int = -1
"""The order when checking the transaction type."""
@ -93,8 +92,6 @@ class TransactionType(ABC):
class IncomeTransaction(TransactionType):
"""An income transaction."""
ID: str = "income"
"""The transaction type ID."""
CHECK_ORDER: int = 2
"""The order when checking the transaction type."""
@ -113,7 +110,8 @@ class IncomeTransaction(TransactionType):
:return: the form to create a transaction.
"""
return render_template("accounting/transaction/income/create.html",
form=form, txn_type=self,
form=form,
txn_type=TransactionTypeEnum.CASH_INCOME,
currency_template=self.__currency_template,
entry_template=self._entry_template)
@ -163,8 +161,6 @@ class IncomeTransaction(TransactionType):
class ExpenseTransaction(TransactionType):
"""An expense transaction."""
ID: str = "expense"
"""The transaction type ID."""
CHECK_ORDER: int = 1
"""The order when checking the transaction type."""
@ -183,7 +179,8 @@ class ExpenseTransaction(TransactionType):
:return: the form to create a transaction.
"""
return render_template("accounting/transaction/expense/create.html",
form=form, txn_type=self,
form=form,
txn_type=TransactionTypeEnum.CASH_EXPENSE,
currency_template=self.__currency_template,
entry_template=self._entry_template)
@ -233,8 +230,6 @@ class ExpenseTransaction(TransactionType):
class TransferTransaction(TransactionType):
"""A transfer transaction."""
ID: str = "transfer"
"""The transaction type ID."""
CHECK_ORDER: int = 3
"""The order when checking the transaction type."""
@ -253,7 +248,8 @@ class TransferTransaction(TransactionType):
:return: the form to create a transaction.
"""
return render_template("accounting/transaction/transfer/create.html",
form=form, txn_type=self,
form=form,
txn_type=TransactionTypeEnum.TRANSFER,
currency_template=self.__currency_template,
entry_template=self._entry_template)
@ -301,44 +297,28 @@ class TransferTransaction(TransactionType):
debit_total="-", credit_total="-")
class TransactionTypes:
"""The transaction types, as object properties."""
def __init__(self, income: IncomeTransaction, expense: ExpenseTransaction,
transfer: TransferTransaction):
"""Constructs the transaction types as object properties.
:param income: The income transaction type.
:param expense: The expense transaction type.
:param transfer: The transfer transaction type.
"""
self.income: IncomeTransaction = income
self.expense: ExpenseTransaction = expense
self.transfer: TransferTransaction = transfer
TXN_ENUM_TO_OP: dict[TransactionTypeEnum, TransactionType] \
= {TransactionTypeEnum.CASH_INCOME: IncomeTransaction(),
TransactionTypeEnum.CASH_EXPENSE: ExpenseTransaction(),
TransactionTypeEnum.TRANSFER: TransferTransaction()}
"""The map from the transaction type enum to its operator."""
TXN_TYPE_DICT: dict[str, TransactionType] \
= {x.ID: x() for x in {IncomeTransaction,
ExpenseTransaction,
TransferTransaction}}
"""The transaction types, as a dictionary."""
TXN_TYPE_OBJ: TransactionTypes = TransactionTypes(**TXN_TYPE_DICT)
"""The transaction types, as an object."""
def get_txn_type(txn: Transaction) -> TransactionType:
"""Returns the transaction type that may be specified in the "as" query
parameter. If it is not specified, check the transaction type from the
transaction.
def get_txn_type_op(txn: Transaction) -> TransactionType:
"""Returns the transaction type operator that may be specified in the "as"
query parameter. If it is not specified, check the transaction type from
the transaction.
:param txn: The transaction.
:return: None.
"""
if "as" in request.args:
if request.args["as"] not in TXN_TYPE_DICT:
type_dict: dict[str, TransactionTypeEnum] \
= {x.value: x for x in TransactionTypeEnum}
if request.args["as"] not in type_dict:
abort(404)
return TXN_TYPE_DICT[request.args["as"]]
for txn_type in sorted(TXN_TYPE_DICT.values(),
return TXN_ENUM_TO_OP[type_dict[request.args["as"]]]
for txn_type in sorted(TXN_ENUM_TO_OP.values(),
key=lambda x: x.CHECK_ORDER):
if txn_type.is_my_type(txn):
return txn_type

View File

@ -32,8 +32,9 @@ 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 has_permission, can_view, can_edit
from accounting.utils.txn_types import TransactionTypeEnum
from accounting.utils.user import get_current_user_pk
from .dispatcher import TransactionType, get_txn_type, TXN_TYPE_OBJ
from .dispatcher import TransactionType, TXN_ENUM_TO_OP, get_txn_type_op
from .forms import sort_transactions_in, TransactionReorderForm
from .queries import get_transaction_query
from .template_filters import with_type, to_transfer, format_amount_input, \
@ -59,37 +60,39 @@ def list_transactions() -> str:
pagination: Pagination = Pagination[Transaction](transactions)
return render_template("accounting/transaction/list.html",
list=pagination.list, pagination=pagination,
types=TXN_TYPE_OBJ)
txn_types=TransactionTypeEnum)
@bp.get("/create/<transactionType:txn_type>", endpoint="create")
@has_permission(can_edit)
def show_add_transaction_form(txn_type: TransactionType) -> str:
def show_add_transaction_form(txn_type: TransactionTypeEnum) -> str:
"""Shows the form to add a transaction.
:param txn_type: The transaction type.
:return: The form to add a transaction.
"""
form: txn_type.form
txn_type_op: TransactionType = TXN_ENUM_TO_OP[txn_type]
form: txn_type_op.form
if "form" in session:
form = txn_type.form(ImmutableMultiDict(parse_qsl(session["form"])))
form = txn_type_op.form(ImmutableMultiDict(parse_qsl(session["form"])))
del session["form"]
form.validate()
else:
form = txn_type.form()
return txn_type.render_create_template(form)
form = txn_type_op.form()
return txn_type_op.render_create_template(form)
@bp.post("/store/<transactionType:txn_type>", endpoint="store")
@has_permission(can_edit)
def add_transaction(txn_type: TransactionType) -> redirect:
def add_transaction(txn_type: TransactionTypeEnum) -> redirect:
"""Adds a transaction.
:param txn_type: The transaction type.
:return: The redirection to the transaction detail on success, or the
transaction creation form on error.
"""
form: txn_type.form = txn_type.form(request.form)
txn_type_op: TransactionType = TXN_ENUM_TO_OP[txn_type]
form: txn_type_op.form = txn_type_op.form(request.form)
if not form.validate():
flash_form_errors(form)
session["form"] = urlencode(list(request.form.items()))
@ -111,8 +114,8 @@ def show_transaction_detail(txn: Transaction) -> str:
:param txn: The transaction.
:return: The detail.
"""
txn_type: TransactionType = get_txn_type(txn)
return txn_type.render_detail_template(txn)
txn_type_op: TransactionType = get_txn_type_op(txn)
return txn_type_op.render_detail_template(txn)
@bp.get("/<transaction:txn>/edit", endpoint="edit")
@ -123,15 +126,15 @@ def show_transaction_edit_form(txn: Transaction) -> str:
:param txn: The transaction.
:return: The form to edit the transaction.
"""
txn_type: TransactionType = get_txn_type(txn)
form: txn_type.form
txn_type_op: TransactionType = get_txn_type_op(txn)
form: txn_type_op.form
if "form" in session:
form = txn_type.form(ImmutableMultiDict(parse_qsl(session["form"])))
form = txn_type_op.form(ImmutableMultiDict(parse_qsl(session["form"])))
del session["form"]
form.validate()
else:
form = txn_type.form(obj=txn)
return txn_type.render_edit_template(txn, form)
form = txn_type_op.form(obj=txn)
return txn_type_op.render_edit_template(txn, form)
@bp.post("/<transaction:txn>/update", endpoint="update")
@ -143,8 +146,8 @@ def update_transaction(txn: Transaction) -> redirect:
:return: The redirection to the transaction detail on success, or the
transaction edit form on error.
"""
txn_type: TransactionType = get_txn_type(txn)
form: txn_type.form = txn_type.form(request.form)
txn_type_op: TransactionType = get_txn_type_op(txn)
form: txn_type_op.form = txn_type_op.form(request.form)
if not form.validate():
flash_form_errors(form)
session["form"] = urlencode(list(request.form.items()))