Added PeriodConverter to convert a period specification to a period, and applied it in the URL patterns.

This commit is contained in:
依瑪貓 2020-07-21 22:05:39 +08:00
parent 01dbd7e60b
commit f9a9a99246
9 changed files with 79 additions and 62 deletions

View File

@ -73,13 +73,13 @@ First written: 2020/7/1
<div class="dropdown-menu account-picker"> <div class="dropdown-menu account-picker">
<div class="dropdown-header">{% trans "Shortcuts" context "Accounting|Account|" as text %}{{ text|force_escape }}</div> <div class="dropdown-header">{% trans "Shortcuts" context "Accounting|Account|" as text %}{{ text|force_escape }}</div>
{% for account in shortcut_accounts %} {% for account in shortcut_accounts %}
<a class="dropdown-item {% if account.code == current_account.code %} active {% endif %}>" href="{% url "accounting:cash" account.code period.spec %}"> <a class="dropdown-item {% if account.code == current_account.code %} active {% endif %}>" href="{% url "accounting:cash" account.code period %}">
{{ account.title|title }} {{ account.title|title }}
</a> </a>
{% endfor %} {% endfor %}
<div class="dropdown-header">{% trans "All" context "Accounting|Account|" as text %}{{ text|force_escape }}</div> <div class="dropdown-header">{% trans "All" context "Accounting|Account|" as text %}{{ text|force_escape }}</div>
{% for account in all_accounts %} {% for account in all_accounts %}
<a class="dropdown-item {% if account.code == current_account.code %} active {% endif %}>" href="{% url "accounting:cash" account.code period.spec %}"> <a class="dropdown-item {% if account.code == current_account.code %} active {% endif %}>" href="{% url "accounting:cash" account.code period %}">
{{ account.code }} {{ account.title|title }} {{ account.code }} {{ account.title|title }}
</a> </a>
{% endfor %} {% endfor %}

View File

@ -112,7 +112,7 @@ First written: 2020/7/19
<td class="amount {% if item.balance < 0 %} text-danger {% endif %}">{{ item.balance|accounting_amount }}</td> <td class="amount {% if item.balance < 0 %} text-danger {% endif %}">{{ item.balance|accounting_amount }}</td>
<td class="amount"></td> <td class="amount"></td>
<td class="actions"> <td class="actions">
<a href="{% url "accounting:ledger" item.code period.spec %}" class="btn btn-info" role="button"> <a href="{% url "accounting:ledger" item.code period %}" class="btn btn-info" role="button">
<i class="fas fa-eye"></i> <i class="fas fa-eye"></i>
<span class="d-none d-lg-inline">{% trans "View" context "Accounting|" as text %}{{ text|force_escape }}</span> <span class="d-none d-lg-inline">{% trans "View" context "Accounting|" as text %}{{ text|force_escape }}</span>
</a> </a>
@ -172,7 +172,7 @@ First written: 2020/7/19
</li> </li>
{% for item in group.details %} {% for item in group.details %}
<li class="list-group-item d-flex justify-content-between align-items-center account"> <li class="list-group-item d-flex justify-content-between align-items-center account">
<a class="list-group-item-action" href="{% url "accounting:ledger" item.code period.spec %}"> <a class="list-group-item-action" href="{% url "accounting:ledger" item.code period %}">
{{ item.title|title }} {{ item.title|title }}
<div class="float-right"> <div class="float-right">
<span class="badge {% if item.balance < 0 %} badge-warning {% else %} badge-secondary {% endif %} badge-pill"> <span class="badge {% if item.balance < 0 %} badge-warning {% else %} badge-secondary {% endif %} badge-pill">

View File

@ -72,7 +72,7 @@ First written: 2020/7/16
</button> </button>
<div class="dropdown-menu account-picker"> <div class="dropdown-menu account-picker">
{% for account in accounts %} {% for account in accounts %}
<a class="dropdown-item {% if account.code == current_account.code %} active {% endif %}" href="{% url "accounting:ledger" account.code period.spec %}"> <a class="dropdown-item {% if account.code == current_account.code %} active {% endif %}" href="{% url "accounting:ledger" account.code period %}">
{{ account.code }} {{ account.title|title }} {{ account.code }} {{ account.title|title }}
</a> </a>
{% endfor %} {% endfor %}

View File

