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

View File

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

View File

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

View File

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

View File

@ -47,10 +47,10 @@ class VoucherLineItemEditor {
#modal; #modal;
/** /**
* The side, either "debit" or "credit" * Either "debit" or "credit"
* @type {string} * @type {string}
*/ */
side; debitCredit;
/** /**
* The prefix of the HTML ID and class * The prefix of the HTML ID and class
@ -143,10 +143,10 @@ class VoucherLineItemEditor {
lineItem; lineItem;
/** /**
* The debit or credit side sub-form * The debit or credit sub-form
* @type {SideSubForm} * @type {DebitCreditSubForm}
*/ */
#sideSubForm; #debitCreditSubForm;
/** /**
* Whether the voucher line item needs offset * Whether the voucher line item needs offset
@ -241,13 +241,13 @@ class VoucherLineItemEditor {
this.originalLineItemSelector = new OriginalLineItemSelector(this); this.originalLineItemSelector = new OriginalLineItemSelector(this);
this.#originalLineItemControl.onclick = () => this.originalLineItemSelector.onOpen() this.#originalLineItemControl.onclick = () => this.originalLineItemSelector.onOpen()
this.#originalLineItemDelete.onclick = () => this.clearOriginalLineItem(); this.#originalLineItemDelete.onclick = () => this.clearOriginalLineItem();
this.#descriptionControl.onclick = () => this.#descriptionEditors[this.side].onOpen(); this.#descriptionControl.onclick = () => this.#descriptionEditors[this.debitCredit].onOpen();
this.#accountControl.onclick = () => this.#accountSelectors[this.side].onOpen(); this.#accountControl.onclick = () => this.#accountSelectors[this.debitCredit].onOpen();
this.#amountInput.onchange = () => this.#validateAmount(); this.#amountInput.onchange = () => this.#validateAmount();
this.#element.onsubmit = () => { this.#element.onsubmit = () => {
if (this.#validate()) { if (this.#validate()) {
if (this.lineItem === null) { if (this.lineItem === null) {
this.lineItem = this.#sideSubForm.addLineItem(); this.lineItem = this.#debitCreditSubForm.addLineItem();
} }
this.amount = this.#amountInput.value; this.amount = this.#amountInput.value;
this.lineItem.save(this); this.lineItem.save(this);
@ -314,7 +314,7 @@ class VoucherLineItemEditor {
* @return {string} the currency code * @return {string} the currency code
*/ */
getCurrencyCode() { 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. * 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.lineItem = null;
this.#sideSubForm = side; this.#debitCreditSubForm = debitCredit;
this.side = this.#sideSubForm.side; this.debitCredit = this.#debitCreditSubForm.debitCredit;
this.isNeedOffset = false; this.isNeedOffset = false;
this.#originalLineItemContainer.classList.add("d-none"); this.#originalLineItemContainer.classList.add("d-none");
this.#originalLineItemControl.classList.remove("accounting-not-empty"); this.#originalLineItemControl.classList.remove("accounting-not-empty");
@ -518,8 +518,8 @@ class VoucherLineItemEditor {
*/ */
onEdit(lineItem) { onEdit(lineItem) {
this.lineItem = lineItem; this.lineItem = lineItem;
this.#sideSubForm = lineItem.sideSubForm; this.#debitCreditSubForm = lineItem.debitCreditSubForm;
this.side = this.#sideSubForm.side; this.debitCredit = this.#debitCreditSubForm.debitCredit;
this.isNeedOffset = lineItem.isNeedOffset(); this.isNeedOffset = lineItem.isNeedOffset();
this.originalLineItemId = lineItem.getOriginalLineItemId(); this.originalLineItemId = lineItem.getOriginalLineItemId();
this.originalLineItemDate = lineItem.getOriginalLineItemDate(); this.originalLineItemDate = lineItem.getOriginalLineItemDate();
@ -575,11 +575,11 @@ class VoucherLineItemEditor {
#setEnableDescriptionAccount(isEnabled) { #setEnableDescriptionAccount(isEnabled) {
if (isEnabled) { if (isEnabled) {
this.#descriptionControl.dataset.bsToggle = "modal"; 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.remove("accounting-disabled");
this.#descriptionControl.classList.add("accounting-clickable"); this.#descriptionControl.classList.add("accounting-clickable");
this.#accountControl.dataset.bsToggle = "modal"; 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.remove("accounting-disabled");
this.#accountControl.classList.add("accounting-clickable"); this.#accountControl.classList.add("accounting-clickable");
} else { } 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"> <ul id="accounting-currency-{{ currency_index }}-debit-list" class="list-group accounting-line-item-list">
{% for line_item_form in debit_forms %} {% for line_item_form in debit_forms %}
{% with currency_index = currency_index, {% with currency_index = currency_index,
side = "debit", debit_credit = "debit",
line_item_index = loop.index, line_item_index = loop.index,
only_one_line_item_form = debit_forms|length == 1, only_one_line_item_form = debit_forms|length == 1,
form = line_item_form.form %} form = line_item_form.form %}
@ -62,7 +62,7 @@ First written: 2023/2/25
</div> </div>
<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> <i class="fas fa-plus"></i>
{{ A_("New") }} {{ A_("New") }}
</button> </button>

View File

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

View File

@ -19,35 +19,35 @@ account-selector-modal.html: The modal for the account selector
Author: imacat@mail.imacat.idv.tw (imacat) Author: imacat@mail.imacat.idv.tw (imacat)
First written: 2023/2/25 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-dialog">
<div class="modal-content"> <div class="modal-content">
<div class="modal-header"> <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> <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>
<div class="modal-body"> <div class="modal-body">
<div class="input-group mb-2"> <div class="input-group mb-2">
<input id="accounting-account-selector-{{ side }}-query" class="form-control form-control-sm" type="search" placeholder=" " required="required"> <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-{{ side }}-query"> <label class="input-group-text" for="accounting-account-selector-{{ debit_credit }}-query">
<i class="fa-solid fa-magnifying-glass"></i> <i class="fa-solid fa-magnifying-glass"></i>
{{ A_("Search") }} {{ A_("Search") }}
</label> </label>
</div> </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 %} {% 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 }} {{ account }}
</li> </li>
{% endfor %} {% 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> </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>
<div class="modal-footer"> <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 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> </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) Author: imacat@mail.imacat.idv.tw (imacat)
First written: 2023/2/28 First written: 2023/2/28
#} #}
<form id="accounting-description-editor-{{ description_editor.side }}" class="accounting-description-editor" data-side="{{ description_editor.side }}"> <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.side }}-modal" class="modal fade" tabindex="-1" aria-labelledby="accounting-description-editor-{{ description_editor.side }}-modal-label" aria-hidden="true"> <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-dialog">
<div class="modal-content"> <div class="modal-content">
<div class="modal-header"> <div class="modal-header">
<h1 class="modal-title fs-5" id="accounting-description-editor-{{ description_editor.side }}-modal-label"> <h1 class="modal-title fs-5" id="accounting-description-editor-{{ description_editor.debit_credit }}-modal-label">
<label for="accounting-description-editor-{{ description_editor.side }}-description">{{ A_("Description") }}</label> <label for="accounting-description-editor-{{ description_editor.debit_credit }}-description">{{ A_("Description") }}</label>
</h1> </h1>
<button class="btn-close" type="button" data-bs-toggle="modal" data-bs-target="#accounting-line-item-editor-modal" aria-label="{{ A_("Close") }}"></button> <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>
<div class="modal-body"> <div class="modal-body">
<div class="d-flex justify-content-between mb-3"> <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"> <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.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"> <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...") }} {{ A_("Offset...") }}
</button> </button>
</div> </div>
@ -40,43 +40,43 @@ First written: 2023/2/28
{# Tab navigation #} {# Tab navigation #}
<ul class="nav nav-tabs mb-2"> <ul class="nav nav-tabs mb-2">
<li class="nav-item"> <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") }} {{ A_("General") }}
</span> </span>
</li> </li>
<li class="nav-item"> <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") }} {{ A_("Travel") }}
</span> </span>
</li> </li>
<li class="nav-item"> <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") }} {{ A_("Bus") }}
</span> </span>
</li> </li>
<li class="nav-item"> <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") }} {{ A_("Regular") }}
</span> </span>
</li> </li>
<li class="nav-item"> <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") }} {{ A_("Annotation") }}
</span> </span>
</li> </li>
</ul> </ul>
{# A general description with a tag #} {# 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"> <div class="form-floating mb-2">
<input id="accounting-description-editor-{{ description_editor.side }}-general-tag" class="form-control" type="text" value="" placeholder=" "> <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.side }}-general-tag">{{ A_("Tag") }}</label> <label class="form-label" for="accounting-description-editor-{{ description_editor.debit_credit }}-general-tag">{{ A_("Tag") }}</label>
<div id="accounting-description-editor-{{ description_editor.side }}-general-tag-error" class="invalid-feedback"></div> <div id="accounting-description-editor-{{ description_editor.debit_credit }}-general-tag-error" class="invalid-feedback"></div>
</div> </div>
<div> <div>
{% for tag in description_editor.general.tags %} {% 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 }} {{ tag }}
</button> </button>
{% endfor %} {% endfor %}
@ -84,16 +84,16 @@ First written: 2023/2/28
</div> </div>
{# A general trip with the origin and distination #} {# 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"> <div class="form-floating mb-2">
<input id="accounting-description-editor-{{ description_editor.side }}-travel-tag" class="form-control" type="text" value="" placeholder=" "> <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.side }}-travel-tag">{{ A_("Tag") }}</label> <label class="form-label" for="accounting-description-editor-{{ description_editor.debit_credit }}-travel-tag">{{ A_("Tag") }}</label>
<div id="accounting-description-editor-{{ description_editor.side }}-travel-tag-error" class="invalid-feedback"></div> <div id="accounting-description-editor-{{ description_editor.debit_credit }}-travel-tag-error" class="invalid-feedback"></div>
</div> </div>
<div> <div>
{% for tag in description_editor.travel.tags %} {% 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 }} {{ tag }}
</button> </button>
{% endfor %} {% endfor %}
@ -101,40 +101,40 @@ First written: 2023/2/28
<div class="d-flex justify-content-between mt-2"> <div class="d-flex justify-content-between mt-2">
<div class="form-floating"> <div class="form-floating">
<input id="accounting-description-editor-{{ description_editor.side }}-travel-from" class="form-control" type="text" value="" placeholder=" "> <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.side }}-travel-from">{{ A_("From") }}</label> <label class="form-label" for="accounting-description-editor-{{ description_editor.debit_credit }}-travel-from">{{ A_("From") }}</label>
<div id="accounting-description-editor-{{ description_editor.side }}-travel-from-error" class="invalid-feedback"></div> <div id="accounting-description-editor-{{ description_editor.debit_credit }}-travel-from-error" class="invalid-feedback"></div>
</div> </div>
<div class="btn-group-vertical ms-1 me-1"> <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-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.side }}-travel-direction" type="button" tabindex="-1" data-arrow="&harr;">&harr;</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>
<div class="form-floating"> <div class="form-floating">
<input id="accounting-description-editor-{{ description_editor.side }}-travel-to" class="form-control" type="text" value="" placeholder=" "> <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.side }}-travel-to">{{ A_("To") }}</label> <label class="form-label" for="accounting-description-editor-{{ description_editor.debit_credit }}-travel-to">{{ A_("To") }}</label>
<div id="accounting-description-editor-{{ description_editor.side }}-travel-to-error" class="invalid-feedback"></div> <div id="accounting-description-editor-{{ description_editor.debit_credit }}-travel-to-error" class="invalid-feedback"></div>
</div> </div>
</div> </div>
</div> </div>
{# A bus trip with the route name or route number, the origin and distination #} {# 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="d-flex justify-content-between mb-2">
<div class="form-floating me-2"> <div class="form-floating me-2">
<input id="accounting-description-editor-{{ description_editor.side }}-bus-tag" class="form-control" type="text" value="" placeholder=" "> <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.side }}-bus-tag">{{ A_("Tag") }}</label> <label class="form-label" for="accounting-description-editor-{{ description_editor.debit_credit }}-bus-tag">{{ A_("Tag") }}</label>
<div id="accounting-description-editor-{{ description_editor.side }}-bus-tag-error" class="invalid-feedback"></div> <div id="accounting-description-editor-{{ description_editor.debit_credit }}-bus-tag-error" class="invalid-feedback"></div>
</div> </div>
<div class="form-floating"> <div class="form-floating">
<input id="accounting-description-editor-{{ description_editor.side }}-bus-route" class="form-control" type="text" value="" placeholder=" "> <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.side }}-bus-route">{{ A_("Route") }}</label> <label class="form-label" for="accounting-description-editor-{{ description_editor.debit_credit }}-bus-route">{{ A_("Route") }}</label>
<div id="accounting-description-editor-{{ description_editor.side }}-bus-route-error" class="invalid-feedback"></div> <div id="accounting-description-editor-{{ description_editor.debit_credit }}-bus-route-error" class="invalid-feedback"></div>
</div> </div>
</div> </div>
<div> <div>
{% for tag in description_editor.bus.tags %} {% 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 }} {{ tag }}
</button> </button>
{% endfor %} {% endfor %}
@ -142,42 +142,42 @@ First written: 2023/2/28
<div class="d-flex justify-content-between mt-2"> <div class="d-flex justify-content-between mt-2">
<div class="form-floating me-2"> <div class="form-floating me-2">
<input id="accounting-description-editor-{{ description_editor.side }}-bus-from" class="form-control" type="text" value="" placeholder=" "> <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.side }}-bus-from">{{ A_("From") }}</label> <label class="form-label" for="accounting-description-editor-{{ description_editor.debit_credit }}-bus-from">{{ A_("From") }}</label>
<div id="accounting-description-editor-{{ description_editor.side }}-bus-from-error" class="invalid-feedback"></div> <div id="accounting-description-editor-{{ description_editor.debit_credit }}-bus-from-error" class="invalid-feedback"></div>
</div> </div>
<div class="form-floating"> <div class="form-floating">
<input id="accounting-description-editor-{{ description_editor.side }}-bus-to" class="form-control" type="text" value="" placeholder=" "> <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.side }}-bus-to">{{ A_("To") }}</label> <label class="form-label" for="accounting-description-editor-{{ description_editor.debit_credit }}-bus-to">{{ A_("To") }}</label>
<div id="accounting-description-editor-{{ description_editor.side }}-bus-to-error" class="invalid-feedback"></div> <div id="accounting-description-editor-{{ description_editor.debit_credit }}-bus-to-error" class="invalid-feedback"></div>
</div> </div>
</div> </div>
</div> </div>
{# A regular income or payment #} {# 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 #} {# TODO: To be done #}
</div> </div>
{# The annotation #} {# 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"> <div class="form-floating">
<input id="accounting-description-editor-{{ description_editor.side }}-annotation-number" class="form-control" type="number" min="1" value="" placeholder=" "> <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.side }}-annotation-number">{{ A_("The number of items") }}</label> <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.side }}-annotation-number-error" class="invalid-feedback"></div> <div id="accounting-description-editor-{{ description_editor.debit_credit }}-annotation-number-error" class="invalid-feedback"></div>
</div> </div>
<div class="form-floating mt-2"> <div class="form-floating mt-2">
<input id="accounting-description-editor-{{ description_editor.side }}-annotation-note" class="form-control" type="text" value="" placeholder=" "> <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.side }}-annotation-note">{{ A_("Note") }}</label> <label class="form-label" for="accounting-description-editor-{{ description_editor.debit_credit }}-annotation-note">{{ A_("Note") }}</label>
<div id="accounting-description-editor-{{ description_editor.side }}-annotation-note-error" class="invalid-feedback"></div> <div id="accounting-description-editor-{{ description_editor.debit_credit }}-annotation-note-error" class="invalid-feedback"></div>
</div> </div>
</div> </div>
{# The suggested accounts #} {# The suggested accounts #}
<div class="mt-3"> <div class="mt-3">
{% for account in description_editor.accounts %} {% 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 }} {{ account }}
</button> </button>
{% endfor %} {% endfor %}
@ -185,7 +185,7 @@ First written: 2023/2/28
</div> </div>
<div class="modal-footer"> <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 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> </div>
</div> </div>

