mirror of
https://github.com/nottinghamtec/PyRIGS.git
synced 2026-01-17 05:22:16 +00:00
Merged feature/invoice-total into develop
This commit is contained in:
@@ -1,32 +1,39 @@
|
|||||||
import cStringIO as StringIO
|
import cStringIO as StringIO
|
||||||
|
import datetime
|
||||||
|
import re
|
||||||
|
|
||||||
|
from django.contrib import messages
|
||||||
from django.core.urlresolvers import reverse_lazy
|
from django.core.urlresolvers import reverse_lazy
|
||||||
from django.db import connection
|
|
||||||
from django.http import Http404, HttpResponseRedirect
|
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.http import HttpResponse
|
||||||
from django.shortcuts import get_object_or_404
|
from django.shortcuts import get_object_or_404
|
||||||
from django.contrib import messages
|
from django.template import RequestContext
|
||||||
import datetime
|
from django.template.loader import get_template
|
||||||
|
from django.views import generic
|
||||||
from z3c.rml import rml2pdf
|
from z3c.rml import rml2pdf
|
||||||
|
|
||||||
from RIGS import models
|
from RIGS import models
|
||||||
|
|
||||||
import re
|
|
||||||
|
|
||||||
class InvoiceIndex(generic.ListView):
|
class InvoiceIndex(generic.ListView):
|
||||||
model = models.Invoice
|
model = models.Invoice
|
||||||
template_name = 'RIGS/invoice_list.html'
|
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):
|
def get_queryset(self):
|
||||||
# Manual query is the only way I have found to do this efficiently. Not ideal but needs must
|
# Manual query is the only way I have found to do this efficiently. Not ideal but needs must
|
||||||
sql = "SELECT * FROM " \
|
sql = "SELECT * FROM " \
|
||||||
"(SELECT " \
|
"(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(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\") " \
|
"\"RIGS_invoice\".\"id\", \"RIGS_invoice\".\"event_id\", \"RIGS_invoice\".\"invoice_date\", \"RIGS_invoice\".\"void\" FROM \"RIGS_invoice\") " \
|
||||||
"AS sub " \
|
"AS sub " \
|
||||||
"WHERE (((cost > 0.0) AND (payment_count=0)) OR (cost - payments) <> 0.0) AND void = '0'" \
|
"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):
|
class InvoiceDetail(generic.DetailView):
|
||||||
model = models.Invoice
|
model = models.Invoice
|
||||||
|
|
||||||
|
|
||||||
class InvoicePrint(generic.View):
|
class InvoicePrint(generic.View):
|
||||||
def get(self, request, pk):
|
def get(self, request, pk):
|
||||||
invoice = get_object_or_404(models.Invoice, pk=pk)
|
invoice = get_object_or_404(models.Invoice, pk=pk)
|
||||||
@@ -54,8 +62,8 @@ class InvoicePrint(generic.View):
|
|||||||
'bold': 'RIGS/static/fonts/OPENSANS-BOLD.TTF',
|
'bold': 'RIGS/static/fonts/OPENSANS-BOLD.TTF',
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
'invoice':invoice,
|
'invoice': invoice,
|
||||||
'current_user':request.user,
|
'current_user': request.user,
|
||||||
})
|
})
|
||||||
|
|
||||||
rml = template.render(context)
|
rml = template.render(context)
|
||||||
@@ -72,6 +80,7 @@ class InvoicePrint(generic.View):
|
|||||||
response.write(pdfData)
|
response.write(pdfData)
|
||||||
return response
|
return response
|
||||||
|
|
||||||
|
|
||||||
class InvoiceVoid(generic.View):
|
class InvoiceVoid(generic.View):
|
||||||
def get(self, *args, **kwargs):
|
def get(self, *args, **kwargs):
|
||||||
pk = kwargs.get('pk')
|
pk = kwargs.get('pk')
|
||||||
@@ -94,14 +103,27 @@ class InvoiceWaiting(generic.ListView):
|
|||||||
paginate_by = 25
|
paginate_by = 25
|
||||||
template_name = 'RIGS/event_invoice.html'
|
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):
|
def get_queryset(self):
|
||||||
|
return self.get_objects()
|
||||||
|
|
||||||
|
def get_objects(self):
|
||||||
# @todo find a way to select items
|
# @todo find a way to select items
|
||||||
events = self.model.objects.filter(is_rig=True, end_date__lt=datetime.date.today(),
|
events = self.model.objects.filter(is_rig=True, end_date__lt=datetime.date.today(),
|
||||||
invoice__isnull=True) \
|
invoice__isnull=True) \
|
||||||
.order_by('start_date') \
|
.order_by('start_date') \
|
||||||
.select_related('person',
|
.select_related('person',
|
||||||
'organisation',
|
'organisation',
|
||||||
'venue', 'mic')
|
'venue', 'mic') \
|
||||||
|
.prefetch_related('items')
|
||||||
|
|
||||||
return events
|
return events
|
||||||
|
|
||||||
|
|
||||||
@@ -119,7 +141,7 @@ class InvoiceEvent(generic.View):
|
|||||||
|
|
||||||
class PaymentCreate(generic.CreateView):
|
class PaymentCreate(generic.CreateView):
|
||||||
model = models.Payment
|
model = models.Payment
|
||||||
fields = ['invoice','date','amount','method']
|
fields = ['invoice', 'date', 'amount', 'method']
|
||||||
|
|
||||||
def get_initial(self):
|
def get_initial(self):
|
||||||
initial = super(generic.CreateView, self).get_initial()
|
initial = super(generic.CreateView, self).get_initial()
|
||||||
@@ -139,4 +161,4 @@ class PaymentDelete(generic.DeleteView):
|
|||||||
model = models.Payment
|
model = models.Payment
|
||||||
|
|
||||||
def get_success_url(self):
|
def get_success_url(self):
|
||||||
return self.request.POST.get('next')
|
return self.request.POST.get('next')
|
||||||
|
|||||||
125
RIGS/models.py
125
RIGS/models.py
@@ -1,31 +1,32 @@
|
|||||||
|
import datetime
|
||||||
import hashlib
|
import hashlib
|
||||||
import datetime, pytz
|
import 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 random
|
import random
|
||||||
|
import string
|
||||||
from collections import Counter
|
from collections import Counter
|
||||||
from django.core.urlresolvers import reverse_lazy
|
|
||||||
from django.core.exceptions import ValidationError
|
|
||||||
|
|
||||||
from decimal import Decimal
|
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.
|
# Create your models here.
|
||||||
@python_2_unicode_compatible
|
@python_2_unicode_compatible
|
||||||
class Profile(AbstractUser):
|
class Profile(AbstractUser):
|
||||||
initials = models.CharField(max_length=5, unique=True, null=True, blank=False)
|
initials = models.CharField(max_length=5, unique=True, null=True, blank=False)
|
||||||
phone = models.CharField(max_length=13, null=True, blank=True)
|
phone = models.CharField(max_length=13, null=True, blank=True)
|
||||||
api_key = models.CharField(max_length=40,blank=True,editable=False, null=True)
|
api_key = models.CharField(max_length=40, blank=True, editable=False, null=True)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def make_api_key(cls):
|
def make_api_key(cls):
|
||||||
size=20
|
size = 20
|
||||||
chars=string.ascii_letters + string.digits
|
chars = string.ascii_letters + string.digits
|
||||||
new_api_key = ''.join(random.choice(chars) for x in range(size))
|
new_api_key = ''.join(random.choice(chars) for x in range(size))
|
||||||
return new_api_key;
|
return new_api_key;
|
||||||
|
|
||||||
@@ -55,6 +56,7 @@ class Profile(AbstractUser):
|
|||||||
('view_profile', 'Can view Profile'),
|
('view_profile', 'Can view Profile'),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class RevisionMixin(object):
|
class RevisionMixin(object):
|
||||||
@property
|
@property
|
||||||
def last_edited_at(self):
|
def last_edited_at(self):
|
||||||
@@ -79,10 +81,11 @@ class RevisionMixin(object):
|
|||||||
versions = reversion.get_for_object(self)
|
versions = reversion.get_for_object(self)
|
||||||
if versions:
|
if versions:
|
||||||
version = reversion.get_for_object(self)[0]
|
version = reversion.get_for_object(self)[0]
|
||||||
return "V{0} | R{1}".format(version.pk,version.revision.pk)
|
return "V{0} | R{1}".format(version.pk, version.revision.pk)
|
||||||
else:
|
else:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
@reversion.register
|
@reversion.register
|
||||||
@python_2_unicode_compatible
|
@python_2_unicode_compatible
|
||||||
class Person(models.Model, RevisionMixin):
|
class Person(models.Model, RevisionMixin):
|
||||||
@@ -97,7 +100,7 @@ class Person(models.Model, RevisionMixin):
|
|||||||
def __str__(self):
|
def __str__(self):
|
||||||
string = self.name
|
string = self.name
|
||||||
if self.notes is not None:
|
if self.notes is not None:
|
||||||
if len(self.notes) > 0:
|
if len(self.notes) > 0:
|
||||||
string += "*"
|
string += "*"
|
||||||
return string
|
return string
|
||||||
|
|
||||||
@@ -108,7 +111,7 @@ class Person(models.Model, RevisionMixin):
|
|||||||
if e.organisation:
|
if e.organisation:
|
||||||
o.append(e.organisation)
|
o.append(e.organisation)
|
||||||
|
|
||||||
#Count up occurances and put them in descending order
|
# Count up occurances and put them in descending order
|
||||||
c = Counter(o)
|
c = Counter(o)
|
||||||
stats = c.most_common()
|
stats = c.most_common()
|
||||||
return stats
|
return stats
|
||||||
@@ -141,7 +144,7 @@ class Organisation(models.Model, RevisionMixin):
|
|||||||
def __str__(self):
|
def __str__(self):
|
||||||
string = self.name
|
string = self.name
|
||||||
if self.notes is not None:
|
if self.notes is not None:
|
||||||
if len(self.notes) > 0:
|
if len(self.notes) > 0:
|
||||||
string += "*"
|
string += "*"
|
||||||
return string
|
return string
|
||||||
|
|
||||||
@@ -151,8 +154,8 @@ class Organisation(models.Model, RevisionMixin):
|
|||||||
for e in Event.objects.filter(organisation=self).select_related('person'):
|
for e in Event.objects.filter(organisation=self).select_related('person'):
|
||||||
if e.person:
|
if e.person:
|
||||||
p.append(e.person)
|
p.append(e.person)
|
||||||
|
|
||||||
#Count up occurances and put them in descending order
|
# Count up occurances and put them in descending order
|
||||||
c = Counter(p)
|
c = Counter(p)
|
||||||
stats = c.most_common()
|
stats = c.most_common()
|
||||||
return stats
|
return stats
|
||||||
@@ -238,12 +241,18 @@ class Venue(models.Model, RevisionMixin):
|
|||||||
class EventManager(models.Manager):
|
class EventManager(models.Manager):
|
||||||
def current_events(self):
|
def current_events(self):
|
||||||
events = self.filter(
|
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(start_date__gte=datetime.date.today(), end_date__isnull=True, dry_hire=False) & ~models.Q(
|
||||||
(models.Q(end_date__gte=datetime.date.today(), dry_hire=False) & ~models.Q(status=Event.CANCELLED)) | # Ends after
|
status=Event.CANCELLED)) | # Starts after with no end
|
||||||
(models.Q(dry_hire=True, start_date__gte=datetime.date.today()) & ~models.Q(status=Event.CANCELLED)) | # Active dry hire
|
(models.Q(end_date__gte=datetime.date.today(), dry_hire=False) & ~models.Q(
|
||||||
(models.Q(dry_hire=True, checked_in_by__isnull=True) & (models.Q(status=Event.BOOKED) | models.Q(status=Event.CONFIRMED))) | # Active dry hire GT
|
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
|
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
|
return events
|
||||||
|
|
||||||
def events_in_bounds(self, start, end):
|
def events_in_bounds(self, start, end):
|
||||||
@@ -251,15 +260,17 @@ class EventManager(models.Manager):
|
|||||||
(models.Q(start_date__gte=start.date(), start_date__lte=end.date())) | # Start date in bounds
|
(models.Q(start_date__gte=start.date(), start_date__lte=end.date())) | # Start date in bounds
|
||||||
(models.Q(end_date__gte=start.date(), end_date__lte=end.date())) | # End date in bounds
|
(models.Q(end_date__gte=start.date(), end_date__lte=end.date())) | # End date in bounds
|
||||||
(models.Q(access_at__gte=start, access_at__lte=end)) | # Access at in bounds
|
(models.Q(access_at__gte=start, access_at__lte=end)) | # Access at in bounds
|
||||||
(models.Q(meet_at__gte=start, meet_at__lte=end)) | # Meet at in bounds
|
(models.Q(meet_at__gte=start, meet_at__lte=end)) | # Meet at in bounds
|
||||||
|
|
||||||
(models.Q(start_date__lte=start, end_date__gte=end)) | # Start before, end after
|
(models.Q(start_date__lte=start, end_date__gte=end)) | # Start before, end after
|
||||||
(models.Q(access_at__lte=start, start_date__gte=end)) | # Access before, start after
|
(models.Q(access_at__lte=start, start_date__gte=end)) | # Access before, start after
|
||||||
(models.Q(access_at__lte=start, end_date__gte=end)) | # Access before, end after
|
(models.Q(access_at__lte=start, end_date__gte=end)) | # Access before, end after
|
||||||
(models.Q(meet_at__lte=start, start_date__gte=end)) | # Meet before, start after
|
(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
|
(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
|
return events
|
||||||
|
|
||||||
def rig_count(self):
|
def rig_count(self):
|
||||||
@@ -301,7 +312,8 @@ class Event(models.Model, RevisionMixin):
|
|||||||
status = models.IntegerField(choices=EVENT_STATUS_CHOICES, default=PROVISIONAL)
|
status = models.IntegerField(choices=EVENT_STATUS_CHOICES, default=PROVISIONAL)
|
||||||
dry_hire = models.BooleanField(default=False)
|
dry_hire = models.BooleanField(default=False)
|
||||||
is_rig = models.BooleanField(default=True)
|
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
|
# Timing
|
||||||
start_date = models.DateField()
|
start_date = models.DateField()
|
||||||
@@ -327,6 +339,7 @@ class Event(models.Model, RevisionMixin):
|
|||||||
"""
|
"""
|
||||||
EX Vat
|
EX Vat
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def sum_total(self):
|
def sum_total(self):
|
||||||
# Manual querying is required for efficiency whilst maintaining floating point arithmetic
|
# Manual querying is required for efficiency whilst maintaining floating point arithmetic
|
||||||
@@ -334,14 +347,15 @@ class Event(models.Model, RevisionMixin):
|
|||||||
# sql = "SELECT SUM(quantity * cost) AS sum_total FROM \"RIGS_eventitem\" WHERE event_id=%i" % self.id
|
# sql = "SELECT SUM(quantity * cost) AS sum_total FROM \"RIGS_eventitem\" WHERE event_id=%i" % self.id
|
||||||
# else:
|
# else:
|
||||||
# sql = "SELECT id, SUM(quantity * cost) AS sum_total FROM RIGS_eventitem WHERE event_id=%i" % self.id
|
# sql = "SELECT id, SUM(quantity * cost) AS sum_total FROM RIGS_eventitem WHERE event_id=%i" % self.id
|
||||||
#total = self.items.raw(sql)[0]
|
# total = self.items.raw(sql)[0]
|
||||||
#if total.sum_total:
|
# if total.sum_total:
|
||||||
# return total.sum_total
|
# return total.sum_total
|
||||||
#total = 0.0
|
# total = 0.0
|
||||||
#for item in self.items.filter(cost__gt=0).extra(select="SUM(cost * quantity) AS sum"):
|
# for item in self.items.filter(cost__gt=0).extra(select="SUM(cost * quantity) AS sum"):
|
||||||
# total += item.sum
|
# total += item.sum
|
||||||
total = EventItem.objects.filter(event=self).aggregate(
|
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']
|
)['sum_total']
|
||||||
if total:
|
if total:
|
||||||
return total
|
return total
|
||||||
@@ -358,6 +372,7 @@ class Event(models.Model, RevisionMixin):
|
|||||||
"""
|
"""
|
||||||
Inc VAT
|
Inc VAT
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def total(self):
|
def total(self):
|
||||||
return self.sum_total + self.vat
|
return self.sum_total + self.vat
|
||||||
@@ -382,7 +397,7 @@ class Event(models.Model, RevisionMixin):
|
|||||||
def earliest_time(self):
|
def earliest_time(self):
|
||||||
"""Finds the earliest time defined in the event - this function could return either a tzaware datetime, or a naiive date object"""
|
"""Finds the earliest time defined in the event - this function could return either a tzaware datetime, or a naiive date object"""
|
||||||
|
|
||||||
#Put all the datetimes in a list
|
# Put all the datetimes in a list
|
||||||
datetime_list = []
|
datetime_list = []
|
||||||
|
|
||||||
if self.access_at:
|
if self.access_at:
|
||||||
@@ -394,22 +409,22 @@ class Event(models.Model, RevisionMixin):
|
|||||||
# If there is no start time defined, pretend it's midnight
|
# If there is no start time defined, pretend it's midnight
|
||||||
startTimeFaked = False
|
startTimeFaked = False
|
||||||
if self.has_start_time:
|
if self.has_start_time:
|
||||||
startDateTime = datetime.datetime.combine(self.start_date,self.start_time)
|
startDateTime = datetime.datetime.combine(self.start_date, self.start_time)
|
||||||
else:
|
else:
|
||||||
startDateTime = datetime.datetime.combine(self.start_date,datetime.time(00,00))
|
startDateTime = datetime.datetime.combine(self.start_date, datetime.time(00, 00))
|
||||||
startTimeFaked = True
|
startTimeFaked = True
|
||||||
|
|
||||||
#timezoneIssues - apply the default timezone to the naiive datetime
|
# timezoneIssues - apply the default timezone to the naiive datetime
|
||||||
tz = pytz.timezone(settings.TIME_ZONE)
|
tz = pytz.timezone(settings.TIME_ZONE)
|
||||||
startDateTime = tz.localize(startDateTime)
|
startDateTime = tz.localize(startDateTime)
|
||||||
datetime_list.append(startDateTime) # then add it to the list
|
datetime_list.append(startDateTime) # then add it to the list
|
||||||
|
|
||||||
earliest = min(datetime_list).astimezone(tz) #find the earliest datetime in the list
|
earliest = min(datetime_list).astimezone(tz) # find the earliest datetime in the list
|
||||||
|
|
||||||
# if we faked it & it's the earliest, better own up
|
# if we faked it & it's the earliest, better own up
|
||||||
if startTimeFaked and earliest==startDateTime:
|
if startTimeFaked and earliest == startDateTime:
|
||||||
return self.start_date
|
return self.start_date
|
||||||
|
|
||||||
return earliest
|
return earliest
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@@ -421,7 +436,7 @@ class Event(models.Model, RevisionMixin):
|
|||||||
endDate = self.start_date
|
endDate = self.start_date
|
||||||
|
|
||||||
if self.has_end_time:
|
if self.has_end_time:
|
||||||
endDateTime = datetime.datetime.combine(endDate,self.end_time)
|
endDateTime = datetime.datetime.combine(endDate, self.end_time)
|
||||||
tz = pytz.timezone(settings.TIME_ZONE)
|
tz = pytz.timezone(settings.TIME_ZONE)
|
||||||
endDateTime = tz.localize(endDateTime)
|
endDateTime = tz.localize(endDateTime)
|
||||||
|
|
||||||
@@ -430,7 +445,6 @@ class Event(models.Model, RevisionMixin):
|
|||||||
else:
|
else:
|
||||||
return endDate
|
return endDate
|
||||||
|
|
||||||
|
|
||||||
objects = EventManager()
|
objects = EventManager()
|
||||||
|
|
||||||
def get_absolute_url(self):
|
def get_absolute_url(self):
|
||||||
@@ -446,7 +460,7 @@ class Event(models.Model, RevisionMixin):
|
|||||||
startEndSameDay = not self.end_date or self.end_date == self.start_date
|
startEndSameDay = not self.end_date or self.end_date == self.start_date
|
||||||
hasStartAndEnd = self.has_start_time and self.has_end_time
|
hasStartAndEnd = self.has_start_time and self.has_end_time
|
||||||
if startEndSameDay and hasStartAndEnd and self.start_time > self.end_time:
|
if startEndSameDay and hasStartAndEnd and self.start_time > self.end_time:
|
||||||
raise ValidationError('Unless you\'ve invented time travel, the event can\'t finish before it has started.')
|
raise ValidationError('Unless you\'ve invented time travel, the event can\'t finish before it has started.')
|
||||||
|
|
||||||
def save(self, *args, **kwargs):
|
def save(self, *args, **kwargs):
|
||||||
"""Call :meth:`full_clean` before saving."""
|
"""Call :meth:`full_clean` before saving."""
|
||||||
@@ -503,15 +517,6 @@ class Invoice(models.Model):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def payment_total(self):
|
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']
|
total = self.payment_set.aggregate(total=models.Sum('amount'))['total']
|
||||||
if total:
|
if total:
|
||||||
return total
|
return total
|
||||||
@@ -552,4 +557,4 @@ class Payment(models.Model):
|
|||||||
method = models.CharField(max_length=2, choices=METHODS, null=True, blank=True)
|
method = models.CharField(max_length=2, choices=METHODS, null=True, blank=True)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return "%s: %d" % (self.get_method_display(), self.amount)
|
return "%s: %d" % (self.get_method_display(), self.amount)
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="col-sm-12">
|
<div class="col-sm-12">
|
||||||
<h2>Events for Invoice</h2>
|
<h2>Events for Invoice (£ {{ total|floatformat:2 }})</h2>
|
||||||
{% if is_paginated %}
|
{% if is_paginated %}
|
||||||
<div class="col-md-6 col-md-offset-6 col-sm-12 text-right">
|
<div class="col-md-6 col-md-offset-6 col-sm-12 text-right">
|
||||||
{% paginator %}
|
{% paginator %}
|
||||||
@@ -26,16 +26,18 @@
|
|||||||
<tbody>
|
<tbody>
|
||||||
{% for object in object_list %}
|
{% for object in object_list %}
|
||||||
<tr class="
|
<tr class="
|
||||||
{% if event.cancelled %}
|
{% if object.cancelled %}
|
||||||
active
|
active text-muted
|
||||||
{% elif event.confirmed and event.mic or not event.is_rig %}
|
{% elif not object.is_rig %}
|
||||||
{# interpreated as (booked and mic) or is non rig #}
|
info
|
||||||
success
|
{% elif object.confirmed and object.mic %}
|
||||||
{% elif event.mic %}
|
{# interpreated as (booked and mic) #}
|
||||||
warning
|
success
|
||||||
{% else %}
|
{% elif object.mic %}
|
||||||
danger
|
warning
|
||||||
{% endif %}
|
{% else %}
|
||||||
|
danger
|
||||||
|
{% endif %}
|
||||||
">
|
">
|
||||||
<td class="hidden-xs"><a href="{% url 'event_detail' object.pk %}" target="_blank">N{{ object.pk|stringformat:"05d" }}</a></td>
|
<td class="hidden-xs"><a href="{% url 'event_detail' object.pk %}" target="_blank">N{{ object.pk|stringformat:"05d" }}</a></td>
|
||||||
<td>{{ object.end_date }}</td>
|
<td>{{ object.end_date }}</td>
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="col-sm-12">
|
<div class="col-sm-12">
|
||||||
<h2>Invoices</h2>
|
<h2>Invoices {% if total %}(£ {{ total|floatformat:2 }}){% endif %}</h2>
|
||||||
{% if is_paginated %}
|
{% if is_paginated %}
|
||||||
<div class="col-md-6 col-md-offset-6 col-sm-12 text-right">
|
<div class="col-md-6 col-md-offset-6 col-sm-12 text-right">
|
||||||
{% paginator %}
|
{% paginator %}
|
||||||
|
|||||||
Reference in New Issue
Block a user