From 06fe8a8ffa65b5e668e450bbaf987f34cc519d86 Mon Sep 17 00:00:00 2001 From: Christopher Neugebauer Date: Sun, 8 Jan 2017 09:42:40 +1100 Subject: [PATCH 1/7] Adds preview function to nag_unpaid --- registrasion/forms.py | 15 +++++++++++++++ registrasion/views.py | 17 ++++++++++++++--- 2 files changed, 29 insertions(+), 3 deletions(-) diff --git a/registrasion/forms.py b/registrasion/forms.py index 7770e9be..ecb889fa 100644 --- a/registrasion/forms.py +++ b/registrasion/forms.py @@ -414,6 +414,15 @@ def staff_products_formset_factory(user): class InvoiceNagForm(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,6 +432,12 @@ 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 [] diff --git a/registrasion/views.py b/registrasion/views.py index 5f57eb20..b3aae73d 100644 --- a/registrasion/views.py +++ b/registrasion/views.py @@ -920,6 +920,11 @@ 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. ''' @@ -933,6 +938,8 @@ def nag_unpaid(request): product=product, ) + emails = [] + if form.is_valid(): emails = [] for invoice in form.cleaned_data["invoice"]: @@ -945,12 +952,16 @@ def nag_unpaid(request): }) ) 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.InvoiceNagForm.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) From 479bdd36a3a2a118091b1f5faafd1adab1d3954d Mon Sep 17 00:00:00 2001 From: Christopher Neugebauer Date: Sun, 8 Jan 2017 09:43:20 +1100 Subject: [PATCH 2/7] s/InvoiceNagForm/InvoiceEmailForm/ --- registrasion/forms.py | 2 +- registrasion/views.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/registrasion/forms.py b/registrasion/forms.py index ecb889fa..1874e448 100644 --- a/registrasion/forms.py +++ b/registrasion/forms.py @@ -413,7 +413,7 @@ 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 diff --git a/registrasion/views.py b/registrasion/views.py index b3aae73d..e30472db 100644 --- a/registrasion/views.py +++ b/registrasion/views.py @@ -932,7 +932,7 @@ def nag_unpaid(request): category = request.GET.getlist("category", []) product = request.GET.getlist("product", []) - form = forms.InvoiceNagForm( + form = forms.InvoiceEmailForm( request.POST or None, category=category, product=product, @@ -954,7 +954,7 @@ def nag_unpaid(request): recipient_list = [invoice.user.email] emails.append(Email(subject, body, from_email, recipient_list)) - if form.cleaned_data["action"] == forms.InvoiceNagForm.ACTION_SEND: + 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.") From 3b985d40ac0b9ab552e135765f6800c5e10fb715 Mon Sep 17 00:00:00 2001 From: Christopher Neugebauer Date: Sun, 8 Jan 2017 09:47:54 +1100 Subject: [PATCH 3/7] Missing line --- registrasion/forms.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/registrasion/forms.py b/registrasion/forms.py index 1874e448..5dee2da8 100644 --- a/registrasion/forms.py +++ b/registrasion/forms.py @@ -446,7 +446,7 @@ class InvoiceEmailForm(forms.Form): 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) qs = commerce.Invoice.objects.filter( status=commerce.Invoice.STATUS_UNPAID, From de902a213d3e5e7f83513b2ea334cc069f587713 Mon Sep 17 00:00:00 2001 From: Christopher Neugebauer Date: Sun, 8 Jan 2017 09:48:48 +1100 Subject: [PATCH 4/7] Adds invoice status to nag_unpaid --- registrasion/forms.py | 3 ++- registrasion/views.py | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/registrasion/forms.py b/registrasion/forms.py index 5dee2da8..837c8e9f 100644 --- a/registrasion/forms.py +++ b/registrasion/forms.py @@ -442,6 +442,7 @@ class InvoiceEmailForm(forms.Form): 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] @@ -449,7 +450,7 @@ class InvoiceEmailForm(forms.Form): super(InvoiceEmailForm, self).__init__(*a, **k) 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/views.py b/registrasion/views.py index e30472db..815aa2e3 100644 --- a/registrasion/views.py +++ b/registrasion/views.py @@ -931,11 +931,13 @@ def nag_unpaid(request): category = request.GET.getlist("category", []) product = request.GET.getlist("product", []) + status = request.GET.get("status") form = forms.InvoiceEmailForm( request.POST or None, category=category, product=product, + status=status, ) emails = [] From 274187b8bf7e630ae752801173cc0535ab4bf16a Mon Sep 17 00:00:00 2001 From: Christopher Neugebauer Date: Sun, 8 Jan 2017 09:52:49 +1100 Subject: [PATCH 5/7] =?UTF-8?q?Renames=20=E2=80=9Cnag=5Funpaid=E2=80=9D=20?= =?UTF-8?q?to=20=E2=80=9Cinvoice=5Fmailout=E2=80=9D,=20better=20matches=20?= =?UTF-8?q?current=20intent.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- registrasion/forms.py | 1 + registrasion/reporting/views.py | 9 +++++---- registrasion/urls.py | 4 ++-- registrasion/views.py | 4 ++-- 4 files changed, 10 insertions(+), 8 deletions(-) diff --git a/registrasion/forms.py b/registrasion/forms.py index 837c8e9f..25a54768 100644 --- a/registrasion/forms.py +++ b/registrasion/forms.py @@ -448,6 +448,7 @@ class InvoiceEmailForm(forms.Form): product = [int(i) for i in product] super(InvoiceEmailForm, self).__init__(*a, **k) + print status qs = commerce.Invoice.objects.filter( status=status or commerce.Invoice.STATUS_UNPAID, 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/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 815aa2e3..f4a59eb0 100644 --- a/registrasion/views.py +++ b/registrasion/views.py @@ -926,8 +926,8 @@ Email = namedtuple( ) @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", []) From fd7fff7879b311b65d413af397dc10467fcda845 Mon Sep 17 00:00:00 2001 From: Christopher Neugebauer Date: Sun, 8 Jan 2017 10:44:19 +1100 Subject: [PATCH 6/7] Allows contexts to directly supply a user (so we can access registration data when e-mailing people.) --- .../templatetags/registrasion_tags.py | 34 ++++++++++++++----- registrasion/views.py | 1 + 2 files changed, 27 insertions(+), 8 deletions(-) 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/views.py b/registrasion/views.py index f4a59eb0..e5ea9218 100644 --- a/registrasion/views.py +++ b/registrasion/views.py @@ -951,6 +951,7 @@ def invoice_mailout(request): body = Template(form.cleaned_data["body"]).render( Context({ "invoice" : invoice, + "user" : invoice.user, }) ) recipient_list = [invoice.user.email] From 42711dde6996d6757fe9a526fbab57840db1497c Mon Sep 17 00:00:00 2001 From: Christopher Neugebauer Date: Sun, 8 Jan 2017 10:45:08 +1100 Subject: [PATCH 7/7] Renames a tempalte. --- registrasion/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/registrasion/views.py b/registrasion/views.py index e5ea9218..24e5ebf1 100644 --- a/registrasion/views.py +++ b/registrasion/views.py @@ -967,4 +967,4 @@ def invoice_mailout(request): "emails": emails, } - return render(request, "registrasion/nag_unpaid.html", data) + return render(request, "registrasion/invoice_mailout.html", data)