Added the get_summary_categories() utility and put the categories in the transaction forms in the accounting application.

This commit is contained in:
依瑪貓 2020-08-04 09:55:27 +08:00
parent 12d4af06a0
commit 245e4c6573
5 changed files with 56 additions and 14 deletions

View File

@ -44,9 +44,7 @@ First written: 2020/7/23
{# TODO: To be done #} {# TODO: To be done #}
<input id="l10n-messages" type="hidden" value="{{ l10n_messages }}" /> <input id="l10n-messages" type="hidden" value="{{ l10n_messages }}" />
<input id="account-option-url" type="hidden" value="{% url "accounting:accounts.options" %}" /> <input id="account-option-url" type="hidden" value="{% url "accounting:accounts.options" %}" />
<input id="debit-general-categories" type="hidden" value="{{ debit_general_categories }}" /> <input id="summary-categories" type="hidden" value="{{ summary_categories }}" />
<input id="debit-travel-categories" type="hidden" value="{{ debit_travel_categories }}" />
<input id="debit-bus-categories" type="hidden" value="{{ debit_bus_categories }}" />
<div class="row form-group"> <div class="row form-group">
<div class="col-sm-2"> <div class="col-sm-2">
<label for="txn-date">{% trans "Date:" context "Accounting|" as text %}{{ text|force_escape }}</label> <label for="txn-date">{% trans "Date:" context "Accounting|" as text %}{{ text|force_escape }}</label>

View File

@ -44,9 +44,7 @@ First written: 2020/7/23
{# TODO: To be done #} {# TODO: To be done #}
<input id="l10n-messages" type="hidden" value="{{ l10n_messages }}" /> <input id="l10n-messages" type="hidden" value="{{ l10n_messages }}" />
<input id="account-option-url" type="hidden" value="{% url "accounting:accounts.options" %}" /> <input id="account-option-url" type="hidden" value="{% url "accounting:accounts.options" %}" />
<input id="credit-general-categories" type="hidden" value="{{ credit_general_categories }}" /> <input id="summary-categories" type="hidden" value="{{ summary_categories }}" />
<input id="credit-travel-categories" type="hidden" value="{{ credit_travel_categories }}" />
<input id="credit-bus-categories" type="hidden" value="{{ credit_bus_categories }}" />
<div class="row form-group"> <div class="row form-group">
<div class="col-sm-2"> <div class="col-sm-2">
<label for="txn-date">{% trans "Date:" context "Accounting|" as text %}{{ text|force_escape }}</label> <label for="txn-date">{% trans "Date:" context "Accounting|" as text %}{{ text|force_escape }}</label>

View File

@ -44,12 +44,7 @@ First written: 2020/7/23
{# TODO: To be done #} {# TODO: To be done #}
<input id="l10n-messages" type="hidden" value="{{ l10n_messages }}" /> <input id="l10n-messages" type="hidden" value="{{ l10n_messages }}" />
<input id="account-option-url" type="hidden" value="{% url "accounting:accounts.options" %}" /> <input id="account-option-url" type="hidden" value="{% url "accounting:accounts.options" %}" />
<input id="debit-general-categories" type="hidden" value="{{ debit_general_categories }}" /> <input id="summary-categories" type="hidden" value="{{ summary_categories }}" />
<input id="debit-travel-categories" type="hidden" value="{{ debit_travel_categories }}" />
<input id="debit-bus-categories" type="hidden" value="{{ debit_bus_categories }}" />
<input id="credit-general-categories" type="hidden" value="{{ credit_general_categories }}" />
<input id="credit-travel-categories" type="hidden" value="{{ credit_travel_categories }}" />
<input id="credit-bus-categories" type="hidden" value="{{ credit_bus_categories }}" />
<div class="row form-group"> <div class="row form-group">
<div class="col-sm-2"> <div class="col-sm-2">
<label for="txn-date">{% trans "Date:" context "Accounting|" as text %}{{ text|force_escape }}</label> <label for="txn-date">{% trans "Date:" context "Accounting|" as text %}{{ text|force_escape }}</label>

View File

@ -18,11 +18,14 @@
"""The utilities of the accounting application. """The utilities of the accounting application.
""" """
import json
import re import re
from django.conf import settings from django.conf import settings
from django.core.exceptions import ObjectDoesNotExist from django.core.exceptions import ObjectDoesNotExist
from django.db.models import Q, Sum, Case, When, F, Count, Max, Min from django.db.models import Q, Sum, Case, When, F, Count, Max, Min, Value, \
CharField
from django.db.models.functions import StrIndex, Left
from django.urls import reverse from django.urls import reverse
from django.utils import timezone from django.utils import timezone
from django.utils.translation import pgettext from django.utils.translation import pgettext
@ -319,6 +322,52 @@ def find_order_holes(records):
record.has_order_hole = record.transaction.date in holes record.has_order_hole = record.transaction.date in holes
def get_summary_categories():
"""Finds and returns the summary categories and their corresponding account
hints.
Returns:
dict[str,str]: The summary categories and their account hints, by
their record types and category types.
"""
filters = {
"general": Q(summary__contains="") & ~Q(summary__regex=".+—.+→.+"),
"travel": Q(summary__regex=".+—.+→.+")
& ~Q(summary__regex=".+—.+—.+→.+"),
"bus": Q(summary__regex=".+—.+—.+→.+"),
}
categories = {
"credit": {},
"debit": {},
}
for cat_type in filters:
rows = Record.objects\
.filter(
~Q(account__code__startswith="114"),
~Q(account__code__startswith="214"),
~Q(account__code__startswith="128"),
~Q(account__code__startswith="228"),
filters[cat_type])\
.annotate(category=Left("summary",
StrIndex("summary", Value("")) - 1,
output_field=CharField()))\
.values("category", "account__code", "is_credit")\
.annotate(count=Count("category"))\
.order_by("category", "is_credit", "-count", "account__code")
for row in rows:
rec_type = "credit" if row["is_credit"] else "debit"
if cat_type not in categories[rec_type]:
categories[rec_type][cat_type] = {}
if row["category"] not in categories[rec_type][cat_type]:
categories[rec_type][cat_type][row["category"]]\
= row["account__code"]
return {F"{r}-{t}": json.dumps(
[{"category": c, "account": categories[r][t][c]}
for c in categories[r][t]])
for r in categories
for t in categories[r]}
def fill_txn_from_post(txn_type, txn, post): def fill_txn_from_post(txn_type, txn, post):
"""Fills the transaction from the POSTed data. The POSTed data must be """Fills the transaction from the POSTed data. The POSTed data must be
validated and clean at this moment. validated and clean at this moment.

View File

@ -42,7 +42,8 @@ from .models import Record, Transaction, Account
from .utils import ReportUrl, get_cash_accounts, get_ledger_accounts, \ from .utils import ReportUrl, get_cash_accounts, get_ledger_accounts, \
find_imbalanced, find_order_holes, fill_txn_from_post, \ find_imbalanced, find_order_holes, fill_txn_from_post, \
sort_post_txn_records, make_txn_form_from_status, \ sort_post_txn_records, make_txn_form_from_status, \
make_txn_form_from_model, make_txn_form_from_post, MonthlySummary make_txn_form_from_model, make_txn_form_from_post, MonthlySummary, \
get_summary_categories
@method_decorator(require_GET, name="dispatch") @method_decorator(require_GET, name="dispatch")
@ -836,6 +837,7 @@ def txn_edit(request, txn_type, txn=None):
form = make_txn_form_from_model(txn_type, txn) form = make_txn_form_from_model(txn_type, txn)
return render(request, F"accounting/transactions/{txn_type}/form.html", { return render(request, F"accounting/transactions/{txn_type}/form.html", {
"item": form, "item": form,
"summary_categories": get_summary_categories,
}) })