source: main/waeup.uniben/trunk/src/waeup/uniben/students/tests/test_browser.py @ 13261

Last change on this file since 13261 was 13252, checked in by Henrik Bettermann, 9 years ago

Only students with verdict 'A' or 'B' are eligible to book accommodation.

  • Property svn:keywords set to Id
File size: 51.5 KB
Line 
1## $Id: test_browser.py 13252 2015-09-06 07:23:27Z henrik $
2##
3## Copyright (C) 2011 Uli Fouquet & Henrik Bettermann
4## This program is free software; you can redistribute it and/or modify
5## it under the terms of the GNU General Public License as published by
6## the Free Software Foundation; either version 2 of the License, or
7## (at your option) any later version.
8##
9## This program is distributed in the hope that it will be useful,
10## but WITHOUT ANY WARRANTY; without even the implied warranty of
11## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12## GNU General Public License for more details.
13##
14## You should have received a copy of the GNU General Public License
15## along with this program; if not, write to the Free Software
16## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17##
18import os
19import shutil
20import tempfile
21import pytz
22from datetime import datetime, timedelta, date
23from StringIO import StringIO
24from hurry.workflow.interfaces import IWorkflowState, IWorkflowInfo
25from zope.securitypolicy.interfaces import IPrincipalRoleManager
26from zope.security.interfaces import Unauthorized
27from zope.component.hooks import setSite, clearSite
28from zope.component import getUtility, createObject
29from zope.interface import verify
30from zope.event import notify
31from waeup.kofa.authentication import LocalRoleSetEvent
32from waeup.kofa.app import University
33from waeup.kofa.students.tests.test_browser import StudentsFullSetup
34from waeup.kofa.students.accommodation import BedTicket
35from waeup.kofa.hostels.hostel import Hostel, Bed, NOT_OCCUPIED
36from waeup.kofa.testing import FunctionalTestCase
37from waeup.kofa.browser.tests.test_pdf import samples_dir
38from waeup.kofa.interfaces import (
39    IExtFileStore, IFileStoreNameChooser)
40from waeup.kofa.students.interfaces import IStudentsUtils
41from waeup.uniben.testing import FunctionalLayer
42
43SAMPLE_FPM = os.path.join(os.path.dirname(__file__), 'sample.fpm')
44
45class StudentUITests(StudentsFullSetup):
46    """Tests for customized student class views and pages
47    """
48
49    layer = FunctionalLayer
50
51    def setUp(self):
52        super(StudentUITests, self).setUp()
53
54    def test_next_session_allowed(self):
55        # Let's see if next_session_allowed works as expected
56        # A, ug_ft, 100
57        IWorkflowState(self.student).setState('returning')
58        self.assertTrue(self.student['studycourse'].next_session_allowed)
59        # Uniben special PG programmes have the same workflow
60        # as UG students
61        self.certificate.study_mode = 'special_pg_pt'
62        self.assertTrue(self.student['studycourse'].next_session_allowed)
63        IWorkflowState(self.student).setState('school fee paid')
64        self.assertFalse(self.student['studycourse'].next_session_allowed)
65        # Now we convert the certificate into a 'regular
66        # postgraduate certificate ...
67        self.certificate.study_mode = 'pg_ft'
68        # ... and voila next session registration is allowed
69        self.assertTrue(self.student['studycourse'].next_session_allowed)
70
71    def test_manage_access(self):
72        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
73        # The student created in the base package is a ug student
74        self.browser.open(self.manage_clearance_path)
75        self.assertMatches('...First Sitting Record...',
76                           self.browser.contents)
77        # There is no pg field in the clearance form
78        self.assertFalse('Second Higher Education Record'
79            in self.browser.contents)
80        # Now we change the study mode ...
81        self.certificate.study_mode = 'pg_ft'
82        self.browser.open(self.clearance_path)
83        # ... and additional pg clearance fields appears
84        self.assertMatches('...Second Higher Education Record...',
85                           self.browser.contents)
86        # But also fields from the ug form are displayed
87        self.assertMatches('...First Sitting Record...',
88                           self.browser.contents)
89        # The same holds for Uniben's special pg students
90        self.certificate.study_mode = 'special_pg_pt'
91        self.browser.open(self.clearance_path)
92        self.assertMatches('...Second Higher Education Record...',
93                           self.browser.contents)
94        self.assertMatches('...First Sitting Record...',
95                           self.browser.contents)
96        # We want to see the signature fields.
97        IWorkflowState(self.student).setState('returning')
98        self.browser.open(self.student_path + '/clearance_slip.pdf')
99        self.assertEqual(self.browser.headers['Status'], '200 Ok')
100        self.assertEqual(self.browser.headers['Content-Type'],
101                         'application/pdf')
102        path = os.path.join(samples_dir(), 'clearance_slip.pdf')
103        open(path, 'wb').write(self.browser.contents)
104        print "Sample PDF clearance_slip.pdf written to %s" % path
105
106    def test_student_access(self):
107        # Students can edit clearance data
108        IWorkflowState(self.student).setState('clearance started')
109        self.student.nationality = u'NG'
110        file_store = getUtility(IExtFileStore)
111        self.browser.open(self.login_path)
112        self.browser.getControl(name="form.login").value = self.student_id
113        self.browser.getControl(name="form.password").value = 'spwd'
114        self.browser.getControl("Login").click()
115        self.browser.open(self.edit_clearance_path)
116        # UG students can't edit date_of_birth, nationality and lga
117        self.assertFalse('form.date_of_birth' in self.browser.contents)
118        self.assertFalse('form.nationality' in self.browser.contents)
119        self.assertFalse('form.lga' in self.browser.contents)
120        # Clearance can only be requested if all required documents
121        # have been uploaded.
122        self.browser.getControl("Save and request clearance").click()
123        self.assertTrue('No birth certificate uploaded'
124            in self.browser.contents)
125        birth_certificate = 'My birth certificate'
126        file_id = IFileStoreNameChooser(self.student).chooseName(
127            attr="birth_certificate.jpg")
128        file_store.createFile(file_id, StringIO(birth_certificate))
129        self.browser.open(self.edit_clearance_path)
130        self.browser.getControl("Save and request clearance").click()
131
132        self.assertTrue('No guarantor/referee letter uploaded'
133            in self.browser.contents)
134        ref_let = 'My ref let'
135        file_id = IFileStoreNameChooser(self.student).chooseName(
136            attr="ref_let.jpg")
137        file_store.createFile(file_id, StringIO(ref_let))
138        self.browser.open(self.edit_clearance_path)
139        self.browser.getControl("Save and request clearance").click()
140
141        self.assertTrue('No acceptance letter uploaded'
142            in self.browser.contents)
143        acc_let = 'My acc let'
144        file_id = IFileStoreNameChooser(self.student).chooseName(
145            attr="acc_let.jpg")
146        file_store.createFile(file_id, StringIO(acc_let))
147        self.browser.open(self.edit_clearance_path)
148        self.browser.getControl("Save and request clearance").click()
149
150        self.assertTrue('No first sitting result uploaded'
151            in self.browser.contents)
152        fst_sit_scan = 'My first sitting result'
153        file_id = IFileStoreNameChooser(self.student).chooseName(
154            attr="fst_sit_scan.jpg")
155        file_store.createFile(file_id, StringIO(fst_sit_scan))
156        self.browser.open(self.edit_clearance_path)
157        self.browser.getControl("Save and request clearance").click()
158
159        #self.assertTrue('No second sitting result uploaded'
160        #    in self.browser.contents)
161        #scd_sit_scan = 'My second sitting result'
162        #file_id = IFileStoreNameChooser(self.student).chooseName(
163        #    attr="scd_sit_scan.jpg")
164        #file_store.createFile(file_id, StringIO(scd_sit_scan))
165        #self.browser.open(self.edit_clearance_path)
166        #self.browser.getControl("Save and request clearance").click()
167
168        self.assertTrue('No affidavit of non-membership of secret cults uploaded'
169            in self.browser.contents)
170        secr_cults = 'My non-membership scan'
171        file_id = IFileStoreNameChooser(self.student).chooseName(
172            attr="secr_cults.jpg")
173        file_store.createFile(file_id, StringIO(secr_cults))
174        # Clearance invitation letter is not yet available
175        self.browser.open(self.clearance_path)
176        self.assertFalse('invitation slip' in self.browser.contents)
177        self.browser.open(self.student_path + '/clearance_invitation_slip.pdf')
178        self.assertTrue('Forbidden' in self.browser.contents)
179        self.browser.open(self.edit_clearance_path)
180        self.browser.getControl("Save and request clearance").click()
181        self.assertTrue('Clearance has been requested'
182            in self.browser.contents)
183        # Now student can export physical_clearance.slip
184        self.app['configuration'].name = u'University of Benin'
185        self.student.physical_clearance_date = u'January 5th, 2015'
186        self.browser.getLink("Clearance Data").click()
187        self.browser.getLink("Download clearance invitation slip").click()
188        self.assertEqual(self.browser.headers['Status'], '200 Ok')
189        self.assertEqual(self.browser.headers['Content-Type'], 'application/pdf')
190        path = os.path.join(samples_dir(), 'clearance_invitation_slip.pdf')
191        open(path, 'wb').write(self.browser.contents)
192        print "Sample PDF clearance_invitation_slip.pdf written to %s" % path
193        # Students can open the personal edit page and see the parent_email field.
194        self.browser.open(self.student_path + '/edit_personal')
195        self.assertTrue('parent_email' in self.browser.contents)
196
197    def test_manage_payments(self):
198        # Add missing configuration data
199        self.app['configuration']['2004'].gown_fee = 150.0
200        self.app['configuration']['2004'].transfer_fee = 90.0
201        #self.app['configuration']['2004'].clearance_fee = 120.0
202        self.app['configuration']['2004'].booking_fee = 150.0
203        self.app['configuration']['2004'].maint_fee = 180.0
204
205        # Managers can add online payment tickets
206        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
207        self.browser.open(self.payments_path)
208        self.browser.getLink("Add current session payment ticket").click()
209        self.browser.getControl("Create ticket").click()
210        self.assertMatches('...Amount could not be determined...',
211                           self.browser.contents)
212        IWorkflowState(self.student).setState('cleared')
213        self.student.nationality = u'NG'
214        self.browser.open(self.payments_path + '/addop')
215        self.browser.getControl(name="form.p_category").value = ['schoolfee']
216        self.browser.getControl("Create ticket").click()
217        self.assertMatches('...ticket created...',
218                           self.browser.contents)
219        ctrl = self.browser.getControl(name='val_id')
220        value = ctrl.options[0]
221        self.browser.getLink(value).click()
222        self.assertMatches('...Amount Authorized...',
223                           self.browser.contents)
224        # Managers can open payment slip
225        self.browser.getLink("Download payment slip").click()
226        self.assertEqual(self.browser.headers['Status'], '200 Ok')
227        self.assertEqual(self.browser.headers['Content-Type'], 'application/pdf')
228        # Set ticket paid
229        ticket = self.student['payments'].items()[0][1]
230        ticket.p_state = 'paid'
231        self.browser.open(self.payments_path + '/addop')
232        self.browser.getControl(name="form.p_category").value = ['schoolfee']
233        self.browser.getControl("Create ticket").click()
234        self.assertMatches('...This type of payment has already been made...',
235                           self.browser.contents)
236        # Remove all payments so that we can add a school fee payment again
237        keys = [i for i in self.student['payments'].keys()]
238        for payment in keys:
239            del self.student['payments'][payment]
240        self.browser.open(self.payments_path + '/addop')
241        self.browser.getControl(name="form.p_category").value = ['schoolfee']
242        self.browser.getControl("Create ticket").click()
243        self.assertMatches('...ticket created...',
244                           self.browser.contents)
245        schoolfee_ticket = self.student['payments'].values()[0]
246        self.browser.open(self.payments_path + '/addop')
247        self.browser.getControl(name="form.p_category").value = ['gown']
248        self.browser.getControl("Create ticket").click()
249        self.assertMatches('...ticket created...',
250                           self.browser.contents)
251        self.browser.open(self.payments_path + '/addop')
252        #self.browser.getControl(
253        #    name="form.p_category").value = ['tempmaint_1']
254        #self.browser.getControl("Create ticket").click()
255        #self.assertMatches('...You have not yet paid the hostel application fee...',
256        #                   self.browser.contents)
257        #self.browser.open(self.payments_path + '/addop')
258        #self.browser.getControl(name="form.p_category").value = ['hostel_application']
259        #self.browser.getControl("Create ticket").click()
260        #self.assertMatches('...ticket created...',
261        #                   self.browser.contents)
262        #ha_ticket = self.student['payments'].values()[2]
263        #ha_ticket.approve()
264        #self.browser.open(self.payments_path + '/addop')
265        #self.browser.getControl(
266        #    name="form.p_category").value = ['tempmaint_1']
267        #self.browser.getControl("Create ticket").click()
268        #self.assertMatches('...ticket created...',
269        #                   self.browser.contents)
270        #self.browser.open(self.payments_path + '/addop')
271        #self.browser.getControl(
272        #    name="form.p_category").value = ['tempmaint_2']
273        #self.browser.getControl("Create ticket").click()
274        #self.assertMatches('...ticket created...',
275        #                   self.browser.contents)
276        #self.browser.open(self.payments_path + '/addop')
277        #self.browser.getControl(
278        #    name="form.p_category").value = ['tempmaint_3']
279        #self.browser.getControl("Create ticket").click()
280        #self.assertMatches('...ticket created...',
281        #                   self.browser.contents)
282        #self.browser.open(self.payments_path + '/addop')
283        # We can't test clearance payment ticket creation at the moment,
284        # since Uniben decided to deactivate clearance for ug students.
285        #self.browser.getControl(name="form.p_category").value = ['clearance']
286        #self.browser.getControl("Create ticket").click()
287        #self.assertMatches('...ticket created...',
288        #                   self.browser.contents)
289        self.browser.open(self.payments_path + '/addop')
290        self.browser.getControl(name="form.p_category").value = ['schoolfee']
291        self.browser.getControl("Create ticket").click()
292        self.assertMatches('...ticket created...',
293                           self.browser.contents)
294        # In state returning we can add a new school fee ticket since
295        # p_session and p_level is different
296        IWorkflowState(self.student).setState('returning')
297        self.browser.open(self.payments_path + '/addop')
298        self.browser.getControl(name="form.p_category").value = ['schoolfee']
299        self.browser.getControl("Create ticket").click()
300        # Uups, we forgot to add a session configuration for next session
301        self.assertMatches('...Session configuration object is not...',
302                           self.browser.contents)
303        configuration = createObject('waeup.SessionConfiguration')
304        configuration.academic_session = 2005
305        self.app['configuration'].addSessionConfiguration(configuration)
306        self.browser.open(self.payments_path + '/addop')
307        self.browser.getControl(name="form.p_category").value = ['schoolfee']
308        self.browser.getControl("Create ticket").click()
309
310
311        #self.assertMatches('...You have not yet paid your current/active session...',
312        #                   self.browser.contents)
313        ## Ok, let's pay the first schoolfee ticket.
314        #schoolfee_ticket.approve()
315        #self.browser.open(self.payments_path + '/addop')
316        #self.browser.getControl(name="form.p_category").value = ['schoolfee']
317        #self.browser.getControl("Create ticket").click()
318
319
320        self.assertMatches('...ticket created...',
321                           self.browser.contents)
322        # In state admitted school fee can't be determined
323        IWorkflowState(self.student).setState('admitted')
324        self.browser.open(self.payments_path + '/addop')
325        self.browser.getControl(name="form.p_category").value = ['schoolfee']
326        self.browser.getControl("Create ticket").click()
327        self.assertMatches('...Amount could not be determined...',
328                           self.browser.contents)
329
330        # If the session configuration doesn't exist an error message will
331        # be shown. No other requirement is being checked.
332        del self.app['configuration']['2004']
333        self.browser.open(self.payments_path)
334        self.browser.getLink("Add current session payment ticket").click()
335        self.browser.getControl("Create ticket").click()
336        self.assertMatches('...Session configuration object is not...',
337                           self.browser.contents)
338
339    def test_student_course_registration(self):
340        # Uniben students see grade instead of score on all level pages
341        # and on course ticket page.
342        IWorkflowState(self.student).setState('school fee paid')
343        self.browser.open(self.login_path)
344        self.browser.getControl(name="form.login").value = self.student_id
345        self.browser.getControl(name="form.password").value = 'spwd'
346        self.browser.getControl("Login").click()
347        # Now students can add the current study level
348        self.browser.getLink("Study Course").click()
349        self.browser.getLink("Add course list").click()
350        self.assertMatches('...Add current level 100 (Year 1)...',
351                           self.browser.contents)
352        self.browser.getControl("Create course list now").click()
353        # A level with one course ticket was created
354        self.assertEqual(self.student['studycourse']['100'].number_of_tickets, 1)
355        self.student['studycourse']['100']['COURSE1'].score = 55
356        self.browser.getLink("100").click()
357        # GPA has been properly calculated
358        self.assertEqual(self.student['studycourse']['100'].gpa_params[0], 3.0)
359        # Score is not shown but grade
360        self.assertTrue('<th>Grade</th>' in self.browser.contents)
361        self.assertFalse('<th>Score</th>' in self.browser.contents)
362        self.browser.getLink("Edit course list").click()
363        self.assertTrue('<th>Grade</th>' in self.browser.contents)
364        self.assertFalse('<th>Score</th>' in self.browser.contents)
365        self.browser.getLink("COURSE1").click()
366        self.assertFalse('Score' in self.browser.contents)
367        # Students can open the special Uniben pdf course result slip
368        self.browser.open(self.student_path + '/studycourse/100')
369        self.browser.getLink("Download course result slip").click()
370        self.assertEqual(self.browser.headers['Status'], '200 Ok')
371        self.assertEqual(self.browser.headers['Content-Type'], 'application/pdf')
372        # Even if course is mandatory, students can remove the course
373        self.student['studycourse']['100']['COURSE1'].mandatory = True
374        self.browser.open(self.student_path + '/studycourse/100')
375        self.browser.getLink("Edit course list").click()
376        ctrl = self.browser.getControl(name='val_id')
377        ctrl.getControl(value='COURSE1').selected = True
378        self.browser.getControl("Remove selected", index=0).click()
379        self.assertTrue('Successfully removed' in self.browser.contents)
380        # Students can open the customized pdf course registration slip
381        # if they have registered their course list
382        self.browser.open(
383            self.student_path + '/studycourse/100/course_registration_slip.pdf')
384        self.assertTrue('Forbidden' in self.browser.contents)
385        IWorkflowState(self.student).setState('courses registered')
386        self.browser.open(self.student_path + '/studycourse/100')
387        self.browser.getLink("Download course registration slip").click()
388        self.assertEqual(self.browser.headers['Status'], '200 Ok')
389        self.assertEqual(self.browser.headers['Content-Type'], 'application/pdf')
390
391    def test_get_returning_data(self):
392        # Student is in level 100, session 2004 with verdict A
393        utils = getUtility(IStudentsUtils)
394        self.assertEqual(utils.getReturningData(self.student),(2005, 200))
395        self.student['studycourse'].current_verdict = 'C'
396        self.assertEqual(utils.getReturningData(self.student),(2005, 110))
397        self.student['studycourse'].current_verdict = 'D'
398        self.assertEqual(utils.getReturningData(self.student),(2005, 100))
399        return
400
401    def test_set_returning_data(self):
402        # Student is in level 100, session 2004 with verdict A
403        utils = getUtility(IStudentsUtils)
404
405        utils.setReturningData(self.student)
406        self.assertEqual(self.student['studycourse'].current_session, 2005)
407        self.assertEqual(self.student['studycourse'].current_level, 200)
408
409        self.student['studycourse'].current_session = 2004
410        self.student['studycourse'].current_level = 100
411        self.student['studycourse'].current_verdict = 'C'
412        utils.setReturningData(self.student)
413        self.assertEqual(self.student['studycourse'].current_session, 2005)
414        self.assertEqual(self.student['studycourse'].current_level, 110)
415
416        self.student['studycourse'].current_session = 2004
417        self.student['studycourse'].current_level = 100
418        self.student['studycourse'].current_verdict = 'D'
419        utils.setReturningData(self.student)
420        self.assertEqual(self.student['studycourse'].current_session, 2005)
421        self.assertEqual(self.student['studycourse'].current_level, 100)
422        return
423
424    def test_set_payment_details(self):
425        self.app['configuration']['2004'].gown_fee = 150.0
426        self.app['configuration']['2004'].transfer_fee = 90.0
427        self.app['configuration']['2004'].booking_fee = 150.0
428        self.app['configuration']['2004'].maint_fee = 180.0
429
430        configuration = createObject('waeup.SessionConfiguration')
431        configuration.academic_session = 2000
432        self.app['configuration'].addSessionConfiguration(configuration)
433        configuration2 = createObject('waeup.SessionConfiguration')
434        configuration2.academic_session = 2002
435        self.app['configuration'].addSessionConfiguration(configuration2)
436        configuration3 = createObject('waeup.SessionConfiguration')
437        configuration3.academic_session = 2003
438        self.app['configuration'].addSessionConfiguration(configuration3)
439        configuration4 = createObject('waeup.SessionConfiguration')
440        configuration4.academic_session = 2005
441        self.app['configuration'].addSessionConfiguration(configuration4)
442        utils = getUtility(IStudentsUtils)
443        self.student['studycourse'].entry_session = 2002
444        self.student.nationality = u'NG'
445
446        error, payment = utils.setPaymentDetails('schoolfee',
447            self.student, None, None)
448        self.assertEqual(payment, None)
449        # Student is in state 'created' and can thus not pay.
450        self.assertTrue(u'Amount could not be determined.' in error)
451
452        # Previous session must be valid.
453        error, payment = utils.setPaymentDetails('schoolfee',
454            self.student, 2000, 300)
455        self.assertEqual(payment, None)
456        self.assertTrue(u'The previous session must not fall below' in error)
457        error, payment = utils.setPaymentDetails('schoolfee',
458            self.student, 2005, 300)
459        self.assertEqual(payment, None)
460        self.assertTrue(u'This is not a previous session' in error)
461
462        # Previous session schoolfee payment; fresh and returning
463        # are distinguished by their entry_level
464        error, payment = utils.setPaymentDetails('schoolfee',
465            self.student, 2002, 300)
466        self.assertEqual(payment.amount_auth, 40000.0)
467        self.assertEqual(payment.p_session, 2002)
468        self.assertEqual(payment.p_level, 300)
469        self.assertFalse(payment.p_current)
470        error, payment = utils.setPaymentDetails('schoolfee',
471            self.student, 2003, 300)
472        self.assertEqual(payment.amount_auth, 20000.0)
473        self.assertEqual(payment.p_session, 2003)
474        self.assertEqual(payment.p_level, 300)
475        self.assertFalse(payment.p_current)
476
477        # Current schoolfee payment; fresh and returning
478        # are distinguished by their state
479        IWorkflowState(self.student).setState('cleared')
480        error, payment = utils.setPaymentDetails('schoolfee',
481            self.student, None, None)
482        self.assertEqual(payment.p_level, 100)
483        self.assertEqual(payment.p_session, 2004)
484        self.assertEqual(payment.amount_auth, 40000.0)
485        self.assertEqual(payment.p_item, u'CERT1')
486        self.assertEqual(error, None)
487        self.assertTrue(payment.p_current)
488
489        # Add penalty fee ...
490        # ... for cleared
491        self.app['configuration']['2004'].penalty_ug = 99.0
492        # ... for returning
493        self.app['configuration']['2005'].penalty_ug = 88.0
494        error, payment = utils.setPaymentDetails('schoolfee',
495            self.student, None, None)
496        self.assertEqual(payment.amount_auth, 40099.0)
497
498        IWorkflowState(self.student).setState('returning')
499
500
501        #error, payment = utils.setPaymentDetails('schoolfee',
502        #    self.student, None, None)
503        #self.assertTrue(
504        #    u'You have not yet paid your current/active session.' in error)
505        ## Ok, that means we have to add paid payment ticket first.
506        #payment = createObject('waeup.StudentOnlinePayment')
507        #payment.p_category = u'schoolfee'
508        #payment.p_session = self.student.current_session
509        #payment.p_item = u'My Certificate'
510        #payment.p_id = u'anyid'
511        #payment.p_state = u'paid'
512        #self.student['payments']['anykey'] = payment
513
514
515        error, payment = utils.setPaymentDetails('schoolfee',
516            self.student, None, None)
517        self.assertEqual(payment.p_level, 200)
518        self.assertEqual(payment.p_session, 2005)
519        self.assertEqual(payment.amount_auth, 20088.0)
520        self.assertEqual(payment.p_item, u'CERT1')
521        self.assertEqual(error, None)
522
523        # Staff members pay less.
524        self.student.is_staff = True
525        error, payment = utils.setPaymentDetails('schoolfee',
526            self.student, None, None)
527        self.assertEqual(payment.p_level, 200)
528        self.assertEqual(payment.p_session, 2005)
529        self.assertEqual(payment.amount_auth, 10088.0)
530        self.assertEqual(payment.p_item, u'CERT1')
531        self.assertEqual(error, None)
532
533        # Foreigners pay more.
534        IWorkflowState(self.student).setState('cleared')
535        self.student.is_staff = False
536        self.student.nationality = u'DE'
537        self.certificate.school_fee_3 = 60000.0
538        error, payment = utils.setPaymentDetails(
539            'schoolfee', self.student, None, None)
540        self.assertEqual(payment.p_level, 100)
541        self.assertEqual(payment.p_session, 2004)
542        self.assertEqual(payment.amount_auth, 60099.0)
543        self.assertEqual(payment.p_item, u'CERT1')
544        self.assertEqual(error, None)
545        IWorkflowState(self.student).setState('returning')
546        self.student.is_staff = False
547        self.certificate.school_fee_4 = 20000.0
548        error, payment = utils.setPaymentDetails(
549            'schoolfee', self.student, None, None)
550        self.assertEqual(payment.p_level, 200)
551        self.assertEqual(payment.p_session, 2005)
552        self.assertEqual(payment.amount_auth, 20088.0)
553        self.assertEqual(payment.p_item, u'CERT1')
554        self.assertEqual(error, None)
555
556        # In Uniben students can pay school fee in all states no matter
557        # if they are ug or pg students.
558        IWorkflowState(self.student).setState('school fee paid')
559        self.student.is_staff = False
560        self.student.nationality = u'NG'
561        self.certificate.school_fee_2 = 10000.0
562        error, payment = utils.setPaymentDetails(
563            'schoolfee', self.student, None, None)
564        self.assertEqual(payment.p_level, None)
565        self.assertEqual(payment.p_session, 2005)
566        self.assertEqual(payment.amount_auth, 10088.0)
567        self.assertEqual(payment.p_item, u'CERT1')
568        self.assertEqual(error, None)
569        IWorkflowState(self.student).setState('courses registered')
570        self.certificate.study_mode = 'special_pg_pt'
571        error, payment = utils.setPaymentDetails(
572            'schoolfee', self.student, None, None)
573        self.assertEqual(payment.p_level, None)
574        self.assertEqual(payment.p_session, 2005)
575        self.assertEqual(payment.amount_auth, 10000.0)
576        self.assertEqual(payment.p_item, u'CERT1')
577        self.assertEqual(error, None)
578        IWorkflowState(self.student).setState('courses validated')
579        error, payment = utils.setPaymentDetails(
580            'schoolfee', self.student, None, None)
581        self.assertEqual(payment.p_level, None)
582        self.assertEqual(payment.p_session, 2005)
583        self.assertEqual(payment.amount_auth, 10000.0)
584        self.assertEqual(payment.p_item, u'CERT1')
585        self.assertEqual(error, None)
586
587        error, payment = utils.setPaymentDetails('clearance',
588            self.student, None, None)
589        self.assertEqual(payment.p_level, 100)
590        self.assertEqual(payment.p_session, 2004)
591        self.assertEqual(payment.amount_auth, 45000.0)
592        self.assertEqual(payment.p_item, u'CERT1')
593        self.assertEqual(error, None)
594
595        error, payment = utils.setPaymentDetails('gown',
596            self.student, None, None)
597        self.assertEqual(payment.p_level, 100)
598        self.assertEqual(payment.p_session, 2004)
599        self.assertEqual(payment.amount_auth, 150.0)
600        self.assertEqual(payment.p_item, u'')
601        self.assertEqual(error, None)
602
603        bedticket = BedTicket()
604        bedticket.booking_session = 2004
605        bedticket.bed_type = u'any bed type'
606        bedticket.bed = self.app['hostels']['hall-1']['hall-1_A_101_A']
607        bedticket.bed_coordinates = u'My bed coordinates'
608        self.student['accommodation'].addBedTicket(bedticket)
609
610        error, payment = utils.setPaymentDetails('hostel_maintenance',
611            self.student, None, None)
612        self.assertEqual(payment.p_level, 100)
613        self.assertEqual(payment.p_session, 2004)
614        self.assertEqual(payment.amount_auth, 876.0)
615        self.assertEqual(payment.p_item, u'My bed coordinates')
616        self.assertEqual(error, None)
617
618        error, payment = utils.setPaymentDetails('bed_allocation',
619            self.student, None, None)
620        self.assertEqual(payment.p_level, 100)
621        self.assertEqual(payment.p_session, 2004)
622        self.assertEqual(payment.amount_auth, 150.0)
623        self.assertEqual(payment.p_item, u'regular_male_re')
624        self.assertEqual(error, None)
625
626        #error, payment = utils.setPaymentDetails('hostel_application',
627        #    self.student, None, None)
628        #self.assertEqual(payment.p_level, 100)
629        #self.assertEqual(payment.p_session, 2004)
630        #self.assertEqual(payment.amount_auth, 1000.0)
631        #self.assertEqual(payment.p_item, u'')
632        #self.assertEqual(error, None)
633
634        #payment.approve()
635        #self.student['payments'][payment.p_id] = payment
636
637        #error, payment = utils.setPaymentDetails('tempmaint_1',
638        #    self.student, None, None)
639        #self.assertEqual(payment.p_level, 100)
640        #self.assertEqual(payment.p_session, 2004)
641        #self.assertEqual(payment.amount_auth, 8150.0)
642        #self.assertEqual(payment.p_item, u'Hall 1-4 M/F Ekehuan')
643        #self.assertEqual(error, None)
644
645        #error, payment = utils.setPaymentDetails('tempmaint_2',
646        #    self.student, None, None)
647        #self.assertEqual(payment.p_level, 100)
648        #self.assertEqual(payment.p_session, 2004)
649        #self.assertEqual(payment.amount_auth, 12650.0)
650        #self.assertEqual(payment.p_item, u'Hall 5 M/F')
651        #self.assertEqual(error, None)
652
653        #error, payment = utils.setPaymentDetails('tempmaint_3',
654        #    self.student, None, None)
655        #self.assertEqual(payment.p_level, 100)
656        #self.assertEqual(payment.p_session, 2004)
657        #self.assertEqual(payment.amount_auth, 9650.0)
658        #self.assertEqual(payment.p_item, u'Clinical Hostel')
659        #self.assertEqual(error, None)
660
661        error, payment = utils.setPaymentDetails('transfer',
662            self.student, None, None)
663        self.assertEqual(payment.p_level, 100)
664        self.assertEqual(payment.p_session, 2004)
665        self.assertEqual(payment.amount_auth, 90.0)
666        self.assertEqual(payment.p_item, u'')
667        self.assertEqual(error, None)
668        return
669
670    def test_edit_level_by_co(self):
671        # Create clearance officer
672        self.app['users'].addUser('mrclear', 'mrclearsecret')
673        self.app['users']['mrclear'].email = 'mrclear@foo.ng'
674        self.app['users']['mrclear'].title = 'Carlo Pitter'
675        # Assign local ClearanceOfficer role
676        department = self.app['faculties']['fac1']['dep1']
677        prmlocal = IPrincipalRoleManager(department)
678        prmlocal.assignRoleToPrincipal('waeup.local.ClearanceOfficer', 'mrclear')
679        notify(LocalRoleSetEvent(
680            department, 'waeup.local.ClearanceOfficer', 'mrclear', granted=True))
681        IWorkflowState(self.student).setState('clearance started')
682        # Login as clearance officer
683        self.browser.open(self.login_path)
684        self.browser.getControl(name="form.login").value = 'mrclear'
685        self.browser.getControl(name="form.password").value = 'mrclearsecret'
686        self.browser.getControl("Login").click()
687        self.assertMatches('...You logged in...', self.browser.contents)
688        # Only in state clearance requested the CO does see the
689        # 'Edit level' button ...
690        self.browser.open(self.studycourse_path)
691        self.assertFalse('Edit level' in self.browser.contents)
692        # ... and can open the edit_level view
693        self.browser.open(self.studycourse_path + '/edit_level')
694        self.assertMatches('...is locked...', self.browser.contents)
695        self.assertEqual(self.browser.url, self.studycourse_path)
696        IWorkflowInfo(self.student).fireTransition('request_clearance')
697        self.browser.open(self.studycourse_path)
698        self.assertTrue('Edit level' in self.browser.contents)
699        self.browser.getLink("Edit level").click()
700        self.browser.getControl(name="form.current_level").value = ['200']
701        self.browser.getControl("Save").click()
702        self.assertMatches('...has been saved...', self.browser.contents)
703        self.assertEqual(self.student.current_level, 200)
704
705    def test_postgraduate_student_access(self):
706        self.certificate.study_mode = 'special_pg_pt'
707        self.certificate.start_level = 700
708        self.certificate.end_level = 800
709        self.student['studycourse'].current_level = 700
710        IWorkflowState(self.student).setState('school fee paid')
711        self.browser.open(self.login_path)
712        self.browser.getControl(name="form.login").value = self.student_id
713        self.browser.getControl(name="form.password").value = 'spwd'
714        self.browser.getControl("Login").click()
715        self.assertTrue(
716            'You logged in.' in self.browser.contents)
717        # Now students can add the current study level
718        self.browser.getLink("Study Course").click()
719        self.browser.getLink("Add course list").click()
720        self.assertMatches('...Add current level 700...',
721                           self.browser.contents)
722        self.browser.getControl("Create course list now").click()
723        # A level with no course ticket was created
724        self.assertEqual(self.student['studycourse']['700'].number_of_tickets, 0)
725        self.browser.getLink("700").click()
726        self.browser.getLink("Edit course list").click()
727        self.browser.getLink("here").click()
728        self.browser.getControl(name="form.course").value = ['COURSE1']
729        # Non-final year students can't add ticket with 51 credits
730        self.app['faculties']['fac1']['dep1'].courses['COURSE1'].credits = 51
731        self.browser.getControl("Add course ticket").click()
732        self.assertMatches('...Total credits exceed 50...',
733                           self.browser.contents)
734        # Final year students can't add ticket with 52 credits ...
735        self.app['faculties']['fac1']['dep1'].courses['COURSE1'].credits = 52
736        self.student['studycourse'].certificate.end_level = 700
737        self.browser.getControl("Add course ticket").click()
738        self.assertMatches('...Total credits exceed 51...',
739                           self.browser.contents)
740        # ... but with 51 credits
741        self.app['faculties']['fac1']['dep1'].courses['COURSE1'].credits = 51
742        self.browser.getControl("Add course ticket").click()
743        self.assertMatches('...Successfully added COURSE1...',
744                           self.browser.contents)
745        # Non-final year special postgraduate students can't register
746        # course lists if their total credits are 51 and thus exceed 50 ...
747        self.student['studycourse'].certificate.end_level = 800
748        self.browser.getControl("Register course list").click()
749        self.assertMatches('...Maximum credits of 50 exceeded...',
750            self.browser.contents)
751        # ... but final year students can
752        self.student['studycourse'].certificate.end_level = 700
753        self.browser.getControl("Register course list").click()
754        self.assertMatches('...Course list has been registered...',
755            self.browser.contents)
756        self.assertEqual(self.student.state, 'courses registered')
757        return
758
759    def test_login(self):
760        # If suspended_comment is set this message will be flashed instead
761        self.student.suspended_comment = u'Aetsch baetsch!'
762        self.student.suspended = True
763        self.browser.open(self.login_path)
764        self.browser.getControl(name="form.login").value = self.student_id
765        self.browser.getControl(name="form.password").value = 'spwd'
766        self.browser.getControl("Login").click()
767        # Uniben does not display suspended_comment
768        self.assertMatches(
769            '...<div class="alert alert-warning">Your account has been deactivated.</div>...',
770            self.browser.contents)
771        self.student.suspended = False
772
773    def test_activate_deactivate_buttons(self):
774        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
775        self.browser.open(self.student_path)
776        self.browser.getLink("Deactivate").click()
777        self.assertTrue(
778            'Student account has been deactivated.' in self.browser.contents)
779        self.assertTrue(
780            'Base Data (account deactivated)' in self.browser.contents)
781        self.assertTrue(self.student.suspended)
782        self.browser.getLink("Activate").click()
783        self.assertTrue(
784            'Student account has been activated.' in self.browser.contents)
785        self.assertFalse(
786            'Base Data (account deactivated)' in self.browser.contents)
787        self.assertFalse(self.student.suspended)
788        # History messages have been added ...
789        self.browser.getLink("History").click()
790        # User is undisclosed
791        self.assertTrue(
792            'Student account deactivated<br />' in self.browser.contents)
793        self.assertTrue(
794            'Student account activated<br />' in self.browser.contents)
795        # ... and actions have been logged.
796        logfile = os.path.join(
797            self.app['datacenter'].storage, 'logs', 'students.log')
798        logcontent = open(logfile).read()
799        self.assertTrue('zope.mgr - waeup.uniben.students.browser.CustomStudentDeactivateView - '
800                        'B1000000 - account deactivated' in logcontent)
801        self.assertTrue('zope.mgr - waeup.uniben.students.browser.CustomStudentActivateView - '
802                        'B1000000 - account activated' in logcontent)
803
804    def test_manage_upload_fpm_file(self):
805        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
806        self.browser.open(self.manage_clearance_path)
807        image = open(SAMPLE_FPM, 'rb')
808        ctrl = self.browser.getControl(name='leftthumbprintupload')
809        file_ctrl = ctrl.mech_control
810        file_ctrl.add_file(image, filename='thumbprint.fpm')
811        self.browser.getControl(
812            name='upload_leftthumbprintupload').click()
813        self.assertTrue(
814            'File finger1.fpm uploaded.' in self.browser.contents)
815        self.assertTrue(
816            'http://localhost/app/students/B1000000/finger1.fpm'
817            in self.browser.contents)
818        self.browser.getControl(
819            name='delete_leftthumbprintupload').click()
820        self.assertTrue(
821            'finger1.fpm deleted'
822            in self.browser.contents)
823
824    def test_handle_clearance_by_co(self):
825        # Create clearance officer
826        self.app['users'].addUser('mrclear', 'mrclearsecret')
827        self.app['users']['mrclear'].email = 'mrclear@foo.ng'
828        self.app['users']['mrclear'].title = 'Carlo Pitter'
829        department = self.app['faculties']['fac1']['dep1']
830        prmlocal = IPrincipalRoleManager(department)
831        prmlocal.assignRoleToPrincipal('waeup.local.ClearanceOfficer', 'mrclear')
832        notify(LocalRoleSetEvent(
833            department, 'waeup.local.ClearanceOfficer', 'mrclear', granted=True))
834        IWorkflowState(self.student).setState('clearance requested')
835        # Login as clearance officer
836        self.browser.open(self.login_path)
837        self.browser.getControl(name="form.login").value = 'mrclear'
838        self.browser.getControl(name="form.password").value = 'mrclearsecret'
839        self.browser.getControl("Login").click()
840        self.assertMatches('...You logged in...', self.browser.contents)
841        # CO can view the student ...
842        self.browser.open(self.clearance_path)
843        self.assertEqual(self.browser.headers['Status'], '200 Ok')
844        self.assertEqual(self.browser.url, self.clearance_path)
845        # Clearance is disabled for this session for ug students ...
846        self.browser.open(self.clearance_path)
847        self.assertFalse('Clear student' in self.browser.contents)
848        self.browser.open(self.student_path + '/clear')
849        self.assertTrue('Clearance is disabled for this session'
850            in self.browser.contents)
851        # ... but not for
852        self.certificate.study_mode = 'pg_ft'
853        self.browser.open(self.clearance_path)
854        self.assertTrue('Clear student' in self.browser.contents)
855        self.browser.open(self.student_path + '/clear')
856        self.assertTrue('Student has been cleared' in self.browser.contents)
857
858    def test_transcripts(self):
859        studylevel = createObject(u'waeup.StudentStudyLevel')
860        studylevel.level = 100
861        studylevel.level_session = 2005
862        self.student['studycourse'].entry_mode = 'ug_ft'
863        self.student['studycourse'].addStudentStudyLevel(
864            self.certificate, studylevel)
865        studylevel2 = createObject(u'waeup.StudentStudyLevel')
866        studylevel2.level = 110
867        studylevel2.level_session = 2006
868        self.student['studycourse'].addStudentStudyLevel(
869            self.certificate, studylevel2)
870        # Add second course (COURSE has been added automatically)
871        courseticket = createObject('waeup.CourseTicket')
872        courseticket.code = 'ANYCODE'
873        courseticket.title = u'Any TITLE'
874        courseticket.credits = 13
875        courseticket.score = 66
876        courseticket.semester = 1
877        courseticket.dcode = u'ANYDCODE'
878        courseticket.fcode = u'ANYFCODE'
879        self.student['studycourse']['110']['COURSE2'] = courseticket
880        self.student['studycourse']['100']['COURSE1'].score = 55
881        self.assertEqual(self.student['studycourse']['100'].gpa_params_rectified[0], 3.0)
882        self.assertEqual(self.student['studycourse']['110'].gpa_params_rectified[0], 4.0)
883        # Get transcript data
884        td = self.student['studycourse'].getTranscriptData()
885        self.assertEqual(td[0][0]['level_key'], '100')
886        self.assertEqual(td[0][0]['sgpa'], 3.0)
887        self.assertEqual(td[0][0]['level'].level, 100)
888        self.assertEqual(td[0][0]['level'].level_session, 2005)
889        self.assertEqual(td[0][0]['tickets_1'][0].code, 'COURSE1')
890        self.assertEqual(td[0][1]['level_key'], '110')
891        self.assertEqual(td[0][1]['sgpa'], 4.0)
892        self.assertEqual(td[0][1]['level'].level, 110)
893        self.assertEqual(td[0][1]['level'].level_session, 2006)
894        self.assertEqual(td[0][1]['tickets_1'][0].code, 'ANYCODE')
895        self.assertEqual(td[1], 3.57)
896        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
897        self.browser.open(self.student_path + '/studycourse/transcript')
898        self.assertEqual(self.browser.headers['Status'], '200 Ok')
899        self.assertTrue('Transcript' in self.browser.contents)
900        # Officers can open the pdf transcript
901        self.browser.open(self.student_path + '/studycourse/transcript.pdf')
902        self.assertEqual(self.browser.headers['Status'], '200 Ok')
903        self.assertEqual(self.browser.headers['Content-Type'], 'application/pdf')
904        path = os.path.join(samples_dir(), 'transcript.pdf')
905        open(path, 'wb').write(self.browser.contents)
906        print "Sample PDF transcript.pdf written to %s" % path
907
908    def test_student_accommodation(self):
909        self.browser.open(self.login_path)
910        self.browser.getControl(name="form.login").value = self.student_id
911        self.browser.getControl(name="form.password").value = 'spwd'
912        self.browser.getControl("Login").click()
913        # Students can add online booking fee payment tickets and open the
914        # callback view (see test_manage_payments).
915        self.browser.getLink("Payments").click()
916        self.browser.getLink("Add current session payment ticket").click()
917        self.browser.getControl(name="form.p_category").value = ['bed_allocation']
918        self.browser.getControl("Create ticket").click()
919        ctrl = self.browser.getControl(name='val_id')
920        value = ctrl.options[0]
921        self.browser.getLink(value).click()
922        p_ticket = self.student['payments'].values()[0]
923        p_ticket.approveStudentPayment()
924        # The new HOS-0 pin has been created.
925        self.assertEqual(len(self.app['accesscodes']['HOS-0']),1)
926        pin = self.app['accesscodes']['HOS-0'].keys()[0]
927        ac = self.app['accesscodes']['HOS-0'][pin]
928        parts = pin.split('-')[1:]
929        sfeseries, sfenumber = parts
930        # Students can use HOS code and book a bed space with it ...
931        self.browser.open(self.acco_path)
932        # ... but not if booking period has expired ...
933        self.app['hostels'].enddate = datetime.now(pytz.utc)
934        self.browser.getLink("Book accommodation").click()
935        self.assertMatches('...Outside booking period: ...',
936                           self.browser.contents)
937        self.app['hostels'].enddate = datetime.now(pytz.utc) + timedelta(days=10)
938        # ... or student data are incomplete ...
939        self.student['studycourse'].current_level = None
940        self.browser.getLink("Book accommodation").click()
941        self.assertMatches('...Your data are incomplete...',
942            self.browser.contents)
943        self.student['studycourse'].current_level = 100
944        # ... or student has not appropriate verdict (Uniben only!)
945        self.student['studycourse'].current_verdict = 'C'
946        self.browser.getLink("Book accommodation").click()
947        self.assertMatches('...Your are not eligible...',
948            self.browser.contents)
949        self.student['studycourse'].current_verdict = 'A'
950        # ... or student is not the an allowed state ...
951        self.browser.getLink("Book accommodation").click()
952        self.assertMatches('...You are in the wrong...',
953                           self.browser.contents)
954        IWorkflowInfo(self.student).fireTransition('admit')
955        self.browser.getLink("Book accommodation").click()
956        self.assertMatches('...Activation Code:...',
957                           self.browser.contents)
958        # Student can't use faked ACs ...
959        self.browser.getControl(name="ac_series").value = u'nonsense'
960        self.browser.getControl(name="ac_number").value = sfenumber
961        self.browser.getControl("Create bed ticket").click()
962        self.assertMatches('...Activation code is invalid...',
963                           self.browser.contents)
964        # ... or ACs owned by somebody else.
965        ac.owner = u'Anybody'
966        self.browser.getControl(name="ac_series").value = sfeseries
967        self.browser.getControl(name="ac_number").value = sfenumber
968        self.browser.getControl("Create bed ticket").click()
969        self.assertMatches('...You are not the owner of this access code...',
970                           self.browser.contents)
971        # The bed remains empty.
972        bed = self.app['hostels']['hall-1']['hall-1_A_101_A']
973        self.assertTrue(bed.owner == NOT_OCCUPIED)
974        ac.owner = self.student_id
975        self.browser.getControl(name="ac_series").value = sfeseries
976        self.browser.getControl(name="ac_number").value = sfenumber
977        self.browser.getControl("Create bed ticket").click()
978        self.assertMatches('...Hall 1, Block A, Room 101, Bed A...',
979                           self.browser.contents)
980        # Bed has been allocated.
981        self.assertTrue(bed.owner == self.student_id)
982        # BedTicketAddPage is now blocked.
983        self.browser.getLink("Book accommodation").click()
984        self.assertMatches('...You already booked a bed space...',
985            self.browser.contents)
986        # The bed ticket displays the data correctly.
987        self.browser.open(self.acco_path + '/2004')
988        self.assertMatches('...Hall 1, Block A, Room 101, Bed A...',
989                           self.browser.contents)
990        self.assertMatches('...2004/2005...', self.browser.contents)
991        self.assertMatches('...regular_male_fr...', self.browser.contents)
992        self.assertMatches('...%s...' % pin, self.browser.contents)
993        # Students can open the pdf slip.
994        self.browser.open(self.browser.url + '/bed_allocation_slip.pdf')
995        self.assertEqual(self.browser.headers['Status'], '200 Ok')
996        self.assertEqual(self.browser.headers['Content-Type'], 'application/pdf')
997        # Students can't relocate themselves.
998        self.assertFalse('Relocate' in self.browser.contents)
999        relocate_path = self.acco_path + '/2004/relocate'
1000        self.assertRaises(
1001            Unauthorized, self.browser.open, relocate_path)
1002        # Students can't see the Remove button and check boxes.
1003        self.browser.open(self.acco_path)
1004        self.assertFalse('Remove' in self.browser.contents)
1005        self.assertFalse('val_id' in self.browser.contents)
1006        # Students can pay maintenance fee now.
1007        self.browser.open(self.payments_path)
1008        self.browser.open(self.payments_path + '/addop')
1009        self.browser.getControl(name="form.p_category").value = ['hostel_maintenance']
1010        self.browser.getControl("Create ticket").click()
1011        self.assertMatches('...Payment ticket created...',
1012                           self.browser.contents)
1013        ctrl = self.browser.getControl(name='val_id')
1014        value = ctrl.options[0]
1015        # Maintennace fee is taken from the hostel object.
1016        self.assertEqual(self.student['payments'][value].amount_auth, 876.0)
1017        # If the hostel's maintenance fee isn't set, the fee is
1018        # taken from the session configuration object.
1019        self.app['hostels']['hall-1'].maint_fee = 0.0
1020        self.browser.open(self.payments_path + '/addop')
1021        self.browser.getControl(name="form.p_category").value = ['hostel_maintenance']
1022        self.browser.getControl("Create ticket").click()
1023        ctrl = self.browser.getControl(name='val_id')
1024        value = ctrl.options[1]
1025        self.assertEqual(self.student['payments'][value].amount_auth, 987.0)
1026        return
Note: See TracBrowser for help on using the repository browser.