Added a simple pagination filter and refined the cash report.

This commit is contained in:
依瑪貓 2020-06-30 23:49:21 +08:00
parent 06b8d470fb
commit 42fe685d53
4 changed files with 150 additions and 38 deletions

View File

@ -1,11 +1,32 @@
<h1>Cash Report</h1> <h1>Cash Report</h1>
{% if records %} {% if records %}
<ul> <table>
<thead>
<tr>
<th scope="col">Date</th>
<th scope="col">Subject</th>
<th scope="col">Summary</th>
<th scope="col">Income</th>
<th scope="col">Expense</th>
<th scope="col">Balance</th>
<th scope="col">View</th>
</tr>
</thead>
<tbody>
{% for record in records %} {% for record in records %}
<li>{{record.transaction.date}} {{record.summary}}</li> <tr>
<td>{{ record.transaction.date }}</td>
<td>{{ record.subject.title_zhtw }}</td>
<td>{{ record.summary }}</td>
<td>{{ record.debit_amount }}</td>
<td>{{ record.credit_amount }}</td>
<td>{{ record.amount }}</td>
<td><a href="{% url "accounting:transaction" "transfer" record.transaction.sn %}">View</a></td>
</tr>
{% endfor %} {% endfor %}
</ul> </tbody>
</table>
{% else %} {% else %}
<p>No data.</p> <p>No data.</p>
{% endif %} {% endif %}

View File

@ -19,13 +19,29 @@
""" """
from django.urls import path from django.urls import path, register_converter
from . import views from . import views
class TransactionTypeConverter:
"""The path converter for the transaction types."""
regex = "income|expense|transfer"
def to_python(self, value):
return value
def to_url(self, value):
return value
register_converter(TransactionTypeConverter, "txn-type")
app_name = "accounting" app_name = "accounting"
urlpatterns = [ urlpatterns = [
path("", views.home, name="home"), path("", views.home, name="home"),
path("cash", views.cash_home, name="cash.home"), path("cash", views.cash_home, name="cash.home"),
path("cash/<str:subject_code>/<str:period_spec>", views.CashReportView.as_view(), name="cash"), path("cash/<str:subject_code>/<str:period_spec>", views.CashReportView.as_view(), name="cash"),
path("transactions/<txn-type:type>/<int:pk>", views.CashReportView.as_view(), name="transaction"),
path("transactions/<txn-type:type>/<int:pk>/edit", views.CashReportView.as_view(), name="transaction.edit"),
] ]

View File

@ -38,3 +38,36 @@ class PeriodParser:
""" """
self.start = period_spec + "-01" self.start = period_spec + "-01"
self.end = period_spec + "-30" self.end = period_spec + "-30"
class Pagination:
"""The pagination.
Attributes:
page_no (int): The current page number
page_size (int): The page size
"""
page_no = None
page_size = None
DEFAULT_PAGE_SIZE = 10
def __init__(self, count, page_no, page_size, is_reverse = False):
"""Constructs a new pagination.
Args:
count (int): The total number of records
page_no (int): The specified page number
page_size (int): The specified number of records per page
is_reverse (bool): Whether we should display the last
page first
"""
self.page_size = page_size \
if page_size is not None \
else self.DEFAULT_PAGE_SIZE
total_pages = int((count - 1) / self.page_size) + 1
self.page_no = page_no \
if page_no is not None \
else 1 if not is_reverse else total_pages
if self.page_no > total_pages:
self.page_no = total_pages

View File

