diff --git a/accounting/static/accounting/css/report.css b/accounting/static/accounting/css/report.css new file mode 100644 index 0000000..c3581fb --- /dev/null +++ b/accounting/static/accounting/css/report.css @@ -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; +} diff --git a/accounting/templates/accounting/trial-balance.html b/accounting/templates/accounting/trial-balance.html new file mode 100644 index 0000000..baed463 --- /dev/null +++ b/accounting/templates/accounting/trial-balance.html @@ -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 %} + +
+
+ + +
+ {% with current_report_icon="fas fa-book" %} + {% trans "Journal" context "Accounting|" as current_report_title %} + {% include "accounting/include/report-chooser.html" %} + {% endwith %} + +
+ +{% include "mia_core/include/period-chooser.html" %} + +{% if records %} + {% include "mia_core/include/pagination.html" %} + + {# The table for large screens #} +
+
+

{{ title|force_escape }}

+
+ +
+
+ + + + + + + + + + + {% for record in records %} + + + + + + + {% endfor %} + + + + + + + + + +
{% trans "Subject" context "Accounting|" as text %}{{ text|force_escape }}{% trans "Debit" context "Accounting|" as text %}{{ text|force_escape }}{% trans "Credit" context "Accounting|" as text %}{{ text|force_escape }}{% trans "View" context "Accounting|" as text %}{{ text|force_escape }}
{{ record.title }}{{ record.debit|accounting_amount }}{{ record.credit|accounting_amount }} + + + {% trans "View" context "Accounting|" as text %}{{ text|force_escape }} + +
{% trans "Total" context "Accounting|" as text %}{{ text|force_escape }}{{ record_sum.debit|accounting_amount }}{{ record_sum.credit|accounting_amount }}
+
+
+
+ + {# The list for mobile browsers #} +
+
+

{{ title|force_escape }}

+
+ +
+
+ +
+
+
+{% else %} +

{{ _("There is currently no data.")|force_escape }}

+{% endif %} + +{% endblock %} diff --git a/accounting/templatetags/accounting.py b/accounting/templatetags/accounting.py index 2eaa574..99db968 100644 --- a/accounting/templatetags/accounting.py +++ b/accounting/templatetags/accounting.py @@ -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) diff --git a/accounting/urls.py b/accounting/urls.py index 2390540..f47fcc4 100644 --- a/accounting/urls.py +++ b/accounting/urls.py @@ -63,7 +63,7 @@ urlpatterns = [ path("trial-balance", mia_core_views.todo, name="trial-balance.home"), path("trial-balance/", - 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/", diff --git a/accounting/views/__init__.py b/accounting/views/__init__.py index 9a08ea0..9290fee 100644 --- a/accounting/views/__init__.py +++ b/accounting/views/__init__.py @@ -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, + })