2016-09-02 04:33:23 +00:00
|
|
|
import forms
|
|
|
|
|
2016-09-01 23:31:12 +00:00
|
|
|
from django.contrib.auth.decorators import user_passes_test
|
2016-09-01 23:55:29 +00:00
|
|
|
from django.core.urlresolvers import reverse
|
2016-08-26 04:59:54 +00:00
|
|
|
from django.db import models
|
2016-08-26 05:53:33 +00:00
|
|
|
from django.db.models import F, Q
|
2016-09-02 05:15:15 +00:00
|
|
|
from django.db.models import Count, Sum
|
2016-08-26 04:59:54 +00:00
|
|
|
from django.db.models import Case, When, Value
|
2016-08-25 10:33:19 +00:00
|
|
|
from django.shortcuts import render
|
|
|
|
|
2016-09-02 05:57:44 +00:00
|
|
|
from registrasion.controllers.item import ItemController
|
2016-09-02 01:20:03 +00:00
|
|
|
from registrasion.models import commerce
|
2016-09-02 05:15:15 +00:00
|
|
|
from registrasion.models import people
|
2016-09-02 01:20:03 +00:00
|
|
|
from registrasion import views
|
2016-08-25 10:33:19 +00:00
|
|
|
|
2016-09-02 01:20:03 +00:00
|
|
|
from reports import get_all_reports
|
2016-09-13 05:32:55 +00:00
|
|
|
from reports import OldReport
|
2016-09-02 01:20:03 +00:00
|
|
|
from reports import report_view
|
2016-08-25 10:33:19 +00:00
|
|
|
|
2016-08-25 11:10:07 +00:00
|
|
|
|
2016-09-13 02:27:07 +00:00
|
|
|
def CURRENCY():
|
|
|
|
return models.DecimalField(decimal_places=2)
|
|
|
|
|
|
|
|
|
2016-09-01 23:55:29 +00:00
|
|
|
@user_passes_test(views._staff_only)
|
|
|
|
def reports_list(request):
|
|
|
|
''' Lists all of the reports currently available. '''
|
|
|
|
|
|
|
|
reports = []
|
|
|
|
|
2016-09-02 00:10:21 +00:00
|
|
|
for report in get_all_reports():
|
2016-09-01 23:55:29 +00:00
|
|
|
reports.append({
|
2016-09-02 01:25:50 +00:00
|
|
|
"name": report.__name__,
|
|
|
|
"url": reverse(report),
|
|
|
|
"description": report.__doc__,
|
2016-09-01 23:55:29 +00:00
|
|
|
})
|
|
|
|
|
|
|
|
reports.sort(key=lambda report: report["name"])
|
|
|
|
|
|
|
|
ctx = {
|
2016-09-02 01:25:50 +00:00
|
|
|
"reports": reports,
|
2016-09-01 23:55:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return render(request, "registrasion/reports_list.html", ctx)
|
|
|
|
|
|
|
|
|
|
|
|
# Report functions
|
|
|
|
|
|
|
|
|
2016-09-02 00:30:12 +00:00
|
|
|
@report_view("Paid items", form_type=forms.ProductAndCategoryForm)
|
2016-08-26 05:11:08 +00:00
|
|
|
def items_sold(request, form):
|
|
|
|
''' Summarises the items sold and discounts granted for a given set of
|
|
|
|
products, or products from categories. '''
|
2016-08-25 10:33:19 +00:00
|
|
|
|
|
|
|
data = None
|
|
|
|
headings = None
|
|
|
|
|
2016-08-26 05:11:08 +00:00
|
|
|
products = form.cleaned_data["product"]
|
|
|
|
categories = form.cleaned_data["category"]
|
|
|
|
|
|
|
|
line_items = commerce.LineItem.objects.filter(
|
|
|
|
Q(product__in=products) | Q(product__category__in=categories),
|
|
|
|
invoice__status=commerce.Invoice.STATUS_PAID,
|
|
|
|
).select_related("invoice")
|
2016-08-25 11:05:02 +00:00
|
|
|
|
2016-08-26 05:11:08 +00:00
|
|
|
line_items = line_items.order_by(
|
|
|
|
# sqlite requires an order_by for .values() to work
|
|
|
|
"-price", "description",
|
|
|
|
).values(
|
|
|
|
"price", "description",
|
|
|
|
).annotate(
|
|
|
|
total_quantity=Sum("quantity"),
|
|
|
|
)
|
2016-08-25 11:05:02 +00:00
|
|
|
|
2016-08-26 05:11:08 +00:00
|
|
|
print line_items
|
2016-08-25 10:33:19 +00:00
|
|
|
|
2016-08-26 05:11:08 +00:00
|
|
|
headings = ["Description", "Quantity", "Price", "Total"]
|
2016-08-26 03:40:49 +00:00
|
|
|
|
2016-08-26 05:11:08 +00:00
|
|
|
data = []
|
|
|
|
total_income = 0
|
|
|
|
for line in line_items:
|
|
|
|
cost = line["total_quantity"] * line["price"]
|
2016-08-26 03:40:49 +00:00
|
|
|
data.append([
|
2016-08-26 05:11:08 +00:00
|
|
|
line["description"], line["total_quantity"],
|
|
|
|
line["price"], cost,
|
2016-08-26 03:40:49 +00:00
|
|
|
])
|
2016-08-26 05:11:08 +00:00
|
|
|
total_income += cost
|
2016-08-25 10:33:19 +00:00
|
|
|
|
2016-08-26 05:11:08 +00:00
|
|
|
data.append([
|
|
|
|
"(TOTAL)", "--", "--", total_income,
|
|
|
|
])
|
2016-08-26 04:59:54 +00:00
|
|
|
|
2016-09-13 05:32:55 +00:00
|
|
|
return OldReport("Paid items", headings, data)
|
2016-08-26 04:59:54 +00:00
|
|
|
|
2016-08-26 05:11:08 +00:00
|
|
|
|
2016-09-03 02:17:03 +00:00
|
|
|
@report_view("Reconcilitation")
|
|
|
|
def reconciliation(request, form):
|
|
|
|
''' Reconciles all sales in the system with the payments in the
|
|
|
|
system. '''
|
|
|
|
|
|
|
|
headings = ["Thing", "Total"]
|
|
|
|
data = []
|
|
|
|
|
|
|
|
sales = commerce.LineItem.objects.filter(
|
|
|
|
invoice__status=commerce.Invoice.STATUS_PAID,
|
|
|
|
).values(
|
|
|
|
"price", "quantity"
|
2016-09-13 02:27:07 +00:00
|
|
|
).aggregate(
|
|
|
|
total=Sum(F("price") * F("quantity"), output_field=CURRENCY()),
|
|
|
|
)
|
2016-09-03 02:17:03 +00:00
|
|
|
|
|
|
|
data.append(["Paid items", sales["total"]])
|
|
|
|
|
|
|
|
payments = commerce.PaymentBase.objects.values(
|
|
|
|
"amount",
|
|
|
|
).aggregate(total=Sum("amount"))
|
|
|
|
|
|
|
|
data.append(["Payments", payments["total"]])
|
|
|
|
|
|
|
|
ucn = commerce.CreditNote.unclaimed().values(
|
|
|
|
"amount"
|
|
|
|
).aggregate(total=Sum("amount"))
|
|
|
|
|
|
|
|
data.append(["Unclaimed credit notes", 0 - ucn["total"]])
|
|
|
|
|
|
|
|
data.append([
|
|
|
|
"(Money not on invoices)",
|
|
|
|
sales["total"] - payments["total"] - ucn["total"],
|
|
|
|
])
|
|
|
|
|
2016-09-13 05:32:55 +00:00
|
|
|
return OldReport("Sales and Payments", headings, data)
|
2016-09-03 02:17:03 +00:00
|
|
|
|
|
|
|
|
2016-09-02 01:25:50 +00:00
|
|
|
@report_view("Product status", form_type=forms.ProductAndCategoryForm)
|
|
|
|
def product_status(request, form):
|
2016-08-26 04:59:54 +00:00
|
|
|
''' Summarises the inventory status of the given items, grouping by
|
|
|
|
invoice status. '''
|
|
|
|
|
2016-08-26 05:11:08 +00:00
|
|
|
products = form.cleaned_data["product"]
|
|
|
|
categories = form.cleaned_data["category"]
|
|
|
|
|
|
|
|
items = commerce.ProductItem.objects.filter(
|
|
|
|
Q(product__in=products) | Q(product__category__in=categories),
|
|
|
|
).select_related("cart", "product")
|
|
|
|
|
2016-08-26 05:53:33 +00:00
|
|
|
items = items.annotate(
|
|
|
|
is_reserved=Case(
|
2016-09-05 11:10:21 +00:00
|
|
|
When(cart__in=commerce.Cart.reserved_carts(), then=Value(True)),
|
|
|
|
default=Value(False),
|
2016-08-26 05:53:33 +00:00
|
|
|
output_field=models.BooleanField(),
|
|
|
|
),
|
|
|
|
)
|
2016-08-26 05:11:08 +00:00
|
|
|
|
|
|
|
items = items.order_by(
|
|
|
|
"product__category__order",
|
|
|
|
"product__order",
|
|
|
|
).values(
|
|
|
|
"product",
|
|
|
|
"product__category__name",
|
|
|
|
"product__name",
|
|
|
|
).annotate(
|
2016-08-26 05:53:33 +00:00
|
|
|
total_paid=Sum(Case(
|
|
|
|
When(
|
|
|
|
cart__status=commerce.Cart.STATUS_PAID,
|
|
|
|
then=F("quantity"),
|
|
|
|
),
|
|
|
|
default=Value(0),
|
|
|
|
)),
|
|
|
|
total_refunded=Sum(Case(
|
|
|
|
When(
|
|
|
|
cart__status=commerce.Cart.STATUS_RELEASED,
|
|
|
|
then=F("quantity"),
|
|
|
|
),
|
|
|
|
default=Value(0),
|
|
|
|
)),
|
|
|
|
total_unreserved=Sum(Case(
|
|
|
|
When(
|
|
|
|
(
|
|
|
|
Q(cart__status=commerce.Cart.STATUS_ACTIVE) &
|
|
|
|
Q(is_reserved=False)
|
|
|
|
),
|
|
|
|
then=F("quantity"),
|
|
|
|
),
|
|
|
|
default=Value(0),
|
|
|
|
)),
|
|
|
|
total_reserved=Sum(Case(
|
|
|
|
When(
|
|
|
|
(
|
|
|
|
Q(cart__status=commerce.Cart.STATUS_ACTIVE) &
|
|
|
|
Q(is_reserved=True)
|
|
|
|
),
|
|
|
|
then=F("quantity"),
|
|
|
|
),
|
|
|
|
default=Value(0),
|
|
|
|
)),
|
2016-08-26 05:11:08 +00:00
|
|
|
)
|
|
|
|
|
2016-08-26 05:53:33 +00:00
|
|
|
headings = [
|
|
|
|
"Product", "Paid", "Reserved", "Unreserved", "Refunded",
|
|
|
|
]
|
2016-08-26 05:11:08 +00:00
|
|
|
data = []
|
|
|
|
|
|
|
|
for item in items:
|
|
|
|
data.append([
|
|
|
|
"%s - %s" % (
|
|
|
|
item["product__category__name"], item["product__name"]
|
|
|
|
),
|
2016-08-26 05:53:33 +00:00
|
|
|
item["total_paid"],
|
|
|
|
item["total_reserved"],
|
|
|
|
item["total_unreserved"],
|
|
|
|
item["total_refunded"],
|
2016-08-26 05:11:08 +00:00
|
|
|
])
|
2016-08-26 04:59:54 +00:00
|
|
|
|
2016-09-13 05:32:55 +00:00
|
|
|
return OldReport("Inventory", headings, data)
|
2016-09-02 00:40:28 +00:00
|
|
|
|
|
|
|
|
|
|
|
@report_view("Credit notes")
|
|
|
|
def credit_notes(request, form):
|
|
|
|
''' Shows all of the credit notes in the system. '''
|
|
|
|
|
|
|
|
notes = commerce.CreditNote.objects.all().select_related(
|
|
|
|
"creditnoterefund",
|
|
|
|
"creditnoteapplication",
|
|
|
|
"invoice",
|
|
|
|
"invoice__user__attendee__attendeeprofilebase",
|
|
|
|
)
|
|
|
|
|
|
|
|
headings = [
|
|
|
|
"id", "Owner", "Status", "Value",
|
|
|
|
]
|
|
|
|
|
|
|
|
data = []
|
|
|
|
for note in notes:
|
|
|
|
data.append([
|
|
|
|
note.id,
|
|
|
|
note.invoice.user.attendee.attendeeprofilebase.invoice_recipient(),
|
|
|
|
note.status,
|
|
|
|
note.value,
|
|
|
|
])
|
|
|
|
|
2016-09-13 05:32:55 +00:00
|
|
|
return OldReport("Credit Notes", headings, data, link_view="credit_note")
|
2016-09-02 05:15:15 +00:00
|
|
|
|
|
|
|
|
|
|
|
@report_view("Attendee", form_type=forms.UserIdForm)
|
2016-09-03 04:24:58 +00:00
|
|
|
def attendee(request, form, user_id=None):
|
2016-09-02 05:15:15 +00:00
|
|
|
''' Returns a list of all manifested attendees if no attendee is specified,
|
|
|
|
else displays the attendee manifest. '''
|
|
|
|
|
2016-09-03 04:24:58 +00:00
|
|
|
if user_id is None and not form.has_changed():
|
2016-09-02 05:15:15 +00:00
|
|
|
return attendee_list(request)
|
|
|
|
|
2016-09-02 06:14:58 +00:00
|
|
|
if form.cleaned_data["user"] is not None:
|
2016-09-03 04:24:58 +00:00
|
|
|
user_id = form.cleaned_data["user"]
|
2016-09-02 05:43:01 +00:00
|
|
|
|
2016-09-03 04:24:58 +00:00
|
|
|
attendee = people.Attendee.objects.get(user__id=user_id)
|
2016-09-02 05:43:01 +00:00
|
|
|
|
2016-09-02 05:15:15 +00:00
|
|
|
reports = []
|
|
|
|
|
|
|
|
# TODO: METADATA.
|
|
|
|
|
2016-09-02 05:57:44 +00:00
|
|
|
ic = ItemController(attendee.user)
|
2016-09-02 05:15:15 +00:00
|
|
|
# Paid products
|
|
|
|
headings = ["Product", "Quantity"]
|
|
|
|
data = []
|
2016-09-02 05:57:44 +00:00
|
|
|
|
|
|
|
for pq in ic.items_purchased():
|
|
|
|
data.append([
|
|
|
|
pq.product,
|
|
|
|
pq.quantity,
|
|
|
|
])
|
|
|
|
|
2016-09-13 05:32:55 +00:00
|
|
|
reports.append(OldReport("Paid Products", headings, data))
|
2016-09-02 05:15:15 +00:00
|
|
|
|
|
|
|
# Unpaid products
|
|
|
|
headings = ["Product", "Quantity"]
|
|
|
|
data = []
|
2016-09-02 05:57:44 +00:00
|
|
|
|
|
|
|
for pq in ic.items_pending():
|
|
|
|
data.append([
|
|
|
|
pq.product,
|
|
|
|
pq.quantity,
|
|
|
|
])
|
|
|
|
|
2016-09-13 05:32:55 +00:00
|
|
|
reports.append( OldReport("Unpaid Products", headings, data))
|
2016-09-02 05:15:15 +00:00
|
|
|
|
|
|
|
# Invoices
|
2016-09-02 05:43:01 +00:00
|
|
|
headings = ["Invoice ID", "Status", "Value"]
|
2016-09-02 05:15:15 +00:00
|
|
|
data = []
|
2016-09-02 05:43:01 +00:00
|
|
|
|
|
|
|
invoices = commerce.Invoice.objects.filter(
|
|
|
|
user=attendee.user,
|
|
|
|
)
|
|
|
|
for invoice in invoices:
|
|
|
|
data.append([
|
|
|
|
invoice.id, invoice.get_status_display(), invoice.value,
|
|
|
|
])
|
|
|
|
|
2016-09-13 05:32:55 +00:00
|
|
|
reports.append(OldReport("Invoices", headings, data, link_view="invoice"))
|
2016-09-02 05:15:15 +00:00
|
|
|
|
|
|
|
# Credit Notes
|
|
|
|
headings = ["Note ID", "Status", "Value"]
|
|
|
|
data = []
|
2016-09-02 05:43:01 +00:00
|
|
|
|
|
|
|
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(
|
2016-09-13 05:32:55 +00:00
|
|
|
OldReport("Credit Notes", headings, data, link_view="credit_note")
|
2016-09-02 05:43:01 +00:00
|
|
|
)
|
2016-09-02 05:15:15 +00:00
|
|
|
|
2016-09-03 01:51:12 +00:00
|
|
|
# All payments
|
|
|
|
headings = ["To Invoice", "Payment ID", "Reference", "Amount"]
|
|
|
|
data = []
|
|
|
|
|
|
|
|
payments = commerce.PaymentBase.objects.filter(
|
|
|
|
invoice__user=attendee.user,
|
|
|
|
)
|
|
|
|
for payment in payments:
|
|
|
|
data.append([
|
|
|
|
payment.invoice.id, payment.id, payment.reference, payment.amount,
|
|
|
|
])
|
|
|
|
|
|
|
|
reports.append(
|
2016-09-13 05:32:55 +00:00
|
|
|
OldReport("Payments", headings, data, link_view="invoice")
|
2016-09-03 01:51:12 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
|
2016-09-02 05:15:15 +00:00
|
|
|
return reports
|
|
|
|
|
|
|
|
|
|
|
|
def attendee_list(request):
|
|
|
|
''' Returns a list of all attendees. '''
|
|
|
|
|
|
|
|
attendees = people.Attendee.objects.all().select_related(
|
|
|
|
"attendeeprofilebase",
|
2016-09-03 02:45:21 +00:00
|
|
|
"user",
|
2016-09-02 05:15:15 +00:00
|
|
|
)
|
2016-09-02 05:37:57 +00:00
|
|
|
|
2016-09-03 02:45:21 +00:00
|
|
|
attendees = attendees.annotate(
|
2016-09-02 05:15:15 +00:00
|
|
|
has_registered=Count(
|
|
|
|
Q(user__invoice__status=commerce.Invoice.STATUS_PAID)
|
|
|
|
),
|
|
|
|
)
|
|
|
|
|
|
|
|
headings = [
|
2016-09-03 02:45:21 +00:00
|
|
|
"User ID", "Name", "Email", "Has registered",
|
2016-09-02 05:15:15 +00:00
|
|
|
]
|
|
|
|
|
|
|
|
data = []
|
|
|
|
|
|
|
|
for attendee in attendees:
|
|
|
|
data.append([
|
2016-09-03 04:24:58 +00:00
|
|
|
attendee.user.id,
|
2016-09-03 02:45:21 +00:00
|
|
|
attendee.attendeeprofilebase.attendee_name(),
|
|
|
|
attendee.user.email,
|
|
|
|
attendee.has_registered > 0,
|
2016-09-02 05:15:15 +00:00
|
|
|
])
|
|
|
|
|
|
|
|
# Sort by whether they've registered, then ID.
|
2016-09-03 02:45:21 +00:00
|
|
|
data.sort(key=lambda attendee: (-attendee[3], attendee[0]))
|
2016-09-02 05:15:15 +00:00
|
|
|
|
2016-09-13 05:32:55 +00:00
|
|
|
return OldReport("Attendees", headings, data, link_view="attendee")
|