Added the balance-before record and the sum record to the cash account report. Moved the SqlQuery utility from the accounting application to the Mia core application.

This commit is contained in:
依瑪貓 2020-07-13 19:54:27 +08:00
parent fa7416d0f3
commit 39c75f772a
4 changed files with 150 additions and 41 deletions

View File

@ -118,6 +118,8 @@ class Transaction(models.Model):
if self._has_order_hole is None:
orders = [x.ord for x in Transaction.objects.filter(
date=self.date)]
if len(orders) == 0:
self._has_order_hole = False
if max(orders) != len(orders):
self._has_order_hole = True
elif min(orders) != 1:
@ -198,23 +200,41 @@ class Record(models.Model):
db_column="updatedby",
related_name="updated_accounting_records")
_debit_amount = None
@property
def debit_amount(self):
"""The debit amount of this accounting record"""
if self._debit_amount is not None:
return self._debit_amount
return self.amount if not self.is_credit else None
@debit_amount.setter
def debit_amount(self, value):
self._debit_amount = value
_credit_amount = None
@property
def credit_amount(self):
"""The credit amount of this accounting record"""
if self._credit_amount is not None:
return self._credit_amount
return self.amount if self.is_credit else None
@property
def accumulative_balance(self):
return self._accumulative_balance
@credit_amount.setter
def credit_amount(self, value):
self._credit_amount = value
@accumulative_balance.setter
def accumulative_balance(self, value):
self._accumulative_balance = value
_balance = None
@property
def balance(self):
return self._balance
@balance.setter
def balance(self, value):
self._balance = value
def __str__(self):
"""Returns the string representation of this accounting

View File

@ -95,7 +95,7 @@ First written: 2020/7/1
<tr class="{% if not record.transaction.is_balanced or record.transaction.has_order_hole %} table-danger {% endif %}">
<td>{{ record.transaction.date|smart_date }}</td>
<td>{{ record.subject.title_zhtw }}</td>
<td>{{ record.summary }}{% if not record.transaction.is_balanced %}
<td>{{ record.summary|default:"" }}{% if not record.transaction.is_balanced %}
<span class="badge badge-danger badge-pill">
{% trans "Unbalanced" context "Accounting|" as text %}
{{ text|force_escape }}
@ -106,14 +106,16 @@ First written: 2020/7/1
{{ text|force_escape }}
</span>
{% endif %}</td>
<td class="amount">{{ record.credit_amount|default:""|intcomma:False }}</td>
<td class="amount">{{ record.debit_amount|default:""|intcomma:False }}</td>
<td class="amount">{{ record.amount|intcomma:False }}</td>
<td class="amount">{% if record.credit_amount is not None %}{{ record.credit_amount|intcomma:False }}{% endif %}</td>
<td class="amount">{% if record.debit_amount is not None %}{{ record.debit_amount|intcomma:False }}{% endif %}</td>
<td class="amount">{{ record.balance|intcomma:False }}</td>
<td class="actions">
{% if record.sn is not None %}
<a href="{{ record.transaction.get_absolute_url }}" class="btn btn-info" role="button">
<i class="fas fa-eye"></i>
<span class="d-none d-lg-inline">{% trans "View" context "Accounting|" as text %}{{ text|force_escape }}</span>
</a>
{% endif %}
</td>
</tr>
{% endfor %}
@ -124,13 +126,14 @@ First written: 2020/7/1
<ul class="list-group d-md-none">
{% for record in records %}
<li class="list-group-item {% if not record.transaction.is_balanced or record.transaction.has_order_hole %} list-group-item-danger {% endif %}">
{% if record.sn is not None %}
<a class="list-group-item-action" href="{{ record.transaction.get_absolute_url }}">
<div class="date-subject-line d-flex justify-content-between align-items-center">
{{ record.transaction.date|smart_date }} {{ record.subject.title_zhtw }}
</div>
<div class="d-flex justify-content-between align-items-center">
<div>
{{ record.summary }}
{{ record.summary|default:"" }}
{% if not record.transaction.is_balanced %}
<span class="badge badge-danger badge-pill">
{% trans "Unbalanced" context "Accounting|" as text %}
@ -146,10 +149,24 @@ First written: 2020/7/1
</div>
</div>
<div>
<span class="badge {% if not record.is_credit %} badge-warning {% else %} badge-success {% endif %} badge-pill">{{ record.amount|intcomma:False }}</span>
{% if record.credit_amount is not None %}<span class="badge badge-success badge-pill">{{ record.credit_amount|intcomma:False }}</span>{% endif %}
{% if record.debit_amount is not None %}<span class="badge badge-warning badge-pill">-{{ record.debit_amount|intcomma:False }}</span>{% endif %}
<span class="badge {% if record.balance < 0 %} badge-danger {% else %} badge-primary {% endif %} badge-pill">{{ record.balance|intcomma:False }}</span>
</div>
</a>
{% else %}
<div class="date-subject-line d-flex justify-content-between align-items-center">
{{ record.transaction.date|smart_date }} {{ record.subject.title_zhtw }}
</div>
<div class="d-flex justify-content-between align-items-center">
<div>{{ record.summary|default:"" }}</div>
</div>
<div>
{% if record.credit_amount is not None %}<span class="badge badge-success badge-pill">{{ record.credit_amount|intcomma:False }}</span>{% endif %}
{% if record.debit_amount is not None %}<span class="badge badge-warning badge-pill">-{{ record.debit_amount|intcomma:False }}</span>{% endif %}
<span class="badge {% if record.balance < 0 %} badge-danger {% else %} badge-primary {% endif %} badge-pill">{{ record.balance|intcomma:False }}</span>
</div>
{% endif %}
</li>
{% endfor %}
</ul>

View File

@ -18,7 +18,9 @@
"""The view controllers of the accounting application.
"""
from datetime import timedelta
from django.db import connection
from django.http import HttpResponseRedirect
from django.shortcuts import render
from django.urls import reverse
@ -31,7 +33,7 @@ from accounting.models import Record, Transaction, Subject
from mia import settings
from mia_core.digest_auth import digest_login_required
from mia_core.period import Period
from mia_core.utils import Pagination
from mia_core.utils import Pagination, SqlQuery
@require_GET
@ -65,6 +67,7 @@ def cash_home(request):
@digest_login_required
def cash(request, subject_code, period_spec):
"""The cash account report."""
# 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()
@ -72,13 +75,12 @@ def cash(request, subject_code, period_spec):
period = Period(
get_language(), data_start, data_end,
period_spec)
# The list data
# The SQL query
if subject_code == "0":
subject = Subject(code="0")
subject.title_zhtw = pgettext(
"Accounting|", "Current Assets And Liabilities")
records = Record.objects.raw(
"""SELECT r.*
select_records = """SELECT r.*
FROM accounting_records AS r
INNER JOIN (SELECT
t1.sn AS sn,
@ -106,12 +108,20 @@ ORDER BY
t.date,
t.ord,
CASE WHEN is_credit THEN 1 ELSE 2 END,
r.ord""",
r.ord"""
sql_records = SqlQuery(
select_records,
[period.start, period.end])
select_balance_before = """SELECT
SUM(CASE WHEN is_credit THEN 1 ELSE -1 END * amount) AS amount
FROM (%s) AS b""" % select_records
sql_balance_before = SqlQuery(
select_balance_before,
[data_start, period.start - timedelta(days=1)])
else:
subject = Subject.objects.filter(code=subject_code).first()
records = Record.objects.raw(
"""SELECT r.*
subject = Subject.objects.filter(
code=subject_code).first()
select_records = """SELECT r.*
FROM accounting_records AS r
INNER JOIN (SELECT
t1.sn AS sn,
@ -133,11 +143,55 @@ ORDER BY
t.date,
t.ord,
CASE WHEN is_credit THEN 1 ELSE 2 END,
r.ord""",
r.ord"""
sql_records = SqlQuery(
select_records,
[period.start,
period.end,
subject.code + "%",
subject.code + "%"])
select_balance_before = """SELECT
SUM(CASE WHEN is_credit THEN 1 ELSE -1 END * amount) AS amount
FROM (%s) AS b""" % select_records
sql_balance_before = SqlQuery(
select_balance_before,
[data_start,
period.start - timedelta(days=1),
subject.code + "%",
subject.code + "%"])
# The list data
records = list(Record.objects.raw(
sql_records.sql,
sql_records.params))
with connection.cursor() as cursor:
cursor.execute(
sql_balance_before.sql, sql_balance_before.params)
row = cursor.fetchone()
balance_before = row[0]
if balance_before is None:
balance_before = 0
balance = balance_before
for record in records:
sign = 1 if record.is_credit else -1
balance = balance + sign * record.amount
record.balance = balance
record_sum = Record(
transaction=Transaction(date=records[-1].transaction.date),
subject=subject,
summary=pgettext("Accounting|", "Total"),
balance=balance
)
record_sum.credit_amount = sum([
x.amount for x in records if x.is_credit])
record_sum.debit_amount = sum([
x.amount for x in records if not x.is_credit])
records.insert(0, Record(
transaction=Transaction(date=period.start),
subject=Subject.objects.filter(code="3351").first(),
is_credit=balance_before >= 0,
amount=abs(balance_before),
balance=balance_before))
records.append(record_sum)
pagination = Pagination(request, records, True)
return render(request, "accounting/cash.html", {
"records": pagination.records,

View File

@ -394,3 +394,21 @@ class PaginationException(Exception):
def __init__(self, url):
self.url = url
class SqlQuery:
"""A SQL query statement with its parameters.
Args:
sql (str): The SQL statement.
params (list[str]): the parameters.
Attributes:
sql (str): The SQL statement.
params (list[str]): the parameters.
"""
sql = ""
params = []
def __init__(self, sql, params):
self.sql = sql
self.params = params