Added the ledger summary in the accounting application.

This commit is contained in:
依瑪貓 2020-07-17 00:12:30 +08:00
parent d9ccefa27c
commit 70c3f01368
4 changed files with 221 additions and 5 deletions

View File

@ -324,6 +324,7 @@ class RecordSummary(models.Model):
month = models.DateField(primary_key=True)
credit_amount = models.PositiveIntegerField()
debit_amount = models.PositiveIntegerField()
balance = models.IntegerField()
_label = None
@ -337,10 +338,6 @@ class RecordSummary(models.Model):
def label(self, value):
self._label = value
@property
def balance(self):
return self.credit_amount - self.debit_amount
_cumulative_balance = None
@property

View File

@ -0,0 +1,169 @@
{% extends "base.html" %}
{% comment %}
The Mia Accounting Application
ledger_summary.html: The template for the ledger summary reports
Copyright (c) 2020 imacat.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
Author: imacat@mail.imacat.idv.tw (imacat)
First written: 2020/7/17
{% endcomment %}
{% load i18n %}
{% load humanize %}
{% load mia_core %}
{% load accounting %}
{% block settings %}
{% blocktrans asvar title with subject=current_subject.title|title context "Accounting|" %}Ledger Summary for {{ subject }}{% endblocktrans %}
{% setvar "title" title %}
{% endblock %}
{% block content %}
<div class="btn-group btn-actions">
<div class="btn-group">
<button type="button" class="btn btn-primary dropdown-toggle" data-toggle="dropdown">
<i class="fas fa-edit"></i>
{% trans "New" context "Accounting|" as text %}
{{ text|force_escape }}
</button>
<div class="dropdown-menu">
{% url "accounting:transaction.create" "expense" as url %}
<a class="dropdown-item" href="{% url_query url r=request.get_full_path %}">
{% trans "Cash Expense" context "Accounting|" as text %}
{{ text|force_escape }}
</a>
{% url "accounting:transaction.create" "income" as url %}
<a class="dropdown-item" href="{% url_query url r=request.get_full_path %}">
{% trans "Cash Income" context "Accounting|" as text %}
{{ text|force_escape }}
</a>
{% url "accounting:transaction.create" "transfer" as url %}
<a class="dropdown-item" href="{% url_query url r=request.get_full_path %}">
{% trans "Transfer" context "Accounting|" as text %}
{{ text|force_escape }}
</a>
</div>
</div>
{% with current_report_icon="fas fa-file-invoice-dollar" %}
{% trans "Ledger Summary" context "Accounting|" as current_report_title %}
{% include "accounting/include/report-chooser.html" %}
{% endwith %}
<div class="btn-group">
<button type="button" class="btn btn-primary dropdown-toggle" data-toggle="dropdown">
<span class="d-none d-md-inline">{{ current_subject.title|title }}</span>
<span class="d-md-none">{% trans "Subject" context "Accounting|" as text %}{{ text|force_escape }}</span>
</button>
<div class="dropdown-menu subject-picker">
{% for subject in subjects %}
<a class="dropdown-item {% if subject.code == current_subject.code %} active {% endif %}>" href="{% url "accounting:cash-summary" subject.code %}">
{{ subject.title|title }}
</a>
{% endfor %}
</div>
</div>
</div>
{% if records %}
{% include "mia_core/include/pagination.html" %}
{# The table for large screens #}
<table class="table table-striped table-hover d-none d-sm-table general-journal-table">
<thead>
<tr>
<th scope="col">{% trans "Month" context "Accounting|" as text %}{{ text|force_escape }}</th>
<th class="amount" scope="col">{% trans "Debit" context "Accounting|" as text %}{{ text|force_escape }}</th>
<th class="amount" scope="col">{% trans "Credit" context "Accounting|" as text %}{{ text|force_escape }}</th>
<th class="amount" scope="col">{% trans "Balance" context "Accounting|" as text %}{{ text|force_escape }}</th>
<th class="amount" scope="col">{% trans "Cumulative Balance" context "Accounting|" as text %}{{ text|force_escape }}</th>
<th class="actions" scope="col">{% trans "View" context "Accounting|" as text %}{{ text|force_escape }}</th>
</tr>
</thead>
<tbody>
{% for record in records %}
<tr class="{% if current_subject.code|first in "12" and record.balance < 0 %} table-danger {% endif %}">
<td>{{ record.label }}</td>
<td class="amount">{{ record.debit_amount|accounting_amount }}</td>
<td class="amount">{{ record.credit_amount|accounting_amount }}</td>
<td class="amount {% if record.balance < 0 %} text-danger {% endif %}">{{ record.balance|accounting_amount }}</td>
<td class="amount {% if record.cumulative_balance < 0 %} text-danger {% endif %}">{{ record.cumulative_balance|accounting_amount }}</td>
<td class="actions">
{% if record.month is not None %}
<a class="btn btn-info" role="button" href="{% url "accounting:ledger" current_subject.code record.month|date:"Y-m" %}">
<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 %}
</tbody>
</table>
{# The list for small screens #}
<ul class="list-group d-sm-none">
{% for record in records %}
<li class="list-group-item {% if current_subject.code|first in "12" and record.balance < 0 %} list-group-item-danger {% endif %}">
{% if record.month is not None %}
<a class="list-group-item-action d-flex justify-content-between align-items-center" href="{% url "accounting:ledger" current_subject.code record.month|date:"Y-m" %}">
{{ record.label }}
<div>
<span class="badge badge-success badge-pill">
{{ record.debit_amount|accounting_amount }}
</span>
<span class="badge badge-warning badge-pill">
{{ record.credit_amount|accounting_amount }}
</span>
<span class="badge {% if record.balance < 0 %} badge-danger {% else %} badge-info {% endif %} badge-pill">
{{ record.balance|intcomma:False }}
</span>
<span class="badge {% if record.cumulative_balance < 0 %} badge-danger {% else %} badge-info {% endif %} badge-pill">
{{ record.cumulative_balance|intcomma:False }}
</span>
<span class="badge {% if record.balance < 0 %} badge-danger {% else %} badge-info {% endif %} badge-pill">
{{ record.balance|intcomma:False }}
</span>
<span class="badge {% if record.cumulative_balance < 0 %} badge-danger {% else %} badge-primary {% endif %} badge-pill">
{{ record.cumulative_balance|intcomma:False }}
</span>
</div>
</a>
{% else %}
<div class="d-flex justify-content-between align-items-center">
{{ record.label }}
<div>
<span class="badge badge-success badge-pill">
{{ record.debit_amount|accounting_amount }}
</span>
<span class="badge badge-warning badge-pill">
{{ record.credit_amount|accounting_amount }}
</span>
<span class="badge {% if record.balance < 0 %} badge-danger {% else %} badge-info {% endif %} badge-pill">
{{ record.balance|intcomma:False }}
</span>
<span class="badge {% if record.cumulative_balance < 0 %} badge-danger {% else %} badge-primary {% endif %} badge-pill">
{{ record.cumulative_balance|intcomma:False }}
</span>
</div>
</div>
{% endif %}
</li>
{% endfor %}
</ul>
{% else %}
<p>{{ _("There is currently no data.")|force_escape }}</p>
{% endif %}
{% endblock %}

View File

@ -55,7 +55,7 @@ urlpatterns = [
path("ledger-summary",
mia_core_views.todo, name="ledger-summary.home"),
path("ledger-summary/<str:subject_code>",
mia_core_views.todo, name="ledger-summary"),
views.ledger_summary, name="ledger-summary"),
path("journal",
mia_core_views.todo, name="journal.home"),
path("journal/<str:period_spec>",

View File

@ -455,3 +455,53 @@ def ledger(request, subject_code, period_spec):
"reports": ReportUrl(ledger=current_subject, period=period),
"subjects": subjects,
})
def ledger_summary(request, subject_code):
"""The ledger summary report."""
# The subject
subjects = _ledger_subjects()
current_subject = None
for subject in subjects:
if subject.code == subject_code:
current_subject = subject
if current_subject is None:
raise Http404()
if connection.vendor == "postgresql":
month_definition = "CAST(DATE_TRUNC('month', t.date) AS date)"
elif connection.vendor == "sqlite":
month_definition = "DATE(t.date, 'start of month')"
else:
month_definition = None
# The SQL query
records = list(RecordSummary.objects.raw(
f"""SELECT
{month_definition} AS month,
SUM(CASE WHEN r.is_credit THEN 0 ELSE r.amount END) AS debit_amount,
SUM(CASE WHEN r.is_credit THEN r.amount ELSE 0 END) AS credit_amount,
SUM(CASE WHEN r.is_credit THEN -1 ELSE 1 END * r.amount) AS balance
FROM accounting_records AS r
INNER JOIN accounting_transactions AS t ON r.transaction_sn = t.sn
INNER JOIN accounting_subjects AS s ON r.subject_sn = s.sn
WHERE s.code LIKE %s
GROUP BY month
ORDER BY month""",
[current_subject.code + "%"]))
cumulative_balance = 0
for record in records:
cumulative_balance = cumulative_balance + record.balance
record.cumulative_balance = cumulative_balance
records.append(RecordSummary(
label=pgettext("Accounting|", "Total"),
credit_amount=sum([x.credit_amount for x in records]),
debit_amount=sum([x.debit_amount for x in records]),
cumulative_balance=cumulative_balance,
))
pagination = Pagination(request, records, True)
return render(request, "accounting/ledger_summary.html", {
"records": pagination.records,
"pagination": pagination,
"current_subject": current_subject,
"reports": ReportUrl(cash=current_subject),
"subjects": subjects,
})