Compare commits
21 Commits
4c2dcc5070
...
v1.0.0
Author | SHA1 | Date | |
---|---|---|---|
cb1d254cf0 | |||
eb9ad57e72 | |||
ec26f8ef4d | |||
7ed29115ed | |||
95955197ac | |||
d5a0f79e4b | |||
6aa655aa64 | |||
6e532af26e | |||
fa1818d124 | |||
f21ecc2aa9 | |||
5ae1ab95ae | |||
7a5b3b78fc | |||
7df4051452 | |||
85084c68fd | |||
0185c16654 | |||
7dd007f3cf | |||
38b8a028d5 | |||
213981a8b2 | |||
a4d1789b58 | |||
91620d7db2 | |||
02fcabb0ce |
40
.readthedocs.yaml
Normal file
40
.readthedocs.yaml
Normal 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: .
|
21
MANIFEST.in
21
MANIFEST.in
@ -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/*
|
|
||||||
|
234
README.rst
234
README.rst
@ -6,19 +6,212 @@ 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.
|
||||||
|
|
||||||
|
You may try the `Mia! Accounting live demonstration`_.
|
||||||
|
|
||||||
|
|
||||||
Install
|
History
|
||||||
=======
|
=======
|
||||||
|
|
||||||
Install the latest source from the
|
I created my own private accounting application in Perl_/mod_perl_ in
|
||||||
`Mia! Accounting repository`_.
|
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 wanted to add something new
|
||||||
|
to 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! Account Django application`_.
|
||||||
|
It took me another 1.5 months to make it an independent module, which
|
||||||
|
I later released as an open source project.
|
||||||
|
|
||||||
|
The application worked nicely for my household bookkeeping for two
|
||||||
|
years. However, new demands arose over time, especially with tracking
|
||||||
|
payables and receivables, which became difficult with credit card
|
||||||
|
payments. This was critical `during the pandemic`_ as more payments
|
||||||
|
were made online with credit cards.
|
||||||
|
|
||||||
|
The biggest issue I encountered was with Django's MVT framework. Due
|
||||||
|
to my lack of experience with Django during development, I ended up
|
||||||
|
with mixed function-based view controllers and class-based views. It
|
||||||
|
became very difficult to track whether problems originated from my
|
||||||
|
overridden methods or not-overridden methods, or from the Django base
|
||||||
|
views themselves. I did not fully understand how everything worked.
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
|
||||||
|
Installation
|
||||||
|
============
|
||||||
|
|
||||||
|
Install *Mia! Accounting* with ``pip``:
|
||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
pip install git+https://github.com/imacat/mia-accounting.git
|
pip install mia-accounting
|
||||||
|
|
||||||
|
You may also download the from the `PyPI project page`_ or the
|
||||||
|
`release page`_ on the `Git repository`_.
|
||||||
|
|
||||||
|
|
||||||
|
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 UserUtilities(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``
|
||||||
|
|
||||||
|
You need to 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.
|
||||||
|
|
||||||
|
|
||||||
|
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, you may start with the
|
||||||
|
test site.
|
||||||
|
|
||||||
|
|
||||||
|
Documentation
|
||||||
|
=============
|
||||||
|
|
||||||
|
Refer to the `documentation on Read the Docs`_.
|
||||||
|
|
||||||
|
|
||||||
Copyright
|
Copyright
|
||||||
@ -46,5 +239,32 @@ 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
|
||||||
|
.. _Mia! Accounting live demonstration: https://accounting.imacat.idv.tw/
|
||||||
|
.. _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! Account Django application: https://github.com/imacat/mia-accounting-django
|
||||||
|
.. _during the pandemic: https://en.wikipedia.org/wiki/COVID-19_pandemic
|
||||||
|
.. _FastAPI: https://fastapi.tiangolo.com
|
||||||
|
.. _FontAwesome: https://fontawesome.com
|
||||||
|
.. _Decimal.js: https://mikemcl.github.io/decimal.js
|
||||||
|
.. _Tempus-Dominus: https://getdatepicker.com
|
||||||
|
.. _CDN: https://en.wikipedia.org/wiki/Content_delivery_network
|
||||||
|
.. _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
|
||||||
|
.. _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/
|
||||||
|
.. _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
|
||||||
|
.. _documentation on Read the Docs: https://mia-accounting.readthedocs.io
|
||||||
|
@ -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.10.0'
|
release = '1.0.0'
|
||||||
|
|
||||||
# -- 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
61
docs/source/examples.rst
Normal 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 UserUtilities(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
|
@ -10,6 +10,10 @@ Welcome to Mia! Accounting's documentation!
|
|||||||
:maxdepth: 2
|
:maxdepth: 2
|
||||||
:caption: Contents:
|
:caption: Contents:
|
||||||
|
|
||||||
|
intro
|
||||||
|
accounting
|
||||||
|
examples
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Indices and tables
|
Indices and tables
|
||||||
|
188
docs/source/intro.rst
Normal file
188
docs/source/intro.rst
Normal file
@ -0,0 +1,188 @@
|
|||||||
|
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.
|
||||||
|
|
||||||
|
You may try the `Mia! Accounting live demonstration`_.
|
||||||
|
|
||||||
|
|
||||||
|
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 wanted to add something new
|
||||||
|
to 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! Account Django application`_.
|
||||||
|
It took me another 1.5 months to make it an independent module, which
|
||||||
|
I later released as an open source project.
|
||||||
|
|
||||||
|
The application worked nicely for my household bookkeeping for two
|
||||||
|
years. However, new demands arose over time, especially with tracking
|
||||||
|
payables and receivables, which became difficult with credit card
|
||||||
|
payments. This was critical `during the pandemic`_ as more payments
|
||||||
|
were made online with credit cards.
|
||||||
|
|
||||||
|
The biggest issue I encountered was with Django's MVT framework. Due
|
||||||
|
to my lack of experience with Django during development, I ended up
|
||||||
|
with mixed function-based view controllers and class-based views. It
|
||||||
|
became very difficult to track whether problems originated from my
|
||||||
|
overridden methods or not-overridden methods, or from the Django base
|
||||||
|
views themselves. I did not fully understand how everything worked.
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
|
||||||
|
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`_.
|
||||||
|
|
||||||
|
|
||||||
|
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
|
||||||
|
:py:meth:`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``
|
||||||
|
|
||||||
|
You need to 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.
|
||||||
|
|
||||||
|
|
||||||
|
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, you may start with the
|
||||||
|
test site.
|
||||||
|
|
||||||
|
|
||||||
|
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
|
||||||
|
.. _Mia! Accounting live demonstration: https://accounting.imacat.idv.tw/
|
||||||
|
.. _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! Account Django application: https://github.com/imacat/mia-accounting-django
|
||||||
|
.. _during the pandemic: https://en.wikipedia.org/wiki/COVID-19_pandemic
|
||||||
|
.. _FastAPI: https://fastapi.tiangolo.com
|
||||||
|
.. _FontAwesome: https://fontawesome.com
|
||||||
|
.. _Decimal.js: https://mikemcl.github.io/decimal.js
|
||||||
|
.. _Tempus-Dominus: https://getdatepicker.com
|
||||||
|
.. _CDN: https://en.wikipedia.org/wiki/Content_delivery_network
|
||||||
|
.. _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
|
||||||
|
.. _Bootstrap navigation bar: https://getbootstrap.com/docs/5.3/components/navbar/
|
||||||
|
.. _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
|
||||||
|
.. _documentation on Read the Docs: https://mia-accounting.readthedocs.io
|
@ -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.0"
|
||||||
|
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",
|
||||||
|
]
|
||||||
|
56
setup.cfg
56
setup.cfg
@ -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.10.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/**
|
|
@ -47,7 +47,6 @@ def init_app(app: Flask, user_utils: UserUtilityInterface,
|
|||||||
init_user_utils(user_utils)
|
init_user_utils(user_utils)
|
||||||
|
|
||||||
bp: Blueprint = Blueprint("accounting", __name__,
|
bp: Blueprint = Blueprint("accounting", __name__,
|
||||||
url_prefix=url_prefix,
|
|
||||||
template_folder="templates",
|
template_folder="templates",
|
||||||
static_folder="static")
|
static_folder="static")
|
||||||
|
|
||||||
@ -84,9 +83,9 @@ def init_app(app: Flask, user_utils: UserUtilityInterface,
|
|||||||
journal_entry.init_app(app, bp)
|
journal_entry.init_app(app, bp)
|
||||||
|
|
||||||
from . import report
|
from . import report
|
||||||
report.init_app(app, bp)
|
report.init_app(app, url_prefix)
|
||||||
|
|
||||||
from . import option
|
from . import option
|
||||||
option.init_app(bp)
|
option.init_app(bp)
|
||||||
|
|
||||||
app.register_blueprint(bp)
|
app.register_blueprint(bp, url_prefix=url_prefix)
|
||||||
|
@ -77,6 +77,7 @@ def get_selectable_original_line_items(
|
|||||||
.options(selectinload(JournalEntryLineItem.currency),
|
.options(selectinload(JournalEntryLineItem.currency),
|
||||||
selectinload(JournalEntryLineItem.account),
|
selectinload(JournalEntryLineItem.account),
|
||||||
selectinload(JournalEntryLineItem.journal_entry)).all()
|
selectinload(JournalEntryLineItem.journal_entry)).all()
|
||||||
|
line_items.reverse()
|
||||||
for line_item in line_items:
|
for line_item in line_items:
|
||||||
line_item.net_balance = line_item.amount \
|
line_item.net_balance = line_item.amount \
|
||||||
if net_balances[line_item.id] is None \
|
if net_balances[line_item.id] is None \
|
||||||
|
@ -235,4 +235,4 @@ def __get_default_page_uri() -> str:
|
|||||||
|
|
||||||
:return: The URI for the default page.
|
:return: The URI for the default page.
|
||||||
"""
|
"""
|
||||||
return url_for("accounting.report.default")
|
return url_for("accounting-report.default")
|
||||||
|
@ -17,14 +17,14 @@
|
|||||||
"""The report management.
|
"""The report management.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
from flask import Flask, Blueprint
|
from flask import Flask
|
||||||
|
|
||||||
|
|
||||||
def init_app(app: Flask, bp: Blueprint) -> None:
|
def init_app(app: Flask, url_prefix: str) -> None:
|
||||||
"""Initialize the application.
|
"""Initialize the application.
|
||||||
|
|
||||||
:param app: The Flask application.
|
:param app: The Flask application.
|
||||||
:param bp: The blueprint of the accounting application.
|
:param url_prefix: The URL prefix of the accounting application.
|
||||||
:return: None.
|
:return: None.
|
||||||
"""
|
"""
|
||||||
from .converters import PeriodConverter, IncomeExpensesAccountConverter
|
from .converters import PeriodConverter, IncomeExpensesAccountConverter
|
||||||
@ -32,4 +32,4 @@ def init_app(app: Flask, bp: Blueprint) -> None:
|
|||||||
app.url_map.converters["ieAccount"] = IncomeExpensesAccountConverter
|
app.url_map.converters["ieAccount"] = IncomeExpensesAccountConverter
|
||||||
|
|
||||||
from .views import bp as report_bp
|
from .views import bp as report_bp
|
||||||
bp.register_blueprint(report_bp, url_prefix="/reports")
|
app.register_blueprint(report_bp, url_prefix=url_prefix)
|
||||||
|
@ -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()
|
||||||
|
@ -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] \
|
||||||
|
@ -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] \
|
||||||
|
@ -68,9 +68,9 @@ class ReportChooser:
|
|||||||
"""The title of the current report."""
|
"""The title of the current report."""
|
||||||
self.is_search: bool = active_report == ReportType.SEARCH
|
self.is_search: bool = active_report == ReportType.SEARCH
|
||||||
"""Whether the current report is the search page."""
|
"""Whether the current report is the search page."""
|
||||||
self.__reports.append(self.__journal)
|
|
||||||
self.__reports.append(self.__ledger)
|
|
||||||
self.__reports.append(self.__income_expenses)
|
self.__reports.append(self.__income_expenses)
|
||||||
|
self.__reports.append(self.__ledger)
|
||||||
|
self.__reports.append(self.__journal)
|
||||||
self.__reports.append(self.__trial_balance)
|
self.__reports.append(self.__trial_balance)
|
||||||
self.__reports.append(self.__income_statement)
|
self.__reports.append(self.__income_statement)
|
||||||
self.__reports.append(self.__balance_sheet)
|
self.__reports.append(self.__balance_sheet)
|
||||||
@ -80,28 +80,6 @@ class ReportChooser:
|
|||||||
if self.is_search:
|
if self.is_search:
|
||||||
self.current_report = gettext("Search")
|
self.current_report = gettext("Search")
|
||||||
|
|
||||||
@property
|
|
||||||
def __journal(self) -> OptionLink:
|
|
||||||
"""Returns the journal.
|
|
||||||
|
|
||||||
:return: The journal.
|
|
||||||
"""
|
|
||||||
return OptionLink(gettext("Journal"), journal_url(self.__period),
|
|
||||||
self.__active_report == ReportType.JOURNAL,
|
|
||||||
fa_icon="fa-solid fa-book")
|
|
||||||
|
|
||||||
@property
|
|
||||||
def __ledger(self) -> OptionLink:
|
|
||||||
"""Returns the ledger.
|
|
||||||
|
|
||||||
:return: The ledger.
|
|
||||||
"""
|
|
||||||
return OptionLink(gettext("Ledger"),
|
|
||||||
ledger_url(self.__currency, self.__account,
|
|
||||||
self.__period),
|
|
||||||
self.__active_report == ReportType.LEDGER,
|
|
||||||
fa_icon="fa-solid fa-clipboard")
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def __income_expenses(self) -> OptionLink:
|
def __income_expenses(self) -> OptionLink:
|
||||||
"""Returns the income and expenses log.
|
"""Returns the income and expenses log.
|
||||||
@ -118,6 +96,28 @@ class ReportChooser:
|
|||||||
self.__active_report == ReportType.INCOME_EXPENSES,
|
self.__active_report == ReportType.INCOME_EXPENSES,
|
||||||
fa_icon="fa-solid fa-money-bill-wave")
|
fa_icon="fa-solid fa-money-bill-wave")
|
||||||
|
|
||||||
|
@property
|
||||||
|
def __ledger(self) -> OptionLink:
|
||||||
|
"""Returns the ledger.
|
||||||
|
|
||||||
|
:return: The ledger.
|
||||||
|
"""
|
||||||
|
return OptionLink(gettext("Ledger"),
|
||||||
|
ledger_url(self.__currency, self.__account,
|
||||||
|
self.__period),
|
||||||
|
self.__active_report == ReportType.LEDGER,
|
||||||
|
fa_icon="fa-solid fa-clipboard")
|
||||||
|
|
||||||
|
@property
|
||||||
|
def __journal(self) -> OptionLink:
|
||||||
|
"""Returns the journal.
|
||||||
|
|
||||||
|
:return: The journal.
|
||||||
|
"""
|
||||||
|
return OptionLink(gettext("Journal"), journal_url(self.__period),
|
||||||
|
self.__active_report == ReportType.JOURNAL,
|
||||||
|
fa_icon="fa-solid fa-book")
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def __trial_balance(self) -> OptionLink:
|
def __trial_balance(self) -> OptionLink:
|
||||||
"""Returns the trial balance.
|
"""Returns the trial balance.
|
||||||
|
@ -34,8 +34,8 @@ def journal_url(period: Period) \
|
|||||||
:return: The URL of the journal.
|
:return: The URL of the journal.
|
||||||
"""
|
"""
|
||||||
if period.is_default:
|
if period.is_default:
|
||||||
return url_for("accounting.report.journal-default")
|
return url_for("accounting-report.journal-default")
|
||||||
return url_for("accounting.report.journal", period=period)
|
return url_for("accounting-report.journal", period=period)
|
||||||
|
|
||||||
|
|
||||||
def ledger_url(currency: Currency, account: Account, period: Period) \
|
def ledger_url(currency: Currency, account: Account, period: Period) \
|
||||||
@ -48,9 +48,9 @@ def ledger_url(currency: Currency, account: Account, period: Period) \
|
|||||||
:return: The URL of the ledger.
|
:return: The URL of the ledger.
|
||||||
"""
|
"""
|
||||||
if period.is_default:
|
if period.is_default:
|
||||||
return url_for("accounting.report.ledger-default",
|
return url_for("accounting-report.ledger-default",
|
||||||
currency=currency, account=account)
|
currency=currency, account=account)
|
||||||
return url_for("accounting.report.ledger",
|
return url_for("accounting-report.ledger",
|
||||||
currency=currency, account=account,
|
currency=currency, account=account,
|
||||||
period=period)
|
period=period)
|
||||||
|
|
||||||
@ -67,11 +67,11 @@ def income_expenses_url(currency: Currency, account: CurrentAccount,
|
|||||||
if currency.code == default_currency_code() \
|
if currency.code == default_currency_code() \
|
||||||
and account.code == options.default_ie_account_code \
|
and account.code == options.default_ie_account_code \
|
||||||
and period.is_default:
|
and period.is_default:
|
||||||
return url_for("accounting.report.default")
|
return url_for("accounting-report.default")
|
||||||
if period.is_default:
|
if period.is_default:
|
||||||
return url_for("accounting.report.income-expenses-default",
|
return url_for("accounting-report.income-expenses-default",
|
||||||
currency=currency, account=account)
|
currency=currency, account=account)
|
||||||
return url_for("accounting.report.income-expenses",
|
return url_for("accounting-report.income-expenses",
|
||||||
currency=currency, account=account,
|
currency=currency, account=account,
|
||||||
period=period)
|
period=period)
|
||||||
|
|
||||||
@ -84,9 +84,9 @@ def trial_balance_url(currency: Currency, period: Period) -> str:
|
|||||||
:return: The URL of the trial balance.
|
:return: The URL of the trial balance.
|
||||||
"""
|
"""
|
||||||
if period.is_default:
|
if period.is_default:
|
||||||
return url_for("accounting.report.trial-balance-default",
|
return url_for("accounting-report.trial-balance-default",
|
||||||
currency=currency)
|
currency=currency)
|
||||||
return url_for("accounting.report.trial-balance",
|
return url_for("accounting-report.trial-balance",
|
||||||
currency=currency, period=period)
|
currency=currency, period=period)
|
||||||
|
|
||||||
|
|
||||||
@ -98,9 +98,9 @@ def income_statement_url(currency: Currency, period: Period) -> str:
|
|||||||
:return: The URL of the income statement.
|
:return: The URL of the income statement.
|
||||||
"""
|
"""
|
||||||
if period.is_default:
|
if period.is_default:
|
||||||
return url_for("accounting.report.income-statement-default",
|
return url_for("accounting-report.income-statement-default",
|
||||||
currency=currency)
|
currency=currency)
|
||||||
return url_for("accounting.report.income-statement",
|
return url_for("accounting-report.income-statement",
|
||||||
currency=currency, period=period)
|
currency=currency, period=period)
|
||||||
|
|
||||||
|
|
||||||
@ -112,7 +112,7 @@ def balance_sheet_url(currency: Currency, period: Period) -> str:
|
|||||||
:return: The URL of the balance sheet.
|
:return: The URL of the balance sheet.
|
||||||
"""
|
"""
|
||||||
if period.is_default:
|
if period.is_default:
|
||||||
return url_for("accounting.report.balance-sheet-default",
|
return url_for("accounting-report.balance-sheet-default",
|
||||||
currency=currency)
|
currency=currency)
|
||||||
return url_for("accounting.report.balance-sheet",
|
return url_for("accounting-report.balance-sheet",
|
||||||
currency=currency, period=period)
|
currency=currency, period=period)
|
||||||
|
@ -30,7 +30,7 @@ from .reports import Journal, Ledger, IncomeExpenses, TrialBalance, \
|
|||||||
IncomeStatement, BalanceSheet, Search
|
IncomeStatement, BalanceSheet, Search
|
||||||
from .template_filters import format_amount
|
from .template_filters import format_amount
|
||||||
|
|
||||||
bp: Blueprint = Blueprint("report", __name__)
|
bp: Blueprint = Blueprint("accounting-report", __name__)
|
||||||
"""The view blueprint for the reports."""
|
"""The view blueprint for the reports."""
|
||||||
bp.add_app_template_filter(format_amount, "accounting_report_format_amount")
|
bp.add_app_template_filter(format_amount, "accounting_report_format_amount")
|
||||||
|
|
||||||
|
@ -316,6 +316,10 @@ a.accounting-report-table-row {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* The description editor */
|
/* The description editor */
|
||||||
|
.accounting-description-editor-buttons {
|
||||||
|
max-height: 7rem;
|
||||||
|
overflow-y: scroll;
|
||||||
|
}
|
||||||
.accounting-description-editor-buttons .btn {
|
.accounting-description-editor-buttons .btn {
|
||||||
margin-bottom: 0.3rem;
|
margin-bottom: 0.3rem;
|
||||||
}
|
}
|
||||||
|
@ -28,7 +28,7 @@ First written: 2023/1/26
|
|||||||
</span>
|
</span>
|
||||||
<ul class="dropdown-menu">
|
<ul class="dropdown-menu">
|
||||||
<li>
|
<li>
|
||||||
<a class="dropdown-item {% if request.endpoint and request.endpoint.startswith("accounting.report.") %} active {% endif %}" href="{{ url_for("accounting.report.default") }}">
|
<a class="dropdown-item {% if request.endpoint and request.endpoint.startswith("accounting-report.") %} active {% endif %}" href="{{ url_for("accounting-report.default") }}">
|
||||||
<i class="fa-solid fa-book"></i>
|
<i class="fa-solid fa-book"></i>
|
||||||
{{ A_("Reports") }}
|
{{ A_("Reports") }}
|
||||||
</a>
|
</a>
|
||||||
|
@ -23,6 +23,6 @@ First written: 2023/2/25
|
|||||||
|
|
||||||
{% block header %}{% block title %}{{ A_("Add a New Cash Disbursement Journal Entry") }}{% endblock %}{% endblock %}
|
{% block header %}{% block title %}{{ A_("Add a New Cash Disbursement Journal Entry") }}{% endblock %}{% endblock %}
|
||||||
|
|
||||||
{% block back_url %}{{ request.args.get("next") or url_for("accounting.report.default") }}{% endblock %}
|
{% block back_url %}{{ request.args.get("next") or url_for("accounting-report.default") }}{% endblock %}
|
||||||
|
|
||||||
{% block action_url %}{{ url_for("accounting.journal-entry.store", journal_entry_type=journal_entry_type) }}{% endblock %}
|
{% block action_url %}{{ url_for("accounting.journal-entry.store", journal_entry_type=journal_entry_type) }}{% endblock %}
|
||||||
|
@ -181,10 +181,10 @@ First written: 2023/2/28
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{# The suggested accounts #}
|
{# The suggested accounts #}
|
||||||
<div class="mt-3">
|
<div class="mt-3 accounting-description-editor-buttons">
|
||||||
<button id="accounting-description-editor-{{ description_editor.debit_credit }}-account-confirmed" class="btn btn-primary mb-1 d-none" type="button"></button>
|
<button id="accounting-description-editor-{{ description_editor.debit_credit }}-account-confirmed" class="btn btn-primary mb-1 d-none" type="button"></button>
|
||||||
{% for account in description_editor.accounts %}
|
{% for account in description_editor.accounts %}
|
||||||
<button class="btn btn-outline-primary mb-1 d-none accounting-description-editor-{{ description_editor.debit_credit }}-account {% if account.is_need_offset %} accounting-account-is-need-offset {% endif %}" type="button" data-code="{{ account.code }}" data-text="{{ account }}">
|
<button class="btn btn-outline-primary d-none accounting-description-editor-{{ description_editor.debit_credit }}-account {% if account.is_need_offset %} accounting-account-is-need-offset {% endif %}" type="button" data-code="{{ account.code }}" data-text="{{ account }}">
|
||||||
{{ account }}
|
{{ account }}
|
||||||
</button>
|
</button>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
@ -26,7 +26,7 @@ First written: 2023/2/26
|
|||||||
{% block content %}
|
{% block content %}
|
||||||
|
|
||||||
<div class="mb-3 accounting-toolbar">
|
<div class="mb-3 accounting-toolbar">
|
||||||
<a class="btn btn-primary" role="button" href="{{ url_for("accounting.report.default")|accounting_or_next }}">
|
<a class="btn btn-primary" role="button" href="{{ url_for("accounting-report.default")|accounting_or_next }}">
|
||||||
<i class="fa-solid fa-circle-chevron-left"></i>
|
<i class="fa-solid fa-circle-chevron-left"></i>
|
||||||
<span class="d-none d-md-inline">{{ A_("Back") }}</span>
|
<span class="d-none d-md-inline">{{ A_("Back") }}</span>
|
||||||
</a>
|
</a>
|
||||||
|
@ -31,7 +31,7 @@ First written: 2023/2/26
|
|||||||
{% block content %}
|
{% block content %}
|
||||||
|
|
||||||
<div class="mb-3 accounting-toolbar">
|
<div class="mb-3 accounting-toolbar">
|
||||||
<a class="btn btn-primary" role="button" href="{{ url_for("accounting.report.default")|accounting_or_next }}">
|
<a class="btn btn-primary" role="button" href="{{ url_for("accounting-report.default")|accounting_or_next }}">
|
||||||
<i class="fa-solid fa-circle-chevron-left"></i>
|
<i class="fa-solid fa-circle-chevron-left"></i>
|
||||||
<span class="d-none d-md-inline">{{ A_("Back") }}</span>
|
<span class="d-none d-md-inline">{{ A_("Back") }}</span>
|
||||||
</a>
|
</a>
|
||||||
|
@ -23,6 +23,6 @@ First written: 2023/2/25
|
|||||||
|
|
||||||
{% block header %}{% block title %}{{ A_("Add a New Cash Receipt Journal Entry") }}{% endblock %}{% endblock %}
|
{% block header %}{% block title %}{{ A_("Add a New Cash Receipt Journal Entry") }}{% endblock %}{% endblock %}
|
||||||
|
|
||||||
{% block back_url %}{{ request.args.get("next") or url_for("accounting.report.default") }}{% endblock %}
|
{% block back_url %}{{ request.args.get("next") or url_for("accounting-report.default") }}{% endblock %}
|
||||||
|
|
||||||
{% block action_url %}{{ url_for("accounting.journal-entry.store", journal_entry_type=journal_entry_type) }}{% endblock %}
|
{% block action_url %}{{ url_for("accounting.journal-entry.store", journal_entry_type=journal_entry_type) }}{% endblock %}
|
||||||
|
@ -23,6 +23,6 @@ First written: 2023/2/25
|
|||||||
|
|
||||||
{% block header %}{% block title %}{{ A_("Add a New Transfer Journal Entry") }}{% endblock %}{% endblock %}
|
{% block header %}{% block title %}{{ A_("Add a New Transfer Journal Entry") }}{% endblock %}{% endblock %}
|
||||||
|
|
||||||
{% block back_url %}{{ request.args.get("next") or url_for("accounting.report.default") }}{% endblock %}
|
{% block back_url %}{{ request.args.get("next") or url_for("accounting-report.default") }}{% endblock %}
|
||||||
|
|
||||||
{% block action_url %}{{ url_for("accounting.journal-entry.store", journal_entry_type=journal_entry_type) }}{% endblock %}
|
{% block action_url %}{{ url_for("accounting.journal-entry.store", journal_entry_type=journal_entry_type) }}{% endblock %}
|
||||||
|
@ -19,7 +19,7 @@ search-modal.html: The search modal
|
|||||||
Author: imacat@mail.imacat.idv.tw (imacat)
|
Author: imacat@mail.imacat.idv.tw (imacat)
|
||||||
First written: 2023/3/8
|
First written: 2023/3/8
|
||||||
#}
|
#}
|
||||||
<form action="{{ url_for("accounting.report.search") }}" method="get" role="search" aria-labelledby="accounting-search-modal-label">
|
<form action="{{ url_for("accounting-report.search") }}" method="get" role="search" aria-labelledby="accounting-search-modal-label">
|
||||||
<div class="modal fade" id="accounting-search-modal" tabindex="-1" aria-labelledby="accounting-search-modal-label" aria-hidden="true">
|
<div class="modal fade" id="accounting-search-modal" tabindex="-1" aria-labelledby="accounting-search-modal-label" aria-hidden="true">
|
||||||
<div class="modal-dialog">
|
<div class="modal-dialog">
|
||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
|
@ -118,7 +118,7 @@ First written: 2023/3/8
|
|||||||
</button>
|
</button>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if use_search %}
|
{% if use_search %}
|
||||||
<form class="btn btn-primary d-flex input-group" action="{{ url_for("accounting.report.search") }}" method="get" role="search" aria-labelledby="accounting-toolbar-search-label">
|
<form class="btn btn-primary d-flex input-group" action="{{ url_for("accounting-report.search") }}" method="get" role="search" aria-labelledby="accounting-toolbar-search-label">
|
||||||
<input id="accounting-toolbar-search" class="form-control form-control-sm" type="search" name="q" value="{{ request.args.q }}" placeholder=" " required="required">
|
<input id="accounting-toolbar-search" class="form-control form-control-sm" type="search" name="q" value="{{ request.args.q }}" placeholder=" " required="required">
|
||||||
<label id="accounting-toolbar-search-label" for="accounting-toolbar-search" class="input-group-text">
|
<label id="accounting-toolbar-search-label" for="accounting-toolbar-search" class="input-group-text">
|
||||||
<button type="submit">
|
<button type="submit">
|
||||||
|
@ -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"
|
||||||
|
@ -35,7 +35,7 @@ from testlib_journal_entry import NON_EMPTY_NOTE, EMPTY_NOTE, \
|
|||||||
|
|
||||||
PREFIX: str = "/accounting/journal-entries"
|
PREFIX: str = "/accounting/journal-entries"
|
||||||
"""The URL prefix for the journal entry management."""
|
"""The URL prefix for the journal entry management."""
|
||||||
RETURN_TO_URI: str = "/accounting/reports"
|
RETURN_TO_URI: str = "/accounting"
|
||||||
"""The URL to return to after the operation."""
|
"""The URL to return to after the operation."""
|
||||||
|
|
||||||
|
|
||||||
|
@ -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 -->
|
||||||
|
@ -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"
|
||||||
|
Reference in New Issue
Block a user