Compare commits
11 Commits
v1.0.1
...
3a0e978f76
Author | SHA1 | Date | |
---|---|---|---|
3a0e978f76 | |||
8c10d42d7b | |||
04ec51afbe | |||
fe7a8842ce | |||
66daa5c42c | |||
27fb44937d | |||
7026ed3a65 | |||
fdd3e93778 | |||
def7559457 | |||
7905820d68 | |||
7ae332c975 |
30
README.rst
30
README.rst
@ -17,8 +17,6 @@ accounting reports:
|
||||
In addition, *Mia! Accounting* tracks offsets for unpaid payables and
|
||||
receivables.
|
||||
|
||||
You may try the `live demonstration`_.
|
||||
|
||||
|
||||
Installation
|
||||
============
|
||||
@ -33,6 +31,18 @@ You may also download the from the `PyPI project page`_ or the
|
||||
`release page`_ on the `Git repository`_.
|
||||
|
||||
|
||||
Test Site and Live Demonstration
|
||||
================================
|
||||
|
||||
You may find a working example in the `test site`_ in the
|
||||
`source distribution`_. It is the simplest website that works with
|
||||
*Mia! Accounting*. It is used in the automatic tests. It is the same
|
||||
code run for `live demonstration`_.
|
||||
|
||||
If you do not have a running Flask application or do not know how to
|
||||
start one, you may start with the test site.
|
||||
|
||||
|
||||
Prerequisites
|
||||
=============
|
||||
|
||||
@ -157,18 +167,6 @@ base template:
|
||||
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
|
||||
=============
|
||||
|
||||
@ -203,6 +201,8 @@ Authors
|
||||
|
||||
.. _Flask: https://flask.palletsprojects.com
|
||||
.. _double-entry bookkeeping: https://en.wikipedia.org/wiki/Double-entry_bookkeeping
|
||||
.. _test site: https://github.com/imacat/mia-accounting/tree/main/tests/test_site
|
||||
.. _source distribution: https://pypi.org/project/mia-accounting/#files
|
||||
.. _live demonstration: https://accounting.imacat.idv.tw
|
||||
.. _PyPI project page: https://pypi.org/project/mia-accounting
|
||||
.. _release page: https://github.com/imacat/mia-accounting/releases
|
||||
@ -216,6 +216,4 @@ Authors
|
||||
.. _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/
|
||||
.. _test site: https://github.com/imacat/mia-accounting/tree/main/tests/test_site
|
||||
.. _source distribution: https://pypi.org/project/mia-accounting/#files
|
||||
.. _documentation on Read the Docs: https://mia-accounting.readthedocs.io
|
||||
|
@ -12,8 +12,6 @@ accounting reports:
|
||||
In addition, *Mia! Accounting* tracks offsets for unpaid payables and
|
||||
receivables.
|
||||
|
||||
You may try the `live demonstration`_.
|
||||
|
||||
|
||||
Installation
|
||||
------------
|
||||
@ -28,6 +26,18 @@ You may also download the from the `PyPI project page`_ or the
|
||||
`release page`_ on the `Git repository`_.
|
||||
|
||||
|
||||
Test Site and Live Demonstration
|
||||
--------------------------------
|
||||
|
||||
You may find a working example in the `test site`_ in the
|
||||
`source distribution`_. It is the simplest website that works with
|
||||
*Mia! Accounting*. It is used in the automatic tests. It is the same
|
||||
code run for `live demonstration`_.
|
||||
|
||||
If you do not have a running Flask application or do not know how to
|
||||
start one, you may start with the test site.
|
||||
|
||||
|
||||
Prerequisites
|
||||
-------------
|
||||
|
||||
@ -102,18 +112,6 @@ base template:
|
||||
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
|
||||
-------------
|
||||
|
||||
@ -122,6 +120,8 @@ Refer to the `documentation on Read the Docs`_.
|
||||
|
||||
.. _Flask: https://flask.palletsprojects.com
|
||||
.. _double-entry bookkeeping: https://en.wikipedia.org/wiki/Double-entry_bookkeeping
|
||||
.. _test site: https://github.com/imacat/mia-accounting/tree/main/tests/test_site
|
||||
.. _source distribution: https://pypi.org/project/mia-accounting/#files
|
||||
.. _live demonstration: https://accounting.imacat.idv.tw
|
||||
.. _PyPI project page: https://pypi.org/project/mia-accounting
|
||||
.. _release page: https://github.com/imacat/mia-accounting/releases
|
||||
@ -133,6 +133,4 @@ Refer to the `documentation on Read the Docs`_.
|
||||
.. _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/
|
||||
.. _test site: https://github.com/imacat/mia-accounting/tree/main/tests/test_site
|
||||
.. _source distribution: https://pypi.org/project/mia-accounting/#files
|
||||
.. _documentation on Read the Docs: https://mia-accounting.readthedocs.io
|
||||
|
@ -22,6 +22,7 @@ from flask import Blueprint, render_template
|
||||
from accounting.models import BaseAccount
|
||||
from accounting.utils.pagination import Pagination
|
||||
from accounting.utils.permission import has_permission, can_view
|
||||
from .queries import get_base_account_query
|
||||
|
||||
bp: Blueprint = Blueprint("base-account", __name__)
|
||||
"""The view blueprint for the base account management."""
|
||||
@ -34,7 +35,6 @@ def list_accounts() -> str:
|
||||
|
||||
:return: The account list.
|
||||
"""
|
||||
from .queries import get_base_account_query
|
||||
accounts: list[BaseAccount] = get_base_account_query()
|
||||
pagination: Pagination = Pagination[BaseAccount](accounts)
|
||||
return render_template("accounting/base-account/list.html",
|
||||
|
@ -34,6 +34,7 @@ from accounting.utils.pagination import Pagination
|
||||
from accounting.utils.permission import has_permission, can_view, can_edit
|
||||
from accounting.utils.user import get_current_user_pk
|
||||
from .forms import CurrencyForm
|
||||
from .queries import get_currency_query
|
||||
|
||||
bp: Blueprint = Blueprint("currency", __name__)
|
||||
"""The view blueprint for the currency management."""
|
||||
@ -48,7 +49,6 @@ def list_currencies() -> str:
|
||||
|
||||
:return: The currency list.
|
||||
"""
|
||||
from .queries import get_currency_query
|
||||
currencies: list[Currency] = get_currency_query()
|
||||
pagination: Pagination = Pagination[Currency](currencies)
|
||||
return render_template("accounting/currency/list.html",
|
||||
|
@ -23,6 +23,7 @@ from flask import abort
|
||||
from sqlalchemy.orm import selectinload
|
||||
from werkzeug.routing import BaseConverter
|
||||
|
||||
from accounting import db
|
||||
from accounting.models import JournalEntry, JournalEntryLineItem
|
||||
from accounting.utils.journal_entry_types import JournalEntryType
|
||||
|
||||
@ -37,13 +38,7 @@ class JournalEntryConverter(BaseConverter):
|
||||
:param value: The journal entry ID.
|
||||
:return: The corresponding journal entry.
|
||||
"""
|
||||
journal_entry: JournalEntry | None = JournalEntry.query\
|
||||
.join(JournalEntryLineItem)\
|
||||
.filter(JournalEntry.id == value)\
|
||||
.options(selectinload(JournalEntry.line_items)
|
||||
.selectinload(JournalEntryLineItem.offsets)
|
||||
.selectinload(JournalEntryLineItem.journal_entry))\
|
||||
.first()
|
||||
journal_entry: JournalEntry | None = db.session.get(JournalEntry, value)
|
||||
if journal_entry is None:
|
||||
abort(404)
|
||||
return journal_entry
|
||||
|
@ -31,7 +31,7 @@ from accounting import db
|
||||
from accounting.forms import ACCOUNT_REQUIRED, AccountExists, IsDebitAccount, \
|
||||
IsCreditAccount
|
||||
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.utils.cast import be
|
||||
from accounting.utils.random_id import new_id
|
||||
@ -127,10 +127,8 @@ class KeepAccountWhenHavingOffset:
|
||||
assert isinstance(form, LineItemForm)
|
||||
if field.data is None or form.id.data is None:
|
||||
return
|
||||
line_item: JournalEntryLineItem | None = db.session\
|
||||
.query(JournalEntryLineItem)\
|
||||
.filter(JournalEntryLineItem.id == form.id.data)\
|
||||
.options(selectinload(JournalEntryLineItem.offsets)).first()
|
||||
line_item: JournalEntryLineItem | None \
|
||||
= db.session.get(JournalEntryLineItem, form.id.data)
|
||||
if line_item is None or len(line_item.offsets) == 0:
|
||||
return
|
||||
if field.data != line_item.account_code:
|
||||
@ -344,14 +342,13 @@ class LineItemForm(FlaskForm):
|
||||
def get_offsets() -> list[JournalEntryLineItem]:
|
||||
if not self.is_need_offset or self.id.data is None:
|
||||
return []
|
||||
return JournalEntryLineItem.query\
|
||||
return JournalEntryLineItem.query.join(JournalEntry)\
|
||||
.filter(JournalEntryLineItem.original_line_item_id
|
||||
== self.id.data)\
|
||||
.order_by(JournalEntry.date, JournalEntry.no,
|
||||
JournalEntryLineItem.no)\
|
||||
.options(selectinload(JournalEntryLineItem.journal_entry),
|
||||
selectinload(JournalEntryLineItem.account),
|
||||
selectinload(JournalEntryLineItem.offsets)
|
||||
.selectinload(
|
||||
JournalEntryLineItem.journal_entry)).all()
|
||||
selectinload(JournalEntryLineItem.account)).all()
|
||||
setattr(self, "__offsets", get_offsets())
|
||||
return getattr(self, "__offsets")
|
||||
|
||||
|
@ -660,12 +660,8 @@ class JournalEntryLineItem(db.Model):
|
||||
nullable=True)
|
||||
"""The ID of the original line item."""
|
||||
original_line_item = db.relationship("JournalEntryLineItem",
|
||||
back_populates="offsets",
|
||||
remote_side=id, passive_deletes=True)
|
||||
"""The original line item."""
|
||||
offsets = db.relationship("JournalEntryLineItem",
|
||||
back_populates="original_line_item")
|
||||
"""The offset items."""
|
||||
currency_code = db.Column(db.String,
|
||||
db.ForeignKey(Currency.code, onupdate="CASCADE"),
|
||||
nullable=False)
|
||||
@ -758,6 +754,21 @@ class JournalEntryLineItem(db.Model):
|
||||
"""
|
||||
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
|
||||
def query_values(self) -> list[str]:
|
||||
"""Returns the values to be queried.
|
||||
|
@ -77,6 +77,8 @@ class CSVRow(BaseCSVRow):
|
||||
"""Constructs a row in the CSV.
|
||||
|
||||
:param journal_entry_date: The journal entry date.
|
||||
:param currency: The currency.
|
||||
:param account: The account.
|
||||
:param description: The description.
|
||||
:param debit: The debit amount.
|
||||
:param credit: The credit amount.
|
||||
@ -116,6 +118,7 @@ class PageParams(BasePageParams):
|
||||
"""Constructs the HTML page parameters.
|
||||
|
||||
:param period: The period.
|
||||
:param pagination: The pagination.
|
||||
:param line_items: The line items.
|
||||
"""
|
||||
self.period: Period = period
|
||||
|
@ -123,7 +123,7 @@ class JournalEntryAccountSelector {
|
||||
option.setShown(false);
|
||||
}
|
||||
}
|
||||
if (!isAnyMatched) {
|
||||
if (!isAnyMatched && this.#isShowMore) {
|
||||
this.#optionList.classList.add("d-none");
|
||||
this.#queryNoResult.classList.remove("d-none");
|
||||
} else {
|
||||
|
@ -50,10 +50,10 @@ First written: 2023/3/14
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
{% if line_item.balance %}
|
||||
{% if line_item.net_balance %}
|
||||
<div class="d-flex justify-content-between">
|
||||
<div>{{ A_("Net balance") }}</div>
|
||||
<div>{{ line_item.balance|accounting_format_amount }}</div>
|
||||
<div>{{ line_item.net_balance|accounting_format_amount }}</div>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="d-flex justify-content-between">
|
||||
|
Reference in New Issue
Block a user