Compare commits
15 Commits
014d67f7b8
...
v1.5.0
Author | SHA1 | Date | |
---|---|---|---|
86637267d3 | |||
71e97721aa | |||
5815608288 | |||
5f75d93c6a | |||
118c4b458e | |||
3f7e4c0dda | |||
eed4c923f6 | |||
09dd5ae541 | |||
172a12b134 | |||
f3c558f48a | |||
988757d30e | |||
50cea90d1b | |||
71dfb6f003 | |||
be628b4aa1 | |||
5d444adec4 |
129
README.rst
129
README.rst
@ -44,126 +44,18 @@ You may also download 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. You also
|
||||
need at least one user.
|
||||
|
||||
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, run the ``accounting-init-db`` console
|
||||
command to initialize the accounting database. You need to specify
|
||||
the username of a user as the data creator.
|
||||
|
||||
::
|
||||
|
||||
% flask --app myapp accounting-init-db -u username
|
||||
|
||||
|
||||
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`_.
|
||||
|
||||
|
||||
Change Log
|
||||
==========
|
||||
|
||||
Refer to the `change log`_.
|
||||
|
||||
|
||||
Copyright
|
||||
=========
|
||||
|
||||
@ -198,12 +90,5 @@ Authors
|
||||
.. _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
|
||||
.. _Bootstrap navigation bar: https://getbootstrap.com/docs/5.3/components/navbar/
|
||||
.. _documentation on Read the Docs: https://mia-accounting.readthedocs.io
|
||||
.. _change log: https://mia-accounting.readthedocs.io/en/latest/changelog.html
|
||||
|
@ -76,6 +76,22 @@ accounting.report.reports.unapplied\_accounts module
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
accounting.report.reports.unmatched module
|
||||
------------------------------------------
|
||||
|
||||
.. automodule:: accounting.report.reports.unmatched
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
accounting.report.reports.unmatched\_accounts module
|
||||
----------------------------------------------------
|
||||
|
||||
.. automodule:: accounting.report.reports.unmatched_accounts
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
Module contents
|
||||
---------------
|
||||
|
||||
|
@ -28,6 +28,14 @@ accounting.report.utils.csv\_export module
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
accounting.report.utils.offset\_matcher module
|
||||
----------------------------------------------
|
||||
|
||||
.. automodule:: accounting.report.utils.offset_matcher
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
accounting.report.utils.option\_link module
|
||||
-------------------------------------------
|
||||
|
||||
@ -60,6 +68,14 @@ accounting.report.utils.unapplied module
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
accounting.report.utils.unmatched module
|
||||
----------------------------------------
|
||||
|
||||
.. automodule:: accounting.report.utils.unmatched
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
accounting.report.utils.urls module
|
||||
-----------------------------------
|
||||
|
||||
|
@ -13,7 +13,6 @@ Subpackages
|
||||
accounting.journal_entry
|
||||
accounting.option
|
||||
accounting.report
|
||||
accounting.unmatched_offset
|
||||
accounting.utils
|
||||
|
||||
Submodules
|
||||
|
@ -1,29 +0,0 @@
|
||||
accounting.unmatched\_offset package
|
||||
====================================
|
||||
|
||||
Submodules
|
||||
----------
|
||||
|
||||
accounting.unmatched\_offset.queries module
|
||||
-------------------------------------------
|
||||
|
||||
.. automodule:: accounting.unmatched_offset.queries
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
accounting.unmatched\_offset.views module
|
||||
-----------------------------------------
|
||||
|
||||
.. automodule:: accounting.unmatched_offset.views
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
Module contents
|
||||
---------------
|
||||
|
||||
.. automodule:: accounting.unmatched_offset
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
@ -52,14 +52,6 @@ accounting.utils.offset\_alias module
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
accounting.utils.offset\_matcher module
|
||||
---------------------------------------
|
||||
|
||||
.. automodule:: accounting.utils.offset_matcher
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
accounting.utils.options module
|
||||
-------------------------------
|
||||
|
||||
@ -108,14 +100,6 @@ accounting.utils.strip\_text module
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
accounting.utils.unapplied module
|
||||
---------------------------------
|
||||
|
||||
.. automodule:: accounting.utils.unapplied
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
accounting.utils.user module
|
||||
----------------------------
|
||||
|
||||
|
310
docs/source/changelog.rst
Normal file
310
docs/source/changelog.rst
Normal file
@ -0,0 +1,310 @@
|
||||
Changes
|
||||
=======
|
||||
|
||||
|
||||
Version 1.5.0
|
||||
-------------
|
||||
|
||||
Released 2023/4/23
|
||||
|
||||
* Updated to require ``SQLAlchemy >= 2``.
|
||||
* Added the change log.
|
||||
* Added the ``VERSION`` constant to the ``accounting`` module for
|
||||
the package version, and revised ``pyproject.toml`` and ``conf.py``
|
||||
to read the version from it.
|
||||
|
||||
|
||||
Version 1.4.1
|
||||
-------------
|
||||
|
||||
Released 2023/4/22
|
||||
|
||||
* Updated to allow editing the description of the journal entry line
|
||||
item with offsets or are offsetting to original line items.
|
||||
* Updated not to override the existing description of a journal entry
|
||||
line item after choosing the original line item to offset to.
|
||||
|
||||
|
||||
Version 1.4.0
|
||||
-------------
|
||||
|
||||
Released 2023/4/18
|
||||
|
||||
* Rewrote the unapplied original line items and unmatched offsets.
|
||||
|
||||
* The unapplied original line items and unmatched offsets are both
|
||||
in the report submodule. They can be filtered with currency and
|
||||
period now.
|
||||
* Show the unapplied original line items and unmatched offsets
|
||||
together, and added the accumulated balance in the unmatched
|
||||
offset list, for ease of reference.
|
||||
|
||||
* Removed the account code from the journal entry detail and journal
|
||||
entry form for mobile devices.
|
||||
* Made the account options in the reports to be scrollable.
|
||||
|
||||
|
||||
Version 1.3.3
|
||||
-------------
|
||||
|
||||
Released 2023/4/13
|
||||
|
||||
Changed the sample data generation in the test site live demonstration
|
||||
from pre-recorded data to real-time generation, to avoid the problem
|
||||
with the start of months and weeks changed with the date of the
|
||||
import.
|
||||
|
||||
|
||||
Version 1.3.2
|
||||
-------------
|
||||
|
||||
Released 2023/4/12
|
||||
|
||||
Added the sample data generation and database reset on the test site
|
||||
for live demonstration.
|
||||
|
||||
|
||||
Version 1.3.1
|
||||
-------------
|
||||
|
||||
Released 2023/4/11
|
||||
|
||||
* Fixed the permission of the navigation menu of the unmatched offsets.
|
||||
* Revised the test site to be more accessible as the live demonstration.
|
||||
|
||||
|
||||
Version 1.3.0
|
||||
-------------
|
||||
|
||||
Released 2023/4/11
|
||||
|
||||
Added the ``accounting-init-db`` console command to replace all the
|
||||
other console commands to initialize the accounting database. The
|
||||
test site does not work with previous versions (<1.3.0).
|
||||
|
||||
|
||||
Version 1.2.1
|
||||
-------------
|
||||
|
||||
Released 2023/4/9
|
||||
|
||||
Fixed the search result to allow full ``year/month/day``
|
||||
specification.
|
||||
|
||||
|
||||
Version 1.2.0
|
||||
-------------
|
||||
|
||||
Released 2023/4/9
|
||||
|
||||
* Simplified the URL of the default reports.
|
||||
* Fixed the crash with malformed Chinese translation.
|
||||
* Fixed the crash when downloading CSV data with non-US-ASCII
|
||||
filenames.
|
||||
|
||||
|
||||
Version 1.1.0
|
||||
-------------
|
||||
|
||||
Released 2023/4/9
|
||||
|
||||
* Added the unapplied original line item list, to track unpaid
|
||||
payables, unreceived receivables, assets, prepaids, refundable
|
||||
deposits, etc.
|
||||
* Added the offset matcher to match unapplied original line items
|
||||
with unmatched offsets.
|
||||
|
||||
|
||||
Version 1.0.1
|
||||
-------------
|
||||
|
||||
Released 2023/4/6
|
||||
|
||||
Documentation fixes.
|
||||
|
||||
|
||||
Version 1.0.0
|
||||
-------------
|
||||
|
||||
Released 2023/4/6
|
||||
|
||||
The first formal release in Flask.
|
||||
|
||||
Added the documentation.
|
||||
|
||||
|
||||
Version 0.11.1 (Pre-release)
|
||||
----------------------------
|
||||
|
||||
Released 2023/4/5
|
||||
|
||||
Removed the zero balances from the trial balance, the income
|
||||
statement, and the balance sheet.
|
||||
|
||||
|
||||
Version 0.11.0 (Pre-release)
|
||||
----------------------------
|
||||
|
||||
Released 2023/4/5
|
||||
|
||||
* Renamed the project from ``mia-accounting-flask`` to
|
||||
``mia-accounting``.
|
||||
* Updated the URL of the reports, as the default views of the
|
||||
accounting application.
|
||||
* Updated ``README``.
|
||||
* Various fixes.
|
||||
|
||||
|
||||
Version 0.10.0 (Pre-release)
|
||||
----------------------------
|
||||
|
||||
Released 2023/4/3
|
||||
|
||||
* Added the unauthorized method to the ``UserUtilityInterface``
|
||||
interface to allow fine control to how to handle the case when the
|
||||
user has not logged in.
|
||||
* Revised the JavaScript description editor to respect the account
|
||||
that the user has confirmed or specifically selected.
|
||||
* Various fixes.
|
||||
|
||||
|
||||
Version 0.9.1 (Pre-release)
|
||||
---------------------------
|
||||
|
||||
Released 2023/3/24
|
||||
|
||||
* A distinguishable look in the option detail than the option form.
|
||||
* A better look in the new journal entry forms when there is no line
|
||||
item yet.
|
||||
* Fixed the search in the original entry selector in the journal
|
||||
entry form to always do a partial match, to fix the problem that
|
||||
there is no match when typing is not finished yet.
|
||||
* Fixed the search in the original entry selector to search the net
|
||||
balance correctly.
|
||||
* Replaced the ``editor`` and ``editor2`` accounts with the ``admin``
|
||||
and ``editor`` accounts.
|
||||
* Various fixes.
|
||||
|
||||
|
||||
Version 0.9.0 (Pre-release)
|
||||
---------------------------
|
||||
|
||||
Released 2023/3/23
|
||||
|
||||
Moved the settings from the ``.env`` file to the option table in the
|
||||
database that can be set and updated on the web interface. Added the
|
||||
settings page to show and update the settings.
|
||||
|
||||
|
||||
Version 0.8.0 (Pre-release)
|
||||
---------------------------
|
||||
|
||||
Released 2023/3/22
|
||||
|
||||
* Added the recurring transactions to the description editor.
|
||||
* Added prevention to delete database objects that are essential or
|
||||
referenced by others with foreign keys.
|
||||
* Various fixes on the visual layout.
|
||||
|
||||
|
||||
Version 0.7.0 (Pre-release)
|
||||
---------------------------
|
||||
|
||||
Released 2023/3/21
|
||||
|
||||
* Renamed "transaction" to "journal entry", and "journal entry" to
|
||||
"journal entry line item".
|
||||
* Renamed ``summary`` to ``description``.
|
||||
* Updated tempus-dominus from version 6.2.10 to 6.4.3.
|
||||
* Fixed titles and capitalization.
|
||||
* Fixed to search case-insensitively.
|
||||
* Added favicon to the test site.
|
||||
* Fixed the navigation menu when there is no matching endpoint.
|
||||
* Various fixes.
|
||||
|
||||
|
||||
Version 0.6.0 (Pre-release)
|
||||
---------------------------
|
||||
|
||||
Released 2023/3/18
|
||||
|
||||
* Added offset tracking to the journal entries in the payable and
|
||||
receivable accounts.
|
||||
* Renamed the ``is_offset_needed`` column to ``is_need_offset`` in
|
||||
the ``Account`` data model.
|
||||
|
||||
|
||||
Version 0.5.0 (Pre-release)
|
||||
---------------------------
|
||||
|
||||
Released 2023/3/10
|
||||
|
||||
Added the accounting reports.
|
||||
|
||||
|
||||
Version 0.4.0 (Pre-release)
|
||||
---------------------------
|
||||
|
||||
Released 2023/3/1
|
||||
|
||||
Added the transaction summary helper.
|
||||
|
||||
|
||||
Version 0.3.1 (Pre-release)
|
||||
---------------------------
|
||||
|
||||
Released 2023/2/28
|
||||
|
||||
* Fixed the error that cannot select any account when adding new
|
||||
transactions.
|
||||
* Fixed the database error when adding new transactions.
|
||||
* Added the button to convert a cash income or cash expense
|
||||
transaction to a transfer transaction.
|
||||
|
||||
|
||||
Version 0.3.0 (Pre-release)
|
||||
---------------------------
|
||||
|
||||
Released 2023/2/27
|
||||
|
||||
Added the transaction management.
|
||||
|
||||
|
||||
Version 0.2.0 (Pre-release)
|
||||
---------------------------
|
||||
|
||||
Released 2023/2/7
|
||||
|
||||
* Added the currency management.
|
||||
* Changed the ``can_edit`` permission to at least require the user to
|
||||
log in first.
|
||||
* Changed the type hint of the ``current_user`` pseudo property of
|
||||
the ``AbstractUserUtils`` class to return ``None`` when the user
|
||||
has not logged in.
|
||||
|
||||
|
||||
Version 0.1.1 (Pre-release)
|
||||
---------------------------
|
||||
|
||||
Released 2023/2/3
|
||||
|
||||
Finalized the account management, with tests and reordering.
|
||||
|
||||
|
||||
Version 0.1.0 (Pre-release)
|
||||
---------------------------
|
||||
|
||||
Released 2023/2/3
|
||||
|
||||
Added the account management, and updated the API to initialize the
|
||||
accounting application.
|
||||
|
||||
|
||||
Version 0.0.0 (Pre-release)
|
||||
---------------------------
|
||||
|
||||
Released 2023/2/3
|
||||
|
||||
Initial release with main account list, localization, pagination,
|
||||
query, permission, Sphinx documentation, and a test case based on a
|
||||
test demonstration site.
|
@ -6,6 +6,7 @@ import os
|
||||
import sys
|
||||
|
||||
sys.path.insert(0, os.path.abspath('../../src/'))
|
||||
import accounting
|
||||
|
||||
# -- Project information -----------------------------------------------------
|
||||
# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information
|
||||
@ -13,7 +14,7 @@ sys.path.insert(0, os.path.abspath('../../src/'))
|
||||
project = 'Mia! Accounting'
|
||||
copyright = '2023, imacat'
|
||||
author = 'imacat'
|
||||
release = '1.3.3'
|
||||
release = accounting.VERSION
|
||||
|
||||
# -- General configuration ---------------------------------------------------
|
||||
# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration
|
||||
|
@ -14,6 +14,7 @@ Welcome to Mia! Accounting's documentation!
|
||||
accounting
|
||||
examples
|
||||
history
|
||||
changelog
|
||||
|
||||
|
||||
|
||||
|
@ -103,12 +103,6 @@ base template:
|
||||
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
|
||||
.. _live demonstration: https://accounting.imacat.idv.tw
|
||||
@ -123,4 +117,3 @@ Refer to the `documentation on Read the Docs`_.
|
||||
.. _Decimal.js: https://mikemcl.github.io/decimal.js
|
||||
.. _Tempus-Dominus: https://getdatepicker.com
|
||||
.. _Bootstrap navigation bar: https://getbootstrap.com/docs/5.3/components/navbar/
|
||||
.. _documentation on Read the Docs: https://mia-accounting.readthedocs.io
|
||||
|
@ -17,7 +17,7 @@
|
||||
|
||||
[project]
|
||||
name = "mia-accounting"
|
||||
version = "1.3.3"
|
||||
dynamic = ["version"]
|
||||
description = "A Flask accounting module."
|
||||
readme = "README.rst"
|
||||
requires-python = ">=3.11"
|
||||
@ -34,6 +34,7 @@ classifiers = [
|
||||
]
|
||||
dependencies = [
|
||||
"flask",
|
||||
"SQLAlchemy >= 2",
|
||||
"Flask-SQLAlchemy",
|
||||
"Flask-WTF",
|
||||
"Flask-Babel >= 3",
|
||||
@ -49,6 +50,7 @@ test = [
|
||||
|
||||
[project.urls]
|
||||
"Documentation" = "https://mia-accounting.readthedocs.io"
|
||||
"Change Log" = "https://mia-accounting.readthedocs.io/en/latest/changelog.html"
|
||||
"Repository" = "https://github.com/imacat/mia-accounting"
|
||||
"Bug Tracker" = "https://github.com/imacat/mia-accounting/issues"
|
||||
"Demonstration" = "https://accounting.imacat.idv.tw"
|
||||
@ -57,6 +59,9 @@ test = [
|
||||
requires = ["setuptools>=42"]
|
||||
build-backend = "setuptools.build_meta"
|
||||
|
||||
[tool.setuptools.dynamic]
|
||||
version = {attr = "accounting.VERSION"}
|
||||
|
||||
[tool.setuptools.exclude-package-data]
|
||||
"*" = [
|
||||
"babel.cfg",
|
||||
|
@ -24,6 +24,8 @@ from flask_sqlalchemy import SQLAlchemy
|
||||
|
||||
from accounting.utils.user import UserUtilityInterface
|
||||
|
||||
VERSION: str = "1.5.0"
|
||||
"""The package version."""
|
||||
db: SQLAlchemy = SQLAlchemy()
|
||||
"""The database instance."""
|
||||
data_dir: Path = Path(__file__).parent / "data"
|
||||
|
@ -37,7 +37,8 @@ class JournalEntryConverter(BaseConverter):
|
||||
:param value: The journal entry ID.
|
||||
:return: The corresponding journal entry.
|
||||
"""
|
||||
journal_entry: JournalEntry | None = db.session.get(JournalEntry, value)
|
||||
journal_entry: JournalEntry | None \
|
||||
= db.session.get(JournalEntry, value)
|
||||
if journal_entry is None:
|
||||
abort(404)
|
||||
return journal_entry
|
||||
|
@ -30,7 +30,6 @@ from accounting import db
|
||||
from accounting.forms import CurrencyExists
|
||||
from accounting.locale import lazy_gettext
|
||||
from accounting.models import JournalEntryLineItem
|
||||
from accounting.utils.cast import be
|
||||
from accounting.utils.offset_alias import offset_alias
|
||||
from accounting.utils.strip_text import strip_text
|
||||
from .line_item import LineItemForm, CreditLineItemForm, DebitLineItemForm
|
||||
@ -75,8 +74,8 @@ class KeepCurrencyWhenHavingOffset:
|
||||
offset: sa.Alias = offset_alias()
|
||||
original_line_items: list[JournalEntryLineItem]\
|
||||
= JournalEntryLineItem.query\
|
||||
.join(offset, be(JournalEntryLineItem.id
|
||||
== offset.c.original_line_item_id),
|
||||
.join(offset,
|
||||
JournalEntryLineItem.id == offset.c.original_line_item_id,
|
||||
isouter=True)\
|
||||
.filter(JournalEntryLineItem.id
|
||||
.in_({x.id.data for x in form.line_items
|
||||
|
@ -33,7 +33,6 @@ from accounting.forms import ACCOUNT_REQUIRED, AccountExists, IsDebitAccount, \
|
||||
from accounting.locale import lazy_gettext
|
||||
from accounting.models import Account, JournalEntry, JournalEntryLineItem
|
||||
from accounting.template_filters import format_amount
|
||||
from accounting.utils.cast import be
|
||||
from accounting.utils.random_id import new_id
|
||||
from accounting.utils.strip_text import strip_text
|
||||
from accounting.utils.user import get_current_user_pk
|
||||
@ -198,13 +197,13 @@ class NotExceedingOriginalLineItemNetBalance:
|
||||
existing_line_item_id \
|
||||
= {x.id for x in form.journal_entry_form.obj.line_items}
|
||||
offset_total_func: sa.Function = sa.func.sum(sa.case(
|
||||
(be(JournalEntryLineItem.is_debit == is_debit),
|
||||
(JournalEntryLineItem.is_debit == is_debit,
|
||||
JournalEntryLineItem.amount),
|
||||
else_=-JournalEntryLineItem.amount))
|
||||
offset_total_but_form: Decimal | None = db.session.scalar(
|
||||
sa.select(offset_total_func)
|
||||
.filter(be(JournalEntryLineItem.original_line_item_id
|
||||
== original_line_item.id),
|
||||
.filter(JournalEntryLineItem.original_line_item_id
|
||||
== original_line_item.id,
|
||||
JournalEntryLineItem.id.not_in(existing_line_item_id)))
|
||||
if offset_total_but_form is None:
|
||||
offset_total_but_form = Decimal("0")
|
||||
@ -232,8 +231,7 @@ class NotLessThanOffsetTotal:
|
||||
(JournalEntryLineItem.is_debit != is_debit,
|
||||
JournalEntryLineItem.amount),
|
||||
else_=-JournalEntryLineItem.amount)))\
|
||||
.filter(be(JournalEntryLineItem.original_line_item_id
|
||||
== form.id.data))
|
||||
.filter(JournalEntryLineItem.original_line_item_id == form.id.data)
|
||||
offset_total: Decimal | None = db.session.scalar(select_offset_total)
|
||||
if offset_total is not None and field.data < offset_total:
|
||||
raise ValidationError(lazy_gettext(
|
||||
|
@ -24,7 +24,6 @@ from sqlalchemy.orm import selectinload
|
||||
|
||||
from accounting import db
|
||||
from accounting.models import Account, JournalEntry, JournalEntryLineItem
|
||||
from accounting.utils.cast import be
|
||||
from accounting.utils.offset_alias import offset_alias
|
||||
|
||||
|
||||
@ -45,8 +44,7 @@ def get_selectable_original_line_items(
|
||||
offset: sa.Alias = offset_alias()
|
||||
net_balance: sa.Label = (JournalEntryLineItem.amount + sa.func.sum(sa.case(
|
||||
(offset.c.id.in_(line_item_id_on_form), 0),
|
||||
(be(offset.c.is_debit == JournalEntryLineItem.is_debit),
|
||||
offset.c.amount),
|
||||
(offset.c.is_debit == JournalEntryLineItem.is_debit, offset.c.amount),
|
||||
else_=-offset.c.amount))).label("net_balance")
|
||||
conditions: list[sa.BinaryExpression] = [Account.is_need_offset]
|
||||
sub_conditions: list[sa.BinaryExpression] = []
|
||||
@ -60,8 +58,8 @@ def get_selectable_original_line_items(
|
||||
select_net_balances: sa.Select \
|
||||
= sa.select(JournalEntryLineItem.id, net_balance)\
|
||||
.join(Account)\
|
||||
.join(offset, be(JournalEntryLineItem.id
|
||||
== offset.c.original_line_item_id),
|
||||
.join(offset,
|
||||
JournalEntryLineItem.id == offset.c.original_line_item_id,
|
||||
isouter=True)\
|
||||
.filter(*conditions)\
|
||||
.group_by(JournalEntryLineItem.id)\
|
||||
|
@ -19,6 +19,7 @@
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import datetime as dt
|
||||
import re
|
||||
import typing as t
|
||||
from decimal import Decimal
|
||||
@ -27,6 +28,7 @@ import sqlalchemy as sa
|
||||
from babel import Locale
|
||||
from flask_babel import get_locale, get_babel
|
||||
from sqlalchemy import text
|
||||
from sqlalchemy.orm import Mapped, mapped_column
|
||||
|
||||
from accounting import db
|
||||
from accounting.locale import gettext
|
||||
@ -37,14 +39,14 @@ class BaseAccount(db.Model):
|
||||
"""A base account."""
|
||||
__tablename__ = "accounting_base_accounts"
|
||||
"""The table name."""
|
||||
code = db.Column(db.String, nullable=False, primary_key=True)
|
||||
code: Mapped[str] = mapped_column(primary_key=True)
|
||||
"""The code."""
|
||||
title_l10n = db.Column("title", db.String, nullable=False)
|
||||
title_l10n: Mapped[str] = mapped_column("title")
|
||||
"""The title."""
|
||||
l10n = db.relationship("BaseAccountL10n", back_populates="account",
|
||||
lazy=False)
|
||||
l10n: Mapped[list[BaseAccountL10n]] \
|
||||
= db.relationship(back_populates="account", lazy=False)
|
||||
"""The localized titles."""
|
||||
accounts = db.relationship("Account", back_populates="base")
|
||||
accounts: Mapped[list[Account]] = db.relationship(back_populates="base")
|
||||
"""The descendant accounts under the base account."""
|
||||
|
||||
def __str__(self) -> str:
|
||||
@ -81,17 +83,16 @@ class BaseAccountL10n(db.Model):
|
||||
"""A localized base account title."""
|
||||
__tablename__ = "accounting_base_accounts_l10n"
|
||||
"""The table name."""
|
||||
account_code = db.Column(db.String,
|
||||
db.ForeignKey(BaseAccount.code,
|
||||
onupdate="CASCADE",
|
||||
account_code: Mapped[str] \
|
||||
= mapped_column(db.ForeignKey(BaseAccount.code, onupdate="CASCADE",
|
||||
ondelete="CASCADE"),
|
||||
nullable=False, primary_key=True)
|
||||
primary_key=True)
|
||||
"""The code of the account."""
|
||||
account = db.relationship(BaseAccount, back_populates="l10n")
|
||||
account: Mapped[BaseAccount] = db.relationship(back_populates="l10n")
|
||||
"""The account."""
|
||||
locale = db.Column(db.String, nullable=False, primary_key=True)
|
||||
locale: Mapped[str] = mapped_column(primary_key=True)
|
||||
"""The locale."""
|
||||
title = db.Column(db.String, nullable=False)
|
||||
title: Mapped[str]
|
||||
"""The localized title."""
|
||||
|
||||
|
||||
@ -99,47 +100,43 @@ class Account(db.Model):
|
||||
"""An account."""
|
||||
__tablename__ = "accounting_accounts"
|
||||
"""The table name."""
|
||||
id = db.Column(db.Integer, nullable=False, primary_key=True,
|
||||
autoincrement=False)
|
||||
id: Mapped[int] = mapped_column(primary_key=True, autoincrement=False)
|
||||
"""The account ID."""
|
||||
base_code = db.Column(db.String,
|
||||
db.ForeignKey(BaseAccount.code, onupdate="CASCADE",
|
||||
ondelete="CASCADE"),
|
||||
nullable=False)
|
||||
base_code: Mapped[str] \
|
||||
= mapped_column(db.ForeignKey(BaseAccount.code, onupdate="CASCADE",
|
||||
ondelete="CASCADE"))
|
||||
"""The code of the base account."""
|
||||
base = db.relationship(BaseAccount, back_populates="accounts")
|
||||
base: Mapped[BaseAccount] = db.relationship(back_populates="accounts")
|
||||
"""The base account."""
|
||||
no = db.Column(db.Integer, nullable=False, default=text("1"))
|
||||
no: Mapped[int] = mapped_column(default=text("1"))
|
||||
"""The account number under the base account."""
|
||||
title_l10n = db.Column("title", db.String, nullable=False)
|
||||
title_l10n: Mapped[str] = mapped_column("title")
|
||||
"""The title."""
|
||||
is_need_offset = db.Column(db.Boolean, nullable=False, default=False)
|
||||
is_need_offset: Mapped[bool] = mapped_column(default=False)
|
||||
"""Whether the journal entry line items of this account need offset."""
|
||||
created_at = db.Column(db.DateTime(timezone=True), nullable=False,
|
||||
created_at: Mapped[dt.datetime] \
|
||||
= mapped_column(db.DateTime(timezone=True),
|
||||
server_default=db.func.now())
|
||||
"""The time of creation."""
|
||||
created_by_id = db.Column(db.Integer,
|
||||
db.ForeignKey(user_pk_column,
|
||||
onupdate="CASCADE"),
|
||||
nullable=False)
|
||||
created_by_id: Mapped[int] \
|
||||
= mapped_column(db.ForeignKey(user_pk_column, onupdate="CASCADE"))
|
||||
"""The ID of the creator."""
|
||||
created_by = db.relationship(user_cls, foreign_keys=created_by_id)
|
||||
created_by: Mapped[user_cls] = db.relationship(foreign_keys=created_by_id)
|
||||
"""The creator."""
|
||||
updated_at = db.Column(db.DateTime(timezone=True), nullable=False,
|
||||
updated_at: Mapped[dt.datetime] \
|
||||
= mapped_column(db.DateTime(timezone=True),
|
||||
server_default=db.func.now())
|
||||
"""The time of last update."""
|
||||
updated_by_id = db.Column(db.Integer,
|
||||
db.ForeignKey(user_pk_column,
|
||||
onupdate="CASCADE"),
|
||||
nullable=False)
|
||||
updated_by_id: Mapped[int] \
|
||||
= mapped_column(db.ForeignKey(user_pk_column, onupdate="CASCADE"))
|
||||
"""The ID of the updator."""
|
||||
updated_by = db.relationship(user_cls, foreign_keys=updated_by_id)
|
||||
updated_by: Mapped[user_cls] = db.relationship(foreign_keys=updated_by_id)
|
||||
"""The updator."""
|
||||
l10n = db.relationship("AccountL10n", back_populates="account",
|
||||
lazy=False)
|
||||
l10n: Mapped[list[AccountL10n]] \
|
||||
= db.relationship(back_populates="account", lazy=False)
|
||||
"""The localized titles."""
|
||||
line_items = db.relationship("JournalEntryLineItem",
|
||||
back_populates="account")
|
||||
line_items: Mapped[list[JournalEntryLineItem]] \
|
||||
= db.relationship(back_populates="account")
|
||||
"""The journal entry line items."""
|
||||
|
||||
CASH_CODE: str = "1111-001"
|
||||
@ -352,16 +349,16 @@ class AccountL10n(db.Model):
|
||||
"""A localized account title."""
|
||||
__tablename__ = "accounting_accounts_l10n"
|
||||
"""The table name."""
|
||||
account_id = db.Column(db.Integer,
|
||||
db.ForeignKey(Account.id, onupdate="CASCADE",
|
||||
account_id: Mapped[int] \
|
||||
= mapped_column(db.ForeignKey(Account.id, onupdate="CASCADE",
|
||||
ondelete="CASCADE"),
|
||||
nullable=False, primary_key=True)
|
||||
primary_key=True)
|
||||
"""The account ID."""
|
||||
account = db.relationship(Account, back_populates="l10n")
|
||||
account: Mapped[Account] = db.relationship(back_populates="l10n")
|
||||
"""The account."""
|
||||
locale = db.Column(db.String, nullable=False, primary_key=True)
|
||||
locale: Mapped[str] = mapped_column(primary_key=True)
|
||||
"""The locale."""
|
||||
title = db.Column(db.String, nullable=False)
|
||||
title: Mapped[str]
|
||||
"""The localized title."""
|
||||
|
||||
|
||||
@ -369,35 +366,34 @@ class Currency(db.Model):
|
||||
"""A currency."""
|
||||
__tablename__ = "accounting_currencies"
|
||||
"""The table name."""
|
||||
code = db.Column(db.String, nullable=False, primary_key=True)
|
||||
code: Mapped[str] = mapped_column(primary_key=True)
|
||||
"""The code."""
|
||||
name_l10n = db.Column("name", db.String, nullable=False)
|
||||
name_l10n: Mapped[str] = mapped_column("name")
|
||||
"""The name."""
|
||||
created_at = db.Column(db.DateTime(timezone=True), nullable=False,
|
||||
created_at: Mapped[dt.datetime] \
|
||||
= mapped_column(db.DateTime(timezone=True),
|
||||
server_default=db.func.now())
|
||||
"""The time of creation."""
|
||||
created_by_id = db.Column(db.Integer,
|
||||
db.ForeignKey(user_pk_column,
|
||||
onupdate="CASCADE"),
|
||||
nullable=False)
|
||||
created_by_id: Mapped[int] \
|
||||
= mapped_column(db.ForeignKey(user_pk_column, onupdate="CASCADE"))
|
||||
"""The ID of the creator."""
|
||||
created_by = db.relationship(user_cls, foreign_keys=created_by_id)
|
||||
created_by: Mapped[user_cls] = db.relationship(foreign_keys=created_by_id)
|
||||
"""The creator."""
|
||||
updated_at = db.Column(db.DateTime(timezone=True), nullable=False,
|
||||
updated_at: Mapped[dt.datetime] \
|
||||
= mapped_column(db.DateTime(timezone=True),
|
||||
server_default=db.func.now())
|
||||
"""The time of last update."""
|
||||
updated_by_id = db.Column(db.Integer,
|
||||
db.ForeignKey(user_pk_column,
|
||||
onupdate="CASCADE"),
|
||||
nullable=False)
|
||||
updated_by_id: Mapped[int] \
|
||||
= mapped_column(db.ForeignKey(user_pk_column, onupdate="CASCADE"))
|
||||
"""The ID of the updator."""
|
||||
updated_by = db.relationship(user_cls, foreign_keys=updated_by_id)
|
||||
updated_by: Mapped[user_cls] \
|
||||
= db.relationship(foreign_keys=updated_by_id)
|
||||
"""The updator."""
|
||||
l10n = db.relationship("CurrencyL10n", back_populates="currency",
|
||||
lazy=False)
|
||||
l10n: Mapped[list[CurrencyL10n]] \
|
||||
= db.relationship(back_populates="currency", lazy=False)
|
||||
"""The localized names."""
|
||||
line_items = db.relationship("JournalEntryLineItem",
|
||||
back_populates="currency")
|
||||
line_items: Mapped[list[JournalEntryLineItem]] \
|
||||
= db.relationship(back_populates="currency")
|
||||
"""The journal entry line items."""
|
||||
|
||||
def __str__(self) -> str:
|
||||
@ -479,16 +475,16 @@ class CurrencyL10n(db.Model):
|
||||
"""A localized currency name."""
|
||||
__tablename__ = "accounting_currencies_l10n"
|
||||
"""The table name."""
|
||||
currency_code = db.Column(db.String,
|
||||
db.ForeignKey(Currency.code, onupdate="CASCADE",
|
||||
currency_code: Mapped[str] \
|
||||
= mapped_column(db.ForeignKey(Currency.code, onupdate="CASCADE",
|
||||
ondelete="CASCADE"),
|
||||
nullable=False, primary_key=True)
|
||||
primary_key=True)
|
||||
"""The currency code."""
|
||||
currency = db.relationship(Currency, back_populates="l10n")
|
||||
currency: Mapped[Currency] = db.relationship(back_populates="l10n")
|
||||
"""The currency."""
|
||||
locale = db.Column(db.String, nullable=False, primary_key=True)
|
||||
locale: Mapped[str] = mapped_column(primary_key=True)
|
||||
"""The locale."""
|
||||
name = db.Column(db.String, nullable=False)
|
||||
name: Mapped[str]
|
||||
"""The localized name."""
|
||||
|
||||
|
||||
@ -539,37 +535,34 @@ class JournalEntry(db.Model):
|
||||
"""A journal entry."""
|
||||
__tablename__ = "accounting_journal_entries"
|
||||
"""The table name."""
|
||||
id = db.Column(db.Integer, nullable=False, primary_key=True,
|
||||
autoincrement=False)
|
||||
id: Mapped[int] = mapped_column(primary_key=True, autoincrement=False)
|
||||
"""The journal entry ID."""
|
||||
date = db.Column(db.Date, nullable=False)
|
||||
date: Mapped[dt.date]
|
||||
"""The date."""
|
||||
no = db.Column(db.Integer, nullable=False, default=text("1"))
|
||||
no: Mapped[int] = mapped_column(default=text("1"))
|
||||
"""The account number under the date."""
|
||||
note = db.Column(db.String)
|
||||
note: Mapped[str | None]
|
||||
"""The note."""
|
||||
created_at = db.Column(db.DateTime(timezone=True), nullable=False,
|
||||
created_at: Mapped[dt.datetime] \
|
||||
= mapped_column(db.DateTime(timezone=True),
|
||||
server_default=db.func.now())
|
||||
"""The time of creation."""
|
||||
created_by_id = db.Column(db.Integer,
|
||||
db.ForeignKey(user_pk_column,
|
||||
onupdate="CASCADE"),
|
||||
nullable=False)
|
||||
created_by_id: Mapped[int] \
|
||||
= mapped_column(db.ForeignKey(user_pk_column, onupdate="CASCADE"))
|
||||
"""The ID of the creator."""
|
||||
created_by = db.relationship(user_cls, foreign_keys=created_by_id)
|
||||
created_by: Mapped[user_cls] = db.relationship(foreign_keys=created_by_id)
|
||||
"""The creator."""
|
||||
updated_at = db.Column(db.DateTime(timezone=True), nullable=False,
|
||||
updated_at: Mapped[dt.datetime] \
|
||||
= mapped_column(db.DateTime(timezone=True),
|
||||
server_default=db.func.now())
|
||||
"""The time of last update."""
|
||||
updated_by_id = db.Column(db.Integer,
|
||||
db.ForeignKey(user_pk_column,
|
||||
onupdate="CASCADE"),
|
||||
nullable=False)
|
||||
updated_by_id: Mapped[int] \
|
||||
= mapped_column(db.ForeignKey(user_pk_column, onupdate="CASCADE"))
|
||||
"""The ID of the updator."""
|
||||
updated_by = db.relationship(user_cls, foreign_keys=updated_by_id)
|
||||
updated_by: Mapped[user_cls] = db.relationship(foreign_keys=updated_by_id)
|
||||
"""The updator."""
|
||||
line_items = db.relationship("JournalEntryLineItem",
|
||||
back_populates="journal_entry")
|
||||
line_items: Mapped[list[JournalEntryLineItem]] \
|
||||
= db.relationship(back_populates="journal_entry")
|
||||
"""The line items."""
|
||||
|
||||
def __str__(self) -> str:
|
||||
@ -659,44 +652,39 @@ class JournalEntryLineItem(db.Model):
|
||||
"""A line item in the journal entry."""
|
||||
__tablename__ = "accounting_journal_entry_line_items"
|
||||
"""The table name."""
|
||||
id = db.Column(db.Integer, nullable=False, primary_key=True,
|
||||
autoincrement=False)
|
||||
id: Mapped[int] = mapped_column(primary_key=True, autoincrement=False)
|
||||
"""The line item ID."""
|
||||
journal_entry_id = db.Column(db.Integer,
|
||||
db.ForeignKey(JournalEntry.id,
|
||||
onupdate="CASCADE",
|
||||
ondelete="CASCADE"),
|
||||
nullable=False)
|
||||
journal_entry_id: Mapped[int] \
|
||||
= mapped_column(db.ForeignKey(JournalEntry.id, onupdate="CASCADE",
|
||||
ondelete="CASCADE"))
|
||||
"""The journal entry ID."""
|
||||
journal_entry = db.relationship(JournalEntry, back_populates="line_items")
|
||||
journal_entry: Mapped[JournalEntry] \
|
||||
= db.relationship(back_populates="line_items")
|
||||
"""The journal entry."""
|
||||
is_debit = db.Column(db.Boolean, nullable=False)
|
||||
is_debit: Mapped[bool]
|
||||
"""True for a debit line item, or False for a credit line item."""
|
||||
no = db.Column(db.Integer, nullable=False)
|
||||
no: Mapped[int]
|
||||
"""The line item number under the journal entry and debit or credit."""
|
||||
original_line_item_id = db.Column(db.Integer,
|
||||
db.ForeignKey(id, onupdate="CASCADE"),
|
||||
nullable=True)
|
||||
original_line_item_id: Mapped[int | None] \
|
||||
= mapped_column(db.ForeignKey(id, onupdate="CASCADE"))
|
||||
"""The ID of the original line item."""
|
||||
original_line_item = db.relationship("JournalEntryLineItem",
|
||||
remote_side=id, passive_deletes=True)
|
||||
original_line_item: Mapped[JournalEntryLineItem | None] \
|
||||
= db.relationship(remote_side=id, passive_deletes=True)
|
||||
"""The original line item."""
|
||||
currency_code = db.Column(db.String,
|
||||
db.ForeignKey(Currency.code, onupdate="CASCADE"),
|
||||
nullable=False)
|
||||
currency_code: Mapped[str] \
|
||||
= mapped_column(db.ForeignKey(Currency.code, onupdate="CASCADE"))
|
||||
"""The currency code."""
|
||||
currency = db.relationship(Currency, back_populates="line_items")
|
||||
currency: Mapped[Currency] = db.relationship(back_populates="line_items")
|
||||
"""The currency."""
|
||||
account_id = db.Column(db.Integer,
|
||||
db.ForeignKey(Account.id,
|
||||
onupdate="CASCADE"),
|
||||
nullable=False)
|
||||
account_id: Mapped[int] \
|
||||
= mapped_column(db.ForeignKey(Account.id, onupdate="CASCADE"))
|
||||
"""The account ID."""
|
||||
account = db.relationship(Account, back_populates="line_items", lazy=False)
|
||||
account: Mapped[Account] \
|
||||
= db.relationship(back_populates="line_items", lazy=False)
|
||||
"""The account."""
|
||||
description = db.Column(db.String, nullable=True)
|
||||
description: Mapped[str | None]
|
||||
"""The description."""
|
||||
amount = db.Column(db.Numeric(14, 2), nullable=False)
|
||||
amount: Mapped[Decimal] = mapped_column(db.Numeric(14, 2))
|
||||
"""The amount."""
|
||||
|
||||
def __str__(self) -> str:
|
||||
@ -891,27 +879,25 @@ class Option(db.Model):
|
||||
"""An option."""
|
||||
__tablename__ = "accounting_options"
|
||||
"""The table name."""
|
||||
name = db.Column(db.String, nullable=False, primary_key=True)
|
||||
name: Mapped[str] = mapped_column(primary_key=True)
|
||||
"""The name."""
|
||||
value = db.Column(db.Text, nullable=False)
|
||||
value: Mapped[str] = mapped_column(db.Text)
|
||||
"""The option value."""
|
||||
created_at = db.Column(db.DateTime(timezone=True), nullable=False,
|
||||
created_at: Mapped[dt.datetime] \
|
||||
= mapped_column(db.DateTime(timezone=True),
|
||||
server_default=db.func.now())
|
||||
"""The time of creation."""
|
||||
created_by_id = db.Column(db.Integer,
|
||||
db.ForeignKey(user_pk_column,
|
||||
onupdate="CASCADE"),
|
||||
nullable=False)
|
||||
created_by_id: Mapped[int] \
|
||||
= mapped_column(db.ForeignKey(user_pk_column, onupdate="CASCADE"))
|
||||
"""The ID of the creator."""
|
||||
created_by = db.relationship(user_cls, foreign_keys=created_by_id)
|
||||
created_by: Mapped[user_cls] = db.relationship(foreign_keys=created_by_id)
|
||||
"""The creator."""
|
||||
updated_at = db.Column(db.DateTime(timezone=True), nullable=False,
|
||||
updated_at: Mapped[dt.datetime] \
|
||||
= mapped_column(db.DateTime(timezone=True),
|
||||
server_default=db.func.now())
|
||||
"""The time of last update."""
|
||||
updated_by_id = db.Column(db.Integer,
|
||||
db.ForeignKey(user_pk_column,
|
||||
onupdate="CASCADE"),
|
||||
nullable=False)
|
||||
updated_by_id: Mapped[int] \
|
||||
= mapped_column(db.ForeignKey(user_pk_column, onupdate="CASCADE"))
|
||||
"""The ID of the updator."""
|
||||
updated_by = db.relationship(user_cls, foreign_keys=updated_by_id)
|
||||
updated_by: Mapped[user_cls] = db.relationship(foreign_keys=updated_by_id)
|
||||
"""The updator."""
|
||||
|
@ -37,7 +37,6 @@ from accounting.report.utils.option_link import OptionLink
|
||||
from accounting.report.utils.report_chooser import ReportChooser
|
||||
from accounting.report.utils.report_type import ReportType
|
||||
from accounting.report.utils.urls import income_expenses_url
|
||||
from accounting.utils.cast import be
|
||||
from accounting.utils.current_account import CurrentAccount
|
||||
from accounting.utils.pagination import Pagination
|
||||
|
||||
@ -122,8 +121,7 @@ class LineItemCollector:
|
||||
else_=-JournalEntryLineItem.amount))
|
||||
select: sa.Select = sa.Select(balance_func)\
|
||||
.join(JournalEntry).join(Account)\
|
||||
.filter(be(JournalEntryLineItem.currency_code
|
||||
== self.__currency.code),
|
||||
.filter(JournalEntryLineItem.currency_code == self.__currency.code,
|
||||
self.__account_condition,
|
||||
JournalEntry.date < self.__period.start)
|
||||
balance: int | None = db.session.scalar(select)
|
||||
@ -347,8 +345,7 @@ class PageParams(BasePageParams):
|
||||
self.account.id == 0)]
|
||||
in_use: sa.Select = sa.Select(JournalEntryLineItem.account_id)\
|
||||
.join(Account)\
|
||||
.filter(be(JournalEntryLineItem.currency_code
|
||||
== self.currency.code),
|
||||
.filter(JournalEntryLineItem.currency_code == self.currency.code,
|
||||
CurrentAccount.sql_condition())\
|
||||
.group_by(JournalEntryLineItem.account_id)
|
||||
options.extend([OptionLink(str(x),
|
||||
|
@ -37,7 +37,6 @@ from accounting.report.utils.option_link import OptionLink
|
||||
from accounting.report.utils.report_chooser import ReportChooser
|
||||
from accounting.report.utils.report_type import ReportType
|
||||
from accounting.report.utils.urls import ledger_url
|
||||
from accounting.utils.cast import be
|
||||
from accounting.utils.pagination import Pagination
|
||||
|
||||
|
||||
@ -118,10 +117,8 @@ class LineItemCollector:
|
||||
(JournalEntryLineItem.is_debit, JournalEntryLineItem.amount),
|
||||
else_=-JournalEntryLineItem.amount))
|
||||
select: sa.Select = sa.Select(balance_func).join(JournalEntry)\
|
||||
.filter(be(JournalEntryLineItem.currency_code
|
||||
== self.__currency.code),
|
||||
be(JournalEntryLineItem.account_id
|
||||
== self.__account.id),
|
||||
.filter(JournalEntryLineItem.currency_code == self.__currency.code,
|
||||
JournalEntryLineItem.account_id == self.__account.id,
|
||||
JournalEntry.date < self.__period.start)
|
||||
balance: int | None = db.session.scalar(select)
|
||||
if balance is None:
|
||||
@ -313,8 +310,7 @@ class PageParams(BasePageParams):
|
||||
:return: The account options.
|
||||
"""
|
||||
in_use: sa.Select = sa.Select(JournalEntryLineItem.account_id)\
|
||||
.filter(be(JournalEntryLineItem.currency_code
|
||||
== self.currency.code))\
|
||||
.filter(JournalEntryLineItem.currency_code == self.currency.code)\
|
||||
.group_by(JournalEntryLineItem.account_id)
|
||||
return [OptionLink(str(x), ledger_url(self.currency, x, self.period),
|
||||
x.id == self.account.id)
|
||||
|
@ -32,7 +32,6 @@ from accounting.report.utils.base_report import BaseReport
|
||||
from accounting.report.utils.csv_export import csv_download
|
||||
from accounting.report.utils.report_chooser import ReportChooser
|
||||
from accounting.report.utils.report_type import ReportType
|
||||
from accounting.utils.cast import be
|
||||
from accounting.utils.pagination import Pagination
|
||||
from accounting.utils.query import parse_query_keywords
|
||||
from .journal import get_csv_rows
|
||||
@ -128,9 +127,8 @@ class LineItemCollector:
|
||||
journal_entry_date: datetime
|
||||
try:
|
||||
journal_entry_date = datetime.strptime(k, "%Y")
|
||||
conditions.append(
|
||||
be(sa.extract("year", JournalEntry.date)
|
||||
== journal_entry_date.year))
|
||||
conditions.append(sa.extract("year", JournalEntry.date)
|
||||
== journal_entry_date.year)
|
||||
except ValueError:
|
||||
pass
|
||||
try:
|
||||
|
@ -24,7 +24,6 @@ import sqlalchemy as sa
|
||||
from accounting import db
|
||||
from accounting.models import Currency, Account, JournalEntry, \
|
||||
JournalEntryLineItem
|
||||
from accounting.utils.cast import be
|
||||
from accounting.utils.offset_alias import offset_alias
|
||||
|
||||
|
||||
@ -38,17 +37,17 @@ def get_accounts_with_unapplied(currency: Currency) -> list[Account]:
|
||||
net_balance: sa.Label \
|
||||
= (JournalEntryLineItem.amount
|
||||
+ sa.func.sum(sa.case(
|
||||
(be(offset.c.is_debit == JournalEntryLineItem.is_debit),
|
||||
(offset.c.is_debit == JournalEntryLineItem.is_debit,
|
||||
offset.c.amount),
|
||||
else_=-offset.c.amount))).label("net_balance")
|
||||
select_unapplied: sa.Select \
|
||||
= sa.select(JournalEntryLineItem.id)\
|
||||
.join(JournalEntry).join(Account)\
|
||||
.join(offset, be(JournalEntryLineItem.id
|
||||
== offset.c.original_line_item_id),
|
||||
.join(offset,
|
||||
JournalEntryLineItem.id == offset.c.original_line_item_id,
|
||||
isouter=True)\
|
||||
.filter(Account.is_need_offset,
|
||||
be(JournalEntryLineItem.currency_code == currency.code),
|
||||
JournalEntryLineItem.currency_code == currency.code,
|
||||
sa.or_(sa.and_(Account.base_code.startswith("2"),
|
||||
sa.not_(JournalEntryLineItem.is_debit)),
|
||||
sa.and_(Account.base_code.startswith("1"),
|
||||
@ -84,17 +83,17 @@ def get_net_balances(currency: Currency, account: Account) \
|
||||
net_balance: sa.Label \
|
||||
= (JournalEntryLineItem.amount
|
||||
+ sa.func.sum(sa.case(
|
||||
(be(offset.c.is_debit == JournalEntryLineItem.is_debit),
|
||||
(offset.c.is_debit == JournalEntryLineItem.is_debit,
|
||||
offset.c.amount),
|
||||
else_=-offset.c.amount))).label("net_balance")
|
||||
select_net_balances: sa.Select \
|
||||
= sa.select(JournalEntryLineItem.id, net_balance) \
|
||||
.join(JournalEntry).join(Account) \
|
||||
.join(offset, be(JournalEntryLineItem.id
|
||||
== offset.c.original_line_item_id),
|
||||
.join(offset,
|
||||
JournalEntryLineItem.id == offset.c.original_line_item_id,
|
||||
isouter=True) \
|
||||
.filter(be(Account.id == account.id),
|
||||
be(JournalEntryLineItem.currency_code == currency.code),
|
||||
.filter(Account.id == account.id,
|
||||
JournalEntryLineItem.currency_code == currency.code,
|
||||
sa.or_(sa.and_(Account.base_code.startswith("2"),
|
||||
sa.not_(JournalEntryLineItem.is_debit)),
|
||||
sa.and_(Account.base_code.startswith("1"),
|
||||
|
@ -22,7 +22,6 @@ import sqlalchemy as sa
|
||||
from accounting import db
|
||||
from accounting.models import Currency, Account, JournalEntry, \
|
||||
JournalEntryLineItem
|
||||
from accounting.utils.cast import be
|
||||
|
||||
|
||||
def get_accounts_with_unmatched(currency: Currency) -> list[Account]:
|
||||
@ -38,7 +37,7 @@ def get_accounts_with_unmatched(currency: Currency) -> list[Account]:
|
||||
.select_from(Account)\
|
||||
.join(JournalEntryLineItem, isouter=True).join(JournalEntry)\
|
||||
.filter(Account.is_need_offset,
|
||||
be(JournalEntryLineItem.currency_code == currency.code),
|
||||
JournalEntryLineItem.currency_code == currency.code,
|
||||
JournalEntryLineItem.original_line_item_id.is_(None),
|
||||
sa.or_(sa.and_(Account.base_code.startswith("2"),
|
||||
JournalEntryLineItem.is_debit),
|
||||
|
@ -277,6 +277,7 @@ class JournalEntryLineItemEditor {
|
||||
this.originalLineItemText = originalLineItem.text;
|
||||
this.#originalLineItemText.innerText = originalLineItem.text;
|
||||
this.#setEnableDescriptionAccount(false);
|
||||
if (this.description === null) {
|
||||
if (originalLineItem.description === "") {
|
||||
this.#descriptionControl.classList.remove("accounting-not-empty");
|
||||
} else {
|
||||
@ -284,6 +285,8 @@ class JournalEntryLineItemEditor {
|
||||
}
|
||||
this.description = originalLineItem.description === ""? null: originalLineItem.description;
|
||||
this.#descriptionText.innerText = originalLineItem.description;
|
||||
}
|
||||
this.#setEnableAccount(false);
|
||||
this.#accountControl.classList.add("accounting-not-empty");
|
||||
this.account = originalLineItem.account.copy();
|
||||
this.isAccountConfirmed = false;
|
||||
@ -305,7 +308,7 @@ class JournalEntryLineItemEditor {
|
||||
this.originalLineItemDate = null;
|
||||
this.originalLineItemText = null;
|
||||
this.#originalLineItemText.innerText = "";
|
||||
this.#setEnableDescriptionAccount(true);
|
||||
this.#setEnableAccount(true);
|
||||
this.#accountControl.classList.remove("accounting-not-empty");
|
||||
this.account = null;
|
||||
this.isAccountConfirmed = false;
|
||||
@ -472,12 +475,13 @@ class JournalEntryLineItemEditor {
|
||||
this.originalLineItemDate = null;
|
||||
this.originalLineItemText = null;
|
||||
this.#originalLineItemText.innerText = "";
|
||||
this.#setEnableDescriptionAccount(true);
|
||||
this.#descriptionControl.dataset.bsTarget = `#accounting-description-editor-${this.#debitCreditSubForm.debitCredit}-modal`;
|
||||
this.#descriptionControl.classList.remove("accounting-not-empty");
|
||||
this.#descriptionControl.classList.remove("is-invalid");
|
||||
this.description = null;
|
||||
this.#descriptionText.innerText = ""
|
||||
this.#descriptionError.innerText = ""
|
||||
this.#setEnableAccount(true);
|
||||
this.#accountControl.classList.remove("accounting-not-empty");
|
||||
this.#accountControl.classList.remove("is-invalid");
|
||||
this.account = null;
|
||||
@ -511,7 +515,7 @@ class JournalEntryLineItemEditor {
|
||||
this.#originalLineItemContainer.classList.remove("d-none");
|
||||
this.#originalLineItemControl.classList.add("accounting-not-empty");
|
||||
}
|
||||
this.#setEnableDescriptionAccount(!lineItem.isMatched && this.originalLineItemId === null);
|
||||
this.#descriptionControl.dataset.bsTarget = `#accounting-description-editor-${this.#debitCreditSubForm.debitCredit}-modal`;
|
||||
this.description = lineItem.description;
|
||||
if (this.description === null) {
|
||||
this.#descriptionControl.classList.remove("accounting-not-empty");
|
||||
@ -519,6 +523,7 @@ class JournalEntryLineItemEditor {
|
||||
this.#descriptionControl.classList.add("accounting-not-empty");
|
||||
}
|
||||
this.#descriptionText.innerText = this.description === null? "": this.description;
|
||||
this.#setEnableAccount(!lineItem.isMatched && this.originalLineItemId === null);
|
||||
this.account = lineItem.account;
|
||||
this.isAccountConfirmed = true;
|
||||
if (this.account === null) {
|
||||
@ -547,25 +552,17 @@ class JournalEntryLineItemEditor {
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the enable status of the description and account.
|
||||
* Sets the enable status of the account.
|
||||
*
|
||||
* @param isEnabled {boolean} true to enable, or false otherwise
|
||||
*/
|
||||
#setEnableDescriptionAccount(isEnabled) {
|
||||
#setEnableAccount(isEnabled) {
|
||||
if (isEnabled) {
|
||||
this.#descriptionControl.dataset.bsToggle = "modal";
|
||||
this.#descriptionControl.dataset.bsTarget = `#accounting-description-editor-${this.#debitCreditSubForm.debitCredit}-modal`;
|
||||
this.#descriptionControl.classList.remove("accounting-disabled");
|
||||
this.#descriptionControl.classList.add("accounting-clickable");
|
||||
this.#accountControl.dataset.bsToggle = "modal";
|
||||
this.#accountControl.dataset.bsTarget = `#accounting-account-selector-${this.#debitCreditSubForm.debitCredit}-modal`;
|
||||
this.#accountControl.classList.remove("accounting-disabled");
|
||||
this.#accountControl.classList.add("accounting-clickable");
|
||||
} else {
|
||||
this.#descriptionControl.dataset.bsToggle = "";
|
||||
this.#descriptionControl.dataset.bsTarget = "";
|
||||
this.#descriptionControl.classList.add("accounting-disabled");
|
||||
this.#descriptionControl.classList.remove("accounting-clickable");
|
||||
this.#accountControl.dataset.bsToggle = "";
|
||||
this.#accountControl.dataset.bsTarget = "";
|
||||
this.#accountControl.classList.add("accounting-disabled");
|
||||
|
@ -21,7 +21,7 @@ from accounting.models import Currency
|
||||
from accounting.utils.options import options
|
||||
|
||||
|
||||
def currency_options() -> str:
|
||||
def currency_options() -> list[Currency]:
|
||||
"""Returns the currency options.
|
||||
|
||||
:return: The currency options.
|
||||
|
@ -6,10 +6,10 @@
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: mia-accounting 1.1.1\n"
|
||||
"Project-Id-Version: mia-accounting 1.4.0\n"
|
||||
"Report-Msgid-Bugs-To: imacat@mail.imacat.idv.tw\n"
|
||||
"POT-Creation-Date: 2023-04-09 01:41+0800\n"
|
||||
"PO-Revision-Date: 2023-04-09 01:41+0800\n"
|
||||
"POT-Creation-Date: 2023-04-18 09:32+0800\n"
|
||||
"PO-Revision-Date: 2023-04-18 09:32+0800\n"
|
||||
"Last-Translator: imacat <imacat@mail.imacat.idv.tw>\n"
|
||||
"Language: zh_Hant\n"
|
||||
"Language-Team: zh_Hant <imacat@mail.imacat.idv.tw>\n"
|
||||
@ -20,7 +20,7 @@ msgstr ""
|
||||
"Generated-By: Babel 2.12.1\n"
|
||||
|
||||
#: src/accounting/forms.py:33
|
||||
#: src/accounting/static/js/journal-entry-form.js:1065
|
||||
#: src/accounting/static/js/journal-entry-form.js:1080
|
||||
#: src/accounting/static/js/journal-entry-line-item-editor.js:411
|
||||
#: src/accounting/static/js/option-form.js:537
|
||||
#: src/accounting/static/js/option-form.js:803
|
||||
@ -302,11 +302,11 @@ msgstr "金額不可超過原始分錄凈額 %(balance)s 。"
|
||||
msgid "The amount must not be less than the offset total %(total)s."
|
||||
msgstr "金額不可低於抵銷總額 %(total)s 。"
|
||||
|
||||
#: src/accounting/journal_entry/forms/line_item.py:413
|
||||
#: src/accounting/journal_entry/forms/line_item.py:426
|
||||
msgid "This account is not for debit line items."
|
||||
msgstr "科目不是借方科目。"
|
||||
|
||||
#: src/accounting/journal_entry/forms/line_item.py:465
|
||||
#: src/accounting/journal_entry/forms/line_item.py:478
|
||||
msgid "This account is not for credit line items."
|
||||
msgstr "科目不是貸方科目。"
|
||||
|
||||
@ -354,6 +354,15 @@ msgstr "設定未異動。"
|
||||
msgid "The settings are saved successfully."
|
||||
msgstr "設定存好了。"
|
||||
|
||||
#: src/accounting/report/views.py:401
|
||||
msgid "No more offset to match automatically."
|
||||
msgstr "無法自動配對抵銷。"
|
||||
|
||||
#: src/accounting/report/views.py:408
|
||||
#, python-format
|
||||
msgid "Matched %(matches)s offsets."
|
||||
msgstr "抵銷了 %(matches)s 筆。"
|
||||
|
||||
#: src/accounting/report/period/description.py:33
|
||||
msgid "for all time"
|
||||
msgstr "全部"
|
||||
@ -423,16 +432,16 @@ msgstr "全部"
|
||||
#: 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:55
|
||||
#: src/accounting/templates/accounting/report/balance-sheet.html:59
|
||||
#: src/accounting/templates/accounting/report/balance-sheet.html:71
|
||||
#: src/accounting/templates/accounting/report/balance-sheet.html:81
|
||||
#: src/accounting/templates/accounting/report/balance-sheet.html:65
|
||||
#: src/accounting/templates/accounting/report/balance-sheet.html:77
|
||||
#: src/accounting/templates/accounting/report/balance-sheet.html:87
|
||||
#: src/accounting/templates/accounting/report/balance-sheet.html:96
|
||||
#: src/accounting/templates/accounting/report/balance-sheet.html:103
|
||||
#: src/accounting/templates/accounting/report/balance-sheet.html:93
|
||||
#: src/accounting/templates/accounting/report/balance-sheet.html:102
|
||||
#: src/accounting/templates/accounting/report/balance-sheet.html:109
|
||||
#: src/accounting/templates/accounting/report/income-expenses.html:81
|
||||
#: src/accounting/templates/accounting/report/income-statement.html:83
|
||||
#: src/accounting/templates/accounting/report/income-statement.html:89
|
||||
#: src/accounting/templates/accounting/report/ledger.html:82
|
||||
#: src/accounting/templates/accounting/report/trial-balance.html:74
|
||||
#: src/accounting/templates/accounting/report/trial-balance.html:80
|
||||
msgid "Total"
|
||||
msgstr "合計"
|
||||
|
||||
@ -444,42 +453,47 @@ msgstr "前期轉入"
|
||||
#: src/accounting/report/reports/income_expenses.py:407
|
||||
#: src/accounting/report/reports/journal.py:158
|
||||
#: src/accounting/report/reports/ledger.py:366
|
||||
#: src/accounting/report/reports/unapplied.py:137
|
||||
#: src/accounting/report/reports/unapplied.py:148
|
||||
#: src/accounting/report/reports/unmatched.py:158
|
||||
#: src/accounting/templates/accounting/journal-entry/include/form.html:50
|
||||
#: src/accounting/templates/accounting/report/include/period-chooser.html:111
|
||||
#: src/accounting/templates/accounting/report/income-expenses.html:55
|
||||
#: src/accounting/templates/accounting/report/journal.html:53
|
||||
#: src/accounting/templates/accounting/report/ledger.html:55
|
||||
#: src/accounting/templates/accounting/report/search.html:50
|
||||
#: src/accounting/templates/accounting/report/unapplied.html:50
|
||||
#: src/accounting/templates/accounting/report/unapplied.html:52
|
||||
#: src/accounting/templates/accounting/report/unmatched.html:93
|
||||
msgid "Date"
|
||||
msgstr "日期"
|
||||
|
||||
#: src/accounting/report/reports/income_expenses.py:407
|
||||
#: src/accounting/report/reports/journal.py:159
|
||||
#: src/accounting/report/reports/trial_balance.py:225
|
||||
#: src/accounting/report/reports/unapplied_accounts.py:109
|
||||
#: src/accounting/report/reports/unapplied_accounts.py:122
|
||||
#: src/accounting/report/reports/unmatched_accounts.py:122
|
||||
#: 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/report/include/toolbar-buttons.html:90
|
||||
#: src/accounting/templates/accounting/report/income-expenses.html:56
|
||||
#: src/accounting/templates/accounting/report/journal.html:55
|
||||
#: src/accounting/templates/accounting/report/search.html:52
|
||||
#: src/accounting/templates/accounting/report/trial-balance.html:55
|
||||
#: src/accounting/templates/accounting/report/trial-balance.html:61
|
||||
msgid "Account"
|
||||
msgstr "科目"
|
||||
|
||||
#: src/accounting/report/reports/income_expenses.py:408
|
||||
#: src/accounting/report/reports/journal.py:159
|
||||
#: src/accounting/report/reports/ledger.py:366
|
||||
#: src/accounting/report/reports/unapplied.py:138
|
||||
#: src/accounting/report/reports/unapplied.py:149
|
||||
#: src/accounting/report/reports/unmatched.py:159
|
||||
#: src/accounting/templates/accounting/journal-entry/include/description-editor-modal.html:28
|
||||
#: src/accounting/templates/accounting/journal-entry/include/journal-entry-line-item-editor-modal.html:49
|
||||
#: src/accounting/templates/accounting/report/income-expenses.html:57
|
||||
#: src/accounting/templates/accounting/report/journal.html:56
|
||||
#: src/accounting/templates/accounting/report/ledger.html:56
|
||||
#: src/accounting/templates/accounting/report/search.html:53
|
||||
#: src/accounting/templates/accounting/report/unapplied.html:52
|
||||
#: src/accounting/templates/accounting/report/unapplied.html:53
|
||||
#: src/accounting/templates/accounting/report/unmatched.html:94
|
||||
msgid "Description"
|
||||
msgstr "摘要"
|
||||
|
||||
@ -495,8 +509,10 @@ msgstr "支出"
|
||||
|
||||
#: src/accounting/report/reports/income_expenses.py:409
|
||||
#: src/accounting/report/reports/ledger.py:368
|
||||
#: src/accounting/report/reports/unmatched.py:160
|
||||
#: src/accounting/templates/accounting/report/income-expenses.html:60
|
||||
#: src/accounting/templates/accounting/report/ledger.html:60
|
||||
#: src/accounting/templates/accounting/report/unmatched.html:97
|
||||
msgid "Balance"
|
||||
msgstr "餘額"
|
||||
|
||||
@ -533,64 +549,88 @@ msgid "net income or loss for current period"
|
||||
msgstr "本期損益"
|
||||
|
||||
#: src/accounting/report/reports/income_statement.py:301
|
||||
#: src/accounting/report/reports/unapplied.py:138
|
||||
#: src/accounting/report/reports/unapplied.py:149
|
||||
#: 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/unapplied.html:53
|
||||
#: src/accounting/templates/accounting/report/income-statement.html:61
|
||||
#: src/accounting/templates/accounting/report/unapplied.html:54
|
||||
msgid "Amount"
|
||||
msgstr "金額"
|
||||
|
||||
#: src/accounting/report/reports/journal.py:158
|
||||
#: src/accounting/report/reports/unapplied.py:137
|
||||
#: src/accounting/report/reports/unapplied.py:148
|
||||
#: src/accounting/report/reports/unmatched.py:158
|
||||
#: src/accounting/templates/accounting/journal-entry/include/form-currency.html:33
|
||||
#: src/accounting/templates/accounting/report/include/toolbar-buttons.html:73
|
||||
#: src/accounting/templates/accounting/report/journal.html:54
|
||||
#: src/accounting/templates/accounting/report/search.html:51
|
||||
#: src/accounting/templates/accounting/report/unapplied.html:51
|
||||
msgid "Currency"
|
||||
msgstr "貨幣"
|
||||
|
||||
#: src/accounting/report/reports/journal.py:160
|
||||
#: src/accounting/report/reports/ledger.py:367
|
||||
#: src/accounting/report/reports/trial_balance.py:225
|
||||
#: src/accounting/report/reports/unmatched.py:159
|
||||
#: 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/report/journal.html:57
|
||||
#: src/accounting/templates/accounting/report/ledger.html:57
|
||||
#: src/accounting/templates/accounting/report/search.html:54
|
||||
#: src/accounting/templates/accounting/report/trial-balance.html:56
|
||||
#: src/accounting/templates/accounting/report/trial-balance.html:62
|
||||
#: src/accounting/templates/accounting/report/unmatched.html:95
|
||||
msgid "Debit"
|
||||
msgstr "借方"
|
||||
|
||||
#: src/accounting/report/reports/journal.py:160
|
||||
#: src/accounting/report/reports/ledger.py:367
|
||||
#: src/accounting/report/reports/trial_balance.py:226
|
||||
#: src/accounting/report/reports/unmatched.py:160
|
||||
#: 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/report/journal.html:58
|
||||
#: src/accounting/templates/accounting/report/ledger.html:58
|
||||
#: src/accounting/templates/accounting/report/search.html:55
|
||||
#: src/accounting/templates/accounting/report/trial-balance.html:57
|
||||
#: src/accounting/templates/accounting/report/trial-balance.html:63
|
||||
#: src/accounting/templates/accounting/report/unmatched.html:96
|
||||
msgid "Credit"
|
||||
msgstr "貸方"
|
||||
|
||||
#: src/accounting/report/reports/unapplied.py:121
|
||||
#: src/accounting/report/reports/unapplied_accounts.py:93
|
||||
#: src/accounting/report/reports/unapplied.py:132
|
||||
#: src/accounting/report/reports/unapplied_accounts.py:107
|
||||
#: src/accounting/report/reports/unmatched.py:142
|
||||
#: src/accounting/report/reports/unmatched_accounts.py:107
|
||||
#: src/accounting/templates/accounting/include/nav.html:39
|
||||
msgid "Accounts"
|
||||
msgstr "科目"
|
||||
|
||||
#: src/accounting/report/reports/unapplied.py:139
|
||||
#: src/accounting/templates/accounting/report/unapplied.html:54
|
||||
#: src/accounting/report/reports/unapplied.py:150
|
||||
#: src/accounting/templates/accounting/report/unapplied.html:55
|
||||
msgid "Net Balance"
|
||||
msgstr "淨額"
|
||||
|
||||
#: src/accounting/report/reports/unapplied_accounts.py:109
|
||||
#: src/accounting/templates/accounting/report/unapplied-accounts.html:47
|
||||
#: src/accounting/report/reports/unapplied_accounts.py:122
|
||||
#: src/accounting/report/reports/unmatched_accounts.py:122
|
||||
#: src/accounting/templates/accounting/report/unapplied-accounts.html:59
|
||||
#: src/accounting/templates/accounting/report/unmatched-accounts.html:59
|
||||
msgid "Count"
|
||||
msgstr "數量"
|
||||
|
||||
#: src/accounting/report/utils/report_chooser.py:82
|
||||
#: src/accounting/report/utils/offset_matcher.py:163
|
||||
msgid "There is no unmatched offset."
|
||||
msgstr "沒有遺漏的抵銷分錄"
|
||||
|
||||
#: src/accounting/report/utils/offset_matcher.py:167
|
||||
#, python-format
|
||||
msgid "%(total)s unmatched offsets without original items."
|
||||
msgstr "%(total)s 筆遺漏的抵銷分錄無法自動抵銷。"
|
||||
|
||||
#: src/accounting/report/utils/offset_matcher.py:172
|
||||
#, python-format
|
||||
msgid ""
|
||||
"%(matches)s unmatched offsets out of %(total)s can match with their "
|
||||
"original items."
|
||||
msgstr "%(total)s 筆遺漏的抵銷分錄中,可配對抵銷掉 %(matches)s 筆。"
|
||||
|
||||
#: src/accounting/report/utils/report_chooser.py:86
|
||||
#: src/accounting/templates/accounting/account/include/form.html:98
|
||||
#: src/accounting/templates/accounting/account/list.html:40
|
||||
#: src/accounting/templates/accounting/base-account/list.html:34
|
||||
@ -606,114 +646,119 @@ msgstr "數量"
|
||||
msgid "Search"
|
||||
msgstr "搜尋"
|
||||
|
||||
#: src/accounting/report/utils/report_chooser.py:93
|
||||
#: src/accounting/report/utils/report_chooser.py:97
|
||||
msgid "Income and Expenses Log"
|
||||
msgstr "收支帳"
|
||||
|
||||
#: src/accounting/report/utils/report_chooser.py:106
|
||||
#: src/accounting/report/utils/report_chooser.py:110
|
||||
msgid "Ledger"
|
||||
msgstr "分類帳"
|
||||
|
||||
#: src/accounting/report/utils/report_chooser.py:118
|
||||
#: src/accounting/report/utils/report_chooser.py:122
|
||||
msgid "Journal"
|
||||
msgstr "日記簿"
|
||||
|
||||
#: src/accounting/report/utils/report_chooser.py:128
|
||||
#: src/accounting/report/utils/report_chooser.py:132
|
||||
msgid "Trial Balance"
|
||||
msgstr "試算表"
|
||||
|
||||
#: src/accounting/report/utils/report_chooser.py:139
|
||||
#: src/accounting/report/utils/report_chooser.py:143
|
||||
msgid "Income Statement"
|
||||
msgstr "損益表"
|
||||
|
||||
#: src/accounting/report/utils/report_chooser.py:150
|
||||
#: src/accounting/report/utils/report_chooser.py:154
|
||||
msgid "Balance Sheet"
|
||||
msgstr "資產負債表"
|
||||
|
||||
#: src/accounting/report/utils/report_chooser.py:163
|
||||
#: src/accounting/report/utils/report_chooser.py:167
|
||||
msgid "Unapplied Original Line Items"
|
||||
msgstr "未抵銷原始分錄"
|
||||
#: src/accounting/report/utils/report_chooser.py:171
|
||||
msgid "Unapplied Items"
|
||||
msgstr "未抵銷項目"
|
||||
|
||||
#: src/accounting/report/utils/report_chooser.py:184
|
||||
#: src/accounting/report/utils/report_chooser.py:188
|
||||
msgid "Unmatched Offsets"
|
||||
msgstr "遺漏的抵銷項目"
|
||||
|
||||
#: src/accounting/static/js/account-form.js:206
|
||||
msgid "Please fill in the title."
|
||||
msgstr "請填上標題。"
|
||||
|
||||
#: src/accounting/static/js/description-editor.js:951
|
||||
#: src/accounting/static/js/description-editor.js:1129
|
||||
#: src/accounting/static/js/description-editor.js:952
|
||||
#: src/accounting/static/js/description-editor.js:1130
|
||||
msgid "Please fill in the tag."
|
||||
msgstr "請填上標籤。"
|
||||
|
||||
#: src/accounting/static/js/description-editor.js:961
|
||||
#: src/accounting/static/js/description-editor.js:1149
|
||||
#: src/accounting/static/js/description-editor.js:962
|
||||
#: src/accounting/static/js/description-editor.js:1150
|
||||
msgid "Please fill in the origin."
|
||||
msgstr "請填上起點。"
|
||||
|
||||
#: src/accounting/static/js/description-editor.js:971
|
||||
#: src/accounting/static/js/description-editor.js:1159
|
||||
#: src/accounting/static/js/description-editor.js:972
|
||||
#: src/accounting/static/js/description-editor.js:1160
|
||||
msgid "Please fill in the destination."
|
||||
msgstr "請填上終點。"
|
||||
|
||||
#: src/accounting/static/js/description-editor.js:1139
|
||||
#: src/accounting/static/js/description-editor.js:1140
|
||||
msgid "Please fill in the route."
|
||||
msgstr "請填上路線名稱。"
|
||||
|
||||
#: src/accounting/static/js/description-editor.js:1192
|
||||
#: src/accounting/static/js/description-editor.js:1193
|
||||
msgid "January"
|
||||
msgstr "一月"
|
||||
|
||||
#: src/accounting/static/js/description-editor.js:1192
|
||||
#: src/accounting/static/js/description-editor.js:1193
|
||||
msgid "February"
|
||||
msgstr "二月"
|
||||
|
||||
#: src/accounting/static/js/description-editor.js:1192
|
||||
#: src/accounting/static/js/description-editor.js:1193
|
||||
msgid "March"
|
||||
msgstr "三月"
|
||||
|
||||
#: src/accounting/static/js/description-editor.js:1192
|
||||
#: src/accounting/static/js/description-editor.js:1193
|
||||
msgid "April"
|
||||
msgstr "四月"
|
||||
|
||||
#: src/accounting/static/js/description-editor.js:1193
|
||||
#: src/accounting/static/js/description-editor.js:1194
|
||||
msgid "May"
|
||||
msgstr "五月"
|
||||
|
||||
#: src/accounting/static/js/description-editor.js:1193
|
||||
#: src/accounting/static/js/description-editor.js:1194
|
||||
msgid "June"
|
||||
msgstr "六月"
|
||||
|
||||
#: src/accounting/static/js/description-editor.js:1193
|
||||
#: src/accounting/static/js/description-editor.js:1194
|
||||
msgid "July"
|
||||
msgstr "七月"
|
||||
|
||||
#: src/accounting/static/js/description-editor.js:1193
|
||||
#: src/accounting/static/js/description-editor.js:1194
|
||||
msgid "August"
|
||||
msgstr "八月"
|
||||
|
||||
#: src/accounting/static/js/description-editor.js:1194
|
||||
#: src/accounting/static/js/description-editor.js:1195
|
||||
msgid "September"
|
||||
msgstr "九月"
|
||||
|
||||
#: src/accounting/static/js/description-editor.js:1194
|
||||
#: src/accounting/static/js/description-editor.js:1195
|
||||
msgid "October"
|
||||
msgstr "十月"
|
||||
|
||||
#: src/accounting/static/js/description-editor.js:1194
|
||||
#: src/accounting/static/js/description-editor.js:1195
|
||||
msgid "November"
|
||||
msgstr "十一月"
|
||||
|
||||
#: src/accounting/static/js/description-editor.js:1194
|
||||
#: src/accounting/static/js/description-editor.js:1195
|
||||
msgid "December"
|
||||
msgstr "十二月"
|
||||
|
||||
#: src/accounting/static/js/journal-entry-form.js:1070
|
||||
#: src/accounting/static/js/journal-entry-form.js:1085
|
||||
#: src/accounting/static/js/journal-entry-line-item-editor.js:430
|
||||
msgid "Please fill in the amount."
|
||||
msgstr "請填上金額。"
|
||||
|
||||
#: 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/form-line-item.html:38
|
||||
#: src/accounting/static/js/journal-entry-form.js:1107
|
||||
#: src/accounting/templates/accounting/journal-entry/include/detail-line-items.html:37
|
||||
#: src/accounting/templates/accounting/journal-entry/include/form-line-item.html:41
|
||||
#, python-format
|
||||
msgid "Offset %(item)s"
|
||||
msgstr "抵銷 %(item)s"
|
||||
@ -756,7 +801,6 @@ msgstr "新增科目"
|
||||
#: src/accounting/templates/accounting/journal-entry/include/form.html:38
|
||||
#: src/accounting/templates/accounting/journal-entry/order.html:36
|
||||
#: src/accounting/templates/accounting/option/form.html:36
|
||||
#: src/accounting/templates/accounting/unmatched-offset/list.html:31
|
||||
msgid "Back"
|
||||
msgstr "回上頁"
|
||||
|
||||
@ -797,7 +841,7 @@ msgstr "確認刪除科目"
|
||||
#: src/accounting/templates/accounting/option/include/recurring-item-editor-modal.html:28
|
||||
#: src/accounting/templates/accounting/report/include/period-chooser.html:27
|
||||
#: src/accounting/templates/accounting/report/include/search-modal.html:28
|
||||
#: src/accounting/templates/accounting/unmatched-offset/list.html:54
|
||||
#: src/accounting/templates/accounting/report/unmatched.html:58
|
||||
msgid "Close"
|
||||
msgstr "關閉"
|
||||
|
||||
@ -815,7 +859,7 @@ msgstr "你確定要刪掉這個科目嗎?"
|
||||
#: src/accounting/templates/accounting/option/include/recurring-account-selector-modal.html:48
|
||||
#: src/accounting/templates/accounting/option/include/recurring-item-editor-modal.html:65
|
||||
#: src/accounting/templates/accounting/report/include/search-modal.html:37
|
||||
#: src/accounting/templates/accounting/unmatched-offset/list.html:70
|
||||
#: src/accounting/templates/accounting/report/unmatched.html:74
|
||||
msgid "Cancel"
|
||||
msgstr "取消"
|
||||
|
||||
@ -823,7 +867,7 @@ msgstr "取消"
|
||||
#: src/accounting/templates/accounting/currency/detail.html:80
|
||||
#: src/accounting/templates/accounting/journal-entry/include/detail.html:85
|
||||
#: src/accounting/templates/accounting/report/include/period-chooser.html:141
|
||||
#: src/accounting/templates/accounting/unmatched-offset/list.html:71
|
||||
#: src/accounting/templates/accounting/report/unmatched.html:75
|
||||
msgid "Confirm"
|
||||
msgstr "確定"
|
||||
|
||||
@ -871,22 +915,22 @@ msgstr "新增"
|
||||
#: src/accounting/templates/accounting/base-account/list.html:51
|
||||
#: src/accounting/templates/accounting/currency/list.html:65
|
||||
#: 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:58
|
||||
#: 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/report/balance-sheet.html:110
|
||||
#: src/accounting/templates/accounting/report/balance-sheet.html:116
|
||||
#: src/accounting/templates/accounting/report/income-expenses.html:113
|
||||
#: src/accounting/templates/accounting/report/income-statement.html:96
|
||||
#: src/accounting/templates/accounting/report/income-statement.html:102
|
||||
#: src/accounting/templates/accounting/report/journal.html:103
|
||||
#: src/accounting/templates/accounting/report/ledger.html:116
|
||||
#: src/accounting/templates/accounting/report/search.html:100
|
||||
#: src/accounting/templates/accounting/report/trial-balance.html:82
|
||||
#: src/accounting/templates/accounting/report/unapplied-accounts.html:61
|
||||
#: src/accounting/templates/accounting/report/unapplied.html:98
|
||||
#: src/accounting/templates/accounting/unmatched-offset/dashboard.html:37
|
||||
#: src/accounting/templates/accounting/unmatched-offset/list.html:104
|
||||
#: src/accounting/templates/accounting/report/trial-balance.html:88
|
||||
#: src/accounting/templates/accounting/report/unapplied-accounts.html:76
|
||||
#: src/accounting/templates/accounting/report/unapplied.html:90
|
||||
#: src/accounting/templates/accounting/report/unmatched-accounts.html:76
|
||||
#: src/accounting/templates/accounting/report/unmatched.html:147
|
||||
msgid "There is no data."
|
||||
msgstr "沒有資料。"
|
||||
|
||||
@ -984,12 +1028,7 @@ msgstr "基本科目"
|
||||
msgid "Currencies"
|
||||
msgstr "貨幣"
|
||||
|
||||
#: src/accounting/templates/accounting/include/nav.html:57
|
||||
#: src/accounting/templates/accounting/unmatched-offset/dashboard.html:24
|
||||
msgid "Unmatched Offsets"
|
||||
msgstr "遺漏的抵銷分錄"
|
||||
|
||||
#: src/accounting/templates/accounting/include/nav.html:64
|
||||
#: src/accounting/templates/accounting/include/nav.html:58
|
||||
#: src/accounting/templates/accounting/option/detail.html:24
|
||||
#: src/accounting/templates/accounting/option/detail.html:41
|
||||
#: src/accounting/templates/accounting/option/form.html:29
|
||||
@ -1088,23 +1127,23 @@ msgstr "路線"
|
||||
msgid "The Number of Items"
|
||||
msgstr "數量"
|
||||
|
||||
#: src/accounting/templates/accounting/journal-entry/include/detail-line-items.html:42
|
||||
#: src/accounting/templates/accounting/journal-entry/include/form-line-item.html:43
|
||||
#: src/accounting/templates/accounting/journal-entry/include/detail-line-items.html:45
|
||||
#: src/accounting/templates/accounting/journal-entry/include/form-line-item.html:46
|
||||
msgid "Offsets"
|
||||
msgstr "抵銷"
|
||||
|
||||
#: src/accounting/templates/accounting/journal-entry/include/detail-line-items.html:55
|
||||
#: src/accounting/templates/accounting/journal-entry/include/form-line-item.html:54
|
||||
#: src/accounting/templates/accounting/journal-entry/include/detail-line-items.html:58
|
||||
#: src/accounting/templates/accounting/journal-entry/include/form-line-item.html:57
|
||||
msgid "Net balance"
|
||||
msgstr "淨額"
|
||||
|
||||
#: src/accounting/templates/accounting/journal-entry/include/detail-line-items.html:60
|
||||
#: src/accounting/templates/accounting/journal-entry/include/form-line-item.html:51
|
||||
#: src/accounting/templates/accounting/journal-entry/include/detail-line-items.html:63
|
||||
#: src/accounting/templates/accounting/journal-entry/include/form-line-item.html:54
|
||||
msgid "Fully offset"
|
||||
msgstr "全部抵銷"
|
||||
|
||||
#: src/accounting/templates/accounting/journal-entry/include/detail-line-items.html:65
|
||||
#: src/accounting/templates/accounting/journal-entry/include/form-line-item.html:59
|
||||
#: src/accounting/templates/accounting/journal-entry/include/detail-line-items.html:68
|
||||
#: src/accounting/templates/accounting/journal-entry/include/form-line-item.html:62
|
||||
msgid "Unmatched"
|
||||
msgstr "未抵銷"
|
||||
|
||||
@ -1217,18 +1256,35 @@ msgid "Water bill for {last_bimonthly_name}"
|
||||
msgstr "水費{last_bimonthly_number}月"
|
||||
|
||||
#: src/accounting/templates/accounting/report/balance-sheet.html:29
|
||||
#: src/accounting/templates/accounting/report/balance-sheet.html:49
|
||||
#: src/accounting/templates/accounting/report/balance-sheet.html:51
|
||||
#, python-format
|
||||
msgid "Balance Sheet %(period)s"
|
||||
msgstr "%(period)s資產負債表"
|
||||
|
||||
#: src/accounting/templates/accounting/report/balance-sheet.html:29
|
||||
#: src/accounting/templates/accounting/report/balance-sheet.html:53
|
||||
#, python-format
|
||||
msgid "Balance Sheet of %(currency)s %(period)s"
|
||||
msgstr "%(period)s%(currency)s資產負債表"
|
||||
|
||||
#: src/accounting/templates/accounting/report/income-expenses.html:29
|
||||
#, python-format
|
||||
msgid "Income and Expenses Log of %(account)s %(period)s"
|
||||
msgstr "%(period)s%(account)s收支帳"
|
||||
|
||||
#: src/accounting/templates/accounting/report/income-expenses.html:29
|
||||
#, python-format
|
||||
msgid "Income and Expenses Log of %(account)s in %(currency)s %(period)s"
|
||||
msgstr "%(period)s%(currency)s%(account)s收支帳"
|
||||
|
||||
#: src/accounting/templates/accounting/report/income-statement.html:29
|
||||
#: src/accounting/templates/accounting/report/income-statement.html:49
|
||||
#: src/accounting/templates/accounting/report/income-statement.html:51
|
||||
#, python-format
|
||||
msgid "Income Statement %(period)s"
|
||||
msgstr "%(period)s損益表"
|
||||
|
||||
#: src/accounting/templates/accounting/report/income-statement.html:29
|
||||
#: src/accounting/templates/accounting/report/income-statement.html:53
|
||||
#, python-format
|
||||
msgid "Income Statement of %(currency)s %(period)s"
|
||||
msgstr "%(period)s%(currency)s損益表"
|
||||
@ -1238,31 +1294,89 @@ msgstr "%(period)s%(currency)s損益表"
|
||||
msgid "Journal %(period)s"
|
||||
msgstr "%(period)s日記簿"
|
||||
|
||||
#: src/accounting/templates/accounting/report/ledger.html:29
|
||||
#, python-format
|
||||
msgid "Ledger of %(account)s %(period)s"
|
||||
msgstr "%(period)s%(account)s分類帳"
|
||||
|
||||
#: src/accounting/templates/accounting/report/ledger.html:29
|
||||
#, python-format
|
||||
msgid "Ledger of %(account)s in %(currency)s %(period)s"
|
||||
msgstr "%(period)s%(currency)s%(account)s分類帳"
|
||||
|
||||
#: src/accounting/templates/accounting/report/trial-balance.html:29
|
||||
#: src/accounting/templates/accounting/report/trial-balance.html:49
|
||||
#: src/accounting/templates/accounting/report/trial-balance.html:51
|
||||
#, python-format
|
||||
msgid "Trial Balance %(period)s"
|
||||
msgstr "%(period)s試算表"
|
||||
|
||||
#: src/accounting/templates/accounting/report/trial-balance.html:29
|
||||
#: src/accounting/templates/accounting/report/trial-balance.html:53
|
||||
#, python-format
|
||||
msgid "Trial Balance of %(currency)s %(period)s"
|
||||
msgstr "%(period)s%(currency)s試算表"
|
||||
|
||||
#: src/accounting/templates/accounting/report/unapplied-accounts.html:24
|
||||
#: src/accounting/templates/accounting/report/unapplied-accounts.html:41
|
||||
msgid "Accounts with Unapplied Original Line Items"
|
||||
msgstr "有未抵銷原始分錄的科目"
|
||||
#: src/accounting/templates/accounting/report/unapplied-accounts.html:29
|
||||
#: src/accounting/templates/accounting/report/unapplied-accounts.html:49
|
||||
msgid "Accounts with Unapplied Items"
|
||||
msgstr "含未抵銷項目的科目"
|
||||
|
||||
#: src/accounting/templates/accounting/report/unapplied.html:28
|
||||
#: src/accounting/templates/accounting/report/unapplied-accounts.html:29
|
||||
#: src/accounting/templates/accounting/report/unapplied-accounts.html:51
|
||||
#, python-format
|
||||
msgid "Unapplied Original Line Items of %(account)s"
|
||||
msgstr "%(account)s未抵銷原始分錄"
|
||||
msgid "Accounts with Unapplied Items in %(currency)s"
|
||||
msgstr "%(currency)s含未抵銷項目的科目"
|
||||
|
||||
#: src/accounting/templates/accounting/report/unapplied.html:65
|
||||
#: src/accounting/templates/accounting/report/unapplied.html:29
|
||||
#, python-format
|
||||
msgid "Can match %(offset)s"
|
||||
msgstr "可抵銷 %(offset)s"
|
||||
msgid "Unapplied Items of %(account)s"
|
||||
msgstr "%(account)s未抵銷項目"
|
||||
|
||||
#: src/accounting/templates/accounting/report/unapplied.html:29
|
||||
#, python-format
|
||||
msgid "Unapplied Items of %(account)s in %(currency)s"
|
||||
msgstr "%(currency)s%(account)s未抵銷項目"
|
||||
|
||||
#: src/accounting/templates/accounting/report/unmatched-accounts.html:29
|
||||
#: src/accounting/templates/accounting/report/unmatched-accounts.html:49
|
||||
msgid "Accounts with Unmatched Offsets"
|
||||
msgstr "含遺漏抵銷項目的科目"
|
||||
|
||||
#: src/accounting/templates/accounting/report/unmatched-accounts.html:29
|
||||
#: src/accounting/templates/accounting/report/unmatched-accounts.html:51
|
||||
#, python-format
|
||||
msgid "Accounts with Unmatched Offsets in %(currency)s"
|
||||
msgstr "%(currency)s含遺漏抵銷項目的科目"
|
||||
|
||||
#: src/accounting/templates/accounting/report/unmatched.html:29
|
||||
#, python-format
|
||||
msgid "Unmatched Offsets of %(account)s"
|
||||
msgstr "%(account)s遺漏的抵銷項目"
|
||||
|
||||
#: src/accounting/templates/accounting/report/unmatched.html:29
|
||||
#, python-format
|
||||
msgid "Unmatched Offsets of %(account)s in %(currency)s"
|
||||
msgstr "%(currency)s%(account)s遺漏的抵銷項目"
|
||||
|
||||
#: src/accounting/templates/accounting/report/unmatched.html:47
|
||||
msgid "Match"
|
||||
msgstr "抵銷"
|
||||
|
||||
#: src/accounting/templates/accounting/report/unmatched.html:57
|
||||
msgid "Confirm Match Offsets"
|
||||
msgstr "確認抵銷"
|
||||
|
||||
#: src/accounting/templates/accounting/report/unmatched.html:61
|
||||
msgid ""
|
||||
"Do you really want to match the following original line items with their "
|
||||
"offsets? This cannot be undone. Please backup your database first, and "
|
||||
"review before you confirm."
|
||||
msgstr "你確定要抵銷下列原始分錄與抵銷分錄嗎?結果無法復原。請先備份資料庫,並仔細核對抵銷分錄是否正確。"
|
||||
|
||||
#: src/accounting/templates/accounting/report/unmatched.html:107
|
||||
#, python-format
|
||||
msgid "Can match %(item)s"
|
||||
msgstr "可抵銷 %(item)s"
|
||||
|
||||
#: src/accounting/templates/accounting/report/include/period-chooser.html:26
|
||||
msgid "Period Chooser"
|
||||
@ -1297,65 +1411,6 @@ msgstr "期間"
|
||||
msgid "Download"
|
||||
msgstr "下載"
|
||||
|
||||
#: src/accounting/templates/accounting/unmatched-offset/list.html:24
|
||||
#, python-format
|
||||
msgid "Unmatched Offsets in %(account)s"
|
||||
msgstr "%(account)s遺漏的抵銷分錄"
|
||||
|
||||
#: src/accounting/templates/accounting/unmatched-offset/list.html:28
|
||||
msgid "Toolbar"
|
||||
msgstr "工具列"
|
||||
|
||||
#: src/accounting/templates/accounting/unmatched-offset/list.html:36
|
||||
#: src/accounting/templates/accounting/unmatched-offset/list.html:41
|
||||
msgid "Match"
|
||||
msgstr "抵銷"
|
||||
|
||||
#: src/accounting/templates/accounting/unmatched-offset/list.html:53
|
||||
msgid "Confirm Match Offsets"
|
||||
msgstr "確認抵銷"
|
||||
|
||||
#: src/accounting/templates/accounting/unmatched-offset/list.html:57
|
||||
msgid ""
|
||||
"Do you really want to match the following original line items with their "
|
||||
"offsets? This cannot be undone. Please backup your database first, and "
|
||||
"review before you confirm."
|
||||
msgstr "你確定要抵銷下列原始分錄與抵銷分錄嗎?結果無法復原。請先備份資料庫,並仔細核對抵銷分錄是否正確。"
|
||||
|
||||
#: src/accounting/templates/accounting/unmatched-offset/list.html:81
|
||||
#, python-format
|
||||
msgid ""
|
||||
"%(matches)s unapplied original line items out of %(total)s can match with"
|
||||
" their offsets."
|
||||
msgstr "%(total)s 筆未抵銷原始分錄中,可配對抵銷掉 %(matches)s 筆。"
|
||||
|
||||
#: src/accounting/templates/accounting/unmatched-offset/list.html:83
|
||||
#, python-format
|
||||
msgid "%(total)s unapplied original line items without matching offsets."
|
||||
msgstr "%(total)s 筆未抵銷原始分錄,無法自動配對抵銷。"
|
||||
|
||||
#: src/accounting/templates/accounting/unmatched-offset/list.html:85
|
||||
msgid "Go to unapplied original line items."
|
||||
msgstr "查閱未抵銷原始分錄。"
|
||||
|
||||
#: src/accounting/templates/accounting/unmatched-offset/list.html:87
|
||||
msgid "All original line items are fully offset."
|
||||
msgstr "原始分錄已全部抵銷。"
|
||||
|
||||
#: src/accounting/templates/accounting/unmatched-offset/list.html:98
|
||||
#, python-format
|
||||
msgid "Can match %(item)s"
|
||||
msgstr "可抵銷 %(item)s"
|
||||
|
||||
#: src/accounting/unmatched_offset/views.py:71
|
||||
msgid "No more offset to match automatically."
|
||||
msgstr "無法自動配對抵銷。"
|
||||
|
||||
#: src/accounting/unmatched_offset/views.py:77
|
||||
#, python-format
|
||||
msgid "Matches %(matches)s from %(total)s unapplied line items."
|
||||
msgstr "%(total)s 筆未抵銷原始分錄中,配對抵銷掉 %(matches)s 筆。"
|
||||
|
||||
#: src/accounting/utils/current_account.py:65
|
||||
msgid "current assets and liabilities"
|
||||
msgstr "流動資產與負債"
|
||||
|
@ -25,16 +25,6 @@ import typing as t
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
def be(expression: t.Any) -> sa.BinaryExpression:
|
||||
"""Casts the SQLAlchemy binary expression to the binary expression type.
|
||||
|
||||
:param expression: The binary expression.
|
||||
:return: The binary expression itself.
|
||||
"""
|
||||
assert isinstance(expression, sa.BinaryExpression)
|
||||
return expression
|
||||
|
||||
|
||||
def s(message: t.Any) -> str:
|
||||
"""Casts the LazyString message to the string type.
|
||||
|
||||
|
Reference in New Issue
Block a user