dded the account_store() view in the accounting application.
This commit is contained in:
parent
7c218cbc76
commit
00ee0cc3bb
@ -22,6 +22,8 @@ import re
|
||||
|
||||
from django import forms
|
||||
from django.core.validators import RegexValidator
|
||||
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
|
||||
@ -317,7 +319,6 @@ class AccountForm(forms.Form):
|
||||
RegexValidator(
|
||||
regex="^[1-9]+$",
|
||||
message=_("You can only use numbers 1-9 in the code.")),
|
||||
validate_account_code,
|
||||
])
|
||||
title = forms.CharField(
|
||||
max_length=128,
|
||||
@ -328,6 +329,7 @@ class AccountForm(forms.Form):
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(AccountForm, self).__init__(*args, **kwargs)
|
||||
self.account = None
|
||||
|
||||
@property
|
||||
def parent(self):
|
||||
@ -335,3 +337,109 @@ class AccountForm(forms.Form):
|
||||
if code is None or len(code) < 2:
|
||||
return None
|
||||
return Account.objects.get(code=code[:-1])
|
||||
|
||||
def clean(self):
|
||||
"""Validates the form globally.
|
||||
|
||||
Raises:
|
||||
ValidationError: When the validation fails.
|
||||
"""
|
||||
errors = []
|
||||
validators = [self._validate_code_not_under_myself,
|
||||
self._validate_code_unique,
|
||||
self._validate_code_parent_exists,
|
||||
self._validate_code_descendant_code_size]
|
||||
for validator in validators:
|
||||
try:
|
||||
validator()
|
||||
except forms.ValidationError as e:
|
||||
errors.append(e)
|
||||
if errors:
|
||||
raise forms.ValidationError(errors)
|
||||
|
||||
def _validate_code_not_under_myself(self):
|
||||
"""Validates whether the code is under itself.
|
||||
|
||||
Raises:
|
||||
ValidationError: When the validation fails.
|
||||
"""
|
||||
if self.account is None:
|
||||
return
|
||||
if "code" not in self.data:
|
||||
return
|
||||
if self.data["code"] == self.account.code:
|
||||
return
|
||||
if not self.data["code"].startswith(self.account.code):
|
||||
return
|
||||
error = forms.ValidationError(
|
||||
_("You cannot set the code under itself."),
|
||||
code="not_under_myself")
|
||||
self.add_error("code", error)
|
||||
raise error
|
||||
|
||||
def _validate_code_unique(self):
|
||||
"""Validates whether the code is unique.
|
||||
|
||||
Raises:
|
||||
ValidationError: When the validation fails.
|
||||
"""
|
||||
if "code" not in self.data:
|
||||
return
|
||||
try:
|
||||
if self.account is None:
|
||||
Account.objects.get(code=self.data["code"])
|
||||
else:
|
||||
Account.objects.get(Q(code=self.data["code"]),
|
||||
~Q(pk=self.account.pk))
|
||||
except Account.DoesNotExist:
|
||||
return
|
||||
error = forms.ValidationError(_("This code is already in use."),
|
||||
code="code_unique")
|
||||
self.add_error("code", error)
|
||||
raise error
|
||||
|
||||
def _validate_code_parent_exists(self):
|
||||
"""Validates whether the parent account exists.
|
||||
|
||||
Raises:
|
||||
ValidationError: When the validation fails.
|
||||
"""
|
||||
if "code" not in self.data:
|
||||
return
|
||||
if len(self.data["code"]) < 2:
|
||||
return
|
||||
try:
|
||||
Account.objects.get(code=self.data["code"][:-1])
|
||||
except Account.DoesNotExist:
|
||||
error = forms.ValidationError(
|
||||
_("The parent account of this code does not exist."),
|
||||
code="code_unique")
|
||||
self.add_error("code", error)
|
||||
raise error
|
||||
return
|
||||
|
||||
def _validate_code_descendant_code_size(self):
|
||||
"""Validates whether the codes of the descendants will be too long.
|
||||
|
||||
Raises:
|
||||
ValidationError: When the validation fails.
|
||||
"""
|
||||
if "code" not in self.data:
|
||||
return
|
||||
if self.account is None:
|
||||
return
|
||||
cur_max_len = Account.objects\
|
||||
.filter(Q(code__startswith=self.account.code),
|
||||
~Q(pk=self.account.pk))\
|
||||
.aggregate(max_len=Max(Length("code")))["max_len"]
|
||||
if cur_max_len is None:
|
||||
return True
|
||||
new_max_len = cur_max_len - len(self.account.code)\
|
||||
+ len(self.data["code"])
|
||||
if new_max_len <= 5:
|
||||
return
|
||||
error = forms.ValidationError(
|
||||
_("The descendant account codes will be too long (max. 5)."),
|
||||
code="descendant_code_size")
|
||||
self.add_error("code", error)
|
||||
raise error
|
||||
|
@ -24,7 +24,8 @@ from django.db import models, transaction
|
||||
from django.db.models import Q
|
||||
from django.urls import reverse
|
||||
|
||||
from mia_core.utils import get_multi_lingual_attr, set_multi_lingual_attr
|
||||
from mia_core.utils import get_multi_lingual_attr, set_multi_lingual_attr, \
|
||||
new_pk
|
||||
|
||||
|
||||
class Account(DirtyFieldsMixin, models.Model):
|
||||
@ -70,6 +71,19 @@ class Account(DirtyFieldsMixin, models.Model):
|
||||
"""Returns the string representation of this account."""
|
||||
return self.code.__str__() + " " + self.title
|
||||
|
||||
def save(self, current_user=None, force_insert=False, force_update=False,
|
||||
using=None, update_fields=None):
|
||||
self.parent = None if len(self.code) == 1\
|
||||
else Account.objects.get(code=self.code[:-1])
|
||||
if self.pk is None:
|
||||
self.pk = new_pk(Account)
|
||||
self.created_by = current_user
|
||||
self.updated_by = current_user
|
||||
with transaction.atomic():
|
||||
super(Account, self).save(
|
||||
force_insert=force_insert, force_update=force_update,
|
||||
using=using, update_fields=update_fields)
|
||||
|
||||
class Meta:
|
||||
db_table = "accounting_accounts"
|
||||
|
||||
|
@ -94,9 +94,8 @@ urlpatterns = [
|
||||
views.AccountListView.as_view(), name="accounts"),
|
||||
path("accounts/create",
|
||||
views.account_form, name="accounts.create"),
|
||||
# TODO: To be done
|
||||
path("accounts/store",
|
||||
mia_core_views.todo, name="accounts.store"),
|
||||
views.account_store, name="accounts.store"),
|
||||
path("api/accounts",
|
||||
views.api_account_list, name="api.accounts"),
|
||||
path("api/accounts/options",
|
||||
@ -105,9 +104,8 @@ urlpatterns = [
|
||||
views.AccountView.as_view(), name="accounts.detail"),
|
||||
path("accounts/<account:account>/edit",
|
||||
views.account_form, name="accounts.edit"),
|
||||
# TODO: To be done
|
||||
path("accounts/<account:account>/update",
|
||||
mia_core_views.todo, name="accounts.update"),
|
||||
views.account_store, name="accounts.update"),
|
||||
# TODO: To be done
|
||||
path("accounts/<account:account>/delete",
|
||||
mia_core_views.todo, name="accounts.delete"),
|
||||
|
@ -1053,11 +1053,48 @@ def account_form(request, account=None):
|
||||
})
|
||||
else:
|
||||
form = AccountForm()
|
||||
form.account = account
|
||||
return render(request, "accounting/account_form.html", {
|
||||
"form": form,
|
||||
})
|
||||
|
||||
|
||||
@require_POST
|
||||
@login_required
|
||||
def account_store(request, account=None):
|
||||
"""The view to edit an accounting transaction.
|
||||
|
||||
Args:
|
||||
request (HttpRequest): The request.
|
||||
account (Account): The account.
|
||||
|
||||
Returns:
|
||||
HttpResponseRedirect: The response.
|
||||
"""
|
||||
post = request.POST.dict()
|
||||
strip_post(post)
|
||||
form = AccountForm(post)
|
||||
form.account = account
|
||||
if not form.is_valid():
|
||||
if account is None:
|
||||
url = reverse("accounting:accounts.create")
|
||||
else:
|
||||
url = reverse("accounting:accounts.edit", args=(account,))
|
||||
return stored_post.error_redirect(request, url, post)
|
||||
if account is None:
|
||||
account = Account()
|
||||
account.code = form["code"].value()
|
||||
account.title = form["title"].value()
|
||||
if not account.is_dirty():
|
||||
message = gettext_noop("This account was not modified.")
|
||||
else:
|
||||
account.save(current_user=request.user)
|
||||
message = gettext_noop("This account was saved successfully.")
|
||||
messages.success(request, message)
|
||||
return HttpResponseRedirect(reverse("accounting:accounts.detail",
|
||||
args=(account,)))
|
||||
|
||||
|
||||
@require_GET
|
||||
@login_required
|
||||
def api_account_list(request):
|
||||
|
Loading…
Reference in New Issue
Block a user