Merged feature/invoiceDelete into develop

This commit is contained in:
Tom Price
2016-07-11 13:08:47 +01:00
8 changed files with 225 additions and 104 deletions

View File

@@ -94,6 +94,25 @@ class InvoiceVoid(generic.View):
return HttpResponseRedirect(reverse_lazy('invoice_list')) return HttpResponseRedirect(reverse_lazy('invoice_list'))
return HttpResponseRedirect(reverse_lazy('invoice_detail', kwargs={'pk': object.pk})) return HttpResponseRedirect(reverse_lazy('invoice_detail', kwargs={'pk': object.pk}))
class InvoiceDelete(generic.DeleteView):
model = models.Invoice
def get(self, request, pk):
obj = self.get_object()
if obj.payment_set.all().count() > 0:
messages.info(self.request, 'To delete an invoice, delete the payments first.')
return HttpResponseRedirect(reverse_lazy('invoice_detail', kwargs={'pk': obj.pk}))
return super(InvoiceDelete, self).get(pk)
def post(self, request, pk):
obj = self.get_object()
if obj.payment_set.all().count() > 0:
messages.info(self.request, 'To delete an invoice, delete the payments first.')
return HttpResponseRedirect(reverse_lazy('invoice_detail', kwargs={'pk': obj.pk}))
return super(InvoiceDelete, self).post(pk)
def get_success_url(self):
return self.request.POST.get('next')
class InvoiceArchive(generic.ListView): class InvoiceArchive(generic.ListView):
model = models.Invoice model = models.Invoice
@@ -112,6 +131,7 @@ class InvoiceWaiting(generic.ListView):
for obj in self.get_objects(): for obj in self.get_objects():
total += obj.sum_total total += obj.sum_total
context['total'] = total context['total'] = total
context['count'] = len(self.get_objects())
return context return context
def get_queryset(self): def get_queryset(self):
@@ -124,6 +144,7 @@ class InvoiceWaiting(generic.ListView):
Q(start_date__lte=datetime.date.today(), end_date__isnull=True) | # Starts before with no end Q(start_date__lte=datetime.date.today(), end_date__isnull=True) | # Starts before with no end
Q(end_date__lte=datetime.date.today()) # Has end date, finishes before Q(end_date__lte=datetime.date.today()) # Has end date, finishes before
) & Q(invoice__isnull=True) # Has not already been invoiced ) & Q(invoice__isnull=True) # Has not already been invoiced
& Q(is_rig=True) # Is a rig (not non-rig)
).order_by('start_date') \ ).order_by('start_date') \
.select_related('person', .select_related('person',

View File

@@ -15,75 +15,77 @@
{% block content %} {% block content %}
<div class="col-sm-12"> <div class="col-sm-12">
<h2>Events for Invoice ({{object_list|length}} Events, £ {{ total|floatformat:2 }})</h2> <h2>Events for Invoice ({{count}} Events, £ {{ total|floatformat:2 }})</h2>
<p>These events have happened, but paperwork has not yet been sent to treasury</p> <p>These events have happened, but paperwork has not yet been sent to treasury</p>
{% 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 %}
</div> </div>
{% endif %} {% endif %}
<table class="table table-responsive table-hover"> <div class="table-responsive col-sm-12">
<thead> <table class="table table-hover">
<tr> <thead>
<th class="hiddenx-xs">#</th> <tr>
<th>Start Date</th> <th>#</th>
<th>Event Name</th> <th>Start Date</th>
<th>Client</th> <th>Event Name</th>
<th>Cost</th> <th>Client</th>
<th class="hidden-xs">MIC</th> <th>Cost</th>
<th></th> <th>MIC</th>
</tr> <th></th>
</thead>
<tbody>
{% for object in object_list %}
<tr class="
{% 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 object.mic %}
warning
{% else %}
danger
{% endif %}
">
<td class="hidden-xs"><a href="{% url 'event_detail' object.pk %}">N{{ object.pk|stringformat:"05d" }}</a><br>
<span class="text-muted">{{ object.get_status_display }}</span></td>
<td>{{ object.start_date }}</td>
<td>{{ object.name }}</td>
<td>
{% if object.organisation %}
{{ object.organisation.name }}
<br>
<span class="text-muted">{{ object.organisation.union_account|yesno:'Internal,External' }}</span>
{% else %}
{{ object.person.name }}
<br>
<span class="text-muted">External</span>
{% endif %}
</td>
<td>{{ object.sum_total|floatformat:2 }}</td>
<td class="text-center">
{% if object.mic %}
{{ object.mic.initials }}<br>
<img src="{{ object.mic.profile_picture }}" class="event-mic-photo"/>
{% else %}
<span class="glyphicon glyphicon-exclamation-sign"></span>
{% endif %}
</td>
<td class="text-right">
<a href="{% url 'invoice_event' object.pk %}" class="btn btn-default" data-toggle="tooltip" title="'Invoice' this event - click this when paperwork has been sent to treasury">
<span class="glyphicon glyphicon-gbp"></span>
</a>
</td>
</tr> </tr>
{% endfor %} </thead>
</tbody> <tbody>
</table> {% for object in object_list %}
<tr class="
{% 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 object.mic %}
warning
{% else %}
danger
{% endif %}
">
<td><a href="{% url 'event_detail' object.pk %}">N{{ object.pk|stringformat:"05d" }}</a><br>
<span class="text-muted">{{ object.get_status_display }}</span></td>
<td>{{ object.start_date }}</td>
<td>{{ object.name }}</td>
<td>
{% if object.organisation %}
{{ object.organisation.name }}
<br>
<span class="text-muted">{{ object.organisation.union_account|yesno:'Internal,External' }}</span>
{% else %}
{{ object.person.name }}
<br>
<span class="text-muted">External</span>
{% endif %}
</td>
<td>{{ object.sum_total|floatformat:2 }}</td>
<td class="text-center">
{% if object.mic %}
{{ object.mic.initials }}<br>
<img src="{{ object.mic.profile_picture }}" class="event-mic-photo"/>
{% else %}
<span class="glyphicon glyphicon-exclamation-sign"></span>
{% endif %}
</td>
<td class="text-right">
<a href="{% url 'invoice_event' object.pk %}" class="btn btn-default" data-toggle="tooltip" title="'Invoice' this event - click this when paperwork has been sent to treasury">
<span class="glyphicon glyphicon-gbp"></span>
</a>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% 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 %}

View File

@@ -1,7 +1,7 @@
<div class="table-responsive"> <div class="table-responsive">
<table class="table"> <table class="table">
<thead> <thead>
<td class="hidden-xs">#</td> <td>#</td>
<td>Event Date</td> <td>Event Date</td>
<td>Event Details</td> <td>Event Details</td>
<td>Event Timings</td> <td>Event Timings</td>
@@ -23,7 +23,7 @@
danger danger
{% endif %} {% endif %}
"> ">
<td class="hidden-xs">{{ event.pk }}</td> <td>{{ event.pk }}</td>
<td> <td>
<div><strong>{{ event.start_date|date:"D d/m/Y" }}</strong></div> <div><strong>{{ event.start_date|date:"D d/m/Y" }}</strong></div>
{% if event.end_date and event.end_date != event.start_date %} {% if event.end_date and event.end_date != event.start_date %}

View File

@@ -0,0 +1,35 @@
{% extends 'base.html' %}
{% block title %}Delete payment on invoice {{ object.invoice.pk }}{% endblock %}
{% block content %}
<div class="col-sm-offset-2 col-sm-8">
<div class="alert alert-danger" role="alert">
<h2>Delete invoice {{ object.pk }}</h2>
<p>Are you sure you wish to delete invoice {{ object.pk }}?</p>
<p class="text-center"><strong>This action cannot be undone!</strong></p>
<div class="row">
<div class="col-sm-12">
<form action="{{ action_link }}" method="post">{% csrf_token %}
<input type="hidden" name="next" value="{% url 'invoice_list' %}"/>
<a href="{% url 'invoice_detail' object.pk %}" class="btn btn-default col-sm-1">No</a>
<a href="{% url 'invoice_detail' object.pk %}" class="btn btn-default col-sm-1">No</a>
<a href="{% url 'invoice_detail' object.pk %}" class="btn btn-default col-sm-1">No</a>
<input type="submit" value="Yes" class="btn btn-danger col-sm-1"/>
<a href="{% url 'invoice_detail' object.pk %}" class="btn btn-default col-sm-1">No</a>
<a href="{% url 'invoice_detail' object.pk %}" class="btn btn-default col-sm-1">No</a>
<a href="{% url 'invoice_detail' object.pk %}" class="btn btn-default col-sm-1">No</a>
<a href="{% url 'invoice_detail' object.pk %}" class="btn btn-default col-sm-1">No</a>
<a href="{% url 'invoice_detail' object.pk %}" class="btn btn-default col-sm-1">No</a>
<a href="{% url 'invoice_detail' object.pk %}" class="btn btn-default col-sm-1">No</a>
<a href="{% url 'invoice_detail' object.pk %}" class="btn btn-default col-sm-1">No</a>
<a href="{% url 'invoice_detail' object.pk %}" class="btn btn-default col-sm-1">No</a>
</form>
</div>
</div>
</div>
</div>
{% endblock %}

View File

@@ -11,6 +11,10 @@
<div class="col-sm-4 text-right"> <div class="col-sm-4 text-right">
<div class="btn-group btn-page"> <div class="btn-group btn-page">
<a href="{% url 'invoice_delete' object.pk %}" class="btn btn-default" title="Void Invoice">
<span class="glyphicon glyphicon-remove"></span> <span
class="hidden-xs">Delete</span>
</a>
<a href="{% url 'invoice_void' object.pk %}" class="btn btn-default" title="Void Invoice"> <a href="{% url 'invoice_void' object.pk %}" class="btn btn-default" title="Void Invoice">
<span class="glyphicon glyphicon-ban-circle"></span> <span <span class="glyphicon glyphicon-ban-circle"></span> <span
class="hidden-xs">Void</span> class="hidden-xs">Void</span>

View File

@@ -12,47 +12,49 @@
{% paginator %} {% paginator %}
</div> </div>
{% endif %} {% endif %}
<table class="table table-responsive table-hover"> <div class="table-responsive col-sm-12">
<thead> <table class="table table-hover">
<tr> <thead>
<th>#</th> <tr>
<th>Event</th> <th>#</th>
<th>Client</th> <th>Event</th>
<th>Event Date</th> <th>Client</th>
<th>Invoice Date</th> <th>Event Date</th>
<th>Balance</th> <th>Invoice Date</th>
<th></th> <th>Balance</th>
</tr> <th></th>
</thead>
<tbody>
{% for object in object_list %}
<tr class="{% if object.void %}danger{% elif object.balance == 0 %}success{% endif %}">
<td>{{ object.pk }}</td>
<td><a href="{% url 'event_detail' object.event.pk %}">N{{ object.event.pk|stringformat:"05d" }}</a>: {{ object.event.name }} <br>
<span class="text-muted">{{ object.event.get_status_display }}</span></td>
</td>
<td>{% if object.organisation %}
{{ object.event.organisation.name }}
<br>
<span class="text-muted">{{ object.organisation.union_account|yesno:'Internal,External' }}</span>
{% else %}
{{ object.event.person.name }}
<br>
<span class="text-muted">External</span>
{% endif %}
</td>
<td>{{ object.event.start_date }}</td>
<td>{{ object.invoice_date }}</td>
<td>{{ object.balance|floatformat:2 }}</td>
<td class="text-right">
<a href="{% url 'invoice_detail' object.pk %}" class="btn btn-default">
<span class="glyphicon glyphicon-pencil"></span>
</a>
</td>
</tr> </tr>
{% endfor %} </thead>
</tbody> <tbody>
</table> {% for object in object_list %}
<tr class="{% if object.void %}danger{% elif object.balance == 0 %}success{% endif %}">
<td>{{ object.pk }}</td>
<td><a href="{% url 'event_detail' object.event.pk %}">N{{ object.event.pk|stringformat:"05d" }}</a>: {{ object.event.name }} <br>
<span class="text-muted">{{ object.event.get_status_display }}</span></td>
</td>
<td>{% if object.event.organisation %}
{{ object.event.organisation.name }}
<br>
<span class="text-muted">{{ object.event.organisation.union_account|yesno:'Internal,External' }}</span>
{% else %}
{{ object.event.person.name }}
<br>
<span class="text-muted">External</span>
{% endif %}
</td>
<td>{{ object.event.start_date }}</td>
<td>{{ object.invoice_date }}</td>
<td>{{ object.balance|floatformat:2 }}</td>
<td class="text-right">
<a href="{% url 'invoice_detail' object.pk %}" class="btn btn-default">
<span class="glyphicon glyphicon-pencil"></span>
</a>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% 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 %}

