Renamed "side" to "debit-credit".

This commit is contained in:
依瑪貓 2023-03-20 20:35:10 +08:00
parent 02fffc3400
commit e26af6f3fc
22 changed files with 225 additions and 224 deletions

View File

@ -35,10 +35,10 @@ class AccountSelector {
#lineItemEditor;
/**
* The side, either "debit" or "credit"
* Either "debit" or "credit"
* @type {string}
*/
#side;
#debitCredit;
/**
* The prefix of the HTML ID and class
@ -86,12 +86,12 @@ class AccountSelector {
* Constructs an account selector.
*
* @param lineItemEditor {VoucherLineItemEditor} the line item editor
* @param side {string} the side, either "debit" or "credit"
* @param debitCredit {string} either "debit" or "credit"
*/
constructor(lineItemEditor, side) {
constructor(lineItemEditor, debitCredit) {
this.#lineItemEditor = lineItemEditor
this.#side = side;
this.#prefix = "accounting-account-selector-" + side;
this.#debitCredit = debitCredit;
this.#prefix = "accounting-account-selector-" + debitCredit;
this.#query = document.getElementById(this.#prefix + "-query");
this.#queryNoResult = document.getElementById(this.#prefix + "-option-no-result");
this.#optionList = document.getElementById(this.#prefix + "-option-list");
@ -143,7 +143,7 @@ class AccountSelector {
* @return {string[]} the account codes that are used in the form
*/
#getCodesUsedInForm() {
const inUse = this.#lineItemEditor.form.getAccountCodesUsed(this.#side);
const inUse = this.#lineItemEditor.form.getAccountCodesUsed(this.#debitCredit);
if (this.#lineItemEditor.accountCode !== null) {
inUse.push(this.#lineItemEditor.accountCode);
}
@ -217,7 +217,7 @@ class AccountSelector {
const selectors = {}
const modals = Array.from(document.getElementsByClassName("accounting-account-selector"));
for (const modal of modals) {
selectors[modal.dataset.side] = new AccountSelector(lineItemEditor, modal.dataset.side);
selectors[modal.dataset.debitCredit] = new AccountSelector(lineItemEditor, modal.dataset.debitCredit);
}
return selectors;
}

View File

@ -53,10 +53,10 @@ class DescriptionEditor {
#modal;
/**
* The side, either "debit" or "credit"
* Either "debit" or "credit"
* @type {string}
*/
side;
debitCredit;
/**
* The current tab
@ -110,12 +110,12 @@ class DescriptionEditor {
* Constructs a description editor.
*
* @param lineItemEditor {VoucherLineItemEditor} the line item editor
* @param side {string} the side, either "debit" or "credit"
* @param debitCredit {string} either "debit" or "credit"
*/
constructor(lineItemEditor, side) {
constructor(lineItemEditor, debitCredit) {
this.#lineItemEditor = lineItemEditor;
this.side = side;
this.prefix = "accounting-description-editor-" + side;
this.debitCredit = debitCredit;
this.prefix = "accounting-description-editor-" + debitCredit;
this.#form = document.getElementById(this.prefix);
this.#modal = document.getElementById(this.prefix + "-modal");
this.description = document.getElementById(this.prefix + "-description");
@ -253,7 +253,7 @@ class DescriptionEditor {
const editors = {}
const forms = Array.from(document.getElementsByClassName("accounting-description-editor"));
for (const form of forms) {
editors[form.dataset.side] = new DescriptionEditor(lineItemEditor, form.dataset.side);
editors[form.dataset.debitCredit] = new DescriptionEditor(lineItemEditor, form.dataset.debitCredit);
}
return editors;
}

View File

@ -77,9 +77,9 @@ class OriginalLineItemSelector {
#currencyCode;
/**
* The side, either "credit" or "debit"
* Either "credit" or "debit"
*/
#side;
#debitCredit;
/**
* Constructs an original line item selector.
@ -157,7 +157,7 @@ class OriginalLineItemSelector {
#filterOptions() {
let hasAnyMatched = false;
for (const option of this.#options) {
if (option.isMatched(this.#side, this.#currencyCode, this.#query.value)) {
if (option.isMatched(this.#debitCredit, this.#currencyCode, this.#query.value)) {
option.setShown(true);
hasAnyMatched = true;
} else {
@ -179,7 +179,7 @@ class OriginalLineItemSelector {
*/
onOpen() {
this.#currencyCode = this.lineItemEditor.getCurrencyCode();
this.#side = this.lineItemEditor.side;
this.#debitCredit = this.lineItemEditor.debitCredit;
for (const option of this.#options) {
option.setActive(option.id === this.lineItemEditor.originalLineItemId);
}
@ -220,10 +220,10 @@ class OriginalLineItem {
date;
/**
* The side, either "debit" or "credit"
* Either "debit" or "credit"
* @type {string}
*/
#side;
#debitCredit;
/**
* The currency code
@ -290,7 +290,7 @@ class OriginalLineItem {
this.#element = element;
this.id = element.dataset.id;
this.date = element.dataset.date;
this.#side = element.dataset.side;
this.#debitCredit = element.dataset.debitCredit;
this.#currencyCode = element.dataset.currencyCode;
this.accountCode = element.dataset.accountCode;
this.accountText = element.dataset.accountText;
@ -335,27 +335,27 @@ class OriginalLineItem {
/**
* Returns whether the original matches.
*
* @param side {string} the side, either "debit" or "credit"
* @param debitCredit {string} either "debit" or "credit"
* @param currencyCode {string} the currency code
* @param query {string|null} the query term
*/
isMatched(side, currencyCode, query = null) {
isMatched(debitCredit, currencyCode, query = null) {
return this.netBalance.greaterThan(0)
&& this.date <= this.#selector.lineItemEditor.form.getDate()
&& this.#isSideMatches(side)
&& this.#isDebitCreditMatches(debitCredit)
&& this.#currencyCode === currencyCode
&& this.#isQueryMatches(query);
}
/**
* Returns whether the original line item matches the debit or credit side.
* Returns whether the original line item matches the debit or credit.
*
* @param side {string} the side, either "debit" or credit
* @param debitCredit {string} either "debit" or credit
* @return {boolean} true if the option matches, or false otherwise
*/
#isSideMatches(side) {
return (side === "debit" && this.#side === "credit")
|| (side === "credit" && this.#side === "debit");
#isDebitCreditMatches(debitCredit) {
return (debitCredit === "debit" && this.#debitCredit === "credit")
|| (debitCredit === "credit" && this.#debitCredit === "debit");
}
/**

View File

@ -195,13 +195,13 @@ class VoucherForm {
/**
* Returns all the line items in the form.
*
* @param side {string|null} the side, either "debit" or "credit", or null for both
* @param debitCredit {string|null} Either "debit" or "credit", or null for both
* @return {LineItemSubForm[]} all the line item sub-forms
*/
getLineItems(side = null) {
getLineItems(debitCredit = null) {
const lineItems = [];
for (const currency of this.#currencies) {
lineItems.push(...currency.getLineItems(side));
lineItems.push(...currency.getLineItems(debitCredit));
}
return lineItems;
}
@ -209,11 +209,11 @@ class VoucherForm {
/**
* Returns the account codes used in the form.
*
* @param side {string} the side, either "debit" or "credit"
* @param debitCredit {string} either "debit" or "credit"
* @return {string[]} the account codes used in the form
*/
getAccountCodesUsed(side) {
return this.getLineItems(side).map((lineItem) => lineItem.getAccountCode())
getAccountCodesUsed(debitCredit) {
return this.getLineItems(debitCredit).map((lineItem) => lineItem.getAccountCode())
.filter((code) => code !== null);
}
@ -406,14 +406,14 @@ class CurrencySubForm {
deleteButton;
/**
* The debit side
* @type {SideSubForm|null}
* The debit sub-form
* @type {DebitCreditSubForm|null}
*/
#debit;
/**
* The credit side
* @type {SideSubForm|null}
* The credit sub-form
* @type {DebitCreditSubForm|null}
*/
#credit;
@ -435,9 +435,9 @@ class CurrencySubForm {
this.#codeSelect = document.getElementById(this.#prefix + "-code-select");
this.deleteButton = document.getElementById(this.#prefix + "-delete");
const debitElement = document.getElementById(this.#prefix + "-debit");
this.#debit = debitElement === null? null: new SideSubForm(this, debitElement, "debit");
this.#debit = debitElement === null? null: new DebitCreditSubForm(this, debitElement, "debit");
const creditElement = document.getElementById(this.#prefix + "-credit");
this.#credit = creditElement == null? null: new SideSubForm(this, creditElement, "credit");
this.#credit = creditElement == null? null: new DebitCreditSubForm(this, creditElement, "credit");
this.#codeSelect.onchange = () => this.#code.value = this.#codeSelect.value;
this.deleteButton.onclick = () => {
this.element.parentElement.removeChild(this.element);
@ -457,15 +457,15 @@ class CurrencySubForm {
/**
* Returns all the line items in the form.
*
* @param side {string|null} the side, either "debit" or "credit", or null for both
* @param debitCredit {string|null} either "debit" or "credit", or null for both
* @return {LineItemSubForm[]} all the line item sub-forms
*/
getLineItems(side = null) {
getLineItems(debitCredit = null) {
const lineItems = []
for (const sideSubForm of [this.#debit, this.#credit]) {
if (sideSubForm !== null ) {
if (side === null || sideSubForm.side === side) {
lineItems.push(...sideSubForm.lineItems);
for (const debitCreditSubForm of [this.#debit, this.#credit]) {
if (debitCreditSubForm !== null ) {
if (debitCredit === null || debitCreditSubForm.debitCredit === debitCredit) {
lineItems.push(...debitCreditSubForm.lineItems);
}
}
}
@ -524,10 +524,10 @@ class CurrencySubForm {
}
/**
* The debit or credit side sub-form
* The debit or credit sub-form
*
*/
class SideSubForm {
class DebitCreditSubForm {
/**
* The currency sub-form
@ -548,10 +548,10 @@ class SideSubForm {
#currencyIndex;
/**
* The side, either "debit" or "credit"
* Either "debit" or "credit"
* @type {string}
*/
side;
debitCredit;
/**
* The prefix of the HTML ID and class
@ -590,18 +590,18 @@ class SideSubForm {
#addLineItemButton;
/**
* Constructs a debit or credit side sub-form
* Constructs a debit or credit sub-form
*
* @param currency {CurrencySubForm} the currency sub-form
* @param element {HTMLDivElement} the element
* @param side {string} the side, either "debit" or "credit"
* @param debitCredit {string} either "debit" or "credit"
*/
constructor(currency, element, side) {
constructor(currency, element, debitCredit) {
this.currency = currency;
this.#element = element;
this.#currencyIndex = currency.index;
this.side = side;
this.#prefix = "accounting-currency-" + String(this.#currencyIndex) + "-" + side;
this.debitCredit = debitCredit;
this.#prefix = "accounting-currency-" + String(this.#currencyIndex) + "-" + debitCredit;
this.#error = document.getElementById(this.#prefix + "-error");
this.#lineItemList = document.getElementById(this.#prefix + "-list");
// noinspection JSValidateTypes
@ -622,7 +622,7 @@ class SideSubForm {
const newIndex = 1 + (this.lineItems.length === 0? 0: Math.max(...this.lineItems.map((lineItem) => lineItem.lineItemIndex)));
const html = this.currency.form.lineItemTemplate
.replaceAll("CURRENCY_INDEX", escapeHtml(String(this.#currencyIndex)))
.replaceAll("SIDE", escapeHtml(this.side))
.replaceAll("DEBIT_CREDIT", escapeHtml(this.debitCredit))
.replaceAll("LINE_ITEM_INDEX", escapeHtml(String(newIndex)));
this.#lineItemList.insertAdjacentHTML("beforeend", html);
const lineItem = new LineItemSubForm(this, document.getElementById(this.#prefix + "-" + String(newIndex)));
@ -742,10 +742,10 @@ class SideSubForm {
class LineItemSubForm {
/**
* The debit or credit side sub-form
* @type {SideSubForm}
* The debit or credit sub-form
* @type {DebitCreditSubForm}
*/
sideSubForm;
debitCreditSubForm;
/**
* The element
@ -754,10 +754,10 @@ class LineItemSubForm {
element;
/**
* The side, either "debit" or "credit"
* Either "debit" or "credit"
* @type {string}
*/
side;
debitCredit;
/**
* The line item index
@ -858,16 +858,16 @@ class LineItemSubForm {
/**
* Constructs the line item sub-form.
*
* @param side {SideSubForm} the debit or credit side sub-form
* @param debitCredit {DebitCreditSubForm} the debit or credit sub-form
* @param element {HTMLLIElement} the element
*/
constructor(side, element) {
this.sideSubForm = side;
constructor(debitCredit, element) {
this.debitCreditSubForm = debitCredit;
this.element = element;
this.side = element.dataset.side;
this.debitCredit = element.dataset.debitCredit;
this.lineItemIndex = parseInt(element.dataset.lineItemIndex);
this.isMatched = element.classList.contains("accounting-matched-line-item");
this.#prefix = "accounting-currency-" + element.dataset.currencyIndex + "-" + this.side + "-" + this.lineItemIndex;
this.#prefix = "accounting-currency-" + element.dataset.currencyIndex + "-" + this.debitCredit + "-" + this.lineItemIndex;
this.#control = document.getElementById(this.#prefix + "-control");
this.#error = document.getElementById(this.#prefix + "-error");
this.no = document.getElementById(this.#prefix + "-no");
@ -881,10 +881,10 @@ class LineItemSubForm {
this.#amount = document.getElementById(this.#prefix + "-amount");
this.#amountText = document.getElementById(this.#prefix + "-amount-text");
this.deleteButton = document.getElementById(this.#prefix + "-delete");
this.#control.onclick = () => this.sideSubForm.currency.form.lineItemEditor.onEdit(this);
this.#control.onclick = () => this.debitCreditSubForm.currency.form.lineItemEditor.onEdit(this);
this.deleteButton.onclick = () => {
this.element.parentElement.removeChild(this.element);
this.sideSubForm.deleteLineItem(this);
this.debitCreditSubForm.deleteLineItem(this);
};
}
@ -1019,9 +1019,9 @@ class LineItemSubForm {
this.#amount.value = editor.amount;
this.#amountText.innerText = formatDecimal(new Decimal(editor.amount));
this.validate();
this.sideSubForm.updateTotal();
this.sideSubForm.currency.updateCodeSelectorStatus();
this.sideSubForm.currency.form.updateMinDate();
this.debitCreditSubForm.updateTotal();
this.debitCreditSubForm.currency.updateCodeSelectorStatus();
this.debitCreditSubForm.currency.form.updateMinDate();
}
}

View File

@ -47,10 +47,10 @@ class VoucherLineItemEditor {
#modal;
/**
* The side, either "debit" or "credit"
* Either "debit" or "credit"
* @type {string}
*/
side;
debitCredit;
/**
* The prefix of the HTML ID and class
@ -143,10 +143,10 @@ class VoucherLineItemEditor {
lineItem;
/**
* The debit or credit side sub-form
* @type {SideSubForm}
* The debit or credit sub-form
* @type {DebitCreditSubForm}
*/
#sideSubForm;
#debitCreditSubForm;
/**
* Whether the voucher line item needs offset
@ -241,13 +241,13 @@ class VoucherLineItemEditor {
this.originalLineItemSelector = new OriginalLineItemSelector(this);
this.#originalLineItemControl.onclick = () => this.originalLineItemSelector.onOpen()
this.#originalLineItemDelete.onclick = () => this.clearOriginalLineItem();
this.#descriptionControl.onclick = () => this.#descriptionEditors[this.side].onOpen();
this.#accountControl.onclick = () => this.#accountSelectors[this.side].onOpen();
this.#descriptionControl.onclick = () => this.#descriptionEditors[this.debitCredit].onOpen();
this.#accountControl.onclick = () => this.#accountSelectors[this.debitCredit].onOpen();
this.#amountInput.onchange = () => this.#validateAmount();
this.#element.onsubmit = () => {
if (this.#validate()) {
if (this.lineItem === null) {
this.lineItem = this.#sideSubForm.addLineItem();
this.lineItem = this.#debitCreditSubForm.addLineItem();
}
this.amount = this.#amountInput.value;
this.lineItem.save(this);
@ -314,7 +314,7 @@ class VoucherLineItemEditor {
* @return {string} the currency code
*/
getCurrencyCode() {
return this.#sideSubForm.currency.getCurrencyCode();
return this.#debitCreditSubForm.currency.getCurrencyCode();
}
/**
@ -478,12 +478,12 @@ class VoucherLineItemEditor {
/**
* The callback when adding a new voucher line item.
*
* @param side {SideSubForm} the debit or credit side sub-form
* @param debitCredit {DebitCreditSubForm} the debit or credit sub-form
*/
onAddNew(side) {
onAddNew(debitCredit) {
this.lineItem = null;
this.#sideSubForm = side;
this.side = this.#sideSubForm.side;
this.#debitCreditSubForm = debitCredit;
this.debitCredit = this.#debitCreditSubForm.debitCredit;
this.isNeedOffset = false;
this.#originalLineItemContainer.classList.add("d-none");
this.#originalLineItemControl.classList.remove("accounting-not-empty");
@ -518,8 +518,8 @@ class VoucherLineItemEditor {
*/
onEdit(lineItem) {
this.lineItem = lineItem;
this.#sideSubForm = lineItem.sideSubForm;
this.side = this.#sideSubForm.side;
this.#debitCreditSubForm = lineItem.debitCreditSubForm;
this.debitCredit = this.#debitCreditSubForm.debitCredit;
this.isNeedOffset = lineItem.isNeedOffset();
this.originalLineItemId = lineItem.getOriginalLineItemId();
this.originalLineItemDate = lineItem.getOriginalLineItemDate();
@ -575,11 +575,11 @@ class VoucherLineItemEditor {
#setEnableDescriptionAccount(isEnabled) {
if (isEnabled) {
this.#descriptionControl.dataset.bsToggle = "modal";
this.#descriptionControl.dataset.bsTarget = "#accounting-description-editor-" + this.#sideSubForm.side + "-modal";
this.#descriptionControl.dataset.bsTarget = "#accounting-description-editor-" + this.#debitCreditSubForm.debitCredit + "-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.dataset.bsTarget = "#accounting-account-selector-" + this.#debitCreditSubForm.debitCredit + "-modal";
this.#accountControl.classList.remove("accounting-disabled");
this.#accountControl.classList.add("accounting-clickable");
} else {

View File

@ -47,7 +47,7 @@ First written: 2023/2/25
<ul id="accounting-currency-{{ currency_index }}-debit-list" class="list-group accounting-line-item-list">
{% for line_item_form in debit_forms %}
{% with currency_index = currency_index,
side = "debit",
debit_credit = "debit",
line_item_index = loop.index,
only_one_line_item_form = debit_forms|length == 1,
form = line_item_form.form %}
@ -62,7 +62,7 @@ First written: 2023/2/25
</div>
<div>
<button id="accounting-currency-{{ currency_index }}-debit-add-line-item" class="btn btn-primary" type="button" data-currency-index="{{ currency_index }}" data-side="debit" data-bs-toggle="modal" data-bs-target="#accounting-line-item-editor-modal">
<button id="accounting-currency-{{ currency_index }}-debit-add-line-item" class="btn btn-primary" type="button" data-currency-index="{{ currency_index }}" data-debit-credit="debit" data-bs-toggle="modal" data-bs-target="#accounting-line-item-editor-modal">
<i class="fas fa-plus"></i>
{{ A_("New") }}
</button>

View File

@ -50,7 +50,7 @@ First written: 2023/2/25
{% with description_editor = form.description_editor.debit %}
{% include "accounting/voucher/include/description-editor-modal.html" %}
{% endwith %}
{% with side = "debit",
{% with debit_credit = "debit",
account_options = form.debit_account_options %}
{% include "accounting/voucher/include/account-selector-modal.html" %}
{% endwith %}

View File

@ -19,35 +19,35 @@ account-selector-modal.html: The modal for the account selector
Author: imacat@mail.imacat.idv.tw (imacat)
First written: 2023/2/25
#}
<div id="accounting-account-selector-{{ side }}-modal" class="modal fade accounting-account-selector" data-side="{{ side }}" tabindex="-1" aria-labelledby="accounting-account-selector-{{ side }}-modal-label" aria-hidden="true">
<div id="accounting-account-selector-{{ debit_credit }}-modal" class="modal fade accounting-account-selector" data-debit-credit="{{ debit_credit }}" tabindex="-1" aria-labelledby="accounting-account-selector-{{ debit_credit }}-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-account-selector-{{ side }}-modal-label">{{ A_("Select Account") }}</h1>
<h1 class="modal-title fs-5" id="accounting-account-selector-{{ debit_credit }}-modal-label">{{ A_("Select Account") }}</h1>
<button type="button" class="btn-close" data-bs-toggle="modal" data-bs-target="#accounting-line-item-editor-modal" aria-label="{{ A_("Close") }}"></button>
</div>
<div class="modal-body">
<div class="input-group mb-2">
<input id="accounting-account-selector-{{ side }}-query" class="form-control form-control-sm" type="search" placeholder=" " required="required">
<label class="input-group-text" for="accounting-account-selector-{{ side }}-query">
<input id="accounting-account-selector-{{ debit_credit }}-query" class="form-control form-control-sm" type="search" placeholder=" " required="required">
<label class="input-group-text" for="accounting-account-selector-{{ debit_credit }}-query">
<i class="fa-solid fa-magnifying-glass"></i>
{{ A_("Search") }}
</label>
</div>
<ul id="accounting-account-selector-{{ side }}-option-list" class="list-group accounting-selector-list">
<ul id="accounting-account-selector-{{ debit_credit }}-option-list" class="list-group accounting-selector-list">
{% for account in account_options %}
<li id="accounting-account-selector-{{ side }}-option-{{ account.code }}" class="list-group-item accounting-clickable accounting-account-selector-{{ side }}-option {% if account.is_in_use %} accounting-account-in-use {% endif %} {% if account.is_need_offset %} accounting-account-is-need-offset {% endif %}" data-code="{{ account.code }}" data-content="{{ account }}" data-query-values="{{ account.query_values|tojson|forceescape }}" data-bs-toggle="modal" data-bs-target="#accounting-line-item-editor-modal">
<li id="accounting-account-selector-{{ debit_credit }}-option-{{ account.code }}" class="list-group-item accounting-clickable accounting-account-selector-{{ debit_credit }}-option {% if account.is_in_use %} accounting-account-in-use {% endif %} {% if account.is_need_offset %} accounting-account-is-need-offset {% endif %}" data-code="{{ account.code }}" data-content="{{ account }}" data-query-values="{{ account.query_values|tojson|forceescape }}" data-bs-toggle="modal" data-bs-target="#accounting-line-item-editor-modal">
{{ account }}
</li>
{% endfor %}
<li id="accounting-account-selector-{{ side }}-more" class="list-group-item accounting-clickable">{{ A_("More…") }}</li>
<li id="accounting-account-selector-{{ debit_credit }}-more" class="list-group-item accounting-clickable">{{ A_("More…") }}</li>
</ul>
<p id="accounting-account-selector-{{ side }}-option-no-result" class="d-none">{{ A_("There is no data.") }}</p>
<p id="accounting-account-selector-{{ debit_credit }}-option-no-result" class="d-none">{{ A_("There is no data.") }}</p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-toggle="modal" data-bs-target="#accounting-line-item-editor-modal">{{ A_("Cancel") }}</button>
<button id="accounting-account-selector-{{ side }}-btn-clear" type="button" class="btn btn-danger" data-bs-toggle="modal" data-bs-target="#accounting-line-item-editor-modal">{{ A_("Clear") }}</button>
<button id="accounting-account-selector-{{ debit_credit }}-btn-clear" type="button" class="btn btn-danger" data-bs-toggle="modal" data-bs-target="#accounting-line-item-editor-modal">{{ A_("Clear") }}</button>
</div>
</div>
</div>

View File

@ -19,20 +19,20 @@ description-editor-modal.html: The modal of the description editor
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">
<form id="accounting-description-editor-{{ description_editor.debit_credit }}" class="accounting-description-editor" data-debit-credit="{{ description_editor.debit_credit }}">
<div id="accounting-description-editor-{{ description_editor.debit_credit }}-modal" class="modal fade" tabindex="-1" aria-labelledby="accounting-description-editor-{{ description_editor.debit_credit }}-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 class="modal-title fs-5" id="accounting-description-editor-{{ description_editor.debit_credit }}-modal-label">
<label for="accounting-description-editor-{{ description_editor.debit_credit }}-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">
<input id="accounting-description-editor-{{ description_editor.debit_credit }}-description" class="form-control" type="text" aria-labelledby="accounting-description-editor-{{ description_editor.debit_credit }}-modal-label">
<button id="accounting-description-editor-{{ description_editor.debit_credit }}-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>
@ -40,43 +40,43 @@ First written: 2023/2/28
{# 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">
<span id="accounting-description-editor-{{ description_editor.debit_credit }}-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">
<span id="accounting-description-editor-{{ description_editor.debit_credit }}-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">
<span id="accounting-description-editor-{{ description_editor.debit_credit }}-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">
<span id="accounting-description-editor-{{ description_editor.debit_credit }}-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">
<span id="accounting-description-editor-{{ description_editor.debit_credit }}-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 id="accounting-description-editor-{{ description_editor.debit_credit }}-general-page" aria-current="page" aria-labelledby="accounting-description-editor-{{ description_editor.debit_credit }}-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>
<input id="accounting-description-editor-{{ description_editor.debit_credit }}-general-tag" class="form-control" type="text" value="" placeholder=" ">
<label class="form-label" for="accounting-description-editor-{{ description_editor.debit_credit }}-general-tag">{{ A_("Tag") }}</label>
<div id="accounting-description-editor-{{ description_editor.debit_credit }}-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 }}">
<button class="btn btn-outline-primary accounting-description-editor-{{ description_editor.debit_credit }}-general-btn-tag" type="button" tabindex="-1" data-value="{{ tag.name }}" data-accounts="{{ tag.account_codes|tojson|forceescape }}">
{{ tag }}
</button>
{% endfor %}
@ -84,16 +84,16 @@ First written: 2023/2/28
</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 id="accounting-description-editor-{{ description_editor.debit_credit }}-travel-page" class="d-none" aria-current="false" aria-labelledby="accounting-description-editor-{{ description_editor.debit_credit }}-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>
<input id="accounting-description-editor-{{ description_editor.debit_credit }}-travel-tag" class="form-control" type="text" value="" placeholder=" ">
<label class="form-label" for="accounting-description-editor-{{ description_editor.debit_credit }}-travel-tag">{{ A_("Tag") }}</label>
<div id="accounting-description-editor-{{ description_editor.debit_credit }}-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 }}">
<button class="btn btn-outline-primary accounting-description-editor-{{ description_editor.debit_credit }}-travel-btn-tag" type="button" tabindex="-1" data-value="{{ tag.name }}" data-accounts="{{ tag.account_codes|tojson|forceescape }}">
{{ tag }}
</button>
{% endfor %}
@ -101,40 +101,40 @@ First written: 2023/2/28
<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>
<input id="accounting-description-editor-{{ description_editor.debit_credit }}-travel-from" class="form-control" type="text" value="" placeholder=" ">
<label class="form-label" for="accounting-description-editor-{{ description_editor.debit_credit }}-travel-from">{{ A_("From") }}</label>
<div id="accounting-description-editor-{{ description_editor.debit_credit }}-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>
<button class="btn btn-primary accounting-description-editor-{{ description_editor.debit_credit }}-travel-direction accounting-default" type="button" tabindex="-1" data-arrow="&rarr;">&rarr;</button>
<button class="btn btn-outline-primary accounting-description-editor-{{ description_editor.debit_credit }}-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>
<input id="accounting-description-editor-{{ description_editor.debit_credit }}-travel-to" class="form-control" type="text" value="" placeholder=" ">
<label class="form-label" for="accounting-description-editor-{{ description_editor.debit_credit }}-travel-to">{{ A_("To") }}</label>
<div id="accounting-description-editor-{{ description_editor.debit_credit }}-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 id="accounting-description-editor-{{ description_editor.debit_credit }}-bus-page" class="d-none" aria-current="false" aria-labelledby="accounting-description-editor-{{ description_editor.debit_credit }}-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>
<input id="accounting-description-editor-{{ description_editor.debit_credit }}-bus-tag" class="form-control" type="text" value="" placeholder=" ">
<label class="form-label" for="accounting-description-editor-{{ description_editor.debit_credit }}-bus-tag">{{ A_("Tag") }}</label>
<div id="accounting-description-editor-{{ description_editor.debit_credit }}-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>
<input id="accounting-description-editor-{{ description_editor.debit_credit }}-bus-route" class="form-control" type="text" value="" placeholder=" ">
<label class="form-label" for="accounting-description-editor-{{ description_editor.debit_credit }}-bus-route">{{ A_("Route") }}</label>
<div id="accounting-description-editor-{{ description_editor.debit_credit }}-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 }}">
<button class="btn btn-outline-primary accounting-description-editor-{{ description_editor.debit_credit }}-bus-btn-tag" type="button" tabindex="-1" data-value="{{ tag.name }}" data-accounts="{{ tag.account_codes|tojson|forceescape }}">
{{ tag }}
</button>
{% endfor %}
@ -142,42 +142,42 @@ First written: 2023/2/28
<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>
<input id="accounting-description-editor-{{ description_editor.debit_credit }}-bus-from" class="form-control" type="text" value="" placeholder=" ">
<label class="form-label" for="accounting-description-editor-{{ description_editor.debit_credit }}-bus-from">{{ A_("From") }}</label>
<div id="accounting-description-editor-{{ description_editor.debit_credit }}-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>
<input id="accounting-description-editor-{{ description_editor.debit_credit }}-bus-to" class="form-control" type="text" value="" placeholder=" ">
<label class="form-label" for="accounting-description-editor-{{ description_editor.debit_credit }}-bus-to">{{ A_("To") }}</label>
<div id="accounting-description-editor-{{ description_editor.debit_credit }}-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">
<div id="accounting-description-editor-{{ description_editor.debit_credit }}-regular-page" class="d-none" aria-current="false" aria-labelledby="accounting-description-editor-{{ description_editor.debit_credit }}-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 id="accounting-description-editor-{{ description_editor.debit_credit }}-annotation-page" class="d-none" aria-current="false" aria-labelledby="accounting-description-editor-{{ description_editor.debit_credit }}-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>
<input id="accounting-description-editor-{{ description_editor.debit_credit }}-annotation-number" class="form-control" type="number" min="1" value="" placeholder=" ">
<label class="form-label" for="accounting-description-editor-{{ description_editor.debit_credit }}-annotation-number">{{ A_("The number of items") }}</label>
<div id="accounting-description-editor-{{ description_editor.debit_credit }}-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>
<input id="accounting-description-editor-{{ description_editor.debit_credit }}-annotation-note" class="form-control" type="text" value="" placeholder=" ">
<label class="form-label" for="accounting-description-editor-{{ description_editor.debit_credit }}-annotation-note">{{ A_("Note") }}</label>
<div id="accounting-description-editor-{{ description_editor.debit_credit }}-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 }}">
<button class="btn btn-outline-primary d-none accounting-description-editor-{{ description_editor.debit_credit }}-account {% if account.is_need_offset %} accounting-account-is-need-offset {% endif %}" type="button" data-code="{{ account.code }}" data-text="{{ account }}">
{{ account }}
</button>
{% endfor %}
@ -185,7 +185,7 @@ First written: 2023/2/28
</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>
<button id="accounting-description-editor-{{ description_editor.debit_credit }}-btn-save" type="submit" class="btn btn-primary">{{ A_("Save") }}</button>
</div>
</div>
</div>

View File

@ -20,24 +20,24 @@ Author: imacat@mail.imacat.idv.tw (imacat)
First written: 2023/2/25
#}
{# <ul> For SonarQube not to complain about incorrect HTML #}
<li id="accounting-currency-{{ currency_index }}-{{ side }}-{{ line_item_index }}" class="list-group-item list-group-item-action d-flex justify-content-between accounting-currency-{{ currency_index }}-{{ side }} {% if form.offsets %} accounting-matched-line-item {% endif %}" data-currency-index="{{ currency_index }}" data-side="{{ side }}" data-line-item-index="{{ line_item_index }}" {% if form.is_need_offset %} data-is-need-offset="true" {% endif %}>
<li id="accounting-currency-{{ currency_index }}-{{ debit_credit }}-{{ line_item_index }}" class="list-group-item list-group-item-action d-flex justify-content-between accounting-currency-{{ currency_index }}-{{ debit_credit }} {% if form.offsets %} accounting-matched-line-item {% endif %}" data-currency-index="{{ currency_index }}" data-debit-credit="{{ debit_credit }}" data-line-item-index="{{ line_item_index }}" {% if form.is_need_offset %} data-is-need-offset="true" {% endif %}>
{% if form.eid.data %}
<input type="hidden" name="currency-{{ currency_index }}-{{ side }}-{{ line_item_index }}-eid" value="{{ form.eid.data }}">
<input type="hidden" name="currency-{{ currency_index }}-{{ debit_credit }}-{{ line_item_index }}-eid" value="{{ form.eid.data }}">
{% endif %}
<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="{{ form.original_line_item_id.data|accounting_default }}" data-date="{{ form.original_line_item_date|accounting_default }}" data-text="{{ form.original_line_item_text|accounting_default }}">
<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="{{ form.account_code.data|accounting_default }}" data-text="{{ form.account_text }}">
<input id="accounting-currency-{{ currency_index }}-{{ side }}-{{ line_item_index }}-description" type="hidden" name="currency-{{ currency_index }}-{{ side }}-{{ line_item_index }}-description" value="{{ form.description.data|accounting_default }}">
<input id="accounting-currency-{{ currency_index }}-{{ side }}-{{ line_item_index }}-amount" type="hidden" name="currency-{{ currency_index }}-{{ side }}-{{ line_item_index }}-amount" value="{{ form.amount.data|accounting_voucher_format_amount_input }}" data-min="{{ form.offset_total|accounting_default("0") }}">
<input id="accounting-currency-{{ currency_index }}-{{ debit_credit }}-{{ line_item_index }}-no" type="hidden" name="currency-{{ currency_index }}-{{ debit_credit }}-{{ line_item_index }}-no" value="{{ line_item_index }}">
<input id="accounting-currency-{{ currency_index }}-{{ debit_credit }}-{{ line_item_index }}-original-line-item-id" class="accounting-original-line-item-id" type="hidden" name="currency-{{ currency_index }}-{{ debit_credit }}-{{ line_item_index }}-original_line_item_id" value="{{ form.original_line_item_id.data|accounting_default }}" data-date="{{ form.original_line_item_date|accounting_default }}" data-text="{{ form.original_line_item_text|accounting_default }}">
<input id="accounting-currency-{{ currency_index }}-{{ debit_credit }}-{{ line_item_index }}-account-code" type="hidden" name="currency-{{ currency_index }}-{{ debit_credit }}-{{ line_item_index }}-account_code" value="{{ form.account_code.data|accounting_default }}" data-text="{{ form.account_text }}">
<input id="accounting-currency-{{ currency_index }}-{{ debit_credit }}-{{ line_item_index }}-description" type="hidden" name="currency-{{ currency_index }}-{{ debit_credit }}-{{ line_item_index }}-description" value="{{ form.description.data|accounting_default }}">
<input id="accounting-currency-{{ currency_index }}-{{ debit_credit }}-{{ line_item_index }}-amount" type="hidden" name="currency-{{ currency_index }}-{{ debit_credit }}-{{ line_item_index }}-amount" value="{{ form.amount.data|accounting_voucher_format_amount_input }}" data-min="{{ form.offset_total|accounting_default("0") }}">
<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 form.all_errors %} is-invalid {% endif %}" data-bs-toggle="modal" data-bs-target="#accounting-line-item-editor-modal">
<div id="accounting-currency-{{ currency_index }}-{{ debit_credit }}-{{ line_item_index }}-control" class="form-control clickable d-flex justify-content-between {% if form.all_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">{{ form.account_text }}</div>
<div id="accounting-currency-{{ currency_index }}-{{ side }}-{{ line_item_index }}-description-text">{{ form.description.data|accounting_default }}</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 form.original_line_item_id.data %} d-none {% endif %}">
<div id="accounting-currency-{{ currency_index }}-{{ debit_credit }}-{{ line_item_index }}-account-text" class="small">{{ form.account_text }}</div>
<div id="accounting-currency-{{ currency_index }}-{{ debit_credit }}-{{ line_item_index }}-description-text">{{ form.description.data|accounting_default }}</div>
<div id="accounting-currency-{{ currency_index }}-{{ debit_credit }}-{{ line_item_index }}-original-line-item-text" class="fst-italic small accounting-original-line-item {% if not form.original_line_item_id.data %} d-none {% endif %}">
{% if form.original_line_item_id.data %}{{ A_("Offset %(item)s", item=form.original_line_item_text|accounting_default) }}{% endif %}
</div>
<div id="accounting-currency-{{ currency_index }}-{{ side }}-{{ line_item_index }}-offsets" class="fst-italic small accounting-offset-line-items {% if not form.is_need_offset %} d-none {% endif %}">
<div id="accounting-currency-{{ currency_index }}-{{ debit_credit }}-{{ line_item_index }}-offsets" class="fst-italic small accounting-offset-line-items {% if not form.is_need_offset %} d-none {% endif %}">
{% if form.offsets %}
<div class="d-flex justify-content-between {% if not form.offsets %} d-none {% endif %}">
<div>{{ A_("Offsets") }}</div>
@ -60,13 +60,13 @@ First written: 2023/2/25
{% endif %}
</div>
</div>
<div><span id="accounting-currency-{{ currency_index }}-{{ side }}-{{ line_item_index }}-amount-text" class="badge rounded-pill bg-primary">{{ form.amount.data|accounting_format_amount }}</span></div>
<div><span id="accounting-currency-{{ currency_index }}-{{ debit_credit }}-{{ line_item_index }}-amount-text" class="badge rounded-pill bg-primary">{{ form.amount.data|accounting_format_amount }}</span></div>
</div>
<div id="accounting-currency-{{ currency_index }}-{{ side }}-{{ line_item_index }}-error" class="invalid-feedback">{% if form.all_errors %}{{ form.all_errors[0] }}{% endif %}</div>
<div id="accounting-currency-{{ currency_index }}-{{ debit_credit }}-{{ line_item_index }}-error" class="invalid-feedback">{% if form.all_errors %}{{ form.all_errors[0] }}{% endif %}</div>
</div>
<div>
<button id="accounting-currency-{{ currency_index }}-{{ side }}-{{ line_item_index }}-delete" class="btn btn-danger rounded-circle {% if only_one_form or form.offsets %} d-none {% endif %}" type="button" data-target="accounting-currency-{{ currency_index }}-{{ side }}-{{ line_item_index }}">
<button id="accounting-currency-{{ currency_index }}-{{ debit_credit }}-{{ line_item_index }}-delete" class="btn btn-danger rounded-circle {% if only_one_form or form.offsets %} d-none {% endif %}" type="button" data-target="accounting-currency-{{ currency_index }}-{{ debit_credit }}-{{ line_item_index }}">
<i class="fas fa-minus"></i>
</button>
</div>

View File

@ -37,7 +37,7 @@ 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-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">
<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-debit-credit="{{ "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">

View File

@ -47,7 +47,7 @@ First written: 2023/2/25
<ul id="accounting-currency-{{ currency_index }}-credit-list" class="list-group accounting-line-item-list">
{% for line_item_form in credit_forms %}
{% with currency_index = currency_index,
side = "credit",
debit_credit = "credit",
line_item_index = loop.index,
only_one_line_item_form = credit_forms|length == 1,
form = line_item_form.form %}
@ -62,7 +62,7 @@ First written: 2023/2/25
</div>
<div>
<button id="accounting-currency-{{ currency_index }}-credit-add-line-item" class="btn btn-primary" type="button" data-currency-index="{{ currency_index }}" data-side="credit" data-bs-toggle="modal" data-bs-target="#accounting-line-item-editor-modal">
<button id="accounting-currency-{{ currency_index }}-credit-add-line-item" class="btn btn-primary" type="button" data-currency-index="{{ currency_index }}" data-debit-credit="credit" data-bs-toggle="modal" data-bs-target="#accounting-line-item-editor-modal">
<i class="fas fa-plus"></i>
{{ A_("New") }}
</button>

View File

@ -50,7 +50,7 @@ First written: 2023/2/25
{% with description_editor = form.description_editor.credit %}
{% include "accounting/voucher/include/description-editor-modal.html" %}
{% endwith %}
{% with side = "credit",
{% with debit_credit = "credit",
account_options = form.credit_account_options %}
{% include "accounting/voucher/include/account-selector-modal.html" %}
{% endwith %}

View File

@ -49,7 +49,7 @@ First written: 2023/2/25
<ul id="accounting-currency-{{ currency_index }}-debit-list" class="list-group accounting-line-item-list">
{% for line_item_form in debit_forms %}
{% with currency_index = currency_index,
side = "debit",
debit_credit = "debit",
line_item_index = loop.index,
only_one_line_item_form = debit_forms|length == 1,
form = line_item_form.form %}
@ -64,7 +64,7 @@ First written: 2023/2/25
</div>
<div>
<button id="accounting-currency-{{ currency_index }}-debit-add-line-item" class="btn btn-primary" type="button" data-currency-index="{{ currency_index }}" data-side="debit" data-bs-toggle="modal" data-bs-target="#accounting-line-item-editor-modal">
<button id="accounting-currency-{{ currency_index }}-debit-add-line-item" class="btn btn-primary" type="button" data-currency-index="{{ currency_index }}" data-debit-credit="debit" data-bs-toggle="modal" data-bs-target="#accounting-line-item-editor-modal">
<i class="fas fa-plus"></i>
{{ A_("New") }}
</button>
@ -80,7 +80,7 @@ First written: 2023/2/25
<ul id="accounting-currency-{{ currency_index }}-credit-list" class="list-group accounting-line-item-list">
{% for line_item_form in credit_forms %}
{% with currency_index = currency_index,
side = "credit",
debit_credit = "credit",
line_item_index = loop.index,
only_one_line_item_form = credit_forms|length == 1,
form = line_item_form.form %}
@ -95,7 +95,7 @@ First written: 2023/2/25
</div>
<div>
<button id="accounting-currency-{{ currency_index }}-credit-add-line-item" class="btn btn-primary" type="button" data-currency-index="{{ currency_index }}" data-side="credit" data-bs-toggle="modal" data-bs-target="#accounting-line-item-editor-modal">
<button id="accounting-currency-{{ currency_index }}-credit-add-line-item" class="btn btn-primary" type="button" data-currency-index="{{ currency_index }}" data-debit-credit="credit" data-bs-toggle="modal" data-bs-target="#accounting-line-item-editor-modal">
<i class="fas fa-plus"></i>
{{ A_("New") }}
</button>

View File

@ -57,11 +57,11 @@ First written: 2023/2/25
{% with description_editor = form.description_editor.credit %}
{% include "accounting/voucher/include/description-editor-modal.html" %}
{% endwith %}
{% with side = "debit",
{% with debit_credit = "debit",
account_options = form.debit_account_options %}
{% include "accounting/voucher/include/account-selector-modal.html" %}
{% endwith %}
{% with side = "credit",
{% with debit_credit = "credit",
account_options = form.credit_account_options %}
{% include "accounting/voucher/include/account-selector-modal.html" %}
{% endwith %}

View File

@ -53,9 +53,9 @@ class OriginalLineItemExists:
"The original line item does not exist."))
class OriginalLineItemOppositeSide:
class OriginalLineItemOppositeDebitCredit:
"""The validator to check if the original line item is on the opposite
side."""
debit or credit."""
def __call__(self, form: FlaskForm, field: IntegerField) -> None:
if field.data is None:
@ -71,7 +71,7 @@ class OriginalLineItemOppositeSide:
and not original_line_item.is_debit:
return
raise ValidationError(lazy_gettext(
"The original line item is on the same side."))
"The original line item is on the same debit or credit."))
class OriginalLineItemNeedOffset:
@ -180,7 +180,7 @@ class KeepAccountWhenHavingOffset:
class NotStartPayableFromDebit:
"""The validator to check that a payable line item does not start from
the debit side."""
debit."""
def __call__(self, form: FlaskForm, field: StringField) -> None:
assert isinstance(form, DebitLineItemForm)
@ -191,12 +191,12 @@ class NotStartPayableFromDebit:
account: Account | None = Account.find_by_code(field.data)
if account is not None and account.is_need_offset:
raise ValidationError(lazy_gettext(
"A payable line item cannot start from the debit side."))
"A payable line item cannot start from debit."))
class NotStartReceivableFromCredit:
"""The validator to check that a receivable line item does not start
from the credit side."""
from credit."""
def __call__(self, form: FlaskForm, field: StringField) -> None:
assert isinstance(form, CreditLineItemForm)
@ -207,7 +207,7 @@ class NotStartReceivableFromCredit:
account: Account | None = Account.find_by_code(field.data)
if account is not None and account.is_need_offset:
raise ValidationError(lazy_gettext(
"A receivable line item cannot start from the credit side."))
"A receivable line item cannot start from credit."))
class PositiveAmount:
@ -439,7 +439,7 @@ class DebitLineItemForm(LineItemForm):
original_line_item_id = IntegerField(
validators=[Optional(),
OriginalLineItemExists(),
OriginalLineItemOppositeSide(),
OriginalLineItemOppositeDebitCredit(),
OriginalLineItemNeedOffset(),
OriginalLineItemNotOffset()])
"""The ID of the original line item."""
@ -489,7 +489,7 @@ class CreditLineItemForm(LineItemForm):
original_line_item_id = IntegerField(
validators=[Optional(),
OriginalLineItemExists(),
OriginalLineItemOppositeSide(),
OriginalLineItemOppositeDebitCredit(),
OriginalLineItemNeedOffset(),
OriginalLineItemNotOffset()])
"""The ID of the original line item."""

View File

@ -376,7 +376,7 @@ class LineItemCollector(t.Generic[T], ABC):
def _make_cash_line_item(self, forms: list[LineItemForm], is_debit: bool,
currency_code: str, no: int) -> None:
"""Composes the cash line item at the other side of the cash
"""Composes the cash line item at the other debit or credit of the cash
voucher.
:param forms: The line item forms in the same currency.

View File

@ -143,16 +143,16 @@ class DescriptionType:
return sorted(self.__tag_dict.values(), key=lambda x: -x.freq)
class DescriptionSide:
"""A description side"""
class DescriptionDebitCredit:
"""The description on debit or credit."""
def __init__(self, side_id: t.Literal["debit", "credit"]):
"""Constructs a description side.
def __init__(self, debit_credit: t.Literal["debit", "credit"]):
"""Constructs the description on debit or credit.
:param side_id: The side ID, either "debit" or "credit".
:param debit_credit: Either "debit" or "credit".
"""
self.side: t.Literal["debit", "credit"] = side_id
"""The side."""
self.debit_credit: t.Literal["debit", "credit"] = debit_credit
"""Either debit or credit."""
self.general: DescriptionType = DescriptionType("general")
"""The general tags."""
self.travel: DescriptionType = DescriptionType("travel")
@ -179,7 +179,7 @@ class DescriptionSide:
@property
def accounts(self) -> list[DescriptionAccount]:
"""Returns the suggested accounts of all tags in the description editor
in the side, in their frequency order.
in debit or credit, in their frequency order.
:return: The suggested accounts of all tags, in their frequency order.
"""
@ -202,12 +202,12 @@ class DescriptionEditor:
def __init__(self):
"""Constructs the description editor."""
self.debit: DescriptionSide = DescriptionSide("debit")
self.debit: DescriptionDebitCredit = DescriptionDebitCredit("debit")
"""The debit tags."""
self.credit: DescriptionSide = DescriptionSide("credit")
self.credit: DescriptionDebitCredit = DescriptionDebitCredit("credit")
"""The credit tags."""
side: sa.Label = sa.case((VoucherLineItem.is_debit, "debit"),
else_="credit").label("side")
debit_credit: sa.Label = sa.case((VoucherLineItem.is_debit, "debit"),
else_="credit").label("debit_credit")
tag_type: sa.Label = sa.case(
(VoucherLineItem.description.like("_%—_%—_%→_%"), "bus"),
(sa.or_(VoucherLineItem.description.like("_%—_%→_%"),
@ -215,21 +215,22 @@ class DescriptionEditor:
else_="general").label("tag_type")
tag: sa.Label = get_prefix(VoucherLineItem.description, "")\
.label("tag")
select: sa.Select = sa.Select(side, tag_type, tag,
select: sa.Select = sa.Select(debit_credit, tag_type, tag,
VoucherLineItem.account_id,
sa.func.count().label("freq"))\
.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)
.group_by(debit_credit, 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"], DescriptionSide] \
= {x.side: x for x in {self.debit, self.credit}}
debit_credit_dict: dict[t.Literal["debit", "credit"],
DescriptionDebitCredit] \
= {x.debit_credit: x for x in {self.debit, self.credit}}
for row in result:
side_dict[row.side].add_tag(
debit_credit_dict[row.debit_credit].add_tag(
row.tag_type, row.tag, accounts[row.account_id], row.freq)

View File

@ -87,7 +87,7 @@ class VoucherOperator(ABC):
return render_template(
"accounting/voucher/include/form-line-item.html",
currency_index="CURRENCY_INDEX",
side="SIDE",
debit_credit="DEBIT_CREDIT",
line_item_index="LINE_ITEM_INDEX",
form=LineItemForm())

View File

@ -101,7 +101,7 @@ class OffsetTestCase(unittest.TestCase):
self.assertEqual(response.status_code, 302)
self.assertEqual(response.headers["Location"], create_uri)
# The same side
# The same debit or credit
form = voucher_data.new_form(self.csrf_token)
form["currency-1-credit-1-original_line_item_id"] \
= self.data.e_p_or1c.id
@ -210,7 +210,7 @@ class OffsetTestCase(unittest.TestCase):
self.assertEqual(response.status_code, 302)
self.assertEqual(response.headers["Location"], edit_uri)
# The same side
# The same debit or credit
form = voucher_data.update_form(self.csrf_token)
form["currency-1-credit-1-original_line_item_id"] \
= self.data.e_p_or1c.id
@ -417,7 +417,7 @@ class OffsetTestCase(unittest.TestCase):
self.assertEqual(response.status_code, 302)
self.assertEqual(response.headers["Location"], create_uri)
# The same side
# The same debit or credit
form = voucher_data.new_form(self.csrf_token)
form["currency-1-debit-1-original_line_item_id"] \
= self.data.e_r_or1d.id
@ -524,7 +524,7 @@ class OffsetTestCase(unittest.TestCase):
self.assertEqual(response.status_code, 302)
self.assertEqual(response.headers["Location"], edit_uri)
# The same side
# The same debit or credit
form = voucher_data.update_form(self.csrf_token)
form["currency-1-debit-1-original_line_item_id"] \
= self.data.e_r_or1d.id

View File

@ -231,7 +231,7 @@ class CashReceiptVoucherTestCase(unittest.TestCase):
self.assertEqual(response.status_code, 302)
self.assertEqual(response.headers["Location"], create_uri)
# A receivable line item cannot start from the credit side
# A receivable line item cannot start from credit
form = self.__get_add_form()
key: str = [x for x in form.keys()
if x.endswith("-account_code") and "-credit-" in x][0]
@ -391,7 +391,7 @@ class CashReceiptVoucherTestCase(unittest.TestCase):
self.assertEqual(response.status_code, 302)
self.assertEqual(response.headers["Location"], edit_uri)
# A receivable line item cannot start from the credit side
# A receivable line item cannot start from credit
form = self.__get_add_form()
key: str = [x for x in form.keys()
if x.endswith("-account_code") and "-credit-" in x][0]
@ -803,7 +803,7 @@ class CashDisbursementVoucherTestCase(unittest.TestCase):
self.assertEqual(response.status_code, 302)
self.assertEqual(response.headers["Location"], create_uri)
# A payable line item cannot start from the debit side
# A payable line item cannot start from debit
form = self.__get_add_form()
key: str = [x for x in form.keys()
if x.endswith("-account_code") and "-debit-" in x][0]
@ -966,7 +966,7 @@ class CashDisbursementVoucherTestCase(unittest.TestCase):
self.assertEqual(response.status_code, 302)
self.assertEqual(response.headers["Location"], edit_uri)
# A payable line item cannot start from the debit side
# A payable line item cannot start from debit
form = self.__get_add_form()
key: str = [x for x in form.keys()
if x.endswith("-account_code") and "-debit-" in x][0]
@ -1398,7 +1398,7 @@ class TransferVoucherTestCase(unittest.TestCase):
self.assertEqual(response.status_code, 302)
self.assertEqual(response.headers["Location"], create_uri)
# A receivable line item cannot start from the credit side
# A receivable line item cannot start from credit
form = self.__get_add_form()
key: str = [x for x in form.keys()
if x.endswith("-account_code") and "-credit-" in x][0]
@ -1407,7 +1407,7 @@ class TransferVoucherTestCase(unittest.TestCase):
self.assertEqual(response.status_code, 302)
self.assertEqual(response.headers["Location"], create_uri)
# A payable line item cannot start from the debit side
# A payable line item cannot start from debit
form = self.__get_add_form()
key: str = [x for x in form.keys()
if x.endswith("-account_code") and "-debit-" in x][0]
@ -1597,7 +1597,7 @@ class TransferVoucherTestCase(unittest.TestCase):
self.assertEqual(response.status_code, 302)
self.assertEqual(response.headers["Location"], edit_uri)
# A receivable line item cannot start from the credit side
# A receivable line item cannot start from credit
form = self.__get_add_form()
key: str = [x for x in form.keys()
if x.endswith("-account_code") and "-credit-" in x][0]
@ -1606,7 +1606,7 @@ class TransferVoucherTestCase(unittest.TestCase):
self.assertEqual(response.status_code, 302)
self.assertEqual(response.headers["Location"], edit_uri)
# A payable line item cannot start from the debit side
# A payable line item cannot start from debit
form = self.__get_add_form()
key: str = [x for x in form.keys()
if x.endswith("-account_code") and "-debit-" in x][0]

View File

@ -50,17 +50,17 @@ class VoucherLineItemData:
self.description: str = description
self.amount: Decimal = Decimal(amount)
def form(self, prefix: str, side: str, index: int, is_update: bool) \
-> dict[str, str]:
def form(self, prefix: str, debit_credit: str, index: int,
is_update: bool) -> dict[str, str]:
"""Returns the line item as form data.
:param prefix: The prefix of the form fields.
:param side: The side, either "debit" or "credit".
:param debit_credit: Either "debit" or "credit".
:param index: The line item index.
:param is_update: True for an update operation, or False otherwise
:return: The form data.
"""
prefix = f"{prefix}-{side}-{index}"
prefix = f"{prefix}-{debit_credit}-{index}"
form: dict[str, str] = {f"{prefix}-account_code": self.account,
f"{prefix}-description": self.description,
f"{prefix}-amount": str(self.amount)}