15 Commits

Author SHA1 Message Date
3a0e978f76 Removed an unused import from the "accounting.journal_entry.forms.line_item" module. 2023-04-08 00:44:13 +08:00
8c10d42d7b Added documentation to the currency and account parameters of the CSVRow class, and the pagination parameter of the PageParams class in the "accounting.report.reports.journal" module. 2023-04-08 00:44:13 +08:00
04ec51afbe Changed the "offsets" relationship to a pseudo property, to apply the correct but complex ordering rules. 2023-04-07 16:04:54 +08:00
fe7a8842ce Fixed the query in the JournalEntryConverter converter. 2023-04-07 15:31:06 +08:00
66daa5c42c Fixed the query in the KeepAccountWhenHavingOffset validator. 2023-04-07 15:29:17 +08:00
27fb44937d Fixed the incorrect query in the "offsets" pseudo property of the LineItemForm form. 2023-04-07 15:11:04 +08:00
7026ed3a65 Fixed the order of the items in the "offsets" pseudo property of the LineItemForm form. 2023-04-07 15:01:22 +08:00
fdd3e93778 Fixed the net balance in the line items in the journal entry detail. 2023-04-07 14:57:24 +08:00
def7559457 Fixed the #filterOptions in the JavaScript JournalEntryAccountSelector to show the "more" option when there is no matches, but it is not showing all the accounts. 2023-04-07 12:34:24 +08:00
7905820d68 Revised the imports in the "accounting.base_account.views" and "accounting.currency.views" modules. 2023-04-06 16:09:36 +08:00
7ae332c975 Moved the "Test Site and Live Demonstration" section to the front of the documentation. 2023-04-06 10:00:24 +08:00
86c5b91697 Advanced to version 1.0.1. 2023-04-06 08:43:14 +08:00
9168840e64 Fixed an error in the example configuration. 2023-04-06 08:38:39 +08:00
21b9cfa8b8 Revised the documentation. 2023-04-06 08:31:19 +08:00
b0b3b3acb1 Moved the history section out from README.rst and intro.rst, to the new history.rst. 2023-04-06 08:21:32 +08:00
15 changed files with 146 additions and 185 deletions

View File

@ -17,47 +17,6 @@ accounting reports:
In addition, *Mia! Accounting* tracks offsets for unpaid payables and In addition, *Mia! Accounting* tracks offsets for unpaid payables and
receivables. 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 Installation
============ ============
@ -72,6 +31,18 @@ You may also download the from the `PyPI project page`_ or the
`release page`_ on the `Git repository`_. `release page`_ on the `Git repository`_.
Test Site and Live Demonstration
================================
You may find a working example in the `test site`_ in the
`source distribution`_. It is the simplest website that works with
*Mia! Accounting*. It is used in the automatic tests. It is the same
code run for `live demonstration`_.
If you do not have a running Flask application or do not know how to
start one, you may start with the test site.
Prerequisites Prerequisites
============= =============
@ -91,7 +62,7 @@ Configuration
============= =============
You need to pass the Flask *app* and an implementation of You need to pass the Flask *app* and an implementation of
``UserUtilityInterface`` to the ``init_app`` function. `UserUtilityInterface`_ to the `init_app`_ function.
``UserUtilityInterface`` contains everything *Mia! Accounting* needs. ``UserUtilityInterface`` contains everything *Mia! Accounting* needs.
The following is an example configuration for *Mia! Accounting*. The following is an example configuration for *Mia! Accounting*.
@ -109,7 +80,7 @@ The following is an example configuration for *Mia! Accounting*.
import accounting import accounting
class UserUtilities(accounting.UserUtilityInterface[User]): class UserUtils(accounting.UserUtilityInterface[User]):
def can_view(self) -> bool: def can_view(self) -> bool:
return True return True
@ -161,8 +132,8 @@ database tables that *Mia! Accounting* uses.
* ``accounting-init-accounts`` * ``accounting-init-accounts``
* ``accounting-init-currencies`` * ``accounting-init-currencies``
You need to run ``accounting-init-base`` first, and then the other After database tables are created, run
two commands. ``accounting-init-base`` first, and then the other two commands.
:: ::
@ -196,18 +167,6 @@ base template:
Check your Flask application and see how it works. 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 Documentation
============= =============
@ -242,29 +201,19 @@ Authors
.. _Flask: https://flask.palletsprojects.com .. _Flask: https://flask.palletsprojects.com
.. _double-entry bookkeeping: https://en.wikipedia.org/wiki/Double-entry_bookkeeping .. _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 .. _test site: https://github.com/imacat/mia-accounting/tree/main/tests/test_site
.. _source distribution: https://pypi.org/project/mia-accounting/#files .. _source distribution: https://pypi.org/project/mia-accounting/#files
.. _live demonstration: https://accounting.imacat.idv.tw .. _live demonstration: https://accounting.imacat.idv.tw
.. _PyPI project page: https://pypi.org/project/mia-accounting
.. _release page: https://github.com/imacat/mia-accounting/releases
.. _Git repository: https://github.com/imacat/mia-accounting
.. _CDN: https://en.wikipedia.org/wiki/Content_delivery_network
.. _Bootstrap: https://getbootstrap.com
.. _FontAwesome: https://fontawesome.com
.. _Decimal.js: https://mikemcl.github.io/decimal.js
.. _Tempus-Dominus: https://getdatepicker.com
.. _UserUtilityInterface: https://mia-accounting.readthedocs.io/en/latest/accounting.utils.html#accounting.utils.user.UserUtilityInterface
.. _init_app: https://mia-accounting.readthedocs.io/en/latest/accounting.html#accounting.init_app
.. _flask_sqlalchemy.SQLAlchemy.create_all: https://flask-sqlalchemy.palletsprojects.com/en/3.0.x/api/#flask_sqlalchemy.SQLAlchemy.create_all
.. _Bootstrap navigation bar: https://getbootstrap.com/docs/5.3/components/navbar/
.. _documentation on Read the Docs: https://mia-accounting.readthedocs.io .. _documentation on Read the Docs: https://mia-accounting.readthedocs.io