@ -101,7 +101,7 @@ First written: 2020/7/19
<td class="amount">{{ item.debit|accounting_amount }}</td> <td class="amount">{{ item.debit|accounting_amount }}</td>
<td class="amount">{{ item.credit|accounting_amount }}</td> <td class="amount">{{ item.credit|accounting_amount }}</td>
<td class="actions"> <td class="actions">
<a href="{% url "accounting:ledger" item.code period.spec %}" class="btn btn-info" role="button"> <a href="{% url "accounting:ledger" item.code period %}" class="btn btn-info" role="button">
<i class="fas fa-eye"></i> <i class="fas fa-eye"></i>
<span class="d-none d-lg-inline">{% trans "View" context "Accounting|" as text %}{{ text|force_escape }}</span> <span class="d-none d-lg-inline">{% trans "View" context "Accounting|" as text %}{{ text|force_escape }}</span>
</a> </a>

View File

@ -21,8 +21,10 @@
from django.urls import path, register_converter from django.urls import path, register_converter
from mia_core.period import Period
from . import views from . import views
from mia_core import views as mia_core_views from mia_core import views as mia_core_views
from .models import Transaction
from .views import reports from .views import reports
@ -39,11 +41,50 @@ class TransactionTypeConverter:
register_converter(TransactionTypeConverter, "txn-type") register_converter(TransactionTypeConverter, "txn-type")
class PeriodConverter:
"""The path converter for the period."""
regex = ".+"
def to_python(self, value):
"""Returns the period by the period specification.
Args:
value (str): The period specification.
Returns:
Period: The period.
"""
first_txn = Transaction.objects.order_by("date").first()
data_start = first_txn.date if first_txn is not None else None
last_txn = Transaction.objects.order_by("-date").first()
data_end = last_txn.date if last_txn is not None else None
period = Period(value, data_start, data_end)
if period.error is not None:
raise ValueError
return period
def to_url(self, value):
"""Returns the specification of a period.
Args:
value (Period|str): The period.
Returns:
str: The period specification.
"""
if isinstance(value, Period):
return value.spec
return value
register_converter(PeriodConverter, "period")
app_name = "accounting" app_name = "accounting"
urlpatterns = [ urlpatterns = [
path("", views.home, name="home"), path("", views.home, name="home"),
path("cash", reports.cash_default, name="cash.home"), path("cash", reports.cash_default, name="cash.home"),
path("cash/<str:account_code>/<str:period_spec>", path("cash/<str:account_code>/<period:period>",
reports.cash, name="cash"), reports.cash, name="cash"),
path("cash-summary", path("cash-summary",
reports.cash_summary_default, name="cash-summary.home"), reports.cash_summary_default, name="cash-summary.home"),
@ -51,7 +92,7 @@ urlpatterns = [
reports.cash_summary, name="cash-summary"), reports.cash_summary, name="cash-summary"),
path("ledger", path("ledger",
reports.ledger_default, name="ledger.home"), reports.ledger_default, name="ledger.home"),
path("ledger/<str:account_code>/<str:period_spec>", path("ledger/<str:account_code>/<period:period>",
reports.ledger, name="ledger"), reports.ledger, name="ledger"),
path("ledger-summary", path("ledger-summary",
reports.ledger_summary_default, name="ledger-summary.home"), reports.ledger_summary_default, name="ledger-summary.home"),
@ -59,19 +100,19 @@ urlpatterns = [
reports.ledger_summary, name="ledger-summary"), reports.ledger_summary, name="ledger-summary"),
path("journal", path("journal",
reports.journal_default, name="journal.home"), reports.journal_default, name="journal.home"),
path("journal/<str:period_spec>", path("journal/<period:period>",
reports.journal, name="journal"), reports.journal, name="journal"),
path("trial-balance", path("trial-balance",
reports.trial_balance_default, name="trial-balance.home"), reports.trial_balance_default, name="trial-balance.home"),
path("trial-balance/<str:period_spec>", path("trial-balance/<period:period>",
reports.trial_balance, name="trial-balance"), reports.trial_balance, name="trial-balance"),
path("income-statement", path("income-statement",
reports.income_statement_default, name="income-statement.home"), reports.income_statement_default, name="income-statement.home"),
path("income-statement/<str:period_spec>", path("income-statement/<period:period>",
reports.income_statement, name="income-statement"), reports.income_statement, name="income-statement"),
path("balance-sheet", path("balance-sheet",
reports.balance_sheet_default, name="balance-sheet.home"), reports.balance_sheet_default, name="balance-sheet.home"),
path("balance-sheet/<str:period_spec>", path("balance-sheet/<period:period>",
reports.balance_sheet, name="balance-sheet"), reports.balance_sheet, name="balance-sheet"),
path("search", path("search",
reports.search, name="search"), reports.search, name="search"),

