Can send out information e-mails to people with paid invoices.
Merge branch 'chrisjrn/email_reminders'
This commit is contained in:
commit
cbb04cbdf4
5 changed files with 74 additions and 24 deletions
|
@ -413,7 +413,16 @@ def staff_products_formset_factory(user):
|
||||||
return forms.formset_factory(form_type)
|
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(
|
invoice = forms.ModelMultipleChoiceField(
|
||||||
widget=forms.CheckboxSelectMultiple,
|
widget=forms.CheckboxSelectMultiple,
|
||||||
queryset=commerce.Invoice.objects.all(),
|
queryset=commerce.Invoice.objects.all(),
|
||||||
|
@ -423,18 +432,26 @@ class InvoiceNagForm(forms.Form):
|
||||||
body = forms.CharField(
|
body = forms.CharField(
|
||||||
widget=forms.Textarea,
|
widget=forms.Textarea,
|
||||||
)
|
)
|
||||||
|
action = forms.TypedChoiceField(
|
||||||
|
widget=forms.RadioSelect,
|
||||||
|
coerce=int,
|
||||||
|
choices=ACTION_CHOICES,
|
||||||
|
initial=ACTION_PREVIEW,
|
||||||
|
)
|
||||||
|
|
||||||
def __init__(self, *a, **k):
|
def __init__(self, *a, **k):
|
||||||
category = k.pop('category', None) or []
|
category = k.pop('category', None) or []
|
||||||
product = k.pop('product', None) or []
|
product = k.pop('product', None) or []
|
||||||
|
status = int(k.pop('status', None) or 0)
|
||||||
|
|
||||||
category = [int(i) for i in category]
|
category = [int(i) for i in category]
|
||||||
product = [int(i) for i in product]
|
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(
|
qs = commerce.Invoice.objects.filter(
|
||||||
status=commerce.Invoice.STATUS_UNPAID,
|
status=status or commerce.Invoice.STATUS_UNPAID,
|
||||||
).filter(
|
).filter(
|
||||||
Q(lineitem__product__category__in=category) |
|
Q(lineitem__product__category__in=category) |
|
||||||
Q(lineitem__product__in=product)
|
Q(lineitem__product__in=product)
|
||||||
|
|
|
@ -593,10 +593,11 @@ def attendee_data(request, form, user_id=None):
|
||||||
|
|
||||||
# Add invoice nag link
|
# Add invoice nag link
|
||||||
links = []
|
links = []
|
||||||
links.append((
|
invoice_mailout = reverse(views.invoice_mailout, args=[]) + "?" + request.META["QUERY_STRING"]
|
||||||
reverse(views.nag_unpaid, args=[]) + "?" + request.META["QUERY_STRING"],
|
links += [
|
||||||
"Send invoice reminders",
|
(invoice_mailout + "&status=1", "Send invoice reminders",),
|
||||||
))
|
(invoice_mailout + "&status=2", "Send mail for paid invoices",),
|
||||||
|
]
|
||||||
|
|
||||||
if items.count() > 0:
|
if items.count() > 0:
|
||||||
output.append(Links("Actions", links))
|
output.append(Links("Actions", links))
|
||||||
|
|
|
@ -9,6 +9,15 @@ from urllib import urlencode
|
||||||
register = template.Library()
|
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)
|
@register.assignment_tag(takes_context=True)
|
||||||
def available_categories(context):
|
def available_categories(context):
|
||||||
''' Gets all of the currently available products.
|
''' Gets all of the currently available products.
|
||||||
|
@ -18,13 +27,13 @@ def available_categories(context):
|
||||||
have Products that the current user can reserve.
|
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)
|
@register.assignment_tag(takes_context=True)
|
||||||
def missing_categories(context):
|
def missing_categories(context):
|
||||||
''' Adds the categories that the user does not currently have. '''
|
''' 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))
|
categories_available = set(CategoryController.available_categories(user))
|
||||||
items = ItemController(user).items_pending_or_purchased()
|
items = ItemController(user).items_pending_or_purchased()
|
||||||
|
|
||||||
|
@ -47,7 +56,7 @@ def available_credit(context):
|
||||||
'''
|
'''
|
||||||
|
|
||||||
notes = commerce.CreditNote.unclaimed().filter(
|
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
|
ret = notes.values("amount").aggregate(Sum("amount"))["amount__sum"] or 0
|
||||||
return 0 - ret
|
return 0 - ret
|
||||||
|
@ -59,20 +68,29 @@ def invoices(context):
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
[models.commerce.Invoice, ...]: All of the current user's invoices. '''
|
[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)
|
@register.assignment_tag(takes_context=True)
|
||||||
def items_pending(context):
|
def items_pending(context):
|
||||||
''' Gets all of the items that the user from this context has reserved.'''
|
''' Gets all of the items that the user from this context has reserved.
|
||||||
return ItemController(context.request.user).items_pending()
|
|
||||||
|
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)
|
@register.assignment_tag(takes_context=True)
|
||||||
def items_purchased(context, category=None):
|
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
|
category=category
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -12,8 +12,8 @@ from .views import (
|
||||||
guided_registration,
|
guided_registration,
|
||||||
invoice,
|
invoice,
|
||||||
invoice_access,
|
invoice_access,
|
||||||
|
invoice_mailout,
|
||||||
manual_payment,
|
manual_payment,
|
||||||
nag_unpaid,
|
|
||||||
product_category,
|
product_category,
|
||||||
refund,
|
refund,
|
||||||
review,
|
review,
|
||||||
|
@ -35,7 +35,7 @@ public = [
|
||||||
refund, name="refund"),
|
refund, name="refund"),
|
||||||
url(r"^invoice_access/([A-Z0-9]+)$", invoice_access,
|
url(r"^invoice_access/([A-Z0-9]+)$", invoice_access,
|
||||||
name="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"^profile$", edit_profile, name="attendee_edit"),
|
||||||
url(r"^register$", guided_registration, name="guided_registration"),
|
url(r"^register$", guided_registration, name="guided_registration"),
|
||||||
url(r"^review$", review, name="review"),
|
url(r"^review$", review, name="review"),
|
||||||
|
|
|
@ -920,19 +920,28 @@ def extend_reservation(request, user_id, days=7):
|
||||||
return redirect(request.META["HTTP_REFERER"])
|
return redirect(request.META["HTTP_REFERER"])
|
||||||
|
|
||||||
|
|
||||||
|
Email = namedtuple(
|
||||||
|
"Email",
|
||||||
|
("subject", "body", "from_email", "recipient_list"),
|
||||||
|
)
|
||||||
|
|
||||||
@user_passes_test(_staff_only)
|
@user_passes_test(_staff_only)
|
||||||
def nag_unpaid(request):
|
def invoice_mailout(request):
|
||||||
''' Allows staff to nag users with unpaid invoices. '''
|
''' Allows staff to send emails to users based on their invoice status. '''
|
||||||
|
|
||||||
category = request.GET.getlist("category", [])
|
category = request.GET.getlist("category", [])
|
||||||
product = request.GET.getlist("product", [])
|
product = request.GET.getlist("product", [])
|
||||||
|
status = request.GET.get("status")
|
||||||
|
|
||||||
form = forms.InvoiceNagForm(
|
form = forms.InvoiceEmailForm(
|
||||||
request.POST or None,
|
request.POST or None,
|
||||||
category=category,
|
category=category,
|
||||||
product=product,
|
product=product,
|
||||||
|
status=status,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
emails = []
|
||||||
|
|
||||||
if form.is_valid():
|
if form.is_valid():
|
||||||
emails = []
|
emails = []
|
||||||
for invoice in form.cleaned_data["invoice"]:
|
for invoice in form.cleaned_data["invoice"]:
|
||||||
|
@ -942,15 +951,20 @@ def nag_unpaid(request):
|
||||||
body = Template(form.cleaned_data["body"]).render(
|
body = Template(form.cleaned_data["body"]).render(
|
||||||
Context({
|
Context({
|
||||||
"invoice" : invoice,
|
"invoice" : invoice,
|
||||||
|
"user" : invoice.user,
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
recipient_list = [invoice.user.email]
|
recipient_list = [invoice.user.email]
|
||||||
emails.append((subject, body, from_email, recipient_list))
|
emails.append(Email(subject, body, from_email, recipient_list))
|
||||||
send_mass_mail(emails)
|
|
||||||
messages.info(request, "The e-mails have been sent.")
|
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 = {
|
data = {
|
||||||
"form": form,
|
"form": form,
|
||||||
|
"emails": emails,
|
||||||
}
|
}
|
||||||
|
|
||||||
return render(request, "registrasion/nag_unpaid.html", data)
|
return render(request, "registrasion/invoice_mailout.html", data)
|
||||||
|
|
Loading…
Reference in a new issue