Replaced the function-based txn_sort view with the class-based TransactionSortView in the accounting application.

This commit is contained in:
依瑪貓 2020-08-16 22:38:35 +08:00
parent b829002c61
commit 8f6c8f3497
4 changed files with 78 additions and 59 deletions

View File

@ -18,8 +18,9 @@
"""The forms of the Mia core application.
"""
import datetime
import re
from typing import Optional
from typing import Optional, List, Dict
from django import forms
from django.core.validators import RegexValidator
@ -27,7 +28,7 @@ from django.db.models import Q, Max
from django.db.models.functions import Length
from django.utils.translation import gettext as _
from .models import Account, Record
from .models import Account, Record, Transaction
from .validators import validate_record_account_code, validate_record_id
@ -342,6 +343,41 @@ class TransactionForm(forms.Form):
if "amount" in x.data and "amount" not in x.errors])
class TransactionSortForm(forms.Form):
"""A form to sort the transactions in a same day."""
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.date = None
self.txn_list: Optional[List[Transaction]] = None
self.txn_orders: List[TransactionSortForm.Order] = []
@staticmethod
def from_post(date: datetime.date, post: Dict[str, str]):
form = TransactionSortForm({})
form.date = date
post_orders: List[TransactionSortForm.Order] = []
for txn in Transaction.objects.filter(date=date).all():
key = F"transaction-{txn.pk}-ord"
if key not in post:
post_orders.append(form.Order(txn, 9999))
elif not re.match("^[0-9]+$", post[key]):
post_orders.append(form.Order(txn, 9999))
else:
post_orders.append(form.Order(txn, int(post[key])))
post_orders.sort(key=lambda x: (x.order, x.txn.ord))
form.txn_orders = []
for i in range(len(post_orders)):
form.txn_orders.append(form.Order(post_orders[i].txn, i + 1))
form.txn_list = [x.txn for x in form.txn_orders]
return form
class Order:
"""A transaction order"""
def __init__(self, txn: Transaction, order: int):
self.txn = txn
self.order = order
class AccountForm(forms.Form):
"""An account form."""
code = forms.CharField(

View File

@ -27,7 +27,7 @@ First written: 2020/8/6
{% load accounting %}
{% block settings %}
{% blocktrans asvar title with date=date|smart_date %}Reorder the Transactions in {{ date }}{% endblocktrans %}
{% blocktrans asvar title with date=form.date|smart_date %}Reorder the Transactions in {{ date }}{% endblocktrans %}
{% setvar "title" title %}
{% setvar "use_jqueryui" True %}
{% static "accounting/css/report.css" as file %}{% add_css file %}
@ -50,12 +50,12 @@ First written: 2020/8/6
</div>
<div id="txn-date" class="col-sm-10">
{{ date|smart_date }}
{{ form.date|smart_date }}
</div>
</div>
{% if txn_list|length > 1 %}
<form action="{% url "accounting:transactions.sort" date as url %}{% url_keep_return url %}" method="post">
{% if form.txn_list|length > 1 %}
<form action="{% url "accounting:transactions.sort" form.date as url %}{% url_keep_return url %}" method="post">
{% csrf_token %}
<table class="table general-journal-table">
<thead>
@ -68,7 +68,7 @@ First written: 2020/8/6
</tr>
</thead>
<tbody id="transactions">
{% for txn in txn_list %}
{% for txn in form.txn_list %}
<tr id="transaction-{{ txn.pk }}" class="transaction {% if not txn.is_balanced %} table-danger {% endif %}">
<td class="actions">
<div class="btn-group">

View File

@ -84,7 +84,7 @@ urlpatterns = [
path("transactions/<txn:txn>/delete",
views.TransactionDeleteView.as_view(), name="transactions.delete"),
path("transactions/sort/<date:date>",
views.txn_sort, name="transactions.sort"),
views.TransactionSortFormView.as_view(), name="transactions.sort"),
path("accounts",
views.AccountListView.as_view(), name="accounts"),
path("accounts/create",

View File

@ -18,7 +18,6 @@
"""The view controllers of the accounting application.
"""
import datetime
import json
import re
from typing import Dict, Optional
@ -39,14 +38,13 @@ from django.utils.translation import gettext as _, gettext_noop
from django.views.decorators.http import require_GET, require_POST
from django.views.generic import RedirectView, ListView, DetailView
from mia_core import stored_post
from mia_core.digest_auth import login_required
from mia_core.period import Period
from mia_core.utils import Pagination, get_multi_lingual_search, UrlBuilder, \
strip_post, PaginationException
from mia_core.utils import Pagination, get_multi_lingual_search, \
PaginationException
from mia_core.views import DeleteView, FormView
from . import utils
from .forms import AccountForm, TransactionForm
from .forms import AccountForm, TransactionForm, TransactionSortForm
from .models import Record, Transaction, Account
@ -882,59 +880,44 @@ class TransactionDeleteView(DeleteView):
return self.request.GET.get("r") or reverse("accounting:home")
@login_required
def txn_sort(request: HttpRequest, date: datetime.date) -> HttpResponse:
"""The view for the form to sort the transactions in a same day.
@method_decorator(login_required, name="dispatch")
class TransactionSortFormView(FormView):
"""The form to sort the transactions in a same day."""
template_name = "accounting/transaction-sort.html"
form_class = TransactionSortForm
not_modified_message = gettext_noop(
"The transaction orders were not modified.")
success_message = gettext_noop(
"The transaction orders were saved successfully.")
Args:
request: The request.
date: The day.
def get_form(self, **kwargs):
"""Returns the form for the template."""
form = super().get_form()
if form.txn_list is None:
form.date = self.kwargs["date"]
form.txn_list = Transaction.objects.filter(date=form.date)\
.order_by("ord").all()
if len(form.txn_list) < 2:
raise Http404
return form
Returns:
The response.
Raises:
Http404: When there are less than two transactions in this day.
"""
transactions = Transaction.objects.filter(date=date).order_by("ord")
if len(transactions) < 2:
raise Http404
if request.method != "POST":
return render(request, "accounting/transaction-sort.html", {
"txn_list": transactions,
"date": date,
})
else:
post = request.POST.dict()
errors = {}
for txn in transactions:
key = F"transaction-{txn.pk}-ord"
if key not in post:
errors[key] = gettext_noop("Invalid arguments.")
elif not re.match("^[1-9][0-9]*", post[key]):
errors[key] = gettext_noop("Invalid order.")
if len(errors) > 0:
return stored_post.error_redirect(
request, reverse("accounting:transactions.sort"), post)
keys = [F"transaction-{x.pk}-ord" for x in transactions]
keys.sort(key=lambda x: int(post[x]))
for i in range(len(keys)):
post[keys[i]] = i + 1
modified = [[x, post[F"transaction-{x.pk}-ord"]] for x in transactions
if x.ord != post[F"transaction-{x.pk}-ord"]]
def make_form_from_post(self, post: Dict[str, str]) -> TransactionSortForm:
"""Creates and returns the form from the POST data."""
return TransactionSortForm.from_post(self.kwargs["date"], post)
def form_valid(self, form: TransactionSortForm) -> HttpResponseRedirect:
"""Handles the action when the POST form is valid."""
modified = [x for x in form.txn_orders if x.txn.ord != x.order]
if len(modified) == 0:
message = gettext_noop("The transaction orders were not modified.")
message = self.get_not_modified_message(form.cleaned_data)
else:
with transaction.atomic():
for x in modified:
Transaction.objects.filter(pk=x[0].pk).update(ord=x[1])
message = gettext_noop(
"The transaction orders were saved successfully.")
messages.success(request, message)
return redirect(request.GET.get("r") or reverse("accounting:home"))
Transaction.objects.filter(pk=x.txn.pk).update(ord=x.order)
message = self.get_success_message(form.cleaned_data)
messages.success(self.request, message)
return redirect(self.request.GET.get("r")
or reverse("accounting:home"))
@method_decorator(require_GET, name="dispatch")