From f58bd1fd54d23c3fd97428c455a1f7c3a3173bb6 Mon Sep 17 00:00:00 2001 From: Stephen Burrows Date: Fri, 28 Mar 2014 18:15:07 -0700 Subject: [PATCH] Initial work on simpler CBVs, inline transaction creation, and transaction updates. Touches on #26, #16, and philosophical discussions. --- argus/models.py | 2 +- argus/templates/argus/group_detail.html | 31 ++- ...tion_create.html => transaction_form.html} | 0 argus/urls.py | 17 +- argus/views.py | 233 +++++++++--------- 5 files changed, 152 insertions(+), 131 deletions(-) rename argus/templates/argus/{transaction_create.html => transaction_form.html} (100%) diff --git a/argus/models.py b/argus/models.py index eb7fc30..1f9c224 100644 --- a/argus/models.py +++ b/argus/models.py @@ -38,7 +38,7 @@ def __unicode__(self): def get_absolute_url(self): return reverse("argus_group_detail", - kwargs={"slug": self.slug}) + kwargs={"group_slug": self.slug}) def set_password(self, raw_password): self.password = make_password(raw_password) diff --git a/argus/templates/argus/group_detail.html b/argus/templates/argus/group_detail.html index e634a18..ec3b5b0 100644 --- a/argus/templates/argus/group_detail.html +++ b/argus/templates/argus/group_detail.html @@ -1,5 +1,7 @@ {% extends "argus/__group.html" %} +{% load floppyforms %} + {% block side %}
@@ -40,10 +42,31 @@

Categories

{% block main %}

Record a New Expense

-

- Simple Payment - Even Split - Uneven Split

+ +
+

Simple transaction (payment)

+
+ {% csrf_token %} + {% form forms.simple_form %} + +
+
+
+

Even split transaction

+
+ {% csrf_token %} + {% form forms.even_form %} + +
+
+
+

Manual split transaction