@ -18,6 +18,8 @@
"""The view controllers of the accounting application. """The view controllers of the accounting application.
""" """
import logging
import re
from django.http import HttpResponseRedirect from django.http import HttpResponseRedirect
from django.shortcuts import render from django.shortcuts import render
@ -28,7 +30,7 @@ from django.views import generic
from django.views.decorators.http import require_http_methods from django.views.decorators.http import require_http_methods
from accounting.models import Record from accounting.models import Record
from accounting.utils import PeriodParser from accounting.utils import PeriodParser, Pagination
from mia import settings from mia import settings
@ -62,55 +64,95 @@ class CashReportView(generic.ListView):
context_object_name = "records" context_object_name = "records"
def get_queryset(self): def get_queryset(self):
"""Return the records for the accounting cash report.""" """Return the accounting records for the cash report.
Returns:
The accounting records for the cash report
"""
period = PeriodParser(self.kwargs["period_spec"]) period = PeriodParser(self.kwargs["period_spec"])
if self.kwargs["subject_code"] == "0": if self.kwargs["subject_code"] == "0":
records = Record.objects.raw( records = Record.objects.raw(
"""SELECT r.* """SELECT r.*
FROM accounting_records AS r FROM accounting_records AS r
INNER JOIN (SELECT INNER JOIN (SELECT
t1.sn AS sn, t1.sn AS sn,
t1.date AS date, t1.date AS date,
t1.ord AS ord t1.ord AS ord
FROM accounting_records AS r1 FROM accounting_records AS r1
LEFT JOIN accounting_transactions AS t1 ON r1.transaction_sn=t1.sn LEFT JOIN accounting_transactions AS t1
LEFT JOIN accounting_subjects AS s1 ON r1.subject_sn = s1.sn ON r1.transaction_sn=t1.sn
WHERE (s1.code LIKE '11%%' LEFT JOIN accounting_subjects AS s1
OR s1.code LIKE '12%%' ON r1.subject_sn = s1.sn
OR s1.code LIKE '21%%' WHERE (s1.code LIKE '11%%'
OR s1.code LIKE '22%%') OR s1.code LIKE '12%%'
AND t1.date >= %s OR s1.code LIKE '21%%'
AND t1.date <= %s OR s1.code LIKE '22%%')
GROUP BY t1.sn) AS t AND t1.date >= %s
ON r.transaction_sn=t.sn AND t1.date <= %s
LEFT JOIN accounting_subjects AS s ON r.subject_sn = s.sn GROUP BY t1.sn) AS t
ON r.transaction_sn=t.sn
LEFT JOIN accounting_subjects AS s ON r.subject_sn = s.sn
WHERE s.code NOT LIKE '11%%' WHERE s.code NOT LIKE '11%%'
AND s.code NOT LIKE '12%%' AND s.code NOT LIKE '12%%'
AND s.code NOT LIKE '21%%' AND s.code NOT LIKE '21%%'
AND s.code NOT LIKE '22%%' AND s.code NOT LIKE '22%%'
ORDER BY t.date, t.ord, CASE WHEN is_credit THEN 1 ELSE 2 END, r.ord""", ORDER BY
t.date,
t.ord,
CASE WHEN is_credit THEN 1 ELSE 2 END,
r.ord""",
[period.start, period.end]) [period.start, period.end])
else: else:
records = Record.objects.raw( records = Record.objects.raw(
"""SELECT r.* """SELECT r.*
FROM accounting_records AS r FROM accounting_records AS r
INNER JOIN (SELECT INNER JOIN (SELECT
t1.sn AS sn, t1.sn AS sn,
t1.date AS date, t1.date AS date,
t1.ord AS ord t1.ord AS ord
FROM accounting_records AS r1 FROM accounting_records AS r1
LEFT JOIN accounting_transactions AS t1 ON r1.transaction_sn=t1.sn LEFT JOIN accounting_transactions AS t1
LEFT JOIN accounting_subjects AS s1 ON r1.subject_sn = s1.sn ON r1.transaction_sn=t1.sn
WHERE t1.date >= %s LEFT JOIN accounting_subjects AS s1
AND t1.date <= %s ON r1.subject_sn = s1.sn
AND s1.code LIKE %s WHERE t1.date >= %s
GROUP BY t1.sn) AS t AND t1.date <= %s
ON r.transaction_sn=t.sn AND s1.code LIKE %s
LEFT JOIN accounting_subjects AS s ON r.subject_sn = s.sn GROUP BY t1.sn) AS t
ON r.transaction_sn=t.sn
LEFT JOIN accounting_subjects AS s ON r.subject_sn = s.sn
WHERE s.code NOT LIKE %s WHERE s.code NOT LIKE %s
ORDER BY t.date, t.ord, CASE WHEN is_credit THEN 1 ELSE 2 END, r.ord""", ORDER BY
t.date,
t.ord,
CASE WHEN is_credit THEN 1 ELSE 2 END,
r.ord""",
[period.start, [period.start,
period.end, period.end,
self.kwargs["subject_code"] + "%", self.kwargs["subject_code"] + "%",
self.kwargs["subject_code"] + "%"]) self.kwargs["subject_code"] + "%"])
return records get = self.request.GET
pagination = Pagination(
len(records),
self.get_number_query("page"),
self.get_number_query("page-size"),
True)
start_no = pagination.page_size * (pagination.page_no - 1)
return records[start_no:start_no + pagination.page_size]
def get_number_query(self, name):
"""Returns a positive number query parameter.
Args:
name (str): The name of the query parameter
Returns:
The parameter value, or None if this parameter does not
exist or is not a positive number
"""
if name not in self.request.GET:
return None
elif not re.match("^[1-9][0-9]*$", self.request.GET[name]):
return None
else:
return int(self.request.GET)