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 import forms
|
||||||
from django.core.validators import RegexValidator
|
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 django.utils.translation import gettext as _
|
||||||
|
|
||||||
from .models import Account, Record
|
from .models import Account, Record
|
||||||
@ -317,7 +319,6 @@ class AccountForm(forms.Form):
|
|||||||
RegexValidator(
|
RegexValidator(
|
||||||
regex="^[1-9]+$",
|
regex="^[1-9]+$",
|
||||||
message=_("You can only use numbers 1-9 in the code.")),
|
message=_("You can only use numbers 1-9 in the code.")),
|
||||||
validate_account_code,
|
|
||||||
])
|
])
|
||||||
title = forms.CharField(
|
title = forms.CharField(
|
||||||
max_length=128,
|
max_length=128,
|
||||||
@ -328,6 +329,7 @@ class AccountForm(forms.Form):
|
|||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super(AccountForm, self).__init__(*args, **kwargs)
|
super(AccountForm, self).__init__(*args, **kwargs)
|
||||||
|
self.account = None
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def parent(self):
|
def parent(self):
|
||||||
@ -335,3 +337,109 @@ class AccountForm(forms.Form):
|
|||||||
if code is None or len(code) < 2:
|
if code is None or len(code) < 2:
|
||||||
return None
|
return None
|
||||||
return Account.objects.get(code=code[:-1])
|
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.db.models import Q
|
||||||
from django.urls import reverse
|
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):
|
class Account(DirtyFieldsMixin, models.Model):
|
||||||
@ -70,6 +71,19 @@ class Account(DirtyFieldsMixin, models.Model):
|
|||||||
"""Returns the string representation of this account."""
|
"""Returns the string representation of this account."""
|
||||||
return self.code.__str__() + " " + self.title
|
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:
|
class Meta:
|
||||||
db_table = "accounting_accounts"
|
db_table = "accounting_accounts"
|
||||||
|
|
||||||
|
@ -94,9 +94,8 @@ urlpatterns = [
|
|||||||
views.AccountListView.as_view(), name="accounts"),
|
views.AccountListView.as_view(), name="accounts"),
|
||||||
path("accounts/create",
|
path("accounts/create",
|
||||||
views.account_form, name="accounts.create"),
|
views.account_form, name="accounts.create"),
|
||||||
# TODO: To be done
|
|
||||||
path("accounts/store",
|
path("accounts/store",
|
||||||
mia_core_views.todo, name="accounts.store"),
|
views.account_store, name="accounts.store"),
|
||||||
path("api/accounts",
|
path("api/accounts",
|
||||||
views.api_account_list, name="api.accounts"),
|
views.api_account_list, name="api.accounts"),
|
||||||
path("api/accounts/options",
|
path("api/accounts/options",
|
||||||
@ -105,9 +104,8 @@ urlpatterns = [
|
|||||||
views.AccountView.as_view(), name="accounts.detail"),
|
views.AccountView.as_view(), name="accounts.detail"),
|
||||||
path("accounts/<account:account>/edit",
|
path("accounts/<account:account>/edit",
|
||||||
views.account_form, name="accounts.edit"),
|
views.account_form, name="accounts.edit"),
|
||||||
# TODO: To be done
|
|
||||||
path("accounts/<account:account>/update",
|
path("accounts/<account:account>/update",
|
||||||
mia_core_views.todo, name="accounts.update"),
|
views.account_store, name="accounts.update"),
|
||||||
# TODO: To be done
|
# TODO: To be done
|
||||||
path("accounts/<account:account>/delete",
|
path("accounts/<account:account>/delete",
|
||||||
mia_core_views.todo, name="accounts.delete"),
|
mia_core_views.todo, name="accounts.delete"),
|
||||||
|
@ -1053,11 +1053,48 @@ def account_form(request, account=None):
|
|||||||
})
|
})
|
||||||
else:
|
else:
|
||||||
form = AccountForm()
|
form = AccountForm()
|
||||||
|
form.account = account
|
||||||
return render(request, "accounting/account_form.html", {
|
return render(request, "accounting/account_form.html", {
|
||||||
"form": form,
|
"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
|
@require_GET
|
||||||
@login_required
|
@login_required
|
||||||
def api_account_list(request):
|
def api_account_list(request):
|
||||||
|
Loading…
Reference in New Issue
Block a user