View File

@ -71,7 +71,7 @@ class ReportUrl:
def cash(self): def cash(self):
return reverse( return reverse(
"accounting:cash", "accounting:cash",
args=[self._cash_account.code, self._period.spec]) args=[self._cash_account.code, self._period])
@property @property
def cash_summary(self): def cash_summary(self):
@ -82,7 +82,7 @@ class ReportUrl:
def ledger(self): def ledger(self):
return reverse( return reverse(
"accounting:ledger", "accounting:ledger",
args=[self._ledger_account.code, self._period.spec]) args=[self._ledger_account.code, self._period])
@property @property
def ledger_summary(self): def ledger_summary(self):
@ -92,19 +92,19 @@ class ReportUrl:
@property @property
def journal(self): def journal(self):
return reverse("accounting:journal", args=[self._period.spec]) return reverse("accounting:journal", args=[self._period])
@property @property
def trial_balance(self): def trial_balance(self):
return reverse( return reverse(
"accounting:trial-balance", args=[self._period.spec]) "accounting:trial-balance", args=[self._period])
@property @property
def income_statement(self): def income_statement(self):
return reverse( return reverse(
"accounting:income-statement", args=[self._period.spec]) "accounting:income-statement", args=[self._period])
@property @property
def balance_sheet(self): def balance_sheet(self):
return reverse( return reverse(
"accounting:balance-sheet", args=[self._period.spec]) "accounting:balance-sheet", args=[self._period])

View File

