Renamed "summary" to "description" in the voucher line item.

This commit is contained in:
依瑪貓 2023-03-20 16:01:25 +08:00
parent 3251660092
commit d18dd7d4d2
38 changed files with 680 additions and 678 deletions

View File

@ -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),

View File

@ -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:

View File

@ -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

View File

@ -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:

View File

@ -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_(

View File

@ -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;
}

View File

@ -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");

View File

@ -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();

View File

@ -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");

View File

@ -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>

View File

@ -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>

View File

@ -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 %}

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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 %}

View File

@ -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>

View File

@ -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 %}

View File

@ -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 %}

View File

@ -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="&rarr;">&rarr;</button>
<button class="btn btn-outline-primary accounting-description-editor-{{ description_editor.side }}-travel-direction" type="button" tabindex="-1" data-arrow="&harr;">&harr;</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>

View File

@ -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">

View File

@ -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>

View File

@ -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 %}

View File

@ -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>

View File

@ -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="&rarr;">&rarr;</button>
<button class="btn btn-outline-primary accounting-summary-editor-{{ summary_editor.side }}-travel-direction" type="button" tabindex="-1" data-arrow="&harr;">&harr;</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>

View File

@ -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">

View File

@ -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 %}

View File

@ -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 %}

View File

@ -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 %}

View File

@ -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 %}

View File

@ -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:

View File

@ -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)

View File