View File

@@ -155,3 +155,57 @@ class TestAdminMergeObjects(TestCase):
if event.organisation == self.organisations[3]: # The one we left in place if event.organisation == self.organisations[3]: # The one we left in place
continue continue
self.assertEqual(updatedEvent.organisation, self.organisations[1]) self.assertEqual(updatedEvent.organisation, self.organisations[1])
class TestInvoiceDelete(TestCase):
@classmethod
def setUpTestData(cls):
cls.profile = models.Profile.objects.create(username="testuser1", email="1@test.com", is_superuser=True, is_active=True, is_staff=True)
cls.events = {
1: models.Event.objects.create(name="TE E1", start_date=date.today()),
2: models.Event.objects.create(name="TE E2", start_date=date.today())
}
cls.invoices = {
1: models.Invoice.objects.create(event=cls.events[1]),
2: models.Invoice.objects.create(event=cls.events[2])
}
cls.payments = {
1: models.Payment.objects.create(invoice=cls.invoices[1], date=date.today(), amount=12.34, method=models.Payment.CASH)
}
def setUp(self):
self.profile.set_password('testuser')
self.profile.save()
self.assertTrue(self.client.login(username=self.profile.username, password='testuser'))
def test_invoice_delete_allowed(self):
request_url = reverse('invoice_delete', kwargs={'pk':self.invoices[2].pk})
response = self.client.get(request_url, follow=True)
self.assertContains(response, "Are you sure")
# Check the invoice still exists
self.assertTrue(models.Invoice.objects.get(pk=self.invoices[2].pk))
# Actually delete it
response = self.client.post(request_url, follow=True)
# Check the invoice is deleted
self.assertRaises(ObjectDoesNotExist, models.Invoice.objects.get, pk=self.invoices[2].pk)
def test_invoice_delete_not_allowed(self):
request_url = reverse('invoice_delete', kwargs={'pk':self.invoices[1].pk})
response = self.client.get(request_url, follow=True)
self.assertContains(response, "To delete an invoice, delete the payments first.")
# Check the invoice still exists
self.assertTrue(models.Invoice.objects.get(pk=self.invoices[1].pk))
# Try to actually delete it
response = self.client.post(request_url, follow=True)
# Check this didn't work
self.assertTrue(models.Invoice.objects.get(pk=self.invoices[1].pk))

View File

@@ -127,6 +127,9 @@ urlpatterns = patterns('',
url(r'^invoice/(?P<pk>\d+)/void/$', url(r'^invoice/(?P<pk>\d+)/void/$',
permission_required_with_403('RIGS.change_invoice')(finance.InvoiceVoid.as_view()), permission_required_with_403('RIGS.change_invoice')(finance.InvoiceVoid.as_view()),
name='invoice_void'), name='invoice_void'),
url(r'^invoice/(?P<pk>\d+)/delete/$',
permission_required_with_403('RIGS.change_invoice')(finance.InvoiceDelete.as_view()),
name='invoice_delete'),
url(r'^payment/create/$', url(r'^payment/create/$',
permission_required_with_403('RIGS.add_payment')(finance.PaymentCreate.as_view()), permission_required_with_403('RIGS.add_payment')(finance.PaymentCreate.as_view()),
name='payment_create'), name='payment_create'),