Renamed "voucher line item" to "journal entry line item".

This commit is contained in:
2023-03-20 20:52:35 +08:00
parent e26af6f3fc
commit 8f909965a9
34 changed files with 336 additions and 303 deletions

View File

@ -25,7 +25,7 @@ from flask import render_template, Response
from accounting import db
from accounting.locale import gettext
from accounting.models import Currency, BaseAccount, Account, Voucher, \
VoucherLineItem
JournalEntryLineItem
from accounting.report.period import Period, PeriodChooser
from accounting.report.utils.base_page_params import BasePageParams
from accounting.report.utils.base_report import BaseReport
@ -124,13 +124,13 @@ class AccountCollector:
sub_conditions: list[sa.BinaryExpression] \
= [Account.base_code.startswith(x) for x in {"1", "2", "3"}]
conditions: list[sa.BinaryExpression] \
= [VoucherLineItem.currency_code == self.__currency.code,
= [JournalEntryLineItem.currency_code == self.__currency.code,
sa.or_(*sub_conditions)]
if self.__period.end is not None:
conditions.append(Voucher.date <= self.__period.end)
balance_func: sa.Function = sa.func.sum(sa.case(
(VoucherLineItem.is_debit, VoucherLineItem.amount),
else_=-VoucherLineItem.amount)).label("balance")
(JournalEntryLineItem.is_debit, JournalEntryLineItem.amount),
else_=-JournalEntryLineItem.amount)).label("balance")
select_balance: sa.Select \
= sa.select(Account.id, Account.base_code, Account.no,
balance_func)\
@ -178,7 +178,7 @@ class AccountCollector:
if self.__period.start is None:
return None
conditions: list[sa.BinaryExpression] \
= [VoucherLineItem.currency_code == self.__currency.code,
= [JournalEntryLineItem.currency_code == self.__currency.code,
Voucher.date < self.__period.start]
return self.__query_balance(conditions)
@ -197,7 +197,7 @@ class AccountCollector:
:return: The net income or loss for current period.
"""
conditions: list[sa.BinaryExpression] \
= [VoucherLineItem.currency_code == self.__currency.code]
= [JournalEntryLineItem.currency_code == self.__currency.code]
if self.__period.start is not None:
conditions.append(Voucher.date >= self.__period.start)
if self.__period.end is not None:
@ -215,8 +215,8 @@ class AccountCollector:
conditions.extend([sa.not_(Account.base_code.startswith(x))
for x in {"1", "2", "3"}])
balance_func: sa.Function = sa.func.sum(sa.case(
(VoucherLineItem.is_debit, VoucherLineItem.amount),
else_=-VoucherLineItem.amount))
(JournalEntryLineItem.is_debit, JournalEntryLineItem.amount),
else_=-JournalEntryLineItem.amount))
select_balance: sa.Select = sa.select(balance_func)\
.join(Voucher).join(Account).filter(*conditions)
return db.session.scalar(select_balance)

View File

@ -26,7 +26,7 @@ from sqlalchemy.orm import selectinload
from accounting import db
from accounting.locale import gettext
from accounting.models import Currency, Account, Voucher, VoucherLineItem
from accounting.models import Currency, Account, Voucher, JournalEntryLineItem
from accounting.report.period import Period, PeriodChooser
from accounting.report.utils.base_page_params import BasePageParams
from accounting.report.utils.base_report import BaseReport
@ -44,10 +44,10 @@ from accounting.utils.pagination import Pagination
class ReportLineItem:
"""A line item in the report."""
def __init__(self, line_item: VoucherLineItem | None = None):
def __init__(self, line_item: JournalEntryLineItem | None = None):
"""Constructs the line item in the report.
:param line_item: The voucher line item.
:param line_item: The journal entry line item.
"""
self.is_brought_forward: bool = False
"""Whether this is the brought-forward line item."""
@ -68,7 +68,7 @@ class ReportLineItem:
self.note: str | None = None
"""The note."""
self.url: str | None = None
"""The URL to the voucher line item."""
"""The URL to the journal entry line item."""
if line_item is not None:
self.date = line_item.voucher.date
self.account = line_item.account
@ -117,11 +117,12 @@ class LineItemCollector:
if self.__period.start is None:
return None
balance_func: sa.Function = sa.func.sum(sa.case(
(VoucherLineItem.is_debit, VoucherLineItem.amount),
else_=-VoucherLineItem.amount))
(JournalEntryLineItem.is_debit, JournalEntryLineItem.amount),
else_=-JournalEntryLineItem.amount))
select: sa.Select = sa.Select(balance_func)\
.join(Voucher).join(Account)\
.filter(be(VoucherLineItem.currency_code == self.__currency.code),
.filter(be(JournalEntryLineItem.currency_code
== self.__currency.code),
self.__account_condition,
Voucher.date < self.__period.start)
balance: int | None = db.session.scalar(select)
@ -145,26 +146,28 @@ class LineItemCollector:
:return: The line items.
"""
conditions: list[sa.BinaryExpression] \
= [VoucherLineItem.currency_code == self.__currency.code,
= [JournalEntryLineItem.currency_code == self.__currency.code,
self.__account_condition]
if self.__period.start is not None:
conditions.append(Voucher.date >= self.__period.start)
if self.__period.end is not None:
conditions.append(Voucher.date <= self.__period.end)
voucher_with_account: sa.Select = sa.Select(Voucher.id).\
join(VoucherLineItem).join(Account).filter(*conditions)
join(JournalEntryLineItem).join(Account).filter(*conditions)
return [ReportLineItem(x)
for x in VoucherLineItem.query.join(Voucher).join(Account)
.filter(VoucherLineItem.voucher_id.in_(voucher_with_account),
VoucherLineItem.currency_code == self.__currency.code,
for x in JournalEntryLineItem.query.join(Voucher).join(Account)
.filter(JournalEntryLineItem.voucher_id
.in_(voucher_with_account),
JournalEntryLineItem.currency_code
== self.__currency.code,
sa.not_(self.__account_condition))
.order_by(Voucher.date,
Voucher.no,
VoucherLineItem.is_debit,
VoucherLineItem.no)
.options(selectinload(VoucherLineItem.account),
selectinload(VoucherLineItem.voucher))]
JournalEntryLineItem.is_debit,
JournalEntryLineItem.no)
.options(selectinload(JournalEntryLineItem.account),
selectinload(JournalEntryLineItem.voucher))]
@property
def __account_condition(self) -> sa.BinaryExpression:
@ -343,14 +346,15 @@ class PageParams(BasePageParams):
income_expenses_url(self.currency, current_al,
self.period),
self.account.id == 0)]
in_use: sa.Select = sa.Select(VoucherLineItem.account_id)\
in_use: sa.Select = sa.Select(JournalEntryLineItem.account_id)\
.join(Account)\
.filter(be(VoucherLineItem.currency_code == self.currency.code),
.filter(be(JournalEntryLineItem.currency_code
== self.currency.code),
sa.or_(Account.base_code.startswith("11"),
Account.base_code.startswith("12"),
Account.base_code.startswith("21"),
Account.base_code.startswith("22")))\
.group_by(VoucherLineItem.account_id)
.group_by(JournalEntryLineItem.account_id)
options.extend([OptionLink(str(x),
income_expenses_url(
self.currency,

View File

@ -25,7 +25,7 @@ from flask import render_template, Response
from accounting import db
from accounting.locale import gettext
from accounting.models import Currency, BaseAccount, Account, Voucher, \
VoucherLineItem
JournalEntryLineItem
from accounting.report.period import Period, PeriodChooser
from accounting.report.utils.base_page_params import BasePageParams
from accounting.report.utils.base_report import BaseReport
@ -256,15 +256,15 @@ class IncomeStatement(BaseReport):
sub_conditions: list[sa.BinaryExpression] \
= [Account.base_code.startswith(str(x)) for x in range(4, 10)]
conditions: list[sa.BinaryExpression] \
= [VoucherLineItem.currency_code == self.__currency.code,
= [JournalEntryLineItem.currency_code == self.__currency.code,
sa.or_(*sub_conditions)]
if self.__period.start is not None:
conditions.append(Voucher.date >= self.__period.start)
if self.__period.end is not None:
conditions.append(Voucher.date <= self.__period.end)
balance_func: sa.Function = sa.func.sum(sa.case(
(VoucherLineItem.is_debit, -VoucherLineItem.amount),
else_=VoucherLineItem.amount)).label("balance")
(JournalEntryLineItem.is_debit, -JournalEntryLineItem.amount),
else_=JournalEntryLineItem.amount)).label("balance")
select_balances: sa.Select = sa.select(Account.id, balance_func)\
.join(Voucher).join(Account)\
.filter(*conditions)\

View File

@ -25,7 +25,7 @@ from flask import render_template, Response
from sqlalchemy.orm import selectinload
from accounting.locale import gettext
from accounting.models import Currency, Account, Voucher, VoucherLineItem
from accounting.models import Currency, Account, Voucher, JournalEntryLineItem
from accounting.report.period import Period, PeriodChooser
from accounting.report.utils.base_page_params import BasePageParams
from accounting.report.utils.base_report import BaseReport
@ -40,13 +40,13 @@ from accounting.utils.pagination import Pagination
class ReportLineItem:
"""A line item in the report."""
def __init__(self, line_item: VoucherLineItem):
def __init__(self, line_item: JournalEntryLineItem):
"""Constructs the line item in the report.
:param line_item: The voucher line item.
:param line_item: The journal entry line item.
"""
self.line_item: VoucherLineItem = line_item
"""The voucher line item."""
self.line_item: JournalEntryLineItem = line_item
"""The journal entry line item."""
self.voucher: Voucher = line_item.voucher
"""The voucher."""
self.currency: Currency = line_item.currency
@ -110,8 +110,8 @@ class PageParams(BasePageParams):
"""The HTML page parameters."""
def __init__(self, period: Period,
pagination: Pagination[VoucherLineItem],
line_items: list[VoucherLineItem]):
pagination: Pagination[JournalEntryLineItem],
line_items: list[JournalEntryLineItem]):
"""Constructs the HTML page parameters.
:param period: The period.
@ -119,9 +119,9 @@ class PageParams(BasePageParams):
"""
self.period: Period = period
"""The period."""
self.pagination: Pagination[VoucherLineItem] = pagination
self.pagination: Pagination[JournalEntryLineItem] = pagination
"""The pagination."""
self.line_items: list[VoucherLineItem] = line_items
self.line_items: list[JournalEntryLineItem] = line_items
"""The line items."""
self.period_chooser: PeriodChooser = PeriodChooser(
lambda x: journal_url(x))
@ -145,7 +145,7 @@ class PageParams(BasePageParams):
period=self.period)
def get_csv_rows(line_items: list[VoucherLineItem]) -> list[CSVRow]:
def get_csv_rows(line_items: list[JournalEntryLineItem]) -> list[CSVRow]:
"""Composes and returns the CSV rows from the line items.
:param line_items: The line items.
@ -172,10 +172,11 @@ class Journal(BaseReport):
"""
self.__period: Period = period
"""The period."""
self.__line_items: list[VoucherLineItem] = self.__query_line_items()
self.__line_items: list[JournalEntryLineItem] \
= self.__query_line_items()
"""The line items."""
def __query_line_items(self) -> list[VoucherLineItem]:
def __query_line_items(self) -> list[JournalEntryLineItem]:
"""Queries and returns the line items.
:return: The line items.
@ -185,15 +186,15 @@ class Journal(BaseReport):
conditions.append(Voucher.date >= self.__period.start)
if self.__period.end is not None:
conditions.append(Voucher.date <= self.__period.end)
return VoucherLineItem.query.join(Voucher)\
return JournalEntryLineItem.query.join(Voucher)\
.filter(*conditions)\
.order_by(Voucher.date,
Voucher.no,
VoucherLineItem.is_debit.desc(),
VoucherLineItem.no)\
.options(selectinload(VoucherLineItem.account),
selectinload(VoucherLineItem.currency),
selectinload(VoucherLineItem.voucher)).all()
JournalEntryLineItem.is_debit.desc(),
JournalEntryLineItem.no)\
.options(selectinload(JournalEntryLineItem.account),
selectinload(JournalEntryLineItem.currency),
selectinload(JournalEntryLineItem.voucher)).all()
def csv(self) -> Response:
"""Returns the report as CSV for download.
@ -208,8 +209,9 @@ class Journal(BaseReport):
:return: The report as HTML.
"""
pagination: Pagination[VoucherLineItem] \
= Pagination[VoucherLineItem](self.__line_items, is_reversed=True)
pagination: Pagination[JournalEntryLineItem] \
= Pagination[JournalEntryLineItem](self.__line_items,
is_reversed=True)
params: PageParams = PageParams(period=self.__period,
pagination=pagination,
line_items=pagination.list)

View File

@ -26,7 +26,7 @@ from sqlalchemy.orm import selectinload
from accounting import db
from accounting.locale import gettext
from accounting.models import Currency, Account, Voucher, VoucherLineItem
from accounting.models import Currency, Account, Voucher, JournalEntryLineItem
from accounting.report.period import Period, PeriodChooser
from accounting.report.utils.base_page_params import BasePageParams
from accounting.report.utils.base_report import BaseReport
@ -43,10 +43,10 @@ from accounting.utils.pagination import Pagination
class ReportLineItem:
"""A line item in the report."""
def __init__(self, line_item: VoucherLineItem | None = None):
def __init__(self, line_item: JournalEntryLineItem | None = None):
"""Constructs the line item in the report.
:param line_item: The voucher line item.
:param line_item: The journal entry line item.
"""
self.is_brought_forward: bool = False
"""Whether this is the brought-forward line item."""
@ -65,7 +65,7 @@ class ReportLineItem:
self.note: str | None = None
"""The note."""
self.url: str | None = None
"""The URL to the voucher line item."""
"""The URL to the journal entry line item."""
if line_item is not None:
self.date = line_item.voucher.date
self.description = line_item.description
@ -114,11 +114,13 @@ class LineItemCollector:
if self.__account.is_nominal:
return None
balance_func: sa.Function = sa.func.sum(sa.case(
(VoucherLineItem.is_debit, VoucherLineItem.amount),
else_=-VoucherLineItem.amount))
(JournalEntryLineItem.is_debit, JournalEntryLineItem.amount),
else_=-JournalEntryLineItem.amount))
select: sa.Select = sa.Select(balance_func).join(Voucher)\
.filter(be(VoucherLineItem.currency_code == self.__currency.code),
be(VoucherLineItem.account_id == self.__account.id),
.filter(be(JournalEntryLineItem.currency_code
== self.__currency.code),
be(JournalEntryLineItem.account_id
== self.__account.id),
Voucher.date < self.__period.start)
balance: int | None = db.session.scalar(select)
if balance is None:
@ -140,19 +142,20 @@ class LineItemCollector:
:return: The line items.
"""
conditions: list[sa.BinaryExpression] \
= [VoucherLineItem.currency_code == self.__currency.code,
VoucherLineItem.account_id == self.__account.id]
= [JournalEntryLineItem.currency_code == self.__currency.code,
JournalEntryLineItem.account_id == self.__account.id]
if self.__period.start is not None:
conditions.append(Voucher.date >= self.__period.start)
if self.__period.end is not None:
conditions.append(Voucher.date <= self.__period.end)
return [ReportLineItem(x) for x in VoucherLineItem.query.join(Voucher)
return [ReportLineItem(x) for x in JournalEntryLineItem.query
.join(Voucher)
.filter(*conditions)
.order_by(Voucher.date,
Voucher.no,
VoucherLineItem.is_debit.desc(),
VoucherLineItem.no)
.options(selectinload(VoucherLineItem.voucher)).all()]
JournalEntryLineItem.is_debit.desc(),
JournalEntryLineItem.no)
.options(selectinload(JournalEntryLineItem.voucher)).all()]
def __get_total(self) -> ReportLineItem | None:
"""Composes the total line item.
@ -307,9 +310,10 @@ class PageParams(BasePageParams):
:return: The account options.
"""
in_use: sa.Select = sa.Select(VoucherLineItem.account_id)\
.filter(be(VoucherLineItem.currency_code == self.currency.code))\
.group_by(VoucherLineItem.account_id)
in_use: sa.Select = sa.Select(JournalEntryLineItem.account_id)\
.filter(be(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)
for x in Account.query.filter(Account.id.in_(in_use))

View File

@ -26,7 +26,7 @@ from sqlalchemy.orm import selectinload
from accounting.locale import gettext
from accounting.models import Currency, CurrencyL10n, Account, AccountL10n, \
Voucher, VoucherLineItem
Voucher, JournalEntryLineItem
from accounting.report.utils.base_page_params import BasePageParams
from accounting.report.utils.base_report import BaseReport
from accounting.report.utils.csv_export import csv_download
@ -43,10 +43,10 @@ class LineItemCollector:
def __init__(self):
"""Constructs the line item collector."""
self.line_items: list[VoucherLineItem] = self.__query_line_items()
self.line_items: list[JournalEntryLineItem] = self.__query_line_items()
"""The line items."""
def __query_line_items(self) -> list[VoucherLineItem]:
def __query_line_items(self) -> list[JournalEntryLineItem]:
"""Queries and returns the line items.
:return: The line items.
@ -57,26 +57,27 @@ class LineItemCollector:
conditions: list[sa.BinaryExpression] = []
for k in keywords:
sub_conditions: list[sa.BinaryExpression] \
= [VoucherLineItem.description.contains(k),
VoucherLineItem.account_id.in_(
= [JournalEntryLineItem.description.contains(k),
JournalEntryLineItem.account_id.in_(
self.__get_account_condition(k)),
VoucherLineItem.currency_code.in_(
JournalEntryLineItem.currency_code.in_(
self.__get_currency_condition(k)),
VoucherLineItem.voucher_id.in_(
JournalEntryLineItem.voucher_id.in_(
self.__get_voucher_condition(k))]
try:
sub_conditions.append(VoucherLineItem.amount == Decimal(k))
sub_conditions.append(
JournalEntryLineItem.amount == Decimal(k))
except ArithmeticError:
pass
conditions.append(sa.or_(*sub_conditions))
return VoucherLineItem.query.join(Voucher).filter(*conditions)\
return JournalEntryLineItem.query.join(Voucher).filter(*conditions)\
.order_by(Voucher.date,
Voucher.no,
VoucherLineItem.is_debit,
VoucherLineItem.no)\
.options(selectinload(VoucherLineItem.account),
selectinload(VoucherLineItem.currency),
selectinload(VoucherLineItem.voucher)).all()
JournalEntryLineItem.is_debit,
JournalEntryLineItem.no)\
.options(selectinload(JournalEntryLineItem.account),
selectinload(JournalEntryLineItem.currency),
selectinload(JournalEntryLineItem.voucher)).all()
@staticmethod
def __get_account_condition(k: str) -> sa.Select:
@ -149,15 +150,15 @@ class LineItemCollector:
class PageParams(BasePageParams):
"""The HTML page parameters."""
def __init__(self, pagination: Pagination[VoucherLineItem],
line_items: list[VoucherLineItem]):
def __init__(self, pagination: Pagination[JournalEntryLineItem],
line_items: list[JournalEntryLineItem]):
"""Constructs the HTML page parameters.
:param line_items: The search result line items.
"""
self.pagination: Pagination[VoucherLineItem] = pagination
self.pagination: Pagination[JournalEntryLineItem] = pagination
"""The pagination."""
self.line_items: list[VoucherLineItem] = line_items
self.line_items: list[JournalEntryLineItem] = line_items
"""The line items."""
@property
@ -182,7 +183,7 @@ class Search(BaseReport):
def __init__(self):
"""Constructs a search."""
self.__line_items: list[VoucherLineItem] \
self.__line_items: list[JournalEntryLineItem] \
= LineItemCollector().line_items
"""The line items."""
@ -199,8 +200,9 @@ class Search(BaseReport):
:return: The report as HTML.
"""
pagination: Pagination[VoucherLineItem] \
= Pagination[VoucherLineItem](self.__line_items, is_reversed=True)
pagination: Pagination[JournalEntryLineItem] \
= Pagination[JournalEntryLineItem](self.__line_items,
is_reversed=True)
params: PageParams = PageParams(pagination=pagination,
line_items=pagination.list)
return render_template("accounting/report/search.html",

View File

@ -24,7 +24,7 @@ from flask import Response, render_template
from accounting import db
from accounting.locale import gettext
from accounting.models import Currency, Account, Voucher, VoucherLineItem
from accounting.models import Currency, Account, Voucher, JournalEntryLineItem
from accounting.report.period import Period, PeriodChooser
from accounting.report.utils.base_page_params import BasePageParams
from accounting.report.utils.base_report import BaseReport
@ -178,14 +178,14 @@ class TrialBalance(BaseReport):
:return: None.
"""
conditions: list[sa.BinaryExpression] \
= [VoucherLineItem.currency_code == self.__currency.code]
= [JournalEntryLineItem.currency_code == self.__currency.code]
if self.__period.start is not None:
conditions.append(Voucher.date >= self.__period.start)
if self.__period.end is not None:
conditions.append(Voucher.date <= self.__period.end)
balance_func: sa.Function = sa.func.sum(sa.case(
(VoucherLineItem.is_debit, VoucherLineItem.amount),
else_=-VoucherLineItem.amount)).label("balance")
(JournalEntryLineItem.is_debit, JournalEntryLineItem.amount),
else_=-JournalEntryLineItem.amount)).label("balance")
select_balances: sa.Select = sa.select(Account.id, balance_func)\
.join(Voucher).join(Account)\
.filter(*conditions)\