Replaced the function-based txn_sort view with the class-based TransactionSortView in the accounting application.
This commit is contained in:
		| @@ -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( | ||||
|   | ||||
| @@ -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"> | ||||
|   | ||||
| @@ -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", | ||||
|   | ||||
| @@ -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. | ||||
|  | ||||
|     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: | ||||
|     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 | ||||
|     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.") | ||||
|         return form | ||||
|  | ||||
|         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") | ||||
|   | ||||
		Reference in New Issue
	
	Block a user