diff --git a/RIGS/finance.py b/RIGS/finance.py index fb075684..20199f0a 100644 --- a/RIGS/finance.py +++ b/RIGS/finance.py @@ -13,13 +13,20 @@ class InvoiceIndex(generic.ListView): template_name = 'RIGS/invoice_list.html' def get_queryset(self): - active = self.model.objects.filter(void=False).select_related('payment_set', 'event').prefetch_related( - 'event__items').defer('event__person', 'event__organisation', 'event__venue', 'event__mic') - set = [] - for invoice in active: - if invoice.balance != 0: - set.append(invoice) - return set + # Manual query is the only way I have found to do this efficiently. Not ideal but needs must + query = self.model.objects.raw( + "SELECT *, " + "(SELECT SUM(ei.cost * ei.quantity) FROM RIGS_eventitem AS ei WHERE ei.event_id = i.event_id) AS cost, " + "(SELECT SUM(p.amount) FROM RIGS_payment AS p WHERE p.invoice_id = i.id) AS payments FROM RIGS_invoice as i " + "HAVING (cost - payments) > 0;" + ) + + items = [] + + for invoice in query: + items.append(invoice) + + return query class InvoiceDetail(generic.DetailView): diff --git a/RIGS/models.py b/RIGS/models.py index da58bbc6..de1156b7 100644 --- a/RIGS/models.py +++ b/RIGS/models.py @@ -247,10 +247,11 @@ class Event(models.Model, RevisionMixin): """ @property def sum_total(self): - total = 0 - for item in self.items.filter(cost__gt=0): - total += item.total_cost - return total + # Manual querying is required for efficiency whilst maintaining floating point arithmetic + total = self.items.raw("SELECT id, SUM(quantity * cost) AS sum_total FROM RIGS_eventitem WHERE event_id=%i" % self.id)[0] + if total.sum_total: + return total.sum_total + return 0.0 @cached_property def vat_rate(self): @@ -329,16 +330,15 @@ class Invoice(models.Model): @property def payment_total(self): - sum = self.payment_set.aggregate(models.Sum('amount'))['amount__sum'] - # for payment in self.payment_set.all(): - # total += payment.amount - if sum: - return sum - return 0 + # Manual querying is required for efficiency whilst maintaining floating point arithmetic + total = self.payment_set.raw("SELECT id, SUM(amount) AS total FROM RIGS_payment WHERE invoice_id=%i" % self.id)[0] + if total.total: + return total.total + return 0.0 @property def balance(self): - return self.sum_total - self.payment_total + return float(self.sum_total) - float(self.payment_total) def __str__(self): return "%i: %s (%.2f)" % (self.pk, self.event, self.balance)