Compare commits
23 Commits
e083b11394
...
ee5b447c23
Author | SHA1 | Date | |
---|---|---|---|
ee5b447c23 | |||
25bfcf4aa4 | |||
5956d2cd4c | |||
833285d924 | |||
dee4f5e83f | |||
f0d1cae32d | |||
5dc71697b3 | |||
1bb1e03c08 | |||
914ff92e0f | |||
8a1cf463b1 | |||
d4cf224d6b | |||
8d412ec00a | |||
2986c518ce | |||
f1351243a6 | |||
969e8c76a6 | |||
10f5e75752 | |||
169b3c292a | |||
3eb3aef2f2 | |||
6c455a615c | |||
4f3339bf68 | |||
b5aa7e923f | |||
359c335662 | |||
c11ae23885 |
@ -17,7 +17,7 @@
|
|||||||
"""The path converters for the journal entry management.
|
"""The path converters for the journal entry management.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
from datetime import date
|
import datetime as dt
|
||||||
|
|
||||||
from flask import abort
|
from flask import abort
|
||||||
from werkzeug.routing import BaseConverter
|
from werkzeug.routing import BaseConverter
|
||||||
@ -82,18 +82,18 @@ class DateConverter(BaseConverter):
|
|||||||
"""The date converter to convert the ISO date from and to the
|
"""The date converter to convert the ISO date from and to the
|
||||||
corresponding date in the routes."""
|
corresponding date in the routes."""
|
||||||
|
|
||||||
def to_python(self, value: str) -> date:
|
def to_python(self, value: str) -> dt.date:
|
||||||
"""Converts an ISO date to a date.
|
"""Converts an ISO date to a date.
|
||||||
|
|
||||||
:param value: The ISO date.
|
:param value: The ISO date.
|
||||||
:return: The corresponding date.
|
:return: The corresponding date.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
return date.fromisoformat(value)
|
return dt.date.fromisoformat(value)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
abort(404)
|
abort(404)
|
||||||
|
|
||||||
def to_url(self, value: date) -> str:
|
def to_url(self, value: dt.date) -> str:
|
||||||
"""Converts a date to its ISO date.
|
"""Converts a date to its ISO date.
|
||||||
|
|
||||||
:param value: The date.
|
:param value: The date.
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
"""The line item sub-forms for the journal entry management.
|
"""The line item sub-forms for the journal entry management.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
from datetime import date
|
import datetime as dt
|
||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
|
|
||||||
import sqlalchemy as sa
|
import sqlalchemy as sa
|
||||||
@ -307,7 +307,7 @@ class LineItemForm(FlaskForm):
|
|||||||
return getattr(self, "____original_line_item")
|
return getattr(self, "____original_line_item")
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def original_line_item_date(self) -> date | None:
|
def original_line_item_date(self) -> dt.date | None:
|
||||||
"""Returns the text representation of the original line item.
|
"""Returns the text representation of the original line item.
|
||||||
|
|
||||||
:return: The text representation of the original line item.
|
:return: The text representation of the original line item.
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
"""The reorder forms for the journal entry management.
|
"""The reorder forms for the journal entry management.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
from datetime import date
|
import datetime as dt
|
||||||
|
|
||||||
import sqlalchemy as sa
|
import sqlalchemy as sa
|
||||||
from flask import request
|
from flask import request
|
||||||
@ -26,17 +26,15 @@ from accounting import db
|
|||||||
from accounting.models import JournalEntry
|
from accounting.models import JournalEntry
|
||||||
|
|
||||||
|
|
||||||
def sort_journal_entries_in(journal_entry_date: date,
|
def sort_journal_entries_in(date: dt.date, exclude: int | None = None) -> None:
|
||||||
exclude: int | None = None) -> None:
|
|
||||||
"""Sorts the journal entries under a date after changing the date or
|
"""Sorts the journal entries under a date after changing the date or
|
||||||
deleting a journal entry.
|
deleting a journal entry.
|
||||||
|
|
||||||
:param journal_entry_date: The date of the journal entry.
|
:param date: The date of the journal entry.
|
||||||
:param exclude: The journal entry ID to exclude.
|
:param exclude: The journal entry ID to exclude.
|
||||||
:return: None.
|
:return: None.
|
||||||
"""
|
"""
|
||||||
conditions: list[sa.BinaryExpression] \
|
conditions: list[sa.BinaryExpression] = [JournalEntry.date == date]
|
||||||
= [JournalEntry.date == journal_entry_date]
|
|
||||||
if exclude is not None:
|
if exclude is not None:
|
||||||
conditions.append(JournalEntry.id != exclude)
|
conditions.append(JournalEntry.id != exclude)
|
||||||
journal_entries: list[JournalEntry] = JournalEntry.query\
|
journal_entries: list[JournalEntry] = JournalEntry.query\
|
||||||
@ -50,12 +48,12 @@ def sort_journal_entries_in(journal_entry_date: date,
|
|||||||
class JournalEntryReorderForm:
|
class JournalEntryReorderForm:
|
||||||
"""The form to reorder the journal entries."""
|
"""The form to reorder the journal entries."""
|
||||||
|
|
||||||
def __init__(self, journal_entry_date: date):
|
def __init__(self, date: dt.date):
|
||||||
"""Constructs the form to reorder the journal entries in a day.
|
"""Constructs the form to reorder the journal entries in a day.
|
||||||
|
|
||||||
:param journal_entry_date: The date.
|
:param date: The date.
|
||||||
"""
|
"""
|
||||||
self.date: date = journal_entry_date
|
self.date: dt.date = date
|
||||||
self.is_modified: bool = False
|
self.is_modified: bool = False
|
||||||
|
|
||||||
def save_order(self) -> None:
|
def save_order(self) -> None:
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
"""The views for the journal entry management.
|
"""The views for the journal entry management.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
from datetime import date
|
import datetime as dt
|
||||||
from urllib.parse import parse_qsl, urlencode
|
from urllib.parse import parse_qsl, urlencode
|
||||||
|
|
||||||
import sqlalchemy as sa
|
import sqlalchemy as sa
|
||||||
@ -30,9 +30,9 @@ from accounting.locale import lazy_gettext
|
|||||||
from accounting.models import JournalEntry
|
from accounting.models import JournalEntry
|
||||||
from accounting.utils.cast import s
|
from accounting.utils.cast import s
|
||||||
from accounting.utils.flash_errors import flash_form_errors
|
from accounting.utils.flash_errors import flash_form_errors
|
||||||
|
from accounting.utils.journal_entry_types import JournalEntryType
|
||||||
from accounting.utils.next_uri import inherit_next, or_next
|
from accounting.utils.next_uri import inherit_next, or_next
|
||||||
from accounting.utils.permission import has_permission, can_view, can_edit
|
from accounting.utils.permission import has_permission, can_view, can_edit
|
||||||
from accounting.utils.journal_entry_types import JournalEntryType
|
|
||||||
from accounting.utils.user import get_current_user_pk
|
from accounting.utils.user import get_current_user_pk
|
||||||
from .forms import sort_journal_entries_in, JournalEntryReorderForm
|
from .forms import sort_journal_entries_in, JournalEntryReorderForm
|
||||||
from .template_filters import with_type, to_transfer, format_amount_input, \
|
from .template_filters import with_type, to_transfer, format_amount_input, \
|
||||||
@ -67,7 +67,7 @@ def show_add_journal_entry_form(journal_entry_type: JournalEntryType) -> str:
|
|||||||
form.validate()
|
form.validate()
|
||||||
else:
|
else:
|
||||||
form = journal_entry_op.form()
|
form = journal_entry_op.form()
|
||||||
form.date.data = date.today()
|
form.date.data = dt.date.today()
|
||||||
return journal_entry_op.render_create_template(form)
|
return journal_entry_op.render_create_template(form)
|
||||||
|
|
||||||
|
|
||||||
@ -186,31 +186,31 @@ def delete_journal_entry(journal_entry: JournalEntry) -> redirect:
|
|||||||
return redirect(or_next(__get_default_page_uri()))
|
return redirect(or_next(__get_default_page_uri()))
|
||||||
|
|
||||||
|
|
||||||
@bp.get("dates/<date:journal_entry_date>", endpoint="order")
|
@bp.get("dates/<date:date>", endpoint="order")
|
||||||
@has_permission(can_view)
|
@has_permission(can_view)
|
||||||
def show_journal_entry_order(journal_entry_date: date) -> str:
|
def show_journal_entry_order(date: dt.date) -> str:
|
||||||
"""Shows the order of the journal entries in a same date.
|
"""Shows the order of the journal entries in a same date.
|
||||||
|
|
||||||
:param journal_entry_date: The date.
|
:param date: The date.
|
||||||
:return: The order of the journal entries in the date.
|
:return: The order of the journal entries in the date.
|
||||||
"""
|
"""
|
||||||
journal_entries: list[JournalEntry] = JournalEntry.query \
|
journal_entries: list[JournalEntry] = JournalEntry.query \
|
||||||
.filter(JournalEntry.date == journal_entry_date) \
|
.filter(JournalEntry.date == date) \
|
||||||
.order_by(JournalEntry.no).all()
|
.order_by(JournalEntry.no).all()
|
||||||
return render_template("accounting/journal-entry/order.html",
|
return render_template("accounting/journal-entry/order.html",
|
||||||
date=journal_entry_date, list=journal_entries)
|
date=date, list=journal_entries)
|
||||||
|
|
||||||
|
|
||||||
@bp.post("dates/<date:journal_entry_date>", endpoint="sort")
|
@bp.post("dates/<date:date>", endpoint="sort")
|
||||||
@has_permission(can_edit)
|
@has_permission(can_edit)
|
||||||
def sort_journal_entries(journal_entry_date: date) -> redirect:
|
def sort_journal_entries(date: dt.date) -> redirect:
|
||||||
"""Reorders the journal entries in a date.
|
"""Reorders the journal entries in a date.
|
||||||
|
|
||||||
:param journal_entry_date: The date.
|
:param date: The date.
|
||||||
:return: The redirection to the incoming account or the account list. The
|
:return: The redirection to the incoming account or the account list. The
|
||||||
reordering operation does not fail.
|
reordering operation does not fail.
|
||||||
"""
|
"""
|
||||||
form: JournalEntryReorderForm = JournalEntryReorderForm(journal_entry_date)
|
form: JournalEntryReorderForm = JournalEntryReorderForm(date)
|
||||||
form.save_order()
|
form.save_order()
|
||||||
if not form.is_modified:
|
if not form.is_modified:
|
||||||
flash(s(lazy_gettext("The order was not modified.")), "success")
|
flash(s(lazy_gettext("The order was not modified.")), "success")
|
||||||
|
@ -228,13 +228,13 @@ class Account(db.Model):
|
|||||||
return getattr(self, "__count")
|
return getattr(self, "__count")
|
||||||
|
|
||||||
@count.setter
|
@count.setter
|
||||||
def count(self, count: int) -> None:
|
def count(self, value: int) -> None:
|
||||||
"""Sets the number of items in the account.
|
"""Sets the number of items in the account.
|
||||||
|
|
||||||
:param count: The number of items in the account.
|
:param value: The number of items in the account.
|
||||||
:return: None.
|
:return: None.
|
||||||
"""
|
"""
|
||||||
setattr(self, "__count", count)
|
setattr(self, "__count", value)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def query_values(self) -> list[str]:
|
def query_values(self) -> list[str]:
|
||||||
@ -729,13 +729,13 @@ class JournalEntryLineItem(db.Model):
|
|||||||
return getattr(self, "__debit")
|
return getattr(self, "__debit")
|
||||||
|
|
||||||
@debit.setter
|
@debit.setter
|
||||||
def debit(self, debit: Decimal | None) -> None:
|
def debit(self, value: Decimal | None) -> None:
|
||||||
"""Sets the debit amount.
|
"""Sets the debit amount.
|
||||||
|
|
||||||
:param debit: The debit amount.
|
:param value: The debit amount.
|
||||||
:return: None.
|
:return: None.
|
||||||
"""
|
"""
|
||||||
setattr(self, "__debit", debit)
|
setattr(self, "__debit", value)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def credit(self) -> Decimal | None:
|
def credit(self) -> Decimal | None:
|
||||||
@ -748,13 +748,13 @@ class JournalEntryLineItem(db.Model):
|
|||||||
return getattr(self, "__credit")
|
return getattr(self, "__credit")
|
||||||
|
|
||||||
@credit.setter
|
@credit.setter
|
||||||
def credit(self, credit: Decimal | None) -> None:
|
def credit(self, value: Decimal | None) -> None:
|
||||||
"""Sets the credit amount.
|
"""Sets the credit amount.
|
||||||
|
|
||||||
:param credit: The credit amount.
|
:param value: The credit amount.
|
||||||
:return: None.
|
:return: None.
|
||||||
"""
|
"""
|
||||||
setattr(self, "__credit", credit)
|
setattr(self, "__credit", value)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def net_balance(self) -> Decimal:
|
def net_balance(self) -> Decimal:
|
||||||
@ -769,32 +769,32 @@ class JournalEntryLineItem(db.Model):
|
|||||||
return getattr(self, "__net_balance")
|
return getattr(self, "__net_balance")
|
||||||
|
|
||||||
@net_balance.setter
|
@net_balance.setter
|
||||||
def net_balance(self, net_balance: Decimal) -> None:
|
def net_balance(self, value: Decimal) -> None:
|
||||||
"""Sets the net balance.
|
"""Sets the net balance.
|
||||||
|
|
||||||
:param net_balance: The net balance.
|
:param value: The net balance.
|
||||||
:return: None.
|
:return: None.
|
||||||
"""
|
"""
|
||||||
setattr(self, "__net_balance", net_balance)
|
setattr(self, "__net_balance", value)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def balance(self) -> Decimal:
|
def balance(self) -> Decimal:
|
||||||
"""Returns the net balance.
|
"""Returns the balance.
|
||||||
|
|
||||||
:return: The net balance.
|
:return: The balance.
|
||||||
"""
|
"""
|
||||||
if not hasattr(self, "__balance"):
|
if not hasattr(self, "__balance"):
|
||||||
setattr(self, "__balance", Decimal("0"))
|
setattr(self, "__balance", Decimal("0"))
|
||||||
return getattr(self, "__balance")
|
return getattr(self, "__balance")
|
||||||
|
|
||||||
@balance.setter
|
@balance.setter
|
||||||
def balance(self, balance: Decimal) -> None:
|
def balance(self, value: Decimal) -> None:
|
||||||
"""Sets the net balance.
|
"""Sets the balance.
|
||||||
|
|
||||||
:param balance: The net balance.
|
:param value: The balance.
|
||||||
:return: None.
|
:return: None.
|
||||||
"""
|
"""
|
||||||
setattr(self, "__balance", balance)
|
setattr(self, "__balance", value)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def offsets(self) -> list[t.Self]:
|
def offsets(self) -> list[t.Self]:
|
||||||
@ -822,14 +822,13 @@ class JournalEntryLineItem(db.Model):
|
|||||||
return getattr(self, "__is_offset")
|
return getattr(self, "__is_offset")
|
||||||
|
|
||||||
@is_offset.setter
|
@is_offset.setter
|
||||||
def is_offset(self, is_offset: bool) -> None:
|
def is_offset(self, value: bool) -> None:
|
||||||
"""Sets whether the line item is an offset.
|
"""Sets whether the line item is an offset.
|
||||||
|
|
||||||
:param is_offset: True if the line item is an offset, or False
|
:param value: True if the line item is an offset, or False otherwise.
|
||||||
otherwise.
|
|
||||||
:return: None.
|
:return: None.
|
||||||
"""
|
"""
|
||||||
setattr(self, "__is_offset", is_offset)
|
setattr(self, "__is_offset", value)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def match(self) -> t.Self | None:
|
def match(self) -> t.Self | None:
|
||||||
@ -842,13 +841,13 @@ class JournalEntryLineItem(db.Model):
|
|||||||
return getattr(self, "__match")
|
return getattr(self, "__match")
|
||||||
|
|
||||||
@match.setter
|
@match.setter
|
||||||
def match(self, match: t.Self) -> None:
|
def match(self, value: t.Self) -> None:
|
||||||
"""Sets the match of the line item.
|
"""Sets the match of the line item.
|
||||||
|
|
||||||
:param match: The matcho of the line item.
|
:param value: The matcho of the line item.
|
||||||
:return: None.
|
:return: None.
|
||||||
"""
|
"""
|
||||||
setattr(self, "__match", match)
|
setattr(self, "__match", value)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def query_values(self) -> list[str]:
|
def query_values(self) -> list[str]:
|
||||||
|
@ -20,8 +20,8 @@ This file is largely taken from the NanoParma ERP project, first written in
|
|||||||
2021/9/16 by imacat (imacat@nanoparma.com).
|
2021/9/16 by imacat (imacat@nanoparma.com).
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
import datetime as dt
|
||||||
import typing as t
|
import typing as t
|
||||||
from datetime import date
|
|
||||||
|
|
||||||
from accounting.models import JournalEntry
|
from accounting.models import JournalEntry
|
||||||
from .period import Period
|
from .period import Period
|
||||||
@ -63,10 +63,10 @@ class PeriodChooser:
|
|||||||
|
|
||||||
first: JournalEntry | None \
|
first: JournalEntry | None \
|
||||||
= JournalEntry.query.order_by(JournalEntry.date).first()
|
= JournalEntry.query.order_by(JournalEntry.date).first()
|
||||||
start: date | None = None if first is None else first.date
|
start: dt.date | None = None if first is None else first.date
|
||||||
|
|
||||||
# Attributes
|
# Attributes
|
||||||
self.data_start: date | None = start
|
self.data_start: dt.date | None = start
|
||||||
"""The start of the data."""
|
"""The start of the data."""
|
||||||
self.has_data: bool = start is not None
|
self.has_data: bool = start is not None
|
||||||
"""Whether there is any data."""
|
"""Whether there is any data."""
|
||||||
@ -80,8 +80,8 @@ class PeriodChooser:
|
|||||||
"""The available years."""
|
"""The available years."""
|
||||||
|
|
||||||
if self.has_data:
|
if self.has_data:
|
||||||
today: date = date.today()
|
today: dt.date = dt.date.today()
|
||||||
self.has_last_month = start < date(today.year, today.month, 1)
|
self.has_last_month = start < dt.date(today.year, today.month, 1)
|
||||||
self.has_last_year = start.year < today.year
|
self.has_last_year = start.year < today.year
|
||||||
self.has_yesterday = start < today
|
self.has_yesterday = start < today
|
||||||
if start.year < today.year - 1:
|
if start.year < today.year - 1:
|
||||||
|
@ -17,12 +17,12 @@
|
|||||||
"""The period description composer.
|
"""The period description composer.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
from datetime import date, timedelta
|
import datetime as dt
|
||||||
|
|
||||||
from accounting.locale import gettext
|
from accounting.locale import gettext
|
||||||
|
|
||||||
|
|
||||||
def get_desc(start: date | None, end: date | None) -> str:
|
def get_desc(start: dt.date | None, end: dt.date | None) -> str:
|
||||||
"""Returns the period description.
|
"""Returns the period description.
|
||||||
|
|
||||||
:param start: The start of the period.
|
:param start: The start of the period.
|
||||||
@ -46,7 +46,7 @@ def get_desc(start: date | None, end: date | None) -> str:
|
|||||||
return __get_day_desc(start, end)
|
return __get_day_desc(start, end)
|
||||||
|
|
||||||
|
|
||||||
def __get_since_desc(start: date) -> str:
|
def __get_since_desc(start: dt.date) -> str:
|
||||||
"""Returns the description without the end day.
|
"""Returns the description without the end day.
|
||||||
|
|
||||||
:param start: The start of the period.
|
:param start: The start of the period.
|
||||||
@ -67,7 +67,7 @@ def __get_since_desc(start: date) -> str:
|
|||||||
return gettext("since %(start)s", start=get_start_desc())
|
return gettext("since %(start)s", start=get_start_desc())
|
||||||
|
|
||||||
|
|
||||||
def __get_until_desc(end: date) -> str:
|
def __get_until_desc(end: dt.date) -> str:
|
||||||
"""Returns the description without the start day.
|
"""Returns the description without the start day.
|
||||||
|
|
||||||
:param end: The end of the period.
|
:param end: The end of the period.
|
||||||
@ -81,14 +81,14 @@ def __get_until_desc(end: date) -> str:
|
|||||||
"""
|
"""
|
||||||
if end.month == 12 and end.day == 31:
|
if end.month == 12 and end.day == 31:
|
||||||
return str(end.year)
|
return str(end.year)
|
||||||
if (end + timedelta(days=1)).day == 1:
|
if (end + dt.timedelta(days=1)).day == 1:
|
||||||
return __format_month(end)
|
return __format_month(end)
|
||||||
return __format_day(end)
|
return __format_day(end)
|
||||||
|
|
||||||
return gettext("until %(end)s", end=get_end_desc())
|
return gettext("until %(end)s", end=get_end_desc())
|
||||||
|
|
||||||
|
|
||||||
def __get_year_desc(start: date, end: date) -> str:
|
def __get_year_desc(start: dt.date, end: dt.date) -> str:
|
||||||
"""Returns the description as a year range.
|
"""Returns the description as a year range.
|
||||||
|
|
||||||
:param start: The start of the period.
|
:param start: The start of the period.
|
||||||
@ -105,7 +105,7 @@ def __get_year_desc(start: date, end: date) -> str:
|
|||||||
return __get_from_to_desc(start_text, str(end.year))
|
return __get_from_to_desc(start_text, str(end.year))
|
||||||
|
|
||||||
|
|
||||||
def __get_month_desc(start: date, end: date) -> str:
|
def __get_month_desc(start: dt.date, end: dt.date) -> str:
|
||||||
"""Returns the description as a month range.
|
"""Returns the description as a month range.
|
||||||
|
|
||||||
:param start: The start of the period.
|
:param start: The start of the period.
|
||||||
@ -113,7 +113,7 @@ def __get_month_desc(start: date, end: date) -> str:
|
|||||||
:return: The description as a month range.
|
:return: The description as a month range.
|
||||||
:raise ValueError: The period is not a month range.
|
:raise ValueError: The period is not a month range.
|
||||||
"""
|
"""
|
||||||
if start.day != 1 or (end + timedelta(days=1)).day != 1:
|
if start.day != 1 or (end + dt.timedelta(days=1)).day != 1:
|
||||||
raise ValueError
|
raise ValueError
|
||||||
start_text: str = __format_month(start)
|
start_text: str = __format_month(start)
|
||||||
if start.year == end.year and start.month == end.month:
|
if start.year == end.year and start.month == end.month:
|
||||||
@ -123,7 +123,7 @@ def __get_month_desc(start: date, end: date) -> str:
|
|||||||
return __get_from_to_desc(start_text, __format_month(end))
|
return __get_from_to_desc(start_text, __format_month(end))
|
||||||
|
|
||||||
|
|
||||||
def __get_day_desc(start: date, end: date) -> str:
|
def __get_day_desc(start: dt.date, end: dt.date) -> str:
|
||||||
"""Returns the description as a day range.
|
"""Returns the description as a day range.
|
||||||
|
|
||||||
:param start: The start of the period.
|
:param start: The start of the period.
|
||||||
@ -142,7 +142,7 @@ def __get_day_desc(start: date, end: date) -> str:
|
|||||||
return __get_from_to_desc(start_text, __format_day(end))
|
return __get_from_to_desc(start_text, __format_day(end))
|
||||||
|
|
||||||
|
|
||||||
def __format_month(month: date) -> str:
|
def __format_month(month: dt.date) -> str:
|
||||||
"""Formats a month.
|
"""Formats a month.
|
||||||
|
|
||||||
:param month: The month.
|
:param month: The month.
|
||||||
@ -151,7 +151,7 @@ def __format_month(month: date) -> str:
|
|||||||
return f"{month.year}/{month.month}"
|
return f"{month.year}/{month.month}"
|
||||||
|
|
||||||
|
|
||||||
def __format_day(day: date) -> str:
|
def __format_day(day: dt.date) -> str:
|
||||||
"""Formats a day.
|
"""Formats a day.
|
||||||
|
|
||||||
:param day: The day.
|
:param day: The day.
|
||||||
|
@ -18,14 +18,14 @@
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
import calendar
|
import calendar
|
||||||
from datetime import date
|
import datetime as dt
|
||||||
|
|
||||||
|
|
||||||
def month_end(day: date) -> date:
|
def month_end(day: dt.date) -> dt.date:
|
||||||
"""Returns the end day of month for a date.
|
"""Returns the end day of month for a date.
|
||||||
|
|
||||||
:param day: The date.
|
:param day: The date.
|
||||||
:return: The end day of the month of that day.
|
:return: The end day of the month of that day.
|
||||||
"""
|
"""
|
||||||
last_day: int = calendar.monthrange(day.year, day.month)[1]
|
last_day: int = calendar.monthrange(day.year, day.month)[1]
|
||||||
return date(day.year, day.month, last_day)
|
return dt.date(day.year, day.month, last_day)
|
||||||
|
@ -18,9 +18,9 @@
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
import calendar
|
import calendar
|
||||||
|
import datetime as dt
|
||||||
import re
|
import re
|
||||||
import typing as t
|
import typing as t
|
||||||
from datetime import date
|
|
||||||
|
|
||||||
from .period import Period
|
from .period import Period
|
||||||
from .shortcuts import ThisMonth, LastMonth, SinceLastMonth, ThisYear, \
|
from .shortcuts import ThisMonth, LastMonth, SinceLastMonth, ThisYear, \
|
||||||
@ -57,7 +57,7 @@ def get_period(spec: str | None = None) -> Period:
|
|||||||
return Period(start, end)
|
return Period(start, end)
|
||||||
|
|
||||||
|
|
||||||
def __parse_spec(text: str) -> tuple[date | None, date | None]:
|
def __parse_spec(text: str) -> tuple[dt.date | None, dt.date | None]:
|
||||||
"""Parses the period specification.
|
"""Parses the period specification.
|
||||||
|
|
||||||
:param text: The period specification.
|
:param text: The period specification.
|
||||||
@ -84,7 +84,7 @@ def __parse_spec(text: str) -> tuple[date | None, date | None]:
|
|||||||
raise ValueError
|
raise ValueError
|
||||||
|
|
||||||
|
|
||||||
def __get_start(year: str, month: str | None, day: str | None) -> date:
|
def __get_start(year: str, month: str | None, day: str | None) -> dt.date:
|
||||||
"""Returns the start of the period from the date representation.
|
"""Returns the start of the period from the date representation.
|
||||||
|
|
||||||
:param year: The year.
|
:param year: The year.
|
||||||
@ -94,13 +94,13 @@ def __get_start(year: str, month: str | None, day: str | None) -> date:
|
|||||||
:raise ValueError: When the date is invalid.
|
:raise ValueError: When the date is invalid.
|
||||||
"""
|
"""
|
||||||
if day is not None:
|
if day is not None:
|
||||||
return date(int(year), int(month), int(day))
|
return dt.date(int(year), int(month), int(day))
|
||||||
if month is not None:
|
if month is not None:
|
||||||
return date(int(year), int(month), 1)
|
return dt.date(int(year), int(month), 1)
|
||||||
return date(int(year), 1, 1)
|
return dt.date(int(year), 1, 1)
|
||||||
|
|
||||||
|
|
||||||
def __get_end(year: str, month: str | None, day: str | None) -> date:
|
def __get_end(year: str, month: str | None, day: str | None) -> dt.date:
|
||||||
"""Returns the end of the period from the date representation.
|
"""Returns the end of the period from the date representation.
|
||||||
|
|
||||||
:param year: The year.
|
:param year: The year.
|
||||||
@ -110,10 +110,10 @@ def __get_end(year: str, month: str | None, day: str | None) -> date:
|
|||||||
:raise ValueError: When the date is invalid.
|
:raise ValueError: When the date is invalid.
|
||||||
"""
|
"""
|
||||||
if day is not None:
|
if day is not None:
|
||||||
return date(int(year), int(month), int(day))
|
return dt.date(int(year), int(month), int(day))
|
||||||
if month is not None:
|
if month is not None:
|
||||||
year_n: int = int(year)
|
year_n: int = int(year)
|
||||||
month_n: int = int(month)
|
month_n: int = int(month)
|
||||||
day_n: int = calendar.monthrange(year_n, month_n)[1]
|
day_n: int = calendar.monthrange(year_n, month_n)[1]
|
||||||
return date(year_n, month_n, day_n)
|
return dt.date(year_n, month_n, day_n)
|
||||||
return date(int(year), 12, 31)
|
return dt.date(int(year), 12, 31)
|
||||||
|
@ -20,8 +20,8 @@ This file is largely taken from the NanoParma ERP project, first written in
|
|||||||
2021/9/16 by imacat (imacat@nanoparma.com).
|
2021/9/16 by imacat (imacat@nanoparma.com).
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
import datetime as dt
|
||||||
import typing as t
|
import typing as t
|
||||||
from datetime import date, timedelta
|
|
||||||
|
|
||||||
from .description import get_desc
|
from .description import get_desc
|
||||||
from .month_end import month_end
|
from .month_end import month_end
|
||||||
@ -31,15 +31,15 @@ from .specification import get_spec
|
|||||||
class Period:
|
class Period:
|
||||||
"""A date period."""
|
"""A date period."""
|
||||||
|
|
||||||
def __init__(self, start: date | None, end: date | None):
|
def __init__(self, start: dt.date | None, end: dt.date | None):
|
||||||
"""Constructs a new date period.
|
"""Constructs a new date period.
|
||||||
|
|
||||||
:param start: The start date, or None from the very beginning.
|
:param start: The start date, or None from the very beginning.
|
||||||
:param end: The end date, or None till no end.
|
:param end: The end date, or None till no end.
|
||||||
"""
|
"""
|
||||||
self.start: date | None = start
|
self.start: dt.date | None = start
|
||||||
"""The start of the period."""
|
"""The start of the period."""
|
||||||
self.end: date | None = end
|
self.end: dt.date | None = end
|
||||||
"""The end of the period."""
|
"""The end of the period."""
|
||||||
self.is_default: bool = False
|
self.is_default: bool = False
|
||||||
"""Whether the is the default period."""
|
"""Whether the is the default period."""
|
||||||
@ -95,8 +95,8 @@ class Period:
|
|||||||
self.is_a_month = self.start.day == 1 \
|
self.is_a_month = self.start.day == 1 \
|
||||||
and self.end == month_end(self.start)
|
and self.end == month_end(self.start)
|
||||||
self.is_type_month = self.is_a_month
|
self.is_type_month = self.is_a_month
|
||||||
self.is_a_year = self.start == date(self.start.year, 1, 1) \
|
self.is_a_year = self.start == dt.date(self.start.year, 1, 1) \
|
||||||
and self.end == date(self.start.year, 12, 31)
|
and self.end == dt.date(self.start.year, 12, 31)
|
||||||
self.is_a_day = self.start == self.end
|
self.is_a_day = self.start == self.end
|
||||||
|
|
||||||
def is_year(self, year: int) -> bool:
|
def is_year(self, year: int) -> bool:
|
||||||
@ -126,4 +126,4 @@ class Period:
|
|||||||
"""
|
"""
|
||||||
if self.start is None:
|
if self.start is None:
|
||||||
return None
|
return None
|
||||||
return Period(None, self.start - timedelta(days=1))
|
return Period(None, self.start - dt.timedelta(days=1))
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
"""The named shortcut periods.
|
"""The named shortcut periods.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
from datetime import date, timedelta
|
import datetime as dt
|
||||||
|
|
||||||
from accounting.locale import gettext
|
from accounting.locale import gettext
|
||||||
from .month_end import month_end
|
from .month_end import month_end
|
||||||
@ -27,8 +27,8 @@ from .period import Period
|
|||||||
class ThisMonth(Period):
|
class ThisMonth(Period):
|
||||||
"""The period of this month."""
|
"""The period of this month."""
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
today: date = date.today()
|
today: dt.date = dt.date.today()
|
||||||
this_month_start: date = date(today.year, today.month, 1)
|
this_month_start: dt.date = dt.date(today.year, today.month, 1)
|
||||||
super().__init__(this_month_start, month_end(today))
|
super().__init__(this_month_start, month_end(today))
|
||||||
self.is_default = True
|
self.is_default = True
|
||||||
self.is_this_month = True
|
self.is_this_month = True
|
||||||
@ -43,13 +43,13 @@ class ThisMonth(Period):
|
|||||||
class LastMonth(Period):
|
class LastMonth(Period):
|
||||||
"""The period of this month."""
|
"""The period of this month."""
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
today: date = date.today()
|
today: dt.date = dt.date.today()
|
||||||
year: int = today.year
|
year: int = today.year
|
||||||
month: int = today.month - 1
|
month: int = today.month - 1
|
||||||
if month < 1:
|
if month < 1:
|
||||||
year = year - 1
|
year = year - 1
|
||||||
month = 12
|
month = 12
|
||||||
start: date = date(year, month, 1)
|
start: dt.date = dt.date(year, month, 1)
|
||||||
super().__init__(start, month_end(start))
|
super().__init__(start, month_end(start))
|
||||||
self.is_last_month = True
|
self.is_last_month = True
|
||||||
|
|
||||||
@ -63,13 +63,13 @@ class LastMonth(Period):
|
|||||||
class SinceLastMonth(Period):
|
class SinceLastMonth(Period):
|
||||||
"""The period of this month."""
|
"""The period of this month."""
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
today: date = date.today()
|
today: dt.date = dt.date.today()
|
||||||
year: int = today.year
|
year: int = today.year
|
||||||
month: int = today.month - 1
|
month: int = today.month - 1
|
||||||
if month < 1:
|
if month < 1:
|
||||||
year = year - 1
|
year = year - 1
|
||||||
month = 12
|
month = 12
|
||||||
start: date = date(year, month, 1)
|
start: dt.date = dt.date(year, month, 1)
|
||||||
super().__init__(start, None)
|
super().__init__(start, None)
|
||||||
self.is_since_last_month = True
|
self.is_since_last_month = True
|
||||||
|
|
||||||
@ -82,9 +82,9 @@ class SinceLastMonth(Period):
|
|||||||
class ThisYear(Period):
|
class ThisYear(Period):
|
||||||
"""The period of this year."""
|
"""The period of this year."""
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
year: int = date.today().year
|
year: int = dt.date.today().year
|
||||||
start: date = date(year, 1, 1)
|
start: dt.date = dt.date(year, 1, 1)
|
||||||
end: date = date(year, 12, 31)
|
end: dt.date = dt.date(year, 12, 31)
|
||||||
super().__init__(start, end)
|
super().__init__(start, end)
|
||||||
self.is_this_year = True
|
self.is_this_year = True
|
||||||
|
|
||||||
@ -97,9 +97,9 @@ class ThisYear(Period):
|
|||||||
class LastYear(Period):
|
class LastYear(Period):
|
||||||
"""The period of last year."""
|
"""The period of last year."""
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
year: int = date.today().year
|
year: int = dt.date.today().year
|
||||||
start: date = date(year - 1, 1, 1)
|
start: dt.date = dt.date(year - 1, 1, 1)
|
||||||
end: date = date(year - 1, 12, 31)
|
end: dt.date = dt.date(year - 1, 12, 31)
|
||||||
super().__init__(start, end)
|
super().__init__(start, end)
|
||||||
self.is_last_year = True
|
self.is_last_year = True
|
||||||
|
|
||||||
@ -112,7 +112,7 @@ class LastYear(Period):
|
|||||||
class Today(Period):
|
class Today(Period):
|
||||||
"""The period of today."""
|
"""The period of today."""
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
today: date = date.today()
|
today: dt.date = dt.date.today()
|
||||||
super().__init__(today, today)
|
super().__init__(today, today)
|
||||||
self.is_today = True
|
self.is_today = True
|
||||||
|
|
||||||
@ -125,7 +125,7 @@ class Today(Period):
|
|||||||
class Yesterday(Period):
|
class Yesterday(Period):
|
||||||
"""The period of yesterday."""
|
"""The period of yesterday."""
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
yesterday: date = date.today() - timedelta(days=1)
|
yesterday: dt.date = dt.date.today() - dt.timedelta(days=1)
|
||||||
super().__init__(yesterday, yesterday)
|
super().__init__(yesterday, yesterday)
|
||||||
self.is_yesterday = True
|
self.is_yesterday = True
|
||||||
|
|
||||||
@ -163,6 +163,6 @@ class YearPeriod(Period):
|
|||||||
|
|
||||||
:param year: The year.
|
:param year: The year.
|
||||||
"""
|
"""
|
||||||
start: date = date(year, 1, 1)
|
start: dt.date = dt.date(year, 1, 1)
|
||||||
end: date = date(year, 12, 31)
|
end: dt.date = dt.date(year, 12, 31)
|
||||||
super().__init__(start, end)
|
super().__init__(start, end)
|
||||||
|
@ -17,10 +17,10 @@
|
|||||||
"""The period specification composer.
|
"""The period specification composer.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
from datetime import date, timedelta
|
import datetime as dt
|
||||||
|
|
||||||
|
|
||||||
def get_spec(start: date | None, end: date | None) -> str:
|
def get_spec(start: dt.date | None, end: dt.date | None) -> str:
|
||||||
"""Returns the period specification.
|
"""Returns the period specification.
|
||||||
|
|
||||||
:param start: The start of the period.
|
:param start: The start of the period.
|
||||||
@ -44,7 +44,7 @@ def get_spec(start: date | None, end: date | None) -> str:
|
|||||||
return __get_day_spec(start, end)
|
return __get_day_spec(start, end)
|
||||||
|
|
||||||
|
|
||||||
def __get_since_spec(start: date) -> str:
|
def __get_since_spec(start: dt.date) -> str:
|
||||||
"""Returns the period specification without the end day.
|
"""Returns the period specification without the end day.
|
||||||
|
|
||||||
:param start: The start of the period.
|
:param start: The start of the period.
|
||||||
@ -57,7 +57,7 @@ def __get_since_spec(start: date) -> str:
|
|||||||
return start.strftime("%Y-%m-%d-")
|
return start.strftime("%Y-%m-%d-")
|
||||||
|
|
||||||
|
|
||||||
def __get_until_spec(end: date) -> str:
|
def __get_until_spec(end: dt.date) -> str:
|
||||||
"""Returns the period specification without the start day.
|
"""Returns the period specification without the start day.
|
||||||
|
|
||||||
:param end: The end of the period.
|
:param end: The end of the period.
|
||||||
@ -65,12 +65,12 @@ def __get_until_spec(end: date) -> str:
|
|||||||
"""
|
"""
|
||||||
if end.month == 12 and end.day == 31:
|
if end.month == 12 and end.day == 31:
|
||||||
return end.strftime("-%Y")
|
return end.strftime("-%Y")
|
||||||
if (end + timedelta(days=1)).day == 1:
|
if (end + dt.timedelta(days=1)).day == 1:
|
||||||
return end.strftime("-%Y-%m")
|
return end.strftime("-%Y-%m")
|
||||||
return end.strftime("-%Y-%m-%d")
|
return end.strftime("-%Y-%m-%d")
|
||||||
|
|
||||||
|
|
||||||
def __get_year_spec(start: date, end: date) -> str:
|
def __get_year_spec(start: dt.date, end: dt.date) -> str:
|
||||||
"""Returns the period specification as a year range.
|
"""Returns the period specification as a year range.
|
||||||
|
|
||||||
:param start: The start of the period.
|
:param start: The start of the period.
|
||||||
@ -88,7 +88,7 @@ def __get_year_spec(start: date, end: date) -> str:
|
|||||||
return f"{start_spec}-{end_spec}"
|
return f"{start_spec}-{end_spec}"
|
||||||
|
|
||||||
|
|
||||||
def __get_month_spec(start: date, end: date) -> str:
|
def __get_month_spec(start: dt.date, end: dt.date) -> str:
|
||||||
"""Returns the period specification as a month range.
|
"""Returns the period specification as a month range.
|
||||||
|
|
||||||
:param start: The start of the period.
|
:param start: The start of the period.
|
||||||
@ -96,7 +96,7 @@ def __get_month_spec(start: date, end: date) -> str:
|
|||||||
:return: The period specification as a month range.
|
:return: The period specification as a month range.
|
||||||
:raise ValueError: The period is not a month range.
|
:raise ValueError: The period is not a month range.
|
||||||
"""
|
"""
|
||||||
if start.day != 1 or (end + timedelta(days=1)).day != 1:
|
if start.day != 1 or (end + dt.timedelta(days=1)).day != 1:
|
||||||
raise ValueError
|
raise ValueError
|
||||||
start_spec: str = start.strftime("%Y-%m")
|
start_spec: str = start.strftime("%Y-%m")
|
||||||
if start.year == end.year and start.month == end.month:
|
if start.year == end.year and start.month == end.month:
|
||||||
@ -105,7 +105,7 @@ def __get_month_spec(start: date, end: date) -> str:
|
|||||||
return f"{start_spec}-{end_spec}"
|
return f"{start_spec}-{end_spec}"
|
||||||
|
|
||||||
|
|
||||||
def __get_day_spec(start: date, end: date) -> str:
|
def __get_day_spec(start: dt.date, end: dt.date) -> str:
|
||||||
"""Returns the period specification as a day range.
|
"""Returns the period specification as a day range.
|
||||||
|
|
||||||
:param start: The start of the period.
|
:param start: The start of the period.
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
"""The income and expenses log.
|
"""The income and expenses log.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
from datetime import date
|
import datetime as dt
|
||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
|
|
||||||
import sqlalchemy as sa
|
import sqlalchemy as sa
|
||||||
@ -53,7 +53,7 @@ class ReportLineItem:
|
|||||||
"""Whether this is the brought-forward line item."""
|
"""Whether this is the brought-forward line item."""
|
||||||
self.is_total: bool = False
|
self.is_total: bool = False
|
||||||
"""Whether this is the total line item."""
|
"""Whether this is the total line item."""
|
||||||
self.date: date | None = None
|
self.date: dt.date | None = None
|
||||||
"""The date."""
|
"""The date."""
|
||||||
self.account: Account | None = None
|
self.account: Account | None = None
|
||||||
"""The account."""
|
"""The account."""
|
||||||
@ -213,7 +213,7 @@ class LineItemCollector:
|
|||||||
class CSVRow(BaseCSVRow):
|
class CSVRow(BaseCSVRow):
|
||||||
"""A row in the CSV."""
|
"""A row in the CSV."""
|
||||||
|
|
||||||
def __init__(self, journal_entry_date: date | str | None,
|
def __init__(self, date: dt.date | str | None,
|
||||||
account: str | None,
|
account: str | None,
|
||||||
description: str | None,
|
description: str | None,
|
||||||
income: str | Decimal | None,
|
income: str | Decimal | None,
|
||||||
@ -222,7 +222,7 @@ class CSVRow(BaseCSVRow):
|
|||||||
note: str | None):
|
note: str | None):
|
||||||
"""Constructs a row in the CSV.
|
"""Constructs a row in the CSV.
|
||||||
|
|
||||||
:param journal_entry_date: The journal entry date.
|
:param date: The journal entry date.
|
||||||
:param account: The account.
|
:param account: The account.
|
||||||
:param description: The description.
|
:param description: The description.
|
||||||
:param income: The income.
|
:param income: The income.
|
||||||
@ -230,7 +230,7 @@ class CSVRow(BaseCSVRow):
|
|||||||
:param balance: The balance.
|
:param balance: The balance.
|
||||||
:param note: The note.
|
:param note: The note.
|
||||||
"""
|
"""
|
||||||
self.date: date | str | None = journal_entry_date
|
self.date: dt.date | str | None = date
|
||||||
"""The date."""
|
"""The date."""
|
||||||
self.account: str | None = account
|
self.account: str | None = account
|
||||||
"""The account."""
|
"""The account."""
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
"""The journal.
|
"""The journal.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
from datetime import date
|
import datetime as dt
|
||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
|
|
||||||
import sqlalchemy as sa
|
import sqlalchemy as sa
|
||||||
@ -67,7 +67,7 @@ class ReportLineItem:
|
|||||||
class CSVRow(BaseCSVRow):
|
class CSVRow(BaseCSVRow):
|
||||||
"""A row in the CSV."""
|
"""A row in the CSV."""
|
||||||
|
|
||||||
def __init__(self, journal_entry_date: str | date,
|
def __init__(self, journal_entry_date: str | dt.date,
|
||||||
currency: str,
|
currency: str,
|
||||||
account: str,
|
account: str,
|
||||||
description: str | None,
|
description: str | None,
|
||||||
@ -84,7 +84,7 @@ class CSVRow(BaseCSVRow):
|
|||||||
:param credit: The credit amount.
|
:param credit: The credit amount.
|
||||||
:param note: The note.
|
:param note: The note.
|
||||||
"""
|
"""
|
||||||
self.date: str | date = journal_entry_date
|
self.date: str | dt.date = journal_entry_date
|
||||||
"""The date."""
|
"""The date."""
|
||||||
self.currency: str = currency
|
self.currency: str = currency
|
||||||
"""The currency."""
|
"""The currency."""
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
"""The ledger.
|
"""The ledger.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
from datetime import date
|
import datetime as dt
|
||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
|
|
||||||
import sqlalchemy as sa
|
import sqlalchemy as sa
|
||||||
@ -52,7 +52,7 @@ class ReportLineItem:
|
|||||||
"""Whether this is the brought-forward line item."""
|
"""Whether this is the brought-forward line item."""
|
||||||
self.is_total: bool = False
|
self.is_total: bool = False
|
||||||
"""Whether this is the total line item."""
|
"""Whether this is the total line item."""
|
||||||
self.date: date | None = None
|
self.date: dt.date | None = None
|
||||||
"""The date."""
|
"""The date."""
|
||||||
self.description: str | None = None
|
self.description: str | None = None
|
||||||
"""The description."""
|
"""The description."""
|
||||||
@ -196,7 +196,7 @@ class LineItemCollector:
|
|||||||
class CSVRow(BaseCSVRow):
|
class CSVRow(BaseCSVRow):
|
||||||
"""A row in the CSV."""
|
"""A row in the CSV."""
|
||||||
|
|
||||||
def __init__(self, journal_entry_date: date | str | None,
|
def __init__(self, date: dt.date | str | None,
|
||||||
description: str | None,
|
description: str | None,
|
||||||
debit: str | Decimal | None,
|
debit: str | Decimal | None,
|
||||||
credit: str | Decimal | None,
|
credit: str | Decimal | None,
|
||||||
@ -204,14 +204,14 @@ class CSVRow(BaseCSVRow):
|
|||||||
note: str | None):
|
note: str | None):
|
||||||
"""Constructs a row in the CSV.
|
"""Constructs a row in the CSV.
|
||||||
|
|
||||||
:param journal_entry_date: The journal entry date.
|
:param date: The journal entry date.
|
||||||
:param description: The description.
|
:param description: The description.
|
||||||
:param debit: The debit amount.
|
:param debit: The debit amount.
|
||||||
:param credit: The credit amount.
|
:param credit: The credit amount.
|
||||||
:param balance: The balance.
|
:param balance: The balance.
|
||||||
:param note: The note.
|
:param note: The note.
|
||||||
"""
|
"""
|
||||||
self.date: date | str | None = journal_entry_date
|
self.date: dt.date | str | None = date
|
||||||
"""The date."""
|
"""The date."""
|
||||||
self.description: str | None = description
|
self.description: str | None = description
|
||||||
"""The description."""
|
"""The description."""
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
"""The search.
|
"""The search.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
from datetime import datetime
|
import datetime as dt
|
||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
|
|
||||||
import sqlalchemy as sa
|
import sqlalchemy as sa
|
||||||
@ -124,40 +124,33 @@ class LineItemCollector:
|
|||||||
"""
|
"""
|
||||||
conditions: list[sa.BinaryExpression] \
|
conditions: list[sa.BinaryExpression] \
|
||||||
= [JournalEntry.note.icontains(k)]
|
= [JournalEntry.note.icontains(k)]
|
||||||
journal_entry_date: datetime
|
date: dt.datetime
|
||||||
try:
|
try:
|
||||||
journal_entry_date = datetime.strptime(k, "%Y")
|
date = dt.datetime.strptime(k, "%Y")
|
||||||
conditions.append(sa.extract("year", JournalEntry.date)
|
conditions.append(
|
||||||
== journal_entry_date.year)
|
sa.extract("year", JournalEntry.date) == date.year)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
pass
|
pass
|
||||||
try:
|
try:
|
||||||
journal_entry_date = datetime.strptime(k, "%Y/%m")
|
date = dt.datetime.strptime(k, "%Y/%m")
|
||||||
conditions.append(sa.and_(
|
conditions.append(sa.and_(
|
||||||
sa.extract("year", JournalEntry.date)
|
sa.extract("year", JournalEntry.date) == date.year,
|
||||||
== journal_entry_date.year,
|
sa.extract("month", JournalEntry.date) == date.month))
|
||||||
sa.extract("month", JournalEntry.date)
|
|
||||||
== journal_entry_date.month))
|
|
||||||
except ValueError:
|
except ValueError:
|
||||||
pass
|
pass
|
||||||
try:
|
try:
|
||||||
journal_entry_date = datetime.strptime(f"2000/{k}", "%Y/%m/%d")
|
date = dt.datetime.strptime(f"2000/{k}", "%Y/%m/%d")
|
||||||
conditions.append(sa.and_(
|
conditions.append(sa.and_(
|
||||||
sa.extract("month", JournalEntry.date)
|
sa.extract("month", JournalEntry.date) == date.month,
|
||||||
== journal_entry_date.month,
|
sa.extract("day", JournalEntry.date) == date.day))
|
||||||
sa.extract("day", JournalEntry.date)
|
|
||||||
== journal_entry_date.day))
|
|
||||||
except ValueError:
|
except ValueError:
|
||||||
pass
|
pass
|
||||||
try:
|
try:
|
||||||
journal_entry_date = datetime.strptime(k, "%Y/%m/%d")
|
date = dt.datetime.strptime(k, "%Y/%m/%d")
|
||||||
conditions.append(sa.and_(
|
conditions.append(sa.and_(
|
||||||
sa.extract("year", JournalEntry.date)
|
sa.extract("year", JournalEntry.date) == date.year,
|
||||||
== journal_entry_date.year,
|
sa.extract("month", JournalEntry.date) == date.month,
|
||||||
sa.extract("month", JournalEntry.date)
|
sa.extract("day", JournalEntry.date) == date.day))
|
||||||
== journal_entry_date.month,
|
|
||||||
sa.extract("day", JournalEntry.date)
|
|
||||||
== journal_entry_date.day))
|
|
||||||
except ValueError:
|
except ValueError:
|
||||||
pass
|
pass
|
||||||
return sa.select(JournalEntry.id).filter(sa.or_(*conditions))
|
return sa.select(JournalEntry.id).filter(sa.or_(*conditions))
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
"""The unapplied original line items.
|
"""The unapplied original line items.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
from datetime import date
|
import datetime as dt
|
||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
|
|
||||||
from flask import render_template, Response
|
from flask import render_template, Response
|
||||||
@ -41,7 +41,7 @@ from accounting.utils.pagination import Pagination
|
|||||||
class CSVRow(BaseCSVRow):
|
class CSVRow(BaseCSVRow):
|
||||||
"""A row in the CSV."""
|
"""A row in the CSV."""
|
||||||
|
|
||||||
def __init__(self, journal_entry_date: str | date, currency: str,
|
def __init__(self, journal_entry_date: str | dt.date, currency: str,
|
||||||
description: str | None, amount: str | Decimal,
|
description: str | None, amount: str | Decimal,
|
||||||
net_balance: str | Decimal):
|
net_balance: str | Decimal):
|
||||||
"""Constructs a row in the CSV.
|
"""Constructs a row in the CSV.
|
||||||
@ -52,7 +52,7 @@ class CSVRow(BaseCSVRow):
|
|||||||
:param amount: The amount.
|
:param amount: The amount.
|
||||||
:param net_balance: The net balance.
|
:param net_balance: The net balance.
|
||||||
"""
|
"""
|
||||||
self.date: str | date = journal_entry_date
|
self.date: str | dt.date = journal_entry_date
|
||||||
"""The date."""
|
"""The date."""
|
||||||
self.currency: str = currency
|
self.currency: str = currency
|
||||||
"""The currency."""
|
"""The currency."""
|
||||||
@ -64,7 +64,7 @@ class CSVRow(BaseCSVRow):
|
|||||||
"""The net balance."""
|
"""The net balance."""
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def values(self) -> list[str | date | Decimal | None]:
|
def values(self) -> list[str | dt.date | Decimal | None]:
|
||||||
"""Returns the values of the row.
|
"""Returns the values of the row.
|
||||||
|
|
||||||
:return: The values of the row.
|
:return: The values of the row.
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
"""The accounts with unapplied original line items.
|
"""The accounts with unapplied original line items.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
from datetime import date
|
import datetime as dt
|
||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
|
|
||||||
from flask import render_template, Response
|
from flask import render_template, Response
|
||||||
@ -49,7 +49,7 @@ class CSVRow(BaseCSVRow):
|
|||||||
"""The number of unapplied original line items."""
|
"""The number of unapplied original line items."""
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def values(self) -> list[str | date | Decimal | None]:
|
def values(self) -> list[str | dt.date | Decimal | None]:
|
||||||
"""Returns the values of the row.
|
"""Returns the values of the row.
|
||||||
|
|
||||||
:return: The values of the row.
|
:return: The values of the row.
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
"""The unmatched offsets.
|
"""The unmatched offsets.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
from datetime import date
|
import datetime as dt
|
||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
|
|
||||||
from flask import render_template, Response
|
from flask import render_template, Response
|
||||||
@ -40,7 +40,7 @@ from accounting.utils.pagination import Pagination
|
|||||||
class CSVRow(BaseCSVRow):
|
class CSVRow(BaseCSVRow):
|
||||||
"""A row in the CSV."""
|
"""A row in the CSV."""
|
||||||
|
|
||||||
def __init__(self, journal_entry_date: str | date, currency: str,
|
def __init__(self, journal_entry_date: str | dt.date, currency: str,
|
||||||
description: str | None, debit: str | Decimal,
|
description: str | None, debit: str | Decimal,
|
||||||
credit: str | Decimal, balance: str | Decimal):
|
credit: str | Decimal, balance: str | Decimal):
|
||||||
"""Constructs a row in the CSV.
|
"""Constructs a row in the CSV.
|
||||||
@ -52,7 +52,7 @@ class CSVRow(BaseCSVRow):
|
|||||||
:param credit: The credit amount.
|
:param credit: The credit amount.
|
||||||
:param balance: The balance.
|
:param balance: The balance.
|
||||||
"""
|
"""
|
||||||
self.date: str | date = journal_entry_date
|
self.date: str | dt.date = journal_entry_date
|
||||||
"""The date."""
|
"""The date."""
|
||||||
self.currency: str = currency
|
self.currency: str = currency
|
||||||
"""The currency."""
|
"""The currency."""
|
||||||
@ -66,7 +66,7 @@ class CSVRow(BaseCSVRow):
|
|||||||
"""The balance."""
|
"""The balance."""
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def values(self) -> list[str | date | Decimal | None]:
|
def values(self) -> list[str | dt.date | Decimal | None]:
|
||||||
"""Returns the values of the row.
|
"""Returns the values of the row.
|
||||||
|
|
||||||
:return: The values of the row.
|
:return: The values of the row.
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
"""The accounts with unmatched offsets.
|
"""The accounts with unmatched offsets.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
from datetime import date
|
import datetime as dt
|
||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
|
|
||||||
from flask import render_template, Response
|
from flask import render_template, Response
|
||||||
@ -49,7 +49,7 @@ class CSVRow(BaseCSVRow):
|
|||||||
"""The number of unapplied original line items."""
|
"""The number of unapplied original line items."""
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def values(self) -> list[str | date | Decimal | None]:
|
def values(self) -> list[str | dt.date | Decimal | None]:
|
||||||
"""Returns the values of the row.
|
"""Returns the values of the row.
|
||||||
|
|
||||||
:return: The values of the row.
|
:return: The values of the row.
|
||||||
|
@ -18,8 +18,8 @@
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
import csv
|
import csv
|
||||||
|
import datetime as dt
|
||||||
from abc import ABC, abstractmethod
|
from abc import ABC, abstractmethod
|
||||||
from datetime import timedelta, date
|
|
||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
from io import StringIO
|
from io import StringIO
|
||||||
from urllib.parse import quote
|
from urllib.parse import quote
|
||||||
@ -77,7 +77,7 @@ def period_spec(period: Period) -> str:
|
|||||||
return f"{start}-{end}"
|
return f"{start}-{end}"
|
||||||
|
|
||||||
|
|
||||||
def __get_start_str(start: date | None) -> str | None:
|
def __get_start_str(start: dt.date | None) -> str | None:
|
||||||
"""Returns the string representation of the start date.
|
"""Returns the string representation of the start date.
|
||||||
|
|
||||||
:param start: The start date.
|
:param start: The start date.
|
||||||
@ -93,7 +93,7 @@ def __get_start_str(start: date | None) -> str | None:
|
|||||||
return start.strftime("%Y%m%d")
|
return start.strftime("%Y%m%d")
|
||||||
|
|
||||||
|
|
||||||
def __get_end_str(end: date | None) -> str | None:
|
def __get_end_str(end: dt.date | None) -> str | None:
|
||||||
"""Returns the string representation of the end date.
|
"""Returns the string representation of the end date.
|
||||||
|
|
||||||
:param end: The end date.
|
:param end: The end date.
|
||||||
@ -104,6 +104,6 @@ def __get_end_str(end: date | None) -> str | None:
|
|||||||
return None
|
return None
|
||||||
if end.month == 12 and end.day == 31:
|
if end.month == 12 and end.day == 31:
|
||||||
return str(end.year)
|
return str(end.year)
|
||||||
if (end + timedelta(days=1)).day == 1:
|
if (end + dt.timedelta(days=1)).day == 1:
|
||||||
return end.strftime("%Y%m")
|
return end.strftime("%Y%m")
|
||||||
return end.strftime("%Y%m%d")
|
return end.strftime("%Y%m%d")
|
||||||
|
@ -17,8 +17,8 @@
|
|||||||
"""The template filters.
|
"""The template filters.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
import datetime as dt
|
||||||
import typing as t
|
import typing as t
|
||||||
from datetime import date, timedelta
|
|
||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
|
|
||||||
from flask_babel import get_locale
|
from flask_babel import get_locale
|
||||||
@ -41,24 +41,24 @@ def format_amount(value: Decimal | None) -> str | None:
|
|||||||
return "{:,}".format(whole) + str(abs(frac))[1:]
|
return "{:,}".format(whole) + str(abs(frac))[1:]
|
||||||
|
|
||||||
|
|
||||||
def format_date(value: date) -> str:
|
def format_date(value: dt.date) -> str:
|
||||||
"""Formats a date to be human-friendly.
|
"""Formats a date to be human-friendly.
|
||||||
|
|
||||||
:param value: The date.
|
:param value: The date.
|
||||||
:return: The human-friendly date text.
|
:return: The human-friendly date text.
|
||||||
"""
|
"""
|
||||||
today: date = date.today()
|
today: dt.date = dt.date.today()
|
||||||
if value == today:
|
if value == today:
|
||||||
return gettext("Today")
|
return gettext("Today")
|
||||||
if value == today - timedelta(days=1):
|
if value == today - dt.timedelta(days=1):
|
||||||
return gettext("Yesterday")
|
return gettext("Yesterday")
|
||||||
if value == today + timedelta(days=1):
|
if value == today + dt.timedelta(days=1):
|
||||||
return gettext("Tomorrow")
|
return gettext("Tomorrow")
|
||||||
locale = str(get_locale())
|
locale = str(get_locale())
|
||||||
if locale == "zh" or locale.startswith("zh_"):
|
if locale == "zh" or locale.startswith("zh_"):
|
||||||
if value == today - timedelta(days=2):
|
if value == today - dt.timedelta(days=2):
|
||||||
return gettext("The day before yesterday")
|
return gettext("The day before yesterday")
|
||||||
if value == today + timedelta(days=2):
|
if value == today + dt.timedelta(days=2):
|
||||||
return gettext("The day after tomorrow")
|
return gettext("The day after tomorrow")
|
||||||
if locale == "zh" or locale.startswith("zh_"):
|
if locale == "zh" or locale.startswith("zh_"):
|
||||||
weekdays = ["一", "二", "三", "四", "五", "六", "日"]
|
weekdays = ["一", "二", "三", "四", "五", "六", "日"]
|
||||||
|
@ -36,7 +36,7 @@ First written: 2023/2/26
|
|||||||
{{ A_("Edit") }}
|
{{ A_("Edit") }}
|
||||||
</a>
|
</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<a class="btn btn-primary" role="button" href="{{ url_for("accounting.journal-entry.order", journal_entry_date=obj.date)|accounting_append_next }}">
|
<a class="btn btn-primary" role="button" href="{{ url_for("accounting.journal-entry.order", date=obj.date)|accounting_append_next }}">
|
||||||
<i class="fa-solid fa-bars-staggered"></i>
|
<i class="fa-solid fa-bars-staggered"></i>
|
||||||
<span class="d-none d-md-inline">{{ A_("Order") }}</span>
|
<span class="d-none d-md-inline">{{ A_("Order") }}</span>
|
||||||
</a>
|
</a>
|
||||||
|
@ -38,7 +38,7 @@ First written: 2023/2/26
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{% if list|length > 1 and accounting_can_edit() %}
|
{% if list|length > 1 and accounting_can_edit() %}
|
||||||
<form action="{{ url_for("accounting.journal-entry.sort", journal_entry_date=date) }}" method="post">
|
<form action="{{ url_for("accounting.journal-entry.sort", date=date) }}" method="post">
|
||||||
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
|
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
|
||||||
{% if request.args.next %}
|
{% if request.args.next %}
|
||||||
<input type="hidden" name="next" value="{{ request.args.next }}">
|
<input type="hidden" name="next" value="{{ request.args.next }}">
|
||||||
|
@ -22,8 +22,6 @@ This module should not import any other module from the application.
|
|||||||
"""
|
"""
|
||||||
import typing as t
|
import typing as t
|
||||||
|
|
||||||
import sqlalchemy as sa
|
|
||||||
|
|
||||||
|
|
||||||
def s(message: t.Any) -> str:
|
def s(message: t.Any) -> str:
|
||||||
"""Casts the LazyString message to the string type.
|
"""Casts the LazyString message to the string type.
|
||||||
|
@ -17,8 +17,8 @@
|
|||||||
"""The test for the account management.
|
"""The test for the account management.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
import datetime as dt
|
||||||
import unittest
|
import unittest
|
||||||
from datetime import timedelta, date
|
|
||||||
|
|
||||||
import httpx
|
import httpx
|
||||||
from flask import Flask
|
from flask import Flask
|
||||||
@ -461,7 +461,7 @@ class AccountTestCase(unittest.TestCase):
|
|||||||
account = Account.find_by_code(CASH.code)
|
account = Account.find_by_code(CASH.code)
|
||||||
self.assertIsNotNone(account)
|
self.assertIsNotNone(account)
|
||||||
account.created_at \
|
account.created_at \
|
||||||
= account.created_at - timedelta(seconds=5)
|
= account.created_at - dt.timedelta(seconds=5)
|
||||||
account.updated_at = account.created_at
|
account.updated_at = account.created_at
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
|
||||||
@ -592,7 +592,7 @@ class AccountTestCase(unittest.TestCase):
|
|||||||
add_journal_entry(self.client,
|
add_journal_entry(self.client,
|
||||||
form={"csrf_token": self.csrf_token,
|
form={"csrf_token": self.csrf_token,
|
||||||
"next": NEXT_URI,
|
"next": NEXT_URI,
|
||||||
"date": date.today().isoformat(),
|
"date": dt.date.today().isoformat(),
|
||||||
"currency-1-code": "USD",
|
"currency-1-code": "USD",
|
||||||
"currency-1-credit-1-account_code": BANK.code,
|
"currency-1-credit-1-account_code": BANK.code,
|
||||||
"currency-1-credit-1-amount": "20"})
|
"currency-1-credit-1-amount": "20"})
|
||||||
|
@ -17,8 +17,8 @@
|
|||||||
"""The test for the currency management.
|
"""The test for the currency management.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
import datetime as dt
|
||||||
import unittest
|
import unittest
|
||||||
from datetime import timedelta, date
|
|
||||||
|
|
||||||
import httpx
|
import httpx
|
||||||
from flask import Flask
|
from flask import Flask
|
||||||
@ -384,7 +384,7 @@ class CurrencyTestCase(unittest.TestCase):
|
|||||||
currency = db.session.get(Currency, USD.code)
|
currency = db.session.get(Currency, USD.code)
|
||||||
self.assertIsNotNone(currency)
|
self.assertIsNotNone(currency)
|
||||||
currency.created_at \
|
currency.created_at \
|
||||||
= currency.created_at - timedelta(seconds=5)
|
= currency.created_at - dt.timedelta(seconds=5)
|
||||||
currency.updated_at = currency.created_at
|
currency.updated_at = currency.created_at
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
|
||||||
@ -534,7 +534,7 @@ class CurrencyTestCase(unittest.TestCase):
|
|||||||
add_journal_entry(self.client,
|
add_journal_entry(self.client,
|
||||||
form={"csrf_token": self.csrf_token,
|
form={"csrf_token": self.csrf_token,
|
||||||
"next": NEXT_URI,
|
"next": NEXT_URI,
|
||||||
"date": date.today().isoformat(),
|
"date": dt.date.today().isoformat(),
|
||||||
"currency-1-code": EUR.code,
|
"currency-1-code": EUR.code,
|
||||||
"currency-1-credit-1-account_code": "1111-001",
|
"currency-1-credit-1-account_code": "1111-001",
|
||||||
"currency-1-credit-1-amount": "20"})
|
"currency-1-credit-1-amount": "20"})
|
||||||
|
@ -17,8 +17,8 @@
|
|||||||
"""The test for the description editor.
|
"""The test for the description editor.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
import datetime as dt
|
||||||
import unittest
|
import unittest
|
||||||
from datetime import date
|
|
||||||
|
|
||||||
from flask import Flask
|
from flask import Flask
|
||||||
|
|
||||||
@ -149,7 +149,7 @@ def get_form_data(csrf_token: str) -> list[dict[str, str]]:
|
|||||||
:param csrf_token: The CSRF token.
|
:param csrf_token: The CSRF token.
|
||||||
:return: A list of the form data.
|
:return: A list of the form data.
|
||||||
"""
|
"""
|
||||||
journal_entry_date: str = date.today().isoformat()
|
journal_entry_date: str = dt.date.today().isoformat()
|
||||||
return [{"csrf_token": csrf_token,
|
return [{"csrf_token": csrf_token,
|
||||||
"next": NEXT_URI,
|
"next": NEXT_URI,
|
||||||
"date": journal_entry_date,
|
"date": journal_entry_date,
|
||||||
|
@ -17,8 +17,8 @@
|
|||||||
"""The test for the journal entry management.
|
"""The test for the journal entry management.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
import datetime as dt
|
||||||
import unittest
|
import unittest
|
||||||
from datetime import date, timedelta
|
|
||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
|
|
||||||
import httpx
|
import httpx
|
||||||
@ -500,7 +500,7 @@ class CashReceiptJournalEntryTestCase(unittest.TestCase):
|
|||||||
journal_entry = db.session.get(JournalEntry, journal_entry_id)
|
journal_entry = db.session.get(JournalEntry, journal_entry_id)
|
||||||
self.assertIsNotNone(journal_entry)
|
self.assertIsNotNone(journal_entry)
|
||||||
journal_entry.created_at \
|
journal_entry.created_at \
|
||||||
= journal_entry.created_at - timedelta(seconds=5)
|
= journal_entry.created_at - dt.timedelta(seconds=5)
|
||||||
journal_entry.updated_at = journal_entry.created_at
|
journal_entry.updated_at = journal_entry.created_at
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
|
||||||
@ -576,7 +576,7 @@ class CashReceiptJournalEntryTestCase(unittest.TestCase):
|
|||||||
self.client,
|
self.client,
|
||||||
form={"csrf_token": self.csrf_token,
|
form={"csrf_token": self.csrf_token,
|
||||||
"next": NEXT_URI,
|
"next": NEXT_URI,
|
||||||
"date": date.today().isoformat(),
|
"date": dt.date.today().isoformat(),
|
||||||
"currency-1-code": line_item.currency_code,
|
"currency-1-code": line_item.currency_code,
|
||||||
"currency-1-debit-1-original_line_item_id": line_item.id,
|
"currency-1-debit-1-original_line_item_id": line_item.id,
|
||||||
"currency-1-debit-1-account_code": line_item.account_code,
|
"currency-1-debit-1-account_code": line_item.account_code,
|
||||||
@ -1112,7 +1112,7 @@ class CashDisbursementJournalEntryTestCase(unittest.TestCase):
|
|||||||
journal_entry = db.session.get(JournalEntry, journal_entry_id)
|
journal_entry = db.session.get(JournalEntry, journal_entry_id)
|
||||||
self.assertIsNotNone(journal_entry)
|
self.assertIsNotNone(journal_entry)
|
||||||
journal_entry.created_at \
|
journal_entry.created_at \
|
||||||
= journal_entry.created_at - timedelta(seconds=5)
|
= journal_entry.created_at - dt.timedelta(seconds=5)
|
||||||
journal_entry.updated_at = journal_entry.created_at
|
journal_entry.updated_at = journal_entry.created_at
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
|
||||||
@ -1773,7 +1773,7 @@ class TransferJournalEntryTestCase(unittest.TestCase):
|
|||||||
journal_entry = db.session.get(JournalEntry, journal_entry_id)
|
journal_entry = db.session.get(JournalEntry, journal_entry_id)
|
||||||
self.assertIsNotNone(journal_entry)
|
self.assertIsNotNone(journal_entry)
|
||||||
journal_entry.created_at \
|
journal_entry.created_at \
|
||||||
= journal_entry.created_at - timedelta(seconds=5)
|
= journal_entry.created_at - dt.timedelta(seconds=5)
|
||||||
journal_entry.updated_at = journal_entry.created_at
|
journal_entry.updated_at = journal_entry.created_at
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
|
||||||
@ -2124,9 +2124,9 @@ class JournalEntryReorderTestCase(unittest.TestCase):
|
|||||||
|
|
||||||
with self.app.app_context():
|
with self.app.app_context():
|
||||||
journal_entry_1: JournalEntry = db.session.get(JournalEntry, id_1)
|
journal_entry_1: JournalEntry = db.session.get(JournalEntry, id_1)
|
||||||
journal_entry_date_2: date = journal_entry_1.date
|
journal_entry_date_2: dt.date = journal_entry_1.date
|
||||||
journal_entry_date_1: date \
|
journal_entry_date_1: dt.date \
|
||||||
= journal_entry_date_2 - timedelta(days=1)
|
= journal_entry_date_2 - dt.timedelta(days=1)
|
||||||
journal_entry_1.date = journal_entry_date_1
|
journal_entry_1.date = journal_entry_date_1
|
||||||
journal_entry_1.no = 3
|
journal_entry_1.no = 3
|
||||||
journal_entry_2: JournalEntry = db.session.get(JournalEntry, id_2)
|
journal_entry_2: JournalEntry = db.session.get(JournalEntry, id_2)
|
||||||
@ -2176,10 +2176,10 @@ class JournalEntryReorderTestCase(unittest.TestCase):
|
|||||||
self.__get_add_disbursement_form())
|
self.__get_add_disbursement_form())
|
||||||
|
|
||||||
with self.app.app_context():
|
with self.app.app_context():
|
||||||
journal_entry_date: date = db.session.get(JournalEntry, id_1).date
|
date: dt.date = db.session.get(JournalEntry, id_1).date
|
||||||
|
|
||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
f"{PREFIX}/dates/{journal_entry_date.isoformat()}",
|
f"{PREFIX}/dates/{date.isoformat()}",
|
||||||
data={"csrf_token": self.csrf_token,
|
data={"csrf_token": self.csrf_token,
|
||||||
"next": "/next",
|
"next": "/next",
|
||||||
f"{id_1}-no": "4",
|
f"{id_1}-no": "4",
|
||||||
@ -2207,7 +2207,7 @@ class JournalEntryReorderTestCase(unittest.TestCase):
|
|||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
|
||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
f"{PREFIX}/dates/{journal_entry_date.isoformat()}",
|
f"{PREFIX}/dates/{date.isoformat()}",
|
||||||
data={"csrf_token": self.csrf_token,
|
data={"csrf_token": self.csrf_token,
|
||||||
"next": "/next",
|
"next": "/next",
|
||||||
f"{id_2}-no": "3a",
|
f"{id_2}-no": "3a",
|
||||||
|
@ -17,8 +17,8 @@
|
|||||||
"""The test for the options.
|
"""The test for the options.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
import datetime as dt
|
||||||
import unittest
|
import unittest
|
||||||
from datetime import datetime, timedelta
|
|
||||||
|
|
||||||
import httpx
|
import httpx
|
||||||
from flask import Flask
|
from flask import Flask
|
||||||
@ -286,7 +286,8 @@ class OptionTestCase(unittest.TestCase):
|
|||||||
with self.app.app_context():
|
with self.app.app_context():
|
||||||
option = db.session.get(Option, "recurring")
|
option = db.session.get(Option, "recurring")
|
||||||
self.assertIsNotNone(option)
|
self.assertIsNotNone(option)
|
||||||
timestamp: datetime = option.created_at - timedelta(seconds=5)
|
timestamp: dt.datetime \
|
||||||
|
= option.created_at - dt.timedelta(seconds=5)
|
||||||
option.created_at = timestamp
|
option.created_at = timestamp
|
||||||
option.updated_at = timestamp
|
option.updated_at = timestamp
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
@ -17,8 +17,8 @@
|
|||||||
"""The test for the reports.
|
"""The test for the reports.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
import datetime as dt
|
||||||
import unittest
|
import unittest
|
||||||
from datetime import date
|
|
||||||
|
|
||||||
import httpx
|
import httpx
|
||||||
from flask import Flask
|
from flask import Flask
|
||||||
@ -446,15 +446,15 @@ class ReportTestData(BaseTestData):
|
|||||||
"""The report test data."""
|
"""The report test data."""
|
||||||
|
|
||||||
def _init_data(self) -> None:
|
def _init_data(self) -> None:
|
||||||
today: date = date.today()
|
today: dt.date = dt.date.today()
|
||||||
year: int = today.year - 5
|
year: int = today.year - 5
|
||||||
month: int = today.month
|
month: int = today.month
|
||||||
while True:
|
while True:
|
||||||
j_date: date = date(year, month, 5)
|
date: dt.date = dt.date(year, month, 5)
|
||||||
if j_date > today:
|
if date > today:
|
||||||
break
|
break
|
||||||
self._add_simple_journal_entry(
|
self._add_simple_journal_entry(
|
||||||
(j_date - today).days, "USD",
|
(date - today).days, "USD",
|
||||||
"Salary薪水", "1200", Accounts.BANK, Accounts.SERVICE)
|
"Salary薪水", "1200", Accounts.BANK, Accounts.SERVICE)
|
||||||
month = month + 1
|
month = month + 1
|
||||||
if month > 12:
|
if month > 12:
|
||||||
|
@ -19,9 +19,9 @@
|
|||||||
"""
|
"""
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import datetime as dt
|
||||||
import typing as t
|
import typing as t
|
||||||
from abc import ABC, abstractmethod
|
from abc import ABC, abstractmethod
|
||||||
from datetime import date, timedelta
|
|
||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
from secrets import randbelow
|
from secrets import randbelow
|
||||||
|
|
||||||
@ -167,10 +167,10 @@ class JournalEntryData:
|
|||||||
:param is_update: True for an update operation, or False otherwise
|
:param is_update: True for an update operation, or False otherwise
|
||||||
:return: The journal entry as a form.
|
:return: The journal entry as a form.
|
||||||
"""
|
"""
|
||||||
journal_entry_date: date = date.today() - timedelta(days=self.days)
|
date: dt.date = dt.date.today() - dt.timedelta(days=self.days)
|
||||||
form: dict[str, str] = {"csrf_token": csrf_token,
|
form: dict[str, str] = {"csrf_token": csrf_token,
|
||||||
"next": next_uri,
|
"next": next_uri,
|
||||||
"date": journal_entry_date.isoformat()}
|
"date": date.isoformat()}
|
||||||
for i in range(len(self.currencies)):
|
for i in range(len(self.currencies)):
|
||||||
form.update(self.currencies[i].form(i + 1, is_update))
|
form.update(self.currencies[i].form(i + 1, is_update))
|
||||||
if self.note is not None:
|
if self.note is not None:
|
||||||
@ -240,11 +240,12 @@ class BaseTestData(ABC):
|
|||||||
existing_j_id: set[int] = {x["id"] for x in self.__journal_entries}
|
existing_j_id: set[int] = {x["id"] for x in self.__journal_entries}
|
||||||
existing_l_id: set[int] = {x["id"] for x in self.__line_items}
|
existing_l_id: set[int] = {x["id"] for x in self.__line_items}
|
||||||
journal_entry_data.id = self.__new_id(existing_j_id)
|
journal_entry_data.id = self.__new_id(existing_j_id)
|
||||||
j_date: date = date.today() - timedelta(days=journal_entry_data.days)
|
date: dt.date \
|
||||||
|
= dt.date.today() - dt.timedelta(days=journal_entry_data.days)
|
||||||
self.__journal_entries.append(
|
self.__journal_entries.append(
|
||||||
{"id": journal_entry_data.id,
|
{"id": journal_entry_data.id,
|
||||||
"date": j_date,
|
"date": date,
|
||||||
"no": self.__next_j_no(j_date),
|
"no": self.__next_j_no(date),
|
||||||
"note": journal_entry_data.note,
|
"note": journal_entry_data.note,
|
||||||
"created_by_id": self.__current_user_id,
|
"created_by_id": self.__current_user_id,
|
||||||
"updated_by_id": self.__current_user_id})
|
"updated_by_id": self.__current_user_id})
|
||||||
@ -303,14 +304,14 @@ class BaseTestData(ABC):
|
|||||||
existing_id.add(obj_id)
|
existing_id.add(obj_id)
|
||||||
return obj_id
|
return obj_id
|
||||||
|
|
||||||
def __next_j_no(self, j_date: date) -> int:
|
def __next_j_no(self, date: dt.date) -> int:
|
||||||
"""Returns the next journal entry number in a day.
|
"""Returns the next journal entry number in a day.
|
||||||
|
|
||||||
:param j_date: The journal entry date.
|
:param date: The journal entry date.
|
||||||
:return: The next journal entry number.
|
:return: The next journal entry number.
|
||||||
"""
|
"""
|
||||||
existing: set[int] = {x["no"] for x in self.__journal_entries
|
existing: set[int] = {x["no"] for x in self.__journal_entries
|
||||||
if x["date"] == j_date}
|
if x["date"] == date}
|
||||||
return 1 if len(existing) == 0 else max(existing) + 1
|
return 1 if len(existing) == 0 else max(existing) + 1
|
||||||
|
|
||||||
def _add_simple_journal_entry(
|
def _add_simple_journal_entry(
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
"""The data reset for the Mia! Accounting demonstration website.
|
"""The data reset for the Mia! Accounting demonstration website.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
from datetime import date, timedelta
|
import datetime as dt
|
||||||
|
|
||||||
from flask import Flask, Blueprint, url_for, flash, redirect, session, \
|
from flask import Flask, Blueprint, url_for, flash, redirect, session, \
|
||||||
render_template, current_app
|
render_template, current_app
|
||||||
@ -116,15 +116,15 @@ class SampleData(BaseTestData):
|
|||||||
|
|
||||||
:return: None.
|
:return: None.
|
||||||
"""
|
"""
|
||||||
today: date = date.today()
|
today: dt.date = dt.date.today()
|
||||||
days: int
|
days: int
|
||||||
year: int
|
year: int
|
||||||
month: int
|
month: int
|
||||||
|
|
||||||
# Recurring in USD
|
# Recurring in USD
|
||||||
j_date: date = date(today.year - 5, today.month, today.day)
|
date: dt.date = dt.date(today.year - 5, today.month, today.day)
|
||||||
j_date = j_date + timedelta(days=(4 - j_date.weekday()))
|
date = date + dt.timedelta(days=(4 - date.weekday()))
|
||||||
days = (today - j_date).days
|
days = (today - date).days
|
||||||
while True:
|
while True:
|
||||||
if days < 0:
|
if days < 0:
|
||||||
break
|
break
|
||||||
@ -147,7 +147,7 @@ class SampleData(BaseTestData):
|
|||||||
if month > 12:
|
if month > 12:
|
||||||
year = year + 1
|
year = year + 1
|
||||||
month = 1
|
month = 1
|
||||||
days = (today - date(year, month, 1)).days
|
days = (today - dt.date(year, month, 1)).days
|
||||||
if days < 0:
|
if days < 0:
|
||||||
break
|
break
|
||||||
self.__add_journal_entry(
|
self.__add_journal_entry(
|
||||||
@ -159,12 +159,12 @@ class SampleData(BaseTestData):
|
|||||||
|
|
||||||
:return: None.
|
:return: None.
|
||||||
"""
|
"""
|
||||||
today: date = date.today()
|
today: dt.date = dt.date.today()
|
||||||
|
|
||||||
year: int = today.year - 5
|
year: int = today.year - 5
|
||||||
month: int = today.month
|
month: int = today.month
|
||||||
while True:
|
while True:
|
||||||
days: int = (today - date(year, month, 5)).days
|
days: int = (today - dt.date(year, month, 5)).days
|
||||||
if days < 0:
|
if days < 0:
|
||||||
break
|
break
|
||||||
self.__add_journal_entry(
|
self.__add_journal_entry(
|
||||||
|
@ -17,8 +17,8 @@
|
|||||||
"""The common test libraries for the journal entry test cases.
|
"""The common test libraries for the journal entry test cases.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
import datetime as dt
|
||||||
import re
|
import re
|
||||||
from datetime import date
|
|
||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
from secrets import randbelow
|
from secrets import randbelow
|
||||||
|
|
||||||
@ -41,7 +41,7 @@ def get_add_form(csrf_token: str) -> dict[str, str]:
|
|||||||
"""
|
"""
|
||||||
return {"csrf_token": csrf_token,
|
return {"csrf_token": csrf_token,
|
||||||
"next": NEXT_URI,
|
"next": NEXT_URI,
|
||||||
"date": date.today().isoformat(),
|
"date": dt.date.today().isoformat(),
|
||||||
"currency-0-code": "USD",
|
"currency-0-code": "USD",
|
||||||
"currency-0-debit-0-no": "16",
|
"currency-0-debit-0-no": "16",
|
||||||
"currency-0-debit-0-account_code": Accounts.CASH,
|
"currency-0-debit-0-account_code": Accounts.CASH,
|
||||||
|
Loading…
Reference in New Issue
Block a user