Added thje trial balance in the accounting application.

This commit is contained in:
依瑪貓 2020-07-19 18:50:56 +08:00
parent 36389623ef
commit 3cc7c5458b
5 changed files with 471 additions and 1 deletions

View File

@ -0,0 +1,212 @@
/* The Mia Website
* report.css: The style sheet for the accounting report
*/
/* Copyright (c) 2019-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: 2019/9/12
*/
.subject-picker {
height: auto;
max-height: 400px;
overflow-x: hidden;
}
.date-subject-line {
font-size: 0.833em;
}
.negative {
color: red;
}
.journal-credit {
padding-left: 1em;
}
/* The general journal tables */
.general-journal-table th, .general-journal-table td {
vertical-align: middle;
height: 50px;
}
/* The report block */
.report-block {
margin: 1em;
background-color: #E9ECEF;
border-radius: 0.3em;
box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19);
}
.report-block .table {
background-color: transparent;
}
.report-block h2 {
border-bottom: thick double slategray;
}
.report-block-lg {
padding: 2em 1.5em;
}
.report-block-sm {
padding: 1em 1em;
}
.report-block-lg table th, .report-block-lg table td {
vertical-align: middle;
height: 50px;
}
.report-block-sm .list-group-item {
background-color: transparent;
border: none;
}
/* The trial balance */
.trial-balance-table thead {
font-size: 1.1em;
}
.trial-balance-table tbody {
border-top: thick double slategray;
border-bottom: thick double slategray;
}
.trial-balance-table tfoot {
font-size: 1.1em;
font-weight: bolder;
}
.trial-balance-list .total {
border-top: thick double slategray;
font-weight: bolder;
font-size: 1.1em;
}
/* The income statement */
.income-statement-table thead {
font-size: 1.21em;
}
.income-statement-table tbody {
border-top: thick double slategray;
border-bottom: thick double slategray;
}
.income-statement-table tr {
height: 50px;
}
.income-statement-table td .subject {
text-indent: 2em;
}
.income-statement-table tr.first-level-header {
font-weight: bolder;
font-size: 1.21em;
}
.income-statement-table tr.second-level-header {
font-weight: bolder;
font-size: 1.1em;
}
.income-statement-table td .second-level-header {
text-indent: 1em;
}
.income-statement-table .total {
border-top: 1px solid slategray;
font-size: 1.1em;
font-weight: bolder;
}
.income-statement-table .cum-total {
font-size: 1.21em;
font-weight: bolder;
}
.income-statement-list .list-group-item {
background-color: transparent;
border: none;
}
.income-statement-list .first-level-header {
font-weight: bolder;
font-size: 1.21em;
}
.income-statement-list .second-level-header {
font-weight: bolder;
font-size: 1.1em;
}
.income-statement-list .total {
border-top: 1px solid slategray;
font-size: 1.1em;
font-weight: bolder;
}
.income-statement-list .cum-total {
font-weight: bolder;
font-size: 1.21em;
}
/* The balance sheet */
.balance-sheet-table thead {
font-size: 1.21em;
border-bottom: thick double slategray;
}
.balance-sheet-table tbody {
}
.balance-sheet-table .second-level-header {
font-size: 1.1em;
font-weight: bolder;
}
.balance-sheet-table td .subject {
text-indent: 1em;
}
.balance-sheet-table .total {
border-top: thick double slategray;
font-size: 1.1em;
font-weight: bolder;
}
.balance-sheet-total-table .total {
border-top: thick double slategray;
font-size: 1.21em;
font-weight: bolder;
}
.balance-sheet-list {
margin-bottom: 1em;
}
.balance-sheet-list .list-group-item {
background-color: transparent;
border: none;
}
.balance-sheet-list .section-title {
font-size: 1.21em;
font-weight: bolder;
border-bottom: thick double slategray;
}
.balance-sheet-list .second-level-header {
font-size: 1.1em;
font-weight: bolder;
}
.balance-sheet-list .total {
font-size: 1.1em;
font-weight: bolder;
border-top: thick double slategray;
}
.balance-sheet-list .grand-total {
font-size: 1.21em;
font-weight: bolder;
border-top: thick double slategray;
}
/* The search */
.btn-actions .btn .search-input {
height: calc(1em + .5rem + 2px);
border-radius: .2rem;
}
.btn-actions .btn .search-label {
margin-bottom: 0;
}
.btn-actions .btn .search-label button {
border: none;
background-color: transparent;
color: inherit;
padding-right: 0;
}

View File