@ -59,19 +59,17 @@ def cash_default(request):
@require_GET @require_GET
@digest_login_required @digest_login_required
def cash(request, account_code, period_spec): def cash(request, account_code, period):
"""The cash account. """The cash account.
Args: Args:
request (HttpRequest) The request. request (HttpRequest) The request.
account_code (str): The code of the specified account. account_code (str): The code of the specified account.
period_spec (str): The period specification. period (Period): The period.
Returns: Returns:
HttpResponse: The response. HttpResponse: The response.
""" """
# The period
period = _get_period(period_spec)
# The account # The account
accounts = _cash_accounts() accounts = _cash_accounts()
current_account = None current_account = None
@ -295,19 +293,17 @@ def ledger_default(request):
@require_GET @require_GET
@digest_login_required @digest_login_required
def ledger(request, account_code, period_spec): def ledger(request, account_code, period):
"""The ledger. """The ledger.
Args: Args:
request (HttpRequest) The request. request (HttpRequest) The request.
account_code (str): The code of the specified account. account_code (str): The code of the specified account.
period_spec (str): The period specification. period (Period): The period.
Returns: Returns:
HttpResponse: The response. HttpResponse: The response.
""" """
# The period
period = _get_period(period_spec)
# The account # The account
accounts = _ledger_accounts() accounts = _ledger_accounts()
current_account = None current_account = None
@ -458,18 +454,16 @@ def journal_default(request):
@require_GET @require_GET
@digest_login_required @digest_login_required
def journal(request, period_spec): def journal(request, period):
"""The journal. """The journal.
Args: Args:
request (HttpRequest) The request. request (HttpRequest) The request.
period_spec (str): The period specification. period (Period): The period.
Returns: Returns:
HttpResponse: The response. HttpResponse: The response.
""" """
# The period
period = _get_period(period_spec)
# The accounting records # The accounting records
records = Record.objects\ records = Record.objects\
.filter( .filter(
@ -547,18 +541,16 @@ def trial_balance_default(request):
@require_GET @require_GET
@digest_login_required @digest_login_required
def trial_balance(request, period_spec): def trial_balance(request, period):
"""The trial balance. """The trial balance.
Args: Args:
request (HttpRequest) The request. request (HttpRequest) The request.
period_spec (str): The period specification. period (Period): The period.
Returns: Returns:
HttpResponse: The response. HttpResponse: The response.
""" """
# The period
period = _get_period(period_spec)
# The accounts # The accounts
nominal = list( nominal = list(
Account.objects Account.objects
@ -658,18 +650,16 @@ def income_statement_default(request):
@require_GET @require_GET
@digest_login_required @digest_login_required
def income_statement(request, period_spec): def income_statement(request, period):
"""The income statement. """The income statement.
Args: Args:
request (HttpRequest) The request. request (HttpRequest) The request.
period_spec (str): The period specification. period (Period): The period.
Returns: Returns:
HttpResponse: The response. HttpResponse: The response.
""" """
# The period
period = _get_period(period_spec)
# The accounts # The accounts
accounts = list( accounts = list(
Account.objects Account.objects
@ -745,18 +735,16 @@ def balance_sheet_default(request):
@require_GET @require_GET
@digest_login_required @digest_login_required
def balance_sheet(request, period_spec): def balance_sheet(request, period):
"""The balance sheet. """The balance sheet.
Args: Args:
request (HttpRequest) The request. request (HttpRequest) The request.
period_spec (str): The period specification. period (Period): The period.
Returns: Returns:
HttpResponse: The response. HttpResponse: The response.
""" """
# The period
period = _get_period(period_spec)
# The accounts # The accounts
accounts = list( accounts = list(
Account.objects Account.objects
@ -774,7 +762,7 @@ def balance_sheet(request, period_spec):
.order_by("code")) .order_by("code"))
for account in accounts: for account in accounts:
account.url = reverse( account.url = reverse(
"accounting:ledger", args=[account.code, period.spec]) "accounting:ledger", args=(account.code, period))
balance = Record.objects\ balance = Record.objects\
.filter( .filter(
Q(transaction__date__lt=period.start) Q(transaction__date__lt=period.start)
@ -790,7 +778,7 @@ def balance_sheet(request, period_spec):
brought_forward = Account.objects.get(code="3351") brought_forward = Account.objects.get(code="3351")
brought_forward.balance = -balance brought_forward.balance = -balance
brought_forward.url = reverse( brought_forward.url = reverse(
"accounting:income-statement", args=[period.spec]) "accounting:income-statement", args=(period,))
accounts.append(brought_forward) accounts.append(brought_forward)
balance = Record.objects\ balance = Record.objects\
.filter( .filter(
@ -808,7 +796,7 @@ def balance_sheet(request, period_spec):
net_income = Account.objects.get(code="3353") net_income = Account.objects.get(code="3353")
net_income.balance = -balance net_income.balance = -balance
net_income.url = reverse( net_income.url = reverse(
"accounting:income-statement", args=[period.spec]) "accounting:income-statement", args=(period,))
accounts.append(net_income) accounts.append(net_income)
groups = list(Account.objects.filter( groups = list(Account.objects.filter(
code__in=[x.code[:2] for x in accounts])) code__in=[x.code[:2] for x in accounts]))
@ -861,22 +849,6 @@ def search(request):
}) })
def _get_period(period_spec):
"""Obtains the period helper.
Args:
period_spec (str): The period specification.
Returns:
Period: The period helper.
"""
first_txn = Transaction.objects.order_by("date").first()
data_start = first_txn.date if first_txn is not None else None
last_txn = Transaction.objects.order_by("-date").first()
data_end = last_txn.date if last_txn is not None else None
return Period(period_spec, data_start, data_end)
def _cash_accounts(): def _cash_accounts():
"""Returns the cash accounts. """Returns the cash accounts.

View File

@ -102,6 +102,10 @@ class Period:
def description(self): def description(self):
return self._period.description return self._period.description
@property
def error(self):
return self._period.error
@staticmethod @staticmethod
def _get_last_month_start(): def _get_last_month_start():
"""Returns the first day of the last month. """Returns the first day of the last month.

View File

@ -86,7 +86,7 @@ def url_period(context, period_spec):
request.resolver_match.app_name, request.resolver_match.app_name,
request.resolver_match.url_name) request.resolver_match.url_name)
kwargs = request.resolver_match.kwargs kwargs = request.resolver_match.kwargs
kwargs["period_spec"] = period_spec kwargs["period"] = period_spec
return reverse(view_name, kwargs=kwargs) return reverse(view_name, kwargs=kwargs)