29 Commits

Author SHA1 Message Date
3a0e978f76 Removed an unused import from the "accounting.journal_entry.forms.line_item" module. 2023-04-08 00:44:13 +08:00
8c10d42d7b Added documentation to the currency and account parameters of the CSVRow class, and the pagination parameter of the PageParams class in the "accounting.report.reports.journal" module. 2023-04-08 00:44:13 +08:00
04ec51afbe Changed the "offsets" relationship to a pseudo property, to apply the correct but complex ordering rules. 2023-04-07 16:04:54 +08:00
fe7a8842ce Fixed the query in the JournalEntryConverter converter. 2023-04-07 15:31:06 +08:00
66daa5c42c Fixed the query in the KeepAccountWhenHavingOffset validator. 2023-04-07 15:29:17 +08:00
27fb44937d Fixed the incorrect query in the "offsets" pseudo property of the LineItemForm form. 2023-04-07 15:11:04 +08:00
7026ed3a65 Fixed the order of the items in the "offsets" pseudo property of the LineItemForm form. 2023-04-07 15:01:22 +08:00
fdd3e93778 Fixed the net balance in the line items in the journal entry detail. 2023-04-07 14:57:24 +08:00
def7559457 Fixed the #filterOptions in the JavaScript JournalEntryAccountSelector to show the "more" option when there is no matches, but it is not showing all the accounts. 2023-04-07 12:34:24 +08:00
7905820d68 Revised the imports in the "accounting.base_account.views" and "accounting.currency.views" modules. 2023-04-06 16:09:36 +08:00
7ae332c975 Moved the "Test Site and Live Demonstration" section to the front of the documentation. 2023-04-06 10:00:24 +08:00
86c5b91697 Advanced to version 1.0.1. 2023-04-06 08:43:14 +08:00
9168840e64 Fixed an error in the example configuration. 2023-04-06 08:38:39 +08:00
21b9cfa8b8 Revised the documentation. 2023-04-06 08:31:19 +08:00
b0b3b3acb1 Moved the history section out from README.rst and intro.rst, to the new history.rst. 2023-04-06 08:21:32 +08:00
cb1d254cf0 Advanced to version 1.0.0. Hooray! 2023-04-06 02:55:19 +08:00
eb9ad57e72 Updated the translation. 2023-04-06 02:55:17 +08:00
ec26f8ef4d Added the documentation. 2023-04-06 02:54:45 +08:00
7ed29115ed Revised the inclusion in the base template of the test site. 2023-04-06 02:01:05 +08:00
95955197ac Updated the copyright year in pyproject.toml. 2023-04-05 22:50:54 +08:00
d5a0f79e4b Revised the Read the Docs configuration, and removed the redundant requirements.txt for Read the Docs. 2023-04-05 22:01:53 +08:00
6aa655aa64 Replaced setup.cfg with pyproject.toml for the package settings, and rewrote the packaging rules in MANIFEST.in. 2023-04-05 19:49:52 +08:00
6e532af26e Added the Read the Docs documentation link to README.rst. 2023-04-05 14:25:33 +08:00
fa1818d124 Added the Read the Docs configuration file. 2023-04-05 14:12:46 +08:00
f21ecc2aa9 Added requirements.txt for Read the Docs. 2023-04-05 14:07:37 +08:00
5ae1ab95ae Advanced to version 0.11.1. 2023-04-05 13:00:46 +08:00
7a5b3b78fc Removed the rows with zero balance from the income statement. 2023-04-05 12:59:50 +08:00
7df4051452 Removed the rows with zero balance from the trial balance. 2023-04-05 12:56:28 +08:00
85084c68fd Removed the rows with zero balance from the balance sheet. 2023-04-05 12:29:58 +08:00
24 changed files with 650 additions and 194 deletions

40
.readthedocs.yaml Normal file
View File

@ -0,0 +1,40 @@
# The Mia! Accounting Project.
# Author: imacat@mail.imacat.idv.tw (imacat), 2023/4/5
# 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.
# .readthedocs.yaml
# Read the Docs configuration file
# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details
# Required
version: 2
# Set the version of Python and other tools you might need
build:
os: ubuntu-22.04
tools:
python: "3.11"
# Build documentation in the docs/ directory with Sphinx
# If using Sphinx, optionally build your docs in additional formats such as PDF
formats: all
# Optionally declare the Python requirements required to build your docs
python:
install:
- method: pip
path: .

View File

