Example #1
0
class TransferForm(forms.Form):
    submit_label = 'Transfer'

    target_enrolment = forms.ModelChoiceField(
        queryset=Enrolment.objects.all(),
        label='Transfer to',
    )
    amount = forms.DecimalField(
        min_value=0,
        error_messages={'min_value': 'Amount must be positive'},
        widget=PoundInput(),
    )
    narrative = forms.CharField()
    type = forms.ModelChoiceField(queryset=models.TransactionType.objects.filter(is_cash=True, is_active=True))

    def __init__(self, source_enrolment: Enrolment, *args, **kwargs):
        super().__init__(*args, **kwargs)
        # Limit target field to the student's other enrolments
        self.fields['target_enrolment'].queryset = (
            Enrolment.objects.filter(qa__student=source_enrolment.qa.student)
            .exclude(id=source_enrolment.id)
            .order_by('-module__start_date')
            .select_related('module')
        )
        # Easier than subclassing ModelChoiceField solely for this
        self.fields['target_enrolment'].label_from_instance = lambda obj: f'{obj.module} ({obj.module.code})'
Example #2
0
class MultipleEnrolmentPaymentForm(forms.Form):
    """Allows distributing a single payment between multiple enrolments.  Creates a field for every enrolment provided.
    Returns an `allocations` dict as part of `cleaned_data` for easy iteration over the results
    """

    submit_label = 'Add'

    amount = forms.DecimalField(widget=PoundInput(), label='Total paid')
    narrative = forms.CharField()
    type = forms.ModelChoiceField(queryset=models.TransactionType.objects.filter(is_cash=True, is_active=True))

    def clean(self):
        # Extract the allocation fields, and create a dictionary of {id: amount} for easy processing
        allocations: dict[int, Decimal] = {
            int(key.replace('allocation_', '')): val for key, val in self.cleaned_data.items() if 'allocation_' in key
        }
        self.cleaned_data['allocations'] = allocations

        # Ensure the total allocations matches the amount
        amount = self.cleaned_data.get('amount')
        allocated = sum(allocations.values())
        if amount and allocated != amount:
            self.add_error('amount', f'Does not match amount allocated below (£{allocated})')

    def __init__(self, enrolments: list[Enrolment], *args, **kwargs):
        super().__init__(*args, **kwargs)
        # Add an input row for every enrolment
        for enrolment in enrolments:
            fieldname = f'allocation_{enrolment.id}'
            self.fields[fieldname] = forms.DecimalField(
                label=f'{enrolment.module.code}',
                help_text=f'£{enrolment.get_balance():.2f} owing • {enrolment.module.title}',
                widget=PoundInput(),
            )
Example #3
0
 class Meta:
     model = models.PaymentPlan
     fields = ('invoice', 'type', 'status', 'amount')
     help_texts = {
         'status':
         "If plan already exists, choose 'Payment schedule active'"
     }
     widgets = {'amount': PoundInput()}
Example #4
0
 def __init__(self, enrolments: list[Enrolment], *args, **kwargs):
     super().__init__(*args, **kwargs)
     # Add an input row for every enrolment
     for enrolment in enrolments:
         fieldname = f'allocation_{enrolment.id}'
         self.fields[fieldname] = forms.DecimalField(
             label=f'{enrolment.module.code}',
             help_text=f'£{enrolment.get_balance():.2f} owing • {enrolment.module.title}',
             widget=PoundInput(),
         )
Example #5
0
 class Meta:
     model = Fee
     fields = (
         'amount',
         'type',
         'description',
         'allocation',
         'eu_fee',
         'is_visible',
         'is_payable',
         'is_catering',
         'is_single_accom',
         'is_twin_accom',
         'credit_fee',
         'end_date',
         'limit',
     )
     widgets = {'amount': PoundInput()}
Example #6
0
class WeeklyTeachingForm(forms.Form):
    rate = forms.DecimalField(max_digits=10, decimal_places=4, widget=PoundInput(), disabled=True, label='Hourly rate')
    length = forms.DecimalField(
        max_digits=10,
        decimal_places=1,
        validators=[validators.MinValueValidator(0), validators.MaxValueValidator(100)],
        label='Hours per week (prep & teaching)',
        help_text='Typically 4 for in-person, 5 for online',
    )
    no_meetings = forms.IntegerField(
        validators=[validators.MinValueValidator(1), validators.MaxValueValidator(21)],
        label='Number of meetings',
    )
    schedule = forms.TypedChoiceField(choices=schedule_choices, coerce=int)
    approver = ApproverChoiceField('tutor_payment.approve')

    def clean(self) -> None:
        self.cleaned_data['schedule'] = models.schedules[self.cleaned_data['schedule']]
Example #7
0
 class Meta:
     first_fields, last_fields = [
         'enrolment', 'type', 'status', 'amount', 'reason'
     ], [
         'details',
         'approver',
         'batch',
         'narrative',
         'is_complete',
     ]
     model = models.Amendment
     fields = first_fields + last_fields
     widgets = {
         'amount': PoundInput(),
         'enrolment': forms.HiddenInput(),
         'type': ReadOnlyModelWidget(model=models.AmendmentType),
         'status': ReadOnlyModelWidget(model=models.AmendmentStatus),
     }
