diff --git a/smash/web/forms/voucher_forms.py b/smash/web/forms/voucher_forms.py index 7768920a81e851f7cc00a4174f3c18b28eb48fb6..67dabe3c04ee6675ed6a721c71d9e1002d02798b 100644 --- a/smash/web/forms/voucher_forms.py +++ b/smash/web/forms/voucher_forms.py @@ -47,6 +47,7 @@ class VoucherForm(ModelForm): self.fields['number'].widget.attrs['readonly'] = True self.fields['number'].required = False + self.fields['issue_worker'].widget.attrs['readonly'] = True self.fields['issue_date'].widget.attrs['readonly'] = True self.fields['issue_date'].required = False diff --git a/smash/web/migrations/0114_auto_20180611_0950.py b/smash/web/migrations/0114_auto_20180611_0950.py new file mode 100644 index 0000000000000000000000000000000000000000..510157daf86e456fad233b0ab7d32af5452baa9a --- /dev/null +++ b/smash/web/migrations/0114_auto_20180611_0950.py @@ -0,0 +1,17 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.7 on 2018-06-11 09:50 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + dependencies = [ + ('web', '0113_auto_20180608_1258'), + ] + + operations = [ + migrations.RunSQL('delete from web_voucherpartnersession;'), + migrations.RunSQL('delete from web_voucher;'), + ] diff --git a/smash/web/migrations/0115_auto_20180611_0950.py b/smash/web/migrations/0115_auto_20180611_0950.py new file mode 100644 index 0000000000000000000000000000000000000000..62cb787471b843b8cb36ef0107f65e3e67956e27 --- /dev/null +++ b/smash/web/migrations/0115_auto_20180611_0950.py @@ -0,0 +1,137 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.7 on 2018-06-11 09:50 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + dependencies = [ + ('web', '0114_auto_20180611_0950'), + ] + + operations = [ + migrations.AddField( + model_name='voucher', + name='issue_worker', + field=models.ForeignKey(default=0, on_delete=django.db.models.deletion.CASCADE, + related_name='issued_vouchers', to='web.Worker', verbose_name=b'Issued by'), + preserve_default=False, + ), + migrations.AlterField( + model_name='studycolumns', + name='comments', + field=models.BooleanField(default=True, verbose_name=b'Comments'), + ), + migrations.AlterField( + model_name='studycolumns', + name='datetime_contact_reminder', + field=models.BooleanField(default=True, verbose_name=b'Please make a contact on'), + ), + migrations.AlterField( + model_name='studycolumns', + name='default_location', + field=models.BooleanField(default=True, verbose_name=b'Default appointment location'), + ), + migrations.AlterField( + model_name='studycolumns', + name='diagnosis', + field=models.BooleanField(default=True, verbose_name=b'Diagnosis'), + ), + migrations.AlterField( + model_name='studycolumns', + name='flying_team', + field=models.BooleanField(default=True, verbose_name=b'Default flying team location (if applicable)'), + ), + migrations.AlterField( + model_name='studycolumns', + name='health_partner', + field=models.BooleanField(default=False, verbose_name=b'Health partner'), + ), + migrations.AlterField( + model_name='studycolumns', + name='health_partner_feedback_agreement', + field=models.BooleanField(default=False, verbose_name=b'Agrees to give information to referral'), + ), + migrations.AlterField( + model_name='studycolumns', + name='information_sent', + field=models.BooleanField(default=True, verbose_name=b'Information sent'), + ), + migrations.AlterField( + model_name='studycolumns', + name='mpower_id', + field=models.BooleanField(default=True, verbose_name=b'MPower ID'), + ), + migrations.AlterField( + model_name='studycolumns', + name='nd_number', + field=models.BooleanField(default=True, verbose_name=b'ND number'), + ), + migrations.AlterField( + model_name='studycolumns', + name='pd_in_family', + field=models.BooleanField(default=True, verbose_name=b'PD in family'), + ), + migrations.AlterField( + model_name='studycolumns', + name='postponed', + field=models.BooleanField(default=True, verbose_name=b'Postponed'), + ), + migrations.AlterField( + model_name='studycolumns', + name='previously_in_study', + field=models.BooleanField(default=False, verbose_name=b'Previously in PDP study'), + ), + migrations.AlterField( + model_name='studycolumns', + name='referral', + field=models.BooleanField(default=True, verbose_name=b'Referred by'), + ), + migrations.AlterField( + model_name='studycolumns', + name='referral_letter', + field=models.BooleanField(default=False, verbose_name=b'Referral letter'), + ), + migrations.AlterField( + model_name='studycolumns', + name='resign_reason', + field=models.BooleanField(default=True, verbose_name=b'Resign reason'), + ), + migrations.AlterField( + model_name='studycolumns', + name='resigned', + field=models.BooleanField(default=True, verbose_name=b'Resigned'), + ), + migrations.AlterField( + model_name='studycolumns', + name='screening', + field=models.BooleanField(default=False, verbose_name=b'Screening'), + ), + migrations.AlterField( + model_name='studycolumns', + name='screening_number', + field=models.BooleanField(default=True, verbose_name=b'Screening number'), + ), + migrations.AlterField( + model_name='studycolumns', + name='type', + field=models.BooleanField(default=True, verbose_name=b'Type'), + ), + migrations.AlterField( + model_name='studycolumns', + name='voucher_types', + field=models.BooleanField(default=False, verbose_name=b'Voucher types'), + ), + migrations.AlterField( + model_name='studycolumns', + name='vouchers', + field=models.BooleanField(default=False, verbose_name=b'Vouchers'), + ), + migrations.AlterField( + model_name='studycolumns', + name='year_of_diagnosis', + field=models.BooleanField(default=True, verbose_name=b'Year of diagnosis (YYYY)'), + ), + ] diff --git a/smash/web/models/mail_template.py b/smash/web/models/mail_template.py index f80b0e554c27f31bb61e72026edc98c05fc678b4..50739c4b14faf152d6fc445b5d1bf11f718ffc64 100644 --- a/smash/web/models/mail_template.py +++ b/smash/web/models/mail_template.py @@ -45,7 +45,8 @@ class MailTemplate(models.Model): get_formatted_time(DATE_FORMAT_FULL)), ("##DATE_SHORT##", "Current date when the mail will be generated (short format)", now.strftime(DATE_FORMAT_SHORT)), - ("##WORKER##", "The full name of the currently logged in user", "") + ("##WORKER##", "The full name of the currently logged in user", ""), + ("##WORKER_EMAIL##", "Email address of the currently logged in user", "") ] MAILS_TEMPLATE_SUBJECT_TAGS = [ @@ -176,11 +177,11 @@ class MailTemplate(models.Model): locale_name = self.get_locale_name() with setlocale(locale_name.encode('utf8')): replacements = {} - self._add_generic_replacements(replacements, Worker.get_by_user(user)) - self._add_appointment_replacements(replacements, appointment) - self._add_visit_replacements(replacements, visit) - self._add_subject_replacements(replacements, study_subject) - self._add_voucher_replacements(replacements, voucher) + replacements.update(self.get_generic_replacements(Worker.get_by_user(user))) + replacements.update(self.get_appointment_replacements(appointment)) + replacements.update(self.get_visit_replacements(visit)) + replacements.update(self.get_subject_replacements(study_subject)) + replacements.update(self.get_voucher_replacements(voucher)) process_file(self.template_file.path, stream, replacements) return stream @@ -193,64 +194,75 @@ class MailTemplate(models.Model): locale_name = self.language.windows_locale_name return locale_name - def _add_generic_replacements(self, replacements, worker): + @staticmethod + def get_generic_replacements(worker): current_datetime = datetime.datetime.now() - replacements.update({ + email = '' + if worker is not None: + email = worker.email + return { "##DATE_FULL##": current_datetime.strftime(DATE_FORMAT_FULL).decode(date_format_encoding()), "##DATE_SHORT##": current_datetime.strftime(DATE_FORMAT_SHORT).decode(date_format_encoding()), - "##WORKER##": unicode(worker) - }) - - def _add_appointment_replacements(self, replacements, appointment): - if appointment is not None: - if appointment.worker_assigned is not None: - worker_phone_number = appointment.worker_assigned.phone_number - worker_email_address = appointment.worker_assigned.email - else: - worker_phone_number = "" - worker_email_address = "" - if appointment.datetime_when is not None: - appointment_date_full = appointment.datetime_when.strftime(DATETIME_FORMAT).decode( - date_format_encoding()) - appointment_date_short = appointment.datetime_when.strftime(DATE_FORMAT_SHORT).decode( - date_format_encoding()) - appointment_date_time = appointment.datetime_when.strftime(DATE_FORMAT_TIME).decode( - date_format_encoding()) - else: - appointment_date_full = appointment_date_short = appointment_date_time = "" - replacements.update({ - "##A_DATE_FULL##": appointment_date_full, - "##A_DATE_SHORT##": appointment_date_short, - "##A_TIME##": appointment_date_time, - "##A_FLYING_TEAM##": str(appointment.flying_team), - "##A_STATUS##": appointment.get_status_display(), - "##A_LOCATION##": appointment.location.name, - "##A_LOCATION_OR_FLYINGTEAM##": str(appointment.flying_team) or appointment.location.name, - "##A_WORKER##": unicode(appointment.worker_assigned), - '##A_WORKER_PHONE##': worker_phone_number, - '##A_WORKER_EMAIL##': worker_email_address, - "##A_ROOM##": str(appointment.room), - "##A_LENGTH##": appointment.length, - "##A_TYPES##": ", ".join([a.description for a in appointment.appointment_types.all()]) - }) - - def _add_visit_replacements(self, replacements, visit): + "##WORKER##": unicode(worker), + "##WORKER_EMAIL##": email + } + + @staticmethod + def get_appointment_replacements(appointment): + if appointment is None: + return {} + + if appointment.worker_assigned is not None: + worker_phone_number = appointment.worker_assigned.phone_number + worker_email_address = appointment.worker_assigned.email + else: + worker_phone_number = "" + worker_email_address = "" + if appointment.datetime_when is not None: + appointment_date_full = appointment.datetime_when.strftime(DATETIME_FORMAT).decode( + date_format_encoding()) + appointment_date_short = appointment.datetime_when.strftime(DATE_FORMAT_SHORT).decode( + date_format_encoding()) + appointment_date_time = appointment.datetime_when.strftime(DATE_FORMAT_TIME).decode( + date_format_encoding()) + else: + appointment_date_full = appointment_date_short = appointment_date_time = "" + return { + "##A_DATE_FULL##": appointment_date_full, + "##A_DATE_SHORT##": appointment_date_short, + "##A_TIME##": appointment_date_time, + "##A_FLYING_TEAM##": str(appointment.flying_team), + "##A_STATUS##": appointment.get_status_display(), + "##A_LOCATION##": appointment.location.name, + "##A_LOCATION_OR_FLYINGTEAM##": str(appointment.flying_team) or appointment.location.name, + "##A_WORKER##": unicode(appointment.worker_assigned), + '##A_WORKER_PHONE##': worker_phone_number, + '##A_WORKER_EMAIL##': worker_email_address, + "##A_ROOM##": str(appointment.room), + "##A_LENGTH##": appointment.length, + "##A_TYPES##": ", ".join([a.description for a in appointment.appointment_types.all()]) + } + + @staticmethod + def get_visit_replacements(visit): if visit is not None: - replacements.update({ + return { "##V_DATE_START_FULL##": visit.datetime_begin.strftime(DATETIME_FORMAT).decode(date_format_encoding()), "##V_DATE_START_SHORT##": visit.datetime_begin.strftime(DATE_FORMAT_SHORT).decode( date_format_encoding()), "##V_DATE_ENDS_FULL##": visit.datetime_end.strftime(DATETIME_FORMAT).decode(date_format_encoding()), "##V_DATE_ENDS_SHORT##": visit.datetime_end.strftime(DATE_FORMAT_SHORT).decode(date_format_encoding()), - }) + } + return {} - def _add_subject_replacements(self, replacements, study_subject): + @staticmethod + def get_subject_replacements(study_subject): if study_subject is not None: if study_subject.subject.date_born is not None: date_born = study_subject.subject.date_born.strftime(DATE_FORMAT_SHORT).decode(date_format_encoding()) else: date_born = None - replacements.update({ + return { "##S_FULL_NAME##": unicode(study_subject), "##S_FIRST_NAME##": study_subject.subject.first_name, "##S_LAST_NAME##": study_subject.subject.last_name, @@ -273,11 +285,13 @@ class MailTemplate(models.Model): "##S_TYPE##": study_subject.get_type_display(), '##S_MAIL_LANGUAGE##': str(study_subject.subject.default_written_communication_language), '##S_KNOWN_LANGUAGES##': ", ".join([l.name for l in study_subject.subject.languages.all()]) - }) + } + return {} - def _add_voucher_replacements(self, replacements, voucher): + @staticmethod + def get_voucher_replacements(voucher): if voucher is not None: - replacements.update({ + return { "##C_NUMBER##": voucher.number, "##C_PATIENT_NAME##": voucher.study_subject.subject.first_name + ' ' + voucher.study_subject.subject.last_name, "##C_VOUCHER_TYPE##": voucher.voucher_type.description, @@ -291,4 +305,5 @@ class MailTemplate(models.Model): "##C_PARTNER_COUNTRY##": unicode(voucher.usage_partner.country), "##C_PARTNER_PHONE##": voucher.usage_partner.phone_number, "##C_HOURS##": str(voucher.hours), - }) + } + return {} diff --git a/smash/web/models/voucher.py b/smash/web/models/voucher.py index 7de7d71443b2038e7af377f1a27868fda80dd33c..2f47fae06cd7c58e4c75424d0b768daa8d51aa2a 100644 --- a/smash/web/models/voucher.py +++ b/smash/web/models/voucher.py @@ -21,6 +21,8 @@ class Voucher(models.Model): issue_date = models.DateField(verbose_name='Issue date', null=False) expiry_date = models.DateField(verbose_name='Expiry date', null=False) + issue_worker = models.ForeignKey(Worker, verbose_name='Issued by', null=False, related_name='issued_vouchers') + hours = models.IntegerField( verbose_name='Hours', default=0, diff --git a/smash/web/static/js/smash.js b/smash/web/static/js/smash.js index 45122991ee81b1341c86e6e665f1faf113e255d5..f2af5cef26d8c4b20f2d93c4530785fdf9a57f5e 100644 --- a/smash/web/static/js/smash.js +++ b/smash/web/static/js/smash.js @@ -23,8 +23,10 @@ $(document).ready(function () { } else { return true; } - }) - + }); + + //disable all dropdown HTML select elements that should be readonly but aren't + $("select[readonly] option:not(:selected)").attr('disabled','disabled'); }); $.ajaxSetup({ diff --git a/smash/web/tests/forms/test_voucher_forms.py b/smash/web/tests/forms/test_voucher_forms.py index 71031d21400246576f383ac3b71ba7b22a2ed995..17a25f96a4699ef3cc7173a0633fd1854f277e8f 100644 --- a/smash/web/tests/forms/test_voucher_forms.py +++ b/smash/web/tests/forms/test_voucher_forms.py @@ -8,7 +8,7 @@ from web.models.constants import VOUCHER_STATUS_USED from web.models.worker_study_role import ROLE_CHOICES_VOUCHER_PARTNER from web.tests import LoggedInWithWorkerTestCase from web.tests.functions import create_study_subject, create_voucher_type, format_form_field, create_voucher, \ - create_worker + create_worker, create_voucher_partner logger = logging.getLogger(__name__) @@ -16,8 +16,8 @@ logger = logging.getLogger(__name__) class VoucherFormTests(LoggedInWithWorkerTestCase): def setUp(self): super(VoucherFormTests, self).setUp() - self.voucher_partner = create_worker() - WorkerStudyRole.objects.filter(worker=self.voucher_partner).update(role=ROLE_CHOICES_VOUCHER_PARTNER) + self.worker = create_worker() + self.voucher_partner = create_voucher_partner() def test_create_voucher(self): voucher_type = create_voucher_type() @@ -28,7 +28,8 @@ class VoucherFormTests(LoggedInWithWorkerTestCase): voucher_form = VoucherForm() form_data = { "status": VOUCHER_STATUS_USED, - "usage_partner": str(self.voucher_partner.id), + "usage_partner": self.voucher_partner.id, + "issue_worker": self.worker.id, "hours": 10, "voucher_type": voucher_type.id } diff --git a/smash/web/tests/functions.py b/smash/web/tests/functions.py index 0164230b3ae50aa50a93cc8979135df6c938670b..fbb6a99139489a7dd9e3e71b5b1702c170260a2f 100644 --- a/smash/web/tests/functions.py +++ b/smash/web/tests/functions.py @@ -78,11 +78,13 @@ def get_test_id(): return result -def create_voucher(study_subject=None, partner=None): +def create_voucher(study_subject=None, partner=None, worker=None): if study_subject is None: study_subject = create_study_subject() if partner is None: partner = create_voucher_partner() + if worker is None: + worker = create_worker() number = str(get_test_id()) return Voucher.objects.create(number=number, study_subject=study_subject, @@ -90,6 +92,7 @@ def create_voucher(study_subject=None, partner=None): expiry_date=get_today_midnight_date(), voucher_type=create_voucher_type(), usage_partner=partner, + issue_worker=worker, status=VOUCHER_STATUS_NEW) diff --git a/smash/web/tests/models/test_mail_template.py b/smash/web/tests/models/test_mail_template.py index 500b3a0709ffb1afdb53a0c13b15cdfa71343c96..0171459e4cdfcc835772fc4e568b2e8f13479703 100644 --- a/smash/web/tests/models/test_mail_template.py +++ b/smash/web/tests/models/test_mail_template.py @@ -9,7 +9,7 @@ from web.models.constants import MAIL_TEMPLATE_CONTEXT_APPOINTMENT, MAIL_TEMPLAT from web.models.mail_template import DATE_FORMAT_SHORT from web.tests.functions import create_language, get_resource_path, create_appointment, create_user, \ create_study_subject, \ - create_visit, create_voucher + create_visit, create_voucher, create_worker class MailTemplateModelTests(TestCase): @@ -169,3 +169,8 @@ class MailTemplateModelTests(TestCase): if not all_founds: for i in range(0, len(needles)): self.assertTrue(founds[i], "{} was not found in the generated Word document".format(needles[i])) + + def test_get_generic_replacements(self): + worker = create_worker() + result = MailTemplate.get_generic_replacements(worker) + self.assertEquals(result['##WORKER_EMAIL##'], worker.email) diff --git a/smash/web/tests/view/test_KitRequestEmailSendJob.py b/smash/web/tests/view/test_KitRequestEmailSendJob.py index 9959087201c359cb02141f5dedf0f390449e27e9..f6e58798c859a092c2481fb824cab461a3fb7d08 100644 --- a/smash/web/tests/view/test_KitRequestEmailSendJob.py +++ b/smash/web/tests/view/test_KitRequestEmailSendJob.py @@ -37,6 +37,5 @@ class KitRequestEmailSendJobTests(LoggedInTestCase): status = job.do() self.assertEqual("mail sent", status) - print len(mail.outbox) self.assertEqual(1, len(mail.outbox)) self.assertEqual(workers_count, Worker.objects.all().count()) diff --git a/smash/web/tests/view/test_voucher.py b/smash/web/tests/view/test_voucher.py index 18609fb8775bef36a332864257817c34dbc7461b..40237c10b79cdb19a4c10ecf4eee65606bf23d24 100644 --- a/smash/web/tests/view/test_voucher.py +++ b/smash/web/tests/view/test_voucher.py @@ -1,17 +1,15 @@ +import datetime import logging -import datetime from django.urls import reverse -from web.models import Worker -from web.models.worker_study_role import WORKER_VOUCHER_PARTNER -from web.views.notifications import get_today_midnight_date -from web.views.voucher import ExpireVouchersJob from web.forms import VoucherForm from web.models import Voucher from web.models.constants import VOUCHER_STATUS_NEW, VOUCHER_STATUS_USED, VOUCHER_STATUS_EXPIRED from web.tests.functions import create_voucher, create_study_subject, format_form_field, create_voucher_type, \ - create_voucher_partner + create_voucher_partner, create_worker +from web.views.notifications import get_today_midnight_date +from web.views.voucher import ExpireVouchersJob from .. import LoggedInTestCase logger = logging.getLogger(__name__) @@ -38,9 +36,12 @@ class VoucherTypeViewTests(LoggedInTestCase): usage_partner.voucher_types.add(voucher_type) usage_partner.save() + worker = create_worker() + visit_detail_form = VoucherForm() form_data = { "usage_partner": usage_partner.id, + "issue_worker": worker.id, "status": VOUCHER_STATUS_NEW, "hours": 10, "voucher_type": voucher_type.id @@ -50,7 +51,6 @@ class VoucherTypeViewTests(LoggedInTestCase): url = reverse('web.views.voucher_add') + '?study_subject_id=' + str(study_subject.id) response = self.client.post(url, data=form_data) - print response.content self.assertEqual(response.status_code, 302) self.assertEqual(1, Voucher.objects.all().count()) diff --git a/smash/web/tests/view/test_voucher_partner_session.py b/smash/web/tests/view/test_voucher_partner_session.py index 4b8033291eb0600defc0274a5c9ca848e3565d42..fa666e13b0c6389c024b887a06d7113cf6b49631 100644 --- a/smash/web/tests/view/test_voucher_partner_session.py +++ b/smash/web/tests/view/test_voucher_partner_session.py @@ -34,7 +34,6 @@ class VoucherTypeViewTests(LoggedInTestCase): url = reverse('web.views.voucher_partner_sessions_add', kwargs={'pk': voucher.id}) response = self.client.post(url, data=form_data) - print response.content self.assertEqual(response.status_code, 302) voucher = Voucher.objects.get(id=voucher.id) @@ -55,7 +54,6 @@ class VoucherTypeViewTests(LoggedInTestCase): url = reverse('web.views.voucher_partner_sessions_add', kwargs={'pk': voucher.id}) response = self.client.post(url, data=form_data) - print response.content self.assertEqual(response.status_code, 302) voucher = Voucher.objects.get(id=voucher.id) diff --git a/smash/web/views/voucher.py b/smash/web/views/voucher.py index 5323427791e0d480e0c4b485327d0d103e0d966d..d3284437be4279f414b30eebf7052877e0bfc742 100644 --- a/smash/web/views/voucher.py +++ b/smash/web/views/voucher.py @@ -11,7 +11,7 @@ from django_cron import CronJobBase, Schedule from web.views.notifications import get_today_midnight_date from web.forms import VoucherForm -from web.models import Voucher, StudySubject, MailTemplate +from web.models import Voucher, StudySubject, MailTemplate, Worker from web.models.constants import GLOBAL_STUDY_ID, VOUCHER_STATUS_NEW, VOUCHER_STATUS_EXPIRED, CRON_JOB_TIMEOUT from . import WrappedView @@ -36,6 +36,15 @@ class VoucherCreateView(CreateView, WrappedView): success_url = reverse_lazy('web.views.vouchers') success_message = "Voucher type created" + def get_initial(self): + worker = Worker.get_by_user(self.request.user) + worker_id = None + if worker is not None: + worker_id = worker.id + return { + 'issue_worker': worker_id, + } + def form_valid(self, form): form.instance.study_id = GLOBAL_STUDY_ID # noinspection PyUnresolvedReferences @@ -70,8 +79,7 @@ class VoucherEditView(SuccessMessageMixin, UpdateView, WrappedView): def get_context_data(self, *args, **kwargs): context = super(VoucherEditView, self).get_context_data(*args, **kwargs) - context['mail_templates']= MailTemplate.get_voucher_mail_templates([]) - print context + context['mail_templates'] = MailTemplate.get_voucher_mail_templates([]) return context