+
+ {% csrf_token %} + {% form forms.manual_form %} + +
+
diff --git a/argus/templates/argus/transaction_create.html b/argus/templates/argus/transaction_form.html similarity index 100% rename from argus/templates/argus/transaction_create.html rename to argus/templates/argus/transaction_form.html diff --git a/argus/urls.py b/argus/urls.py index 5f1baef..5edcf34 100644 --- a/argus/urls.py +++ b/argus/urls.py @@ -7,8 +7,7 @@ GroupChangePasswordView, GroupPasswordResetTokenView, GroupPasswordResetConfirmView, GroupEmailConfirmView, GroupLogoutView, GroupRelatedCreateView, - SimpleSplitCreateView, EvenSplitCreateView, - ManualSplitCreateView, CategoryDetailView, + TransactionUpdateView, CategoryDetailView, GroupRelatedUpdateView) @@ -20,7 +19,7 @@ GroupLogoutView.as_view(), name='argus_group_logout'), - url(r'^(?P{})/$'.format(Group.SLUG_REGEX), + url(r'^(?P{})/$'.format(Group.SLUG_REGEX), GroupDetailView.as_view(), name='argus_group_detail'), url(r'^(?P{})/edit/$'.format(Group.SLUG_REGEX), @@ -75,13 +74,7 @@ model=Category), name='argus_category_update'), - url(r'^(?P{})/simple/$'.format(Group.SLUG_REGEX), - SimpleSplitCreateView.as_view(), - name='argus_payment_create'), - url(r'^(?P{})/even/$'.format(Group.SLUG_REGEX), - EvenSplitCreateView.as_view(), - name='argus_even_create'), - url(r'^(?P{})/manual/$'.format(Group.SLUG_REGEX), - ManualSplitCreateView.as_view(), - name='argus_manual_create'), + url(r'^(?P{})/transaction/(?P\d+)/$'.format(Group.SLUG_REGEX), + TransactionUpdateView.as_view(), + name='argus_transaction'), ) diff --git a/argus/views.py b/argus/views.py index 3269933..e4693e4 100644 --- a/argus/views.py +++ b/argus/views.py @@ -7,8 +7,9 @@ from django.db.models import Q from django.forms.models import modelform_factory from django.http import Http404, HttpResponseRedirect +from django.shortcuts import get_object_or_404 from django.template import loader -from django.views.generic import (DetailView, ListView, RedirectView, +from django.views.generic import (DetailView, TemplateView, RedirectView, UpdateView, FormView, CreateView) from django.views.generic.edit import BaseUpdateView @@ -212,30 +213,130 @@ def get_context_data(self, **kwargs): return context -class GroupDetailView(DetailView): - model = Group - template_name = 'argus/group_detail.html' +class TransactionFormMixin(object): + def dispatch(self, request, *args, **kwargs): + if request.method.lower() in self.http_method_names: + self.group = self.get_group() + if _group_auth_needed(request, self.group): + return _group_auth_redirect(self.group) + return super(TransactionFormMixin, self).dispatch(request, *args, + **kwargs) - def get_queryset(self): - qs = super(GroupDetailView, self).get_queryset() - return qs.prefetch_related('parties', 'categories') + def get_group(self): + return get_object_or_404(Group, slug=self.kwargs['group_slug']) - def get(self, request, *args, **kwargs): - self.object = self.get_object() - if _group_auth_needed(request, self.object): - return _group_auth_redirect(self.object) - context = self.get_context_data(object=self.object) + def get_transaction_forms(self, pk=None): + if pk: + self.transaction = get_object_or_404(Transaction, pk=pk) + else: + self.transaction = Transaction() + kwargs = { + 'group': self.group, + 'instance': self.transaction, + } + if self.request.method == 'POST': + kwargs['data'] = self.request.POST + + forms = { + 'simple_form': SimpleSplitForm(prefix='simple', **kwargs), + 'even_form': EvenSplitForm(prefix='even', **kwargs), + 'manual_form': ManualSplitForm(prefix='manual', **kwargs) + } + return forms + + def get_context_data(self, **kwargs): + context = super(TransactionFormMixin, self).get_context_data(**kwargs) + forms = self.get_transaction_forms() + context['forms'] = forms + context['group'] = self.group + return context + + def post(self, request, *args, **kwargs): + context = self.get_context_data(**kwargs) + forms = context['forms'] + for form in forms: + if form.is_valid(): + form.save() + return HttpResponseRedirect(self.group.get_absolute_url()) return self.render_to_response(context) + +class TransactionUpdateView(TransactionFormMixin, TemplateView): + template_name = 'argus/transaction_update.html' + + def get_transaction_forms(self, pk=None): + pk = self.kwargs['pk'] + return super(TransactionUpdateView, self).get_transaction_forms(pk) + + +class TransactionListView(TransactionFormMixin, TemplateView): + template_name = 'argus/transaction_list.html' + + def get_group(self): + qs = Group.objects.prefetch_related('parties', 'categories') + return get_object_or_404(qs, slug=self.kwargs['group_slug']) + + def get_transactions(self): + return Transaction.objects.filter(paid_by__group=self.group + ).order_by('-paid_at' + ).prefetch_related('shares') + + def get_context_data(self, **kwargs): + context = super(TransactionListView, self).get_context_data(**kwargs) + context['recent_transactions'] = self.get_transactions() + context['members'] = [p for p in self.group.parties.all() + if p.party_type == Party.MEMBER] + return context + + +class GroupDetailView(TransactionListView): + template_name = 'argus/group_detail.html' + + +class GroupRelatedDetailView(TransactionListView): + def get_group(self): + group = super(GroupRelatedDetailView, self).get_group() + self.object = get_object_or_404(self.model, group=group) + return group + def get_context_data(self, **kwargs): - context = super(GroupDetailView, self).get_context_data(**kwargs) - transactions = Transaction.objects.filter(paid_by__group=self.object - ).order_by('-paid_at' - ).prefetch_related('shares') - context['recent_transactions'] = transactions - context['members'] = self.object.parties.filter(party_type=Party.MEMBER) + context = super(GroupRelatedDetailView, self).get_context_data(**kwargs) + context_object_name = getattr(self, 'context_object_name', + self.model._meta.verbose_name.lower()) + context[context_object_name] = self.object + context['balance'] = self.get_balance() return context + def get_balance(self): + raise NotImplementedError + + +class PartyDetailView(GroupRelatedDetailView): + model = Party + template_name = 'argus/party_detail.html' + + def get_balance(self): + return self.object.balance + + def get_transactions(self): + return Transaction.objects.filter(Q(shares__party=self.object) | + Q(paid_by=self.object) | + Q(paid_to=self.object) + ).order_by('-paid_at' + ).distinct() + + +class CategoryDetailView(GroupRelatedDetailView): + model = Category + template_name = 'argus/category_detail.html' + + def get_balance(self): + transactions = self.get_transactions() + return transactions.aggregate(models.Sum('amount'))['amount__sum'] + + def get_transactions(self): + return self.object.transactions.order_by('-paid_at') + class GroupUpdateView(UpdateView): model = Group @@ -304,99 +405,3 @@ def get_success_url(self): class GroupRelatedUpdateView(GroupRelatedFormMixin, UpdateView): def get_success_url(self): return self.object.get_absolute_url() - - -class GroupRelatedDetailView(DetailView): - def get_queryset(self): - qs = super(GroupRelatedDetailView, self).get_queryset() - qs = qs.select_related('group') - return qs.filter(group__slug=self.kwargs['group_slug']) - - def get(self, request, *args, **kwargs): - self.object = self.get_object() - if _group_auth_needed(request, self.object.group): - return _group_auth_redirect(self.object.group) - context = self.get_context_data(object=self.object) - return self.render_to_response(context) - - def get_context_data(self, **kwargs): - context = super(GroupRelatedDetailView, self).get_context_data(**kwargs) - context['group'] = self.object.group - return context - - -class PartyDetailView(GroupRelatedDetailView): - model = Party - template_name = 'argus/party_detail.html' - - def get_context_data(self, **kwargs): - context = super(PartyDetailView, self).get_context_data(**kwargs) - context['balance'] = self.object.balance - transactions = Transaction.objects.filter(Q(shares__party=self.object) | - Q(paid_by=self.object) | - Q(paid_to=self.object) - ).order_by('-paid_at' - ).distinct() - context['recent_transactions'] = transactions - return context - - -class CategoryDetailView(GroupRelatedDetailView): - model = Category - template_name = 'argus/category_detail.html' - - def get(self, request, *args, **kwargs): - self.object = self.get_object() - if _group_auth_needed(request, self.object.group): - return _group_auth_redirect(self.object.group) - context = self.get_context_data(object=self.object) - return self.render_to_response(context) - - def get_context_data(self, **kwargs): - context = super(CategoryDetailView, self).get_context_data(**kwargs) - transactions = self.object.transactions.all() - context['balance'] = transactions.aggregate(models.Sum('amount') - )['amount__sum'] - transactions = transactions.order_by('-paid_at') - context['recent_transactions'] = transactions - return context - - -class BaseTransactionCreateView(CreateView): - template_name = 'argus/transaction_create.html' - - def dispatch(self, request, *args, **kwargs): - if request.method.lower() in self.http_method_names: - try: - self.group = Group.objects.get(slug=kwargs['group_slug']) - except Group.DoesNotExist: - raise Http404 - if _group_auth_needed(request, self.group): - return _group_auth_redirect(self.group) - return super(BaseTransactionCreateView, self).dispatch(request, *args, - **kwargs) - - def get_form_kwargs(self): - kwargs = super(BaseTransactionCreateView, self).get_form_kwargs() - kwargs['group'] = self.group - return kwargs - - def get_context_data(self, **kwargs): - context = super(BaseTransactionCreateView, self).get_context_data(**kwargs) - context['group'] = self.group - return context - - def get_success_url(self): - return self.group.get_absolute_url() - - -class SimpleSplitCreateView(BaseTransactionCreateView): - form_class = SimpleSplitForm - - -class EvenSplitCreateView(BaseTransactionCreateView): - form_class = EvenSplitForm - - -class ManualSplitCreateView(BaseTransactionCreateView): - form_class = ManualSplitForm