Replaced importing the "typing" module as "t" with importing the individual names in the "typing" module. Since Python 3.9 introduced type hinting generics in standard collections, we do not have as many names to import now. This is also to be consistent with the practices of most major and standard packages and examples.
This commit is contained in:
parent
ee5b447c23
commit
cda9e4e3c6
@ -17,15 +17,15 @@
|
|||||||
"""The console commands for the account management.
|
"""The console commands for the account management.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
import typing as t
|
|
||||||
from secrets import randbelow
|
from secrets import randbelow
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
import click
|
import click
|
||||||
|
import sqlalchemy as sa
|
||||||
|
|
||||||
from accounting import db
|
from accounting import db
|
||||||
from accounting.models import BaseAccount, Account, AccountL10n
|
from accounting.models import BaseAccount, Account, AccountL10n
|
||||||
from accounting.utils.user import get_user_pk
|
from accounting.utils.user import get_user_pk
|
||||||
import sqlalchemy as sa
|
|
||||||
|
|
||||||
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,
|
||||||
@ -63,8 +63,8 @@ def init_accounts_command(username: str) -> None:
|
|||||||
existing_id.add(new_id)
|
existing_id.add(new_id)
|
||||||
return new_id
|
return new_id
|
||||||
|
|
||||||
data: list[dict[str, t.Any]] = []
|
data: list[dict[str, Any]] = []
|
||||||
l10n_data: list[dict[str, t.Any]] = []
|
l10n_data: list[dict[str, Any]] = []
|
||||||
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}
|
||||||
account_id: int = get_new_id()
|
account_id: int = get_new_id()
|
||||||
|
@ -18,7 +18,7 @@
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
import csv
|
import csv
|
||||||
import typing as t
|
from typing import Any
|
||||||
|
|
||||||
import sqlalchemy as sa
|
import sqlalchemy as sa
|
||||||
|
|
||||||
@ -39,11 +39,11 @@ def init_currencies_command(username: str) -> None:
|
|||||||
return
|
return
|
||||||
|
|
||||||
creator_pk: int = get_user_pk(username)
|
creator_pk: int = get_user_pk(username)
|
||||||
currency_data: list[dict[str, t.Any]] = [{"code": x["code"],
|
currency_data: list[dict[str, Any]] = [{"code": x["code"],
|
||||||
"name_l10n": x["name"],
|
"name_l10n": x["name"],
|
||||||
"created_by_id": creator_pk,
|
"created_by_id": creator_pk,
|
||||||
"updated_by_id": creator_pk}
|
"updated_by_id": creator_pk}
|
||||||
for x in to_add]
|
for x in to_add]
|
||||||
locales: list[str] = [x[5:] for x in to_add[0] if x.startswith("l10n-")]
|
locales: list[str] = [x[5:] for x in to_add[0] if x.startswith("l10n-")]
|
||||||
l10n_data: list[dict[str, str]] = [{"currency_code": x["code"],
|
l10n_data: list[dict[str, str]] = [{"currency_code": x["code"],
|
||||||
"locale": y,
|
"locale": y,
|
||||||
|
@ -18,8 +18,8 @@
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
import datetime as dt
|
import datetime as dt
|
||||||
import typing as t
|
|
||||||
from abc import ABC, abstractmethod
|
from abc import ABC, abstractmethod
|
||||||
|
from typing import TypeVar, Generic, Type
|
||||||
|
|
||||||
import sqlalchemy as sa
|
import sqlalchemy as sa
|
||||||
from flask_babel import LazyString
|
from flask_babel import LazyString
|
||||||
@ -29,13 +29,13 @@ from wtforms import DateField, FieldList, FormField, TextAreaField, \
|
|||||||
from wtforms.validators import DataRequired, ValidationError
|
from wtforms.validators import DataRequired, ValidationError
|
||||||
|
|
||||||
from accounting import db
|
from accounting import db
|
||||||
|
from accounting.journal_entry.utils.account_option import AccountOption
|
||||||
|
from accounting.journal_entry.utils.description_editor import DescriptionEditor
|
||||||
|
from accounting.journal_entry.utils.original_line_items import \
|
||||||
|
get_selectable_original_line_items
|
||||||
from accounting.locale import lazy_gettext
|
from accounting.locale import lazy_gettext
|
||||||
from accounting.models import JournalEntry, Account, JournalEntryLineItem, \
|
from accounting.models import JournalEntry, Account, JournalEntryLineItem, \
|
||||||
JournalEntryCurrency
|
JournalEntryCurrency
|
||||||
from accounting.journal_entry.utils.account_option import AccountOption
|
|
||||||
from accounting.journal_entry.utils.original_line_items import \
|
|
||||||
get_selectable_original_line_items
|
|
||||||
from accounting.journal_entry.utils.description_editor import DescriptionEditor
|
|
||||||
from accounting.utils.random_id import new_id
|
from accounting.utils.random_id import new_id
|
||||||
from accounting.utils.strip_text import strip_multiline_text
|
from accounting.utils.strip_text import strip_multiline_text
|
||||||
from accounting.utils.user import get_current_user_pk
|
from accounting.utils.user import get_current_user_pk
|
||||||
@ -123,7 +123,7 @@ class JournalEntryForm(FlaskForm):
|
|||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
self.is_modified: bool = False
|
self.is_modified: bool = False
|
||||||
"""Whether the journal entry is modified during populate_obj()."""
|
"""Whether the journal entry is modified during populate_obj()."""
|
||||||
self.collector: t.Type[LineItemCollector] = LineItemCollector
|
self.collector: Type[LineItemCollector] = LineItemCollector
|
||||||
"""The line item collector. The default is the base abstract
|
"""The line item collector. The default is the base abstract
|
||||||
collector only to provide the correct type. The subclass forms should
|
collector only to provide the correct type. The subclass forms should
|
||||||
provide their own collectors."""
|
provide their own collectors."""
|
||||||
@ -155,7 +155,7 @@ class JournalEntryForm(FlaskForm):
|
|||||||
self.__set_date(obj, self.date.data)
|
self.__set_date(obj, self.date.data)
|
||||||
obj.note = self.note.data
|
obj.note = self.note.data
|
||||||
|
|
||||||
collector_cls: t.Type[LineItemCollector] = self.collector
|
collector_cls: Type[LineItemCollector] = self.collector
|
||||||
collector: collector_cls = collector_cls(self, obj)
|
collector: collector_cls = collector_cls(self, obj)
|
||||||
collector.collect()
|
collector.collect()
|
||||||
|
|
||||||
@ -309,11 +309,11 @@ class JournalEntryForm(FlaskForm):
|
|||||||
return db.session.scalar(select)
|
return db.session.scalar(select)
|
||||||
|
|
||||||
|
|
||||||
T = t.TypeVar("T", bound=JournalEntryForm)
|
T = TypeVar("T", bound=JournalEntryForm)
|
||||||
"""A journal entry form variant."""
|
"""A journal entry form variant."""
|
||||||
|
|
||||||
|
|
||||||
class LineItemCollector(t.Generic[T], ABC):
|
class LineItemCollector(Generic[T], ABC):
|
||||||
"""The line item collector."""
|
"""The line item collector."""
|
||||||
|
|
||||||
def __init__(self, form: T, obj: JournalEntry):
|
def __init__(self, form: T, obj: JournalEntry):
|
||||||
|
@ -18,7 +18,7 @@
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
import re
|
import re
|
||||||
import typing as t
|
from typing import Literal
|
||||||
|
|
||||||
import sqlalchemy as sa
|
import sqlalchemy as sa
|
||||||
|
|
||||||
@ -124,12 +124,12 @@ class DescriptionTag:
|
|||||||
class DescriptionType:
|
class DescriptionType:
|
||||||
"""A description type"""
|
"""A description type"""
|
||||||
|
|
||||||
def __init__(self, type_id: t.Literal["general", "travel", "bus"]):
|
def __init__(self, type_id: Literal["general", "travel", "bus"]):
|
||||||
"""Constructs a description type.
|
"""Constructs a description type.
|
||||||
|
|
||||||
:param type_id: The type ID, either "general", "travel", or "bus".
|
:param type_id: The type ID, either "general", "travel", or "bus".
|
||||||
"""
|
"""
|
||||||
self.id: t.Literal["general", "travel", "bus"] = type_id
|
self.id: Literal["general", "travel", "bus"] = type_id
|
||||||
"""The type ID."""
|
"""The type ID."""
|
||||||
self.__tag_dict: dict[str, DescriptionTag] = {}
|
self.__tag_dict: dict[str, DescriptionTag] = {}
|
||||||
"""A dictionary from the tag name to their corresponding tag."""
|
"""A dictionary from the tag name to their corresponding tag."""
|
||||||
@ -181,12 +181,12 @@ class DescriptionRecurring:
|
|||||||
class DescriptionDebitCredit:
|
class DescriptionDebitCredit:
|
||||||
"""The description on debit or credit."""
|
"""The description on debit or credit."""
|
||||||
|
|
||||||
def __init__(self, debit_credit: t.Literal["debit", "credit"]):
|
def __init__(self, debit_credit: Literal["debit", "credit"]):
|
||||||
"""Constructs the description on debit or credit.
|
"""Constructs the description on debit or credit.
|
||||||
|
|
||||||
:param debit_credit: Either "debit" or "credit".
|
:param debit_credit: Either "debit" or "credit".
|
||||||
"""
|
"""
|
||||||
self.debit_credit: t.Literal["debit", "credit"] = debit_credit
|
self.debit_credit: Literal["debit", "credit"] = debit_credit
|
||||||
"""Either debit or credit."""
|
"""Either debit or credit."""
|
||||||
self.general: DescriptionType = DescriptionType("general")
|
self.general: DescriptionType = DescriptionType("general")
|
||||||
"""The general tags."""
|
"""The general tags."""
|
||||||
@ -194,14 +194,14 @@ class DescriptionDebitCredit:
|
|||||||
"""The travel tags."""
|
"""The travel tags."""
|
||||||
self.bus: DescriptionType = DescriptionType("bus")
|
self.bus: DescriptionType = DescriptionType("bus")
|
||||||
"""The bus tags."""
|
"""The bus tags."""
|
||||||
self.__type_dict: dict[t.Literal["general", "travel", "bus"],
|
self.__type_dict: dict[Literal["general", "travel", "bus"],
|
||||||
DescriptionType] \
|
DescriptionType] \
|
||||||
= {x.id: x for x in {self.general, self.travel, self.bus}}
|
= {x.id: x for x in {self.general, self.travel, self.bus}}
|
||||||
"""A dictionary from the type ID to the corresponding tags."""
|
"""A dictionary from the type ID to the corresponding tags."""
|
||||||
self.recurring: list[DescriptionRecurring] = []
|
self.recurring: list[DescriptionRecurring] = []
|
||||||
"""The recurring transactions."""
|
"""The recurring transactions."""
|
||||||
|
|
||||||
def add_tag(self, tag_type: t.Literal["general", "travel", "bus"],
|
def add_tag(self, tag_type: Literal["general", "travel", "bus"],
|
||||||
name: str, account: Account, freq: int) -> None:
|
name: str, account: Account, freq: int) -> None:
|
||||||
"""Adds a tag.
|
"""Adds a tag.
|
||||||
|
|
||||||
@ -278,7 +278,7 @@ class DescriptionEditor:
|
|||||||
accounts: dict[int, Account] \
|
accounts: dict[int, Account] \
|
||||||
= {x.id: x for x in Account.query
|
= {x.id: x for x in Account.query
|
||||||
.filter(Account.id.in_({x.account_id for x in result})).all()}
|
.filter(Account.id.in_({x.account_id for x in result})).all()}
|
||||||
debit_credit_dict: dict[t.Literal["debit", "credit"],
|
debit_credit_dict: dict[Literal["debit", "credit"],
|
||||||
DescriptionDebitCredit] \
|
DescriptionDebitCredit] \
|
||||||
= {x.debit_credit: x for x in {self.debit, self.credit}}
|
= {x.debit_credit: x for x in {self.debit, self.credit}}
|
||||||
for row in result:
|
for row in result:
|
||||||
|
@ -17,19 +17,19 @@
|
|||||||
"""The operators for different journal entry types.
|
"""The operators for different journal entry types.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
import typing as t
|
|
||||||
from abc import ABC, abstractmethod
|
from abc import ABC, abstractmethod
|
||||||
|
from typing import Type
|
||||||
|
|
||||||
from flask import render_template, request, abort
|
from flask import render_template, request, abort
|
||||||
from flask_wtf import FlaskForm
|
from flask_wtf import FlaskForm
|
||||||
|
|
||||||
from accounting.models import JournalEntry
|
|
||||||
from accounting.template_globals import default_currency_code
|
|
||||||
from accounting.utils.journal_entry_types import JournalEntryType
|
|
||||||
from accounting.journal_entry.forms import JournalEntryForm, \
|
from accounting.journal_entry.forms import JournalEntryForm, \
|
||||||
CashReceiptJournalEntryForm, CashDisbursementJournalEntryForm, \
|
CashReceiptJournalEntryForm, CashDisbursementJournalEntryForm, \
|
||||||
TransferJournalEntryForm
|
TransferJournalEntryForm
|
||||||
from accounting.journal_entry.forms.line_item import LineItemForm
|
from accounting.journal_entry.forms.line_item import LineItemForm
|
||||||
|
from accounting.models import JournalEntry
|
||||||
|
from accounting.template_globals import default_currency_code
|
||||||
|
from accounting.utils.journal_entry_types import JournalEntryType
|
||||||
|
|
||||||
|
|
||||||
class JournalEntryOperator(ABC):
|
class JournalEntryOperator(ABC):
|
||||||
@ -39,7 +39,7 @@ class JournalEntryOperator(ABC):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def form(self) -> t.Type[JournalEntryForm]:
|
def form(self) -> Type[JournalEntryForm]:
|
||||||
"""Returns the form class.
|
"""Returns the form class.
|
||||||
|
|
||||||
:return: The form class.
|
:return: The form class.
|
||||||
@ -100,7 +100,7 @@ class CashReceiptJournalEntry(JournalEntryOperator):
|
|||||||
"""The order when checking the journal entry operator."""
|
"""The order when checking the journal entry operator."""
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def form(self) -> t.Type[JournalEntryForm]:
|
def form(self) -> Type[JournalEntryForm]:
|
||||||
"""Returns the form class.
|
"""Returns the form class.
|
||||||
|
|
||||||
:return: The form class.
|
:return: The form class.
|
||||||
@ -170,7 +170,7 @@ class CashDisbursementJournalEntry(JournalEntryOperator):
|
|||||||
"""The order when checking the journal entry operator."""
|
"""The order when checking the journal entry operator."""
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def form(self) -> t.Type[JournalEntryForm]:
|
def form(self) -> Type[JournalEntryForm]:
|
||||||
"""Returns the form class.
|
"""Returns the form class.
|
||||||
|
|
||||||
:return: The form class.
|
:return: The form class.
|
||||||
@ -243,7 +243,7 @@ class TransferJournalEntry(JournalEntryOperator):
|
|||||||
"""The order when checking the journal entry operator."""
|
"""The order when checking the journal entry operator."""
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def form(self) -> t.Type[JournalEntryForm]:
|
def form(self) -> Type[JournalEntryForm]:
|
||||||
"""Returns the form class.
|
"""Returns the form class.
|
||||||
|
|
||||||
:return: The form class.
|
:return: The form class.
|
||||||
|
@ -21,8 +21,8 @@ from __future__ import annotations
|
|||||||
|
|
||||||
import datetime as dt
|
import datetime as dt
|
||||||
import re
|
import re
|
||||||
import typing as t
|
|
||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
|
from typing import Type, Annotated, Self
|
||||||
|
|
||||||
import sqlalchemy as sa
|
import sqlalchemy as sa
|
||||||
from babel import Locale
|
from babel import Locale
|
||||||
@ -34,16 +34,16 @@ from accounting import db
|
|||||||
from accounting.locale import gettext
|
from accounting.locale import gettext
|
||||||
from accounting.utils.user import user_cls, user_pk_column
|
from accounting.utils.user import user_cls, user_pk_column
|
||||||
|
|
||||||
timestamp: t.Type[dt.datetime] \
|
timestamp: Type[dt.datetime] \
|
||||||
= t.Annotated[dt.datetime, mapped_column(db.DateTime(timezone=True),
|
= Annotated[dt.datetime, mapped_column(db.DateTime(timezone=True),
|
||||||
server_default=db.func.now())]
|
server_default=db.func.now())]
|
||||||
"""The timestamp."""
|
"""The timestamp."""
|
||||||
user_pk: t.Type[int] \
|
user_pk: Type[int] \
|
||||||
= t.Annotated[int, mapped_column(db.ForeignKey(user_pk_column,
|
= Annotated[int, mapped_column(db.ForeignKey(user_pk_column,
|
||||||
onupdate="CASCADE"))]
|
onupdate="CASCADE"))]
|
||||||
"""The user primary key."""
|
"""The user primary key."""
|
||||||
random_pk: t.Type[int] \
|
random_pk: Type[int] \
|
||||||
= t.Annotated[int, mapped_column(primary_key=True, autoincrement=False)]
|
= Annotated[int, mapped_column(primary_key=True, autoincrement=False)]
|
||||||
"""The random primary key."""
|
"""The random primary key."""
|
||||||
|
|
||||||
|
|
||||||
@ -273,11 +273,11 @@ class Account(db.Model):
|
|||||||
:return: None.
|
:return: None.
|
||||||
"""
|
"""
|
||||||
AccountL10n.query.filter(AccountL10n.account == self).delete()
|
AccountL10n.query.filter(AccountL10n.account == self).delete()
|
||||||
cls: t.Type[t.Self] = self.__class__
|
cls: Type[Self] = self.__class__
|
||||||
cls.query.filter(cls.id == self.id).delete()
|
cls.query.filter(cls.id == self.id).delete()
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def find_by_code(cls, code: str) -> t.Self | None:
|
def find_by_code(cls, code: str) -> Self | None:
|
||||||
"""Finds an account by its code.
|
"""Finds an account by its code.
|
||||||
|
|
||||||
:param code: The code.
|
:param code: The code.
|
||||||
@ -290,7 +290,7 @@ class Account(db.Model):
|
|||||||
cls.no == int(m.group(2))).first()
|
cls.no == int(m.group(2))).first()
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def selectable_debit(cls) -> list[t.Self]:
|
def selectable_debit(cls) -> list[Self]:
|
||||||
"""Returns the selectable debit accounts.
|
"""Returns the selectable debit accounts.
|
||||||
Payable line items can not start from debit.
|
Payable line items can not start from debit.
|
||||||
|
|
||||||
@ -313,7 +313,7 @@ class Account(db.Model):
|
|||||||
.order_by(cls.base_code, cls.no).all()
|
.order_by(cls.base_code, cls.no).all()
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def selectable_credit(cls) -> list[t.Self]:
|
def selectable_credit(cls) -> list[Self]:
|
||||||
"""Returns the selectable debit accounts.
|
"""Returns the selectable debit accounts.
|
||||||
Receivable line items can not start from credit.
|
Receivable line items can not start from credit.
|
||||||
|
|
||||||
@ -335,7 +335,7 @@ class Account(db.Model):
|
|||||||
.order_by(cls.base_code, cls.no).all()
|
.order_by(cls.base_code, cls.no).all()
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def cash(cls) -> t.Self:
|
def cash(cls) -> Self:
|
||||||
"""Returns the cash account.
|
"""Returns the cash account.
|
||||||
|
|
||||||
:return: The cash account
|
:return: The cash account
|
||||||
@ -343,7 +343,7 @@ class Account(db.Model):
|
|||||||
return cls.find_by_code(cls.CASH_CODE)
|
return cls.find_by_code(cls.CASH_CODE)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def accumulated_change(cls) -> t.Self:
|
def accumulated_change(cls) -> Self:
|
||||||
"""Returns the accumulated-change account.
|
"""Returns the accumulated-change account.
|
||||||
|
|
||||||
:return: The accumulated-change account
|
:return: The accumulated-change account
|
||||||
@ -467,7 +467,7 @@ class Currency(db.Model):
|
|||||||
:return: None.
|
:return: None.
|
||||||
"""
|
"""
|
||||||
CurrencyL10n.query.filter(CurrencyL10n.currency == self).delete()
|
CurrencyL10n.query.filter(CurrencyL10n.currency == self).delete()
|
||||||
cls: t.Type[t.Self] = self.__class__
|
cls: Type[Self] = self.__class__
|
||||||
cls.query.filter(cls.code == self.code).delete()
|
cls.query.filter(cls.code == self.code).delete()
|
||||||
|
|
||||||
|
|
||||||
@ -797,14 +797,14 @@ class JournalEntryLineItem(db.Model):
|
|||||||
setattr(self, "__balance", value)
|
setattr(self, "__balance", value)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def offsets(self) -> list[t.Self]:
|
def offsets(self) -> list[Self]:
|
||||||
"""Returns the offset items.
|
"""Returns the offset items.
|
||||||
|
|
||||||
:return: The offset items.
|
:return: The offset items.
|
||||||
"""
|
"""
|
||||||
if not hasattr(self, "__offsets"):
|
if not hasattr(self, "__offsets"):
|
||||||
cls: t.Type[t.Self] = self.__class__
|
cls: Type[Self] = self.__class__
|
||||||
offsets: list[t.Self] = cls.query.join(JournalEntry)\
|
offsets: list[Self] = cls.query.join(JournalEntry)\
|
||||||
.filter(JournalEntryLineItem.original_line_item_id == self.id)\
|
.filter(JournalEntryLineItem.original_line_item_id == self.id)\
|
||||||
.order_by(JournalEntry.date, JournalEntry.no,
|
.order_by(JournalEntry.date, JournalEntry.no,
|
||||||
cls.is_debit, cls.no).all()
|
cls.is_debit, cls.no).all()
|
||||||
@ -831,7 +831,7 @@ class JournalEntryLineItem(db.Model):
|
|||||||
setattr(self, "__is_offset", value)
|
setattr(self, "__is_offset", value)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def match(self) -> t.Self | None:
|
def match(self) -> Self | None:
|
||||||
"""Returns the match of the line item.
|
"""Returns the match of the line item.
|
||||||
|
|
||||||
:return: The match of the line item.
|
:return: The match of the line item.
|
||||||
@ -841,7 +841,7 @@ class JournalEntryLineItem(db.Model):
|
|||||||
return getattr(self, "__match")
|
return getattr(self, "__match")
|
||||||
|
|
||||||
@match.setter
|
@match.setter
|
||||||
def match(self, value: t.Self) -> None:
|
def match(self, value: Self) -> None:
|
||||||
"""Sets the match of the line item.
|
"""Sets the match of the line item.
|
||||||
|
|
||||||
:param value: The matcho of the line item.
|
:param value: The matcho of the line item.
|
||||||
|
@ -21,7 +21,7 @@ This file is largely taken from the NanoParma ERP project, first written in
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
import datetime as dt
|
import datetime as dt
|
||||||
import typing as t
|
from collections.abc import Callable
|
||||||
|
|
||||||
from accounting.models import JournalEntry
|
from accounting.models import JournalEntry
|
||||||
from .period import Period
|
from .period import Period
|
||||||
@ -32,13 +32,13 @@ from .shortcuts import ThisMonth, LastMonth, SinceLastMonth, ThisYear, \
|
|||||||
class PeriodChooser:
|
class PeriodChooser:
|
||||||
"""The period chooser."""
|
"""The period chooser."""
|
||||||
|
|
||||||
def __init__(self, get_url: t.Callable[[Period], str]):
|
def __init__(self, get_url: Callable[[Period], str]):
|
||||||
"""Constructs a period chooser.
|
"""Constructs a period chooser.
|
||||||
|
|
||||||
:param get_url: The callback to return the URL of the current report in
|
:param get_url: The callback to return the URL of the current report in
|
||||||
a period.
|
a period.
|
||||||
"""
|
"""
|
||||||
self.__get_url: t.Callable[[Period], str] = get_url
|
self.__get_url: Callable[[Period], str] = get_url
|
||||||
"""The callback to return the URL of the current report in a period."""
|
"""The callback to return the URL of the current report in a period."""
|
||||||
|
|
||||||
# Shortcut periods
|
# Shortcut periods
|
||||||
|
@ -20,7 +20,8 @@
|
|||||||
import calendar
|
import calendar
|
||||||
import datetime as dt
|
import datetime as dt
|
||||||
import re
|
import re
|
||||||
import typing as t
|
from collections.abc import Callable
|
||||||
|
from typing import Type
|
||||||
|
|
||||||
from .period import Period
|
from .period import Period
|
||||||
from .shortcuts import ThisMonth, LastMonth, SinceLastMonth, ThisYear, \
|
from .shortcuts import ThisMonth, LastMonth, SinceLastMonth, ThisYear, \
|
||||||
@ -39,7 +40,7 @@ def get_period(spec: str | None = None) -> Period:
|
|||||||
"""
|
"""
|
||||||
if spec is None:
|
if spec is None:
|
||||||
return ThisMonth()
|
return ThisMonth()
|
||||||
named_periods: dict[str, t.Type[t.Callable[[], Period]]] = {
|
named_periods: dict[str, Type[Callable[[], Period]]] = {
|
||||||
"this-month": lambda: ThisMonth(),
|
"this-month": lambda: ThisMonth(),
|
||||||
"last-month": lambda: LastMonth(),
|
"last-month": lambda: LastMonth(),
|
||||||
"since-last-month": lambda: SinceLastMonth(),
|
"since-last-month": lambda: SinceLastMonth(),
|
||||||
|
@ -21,7 +21,7 @@ This file is largely taken from the NanoParma ERP project, first written in
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
import datetime as dt
|
import datetime as dt
|
||||||
import typing as t
|
from typing import Self
|
||||||
|
|
||||||
from .description import get_desc
|
from .description import get_desc
|
||||||
from .month_end import month_end
|
from .month_end import month_end
|
||||||
@ -119,7 +119,7 @@ class Period:
|
|||||||
and not self.is_a_day
|
and not self.is_a_day
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def before(self) -> t.Self | None:
|
def before(self) -> Self | None:
|
||||||
"""Returns the period before this period.
|
"""Returns the period before this period.
|
||||||
|
|
||||||
:return: The period before this period.
|
:return: The period before this period.
|
||||||
|
@ -17,8 +17,9 @@
|
|||||||
"""The page parameters of a report.
|
"""The page parameters of a report.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
import typing as t
|
|
||||||
from abc import ABC, abstractmethod
|
from abc import ABC, abstractmethod
|
||||||
|
from collections.abc import Callable
|
||||||
|
from typing import Type
|
||||||
from urllib.parse import urlparse, ParseResult, parse_qsl, urlencode, \
|
from urllib.parse import urlparse, ParseResult, parse_qsl, urlencode, \
|
||||||
urlunparse
|
urlunparse
|
||||||
|
|
||||||
@ -52,7 +53,7 @@ class BasePageParams(ABC):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def journal_entry_types(self) -> t.Type[JournalEntryType]:
|
def journal_entry_types(self) -> Type[JournalEntryType]:
|
||||||
"""Returns the journal entry types.
|
"""Returns the journal entry types.
|
||||||
|
|
||||||
:return: The journal entry types.
|
:return: The journal entry types.
|
||||||
@ -72,7 +73,7 @@ class BasePageParams(ABC):
|
|||||||
return urlunparse(parts)
|
return urlunparse(parts)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _get_currency_options(get_url: t.Callable[[Currency], str],
|
def _get_currency_options(get_url: Callable[[Currency], str],
|
||||||
active_currency: Currency) -> list[OptionLink]:
|
active_currency: Currency) -> list[OptionLink]:
|
||||||
"""Returns the currency options.
|
"""Returns the currency options.
|
||||||
|
|
||||||
|
@ -21,7 +21,7 @@ This file is largely taken from the NanoParma ERP project, first written in
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
import re
|
import re
|
||||||
import typing as t
|
from collections.abc import Iterator
|
||||||
|
|
||||||
from flask_babel import LazyString
|
from flask_babel import LazyString
|
||||||
|
|
||||||
@ -190,7 +190,7 @@ class ReportChooser:
|
|||||||
self.__active_report == ReportType.UNMATCHED,
|
self.__active_report == ReportType.UNMATCHED,
|
||||||
fa_icon="fa-solid fa-file-circle-question")
|
fa_icon="fa-solid fa-file-circle-question")
|
||||||
|
|
||||||
def __iter__(self) -> t.Iterator[OptionLink]:
|
def __iter__(self) -> Iterator[OptionLink]:
|
||||||
"""Returns the iteration of the reports.
|
"""Returns the iteration of the reports.
|
||||||
|
|
||||||
:return: The iteration of the reports.
|
:return: The iteration of the reports.
|
||||||
|
@ -18,8 +18,8 @@
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
import datetime as dt
|
import datetime as dt
|
||||||
import typing as t
|
|
||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
from flask_babel import get_locale
|
from flask_babel import get_locale
|
||||||
|
|
||||||
@ -71,7 +71,7 @@ def format_date(value: dt.date) -> str:
|
|||||||
return "{}/{}({})".format(value.month, value.day, weekday)
|
return "{}/{}({})".format(value.month, value.day, weekday)
|
||||||
|
|
||||||
|
|
||||||
def default(value: t.Any, default_value: t.Any = "") -> t.Any:
|
def default(value: Any, default_value: Any = "") -> Any:
|
||||||
"""Returns the default value if the given value is None.
|
"""Returns the default value if the given value is None.
|
||||||
|
|
||||||
:param value: The value.
|
:param value: The value.
|
||||||
|
@ -20,10 +20,10 @@ warnings from the IDE.
|
|||||||
This module should not import any other module from the application.
|
This module should not import any other module from the application.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
import typing as t
|
from typing import Any
|
||||||
|
|
||||||
|
|
||||||
def s(message: t.Any) -> str:
|
def s(message: Any) -> str:
|
||||||
"""Casts the LazyString message to the string type.
|
"""Casts the LazyString message to the string type.
|
||||||
|
|
||||||
:param message: The message.
|
:param message: The message.
|
||||||
|
@ -17,12 +17,13 @@
|
|||||||
"""The current assets and liabilities account.
|
"""The current assets and liabilities account.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
import typing as t
|
from typing import Self
|
||||||
|
|
||||||
|
import sqlalchemy as sa
|
||||||
|
|
||||||
from accounting import db
|
from accounting import db
|
||||||
from accounting.locale import gettext
|
from accounting.locale import gettext
|
||||||
from accounting.models import Account
|
from accounting.models import Account
|
||||||
import sqlalchemy as sa
|
|
||||||
|
|
||||||
|
|
||||||
class CurrentAccount:
|
class CurrentAccount:
|
||||||
@ -54,7 +55,7 @@ class CurrentAccount:
|
|||||||
return self.str
|
return self.str
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def current_assets_and_liabilities(cls) -> t.Self:
|
def current_assets_and_liabilities(cls) -> Self:
|
||||||
"""Returns the pseudo account for all current assets and liabilities.
|
"""Returns the pseudo account for all current assets and liabilities.
|
||||||
|
|
||||||
:return: The pseudo account for all current assets and liabilities.
|
:return: The pseudo account for all current assets and liabilities.
|
||||||
@ -67,7 +68,7 @@ class CurrentAccount:
|
|||||||
return account
|
return account
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def accounts(cls) -> list[t.Self]:
|
def accounts(cls) -> list[Self]:
|
||||||
"""Returns the current assets and liabilities accounts.
|
"""Returns the current assets and liabilities accounts.
|
||||||
|
|
||||||
:return: The current assets and liabilities accounts.
|
:return: The current assets and liabilities accounts.
|
||||||
|
@ -19,7 +19,7 @@
|
|||||||
This module should not import any other module from the application.
|
This module should not import any other module from the application.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
import typing as t
|
from typing import Any
|
||||||
|
|
||||||
from flask import flash
|
from flask import flash
|
||||||
from flask_wtf import FlaskForm
|
from flask_wtf import FlaskForm
|
||||||
@ -34,7 +34,7 @@ def flash_form_errors(form: FlaskForm) -> None:
|
|||||||
__flash_errors(form.errors)
|
__flash_errors(form.errors)
|
||||||
|
|
||||||
|
|
||||||
def __flash_errors(error: t.Any) -> None:
|
def __flash_errors(error: Any) -> None:
|
||||||
"""Flash all errors recursively.
|
"""Flash all errors recursively.
|
||||||
|
|
||||||
:param error: The errors.
|
:param error: The errors.
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
"""The SQLAlchemy alias for the offset items.
|
"""The SQLAlchemy alias for the offset items.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
import typing as t
|
from typing import Any
|
||||||
|
|
||||||
import sqlalchemy as sa
|
import sqlalchemy as sa
|
||||||
|
|
||||||
@ -30,10 +30,10 @@ def offset_alias() -> sa.Alias:
|
|||||||
:return: The SQLAlchemy alias for the offset items.
|
:return: The SQLAlchemy alias for the offset items.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def as_from(model_cls: t.Any) -> sa.FromClause:
|
def as_from(model_cls: Any) -> sa.FromClause:
|
||||||
return model_cls
|
return model_cls
|
||||||
|
|
||||||
def as_alias(alias: t.Any) -> sa.Alias:
|
def as_alias(alias: Any) -> sa.Alias:
|
||||||
return alias
|
return alias
|
||||||
|
|
||||||
return as_alias(sa.alias(as_from(JournalEntryLineItem), name="offset"))
|
return as_alias(sa.alias(as_from(JournalEntryLineItem), name="offset"))
|
||||||
|
@ -19,7 +19,7 @@
|
|||||||
This module should not import any other module from the application.
|
This module should not import any other module from the application.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
import typing as t
|
from typing import TypeVar, Generic
|
||||||
from urllib.parse import urlparse, parse_qsl, urlencode, urlunparse, \
|
from urllib.parse import urlparse, parse_qsl, urlencode, urlunparse, \
|
||||||
ParseResult
|
ParseResult
|
||||||
|
|
||||||
@ -62,10 +62,10 @@ class Redirection(RequestRedirect):
|
|||||||
DEFAULT_PAGE_SIZE: int = 10
|
DEFAULT_PAGE_SIZE: int = 10
|
||||||
"""The default page size."""
|
"""The default page size."""
|
||||||
|
|
||||||
T = t.TypeVar("T")
|
T = TypeVar("T")
|
||||||
|
|
||||||
|
|
||||||
class Pagination(t.Generic[T]):
|
class Pagination(Generic[T]):
|
||||||
"""The pagination utility."""
|
"""The pagination utility."""
|
||||||
|
|
||||||
def __init__(self, items: list[T], is_reversed: bool = False):
|
def __init__(self, items: list[T], is_reversed: bool = False):
|
||||||
@ -91,7 +91,7 @@ class Pagination(t.Generic[T]):
|
|||||||
"""The options to the number of items in a page."""
|
"""The options to the number of items in a page."""
|
||||||
|
|
||||||
|
|
||||||
class AbstractPagination(t.Generic[T]):
|
class AbstractPagination(Generic[T]):
|
||||||
"""An abstract pagination."""
|
"""An abstract pagination."""
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
@ -19,21 +19,21 @@
|
|||||||
This module should not import any other module from the application.
|
This module should not import any other module from the application.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
import typing as t
|
from collections.abc import Callable
|
||||||
|
|
||||||
from flask import abort, Blueprint, Response
|
from flask import abort, Blueprint, Response
|
||||||
|
|
||||||
from accounting.utils.user import get_current_user, UserUtilityInterface
|
from accounting.utils.user import get_current_user, UserUtilityInterface
|
||||||
|
|
||||||
|
|
||||||
def has_permission(rule: t.Callable[[], bool]) -> t.Callable:
|
def has_permission(rule: Callable[[], bool]) -> Callable:
|
||||||
"""The permission decorator to check whether the current user is allowed.
|
"""The permission decorator to check whether the current user is allowed.
|
||||||
|
|
||||||
:param rule: The permission rule.
|
:param rule: The permission rule.
|
||||||
:return: The view decorator.
|
:return: The view decorator.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def decorator(view: t.Callable) -> t.Callable:
|
def decorator(view: Callable) -> Callable:
|
||||||
"""The view decorator to decorate a view with permission tests.
|
"""The view decorator to decorate a view with permission tests.
|
||||||
|
|
||||||
:param view: The view.
|
:param view: The view.
|
||||||
@ -61,16 +61,16 @@ def has_permission(rule: t.Callable[[], bool]) -> t.Callable:
|
|||||||
return decorator
|
return decorator
|
||||||
|
|
||||||
|
|
||||||
__can_view_func: t.Callable[[], bool] = lambda: True
|
__can_view_func: Callable[[], bool] = lambda: True
|
||||||
"""The callback that returns whether the current user can view the accounting
|
"""The callback that returns whether the current user can view the accounting
|
||||||
data."""
|
data."""
|
||||||
__can_edit_func: t.Callable[[], bool] = lambda: True
|
__can_edit_func: Callable[[], bool] = lambda: True
|
||||||
"""The callback that returns whether the current user can edit the accounting
|
"""The callback that returns whether the current user can edit the accounting
|
||||||
data."""
|
data."""
|
||||||
__can_admin_func: t.Callable[[], bool] = lambda: True
|
__can_admin_func: Callable[[], bool] = lambda: True
|
||||||
"""The callback that returns whether the current user can administrate the
|
"""The callback that returns whether the current user can administrate the
|
||||||
accounting settings."""
|
accounting settings."""
|
||||||
_unauthorized_func: t.Callable[[], Response | None] \
|
_unauthorized_func: Callable[[], Response | None] \
|
||||||
= lambda: Response(status=403)
|
= lambda: Response(status=403)
|
||||||
"""The callback that returns the response to require the user to log in."""
|
"""The callback that returns the response to require the user to log in."""
|
||||||
|
|
||||||
|
@ -19,13 +19,13 @@
|
|||||||
This module should not import any other module from the application.
|
This module should not import any other module from the application.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
import typing as t
|
|
||||||
from secrets import randbelow
|
from secrets import randbelow
|
||||||
|
from typing import Type
|
||||||
|
|
||||||
from accounting import db
|
from accounting import db
|
||||||
|
|
||||||
|
|
||||||
def new_id(cls: t.Type):
|
def new_id(cls: Type):
|
||||||
"""Returns a new random ID for the data model.
|
"""Returns a new random ID for the data model.
|
||||||
|
|
||||||
:param cls: The data model.
|
:param cls: The data model.
|
||||||
|
@ -19,17 +19,17 @@
|
|||||||
This module should not import any other module from the application.
|
This module should not import any other module from the application.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
import typing as t
|
|
||||||
from abc import ABC, abstractmethod
|
from abc import ABC, abstractmethod
|
||||||
|
from typing import TypeVar, Generic, Type
|
||||||
|
|
||||||
import sqlalchemy as sa
|
import sqlalchemy as sa
|
||||||
from flask import g, Response
|
from flask import g, Response
|
||||||
from flask_sqlalchemy.model import Model
|
from flask_sqlalchemy.model import Model
|
||||||
|
|
||||||
T = t.TypeVar("T", bound=Model)
|
T = TypeVar("T", bound=Model)
|
||||||
|
|
||||||
|
|
||||||
class UserUtilityInterface(t.Generic[T], ABC):
|
class UserUtilityInterface(Generic[T], ABC):
|
||||||
"""The interface for the user utilities."""
|
"""The interface for the user utilities."""
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
@ -72,7 +72,7 @@ class UserUtilityInterface(t.Generic[T], ABC):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def cls(self) -> t.Type[T]:
|
def cls(self) -> Type[T]:
|
||||||
"""Returns the class of the user data model.
|
"""Returns the class of the user data model.
|
||||||
|
|
||||||
:return: The class of the user data model.
|
:return: The class of the user data model.
|
||||||
@ -112,7 +112,7 @@ class UserUtilityInterface(t.Generic[T], ABC):
|
|||||||
|
|
||||||
__user_utils: UserUtilityInterface
|
__user_utils: UserUtilityInterface
|
||||||
"""The user utilities."""
|
"""The user utilities."""
|
||||||
user_cls: t.Type[Model] = Model
|
user_cls: Type[Model] = Model
|
||||||
"""The user class."""
|
"""The user class."""
|
||||||
user_pk_column: sa.Column = sa.Column(sa.Integer)
|
user_pk_column: sa.Column = sa.Column(sa.Integer)
|
||||||
"""The primary key column of the user class."""
|
"""The primary key column of the user class."""
|
||||||
|
@ -18,8 +18,8 @@
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
import csv
|
import csv
|
||||||
import typing as t
|
|
||||||
import unittest
|
import unittest
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
import sqlalchemy as sa
|
import sqlalchemy as sa
|
||||||
from click.testing import Result
|
from click.testing import Result
|
||||||
@ -80,7 +80,7 @@ class ConsoleCommandTestCase(unittest.TestCase):
|
|||||||
from accounting.models import BaseAccount
|
from accounting.models import BaseAccount
|
||||||
|
|
||||||
with open(data_dir / "base_accounts.csv") as fp:
|
with open(data_dir / "base_accounts.csv") as fp:
|
||||||
data: dict[dict[str, t.Any]] \
|
data: dict[dict[str, Any]] \
|
||||||
= {x["code"]: {"code": x["code"],
|
= {x["code"]: {"code": x["code"],
|
||||||
"title": x["title"],
|
"title": x["title"],
|
||||||
"l10n": {y[5:]: x[y]
|
"l10n": {y[5:]: x[y]
|
||||||
@ -135,7 +135,7 @@ class ConsoleCommandTestCase(unittest.TestCase):
|
|||||||
from accounting.models import Currency
|
from accounting.models import Currency
|
||||||
|
|
||||||
with open(data_dir / "currencies.csv") as fp:
|
with open(data_dir / "currencies.csv") as fp:
|
||||||
data: dict[dict[str, t.Any]] \
|
data: dict[dict[str, Any]] \
|
||||||
= {x["code"]: {"code": x["code"],
|
= {x["code"]: {"code": x["code"],
|
||||||
"name": x["name"],
|
"name": x["name"],
|
||||||
"l10n": {y[5:]: x[y]
|
"l10n": {y[5:]: x[y]
|
||||||
|
@ -18,8 +18,8 @@
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
import os
|
import os
|
||||||
import typing as t
|
|
||||||
from secrets import token_urlsafe
|
from secrets import token_urlsafe
|
||||||
|
from typing import Type
|
||||||
|
|
||||||
from click.testing import Result
|
from click.testing import Result
|
||||||
from flask import Flask, Blueprint, render_template, redirect, Response, \
|
from flask import Flask, Blueprint, render_template, redirect, Response, \
|
||||||
@ -94,7 +94,7 @@ def create_app(is_testing: bool = False) -> Flask:
|
|||||||
return redirect(append_next(url_for("auth.login-form")))
|
return redirect(append_next(url_for("auth.login-form")))
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def cls(self) -> t.Type[auth.User]:
|
def cls(self) -> Type[auth.User]:
|
||||||
return auth.User
|
return auth.User
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
"""The authentication for the Mia! Accounting demonstration website.
|
"""The authentication for the Mia! Accounting demonstration website.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
import typing as t
|
from collections.abc import Callable
|
||||||
|
|
||||||
from flask import Blueprint, render_template, Flask, redirect, url_for, \
|
from flask import Blueprint, render_template, Flask, redirect, url_for, \
|
||||||
session, request, g, Response, abort
|
session, request, g, Response, abort
|
||||||
@ -96,7 +96,7 @@ def current_user() -> User | None:
|
|||||||
return g.user
|
return g.user
|
||||||
|
|
||||||
|
|
||||||
def admin_required(view: t.Callable) -> t.Callable:
|
def admin_required(view: Callable) -> Callable:
|
||||||
"""The view decorator to require the user to be an administrator.
|
"""The view decorator to require the user to be an administrator.
|
||||||
|
|
||||||
:param view: The view.
|
:param view: The view.
|
||||||
|
@ -20,10 +20,10 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import datetime as dt
|
import datetime as dt
|
||||||
import typing as t
|
|
||||||
from abc import ABC, abstractmethod
|
from abc import ABC, abstractmethod
|
||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
from secrets import randbelow
|
from secrets import randbelow
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
import sqlalchemy as sa
|
import sqlalchemy as sa
|
||||||
from flask import Flask
|
from flask import Flask
|
||||||
@ -193,8 +193,8 @@ class BaseTestData(ABC):
|
|||||||
.filter(User.username == username).first()
|
.filter(User.username == username).first()
|
||||||
assert current_user is not None
|
assert current_user is not None
|
||||||
self.__current_user_id: int = current_user.id
|
self.__current_user_id: int = current_user.id
|
||||||
self.__journal_entries: list[dict[str, t.Any]] = []
|
self.__journal_entries: list[dict[str, Any]] = []
|
||||||
self.__line_items: list[dict[str, t.Any]] = []
|
self.__line_items: list[dict[str, Any]] = []
|
||||||
self._init_data()
|
self._init_data()
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
@ -258,7 +258,7 @@ class BaseTestData(ABC):
|
|||||||
assert account is not None
|
assert account is not None
|
||||||
debit_no = debit_no + 1
|
debit_no = debit_no + 1
|
||||||
line_item.id = self.__new_id(existing_l_id)
|
line_item.id = self.__new_id(existing_l_id)
|
||||||
data: dict[str, t.Any] \
|
data: dict[str, Any] \
|
||||||
= {"id": line_item.id,
|
= {"id": line_item.id,
|
||||||
"journal_entry_id": journal_entry_data.id,
|
"journal_entry_id": journal_entry_data.id,
|
||||||
"is_debit": True,
|
"is_debit": True,
|
||||||
@ -277,7 +277,7 @@ class BaseTestData(ABC):
|
|||||||
assert account is not None
|
assert account is not None
|
||||||
credit_no = credit_no + 1
|
credit_no = credit_no + 1
|
||||||
line_item.id = self.__new_id(existing_l_id)
|
line_item.id = self.__new_id(existing_l_id)
|
||||||
data: dict[str, t.Any] \
|
data: dict[str, Any] \
|
||||||
= {"id": line_item.id,
|
= {"id": line_item.id,
|
||||||
"journal_entry_id": journal_entry_data.id,
|
"journal_entry_id": journal_entry_data.id,
|
||||||
"is_debit": False,
|
"is_debit": False,
|
||||||
|
@ -20,7 +20,7 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import re
|
import re
|
||||||
import typing as t
|
from typing import Literal
|
||||||
|
|
||||||
import httpx
|
import httpx
|
||||||
from flask import Flask, render_template_string
|
from flask import Flask, render_template_string
|
||||||
@ -108,7 +108,7 @@ def get_client(app: Flask, username: str) -> tuple[httpx.Client, str]:
|
|||||||
|
|
||||||
|
|
||||||
def set_locale(client: httpx.Client, csrf_token: str,
|
def set_locale(client: httpx.Client, csrf_token: str,
|
||||||
locale: t.Literal["en", "zh_Hant", "zh_Hans"]) -> None:
|
locale: Literal["en", "zh_Hant", "zh_Hans"]) -> None:
|
||||||
"""Sets the current locale.
|
"""Sets the current locale.
|
||||||
|
|
||||||
:param client: The test client.
|
:param client: The test client.
|
||||||
|
Loading…
Reference in New Issue
Block a user