Plumb most of the basic interactions
This commit is contained in:
@@ -4,7 +4,7 @@ from django.contrib.auth.models import User
|
|||||||
from django.db import models
|
from django.db import models
|
||||||
|
|
||||||
class Committee(models.Model):
|
class Committee(models.Model):
|
||||||
name = models.CharField(max_length=100)
|
name = models.CharField(max_length=100, unique=True)
|
||||||
chair = models.ForeignKey(User, null=True)
|
chair = models.ForeignKey(User, null=True)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
|
|||||||
@@ -41,9 +41,13 @@ INSTALLED_APPS = [
|
|||||||
'django.contrib.sessions',
|
'django.contrib.sessions',
|
||||||
'django.contrib.messages',
|
'django.contrib.messages',
|
||||||
'django.contrib.staticfiles',
|
'django.contrib.staticfiles',
|
||||||
|
'django_cleanup',
|
||||||
|
'social_django',
|
||||||
|
'storages',
|
||||||
|
'stdimage',
|
||||||
|
|
||||||
'items',
|
'items',
|
||||||
'committee',
|
'committee',
|
||||||
'social_django',
|
|
||||||
]
|
]
|
||||||
|
|
||||||
MIDDLEWARE = [
|
MIDDLEWARE = [
|
||||||
@@ -93,6 +97,14 @@ DATABASES = {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# S3 File Storage
|
||||||
|
DEFAULT_FILE_STORAGE = 'storages.backends.s3boto3.S3Boto3Storage'
|
||||||
|
AWS_ACCESS_KEY_ID=os.environ['AWS_ACCESS_KEY_ID']
|
||||||
|
AWS_SECRET_ACCESS_KEY=os.environ['AWS_SECRET_ACCESS_KEY']
|
||||||
|
AWS_AUTO_CREATE_BUCKET=True
|
||||||
|
AWS_STORAGE_BUCKET_NAME='fincom'
|
||||||
|
AWS_S3_FILE_OVERWRITE=False
|
||||||
|
|
||||||
|
|
||||||
# Password validation
|
# Password validation
|
||||||
# https://docs.djangoproject.com/en/1.10/ref/settings/#auth-password-validators
|
# https://docs.djangoproject.com/en/1.10/ref/settings/#auth-password-validators
|
||||||
@@ -118,6 +130,7 @@ AUTH_PASSWORD_VALIDATORS = [
|
|||||||
SOCIAL_AUTH_GOOGLE_OAUTH2_KEY = os.environ['SOCIAL_AUTH_GOOGLE_OAUTH2_KEY']
|
SOCIAL_AUTH_GOOGLE_OAUTH2_KEY = os.environ['SOCIAL_AUTH_GOOGLE_OAUTH2_KEY']
|
||||||
SOCIAL_AUTH_GOOGLE_OAUTH2_SECRET = os.environ['SOCIAL_AUTH_GOOGLE_OAUTH2_SECRET']
|
SOCIAL_AUTH_GOOGLE_OAUTH2_SECRET = os.environ['SOCIAL_AUTH_GOOGLE_OAUTH2_SECRET']
|
||||||
SOCIAL_AUTH_GOOGLE_OAUTH2_WHITELISTED_DOMAINS = ['andrew.cmu.edu']
|
SOCIAL_AUTH_GOOGLE_OAUTH2_WHITELISTED_DOMAINS = ['andrew.cmu.edu']
|
||||||
|
SOCIAL_AUTH_GOOGLE_OAUTH2_AUTH_EXTRA_ARGUMENTS = {'hd': 'andrew.cmu.edu' }
|
||||||
SOCIAL_AUTH_LOGIN_REDIRECT_URL = '/items/'
|
SOCIAL_AUTH_LOGIN_REDIRECT_URL = '/items/'
|
||||||
|
|
||||||
# Internationalization
|
# Internationalization
|
||||||
|
|||||||
@@ -1,11 +1,16 @@
|
|||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
|
from storages.backends.s3boto3 import S3Boto3Storage
|
||||||
|
from stdimage.models import StdImageField
|
||||||
|
from datetime import datetime, date
|
||||||
|
|
||||||
from committee.models import Committee
|
from committee.models import Committee
|
||||||
|
|
||||||
from django.db import models
|
from django.db import models
|
||||||
|
|
||||||
class Item(models.Model):
|
class Item(models.Model):
|
||||||
|
S3 = S3Boto3Storage()
|
||||||
|
|
||||||
NEW = 'N'
|
NEW = 'N'
|
||||||
PREAPPROVED = 'C'
|
PREAPPROVED = 'C'
|
||||||
PROCESSED = 'P'
|
PROCESSED = 'P'
|
||||||
@@ -24,24 +29,29 @@ class Item(models.Model):
|
|||||||
details = models.TextField()
|
details = models.TextField()
|
||||||
cost = models.DecimalField(max_digits=7, decimal_places=2)
|
cost = models.DecimalField(max_digits=7, decimal_places=2)
|
||||||
date_purchased = models.DateField('date purchased')
|
date_purchased = models.DateField('date purchased')
|
||||||
|
image = StdImageField(upload_to='images/%Y/%m/%d',
|
||||||
|
variations={'thumbnail': (600, 600)}
|
||||||
|
)
|
||||||
|
|
||||||
created_by = models.ForeignKey(User, related_name='created_by')
|
created_by = models.ForeignKey(User, related_name='created_by')
|
||||||
approved_by = models.ManyToManyField(User, blank=True, related_name='approved_by')
|
approved_by = models.ManyToManyField(User, blank=True, related_name='approved_by')
|
||||||
date_filed = models.DateTimeField('date filed')
|
date_filed = models.DateTimeField('date filed')
|
||||||
status = models.CharField(max_length=2, choices=STATUS)
|
status = models.CharField(max_length=2, choices=STATUS)
|
||||||
task_id = models.CharField(max_length=30)
|
|
||||||
|
|
||||||
def approved(self):
|
def approved(self):
|
||||||
return self.status == 'P'
|
return self.status == Item.PREAPPROVED
|
||||||
|
|
||||||
def processed(self):
|
def processed(self):
|
||||||
return self.status == 'C'
|
return self.status == Item.PROCESSED
|
||||||
|
|
||||||
def rejected(self):
|
def rejected(self):
|
||||||
return self.status == 'R'
|
return self.status == Item.REJECTED
|
||||||
|
|
||||||
def new(self):
|
def new(self):
|
||||||
return self.status == 'N'
|
return self.status == Item.NEW
|
||||||
|
|
||||||
|
def statusText(self):
|
||||||
|
return dict(Item.STATUS)[self.status]
|
||||||
|
|
||||||
def comName(self):
|
def comName(self):
|
||||||
return self.committee.name
|
return self.committee.name
|
||||||
|
|||||||
@@ -3,5 +3,10 @@ from . import views
|
|||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
url(r'^$', views.list, name='list'),
|
url(r'^$', views.list, name='list'),
|
||||||
|
url(r'^(?P<item_id>\d+)/$', views.details, name='details'),
|
||||||
|
url(r'^(?P<item_id>\d+)/approve$', views.approve, name='approve'),
|
||||||
|
url(r'^(?P<item_id>\d+)/reject$', views.reject, name='reject'),
|
||||||
|
url(r'^(?P<item_id>\d+)/edit$', views.edit, name='edit'),
|
||||||
|
url(r'^(?P<item_id>\d+)/delete$', views.delete, name='delete'),
|
||||||
url(r'^new$', views.new_form, name='new_form'),
|
url(r'^new$', views.new_form, name='new_form'),
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -1,22 +1,158 @@
|
|||||||
from django.shortcuts import HttpResponse
|
from django.shortcuts import HttpResponse, HttpResponseRedirect
|
||||||
from django.template import loader
|
from django.template import loader
|
||||||
|
from django.utils import timezone
|
||||||
|
from django.contrib.auth.decorators import login_required
|
||||||
|
from django.db.models import Q
|
||||||
from models import Item
|
from models import Item
|
||||||
from committee.models import Committee
|
from committee.models import Committee
|
||||||
|
|
||||||
|
def isAuthorised(request, item):
|
||||||
|
return (request.user == item.committee.chair
|
||||||
|
or request.user.groups.filter(name='Fincom').exists())
|
||||||
|
|
||||||
# Create your views here.
|
def authError():
|
||||||
|
return HttpResponseRedirect('/items')
|
||||||
|
|
||||||
|
def myItems(user):
|
||||||
|
if (user.groups.filter(name='Fincom').exists()):
|
||||||
|
return Items.objects.order_by('-date_filed', 'desc')
|
||||||
|
|
||||||
|
comms = []
|
||||||
|
for c in Committee.objects.all():
|
||||||
|
if (c.chair == user):
|
||||||
|
comms.append(c)
|
||||||
|
|
||||||
|
return Item.objects.filter(Q(created_by=user) | Q(committee__in=comms)) \
|
||||||
|
.order_by('-date_filed', 'desc')
|
||||||
|
|
||||||
|
@login_required
|
||||||
def list(request):
|
def list(request):
|
||||||
template = loader.get_template('items/list.html')
|
template = loader.get_template('items/list.html')
|
||||||
|
items = myItems(request.user)
|
||||||
|
|
||||||
context = {
|
context = {
|
||||||
'items': Item.objects.all(),
|
'preapproved': items.filter(status=Item.PREAPPROVED),
|
||||||
|
'processed': items.filter(status=Item.PROCESSED),
|
||||||
|
'newitems': items.filter(status=Item.NEW),
|
||||||
|
'rejected': items.filter(status=Item.REJECTED),
|
||||||
}
|
}
|
||||||
|
|
||||||
return HttpResponse(template.render(context, request))
|
return HttpResponse(template.render(context, request))
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
def details(request, item_id):
|
||||||
|
I = Item.objects.get(pk=item_id)
|
||||||
|
if (not isAuthorised(request, I)):
|
||||||
|
return HttpResponseRedirect('/items/' + str(item_id) + '/edit')
|
||||||
|
|
||||||
|
template = loader.get_template('items/details.html')
|
||||||
|
|
||||||
|
context = {
|
||||||
|
'I': I,
|
||||||
|
}
|
||||||
|
return HttpResponse(template.render(context, request))
|
||||||
|
|
||||||
|
def approve(request, item_id):
|
||||||
|
I = Item.objects.get(pk=item_id)
|
||||||
|
|
||||||
|
if (not isAuthorised(request, I)):
|
||||||
|
return authError()
|
||||||
|
|
||||||
|
I.approved_by.add(request.user)
|
||||||
|
if (I.committee.chair == request.user and
|
||||||
|
I.status == Item.NEW):
|
||||||
|
I.status = Item.PREAPPROVED
|
||||||
|
elif (request.user.groups.filter(name='Fincom').exists()):
|
||||||
|
I.status = Item.PROCESSED
|
||||||
|
|
||||||
|
I.save()
|
||||||
|
|
||||||
|
return HttpResponseRedirect('/items')
|
||||||
|
|
||||||
|
def reject(request, item_id):
|
||||||
|
I = Item.objects.get(pk=item_id)
|
||||||
|
|
||||||
|
if (not isAuthorised(request, I)):
|
||||||
|
return authError()
|
||||||
|
|
||||||
|
I.status = Item.REJECTED
|
||||||
|
I.save()
|
||||||
|
|
||||||
|
return HttpResponseRedirect('/items')
|
||||||
|
|
||||||
|
def delete(request, item_id):
|
||||||
|
I = Item.objects.get(pk=item_id)
|
||||||
|
|
||||||
|
if (not isAuthorised(request, I)):
|
||||||
|
return authError()
|
||||||
|
|
||||||
|
I.delete()
|
||||||
|
|
||||||
|
return HttpResponseRedirect('/items')
|
||||||
|
|
||||||
|
def edit(request, item_id):
|
||||||
|
I = Item.objects.get(pk=item_id)
|
||||||
|
|
||||||
|
if (not isAuthorised(request, I)
|
||||||
|
and request.user != I.created_by):
|
||||||
|
return authError()
|
||||||
|
|
||||||
|
if request.method == 'POST':
|
||||||
|
if (request.POST.get('desc', None)):
|
||||||
|
I.desc = request.POST['desc']
|
||||||
|
if (request.POST.get('event', None)):
|
||||||
|
I.event = request.POST['event']
|
||||||
|
if (request.POST.get('committee', None)):
|
||||||
|
I.committee = Committee.objects.get(name=request.POST['committee'])
|
||||||
|
if (request.POST.get('cost', None)):
|
||||||
|
I.cost = request.POST['cost']
|
||||||
|
if (request.POST.get('date', None)):
|
||||||
|
I.date_purchased = Item.parseDate(request.POST['date']),
|
||||||
|
if (request.POST.get('details', None)):
|
||||||
|
I.details = request.POST['details']
|
||||||
|
|
||||||
|
I.save()
|
||||||
|
|
||||||
|
return HttpResponseRedirect('/items/' + str(item_id))
|
||||||
|
else:
|
||||||
|
template = loader.get_template('items/edit.html')
|
||||||
|
|
||||||
|
context = {
|
||||||
|
'I': I,
|
||||||
|
'committees': Committee.objects.order_by('name'),
|
||||||
|
}
|
||||||
|
return HttpResponse(template.render(context, request))
|
||||||
|
|
||||||
def new_form(request):
|
def new_form(request):
|
||||||
template = loader.get_template('items/new.html')
|
if request.method == 'POST':
|
||||||
context = {
|
if request.FILES['image'].size > 10 * (1 << 20):
|
||||||
'committees': Committee.objects.order_by('name'),
|
template = loader.get_template('items/new.html')
|
||||||
}
|
context = {
|
||||||
|
'committees': Committee.objects.order_by('name'),
|
||||||
|
'error': 'Your image file is too large. Maximum size is 20MB',
|
||||||
|
}
|
||||||
|
return HttpResponse(template.render(context, request))
|
||||||
|
item = Item(
|
||||||
|
desc = request.POST['desc'],
|
||||||
|
event = request.POST['event'],
|
||||||
|
committee = Committee.objects.get(name=request.POST['committee']),
|
||||||
|
cost = request.POST['cost'],
|
||||||
|
date_purchased = Item.parseDate(request.POST['date']),
|
||||||
|
details = request.POST['details'],
|
||||||
|
date_filed = timezone.now(),
|
||||||
|
created_by = request.user,
|
||||||
|
status = Item.NEW,
|
||||||
|
image = request.FILES['image'],
|
||||||
|
)
|
||||||
|
|
||||||
return HttpResponse(template.render(context, request))
|
item.save()
|
||||||
|
|
||||||
|
return HttpResponseRedirect('/items')
|
||||||
|
|
||||||
|
else:
|
||||||
|
template = loader.get_template('items/new.html')
|
||||||
|
context = {
|
||||||
|
'committees': Committee.objects.order_by('name'),
|
||||||
|
}
|
||||||
|
|
||||||
|
return HttpResponse(template.render(context, request))
|
||||||
|
|||||||
28
fincom/static/sass/details.scss
Normal file
28
fincom/static/sass/details.scss
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
.approve {
|
||||||
|
padding: $pad-xl;
|
||||||
|
|
||||||
|
.status {
|
||||||
|
font-size: 16px;
|
||||||
|
color: $midgray;
|
||||||
|
font-weight: lighter;
|
||||||
|
|
||||||
|
span {
|
||||||
|
color: $text;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
img {
|
||||||
|
border: 1px solid $midgray;
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.actions {
|
||||||
|
margin: $pad-m 0;
|
||||||
|
|
||||||
|
a {
|
||||||
|
text-decoration: none;
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: normal;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
$purple: #563d7c;
|
$purple: #563d7c;
|
||||||
$gold: #fdd017;
|
$gold: #fdd017;
|
||||||
$lightgray: #fcfcfc;
|
$lightgray: #f8f8f8;
|
||||||
$midgray: #969499;
|
$midgray: #969499;
|
||||||
$darkgray: #4b4a4d;
|
$darkgray: #4b4a4d;
|
||||||
$text: #252526;
|
$text: #252526;
|
||||||
@@ -41,18 +41,20 @@ body {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.container {
|
.container {
|
||||||
height: 100%;
|
& > div {
|
||||||
margin-left: auto;
|
margin-left: auto;
|
||||||
margin-right: auto;
|
margin-right: auto;
|
||||||
margin-top: 64px;
|
margin-top: 64px;
|
||||||
max-width: 960;
|
max-width: 960;
|
||||||
background-color: #fff;
|
background-color: #fff;
|
||||||
border: solid 1px $midgray;
|
border: solid 1px $midgray;
|
||||||
|
box-shadow: 0px 3px 9px lighten($shadowgray, 20%);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
.btn {
|
.btn {
|
||||||
border-radius: $pad-s;
|
border-radius: $pad-s;
|
||||||
|
border: 1px solid $midgray;
|
||||||
color: $purple;
|
color: $purple;
|
||||||
|
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
@@ -63,6 +65,6 @@ body {
|
|||||||
|
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background-color: darken($purple, 15%);
|
background-color: lighten($purple, 35%);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
@import 'globals';
|
@import 'globals';
|
||||||
|
@import 'details';
|
||||||
@import 'items';
|
@import 'items';
|
||||||
@import 'login';
|
@import 'login';
|
||||||
@import 'new';
|
@import 'new';
|
||||||
|
|||||||
@@ -1,3 +1,11 @@
|
|||||||
|
section {
|
||||||
|
color: $midgray;
|
||||||
|
font-weight: lighter;
|
||||||
|
font-size: 16px;
|
||||||
|
margin-top: 2*$pad-xl;
|
||||||
|
margin-bottom: -2*$pad-xl + $pad-l;
|
||||||
|
}
|
||||||
|
|
||||||
.item {
|
.item {
|
||||||
padding: $pad-m $pad-xl;
|
padding: $pad-m $pad-xl;
|
||||||
margin: 0px;
|
margin: 0px;
|
||||||
@@ -12,6 +20,10 @@
|
|||||||
margin: 0px;
|
margin: 0px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
.details-row {
|
.details-row {
|
||||||
margin: $pad-m 0;
|
margin: $pad-m 0;
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
@@ -36,7 +48,6 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.cost {
|
.cost {
|
||||||
float: right;
|
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
width: 120px;
|
width: 120px;
|
||||||
@@ -58,22 +69,52 @@
|
|||||||
background-color: #d9ffd9;
|
background-color: #d9ffd9;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.rejected {
|
||||||
|
background-color: $midgray;
|
||||||
|
color: $lightgray;
|
||||||
|
}
|
||||||
|
|
||||||
.newItem {
|
.newItem {
|
||||||
background-color: #fff7d9;
|
background-color: #fff7d9;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@media (min-width: 800px) {
|
||||||
|
.item .cost {
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty {
|
||||||
|
margin: $pad-l;
|
||||||
|
color: $midgray;
|
||||||
|
font-weight: lighter;
|
||||||
|
font-size: 16px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
$button-size: 24px;
|
$button-size: 24px;
|
||||||
|
|
||||||
.btn-floating {
|
.btn-floating {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
bottom: $button-size;
|
bottom: $button-size;
|
||||||
right: $button-size;
|
right: $button-size;
|
||||||
padding: $button-size;
|
padding: $button-size/2;
|
||||||
|
color: $gold;
|
||||||
|
background-color: $purple;
|
||||||
|
text-decoration: none;
|
||||||
|
font-weight: bold;
|
||||||
|
|
||||||
z-index: 1000;
|
z-index: 1000;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
|
|
||||||
text-align: center;
|
text-align: center;
|
||||||
box-shadow: 0 0 6px rgba(0,0,0,.16),0 6px 12px rgba(0,0,0,.32);
|
box-shadow: 0 0 6px rgba(0,0,0,.16),0 6px 12px rgba(0,0,0,.32);
|
||||||
|
|
||||||
|
span {
|
||||||
|
display: block;
|
||||||
|
width: 36;
|
||||||
|
height: 36;
|
||||||
|
font-size: 28;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,14 +14,12 @@ hr {
|
|||||||
form {
|
form {
|
||||||
div {
|
div {
|
||||||
margin: $pad-l;
|
margin: $pad-l;
|
||||||
width: 376px;
|
|
||||||
|
|
||||||
* {
|
* {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
}
|
}
|
||||||
|
|
||||||
label {
|
label {
|
||||||
text-align: right;
|
|
||||||
width: 180px;
|
width: 180px;
|
||||||
color: $text;
|
color: $text;
|
||||||
vertical-align: top;
|
vertical-align: top;
|
||||||
@@ -29,7 +27,6 @@ form {
|
|||||||
|
|
||||||
input, select, textarea {
|
input, select, textarea {
|
||||||
width: 180px;
|
width: 180px;
|
||||||
float: right;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
&.clear {
|
&.clear {
|
||||||
@@ -46,9 +43,32 @@ form {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.rcol {
|
.rcol {
|
||||||
float: right;
|
|
||||||
width: 40%;
|
|
||||||
margin: $pad-l;
|
margin: $pad-l;
|
||||||
color: $midgray;
|
color: $midgray;
|
||||||
font-weight: lighter;
|
font-weight: lighter;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@media (min-width: 430px) {
|
||||||
|
form {
|
||||||
|
div {
|
||||||
|
width: 376px;
|
||||||
|
|
||||||
|
|
||||||
|
label {
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
input, select, textarea {
|
||||||
|
width: 180px;
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 800px) {
|
||||||
|
.rcol {
|
||||||
|
float: right;
|
||||||
|
width: 40%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -13,6 +13,8 @@
|
|||||||
{% block main %}
|
{% block main %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
</div>
|
</div>
|
||||||
|
{% block bottom %}
|
||||||
|
{% endblock %}
|
||||||
<script>
|
<script>
|
||||||
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
|
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
|
||||||
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
|
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
<link href="{% static "css/site.css" %}" rel="stylesheet">
|
<link href="{% static "css/site.css" %}" rel="stylesheet">
|
||||||
</head>
|
</head>
|
||||||
<body class="login">
|
<body class="login">
|
||||||
<div class="container">
|
<div>
|
||||||
<h3>Delta Beta Chapter</h3>
|
<h3>Delta Beta Chapter</h3>
|
||||||
<h2>Fincom Webapp</h2>
|
<h2>Fincom Webapp</h2>
|
||||||
{% if error %}
|
{% if error %}
|
||||||
|
|||||||
22
fincom/templates/items/details.html
Normal file
22
fincom/templates/items/details.html
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
{% extends "../boilerplate.html" %}
|
||||||
|
|
||||||
|
{% block main %}
|
||||||
|
|
||||||
|
<div>
|
||||||
|
{% include "./item.html" %}
|
||||||
|
|
||||||
|
<div class="approve">
|
||||||
|
<div class="status">Status: <span>{{ I.statusText }}</span></div>
|
||||||
|
|
||||||
|
<div class="actions">
|
||||||
|
<a href="/items/{{ I.pk }}/approve" class="btn">Approve</a>
|
||||||
|
<a href="/items/{{ I.pk }}/reject" class="btn">Reject</a>
|
||||||
|
<a href="/items/{{ I.pk }}/delete" class="btn">Delete</a>
|
||||||
|
<a href="/items/{{ I.pk }}/edit" class="btn">Edit</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<a href="{{ I.image.url }}"><img src="{{ I.image.thumbnail.url }}"></a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
65
fincom/templates/items/edit.html
Normal file
65
fincom/templates/items/edit.html
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
{% extends "../boilerplate.html" %}
|
||||||
|
|
||||||
|
{% block title %}Edit Reimbursement{% endblock %}
|
||||||
|
|
||||||
|
{% block main %}
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<h1>Edit Reimbursements</h1>
|
||||||
|
<hr>
|
||||||
|
|
||||||
|
<form action="/items/{{ I.pk }}/edit" method="post">
|
||||||
|
{% csrf_token %}
|
||||||
|
<div>
|
||||||
|
<label for="desc">Item</label>
|
||||||
|
<input id="desc" type="text" name="desc" placeholder="What you bought"
|
||||||
|
value="{{ I.desc }}">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label for="event">Event</label>
|
||||||
|
<input id="event" type="text" name="event" placeholder="When we need it"
|
||||||
|
value="{{ I.event }}">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label for="committee">Committee or Budget</label>
|
||||||
|
<select name="committee" id="committee">
|
||||||
|
{% for C in committees %}
|
||||||
|
{% if I.committee == C %}
|
||||||
|
<option selected>{{ C.name }}</option>
|
||||||
|
{% else %}
|
||||||
|
<option>{{ C.name }}</option>
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label for="cost">Cost</label>
|
||||||
|
<input id="cost" type="text" name="cost" placeholder="15.00"
|
||||||
|
value="{{ I.cost }}">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label for="date">Date Purchased</label>
|
||||||
|
<input id="date" type="date" name="date" placeholder="mm/dd/yyyy"
|
||||||
|
value="{{ I.date_purchased }}">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="clear">
|
||||||
|
<div>
|
||||||
|
<label for="details">Details</label>
|
||||||
|
<textarea id="details" name="details" rows="4" class="clear">{{ I.details }}</textarea>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="clear">
|
||||||
|
<div>
|
||||||
|
<input type="submit">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
@@ -6,11 +6,14 @@
|
|||||||
<div class="cost approved">
|
<div class="cost approved">
|
||||||
{% elif I.processed %}
|
{% elif I.processed %}
|
||||||
<div class="cost processed">
|
<div class="cost processed">
|
||||||
|
{% elif I.rejected %}
|
||||||
|
<div class="cost rejected">
|
||||||
{% else %}
|
{% else %}
|
||||||
<div class="cost newItem">
|
<div class="cost newItem">
|
||||||
{% endif %}
|
{% endif %}
|
||||||
${{ I.cost|floatformat:"2" }}
|
${{ I.cost|floatformat:"2" }}
|
||||||
</div>
|
</div>
|
||||||
|
<a href="/items/{{ I.pk }}">
|
||||||
<div class="details-row">
|
<div class="details-row">
|
||||||
<b>{{ I.event }}:</b>
|
<b>{{ I.event }}:</b>
|
||||||
{{ I.desc }}
|
{{ I.desc }}
|
||||||
@@ -19,12 +22,15 @@
|
|||||||
<em>on</em> {{ I.date_purchased }}
|
<em>on</em> {{ I.date_purchased }}
|
||||||
<em>(filed {{ I.date_filed }})</em>
|
<em>(filed {{ I.date_filed }})</em>
|
||||||
</div>
|
</div>
|
||||||
|
</a>
|
||||||
<div class="status">
|
<div class="status">
|
||||||
{% if I.approved or I.processed %}
|
{% if I.approved or I.processed %}
|
||||||
Approved By:
|
Approved By:
|
||||||
{% for u in I.approved_by.all %}
|
{% for u in I.approved_by.all %}
|
||||||
{{ u.first_name }} {{ u.last_name }}
|
{{ u.first_name }} {{ u.last_name }}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
{% elif I.rejected %}
|
||||||
|
Reimbursement Declined
|
||||||
{% else %}
|
{% else %}
|
||||||
Needs Approval
|
Needs Approval
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|||||||
@@ -2,10 +2,60 @@
|
|||||||
|
|
||||||
{% block main %}
|
{% block main %}
|
||||||
|
|
||||||
{% for I in items %}
|
{% if error %}
|
||||||
|
<div>
|
||||||
|
<span class="error">{{ error }}</span>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if preapproved %}
|
||||||
|
<section>Pre-Approved</section>
|
||||||
|
<div>
|
||||||
|
{% for I in preapproved %}
|
||||||
{% include "./item.html" %}
|
{% include "./item.html" %}
|
||||||
{% empty %}
|
{% empty %}
|
||||||
<div>No Reimbursements</div>
|
<div class="empty">No Reimbursements</div>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if newitems %}
|
||||||
|
<section>New Items</section>
|
||||||
|
<div>
|
||||||
|
{% for I in newitems %}
|
||||||
|
{% include "./item.html" %}
|
||||||
|
{% empty %}
|
||||||
|
<div class="empty">No Reimbursements</div>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if processed %}
|
||||||
|
<section>Approved</section>
|
||||||
|
<div>
|
||||||
|
{% for I in processed %}
|
||||||
|
{% include "./item.html" %}
|
||||||
|
{% empty %}
|
||||||
|
<div class="empty">No Reimbursements</div>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if rejected %}
|
||||||
|
<section>Rejected</section>
|
||||||
|
<div>
|
||||||
|
{% for I in rejected %}
|
||||||
|
{% include "./item.html" %}
|
||||||
|
{% empty %}
|
||||||
|
<div class="empty">No Reimbursements</div>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block bottom %}
|
||||||
|
<a href="/items/new" title="Submit new item" class="btn-floating">
|
||||||
|
<span>+</span>
|
||||||
|
</a>
|
||||||
|
{% endblock %}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
{% block main %}
|
{% block main %}
|
||||||
|
|
||||||
|
<div>
|
||||||
<h1>Add New Reimbursements</h1>
|
<h1>Add New Reimbursements</h1>
|
||||||
<hr>
|
<hr>
|
||||||
|
|
||||||
@@ -12,7 +13,7 @@
|
|||||||
you select the correct committee.
|
you select the correct committee.
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<form action="/new/" method="post">
|
<form action="/items/new" method="post" enctype="multipart/form-data">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
<div>
|
<div>
|
||||||
<label for="desc">Item</label>
|
<label for="desc">Item</label>
|
||||||
@@ -53,16 +54,15 @@
|
|||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label for="image">Receipt</label>
|
<label for="image">Receipt</label>
|
||||||
<input type="file" name="image" id="image">
|
<input type="file" name="image" id="image" accept="image/*">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="clear">
|
||||||
<div>
|
<div>
|
||||||
<input type="submit">
|
<input type="submit">
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|||||||
Reference in New Issue
Block a user