mirror of
https://github.com/nottinghamtec/PyRIGS.git
synced 2026-01-24 00:42:17 +00:00
Merge branch 'master' into web-calendar
This commit is contained in:
@@ -14,6 +14,7 @@ from z3c.rml import rml2pdf
|
||||
|
||||
from RIGS import models
|
||||
|
||||
import re
|
||||
|
||||
class InvoiceIndex(generic.ListView):
|
||||
model = models.Invoice
|
||||
@@ -63,8 +64,10 @@ class InvoicePrint(generic.View):
|
||||
|
||||
pdfData = buffer.read()
|
||||
|
||||
escapedEventName = re.sub('[^a-zA-Z0-9 \n\.]', '', object.name)
|
||||
|
||||
response = HttpResponse(content_type='application/pdf')
|
||||
response['Content-Disposition'] = "filename=Invoice %05d | %s.pdf" % (invoice.pk, object.name)
|
||||
response['Content-Disposition'] = "filename=Invoice %05d | %s.pdf" % (invoice.pk, escapedEventName)
|
||||
response.write(pdfData)
|
||||
return response
|
||||
|
||||
|
||||
@@ -9,7 +9,9 @@ from django.utils.encoding import python_2_unicode_compatible
|
||||
import reversion
|
||||
import string
|
||||
import random
|
||||
from collections import Counter
|
||||
from django.core.urlresolvers import reverse_lazy
|
||||
from django.core.exceptions import ValidationError
|
||||
|
||||
from decimal import Decimal
|
||||
|
||||
@@ -91,9 +93,13 @@ class Person(models.Model, RevisionMixin):
|
||||
def organisations(self):
|
||||
o = []
|
||||
for e in Event.objects.filter(person=self).select_related('organisation'):
|
||||
if e.organisation and e.organisation not in o:
|
||||
if e.organisation:
|
||||
o.append(e.organisation)
|
||||
return o
|
||||
|
||||
#Count up occurances and put them in descending order
|
||||
c = Counter(o)
|
||||
stats = c.most_common()
|
||||
return stats
|
||||
|
||||
@property
|
||||
def latest_events(self):
|
||||
@@ -131,9 +137,13 @@ class Organisation(models.Model, RevisionMixin):
|
||||
def persons(self):
|
||||
p = []
|
||||
for e in Event.objects.filter(organisation=self).select_related('person'):
|
||||
if e.person and e.person not in p:
|
||||
if e.person:
|
||||
p.append(e.person)
|
||||
return p
|
||||
|
||||
#Count up occurances and put them in descending order
|
||||
c = Counter(p)
|
||||
stats = c.most_common()
|
||||
return stats
|
||||
|
||||
@property
|
||||
def latest_events(self):
|
||||
@@ -415,7 +425,21 @@ class Event(models.Model, RevisionMixin):
|
||||
return reverse_lazy('event_detail', kwargs={'pk': self.pk})
|
||||
|
||||
def __str__(self):
|
||||
return str(self.pk) + ": " + self.name
|
||||
return unicode(self.pk) + ": " + self.name
|
||||
|
||||
def clean(self):
|
||||
if self.end_date and self.start_date > self.end_date:
|
||||
raise ValidationError('Unless you\'ve invented time travel, the event can\'t finish before it has started.')
|
||||
|
||||
startEndSameDay = not self.end_date or self.end_date == self.start_date
|
||||
hasStartAndEnd = self.has_start_time and self.has_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.')
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
"""Call :meth:`full_clean` before saving."""
|
||||
self.full_clean()
|
||||
super(Event, self).save(*args, **kwargs)
|
||||
|
||||
class Meta:
|
||||
permissions = (
|
||||
|
||||
@@ -47,6 +47,7 @@ class EventCreate(generic.CreateView):
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super(EventCreate, self).get_context_data(**kwargs)
|
||||
context['edit'] = True
|
||||
context['currentVAT'] = models.VatRate.objects.current_rate()
|
||||
|
||||
form = context['form']
|
||||
if re.search('"-\d+"', form['items_json'].value()):
|
||||
@@ -124,7 +125,10 @@ class EventPrint(generic.View):
|
||||
merger.write(merged)
|
||||
|
||||
response = HttpResponse(content_type='application/pdf')
|
||||
response['Content-Disposition'] = "filename=N%05d | %s.pdf" % (object.pk, object.name)
|
||||
|
||||
escapedEventName = re.sub('[^a-zA-Z0-9 \n\.]', '', object.name)
|
||||
|
||||
response['Content-Disposition'] = "filename=N%05d | %s.pdf" % (object.pk, escapedEventName)
|
||||
response.write(merged.getvalue())
|
||||
return response
|
||||
|
||||
|
||||
BIN
RIGS/static/imgs/paperwork/corner-bl.jpg
Normal file
BIN
RIGS/static/imgs/paperwork/corner-bl.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 29 KiB |
|
Before Width: | Height: | Size: 48 KiB After Width: | Height: | Size: 48 KiB |
BIN
RIGS/static/imgs/paperwork/corner-tr.jpg
Normal file
BIN
RIGS/static/imgs/paperwork/corner-tr.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 28 KiB |
@@ -11,6 +11,10 @@ function nl2br (str, is_xhtml) {
|
||||
return (str + '').replace(/([^>\r\n]?)(\r\n|\n\r|\r|\n)/g, '$1'+ breakTag +'$2');
|
||||
}
|
||||
|
||||
function escapeHtml (str) {
|
||||
return $('<div/>').text(str).html();
|
||||
}
|
||||
|
||||
function updatePrices() {
|
||||
// individual rows
|
||||
var sum = 0;
|
||||
@@ -101,8 +105,8 @@ $('body').on('submit', '#item-form', function (e) {
|
||||
}
|
||||
// update the table
|
||||
$row = $('#item-' + pk);
|
||||
$row.find('.name').html(fields.name);
|
||||
$row.find('.description').html(nl2br(fields.description));
|
||||
$row.find('.name').html(escapeHtml(fields.name));
|
||||
$row.find('.description').html(nl2br(escapeHtml(fields.description)));
|
||||
$row.find('.cost').html(parseFloat(fields.cost).toFixed(2));
|
||||
$row.find('.quantity').html(fields.quantity);
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/* ========================================================================
|
||||
* Bootstrap: modal.js v3.3.2
|
||||
* Bootstrap: modal.js v3.3.5
|
||||
* http://getbootstrap.com/javascript/#modals
|
||||
* ========================================================================
|
||||
* Copyright 2011-2015 Twitter, Inc.
|
||||
@@ -14,12 +14,15 @@
|
||||
// ======================
|
||||
|
||||
var Modal = function (element, options) {
|
||||
this.options = options
|
||||
this.$body = $(document.body)
|
||||
this.$element = $(element)
|
||||
this.$backdrop =
|
||||
this.isShown = null
|
||||
this.scrollbarWidth = 0
|
||||
this.options = options
|
||||
this.$body = $(document.body)
|
||||
this.$element = $(element)
|
||||
this.$dialog = this.$element.find('.modal-dialog')
|
||||
this.$backdrop = null
|
||||
this.isShown = null
|
||||
this.originalBodyPad = null
|
||||
this.scrollbarWidth = 0
|
||||
this.ignoreBackdropClick = false
|
||||
|
||||
if (this.options.remote) {
|
||||
this.$element
|
||||
@@ -30,7 +33,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
Modal.VERSION = '3.3.2'
|
||||
Modal.VERSION = '3.3.5'
|
||||
|
||||
Modal.TRANSITION_DURATION = 300
|
||||
Modal.BACKDROP_TRANSITION_DURATION = 150
|
||||
@@ -64,6 +67,12 @@
|
||||
|
||||
this.$element.on('click.dismiss.bs.modal', '[data-dismiss="modal"]', $.proxy(this.hide, this))
|
||||
|
||||
this.$dialog.on('mousedown.dismiss.bs.modal', function () {
|
||||
that.$element.one('mouseup.dismiss.bs.modal', function (e) {
|
||||
if ($(e.target).is(that.$element)) that.ignoreBackdropClick = true
|
||||
})
|
||||
})
|
||||
|
||||
this.backdrop(function () {
|
||||
var transition = $.support.transition && that.$element.hasClass('fade')
|
||||
|
||||
@@ -75,23 +84,20 @@
|
||||
.show()
|
||||
.scrollTop(0)
|
||||
|
||||
if (that.options.backdrop) that.adjustBackdrop()
|
||||
that.adjustDialog()
|
||||
|
||||
if (transition) {
|
||||
that.$element[0].offsetWidth // force reflow
|
||||
}
|
||||
|
||||
that.$element
|
||||
.addClass('in')
|
||||
.attr('aria-hidden', false)
|
||||
that.$element.addClass('in')
|
||||
|
||||
that.enforceFocus()
|
||||
|
||||
var e = $.Event('shown.bs.modal', { relatedTarget: _relatedTarget })
|
||||
|
||||
transition ?
|
||||
that.$element.find('.modal-dialog') // wait for modal to slide in
|
||||
that.$dialog // wait for modal to slide in
|
||||
.one('bsTransitionEnd', function () {
|
||||
that.$element.trigger('focus').trigger(e)
|
||||
})
|
||||
@@ -118,8 +124,10 @@
|
||||
|
||||
this.$element
|
||||
.removeClass('in')
|
||||
.attr('aria-hidden', true)
|
||||
.off('click.dismiss.bs.modal')
|
||||
.off('mouseup.dismiss.bs.modal')
|
||||
|
||||
this.$dialog.off('mousedown.dismiss.bs.modal')
|
||||
|
||||
$.support.transition && this.$element.hasClass('fade') ?
|
||||
this.$element
|
||||
@@ -179,14 +187,20 @@
|
||||
if (this.isShown && this.options.backdrop) {
|
||||
var doAnimate = $.support.transition && animate
|
||||
|
||||
this.$backdrop = $('<div class="modal-backdrop ' + animate + '" />')
|
||||
.prependTo(this.$element)
|
||||
.on('click.dismiss.bs.modal', $.proxy(function (e) {
|
||||
if (e.target !== e.currentTarget) return
|
||||
this.options.backdrop == 'static'
|
||||
? this.$element[0].focus.call(this.$element[0])
|
||||
: this.hide.call(this)
|
||||
}, this))
|
||||
this.$backdrop = $(document.createElement('div'))
|
||||
.addClass('modal-backdrop ' + animate)
|
||||
.appendTo(this.$body)
|
||||
|
||||
this.$element.on('click.dismiss.bs.modal', $.proxy(function (e) {
|
||||
if (this.ignoreBackdropClick) {
|
||||
this.ignoreBackdropClick = false
|
||||
return
|
||||
}
|
||||
if (e.target !== e.currentTarget) return
|
||||
this.options.backdrop == 'static'
|
||||
? this.$element[0].focus()
|
||||
: this.hide()
|
||||
}, this))
|
||||
|
||||
if (doAnimate) this.$backdrop[0].offsetWidth // force reflow
|
||||
|
||||
@@ -221,16 +235,9 @@
|
||||
// these following methods are used to handle overflowing modals
|
||||
|
||||
Modal.prototype.handleUpdate = function () {
|
||||
if (this.options.backdrop) this.adjustBackdrop()
|
||||
this.adjustDialog()
|
||||
}
|
||||
|
||||
Modal.prototype.adjustBackdrop = function () {
|
||||
this.$backdrop
|
||||
.css('height', 0)
|
||||
.css('height', this.$element[0].scrollHeight)
|
||||
}
|
||||
|
||||
Modal.prototype.adjustDialog = function () {
|
||||
var modalIsOverflowing = this.$element[0].scrollHeight > document.documentElement.clientHeight
|
||||
|
||||
@@ -248,17 +255,23 @@
|
||||
}
|
||||
|
||||
Modal.prototype.checkScrollbar = function () {
|
||||
this.bodyIsOverflowing = document.body.scrollHeight > document.documentElement.clientHeight
|
||||
var fullWindowWidth = window.innerWidth
|
||||
if (!fullWindowWidth) { // workaround for missing window.innerWidth in IE8
|
||||
var documentElementRect = document.documentElement.getBoundingClientRect()
|
||||
fullWindowWidth = documentElementRect.right - Math.abs(documentElementRect.left)
|
||||
}
|
||||
this.bodyIsOverflowing = document.body.clientWidth < fullWindowWidth
|
||||
this.scrollbarWidth = this.measureScrollbar()
|
||||
}
|
||||
|
||||
Modal.prototype.setScrollbar = function () {
|
||||
var bodyPad = parseInt((this.$body.css('padding-right') || 0), 10)
|
||||
this.originalBodyPad = document.body.style.paddingRight || ''
|
||||
if (this.bodyIsOverflowing) this.$body.css('padding-right', bodyPad + this.scrollbarWidth)
|
||||
}
|
||||
|
||||
Modal.prototype.resetScrollbar = function () {
|
||||
this.$body.css('padding-right', '')
|
||||
this.$body.css('padding-right', this.originalBodyPad)
|
||||
}
|
||||
|
||||
Modal.prototype.measureScrollbar = function () { // thx walsh
|
||||
|
||||
@@ -82,7 +82,8 @@
|
||||
<template > {# Note: page is 595x842 points (1 point=1/72in) #}
|
||||
<pageTemplate id="Headed" >
|
||||
<pageGraphics>
|
||||
<image file="RIGS/static/imgs/paperwork/corner.jpg" x="395" y="642" height="200" width="200"/>
|
||||
<image file="RIGS/static/imgs/paperwork/corner-tr-su.jpg" x="395" y="642" height="200" width="200"/>
|
||||
<image file="RIGS/static/imgs/paperwork/corner-bl.jpg" x="0" y="0" height="200" width="200"/>
|
||||
|
||||
{# logo positioned 42 from left, 33 from top #}
|
||||
<image file="RIGS/static/imgs/paperwork/tec-logo.jpg" x="42" y="719" height="90" width="84"/>
|
||||
@@ -108,6 +109,9 @@
|
||||
|
||||
<pageTemplate id="Main">
|
||||
<pageGraphics>
|
||||
<image file="RIGS/static/imgs/paperwork/corner-tr.jpg" x="395" y="642" height="200" width="200"/>
|
||||
<image file="RIGS/static/imgs/paperwork/corner-bl.jpg" x="0" y="0" height="200" width="200"/>
|
||||
|
||||
<setFont name="OpenSans" size="10"/>
|
||||
{% if not invoice %}<drawCenteredString x="302.5" y="50">[{{ copy }} Copy]</drawCenteredString>{% endif %}
|
||||
<drawCenteredString x="302.5" y="38">[Page <pageNumber/> of <getName id="lastPage" default="0" />]</drawCenteredString>
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
<b>{{object.start_date|date:"D jS N Y"}}</b>
|
||||
</para>
|
||||
|
||||
<keepInFrame maxHeight="30">
|
||||
<keepInFrame>
|
||||
<para style="style.event_description">
|
||||
{{ object.description|default_if_none:""|linebreaksbr }}
|
||||
</para>
|
||||
@@ -268,7 +268,7 @@
|
||||
|
||||
<para style="blockPara">
|
||||
<b>
|
||||
Conditions of hire available on request or on the TEC PA & Lighting website. E&OE
|
||||
Conditions of hire attached and available on the TEC PA & Lighting website. E&OE
|
||||
</b>
|
||||
</para>
|
||||
|
||||
@@ -295,7 +295,7 @@
|
||||
|
||||
<para style="blockPara">
|
||||
<b>
|
||||
Conditions of hire available on request or on the TEC PA & Lighting website. E&OE
|
||||
Conditions of hire attached and available on the TEC PA & Lighting website. E&OE
|
||||
</b>
|
||||
</para>
|
||||
|
||||
|
||||
@@ -38,7 +38,7 @@
|
||||
<small>at {{ event.venue }}</small>
|
||||
{% endif %}
|
||||
{% if event.dry_hire %}
|
||||
<span class="badge">Dry Hire</span>
|
||||
<span class="label label-default">Dry Hire</span>
|
||||
{% endif %}
|
||||
</h4>
|
||||
{% if event.is_rig and not event.cancelled %}
|
||||
|
||||
@@ -29,8 +29,13 @@
|
||||
<td colspan="2">£ <span id="sumtotal">{{object.sum_total|default:0|floatformat:2}}</span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td id="vat-rate" data-rate="{{object.vat_rate.rate}}">VAT @
|
||||
{{object.vat_rate.as_percent|floatformat|default:"TBD"}}%</td>
|
||||
{% if not object.pk %}
|
||||
<td id="vat-rate" data-rate="{{currentVAT.rate}}">VAT @
|
||||
{{currentVAT.as_percent|floatformat}}% (TBC)</td>
|
||||
{% else %}
|
||||
<td id="vat-rate" data-rate="{{object.vat_rate.rate}}">VAT @
|
||||
{{object.vat_rate.as_percent|floatformat|default:"TBD"}}%</td>
|
||||
{% endif %}
|
||||
<td colspan="2">£ <span id="vat">{{object.vat|default:0|floatformat:2}}</span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
|
||||
@@ -1,82 +1,109 @@
|
||||
{% extends request.is_ajax|yesno:"base_ajax.html,base.html" %}
|
||||
{% load widget_tweaks %}
|
||||
|
||||
{% block title %}{{ object.name }}{% endblock %}
|
||||
{% block title %}Organisation | {{ object.name }}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
{% if not request.is_ajax %}
|
||||
<div class="row">
|
||||
<div class="col-sm-8">
|
||||
<h3>{{ object.name }}<br/>
|
||||
<span class="small"><a href="{% url 'organisation_history' object.pk %}" title="View Revision History">
|
||||
Last edited at {{ object.last_edited_at|date:"d/m/Y H:i" }} by {{ object.last_edited_by.name }}
|
||||
</a></span>
|
||||
</h3>
|
||||
</div>
|
||||
<div class="pull-right">
|
||||
<a href="{% url 'organisation_update' object.pk %}" class="btn btn-primary">Edit <span
|
||||
class="glyphicon glyphicon-pencil"></span></a>
|
||||
</div>
|
||||
<div class="col-sm-4">
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
<div class="row">
|
||||
{% if not request.is_ajax %}
|
||||
<div class="col-sm-12">
|
||||
<h1>Organisation | {{ object.name }}</h1>
|
||||
</div>
|
||||
|
||||
<div class="col-sm-12 text-right">
|
||||
<div class="btn-group btn-page">
|
||||
<a href="{% url 'organisation_update' object.pk %}" class="btn btn-default"><span
|
||||
class="glyphicon glyphicon-pencil"></span> Edit</a>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
<div class="col-sm-6">
|
||||
<h4>Details</h4>
|
||||
<dl class="dl-horizontal">
|
||||
<dt>Name</dt>
|
||||
<dd>{{ object.name }}</dd>
|
||||
<div class="panel panel-info">
|
||||
<div class="panel-heading">Organisation Details</div>
|
||||
<div class="panel-body">
|
||||
<dl class="dl-horizontal">
|
||||
<dt>Name</dt>
|
||||
<dd>{{ object.name }}</dd>
|
||||
|
||||
<dt>Phone</dt>
|
||||
<dd><a href="tel:{{ object.phone }}">{{ object.phone }}</a></dd>
|
||||
<dt>Phone</dt>
|
||||
<dd><a href="tel:{{ object.phone }}">{{ object.phone }}</a></dd>
|
||||
|
||||
<dt>Email</dt>
|
||||
<dd><a href="mailto:{{ object.email }}">{{ object.email }}</a></dd>
|
||||
<dt>Email</dt>
|
||||
<dd><a href="mailto:{{ object.email }}"><span class="overflow-ellipsis">{{ object.email }}</span></a></dd>
|
||||
|
||||
<dt>Address</dt>
|
||||
<dd>{{ object.address|linebreaksbr }}</dd>
|
||||
<dt>Address</dt>
|
||||
<dd>{{ object.address|linebreaksbr }}</dd>
|
||||
|
||||
<dt>Notes</dt>
|
||||
<dd>{{ object.notes|linebreaksbr }}</dd>
|
||||
<dt>Notes</dt>
|
||||
<dd>{{ object.notes|linebreaksbr }}</dd>
|
||||
|
||||
<dt>Union Account</dt>
|
||||
<dd>{{ object.union_account|yesno|capfirst }}</dd>
|
||||
</dl>
|
||||
<dt>Union Account</dt>
|
||||
<dd>{{ object.union_account|yesno|capfirst }}</dd>
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-sm-6">
|
||||
<h4>People</h4>
|
||||
<ul class="list-unstyled">
|
||||
{% for person in object.persons %}
|
||||
<li><a href="{% url 'person_detail' person.pk %}">{{ person.name }}</a></li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">Associated People</div>
|
||||
<div class="panel-body">
|
||||
<div class="list-group">
|
||||
{% for person,count in object.persons %}
|
||||
<a class="list-group-item" href="{% url 'person_detail' person.pk %}">{{ person.pk|stringformat:"05d" }} | {{ person.name }} <span class="badge" title="{{count}} events with {{person.name}} for {{object.name}}">{{count}}</span></a>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
<h4>Events</h4>
|
||||
{% with object.latest_events as events %}
|
||||
{% include 'RIGS/event_table.html' %}
|
||||
{% endwith %}
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">Associated Events</div>
|
||||
<div class="panel-body">
|
||||
{% with object.latest_events as events %}
|
||||
{% include 'RIGS/event_table.html' %}
|
||||
{% endwith %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
{% if not request.is_ajax %}
|
||||
<div class="row">
|
||||
<div class="col-sm-12 text-right">
|
||||
<div class="btn-group btn-page">
|
||||
<a href="{% url 'organisation_update' object.pk %}" class="btn btn-default"><span
|
||||
class="glyphicon glyphicon-pencil"></span> Edit</a>
|
||||
</div>
|
||||
<div>
|
||||
<a href="{% url 'organisation_history' object.pk %}" title="View Revision History">
|
||||
Last edited {{ object.last_edited_at }} by {{ object.last_edited_by.name }}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% endblock %}
|
||||
|
||||
{% if request.is_ajax %}
|
||||
{% block footer %}
|
||||
<div class="row">
|
||||
<div class="col-sm-10 align-left">
|
||||
<a href="{% url 'organisation_history' object.pk %}" title="View Revision History">
|
||||
Last edited at {{ object.last_edited_at }} by {{ object.last_edited_by.name }}
|
||||
</a>
|
||||
</div>
|
||||
<div class="col-sm-2">
|
||||
<div class="pull-right">
|
||||
<a href="{% url 'organisation_update' object.pk %}" class="btn btn-primary">Edit <span
|
||||
class="glyphicon glyphicon-pencil"></span></a>
|
||||
<div class="col-sm-12 text-right">
|
||||
<div class="btn-group btn-page">
|
||||
<a href="{% url 'organisation_detail' object.pk %}" class="btn btn-default"><span
|
||||
class="glyphicon glyphicon-eye-open"></span> Open Page</a>
|
||||
<a href="{% url 'organisation_update' object.pk %}" class="btn btn-default"><span
|
||||
class="glyphicon glyphicon-pencil"></span> Edit</a>
|
||||
</div>
|
||||
<div>
|
||||
<a href="{% url 'organisation_history' object.pk %}" title="View Revision History">
|
||||
Last edited {{ object.last_edited_at }} by {{ object.last_edited_by.name }}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{% extends request.is_ajax|yesno:'base_ajax.html,base.html' %}
|
||||
{% load widget_tweaks %}
|
||||
|
||||
{% block title %}{% if object.pk %}Edit {{ object.name }}{% else %}Add Person{% endif %}{% endblock %}
|
||||
{% block title %}{% if object.pk %}Edit {{ object.name }}{% else %}Add Organisation{% endif %}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="col-sm-offset-1 col-sm-10">
|
||||
|
||||
@@ -1,74 +1,106 @@
|
||||
{% extends request.is_ajax|yesno:"base_ajax.html,base.html" %}
|
||||
{% load widget_tweaks %}
|
||||
|
||||
{% block title %}{{ object.name }}{% endblock %}
|
||||
{% block title %}Person | {{ object.name }}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row">
|
||||
<div class="col-sm-6">
|
||||
<h4>Details</h4>
|
||||
{% if not request.is_ajax %}
|
||||
<h3>{{ object.name }}<br/>
|
||||
<span class="small"><a href="{% url 'person_history' object.pk %}" title="View Revision History">
|
||||
Last edited at {{ object.last_edited_at|date:"d/m/Y H:i" }} by {{ object.last_edited_by.name }}
|
||||
</a></span>
|
||||
</h3>
|
||||
<div class="pull-right">
|
||||
<a href="{% url 'person_update' object.pk %}" class="btn btn-primary">Edit <span
|
||||
class="glyphicon glyphicon-pencil"></span></a>
|
||||
{% if not request.is_ajax %}
|
||||
<div class="col-sm-12">
|
||||
<h1>Person | {{ object.name }}</h1>
|
||||
</div>
|
||||
|
||||
<div class="col-sm-12 text-right">
|
||||
<div class="btn-group btn-page">
|
||||
<a href="{% url 'person_update' object.pk %}" class="btn btn-default"><span
|
||||
class="glyphicon glyphicon-pencil"></span> Edit</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
<div class="">
|
||||
<dl class="dl-horizontal">
|
||||
<dt>Name</dt>
|
||||
<dd>{{ object.name }}</dd>
|
||||
</div>
|
||||
{% endif %}
|
||||
<div class="col-sm-6">
|
||||
<div class="panel panel-info">
|
||||
<div class="panel-heading">Person Details</div>
|
||||
<div class="panel-body">
|
||||
<dl class="dl-horizontal">
|
||||
<dt>Name</dt>
|
||||
<dd>{{ object.name }}</dd>
|
||||
|
||||
<dt>Phone</dt>
|
||||
<dd><a href="tel:{{ object.phone }}">{{ object.phone }}</a></dd>
|
||||
<dt>Phone</dt>
|
||||
<dd><a href="tel:{{ object.phone }}">{{ object.phone }}</a></dd>
|
||||
|
||||
<dt>Email</dt>
|
||||
<dd><a href="mailto:{{ object.email }}">{{ object.email }}</a></dd>
|
||||
<dt>Email</dt>
|
||||
<dd><a href="mailto:{{ object.email }}"><span class="overflow-ellipsis">{{ object.email }}</span></a></dd>
|
||||
|
||||
<dt>Address</dt>
|
||||
<dd>{{ object.address|linebreaksbr }}</dd>
|
||||
<dt>Address</dt>
|
||||
<dd>{{ object.address|linebreaksbr }}</dd>
|
||||
|
||||
<dt>Notes</dt>
|
||||
<dd>{{ object.notes|linebreaksbr }}</dd>
|
||||
</dl>
|
||||
<dt>Notes</dt>
|
||||
<dd>{{ object.notes|linebreaksbr }}</dd>
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-sm-6">
|
||||
<h4>Organisations</h4>
|
||||
<ul class="list-unstyled">
|
||||
{% for organisation in object.organisations %}
|
||||
<li><a href="{% url 'organisation_detail' organisation.pk %}">{{ organisation.name }}</a></li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">Associated Organisations</div>
|
||||
<div class="panel-body">
|
||||
<div class="list-group">
|
||||
{% for organisation,count in object.organisations %}
|
||||
<a class="list-group-item" href="{% url 'organisation_detail' organisation.pk %}">{{ organisation.pk|stringformat:"05d" }} | {{ organisation.name }} <span class="badge" title="{{count}} events with {{object.name}} for {{organisation.name}}">{{count}}</span></a>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
<h4>Events</h4>
|
||||
{% with object.latest_events as events %}
|
||||
{% include 'RIGS/event_table.html' %}
|
||||
{% endwith %}
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">Associated Events</div>
|
||||
<div class="panel-body">
|
||||
{% with object.latest_events as events %}
|
||||
{% include 'RIGS/event_table.html' %}
|
||||
{% endwith %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
{% if not request.is_ajax %}
|
||||
<div class="row">
|
||||
<div class="col-sm-12 text-right">
|
||||
<div class="btn-group btn-page">
|
||||
<a href="{% url 'person_update' object.pk %}" class="btn btn-default"><span
|
||||
class="glyphicon glyphicon-pencil"></span> Edit</a>
|
||||
</div>
|
||||
<div>
|
||||
<a href="{% url 'person_history' object.pk %}" title="View Revision History">
|
||||
Last edited {{ object.last_edited_at }} by {{ object.last_edited_by.name }}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% endblock %}
|
||||
|
||||
{% if request.is_ajax %}
|
||||
{% block footer %}
|
||||
<div class="row">
|
||||
<div class="col-sm-10 align-left">
|
||||
<a href="{% url 'person_history' object.pk %}" title="View Revision History">
|
||||
Last edited at {{ object.last_edited_at|date:"d/m/Y H:i" }} by {{ object.last_edited_by.name }}
|
||||
</a>
|
||||
</div>
|
||||
<div class="col-sm-2">
|
||||
<div class="pull-right">
|
||||
<a href="{% url 'person_update' object.pk %}" class="btn btn-primary">Edit <span
|
||||
class="glyphicon glyphicon-pencil"></span></a>
|
||||
<div class="col-sm-12 text-right">
|
||||
<div class="btn-group btn-page">
|
||||
<a href="{% url 'person_detail' object.pk %}" class="btn btn-default"><span
|
||||
class="glyphicon glyphicon-eye-open"></span> Open Page</a>
|
||||
<a href="{% url 'person_update' object.pk %}" class="btn btn-default"><span
|
||||
class="glyphicon glyphicon-pencil"></span> Edit</a>
|
||||
</div>
|
||||
<div>
|
||||
<a href="{% url 'person_history' object.pk %}" title="View Revision History">
|
||||
Last edited {{ object.last_edited_at }} by {{ object.last_edited_by.name }}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,64 +1,96 @@
|
||||
{% extends request.is_ajax|yesno:"base_ajax.html,base.html" %}
|
||||
{% load widget_tweaks %}
|
||||
|
||||
{% block title %}{{ object.name }}{% endblock %}
|
||||
{% block title %}Venue | {{ object.name }}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row">
|
||||
<div class="col-sm-10 col-sm-offset-1">
|
||||
{% if not request.is_ajax %}
|
||||
<h3>{{ object.name }}<br/>
|
||||
<span class="small"><a href="{% url 'venue_history' object.pk %}" title="View Revision History">
|
||||
Last edited at {{ object.last_edited_at|date:"d/m/Y H:i" }} by {{ object.last_edited_by.name }}
|
||||
</a></span>
|
||||
</h3>
|
||||
<div class="pull-right">
|
||||
<a href="{% url 'venue_update' object.pk %}" class="btn btn-primary">Edit <span
|
||||
class="glyphicon glyphicon-pencil"></span></a>
|
||||
{% if not request.is_ajax %}
|
||||
<div class="col-sm-12">
|
||||
<h1>Venue | {{ object.name }}</h1>
|
||||
</div>
|
||||
|
||||
<div class="col-sm-12 text-right">
|
||||
<div class="btn-group btn-page">
|
||||
<a href="{% url 'venue_update' object.pk %}" class="btn btn-default"><span
|
||||
class="glyphicon glyphicon-pencil"></span> Edit</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
<div class="">
|
||||
<dl class="dl-horizontal">
|
||||
<dt>Name</dt>
|
||||
<dd>{{ object.name }}</dd>
|
||||
</div>
|
||||
{% endif %}
|
||||
<div class="col-sm-12">
|
||||
<div class="panel panel-info">
|
||||
<div class="panel-heading">Venue Details</div>
|
||||
<div class="panel-body">
|
||||
<dl class="dl-horizontal">
|
||||
<dt>Name</dt>
|
||||
<dd>{{ object.name }}</dd>
|
||||
|
||||
<dt>Phone</dt>
|
||||
<dd><a href="tel:{{ object.phone }}">{{ object.phone }}</a></dd>
|
||||
<dt>Phone</dt>
|
||||
<dd><a href="tel:{{ object.phone }}">{{ object.phone }}</a></dd>
|
||||
|
||||
<dt>Email</dt>
|
||||
<dd><a href="mailto:{{ object.email }}">{{ object.email }}</a></dd>
|
||||
<dt>Email</dt>
|
||||
<dd><a href="mailto:{{ object.email }}"><span class="overflow-ellipsis">{{ object.email }}</span></a></dd>
|
||||
|
||||
<dt>Address</dt>
|
||||
<dd>{{ object.address|linebreaksbr }}</dd>
|
||||
<dt>Address</dt>
|
||||
<dd>{{ object.address|linebreaksbr }}</dd>
|
||||
|
||||
<dt>Notes</dt>
|
||||
<dd>{{ object.notes|linebreaksbr }}</dd>
|
||||
</dl>
|
||||
<dt>Notes</dt>
|
||||
<dd>{{ object.notes|linebreaksbr }}</dd>
|
||||
|
||||
<dt>Three Phase Available</dt>
|
||||
<dd>{{ object.three_phase_available|yesno|capfirst }}</dd>
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
{% with object.latest_events as events %}
|
||||
{% include 'RIGS/event_table.html' %}
|
||||
{% endwith %}
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">Associated Events</div>
|
||||
<div class="panel-body">
|
||||
{% with object.latest_events as events %}
|
||||
{% include 'RIGS/event_table.html' %}
|
||||
{% endwith %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% if not request.is_ajax %}
|
||||
<div class="row">
|
||||
<div class="col-sm-12 text-right">
|
||||
<div class="btn-group btn-page">
|
||||
<a href="{% url 'venue_update' object.pk %}" class="btn btn-default"><span
|
||||
class="glyphicon glyphicon-pencil"></span> Edit</a>
|
||||
</div>
|
||||
<div>
|
||||
<a href="{% url 'venue_history' object.pk %}" title="View Revision History">
|
||||
Last edited {{ object.last_edited_at }} by {{ object.last_edited_by.name }}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% endblock %}
|
||||
|
||||
|
||||
{% if request.is_ajax %}
|
||||
{% block footer %}
|
||||
<div class="row">
|
||||
<div class="col-sm-10 align-left">
|
||||
<a href="{% url 'venue_history' object.pk %}" title="View Revision History">
|
||||
Last edited at {{ object.last_edited_at|date:"d/m/Y H:i" }} by {{ object.last_edited_by.name }}
|
||||
</a>
|
||||
</div>
|
||||
<div class="col-sm-2">
|
||||
<div class="pull-right">
|
||||
<a href="{% url 'venue_update' object.pk %}" class="btn btn-primary">Edit <span
|
||||
class="glyphicon glyphicon-pencil"></span></a>
|
||||
<div class="col-sm-12 text-right">
|
||||
<div class="btn-group btn-page">
|
||||
<a href="{% url 'venue_detail' object.pk %}" class="btn btn-default"><span
|
||||
class="glyphicon glyphicon-eye-open"></span> Open Page</a>
|
||||
<a href="{% url 'venue_update' object.pk %}" class="btn btn-default"><span
|
||||
class="glyphicon glyphicon-pencil"></span> Edit</a>
|
||||
</div>
|
||||
<div>
|
||||
<a href="{% url 'venue_history' object.pk %}" title="View Revision History">
|
||||
Last edited {{ object.last_edited_at }} by {{ object.last_edited_by.name }}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -58,7 +58,21 @@
|
||||
{% render_field form.notes class+="form-control" placeholder=form.notes.label %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="form-group">
|
||||
<div class="col-sm-10 col-sm-offset-2">
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
{% render_field form.three_phase_available %} {{ form.three_phase_available.label }}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<input class="btn btn-primary pull-right" type="submit"/>
|
||||
|
||||
@@ -145,6 +145,8 @@ class EventTest(LiveServerTestCase):
|
||||
self.profile.set_password("EventTestPassword")
|
||||
self.profile.save()
|
||||
|
||||
self.vatrate = models.VatRate.objects.create(start_at='2014-03-05',rate=0.20,comment='test1')
|
||||
|
||||
self.browser = webdriver.Firefox()
|
||||
os.environ['RECAPTCHA_TESTING'] = 'True'
|
||||
|
||||
@@ -377,9 +379,9 @@ class EventTest(LiveServerTestCase):
|
||||
|
||||
# Check totals
|
||||
self.assertEqual("47.90", self.browser.find_element_by_id('sumtotal').text)
|
||||
self.assertIn("TBD%", self.browser.find_element_by_id('vat-rate').text)
|
||||
self.assertEqual("0.00", self.browser.find_element_by_id('vat').text)
|
||||
self.assertEqual("47.90", self.browser.find_element_by_id('total').text)
|
||||
self.assertIn("(TBC)", self.browser.find_element_by_id('vat-rate').text)
|
||||
self.assertEqual("9.58", self.browser.find_element_by_id('vat').text)
|
||||
self.assertEqual("57.48", self.browser.find_element_by_id('total').text)
|
||||
|
||||
# Attempt to save - missing title
|
||||
save.click()
|
||||
@@ -414,6 +416,121 @@ class EventTest(LiveServerTestCase):
|
||||
event = models.Event.objects.get(name='Test Event Name')
|
||||
self.assertIn("N0000%d | Test Event Name"%event.pk, self.browser.find_element_by_xpath('//h1').text)
|
||||
|
||||
def testDateValidation(self):
|
||||
self.browser.get(self.live_server_url + '/event/create/')
|
||||
# Gets redirected to login and back
|
||||
self.authenticate('/event/create/')
|
||||
|
||||
wait = WebDriverWait(self.browser, 10) #setup WebDriverWait to use later (to wait for animations)
|
||||
self.browser.implicitly_wait(3) #Set session-long wait (only works for non-existant DOM objects)
|
||||
|
||||
wait.until(animation_is_finished())
|
||||
|
||||
# Click Rig button
|
||||
self.browser.find_element_by_xpath('//button[.="Rig"]').click()
|
||||
|
||||
form = self.browser.find_element_by_tag_name('form')
|
||||
save = self.browser.find_element_by_xpath('(//button[@type="submit"])[3]')
|
||||
|
||||
# Set title
|
||||
e = self.browser.find_element_by_id('id_name')
|
||||
e.send_keys('Test Event Name')
|
||||
|
||||
# Both dates, no times, end before start
|
||||
form.find_element_by_id('id_start_date').clear()
|
||||
form.find_element_by_id('id_start_date').send_keys('3015-04-24')
|
||||
|
||||
form.find_element_by_id('id_end_date').clear()
|
||||
form.find_element_by_id('id_end_date').send_keys('3015-04-23')
|
||||
|
||||
# Attempt to save - should fail
|
||||
save.click()
|
||||
error = self.browser.find_element_by_xpath('//div[contains(@class, "alert-danger")]')
|
||||
self.assertTrue(error.is_displayed())
|
||||
self.assertIn("can't finish before it has started", error.find_element_by_xpath('//dd[1]/ul/li').text)
|
||||
|
||||
|
||||
# Same date, end time before start time
|
||||
form = self.browser.find_element_by_tag_name('form')
|
||||
save = self.browser.find_element_by_xpath('(//button[@type="submit"])[3]')
|
||||
form.find_element_by_id('id_start_date').clear()
|
||||
form.find_element_by_id('id_start_date').send_keys('3015-04-24')
|
||||
|
||||
form.find_element_by_id('id_end_date').clear()
|
||||
form.find_element_by_id('id_end_date').send_keys('3015-04-23')
|
||||
|
||||
form.find_element_by_id('id_start_time').clear()
|
||||
form.find_element_by_id('id_start_time').send_keys('06:59')
|
||||
|
||||
form.find_element_by_id('id_end_time').clear()
|
||||
form.find_element_by_id('id_end_time').send_keys('06:00')
|
||||
|
||||
# Attempt to save - should fail
|
||||
save.click()
|
||||
error = self.browser.find_element_by_xpath('//div[contains(@class, "alert-danger")]')
|
||||
self.assertTrue(error.is_displayed())
|
||||
self.assertIn("can't finish before it has started", error.find_element_by_xpath('//dd[1]/ul/li').text)
|
||||
|
||||
|
||||
# Same date, end time before start time
|
||||
form = self.browser.find_element_by_tag_name('form')
|
||||
save = self.browser.find_element_by_xpath('(//button[@type="submit"])[3]')
|
||||
form.find_element_by_id('id_start_date').clear()
|
||||
form.find_element_by_id('id_start_date').send_keys('3015-04-24')
|
||||
|
||||
form.find_element_by_id('id_end_date').clear()
|
||||
form.find_element_by_id('id_end_date').send_keys('3015-04-23')
|
||||
|
||||
form.find_element_by_id('id_start_time').clear()
|
||||
form.find_element_by_id('id_start_time').send_keys('06:59')
|
||||
|
||||
form.find_element_by_id('id_end_time').clear()
|
||||
form.find_element_by_id('id_end_time').send_keys('06:00')
|
||||
|
||||
|
||||
# No end date, end time before start time
|
||||
form = self.browser.find_element_by_tag_name('form')
|
||||
save = self.browser.find_element_by_xpath('(//button[@type="submit"])[3]')
|
||||
form.find_element_by_id('id_start_date').clear()
|
||||
form.find_element_by_id('id_start_date').send_keys('3015-04-24')
|
||||
|
||||
form.find_element_by_id('id_end_date').clear()
|
||||
|
||||
form.find_element_by_id('id_start_time').clear()
|
||||
form.find_element_by_id('id_start_time').send_keys('06:59')
|
||||
|
||||
form.find_element_by_id('id_end_time').clear()
|
||||
form.find_element_by_id('id_end_time').send_keys('06:00')
|
||||
|
||||
# Attempt to save - should fail
|
||||
save.click()
|
||||
error = self.browser.find_element_by_xpath('//div[contains(@class, "alert-danger")]')
|
||||
self.assertTrue(error.is_displayed())
|
||||
self.assertIn("can't finish before it has started", error.find_element_by_xpath('//dd[1]/ul/li').text)
|
||||
|
||||
|
||||
# 2 dates, end after start
|
||||
form = self.browser.find_element_by_tag_name('form')
|
||||
save = self.browser.find_element_by_xpath('(//button[@type="submit"])[3]')
|
||||
form.find_element_by_id('id_start_date').clear()
|
||||
form.find_element_by_id('id_start_date').send_keys('3015-04-24')
|
||||
|
||||
form.find_element_by_id('id_end_date').clear()
|
||||
form.find_element_by_id('id_end_date').send_keys('3015-04-26')
|
||||
|
||||
form.find_element_by_id('id_start_time').clear()
|
||||
|
||||
form.find_element_by_id('id_end_time').clear()
|
||||
|
||||
# Attempt to save - should succeed
|
||||
save.click()
|
||||
|
||||
# See redirected to success page
|
||||
event = models.Event.objects.get(name='Test Event Name')
|
||||
self.assertIn("N0000%d | Test Event Name"%event.pk, self.browser.find_element_by_xpath('//h1').text)
|
||||
|
||||
|
||||
|
||||
def testEventDetail(self):
|
||||
with transaction.atomic(), reversion.create_revision():
|
||||
person = models.Person(name="Event Detail Person", email="eventdetail@person.tests.rigs", phone="123 123")
|
||||
|
||||
@@ -144,16 +144,16 @@ class EventTestCase(TestCase):
|
||||
events = models.Event.objects.all()
|
||||
|
||||
# Check person's organisations
|
||||
self.assertIn(o1, p1.organisations)
|
||||
self.assertIn(o2, p1.organisations)
|
||||
self.assertIn(o1, p2.organisations)
|
||||
self.assertNotIn(o2, p2.organisations)
|
||||
self.assertIn((o1,2), p1.organisations)
|
||||
self.assertIn((o2,1), p1.organisations)
|
||||
self.assertIn((o1,2), p2.organisations)
|
||||
self.assertEqual(len(p2.organisations), 1)
|
||||
|
||||
# Check organisation's persons
|
||||
self.assertIn(p1, o1.persons)
|
||||
self.assertIn(p2, o1.persons)
|
||||
self.assertIn(p1, o2.persons)
|
||||
self.assertNotIn(p2, o2.persons)
|
||||
self.assertIn((p1,2), o1.persons)
|
||||
self.assertIn((p2,2), o1.persons)
|
||||
self.assertIn((p1,1), o2.persons)
|
||||
self.assertEqual(len(o2.persons),1)
|
||||
|
||||
def test_cancelled_property(self):
|
||||
event = models.Event.objects.all()[0]
|
||||
|
||||
@@ -177,7 +177,7 @@ class VenueDetail(generic.DetailView):
|
||||
|
||||
class VenueCreate(generic.CreateView):
|
||||
model = models.Venue
|
||||
fields = ['name','phone','email','address','notes']
|
||||
fields = ['name','phone','email','address','notes','three_phase_available']
|
||||
|
||||
def get_success_url(self):
|
||||
if self.request.is_ajax():
|
||||
@@ -194,7 +194,7 @@ class VenueCreate(generic.CreateView):
|
||||
|
||||
class VenueUpdate(generic.UpdateView):
|
||||
model = models.Venue
|
||||
fields = ['name','phone','email','address','notes']
|
||||
fields = ['name','phone','email','address','notes','three_phase_available']
|
||||
|
||||
def get_success_url(self):
|
||||
if self.request.is_ajax():
|
||||
|
||||
Reference in New Issue
Block a user