Merge branch 'chrisjrn/attendee_manifest'

This commit is contained in:
Christopher Neugebauer 2016-09-02 16:07:40 +10:00
commit 85398a5cf5
8 changed files with 244 additions and 95 deletions

View file

@ -0,0 +1,95 @@
''' NEEDS TESTS '''
from registrasion.models import commerce
from registrasion.models import inventory
from collections import namedtuple
from django.db.models import Case
from django.db.models import Q
from django.db.models import Sum
from django.db.models import When
from django.db.models import Value
_ProductAndQuantity = namedtuple("ProductAndQuantity", ["product", "quantity"])
class ProductAndQuantity(_ProductAndQuantity):
''' Class that holds a product and a quantity.
Attributes:
product (models.inventory.Product)
quantity (int)
'''
pass
class ItemController(object):
def __init__(self, user):
self.user = user
def items_purchased(self, category=None):
''' Aggregates the items that this user has purchased.
Arguments:
category (Optional[models.inventory.Category]): the category
of items to restrict to.
Returns:
[ProductAndQuantity, ...]: A list of product-quantity pairs,
aggregating like products from across multiple invoices.
'''
in_cart = (
Q(productitem__cart__user=self.user) &
Q(productitem__cart__status=commerce.Cart.STATUS_PAID)
)
quantities_in_cart = When(
in_cart,
then="productitem__quantity",
)
quantities_or_zero = Case(
quantities_in_cart,
default=Value(0),
)
products = inventory.Product.objects
if category:
products = products.filter(category=category)
products = products.select_related("category")
products = products.annotate(quantity=Sum(quantities_or_zero))
products = products.filter(quantity__gt=0)
out = []
for prod in products:
out.append(ProductAndQuantity(prod, prod.quantity))
return out
def items_pending(self):
''' Gets all of the items that the user has reserved, but has not yet
paid for.
Returns:
[ProductAndQuantity, ...]: A list of product-quantity pairs for the
items that the user has not yet paid for.
'''
all_items = commerce.ProductItem.objects.filter(
cart__user=self.user,
cart__status=commerce.Cart.STATUS_ACTIVE,
).select_related(
"product",
"product__category",
).order_by(
"product__category__order",
"product__order",
)
return all_items

View file

@ -347,16 +347,3 @@ class VoucherForm(forms.Form):
help_text="If you have a voucher code, enter it here",
required=False,
)
# Staff-facing forms.
class ProductAndCategoryForm(forms.Form):
product = forms.ModelMultipleChoiceField(
queryset=inventory.Product.objects.all(),
required=False,
)
category = forms.ModelMultipleChoiceField(
queryset=inventory.Category.objects.all(),
required=False,
)

View file

@ -0,0 +1,22 @@
from registrasion.models import inventory
from django import forms
# Staff-facing forms.
class ProductAndCategoryForm(forms.Form):
product = forms.ModelMultipleChoiceField(
queryset=inventory.Product.objects.all(),
required=False,
)
category = forms.ModelMultipleChoiceField(
queryset=inventory.Category.objects.all(),
required=False,
)
class UserIdForm(forms.Form):
user = forms.IntegerField(
label="User ID",
required=False,
)

View file

@ -13,6 +13,7 @@ _all_report_views = []
class Report(object):
def __init__(self, title, headings, data, link_view=None):
self._title = title
self._headings = headings
self._data = data
self._link_view = link_view
@ -66,12 +67,15 @@ def report_view(title, form_type=None):
else:
form = None
report = view(request, form, *a, **k)
reports = view(request, form, *a, **k)
if isinstance(reports, Report):
reports = [reports]
ctx = {
"title": title,
"form": form,
"report": report,
"reports": reports,
}
return render(request, "registrasion/report.html", ctx)

View file

