Added the suggested accounts to the summary helper.

This commit is contained in:
依瑪貓 2023-02-28 19:11:09 +08:00
parent a9c7360020
commit 8b77d9ff93
3 changed files with 163 additions and 12 deletions

View File

@ -78,6 +78,7 @@ class SummaryHelper {
this.#initializeGeneralTripHelper(); this.#initializeGeneralTripHelper();
this.#initializeBusTripHelper(); this.#initializeBusTripHelper();
this.#initializeNumberHelper(); this.#initializeNumberHelper();
this.#initializeSuggestedAccounts();
this.#initializeSubmission(); this.#initializeSubmission();
} }
@ -89,6 +90,7 @@ class SummaryHelper {
#switchToTab(tabId) { #switchToTab(tabId) {
const tabs = Array.from(document.getElementsByClassName(this.#prefix + "-tab")); const tabs = Array.from(document.getElementsByClassName(this.#prefix + "-tab"));
const pages = Array.from(document.getElementsByClassName(this.#prefix + "-page")); const pages = Array.from(document.getElementsByClassName(this.#prefix + "-page"));
const tagButtons = Array.from(document.getElementsByClassName(this.#prefix + "-" + tabId + "-btn-tag"));
tabs.forEach(function (tab) { tabs.forEach(function (tab) {
if (tab.dataset.tabId === tabId) { if (tab.dataset.tabId === tabId) {
tab.classList.add("active"); tab.classList.add("active");
@ -107,6 +109,14 @@ class SummaryHelper {
page.ariaCurrent = "false"; page.ariaCurrent = "false";
} }
}); });
let selectedBtnTag = null;
for (const btnTag of tagButtons) {
if (btnTag.classList.contains("btn-primary")) {
selectedBtnTag = btnTag;
break;
}
}
this.#filterSuggestedAccounts(selectedBtnTag);
} }
/** /**
@ -117,6 +127,7 @@ class SummaryHelper {
const buttons = Array.from(document.getElementsByClassName(this.#prefix + "-general-btn-tag")); const buttons = Array.from(document.getElementsByClassName(this.#prefix + "-general-btn-tag"));
const summary = document.getElementById(this.#prefix + "-summary"); const summary = document.getElementById(this.#prefix + "-summary");
const tag = document.getElementById(this.#prefix + "-general-tag"); const tag = document.getElementById(this.#prefix + "-general-tag");
const helper = this;
const updateSummary = function () { const updateSummary = function () {
const pos = summary.value.indexOf("—"); const pos = summary.value.indexOf("—");
const prefix = tag.value === ""? "": tag.value + "—"; const prefix = tag.value === ""? "": tag.value + "—";
@ -135,19 +146,26 @@ class SummaryHelper {
button.classList.remove("btn-outline-primary"); button.classList.remove("btn-outline-primary");
button.classList.add("btn-primary"); button.classList.add("btn-primary");
tag.value = button.dataset.value; tag.value = button.dataset.value;
helper.#filterSuggestedAccounts(button);
updateSummary(); updateSummary();
}; };
}); });
tag.onchange = function () { tag.onchange = function () {
let isMatched = false;
buttons.forEach(function (button) { buttons.forEach(function (button) {
if (button.dataset.value === tag.value) { if (button.dataset.value === tag.value) {
button.classList.remove("btn-outline-primary"); button.classList.remove("btn-outline-primary");
button.classList.add("btn-primary"); button.classList.add("btn-primary");
helper.#filterSuggestedAccounts(button);
isMatched = true;
} else { } else {
button.classList.remove("btn-primary"); button.classList.remove("btn-primary");
button.classList.add("btn-outline-primary"); button.classList.add("btn-outline-primary");
} }
}); });
if (!isMatched) {
helper.#filterSuggestedAccounts(null);
}
updateSummary(); updateSummary();
}; };
} }
@ -183,19 +201,26 @@ class SummaryHelper {
button.classList.remove("btn-outline-primary"); button.classList.remove("btn-outline-primary");
button.classList.add("btn-primary"); button.classList.add("btn-primary");
tag.value = button.dataset.value; tag.value = button.dataset.value;
helper.#filterSuggestedAccounts(button);
updateSummary(); updateSummary();
}; };
}); });
tag.onchange = function () { tag.onchange = function () {
let isMatched = false;
buttons.forEach(function (button) { buttons.forEach(function (button) {
if (button.dataset.value === tag.value) { if (button.dataset.value === tag.value) {
button.classList.remove("btn-outline-primary"); button.classList.remove("btn-outline-primary");
button.classList.add("btn-primary"); button.classList.add("btn-primary");
helper.#filterSuggestedAccounts(button);
isMatched = true;
} else { } else {
button.classList.remove("btn-primary"); button.classList.remove("btn-primary");
button.classList.add("btn-outline-primary"); button.classList.add("btn-outline-primary");
} }
}); });
if (!isMatched) {
helper.#filterSuggestedAccounts(null)
}
updateSummary(); updateSummary();
helper.#validateGeneralTripTag(); helper.#validateGeneralTripTag();
}; };
@ -244,19 +269,26 @@ class SummaryHelper {
button.classList.remove("btn-outline-primary"); button.classList.remove("btn-outline-primary");
button.classList.add("btn-primary"); button.classList.add("btn-primary");
tag.value = button.dataset.value; tag.value = button.dataset.value;
helper.#filterSuggestedAccounts(button);
updateSummary(); updateSummary();
}; };
}); });
tag.onchange = function () { tag.onchange = function () {
let isMatched = false;
buttons.forEach(function (button) { buttons.forEach(function (button) {
if (button.dataset.value === tag.value) { if (button.dataset.value === tag.value) {
button.classList.remove("btn-outline-primary"); button.classList.remove("btn-outline-primary");
button.classList.add("btn-primary"); button.classList.add("btn-primary");
helper.#filterSuggestedAccounts(button);
isMatched = true;
} else { } else {
button.classList.remove("btn-primary"); button.classList.remove("btn-primary");
button.classList.add("btn-outline-primary"); button.classList.add("btn-outline-primary");
} }
}); });
if (!isMatched) {
helper.#filterSuggestedAccounts(null);
}
updateSummary(); updateSummary();
helper.#validateBusTripTag(); helper.#validateBusTripTag();
}; };
@ -274,6 +306,33 @@ class SummaryHelper {
}; };
} }
/**
* Filters the suggested accounts.
*
* @param btnTag {HTMLButtonElement|null} the tag button
*/
#filterSuggestedAccounts(btnTag) {
const accountButtons = Array.from(document.getElementsByClassName(this.#prefix + "-account"));
if (btnTag === null) {
for (const btnAccount of accountButtons) {
btnAccount.classList.add("d-none");
btnAccount.classList.remove("btn-primary");
btnAccount.classList.add("btn-outline-primary");
this.#selectAccount(null);
}
return;
}
const suggested = JSON.parse(btnTag.dataset.accounts);
for (const btnAccount of accountButtons) {
if (suggested.includes(btnAccount.dataset.code)) {
btnAccount.classList.remove("d-none");
} else {
btnAccount.classList.add("d-none");
}
this.#selectAccount(suggested[0]);
}
}
/** /**
* Initializes the number helper. * Initializes the number helper.
* *
@ -292,6 +351,46 @@ class SummaryHelper {
}; };
} }
/**
* Initializes the suggested accounts.
*
*/
#initializeSuggestedAccounts() {
const accountButtons = Array.from(document.getElementsByClassName(this.#prefix + "-account"));
const helper = this;
accountButtons.forEach(function (btnAccount) {
btnAccount.onclick = function () {
helper.#selectAccount(btnAccount.dataset.code);
};
});
}
/**
* Select a suggested account.
*
* @param selectedCode {string|null} the account code, or null to deselect the account
*/
#selectAccount(selectedCode) {
const form = document.getElementById(this.#prefix);
if (selectedCode === null) {
form.dataset.selectedAccountCode = "";
form.dataset.selectedAccountText = "";
return;
}
const accountButtons = Array.from(document.getElementsByClassName(this.#prefix + "-account"));
accountButtons.forEach(function (btnAccount) {
if (btnAccount.dataset.code === selectedCode) {
btnAccount.classList.remove("btn-outline-primary");
btnAccount.classList.add("btn-primary");
form.dataset.selectedAccountCode = btnAccount.dataset.code;
form.dataset.selectedAccountText = btnAccount.dataset.text;
} else {
btnAccount.classList.remove("btn-primary");
btnAccount.classList.add("btn-outline-primary");
}
})
}
/** /**
* Initializes the summary submission * Initializes the summary submission
* *
@ -512,9 +611,12 @@ class SummaryHelper {
* *
*/ */
#submit() { #submit() {
const form = document.getElementById(this.#prefix);
const summary = document.getElementById(this.#prefix + "-summary"); const summary = document.getElementById(this.#prefix + "-summary");
const formSummaryControl = document.getElementById("accounting-entry-form-summary-control"); const formSummaryControl = document.getElementById("accounting-entry-form-summary-control");
const formSummary = document.getElementById("accounting-entry-form-summary"); const formSummary = document.getElementById("accounting-entry-form-summary");
const formAccountControl = document.getElementById("accounting-entry-form-account-control");
const formAccount = document.getElementById("accounting-entry-form-account");
const helperModal = document.getElementById(this.#prefix + "-modal"); const helperModal = document.getElementById(this.#prefix + "-modal");
const entryModal = document.getElementById("accounting-entry-form-modal"); const entryModal = document.getElementById("accounting-entry-form-modal");
if (summary.value === "") { if (summary.value === "") {
@ -522,6 +624,12 @@ class SummaryHelper {
} else { } else {
formSummaryControl.classList.add("accounting-not-empty"); formSummaryControl.classList.add("accounting-not-empty");
} }
if (form.dataset.selectedAccountCode !== "") {
formAccountControl.classList.add("accounting-not-empty");
formAccount.dataset.code = form.dataset.selectedAccountCode;
formAccount.dataset.text = form.dataset.selectedAccountText;
formAccount.innerText = form.dataset.selectedAccountText;
}
formSummary.dataset.value = summary.value; formSummary.dataset.value = summary.value;
formSummary.innerText = summary.value; formSummary.innerText = summary.value;
bootstrap.Modal.getInstance(helperModal).hide(); bootstrap.Modal.getInstance(helperModal).hide();
@ -573,6 +681,7 @@ class SummaryHelper {
button.classList.remove("btn-primary"); button.classList.remove("btn-primary");
} }
}); });
this.#filterSuggestedAccounts(null);
this.#switchToTab(this.#defaultTabId); this.#switchToTab(this.#defaultTabId);
} }
@ -626,12 +735,13 @@ class SummaryHelper {
if (numberStr !== undefined) { if (numberStr !== undefined) {
number.value = parseInt(numberStr); number.value = parseInt(numberStr);
} }
buttons.forEach(function (button) { for (const button of buttons) {
if (button.dataset.value === tagName) { if (button.dataset.value === tagName) {
button.classList.remove("btn-outline-primary"); button.classList.remove("btn-outline-primary");
button.classList.add("btn-primary"); button.classList.add("btn-primary");
this.#filterSuggestedAccounts(button);
} }
}); }
this.#switchToTab("bus"); this.#switchToTab("bus");
} }
@ -666,12 +776,13 @@ class SummaryHelper {
if (numberStr !== undefined) { if (numberStr !== undefined) {
number.value = parseInt(numberStr); number.value = parseInt(numberStr);
} }
buttons.forEach(function (button) { for (const button of buttons) {
if (button.dataset.value === tagName) { if (button.dataset.value === tagName) {
button.classList.remove("btn-outline-primary"); button.classList.remove("btn-outline-primary");
button.classList.add("btn-primary"); button.classList.add("btn-primary");
this.#filterSuggestedAccounts(button);
} }
}); }
this.#switchToTab("travel"); this.#switchToTab("travel");
} }
@ -689,12 +800,13 @@ class SummaryHelper {
if (numberStr !== undefined) { if (numberStr !== undefined) {
number.value = parseInt(numberStr); number.value = parseInt(numberStr);
} }
buttons.forEach(function (button) { for (const button of buttons) {
if (button.dataset.value === tagName) { if (button.dataset.value === tagName) {
button.classList.remove("btn-outline-primary"); button.classList.remove("btn-outline-primary");
button.classList.add("btn-primary"); button.classList.add("btn-primary");
this.#filterSuggestedAccounts(button);
} }
}); }
this.#switchToTab("general"); this.#switchToTab("general");
} }

View File

@ -19,7 +19,7 @@ entry-form-modal.html: The modal of the summary helper
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-summary-helper-{{ summary_helper.type }}" class="accounting-summary-helper" data-entry-type="{{ summary_helper.type }}" data-default-tab-id="general"> <form id="accounting-summary-helper-{{ summary_helper.type }}" class="accounting-summary-helper" data-entry-type="{{ summary_helper.type }}" data-default-tab-id="general" data-selected-account-code="" data-selected-account-text="">
<div id="accounting-summary-helper-{{ summary_helper.type }}-modal" class="modal fade" tabindex="-1" aria-labelledby="accounting-summary-helper-{{ summary_helper.type }}-modal-label" aria-hidden="true"> <div id="accounting-summary-helper-{{ summary_helper.type }}-modal" class="modal fade" tabindex="-1" aria-labelledby="accounting-summary-helper-{{ summary_helper.type }}-modal-label" aria-hidden="true">
<div class="modal-dialog"> <div class="modal-dialog">
<div class="modal-content"> <div class="modal-content">
@ -70,7 +70,7 @@ First written: 2023/2/28
<div> <div>
{% for tag in summary_helper.general.tags %} {% for tag in summary_helper.general.tags %}
<button class="btn btn-outline-primary accounting-summary-helper-{{ summary_helper.type }}-btn-tag accounting-summary-helper-{{ summary_helper.type }}-general-btn-tag" type="button" tabindex="-1" data-value="{{ tag.name }}"> <button class="btn btn-outline-primary accounting-summary-helper-{{ summary_helper.type }}-btn-tag accounting-summary-helper-{{ summary_helper.type }}-general-btn-tag" type="button" tabindex="-1" data-value="{{ tag.name }}" data-accounts="{{ tag.account_codes|tojson|forceescape }}">
{{ tag }} {{ tag }}
</button> </button>
{% endfor %} {% endfor %}
@ -87,7 +87,7 @@ First written: 2023/2/28
<div> <div>
{% for tag in summary_helper.travel.tags %} {% for tag in summary_helper.travel.tags %}
<button class="btn btn-outline-primary accounting-summary-helper-{{ summary_helper.type }}-btn-tag accounting-summary-helper-{{ summary_helper.type }}-travel-btn-tag" type="button" tabindex="-1" data-value="{{ tag.name }}"> <button class="btn btn-outline-primary accounting-summary-helper-{{ summary_helper.type }}-btn-tag accounting-summary-helper-{{ summary_helper.type }}-travel-btn-tag" type="button" tabindex="-1" data-value="{{ tag.name }}" data-accounts="{{ tag.account_codes|tojson|forceescape }}">
{{ tag }} {{ tag }}
</button> </button>
{% endfor %} {% endfor %}
@ -128,7 +128,7 @@ First written: 2023/2/28
<div> <div>
{% for tag in summary_helper.bus.tags %} {% for tag in summary_helper.bus.tags %}
<button class="btn btn-outline-primary accounting-summary-helper-{{ summary_helper.type }}-btn-tag accounting-summary-helper-{{ summary_helper.type }}-bus-btn-tag" type="button" tabindex="-1" data-value="{{ tag.name }}"> <button class="btn btn-outline-primary accounting-summary-helper-{{ summary_helper.type }}-btn-tag accounting-summary-helper-{{ summary_helper.type }}-bus-btn-tag" type="button" tabindex="-1" data-value="{{ tag.name }}" data-accounts="{{ tag.account_codes|tojson|forceescape }}">
{{ tag }} {{ tag }}
</button> </button>
{% endfor %} {% endfor %}
@ -161,6 +161,15 @@ First written: 2023/2/28
<div id="accounting-summary-helper-{{ summary_helper.type }}-number-error" class="invalid-feedback"></div> <div id="accounting-summary-helper-{{ summary_helper.type }}-number-error" class="invalid-feedback"></div>
</div> </div>
</div> </div>
{# The suggested accounts #}
<div class="mt-3">
{% for account in summary_helper.accounts %}
<button class="btn btn-outline-primary d-none accounting-summary-helper-{{ summary_helper.type }}-account" type="button" role="button" data-code="{{ account.code }}" data-text="{{ account }}">
{{ account }}
</button>
{% endfor %}
</div>
</div> </div>
<div class="modal-footer"> <div class="modal-footer">
<button type="button" class="btn btn-secondary accounting-summary-helper-{{ summary_helper.type }}-close" data-bs-toggle="modal" data-bs-target="">{{ A_("Cancel") }}</button> <button type="button" class="btn btn-secondary accounting-summary-helper-{{ summary_helper.type }}-close" data-bs-toggle="modal" data-bs-target="">{{ A_("Cancel") }}</button>

View File

@ -34,8 +34,10 @@ class SummaryAccount:
:param account: The account. :param account: The account.
:param freq: The frequency of the tag with the account. :param freq: The frequency of the tag with the account.
""" """
self.__account: Account = account self.account: Account = account
"""The account.""" """The account."""
self.id: int = account.id
"""The account ID."""
self.code: str = account.code self.code: str = account.code
"""The account code.""" """The account code."""
self.freq: int = freq self.freq: int = freq
@ -46,7 +48,7 @@ class SummaryAccount:
:return: The string representation of the account. :return: The string representation of the account.
""" """
return str(self.__account) return str(self.account)
def add_freq(self, freq: int) -> None: def add_freq(self, freq: int) -> None:
"""Adds the frequency of an account. """Adds the frequency of an account.
@ -98,6 +100,14 @@ class SummaryTag:
""" """
return sorted(self.__account_dict.values(), key=lambda x: -x.freq) return sorted(self.__account_dict.values(), key=lambda x: -x.freq)
@property
def account_codes(self) -> list[str]:
"""Returns the account codes by the order of their frequencies.
:return: The account codes by the order of their frequencies.
"""
return [x.code for x in self.accounts]
class SummaryType: class SummaryType:
"""A summary type""" """A summary type"""
@ -166,6 +176,26 @@ class SummaryEntryType:
""" """
self.__type_dict[tag_type].add_tag(name, account, freq) self.__type_dict[tag_type].add_tag(name, account, freq)
@property
def accounts(self) -> list[SummaryAccount]:
"""Returns the suggested accounts of all tags in the summary helper in
the entry type, in their frequency order.
:return: The suggested accounts of all tags, in their frequency order.
"""
accounts: dict[int, SummaryAccount] = {}
freq: dict[int, int] = {}
for tag_type in self.__type_dict.values():
for tag in tag_type.tags:
for account in tag.accounts:
accounts[account.id] = account
if account.id not in freq:
freq[account.id] = 0
freq[account.id] \
= freq[account.id] + account.freq
return [accounts[y] for y in sorted(freq.keys(),
key=lambda x: -freq[x])]
class SummaryHelper: class SummaryHelper:
"""The summary helper.""" """The summary helper."""