Compare commits

...

5 Commits

22 changed files with 117 additions and 91 deletions

View File

@ -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]

View File

@ -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

View File

@ -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)\

View File

@ -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

View File

@ -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

View File

@ -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();
} }

View File

@ -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();

View File

@ -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();
} }

View File

@ -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);
} }

View File

@ -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.
* *

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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 %}

View File

@ -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 %}

View File

@ -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]:

View File

@ -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."""

View File

@ -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.

View File

@ -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"),

View File

@ -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)

View File

@ -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

View File

@ -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.
""" """