@ -1,13 +1,16 @@
import forms
from django.contrib.auth.decorators import user_passes_test
from django.core.urlresolvers import reverse
from django.db import models
from django.db.models import F, Q
from django.db.models import Sum
from django.db.models import Count, Sum
from django.db.models import Case, When, Value
from django.shortcuts import render
from registrasion import forms
from registrasion.controllers.item import ItemController
from registrasion.models import commerce
from registrasion.models import people
from registrasion import views
from reports import get_all_reports
@ -194,3 +197,110 @@ def credit_notes(request, form):
])
return Report("Credit Notes", headings, data, link_view="credit_note")
@report_view("Attendee", form_type=forms.UserIdForm)
def attendee(request, form, attendee_id=None):
''' Returns a list of all manifested attendees if no attendee is specified,
else displays the attendee manifest. '''
if attendee_id is None and not form.has_changed():
return attendee_list(request)
if attendee_id is None:
attendee_id = form.user
attendee = people.Attendee.objects.get(id=attendee_id)
reports = []
# TODO: METADATA.
ic = ItemController(attendee.user)
# Paid products
headings = ["Product", "Quantity"]
data = []
for pq in ic.items_purchased():
data.append([
pq.product,
pq.quantity,
])
reports.append(Report("Paid Products", headings, data))
# Unpaid products
headings = ["Product", "Quantity"]
data = []
for pq in ic.items_pending():
data.append([
pq.product,
pq.quantity,
])
reports.append( Report("Unpaid Products", headings, data))
# Invoices
headings = ["Invoice ID", "Status", "Value"]
data = []
invoices = commerce.Invoice.objects.filter(
user=attendee.user,
)
for invoice in invoices:
data.append([
invoice.id, invoice.get_status_display(), invoice.value,
])
reports.append(Report("Invoices", headings, data, link_view="invoice"))
# Credit Notes
headings = ["Note ID", "Status", "Value"]
data = []
credit_notes = commerce.CreditNote.objects.filter(
invoice__user=attendee.user,
)
for credit_note in credit_notes:
data.append([
credit_note.id, credit_note.status, credit_note.value,
])
reports.append(
Report("Credit Notes", headings, data, link_view="credit_note")
)
return reports
def attendee_list(request):
''' Returns a list of all attendees. '''
attendees = people.Attendee.objects.all().select_related(
"attendeeprofilebase",
)
attendees = attendees.values("id", "user__email").annotate(
has_registered=Count(
Q(user__invoice__status=commerce.Invoice.STATUS_PAID)
),
)
headings = [
"User ID", "Email", "Has registered",
]
data = []
for attendee in attendees:
data.append([
attendee["id"],
attendee["user__email"],
attendee["has_registered"],
])
# Sort by whether they've registered, then ID.
data.sort(key=lambda attendee: (-attendee[2], attendee[0]))
return Report("Attendees", headings, data, link_view="attendee")

View file

@ -1,31 +1,12 @@
from registrasion.models import commerce
from registrasion.models import inventory
from registrasion.controllers.category import CategoryController
from registrasion.controllers.item import ItemController
from collections import namedtuple
from django import template
from django.db.models import Case
from django.db.models import Q
from django.db.models import Sum
from django.db.models import When
from django.db.models import Value
register = template.Library()
_ProductAndQuantity = namedtuple("ProductAndQuantity", ["product", "quantity"])
class ProductAndQuantity(_ProductAndQuantity):
''' Class that holds a product and a quantity.
Attributes:
product (models.inventory.Product)
quantity (int)
'''
pass
@register.assignment_tag(takes_context=True)
def available_categories(context):
@ -67,71 +48,18 @@ def invoices(context):
@register.assignment_tag(takes_context=True)
def items_pending(context):
''' Gets all of the items that the user has reserved, but has not yet
paid for.
Returns:
[ProductAndQuantity, ...]: A list of product-quantity pairs for the
items that the user has not yet paid for.
'''
all_items = commerce.ProductItem.objects.filter(
cart__user=context.request.user,
cart__status=commerce.Cart.STATUS_ACTIVE,
).select_related(
"product",
"product__category",
).order_by(
"product__category__order",
"product__order",
)
return all_items
''' Gets all of the items that the user from this context has reserved.'''
return ItemController(context.request.user).items_pending()
@register.assignment_tag(takes_context=True)
def items_purchased(context, category=None):
''' Aggregates the items that this user has purchased.
''' Returns the items purchased for this user. '''
Arguments:
category (Optional[models.inventory.Category]): the category of items
to restrict to.
Returns:
[ProductAndQuantity, ...]: A list of product-quantity pairs,
aggregating like products from across multiple invoices.
'''
in_cart = (
Q(productitem__cart__user=context.request.user) &
Q(productitem__cart__status=commerce.Cart.STATUS_PAID)
return ItemController(context.request.user).items_purchased(
category=category
)
quantities_in_cart = When(
in_cart,
then="productitem__quantity",
)
quantities_or_zero = Case(
quantities_in_cart,
default=Value(0),
)
products = inventory.Product.objects
if category:
products = products.filter(category=category)
products = products.select_related("category")
products = products.annotate(quantity=Sum(quantities_or_zero))
products = products.filter(quantity__gt=0)
out = []
for prod in products:
out.append(ProductAndQuantity(prod, prod.quantity))
return out
@register.filter
def multiply(value, arg):

View file

@ -37,6 +37,8 @@ public = [
reports = [
url(r"^$", reporting_views.reports_list, name="reports_list"),
url(r"^attendee/?$", reporting_views.attendee, name="attendee"),
url(r"^attendee/([0-9]*)$", reporting_views.attendee, name="attendee"),
url(
r"^credit_notes/?$",
reporting_views.credit_notes,

View file

@ -747,6 +747,7 @@ def credit_note(request, note_id, access_code=None):
'''
note_id = int(note_id)
current_note = CreditNoteController.for_id_or_404(note_id)
apply_form = forms.ApplyCreditNoteForm(