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 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):
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user