Example #8
0
class CreditForm(forms.Form):
    submit_label = 'Add credit'
    enrolment = forms.ModelChoiceField(queryset=Enrolment.objects.all())
    account = fields_for_model(Ledger)['account']
    amount = forms.DecimalField(
        widget=PoundInput(),
        help_text='Positive values decrease debt, negative increase debt')
    narrative = forms.CharField()
    type = forms.ModelChoiceField(
        queryset=TransactionType.objects.filter(is_cash=False, is_active=True))

    def __init__(self, invoice: models.Invoice, *args, **kwargs):
        super().__init__(*args, **kwargs)
        # Limit the enrolments to those connected with the invoice
        self.fields['enrolment'].queryset = (Enrolment.objects.filter(
            ledger__invoice=invoice).distinct().select_related(
                'module').order_by('-id'))
        # Dropdown options display the module title
        self.fields['enrolment'].label_from_instance = lambda obj: obj.module
Example #9
0
class PaymentForm(forms.Form):
    submit_label = 'Add payment'
    enrolment = forms.ModelChoiceField(
        queryset=Enrolment.objects.all(),
        required=False,
        empty_label=' – All – ',
    )
    amount = forms.DecimalField(widget=PoundInput())
    narrative = forms.CharField()
    type = forms.ModelChoiceField(
        queryset=TransactionType.objects.filter(is_cash=True, is_active=True))

    def __init__(self, invoice: models.Invoice, *args, **kwargs):
        super().__init__(*args, **kwargs)
        # Limit the enrolments to those connected with the invoice
        self.fields['enrolment'].queryset = (Enrolment.objects.filter(
            ledger__invoice=invoice).distinct().select_related(
                'module').order_by('-id'))
        # Dropdown options display the module title
        self.fields[
            'enrolment'].label_from_instance = lambda obj: f'{obj.module.code} - {obj.module} (£{obj.get_balance():.2f})'
Example #10
0
class GuestSpeakerForm(ContractForm):
    topic = forms.CharField(label='Lecture topic(s)')
    dates_and_times = forms.CharField(
        help_text=mark_safe('When the lectures will take place. E.g. <i>12 Jan 2020 at 9:45, 11:15, and 13:30</i>'),
    )
    lecture_no = forms.IntegerField(label='Number of lectures', initial=1, min_value=1, max_value=100)
    fee_per_lecture = forms.DecimalField(
        max_digits=8, decimal_places=2, max_value=10000, min_value=0, widget=PoundInput()
    )

    field_order = [
        'phone',
        'email',
        'topic',
        'venue',
        'dates_and_times',
        'lecture_no',
        'fee_per_lecture',
        'approver',
        'return_to',
        'return_address',
        'email_notification',
    ]
Example #11
0
class InvoiceSearchFilter(django_filters.FilterSet):
    invoiced_to = django_filters.CharFilter(field_name='invoiced_to', label='Invoiced to', lookup_expr='icontains')
    minimum = django_filters.NumberFilter(
        field_name='amount', label='Minimum amount', lookup_expr='gte', widget=PoundInput()
    )
    maximum = django_filters.NumberFilter(
        field_name='amount', label='Maximum amount', lookup_expr='lte', widget=PoundInput()
    )
    created_by = django_filters.CharFilter(field_name='created_by', label='Created by (username)', lookup_expr='exact')
    created_after = django_filters.DateFilter(
        field_name='created_on', label='Created on or after', lookup_expr='gte', widget=DatePickerInput()
    )

    def filter_address(self, queryset, field_name, value):
        """Filters on any invoice address field"""
        if value:
            return queryset.filter(
                Q(line1__icontains=value)
                | Q(line2__icontains=value)
                | Q(line3__icontains=value)
                | Q(town__icontains=value)
                | Q(countystate__icontains=value)
                | Q(country__icontains=value)
                | Q(postcode__icontains=value)
            )
        return queryset

    address = django_filters.CharFilter(
        label='Address',
        method='filter_address',
        help_text="E.g. 'OX1 2JA', 'Sacramento', or 'Mexico'",
    )

    def overdue_only(self, queryset, field_name, value):
        if value:
            return queryset.overdue()
        return queryset

    overdue = django_filters.BooleanFilter(
        label='Overdue only?',
        method='overdue_only',
        widget=forms.CheckboxInput,
    )

    def outstanding_only(self, queryset, field_name, value):
        if value:
            return queryset.outstanding()
        return queryset

    outstanding = django_filters.BooleanFilter(
        label='Outstanding only?',
        method='outstanding_only',
        widget=forms.CheckboxInput,
    )

    class Meta:
        model = Invoice
        fields = [
            'invoiced_to',
            'address',
            'minimum',
            'maximum',
            'created_by',
            'created_after',
            'overdue',
            'outstanding',
        ]
Example #12
0
 class Meta:
     model = models.Ledger
     fields = ('amount', 'narrative', 'type')
     widgets = {'amount': PoundInput()}
Example #13
0
 class Meta:
     model = models.Ledger
     fields = ('amount', 'narrative', 'account', 'type')
     widgets = {'amount': PoundInput()}
     help_texts = {'amount': 'Positive values increase debt, negative decrease debt'}