Renamed "transaction" to "voucher", "cash expense transaction" to "cash disbursement voucher", and "cash income transaction" to "cash receipt voucher".

This commit is contained in:
2023-03-19 13:44:51 +08:00
parent 1e286fbeba
commit 5db13393cc
75 changed files with 1812 additions and 1792 deletions

View File

@ -24,7 +24,7 @@ from flask import render_template, Response
from accounting import db
from accounting.locale import gettext
from accounting.models import Currency, BaseAccount, Account, Transaction, \
from accounting.models import Currency, BaseAccount, Account, Voucher, \
JournalEntry
from accounting.report.period import Period, PeriodChooser
from accounting.report.utils.base_page_params import BasePageParams
@ -127,14 +127,14 @@ class AccountCollector:
= [JournalEntry.currency_code == self.__currency.code,
sa.or_(*sub_conditions)]
if self.__period.end is not None:
conditions.append(Transaction.date <= self.__period.end)
conditions.append(Voucher.date <= self.__period.end)
balance_func: sa.Function = sa.func.sum(sa.case(
(JournalEntry.is_debit, JournalEntry.amount),
else_=-JournalEntry.amount)).label("balance")
select_balance: sa.Select \
= sa.select(Account.id, Account.base_code, Account.no,
balance_func)\
.join(Transaction).join(Account)\
.join(Voucher).join(Account)\
.filter(*conditions)\
.group_by(Account.id, Account.base_code, Account.no)\
.order_by(Account.base_code, Account.no)
@ -179,7 +179,7 @@ class AccountCollector:
return None
conditions: list[sa.BinaryExpression] \
= [JournalEntry.currency_code == self.__currency.code,
Transaction.date < self.__period.start]
Voucher.date < self.__period.start]
return self.__query_balance(conditions)
def __add_current_period(self) -> None:
@ -199,9 +199,9 @@ class AccountCollector:
conditions: list[sa.BinaryExpression] \
= [JournalEntry.currency_code == self.__currency.code]
if self.__period.start is not None:
conditions.append(Transaction.date >= self.__period.start)
conditions.append(Voucher.date >= self.__period.start)
if self.__period.end is not None:
conditions.append(Transaction.date <= self.__period.end)
conditions.append(Voucher.date <= self.__period.end)
return self.__query_balance(conditions)
@staticmethod
@ -218,7 +218,7 @@ class AccountCollector:
(JournalEntry.is_debit, JournalEntry.amount),
else_=-JournalEntry.amount))
select_balance: sa.Select = sa.select(balance_func)\
.join(Transaction).join(Account).filter(*conditions)
.join(Voucher).join(Account).filter(*conditions)
return db.session.scalar(select_balance)
def __add_owner_s_equity(self, code: str, amount: Decimal | None,

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, Transaction, JournalEntry
from accounting.models import Currency, Account, Voucher, JournalEntry
from accounting.report.period import Period, PeriodChooser
from accounting.report.utils.base_page_params import BasePageParams
from accounting.report.utils.base_report import BaseReport
@ -70,14 +70,14 @@ class ReportEntry:
self.url: str | None = None
"""The URL to the journal entry."""
if entry is not None:
self.date = entry.transaction.date
self.date = entry.voucher.date
self.account = entry.account
self.summary = entry.summary
self.income = None if entry.is_debit else entry.amount
self.expense = entry.amount if entry.is_debit else None
self.note = entry.transaction.note
self.url = url_for("accounting.transaction.detail",
txn=entry.transaction)
self.note = entry.voucher.note
self.url = url_for("accounting.voucher.detail",
voucher=entry.voucher)
class EntryCollector:
@ -120,10 +120,10 @@ class EntryCollector:
(JournalEntry.is_debit, JournalEntry.amount),
else_=-JournalEntry.amount))
select: sa.Select = sa.Select(balance_func)\
.join(Transaction).join(Account)\
.join(Voucher).join(Account)\
.filter(be(JournalEntry.currency_code == self.__currency.code),
self.__account_condition,
Transaction.date < self.__period.start)
Voucher.date < self.__period.start)
balance: int | None = db.session.scalar(select)
if balance is None:
return None
@ -148,23 +148,23 @@ class EntryCollector:
= [JournalEntry.currency_code == self.__currency.code,
self.__account_condition]
if self.__period.start is not None:
conditions.append(Transaction.date >= self.__period.start)
conditions.append(Voucher.date >= self.__period.start)
if self.__period.end is not None:
conditions.append(Transaction.date <= self.__period.end)
txn_with_account: sa.Select = sa.Select(Transaction.id).\
conditions.append(Voucher.date <= self.__period.end)
voucher_with_account: sa.Select = sa.Select(Voucher.id).\
join(JournalEntry).join(Account).filter(*conditions)
return [ReportEntry(x)
for x in JournalEntry.query.join(Transaction).join(Account)
.filter(JournalEntry.transaction_id.in_(txn_with_account),
for x in JournalEntry.query.join(Voucher).join(Account)
.filter(JournalEntry.voucher_id.in_(voucher_with_account),
JournalEntry.currency_code == self.__currency.code,
sa.not_(self.__account_condition))
.order_by(Transaction.date,
Transaction.no,
.order_by(Voucher.date,
Voucher.no,
JournalEntry.is_debit,
JournalEntry.no)
.options(selectinload(JournalEntry.account),
selectinload(JournalEntry.transaction))]
selectinload(JournalEntry.voucher))]
@property
def __account_condition(self) -> sa.BinaryExpression:
@ -212,7 +212,7 @@ class EntryCollector:
class CSVRow(BaseCSVRow):
"""A row in the CSV."""
def __init__(self, txn_date: date | str | None,
def __init__(self, voucher_date: date | str | None,
account: str | None,
summary: str | None,
income: str | Decimal | None,
@ -221,7 +221,7 @@ class CSVRow(BaseCSVRow):
note: str | None):
"""Constructs a row in the CSV.
:param txn_date: The transaction date.
:param voucher_date: The voucher date.
:param account: The account.
:param summary: The summary.
:param income: The income.
@ -229,7 +229,7 @@ class CSVRow(BaseCSVRow):
:param balance: The balance.
:param note: The note.
"""
self.date: date | str | None = txn_date
self.date: date | str | None = voucher_date
"""The date."""
self.account: str | None = account
"""The account."""

View File

@ -24,7 +24,7 @@ from flask import render_template, Response
from accounting import db
from accounting.locale import gettext
from accounting.models import Currency, BaseAccount, Account, Transaction, \
from accounting.models import Currency, BaseAccount, Account, Voucher, \
JournalEntry
from accounting.report.period import Period, PeriodChooser
from accounting.report.utils.base_page_params import BasePageParams
@ -259,14 +259,14 @@ class IncomeStatement(BaseReport):
= [JournalEntry.currency_code == self.__currency.code,
sa.or_(*sub_conditions)]
if self.__period.start is not None:
conditions.append(Transaction.date >= self.__period.start)
conditions.append(Voucher.date >= self.__period.start)
if self.__period.end is not None:
conditions.append(Transaction.date <= self.__period.end)
conditions.append(Voucher.date <= self.__period.end)
balance_func: sa.Function = sa.func.sum(sa.case(
(JournalEntry.is_debit, -JournalEntry.amount),
else_=JournalEntry.amount)).label("balance")
select_balances: sa.Select = sa.select(Account.id, balance_func)\
.join(Transaction).join(Account)\
.join(Voucher).join(Account)\
.filter(*conditions)\
.group_by(Account.id)\
.order_by(Account.base_code, Account.no)

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, Transaction, JournalEntry
from accounting.models import Currency, Account, Voucher, JournalEntry
from accounting.report.period import Period, PeriodChooser
from accounting.report.utils.base_page_params import BasePageParams
from accounting.report.utils.base_report import BaseReport
@ -47,8 +47,8 @@ class ReportEntry:
"""
self.entry: JournalEntry = entry
"""The journal entry."""
self.transaction: Transaction = entry.transaction
"""The transaction."""
self.voucher: Voucher = entry.voucher
"""The voucher."""
self.currency: Currency = entry.currency
"""The account."""
self.account: Account = entry.account
@ -66,7 +66,7 @@ class ReportEntry:
class CSVRow(BaseCSVRow):
"""A row in the CSV."""
def __init__(self, txn_date: str | date,
def __init__(self, voucher_date: str | date,
currency: str,
account: str,
summary: str | None,
@ -75,13 +75,13 @@ class CSVRow(BaseCSVRow):
note: str | None):
"""Constructs a row in the CSV.
:param txn_date: The transaction date.
:param voucher_date: The voucher date.
:param summary: The summary.
:param debit: The debit amount.
:param credit: The credit amount.
:param note: The note.
"""
self.date: str | date = txn_date
self.date: str | date = voucher_date
"""The date."""
self.currency: str = currency
"""The currency."""
@ -155,9 +155,9 @@ def get_csv_rows(entries: list[JournalEntry]) -> list[CSVRow]:
gettext("Account"), gettext("Summary"),
gettext("Debit"), gettext("Credit"),
gettext("Note"))]
rows.extend([CSVRow(x.transaction.date, x.currency.code,
rows.extend([CSVRow(x.voucher.date, x.currency.code,
str(x.account).title(), x.summary,
x.debit, x.credit, x.transaction.note)
x.debit, x.credit, x.voucher.note)
for x in entries])
return rows
@ -182,18 +182,18 @@ class Journal(BaseReport):
"""
conditions: list[sa.BinaryExpression] = []
if self.__period.start is not None:
conditions.append(Transaction.date >= self.__period.start)
conditions.append(Voucher.date >= self.__period.start)
if self.__period.end is not None:
conditions.append(Transaction.date <= self.__period.end)
return JournalEntry.query.join(Transaction)\
conditions.append(Voucher.date <= self.__period.end)
return JournalEntry.query.join(Voucher)\
.filter(*conditions)\
.order_by(Transaction.date,
Transaction.no,
.order_by(Voucher.date,
Voucher.no,
JournalEntry.is_debit.desc(),
JournalEntry.no)\
.options(selectinload(JournalEntry.account),
selectinload(JournalEntry.currency),
selectinload(JournalEntry.transaction)).all()
selectinload(JournalEntry.voucher)).all()
def csv(self) -> Response:
"""Returns the report as CSV for download.

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, Transaction, JournalEntry
from accounting.models import Currency, Account, Voucher, JournalEntry
from accounting.report.period import Period, PeriodChooser
from accounting.report.utils.base_page_params import BasePageParams
from accounting.report.utils.base_report import BaseReport
@ -67,13 +67,13 @@ class ReportEntry:
self.url: str | None = None
"""The URL to the journal entry."""
if entry is not None:
self.date = entry.transaction.date
self.date = entry.voucher.date
self.summary = entry.summary
self.debit = entry.amount if entry.is_debit else None
self.credit = None if entry.is_debit else entry.amount
self.note = entry.transaction.note
self.url = url_for("accounting.transaction.detail",
txn=entry.transaction)
self.note = entry.voucher.note
self.url = url_for("accounting.voucher.detail",
voucher=entry.voucher)
class EntryCollector:
@ -116,10 +116,10 @@ class EntryCollector:
balance_func: sa.Function = sa.func.sum(sa.case(
(JournalEntry.is_debit, JournalEntry.amount),
else_=-JournalEntry.amount))
select: sa.Select = sa.Select(balance_func).join(Transaction)\
select: sa.Select = sa.Select(balance_func).join(Voucher)\
.filter(be(JournalEntry.currency_code == self.__currency.code),
be(JournalEntry.account_id == self.__account.id),
Transaction.date < self.__period.start)
Voucher.date < self.__period.start)
balance: int | None = db.session.scalar(select)
if balance is None:
return None
@ -143,16 +143,16 @@ class EntryCollector:
= [JournalEntry.currency_code == self.__currency.code,
JournalEntry.account_id == self.__account.id]
if self.__period.start is not None:
conditions.append(Transaction.date >= self.__period.start)
conditions.append(Voucher.date >= self.__period.start)
if self.__period.end is not None:
conditions.append(Transaction.date <= self.__period.end)
return [ReportEntry(x) for x in JournalEntry.query.join(Transaction)
conditions.append(Voucher.date <= self.__period.end)
return [ReportEntry(x) for x in JournalEntry.query.join(Voucher)
.filter(*conditions)
.order_by(Transaction.date,
Transaction.no,
.order_by(Voucher.date,
Voucher.no,
JournalEntry.is_debit.desc(),
JournalEntry.no)
.options(selectinload(JournalEntry.transaction)).all()]
.options(selectinload(JournalEntry.voucher)).all()]
def __get_total_entry(self) -> ReportEntry | None:
"""Composes the total entry.
@ -193,7 +193,7 @@ class EntryCollector:
class CSVRow(BaseCSVRow):
"""A row in the CSV."""
def __init__(self, txn_date: date | str | None,
def __init__(self, voucher_date: date | str | None,
summary: str | None,
debit: str | Decimal | None,
credit: str | Decimal | None,
@ -201,14 +201,14 @@ class CSVRow(BaseCSVRow):
note: str | None):
"""Constructs a row in the CSV.
:param txn_date: The transaction date.
:param voucher_date: The voucher date.
:param summary: The summary.
:param debit: The debit amount.
:param credit: The credit amount.
:param balance: The balance.
:param note: The note.
"""
self.date: date | str | None = txn_date
self.date: date | str | None = voucher_date
"""The date."""
self.summary: str | None = summary
"""The summary."""

View File

@ -26,7 +26,7 @@ from sqlalchemy.orm import selectinload
from accounting.locale import gettext
from accounting.models import Currency, CurrencyL10n, Account, AccountL10n, \
Transaction, JournalEntry
Voucher, JournalEntry
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
@ -62,21 +62,21 @@ class EntryCollector:
self.__get_account_condition(k)),
JournalEntry.currency_code.in_(
self.__get_currency_condition(k)),
JournalEntry.transaction_id.in_(
self.__get_transaction_condition(k))]
JournalEntry.voucher_id.in_(
self.__get_voucher_condition(k))]
try:
sub_conditions.append(JournalEntry.amount == Decimal(k))
except ArithmeticError:
pass
conditions.append(sa.or_(*sub_conditions))
return JournalEntry.query.join(Transaction).filter(*conditions)\
.order_by(Transaction.date,
Transaction.no,
return JournalEntry.query.join(Voucher).filter(*conditions)\
.order_by(Voucher.date,
Voucher.no,
JournalEntry.is_debit,
JournalEntry.no)\
.options(selectinload(JournalEntry.account),
selectinload(JournalEntry.currency),
selectinload(JournalEntry.transaction)).all()
selectinload(JournalEntry.voucher)).all()
@staticmethod
def __get_account_condition(k: str) -> sa.Select:
@ -115,35 +115,35 @@ class EntryCollector:
Currency.code.in_(select_l10n)))
@staticmethod
def __get_transaction_condition(k: str) -> sa.Select:
"""Composes and returns the condition to filter the transaction.
def __get_voucher_condition(k: str) -> sa.Select:
"""Composes and returns the condition to filter the voucher.
:param k: The keyword.
:return: The condition to filter the transaction.
:return: The condition to filter the voucher.
"""
conditions: list[sa.BinaryExpression] = [Transaction.note.contains(k)]
txn_date: datetime
conditions: list[sa.BinaryExpression] = [Voucher.note.contains(k)]
voucher_date: datetime
try:
txn_date = datetime.strptime(k, "%Y")
voucher_date = datetime.strptime(k, "%Y")
conditions.append(
be(sa.extract("year", Transaction.date) == txn_date.year))
be(sa.extract("year", Voucher.date) == voucher_date.year))
except ValueError:
pass
try:
txn_date = datetime.strptime(k, "%Y/%m")
voucher_date = datetime.strptime(k, "%Y/%m")
conditions.append(sa.and_(
sa.extract("year", Transaction.date) == txn_date.year,
sa.extract("month", Transaction.date) == txn_date.month))
sa.extract("year", Voucher.date) == voucher_date.year,
sa.extract("month", Voucher.date) == voucher_date.month))
except ValueError:
pass
try:
txn_date = datetime.strptime(f"2000/{k}", "%Y/%m/%d")
voucher_date = datetime.strptime(f"2000/{k}", "%Y/%m/%d")
conditions.append(sa.and_(
sa.extract("month", Transaction.date) == txn_date.month,
sa.extract("day", Transaction.date) == txn_date.day))
sa.extract("month", Voucher.date) == voucher_date.month,
sa.extract("day", Voucher.date) == voucher_date.day))
except ValueError:
pass
return sa.select(Transaction.id).filter(sa.or_(*conditions))
return sa.select(Voucher.id).filter(sa.or_(*conditions))
class PageParams(BasePageParams):

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, Transaction, JournalEntry
from accounting.models import Currency, Account, Voucher, JournalEntry
from accounting.report.period import Period, PeriodChooser
from accounting.report.utils.base_page_params import BasePageParams
from accounting.report.utils.base_report import BaseReport
@ -180,14 +180,14 @@ class TrialBalance(BaseReport):
conditions: list[sa.BinaryExpression] \
= [JournalEntry.currency_code == self.__currency.code]
if self.__period.start is not None:
conditions.append(Transaction.date >= self.__period.start)
conditions.append(Voucher.date >= self.__period.start)
if self.__period.end is not None:
conditions.append(Transaction.date <= self.__period.end)
conditions.append(Voucher.date <= self.__period.end)
balance_func: sa.Function = sa.func.sum(sa.case(
(JournalEntry.is_debit, JournalEntry.amount),
else_=-JournalEntry.amount)).label("balance")
select_balances: sa.Select = sa.select(Account.id, balance_func)\
.join(Transaction).join(Account)\
.join(Voucher).join(Account)\
.filter(*conditions)\
.group_by(Account.id)\
.order_by(Account.base_code, Account.no)