You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

84 lines
3.5 KiB

from uuid import UUID
from django import forms
from finance.models import Account, Category, Payee, Transaction
def _reference_choices(model, *, include=None):
rows = model.objects.filter(archived=False)
if include is not None:
rows = model.objects.filter(pk=include.pk) | rows
return [(str(row.pk), row.name) for row in rows.order_by("name")]
def _uuid(value):
return None if value == "" else UUID(value)
class ManualTransactionForm(forms.Form):
account = forms.TypedChoiceField(coerce=UUID)
amount = forms.DecimalField(max_digits=18, decimal_places=2)
posting_date = forms.DateField(widget=forms.DateInput(attrs={"type": "date"}))
occurrence_date = forms.DateField(required=False, widget=forms.DateInput(attrs={"type": "date"}))
kind = forms.ChoiceField(choices=Transaction.Kind.choices)
description = forms.CharField(required=False, widget=forms.Textarea(attrs={"rows": 3}))
payee = forms.TypedChoiceField(required=False, empty_value=None, coerce=_uuid)
category = forms.TypedChoiceField(required=False, empty_value=None, coerce=_uuid)
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields["account"].choices = _reference_choices(Account)
self.fields["payee"].choices = [("", "None"), *_reference_choices(Payee)]
self.fields["category"].choices = [("", "Uncategorized"), *_reference_choices(Category)]
def clean(self):
values = super().clean()
amount = values.get("amount")
kind = values.get("kind")
if amount is not None and kind == Transaction.Kind.EXPENSE and amount >= 0:
self.add_error("amount", "Expense amounts must be negative.")
if amount is not None and kind in {Transaction.Kind.INCOME, Transaction.Kind.REFUND} and amount <= 0:
self.add_error("amount", "Income and refund amounts must be positive.")
return values
class AnnotationForm(forms.Form):
description = forms.CharField(required=False, widget=forms.Textarea(attrs={"rows": 3}))
payee = forms.TypedChoiceField(required=False, empty_value=None, coerce=_uuid)
category = forms.TypedChoiceField(required=False, empty_value=None, coerce=_uuid)
def __init__(self, *args, transaction, **kwargs):
super().__init__(*args, **kwargs)
self.transaction = transaction
self.fields["payee"].choices = [("", "None"), *_reference_choices(Payee, include=transaction.payee)]
self.fields["category"].choices = [("", "Uncategorized"), *_reference_choices(Category, include=transaction.category)]
if not self.is_bound:
self.initial = {
"description": transaction.description,
"payee": transaction.payee_id,
"category": transaction.category_id,
}
def changes(self):
values = self.cleaned_data
changes = {}
for form_name, model_name in (("description", "description"), ("payee", "payee_id"), ("category", "category_id")):
if values[form_name] != getattr(self.transaction, model_name):
changes[model_name] = values[form_name]
return changes
class AccountForm(forms.Form):
name = forms.CharField(max_length=255)
kind = forms.ChoiceField(choices=Account.Kind.choices)
currency = forms.RegexField(regex=r"^[A-Z]{3}$", max_length=3)
class CategoryForm(forms.Form):
name = forms.CharField(max_length=255)
kind = forms.ChoiceField(choices=Category.Kind.choices)
class PayeeForm(forms.Form):
name = forms.CharField(max_length=255)