diff --git a/registrasion/forms.py b/registrasion/forms.py index 7770e9be..25a54768 100644 --- a/registrasion/forms.py +++ b/registrasion/forms.py @@ -413,7 +413,16 @@ def staff_products_formset_factory(user): return forms.formset_factory(form_type) -class InvoiceNagForm(forms.Form): +class InvoiceEmailForm(forms.Form): + + ACTION_PREVIEW = 1 + ACTION_SEND = 2 + + ACTION_CHOICES = ( + (ACTION_PREVIEW, "Preview"), + (ACTION_SEND, "Send emails"), + ) + invoice = forms.ModelMultipleChoiceField( widget=forms.CheckboxSelectMultiple, queryset=commerce.Invoice.objects.all(), @@ -423,18 +432,26 @@ class InvoiceNagForm(forms.Form): body = forms.CharField( widget=forms.Textarea, ) + action = forms.TypedChoiceField( + widget=forms.RadioSelect, + coerce=int, + choices=ACTION_CHOICES, + initial=ACTION_PREVIEW, + ) def __init__(self, *a, **k): category = k.pop('category', None) or [] product = k.pop('product', None) or [] + status = int(k.pop('status', None) or 0) category = [int(i) for i in category] product = [int(i) for i in product] - super(InvoiceNagForm, self).__init__(*a, **k) + super(InvoiceEmailForm, self).__init__(*a, **k) + print status qs = commerce.Invoice.objects.filter( - status=commerce.Invoice.STATUS_UNPAID, + status=status or commerce.Invoice.STATUS_UNPAID, ).filter( Q(lineitem__product__category__in=category) | Q(lineitem__product__in=product) diff --git a/registrasion/reporting/views.py b/registrasion/reporting/views.py index a350cb3d..eac9fd80 100644 --- a/registrasion/reporting/views.py +++ b/registrasion/reporting/views.py @@ -593,10 +593,11 @@ def attendee_data(request, form, user_id=None): # Add invoice nag link links = [] - links.append(( - reverse(views.nag_unpaid, args=[]) + "?" + request.META["QUERY_STRING"], - "Send invoice reminders", - )) + invoice_mailout = reverse(views.invoice_mailout, args=[]) + "?" + request.META["QUERY_STRING"] + links += [ + (invoice_mailout + "&status=1", "Send invoice reminders",), + (invoice_mailout + "&status=2", "Send mail for paid invoices",), + ] if items.count() > 0: output.append(Links("Actions", links)) diff --git a/registrasion/templatetags/registrasion_tags.py b/registrasion/templatetags/registrasion_tags.py index 1f408ed1..02cda54e 100644 --- a/registrasion/templatetags/registrasion_tags.py +++ b/registrasion/templatetags/registrasion_tags.py @@ -9,6 +9,15 @@ from urllib import urlencode register = template.Library() +def user_for_context(context): + ''' Returns either context.user or context.request.user if the former is + not defined. ''' + try: + return context["user"] + except KeyError: + return context.request.user + + @register.assignment_tag(takes_context=True) def available_categories(context): ''' Gets all of the currently available products. @@ -18,13 +27,13 @@ def available_categories(context): have Products that the current user can reserve. ''' - return CategoryController.available_categories(context.request.user) + return CategoryController.available_categories(user_for_context(context)) @register.assignment_tag(takes_context=True) def missing_categories(context): ''' Adds the categories that the user does not currently have. ''' - user = context.request.user + user = user_for_context(context) categories_available = set(CategoryController.available_categories(user)) items = ItemController(user).items_pending_or_purchased() @@ -47,7 +56,7 @@ def available_credit(context): ''' notes = commerce.CreditNote.unclaimed().filter( - invoice__user=context.request.user, + invoice__user=user_for_context(context), ) ret = notes.values("amount").aggregate(Sum("amount"))["amount__sum"] or 0 return 0 - ret @@ -59,20 +68,29 @@ def invoices(context): Returns: [models.commerce.Invoice, ...]: All of the current user's invoices. ''' - return commerce.Invoice.objects.filter(user=context.request.user) + return commerce.Invoice.objects.filter(user=user_for_context(context)) @register.assignment_tag(takes_context=True) def items_pending(context): - ''' Gets all of the items that the user from this context has reserved.''' - return ItemController(context.request.user).items_pending() + ''' Gets all of the items that the user from this context has reserved. + + The user will be either `context.user`, and `context.request.user` if + the former is not defined. + ''' + + return ItemController(user_for_context(context)).items_pending() @register.assignment_tag(takes_context=True) def items_purchased(context, category=None): - ''' Returns the items purchased for this user. ''' + ''' Returns the items purchased for this user. - return ItemController(context.request.user).items_purchased( + The user will be either `context.user`, and `context.request.user` if + the former is not defined. + ''' + + return ItemController(user_for_context(context)).items_purchased( category=category ) diff --git a/registrasion/urls.py b/registrasion/urls.py index 2028d86b..8f2e1663 100644 --- a/registrasion/urls.py +++ b/registrasion/urls.py @@ -12,8 +12,8 @@ from .views import ( guided_registration, invoice, invoice_access, + invoice_mailout, manual_payment, - nag_unpaid, product_category, refund, review, @@ -35,7 +35,7 @@ public = [ refund, name="refund"), url(r"^invoice_access/([A-Z0-9]+)$", invoice_access, name="invoice_access"), - url(r"^nag_unpaid$", nag_unpaid, name="nag_unpaid"), + url(r"^invoice_mailout$", invoice_mailout, name="invoice_mailout"), url(r"^profile$", edit_profile, name="attendee_edit"), url(r"^register$", guided_registration, name="guided_registration"), url(r"^review$", review, name="review"), diff --git a/registrasion/views.py b/registrasion/views.py index 5f57eb20..24e5ebf1 100644 --- a/registrasion/views.py +++ b/registrasion/views.py @@ -920,19 +920,28 @@ def extend_reservation(request, user_id, days=7): return redirect(request.META["HTTP_REFERER"]) +Email = namedtuple( + "Email", + ("subject", "body", "from_email", "recipient_list"), +) + @user_passes_test(_staff_only) -def nag_unpaid(request): - ''' Allows staff to nag users with unpaid invoices. ''' +def invoice_mailout(request): + ''' Allows staff to send emails to users based on their invoice status. ''' category = request.GET.getlist("category", []) product = request.GET.getlist("product", []) + status = request.GET.get("status") - form = forms.InvoiceNagForm( + form = forms.InvoiceEmailForm( request.POST or None, category=category, product=product, + status=status, ) + emails = [] + if form.is_valid(): emails = [] for invoice in form.cleaned_data["invoice"]: @@ -942,15 +951,20 @@ def nag_unpaid(request): body = Template(form.cleaned_data["body"]).render( Context({ "invoice" : invoice, + "user" : invoice.user, }) ) recipient_list = [invoice.user.email] - emails.append((subject, body, from_email, recipient_list)) - send_mass_mail(emails) - messages.info(request, "The e-mails have been sent.") + emails.append(Email(subject, body, from_email, recipient_list)) + + if form.cleaned_data["action"] == forms.InvoiceEmailForm.ACTION_SEND: + # Send e-mails *ONLY* if we're sending. + send_mass_mail(emails) + messages.info(request, "The e-mails have been sent.") data = { "form": form, + "emails": emails, } - return render(request, "registrasion/nag_unpaid.html", data) + return render(request, "registrasion/invoice_mailout.html", data)