@ -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(

View File

@ -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 test for the summary editor.
"""The test for the description editor.
"""
import unittest
@ -28,8 +28,8 @@ from testlib import create_test_app, get_client
from testlib_voucher import Accounts, NEXT_URI, add_voucher
class SummeryEditorTestCase(unittest.TestCase):
"""The summary editor test case."""
class DescriptionEditorTestCase(unittest.TestCase):
"""The description editor test case."""
def setUp(self) -> None:
"""Sets up the test.
@ -60,16 +60,17 @@ class SummeryEditorTestCase(unittest.TestCase):
self.client, self.csrf_token = get_client(self.app, "editor")
def test_summary_editor(self) -> None:
"""Test the summary editor.
def test_description_editor(self) -> None:
"""Test the description editor.
:return: None.
"""
from accounting.voucher.utils.summary_editor import SummaryEditor
from accounting.voucher.utils.description_editor import \
DescriptionEditor
for form in get_form_data(self.csrf_token):
add_voucher(self.client, form)
with self.app.app_context():
editor: SummaryEditor = SummaryEditor()
editor: DescriptionEditor = DescriptionEditor()
# Debit-General
self.assertEqual(len(editor.debit.general.tags), 2)
@ -170,156 +171,156 @@ def get_form_data(csrf_token: str) -> list[dict[str, str]]:
"date": voucher_date,
"currency-0-code": "USD",
"currency-0-credit-0-account_code": Accounts.SERVICE,
"currency-0-credit-0-summary": " Salary ",
"currency-0-credit-0-description": " Salary ",
"currency-0-credit-0-amount": "2500"},
{"csrf_token": csrf_token,
"next": NEXT_URI,
"date": voucher_date,
"currency-0-code": "USD",
"currency-0-debit-0-account_code": Accounts.MEAL,
"currency-0-debit-0-summary": " Lunch—Fish ",
"currency-0-debit-0-description": " Lunch—Fish ",
"currency-0-debit-0-amount": "4.7",
"currency-0-credit-0-account_code": Accounts.BANK,
"currency-0-credit-0-summary": " Lunch—Fish ",
"currency-0-credit-0-description": " Lunch—Fish ",
"currency-0-credit-0-amount": "4.7",
"currency-0-debit-1-account_code": Accounts.MEAL,
"currency-0-debit-1-summary": " Lunch—Fries ",
"currency-0-debit-1-description": " Lunch—Fries ",
"currency-0-debit-1-amount": "2.15",
"currency-0-credit-1-account_code": Accounts.PETTY_CASH,
"currency-0-credit-1-summary": " Lunch—Fries ",
"currency-0-credit-1-description": " Lunch—Fries ",
"currency-0-credit-1-amount": "2.15",
"currency-0-debit-2-account_code": Accounts.MEAL,
"currency-0-debit-2-summary": " Dinner—Hamburger ",
"currency-0-debit-2-description": " Dinner—Hamburger ",
"currency-0-debit-2-amount": "4.25",
"currency-0-credit-2-account_code": Accounts.BANK,
"currency-0-credit-2-summary": " Dinner—Hamburger ",
"currency-0-credit-2-description": " Dinner—Hamburger ",
"currency-0-credit-2-amount": "4.25"},
{"csrf_token": csrf_token,
"next": NEXT_URI,
"date": voucher_date,
"currency-0-code": "USD",
"currency-0-debit-0-account_code": Accounts.MEAL,
"currency-0-debit-0-summary": " Lunch—Salad ",
"currency-0-debit-0-description": " Lunch—Salad ",
"currency-0-debit-0-amount": "4.99",
"currency-0-credit-0-account_code": Accounts.CASH,
"currency-0-credit-0-summary": " Lunch—Salad ",
"currency-0-credit-0-description": " Lunch—Salad ",
"currency-0-credit-0-amount": "4.99",
"currency-0-debit-1-account_code": Accounts.MEAL,
"currency-0-debit-1-summary": " Dinner—Steak ",
"currency-0-debit-1-description": " Dinner—Steak ",
"currency-0-debit-1-amount": "8.28",
"currency-0-credit-1-account_code": Accounts.PETTY_CASH,
"currency-0-credit-1-summary": " Dinner—Steak ",
"currency-0-credit-1-description": " Dinner—Steak ",
"currency-0-credit-1-amount": "8.28"},
{"csrf_token": csrf_token,
"next": NEXT_URI,
"date": voucher_date,
"currency-0-code": "USD",
"currency-0-debit-0-account_code": Accounts.MEAL,
"currency-0-debit-0-summary": " Lunch—Pizza ",
"currency-0-debit-0-description": " Lunch—Pizza ",
"currency-0-debit-0-amount": "5.49",
"currency-0-credit-0-account_code": Accounts.PETTY_CASH,
"currency-0-credit-0-summary": " Lunch—Pizza ",
"currency-0-credit-0-description": " Lunch—Pizza ",
"currency-0-credit-0-amount": "5.49",
"currency-0-debit-1-account_code": Accounts.MEAL,
"currency-0-debit-1-summary": " Lunch—Noodles ",
"currency-0-debit-1-description": " Lunch—Noodles ",
"currency-0-debit-1-amount": "7.47",
"currency-0-credit-1-account_code": Accounts.PETTY_CASH,
"currency-0-credit-1-summary": " Lunch—Noodles ",
"currency-0-credit-1-description": " Lunch—Noodles ",
"currency-0-credit-1-amount": "7.47"},
{"csrf_token": csrf_token,
"next": NEXT_URI,
"date": voucher_date,
"currency-0-code": "USD",
"currency-0-debit-0-account_code": Accounts.TRAVEL,
"currency-0-debit-0-summary": " Airplane—Lake City↔Hill Town ",
"currency-0-debit-0-description": " Airplane—Lake City↔Hill Town",
"currency-0-debit-0-amount": "800"},
{"csrf_token": csrf_token,
"next": NEXT_URI,
"date": voucher_date,
"currency-0-code": "USD",
"currency-0-debit-0-account_code": Accounts.TRAVEL,
"currency-0-debit-0-summary": " Bus—323—Downtown→Museum ",
"currency-0-debit-0-description": " Bus—323—Downtown→Museum ",
"currency-0-debit-0-amount": "2.5",
"currency-0-credit-0-account_code": Accounts.PREPAID,
"currency-0-credit-0-summary": " Bus—323—Downtown→Museum ",
"currency-0-credit-0-description": " Bus—323—Downtown→Museum ",
"currency-0-credit-0-amount": "2.5",
"currency-0-debit-1-account_code": Accounts.TRAVEL,
"currency-0-debit-1-summary": " Train—Blue—Museum→Central ",
"currency-0-debit-1-description": " Train—Blue—Museum→Central ",
"currency-0-debit-1-amount": "3.2",
"currency-0-credit-1-account_code": Accounts.PREPAID,
"currency-0-credit-1-summary": " Train—Blue—Museum→Central ",
"currency-0-credit-1-description": " Train—Blue—Museum→Central ",
"currency-0-credit-1-amount": "3.2",
"currency-0-debit-2-account_code": Accounts.TRAVEL,
"currency-0-debit-2-summary": " Train—Green—Central→Mall ",
"currency-0-debit-2-description": " Train—Green—Central→Mall ",
"currency-0-debit-2-amount": "3.2",
"currency-0-credit-2-account_code": Accounts.PREPAID,
"currency-0-credit-2-summary": " Train—Green—Central→Mall ",
"currency-0-credit-2-description": " Train—Green—Central→Mall ",
"currency-0-credit-2-amount": "3.2",
"currency-0-debit-3-account_code": Accounts.TRAVEL,
"currency-0-debit-3-summary": " Train—Red—Mall→Museum ",
"currency-0-debit-3-description": " Train—Red—Mall→Museum ",
"currency-0-debit-3-amount": "4.4",
"currency-0-credit-3-account_code": Accounts.PETTY_CASH,
"currency-0-credit-3-summary": " Train—Red—Mall→Museum ",
"currency-0-credit-3-description": " Train—Red—Mall→Museum ",
"currency-0-credit-3-amount": "4.4"},
{"csrf_token": csrf_token,
"next": NEXT_URI,
"date": voucher_date,
"currency-0-code": "USD",
"currency-0-debit-0-account_code": Accounts.TRAVEL,
"currency-0-debit-0-summary": " Taxi—Museum→Office ",
"currency-0-debit-0-description": " Taxi—Museum→Office ",
"currency-0-debit-0-amount": "15.5",
"currency-0-credit-0-account_code": Accounts.CASH,
"currency-0-credit-0-summary": " Taxi—Museum→Office ",
"currency-0-credit-0-description": " Taxi—Museum→Office ",
"currency-0-credit-0-amount": "15.5",
"currency-0-debit-1-account_code": Accounts.TRAVEL,
"currency-0-debit-1-summary": " Taxi—Office→Restaurant ",
"currency-0-debit-1-description": " Taxi—Office→Restaurant ",
"currency-0-debit-1-amount": "12",
"currency-0-credit-1-account_code": Accounts.PETTY_CASH,
"currency-0-credit-1-summary": " Taxi—Office→Restaurant ",
"currency-0-credit-1-description": " Taxi—Office→Restaurant ",
"currency-0-credit-1-amount": "12",
"currency-0-debit-2-account_code": Accounts.TRAVEL,
"currency-0-debit-2-summary": " Taxi—Restaurant→City Hall ",
"currency-0-debit-2-description": " Taxi—Restaurant→City Hall ",
"currency-0-debit-2-amount": "8",
"currency-0-credit-2-account_code": Accounts.PETTY_CASH,
"currency-0-credit-2-summary": " Taxi—Restaurant→City Hall ",
"currency-0-credit-2-description": " Taxi—Restaurant→City Hall ",
"currency-0-credit-2-amount": "8",
"currency-0-debit-3-account_code": Accounts.TRAVEL,
"currency-0-debit-3-summary": " Bike—City Hall→Office ",
"currency-0-debit-3-description": " Bike—City Hall→Office ",
"currency-0-debit-3-amount": "3.5",
"currency-0-credit-3-account_code": Accounts.PETTY_CASH,
"currency-0-credit-3-summary": " Bike—City Hall→Office ",
"currency-0-credit-3-description": " Bike—City Hall→Office ",
"currency-0-credit-3-amount": "3.5",
"currency-0-debit-4-account_code": Accounts.TRAVEL,
"currency-0-debit-4-summary": " Bike—Restaurant→Office ",
"currency-0-debit-4-description": " Bike—Restaurant→Office ",
"currency-0-debit-4-amount": "4",
"currency-0-credit-4-account_code": Accounts.PETTY_CASH,
"currency-0-credit-4-summary": " Bike—Restaurant→Office ",
"currency-0-credit-4-description": " Bike—Restaurant→Office ",
"currency-0-credit-4-amount": "4",
"currency-0-debit-5-account_code": Accounts.TRAVEL,
"currency-0-debit-5-summary": " Bike—Office→Theatre ",
"currency-0-debit-5-description": " Bike—Office→Theatre ",
"currency-0-debit-5-amount": "1.5",
"currency-0-credit-5-account_code": Accounts.PETTY_CASH,
"currency-0-credit-5-summary": " Bike—Office→Theatre ",
"currency-0-credit-5-description": " Bike—Office→Theatre ",
"currency-0-credit-5-amount": "1.5",
"currency-0-debit-6-account_code": Accounts.TRAVEL,
"currency-0-debit-6-summary": " Bike—Theatre→Home ",
"currency-0-debit-6-description": " Bike—Theatre→Home ",
"currency-0-debit-6-amount": "5.5",
"currency-0-credit-6-account_code": Accounts.PREPAID,
"currency-0-credit-6-summary": " Bike—Theatre→Home ",
"currency-0-credit-6-description": " Bike—Theatre→Home ",
"currency-0-credit-6-amount": "5.5"},
{"csrf_token": csrf_token,
"next": NEXT_URI,
"date": voucher_date,
"currency-0-code": "USD",
"currency-0-debit-0-account_code": Accounts.PETTY_CASH,
"currency-0-debit-0-summary": " Dinner—Steak ",
"currency-0-debit-0-description": " Dinner—Steak ",
"currency-0-debit-0-amount": "8.28",
"currency-0-credit-0-account_code": Accounts.BANK,
"currency-0-credit-0-summary": " Dinner—Steak ",
"currency-0-credit-0-description": " Dinner—Steak ",
"currency-0-credit-0-amount": "8.28",
"currency-0-debit-1-account_code": Accounts.PETTY_CASH,
"currency-0-debit-1-summary": " Lunch—Pizza ",
"currency-0-debit-1-description": " Lunch—Pizza ",
"currency-0-debit-1-amount": "5.49",
"currency-0-credit-1-account_code": Accounts.BANK,
"currency-0-credit-1-summary": " Lunch—Pizza ",
"currency-0-credit-1-description": " Lunch—Pizza ",
"currency-0-credit-1-amount": "5.49"}]

