Replaced the function-based txn_sort view with the class-based TransactionSortView in the accounting application.
This commit is contained in:
parent
b829002c61
commit
8f6c8f3497
@ -18,8 +18,9 @@
|
|||||||
"""The forms of the Mia core application.
|
"""The forms of the Mia core application.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
import datetime
|
||||||
import re
|
import re
|
||||||
from typing import Optional
|
from typing import Optional, List, Dict
|
||||||
|
|
||||||
from django import forms
|
from django import forms
|
||||||
from django.core.validators import RegexValidator
|
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.db.models.functions import Length
|
||||||
from django.utils.translation import gettext as _
|
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
|
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])
|
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):
|
class AccountForm(forms.Form):
|
||||||
"""An account form."""
|
"""An account form."""
|
||||||
code = forms.CharField(
|
code = forms.CharField(
|
||||||
|
@ -27,7 +27,7 @@ First written: 2020/8/6
|
|||||||
{% load accounting %}
|
{% load accounting %}
|
||||||
|
|
||||||
{% block settings %}
|
{% 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 "title" title %}
|
||||||
{% setvar "use_jqueryui" True %}
|
{% setvar "use_jqueryui" True %}
|
||||||
{% static "accounting/css/report.css" as file %}{% add_css file %}
|
{% static "accounting/css/report.css" as file %}{% add_css file %}
|
||||||
@ -50,12 +50,12 @@ First written: 2020/8/6
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="txn-date" class="col-sm-10">
|
<div id="txn-date" class="col-sm-10">
|
||||||
{{ date|smart_date }}
|
{{ form.date|smart_date }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{% if txn_list|length > 1 %}
|
{% if form.txn_list|length > 1 %}
|
||||||
<form action="{% url "accounting:transactions.sort" date as url %}{% url_keep_return url %}" method="post">
|
<form action="{% url "accounting:transactions.sort" form.date as url %}{% url_keep_return url %}" method="post">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
<table class="table general-journal-table">
|
<table class="table general-journal-table">
|
||||||
<thead>
|
<thead>
|
||||||
@ -68,7 +68,7 @@ First written: 2020/8/6
|
|||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody id="transactions">
|
<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 %}">
|
<tr id="transaction-{{ txn.pk }}" class="transaction {% if not txn.is_balanced %} table-danger {% endif %}">
|
||||||
<td class="actions">
|
<td class="actions">
|
||||||
<div class="btn-group">
|
<div class="btn-group">
|
||||||
|
@ -84,7 +84,7 @@ urlpatterns = [
|
|||||||
path("transactions/<txn:txn>/delete",
|
path("transactions/<txn:txn>/delete",
|
||||||
views.TransactionDeleteView.as_view(), name="transactions.delete"),
|
views.TransactionDeleteView.as_view(), name="transactions.delete"),
|
||||||
path("transactions/sort/<date:date>",
|
path("transactions/sort/<date:date>",
|
||||||
views.txn_sort, name="transactions.sort"),
|
views.TransactionSortFormView.as_view(), name="transactions.sort"),
|
||||||
path("accounts",
|
path("accounts",
|
||||||
views.AccountListView.as_view(), name="accounts"),
|
views.AccountListView.as_view(), name="accounts"),
|
||||||
path("accounts/create",
|
path("accounts/create",
|
||||||
|
@ -18,7 +18,6 @@
|
|||||||
"""The view controllers of the accounting application.
|
"""The view controllers of the accounting application.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
import datetime
|
|
||||||
import json
|
import json
|
||||||
import re
|
import re
|
||||||
from typing import Dict, Optional
|
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.decorators.http import require_GET, require_POST
|
||||||
from django.views.generic import RedirectView, ListView, DetailView
|
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.digest_auth import login_required
|
||||||
from mia_core.period import Period
|
from mia_core.period import Period
|
||||||
from mia_core.utils import Pagination, get_multi_lingual_search, UrlBuilder, \
|
from mia_core.utils import Pagination, get_multi_lingual_search, \
|
||||||
strip_post, PaginationException
|
PaginationException
|
||||||
from mia_core.views import DeleteView, FormView
|
from mia_core.views import DeleteView, FormView
|
||||||
from . import utils
|
from . import utils
|
||||||
from .forms import AccountForm, TransactionForm
|
from .forms import AccountForm, TransactionForm, TransactionSortForm
|
||||||
from .models import Record, Transaction, Account
|
from .models import Record, Transaction, Account
|
||||||
|
|
||||||
|
|
||||||
@ -882,59 +880,44 @@ class TransactionDeleteView(DeleteView):
|
|||||||
return self.request.GET.get("r") or reverse("accounting:home")
|
return self.request.GET.get("r") or reverse("accounting:home")
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@method_decorator(login_required, name="dispatch")
|
||||||
def txn_sort(request: HttpRequest, date: datetime.date) -> HttpResponse:
|
class TransactionSortFormView(FormView):
|
||||||
"""The view for the form to sort the transactions in a same day.
|
"""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:
|
def get_form(self, **kwargs):
|
||||||
request: The request.
|
"""Returns the form for the template."""
|
||||||
date: The day.
|
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:
|
def make_form_from_post(self, post: Dict[str, str]) -> TransactionSortForm:
|
||||||
The response.
|
"""Creates and returns the form from the POST data."""
|
||||||
|
return TransactionSortForm.from_post(self.kwargs["date"], post)
|
||||||
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 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:
|
if len(modified) == 0:
|
||||||
message = gettext_noop("The transaction orders were not modified.")
|
message = self.get_not_modified_message(form.cleaned_data)
|
||||||
else:
|
else:
|
||||||
with transaction.atomic():
|
with transaction.atomic():
|
||||||
for x in modified:
|
for x in modified:
|
||||||
Transaction.objects.filter(pk=x[0].pk).update(ord=x[1])
|
Transaction.objects.filter(pk=x.txn.pk).update(ord=x.order)
|
||||||
message = gettext_noop(
|
message = self.get_success_message(form.cleaned_data)
|
||||||
"The transaction orders were saved successfully.")
|
messages.success(self.request, message)
|
||||||
messages.success(request, message)
|
return redirect(self.request.GET.get("r")
|
||||||
return redirect(request.GET.get("r") or reverse("accounting:home"))
|
or reverse("accounting:home"))
|
||||||
|
|
||||||
|
|
||||||
@method_decorator(require_GET, name="dispatch")
|
@method_decorator(require_GET, name="dispatch")
|
||||||
|
Loading…
Reference in New Issue
Block a user