Compare commits
204 Commits
feature/bu
...
embed_impr
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d881ce3a96 | ||
|
|
e3fd84c1b6 | ||
|
|
7683dc37ff | ||
|
|
85a835ed0d | ||
|
|
14836f135c | ||
|
|
5f8a77586a | ||
|
|
e5b7fdbae1 | ||
|
|
f1c8dca8c4 | ||
|
|
843b76d8ea | ||
|
|
e81af9e479 | ||
|
|
efab8c8cef | ||
|
|
3f7531e157 | ||
|
|
988f3dced4 | ||
|
|
bd3240c2bc | ||
|
|
7fbbe2871f | ||
|
|
109ece508b | ||
|
|
971144c2e6 | ||
|
|
a3c6edda0b | ||
|
|
71bb4696b8 | ||
|
|
2a3ed0b763 | ||
|
|
632fa56353 | ||
|
|
f3020fc783 | ||
|
|
9fdf5e674e | ||
|
|
cfe03a8628 | ||
|
|
eccf224b63 | ||
|
|
0b2c86ebb5 | ||
|
|
f616857131 | ||
|
|
60fb90a50c | ||
|
|
66f024e961 | ||
|
|
06daacf611 | ||
|
|
c74bc945b6 | ||
|
|
3c605d2976 | ||
|
|
9720066fd7 | ||
|
|
b157e3b187 | ||
|
|
19030fdf2f | ||
|
|
42450b5a22 | ||
|
|
ce11df9bbc | ||
|
|
82e664c5e0 | ||
|
|
8098b33698 | ||
|
|
9c2603557c | ||
|
|
f4209f21dc | ||
|
|
1e3c021a76 | ||
|
|
d8f9256252 | ||
|
|
014bab6c1f | ||
|
|
8872084cab | ||
|
|
76ceb15000 | ||
|
|
7dff951f28 | ||
|
|
05feb7df1c | ||
|
|
ad3b38d222 | ||
|
|
a26e65073c | ||
|
|
7f3d628d01 | ||
|
|
abdf785723 | ||
|
|
85edba03a2 | ||
|
|
d61b21df0a | ||
|
|
7e68dfb851 | ||
|
|
69cbac31e7 | ||
|
|
ce4e0f5630 | ||
|
|
c99c5d573f | ||
|
|
221ef739b9 | ||
|
|
aa98039c35 | ||
|
|
7ef923b89f | ||
|
|
9ac86a6ad0 | ||
|
|
c0c143a166 | ||
|
|
0abfa2fd0c | ||
|
|
3333b29f24 | ||
|
|
880509d611 | ||
|
|
387f5b0d8e | ||
|
|
27b12d6bf4 | ||
|
|
c511f2f528 | ||
|
|
a61165f301 | ||
|
|
98245939fe | ||
|
|
b203832f79 | ||
|
|
c6846b85c7 | ||
|
|
0c15718d31 | ||
|
|
0f1db22452 | ||
|
|
e3970929e4 | ||
|
|
a64bf8e16f | ||
|
|
cf8edc8a1c | ||
|
|
ca9e309b1b | ||
|
|
0ccb669019 | ||
|
|
92acd17e64 | ||
|
|
ff83bcc14a | ||
|
|
f17e0e199c | ||
|
|
7b3b05677c | ||
|
|
403549f449 | ||
|
|
aee4ccb5a2 | ||
|
|
f8c2551eeb | ||
|
|
1bab3464b7 | ||
|
|
2b863b91b3 | ||
|
|
5c4181a5cc | ||
|
|
f9389e3996 | ||
|
|
131ff3e612 | ||
|
|
7dd7378e6c | ||
|
|
4d0da2fdc1 | ||
|
|
ddbc0699ee | ||
|
|
5f14001d01 | ||
|
|
91f1e6d290 | ||
|
|
00d46274f1 | ||
|
|
0aec836b1a | ||
|
|
ee930aa86a | ||
|
|
a88f4d0cb2 | ||
|
|
50c997b568 | ||
|
|
3035320e82 | ||
|
|
98182143ec | ||
|
|
bffbaeb4c6 | ||
|
|
8f1c640bea | ||
|
|
827a2cdd25 | ||
|
|
3202981fbe | ||
|
|
f0edd5020e | ||
|
|
cf1c7ae725 | ||
|
|
1ac637bfd7 | ||
|
|
37933650d3 | ||
|
|
1266dd419c | ||
|
|
74747e8700 | ||
|
|
34ffd62436 | ||
|
|
22c520e841 | ||
|
|
3fa9191150 | ||
|
|
ae4f05a661 | ||
|
|
c178a293a1 | ||
|
|
aebaa16311 | ||
|
|
175807f664 | ||
|
|
abb56af222 | ||
|
|
48139b29cf | ||
|
|
7ec09fb774 | ||
|
|
e37d1c663b | ||
|
|
bdd7f02fe2 | ||
|
|
b4ea818992 | ||
|
|
7cb9e97ecb | ||
|
|
7fdafd854e | ||
|
|
d85ebb63a1 | ||
|
|
c6b7bbc219 | ||
|
|
4d316c7a4a | ||
|
|
75a3059c88 | ||
|
|
b6e4c0ed14 | ||
|
|
0a45b047a2 | ||
|
|
4e79f00551 | ||
|
|
b4ab29393e | ||
|
|
703fb8561a | ||
|
|
f3c020b613 | ||
|
|
4b87b0a196 | ||
|
|
7cc715cedc | ||
|
|
4b032944ac | ||
|
|
cb23fd183e | ||
|
|
fbc039c274 | ||
|
|
b3156dbb0d | ||
|
|
fdce2fa53d | ||
|
|
55d24e96cb | ||
|
|
36d258253f | ||
|
|
865bb131a5 | ||
|
|
eb1e8935f4 | ||
|
|
f8aaf9f36e | ||
|
|
7a4f5ba8bf | ||
|
|
16a993123b | ||
|
|
e547e3e858 | ||
|
|
ea26823fec | ||
|
|
1f4e53ad27 | ||
|
|
374c31e8b4 | ||
|
|
0d726b2b60 | ||
|
|
38a8ac1eb4 | ||
|
|
872e5e72f3 | ||
|
|
d916c1ca19 | ||
|
|
9b1cc965c7 | ||
|
|
83028418fe | ||
|
|
7f680dcffb | ||
|
|
e573088c5e | ||
|
|
7ac9eef7a2 | ||
|
|
286e4314f5 | ||
|
|
6b05938953 | ||
|
|
602ba1d051 | ||
|
|
1710c3f01f | ||
|
|
f57ac3acb1 | ||
|
|
331dab20f7 | ||
|
|
d9076a4f5f | ||
|
|
56d4e438b6 | ||
|
|
430862b24d | ||
|
|
e12367bde7 | ||
|
|
c0f4884242 | ||
|
|
36638e4df6 | ||
|
|
a0440e1587 | ||
|
|
6e78f16c33 | ||
|
|
82b6f1cbf8 | ||
|
|
5be3842aea | ||
|
|
067e03b757 | ||
|
|
391d9ef28f | ||
|
|
5d17d642ec | ||
|
|
22119a3d08 | ||
|
|
306c11bb2f | ||
|
|
3fa9795cde | ||
|
|
7fd0c50146 | ||
|
|
97b11eabbd | ||
|
|
3b2aa02ae5 | ||
|
|
cf11e8235f | ||
|
|
1670b190c2 | ||
|
|
e65e97b1a3 | ||
|
|
c2787d54b0 | ||
|
|
8cfda69717 | ||
|
|
463c4d147c | ||
|
|
6da688cc9e | ||
|
|
c1d164bd73 | ||
|
|
d43e4b2465 | ||
|
|
98ee9bb0db | ||
|
|
cd2aed00d7 | ||
|
|
0ee37b1cd3 | ||
|
|
486c66b198 |
1
.gitignore
vendored
@@ -53,6 +53,7 @@ coverage.xml
|
||||
|
||||
# Django stuff:
|
||||
*.log
|
||||
db.sqlite3
|
||||
|
||||
# Sphinx documentation
|
||||
docs/_build/
|
||||
|
||||
23
.travis.yml
@@ -1,21 +1,32 @@
|
||||
language: python
|
||||
python:
|
||||
"2.7"
|
||||
"3.6"
|
||||
cache: pip
|
||||
|
||||
before_install:
|
||||
- "export DISPLAY=:99.0"
|
||||
- "/sbin/start-stop-daemon --start --quiet --pidfile /tmp/custom_xvfb_99.pid --make-pidfile --background --exec /usr/bin/Xvfb -- :99 -ac -screen 0 1280x1024x16"
|
||||
addons:
|
||||
chrome: stable
|
||||
|
||||
install:
|
||||
- wget https://chromedriver.storage.googleapis.com/2.36/chromedriver_linux64.zip
|
||||
- unzip chromedriver_linux64.zip
|
||||
- export PATH=$PATH:$(pwd)
|
||||
- chmod +x chromedriver
|
||||
- pip install -r requirements.txt
|
||||
- pip install coveralls codeclimate-test-reporter
|
||||
- pip install coveralls codeclimate-test-reporter pep8
|
||||
|
||||
before_script:
|
||||
- export PATH=$PATH:/usr/lib/chromium-browser/
|
||||
- python manage.py collectstatic --noinput
|
||||
|
||||
script:
|
||||
- coverage run manage.py test RIGS
|
||||
- pep8 . --exclude=migrations,importer*
|
||||
- python manage.py check
|
||||
- python manage.py makemigrations --check --dry-run
|
||||
- coverage run manage.py test --verbosity=2
|
||||
|
||||
after_success:
|
||||
- coveralls
|
||||
- codeclimate-test-reporter
|
||||
|
||||
notifications:
|
||||
webhooks: https://fathomless-fjord-24024.herokuapp.com/notify
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
from django.contrib.auth import REDIRECT_FIELD_NAME
|
||||
from django.shortcuts import render_to_response
|
||||
from django.template import RequestContext
|
||||
from django.shortcuts import render
|
||||
from django.http import HttpResponseRedirect
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.urls import reverse
|
||||
|
||||
from RIGS import models
|
||||
|
||||
@@ -24,17 +23,17 @@ def user_passes_test_with_403(test_func, login_url=None, oembed_view=None):
|
||||
def _checklogin(request, *args, **kwargs):
|
||||
if test_func(request.user):
|
||||
return view_func(request, *args, **kwargs)
|
||||
elif not request.user.is_authenticated():
|
||||
elif not request.user.is_authenticated:
|
||||
if oembed_view is not None:
|
||||
extra_context = {}
|
||||
extra_context['oembed_url'] = "{0}://{1}{2}".format(request.scheme, request.META['HTTP_HOST'], reverse(oembed_view, kwargs=kwargs))
|
||||
extra_context['login_url'] = "{0}?{1}={2}".format(login_url, REDIRECT_FIELD_NAME, request.get_full_path())
|
||||
resp = render_to_response('login_redirect.html', extra_context, context_instance=RequestContext(request))
|
||||
context = {}
|
||||
context['oembed_url'] = "{0}://{1}{2}".format(request.scheme, request.META['HTTP_HOST'], reverse(oembed_view, kwargs=kwargs))
|
||||
context['login_url'] = "{0}?{1}={2}".format(login_url, REDIRECT_FIELD_NAME, request.get_full_path())
|
||||
resp = render(request, 'login_redirect.html', context=context)
|
||||
return resp
|
||||
else:
|
||||
return HttpResponseRedirect('%s?%s=%s' % (login_url, REDIRECT_FIELD_NAME, request.get_full_path()))
|
||||
else:
|
||||
resp = render_to_response('403.html', context_instance=RequestContext(request))
|
||||
resp = render(request, '403.html')
|
||||
resp.status_code = 403
|
||||
return resp
|
||||
_checklogin.__doc__ = view_func.__doc__
|
||||
@@ -62,7 +61,7 @@ def api_key_required(function):
|
||||
userid = kwargs.get('api_pk')
|
||||
key = kwargs.get('api_key')
|
||||
|
||||
error_resp = render_to_response('403.html', context_instance=RequestContext(request))
|
||||
error_resp = render(request, '403.html')
|
||||
error_resp.status_code = 403
|
||||
|
||||
if key is None:
|
||||
@@ -79,3 +78,17 @@ def api_key_required(function):
|
||||
return error_resp
|
||||
return function(request, *args, **kwargs)
|
||||
return wrap
|
||||
|
||||
|
||||
def nottinghamtec_address_required(function):
|
||||
"""
|
||||
Checks that the current user has an email address ending @nottinghamtec.co.uk
|
||||
"""
|
||||
def wrap(request, *args, **kwargs):
|
||||
# Fail if current user's email address isn't @nottinghamtec.co.uk
|
||||
if not request.user.email.endswith('@nottinghamtec.co.uk'):
|
||||
error_resp = render(request, 'RIGS/eventauthorisation_request_error.html')
|
||||
return error_resp
|
||||
|
||||
return function(request, *args, **kwargs)
|
||||
return wrap
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
|
||||
DATETIME_FORMAT = ('d/m/Y H:i')
|
||||
DATE_FORMAT = ('d/m/Y')
|
||||
TIME_FORMAT = ('H:i')
|
||||
TIME_FORMAT = ('H:i')
|
||||
|
||||
@@ -10,29 +10,36 @@ https://docs.djangoproject.com/en/1.7/ref/settings/
|
||||
|
||||
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
|
||||
import os
|
||||
import raven
|
||||
|
||||
BASE_DIR = os.path.dirname(os.path.dirname(__file__))
|
||||
|
||||
# Quick-start development settings - unsuitable for production
|
||||
# See https://docs.djangoproject.com/en/1.7/howto/deployment/checklist/
|
||||
|
||||
# SECURITY WARNING: keep the secret key used in production secret!
|
||||
SECRET_KEY = os.environ.get('SECRET_KEY') if os.environ.get('SECRET_KEY') else 'gxhy(a#5mhp289_=6xx$7jh=eh$ymxg^ymc+di*0c*geiu3p_e'
|
||||
SECRET_KEY = os.environ.get('SECRET_KEY') if os.environ.get(
|
||||
'SECRET_KEY') else 'gxhy(a#5mhp289_=6xx$7jh=eh$ymxg^ymc+di*0c*geiu3p_e'
|
||||
|
||||
# SECURITY WARNING: don't run with debug turned on in production!
|
||||
DEBUG = bool(int(os.environ.get('DEBUG'))) if os.environ.get('DEBUG') else True
|
||||
|
||||
STAGING = bool(int(os.environ.get('STAGING'))) if os.environ.get('STAGING') else False
|
||||
|
||||
TEMPLATE_DEBUG = True
|
||||
STAGING = bool(int(os.environ.get('STAGING'))) if os.environ.get('STAGING') else False
|
||||
|
||||
ALLOWED_HOSTS = ['pyrigs.nottinghamtec.co.uk', 'rigs.nottinghamtec.co.uk', 'pyrigs.herokuapp.com']
|
||||
|
||||
if STAGING:
|
||||
ALLOWED_HOSTS.append('.herokuapp.com')
|
||||
|
||||
if DEBUG:
|
||||
ALLOWED_HOSTS.append('localhost')
|
||||
ALLOWED_HOSTS.append('example.com')
|
||||
ALLOWED_HOSTS.append('127.0.0.1')
|
||||
|
||||
SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
|
||||
if not DEBUG:
|
||||
SECURE_SSL_REDIRECT = True # Redirect all http requests to https
|
||||
SECURE_SSL_REDIRECT = True # Redirect all http requests to https
|
||||
|
||||
INTERNAL_IPS = ['127.0.0.1']
|
||||
|
||||
@@ -40,7 +47,6 @@ ADMINS = (
|
||||
('Tom Price', 'tomtom5152@gmail.com')
|
||||
)
|
||||
|
||||
|
||||
# Application definition
|
||||
|
||||
INSTALLED_APPS = (
|
||||
@@ -60,15 +66,15 @@ INSTALLED_APPS = (
|
||||
'raven.contrib.django.raven_compat',
|
||||
)
|
||||
|
||||
MIDDLEWARE_CLASSES = (
|
||||
MIDDLEWARE = (
|
||||
'raven.contrib.django.raven_compat.middleware.SentryResponseErrorIdMiddleware',
|
||||
'django.middleware.security.SecurityMiddleware',
|
||||
'debug_toolbar.middleware.DebugToolbarMiddleware',
|
||||
'reversion.middleware.RevisionMiddleware',
|
||||
'django.contrib.sessions.middleware.SessionMiddleware',
|
||||
'django.middleware.common.CommonMiddleware',
|
||||
'django.middleware.csrf.CsrfViewMiddleware',
|
||||
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
||||
'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
|
||||
'django.contrib.messages.middleware.MessageMiddleware',
|
||||
'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
||||
)
|
||||
@@ -77,7 +83,6 @@ ROOT_URLCONF = 'PyRIGS.urls'
|
||||
|
||||
WSGI_APPLICATION = 'PyRIGS.wsgi.application'
|
||||
|
||||
|
||||
# Database
|
||||
# https://docs.djangoproject.com/en/1.7/ref/settings/#databases
|
||||
DATABASES = {
|
||||
@@ -89,9 +94,10 @@ DATABASES = {
|
||||
|
||||
if not DEBUG:
|
||||
import dj_database_url
|
||||
|
||||
DATABASES['default'] = dj_database_url.config()
|
||||
|
||||
# Logging
|
||||
# Logging
|
||||
LOGGING = {
|
||||
'version': 1,
|
||||
'disable_existing_loggers': False,
|
||||
@@ -119,12 +125,12 @@ LOGGING = {
|
||||
'mail_admins': {
|
||||
'class': 'django.utils.log.AdminEmailHandler',
|
||||
'level': 'ERROR',
|
||||
# But the emails are plain text by default - HTML is nicer
|
||||
# But the emails are plain text by default - HTML is nicer
|
||||
'include_html': True,
|
||||
},
|
||||
},
|
||||
'loggers': {
|
||||
# Again, default Django configuration to email unhandled exceptions
|
||||
# Again, default Django configuration to email unhandled exceptions
|
||||
'django.request': {
|
||||
'handlers': ['mail_admins'],
|
||||
'level': 'ERROR',
|
||||
@@ -139,8 +145,6 @@ LOGGING = {
|
||||
}
|
||||
}
|
||||
|
||||
import raven
|
||||
|
||||
RAVEN_CONFIG = {
|
||||
'dsn': os.environ.get('RAVEN_DSN'),
|
||||
# If you are using git, you can also automatically configure the
|
||||
@@ -152,14 +156,14 @@ RAVEN_CONFIG = {
|
||||
AUTH_USER_MODEL = 'RIGS.Profile'
|
||||
|
||||
LOGIN_REDIRECT_URL = '/'
|
||||
LOGIN_URL = '/user/login'
|
||||
LOGOUT_URL = '/user/logout'
|
||||
LOGIN_URL = '/user/login/'
|
||||
LOGOUT_URL = '/user/logout/'
|
||||
|
||||
ACCOUNT_ACTIVATION_DAYS = 7
|
||||
|
||||
# reCAPTCHA settings
|
||||
RECAPTCHA_PUBLIC_KEY = os.environ.get('RECAPTCHA_PUBLIC_KEY', None)
|
||||
RECAPTCHA_PRIVATE_KEY = os.environ.get('RECAPTCHA_PRIVATE_KEY', None)
|
||||
RECAPTCHA_PUBLIC_KEY = os.environ.get('RECAPTCHA_PUBLIC_KEY', "6LeIxAcTAAAAAJcZVRqyHh71UMIEGNQ_MXjiZKhI") # If not set, use development key
|
||||
RECAPTCHA_PRIVATE_KEY = os.environ.get('RECAPTCHA_PRIVATE_KEY', "6LeIxAcTAAAAAGG-vFI1TnRWxMZNFuojJ4WifJWe") # If not set, use development key
|
||||
NOCAPTCHA = True
|
||||
|
||||
# Email
|
||||
@@ -167,7 +171,7 @@ EMAILER_TEST = False
|
||||
if not DEBUG or EMAILER_TEST:
|
||||
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
|
||||
EMAIL_HOST = os.environ.get('EMAIL_HOST')
|
||||
EMAIL_PORT = int(os.environ.get('EMAIL_PORT'))
|
||||
EMAIL_PORT = int(os.environ.get('EMAIL_PORT', 25))
|
||||
EMAIL_HOST_USER = os.environ.get('EMAIL_HOST_USER')
|
||||
EMAIL_HOST_PASSWORD = os.environ.get('EMAIL_HOST_PASSWORD')
|
||||
EMAIL_USE_TLS = bool(int(os.environ.get('EMAIL_USE_TLS', 0)))
|
||||
@@ -191,19 +195,7 @@ USE_L10N = True
|
||||
|
||||
USE_TZ = True
|
||||
|
||||
DATETIME_INPUT_FORMATS = ('%Y-%m-%dT%H:%M','%Y-%m-%dT%H:%M:%S')
|
||||
|
||||
TEMPLATE_CONTEXT_PROCESSORS = (
|
||||
"django.contrib.auth.context_processors.auth",
|
||||
"django.core.context_processors.debug",
|
||||
"django.core.context_processors.i18n",
|
||||
"django.core.context_processors.media",
|
||||
"django.core.context_processors.static",
|
||||
"django.core.context_processors.tz",
|
||||
"django.core.context_processors.request",
|
||||
"django.contrib.messages.context_processors.messages",
|
||||
)
|
||||
|
||||
DATETIME_INPUT_FORMATS = ('%Y-%m-%dT%H:%M', '%Y-%m-%dT%H:%M:%S')
|
||||
|
||||
# Static files (CSS, JavaScript, Images)
|
||||
# https://docs.djangoproject.com/en/1.7/howto/static-files/
|
||||
@@ -214,10 +206,30 @@ STATIC_DIRS = (
|
||||
os.path.join(BASE_DIR, 'static/')
|
||||
)
|
||||
|
||||
TEMPLATE_DIRS = (
|
||||
os.path.join(BASE_DIR, 'templates'),
|
||||
)
|
||||
TEMPLATES = [
|
||||
{
|
||||
'BACKEND': 'django.template.backends.django.DjangoTemplates',
|
||||
'DIRS': [
|
||||
os.path.join(BASE_DIR, 'templates'),
|
||||
],
|
||||
'APP_DIRS': True,
|
||||
'OPTIONS': {
|
||||
'context_processors': [
|
||||
"django.contrib.auth.context_processors.auth",
|
||||
"django.template.context_processors.debug",
|
||||
"django.template.context_processors.i18n",
|
||||
"django.template.context_processors.media",
|
||||
"django.template.context_processors.static",
|
||||
"django.template.context_processors.tz",
|
||||
"django.template.context_processors.request",
|
||||
"django.contrib.messages.context_processors.messages",
|
||||
],
|
||||
'debug': DEBUG
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
USE_GRAVATAR=True
|
||||
USE_GRAVATAR = True
|
||||
|
||||
TERMS_OF_HIRE_URL = "http://www.nottinghamtec.co.uk/terms.pdf"
|
||||
AUTHORISATION_NOTIFICATION_ADDRESS = 'productions@nottinghamtec.co.uk'
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
from django.conf.urls import patterns, include, url
|
||||
from django.conf.urls import include, url
|
||||
from django.contrib import admin
|
||||
from django.contrib.staticfiles.urls import staticfiles_urlpatterns
|
||||
from django.conf import settings
|
||||
@@ -6,19 +6,24 @@ from registration.backends.default.views import RegistrationView
|
||||
import RIGS
|
||||
from RIGS import regbackend
|
||||
|
||||
urlpatterns = patterns('',
|
||||
urlpatterns = [
|
||||
# Examples:
|
||||
# url(r'^$', 'PyRIGS.views.home', name='home'),
|
||||
# url(r'^blog/', include('blog.urls')),
|
||||
|
||||
url(r'^', include('RIGS.urls')),
|
||||
url('^user/register/$', RegistrationView.as_view(form_class=RIGS.forms.ProfileRegistrationFormUniqueEmail),
|
||||
url('^user/register/$', RegistrationView.as_view(form_class=RIGS.forms.ProfileRegistrationFormUniqueEmail),
|
||||
name="registration_register"),
|
||||
url('^user/', include('django.contrib.auth.urls')),
|
||||
url('^user/', include('registration.backends.default.urls')),
|
||||
|
||||
url(r'^admin/', include(admin.site.urls)),
|
||||
)
|
||||
url(r'^admin/', admin.site.urls),
|
||||
]
|
||||
|
||||
if settings.DEBUG:
|
||||
urlpatterns += staticfiles_urlpatterns()
|
||||
urlpatterns += staticfiles_urlpatterns()
|
||||
|
||||
import debug_toolbar
|
||||
urlpatterns = [
|
||||
url(r'^__debug__/', include(debug_toolbar.urls)),
|
||||
] + urlpatterns
|
||||
|
||||
@@ -9,7 +9,7 @@ https://docs.djangoproject.com/en/1.7/howto/deployment/wsgi/
|
||||
import os
|
||||
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "PyRIGS.settings")
|
||||
|
||||
from django.core.wsgi import get_wsgi_application
|
||||
from dj_static import Cling
|
||||
from django.core.wsgi import get_wsgi_application # noqa
|
||||
from dj_static import Cling # noqa
|
||||
|
||||
application = Cling(get_wsgi_application())
|
||||
|
||||
15
README.md
@@ -1,6 +1,8 @@
|
||||
# TEC PA & Lighting - PyRIGS #
|
||||
[](https://travis-ci.org/nottinghamtec/PyRIGS)
|
||||
[](https://coveralls.io/github/nottinghamtec/PyRIGS?branch=develop)
|
||||
[](https://gemnasium.com/github.com/nottinghamtec/PyRIGS)
|
||||
|
||||
|
||||
Welcome to TEC PA & Lightings PyRIGS program. This is a reimplementation of the existing Rig Information Gathering System (RIGS) that was developed using Ruby on Rails.
|
||||
|
||||
@@ -93,5 +95,18 @@ python manage.py generateSampleData
|
||||
|keyholder|keyholder|
|
||||
|basic |basic |
|
||||
|
||||
### Testing ###
|
||||
Tests are contained in 3 files. `RIGS/test_models.py` contains tests for logic within the data models. `RIGS/test_unit.py` contains "Live server" tests, using raw web requests. `RIGS/test_integration.py` contains user interface tests which take control of a web browser. For automated Travis tests, we use [Sauce Labs](https://saucelabs.com). When debugging locally, ensure that you have the latest version of Google Chrome installed, then install [chromedriver](https://sites.google.com/a/chromium.org/chromedriver/) and ensure it is on the `PATH`.
|
||||
|
||||
You can run the entire test suite, or you can run specific sections individually. For example, in order of specificity:
|
||||
|
||||
```
|
||||
python manage.py test
|
||||
python manage.py test RIGS.test_models
|
||||
python manage.py test RIGS.test_models.EventTestCase
|
||||
python manage.py test RIGS.test_models.EventTestCase.test_current_events
|
||||
|
||||
```
|
||||
|
||||
### Committing, pushing and testing ###
|
||||
Feel free to commit as you wish, on your own branch. On my branch (master for development) do not commit code that you either know doesn't work or don't know works. If you must commit this code, please make sure you say in the commit message that it isn't working, and if you can why it isn't working. If and only if you absolutely must push, then please don't leave it as the HEAD for too long, it's not much to ask but when you are done just make sure you haven't broken the HEAD for the next person.
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
default_app_config = 'RIGS.apps.RIGSAppConfig'
|
||||
|
||||
@@ -2,7 +2,7 @@ from django.contrib import admin
|
||||
from RIGS import models, forms
|
||||
from django.contrib.auth.admin import UserAdmin
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
import reversion
|
||||
from reversion.admin import VersionAdmin
|
||||
|
||||
from django.contrib.admin import helpers
|
||||
from django.template.response import TemplateResponse
|
||||
@@ -12,10 +12,12 @@ from django.core.exceptions import ObjectDoesNotExist
|
||||
from django.db.models import Count
|
||||
from django.forms import ModelForm
|
||||
|
||||
from reversion import revisions as reversion
|
||||
|
||||
# Register your models here.
|
||||
admin.site.register(models.VatRate, reversion.VersionAdmin)
|
||||
admin.site.register(models.Event, reversion.VersionAdmin)
|
||||
admin.site.register(models.EventItem, reversion.VersionAdmin)
|
||||
admin.site.register(models.VatRate, VersionAdmin)
|
||||
admin.site.register(models.Event, VersionAdmin)
|
||||
admin.site.register(models.EventItem, VersionAdmin)
|
||||
admin.site.register(models.Invoice)
|
||||
admin.site.register(models.Payment)
|
||||
|
||||
@@ -41,7 +43,7 @@ class ProfileAdmin(UserAdmin):
|
||||
add_form = forms.ProfileCreationForm
|
||||
|
||||
|
||||
class AssociateAdmin(reversion.VersionAdmin):
|
||||
class AssociateAdmin(VersionAdmin):
|
||||
list_display = ('id', 'name', 'number_of_events')
|
||||
search_fields = ['id', 'name']
|
||||
list_display_links = ['id', 'name']
|
||||
@@ -93,8 +95,7 @@ class AssociateAdmin(reversion.VersionAdmin):
|
||||
'action_checkbox_name': helpers.ACTION_CHECKBOX_NAME,
|
||||
'forms': forms
|
||||
}
|
||||
return TemplateResponse(request, 'RIGS/admin_associate_merge.html', context,
|
||||
current_app=self.admin_site.name)
|
||||
return TemplateResponse(request, 'RIGS/admin_associate_merge.html', context)
|
||||
|
||||
|
||||
@admin.register(models.Person)
|
||||
|
||||
8
RIGS/apps.py
Normal file
@@ -0,0 +1,8 @@
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class RIGSAppConfig(AppConfig):
|
||||
name = 'RIGS'
|
||||
|
||||
def ready(self):
|
||||
import RIGS.signals
|
||||
@@ -1,9 +1,8 @@
|
||||
import cStringIO as StringIO
|
||||
import datetime
|
||||
import re
|
||||
|
||||
from django.contrib import messages
|
||||
from django.core.urlresolvers import reverse_lazy
|
||||
from django.urls import reverse_lazy
|
||||
from django.http import Http404, HttpResponseRedirect
|
||||
from django.http import HttpResponse
|
||||
from django.shortcuts import get_object_or_404
|
||||
@@ -15,6 +14,9 @@ from z3c.rml import rml2pdf
|
||||
|
||||
from RIGS import models
|
||||
|
||||
from django import forms
|
||||
forms.DateField.widget = forms.DateInput(attrs={'type': 'date'})
|
||||
|
||||
|
||||
class InvoiceIndex(generic.ListView):
|
||||
model = models.Invoice
|
||||
@@ -55,8 +57,8 @@ class InvoicePrint(generic.View):
|
||||
invoice = get_object_or_404(models.Invoice, pk=pk)
|
||||
object = invoice.event
|
||||
template = get_template('RIGS/event_print.xml')
|
||||
copies = ('TEC', 'Client')
|
||||
context = RequestContext(request, {
|
||||
|
||||
context = {
|
||||
'object': object,
|
||||
'fonts': {
|
||||
'opensans': {
|
||||
@@ -66,10 +68,9 @@ class InvoicePrint(generic.View):
|
||||
},
|
||||
'invoice': invoice,
|
||||
'current_user': request.user,
|
||||
})
|
||||
}
|
||||
|
||||
rml = template.render(context)
|
||||
buffer = StringIO.StringIO()
|
||||
|
||||
buffer = rml2pdf.parseString(rml)
|
||||
|
||||
@@ -94,6 +95,7 @@ class InvoiceVoid(generic.View):
|
||||
return HttpResponseRedirect(reverse_lazy('invoice_list'))
|
||||
return HttpResponseRedirect(reverse_lazy('invoice_detail', kwargs={'pk': object.pk}))
|
||||
|
||||
|
||||
class InvoiceDelete(generic.DeleteView):
|
||||
model = models.Invoice
|
||||
|
||||
@@ -114,6 +116,7 @@ class InvoiceDelete(generic.DeleteView):
|
||||
def get_success_url(self):
|
||||
return self.request.POST.get('next')
|
||||
|
||||
|
||||
class InvoiceArchive(generic.ListView):
|
||||
model = models.Invoice
|
||||
template_name = 'RIGS/invoice_list_archive.html'
|
||||
@@ -142,11 +145,11 @@ class InvoiceWaiting(generic.ListView):
|
||||
events = self.model.objects.filter(
|
||||
(
|
||||
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(invoice__isnull=True) # Has not already been invoiced
|
||||
& Q(is_rig=True) # Is a rig (not non-rig)
|
||||
|
||||
).order_by('start_date') \
|
||||
Q(end_date__lte=datetime.date.today()) # Has end date, finishes before
|
||||
) & Q(invoice__isnull=True) & # Has not already been invoiced
|
||||
Q(is_rig=True) # Is a rig (not non-rig)
|
||||
|
||||
).order_by('start_date') \
|
||||
.select_related('person',
|
||||
'organisation',
|
||||
'venue', 'mic') \
|
||||
@@ -175,7 +178,7 @@ class PaymentCreate(generic.CreateView):
|
||||
def get_initial(self):
|
||||
initial = super(generic.CreateView, self).get_initial()
|
||||
invoicepk = self.request.GET.get('invoice', self.request.POST.get('invoice', None))
|
||||
if invoicepk == None:
|
||||
if invoicepk is None:
|
||||
raise Http404()
|
||||
invoice = get_object_or_404(models.Invoice, pk=invoicepk)
|
||||
initial.update({'invoice': invoice})
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
__author__ = 'Ghost'
|
||||
from django import forms
|
||||
from django.utils import formats
|
||||
from django.conf import settings
|
||||
@@ -10,8 +9,14 @@ import simplejson
|
||||
|
||||
from RIGS import models
|
||||
|
||||
# Override the django form defaults to use the HTML date/time/datetime UI elements
|
||||
forms.DateField.widget = forms.DateInput(attrs={'type': 'date'})
|
||||
forms.TimeField.widget = forms.TextInput(attrs={'type': 'time'})
|
||||
forms.DateTimeField.widget = forms.DateTimeInput(attrs={'type': 'datetime-local'})
|
||||
|
||||
# Registration
|
||||
|
||||
|
||||
class ProfileRegistrationFormUniqueEmail(RegistrationFormUniqueEmail):
|
||||
captcha = ReCaptchaField()
|
||||
|
||||
@@ -45,7 +50,7 @@ class ProfileChangeForm(UserChangeForm):
|
||||
|
||||
# Events Shit
|
||||
class EventForm(forms.ModelForm):
|
||||
datetime_input_formats = formats.get_format_lazy("DATETIME_INPUT_FORMATS") + settings.DATETIME_INPUT_FORMATS
|
||||
datetime_input_formats = formats.get_format_lazy("DATETIME_INPUT_FORMATS") + list(settings.DATETIME_INPUT_FORMATS)
|
||||
meet_at = forms.DateTimeField(input_formats=datetime_input_formats, required=False)
|
||||
access_at = forms.DateTimeField(input_formats=datetime_input_formats, required=False)
|
||||
|
||||
@@ -140,4 +145,32 @@ class EventForm(forms.ModelForm):
|
||||
fields = ['is_rig', 'name', 'venue', 'start_time', 'end_date', 'start_date',
|
||||
'end_time', 'meet_at', 'access_at', 'description', 'notes', 'mic',
|
||||
'person', 'organisation', 'dry_hire', 'checked_in_by', 'status',
|
||||
'collector', 'purchase_order']
|
||||
'purchase_order', 'collector']
|
||||
|
||||
|
||||
class BaseClientEventAuthorisationForm(forms.ModelForm):
|
||||
tos = forms.BooleanField(required=True, label="Terms of hire")
|
||||
name = forms.CharField(label="Your Name")
|
||||
|
||||
def clean(self):
|
||||
if self.cleaned_data.get('amount') != self.instance.event.total:
|
||||
self.add_error('amount', 'The amount authorised must equal the total for the event (inc VAT).')
|
||||
return super(BaseClientEventAuthorisationForm, self).clean()
|
||||
|
||||
class Meta:
|
||||
abstract = True
|
||||
|
||||
|
||||
class InternalClientEventAuthorisationForm(BaseClientEventAuthorisationForm):
|
||||
def __init__(self, **kwargs):
|
||||
super(InternalClientEventAuthorisationForm, self).__init__(**kwargs)
|
||||
self.fields['uni_id'].required = True
|
||||
self.fields['account_code'].required = True
|
||||
|
||||
class Meta:
|
||||
model = models.EventAuthorisation
|
||||
fields = ('tos', 'name', 'amount', 'uni_id', 'account_code')
|
||||
|
||||
|
||||
class EventAuthorisationRequestForm(forms.Form):
|
||||
email = forms.EmailField(required=True, label='Authoriser Email')
|
||||
|
||||
67
RIGS/ical.py
@@ -1,17 +1,19 @@
|
||||
from RIGS import models, forms
|
||||
from django_ical.views import ICalFeed
|
||||
from django.db.models import Q
|
||||
from django.core.urlresolvers import reverse_lazy, reverse, NoReverseMatch
|
||||
from django.urls import reverse_lazy, reverse, NoReverseMatch
|
||||
from django.utils import timezone
|
||||
from django.conf import settings
|
||||
|
||||
import datetime, pytz
|
||||
import datetime
|
||||
import pytz
|
||||
|
||||
|
||||
class CalendarICS(ICalFeed):
|
||||
"""
|
||||
A simple event calender
|
||||
"""
|
||||
#Metadata which is passed on to clients
|
||||
# Metadata which is passed on to clients
|
||||
product_id = 'RIGS'
|
||||
title = 'RIGS Calendar'
|
||||
timezone = settings.TIME_ZONE
|
||||
@@ -27,39 +29,39 @@ class CalendarICS(ICalFeed):
|
||||
def get_object(self, request, *args, **kwargs):
|
||||
params = {}
|
||||
|
||||
params['dry-hire'] = request.GET.get('dry-hire','true') == 'true'
|
||||
params['non-rig'] = request.GET.get('non-rig','true') == 'true'
|
||||
params['rig'] = request.GET.get('rig','true') == 'true'
|
||||
params['dry-hire'] = request.GET.get('dry-hire', 'true') == 'true'
|
||||
params['non-rig'] = request.GET.get('non-rig', 'true') == 'true'
|
||||
params['rig'] = request.GET.get('rig', 'true') == 'true'
|
||||
|
||||
params['cancelled'] = request.GET.get('cancelled','false') == 'true'
|
||||
params['provisional'] = request.GET.get('provisional','true') == 'true'
|
||||
params['confirmed'] = request.GET.get('confirmed','true') == 'true'
|
||||
params['cancelled'] = request.GET.get('cancelled', 'false') == 'true'
|
||||
params['provisional'] = request.GET.get('provisional', 'true') == 'true'
|
||||
params['confirmed'] = request.GET.get('confirmed', 'true') == 'true'
|
||||
|
||||
return params
|
||||
|
||||
def description(self,params):
|
||||
def description(self, params):
|
||||
desc = "Calendar generated by RIGS system. This includes event types: " + ('Rig, ' if params['rig'] else '') + ('Non-rig, ' if params['non-rig'] else '') + ('Dry Hire ' if params['dry-hire'] else '') + '\n'
|
||||
desc = desc + "Includes events with status: " + ('Cancelled, ' if params['cancelled'] else '') + ('Provisional, ' if params['provisional'] else '') + ('Confirmed/Booked, ' if params['confirmed'] else '')
|
||||
|
||||
return desc
|
||||
|
||||
def items(self, params):
|
||||
#include events from up to 1 year ago
|
||||
# include events from up to 1 year ago
|
||||
start = datetime.datetime.now() - datetime.timedelta(days=365)
|
||||
filter = Q(start_date__gte=start)
|
||||
|
||||
typeFilters = Q(pk=None) #Need something that is false for every entry
|
||||
typeFilters = Q(pk=None) # Need something that is false for every entry
|
||||
|
||||
if params['dry-hire']:
|
||||
typeFilters = typeFilters | Q(dry_hire=True, is_rig=True)
|
||||
|
||||
if params['non-rig']:
|
||||
typeFilters = typeFilters | Q(is_rig=False)
|
||||
|
||||
|
||||
if params['rig']:
|
||||
typeFilters = typeFilters | Q(is_rig=True, dry_hire=False)
|
||||
|
||||
statusFilters = Q(pk=None) #Need something that is false for every entry
|
||||
|
||||
statusFilters = Q(pk=None) # Need something that is false for every entry
|
||||
|
||||
if params['cancelled']:
|
||||
statusFilters = statusFilters | Q(status=models.Event.CANCELLED)
|
||||
@@ -87,9 +89,9 @@ class CalendarICS(ICalFeed):
|
||||
|
||||
# Add the rig name
|
||||
title += item.name
|
||||
|
||||
|
||||
# Add the status
|
||||
title += ' ('+str(item.get_status_display())+')'
|
||||
title += ' (' + str(item.get_status_display()) + ')'
|
||||
|
||||
return title
|
||||
|
||||
@@ -97,12 +99,12 @@ class CalendarICS(ICalFeed):
|
||||
return item.earliest_time
|
||||
|
||||
def item_end_datetime(self, item):
|
||||
if type(item.latest_time) is datetime.date: # Ical end_datetime is non-inclusive, so add a day
|
||||
if type(item.latest_time) == datetime.date: # Ical end_datetime is non-inclusive, so add a day
|
||||
return item.latest_time + datetime.timedelta(days=1)
|
||||
|
||||
return item.latest_time
|
||||
|
||||
def item_location(self,item):
|
||||
def item_location(self, item):
|
||||
return item.venue
|
||||
|
||||
def item_description(self, item):
|
||||
@@ -111,34 +113,33 @@ class CalendarICS(ICalFeed):
|
||||
|
||||
tz = pytz.timezone(self.timezone)
|
||||
|
||||
desc = 'Rig ID = '+str(item.pk)+'\n'
|
||||
desc = 'Rig ID = ' + str(item.pk) + '\n'
|
||||
desc += 'Event = ' + item.name + '\n'
|
||||
desc += 'Venue = ' + (item.venue.name if item.venue else '---') + '\n'
|
||||
if item.is_rig and item.person:
|
||||
desc += 'Client = ' + item.person.name + ( (' for '+item.organisation.name) if item.organisation else '') + '\n'
|
||||
desc += 'Client = ' + item.person.name + ((' for ' + item.organisation.name) if item.organisation else '') + '\n'
|
||||
desc += 'Status = ' + str(item.get_status_display()) + '\n'
|
||||
desc += 'MIC = ' + (item.mic.name if item.mic else '---') + '\n'
|
||||
|
||||
|
||||
|
||||
desc += '\n'
|
||||
if item.meet_at:
|
||||
desc += 'Crew Meet = ' + (item.meet_at.astimezone(tz).strftime('%Y-%m-%d %H:%M') if item.meet_at else '---') + '\n'
|
||||
if item.access_at:
|
||||
desc += 'Access At = ' + (item.access_at.astimezone(tz).strftime('%Y-%m-%d %H:%M') if item.access_at else '---') + '\n'
|
||||
if item.start_date:
|
||||
desc += 'Event Start = ' + item.start_date.strftime('%Y-%m-%d') + ((' '+item.start_time.strftime('%H:%M')) if item.has_start_time else '') + '\n'
|
||||
desc += 'Event Start = ' + item.start_date.strftime('%Y-%m-%d') + ((' ' + item.start_time.strftime('%H:%M')) if item.has_start_time else '') + '\n'
|
||||
if item.end_date:
|
||||
desc += 'Event End = ' + item.end_date.strftime('%Y-%m-%d') + ((' '+item.end_time.strftime('%H:%M')) if item.has_end_time else '') + '\n'
|
||||
desc += 'Event End = ' + item.end_date.strftime('%Y-%m-%d') + ((' ' + item.end_time.strftime('%H:%M')) if item.has_end_time else '') + '\n'
|
||||
|
||||
desc += '\n'
|
||||
if item.description:
|
||||
desc += 'Event Description:\n'+item.description+'\n\n'
|
||||
desc += 'Event Description:\n' + item.description + '\n\n'
|
||||
# if item.notes: // Need to add proper keyholder checks before this gets put back
|
||||
# desc += 'Notes:\n'+item.notes+'\n\n'
|
||||
# desc += 'Notes:\n'+item.notes+'\n\n'
|
||||
|
||||
base_url = "https://rigs.nottinghamtec.co.uk"
|
||||
desc += 'URL = ' + base_url + str(item.get_absolute_url())
|
||||
|
||||
base_url = "http://rigs.nottinghamtec.co.uk"
|
||||
desc += 'URL = '+base_url+str(item.get_absolute_url())
|
||||
|
||||
return desc
|
||||
|
||||
def item_link(self, item):
|
||||
@@ -149,8 +150,8 @@ class CalendarICS(ICalFeed):
|
||||
# def item_created(self, item): #TODO - Implement created date-time (using django-reversion?) - not really necessary though
|
||||
# return ''
|
||||
|
||||
def item_updated(self, item): # some ical clients will display this
|
||||
def item_updated(self, item): # some ical clients will display this
|
||||
return item.last_edited_at
|
||||
|
||||
def item_guid(self, item): # use the rig-id as the ical unique event identifier
|
||||
return item.pk
|
||||
def item_guid(self, item): # use the rig-id as the ical unique event identifier
|
||||
return item.pk
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
from django.core.management.base import BaseCommand, CommandError
|
||||
from django.contrib.auth.models import Group, Permission
|
||||
from django.db import transaction
|
||||
import reversion
|
||||
from reversion import revisions as reversion
|
||||
|
||||
import datetime
|
||||
import random
|
||||
|
||||
from RIGS import models
|
||||
|
||||
|
||||
class Command(BaseCommand):
|
||||
help = 'Adds sample data to use for testing'
|
||||
can_import_settings = True
|
||||
@@ -19,32 +21,32 @@ class Command(BaseCommand):
|
||||
keyholder_group = None
|
||||
finance_group = None
|
||||
|
||||
|
||||
def handle(self, *args, **options):
|
||||
from django.conf import settings
|
||||
|
||||
if not (settings.DEBUG or settings.STAGING):
|
||||
raise CommandError('You cannot run this command in production')
|
||||
|
||||
random.seed('Some object to seed the random number generator') # otherwise it is done by time, which could lead to inconsistant tests
|
||||
random.seed('Some object to seed the random number generator') # otherwise it is done by time, which could lead to inconsistant tests
|
||||
|
||||
with transaction.atomic():
|
||||
models.VatRate.objects.create(start_at='2014-03-05',rate=0.20,comment='test1')
|
||||
models.VatRate.objects.create(start_at='2014-03-05', rate=0.20, comment='test1')
|
||||
|
||||
self.setupGenericProfiles()
|
||||
|
||||
self.setupPeople()
|
||||
self.setupOrganisations()
|
||||
self.setupVenues()
|
||||
|
||||
|
||||
self.setupGroups()
|
||||
|
||||
|
||||
self.setupEvents()
|
||||
|
||||
self.setupUsefulProfiles()
|
||||
|
||||
def setupPeople(self):
|
||||
names = ["Regulus Black","Sirius Black","Lavender Brown","Cho Chang","Vincent Crabbe","Vincent Crabbe","Bartemius Crouch","Fleur Delacour","Cedric Diggory","Alberforth Dumbledore","Albus Dumbledore","Dudley Dursley","Petunia Dursley","Vernon Dursley","Argus Filch","Seamus Finnigan","Nicolas Flamel","Cornelius Fudge","Goyle","Gregory Goyle","Hermione Granger","Rubeus Hagrid","Igor Karkaroff","Viktor Krum","Bellatrix Lestrange","Alice Longbottom","Frank Longbottom","Neville Longbottom","Luna Lovegood","Xenophilius Lovegood","Remus Lupin","Draco Malfoy","Lucius Malfoy","Narcissa Malfoy","Olympe Maxime","Minerva McGonagall","Mad-Eye Moody","Peter Pettigrew","Harry Potter","James Potter","Lily Potter","Quirinus Quirrell","Tom Riddle","Mary Riddle","Lord Voldemort","Rita Skeeter","Severus Snape","Nymphadora Tonks","Dolores Janes Umbridge","Arthur Weasley","Bill Weasley","Charlie Weasley","Fred Weasley","George Weasley","Ginny Weasley","Molly Weasley","Percy Weasley","Ron Weasley","Dobby","Fluffy","Hedwig","Moaning Myrtle","Aragog","Grawp"]
|
||||
names = ["Regulus Black", "Sirius Black", "Lavender Brown", "Cho Chang", "Vincent Crabbe", "Vincent Crabbe", "Bartemius Crouch", "Fleur Delacour", "Cedric Diggory", "Alberforth Dumbledore", "Albus Dumbledore", "Dudley Dursley", "Petunia Dursley", "Vernon Dursley", "Argus Filch", "Seamus Finnigan", "Nicolas Flamel", "Cornelius Fudge", "Goyle", "Gregory Goyle", "Hermione Granger", "Rubeus Hagrid", "Igor Karkaroff", "Viktor Krum", "Bellatrix Lestrange", "Alice Longbottom", "Frank Longbottom", "Neville Longbottom", "Luna Lovegood", "Xenophilius Lovegood", # noqa
|
||||
"Remus Lupin", "Draco Malfoy", "Lucius Malfoy", "Narcissa Malfoy", "Olympe Maxime", "Minerva McGonagall", "Mad-Eye Moody", "Peter Pettigrew", "Harry Potter", "James Potter", "Lily Potter", "Quirinus Quirrell", "Tom Riddle", "Mary Riddle", "Lord Voldemort", "Rita Skeeter", "Severus Snape", "Nymphadora Tonks", "Dolores Janes Umbridge", "Arthur Weasley", "Bill Weasley", "Charlie Weasley", "Fred Weasley", "George Weasley", "Ginny Weasley", "Molly Weasley", "Percy Weasley", "Ron Weasley", "Dobby", "Fluffy", "Hedwig", "Moaning Myrtle", "Aragog", "Grawp"] # noqa
|
||||
for i, name in enumerate(names):
|
||||
with reversion.create_revision():
|
||||
reversion.set_user(random.choice(self.profiles))
|
||||
@@ -66,7 +68,8 @@ class Command(BaseCommand):
|
||||
self.people.append(newPerson)
|
||||
|
||||
def setupOrganisations(self):
|
||||
names = ["Acme, inc.","Widget Corp","123 Warehousing","Demo Company","Smith and Co.","Foo Bars","ABC Telecom","Fake Brothers","QWERTY Logistics","Demo, inc.","Sample Company","Sample, inc","Acme Corp","Allied Biscuit","Ankh-Sto Associates","Extensive Enterprise","Galaxy Corp","Globo-Chem","Mr. Sparkle","Globex Corporation","LexCorp","LuthorCorp","North Central Positronics","Omni Consimer Products","Praxis Corporation","Sombra Corporation","Sto Plains Holdings","Tessier-Ashpool","Wayne Enterprises","Wentworth Industries","ZiffCorp","Bluth Company","Strickland Propane","Thatherton Fuels","Three Waters","Water and Power","Western Gas & Electric","Mammoth Pictures","Mooby Corp","Gringotts","Thrift Bank","Flowers By Irene","The Legitimate Businessmens Club","Osato Chemicals","Transworld Consortium","Universal Export","United Fried Chicken","Virtucon","Kumatsu Motors","Keedsler Motors","Powell Motors","Industrial Automation","Sirius Cybernetics Corporation","U.S. Robotics and Mechanical Men","Colonial Movers","Corellian Engineering Corporation","Incom Corporation","General Products","Leeding Engines Ltd.","Blammo","Input, Inc.","Mainway Toys","Videlectrix","Zevo Toys","Ajax","Axis Chemical Co.","Barrytron","Carrys Candles","Cogswell Cogs","Spacely Sprockets","General Forge and Foundry","Duff Brewing Company","Dunder Mifflin","General Services Corporation","Monarch Playing Card Co.","Krustyco","Initech","Roboto Industries","Primatech","Sonky Rubber Goods","St. Anky Beer","Stay Puft Corporation","Vandelay Industries","Wernham Hogg","Gadgetron","Burleigh and Stronginthearm","BLAND Corporation","Nordyne Defense Dynamics","Petrox Oil Company","Roxxon","McMahon and Tate","Sixty Second Avenue","Charles Townsend Agency","Spade and Archer","Megadodo Publications","Rouster and Sideways","C.H. Lavatory and Sons","Globo Gym American Corp","The New Firm","SpringShield","Compuglobalhypermeganet","Data Systems","Gizmonic Institute","Initrode","Taggart Transcontinental","Atlantic Northern","Niagular","Plow King","Big Kahuna Burger","Big T Burgers and Fries","Chez Quis","Chotchkies","The Frying Dutchman","Klimpys","The Krusty Krab","Monks Diner","Milliways","Minuteman Cafe","Taco Grande","Tip Top Cafe","Moes Tavern","Central Perk","Chasers"]
|
||||
names = ["Acme, inc.", "Widget Corp", "123 Warehousing", "Demo Company", "Smith and Co.", "Foo Bars", "ABC Telecom", "Fake Brothers", "QWERTY Logistics", "Demo, inc.", "Sample Company", "Sample, inc", "Acme Corp", "Allied Biscuit", "Ankh-Sto Associates", "Extensive Enterprise", "Galaxy Corp", "Globo-Chem", "Mr. Sparkle", "Globex Corporation", "LexCorp", "LuthorCorp", "North Central Positronics", "Omni Consimer Products", "Praxis Corporation", "Sombra Corporation", "Sto Plains Holdings", "Tessier-Ashpool", "Wayne Enterprises", "Wentworth Industries", "ZiffCorp", "Bluth Company", "Strickland Propane", "Thatherton Fuels", "Three Waters", "Water and Power", "Western Gas & Electric", "Mammoth Pictures", "Mooby Corp", "Gringotts", "Thrift Bank", "Flowers By Irene", "The Legitimate Businessmens Club", "Osato Chemicals", "Transworld Consortium", "Universal Export", "United Fried Chicken", "Virtucon", "Kumatsu Motors", "Keedsler Motors", "Powell Motors", "Industrial Automation", "Sirius Cybernetics Corporation", "U.S. Robotics and Mechanical Men", "Colonial Movers", "Corellian Engineering Corporation", "Incom Corporation", "General Products", "Leeding Engines Ltd.", "Blammo", # noqa
|
||||
"Input, Inc.", "Mainway Toys", "Videlectrix", "Zevo Toys", "Ajax", "Axis Chemical Co.", "Barrytron", "Carrys Candles", "Cogswell Cogs", "Spacely Sprockets", "General Forge and Foundry", "Duff Brewing Company", "Dunder Mifflin", "General Services Corporation", "Monarch Playing Card Co.", "Krustyco", "Initech", "Roboto Industries", "Primatech", "Sonky Rubber Goods", "St. Anky Beer", "Stay Puft Corporation", "Vandelay Industries", "Wernham Hogg", "Gadgetron", "Burleigh and Stronginthearm", "BLAND Corporation", "Nordyne Defense Dynamics", "Petrox Oil Company", "Roxxon", "McMahon and Tate", "Sixty Second Avenue", "Charles Townsend Agency", "Spade and Archer", "Megadodo Publications", "Rouster and Sideways", "C.H. Lavatory and Sons", "Globo Gym American Corp", "The New Firm", "SpringShield", "Compuglobalhypermeganet", "Data Systems", "Gizmonic Institute", "Initrode", "Taggart Transcontinental", "Atlantic Northern", "Niagular", "Plow King", "Big Kahuna Burger", "Big T Burgers and Fries", "Chez Quis", "Chotchkies", "The Frying Dutchman", "Klimpys", "The Krusty Krab", "Monks Diner", "Milliways", "Minuteman Cafe", "Taco Grande", "Tip Top Cafe", "Moes Tavern", "Central Perk", "Chasers"] # noqa
|
||||
for i, name in enumerate(names):
|
||||
with reversion.create_revision():
|
||||
reversion.set_user(random.choice(self.profiles))
|
||||
@@ -90,7 +93,8 @@ class Command(BaseCommand):
|
||||
self.organisations.append(newOrganisation)
|
||||
|
||||
def setupVenues(self):
|
||||
names = ["Bear Island","Crossroads Inn","Deepwood Motte","The Dreadfort","The Eyrie","Greywater Watch","The Iron Islands","Karhold","Moat Cailin","Oldstones","Raventree Hall","Riverlands","The Ruby Ford","Saltpans","Seagard","Torrhen's Square","The Trident","The Twins","The Vale of Arryn","The Whispering Wood","White Harbor","Winterfell","The Arbor","Ashemark","Brightwater Keep","Casterly Rock","Clegane's Keep","Dragonstone","Dorne","God's Eye","The Golden Tooth","Harrenhal","Highgarden","Horn Hill","Fingers","King's Landing","Lannisport","Oldtown","Rainswood","Storm's End","Summerhall","Sunspear","Tarth","Castle Black","Craster's Keep","Fist of the First Men","The Frostfangs","The Gift","The Skirling Pass","The Wall","Asshai","Astapor","Braavos","The Dothraki Sea","Lys","Meereen","Myr","Norvos","Pentos","Qarth","Qohor","The Red Waste","Tyrosh","Vaes Dothrak","Valyria","Village of the Lhazareen","Volantis","Yunkai"]
|
||||
names = ["Bear Island", "Crossroads Inn", "Deepwood Motte", "The Dreadfort", "The Eyrie", "Greywater Watch", "The Iron Islands", "Karhold", "Moat Cailin", "Oldstones", "Raventree Hall", "Riverlands", "The Ruby Ford", "Saltpans", "Seagard", "Torrhen's Square", "The Trident", "The Twins", "The Vale of Arryn", "The Whispering Wood", "White Harbor", "Winterfell", "The Arbor", "Ashemark", "Brightwater Keep", "Casterly Rock", "Clegane's Keep", "Dragonstone", "Dorne", "God's Eye", "The Golden Tooth", # noqa
|
||||
"Harrenhal", "Highgarden", "Horn Hill", "Fingers", "King's Landing", "Lannisport", "Oldtown", "Rainswood", "Storm's End", "Summerhall", "Sunspear", "Tarth", "Castle Black", "Craster's Keep", "Fist of the First Men", "The Frostfangs", "The Gift", "The Skirling Pass", "The Wall", "Asshai", "Astapor", "Braavos", "The Dothraki Sea", "Lys", "Meereen", "Myr", "Norvos", "Pentos", "Qarth", "Qohor", "The Red Waste", "Tyrosh", "Vaes Dothrak", "Valyria", "Village of the Lhazareen", "Volantis", "Yunkai"] # noqa
|
||||
for i, name in enumerate(names):
|
||||
with reversion.create_revision():
|
||||
reversion.set_user(random.choice(self.profiles))
|
||||
@@ -117,8 +121,8 @@ class Command(BaseCommand):
|
||||
self.keyholder_group = Group.objects.create(name='Keyholders')
|
||||
self.finance_group = Group.objects.create(name='Finance')
|
||||
|
||||
keyholderPerms = ["add_event","change_event","view_event","add_eventitem","change_eventitem","delete_eventitem","add_organisation","change_organisation","view_organisation","add_person","change_person","view_person","view_profile","add_venue","change_venue","view_venue"]
|
||||
financePerms = ["change_event","view_event","add_eventitem","change_eventitem","add_invoice","change_invoice","view_invoice","add_organisation","change_organisation","view_organisation","add_payment","change_payment","delete_payment","add_person","change_person","view_person"]
|
||||
keyholderPerms = ["add_event", "change_event", "view_event", "add_eventitem", "change_eventitem", "delete_eventitem", "add_organisation", "change_organisation", "view_organisation", "add_person", "change_person", "view_person", "view_profile", "add_venue", "change_venue", "view_venue"]
|
||||
financePerms = ["change_event", "view_event", "add_eventitem", "change_eventitem", "add_invoice", "change_invoice", "view_invoice", "add_organisation", "change_organisation", "view_organisation", "add_payment", "change_payment", "delete_payment", "add_person", "change_person", "view_person"]
|
||||
|
||||
for permId in keyholderPerms:
|
||||
self.keyholder_group.permissions.add(Permission.objects.get(codename=permId))
|
||||
@@ -127,11 +131,11 @@ class Command(BaseCommand):
|
||||
self.finance_group.permissions.add(Permission.objects.get(codename=permId))
|
||||
|
||||
def setupGenericProfiles(self):
|
||||
names = ["Clara Oswin Oswald","Rory Williams","Amy Pond","River Song","Martha Jones","Donna Noble","Jack Harkness","Mickey Smith","Rose Tyler"]
|
||||
names = ["Clara Oswin Oswald", "Rory Williams", "Amy Pond", "River Song", "Martha Jones", "Donna Noble", "Jack Harkness", "Mickey Smith", "Rose Tyler"]
|
||||
for i, name in enumerate(names):
|
||||
newProfile = models.Profile.objects.create(username=name.replace(" ",""), first_name=name.split(" ")[0], last_name=name.split(" ")[-1],
|
||||
email=name.replace(" ","")+"@example.com",
|
||||
initials="".join([ j[0].upper() for j in name.split() ]))
|
||||
newProfile = models.Profile.objects.create(username=name.replace(" ", ""), first_name=name.split(" ")[0], last_name=name.split(" ")[-1],
|
||||
email=name.replace(" ", "") + "@example.com",
|
||||
initials="".join([j[0].upper() for j in name.split()]))
|
||||
if i % 2 == 0:
|
||||
newProfile.phone = "01234 567894"
|
||||
|
||||
@@ -139,110 +143,110 @@ class Command(BaseCommand):
|
||||
self.profiles.append(newProfile)
|
||||
|
||||
def setupUsefulProfiles(self):
|
||||
superUser = models.Profile.objects.create(username="superuser", first_name="Super", last_name="User", initials="SU",
|
||||
email="superuser@example.com", is_superuser=True, is_active=True, is_staff=True)
|
||||
superUser = models.Profile.objects.create(username="superuser", first_name="Super", last_name="User", initials="SU",
|
||||
email="superuser@example.com", is_superuser=True, is_active=True, is_staff=True)
|
||||
superUser.set_password('superuser')
|
||||
superUser.save()
|
||||
|
||||
financeUser = models.Profile.objects.create(username="finance", first_name="Finance", last_name="User", initials="FU",
|
||||
email="financeuser@example.com", is_active=True)
|
||||
financeUser = models.Profile.objects.create(username="finance", first_name="Finance", last_name="User", initials="FU",
|
||||
email="financeuser@example.com", is_active=True)
|
||||
financeUser.groups.add(self.finance_group)
|
||||
financeUser.groups.add(self.keyholder_group)
|
||||
financeUser.set_password('finance')
|
||||
financeUser.save()
|
||||
|
||||
keyholderUser = models.Profile.objects.create(username="keyholder", first_name="Keyholder", last_name="User", initials="KU",
|
||||
email="keyholderuser@example.com", is_active=True)
|
||||
keyholderUser = models.Profile.objects.create(username="keyholder", first_name="Keyholder", last_name="User", initials="KU",
|
||||
email="keyholderuser@example.com", is_active=True)
|
||||
keyholderUser.groups.add(self.keyholder_group)
|
||||
keyholderUser.set_password('keyholder')
|
||||
keyholderUser.save()
|
||||
|
||||
basicUser = models.Profile.objects.create(username="basic", first_name="Basic", last_name="User", initials="BU",
|
||||
email="basicuser@example.com", is_active=True)
|
||||
basicUser = models.Profile.objects.create(username="basic", first_name="Basic", last_name="User", initials="BU",
|
||||
email="basicuser@example.com", is_active=True)
|
||||
basicUser.set_password('basic')
|
||||
basicUser.save()
|
||||
|
||||
def setupEvents(self):
|
||||
names = ["Outdoor Concert","Hall Open Mic Night","Festival","Weekend Event","Magic Show","Society Ball","Evening Show","Talent Show","Acoustic Evening","Hire of Things","SU Event","End of Term Show","Theatre Show","Outdoor Fun Day","Summer Carnival","Open Days","Magic Show","Awards Ceremony","Debating Event","Club Night","DJ Evening","Building Projection","Choir Concert"]
|
||||
descriptions = ["A brief desciption of the event","This event is boring","Probably wont happen","Warning: this has lots of kit"]
|
||||
notes = ["The client came into the office at some point","Who knows if this will happen", "Probably should check this event", "Maybe not happening", "Run away!"]
|
||||
names = ["Outdoor Concert", "Hall Open Mic Night", "Festival", "Weekend Event", "Magic Show", "Society Ball", "Evening Show", "Talent Show", "Acoustic Evening", "Hire of Things", "SU Event",
|
||||
"End of Term Show", "Theatre Show", "Outdoor Fun Day", "Summer Carnival", "Open Days", "Magic Show", "Awards Ceremony", "Debating Event", "Club Night", "DJ Evening", "Building Projection", "Choir Concert"]
|
||||
descriptions = ["A brief desciption of the event", "This event is boring", "Probably wont happen", "Warning: this has lots of kit"]
|
||||
notes = ["The client came into the office at some point", "Who knows if this will happen", "Probably should check this event", "Maybe not happening", "Run away!"]
|
||||
|
||||
itemOptions = [{'name': 'Speakers', 'description': 'Some really really big speakers \n these are very loud', 'quantity': 2, 'cost': 200.00},
|
||||
{'name': 'Projector', 'description': 'Some kind of video thinamejig, probably with unnecessary processing for free', 'quantity': 1, 'cost': 500.00},
|
||||
{'name': 'Lighting Desk', 'description': 'Cannot provide guarentee that it will work', 'quantity': 1, 'cost': 200.52},
|
||||
{'name': 'Moving lights', 'description': 'Flashy lights, with the copper', 'quantity': 8, 'cost': 50.00},
|
||||
{'name': 'Microphones', 'description': 'Make loud noise \n you will want speakers with this', 'quantity': 5, 'cost': 0.50},
|
||||
{'name': 'Sound Mixer Thing', 'description': 'Might be analogue, might be digital', 'quantity': 1, 'cost': 100.00},
|
||||
{'name': 'Electricity', 'description': 'You need this', 'quantity': 1, 'cost': 200.00},
|
||||
{'name': 'Crew', 'description': 'Costs nothing, because reasons', 'quantity': 1, 'cost': 0.00},
|
||||
{'name': 'Loyalty Discount', 'description': 'Have some negative moneys', 'quantity': 1, 'cost': -50.00}]
|
||||
{'name': 'Projector', 'description': 'Some kind of video thinamejig, probably with unnecessary processing for free', 'quantity': 1, 'cost': 500.00},
|
||||
{'name': 'Lighting Desk', 'description': 'Cannot provide guarentee that it will work', 'quantity': 1, 'cost': 200.52},
|
||||
{'name': 'Moving lights', 'description': 'Flashy lights, with the copper', 'quantity': 8, 'cost': 50.00},
|
||||
{'name': 'Microphones', 'description': 'Make loud noise \n you will want speakers with this', 'quantity': 5, 'cost': 0.50},
|
||||
{'name': 'Sound Mixer Thing', 'description': 'Might be analogue, might be digital', 'quantity': 1, 'cost': 100.00},
|
||||
{'name': 'Electricity', 'description': 'You need this', 'quantity': 1, 'cost': 200.00},
|
||||
{'name': 'Crew', 'description': 'Costs nothing, because reasons', 'quantity': 1, 'cost': 0.00},
|
||||
{'name': 'Loyalty Discount', 'description': 'Have some negative moneys', 'quantity': 1, 'cost': -50.00}]
|
||||
|
||||
dayDelta = -120 # start adding events from 4 months ago
|
||||
dayDelta = -120 # start adding events from 4 months ago
|
||||
|
||||
for i in range(150): # Let's add 100 events
|
||||
for i in range(150): # Let's add 100 events
|
||||
with reversion.create_revision():
|
||||
reversion.set_user(random.choice(self.profiles))
|
||||
|
||||
name = names[i%len(names)]
|
||||
name = names[i % len(names)]
|
||||
|
||||
startDate = datetime.date.today() + datetime.timedelta(days=dayDelta)
|
||||
dayDelta = dayDelta + random.randint(0,3)
|
||||
dayDelta = dayDelta + random.randint(0, 3)
|
||||
|
||||
newEvent = models.Event.objects.create(name=name, start_date=startDate)
|
||||
|
||||
if random.randint(0,2) > 1: # 1 in 3 have a start time
|
||||
newEvent.start_time = datetime.time(random.randint(15,20))
|
||||
if random.randint(0,2) > 1: # of those, 1 in 3 have an end time on the same day
|
||||
newEvent.end_time = datetime.time(random.randint(21,23))
|
||||
elif random.randint(0,1)>0: # half of the others finish early the next day
|
||||
if random.randint(0, 2) > 1: # 1 in 3 have a start time
|
||||
newEvent.start_time = datetime.time(random.randint(15, 20))
|
||||
if random.randint(0, 2) > 1: # of those, 1 in 3 have an end time on the same day
|
||||
newEvent.end_time = datetime.time(random.randint(21, 23))
|
||||
elif random.randint(0, 1) > 0: # half of the others finish early the next day
|
||||
newEvent.end_date = newEvent.start_date + datetime.timedelta(days=1)
|
||||
newEvent.end_time = datetime.time(random.randint(0,5))
|
||||
elif random.randint(0,2)>1: # 1 in 3 of the others finish a few days ahead
|
||||
newEvent.end_date = newEvent.start_date + datetime.timedelta(days=random.randint(1,4))
|
||||
newEvent.end_time = datetime.time(random.randint(0, 5))
|
||||
elif random.randint(0, 2) > 1: # 1 in 3 of the others finish a few days ahead
|
||||
newEvent.end_date = newEvent.start_date + datetime.timedelta(days=random.randint(1, 4))
|
||||
|
||||
|
||||
if random.randint(0,6) > 0: # 5 in 6 have MIC
|
||||
if random.randint(0, 6) > 0: # 5 in 6 have MIC
|
||||
newEvent.mic = random.choice(self.profiles)
|
||||
|
||||
if random.randint(0,6) > 0: # 5 in 6 have organisation
|
||||
if random.randint(0, 6) > 0: # 5 in 6 have organisation
|
||||
newEvent.organisation = random.choice(self.organisations)
|
||||
|
||||
if random.randint(0,6) > 0: # 5 in 6 have person
|
||||
if random.randint(0, 6) > 0: # 5 in 6 have person
|
||||
newEvent.person = random.choice(self.people)
|
||||
|
||||
if random.randint(0,6) > 0: # 5 in 6 have venue
|
||||
if random.randint(0, 6) > 0: # 5 in 6 have venue
|
||||
newEvent.venue = random.choice(self.venues)
|
||||
|
||||
# Could have any status, equally weighted
|
||||
newEvent.status = random.choice([models.Event.BOOKED,models.Event.CONFIRMED,models.Event.PROVISIONAL, models.Event.CANCELLED])
|
||||
newEvent.status = random.choice([models.Event.BOOKED, models.Event.CONFIRMED, models.Event.PROVISIONAL, models.Event.CANCELLED])
|
||||
|
||||
newEvent.dry_hire = (random.randint(0,7)==0) # 1 in 7 are dry hire
|
||||
newEvent.dry_hire = (random.randint(0, 7) == 0) # 1 in 7 are dry hire
|
||||
|
||||
if random.randint(0,1) > 0: # 1 in 2 have description
|
||||
if random.randint(0, 1) > 0: # 1 in 2 have description
|
||||
newEvent.description = random.choice(descriptions)
|
||||
|
||||
if random.randint(0,1) > 0: # 1 in 2 have notes
|
||||
if random.randint(0, 1) > 0: # 1 in 2 have notes
|
||||
newEvent.notes = random.choice(notes)
|
||||
|
||||
newEvent.save()
|
||||
|
||||
# Now add some items
|
||||
for j in range(random.randint(1,5)):
|
||||
itemData = itemOptions[random.randint(0,len(itemOptions)-1)]
|
||||
for j in range(random.randint(1, 5)):
|
||||
itemData = itemOptions[random.randint(0, len(itemOptions) - 1)]
|
||||
newItem = models.EventItem.objects.create(event=newEvent, order=j, **itemData)
|
||||
newItem.save()
|
||||
|
||||
while newEvent.sum_total < 0:
|
||||
itemData = itemOptions[random.randint(0,len(itemOptions)-1)]
|
||||
itemData = itemOptions[random.randint(0, len(itemOptions) - 1)]
|
||||
newItem = models.EventItem.objects.create(event=newEvent, order=j, **itemData)
|
||||
newItem.save()
|
||||
|
||||
with reversion.create_revision():
|
||||
reversion.set_user(random.choice(self.profiles))
|
||||
if newEvent.start_date < datetime.date.today(): # think about adding an invoice
|
||||
if random.randint(0,2) > 0: # 2 in 3 have had paperwork sent to treasury
|
||||
if newEvent.start_date < datetime.date.today(): # think about adding an invoice
|
||||
if random.randint(0, 2) > 0: # 2 in 3 have had paperwork sent to treasury
|
||||
newInvoice = models.Invoice.objects.create(event=newEvent)
|
||||
if newEvent.status is models.Event.CANCELLED: # void cancelled events
|
||||
if newEvent.status is models.Event.CANCELLED: # void cancelled events
|
||||
newInvoice.void = True
|
||||
elif random.randint(0,2)>1: # 1 in 3 have been paid
|
||||
elif random.randint(0, 2) > 1: # 1 in 3 have been paid
|
||||
models.Payment.objects.create(invoice=newInvoice, amount=newInvoice.balance, date=datetime.date.today())
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
|
||||
from django.db import models, migrations
|
||||
import django.core.validators
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
|
||||
from django.db import models, migrations
|
||||
from django.conf import settings
|
||||
@@ -18,7 +18,7 @@ class Migration(migrations.Migration):
|
||||
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
|
||||
('postedAt', models.DateTimeField(auto_now=True)),
|
||||
('message', models.TextField()),
|
||||
('user', models.ForeignKey(to=settings.AUTH_USER_MODEL)),
|
||||
('user', models.ForeignKey(to=settings.AUTH_USER_MODEL, on_delete=models.CASCADE)),
|
||||
],
|
||||
options={
|
||||
},
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
|
||||
from django.db import models, migrations
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
|
||||
from django.db import models, migrations
|
||||
import RIGS.models
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
|
||||
from django.db import models, migrations
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
|
||||
from django.db import models, migrations
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
|
||||
from django.db import models, migrations
|
||||
import RIGS.models
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
|
||||
from django.db import models, migrations
|
||||
from django.conf import settings
|
||||
@@ -33,11 +33,11 @@ class Migration(migrations.Migration):
|
||||
('payment_method', models.CharField(blank=True, null=True, max_length=255)),
|
||||
('payment_received', models.CharField(blank=True, null=True, max_length=255)),
|
||||
('purchase_order', models.CharField(blank=True, null=True, max_length=255)),
|
||||
('based_on', models.ForeignKey(to='RIGS.Event', related_name='future_events')),
|
||||
('checked_in_by', models.ForeignKey(to=settings.AUTH_USER_MODEL, related_name='event_checked_in')),
|
||||
('mic', models.ForeignKey(to=settings.AUTH_USER_MODEL, related_name='event_mic')),
|
||||
('organisation', models.ForeignKey(to='RIGS.Organisation')),
|
||||
('person', models.ForeignKey(to='RIGS.Person')),
|
||||
('based_on', models.ForeignKey(to='RIGS.Event', related_name='future_events', on_delete=models.CASCADE)),
|
||||
('checked_in_by', models.ForeignKey(to=settings.AUTH_USER_MODEL, related_name='event_checked_in', on_delete=models.CASCADE)),
|
||||
('mic', models.ForeignKey(to=settings.AUTH_USER_MODEL, related_name='event_mic', on_delete=models.CASCADE)),
|
||||
('organisation', models.ForeignKey(to='RIGS.Organisation', on_delete=models.CASCADE)),
|
||||
('person', models.ForeignKey(to='RIGS.Person', on_delete=models.CASCADE)),
|
||||
],
|
||||
options={
|
||||
},
|
||||
@@ -52,7 +52,7 @@ class Migration(migrations.Migration):
|
||||
('quantity', models.IntegerField()),
|
||||
('cost', models.DecimalField(max_digits=10, decimal_places=2)),
|
||||
('order', models.IntegerField()),
|
||||
('event', models.ForeignKey(to='RIGS.Event', related_name='item')),
|
||||
('event', models.ForeignKey(to='RIGS.Event', related_name='item', on_delete=models.CASCADE)),
|
||||
],
|
||||
options={
|
||||
},
|
||||
@@ -75,7 +75,7 @@ class Migration(migrations.Migration):
|
||||
migrations.AddField(
|
||||
model_name='event',
|
||||
name='venue',
|
||||
field=models.ForeignKey(to='RIGS.Venue'),
|
||||
field=models.ForeignKey(to='RIGS.Venue', on_delete=models.CASCADE),
|
||||
preserve_default=True,
|
||||
),
|
||||
]
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
|
||||
from django.db import models, migrations
|
||||
from django.conf import settings
|
||||
@@ -14,26 +14,26 @@ class Migration(migrations.Migration):
|
||||
migrations.AlterField(
|
||||
model_name='event',
|
||||
name='based_on',
|
||||
field=models.ForeignKey(to='RIGS.Event', related_name='future_events', blank=True, null=True),
|
||||
field=models.ForeignKey(to='RIGS.Event', related_name='future_events', blank=True, null=True, on_delete=models.CASCADE),
|
||||
preserve_default=True,
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='event',
|
||||
name='checked_in_by',
|
||||
field=models.ForeignKey(to=settings.AUTH_USER_MODEL, related_name='event_checked_in', blank=True,
|
||||
null=True),
|
||||
null=True, on_delete=models.CASCADE),
|
||||
preserve_default=True,
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='event',
|
||||
name='mic',
|
||||
field=models.ForeignKey(to=settings.AUTH_USER_MODEL, related_name='event_mic', blank=True, null=True),
|
||||
field=models.ForeignKey(to=settings.AUTH_USER_MODEL, related_name='event_mic', blank=True, null=True, on_delete=models.CASCADE),
|
||||
preserve_default=True,
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='event',
|
||||
name='organisation',
|
||||
field=models.ForeignKey(to='RIGS.Organisation', blank=True, null=True),
|
||||
field=models.ForeignKey(to='RIGS.Organisation', blank=True, null=True, on_delete=models.CASCADE),
|
||||
preserve_default=True,
|
||||
),
|
||||
migrations.AlterField(
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
|
||||
from django.db import models, migrations
|
||||
from django.conf import settings
|
||||
@@ -19,8 +19,8 @@ class Migration(migrations.Migration):
|
||||
('run', models.BooleanField(default=False)),
|
||||
('derig', models.BooleanField(default=False)),
|
||||
('notes', models.TextField(blank=True, null=True)),
|
||||
('event', models.ForeignKey(related_name='crew', to='RIGS.Event')),
|
||||
('user', models.ForeignKey(to=settings.AUTH_USER_MODEL)),
|
||||
('event', models.ForeignKey(related_name='crew', to='RIGS.Event', on_delete=models.CASCADE)),
|
||||
('user', models.ForeignKey(to=settings.AUTH_USER_MODEL, on_delete=models.CASCADE)),
|
||||
],
|
||||
options={
|
||||
},
|
||||
@@ -35,7 +35,7 @@ class Migration(migrations.Migration):
|
||||
migrations.AlterField(
|
||||
model_name='eventitem',
|
||||
name='event',
|
||||
field=models.ForeignKey(related_name='items', to='RIGS.Event'),
|
||||
field=models.ForeignKey(related_name='items', to='RIGS.Event', on_delete=models.CASCADE),
|
||||
preserve_default=True,
|
||||
),
|
||||
]
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
|
||||
from django.db import models, migrations
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
|
||||
from django.db import models, migrations
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
|
||||
from django.db import models, migrations
|
||||
|
||||
@@ -14,7 +14,7 @@ class Migration(migrations.Migration):
|
||||
migrations.AlterField(
|
||||
model_name='event',
|
||||
name='person',
|
||||
field=models.ForeignKey(blank=True, null=True, to='RIGS.Person'),
|
||||
field=models.ForeignKey(blank=True, null=True, to='RIGS.Person', on_delete=models.CASCADE),
|
||||
preserve_default=True,
|
||||
),
|
||||
]
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
|
||||
from django.db import models, migrations
|
||||
|
||||
@@ -14,13 +14,13 @@ class Migration(migrations.Migration):
|
||||
migrations.AlterField(
|
||||
model_name='event',
|
||||
name='venue',
|
||||
field=models.ForeignKey(blank=True, to='RIGS.Venue', null=True),
|
||||
field=models.ForeignKey(blank=True, to='RIGS.Venue', null=True, on_delete=models.CASCADE),
|
||||
preserve_default=True,
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='eventitem',
|
||||
name='event',
|
||||
field=models.ForeignKey(related_name='items', blank=True, to='RIGS.Event'),
|
||||
field=models.ForeignKey(related_name='items', blank=True, to='RIGS.Event', on_delete=models.CASCADE),
|
||||
preserve_default=True,
|
||||
),
|
||||
]
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
|
||||
from django.db import models, migrations
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
|
||||
from django.db import models, migrations
|
||||
from django.conf import settings
|
||||
@@ -18,7 +18,7 @@ class Migration(migrations.Migration):
|
||||
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
|
||||
('invoice_date', models.DateField(auto_now_add=True)),
|
||||
('void', models.BooleanField()),
|
||||
('event', models.OneToOneField(to='RIGS.Event')),
|
||||
('event', models.OneToOneField(to='RIGS.Event', on_delete=models.CASCADE)),
|
||||
],
|
||||
options={
|
||||
},
|
||||
@@ -31,7 +31,7 @@ class Migration(migrations.Migration):
|
||||
('date', models.DateField()),
|
||||
('amount', models.DecimalField(help_text=b'Please use ex. VAT', max_digits=10, decimal_places=2)),
|
||||
('method', models.CharField(max_length=2, choices=[(b'C', b'Cash'), (b'I', b'Internal'), (b'E', b'External'), (b'SU', b'SU Core'), (b'M', b'Members')])),
|
||||
('invoice', models.ForeignKey(to='RIGS.Invoice')),
|
||||
('invoice', models.ForeignKey(to='RIGS.Invoice', on_delete=models.CASCADE)),
|
||||
],
|
||||
options={
|
||||
},
|
||||
@@ -40,7 +40,7 @@ class Migration(migrations.Migration):
|
||||
migrations.AlterField(
|
||||
model_name='event',
|
||||
name='mic',
|
||||
field=models.ForeignKey(related_name='event_mic', verbose_name=b'MIC', blank=True, to=settings.AUTH_USER_MODEL, null=True),
|
||||
field=models.ForeignKey(related_name='event_mic', verbose_name=b'MIC', blank=True, to=settings.AUTH_USER_MODEL, null=True, on_delete=models.CASCADE),
|
||||
preserve_default=True,
|
||||
),
|
||||
]
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
|
||||
from django.db import models, migrations
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
|
||||
from django.db import models, migrations
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
|
||||
from django.db import models, migrations
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
|
||||
from django.db import models, migrations
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
|
||||
from django.db import models, migrations
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
|
||||
from django.db import models, migrations
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
|
||||
from django.db import models, migrations
|
||||
import django.core.validators
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
|
||||
from django.db import models, migrations
|
||||
import django.db.models.deletion
|
||||
|
||||
26
RIGS/migrations/0025_auto_20160331_1302.py
Normal file
@@ -0,0 +1,26 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.9.4 on 2016-03-31 12:02
|
||||
|
||||
|
||||
import django.core.validators
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('RIGS', '0024_auto_20160229_2042'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='profile',
|
||||
name='username',
|
||||
field=models.CharField(error_messages={'unique': 'A user with that username already exists.'}, help_text='Required. 30 characters or fewer. Letters, digits and @/./+/-/_ only.', max_length=30, unique=True, validators=[django.core.validators.RegexValidator('^[\\w.@+-]+$', 'Enter a valid username. This value may contain only letters, numbers and @/./+/-/_ characters.')], verbose_name='username'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='vatrate',
|
||||
name='start_at',
|
||||
field=models.DateField(),
|
||||
),
|
||||
]
|
||||
27
RIGS/migrations/0025_eventauthorisation.py
Normal file
@@ -0,0 +1,27 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
|
||||
from django.db import models, migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('RIGS', '0024_auto_20160229_2042'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='EventAuthorisation',
|
||||
fields=[
|
||||
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
|
||||
('email', models.EmailField(max_length=254)),
|
||||
('name', models.CharField(max_length=255)),
|
||||
('uni_id', models.CharField(max_length=10, null=True, verbose_name=b'University ID', blank=True)),
|
||||
('account_code', models.CharField(max_length=50, null=True, blank=True)),
|
||||
('amount', models.DecimalField(verbose_name=b'authorisation amount', max_digits=10, decimal_places=2)),
|
||||
('created_at', models.DateTimeField(auto_now_add=True)),
|
||||
('event', models.ForeignKey(related_name='authroisations', to='RIGS.Event', on_delete=models.CASCADE)),
|
||||
],
|
||||
),
|
||||
]
|
||||
21
RIGS/migrations/0026_auto_20170510_1846.py
Normal file
@@ -0,0 +1,21 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.11.1 on 2017-05-10 17:46
|
||||
|
||||
|
||||
import django.contrib.auth.validators
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('RIGS', '0025_auto_20160331_1302'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='profile',
|
||||
name='username',
|
||||
field=models.CharField(error_messages={'unique': 'A user with that username already exists.'}, help_text='Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.', max_length=150, unique=True, validators=[django.contrib.auth.validators.ASCIIUsernameValidator()], verbose_name='username'),
|
||||
),
|
||||
]
|
||||
18
RIGS/migrations/0026_remove_eventauthorisation_created_at.py
Normal file
@@ -0,0 +1,18 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
|
||||
from django.db import models, migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('RIGS', '0025_eventauthorisation'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveField(
|
||||
model_name='eventauthorisation',
|
||||
name='created_at',
|
||||
),
|
||||
]
|
||||
19
RIGS/migrations/0027_eventauthorisation_event_singular.py
Normal file
@@ -0,0 +1,19 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
|
||||
from django.db import models, migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('RIGS', '0026_remove_eventauthorisation_created_at'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='eventauthorisation',
|
||||
name='event',
|
||||
field=models.OneToOneField(related_name='authorisation', to='RIGS.Event', on_delete=models.CASCADE),
|
||||
),
|
||||
]
|
||||
21
RIGS/migrations/0029_eventauthorisation_sent_by.py
Normal file
@@ -0,0 +1,21 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
|
||||
from django.db import models, migrations
|
||||
from django.conf import settings
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('RIGS', '0027_eventauthorisation_event_singular'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='eventauthorisation',
|
||||
name='sent_by',
|
||||
field=models.ForeignKey(default=1, to=settings.AUTH_USER_MODEL, on_delete=models.CASCADE),
|
||||
preserve_default=False,
|
||||
),
|
||||
]
|
||||
30
RIGS/migrations/0030_auth_request_sending.py
Normal file
@@ -0,0 +1,30 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
|
||||
from django.db import models, migrations
|
||||
from django.conf import settings
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('RIGS', '0029_eventauthorisation_sent_by'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='event',
|
||||
name='auth_request_at',
|
||||
field=models.DateTimeField(null=True, blank=True),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='event',
|
||||
name='auth_request_by',
|
||||
field=models.ForeignKey(blank=True, to=settings.AUTH_USER_MODEL, null=True, on_delete=models.CASCADE),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='event',
|
||||
name='auth_request_to',
|
||||
field=models.EmailField(max_length=254, null=True, blank=True),
|
||||
),
|
||||
]
|
||||
16
RIGS/migrations/0031_merge_20170512_2102.py
Normal file
@@ -0,0 +1,16 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.11.1 on 2017-05-12 20:02
|
||||
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('RIGS', '0030_auth_request_sending'),
|
||||
('RIGS', '0026_auto_20170510_1846'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
]
|
||||
69
RIGS/migrations/0032_auto_20170904_2355.py
Normal file
@@ -0,0 +1,69 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.11.4 on 2017-09-04 22:55
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.conf import settings
|
||||
import django.contrib.auth.validators
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('reversion', '0001_squashed_0004_auto_20160611_1202'),
|
||||
('RIGS', '0031_merge_20170512_2102'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='RIGSVersion',
|
||||
fields=[
|
||||
],
|
||||
options={
|
||||
'indexes': [],
|
||||
'proxy': True,
|
||||
},
|
||||
bases=('reversion.version',),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='event',
|
||||
name='collector',
|
||||
field=models.CharField(blank=True, max_length=255, null=True, verbose_name='collected by'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='event',
|
||||
name='mic',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='event_mic', to=settings.AUTH_USER_MODEL, verbose_name='MIC'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='event',
|
||||
name='purchase_order',
|
||||
field=models.CharField(blank=True, max_length=255, null=True, verbose_name='PO'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='eventauthorisation',
|
||||
name='amount',
|
||||
field=models.DecimalField(decimal_places=2, max_digits=10, verbose_name='authorisation amount'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='eventauthorisation',
|
||||
name='uni_id',
|
||||
field=models.CharField(blank=True, max_length=10, null=True, verbose_name='University ID'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='payment',
|
||||
name='amount',
|
||||
field=models.DecimalField(decimal_places=2, help_text='Please use ex. VAT', max_digits=10),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='payment',
|
||||
name='method',
|
||||
field=models.CharField(blank=True, choices=[('C', 'Cash'), ('I', 'Internal'), ('E', 'External'), ('SU', 'SU Core'), ('T', 'TEC Adjustment')], max_length=2, null=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='profile',
|
||||
name='username',
|
||||
field=models.CharField(error_messages={'unique': 'A user with that username already exists.'}, help_text='Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.', max_length=150, unique=True, validators=[django.contrib.auth.validators.UnicodeUsernameValidator()], verbose_name='username'),
|
||||
),
|
||||
]
|
||||
18
RIGS/migrations/0033_auto_20180325_0016.py
Normal file
@@ -0,0 +1,18 @@
|
||||
# Generated by Django 2.0.3 on 2018-03-25 00:16
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('RIGS', '0032_auto_20170904_2355'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='profile',
|
||||
name='last_name',
|
||||
field=models.CharField(blank=True, max_length=150, verbose_name='last name'),
|
||||
),
|
||||
]
|
||||
139
RIGS/models.py
@@ -1,19 +1,24 @@
|
||||
import datetime
|
||||
import hashlib
|
||||
import datetime
|
||||
import pytz
|
||||
import random
|
||||
|
||||
from django.db import models
|
||||
from django.contrib.auth.models import AbstractUser
|
||||
from django.conf import settings
|
||||
from django.utils import timezone
|
||||
from django.utils.functional import cached_property
|
||||
from django.utils.encoding import python_2_unicode_compatible
|
||||
from reversion import revisions as reversion
|
||||
from reversion.models import Version
|
||||
import string
|
||||
|
||||
import random
|
||||
from collections import Counter
|
||||
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
|
||||
from django.urls import reverse_lazy
|
||||
|
||||
|
||||
# Create your models here.
|
||||
@@ -28,13 +33,13 @@ class Profile(AbstractUser):
|
||||
size = 20
|
||||
chars = string.ascii_letters + string.digits
|
||||
new_api_key = ''.join(random.choice(chars) for x in range(size))
|
||||
return new_api_key;
|
||||
return new_api_key
|
||||
|
||||
@property
|
||||
def profile_picture(self):
|
||||
url = ""
|
||||
if settings.USE_GRAVATAR or settings.USE_GRAVATAR is None:
|
||||
url = "https://www.gravatar.com/avatar/" + hashlib.md5(self.email).hexdigest() + "?d=wavatar&s=500"
|
||||
url = "https://www.gravatar.com/avatar/" + hashlib.md5(self.email.encode('utf-8')).hexdigest() + "?d=wavatar&s=500"
|
||||
return url
|
||||
|
||||
@property
|
||||
@@ -58,32 +63,31 @@ class Profile(AbstractUser):
|
||||
|
||||
|
||||
class RevisionMixin(object):
|
||||
@property
|
||||
def current_version(self):
|
||||
version = Version.objects.get_for_object(self).select_related('revision').first()
|
||||
return version
|
||||
|
||||
@property
|
||||
def last_edited_at(self):
|
||||
versions = reversion.get_for_object(self)
|
||||
if versions:
|
||||
version = reversion.get_for_object(self)[0]
|
||||
return version.revision.date_created
|
||||
else:
|
||||
version = self.current_version
|
||||
if version is None:
|
||||
return None
|
||||
return version.revision.date_created
|
||||
|
||||
@property
|
||||
def last_edited_by(self):
|
||||
versions = reversion.get_for_object(self)
|
||||
if versions:
|
||||
version = reversion.get_for_object(self)[0]
|
||||
return version.revision.user
|
||||
else:
|
||||
version = self.current_version
|
||||
if version is None:
|
||||
return None
|
||||
return version.revision.user
|
||||
|
||||
@property
|
||||
def current_version_id(self):
|
||||
versions = reversion.get_for_object(self)
|
||||
if versions:
|
||||
version = reversion.get_for_object(self)[0]
|
||||
return "V{0} | R{1}".format(version.pk, version.revision.pk)
|
||||
else:
|
||||
version = self.current_version
|
||||
if version is None:
|
||||
return None
|
||||
return "V{0} | R{1}".format(version.pk, version.revision.pk)
|
||||
|
||||
|
||||
@reversion.register
|
||||
@@ -175,7 +179,7 @@ class Organisation(models.Model, RevisionMixin):
|
||||
|
||||
class VatManager(models.Manager):
|
||||
def current_rate(self):
|
||||
return self.find_rate(datetime.datetime.now())
|
||||
return self.find_rate(timezone.now())
|
||||
|
||||
def find_rate(self, date):
|
||||
# return self.filter(startAt__lte=date).latest()
|
||||
@@ -190,7 +194,7 @@ class VatManager(models.Manager):
|
||||
@reversion.register
|
||||
@python_2_unicode_compatible
|
||||
class VatRate(models.Model, RevisionMixin):
|
||||
start_at = models.DateTimeField()
|
||||
start_at = models.DateField()
|
||||
rate = models.DecimalField(max_digits=6, decimal_places=6)
|
||||
comment = models.CharField(max_length=255)
|
||||
|
||||
@@ -241,18 +245,12 @@ 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(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')
|
||||
(models.Q(start_date__gte=timezone.now().date(), end_date__isnull=True, dry_hire=False) & ~models.Q(status=Event.CANCELLED)) | # Starts after with no end
|
||||
(models.Q(end_date__gte=timezone.now().date(), dry_hire=False) & ~models.Q(status=Event.CANCELLED)) | # Ends after
|
||||
(models.Q(dry_hire=True, start_date__gte=timezone.now().date()) & ~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=timezone.now().date()) # Canceled but not started
|
||||
).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):
|
||||
@@ -275,12 +273,12 @@ class EventManager(models.Manager):
|
||||
|
||||
def rig_count(self):
|
||||
event_count = self.filter(
|
||||
(models.Q(start_date__gte=datetime.date.today(), end_date__isnull=True, dry_hire=False,
|
||||
(models.Q(start_date__gte=timezone.now().date(), end_date__isnull=True, dry_hire=False,
|
||||
is_rig=True) & ~models.Q(
|
||||
status=Event.CANCELLED)) | # Starts after with no end
|
||||
(models.Q(end_date__gte=datetime.date.today(), dry_hire=False, is_rig=True) & ~models.Q(
|
||||
(models.Q(end_date__gte=timezone.now().date(), dry_hire=False, is_rig=True) & ~models.Q(
|
||||
status=Event.CANCELLED)) | # Ends after
|
||||
(models.Q(dry_hire=True, start_date__gte=datetime.date.today(), is_rig=True) & ~models.Q(
|
||||
(models.Q(dry_hire=True, start_date__gte=timezone.now().date(), is_rig=True) & ~models.Q(
|
||||
status=Event.CANCELLED)) | # Active dry hire
|
||||
(models.Q(dry_hire=True, checked_in_by__isnull=True, is_rig=True) & (
|
||||
models.Q(status=Event.BOOKED) | models.Q(status=Event.CONFIRMED))) # Active dry hire GT
|
||||
@@ -304,9 +302,9 @@ class Event(models.Model, RevisionMixin):
|
||||
)
|
||||
|
||||
name = models.CharField(max_length=255)
|
||||
person = models.ForeignKey('Person', null=True, blank=True)
|
||||
organisation = models.ForeignKey('Organisation', blank=True, null=True)
|
||||
venue = models.ForeignKey('Venue', blank=True, null=True)
|
||||
person = models.ForeignKey('Person', null=True, blank=True, on_delete=models.CASCADE)
|
||||
organisation = models.ForeignKey('Organisation', blank=True, null=True, on_delete=models.CASCADE)
|
||||
venue = models.ForeignKey('Venue', blank=True, null=True, on_delete=models.CASCADE)
|
||||
description = models.TextField(blank=True, null=True)
|
||||
notes = models.TextField(blank=True, null=True)
|
||||
status = models.IntegerField(choices=EVENT_STATUS_CHOICES, default=PROVISIONAL)
|
||||
@@ -325,9 +323,9 @@ class Event(models.Model, RevisionMixin):
|
||||
meet_info = models.CharField(max_length=255, blank=True, null=True)
|
||||
|
||||
# Crew management
|
||||
checked_in_by = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='event_checked_in', blank=True, null=True)
|
||||
checked_in_by = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='event_checked_in', blank=True, null=True, on_delete=models.CASCADE)
|
||||
mic = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='event_mic', blank=True, null=True,
|
||||
verbose_name="MIC")
|
||||
verbose_name="MIC", on_delete=models.CASCADE)
|
||||
|
||||
# Monies
|
||||
payment_method = models.CharField(max_length=255, blank=True, null=True)
|
||||
@@ -335,6 +333,11 @@ class Event(models.Model, RevisionMixin):
|
||||
purchase_order = models.CharField(max_length=255, blank=True, null=True, verbose_name='PO')
|
||||
collector = models.CharField(max_length=255, blank=True, null=True, verbose_name='collected by')
|
||||
|
||||
# Authorisation request details
|
||||
auth_request_by = models.ForeignKey('Profile', null=True, blank=True, on_delete=models.CASCADE)
|
||||
auth_request_at = models.DateTimeField(null=True, blank=True)
|
||||
auth_request_to = models.EmailField(null=True, blank=True)
|
||||
|
||||
# Calculated values
|
||||
"""
|
||||
EX Vat
|
||||
@@ -367,7 +370,7 @@ class Event(models.Model, RevisionMixin):
|
||||
|
||||
@property
|
||||
def vat(self):
|
||||
return self.sum_total * self.vat_rate.rate
|
||||
return Decimal(self.sum_total * self.vat_rate.rate).quantize(Decimal('.01'))
|
||||
|
||||
"""
|
||||
Inc VAT
|
||||
@@ -375,7 +378,7 @@ class Event(models.Model, RevisionMixin):
|
||||
|
||||
@property
|
||||
def total(self):
|
||||
return self.sum_total + self.vat
|
||||
return Decimal(self.sum_total + self.vat).quantize(Decimal('.01'))
|
||||
|
||||
@property
|
||||
def cancelled(self):
|
||||
@@ -385,6 +388,10 @@ class Event(models.Model, RevisionMixin):
|
||||
def confirmed(self):
|
||||
return (self.status == self.BOOKED or self.status == self.CONFIRMED)
|
||||
|
||||
@property
|
||||
def authorised(self):
|
||||
return not self.internal and self.purchase_order or self.authorisation.amount == self.total
|
||||
|
||||
@property
|
||||
def has_start_time(self):
|
||||
return self.start_time is not None
|
||||
@@ -445,13 +452,17 @@ class Event(models.Model, RevisionMixin):
|
||||
else:
|
||||
return endDate
|
||||
|
||||
@property
|
||||
def internal(self):
|
||||
return self.organisation and self.organisation.union_account
|
||||
|
||||
objects = EventManager()
|
||||
|
||||
def get_absolute_url(self):
|
||||
return reverse_lazy('event_detail', kwargs={'pk': self.pk})
|
||||
|
||||
def __str__(self):
|
||||
return unicode(self.pk) + ": " + self.name
|
||||
return str(self.pk) + ": " + self.name
|
||||
|
||||
def clean(self):
|
||||
if self.end_date and self.start_date > self.end_date:
|
||||
@@ -474,7 +485,7 @@ class Event(models.Model, RevisionMixin):
|
||||
|
||||
|
||||
class EventItem(models.Model):
|
||||
event = models.ForeignKey('Event', related_name='items', blank=True)
|
||||
event = models.ForeignKey('Event', related_name='items', blank=True, on_delete=models.CASCADE)
|
||||
name = models.CharField(max_length=255)
|
||||
description = models.TextField(blank=True, null=True)
|
||||
quantity = models.IntegerField()
|
||||
@@ -493,17 +504,35 @@ class EventItem(models.Model):
|
||||
|
||||
|
||||
class EventCrew(models.Model):
|
||||
event = models.ForeignKey('Event', related_name='crew')
|
||||
user = models.ForeignKey(settings.AUTH_USER_MODEL)
|
||||
event = models.ForeignKey('Event', related_name='crew', on_delete=models.CASCADE)
|
||||
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
|
||||
rig = models.BooleanField(default=False)
|
||||
run = models.BooleanField(default=False)
|
||||
derig = models.BooleanField(default=False)
|
||||
notes = models.TextField(blank=True, null=True)
|
||||
|
||||
|
||||
@reversion.register
|
||||
class EventAuthorisation(models.Model, RevisionMixin):
|
||||
event = models.OneToOneField('Event', related_name='authorisation', on_delete=models.CASCADE)
|
||||
email = models.EmailField()
|
||||
name = models.CharField(max_length=255)
|
||||
uni_id = models.CharField(max_length=10, blank=True, null=True, verbose_name="University ID")
|
||||
account_code = models.CharField(max_length=50, blank=True, null=True)
|
||||
amount = models.DecimalField(max_digits=10, decimal_places=2, verbose_name="authorisation amount")
|
||||
sent_by = models.ForeignKey('RIGS.Profile', on_delete=models.CASCADE)
|
||||
|
||||
def get_absolute_url(self):
|
||||
return reverse_lazy('event_detail', kwargs={'pk': self.event.pk})
|
||||
|
||||
@property
|
||||
def activity_feed_string(self):
|
||||
return str("N%05d" % self.event.pk + ' (requested by ' + self.sent_by.initials + ')')
|
||||
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class Invoice(models.Model):
|
||||
event = models.OneToOneField('Event')
|
||||
event = models.OneToOneField('Event', on_delete=models.CASCADE)
|
||||
invoice_date = models.DateField(auto_now_add=True)
|
||||
void = models.BooleanField(default=False)
|
||||
|
||||
@@ -555,7 +584,7 @@ class Payment(models.Model):
|
||||
(ADJUSTMENT, 'TEC Adjustment'),
|
||||
)
|
||||
|
||||
invoice = models.ForeignKey('Invoice')
|
||||
invoice = models.ForeignKey('Invoice', on_delete=models.CASCADE)
|
||||
date = models.DateField()
|
||||
amount = models.DecimalField(max_digits=10, decimal_places=2, help_text='Please use ex. VAT')
|
||||
method = models.CharField(max_length=2, choices=METHODS, null=True, blank=True)
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
from RIGS.models import Profile
|
||||
from RIGS.forms import ProfileRegistrationFormUniqueEmail
|
||||
from registration.signals import user_registered
|
||||
|
||||
|
||||
def user_created(sender, user, request, **kwargs):
|
||||
form = ProfileRegistrationFormUniqueEmail(request.POST)
|
||||
user.first_name = form.data['first_name']
|
||||
user.last_name = form.data['last_name']
|
||||
user.initials = form.data['initials']
|
||||
user.phone = form.data['phone']
|
||||
user.save()
|
||||
form = ProfileRegistrationFormUniqueEmail(request.POST)
|
||||
user.first_name = form.data['first_name']
|
||||
user.last_name = form.data['last_name']
|
||||
user.initials = form.data['initials']
|
||||
user.phone = form.data['phone']
|
||||
user.save()
|
||||
|
||||
from registration.signals import user_registered
|
||||
user_registered.connect(user_created)
|
||||
|
||||
user_registered.connect(user_created)
|
||||
|
||||
255
RIGS/rigboard.py
@@ -1,23 +1,30 @@
|
||||
import os
|
||||
import cStringIO as StringIO
|
||||
from io import BytesIO
|
||||
import urllib2
|
||||
import urllib.request
|
||||
import urllib.error
|
||||
import urllib.parse
|
||||
|
||||
from django.contrib.staticfiles.storage import staticfiles_storage
|
||||
from django.core.mail import EmailMessage, EmailMultiAlternatives
|
||||
from django.views import generic
|
||||
from django.core.urlresolvers import reverse_lazy
|
||||
from django.urls import reverse_lazy
|
||||
from django.shortcuts import get_object_or_404
|
||||
from django.template import RequestContext
|
||||
from django.template.loader import get_template
|
||||
from django.conf import settings
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.urls import reverse
|
||||
from django.core import signing
|
||||
from django.http import HttpResponse
|
||||
from django.core.exceptions import SuspiciousOperation
|
||||
from django.db.models import Q
|
||||
from django.contrib import messages
|
||||
from django.utils.decorators import method_decorator
|
||||
from z3c.rml import rml2pdf
|
||||
from PyPDF2 import PdfFileMerger, PdfFileReader
|
||||
import simplejson
|
||||
import premailer
|
||||
|
||||
from RIGS import models, forms
|
||||
from PyRIGS import decorators
|
||||
import datetime
|
||||
import re
|
||||
import copy
|
||||
@@ -36,15 +43,17 @@ class RigboardIndex(generic.TemplateView):
|
||||
context['events'] = models.Event.objects.current_events()
|
||||
return context
|
||||
|
||||
|
||||
class WebCalendar(generic.TemplateView):
|
||||
template_name = 'RIGS/calendar.html'
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super(WebCalendar, self).get_context_data(**kwargs)
|
||||
context['view'] = kwargs.get('view','')
|
||||
context['date'] = kwargs.get('date','')
|
||||
context['view'] = kwargs.get('view', '')
|
||||
context['date'] = kwargs.get('date', '')
|
||||
return context
|
||||
|
||||
|
||||
class EventDetail(generic.DetailView):
|
||||
model = models.Event
|
||||
|
||||
@@ -53,15 +62,14 @@ class EventOembed(generic.View):
|
||||
model = models.Event
|
||||
|
||||
def get(self, request, pk=None):
|
||||
|
||||
embed_url = reverse('event_embed', args=[pk])
|
||||
full_url = "{0}://{1}{2}".format(request.scheme, request.META['HTTP_HOST'], embed_url)
|
||||
|
||||
data = {
|
||||
'html': '<iframe src="{0}" frameborder="0" width="100%" height="250"></iframe>'.format(full_url),
|
||||
'html': '<iframe src="{0}" frameborder="0" width="100%" height="350"></iframe>'.format(full_url),
|
||||
'version': '1.0',
|
||||
'type': 'rich',
|
||||
'height': '250'
|
||||
'height': '350'
|
||||
}
|
||||
|
||||
json = simplejson.JSONEncoderForHTML().encode(data)
|
||||
@@ -85,9 +93,8 @@ class EventCreate(generic.CreateView):
|
||||
if re.search('"-\d+"', form['items_json'].value()):
|
||||
messages.info(self.request, "Your item changes have been saved. Please fix the errors and save the event.")
|
||||
|
||||
|
||||
# Get some other objects to include in the form. Used when there are errors but also nice and quick.
|
||||
for field, model in form.related_models.iteritems():
|
||||
for field, model in form.related_models.items():
|
||||
value = form[field].value()
|
||||
if value is not None and value != '':
|
||||
context[field] = model.objects.get(pk=value)
|
||||
@@ -108,24 +115,43 @@ class EventUpdate(generic.UpdateView):
|
||||
form = context['form']
|
||||
|
||||
# Get some other objects to include in the form. Used when there are errors but also nice and quick.
|
||||
for field, model in form.related_models.iteritems():
|
||||
for field, model in form.related_models.items():
|
||||
value = form[field].value()
|
||||
if value is not None and value != '':
|
||||
context[field] = model.objects.get(pk=value)
|
||||
|
||||
# If this event has already been emailed to a client, show a warning
|
||||
if self.object.auth_request_at is not None:
|
||||
messages.info(self.request, 'This event has already been sent to the client for authorisation, any changes you make will be visible to them immediately.')
|
||||
|
||||
if hasattr(self.object, 'authorised'):
|
||||
messages.warning(self.request, 'This event has already been authorised by client, any changes to price will require reauthorisation.')
|
||||
|
||||
return context
|
||||
|
||||
def get_success_url(self):
|
||||
return reverse_lazy('event_detail', kwargs={'pk': self.object.pk})
|
||||
|
||||
|
||||
class EventDuplicate(EventUpdate):
|
||||
def get_object(self, queryset=None):
|
||||
old = super(EventDuplicate, self).get_object(queryset) # Get the object (the event you're duplicating)
|
||||
new = copy.copy(old) # Make a copy of the object in memory
|
||||
new.based_on = old # Make the new event based on the old event
|
||||
new.purchase_order = None
|
||||
old = super(EventDuplicate, self).get_object(queryset) # Get the object (the event you're duplicating)
|
||||
new = copy.copy(old) # Make a copy of the object in memory
|
||||
new.based_on = old # Make the new event based on the old event
|
||||
new.purchase_order = None # Remove old PO
|
||||
|
||||
if self.request.method in ('POST', 'PUT'): # This only happens on save (otherwise items won't display in editor)
|
||||
new.pk = None # This means a new event will be created on save, and all items will be re-created
|
||||
# Clear checked in by if it's a dry hire
|
||||
if new.dry_hire is True:
|
||||
new.checked_in_by = None
|
||||
|
||||
# Remove all the authorisation information from the new event
|
||||
new.auth_request_to = None
|
||||
new.auth_request_by = None
|
||||
new.auth_request_at = None
|
||||
|
||||
if self.request.method in (
|
||||
'POST', 'PUT'): # This only happens on save (otherwise items won't display in editor)
|
||||
new.pk = None # This means a new event will be created on save, and all items will be re-created
|
||||
else:
|
||||
messages.info(self.request, 'Event data duplicated but not yet saved. Click save to complete operation.')
|
||||
|
||||
@@ -136,41 +162,34 @@ class EventDuplicate(EventUpdate):
|
||||
context["duplicate"] = True
|
||||
return context
|
||||
|
||||
|
||||
class EventPrint(generic.View):
|
||||
def get(self, request, pk):
|
||||
object = get_object_or_404(models.Event, pk=pk)
|
||||
template = get_template('RIGS/event_print.xml')
|
||||
copies = ('TEC', 'Client')
|
||||
|
||||
merger = PdfFileMerger()
|
||||
|
||||
for copy in copies:
|
||||
context = {
|
||||
'object': object,
|
||||
'fonts': {
|
||||
'opensans': {
|
||||
'regular': 'RIGS/static/fonts/OPENSANS-REGULAR.TTF',
|
||||
'bold': 'RIGS/static/fonts/OPENSANS-BOLD.TTF',
|
||||
}
|
||||
},
|
||||
'quote': True,
|
||||
'current_user': request.user,
|
||||
}
|
||||
|
||||
context = RequestContext(request, { # this should be outside the loop, but bug in 1.8.2 prevents this
|
||||
'object': object,
|
||||
'fonts': {
|
||||
'opensans': {
|
||||
'regular': 'RIGS/static/fonts/OPENSANS-REGULAR.TTF',
|
||||
'bold': 'RIGS/static/fonts/OPENSANS-BOLD.TTF',
|
||||
}
|
||||
},
|
||||
'copy':copy,
|
||||
'current_user':request.user,
|
||||
})
|
||||
rml = template.render(context)
|
||||
|
||||
# context['copy'] = copy # this is the way to do it once we upgrade to Django 1.8.3
|
||||
buffer = rml2pdf.parseString(rml)
|
||||
merger.append(PdfFileReader(buffer))
|
||||
buffer.close()
|
||||
|
||||
rml = template.render(context)
|
||||
buffer = StringIO.StringIO()
|
||||
|
||||
buffer = rml2pdf.parseString(rml)
|
||||
|
||||
merger.append(PdfFileReader(buffer))
|
||||
|
||||
buffer.close()
|
||||
|
||||
terms = urllib2.urlopen(settings.TERMS_OF_HIRE_URL)
|
||||
merger.append(StringIO.StringIO(terms.read()))
|
||||
terms = urllib.request.urlopen(settings.TERMS_OF_HIRE_URL)
|
||||
merger.append(BytesIO(terms.read()))
|
||||
|
||||
merged = BytesIO()
|
||||
merger.write(merged)
|
||||
@@ -183,6 +202,7 @@ class EventPrint(generic.View):
|
||||
response.write(merged.getvalue())
|
||||
return response
|
||||
|
||||
|
||||
class EventArchive(generic.ArchiveIndexView):
|
||||
model = models.Event
|
||||
date_field = "start_date"
|
||||
@@ -219,3 +239,150 @@ class EventArchive(generic.ArchiveIndexView):
|
||||
messages.add_message(self.request, messages.WARNING, "No events have been found matching those criteria.")
|
||||
|
||||
return qs
|
||||
|
||||
|
||||
class EventAuthorise(generic.UpdateView):
|
||||
template_name = 'RIGS/eventauthorisation_form.html'
|
||||
success_template = 'RIGS/eventauthorisation_success.html'
|
||||
|
||||
def form_valid(self, form):
|
||||
self.object = form.save()
|
||||
|
||||
self.template_name = self.success_template
|
||||
messages.add_message(self.request, messages.SUCCESS,
|
||||
'Success! Your event has been authorised. ' +
|
||||
'You will also receive email confirmation to %s.' % (self.object.email))
|
||||
return self.render_to_response(self.get_context_data())
|
||||
|
||||
@property
|
||||
def event(self):
|
||||
return models.Event.objects.select_related('organisation', 'person', 'venue').get(pk=self.kwargs['pk'])
|
||||
|
||||
def get_object(self, queryset=None):
|
||||
return getattr(self.event, 'authorisation', None)
|
||||
|
||||
def get_form_class(self):
|
||||
return forms.InternalClientEventAuthorisationForm
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super(EventAuthorise, self).get_context_data(**kwargs)
|
||||
context['event'] = self.event
|
||||
|
||||
context['tos_url'] = settings.TERMS_OF_HIRE_URL
|
||||
return context
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
if self.get_object() is not None and self.get_object().pk is not None:
|
||||
if self.event.authorised:
|
||||
messages.add_message(self.request, messages.WARNING,
|
||||
"This event has already been authorised. "
|
||||
"Reauthorising is not necessary at this time.")
|
||||
else:
|
||||
messages.add_message(self.request, messages.WARNING,
|
||||
"This event has already been authorised, but the amount has changed. " +
|
||||
"Please check the amount and reauthorise.")
|
||||
return super(EventAuthorise, self).get(request, *args, **kwargs)
|
||||
|
||||
def get_form(self, **kwargs):
|
||||
form = super(EventAuthorise, self).get_form(**kwargs)
|
||||
form.instance.event = self.event
|
||||
form.instance.email = self.request.email
|
||||
form.instance.sent_by = self.request.sent_by
|
||||
return form
|
||||
|
||||
def dispatch(self, request, *args, **kwargs):
|
||||
# Verify our signature matches up and all is well with the integrity of the URL
|
||||
try:
|
||||
data = signing.loads(kwargs.get('hmac'))
|
||||
assert int(kwargs.get('pk')) == int(data.get('pk'))
|
||||
request.email = data['email']
|
||||
request.sent_by = models.Profile.objects.get(pk=data['sent_by'])
|
||||
except (signing.BadSignature, AssertionError, KeyError, models.Profile.DoesNotExist):
|
||||
raise SuspiciousOperation(
|
||||
"This URL is invalid. Please ask your TEC contact for a new URL")
|
||||
return super(EventAuthorise, self).dispatch(request, *args, **kwargs)
|
||||
|
||||
|
||||
class EventAuthorisationRequest(generic.FormView, generic.detail.SingleObjectMixin):
|
||||
model = models.Event
|
||||
form_class = forms.EventAuthorisationRequestForm
|
||||
template_name = 'RIGS/eventauthorisation_request.html'
|
||||
|
||||
@method_decorator(decorators.nottinghamtec_address_required)
|
||||
def dispatch(self, *args, **kwargs):
|
||||
return super(EventAuthorisationRequest, self).dispatch(*args, **kwargs)
|
||||
|
||||
@property
|
||||
def object(self):
|
||||
return self.get_object()
|
||||
|
||||
def get_success_url(self):
|
||||
if self.request.is_ajax():
|
||||
url = reverse_lazy('closemodal')
|
||||
messages.info(self.request, "location.reload()")
|
||||
else:
|
||||
url = reverse_lazy('event_detail', kwargs={
|
||||
'pk': self.object.pk,
|
||||
})
|
||||
messages.add_message(self.request, messages.SUCCESS, "Authorisation request successfully sent.")
|
||||
return url
|
||||
|
||||
def form_valid(self, form):
|
||||
email = form.cleaned_data['email']
|
||||
event = self.object
|
||||
event.auth_request_by = self.request.user
|
||||
event.auth_request_at = datetime.datetime.now()
|
||||
event.auth_request_to = email
|
||||
event.save()
|
||||
|
||||
context = {
|
||||
'object': self.object,
|
||||
'request': self.request,
|
||||
'hmac': signing.dumps({
|
||||
'pk': self.object.pk,
|
||||
'email': email,
|
||||
'sent_by': self.request.user.pk,
|
||||
}),
|
||||
}
|
||||
if event.person is not None and email == event.person.email:
|
||||
context['to_name'] = event.person.name
|
||||
elif event.organisation is not None and email == event.organisation.email:
|
||||
context['to_name'] = event.organisation.name
|
||||
|
||||
msg = EmailMultiAlternatives(
|
||||
"N%05d | %s - Event Authorisation Request" % (self.object.pk, self.object.name),
|
||||
get_template("RIGS/eventauthorisation_client_request.txt").render(context),
|
||||
to=[email],
|
||||
reply_to=[self.request.user.email],
|
||||
)
|
||||
css = staticfiles_storage.path('css/email.css')
|
||||
html = premailer.Premailer(get_template("RIGS/eventauthorisation_client_request.html").render(context),
|
||||
external_styles=css).transform()
|
||||
msg.attach_alternative(html, 'text/html')
|
||||
|
||||
msg.send()
|
||||
|
||||
return super(EventAuthorisationRequest, self).form_valid(form)
|
||||
|
||||
|
||||
class EventAuthoriseRequestEmailPreview(generic.DetailView):
|
||||
template_name = "RIGS/eventauthorisation_client_request.html"
|
||||
model = models.Event
|
||||
|
||||
def render_to_response(self, context, **response_kwargs):
|
||||
from django.contrib.staticfiles.storage import staticfiles_storage
|
||||
css = staticfiles_storage.path('css/email.css')
|
||||
response = super(EventAuthoriseRequestEmailPreview, self).render_to_response(context, **response_kwargs)
|
||||
assert isinstance(response, HttpResponse)
|
||||
response.content = premailer.Premailer(response.rendered_content, external_styles=css).transform()
|
||||
return response
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super(EventAuthoriseRequestEmailPreview, self).get_context_data(**kwargs)
|
||||
context['hmac'] = signing.dumps({
|
||||
'pk': self.object.pk,
|
||||
'email': self.request.GET.get('email', 'hello@world.test'),
|
||||
'sent_by': self.request.user.pk,
|
||||
})
|
||||
context['to_name'] = self.request.GET.get('to_name', None)
|
||||
return context
|
||||
|
||||
104
RIGS/signals.py
Normal file
@@ -0,0 +1,104 @@
|
||||
import re
|
||||
import urllib.request
|
||||
import urllib.error
|
||||
import urllib.parse
|
||||
from io import BytesIO
|
||||
|
||||
from django.db.models.signals import post_save
|
||||
from PyPDF2 import PdfFileReader, PdfFileMerger
|
||||
from django.conf import settings
|
||||
from django.contrib.staticfiles.storage import staticfiles_storage
|
||||
from django.core.mail import EmailMessage, EmailMultiAlternatives
|
||||
from django.template.loader import get_template
|
||||
from premailer import Premailer
|
||||
from z3c.rml import rml2pdf
|
||||
|
||||
from RIGS import models
|
||||
|
||||
|
||||
def send_eventauthorisation_success_email(instance):
|
||||
# Generate PDF first to prevent context conflicts
|
||||
context = {
|
||||
'object': instance.event,
|
||||
'fonts': {
|
||||
'opensans': {
|
||||
'regular': 'RIGS/static/fonts/OPENSANS-REGULAR.TTF',
|
||||
'bold': 'RIGS/static/fonts/OPENSANS-BOLD.TTF',
|
||||
}
|
||||
},
|
||||
'receipt': True,
|
||||
'current_user': False,
|
||||
}
|
||||
|
||||
template = get_template('RIGS/event_print.xml')
|
||||
merger = PdfFileMerger()
|
||||
|
||||
rml = template.render(context)
|
||||
|
||||
buffer = rml2pdf.parseString(rml)
|
||||
merger.append(PdfFileReader(buffer))
|
||||
buffer.close()
|
||||
|
||||
terms = urllib.request.urlopen(settings.TERMS_OF_HIRE_URL)
|
||||
merger.append(BytesIO(terms.read()))
|
||||
|
||||
merged = BytesIO()
|
||||
merger.write(merged)
|
||||
|
||||
# Produce email content
|
||||
context = {
|
||||
'object': instance,
|
||||
}
|
||||
|
||||
if instance.event.person is not None and instance.email == instance.event.person.email:
|
||||
context['to_name'] = instance.event.person.name
|
||||
elif instance.event.organisation is not None and instance.email == instance.event.organisation.email:
|
||||
context['to_name'] = instance.event.organisation.name
|
||||
|
||||
subject = "N%05d | %s - Event Authorised" % (instance.event.pk, instance.event.name)
|
||||
|
||||
client_email = EmailMultiAlternatives(
|
||||
subject,
|
||||
get_template("RIGS/eventauthorisation_client_success.txt").render(context),
|
||||
to=[instance.email],
|
||||
reply_to=[settings.AUTHORISATION_NOTIFICATION_ADDRESS],
|
||||
)
|
||||
|
||||
css = staticfiles_storage.path('css/email.css')
|
||||
html = Premailer(get_template("RIGS/eventauthorisation_client_success.html").render(context),
|
||||
external_styles=css).transform()
|
||||
client_email.attach_alternative(html, 'text/html')
|
||||
|
||||
escapedEventName = re.sub('[^a-zA-Z0-9 \n\.]', '', instance.event.name)
|
||||
|
||||
client_email.attach('N%05d - %s - CONFIRMATION.pdf' % (instance.event.pk, escapedEventName),
|
||||
merged.getvalue(),
|
||||
'application/pdf'
|
||||
)
|
||||
|
||||
if instance.event.mic:
|
||||
mic_email_address = instance.event.mic.email
|
||||
else:
|
||||
mic_email_address = settings.AUTHORISATION_NOTIFICATION_ADDRESS
|
||||
|
||||
mic_email = EmailMessage(
|
||||
subject,
|
||||
get_template("RIGS/eventauthorisation_mic_success.txt").render(context),
|
||||
to=[mic_email_address]
|
||||
)
|
||||
|
||||
# Now we have both emails successfully generated, send them out
|
||||
client_email.send(fail_silently=True)
|
||||
mic_email.send(fail_silently=True)
|
||||
|
||||
# Set event to booked now that it's authorised
|
||||
instance.event.status = models.Event.BOOKED
|
||||
instance.event.save()
|
||||
|
||||
|
||||
def on_revision_commit(sender, instance, created, **kwargs):
|
||||
if created:
|
||||
send_eventauthorisation_success_email(instance)
|
||||
|
||||
|
||||
post_save.connect(on_revision_commit, sender=models.EventAuthorisation)
|
||||
12
RIGS/static/css/bootstrap-select.min.css
vendored
Normal file → Executable file
1
RIGS/static/css/email.css
Normal file
@@ -0,0 +1 @@
|
||||
body{margin:0px}.main-table{width:100%;border-collapse:collapse}.client-header{background-image:url("https://www.nottinghamtec.co.uk/imgs/wof2014-1.jpg");background-color:#222;background-repeat:no-repeat;background-position:center;width:100%;margin-bottom:28px}.client-header .logos{width:100%;max-width:640px}.client-header img{height:110px}.content-container{width:100%}.content-container .content{font-family:"Open Sans", "Helvetica Neue", Helvetica, Arial, sans-serif;width:100%;max-width:600px;padding:10px;text-align:left}.content-container .content .button-container{width:100%}.content-container .content .button-container .button{padding:6px 12px;background-color:#357ebf;border-radius:4px}.content-container .content .button-container .button a{color:#fff;text-decoration:none}
|
||||
|
Before Width: | Height: | Size: 105 KiB After Width: | Height: | Size: 93 KiB |
|
Before Width: | Height: | Size: 151 KiB After Width: | Height: | Size: 129 KiB |
|
Before Width: | Height: | Size: 156 KiB After Width: | Height: | Size: 130 KiB |
|
Before Width: | Height: | Size: 132 KiB After Width: | Height: | Size: 109 KiB |
|
Before Width: | Height: | Size: 119 KiB After Width: | Height: | Size: 100 KiB |
|
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 4.8 KiB After Width: | Height: | Size: 2.2 KiB |
12
RIGS/static/js/affix.js
Normal file → Executable file
@@ -1,8 +1,8 @@
|
||||
/* ========================================================================
|
||||
* Bootstrap: affix.js v3.3.2
|
||||
* Bootstrap: affix.js v3.3.7
|
||||
* http://getbootstrap.com/javascript/#affix
|
||||
* ========================================================================
|
||||
* Copyright 2011-2015 Twitter, Inc.
|
||||
* Copyright 2011-2016 Twitter, Inc.
|
||||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
|
||||
* ======================================================================== */
|
||||
|
||||
@@ -21,14 +21,14 @@
|
||||
.on('click.bs.affix.data-api', $.proxy(this.checkPositionWithEventLoop, this))
|
||||
|
||||
this.$element = $(element)
|
||||
this.affixed =
|
||||
this.unpin =
|
||||
this.affixed = null
|
||||
this.unpin = null
|
||||
this.pinnedOffset = null
|
||||
|
||||
this.checkPosition()
|
||||
}
|
||||
|
||||
Affix.VERSION = '3.3.2'
|
||||
Affix.VERSION = '3.3.7'
|
||||
|
||||
Affix.RESET = 'affix affix-top affix-bottom'
|
||||
|
||||
@@ -78,7 +78,7 @@
|
||||
var offset = this.options.offset
|
||||
var offsetTop = offset.top
|
||||
var offsetBottom = offset.bottom
|
||||
var scrollHeight = $('body').height()
|
||||
var scrollHeight = Math.max($(document).height(), $(document.body).height())
|
||||
|
||||
if (typeof offset != 'object') offsetBottom = offsetTop = offset
|
||||
if (typeof offsetTop == 'function') offsetTop = offset.top(this.$element)
|
||||
|
||||
84
RIGS/static/js/ajax-bootstrap-select.js
Executable file → Normal file
@@ -3,16 +3,16 @@
|
||||
*
|
||||
* Extends existing [Bootstrap Select] implementations by adding the ability to search via AJAX requests as you type. Originally for CROSCON.
|
||||
*
|
||||
* @version 1.3.1
|
||||
* @version 1.4.1
|
||||
* @author Adam Heim - https://github.com/truckingsim
|
||||
* @link https://github.com/truckingsim/Ajax-Bootstrap-Select
|
||||
* @copyright 2015 Adam Heim
|
||||
* @copyright 2017 Adam Heim
|
||||
* @license Released under the MIT license.
|
||||
*
|
||||
* Contributors:
|
||||
* Mark Carver - https://github.com/markcarver
|
||||
*
|
||||
* Last build: 2015-01-06 8:43:11 PM EST
|
||||
* Last build: 2017-07-21 1:08:54 PM GMT-0400
|
||||
*/
|
||||
!(function ($, window) {
|
||||
|
||||
@@ -186,10 +186,25 @@ var AjaxBootstrapSelect = function (element, options) {
|
||||
if (dataKeys.length) {
|
||||
// Object containing the data attribute options.
|
||||
var dataOptions = {};
|
||||
var flattenedOptions = ['locale'];
|
||||
for (i = 0, l = dataKeys.length; i < l; i++) {
|
||||
var name = dataKeys[i].replace(/^abs([A-Z])/, matchToLowerCase).replace(/([A-Z])/g, '-$1').toLowerCase();
|
||||
var keys = name.split('-');
|
||||
|
||||
// Certain options should be flattened to a single object
|
||||
// and not fully expanded (such as Locale).
|
||||
if (keys[0] && keys.length > 1 && flattenedOptions.indexOf(keys[0]) !== -1) {
|
||||
var newKeys = [keys.shift()];
|
||||
var property = '';
|
||||
// Combine the remaining keys as a single property.
|
||||
for (var ii = 0; ii < keys.length; ii++) {
|
||||
property += (ii === 0 ? keys[ii] : keys[ii].charAt(0).toUpperCase() + keys[ii].slice(1));
|
||||
}
|
||||
newKeys.push(property);
|
||||
keys = newKeys;
|
||||
}
|
||||
this.log(this.LOG_DEBUG, 'Processing data attribute "data-abs-' + name + '":', data[dataKeys[i]]);
|
||||
expandObject(name.split('-'), data[dataKeys[i]], dataOptions);
|
||||
expandObject(keys, data[dataKeys[i]], dataOptions);
|
||||
}
|
||||
this.options = $.extend(true, {}, this.options, dataOptions);
|
||||
this.log(this.LOG_DEBUG, 'Merged in the data attribute options: ', dataOptions, this.options);
|
||||
@@ -315,6 +330,12 @@ AjaxBootstrapSelect.prototype.init = function () {
|
||||
plugin.log(plugin.LOG_DEBUG, 'Key ignored.');
|
||||
return;
|
||||
}
|
||||
|
||||
// Don't process if below minimum query length
|
||||
if (query.length < plugin.options.minLength) {
|
||||
plugin.list.setStatus(plugin.t('statusTooShort'));
|
||||
return;
|
||||
}
|
||||
|
||||
// Clear out any existing timer.
|
||||
clearTimeout(requestDelayTimer);
|
||||
@@ -573,8 +594,25 @@ var AjaxBootstrapSelectList = function (plugin) {
|
||||
this.title = null;
|
||||
this.selectedTextFormat = plugin.selectpicker.options.selectedTextFormat;
|
||||
|
||||
// Save initial options
|
||||
var initial_options = [];
|
||||
plugin.$element.find('option').each(function() {
|
||||
var $option = $(this);
|
||||
var value = $option.attr('value');
|
||||
initial_options.push({
|
||||
value: value,
|
||||
text: $option.text(),
|
||||
'class': $option.attr('class') || '',
|
||||
data: $option.data() || {},
|
||||
preserved: plugin.options.preserveSelected,
|
||||
selected: !!$option.attr('selected')
|
||||
});
|
||||
});
|
||||
this.cacheSet(/*query=*/'', initial_options);
|
||||
|
||||
// Preserve selected options.
|
||||
if (plugin.options.preserveSelected) {
|
||||
that.selected = initial_options;
|
||||
plugin.$element.on('change.abs.preserveSelected', function (e) {
|
||||
var $selected = plugin.$element.find(':selected');
|
||||
that.selected = [];
|
||||
@@ -644,7 +682,7 @@ AjaxBootstrapSelectList.prototype.build = function (data) {
|
||||
}
|
||||
|
||||
// Set various properties.
|
||||
$option.val(item.value).text(item.text);
|
||||
$option.val(item.value).text(item.text).attr('title', item.text);
|
||||
if (item['class'].length) {
|
||||
$option.attr('class', item['class']);
|
||||
}
|
||||
@@ -732,7 +770,13 @@ AjaxBootstrapSelectList.prototype.refresh = function (triggerChange) {
|
||||
if (!this.plugin.$element.find('option').length && emptyTitle && emptyTitle.length) {
|
||||
this.setTitle(emptyTitle);
|
||||
}
|
||||
else if (this.title) {
|
||||
else if (
|
||||
this.title ||
|
||||
(
|
||||
this.selectedTextFormat !== 'static' &&
|
||||
this.selectedTextFormat !== this.plugin.selectpicker.options.selectedTextFormat
|
||||
)
|
||||
) {
|
||||
this.restoreTitle();
|
||||
}
|
||||
this.plugin.selectpicker.refresh();
|
||||
@@ -1224,6 +1268,14 @@ $.fn.ajaxSelectPicker.defaults = {
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* @member $.fn.ajaxSelectPicker.defaults
|
||||
* @cfg {Number} minLength = 0
|
||||
* @markdown
|
||||
* Invoke a request for empty search values.
|
||||
*/
|
||||
minLength: 0,
|
||||
|
||||
/**
|
||||
* @member $.fn.ajaxSelectPicker.defaults
|
||||
* @cfg {String} ajaxSearchUrl
|
||||
@@ -1296,8 +1348,7 @@ $.fn.ajaxSelectPicker.defaults = {
|
||||
* 39: "right",
|
||||
* 38: "up",
|
||||
* 40: "down",
|
||||
* 91: "meta",
|
||||
* 229: "unknown"
|
||||
* 91: "meta"
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
@@ -1311,8 +1362,7 @@ $.fn.ajaxSelectPicker.defaults = {
|
||||
39: "right",
|
||||
38: "up",
|
||||
40: "down",
|
||||
91: "meta",
|
||||
229: "unknown"
|
||||
91: "meta"
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -1406,7 +1456,7 @@ $.fn.ajaxSelectPicker.defaults = {
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
preprocessData: function(){},
|
||||
preprocessData: function () { },
|
||||
|
||||
/**
|
||||
* @member $.fn.ajaxSelectPicker.defaults
|
||||
@@ -1434,7 +1484,7 @@ $.fn.ajaxSelectPicker.defaults = {
|
||||
* @markdown
|
||||
* Process the data returned after this plugin, but before the list is built.
|
||||
*/
|
||||
processData: function(){},
|
||||
processData: function () { },
|
||||
|
||||
/**
|
||||
* @member $.fn.ajaxSelectPicker.defaults
|
||||
@@ -1547,7 +1597,15 @@ $.fn.ajaxSelectPicker.locale['en-US'] = {
|
||||
* @markdown
|
||||
* The text to use in the status container when a request is being initiated.
|
||||
*/
|
||||
statusSearching: 'Searching...'
|
||||
statusSearching: 'Searching...',
|
||||
|
||||
/**
|
||||
* @member $.fn.ajaxSelectPicker.locale
|
||||
* @cfg {String} statusToShort = 'Please enter more characters'
|
||||
* @markdown
|
||||
* The text used in the status container when the request returns no results.
|
||||
*/
|
||||
statusTooShort: 'Please enter more characters'
|
||||
};
|
||||
$.fn.ajaxSelectPicker.locale.en = $.fn.ajaxSelectPicker.locale['en-US'];
|
||||
|
||||
|
||||
8
RIGS/static/js/alert.js
Normal file → Executable file
@@ -1,8 +1,8 @@
|
||||
/* ========================================================================
|
||||
* Bootstrap: alert.js v3.3.2
|
||||
* Bootstrap: alert.js v3.3.7
|
||||
* http://getbootstrap.com/javascript/#alerts
|
||||
* ========================================================================
|
||||
* Copyright 2011-2015 Twitter, Inc.
|
||||
* Copyright 2011-2016 Twitter, Inc.
|
||||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
|
||||
* ======================================================================== */
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
$(el).on('click', dismiss, this.close)
|
||||
}
|
||||
|
||||
Alert.VERSION = '3.3.2'
|
||||
Alert.VERSION = '3.3.7'
|
||||
|
||||
Alert.TRANSITION_DURATION = 150
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') // strip for ie7
|
||||
}
|
||||
|
||||
var $parent = $(selector)
|
||||
var $parent = $(selector === '#' ? [] : selector)
|
||||
|
||||
if (e) e.preventDefault()
|
||||
|
||||
|
||||
1485
RIGS/static/js/bootstrap-select.js
vendored
37
RIGS/static/js/button.js
Normal file → Executable file
@@ -1,8 +1,8 @@
|
||||
/* ========================================================================
|
||||
* Bootstrap: button.js v3.3.2
|
||||
* Bootstrap: button.js v3.3.7
|
||||
* http://getbootstrap.com/javascript/#buttons
|
||||
* ========================================================================
|
||||
* Copyright 2011-2015 Twitter, Inc.
|
||||
* Copyright 2011-2016 Twitter, Inc.
|
||||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
|
||||
* ======================================================================== */
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
this.isLoading = false
|
||||
}
|
||||
|
||||
Button.VERSION = '3.3.2'
|
||||
Button.VERSION = '3.3.7'
|
||||
|
||||
Button.DEFAULTS = {
|
||||
loadingText: 'loading...'
|
||||
@@ -31,7 +31,7 @@
|
||||
var val = $el.is('input') ? 'val' : 'html'
|
||||
var data = $el.data()
|
||||
|
||||
state = state + 'Text'
|
||||
state += 'Text'
|
||||
|
||||
if (data.resetText == null) $el.data('resetText', $el[val]())
|
||||
|
||||
@@ -41,10 +41,10 @@
|
||||
|
||||
if (state == 'loadingText') {
|
||||
this.isLoading = true
|
||||
$el.addClass(d).attr(d, d)
|
||||
$el.addClass(d).attr(d, d).prop(d, true)
|
||||
} else if (this.isLoading) {
|
||||
this.isLoading = false
|
||||
$el.removeClass(d).removeAttr(d)
|
||||
$el.removeClass(d).removeAttr(d).prop(d, false)
|
||||
}
|
||||
}, this), 0)
|
||||
}
|
||||
@@ -56,15 +56,19 @@
|
||||
if ($parent.length) {
|
||||
var $input = this.$element.find('input')
|
||||
if ($input.prop('type') == 'radio') {
|
||||
if ($input.prop('checked') && this.$element.hasClass('active')) changed = false
|
||||
else $parent.find('.active').removeClass('active')
|
||||
if ($input.prop('checked')) changed = false
|
||||
$parent.find('.active').removeClass('active')
|
||||
this.$element.addClass('active')
|
||||
} else if ($input.prop('type') == 'checkbox') {
|
||||
if (($input.prop('checked')) !== this.$element.hasClass('active')) changed = false
|
||||
this.$element.toggleClass('active')
|
||||
}
|
||||
if (changed) $input.prop('checked', !this.$element.hasClass('active')).trigger('change')
|
||||
$input.prop('checked', this.$element.hasClass('active'))
|
||||
if (changed) $input.trigger('change')
|
||||
} else {
|
||||
this.$element.attr('aria-pressed', !this.$element.hasClass('active'))
|
||||
this.$element.toggleClass('active')
|
||||
}
|
||||
|
||||
if (changed) this.$element.toggleClass('active')
|
||||
}
|
||||
|
||||
|
||||
@@ -104,10 +108,15 @@
|
||||
|
||||
$(document)
|
||||
.on('click.bs.button.data-api', '[data-toggle^="button"]', function (e) {
|
||||
var $btn = $(e.target)
|
||||
if (!$btn.hasClass('btn')) $btn = $btn.closest('.btn')
|
||||
var $btn = $(e.target).closest('.btn')
|
||||
Plugin.call($btn, 'toggle')
|
||||
e.preventDefault()
|
||||
if (!($(e.target).is('input[type="radio"], input[type="checkbox"]'))) {
|
||||
// Prevent double click on radios, and the double selections (so cancellation) on checkboxes
|
||||
e.preventDefault()
|
||||
// The target component still receive the focus
|
||||
if ($btn.is('input,button')) $btn.trigger('focus')
|
||||
else $btn.find('input:visible,button:visible').first().trigger('focus')
|
||||
}
|
||||
})
|
||||
.on('focus.bs.button.data-api blur.bs.button.data-api', '[data-toggle^="button"]', function (e) {
|
||||
$(e.target).closest('.btn').toggleClass('focus', /^focus(in)?$/.test(e.type))
|
||||
|
||||
14
RIGS/static/js/carousel.js
Normal file → Executable file
@@ -1,8 +1,8 @@
|
||||
/* ========================================================================
|
||||
* Bootstrap: carousel.js v3.3.2
|
||||
* Bootstrap: carousel.js v3.3.7
|
||||
* http://getbootstrap.com/javascript/#carousel
|
||||
* ========================================================================
|
||||
* Copyright 2011-2015 Twitter, Inc.
|
||||
* Copyright 2011-2016 Twitter, Inc.
|
||||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
|
||||
* ======================================================================== */
|
||||
|
||||
@@ -17,10 +17,10 @@
|
||||
this.$element = $(element)
|
||||
this.$indicators = this.$element.find('.carousel-indicators')
|
||||
this.options = options
|
||||
this.paused =
|
||||
this.sliding =
|
||||
this.interval =
|
||||
this.$active =
|
||||
this.paused = null
|
||||
this.sliding = null
|
||||
this.interval = null
|
||||
this.$active = null
|
||||
this.$items = null
|
||||
|
||||
this.options.keyboard && this.$element.on('keydown.bs.carousel', $.proxy(this.keydown, this))
|
||||
@@ -30,7 +30,7 @@
|
||||
.on('mouseleave.bs.carousel', $.proxy(this.cycle, this))
|
||||
}
|
||||
|
||||
Carousel.VERSION = '3.3.2'
|
||||
Carousel.VERSION = '3.3.7'
|
||||
|
||||
Carousel.TRANSITION_DURATION = 600
|
||||
|
||||
|
||||
17
RIGS/static/js/collapse.js
Normal file → Executable file
@@ -1,11 +1,12 @@
|
||||
/* ========================================================================
|
||||
* Bootstrap: collapse.js v3.3.2
|
||||
* Bootstrap: collapse.js v3.3.7
|
||||
* http://getbootstrap.com/javascript/#collapse
|
||||
* ========================================================================
|
||||
* Copyright 2011-2015 Twitter, Inc.
|
||||
* Copyright 2011-2016 Twitter, Inc.
|
||||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
|
||||
* ======================================================================== */
|
||||
|
||||
/* jshint latedef: false */
|
||||
|
||||
+function ($) {
|
||||
'use strict';
|
||||
@@ -16,7 +17,8 @@
|
||||
var Collapse = function (element, options) {
|
||||
this.$element = $(element)
|
||||
this.options = $.extend({}, Collapse.DEFAULTS, options)
|
||||
this.$trigger = $(this.options.trigger).filter('[href="#' + element.id + '"], [data-target="#' + element.id + '"]')
|
||||
this.$trigger = $('[data-toggle="collapse"][href="#' + element.id + '"],' +
|
||||
'[data-toggle="collapse"][data-target="#' + element.id + '"]')
|
||||
this.transitioning = null
|
||||
|
||||
if (this.options.parent) {
|
||||
@@ -28,13 +30,12 @@
|
||||
if (this.options.toggle) this.toggle()
|
||||
}
|
||||
|
||||
Collapse.VERSION = '3.3.2'
|
||||
Collapse.VERSION = '3.3.7'
|
||||
|
||||
Collapse.TRANSITION_DURATION = 350
|
||||
|
||||
Collapse.DEFAULTS = {
|
||||
toggle: true,
|
||||
trigger: '[data-toggle="collapse"]'
|
||||
toggle: true
|
||||
}
|
||||
|
||||
Collapse.prototype.dimension = function () {
|
||||
@@ -172,7 +173,7 @@
|
||||
var data = $this.data('bs.collapse')
|
||||
var options = $.extend({}, Collapse.DEFAULTS, $this.data(), typeof option == 'object' && option)
|
||||
|
||||
if (!data && options.toggle && option == 'show') options.toggle = false
|
||||
if (!data && options.toggle && /show|hide/.test(option)) options.toggle = false
|
||||
if (!data) $this.data('bs.collapse', (data = new Collapse(this, options)))
|
||||
if (typeof option == 'string') data[option]()
|
||||
})
|
||||
@@ -203,7 +204,7 @@
|
||||
|
||||
var $target = getTargetFromTrigger($this)
|
||||
var data = $target.data('bs.collapse')
|
||||
var option = data ? 'toggle' : $.extend({}, $this.data(), { trigger: this })
|
||||
var option = data ? 'toggle' : $this.data()
|
||||
|
||||
Plugin.call($target, option)
|
||||
})
|
||||
|
||||
94
RIGS/static/js/dropdown.js
Normal file → Executable file
@@ -1,8 +1,8 @@
|
||||
/* ========================================================================
|
||||
* Bootstrap: dropdown.js v3.3.2
|
||||
* Bootstrap: dropdown.js v3.3.7
|
||||
* http://getbootstrap.com/javascript/#dropdowns
|
||||
* ========================================================================
|
||||
* Copyright 2011-2015 Twitter, Inc.
|
||||
* Copyright 2011-2016 Twitter, Inc.
|
||||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
|
||||
* ======================================================================== */
|
||||
|
||||
@@ -19,7 +19,41 @@
|
||||
$(element).on('click.bs.dropdown', this.toggle)
|
||||
}
|
||||
|
||||
Dropdown.VERSION = '3.3.2'
|
||||
Dropdown.VERSION = '3.3.7'
|
||||
|
||||
function getParent($this) {
|
||||
var selector = $this.attr('data-target')
|
||||
|
||||
if (!selector) {
|
||||
selector = $this.attr('href')
|
||||
selector = selector && /#[A-Za-z]/.test(selector) && selector.replace(/.*(?=#[^\s]*$)/, '') // strip for ie7
|
||||
}
|
||||
|
||||
var $parent = selector && $(selector)
|
||||
|
||||
return $parent && $parent.length ? $parent : $this.parent()
|
||||
}
|
||||
|
||||
function clearMenus(e) {
|
||||
if (e && e.which === 3) return
|
||||
$(backdrop).remove()
|
||||
$(toggle).each(function () {
|
||||
var $this = $(this)
|
||||
var $parent = getParent($this)
|
||||
var relatedTarget = { relatedTarget: this }
|
||||
|
||||
if (!$parent.hasClass('open')) return
|
||||
|
||||
if (e && e.type == 'click' && /input|textarea/i.test(e.target.tagName) && $.contains($parent[0], e.target)) return
|
||||
|
||||
$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
|
||||
|
||||
if (e.isDefaultPrevented()) return
|
||||
|
||||
$this.attr('aria-expanded', 'false')
|
||||
$parent.removeClass('open').trigger($.Event('hidden.bs.dropdown', relatedTarget))
|
||||
})
|
||||
}
|
||||
|
||||
Dropdown.prototype.toggle = function (e) {
|
||||
var $this = $(this)
|
||||
@@ -34,7 +68,10 @@
|
||||
if (!isActive) {
|
||||
if ('ontouchstart' in document.documentElement && !$parent.closest('.navbar-nav').length) {
|
||||
// if mobile we use a backdrop because click events don't delegate
|
||||
$('<div class="dropdown-backdrop"/>').insertAfter($(this)).on('click', clearMenus)
|
||||
$(document.createElement('div'))
|
||||
.addClass('dropdown-backdrop')
|
||||
.insertAfter($(this))
|
||||
.on('click', clearMenus)
|
||||
}
|
||||
|
||||
var relatedTarget = { relatedTarget: this }
|
||||
@@ -48,7 +85,7 @@
|
||||
|
||||
$parent
|
||||
.toggleClass('open')
|
||||
.trigger('shown.bs.dropdown', relatedTarget)
|
||||
.trigger($.Event('shown.bs.dropdown', relatedTarget))
|
||||
}
|
||||
|
||||
return false
|
||||
@@ -67,57 +104,25 @@
|
||||
var $parent = getParent($this)
|
||||
var isActive = $parent.hasClass('open')
|
||||
|
||||
if ((!isActive && e.which != 27) || (isActive && e.which == 27)) {
|
||||
if (!isActive && e.which != 27 || isActive && e.which == 27) {
|
||||
if (e.which == 27) $parent.find(toggle).trigger('focus')
|
||||
return $this.trigger('click')
|
||||
}
|
||||
|
||||
var desc = ' li:not(.divider):visible a'
|
||||
var $items = $parent.find('[role="menu"]' + desc + ', [role="listbox"]' + desc)
|
||||
var desc = ' li:not(.disabled):visible a'
|
||||
var $items = $parent.find('.dropdown-menu' + desc)
|
||||
|
||||
if (!$items.length) return
|
||||
|
||||
var index = $items.index(e.target)
|
||||
|
||||
if (e.which == 38 && index > 0) index-- // up
|
||||
if (e.which == 40 && index < $items.length - 1) index++ // down
|
||||
if (!~index) index = 0
|
||||
if (e.which == 38 && index > 0) index-- // up
|
||||
if (e.which == 40 && index < $items.length - 1) index++ // down
|
||||
if (!~index) index = 0
|
||||
|
||||
$items.eq(index).trigger('focus')
|
||||
}
|
||||
|
||||
function clearMenus(e) {
|
||||
if (e && e.which === 3) return
|
||||
$(backdrop).remove()
|
||||
$(toggle).each(function () {
|
||||
var $this = $(this)
|
||||
var $parent = getParent($this)
|
||||
var relatedTarget = { relatedTarget: this }
|
||||
|
||||
if (!$parent.hasClass('open')) return
|
||||
|
||||
$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
|
||||
|
||||
if (e.isDefaultPrevented()) return
|
||||
|
||||
$this.attr('aria-expanded', 'false')
|
||||
$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
|
||||
})
|
||||
}
|
||||
|
||||
function getParent($this) {
|
||||
var selector = $this.attr('data-target')
|
||||
|
||||
if (!selector) {
|
||||
selector = $this.attr('href')
|
||||
selector = selector && /#[A-Za-z]/.test(selector) && selector.replace(/.*(?=#[^\s]*$)/, '') // strip for ie7
|
||||
}
|
||||
|
||||
var $parent = selector && $(selector)
|
||||
|
||||
return $parent && $parent.length ? $parent : $this.parent()
|
||||
}
|
||||
|
||||
|
||||
// DROPDOWN PLUGIN DEFINITION
|
||||
// ==========================
|
||||
@@ -155,7 +160,6 @@
|
||||
.on('click.bs.dropdown.data-api', '.dropdown form', function (e) { e.stopPropagation() })
|
||||
.on('click.bs.dropdown.data-api', toggle, Dropdown.prototype.toggle)
|
||||
.on('keydown.bs.dropdown.data-api', toggle, Dropdown.prototype.keydown)
|
||||
.on('keydown.bs.dropdown.data-api', '[role="menu"]', Dropdown.prototype.keydown)
|
||||
.on('keydown.bs.dropdown.data-api', '[role="listbox"]', Dropdown.prototype.keydown)
|
||||
.on('keydown.bs.dropdown.data-api', '.dropdown-menu', Dropdown.prototype.keydown)
|
||||
|
||||
}(jQuery);
|
||||
|
||||
@@ -136,4 +136,4 @@ $("#item-table tbody").sortable({
|
||||
});
|
||||
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
10
RIGS/static/js/modal.js
Normal file → Executable file
@@ -1,8 +1,8 @@
|
||||
/* ========================================================================
|
||||
* Bootstrap: modal.js v3.3.5
|
||||
* Bootstrap: modal.js v3.3.7
|
||||
* http://getbootstrap.com/javascript/#modals
|
||||
* ========================================================================
|
||||
* Copyright 2011-2015 Twitter, Inc.
|
||||
* Copyright 2011-2016 Twitter, Inc.
|
||||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
|
||||
* ======================================================================== */
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
Modal.VERSION = '3.3.5'
|
||||
Modal.VERSION = '3.3.7'
|
||||
|
||||
Modal.TRANSITION_DURATION = 300
|
||||
Modal.BACKDROP_TRANSITION_DURATION = 150
|
||||
@@ -140,7 +140,9 @@
|
||||
$(document)
|
||||
.off('focusin.bs.modal') // guard against infinite focus loop
|
||||
.on('focusin.bs.modal', $.proxy(function (e) {
|
||||
if (this.$element[0] !== e.target && !this.$element.has(e.target).length) {
|
||||
if (document !== e.target &&
|
||||
this.$element[0] !== e.target &&
|
||||
!this.$element.has(e.target).length) {
|
||||
this.$element.trigger('focus')
|
||||
}
|
||||
}, this))
|
||||
|
||||
13
RIGS/static/js/popover.js
Normal file → Executable file
@@ -1,8 +1,8 @@
|
||||
/* ========================================================================
|
||||
* Bootstrap: popover.js v3.3.2
|
||||
* Bootstrap: popover.js v3.3.7
|
||||
* http://getbootstrap.com/javascript/#popovers
|
||||
* ========================================================================
|
||||
* Copyright 2011-2015 Twitter, Inc.
|
||||
* Copyright 2011-2016 Twitter, Inc.
|
||||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
|
||||
* ======================================================================== */
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
|
||||
if (!$.fn.tooltip) throw new Error('Popover requires tooltip.js')
|
||||
|
||||
Popover.VERSION = '3.3.2'
|
||||
Popover.VERSION = '3.3.7'
|
||||
|
||||
Popover.DEFAULTS = $.extend({}, $.fn.tooltip.Constructor.DEFAULTS, {
|
||||
placement: 'right',
|
||||
@@ -75,11 +75,6 @@
|
||||
return (this.$arrow = this.$arrow || this.tip().find('.arrow'))
|
||||
}
|
||||
|
||||
Popover.prototype.tip = function () {
|
||||
if (!this.$tip) this.$tip = $(this.options.template)
|
||||
return this.$tip
|
||||
}
|
||||
|
||||
|
||||
// POPOVER PLUGIN DEFINITION
|
||||
// =========================
|
||||
@@ -90,7 +85,7 @@
|
||||
var data = $this.data('bs.popover')
|
||||
var options = typeof option == 'object' && option
|
||||
|
||||
if (!data && option == 'destroy') return
|
||||
if (!data && /destroy|hide/.test(option)) return
|
||||
if (!data) $this.data('bs.popover', (data = new Popover(this, options)))
|
||||
if (typeof option == 'string') data[option]()
|
||||
})
|
||||
|
||||
39
RIGS/static/js/scrollspy.js
Normal file → Executable file
@@ -1,8 +1,8 @@
|
||||
/* ========================================================================
|
||||
* Bootstrap: scrollspy.js v3.3.2
|
||||
* Bootstrap: scrollspy.js v3.3.7
|
||||
* http://getbootstrap.com/javascript/#scrollspy
|
||||
* ========================================================================
|
||||
* Copyright 2011-2015 Twitter, Inc.
|
||||
* Copyright 2011-2016 Twitter, Inc.
|
||||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
|
||||
* ======================================================================== */
|
||||
|
||||
@@ -14,10 +14,8 @@
|
||||
// ==========================
|
||||
|
||||
function ScrollSpy(element, options) {
|
||||
var process = $.proxy(this.process, this)
|
||||
|
||||
this.$body = $('body')
|
||||
this.$scrollElement = $(element).is('body') ? $(window) : $(element)
|
||||
this.$body = $(document.body)
|
||||
this.$scrollElement = $(element).is(document.body) ? $(window) : $(element)
|
||||
this.options = $.extend({}, ScrollSpy.DEFAULTS, options)
|
||||
this.selector = (this.options.target || '') + ' .nav li > a'
|
||||
this.offsets = []
|
||||
@@ -25,12 +23,12 @@
|
||||
this.activeTarget = null
|
||||
this.scrollHeight = 0
|
||||
|
||||
this.$scrollElement.on('scroll.bs.scrollspy', process)
|
||||
this.$scrollElement.on('scroll.bs.scrollspy', $.proxy(this.process, this))
|
||||
this.refresh()
|
||||
this.process()
|
||||
}
|
||||
|
||||
ScrollSpy.VERSION = '3.3.2'
|
||||
ScrollSpy.VERSION = '3.3.7'
|
||||
|
||||
ScrollSpy.DEFAULTS = {
|
||||
offset: 10
|
||||
@@ -41,20 +39,19 @@
|
||||
}
|
||||
|
||||
ScrollSpy.prototype.refresh = function () {
|
||||
var offsetMethod = 'offset'
|
||||
var offsetBase = 0
|
||||
var that = this
|
||||
var offsetMethod = 'offset'
|
||||
var offsetBase = 0
|
||||
|
||||
this.offsets = []
|
||||
this.targets = []
|
||||
this.scrollHeight = this.getScrollHeight()
|
||||
|
||||
if (!$.isWindow(this.$scrollElement[0])) {
|
||||
offsetMethod = 'position'
|
||||
offsetBase = this.$scrollElement.scrollTop()
|
||||
}
|
||||
|
||||
this.offsets = []
|
||||
this.targets = []
|
||||
this.scrollHeight = this.getScrollHeight()
|
||||
|
||||
var self = this
|
||||
|
||||
this.$body
|
||||
.find(this.selector)
|
||||
.map(function () {
|
||||
@@ -69,8 +66,8 @@
|
||||
})
|
||||
.sort(function (a, b) { return a[0] - b[0] })
|
||||
.each(function () {
|
||||
self.offsets.push(this[0])
|
||||
self.targets.push(this[1])
|
||||
that.offsets.push(this[0])
|
||||
that.targets.push(this[1])
|
||||
})
|
||||
}
|
||||
|
||||
@@ -99,7 +96,7 @@
|
||||
for (i = offsets.length; i--;) {
|
||||
activeTarget != targets[i]
|
||||
&& scrollTop >= offsets[i]
|
||||
&& (!offsets[i + 1] || scrollTop <= offsets[i + 1])
|
||||
&& (offsets[i + 1] === undefined || scrollTop < offsets[i + 1])
|
||||
&& this.activate(targets[i])
|
||||
}
|
||||
}
|
||||
@@ -110,8 +107,8 @@
|
||||
this.clear()
|
||||
|
||||
var selector = this.selector +
|
||||
'[data-target="' + target + '"],' +
|
||||
this.selector + '[href="' + target + '"]'
|
||||
'[data-target="' + target + '"],' +
|
||||
this.selector + '[href="' + target + '"]'
|
||||
|
||||
var active = $(selector)
|
||||
.parents('li')
|
||||
|
||||
12
RIGS/static/js/tab.js
Normal file → Executable file
@@ -1,8 +1,8 @@
|
||||
/* ========================================================================
|
||||
* Bootstrap: tab.js v3.3.2
|
||||
* Bootstrap: tab.js v3.3.7
|
||||
* http://getbootstrap.com/javascript/#tabs
|
||||
* ========================================================================
|
||||
* Copyright 2011-2015 Twitter, Inc.
|
||||
* Copyright 2011-2016 Twitter, Inc.
|
||||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
|
||||
* ======================================================================== */
|
||||
|
||||
@@ -14,10 +14,12 @@
|
||||
// ====================
|
||||
|
||||
var Tab = function (element) {
|
||||
// jscs:disable requireDollarBeforejQueryAssignment
|
||||
this.element = $(element)
|
||||
// jscs:enable requireDollarBeforejQueryAssignment
|
||||
}
|
||||
|
||||
Tab.VERSION = '3.3.2'
|
||||
Tab.VERSION = '3.3.7'
|
||||
|
||||
Tab.TRANSITION_DURATION = 150
|
||||
|
||||
@@ -65,7 +67,7 @@
|
||||
var $active = container.find('> .active')
|
||||
var transition = callback
|
||||
&& $.support.transition
|
||||
&& (($active.length && $active.hasClass('fade')) || !!container.find('> .fade').length)
|
||||
&& ($active.length && $active.hasClass('fade') || !!container.find('> .fade').length)
|
||||
|
||||
function next() {
|
||||
$active
|
||||
@@ -88,7 +90,7 @@
|
||||
element.removeClass('fade')
|
||||
}
|
||||
|
||||
if (element.parent('.dropdown-menu')) {
|
||||
if (element.parent('.dropdown-menu').length) {
|
||||
element
|
||||
.closest('li.dropdown')
|
||||
.addClass('active')
|
||||
|
||||
120
RIGS/static/js/tooltip.js
Normal file → Executable file
@@ -1,9 +1,9 @@
|
||||
/* ========================================================================
|
||||
* Bootstrap: tooltip.js v3.3.2
|
||||
* Bootstrap: tooltip.js v3.3.7
|
||||
* http://getbootstrap.com/javascript/#tooltip
|
||||
* Inspired by the original jQuery.tipsy by Jason Frame
|
||||
* ========================================================================
|
||||
* Copyright 2011-2015 Twitter, Inc.
|
||||
* Copyright 2011-2016 Twitter, Inc.
|
||||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
|
||||
* ======================================================================== */
|
||||
|
||||
@@ -15,17 +15,18 @@
|
||||
// ===============================
|
||||
|
||||
var Tooltip = function (element, options) {
|
||||
this.type =
|
||||
this.options =
|
||||
this.enabled =
|
||||
this.timeout =
|
||||
this.hoverState =
|
||||
this.type = null
|
||||
this.options = null
|
||||
this.enabled = null
|
||||
this.timeout = null
|
||||
this.hoverState = null
|
||||
this.$element = null
|
||||
this.inState = null
|
||||
|
||||
this.init('tooltip', element, options)
|
||||
}
|
||||
|
||||
Tooltip.VERSION = '3.3.2'
|
||||
Tooltip.VERSION = '3.3.7'
|
||||
|
||||
Tooltip.TRANSITION_DURATION = 150
|
||||
|
||||
@@ -50,7 +51,12 @@
|
||||
this.type = type
|
||||
this.$element = $(element)
|
||||
this.options = this.getOptions(options)
|
||||
this.$viewport = this.options.viewport && $(this.options.viewport.selector || this.options.viewport)
|
||||
this.$viewport = this.options.viewport && $($.isFunction(this.options.viewport) ? this.options.viewport.call(this, this.$element) : (this.options.viewport.selector || this.options.viewport))
|
||||
this.inState = { click: false, hover: false, focus: false }
|
||||
|
||||
if (this.$element[0] instanceof document.constructor && !this.options.selector) {
|
||||
throw new Error('`selector` option must be specified when initializing ' + this.type + ' on the window.document object!')
|
||||
}
|
||||
|
||||
var triggers = this.options.trigger.split(' ')
|
||||
|
||||
@@ -105,16 +111,20 @@
|
||||
var self = obj instanceof this.constructor ?
|
||||
obj : $(obj.currentTarget).data('bs.' + this.type)
|
||||
|
||||
if (self && self.$tip && self.$tip.is(':visible')) {
|
||||
self.hoverState = 'in'
|
||||
return
|
||||
}
|
||||
|
||||
if (!self) {
|
||||
self = new this.constructor(obj.currentTarget, this.getDelegateOptions())
|
||||
$(obj.currentTarget).data('bs.' + this.type, self)
|
||||
}
|
||||
|
||||
if (obj instanceof $.Event) {
|
||||
self.inState[obj.type == 'focusin' ? 'focus' : 'hover'] = true
|
||||
}
|
||||
|
||||
if (self.tip().hasClass('in') || self.hoverState == 'in') {
|
||||
self.hoverState = 'in'
|
||||
return
|
||||
}
|
||||
|
||||
clearTimeout(self.timeout)
|
||||
|
||||
self.hoverState = 'in'
|
||||
@@ -126,6 +136,14 @@
|
||||
}, self.options.delay.show)
|
||||
}
|
||||
|
||||
Tooltip.prototype.isInStateTrue = function () {
|
||||
for (var key in this.inState) {
|
||||
if (this.inState[key]) return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
Tooltip.prototype.leave = function (obj) {
|
||||
var self = obj instanceof this.constructor ?
|
||||
obj : $(obj.currentTarget).data('bs.' + this.type)
|
||||
@@ -135,6 +153,12 @@
|
||||
$(obj.currentTarget).data('bs.' + this.type, self)
|
||||
}
|
||||
|
||||
if (obj instanceof $.Event) {
|
||||
self.inState[obj.type == 'focusout' ? 'focus' : 'hover'] = false
|
||||
}
|
||||
|
||||
if (self.isInStateTrue()) return
|
||||
|
||||
clearTimeout(self.timeout)
|
||||
|
||||
self.hoverState = 'out'
|
||||
@@ -181,6 +205,7 @@
|
||||
.data('bs.' + this.type, this)
|
||||
|
||||
this.options.container ? $tip.appendTo(this.options.container) : $tip.insertAfter(this.$element)
|
||||
this.$element.trigger('inserted.bs.' + this.type)
|
||||
|
||||
var pos = this.getPosition()
|
||||
var actualWidth = $tip[0].offsetWidth
|
||||
@@ -188,13 +213,12 @@
|
||||
|
||||
if (autoPlace) {
|
||||
var orgPlacement = placement
|
||||
var $container = this.options.container ? $(this.options.container) : this.$element.parent()
|
||||
var containerDim = this.getPosition($container)
|
||||
var viewportDim = this.getPosition(this.$viewport)
|
||||
|
||||
placement = placement == 'bottom' && pos.bottom + actualHeight > containerDim.bottom ? 'top' :
|
||||
placement == 'top' && pos.top - actualHeight < containerDim.top ? 'bottom' :
|
||||
placement == 'right' && pos.right + actualWidth > containerDim.width ? 'left' :
|
||||
placement == 'left' && pos.left - actualWidth < containerDim.left ? 'right' :
|
||||
placement = placement == 'bottom' && pos.bottom + actualHeight > viewportDim.bottom ? 'top' :
|
||||
placement == 'top' && pos.top - actualHeight < viewportDim.top ? 'bottom' :
|
||||
placement == 'right' && pos.right + actualWidth > viewportDim.width ? 'left' :
|
||||
placement == 'left' && pos.left - actualWidth < viewportDim.left ? 'right' :
|
||||
placement
|
||||
|
||||
$tip
|
||||
@@ -235,8 +259,8 @@
|
||||
if (isNaN(marginTop)) marginTop = 0
|
||||
if (isNaN(marginLeft)) marginLeft = 0
|
||||
|
||||
offset.top = offset.top + marginTop
|
||||
offset.left = offset.left + marginLeft
|
||||
offset.top += marginTop
|
||||
offset.left += marginLeft
|
||||
|
||||
// $.fn.offset doesn't round pixel values
|
||||
// so we use setOffset directly with our own function B-0
|
||||
@@ -272,10 +296,10 @@
|
||||
this.replaceArrow(arrowDelta, $tip[0][arrowOffsetPosition], isVertical)
|
||||
}
|
||||
|
||||
Tooltip.prototype.replaceArrow = function (delta, dimension, isHorizontal) {
|
||||
Tooltip.prototype.replaceArrow = function (delta, dimension, isVertical) {
|
||||
this.arrow()
|
||||
.css(isHorizontal ? 'left' : 'top', 50 * (1 - delta / dimension) + '%')
|
||||
.css(isHorizontal ? 'top' : 'left', '')
|
||||
.css(isVertical ? 'left' : 'top', 50 * (1 - delta / dimension) + '%')
|
||||
.css(isVertical ? 'top' : 'left', '')
|
||||
}
|
||||
|
||||
Tooltip.prototype.setContent = function () {
|
||||
@@ -288,14 +312,16 @@
|
||||
|
||||
Tooltip.prototype.hide = function (callback) {
|
||||
var that = this
|
||||
var $tip = this.tip()
|
||||
var $tip = $(this.$tip)
|
||||
var e = $.Event('hide.bs.' + this.type)
|
||||
|
||||
function complete() {
|
||||
if (that.hoverState != 'in') $tip.detach()
|
||||
that.$element
|
||||
.removeAttr('aria-describedby')
|
||||
.trigger('hidden.bs.' + that.type)
|
||||
if (that.$element) { // TODO: Check whether guarding this code with this `if` is really necessary.
|
||||
that.$element
|
||||
.removeAttr('aria-describedby')
|
||||
.trigger('hidden.bs.' + that.type)
|
||||
}
|
||||
callback && callback()
|
||||
}
|
||||
|
||||
@@ -305,7 +331,7 @@
|
||||
|
||||
$tip.removeClass('in')
|
||||
|
||||
$.support.transition && this.$tip.hasClass('fade') ?
|
||||
$.support.transition && $tip.hasClass('fade') ?
|
||||
$tip
|
||||
.one('bsTransitionEnd', complete)
|
||||
.emulateTransitionEnd(Tooltip.TRANSITION_DURATION) :
|
||||
@@ -318,7 +344,7 @@
|
||||
|
||||
Tooltip.prototype.fixTitle = function () {
|
||||
var $e = this.$element
|
||||
if ($e.attr('title') || typeof ($e.attr('data-original-title')) != 'string') {
|
||||
if ($e.attr('title') || typeof $e.attr('data-original-title') != 'string') {
|
||||
$e.attr('data-original-title', $e.attr('title') || '').attr('title', '')
|
||||
}
|
||||
}
|
||||
@@ -338,7 +364,10 @@
|
||||
// width and height are missing in IE8, so compute them manually; see https://github.com/twbs/bootstrap/issues/14093
|
||||
elRect = $.extend({}, elRect, { width: elRect.right - elRect.left, height: elRect.bottom - elRect.top })
|
||||
}
|
||||
var elOffset = isBody ? { top: 0, left: 0 } : $element.offset()
|
||||
var isSvg = window.SVGElement && el instanceof window.SVGElement
|
||||
// Avoid using $.offset() on SVGs since it gives incorrect results in jQuery 3.
|
||||
// See https://github.com/twbs/bootstrap/issues/20280
|
||||
var elOffset = isBody ? { top: 0, left: 0 } : (isSvg ? null : $element.offset())
|
||||
var scroll = { scroll: isBody ? document.documentElement.scrollTop || document.body.scrollTop : $element.scrollTop() }
|
||||
var outerDims = isBody ? { width: $(window).width(), height: $(window).height() } : null
|
||||
|
||||
@@ -373,7 +402,7 @@
|
||||
var rightEdgeOffset = pos.left + viewportPadding + actualWidth
|
||||
if (leftEdgeOffset < viewportDimensions.left) { // left overflow
|
||||
delta.left = viewportDimensions.left - leftEdgeOffset
|
||||
} else if (rightEdgeOffset > viewportDimensions.width) { // right overflow
|
||||
} else if (rightEdgeOffset > viewportDimensions.right) { // right overflow
|
||||
delta.left = viewportDimensions.left + viewportDimensions.width - rightEdgeOffset
|
||||
}
|
||||
}
|
||||
@@ -399,7 +428,13 @@
|
||||
}
|
||||
|
||||
Tooltip.prototype.tip = function () {
|
||||
return (this.$tip = this.$tip || $(this.options.template))
|
||||
if (!this.$tip) {
|
||||
this.$tip = $(this.options.template)
|
||||
if (this.$tip.length != 1) {
|
||||
throw new Error(this.type + ' `template` option must consist of exactly 1 top-level element!')
|
||||
}
|
||||
}
|
||||
return this.$tip
|
||||
}
|
||||
|
||||
Tooltip.prototype.arrow = function () {
|
||||
@@ -428,7 +463,13 @@
|
||||
}
|
||||
}
|
||||
|
||||
self.tip().hasClass('in') ? self.leave(self) : self.enter(self)
|
||||
if (e) {
|
||||
self.inState.click = !self.inState.click
|
||||
if (self.isInStateTrue()) self.enter(self)
|
||||
else self.leave(self)
|
||||
} else {
|
||||
self.tip().hasClass('in') ? self.leave(self) : self.enter(self)
|
||||
}
|
||||
}
|
||||
|
||||
Tooltip.prototype.destroy = function () {
|
||||
@@ -436,6 +477,13 @@
|
||||
clearTimeout(this.timeout)
|
||||
this.hide(function () {
|
||||
that.$element.off('.' + that.type).removeData('bs.' + that.type)
|
||||
if (that.$tip) {
|
||||
that.$tip.detach()
|
||||
}
|
||||
that.$tip = null
|
||||
that.$arrow = null
|
||||
that.$viewport = null
|
||||
that.$element = null
|
||||
})
|
||||
}
|
||||
|
||||
@@ -449,7 +497,7 @@
|
||||
var data = $this.data('bs.tooltip')
|
||||
var options = typeof option == 'object' && option
|
||||
|
||||
if (!data && option == 'destroy') return
|
||||
if (!data && /destroy|hide/.test(option)) return
|
||||
if (!data) $this.data('bs.tooltip', (data = new Tooltip(this, options)))
|
||||
if (typeof option == 'string') data[option]()
|
||||
})
|
||||
|
||||
4
RIGS/static/js/transition.js
Normal file → Executable file
@@ -1,8 +1,8 @@
|
||||
/* ========================================================================
|
||||
* Bootstrap: transition.js v3.3.2
|
||||
* Bootstrap: transition.js v3.3.7
|
||||
* http://getbootstrap.com/javascript/#transitions
|
||||
* ========================================================================
|
||||
* Copyright 2011-2015 Twitter, Inc.
|
||||
* Copyright 2011-2016 Twitter, Inc.
|
||||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
|
||||
* ======================================================================== */
|
||||
|
||||
|
||||
64
RIGS/static/scss/email.scss
Normal file
@@ -0,0 +1,64 @@
|
||||
$button_color: #357ebf;
|
||||
|
||||
body{
|
||||
margin: 0px;
|
||||
}
|
||||
|
||||
.main-table{
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
|
||||
}
|
||||
|
||||
.client-header {
|
||||
background-image: url("https://www.nottinghamtec.co.uk/imgs/wof2014-1.jpg");
|
||||
background-color: #222;
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
|
||||
width: 100%;
|
||||
|
||||
margin-bottom: 28px;
|
||||
|
||||
.logos{
|
||||
width: 100%;
|
||||
max-width: 640px;
|
||||
}
|
||||
|
||||
img {
|
||||
height: 110px;
|
||||
}
|
||||
}
|
||||
|
||||
.content-container{
|
||||
width: 100%;
|
||||
|
||||
.content {
|
||||
font-family: "Open Sans", "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||
|
||||
width: 100%;
|
||||
max-width: 600px;
|
||||
padding: 10px;
|
||||
text-align: left;
|
||||
|
||||
.button-container{
|
||||
width: 100%;
|
||||
|
||||
.button {
|
||||
padding: 6px 12px;
|
||||
background-color: $button_color;
|
||||
border-radius: 4px;
|
||||
|
||||
a {
|
||||
color: #fff;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -33,13 +33,13 @@
|
||||
{% endif %}
|
||||
<p>
|
||||
<small>
|
||||
{% if version.old == None %}
|
||||
{% if version.changes.old == None %}
|
||||
Created
|
||||
{% else %}
|
||||
Changed {% include 'RIGS/version_changes.html' %} in
|
||||
{% endif %}
|
||||
|
||||
{% include 'RIGS/object_button.html' with object=version.new %}
|
||||
{% include 'RIGS/object_button.html' with object=version.changes.new %}
|
||||
{% if version.revision.comment %}
|
||||
({{ version.revision.comment }})
|
||||
{% endif %}
|
||||
|
||||
@@ -67,16 +67,16 @@
|
||||
|
||||
<tr>
|
||||
<td>{{ version.revision.date_created }}</td>
|
||||
<td><a href="{{ version.new.get_absolute_url }}">{{version.new|to_class_name}} {{ version.new.pk|stringformat:"05d" }}</a></td>
|
||||
<td>{{ version.version.pk }}|{{ version.revision.pk }}</td>
|
||||
<td><a href="{{ version.changes.new.get_absolute_url }}">{{version.changes.new|to_class_name}} {{ version.changes.new.pk|stringformat:"05d" }}</a></td>
|
||||
<td>{{ version.pk }}|{{ version.revision.pk }}</td>
|
||||
<td>{{ version.revision.user.name }}</td>
|
||||
<td>
|
||||
{% if version.old == None %}
|
||||
{{version.new|to_class_name}} Created
|
||||
{% if version.changes.old == None %}
|
||||
{{version.changes.new|to_class_name}} Created
|
||||
{% else %}
|
||||
{% include 'RIGS/version_changes.html' %}
|
||||
{% endif %} </td>
|
||||
<td>{{ version.revision.comment }}</td>
|
||||
<td>{{ version.changes.revision.comment }}</td>
|
||||
</tr>
|
||||
|
||||
{% endfor %}
|
||||
|
||||
78
RIGS/templates/RIGS/client_eventdetails.html
Normal file
@@ -0,0 +1,78 @@
|
||||
<div class="row">
|
||||
<div class="col-sm-12 col-md-6 col-lg-5">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">Contact Details</div>
|
||||
<div class="panel-body">
|
||||
<dl class="dl-horizontal">
|
||||
<dt>Person</dt>
|
||||
<dd>
|
||||
{% if event.person %}
|
||||
{{ event.person.name }}
|
||||
{% endif %}
|
||||
</dd>
|
||||
|
||||
<dt>Email</dt>
|
||||
<dd>
|
||||
<span class="overflow-ellipsis">{{ event.person.email }}</span>
|
||||
</dd>
|
||||
|
||||
<dt>Phone Number</dt>
|
||||
<dd>{{ event.person.phone }}</dd>
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
||||
{% if event.organisation %}
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">Organisation</div>
|
||||
<div class="panel-body">
|
||||
<dl class="dl-horizontal">
|
||||
<dt>Organisation</dt>
|
||||
<dd>
|
||||
{{ event.organisation.name }}
|
||||
</dd>
|
||||
|
||||
<dt>Phone Number</dt>
|
||||
<dd>{{ object.organisation.phone }}</dd>
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div class="col-sm-12 col-md-6 col-lg-7">
|
||||
<div class="panel panel-info">
|
||||
<div class="panel-heading">Event Info</div>
|
||||
<div class="panel-body">
|
||||
<dl class="dl-horizontal">
|
||||
<dt>Event Venue</dt>
|
||||
<dd>
|
||||
{% if object.venue %}
|
||||
<a href="{% url 'venue_detail' object.venue.pk %}" class="modal-href">
|
||||
{{ object.venue }}
|
||||
</a>
|
||||
{% endif %}
|
||||
</dd>
|
||||
|
||||
<dt>Status</dt>
|
||||
<dd>{{ event.get_status_display }}</dd>
|
||||
|
||||
<dd> </dd>
|
||||
|
||||
<dt>Access From</dt>
|
||||
<dd>{{ event.access_at|date:"D d M Y H:i"|default:"" }}</dd>
|
||||
|
||||
<dt>Event Starts</dt>
|
||||
<dd>{{ event.start_date|date:"D d M Y" }} {{ event.start_time|date:"H:i" }}</dd>
|
||||
|
||||
<dt>Event Ends</dt>
|
||||
<dd>{{ event.end_date|date:"D d M Y" }} {{ event.end_time|date:"H:i" }}</dd>
|
||||
|
||||
<dd> </dd>
|
||||
|
||||
<dt>Event Description</dt>
|
||||
<dd>{{ event.description|linebreaksbr }}</dd>
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -11,36 +11,9 @@
|
||||
</h1>
|
||||
</div>
|
||||
<div class="col-sm-12 text-right">
|
||||
<div class="btn-group btn-page">
|
||||
<a href="{% url 'event_update' event.pk %}" class="btn btn-default"><span
|
||||
class="glyphicon glyphicon-edit"></span> <span
|
||||
class="hidden-xs">Edit</span></a>
|
||||
{% if event.is_rig %}
|
||||
<a href="{% url 'event_print' event.pk %}" target="_blank" class="btn btn-default"><span
|
||||
class="glyphicon glyphicon-print"></span> <span
|
||||
class="hidden-xs">Print</span></a>
|
||||
{% endif %}
|
||||
<a href="{% url 'event_duplicate' event.pk %}" class="btn btn-default" title="Duplicate Rig"><span
|
||||
class="glyphicon glyphicon-duplicate"></span> <span
|
||||
class="hidden-xs">Duplicate</span></a>
|
||||
{% if event.is_rig %}
|
||||
{% if perms.RIGS.add_invoice %}
|
||||
<a id="invoiceDropdownLabel" href="{% url 'invoice_event' event.pk %}" class="btn
|
||||
{% if event.invoice and event.invoice.is_closed %}
|
||||
btn-success
|
||||
{% elif event.invoice %}
|
||||
btn-warning
|
||||
{% else %}
|
||||
btn-danger
|
||||
{% endif %}
|
||||
" title="Invoice Rig"><span
|
||||
class="glyphicon glyphicon-gbp"></span>
|
||||
<span class="hidden-xs">Invoice</span></a>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</div>
|
||||
{% include 'RIGS/event_detail_buttons.html' %}
|
||||
</div>
|
||||
|
||||
|
||||
{% endif %}
|
||||
{% if object.is_rig %}
|
||||
{# only need contact details for a rig #}
|
||||
@@ -97,6 +70,39 @@
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if event.is_rig and event.internal %}
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">Client Authorisation</div>
|
||||
<div class="panel-body">
|
||||
<dl class="dl-horizontal">
|
||||
<dt>Authorised</dt>
|
||||
<dd>{{ object.authorised|yesno:"Yes,No" }}</dd>
|
||||
|
||||
<dt>Authorised by</dt>
|
||||
<dd>
|
||||
{% if object.authorisation %}
|
||||
{{ object.authorisation.name }}
|
||||
(<a href="mailto:{{ object.authorisation.email }}">{{ object.authorisation.email }}</a>)
|
||||
{% endif %}
|
||||
</dd>
|
||||
|
||||
<dt>Authorised at</dt>
|
||||
<dd>{{ object.authorisation.last_edited_at }}</dd>
|
||||
|
||||
<dt>Authorised amount</dt>
|
||||
<dd>
|
||||
{% if object.authorisation %}
|
||||
£ {{ object.authorisation.amount|floatformat:"2" }}
|
||||
{% endif %}
|
||||
</dd>
|
||||
|
||||
<dt>Requested by</dt>
|
||||
<dd>{{ object.authorisation.sent_by }}</dd>
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
<div class="col-sm-12 {% if event.is_rig %}col-md-6 col-lg-7{% endif %}">
|
||||
@@ -175,8 +181,25 @@
|
||||
{% endif %}
|
||||
|
||||
{% if event.is_rig %}
|
||||
<dt>PO</dt>
|
||||
<dd>{{ object.purchase_order }}</dd>
|
||||
<dd> </dd>
|
||||
|
||||
{% if object.internal %}
|
||||
<dt>Authorisation Request</dt>
|
||||
<dd>{{ object.auth_request_to|yesno:"Yes,No" }}</dd>
|
||||
|
||||
<dt>By</dt>
|
||||
<dd>{{ object.auth_request_by }}</dd>
|
||||
|
||||
<dt>At</dt>
|
||||
<dd>{{ object.auth_request_at|date:"D d M Y H:i"|default:"" }}</dd>
|
||||
|
||||
<dt>To</dt>
|
||||
<dd>{{ object.auth_request_to }}</dd>
|
||||
|
||||
{% else %}
|
||||
<dt>PO</dt>
|
||||
<dd>{{ object.purchase_order }}</dd>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</dl>
|
||||
</div>
|
||||
@@ -184,34 +207,7 @@
|
||||
</div>
|
||||
{% if not request.is_ajax %}
|
||||
<div class="col-sm-12 text-right">
|
||||
<div class="btn-group btn-page">
|
||||
<a href="{% url 'event_update' event.pk %}" class="btn btn-default"><span
|
||||
class="glyphicon glyphicon-edit"></span> <span
|
||||
class="hidden-xs">Edit</span></a>
|
||||
{% if event.is_rig %}
|
||||
<a href="{% url 'event_print' event.pk %}" target="_blank" class="btn btn-default"><span
|
||||
class="glyphicon glyphicon-print"></span> <span
|
||||
class="hidden-xs">Print</span></a>
|
||||
{% endif %}
|
||||
<a href="{% url 'event_duplicate' event.pk %}" class="btn btn-default" title="Duplicate Rig"><span
|
||||
class="glyphicon glyphicon-duplicate"></span> <span
|
||||
class="hidden-xs">Duplicate</span></a>
|
||||
{% if event.is_rig %}
|
||||
{% if perms.RIGS.add_invoice %}
|
||||
<a id="invoiceDropdownLabel" href="{% url 'invoice_event' event.pk %}" class="btn
|
||||
{% if event.invoice and event.invoice.is_closed %}
|
||||
btn-success
|
||||
{% elif event.invoice %}
|
||||
btn-warning
|
||||
{% else %}
|
||||
btn-danger
|
||||
{% endif %}
|
||||
" title="Invoice Rig"><span
|
||||
class="glyphicon glyphicon-gbp"></span>
|
||||
<span class="hidden-xs">Invoice</span></a>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</div>
|
||||
{% include 'RIGS/event_detail_buttons.html' %}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if event.is_rig %}
|
||||
@@ -229,34 +225,7 @@
|
||||
</div>
|
||||
{% if not request.is_ajax %}
|
||||
<div class="col-sm-12 text-right">
|
||||
<div class="btn-group btn-page">
|
||||
<a href="{% url 'event_update' event.pk %}" class="btn btn-default"><span
|
||||
class="glyphicon glyphicon-edit"></span> <span
|
||||
class="hidden-xs">Edit</span></a>
|
||||
{% if event.is_rig %}
|
||||
<a href="{% url 'event_print' event.pk %}" target="_blank" class="btn btn-default"><span
|
||||
class="glyphicon glyphicon-print"></span> <span
|
||||
class="hidden-xs">Print</span></a>
|
||||
{% endif %}
|
||||
<a href="{% url 'event_duplicate' event.pk %}" class="btn btn-default" title="Duplicate Rig"><span
|
||||
class="glyphicon glyphicon-duplicate"></span> <span
|
||||
class="hidden-xs">Duplicate</span></a>
|
||||
{% if event.is_rig %}
|
||||
{% if perms.RIGS.add_invoice %}
|
||||
<a id="invoiceDropdownLabel" href="{% url 'invoice_event' event.pk %}" class="btn
|
||||
{% if event.invoice and event.invoice.is_closed %}
|
||||
btn-success
|
||||
{% elif event.invoice %}
|
||||
btn-warning
|
||||
{% else %}
|
||||
btn-danger
|
||||
{% endif %}
|
||||
" title="Invoice Rig"><span
|
||||
class="glyphicon glyphicon-gbp"></span>
|
||||
<span class="hidden-xs">Invoice</span></a>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</div>
|
||||
{% include 'RIGS/event_detail_buttons.html' %}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
54
RIGS/templates/RIGS/event_detail_buttons.html
Normal file
@@ -0,0 +1,54 @@
|
||||
<div class="btn-group btn-page">
|
||||
<a href="{% url 'event_update' event.pk %}" class="btn btn-default"><span
|
||||
class="glyphicon glyphicon-edit"></span> <span
|
||||
class="hidden-xs">Edit</span></a>
|
||||
{% if event.is_rig %}
|
||||
<a href="{% url 'event_print' event.pk %}" target="_blank" class="btn btn-default"><span
|
||||
class="glyphicon glyphicon-print"></span> <span
|
||||
class="hidden-xs">Print</span></a>
|
||||
{% endif %}
|
||||
<a href="{% url 'event_duplicate' event.pk %}" class="btn btn-default" title="Duplicate Rig"><span
|
||||
class="glyphicon glyphicon-duplicate"></span> <span
|
||||
class="hidden-xs">Duplicate</span></a>
|
||||
{% if event.is_rig %}
|
||||
{% if event.internal %}
|
||||
<a class="btn btn-default item-add modal-href event-authorise-request
|
||||
{% if event.authorised %}
|
||||
btn-success
|
||||
{% elif event.authorisation and event.authorisation.amount != event.total and event.authorisation.last_edited_at > event.auth_request_at %}
|
||||
btn-warning
|
||||
{% elif event.auth_request_to %}
|
||||
btn-info
|
||||
{% endif %}
|
||||
"
|
||||
href="{% url 'event_authorise_request' object.pk %}">
|
||||
<span class="glyphicon glyphicon-send"></span>
|
||||
<span class="hidden-xs">
|
||||
{% if event.authorised %}
|
||||
Authorised
|
||||
{% elif event.authorisation and event.authorisation.amount != event.total and event.authorisation.last_edited_at > event.auth_request_at %}
|
||||
Authorisation Issue
|
||||
{% elif event.auth_request_to %}
|
||||
Awaiting Authorisation
|
||||
{% else %}
|
||||
Request Authorisation
|
||||
{% endif %}
|
||||
</span>
|
||||
</a>
|
||||
{% endif %}
|
||||
|
||||
{% if perms.RIGS.add_invoice %}
|
||||
<a id="invoiceDropdownLabel" href="{% url 'invoice_event' event.pk %}" class="btn
|
||||
{% if event.invoice and event.invoice.is_closed %}
|
||||
btn-success
|
||||
{% elif event.invoice %}
|
||||
btn-warning
|
||||
{% else %}
|
||||
btn-danger
|
||||
{% endif %}
|
||||
" title="Invoice Rig"><span
|
||||
class="glyphicon glyphicon-gbp"></span>
|
||||
<span class="hidden-xs">Invoice</span></a>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</div>
|
||||
@@ -289,10 +289,10 @@
|
||||
<div class="col-sm-8">
|
||||
<div class="row">
|
||||
<div class="col-sm-12 col-md-7" data-toggle="tooltip" title="Start date for event, required">
|
||||
{% render_field form.start_date type="date" class+="form-control" %}
|
||||
{% render_field form.start_date class+="form-control" %}
|
||||
</div>
|
||||
<div class="col-sm-12 col-md-5" data-toggle="tooltip" title="Start time of event, can be left blank">
|
||||
{% render_field form.start_time type="time" class+="form-control" %}
|
||||
{% render_field form.start_time class+="form-control" %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -304,10 +304,10 @@
|
||||
<div class="col-sm-8">
|
||||
<div class="row">
|
||||
<div class="col-sm-12 col-md-7" data-toggle="tooltip" title="End date of event, leave blank if unknown or same as start date">
|
||||
{% render_field form.end_date type="date" class+="form-control" %}
|
||||
{% render_field form.end_date class+="form-control" %}
|
||||
</div>
|
||||
<div class="col-sm-12 col-md-5" data-toggle="tooltip" title="End time of event, leave blank if unknown">
|
||||
{% render_field form.end_time type="time" class+="form-control" %}
|
||||
{% render_field form.end_time class+="form-control" %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -329,7 +329,7 @@
|
||||
class="col-sm-4 control-label">{{ form.access_at.label }}</label>
|
||||
|
||||
<div class="col-sm-8">
|
||||
{% render_field form.access_at type="datetime-local" class+="form-control" %}
|
||||
{% render_field form.access_at class+="form-control" %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group" data-toggle="tooltip" title="The date/time at which crew should meet for this event">
|
||||
@@ -337,7 +337,7 @@
|
||||
class="col-sm-4 control-label">{{ form.meet_at.label }}</label>
|
||||
|
||||
<div class="col-sm-8">
|
||||
{% render_field form.meet_at type="datetime-local" class+="form-control" %}
|
||||
{% render_field form.meet_at class+="form-control" %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
@@ -398,6 +398,7 @@
|
||||
{% render_field form.collector class+="form-control" %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group" data-toggle="tooltip" title="The purchase order number (for external clients)">
|
||||
<label for="{{ form.purchase_order.id_for_label }}"
|
||||
class="col-sm-4 control-label">{{ form.purchase_order.label }}</label>
|
||||
|
||||
@@ -54,20 +54,22 @@
|
||||
<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>
|
||||
{{ object.name }}
|
||||
{% if object.is_rig and perms.RIGS.view_event and object.authorised %}
|
||||
<span class="glyphicon glyphicon-check"></span>
|
||||
{% endif %}
|
||||
|
||||
</td>
|
||||
<td>{{ object.sum_total|floatformat:2 }}</td>
|
||||
<td>
|
||||
{{ object.organisation.name }}
|
||||
<br>
|
||||
<span class="text-muted">{{ object.internal|yesno:'Internal,External' }}</span>
|
||||
</td>
|
||||
<td>
|
||||
{{ object.sum_total|floatformat:2 }}
|
||||
<br />
|
||||
<span class="text-muted">{% if not object.internal %}{{ object.purchase_order }}{% endif %}</span>
|
||||
</td>
|
||||
<td class="text-center">
|
||||
{% if object.mic %}
|
||||
{{ object.mic.initials }}<br>
|
||||
@@ -77,7 +79,10 @@
|
||||
{% 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">
|
||||
<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>
|
||||
@@ -92,4 +97,4 @@
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endblock %}
|
||||
{% endblock %}
|
||||
|
||||
@@ -22,21 +22,21 @@
|
||||
<paraStyle name="style.Heading2" fontName="OpenSans-Bold" fontSize="10" spaceAfter="2"/>
|
||||
<paraStyle name="style.Heading3" fontName="OpenSans" fontSize="10" spaceAfter="0"/>
|
||||
<paraStyle name="center" alignment="center"/>
|
||||
<paraStyle name="invoice-head" alignment="center" fontName="OpenSans-Bold" fontSize="16" leading="18" spaceAfter="0"/>
|
||||
<paraStyle name="page-head" alignment="center" fontName="OpenSans-Bold" fontSize="16" leading="18" spaceAfter="0"/>
|
||||
|
||||
<paraStyle name="style.event_description" fontName="OpenSans" textColor="DarkGray" />
|
||||
<paraStyle name="style.item_description" fontName="OpenSans" textColor="DarkGray" leftIndent="10" />
|
||||
<paraStyle name="style.specific_description" fontName="OpenSans" textColor="DarkGray" fontSize="10" />
|
||||
<paraStyle name="style.times" fontName="OpenSans" fontSize="10" />
|
||||
<paraStyle name="style.invoice_titles" fontName="OpenSans-Bold" fontSize="10" />
|
||||
<paraStyle name="style.invoice_numbers" fontName="OpenSans" fontSize="10" />
|
||||
<paraStyle name="style.head_titles" fontName="OpenSans-Bold" fontSize="10" />
|
||||
<paraStyle name="style.head_numbers" fontName="OpenSans" fontSize="10" />
|
||||
|
||||
<blockTableStyle id="eventSpecifics">
|
||||
<blockValign value="top"/>
|
||||
<lineStyle kind="LINEAFTER" colorName="LightGrey" start="0,0" stop="1,0" thickness="1"/>
|
||||
</blockTableStyle>
|
||||
|
||||
<blockTableStyle id="invoiceLayout">
|
||||
<blockTableStyle id="headLayout">
|
||||
<blockValign value="top"/>
|
||||
|
||||
</blockTableStyle>
|
||||
@@ -100,10 +100,11 @@
|
||||
|
||||
|
||||
<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>
|
||||
<setFont name="OpenSans" size="7" />
|
||||
<drawCenteredString x="302.5" y="26">[Paperwork generated by {{current_user.name}} | {% now "d/m/Y H:i" %} | {{object.current_version_id}}]</drawCenteredString>
|
||||
<drawCenteredString x="302.5" y="26">
|
||||
[Paperwork generated{% if current_user %} by {{current_user.name}} |{% endif %} {% now "d/m/Y H:i" %} | {{object.current_version_id}}]
|
||||
</drawCenteredString>
|
||||
</pageGraphics>
|
||||
|
||||
<frame id="main" x1="50" y1="65" width="495" height="645"/>
|
||||
@@ -115,10 +116,11 @@
|
||||
<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>
|
||||
<setFont name="OpenSans" size="7" />
|
||||
<drawCenteredString x="302.5" y="26">[Paperwork generated by {{current_user.name}} | {% now "d/m/Y H:i" %} | {{object.current_version_id}}]</drawCenteredString>
|
||||
<drawCenteredString x="302.5" y="26">
|
||||
[Paperwork generated{% if current_user %} by {{current_user.name}} |{% endif %} {% now "d/m/Y H:i" %} | {{object.current_version_id}}]
|
||||
</drawCenteredString>
|
||||
</pageGraphics>
|
||||
<frame id="main" x1="50" y1="65" width="495" height="727"/>
|
||||
</pageTemplate>
|
||||
@@ -128,4 +130,4 @@
|
||||
{% include "RIGS/event_print_page.xml" %}
|
||||
</story>
|
||||
|
||||
</document>
|
||||
</document>
|
||||
|
||||
@@ -1,59 +1,70 @@
|
||||
<setNextFrame name="main"/>
|
||||
<nextFrame/>
|
||||
|
||||
{% if invoice %}
|
||||
|
||||
<blockTable style="invoiceLayout" colWidths="330,165">
|
||||
<blockTable style="headLayout" colWidths="330,165">
|
||||
<tr>
|
||||
<td>
|
||||
{% endif %}
|
||||
<h1><b>N{{ object.pk|stringformat:"05d" }}:</b> '{{ object.name }}'<small></small></h1>
|
||||
|
||||
<h1><b>N{{ object.pk|stringformat:"05d" }}:</b> '{{ object.name }}'<small></small></h1>
|
||||
|
||||
<para style="style.event_description">
|
||||
<b>{{object.start_date|date:"D jS N Y"}}</b>
|
||||
</para>
|
||||
|
||||
<keepInFrame>
|
||||
<para style="style.event_description">
|
||||
{{ object.description|default_if_none:""|linebreaksbr }}
|
||||
<para style="style.event_description">
|
||||
<b>{{object.start_date|date:"D jS N Y"}}</b>
|
||||
</para>
|
||||
</keepInFrame>
|
||||
|
||||
{% if invoice %}
|
||||
|
||||
<keepInFrame>
|
||||
<para style="style.event_description">
|
||||
{{ object.description|default_if_none:""|linebreaksbr }}
|
||||
</para>
|
||||
</keepInFrame>
|
||||
</td>
|
||||
<td>
|
||||
<para style="invoice-head">INVOICE</para>
|
||||
<spacer length="10"/>
|
||||
<blockTable style="eventDetails" colWidths="100,175">
|
||||
<tr>
|
||||
<td><para style="invoice_titles">Invoice Number</para></td>
|
||||
<td>
|
||||
<para style="invoice_numbers">{{ invoice.pk|stringformat:"05d" }}</para>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><para style="invoice_titles">Invoice Date</para></td>
|
||||
<td>
|
||||
<para style="invoice_numbers">{{ invoice.invoice_date|date:"d/m/Y" }}</para>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><para style="invoice_titles">PO Number</para></td>
|
||||
<td>
|
||||
<para style="invoice_numbers">{{ object.purchase_order|default_if_none:"" }}</para>
|
||||
</td>
|
||||
</tr>
|
||||
{% if invoice %}
|
||||
<para style="page-head">INVOICE</para>
|
||||
<spacer length="10"/>
|
||||
<blockTable style="eventDetails" colWidths="100,175">
|
||||
<tr>
|
||||
<td><para style="head_titles">Invoice Number</para></td>
|
||||
<td>
|
||||
<para style="head_numbers">{{ invoice.pk|stringformat:"05d" }}</para>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><para style="head_titles">Invoice Date</para></td>
|
||||
<td>
|
||||
<para style="head_numbers">{{ invoice.invoice_date|date:"d/m/Y" }}</para>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
</blockTable>
|
||||
{% if not object.internal %}
|
||||
<tr>
|
||||
<td><para style="head_titles">PO</para></td>
|
||||
<td><para style="head_numbers">{{ object.purchase_order }}</para></td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
</blockTable>
|
||||
|
||||
{% elif quote %}
|
||||
|
||||
<para style="page-head">QUOTE</para>
|
||||
<spacer length="10"/>
|
||||
<blockTable style="eventDetails" colWidths="100,175">
|
||||
<tr>
|
||||
<td><para style="head_titles">Quote Date</para></td>
|
||||
<td>
|
||||
<para style="head_numbers">{% now "d/m/Y" %}</para>
|
||||
</td>
|
||||
</tr>
|
||||
</blockTable>
|
||||
|
||||
{% elif receipt %}
|
||||
|
||||
<para style="page-head">CONFIRMATION</para>
|
||||
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
</blockTable>
|
||||
|
||||
|
||||
{% endif %}
|
||||
|
||||
<spacer length="15"/>
|
||||
<blockTable style="eventSpecifics" colWidths="165,165,165">
|
||||
<tr>
|
||||
@@ -189,16 +200,16 @@
|
||||
<keepTogether>
|
||||
<blockTable style="totalTable" colWidths="300,115,80">
|
||||
<tr>
|
||||
<td>{% if not invoice %}VAT Registration Number: 170734807{% endif %}</td>
|
||||
<td>{% if quote %}VAT Registration Number: 170734807{% endif %}</td>
|
||||
<td>Total (ex. VAT)</td>
|
||||
<td>£ {{ object.sum_total|floatformat:2 }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
{% if not invoice %}
|
||||
<para>
|
||||
<b>The full hire fee is payable at least 10 days before the event.</b>
|
||||
</para>
|
||||
{% if quote %}
|
||||
<para>
|
||||
This quote is valid for 30 days unless otherwise arranged.
|
||||
</para>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>VAT @ {{ object.vat_rate.as_percent|floatformat:2 }}%</td>
|
||||
@@ -206,113 +217,140 @@
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
<para>
|
||||
{% if invoice %}
|
||||
VAT Registration Number: 170734807
|
||||
{% if quote %}
|
||||
<para>
|
||||
<b>The full hire fee is payable at least 10 days before the event.</b>
|
||||
</para>
|
||||
{% endif %}
|
||||
</td>
|
||||
{% if invoice %}
|
||||
<td>Total</td>
|
||||
<td>£ {{ object.total|floatformat:2 }}</td>
|
||||
{% else %}
|
||||
<td>
|
||||
<para>
|
||||
<b>Total</b>
|
||||
</para>
|
||||
</td>
|
||||
<td>
|
||||
<para>
|
||||
<b>£ {{ object.total|floatformat:2 }}</b>
|
||||
</para>
|
||||
</td>
|
||||
{% endif %}
|
||||
</tr>
|
||||
</blockTable>
|
||||
</keepTogether>
|
||||
|
||||
{% if invoice %}
|
||||
<spacer length="15"/>
|
||||
<keepTogether>
|
||||
<h2>Payments</h2>
|
||||
<blockTable style="itemTable" colWidths="300,115,80">
|
||||
<tr>
|
||||
<td>
|
||||
<para>
|
||||
<b>Method</b>
|
||||
</para>
|
||||
</td>
|
||||
<td>
|
||||
<para>
|
||||
<b>Date</b>
|
||||
</para>
|
||||
</td>
|
||||
<td>
|
||||
<para>
|
||||
<b>Amount</b>
|
||||
</para>
|
||||
</td>
|
||||
</tr>
|
||||
{% for payment in object.invoice.payment_set.all %}
|
||||
<tr>
|
||||
<td>{{ payment.get_method_display }}</td>
|
||||
<td>{{ payment.date }}</td>
|
||||
<td>£ {{ payment.amount|floatformat:2 }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</blockTable>
|
||||
<blockTable style="totalTable" colWidths="300,115,80">
|
||||
<tr>
|
||||
<td></td>
|
||||
<td>Payment Total</td>
|
||||
<td>£ {{ object.invoice.payment_total|floatformat:2 }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td></td>
|
||||
<td>
|
||||
<para>
|
||||
<b>Balance</b> (ex. VAT)
|
||||
</para>
|
||||
</td>
|
||||
<td>
|
||||
<para>
|
||||
<b>£ {{ object.invoice.balance|floatformat:2 }}</b>
|
||||
</para>
|
||||
</td>
|
||||
</tr>
|
||||
</blockTable>
|
||||
</keepTogether>
|
||||
{% endif %}
|
||||
|
||||
<keepTogether>
|
||||
<blockTable style="infoTable">>
|
||||
{% if quote %}
|
||||
<tr><td><spacer length="15" /></td></tr>
|
||||
<tr>
|
||||
<td>
|
||||
{% if object.internal %}
|
||||
<para>Bookings will
|
||||
<b>not</b>
|
||||
be confirmed until the event is authorised online.
|
||||
</para>
|
||||
{% else %}
|
||||
<b>This contract is not an invoice.</b>
|
||||
<para>Bookings will
|
||||
<b>not</b>
|
||||
be confirmed until we have received written confirmation and a Purchase Order.
|
||||
</para>
|
||||
{% endif %}
|
||||
</para>
|
||||
|
||||
</td>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>24 Hour Emergency Contacts: 07825 065681 and 07825 065678</td>
|
||||
</tr>
|
||||
{% else %}
|
||||
<tr>
|
||||
<td>
|
||||
<para>
|
||||
<b>Total</b>
|
||||
</para>
|
||||
<para>VAT Registration Number: 170734807</para>
|
||||
</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
|
||||
<tr><td><spacer length="15" /></td></tr>
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
<para>
|
||||
<b>£ {{ object.total|floatformat:2 }}</b>
|
||||
</para>
|
||||
{% if object.internal and object.authorised %}
|
||||
<para>
|
||||
Event authorised online by {{ object.authorisation.name }} ({{ object.authorisation.email }}) at
|
||||
{{ object.authorisation.last_edited_at }}.
|
||||
</para>
|
||||
|
||||
<blockTable colWidths="165,165,165">
|
||||
<tr>
|
||||
<td><para><b>University ID</b></para></td>
|
||||
<td><para><b>Account Code</b></para></td>
|
||||
<td><para><b>Authorised Amount</b></para></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{{ object.authorisation.uni_id }}</td>
|
||||
<td>{{ object.authorisation.account_code }}</td>
|
||||
<td>£ {{ object.authorisation.amount|floatformat:2 }}</td>
|
||||
</tr>
|
||||
</blockTable>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
</blockTable>
|
||||
</keepTogether>
|
||||
{% if not invoice %}
|
||||
<keepTogether>
|
||||
<blockTable style="infoTable">
|
||||
<tr>
|
||||
<td>
|
||||
<para>Bookings will
|
||||
<b>not</b>
|
||||
be confirmed until payment is received and the contract is signed.
|
||||
</para>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>24 Hour Emergency Contacts: 07825 065681 and 07825 065678</td>
|
||||
</tr>
|
||||
</blockTable>
|
||||
</keepTogether>
|
||||
<spacer length="15"/>
|
||||
<keepTogether>
|
||||
|
||||
<para style="blockPara">
|
||||
<b>To be signed on booking:</b>
|
||||
</para>
|
||||
{% if object.organisation.union_account %}
|
||||
<para style="blockPara">
|
||||
<i>
|
||||
I agree that am authorised to sign this invoice. I agree that I am the President/Treasurer of the hirer, or
|
||||
that I have provided written permission from either the President or Treasurer of the hirer stating that I can
|
||||
sign for this invoice.
|
||||
</i>
|
||||
</para>
|
||||
<para style="blockPara">
|
||||
<i>
|
||||
I have read, understood and fully accepted the current conditions of hire. I agree to return any dry hire
|
||||
items to TEC PA & Lighting in the same condition at the end of the hire period.
|
||||
</i>
|
||||
</para>
|
||||
|
||||
<para style="blockPara">
|
||||
<b>
|
||||
Conditions of hire attached and available on the TEC PA & Lighting website. E&OE
|
||||
</b>
|
||||
</para>
|
||||
|
||||
<para style="blockPara">
|
||||
Please return this form directly to TEC PA & Lighting and not the Students' Union Finance Department.
|
||||
</para>
|
||||
|
||||
<blockTable style="signatureTable" colWidths="70,100,325">
|
||||
<tr>
|
||||
<td>Account Code</td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
</tr>
|
||||
</blockTable>
|
||||
|
||||
{% else %}
|
||||
<para style="blockPara">
|
||||
<i>
|
||||
I, the hirer, have read, understand and fully accept the current conditions of hire. This document forms a
|
||||
binding contract between TEC PA & Lighting and the hirer, the aforementioned conditions of hire forming
|
||||
an integral part of it.
|
||||
</i>
|
||||
</para>
|
||||
|
||||
<para style="blockPara">
|
||||
<b>
|
||||
Conditions of hire attached and available on the TEC PA & Lighting website. E&OE
|
||||
</b>
|
||||
</para>
|
||||
|
||||
{% include "RIGS/event_print_signature.xml" %}
|
||||
<spacer length="10"/>
|
||||
<para style="blockPara">
|
||||
<b>To be signed on the day of the event/hire:</b>
|
||||
</para>
|
||||
<para style="blockPara">
|
||||
<i>
|
||||
I, the hirer, have received the goods/services as requested and in good order. I agree to return any dry hire
|
||||
items to TEC PA & Lighting in a similar condition at the end of the hire period.
|
||||
</i>
|
||||
</para>
|
||||
{% endif %}
|
||||
|
||||
{% include "RIGS/event_print_signature.xml" %}
|
||||
</keepTogether>
|
||||
{% endif %}
|
||||
<namedString id="lastPage"><pageNumber/></namedString>
|
||||
<namedString id="lastPage"><pageNumber/></namedString>
|
||||
|
||||
@@ -33,13 +33,18 @@
|
||||
</td>
|
||||
<td>
|
||||
<h4>
|
||||
<a {% if perms.RIGS.view_event %}href="{% url 'event_detail' event.pk %}" {% endif %}>{{ event.name }}</a>
|
||||
<a {% if perms.RIGS.view_event %}href="{% url 'event_detail' event.pk %}" {% endif %}>
|
||||
{{ event.name }}
|
||||
</a>
|
||||
{% if event.venue %}
|
||||
<small>at {{ event.venue }}</small>
|
||||
{% endif %}
|
||||
{% if event.dry_hire %}
|
||||
<span class="label label-default">Dry Hire</span>
|
||||
{% endif %}
|
||||
{% if event.is_rig and perms.RIGS.view_event and event.authorised %}
|
||||
<span class="glyphicon glyphicon-check"></span>
|
||||
{% endif %}
|
||||
</h4>
|
||||
{% if event.is_rig and not event.cancelled %}
|
||||
<h5>
|
||||
@@ -99,4 +104,4 @@
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
41
RIGS/templates/RIGS/eventauthorisation_client_request.html
Normal file
@@ -0,0 +1,41 @@
|
||||
{% extends 'base_client_email.html' %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<p>Hi {{ to_name|default:"there" }},</p>
|
||||
|
||||
<p><b>{{ request.user.get_full_name }}</b> has requested that you authorise <b>N{{ object.pk|stringformat:"05d" }}
|
||||
| {{ object.name }}</b>{% if not to_name %} on behalf of <b>{{ object.person.name }}</b>{% endif %}.</p>
|
||||
|
||||
<p>
|
||||
Please find the link below to complete the event booking process.
|
||||
{% if object.event.organisation and object.event.organisation.union_account %}{# internal #}
|
||||
Remember that only Presidents or Treasurers are allowed to sign off payments. You may need to forward
|
||||
this
|
||||
email on.
|
||||
{% endif %}
|
||||
</p>
|
||||
|
||||
|
||||
<table class="button-container" width="100%" border="0" cellspacing="0" cellpadding="0">
|
||||
<tr>
|
||||
<td align="center">
|
||||
<table border="0" cellspacing="0" cellpadding="0">
|
||||
<tr>
|
||||
<td class="button" align="center">
|
||||
<a href="{{ request.scheme }}://{{ request.get_host }}{% url 'event_authorise' object.pk hmac %}">
|
||||
Complete Authorisation Form
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
|
||||
<p>Your event will not be booked until you complete this form.</p>
|
||||
|
||||
<p>TEC PA & Lighting<br/>
|
||||
|
||||
{% endblock %}
|
||||
16
RIGS/templates/RIGS/eventauthorisation_client_request.txt
Normal file
@@ -0,0 +1,16 @@
|
||||
Hi {{ to_name|default:"there" }},
|
||||
|
||||
{{ request.user.get_full_name }} has requested that you authorise N{{ object.pk|stringformat:"05d" }}| {{ object.name }}{% if not to_name %} on behalf of {{ object.person.name }}{% endif %}.
|
||||
|
||||
Please find the link below to complete the event booking process.
|
||||
{% if object.event.organisation and object.event.organisation.union_account %}{# internal #}
|
||||
Remember that only Presidents or Treasurers are allowed to sign off payments. You may need to forward
|
||||
this
|
||||
email on.
|
||||
{% endif %}
|
||||
|
||||
{{ request.scheme }}://{{ request.get_host }}{% url 'event_authorise' object.pk hmac %}
|
||||
|
||||
Please note you event will not be booked until you complete this form.
|
||||
|
||||
TEC PA & Lighting
|
||||
21
RIGS/templates/RIGS/eventauthorisation_client_success.html
Normal file
@@ -0,0 +1,21 @@
|
||||
{% extends 'base_client_email.html' %}
|
||||
|
||||
{% block content %}
|
||||
<p>Hi {{ to_name|default:"there" }},</p>
|
||||
|
||||
<p>
|
||||
Your event <b>N{{ object.event.pk|stringformat:"05d" }}</b> has been successfully authorised
|
||||
for <b>£{{ object.amount }}</b>
|
||||
by <b>{{ object.name }}</b> as of <b>{{ object.last_edited_at }}</b>.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
{% if object.event.organisation and object.event.organisation.union_account %}{# internal #}
|
||||
Your event is now fully booked and payment will be processed by the finance department automatically.
|
||||
{% else %}{# external #}
|
||||
Your event is now fully booked and our finance department will be contact to arrange payment.
|
||||
{% endif %}
|
||||
</p>
|
||||
|
||||
<p>TEC PA & Lighting</p>
|
||||
{% endblock %}
|
||||
11
RIGS/templates/RIGS/eventauthorisation_client_success.txt
Normal file
@@ -0,0 +1,11 @@
|
||||
Hi {{ to_name|default:"there" }},
|
||||
|
||||
Your event N{{object.event.pk|stringformat:"05d"}} has been successfully authorised for £{{object.amount}} by {{object.name}} as of {{object.last_edited_at}}.
|
||||
|
||||
{% if object.event.organisation and object.event.organisation.union_account %}{# internal #}
|
||||
Your event is now fully booked and payment will be processed by the finance department automatically.
|
||||
{% else %}{# external #}
|
||||
Your event is now fully booked and our finance department will be contact to arrange payment.
|
||||
{% endif %}
|
||||
|
||||
TEC PA & Lighting
|
||||
133
RIGS/templates/RIGS/eventauthorisation_form.html
Normal file
@@ -0,0 +1,133 @@
|
||||
{% extends 'base_client.html' %}
|
||||
{% load widget_tweaks %}
|
||||
{% load static %}
|
||||
|
||||
{% block js %}
|
||||
<script src="{% static "js/tooltip.js" %}"></script>
|
||||
<script>
|
||||
$(function () {
|
||||
$('[data-toggle="tooltip"]').tooltip();
|
||||
});
|
||||
|
||||
$('form').on('submit', function () {
|
||||
$('#loading-modal').modal({
|
||||
backdrop: 'static',
|
||||
show: true
|
||||
});
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
{% block title %}
|
||||
{% if event.is_rig %}N{{ event.pk|stringformat:"05d" }}{% else %}{{ event.pk }}{% endif %} | {{ event.name }}
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
<h1>
|
||||
{% if event.is_rig %}N{{ event.pk|stringformat:"05d" }}{% else %}{{ event.pk }}{% endif %}
|
||||
| {{ event.name }} {% if event.dry_hire %}<span class="badge">Dry Hire</span>{% endif %}
|
||||
</h1>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
{% include 'RIGS/client_eventdetails.html' %}
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
{% with object=event %}
|
||||
{% include 'RIGS/item_table.html' %}
|
||||
{% endwith %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading" id="eventauth">Event Authorisation</div>
|
||||
|
||||
<div class="panel-body">
|
||||
<form class="form-horizontal itemised_form" role="form" method="POST" action="#eventauth">
|
||||
{% csrf_token %}
|
||||
{% include 'form_errors.html' %}
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
<p>
|
||||
I agree that I am authorised to approve this event. I agree that I am the
|
||||
<strong>President/Treasurer or account holder</strong> of the hirer, or that I
|
||||
have the written permission of the
|
||||
<strong>President/Treasurer or account holder</strong> of the hirer stating that
|
||||
I can authorise this event.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="col-sm-12 col-md-6">
|
||||
<div class="col-sm-12 form-group" data-toggle="tooltip"
|
||||
title="Your name as the person authorising the event.">
|
||||
<label for="{{ form.name.id_for_label }}"
|
||||
class="col-sm-4 control-label">{{ form.name.label }}</label>
|
||||
|
||||
<div class="col-sm-8">
|
||||
{% render_field form.name class+="form-control" %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-sm-12 form-group" data-toggle="tooltip"
|
||||
title="Your Student ID or Staff username as the person authorising the event.">
|
||||
<label for="{{ form.uni_id.id_for_label }}"
|
||||
class="col-sm-4 control-label">{{ form.uni_id.label }}</label>
|
||||
<div class="col-sm-8">
|
||||
{% render_field form.uni_id class+="form-control" %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-sm-12 col-md-6">
|
||||
<div class="col-sm-12 form-group" data-toggle="tooltip"
|
||||
title="The Students' Union account code you wish this event to be charged to.">
|
||||
<label for="{{ form.account_code.id_for_label }}"
|
||||
class="col-sm-4 control-label">{{ form.account_code.label }}</label>
|
||||
<div class="col-sm-8">
|
||||
{% render_field form.account_code class+="form-control" %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-sm-12 form-group" data-toggle="tooltip"
|
||||
title="The full amount chargable for this event as displayed above, including VAT.">
|
||||
<label for="{{ form.amount.id_for_label }}"
|
||||
class="col-sm-4 control-label">{{ form.amount.label }}</label>
|
||||
<div class="col-sm-8">
|
||||
{% render_field form.amount class+="form-control" %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-sm-12">
|
||||
<div class="col-sm-12 col-md-8 form-group">
|
||||
<div class="col-sm-offset-4 col-sm-8 col-md-offset-3" data-toggle="tooltip"
|
||||
title="In order to book an event you must agree to the TEC Terms of Hire.">
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
{% render_field form.tos %} I have read and agree to the TEC
|
||||
<a href="{{ tos_url }}">Terms of Hire</a>. E&OE.
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-sm-12 col-md-4 text-right">
|
||||
<div class="btn-group">
|
||||
<button class="btn btn-primary" type="submit">Authorise</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
5
RIGS/templates/RIGS/eventauthorisation_mic_success.txt
Normal file
@@ -0,0 +1,5 @@
|
||||
Hi {{object.event.mic.get_full_name|default_if_none:"somebody"}},
|
||||
|
||||
Just to let you know your event N{{object.event.pk|stringformat:"05d"}} has been successfully authorised for £{{object.amount}} by {{object.name}} as of {{object.last_edited_at}}.
|
||||
|
||||
The TEC Rig Information Gathering System
|
||||
59
RIGS/templates/RIGS/eventauthorisation_request.html
Normal file
@@ -0,0 +1,59 @@
|
||||
{% extends request.is_ajax|yesno:'base_ajax.html,base.html' %}
|
||||
{% load widget_tweaks %}
|
||||
|
||||
{% block title %}Request Authorisation{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
<div class="alert alert-warning">
|
||||
<h1>Send authorisation request email.</h1>
|
||||
<p>Pressing send will email the address provided. Please triple check everything before continuing.</p>
|
||||
</div>
|
||||
|
||||
<div class="alert alert-info">
|
||||
<dl class="dl-horizontal">
|
||||
<dt>Person Email</dt>
|
||||
<dd>{{ object.person.email }}</dd>
|
||||
|
||||
<dt>Organisation Email</dt>
|
||||
<dd>{{ object.organisation.email }}</dd>
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-12 col-md-10 col-md-offset-1">
|
||||
<form action="{{ form.action|default:request.path }}" method="post" class="form-horizontal"
|
||||
id="auth-request-form">
|
||||
{% csrf_token %}
|
||||
|
||||
<div class="row">
|
||||
{% include 'form_errors.html' %}
|
||||
|
||||
<div class="form-group">
|
||||
<label class="col-sm-3 control-label"
|
||||
for="{{ form.email.id_for_label }}">{{ form.email.label }}</label>
|
||||
|
||||
<div class="col-sm-9">
|
||||
{% render_field form.email type="email" class+="form-control" %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="text-right col-sm-3 col-sm-offset-9">
|
||||
<div class="form-group">
|
||||
<button type="submit" class="form-control btn btn-primary">
|
||||
<span class="glyphicon glyphicon-send"></span>
|
||||
Send
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
$('#auth-request-form').on('submit', function () {
|
||||
$('#auth-request-form button').attr('disabled', true);
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
15
RIGS/templates/RIGS/eventauthorisation_request_error.html
Normal file
@@ -0,0 +1,15 @@
|
||||
{% extends request.is_ajax|yesno:'base_ajax.html,base.html' %}
|
||||
{% load widget_tweaks %}
|
||||
|
||||
{% block title %}NottinghamTEC Email Address Required{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
<div class="alert alert-warning">
|
||||
<h1>An error occured.</h1>
|
||||
<p>Your RIGS account must have an @nottinghamtec.co.uk email address before you can send emails to clients.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
64
RIGS/templates/RIGS/eventauthorisation_success.html
Normal file
@@ -0,0 +1,64 @@
|
||||
{% extends 'base_client.html' %}
|
||||
{% load widget_tweaks %}
|
||||
|
||||
{% block title %}
|
||||
{% if event.is_rig %}N{{ event.pk|stringformat:"05d" }}{% else %}{{ event.pk }}{% endif %} | {{ event.name }}
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
<h1>
|
||||
{% if event.is_rig %}N{{ event.pk|stringformat:"05d" }}{% else %}{{ event.pk }}{% endif %}
|
||||
| {{ event.name }} {% if event.dry_hire %}<span class="badge">Dry Hire</span>{% endif %}
|
||||
</h1>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% include 'RIGS/client_eventdetails.html' %}
|
||||
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
{% with object=event %}
|
||||
{% include 'RIGS/item_table.html' %}
|
||||
{% endwith %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">Event Authorisation</div>
|
||||
|
||||
<div class="panel-body">
|
||||
<div class="row">
|
||||
<div class="col-sm-12 col-md-6">
|
||||
<dl class="dl-horizontal">
|
||||
<dt>Name</dt>
|
||||
<dd>{{ object.name }}</dd>
|
||||
|
||||
<dt>Email</dt>
|
||||
<dd>{{ object.email }}</dd>
|
||||
|
||||
{% if internal %}
|
||||
<dt>University ID</dt>
|
||||
<dd>{{ object.uni_id }}</dd>
|
||||
{% endif %}
|
||||
</dl>
|
||||
</div>
|
||||
|
||||
<div class="col-sm-12 col-md-6">
|
||||
<dl class="dl-horizontal">
|
||||
<dt>Account code</dt>
|
||||
<dd>{{ object.account_code }}</dd>
|
||||
|
||||
<dt>Authorised amount</dt>
|
||||
<dd>£ {{ object.amount|floatformat:2 }}</dd>
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -27,7 +27,7 @@
|
||||
<a class="list-group-item" href="//members.nottinghamtec.co.uk/wiki" target="_blank"><span class="glyphicon glyphicon-link"></span> TEC Wiki</a>
|
||||
<a class="list-group-item" href="http://members.nottinghamtec.co.uk/wiki/images/2/22/Event_Risk_Assesment.pdf" target="_blank"><span class="glyphicon glyphicon-link"></span> Pre-Event Risk Assessment</a>
|
||||
<a class="list-group-item" href="//members.nottinghamtec.co.uk/price" target="_blank"><span class="glyphicon glyphicon-link"></span> Price List</a>
|
||||
<a class="list-group-item" href="https://form.jotformeu.com/62203600438344" target="_blank"><span class="glyphicon glyphicon-link"></span> Subhire Insurance Form</a>
|
||||
<a class="list-group-item" href="https://goo.gl/forms/jdPWov8PCNPoXtbn2" target="_blank"><span class="glyphicon glyphicon-link"></span> Subhire Insurance Form</a>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
|
||||
<div class="col-sm-4 text-right">
|
||||
<div class="btn-group btn-page">
|
||||
<a href="{% url 'invoice_delete' object.pk %}" class="btn btn-default" title="Void Invoice">
|
||||
<a href="{% url 'invoice_delete' object.pk %}" class="btn btn-default" title="Delete Invoice">
|
||||
<span class="glyphicon glyphicon-remove"></span> <span
|
||||
class="hidden-xs">Delete</span>
|
||||
</a>
|
||||
@@ -19,7 +19,7 @@
|
||||
<span class="glyphicon glyphicon-ban-circle"></span> <span
|
||||
class="hidden-xs">Void</span>
|
||||
</a>
|
||||
<a href="{% url 'invoice_print' object.pk %}" target="_blank" class="btn btn-default"><span
|
||||
<a href="{% url 'invoice_print' object.pk %}" target="_blank" title="Print Invoice" class="btn btn-default"><span
|
||||
class="glyphicon glyphicon-print"></span> <span
|
||||
class="hidden-xs">Print</span></a>
|
||||
</div>
|
||||
@@ -76,8 +76,43 @@
|
||||
<dd>{{ object.checked_in_by.name }}</dd>
|
||||
{% endif %}
|
||||
|
||||
<dt>PO</dt>
|
||||
<dd>{{ object.event.purchase_order }}</dd>
|
||||
<dd> </dd>
|
||||
|
||||
<dt>Authorised</dt>
|
||||
<dd>{{ object.event.authorised|yesno:"Yes,No" }}</dd>
|
||||
|
||||
<dt>Authorised by</dt>
|
||||
<dd>
|
||||
{% if object.event.authorised %}
|
||||
{{ object.event.authorisation.name }}
|
||||
(<a href="mailto:{{ object.event.authorisation.email }}">{{ object.event.authorisation.email }}</a>)
|
||||
{% endif %}
|
||||
</dd>
|
||||
|
||||
{% if object.event.internal %}
|
||||
{# internal #}
|
||||
<dt>Uni ID</dt>
|
||||
<dd>{{ object.event.authorisation.uni_id }}</dd>
|
||||
|
||||
<dt>Account code</dt>
|
||||
<dd>{{ object.event.authorisation.account_code }}</dd>
|
||||
{% else %}
|
||||
<dt>PO</dt>
|
||||
<dd>{{ object.event.purchase_order }}</dd>
|
||||
{% endif %}
|
||||
|
||||
<dt>Authorised at</dt>
|
||||
<dd>{{ object.event.authorisation.last_edited_at }}</dd>
|
||||
|
||||
<dt>Authorised amount</dt>
|
||||
<dd>
|
||||
{% if object.event.authorised %}
|
||||
£ {{ object.event.authorisation.amount|floatformat:"2" }}
|
||||
{% endif %}
|
||||
</dd>
|
||||
|
||||
<dt>Authorsation request sent by</dt>
|
||||
<dd>{{ object.authorisation.sent_by }}</dd>
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
||||
@@ -139,4 +174,4 @@
|
||||
</div>
|
||||
|
||||
</div>
|
||||
{% endblock %}
|
||||
{% endblock %}
|
||||
|
||||
@@ -50,7 +50,7 @@
|
||||
<td>{% if object.event.organisation %}
|
||||
{{ object.event.organisation.name }}
|
||||
<br>
|
||||
<span class="text-muted">{{ object.event.organisation.union_account|yesno:'Internal,External' }}</span>
|
||||
<span class="text-muted">{{ object.event.internal|yesno:'Internal,External' }}</span>
|
||||
{% else %}
|
||||
{{ object.event.person.name }}
|
||||
<br>
|
||||
@@ -59,7 +59,11 @@
|
||||
</td>
|
||||
<td>{{ object.event.start_date }}</td>
|
||||
<td>{{ object.invoice_date }}</td>
|
||||
<td>{{ object.balance|floatformat:2 }}</td>
|
||||
<td>
|
||||
{{ object.balance|floatformat:2 }}
|
||||
<br />
|
||||
<span class="text-muted">{{ object.event.purchase_order }}</span>
|
||||
</td>
|
||||
<td class="text-right">
|
||||
<a href="{% url 'invoice_detail' object.pk %}" class="btn btn-default">
|
||||
<span class="glyphicon glyphicon-pencil"></span>
|
||||
@@ -76,4 +80,4 @@
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endblock %}
|
||||
{% endblock %}
|
||||
|
||||