Source code for fiscalyears.forms

import datetime

from dateutil import relativedelta
from django import forms
from django.forms.models import modelformset_factory
from parsley.decorators import parsleyfy

from accounts.models import Account
from entries.models import Transaction

from .fiscalyears import get_start_of_current_fiscal_year
from .models import FiscalYear


@parsleyfy
[docs]class FiscalYearForm(forms.ModelForm): """ This form is used to create new :class:`Fiscal Years<.models.FiscalYear>`. The form validates the period length against the new Fiscal Year's End Month and Year. To pass validation, the new Year must be greater than any previous years and the end Month must create a period less than or equal to the selected Period. .. seealso:: View :func:`~.views.add_fiscal_year` The :func:`~.views.add_fiscal_year` view processes all actions required for starting a New Fiscal Year. Form :class:`FiscalYearAccountsForm` This form allows selection of the accounts to exclude in the New Fiscal Year's Transaction purging. """ class Meta: model = FiscalYear widgets = { 'year': forms.TextInput(attrs={'maxlength': 4, 'size': 4, 'class': 'form-control'}), 'end_month': forms.Select(attrs={'class': 'form-control'}), 'period': forms.Select(attrs={'class': 'form-control'}), }
[docs] def clean_year(self): """ Validates that the entered ``Year`` value. The value is required to be greater than or equal to any previously entered ``Year``. """ new_fiscal_year = self.cleaned_data.get('year') current_year = get_start_of_current_fiscal_year() if current_year and new_fiscal_year < current_year.year: raise forms.ValidationError("The Year cannot be before the " "current Year.") return new_fiscal_year
[docs] def clean(self): """ Validates that certain :class:`Accounts<accounts.models.Account>` exist and that the entered Year is in the allowed range. Validates that no previous year's ending month is within the entered ``Period`` with respect to the entered ``end_month``. If the form causes a period change from 13 to 12 months, the method will ensure that there are no :class:`Transactions<entries.models.Transaction>` in the 13th month of the last :class:`~.models.FiscalYear`. If there are previous :class:`FiscalYears<.models.FiscalYear>` the method will make sure there are both a ``Current Year Earnings`` and ``Retained Earnings`` Equity :class:`Accounts<accounts.models.Account>` with a :attr:`~accounts.models.Account.type` of ``3``. """ super(FiscalYearForm, self).clean() if any(self.errors): return self.cleaned_data cleaned_data = self.cleaned_data if FiscalYear.objects.count() > 0: retained = Account.objects.filter(name="Retained Earnings", type=3).exists() current = Account.objects.filter(name="Current Year Earnings", type=3).exists() if not (retained and current): raise forms.ValidationError( "'Current Year Earnings' and 'Retained Earnings' Equity " "Accounts are required to start a new Fiscal Year.") latest = FiscalYear.objects.latest() new_date = datetime.date(cleaned_data.get('year'), cleaned_data.get('end_month'), 1) max_date = latest.date + relativedelta.relativedelta( months=cleaned_data.get('period')) if new_date <= latest.date: raise forms.ValidationError("The new ending Date must be " "after the current ending Date.") elif new_date > max_date: raise forms.ValidationError("The new ending Date cannot be " "greater than the current ending " "Date plus the new period.") if latest.period == 13 and cleaned_data.get('period') == 12: trans_in_end_month = Transaction.objects.filter( date__year=latest.year, date__month=latest.end_month).exists() if trans_in_end_month: raise forms.ValidationError( "When switching from a 13 month to 12 month period, " "no Transactions can be in the last Year's 13th " "month.") return cleaned_data
[docs]class FiscalYearAccountsForm(forms.ModelForm): """ This form is used to select whether to exclude an account from the :class:`~entries.models.Transaction` purging caused by the :func:`~.views.add_fiscal_year` view. Selected :class:`Accounts<accounts.models.Account>` will retain their unreconciled :class:`Transactions<entries.models.Transaction>`. This form is used by the :func:`~django.forms.models.modelformset_factory` to create the :data:`FiscalYearAccountsFormSet`. .. seealso:: View :func:`~.views.add_fiscal_year` This view processes all actions required for starting a New Fiscal Year. """ exclude = forms.BooleanField(required=False) class Meta: model = Account def __init__(self, *args, **kwargs): """ Mark an :class:`Account` for exclusion if it has been reconciled. """ super(FiscalYearAccountsForm, self).__init__(*args, **kwargs) self.fields['exclude'].initial = bool(self.instance.last_reconciled) #: An FormSet of :class:`Accounts<accounts.models.Account>` using the #: :class:`FiscalYearAccountsForm` as the base form.
FiscalYearAccountsFormSet = modelformset_factory(Account, extra=0, can_delete=False, fields=('exclude',), form=FiscalYearAccountsForm)