Renamed "summary" to "description" in the voucher line item.
This commit is contained in:
@ -653,8 +653,8 @@ class VoucherLineItem(db.Model):
|
||||
"""The account ID."""
|
||||
account = db.relationship(Account, back_populates="line_items", lazy=False)
|
||||
"""The account."""
|
||||
summary = db.Column(db.String, nullable=True)
|
||||
"""The summary."""
|
||||
description = db.Column(db.String, nullable=True)
|
||||
"""The description."""
|
||||
amount = db.Column(db.Numeric(14, 2), nullable=False)
|
||||
"""The amount."""
|
||||
|
||||
@ -666,10 +666,10 @@ class VoucherLineItem(db.Model):
|
||||
if not hasattr(self, "__str"):
|
||||
from accounting.template_filters import format_date, format_amount
|
||||
setattr(self, "__str",
|
||||
gettext("%(date)s %(summary)s %(amount)s",
|
||||
gettext("%(date)s %(description)s %(amount)s",
|
||||
date=format_date(self.voucher.date),
|
||||
summary="" if self.summary is None
|
||||
else self.summary,
|
||||
description="" if self.description is None
|
||||
else self.description,
|
||||
amount=format_amount(self.amount)))
|
||||
return getattr(self, "__str")
|
||||
|
||||
@ -753,8 +753,8 @@ class VoucherLineItem(db.Model):
|
||||
return str(whole) + str(abs(frac))[1:]
|
||||
|
||||
voucher_day: date = self.voucher.date
|
||||
summary: str = "" if self.summary is None else self.summary
|
||||
return ([summary],
|
||||
description: str = "" if self.description is None else self.description
|
||||
return ([description],
|
||||
[str(voucher_day.year),
|
||||
"{}/{}".format(voucher_day.year, voucher_day.month),
|
||||
"{}/{}".format(voucher_day.month, voucher_day.day),
|
||||
|
@ -57,8 +57,8 @@ class ReportLineItem:
|
||||
"""The date."""
|
||||
self.account: Account | None = None
|
||||
"""The account."""
|
||||
self.summary: str | None = None
|
||||
"""The summary."""
|
||||
self.description: str | None = None
|
||||
"""The description."""
|
||||
self.income: Decimal | None = None
|
||||
"""The income amount."""
|
||||
self.expense: Decimal | None = None
|
||||
@ -72,7 +72,7 @@ class ReportLineItem:
|
||||
if line_item is not None:
|
||||
self.date = line_item.voucher.date
|
||||
self.account = line_item.account
|
||||
self.summary = line_item.summary
|
||||
self.description = line_item.description
|
||||
self.income = None if line_item.is_debit else line_item.amount
|
||||
self.expense = line_item.amount if line_item.is_debit else None
|
||||
self.note = line_item.voucher.note
|
||||
@ -131,7 +131,7 @@ class LineItemCollector:
|
||||
line_item.is_brought_forward = True
|
||||
line_item.date = self.__period.start
|
||||
line_item.account = Account.accumulated_change()
|
||||
line_item.summary = gettext("Brought forward")
|
||||
line_item.description = gettext("Brought forward")
|
||||
if balance > 0:
|
||||
line_item.income = balance
|
||||
elif balance < 0:
|
||||
@ -184,7 +184,7 @@ class LineItemCollector:
|
||||
return None
|
||||
line_item: ReportLineItem = ReportLineItem()
|
||||
line_item.is_total = True
|
||||
line_item.summary = gettext("Total")
|
||||
line_item.description = gettext("Total")
|
||||
line_item.income = sum([x.income for x in self.line_items
|
||||
if x.income is not None])
|
||||
line_item.expense = sum([x.expense for x in self.line_items
|
||||
@ -215,7 +215,7 @@ class CSVRow(BaseCSVRow):
|
||||
|
||||
def __init__(self, voucher_date: date | str | None,
|
||||
account: str | None,
|
||||
summary: str | None,
|
||||
description: str | None,
|
||||
income: str | Decimal | None,
|
||||
expense: str | Decimal | None,
|
||||
balance: str | Decimal | None,
|
||||
@ -224,7 +224,7 @@ class CSVRow(BaseCSVRow):
|
||||
|
||||
:param voucher_date: The voucher date.
|
||||
:param account: The account.
|
||||
:param summary: The summary.
|
||||
:param description: The description.
|
||||
:param income: The income.
|
||||
:param expense: The expense.
|
||||
:param balance: The balance.
|
||||
@ -234,8 +234,8 @@ class CSVRow(BaseCSVRow):
|
||||
"""The date."""
|
||||
self.account: str | None = account
|
||||
"""The account."""
|
||||
self.summary: str | None = summary
|
||||
"""The summary."""
|
||||
self.description: str | None = description
|
||||
"""The description."""
|
||||
self.income: str | Decimal | None = income
|
||||
"""The income."""
|
||||
self.expense: str | Decimal | None = expense
|
||||
@ -251,7 +251,7 @@ class CSVRow(BaseCSVRow):
|
||||
|
||||
:return: The values of the row.
|
||||
"""
|
||||
return [self.date, self.account, self.summary,
|
||||
return [self.date, self.account, self.description,
|
||||
self.income, self.expense, self.balance, self.note]
|
||||
|
||||
|
||||
@ -405,18 +405,18 @@ class IncomeExpenses(BaseReport):
|
||||
:return: The CSV rows.
|
||||
"""
|
||||
rows: list[CSVRow] = [CSVRow(gettext("Date"), gettext("Account"),
|
||||
gettext("Summary"), gettext("Income"),
|
||||
gettext("Description"), gettext("Income"),
|
||||
gettext("Expense"), gettext("Balance"),
|
||||
gettext("Note"))]
|
||||
if self.__brought_forward is not None:
|
||||
rows.append(CSVRow(self.__brought_forward.date,
|
||||
str(self.__brought_forward.account).title(),
|
||||
self.__brought_forward.summary,
|
||||
self.__brought_forward.description,
|
||||
self.__brought_forward.income,
|
||||
self.__brought_forward.expense,
|
||||
self.__brought_forward.balance,
|
||||
None))
|
||||
rows.extend([CSVRow(x.date, str(x.account).title(), x.summary,
|
||||
rows.extend([CSVRow(x.date, str(x.account).title(), x.description,
|
||||
x.income, x.expense, x.balance, x.note)
|
||||
for x in self.__line_items])
|
||||
if self.__total is not None:
|
||||
|
@ -53,8 +53,8 @@ class ReportLineItem:
|
||||
"""The account."""
|
||||
self.account: Account = line_item.account
|
||||
"""The account."""
|
||||
self.summary: str | None = line_item.summary
|
||||
"""The summary."""
|
||||
self.description: str | None = line_item.description
|
||||
"""The description."""
|
||||
self.debit: Decimal | None = line_item.debit
|
||||
"""The debit amount."""
|
||||
self.credit: Decimal | None = line_item.credit
|
||||
@ -69,14 +69,14 @@ class CSVRow(BaseCSVRow):
|
||||
def __init__(self, voucher_date: str | date,
|
||||
currency: str,
|
||||
account: str,
|
||||
summary: str | None,
|
||||
description: str | None,
|
||||
debit: str | Decimal | None,
|
||||
credit: str | Decimal | None,
|
||||
note: str | None):
|
||||
"""Constructs a row in the CSV.
|
||||
|
||||
:param voucher_date: The voucher date.
|
||||
:param summary: The summary.
|
||||
:param description: The description.
|
||||
:param debit: The debit amount.
|
||||
:param credit: The credit amount.
|
||||
:param note: The note.
|
||||
@ -87,8 +87,8 @@ class CSVRow(BaseCSVRow):
|
||||
"""The currency."""
|
||||
self.account: str = account
|
||||
"""The account."""
|
||||
self.summary: str | None = summary
|
||||
"""The summary."""
|
||||
self.description: str | None = description
|
||||
"""The description."""
|
||||
self.debit: str | Decimal | None = debit
|
||||
"""The debit amount."""
|
||||
self.credit: str | Decimal | None = credit
|
||||
@ -102,7 +102,7 @@ class CSVRow(BaseCSVRow):
|
||||
|
||||
:return: The values of the row.
|
||||
"""
|
||||
return [self.date, self.currency, self.account, self.summary,
|
||||
return [self.date, self.currency, self.account, self.description,
|
||||
self.debit, self.credit, self.note]
|
||||
|
||||
|
||||
@ -152,11 +152,11 @@ def get_csv_rows(line_items: list[VoucherLineItem]) -> list[CSVRow]:
|
||||
:return: The CSV rows.
|
||||
"""
|
||||
rows: list[CSVRow] = [CSVRow(gettext("Date"), gettext("Currency"),
|
||||
gettext("Account"), gettext("Summary"),
|
||||
gettext("Account"), gettext("Description"),
|
||||
gettext("Debit"), gettext("Credit"),
|
||||
gettext("Note"))]
|
||||
rows.extend([CSVRow(x.voucher.date, x.currency.code,
|
||||
str(x.account).title(), x.summary,
|
||||
str(x.account).title(), x.description,
|
||||
x.debit, x.credit, x.voucher.note)
|
||||
for x in line_items])
|
||||
return rows
|
||||
|
@ -54,8 +54,8 @@ class ReportLineItem:
|
||||
"""Whether this is the total line item."""
|
||||
self.date: date | None = None
|
||||
"""The date."""
|
||||
self.summary: str | None = None
|
||||
"""The summary."""
|
||||
self.description: str | None = None
|
||||
"""The description."""
|
||||
self.debit: Decimal | None = None
|
||||
"""The debit amount."""
|
||||
self.credit: Decimal | None = None
|
||||
@ -68,7 +68,7 @@ class ReportLineItem:
|
||||
"""The URL to the voucher line item."""
|
||||
if line_item is not None:
|
||||
self.date = line_item.voucher.date
|
||||
self.summary = line_item.summary
|
||||
self.description = line_item.description
|
||||
self.debit = line_item.amount if line_item.is_debit else None
|
||||
self.credit = None if line_item.is_debit else line_item.amount
|
||||
self.note = line_item.voucher.note
|
||||
@ -126,7 +126,7 @@ class LineItemCollector:
|
||||
line_item: ReportLineItem = ReportLineItem()
|
||||
line_item.is_brought_forward = True
|
||||
line_item.date = self.__period.start
|
||||
line_item.summary = gettext("Brought forward")
|
||||
line_item.description = gettext("Brought forward")
|
||||
if balance > 0:
|
||||
line_item.debit = balance
|
||||
elif balance < 0:
|
||||
@ -163,7 +163,7 @@ class LineItemCollector:
|
||||
return None
|
||||
line_item: ReportLineItem = ReportLineItem()
|
||||
line_item.is_total = True
|
||||
line_item.summary = gettext("Total")
|
||||
line_item.description = gettext("Total")
|
||||
line_item.debit = sum([x.debit for x in self.line_items
|
||||
if x.debit is not None])
|
||||
line_item.credit = sum([x.credit for x in self.line_items
|
||||
@ -195,7 +195,7 @@ class CSVRow(BaseCSVRow):
|
||||
"""A row in the CSV."""
|
||||
|
||||
def __init__(self, voucher_date: date | str | None,
|
||||
summary: str | None,
|
||||
description: str | None,
|
||||
debit: str | Decimal | None,
|
||||
credit: str | Decimal | None,
|
||||
balance: str | Decimal | None,
|
||||
@ -203,7 +203,7 @@ class CSVRow(BaseCSVRow):
|
||||
"""Constructs a row in the CSV.
|
||||
|
||||
:param voucher_date: The voucher date.
|
||||
:param summary: The summary.
|
||||
:param description: The description.
|
||||
:param debit: The debit amount.
|
||||
:param credit: The credit amount.
|
||||
:param balance: The balance.
|
||||
@ -211,8 +211,8 @@ class CSVRow(BaseCSVRow):
|
||||
"""
|
||||
self.date: date | str | None = voucher_date
|
||||
"""The date."""
|
||||
self.summary: str | None = summary
|
||||
"""The summary."""
|
||||
self.description: str | None = description
|
||||
"""The description."""
|
||||
self.debit: str | Decimal | None = debit
|
||||
"""The debit amount."""
|
||||
self.credit: str | Decimal | None = credit
|
||||
@ -228,7 +228,7 @@ class CSVRow(BaseCSVRow):
|
||||
|
||||
:return: The values of the row.
|
||||
"""
|
||||
return [self.date, self.summary,
|
||||
return [self.date, self.description,
|
||||
self.debit, self.credit, self.balance, self.note]
|
||||
|
||||
|
||||
@ -357,17 +357,17 @@ class Ledger(BaseReport):
|
||||
|
||||
:return: The CSV rows.
|
||||
"""
|
||||
rows: list[CSVRow] = [CSVRow(gettext("Date"), gettext("Summary"),
|
||||
rows: list[CSVRow] = [CSVRow(gettext("Date"), gettext("Description"),
|
||||
gettext("Debit"), gettext("Credit"),
|
||||
gettext("Balance"), gettext("Note"))]
|
||||
if self.__brought_forward is not None:
|
||||
rows.append(CSVRow(self.__brought_forward.date,
|
||||
self.__brought_forward.summary,
|
||||
self.__brought_forward.description,
|
||||
self.__brought_forward.debit,
|
||||
self.__brought_forward.credit,
|
||||
self.__brought_forward.balance,
|
||||
None))
|
||||
rows.extend([CSVRow(x.date, x.summary,
|
||||
rows.extend([CSVRow(x.date, x.description,
|
||||
x.debit, x.credit, x.balance, x.note)
|
||||
for x in self.__line_items])
|
||||
if self.__total is not None:
|
||||
|
@ -57,7 +57,7 @@ class LineItemCollector:
|
||||
conditions: list[sa.BinaryExpression] = []
|
||||
for k in keywords:
|
||||
sub_conditions: list[sa.BinaryExpression] \
|
||||
= [VoucherLineItem.summary.contains(k),
|
||||
= [VoucherLineItem.description.contains(k),
|
||||
VoucherLineItem.account_id.in_(
|
||||
self.__get_account_condition(k)),
|
||||
VoucherLineItem.currency_code.in_(
|
||||
|
@ -1,5 +1,5 @@
|
||||
/* The Mia! Accounting Flask Project
|
||||
* summary-editor.js: The JavaScript for the summary editor
|
||||
* description-editor.js: The JavaScript for the description editor
|
||||
*/
|
||||
|
||||
/* Copyright (c) 2023 imacat.
|
||||
@ -23,10 +23,10 @@
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
* A summary editor.
|
||||
* A description editor.
|
||||
*
|
||||
*/
|
||||
class SummaryEditor {
|
||||
class DescriptionEditor {
|
||||
|
||||
/**
|
||||
* The line item editor
|
||||
@ -35,7 +35,7 @@ class SummaryEditor {
|
||||
#lineItemEditor;
|
||||
|
||||
/**
|
||||
* The summary editor form
|
||||
* The description editor form
|
||||
* @type {HTMLFormElement}
|
||||
*/
|
||||
#form;
|
||||
@ -47,7 +47,7 @@ class SummaryEditor {
|
||||
prefix;
|
||||
|
||||
/**
|
||||
* The modal of the summary editor
|
||||
* The modal of the description editor
|
||||
* @type {HTMLDivElement}
|
||||
*/
|
||||
#modal;
|
||||
@ -65,10 +65,10 @@ class SummaryEditor {
|
||||
currentTab;
|
||||
|
||||
/**
|
||||
* The summary input
|
||||
* The description input
|
||||
* @type {HTMLInputElement}
|
||||
*/
|
||||
summary;
|
||||
description;
|
||||
|
||||
/**
|
||||
* The button to the original line item selector
|
||||
@ -107,7 +107,7 @@ class SummaryEditor {
|
||||
tabPlanes = {};
|
||||
|
||||
/**
|
||||
* Constructs a summary editor.
|
||||
* Constructs a description editor.
|
||||
*
|
||||
* @param lineItemEditor {VoucherLineItemEditor} the line item editor
|
||||
* @param side {string} the side, either "debit" or "credit"
|
||||
@ -115,10 +115,10 @@ class SummaryEditor {
|
||||
constructor(lineItemEditor, side) {
|
||||
this.#lineItemEditor = lineItemEditor;
|
||||
this.side = side;
|
||||
this.prefix = "accounting-summary-editor-" + side;
|
||||
this.prefix = "accounting-description-editor-" + side;
|
||||
this.#form = document.getElementById(this.prefix);
|
||||
this.#modal = document.getElementById(this.prefix + "-modal");
|
||||
this.summary = document.getElementById(this.prefix + "-summary");
|
||||
this.description = document.getElementById(this.prefix + "-description");
|
||||
this.#offsetButton = document.getElementById(this.prefix + "-offset");
|
||||
this.number = document.getElementById(this.prefix + "-annotation-number");
|
||||
this.note = document.getElementById(this.prefix + "-annotation-note");
|
||||
@ -131,7 +131,7 @@ class SummaryEditor {
|
||||
}
|
||||
this.currentTab = this.tabPlanes.general;
|
||||
this.#initializeSuggestedAccounts();
|
||||
this.summary.onchange = () => this.#onSummaryChange();
|
||||
this.description.onchange = () => this.#onDescriptionChange();
|
||||
this.#offsetButton.onclick = () => this.#lineItemEditor.originalLineItemSelector.onOpen();
|
||||
this.#form.onsubmit = () => {
|
||||
if (this.currentTab.validate()) {
|
||||
@ -142,11 +142,11 @@ class SummaryEditor {
|
||||
}
|
||||
|
||||
/**
|
||||
* The callback when the summary input is changed.
|
||||
* The callback when the description input is changed.
|
||||
*
|
||||
*/
|
||||
#onSummaryChange() {
|
||||
this.summary.value = this.summary.value.trim();
|
||||
#onDescriptionChange() {
|
||||
this.description.value = this.description.value.trim();
|
||||
for (const tabPlane of [this.tabPlanes.bus, this.tabPlanes.travel, this.tabPlanes.general]) {
|
||||
if (tabPlane.populate()) {
|
||||
break;
|
||||
@ -209,34 +209,34 @@ class SummaryEditor {
|
||||
}
|
||||
|
||||
/**
|
||||
* Submits the summary.
|
||||
* Submits the description.
|
||||
*
|
||||
*/
|
||||
#submit() {
|
||||
bootstrap.Modal.getOrCreateInstance(this.#modal).hide();
|
||||
if (this.#selectedAccount !== null) {
|
||||
this.#lineItemEditor.saveSummaryWithAccount(this.summary.value, this.#selectedAccount.dataset.code, this.#selectedAccount.dataset.text, this.#selectedAccount.classList.contains("accounting-account-is-need-offset"));
|
||||
this.#lineItemEditor.saveDescriptionWithAccount(this.description.value, this.#selectedAccount.dataset.code, this.#selectedAccount.dataset.text, this.#selectedAccount.classList.contains("accounting-account-is-need-offset"));
|
||||
} else {
|
||||
this.#lineItemEditor.saveSummary(this.summary.value);
|
||||
this.#lineItemEditor.saveDescription(this.description.value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The callback when the summary editor is shown.
|
||||
* The callback when the description editor is shown.
|
||||
*
|
||||
*/
|
||||
onOpen() {
|
||||
this.#reset();
|
||||
this.summary.value = this.#lineItemEditor.summary === null? "": this.#lineItemEditor.summary;
|
||||
this.#onSummaryChange();
|
||||
this.description.value = this.#lineItemEditor.description === null? "": this.#lineItemEditor.description;
|
||||
this.#onDescriptionChange();
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the summary editor.
|
||||
* Resets the description editor.
|
||||
*
|
||||
*/
|
||||
#reset() {
|
||||
this.summary.value = "";
|
||||
this.description.value = "";
|
||||
for (const tabPlane of Object.values(this.tabPlanes)) {
|
||||
tabPlane.reset();
|
||||
}
|
||||
@ -244,16 +244,16 @@ class SummaryEditor {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the summary editor instances.
|
||||
* Returns the description editor instances.
|
||||
*
|
||||
* @param lineItemEditor {VoucherLineItemEditor} the line item editor
|
||||
* @return {{debit: SummaryEditor, credit: SummaryEditor}}
|
||||
* @return {{debit: DescriptionEditor, credit: DescriptionEditor}}
|
||||
*/
|
||||
static getInstances(lineItemEditor) {
|
||||
const editors = {}
|
||||
const forms = Array.from(document.getElementsByClassName("accounting-summary-editor"));
|
||||
const forms = Array.from(document.getElementsByClassName("accounting-description-editor"));
|
||||
for (const form of forms) {
|
||||
editors[form.dataset.side] = new SummaryEditor(lineItemEditor, form.dataset.side);
|
||||
editors[form.dataset.side] = new DescriptionEditor(lineItemEditor, form.dataset.side);
|
||||
}
|
||||
return editors;
|
||||
}
|
||||
@ -268,8 +268,8 @@ class SummaryEditor {
|
||||
class TabPlane {
|
||||
|
||||
/**
|
||||
* The parent summary editor
|
||||
* @type {SummaryEditor}
|
||||
* The parent description editor
|
||||
* @type {DescriptionEditor}
|
||||
*/
|
||||
editor;
|
||||
|
||||
@ -294,7 +294,7 @@ class TabPlane {
|
||||
/**
|
||||
* Constructs a tab plane.
|
||||
*
|
||||
* @param editor {SummaryEditor} the parent summary editor
|
||||
* @param editor {DescriptionEditor} the parent description editor
|
||||
*/
|
||||
constructor(editor) {
|
||||
this.editor = editor;
|
||||
@ -320,9 +320,9 @@ class TabPlane {
|
||||
reset() { throw new Error("Method not implemented."); }
|
||||
|
||||
/**
|
||||
* Populates the tab plane with the summary input.
|
||||
* Populates the tab plane with the description input.
|
||||
*
|
||||
* @return {boolean} true if the summary input matches this tab, or false otherwise
|
||||
* @return {boolean} true if the description input matches this tab, or false otherwise
|
||||
* @abstract
|
||||
*/
|
||||
populate() { throw new Error("Method not implemented."); }
|
||||
@ -383,7 +383,7 @@ class TagTabPlane extends TabPlane {
|
||||
/**
|
||||
* Constructs a tab plane.
|
||||
*
|
||||
* @param editor {SummaryEditor} the parent summary editor
|
||||
* @param editor {DescriptionEditor} the parent description editor
|
||||
* @override
|
||||
*/
|
||||
constructor(editor) {
|
||||
@ -395,7 +395,7 @@ class TagTabPlane extends TabPlane {
|
||||
this.initializeTagButtons();
|
||||
this.tag.onchange = () => {
|
||||
this.onTagChange();
|
||||
this.updateSummary();
|
||||
this.updateDescription();
|
||||
};
|
||||
}
|
||||
|
||||
@ -424,11 +424,11 @@ class TagTabPlane extends TabPlane {
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the summary according to the input in the tab plane.
|
||||
* Updates the description according to the input in the tab plane.
|
||||
*
|
||||
* @abstract
|
||||
*/
|
||||
updateSummary() { throw new Error("Method not implemented."); }
|
||||
updateDescription() { throw new Error("Method not implemented."); }
|
||||
|
||||
/**
|
||||
* Switches to the tab plane.
|
||||
@ -461,7 +461,7 @@ class TagTabPlane extends TabPlane {
|
||||
tagButton.classList.add("btn-primary");
|
||||
this.tag.value = tagButton.dataset.value;
|
||||
this.editor.filterSuggestedAccounts(tagButton);
|
||||
this.updateSummary();
|
||||
this.updateDescription();
|
||||
};
|
||||
}
|
||||
}
|
||||
@ -532,28 +532,28 @@ class GeneralTagTab extends TagTabPlane {
|
||||
};
|
||||
|
||||
/**
|
||||
* Updates the summary according to the input in the tab plane.
|
||||
* Updates the description according to the input in the tab plane.
|
||||
*
|
||||
* @override
|
||||
*/
|
||||
updateSummary() {
|
||||
const pos = this.editor.summary.value.indexOf("—");
|
||||
updateDescription() {
|
||||
const pos = this.editor.description.value.indexOf("—");
|
||||
const prefix = this.tag.value === ""? "": this.tag.value + "—";
|
||||
if (pos === -1) {
|
||||
this.editor.summary.value = prefix + this.editor.summary.value;
|
||||
this.editor.description.value = prefix + this.editor.description.value;
|
||||
} else {
|
||||
this.editor.summary.value = prefix + this.editor.summary.value.substring(pos + 1);
|
||||
this.editor.description.value = prefix + this.editor.description.value.substring(pos + 1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Populates the tab plane with the summary input.
|
||||
* Populates the tab plane with the description input.
|
||||
*
|
||||
* @return {boolean} true if the summary input matches this tab, or false otherwise
|
||||
* @return {boolean} true if the description input matches this tab, or false otherwise
|
||||
* @override
|
||||
*/
|
||||
populate() {
|
||||
const found = this.editor.summary.value.match(/^([^—]+)—/);
|
||||
const found = this.editor.description.value.match(/^([^—]+)—/);
|
||||
if (found === null) {
|
||||
return false;
|
||||
}
|
||||
@ -622,7 +622,7 @@ class GeneralTripTab extends TagTabPlane {
|
||||
/**
|
||||
* Constructs a tab plane.
|
||||
*
|
||||
* @param editor {SummaryEditor} the parent summary editor
|
||||
* @param editor {DescriptionEditor} the parent description editor
|
||||
* @override
|
||||
*/
|
||||
constructor(editor) {
|
||||
@ -635,7 +635,7 @@ class GeneralTripTab extends TagTabPlane {
|
||||
this.#directionButtons = Array.from(document.getElementsByClassName(this.prefix + "-direction"));
|
||||
this.#from.onchange = () => {
|
||||
this.#from.value = this.#from.value.trim();
|
||||
this.updateSummary();
|
||||
this.updateDescription();
|
||||
this.validateFrom();
|
||||
};
|
||||
for (const directionButton of this.#directionButtons) {
|
||||
@ -646,12 +646,12 @@ class GeneralTripTab extends TagTabPlane {
|
||||
}
|
||||
directionButton.classList.remove("btn-outline-primary");
|
||||
directionButton.classList.add("btn-primary");
|
||||
this.updateSummary();
|
||||
this.updateDescription();
|
||||
};
|
||||
}
|
||||
this.#to.onchange = () => {
|
||||
this.#to.value = this.#to.value.trim();
|
||||
this.updateSummary();
|
||||
this.updateDescription();
|
||||
this.validateTo();
|
||||
};
|
||||
}
|
||||
@ -667,11 +667,11 @@ class GeneralTripTab extends TagTabPlane {
|
||||
};
|
||||
|
||||
/**
|
||||
* Updates the summary according to the input in the tab plane.
|
||||
* Updates the description according to the input in the tab plane.
|
||||
*
|
||||
* @override
|
||||
*/
|
||||
updateSummary() {
|
||||
updateDescription() {
|
||||
let direction;
|
||||
for (const directionButton of this.#directionButtons) {
|
||||
if (directionButton.classList.contains("btn-primary")) {
|
||||
@ -679,7 +679,7 @@ class GeneralTripTab extends TagTabPlane {
|
||||
break;
|
||||
}
|
||||
}
|
||||
this.editor.summary.value = this.tag.value + "—" + this.#from.value + direction + this.#to.value;
|
||||
this.editor.description.value = this.tag.value + "—" + this.#from.value + direction + this.#to.value;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -707,13 +707,13 @@ class GeneralTripTab extends TagTabPlane {
|
||||
}
|
||||
|
||||
/**
|
||||
* Populates the tab plane with the summary input.
|
||||
* Populates the tab plane with the description input.
|
||||
*
|
||||
* @return {boolean} true if the summary input matches this tab, or false otherwise
|
||||
* @return {boolean} true if the description input matches this tab, or false otherwise
|
||||
* @override
|
||||
*/
|
||||
populate() {
|
||||
const found = this.editor.summary.value.match(/^([^—]+)—([^—→↔]+)([→↔])(.+?)(?:[*×]\d+)?(?:\([^()]+\))?$/);
|
||||
const found = this.editor.description.value.match(/^([^—]+)—([^—→↔]+)([→↔])(.+?)(?:[*×]\d+)?(?:\([^()]+\))?$/);
|
||||
if (found === null) {
|
||||
return false;
|
||||
}
|
||||
@ -834,7 +834,7 @@ class BusTripTab extends TagTabPlane {
|
||||
/**
|
||||
* Constructs a tab plane.
|
||||
*
|
||||
* @param editor {SummaryEditor} the parent summary editor
|
||||
* @param editor {DescriptionEditor} the parent description editor
|
||||
* @override
|
||||
*/
|
||||
constructor(editor) {
|
||||
@ -847,17 +847,17 @@ class BusTripTab extends TagTabPlane {
|
||||
this.#toError = document.getElementById(this.prefix + "-to-error")
|
||||
this.#route.onchange = () => {
|
||||
this.#route.value = this.#route.value.trim();
|
||||
this.updateSummary();
|
||||
this.updateDescription();
|
||||
this.validateRoute();
|
||||
};
|
||||
this.#from.onchange = () => {
|
||||
this.#from.value = this.#from.value.trim();
|
||||
this.updateSummary();
|
||||
this.updateDescription();
|
||||
this.validateFrom();
|
||||
};
|
||||
this.#to.onchange = () => {
|
||||
this.#to.value = this.#to.value.trim();
|
||||
this.updateSummary();
|
||||
this.updateDescription();
|
||||
this.validateTo();
|
||||
};
|
||||
}
|
||||
@ -873,12 +873,12 @@ class BusTripTab extends TagTabPlane {
|
||||
};
|
||||
|
||||
/**
|
||||
* Updates the summary according to the input in the tab plane.
|
||||
* Updates the description according to the input in the tab plane.
|
||||
*
|
||||
* @override
|
||||
*/
|
||||
updateSummary() {
|
||||
this.editor.summary.value = this.tag.value + "—" + this.#route.value + "—" + this.#from.value + "→" + this.#to.value;
|
||||
updateDescription() {
|
||||
this.editor.description.value = this.tag.value + "—" + this.#route.value + "—" + this.#from.value + "→" + this.#to.value;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -900,13 +900,13 @@ class BusTripTab extends TagTabPlane {
|
||||
}
|
||||
|
||||
/**
|
||||
* Populates the tab plane with the summary input.
|
||||
* Populates the tab plane with the description input.
|
||||
*
|
||||
* @return {boolean} true if the summary input matches this tab, or false otherwise
|
||||
* @return {boolean} true if the description input matches this tab, or false otherwise
|
||||
* @override
|
||||
*/
|
||||
populate() {
|
||||
const found = this.editor.summary.value.match(/^([^—]+)—([^—]+)—([^—→]+)→(.+?)(?:[*×]\d+)?(?:\([^()]+\))?$/);
|
||||
const found = this.editor.description.value.match(/^([^—]+)—([^—]+)—([^—→]+)→(.+?)(?:[*×]\d+)?(?:\([^()]+\))?$/);
|
||||
if (found === null) {
|
||||
return false;
|
||||
}
|
||||
@ -1001,7 +1001,7 @@ class RegularPaymentTab extends TabPlane {
|
||||
/**
|
||||
* Constructs a tab plane.
|
||||
*
|
||||
* @param editor {SummaryEditor} the parent summary editor
|
||||
* @param editor {DescriptionEditor} the parent description editor
|
||||
* @override
|
||||
*/
|
||||
constructor(editor) {
|
||||
@ -1033,9 +1033,9 @@ class RegularPaymentTab extends TabPlane {
|
||||
}
|
||||
|
||||
/**
|
||||
* Populates the tab plane with the summary input.
|
||||
* Populates the tab plane with the description input.
|
||||
*
|
||||
* @return {boolean} true if the summary input matches this tab, or false otherwise
|
||||
* @return {boolean} true if the description input matches this tab, or false otherwise
|
||||
* @override
|
||||
*/
|
||||
populate() {
|
||||
@ -1063,15 +1063,15 @@ class AnnotationTab extends TabPlane {
|
||||
/**
|
||||
* Constructs a tab plane.
|
||||
*
|
||||
* @param editor {SummaryEditor} the parent summary editor
|
||||
* @param editor {DescriptionEditor} the parent description editor
|
||||
* @override
|
||||
*/
|
||||
constructor(editor) {
|
||||
super(editor);
|
||||
this.editor.number.onchange = () => this.updateSummary();
|
||||
this.editor.number.onchange = () => this.updateDescription();
|
||||
this.editor.note.onchange = () => {
|
||||
this.editor.note.value = this.editor.note.value.trim();
|
||||
this.updateSummary();
|
||||
this.updateDescription();
|
||||
};
|
||||
}
|
||||
|
||||
@ -1086,20 +1086,20 @@ class AnnotationTab extends TabPlane {
|
||||
};
|
||||
|
||||
/**
|
||||
* Updates the summary according to the input in the tab plane.
|
||||
* Updates the description according to the input in the tab plane.
|
||||
*
|
||||
* @override
|
||||
*/
|
||||
updateSummary() {
|
||||
const found = this.editor.summary.value.match(/^(.*?)(?:[*×]\d+)?(?:\([^()]+\))?$/);
|
||||
updateDescription() {
|
||||
const found = this.editor.description.value.match(/^(.*?)(?:[*×]\d+)?(?:\([^()]+\))?$/);
|
||||
if (found !== null) {
|
||||
this.editor.summary.value = found[1];
|
||||
this.editor.description.value = found[1];
|
||||
}
|
||||
if (parseInt(this.editor.number.value) > 1) {
|
||||
this.editor.summary.value = this.editor.summary.value + "×" + this.editor.number.value;
|
||||
this.editor.description.value = this.editor.description.value + "×" + this.editor.number.value;
|
||||
}
|
||||
if (this.editor.note.value !== "") {
|
||||
this.editor.summary.value = this.editor.summary.value + "(" + this.editor.note.value + ")";
|
||||
this.editor.description.value = this.editor.description.value + "(" + this.editor.note.value + ")";
|
||||
}
|
||||
}
|
||||
|
||||
@ -1114,25 +1114,25 @@ class AnnotationTab extends TabPlane {
|
||||
}
|
||||
|
||||
/**
|
||||
* Populates the tab plane with the summary input.
|
||||
* Populates the tab plane with the description input.
|
||||
*
|
||||
* @return {boolean} true if the summary input matches this tab, or false otherwise
|
||||
* @return {boolean} true if the description input matches this tab, or false otherwise
|
||||
* @override
|
||||
*/
|
||||
populate() {
|
||||
const found = this.editor.summary.value.match(/^(.*?)(?:[*×](\d+))?(?:\(([^()]+)\))?$/);
|
||||
this.editor.summary.value = found[1];
|
||||
const found = this.editor.description.value.match(/^(.*?)(?:[*×](\d+))?(?:\(([^()]+)\))?$/);
|
||||
this.editor.description.value = found[1];
|
||||
if (found[2] === undefined || parseInt(found[2]) === 1) {
|
||||
this.editor.number.value = "";
|
||||
} else {
|
||||
this.editor.number.value = found[2];
|
||||
this.editor.summary.value = this.editor.summary.value + "×" + this.editor.number.value;
|
||||
this.editor.description.value = this.editor.description.value + "×" + this.editor.number.value;
|
||||
}
|
||||
if (found[3] === undefined) {
|
||||
this.editor.note.value = "";
|
||||
} else {
|
||||
this.editor.note.value = found[3];
|
||||
this.editor.summary.value = this.editor.summary.value + "(" + this.editor.note.value + ")";
|
||||
this.editor.description.value = this.editor.description.value + "(" + this.editor.note.value + ")";
|
||||
}
|
||||
return true;
|
||||
}
|
@ -244,10 +244,10 @@ class OriginalLineItem {
|
||||
accountText;
|
||||
|
||||
/**
|
||||
* The summary
|
||||
* The description
|
||||
* @type {string}
|
||||
*/
|
||||
summary;
|
||||
description;
|
||||
|
||||
/**
|
||||
* The net balance, without the offset amounts on the form
|
||||
@ -294,7 +294,7 @@ class OriginalLineItem {
|
||||
this.#currencyCode = element.dataset.currencyCode;
|
||||
this.accountCode = element.dataset.accountCode;
|
||||
this.accountText = element.dataset.accountText;
|
||||
this.summary = element.dataset.summary;
|
||||
this.description = element.dataset.description;
|
||||
this.bareNetBalance = new Decimal(element.dataset.netBalance);
|
||||
this.netBalance = this.bareNetBalance;
|
||||
this.netBalanceText = document.getElementById("accounting-original-line-item-selector-option-" + this.id + "-net-balance");
|
||||
|
@ -808,16 +808,16 @@ class LineItemSubForm {
|
||||
#accountText;
|
||||
|
||||
/**
|
||||
* The summary
|
||||
* The description
|
||||
* @type {HTMLInputElement}
|
||||
*/
|
||||
#summary;
|
||||
#description;
|
||||
|
||||
/**
|
||||
* The text display of the summary
|
||||
* The text display of the description
|
||||
* @type {HTMLDivElement}
|
||||
*/
|
||||
#summaryText;
|
||||
#descriptionText;
|
||||
|
||||
/**
|
||||
* The ID of the original line item
|
||||
@ -873,8 +873,8 @@ class LineItemSubForm {
|
||||
this.no = document.getElementById(this.#prefix + "-no");
|
||||
this.#accountCode = document.getElementById(this.#prefix + "-account-code");
|
||||
this.#accountText = document.getElementById(this.#prefix + "-account-text");
|
||||
this.#summary = document.getElementById(this.#prefix + "-summary");
|
||||
this.#summaryText = document.getElementById(this.#prefix + "-summary-text");
|
||||
this.#description = document.getElementById(this.#prefix + "-description");
|
||||
this.#descriptionText = document.getElementById(this.#prefix + "-description-text");
|
||||
this.#originalLineItemId = document.getElementById(this.#prefix + "-original-line-item-id");
|
||||
this.#originalLineItemText = document.getElementById(this.#prefix + "-original-line-item-text");
|
||||
this.#offsets = document.getElementById(this.#prefix + "-offsets");
|
||||
@ -925,12 +925,12 @@ class LineItemSubForm {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the summary.
|
||||
* Returns the description.
|
||||
*
|
||||
* @return {string|null} the summary
|
||||
* @return {string|null} the description
|
||||
*/
|
||||
getSummary() {
|
||||
return this.#summary.value === ""? null: this.#summary.value;
|
||||
getDescription() {
|
||||
return this.#description.value === ""? null: this.#description.value;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1014,8 +1014,8 @@ class LineItemSubForm {
|
||||
this.#accountCode.value = editor.accountCode === null? "": editor.accountCode;
|
||||
this.#accountCode.dataset.text = editor.accountText === null? "": editor.accountText;
|
||||
this.#accountText.innerText = editor.accountText === null? "": editor.accountText;
|
||||
this.#summary.value = editor.summary === null? "": editor.summary;
|
||||
this.#summaryText.innerText = editor.summary === null? "": editor.summary;
|
||||
this.#description.value = editor.description === null? "": editor.description;
|
||||
this.#descriptionText.innerText = editor.description === null? "": editor.description;
|
||||
this.#amount.value = editor.amount;
|
||||
this.#amountText.innerText = formatDecimal(new Decimal(editor.amount));
|
||||
this.validate();
|
||||
|
@ -89,22 +89,22 @@ class VoucherLineItemEditor {
|
||||
#originalLineItemDelete;
|
||||
|
||||
/**
|
||||
* The control of the summary
|
||||
* The control of the description
|
||||
* @type {HTMLDivElement}
|
||||
*/
|
||||
#summaryControl;
|
||||
#descriptionControl;
|
||||
|
||||
/**
|
||||
* The summary
|
||||
* The description
|
||||
* @type {HTMLDivElement}
|
||||
*/
|
||||
#summaryText;
|
||||
#descriptionText;
|
||||
|
||||
/**
|
||||
* The error message of the summary
|
||||
* The error message of the description
|
||||
* @type {HTMLDivElement}
|
||||
*/
|
||||
#summaryError;
|
||||
#descriptionError;
|
||||
|
||||
/**
|
||||
* The control of the account
|
||||
@ -185,10 +185,10 @@ class VoucherLineItemEditor {
|
||||
accountText = null;
|
||||
|
||||
/**
|
||||
* The summary
|
||||
* The description
|
||||
* @type {string|null}
|
||||
*/
|
||||
summary = null;
|
||||
description = null;
|
||||
|
||||
/**
|
||||
* The amount
|
||||
@ -197,10 +197,10 @@ class VoucherLineItemEditor {
|
||||
amount = "";
|
||||
|
||||
/**
|
||||
* The summary editors
|
||||
* @type {{debit: SummaryEditor, credit: SummaryEditor}}
|
||||
* The description editors
|
||||
* @type {{debit: DescriptionEditor, credit: DescriptionEditor}}
|
||||
*/
|
||||
#summaryEditors;
|
||||
#descriptionEditors;
|
||||
|
||||
/**
|
||||
* The account selectors
|
||||
@ -228,20 +228,20 @@ class VoucherLineItemEditor {
|
||||
this.#originalLineItemText = document.getElementById(this.#prefix + "-original-line-item");
|
||||
this.#originalLineItemError = document.getElementById(this.#prefix + "-original-line-item-error");
|
||||
this.#originalLineItemDelete = document.getElementById(this.#prefix + "-original-line-item-delete");
|
||||
this.#summaryControl = document.getElementById(this.#prefix + "-summary-control");
|
||||
this.#summaryText = document.getElementById(this.#prefix + "-summary");
|
||||
this.#summaryError = document.getElementById(this.#prefix + "-summary-error");
|
||||
this.#descriptionControl = document.getElementById(this.#prefix + "-description-control");
|
||||
this.#descriptionText = document.getElementById(this.#prefix + "-description");
|
||||
this.#descriptionError = document.getElementById(this.#prefix + "-description-error");
|
||||
this.#accountControl = document.getElementById(this.#prefix + "-account-control");
|
||||
this.#accountText = document.getElementById(this.#prefix + "-account");
|
||||
this.#accountError = document.getElementById(this.#prefix + "-account-error")
|
||||
this.#amountInput = document.getElementById(this.#prefix + "-amount");
|
||||
this.#amountError = document.getElementById(this.#prefix + "-amount-error");
|
||||
this.#summaryEditors = SummaryEditor.getInstances(this);
|
||||
this.#descriptionEditors = DescriptionEditor.getInstances(this);
|
||||
this.#accountSelectors = AccountSelector.getInstances(this);
|
||||
this.originalLineItemSelector = new OriginalLineItemSelector(this);
|
||||
this.#originalLineItemControl.onclick = () => this.originalLineItemSelector.onOpen()
|
||||
this.#originalLineItemDelete.onclick = () => this.clearOriginalLineItem();
|
||||
this.#summaryControl.onclick = () => this.#summaryEditors[this.side].onOpen();
|
||||
this.#descriptionControl.onclick = () => this.#descriptionEditors[this.side].onOpen();
|
||||
this.#accountControl.onclick = () => this.#accountSelectors[this.side].onOpen();
|
||||
this.#amountInput.onchange = () => this.#validateAmount();
|
||||
this.#element.onsubmit = () => {
|
||||
@ -270,14 +270,14 @@ class VoucherLineItemEditor {
|
||||
this.originalLineItemDate = originalLineItem.date;
|
||||
this.originalLineItemText = originalLineItem.text;
|
||||
this.#originalLineItemText.innerText = originalLineItem.text;
|
||||
this.#setEnableSummaryAccount(false);
|
||||
if (originalLineItem.summary === "") {
|
||||
this.#summaryControl.classList.remove("accounting-not-empty");
|
||||
this.#setEnableDescriptionAccount(false);
|
||||
if (originalLineItem.description === "") {
|
||||
this.#descriptionControl.classList.remove("accounting-not-empty");
|
||||
} else {
|
||||
this.#summaryControl.classList.add("accounting-not-empty");
|
||||
this.#descriptionControl.classList.add("accounting-not-empty");
|
||||
}
|
||||
this.summary = originalLineItem.summary === ""? null: originalLineItem.summary;
|
||||
this.#summaryText.innerText = originalLineItem.summary;
|
||||
this.description = originalLineItem.description === ""? null: originalLineItem.description;
|
||||
this.#descriptionText.innerText = originalLineItem.description;
|
||||
this.#accountControl.classList.add("accounting-not-empty");
|
||||
this.accountCode = originalLineItem.accountCode;
|
||||
this.accountText = originalLineItem.accountText;
|
||||
@ -300,7 +300,7 @@ class VoucherLineItemEditor {
|
||||
this.originalLineItemDate = null;
|
||||
this.originalLineItemText = null;
|
||||
this.#originalLineItemText.innerText = "";
|
||||
this.#setEnableSummaryAccount(true);
|
||||
this.#setEnableDescriptionAccount(true);
|
||||
this.#accountControl.classList.remove("accounting-not-empty");
|
||||
this.accountCode = null;
|
||||
this.accountText = null;
|
||||
@ -318,38 +318,38 @@ class VoucherLineItemEditor {
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves the summary from the summary editor.
|
||||
* Saves the description from the description editor.
|
||||
*
|
||||
* @param summary {string} the summary
|
||||
* @param description {string} the description
|
||||
*/
|
||||
saveSummary(summary) {
|
||||
if (summary === "") {
|
||||
this.#summaryControl.classList.remove("accounting-not-empty");
|
||||
saveDescription(description) {
|
||||
if (description === "") {
|
||||
this.#descriptionControl.classList.remove("accounting-not-empty");
|
||||
} else {
|
||||
this.#summaryControl.classList.add("accounting-not-empty");
|
||||
this.#descriptionControl.classList.add("accounting-not-empty");
|
||||
}
|
||||
this.summary = summary === ""? null: summary;
|
||||
this.#summaryText.innerText = summary;
|
||||
this.#validateSummary();
|
||||
this.description = description === ""? null: description;
|
||||
this.#descriptionText.innerText = description;
|
||||
this.#validateDescription();
|
||||
bootstrap.Modal.getOrCreateInstance(this.#modal).show();
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves the summary with the suggested account from the summary editor.
|
||||
* Saves the description with the suggested account from the description editor.
|
||||
*
|
||||
* @param summary {string} the summary
|
||||
* @param description {string} the description
|
||||
* @param accountCode {string} the account code
|
||||
* @param accountText {string} the account text
|
||||
* @param isAccountNeedOffset {boolean} true if the line items in the account need offset, or false otherwise
|
||||
*/
|
||||
saveSummaryWithAccount(summary, accountCode, accountText, isAccountNeedOffset) {
|
||||
saveDescriptionWithAccount(description, accountCode, accountText, isAccountNeedOffset) {
|
||||
this.isNeedOffset = isAccountNeedOffset;
|
||||
this.#accountControl.classList.add("accounting-not-empty");
|
||||
this.accountCode = accountCode;
|
||||
this.accountText = accountText;
|
||||
this.#accountText.innerText = accountText;
|
||||
this.#validateAccount();
|
||||
this.saveSummary(summary)
|
||||
this.saveDescription(description)
|
||||
}
|
||||
|
||||
/**
|
||||
@ -389,7 +389,7 @@ class VoucherLineItemEditor {
|
||||
#validate() {
|
||||
let isValid = true;
|
||||
isValid = this.#validateOriginalLineItem() && isValid;
|
||||
isValid = this.#validateSummary() && isValid;
|
||||
isValid = this.#validateDescription() && isValid;
|
||||
isValid = this.#validateAccount() && isValid;
|
||||
isValid = this.#validateAmount() && isValid
|
||||
return isValid;
|
||||
@ -408,14 +408,14 @@ class VoucherLineItemEditor {
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates the summary.
|
||||
* Validates the description.
|
||||
*
|
||||
* @return {boolean} true if valid, or false otherwise
|
||||
* @private
|
||||
*/
|
||||
#validateSummary() {
|
||||
this.#summaryText.classList.remove("is-invalid");
|
||||
this.#summaryError.innerText = "";
|
||||
#validateDescription() {
|
||||
this.#descriptionText.classList.remove("is-invalid");
|
||||
this.#descriptionError.innerText = "";
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -492,12 +492,12 @@ class VoucherLineItemEditor {
|
||||
this.originalLineItemDate = null;
|
||||
this.originalLineItemText = null;
|
||||
this.#originalLineItemText.innerText = "";
|
||||
this.#setEnableSummaryAccount(true);
|
||||
this.#summaryControl.classList.remove("accounting-not-empty");
|
||||
this.#summaryControl.classList.remove("is-invalid");
|
||||
this.summary = null;
|
||||
this.#summaryText.innerText = ""
|
||||
this.#summaryError.innerText = ""
|
||||
this.#setEnableDescriptionAccount(true);
|
||||
this.#descriptionControl.classList.remove("accounting-not-empty");
|
||||
this.#descriptionControl.classList.remove("is-invalid");
|
||||
this.description = null;
|
||||
this.#descriptionText.innerText = ""
|
||||
this.#descriptionError.innerText = ""
|
||||
this.#accountControl.classList.remove("accounting-not-empty");
|
||||
this.#accountControl.classList.remove("is-invalid");
|
||||
this.accountCode = null;
|
||||
@ -532,14 +532,14 @@ class VoucherLineItemEditor {
|
||||
this.#originalLineItemContainer.classList.remove("d-none");
|
||||
this.#originalLineItemControl.classList.add("accounting-not-empty");
|
||||
}
|
||||
this.#setEnableSummaryAccount(!lineItem.isMatched && this.originalLineItemId === null);
|
||||
this.summary = lineItem.getSummary();
|
||||
if (this.summary === null) {
|
||||
this.#summaryControl.classList.remove("accounting-not-empty");
|
||||
this.#setEnableDescriptionAccount(!lineItem.isMatched && this.originalLineItemId === null);
|
||||
this.description = lineItem.getDescription();
|
||||
if (this.description === null) {
|
||||
this.#descriptionControl.classList.remove("accounting-not-empty");
|
||||
} else {
|
||||
this.#summaryControl.classList.add("accounting-not-empty");
|
||||
this.#descriptionControl.classList.add("accounting-not-empty");
|
||||
}
|
||||
this.#summaryText.innerText = this.summary === null? "": this.summary;
|
||||
this.#descriptionText.innerText = this.description === null? "": this.description;
|
||||
if (lineItem.getAccountCode() === null) {
|
||||
this.#accountControl.classList.remove("accounting-not-empty");
|
||||
} else {
|
||||
@ -568,25 +568,25 @@ class VoucherLineItemEditor {
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the enable status of the summary and account.
|
||||
* Sets the enable status of the description and account.
|
||||
*
|
||||
* @param isEnabled {boolean} true to enable, or false otherwise
|
||||
*/
|
||||
#setEnableSummaryAccount(isEnabled) {
|
||||
#setEnableDescriptionAccount(isEnabled) {
|
||||
if (isEnabled) {
|
||||
this.#summaryControl.dataset.bsToggle = "modal";
|
||||
this.#summaryControl.dataset.bsTarget = "#accounting-summary-editor-" + this.#sideSubForm.side + "-modal";
|
||||
this.#summaryControl.classList.remove("accounting-disabled");
|
||||
this.#summaryControl.classList.add("accounting-clickable");
|
||||
this.#descriptionControl.dataset.bsToggle = "modal";
|
||||
this.#descriptionControl.dataset.bsTarget = "#accounting-description-editor-" + this.#sideSubForm.side + "-modal";
|
||||
this.#descriptionControl.classList.remove("accounting-disabled");
|
||||
this.#descriptionControl.classList.add("accounting-clickable");
|
||||
this.#accountControl.dataset.bsToggle = "modal";
|
||||
this.#accountControl.dataset.bsTarget = "#accounting-account-selector-" + this.#sideSubForm.side + "-modal";
|
||||
this.#accountControl.classList.remove("accounting-disabled");
|
||||
this.#accountControl.classList.add("accounting-clickable");
|
||||
} else {
|
||||
this.#summaryControl.dataset.bsToggle = "";
|
||||
this.#summaryControl.dataset.bsTarget = "";
|
||||
this.#summaryControl.classList.add("accounting-disabled");
|
||||
this.#summaryControl.classList.remove("accounting-clickable");
|
||||
this.#descriptionControl.dataset.bsToggle = "";
|
||||
this.#descriptionControl.dataset.bsTarget = "";
|
||||
this.#descriptionControl.classList.add("accounting-disabled");
|
||||
this.#descriptionControl.classList.remove("accounting-clickable");
|
||||
this.#accountControl.dataset.bsToggle = "";
|
||||
this.#accountControl.dataset.bsTarget = "";
|
||||
this.#accountControl.classList.add("accounting-disabled");
|
||||
|
@ -21,7 +21,7 @@ First written: 2023/3/8
|
||||
#}
|
||||
<div>{{ line_item.date|accounting_format_date }}</div>
|
||||
<div>{{ line_item.account.title|title }}</div>
|
||||
<div>{{ line_item.summary|accounting_default }}</div>
|
||||
<div>{{ line_item.description|accounting_default }}</div>
|
||||
<div class="accounting-amount">{{ line_item.income|accounting_format_amount|accounting_default }}</div>
|
||||
<div class="accounting-amount">{{ line_item.expense|accounting_format_amount|accounting_default }}</div>
|
||||
<div class="accounting-amount {% if line_item.balance < 0 %} text-danger {% endif %}">{{ line_item.balance|accounting_report_format_amount }}</div>
|
||||
|
@ -30,8 +30,8 @@ First written: 2023/3/5
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if line_item.summary %}
|
||||
<div>{{ line_item.summary }}</div>
|
||||
{% if line_item.description %}
|
||||
<div>{{ line_item.description }}</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
|
@ -20,7 +20,7 @@ Author: imacat@mail.imacat.idv.tw (imacat)
|
||||
First written: 2023/3/8
|
||||
#}
|
||||
<div>{{ line_item.date|accounting_format_date }}</div>
|
||||
<div>{{ line_item.summary|accounting_default }}</div>
|
||||
<div>{{ line_item.description|accounting_default }}</div>
|
||||
<div class="accounting-amount">{{ line_item.debit|accounting_format_amount|accounting_default }}</div>
|
||||
<div class="accounting-amount">{{ line_item.credit|accounting_format_amount|accounting_default }}</div>
|
||||
{% if report.account.is_real %}
|
||||
|
@ -25,8 +25,8 @@ First written: 2023/3/5
|
||||
{{ line_item.date|accounting_format_date }}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if line_item.summary %}
|
||||
<div>{{ line_item.summary }}</div>
|
||||
{% if line_item.description %}
|
||||
<div>{{ line_item.description }}</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
|
@ -54,7 +54,7 @@ First written: 2023/3/5
|
||||
<div class="accounting-report-table-row">
|
||||
<div>{{ A_("Date") }}</div>
|
||||
<div>{{ A_("Account") }}</div>
|
||||
<div>{{ A_("Summary") }}</div>
|
||||
<div>{{ A_("Description") }}</div>
|
||||
<div class="accounting-amount">{{ A_("Income") }}</div>
|
||||
<div class="accounting-amount">{{ A_("Expense") }}</div>
|
||||
<div class="accounting-amount">{{ A_("Balance") }}</div>
|
||||
|
@ -53,7 +53,7 @@ First written: 2023/3/4
|
||||
<div>{{ A_("Date") }}</div>
|
||||
<div>{{ A_("Currency") }}</div>
|
||||
<div>{{ A_("Account") }}</div>
|
||||
<div>{{ A_("Summary") }}</div>
|
||||
<div>{{ A_("Description") }}</div>
|
||||
<div class="accounting-amount">{{ A_("Debit") }}</div>
|
||||
<div class="accounting-amount">{{ A_("Credit") }}</div>
|
||||
</div>
|
||||
@ -67,7 +67,7 @@ First written: 2023/3/4
|
||||
<span class="d-none d-md-inline">{{ line_item.account.code }}</span>
|
||||
{{ line_item.account.title|title }}
|
||||
</div>
|
||||
<div>{{ line_item.summary|accounting_default }}</div>
|
||||
<div>{{ line_item.description|accounting_default }}</div>
|
||||
<div class="accounting-amount">{{ line_item.debit|accounting_format_amount|accounting_default }}</div>
|
||||
<div class="accounting-amount">{{ line_item.credit|accounting_format_amount|accounting_default }}</div>
|
||||
</a>
|
||||
@ -87,8 +87,8 @@ First written: 2023/3/4
|
||||
<span class="badge rounded-pill bg-info">{{ line_item.currency.code }}</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% if line_item.summary is not none %}
|
||||
<div>{{ line_item.summary }}</div>
|
||||
{% if line_item.description is not none %}
|
||||
<div>{{ line_item.description }}</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
|
@ -53,7 +53,7 @@ First written: 2023/3/5
|
||||
<div class="accounting-report-table-header">
|
||||
<div class="accounting-report-table-row">
|
||||
<div>{{ A_("Date") }}</div>
|
||||
<div>{{ A_("Summary") }}</div>
|
||||
<div>{{ A_("Description") }}</div>
|
||||
<div class="accounting-amount">{{ A_("Debit") }}</div>
|
||||
<div class="accounting-amount">{{ A_("Credit") }}</div>
|
||||
{% if report.account.is_real %}
|
||||
|
@ -50,7 +50,7 @@ First written: 2023/3/8
|
||||
<div>{{ A_("Date") }}</div>
|
||||
<div>{{ A_("Currency") }}</div>
|
||||
<div>{{ A_("Account") }}</div>
|
||||
<div>{{ A_("Summary") }}</div>
|
||||
<div>{{ A_("Description") }}</div>
|
||||
<div class="accounting-amount">{{ A_("Debit") }}</div>
|
||||
<div class="accounting-amount">{{ A_("Credit") }}</div>
|
||||
</div>
|
||||
@ -64,7 +64,7 @@ First written: 2023/3/8
|
||||
<span class="d-none d-md-inline">{{ line_item.account.code }}</span>
|
||||
{{ line_item.account.title|title }}
|
||||
</div>
|
||||
<div>{{ line_item.summary|accounting_default }}</div>
|
||||
<div>{{ line_item.description|accounting_default }}</div>
|
||||
<div class="accounting-amount">{{ line_item.debit|accounting_format_amount|accounting_default }}</div>
|
||||
<div class="accounting-amount">{{ line_item.credit|accounting_format_amount|accounting_default }}</div>
|
||||
</a>
|
||||
@ -84,8 +84,8 @@ First written: 2023/3/8
|
||||
<span class="badge rounded-pill bg-info">{{ line_item.currency.code }}</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% if line_item.summary is not none %}
|
||||
<div>{{ line_item.summary }}</div>
|
||||
{% if line_item.description is not none %}
|
||||
<div>{{ line_item.description }}</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
|
@ -49,25 +49,25 @@ First written: 2023/2/25
|
||||
{% with currency_index = currency_index,
|
||||
side = "debit",
|
||||
line_item_index = loop.index,
|
||||
line_item_id = line_item_form.eid.data,
|
||||
line_item_id = line_item_form.form.eid.data,
|
||||
only_one_line_item_form = debit_forms|length == 1,
|
||||
account_code_data = line_item_form.account_code.data|accounting_default,
|
||||
account_code_error = line_item_form.account_code.errors,
|
||||
account_text = line_item_form.account_text,
|
||||
summary_data = line_item_form.summary.data|accounting_default,
|
||||
summary_errors = line_item_form.summary.errors,
|
||||
original_line_item_id_data = line_item_form.original_line_item_id.data|accounting_default,
|
||||
original_line_item_date = line_item_form.original_line_item_date|accounting_default,
|
||||
original_line_item_text = line_item_form.original_line_item_text|accounting_default,
|
||||
is_need_offset = line_item_form.is_need_offset,
|
||||
offset_items = line_item_form.offsets,
|
||||
offset_total = line_item_form.offset_total|accounting_default("0"),
|
||||
net_balance_data = line_item_form.net_balance,
|
||||
net_balance_text = line_item_form.net_balance|accounting_format_amount,
|
||||
amount_data = line_item_form.amount.data|accounting_voucher_format_amount_input,
|
||||
amount_errors = line_item_form.amount.errors,
|
||||
amount_text = line_item_form.amount.data|accounting_format_amount,
|
||||
line_item_errors = line_item_form.all_errors %}
|
||||
account_code_data = line_item_form.form.account_code.data|accounting_default,
|
||||
account_code_error = line_item_form.form.account_code.errors,
|
||||
account_text = line_item_form.form.account_text,
|
||||
description_data = line_item_form.form.description.data|accounting_default,
|
||||
description_errors = line_item_form.form.description.errors,
|
||||
original_line_item_id_data = line_item_form.form.original_line_item_id.data|accounting_default,
|
||||
original_line_item_date = line_item_form.form.original_line_item_date|accounting_default,
|
||||
original_line_item_text = line_item_form.form.original_line_item_text|accounting_default,
|
||||
is_need_offset = line_item_form.form.is_need_offset,
|
||||
offset_items = line_item_form.form.offsets,
|
||||
offset_total = line_item_form.form.offset_total|accounting_default("0"),
|
||||
net_balance_data = line_item_form.form.net_balance,
|
||||
net_balance_text = line_item_form.form.net_balance|accounting_format_amount,
|
||||
amount_data = line_item_form.form.amount.data|accounting_voucher_format_amount_input,
|
||||
amount_errors = line_item_form.form.amount.errors,
|
||||
amount_text = line_item_form.form.amount.data|accounting_format_amount,
|
||||
line_item_errors = line_item_form.form.all_errors %}
|
||||
{% include "accounting/voucher/include/form-line-item.html" %}
|
||||
{% endwith %}
|
||||
{% endfor %}
|
||||
|
@ -47,8 +47,8 @@ First written: 2023/2/25
|
||||
{% endblock %}
|
||||
|
||||
{% block form_modals %}
|
||||
{% with summary_editor = form.summary_editor.debit %}
|
||||
{% include "accounting/voucher/include/summary-editor-modal.html" %}
|
||||
{% with description_editor = form.description_editor.debit %}
|
||||
{% include "accounting/voucher/include/description-editor-modal.html" %}
|
||||
{% endwith %}
|
||||
{% with side = "debit",
|
||||
account_options = form.debit_account_options %}
|
||||
|
@ -0,0 +1,193 @@
|
||||
{#
|
||||
The Mia! Accounting Flask Project
|
||||
description-editor-modal.html: The modal of the description editor
|
||||
|
||||
Copyright (c) 2023 imacat.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
Author: imacat@mail.imacat.idv.tw (imacat)
|
||||
First written: 2023/2/28
|
||||
#}
|
||||
<form id="accounting-description-editor-{{ description_editor.side }}" class="accounting-description-editor" data-side="{{ description_editor.side }}">
|
||||
<div id="accounting-description-editor-{{ description_editor.side }}-modal" class="modal fade" tabindex="-1" aria-labelledby="accounting-description-editor-{{ description_editor.side }}-modal-label" aria-hidden="true">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h1 class="modal-title fs-5" id="accounting-description-editor-{{ description_editor.side }}-modal-label">
|
||||
<label for="accounting-description-editor-{{ description_editor.side }}-description">{{ A_("Description") }}</label>
|
||||
</h1>
|
||||
<button class="btn-close" type="button" data-bs-toggle="modal" data-bs-target="#accounting-line-item-editor-modal" aria-label="{{ A_("Close") }}"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="d-flex justify-content-between mb-3">
|
||||
<input id="accounting-description-editor-{{ description_editor.side }}-description" class="form-control" type="text" aria-labelledby="accounting-description-editor-{{ description_editor.side }}-modal-label">
|
||||
<button id="accounting-description-editor-{{ description_editor.side }}-offset" class="btn btn-primary text-nowrap ms-2" type="button" data-bs-toggle="modal" data-bs-target="#accounting-original-line-item-selector-modal">
|
||||
{{ A_("Offset...") }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{# Tab navigation #}
|
||||
<ul class="nav nav-tabs mb-2">
|
||||
<li class="nav-item">
|
||||
<span id="accounting-description-editor-{{ description_editor.side }}-general-tab" class="nav-link active accounting-clickable" aria-current="page">
|
||||
{{ A_("General") }}
|
||||
</span>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<span id="accounting-description-editor-{{ description_editor.side }}-travel-tab" class="nav-link accounting-clickable" aria-current="false">
|
||||
{{ A_("Travel") }}
|
||||
</span>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<span id="accounting-description-editor-{{ description_editor.side }}-bus-tab" class="nav-link accounting-clickable" aria-current="false">
|
||||
{{ A_("Bus") }}
|
||||
</span>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<span id="accounting-description-editor-{{ description_editor.side }}-regular-tab" class="nav-link accounting-clickable" aria-current="false">
|
||||
{{ A_("Regular") }}
|
||||
</span>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<span id="accounting-description-editor-{{ description_editor.side }}-annotation-tab" class="nav-link accounting-clickable" aria-current="false">
|
||||
{{ A_("Annotation") }}
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
{# A general description with a tag #}
|
||||
<div id="accounting-description-editor-{{ description_editor.side }}-general-page" aria-current="page" aria-labelledby="accounting-description-editor-{{ description_editor.side }}-general-tab">
|
||||
<div class="form-floating mb-2">
|
||||
<input id="accounting-description-editor-{{ description_editor.side }}-general-tag" class="form-control" type="text" value="" placeholder=" ">
|
||||
<label class="form-label" for="accounting-description-editor-{{ description_editor.side }}-general-tag">{{ A_("Tag") }}</label>
|
||||
<div id="accounting-description-editor-{{ description_editor.side }}-general-tag-error" class="invalid-feedback"></div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
{% for tag in description_editor.general.tags %}
|
||||
<button class="btn btn-outline-primary accounting-description-editor-{{ description_editor.side }}-general-btn-tag" type="button" tabindex="-1" data-value="{{ tag.name }}" data-accounts="{{ tag.account_codes|tojson|forceescape }}">
|
||||
{{ tag }}
|
||||
</button>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{# A general trip with the origin and distination #}
|
||||
<div id="accounting-description-editor-{{ description_editor.side }}-travel-page" class="d-none" aria-current="false" aria-labelledby="accounting-description-editor-{{ description_editor.side }}-travel-tab">
|
||||
<div class="form-floating mb-2">
|
||||
<input id="accounting-description-editor-{{ description_editor.side }}-travel-tag" class="form-control" type="text" value="" placeholder=" ">
|
||||
<label class="form-label" for="accounting-description-editor-{{ description_editor.side }}-travel-tag">{{ A_("Tag") }}</label>
|
||||
<div id="accounting-description-editor-{{ description_editor.side }}-travel-tag-error" class="invalid-feedback"></div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
{% for tag in description_editor.travel.tags %}
|
||||
<button class="btn btn-outline-primary accounting-description-editor-{{ description_editor.side }}-travel-btn-tag" type="button" tabindex="-1" data-value="{{ tag.name }}" data-accounts="{{ tag.account_codes|tojson|forceescape }}">
|
||||
{{ tag }}
|
||||
</button>
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
<div class="d-flex justify-content-between mt-2">
|
||||
<div class="form-floating">
|
||||
<input id="accounting-description-editor-{{ description_editor.side }}-travel-from" class="form-control" type="text" value="" placeholder=" ">
|
||||
<label class="form-label" for="accounting-description-editor-{{ description_editor.side }}-travel-from">{{ A_("From") }}</label>
|
||||
<div id="accounting-description-editor-{{ description_editor.side }}-travel-from-error" class="invalid-feedback"></div>
|
||||
</div>
|
||||
<div class="btn-group-vertical ms-1 me-1">
|
||||
<button class="btn btn-primary accounting-description-editor-{{ description_editor.side }}-travel-direction accounting-default" type="button" tabindex="-1" data-arrow="→">→</button>
|
||||
<button class="btn btn-outline-primary accounting-description-editor-{{ description_editor.side }}-travel-direction" type="button" tabindex="-1" data-arrow="↔">↔</button>
|
||||
</div>
|
||||
<div class="form-floating">
|
||||
<input id="accounting-description-editor-{{ description_editor.side }}-travel-to" class="form-control" type="text" value="" placeholder=" ">
|
||||
<label class="form-label" for="accounting-description-editor-{{ description_editor.side }}-travel-to">{{ A_("To") }}</label>
|
||||
<div id="accounting-description-editor-{{ description_editor.side }}-travel-to-error" class="invalid-feedback"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{# A bus trip with the route name or route number, the origin and distination #}
|
||||
<div id="accounting-description-editor-{{ description_editor.side }}-bus-page" class="d-none" aria-current="false" aria-labelledby="accounting-description-editor-{{ description_editor.side }}-bus-tab">
|
||||
<div class="d-flex justify-content-between mb-2">
|
||||
<div class="form-floating me-2">
|
||||
<input id="accounting-description-editor-{{ description_editor.side }}-bus-tag" class="form-control" type="text" value="" placeholder=" ">
|
||||
<label class="form-label" for="accounting-description-editor-{{ description_editor.side }}-bus-tag">{{ A_("Tag") }}</label>
|
||||
<div id="accounting-description-editor-{{ description_editor.side }}-bus-tag-error" class="invalid-feedback"></div>
|
||||
</div>
|
||||
<div class="form-floating">
|
||||
<input id="accounting-description-editor-{{ description_editor.side }}-bus-route" class="form-control" type="text" value="" placeholder=" ">
|
||||
<label class="form-label" for="accounting-description-editor-{{ description_editor.side }}-bus-route">{{ A_("Route") }}</label>
|
||||
<div id="accounting-description-editor-{{ description_editor.side }}-bus-route-error" class="invalid-feedback"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
{% for tag in description_editor.bus.tags %}
|
||||
<button class="btn btn-outline-primary accounting-description-editor-{{ description_editor.side }}-bus-btn-tag" type="button" tabindex="-1" data-value="{{ tag.name }}" data-accounts="{{ tag.account_codes|tojson|forceescape }}">
|
||||
{{ tag }}
|
||||
</button>
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
<div class="d-flex justify-content-between mt-2">
|
||||
<div class="form-floating me-2">
|
||||
<input id="accounting-description-editor-{{ description_editor.side }}-bus-from" class="form-control" type="text" value="" placeholder=" ">
|
||||
<label class="form-label" for="accounting-description-editor-{{ description_editor.side }}-bus-from">{{ A_("From") }}</label>
|
||||
<div id="accounting-description-editor-{{ description_editor.side }}-bus-from-error" class="invalid-feedback"></div>
|
||||
</div>
|
||||
<div class="form-floating">
|
||||
<input id="accounting-description-editor-{{ description_editor.side }}-bus-to" class="form-control" type="text" value="" placeholder=" ">
|
||||
<label class="form-label" for="accounting-description-editor-{{ description_editor.side }}-bus-to">{{ A_("To") }}</label>
|
||||
<div id="accounting-description-editor-{{ description_editor.side }}-bus-to-error" class="invalid-feedback"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{# A regular income or payment #}
|
||||
<div id="accounting-description-editor-{{ description_editor.side }}-regular-page" class="d-none" aria-current="false" aria-labelledby="accounting-description-editor-{{ description_editor.side }}-regular-tab">
|
||||
{# TODO: To be done #}
|
||||
</div>
|
||||
|
||||
{# The annotation #}
|
||||
<div id="accounting-description-editor-{{ description_editor.side }}-annotation-page" class="d-none" aria-current="false" aria-labelledby="accounting-description-editor-{{ description_editor.side }}-annotation-tab">
|
||||
<div class="form-floating">
|
||||
<input id="accounting-description-editor-{{ description_editor.side }}-annotation-number" class="form-control" type="number" min="1" value="" placeholder=" ">
|
||||
<label class="form-label" for="accounting-description-editor-{{ description_editor.side }}-annotation-number">{{ A_("The number of items") }}</label>
|
||||
<div id="accounting-description-editor-{{ description_editor.side }}-annotation-number-error" class="invalid-feedback"></div>
|
||||
</div>
|
||||
|
||||
<div class="form-floating mt-2">
|
||||
<input id="accounting-description-editor-{{ description_editor.side }}-annotation-note" class="form-control" type="text" value="" placeholder=" ">
|
||||
<label class="form-label" for="accounting-description-editor-{{ description_editor.side }}-annotation-note">{{ A_("Note") }}</label>
|
||||
<div id="accounting-description-editor-{{ description_editor.side }}-annotation-note-error" class="invalid-feedback"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{# The suggested accounts #}
|
||||
<div class="mt-3">
|
||||
{% for account in description_editor.accounts %}
|
||||
<button class="btn btn-outline-primary d-none accounting-description-editor-{{ description_editor.side }}-account {% if account.is_need_offset %} accounting-account-is-need-offset {% endif %}" type="button" data-code="{{ account.code }}" data-text="{{ account }}">
|
||||
{{ account }}
|
||||
</button>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button class="btn btn-secondary" type="button" data-bs-toggle="modal" data-bs-target="#accounting-line-item-editor-modal">{{ A_("Cancel") }}</button>
|
||||
<button id="accounting-description-editor-{{ description_editor.side }}-btn-save" type="submit" class="btn btn-primary">{{ A_("Save") }}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
@ -25,8 +25,8 @@ First written: 2023/3/14
|
||||
<div class="d-flex justify-content-between">
|
||||
<div>
|
||||
<div class="small">{{ line_item.account }}</div>
|
||||
{% if line_item.summary is not none %}
|
||||
<div>{{ line_item.summary }}</div>
|
||||
{% if line_item.description is not none %}
|
||||
<div>{{ line_item.description }}</div>
|
||||
{% endif %}
|
||||
{% if line_item.original_line_item %}
|
||||
<div class="fst-italic small accounting-original-line-item">
|
||||
|
@ -27,13 +27,13 @@ First written: 2023/2/25
|
||||
<input id="accounting-currency-{{ currency_index }}-{{ side }}-{{ line_item_index }}-no" type="hidden" name="currency-{{ currency_index }}-{{ side }}-{{ line_item_index }}-no" value="{{ line_item_index }}">
|
||||
<input id="accounting-currency-{{ currency_index }}-{{ side }}-{{ line_item_index }}-original-line-item-id" class="accounting-original-line-item-id" type="hidden" name="currency-{{ currency_index }}-{{ side }}-{{ line_item_index }}-original_line_item_id" value="{{ original_line_item_id_data }}" data-date="{{ original_line_item_date }}" data-text="{{ original_line_item_text }}">
|
||||
<input id="accounting-currency-{{ currency_index }}-{{ side }}-{{ line_item_index }}-account-code" type="hidden" name="currency-{{ currency_index }}-{{ side }}-{{ line_item_index }}-account_code" value="{{ account_code_data }}" data-text="{{ account_text }}">
|
||||
<input id="accounting-currency-{{ currency_index }}-{{ side }}-{{ line_item_index }}-summary" type="hidden" name="currency-{{ currency_index }}-{{ side }}-{{ line_item_index }}-summary" value="{{ summary_data }}">
|
||||
<input id="accounting-currency-{{ currency_index }}-{{ side }}-{{ line_item_index }}-description" type="hidden" name="currency-{{ currency_index }}-{{ side }}-{{ line_item_index }}-description" value="{{ description_data }}">
|
||||
<input id="accounting-currency-{{ currency_index }}-{{ side }}-{{ line_item_index }}-amount" type="hidden" name="currency-{{ currency_index }}-{{ side }}-{{ line_item_index }}-amount" value="{{ amount_data }}" data-min="{{ offset_total }}">
|
||||
<div class="accounting-line-item-content">
|
||||
<div id="accounting-currency-{{ currency_index }}-{{ side }}-{{ line_item_index }}-control" class="form-control clickable d-flex justify-content-between {% if line_item_errors %} is-invalid {% endif %}" data-bs-toggle="modal" data-bs-target="#accounting-line-item-editor-modal">
|
||||
<div>
|
||||
<div id="accounting-currency-{{ currency_index }}-{{ side }}-{{ line_item_index }}-account-text" class="small">{{ account_text }}</div>
|
||||
<div id="accounting-currency-{{ currency_index }}-{{ side }}-{{ line_item_index }}-summary-text">{{ summary_data }}</div>
|
||||
<div id="accounting-currency-{{ currency_index }}-{{ side }}-{{ line_item_index }}-description-text">{{ description_data }}</div>
|
||||
<div id="accounting-currency-{{ currency_index }}-{{ side }}-{{ line_item_index }}-original-line-item-text" class="fst-italic small accounting-original-line-item {% if not original_line_item_text %} d-none {% endif %}">
|
||||
{% if original_line_item_text %}{{ A_("Offset %(item)s", item=original_line_item_text) }}{% endif %}
|
||||
</div>
|
||||
|
@ -27,7 +27,7 @@ First written: 2023/2/26
|
||||
<script src="{{ url_for("accounting.static", filename="js/voucher-line-item-editor.js") }}"></script>
|
||||
<script src="{{ url_for("accounting.static", filename="js/account-selector.js") }}"></script>
|
||||
<script src="{{ url_for("accounting.static", filename="js/original-line-item-selector.js") }}"></script>
|
||||
<script src="{{ url_for("accounting.static", filename="js/summary-editor.js") }}"></script>
|
||||
<script src="{{ url_for("accounting.static", filename="js/description-editor.js") }}"></script>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
|
@ -37,8 +37,8 @@ First written: 2023/2/25
|
||||
|
||||
<ul id="accounting-original-line-item-selector-option-list" class="list-group accounting-selector-list">
|
||||
{% for line_item in form.original_line_item_options %}
|
||||
<li id="accounting-original-line-item-selector-option-{{ line_item.id }}" class="list-group-item d-flex justify-content-between accounting-clickable accounting-original-line-item-selector-option" data-id="{{ line_item.id }}" data-date="{{ line_item.voucher.date }}" data-side="{{ "debit" if line_item.is_debit else "credit" }}" data-currency-code="{{ line_item.currency.code }}" data-account-code="{{ line_item.account_code }}" data-account-text="{{ line_item.account }}" data-summary="{{ line_item.summary|accounting_default }}" data-net-balance="{{ line_item.net_balance|accounting_voucher_format_amount_input }}" data-text="{{ line_item }}" data-query-values="{{ line_item.query_values|tojson|forceescape }}" data-bs-toggle="modal" data-bs-target="#accounting-line-item-editor-modal">
|
||||
<div>{{ line_item.voucher.date|accounting_format_date }} {{ line_item.summary|accounting_default }}</div>
|
||||
<li id="accounting-original-line-item-selector-option-{{ line_item.id }}" class="list-group-item d-flex justify-content-between accounting-clickable accounting-original-line-item-selector-option" data-id="{{ line_item.id }}" data-date="{{ line_item.voucher.date }}" data-side="{{ "debit" if line_item.is_debit else "credit" }}" data-currency-code="{{ line_item.currency.code }}" data-account-code="{{ line_item.account_code }}" data-account-text="{{ line_item.account }}" data-description="{{ line_item.description|accounting_default }}" data-net-balance="{{ line_item.net_balance|accounting_voucher_format_amount_input }}" data-text="{{ line_item }}" data-query-values="{{ line_item.query_values|tojson|forceescape }}" data-bs-toggle="modal" data-bs-target="#accounting-line-item-editor-modal">
|
||||
<div>{{ line_item.voucher.date|accounting_format_date }} {{ line_item.description|accounting_default }}</div>
|
||||
<div>
|
||||
<span class="badge bg-primary rounded-pill">
|
||||
<span id="accounting-original-line-item-selector-option-{{ line_item.id }}-net-balance">{{ line_item.net_balance|accounting_format_amount }}</span>
|
||||
|
@ -1,193 +0,0 @@
|
||||
{#
|
||||
The Mia! Accounting Flask Project
|
||||
summary-editor-modal.html: The modal of the summary editor
|
||||
|
||||
Copyright (c) 2023 imacat.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
Author: imacat@mail.imacat.idv.tw (imacat)
|
||||
First written: 2023/2/28
|
||||
#}
|
||||
<form id="accounting-summary-editor-{{ summary_editor.side }}" class="accounting-summary-editor" data-side="{{ summary_editor.side }}">
|
||||
<div id="accounting-summary-editor-{{ summary_editor.side }}-modal" class="modal fade" tabindex="-1" aria-labelledby="accounting-summary-editor-{{ summary_editor.side }}-modal-label" aria-hidden="true">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h1 class="modal-title fs-5" id="accounting-summary-editor-{{ summary_editor.side }}-modal-label">
|
||||
<label for="accounting-summary-editor-{{ summary_editor.side }}-summary">{{ A_("Summary") }}</label>
|
||||
</h1>
|
||||
<button class="btn-close" type="button" data-bs-toggle="modal" data-bs-target="#accounting-line-item-editor-modal" aria-label="{{ A_("Close") }}"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="d-flex justify-content-between mb-3">
|
||||
<input id="accounting-summary-editor-{{ summary_editor.side }}-summary" class="form-control" type="text" aria-labelledby="accounting-summary-editor-{{ summary_editor.side }}-modal-label">
|
||||
<button id="accounting-summary-editor-{{ summary_editor.side }}-offset" class="btn btn-primary text-nowrap ms-2" type="button" data-bs-toggle="modal" data-bs-target="#accounting-original-line-item-selector-modal">
|
||||
{{ A_("Offset...") }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{# Tab navigation #}
|
||||
<ul class="nav nav-tabs mb-2">
|
||||
<li class="nav-item">
|
||||
<span id="accounting-summary-editor-{{ summary_editor.side }}-general-tab" class="nav-link active accounting-clickable" aria-current="page">
|
||||
{{ A_("General") }}
|
||||
</span>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<span id="accounting-summary-editor-{{ summary_editor.side }}-travel-tab" class="nav-link accounting-clickable" aria-current="false">
|
||||
{{ A_("Travel") }}
|
||||
</span>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<span id="accounting-summary-editor-{{ summary_editor.side }}-bus-tab" class="nav-link accounting-clickable" aria-current="false">
|
||||
{{ A_("Bus") }}
|
||||
</span>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<span id="accounting-summary-editor-{{ summary_editor.side }}-regular-tab" class="nav-link accounting-clickable" aria-current="false">
|
||||
{{ A_("Regular") }}
|
||||
</span>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<span id="accounting-summary-editor-{{ summary_editor.side }}-annotation-tab" class="nav-link accounting-clickable" aria-current="false">
|
||||
{{ A_("Annotation") }}
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
{# A general summary with a tag #}
|
||||
<div id="accounting-summary-editor-{{ summary_editor.side }}-general-page" aria-current="page" aria-labelledby="accounting-summary-editor-{{ summary_editor.side }}-general-tab">
|
||||
<div class="form-floating mb-2">
|
||||
<input id="accounting-summary-editor-{{ summary_editor.side }}-general-tag" class="form-control" type="text" value="" placeholder=" ">
|
||||
<label class="form-label" for="accounting-summary-editor-{{ summary_editor.side }}-general-tag">{{ A_("Tag") }}</label>
|
||||
<div id="accounting-summary-editor-{{ summary_editor.side }}-general-tag-error" class="invalid-feedback"></div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
{% for tag in summary_editor.general.tags %}
|
||||
<button class="btn btn-outline-primary accounting-summary-editor-{{ summary_editor.side }}-general-btn-tag" type="button" tabindex="-1" data-value="{{ tag.name }}" data-accounts="{{ tag.account_codes|tojson|forceescape }}">
|
||||
{{ tag }}
|
||||
</button>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{# A general trip with the origin and distination #}
|
||||
<div id="accounting-summary-editor-{{ summary_editor.side }}-travel-page" class="d-none" aria-current="false" aria-labelledby="accounting-summary-editor-{{ summary_editor.side }}-travel-tab">
|
||||
<div class="form-floating mb-2">
|
||||
<input id="accounting-summary-editor-{{ summary_editor.side }}-travel-tag" class="form-control" type="text" value="" placeholder=" ">
|
||||
<label class="form-label" for="accounting-summary-editor-{{ summary_editor.side }}-travel-tag">{{ A_("Tag") }}</label>
|
||||
<div id="accounting-summary-editor-{{ summary_editor.side }}-travel-tag-error" class="invalid-feedback"></div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
{% for tag in summary_editor.travel.tags %}
|
||||
<button class="btn btn-outline-primary accounting-summary-editor-{{ summary_editor.side }}-travel-btn-tag" type="button" tabindex="-1" data-value="{{ tag.name }}" data-accounts="{{ tag.account_codes|tojson|forceescape }}">
|
||||
{{ tag }}
|
||||
</button>
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
<div class="d-flex justify-content-between mt-2">
|
||||
<div class="form-floating">
|
||||
<input id="accounting-summary-editor-{{ summary_editor.side }}-travel-from" class="form-control" type="text" value="" placeholder=" ">
|
||||
<label class="form-label" for="accounting-summary-editor-{{ summary_editor.side }}-travel-from">{{ A_("From") }}</label>
|
||||
<div id="accounting-summary-editor-{{ summary_editor.side }}-travel-from-error" class="invalid-feedback"></div>
|
||||
</div>
|
||||
<div class="btn-group-vertical ms-1 me-1">
|
||||
<button class="btn btn-primary accounting-summary-editor-{{ summary_editor.side }}-travel-direction accounting-default" type="button" tabindex="-1" data-arrow="→">→</button>
|
||||
<button class="btn btn-outline-primary accounting-summary-editor-{{ summary_editor.side }}-travel-direction" type="button" tabindex="-1" data-arrow="↔">↔</button>
|
||||
</div>
|
||||
<div class="form-floating">
|
||||
<input id="accounting-summary-editor-{{ summary_editor.side }}-travel-to" class="form-control" type="text" value="" placeholder=" ">
|
||||
<label class="form-label" for="accounting-summary-editor-{{ summary_editor.side }}-travel-to">{{ A_("To") }}</label>
|
||||
<div id="accounting-summary-editor-{{ summary_editor.side }}-travel-to-error" class="invalid-feedback"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{# A bus trip with the route name or route number, the origin and distination #}
|
||||
<div id="accounting-summary-editor-{{ summary_editor.side }}-bus-page" class="d-none" aria-current="false" aria-labelledby="accounting-summary-editor-{{ summary_editor.side }}-bus-tab">
|
||||
<div class="d-flex justify-content-between mb-2">
|
||||
<div class="form-floating me-2">
|
||||
<input id="accounting-summary-editor-{{ summary_editor.side }}-bus-tag" class="form-control" type="text" value="" placeholder=" ">
|
||||
<label class="form-label" for="accounting-summary-editor-{{ summary_editor.side }}-bus-tag">{{ A_("Tag") }}</label>
|
||||
<div id="accounting-summary-editor-{{ summary_editor.side }}-bus-tag-error" class="invalid-feedback"></div>
|
||||
</div>
|
||||
<div class="form-floating">
|
||||
<input id="accounting-summary-editor-{{ summary_editor.side }}-bus-route" class="form-control" type="text" value="" placeholder=" ">
|
||||
<label class="form-label" for="accounting-summary-editor-{{ summary_editor.side }}-bus-route">{{ A_("Route") }}</label>
|
||||
<div id="accounting-summary-editor-{{ summary_editor.side }}-bus-route-error" class="invalid-feedback"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
{% for tag in summary_editor.bus.tags %}
|
||||
<button class="btn btn-outline-primary accounting-summary-editor-{{ summary_editor.side }}-bus-btn-tag" type="button" tabindex="-1" data-value="{{ tag.name }}" data-accounts="{{ tag.account_codes|tojson|forceescape }}">
|
||||
{{ tag }}
|
||||
</button>
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
<div class="d-flex justify-content-between mt-2">
|
||||
<div class="form-floating me-2">
|
||||
<input id="accounting-summary-editor-{{ summary_editor.side }}-bus-from" class="form-control" type="text" value="" placeholder=" ">
|
||||
<label class="form-label" for="accounting-summary-editor-{{ summary_editor.side }}-bus-from">{{ A_("From") }}</label>
|
||||
<div id="accounting-summary-editor-{{ summary_editor.side }}-bus-from-error" class="invalid-feedback"></div>
|
||||
</div>
|
||||
<div class="form-floating">
|
||||
<input id="accounting-summary-editor-{{ summary_editor.side }}-bus-to" class="form-control" type="text" value="" placeholder=" ">
|
||||
<label class="form-label" for="accounting-summary-editor-{{ summary_editor.side }}-bus-to">{{ A_("To") }}</label>
|
||||
<div id="accounting-summary-editor-{{ summary_editor.side }}-bus-to-error" class="invalid-feedback"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{# A regular income or payment #}
|
||||
<div id="accounting-summary-editor-{{ summary_editor.side }}-regular-page" class="d-none" aria-current="false" aria-labelledby="accounting-summary-editor-{{ summary_editor.side }}-regular-tab">
|
||||
{# TODO: To be done #}
|
||||
</div>
|
||||
|
||||
{# The annotation #}
|
||||
<div id="accounting-summary-editor-{{ summary_editor.side }}-annotation-page" class="d-none" aria-current="false" aria-labelledby="accounting-summary-editor-{{ summary_editor.side }}-annotation-tab">
|
||||
<div class="form-floating">
|
||||
<input id="accounting-summary-editor-{{ summary_editor.side }}-annotation-number" class="form-control" type="number" min="1" value="" placeholder=" ">
|
||||
<label class="form-label" for="accounting-summary-editor-{{ summary_editor.side }}-annotation-number">{{ A_("The number of items") }}</label>
|
||||
<div id="accounting-summary-editor-{{ summary_editor.side }}-annotation-number-error" class="invalid-feedback"></div>
|
||||
</div>
|
||||
|
||||
<div class="form-floating mt-2">
|
||||
<input id="accounting-summary-editor-{{ summary_editor.side }}-annotation-note" class="form-control" type="text" value="" placeholder=" ">
|
||||
<label class="form-label" for="accounting-summary-editor-{{ summary_editor.side }}-annotation-note">{{ A_("Note") }}</label>
|
||||
<div id="accounting-summary-editor-{{ summary_editor.side }}-annotation-note-error" class="invalid-feedback"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{# The suggested accounts #}
|
||||
<div class="mt-3">
|
||||
{% for account in summary_editor.accounts %}
|
||||
<button class="btn btn-outline-primary d-none accounting-summary-editor-{{ summary_editor.side }}-account {% if account.is_need_offset %} accounting-account-is-need-offset {% endif %}" type="button" data-code="{{ account.code }}" data-text="{{ account }}">
|
||||
{{ account }}
|
||||
</button>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button class="btn btn-secondary" type="button" data-bs-toggle="modal" data-bs-target="#accounting-line-item-editor-modal">{{ A_("Cancel") }}</button>
|
||||
<button id="accounting-summary-editor-{{ summary_editor.side }}-btn-save" type="submit" class="btn btn-primary">{{ A_("Save") }}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
@ -45,11 +45,11 @@ First written: 2023/2/25
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<div id="accounting-line-item-editor-summary-control" class="form-control accounting-clickable accounting-material-text-field" data-bs-toggle="modal" data-bs-target="">
|
||||
<label class="form-label" for="accounting-line-item-editor-summary">{{ A_("Summary") }}</label>
|
||||
<div id="accounting-line-item-editor-summary"></div>
|
||||
<div id="accounting-line-item-editor-description-control" class="form-control accounting-clickable accounting-material-text-field" data-bs-toggle="modal" data-bs-target="">
|
||||
<label class="form-label" for="accounting-line-item-editor-description">{{ A_("Description") }}</label>
|
||||
<div id="accounting-line-item-editor-description"></div>
|
||||
</div>
|
||||
<div id="accounting-line-item-editor-summary-error" class="invalid-feedback"></div>
|
||||
<div id="accounting-line-item-editor-description-error" class="invalid-feedback"></div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
|
@ -49,25 +49,25 @@ First written: 2023/2/25
|
||||
{% with currency_index = currency_index,
|
||||
side = "credit",
|
||||
line_item_index = loop.index,
|
||||
only_one_line_item_form = debit_forms|length == 1,
|
||||
line_item_id = line_item_form.eid.data,
|
||||
account_code_data = line_item_form.account_code.data|accounting_default,
|
||||
account_code_error = line_item_form.account_code.errors,
|
||||
account_text = line_item_form.account_text,
|
||||
summary_data = line_item_form.summary.data|accounting_default,
|
||||
summary_errors = line_item_form.summary.errors,
|
||||
original_line_item_id_data = line_item_form.original_line_item_id.data|accounting_default,
|
||||
original_line_item_date = line_item_form.original_line_item_date|accounting_default,
|
||||
original_line_item_text = line_item_form.original_line_item_text|accounting_default,
|
||||
is_need_offset = line_item_form.is_need_offset,
|
||||
offset_items = line_item_form.offsets,
|
||||
offset_total = line_item_form.offset_total|accounting_default("0"),
|
||||
net_balance_data = line_item_form.net_balance,
|
||||
net_balance_text = line_item_form.net_balance|accounting_format_amount,
|
||||
amount_data = line_item_form.amount.data|accounting_voucher_format_amount_input,
|
||||
amount_errors = line_item_form.amount.errors,
|
||||
amount_text = line_item_form.amount.data|accounting_format_amount,
|
||||
line_item_errors = line_item_form.all_errors %}
|
||||
only_one_line_item_form = credit_forms|length == 1,
|
||||
line_item_id = line_item_form.form.eid.data,
|
||||
account_code_data = line_item_form.form.account_code.data|accounting_default,
|
||||
account_code_error = line_item_form.form.account_code.errors,
|
||||
account_text = line_item_form.form.account_text,
|
||||
description_data = line_item_form.form.description.data|accounting_default,
|
||||
description_errors = line_item_form.form.description.errors,
|
||||
original_line_item_id_data = line_item_form.form.original_line_item_id.data|accounting_default,
|
||||
original_line_item_date = line_item_form.form.original_line_item_date|accounting_default,
|
||||
original_line_item_text = line_item_form.form.original_line_item_text|accounting_default,
|
||||
is_need_offset = line_item_form.form.is_need_offset,
|
||||
offset_items = line_item_form.form.offsets,
|
||||
offset_total = line_item_form.form.offset_total|accounting_default("0"),
|
||||
net_balance_data = line_item_form.form.net_balance,
|
||||
net_balance_text = line_item_form.form.net_balance|accounting_format_amount,
|
||||
amount_data = line_item_form.form.amount.data|accounting_voucher_format_amount_input,
|
||||
amount_errors = line_item_form.form.amount.errors,
|
||||
amount_text = line_item_form.form.amount.data|accounting_format_amount,
|
||||
line_item_errors = line_item_form.form.all_errors %}
|
||||
{% include "accounting/voucher/include/form-line-item.html" %}
|
||||
{% endwith %}
|
||||
{% endfor %}
|
||||
|
@ -47,8 +47,8 @@ First written: 2023/2/25
|
||||
{% endblock %}
|
||||
|
||||
{% block form_modals %}
|
||||
{% with summary_editor = form.summary_editor.credit %}
|
||||
{% include "accounting/voucher/include/summary-editor-modal.html" %}
|
||||
{% with description_editor = form.description_editor.credit %}
|
||||
{% include "accounting/voucher/include/description-editor-modal.html" %}
|
||||
{% endwith %}
|
||||
{% with side = "credit",
|
||||
account_options = form.credit_account_options %}
|
||||
|
@ -52,24 +52,24 @@ First written: 2023/2/25
|
||||
side = "debit",
|
||||
line_item_index = loop.index,
|
||||
only_one_line_item_form = debit_forms|length == 1,
|
||||
line_item_id = line_item_form.eid.data,
|
||||
account_code_data = line_item_form.account_code.data|accounting_default,
|
||||
account_code_error = line_item_form.account_code.errors,
|
||||
account_text = line_item_form.account_text,
|
||||
summary_data = line_item_form.summary.data|accounting_default,
|
||||
summary_errors = line_item_form.summary.errors,
|
||||
original_line_item_id_data = line_item_form.original_line_item_id.data|accounting_default,
|
||||
original_line_item_date = line_item_form.original_line_item_date|accounting_default,
|
||||
original_line_item_text = line_item_form.original_line_item_text|accounting_default,
|
||||
is_need_offset = line_item_form.is_need_offset,
|
||||
offset_items = line_item_form.offsets,
|
||||
offset_total = line_item_form.offset_total|accounting_default,
|
||||
net_balance_data = line_item_form.net_balance,
|
||||
net_balance_text = line_item_form.net_balance|accounting_format_amount,
|
||||
amount_data = line_item_form.amount.data|accounting_voucher_format_amount_input,
|
||||
amount_errors = line_item_form.amount.errors,
|
||||
amount_text = line_item_form.amount.data|accounting_format_amount,
|
||||
line_item_errors = line_item_form.all_errors %}
|
||||
line_item_id = line_item_form.form.eid.data,
|
||||
account_code_data = line_item_form.form.account_code.data|accounting_default,
|
||||
account_code_error = line_item_form.form.account_code.errors,
|
||||
account_text = line_item_form.form.account_text,
|
||||
description_data = line_item_form.form.description.data|accounting_default,
|
||||
description_errors = line_item_form.form.description.errors,
|
||||
original_line_item_id_data = line_item_form.form.original_line_item_id.data|accounting_default,
|
||||
original_line_item_date = line_item_form.form.original_line_item_date|accounting_default,
|
||||
original_line_item_text = line_item_form.form.original_line_item_text|accounting_default,
|
||||
is_need_offset = line_item_form.form.is_need_offset,
|
||||
offset_items = line_item_form.form.offsets,
|
||||
offset_total = line_item_form.form.offset_total|accounting_default,
|
||||
net_balance_data = line_item_form.form.net_balance,
|
||||
net_balance_text = line_item_form.form.net_balance|accounting_format_amount,
|
||||
amount_data = line_item_form.form.amount.data|accounting_voucher_format_amount_input,
|
||||
amount_errors = line_item_form.form.amount.errors,
|
||||
amount_text = line_item_form.form.amount.data|accounting_format_amount,
|
||||
line_item_errors = line_item_form.form.all_errors %}
|
||||
{% include "accounting/voucher/include/form-line-item.html" %}
|
||||
{% endwith %}
|
||||
{% endfor %}
|
||||
@ -100,24 +100,24 @@ First written: 2023/2/25
|
||||
side = "credit",
|
||||
line_item_index = loop.index,
|
||||
only_one_line_item_form = debit_forms|length == 1,
|
||||
line_item_id = line_item_form.eid.data,
|
||||
account_code_data = line_item_form.account_code.data|accounting_default,
|
||||
account_code_error = line_item_form.account_code.errors,
|
||||
account_text = line_item_form.account_text,
|
||||
summary_data = line_item_form.summary.data|accounting_default,
|
||||
summary_errors = line_item_form.summary.errors,
|
||||
original_line_item_id_data = line_item_form.original_line_item_id.data|accounting_default,
|
||||
original_line_item_date = line_item_form.original_line_item_date|accounting_default,
|
||||
original_line_item_text = line_item_form.original_line_item_text|accounting_default,
|
||||
is_need_offset = line_item_form.is_need_offset,
|
||||
offset_items = line_item_form.offsets,
|
||||
offset_total = line_item_form.offset_total|accounting_default("0"),
|
||||
net_balance_data = line_item_form.net_balance,
|
||||
net_balance_text = line_item_form.net_balance|accounting_format_amount,
|
||||
amount_data = line_item_form.amount.data|accounting_voucher_format_amount_input,
|
||||
amount_errors = line_item_form.amount.errors,
|
||||
amount_text = line_item_form.amount.data|accounting_format_amount,
|
||||
line_item_errors = line_item_form.all_errors %}
|
||||
line_item_id = line_item_form.form.eid.data,
|
||||
account_code_data = line_item_form.form.account_code.data|accounting_default,
|
||||
account_code_error = line_item_form.form.account_code.errors,
|
||||
account_text = line_item_form.form.account_text,
|
||||
description_data = line_item_form.form.description.data|accounting_default,
|
||||
description_errors = line_item_form.form.description.errors,
|
||||
original_line_item_id_data = line_item_form.form.original_line_item_id.data|accounting_default,
|
||||
original_line_item_date = line_item_form.form.original_line_item_date|accounting_default,
|
||||
original_line_item_text = line_item_form.form.original_line_item_text|accounting_default,
|
||||
is_need_offset = line_item_form.form.is_need_offset,
|
||||
offset_items = line_item_form.form.offsets,
|
||||
offset_total = line_item_form.form.offset_total|accounting_default("0"),
|
||||
net_balance_data = line_item_form.form.net_balance,
|
||||
net_balance_text = line_item_form.form.net_balance|accounting_format_amount,
|
||||
amount_data = line_item_form.form.amount.data|accounting_voucher_format_amount_input,
|
||||
amount_errors = line_item_form.form.amount.errors,
|
||||
amount_text = line_item_form.form.amount.data|accounting_format_amount,
|
||||
line_item_errors = line_item_form.form.all_errors %}
|
||||
{% include "accounting/voucher/include/form-line-item.html" %}
|
||||
{% endwith %}
|
||||
{% endfor %}
|
||||
|
@ -51,11 +51,11 @@ First written: 2023/2/25
|
||||
{% endblock %}
|
||||
|
||||
{% block form_modals %}
|
||||
{% with summary_editor = form.summary_editor.debit %}
|
||||
{% include "accounting/voucher/include/summary-editor-modal.html" %}
|
||||
{% with description_editor = form.description_editor.debit %}
|
||||
{% include "accounting/voucher/include/description-editor-modal.html" %}
|
||||
{% endwith %}
|
||||
{% with summary_editor = form.summary_editor.credit %}
|
||||
{% include "accounting/voucher/include/summary-editor-modal.html" %}
|
||||
{% with description_editor = form.description_editor.credit %}
|
||||
{% include "accounting/voucher/include/description-editor-modal.html" %}
|
||||
{% endwith %}
|
||||
{% with side = "debit",
|
||||
account_options = form.debit_account_options %}
|
||||
|
@ -452,8 +452,8 @@ class DebitLineItemForm(LineItemForm):
|
||||
"""The account code."""
|
||||
offset_original_line_item_id = IntegerField()
|
||||
"""The ID of the original line item."""
|
||||
summary = StringField(filters=[strip_text])
|
||||
"""The summary."""
|
||||
description = StringField(filters=[strip_text])
|
||||
"""The description."""
|
||||
amount = DecimalField(
|
||||
validators=[PositiveAmount(),
|
||||
NotExceedingOriginalLineItemNetBalance(),
|
||||
@ -471,7 +471,7 @@ class DebitLineItemForm(LineItemForm):
|
||||
obj.id = new_id(VoucherLineItem)
|
||||
obj.original_line_item_id = self.original_line_item_id.data
|
||||
obj.account_id = Account.find_by_code(self.account_code.data).id
|
||||
obj.summary = self.summary.data
|
||||
obj.description = self.description.data
|
||||
obj.is_debit = True
|
||||
obj.amount = self.amount.data
|
||||
if is_new:
|
||||
@ -502,8 +502,8 @@ class CreditLineItemForm(LineItemForm):
|
||||
KeepAccountWhenHavingOffset(),
|
||||
NotStartReceivableFromCredit()])
|
||||
"""The account code."""
|
||||
summary = StringField(filters=[strip_text])
|
||||
"""The summary."""
|
||||
description = StringField(filters=[strip_text])
|
||||
"""The description."""
|
||||
amount = DecimalField(
|
||||
validators=[PositiveAmount(),
|
||||
NotExceedingOriginalLineItemNetBalance(),
|
||||
@ -521,7 +521,7 @@ class CreditLineItemForm(LineItemForm):
|
||||
obj.id = new_id(VoucherLineItem)
|
||||
obj.original_line_item_id = self.original_line_item_id.data
|
||||
obj.account_id = Account.find_by_code(self.account_code.data).id
|
||||
obj.summary = self.summary.data
|
||||
obj.description = self.description.data
|
||||
obj.is_debit = False
|
||||
obj.amount = self.amount.data
|
||||
if is_new:
|
||||
|
@ -35,7 +35,7 @@ from accounting.models import Voucher, Account, VoucherLineItem, \
|
||||
from accounting.voucher.utils.account_option import AccountOption
|
||||
from accounting.voucher.utils.original_line_items import \
|
||||
get_selectable_original_line_items
|
||||
from accounting.voucher.utils.summary_editor import SummaryEditor
|
||||
from accounting.voucher.utils.description_editor import DescriptionEditor
|
||||
from accounting.utils.random_id import new_id
|
||||
from accounting.utils.strip_text import strip_multiline_text
|
||||
from accounting.utils.user import get_current_user_pk
|
||||
@ -256,12 +256,12 @@ class VoucherForm(FlaskForm):
|
||||
if isinstance(x, str) or isinstance(x, LazyString)]
|
||||
|
||||
@property
|
||||
def summary_editor(self) -> SummaryEditor:
|
||||
"""Returns the summary editor.
|
||||
def description_editor(self) -> DescriptionEditor:
|
||||
"""Returns the description editor.
|
||||
|
||||
:return: The summary editor.
|
||||
:return: The description editor.
|
||||
"""
|
||||
return SummaryEditor()
|
||||
return DescriptionEditor()
|
||||
|
||||
@property
|
||||
def original_line_item_options(self) -> list[VoucherLineItem]:
|
||||
@ -394,7 +394,7 @@ class LineItemCollector(t.Generic[T], ABC):
|
||||
candidates.sort(key=lambda x: x.no)
|
||||
line_item = candidates[0]
|
||||
line_item.account_id = Account.cash().id
|
||||
line_item.summary = None
|
||||
line_item.description = None
|
||||
line_item.amount = sum([x.amount.data for x in forms])
|
||||
line_item.no = no
|
||||
if db.session.is_modified(line_item):
|
||||
@ -405,7 +405,7 @@ class LineItemCollector(t.Generic[T], ABC):
|
||||
line_item.is_debit = is_debit
|
||||
line_item.currency_code = currency_code
|
||||
line_item.account_id = Account.cash().id
|
||||
line_item.summary = None
|
||||
line_item.description = None
|
||||
line_item.amount = sum([x.amount.data for x in forms])
|
||||
line_item.no = no
|
||||
self.__obj.line_items.append(line_item)
|
||||
|
@ -14,7 +14,7 @@
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
"""The summary editor.
|
||||
"""The description editor.
|
||||
|
||||
"""
|
||||
import typing as t
|
||||
@ -25,11 +25,11 @@ from accounting import db
|
||||
from accounting.models import Account, VoucherLineItem
|
||||
|
||||
|
||||
class SummaryAccount:
|
||||
"""An account for a summary tag."""
|
||||
class DescriptionAccount:
|
||||
"""An account for a description tag."""
|
||||
|
||||
def __init__(self, account: Account, freq: int):
|
||||
"""Constructs an account for a summary tag.
|
||||
"""Constructs an account for a description tag.
|
||||
|
||||
:param account: The account.
|
||||
:param freq: The frequency of the tag with the account.
|
||||
@ -59,17 +59,17 @@ class SummaryAccount:
|
||||
self.freq = self.freq + freq
|
||||
|
||||
|
||||
class SummaryTag:
|
||||
"""A summary tag."""
|
||||
class DescriptionTag:
|
||||
"""A description tag."""
|
||||
|
||||
def __init__(self, name: str):
|
||||
"""Constructs a summary tag.
|
||||
"""Constructs a description tag.
|
||||
|
||||
:param name: The tag name.
|
||||
"""
|
||||
self.name: str = name
|
||||
"""The tag name."""
|
||||
self.__account_dict: dict[int, SummaryAccount] = {}
|
||||
self.__account_dict: dict[int, DescriptionAccount] = {}
|
||||
"""The accounts that come with the tag, in the order of their
|
||||
frequency."""
|
||||
self.freq: int = 0
|
||||
@ -89,11 +89,11 @@ class SummaryTag:
|
||||
:param freq: The frequency of the tag name with the account.
|
||||
:return: None.
|
||||
"""
|
||||
self.__account_dict[account.id] = SummaryAccount(account, freq)
|
||||
self.__account_dict[account.id] = DescriptionAccount(account, freq)
|
||||
self.freq = self.freq + freq
|
||||
|
||||
@property
|
||||
def accounts(self) -> list[SummaryAccount]:
|
||||
def accounts(self) -> list[DescriptionAccount]:
|
||||
"""Returns the accounts by the order of their frequencies.
|
||||
|
||||
:return: The accounts by the order of their frequencies.
|
||||
@ -109,17 +109,17 @@ class SummaryTag:
|
||||
return [x.code for x in self.accounts]
|
||||
|
||||
|
||||
class SummaryType:
|
||||
"""A summary type"""
|
||||
class DescriptionType:
|
||||
"""A description type"""
|
||||
|
||||
def __init__(self, type_id: t.Literal["general", "travel", "bus"]):
|
||||
"""Constructs a summary type.
|
||||
"""Constructs a description type.
|
||||
|
||||
:param type_id: The type ID, either "general", "travel", or "bus".
|
||||
"""
|
||||
self.id: t.Literal["general", "travel", "bus"] = type_id
|
||||
"""The type ID."""
|
||||
self.__tag_dict: dict[str, SummaryTag] = {}
|
||||
self.__tag_dict: dict[str, DescriptionTag] = {}
|
||||
"""A dictionary from the tag name to their corresponding tag."""
|
||||
|
||||
def add_tag(self, name: str, account: Account, freq: int) -> None:
|
||||
@ -131,11 +131,11 @@ class SummaryType:
|
||||
:return: None.
|
||||
"""
|
||||
if name not in self.__tag_dict:
|
||||
self.__tag_dict[name] = SummaryTag(name)
|
||||
self.__tag_dict[name] = DescriptionTag(name)
|
||||
self.__tag_dict[name].add_account(account, freq)
|
||||
|
||||
@property
|
||||
def tags(self) -> list[SummaryTag]:
|
||||
def tags(self) -> list[DescriptionTag]:
|
||||
"""Returns the tags by the order of their frequencies.
|
||||
|
||||
:return: The tags by the order of their frequencies.
|
||||
@ -143,24 +143,24 @@ class SummaryType:
|
||||
return sorted(self.__tag_dict.values(), key=lambda x: -x.freq)
|
||||
|
||||
|
||||
class SummarySide:
|
||||
"""A summary side"""
|
||||
class DescriptionSide:
|
||||
"""A description side"""
|
||||
|
||||
def __init__(self, side_id: t.Literal["debit", "credit"]):
|
||||
"""Constructs a summary side.
|
||||
"""Constructs a description side.
|
||||
|
||||
:param side_id: The side ID, either "debit" or "credit".
|
||||
"""
|
||||
self.side: t.Literal["debit", "credit"] = side_id
|
||||
"""The side."""
|
||||
self.general: SummaryType = SummaryType("general")
|
||||
self.general: DescriptionType = DescriptionType("general")
|
||||
"""The general tags."""
|
||||
self.travel: SummaryType = SummaryType("travel")
|
||||
self.travel: DescriptionType = DescriptionType("travel")
|
||||
"""The travel tags."""
|
||||
self.bus: SummaryType = SummaryType("bus")
|
||||
self.bus: DescriptionType = DescriptionType("bus")
|
||||
"""The bus tags."""
|
||||
self.__type_dict: dict[t.Literal["general", "travel", "bus"],
|
||||
SummaryType] \
|
||||
DescriptionType] \
|
||||
= {x.id: x for x in {self.general, self.travel, self.bus}}
|
||||
"""A dictionary from the type ID to the corresponding tags."""
|
||||
|
||||
@ -177,13 +177,13 @@ class SummarySide:
|
||||
self.__type_dict[tag_type].add_tag(name, account, freq)
|
||||
|
||||
@property
|
||||
def accounts(self) -> list[SummaryAccount]:
|
||||
"""Returns the suggested accounts of all tags in the summary editor in
|
||||
the side, in their frequency order.
|
||||
def accounts(self) -> list[DescriptionAccount]:
|
||||
"""Returns the suggested accounts of all tags in the description editor
|
||||
in the side, in their frequency order.
|
||||
|
||||
:return: The suggested accounts of all tags, in their frequency order.
|
||||
"""
|
||||
accounts: dict[int, SummaryAccount] = {}
|
||||
accounts: dict[int, DescriptionAccount] = {}
|
||||
freq: dict[int, int] = {}
|
||||
for tag_type in self.__type_dict.values():
|
||||
for tag in tag_type.tags:
|
||||
@ -197,35 +197,36 @@ class SummarySide:
|
||||
key=lambda x: -freq[x])]
|
||||
|
||||
|
||||
class SummaryEditor:
|
||||
"""The summary editor."""
|
||||
class DescriptionEditor:
|
||||
"""The description editor."""
|
||||
|
||||
def __init__(self):
|
||||
"""Constructs the summary editor."""
|
||||
self.debit: SummarySide = SummarySide("debit")
|
||||
"""Constructs the description editor."""
|
||||
self.debit: DescriptionSide = DescriptionSide("debit")
|
||||
"""The debit tags."""
|
||||
self.credit: SummarySide = SummarySide("credit")
|
||||
self.credit: DescriptionSide = DescriptionSide("credit")
|
||||
"""The credit tags."""
|
||||
side: sa.Label = sa.case((VoucherLineItem.is_debit, "debit"),
|
||||
else_="credit").label("side")
|
||||
tag_type: sa.Label = sa.case(
|
||||
(VoucherLineItem.summary.like("_%—_%—_%→_%"), "bus"),
|
||||
(sa.or_(VoucherLineItem.summary.like("_%—_%→_%"),
|
||||
VoucherLineItem.summary.like("_%—_%↔_%")), "travel"),
|
||||
(VoucherLineItem.description.like("_%—_%—_%→_%"), "bus"),
|
||||
(sa.or_(VoucherLineItem.description.like("_%—_%→_%"),
|
||||
VoucherLineItem.description.like("_%—_%↔_%")), "travel"),
|
||||
else_="general").label("tag_type")
|
||||
tag: sa.Label = get_prefix(VoucherLineItem.summary, "—").label("tag")
|
||||
tag: sa.Label = get_prefix(VoucherLineItem.description, "—")\
|
||||
.label("tag")
|
||||
select: sa.Select = sa.Select(side, tag_type, tag,
|
||||
VoucherLineItem.account_id,
|
||||
sa.func.count().label("freq"))\
|
||||
.filter(VoucherLineItem.summary.is_not(None),
|
||||
VoucherLineItem.summary.like("_%—_%"),
|
||||
.filter(VoucherLineItem.description.is_not(None),
|
||||
VoucherLineItem.description.like("_%—_%"),
|
||||
VoucherLineItem.original_line_item_id.is_(None))\
|
||||
.group_by(side, tag_type, tag, VoucherLineItem.account_id)
|
||||
result: list[sa.Row] = db.session.execute(select).all()
|
||||
accounts: dict[int, Account] \
|
||||
= {x.id: x for x in Account.query
|
||||
.filter(Account.id.in_({x.account_id for x in result})).all()}
|
||||
side_dict: dict[t.Literal["debit", "credit"], SummarySide] \
|
||||
side_dict: dict[t.Literal["debit", "credit"], DescriptionSide] \
|
||||
= {x.side: x for x in {self.debit, self.credit}}
|
||||
for row in result:
|
||||
side_dict[row.side].add_tag(
|
Reference in New Issue
Block a user