Compare commits
	
		
			10 Commits
		
	
	
		
			v1.5.9
			...
			99564c02d0
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 99564c02d0 | |||
| 25d9904180 | |||
| 1cf83adf87 | |||
| 8e3d1f11b5 | |||
| 0ab14aa34d | |||
| e0ed81ad1f | |||
| ece7481e9e | |||
| 50d4526e0b | |||
| 3f0a0b4227 | |||
| dcc9626b23 | 
| @@ -59,7 +59,7 @@ Refer to the `change log`_. | ||||
| Copyright | ||||
| ========= | ||||
|  | ||||
|  Copyright (c) 2023 imacat. | ||||
|  Copyright (c) 2023-2024 imacat. | ||||
|  | ||||
|  Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|  you may not use this file except in compliance with the License. | ||||
|   | ||||
| @@ -2,10 +2,32 @@ Change Log | ||||
| ========== | ||||
|  | ||||
|  | ||||
| Version 1.5.11 | ||||
| -------------- | ||||
|  | ||||
| Released 2023/12/26 | ||||
|  | ||||
| Bug fix. | ||||
|  | ||||
| * Refined to enable the selection of the 3351-001 Accumulated Profit or Loss | ||||
|   account. | ||||
|  | ||||
|  | ||||
| Version 1.5.10 | ||||
| -------------- | ||||
|  | ||||
| Released 2023/11/28 | ||||
|  | ||||
| Bug fix. | ||||
|  | ||||
| * Fixed the form validator to enable the selection of Accumulated Profit or | ||||
|   Loss accounts other than 3351-001. | ||||
|  | ||||
|  | ||||
| Version 1.5.9 | ||||
| ------------- | ||||
|  | ||||
| Released 2023/10/24 | ||||
| Released 2023/11/28 | ||||
|  | ||||
| Bug fix. | ||||
|  | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| # The Mia! Accounting Project. | ||||
| # Author: imacat@mail.imacat.idv.tw (imacat), 2022/8/21 | ||||
|  | ||||
| #  Copyright (c) 2022-2023 imacat. | ||||
| #  Copyright (c) 2022-2024 imacat. | ||||
| # | ||||
| #  Licensed under the Apache License, Version 2.0 (the "License"); | ||||
| #  you may not use this file except in compliance with the License. | ||||
| @@ -20,7 +20,7 @@ name = "mia-accounting" | ||||
| dynamic = ["version"] | ||||
| description = "A Flask accounting module." | ||||
| readme = "README.rst" | ||||
| requires-python = ">=3.11" | ||||
| requires-python = ">=3.12" | ||||
| authors = [ | ||||
|     {name = "imacat", email = "imacat@mail.imacat.idv.tw"}, | ||||
| ] | ||||
|   | ||||
| @@ -24,7 +24,7 @@ from flask_sqlalchemy import SQLAlchemy | ||||
|  | ||||
| from accounting.utils.user import UserUtilityInterface | ||||
|  | ||||
| VERSION: str = "1.5.9" | ||||
| VERSION: str = "1.5.11" | ||||
| """The package version.""" | ||||
| db: SQLAlchemy = SQLAlchemy() | ||||
| """The database instance.""" | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| # The Mia! Accounting Project. | ||||
| # Author: imacat@mail.imacat.idv.tw (imacat), 2023/1/30 | ||||
|  | ||||
| #  Copyright (c) 2023 imacat. | ||||
| #  Copyright (c) 2023-2024 imacat. | ||||
| # | ||||
| #  Licensed under the Apache License, Version 2.0 (the "License"); | ||||
| #  you may not use this file except in compliance with the License. | ||||
| @@ -27,7 +27,7 @@ from accounting import db | ||||
| from accounting.models import BaseAccount, Account, AccountL10n | ||||
| from accounting.utils.user import get_user_pk | ||||
|  | ||||
| AccountData = tuple[int, str, int, str, str, str, bool] | ||||
| type AccountData = tuple[int, str, int, str, str, str, bool] | ||||
| """The format of the account data, as a list of (ID, base account code, number, | ||||
| English, Traditional Chinese, Simplified Chinese, is-need-offset) tuples.""" | ||||
|  | ||||
|   | ||||
| @@ -71,7 +71,6 @@ class IsDebitAccount: | ||||
|         if field.data is None: | ||||
|             return | ||||
|         if re.match(r"^(?:[1235689]|7[5678])", field.data) \ | ||||
|                 and not field.data.startswith("3351-") \ | ||||
|                 and not field.data.startswith("3353-"): | ||||
|             return | ||||
|         raise ValidationError(self.__message) | ||||
| @@ -92,7 +91,6 @@ class IsCreditAccount: | ||||
|         if field.data is None: | ||||
|             return | ||||
|         if re.match(r"^(?:[123489]|7[1234])", field.data) \ | ||||
|                 and not field.data.startswith("3351-") \ | ||||
|                 and not field.data.startswith("3353-"): | ||||
|             return | ||||
|         raise ValidationError(self.__message) | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| # The Mia! Accounting Project. | ||||
| # Author: imacat@mail.imacat.idv.tw (imacat), 2023/2/18 | ||||
|  | ||||
| #  Copyright (c) 2023 imacat. | ||||
| #  Copyright (c) 2023-2024 imacat. | ||||
| # | ||||
| #  Licensed under the Apache License, Version 2.0 (the "License"); | ||||
| #  you may not use this file except in compliance with the License. | ||||
| @@ -19,7 +19,7 @@ | ||||
| """ | ||||
| import datetime as dt | ||||
| from abc import ABC, abstractmethod | ||||
| from typing import TypeVar, Generic, Type | ||||
| from typing import Type | ||||
|  | ||||
| import sqlalchemy as sa | ||||
| from flask_babel import LazyString | ||||
| @@ -308,11 +308,7 @@ class JournalEntryForm(FlaskForm): | ||||
|         return db.session.scalar(select) | ||||
|  | ||||
|  | ||||
| T = TypeVar("T", bound=JournalEntryForm) | ||||
| """A journal entry form variant.""" | ||||
|  | ||||
|  | ||||
| class LineItemCollector(Generic[T], ABC): | ||||
| class LineItemCollector[T: JournalEntryForm](ABC): | ||||
|     """The line item collector.""" | ||||
|  | ||||
|     def __init__(self, form: T, obj: JournalEntry): | ||||
|   | ||||
| @@ -304,8 +304,6 @@ class Account(db.Model): | ||||
|                                        cls.base_code.startswith("78"), | ||||
|                                        cls.base_code.startswith("8"), | ||||
|                                        cls.base_code.startswith("9")), | ||||
|                                 sa.not_(sa.and_(cls.base_code == "3351", | ||||
|                                                 cls.no == 1)), | ||||
|                                 cls.base_code != "3353")\ | ||||
|             .order_by(cls.base_code, cls.no).all() | ||||
|  | ||||
| @@ -327,8 +325,6 @@ class Account(db.Model): | ||||
|                                        cls.base_code.startswith("74"), | ||||
|                                        cls.base_code.startswith("8"), | ||||
|                                        cls.base_code.startswith("9")), | ||||
|                                 sa.not_(sa.and_(cls.base_code == "3351", | ||||
|                                                 cls.no == 1)), | ||||
|                                 cls.base_code != "3353")\ | ||||
|             .order_by(cls.base_code, cls.no).all() | ||||
|  | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| # The Mia! Accounting Project. | ||||
| # Author: imacat@mail.imacat.idv.tw (imacat), 2023/1/25 | ||||
|  | ||||
| #  Copyright (c) 2023 imacat. | ||||
| #  Copyright (c) 2023-2024 imacat. | ||||
| # | ||||
| #  Licensed under the Apache License, Version 2.0 (the "License"); | ||||
| #  you may not use this file except in compliance with the License. | ||||
| @@ -19,7 +19,6 @@ | ||||
| This module should not import any other module from the application. | ||||
|  | ||||
| """ | ||||
| from typing import TypeVar, Generic | ||||
| from urllib.parse import urlparse, parse_qsl, urlencode, urlunparse, \ | ||||
|     ParseResult | ||||
|  | ||||
| @@ -62,11 +61,8 @@ class Redirection(RequestRedirect): | ||||
| DEFAULT_PAGE_SIZE: int = 10 | ||||
| """The default page size.""" | ||||
|  | ||||
| T = TypeVar("T") | ||||
| """The pagination item type.""" | ||||
|  | ||||
|  | ||||
| class Pagination(Generic[T]): | ||||
| class Pagination[T]: | ||||
|     """The pagination utility.""" | ||||
|  | ||||
|     def __init__(self, items: list[T], is_reversed: bool = False): | ||||
| @@ -92,7 +88,7 @@ class Pagination(Generic[T]): | ||||
|         """The options to the number of items in a page.""" | ||||
|  | ||||
|  | ||||
| class AbstractPagination(Generic[T]): | ||||
| class AbstractPagination[T]: | ||||
|     """An abstract pagination.""" | ||||
|  | ||||
|     def __init__(self): | ||||
| @@ -109,12 +105,12 @@ class AbstractPagination(Generic[T]): | ||||
|         """The options to the number of items in a page.""" | ||||
|  | ||||
|  | ||||
| class EmptyPagination(AbstractPagination[T]): | ||||
| class EmptyPagination[T](AbstractPagination[T]): | ||||
|     """The pagination from empty data.""" | ||||
|     pass | ||||
|  | ||||
|  | ||||
| class NonEmptyPagination(AbstractPagination[T]): | ||||
| class NonEmptyPagination[T](AbstractPagination[T]): | ||||
|     """The pagination with real data.""" | ||||
|     PAGE_SIZE_OPTION_VALUES: list[int] = [10, 100, 200] | ||||
|     """The page size options.""" | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| # The Mia! Accounting Project. | ||||
| # Author: imacat@mail.imacat.idv.tw (imacat), 2023/2/1 | ||||
|  | ||||
| #  Copyright (c) 2023 imacat. | ||||
| #  Copyright (c) 2023-2024 imacat. | ||||
| # | ||||
| #  Licensed under the Apache License, Version 2.0 (the "License"); | ||||
| #  you may not use this file except in compliance with the License. | ||||
| @@ -20,17 +20,14 @@ This module should not import any other module from the application. | ||||
|  | ||||
| """ | ||||
| from abc import ABC, abstractmethod | ||||
| from typing import TypeVar, Generic, Type | ||||
| from typing import Type | ||||
|  | ||||
| import sqlalchemy as sa | ||||
| from flask import g, Response | ||||
| from flask_sqlalchemy.model import Model | ||||
|  | ||||
| T = TypeVar("T", bound=Model) | ||||
| """The user data model data type.""" | ||||
|  | ||||
|  | ||||
| class UserUtilityInterface(Generic[T], ABC): | ||||
| class UserUtilityInterface[T: Model](ABC): | ||||
|     """The interface for the user utilities.""" | ||||
|  | ||||
|     @abstractmethod | ||||
| @@ -113,7 +110,7 @@ class UserUtilityInterface(Generic[T], ABC): | ||||
|  | ||||
| __user_utils: UserUtilityInterface | ||||
| """The user utilities.""" | ||||
| user_cls: Type[Model] = Model | ||||
| type user_cls = Model | ||||
| """The user class.""" | ||||
| user_pk_column: sa.Column = sa.Column(sa.Integer) | ||||
| """The primary key column of the user class.""" | ||||
|   | ||||
| @@ -2,7 +2,7 @@ | ||||
| The Mia! Accounting Demonstration Website | ||||
| base.html: The side-wide layout template | ||||
|  | ||||
|  Copyright (c) 2023 imacat. | ||||
|  Copyright (c) 2023-2024 imacat. | ||||
|  | ||||
|  Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|  you may not use this file except in compliance with the License. | ||||
| @@ -25,21 +25,21 @@ First written: 2023/1/27 | ||||
|   <meta charset="UTF-8"> | ||||
|   <meta name="viewport" content="width=device-width, initial-scale=1"> | ||||
|   <meta name="author" content="{{ "imacat" }}" /> | ||||
|   <link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/css/bootstrap.min.css" integrity="sha384-rbsA2VBKQhggwzxH7pPCaAqO46MgnOM80zW1RWuH61DGLwZJEdK2Kadq2F9CUG65" crossorigin="anonymous"> | ||||
|   <link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free@6.4.0/css/all.min.css" integrity="sha384-iw3OoTErCYJJB9mCa8LNS2hbsQ7M3C0EpIsO/H5+EGAkPGc6rk+V8i04oW/K5xq0" crossorigin="anonymous"> | ||||
|   <link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/@eonasdan/tempus-dominus@6.7.7/dist/css/tempus-dominus.min.css" integrity="sha384-l66rSL7gUubrdJxFRbXUo/tO7eNPAcCiZXFs/Xl147146xNqQ1qt4oPW6jlVezsS" crossorigin="anonymous"> | ||||
|   <link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous"> | ||||
|   <link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free@6.5.1/css/all.min.css" integrity="sha384-t1nt8BQoYMLFN5p42tRAtuAAFQaCQODekUVeKKZrEnEyp4H2R0RHFz0KWpmj7i8g" crossorigin="anonymous"> | ||||
|   <link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/@eonasdan/tempus-dominus@6.9.6/dist/css/tempus-dominus.min.css" integrity="sha384-NzVf7b26bC2au5J9EqNceWlrs7iIkBa0bA46tRpK5C3J08J7MRTPmSdpRKhWNgDL" crossorigin="anonymous"> | ||||
|   {% block styles %}{% endblock %} | ||||
|   <script src="{{ url_for("babel_catalog") }}"></script> | ||||
|   <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-kenU1KFdBIe4zVF0s0G1M5b4hcpxyD9F7jL+jjXkk+Q2h455rYXK/7HAuoJl+0I4" crossorigin="anonymous"></script> | ||||
|   <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz" crossorigin="anonymous"></script> | ||||
|   <script src="https://cdn.jsdelivr.net/npm/decimal.js-light@2.5.1/decimal.min.js" integrity="sha384-QdsxGEq4Y0erX8WUIsZJDtfoSSyBF6dmNCnzRNYCa2AOM/xzNsyhHu0RbdFBAm+l" crossorigin="anonymous"></script> | ||||
|   <script src="https://cdn.jsdelivr.net/npm/@eonasdan/tempus-dominus@6.7.7/dist/js/tempus-dominus.min.js" integrity="sha384-MxHp+/TqTjbku1jSTIe1e/4l6CZTLhACLDbWyxYaFRgD3AM4oh99AY8bxsGhIoRc" crossorigin="anonymous"></script> | ||||
|   <script src="https://cdn.jsdelivr.net/npm/@eonasdan/tempus-dominus@6.9.6/dist/js/tempus-dominus.min.js" integrity="sha384-GRg4jmBEA/AnwmpV7MhpXUTim20ncyZTm9/1fbna86CRqMcdrou46etX8scQ9dPe" crossorigin="anonymous"></script> | ||||
|   {% block scripts %}{% endblock %} | ||||
|   <link rel="shortcut icon" href="{{ url_for("static", filename="favicon.svg") }}"> | ||||
|   <title>{% block title %}{% endblock %}</title> | ||||
| </head> | ||||
| <body> | ||||
|  | ||||
| <nav class="navbar navbar-expand-lg bg-body-tertiary bg-dark navbar-dark"> | ||||
| <nav class="navbar navbar-expand-lg bg-body-tertiary bg-dark" data-bs-theme="dark"> | ||||
|   <div class="container-fluid"> | ||||
|     <a class="navbar-brand" href="{{ url_for("home.home") }}"> | ||||
|       <i class="fa-solid fa-house"></i> | ||||
|   | ||||
		Reference in New Issue
	
	Block a user