Compare commits
	
		
			5 Commits
		
	
	
		
			ca928636fd
			...
			5571c0d01f
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 5571c0d01f | |||
| 98e1bad413 | |||
| 7ff52d99e6 | |||
| cc440a4110 | |||
| f5149a0c37 | 
@@ -29,7 +29,7 @@ from accounting.utils.user import has_user, get_user_pk
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
AccountData = tuple[int, str, int, str, str, str, bool]
 | 
					AccountData = tuple[int, str, int, str, str, str, bool]
 | 
				
			||||||
"""The format of the account data, as a list of (ID, base account code, number,
 | 
					"""The format of the account data, as a list of (ID, base account code, number,
 | 
				
			||||||
English, Traditional Chinese, Simplified Chinese, is-offset-needed) tuples."""
 | 
					English, Traditional Chinese, Simplified Chinese, is-need-offset) tuples."""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def __validate_username(ctx: click.core.Context, param: click.core.Option,
 | 
					def __validate_username(ctx: click.core.Context, param: click.core.Option,
 | 
				
			||||||
@@ -92,14 +92,14 @@ def init_accounts_command(username: str) -> None:
 | 
				
			|||||||
    data: list[AccountData] = []
 | 
					    data: list[AccountData] = []
 | 
				
			||||||
    for base in bases_to_add:
 | 
					    for base in bases_to_add:
 | 
				
			||||||
        l10n: dict[str, str] = {x.locale: x.title for x in base.l10n}
 | 
					        l10n: dict[str, str] = {x.locale: x.title for x in base.l10n}
 | 
				
			||||||
        is_offset_needed: bool = __is_offset_needed(base.code)
 | 
					        is_need_offset: bool = __is_need_offset(base.code)
 | 
				
			||||||
        data.append((get_new_id(), base.code, 1, base.title_l10n,
 | 
					        data.append((get_new_id(), base.code, 1, base.title_l10n,
 | 
				
			||||||
                     l10n["zh_Hant"], l10n["zh_Hans"], is_offset_needed))
 | 
					                     l10n["zh_Hant"], l10n["zh_Hans"], is_need_offset))
 | 
				
			||||||
    __add_accounting_accounts(data, creator_pk)
 | 
					    __add_accounting_accounts(data, creator_pk)
 | 
				
			||||||
    click.echo(F"{len(data)} added.  Accounting accounts initialized.")
 | 
					    click.echo(F"{len(data)} added.  Accounting accounts initialized.")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def __is_offset_needed(base_code: str) -> bool:
 | 
					def __is_need_offset(base_code: str) -> bool:
 | 
				
			||||||
    """Checks that whether entries in the account need offset.
 | 
					    """Checks that whether entries in the account need offset.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    :param base_code: The code of the base account.
 | 
					    :param base_code: The code of the base account.
 | 
				
			||||||
@@ -134,7 +134,7 @@ def __add_accounting_accounts(data: list[AccountData], creator_pk: int)\
 | 
				
			|||||||
                                       base_code=x[1],
 | 
					                                       base_code=x[1],
 | 
				
			||||||
                                       no=x[2],
 | 
					                                       no=x[2],
 | 
				
			||||||
                                       title_l10n=x[3],
 | 
					                                       title_l10n=x[3],
 | 
				
			||||||
                                       is_offset_needed=x[6],
 | 
					                                       is_need_offset=x[6],
 | 
				
			||||||
                                       created_by_id=creator_pk,
 | 
					                                       created_by_id=creator_pk,
 | 
				
			||||||
                                       updated_by_id=creator_pk)
 | 
					                                       updated_by_id=creator_pk)
 | 
				
			||||||
                               for x in data]
 | 
					                               for x in data]
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -80,7 +80,7 @@ class AccountForm(FlaskForm):
 | 
				
			|||||||
        filters=[strip_text],
 | 
					        filters=[strip_text],
 | 
				
			||||||
        validators=[DataRequired(lazy_gettext("Please fill in the title"))])
 | 
					        validators=[DataRequired(lazy_gettext("Please fill in the title"))])
 | 
				
			||||||
    """The title."""
 | 
					    """The title."""
 | 
				
			||||||
    is_offset_needed = BooleanField(
 | 
					    is_need_offset = BooleanField(
 | 
				
			||||||
        validators=[NoOffsetNominalAccount()])
 | 
					        validators=[NoOffsetNominalAccount()])
 | 
				
			||||||
    """Whether the the entries of this account need offset."""
 | 
					    """Whether the the entries of this account need offset."""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -103,9 +103,9 @@ class AccountForm(FlaskForm):
 | 
				
			|||||||
            obj.no = count + 1
 | 
					            obj.no = count + 1
 | 
				
			||||||
        obj.title = self.title.data
 | 
					        obj.title = self.title.data
 | 
				
			||||||
        if self.base_code.data[0] in {"1", "2", "3"}:
 | 
					        if self.base_code.data[0] in {"1", "2", "3"}:
 | 
				
			||||||
            obj.is_offset_needed = self.is_offset_needed.data
 | 
					            obj.is_need_offset = self.is_need_offset.data
 | 
				
			||||||
        else:
 | 
					        else:
 | 
				
			||||||
            obj.is_offset_needed = False
 | 
					            obj.is_need_offset = False
 | 
				
			||||||
        if is_new:
 | 
					        if is_new:
 | 
				
			||||||
            current_user_pk: int = get_current_user_pk()
 | 
					            current_user_pk: int = get_current_user_pk()
 | 
				
			||||||
            obj.created_by_id = current_user_pk
 | 
					            obj.created_by_id = current_user_pk
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -48,7 +48,7 @@ def get_account_query() -> list[Account]:
 | 
				
			|||||||
               code.contains(k),
 | 
					               code.contains(k),
 | 
				
			||||||
               Account.id.in_(l10n_matches)]
 | 
					               Account.id.in_(l10n_matches)]
 | 
				
			||||||
        if k in gettext("Need offset"):
 | 
					        if k in gettext("Need offset"):
 | 
				
			||||||
            sub_conditions.append(Account.is_offset_needed)
 | 
					            sub_conditions.append(Account.is_need_offset)
 | 
				
			||||||
        conditions.append(sa.or_(*sub_conditions))
 | 
					        conditions.append(sa.or_(*sub_conditions))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return Account.query.filter(*conditions)\
 | 
					    return Account.query.filter(*conditions)\
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -114,7 +114,7 @@ class Account(db.Model):
 | 
				
			|||||||
    """The account number under the base account."""
 | 
					    """The account number under the base account."""
 | 
				
			||||||
    title_l10n = db.Column("title", db.String, nullable=False)
 | 
					    title_l10n = db.Column("title", db.String, nullable=False)
 | 
				
			||||||
    """The title."""
 | 
					    """The title."""
 | 
				
			||||||
    is_offset_needed = db.Column(db.Boolean, nullable=False, default=False)
 | 
					    is_need_offset = db.Column(db.Boolean, nullable=False, default=False)
 | 
				
			||||||
    """Whether the entries of this account need offset."""
 | 
					    """Whether the entries of this account need offset."""
 | 
				
			||||||
    created_at = db.Column(db.DateTime(timezone=True), nullable=False,
 | 
					    created_at = db.Column(db.DateTime(timezone=True), nullable=False,
 | 
				
			||||||
                           server_default=db.func.now())
 | 
					                           server_default=db.func.now())
 | 
				
			||||||
