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:
parent
fa7416d0f3
commit
39c75f772a
@ -118,6 +118,8 @@ class Transaction(models.Model):
|
|||||||
if self._has_order_hole is None:
|
if self._has_order_hole is None:
|
||||||
orders = [x.ord for x in Transaction.objects.filter(
|
orders = [x.ord for x in Transaction.objects.filter(
|
||||||
date=self.date)]
|
date=self.date)]
|
||||||
|
if len(orders) == 0:
|
||||||
|
self._has_order_hole = False
|
||||||
if max(orders) != len(orders):
|
if max(orders) != len(orders):
|
||||||
self._has_order_hole = True
|
self._has_order_hole = True
|
||||||
elif min(orders) != 1:
|
elif min(orders) != 1:
|
||||||
@ -198,23 +200,41 @@ class Record(models.Model):
|
|||||||
db_column="updatedby",
|
db_column="updatedby",
|
||||||
related_name="updated_accounting_records")
|
related_name="updated_accounting_records")
|
||||||
|
|
||||||
|
_debit_amount = None
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def debit_amount(self):
|
def debit_amount(self):
|
||||||
"""The debit amount of this accounting record"""
|
"""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
|
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
|
@property
|
||||||
def credit_amount(self):
|
def credit_amount(self):
|
||||||
"""The credit amount of this accounting record"""
|
"""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
|
return self.amount if self.is_credit else None
|
||||||
|
|
||||||
@property
|
@credit_amount.setter
|
||||||
def accumulative_balance(self):
|
def credit_amount(self, value):
|
||||||
return self._accumulative_balance
|
self._credit_amount = value
|
||||||
|
|
||||||
@accumulative_balance.setter
|
_balance = None
|
||||||
def accumulative_balance(self, value):
|
|
||||||
self._accumulative_balance = value
|
@property
|
||||||
|
def balance(self):
|
||||||
|
return self._balance
|
||||||
|
|
||||||
|
@balance.setter
|
||||||
|
def balance(self, value):
|
||||||
|
self._balance = value
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
"""Returns the string representation of this accounting
|
"""Returns the string representation of this accounting
|
||||||
|
@ -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 %}">
|
<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.transaction.date|smart_date }}</td>
|
||||||
<td>{{ record.subject.title_zhtw }}</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">
|
<span class="badge badge-danger badge-pill">
|
||||||
{% trans "Unbalanced" context "Accounting|" as text %}
|
{% trans "Unbalanced" context "Accounting|" as text %}
|
||||||
{{ text|force_escape }}
|
{{ text|force_escape }}
|
||||||
@ -106,14 +106,16 @@ First written: 2020/7/1
|
|||||||
{{ text|force_escape }}
|
{{ text|force_escape }}
|
||||||
</span>
|
</span>
|
||||||
{% endif %}</td>
|
{% endif %}</td>
|
||||||
<td class="amount">{{ record.credit_amount|default:""|intcomma:False }}</td>
|
<td class="amount">{% if record.credit_amount is not None %}{{ record.credit_amount|intcomma:False }}{% endif %}</td>
|
||||||
<td class="amount">{{ record.debit_amount|default:""|intcomma:False }}</td>
|
<td class="amount">{% if record.debit_amount is not None %}{{ record.debit_amount|intcomma:False }}{% endif %}</td>
|
||||||
<td class="amount">{{ record.amount|intcomma:False }}</td>
|
<td class="amount">{{ record.balance|intcomma:False }}</td>
|
||||||
<td class="actions">
|
<td class="actions">
|
||||||
<a href="{{ record.transaction.get_absolute_url }}" class="btn btn-info" role="button">
|
{% if record.sn is not None %}
|
||||||
<i class="fas fa-eye"></i>
|
<a href="{{ record.transaction.get_absolute_url }}" class="btn btn-info" role="button">
|
||||||
<span class="d-none d-lg-inline">{% trans "View" context "Accounting|" as text %}{{ text|force_escape }}</span>
|
<i class="fas fa-eye"></i>
|
||||||
</a>
|
<span class="d-none d-lg-inline">{% trans "View" context "Accounting|" as text %}{{ text|force_escape }}</span>
|
||||||
|
</a>
|
||||||
|
{% endif %}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
@ -124,32 +126,47 @@ First written: 2020/7/1
|
|||||||
<ul class="list-group d-md-none">
|
<ul class="list-group d-md-none">
|
||||||
{% for record in records %}
|
{% 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 %}">
|
<li class="list-group-item {% if not record.transaction.is_balanced or record.transaction.has_order_hole %} list-group-item-danger {% endif %}">
|
||||||
<a class="list-group-item-action" href="{{ record.transaction.get_absolute_url }}">
|
{% 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|default:"" }}
|
||||||
|
{% if not record.transaction.is_balanced %}
|
||||||
|
<span class="badge badge-danger badge-pill">
|
||||||
|
{% trans "Unbalanced" context "Accounting|" as text %}
|
||||||
|
{{ text|force_escape }}
|
||||||
|
</span>
|
||||||
|
{% endif %}
|
||||||
|
{% if record.transaction.has_order_hole %}
|
||||||
|
<span class="badge badge-danger badge-pill">
|
||||||
|
{% trans "Need Reorder" context "Accounting|" as text %}
|
||||||
|
{{ text|force_escape }}
|
||||||
|
</span>
|
||||||
|
{% endif %}
|
||||||
|
</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>
|
||||||
|
</a>
|
||||||
|
{% else %}
|
||||||
<div class="date-subject-line d-flex justify-content-between align-items-center">
|
<div class="date-subject-line d-flex justify-content-between align-items-center">
|
||||||
{{ record.transaction.date|smart_date }} {{ record.subject.title_zhtw }}
|
{{ record.transaction.date|smart_date }} {{ record.subject.title_zhtw }}
|
||||||
</div>
|
</div>
|
||||||
<div class="d-flex justify-content-between align-items-center">
|
<div class="d-flex justify-content-between align-items-center">
|
||||||
<div>
|
<div>{{ record.summary|default:"" }}</div>
|
||||||
{{ record.summary }}
|
|
||||||
{% if not record.transaction.is_balanced %}
|
|
||||||
<span class="badge badge-danger badge-pill">
|
|
||||||
{% trans "Unbalanced" context "Accounting|" as text %}
|
|
||||||
{{ text|force_escape }}
|
|
||||||
</span>
|
|
||||||
{% endif %}
|
|
||||||
{% if record.transaction.has_order_hole %}
|
|
||||||
<span class="badge badge-danger badge-pill">
|
|
||||||
{% trans "Need Reorder" context "Accounting|" as text %}
|
|
||||||
{{ text|force_escape }}
|
|
||||||
</span>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
</div>
|
</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>
|
<span class="badge {% if record.balance < 0 %} badge-danger {% else %} badge-primary {% endif %} badge-pill">{{ record.balance|intcomma:False }}</span>
|
||||||
</div>
|
</div>
|
||||||
</a>
|
{% endif %}
|
||||||
</li>
|
</li>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</ul>
|
</ul>
|
||||||
|
@ -18,7 +18,9 @@
|
|||||||
"""The view controllers of the accounting application.
|
"""The view controllers of the accounting application.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
from datetime import timedelta
|
||||||
|
|
||||||
|
from django.db import connection
|
||||||
from django.http import HttpResponseRedirect
|
from django.http import HttpResponseRedirect
|
||||||
from django.shortcuts import render
|
from django.shortcuts import render
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
@ -31,7 +33,7 @@ from accounting.models import Record, Transaction, Subject
|
|||||||
from mia import settings
|
from mia import settings
|
||||||
from mia_core.digest_auth import digest_login_required
|
from mia_core.digest_auth import digest_login_required
|
||||||
from mia_core.period import Period
|
from mia_core.period import Period
|
||||||
from mia_core.utils import Pagination
|
from mia_core.utils import Pagination, SqlQuery
|
||||||
|
|
||||||
|
|
||||||
@require_GET
|
@require_GET
|
||||||
@ -65,6 +67,7 @@ def cash_home(request):
|
|||||||
@digest_login_required
|
@digest_login_required
|
||||||
def cash(request, subject_code, period_spec):
|
def cash(request, subject_code, period_spec):
|
||||||
"""The cash account report."""
|
"""The cash account report."""
|
||||||
|
# The period
|
||||||
first_txn = Transaction.objects.order_by("date").first()
|
first_txn = Transaction.objects.order_by("date").first()
|
||||||
data_start = first_txn.date if first_txn is not None else None
|
data_start = first_txn.date if first_txn is not None else None
|
||||||
last_txn = Transaction.objects.order_by("-date").first()
|
last_txn = Transaction.objects.order_by("-date").first()
|
||||||
@ -72,13 +75,12 @@ def cash(request, subject_code, period_spec):
|
|||||||
period = Period(
|
period = Period(
|
||||||
get_language(), data_start, data_end,
|
get_language(), data_start, data_end,
|
||||||
period_spec)
|
period_spec)
|
||||||
# The list data
|
# The SQL query
|
||||||
if subject_code == "0":
|
if subject_code == "0":
|
||||||
subject = Subject(code="0")
|
subject = Subject(code="0")
|
||||||
subject.title_zhtw = pgettext(
|
subject.title_zhtw = pgettext(
|
||||||
"Accounting|", "Current Assets And Liabilities")
|
"Accounting|", "Current Assets And Liabilities")
|
||||||
records = Record.objects.raw(
|
select_records = """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,
|
||||||
@ -106,12 +108,20 @@ ORDER BY
|
|||||||
t.date,
|
t.date,
|
||||||
t.ord,
|
t.ord,
|
||||||
CASE WHEN is_credit THEN 1 ELSE 2 END,
|
CASE WHEN is_credit THEN 1 ELSE 2 END,
|
||||||
r.ord""",
|
r.ord"""
|
||||||
|
sql_records = SqlQuery(
|
||||||
|
select_records,
|
||||||
[period.start, period.end])
|
[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:
|
else:
|
||||||
subject = Subject.objects.filter(code=subject_code).first()
|
subject = Subject.objects.filter(
|
||||||
records = Record.objects.raw(
|
code=subject_code).first()
|
||||||
"""SELECT r.*
|
select_records = """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,
|
||||||
@ -133,11 +143,55 @@ ORDER BY
|
|||||||
t.date,
|
t.date,
|
||||||
t.ord,
|
t.ord,
|
||||||
CASE WHEN is_credit THEN 1 ELSE 2 END,
|
CASE WHEN is_credit THEN 1 ELSE 2 END,
|
||||||
r.ord""",
|
r.ord"""
|
||||||
|
sql_records = SqlQuery(
|
||||||
|
select_records,
|
||||||
[period.start,
|
[period.start,
|
||||||
period.end,
|
period.end,
|
||||||
subject.code + "%",
|
subject.code + "%",
|
||||||
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)
|
pagination = Pagination(request, records, True)
|
||||||
return render(request, "accounting/cash.html", {
|
return render(request, "accounting/cash.html", {
|
||||||
"records": pagination.records,
|
"records": pagination.records,
|
||||||
|
@ -394,3 +394,21 @@ class PaginationException(Exception):
|
|||||||
|
|
||||||
def __init__(self, url):
|
def __init__(self, url):
|
||||||
self.url = 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
|
||||||
|
Loading…
Reference in New Issue
Block a user