Compare commits
9 Commits
46c6767a90
...
84bd01087c
| Author | SHA1 | Date | |
|---|---|---|---|
| 84bd01087c | |||
| c10bf81d24 | |||
| acf2a0fa87 | |||
| 090acbd66b | |||
| 220dbaa683 | |||
| 800832d15e | |||
| 454ff8bb5f | |||
| 9b002cd9a9 | |||
| c1d5b46145 |
@@ -48,7 +48,7 @@ class AccountForm {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* The control of the base account
|
* The control of the base account
|
||||||
* @type {HTMLDivElement}
|
* @type {HTMLButtonElement}
|
||||||
*/
|
*/
|
||||||
#baseControl;
|
#baseControl;
|
||||||
|
|
||||||
@@ -60,7 +60,7 @@ class AccountForm {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* The base account
|
* The base account
|
||||||
* @type {HTMLDivElement}
|
* @type {HTMLSpanElement}
|
||||||
*/
|
*/
|
||||||
#base;
|
#base;
|
||||||
|
|
||||||
@@ -225,15 +225,16 @@ class AccountForm {
|
|||||||
/**
|
/**
|
||||||
* The base account selector.
|
* The base account selector.
|
||||||
*
|
*
|
||||||
|
* @extends {BaseCombobox<BaseAccountOption>}
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
class BaseAccountSelector {
|
class BaseAccountSelector extends BaseCombobox {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The account form
|
* The account form
|
||||||
* @type {AccountForm}
|
* @type {AccountForm}
|
||||||
*/
|
*/
|
||||||
form;
|
#form;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The selector modal
|
* The selector modal
|
||||||
@@ -241,12 +242,6 @@ class BaseAccountSelector {
|
|||||||
*/
|
*/
|
||||||
#modal;
|
#modal;
|
||||||
|
|
||||||
/**
|
|
||||||
* The query input
|
|
||||||
* @type {HTMLInputElement}
|
|
||||||
*/
|
|
||||||
#query;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The error message when the query has no result
|
* The error message when the query has no result
|
||||||
* @type {HTMLParagraphElement}
|
* @type {HTMLParagraphElement}
|
||||||
@@ -259,12 +254,6 @@ class BaseAccountSelector {
|
|||||||
*/
|
*/
|
||||||
#optionList;
|
#optionList;
|
||||||
|
|
||||||
/**
|
|
||||||
* The options
|
|
||||||
* @type {BaseAccountOption[]}
|
|
||||||
*/
|
|
||||||
#options;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The button to clear the base account value
|
* The button to clear the base account value
|
||||||
* @type {HTMLButtonElement}
|
* @type {HTMLButtonElement}
|
||||||
@@ -277,29 +266,32 @@ class BaseAccountSelector {
|
|||||||
* @param form {AccountForm} the form
|
* @param form {AccountForm} the form
|
||||||
*/
|
*/
|
||||||
constructor(form) {
|
constructor(form) {
|
||||||
this.form = form;
|
|
||||||
const prefix = "accounting-base-selector";
|
const prefix = "accounting-base-selector";
|
||||||
|
const query = document.getElementById(`${prefix}-query`);
|
||||||
|
const options = Array.from(document.getElementsByClassName(`${prefix}-option`)).map((element) => new BaseAccountOption(element, form.saveBaseAccount.bind(form)));
|
||||||
|
super(query, options);
|
||||||
|
this.#form = form;
|
||||||
this.#modal = document.getElementById(`${prefix}-modal`);
|
this.#modal = document.getElementById(`${prefix}-modal`);
|
||||||
this.#query = document.getElementById(`${prefix}-query`);
|
this.#modal.addEventListener("hidden.bs.modal", () => this.#form.onBaseAccountSelectorClosed());
|
||||||
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`);
|
||||||
this.#options = Array.from(document.getElementsByClassName(`${prefix}-option`)).map((element) => new BaseAccountOption(this, element));
|
|
||||||
this.#clearButton = document.getElementById(`${prefix}-clear`);
|
|
||||||
|
|
||||||
this.#modal.addEventListener("hidden.bs.modal", () => this.form.onBaseAccountSelectorClosed());
|
this.#clearButton = document.getElementById(`${prefix}-clear`);
|
||||||
this.#query.oninput = () => this.#filterOptions();
|
this.#clearButton.onclick = () => this.#form.clearBaseAccount();
|
||||||
this.#clearButton.onclick = () => this.form.clearBaseAccount();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Filters the options.
|
* Filters the options.
|
||||||
*
|
*
|
||||||
|
* @override
|
||||||
*/
|
*/
|
||||||
#filterOptions() {
|
filterOptions() {
|
||||||
|
this.shownOptions = [];
|
||||||
let isAnyMatched = false;
|
let isAnyMatched = false;
|
||||||
for (const option of this.#options) {
|
for (const option of this.options) {
|
||||||
if (option.isMatched(this.#query.value)) {
|
if (option.isMatched(this.query.value)) {
|
||||||
option.setShown(true);
|
option.setShown(true);
|
||||||
|
this.shownOptions.push(option);
|
||||||
isAnyMatched = true;
|
isAnyMatched = true;
|
||||||
} else {
|
} else {
|
||||||
option.setShown(false);
|
option.setShown(false);
|
||||||
@@ -319,12 +311,11 @@ class BaseAccountSelector {
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
onOpen() {
|
onOpen() {
|
||||||
this.#query.value = "";
|
this.query.value = "";
|
||||||
this.#filterOptions();
|
this.filterOptions();
|
||||||
for (const option of this.#options) {
|
this.query.removeAttribute("aria-activedescendant");
|
||||||
option.setActive(option.code === this.form.baseCode);
|
this.selectOption(this.shownOptions.find((option) => option.code === this.#form.baseCode));
|
||||||
}
|
if (this.#form.baseCode === null) {
|
||||||
if (this.form.baseCode === 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;
|
||||||
@@ -341,13 +332,7 @@ class BaseAccountSelector {
|
|||||||
*
|
*
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
class BaseAccountOption {
|
class BaseAccountOption extends BaseOption {
|
||||||
|
|
||||||
/**
|
|
||||||
* The element
|
|
||||||
* @type {HTMLLIElement}
|
|
||||||
*/
|
|
||||||
#element;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The account code
|
* The account code
|
||||||
@@ -370,16 +355,16 @@ class BaseAccountOption {
|
|||||||
/**
|
/**
|
||||||
* Constructs the account in the base account selector.
|
* Constructs the account in the base account selector.
|
||||||
*
|
*
|
||||||
* @param selector {BaseAccountSelector} the base account selector
|
|
||||||
* @param element {HTMLLIElement} the element
|
* @param element {HTMLLIElement} the element
|
||||||
|
* @param save {function(BaseAccountOption): void} the callback to save the option
|
||||||
*/
|
*/
|
||||||
constructor(selector, element) {
|
constructor(element, save) {
|
||||||
this.#element = element;
|
super(element);
|
||||||
this.code = element.dataset.code;
|
this.code = element.dataset.code;
|
||||||
this.text = element.dataset.text;
|
this.text = element.dataset.text;
|
||||||
this.#queryValues = JSON.parse(element.dataset.queryValues);
|
this.#queryValues = JSON.parse(element.dataset.queryValues);
|
||||||
|
|
||||||
this.#element.onclick = () => selector.form.saveBaseAccount(this);
|
element.onclick = () => save(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -399,30 +384,4 @@ class BaseAccountOption {
|
|||||||
}
|
}
|
||||||
return false;
|
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");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,237 @@
|
|||||||
|
/* The Mia! Accounting Project
|
||||||
|
* base-combobox.js: The JavaScript for the base abstract combobox
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Copyright (c) 2026 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: 2026/4/16
|
||||||
|
*/
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The base abstract combobox.
|
||||||
|
*
|
||||||
|
* @abstract
|
||||||
|
* @template {BaseOption} T
|
||||||
|
*/
|
||||||
|
class BaseCombobox {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The query input
|
||||||
|
* @type {HTMLInputElement}
|
||||||
|
*/
|
||||||
|
query;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The options
|
||||||
|
* @type {T[]}
|
||||||
|
*/
|
||||||
|
options;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The options that are shown
|
||||||
|
* @type {T[]}
|
||||||
|
*/
|
||||||
|
shownOptions;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a base abstract combobox.
|
||||||
|
*
|
||||||
|
* @param query {HTMLInputElement} the query input
|
||||||
|
* @param options {T[]} the options
|
||||||
|
*/
|
||||||
|
constructor(query, options) {
|
||||||
|
this.query = query;
|
||||||
|
this.query.oninput = () => this.filterOptions();
|
||||||
|
this.query.onkeydown = this.onQueryKeyDown.bind(this);
|
||||||
|
this.options = options;
|
||||||
|
this.shownOptions = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Actions when keys are pressed on the query input.
|
||||||
|
*
|
||||||
|
* @param event {KeyboardEvent} the key event
|
||||||
|
*/
|
||||||
|
onQueryKeyDown(event) {
|
||||||
|
if (this.shownOptions.length === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const currentID = this.query.getAttribute("aria-activedescendant");
|
||||||
|
const currentIndex = this.shownOptions.findIndex((option) => option.elementID === currentID);
|
||||||
|
|
||||||
|
let newIndex;
|
||||||
|
switch (event.key) {
|
||||||
|
case "ArrowUp":
|
||||||
|
if (currentIndex === -1) {
|
||||||
|
newIndex = this.shownOptions.length - 1;
|
||||||
|
} else {
|
||||||
|
newIndex = (currentIndex - 1 + this.shownOptions.length) % this.shownOptions.length;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "ArrowDown":
|
||||||
|
if (currentIndex === -1) {
|
||||||
|
newIndex = 0;
|
||||||
|
} else {
|
||||||
|
newIndex = (currentIndex + 1) % this.shownOptions.length;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "Home":
|
||||||
|
if (this.query.value !== "") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
newIndex = 0;
|
||||||
|
break;
|
||||||
|
case "End":
|
||||||
|
if (this.query.value !== "") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
newIndex = this.shownOptions.length - 1;
|
||||||
|
break;
|
||||||
|
case "PageUp":
|
||||||
|
if (currentIndex === -1) {
|
||||||
|
newIndex = this.shownOptions.length - 1;
|
||||||
|
} else {
|
||||||
|
newIndex = Math.max(currentIndex - 10, 0);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "PageDown":
|
||||||
|
if (currentIndex === -1) {
|
||||||
|
newIndex = 0;
|
||||||
|
} else {
|
||||||
|
newIndex = Math.min(currentIndex + 10, this.shownOptions.length - 1);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "Enter":
|
||||||
|
event.preventDefault();
|
||||||
|
if (currentIndex !== -1) {
|
||||||
|
this.shownOptions[currentIndex].click();
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
case "Escape":
|
||||||
|
if (this.query.value !== "") {
|
||||||
|
event.preventDefault();
|
||||||
|
event.stopPropagation();
|
||||||
|
this.query.value = "";
|
||||||
|
this.filterOptions();
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
default:
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
event.preventDefault();
|
||||||
|
this.selectOption(this.shownOptions[newIndex]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Filters the options.
|
||||||
|
*
|
||||||
|
* @abstract
|
||||||
|
*/
|
||||||
|
filterOptions() {
|
||||||
|
throw new Error("Method not implemented");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Selects an option.
|
||||||
|
*
|
||||||
|
* @param option {T|undefined} the option.
|
||||||
|
*/
|
||||||
|
selectOption(option) {
|
||||||
|
this.options.forEach((opt) => opt.setActive(false));
|
||||||
|
if (option === undefined) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
option.setActive(true);
|
||||||
|
this.query.setAttribute("aria-activedescendant", option.elementID);
|
||||||
|
option.scrollIntoView();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The base abstract option
|
||||||
|
*
|
||||||
|
* @abstract
|
||||||
|
*/
|
||||||
|
class BaseOption {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The element
|
||||||
|
* @type {HTMLLIElement}
|
||||||
|
*/
|
||||||
|
#element;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The element ID
|
||||||
|
* @type {string}
|
||||||
|
*/
|
||||||
|
elementID;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs the base abstract option.
|
||||||
|
*
|
||||||
|
* @param element {HTMLLIElement} the element
|
||||||
|
*/
|
||||||
|
constructor(element) {
|
||||||
|
this.#element = element;
|
||||||
|
this.elementID = element.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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");
|
||||||
|
this.#element.ariaSelected = "true";
|
||||||
|
} else {
|
||||||
|
this.#element.classList.remove("active");
|
||||||
|
this.#element.ariaSelected = "false";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clicks the option.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
click() {
|
||||||
|
this.#element.click();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scrolls the option into view.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
scrollIntoView() {
|
||||||
|
this.#element.scrollIntoView({block: "nearest"});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,177 @@
|
|||||||
|
/* The Mia! Accounting Project
|
||||||
|
* base-tablist.js: The JavaScript for base abstract tablist
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Copyright (c) 2026 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: 2026/4/16
|
||||||
|
*/
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The base abstract tablist.
|
||||||
|
*
|
||||||
|
* @abstract
|
||||||
|
* @template {BaseTab} T
|
||||||
|
*/
|
||||||
|
class BaseTablist {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The tabs.
|
||||||
|
* @type {T[]}
|
||||||
|
*/
|
||||||
|
tabs;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The current tab.
|
||||||
|
* @type {T}
|
||||||
|
*/
|
||||||
|
currentTab;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a new base abstract tablist.
|
||||||
|
*
|
||||||
|
* @param tablist {HTMLUListElement} the tab list
|
||||||
|
*/
|
||||||
|
constructor(tablist) {
|
||||||
|
tablist.onkeydown = this.onTabKeyDown.bind(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Actions when keys are pressed on the tabs.
|
||||||
|
*
|
||||||
|
* @param event {KeyboardEvent} the key event
|
||||||
|
*/
|
||||||
|
onTabKeyDown(event) {
|
||||||
|
const currentIndex = this.tabs.indexOf(this.currentTab);
|
||||||
|
if (currentIndex === -1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let newIndex = currentIndex;
|
||||||
|
switch (event.key) {
|
||||||
|
case "ArrowRight":
|
||||||
|
newIndex = (newIndex + 1) % this.tabs.length;
|
||||||
|
break;
|
||||||
|
case "ArrowLeft":
|
||||||
|
newIndex = (newIndex - 1 + this.tabs.length) % this.tabs.length;
|
||||||
|
break;
|
||||||
|
case "Home":
|
||||||
|
newIndex = 0;
|
||||||
|
break;
|
||||||
|
case "End":
|
||||||
|
newIndex = this.tabs.length - 1;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
event.preventDefault();
|
||||||
|
this.tabs[newIndex].focus();
|
||||||
|
this.onTabFocus(this.tabs[newIndex]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Actions when a tab is focused.
|
||||||
|
*
|
||||||
|
* @param tab {T} the tab
|
||||||
|
*/
|
||||||
|
onTabFocus(tab) { /* Do nothing */ }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Switches to a tab.
|
||||||
|
*
|
||||||
|
* @param tab {T} the tab
|
||||||
|
*/
|
||||||
|
switchTo(tab) {
|
||||||
|
this.tabs.forEach(t => t.setActive(t === tab));
|
||||||
|
this.currentTab = tab;
|
||||||
|
this.currentTab.onActivated();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The base abstract tab.
|
||||||
|
*
|
||||||
|
* @abstract
|
||||||
|
*/
|
||||||
|
class BaseTab {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The tab element.
|
||||||
|
* @type {HTMLButtonElement}
|
||||||
|
*/
|
||||||
|
#tab;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The panel element.
|
||||||
|
* @type {HTMLDivElement}
|
||||||
|
*/
|
||||||
|
#panel;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a new base abstract tab.
|
||||||
|
*
|
||||||
|
* @param tab {HTMLButtonElement} The tab element.
|
||||||
|
* @param panel {HTMLDivElement} The panel element.
|
||||||
|
* @param switchTo {function(BaseTab): void} The function to switch to the tab.
|
||||||
|
*/
|
||||||
|
constructor(tab, panel, switchTo) {
|
||||||
|
this.#tab = tab;
|
||||||
|
this.#panel = panel;
|
||||||
|
this.#tab.onclick = () => switchTo(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the active state of the tab.
|
||||||
|
*
|
||||||
|
* @param isActive {boolean} true if the tab is active, false otherwise
|
||||||
|
*/
|
||||||
|
setActive(isActive) {
|
||||||
|
if (isActive) {
|
||||||
|
this.#tab.classList.add("active");
|
||||||
|
this.#tab.tabIndex = 0;
|
||||||
|
this.#tab.ariaSelected = "true";
|
||||||
|
this.#panel.classList.remove("d-none");
|
||||||
|
} else {
|
||||||
|
this.#tab.classList.remove("active");
|
||||||
|
this.#tab.tabIndex = -1;
|
||||||
|
this.#tab.ariaSelected = "false";
|
||||||
|
this.#panel.classList.add("d-none");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether the tab is active.
|
||||||
|
*
|
||||||
|
* @returns {boolean} true if the tab is active, false otherwise
|
||||||
|
*/
|
||||||
|
isActive() {
|
||||||
|
return this.#tab.classList.contains("active");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Actions when the tab is activated.
|
||||||
|
*/
|
||||||
|
onActivated() { /* Do nothing */ }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Focuses the tab.
|
||||||
|
*/
|
||||||
|
focus() {
|
||||||
|
this.#tab.focus();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -25,8 +25,9 @@
|
|||||||
/**
|
/**
|
||||||
* A description editor.
|
* A description editor.
|
||||||
*
|
*
|
||||||
|
* @extends {BaseTablist<BaseDescriptionEditorTab>}
|
||||||
*/
|
*/
|
||||||
class DescriptionEditor {
|
class DescriptionEditor extends BaseTablist {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The line item editor
|
* The line item editor
|
||||||
@@ -58,12 +59,6 @@ class DescriptionEditor {
|
|||||||
*/
|
*/
|
||||||
debitCredit;
|
debitCredit;
|
||||||
|
|
||||||
/**
|
|
||||||
* The current tab
|
|
||||||
* @type {DescriptionEditorTabPlane}
|
|
||||||
*/
|
|
||||||
currentTab;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The description input
|
* The description input
|
||||||
* @type {HTMLInputElement}
|
* @type {HTMLInputElement}
|
||||||
@@ -125,10 +120,10 @@ class DescriptionEditor {
|
|||||||
selectedAccount = null;
|
selectedAccount = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The tab planes
|
* The tabs by their ID.
|
||||||
* @type {{general: DescriptionEditorGeneralTagTab, travel: DescriptionEditorGeneralTripTab, bus: DescriptionEditorBusTripTab, recurring: DescriptionEditorRecurringTab, annotation: DescriptionEditorAnnotationTab}}
|
* @type {DescriptionEditorTabFactory}
|
||||||
*/
|
*/
|
||||||
tabPlanes = {};
|
#tabsByID;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs a description editor.
|
* Constructs a description editor.
|
||||||
@@ -137,31 +132,38 @@ class DescriptionEditor {
|
|||||||
* @param debitCredit {string} either "debit" or "credit"
|
* @param debitCredit {string} either "debit" or "credit"
|
||||||
*/
|
*/
|
||||||
constructor(lineItemEditor, debitCredit) {
|
constructor(lineItemEditor, debitCredit) {
|
||||||
|
const prefix = `accounting-description-editor-${debitCredit}`;
|
||||||
|
super(document.getElementById(`${prefix}-tab-list`));
|
||||||
|
this.prefix = prefix;
|
||||||
this.lineItemEditor = lineItemEditor;
|
this.lineItemEditor = lineItemEditor;
|
||||||
this.debitCredit = debitCredit;
|
this.debitCredit = debitCredit;
|
||||||
this.prefix = `accounting-description-editor-${debitCredit}`;
|
this.#form = document.getElementById(prefix);
|
||||||
this.#form = document.getElementById(this.prefix);
|
this.#modal = document.getElementById(`${prefix}-modal`);
|
||||||
this.#modal = document.getElementById(`${this.prefix}-modal`);
|
this.#descriptionInput = document.getElementById(`${prefix}-description`);
|
||||||
this.#descriptionInput = document.getElementById(`${this.prefix}-description`);
|
this.#offsetButton = document.getElementById(`${prefix}-offset`);
|
||||||
this.#offsetButton = document.getElementById(`${this.prefix}-offset`);
|
this.number = document.getElementById(`${prefix}-annotation-number`);
|
||||||
this.number = document.getElementById(`${this.prefix}-annotation-number`);
|
this.note = document.getElementById(`${prefix}-annotation-note`);
|
||||||
this.note = document.getElementById(`${this.prefix}-annotation-note`);
|
this.#confirmedAccountPlaceholder = new DescriptionEditorConfirmedAccount(this, document.getElementById(`${prefix}-account-confirmed`));
|
||||||
this.#confirmedAccountPlaceholder = new DescriptionEditorConfirmedAccount(this, document.getElementById(`${this.prefix}-account-confirmed`));
|
this.#allSuggestedAccounts = Array.from(document.getElementsByClassName(`${prefix}-account`)).map((button) => new DescriptionEditorSuggestedAccount(this, button));
|
||||||
this.#allSuggestedAccounts = Array.from(document.getElementsByClassName(`${this.prefix}-account`)).map((button) => new DescriptionEditorSuggestedAccount(this, button));
|
|
||||||
|
|
||||||
for (const cls of [DescriptionEditorGeneralTagTab, DescriptionEditorGeneralTripTab, DescriptionEditorBusTripTab, DescriptionEditorRecurringTab, DescriptionEditorAnnotationTab]) {
|
this.#tabsByID = new DescriptionEditorTabFactory(this);
|
||||||
const tab = new cls(this);
|
this.tabs = [this.#tabsByID.general, this.#tabsByID.travel, this.#tabsByID.bus, this.#tabsByID.recurring, this.#tabsByID.annotation];
|
||||||
this.tabPlanes[tab.tabId()] = tab;
|
this.currentTab = this.tabs[0];
|
||||||
}
|
|
||||||
this.currentTab = this.tabPlanes.general;
|
|
||||||
this.#descriptionInput.onchange = () => this.#onDescriptionChange();
|
this.#descriptionInput.onchange = () => this.#onDescriptionChange();
|
||||||
this.#offsetButton.onclick = () => this.lineItemEditor.originalLineItemSelector.onOpen();
|
this.#offsetButton.onclick = () => this.lineItemEditor.originalLineItemSelector.onOpen(this.#modal.id);
|
||||||
this.#form.onsubmit = () => {
|
this.#form.onsubmit = () => {
|
||||||
if (this.currentTab.validate()) {
|
if (this.currentTab.validate()) {
|
||||||
this.#submit();
|
this.#submit();
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const closeButton = document.getElementById(`${prefix}-close`);
|
||||||
|
this.#modal.onkeydown = (event) => {
|
||||||
|
if (event.key === "Escape") {
|
||||||
|
closeButton.click();
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -199,26 +201,26 @@ class DescriptionEditor {
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
#onDescriptionChange() {
|
#onDescriptionChange() {
|
||||||
this.#resetTabPlanes();
|
this.#resetTabs();
|
||||||
this.selectedAccount = null;
|
this.selectedAccount = null;
|
||||||
this.description = this.description.trim();
|
this.description = this.description.trim();
|
||||||
for (const tabPlane of [this.tabPlanes.recurring, this.tabPlanes.bus, this.tabPlanes.travel, this.tabPlanes.general]) {
|
for (const tab of [this.#tabsByID.recurring, this.#tabsByID.bus, this.#tabsByID.travel, this.#tabsByID.general]) {
|
||||||
if (tabPlane.populate()) {
|
if (tab.populate()) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.tabPlanes.annotation.populate();
|
this.#tabsByID.annotation.populate();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resets the tab planes.
|
* Resets the tabs.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
#resetTabPlanes() {
|
#resetTabs() {
|
||||||
for (const tabPlane of Object.values(this.tabPlanes)) {
|
for (const tab of this.tabs) {
|
||||||
tabPlane.reset();
|
tab.reset();
|
||||||
}
|
}
|
||||||
this.tabPlanes.general.switchToMe();
|
this.switchTo(this.tabs[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -463,12 +465,63 @@ class DescriptionEditorConfirmedAccount extends DescriptionEditorAccount {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A tab plane.
|
* The tab factory.
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
class DescriptionEditorTabFactory {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The general tag tab
|
||||||
|
* @type {GeneralTagTab}
|
||||||
|
*/
|
||||||
|
general;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The general trip tab
|
||||||
|
* @type {GeneralTripTab}
|
||||||
|
*/
|
||||||
|
travel;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The bus trip tab
|
||||||
|
* @type {BusTripTab}
|
||||||
|
*/
|
||||||
|
bus;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The recurring transactions tab
|
||||||
|
* @type {RecurringTab}
|
||||||
|
*/
|
||||||
|
recurring;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The annotation tab
|
||||||
|
* @type {AnnotationTab}
|
||||||
|
*/
|
||||||
|
annotation;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs the tab factory
|
||||||
|
*
|
||||||
|
* @param editor {DescriptionEditor} the parent description editor
|
||||||
|
*/
|
||||||
|
constructor(editor) {
|
||||||
|
this.general = new GeneralTagTab(editor);
|
||||||
|
this.travel = new GeneralTripTab(editor);
|
||||||
|
this.bus = new BusTripTab(editor);
|
||||||
|
this.recurring = new RecurringTab(editor);
|
||||||
|
this.annotation = new AnnotationTab(editor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The base abstract tab in the description editor.
|
||||||
*
|
*
|
||||||
* @abstract
|
* @abstract
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
class DescriptionEditorTabPlane {
|
class BaseDescriptionEditorTab extends BaseTab {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The parent description editor
|
* The parent description editor
|
||||||
@@ -483,47 +536,29 @@ class DescriptionEditorTabPlane {
|
|||||||
prefix;
|
prefix;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The tab
|
* Constructs a base abstract tab in the description editor.
|
||||||
* @type {HTMLSpanElement}
|
|
||||||
*/
|
|
||||||
#tab;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The page
|
|
||||||
* @type {HTMLDivElement}
|
|
||||||
*/
|
|
||||||
#page;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructs a tab plane.
|
|
||||||
*
|
*
|
||||||
|
* @param tabID {string} the tab ID
|
||||||
* @param editor {DescriptionEditor} the parent description editor
|
* @param editor {DescriptionEditor} the parent description editor
|
||||||
*/
|
*/
|
||||||
constructor(editor) {
|
constructor(tabID, editor) {
|
||||||
|
const prefix = `${editor.prefix}-${tabID}`;
|
||||||
|
const tab = document.getElementById(`${prefix}-tab`);
|
||||||
|
const panel = document.getElementById(`${prefix}-panel`);
|
||||||
|
super(tab, panel, editor.switchTo.bind(editor));
|
||||||
this.editor = editor;
|
this.editor = editor;
|
||||||
this.prefix = `${this.editor.prefix}-${this.tabId()}`;
|
this.prefix = prefix;
|
||||||
this.#tab = document.getElementById(`${this.prefix}-tab`);
|
|
||||||
this.#page = document.getElementById(`${this.prefix}-page`);
|
|
||||||
this.#tab.onclick = () => this.switchToMe();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The tab ID
|
* Resets the tab panel input.
|
||||||
*
|
|
||||||
* @return {string}
|
|
||||||
* @abstract
|
|
||||||
*/
|
|
||||||
tabId() { throw new Error("Method not implemented.") };
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Resets the tab plane input.
|
|
||||||
*
|
*
|
||||||
* @abstract
|
* @abstract
|
||||||
*/
|
*/
|
||||||
reset() { throw new Error("Method not implemented."); }
|
reset() { throw new Error("Method not implemented."); }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Populates the tab plane with the description input.
|
* Populates the tab panel with the description input.
|
||||||
*
|
*
|
||||||
* @return {boolean} true if the description input matches this tab, or false otherwise
|
* @return {boolean} true if the description input matches this tab, or false otherwise
|
||||||
* @abstract
|
* @abstract
|
||||||
@@ -531,39 +566,21 @@ class DescriptionEditorTabPlane {
|
|||||||
populate() { throw new Error("Method not implemented."); }
|
populate() { throw new Error("Method not implemented."); }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Validates the input in the tab plane.
|
* Validates the input in the tab panel.
|
||||||
*
|
*
|
||||||
* @return {boolean} true if valid, or false otherwise
|
* @return {boolean} true if valid, or false otherwise
|
||||||
* @abstract
|
* @abstract
|
||||||
*/
|
*/
|
||||||
validate() { throw new Error("Method not implemented."); }
|
validate() { throw new Error("Method not implemented."); }
|
||||||
|
|
||||||
/**
|
|
||||||
* Switches to the tab plane.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
switchToMe() {
|
|
||||||
for (const tabPlane of Object.values(this.editor.tabPlanes)) {
|
|
||||||
tabPlane.#tab.classList.remove("active")
|
|
||||||
tabPlane.#tab.ariaCurrent = "false";
|
|
||||||
tabPlane.#page.classList.add("d-none");
|
|
||||||
tabPlane.#page.ariaCurrent = "false";
|
|
||||||
}
|
|
||||||
this.#tab.classList.add("active");
|
|
||||||
this.#tab.ariaCurrent = "page";
|
|
||||||
this.#page.classList.remove("d-none");
|
|
||||||
this.#page.ariaCurrent = "page";
|
|
||||||
this.editor.currentTab = this;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A tag plane with selectable tags.
|
* The base abstract tab with selectable tags.
|
||||||
*
|
*
|
||||||
* @abstract
|
* @abstract
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
class DescriptionEditorTagTabPlane extends DescriptionEditorTabPlane {
|
class BaseTagTab extends BaseDescriptionEditorTab {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The tag input
|
* The tag input
|
||||||
@@ -581,20 +598,21 @@ class DescriptionEditorTagTabPlane extends DescriptionEditorTabPlane {
|
|||||||
* The tag buttons
|
* The tag buttons
|
||||||
* @type {HTMLButtonElement[]}
|
* @type {HTMLButtonElement[]}
|
||||||
*/
|
*/
|
||||||
tagButtons;
|
#tagButtons;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs a tab plane.
|
* Constructs a base abstract tab with selectable tags.
|
||||||
*
|
*
|
||||||
|
* @param tabID {string} the tab ID
|
||||||
* @param editor {DescriptionEditor} the parent description editor
|
* @param editor {DescriptionEditor} the parent description editor
|
||||||
* @override
|
* @override
|
||||||
*/
|
*/
|
||||||
constructor(editor) {
|
constructor(tabID, editor) {
|
||||||
super(editor);
|
super(tabID, editor);
|
||||||
this.tag = document.getElementById(`${this.prefix}-tag`);
|
this.tag = document.getElementById(`${this.prefix}-tag`);
|
||||||
this.tagError = document.getElementById(`${this.prefix}-tag-error`);
|
this.tagError = document.getElementById(`${this.prefix}-tag-error`);
|
||||||
// noinspection JSValidateTypes
|
// noinspection JSValidateTypes
|
||||||
this.tagButtons = Array.from(document.getElementsByClassName(`${this.prefix}-btn-tag`));
|
this.#tagButtons = Array.from(document.getElementsByClassName(`${this.prefix}-btn-tag`));
|
||||||
this.initializeTagButtons();
|
this.initializeTagButtons();
|
||||||
this.tag.onchange = () => {
|
this.tag.onchange = () => {
|
||||||
this.onTagChange();
|
this.onTagChange();
|
||||||
@@ -609,7 +627,7 @@ class DescriptionEditorTagTabPlane extends DescriptionEditorTabPlane {
|
|||||||
onTagChange() {
|
onTagChange() {
|
||||||
this.tag.value = this.tag.value.trim();
|
this.tag.value = this.tag.value.trim();
|
||||||
let isMatched = false;
|
let isMatched = false;
|
||||||
for (const tagButton of this.tagButtons) {
|
for (const tagButton of this.#tagButtons) {
|
||||||
if (tagButton.dataset.value === this.tag.value) {
|
if (tagButton.dataset.value === this.tag.value) {
|
||||||
tagButton.classList.remove("btn-outline-primary");
|
tagButton.classList.remove("btn-outline-primary");
|
||||||
tagButton.classList.add("btn-primary");
|
tagButton.classList.add("btn-primary");
|
||||||
@@ -627,19 +645,18 @@ class DescriptionEditorTagTabPlane extends DescriptionEditorTabPlane {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates the description according to the input in the tab plane.
|
* Updates the description according to the input in the tab panel.
|
||||||
*
|
*
|
||||||
* @abstract
|
* @abstract
|
||||||
*/
|
*/
|
||||||
updateDescription() { throw new Error("Method not implemented."); }
|
updateDescription() { throw new Error("Method not implemented."); }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Switches to the tab plane.
|
* @inheritDoc
|
||||||
*
|
* @override
|
||||||
*/
|
*/
|
||||||
switchToMe() {
|
onActivated() {
|
||||||
super.switchToMe();
|
for (const tagButton of this.#tagButtons) {
|
||||||
for (const tagButton of this.tagButtons) {
|
|
||||||
if (tagButton.classList.contains("btn-primary")) {
|
if (tagButton.classList.contains("btn-primary")) {
|
||||||
this.editor.updateCurrentSuggestedAccounts(tagButton);
|
this.editor.updateCurrentSuggestedAccounts(tagButton);
|
||||||
return;
|
return;
|
||||||
@@ -653,9 +670,9 @@ class DescriptionEditorTagTabPlane extends DescriptionEditorTabPlane {
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
initializeTagButtons() {
|
initializeTagButtons() {
|
||||||
for (const tagButton of this.tagButtons) {
|
for (const tagButton of this.#tagButtons) {
|
||||||
tagButton.onclick = () => {
|
tagButton.onclick = () => {
|
||||||
for (const otherButton of this.tagButtons) {
|
for (const otherButton of this.#tagButtons) {
|
||||||
otherButton.classList.remove("btn-primary");
|
otherButton.classList.remove("btn-primary");
|
||||||
otherButton.classList.add("btn-outline-primary");
|
otherButton.classList.add("btn-outline-primary");
|
||||||
}
|
}
|
||||||
@@ -701,7 +718,7 @@ class DescriptionEditorTagTabPlane extends DescriptionEditorTabPlane {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resets the tab plane input.
|
* Resets the tab panel input.
|
||||||
*
|
*
|
||||||
* @override
|
* @override
|
||||||
*/
|
*/
|
||||||
@@ -709,7 +726,7 @@ class DescriptionEditorTagTabPlane extends DescriptionEditorTabPlane {
|
|||||||
this.tag.value = "";
|
this.tag.value = "";
|
||||||
this.tag.classList.remove("is-invalid");
|
this.tag.classList.remove("is-invalid");
|
||||||
this.tagError.innerText = "";
|
this.tagError.innerText = "";
|
||||||
for (const tagButton of this.tagButtons) {
|
for (const tagButton of this.#tagButtons) {
|
||||||
tagButton.classList.remove("btn-primary");
|
tagButton.classList.remove("btn-primary");
|
||||||
tagButton.classList.add("btn-outline-primary");
|
tagButton.classList.add("btn-outline-primary");
|
||||||
}
|
}
|
||||||
@@ -717,24 +734,24 @@ class DescriptionEditorTagTabPlane extends DescriptionEditorTabPlane {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The general tag tab plane.
|
* The general tag tab.
|
||||||
*
|
*
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
class DescriptionEditorGeneralTagTab extends DescriptionEditorTagTabPlane {
|
class GeneralTagTab extends BaseTagTab {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The tab ID
|
* Constructs a general tag tab.
|
||||||
*
|
*
|
||||||
* @return {string}
|
* @param editor {DescriptionEditor} the parent description editor
|
||||||
* @abstract
|
* @override
|
||||||
*/
|
*/
|
||||||
tabId() {
|
constructor(editor) {
|
||||||
return "general";
|
super("general", editor);
|
||||||
};
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates the description according to the input in the tab plane.
|
* Updates the description according to the input in the tab panel.
|
||||||
*
|
*
|
||||||
* @override
|
* @override
|
||||||
*/
|
*/
|
||||||
@@ -749,7 +766,7 @@ class DescriptionEditorGeneralTagTab extends DescriptionEditorTagTabPlane {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Populates the tab plane with the description input.
|
* Populates the tab panel with the description input.
|
||||||
*
|
*
|
||||||
* @return {boolean} true if the description input matches this tab, or false otherwise
|
* @return {boolean} true if the description input matches this tab, or false otherwise
|
||||||
* @override
|
* @override
|
||||||
@@ -763,12 +780,12 @@ class DescriptionEditorGeneralTagTab extends DescriptionEditorTagTabPlane {
|
|||||||
this.tag.value = found[1];
|
this.tag.value = found[1];
|
||||||
this.onTagChange();
|
this.onTagChange();
|
||||||
}
|
}
|
||||||
this.switchToMe();
|
this.editor.switchTo(this);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Validates the input in the tab plane.
|
* Validates the input in the tab panel.
|
||||||
*
|
*
|
||||||
* @return {boolean} true if valid, or false otherwise
|
* @return {boolean} true if valid, or false otherwise
|
||||||
*/
|
*/
|
||||||
@@ -778,11 +795,11 @@ class DescriptionEditorGeneralTagTab extends DescriptionEditorTagTabPlane {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The general trip tab plane.
|
* The general trip tab.
|
||||||
*
|
*
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
class DescriptionEditorGeneralTripTab extends DescriptionEditorTagTabPlane {
|
class GeneralTripTab extends BaseTagTab {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The origin
|
* The origin
|
||||||
@@ -815,13 +832,13 @@ class DescriptionEditorGeneralTripTab extends DescriptionEditorTagTabPlane {
|
|||||||
#directionButtons;
|
#directionButtons;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs a tab plane.
|
* Constructs a general trip tab.
|
||||||
*
|
*
|
||||||
* @param editor {DescriptionEditor} the parent description editor
|
* @param editor {DescriptionEditor} the parent description editor
|
||||||
* @override
|
* @override
|
||||||
*/
|
*/
|
||||||
constructor(editor) {
|
constructor(editor) {
|
||||||
super(editor);
|
super("travel", editor);
|
||||||
this.#from = document.getElementById(`${this.prefix}-from`);
|
this.#from = document.getElementById(`${this.prefix}-from`);
|
||||||
this.#fromError = document.getElementById(`${this.prefix}-from-error`);
|
this.#fromError = document.getElementById(`${this.prefix}-from-error`);
|
||||||
this.#to = document.getElementById(`${this.prefix}-to`);
|
this.#to = document.getElementById(`${this.prefix}-to`);
|
||||||
@@ -852,17 +869,7 @@ class DescriptionEditorGeneralTripTab extends DescriptionEditorTagTabPlane {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The tab ID
|
* Updates the description according to the input in the tab panel.
|
||||||
*
|
|
||||||
* @return {string}
|
|
||||||
* @abstract
|
|
||||||
*/
|
|
||||||
tabId() {
|
|
||||||
return "travel";
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Updates the description according to the input in the tab plane.
|
|
||||||
*
|
*
|
||||||
* @override
|
* @override
|
||||||
*/
|
*/
|
||||||
@@ -878,7 +885,7 @@ class DescriptionEditorGeneralTripTab extends DescriptionEditorTagTabPlane {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resets the tab plane input.
|
* Resets the tab panel input.
|
||||||
*
|
*
|
||||||
* @override
|
* @override
|
||||||
*/
|
*/
|
||||||
@@ -902,7 +909,7 @@ class DescriptionEditorGeneralTripTab extends DescriptionEditorTagTabPlane {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Populates the tab plane with the description input.
|
* Populates the tab panel with the description input.
|
||||||
*
|
*
|
||||||
* @return {boolean} true if the description input matches this tab, or false otherwise
|
* @return {boolean} true if the description input matches this tab, or false otherwise
|
||||||
* @override
|
* @override
|
||||||
@@ -927,12 +934,12 @@ class DescriptionEditorGeneralTripTab extends DescriptionEditorTagTabPlane {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.#to.value = found[4];
|
this.#to.value = found[4];
|
||||||
this.switchToMe();
|
this.editor.switchTo(this);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Validates the input in the tab plane.
|
* Validates the input in the tab panel.
|
||||||
*
|
*
|
||||||
* @return {boolean} true if valid, or false otherwise
|
* @return {boolean} true if valid, or false otherwise
|
||||||
* @override
|
* @override
|
||||||
@@ -977,11 +984,11 @@ class DescriptionEditorGeneralTripTab extends DescriptionEditorTagTabPlane {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The bus trip tab plane.
|
* The bus trip tab.
|
||||||
*
|
*
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
class DescriptionEditorBusTripTab extends DescriptionEditorTagTabPlane {
|
class BusTripTab extends BaseTagTab {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The route
|
* The route
|
||||||
@@ -1020,13 +1027,13 @@ class DescriptionEditorBusTripTab extends DescriptionEditorTagTabPlane {
|
|||||||
#toError;
|
#toError;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs a tab plane.
|
* Constructs a bus trip tab.
|
||||||
*
|
*
|
||||||
* @param editor {DescriptionEditor} the parent description editor
|
* @param editor {DescriptionEditor} the parent description editor
|
||||||
* @override
|
* @override
|
||||||
*/
|
*/
|
||||||
constructor(editor) {
|
constructor(editor) {
|
||||||
super(editor);
|
super("bus", editor);
|
||||||
this.#route = document.getElementById(`${this.prefix}-route`);
|
this.#route = document.getElementById(`${this.prefix}-route`);
|
||||||
this.#routeError = document.getElementById(`${this.prefix}-route-error`);
|
this.#routeError = document.getElementById(`${this.prefix}-route-error`);
|
||||||
this.#from = document.getElementById(`${this.prefix}-from`);
|
this.#from = document.getElementById(`${this.prefix}-from`);
|
||||||
@@ -1051,17 +1058,7 @@ class DescriptionEditorBusTripTab extends DescriptionEditorTagTabPlane {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The tab ID
|
* Updates the description according to the input in the tab panel.
|
||||||
*
|
|
||||||
* @return {string}
|
|
||||||
* @abstract
|
|
||||||
*/
|
|
||||||
tabId() {
|
|
||||||
return "bus";
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Updates the description according to the input in the tab plane.
|
|
||||||
*
|
*
|
||||||
* @override
|
* @override
|
||||||
*/
|
*/
|
||||||
@@ -1070,7 +1067,7 @@ class DescriptionEditorBusTripTab extends DescriptionEditorTagTabPlane {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resets the tab plane input.
|
* Resets the tab panel input.
|
||||||
*
|
*
|
||||||
* @override
|
* @override
|
||||||
*/
|
*/
|
||||||
@@ -1088,7 +1085,7 @@ class DescriptionEditorBusTripTab extends DescriptionEditorTagTabPlane {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Populates the tab plane with the description input.
|
* Populates the tab panel with the description input.
|
||||||
*
|
*
|
||||||
* @return {boolean} true if the description input matches this tab, or false otherwise
|
* @return {boolean} true if the description input matches this tab, or false otherwise
|
||||||
* @override
|
* @override
|
||||||
@@ -1105,12 +1102,12 @@ class DescriptionEditorBusTripTab extends DescriptionEditorTagTabPlane {
|
|||||||
this.#route.value = found[2];
|
this.#route.value = found[2];
|
||||||
this.#from.value = found[3];
|
this.#from.value = found[3];
|
||||||
this.#to.value = found[4];
|
this.#to.value = found[4];
|
||||||
this.switchToMe();
|
this.editor.switchTo(this);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Validates the input in the tab plane.
|
* Validates the input in the tab panel.
|
||||||
*
|
*
|
||||||
* @return {boolean} true if valid, or false otherwise
|
* @return {boolean} true if valid, or false otherwise
|
||||||
*/
|
*/
|
||||||
@@ -1165,11 +1162,11 @@ class DescriptionEditorBusTripTab extends DescriptionEditorTagTabPlane {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The recurring transaction tab plane.
|
* The recurring transaction tab.
|
||||||
*
|
*
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
class DescriptionEditorRecurringTab extends DescriptionEditorTabPlane {
|
class RecurringTab extends BaseDescriptionEditorTab {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The month names
|
* The month names
|
||||||
@@ -1184,13 +1181,13 @@ class DescriptionEditorRecurringTab extends DescriptionEditorTabPlane {
|
|||||||
#itemButtons;
|
#itemButtons;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs a tab plane.
|
* Constructs a recurring transaction tab.
|
||||||
*
|
*
|
||||||
* @param editor {DescriptionEditor} the parent description editor
|
* @param editor {DescriptionEditor} the parent description editor
|
||||||
* @override
|
* @override
|
||||||
*/
|
*/
|
||||||
constructor(editor) {
|
constructor(editor) {
|
||||||
super(editor);
|
super("recurring", editor);
|
||||||
this.#monthNames = [
|
this.#monthNames = [
|
||||||
"",
|
"",
|
||||||
A_("January"), A_("February"), A_("March"), A_("April"),
|
A_("January"), A_("February"), A_("March"), A_("April"),
|
||||||
@@ -1232,17 +1229,7 @@ class DescriptionEditorRecurringTab extends DescriptionEditorTabPlane {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The tab ID
|
* Resets the tab panel input.
|
||||||
*
|
|
||||||
* @return {string}
|
|
||||||
* @abstract
|
|
||||||
*/
|
|
||||||
tabId() {
|
|
||||||
return "recurring";
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Resets the tab plane input.
|
|
||||||
*
|
*
|
||||||
* @override
|
* @override
|
||||||
*/
|
*/
|
||||||
@@ -1254,7 +1241,7 @@ class DescriptionEditorRecurringTab extends DescriptionEditorTabPlane {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Populates the tab plane with the description input.
|
* Populates the tab panel with the description input.
|
||||||
*
|
*
|
||||||
* @return {boolean} true if the description input matches this tab, or false otherwise
|
* @return {boolean} true if the description input matches this tab, or false otherwise
|
||||||
* @override
|
* @override
|
||||||
@@ -1264,7 +1251,7 @@ class DescriptionEditorRecurringTab extends DescriptionEditorTabPlane {
|
|||||||
if (this.#getDescription(itemButton) === this.editor.description) {
|
if (this.#getDescription(itemButton) === this.editor.description) {
|
||||||
itemButton.classList.add("btn-primary");
|
itemButton.classList.add("btn-primary");
|
||||||
itemButton.classList.remove("btn-outline-primary");
|
itemButton.classList.remove("btn-outline-primary");
|
||||||
this.switchToMe();
|
this.editor.switchTo(this);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1272,11 +1259,10 @@ class DescriptionEditorRecurringTab extends DescriptionEditorTabPlane {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Switches to the tab plane.
|
* @inheritDoc
|
||||||
*
|
* @override
|
||||||
*/
|
*/
|
||||||
switchToMe() {
|
onActivated() {
|
||||||
super.switchToMe();
|
|
||||||
for (const itemButton of this.#itemButtons) {
|
for (const itemButton of this.#itemButtons) {
|
||||||
if (itemButton.classList.contains("btn-primary")) {
|
if (itemButton.classList.contains("btn-primary")) {
|
||||||
this.editor.updateCurrentSuggestedAccounts(itemButton);
|
this.editor.updateCurrentSuggestedAccounts(itemButton);
|
||||||
@@ -1287,7 +1273,7 @@ class DescriptionEditorRecurringTab extends DescriptionEditorTabPlane {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Validates the input in the tab plane.
|
* Validates the input in the tab panel.
|
||||||
*
|
*
|
||||||
* @return {boolean} true if valid, or false otherwise
|
* @return {boolean} true if valid, or false otherwise
|
||||||
* @override
|
* @override
|
||||||
@@ -1298,20 +1284,20 @@ class DescriptionEditorRecurringTab extends DescriptionEditorTabPlane {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The annotation tab plane.
|
* The annotation tab.
|
||||||
*
|
*
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
class DescriptionEditorAnnotationTab extends DescriptionEditorTabPlane {
|
class AnnotationTab extends BaseDescriptionEditorTab {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs a tab plane.
|
* Constructs an annotation tab.
|
||||||
*
|
*
|
||||||
* @param editor {DescriptionEditor} the parent description editor
|
* @param editor {DescriptionEditor} the parent description editor
|
||||||
* @override
|
* @override
|
||||||
*/
|
*/
|
||||||
constructor(editor) {
|
constructor(editor) {
|
||||||
super(editor);
|
super("annotation", editor);
|
||||||
this.editor.number.onchange = () => this.updateDescription();
|
this.editor.number.onchange = () => this.updateDescription();
|
||||||
this.editor.note.onchange = () => {
|
this.editor.note.onchange = () => {
|
||||||
this.editor.note.value = this.editor.note.value.trim();
|
this.editor.note.value = this.editor.note.value.trim();
|
||||||
@@ -1320,17 +1306,7 @@ class DescriptionEditorAnnotationTab extends DescriptionEditorTabPlane {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The tab ID
|
* Updates the description according to the input in the tab panel.
|
||||||
*
|
|
||||||
* @return {string}
|
|
||||||
* @abstract
|
|
||||||
*/
|
|
||||||
tabId() {
|
|
||||||
return "annotation";
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Updates the description according to the input in the tab plane.
|
|
||||||
*
|
*
|
||||||
* @override
|
* @override
|
||||||
*/
|
*/
|
||||||
@@ -1348,7 +1324,7 @@ class DescriptionEditorAnnotationTab extends DescriptionEditorTabPlane {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resets the tab plane input.
|
* Resets the tab panel input.
|
||||||
*
|
*
|
||||||
* @override
|
* @override
|
||||||
*/
|
*/
|
||||||
@@ -1358,7 +1334,7 @@ class DescriptionEditorAnnotationTab extends DescriptionEditorTabPlane {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Populates the tab plane with the description input.
|
* Populates the tab panel with the description input.
|
||||||
*
|
*
|
||||||
* @return {boolean} true if the description input matches this tab, or false otherwise
|
* @return {boolean} true if the description input matches this tab, or false otherwise
|
||||||
* @override
|
* @override
|
||||||
@@ -1382,7 +1358,7 @@ class DescriptionEditorAnnotationTab extends DescriptionEditorTabPlane {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Validates the input in the tab plane.
|
* Validates the input in the tab panel.
|
||||||
*
|
*
|
||||||
* @return {boolean} true if valid, or false otherwise
|
* @return {boolean} true if valid, or false otherwise
|
||||||
* @override
|
* @override
|
||||||
|
|||||||
@@ -25,15 +25,16 @@
|
|||||||
/**
|
/**
|
||||||
* The account selector.
|
* The account selector.
|
||||||
*
|
*
|
||||||
|
* @extends {BaseCombobox<BaseJournalEntryAccountOption>}
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
class JournalEntryAccountSelector {
|
class JournalEntryAccountSelector extends BaseCombobox {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The line item editor
|
* The line item editor
|
||||||
* @type {JournalEntryLineItemEditor}
|
* @type {JournalEntryLineItemEditor}
|
||||||
*/
|
*/
|
||||||
lineItemEditor;
|
#lineItemEditor;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Either "debit" or "credit"
|
* Either "debit" or "credit"
|
||||||
@@ -47,12 +48,6 @@ class JournalEntryAccountSelector {
|
|||||||
*/
|
*/
|
||||||
#clearButton
|
#clearButton
|
||||||
|
|
||||||
/**
|
|
||||||
* The query input
|
|
||||||
* @type {HTMLInputElement}
|
|
||||||
*/
|
|
||||||
#query;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The error message when the query has no result
|
* The error message when the query has no result
|
||||||
* @type {HTMLParagraphElement}
|
* @type {HTMLParagraphElement}
|
||||||
@@ -65,15 +60,9 @@ class JournalEntryAccountSelector {
|
|||||||
*/
|
*/
|
||||||
#optionList;
|
#optionList;
|
||||||
|
|
||||||
/**
|
|
||||||
* The options
|
|
||||||
* @type {JournalEntryAccountOption[]}
|
|
||||||
*/
|
|
||||||
#options;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The more item to show all accounts
|
* The more item to show all accounts
|
||||||
* @type {HTMLLIElement}
|
* @type {MoreItems}
|
||||||
*/
|
*/
|
||||||
#more;
|
#more;
|
||||||
|
|
||||||
@@ -90,40 +79,55 @@ class JournalEntryAccountSelector {
|
|||||||
* @param debitCredit {string} either "debit" or "credit"
|
* @param debitCredit {string} either "debit" or "credit"
|
||||||
*/
|
*/
|
||||||
constructor(lineItemEditor, debitCredit) {
|
constructor(lineItemEditor, debitCredit) {
|
||||||
this.lineItemEditor = lineItemEditor;
|
|
||||||
this.#debitCredit = debitCredit;
|
|
||||||
const prefix = `accounting-account-selector-${debitCredit}`;
|
const prefix = `accounting-account-selector-${debitCredit}`;
|
||||||
this.#query = document.getElementById(`${prefix}-query`);
|
const query = document.getElementById(`${prefix}-query`);
|
||||||
|
const options = Array.from(document.getElementsByClassName(`${prefix}-option`)).map((element) => new JournalEntryAccountOption(element, lineItemEditor.saveAccount.bind(lineItemEditor)));
|
||||||
|
super(query, options);
|
||||||
|
this.#lineItemEditor = lineItemEditor;
|
||||||
|
this.#debitCredit = debitCredit;
|
||||||
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`);
|
||||||
this.#options = Array.from(document.getElementsByClassName(`${prefix}-option`)).map((element) => new JournalEntryAccountOption(this, element));
|
const moreElement = document.getElementById(`${prefix}-more`);
|
||||||
this.#more = document.getElementById(`${prefix}-more`);
|
this.#more = new MoreItems(moreElement);
|
||||||
this.#clearButton = document.getElementById(`${prefix}-btn-clear`);
|
moreElement.onclick = () => {
|
||||||
|
|
||||||
this.#more.onclick = () => {
|
|
||||||
this.#isShowMore = true;
|
this.#isShowMore = true;
|
||||||
this.#more.classList.add("d-none");
|
this.#more.setShown(false);
|
||||||
this.#filterOptions();
|
this.filterOptions();
|
||||||
|
};
|
||||||
|
|
||||||
|
this.#clearButton = document.getElementById(`${prefix}-btn-clear`);
|
||||||
|
this.#clearButton.onclick = () => this.#lineItemEditor.clearAccount();
|
||||||
|
|
||||||
|
const modal = document.getElementById(`${prefix}-modal`);
|
||||||
|
const closeButton = document.getElementById(`${prefix}-close`);
|
||||||
|
modal.onkeydown = (event) => {
|
||||||
|
if (event.key === "Escape") {
|
||||||
|
closeButton.click();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
this.#query.oninput = () => this.#filterOptions();
|
|
||||||
this.#clearButton.onclick = () => this.lineItemEditor.clearAccount();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Filters the options.
|
* Filters the options.
|
||||||
*
|
*
|
||||||
|
* @override
|
||||||
*/
|
*/
|
||||||
#filterOptions() {
|
filterOptions() {
|
||||||
|
this.shownOptions = [];
|
||||||
const codesInUse = this.#getCodesUsedInForm();
|
const codesInUse = this.#getCodesUsedInForm();
|
||||||
let isAnyMatched = false;
|
let isAnyMatched = false;
|
||||||
for (const option of this.#options) {
|
for (const option of this.options) {
|
||||||
if (option.isMatched(this.#isShowMore, codesInUse, this.#query.value)) {
|
if (option.isMatched(this.#isShowMore, codesInUse, this.query.value)) {
|
||||||
option.setShown(true);
|
option.setShown(true);
|
||||||
|
this.shownOptions.push(option);
|
||||||
isAnyMatched = true;
|
isAnyMatched = true;
|
||||||
} else {
|
} else {
|
||||||
option.setShown(false);
|
option.setShown(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (!this.#isShowMore) {
|
||||||
|
this.shownOptions.push(this.#more);
|
||||||
|
}
|
||||||
if (!isAnyMatched && this.#isShowMore) {
|
if (!isAnyMatched && this.#isShowMore) {
|
||||||
this.#optionList.classList.add("d-none");
|
this.#optionList.classList.add("d-none");
|
||||||
this.#queryNoResult.classList.remove("d-none");
|
this.#queryNoResult.classList.remove("d-none");
|
||||||
@@ -139,9 +143,9 @@ class JournalEntryAccountSelector {
|
|||||||
* @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.account !== null) {
|
if (this.#lineItemEditor.account !== null) {
|
||||||
inUse.push(this.lineItemEditor.account.code);
|
inUse.push(this.#lineItemEditor.account.code);
|
||||||
}
|
}
|
||||||
return inUse
|
return inUse
|
||||||
}
|
}
|
||||||
@@ -151,14 +155,13 @@ class JournalEntryAccountSelector {
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
onOpen() {
|
onOpen() {
|
||||||
this.#query.value = "";
|
this.query.value = "";
|
||||||
this.#isShowMore = false;
|
this.#isShowMore = false;
|
||||||
this.#more.classList.remove("d-none");
|
this.#more.setShown(true);
|
||||||
this.#filterOptions();
|
this.filterOptions();
|
||||||
for (const option of this.#options) {
|
this.query.removeAttribute("aria-activedescendant");
|
||||||
option.setActive(this.lineItemEditor.account !== null && option.code === this.lineItemEditor.account.code);
|
this.selectOption(this.shownOptions.find((option) => this.#lineItemEditor.account !== null && option.code === this.#lineItemEditor.account.code));
|
||||||
}
|
if (this.#lineItemEditor.account === null) {
|
||||||
if (this.lineItemEditor.account === 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;
|
||||||
@@ -169,6 +172,17 @@ class JournalEntryAccountSelector {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Selects an option.
|
||||||
|
*
|
||||||
|
* @param option {BaseJournalEntryAccountOption|undefined} the option.
|
||||||
|
* @override
|
||||||
|
*/
|
||||||
|
selectOption(option) {
|
||||||
|
this.#more.setActive(false);
|
||||||
|
super.selectOption(option);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the account selector instances.
|
* Returns the account selector instances.
|
||||||
*
|
*
|
||||||
@@ -186,23 +200,37 @@ class JournalEntryAccountSelector {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An account option
|
* The base abstract account option
|
||||||
*
|
*
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
class JournalEntryAccountOption {
|
class BaseJournalEntryAccountOption extends BaseOption {
|
||||||
|
|
||||||
/**
|
|
||||||
* The element
|
|
||||||
* @type {HTMLLIElement}
|
|
||||||
*/
|
|
||||||
#element;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The account code
|
* The account code
|
||||||
* @type {string}
|
* @type {string}
|
||||||
*/
|
*/
|
||||||
code;
|
code = "";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An account option
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
class JournalEntryAccountOption extends BaseJournalEntryAccountOption {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The account title
|
* The account title
|
||||||
@@ -237,11 +265,11 @@ class JournalEntryAccountOption {
|
|||||||
/**
|
/**
|
||||||
* Constructs the account in the account selector.
|
* Constructs the account in the account selector.
|
||||||
*
|
*
|
||||||
* @param selector {JournalEntryAccountSelector} the account selector
|
|
||||||
* @param element {HTMLLIElement} the element
|
* @param element {HTMLLIElement} the element
|
||||||
|
* @param save {function(JournalEntryAccountOption): void} the callback to save the option
|
||||||
*/
|
*/
|
||||||
constructor(selector, element) {
|
constructor(element, save) {
|
||||||
this.#element = element;
|
super(element);
|
||||||
this.code = element.dataset.code;
|
this.code = element.dataset.code;
|
||||||
this.title = element.dataset.title;
|
this.title = element.dataset.title;
|
||||||
this.text = element.dataset.text;
|
this.text = element.dataset.text;
|
||||||
@@ -249,7 +277,7 @@ class JournalEntryAccountOption {
|
|||||||
this.isNeedOffset = element.classList.contains("accounting-account-is-need-offset");
|
this.isNeedOffset = element.classList.contains("accounting-account-is-need-offset");
|
||||||
this.#queryValues = JSON.parse(element.dataset.queryValues);
|
this.#queryValues = JSON.parse(element.dataset.queryValues);
|
||||||
|
|
||||||
this.#element.onclick = () => selector.lineItemEditor.saveAccount(this);
|
element.onclick = () => save(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -259,6 +287,7 @@ class JournalEntryAccountOption {
|
|||||||
* @param codesInUse {string[]} the account codes that are used in the form
|
* @param codesInUse {string[]} the account codes that are used in the form
|
||||||
* @param query {string} the query term
|
* @param query {string} the query term
|
||||||
* @return {boolean} true if the option matches, or false otherwise
|
* @return {boolean} true if the option matches, or false otherwise
|
||||||
|
* @override
|
||||||
*/
|
*/
|
||||||
isMatched(isShowMore, codesInUse, query) {
|
isMatched(isShowMore, codesInUse, query) {
|
||||||
return this.#isInUseMatched(isShowMore, codesInUse) && this.#isQueryMatched(query);
|
return this.#isInUseMatched(isShowMore, codesInUse) && this.#isQueryMatched(query);
|
||||||
@@ -292,30 +321,12 @@ class JournalEntryAccountOption {
|
|||||||
}
|
}
|
||||||
return false;
|
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.
|
* The more item to show all accounts.
|
||||||
*
|
*
|
||||||
* @param isActive {boolean} true if active, or false otherwise
|
* @private
|
||||||
*/
|
*/
|
||||||
setActive(isActive) {
|
class MoreItems extends BaseJournalEntryAccountOption {
|
||||||
if (isActive) {
|
|
||||||
this.#element.classList.add("active");
|
|
||||||
} else {
|
|
||||||
this.#element.classList.remove("active");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -701,12 +701,23 @@ class DebitCreditSubForm {
|
|||||||
this.#element.classList.add("accounting-not-empty");
|
this.#element.classList.add("accounting-not-empty");
|
||||||
this.currency.form.lineItemEditor.onAddNew(this);
|
this.currency.form.lineItemEditor.onAddNew(this);
|
||||||
};
|
};
|
||||||
|
this.#element.role = "button";
|
||||||
|
this.#element.tabIndex = 0;
|
||||||
|
this.#element.onkeydown = (event) => {
|
||||||
|
if (event.key === "Enter" || event.key === " ") {
|
||||||
|
event.preventDefault();
|
||||||
|
this.#element.click();
|
||||||
|
}
|
||||||
|
};
|
||||||
} else {
|
} else {
|
||||||
this.#element.classList.add("accounting-not-empty");
|
this.#element.classList.add("accounting-not-empty");
|
||||||
this.#element.classList.remove("accounting-clickable");
|
this.#element.classList.remove("accounting-clickable");
|
||||||
delete this.#element.dataset.bsToggle;
|
delete this.#element.dataset.bsToggle;
|
||||||
delete this.#element.dataset.bsTarget;
|
delete this.#element.dataset.bsTarget;
|
||||||
this.#element.onclick = null;
|
this.#element.onclick = null;
|
||||||
|
this.#element.removeAttribute("role");
|
||||||
|
this.#element.tabIndex = -1;
|
||||||
|
this.#element.onkeydown = null;
|
||||||
}
|
}
|
||||||
setElementShown(this.#content, this.lineItems.length !== 0);
|
setElementShown(this.#content, this.lineItems.length !== 0);
|
||||||
}
|
}
|
||||||
@@ -986,6 +997,12 @@ class LineItemSubForm {
|
|||||||
this.#element.parentElement.removeChild(this.#element);
|
this.#element.parentElement.removeChild(this.#element);
|
||||||
this.debitCreditSubForm.deleteLineItem(this);
|
this.debitCreditSubForm.deleteLineItem(this);
|
||||||
};
|
};
|
||||||
|
this.#control.onkeydown = (event) => {
|
||||||
|
if (event.key === "Enter" || event.key === " ") {
|
||||||
|
event.preventDefault();
|
||||||
|
this.#control.click();
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -66,13 +66,13 @@ class JournalEntryLineItemEditor {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* The control of the original line item
|
* The control of the original line item
|
||||||
* @type {HTMLDivElement}
|
* @type {HTMLButtonElement}
|
||||||
*/
|
*/
|
||||||
#originalLineItemControl;
|
#originalLineItemControl;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The original line item
|
* The original line item
|
||||||
* @type {HTMLDivElement}
|
* @type {HTMLSpanElement}
|
||||||
*/
|
*/
|
||||||
#originalLineItemText;
|
#originalLineItemText;
|
||||||
|
|
||||||
@@ -90,13 +90,13 @@ class JournalEntryLineItemEditor {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* The control of the description
|
* The control of the description
|
||||||
* @type {HTMLDivElement}
|
* @type {HTMLButtonElement}
|
||||||
*/
|
*/
|
||||||
#descriptionControl;
|
#descriptionControl;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The description
|
* The description
|
||||||
* @type {HTMLDivElement}
|
* @type {HTMLSpanElement}
|
||||||
*/
|
*/
|
||||||
#descriptionText;
|
#descriptionText;
|
||||||
|
|
||||||
@@ -108,13 +108,13 @@ class JournalEntryLineItemEditor {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* The control of the account
|
* The control of the account
|
||||||
* @type {HTMLDivElement}
|
* @type {HTMLButtonElement}
|
||||||
*/
|
*/
|
||||||
#accountControl;
|
#accountControl;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The account
|
* The account
|
||||||
* @type {HTMLDivElement}
|
* @type {HTMLSpanElement}
|
||||||
*/
|
*/
|
||||||
#accountText;
|
#accountText;
|
||||||
|
|
||||||
@@ -228,7 +228,7 @@ class JournalEntryLineItemEditor {
|
|||||||
this.#accountSelectors = JournalEntryAccountSelector.getInstances(this);
|
this.#accountSelectors = JournalEntryAccountSelector.getInstances(this);
|
||||||
this.originalLineItemSelector = new OriginalLineItemSelector(this);
|
this.originalLineItemSelector = new OriginalLineItemSelector(this);
|
||||||
|
|
||||||
this.#originalLineItemControl.onclick = () => this.originalLineItemSelector.onOpen()
|
this.#originalLineItemControl.onclick = () => this.originalLineItemSelector.onOpen(this.modal.id)
|
||||||
this.#originalLineItemDelete.onclick = () => this.clearOriginalLineItem();
|
this.#originalLineItemDelete.onclick = () => this.clearOriginalLineItem();
|
||||||
this.#descriptionControl.onclick = () => this.#descriptionEditors[this.debitCredit].onOpen();
|
this.#descriptionControl.onclick = () => this.#descriptionEditors[this.debitCredit].onOpen();
|
||||||
this.#accountControl.onclick = () => this.#accountSelectors[this.debitCredit].onOpen();
|
this.#accountControl.onclick = () => this.#accountSelectors[this.debitCredit].onOpen();
|
||||||
|
|||||||
@@ -298,6 +298,14 @@ class RecurringExpenseIncomeSubForm {
|
|||||||
this.#element.dataset.bsTarget = `#${this.editor.modal.id}`;
|
this.#element.dataset.bsTarget = `#${this.editor.modal.id}`;
|
||||||
this.#element.onclick = () => this.editor.onAddNew();
|
this.#element.onclick = () => this.editor.onAddNew();
|
||||||
this.#content.classList.add("d-none");
|
this.#content.classList.add("d-none");
|
||||||
|
this.#element.role = "button";
|
||||||
|
this.#element.tabIndex = 0;
|
||||||
|
this.#element.onkeydown = (event) => {
|
||||||
|
if (event.key === "Enter" || event.key === " ") {
|
||||||
|
event.preventDefault();
|
||||||
|
this.#element.click();
|
||||||
|
}
|
||||||
|
};
|
||||||
} else {
|
} else {
|
||||||
this.#element.classList.add("accounting-not-empty");
|
this.#element.classList.add("accounting-not-empty");
|
||||||
this.#element.classList.remove("accounting-clickable");
|
this.#element.classList.remove("accounting-clickable");
|
||||||
@@ -305,6 +313,9 @@ class RecurringExpenseIncomeSubForm {
|
|||||||
delete this.#element.dataset.bsTarget;
|
delete this.#element.dataset.bsTarget;
|
||||||
this.#element.onclick = null;
|
this.#element.onclick = null;
|
||||||
this.#content.classList.remove("d-none");
|
this.#content.classList.remove("d-none");
|
||||||
|
this.#element.removeAttribute("role");
|
||||||
|
this.#element.tabIndex = -1;
|
||||||
|
this.#element.onkeydown = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -375,7 +386,7 @@ class RecurringItemSubForm {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* The control
|
* The control
|
||||||
* @type {HTMLDivElement}
|
* @type {HTMLButtonElement}
|
||||||
*/
|
*/
|
||||||
#control;
|
#control;
|
||||||
|
|
||||||
@@ -399,7 +410,7 @@ class RecurringItemSubForm {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* The text display of the name
|
* The text display of the name
|
||||||
* @type {HTMLDivElement}
|
* @type {HTMLSpanElement}
|
||||||
*/
|
*/
|
||||||
#nameText;
|
#nameText;
|
||||||
|
|
||||||
@@ -411,7 +422,7 @@ class RecurringItemSubForm {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* The text display of the account
|
* The text display of the account
|
||||||
* @type {HTMLDivElement}
|
* @type {HTMLSpanElement}
|
||||||
*/
|
*/
|
||||||
#accountText;
|
#accountText;
|
||||||
|
|
||||||
@@ -423,7 +434,7 @@ class RecurringItemSubForm {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* The text display of the description template
|
* The text display of the description template
|
||||||
* @type {HTMLDivElement}
|
* @type {HTMLSpanElement}
|
||||||
*/
|
*/
|
||||||
#descriptionTemplateText;
|
#descriptionTemplateText;
|
||||||
|
|
||||||
@@ -595,13 +606,13 @@ class RecurringItemEditor {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* The control of the account
|
* The control of the account
|
||||||
* @type {HTMLDivElement}
|
* @type {HTMLButtonElement}
|
||||||
*/
|
*/
|
||||||
#accountControl;
|
#accountControl;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The text display of the account
|
* The text display of the account
|
||||||
* @type {HTMLDivElement}
|
* @type {HTMLSpanElement}
|
||||||
*/
|
*/
|
||||||
#accountContainer;
|
#accountContainer;
|
||||||
|
|
||||||
@@ -832,15 +843,16 @@ class RecurringItemEditor {
|
|||||||
/**
|
/**
|
||||||
* The account selector for the recurring item editor.
|
* The account selector for the recurring item editor.
|
||||||
*
|
*
|
||||||
|
* @extends {BaseCombobox<RecurringAccount>}
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
class RecurringAccountSelector {
|
class RecurringAccountSelector extends BaseCombobox {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The recurring item editor
|
* The recurring item editor
|
||||||
* @type {RecurringItemEditor}
|
* @type {RecurringItemEditor}
|
||||||
*/
|
*/
|
||||||
editor;
|
#editor;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Either "expense" or "income"
|
* Either "expense" or "income"
|
||||||
@@ -848,12 +860,6 @@ class RecurringAccountSelector {
|
|||||||
*/
|
*/
|
||||||
#expenseIncome;
|
#expenseIncome;
|
||||||
|
|
||||||
/**
|
|
||||||
* The query input
|
|
||||||
* @type {HTMLInputElement}
|
|
||||||
*/
|
|
||||||
#query;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The error message when the query has no result
|
* The error message when the query has no result
|
||||||
* @type {HTMLParagraphElement}
|
* @type {HTMLParagraphElement}
|
||||||
@@ -866,12 +872,6 @@ class RecurringAccountSelector {
|
|||||||
*/
|
*/
|
||||||
#optionList;
|
#optionList;
|
||||||
|
|
||||||
/**
|
|
||||||
* The account options
|
|
||||||
* @type {RecurringAccount[]}
|
|
||||||
*/
|
|
||||||
#options;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The button to clear the account
|
* The button to clear the account
|
||||||
* @type {HTMLButtonElement}
|
* @type {HTMLButtonElement}
|
||||||
@@ -884,28 +884,39 @@ class RecurringAccountSelector {
|
|||||||
* @param editor {RecurringItemEditor} the recurring item editor
|
* @param editor {RecurringItemEditor} the recurring item editor
|
||||||
*/
|
*/
|
||||||
constructor(editor) {
|
constructor(editor) {
|
||||||
this.editor = editor;
|
|
||||||
this.#expenseIncome = editor.expenseIncome;
|
|
||||||
const prefix = `accounting-recurring-accounting-selector-${editor.expenseIncome}`;
|
const prefix = `accounting-recurring-accounting-selector-${editor.expenseIncome}`;
|
||||||
this.#query = document.getElementById(`${prefix}-query`);
|
const query = document.getElementById(`${prefix}-query`);
|
||||||
|
const options = Array.from(document.getElementsByClassName(`${prefix}-option`)).map((element) => new RecurringAccount(element, editor.saveAccount.bind(editor)));
|
||||||
|
super(query, options);
|
||||||
|
this.#editor = editor;
|
||||||
|
this.#expenseIncome = editor.expenseIncome;
|
||||||
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`);
|
||||||
this.#options = Array.from(document.getElementsByClassName(`${prefix}-option`)).map((element) => new RecurringAccount(this, element));
|
|
||||||
this.#clearButton = document.getElementById(`${prefix}-clear`);
|
|
||||||
|
|
||||||
this.#query.oninput = () => this.#filterOptions();
|
this.#clearButton = document.getElementById(`${prefix}-clear`);
|
||||||
this.#clearButton.onclick = () => this.editor.clearAccount();
|
this.#clearButton.onclick = () => this.#editor.clearAccount();
|
||||||
|
|
||||||
|
const modal = document.getElementById(`${prefix}-modal`);
|
||||||
|
const closeButton = document.getElementById(`${prefix}-close`);
|
||||||
|
modal.onkeydown = (event) => {
|
||||||
|
if (event.key === "Escape") {
|
||||||
|
closeButton.click();
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Filters the options.
|
* Filters the options.
|
||||||
*
|
*
|
||||||
|
* @override
|
||||||
*/
|
*/
|
||||||
#filterOptions() {
|
filterOptions() {
|
||||||
|
this.shownOptions = [];
|
||||||
let isAnyMatched = false;
|
let isAnyMatched = false;
|
||||||
for (const option of this.#options) {
|
for (const option of this.options) {
|
||||||
if (option.isMatched(this.#query.value)) {
|
if (option.isMatched(this.query.value)) {
|
||||||
option.setShown(true);
|
option.setShown(true);
|
||||||
|
this.shownOptions.push(option);
|
||||||
isAnyMatched = true;
|
isAnyMatched = true;
|
||||||
} else {
|
} else {
|
||||||
option.setShown(false);
|
option.setShown(false);
|
||||||
@@ -925,12 +936,11 @@ class RecurringAccountSelector {
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
onOpen() {
|
onOpen() {
|
||||||
this.#query.value = "";
|
this.query.value = "";
|
||||||
this.#filterOptions();
|
this.filterOptions();
|
||||||
for (const option of this.#options) {
|
this.query.removeAttribute("aria-activedescendant");
|
||||||
option.setActive(option.code === this.editor.accountCode);
|
this.selectOption(this.shownOptions.find((option) => option.code === this.#editor.accountCode));
|
||||||
}
|
if (this.#editor.accountCode === null) {
|
||||||
if (this.editor.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;
|
||||||
@@ -947,13 +957,7 @@ class RecurringAccountSelector {
|
|||||||
*
|
*
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
class RecurringAccount {
|
class RecurringAccount extends BaseOption {
|
||||||
|
|
||||||
/**
|
|
||||||
* The element
|
|
||||||
* @type {HTMLLIElement}
|
|
||||||
*/
|
|
||||||
#element;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The account code
|
* The account code
|
||||||
@@ -976,16 +980,16 @@ class RecurringAccount {
|
|||||||
/**
|
/**
|
||||||
* Constructs the account in the account selector for the recurring item editor.
|
* Constructs the account in the account selector for the recurring item editor.
|
||||||
*
|
*
|
||||||
* @param selector {RecurringAccountSelector} the account selector
|
|
||||||
* @param element {HTMLLIElement} the element
|
* @param element {HTMLLIElement} the element
|
||||||
|
* @param save {function(RecurringAccount): void} the callback to save the option
|
||||||
*/
|
*/
|
||||||
constructor(selector, element) {
|
constructor(element, save) {
|
||||||
this.#element = element;
|
super(element);
|
||||||
this.code = element.dataset.code;
|
this.code = element.dataset.code;
|
||||||
this.text = element.dataset.text;
|
this.text = element.dataset.text;
|
||||||
this.#queryValues = JSON.parse(element.dataset.queryValues);
|
this.#queryValues = JSON.parse(element.dataset.queryValues);
|
||||||
|
|
||||||
this.#element.onclick = () => selector.editor.saveAccount(this);
|
element.onclick = () => save(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1005,30 +1009,4 @@ class RecurringAccount {
|
|||||||
}
|
}
|
||||||
return false;
|
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");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,27 +25,16 @@
|
|||||||
/**
|
/**
|
||||||
* The original line item selector.
|
* The original line item selector.
|
||||||
*
|
*
|
||||||
|
* @extends {BaseCombobox<OriginalLineItem>}
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
class OriginalLineItemSelector {
|
class OriginalLineItemSelector extends BaseCombobox {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The line item editor
|
* The line item editor
|
||||||
* @type {JournalEntryLineItemEditor}
|
* @type {JournalEntryLineItemEditor}
|
||||||
*/
|
*/
|
||||||
lineItemEditor;
|
#lineItemEditor;
|
||||||
|
|
||||||
/**
|
|
||||||
* The prefix of the HTML ID and class names
|
|
||||||
* @type {string}
|
|
||||||
*/
|
|
||||||
#prefix = "accounting-original-line-item-selector";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The query input
|
|
||||||
* @type {HTMLInputElement}
|
|
||||||
*/
|
|
||||||
#query;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The error message when the query has no result
|
* The error message when the query has no result
|
||||||
@@ -59,12 +48,6 @@ class OriginalLineItemSelector {
|
|||||||
*/
|
*/
|
||||||
#optionList;
|
#optionList;
|
||||||
|
|
||||||
/**
|
|
||||||
* The options
|
|
||||||
* @type {OriginalLineItem[]}
|
|
||||||
*/
|
|
||||||
#options;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The options by their ID
|
* The options by their ID
|
||||||
* @type {Object.<string, OriginalLineItem>}
|
* @type {Object.<string, OriginalLineItem>}
|
||||||
@@ -82,22 +65,37 @@ class OriginalLineItemSelector {
|
|||||||
*/
|
*/
|
||||||
#debitCredit;
|
#debitCredit;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The close button.
|
||||||
|
* @type {HTMLButtonElement}
|
||||||
|
*/
|
||||||
|
#closeButton;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs an original line item selector.
|
* Constructs an original line item selector.
|
||||||
*
|
*
|
||||||
* @param lineItemEditor {JournalEntryLineItemEditor} the line item editor
|
* @param lineItemEditor {JournalEntryLineItemEditor} the line item editor
|
||||||
*/
|
*/
|
||||||
constructor(lineItemEditor) {
|
constructor(lineItemEditor) {
|
||||||
this.lineItemEditor = lineItemEditor;
|
const prefix = "accounting-original-line-item-selector";
|
||||||
this.#query = document.getElementById(`${this.#prefix}-query`);
|
const query = document.getElementById(`${prefix}-query`);
|
||||||
this.#queryNoResult = document.getElementById(`${this.#prefix}-option-no-result`);
|
const options = Array.from(document.getElementsByClassName(`${prefix}-option`)).map((element) => new OriginalLineItem(element, lineItemEditor.saveOriginalLineItem.bind(lineItemEditor), lineItemEditor.form));
|
||||||
this.#optionList = document.getElementById(`${this.#prefix}-option-list`);
|
super(query, options);
|
||||||
this.#options = Array.from(document.getElementsByClassName(`${this.#prefix}-option`)).map((element) => new OriginalLineItem(this, element));
|
this.#lineItemEditor = lineItemEditor;
|
||||||
|
this.#queryNoResult = document.getElementById(`${prefix}-option-no-result`);
|
||||||
|
this.#optionList = document.getElementById(`${prefix}-option-list`);
|
||||||
this.#optionById = {};
|
this.#optionById = {};
|
||||||
for (const option of this.#options) {
|
for (const option of this.options) {
|
||||||
this.#optionById[option.id] = option;
|
this.#optionById[option.id] = option;
|
||||||
}
|
}
|
||||||
this.#query.oninput = () => this.#filterOptions();
|
this.#closeButton = document.getElementById(`${prefix}-close`);
|
||||||
|
|
||||||
|
const modal = document.getElementById(`${prefix}-modal`);
|
||||||
|
modal.onkeydown = (event) => {
|
||||||
|
if (event.key === "Escape") {
|
||||||
|
this.#closeButton.click();
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -127,7 +125,7 @@ class OriginalLineItemSelector {
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
#updateNetBalances() {
|
#updateNetBalances() {
|
||||||
const otherLineItems = this.lineItemEditor.form.getLineItems().filter((lineItem) => lineItem !== this.lineItemEditor.lineItem);
|
const otherLineItems = this.#lineItemEditor.form.getLineItems().filter((lineItem) => lineItem !== this.#lineItemEditor.lineItem);
|
||||||
const otherOffsets = {}
|
const otherOffsets = {}
|
||||||
for (const otherLineItem of otherLineItems) {
|
for (const otherLineItem of otherLineItems) {
|
||||||
const otherOriginalLineItemId = otherLineItem.originalLineItemId;
|
const otherOriginalLineItemId = otherLineItem.originalLineItemId;
|
||||||
@@ -140,7 +138,7 @@ class OriginalLineItemSelector {
|
|||||||
}
|
}
|
||||||
otherOffsets[otherOriginalLineItemId] = otherOffsets[otherOriginalLineItemId].plus(amount);
|
otherOffsets[otherOriginalLineItemId] = otherOffsets[otherOriginalLineItemId].plus(amount);
|
||||||
}
|
}
|
||||||
for (const option of this.#options) {
|
for (const option of this.options) {
|
||||||
if (option.id in otherOffsets) {
|
if (option.id in otherOffsets) {
|
||||||
option.updateNetBalance(otherOffsets[option.id]);
|
option.updateNetBalance(otherOffsets[option.id]);
|
||||||
} else {
|
} else {
|
||||||
@@ -152,12 +150,15 @@ class OriginalLineItemSelector {
|
|||||||
/**
|
/**
|
||||||
* Filters the options.
|
* Filters the options.
|
||||||
*
|
*
|
||||||
|
* @override
|
||||||
*/
|
*/
|
||||||
#filterOptions() {
|
filterOptions() {
|
||||||
|
this.shownOptions = [];
|
||||||
let isAnyMatched = false;
|
let isAnyMatched = false;
|
||||||
for (const option of this.#options) {
|
for (const option of this.options) {
|
||||||
if (option.isMatched(this.#debitCredit, this.#currencyCode, this.#query.value)) {
|
if (option.isMatched(this.#debitCredit, this.#currencyCode, this.query.value)) {
|
||||||
option.setShown(true);
|
option.setShown(true);
|
||||||
|
this.shownOptions.push(option);
|
||||||
isAnyMatched = true;
|
isAnyMatched = true;
|
||||||
} else {
|
} else {
|
||||||
option.setShown(false);
|
option.setShown(false);
|
||||||
@@ -175,16 +176,17 @@ class OriginalLineItemSelector {
|
|||||||
/**
|
/**
|
||||||
* The callback when the original line item selector is shown.
|
* The callback when the original line item selector is shown.
|
||||||
*
|
*
|
||||||
|
* @param parentID {string} the ID of the parent element
|
||||||
*/
|
*/
|
||||||
onOpen() {
|
onOpen(parentID) {
|
||||||
this.#currencyCode = this.lineItemEditor.currencyCode;
|
this.#closeButton.dataset.bsTarget = `#${parentID}`;
|
||||||
this.#debitCredit = this.lineItemEditor.debitCredit;
|
this.#currencyCode = this.#lineItemEditor.currencyCode;
|
||||||
for (const option of this.#options) {
|
this.#debitCredit = this.#lineItemEditor.debitCredit;
|
||||||
option.setActive(option.id === this.lineItemEditor.originalLineItemId);
|
this.query.value = "";
|
||||||
}
|
|
||||||
this.#query.value = "";
|
|
||||||
this.#updateNetBalances();
|
this.#updateNetBalances();
|
||||||
this.#filterOptions();
|
this.filterOptions();
|
||||||
|
this.query.removeAttribute("aria-activedescendant");
|
||||||
|
this.selectOption(this.shownOptions.find((option) => option.id === this.#lineItemEditor.originalLineItemId));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -193,7 +195,7 @@ class OriginalLineItemSelector {
|
|||||||
*
|
*
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
class OriginalLineItem {
|
class OriginalLineItem extends BaseOption {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The journal entry form
|
* The journal entry form
|
||||||
@@ -201,12 +203,6 @@ class OriginalLineItem {
|
|||||||
*/
|
*/
|
||||||
#form;
|
#form;
|
||||||
|
|
||||||
/**
|
|
||||||
* The element
|
|
||||||
* @type {HTMLLIElement}
|
|
||||||
*/
|
|
||||||
#element;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The ID
|
* The ID
|
||||||
* @type {string}
|
* @type {string}
|
||||||
@@ -276,12 +272,13 @@ class OriginalLineItem {
|
|||||||
/**
|
/**
|
||||||
* Constructs an original line item.
|
* Constructs an original line item.
|
||||||
*
|
*
|
||||||
* @param selector {OriginalLineItemSelector} the original line item selector
|
|
||||||
* @param element {HTMLLIElement} the element
|
* @param element {HTMLLIElement} the element
|
||||||
|
* @param save {function(OriginalLineItem): void} the callback to save the option
|
||||||
|
* @param form {JournalEntryForm} the journal entry form
|
||||||
*/
|
*/
|
||||||
constructor(selector, element) {
|
constructor(element, save, form) {
|
||||||
this.#form = selector.lineItemEditor.form;
|
super(element);
|
||||||
this.#element = element;
|
this.#form = form;
|
||||||
this.id = element.dataset.id;
|
this.id = element.dataset.id;
|
||||||
this.date = element.dataset.date;
|
this.date = element.dataset.date;
|
||||||
this.#debitCredit = element.dataset.debitCredit;
|
this.#debitCredit = element.dataset.debitCredit;
|
||||||
@@ -293,7 +290,8 @@ class OriginalLineItem {
|
|||||||
this.netBalanceText = document.getElementById(`accounting-original-line-item-selector-option-${this.id}-net-balance`);
|
this.netBalanceText = document.getElementById(`accounting-original-line-item-selector-option-${this.id}-net-balance`);
|
||||||
this.text = element.dataset.text;
|
this.text = element.dataset.text;
|
||||||
this.#queryValues = JSON.parse(element.dataset.queryValues);
|
this.#queryValues = JSON.parse(element.dataset.queryValues);
|
||||||
this.#element.onclick = () => selector.lineItemEditor.saveOriginalLineItem(this);
|
|
||||||
|
element.onclick = () => save(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -382,30 +380,4 @@ class OriginalLineItem {
|
|||||||
const whole = Number(this.netBalance.minus(frac));
|
const whole = Number(this.netBalance.minus(frac));
|
||||||
return String(whole) + String(frac).substring(1);
|
return String(whole) + String(frac).substring(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 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");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,21 +30,16 @@ document.addEventListener("DOMContentLoaded", () => {
|
|||||||
/**
|
/**
|
||||||
* The period chooser.
|
* The period chooser.
|
||||||
*
|
*
|
||||||
|
* @extends {BaseTablist<BasePeriodTab>}
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
class PeriodChooser {
|
class PeriodChooser extends BaseTablist {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The modal of the period chooser
|
* The URL template for different periods.
|
||||||
* @type {HTMLDivElement}
|
* @type {string}
|
||||||
*/
|
*/
|
||||||
modal;
|
urlTemplate;
|
||||||
|
|
||||||
/**
|
|
||||||
* The tab planes
|
|
||||||
* @type {{month: MonthTab, year: YearTab, day: DayTab, custom: CustomTab}}
|
|
||||||
*/
|
|
||||||
tabPlanes = {};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs the period chooser.
|
* Constructs the period chooser.
|
||||||
@@ -52,12 +47,23 @@ class PeriodChooser {
|
|||||||
*/
|
*/
|
||||||
constructor() {
|
constructor() {
|
||||||
const prefix = "accounting-period-chooser";
|
const prefix = "accounting-period-chooser";
|
||||||
this.modal = document.getElementById(`${prefix}-modal`);
|
super(document.getElementById(`${prefix}-tab-list`));
|
||||||
for (const cls of [MonthTab, YearTab, DayTab, CustomTab]) {
|
this.tabs = [new MonthTab(this), new YearTab(this), new DayTab(this), new CustomTab(this)];
|
||||||
const tab = new cls(this);
|
const modal = document.getElementById(`${prefix}-modal`);
|
||||||
this.tabPlanes[tab.tabId()] = tab;
|
this.urlTemplate = modal.dataset.urlTemplate;
|
||||||
|
for (const tab of this.tabs) {
|
||||||
|
if (tab.isActive()) {
|
||||||
|
this.currentTab = tab;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
* @override
|
||||||
|
*/
|
||||||
|
onTabFocus(tab) { this.switchTo(tab); }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The period chooser.
|
* The period chooser.
|
||||||
@@ -75,12 +81,12 @@ class PeriodChooser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A tab plane.
|
* A base abstract period tab.
|
||||||
*
|
*
|
||||||
* @abstract
|
* @abstract
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
class TabPlane {
|
class BasePeriodTab extends BaseTab {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The period chooser
|
* The period chooser
|
||||||
@@ -95,62 +101,27 @@ class TabPlane {
|
|||||||
prefix;
|
prefix;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The tab
|
* Constructs a base abstract period tab.
|
||||||
* @type {HTMLSpanElement}
|
|
||||||
*/
|
|
||||||
#tab;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The page
|
|
||||||
* @type {HTMLDivElement}
|
|
||||||
*/
|
|
||||||
#page;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructs a tab plane.
|
|
||||||
*
|
*
|
||||||
|
* @param tabID {string} the tab ID
|
||||||
* @param chooser {PeriodChooser} the period chooser
|
* @param chooser {PeriodChooser} the period chooser
|
||||||
*/
|
*/
|
||||||
constructor(chooser) {
|
constructor(tabID, chooser) {
|
||||||
|
const prefix = `accounting-period-chooser-${tabID}`;
|
||||||
|
const tab = document.getElementById(`${prefix}-tab`);
|
||||||
|
const panel = document.getElementById(`${prefix}-panel`);
|
||||||
|
super(tab, panel, chooser.switchTo.bind(chooser));
|
||||||
this.chooser = chooser;
|
this.chooser = chooser;
|
||||||
this.prefix = `accounting-period-chooser-${this.tabId()}`;
|
this.prefix = prefix;
|
||||||
this.#tab = document.getElementById(`${this.prefix}-tab`);
|
|
||||||
this.#page = document.getElementById(`${this.prefix}-page`);
|
|
||||||
this.#tab.onclick = () => this.#switchToMe();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The tab ID
|
|
||||||
*
|
|
||||||
* @return {string}
|
|
||||||
* @abstract
|
|
||||||
*/
|
|
||||||
tabId() { throw new Error("Method not implemented.") };
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Switches to the tab plane.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
#switchToMe() {
|
|
||||||
for (const tabPlane of Object.values(this.chooser.tabPlanes)) {
|
|
||||||
tabPlane.#tab.classList.remove("active")
|
|
||||||
tabPlane.#tab.ariaCurrent = "false";
|
|
||||||
tabPlane.#page.classList.add("d-none");
|
|
||||||
tabPlane.#page.ariaCurrent = "false";
|
|
||||||
}
|
|
||||||
this.#tab.classList.add("active");
|
|
||||||
this.#tab.ariaCurrent = "page";
|
|
||||||
this.#page.classList.remove("d-none");
|
|
||||||
this.#page.ariaCurrent = "page";
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The month tab plane.
|
* The month tab.
|
||||||
*
|
*
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
class MonthTab extends TabPlane {
|
class MonthTab extends BasePeriodTab {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The month chooser.
|
* The month chooser.
|
||||||
@@ -159,12 +130,12 @@ class MonthTab extends TabPlane {
|
|||||||
#monthChooser
|
#monthChooser
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs a tab plane.
|
* Constructs a month tab.
|
||||||
*
|
*
|
||||||
* @param chooser {PeriodChooser} the period chooser
|
* @param chooser {PeriodChooser} the period chooser
|
||||||
*/
|
*/
|
||||||
constructor(chooser) {
|
constructor(chooser) {
|
||||||
super(chooser);
|
super("month", chooser);
|
||||||
const monthChooser = document.getElementById(`${this.prefix}-chooser`);
|
const monthChooser = document.getElementById(`${this.prefix}-chooser`);
|
||||||
if (monthChooser !== null) {
|
if (monthChooser !== null) {
|
||||||
this.#monthChooser = new tempusDominus.TempusDominus(monthChooser, {
|
this.#monthChooser = new tempusDominus.TempusDominus(monthChooser, {
|
||||||
@@ -184,45 +155,36 @@ class MonthTab extends TabPlane {
|
|||||||
const date = e.detail.date;
|
const date = e.detail.date;
|
||||||
const zeroPaddedMonth = `0${date.month + 1}`.slice(-2)
|
const zeroPaddedMonth = `0${date.month + 1}`.slice(-2)
|
||||||
const period = `${date.year}-${zeroPaddedMonth}`;
|
const period = `${date.year}-${zeroPaddedMonth}`;
|
||||||
window.location = chooser.modal.dataset.urlTemplate
|
window.location = chooser.urlTemplate
|
||||||
.replaceAll("PERIOD", period);
|
.replaceAll("PERIOD", period);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* The tab ID
|
|
||||||
*
|
|
||||||
* @return {string}
|
|
||||||
*/
|
|
||||||
tabId() {
|
|
||||||
return "month";
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The year tab plane.
|
* The year tab.
|
||||||
*
|
*
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
class YearTab extends TabPlane {
|
class YearTab extends BasePeriodTab {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The tab ID
|
* Constructs a year tab.
|
||||||
*
|
*
|
||||||
* @return {string}
|
* @param chooser {PeriodChooser} the period chooser
|
||||||
*/
|
*/
|
||||||
tabId() {
|
constructor(chooser) {
|
||||||
return "year";
|
super("year", chooser);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The day tab plane.
|
* The day tab.
|
||||||
*
|
*
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
class DayTab extends TabPlane {
|
class DayTab extends BasePeriodTab {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The day input
|
* The day input
|
||||||
@@ -237,18 +199,18 @@ class DayTab extends TabPlane {
|
|||||||
#dateError;
|
#dateError;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs a tab plane.
|
* Constructs a day tab.
|
||||||
*
|
*
|
||||||
* @param chooser {PeriodChooser} the period chooser
|
* @param chooser {PeriodChooser} the period chooser
|
||||||
*/
|
*/
|
||||||
constructor(chooser) {
|
constructor(chooser) {
|
||||||
super(chooser);
|
super("day", chooser);
|
||||||
this.#date = document.getElementById(`${this.prefix}-date`);
|
this.#date = document.getElementById(`${this.prefix}-date`);
|
||||||
this.#dateError = document.getElementById(`${this.prefix}-date-error`);
|
this.#dateError = document.getElementById(`${this.prefix}-date-error`);
|
||||||
if (this.#date !== null) {
|
if (this.#date !== null) {
|
||||||
this.#date.onchange = () => {
|
this.#date.onchange = () => {
|
||||||
if (this.#validateDate()) {
|
if (this.#validateDate()) {
|
||||||
window.location = chooser.modal.dataset.urlTemplate
|
window.location = chooser.urlTemplate
|
||||||
.replaceAll("PERIOD", this.#date.value);
|
.replaceAll("PERIOD", this.#date.value);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -275,23 +237,14 @@ class DayTab extends TabPlane {
|
|||||||
this.#dateError.innerText = "";
|
this.#dateError.innerText = "";
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* The tab ID
|
|
||||||
*
|
|
||||||
* @return {string}
|
|
||||||
*/
|
|
||||||
tabId() {
|
|
||||||
return "day";
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The custom tab plane.
|
* The custom tab.
|
||||||
*
|
*
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
class CustomTab extends TabPlane {
|
class CustomTab extends BasePeriodTab {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The start of the period
|
* The start of the period
|
||||||
@@ -324,12 +277,12 @@ class CustomTab extends TabPlane {
|
|||||||
#confirm;
|
#confirm;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs a tab plane.
|
* Constructs a custom tab.
|
||||||
*
|
*
|
||||||
* @param chooser {PeriodChooser} the period chooser
|
* @param chooser {PeriodChooser} the period chooser
|
||||||
*/
|
*/
|
||||||
constructor(chooser) {
|
constructor(chooser) {
|
||||||
super(chooser);
|
super("custom", chooser);
|
||||||
this.#start = document.getElementById(`${this.prefix}-start`);
|
this.#start = document.getElementById(`${this.prefix}-start`);
|
||||||
this.#startError = document.getElementById(`${this.prefix}-start-error`);
|
this.#startError = document.getElementById(`${this.prefix}-start-error`);
|
||||||
this.#end = document.getElementById(`${this.prefix}-end`);
|
this.#end = document.getElementById(`${this.prefix}-end`);
|
||||||
@@ -351,7 +304,7 @@ class CustomTab extends TabPlane {
|
|||||||
isValid = this.#validateStart() && isValid;
|
isValid = this.#validateStart() && isValid;
|
||||||
isValid = this.#validateEnd() && isValid;
|
isValid = this.#validateEnd() && isValid;
|
||||||
if (isValid) {
|
if (isValid) {
|
||||||
window.location = chooser.modal.dataset.urlTemplate
|
window.location = chooser.urlTemplate
|
||||||
.replaceAll("PERIOD", `${this.#start.value}-${this.#end.value}`);
|
.replaceAll("PERIOD", `${this.#start.value}-${this.#end.value}`);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -407,13 +360,4 @@ class CustomTab extends TabPlane {
|
|||||||
this.#endError.innerText = "";
|
this.#endError.innerText = "";
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* The tab ID
|
|
||||||
*
|
|
||||||
* @return {string}
|
|
||||||
*/
|
|
||||||
tabId() {
|
|
||||||
return "custom";
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,28 +27,28 @@ First written: 2023/1/31
|
|||||||
|
|
||||||
<div class="mb-3 accounting-toolbar">
|
<div class="mb-3 accounting-toolbar">
|
||||||
<a class="btn btn-primary" role="button" href="{{ url_for("accounting.account.list")|accounting_or_next }}">
|
<a class="btn btn-primary" role="button" href="{{ url_for("accounting.account.list")|accounting_or_next }}">
|
||||||
<i class="fa-solid fa-circle-chevron-left"></i>
|
<i class="fa-solid fa-circle-chevron-left" aria-hidden="true"></i>
|
||||||
<span class="d-none d-md-inline">{{ A_("Back") }}</span>
|
<span class="d-none d-md-inline">{{ A_("Back") }}</span>
|
||||||
</a>
|
</a>
|
||||||
{% if accounting_can_edit() %}
|
{% if accounting_can_edit() %}
|
||||||
<a class="btn btn-primary d-none d-md-inline" role="button" href="{{ url_for("accounting.account.edit", account=obj)|accounting_inherit_next }}">
|
<a class="btn btn-primary d-none d-md-inline" role="button" href="{{ url_for("accounting.account.edit", account=obj)|accounting_inherit_next }}">
|
||||||
<i class="fa-solid fa-pen-to-square"></i>
|
<i class="fa-solid fa-pen-to-square" aria-hidden="true"></i>
|
||||||
{{ A_("Edit") }}
|
{{ A_("Edit") }}
|
||||||
</a>
|
</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<a class="btn btn-primary" role="button" href="{{ url_for("accounting.account.order", base=obj.base)|accounting_append_next }}">
|
<a class="btn btn-primary" role="button" href="{{ url_for("accounting.account.order", base=obj.base)|accounting_append_next }}">
|
||||||
<i class="fa-solid fa-bars-staggered"></i>
|
<i class="fa-solid fa-bars-staggered" aria-hidden="true"></i>
|
||||||
<span class="d-none d-md-inline">{{ A_("Order") }}</span>
|
<span class="d-none d-md-inline">{{ A_("Order") }}</span>
|
||||||
</a>
|
</a>
|
||||||
{% if accounting_can_edit() %}
|
{% if accounting_can_edit() %}
|
||||||
{% if obj.can_delete %}
|
{% if obj.can_delete %}
|
||||||
<button class="btn btn-danger" type="button" data-bs-toggle="modal" data-bs-target="#accounting-delete-modal">
|
<button class="btn btn-danger" type="button" data-bs-toggle="modal" data-bs-target="#accounting-delete-modal">
|
||||||
<i class="fa-solid fa-trash"></i>
|
<i class="fa-solid fa-trash" aria-hidden="true"></i>
|
||||||
<span class="d-none d-md-inline">{{ A_("Delete") }}</span>
|
<span class="d-none d-md-inline">{{ A_("Delete") }}</span>
|
||||||
</button>
|
</button>
|
||||||
{% else %}
|
{% else %}
|
||||||
<button class="btn btn-secondary" type="button" disabled="disabled">
|
<button class="btn btn-secondary" type="button" disabled="disabled">
|
||||||
<i class="fa-solid fa-trash"></i>
|
<i class="fa-solid fa-trash" aria-hidden="true"></i>
|
||||||
<span class="d-none d-md-inline">{{ A_("Delete") }}</span>
|
<span class="d-none d-md-inline">{{ A_("Delete") }}</span>
|
||||||
</button>
|
</button>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
@@ -57,8 +57,8 @@ First written: 2023/1/31
|
|||||||
|
|
||||||
{% if accounting_can_edit() %}
|
{% if accounting_can_edit() %}
|
||||||
<div class="d-md-none accounting-material-fab">
|
<div class="d-md-none accounting-material-fab">
|
||||||
<a class="btn btn-primary" role="button" href="{{ url_for("accounting.account.edit", account=obj)|accounting_inherit_next }}">
|
<a class="btn btn-primary" role="button" href="{{ url_for("accounting.account.edit", account=obj)|accounting_inherit_next }}" aria-label="{{ A_("Edit") }}">
|
||||||
<i class="fa-solid fa-pen-to-square"></i>
|
<i class="fa-solid fa-pen-to-square" aria-hidden="true"></i>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ First written: 2023/2/1
|
|||||||
{% extends "accounting/base.html" %}
|
{% extends "accounting/base.html" %}
|
||||||
|
|
||||||
{% block accounting_scripts %}
|
{% block accounting_scripts %}
|
||||||
|
<script src="{{ url_for("accounting.static", filename="js/base-combobox.js") }}"></script>
|
||||||
<script src="{{ url_for("accounting.static", filename="js/account-form.js") }}"></script>
|
<script src="{{ url_for("accounting.static", filename="js/account-form.js") }}"></script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
@@ -29,7 +30,7 @@ First written: 2023/2/1
|
|||||||
|
|
||||||
<div class="mb-3 accounting-toolbar">
|
<div class="mb-3 accounting-toolbar">
|
||||||
<a class="btn btn-primary" role="button" href="{% block back_url %}{% endblock %}">
|
<a class="btn btn-primary" role="button" href="{% block back_url %}{% endblock %}">
|
||||||
<i class="fa-solid fa-circle-chevron-left"></i>
|
<i class="fa-solid fa-circle-chevron-left" aria-hidden="true"></i>
|
||||||
<span class="d-none d-md-inline">{{ A_("Back") }}</span>
|
<span class="d-none d-md-inline">{{ A_("Back") }}</span>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
@@ -41,9 +42,9 @@ First written: 2023/2/1
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
<div class="form-floating mb-3">
|
<div class="form-floating mb-3">
|
||||||
<input id="accounting-base-code" type="hidden" name="base_code" value="{{ form.base_code.data|accounting_default }}">
|
<input id="accounting-base-code" type="hidden" name="base_code" value="{{ form.base_code.data|accounting_default }}">
|
||||||
<div id="accounting-base-control" class="form-control accounting-clickable accounting-material-text-field {% if form.base_code.data %} accounting-not-empty {% endif %} {% if form.base_code.errors %} is-invalid {% endif %}" data-bs-toggle="modal" data-bs-target="#accounting-base-selector-modal">
|
<button id="accounting-base-control" class="form-control text-start accounting-material-text-field {% if form.base_code.data %} accounting-not-empty {% endif %} {% if form.base_code.errors %} is-invalid {% endif %}" type="button" data-bs-toggle="modal" data-bs-target="#accounting-base-selector-modal">
|
||||||
<label class="form-label" for="accounting-base">{{ A_("Base account") }}</label>
|
<span class="form-label">{{ A_("Base account") }}</span>
|
||||||
<div id="accounting-base">
|
<span id="accounting-base">
|
||||||
{% if form.base_code.data %}
|
{% if form.base_code.data %}
|
||||||
{% if form.base_code.errors %}
|
{% if form.base_code.errors %}
|
||||||
{{ A_("(Unknown)") }}
|
{{ A_("(Unknown)") }}
|
||||||
@@ -51,15 +52,15 @@ First written: 2023/2/1
|
|||||||
{{ form.selected_base }}
|
{{ form.selected_base }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</span>
|
||||||
</div>
|
</button>
|
||||||
<div id="accounting-base-error" class="invalid-feedback">{% if form.base_code.errors %}{{ form.base_code.errors[0] }}{% endif %}</div>
|
<div id="accounting-base-error" class="invalid-feedback" role="alert">{% if form.base_code.errors %}{{ form.base_code.errors[0] }}{% endif %}</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-floating mb-3">
|
<div class="form-floating mb-3">
|
||||||
<input id="accounting-title" class="form-control {% if form.title.errors %} is-invalid {% endif %}" type="text" name="title" value="{{ form.title.data|accounting_default }}" placeholder=" " required="required">
|
<input id="accounting-title" class="form-control {% if form.title.errors %} is-invalid {% endif %}" type="text" name="title" value="{{ form.title.data|accounting_default }}" placeholder=" " required="required">
|
||||||
<label class="form-label" for="accounting-title">{{ A_("Title") }}</label>
|
<label class="form-label" for="accounting-title">{{ A_("Title") }}</label>
|
||||||
<div id="accounting-title-error" class="invalid-feedback">{% if form.title.errors %}{{ form.title.errors[0] }}{% endif %}</div>
|
<div id="accounting-title-error" class="invalid-feedback" role="alert">{% if form.title.errors %}{{ form.title.errors[0] }}{% endif %}</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="accounting-is-need-offset-control" class="form-check form-switch mb-3 {% if form.base_code.data[0] not in ["1", "2", "3"] %} d-none {% endif %}">
|
<div id="accounting-is-need-offset-control" class="form-check form-switch mb-3 {% if form.base_code.data[0] not in ["1", "2", "3"] %} d-none {% endif %}">
|
||||||
@@ -71,14 +72,14 @@ First written: 2023/2/1
|
|||||||
|
|
||||||
<div class="d-none d-md-block">
|
<div class="d-none d-md-block">
|
||||||
<button class="btn btn-primary" type="submit">
|
<button class="btn btn-primary" type="submit">
|
||||||
<i class="fa-solid fa-floppy-disk"></i>
|
<i class="fa-solid fa-floppy-disk" aria-hidden="true"></i>
|
||||||
{{ A_("Save") }}
|
{{ A_("Save") }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="d-md-none accounting-material-fab">
|
<div class="d-md-none accounting-material-fab">
|
||||||
<button class="btn btn-primary" type="submit">
|
<button class="btn btn-primary" type="submit" aria-label="{{ A_("Save") }}">
|
||||||
<i class="fa-solid fa-floppy-disk"></i>
|
<i class="fa-solid fa-floppy-disk" aria-hidden="true"></i>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
@@ -92,21 +93,21 @@ First written: 2023/2/1
|
|||||||
</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-base-selector-query" class="form-control form-control-sm" type="search" placeholder=" " required="required">
|
<input id="accounting-base-selector-query" class="form-control form-control-sm" type="search" placeholder=" " required="required" role="combobox" aria-expanded="true" aria-controls="accounting-base-selector-option-list" aria-autocomplete="list" aria-activedescendant="">
|
||||||
<label class="input-group-text" for="accounting-base-selector-query">
|
<label class="input-group-text" for="accounting-base-selector-query">
|
||||||
<i class="fa-solid fa-magnifying-glass"></i>
|
<i class="fa-solid fa-magnifying-glass" aria-hidden="true"></i>
|
||||||
{{ A_("Search") }}
|
{{ A_("Search") }}
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<ul id="accounting-base-selector-option-list" class="list-group accounting-selector-list">
|
<ul id="accounting-base-selector-option-list" class="list-group accounting-selector-list" role="listbox" tabindex="-1" aria-labelledby="accounting-base-selector-modal-label">
|
||||||
{% for base in form.base_options %}
|
{% for base in form.base_options %}
|
||||||
<li class="list-group-item accounting-clickable accounting-base-selector-option" data-code="{{ base.code }}" data-text="{{ base }}" data-query-values="{{ base.query_values|tojson|forceescape }}" data-bs-dismiss="modal">
|
<li id="accounting-base-selector-option-{{ base.code }}" class="list-group-item accounting-clickable accounting-base-selector-option" role="option" aria-selected="false" data-code="{{ base.code }}" data-text="{{ base }}" data-query-values="{{ base.query_values|tojson|forceescape }}" data-bs-dismiss="modal">
|
||||||
{{ base }}
|
{{ base }}
|
||||||
</li>
|
</li>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</ul>
|
</ul>
|
||||||
<p id="accounting-base-selector-option-no-result" class="d-none">{{ A_("There is no data.") }}</p>
|
<p id="accounting-base-selector-option-no-result" class="d-none" role="status">{{ 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-dismiss="modal">{{ A_("Cancel") }}</button>
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">{{ A_("Cancel") }}</button>
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ First written: 2023/1/30
|
|||||||
<div class="mb-2 accounting-toolbar">
|
<div class="mb-2 accounting-toolbar">
|
||||||
{% if accounting_can_edit() %}
|
{% if accounting_can_edit() %}
|
||||||
<a class="btn btn-primary text-nowrap d-none d-md-block" role="button" href="{{ url_for("accounting.account.create")|accounting_append_next }}">
|
<a class="btn btn-primary text-nowrap d-none d-md-block" role="button" href="{{ url_for("accounting.account.create")|accounting_append_next }}">
|
||||||
<i class="fa-solid fa-plus"></i>
|
<i class="fa-solid fa-plus" aria-hidden="true"></i>
|
||||||
{{ A_("New") }}
|
{{ A_("New") }}
|
||||||
</a>
|
</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
@@ -36,7 +36,7 @@ First written: 2023/1/30
|
|||||||
<input id="accounting-toolbar-search" class="form-control form-control-sm" type="search" name="q" value="{{ request.args.q }}" placeholder=" " required="required">
|
<input id="accounting-toolbar-search" class="form-control form-control-sm" type="search" name="q" value="{{ request.args.q }}" placeholder=" " required="required">
|
||||||
<label id="accounting-toolbar-search-label" for="accounting-toolbar-search" class="input-group-text">
|
<label id="accounting-toolbar-search-label" for="accounting-toolbar-search" class="input-group-text">
|
||||||
<button type="submit">
|
<button type="submit">
|
||||||
<i class="fa-solid fa-magnifying-glass"></i>
|
<i class="fa-solid fa-magnifying-glass" aria-hidden="true"></i>
|
||||||
<span class="d-none d-md-inline">{{ A_("Search") }}</span>
|
<span class="d-none d-md-inline">{{ A_("Search") }}</span>
|
||||||
</button>
|
</button>
|
||||||
</label>
|
</label>
|
||||||
@@ -45,8 +45,8 @@ First written: 2023/1/30
|
|||||||
|
|
||||||
{% if accounting_can_edit() %}
|
{% if accounting_can_edit() %}
|
||||||
<div class="d-md-none accounting-material-fab">
|
<div class="d-md-none accounting-material-fab">
|
||||||
<a class="btn btn-primary" role="button" href="{{ url_for("accounting.account.create")|accounting_append_next }}">
|
<a class="btn btn-primary" role="button" href="{{ url_for("accounting.account.create")|accounting_append_next }}" aria-label="{{ A_("New") }}">
|
||||||
<i class="fa-solid fa-plus"></i>
|
<i class="fa-solid fa-plus" aria-hidden="true"></i>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ First written: 2023/2/2
|
|||||||
|
|
||||||
<div class="mb-3 accounting-toolbar">
|
<div class="mb-3 accounting-toolbar">
|
||||||
<a class="btn btn-primary" role="button" href="{{ url_for("accounting.account.list")|accounting_or_next }}">
|
<a class="btn btn-primary" role="button" href="{{ url_for("accounting.account.list")|accounting_or_next }}">
|
||||||
<i class="fa-solid fa-circle-chevron-left"></i>
|
<i class="fa-solid fa-circle-chevron-left" aria-hidden="true"></i>
|
||||||
<span class="d-none d-md-inline">{{ A_("Back") }}</span>
|
<span class="d-none d-md-inline">{{ A_("Back") }}</span>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
@@ -51,21 +51,21 @@ First written: 2023/2/2
|
|||||||
<span id="accounting-order-{{ account.id }}-code">{{ account.code }}</span>
|
<span id="accounting-order-{{ account.id }}-code">{{ account.code }}</span>
|
||||||
{{ account.title }}
|
{{ account.title }}
|
||||||
</div>
|
</div>
|
||||||
<i class="fa-solid fa-bars"></i>
|
<i class="fa-solid fa-bars" aria-hidden="true"></i>
|
||||||
</li>
|
</li>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<div class="d-none d-md-block">
|
<div class="d-none d-md-block">
|
||||||
<button class="btn btn-primary" type="submit">
|
<button class="btn btn-primary" type="submit">
|
||||||
<i class="fa-solid fa-floppy-disk"></i>
|
<i class="fa-solid fa-floppy-disk" aria-hidden="true"></i>
|
||||||
{{ A_("Save") }}
|
{{ A_("Save") }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="d-md-none accounting-material-fab">
|
<div class="d-md-none accounting-material-fab">
|
||||||
<button class="btn btn-primary" type="submit">
|
<button class="btn btn-primary" type="submit" aria-label="{{ A_("Save") }}">
|
||||||
<i class="fa-solid fa-floppy-disk"></i>
|
<i class="fa-solid fa-floppy-disk" aria-hidden="true"></i>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ First written: 2023/2/1
|
|||||||
|
|
||||||
<div class="mb-3 accounting-toolbar">
|
<div class="mb-3 accounting-toolbar">
|
||||||
<a class="btn btn-primary" role="button" href="{{ url_for("accounting.account.list")|accounting_or_next }}">
|
<a class="btn btn-primary" role="button" href="{{ url_for("accounting.account.list")|accounting_or_next }}">
|
||||||
<i class="fa-solid fa-circle-chevron-left"></i>
|
<i class="fa-solid fa-circle-chevron-left" aria-hidden="true"></i>
|
||||||
<span class="d-none d-md-inline">{{ A_("Back") }}</span>
|
<span class="d-none d-md-inline">{{ A_("Back") }}</span>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ First written: 2023/1/26
|
|||||||
<input id="accounting-toolbar-search" class="form-control form-control-sm" type="search" name="q" value="{{ request.args.q }}" placeholder=" " required="required">
|
<input id="accounting-toolbar-search" class="form-control form-control-sm" type="search" name="q" value="{{ request.args.q }}" placeholder=" " required="required">
|
||||||
<label id="accounting-toolbar-search-label" for="accounting-toolbar-search" class="input-group-text">
|
<label id="accounting-toolbar-search-label" for="accounting-toolbar-search" class="input-group-text">
|
||||||
<button type="submit">
|
<button type="submit">
|
||||||
<i class="fa-solid fa-magnifying-glass"></i>
|
<i class="fa-solid fa-magnifying-glass" aria-hidden="true"></i>
|
||||||
<span class="d-none d-md-inline">{{ A_("Search") }}</span>
|
<span class="d-none d-md-inline">{{ A_("Search") }}</span>
|
||||||
</button>
|
</button>
|
||||||
</label>
|
</label>
|
||||||
|
|||||||
@@ -27,24 +27,24 @@ First written: 2023/2/6
|
|||||||
|
|
||||||
<div class="mb-3 accounting-toolbar">
|
<div class="mb-3 accounting-toolbar">
|
||||||
<a class="btn btn-primary" role="button" href="{{ url_for("accounting.currency.list")|accounting_or_next }}">
|
<a class="btn btn-primary" role="button" href="{{ url_for("accounting.currency.list")|accounting_or_next }}">
|
||||||
<i class="fa-solid fa-circle-chevron-left"></i>
|
<i class="fa-solid fa-circle-chevron-left" aria-hidden="true"></i>
|
||||||
<span class="d-none d-md-inline">{{ A_("Back") }}</span>
|
<span class="d-none d-md-inline">{{ A_("Back") }}</span>
|
||||||
</a>
|
</a>
|
||||||
{% if accounting_can_edit() %}
|
{% if accounting_can_edit() %}
|
||||||
<a class="btn btn-primary d-none d-md-inline" role="button" href="{{ url_for("accounting.currency.edit", currency=obj)|accounting_inherit_next }}">
|
<a class="btn btn-primary d-none d-md-inline" role="button" href="{{ url_for("accounting.currency.edit", currency=obj)|accounting_inherit_next }}">
|
||||||
<i class="fa-solid fa-pen-to-square"></i>
|
<i class="fa-solid fa-pen-to-square" aria-hidden="true"></i>
|
||||||
{{ A_("Edit") }}
|
{{ A_("Edit") }}
|
||||||
</a>
|
</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if accounting_can_edit() %}
|
{% if accounting_can_edit() %}
|
||||||
{% if obj.can_delete %}
|
{% if obj.can_delete %}
|
||||||
<button class="btn btn-danger" type="button" data-bs-toggle="modal" data-bs-target="#accounting-delete-modal">
|
<button class="btn btn-danger" type="button" data-bs-toggle="modal" data-bs-target="#accounting-delete-modal">
|
||||||
<i class="fa-solid fa-trash"></i>
|
<i class="fa-solid fa-trash" aria-hidden="true"></i>
|
||||||
<span class="d-none d-md-inline">{{ A_("Delete") }}</span>
|
<span class="d-none d-md-inline">{{ A_("Delete") }}</span>
|
||||||
</button>
|
</button>
|
||||||
{% else %}
|
{% else %}
|
||||||
<button class="btn btn-secondary" type="button" disabled="disabled">
|
<button class="btn btn-secondary" type="button" disabled="disabled">
|
||||||
<i class="fa-solid fa-trash"></i>
|
<i class="fa-solid fa-trash" aria-hidden="true"></i>
|
||||||
<span class="d-none d-md-inline">{{ A_("Delete") }}</span>
|
<span class="d-none d-md-inline">{{ A_("Delete") }}</span>
|
||||||
</button>
|
</button>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
@@ -53,8 +53,8 @@ First written: 2023/2/6
|
|||||||
|
|
||||||
{% if accounting_can_edit() %}
|
{% if accounting_can_edit() %}
|
||||||
<div class="d-md-none accounting-material-fab">
|
<div class="d-md-none accounting-material-fab">
|
||||||
<a class="btn btn-primary" role="button" href="{{ url_for("accounting.currency.edit", currency=obj)|accounting_inherit_next }}">
|
<a class="btn btn-primary" role="button" href="{{ url_for("accounting.currency.edit", currency=obj)|accounting_inherit_next }}" aria-label="{{ A_("Edit") }}">
|
||||||
<i class="fa-solid fa-pen-to-square"></i>
|
<i class="fa-solid fa-pen-to-square" aria-hidden="true"></i>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ First written: 2023/2/6
|
|||||||
|
|
||||||
<div class="mb-3 accounting-toolbar">
|
<div class="mb-3 accounting-toolbar">
|
||||||
<a class="btn btn-primary" role="button" href="{% block back_url %}{% endblock %}">
|
<a class="btn btn-primary" role="button" href="{% block back_url %}{% endblock %}">
|
||||||
<i class="fa-solid fa-circle-chevron-left"></i>
|
<i class="fa-solid fa-circle-chevron-left" aria-hidden="true"></i>
|
||||||
<span class="d-none d-md-inline">{{ A_("Back") }}</span>
|
<span class="d-none d-md-inline">{{ A_("Back") }}</span>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
@@ -42,25 +42,25 @@ First written: 2023/2/6
|
|||||||
<div class="form-floating mb-3">
|
<div class="form-floating mb-3">
|
||||||
<input id="accounting-code" class="form-control {% if form.code.errors %} is-invalid {% endif %}" type="text" name="code" value="{{ form.code.data|accounting_default }}" placeholder=" " required="required" data-exists-url="{{ url_for("accounting.currency-api.exists") }}" data-original="{% block original_code %}{% endblock %}" data-blocklist="{{ form.CODE_BLOCKLIST|tojson|forceescape }}">
|
<input id="accounting-code" class="form-control {% if form.code.errors %} is-invalid {% endif %}" type="text" name="code" value="{{ form.code.data|accounting_default }}" placeholder=" " required="required" data-exists-url="{{ url_for("accounting.currency-api.exists") }}" data-original="{% block original_code %}{% endblock %}" data-blocklist="{{ form.CODE_BLOCKLIST|tojson|forceescape }}">
|
||||||
<label class="form-label" for="accounting-code">{{ A_("Code") }}</label>
|
<label class="form-label" for="accounting-code">{{ A_("Code") }}</label>
|
||||||
<div id="accounting-code-error" class="invalid-feedback">{% if form.code.errors %}{{ form.code.errors[0] }}{% endif %}</div>
|
<div id="accounting-code-error" class="invalid-feedback" role="alert">{% if form.code.errors %}{{ form.code.errors[0] }}{% endif %}</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-floating mb-3">
|
<div class="form-floating mb-3">
|
||||||
<input id="accounting-name" class="form-control {% if form.name.errors %} is-invalid {% endif %}" type="text" name="name" value="{{ form.name.data|accounting_default }}" placeholder=" " required="required">
|
<input id="accounting-name" class="form-control {% if form.name.errors %} is-invalid {% endif %}" type="text" name="name" value="{{ form.name.data|accounting_default }}" placeholder=" " required="required">
|
||||||
<label class="form-label" for="accounting-name">{{ A_("Name") }}</label>
|
<label class="form-label" for="accounting-name">{{ A_("Name") }}</label>
|
||||||
<div id="accounting-name-error" class="invalid-feedback">{% if form.name.errors %}{{ form.name.errors[0] }}{% endif %}</div>
|
<div id="accounting-name-error" class="invalid-feedback" role="alert">{% if form.name.errors %}{{ form.name.errors[0] }}{% endif %}</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="d-none d-md-block">
|
<div class="d-none d-md-block">
|
||||||
<button class="btn btn-primary" type="submit">
|
<button class="btn btn-primary" type="submit">
|
||||||
<i class="fa-solid fa-floppy-disk"></i>
|
<i class="fa-solid fa-floppy-disk" aria-hidden="true"></i>
|
||||||
{{ A_("Save") }}
|
{{ A_("Save") }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="d-md-none accounting-material-fab">
|
<div class="d-md-none accounting-material-fab">
|
||||||
<button class="btn btn-primary" type="submit">
|
<button class="btn btn-primary" type="submit" aria-label="{{ A_("Save") }}">
|
||||||
<i class="fa-solid fa-floppy-disk"></i>
|
<i class="fa-solid fa-floppy-disk" aria-hidden="true"></i>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ First written: 2023/2/6
|
|||||||
<div class="mb-2 accounting-toolbar">
|
<div class="mb-2 accounting-toolbar">
|
||||||
{% if accounting_can_edit() %}
|
{% if accounting_can_edit() %}
|
||||||
<a class="btn btn-primary text-nowrap d-none d-md-block" role="button" href="{{ url_for("accounting.currency.create")|accounting_append_next }}">
|
<a class="btn btn-primary text-nowrap d-none d-md-block" role="button" href="{{ url_for("accounting.currency.create")|accounting_append_next }}">
|
||||||
<i class="fa-solid fa-plus"></i>
|
<i class="fa-solid fa-plus" aria-hidden="true"></i>
|
||||||
{{ A_("New") }}
|
{{ A_("New") }}
|
||||||
</a>
|
</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
@@ -36,7 +36,7 @@ First written: 2023/2/6
|
|||||||
<input id="accounting-toolbar-search" class="form-control form-control-sm" type="search" name="q" value="{{ request.args.q }}" placeholder=" " required="required">
|
<input id="accounting-toolbar-search" class="form-control form-control-sm" type="search" name="q" value="{{ request.args.q }}" placeholder=" " required="required">
|
||||||
<label id="accounting-toolbar-search-label" for="accounting-toolbar-search" class="input-group-text">
|
<label id="accounting-toolbar-search-label" for="accounting-toolbar-search" class="input-group-text">
|
||||||
<button type="submit">
|
<button type="submit">
|
||||||
<i class="fa-solid fa-magnifying-glass"></i>
|
<i class="fa-solid fa-magnifying-glass" aria-hidden="true"></i>
|
||||||
<span class="d-none d-md-inline">{{ A_("Search") }}</span>
|
<span class="d-none d-md-inline">{{ A_("Search") }}</span>
|
||||||
</button>
|
</button>
|
||||||
</label>
|
</label>
|
||||||
@@ -45,8 +45,8 @@ First written: 2023/2/6
|
|||||||
|
|
||||||
{% if accounting_can_edit() %}
|
{% if accounting_can_edit() %}
|
||||||
<div class="d-md-none accounting-material-fab">
|
<div class="d-md-none accounting-material-fab">
|
||||||
<a class="btn btn-primary" role="button" href="{{ url_for("accounting.currency.create")|accounting_append_next }}">
|
<a class="btn btn-primary" role="button" href="{{ url_for("accounting.currency.create")|accounting_append_next }}" aria-label="{{ A_("New") }}">
|
||||||
<i class="fa-solid fa-plus"></i>
|
<i class="fa-solid fa-plus" aria-hidden="true"></i>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|||||||
@@ -22,39 +22,39 @@ First written: 2023/1/26
|
|||||||
{# <ul> For SonarQube not to complain about incorrect HTML #}
|
{# <ul> For SonarQube not to complain about incorrect HTML #}
|
||||||
{% if accounting_can_view() %}
|
{% if accounting_can_view() %}
|
||||||
<li class="nav-item dropdown">
|
<li class="nav-item dropdown">
|
||||||
<span class="nav-link dropdown-toggle" data-bs-toggle="dropdown">
|
<button class="nav-link dropdown-toggle" type="button" data-bs-toggle="dropdown" aria-expanded="false">
|
||||||
<i class="fa-solid fa-file-invoice-dollar"></i>
|
<i class="fa-solid fa-file-invoice-dollar" aria-hidden="true"></i>
|
||||||
{{ A_("Accounting") }}
|
{{ A_("Accounting") }}
|
||||||
</span>
|
</button>
|
||||||
<ul class="dropdown-menu">
|
<ul class="dropdown-menu">
|
||||||
<li>
|
<li>
|
||||||
<a class="dropdown-item {% if request.endpoint and request.endpoint.startswith("accounting-report.") %} active {% endif %}" href="{{ url_for("accounting-report.default") }}">
|
<a class="dropdown-item {% if request.endpoint and request.endpoint.startswith("accounting-report.") %} active {% endif %}" href="{{ url_for("accounting-report.default") }}">
|
||||||
<i class="fa-solid fa-book"></i>
|
<i class="fa-solid fa-book" aria-hidden="true"></i>
|
||||||
{{ A_("Reports") }}
|
{{ A_("Reports") }}
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<a class="dropdown-item {% if request.endpoint and request.endpoint.startswith("accounting.account.") %} active {% endif %}" href="{{ url_for("accounting.account.list") }}">
|
<a class="dropdown-item {% if request.endpoint and request.endpoint.startswith("accounting.account.") %} active {% endif %}" href="{{ url_for("accounting.account.list") }}">
|
||||||
<i class="fa-solid fa-clipboard"></i>
|
<i class="fa-solid fa-clipboard" aria-hidden="true"></i>
|
||||||
{{ A_("Accounts") }}
|
{{ A_("Accounts") }}
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<a class="dropdown-item {% if request.endpoint and request.endpoint.startswith("accounting.base-account.") %} active {% endif %}" href="{{ url_for("accounting.base-account.list") }}">
|
<a class="dropdown-item {% if request.endpoint and request.endpoint.startswith("accounting.base-account.") %} active {% endif %}" href="{{ url_for("accounting.base-account.list") }}">
|
||||||
<i class="fa-solid fa-list"></i>
|
<i class="fa-solid fa-list" aria-hidden="true"></i>
|
||||||
{{ A_("Base Accounts") }}
|
{{ A_("Base Accounts") }}
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<a class="dropdown-item {% if request.endpoint and request.endpoint.startswith("accounting.currency.") %} active {% endif %}" href="{{ url_for("accounting.currency.list") }}">
|
<a class="dropdown-item {% if request.endpoint and request.endpoint.startswith("accounting.currency.") %} active {% endif %}" href="{{ url_for("accounting.currency.list") }}">
|
||||||
<i class="fa-solid fa-money-bill-wave"></i>
|
<i class="fa-solid fa-money-bill-wave" aria-hidden="true"></i>
|
||||||
{{ A_("Currencies") }}
|
{{ A_("Currencies") }}
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
{% if accounting_can_admin() %}
|
{% if accounting_can_admin() %}
|
||||||
<li>
|
<li>
|
||||||
<a class="dropdown-item {% if request.endpoint and request.endpoint.startswith("accounting.option.") %} active {% endif %}" href="{{ url_for("accounting.option.detail") }}">
|
<a class="dropdown-item {% if request.endpoint and request.endpoint.startswith("accounting.option.") %} active {% endif %}" href="{{ url_for("accounting.option.detail") }}">
|
||||||
<i class="fa-solid fa-gear"></i>
|
<i class="fa-solid fa-gear" aria-hidden="true"></i>
|
||||||
{{ A_("Settings") }}
|
{{ A_("Settings") }}
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ First written: 2023/1/26
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
<li class="page-item d-none d-md-inline active dropdown">
|
<li class="page-item d-none d-md-inline active dropdown">
|
||||||
<div class="page-link dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false">
|
<div class="page-link dropdown-toggle" role="button" tabindex="0" data-bs-toggle="dropdown" aria-expanded="false">
|
||||||
{{ pagination.page_size }}
|
{{ pagination.page_size }}
|
||||||
</div>
|
</div>
|
||||||
<ul class="dropdown-menu">
|
<ul class="dropdown-menu">
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ First written: 2023/2/26
|
|||||||
|
|
||||||
{% block as_transfer %}
|
{% block as_transfer %}
|
||||||
<a class="btn btn-primary" role="button" href="{{ url_for("accounting.journal-entry.edit", journal_entry=obj)|accounting_journal_entry_to_transfer|accounting_inherit_next }}">
|
<a class="btn btn-primary" role="button" href="{{ url_for("accounting.journal-entry.edit", journal_entry=obj)|accounting_journal_entry_to_transfer|accounting_inherit_next }}">
|
||||||
<i class="fa-solid fa-table-columns"></i>
|
<i class="fa-solid fa-table-columns" aria-hidden="true"></i>
|
||||||
<span class="d-none d-md-inline">{{ A_("As Transfer") }}</span>
|
<span class="d-none d-md-inline">{{ A_("As Transfer") }}</span>
|
||||||
</a>
|
</a>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|||||||
+8
-8
@@ -19,31 +19,31 @@ 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-{{ 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 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" data-bs-keyboard="false">
|
||||||
<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-{{ debit_credit }}-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 id="accounting-account-selector-{{ debit_credit }}-close" 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-{{ debit_credit }}-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" role="combobox" aria-expanded="true" aria-controls="accounting-account-selector-{{ debit_credit }}-option-list" aria-autocomplete="list" aria-activedescendant="">
|
||||||
<label class="input-group-text" for="accounting-account-selector-{{ debit_credit }}-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" aria-hidden="true"></i>
|
||||||
{{ A_("Search") }}
|
{{ A_("Search") }}
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<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" role="listbox" tabindex="-1" aria-labelledby="accounting-account-selector-{{ debit_credit }}-modal-label">
|
||||||
{% 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-is-in-use {% endif %} {% if account.is_need_offset %} accounting-account-is-need-offset {% endif %}" data-code="{{ account.code }}" data-title="{{ account.title }}" 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 %}" role="option" aria-selected="false" data-code="{{ account.code }}" data-title="{{ account.title }}" 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 %}
|
||||||
<li id="accounting-account-selector-{{ debit_credit }}-more" class="list-group-item accounting-clickable">{{ A_("More…") }}</li>
|
<li id="accounting-account-selector-{{ debit_credit }}-more" class="list-group-item accounting-clickable" role="option" aria-selected="false">{{ A_("More…") }}</li>
|
||||||
</ul>
|
</ul>
|
||||||
<p id="accounting-account-selector-{{ debit_credit }}-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" role="status">{{ 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>
|
||||||
|
|||||||
+28
-28
@@ -21,14 +21,14 @@ First written: 2023/2/28
|
|||||||
#}
|
#}
|
||||||
<form id="accounting-description-editor-{{ description_editor.debit_credit }}" class="accounting-description-editor" data-debit-credit="{{ description_editor.debit_credit }}">
|
<form id="accounting-description-editor-{{ description_editor.debit_credit }}" class="accounting-description-editor" data-debit-credit="{{ description_editor.debit_credit }}">
|
||||||
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
|
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
|
||||||
<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 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" data-bs-keyboard="false">
|
||||||
<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.debit_credit }}-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.debit_credit }}-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 id="accounting-description-editor-{{ description_editor.debit_credit }}-close" 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">
|
||||||
@@ -39,40 +39,40 @@ First written: 2023/2/28
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{# Tab navigation #}
|
{# Tab navigation #}
|
||||||
<ul class="nav nav-tabs mb-2">
|
<ul id="accounting-description-editor-{{ description_editor.debit_credit }}-tab-list" class="nav nav-tabs mb-2" role="tablist" aria-label="{{ A_("Description Type") }}">
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<span id="accounting-description-editor-{{ description_editor.debit_credit }}-general-tab" class="nav-link active accounting-clickable" aria-current="page">
|
<button id="accounting-description-editor-{{ description_editor.debit_credit }}-general-tab" class="nav-link active accounting-clickable" type="button" tabindex="0" role="tab" aria-controls="accounting-description-editor-{{ description_editor.debit_credit }}-general-panel" aria-selected="true">
|
||||||
{{ A_("General") }}
|
{{ A_("General") }}
|
||||||
</span>
|
</button>
|
||||||
</li>
|
</li>
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<span id="accounting-description-editor-{{ description_editor.debit_credit }}-travel-tab" class="nav-link accounting-clickable" aria-current="false">
|
<button id="accounting-description-editor-{{ description_editor.debit_credit }}-travel-tab" class="nav-link accounting-clickable" type="button" tabindex="-1" role="tab" aria-controls="accounting-description-editor-{{ description_editor.debit_credit }}-travel-panel" aria-selected="false">
|
||||||
{{ A_("Travel") }}
|
{{ A_("Travel") }}
|
||||||
</span>
|
</button>
|
||||||
</li>
|
</li>
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<span id="accounting-description-editor-{{ description_editor.debit_credit }}-bus-tab" class="nav-link accounting-clickable" aria-current="false">
|
<button id="accounting-description-editor-{{ description_editor.debit_credit }}-bus-tab" class="nav-link accounting-clickable" type="button" tabindex="-1" role="tab" aria-controls="accounting-description-editor-{{ description_editor.debit_credit }}-bus-panel" aria-selected="false">
|
||||||
{{ A_("Bus") }}
|
{{ A_("Bus") }}
|
||||||
</span>
|
</button>
|
||||||
</li>
|
</li>
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<span id="accounting-description-editor-{{ description_editor.debit_credit }}-recurring-tab" class="nav-link accounting-clickable" aria-current="false">
|
<button id="accounting-description-editor-{{ description_editor.debit_credit }}-recurring-tab" class="nav-link accounting-clickable" type="button" tabindex="-1" role="tab" aria-controls="accounting-description-editor-{{ description_editor.debit_credit }}-recurring-panel" aria-selected="false">
|
||||||
{{ A_("Recurring") }}
|
{{ A_("Recurring") }}
|
||||||
</span>
|
</button>
|
||||||
</li>
|
</li>
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<span id="accounting-description-editor-{{ description_editor.debit_credit }}-annotation-tab" class="nav-link accounting-clickable" aria-current="false">
|
<button id="accounting-description-editor-{{ description_editor.debit_credit }}-annotation-tab" class="nav-link accounting-clickable" type="button" tabindex="-1" role="tab" aria-controls="accounting-description-editor-{{ description_editor.debit_credit }}-annotation-panel" aria-selected="false">
|
||||||
{{ A_("Annotation") }}
|
{{ A_("Annotation") }}
|
||||||
</span>
|
</button>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
{# A general description with a tag #}
|
{# A general description with a tag #}
|
||||||
<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 id="accounting-description-editor-{{ description_editor.debit_credit }}-general-panel" role="tabpanel" 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.debit_credit }}-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.debit_credit }}-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.debit_credit }}-general-tag-error" class="invalid-feedback"></div>
|
<div id="accounting-description-editor-{{ description_editor.debit_credit }}-general-tag-error" class="invalid-feedback" role="alert"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="accounting-description-editor-buttons">
|
<div class="accounting-description-editor-buttons">
|
||||||
@@ -85,11 +85,11 @@ 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.debit_credit }}-travel-page" class="d-none" aria-current="false" aria-labelledby="accounting-description-editor-{{ description_editor.debit_credit }}-travel-tab">
|
<div id="accounting-description-editor-{{ description_editor.debit_credit }}-travel-panel" class="d-none" role="tabpanel" 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.debit_credit }}-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.debit_credit }}-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.debit_credit }}-travel-tag-error" class="invalid-feedback"></div>
|
<div id="accounting-description-editor-{{ description_editor.debit_credit }}-travel-tag-error" class="invalid-feedback" role="alert"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="accounting-description-editor-buttons">
|
<div class="accounting-description-editor-buttons">
|
||||||
@@ -104,7 +104,7 @@ First written: 2023/2/28
|
|||||||
<div class="form-floating">
|
<div class="form-floating">
|
||||||
<input id="accounting-description-editor-{{ description_editor.debit_credit }}-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.debit_credit }}-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.debit_credit }}-travel-from-error" class="invalid-feedback"></div>
|
<div id="accounting-description-editor-{{ description_editor.debit_credit }}-travel-from-error" class="invalid-feedback" role="alert"></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.debit_credit }}-travel-direction accounting-default" type="button" tabindex="-1" data-arrow="→">→</button>
|
<button class="btn btn-primary accounting-description-editor-{{ description_editor.debit_credit }}-travel-direction accounting-default" type="button" tabindex="-1" data-arrow="→">→</button>
|
||||||
@@ -113,23 +113,23 @@ First written: 2023/2/28
|
|||||||
<div class="form-floating">
|
<div class="form-floating">
|
||||||
<input id="accounting-description-editor-{{ description_editor.debit_credit }}-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.debit_credit }}-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.debit_credit }}-travel-to-error" class="invalid-feedback"></div>
|
<div id="accounting-description-editor-{{ description_editor.debit_credit }}-travel-to-error" class="invalid-feedback" role="alert"></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.debit_credit }}-bus-page" class="d-none" aria-current="false" aria-labelledby="accounting-description-editor-{{ description_editor.debit_credit }}-bus-tab">
|
<div id="accounting-description-editor-{{ description_editor.debit_credit }}-bus-panel" class="d-none" role="tabpanel" 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.debit_credit }}-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.debit_credit }}-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.debit_credit }}-bus-tag-error" class="invalid-feedback"></div>
|
<div id="accounting-description-editor-{{ description_editor.debit_credit }}-bus-tag-error" class="invalid-feedback" role="alert"></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-floating">
|
<div class="form-floating">
|
||||||
<input id="accounting-description-editor-{{ description_editor.debit_credit }}-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.debit_credit }}-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.debit_credit }}-bus-route-error" class="invalid-feedback"></div>
|
<div id="accounting-description-editor-{{ description_editor.debit_credit }}-bus-route-error" class="invalid-feedback" role="alert"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -145,18 +145,18 @@ First written: 2023/2/28
|
|||||||
<div class="form-floating me-2">
|
<div class="form-floating me-2">
|
||||||
<input id="accounting-description-editor-{{ description_editor.debit_credit }}-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.debit_credit }}-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.debit_credit }}-bus-from-error" class="invalid-feedback"></div>
|
<div id="accounting-description-editor-{{ description_editor.debit_credit }}-bus-from-error" class="invalid-feedback" role="alert"></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-floating">
|
<div class="form-floating">
|
||||||
<input id="accounting-description-editor-{{ description_editor.debit_credit }}-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.debit_credit }}-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.debit_credit }}-bus-to-error" class="invalid-feedback"></div>
|
<div id="accounting-description-editor-{{ description_editor.debit_credit }}-bus-to-error" class="invalid-feedback" role="alert"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{# A recurring transaction #}
|
{# A recurring transaction #}
|
||||||
<div id="accounting-description-editor-{{ description_editor.debit_credit }}-recurring-page" class="d-none" aria-current="false" aria-labelledby="accounting-description-editor-{{ description_editor.debit_credit }}-recurring-tab">
|
<div id="accounting-description-editor-{{ description_editor.debit_credit }}-recurring-panel" class="d-none" role="tabpanel" aria-labelledby="accounting-description-editor-{{ description_editor.debit_credit }}-recurring-tab">
|
||||||
<div class="accounting-description-editor-buttons">
|
<div class="accounting-description-editor-buttons">
|
||||||
{% for recurring in description_editor.recurring %}
|
{% for recurring in description_editor.recurring %}
|
||||||
<button class="btn btn-outline-primary accounting-description-editor-{{ description_editor.debit_credit }}-recurring-item" type="button" tabindex="-1" data-template="{{ recurring.description_template }}" data-accounts="{{ recurring.account_codes|tojson|forceescape }}">
|
<button class="btn btn-outline-primary accounting-description-editor-{{ description_editor.debit_credit }}-recurring-item" type="button" tabindex="-1" data-template="{{ recurring.description_template }}" data-accounts="{{ recurring.account_codes|tojson|forceescape }}">
|
||||||
@@ -167,17 +167,17 @@ First written: 2023/2/28
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{# The annotation #}
|
{# The annotation #}
|
||||||
<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 id="accounting-description-editor-{{ description_editor.debit_credit }}-annotation-panel" class="d-none" role="tabpanel" 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.debit_credit }}-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.debit_credit }}-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.debit_credit }}-annotation-number-error" class="invalid-feedback"></div>
|
<div id="accounting-description-editor-{{ description_editor.debit_credit }}-annotation-number-error" class="invalid-feedback" role="alert"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-floating mt-2">
|
<div class="form-floating mt-2">
|
||||||
<input id="accounting-description-editor-{{ description_editor.debit_credit }}-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.debit_credit }}-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.debit_credit }}-annotation-note-error" class="invalid-feedback"></div>
|
<div id="accounting-description-editor-{{ description_editor.debit_credit }}-annotation-note-error" class="invalid-feedback" role="alert"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -27,29 +27,29 @@ First written: 2023/2/26
|
|||||||
|
|
||||||
<div class="mb-3 accounting-toolbar">
|
<div class="mb-3 accounting-toolbar">
|
||||||
<a class="btn btn-primary" role="button" href="{{ url_for("accounting-report.default")|accounting_or_next }}">
|
<a class="btn btn-primary" role="button" href="{{ url_for("accounting-report.default")|accounting_or_next }}">
|
||||||
<i class="fa-solid fa-circle-chevron-left"></i>
|
<i class="fa-solid fa-circle-chevron-left" aria-hidden="true"></i>
|
||||||
<span class="d-none d-md-inline">{{ A_("Back") }}</span>
|
<span class="d-none d-md-inline">{{ A_("Back") }}</span>
|
||||||
</a>
|
</a>
|
||||||
{% if accounting_can_edit() %}
|
{% if accounting_can_edit() %}
|
||||||
<a class="btn btn-primary d-none d-md-inline" role="button" href="{{ url_for("accounting.journal-entry.edit", journal_entry=obj)|accounting_inherit_next }}">
|
<a class="btn btn-primary d-none d-md-inline" role="button" href="{{ url_for("accounting.journal-entry.edit", journal_entry=obj)|accounting_inherit_next }}">
|
||||||
<i class="fa-solid fa-pen-to-square"></i>
|
<i class="fa-solid fa-pen-to-square" aria-hidden="true"></i>
|
||||||
{{ A_("Edit") }}
|
{{ A_("Edit") }}
|
||||||
</a>
|
</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<a class="btn btn-primary" role="button" href="{{ url_for("accounting.journal-entry.order", date=obj.date)|accounting_append_next }}">
|
<a class="btn btn-primary" role="button" href="{{ url_for("accounting.journal-entry.order", date=obj.date)|accounting_append_next }}">
|
||||||
<i class="fa-solid fa-bars-staggered"></i>
|
<i class="fa-solid fa-bars-staggered" aria-hidden="true"></i>
|
||||||
<span class="d-none d-md-inline">{{ A_("Order") }}</span>
|
<span class="d-none d-md-inline">{{ A_("Order") }}</span>
|
||||||
</a>
|
</a>
|
||||||
{% if accounting_can_edit() %}
|
{% if accounting_can_edit() %}
|
||||||
{% block as_transfer %}{% endblock %}
|
{% block as_transfer %}{% endblock %}
|
||||||
{% if obj.can_delete %}
|
{% if obj.can_delete %}
|
||||||
<button class="btn btn-danger" type="button" data-bs-toggle="modal" data-bs-target="#accounting-delete-modal">
|
<button class="btn btn-danger" type="button" data-bs-toggle="modal" data-bs-target="#accounting-delete-modal">
|
||||||
<i class="fa-solid fa-trash"></i>
|
<i class="fa-solid fa-trash" aria-hidden="true"></i>
|
||||||
<span class="d-none d-md-inline">{{ A_("Delete") }}</span>
|
<span class="d-none d-md-inline">{{ A_("Delete") }}</span>
|
||||||
</button>
|
</button>
|
||||||
{% else %}
|
{% else %}
|
||||||
<button class="btn btn-secondary" type="button" disabled="disabled">
|
<button class="btn btn-secondary" type="button" disabled="disabled">
|
||||||
<i class="fa-solid fa-trash"></i>
|
<i class="fa-solid fa-trash" aria-hidden="true"></i>
|
||||||
<span class="d-none d-md-inline">{{ A_("Delete") }}</span>
|
<span class="d-none d-md-inline">{{ A_("Delete") }}</span>
|
||||||
</button>
|
</button>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
@@ -58,8 +58,8 @@ First written: 2023/2/26
|
|||||||
|
|
||||||
{% if accounting_can_edit() %}
|
{% if accounting_can_edit() %}
|
||||||
<div class="d-md-none accounting-material-fab">
|
<div class="d-md-none accounting-material-fab">
|
||||||
<a class="btn btn-primary" role="button" href="{{ url_for("accounting.journal-entry.edit", journal_entry=obj)|accounting_inherit_next }}">
|
<a class="btn btn-primary" role="button" href="{{ url_for("accounting.journal-entry.edit", journal_entry=obj)|accounting_inherit_next }}" aria-label="{{ A_("Edit") }}">
|
||||||
<i class="fa-solid fa-pen-to-square"></i>
|
<i class="fa-solid fa-pen-to-square" aria-hidden="true"></i>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
@@ -104,7 +104,7 @@ First written: 2023/2/26
|
|||||||
{% if obj.note %}
|
{% if obj.note %}
|
||||||
<div class="card mb-3">
|
<div class="card mb-3">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<i class="far fa-comment-dots"></i>
|
<i class="far fa-comment-dots" aria-hidden="true"></i>
|
||||||
{{ obj.note|accounting_journal_entry_text2html|safe }}
|
{{ obj.note|accounting_journal_entry_text2html|safe }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -31,17 +31,17 @@ First written: 2023/3/21
|
|||||||
{% endfor %}
|
{% endfor %}
|
||||||
</select>
|
</select>
|
||||||
<label class="form-label" for="accounting-currency-{{ currency_index }}-code-select">{{ A_("Currency") }}</label>
|
<label class="form-label" for="accounting-currency-{{ currency_index }}-code-select">{{ A_("Currency") }}</label>
|
||||||
<div id="accounting-currency-{{ currency_index }}-code-error" class="invalid-feedback">{% if currency_code_errors %}{{ currency_code_errors[0] }}{% endif %}</div>
|
<div id="accounting-currency-{{ currency_index }}-code-error" class="invalid-feedback" role="alert">{% if currency_code_errors %}{{ currency_code_errors[0] }}{% endif %}</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<button id="accounting-currency-{{ currency_index }}-delete" class="btn btn-danger rounded-circle {% if only_one_currency_form %} d-none {% endif %}" type="button" data-target="accounting-currency-{{ currency_index }}">
|
<button id="accounting-currency-{{ currency_index }}-delete" class="btn btn-danger rounded-circle {% if only_one_currency_form %} d-none {% endif %}" type="button" data-target="accounting-currency-{{ currency_index }}" aria-label="{{ A_("Remove") }}">
|
||||||
<i class="fas fa-minus"></i>
|
<i class="fas fa-minus" aria-hidden="true"></i>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{% block line_items %}{% endblock %}
|
{% block line_items %}{% endblock %}
|
||||||
</div>
|
</div>
|
||||||
<div id="accounting-currency-{{ currency_index }}-error" class="invalid-feedback">{% if currency_errors %}{{ currency_errors[0] }}{% endif %}</div>
|
<div id="accounting-currency-{{ currency_index }}-error" class="invalid-feedback" role="alert">{% if currency_errors %}{{ currency_errors[0] }}{% endif %}</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -20,8 +20,8 @@ Author: imacat@mail.imacat.idv.tw (imacat)
|
|||||||
First written: 2023/3/21
|
First written: 2023/3/21
|
||||||
#}
|
#}
|
||||||
<div class="mb-2">
|
<div class="mb-2">
|
||||||
<div id="accounting-currency-{{ currency_index }}-{{ debit_credit }}" class="form-control accounting-material-text-field {% if line_item_forms %} accounting-not-empty {% else %} accounting-clickable {% endif %} {% if debit_errors %} is-invalid {% endif %}">
|
<div id="accounting-currency-{{ currency_index }}-{{ debit_credit }}" class="form-control accounting-material-text-field {% if line_item_forms %} accounting-not-empty {% else %} accounting-clickable {% endif %} {% if debit_errors %} is-invalid {% endif %}" {% if line_item_forms %} tabindex="-1" {% else %} role="button" tabindex="0" {% endif %}>
|
||||||
<label class="form-label" for="accounting-currency-{{ currency_index }}-{{ debit_credit }}">{{ header }}</label>
|
<span class="form-label">{{ header }}</span>
|
||||||
<div id="accounting-currency-{{ currency_index }}-{{ debit_credit }}-content" class="mt-2 {% if not line_item_forms %} d-none {% endif %}">
|
<div id="accounting-currency-{{ currency_index }}-{{ debit_credit }}-content" class="mt-2 {% if not line_item_forms %} d-none {% endif %}">
|
||||||
<ul id="accounting-currency-{{ currency_index }}-{{ debit_credit }}-list" class="list-group accounting-line-item-list">
|
<ul id="accounting-currency-{{ currency_index }}-{{ debit_credit }}-list" class="list-group accounting-line-item-list">
|
||||||
{% for line_item_form in line_item_forms %}
|
{% for line_item_form in line_item_forms %}
|
||||||
@@ -36,16 +36,16 @@ First written: 2023/3/21
|
|||||||
|
|
||||||
<div class="d-flex justify-content-between mt-2 mb-2">
|
<div class="d-flex justify-content-between mt-2 mb-2">
|
||||||
<div>{{ A_("Total") }}</div>
|
<div>{{ A_("Total") }}</div>
|
||||||
<div><span id="accounting-currency-{{ currency_index }}-{{ debit_credit }}-total" class="badge rounded-pill bg-primary">{{ debit_credit_total }}</span></div>
|
<div><span id="accounting-currency-{{ currency_index }}-{{ debit_credit }}-total" class="badge rounded-pill bg-primary" aria-live="polite">{{ debit_credit_total }}</span></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<button id="accounting-currency-{{ currency_index }}-{{ debit_credit }}-add-line-item" class="btn btn-primary" type="button" data-currency-index="{{ currency_index }}" data-debit-credit="{{ debit_credit }}" data-bs-toggle="modal" data-bs-target="#accounting-line-item-editor-modal">
|
<button id="accounting-currency-{{ currency_index }}-{{ debit_credit }}-add-line-item" class="btn btn-primary" type="button" data-currency-index="{{ currency_index }}" data-debit-credit="{{ debit_credit }}" data-bs-toggle="modal" data-bs-target="#accounting-line-item-editor-modal">
|
||||||
<i class="fas fa-plus"></i>
|
<i class="fas fa-plus" aria-hidden="true"></i>
|
||||||
{{ A_("New") }}
|
{{ A_("New") }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div id="accounting-currency-{{ currency_index }}-{{ debit_credit }}-error" class="invalid-feedback">{% if debit_credit_errors %}{{ debit_credit_errors[0] }}{% endif %}</div>
|
<div id="accounting-currency-{{ currency_index }}-{{ debit_credit }}-error" class="invalid-feedback" role="alert">{% if debit_credit_errors %}{{ debit_credit_errors[0] }}{% endif %}</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ First written: 2023/2/25
|
|||||||
<input id="accounting-currency-{{ currency_index }}-{{ debit_credit }}-{{ line_item_index }}-description" type="hidden" name="currency-{{ currency_index }}-{{ debit_credit }}-{{ line_item_index }}-description" value="{{ form.description.data|accounting_default }}">
|
<input id="accounting-currency-{{ currency_index }}-{{ debit_credit }}-{{ line_item_index }}-description" type="hidden" name="currency-{{ currency_index }}-{{ debit_credit }}-{{ line_item_index }}-description" value="{{ form.description.data|accounting_default }}">
|
||||||
<input id="accounting-currency-{{ currency_index }}-{{ debit_credit }}-{{ line_item_index }}-amount" type="hidden" name="currency-{{ currency_index }}-{{ debit_credit }}-{{ line_item_index }}-amount" value="{{ form.amount.data|accounting_journal_entry_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_journal_entry_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 }}-{{ 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 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 %}" role="button" tabindex="0" data-bs-toggle="modal" data-bs-target="#accounting-line-item-editor-modal">
|
||||||
<div>
|
<div>
|
||||||
<div class="small">
|
<div class="small">
|
||||||
<span id="accounting-currency-{{ currency_index }}-{{ debit_credit }}-{{ line_item_index }}-account-text-code" class="d-none d-md-inline">{{ form.account_code.data|accounting_default }}</span>
|
<span id="accounting-currency-{{ currency_index }}-{{ debit_credit }}-{{ line_item_index }}-account-text-code" class="d-none d-md-inline">{{ form.account_code.data|accounting_default }}</span>
|
||||||
@@ -65,12 +65,12 @@ First written: 2023/2/25
|
|||||||
</div>
|
</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><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 }}-{{ debit_credit }}-{{ 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" role="alert">{% if form.all_errors %}{{ form.all_errors[0] }}{% endif %}</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<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 }}">
|
<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 }}" aria-label="{{ A_("Remove") }}">
|
||||||
<i class="fas fa-minus"></i>
|
<i class="fas fa-minus" aria-hidden="true"></i>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
|
|||||||
@@ -23,10 +23,12 @@ First written: 2023/2/26
|
|||||||
|
|
||||||
{% block accounting_scripts %}
|
{% block accounting_scripts %}
|
||||||
<script src="{{ url_for("accounting.static", filename="js/drag-and-drop-reorder.js") }}"></script>
|
<script src="{{ url_for("accounting.static", filename="js/drag-and-drop-reorder.js") }}"></script>
|
||||||
|
<script src="{{ url_for("accounting.static", filename="js/base-combobox.js") }}"></script>
|
||||||
<script src="{{ url_for("accounting.static", filename="js/journal-entry-form.js") }}"></script>
|
<script src="{{ url_for("accounting.static", filename="js/journal-entry-form.js") }}"></script>
|
||||||
<script src="{{ url_for("accounting.static", filename="js/journal-entry-line-item-editor.js") }}"></script>
|
<script src="{{ url_for("accounting.static", filename="js/journal-entry-line-item-editor.js") }}"></script>
|
||||||
<script src="{{ url_for("accounting.static", filename="js/journal-entry-account-selector.js") }}"></script>
|
<script src="{{ url_for("accounting.static", filename="js/journal-entry-account-selector.js") }}"></script>
|
||||||
<script src="{{ url_for("accounting.static", filename="js/original-line-item-selector.js") }}"></script>
|
<script src="{{ url_for("accounting.static", filename="js/original-line-item-selector.js") }}"></script>
|
||||||
|
<script src="{{ url_for("accounting.static", filename="js/base-tablist.js") }}"></script>
|
||||||
<script src="{{ url_for("accounting.static", filename="js/description-editor.js") }}"></script>
|
<script src="{{ url_for("accounting.static", filename="js/description-editor.js") }}"></script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
@@ -34,7 +36,7 @@ First written: 2023/2/26
|
|||||||
|
|
||||||
<div class="mb-3 accounting-toolbar">
|
<div class="mb-3 accounting-toolbar">
|
||||||
<a class="btn btn-primary" role="button" href="{% block back_url %}{% endblock %}">
|
<a class="btn btn-primary" role="button" href="{% block back_url %}{% endblock %}">
|
||||||
<i class="fa-solid fa-circle-chevron-left"></i>
|
<i class="fa-solid fa-circle-chevron-left" aria-hidden="true"></i>
|
||||||
<span class="d-none d-md-inline">{{ A_("Back") }}</span>
|
<span class="d-none d-md-inline">{{ A_("Back") }}</span>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
@@ -48,7 +50,7 @@ First written: 2023/2/26
|
|||||||
<div class="form-floating mb-3">
|
<div class="form-floating mb-3">
|
||||||
<input id="accounting-date" class="form-control {% if form.date.errors %} is-invalid {% endif %}" type="date" name="date" value="{{ form.date.data|accounting_default }}" max="{{ form.max_date|accounting_default }}" min="{{ form.min_date|accounting_default }}" placeholder=" " required="required">
|
<input id="accounting-date" class="form-control {% if form.date.errors %} is-invalid {% endif %}" type="date" name="date" value="{{ form.date.data|accounting_default }}" max="{{ form.max_date|accounting_default }}" min="{{ form.min_date|accounting_default }}" placeholder=" " required="required">
|
||||||
<label class="form-label" for="accounting-date">{{ A_("Date") }}</label>
|
<label class="form-label" for="accounting-date">{{ A_("Date") }}</label>
|
||||||
<div id="accounting-date-error" class="invalid-feedback">{% if form.date.errors %}{{ form.date.errors[0] }}{% endif %}</div>
|
<div id="accounting-date-error" class="invalid-feedback" role="alert">{% if form.date.errors %}{{ form.date.errors[0] }}{% endif %}</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
@@ -60,30 +62,30 @@ First written: 2023/2/26
|
|||||||
|
|
||||||
<div>
|
<div>
|
||||||
<button id="accounting-add-currency" class="btn btn-primary" type="button">
|
<button id="accounting-add-currency" class="btn btn-primary" type="button">
|
||||||
<i class="fas fa-plus"></i>
|
<i class="fas fa-plus" aria-hidden="true"></i>
|
||||||
{{ A_("New") }}
|
{{ A_("New") }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div id="accounting-currencies-error" class="invalid-feedback">{% if form.currencies_errors %}{{ form.currencies_errors[0] }}{% endif %}</div>
|
<div id="accounting-currencies-error" class="invalid-feedback" role="alert">{% if form.currencies_errors %}{{ form.currencies_errors[0] }}{% endif %}</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-floating mb-3">
|
<div class="form-floating mb-3">
|
||||||
<textarea id="accounting-note" class="form-control form-control-lg {% if form.note.errors %} is-invalid {% endif %}" name="note" rows="5" placeholder=" ">{{ form.note.data|accounting_default }}</textarea>
|
<textarea id="accounting-note" class="form-control form-control-lg {% if form.note.errors %} is-invalid {% endif %}" name="note" rows="5" placeholder=" ">{{ form.note.data|accounting_default }}</textarea>
|
||||||
<label class="form-label" for="accounting-note">{{ A_("Note") }}</label>
|
<label class="form-label" for="accounting-note">{{ A_("Note") }}</label>
|
||||||
<div id="accounting-note-error" class="invalid-feedback">{% if form.note.errors %}{{ form.note.errors[0] }}{% endif %}</div>
|
<div id="accounting-note-error" class="invalid-feedback" role="alert">{% if form.note.errors %}{{ form.note.errors[0] }}{% endif %}</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="d-none d-md-block">
|
<div class="d-none d-md-block">
|
||||||
<button class="btn btn-primary" type="submit">
|
<button class="btn btn-primary" type="submit">
|
||||||
<i class="fa-solid fa-floppy-disk"></i>
|
<i class="fa-solid fa-floppy-disk" aria-hidden="true"></i>
|
||||||
{{ A_("Save") }}
|
{{ A_("Save") }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="d-md-none accounting-material-fab">
|
<div class="d-md-none accounting-material-fab">
|
||||||
<button class="btn btn-primary" type="submit">
|
<button class="btn btn-primary" type="submit" aria-label="{{ A_("Save") }}">
|
||||||
<i class="fa-solid fa-floppy-disk"></i>
|
<i class="fa-solid fa-floppy-disk" aria-hidden="true"></i>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|||||||
+18
-18
@@ -31,40 +31,40 @@ First written: 2023/2/25
|
|||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<div id="accounting-line-item-editor-original-line-item-container" class="d-flex justify-content-between mb-3">
|
<div id="accounting-line-item-editor-original-line-item-container" class="d-flex justify-content-between mb-3">
|
||||||
<div class="accounting-line-item-editor-original-line-item-content">
|
<div class="accounting-line-item-editor-original-line-item-content">
|
||||||
<div id="accounting-line-item-editor-original-line-item-control" class="form-control accounting-clickable accounting-material-text-field" data-bs-toggle="modal" data-bs-target="#accounting-original-line-item-selector-modal">
|
<button id="accounting-line-item-editor-original-line-item-control" class="form-control text-start accounting-material-text-field" type="button" data-bs-toggle="modal" data-bs-target="#accounting-original-line-item-selector-modal">
|
||||||
<label class="form-label" for="accounting-line-item-editor-original-line-item">{{ A_("Original Line Item") }}</label>
|
<span class="form-label">{{ A_("Original Line Item") }}</span>
|
||||||
<div id="accounting-line-item-editor-original-line-item"></div>
|
<span id="accounting-line-item-editor-original-line-item"></span>
|
||||||
</div>
|
</button>
|
||||||
<div id="accounting-line-item-editor-original-line-item-error" class="invalid-feedback"></div>
|
<div id="accounting-line-item-editor-original-line-item-error" class="invalid-feedback" role="alert"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<button id="accounting-line-item-editor-original-line-item-delete" class="btn btn-danger rounded-circle" type="button">
|
<button id="accounting-line-item-editor-original-line-item-delete" class="btn btn-danger rounded-circle" type="button" aria-label="{{ A_("Remove") }}">
|
||||||
<i class="fas fa-minus"></i>
|
<i class="fas fa-minus" aria-hidden="true"></i>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<div id="accounting-line-item-editor-description-control" class="form-control accounting-clickable accounting-material-text-field" data-bs-toggle="modal" data-bs-target="">
|
<button id="accounting-line-item-editor-description-control" class="form-control text-start accounting-material-text-field" type="button" data-bs-toggle="modal" data-bs-target="">
|
||||||
<label class="form-label" for="accounting-line-item-editor-description">{{ A_("Description") }}</label>
|
<span class="form-label">{{ A_("Description") }}</span>
|
||||||
<div id="accounting-line-item-editor-description"></div>
|
<span id="accounting-line-item-editor-description"></span>
|
||||||
</div>
|
</button>
|
||||||
<div id="accounting-line-item-editor-description-error" class="invalid-feedback"></div>
|
<div id="accounting-line-item-editor-description-error" class="invalid-feedback" role="alert"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<div id="accounting-line-item-editor-account-control" class="form-control accounting-clickable accounting-material-text-field" data-bs-toggle="modal" data-bs-target="">
|
<button id="accounting-line-item-editor-account-control" class="form-control text-start accounting-material-text-field" type="button" data-bs-toggle="modal" data-bs-target="">
|
||||||
<label class="form-label" for="accounting-line-item-editor-account">{{ A_("Account") }}</label>
|
<span class="form-label">{{ A_("Account") }}</span>
|
||||||
<div id="accounting-line-item-editor-account"></div>
|
<span id="accounting-line-item-editor-account"></span>
|
||||||
</div>
|
</button>
|
||||||
<div id="accounting-line-item-editor-account-error" class="invalid-feedback"></div>
|
<div id="accounting-line-item-editor-account-error" class="invalid-feedback" role="alert"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-floating mb-3">
|
<div class="form-floating mb-3">
|
||||||
<input id="accounting-line-item-editor-amount" class="form-control" type="number" value="" min="0" max="" step="0.01" placeholder=" " required="required">
|
<input id="accounting-line-item-editor-amount" class="form-control" type="number" value="" min="0" max="" step="0.01" placeholder=" " required="required">
|
||||||
<label for="accounting-line-item-editor-amount">{{ A_("Amount") }}</label>
|
<label for="accounting-line-item-editor-amount">{{ A_("Amount") }}</label>
|
||||||
<div id="accounting-line-item-editor-amount-error" class="invalid-feedback"></div>
|
<div id="accounting-line-item-editor-amount-error" class="invalid-feedback" role="alert"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
|
|||||||
+7
-7
@@ -19,25 +19,25 @@ original-line-item-selector-modal.html: The modal of the original line item sele
|
|||||||
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-original-line-item-selector-modal" class="modal fade" tabindex="-1" aria-labelledby="accounting-original-line-item-selector-modal-label" aria-hidden="true">
|
<div id="accounting-original-line-item-selector-modal" class="modal fade" tabindex="-1" aria-labelledby="accounting-original-line-item-selector-modal-label" aria-hidden="true" data-bs-keyboard="false">
|
||||||
<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-original-line-item-selector-modal-label">{{ A_("Select Original Line Item") }}</h1>
|
<h1 class="modal-title fs-5" id="accounting-original-line-item-selector-modal-label">{{ A_("Select Original Line Item") }}</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 id="accounting-original-line-item-selector-close" 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-original-line-item-selector-query" class="form-control form-control-sm" type="search" placeholder=" " required="required">
|
<input id="accounting-original-line-item-selector-query" class="form-control form-control-sm" type="search" placeholder=" " required="required" role="combobox" aria-expanded="true" aria-controls="accounting-original-line-item-selector-option-list" aria-autocomplete="list" aria-activedescendant="">
|
||||||
<label class="input-group-text" for="accounting-original-line-item-selector-query">
|
<label class="input-group-text" for="accounting-original-line-item-selector-query">
|
||||||
<i class="fa-solid fa-magnifying-glass"></i>
|
<i class="fa-solid fa-magnifying-glass" aria-hidden="true"></i>
|
||||||
{{ A_("Search") }}
|
{{ A_("Search") }}
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<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" role="listbox" tabindex="-1" aria-labelledby="accounting-original-line-item-selector-modal-label">
|
||||||
{% 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.journal_entry.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-title="{{ line_item.account.title }}" data-account-text="{{ line_item.account }}" data-description="{{ line_item.description|accounting_default }}" data-net-balance="{{ line_item.net_balance|accounting_journal_entry_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.journal_entry.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-title="{{ line_item.account.title }}" data-account-text="{{ line_item.account }}" data-description="{{ line_item.description|accounting_default }}" data-net-balance="{{ line_item.net_balance|accounting_journal_entry_format_amount_input }}" data-text="{{ line_item }}" data-query-values="{{ line_item.query_values|tojson|forceescape }}" role="option" aria-selected="false" data-bs-toggle="modal" data-bs-target="#accounting-line-item-editor-modal">
|
||||||
<div>
|
<div>
|
||||||
<div class="small">
|
<div class="small">
|
||||||
{{ line_item.journal_entry.date|accounting_format_date }}
|
{{ line_item.journal_entry.date|accounting_format_date }}
|
||||||
@@ -55,7 +55,7 @@ First written: 2023/2/25
|
|||||||
</li>
|
</li>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</ul>
|
</ul>
|
||||||
<p id="accounting-original-line-item-selector-option-no-result" class="d-none">{{ A_("There is no data.") }}</p>
|
<p id="accounting-original-line-item-selector-option-no-result" class="d-none" role="status">{{ A_("There is no data.") }}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ First written: 2023/2/26
|
|||||||
|
|
||||||
<div class="mb-3 accounting-toolbar">
|
<div class="mb-3 accounting-toolbar">
|
||||||
<a class="btn btn-primary" role="button" href="{{ url_for("accounting-report.default")|accounting_or_next }}">
|
<a class="btn btn-primary" role="button" href="{{ url_for("accounting-report.default")|accounting_or_next }}">
|
||||||
<i class="fa-solid fa-circle-chevron-left"></i>
|
<i class="fa-solid fa-circle-chevron-left" aria-hidden="true"></i>
|
||||||
<span class="d-none d-md-inline">{{ A_("Back") }}</span>
|
<span class="d-none d-md-inline">{{ A_("Back") }}</span>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
@@ -50,21 +50,21 @@ First written: 2023/2/26
|
|||||||
{% with journal_entry = item %}
|
{% with journal_entry = item %}
|
||||||
{% include "accounting/journal-entry/include/order-journal-entry.html" %}
|
{% include "accounting/journal-entry/include/order-journal-entry.html" %}
|
||||||
{% endwith %}
|
{% endwith %}
|
||||||
<i class="fa-solid fa-bars"></i>
|
<i class="fa-solid fa-bars" aria-hidden="true"></i>
|
||||||
</li>
|
</li>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<div class="d-none d-md-block">
|
<div class="d-none d-md-block">
|
||||||
<button class="btn btn-primary" type="submit">
|
<button class="btn btn-primary" type="submit">
|
||||||
<i class="fa-solid fa-floppy-disk"></i>
|
<i class="fa-solid fa-floppy-disk" aria-hidden="true"></i>
|
||||||
{{ A_("Save") }}
|
{{ A_("Save") }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="d-md-none accounting-material-fab">
|
<div class="d-md-none accounting-material-fab">
|
||||||
<button class="btn btn-primary" type="submit">
|
<button class="btn btn-primary" type="submit" aria-label="{{ A_("Save") }}">
|
||||||
<i class="fa-solid fa-floppy-disk"></i>
|
<i class="fa-solid fa-floppy-disk" aria-hidden="true"></i>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ First written: 2023/2/26
|
|||||||
|
|
||||||
{% block as_transfer %}
|
{% block as_transfer %}
|
||||||
<a class="btn btn-primary" role="button" href="{{ url_for("accounting.journal-entry.edit", journal_entry=obj)|accounting_journal_entry_to_transfer|accounting_inherit_next }}">
|
<a class="btn btn-primary" role="button" href="{{ url_for("accounting.journal-entry.edit", journal_entry=obj)|accounting_journal_entry_to_transfer|accounting_inherit_next }}">
|
||||||
<i class="fa-solid fa-table-columns"></i>
|
<i class="fa-solid fa-table-columns" aria-hidden="true"></i>
|
||||||
<span class="d-none d-md-inline">{{ A_("As Transfer") }}</span>
|
<span class="d-none d-md-inline">{{ A_("As Transfer") }}</span>
|
||||||
</a>
|
</a>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|||||||
@@ -27,14 +27,14 @@ First written: 2023/3/22
|
|||||||
|
|
||||||
<div class="mb-3 accounting-toolbar">
|
<div class="mb-3 accounting-toolbar">
|
||||||
<a class="btn btn-primary d-none d-md-inline" role="button" href="{{ url_for("accounting.option.edit")|accounting_inherit_next }}">
|
<a class="btn btn-primary d-none d-md-inline" role="button" href="{{ url_for("accounting.option.edit")|accounting_inherit_next }}">
|
||||||
<i class="fa-solid fa-pen-to-square"></i>
|
<i class="fa-solid fa-pen-to-square" aria-hidden="true"></i>
|
||||||
{{ A_("Edit") }}
|
{{ A_("Edit") }}
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="d-md-none accounting-material-fab">
|
<div class="d-md-none accounting-material-fab">
|
||||||
<a class="btn btn-primary" role="button" href="{{ url_for("accounting.option.edit")|accounting_inherit_next }}">
|
<a class="btn btn-primary" role="button" href="{{ url_for("accounting.option.edit")|accounting_inherit_next }}" aria-label="{{ A_("Edit") }}">
|
||||||
<i class="fa-solid fa-pen-to-square"></i>
|
<i class="fa-solid fa-pen-to-square" aria-hidden="true"></i>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ First written: 2023/3/22
|
|||||||
|
|
||||||
{% block accounting_scripts %}
|
{% block accounting_scripts %}
|
||||||
<script src="{{ url_for("accounting.static", filename="js/drag-and-drop-reorder.js") }}"></script>
|
<script src="{{ url_for("accounting.static", filename="js/drag-and-drop-reorder.js") }}"></script>
|
||||||
|
<script src="{{ url_for("accounting.static", filename="js/base-combobox.js") }}"></script>
|
||||||
<script src="{{ url_for("accounting.static", filename="js/option-form.js") }}"></script>
|
<script src="{{ url_for("accounting.static", filename="js/option-form.js") }}"></script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
@@ -32,7 +33,7 @@ First written: 2023/3/22
|
|||||||
|
|
||||||
<div class="mb-3 accounting-toolbar">
|
<div class="mb-3 accounting-toolbar">
|
||||||
<a class="btn btn-primary" role="button" href="{{ url_for("accounting.option.detail")|accounting_inherit_next }}">
|
<a class="btn btn-primary" role="button" href="{{ url_for("accounting.option.detail")|accounting_inherit_next }}">
|
||||||
<i class="fa-solid fa-circle-chevron-left"></i>
|
<i class="fa-solid fa-circle-chevron-left" aria-hidden="true"></i>
|
||||||
<span class="d-none d-md-inline">{{ A_("Back") }}</span>
|
<span class="d-none d-md-inline">{{ A_("Back") }}</span>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
@@ -49,7 +50,7 @@ First written: 2023/3/22
|
|||||||
{% endfor %}
|
{% endfor %}
|
||||||
</select>
|
</select>
|
||||||
<label class="form-label" for="accounting-default-currency">{{ A_("Default Currency") }}</label>
|
<label class="form-label" for="accounting-default-currency">{{ A_("Default Currency") }}</label>
|
||||||
<div id="accounting-default-currency-error" class="invalid-feedback">{% if form.default_currency_code.errors %}{{ form.default_currency_code.errors[0] }}{% endif %}</div>
|
<div id="accounting-default-currency-error" class="invalid-feedback" role="alert">{% if form.default_currency_code.errors %}{{ form.default_currency_code.errors[0] }}{% endif %}</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-floating mb-3">
|
<div class="form-floating mb-3">
|
||||||
@@ -59,7 +60,7 @@ First written: 2023/3/22
|
|||||||
{% endfor %}
|
{% endfor %}
|
||||||
</select>
|
</select>
|
||||||
<label class="form-label" for="accounting-default-ie-account">{{ A_("Default Account for the Income and Expenses Log") }}</label>
|
<label class="form-label" for="accounting-default-ie-account">{{ A_("Default Account for the Income and Expenses Log") }}</label>
|
||||||
<div id="accounting-default-ie-account-error" class="invalid-feedback">{% if form.default_ie_account_code.errors %}{{ form.default_ie_account_code.errors[0] }}{% endif %}</div>
|
<div id="accounting-default-ie-account-error" class="invalid-feedback" role="alert">{% if form.default_ie_account_code.errors %}{{ form.default_ie_account_code.errors[0] }}{% endif %}</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{% with expense_income = "expense",
|
{% with expense_income = "expense",
|
||||||
@@ -76,14 +77,14 @@ First written: 2023/3/22
|
|||||||
|
|
||||||
<div class="d-none d-md-block">
|
<div class="d-none d-md-block">
|
||||||
<button class="btn btn-primary" type="submit">
|
<button class="btn btn-primary" type="submit">
|
||||||
<i class="fa-solid fa-floppy-disk"></i>
|
<i class="fa-solid fa-floppy-disk" aria-hidden="true"></i>
|
||||||
{{ A_("Save") }}
|
{{ A_("Save") }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="d-md-none accounting-material-fab">
|
<div class="d-md-none accounting-material-fab">
|
||||||
<button class="btn btn-primary" type="submit">
|
<button class="btn btn-primary" type="submit" aria-label="{{ A_("Save") }}">
|
||||||
<i class="fa-solid fa-floppy-disk"></i>
|
<i class="fa-solid fa-floppy-disk" aria-hidden="true"></i>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|||||||
+3
-3
@@ -19,8 +19,8 @@ form-recurring-expense-income.html: The recurring expense or income sub-form in
|
|||||||
Author: imacat@mail.imacat.idv.tw (imacat)
|
Author: imacat@mail.imacat.idv.tw (imacat)
|
||||||
First written: 2023/3/22
|
First written: 2023/3/22
|
||||||
#}
|
#}
|
||||||
<div id="accounting-recurring-{{ expense_income }}" class="form-control mb-3 accounting-material-text-field {% if recurring_items %} accounting-not-empty {% else %} accounting-clickable {% endif %}">
|
<div id="accounting-recurring-{{ expense_income }}" class="form-control mb-3 accounting-material-text-field {% if recurring_items %} accounting-not-empty {% else %} accounting-clickable {% endif %}" {% if recurring_items %} tabindex="-1" {% else %} role="button" tabindex="0" {% endif %}>
|
||||||
<label class="form-label" for="accounting-recurring-{{ expense_income }}">{{ label }}</label>
|
<span class="form-label">{{ label }}</span>
|
||||||
<div id="accounting-recurring-{{ expense_income }}-content" class="{% if not recurring_items %} d-none {% endif %}">
|
<div id="accounting-recurring-{{ expense_income }}-content" class="{% if not recurring_items %} d-none {% endif %}">
|
||||||
<ul id="accounting-recurring-{{ expense_income }}-list" class="list-group mb-2 mt-2">
|
<ul id="accounting-recurring-{{ expense_income }}-list" class="list-group mb-2 mt-2">
|
||||||
{% for recurring_item in recurring_items %}
|
{% for recurring_item in recurring_items %}
|
||||||
@@ -33,7 +33,7 @@ First written: 2023/3/22
|
|||||||
|
|
||||||
<div>
|
<div>
|
||||||
<button id="accounting-recurring-{{ expense_income }}-add" class="btn btn-primary" type="button" data-bs-toggle="modal" data-bs-target="#accounting-recurring-item-editor-{{ expense_income }}-modal">
|
<button id="accounting-recurring-{{ expense_income }}-add" class="btn btn-primary" type="button" data-bs-toggle="modal" data-bs-target="#accounting-recurring-item-editor-{{ expense_income }}-modal">
|
||||||
<i class="fas fa-plus"></i>
|
<i class="fas fa-plus" aria-hidden="true"></i>
|
||||||
{{ A_("New") }}
|
{{ A_("New") }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -27,17 +27,17 @@ First written: 2023/3/22
|
|||||||
<input id="accounting-recurring-{{ expense_income }}-{{ item_index }}-description-template" type="hidden" name="recurring-{{ expense_income }}-{{ item_index }}-description_template" value="{{ form.description_template.data|accounting_default }}">
|
<input id="accounting-recurring-{{ expense_income }}-{{ item_index }}-description-template" type="hidden" name="recurring-{{ expense_income }}-{{ item_index }}-description_template" value="{{ form.description_template.data|accounting_default }}">
|
||||||
<div class="d-flex justify-content-between">
|
<div class="d-flex justify-content-between">
|
||||||
<div class="w-100">
|
<div class="w-100">
|
||||||
<div id="accounting-recurring-{{ expense_income }}-{{ item_index }}-control" class="form-control accounting-clickable {% if form.all_errors %} is-invalid {% endif %}" data-bs-toggle="modal" data-bs-target="#accounting-recurring-item-editor-{{ expense_income }}-modal">
|
<button id="accounting-recurring-{{ expense_income }}-{{ item_index }}-control" class="form-control text-start {% if form.all_errors %} is-invalid {% endif %}" type="button" data-bs-toggle="modal" data-bs-target="#accounting-recurring-item-editor-{{ expense_income }}-modal">
|
||||||
<div id="accounting-recurring-{{ expense_income }}-{{ item_index }}-account-text" class="small">{{ form.account_text|accounting_default }}</div>
|
<span id="accounting-recurring-{{ expense_income }}-{{ item_index }}-account-text" class="d-block small">{{ form.account_text|accounting_default }}</span>
|
||||||
<div id="accounting-recurring-{{ expense_income }}-{{ item_index }}-name-text">{{ form.name.data|accounting_default }}</div>
|
<span id="accounting-recurring-{{ expense_income }}-{{ item_index }}-name-text" class="d-block">{{ form.name.data|accounting_default }}</span>
|
||||||
<div id="accounting-recurring-{{ expense_income }}-{{ item_index }}-description-template-text" class="small">{{ form.description_template.data|accounting_default }}</div>
|
<span id="accounting-recurring-{{ expense_income }}-{{ item_index }}-description-template-text" class="d-block small">{{ form.description_template.data|accounting_default }}</span>
|
||||||
</div>
|
</button>
|
||||||
<div id="accounting-recurring-{{ expense_income }}-{{ item_index }}-error" class="invalid-feedback">{% if form.all_errors %}{{ form.all_errors[0] }}{% endif %}</div>
|
<div id="accounting-recurring-{{ expense_income }}-{{ item_index }}-error" class="invalid-feedback" role="alert">{% if form.all_errors %}{{ form.all_errors[0] }}{% endif %}</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="ms-2">
|
<div class="ms-2">
|
||||||
<button id="accounting-recurring-{{ expense_income }}-{{ item_index }}-delete" class="btn btn-danger rounded-circle" type="button">
|
<button id="accounting-recurring-{{ expense_income }}-{{ item_index }}-delete" class="btn btn-danger rounded-circle" type="button" aria-label="{{ A_("Remove") }}">
|
||||||
<i class="fas fa-minus"></i>
|
<i class="fas fa-minus" aria-hidden="true"></i>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
+7
-7
@@ -19,30 +19,30 @@ recurring-account-selector-modal.html: The modal of the account selector for the
|
|||||||
Author: imacat@mail.imacat.idv.tw (imacat)
|
Author: imacat@mail.imacat.idv.tw (imacat)
|
||||||
First written: 2023/3/22
|
First written: 2023/3/22
|
||||||
#}
|
#}
|
||||||
<div id="accounting-recurring-accounting-selector-{{ expense_income }}-modal" class="modal fade" tabindex="-1" aria-labelledby="accounting-recurring-accounting-selector-{{ expense_income }}-modal-label" aria-hidden="true">
|
<div id="accounting-recurring-accounting-selector-{{ expense_income }}-modal" class="modal fade" tabindex="-1" aria-labelledby="accounting-recurring-accounting-selector-{{ expense_income }}-modal-label" aria-hidden="true" data-bs-keyboard="false">
|
||||||
<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-recurring-accounting-selector-{{ expense_income }}-modal-label">{{ A_("Select Account") }}</h1>
|
<h1 class="modal-title fs-5" id="accounting-recurring-accounting-selector-{{ expense_income }}-modal-label">{{ A_("Select Account") }}</h1>
|
||||||
<button type="button" class="btn-close" data-bs-toggle="modal" data-bs-target="#accounting-recurring-item-editor-{{ expense_income }}-modal" aria-label="{{ A_("Close") }}"></button>
|
<button id="accounting-recurring-accounting-selector-{{ expense_income }}-close" type="button" class="btn-close" data-bs-toggle="modal" data-bs-target="#accounting-recurring-item-editor-{{ expense_income }}-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-recurring-accounting-selector-{{ expense_income }}-query" class="form-control form-control-sm" type="search" placeholder=" " required="required">
|
<input id="accounting-recurring-accounting-selector-{{ expense_income }}-query" class="form-control form-control-sm" type="search" placeholder=" " required="required" role="combobox" aria-expanded="true" aria-controls="accounting-recurring-accounting-selector-{{ expense_income }}-option-list" aria-autocomplete="list" aria-activedescendant="">
|
||||||
<label class="input-group-text" for="accounting-recurring-accounting-selector-{{ expense_income }}-query">
|
<label class="input-group-text" for="accounting-recurring-accounting-selector-{{ expense_income }}-query">
|
||||||
<i class="fa-solid fa-magnifying-glass"></i>
|
<i class="fa-solid fa-magnifying-glass" aria-hidden="true"></i>
|
||||||
{{ A_("Search") }}
|
{{ A_("Search") }}
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<ul id="accounting-recurring-accounting-selector-{{ expense_income }}-option-list" class="list-group accounting-selector-list">
|
<ul id="accounting-recurring-accounting-selector-{{ expense_income }}-option-list" class="list-group accounting-selector-list" role="listbox" tabindex="-1" aria-labelledby="accounting-recurring-accounting-selector-{{ expense_income }}-modal-label">
|
||||||
{% for account in accounts %}
|
{% for account in accounts %}
|
||||||
<li id="accounting-recurring-accounting-selector-{{ expense_income }}-option-{{ account.code }}" class="list-group-item accounting-clickable accounting-recurring-accounting-selector-{{ expense_income }}-option" data-code="{{ account.code }}" data-text="{{ account }}" data-query-values="{{ account.query_values|tojson|forceescape }}" data-bs-toggle="modal" data-bs-target="#accounting-recurring-item-editor-{{ expense_income }}-modal">
|
<li id="accounting-recurring-accounting-selector-{{ expense_income }}-option-{{ account.code }}" class="list-group-item accounting-clickable accounting-recurring-accounting-selector-{{ expense_income }}-option" data-code="{{ account.code }}" role="option" aria-selected="false" data-text="{{ account }}" data-query-values="{{ account.query_values|tojson|forceescape }}" data-bs-toggle="modal" data-bs-target="#accounting-recurring-item-editor-{{ expense_income }}-modal">
|
||||||
{{ account }}
|
{{ account }}
|
||||||
</li>
|
</li>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</ul>
|
</ul>
|
||||||
<p id="accounting-recurring-accounting-selector-{{ expense_income }}-option-no-result" class="d-none">{{ A_("There is no data.") }}</p>
|
<p id="accounting-recurring-accounting-selector-{{ expense_income }}-option-no-result" class="d-none" role="status">{{ 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-recurring-item-editor-{{ expense_income }}-modal">{{ A_("Cancel") }}</button>
|
<button type="button" class="btn btn-secondary" data-bs-toggle="modal" data-bs-target="#accounting-recurring-item-editor-{{ expense_income }}-modal">{{ A_("Cancel") }}</button>
|
||||||
|
|||||||
@@ -32,21 +32,21 @@ First written: 2023/3/22
|
|||||||
<div class="form-floating mb-3">
|
<div class="form-floating mb-3">
|
||||||
<input id="accounting-recurring-item-editor-{{ expense_income }}-name" class="form-control" type="text" value="" placeholder=" " required="required">
|
<input id="accounting-recurring-item-editor-{{ expense_income }}-name" class="form-control" type="text" value="" placeholder=" " required="required">
|
||||||
<label for="accounting-recurring-item-editor-{{ expense_income }}-name">{{ A_("Name") }}</label>
|
<label for="accounting-recurring-item-editor-{{ expense_income }}-name">{{ A_("Name") }}</label>
|
||||||
<div id="accounting-recurring-item-editor-{{ expense_income }}-name-error" class="invalid-feedback"></div>
|
<div id="accounting-recurring-item-editor-{{ expense_income }}-name-error" class="invalid-feedback" role="alert"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<div id="accounting-recurring-item-editor-{{ expense_income }}-account-control" class="form-control accounting-clickable accounting-material-text-field" data-bs-toggle="modal" data-bs-target="#accounting-recurring-accounting-selector-{{ expense_income }}-modal">
|
<button id="accounting-recurring-item-editor-{{ expense_income }}-account-control" class="form-control text-start accounting-material-text-field" type="button" data-bs-toggle="modal" data-bs-target="#accounting-recurring-accounting-selector-{{ expense_income }}-modal">
|
||||||
<label class="form-label" for="accounting-recurring-item-editor-{{ expense_income }}-account">{{ A_("Account") }}</label>
|
<span class="form-label">{{ A_("Account") }}</span>
|
||||||
<div id="accounting-recurring-item-editor-{{ expense_income }}-account"></div>
|
<span id="accounting-recurring-item-editor-{{ expense_income }}-account"></span>
|
||||||
</div>
|
</button>
|
||||||
<div id="accounting-recurring-item-editor-{{ expense_income }}-account-error" class="invalid-feedback"></div>
|
<div id="accounting-recurring-item-editor-{{ expense_income }}-account-error" class="invalid-feedback" role="alert"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-floating mb-3">
|
<div class="form-floating mb-3">
|
||||||
<input id="accounting-recurring-item-editor-{{ expense_income }}-description-template" class="form-control" type="text" value="" placeholder=" " required="required">
|
<input id="accounting-recurring-item-editor-{{ expense_income }}-description-template" class="form-control" type="text" value="" placeholder=" " required="required">
|
||||||
<label for="accounting-recurring-item-editor-{{ expense_income }}-description-template">{{ A_("Description Template") }}</label>
|
<label for="accounting-recurring-item-editor-{{ expense_income }}-description-template">{{ A_("Description Template") }}</label>
|
||||||
<div id="accounting-recurring-item-editor-{{ expense_income }}-description-template-error" class="invalid-feedback"></div>
|
<div id="accounting-recurring-item-editor-{{ expense_income }}-description-template-error" class="invalid-feedback" role="alert"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="mb-3 border-top accounting-recurring-description-template-illustration">
|
<div class="mb-3 border-top accounting-recurring-description-template-illustration">
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ First written: 2023/3/7
|
|||||||
|
|
||||||
{% block accounting_scripts %}
|
{% block accounting_scripts %}
|
||||||
<script src="{{ url_for("accounting.static", filename="js/material-fab-speed-dial.js") }}"></script>
|
<script src="{{ url_for("accounting.static", filename="js/material-fab-speed-dial.js") }}"></script>
|
||||||
|
<script src="{{ url_for("accounting.static", filename="js/base-tablist.js") }}"></script>
|
||||||
<script src="{{ url_for("accounting.static", filename="js/period-chooser.js") }}"></script>
|
<script src="{{ url_for("accounting.static", filename="js/period-chooser.js") }}"></script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
@@ -55,15 +56,15 @@ First written: 2023/3/7
|
|||||||
</h2>
|
</h2>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="row accounting-report-table accounting-balance-sheet-table">
|
<div class="row accounting-report-table accounting-balance-sheet-table" role="table">
|
||||||
<div class="col-sm-6">
|
<div class="col-sm-6">
|
||||||
{% if report.assets.subsections %}
|
{% if report.assets.subsections %}
|
||||||
{% with section = report.assets %}
|
{% with section = report.assets %}
|
||||||
{% include "accounting/report/include/balance-sheet-section.html" %}
|
{% include "accounting/report/include/balance-sheet-section.html" %}
|
||||||
{% endwith %}
|
{% endwith %}
|
||||||
<div class="d-md-none d-flex justify-content-between accounting-report-table-row accounting-balance-sheet-total">
|
<div class="d-md-none d-flex justify-content-between accounting-report-table-row accounting-balance-sheet-total" role="row">
|
||||||
<div>{{ A_("Total") }}</div>
|
<div role="cell">{{ A_("Total") }}</div>
|
||||||
<div class="accounting-amount {% if report.assets.total < 0 %} text-danger {% endif %}">{{ report.assets.total|accounting_report_format_amount }}</div>
|
<div class="accounting-amount {% if report.assets.total < 0 %} text-danger {% endif %}" role="cell">{{ report.assets.total|accounting_report_format_amount }}</div>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
@@ -73,9 +74,9 @@ First written: 2023/3/7
|
|||||||
{% with section = report.liabilities %}
|
{% with section = report.liabilities %}
|
||||||
{% include "accounting/report/include/balance-sheet-section.html" %}
|
{% include "accounting/report/include/balance-sheet-section.html" %}
|
||||||
{% endwith %}
|
{% endwith %}
|
||||||
<div class="d-flex justify-content-between accounting-report-table-row accounting-balance-sheet-subtotal">
|
<div class="d-flex justify-content-between accounting-report-table-row accounting-balance-sheet-subtotal" role="row">
|
||||||
<div>{{ A_("Total") }}</div>
|
<div role="cell">{{ A_("Total") }}</div>
|
||||||
<div class="accounting-amount {% if report.liabilities.total < 0 %} text-danger {% endif %}">{{ report.liabilities.total|accounting_report_format_amount }}</div>
|
<div class="accounting-amount {% if report.liabilities.total < 0 %} text-danger {% endif %}" role="cell">{{ report.liabilities.total|accounting_report_format_amount }}</div>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
@@ -83,15 +84,15 @@ First written: 2023/3/7
|
|||||||
{% with section = report.owner_s_equity %}
|
{% with section = report.owner_s_equity %}
|
||||||
{% include "accounting/report/include/balance-sheet-section.html" %}
|
{% include "accounting/report/include/balance-sheet-section.html" %}
|
||||||
{% endwith %}
|
{% endwith %}
|
||||||
<div class="d-flex justify-content-between accounting-report-table-row accounting-balance-sheet-subtotal">
|
<div class="d-flex justify-content-between accounting-report-table-row accounting-balance-sheet-subtotal" role="row">
|
||||||
<div>{{ A_("Total") }}</div>
|
<div role="cell">{{ A_("Total") }}</div>
|
||||||
<div class="accounting-amount {% if report.owner_s_equity.total < 0 %} text-danger {% endif %}">{{ report.owner_s_equity.total|accounting_report_format_amount }}</div>
|
<div class="accounting-amount {% if report.owner_s_equity.total < 0 %} text-danger {% endif %}" role="cell">{{ report.owner_s_equity.total|accounting_report_format_amount }}</div>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
<div class="d-md-none d-flex justify-content-between accounting-report-table-row accounting-balance-sheet-total">
|
<div class="d-md-none d-flex justify-content-between accounting-report-table-row accounting-balance-sheet-total" role="row">
|
||||||
<div>{{ A_("Total") }}</div>
|
<div role="cell">{{ A_("Total") }}</div>
|
||||||
<div class="accounting-amount {% if report.liabilities.total < 0 %} text-danger {% endif %}">{{ (report.liabilities.total + report.owner_s_equity.total)|accounting_report_format_amount }}</div>
|
<div class="accounting-amount {% if report.liabilities.total < 0 %} text-danger {% endif %}" role="cell">{{ (report.liabilities.total + report.owner_s_equity.total)|accounting_report_format_amount }}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
+2
-2
@@ -32,8 +32,8 @@ First written: 2023/2/25
|
|||||||
{{ A_("Transfer") }}
|
{{ A_("Transfer") }}
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<button id="accounting-btn-material-fab-speed-dial" class="btn btn-primary rounded-circle accounting-btn-material-fab" type="button" data-target="accounting-material-fab-speed-dial">
|
<button id="accounting-btn-material-fab-speed-dial" class="btn btn-primary rounded-circle accounting-btn-material-fab" type="button" data-target="accounting-material-fab-speed-dial" aria-label="{{ A_("New Journal Entry") }}">
|
||||||
<i class="fas fa-plus"></i>
|
<i class="fas fa-plus" aria-hidden="true"></i>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|||||||
@@ -19,24 +19,24 @@ balance-sheet-section.html: A section in the balance sheet.
|
|||||||
Author: imacat@mail.imacat.idv.tw (imacat)
|
Author: imacat@mail.imacat.idv.tw (imacat)
|
||||||
First written: 2023/3/8
|
First written: 2023/3/8
|
||||||
#}
|
#}
|
||||||
<div class="accounting-report-table-row accounting-balance-sheet-section">
|
<div class="accounting-report-table-row accounting-balance-sheet-section" role="row">
|
||||||
<div>{{ section.title.title }}</div>
|
<div role="cell">{{ section.title.title }}</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="accounting-report-table-body">
|
<div class="accounting-report-table-body">
|
||||||
{% for subsection in section.subsections %}
|
{% for subsection in section.subsections %}
|
||||||
<div class="accounting-report-table-row accounting-balance-sheet-subsection">
|
<div class="accounting-report-table-row accounting-balance-sheet-subsection" role="row">
|
||||||
<div>
|
<div role="cell">
|
||||||
<span class="d-none d-md-inline">{{ subsection.title.code }}</span>
|
<span class="d-none d-md-inline">{{ subsection.title.code }}</span>
|
||||||
{{ subsection.title.title }}
|
{{ subsection.title.title }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% for account in subsection.accounts %}
|
{% for account in subsection.accounts %}
|
||||||
<a class="d-flex justify-content-between accounting-report-table-row accounting-balance-sheet-account" href="{{ account.url }}">
|
<a class="d-flex justify-content-between accounting-report-table-row accounting-balance-sheet-account" role="row" href="{{ account.url }}">
|
||||||
<div>
|
<div role="cell">
|
||||||
<span class="d-none d-md-inline">{{ account.account.code }}</span>
|
<span class="d-none d-md-inline">{{ account.account.code }}</span>
|
||||||
{{ account.account.title }}
|
{{ account.account.title }}
|
||||||
</div>
|
</div>
|
||||||
<div class="accounting-amount {% if account.amount < 0 %} text-danger {% endif %}">{{ account.amount|accounting_report_format_amount }}</div>
|
<div class="accounting-amount {% if account.amount < 0 %} text-danger {% endif %}" role="cell">{{ account.amount|accounting_report_format_amount }}</div>
|
||||||
</a>
|
</a>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|||||||
@@ -19,9 +19,9 @@ income-expenses-row-desktop.html: The row in the income and expenses log for the
|
|||||||
Author: imacat@mail.imacat.idv.tw (imacat)
|
Author: imacat@mail.imacat.idv.tw (imacat)
|
||||||
First written: 2023/3/8
|
First written: 2023/3/8
|
||||||
#}
|
#}
|
||||||
<div>{{ line_item.date|accounting_format_date }}</div>
|
<div role="cell">{{ line_item.date|accounting_format_date }}</div>
|
||||||
<div>{{ line_item.account.title }}</div>
|
<div role="cell">{{ line_item.account.title }}</div>
|
||||||
<div>{{ line_item.description|accounting_default }}</div>
|
<div role="cell">{{ line_item.description|accounting_default }}</div>
|
||||||
<div class="accounting-amount">{{ line_item.income|accounting_format_amount|accounting_default }}</div>
|
<div class="accounting-amount" role="cell">{{ line_item.income|accounting_format_amount|accounting_default }}</div>
|
||||||
<div class="accounting-amount">{{ line_item.expense|accounting_format_amount|accounting_default }}</div>
|
<div class="accounting-amount" role="cell">{{ line_item.expense|accounting_format_amount|accounting_default }}</div>
|
||||||
<div class="accounting-amount {% if line_item.balance < 0 %} text-danger {% endif %}">{{ line_item.balance|accounting_report_format_amount }}</div>
|
<div class="accounting-amount {% if line_item.balance < 0 %} text-danger {% endif %}" role="cell">{{ line_item.balance|accounting_report_format_amount }}</div>
|
||||||
|
|||||||
@@ -19,10 +19,10 @@ ledger-row-desktop.html: The row in the ledger for the desktop computers
|
|||||||
Author: imacat@mail.imacat.idv.tw (imacat)
|
Author: imacat@mail.imacat.idv.tw (imacat)
|
||||||
First written: 2023/3/8
|
First written: 2023/3/8
|
||||||
#}
|
#}
|
||||||
<div>{{ line_item.date|accounting_format_date }}</div>
|
<div role="cell">{{ line_item.date|accounting_format_date }}</div>
|
||||||
<div>{{ line_item.description|accounting_default }}</div>
|
<div role="cell">{{ line_item.description|accounting_default }}</div>
|
||||||
<div class="accounting-amount">{{ line_item.debit|accounting_format_amount|accounting_default }}</div>
|
<div class="accounting-amount" role="cell">{{ line_item.debit|accounting_format_amount|accounting_default }}</div>
|
||||||
<div class="accounting-amount">{{ line_item.credit|accounting_format_amount|accounting_default }}</div>
|
<div class="accounting-amount" role="cell">{{ line_item.credit|accounting_format_amount|accounting_default }}</div>
|
||||||
{% if report.account.is_real %}
|
{% if report.account.is_real %}
|
||||||
<div class="accounting-amount {% if line_item.balance < 0 %} text-danger {% endif %}">{{ line_item.balance|accounting_report_format_amount }}</div>
|
<div class="accounting-amount {% if line_item.balance < 0 %} text-danger {% endif %}" role="cell">{{ line_item.balance|accounting_report_format_amount }}</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|||||||
@@ -28,31 +28,31 @@ First written: 2023/3/4
|
|||||||
</div>
|
</div>
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
{# Tab navigation #}
|
{# Tab navigation #}
|
||||||
<ul class="nav nav-tabs mb-2">
|
<ul id="accounting-period-chooser-tab-list" class="nav nav-tabs mb-2" role="tablist" aria-label="{{ A_("Period Type") }}">
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<span id="accounting-period-chooser-month-tab" class="nav-link {% if report.period.is_type_month %} active {% endif %} accounting-clickable" aria-current="{% if report.period.is_type_month %} page {% else %} false {% endif %}" data-tab-id="month">
|
<button id="accounting-period-chooser-month-tab" class="nav-link {% if report.period.is_type_month %} active {% endif %} accounting-clickable" type="button" tabindex="{% if report.period.is_type_month %}0{% else %}-1{% endif %}" role="tab" aria-controls="accounting-period-chooser-month-panel" aria-selected="{% if report.period.is_type_month %}true{% else %}false{% endif %}">
|
||||||
{{ A_("Month") }}
|
{{ A_("Month") }}
|
||||||
</span>
|
</button>
|
||||||
</li>
|
</li>
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<span id="accounting-period-chooser-year-tab" class="nav-link {% if report.period.is_a_year %} active {% endif %} accounting-clickable" aria-current="{% if report.period.is_a_year %} page {% else %} false {% endif %}" data-tab-id="year">
|
<button id="accounting-period-chooser-year-tab" class="nav-link {% if report.period.is_a_year %} active {% endif %} accounting-clickable" type="button" tabindex="{% if report.period.is_a_year %}0{% else %}-1{% endif %}" role="tab" aria-controls="accounting-period-chooser-year-panel" aria-selected="{% if report.period.is_a_year %}true{% else %}false{% endif %}">
|
||||||
{{ A_("Year") }}
|
{{ A_("Year") }}
|
||||||
</span>
|
</button>
|
||||||
</li>
|
</li>
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<span id="accounting-period-chooser-day-tab" class="nav-link {% if report.period.is_a_day %} active {% endif %} accounting-clickable" aria-current="{% if report.period.is_a_day %} page {% else %} false {% endif %}" data-tab-id="day">
|
<button id="accounting-period-chooser-day-tab" class="nav-link {% if report.period.is_a_day %} active {% endif %} accounting-clickable" type="button" tabindex="{% if report.period.is_a_day %}0{% else %}-1{% endif %}" role="tab" aria-controls="accounting-period-chooser-day-panel" aria-selected="{% if report.period.is_a_day %}true{% else %}false{% endif %}">
|
||||||
{{ A_("Day") }}
|
{{ A_("Day") }}
|
||||||
</span>
|
</button>
|
||||||
</li>
|
</li>
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<span id="accounting-period-chooser-custom-tab" class="nav-link {% if report.period.is_type_arbitrary %} active {% endif %} accounting-clickable" aria-current="{% if report.period.is_type_arbitrary %} page {% else %} false {% endif %}" data-tab-id="custom">
|
<button id="accounting-period-chooser-custom-tab" class="nav-link {% if report.period.is_type_arbitrary %} active {% endif %} accounting-clickable" type="button" tabindex="{% if report.period.is_type_arbitrary %}0{% else %}-1{% endif %}" role="tab" aria-controls="accounting-period-chooser-custom-panel" aria-selected="{% if report.period.is_type_arbitrary %}true{% else %}false{% endif %}">
|
||||||
{{ A_("Custom") }}
|
{{ A_("Custom") }}
|
||||||
</span>
|
</button>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
{# The month periods #}
|
{# The month periods #}
|
||||||
<div id="accounting-period-chooser-month-page" {% if report.period.is_type_month %} aria-current="page" {% else %} class="d-none" aria-current="false" {% endif %} aria-labelledby="accounting-period-chooser-month-tab">
|
<div id="accounting-period-chooser-month-panel" {% if not report.period.is_type_month %} class="d-none" {% endif %} role="tabpanel" aria-labelledby="accounting-period-chooser-month-tab">
|
||||||
<div>
|
<div>
|
||||||
<a class="btn {% if report.period.is_this_month %} btn-primary {% else %} btn-outline-primary {% endif %}" role="button" href="{{ report.period_chooser.this_month_url }}">
|
<a class="btn {% if report.period.is_this_month %} btn-primary {% else %} btn-outline-primary {% endif %}" role="button" href="{{ report.period_chooser.this_month_url }}">
|
||||||
{{ A_("This Month") }}
|
{{ A_("This Month") }}
|
||||||
@@ -72,7 +72,7 @@ First written: 2023/3/4
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{# The year periods #}
|
{# The year periods #}
|
||||||
<div id="accounting-period-chooser-year-page" {% if report.period.is_a_year %} aria-current="page" {% else %} class="d-none" aria-current="false" {% endif %} aria-labelledby="accounting-period-chooser-year-tab">
|
<div id="accounting-period-chooser-year-panel" {% if not report.period.is_a_year %} class="d-none" {% endif %} role="tabpanel" aria-labelledby="accounting-period-chooser-year-tab">
|
||||||
<a class="btn {% if report.period.is_this_year %} btn-primary {% else %} btn-outline-primary {% endif %}" role="button" href="{{ report.period_chooser.this_year_url }}">
|
<a class="btn {% if report.period.is_this_year %} btn-primary {% else %} btn-outline-primary {% endif %}" role="button" href="{{ report.period_chooser.this_year_url }}">
|
||||||
{{ A_("This Year") }}
|
{{ A_("This Year") }}
|
||||||
</a>
|
</a>
|
||||||
@@ -93,7 +93,7 @@ First written: 2023/3/4
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{# The day periods #}
|
{# The day periods #}
|
||||||
<div id="accounting-period-chooser-day-page" {% if report.period.is_a_day %} aria-current="page" {% else %} class="d-none" aria-current="false" {% endif %} aria-labelledby="accounting-period-chooser-day-tab">
|
<div id="accounting-period-chooser-day-panel" {% if not report.period.is_a_day %} class="d-none" {% endif %} role="tabpanel" aria-labelledby="accounting-period-chooser-day-tab">
|
||||||
<div>
|
<div>
|
||||||
<a class="btn {% if report.period.is_today %} btn-primary {% else %} btn-outline-primary {% endif %}" role="button" href="{{ report.period_chooser.today_url }}">
|
<a class="btn {% if report.period.is_today %} btn-primary {% else %} btn-outline-primary {% endif %}" role="button" href="{{ report.period_chooser.today_url }}">
|
||||||
{{ A_("Today") }}
|
{{ A_("Today") }}
|
||||||
@@ -109,14 +109,14 @@ First written: 2023/3/4
|
|||||||
<div class="form-floating mb-3">
|
<div class="form-floating mb-3">
|
||||||
<input id="accounting-period-chooser-day-date" class="form-control" type="date" value="{{ report.period.start|accounting_default }}" min="{{ report.period_chooser.data_start }}" required="required">
|
<input id="accounting-period-chooser-day-date" class="form-control" type="date" value="{{ report.period.start|accounting_default }}" min="{{ report.period_chooser.data_start }}" required="required">
|
||||||
<label for="accounting-period-chooser-day-date" class="form-label">{{ A_("Date") }}</label>
|
<label for="accounting-period-chooser-day-date" class="form-label">{{ A_("Date") }}</label>
|
||||||
<div id="accounting-period-chooser-day-date-error" class="invalid-feedback"></div>
|
<div id="accounting-period-chooser-day-date-error" class="invalid-feedback" role="alert"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{# The custom periods #}
|
{# The custom periods #}
|
||||||
<div id="accounting-period-chooser-custom-page" {% if report.period.is_type_arbitrary %} aria-current="page" {% else %} class="d-none" aria-current="false" {% endif %} aria-labelledby="accounting-period-chooser-custom-tab">
|
<div id="accounting-period-chooser-custom-panel" {% if not report.period.is_type_arbitrary %} class="d-none" {% endif %} role="tabpanel" aria-labelledby="accounting-period-chooser-custom-tab">
|
||||||
<div>
|
<div>
|
||||||
<a class="btn {% if report.period.is_all %} btn-primary {% else %} btn-outline-primary {% endif %}" role="button" href="{{ report.period_chooser.all_url }}">
|
<a class="btn {% if report.period.is_all %} btn-primary {% else %} btn-outline-primary {% endif %}" role="button" href="{{ report.period_chooser.all_url }}">
|
||||||
{{ A_("All") }}
|
{{ A_("All") }}
|
||||||
@@ -127,13 +127,13 @@ First written: 2023/3/4
|
|||||||
<div class="form-floating mb-3">
|
<div class="form-floating mb-3">
|
||||||
<input id="accounting-period-chooser-custom-start" class="form-control" type="date" value="{{ report.period.start|accounting_default }}" min="{{ report.period_chooser.data_start }}" max="{{ report.period.end }}" required="required">
|
<input id="accounting-period-chooser-custom-start" class="form-control" type="date" value="{{ report.period.start|accounting_default }}" min="{{ report.period_chooser.data_start }}" max="{{ report.period.end }}" required="required">
|
||||||
<label for="accounting-period-chooser-custom-start" class="form-label">{{ A_("From") }}</label>
|
<label for="accounting-period-chooser-custom-start" class="form-label">{{ A_("From") }}</label>
|
||||||
<div id="accounting-period-chooser-custom-start-error" class="invalid-feedback"></div>
|
<div id="accounting-period-chooser-custom-start-error" class="invalid-feedback" role="alert"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-floating mb-3">
|
<div class="form-floating mb-3">
|
||||||
<input id="accounting-period-chooser-custom-end" class="form-control" type="date" value="{{ report.period.end|accounting_default }}" min="{{ report.period.start }}" required="required">
|
<input id="accounting-period-chooser-custom-end" class="form-control" type="date" value="{{ report.period.end|accounting_default }}" min="{{ report.period.start }}" required="required">
|
||||||
<label for="accounting-period-chooser-custom-end" class="form-label">{{ A_("To") }}</label>
|
<label for="accounting-period-chooser-custom-end" class="form-label">{{ A_("To") }}</label>
|
||||||
<div id="accounting-period-chooser-custom-end-error" class="invalid-feedback"></div>
|
<div id="accounting-period-chooser-custom-end-error" class="invalid-feedback" role="alert"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ First written: 2023/3/8
|
|||||||
{% if accounting_can_edit() %}
|
{% if accounting_can_edit() %}
|
||||||
<div class="btn-group d-none d-md-flex" role="group">
|
<div class="btn-group d-none d-md-flex" role="group">
|
||||||
<button type="button" class="btn btn-primary dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false">
|
<button type="button" class="btn btn-primary dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false">
|
||||||
<i class="fa-solid fa-plus"></i>
|
<i class="fa-solid fa-plus" aria-hidden="true"></i>
|
||||||
<span class="d-none d-md-inline">{{ A_("New") }}</span>
|
<span class="d-none d-md-inline">{{ A_("New") }}</span>
|
||||||
</button>
|
</button>
|
||||||
<ul class="dropdown-menu">
|
<ul class="dropdown-menu">
|
||||||
@@ -46,7 +46,7 @@ First written: 2023/3/8
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
<div class="btn-group" role="group">
|
<div class="btn-group" role="group">
|
||||||
<button id="accounting-choose-report" class="btn btn-primary dropdown-toggle" type="button" data-bs-toggle="dropdown" aria-expanded="false">
|
<button id="accounting-choose-report" class="btn btn-primary dropdown-toggle" type="button" data-bs-toggle="dropdown" aria-expanded="false">
|
||||||
<i class="fa-solid fa-book"></i>
|
<i class="fa-solid fa-book" aria-hidden="true"></i>
|
||||||
<span class="d-none d-md-inline">{{ A_("Report") }}</span>
|
<span class="d-none d-md-inline">{{ A_("Report") }}</span>
|
||||||
</button>
|
</button>
|
||||||
<ul class="dropdown-menu" aria-labelledby="accounting-choose-report">
|
<ul class="dropdown-menu" aria-labelledby="accounting-choose-report">
|
||||||
@@ -59,17 +59,17 @@ First written: 2023/3/8
|
|||||||
</li>
|
</li>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
<li>
|
<li>
|
||||||
<span class="dropdown-item {% if report.report_chooser.is_search %} active {% endif %} accounting-clickable" data-bs-toggle="modal" data-bs-target="#accounting-search-modal">
|
<button class="dropdown-item {% if report.report_chooser.is_search %} active {% endif %}" type="button" data-bs-toggle="modal" data-bs-target="#accounting-search-modal">
|
||||||
<i class="fa-solid fa-magnifying-glass"></i>
|
<i class="fa-solid fa-magnifying-glass" aria-hidden="true"></i>
|
||||||
{{ A_("Search") }}
|
{{ A_("Search") }}
|
||||||
</span>
|
</button>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
{% if use_currency_chooser %}
|
{% if use_currency_chooser %}
|
||||||
<div class="btn-group" role="group">
|
<div class="btn-group" role="group">
|
||||||
<button id="accounting-choose-currency" class="btn btn-primary dropdown-toggle" type="button" data-bs-toggle="dropdown" aria-expanded="false">
|
<button id="accounting-choose-currency" class="btn btn-primary dropdown-toggle" type="button" data-bs-toggle="dropdown" aria-expanded="false">
|
||||||
<i class="fa-solid fa-money-bill-wave"></i>
|
<i class="fa-solid fa-money-bill-wave" aria-hidden="true"></i>
|
||||||
<span class="d-none d-md-inline">{{ A_("Currency") }}</span>
|
<span class="d-none d-md-inline">{{ A_("Currency") }}</span>
|
||||||
</button>
|
</button>
|
||||||
<ul class="dropdown-menu" aria-labelledby="accounting-choose-currency">
|
<ul class="dropdown-menu" aria-labelledby="accounting-choose-currency">
|
||||||
@@ -86,7 +86,7 @@ First written: 2023/3/8
|
|||||||
{% if use_account_chooser %}
|
{% if use_account_chooser %}
|
||||||
<div class="btn-group" role="group">
|
<div class="btn-group" role="group">
|
||||||
<button id="accounting-choose-account" class="btn btn-primary dropdown-toggle" type="button" data-bs-toggle="dropdown" aria-expanded="false">
|
<button id="accounting-choose-account" class="btn btn-primary dropdown-toggle" type="button" data-bs-toggle="dropdown" aria-expanded="false">
|
||||||
<i class="fa-solid fa-clipboard"></i>
|
<i class="fa-solid fa-clipboard" aria-hidden="true"></i>
|
||||||
<span class="d-none d-md-inline">{{ A_("Account") }}</span>
|
<span class="d-none d-md-inline">{{ A_("Account") }}</span>
|
||||||
</button>
|
</button>
|
||||||
<ul class="dropdown-menu accounting-toolbar-accounts" aria-labelledby="accounting-choose-account">
|
<ul class="dropdown-menu accounting-toolbar-accounts" aria-labelledby="accounting-choose-account">
|
||||||
@@ -102,18 +102,18 @@ First written: 2023/3/8
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
{% if use_period_chooser %}
|
{% if use_period_chooser %}
|
||||||
<button class="btn btn-primary" type="button" data-bs-toggle="modal" data-bs-target="#accounting-period-chooser-modal">
|
<button class="btn btn-primary" type="button" data-bs-toggle="modal" data-bs-target="#accounting-period-chooser-modal">
|
||||||
<i class="fa-solid fa-calendar-day"></i>
|
<i class="fa-solid fa-calendar-day" aria-hidden="true"></i>
|
||||||
<span class="d-none d-md-inline">{{ A_("Period") }}</span>
|
<span class="d-none d-md-inline">{{ A_("Period") }}</span>
|
||||||
</button>
|
</button>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if report.has_data %}
|
{% if report.has_data %}
|
||||||
<a class="btn btn-primary" role="button" href="{{ report.csv_uri }}">
|
<a class="btn btn-primary" role="button" href="{{ report.csv_uri }}">
|
||||||
<i class="fa-solid fa-download"></i>
|
<i class="fa-solid fa-download" aria-hidden="true"></i>
|
||||||
<span class="d-none d-md-inline">{{ A_("Download") }}</span>
|
<span class="d-none d-md-inline">{{ A_("Download") }}</span>
|
||||||
</a>
|
</a>
|
||||||
{% else %}
|
{% else %}
|
||||||
<button class="btn btn-secondary" type="button" disabled="disabled">
|
<button class="btn btn-secondary" type="button" disabled="disabled">
|
||||||
<i class="fa-solid fa-download"></i>
|
<i class="fa-solid fa-download" aria-hidden="true"></i>
|
||||||
<span class="d-none d-md-inline">{{ A_("Download") }}</span>
|
<span class="d-none d-md-inline">{{ A_("Download") }}</span>
|
||||||
</button>
|
</button>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
@@ -122,7 +122,7 @@ First written: 2023/3/8
|
|||||||
<input id="accounting-toolbar-search" class="form-control form-control-sm" type="search" name="q" value="{{ request.args.q }}" placeholder=" " required="required">
|
<input id="accounting-toolbar-search" class="form-control form-control-sm" type="search" name="q" value="{{ request.args.q }}" placeholder=" " required="required">
|
||||||
<label id="accounting-toolbar-search-label" for="accounting-toolbar-search" class="input-group-text">
|
<label id="accounting-toolbar-search-label" for="accounting-toolbar-search" class="input-group-text">
|
||||||
<button type="submit">
|
<button type="submit">
|
||||||
<i class="fa-solid fa-magnifying-glass"></i>
|
<i class="fa-solid fa-magnifying-glass" aria-hidden="true"></i>
|
||||||
<span class="d-none d-md-inline">{{ A_("Search") }}</span>
|
<span class="d-none d-md-inline">{{ A_("Search") }}</span>
|
||||||
</button>
|
</button>
|
||||||
</label>
|
</label>
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ First written: 2023/3/5
|
|||||||
|
|
||||||
{% block accounting_scripts %}
|
{% block accounting_scripts %}
|
||||||
<script src="{{ url_for("accounting.static", filename="js/material-fab-speed-dial.js") }}"></script>
|
<script src="{{ url_for("accounting.static", filename="js/material-fab-speed-dial.js") }}"></script>
|
||||||
|
<script src="{{ url_for("accounting.static", filename="js/base-tablist.js") }}"></script>
|
||||||
<script src="{{ url_for("accounting.static", filename="js/period-chooser.js") }}"></script>
|
<script src="{{ url_for("accounting.static", filename="js/period-chooser.js") }}"></script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
@@ -49,27 +50,27 @@ First written: 2023/3/5
|
|||||||
{% include "accounting/include/pagination.html" %}
|
{% include "accounting/include/pagination.html" %}
|
||||||
{% endwith %}
|
{% endwith %}
|
||||||
|
|
||||||
<div class="d-none d-md-block accounting-report-table accounting-income-expenses-table">
|
<div class="d-none d-md-block accounting-report-table accounting-income-expenses-table" role="table">
|
||||||
<div class="accounting-report-table-header">
|
<div class="accounting-report-table-header">
|
||||||
<div class="accounting-report-table-row">
|
<div class="accounting-report-table-row" role="row">
|
||||||
<div>{{ A_("Date") }}</div>
|
<div role="columnheader">{{ A_("Date") }}</div>
|
||||||
<div>{{ A_("Account") }}</div>
|
<div role="columnheader">{{ A_("Account") }}</div>
|
||||||
<div>{{ A_("Description") }}</div>
|
<div role="columnheader">{{ A_("Description") }}</div>
|
||||||
<div class="accounting-amount">{{ A_("Income") }}</div>
|
<div class="accounting-amount" role="columnheader">{{ A_("Income") }}</div>
|
||||||
<div class="accounting-amount">{{ A_("Expense") }}</div>
|
<div class="accounting-amount" role="columnheader">{{ A_("Expense") }}</div>
|
||||||
<div class="accounting-amount">{{ A_("Balance") }}</div>
|
<div class="accounting-amount" role="columnheader">{{ A_("Balance") }}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="accounting-report-table-body">
|
<div class="accounting-report-table-body">
|
||||||
{% if report.brought_forward %}
|
{% if report.brought_forward %}
|
||||||
{% with line_item = report.brought_forward %}
|
{% with line_item = report.brought_forward %}
|
||||||
<div class="accounting-report-table-row">
|
<div class="accounting-report-table-row" role="row">
|
||||||
{% include "accounting/report/include/income-expenses-row-desktop.html" %}
|
{% include "accounting/report/include/income-expenses-row-desktop.html" %}
|
||||||
</div>
|
</div>
|
||||||
{% endwith %}
|
{% endwith %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% for line_item in report.line_items %}
|
{% for line_item in report.line_items %}
|
||||||
<a class="accounting-report-table-row" href="{{ line_item.url|accounting_append_next }}">
|
<a class="accounting-report-table-row" href="{{ line_item.url|accounting_append_next }}" role="row">
|
||||||
{% include "accounting/report/include/income-expenses-row-desktop.html" %}
|
{% include "accounting/report/include/income-expenses-row-desktop.html" %}
|
||||||
</a>
|
</a>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
@@ -77,11 +78,11 @@ First written: 2023/3/5
|
|||||||
{% if report.total %}
|
{% if report.total %}
|
||||||
{% with line_item = report.total %}
|
{% with line_item = report.total %}
|
||||||
<div class="accounting-report-table-footer">
|
<div class="accounting-report-table-footer">
|
||||||
<div class="accounting-report-table-row">
|
<div class="accounting-report-table-row" role="row">
|
||||||
<div>{{ A_("Total") }}</div>
|
<div role="cell">{{ A_("Total") }}</div>
|
||||||
<div class="accounting-amount">{{ line_item.income|accounting_format_amount }}</div>
|
<div class="accounting-amount" role="cell">{{ line_item.income|accounting_format_amount }}</div>
|
||||||
<div class="accounting-amount">{{ line_item.expense|accounting_format_amount }}</div>
|
<div class="accounting-amount" role="cell">{{ line_item.expense|accounting_format_amount }}</div>
|
||||||
<div class="accounting-amount {% if line_item.balance < 0 %} text-danger {% endif %}">{{ line_item.balance|accounting_report_format_amount }}</div>
|
<div class="accounting-amount {% if line_item.balance < 0 %} text-danger {% endif %}" role="cell">{{ line_item.balance|accounting_report_format_amount }}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endwith %}
|
{% endwith %}
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ First written: 2023/3/7
|
|||||||
|
|
||||||
{% block accounting_scripts %}
|
{% block accounting_scripts %}
|
||||||
<script src="{{ url_for("accounting.static", filename="js/material-fab-speed-dial.js") }}"></script>
|
<script src="{{ url_for("accounting.static", filename="js/material-fab-speed-dial.js") }}"></script>
|
||||||
|
<script src="{{ url_for("accounting.static", filename="js/base-tablist.js") }}"></script>
|
||||||
<script src="{{ url_for("accounting.static", filename="js/period-chooser.js") }}"></script>
|
<script src="{{ url_for("accounting.static", filename="js/period-chooser.js") }}"></script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
@@ -55,44 +56,44 @@ First written: 2023/3/7
|
|||||||
</h2>
|
</h2>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="accounting-report-table accounting-income-statement-table">
|
<div class="accounting-report-table accounting-income-statement-table" role="table">
|
||||||
<div class="accounting-report-table-header">
|
<div class="accounting-report-table-header">
|
||||||
<div class="accounting-report-table-row">
|
<div class="accounting-report-table-row" role="row">
|
||||||
<div class="accounting-amount">{{ A_("Amount") }}</div>
|
<div class="accounting-amount" role="columnheader">{{ A_("Amount") }}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="accounting-report-table-body">
|
<div class="accounting-report-table-body">
|
||||||
{% for section in report.sections %}
|
{% for section in report.sections %}
|
||||||
<div class="accounting-report-table-row accounting-income-statement-section">
|
<div class="accounting-report-table-row accounting-income-statement-section" role="row">
|
||||||
<div>
|
<div role="cell">
|
||||||
<span class="d-none d-md-inline">{{ section.title.code }}</span>
|
<span class="d-none d-md-inline">{{ section.title.code }}</span>
|
||||||
{{ section.title.title }}
|
{{ section.title.title }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% for subsection in section.subsections %}
|
{% for subsection in section.subsections %}
|
||||||
<div class="accounting-report-table-row accounting-income-statement-subsection">
|
<div class="accounting-report-table-row accounting-income-statement-subsection" role="row">
|
||||||
<div>
|
<div role="cell">
|
||||||
<span class="d-none d-md-inline">{{ subsection.title.code }}</span>
|
<span class="d-none d-md-inline">{{ subsection.title.code }}</span>
|
||||||
{{ subsection.title.title }}
|
{{ subsection.title.title }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% for account in subsection.accounts %}
|
{% for account in subsection.accounts %}
|
||||||
<a class="accounting-report-table-row accounting-income-statement-account" href="{{ account.url }}">
|
<a class="accounting-report-table-row accounting-income-statement-account" role="row" href="{{ account.url }}">
|
||||||
<div>
|
<div role="cell">
|
||||||
<span class="d-none d-md-inline">{{ account.account.code }}</span>
|
<span class="d-none d-md-inline">{{ account.account.code }}</span>
|
||||||
{{ account.account.title }}
|
{{ account.account.title }}
|
||||||
</div>
|
</div>
|
||||||
<div class="accounting-amount {% if account.amount < 0 %} text-danger {% endif %}">{{ account.amount|accounting_report_format_amount }}</div>
|
<div class="accounting-amount {% if account.amount < 0 %} text-danger {% endif %}" role="cell">{{ account.amount|accounting_report_format_amount }}</div>
|
||||||
</a>
|
</a>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
<div class="accounting-report-table-row accounting-income-statement-subtotal">
|
<div class="accounting-report-table-row accounting-income-statement-subtotal" role="row">
|
||||||
<div>{{ A_("Total") }}</div>
|
<div role="cell">{{ A_("Total") }}</div>
|
||||||
<div class="accounting-amount {% if subsection.total < 0 %} text-danger {% endif %}">{{ subsection.total|accounting_report_format_amount }}</div>
|
<div class="accounting-amount {% if subsection.total < 0 %} text-danger {% endif %}" role="cell">{{ subsection.total|accounting_report_format_amount }}</div>
|
||||||
</div>
|
</div>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
<div class="accounting-report-table-row accounting-income-statement-total">
|
<div class="accounting-report-table-row accounting-income-statement-total" role="row">
|
||||||
<div>{{ section.accumulated.title }}</div>
|
<div role="cell">{{ section.accumulated.title }}</div>
|
||||||
<div class="accounting-amount {% if section.accumulated.amount < 0 %} text-danger {% endif %}">{{ section.accumulated.amount|accounting_report_format_amount }}</div>
|
<div class="accounting-amount {% if section.accumulated.amount < 0 %} text-danger {% endif %}" role="cell">{{ section.accumulated.amount|accounting_report_format_amount }}</div>
|
||||||
</div>
|
</div>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ First written: 2023/3/4
|
|||||||
|
|
||||||
{% block accounting_scripts %}
|
{% block accounting_scripts %}
|
||||||
<script src="{{ url_for("accounting.static", filename="js/material-fab-speed-dial.js") }}"></script>
|
<script src="{{ url_for("accounting.static", filename="js/material-fab-speed-dial.js") }}"></script>
|
||||||
|
<script src="{{ url_for("accounting.static", filename="js/base-tablist.js") }}"></script>
|
||||||
<script src="{{ url_for("accounting.static", filename="js/period-chooser.js") }}"></script>
|
<script src="{{ url_for("accounting.static", filename="js/period-chooser.js") }}"></script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
@@ -47,29 +48,29 @@ First written: 2023/3/4
|
|||||||
{% include "accounting/include/pagination.html" %}
|
{% include "accounting/include/pagination.html" %}
|
||||||
{% endwith %}
|
{% endwith %}
|
||||||
|
|
||||||
<div class="d-none d-md-block accounting-report-table accounting-journal-table">
|
<div class="d-none d-md-block accounting-report-table accounting-journal-table" role="table">
|
||||||
<div class="accounting-report-table-header">
|
<div class="accounting-report-table-header">
|
||||||
<div class="accounting-report-table-row">
|
<div class="accounting-report-table-row" role="row">
|
||||||
<div>{{ A_("Date") }}</div>
|
<div role="columnheader">{{ A_("Date") }}</div>
|
||||||
<div>{{ A_("Currency") }}</div>
|
<div role="columnheader">{{ A_("Currency") }}</div>
|
||||||
<div>{{ A_("Account") }}</div>
|
<div role="columnheader">{{ A_("Account") }}</div>
|
||||||
<div>{{ A_("Description") }}</div>
|
<div role="columnheader">{{ A_("Description") }}</div>
|
||||||
<div class="accounting-amount">{{ A_("Debit") }}</div>
|
<div class="accounting-amount" role="columnheader">{{ A_("Debit") }}</div>
|
||||||
<div class="accounting-amount">{{ A_("Credit") }}</div>
|
<div class="accounting-amount" role="columnheader">{{ A_("Credit") }}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="accounting-report-table-body">
|
<div class="accounting-report-table-body">
|
||||||
{% for line_item in report.line_items %}
|
{% for line_item in report.line_items %}
|
||||||
<a class="accounting-report-table-row" href="{{ url_for("accounting.journal-entry.detail", journal_entry=line_item.journal_entry)|accounting_append_next }}">
|
<a class="accounting-report-table-row" href="{{ url_for("accounting.journal-entry.detail", journal_entry=line_item.journal_entry)|accounting_append_next }}" role="row">
|
||||||
<div>{{ line_item.journal_entry.date|accounting_format_date }}</div>
|
<div role="cell">{{ line_item.journal_entry.date|accounting_format_date }}</div>
|
||||||
<div>{{ line_item.currency.name }}</div>
|
<div role="cell">{{ line_item.currency.name }}</div>
|
||||||
<div>
|
<div role="cell">
|
||||||
<span class="d-none d-md-inline">{{ line_item.account.code }}</span>
|
<span class="d-none d-md-inline">{{ line_item.account.code }}</span>
|
||||||
{{ line_item.account.title }}
|
{{ line_item.account.title }}
|
||||||
</div>
|
</div>
|
||||||
<div>{{ line_item.description|accounting_default }}</div>
|
<div role="cell">{{ line_item.description|accounting_default }}</div>
|
||||||
<div class="accounting-amount">{{ line_item.debit|accounting_format_amount|accounting_default }}</div>
|
<div class="accounting-amount" role="cell">{{ line_item.debit|accounting_format_amount|accounting_default }}</div>
|
||||||
<div class="accounting-amount">{{ line_item.credit|accounting_format_amount|accounting_default }}</div>
|
<div class="accounting-amount" role="cell">{{ line_item.credit|accounting_format_amount|accounting_default }}</div>
|
||||||
</a>
|
</a>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ First written: 2023/3/5
|
|||||||
|
|
||||||
{% block accounting_scripts %}
|
{% block accounting_scripts %}
|
||||||
<script src="{{ url_for("accounting.static", filename="js/material-fab-speed-dial.js") }}"></script>
|
<script src="{{ url_for("accounting.static", filename="js/material-fab-speed-dial.js") }}"></script>
|
||||||
|
<script src="{{ url_for("accounting.static", filename="js/base-tablist.js") }}"></script>
|
||||||
<script src="{{ url_for("accounting.static", filename="js/period-chooser.js") }}"></script>
|
<script src="{{ url_for("accounting.static", filename="js/period-chooser.js") }}"></script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
@@ -49,28 +50,28 @@ First written: 2023/3/5
|
|||||||
{% include "accounting/include/pagination.html" %}
|
{% include "accounting/include/pagination.html" %}
|
||||||
{% endwith %}
|
{% endwith %}
|
||||||
|
|
||||||
<div class="d-none d-md-block accounting-report-table {% if report.account.is_real %} accounting-ledger-real-table {% else %} accounting-ledger-nominal-table {% endif %}">
|
<div class="d-none d-md-block accounting-report-table {% if report.account.is_real %} accounting-ledger-real-table {% else %} accounting-ledger-nominal-table {% endif %}" role="table">
|
||||||
<div class="accounting-report-table-header">
|
<div class="accounting-report-table-header">
|
||||||
<div class="accounting-report-table-row">
|
<div class="accounting-report-table-row">
|
||||||
<div>{{ A_("Date") }}</div>
|
<div role="columnheader">{{ A_("Date") }}</div>
|
||||||
<div>{{ A_("Description") }}</div>
|
<div role="columnheader">{{ A_("Description") }}</div>
|
||||||
<div class="accounting-amount">{{ A_("Debit") }}</div>
|
<div class="accounting-amount" role="columnheader">{{ A_("Debit") }}</div>
|
||||||
<div class="accounting-amount">{{ A_("Credit") }}</div>
|
<div class="accounting-amount" role="columnheader">{{ A_("Credit") }}</div>
|
||||||
{% if report.account.is_real %}
|
{% if report.account.is_real %}
|
||||||
<div class="accounting-amount">{{ A_("Balance") }}</div>
|
<div class="accounting-amount" role="columnheader">{{ A_("Balance") }}</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="accounting-report-table-body">
|
<div class="accounting-report-table-body">
|
||||||
{% if report.brought_forward %}
|
{% if report.brought_forward %}
|
||||||
{% with line_item = report.brought_forward %}
|
{% with line_item = report.brought_forward %}
|
||||||
<div class="accounting-report-table-row">
|
<div class="accounting-report-table-row" role="row">
|
||||||
{% include "accounting/report/include/ledger-row-desktop.html" %}
|
{% include "accounting/report/include/ledger-row-desktop.html" %}
|
||||||
</div>
|
</div>
|
||||||
{% endwith %}
|
{% endwith %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% for line_item in report.line_items %}
|
{% for line_item in report.line_items %}
|
||||||
<a class="accounting-report-table-row" href="{{ line_item.url|accounting_append_next }}">
|
<a class="accounting-report-table-row" href="{{ line_item.url|accounting_append_next }}" role="row">
|
||||||
{% include "accounting/report/include/ledger-row-desktop.html" %}
|
{% include "accounting/report/include/ledger-row-desktop.html" %}
|
||||||
</a>
|
</a>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
@@ -78,12 +79,12 @@ First written: 2023/3/5
|
|||||||
{% if report.total %}
|
{% if report.total %}
|
||||||
{% with line_item = report.total %}
|
{% with line_item = report.total %}
|
||||||
<div class="accounting-report-table-footer">
|
<div class="accounting-report-table-footer">
|
||||||
<div class="accounting-report-table-row">
|
<div class="accounting-report-table-row" role="row">
|
||||||
<div>{{ A_("Total") }}</div>
|
<div role="cell">{{ A_("Total") }}</div>
|
||||||
<div class="accounting-amount">{{ line_item.debit|accounting_format_amount|accounting_default }}</div>
|
<div class="accounting-amount" role="cell">{{ line_item.debit|accounting_format_amount|accounting_default }}</div>
|
||||||
<div class="accounting-amount">{{ line_item.credit|accounting_format_amount|accounting_default }}</div>
|
<div class="accounting-amount" role="cell">{{ line_item.credit|accounting_format_amount|accounting_default }}</div>
|
||||||
{% if report.account.is_real %}
|
{% if report.account.is_real %}
|
||||||
<div class="accounting-amount {% if line_item.balance < 0 %} text-danger {% endif %}">{{ line_item.balance|accounting_report_format_amount }}</div>
|
<div class="accounting-amount {% if line_item.balance < 0 %} text-danger {% endif %}" role="cell">{{ line_item.balance|accounting_report_format_amount }}</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -44,29 +44,29 @@ First written: 2023/3/8
|
|||||||
{% include "accounting/include/pagination.html" %}
|
{% include "accounting/include/pagination.html" %}
|
||||||
{% endwith %}
|
{% endwith %}
|
||||||
|
|
||||||
<div class="d-none d-md-block accounting-report-table accounting-journal-table">
|
<div class="d-none d-md-block accounting-report-table accounting-journal-table" role="table">
|
||||||
<div class="accounting-report-table-header">
|
<div class="accounting-report-table-header">
|
||||||
<div class="accounting-report-table-row">
|
<div class="accounting-report-table-row" role="row">
|
||||||
<div>{{ A_("Date") }}</div>
|
<div role="columnheader">{{ A_("Date") }}</div>
|
||||||
<div>{{ A_("Currency") }}</div>
|
<div role="columnheader">{{ A_("Currency") }}</div>
|
||||||
<div>{{ A_("Account") }}</div>
|
<div role="columnheader">{{ A_("Account") }}</div>
|
||||||
<div>{{ A_("Description") }}</div>
|
<div role="columnheader">{{ A_("Description") }}</div>
|
||||||
<div class="accounting-amount">{{ A_("Debit") }}</div>
|
<div class="accounting-amount" role="columnheader">{{ A_("Debit") }}</div>
|
||||||
<div class="accounting-amount">{{ A_("Credit") }}</div>
|
<div class="accounting-amount" role="columnheader">{{ A_("Credit") }}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="accounting-report-table-body">
|
<div class="accounting-report-table-body">
|
||||||
{% for line_item in report.line_items %}
|
{% for line_item in report.line_items %}
|
||||||
<a class="accounting-report-table-row" href="{{ url_for("accounting.journal-entry.detail", journal_entry=line_item.journal_entry)|accounting_append_next }}">
|
<a class="accounting-report-table-row" href="{{ url_for("accounting.journal-entry.detail", journal_entry=line_item.journal_entry)|accounting_append_next }}" role="row">
|
||||||
<div>{{ line_item.journal_entry.date|accounting_format_date }}</div>
|
<div role="cell">{{ line_item.journal_entry.date|accounting_format_date }}</div>
|
||||||
<div>{{ line_item.currency.name }}</div>
|
<div role="cell">{{ line_item.currency.name }}</div>
|
||||||
<div>
|
<div role="cell">
|
||||||
<span class="d-none d-md-inline">{{ line_item.account.code }}</span>
|
<span class="d-none d-md-inline">{{ line_item.account.code }}</span>
|
||||||
{{ line_item.account.title }}
|
{{ line_item.account.title }}
|
||||||
</div>
|
</div>
|
||||||
<div>{{ line_item.description|accounting_default }}</div>
|
<div role="cell">{{ line_item.description|accounting_default }}</div>
|
||||||
<div class="accounting-amount">{{ line_item.debit|accounting_format_amount|accounting_default }}</div>
|
<div class="accounting-amount" role="cell">{{ line_item.debit|accounting_format_amount|accounting_default }}</div>
|
||||||
<div class="accounting-amount">{{ line_item.credit|accounting_format_amount|accounting_default }}</div>
|
<div class="accounting-amount" role="cell">{{ line_item.credit|accounting_format_amount|accounting_default }}</div>
|
||||||
</a>
|
</a>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ First written: 2023/3/5
|
|||||||
|
|
||||||
{% block accounting_scripts %}
|
{% block accounting_scripts %}
|
||||||
<script src="{{ url_for("accounting.static", filename="js/material-fab-speed-dial.js") }}"></script>
|
<script src="{{ url_for("accounting.static", filename="js/material-fab-speed-dial.js") }}"></script>
|
||||||
|
<script src="{{ url_for("accounting.static", filename="js/base-tablist.js") }}"></script>
|
||||||
<script src="{{ url_for("accounting.static", filename="js/period-chooser.js") }}"></script>
|
<script src="{{ url_for("accounting.static", filename="js/period-chooser.js") }}"></script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
@@ -55,31 +56,31 @@ First written: 2023/3/5
|
|||||||
</h2>
|
</h2>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="accounting-report-table accounting-trial-balance-table">
|
<div class="accounting-report-table accounting-trial-balance-table" role="table">
|
||||||
<div class="accounting-report-table-header">
|
<div class="accounting-report-table-header">
|
||||||
<div class="accounting-report-table-row">
|
<div class="accounting-report-table-row" role="row">
|
||||||
<div>{{ A_("Account") }}</div>
|
<div role="columnheader">{{ A_("Account") }}</div>
|
||||||
<div class="accounting-amount">{{ A_("Debit") }}</div>
|
<div class="accounting-amount" role="columnheader">{{ A_("Debit") }}</div>
|
||||||
<div class="accounting-amount">{{ A_("Credit") }}</div>
|
<div class="accounting-amount" role="columnheader">{{ A_("Credit") }}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="accounting-report-table-body">
|
<div class="accounting-report-table-body">
|
||||||
{% for account in report.accounts %}
|
{% for account in report.accounts %}
|
||||||
<a class="accounting-report-table-row" href="{{ account.url }}">
|
<a class="accounting-report-table-row" href="{{ account.url }}" role="row">
|
||||||
<div>
|
<div role="cell">
|
||||||
<span class="d-none d-md-inline">{{ account.account.code }}</span>
|
<span class="d-none d-md-inline">{{ account.account.code }}</span>
|
||||||
{{ account.account.title }}
|
{{ account.account.title }}
|
||||||
</div>
|
</div>
|
||||||
<div class="accounting-amount">{{ account.debit|accounting_format_amount|accounting_default }}</div>
|
<div class="accounting-amount" role="cell">{{ account.debit|accounting_format_amount|accounting_default }}</div>
|
||||||
<div class="accounting-amount">{{ account.credit|accounting_format_amount|accounting_default }}</div>
|
<div class="accounting-amount" role="cell">{{ account.credit|accounting_format_amount|accounting_default }}</div>
|
||||||
</a>
|
</a>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</div>
|
</div>
|
||||||
<div class="accounting-report-table-footer">
|
<div class="accounting-report-table-footer">
|
||||||
<div class="accounting-report-table-row">
|
<div class="accounting-report-table-row" role="row">
|
||||||
<div>{{ A_("Total") }}</div>
|
<div role="cell">{{ A_("Total") }}</div>
|
||||||
<div class="accounting-amount">{{ report.total.debit|accounting_format_amount }}</div>
|
<div class="accounting-amount" role="cell">{{ report.total.debit|accounting_format_amount }}</div>
|
||||||
<div class="accounting-amount">{{ report.total.credit|accounting_format_amount }}</div>
|
<div class="accounting-amount" role="cell">{{ report.total.credit|accounting_format_amount }}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -52,20 +52,20 @@ First written: 2023/4/8
|
|||||||
</h2>
|
</h2>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="accounting-report-table accounting-unapplied-account-table">
|
<div class="accounting-report-table accounting-unapplied-account-table" role="table">
|
||||||
<div class="accounting-report-table-header">
|
<div class="accounting-report-table-header">
|
||||||
<div class="accounting-report-table-row">
|
<div class="accounting-report-table-row" role="row">
|
||||||
<div class="accounting-amount">{{ A_("Count") }}</div>
|
<div class="accounting-amount" role="columnheader">{{ A_("Count") }}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="accounting-report-table-body">
|
<div class="accounting-report-table-body">
|
||||||
{% for account in report.accounts %}
|
{% for account in report.accounts %}
|
||||||
<a class="accounting-report-table-row" href="{{ url_for("accounting-report.unapplied", currency=report.currency, account=account, period=report.period) }}">
|
<a class="accounting-report-table-row" href="{{ url_for("accounting-report.unapplied", currency=report.currency, account=account, period=report.period) }}" role="row">
|
||||||
<div>
|
<div role="cell">
|
||||||
<span class="d-none d-md-inline">{{ account.code }}</span>
|
<span class="d-none d-md-inline">{{ account.code }}</span>
|
||||||
{{ account.title }}
|
{{ account.title }}
|
||||||
</div>
|
</div>
|
||||||
<div class="accounting-amount">{{ account.count }}</div>
|
<div class="accounting-amount" role="cell">{{ account.count }}</div>
|
||||||
</a>
|
</a>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -45,22 +45,22 @@ First written: 2023/4/7
|
|||||||
{% include "accounting/include/pagination.html" %}
|
{% include "accounting/include/pagination.html" %}
|
||||||
{% endwith %}
|
{% endwith %}
|
||||||
|
|
||||||
<div class="d-none d-md-block accounting-report-table accounting-unapplied-table">
|
<div class="d-none d-md-block accounting-report-table accounting-unapplied-table" role="table">
|
||||||
<div class="accounting-report-table-header">
|
<div class="accounting-report-table-header">
|
||||||
<div class="accounting-report-table-row">
|
<div class="accounting-report-table-row" role="row">
|
||||||
<div>{{ A_("Date") }}</div>
|
<div role="columnheader">{{ A_("Date") }}</div>
|
||||||
<div>{{ A_("Description") }}</div>
|
<div role="columnheader">{{ A_("Description") }}</div>
|
||||||
<div class="accounting-amount">{{ A_("Amount") }}</div>
|
<div class="accounting-amount" role="columnheader">{{ A_("Amount") }}</div>
|
||||||
<div class="accounting-amount">{{ A_("Net Balance") }}</div>
|
<div class="accounting-amount" role="columnheader">{{ A_("Net Balance") }}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="accounting-report-table-body">
|
<div class="accounting-report-table-body">
|
||||||
{% for line_item in report.line_items %}
|
{% for line_item in report.line_items %}
|
||||||
<a class="accounting-report-table-row" href="{{ url_for("accounting.journal-entry.detail", journal_entry=line_item.journal_entry)|accounting_append_next }}">
|
<a class="accounting-report-table-row" href="{{ url_for("accounting.journal-entry.detail", journal_entry=line_item.journal_entry)|accounting_append_next }}" role="row">
|
||||||
<div>{{ line_item.journal_entry.date|accounting_format_date }}</div>
|
<div role="cell">{{ line_item.journal_entry.date|accounting_format_date }}</div>
|
||||||
<div>{{ line_item.description|accounting_default }}</div>
|
<div role="cell">{{ line_item.description|accounting_default }}</div>
|
||||||
<div class="accounting-amount">{{ line_item.amount|accounting_format_amount }}</div>
|
<div class="accounting-amount" role="cell">{{ line_item.amount|accounting_format_amount }}</div>
|
||||||
<div class="accounting-amount">{{ line_item.net_balance|accounting_format_amount }}</div>
|
<div class="accounting-amount" role="cell">{{ line_item.net_balance|accounting_format_amount }}</div>
|
||||||
</a>
|
</a>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -52,20 +52,20 @@ First written: 2023/4/17
|
|||||||
</h2>
|
</h2>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="accounting-report-table accounting-unapplied-account-table">
|
<div class="accounting-report-table accounting-unapplied-account-table" role="table">
|
||||||
<div class="accounting-report-table-header">
|
<div class="accounting-report-table-header">
|
||||||
<div class="accounting-report-table-row">
|
<div class="accounting-report-table-row" role="row">
|
||||||
<div class="accounting-amount">{{ A_("Count") }}</div>
|
<div class="accounting-amount" role="columnheader">{{ A_("Count") }}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="accounting-report-table-body">
|
<div class="accounting-report-table-body">
|
||||||
{% for account in report.accounts %}
|
{% for account in report.accounts %}
|
||||||
<a class="accounting-report-table-row" href="{{ url_for("accounting-report.unmatched", currency=report.currency, account=account, period=report.period) }}">
|
<a class="accounting-report-table-row" href="{{ url_for("accounting-report.unmatched", currency=report.currency, account=account, period=report.period) }}" role="row">
|
||||||
<div>
|
<div role="cell">
|
||||||
<span class="d-none d-md-inline">{{ account.code }}</span>
|
<span class="d-none d-md-inline">{{ account.code }}</span>
|
||||||
{{ account.title }}
|
{{ account.title }}
|
||||||
</div>
|
</div>
|
||||||
<div class="accounting-amount">{{ account.count }}</div>
|
<div class="accounting-amount" role="cell">{{ account.count }}</div>
|
||||||
</a>
|
</a>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ First written: 2023/4/17
|
|||||||
|
|
||||||
{% if report.matched_pairs %}
|
{% if report.matched_pairs %}
|
||||||
<button class="btn btn-primary" type="button" data-bs-toggle="modal" data-bs-target="#accounting-match-modal">
|
<button class="btn btn-primary" type="button" data-bs-toggle="modal" data-bs-target="#accounting-match-modal">
|
||||||
<i class="fa-solid fa-link"></i>
|
<i class="fa-solid fa-link" aria-hidden="true"></i>
|
||||||
{{ A_("Match") }}
|
{{ A_("Match") }}
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
@@ -86,29 +86,29 @@ First written: 2023/4/17
|
|||||||
{% include "accounting/include/pagination.html" %}
|
{% include "accounting/include/pagination.html" %}
|
||||||
{% endwith %}
|
{% endwith %}
|
||||||
|
|
||||||
<div class="d-none d-md-block accounting-report-table accounting-unmatched-table">
|
<div class="d-none d-md-block accounting-report-table accounting-unmatched-table" role="table">
|
||||||
<div class="accounting-report-table-header">
|
<div class="accounting-report-table-header">
|
||||||
<div class="accounting-report-table-row">
|
<div class="accounting-report-table-row" role="row">
|
||||||
<div>{{ A_("Date") }}</div>
|
<div role="columnheader">{{ A_("Date") }}</div>
|
||||||
<div>{{ A_("Description") }}</div>
|
<div role="columnheader">{{ A_("Description") }}</div>
|
||||||
<div class="accounting-amount">{{ A_("Debit") }}</div>
|
<div class="accounting-amount" role="columnheader">{{ A_("Debit") }}</div>
|
||||||
<div class="accounting-amount">{{ A_("Credit") }}</div>
|
<div class="accounting-amount" role="columnheader">{{ A_("Credit") }}</div>
|
||||||
<div class="accounting-amount">{{ A_("Balance") }}</div>
|
<div class="accounting-amount" role="columnheader">{{ A_("Balance") }}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="accounting-report-table-body">
|
<div class="accounting-report-table-body">
|
||||||
{% for line_item in report.line_items %}
|
{% for line_item in report.line_items %}
|
||||||
<a class="accounting-report-table-row {% if not line_item.match %} accounting-report-table-row-danger {% endif %}" href="{{ url_for("accounting.journal-entry.detail", journal_entry=line_item.journal_entry)|accounting_append_next }}">
|
<a class="accounting-report-table-row {% if not line_item.match %} accounting-report-table-row-danger {% endif %}" href="{{ url_for("accounting.journal-entry.detail", journal_entry=line_item.journal_entry)|accounting_append_next }}" role="row">
|
||||||
<div>{{ line_item.journal_entry.date|accounting_format_date }}</div>
|
<div role="cell">{{ line_item.journal_entry.date|accounting_format_date }}</div>
|
||||||
<div>
|
<div role="cell">
|
||||||
{{ line_item.description|accounting_default }}
|
{{ line_item.description|accounting_default }}
|
||||||
{% if line_item.match %}
|
{% if line_item.match %}
|
||||||
<div class="small">{{ A_("Can match %(item)s", item=line_item.match) }}</div>
|
<div class="small">{{ A_("Can match %(item)s", item=line_item.match) }}</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
<div class="accounting-amount">{{ line_item.debit|accounting_format_amount|accounting_default }}</div>
|
<div class="accounting-amount" role="cell">{{ line_item.debit|accounting_format_amount|accounting_default }}</div>
|
||||||
<div class="accounting-amount">{{ line_item.credit|accounting_format_amount|accounting_default }}</div>
|
<div class="accounting-amount" role="cell">{{ line_item.credit|accounting_format_amount|accounting_default }}</div>
|
||||||
<div class="accounting-amount {% if line_item.balance < 0 %} text-danger {% endif %}">{{ line_item.balance|accounting_report_format_amount }}</div>
|
<div class="accounting-amount {% if line_item.balance < 0 %} text-danger {% endif %}" role="cell">{{ line_item.balance|accounting_report_format_amount }}</div>
|
||||||
</a>
|
</a>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user