@ -0,0 +1,172 @@
{% extends "base.html" %}
{% comment %}
The Mia Accounting Application
cash.html: The template for the cash account 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/1
{% endcomment %}
{% load static %}
{% load i18n %}
{% load humanize %}
{% load mia_core %}
{% load accounting %}
{% block settings %}
{% blocktrans asvar title with period=period.description context "Accounting|" %}Trial Balance in {{ period }}{% endblocktrans %}
{% setvar "title" title %}
{% setvar "use_period_chooser" True %}
{% static "accounting/css/report.css" as css %}
{% setvar "css" css %}
{% 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-book" %}
{% trans "Journal" context "Accounting|" as current_report_title %}
{% include "accounting/include/report-chooser.html" %}
{% endwith %}
<button type="button" class="btn btn-primary" data-toggle="modal" data-target="#period-modal">
<i class="far fa-calendar-alt"></i>
<span class="d-none d-md-inline">{{ period.description }}</span>
<span class="d-md-none">{% trans "Period" context "Period|" as text %}{{ text|force_escape }}</span>
</button>
</div>
{% include "mia_core/include/period-chooser.html" %}
{% if records %}
{% include "mia_core/include/pagination.html" %}
{# The table for large screens #}
<div class="d-none d-sm-block report-block report-block-lg">
<div class="row justify-content-center">
<h2>{{ title|force_escape }}</h2>
</div>
<div class="row">
<div class="col-sm-12">
<table class="table table-borderless table-hover trial-balance-table">
<thead>
<tr>
<th scope="col">{% trans "Subject" 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="actions" scope="col">{% trans "View" context "Accounting|" as text %}{{ text|force_escape }}</th>
</tr>
</thead>
<tbody>
{% for record in records %}
<tr>
<td>{{ record.title }}</td>
<td class="amount">{{ record.debit|accounting_amount }}</td>
<td class="amount">{{ record.credit|accounting_amount }}</td>
<td class="actions">
<a href="{% url "accounting:ledger" record.code period.spec %}" 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>
</td>
</tr>
{% endfor %}
</tbody>
<tfoot>
<tr>
<td>{% trans "Total" context "Accounting|" as text %}{{ text|force_escape }}</td>
<td class="amount">{{ record_sum.debit|accounting_amount }}</td>
<td class="amount">{{ record_sum.credit|accounting_amount }}</td>
<td></td>
</tr>
</tfoot>
</table>
</div>
</div>
</div>
{# The list for mobile browsers #}
<div class="d-sm-none report-block report-block-sm">
<div class="row justify-content-center">
<h2>{{ title|force_escape }}</h2>
</div>
<div class="row">
<div class="col-sm-12">
<ul class="list-group d-lg-none trial-balance-list">
{% for record in records %}
<li class="list-group-item">
<a class="list-group-item-action d-flex justify-content-between align-items-center" href="{% url "accounting:ledger" record.code period.spec %}">
{{ record.title }}
<div>
{% if record.debit is not None %}
<span class="badge badge-success badge-pill">
{{ record.debit|intcomma:False }}
</span>
{% endif %}
{% if record.credit is not None %}
<span class="badge badge-warning badge-pill">
{{ record.credit|intcomma:False }}
</span>
{% endif %}
</div>
</a>
</li>
{% endfor %}
<li class="list-group-item d-flex justify-content-between align-items-center total">
{% trans "Total" context "Accounting|" as text %}{{ text|force_escape }}
<div>
<span class="badge badge-success badge-pill">
{{ record_sum.debit|intcomma:False }}
</span>
<span class="badge badge-warning badge-pill">
{{ record_sum.credit|intcomma:False }}
</span>
</div>
</li>
</ul>
</div>
</div>
</div>
{% else %}
<p>{{ _("There is currently no data.")|force_escape }}</p>
{% endif %}
{% endblock %}

View File

@ -31,6 +31,7 @@ def accounting_amount(value):
return ""
if value == 0:
return "-"
print(value)
s = str(abs(value))
while True:
m = re.match("^([1-9][0-9]*)([0-9]{3})", s)

View File

@ -63,7 +63,7 @@ urlpatterns = [
path("trial-balance",
mia_core_views.todo, name="trial-balance.home"),
path("trial-balance/<str:period_spec>",
mia_core_views.todo, name="trial-balance"),
views.trial_balance, name="trial-balance"),
path("income-statement",
mia_core_views.todo, name="income-statement.home"),
path("income-statement/<str:period_spec>",

View File

@ -487,3 +487,88 @@ def journal(request, period_spec):
"pagination": pagination,
"period": period,
})
@require_GET
@digest_login_required
def trial_balance(request, period_spec):
"""The trial blanace."""
# 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()
data_end = last_txn.date if last_txn is not None else None
period = Period(period_spec, data_start, data_end)
# The accounts
nominal = list(
Subject.objects.filter(
Q(record__transaction__date__gte=period.start),
Q(record__transaction__date__lte=period.end),
~(Q(code__startswith="1")
| Q(code__startswith="2")
| Q(code__startswith="3")))
.annotate(
balance=Sum(Case(
When(record__is_credit=True, then=-1),
default=1) * F("record__amount")))
.filter(balance__isnull=False)
.annotate(
debit=Case(
When(balance__gt=0, then=F("balance")),
default=None),
credit=Case(
When(balance__lt=0, then=-F("balance")),
default=None)))
real = list(
Subject.objects
.filter(Q(record__transaction__date__lte=period.end),
(Q(code__startswith="1")
| Q(code__startswith="2")
| Q(code__startswith="3")),
~Q(code="3351"))
.annotate(
balance=Sum(Case(
When(record__is_credit=True, then=-1),
default=1) * F("record__amount")))
.filter(balance__isnull=False)
.annotate(
debit=Case(
When(balance__gt=0, then=F("balance")),
default=None),
credit=Case(
When(balance__lt=0, then=-F("balance")),
default=None)))
balance = Record.objects.filter(
(Q(transaction__date__lt=period.start)
& ~(Q(subject__code__startswith="1")
| Q(subject__code__startswith="2")
| Q(subject__code__startswith="3")))
| (Q(transaction__date__lte=period.end)
& Q(subject__code="3351")))\
.aggregate(
balance=Sum(Case(
When(is_credit=True, then=-1),
default=1) * F("amount")))["balance"]
if balance is not None and balance != 0:
brought_forward = Subject.objects.filter(code="3351").first()
if balance > 0:
brought_forward.debit = balance
brought_forward.credit = 0
else:
brought_forward.debit = None
brought_forward.credit = -balance
real.append(brought_forward)
records = nominal + real
records.sort(key=lambda x: x.code)
record_sum = Subject()
record_sum.title = pgettext("Accounting|", "Total")
record_sum.debit = sum([x.debit for x in records
if x.debit is not None])
record_sum.credit = sum([x.credit for x in records
if x.credit is not None])
return render(request, "accounting/trial-balance.html", {
"records": records,
"record_sum": record_sum,
"reports": ReportUrl(period=period),
"period": period,
})