View File

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

View File

@ -22,7 +22,7 @@ The following is an example configuration for *Mia! Accounting*.
import accounting import accounting
class UserUtilities(accounting.UserUtilityInterface[User]): class UserUtils(accounting.UserUtilityInterface[User]):
def can_view(self) -> bool: def can_view(self) -> bool:
return True return True

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

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

View File

@ -13,6 +13,7 @@ Welcome to Mia! Accounting's documentation!
intro intro
accounting accounting
examples examples
history

View File

@ -12,47 +12,6 @@ accounting reports:
In addition, *Mia! Accounting* tracks offsets for unpaid payables and In addition, *Mia! Accounting* tracks offsets for unpaid payables and
receivables. 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 Installation
------------ ------------
@ -67,6 +26,18 @@ You may also download the from the `PyPI project page`_ or the
`release page`_ on the `Git repository`_. `release page`_ on the `Git repository`_.
Test Site and Live Demonstration
--------------------------------
You may find a working example in the `test site`_ in the
`source distribution`_. It is the simplest website that works with
*Mia! Accounting*. It is used in the automatic tests. It is the same
code run for `live demonstration`_.
If you do not have a running Flask application or do not know how to
start one, you may start with the test site.
Prerequisites Prerequisites
------------- -------------
@ -97,7 +68,7 @@ Database Initialization
----------------------- -----------------------
After the configuration, you need to run After the configuration, you need to run
:py:meth:`flask_sqlalchemy.SQLAlchemy.create_all` to create the `flask_sqlalchemy.SQLAlchemy.create_all`_ to create the
database tables that *Mia! Accounting* uses. database tables that *Mia! Accounting* uses.
*Mia! Accounting* adds three console commands: *Mia! Accounting* adds three console commands:
@ -106,8 +77,8 @@ database tables that *Mia! Accounting* uses.
* ``accounting-init-accounts`` * ``accounting-init-accounts``
* ``accounting-init-currencies`` * ``accounting-init-currencies``
You need to run ``accounting-init-base`` first, and then the other After database tables are created, run
two commands. ``accounting-init-base`` first, and then the other two commands.
:: ::
@ -141,18 +112,6 @@ base template:
Check your Flask application and see how it works. 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 Documentation
------------- -------------
@ -161,28 +120,17 @@ Refer to the `documentation on Read the Docs`_.
.. _Flask: https://flask.palletsprojects.com .. _Flask: https://flask.palletsprojects.com
.. _double-entry bookkeeping: https://en.wikipedia.org/wiki/Double-entry_bookkeeping .. _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 .. _test site: https://github.com/imacat/mia-accounting/tree/main/tests/test_site
.. _source distribution: https://pypi.org/project/mia-accounting/#files .. _source distribution: https://pypi.org/project/mia-accounting/#files
.. _live demonstration: https://accounting.imacat.idv.tw .. _live demonstration: https://accounting.imacat.idv.tw
.. _PyPI project page: https://pypi.org/project/mia-accounting
.. _release page: https://github.com/imacat/mia-accounting/releases
.. _Git repository: https://github.com/imacat/mia-accounting
.. _CDN: https://en.wikipedia.org/wiki/Content_delivery_network
.. _Bootstrap: https://getbootstrap.com
.. _FontAwesome: https://fontawesome.com
.. _Decimal.js: https://mikemcl.github.io/decimal.js
.. _Tempus-Dominus: https://getdatepicker.com
.. _flask_sqlalchemy.SQLAlchemy.create_all: https://flask-sqlalchemy.palletsprojects.com/en/3.0.x/api/#flask_sqlalchemy.SQLAlchemy.create_all
.. _Bootstrap navigation bar: https://getbootstrap.com/docs/5.3/components/navbar/
.. _documentation on Read the Docs: https://mia-accounting.readthedocs.io .. _documentation on Read the Docs: https://mia-accounting.readthedocs.io

View File

@ -17,7 +17,7 @@
[project] [project]
name = "mia-accounting" name = "mia-accounting"
version = "1.0.0" version = "1.0.1"
description = "A Flask accounting module." description = "A Flask accounting module."
readme = "README.rst" readme = "README.rst"
requires-python = ">=3.11" requires-python = ">=3.11"

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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