Added the JavaScript AccountOption class to object-ize the account options in the journal entry form.
This commit is contained in:
parent
8a174d8847
commit
eb5a7bef7e
@ -32,7 +32,7 @@ class AccountSelector {
|
|||||||
* The line item editor
|
* The line item editor
|
||||||
* @type {JournalEntryLineItemEditor}
|
* @type {JournalEntryLineItemEditor}
|
||||||
*/
|
*/
|
||||||
#lineItemEditor;
|
lineItemEditor;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Either "debit" or "credit"
|
* Either "debit" or "credit"
|
||||||
@ -66,7 +66,7 @@ class AccountSelector {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* The options
|
* The options
|
||||||
* @type {HTMLLIElement[]}
|
* @type {AccountOption[]}
|
||||||
*/
|
*/
|
||||||
#options;
|
#options;
|
||||||
|
|
||||||
@ -76,6 +76,12 @@ class AccountSelector {
|
|||||||
*/
|
*/
|
||||||
#more;
|
#more;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether to show all accounts
|
||||||
|
* @type {boolean}
|
||||||
|
*/
|
||||||
|
#isShowMore = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs an account selector.
|
* Constructs an account selector.
|
||||||
*
|
*
|
||||||
@ -83,27 +89,23 @@ class AccountSelector {
|
|||||||
* @param debitCredit {string} either "debit" or "credit"
|
* @param debitCredit {string} either "debit" or "credit"
|
||||||
*/
|
*/
|
||||||
constructor(lineItemEditor, debitCredit) {
|
constructor(lineItemEditor, debitCredit) {
|
||||||
this.#lineItemEditor = lineItemEditor
|
this.lineItemEditor = lineItemEditor
|
||||||
this.#debitCredit = debitCredit;
|
this.#debitCredit = debitCredit;
|
||||||
const prefix = "accounting-account-selector-" + debitCredit;
|
const prefix = "accounting-account-selector-" + debitCredit;
|
||||||
this.#query = document.getElementById(prefix + "-query");
|
this.#query = document.getElementById(prefix + "-query");
|
||||||
this.#queryNoResult = document.getElementById(prefix + "-option-no-result");
|
this.#queryNoResult = document.getElementById(prefix + "-option-no-result");
|
||||||
this.#optionList = document.getElementById(prefix + "-option-list");
|
this.#optionList = document.getElementById(prefix + "-option-list");
|
||||||
// noinspection JSValidateTypes
|
this.#options = Array.from(document.getElementsByClassName(prefix + "-option")).map((element) => new AccountOption(this, element));
|
||||||
this.#options = Array.from(document.getElementsByClassName(prefix + "-option"));
|
|
||||||
this.#more = document.getElementById(prefix + "-more");
|
this.#more = document.getElementById(prefix + "-more");
|
||||||
this.#clearButton = document.getElementById(prefix + "-btn-clear");
|
this.#clearButton = document.getElementById(prefix + "-btn-clear");
|
||||||
|
|
||||||
this.#more.onclick = () => {
|
this.#more.onclick = () => {
|
||||||
|
this.#isShowMore = true;
|
||||||
this.#more.classList.add("d-none");
|
this.#more.classList.add("d-none");
|
||||||
this.#filterOptions();
|
this.#filterOptions();
|
||||||
};
|
};
|
||||||
this.#clearButton.onclick = () => this.#lineItemEditor.clearAccount();
|
this.#query.oninput = () => this.#filterOptions();
|
||||||
for (const option of this.#options) {
|
this.#clearButton.onclick = () => this.lineItemEditor.clearAccount();
|
||||||
option.onclick = () => this.#lineItemEditor.saveAccount(option.dataset.code, option.dataset.text, option.classList.contains("accounting-account-is-need-offset"));
|
|
||||||
}
|
|
||||||
this.#query.addEventListener("input", () => {
|
|
||||||
this.#filterOptions();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -112,17 +114,16 @@ class AccountSelector {
|
|||||||
*/
|
*/
|
||||||
#filterOptions() {
|
#filterOptions() {
|
||||||
const codesInUse = this.#getCodesUsedInForm();
|
const codesInUse = this.#getCodesUsedInForm();
|
||||||
let shouldAnyShow = false;
|
let isAnyMatched = false;
|
||||||
for (const option of this.#options) {
|
for (const option of this.#options) {
|
||||||
const shouldShow = this.#shouldOptionShow(option, this.#more, codesInUse, this.#query);
|
if (option.isMatched(this.#isShowMore, codesInUse, this.#query.value)) {
|
||||||
if (shouldShow) {
|
option.setShown(true);
|
||||||
option.classList.remove("d-none");
|
isAnyMatched = true;
|
||||||
shouldAnyShow = true;
|
|
||||||
} else {
|
} else {
|
||||||
option.classList.add("d-none");
|
option.setShown(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!shouldAnyShow && this.#more.classList.contains("d-none")) {
|
if (!isAnyMatched) {
|
||||||
this.#optionList.classList.add("d-none");
|
this.#optionList.classList.add("d-none");
|
||||||
this.#queryNoResult.classList.remove("d-none");
|
this.#queryNoResult.classList.remove("d-none");
|
||||||
} else {
|
} else {
|
||||||
@ -137,60 +138,26 @@ 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.#debitCredit);
|
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);
|
||||||
}
|
}
|
||||||
return inUse
|
return inUse
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns whether an option should show.
|
|
||||||
*
|
|
||||||
* @param option {HTMLLIElement} the option
|
|
||||||
* @param more {HTMLLIElement} the more element
|
|
||||||
* @param inUse {string[]} the account codes that are used in the form
|
|
||||||
* @param query {HTMLInputElement} the query element, if any
|
|
||||||
* @return {boolean} true if the option should show, or false otherwise
|
|
||||||
*/
|
|
||||||
#shouldOptionShow(option, more, inUse, query) {
|
|
||||||
const isQueryMatched = () => {
|
|
||||||
if (query.value === "") {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
const queryValues = JSON.parse(option.dataset.queryValues);
|
|
||||||
for (const queryValue of queryValues) {
|
|
||||||
if (queryValue.toLowerCase().includes(query.value.toLowerCase())) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
const isMoreMatched = () => {
|
|
||||||
if (more.classList.contains("d-none")) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return option.classList.contains("accounting-account-in-use") || inUse.includes(option.dataset.code);
|
|
||||||
};
|
|
||||||
return isMoreMatched() && isQueryMatched();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The callback when the account selector is shown.
|
* The callback when the account selector is shown.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
onOpen() {
|
onOpen() {
|
||||||
this.#query.value = "";
|
this.#query.value = "";
|
||||||
|
this.#isShowMore = false;
|
||||||
this.#more.classList.remove("d-none");
|
this.#more.classList.remove("d-none");
|
||||||
this.#filterOptions();
|
this.#filterOptions();
|
||||||
for (const option of this.#options) {
|
for (const option of this.#options) {
|
||||||
if (option.dataset.code === this.#lineItemEditor.accountCode) {
|
option.setActive(option.code === this.lineItemEditor.accountCode);
|
||||||
option.classList.add("active");
|
|
||||||
} else {
|
|
||||||
option.classList.remove("active");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (this.#lineItemEditor.accountCode === null) {
|
if (this.lineItemEditor.accountCode === null) {
|
||||||
this.#clearButton.classList.add("btn-secondary");
|
this.#clearButton.classList.add("btn-secondary");
|
||||||
this.#clearButton.classList.remove("btn-danger");
|
this.#clearButton.classList.remove("btn-danger");
|
||||||
this.#clearButton.disabled = true;
|
this.#clearButton.disabled = true;
|
||||||
@ -216,3 +183,137 @@ class AccountSelector {
|
|||||||
return selectors;
|
return selectors;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An account option
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
class AccountOption {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The account selector
|
||||||
|
* @type {AccountSelector}
|
||||||
|
*/
|
||||||
|
#selector;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The element
|
||||||
|
* @type {HTMLLIElement}
|
||||||
|
*/
|
||||||
|
#element;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The account code
|
||||||
|
* @type {string}
|
||||||
|
*/
|
||||||
|
code;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The account text
|
||||||
|
* @type {string}
|
||||||
|
*/
|
||||||
|
text;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether the account is in use
|
||||||
|
* @type {boolean}
|
||||||
|
*/
|
||||||
|
#isInUse;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether line items in the account need offset
|
||||||
|
* @type {boolean}
|
||||||
|
*/
|
||||||
|
isNeedOffset;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The values to query against
|
||||||
|
* @type {string[]}
|
||||||
|
*/
|
||||||
|
#queryValues;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs the account in the account selector.
|
||||||
|
*
|
||||||
|
* @param selector {AccountSelector} the account selector
|
||||||
|
* @param element {HTMLLIElement} the element
|
||||||
|
*/
|
||||||
|
constructor(selector, element) {
|
||||||
|
this.#selector = selector;
|
||||||
|
this.#element = element;
|
||||||
|
this.code = element.dataset.code;
|
||||||
|
this.text = element.dataset.text;
|
||||||
|
this.#isInUse = element.classList.contains("accounting-account-is-in-use");
|
||||||
|
this.isNeedOffset = element.classList.contains("accounting-account-is-need-offset");
|
||||||
|
this.#queryValues = JSON.parse(element.dataset.queryValues);
|
||||||
|
|
||||||
|
this.#element.onclick = () => this.#selector.lineItemEditor.saveAccount(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether the account matches the query.
|
||||||
|
*
|
||||||
|
* @param isShowMore {boolean} true to show all accounts, or false to show only those in use
|
||||||
|
* @param codesInUse {string[]} the account codes that are used in the form
|
||||||
|
* @param query {string} the query term
|
||||||
|
* @return {boolean} true if the option matches, or false otherwise
|
||||||
|
*/
|
||||||
|
isMatched(isShowMore, codesInUse, query) {
|
||||||
|
return this.#isInUseMatched(isShowMore, codesInUse) && this.#isQueryMatched(query);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether the account matches the "in-use" condition.
|
||||||
|
*
|
||||||
|
* @param isShowMore {boolean} true to show all accounts, or false to show only those in use
|
||||||
|
* @param codesInUse {string[]} the account codes that are used in the form
|
||||||
|
* @return {boolean} true if the option matches, or false otherwise
|
||||||
|
*/
|
||||||
|
#isInUseMatched(isShowMore, codesInUse) {
|
||||||
|
return isShowMore || this.#isInUse || codesInUse.includes(this.code);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether the account matches the query term.
|
||||||
|
*
|
||||||
|
* @param query {string} the query term
|
||||||
|
* @return {boolean} true if the option matches, or false otherwise
|
||||||
|
*/
|
||||||
|
#isQueryMatched(query) {
|
||||||
|
if (query === "") {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
for (const queryValue of this.#queryValues) {
|
||||||
|
if (queryValue.toLowerCase().includes(query.toLowerCase())) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets whether the option is shown.
|
||||||
|
*
|
||||||
|
* @param isShown {boolean} true to show, or false otherwise
|
||||||
|
*/
|
||||||
|
setShown(isShown) {
|
||||||
|
if (isShown) {
|
||||||
|
this.#element.classList.remove("d-none");
|
||||||
|
} else {
|
||||||
|
this.#element.classList.add("d-none");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets whether the option is active.
|
||||||
|
*
|
||||||
|
* @param isActive {boolean} true if active, or false otherwise
|
||||||
|
*/
|
||||||
|
setActive(isActive) {
|
||||||
|
if (isActive) {
|
||||||
|
this.#element.classList.add("active");
|
||||||
|
} else {
|
||||||
|
this.#element.classList.remove("active");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -368,18 +368,16 @@ class JournalEntryLineItemEditor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the account.
|
* Saves the selected account.
|
||||||
*
|
*
|
||||||
* @param code {string} the account code
|
* @param account {AccountOption} the selected account
|
||||||
* @param text {string} the account text
|
|
||||||
* @param isNeedOffset {boolean} true if the line items in the account need offset or false otherwise
|
|
||||||
*/
|
*/
|
||||||
saveAccount(code, text, isNeedOffset) {
|
saveAccount(account) {
|
||||||
this.isNeedOffset = isNeedOffset;
|
this.isNeedOffset = account.isNeedOffset;
|
||||||
this.#accountControl.classList.add("accounting-not-empty");
|
this.#accountControl.classList.add("accounting-not-empty");
|
||||||
this.accountCode = code;
|
this.accountCode = account.code;
|
||||||
this.accountText = text;
|
this.accountText = account.text;
|
||||||
this.#accountText.innerText = text;
|
this.#accountText.innerText = account.text;
|
||||||
this.#validateAccount();
|
this.#validateAccount();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -37,7 +37,7 @@ First written: 2023/2/25
|
|||||||
|
|
||||||
<ul id="accounting-account-selector-{{ debit_credit }}-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-{{ 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-text="{{ 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-is-in-use {% endif %} {% if account.is_need_offset %} accounting-account-is-need-offset {% endif %}" data-code="{{ account.code }}" data-text="{{ 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 %}
|
||||||
|
Loading…
Reference in New Issue
Block a user