diff --git a/smash/web/algorithm/__init__.py b/smash/web/algorithm/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..defa4efb79e533df509ea627c72f61a3e4e001b1 --- /dev/null +++ b/smash/web/algorithm/__init__.py @@ -0,0 +1,4 @@ +from luhn_algorithm import LuhnAlgorithm +from verhoeff_alogirthm import VerhoeffAlgorithm + +__all__ = [VerhoeffAlgorithm, LuhnAlgorithm] diff --git a/smash/web/algorithm/luhn_algorithm.py b/smash/web/algorithm/luhn_algorithm.py new file mode 100644 index 0000000000000000000000000000000000000000..c752bc333716c2b7e4081bae0875cdd2911ee02d --- /dev/null +++ b/smash/web/algorithm/luhn_algorithm.py @@ -0,0 +1,21 @@ +class LuhnAlgorithm(object): + def __init__(self): + pass + + @staticmethod + def luhn_checksum(card_number): + def digits_of(n): + return [int(d) for d in str(n)] + + digits = digits_of(card_number) + odd_digits = digits[-1::-2] + even_digits = digits[-2::-2] + checksum = 0 + checksum += sum(odd_digits) + for d in even_digits: + checksum += sum(digits_of(d * 2)) + return checksum % 10 + + @staticmethod + def is_luhn_valid(card_number): + return LuhnAlgorithm.luhn_checksum(card_number) == 0 diff --git a/smash/web/algorithm/verhoeff_alogirthm.py b/smash/web/algorithm/verhoeff_alogirthm.py new file mode 100644 index 0000000000000000000000000000000000000000..0b9dc9ffcf0b5026bfa95845efc71b34fd8b03d4 --- /dev/null +++ b/smash/web/algorithm/verhoeff_alogirthm.py @@ -0,0 +1,45 @@ +verhoeff_multiplication_table = ( + (0, 1, 2, 3, 4, 5, 6, 7, 8, 9), + (1, 2, 3, 4, 0, 6, 7, 8, 9, 5), + (2, 3, 4, 0, 1, 7, 8, 9, 5, 6), + (3, 4, 0, 1, 2, 8, 9, 5, 6, 7), + (4, 0, 1, 2, 3, 9, 5, 6, 7, 8), + (5, 9, 8, 7, 6, 0, 4, 3, 2, 1), + (6, 5, 9, 8, 7, 1, 0, 4, 3, 2), + (7, 6, 5, 9, 8, 2, 1, 0, 4, 3), + (8, 7, 6, 5, 9, 3, 2, 1, 0, 4), + (9, 8, 7, 6, 5, 4, 3, 2, 1, 0)) +verhoeff_permutation_table = ( + (0, 1, 2, 3, 4, 5, 6, 7, 8, 9), + (1, 5, 7, 6, 2, 8, 3, 0, 9, 4), + (5, 8, 0, 3, 7, 9, 6, 1, 4, 2), + (8, 9, 1, 6, 0, 4, 3, 5, 2, 7), + (9, 4, 5, 3, 1, 2, 6, 8, 7, 0), + (4, 2, 8, 6, 5, 7, 3, 9, 0, 1), + (2, 7, 9, 3, 8, 0, 6, 4, 1, 5), + (7, 0, 4, 6, 9, 1, 3, 2, 5, 8)) + + +class VerhoeffAlgorithm(object): + def __init__(self): + pass + + @staticmethod + def verhoeff_checksum(number): + """Calculate the Verhoeff checksum over the provided number. The checksum + is returned as an int. Valid numbers should have a checksum of 0.""" + # transform number list + number = tuple(int(n) for n in reversed(str(number))) + # calculate checksum + check = 0 + for i, n in enumerate(number): + check = verhoeff_multiplication_table[check][verhoeff_permutation_table[i % 8][n]] + return check + + @staticmethod + def is_valid_verhoeff(number): + return VerhoeffAlgorithm.verhoeff_checksum(number) == 0 + + @staticmethod + def calculate_verhoeff_check_sum(number): + return str(verhoeff_multiplication_table[VerhoeffAlgorithm.verhoeff_checksum(str(number) + '0')].index(0)) diff --git a/smash/web/forms/forms.py b/smash/web/forms/forms.py index 570db43352dd9935a0ec0f9dc7871d6aaae6c427..d1f90b4766837c9e8d7b1e993dca9b32cfb66f93 100644 --- a/smash/web/forms/forms.py +++ b/smash/web/forms/forms.py @@ -4,11 +4,13 @@ from collections import OrderedDict from django import forms from django.forms import ModelForm, Form +from django.utils import timezone from django.utils.dates import MONTHS +from web.algorithm import VerhoeffAlgorithm from web.models import StudySubject, Worker, Appointment, Visit, AppointmentType, ContactAttempt, AppointmentTypeLink, \ - Availability, Holiday, VoucherType, VoucherTypePrice -from web.models.constants import SUBJECT_TYPE_CHOICES + Availability, Holiday, VoucherType, VoucherTypePrice, Voucher +from web.models.constants import SUBJECT_TYPE_CHOICES, VOUCHER_STATUS_NEW, VOUCHER_STATUS_USED from web.views.notifications import get_filter_locations """ @@ -404,3 +406,48 @@ class VoucherTypePriceForm(ModelForm): class Meta: model = VoucherTypePrice exclude = ['voucher_type'] + + +class VoucherForm(ModelForm): + class Meta: + model = Voucher + fields = '__all__' + + def __init__(self, *args, **kwargs): + super(VoucherForm, self).__init__(*args, **kwargs) + self.fields['number'].widget.attrs['readonly'] = True + self.fields['number'].required = False + + self.fields['issue_date'].widget.attrs['readonly'] = True + self.fields['issue_date'].required = False + self.fields['expiry_date'].widget.attrs['readonly'] = True + self.fields['expiry_date'].required = False + self.fields['use_date'].widget.attrs['readonly'] = True + instance = getattr(self, 'instance', None) + if instance and instance.pk: + self.fields['voucher_type'].widget.attrs['readonly'] = True + if instance.status != VOUCHER_STATUS_NEW: + self.fields['status'].widget.attrs['readonly'] = True + self.fields['feedback'].widget.attrs['readonly'] = True + self.fields['usage_partner'].widget.attrs['readonly'] = True + + def clean(self): + if self.cleaned_data["status"] == VOUCHER_STATUS_USED and not self.cleaned_data["usage_partner"]: + self.add_error('usage_partner', "Partner must be defined for used voucher") + if self.cleaned_data["status"] != VOUCHER_STATUS_USED and self.cleaned_data["usage_partner"]: + self.add_error('status', "Status must be used for voucher with defined partner") + + def save(self, commit=True): + instance = super(VoucherForm, self).save(commit=False) + if not instance.id: + instance.issue_date = timezone.now() + instance.expiry_date = instance.issue_date + datetime.timedelta(days=92) + max_id = str(0).zfill(5) + if Voucher.objects.latest('id'): + max_id = str(Voucher.objects.latest('id').id).zfill(5) + instance.number = max_id + VerhoeffAlgorithm.calculate_verhoeff_check_sum(max_id) + if instance.status == VOUCHER_STATUS_USED and not instance.use_date: + instance.use_date = timezone.now() + + if commit: + instance.save() diff --git a/smash/web/forms/subject_forms.py b/smash/web/forms/subject_forms.py index cc717842d2065b99fb9e622ebc63ae0ec17fe785..05fe533ea79251c871e1a6c9b66454384758e437 100644 --- a/smash/web/forms/subject_forms.py +++ b/smash/web/forms/subject_forms.py @@ -3,6 +3,7 @@ import logging from django import forms from django.forms import ModelForm +from web.algorithm import VerhoeffAlgorithm, LuhnAlgorithm from web.forms.forms import DATEPICKER_DATE_ATTRS from web.models import Subject from web.models.constants import COUNTRY_OTHER_ID @@ -26,75 +27,14 @@ def is_valid_social_security_number(number): return False if not number.isdigit(): return False - if not is_luhn_valid(number[:12]): + if not LuhnAlgorithm.is_luhn_valid(number[:12]): return False - if not is_valid_verhoeff(number[:11] + number[12]): + if not VerhoeffAlgorithm.is_valid_verhoeff(number[:11] + number[12]): return False return True -def luhn_checksum(card_number): - def digits_of(n): - return [int(d) for d in str(n)] - - digits = digits_of(card_number) - odd_digits = digits[-1::-2] - even_digits = digits[-2::-2] - checksum = 0 - checksum += sum(odd_digits) - for d in even_digits: - checksum += sum(digits_of(d * 2)) - return checksum % 10 - - -def is_luhn_valid(card_number): - return luhn_checksum(card_number) == 0 - - -verhoeff_multiplication_table = ( - (0, 1, 2, 3, 4, 5, 6, 7, 8, 9), - (1, 2, 3, 4, 0, 6, 7, 8, 9, 5), - (2, 3, 4, 0, 1, 7, 8, 9, 5, 6), - (3, 4, 0, 1, 2, 8, 9, 5, 6, 7), - (4, 0, 1, 2, 3, 9, 5, 6, 7, 8), - (5, 9, 8, 7, 6, 0, 4, 3, 2, 1), - (6, 5, 9, 8, 7, 1, 0, 4, 3, 2), - (7, 6, 5, 9, 8, 2, 1, 0, 4, 3), - (8, 7, 6, 5, 9, 3, 2, 1, 0, 4), - (9, 8, 7, 6, 5, 4, 3, 2, 1, 0)) - -verhoeff_permutation_table = ( - (0, 1, 2, 3, 4, 5, 6, 7, 8, 9), - (1, 5, 7, 6, 2, 8, 3, 0, 9, 4), - (5, 8, 0, 3, 7, 9, 6, 1, 4, 2), - (8, 9, 1, 6, 0, 4, 3, 5, 2, 7), - (9, 4, 5, 3, 1, 2, 6, 8, 7, 0), - (4, 2, 8, 6, 5, 7, 3, 9, 0, 1), - (2, 7, 9, 3, 8, 0, 6, 4, 1, 5), - (7, 0, 4, 6, 9, 1, 3, 2, 5, 8)) - - -def verhoeff_checksum(number): - """Calculate the Verhoeff checksum over the provided number. The checksum - is returned as an int. Valid numbers should have a checksum of 0.""" - # transform number list - number = tuple(int(n) for n in reversed(str(number))) - # calculate checksum - check = 0 - for i, n in enumerate(number): - check = verhoeff_multiplication_table[check][verhoeff_permutation_table[i % 8][n]] - return check - - -def is_valid_verhoeff(number): - return verhoeff_checksum(number) == 0 - - -def calculate_verhoeff_check_sum(number): - return str(verhoeff_multiplication_table[verhoeff_checksum(str(number) + '0')].index(0)) - - FIELD_ORDER = ["first_name", "last_name", "sex", "date_born", "social_security_number", "default_written_communication_language", "languages", "phone_number", "phone_number_2", "phone_number_3", "address", "city", "postal_code", "country"] diff --git a/smash/web/migrations/0092_voucher.py b/smash/web/migrations/0092_voucher.py new file mode 100644 index 0000000000000000000000000000000000000000..963120ee80b966459eebaf0c9490d56b9ca37a85 --- /dev/null +++ b/smash/web/migrations/0092_voucher.py @@ -0,0 +1,31 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.7 on 2017-12-08 15:00 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('web', '0091_auto_20171208_1312'), + ] + + operations = [ + migrations.CreateModel( + name='Voucher', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('number', models.CharField(max_length=10, unique=True, verbose_name=b'Number')), + ('issue_date', models.DateField(verbose_name=b'Issue date')), + ('expiry_date', models.DateField(verbose_name=b'Expiry date')), + ('use_date', models.DateField(verbose_name=b'Use date')), + ('status', models.CharField(choices=[(b'NEW', b'New'), (b'USED', b'Used'), (b'EXPIRED', b'Expired')], default=b'NEW', max_length=20, verbose_name=b'Status')), + ('feedback', models.TextField(blank=True, max_length=2000, verbose_name=b'Feedback')), + ('study_subject', models.ForeignKey(editable=False, on_delete=django.db.models.deletion.CASCADE, to='web.StudySubject')), + ('usage_partner', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='web.Worker')), + ('voucher_type', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='web.VoucherType')), + ], + ), + ] diff --git a/smash/web/migrations/0093_auto_20171208_1508.py b/smash/web/migrations/0093_auto_20171208_1508.py new file mode 100644 index 0000000000000000000000000000000000000000..839c2936a2f41311e6f79358abe979c4abe41b83 --- /dev/null +++ b/smash/web/migrations/0093_auto_20171208_1508.py @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.7 on 2017-12-08 15:08 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('web', '0092_voucher'), + ] + + operations = [ + migrations.AlterField( + model_name='voucher', + name='usage_partner', + field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='web.Worker'), + ), + ] diff --git a/smash/web/migrations/0094_auto_20171208_1508.py b/smash/web/migrations/0094_auto_20171208_1508.py new file mode 100644 index 0000000000000000000000000000000000000000..5ca263649479e21e768968f07d8db7ba7d3d65ee --- /dev/null +++ b/smash/web/migrations/0094_auto_20171208_1508.py @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.7 on 2017-12-08 15:08 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('web', '0093_auto_20171208_1508'), + ] + + operations = [ + migrations.AlterField( + model_name='voucher', + name='usage_partner', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='web.Worker'), + ), + ] diff --git a/smash/web/migrations/0095_auto_20171208_1509.py b/smash/web/migrations/0095_auto_20171208_1509.py new file mode 100644 index 0000000000000000000000000000000000000000..37b50905594f0b4d2e34c07991813bb36b5fe7a9 --- /dev/null +++ b/smash/web/migrations/0095_auto_20171208_1509.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.7 on 2017-12-08 15:09 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('web', '0094_auto_20171208_1508'), + ] + + operations = [ + migrations.AlterField( + model_name='voucher', + name='use_date', + field=models.DateField(null=True, verbose_name=b'Use date'), + ), + ] diff --git a/smash/web/migrations/0096_auto_20171208_1509.py b/smash/web/migrations/0096_auto_20171208_1509.py new file mode 100644 index 0000000000000000000000000000000000000000..b076d5c3f0bcb912bab2db36a94f67bc15746c85 --- /dev/null +++ b/smash/web/migrations/0096_auto_20171208_1509.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.7 on 2017-12-08 15:09 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('web', '0095_auto_20171208_1509'), + ] + + operations = [ + migrations.AlterField( + model_name='voucher', + name='use_date', + field=models.DateField(blank=True, null=True, verbose_name=b'Use date'), + ), + ] diff --git a/smash/web/models/__init__.py b/smash/web/models/__init__.py index 6b9eac7eed601cc8b85949cb752d87ecce2f2764..77a30db094dfd4b977b2ca164a0070b39ea1e8cc 100644 --- a/smash/web/models/__init__.py +++ b/smash/web/models/__init__.py @@ -27,6 +27,7 @@ from item import Item from language import Language from subject import Subject from study_subject import StudySubject +from voucher import Voucher from study_subject_list import StudySubjectList from study_visit_list import StudyVisitList from appointment_list import AppointmentList @@ -38,5 +39,5 @@ from inconsistent_subject import InconsistentSubject, InconsistentField __all__ = [Study, FlyingTeam, Appointment, AppointmentType, Availability, Holiday, Item, Language, Location, Room, Subject, StudySubject, StudySubjectList, SubjectColumns, StudyNotificationParameters, AppointmentList, AppointmentColumns, Visit, Worker, ContactAttempt, ConfigurationItem, MailTemplate, - AppointmentTypeLink, VoucherType, VoucherTypePrice, + AppointmentTypeLink, VoucherType, VoucherTypePrice, Voucher, MissingSubject, InconsistentSubject, InconsistentField, Country, StudyColumns, VisitColumns, StudyVisitList] diff --git a/smash/web/models/voucher.py b/smash/web/models/voucher.py index e9e5978735d066365e75307047af72792042f8c7..f7cc8f15c50c5c84cec75c33483678f4abab4ba0 100644 --- a/smash/web/models/voucher.py +++ b/smash/web/models/voucher.py @@ -1,11 +1,9 @@ # coding=utf-8 -import datetime -from time import timezone from django.db import models -from models import VoucherType, StudySubject, Worker -from models.constants import VOUCHER_STATUS_CHOICES, VOUCHER_STATUS_NEW +from web.models import VoucherType, StudySubject, Worker +from web.models.constants import VOUCHER_STATUS_CHOICES, VOUCHER_STATUS_NEW class Voucher(models.Model): @@ -22,7 +20,7 @@ class Voucher(models.Model): issue_date = models.DateField(verbose_name='Issue date', null=False) expiry_date = models.DateField(verbose_name='Expiry date', null=False) - use_date = models.DateField(verbose_name='Use date') + use_date = models.DateField(verbose_name='Use date', null=True, blank=True) voucher_type = models.ForeignKey( VoucherType, on_delete=models.CASCADE, @@ -36,7 +34,7 @@ class Voucher(models.Model): editable=False ) - status = models.CharField(max_length=20, choices=VOUCHER_STATUS_CHOICES.items(), + status = models.CharField(max_length=20, choices=VOUCHER_STATUS_CHOICES, verbose_name='Status', default=VOUCHER_STATUS_NEW ) @@ -49,14 +47,10 @@ class Voucher(models.Model): usage_partner = models.ForeignKey( Worker, on_delete=models.CASCADE, + null=True, + blank=True ) - def save(self, *args, **kwargs): - if not self.id: - self.issue_date = timezone.now() - self.expiry_date = self.issue_date + datetime.timedelta(days=92) - return super(Voucher, self).save(*args, **kwargs) - def __str__(self): return "%s - %s %s" % (self.number, self.study_subject.subject.first_name, self.study_subject.subject.last_name) diff --git a/smash/web/templates/sidebar.html b/smash/web/templates/sidebar.html index a8dd1d750c13fb5c824c11c40f9d9681bf535126..71c325a1cc768df50f5898b59b8e3e1b1aeefa6a 100644 --- a/smash/web/templates/sidebar.html +++ b/smash/web/templates/sidebar.html @@ -57,6 +57,14 @@ <span>Export</span> </a> </li> + + <li data-desc="vouchers"> + <a href="{% url 'web.views.vouchers' %}"> + <i class="fa fa-user-md"></i> + <span>Vouchers</span> + </a> + </li> + <li data-desc="configuration" class="treeview"> <a href="#"> <i class="fa fa-wrench"></i> <span>Configuration</span> diff --git a/smash/web/templates/vouchers/add.html b/smash/web/templates/vouchers/add.html new file mode 100644 index 0000000000000000000000000000000000000000..994ab3cb9337086474d330f2b288d6a4aaa2bba5 --- /dev/null +++ b/smash/web/templates/vouchers/add.html @@ -0,0 +1,9 @@ +{% extends "vouchers/add_edit.html" %} + +{% block page_header %}New voucher{% endblock page_header %} + +{% block title %}{{ block.super }} - Add voucher{% endblock %} + +{% block form-title %}Enter voucher details{% endblock %} + +{% block save-button %}Add{% endblock %} diff --git a/smash/web/templates/vouchers/add_edit.html b/smash/web/templates/vouchers/add_edit.html new file mode 100644 index 0000000000000000000000000000000000000000..8e5e5dfbaf497bf4a88f70ded534fd2e8ef92a8f --- /dev/null +++ b/smash/web/templates/vouchers/add_edit.html @@ -0,0 +1,77 @@ +{% extends "_base.html" %} +{% load static %} +{% load filters %} + +{% block styles %} + {{ block.super }} + <link rel="stylesheet" href="{% static 'AdminLTE/plugins/awesomplete/awesomplete.css' %}"/> + +{% endblock styles %} + +{% block ui_active_tab %}'configuration'{% endblock ui_active_tab %} +{% block page_description %}{% endblock page_description %} + +{% block breadcrumb %} + {% include "vouchers/breadcrumb.html" %} +{% endblock breadcrumb %} + +{% block maincontent %} + + {% block content %} + <div class="row"> + <div class="col-md-12"> + <div class="box box-success"> + <div class="box-header with-border"> + <h3 class="box-title">{% block form-title %}Enter voucher details{% endblock %}</h3> + </div> + + + <form method="post" action="" class="form-horizontal" enctype="multipart/form-data"> + {% csrf_token %} + + <div class="box-body"> + {% for field in form %} + <div class="form-group {% if field.errors %}has-error{% endif %}"> + <label class="col-sm-4 col-lg-offset-1 col-lg-2 control-label"> + {{ field.label }} + </label> + + <div class="col-sm-8 col-lg-4"> + {{ field|add_class:'form-control' }} + {% if field.errors %} + <span class="help-block">{{ field.errors }}</span> + {% endif %} + </div> + + + </div> + {% endfor %} + </div><!-- /.box-body --> + <div class="box-footer"> + <div class="col-sm-6"> + <button type="submit" class="btn btn-block btn-success">{% block save-button %} + Add{% endblock %} + </button> + </div> + <div class="col-sm-6"> + <a href="{% url 'web.views.vouchers' %}" + class="btn btn-block btn-default">Cancel</a> + </div> + </div><!-- /.box-footer --> + </form> + </div> + + </div> + </div> + + {% endblock %} + + +{% endblock maincontent %} + +{% block scripts %} + {{ block.super }} + + <script src="{% static 'AdminLTE/plugins/awesomplete/awesomplete.min.js' %}"></script> + +{% endblock scripts %} \ No newline at end of file diff --git a/smash/web/templates/vouchers/breadcrumb.html b/smash/web/templates/vouchers/breadcrumb.html new file mode 100644 index 0000000000000000000000000000000000000000..f71acc808f35010d4240224a41f00db70a830f0d --- /dev/null +++ b/smash/web/templates/vouchers/breadcrumb.html @@ -0,0 +1,2 @@ +<li><a href="{% url 'web.views.appointments' %}"><i class="fa fa-dashboard"></i> Dashboard</a></li> +<li class="active"><a href="{% url 'web.views.vouchers' %}">Vouchers</a></li> \ No newline at end of file diff --git a/smash/web/templates/vouchers/edit.html b/smash/web/templates/vouchers/edit.html new file mode 100644 index 0000000000000000000000000000000000000000..20e1bea665a6be2c70b8f260a23d8fee1e2b1ba5 --- /dev/null +++ b/smash/web/templates/vouchers/edit.html @@ -0,0 +1,10 @@ +{% extends "vouchers/add_edit.html" %} + +{% block page_header %}Edit voucher "{{ voucher.number }}"{% endblock page_header %} + +{% block title %}{{ block.super }} - Edit voucher "{{ voucher.number }}"{% endblock %} + +{% block form-title %}Enter voucher details{% endblock %} + +{% block save-button %}Save{% endblock %} + diff --git a/smash/web/templates/vouchers/list.html b/smash/web/templates/vouchers/list.html new file mode 100644 index 0000000000000000000000000000000000000000..7a29b16f449daa5efa1baf27c23dc3d98d17c69e --- /dev/null +++ b/smash/web/templates/vouchers/list.html @@ -0,0 +1,74 @@ +{% extends "_base.html" %} +{% load static %} + +{% block styles %} + {{ block.super }} + <!-- DataTables --> + <link rel="stylesheet" href="{% static 'AdminLTE/plugins/datatables/dataTables.bootstrap.css' %}"> +{% endblock styles %} + +{% block ui_active_tab %}'configuration'{% endblock ui_active_tab %} +{% block page_header %}Vouchers{% endblock page_header %} +{% block page_description %}{% endblock page_description %} + +{% block breadcrumb %} + {% include "vouchers/breadcrumb.html" %} +{% endblock breadcrumb %} + +{% block maincontent %} + + <div class="box-body"> + <table id="table" class="table table-bordered table-striped"> + <thead> + <tr> + <th>Number</th> + <th>First name</th> + <th>Last name</th> + <th>Issue date</th> + <th>Expiry date</th> + <th>Status</th> + <th>Use date</th> + <th>Partner</th> + <th>Feedback</th> + <th>Edit</th> + </tr> + </thead> + <tbody> + {% for voucher in vouchers %} + <tr> + <td>{{ voucher.number }}</td> + <td>{{ voucher.study_subject.subject.first_name }}</td> + <td>{{ voucher.study_subject.subject.last_name }}</td> + <td>{{ voucher.issue_date }}</td> + <td>{{ voucher.expiry_date }}</td> + <td>{{ voucher.status }}</td> + <td>{{ voucher.use_date }}</td> + <td>{{ voucher.usage_partner.first_name }} {{ voucher.usage_partner.last_name }}</td> + <td>{{ voucher.feedback }}</td> + <td><a href="{% url 'web.views.voucher_edit' voucher.id %}"><i class="fa fa-edit"></i></a></td> + </tr> + {% endfor %} + </tbody> + </table> + </div> +{% endblock maincontent %} + +{% block scripts %} + {{ block.super }} + + <script src="{% static 'AdminLTE/plugins/datatables/jquery.dataTables.min.js' %}"></script> + <script src="{% static 'AdminLTE/plugins/datatables/dataTables.bootstrap.min.js' %}"></script> + + <script> + $(function () { + $('#table').DataTable({ + "paging": true, + "lengthChange": false, + "searching": true, + "ordering": true, + "info": true, + "autoWidth": false + }); + }); + </script> +{% endblock scripts %} diff --git a/smash/web/urls.py b/smash/web/urls.py index c2bc3d3bd480fecf6f3977d6c3ed209af04cdb34..1ee0abb3b5510f17ced9243d6cec7f7c15543f5a 100644 --- a/smash/web/urls.py +++ b/smash/web/urls.py @@ -178,6 +178,14 @@ urlpatterns = [ url(r'^voucher_types/(?P<voucher_type_id>\d+)/prices/(?P<pk>\d+)/edit$', views.voucher_type_price.VoucherTypePriceEditView.as_view(), name='web.views.voucher_type_price_edit'), + #################### + # VOUCHERS # + #################### + + url(r'^vouchers$', views.voucher.VoucherListView.as_view(), name='web.views.vouchers'), + url(r'^vouchers/add$', views.voucher.VoucherCreateView.as_view(), name='web.views.voucher_add'), + url(r'^vouchers/(?P<pk>\d+)/edit$', views.voucher.VoucherEditView.as_view(), name='web.views.voucher_edit'), + #################### # STATISTICS # #################### diff --git a/smash/web/views/__init__.py b/smash/web/views/__init__.py index 01bdb13fac46bb895c6e33ebc0b9a78f9089461d..9a8ed660a3ba8424b17d85a53d890c66c76cfd66 100644 --- a/smash/web/views/__init__.py +++ b/smash/web/views/__init__.py @@ -72,6 +72,7 @@ import export import contact_attempt import configuration_item import language +import voucher import voucher_type import voucher_type_price import redcap diff --git a/smash/web/views/voucher.py b/smash/web/views/voucher.py new file mode 100644 index 0000000000000000000000000000000000000000..a054d9d0ee753e8d9c248305b03dd5a0a334035a --- /dev/null +++ b/smash/web/views/voucher.py @@ -0,0 +1,55 @@ +# coding=utf-8 +import logging + +from django.contrib.messages.views import SuccessMessageMixin +from django.urls import reverse_lazy +from django.views.generic import CreateView +from django.views.generic import ListView +from django.views.generic import UpdateView + +from web.forms.forms import VoucherForm +from web.models import Voucher +from web.models.constants import GLOBAL_STUDY_ID +from . import WrappedView + +logger = logging.getLogger(__name__) + + +class VoucherListView(ListView, WrappedView): + model = Voucher + context_object_name = "vouchers" + template_name = 'vouchers/list.html' + + +class VoucherCreateView(CreateView, WrappedView): + form_class = VoucherForm + model = Voucher + + template_name = "vouchers/add.html" + success_url = reverse_lazy('web.views.vouchers') + success_message = "Voucher type created" + + def form_valid(self, form): + form.instance.study_id = GLOBAL_STUDY_ID + # noinspection PyUnresolvedReferences + form.instance.study_subject_id = self.request.GET.get("study_subject_id", -1) + return super(VoucherCreateView, self).form_valid(form) + + def get_success_url(self, **kwargs): + # noinspection PyUnresolvedReferences + return reverse_lazy('web.views.subject_edit', kwargs={'id': self.request.GET.get("study_subject_id", -1)}) + + +class VoucherEditView(SuccessMessageMixin, UpdateView, WrappedView): + form_class = VoucherForm + model = Voucher + + success_url = reverse_lazy('web.views.vouchers') + success_message = "Voucher saved successfully" + template_name = "vouchers/edit.html" + context_object_name = "voucher_type" + + def get_success_url(self, **kwargs): + # noinspection PyUnresolvedReferences + study_subject_id = Voucher.objects.get(id=self.kwargs['pk']).study_subject.id + return reverse_lazy('web.views.subject_edit', kwargs={'id': study_subject_id})