dded the account_store() view in the accounting application.
This commit is contained in:
		@@ -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):
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user