@ -15,17 +15,14 @@
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
recursive-include src/accounting/static *
exclude src/accounting/static/js/dummy.js exclude src/accounting/static/js/dummy.js
include src/accounting/translations/* recursive-include src/accounting/templates *
include src/accounting/translations/*/LC_MESSAGES/* recursive-include src/accounting/translations *
include docs/* recursive-include src/accounting/data *
include docs/source/* recursive-include docs *
include docs/source/_static/* recursive-exclude docs/build *
include docs/source/_templates/* recursive-include tests *
include tests/*
exclude tests/test_temp.py exclude tests/test_temp.py
include tests/test_site/* recursive-exclude tests *.pyc
include tests/test_site/static/* recursive-exclude tests/instance *
include tests/test_site/templates/*
include tests/test_site/translations/*
include tests/test_site/translations/*/LC_MESSAGES/*

View File

@ -6,28 +6,171 @@ Mia! Accounting
Description Description
=========== ===========
This is the Mia! Accounting project. It is an accounting *Mia! Accounting* is an accounting module for Flask_ applications.
module for the Flask_ applications. It implements `double-entry bookkeeping`_, and generates the following
accounting reports:
* Trial balance
* Income statement
* Balance sheet
In addition, *Mia! Accounting* tracks offsets for unpaid payables and
receivables.
Install Installation
======= ============
Install ``mia-accounting`` with ``pip``. Install *Mia! Accounting* with ``pip``:
:: ::
pip install mia-accounting pip install mia-accounting
You may also download the from the `PyPI project page`_ or the
`release page`_ on the `Git repository`_.
Usage
=====
This needs to be done. Currently, you can refer to the test site Test Site and Live Demonstration
located in the test directory on the `Mia! Accounting repository`_. ================================
The test site is running as the You may find a working example in the `test site`_ in the
`live demonstration for Mia! Accounting`_. `source distribution`_. It is the simplest website that works with
*Mia! Accounting*. It is used in the automatic tests. It is the same
code run for `live demonstration`_.
If you do not have a running Flask application or do not know how to
start one, you may start with the test site.
Prerequisites
=============
You need a running Flask application with database user login.
The primary key of the user data model must be integer.
The following front-end JavaScript libraries must be loaded. You may
download it locally or use CDN_.
* Bootstrap_ 5.2.3 or above
* FontAwesome_ 6.2.1 or above
* `Decimal.js`_ 6.4.3 or above
* `Tempus-Dominus`_ 6.4.3 or above
Configuration
=============
You need to pass the Flask *app* and an implementation of
`UserUtilityInterface`_ to the `init_app`_ function.
``UserUtilityInterface`` contains everything *Mia! Accounting* needs.
The following is an example configuration for *Mia! Accounting*.
::
from flask import Response, redirect
from .auth import current_user()
from .modules import User
def create_app(test_config=None) -> Flask:
app: Flask = Flask(__name__)
... (Configuration of SQLAlchemy, CSRF, Babel_JS, ... etc) ...
import accounting
class UserUtils(accounting.UserUtilityInterface[User]):
def can_view(self) -> bool:
return True
def can_edit(self) -> bool:
return "editor" in current_user().roles
def can_admin(self) -> bool:
return current_user().is_admin
def unauthorized(self) -> Response:
return redirect("/login")
@property
def cls(self) -> t.Type[User]:
return User
@property
def pk_column(self) -> Column:
return User.id
@property
def current_user(self) -> User | None:
return current_user()
def get_by_username(self, username: str) -> User | None:
return User.query.filter(User.username == username).first()
def get_pk(self, user: User) -> int:
return user.id
accounting.init_app(app, UserUtils())
... (Any other configuration) ...
return app
Database Initialization
=======================
After the configuration, you need to run
`flask_sqlalchemy.SQLAlchemy.create_all`_ to create the
database tables that *Mia! Accounting* uses.
*Mia! Accounting* adds three console commands:
* ``accounting-init-base``
* ``accounting-init-accounts``
* ``accounting-init-currencies``
After database tables are created, run
``accounting-init-base`` first, and then the other two commands.
::
% flask --app myapp accounting-init-base
% flask --app myapp accounting-init-accounts
% flask --app myapp accounting-init-currencies
Navigation Menu
===============
Include the navigation menu in the `Bootstrap navigation bar`_ in your
base template:
::
<nav class="navbar navbar-expand-lg bg-body-tertiary bg-dark navbar-dark">
<div class="container-fluid">
...
<div id="collapsible-navbar" class="collapse navbar-collapse">
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
...
{% include "accounting/include/nav.html" %}
...
</ul>
...
</div>
</div>
</nav>
Check your Flask application and see how it works.
Documentation
=============
Refer to the `documentation on Read the Docs`_.
Copyright Copyright
@ -55,6 +198,22 @@ Authors
| imacat@mail.imacat.idv.tw | imacat@mail.imacat.idv.tw
| 2023/1/27 | 2023/1/27
.. _Flask: https://flask.palletsprojects.com .. _Flask: https://flask.palletsprojects.com
.. _Mia! Accounting repository: https://github.com/imacat/mia-accounting .. _double-entry bookkeeping: https://en.wikipedia.org/wiki/Double-entry_bookkeeping
.. _live demonstration for Mia! Accounting: https://accounting.imacat.idv.tw .. _test site: https://github.com/imacat/mia-accounting/tree/main/tests/test_site
.. _source distribution: https://pypi.org/project/mia-accounting/#files
.. _live demonstration: https://accounting.imacat.idv.tw
.. _PyPI project page: https://pypi.org/project/mia-accounting
.. _release page: https://github.com/imacat/mia-accounting/releases
.. _Git repository: https://github.com/imacat/mia-accounting
.. _CDN: https://en.wikipedia.org/wiki/Content_delivery_network
.. _Bootstrap: https://getbootstrap.com
.. _FontAwesome: https://fontawesome.com
.. _Decimal.js: https://mikemcl.github.io/decimal.js
.. _Tempus-Dominus: https://getdatepicker.com
.. _UserUtilityInterface: https://mia-accounting.readthedocs.io/en/latest/accounting.utils.html#accounting.utils.user.UserUtilityInterface
.. _init_app: https://mia-accounting.readthedocs.io/en/latest/accounting.html#accounting.init_app
.. _flask_sqlalchemy.SQLAlchemy.create_all: https://flask-sqlalchemy.palletsprojects.com/en/3.0.x/api/#flask_sqlalchemy.SQLAlchemy.create_all
.. _Bootstrap navigation bar: https://getbootstrap.com/docs/5.3/components/navbar/
.. _documentation on Read the Docs: https://mia-accounting.readthedocs.io

View File

@ -13,7 +13,7 @@ sys.path.insert(0, os.path.abspath('../../src/'))
project = 'Mia! Accounting' project = 'Mia! Accounting'
copyright = '2023, imacat' copyright = '2023, imacat'
author = 'imacat' author = 'imacat'
release = '0.11.0' release = '1.0.1'
# -- General configuration --------------------------------------------------- # -- General configuration ---------------------------------------------------
# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration # https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration

61
docs/source/examples.rst Normal file
View File

@ -0,0 +1,61 @@
Examples
========
.. _example-userutils:
An Example Configuration
------------------------
The following is an example configuration for *Mia! Accounting*.
::
from flask import Response, redirect
from .auth import current_user()
from .modules import User
def create_app(test_config=None) -> Flask:
app: Flask = Flask(__name__)
... (Configuration of SQLAlchemy, CSRF, Babel_JS, ... etc) ...
import accounting
class UserUtils(accounting.UserUtilityInterface[User]):
def can_view(self) -> bool:
return True
def can_edit(self) -> bool:
return "editor" in current_user().roles
def can_admin(self) -> bool:
return current_user().is_admin
def unauthorized(self) -> Response:
return redirect("/login")
@property
def cls(self) -> t.Type[User]:
return User
@property
def pk_column(self) -> Column:
return User.id
@property
def current_user(self) -> User | None:
return current_user()
def get_by_username(self, username: str) -> User | None:
return User.query.filter(User.username == username).first()
def get_pk(self, user: User) -> int:
return user.id
accounting.init_app(app, UserUtils())
... (Any other configuration) ...
return app

57
docs/source/history.rst Normal file
View File

@ -0,0 +1,57 @@
History
=======
I created my own private accounting application in Perl_/mod_perl_ in
2007, as part of my personal website. The first revision was made
using Perl/Mojolicious_ in 2019, with the aim of making it
mobile-friendly using Bootstrap_, and with modern back-end and
front-end technologies such as jQuery.
The second revision was done in Python_/Django_ in 2020, as I was
looking to change my career from PHP_/Laravel_ to Python, but lacked
experience with large Python projects. I needed something in my
portfolio and decided to work on the somewhat outdated Mojolicious
project.
Despite having no prior experience with Django, I spent two months
working late nights to create the `Mia! Accounting Django`_
application. It took me another 1.5 months to make it an independent
module, which I later released as an open source project on PyPI.
The application worked nicely for my household bookkeeping for two
years. However, new demands arose over time, especially with tracking
payables and receivables. This was critical `during the pandemic`_ as
more payments were made online with credit cards.
The biggest issue I encountered was with
`Django's MTV architectural pattern`_. Django takes over the control
flow. I had to override several parts of the `class-based views`_ for
different but yet simple control flow logic. In the end, it became
very difficult to track whether things went wrong because I overrode
something or because it just wouldn't work with the basic assumption
of the class-based views. By the time I realized it, it was too late
for me to drop Django's MTV and rewrite everything from class-based
views to function-based views.
Therefore, I decided to turn to microframeworks_ like Flask_. After
working with modularized Flask and FastAPI_ applications for two
years, I returned to the project and wrote its third revision using
Flask in 2023.
.. _Perl: https://www.perl.org
.. _mod_perl: https://perl.apache.org
.. _Mojolicious: https://mojolicious.org
.. _Bootstrap: https://getbootstrap.com
.. _jQuery: https://jquery.com
.. _Python: https://www.python.org
.. _Django: https://www.djangoproject.com
.. _PHP: https://www.php.net
.. _Laravel: https://laravel.com
.. _Mia! Accounting Django: https://github.com/imacat/mia-accounting-django
.. _during the pandemic: https://en.wikipedia.org/wiki/COVID-19_pandemic
.. _FastAPI: https://fastapi.tiangolo.com
.. _Django's MTV architectural pattern: https://docs.djangoproject.com/en/dev/faq/general/#django-appears-to-be-a-mvc-framework-but-you-call-the-controller-the-view-and-the-view-the-template-how-come-you-don-t-use-the-standard-names
.. _class-based views: https://docs.djangoproject.com/en/4.2/topics/class-based-views/
.. _microframeworks: https://en.wikipedia.org/wiki/Microframework
.. _Flask: https://flask.palletsprojects.com

View File

@ -10,6 +10,11 @@ Welcome to Mia! Accounting's documentation!
:maxdepth: 2 :maxdepth: 2
:caption: Contents: :caption: Contents:
intro
accounting
examples
history
Indices and tables Indices and tables

136
docs/source/intro.rst Normal file
View File

@ -0,0 +1,136 @@
Introduction
============
*Mia! Accounting* is an accounting module for Flask_ applications.
It implements `double-entry bookkeeping`_, and generates the following
accounting reports:
* Trial balance
* Income statement
* Balance sheet
In addition, *Mia! Accounting* tracks offsets for unpaid payables and
receivables.
Installation
------------
Install *Mia! Accounting* with ``pip``:
::
pip install mia-accounting
You may also download the from the `PyPI project page`_ or the
`release page`_ on the `Git repository`_.
Test Site and Live Demonstration
--------------------------------
You may find a working example in the `test site`_ in the
`source distribution`_. It is the simplest website that works with
*Mia! Accounting*. It is used in the automatic tests. It is the same
code run for `live demonstration`_.
If you do not have a running Flask application or do not know how to
start one, you may start with the test site.
Prerequisites
-------------
You need a running Flask application with database user login.
The primary key of the user data model must be integer.
The following front-end JavaScript libraries must be loaded. You may
download it locally or use CDN_.
* Bootstrap_ 5.2.3 or above
* FontAwesome_ 6.2.1 or above
* `Decimal.js`_ 6.4.3 or above
* `Tempus-Dominus`_ 6.4.3 or above
Configuration
-------------
You need to pass the Flask *app* and an implementation of
:py:class:`accounting.utils.user.UserUtilityInterface` to the
:py:func:`accounting.init_app` function. ``UserUtilityInterface``
contains everything *Mia! Accounting* needs.
See an example in :ref:`example-userutils`.
Database Initialization
-----------------------
After the configuration, you need to run
`flask_sqlalchemy.SQLAlchemy.create_all`_ to create the
database tables that *Mia! Accounting* uses.
*Mia! Accounting* adds three console commands:
* ``accounting-init-base``
* ``accounting-init-accounts``
* ``accounting-init-currencies``
After database tables are created, run
``accounting-init-base`` first, and then the other two commands.
::
% flask --app myapp accounting-init-base
% flask --app myapp accounting-init-accounts
% flask --app myapp accounting-init-currencies
Navigation Menu
---------------
Include the navigation menu in the `Bootstrap navigation bar`_ in your
base template:
::
<nav class="navbar navbar-expand-lg bg-body-tertiary bg-dark navbar-dark">
<div class="container-fluid">
...
<div id="collapsible-navbar" class="collapse navbar-collapse">
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
...
{% include "accounting/include/nav.html" %}
...
</ul>
...
</div>
</div>
</nav>
Check your Flask application and see how it works.
Documentation
-------------
Refer to the `documentation on Read the Docs`_.
.. _Flask: https://flask.palletsprojects.com
.. _double-entry bookkeeping: https://en.wikipedia.org/wiki/Double-entry_bookkeeping
.. _test site: https://github.com/imacat/mia-accounting/tree/main/tests/test_site
.. _source distribution: https://pypi.org/project/mia-accounting/#files
.. _live demonstration: https://accounting.imacat.idv.tw
.. _PyPI project page: https://pypi.org/project/mia-accounting
.. _release page: https://github.com/imacat/mia-accounting/releases
.. _Git repository: https://github.com/imacat/mia-accounting
.. _CDN: https://en.wikipedia.org/wiki/Content_delivery_network
.. _Bootstrap: https://getbootstrap.com
.. _FontAwesome: https://fontawesome.com
.. _Decimal.js: https://mikemcl.github.io/decimal.js
.. _Tempus-Dominus: https://getdatepicker.com
.. _flask_sqlalchemy.SQLAlchemy.create_all: https://flask-sqlalchemy.palletsprojects.com/en/3.0.x/api/#flask_sqlalchemy.SQLAlchemy.create_all
.. _Bootstrap navigation bar: https://getbootstrap.com/docs/5.3/components/navbar/
.. _documentation on Read the Docs: https://mia-accounting.readthedocs.io

View File

@ -1,7 +1,7 @@
# The Mia! Accounting Project. # The Mia! Accounting Project.
# Author: imacat@mail.imacat.idv.tw (imacat), 2022/8/21 # Author: imacat@mail.imacat.idv.tw (imacat), 2022/8/21
# Copyright (c) 2022 imacat. # Copyright (c) 2022-2023 imacat.
# #
# Licensed under the Apache License, Version 2.0 (the "License"); # Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License. # you may not use this file except in compliance with the License.
@ -15,6 +15,51 @@
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
[project]
name = "mia-accounting"
version = "1.0.1"
description = "A Flask accounting module."
readme = "README.rst"
requires-python = ">=3.11"
authors = [
{name = "imacat", email = "imacat@mail.imacat.idv.tw"},
]
keywords = ["mia", "accounting", "flask"]
classifiers = [
"Programming Language :: Python :: 3",
"License :: OSI Approved :: Apache Software License",
"Operating System :: OS Independent",
"Framework :: Flask",
"Topic :: Office/Business :: Financial :: Accounting",
]
dependencies = [
"flask",
"Flask-SQLAlchemy",
"Flask-WTF",
"Flask-Babel >= 3",
"Flask-Babel-JS",
]
[project.optional-dependencies]
test = [
"unittest",
"httpx",
"OpenCC",
]
[project.urls]
"Documentation" = "https://mia-accounting.readthedocs.io"
"Repository" = "https://github.com/imacat/mia-accounting"
"Bug Tracker" = "https://github.com/imacat/mia-accounting/issues"
"Demonstration" = "https://accounting.imacat.idv.tw"
[build-system] [build-system]
requires = ["setuptools>=42"] requires = ["setuptools>=42"]
build-backend = "setuptools.build_meta" build-backend = "setuptools.build_meta"
[tool.setuptools.exclude-package-data]
"*" = [
"babel.cfg",
"*.pot",
"*.po",
]

View File

@ -1,56 +0,0 @@
# The Mia! Accounting Project.
# Author: imacat@mail.imacat.idv.tw (imacat), 2022/8/21
# Copyright (c) 2022-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.
[metadata]
name = mia-accounting
version = 0.11.0
author = imacat
author_email = imacat@mail.imacat.idv.tw
description = The Mia! Accounting project.
long_description = file: README.rst
long_description_content_type = text/x-rst
url = https://github.com/imacat/mia-accounting
project_urls =
Bug Tracker = https://github.com/imacat/mia-accounting/issues
classifiers =
Programming Language :: Python :: 3
License :: OSI Approved :: Apache Software License
Operating System :: OS Independent
Framework :: Flask
Topic :: Office/Business :: Financial :: Accounting
[options]
package_dir =
= src
python_requires = >=3.11
install_requires =
flask
Flask-SQLAlchemy
Flask-WTF
Flask-Babel >= 3
Flask-Babel-JS
tests_require =
unittest
httpx
OpenCC
[options.package_data]
accounting =
static/**
templates/**
translations/*/LC_MESSAGES/*.mo
data/**

View File

@ -22,6 +22,7 @@ from flask import Blueprint, render_template
from accounting.models import BaseAccount from accounting.models import BaseAccount
from accounting.utils.pagination import Pagination from accounting.utils.pagination import Pagination
from accounting.utils.permission import has_permission, can_view from accounting.utils.permission import has_permission, can_view
from .queries import get_base_account_query
bp: Blueprint = Blueprint("base-account", __name__) bp: Blueprint = Blueprint("base-account", __name__)
"""The view blueprint for the base account management.""" """The view blueprint for the base account management."""
@ -34,7 +35,6 @@ def list_accounts() -> str:
:return: The account list. :return: The account list.
""" """
from .queries import get_base_account_query
accounts: list[BaseAccount] = get_base_account_query() accounts: list[BaseAccount] = get_base_account_query()
pagination: Pagination = Pagination[BaseAccount](accounts) pagination: Pagination = Pagination[BaseAccount](accounts)
return render_template("accounting/base-account/list.html", return render_template("accounting/base-account/list.html",

View File

@ -34,6 +34,7 @@ from accounting.utils.pagination import Pagination
from accounting.utils.permission import has_permission, can_view, can_edit from accounting.utils.permission import has_permission, can_view, can_edit
from accounting.utils.user import get_current_user_pk from accounting.utils.user import get_current_user_pk
from .forms import CurrencyForm from .forms import CurrencyForm
from .queries import get_currency_query
bp: Blueprint = Blueprint("currency", __name__) bp: Blueprint = Blueprint("currency", __name__)
"""The view blueprint for the currency management.""" """The view blueprint for the currency management."""
@ -48,7 +49,6 @@ def list_currencies() -> str:
:return: The currency list. :return: The currency list.
""" """
from .queries import get_currency_query
currencies: list[Currency] = get_currency_query() currencies: list[Currency] = get_currency_query()
pagination: Pagination = Pagination[Currency](currencies) pagination: Pagination = Pagination[Currency](currencies)
return render_template("accounting/currency/list.html", return render_template("accounting/currency/list.html",

View File

@ -23,6 +23,7 @@ from flask import abort
from sqlalchemy.orm import selectinload from sqlalchemy.orm import selectinload
from werkzeug.routing import BaseConverter from werkzeug.routing import BaseConverter
from accounting import db
from accounting.models import JournalEntry, JournalEntryLineItem from accounting.models import JournalEntry, JournalEntryLineItem
from accounting.utils.journal_entry_types import JournalEntryType from accounting.utils.journal_entry_types import JournalEntryType
@ -37,13 +38,7 @@ class JournalEntryConverter(BaseConverter):
:param value: The journal entry ID. :param value: The journal entry ID.
:return: The corresponding journal entry. :return: The corresponding journal entry.
""" """
journal_entry: JournalEntry | None = JournalEntry.query\ journal_entry: JournalEntry | None = db.session.get(JournalEntry, value)
.join(JournalEntryLineItem)\
.filter(JournalEntry.id == value)\
.options(selectinload(JournalEntry.line_items)
.selectinload(JournalEntryLineItem.offsets)
.selectinload(JournalEntryLineItem.journal_entry))\
.first()
if journal_entry is None: if journal_entry is None:
abort(404) abort(404)
return journal_entry return journal_entry

View File

@ -31,7 +31,7 @@ from accounting import db
from accounting.forms import ACCOUNT_REQUIRED, AccountExists, IsDebitAccount, \ from accounting.forms import ACCOUNT_REQUIRED, AccountExists, IsDebitAccount, \
IsCreditAccount IsCreditAccount
from accounting.locale import lazy_gettext from accounting.locale import lazy_gettext
from accounting.models import Account, JournalEntryLineItem from accounting.models import Account, JournalEntry, JournalEntryLineItem
from accounting.template_filters import format_amount from accounting.template_filters import format_amount
from accounting.utils.cast import be from accounting.utils.cast import be
from accounting.utils.random_id import new_id from accounting.utils.random_id import new_id
@ -127,10 +127,8 @@ class KeepAccountWhenHavingOffset:
assert isinstance(form, LineItemForm) assert isinstance(form, LineItemForm)
if field.data is None or form.id.data is None: if field.data is None or form.id.data is None:
return return
line_item: JournalEntryLineItem | None = db.session\ line_item: JournalEntryLineItem | None \
.query(JournalEntryLineItem)\ = db.session.get(JournalEntryLineItem, form.id.data)
.filter(JournalEntryLineItem.id == form.id.data)\
.options(selectinload(JournalEntryLineItem.offsets)).first()
if line_item is None or len(line_item.offsets) == 0: if line_item is None or len(line_item.offsets) == 0:
return return
if field.data != line_item.account_code: if field.data != line_item.account_code:
@ -344,14 +342,13 @@ class LineItemForm(FlaskForm):
def get_offsets() -> list[JournalEntryLineItem]: def get_offsets() -> list[JournalEntryLineItem]:
if not self.is_need_offset or self.id.data is None: if not self.is_need_offset or self.id.data is None:
return [] return []
return JournalEntryLineItem.query\ return JournalEntryLineItem.query.join(JournalEntry)\
.filter(JournalEntryLineItem.original_line_item_id .filter(JournalEntryLineItem.original_line_item_id
== self.id.data)\ == self.id.data)\
.order_by(JournalEntry.date, JournalEntry.no,
JournalEntryLineItem.no)\
.options(selectinload(JournalEntryLineItem.journal_entry), .options(selectinload(JournalEntryLineItem.journal_entry),
selectinload(JournalEntryLineItem.account), selectinload(JournalEntryLineItem.account)).all()
selectinload(JournalEntryLineItem.offsets)
.selectinload(
JournalEntryLineItem.journal_entry)).all()
setattr(self, "__offsets", get_offsets()) setattr(self, "__offsets", get_offsets())
return getattr(self, "__offsets") return getattr(self, "__offsets")

View File

@ -660,12 +660,8 @@ class JournalEntryLineItem(db.Model):
nullable=True) nullable=True)
"""The ID of the original line item.""" """The ID of the original line item."""
original_line_item = db.relationship("JournalEntryLineItem", original_line_item = db.relationship("JournalEntryLineItem",
back_populates="offsets",
remote_side=id, passive_deletes=True) remote_side=id, passive_deletes=True)
"""The original line item.""" """The original line item."""
offsets = db.relationship("JournalEntryLineItem",
back_populates="original_line_item")
"""The offset items."""
currency_code = db.Column(db.String, currency_code = db.Column(db.String,
db.ForeignKey(Currency.code, onupdate="CASCADE"), db.ForeignKey(Currency.code, onupdate="CASCADE"),
nullable=False) nullable=False)
@ -758,6 +754,21 @@ class JournalEntryLineItem(db.Model):
""" """
setattr(self, "__net_balance", net_balance) setattr(self, "__net_balance", net_balance)
@property
def offsets(self) -> list[t.Self]:
"""Returns the offset items.
:return: The offset items.
"""
if not hasattr(self, "__offsets"):
cls: t.Type[t.Self] = self.__class__
offsets: list[t.Self] = cls.query.join(JournalEntry)\
.filter(JournalEntryLineItem.original_line_item_id == self.id)\
.order_by(JournalEntry.date, JournalEntry.no,
cls.is_debit, cls.no).all()
setattr(self, "__offsets", offsets)
return getattr(self, "__offsets")
@property @property
def query_values(self) -> list[str]: def query_values(self) -> list[str]:
"""Returns the values to be queried. """Returns the values to be queried.

View File

@ -137,6 +137,7 @@ class AccountCollector:
.join(JournalEntry).join(Account)\ .join(JournalEntry).join(Account)\
.filter(*conditions)\ .filter(*conditions)\
.group_by(Account.id, Account.base_code, Account.no)\ .group_by(Account.id, Account.base_code, Account.no)\
.having(balance_func != 0)\
.order_by(Account.base_code, Account.no) .order_by(Account.base_code, Account.no)
account_balances: list[sa.Row] \ account_balances: list[sa.Row] \
= db.session.execute(select_balance).all() = db.session.execute(select_balance).all()

View File

@ -269,6 +269,7 @@ class IncomeStatement(BaseReport):
.join(JournalEntry).join(Account)\ .join(JournalEntry).join(Account)\
.filter(*conditions)\ .filter(*conditions)\
.group_by(Account.id)\ .group_by(Account.id)\
.having(balance_func != 0)\
.order_by(Account.base_code, Account.no) .order_by(Account.base_code, Account.no)
balances: list[sa.Row] = db.session.execute(select_balances).all() balances: list[sa.Row] = db.session.execute(select_balances).all()
accounts: dict[int, Account] \ accounts: dict[int, Account] \

View File

@ -77,6 +77,8 @@ class CSVRow(BaseCSVRow):
"""Constructs a row in the CSV. """Constructs a row in the CSV.
:param journal_entry_date: The journal entry date. :param journal_entry_date: The journal entry date.
:param currency: The currency.
:param account: The account.
:param description: The description. :param description: The description.
:param debit: The debit amount. :param debit: The debit amount.
:param credit: The credit amount. :param credit: The credit amount.
@ -116,6 +118,7 @@ class PageParams(BasePageParams):
"""Constructs the HTML page parameters. """Constructs the HTML page parameters.
:param period: The period. :param period: The period.
:param pagination: The pagination.
:param line_items: The line items. :param line_items: The line items.
""" """
self.period: Period = period self.period: Period = period

View File

@ -191,6 +191,7 @@ class TrialBalance(BaseReport):
.join(JournalEntry).join(Account)\ .join(JournalEntry).join(Account)\
.filter(*conditions)\ .filter(*conditions)\
.group_by(Account.id)\ .group_by(Account.id)\
.having(balance_func != 0)\
.order_by(Account.base_code, Account.no) .order_by(Account.base_code, Account.no)
balances: list[sa.Row] = db.session.execute(select_balances).all() balances: list[sa.Row] = db.session.execute(select_balances).all()
accounts: dict[int, Account] \ accounts: dict[int, Account] \

View File

@ -123,7 +123,7 @@ class JournalEntryAccountSelector {
option.setShown(false); option.setShown(false);
} }
} }
if (!isAnyMatched) { if (!isAnyMatched && this.#isShowMore) {
this.#optionList.classList.add("d-none"); this.#optionList.classList.add("d-none");
this.#queryNoResult.classList.remove("d-none"); this.#queryNoResult.classList.remove("d-none");
} else { } else {

View File

@ -50,10 +50,10 @@ First written: 2023/3/14
{% endfor %} {% endfor %}
</ul> </ul>
</div> </div>
{% if line_item.balance %} {% if line_item.net_balance %}
<div class="d-flex justify-content-between"> <div class="d-flex justify-content-between">
<div>{{ A_("Net balance") }}</div> <div>{{ A_("Net balance") }}</div>
<div>{{ line_item.balance|accounting_format_amount }}</div> <div>{{ line_item.net_balance|accounting_format_amount }}</div>
</div> </div>
{% else %} {% else %}
<div class="d-flex justify-content-between"> <div class="d-flex justify-content-between">

View File

@ -1,15 +1,15 @@
# Chinese (Traditional) translations for the Mia! Accounting project. # Chinese (Traditional) translations for the Mia! Accounting project.
# Copyright (C) 2023 imacat # Copyright (C) 2023 imacat
# This file is distributed under the same license as the Mia! Accounting # This file is distributed under the same license as the Mia! Accounting
# Flask project. # project.
# imacat <imacat@mail.imacat.idv.tw>, 2023. # imacat <imacat@mail.imacat.idv.tw>, 2023.
# #
msgid "" msgid ""
msgstr "" msgstr ""
"Project-Id-Version: Mia! Accounting 0.0.0\n" "Project-Id-Version: mia-accounting 1.0.0\n"
"Report-Msgid-Bugs-To: imacat@mail.imacat.idv.tw\n" "Report-Msgid-Bugs-To: imacat@mail.imacat.idv.tw\n"
"POT-Creation-Date: 2023-03-23 00:45+0800\n" "POT-Creation-Date: 2023-04-06 02:34+0800\n"
"PO-Revision-Date: 2023-03-23 00:46+0800\n" "PO-Revision-Date: 2023-04-06 02:34+0800\n"
"Last-Translator: imacat <imacat@mail.imacat.idv.tw>\n" "Last-Translator: imacat <imacat@mail.imacat.idv.tw>\n"
"Language: zh_Hant\n" "Language: zh_Hant\n"
"Language-Team: zh_Hant <imacat@mail.imacat.idv.tw>\n" "Language-Team: zh_Hant <imacat@mail.imacat.idv.tw>\n"
@ -20,10 +20,10 @@ msgstr ""
"Generated-By: Babel 2.12.1\n" "Generated-By: Babel 2.12.1\n"
#: src/accounting/forms.py:33 #: src/accounting/forms.py:33
#: src/accounting/static/js/journal-entry-form.js:980 #: src/accounting/static/js/journal-entry-form.js:1065
#: src/accounting/static/js/journal-entry-line-item-editor.js:430 #: src/accounting/static/js/journal-entry-line-item-editor.js:411
#: src/accounting/static/js/option-form.js:530 #: src/accounting/static/js/option-form.js:537
#: src/accounting/static/js/option-form.js:796 #: src/accounting/static/js/option-form.js:803
msgid "Please select the account." msgid "Please select the account."
msgstr "請選擇科目。" msgstr "請選擇科目。"
@ -35,22 +35,22 @@ msgstr "沒有這個貨幣。"
msgid "The account does not exist." msgid "The account does not exist."
msgstr "沒有這個科目。" msgstr "沒有這個科目。"
#: src/accounting/models.py:563 #: src/accounting/models.py:562
#, python-format #, python-format
msgid "Cash Disbursement Journal Entry#%(id)s" msgid "Cash Disbursement Journal Entry#%(id)s"
msgstr "現金支出傳票#%(id)s" msgstr "現金支出傳票#%(id)s"
#: src/accounting/models.py:566 #: src/accounting/models.py:565
#, python-format #, python-format
msgid "Cash Receipt Journal Entry#%(id)s" msgid "Cash Receipt Journal Entry#%(id)s"
msgstr "現金收入傳票#%(id)s" msgstr "現金收入傳票#%(id)s"
#: src/accounting/models.py:567 #: src/accounting/models.py:566
#, python-format #, python-format
msgid "Transfer Journal Entry#%(id)s" msgid "Transfer Journal Entry#%(id)s"
msgstr "轉帳傳票#%(id)s" msgstr "轉帳傳票#%(id)s"
#: src/accounting/models.py:700 #: src/accounting/models.py:699
#, python-format #, python-format
msgid "%(date)s %(description)s %(amount)s" msgid "%(date)s %(description)s %(amount)s"
msgstr "%(date)s %(description)s %(amount)s" msgstr "%(date)s %(description)s %(amount)s"
@ -92,7 +92,7 @@ msgid "A nominal account does not need offset."
msgstr "虛科目不需抵銷。" msgstr "虛科目不需抵銷。"
#: src/accounting/account/forms.py:75 #: src/accounting/account/forms.py:75
#: src/accounting/static/js/account-form.js:181 #: src/accounting/static/js/account-form.js:189
msgid "Please select the base account." msgid "Please select the base account."
msgstr "請選擇基本科目。" msgstr "請選擇基本科目。"
@ -160,8 +160,8 @@ msgstr "不能用這個代碼。"
#: src/accounting/currency/forms.py:62 src/accounting/option/forms.py:124 #: src/accounting/currency/forms.py:62 src/accounting/option/forms.py:124
#: src/accounting/option/forms.py:148 #: src/accounting/option/forms.py:148
#: src/accounting/static/js/currency-form.js:153 #: src/accounting/static/js/currency-form.js:153
#: src/accounting/static/js/option-form.js:525 #: src/accounting/static/js/option-form.js:532
#: src/accounting/static/js/option-form.js:780 #: src/accounting/static/js/option-form.js:787
msgid "Please fill in the name." msgid "Please fill in the name."
msgstr "請填上名稱。" msgstr "請填上名稱。"
@ -218,23 +218,23 @@ msgid "The currency must not be changed when there is offset."
msgstr "抵銷過不可變更貨幣。" msgstr "抵銷過不可變更貨幣。"
#: src/accounting/journal_entry/forms/currency.py:99 #: src/accounting/journal_entry/forms/currency.py:99
#: src/accounting/static/js/journal-entry-form.js:729 #: src/accounting/static/js/journal-entry-form.js:773
msgid "Please add some line items." msgid "Please add some line items."
msgstr "請加上分錄。" msgstr "請加上分錄。"
#: src/accounting/journal_entry/forms/currency.py:112 #: src/accounting/journal_entry/forms/currency.py:112
#: src/accounting/static/js/journal-entry-form.js:516 #: src/accounting/static/js/journal-entry-form.js:522
msgid "The totals of the debit and credit amounts do not match." msgid "The totals of the debit and credit amounts do not match."
msgstr "借方貸方合計不符。 " msgstr "借方貸方合計不符。 "
#: src/accounting/journal_entry/forms/journal_entry.py:48 #: src/accounting/journal_entry/forms/journal_entry.py:48
#: src/accounting/static/js/journal-entry-form.js:270 #: src/accounting/static/js/journal-entry-form.js:264
#: src/accounting/static/js/period-chooser.js:273 #: src/accounting/static/js/period-chooser.js:265
msgid "Please fill in the date." msgid "Please fill in the date."
msgstr "請填上日期。" msgstr "請填上日期。"
#: src/accounting/journal_entry/forms/journal_entry.py:64 #: src/accounting/journal_entry/forms/journal_entry.py:64
#: src/accounting/static/js/journal-entry-form.js:275 #: src/accounting/static/js/journal-entry-form.js:269
msgid "The date cannot be earlier than the original line items." msgid "The date cannot be earlier than the original line items."
msgstr "日期不可早於原始分錄。" msgstr "日期不可早於原始分錄。"
@ -243,7 +243,7 @@ msgid "The date cannot be later than the offset items."
msgstr "日期不可晚於抵銷日期。" msgstr "日期不可晚於抵銷日期。"
#: src/accounting/journal_entry/forms/journal_entry.py:88 #: src/accounting/journal_entry/forms/journal_entry.py:88
#: src/accounting/static/js/journal-entry-form.js:305 #: src/accounting/static/js/journal-entry-form.js:299
msgid "Please add some currencies." msgid "Please add some currencies."
msgstr "請加上貨幣。" msgstr "請加上貨幣。"
@ -284,12 +284,12 @@ msgid "A receivable line item cannot start from credit."
msgstr "不可由貸方新建應收款。" msgstr "不可由貸方新建應收款。"
#: src/accounting/journal_entry/forms/line_item.py:180 #: src/accounting/journal_entry/forms/line_item.py:180
#: src/accounting/static/js/journal-entry-line-item-editor.js:455 #: src/accounting/static/js/journal-entry-line-item-editor.js:436
msgid "Please fill in a positive amount." msgid "Please fill in a positive amount."
msgstr "金額請填正數。" msgstr "金額請填正數。"
#: src/accounting/journal_entry/forms/line_item.py:222 #: src/accounting/journal_entry/forms/line_item.py:222
#: src/accounting/static/js/journal-entry-line-item-editor.js:461 #: src/accounting/static/js/journal-entry-line-item-editor.js:442
#, python-format #, python-format
msgid "" msgid ""
"The amount must not exceed the net balance %(balance)s of the original " "The amount must not exceed the net balance %(balance)s of the original "
@ -297,7 +297,7 @@ msgid ""
msgstr "金額不可超過原始分錄凈額 %(balance)s 。" msgstr "金額不可超過原始分錄凈額 %(balance)s 。"
#: src/accounting/journal_entry/forms/line_item.py:243 #: src/accounting/journal_entry/forms/line_item.py:243
#: src/accounting/static/js/journal-entry-line-item-editor.js:469 #: src/accounting/static/js/journal-entry-line-item-editor.js:450
#, python-format #, python-format
msgid "The amount must not be less than the offset total %(total)s." msgid "The amount must not be less than the offset total %(total)s."
msgstr "金額不可低於抵銷總額 %(total)s 。" msgstr "金額不可低於抵銷總額 %(total)s 。"
@ -327,8 +327,8 @@ msgid "This account is not for expense."
msgstr "科目不是支出科目。" msgstr "科目不是支出科目。"
#: src/accounting/option/forms.py:137 src/accounting/option/forms.py:161 #: src/accounting/option/forms.py:137 src/accounting/option/forms.py:161
#: src/accounting/static/js/option-form.js:535 #: src/accounting/static/js/option-form.js:542
#: src/accounting/static/js/option-form.js:813 #: src/accounting/static/js/option-form.js:820
msgid "Please fill in the description template." msgid "Please fill in the description template."
msgstr "請填上摘要範本。" msgstr "請填上摘要範本。"
@ -408,18 +408,18 @@ msgstr "去年"
msgid "All" msgid "All"
msgstr "全部" msgstr "全部"
#: src/accounting/report/reports/balance_sheet.py:422 #: src/accounting/report/reports/balance_sheet.py:423
#: src/accounting/report/reports/balance_sheet.py:426 #: src/accounting/report/reports/balance_sheet.py:427
#: src/accounting/report/reports/balance_sheet.py:438 #: src/accounting/report/reports/balance_sheet.py:439
#: src/accounting/report/reports/balance_sheet.py:440 #: src/accounting/report/reports/balance_sheet.py:441
#: src/accounting/report/reports/income_expenses.py:189 #: src/accounting/report/reports/income_expenses.py:189
#: src/accounting/report/reports/income_expenses.py:423 #: src/accounting/report/reports/income_expenses.py:423
#: src/accounting/report/reports/income_statement.py:299 #: src/accounting/report/reports/income_statement.py:300
#: src/accounting/report/reports/ledger.py:171 #: src/accounting/report/reports/ledger.py:171
#: src/accounting/report/reports/ledger.py:380 #: src/accounting/report/reports/ledger.py:380
#: src/accounting/report/reports/trial_balance.py:228 #: src/accounting/report/reports/trial_balance.py:229
#: src/accounting/templates/accounting/journal-entry/disbursement/detail.html:43 #: src/accounting/templates/accounting/journal-entry/disbursement/detail.html:43
#: src/accounting/templates/accounting/journal-entry/include/form-debit-credit.html:37 #: src/accounting/templates/accounting/journal-entry/include/form-debit-credit.html:38
#: src/accounting/templates/accounting/journal-entry/receipt/detail.html:43 #: src/accounting/templates/accounting/journal-entry/receipt/detail.html:43
#: src/accounting/templates/accounting/journal-entry/transfer/detail.html:39 #: src/accounting/templates/accounting/journal-entry/transfer/detail.html:39
#: src/accounting/templates/accounting/journal-entry/transfer/detail.html:55 #: src/accounting/templates/accounting/journal-entry/transfer/detail.html:55
@ -455,7 +455,7 @@ msgstr "日期"
#: src/accounting/report/reports/income_expenses.py:407 #: src/accounting/report/reports/income_expenses.py:407
#: src/accounting/report/reports/journal.py:156 #: src/accounting/report/reports/journal.py:156
#: src/accounting/report/reports/trial_balance.py:224 #: src/accounting/report/reports/trial_balance.py:225
#: src/accounting/templates/accounting/journal-entry/include/journal-entry-line-item-editor-modal.html:57 #: src/accounting/templates/accounting/journal-entry/include/journal-entry-line-item-editor-modal.html:57
#: src/accounting/templates/accounting/option/include/recurring-item-editor-modal.html:39 #: src/accounting/templates/accounting/option/include/recurring-item-editor-modal.html:39
#: src/accounting/templates/accounting/report/include/toolbar-buttons.html:90 #: src/accounting/templates/accounting/report/include/toolbar-buttons.html:90
@ -527,7 +527,7 @@ msgstr "稅後淨利"
msgid "net income or loss for current period" msgid "net income or loss for current period"
msgstr "本期損益" msgstr "本期損益"
#: src/accounting/report/reports/income_statement.py:300 #: src/accounting/report/reports/income_statement.py:301
#: src/accounting/templates/accounting/journal-entry/include/journal-entry-line-item-editor-modal.html:65 #: src/accounting/templates/accounting/journal-entry/include/journal-entry-line-item-editor-modal.html:65
#: src/accounting/templates/accounting/report/income-statement.html:55 #: src/accounting/templates/accounting/report/income-statement.html:55
msgid "Amount" msgid "Amount"
@ -543,7 +543,7 @@ msgstr "貨幣"
#: src/accounting/report/reports/journal.py:157 #: src/accounting/report/reports/journal.py:157
#: src/accounting/report/reports/ledger.py:367 #: src/accounting/report/reports/ledger.py:367
#: src/accounting/report/reports/trial_balance.py:224 #: src/accounting/report/reports/trial_balance.py:225
#: src/accounting/templates/accounting/journal-entry/transfer/detail.html:33 #: src/accounting/templates/accounting/journal-entry/transfer/detail.html:33
#: src/accounting/templates/accounting/journal-entry/transfer/include/form-currency.html:30 #: src/accounting/templates/accounting/journal-entry/transfer/include/form-currency.html:30
#: src/accounting/templates/accounting/report/journal.html:57 #: src/accounting/templates/accounting/report/journal.html:57
@ -555,7 +555,7 @@ msgstr "借方"
#: src/accounting/report/reports/journal.py:157 #: src/accounting/report/reports/journal.py:157
#: src/accounting/report/reports/ledger.py:367 #: src/accounting/report/reports/ledger.py:367
#: src/accounting/report/reports/trial_balance.py:225 #: src/accounting/report/reports/trial_balance.py:226
#: src/accounting/templates/accounting/journal-entry/transfer/detail.html:49 #: src/accounting/templates/accounting/journal-entry/transfer/detail.html:49
#: src/accounting/templates/accounting/journal-entry/transfer/include/form-currency.html:41 #: src/accounting/templates/accounting/journal-entry/transfer/include/form-currency.html:41
#: src/accounting/templates/accounting/report/journal.html:58 #: src/accounting/templates/accounting/report/journal.html:58
@ -581,17 +581,17 @@ msgstr "貸方"
msgid "Search" msgid "Search"
msgstr "搜尋" msgstr "搜尋"
#: src/accounting/report/utils/report_chooser.py:89 #: src/accounting/report/utils/report_chooser.py:92
msgid "Journal" msgid "Income and Expenses Log"
msgstr "日記簿" msgstr "收支帳"
#: src/accounting/report/utils/report_chooser.py:99 #: src/accounting/report/utils/report_chooser.py:105
msgid "Ledger" msgid "Ledger"
msgstr "分類帳" msgstr "分類帳"
#: src/accounting/report/utils/report_chooser.py:114 #: src/accounting/report/utils/report_chooser.py:117
msgid "Income and Expenses Log" msgid "Journal"
msgstr "收支帳" msgstr "日記簿"
#: src/accounting/report/utils/report_chooser.py:127 #: src/accounting/report/utils/report_chooser.py:127
msgid "Trial Balance" msgid "Trial Balance"
@ -605,110 +605,110 @@ msgstr "損益表"
msgid "Balance Sheet" msgid "Balance Sheet"
msgstr "資產負債表" msgstr "資產負債表"
#: src/accounting/static/js/account-form.js:198 #: src/accounting/static/js/account-form.js:206
msgid "Please fill in the title." msgid "Please fill in the title."
msgstr "請填上標題。" msgstr "請填上標題。"
#: src/accounting/static/js/description-editor.js:756 #: src/accounting/static/js/description-editor.js:951
#: src/accounting/static/js/description-editor.js:934 #: src/accounting/static/js/description-editor.js:1129
msgid "Please fill in the tag." msgid "Please fill in the tag."
msgstr "請填上標籤。" msgstr "請填上標籤。"
#: src/accounting/static/js/description-editor.js:766 #: src/accounting/static/js/description-editor.js:961
#: src/accounting/static/js/description-editor.js:954 #: src/accounting/static/js/description-editor.js:1149
msgid "Please fill in the origin." msgid "Please fill in the origin."
msgstr "請填上起點。" msgstr "請填上起點。"
#: src/accounting/static/js/description-editor.js:776 #: src/accounting/static/js/description-editor.js:971
#: src/accounting/static/js/description-editor.js:964 #: src/accounting/static/js/description-editor.js:1159
msgid "Please fill in the destination." msgid "Please fill in the destination."
msgstr "請填上終點。" msgstr "請填上終點。"
#: src/accounting/static/js/description-editor.js:944 #: src/accounting/static/js/description-editor.js:1139
msgid "Please fill in the route." msgid "Please fill in the route."
msgstr "請填上路線名稱。" msgstr "請填上路線名稱。"
#: src/accounting/static/js/description-editor.js:998 #: src/accounting/static/js/description-editor.js:1192
msgid "January" msgid "January"
msgstr "一月" msgstr "一月"
#: src/accounting/static/js/description-editor.js:998 #: src/accounting/static/js/description-editor.js:1192
msgid "February" msgid "February"
msgstr "二月" msgstr "二月"
#: src/accounting/static/js/description-editor.js:998 #: src/accounting/static/js/description-editor.js:1192
msgid "March" msgid "March"
msgstr "三月" msgstr "三月"
#: src/accounting/static/js/description-editor.js:998 #: src/accounting/static/js/description-editor.js:1192
msgid "April" msgid "April"
msgstr "四月" msgstr "四月"
#: src/accounting/static/js/description-editor.js:999 #: src/accounting/static/js/description-editor.js:1193
msgid "May" msgid "May"
msgstr "五月" msgstr "五月"
#: src/accounting/static/js/description-editor.js:999 #: src/accounting/static/js/description-editor.js:1193
msgid "June" msgid "June"
msgstr "六月" msgstr "六月"
#: src/accounting/static/js/description-editor.js:999 #: src/accounting/static/js/description-editor.js:1193
msgid "July" msgid "July"
msgstr "七月" msgstr "七月"
#: src/accounting/static/js/description-editor.js:999 #: src/accounting/static/js/description-editor.js:1193
msgid "August" msgid "August"
msgstr "八月" msgstr "八月"
#: src/accounting/static/js/description-editor.js:1000 #: src/accounting/static/js/description-editor.js:1194
msgid "September" msgid "September"
msgstr "九月" msgstr "九月"
#: src/accounting/static/js/description-editor.js:1000 #: src/accounting/static/js/description-editor.js:1194
msgid "October" msgid "October"
msgstr "十月" msgstr "十月"
#: src/accounting/static/js/description-editor.js:1000 #: src/accounting/static/js/description-editor.js:1194
msgid "November" msgid "November"
msgstr "十一月" msgstr "十一月"
#: src/accounting/static/js/description-editor.js:1000 #: src/accounting/static/js/description-editor.js:1194
msgid "December" msgid "December"
msgstr "十二月" msgstr "十二月"
#: src/accounting/static/js/journal-entry-form.js:985 #: src/accounting/static/js/journal-entry-form.js:1070
#: src/accounting/static/js/journal-entry-line-item-editor.js:449 #: src/accounting/static/js/journal-entry-line-item-editor.js:430
msgid "Please fill in the amount." msgid "Please fill in the amount."
msgstr "請填上金額。" msgstr "請填上金額。"
#: src/accounting/static/js/journal-entry-form.js:1012 #: src/accounting/static/js/journal-entry-form.js:1092
#: src/accounting/templates/accounting/journal-entry/include/detail-line-items.html:34 #: src/accounting/templates/accounting/journal-entry/include/detail-line-items.html:34
#: src/accounting/templates/accounting/journal-entry/include/form-line-item.html:38 #: src/accounting/templates/accounting/journal-entry/include/form-line-item.html:38
#, python-format #, python-format
msgid "Offset %(item)s" msgid "Offset %(item)s"
msgstr "抵銷 %(item)s" msgstr "抵銷 %(item)s"
#: src/accounting/static/js/period-chooser.js:278 #: src/accounting/static/js/period-chooser.js:270
msgid "The date is too early." msgid "The date is too early."
msgstr "日期太早。" msgstr "日期太早。"
#: src/accounting/static/js/period-chooser.js:377 #: src/accounting/static/js/period-chooser.js:369
msgid "Please fill in the start date." msgid "Please fill in the start date."
msgstr "請填上開始日期。" msgstr "請填上開始日期。"
#: src/accounting/static/js/period-chooser.js:382 #: src/accounting/static/js/period-chooser.js:374
msgid "The start date is too early." msgid "The start date is too early."
msgstr "開始日期太早。" msgstr "開始日期太早。"
#: src/accounting/static/js/period-chooser.js:387 #: src/accounting/static/js/period-chooser.js:379
msgid "The start date cannot be beyond the end date." msgid "The start date cannot be beyond the end date."
msgstr "開始日期不可晚於結束日期。" msgstr "開始日期不可晚於結束日期。"
#: src/accounting/static/js/period-chooser.js:405 #: src/accounting/static/js/period-chooser.js:397
msgid "Please fill in the end date." msgid "Please fill in the end date."
msgstr "請填上結束日期。" msgstr "請填上結束日期。"
#: src/accounting/static/js/period-chooser.js:410 #: src/accounting/static/js/period-chooser.js:402
msgid "The end date cannot be beyond the start date." msgid "The end date cannot be beyond the start date."
msgstr "結束日期不可早於開始日期。" msgstr "結束日期不可早於開始日期。"
@ -777,7 +777,7 @@ msgstr "你確定要刪掉這個科目嗎?"
#: src/accounting/templates/accounting/account/include/form.html:112 #: src/accounting/templates/accounting/account/include/form.html:112
#: src/accounting/templates/accounting/currency/detail.html:79 #: src/accounting/templates/accounting/currency/detail.html:79
#: src/accounting/templates/accounting/journal-entry/include/account-selector-modal.html:49 #: src/accounting/templates/accounting/journal-entry/include/account-selector-modal.html:49
#: src/accounting/templates/accounting/journal-entry/include/description-editor-modal.html:193 #: src/accounting/templates/accounting/journal-entry/include/description-editor-modal.html:194
#: src/accounting/templates/accounting/journal-entry/include/detail.html:84 #: src/accounting/templates/accounting/journal-entry/include/detail.html:84
#: src/accounting/templates/accounting/journal-entry/include/journal-entry-line-item-editor-modal.html:70 #: src/accounting/templates/accounting/journal-entry/include/journal-entry-line-item-editor-modal.html:70
#: src/accounting/templates/accounting/option/include/recurring-account-selector-modal.html:48 #: src/accounting/templates/accounting/option/include/recurring-account-selector-modal.html:48
@ -824,7 +824,7 @@ msgstr "科目管理"
#: src/accounting/templates/accounting/account/list.html:32 #: src/accounting/templates/accounting/account/list.html:32
#: src/accounting/templates/accounting/currency/list.html:32 #: src/accounting/templates/accounting/currency/list.html:32
#: src/accounting/templates/accounting/journal-entry/include/form-debit-credit.html:44 #: src/accounting/templates/accounting/journal-entry/include/form-debit-credit.html:45
#: src/accounting/templates/accounting/journal-entry/include/form.html:64 #: src/accounting/templates/accounting/journal-entry/include/form.html:64
#: src/accounting/templates/accounting/option/include/form-recurring-expense-income.html:37 #: src/accounting/templates/accounting/option/include/form-recurring-expense-income.html:37
#: src/accounting/templates/accounting/report/include/toolbar-buttons.html:26 #: src/accounting/templates/accounting/report/include/toolbar-buttons.html:26
@ -839,6 +839,8 @@ msgstr "新增"
#: src/accounting/templates/accounting/journal-entry/include/account-selector-modal.html:46 #: src/accounting/templates/accounting/journal-entry/include/account-selector-modal.html:46
#: src/accounting/templates/accounting/journal-entry/include/original-line-item-selector-modal.html:51 #: src/accounting/templates/accounting/journal-entry/include/original-line-item-selector-modal.html:51
#: src/accounting/templates/accounting/journal-entry/order.html:82 #: src/accounting/templates/accounting/journal-entry/order.html:82
#: src/accounting/templates/accounting/option/detail.html:67
#: src/accounting/templates/accounting/option/detail.html:83
#: src/accounting/templates/accounting/option/include/recurring-account-selector-modal.html:45 #: src/accounting/templates/accounting/option/include/recurring-account-selector-modal.html:45
#: src/accounting/templates/accounting/report/balance-sheet.html:110 #: src/accounting/templates/accounting/report/balance-sheet.html:110
#: src/accounting/templates/accounting/report/income-expenses.html:113 #: src/accounting/templates/accounting/report/income-expenses.html:113
@ -858,7 +860,7 @@ msgstr "%(base)s下的科目"
#: src/accounting/templates/accounting/account/include/form.html:75 #: src/accounting/templates/accounting/account/include/form.html:75
#: src/accounting/templates/accounting/account/order.html:62 #: src/accounting/templates/accounting/account/order.html:62
#: src/accounting/templates/accounting/currency/include/form.html:57 #: src/accounting/templates/accounting/currency/include/form.html:57
#: src/accounting/templates/accounting/journal-entry/include/description-editor-modal.html:194 #: src/accounting/templates/accounting/journal-entry/include/description-editor-modal.html:195
#: src/accounting/templates/accounting/journal-entry/include/form.html:80 #: src/accounting/templates/accounting/journal-entry/include/form.html:80
#: src/accounting/templates/accounting/journal-entry/include/journal-entry-line-item-editor-modal.html:71 #: src/accounting/templates/accounting/journal-entry/include/journal-entry-line-item-editor-modal.html:71
#: src/accounting/templates/accounting/journal-entry/order.html:61 #: src/accounting/templates/accounting/journal-entry/order.html:61
@ -950,6 +952,7 @@ msgstr "貨幣"
#: src/accounting/templates/accounting/include/nav.html:58 #: src/accounting/templates/accounting/include/nav.html:58
#: src/accounting/templates/accounting/option/detail.html:24 #: src/accounting/templates/accounting/option/detail.html:24
#: src/accounting/templates/accounting/option/detail.html:41
#: src/accounting/templates/accounting/option/form.html:29 #: src/accounting/templates/accounting/option/form.html:29
msgid "Settings" msgid "Settings"
msgstr "設定" msgstr "設定"
@ -1112,7 +1115,7 @@ msgstr "新增現金收入傳票"
msgid "Add a New Transfer Journal Entry" msgid "Add a New Transfer Journal Entry"
msgstr "新增轉帳傳票" msgstr "新增轉帳傳票"
#: src/accounting/templates/accounting/option/detail.html:43 #: src/accounting/templates/accounting/option/detail.html:44
#: src/accounting/templates/accounting/option/form.html:51 #: src/accounting/templates/accounting/option/form.html:51
msgid "Default Currency" msgid "Default Currency"
msgstr "預設貨幣" msgstr "預設貨幣"
@ -1122,13 +1125,13 @@ msgstr "預設貨幣"
msgid "Default Account for the Income and Expenses Log" msgid "Default Account for the Income and Expenses Log"
msgstr "收支帳預設科目" msgstr "收支帳預設科目"
#: src/accounting/templates/accounting/option/detail.html:52 #: src/accounting/templates/accounting/option/detail.html:54
#: src/accounting/templates/accounting/option/form.html:66 #: src/accounting/templates/accounting/option/form.html:66
#: src/accounting/templates/accounting/option/form.html:92 #: src/accounting/templates/accounting/option/form.html:92
msgid "Recurring Expense" msgid "Recurring Expense"
msgstr "常用支出" msgstr "常用支出"
#: src/accounting/templates/accounting/option/detail.html:58 #: src/accounting/templates/accounting/option/detail.html:70
#: src/accounting/templates/accounting/option/form.html:72 #: src/accounting/templates/accounting/option/form.html:72
#: src/accounting/templates/accounting/option/form.html:96 #: src/accounting/templates/accounting/option/form.html:96
msgid "Recurring Income" msgid "Recurring Income"

View File

@ -51,7 +51,7 @@ First written: 2023/1/27
<div id="collapsible-navbar" class="collapse navbar-collapse"> <div id="collapsible-navbar" class="collapse navbar-collapse">
<ul class="navbar-nav me-auto mb-2 mb-lg-0"> <ul class="navbar-nav me-auto mb-2 mb-lg-0">
{% include "/accounting/include/nav.html" %} {% include "accounting/include/nav.html" %}
</ul> </ul>
<!-- The right side --> <!-- The right side -->

View File

@ -1,16 +1,16 @@
# Chinese (Traditional) translations for the Mia! Accounting # Chinese (Traditional) translations for the Mia! Accounting
# Demonstration website. # Test website.
# Copyright (C) 2023 imacat # Copyright (C) 2023 imacat
# This file is distributed under the same license as the Mia! Accounting # This file is distributed under the same license as the Mia! Accounting
# Flask Demonstration project. # project.
# imacat <imacat@mail.imacat.idv.tw>, 2023. # imacat <imacat@mail.imacat.idv.tw>, 2023.
# #
msgid "" msgid ""
msgstr "" msgstr ""
"Project-Id-Version: Mia! Accounting Demonstration 0.0.0\n" "Project-Id-Version: mia-accounting-test-site 1.0.0\n"
"Report-Msgid-Bugs-To: imacat@mail.imacat.idv.tw\n" "Report-Msgid-Bugs-To: imacat@mail.imacat.idv.tw\n"
"POT-Creation-Date: 2023-03-24 08:32+0800\n" "POT-Creation-Date: 2023-04-06 02:34+0800\n"
"PO-Revision-Date: 2023-03-24 08:33+0800\n" "PO-Revision-Date: 2023-04-06 02:34+0800\n"
"Last-Translator: imacat <imacat@mail.imacat.idv.tw>\n" "Last-Translator: imacat <imacat@mail.imacat.idv.tw>\n"
"Language: zh_Hant\n" "Language: zh_Hant\n"
"Language-Team: zh_Hant <imacat@mail.imacat.idv.tw>\n" "Language-Team: zh_Hant <imacat@mail.imacat.idv.tw>\n"