mirror of
https://github.com/nottinghamtec/PyRIGS.git
synced 2026-03-12 06:58:24 +00:00
Merged feature/invoice-total into develop
This commit is contained in:
@@ -1,32 +1,39 @@
|
||||
import cStringIO as StringIO
|
||||
import datetime
|
||||
import re
|
||||
|
||||
from django.contrib import messages
|
||||
from django.core.urlresolvers import reverse_lazy
|
||||
from django.db import connection
|
||||
from django.http import Http404, HttpResponseRedirect
|
||||
from django.views import generic
|
||||
from django.template import RequestContext
|
||||
from django.template.loader import get_template
|
||||
from django.http import HttpResponse
|
||||
from django.shortcuts import get_object_or_404
|
||||
from django.contrib import messages
|
||||
import datetime
|
||||
from django.template import RequestContext
|
||||
from django.template.loader import get_template
|
||||
from django.views import generic
|
||||
from z3c.rml import rml2pdf
|
||||
|
||||
from RIGS import models
|
||||
|
||||
import re
|
||||
|
||||
class InvoiceIndex(generic.ListView):
|
||||
model = models.Invoice
|
||||
template_name = 'RIGS/invoice_list.html'
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super(InvoiceIndex, self).get_context_data(**kwargs)
|
||||
total = 0
|
||||
for i in context['object_list']:
|
||||
total += i.balance
|
||||
context['total'] = total
|
||||
return context
|
||||
|
||||
def get_queryset(self):
|
||||
# Manual query is the only way I have found to do this efficiently. Not ideal but needs must
|
||||
sql = "SELECT * FROM " \
|
||||
"(SELECT " \
|
||||
"(SELECT COUNT(p.amount) FROM \"RIGS_payment\" as p WHERE p.invoice_id=\"RIGS_invoice\".id) AS \"payment_count\", " \
|
||||
"(SELECT COUNT(p.amount) FROM \"RIGS_payment\" AS p WHERE p.invoice_id=\"RIGS_invoice\".id) AS \"payment_count\", " \
|
||||
"(SELECT SUM(ei.cost * ei.quantity) FROM \"RIGS_eventitem\" AS ei WHERE ei.event_id=\"RIGS_invoice\".event_id) AS \"cost\", " \
|
||||
"(SELECT SUM(p.amount) FROM \"RIGS_payment\" as p WHERE p.invoice_id=\"RIGS_invoice\".id) AS \"payments\", " \
|
||||
"(SELECT SUM(p.amount) FROM \"RIGS_payment\" AS p WHERE p.invoice_id=\"RIGS_invoice\".id) AS \"payments\", " \
|
||||
"\"RIGS_invoice\".\"id\", \"RIGS_invoice\".\"event_id\", \"RIGS_invoice\".\"invoice_date\", \"RIGS_invoice\".\"void\" FROM \"RIGS_invoice\") " \
|
||||
"AS sub " \
|
||||
"WHERE (((cost > 0.0) AND (payment_count=0)) OR (cost - payments) <> 0.0) AND void = '0'" \
|
||||
@@ -40,6 +47,7 @@ class InvoiceIndex(generic.ListView):
|
||||
class InvoiceDetail(generic.DetailView):
|
||||
model = models.Invoice
|
||||
|
||||
|
||||
class InvoicePrint(generic.View):
|
||||
def get(self, request, pk):
|
||||
invoice = get_object_or_404(models.Invoice, pk=pk)
|
||||
@@ -72,6 +80,7 @@ class InvoicePrint(generic.View):
|
||||
response.write(pdfData)
|
||||
return response
|
||||
|
||||
|
||||
class InvoiceVoid(generic.View):
|
||||
def get(self, *args, **kwargs):
|
||||
pk = kwargs.get('pk')
|
||||
@@ -94,14 +103,27 @@ class InvoiceWaiting(generic.ListView):
|
||||
paginate_by = 25
|
||||
template_name = 'RIGS/event_invoice.html'
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super(InvoiceWaiting, self).get_context_data(**kwargs)
|
||||
total = 0
|
||||
for obj in self.get_objects():
|
||||
total += obj.sum_total
|
||||
context['total'] = total
|
||||
return context
|
||||
|
||||
def get_queryset(self):
|
||||
return self.get_objects()
|
||||
|
||||
def get_objects(self):
|
||||
# @todo find a way to select items
|
||||
events = self.model.objects.filter(is_rig=True, end_date__lt=datetime.date.today(),
|
||||
invoice__isnull=True) \
|
||||
.order_by('start_date') \
|
||||
.select_related('person',
|
||||
'organisation',
|
||||
'venue', 'mic')
|
||||
'venue', 'mic') \
|
||||
.prefetch_related('items')
|
||||
|
||||
return events
|
||||
|
||||
|
||||
|
||||
@@ -1,20 +1,21 @@
|
||||
import datetime
|
||||
import hashlib
|
||||
import datetime, pytz
|
||||
|
||||
from django.db import models, connection
|
||||
from django.contrib.auth.models import AbstractUser
|
||||
from django.conf import settings
|
||||
from django.utils.functional import cached_property
|
||||
from django.utils.encoding import python_2_unicode_compatible
|
||||
import reversion
|
||||
import string
|
||||
import pytz
|
||||
import random
|
||||
import string
|
||||
from collections import Counter
|
||||
from django.core.urlresolvers import reverse_lazy
|
||||
from django.core.exceptions import ValidationError
|
||||
|
||||
from decimal import Decimal
|
||||
|
||||
import reversion
|
||||
from django.conf import settings
|
||||
from django.contrib.auth.models import AbstractUser
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.core.urlresolvers import reverse_lazy
|
||||
from django.db import models
|
||||
from django.utils.encoding import python_2_unicode_compatible
|
||||
from django.utils.functional import cached_property
|
||||
|
||||
|
||||
# Create your models here.
|
||||
@python_2_unicode_compatible
|
||||
class Profile(AbstractUser):
|
||||
@@ -55,6 +56,7 @@ class Profile(AbstractUser):
|
||||
('view_profile', 'Can view Profile'),
|
||||
)
|
||||
|
||||
|
||||
class RevisionMixin(object):
|
||||
@property
|
||||
def last_edited_at(self):
|
||||
@@ -83,6 +85,7 @@ class RevisionMixin(object):
|
||||
else:
|
||||
return None
|
||||
|
||||
|
||||
@reversion.register
|
||||
@python_2_unicode_compatible
|
||||
class Person(models.Model, RevisionMixin):
|
||||
@@ -238,12 +241,18 @@ class Venue(models.Model, RevisionMixin):
|
||||
class EventManager(models.Manager):
|
||||
def current_events(self):
|
||||
events = self.filter(
|
||||
(models.Q(start_date__gte=datetime.date.today(), end_date__isnull=True, dry_hire=False) & ~models.Q(status=Event.CANCELLED)) | # Starts after with no end
|
||||
(models.Q(end_date__gte=datetime.date.today(), dry_hire=False) & ~models.Q(status=Event.CANCELLED)) | # Ends after
|
||||
(models.Q(dry_hire=True, start_date__gte=datetime.date.today()) & ~models.Q(status=Event.CANCELLED)) | # Active dry hire
|
||||
(models.Q(dry_hire=True, checked_in_by__isnull=True) & (models.Q(status=Event.BOOKED) | models.Q(status=Event.CONFIRMED))) | # Active dry hire GT
|
||||
(models.Q(start_date__gte=datetime.date.today(), end_date__isnull=True, dry_hire=False) & ~models.Q(
|
||||
status=Event.CANCELLED)) | # Starts after with no end
|
||||
(models.Q(end_date__gte=datetime.date.today(), dry_hire=False) & ~models.Q(
|
||||
status=Event.CANCELLED)) | # Ends after
|
||||
(models.Q(dry_hire=True, start_date__gte=datetime.date.today()) & ~models.Q(
|
||||
status=Event.CANCELLED)) | # Active dry hire
|
||||
(models.Q(dry_hire=True, checked_in_by__isnull=True) & (
|
||||
models.Q(status=Event.BOOKED) | models.Q(status=Event.CONFIRMED))) | # Active dry hire GT
|
||||
models.Q(status=Event.CANCELLED, start_date__gte=datetime.date.today()) # Canceled but not started
|
||||
).order_by('start_date', 'end_date', 'start_time', 'end_time', 'meet_at').select_related('person', 'organisation', 'venue', 'mic')
|
||||
).order_by('start_date', 'end_date', 'start_time', 'end_time', 'meet_at').select_related('person',
|
||||
'organisation',
|
||||
'venue', 'mic')
|
||||
return events
|
||||
|
||||
def events_in_bounds(self, start, end):
|
||||
@@ -259,7 +268,9 @@ class EventManager(models.Manager):
|
||||
(models.Q(meet_at__lte=start, start_date__gte=end)) | # Meet before, start after
|
||||
(models.Q(meet_at__lte=start, end_date__gte=end)) # Meet before, end after
|
||||
|
||||
).order_by('start_date', 'end_date', 'start_time', 'end_time', 'meet_at').select_related('person', 'organisation', 'venue', 'mic')
|
||||
).order_by('start_date', 'end_date', 'start_time', 'end_time', 'meet_at').select_related('person',
|
||||
'organisation',
|
||||
'venue', 'mic')
|
||||
return events
|
||||
|
||||
def rig_count(self):
|
||||
@@ -301,7 +312,8 @@ class Event(models.Model, RevisionMixin):
|
||||
status = models.IntegerField(choices=EVENT_STATUS_CHOICES, default=PROVISIONAL)
|
||||
dry_hire = models.BooleanField(default=False)
|
||||
is_rig = models.BooleanField(default=True)
|
||||
based_on = models.ForeignKey('Event', on_delete=models.SET_NULL, related_name='future_events', blank=True, null=True)
|
||||
based_on = models.ForeignKey('Event', on_delete=models.SET_NULL, related_name='future_events', blank=True,
|
||||
null=True)
|
||||
|
||||
# Timing
|
||||
start_date = models.DateField()
|
||||
@@ -327,6 +339,7 @@ class Event(models.Model, RevisionMixin):
|
||||
"""
|
||||
EX Vat
|
||||
"""
|
||||
|
||||
@property
|
||||
def sum_total(self):
|
||||
# Manual querying is required for efficiency whilst maintaining floating point arithmetic
|
||||
@@ -341,7 +354,8 @@ class Event(models.Model, RevisionMixin):
|
||||
# for item in self.items.filter(cost__gt=0).extra(select="SUM(cost * quantity) AS sum"):
|
||||
# total += item.sum
|
||||
total = EventItem.objects.filter(event=self).aggregate(
|
||||
sum_total=models.Sum(models.F('cost')*models.F('quantity'), output_field=models.DecimalField(max_digits=10, decimal_places=2))
|
||||
sum_total=models.Sum(models.F('cost') * models.F('quantity'),
|
||||
output_field=models.DecimalField(max_digits=10, decimal_places=2))
|
||||
)['sum_total']
|
||||
if total:
|
||||
return total
|
||||
@@ -358,6 +372,7 @@ class Event(models.Model, RevisionMixin):
|
||||
"""
|
||||
Inc VAT
|
||||
"""
|
||||
|
||||
@property
|
||||
def total(self):
|
||||
return self.sum_total + self.vat
|
||||
@@ -430,7 +445,6 @@ class Event(models.Model, RevisionMixin):
|
||||
else:
|
||||
return endDate
|
||||
|
||||
|
||||
objects = EventManager()
|
||||
|
||||
def get_absolute_url(self):
|
||||
@@ -503,15 +517,6 @@ class Invoice(models.Model):
|
||||
|
||||
@property
|
||||
def payment_total(self):
|
||||
# Manual querying is required for efficiency whilst maintaining floating point arithmetic
|
||||
#if connection.vendor == 'postgresql':
|
||||
# sql = "SELECT SUM(amount) AS total FROM \"RIGS_payment\" WHERE invoice_id=%i" % self.id
|
||||
#else:
|
||||
# sql = "SELECT id, SUM(amount) AS total FROM RIGS_payment WHERE invoice_id=%i" % self.id
|
||||
#total = self.payment_set.raw(sql)[0]
|
||||
#if total.total:
|
||||
# return total.total
|
||||
#return 0.0
|
||||
total = self.payment_set.aggregate(total=models.Sum('amount'))['total']
|
||||
if total:
|
||||
return total
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
{% block content %}
|
||||
<div class="col-sm-12">
|
||||
<h2>Events for Invoice</h2>
|
||||
<h2>Events for Invoice (£ {{ total|floatformat:2 }})</h2>
|
||||
{% if is_paginated %}
|
||||
<div class="col-md-6 col-md-offset-6 col-sm-12 text-right">
|
||||
{% paginator %}
|
||||
@@ -26,12 +26,14 @@
|
||||
<tbody>
|
||||
{% for object in object_list %}
|
||||
<tr class="
|
||||
{% if event.cancelled %}
|
||||
active
|
||||
{% elif event.confirmed and event.mic or not event.is_rig %}
|
||||
{# interpreated as (booked and mic) or is non rig #}
|
||||
{% if object.cancelled %}
|
||||
active text-muted
|
||||
{% elif not object.is_rig %}
|
||||
info
|
||||
{% elif object.confirmed and object.mic %}
|
||||
{# interpreated as (booked and mic) #}
|
||||
success
|
||||
{% elif event.mic %}
|
||||
{% elif object.mic %}
|
||||
warning
|
||||
{% else %}
|
||||
danger
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
{% block content %}
|
||||
<div class="col-sm-12">
|
||||
<h2>Invoices</h2>
|
||||
<h2>Invoices {% if total %}(£ {{ total|floatformat:2 }}){% endif %}</h2>
|
||||
{% if is_paginated %}
|
||||
<div class="col-md-6 col-md-offset-6 col-sm-12 text-right">
|
||||
{% paginator %}
|
||||
|
||||
Reference in New Issue
Block a user