Added the account management.
This commit is contained in:
130
src/accounting/account/forms.py
Normal file
130
src/accounting/account/forms.py
Normal file
@ -0,0 +1,130 @@
|
||||
# The Mia! Accounting Flask Project.
|
||||
# Author: imacat@mail.imacat.idv.tw (imacat), 2023/2/1
|
||||
|
||||
# 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 forms for the account management.
|
||||
|
||||
"""
|
||||
import sqlalchemy as sa
|
||||
from flask_wtf import FlaskForm
|
||||
from wtforms import StringField, BooleanField
|
||||
from wtforms.validators import DataRequired, ValidationError
|
||||
|
||||
from accounting.base_account import BaseAccount
|
||||
from accounting.database import db, user_utils
|
||||
from accounting.locale import lazy_gettext
|
||||
from accounting.utils.random_id import new_id
|
||||
from accounting.utils.strip_text import strip_text
|
||||
from .models import Account
|
||||
|
||||
|
||||
class BaseAccountExists:
|
||||
"""The validator to check if the base account code exists."""
|
||||
|
||||
def __call__(self, form: FlaskForm, field: StringField) -> None:
|
||||
if field.data == "":
|
||||
return
|
||||
if db.session.get(BaseAccount, field.data) is None:
|
||||
raise ValidationError(lazy_gettext(
|
||||
"The base account does not exist."))
|
||||
|
||||
|
||||
class AccountForm(FlaskForm):
|
||||
"""The form to create or edit an account."""
|
||||
base_code = StringField(
|
||||
filters=[strip_text],
|
||||
validators=[
|
||||
DataRequired(lazy_gettext("Please select the base account.")),
|
||||
BaseAccountExists()])
|
||||
"""The code of the base account."""
|
||||
title = StringField(
|
||||
filters=[strip_text],
|
||||
validators=[DataRequired(lazy_gettext("Please fill in the title"))])
|
||||
"""The title."""
|
||||
is_offset_needed = BooleanField()
|
||||
"""Whether the the entries of this account need offsets."""
|
||||
|
||||
def populate_obj(self, obj: Account) -> None:
|
||||
"""Populates the form data into an account object.
|
||||
|
||||
:param obj: The account object.
|
||||
:return: None.
|
||||
"""
|
||||
is_new: bool = obj.id is None
|
||||
prev_base_code: str | None = obj.base_code
|
||||
if is_new:
|
||||
obj.id = new_id(Account)
|
||||
obj.base_code = self.base_code.data
|
||||
if prev_base_code != self.base_code.data:
|
||||
last_same_base: Account = Account.query\
|
||||
.filter(Account.base_code == self.base_code.data)\
|
||||
.order_by(Account.base_code.desc()).first()
|
||||
obj.no = 1 if last_same_base is None else last_same_base.no + 1
|
||||
obj.title = self.title.data
|
||||
obj.is_offset_needed = self.is_offset_needed.data
|
||||
if is_new:
|
||||
current_user_pk: int = user_utils.get_pk(user_utils.current_user)
|
||||
obj.created_by_id = current_user_pk
|
||||
obj.updated_by_id = current_user_pk
|
||||
if prev_base_code is not None \
|
||||
and prev_base_code != self.base_code.data:
|
||||
setattr(self, "__post_update",
|
||||
lambda: sort_accounts_in(prev_base_code, obj.id))
|
||||
|
||||
def post_update(self, obj) -> None:
|
||||
"""The post-processing after the update.
|
||||
|
||||
:return: None
|
||||
"""
|
||||
current_user_pk: int = user_utils.get_pk(user_utils.current_user)
|
||||
obj.updated_by_id = current_user_pk
|
||||
obj.updated_at = sa.func.now()
|
||||
if hasattr(self, "__post_update"):
|
||||
getattr(self, "__post_update")()
|
||||
|
||||
@property
|
||||
def selected_base(self) -> BaseAccount | None:
|
||||
"""The selected base account in the form.
|
||||
|
||||
:return: The selected base account in the form.
|
||||
"""
|
||||
return db.session.get(BaseAccount, self.base_code.data)
|
||||
|
||||
@property
|
||||
def base_options(self) -> list[BaseAccount]:
|
||||
"""The selectable base accounts.
|
||||
|
||||
:return: The selectable base accounts.
|
||||
"""
|
||||
return BaseAccount.query\
|
||||
.filter(sa.func.char_length(BaseAccount.code) == 4)\
|
||||
.order_by(BaseAccount.code).all()
|
||||
|
||||
|
||||
def sort_accounts_in(base_code: str, exclude: int) -> None:
|
||||
"""Sorts the accounts under a base account after changing the base
|
||||
account or deleting an account.
|
||||
|
||||
:param base_code: The code of the base account.
|
||||
:param exclude: The account ID to exclude.
|
||||
:return: None.
|
||||
"""
|
||||
accounts: list[Account] = Account.query\
|
||||
.filter(Account.base_code == base_code,
|
||||
Account.id != exclude)\
|
||||
.order_by(Account.no).all()
|
||||
for i in range(len(accounts)):
|
||||
if accounts[i].no != i + 1:
|
||||
accounts[i].no = i + 1
|
Reference in New Issue
Block a user