diff --git a/src/accounting/static/js/journal-entry-editor.js b/src/accounting/static/js/journal-entry-editor.js
new file mode 100644
index 0000000..f097850
--- /dev/null
+++ b/src/accounting/static/js/journal-entry-editor.js
@@ -0,0 +1,387 @@
+/* The Mia! Accounting Flask Project
+ * journal-entry-editor.js: The JavaScript for the journal entry editor
+ */
+
+/* Copyright (c) 2023 imacat.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* Author: imacat@mail.imacat.idv.tw (imacat)
+ * First written: 2023/2/25
+ */
+"use strict";
+
+// Initializes the page JavaScript.
+document.addEventListener("DOMContentLoaded", () => {
+ JournalEntryEditor.initialize();
+});
+
+/**
+ * The journal entry editor.
+ *
+ */
+class JournalEntryEditor {
+
+ /**
+ * The journal entry editor
+ * @type {HTMLFormElement}
+ */
+ #element;
+
+ /**
+ * The bootstrap modal
+ * @type {HTMLDivElement}
+ */
+ #modal;
+
+ /**
+ * The entry type, either "debit" or "credit"
+ * @type {string}
+ */
+ entryType;
+
+ /**
+ * The prefix of the HTML ID and class
+ * @type {string}
+ */
+ #prefix = "accounting-entry-editor"
+
+ /**
+ * The control of the account
+ * @type {HTMLDivElement}
+ */
+ #accountControl;
+
+ /**
+ * The account
+ * @type {HTMLDivElement}
+ */
+ #account;
+
+ /**
+ * The error message of the account
+ * @type {HTMLDivElement}
+ */
+ #accountError;
+
+ /**
+ * The control of the summary
+ * @type {HTMLDivElement}
+ */
+ #summaryControl;
+
+ /**
+ * The summary
+ * @type {HTMLDivElement}
+ */
+ #summary;
+
+ /**
+ * The error message of the summary
+ * @type {HTMLDivElement}
+ */
+ #summaryError;
+
+ /**
+ * The amount
+ * @type {HTMLInputElement}
+ */
+ #amount;
+
+ /**
+ * The error message of the amount
+ * @type {HTMLDivElement}
+ */
+ #amountError;
+
+ /**
+ * The journal entry to edit
+ * @type {JournalEntrySubForm|null}
+ */
+ #entry;
+
+ /**
+ * The debit or credit entry side sub-form
+ * @type {DebitCreditSideSubForm}
+ */
+ #side;
+
+ /**
+ * Constructs a new journal entry editor.
+ *
+ */
+ constructor() {
+ this.#element = document.getElementById(this.#prefix);
+ this.#modal = document.getElementById(this.#prefix + "-modal");
+ this.#accountControl = document.getElementById(this.#prefix + "-account-control");
+ this.#account = document.getElementById(this.#prefix + "-account");
+ this.#accountError = document.getElementById(this.#prefix + "-account-error")
+ this.#summaryControl = document.getElementById(this.#prefix + "-summary-control");
+ this.#summary = document.getElementById(this.#prefix + "-summary");
+ this.#summaryError = document.getElementById(this.#prefix + "-summary-error");
+ this.#amount = document.getElementById(this.#prefix + "-amount");
+ this.#amountError = document.getElementById(this.#prefix + "-amount-error")
+ this.#accountControl.onclick = () => {
+ AccountSelector.start(this, this.entryType);
+ }
+ this.#summaryControl.onclick = () => {
+ SummaryEditor.start(this, this.#summary.dataset.value);
+ };
+ this.#element.onsubmit = () => {
+ if (this.#validate()) {
+ if (this.#entry === null) {
+ this.#entry = this.#side.addJournalEntry();
+ }
+ this.#entry.save(this.#account.dataset.code, this.#account.dataset.text, this.#summary.dataset.value, this.#amount.value);
+ this.#side.updateTotal();
+ this.#side.currency.validateBalance();
+ bootstrap.Modal.getInstance(this.#modal).hide();
+ }
+ return false;
+ };
+ }
+
+ /**
+ * Returns the account code.
+ *
+ * @return {string|null} the account code
+ */
+ getAccountCode() {
+ return this.#account.dataset.code === "" ? null : this.#account.dataset.code;
+ }
+
+ /**
+ * Clears the account.
+ *
+ */
+ clearAccount() {
+ this.#accountControl.classList.remove("accounting-not-empty");
+ this.#account.dataset.code = "";
+ this.#account.dataset.text = "";
+ this.#account.innerText = "";
+ this.#validateAccount();
+ }
+
+ /**
+ * Sets the account.
+ *
+ * @param code {string} the account code
+ * @param text {string} the account text
+ */
+ saveAccount(code, text) {
+ this.#accountControl.classList.add("accounting-not-empty");
+ this.#account.dataset.code = code;
+ this.#account.dataset.text = text;
+ this.#account.innerText = text;
+ this.#validateAccount();
+ }
+
+ /**
+ * Saves the summary from the summary editor.
+ *
+ * @param summary {string} the summary
+ */
+ saveSummary(summary) {
+ if (summary === "") {
+ this.#summaryControl.classList.remove("accounting-not-empty");
+ } else {
+ this.#summaryControl.classList.add("accounting-not-empty");
+ }
+ this.#summary.dataset.value = summary;
+ this.#summary.innerText = summary;
+ bootstrap.Modal.getOrCreateInstance(this.#modal).show();
+ }
+
+ /**
+ * Saves the summary with the suggested account from the summary editor.
+ *
+ * @param summary {string} the summary
+ * @param accountCode {string} the account code
+ * @param accountText {string} the account text
+ */
+ saveSummaryWithAccount(summary, accountCode, accountText) {
+ this.#accountControl.classList.add("accounting-not-empty");
+ this.#account.dataset.code = accountCode;
+ this.#account.dataset.text = accountText;
+ this.#account.innerText = accountText;
+ this.#validateAccount();
+ this.saveSummary(summary)
+ }
+
+ /**
+ * Validates the form.
+ *
+ * @returns {boolean} true if valid, or false otherwise
+ */
+ #validate() {
+ let isValid = true;
+ isValid = this.#validateAccount() && isValid;
+ isValid = this.#validateSummary() && isValid;
+ isValid = this.#validateAmount() && isValid
+ return isValid;
+ }
+
+ /**
+ * Validates the account.
+ *
+ * @return {boolean} true if valid, or false otherwise
+ */
+ #validateAccount() {
+ if (this.#account.dataset.code === "") {
+ this.#accountControl.classList.add("is-invalid");
+ this.#accountError.innerText = A_("Please select the account.");
+ return false;
+ }
+ this.#accountControl.classList.remove("is-invalid");
+ this.#accountError.innerText = "";
+ return true;
+ }
+
+ /**
+ * Validates the summary.
+ *
+ * @return {boolean} true if valid, or false otherwise
+ * @private
+ */
+ #validateSummary() {
+ this.#summary.classList.remove("is-invalid");
+ this.#summaryError.innerText = "";
+ return true;
+ }
+
+ /**
+ * Validates the amount.
+ *
+ * @return {boolean} true if valid, or false otherwise
+ * @private
+ */
+ #validateAmount() {
+ this.#amount.value = this.#amount.value.trim();
+ this.#amount.classList.remove("is-invalid");
+ if (this.#amount.value === "") {
+ this.#amount.classList.add("is-invalid");
+ this.#amountError.innerText = A_("Please fill in the amount.");
+ return false;
+ }
+ this.#amount.classList.remove("is-invalid");
+ this.#amount.innerText = "";
+ return true;
+ }
+
+ /**
+ * Adds a new journal entry.
+ *
+ * @param side {DebitCreditSideSubForm} the debit or credit side sub-form
+ */
+ #onAddNew(side) {
+ this.#entry = null;
+ this.#side = side;
+ this.entryType = this.#side.entryType;
+ this.#element.dataset.entryType = side.entryType;
+ this.#accountControl.dataset.bsTarget = "#accounting-account-selector-" + side.entryType + "-modal";
+ this.#accountControl.classList.remove("accounting-not-empty");
+ this.#accountControl.classList.remove("is-invalid");
+ this.#account.innerText = "";
+ this.#account.dataset.code = "";
+ this.#account.dataset.text = "";
+ this.#accountError.innerText = "";
+ this.#summaryControl.dataset.bsTarget = "#accounting-summary-editor-" + side.entryType + "-modal";
+ this.#summaryControl.classList.remove("accounting-not-empty");
+ this.#summaryControl.classList.remove("is-invalid");
+ this.#summary.dataset.value = "";
+ this.#summary.innerText = ""
+ this.#summaryError.innerText = ""
+ this.#amount.value = "";
+ this.#amount.classList.remove("is-invalid");
+ this.#amountError.innerText = "";
+ }
+
+ /**
+ * Edits a journal entry.
+ *
+ * @param entry {JournalEntrySubForm} the journal entry sub-form
+ * @param accountCode {string} the account code
+ * @param accountText {string} the account text
+ * @param summary {string} the summary
+ * @param amount {string} the amount
+ */
+ #onEdit(entry, accountCode, accountText, summary, amount) {
+ this.#entry = entry;
+ this.#side = entry.side;
+ this.entryType = this.#side.entryType;
+ this.#element.dataset.entryType = entry.entryType;
+ this.#accountControl.dataset.bsTarget = "#accounting-account-selector-" + entry.entryType + "-modal";
+ if (accountCode === "") {
+ this.#accountControl.classList.remove("accounting-not-empty");
+ } else {
+ this.#accountControl.classList.add("accounting-not-empty");
+ }
+ this.#account.innerText = accountText;
+ this.#account.dataset.code = accountCode;
+ this.#account.dataset.text = accountText;
+ this.#summaryControl.dataset.bsTarget = "#accounting-summary-editor-" + entry.entryType + "-modal";
+ if (summary === "") {
+ this.#summaryControl.classList.remove("accounting-not-empty");
+ } else {
+ this.#summaryControl.classList.add("accounting-not-empty");
+ }
+ this.#summary.dataset.value = summary;
+ this.#summary.innerText = summary;
+ this.#amount.value = amount;
+ }
+
+ /**
+ * The journal entry editor
+ * @type {JournalEntryEditor}
+ */
+ static #editor;
+
+ /**
+ * Initializes the journal entry editor.
+ *
+ */
+ static initialize() {
+ this.#editor = new JournalEntryEditor();
+ }
+
+ /**
+ * Adds a new journal entry.
+ *
+ * @param side {DebitCreditSideSubForm} the debit or credit side sub-form
+ */
+ static addNew(side) {
+ this.#editor.#onAddNew(side);
+ }
+
+ /**
+ * Edits a journal entry.
+ *
+ * @param entry {JournalEntrySubForm} the journal entry sub-form
+ * @param accountCode {string} the account code
+ * @param accountText {string} the account text
+ * @param summary {string} the summary
+ * @param amount {string} the amount
+ */
+ static edit(entry, accountCode, accountText, summary, amount) {
+ this.#editor.#onEdit(entry, accountCode, accountText, summary, amount);
+ }
+
+ /**
+ * Validates the account when the account is updated from the account selector.
+ *
+ */
+ static validateAccount() {
+ this.#editor.#validateAccount();
+ }
+}
diff --git a/src/accounting/static/js/transaction-form.js b/src/accounting/static/js/transaction-form.js
index 737bdea..d1acd9e 100644
--- a/src/accounting/static/js/transaction-form.js
+++ b/src/accounting/static/js/transaction-form.js
@@ -22,10 +22,8 @@
*/
"use strict";
-// Initializes the page JavaScript.
document.addEventListener("DOMContentLoaded", () => {
TransactionForm.initialize();
- JournalEntryEditor.initialize();
});
/**
@@ -774,365 +772,6 @@ class JournalEntrySubForm {
}
}
-/**
- * The journal entry editor.
- *
- */
-class JournalEntryEditor {
-
- /**
- * The journal entry editor
- * @type {HTMLFormElement}
- */
- #element;
-
- /**
- * The bootstrap modal
- * @type {HTMLDivElement}
- */
- #modal;
-
- /**
- * The entry type, either "debit" or "credit"
- * @type {string}
- */
- entryType;
-
- /**
- * The prefix of the HTML ID and class
- * @type {string}
- */
- #prefix = "accounting-entry-editor"
-
- /**
- * The control of the account
- * @type {HTMLDivElement}
- */
- #accountControl;
-
- /**
- * The account
- * @type {HTMLDivElement}
- */
- #account;
-
- /**
- * The error message of the account
- * @type {HTMLDivElement}
- */
- #accountError;
-
- /**
- * The control of the summary
- * @type {HTMLDivElement}
- */
- #summaryControl;
-
- /**
- * The summary
- * @type {HTMLDivElement}
- */
- #summary;
-
- /**
- * The error message of the summary
- * @type {HTMLDivElement}
- */
- #summaryError;
-
- /**
- * The amount
- * @type {HTMLInputElement}
- */
- #amount;
-
- /**
- * The error message of the amount
- * @type {HTMLDivElement}
- */
- #amountError;
-
- /**
- * The journal entry to edit
- * @type {JournalEntrySubForm|null}
- */
- #entry;
-
- /**
- * The debit or credit entry side sub-form
- * @type {DebitCreditSideSubForm}
- */
- #side;
-
- /**
- * Constructs a new journal entry editor.
- *
- */
- constructor() {
- this.#element = document.getElementById(this.#prefix);
- this.#modal = document.getElementById(this.#prefix + "-modal");
- this.#accountControl = document.getElementById(this.#prefix + "-account-control");
- this.#account = document.getElementById(this.#prefix + "-account");
- this.#accountError = document.getElementById(this.#prefix + "-account-error")
- this.#summaryControl = document.getElementById(this.#prefix + "-summary-control");
- this.#summary = document.getElementById(this.#prefix + "-summary");
- this.#summaryError = document.getElementById(this.#prefix + "-summary-error");
- this.#amount = document.getElementById(this.#prefix + "-amount");
- this.#amountError = document.getElementById(this.#prefix + "-amount-error")
- this.#accountControl.onclick = () => {
- AccountSelector.start(this, this.entryType);
- }
- this.#summaryControl.onclick = () => {
- SummaryEditor.start(this, this.#summary.dataset.value);
- };
- this.#element.onsubmit = () => {
- if (this.#validate()) {
- if (this.#entry === null) {
- this.#entry = this.#side.addJournalEntry();
- }
- this.#entry.save(this.#account.dataset.code, this.#account.dataset.text, this.#summary.dataset.value, this.#amount.value);
- this.#side.updateTotal();
- this.#side.currency.validateBalance();
- bootstrap.Modal.getInstance(this.#modal).hide();
- }
- return false;
- };
- }
-
- /**
- * Returns the account code.
- *
- * @return {string|null} the account code
- */
- getAccountCode() {
- return this.#account.dataset.code === ""? null: this.#account.dataset.code;
- }
-
- /**
- * Clears the account.
- *
- */
- clearAccount() {
- this.#accountControl.classList.remove("accounting-not-empty");
- this.#account.dataset.code = "";
- this.#account.dataset.text = "";
- this.#account.innerText = "";
- this.#validateAccount();
- }
-
- /**
- * Sets the account.
- *
- * @param code {string} the account code
- * @param text {string} the account text
- */
- saveAccount(code, text) {
- this.#accountControl.classList.add("accounting-not-empty");
- this.#account.dataset.code = code;
- this.#account.dataset.text = text;
- this.#account.innerText = text;
- this.#validateAccount();
- }
-
- /**
- * Saves the summary from the summary editor.
- *
- * @param summary {string} the summary
- */
- saveSummary(summary) {
- if (summary === "") {
- this.#summaryControl.classList.remove("accounting-not-empty");
- } else {
- this.#summaryControl.classList.add("accounting-not-empty");
- }
- this.#summary.dataset.value = summary;
- this.#summary.innerText = summary;
- bootstrap.Modal.getOrCreateInstance(this.#modal).show();
- }
-
- /**
- * Saves the summary with the suggested account from the summary editor.
- *
- * @param summary {string} the summary
- * @param accountCode {string} the account code
- * @param accountText {string} the account text
- */
- saveSummaryWithAccount(summary, accountCode, accountText) {
- this.#accountControl.classList.add("accounting-not-empty");
- this.#account.dataset.code = accountCode;
- this.#account.dataset.text = accountText;
- this.#account.innerText = accountText;
- this.#validateAccount();
- this.saveSummary(summary)
- }
-
- /**
- * Validates the form.
- *
- * @returns {boolean} true if valid, or false otherwise
- */
- #validate() {
- let isValid = true;
- isValid = this.#validateAccount() && isValid;
- isValid = this.#validateSummary() && isValid;
- isValid = this.#validateAmount() && isValid
- return isValid;
- }
-
- /**
- * Validates the account.
- *
- * @return {boolean} true if valid, or false otherwise
- */
- #validateAccount() {
- if (this.#account.dataset.code === "") {
- this.#accountControl.classList.add("is-invalid");
- this.#accountError.innerText = A_("Please select the account.");
- return false;
- }
- this.#accountControl.classList.remove("is-invalid");
- this.#accountError.innerText = "";
- return true;
- }
-
- /**
- * Validates the summary.
- *
- * @return {boolean} true if valid, or false otherwise
- * @private
- */
- #validateSummary() {
- this.#summary.classList.remove("is-invalid");
- this.#summaryError.innerText = "";
- return true;
- }
-
- /**
- * Validates the amount.
- *
- * @return {boolean} true if valid, or false otherwise
- * @private
- */
- #validateAmount() {
- this.#amount.value = this.#amount.value.trim();
- this.#amount.classList.remove("is-invalid");
- if (this.#amount.value === "") {
- this.#amount.classList.add("is-invalid");
- this.#amountError.innerText = A_("Please fill in the amount.");
- return false;
- }
- this.#amount.classList.remove("is-invalid");
- this.#amount.innerText = "";
- return true;
- }
-
- /**
- * Adds a new journal entry.
- *
- * @param side {DebitCreditSideSubForm} the debit or credit side sub-form
- */
- #onAddNew(side) {
- this.#entry = null;
- this.#side = side;
- this.entryType = this.#side.entryType;
- this.#element.dataset.entryType = side.entryType;
- this.#accountControl.dataset.bsTarget = "#accounting-account-selector-" + side.entryType + "-modal";
- this.#accountControl.classList.remove("accounting-not-empty");
- this.#accountControl.classList.remove("is-invalid");
- this.#account.innerText = "";
- this.#account.dataset.code = "";
- this.#account.dataset.text = "";
- this.#accountError.innerText = "";
- this.#summaryControl.dataset.bsTarget = "#accounting-summary-editor-" + side.entryType + "-modal";
- this.#summaryControl.classList.remove("accounting-not-empty");
- this.#summaryControl.classList.remove("is-invalid");
- this.#summary.dataset.value = "";
- this.#summary.innerText = ""
- this.#summaryError.innerText = ""
- this.#amount.value = "";
- this.#amount.classList.remove("is-invalid");
- this.#amountError.innerText = "";
- }
-
- /**
- * Edits a journal entry.
- *
- * @param entry {JournalEntrySubForm} the journal entry sub-form
- * @param accountCode {string} the account code
- * @param accountText {string} the account text
- * @param summary {string} the summary
- * @param amount {string} the amount
- */
- #onEdit(entry, accountCode, accountText, summary, amount) {
- this.#entry = entry;
- this.#side = entry.side;
- this.entryType = this.#side.entryType;
- this.#element.dataset.entryType = entry.entryType;
- this.#accountControl.dataset.bsTarget = "#accounting-account-selector-" + entry.entryType + "-modal";
- if (accountCode === "") {
- this.#accountControl.classList.remove("accounting-not-empty");
- } else {
- this.#accountControl.classList.add("accounting-not-empty");
- }
- this.#account.innerText = accountText;
- this.#account.dataset.code = accountCode;
- this.#account.dataset.text = accountText;
- this.#summaryControl.dataset.bsTarget = "#accounting-summary-editor-" + entry.entryType + "-modal";
- if (summary === "") {
- this.#summaryControl.classList.remove("accounting-not-empty");
- } else {
- this.#summaryControl.classList.add("accounting-not-empty");
- }
- this.#summary.dataset.value = summary;
- this.#summary.innerText = summary;
- this.#amount.value = amount;
- }
-
- /**
- * The journal entry editor
- * @type {JournalEntryEditor}
- */
- static #editor;
-
- /**
- * Initializes the journal entry editor.
- *
- */
- static initialize() {
- this.#editor = new JournalEntryEditor();
- }
-
- /**
- * Adds a new journal entry.
- *
- * @param side {DebitCreditSideSubForm} the debit or credit side sub-form
- */
- static addNew(side) {
- this.#editor.#onAddNew(side);
- }
-
- /**
- * Edits a journal entry.
- *
- * @param entry {JournalEntrySubForm} the journal entry sub-form
- * @param accountCode {string} the account code
- * @param accountText {string} the account text
- * @param summary {string} the summary
- * @param amount {string} the amount
- */
- static edit(entry, accountCode, accountText, summary, amount) {
- this.#editor.#onEdit(entry, accountCode, accountText, summary, amount);
- }
-
- /**
- * Validates the account when the account is updated from the account selector.
- *
- */
- static validateAccount() {
- this.#editor.#validateAccount();
- }
-}
-
/**
* Escapes the HTML special characters and returns.
*
diff --git a/src/accounting/templates/accounting/transaction/include/form.html b/src/accounting/templates/accounting/transaction/include/form.html
index 40af87b..d4ff572 100644
--- a/src/accounting/templates/accounting/transaction/include/form.html
+++ b/src/accounting/templates/accounting/transaction/include/form.html
@@ -24,6 +24,7 @@ First written: 2023/2/26
{% block accounting_scripts %}
+
{% endblock %}