View File

@ -85,13 +85,13 @@ class OffsetTestCase(unittest.TestCase):
"USD",
[],
[VoucherLineItemData(Accounts.RECEIVABLE,
self.data.e_r_or1d.summary, "300",
self.data.e_r_or1d.description, "300",
original_line_item=self.data.e_r_or1d),
VoucherLineItemData(Accounts.RECEIVABLE,
self.data.e_r_or1d.summary, "100",
self.data.e_r_or1d.description, "100",
original_line_item=self.data.e_r_or1d),
VoucherLineItemData(Accounts.RECEIVABLE,
self.data.e_r_or3d.summary, "100",
self.data.e_r_or3d.description, "100",
original_line_item=self.data.e_r_or3d)])])
# Non-existing original line item ID
@ -400,13 +400,13 @@ class OffsetTestCase(unittest.TestCase):
self.data.e_p_or3c.voucher.days, [CurrencyData(
"USD",
[VoucherLineItemData(Accounts.PAYABLE,
self.data.e_p_or1c.summary, "500",
self.data.e_p_or1c.description, "500",
original_line_item=self.data.e_p_or1c),
VoucherLineItemData(Accounts.PAYABLE,
self.data.e_p_or1c.summary, "300",
self.data.e_p_or1c.description, "300",
original_line_item=self.data.e_p_or1c),
VoucherLineItemData(Accounts.PAYABLE,
self.data.e_p_or3c.summary, "120",
self.data.e_p_or3c.description, "120",
original_line_item=self.data.e_p_or3c)],
[])])

