Merge pull request #859 from bornhack/add_ticket_stats_on_overview_take_two

Ticket stats take two
This commit is contained in:
Thomas Steen Rasmussen 2021-07-30 07:28:33 +02:00 committed by GitHub
commit db7d4909c7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 50 additions and 18 deletions

View file

@ -20,6 +20,7 @@
<th class="text-right">Total Income</th>
<th class="text-right">Total Cost</th>
<th class="text-right">Total Profit</th>
<th class="text-right">Avg. Ticket Price</th>
</tr>
</thead>
<tbody>
@ -32,6 +33,7 @@
<td class="text-right">{{ tt.total_income|floatformat:"2" }}&nbsp;DKK</td>
<td class="text-right">{{ tt.total_cost|floatformat:"2" }}&nbsp;DKK</td>
<td class="text-right">{{ tt.total_profit|floatformat:"2" }}&nbsp;DKK</td>
<td class="text-right">{{ tt.avg_ticket_price|floatformat:"2" }}&nbsp;DKK</td>
</tr>
{% endfor %}
</tbody>

View file

@ -203,7 +203,10 @@ class ShopTicketStatsView(CampViewMixin, OrgaTeamPermissionMixin, ListView):
template_name = "ticket_stats.html"
def get_queryset(self):
return TicketType.objects.with_price_stats().filter(camp=self.camp)
query = TicketType.objects.filter(
camp=self.camp, shopticket__isnull=False
).with_price_stats()
return query
class ShopTicketStatsDetailView(CampViewMixin, OrgaTeamPermissionMixin, ListView):

View file

@ -2,11 +2,21 @@ import base64
import hashlib
import io
import logging
from decimal import Decimal
from typing import Union
import qrcode
from django.conf import settings
from django.db import models
from django.db.models import Count, F, Sum
from django.db.models import (
Count,
Expression,
ExpressionWrapper,
F,
OuterRef,
Subquery,
Sum,
)
from django.urls import reverse_lazy
from django.utils.translation import ugettext_lazy as _
@ -16,29 +26,46 @@ from utils.pdf import generate_pdf_letter
logger = logging.getLogger("bornhack.%s" % __name__)
class TicketTypeManager(models.Manager):
class TicketTypeQuerySet(models.QuerySet):
def with_price_stats(self):
total_units_sold = Sum("shopticket__opr__quantity", distinct=True)
cost = F("shopticket__opr__quantity") * F("shopticket__opr__product__cost")
income = F("shopticket__opr__quantity") * F("shopticket__opr__product__price")
profit = income - cost
total_cost = Sum(cost, distinct=True)
total_profit = Sum(profit, distinct=True)
total_income = Sum(income, distinct=True)
return (
self.filter(shopticket__isnull=False)
.annotate(shopticket_count=Count("shopticket"))
.annotate(total_units_sold=total_units_sold)
.annotate(total_income=total_income)
.annotate(total_cost=total_cost)
.annotate(total_profit=total_profit)
def _make_subquery(annotation: Union[Expression, F]) -> Subquery:
return Subquery(
TicketType.objects.annotate(annotation_value=annotation)
.filter(pk=OuterRef("pk"))
.values("annotation_value")[:1]
)
quantity = F("product__orderproductrelation__quantity")
cost = quantity * F("product__cost")
income = quantity * F("product__price")
avg_ticket_price = Subquery(
TicketType.objects.annotate(units=Sum(quantity))
.annotate(income=Sum(income))
.annotate(
avg_ticket_price=ExpressionWrapper(
F("income") * Decimal("1.0") / F("units"),
output_field=models.DecimalField(),
)
)
.filter(pk=OuterRef("pk"))
.values("avg_ticket_price")[:1],
output_field=models.DecimalField(),
)
return self.annotate(
shopticket_count=_make_subquery(Count("shopticket")),
total_units_sold=_make_subquery(quantity),
total_income=_make_subquery(income),
total_cost=_make_subquery(cost),
total_profit=_make_subquery(income - cost),
avg_ticket_price=avg_ticket_price,
).distinct()
# TicketType can be full week, one day, cabins, parking, merch, hax, massage, etc.
class TicketType(CampRelatedModel, UUIDModel):
objects = TicketTypeManager()
objects = TicketTypeQuerySet.as_manager()
name = models.TextField()
camp = models.ForeignKey("camps.Camp", on_delete=models.PROTECT)
includes_badge = models.BooleanField(default=False)