View File

@ -20,24 +20,24 @@ Author: imacat@mail.imacat.idv.tw (imacat)
First written: 2023/2/25 First written: 2023/2/25
#} #}
{# <ul> For SonarQube not to complain about incorrect HTML #} {# <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 %} {% 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 %} {% 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 }}-{{ 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 }}-{{ 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 }}-{{ 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 }}-{{ 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 }}-{{ 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 }}-{{ 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 }}-{{ 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 }}-{{ 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 }}-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 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>
<div id="accounting-currency-{{ currency_index }}-{{ side }}-{{ line_item_index }}-account-text" class="small">{{ form.account_text }}</div> <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 }}-{{ side }}-{{ line_item_index }}-description-text">{{ form.description.data|accounting_default }}</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 }}-{{ 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 }}-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 %} {% if form.original_line_item_id.data %}{{ A_("Offset %(item)s", item=form.original_line_item_text|accounting_default) }}{% endif %}
</div> </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 %} {% if form.offsets %}
<div class="d-flex justify-content-between {% if not form.offsets %} d-none {% endif %}"> <div class="d-flex justify-content-between {% if not form.offsets %} d-none {% endif %}">
<div>{{ A_("Offsets") }}</div> <div>{{ A_("Offsets") }}</div>
@ -60,13 +60,13 @@ First written: 2023/2/25
{% endif %} {% endif %}
</div> </div>
</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>
<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>
<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> <i class="fas fa-minus"></i>
</button> </button>
</div> </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"> <ul id="accounting-original-line-item-selector-option-list" class="list-group accounting-selector-list">
{% for line_item in form.original_line_item_options %} {% 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>{{ line_item.voucher.date|accounting_format_date }} {{ line_item.description|accounting_default }}</div>
<div> <div>
<span class="badge bg-primary rounded-pill"> <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"> <ul id="accounting-currency-{{ currency_index }}-credit-list" class="list-group accounting-line-item-list">
{% for line_item_form in credit_forms %} {% for line_item_form in credit_forms %}
{% with currency_index = currency_index, {% with currency_index = currency_index,
side = "credit", debit_credit = "credit",
line_item_index = loop.index, line_item_index = loop.index,
only_one_line_item_form = credit_forms|length == 1, only_one_line_item_form = credit_forms|length == 1,
form = line_item_form.form %} form = line_item_form.form %}
@ -62,7 +62,7 @@ First written: 2023/2/25
</div> </div>
<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> <i class="fas fa-plus"></i>
{{ A_("New") }} {{ A_("New") }}
</button> </button>

View File

@ -50,7 +50,7 @@ First written: 2023/2/25
{% with description_editor = form.description_editor.credit %} {% with description_editor = form.description_editor.credit %}
{% include "accounting/voucher/include/description-editor-modal.html" %} {% include "accounting/voucher/include/description-editor-modal.html" %}
{% endwith %} {% endwith %}
{% with side = "credit", {% with debit_credit = "credit",
account_options = form.credit_account_options %} account_options = form.credit_account_options %}
{% include "accounting/voucher/include/account-selector-modal.html" %} {% include "accounting/voucher/include/account-selector-modal.html" %}
{% endwith %} {% 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"> <ul id="accounting-currency-{{ currency_index }}-debit-list" class="list-group accounting-line-item-list">
{% for line_item_form in debit_forms %} {% for line_item_form in debit_forms %}
{% with currency_index = currency_index, {% with currency_index = currency_index,
side = "debit", debit_credit = "debit",
line_item_index = loop.index, line_item_index = loop.index,
only_one_line_item_form = debit_forms|length == 1, only_one_line_item_form = debit_forms|length == 1,
form = line_item_form.form %} form = line_item_form.form %}
@ -64,7 +64,7 @@ First written: 2023/2/25
</div> </div>
<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> <i class="fas fa-plus"></i>
{{ A_("New") }} {{ A_("New") }}
</button> </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"> <ul id="accounting-currency-{{ currency_index }}-credit-list" class="list-group accounting-line-item-list">
{% for line_item_form in credit_forms %} {% for line_item_form in credit_forms %}
{% with currency_index = currency_index, {% with currency_index = currency_index,
side = "credit", debit_credit = "credit",
line_item_index = loop.index, line_item_index = loop.index,
only_one_line_item_form = credit_forms|length == 1, only_one_line_item_form = credit_forms|length == 1,
form = line_item_form.form %} form = line_item_form.form %}
@ -95,7 +95,7 @@ First written: 2023/2/25
</div> </div>
<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> <i class="fas fa-plus"></i>
{{ A_("New") }} {{ A_("New") }}
</button> </button>

View File

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

View File

@ -53,9 +53,9 @@ class OriginalLineItemExists:
"The original line item does not exist.")) "The original line item does not exist."))
class OriginalLineItemOppositeSide: class OriginalLineItemOppositeDebitCredit:
"""The validator to check if the original line item is on the opposite """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: def __call__(self, form: FlaskForm, field: IntegerField) -> None:
if field.data is None: if field.data is None:
@ -71,7 +71,7 @@ class OriginalLineItemOppositeSide:
and not original_line_item.is_debit: and not original_line_item.is_debit:
return return
raise ValidationError(lazy_gettext( 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: class OriginalLineItemNeedOffset:
@ -180,7 +180,7 @@ class KeepAccountWhenHavingOffset:
class NotStartPayableFromDebit: class NotStartPayableFromDebit:
"""The validator to check that a payable line item does not start from """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: def __call__(self, form: FlaskForm, field: StringField) -> None:
assert isinstance(form, DebitLineItemForm) assert isinstance(form, DebitLineItemForm)
@ -191,12 +191,12 @@ class NotStartPayableFromDebit:
account: Account | None = Account.find_by_code(field.data) account: Account | None = Account.find_by_code(field.data)
if account is not None and account.is_need_offset: if account is not None and account.is_need_offset:
raise ValidationError(lazy_gettext( raise ValidationError(lazy_gettext(
"A payable line item cannot start from the debit side.")) "A payable line item cannot start from debit."))
class NotStartReceivableFromCredit: class NotStartReceivableFromCredit:
"""The validator to check that a receivable line item does not start """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: def __call__(self, form: FlaskForm, field: StringField) -> None:
assert isinstance(form, CreditLineItemForm) assert isinstance(form, CreditLineItemForm)
@ -207,7 +207,7 @@ class NotStartReceivableFromCredit:
account: Account | None = Account.find_by_code(field.data) account: Account | None = Account.find_by_code(field.data)
if account is not None and account.is_need_offset: if account is not None and account.is_need_offset:
raise ValidationError(lazy_gettext( raise ValidationError(lazy_gettext(
"A receivable line item cannot start from the credit side.")) "A receivable line item cannot start from credit."))
class PositiveAmount: class PositiveAmount:
@ -439,7 +439,7 @@ class DebitLineItemForm(LineItemForm):
original_line_item_id = IntegerField( original_line_item_id = IntegerField(
validators=[Optional(), validators=[Optional(),
OriginalLineItemExists(), OriginalLineItemExists(),
OriginalLineItemOppositeSide(), OriginalLineItemOppositeDebitCredit(),
OriginalLineItemNeedOffset(), OriginalLineItemNeedOffset(),
OriginalLineItemNotOffset()]) OriginalLineItemNotOffset()])
"""The ID of the original line item.""" """The ID of the original line item."""
@ -489,7 +489,7 @@ class CreditLineItemForm(LineItemForm):
original_line_item_id = IntegerField( original_line_item_id = IntegerField(
validators=[Optional(), validators=[Optional(),
OriginalLineItemExists(), OriginalLineItemExists(),
OriginalLineItemOppositeSide(), OriginalLineItemOppositeDebitCredit(),
OriginalLineItemNeedOffset(), OriginalLineItemNeedOffset(),
OriginalLineItemNotOffset()]) OriginalLineItemNotOffset()])
"""The ID of the original line item.""" """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, def _make_cash_line_item(self, forms: list[LineItemForm], is_debit: bool,
currency_code: str, no: int) -> None: 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. voucher.
:param forms: The line item forms in the same currency. :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) return sorted(self.__tag_dict.values(), key=lambda x: -x.freq)
class DescriptionSide: class DescriptionDebitCredit:
"""A description side""" """The description on debit or credit."""
def __init__(self, side_id: t.Literal["debit", "credit"]): def __init__(self, debit_credit: t.Literal["debit", "credit"]):
"""Constructs a description side. """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 self.debit_credit: t.Literal["debit", "credit"] = debit_credit
"""The side.""" """Either debit or credit."""
self.general: DescriptionType = DescriptionType("general") self.general: DescriptionType = DescriptionType("general")
"""The general tags.""" """The general tags."""
self.travel: DescriptionType = DescriptionType("travel") self.travel: DescriptionType = DescriptionType("travel")
@ -179,7 +179,7 @@ class DescriptionSide:
@property @property
def accounts(self) -> list[DescriptionAccount]: def accounts(self) -> list[DescriptionAccount]:
"""Returns the suggested accounts of all tags in the description editor """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. :return: The suggested accounts of all tags, in their frequency order.
""" """
@ -202,12 +202,12 @@ class DescriptionEditor:
def __init__(self): def __init__(self):
"""Constructs the description editor.""" """Constructs the description editor."""
self.debit: DescriptionSide = DescriptionSide("debit") self.debit: DescriptionDebitCredit = DescriptionDebitCredit("debit")
"""The debit tags.""" """The debit tags."""
self.credit: DescriptionSide = DescriptionSide("credit") self.credit: DescriptionDebitCredit = DescriptionDebitCredit("credit")
"""The credit tags.""" """The credit tags."""
side: sa.Label = sa.case((VoucherLineItem.is_debit, "debit"), debit_credit: sa.Label = sa.case((VoucherLineItem.is_debit, "debit"),
else_="credit").label("side") else_="credit").label("debit_credit")
tag_type: sa.Label = sa.case( tag_type: sa.Label = sa.case(
(VoucherLineItem.description.like("_%—_%—_%→_%"), "bus"), (VoucherLineItem.description.like("_%—_%—_%→_%"), "bus"),
(sa.or_(VoucherLineItem.description.like("_%—_%→_%"), (sa.or_(VoucherLineItem.description.like("_%—_%→_%"),
@ -215,21 +215,22 @@ class DescriptionEditor:
else_="general").label("tag_type") else_="general").label("tag_type")
tag: sa.Label = get_prefix(VoucherLineItem.description, "")\ tag: sa.Label = get_prefix(VoucherLineItem.description, "")\
.label("tag") .label("tag")
select: sa.Select = sa.Select(side, tag_type, tag, select: sa.Select = sa.Select(debit_credit, tag_type, tag,
VoucherLineItem.account_id, VoucherLineItem.account_id,
sa.func.count().label("freq"))\ sa.func.count().label("freq"))\
.filter(VoucherLineItem.description.is_not(None), .filter(VoucherLineItem.description.is_not(None),
VoucherLineItem.description.like("_%—_%"), VoucherLineItem.description.like("_%—_%"),
VoucherLineItem.original_line_item_id.is_(None))\ 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() result: list[sa.Row] = db.session.execute(select).all()
accounts: dict[int, Account] \ accounts: dict[int, Account] \
= {x.id: x for x in Account.query = {x.id: x for x in Account.query
.filter(Account.id.in_({x.account_id for x in result})).all()} .filter(Account.id.in_({x.account_id for x in result})).all()}
side_dict: dict[t.Literal["debit", "credit"], DescriptionSide] \ debit_credit_dict: dict[t.Literal["debit", "credit"],
= {x.side: x for x in {self.debit, self.credit}} DescriptionDebitCredit] \
= {x.debit_credit: x for x in {self.debit, self.credit}}
for row in result: 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) row.tag_type, row.tag, accounts[row.account_id], row.freq)

View File

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

View File

@ -101,7 +101,7 @@ class OffsetTestCase(unittest.TestCase):
self.assertEqual(response.status_code, 302) self.assertEqual(response.status_code, 302)
self.assertEqual(response.headers["Location"], create_uri) self.assertEqual(response.headers["Location"], create_uri)
# The same side # The same debit or credit
form = voucher_data.new_form(self.csrf_token) form = voucher_data.new_form(self.csrf_token)
form["currency-1-credit-1-original_line_item_id"] \ form["currency-1-credit-1-original_line_item_id"] \
= self.data.e_p_or1c.id = self.data.e_p_or1c.id
@ -210,7 +210,7 @@ class OffsetTestCase(unittest.TestCase):
self.assertEqual(response.status_code, 302) self.assertEqual(response.status_code, 302)
self.assertEqual(response.headers["Location"], edit_uri) self.assertEqual(response.headers["Location"], edit_uri)
# The same side # The same debit or credit
form = voucher_data.update_form(self.csrf_token) form = voucher_data.update_form(self.csrf_token)
form["currency-1-credit-1-original_line_item_id"] \ form["currency-1-credit-1-original_line_item_id"] \
= self.data.e_p_or1c.id = self.data.e_p_or1c.id
@ -417,7 +417,7 @@ class OffsetTestCase(unittest.TestCase):
self.assertEqual(response.status_code, 302) self.assertEqual(response.status_code, 302)
self.assertEqual(response.headers["Location"], create_uri) self.assertEqual(response.headers["Location"], create_uri)
# The same side # The same debit or credit
form = voucher_data.new_form(self.csrf_token) form = voucher_data.new_form(self.csrf_token)
form["currency-1-debit-1-original_line_item_id"] \ form["currency-1-debit-1-original_line_item_id"] \
= self.data.e_r_or1d.id = self.data.e_r_or1d.id
@ -524,7 +524,7 @@ class OffsetTestCase(unittest.TestCase):
self.assertEqual(response.status_code, 302) self.assertEqual(response.status_code, 302)
self.assertEqual(response.headers["Location"], edit_uri) self.assertEqual(response.headers["Location"], edit_uri)
# The same side # The same debit or credit
form = voucher_data.update_form(self.csrf_token) form = voucher_data.update_form(self.csrf_token)
form["currency-1-debit-1-original_line_item_id"] \ form["currency-1-debit-1-original_line_item_id"] \
= self.data.e_r_or1d.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.status_code, 302)
self.assertEqual(response.headers["Location"], create_uri) 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() form = self.__get_add_form()
key: str = [x for x in form.keys() key: str = [x for x in form.keys()
if x.endswith("-account_code") and "-credit-" in x][0] 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.status_code, 302)
self.assertEqual(response.headers["Location"], edit_uri) 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() form = self.__get_add_form()
key: str = [x for x in form.keys() key: str = [x for x in form.keys()
if x.endswith("-account_code") and "-credit-" in x][0] 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.status_code, 302)
self.assertEqual(response.headers["Location"], create_uri) 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() form = self.__get_add_form()
key: str = [x for x in form.keys() key: str = [x for x in form.keys()
if x.endswith("-account_code") and "-debit-" in x][0] 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.status_code, 302)
self.assertEqual(response.headers["Location"], edit_uri) 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() form = self.__get_add_form()
key: str = [x for x in form.keys() key: str = [x for x in form.keys()
if x.endswith("-account_code") and "-debit-" in x][0] 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.status_code, 302)
self.assertEqual(response.headers["Location"], create_uri) 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() form = self.__get_add_form()
key: str = [x for x in form.keys() key: str = [x for x in form.keys()
if x.endswith("-account_code") and "-credit-" in x][0] 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.status_code, 302)
self.assertEqual(response.headers["Location"], create_uri) 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() form = self.__get_add_form()
key: str = [x for x in form.keys() key: str = [x for x in form.keys()
if x.endswith("-account_code") and "-debit-" in x][0] 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.status_code, 302)
self.assertEqual(response.headers["Location"], edit_uri) 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() form = self.__get_add_form()
key: str = [x for x in form.keys() key: str = [x for x in form.keys()
if x.endswith("-account_code") and "-credit-" in x][0] 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.status_code, 302)
self.assertEqual(response.headers["Location"], edit_uri) 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() form = self.__get_add_form()
key: str = [x for x in form.keys() key: str = [x for x in form.keys()
if x.endswith("-account_code") and "-debit-" in x][0] if x.endswith("-account_code") and "-debit-" in x][0]

View File

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