@@ -702,7 +702,7 @@ class JournalEntry(db.Model):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        :return: True if the entry needs offset, or False otherwise.
 | 
					        :return: True if the entry needs offset, or False otherwise.
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        if not self.account.is_offset_needed:
 | 
					        if not self.account.is_need_offset:
 | 
				
			||||||
            return False
 | 
					            return False
 | 
				
			||||||
        if self.account.base_code[0] == "1" and not self.is_debit:
 | 
					        if self.account.base_code[0] == "1" and not self.is_debit:
 | 
				
			||||||
            return False
 | 
					            return False
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -97,7 +97,7 @@ class EntryCollector:
 | 
				
			|||||||
               code.contains(k),
 | 
					               code.contains(k),
 | 
				
			||||||
               Account.id.in_(select_l10n)]
 | 
					               Account.id.in_(select_l10n)]
 | 
				
			||||||
        if k in gettext("Need offset"):
 | 
					        if k in gettext("Need offset"):
 | 
				
			||||||
            conditions.append(Account.is_offset_needed)
 | 
					            conditions.append(Account.is_need_offset)
 | 
				
			||||||
        return sa.select(Account.id).filter(sa.or_(*conditions))
 | 
					        return sa.select(Account.id).filter(sa.or_(*conditions))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @staticmethod
 | 
					    @staticmethod
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -83,16 +83,16 @@ class AccountForm {
 | 
				
			|||||||
    #titleError;
 | 
					    #titleError;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * The control of the is-offset-needed option
 | 
					     * The control of the is-need-offset option
 | 
				
			||||||
     * @type {HTMLDivElement}
 | 
					     * @type {HTMLDivElement}
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    #isOffsetNeededControl;
 | 
					    #isNeedOffsetControl;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * The is-offset-needed option
 | 
					     * The is-need-offset option
 | 
				
			||||||
     * @type {HTMLInputElement}
 | 
					     * @type {HTMLInputElement}
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    #isOffsetNeeded;
 | 
					    #isNeedOffset;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Constructs the account form.
 | 
					     * Constructs the account form.
 | 
				
			||||||
@@ -107,8 +107,8 @@ class AccountForm {
 | 
				
			|||||||
        this.#baseError = document.getElementById("accounting-base-error");
 | 
					        this.#baseError = document.getElementById("accounting-base-error");
 | 
				
			||||||
        this.#title = document.getElementById("accounting-title");
 | 
					        this.#title = document.getElementById("accounting-title");
 | 
				
			||||||
        this.#titleError = document.getElementById("accounting-title-error");
 | 
					        this.#titleError = document.getElementById("accounting-title-error");
 | 
				
			||||||
        this.#isOffsetNeededControl = document.getElementById("accounting-is-offset-needed-control");
 | 
					        this.#isNeedOffsetControl = document.getElementById("accounting-is-need-offset-control");
 | 
				
			||||||
        this.#isOffsetNeeded = document.getElementById("accounting-is-offset-needed");
 | 
					        this.#isNeedOffset = document.getElementById("accounting-is-need-offset");
 | 
				
			||||||
        this.#formElement.onsubmit = () => {
 | 
					        this.#formElement.onsubmit = () => {
 | 
				
			||||||
            return this.#validateForm();
 | 
					            return this.#validateForm();
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
@@ -138,12 +138,12 @@ class AccountForm {
 | 
				
			|||||||
        this.#baseCode.value = code;
 | 
					        this.#baseCode.value = code;
 | 
				
			||||||
        this.#base.innerText = text;
 | 
					        this.#base.innerText = text;
 | 
				
			||||||
        if (["1", "2", "3"].includes(code.substring(0, 1))) {
 | 
					        if (["1", "2", "3"].includes(code.substring(0, 1))) {
 | 
				
			||||||
            this.#isOffsetNeededControl.classList.remove("d-none");
 | 
					            this.#isNeedOffsetControl.classList.remove("d-none");
 | 
				
			||||||
            this.#isOffsetNeeded.disabled = false;
 | 
					            this.#isNeedOffset.disabled = false;
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
            this.#isOffsetNeededControl.classList.add("d-none");
 | 
					            this.#isNeedOffsetControl.classList.add("d-none");
 | 
				
			||||||
            this.#isOffsetNeeded.disabled = true;
 | 
					            this.#isNeedOffset.disabled = true;
 | 
				
			||||||
            this.#isOffsetNeeded.checked = false;
 | 
					            this.#isNeedOffset.checked = false;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        this.#validateBase();
 | 
					        this.#validateBase();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -105,7 +105,7 @@ class AccountSelector {
 | 
				
			|||||||
        };
 | 
					        };
 | 
				
			||||||
        this.#clearButton.onclick = () => this.#entryEditor.clearAccount();
 | 
					        this.#clearButton.onclick = () => this.#entryEditor.clearAccount();
 | 
				
			||||||
        for (const option of this.#options) {
 | 
					        for (const option of this.#options) {
 | 
				
			||||||
            option.onclick = () => this.#entryEditor.saveAccount(option.dataset.code, option.dataset.content, option.classList.contains("accounting-account-is-offset-needed"));
 | 
					            option.onclick = () => this.#entryEditor.saveAccount(option.dataset.code, option.dataset.content, option.classList.contains("accounting-account-is-need-offset"));
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        this.#query.addEventListener("input", () => {
 | 
					        this.#query.addEventListener("input", () => {
 | 
				
			||||||
            this.#filterOptions();
 | 
					            this.#filterOptions();
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -374,10 +374,10 @@ class JournalEntryEditor {
 | 
				
			|||||||
     *
 | 
					     *
 | 
				
			||||||
     * @param code {string} the account code
 | 
					     * @param code {string} the account code
 | 
				
			||||||
     * @param text {string} the account text
 | 
					     * @param text {string} the account text
 | 
				
			||||||
     * @param isOffsetNeeded {boolean} true if the journal entries in the account need offset or false otherwise
 | 
					     * @param isNeedOffset {boolean} true if the journal entries in the account need offset or false otherwise
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    saveAccount(code, text, isOffsetNeeded) {
 | 
					    saveAccount(code, text, isNeedOffset) {
 | 
				
			||||||
        this.isNeedOffset = isOffsetNeeded;
 | 
					        this.isNeedOffset = isNeedOffset;
 | 
				
			||||||
        this.#accountControl.classList.add("accounting-not-empty");
 | 
					        this.#accountControl.classList.add("accounting-not-empty");
 | 
				
			||||||
        this.accountCode = code;
 | 
					        this.accountCode = code;
 | 
				
			||||||
        this.accountText = text;
 | 
					        this.accountText = text;
 | 
				
			||||||
@@ -519,51 +519,43 @@ class JournalEntryEditor {
 | 
				
			|||||||
     * The callback when editing a journal entry.
 | 
					     * The callback when editing a journal entry.
 | 
				
			||||||
     *
 | 
					     *
 | 
				
			||||||
     * @param entry {JournalEntrySubForm} the journal entry sub-form
 | 
					     * @param entry {JournalEntrySubForm} the journal entry sub-form
 | 
				
			||||||
     * @param originalEntryId {string} the ID of the original entry
 | 
					 | 
				
			||||||
     * @param originalEntryDate {string} the date of the original entry
 | 
					 | 
				
			||||||
     * @param originalEntryText {string} the text of the original entry
 | 
					 | 
				
			||||||
     * @param summary {string} the summary
 | 
					 | 
				
			||||||
     * @param accountCode {string} the account code
 | 
					 | 
				
			||||||
     * @param accountText {string} the account text
 | 
					 | 
				
			||||||
     * @param amount {string} the amount
 | 
					 | 
				
			||||||
     * @param amountMin {string} the minimal amount
 | 
					 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    onEdit(entry, originalEntryId, originalEntryDate, originalEntryText, summary, accountCode, accountText, amount, amountMin) {
 | 
					    onEdit(entry) {
 | 
				
			||||||
        this.entry = entry;
 | 
					        this.entry = entry;
 | 
				
			||||||
        this.#side = entry.side;
 | 
					        this.#side = entry.side;
 | 
				
			||||||
        this.entryType = this.#side.entryType;
 | 
					        this.entryType = this.#side.entryType;
 | 
				
			||||||
        this.isNeedOffset = entry.isNeedOffset();
 | 
					        this.isNeedOffset = entry.isNeedOffset();
 | 
				
			||||||
        if (originalEntryId === "") {
 | 
					        this.originalEntryId = entry.getOriginalEntryId();
 | 
				
			||||||
 | 
					        this.originalEntryDate = entry.getOriginalEntryDate();
 | 
				
			||||||
 | 
					        this.originalEntryText = entry.getOriginalEntryText();
 | 
				
			||||||
 | 
					        this.#originalEntry.innerText = this.originalEntryText;
 | 
				
			||||||
 | 
					        if (this.originalEntryId === null) {
 | 
				
			||||||
            this.#originalEntryContainer.classList.add("d-none");
 | 
					            this.#originalEntryContainer.classList.add("d-none");
 | 
				
			||||||
            this.#originalEntryControl.classList.remove("accounting-not-empty");
 | 
					            this.#originalEntryControl.classList.remove("accounting-not-empty");
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
            this.#originalEntryContainer.classList.remove("d-none");
 | 
					            this.#originalEntryContainer.classList.remove("d-none");
 | 
				
			||||||
            this.#originalEntryControl.classList.add("accounting-not-empty");
 | 
					            this.#originalEntryControl.classList.add("accounting-not-empty");
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        this.originalEntryId = originalEntryId === ""? null: originalEntryId;
 | 
					        this.#setEnableSummaryAccount(!entry.isMatched && this.originalEntryId === null);
 | 
				
			||||||
        this.originalEntryDate = originalEntryDate === ""? null: originalEntryDate;
 | 
					        this.summary = entry.getSummary();
 | 
				
			||||||
        this.originalEntryText = originalEntryText === ""? null: originalEntryText;
 | 
					        if (this.summary === null) {
 | 
				
			||||||
        this.#originalEntry.innerText = originalEntryText;
 | 
					 | 
				
			||||||
        this.#setEnableSummaryAccount(!entry.isMatched && originalEntryId === "");
 | 
					 | 
				
			||||||
        if (summary === "") {
 | 
					 | 
				
			||||||
            this.#summaryControl.classList.remove("accounting-not-empty");
 | 
					            this.#summaryControl.classList.remove("accounting-not-empty");
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
            this.#summaryControl.classList.add("accounting-not-empty");
 | 
					            this.#summaryControl.classList.add("accounting-not-empty");
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        this.summary = summary === ""? null: summary;
 | 
					        this.#summary.innerText = this.summary === null? "": this.summary;
 | 
				
			||||||
        this.#summary.innerText = summary;
 | 
					        if (entry.getAccountCode() === null) {
 | 
				
			||||||
        if (accountCode === "") {
 | 
					 | 
				
			||||||
            this.#accountControl.classList.remove("accounting-not-empty");
 | 
					            this.#accountControl.classList.remove("accounting-not-empty");
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
            this.#accountControl.classList.add("accounting-not-empty");
 | 
					            this.#accountControl.classList.add("accounting-not-empty");
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        this.accountCode = accountCode;
 | 
					        this.accountCode = entry.getAccountCode();
 | 
				
			||||||
        this.accountText = accountText;
 | 
					        this.accountText = entry.getAccountText();
 | 
				
			||||||
        this.#account.innerText = accountText;
 | 
					        this.#account.innerText = this.accountText;
 | 
				
			||||||
        this.#amount.value = amount;
 | 
					        this.#amount.value = entry.getAmount() === null? "": String(entry.getAmount());
 | 
				
			||||||
        const maxAmount = this.#getMaxAmount();
 | 
					        const maxAmount = this.#getMaxAmount();
 | 
				
			||||||
        this.#amount.max = maxAmount === null? "": maxAmount;
 | 
					        this.#amount.max = maxAmount === null? "": maxAmount;
 | 
				
			||||||
        this.#amount.min = amountMin;
 | 
					        this.#amount.min = entry.getAmountMin() === null? "": String(entry.getAmountMin());
 | 
				
			||||||
        this.#validate();
 | 
					        this.#validate();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -215,7 +215,7 @@ class SummaryEditor {
 | 
				
			|||||||
    #submit() {
 | 
					    #submit() {
 | 
				
			||||||
        bootstrap.Modal.getOrCreateInstance(this.#modal).hide();
 | 
					        bootstrap.Modal.getOrCreateInstance(this.#modal).hide();
 | 
				
			||||||
        if (this.#selectedAccount !== null) {
 | 
					        if (this.#selectedAccount !== null) {
 | 
				
			||||||
            this.#entryEditor.saveSummaryWithAccount(this.summary.value, this.#selectedAccount.dataset.code, this.#selectedAccount.dataset.text, this.#selectedAccount.classList.contains("accounting-account-is-offset-needed"));
 | 
					            this.#entryEditor.saveSummaryWithAccount(this.summary.value, this.#selectedAccount.dataset.code, this.#selectedAccount.dataset.text, this.#selectedAccount.classList.contains("accounting-account-is-need-offset"));
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
            this.#entryEditor.saveSummary(this.summary.value);
 | 
					            this.#entryEditor.saveSummary(this.summary.value);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -881,7 +881,7 @@ class JournalEntrySubForm {
 | 
				
			|||||||
        this.#amount = document.getElementById(this.#prefix + "-amount");
 | 
					        this.#amount = document.getElementById(this.#prefix + "-amount");
 | 
				
			||||||
        this.#amountText = document.getElementById(this.#prefix + "-amount-text");
 | 
					        this.#amountText = document.getElementById(this.#prefix + "-amount-text");
 | 
				
			||||||
        this.deleteButton = document.getElementById(this.#prefix + "-delete");
 | 
					        this.deleteButton = document.getElementById(this.#prefix + "-delete");
 | 
				
			||||||
        this.#control.onclick = () => this.side.currency.form.entryEditor.onEdit(this, this.#originalEntryId.value, this.#originalEntryId.dataset.date, this.#originalEntryId.dataset.text, this.#summary.value, this.#accountCode.value, this.#accountCode.dataset.text, this.#amount.value, this.#amount.dataset.min);
 | 
					        this.#control.onclick = () => this.side.currency.form.entryEditor.onEdit(this);
 | 
				
			||||||
        this.deleteButton.onclick = () => {
 | 
					        this.deleteButton.onclick = () => {
 | 
				
			||||||
            this.element.parentElement.removeChild(this.element);
 | 
					            this.element.parentElement.removeChild(this.element);
 | 
				
			||||||
            this.side.deleteJournalEntry(this);
 | 
					            this.side.deleteJournalEntry(this);
 | 
				
			||||||
@@ -915,6 +915,24 @@ class JournalEntrySubForm {
 | 
				
			|||||||
        return this.#originalEntryId.dataset.date === ""? null: this.#originalEntryId.dataset.date;
 | 
					        return this.#originalEntryId.dataset.date === ""? null: this.#originalEntryId.dataset.date;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Returns the text of the original entry.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return {string|null} the text of the original entry
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    getOriginalEntryText() {
 | 
				
			||||||
 | 
					        return this.#originalEntryId.dataset.text === ""? null: this.#originalEntryId.dataset.text;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Returns the summary.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return {string|null} the summary
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    getSummary() {
 | 
				
			||||||
 | 
					        return this.#summary.value === ""? null: this.#summary.value;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Returns the account code.
 | 
					     * Returns the account code.
 | 
				
			||||||
     *
 | 
					     *
 | 
				
			||||||
@@ -924,6 +942,15 @@ class JournalEntrySubForm {
 | 
				
			|||||||
        return this.#accountCode.value === ""? null: this.#accountCode.value;
 | 
					        return this.#accountCode.value === ""? null: this.#accountCode.value;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Returns the account text.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return {string|null} the account text
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    getAccountText() {
 | 
				
			||||||
 | 
					        return this.#accountCode.dataset.text === ""? null: this.#accountCode.dataset.text;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Returns the amount.
 | 
					     * Returns the amount.
 | 
				
			||||||
     *
 | 
					     *
 | 
				
			||||||
@@ -933,6 +960,15 @@ class JournalEntrySubForm {
 | 
				
			|||||||
        return this.#amount.value === ""? null: new Decimal(this.#amount.value);
 | 
					        return this.#amount.value === ""? null: new Decimal(this.#amount.value);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Returns the minimal amount.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return {Decimal|null} the minimal amount
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    getAmountMin() {
 | 
				
			||||||
 | 
					        return this.#amount.dataset.min === ""? null: new Decimal(this.#amount.dataset.min);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Validates the form.
 | 
					     * Validates the form.
 | 
				
			||||||
     *
 | 
					     *
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -85,7 +85,7 @@ First written: 2023/1/31
 | 
				
			|||||||
<div class="accounting-card col-sm-6">
 | 
					<div class="accounting-card col-sm-6">
 | 
				
			||||||
  <div class="accounting-card-title">{{ obj.title }}</div>
 | 
					  <div class="accounting-card-title">{{ obj.title }}</div>
 | 
				
			||||||
  <div class="accounting-card-code">{{ obj.code }}</div>
 | 
					  <div class="accounting-card-code">{{ obj.code }}</div>
 | 
				
			||||||
  {% if obj.is_offset_needed %}
 | 
					  {% if obj.is_need_offset %}
 | 
				
			||||||
    <div>
 | 
					    <div>
 | 
				
			||||||
      <span class="badge rounded-pill bg-info">{{ A_("Need offset") }}</span>
 | 
					      <span class="badge rounded-pill bg-info">{{ A_("Need offset") }}</span>
 | 
				
			||||||
    </div>
 | 
					    </div>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -62,9 +62,9 @@ First written: 2023/2/1
 | 
				
			|||||||
    <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">{% if form.title.errors %}{{ form.title.errors[0] }}{% endif %}</div>
 | 
				
			||||||
  </div>
 | 
					  </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  <div id="accounting-is-offset-needed-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 %}">
 | 
				
			||||||
    <input id="accounting-is-offset-needed" class="form-check-input" type="checkbox" name="is_offset_needed" value="1" {% if form.is_offset_needed.data %} checked="checked" {% endif %}>
 | 
					    <input id="accounting-is-need-offset" class="form-check-input" type="checkbox" name="is_need_offset" value="1" {% if form.is_need_offset.data %} checked="checked" {% endif %}>
 | 
				
			||||||
    <label class="form-check-label" for="accounting-is-offset-needed">
 | 
					    <label class="form-check-label" for="accounting-is-need-offset">
 | 
				
			||||||
      {{ A_("The entries in the account need offset.") }}
 | 
					      {{ A_("The entries in the account need offset.") }}
 | 
				
			||||||
    </label>
 | 
					    </label>
 | 
				
			||||||
  </div>
 | 
					  </div>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -58,7 +58,7 @@ First written: 2023/1/30
 | 
				
			|||||||
  {% for item in list %}
 | 
					  {% for item in list %}
 | 
				
			||||||
    <a class="list-group-item list-group-item-action" href="{{ url_for("accounting.account.detail", account=item)|accounting_append_next }}">
 | 
					    <a class="list-group-item list-group-item-action" href="{{ url_for("accounting.account.detail", account=item)|accounting_append_next }}">
 | 
				
			||||||
      {{ item }}
 | 
					      {{ item }}
 | 
				
			||||||
      {% if item.is_offset_needed %}
 | 
					      {% if item.is_need_offset %}
 | 
				
			||||||
        <span class="badge rounded-pill bg-info">{{ A_("Need offset") }}</span>
 | 
					        <span class="badge rounded-pill bg-info">{{ A_("Need offset") }}</span>
 | 
				
			||||||
      {% endif %}
 | 
					      {% endif %}
 | 
				
			||||||
    </a>
 | 
					    </a>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -37,7 +37,7 @@ First written: 2023/2/25
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        <ul id="accounting-account-selector-{{ entry_type }}-option-list" class="list-group accounting-selector-list">
 | 
					        <ul id="accounting-account-selector-{{ entry_type }}-option-list" class="list-group accounting-selector-list">
 | 
				
			||||||
          {% for account in account_options %}
 | 
					          {% for account in account_options %}
 | 
				
			||||||
            <li id="accounting-account-selector-{{ entry_type }}-option-{{ account.code }}" class="list-group-item accounting-clickable accounting-account-selector-{{ entry_type }}-option {% if account.is_in_use %} accounting-account-in-use {% endif %} {% if account.is_offset_needed %} accounting-account-is-offset-needed {% endif %}" data-code="{{ account.code }}" data-content="{{ account }}" data-query-values="{{ account.query_values|tojson|forceescape }}" data-bs-toggle="modal" data-bs-target="#accounting-entry-editor-modal">
 | 
					            <li id="accounting-account-selector-{{ entry_type }}-option-{{ account.code }}" class="list-group-item accounting-clickable accounting-account-selector-{{ entry_type }}-option {% if account.is_in_use %} accounting-account-in-use {% endif %} {% if account.is_need_offset %} accounting-account-is-need-offset {% endif %}" data-code="{{ account.code }}" data-content="{{ account }}" data-query-values="{{ account.query_values|tojson|forceescape }}" data-bs-toggle="modal" data-bs-target="#accounting-entry-editor-modal">
 | 
				
			||||||
              {{ account }}
 | 
					              {{ account }}
 | 
				
			||||||
            </li>
 | 
					            </li>
 | 
				
			||||||
          {% endfor %}
 | 
					          {% endfor %}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -177,7 +177,7 @@ First written: 2023/2/28
 | 
				
			|||||||
          {# The suggested accounts #}
 | 
					          {# The suggested accounts #}
 | 
				
			||||||
          <div class="mt-3">
 | 
					          <div class="mt-3">
 | 
				
			||||||
            {% for account in summary_editor.accounts %}
 | 
					            {% for account in summary_editor.accounts %}
 | 
				
			||||||
              <button class="btn btn-outline-primary d-none accounting-summary-editor-{{ summary_editor.type }}-account {% if account.is_offset_needed %} accounting-account-is-offset-needed {% endif %}" type="button" data-code="{{ account.code }}" data-text="{{ account }}">
 | 
					              <button class="btn btn-outline-primary d-none accounting-summary-editor-{{ summary_editor.type }}-account {% if account.is_need_offset %} accounting-account-is-need-offset {% endif %}" type="button" data-code="{{ account.code }}" data-text="{{ account }}">
 | 
				
			||||||
                {{ account }}
 | 
					                {{ account }}
 | 
				
			||||||
              </button>
 | 
					              </button>
 | 
				
			||||||
            {% endfor %}
 | 
					            {% endfor %}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -81,7 +81,7 @@ class OriginalEntryNeedOffset:
 | 
				
			|||||||
            = db.session.get(JournalEntry, field.data)
 | 
					            = db.session.get(JournalEntry, field.data)
 | 
				
			||||||
        if original_entry is None:
 | 
					        if original_entry is None:
 | 
				
			||||||
            return
 | 
					            return
 | 
				
			||||||
        if not original_entry.account.is_offset_needed:
 | 
					        if not original_entry.account.is_need_offset:
 | 
				
			||||||
            raise ValidationError(lazy_gettext(
 | 
					            raise ValidationError(lazy_gettext(
 | 
				
			||||||
                "The original entry does not need offset."))
 | 
					                "The original entry does not need offset."))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -186,7 +186,7 @@ class NotStartPayableFromDebit:
 | 
				
			|||||||
                or form.original_entry_id.data is not None:
 | 
					                or form.original_entry_id.data is not None:
 | 
				
			||||||
            return
 | 
					            return
 | 
				
			||||||
        account: Account | None = Account.find_by_code(field.data)
 | 
					        account: Account | None = Account.find_by_code(field.data)
 | 
				
			||||||
        if account is not None and account.is_offset_needed:
 | 
					        if account is not None and account.is_need_offset:
 | 
				
			||||||
            raise ValidationError(lazy_gettext(
 | 
					            raise ValidationError(lazy_gettext(
 | 
				
			||||||
                "A payable entry cannot start from the debit side."))
 | 
					                "A payable entry cannot start from the debit side."))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -202,7 +202,7 @@ class NotStartReceivableFromCredit:
 | 
				
			|||||||
                or form.original_entry_id.data is not None:
 | 
					                or form.original_entry_id.data is not None:
 | 
				
			||||||
            return
 | 
					            return
 | 
				
			||||||
        account: Account | None = Account.find_by_code(field.data)
 | 
					        account: Account | None = Account.find_by_code(field.data)
 | 
				
			||||||
        if account is not None and account.is_offset_needed:
 | 
					        if account is not None and account.is_need_offset:
 | 
				
			||||||
            raise ValidationError(lazy_gettext(
 | 
					            raise ValidationError(lazy_gettext(
 | 
				
			||||||
                "A receivable entry cannot start from the credit side."))
 | 
					                "A receivable entry cannot start from the credit side."))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -361,7 +361,7 @@ class JournalEntryForm(FlaskForm):
 | 
				
			|||||||
        else:
 | 
					        else:
 | 
				
			||||||
            return False
 | 
					            return False
 | 
				
			||||||
        account: Account | None = Account.find_by_code(self.account_code.data)
 | 
					        account: Account | None = Account.find_by_code(self.account_code.data)
 | 
				
			||||||
        return account is not None and account.is_offset_needed
 | 
					        return account is not None and account.is_need_offset
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @property
 | 
					    @property
 | 
				
			||||||
    def offsets(self) -> list[JournalEntry]:
 | 
					    def offsets(self) -> list[JournalEntry]:
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -129,9 +129,9 @@ class TransactionForm(FlaskForm):
 | 
				
			|||||||
        provide their own collectors."""
 | 
					        provide their own collectors."""
 | 
				
			||||||
        self.obj: Transaction | None = kwargs.get("obj")
 | 
					        self.obj: Transaction | None = kwargs.get("obj")
 | 
				
			||||||
        """The transaction, when editing an existing one."""
 | 
					        """The transaction, when editing an existing one."""
 | 
				
			||||||
        self._is_payable_needed: bool = False
 | 
					        self._is_need_payable: bool = False
 | 
				
			||||||
        """Whether we need the payable original entries."""
 | 
					        """Whether we need the payable original entries."""
 | 
				
			||||||
        self._is_receivable_needed: bool = False
 | 
					        self._is_need_receivable: bool = False
 | 
				
			||||||
        """Whether we need the receivable original entries."""
 | 
					        """Whether we need the receivable original entries."""
 | 
				
			||||||
        self.__original_entry_options: list[JournalEntry] | None = None
 | 
					        self.__original_entry_options: list[JournalEntry] | None = None
 | 
				
			||||||
        """The options of the original entries."""
 | 
					        """The options of the original entries."""
 | 
				
			||||||
@@ -219,7 +219,7 @@ class TransactionForm(FlaskForm):
 | 
				
			|||||||
        """
 | 
					        """
 | 
				
			||||||
        accounts: list[AccountOption] \
 | 
					        accounts: list[AccountOption] \
 | 
				
			||||||
            = [AccountOption(x) for x in Account.debit()
 | 
					            = [AccountOption(x) for x in Account.debit()
 | 
				
			||||||
               if not (x.code[0] == "2" and x.is_offset_needed)]
 | 
					               if not (x.code[0] == "2" and x.is_need_offset)]
 | 
				
			||||||
        in_use: set[int] = set(db.session.scalars(
 | 
					        in_use: set[int] = set(db.session.scalars(
 | 
				
			||||||
            sa.select(JournalEntry.account_id)
 | 
					            sa.select(JournalEntry.account_id)
 | 
				
			||||||
            .filter(JournalEntry.is_debit)
 | 
					            .filter(JournalEntry.is_debit)
 | 
				
			||||||
@@ -236,7 +236,7 @@ class TransactionForm(FlaskForm):
 | 
				
			|||||||
        """
 | 
					        """
 | 
				
			||||||
        accounts: list[AccountOption] \
 | 
					        accounts: list[AccountOption] \
 | 
				
			||||||
            = [AccountOption(x) for x in Account.credit()
 | 
					            = [AccountOption(x) for x in Account.credit()
 | 
				
			||||||
               if not (x.code[0] == "1" and x.is_offset_needed)]
 | 
					               if not (x.code[0] == "1" and x.is_need_offset)]
 | 
				
			||||||
        in_use: set[int] = set(db.session.scalars(
 | 
					        in_use: set[int] = set(db.session.scalars(
 | 
				
			||||||
            sa.select(JournalEntry.account_id)
 | 
					            sa.select(JournalEntry.account_id)
 | 
				
			||||||
            .filter(sa.not_(JournalEntry.is_debit))
 | 
					            .filter(sa.not_(JournalEntry.is_debit))
 | 
				
			||||||
@@ -271,7 +271,7 @@ class TransactionForm(FlaskForm):
 | 
				
			|||||||
        if self.__original_entry_options is None:
 | 
					        if self.__original_entry_options is None:
 | 
				
			||||||
            self.__original_entry_options = get_selectable_original_entries(
 | 
					            self.__original_entry_options = get_selectable_original_entries(
 | 
				
			||||||
                {x.eid.data for x in self.entries if x.eid.data is not None},
 | 
					                {x.eid.data for x in self.entries if x.eid.data is not None},
 | 
				
			||||||
                self._is_payable_needed, self._is_receivable_needed)
 | 
					                self._is_need_payable, self._is_need_receivable)
 | 
				
			||||||
        return self.__original_entry_options
 | 
					        return self.__original_entry_options
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @property
 | 
					    @property
 | 
				
			||||||
@@ -459,7 +459,7 @@ class IncomeTransactionForm(TransactionForm):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    def __init__(self, *args, **kwargs):
 | 
					    def __init__(self, *args, **kwargs):
 | 
				
			||||||
        super().__init__(*args, **kwargs)
 | 
					        super().__init__(*args, **kwargs)
 | 
				
			||||||
        self._is_receivable_needed = True
 | 
					        self._is_need_receivable = True
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        class Collector(JournalEntryCollector[IncomeTransactionForm]):
 | 
					        class Collector(JournalEntryCollector[IncomeTransactionForm]):
 | 
				
			||||||
            """The journal entry collector for the cash income transactions."""
 | 
					            """The journal entry collector for the cash income transactions."""
 | 
				
			||||||
@@ -504,7 +504,7 @@ class ExpenseTransactionForm(TransactionForm):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    def __init__(self, *args, **kwargs):
 | 
					    def __init__(self, *args, **kwargs):
 | 
				
			||||||
        super().__init__(*args, **kwargs)
 | 
					        super().__init__(*args, **kwargs)
 | 
				
			||||||
        self._is_payable_needed = True
 | 
					        self._is_need_payable = True
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        class Collector(JournalEntryCollector[ExpenseTransactionForm]):
 | 
					        class Collector(JournalEntryCollector[ExpenseTransactionForm]):
 | 
				
			||||||
            """The journal entry collector for the cash expense
 | 
					            """The journal entry collector for the cash expense
 | 
				
			||||||
@@ -550,8 +550,8 @@ class TransferTransactionForm(TransactionForm):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    def __init__(self, *args, **kwargs):
 | 
					    def __init__(self, *args, **kwargs):
 | 
				
			||||||
        super().__init__(*args, **kwargs)
 | 
					        super().__init__(*args, **kwargs)
 | 
				
			||||||
        self._is_payable_needed = True
 | 
					        self._is_need_payable = True
 | 
				
			||||||
        self._is_receivable_needed = True
 | 
					        self._is_need_receivable = True
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        class Collector(JournalEntryCollector[TransferTransactionForm]):
 | 
					        class Collector(JournalEntryCollector[TransferTransactionForm]):
 | 
				
			||||||
            """The journal entry collector for the transfer transactions."""
 | 
					            """The journal entry collector for the transfer transactions."""
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -38,10 +38,8 @@ class AccountOption:
 | 
				
			|||||||
        """The string representation of the account option."""
 | 
					        """The string representation of the account option."""
 | 
				
			||||||
        self.is_in_use: bool = False
 | 
					        self.is_in_use: bool = False
 | 
				
			||||||
        """True if this account is in use, or False otherwise."""
 | 
					        """True if this account is in use, or False otherwise."""
 | 
				
			||||||
        self.is_offset_needed: bool = account.is_offset_needed
 | 
					        self.is_need_offset: bool = account.is_need_offset
 | 
				
			||||||
        """True if this account needs offset, or False otherwise."""
 | 
					        """True if this account needs offset, or False otherwise."""
 | 
				
			||||||
        self.is_offset_chooser_needed: bool = False
 | 
					 | 
				
			||||||
        """True if this account needs an offset chooser, or False otherwise."""
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def __str__(self) -> str:
 | 
					    def __str__(self) -> str:
 | 
				
			||||||
        """Returns the string representation of the account option.
 | 
					        """Returns the string representation of the account option.
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -50,7 +50,7 @@ def get_selectable_original_entries(
 | 
				
			|||||||
        (offset.c.id.in_(entry_id_on_form), 0),
 | 
					        (offset.c.id.in_(entry_id_on_form), 0),
 | 
				
			||||||
        (be(offset.c.is_debit == JournalEntry.is_debit), offset.c.amount),
 | 
					        (be(offset.c.is_debit == JournalEntry.is_debit), offset.c.amount),
 | 
				
			||||||
        else_=-offset.c.amount))).label("net_balance")
 | 
					        else_=-offset.c.amount))).label("net_balance")
 | 
				
			||||||
    conditions: list[sa.BinaryExpression] = [Account.is_offset_needed]
 | 
					    conditions: list[sa.BinaryExpression] = [Account.is_need_offset]
 | 
				
			||||||
    sub_conditions: list[sa.BinaryExpression] = []
 | 
					    sub_conditions: list[sa.BinaryExpression] = []
 | 
				
			||||||
    if is_payable:
 | 
					    if is_payable:
 | 
				
			||||||
        sub_conditions.append(sa.and_(Account.base_code.startswith("2"),
 | 
					        sub_conditions.append(sa.and_(Account.base_code.startswith("2"),
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -377,7 +377,7 @@ class AccountTestCase(unittest.TestCase):
 | 
				
			|||||||
                                    data={"csrf_token": self.csrf_token,
 | 
					                                    data={"csrf_token": self.csrf_token,
 | 
				
			||||||
                                          "base_code": "6172",
 | 
					                                          "base_code": "6172",
 | 
				
			||||||
                                          "title": stock.title,
 | 
					                                          "title": stock.title,
 | 
				
			||||||
                                          "is_offset_needed": "yes"})
 | 
					                                          "is_need_offset": "yes"})
 | 
				
			||||||
        self.assertEqual(response.status_code, 302)
 | 
					        self.assertEqual(response.status_code, 302)
 | 
				
			||||||
        self.assertEqual(response.headers["Location"], create_uri)
 | 
					        self.assertEqual(response.headers["Location"], create_uri)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -484,7 +484,7 @@ class AccountTestCase(unittest.TestCase):
 | 
				
			|||||||
                                    data={"csrf_token": self.csrf_token,
 | 
					                                    data={"csrf_token": self.csrf_token,
 | 
				
			||||||
                                          "base_code": "6172",
 | 
					                                          "base_code": "6172",
 | 
				
			||||||
                                          "title": stock.title,
 | 
					                                          "title": stock.title,
 | 
				
			||||||
                                          "is_offset_needed": "yes"})
 | 
					                                          "is_need_offset": "yes"})
 | 
				
			||||||
        self.assertEqual(response.status_code, 302)
 | 
					        self.assertEqual(response.status_code, 302)
 | 
				
			||||||
        self.assertEqual(response.headers["Location"], edit_uri)
 | 
					        self.assertEqual(response.headers["Location"], edit_uri)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -113,7 +113,7 @@ class OffsetTestCase(unittest.TestCase):
 | 
				
			|||||||
        # The original entry does not need offset
 | 
					        # The original entry does not need offset
 | 
				
			||||||
        with self.app.app_context():
 | 
					        with self.app.app_context():
 | 
				
			||||||
            account = Account.find_by_code(Accounts.RECEIVABLE)
 | 
					            account = Account.find_by_code(Accounts.RECEIVABLE)
 | 
				
			||||||
            account.is_offset_needed = False
 | 
					            account.is_need_offset = False
 | 
				
			||||||
            db.session.commit()
 | 
					            db.session.commit()
 | 
				
			||||||
        response = self.client.post(store_uri,
 | 
					        response = self.client.post(store_uri,
 | 
				
			||||||
                                    data=txn_data.new_form(self.csrf_token))
 | 
					                                    data=txn_data.new_form(self.csrf_token))
 | 
				
			||||||
@@ -121,7 +121,7 @@ class OffsetTestCase(unittest.TestCase):
 | 
				
			|||||||
        self.assertEqual(response.headers["Location"], create_uri)
 | 
					        self.assertEqual(response.headers["Location"], create_uri)
 | 
				
			||||||
        with self.app.app_context():
 | 
					        with self.app.app_context():
 | 
				
			||||||
            account = Account.find_by_code(Accounts.RECEIVABLE)
 | 
					            account = Account.find_by_code(Accounts.RECEIVABLE)
 | 
				
			||||||
            account.is_offset_needed = True
 | 
					            account.is_need_offset = True
 | 
				
			||||||
            db.session.commit()
 | 
					            db.session.commit()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # The original entry is also an offset
 | 
					        # The original entry is also an offset
 | 
				
			||||||
@@ -219,7 +219,7 @@ class OffsetTestCase(unittest.TestCase):
 | 
				
			|||||||
        # The original entry does not need offset
 | 
					        # The original entry does not need offset
 | 
				
			||||||
        with self.app.app_context():
 | 
					        with self.app.app_context():
 | 
				
			||||||
            account = Account.find_by_code(Accounts.RECEIVABLE)
 | 
					            account = Account.find_by_code(Accounts.RECEIVABLE)
 | 
				
			||||||
            account.is_offset_needed = False
 | 
					            account.is_need_offset = False
 | 
				
			||||||
            db.session.commit()
 | 
					            db.session.commit()
 | 
				
			||||||
        response = self.client.post(update_uri,
 | 
					        response = self.client.post(update_uri,
 | 
				
			||||||
                                    data=txn_data.update_form(self.csrf_token))
 | 
					                                    data=txn_data.update_form(self.csrf_token))
 | 
				
			||||||
@@ -227,7 +227,7 @@ class OffsetTestCase(unittest.TestCase):
 | 
				
			|||||||
        self.assertEqual(response.headers["Location"], edit_uri)
 | 
					        self.assertEqual(response.headers["Location"], edit_uri)
 | 
				
			||||||
        with self.app.app_context():
 | 
					        with self.app.app_context():
 | 
				
			||||||
            account = Account.find_by_code(Accounts.RECEIVABLE)
 | 
					            account = Account.find_by_code(Accounts.RECEIVABLE)
 | 
				
			||||||
            account.is_offset_needed = True
 | 
					            account.is_need_offset = True
 | 
				
			||||||
            db.session.commit()
 | 
					            db.session.commit()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # The original entry is also an offset
 | 
					        # The original entry is also an offset
 | 
				
			||||||
@@ -419,7 +419,7 @@ class OffsetTestCase(unittest.TestCase):
 | 
				
			|||||||
        # The original entry does not need offset
 | 
					        # The original entry does not need offset
 | 
				
			||||||
        with self.app.app_context():
 | 
					        with self.app.app_context():
 | 
				
			||||||
            account = Account.find_by_code(Accounts.PAYABLE)
 | 
					            account = Account.find_by_code(Accounts.PAYABLE)
 | 
				
			||||||
            account.is_offset_needed = False
 | 
					            account.is_need_offset = False
 | 
				
			||||||
            db.session.commit()
 | 
					            db.session.commit()
 | 
				
			||||||
        response = self.client.post(store_uri,
 | 
					        response = self.client.post(store_uri,
 | 
				
			||||||
                                    data=txn_data.new_form(self.csrf_token))
 | 
					                                    data=txn_data.new_form(self.csrf_token))
 | 
				
			||||||
@@ -427,7 +427,7 @@ class OffsetTestCase(unittest.TestCase):
 | 
				
			|||||||
        self.assertEqual(response.headers["Location"], create_uri)
 | 
					        self.assertEqual(response.headers["Location"], create_uri)
 | 
				
			||||||
        with self.app.app_context():
 | 
					        with self.app.app_context():
 | 
				
			||||||
            account = Account.find_by_code(Accounts.PAYABLE)
 | 
					            account = Account.find_by_code(Accounts.PAYABLE)
 | 
				
			||||||
            account.is_offset_needed = True
 | 
					            account.is_need_offset = True
 | 
				
			||||||
            db.session.commit()
 | 
					            db.session.commit()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # The original entry is also an offset
 | 
					        # The original entry is also an offset
 | 
				
			||||||
@@ -525,7 +525,7 @@ class OffsetTestCase(unittest.TestCase):
 | 
				
			|||||||
        # The original entry does not need offset
 | 
					        # The original entry does not need offset
 | 
				
			||||||
        with self.app.app_context():
 | 
					        with self.app.app_context():
 | 
				
			||||||
            account = Account.find_by_code(Accounts.PAYABLE)
 | 
					            account = Account.find_by_code(Accounts.PAYABLE)
 | 
				
			||||||
            account.is_offset_needed = False
 | 
					            account.is_need_offset = False
 | 
				
			||||||
            db.session.commit()
 | 
					            db.session.commit()
 | 
				
			||||||
        response = self.client.post(update_uri,
 | 
					        response = self.client.post(update_uri,
 | 
				
			||||||
                                    data=txn_data.update_form(self.csrf_token))
 | 
					                                    data=txn_data.update_form(self.csrf_token))
 | 
				
			||||||
@@ -533,7 +533,7 @@ class OffsetTestCase(unittest.TestCase):
 | 
				
			|||||||
        self.assertEqual(response.headers["Location"], edit_uri)
 | 
					        self.assertEqual(response.headers["Location"], edit_uri)
 | 
				
			||||||
        with self.app.app_context():
 | 
					        with self.app.app_context():
 | 
				
			||||||
            account = Account.find_by_code(Accounts.PAYABLE)
 | 
					            account = Account.find_by_code(Accounts.PAYABLE)
 | 
				
			||||||
            account.is_offset_needed = True
 | 
					            account.is_need_offset = True
 | 
				
			||||||
            db.session.commit()
 | 
					            db.session.commit()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # The original entry is also an offset
 | 
					        # The original entry is also an offset
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -152,7 +152,7 @@ class PaginationTestCase(unittest.TestCase):
 | 
				
			|||||||
            :param items: All the items in the list.
 | 
					            :param items: All the items in the list.
 | 
				
			||||||
            :param is_reversed: Whether the default page is the last page.
 | 
					            :param is_reversed: Whether the default page is the last page.
 | 
				
			||||||
            :param result: The expected items on the page.
 | 
					            :param result: The expected items on the page.
 | 
				
			||||||
            :param is_paged: Whether the pagination is needed.
 | 
					            :param is_paged: Whether we need pagination.
 | 
				
			||||||
            """
 | 
					            """
 | 
				
			||||||
            self.items: list[int] = items
 | 
					            self.items: list[int] = items
 | 
				
			||||||
            self.is_reversed: bool | None = is_reversed
 | 
					            self.is_reversed: bool | None = is_reversed
 | 
				
			||||||
@@ -192,7 +192,7 @@ class PaginationTestCase(unittest.TestCase):
 | 
				
			|||||||
        :param query: The query string.
 | 
					        :param query: The query string.
 | 
				
			||||||
        :param items: The original items.
 | 
					        :param items: The original items.
 | 
				
			||||||
        :param result: The expected page content.
 | 
					        :param result: The expected page content.
 | 
				
			||||||
        :param is_paged: Whether the pagination is needed.
 | 
					        :param is_paged: Whether we need pagination.
 | 
				
			||||||
        :param is_reversed: Whether the list is reversed.
 | 
					        :param is_reversed: Whether the list is reversed.
 | 
				
			||||||
        :return: None.
 | 
					        :return: None.
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
@@ -247,8 +247,8 @@ class PaginationTestCase(unittest.TestCase):
 | 
				
			|||||||
        self.__test_success("page-no=46&page-size=15", range(1, 687),
 | 
					        self.__test_success("page-no=46&page-size=15", range(1, 687),
 | 
				
			||||||
                            range(676, 687))
 | 
					                            range(676, 687))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_not_needed(self) -> None:
 | 
					    def test_not_need(self) -> None:
 | 
				
			||||||
        """Tests the pagination that is not needed.
 | 
					        """Tests that the data does not need pagination.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        :return: None.
 | 
					        :return: None.
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user