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>
{% 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 %}
<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 %}
</ul>
</tbody>
</table>
{% else %}
<p>No data.</p>
{% endif %}

View File

@ -19,13 +19,29 @@
"""
from django.urls import path
from django.urls import path, register_converter
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"
urlpatterns = [
path("", views.home, name="home"),
path("cash", views.cash_home, name="cash.home"),
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.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.
"""
import logging
import re
from django.http import HttpResponseRedirect
from django.shortcuts import render
@ -28,7 +30,7 @@ from django.views import generic
from django.views.decorators.http import require_http_methods
from accounting.models import Record
from accounting.utils import PeriodParser
from accounting.utils import PeriodParser, Pagination
from mia import settings
@ -62,55 +64,95 @@ class CashReportView(generic.ListView):
context_object_name = "records"
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"])
if self.kwargs["subject_code"] == "0":
records = Record.objects.raw(
"""SELECT r.*
FROM accounting_records AS r
INNER JOIN (SELECT
t1.sn AS sn,
t1.date AS date,
t1.ord AS ord
FROM accounting_records AS r1
LEFT JOIN accounting_transactions AS t1 ON r1.transaction_sn=t1.sn
LEFT JOIN accounting_subjects AS s1 ON r1.subject_sn = s1.sn
WHERE (s1.code LIKE '11%%'
OR s1.code LIKE '12%%'
OR s1.code LIKE '21%%'
OR s1.code LIKE '22%%')
AND t1.date >= %s
AND t1.date <= %s
GROUP BY t1.sn) AS t
ON r.transaction_sn=t.sn
LEFT JOIN accounting_subjects AS s ON r.subject_sn = s.sn
INNER JOIN (SELECT
t1.sn AS sn,
t1.date AS date,
t1.ord AS ord
FROM accounting_records AS r1
LEFT JOIN accounting_transactions AS t1
ON r1.transaction_sn=t1.sn
LEFT JOIN accounting_subjects AS s1
ON r1.subject_sn = s1.sn
WHERE (s1.code LIKE '11%%'
OR s1.code LIKE '12%%'
OR s1.code LIKE '21%%'
OR s1.code LIKE '22%%')
AND t1.date >= %s
AND t1.date <= %s
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%%'
AND s.code NOT LIKE '12%%'
AND s.code NOT LIKE '21%%'
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])
else:
records = Record.objects.raw(
"""SELECT r.*
FROM accounting_records AS r
INNER JOIN (SELECT
t1.sn AS sn,
t1.date AS date,
t1.ord AS ord
FROM accounting_records AS r1
LEFT JOIN accounting_transactions AS t1 ON r1.transaction_sn=t1.sn
LEFT JOIN accounting_subjects AS s1 ON r1.subject_sn = s1.sn
WHERE t1.date >= %s
AND t1.date <= %s
AND s1.code LIKE %s
GROUP BY t1.sn) AS t
ON r.transaction_sn=t.sn
LEFT JOIN accounting_subjects AS s ON r.subject_sn = s.sn
INNER JOIN (SELECT
t1.sn AS sn,
t1.date AS date,
t1.ord AS ord
FROM accounting_records AS r1
LEFT JOIN accounting_transactions AS t1
ON r1.transaction_sn=t1.sn
LEFT JOIN accounting_subjects AS s1
ON r1.subject_sn = s1.sn
WHERE t1.date >= %s
AND t1.date <= %s
AND s1.code LIKE %s
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
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,
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)