Compare commits

..

No commits in common. "b28d446d07d0777f79782511f4523e5f3263aa3e" and "329027969aa140fb2651480a4f2c601c8ac513ba" have entirely different histories.

14 changed files with 75 additions and 118 deletions

View File

@ -17,7 +17,7 @@
[metadata]
name = mia-accounting-flask
version = 0.3.1
version = 0.3.0
author = imacat
author_email = imacat@mail.imacat.idv.tw
description = The Mia! Accounting Flask project.

View File

@ -530,17 +530,17 @@ function filterAccountOptions(prefix) {
const more = document.getElementById(prefix + "-more");
const queryNoResult = document.getElementById(prefix + "-option-no-result");
const codesInUse = getAccountCodeUsedInForm();
let shouldAnyShow = false;
let hasAnyMatched = false;
options.forEach(function (option) {
const shouldShow = shouldAccountOptionShow(option, more, codesInUse, query);
if (shouldShow) {
const isMatched = shouldAccountOptionShow(option, more, codesInUse, query);
if (isMatched) {
option.classList.remove("d-none");
shouldAnyShow = true;
hasAnyMatched = true;
} else {
option.classList.add("d-none");
}
});
if (!shouldAnyShow && more.classList.contains("d-none")) {
if (!hasAnyMatched) {
optionList.classList.add("d-none");
queryNoResult.classList.remove("d-none");
} else {

View File

@ -21,13 +21,6 @@ First written: 2023/2/26
#}
{% extends "accounting/transaction/include/detail.html" %}
{% block to_transfer %}
<a class="btn btn-primary" href="{{ url_for("accounting.transaction.edit", txn=obj)|accounting_txn_to_transfer|accounting_inherit_next }}">
<i class="fa-solid fa-bars-staggered"></i>
{{ A_("To Transfer") }}
</a>
{% endblock %}
{% block transaction_currencies %}
{% for currency in obj.currencies %}
<div class="mb-3">
@ -50,7 +43,7 @@ First written: 2023/2/26
{% endfor %}
<li class="list-group-item accounting-transaction-entry accounting-transaction-entry-total">
<div class="d-flex justify-content-between">
<div>{{ A_("Total") }}</div>
<div>{{ _("Total") }}</div>
<div>{{ currency.debit_total|accounting_txn_format_amount }}</div>
</div>
</li>

View File

@ -55,7 +55,7 @@ First written: 2023/2/25
account_text = entry_form.account_text,
summary_data = "" if entry_form.summary.data is none else entry_form.summary.data,
summary_errors = entry_form.summary.errors,
amount_data = "" if entry_form.amount.data is none else entry_form.amount.data|accounting_txn_format_amount_input,
amount_data = "" if entry_form.amount.data is none else entry_form.amount.data,
amount_errors = entry_form.amount.errors,
amount_text = entry_form.amount.data|accounting_txn_format_amount,
entry_errors = entry_form.all_errors %}

View File

@ -41,7 +41,6 @@ First written: 2023/2/26
{{ A_("Order") }}
</a>
{% if accounting_can_edit() %}
{% block to_transfer %}{% endblock %}
<button class="btn btn-danger" type="button" data-bs-toggle="modal" data-bs-target="#accounting-delete-modal">
<i class="fa-solid fa-trash"></i>
{{ A_("Delete") }}

View File

@ -34,7 +34,7 @@ First written: 2023/2/25
<div id="accounting-currency-{{ currency_index }}-{{ entry_type }}-{{ entry_index }}-account-text" class="small">{{ account_text }}</div>
<div id="accounting-currency-{{ currency_index }}-{{ entry_type }}-{{ entry_index }}-summary-text">{{ "" if summary_data is none else summary_data }}</div>
</div>
<div><span id="accounting-currency-{{ currency_index }}-{{ entry_type }}-{{ entry_index }}-amount-text" class="badge rounded-pill bg-primary">{{ amount_text }}</span></div>
<div><span id="accounting-currency-{{ currency_index }}-{{ entry_type }}-{{ entry_index }}-amount-text" class="badge rounded-pill bg-primary">{{ amount_data }}</span></div>
</div>
<div id="accounting-currency-{{ currency_index }}-{{ entry_type }}-{{ entry_index }}-error" class="invalid-feedback">{% if entry_errors %}{{ entry_errors[0] }}{% endif %}</div>
</div>

View File

@ -21,13 +21,6 @@ First written: 2023/2/26
#}
{% extends "accounting/transaction/include/detail.html" %}
{% block to_transfer %}
<a class="btn btn-primary" href="{{ url_for("accounting.transaction.edit", txn=obj)|accounting_txn_to_transfer|accounting_inherit_next }}">
<i class="fa-solid fa-bars-staggered"></i>
{{ A_("To Transfer") }}
</a>
{% endblock %}
{% block transaction_currencies %}
{% for currency in obj.currencies %}
<div class="mb-3">
@ -50,7 +43,7 @@ First written: 2023/2/26
{% endfor %}
<li class="list-group-item accounting-transaction-entry accounting-transaction-entry-total">
<div class="d-flex justify-content-between">
<div>{{ A_("Total") }}</div>
<div>{{ _("Total") }}</div>
<div>{{ currency.debit_total|accounting_txn_format_amount }}</div>
</div>
</li>

View File

@ -55,7 +55,7 @@ First written: 2023/2/25
account_text = entry_form.account_text,
summary_data = "" if entry_form.summary.data is none else entry_form.summary.data,
summary_errors = entry_form.summary.errors,
amount_data = "" if entry_form.amount.data is none else entry_form.amount.data|accounting_txn_format_amount_input,
amount_data = "" if entry_form.amount.data is none else entry_form.amount.data,
amount_errors = entry_form.amount.errors,
amount_text = entry_form.amount.data|accounting_txn_format_amount,
entry_errors = entry_form.all_errors %}

View File

@ -46,7 +46,7 @@ First written: 2023/2/26
{% endfor %}
<li class="list-group-item accounting-transaction-entry accounting-transaction-entry-total">
<div class="d-flex justify-content-between">
<div>{{ A_("Total") }}</div>
<div>{{ _("Total") }}</div>
<div>{{ currency.debit_total|accounting_txn_format_amount }}</div>
</div>
</li>
@ -72,7 +72,7 @@ First written: 2023/2/26
{% endfor %}
<li class="list-group-item accounting-transaction-entry accounting-transaction-entry-total">
<div class="d-flex justify-content-between">
<div>{{ A_("Total") }}</div>
<div>{{ _("Total") }}</div>
<div>{{ currency.debit_total|accounting_txn_format_amount }}</div>
</div>
</li>

View File

@ -97,7 +97,7 @@ First written: 2023/2/25
account_text = entry_form.account_text,
summary_data = "" if entry_form.summary.data is none else entry_form.summary.data,
summary_errors = entry_form.summary.errors,
amount_data = "" if entry_form.amount.data is none else entry_form.amount.data|accounting_txn_format_amount_input,
amount_data = "" if entry_form.amount.data is none else entry_form.amount.data,
amount_errors = entry_form.amount.errors,
amount_text = entry_form.amount.data|accounting_txn_format_amount,
entry_errors = entry_form.all_errors %}

View File

@ -284,11 +284,13 @@ class TransactionForm(FlaskForm):
self.__set_date(obj, self.date.data)
obj.note = self.note.data
entries: list[JournalEntry] = obj.entries
collector_cls: t.Type[JournalEntryCollector] = self.collector
collector: collector_cls = collector_cls(self, obj)
collector: collector_cls = collector_cls(self, obj.id, entries,
obj.currencies)
collector.collect()
to_delete: set[int] = {x.id for x in obj.entries
to_delete: set[int] = {x.id for x in entries
if x.id not in collector.to_keep}
if len(to_delete) > 0:
JournalEntry.query.filter(JournalEntry.id.in_(to_delete)).delete()
@ -371,24 +373,27 @@ T = t.TypeVar("T", bound=TransactionForm)
class JournalEntryCollector(t.Generic[T], ABC):
"""The journal entry collector."""
def __init__(self, form: T, obj: Transaction):
def __init__(self, form: T, txn_id: int, entries: list[JournalEntry],
currencies: list[TransactionCurrency]):
"""Constructs the journal entry collector.
:param form: The transaction form.
:param obj: The transaction.
:param txn_id: The transaction ID.
:param entries: The existing journal entries.
:param currencies: The currencies in the transaction.
"""
self.form: T = form
"""The transaction form."""
self.__obj: Transaction = obj
"""The transaction object."""
self.__entries: list[JournalEntry] = list(obj.entries)
self.entries: list[JournalEntry] = entries
"""The existing journal entries."""
self.txn_id: int = txn_id
"""The transaction ID."""
self.__entries_by_id: dict[int, JournalEntry] \
= {x.id: x for x in self.__entries}
= {x.id: x for x in entries}
"""A dictionary from the entry ID to their entries."""
self.__no_by_id: dict[int, int] = {x.id: x.no for x in self.__entries}
self.__no_by_id: dict[int, int] = {x.id: x.no for x in entries}
"""A dictionary from the entry number to their entries."""
self.__currencies: list[TransactionCurrency] = obj.currencies
self.__currencies: list[TransactionCurrency] = currencies
"""The currencies in the transaction."""
self._debit_no: int = 1
"""The number index for the debit entries."""
@ -415,6 +420,7 @@ class JournalEntryCollector(t.Generic[T], ABC):
"""
entry: JournalEntry | None = self.__entries_by_id.get(form.eid.data)
if entry is not None:
self.to_keep.add(entry.id)
entry.currency_code = currency_code
form.populate_obj(entry)
entry.no = no
@ -422,12 +428,12 @@ class JournalEntryCollector(t.Generic[T], ABC):
self.form.is_modified = True
else:
entry = JournalEntry()
entry.transaction_id = self.txn_id
entry.currency_code = currency_code
form.populate_obj(entry)
entry.no = no
self.__obj.entries.append(entry)
db.session.add(entry)
self.form.is_modified = True
self.to_keep.add(entry.id)
def _make_cash_entry(self, forms: list[JournalEntryForm], is_debit: bool,
currency_code: str, no: int) -> None:
@ -441,13 +447,14 @@ class JournalEntryCollector(t.Generic[T], ABC):
:param no: The number of the entry.
:return: None.
"""
candidates: list[JournalEntry] = [x for x in self.__entries
candidates: list[JournalEntry] = [x for x in self.entries
if x.is_debit == is_debit
and x.currency_code == currency_code]
entry: JournalEntry
if len(candidates) > 0:
candidates.sort(key=lambda x: x.no)
entry = candidates[0]
self.to_keep.add(entry.id)
entry.account_id = Account.cash().id
entry.summary = None
entry.amount = sum([x.amount.data for x in forms])
@ -457,15 +464,15 @@ class JournalEntryCollector(t.Generic[T], ABC):
else:
entry = JournalEntry()
entry.id = new_id(JournalEntry)
entry.transaction_id = self.txn_id
entry.is_debit = is_debit
entry.currency_code = currency_code
entry.account_id = Account.cash().id
entry.summary = None
entry.amount = sum([x.amount.data for x in forms])
entry.no = no
self.__obj.entries.append(entry)
db.session.add(entry)
self.form.is_modified = True
self.to_keep.add(entry.id)
def _sort_entry_forms(self, forms: list[JournalEntryForm]) -> None:
"""Sorts the journal entry forms.

View File

@ -40,28 +40,13 @@ def with_type(uri: str) -> str:
return uri
uri_p: ParseResult = urlparse(uri)
params: list[tuple[str, str]] = parse_qsl(uri_p.query)
params = [x for x in params if x[0] != "as"]
params = [x for x in params if x[0] != "next"]
params.append(("as", request.args["as"]))
parts: list[str] = list(uri_p)
parts[4] = urlencode(params)
return urlunparse(parts)
def to_transfer(uri: str) -> str:
"""Adds the transfer transaction type to the URI.
:param uri: The URI.
:return: The result URL, with the transfer transaction type added.
"""
uri_p: ParseResult = urlparse(uri)
params: list[tuple[str, str]] = parse_qsl(uri_p.query)
params = [x for x in params if x[0] != "as"]
params.append(("as", "transfer"))
parts: list[str] = list(uri_p)
parts[4] = urlencode(params)
return urlunparse(parts)
def format_amount(value: Decimal | None) -> str:
"""Formats an amount for readability.
@ -75,17 +60,6 @@ def format_amount(value: Decimal | None) -> str:
return "{:,}".format(whole) + str(frac)[1:]
def format_amount_input(value: Decimal) -> str:
"""Format an amount for an input value.
:param value: The amount.
:return: The formatted amount text for an input value.
"""
whole: int = int(value)
frac: Decimal = (value - whole).normalize()
return str(whole) + str(frac)[1:]
def format_date(value: date) -> str:
"""Formats a date to be human-friendly.

View File

@ -34,19 +34,15 @@ 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 .dispatcher import TransactionType, get_txn_type, TXN_TYPE_OBJ
from .template import with_type, format_amount, format_date, text2html, \
currency_options, default_currency_code
from .forms import sort_transactions_in, TransactionReorderForm
from .query import get_transaction_query
from .template import with_type, to_transfer, format_amount, \
format_amount_input, format_date, text2html, currency_options, \
default_currency_code
bp: Blueprint = Blueprint("transaction", __name__)
"""The view blueprint for the transaction management."""
bp.add_app_template_filter(with_type, "accounting_txn_with_type")
bp.add_app_template_filter(to_transfer, "accounting_txn_to_transfer")
bp.add_app_template_filter(format_amount, "accounting_txn_format_amount")
bp.add_app_template_filter(format_amount_input,
"accounting_txn_format_amount_input")
bp.add_app_template_filter(format_date, "accounting_txn_format_date")
bp.add_app_template_filter(text2html, "accounting_txn_text2html")
bp.add_app_template_global(currency_options, "accounting_txn_currency_options")

View File

@ -8,8 +8,8 @@ msgid ""
msgstr ""
"Project-Id-Version: Mia! Accounting Flask 0.0.0\n"
"Report-Msgid-Bugs-To: imacat@mail.imacat.idv.tw\n"
"POT-Creation-Date: 2023-02-27 18:59+0800\n"
"PO-Revision-Date: 2023-02-27 18:59+0800\n"
"POT-Creation-Date: 2023-02-27 15:28+0800\n"
"PO-Revision-Date: 2023-02-27 15:29+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"
@ -61,23 +61,23 @@ msgstr "逐筆核銷"
msgid "The account is added successfully"
msgstr "科目加好了。"
#: src/accounting/account/views.py:141
#: src/accounting/account/views.py:142
msgid "The account was not modified."
msgstr "科目未異動。"
#: src/accounting/account/views.py:146
#: src/accounting/account/views.py:148
msgid "The account is updated successfully."
msgstr "科目存好了。"
#: src/accounting/account/views.py:162
#: src/accounting/account/views.py:165
msgid "The account is deleted successfully."
msgstr "科目刪掉了"
#: src/accounting/account/views.py:189 src/accounting/transaction/views.py:214
#: src/accounting/account/views.py:192 src/accounting/transaction/views.py:210
msgid "The order was not modified."
msgstr "順序未異動。"
#: src/accounting/account/views.py:192 src/accounting/transaction/views.py:217
#: src/accounting/account/views.py:195 src/accounting/transaction/views.py:213
msgid "The order is updated successfully."
msgstr "順序存好了。"
@ -110,15 +110,15 @@ msgstr "請填上名稱。"
msgid "The currency is added successfully"
msgstr "貨幣加好了。"
#: src/accounting/currency/views.py:144
#: src/accounting/currency/views.py:145
msgid "The currency was not modified."
msgstr "貨幣未異動。"
#: src/accounting/currency/views.py:149
#: src/accounting/currency/views.py:151
msgid "The currency is updated successfully."
msgstr "貨幣存好了。"
#: src/accounting/currency/views.py:164
#: src/accounting/currency/views.py:167
msgid "The currency is deleted successfully."
msgstr "貨幣刪掉了"
@ -185,7 +185,7 @@ msgstr "次序"
#: src/accounting/templates/accounting/account/detail.html:46
#: src/accounting/templates/accounting/currency/detail.html:42
#: src/accounting/templates/accounting/transaction/include/detail.html:47
#: src/accounting/templates/accounting/transaction/include/detail.html:46
msgid "Delete"
msgstr "刪除"
@ -198,7 +198,7 @@ msgstr "科目刪除確認"
#: src/accounting/templates/accounting/currency/detail.html:66
#: src/accounting/templates/accounting/transaction/include/credit-account-modal.html:27
#: src/accounting/templates/accounting/transaction/include/debit-account-modal.html:27
#: src/accounting/templates/accounting/transaction/include/detail.html:71
#: src/accounting/templates/accounting/transaction/include/detail.html:70
#: src/accounting/templates/accounting/transaction/include/entry-form-modal.html:28
msgid "Close"
msgstr "關閉"
@ -212,26 +212,26 @@ msgstr "你確定要刪掉這個科目嗎?"
#: src/accounting/templates/accounting/currency/detail.html:72
#: src/accounting/templates/accounting/transaction/include/credit-account-modal.html:49
#: src/accounting/templates/accounting/transaction/include/debit-account-modal.html:49
#: src/accounting/templates/accounting/transaction/include/detail.html:77
#: src/accounting/templates/accounting/transaction/include/detail.html:76
#: src/accounting/templates/accounting/transaction/include/entry-form-modal.html:52
msgid "Cancel"
msgstr "取消"
#: src/accounting/templates/accounting/account/detail.html:77
#: src/accounting/templates/accounting/currency/detail.html:73
#: src/accounting/templates/accounting/transaction/include/detail.html:78
#: src/accounting/templates/accounting/transaction/include/detail.html:77
msgid "Confirm"
msgstr "確定"
#: src/accounting/templates/accounting/account/detail.html:94
#: src/accounting/templates/accounting/currency/detail.html:85
#: src/accounting/templates/accounting/transaction/include/detail.html:107
#: src/accounting/templates/accounting/transaction/include/detail.html:106
msgid "Created"
msgstr "建檔"
#: src/accounting/templates/accounting/account/detail.html:95
#: src/accounting/templates/accounting/currency/detail.html:86
#: src/accounting/templates/accounting/transaction/include/detail.html:108
#: src/accounting/templates/accounting/transaction/include/detail.html:107
msgid "Updated"
msgstr "更新"
@ -375,23 +375,23 @@ msgstr "代碼"
msgid "Name"
msgstr "名稱"
#: src/accounting/templates/accounting/include/nav.html:27
#: src/accounting/templates/accounting/include/nav.html:26
msgid "Accounting"
msgstr "記帳"
#: src/accounting/templates/accounting/include/nav.html:33
#: src/accounting/templates/accounting/include/nav.html:32
msgid "Transactions"
msgstr "傳票"
#: src/accounting/templates/accounting/include/nav.html:39
#: src/accounting/templates/accounting/include/nav.html:38
msgid "Accounts"
msgstr "科目"
#: src/accounting/templates/accounting/include/nav.html:45
#: src/accounting/templates/accounting/include/nav.html:44
msgid "Base Accounts"
msgstr "基本科目"
#: src/accounting/templates/accounting/include/nav.html:51
#: src/accounting/templates/accounting/include/nav.html:50
msgid "Currencies"
msgstr "貨幣"
@ -425,22 +425,17 @@ msgstr "%(date)s的傳票"
msgid "Add a New Cash Expense Transaction"
msgstr "新增現金支出傳票"
#: src/accounting/templates/accounting/transaction/expense/detail.html:27
#: src/accounting/templates/accounting/transaction/income/detail.html:27
msgid "To Transfer"
msgstr "改轉帳"
#: src/accounting/templates/accounting/transaction/expense/detail.html:37
#: src/accounting/templates/accounting/transaction/expense/detail.html:30
#: src/accounting/templates/accounting/transaction/expense/include/form-currency-item.html:45
#: src/accounting/templates/accounting/transaction/include/form.html:52
#: src/accounting/templates/accounting/transaction/income/detail.html:37
#: src/accounting/templates/accounting/transaction/income/detail.html:30
#: src/accounting/templates/accounting/transaction/income/include/form-currency-item.html:45
msgid "Content"
msgstr "內容"
#: src/accounting/templates/accounting/transaction/expense/detail.html:53
#: src/accounting/templates/accounting/transaction/expense/detail.html:46
#: src/accounting/templates/accounting/transaction/expense/include/form-currency-item.html:68
#: src/accounting/templates/accounting/transaction/income/detail.html:53
#: src/accounting/templates/accounting/transaction/income/detail.html:46
#: src/accounting/templates/accounting/transaction/income/include/form-currency-item.html:68
#: src/accounting/templates/accounting/transaction/transfer/detail.html:49
#: src/accounting/templates/accounting/transaction/transfer/detail.html:75
@ -483,11 +478,11 @@ msgstr "更多…"
msgid "Select Debit Account"
msgstr "選擇借方科目"
#: src/accounting/templates/accounting/transaction/include/detail.html:70
#: src/accounting/templates/accounting/transaction/include/detail.html:69
msgid "Delete Transaction Confirmation"
msgstr "傳票刪除確認"
#: src/accounting/templates/accounting/transaction/include/detail.html:74
#: src/accounting/templates/accounting/transaction/include/detail.html:73
msgid "Do you really want to delete this transaction?"
msgstr "你確定要刪掉這張傳票嗎?"
@ -557,39 +552,39 @@ msgstr "科目不是借方科目。"
msgid "This account is not for credit entries."
msgstr "科目不是貸方科目。"
#: src/accounting/transaction/template.py:97
#: src/accounting/transaction/template.py:71
msgid "Today"
msgstr "今天"
#: src/accounting/transaction/template.py:99
#: src/accounting/transaction/template.py:73
msgid "Yesterday"
msgstr "昨天"
#: src/accounting/transaction/template.py:101
#: src/accounting/transaction/template.py:75
msgid "Tomorrow"
msgstr "明天"
#: src/accounting/transaction/template.py:105
#: src/accounting/transaction/template.py:79
msgid "The day before yesterday"
msgstr "前天"
#: src/accounting/transaction/template.py:107
#: src/accounting/transaction/template.py:81
msgid "The day after tomorrow"
msgstr "後天"
#: src/accounting/transaction/views.py:108
#: src/accounting/transaction/views.py:104
msgid "The transaction is added successfully"
msgstr "傳票加好了。"
#: src/accounting/transaction/views.py:162
#: src/accounting/transaction/views.py:158
msgid "The transaction was not modified."
msgstr "傳票未異動。"
#: src/accounting/transaction/views.py:167
#: src/accounting/transaction/views.py:163
msgid "The transaction is updated successfully."
msgstr "傳票存好了。"
#: src/accounting/transaction/views.py:183
#: src/accounting/transaction/views.py:179
msgid "The transaction is deleted successfully."
msgstr "傳票刪掉了"