View File

@ -264,7 +264,7 @@ class CashReceiptVoucherTestCase(unittest.TestCase):
self.assertEqual(currencies[0].debit[0].no, 1)
self.assertEqual(currencies[0].debit[0].account.code,
Accounts.CASH)
self.assertIsNone(currencies[0].debit[0].summary)
self.assertIsNone(currencies[0].debit[0].description)
self.assertEqual(currencies[0].debit[0].amount,
sum([x.amount for x in currencies[0].credit]))
self.assertEqual(len(currencies[0].credit), 2)
@ -280,7 +280,7 @@ class CashReceiptVoucherTestCase(unittest.TestCase):
self.assertEqual(currencies[1].debit[0].no, 2)
self.assertEqual(currencies[1].debit[0].account.code,
Accounts.CASH)
self.assertIsNone(currencies[1].debit[0].summary)
self.assertIsNone(currencies[1].debit[0].description)
self.assertEqual(currencies[1].debit[0].amount,
sum([x.amount for x in currencies[1].credit]))
self.assertEqual(len(currencies[1].credit), 3)
@ -299,7 +299,7 @@ class CashReceiptVoucherTestCase(unittest.TestCase):
self.assertEqual(currencies[2].debit[0].no, 3)
self.assertEqual(currencies[2].debit[0].account.code,
Accounts.CASH)
self.assertIsNone(currencies[2].debit[0].summary)
self.assertIsNone(currencies[2].debit[0].description)
self.assertEqual(currencies[2].debit[0].amount,
sum([x.amount for x in currencies[2].credit]))
self.assertEqual(len(currencies[2].credit), 2)
@ -424,7 +424,7 @@ class CashReceiptVoucherTestCase(unittest.TestCase):
self.assertEqual(currencies1[0].debit[0].no, 1)
self.assertEqual(currencies1[0].debit[0].account.code,
Accounts.CASH)
self.assertIsNone(currencies1[0].debit[0].summary)
self.assertIsNone(currencies1[0].debit[0].description)
self.assertEqual(currencies1[0].debit[0].amount,
sum([x.amount for x in currencies1[0].credit]))
self.assertEqual(len(currencies1[0].credit), 2)
@ -443,7 +443,7 @@ class CashReceiptVoucherTestCase(unittest.TestCase):
self.assertEqual(currencies1[1].debit[0].no, 2)
self.assertEqual(currencies1[1].debit[0].account.code,
Accounts.CASH)
self.assertIsNone(currencies1[1].debit[0].summary)
self.assertIsNone(currencies1[1].debit[0].description)
self.assertEqual(currencies1[1].debit[0].amount,
sum([x.amount for x in currencies1[1].credit]))
self.assertEqual(len(currencies1[1].credit), 2)
@ -465,7 +465,7 @@ class CashReceiptVoucherTestCase(unittest.TestCase):
self.assertEqual(currencies1[2].debit[0].no, 3)
self.assertEqual(currencies1[2].debit[0].account.code,
Accounts.CASH)
self.assertIsNone(currencies1[2].debit[0].summary)
self.assertIsNone(currencies1[2].debit[0].description)
self.assertEqual(currencies1[2].debit[0].amount,
sum([x.amount for x in currencies1[2].credit]))
self.assertEqual(len(currencies1[2].credit), 3)
@ -843,7 +843,7 @@ class CashDisbursementVoucherTestCase(unittest.TestCase):
self.assertEqual(currencies[0].credit[0].no, 1)
self.assertEqual(currencies[0].credit[0].account.code,
Accounts.CASH)
self.assertIsNone(currencies[0].credit[0].summary)
self.assertIsNone(currencies[0].credit[0].description)
self.assertEqual(currencies[0].credit[0].amount,
sum([x.amount for x in currencies[0].debit]))
@ -852,20 +852,20 @@ class CashDisbursementVoucherTestCase(unittest.TestCase):
self.assertEqual(currencies[1].debit[0].no, 3)
self.assertEqual(currencies[1].debit[0].account.code,
Accounts.BANK)
self.assertEqual(currencies[1].debit[0].summary, "Deposit")
self.assertEqual(currencies[1].debit[0].description, "Deposit")
self.assertEqual(currencies[1].debit[1].no, 4)
self.assertEqual(currencies[1].debit[1].account.code,
Accounts.OFFICE)
self.assertEqual(currencies[1].debit[1].summary, "Pens")
self.assertEqual(currencies[1].debit[1].description, "Pens")
self.assertEqual(currencies[1].debit[2].no, 5)
self.assertEqual(currencies[1].debit[2].account.code,
Accounts.CASH)
self.assertIsNone(currencies[1].debit[2].summary)
self.assertIsNone(currencies[1].debit[2].description)
self.assertEqual(len(currencies[1].credit), 1)
self.assertEqual(currencies[1].credit[0].no, 2)
self.assertEqual(currencies[1].credit[0].account.code,
Accounts.CASH)
self.assertIsNone(currencies[1].credit[0].summary)
self.assertIsNone(currencies[1].credit[0].description)
self.assertEqual(currencies[1].credit[0].amount,
sum([x.amount for x in currencies[1].debit]))
@ -881,7 +881,7 @@ class CashDisbursementVoucherTestCase(unittest.TestCase):
self.assertEqual(currencies[2].credit[0].no, 3)
self.assertEqual(currencies[2].credit[0].account.code,
Accounts.CASH)
self.assertIsNone(currencies[2].credit[0].summary)
self.assertIsNone(currencies[2].credit[0].description)
self.assertEqual(currencies[2].credit[0].amount,
sum([x.amount for x in currencies[2].debit]))
@ -1008,7 +1008,7 @@ class CashDisbursementVoucherTestCase(unittest.TestCase):
self.assertEqual(currencies1[0].credit[0].no, 1)
self.assertEqual(currencies1[0].credit[0].account.code,
Accounts.CASH)
self.assertIsNone(currencies1[0].credit[0].summary)
self.assertIsNone(currencies1[0].credit[0].description)
self.assertEqual(currencies1[0].credit[0].amount,
sum([x.amount for x in currencies1[0].debit]))
@ -1019,7 +1019,7 @@ class CashDisbursementVoucherTestCase(unittest.TestCase):
self.assertEqual(currencies1[1].debit[0].no, 3)
self.assertEqual(currencies1[1].debit[0].account.code,
Accounts.CASH)
self.assertIsNone(currencies1[1].debit[0].summary)
self.assertIsNone(currencies1[1].debit[0].description)
self.assertEqual(currencies1[1].debit[1].id,
currencies0[2].debit[1].id)
self.assertEqual(currencies1[1].debit[1].no, 4)
@ -1030,7 +1030,7 @@ class CashDisbursementVoucherTestCase(unittest.TestCase):
self.assertEqual(currencies1[1].credit[0].no, 2)
self.assertEqual(currencies1[1].credit[0].account.code,
Accounts.CASH)
self.assertIsNone(currencies1[1].credit[0].summary)
self.assertIsNone(currencies1[1].credit[0].description)
self.assertEqual(currencies1[1].credit[0].amount,
sum([x.amount for x in currencies1[1].debit]))
@ -1040,26 +1040,26 @@ class CashDisbursementVoucherTestCase(unittest.TestCase):
self.assertEqual(currencies1[2].debit[0].no, 5)
self.assertEqual(currencies1[2].debit[0].account.code,
Accounts.TRAVEL)
self.assertIsNone(currencies1[2].debit[0].summary)
self.assertIsNone(currencies1[2].debit[0].description)
self.assertEqual(currencies1[2].debit[1].id,
currencies0[1].debit[2].id)
self.assertEqual(currencies1[2].debit[1].no, 6)
self.assertEqual(currencies1[2].debit[1].account.code,
Accounts.CASH)
self.assertIsNone(currencies1[2].debit[1].summary)
self.assertIsNone(currencies1[2].debit[1].description)
self.assertEqual(currencies1[2].debit[2].id,
currencies0[1].debit[0].id)
self.assertEqual(currencies1[2].debit[2].no, 7)
self.assertEqual(currencies1[2].debit[2].account.code,
Accounts.BANK)
self.assertEqual(currencies1[2].debit[2].summary, "Deposit")
self.assertEqual(currencies1[2].debit[2].description, "Deposit")
self.assertEqual(len(currencies1[2].credit), 1)
self.assertEqual(currencies1[2].credit[0].id,
currencies0[1].credit[0].id)
self.assertEqual(currencies1[2].credit[0].no, 3)
self.assertEqual(currencies1[2].credit[0].account.code,
Accounts.CASH)
self.assertIsNone(currencies1[2].credit[0].summary)
self.assertIsNone(currencies1[2].credit[0].description)
self.assertEqual(currencies1[2].credit[0].amount,
sum([x.amount for x in currencies1[2].debit]))
@ -1464,15 +1464,15 @@ class TransferVoucherTestCase(unittest.TestCase):
self.assertEqual(currencies[1].debit[0].no, 3)
self.assertEqual(currencies[1].debit[0].account.code,
Accounts.BANK)
self.assertEqual(currencies[1].debit[0].summary, "Deposit")
self.assertEqual(currencies[1].debit[0].description, "Deposit")
self.assertEqual(currencies[1].debit[1].no, 4)
self.assertEqual(currencies[1].debit[1].account.code,
Accounts.OFFICE)
self.assertEqual(currencies[1].debit[1].summary, "Pens")
self.assertEqual(currencies[1].debit[1].description, "Pens")
self.assertEqual(currencies[1].debit[2].no, 5)
self.assertEqual(currencies[1].debit[2].account.code,
Accounts.CASH)
self.assertIsNone(currencies[1].debit[2].summary)
self.assertIsNone(currencies[1].debit[2].description)
self.assertEqual(len(currencies[1].credit), 3)
self.assertEqual(currencies[1].credit[0].no, 3)
self.assertEqual(currencies[1].credit[0].account.code,
@ -1668,7 +1668,7 @@ class TransferVoucherTestCase(unittest.TestCase):
self.assertEqual(currencies1[1].debit[0].no, 3)
self.assertEqual(currencies1[1].debit[0].account.code,
Accounts.CASH)
self.assertIsNone(currencies1[1].debit[0].summary)
self.assertIsNone(currencies1[1].debit[0].description)
self.assertEqual(currencies1[1].debit[1].id,
currencies0[2].debit[1].id)
self.assertEqual(currencies1[1].debit[1].no, 4)
@ -1692,19 +1692,19 @@ class TransferVoucherTestCase(unittest.TestCase):
self.assertEqual(currencies1[2].debit[0].no, 5)
self.assertEqual(currencies1[2].debit[0].account.code,
Accounts.TRAVEL)
self.assertIsNone(currencies1[2].debit[0].summary)
self.assertIsNone(currencies1[2].debit[0].description)
self.assertEqual(currencies1[2].debit[1].id,
currencies0[1].debit[2].id)
self.assertEqual(currencies1[2].debit[1].no, 6)
self.assertEqual(currencies1[2].debit[1].account.code,
Accounts.CASH)
self.assertIsNone(currencies1[2].debit[1].summary)
self.assertIsNone(currencies1[2].debit[1].description)
self.assertEqual(currencies1[2].debit[2].id,
currencies0[1].debit[0].id)
self.assertEqual(currencies1[2].debit[2].no, 7)
self.assertEqual(currencies1[2].debit[2].account.code,
Accounts.BANK)
self.assertEqual(currencies1[2].debit[2].summary, "Deposit")
self.assertEqual(currencies1[2].debit[2].description, "Deposit")
self.assertEqual(len(currencies1[2].credit), 3)
self.assertNotIn(currencies1[2].credit[0].id, old_id)
self.assertEqual(currencies1[2].credit[0].no, 5)
@ -1824,7 +1824,7 @@ class TransferVoucherTestCase(unittest.TestCase):
self.assertEqual(currencies1[0].debit[0].no, 1)
self.assertEqual(currencies1[0].debit[0].account.code,
Accounts.CASH)
self.assertIsNone(currencies1[0].debit[0].summary)
self.assertIsNone(currencies1[0].debit[0].description)
self.assertEqual(currencies1[0].debit[0].amount,
sum([x.amount for x in currencies1[0].credit]))
self.assertEqual(len(currencies1[0].credit), 2)
@ -1843,7 +1843,7 @@ class TransferVoucherTestCase(unittest.TestCase):
self.assertEqual(currencies1[1].debit[0].no, 2)
self.assertEqual(currencies1[1].debit[0].account.code,
Accounts.CASH)
self.assertIsNone(currencies1[1].debit[0].summary)
self.assertIsNone(currencies1[1].debit[0].description)
self.assertEqual(currencies1[1].debit[0].amount,
sum([x.amount for x in currencies1[1].credit]))
self.assertEqual(len(currencies1[1].credit), 2)
@ -1865,7 +1865,7 @@ class TransferVoucherTestCase(unittest.TestCase):
self.assertEqual(currencies1[2].debit[0].no, 3)
self.assertEqual(currencies1[2].debit[0].account.code,
Accounts.CASH)
self.assertIsNone(currencies1[2].debit[0].summary)
self.assertIsNone(currencies1[2].debit[0].description)
self.assertEqual(currencies1[2].debit[0].amount,
sum([x.amount for x in currencies1[2].credit]))
self.assertEqual(len(currencies1[2].credit), 3)
@ -1932,7 +1932,7 @@ class TransferVoucherTestCase(unittest.TestCase):
self.assertEqual(currencies1[0].credit[0].no, 1)
self.assertEqual(currencies1[0].credit[0].account.code,
Accounts.CASH)
self.assertIsNone(currencies1[0].credit[0].summary)
self.assertIsNone(currencies1[0].credit[0].description)
self.assertEqual(currencies1[0].credit[0].amount,
sum([x.amount for x in currencies1[0].debit]))
@ -1943,7 +1943,7 @@ class TransferVoucherTestCase(unittest.TestCase):
self.assertEqual(currencies1[1].debit[0].no, 3)
self.assertEqual(currencies1[1].debit[0].account.code,
Accounts.CASH)
self.assertIsNone(currencies1[1].debit[0].summary)
self.assertIsNone(currencies1[1].debit[0].description)
self.assertEqual(currencies1[1].debit[1].id,
currencies0[2].debit[1].id)
self.assertEqual(currencies1[1].debit[1].no, 4)
@ -1954,7 +1954,7 @@ class TransferVoucherTestCase(unittest.TestCase):
self.assertEqual(currencies1[1].credit[0].no, 2)
self.assertEqual(currencies1[1].credit[0].account.code,
Accounts.CASH)
self.assertIsNone(currencies1[1].credit[0].summary)
self.assertIsNone(currencies1[1].credit[0].description)
self.assertEqual(currencies1[1].credit[0].amount,
sum([x.amount for x in currencies1[1].debit]))
@ -1964,26 +1964,26 @@ class TransferVoucherTestCase(unittest.TestCase):
self.assertEqual(currencies1[2].debit[0].no, 5)
self.assertEqual(currencies1[2].debit[0].account.code,
Accounts.TRAVEL)
self.assertIsNone(currencies1[2].debit[0].summary)
self.assertIsNone(currencies1[2].debit[0].description)
self.assertEqual(currencies1[2].debit[1].id,
currencies0[1].debit[2].id)
self.assertEqual(currencies1[2].debit[1].no, 6)
self.assertEqual(currencies1[2].debit[1].account.code,
Accounts.CASH)
self.assertIsNone(currencies1[2].debit[1].summary)
self.assertIsNone(currencies1[2].debit[1].description)
self.assertEqual(currencies1[2].debit[2].id,
currencies0[1].debit[0].id)
self.assertEqual(currencies1[2].debit[2].no, 7)
self.assertEqual(currencies1[2].debit[2].account.code,
Accounts.BANK)
self.assertEqual(currencies1[2].debit[2].summary, "Deposit")
self.assertEqual(currencies1[2].debit[2].description, "Deposit")
self.assertEqual(len(currencies1[2].credit), 1)
self.assertEqual(currencies1[2].credit[0].id,
currencies0[1].credit[0].id)
self.assertEqual(currencies1[2].credit[0].no, 3)
self.assertEqual(currencies1[2].credit[0].account.code,
Accounts.CASH)
self.assertIsNone(currencies1[2].credit[0].summary)
self.assertIsNone(currencies1[2].credit[0].description)
self.assertEqual(currencies1[2].credit[0].amount,
sum([x.amount for x in currencies1[2].debit]))

View File

@ -32,12 +32,12 @@ from testlib_voucher import Accounts, match_voucher_detail, NEXT_URI
class VoucherLineItemData:
"""The voucher line item data."""
def __init__(self, account: str, summary: str, amount: str,
def __init__(self, account: str, description: str, amount: str,
original_line_item: VoucherLineItemData | None = None):
"""Constructs the voucher line item data.
:param account: The account code.
:param summary: The summary.
:param description: The description.
:param amount: The amount.
:param original_line_item: The original voucher line item.
"""
@ -47,7 +47,7 @@ class VoucherLineItemData:
self.original_line_item: VoucherLineItemData | None \
= original_line_item
self.account: str = account
self.summary: str = summary
self.description: str = description
self.amount: Decimal = Decimal(amount)
def form(self, prefix: str, side: str, index: int, is_update: bool) \
@ -62,7 +62,7 @@ class VoucherLineItemData:
"""
prefix = f"{prefix}-{side}-{index}"
form: dict[str, str] = {f"{prefix}-account_code": self.account,
f"{prefix}-summary": self.summary,
f"{prefix}-description": self.description,
f"{prefix}-amount": str(self.amount)}
if is_update and self.id != -1:
form[f"{prefix}-eid"] = str(self.id)
@ -174,18 +174,18 @@ class TestData:
self.client: httpx.Client = client
self.csrf_token: str = csrf_token
def couple(summary: str, amount: str, debit: str, credit: str) \
def couple(description: str, amount: str, debit: str, credit: str) \
-> tuple[VoucherLineItemData, VoucherLineItemData]:
"""Returns a couple of debit-credit line items.
:param summary: The summary.
:param description: The description.
:param amount: The amount.
:param debit: The debit account code.
:param credit: The credit account code.
:return: The debit line item and credit line item.
"""
return VoucherLineItemData(debit, summary, amount),\
VoucherLineItemData(credit, summary, amount)
return VoucherLineItemData(debit, description, amount),\
VoucherLineItemData(credit, description, amount)
# Receivable original line items
self.e_r_or1d, self.e_r_or1c = couple(

View File

@ -68,58 +68,58 @@ def get_add_form(csrf_token: str) -> dict[str, str]:
"currency-0-code": "USD",
"currency-0-debit-0-no": "16",
"currency-0-debit-0-account_code": Accounts.CASH,
"currency-0-debit-0-summary": " ",
"currency-0-debit-0-description": " ",
"currency-0-debit-0-amount": " 495.26 ",
"currency-0-debit-6-no": "2",
"currency-0-debit-6-account_code": Accounts.BANK,
"currency-0-debit-6-summary": " Deposit ",
"currency-0-debit-6-description": " Deposit ",
"currency-0-debit-6-amount": "6000",
"currency-0-debit-12-no": "2",
"currency-0-debit-12-account_code": Accounts.OFFICE,
"currency-0-debit-12-summary": " Pens ",
"currency-0-debit-12-description": " Pens ",
"currency-0-debit-12-amount": "4.99",
"currency-0-credit-2-no": "6",
"currency-0-credit-2-account_code": Accounts.SERVICE,
"currency-0-credit-2-summary": " ",
"currency-0-credit-2-description": " ",
"currency-0-credit-2-amount": "5500",
"currency-0-credit-7-account_code": Accounts.SALES,
"currency-0-credit-7-summary": " ",
"currency-0-credit-7-description": " ",
"currency-0-credit-7-amount": "950",
"currency-0-credit-27-account_code": Accounts.INTEREST,
"currency-0-credit-27-summary": " ",
"currency-0-credit-27-description": " ",
"currency-0-credit-27-amount": "50.25",
"currency-3-no": "2",
"currency-3-code": "JPY",
"currency-3-debit-2-no": "2",
"currency-3-debit-2-account_code": Accounts.CASH,
"currency-3-debit-2-summary": " ",
"currency-3-debit-2-description": " ",
"currency-3-debit-2-amount": "15000",
"currency-3-debit-9-no": "5",
"currency-3-debit-9-account_code": Accounts.BANK,
"currency-3-debit-9-summary": " Deposit ",
"currency-3-debit-9-description": " Deposit ",
"currency-3-debit-9-amount": "95000",
"currency-3-credit-3-account_code": Accounts.AGENCY,
"currency-3-credit-3-summary": " Realtor ",
"currency-3-credit-3-description": " Realtor ",
"currency-3-credit-3-amount": "65000",
"currency-3-credit-5-no": "4",
"currency-3-credit-5-account_code": Accounts.DONATION,
"currency-3-credit-5-summary": " Donation ",
"currency-3-credit-5-description": " Donation ",
"currency-3-credit-5-amount": "45000",
"currency-16-code": "TWD",
"currency-16-debit-2-no": "2",
"currency-16-debit-2-account_code": Accounts.CASH,
"currency-16-debit-2-summary": " ",
"currency-16-debit-2-description": " ",
"currency-16-debit-2-amount": "10000",
"currency-16-debit-9-no": "2",
"currency-16-debit-9-account_code": Accounts.TRAVEL,
"currency-16-debit-9-summary": " Gas ",
"currency-16-debit-9-description": " Gas ",
"currency-16-debit-9-amount": "30000",
"currency-16-credit-6-no": "6",
"currency-16-credit-6-account_code": Accounts.RENT,
"currency-16-credit-6-summary": " Rent ",
"currency-16-credit-6-description": " Rent ",
"currency-16-credit-6-amount": "35000",
"currency-16-credit-9-account_code": Accounts.DONATION,
"currency-16-credit-9-summary": " Donation ",
"currency-16-credit-9-description": " Donation ",
"currency-16-credit-9-amount": "5000",
"note": f"\n \n\n \n{NON_EMPTY_NOTE} \n \n\n "}
@ -167,9 +167,9 @@ def get_unchanged_update_form(voucher_id: int, app: Flask, csrf_token: str) \
form[f"{prefix}-eid"] = str(line_item.id)
form[f"{prefix}-no"] = str(line_item_no)
form[f"{prefix}-account_code"] = line_item.account.code
form[f"{prefix}-summary"] \
= " " if line_item.summary is None \
else f" {line_item.summary} "
form[f"{prefix}-description"] \
= " " if line_item.description is None \
else f" {line_item.description} "
form[f"{prefix}-amount"] = str(line_item.amount)
line_item_indices_used = set()
@ -181,8 +181,8 @@ def get_unchanged_update_form(voucher_id: int, app: Flask, csrf_token: str) \
form[f"{prefix}-eid"] = str(line_item.id)
form[f"{prefix}-no"] = str(line_item_no)
form[f"{prefix}-account_code"] = line_item.account.code
form[f"{prefix}-summary"] \
= " " if line_item.summary is None else f" {line_item.summary} "
form[f"{prefix}-description"] \
= " " if line_item.description is None else f" {line_item.description} "
form[f"{prefix}-amount"] = str(line_item.amount)
return form
@ -335,19 +335,19 @@ def __mess_up_currencies(form: dict[str, str]) -> dict[str, str]:
f"{prefix}no": str(1 + randbelow(min_no - 1)),
f"{prefix}debit-0-no": "6",
f"{prefix}debit-0-account_code": Accounts.OFFICE,
f"{prefix}debit-0-summary": " Envelop ",
f"{prefix}debit-0-description": " Envelop ",
f"{prefix}debit-0-amount": "5.45",
f"{prefix}debit-14-no": "6",
f"{prefix}debit-14-account_code": Accounts.CASH,
f"{prefix}debit-14-summary": " ",
f"{prefix}debit-14-description": " ",
f"{prefix}debit-14-amount": "14.55",
f"{prefix}credit-16-no": "7",
f"{prefix}credit-16-account_code": Accounts.RENT,
f"{prefix}credit-16-summary": " Bike ",
f"{prefix}credit-16-description": " Bike ",
f"{prefix}credit-16-amount": "19.5",
f"{prefix}credit-22-no": "5",
f"{prefix}credit-22-account_code": Accounts.DONATION,
f"{prefix}credit-22-summary": " Artist ",
f"{prefix}credit-22-description": " Artist ",
f"{prefix}credit-22-amount": "0.5",
})
# Swap the USD and TWD order