Renamed summary helper to summary editor.

This commit is contained in:
依瑪貓 2023-03-03 23:24:24 +08:00
parent 11966a52ba
commit 4eb9346d8d
11 changed files with 339 additions and 339 deletions

View File

@ -1,5 +1,5 @@
/* The Mia! Accounting Flask Project
* summary-helper.js: The JavaScript for the summary helper
* summary-editor.js: The JavaScript for the summary editor
*/
/* Copyright (c) 2023 imacat.
@ -23,23 +23,23 @@
// Initializes the page JavaScript.
document.addEventListener("DOMContentLoaded", function () {
SummaryHelper.initialize();
SummaryEditor.initialize();
});
/**
* A summary helper.
* A summary editor.
*
*/
class SummaryHelper {
class SummaryEditor {
/**
* The summary helper form
* The summary editor form
* @type {HTMLFormElement}
*/
#form;
/**
* The modal of the summary helper
* The modal of the summary editor
* @type {HTMLFormElement}
*/
#modal;
@ -129,14 +129,14 @@ class SummaryHelper {
tabPlanes = {};
/**
* Constructs a summary helper.
* Constructs a summary editor.
*
* @param form {HTMLFormElement} the summary helper form
* @param form {HTMLFormElement} the summary editor form
*/
constructor(form) {
this.#form = form;
this.#entryType = form.dataset.entryType;
this.prefix = "accounting-summary-helper-" + form.dataset.entryType;
this.prefix = "accounting-summary-editor-" + form.dataset.entryType;
this.#modal = document.getElementById(this.prefix + "-modal");
this.summary = document.getElementById(this.prefix + "-summary");
this.number = document.getElementById(this.prefix + "-number-number");
@ -156,13 +156,13 @@ class SummaryHelper {
}
this.currentTab = this.tabPlanes.general;
this.#initializeSuggestedAccounts();
const helper = this;
const editor = this;
this.summary.onchange = function () {
helper.#onSummaryChange();
editor.#onSummaryChange();
};
this.#form.onsubmit = function () {
if (helper.currentTab.validate()) {
helper.#submit();
if (editor.currentTab.validate()) {
editor.#submit();
}
return false;
};
@ -212,10 +212,10 @@ class SummaryHelper {
*
*/
#initializeSuggestedAccounts() {
const helper = this;
const editor = this;
for (const accountButton of this.#accountButtons) {
accountButton.onclick = function () {
helper.#selectAccount(accountButton);
editor.#selectAccount(accountButton);
};
}
}
@ -260,7 +260,7 @@ class SummaryHelper {
}
/**
* The callback when the summary helper is shown.
* The callback when the summary editor is shown.
*
*/
#onOpen() {
@ -270,7 +270,7 @@ class SummaryHelper {
}
/**
* Resets the summary helper.
* Resets the summary editor.
*
*/
#reset() {
@ -282,36 +282,36 @@ class SummaryHelper {
}
/**
* The summary helpers.
* @type {{debit: SummaryHelper, credit: SummaryHelper}}
* The summary editors.
* @type {{debit: SummaryEditor, credit: SummaryEditor}}
*/
static #helpers = {}
static #editors = {}
/**
* Initializes the summary helpers.
* Initializes the summary editors.
*
*/
static initialize() {
const forms = Array.from(document.getElementsByClassName("accounting-summary-helper"));
const forms = Array.from(document.getElementsByClassName("accounting-summary-editor"));
const entryForm = document.getElementById("accounting-entry-form");
const formSummaryControl = document.getElementById("accounting-entry-form-summary-control");
for (const form of forms) {
const helper = new SummaryHelper(form);
this.#helpers[helper.#entryType] = helper;
const editor = new SummaryEditor(form);
this.#editors[editor.#entryType] = editor;
}
const helpers = this;
const editors = this;
formSummaryControl.onclick = function () {
helpers.#helpers[entryForm.dataset.entryType].#onOpen();
editors.#editors[entryForm.dataset.entryType].#onOpen();
};
}
/**
* Initializes the summary helper for a new journal entry.
* Initializes the summary editor for a new journal entry.
*
* @param entryType {string} the entry type, either "debit" or "credit"
*/
static initializeNewJournalEntry(entryType) {
this.#helpers[entryType].#onOpen();
this.#editors[entryType].#onOpen();
}
}
@ -324,10 +324,10 @@ class SummaryHelper {
class TabPlane {
/**
* The parent summary helper
* @type {SummaryHelper}
* The parent summary editor
* @type {SummaryEditor}
*/
helper;
editor;
/**
* The prefix of the HTML ID and classes
@ -350,11 +350,11 @@ class TabPlane {
/**
* Constructs a tab plane.
*
* @param helper {SummaryHelper} the parent summary helper
* @param editor {SummaryEditor} the parent summary editor
*/
constructor(helper) {
this.helper = helper;
this.prefix = this.helper.prefix + "-" + this.tabId();
constructor(editor) {
this.editor = editor;
this.prefix = this.editor.prefix + "-" + this.tabId();
this.#tab = document.getElementById(this.prefix + "-tab");
this.#page = document.getElementById(this.prefix + "-page");
const tabPlane = this;
@ -399,7 +399,7 @@ class TabPlane {
*
*/
switchToMe() {
for (const tabPlane of Object.values(this.helper.tabPlanes)) {
for (const tabPlane of Object.values(this.editor.tabPlanes)) {
tabPlane.#tab.classList.remove("active")
tabPlane.#tab.ariaCurrent = "false";
tabPlane.#page.classList.add("d-none");
@ -409,7 +409,7 @@ class TabPlane {
this.#tab.ariaCurrent = "page";
this.#page.classList.remove("d-none");
this.#page.ariaCurrent = "page";
this.helper.currentTab = this;
this.editor.currentTab = this;
}
}
@ -442,11 +442,11 @@ class TagTabPlane extends TabPlane {
/**
* Constructs a tab plane.
*
* @param helper {SummaryHelper} the parent summary helper
* @param editor {SummaryEditor} the parent summary editor
* @override
*/
constructor(helper) {
super(helper);
constructor(editor) {
super(editor);
this.tag = document.getElementById(this.prefix + "-tag");
this.tagError = document.getElementById(this.prefix + "-tag-error");
// noinspection JSValidateTypes
@ -459,7 +459,7 @@ class TagTabPlane extends TabPlane {
if (tagButton.dataset.value === tabPlane.tag.value) {
tagButton.classList.remove("btn-outline-primary");
tagButton.classList.add("btn-primary");
tabPlane.helper.filterSuggestedAccounts(tagButton);
tabPlane.editor.filterSuggestedAccounts(tagButton);
isMatched = true;
} else {
tagButton.classList.remove("btn-primary");
@ -467,7 +467,7 @@ class TagTabPlane extends TabPlane {
}
}
if (!isMatched) {
tabPlane.helper.filterSuggestedAccounts(null);
tabPlane.editor.filterSuggestedAccounts(null);
}
tabPlane.updateSummary();
tabPlane.validateTag();
@ -494,7 +494,7 @@ class TagTabPlane extends TabPlane {
break;
}
}
this.helper.filterSuggestedAccounts(selectedTagButton);
this.editor.filterSuggestedAccounts(selectedTagButton);
}
/**
@ -512,7 +512,7 @@ class TagTabPlane extends TabPlane {
tagButton.classList.remove("btn-outline-primary");
tagButton.classList.add("btn-primary");
tabPlane.tag.value = tagButton.dataset.value;
tabPlane.helper.filterSuggestedAccounts(tagButton);
tabPlane.editor.filterSuggestedAccounts(tagButton);
tabPlane.updateSummary();
};
}
@ -589,12 +589,12 @@ class GeneralTagTab extends TagTabPlane {
* @override
*/
updateSummary() {
const pos = this.helper.summary.value.indexOf("—");
const pos = this.editor.summary.value.indexOf("—");
const prefix = this.tag.value === ""? "": this.tag.value + "—";
if (pos === -1) {
this.helper.summary.value = prefix + this.helper.summary.value;
this.editor.summary.value = prefix + this.editor.summary.value;
} else {
this.helper.summary.value = prefix + this.helper.summary.value.substring(pos + 1);
this.editor.summary.value = prefix + this.editor.summary.value.substring(pos + 1);
}
}
@ -605,7 +605,7 @@ class GeneralTagTab extends TagTabPlane {
* @override
*/
populate() {
const found = this.helper.summary.value.match(/^([^—]+)—.+?(?:×\d+)?$/);
const found = this.editor.summary.value.match(/^([^—]+)—.+?(?:×\d+)?$/);
if (found === null) {
return false;
}
@ -614,7 +614,7 @@ class GeneralTagTab extends TagTabPlane {
if (tagButton.dataset.value === this.tag.value) {
tagButton.classList.remove("btn-outline-primary");
tagButton.classList.add("btn-primary");
this.helper.filterSuggestedAccounts(tagButton);
this.editor.filterSuggestedAccounts(tagButton);
}
}
this.switchToMe();
@ -671,11 +671,11 @@ class GeneralTripTab extends TagTabPlane {
/**
* Constructs a tab plane.
*
* @param helper {SummaryHelper} the parent summary helper
* @param editor {SummaryEditor} the parent summary editor
* @override
*/
constructor(helper) {
super(helper);
constructor(editor) {
super(editor);
this.#from = document.getElementById(this.prefix + "-from");
this.#fromError = document.getElementById(this.prefix + "-from-error");
this.#to = document.getElementById(this.prefix + "-to");
@ -727,7 +727,7 @@ class GeneralTripTab extends TagTabPlane {
break;
}
}
this.helper.summary.value = this.tag.value + "—" + this.#from.value + direction + this.#to.value;
this.editor.summary.value = this.tag.value + "—" + this.#from.value + direction + this.#to.value;
}
/**
@ -761,7 +761,7 @@ class GeneralTripTab extends TagTabPlane {
* @override
*/
populate() {
const found = this.helper.summary.value.match(/^([^—]+)—([^—→↔]+)([→↔])(.+?)(?:×\d+)?$/);
const found = this.editor.summary.value.match(/^([^—]+)—([^—→↔]+)([→↔])(.+?)(?:×\d+)?$/);
if (found === null) {
return false;
}
@ -781,7 +781,7 @@ class GeneralTripTab extends TagTabPlane {
if (tagButton.dataset.value === this.tag.value) {
tagButton.classList.remove("btn-outline-primary");
tagButton.classList.add("btn-primary");
this.helper.filterSuggestedAccounts(tagButton);
this.editor.filterSuggestedAccounts(tagButton);
}
}
this.switchToMe();
@ -879,11 +879,11 @@ class BusTripTab extends TagTabPlane {
/**
* Constructs a tab plane.
*
* @param helper {SummaryHelper} the parent summary helper
* @param editor {SummaryEditor} the parent summary editor
* @override
*/
constructor(helper) {
super(helper);
constructor(editor) {
super(editor);
this.#route = document.getElementById(this.prefix + "-route");
this.#routeError = document.getElementById(this.prefix + "-route-error");
this.#from = document.getElementById(this.prefix + "-from");
@ -921,7 +921,7 @@ class BusTripTab extends TagTabPlane {
* @override
*/
updateSummary() {
this.helper.summary.value = this.tag.value + "—" + this.#route.value + "—" + this.#from.value + "→" + this.#to.value;
this.editor.summary.value = this.tag.value + "—" + this.#route.value + "—" + this.#from.value + "→" + this.#to.value;
}
/**
@ -949,7 +949,7 @@ class BusTripTab extends TagTabPlane {
* @override
*/
populate() {
const found = this.helper.summary.value.match(/^([^—]+)—([^—]+)—([^—→]+)→(.+?)(?:×\d+)?$/);
const found = this.editor.summary.value.match(/^([^—]+)—([^—]+)—([^—→]+)→(.+?)(?:×\d+)?$/);
if (found === null) {
return false;
}
@ -961,7 +961,7 @@ class BusTripTab extends TagTabPlane {
if (tagButton.dataset.value === this.tag.value) {
tagButton.classList.remove("btn-outline-primary");
tagButton.classList.add("btn-primary");
this.helper.filterSuggestedAccounts(tagButton);
this.editor.filterSuggestedAccounts(tagButton);
break;
}
}
@ -1041,11 +1041,11 @@ class RegularPaymentTab extends TabPlane {
/**
* Constructs a tab plane.
*
* @param helper {SummaryHelper} the parent summary helper
* @param editor {SummaryEditor} the parent summary editor
* @override
*/
constructor(helper) {
super(helper);
constructor(editor) {
super(editor);
// noinspection JSValidateTypes
this.#payments = Array.from(document.getElementsByClassName(this.prefix + "-payment"));
}
@ -1103,19 +1103,19 @@ class NumberTab extends TabPlane {
/**
* Constructs a tab plane.
*
* @param helper {SummaryHelper} the parent summary helper
* @param editor {SummaryEditor} the parent summary editor
* @override
*/
constructor(helper) {
super(helper);
constructor(editor) {
super(editor);
const tabPlane = this;
this.helper.number.onchange = function () {
const found = tabPlane.helper.summary.value.match(/^(.+)×(\d+)$/);
this.editor.number.onchange = function () {
const found = tabPlane.editor.summary.value.match(/^(.+)×(\d+)$/);
if (found !== null) {
tabPlane.helper.summary.value = found[1];
tabPlane.editor.summary.value = found[1];
}
if (parseInt(tabPlane.helper.number.value) > 1) {
tabPlane.helper.summary.value = tabPlane.helper.summary.value + "×" + tabPlane.helper.number.value;
if (parseInt(tabPlane.editor.number.value) > 1) {
tabPlane.editor.summary.value = tabPlane.editor.summary.value + "×" + tabPlane.editor.number.value;
}
};
}
@ -1136,7 +1136,7 @@ class NumberTab extends TabPlane {
* @override
*/
reset() {
this.helper.number.value = "";
this.editor.number.value = "";
}
/**
@ -1146,11 +1146,11 @@ class NumberTab extends TabPlane {
* @override
*/
populate() {
const found = this.helper.summary.value.match(/^.+×(\d+)$/);
const found = this.editor.summary.value.match(/^.+×(\d+)$/);
if (found === null) {
this.helper.number.value = "";
this.editor.number.value = "";
} else {
this.helper.number.value = found[1];
this.editor.number.value = found[1];
}
return true;
}

View File

@ -171,7 +171,7 @@ function initializeNewEntryButton(button) {
formAccount.dataset.code = "";
formAccount.dataset.text = "";
formAccountError.innerText = "";
formSummaryControl.dataset.bsTarget = "#accounting-summary-helper-" + button.dataset.entryType + "-modal";
formSummaryControl.dataset.bsTarget = "#accounting-summary-editor-" + button.dataset.entryType + "-modal";
formSummaryControl.classList.remove("accounting-not-empty");
formSummaryControl.classList.remove("is-invalid");
formSummary.dataset.value = "";
@ -181,7 +181,7 @@ function initializeNewEntryButton(button) {
formAmount.classList.remove("is-invalid");
formAmountError.innerText = "";
AccountSelector.initializeJournalEntryForm();
SummaryHelper.initializeNewJournalEntry(button.dataset.entryType);
SummaryEditor.initializeNewJournalEntry(button.dataset.entryType);
};
}
@ -228,7 +228,7 @@ function initializeJournalEntry(entry) {
formAccount.innerText = accountCode.dataset.text;
formAccount.dataset.code = accountCode.value;
formAccount.dataset.text = accountCode.dataset.text;
formSummaryControl.dataset.bsTarget = "#accounting-summary-helper-" + entry.dataset.entryType + "-modal";
formSummaryControl.dataset.bsTarget = "#accounting-summary-editor-" + entry.dataset.entryType + "-modal";
if (summary.value === "") {
formSummaryControl.classList.remove("accounting-not-empty");
} else {

View File

@ -46,8 +46,8 @@ First written: 2023/2/25
{% endblock %}
{% block form_modals %}
{% with summary_helper = form.summary_helper.debit %}
{% include "accounting/transaction/include/summary-helper-modal.html" %}
{% with summary_editor = form.summary_editor.debit %}
{% include "accounting/transaction/include/summary-editor-modal.html" %}
{% endwith %}
{% with entry_type = "debit",
account_options = form.debit_account_options %}

View File

@ -25,7 +25,7 @@ First written: 2023/2/26
<script src="{{ url_for("accounting.static", filename="js/drag-and-drop-reorder.js") }}"></script>
<script src="{{ url_for("accounting.static", filename="js/transaction-form.js") }}"></script>
<script src="{{ url_for("accounting.static", filename="js/account-selector.js") }}"></script>
<script src="{{ url_for("accounting.static", filename="js/summary-helper.js") }}"></script>
<script src="{{ url_for("accounting.static", filename="js/summary-editor.js") }}"></script>
{% endblock %}
{% block content %}

View File

@ -0,0 +1,181 @@
{#
The Mia! Accounting Flask Project
summary-editor-modal.html: The modal of the summary editor
Copyright (c) 2023 imacat.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
Author: imacat@mail.imacat.idv.tw (imacat)
First written: 2023/2/28
#}
<form id="accounting-summary-editor-{{ summary_editor.type }}" class="accounting-summary-editor" data-entry-type="{{ summary_editor.type }}">
<div id="accounting-summary-editor-{{ summary_editor.type }}-modal" class="modal fade" tabindex="-1" aria-labelledby="accounting-summary-editor-{{ summary_editor.type }}-modal-label" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h1 class="modal-title fs-5" id="accounting-summary-editor-{{ summary_editor.type }}-modal-label">
<label for="accounting-summary-editor-{{ summary_editor.type }}-summary">{{ A_("Summary") }}</label>
</h1>
<button class="btn-close" type="button" data-bs-toggle="modal" data-bs-target="#accounting-entry-form-modal" aria-label="{{ A_("Close") }}"></button>
</div>
<div class="modal-body">
<div class="mb-3">
<input id="accounting-summary-editor-{{ summary_editor.type }}-summary" class="form-control" type="text" aria-labelledby="accounting-summary-editor-{{ summary_editor.type }}-modal-label">
</div>
<ul class="nav nav-tabs mb-2">
<li class="nav-item">
<span id="accounting-summary-editor-{{ summary_editor.type }}-general-tab" class="nav-link active accounting-clickable accounting-summary-editor-{{ summary_editor.type }}-tab" aria-current="page" data-tab-id="general">
{{ A_("General") }}
</span>
</li>
<li class="nav-item">
<span id="accounting-summary-editor-{{ summary_editor.type }}-travel-tab" class="nav-link accounting-clickable accounting-summary-editor-{{ summary_editor.type }}-tab" aria-current="false" data-tab-id="travel">
{{ A_("Travel") }}
</span>
</li>
<li class="nav-item">
<span id="accounting-summary-editor-{{ summary_editor.type }}-bus-tab" class="nav-link accounting-clickable accounting-summary-editor-{{ summary_editor.type }}-tab" aria-current="false" data-tab-id="bus">
{{ A_("Bus") }}
</span>
</li>
<li class="nav-item">
<span id="accounting-summary-editor-{{ summary_editor.type }}-regular-tab" class="nav-link accounting-clickable accounting-summary-editor-{{ summary_editor.type }}-tab" aria-current="false" data-tab-id="regular">
{{ A_("Regular") }}
</span>
</li>
<li class="nav-item">
<span id="accounting-summary-editor-{{ summary_editor.type }}-number-tab" class="nav-link accounting-clickable accounting-summary-editor-{{ summary_editor.type }}-tab" aria-current="false" data-tab-id="number">
{{ A_("Number") }}
</span>
</li>
</ul>
{# A general summary with a tag #}
<div id="accounting-summary-editor-{{ summary_editor.type }}-general-page" class="accounting-summary-editor-{{ summary_editor.type }}-page" aria-current="page" aria-labelledby="accounting-summary-editor-{{ summary_editor.type }}-tab-general" data-tab-id="general">
<div class="form-floating mb-2">
<input id="accounting-summary-editor-{{ summary_editor.type }}-general-tag" class="form-control" type="text" value="" placeholder=" ">
<label class="form-label" for="accounting-summary-editor-{{ summary_editor.type }}-general-tag">{{ A_("Tag") }}</label>
<div id="accounting-summary-editor-{{ summary_editor.type }}-general-tag-error" class="invalid-feedback"></div>
</div>
<div>
{% for tag in summary_editor.general.tags %}
<button class="btn btn-outline-primary accounting-summary-editor-{{ summary_editor.type }}-general-btn-tag" type="button" tabindex="-1" data-value="{{ tag.name }}" data-accounts="{{ tag.account_codes|tojson|forceescape }}">
{{ tag }}
</button>
{% endfor %}
</div>
</div>
{# A general trip with the origin and distination #}
<div id="accounting-summary-editor-{{ summary_editor.type }}-travel-page" class="accounting-summary-editor-{{ summary_editor.type }}-page d-none" aria-current="false" aria-labelledby="accounting-summary-editor-{{ summary_editor.type }}-tab-travel" data-tab-id="travel">
<div class="form-floating mb-2">
<input id="accounting-summary-editor-{{ summary_editor.type }}-travel-tag" class="form-control" type="text" value="" placeholder=" ">
<label class="form-label" for="accounting-summary-editor-{{ summary_editor.type }}-travel-tag">{{ A_("Tag") }}</label>
<div id="accounting-summary-editor-{{ summary_editor.type }}-travel-tag-error" class="invalid-feedback"></div>
</div>
<div>
{% for tag in summary_editor.travel.tags %}
<button class="btn btn-outline-primary accounting-summary-editor-{{ summary_editor.type }}-travel-btn-tag" type="button" tabindex="-1" data-value="{{ tag.name }}" data-accounts="{{ tag.account_codes|tojson|forceescape }}">
{{ tag }}
</button>
{% endfor %}
</div>
<div class="d-flex justify-content-between mt-2">
<div class="form-floating">
<input id="accounting-summary-editor-{{ summary_editor.type }}-travel-from" class="form-control" type="text" value="" placeholder=" ">
<label class="form-label" for="accounting-summary-editor-{{ summary_editor.type }}-travel-from">{{ A_("From") }}</label>
<div id="accounting-summary-editor-{{ summary_editor.type }}-travel-from-error" class="invalid-feedback"></div>
</div>
<div class="btn-group-vertical ms-1 me-1">
<button class="btn btn-primary accounting-summary-editor-{{ summary_editor.type }}-travel-direction accounting-default" type="button" tabindex="-1" data-arrow="&rarr;">&rarr;</button>
<button class="btn btn-outline-primary accounting-summary-editor-{{ summary_editor.type }}-travel-direction" type="button" tabindex="-1" data-arrow="&harr;">&harr;</button>
</div>
<div class="form-floating">
<input id="accounting-summary-editor-{{ summary_editor.type }}-travel-to" class="form-control" type="text" value="" placeholder=" ">
<label class="form-label" for="accounting-summary-editor-{{ summary_editor.type }}-travel-to">{{ A_("To") }}</label>
<div id="accounting-summary-editor-{{ summary_editor.type }}-travel-to-error" class="invalid-feedback"></div>
</div>
</div>
</div>
{# A bus trip with the route name or route number, the origin and distination #}
<div id="accounting-summary-editor-{{ summary_editor.type }}-bus-page" class="accounting-summary-editor-{{ summary_editor.type }}-page d-none" aria-current="false" aria-labelledby="accounting-summary-editor-{{ summary_editor.type }}-tab-bus" data-tab-id="bus">
<div class="d-flex justify-content-between mb-2">
<div class="form-floating me-2">
<input id="accounting-summary-editor-{{ summary_editor.type }}-bus-tag" class="form-control" type="text" value="" placeholder=" ">
<label class="form-label" for="accounting-summary-editor-{{ summary_editor.type }}-bus-tag">{{ A_("Tag") }}</label>
<div id="accounting-summary-editor-{{ summary_editor.type }}-bus-tag-error" class="invalid-feedback"></div>
</div>
<div class="form-floating">
<input id="accounting-summary-editor-{{ summary_editor.type }}-bus-route" class="form-control" type="text" value="" placeholder=" ">
<label class="form-label" for="accounting-summary-editor-{{ summary_editor.type }}-bus-route">{{ A_("Route") }}</label>
<div id="accounting-summary-editor-{{ summary_editor.type }}-bus-route-error" class="invalid-feedback"></div>
</div>
</div>
<div>
{% for tag in summary_editor.bus.tags %}
<button class="btn btn-outline-primary accounting-summary-editor-{{ summary_editor.type }}-bus-btn-tag" type="button" tabindex="-1" data-value="{{ tag.name }}" data-accounts="{{ tag.account_codes|tojson|forceescape }}">
{{ tag }}
</button>
{% endfor %}
</div>
<div class="d-flex justify-content-between mt-2">
<div class="form-floating me-2">
<input id="accounting-summary-editor-{{ summary_editor.type }}-bus-from" class="form-control" type="text" value="" placeholder=" ">
<label class="form-label" for="accounting-summary-editor-{{ summary_editor.type }}-bus-from">{{ A_("From") }}</label>
<div id="accounting-summary-editor-{{ summary_editor.type }}-bus-from-error" class="invalid-feedback"></div>
</div>
<div class="form-floating">
<input id="accounting-summary-editor-{{ summary_editor.type }}-bus-to" class="form-control" type="text" value="" placeholder=" ">
<label class="form-label" for="accounting-summary-editor-{{ summary_editor.type }}-bus-to">{{ A_("To") }}</label>
<div id="accounting-summary-editor-{{ summary_editor.type }}-bus-to-error" class="invalid-feedback"></div>
</div>
</div>
</div>
{# A regular income or payment #}
<div id="accounting-summary-editor-{{ summary_editor.type }}-regular-page" class="accounting-summary-editor-{{ summary_editor.type }}-page d-none" aria-current="false" aria-labelledby="accounting-summary-editor-{{ summary_editor.type }}-tab-regular" data-tab-id="regular">
{# TODO: To be done #}
</div>
{# The number of items #}
<div id="accounting-summary-editor-{{ summary_editor.type }}-number-page" class="accounting-summary-editor-{{ summary_editor.type }}-page d-none" aria-current="false" aria-labelledby="accounting-summary-editor-{{ summary_editor.type }}-tab-number" data-tab-id="number">
<div class="form-floating">
<input id="accounting-summary-editor-{{ summary_editor.type }}-number-number" class="form-control" type="number" min="1" value="" placeholder=" ">
<label class="form-label" for="accounting-summary-editor-{{ summary_editor.type }}-number">{{ A_("The number of items") }}</label>
<div id="accounting-summary-editor-{{ summary_editor.type }}-number-error" class="invalid-feedback"></div>
</div>
</div>
{# The suggested accounts #}
<div class="mt-3">
{% for account in summary_editor.accounts %}
<button class="btn btn-outline-primary d-none accounting-summary-editor-{{ summary_editor.type }}-account" type="button" data-code="{{ account.code }}" data-text="{{ account }}">
{{ account }}
</button>
{% endfor %}
</div>
</div>
<div class="modal-footer">
<button class="btn btn-secondary" type="button" data-bs-toggle="modal" data-bs-target="#accounting-entry-form-modal">{{ A_("Cancel") }}</button>
<button id="accounting-summary-editor-{{ summary_editor.type }}-btn-save" type="submit" class="btn btn-primary">{{ A_("Save") }}</button>
</div>
</div>
</div>
</div>
</form>

View File

@ -1,181 +0,0 @@
{#
The Mia! Accounting Flask Project
entry-form-modal.html: The modal of the summary helper
Copyright (c) 2023 imacat.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
Author: imacat@mail.imacat.idv.tw (imacat)
First written: 2023/2/28
#}
<form id="accounting-summary-helper-{{ summary_helper.type }}" class="accounting-summary-helper" data-entry-type="{{ summary_helper.type }}">
<div id="accounting-summary-helper-{{ summary_helper.type }}-modal" class="modal fade" tabindex="-1" aria-labelledby="accounting-summary-helper-{{ summary_helper.type }}-modal-label" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h1 class="modal-title fs-5" id="accounting-summary-helper-{{ summary_helper.type }}-modal-label">
<label for="accounting-summary-helper-{{ summary_helper.type }}-summary">{{ A_("Summary") }}</label>
</h1>
<button class="btn-close" type="button" data-bs-toggle="modal" data-bs-target="#accounting-entry-form-modal" aria-label="{{ A_("Close") }}"></button>
</div>
<div class="modal-body">
<div class="mb-3">
<input id="accounting-summary-helper-{{ summary_helper.type }}-summary" class="form-control" type="text" aria-labelledby="accounting-summary-helper-{{ summary_helper.type }}-modal-label">
</div>
<ul class="nav nav-tabs mb-2">
<li class="nav-item">
<span id="accounting-summary-helper-{{ summary_helper.type }}-general-tab" class="nav-link active accounting-clickable accounting-summary-helper-{{ summary_helper.type }}-tab" aria-current="page" data-tab-id="general">
{{ A_("General") }}
</span>
</li>
<li class="nav-item">
<span id="accounting-summary-helper-{{ summary_helper.type }}-travel-tab" class="nav-link accounting-clickable accounting-summary-helper-{{ summary_helper.type }}-tab" aria-current="false" data-tab-id="travel">
{{ A_("Travel") }}
</span>
</li>
<li class="nav-item">
<span id="accounting-summary-helper-{{ summary_helper.type }}-bus-tab" class="nav-link accounting-clickable accounting-summary-helper-{{ summary_helper.type }}-tab" aria-current="false" data-tab-id="bus">
{{ A_("Bus") }}
</span>
</li>
<li class="nav-item">
<span id="accounting-summary-helper-{{ summary_helper.type }}-regular-tab" class="nav-link accounting-clickable accounting-summary-helper-{{ summary_helper.type }}-tab" aria-current="false" data-tab-id="regular">
{{ A_("Regular") }}
</span>
</li>
<li class="nav-item">
<span id="accounting-summary-helper-{{ summary_helper.type }}-number-tab" class="nav-link accounting-clickable accounting-summary-helper-{{ summary_helper.type }}-tab" aria-current="false" data-tab-id="number">
{{ A_("Number") }}
</span>
</li>
</ul>
{# A general summary with a tag #}
<div id="accounting-summary-helper-{{ summary_helper.type }}-general-page" class="accounting-summary-helper-{{ summary_helper.type }}-page" aria-current="page" aria-labelledby="accounting-summary-helper-{{ summary_helper.type }}-tab-general" data-tab-id="general">
<div class="form-floating mb-2">
<input id="accounting-summary-helper-{{ summary_helper.type }}-general-tag" class="form-control" type="text" value="" placeholder=" ">
<label class="form-label" for="accounting-summary-helper-{{ summary_helper.type }}-general-tag">{{ A_("Tag") }}</label>
<div id="accounting-summary-helper-{{ summary_helper.type }}-general-tag-error" class="invalid-feedback"></div>
</div>
<div>
{% for tag in summary_helper.general.tags %}
<button class="btn btn-outline-primary accounting-summary-helper-{{ summary_helper.type }}-general-btn-tag" type="button" tabindex="-1" data-value="{{ tag.name }}" data-accounts="{{ tag.account_codes|tojson|forceescape }}">
{{ tag }}
</button>
{% endfor %}
</div>
</div>
{# A general trip with the origin and distination #}
<div id="accounting-summary-helper-{{ summary_helper.type }}-travel-page" class="accounting-summary-helper-{{ summary_helper.type }}-page d-none" aria-current="false" aria-labelledby="accounting-summary-helper-{{ summary_helper.type }}-tab-travel" data-tab-id="travel">
<div class="form-floating mb-2">
<input id="accounting-summary-helper-{{ summary_helper.type }}-travel-tag" class="form-control" type="text" value="" placeholder=" ">
<label class="form-label" for="accounting-summary-helper-{{ summary_helper.type }}-travel-tag">{{ A_("Tag") }}</label>
<div id="accounting-summary-helper-{{ summary_helper.type }}-travel-tag-error" class="invalid-feedback"></div>
</div>
<div>
{% for tag in summary_helper.travel.tags %}
<button class="btn btn-outline-primary accounting-summary-helper-{{ summary_helper.type }}-travel-btn-tag" type="button" tabindex="-1" data-value="{{ tag.name }}" data-accounts="{{ tag.account_codes|tojson|forceescape }}">
{{ tag }}
</button>
{% endfor %}
</div>
<div class="d-flex justify-content-between mt-2">
<div class="form-floating">
<input id="accounting-summary-helper-{{ summary_helper.type }}-travel-from" class="form-control" type="text" value="" placeholder=" ">
<label class="form-label" for="accounting-summary-helper-{{ summary_helper.type }}-travel-from">{{ A_("From") }}</label>
<div id="accounting-summary-helper-{{ summary_helper.type }}-travel-from-error" class="invalid-feedback"></div>
</div>
<div class="btn-group-vertical ms-1 me-1">
<button class="btn btn-primary accounting-summary-helper-{{ summary_helper.type }}-travel-direction accounting-default" type="button" tabindex="-1" data-arrow="&rarr;">&rarr;</button>
<button class="btn btn-outline-primary accounting-summary-helper-{{ summary_helper.type }}-travel-direction" type="button" tabindex="-1" data-arrow="&harr;">&harr;</button>
</div>
<div class="form-floating">
<input id="accounting-summary-helper-{{ summary_helper.type }}-travel-to" class="form-control" type="text" value="" placeholder=" ">
<label class="form-label" for="accounting-summary-helper-{{ summary_helper.type }}-travel-to">{{ A_("To") }}</label>
<div id="accounting-summary-helper-{{ summary_helper.type }}-travel-to-error" class="invalid-feedback"></div>
</div>
</div>
</div>
{# A bus trip with the route name or route number, the origin and distination #}
<div id="accounting-summary-helper-{{ summary_helper.type }}-bus-page" class="accounting-summary-helper-{{ summary_helper.type }}-page d-none" aria-current="false" aria-labelledby="accounting-summary-helper-{{ summary_helper.type }}-tab-bus" data-tab-id="bus">
<div class="d-flex justify-content-between mb-2">
<div class="form-floating me-2">
<input id="accounting-summary-helper-{{ summary_helper.type }}-bus-tag" class="form-control" type="text" value="" placeholder=" ">
<label class="form-label" for="accounting-summary-helper-{{ summary_helper.type }}-bus-tag">{{ A_("Tag") }}</label>
<div id="accounting-summary-helper-{{ summary_helper.type }}-bus-tag-error" class="invalid-feedback"></div>
</div>
<div class="form-floating">
<input id="accounting-summary-helper-{{ summary_helper.type }}-bus-route" class="form-control" type="text" value="" placeholder=" ">
<label class="form-label" for="accounting-summary-helper-{{ summary_helper.type }}-bus-route">{{ A_("Route") }}</label>
<div id="accounting-summary-helper-{{ summary_helper.type }}-bus-route-error" class="invalid-feedback"></div>
</div>
</div>
<div>
{% for tag in summary_helper.bus.tags %}
<button class="btn btn-outline-primary accounting-summary-helper-{{ summary_helper.type }}-bus-btn-tag" type="button" tabindex="-1" data-value="{{ tag.name }}" data-accounts="{{ tag.account_codes|tojson|forceescape }}">
{{ tag }}
</button>
{% endfor %}
</div>
<div class="d-flex justify-content-between mt-2">
<div class="form-floating me-2">
<input id="accounting-summary-helper-{{ summary_helper.type }}-bus-from" class="form-control" type="text" value="" placeholder=" ">
<label class="form-label" for="accounting-summary-helper-{{ summary_helper.type }}-bus-from">{{ A_("From") }}</label>
<div id="accounting-summary-helper-{{ summary_helper.type }}-bus-from-error" class="invalid-feedback"></div>
</div>
<div class="form-floating">
<input id="accounting-summary-helper-{{ summary_helper.type }}-bus-to" class="form-control" type="text" value="" placeholder=" ">
<label class="form-label" for="accounting-summary-helper-{{ summary_helper.type }}-bus-to">{{ A_("To") }}</label>
<div id="accounting-summary-helper-{{ summary_helper.type }}-bus-to-error" class="invalid-feedback"></div>
</div>
</div>
</div>
{# A regular income or payment #}
<div id="accounting-summary-helper-{{ summary_helper.type }}-regular-page" class="accounting-summary-helper-{{ summary_helper.type }}-page d-none" aria-current="false" aria-labelledby="accounting-summary-helper-{{ summary_helper.type }}-tab-regular" data-tab-id="regular">
{# TODO: To be done #}
</div>
{# The number of items #}
<div id="accounting-summary-helper-{{ summary_helper.type }}-number-page" class="accounting-summary-helper-{{ summary_helper.type }}-page d-none" aria-current="false" aria-labelledby="accounting-summary-helper-{{ summary_helper.type }}-tab-number" data-tab-id="number">
<div class="form-floating">
<input id="accounting-summary-helper-{{ summary_helper.type }}-number-number" class="form-control" type="number" min="1" value="" placeholder=" ">
<label class="form-label" for="accounting-summary-helper-{{ summary_helper.type }}-number">{{ A_("The number of items") }}</label>
<div id="accounting-summary-helper-{{ summary_helper.type }}-number-error" class="invalid-feedback"></div>
</div>
</div>
{# The suggested accounts #}
<div class="mt-3">
{% for account in summary_helper.accounts %}
<button class="btn btn-outline-primary d-none accounting-summary-helper-{{ summary_helper.type }}-account" type="button" data-code="{{ account.code }}" data-text="{{ account }}">
{{ account }}
</button>
{% endfor %}
</div>
</div>
<div class="modal-footer">
<button class="btn btn-secondary" type="button" data-bs-toggle="modal" data-bs-target="#accounting-entry-form-modal">{{ A_("Cancel") }}</button>
<button id="accounting-summary-helper-{{ summary_helper.type }}-btn-save" type="submit" class="btn btn-primary">{{ A_("Save") }}</button>
</div>
</div>
</div>
</div>
</form>

View File

@ -46,8 +46,8 @@ First written: 2023/2/25
{% endblock %}
{% block form_modals %}
{% with summary_helper = form.summary_helper.credit %}
{% include "accounting/transaction/include/summary-helper-modal.html" %}
{% with summary_editor = form.summary_editor.credit %}
{% include "accounting/transaction/include/summary-editor-modal.html" %}
{% endwith %}
{% with entry_type = "credit",
account_options = form.credit_account_options %}

View File

@ -50,11 +50,11 @@ First written: 2023/2/25
{% endblock %}
{% block form_modals %}
{% with summary_helper = form.summary_helper.debit %}
{% include "accounting/transaction/include/summary-helper-modal.html" %}
{% with summary_editor = form.summary_editor.debit %}
{% include "accounting/transaction/include/summary-editor-modal.html" %}
{% endwith %}
{% with summary_helper = form.summary_helper.credit %}
{% include "accounting/transaction/include/summary-helper-modal.html" %}
{% with summary_editor = form.summary_editor.credit %}
{% include "accounting/transaction/include/summary-editor-modal.html" %}
{% endwith %}
{% with entry_type = "debit",
account_options = form.debit_account_options %}

View File

@ -37,7 +37,7 @@ from accounting import db
from accounting.locale import lazy_gettext
from accounting.models import Transaction, Account, JournalEntry, \
TransactionCurrency, Currency
from accounting.transaction.summary_helper import SummaryHelper
from accounting.transaction.summary_editor import SummaryEditor
from accounting.utils.random_id import new_id
from accounting.utils.strip_text import strip_text, strip_multiline_text
from accounting.utils.user import get_current_user_pk
@ -391,12 +391,12 @@ class TransactionForm(FlaskForm):
if isinstance(x, str) or isinstance(x, LazyString)]
@property
def summary_helper(self) -> SummaryHelper:
"""Returns the summary helper.
def summary_editor(self) -> SummaryEditor:
"""Returns the summary editor.
:return: The summary helper.
:return: The summary editor.
"""
return SummaryHelper()
return SummaryEditor()
T = t.TypeVar("T", bound=TransactionForm)

View File

@ -14,7 +14,7 @@
# 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.
"""The summary helper.
"""The summary editor.
"""
import typing as t
@ -178,7 +178,7 @@ class SummaryEntryType:
@property
def accounts(self) -> list[SummaryAccount]:
"""Returns the suggested accounts of all tags in the summary helper in
"""Returns the suggested accounts of all tags in the summary editor in
the entry type, in their frequency order.
:return: The suggested accounts of all tags, in their frequency order.
@ -197,11 +197,11 @@ class SummaryEntryType:
key=lambda x: -freq[x])]
class SummaryHelper:
"""The summary helper."""
class SummaryEditor:
"""The summary editor."""
def __init__(self):
"""Constructs the summary helper."""
"""Constructs the summary editor."""
self.debit: SummaryEntryType = SummaryEntryType("debit")
"""The debit tags."""
self.credit: SummaryEntryType = SummaryEntryType("credit")

View File

@ -14,7 +14,7 @@
# 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.
"""The test for the summary helper.
"""The test for the summary editor.
"""
import unittest
@ -29,8 +29,8 @@ from testlib import get_client
from testlib_txn import Accounts, NEXT_URI, add_txn
class SummeryHelperTestCase(unittest.TestCase):
"""The summary helper test case."""
class SummeryEditorTestCase(unittest.TestCase):
"""The summary editor test case."""
def setUp(self) -> None:
"""Sets up the test.
@ -61,101 +61,101 @@ class SummeryHelperTestCase(unittest.TestCase):
self.client, self.csrf_token = get_client(self.app, "editor")
def test_summary_helper(self) -> None:
"""Test the summary helper.
def test_summary_editor(self) -> None:
"""Test the summary editor.
:return: None.
"""
from accounting.transaction.summary_helper import SummaryHelper
from accounting.transaction.summary_editor import SummaryEditor
for form in get_form_data(self.csrf_token):
add_txn(self.client, form)
with self.app.app_context():
helper: SummaryHelper = SummaryHelper()
editor: SummaryEditor = SummaryEditor()
# Debit-General
self.assertEqual(len(helper.debit.general.tags), 2)
self.assertEqual(helper.debit.general.tags[0].name, "Lunch")
self.assertEqual(len(helper.debit.general.tags[0].accounts), 2)
self.assertEqual(helper.debit.general.tags[0].accounts[0].code,
self.assertEqual(len(editor.debit.general.tags), 2)
self.assertEqual(editor.debit.general.tags[0].name, "Lunch")
self.assertEqual(len(editor.debit.general.tags[0].accounts), 2)
self.assertEqual(editor.debit.general.tags[0].accounts[0].code,
Accounts.MEAL)
self.assertEqual(helper.debit.general.tags[0].accounts[1].code,
self.assertEqual(editor.debit.general.tags[0].accounts[1].code,
Accounts.PAYABLE)
self.assertEqual(helper.debit.general.tags[1].name, "Dinner")
self.assertEqual(len(helper.debit.general.tags[1].accounts), 2)
self.assertEqual(helper.debit.general.tags[1].accounts[0].code,
self.assertEqual(editor.debit.general.tags[1].name, "Dinner")
self.assertEqual(len(editor.debit.general.tags[1].accounts), 2)
self.assertEqual(editor.debit.general.tags[1].accounts[0].code,
Accounts.MEAL)
self.assertEqual(helper.debit.general.tags[1].accounts[1].code,
self.assertEqual(editor.debit.general.tags[1].accounts[1].code,
Accounts.PAYABLE)
# Debit-Travel
self.assertEqual(len(helper.debit.travel.tags), 3)
self.assertEqual(helper.debit.travel.tags[0].name, "Bike")
self.assertEqual(len(helper.debit.travel.tags[0].accounts), 1)
self.assertEqual(helper.debit.travel.tags[0].accounts[0].code,
self.assertEqual(len(editor.debit.travel.tags), 3)
self.assertEqual(editor.debit.travel.tags[0].name, "Bike")
self.assertEqual(len(editor.debit.travel.tags[0].accounts), 1)
self.assertEqual(editor.debit.travel.tags[0].accounts[0].code,
Accounts.TRAVEL)
self.assertEqual(helper.debit.travel.tags[1].name, "Taxi")
self.assertEqual(len(helper.debit.travel.tags[1].accounts), 1)
self.assertEqual(helper.debit.travel.tags[1].accounts[0].code,
self.assertEqual(editor.debit.travel.tags[1].name, "Taxi")
self.assertEqual(len(editor.debit.travel.tags[1].accounts), 1)
self.assertEqual(editor.debit.travel.tags[1].accounts[0].code,
Accounts.TRAVEL)
self.assertEqual(helper.debit.travel.tags[2].name, "Airplane")
self.assertEqual(len(helper.debit.travel.tags[2].accounts), 1)
self.assertEqual(helper.debit.travel.tags[2].accounts[0].code,
self.assertEqual(editor.debit.travel.tags[2].name, "Airplane")
self.assertEqual(len(editor.debit.travel.tags[2].accounts), 1)
self.assertEqual(editor.debit.travel.tags[2].accounts[0].code,
Accounts.TRAVEL)
# Debit-Bus
self.assertEqual(len(helper.debit.bus.tags), 2)
self.assertEqual(helper.debit.bus.tags[0].name, "Train")
self.assertEqual(len(helper.debit.bus.tags[0].accounts), 1)
self.assertEqual(helper.debit.bus.tags[0].accounts[0].code,
self.assertEqual(len(editor.debit.bus.tags), 2)
self.assertEqual(editor.debit.bus.tags[0].name, "Train")
self.assertEqual(len(editor.debit.bus.tags[0].accounts), 1)
self.assertEqual(editor.debit.bus.tags[0].accounts[0].code,
Accounts.TRAVEL)
self.assertEqual(helper.debit.bus.tags[1].name, "Bus")
self.assertEqual(len(helper.debit.bus.tags[1].accounts), 1)
self.assertEqual(helper.debit.bus.tags[1].accounts[0].code,
self.assertEqual(editor.debit.bus.tags[1].name, "Bus")
self.assertEqual(len(editor.debit.bus.tags[1].accounts), 1)
self.assertEqual(editor.debit.bus.tags[1].accounts[0].code,
Accounts.TRAVEL)
# Credit-General
self.assertEqual(len(helper.credit.general.tags), 2)
self.assertEqual(helper.credit.general.tags[0].name, "Lunch")
self.assertEqual(len(helper.credit.general.tags[0].accounts), 3)
self.assertEqual(helper.credit.general.tags[0].accounts[0].code,
self.assertEqual(len(editor.credit.general.tags), 2)
self.assertEqual(editor.credit.general.tags[0].name, "Lunch")
self.assertEqual(len(editor.credit.general.tags[0].accounts), 3)
self.assertEqual(editor.credit.general.tags[0].accounts[0].code,
Accounts.PAYABLE)
self.assertEqual(helper.credit.general.tags[0].accounts[1].code,
self.assertEqual(editor.credit.general.tags[0].accounts[1].code,
Accounts.BANK)
self.assertEqual(helper.credit.general.tags[0].accounts[2].code,
self.assertEqual(editor.credit.general.tags[0].accounts[2].code,
Accounts.CASH)
self.assertEqual(helper.credit.general.tags[1].name, "Dinner")
self.assertEqual(len(helper.credit.general.tags[1].accounts), 2)
self.assertEqual(helper.credit.general.tags[1].accounts[0].code,
self.assertEqual(editor.credit.general.tags[1].name, "Dinner")
self.assertEqual(len(editor.credit.general.tags[1].accounts), 2)
self.assertEqual(editor.credit.general.tags[1].accounts[0].code,
Accounts.BANK)
self.assertEqual(helper.credit.general.tags[1].accounts[1].code,
self.assertEqual(editor.credit.general.tags[1].accounts[1].code,
Accounts.PAYABLE)
# Credit-Travel
self.assertEqual(len(helper.credit.travel.tags), 2)
self.assertEqual(helper.credit.travel.tags[0].name, "Bike")
self.assertEqual(len(helper.credit.travel.tags[0].accounts), 2)
self.assertEqual(helper.credit.travel.tags[0].accounts[0].code,
self.assertEqual(len(editor.credit.travel.tags), 2)
self.assertEqual(editor.credit.travel.tags[0].name, "Bike")
self.assertEqual(len(editor.credit.travel.tags[0].accounts), 2)
self.assertEqual(editor.credit.travel.tags[0].accounts[0].code,
Accounts.PAYABLE)
self.assertEqual(helper.credit.travel.tags[0].accounts[1].code,
self.assertEqual(editor.credit.travel.tags[0].accounts[1].code,
Accounts.PREPAID)
self.assertEqual(helper.credit.travel.tags[1].name, "Taxi")
self.assertEqual(len(helper.credit.travel.tags[1].accounts), 2)
self.assertEqual(helper.credit.travel.tags[1].accounts[0].code,
self.assertEqual(editor.credit.travel.tags[1].name, "Taxi")
self.assertEqual(len(editor.credit.travel.tags[1].accounts), 2)
self.assertEqual(editor.credit.travel.tags[1].accounts[0].code,
Accounts.PAYABLE)
self.assertEqual(helper.credit.travel.tags[1].accounts[1].code,
self.assertEqual(editor.credit.travel.tags[1].accounts[1].code,
Accounts.CASH)
# Credit-Bus
self.assertEqual(len(helper.credit.bus.tags), 2)
self.assertEqual(helper.credit.bus.tags[0].name, "Train")
self.assertEqual(len(helper.credit.bus.tags[0].accounts), 2)
self.assertEqual(helper.credit.bus.tags[0].accounts[0].code,
self.assertEqual(len(editor.credit.bus.tags), 2)
self.assertEqual(editor.credit.bus.tags[0].name, "Train")
self.assertEqual(len(editor.credit.bus.tags[0].accounts), 2)
self.assertEqual(editor.credit.bus.tags[0].accounts[0].code,
Accounts.PREPAID)
self.assertEqual(helper.credit.bus.tags[0].accounts[1].code,
self.assertEqual(editor.credit.bus.tags[0].accounts[1].code,
Accounts.PAYABLE)
self.assertEqual(helper.credit.bus.tags[1].name, "Bus")
self.assertEqual(len(helper.credit.bus.tags[1].accounts), 1)
self.assertEqual(helper.credit.bus.tags[1].accounts[0].code,
self.assertEqual(editor.credit.bus.tags[1].name, "Bus")
self.assertEqual(len(editor.credit.bus.tags[1].accounts), 1)
self.assertEqual(editor.credit.bus.tags[1].accounts[0].code,
Accounts.PREPAID)