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

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

It's the student's previous verdict which allows to book accommodation.

  • Property svn:keywords set to Id
File size: 52.3 KB
Line 
1## $Id: test_browser.py 13309 2015-10-12 07:43:56Z 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.assertTrue(u'Amount could not be determined.' in error)
621        self.student['studycourse'].current_session = 2004
622        self.student['studycourse'].entry_session = 2004
623        self.student['studycourse'].current_level = 100
624        error, payment = utils.setPaymentDetails('bed_allocation',
625            self.student, None, None)
626        self.assertEqual(payment.p_level, 100)
627        self.assertEqual(payment.p_session, 2004)
628        self.assertEqual(payment.amount_auth, 650.0) # plus 500 student union
629        self.assertEqual(payment.p_item, u'regular_male_fr')
630        self.assertEqual(error, None)
631
632        #error, payment = utils.setPaymentDetails('hostel_application',
633        #    self.student, None, None)
634        #self.assertEqual(payment.p_level, 100)
635        #self.assertEqual(payment.p_session, 2004)
636        #self.assertEqual(payment.amount_auth, 1000.0)
637        #self.assertEqual(payment.p_item, u'')
638        #self.assertEqual(error, None)
639
640        #payment.approve()
641        #self.student['payments'][payment.p_id] = payment
642
643        #error, payment = utils.setPaymentDetails('tempmaint_1',
644        #    self.student, None, None)
645        #self.assertEqual(payment.p_level, 100)
646        #self.assertEqual(payment.p_session, 2004)
647        #self.assertEqual(payment.amount_auth, 8150.0)
648        #self.assertEqual(payment.p_item, u'Hall 1-4 M/F Ekehuan')
649        #self.assertEqual(error, None)
650
651        #error, payment = utils.setPaymentDetails('tempmaint_2',
652        #    self.student, None, None)
653        #self.assertEqual(payment.p_level, 100)
654        #self.assertEqual(payment.p_session, 2004)
655        #self.assertEqual(payment.amount_auth, 12650.0)
656        #self.assertEqual(payment.p_item, u'Hall 5 M/F')
657        #self.assertEqual(error, None)
658
659        #error, payment = utils.setPaymentDetails('tempmaint_3',
660        #    self.student, None, None)
661        #self.assertEqual(payment.p_level, 100)
662        #self.assertEqual(payment.p_session, 2004)
663        #self.assertEqual(payment.amount_auth, 9650.0)
664        #self.assertEqual(payment.p_item, u'Clinical Hostel')
665        #self.assertEqual(error, None)
666
667        error, payment = utils.setPaymentDetails('transfer',
668            self.student, None, None)
669        self.assertEqual(payment.p_level, 100)
670        self.assertEqual(payment.p_session, 2004)
671        self.assertEqual(payment.amount_auth, 90.0)
672        self.assertEqual(payment.p_item, u'')
673        self.assertEqual(error, None)
674        return
675
676    def test_edit_level_by_co(self):
677        # Create clearance officer
678        self.app['users'].addUser('mrclear', 'mrclearsecret')
679        self.app['users']['mrclear'].email = 'mrclear@foo.ng'
680        self.app['users']['mrclear'].title = 'Carlo Pitter'
681        # Assign local ClearanceOfficer role
682        department = self.app['faculties']['fac1']['dep1']
683        prmlocal = IPrincipalRoleManager(department)
684        prmlocal.assignRoleToPrincipal('waeup.local.ClearanceOfficer', 'mrclear')
685        notify(LocalRoleSetEvent(
686            department, 'waeup.local.ClearanceOfficer', 'mrclear', granted=True))
687        IWorkflowState(self.student).setState('clearance started')
688        # Login as clearance officer
689        self.browser.open(self.login_path)
690        self.browser.getControl(name="form.login").value = 'mrclear'
691        self.browser.getControl(name="form.password").value = 'mrclearsecret'
692        self.browser.getControl("Login").click()
693        self.assertMatches('...You logged in...', self.browser.contents)
694        # Only in state clearance requested the CO does see the
695        # 'Edit level' button ...
696        self.browser.open(self.studycourse_path)
697        self.assertFalse('Edit level' in self.browser.contents)
698        # ... and can open the edit_level view
699        self.browser.open(self.studycourse_path + '/edit_level')
700        self.assertMatches('...is locked...', self.browser.contents)
701        self.assertEqual(self.browser.url, self.studycourse_path)
702        IWorkflowInfo(self.student).fireTransition('request_clearance')
703        self.browser.open(self.studycourse_path)
704        self.assertTrue('Edit level' in self.browser.contents)
705        self.browser.getLink("Edit level").click()
706        self.browser.getControl(name="form.current_level").value = ['200']
707        self.browser.getControl("Save").click()
708        self.assertMatches('...has been saved...', self.browser.contents)
709        self.assertEqual(self.student.current_level, 200)
710
711    def test_postgraduate_student_access(self):
712        self.certificate.study_mode = 'special_pg_pt'
713        self.certificate.start_level = 700
714        self.certificate.end_level = 800
715        self.student['studycourse'].current_level = 700
716        IWorkflowState(self.student).setState('school fee paid')
717        self.browser.open(self.login_path)
718        self.browser.getControl(name="form.login").value = self.student_id
719        self.browser.getControl(name="form.password").value = 'spwd'
720        self.browser.getControl("Login").click()
721        self.assertTrue(
722            'You logged in.' in self.browser.contents)
723        # Now students can add the current study level
724        self.browser.getLink("Study Course").click()
725        self.browser.getLink("Add course list").click()
726        self.assertMatches('...Add current level 700...',
727                           self.browser.contents)
728        self.browser.getControl("Create course list now").click()
729        # A level with no course ticket was created
730        self.assertEqual(self.student['studycourse']['700'].number_of_tickets, 0)
731        self.browser.getLink("700").click()
732        self.browser.getLink("Edit course list").click()
733        self.browser.getLink("here").click()
734        self.browser.getControl(name="form.course").value = ['COURSE1']
735        # Non-final year students can't add ticket with 51 credits
736        self.app['faculties']['fac1']['dep1'].courses['COURSE1'].credits = 51
737        self.browser.getControl("Add course ticket").click()
738        self.assertMatches('...Total credits exceed 50...',
739                           self.browser.contents)
740        # Final year students can't add ticket with 52 credits ...
741        self.app['faculties']['fac1']['dep1'].courses['COURSE1'].credits = 52
742        self.student['studycourse'].certificate.end_level = 700
743        self.browser.getControl("Add course ticket").click()
744        self.assertMatches('...Total credits exceed 51...',
745                           self.browser.contents)
746        # ... but with 51 credits
747        self.app['faculties']['fac1']['dep1'].courses['COURSE1'].credits = 51
748        self.browser.getControl("Add course ticket").click()
749        self.assertMatches('...Successfully added COURSE1...',
750                           self.browser.contents)
751        # Non-final year special postgraduate students can't register
752        # course lists if their total credits are 51 and thus exceed 50 ...
753        self.student['studycourse'].certificate.end_level = 800
754        self.browser.getControl("Register course list").click()
755        self.assertMatches('...Maximum credits of 50 exceeded...',
756            self.browser.contents)
757        # ... but final year students can
758        self.student['studycourse'].certificate.end_level = 700
759        self.browser.getControl("Register course list").click()
760        self.assertMatches('...Course list has been registered...',
761            self.browser.contents)
762        self.assertEqual(self.student.state, 'courses registered')
763        return
764
765    def test_login(self):
766        # If suspended_comment is set this message will be flashed instead
767        self.student.suspended_comment = u'Aetsch baetsch!'
768        self.student.suspended = True
769        self.browser.open(self.login_path)
770        self.browser.getControl(name="form.login").value = self.student_id
771        self.browser.getControl(name="form.password").value = 'spwd'
772        self.browser.getControl("Login").click()
773        # Uniben does not display suspended_comment
774        self.assertMatches(
775            '...<div class="alert alert-warning">Your account has been deactivated.</div>...',
776            self.browser.contents)
777        self.student.suspended = False
778
779    def test_activate_deactivate_buttons(self):
780        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
781        self.browser.open(self.student_path)
782        self.browser.getLink("Deactivate").click()
783        self.assertTrue(
784            'Student account has been deactivated.' in self.browser.contents)
785        self.assertTrue(
786            'Base Data (account deactivated)' in self.browser.contents)
787        self.assertTrue(self.student.suspended)
788        self.browser.getLink("Activate").click()
789        self.assertTrue(
790            'Student account has been activated.' in self.browser.contents)
791        self.assertFalse(
792            'Base Data (account deactivated)' in self.browser.contents)
793        self.assertFalse(self.student.suspended)
794        # History messages have been added ...
795        self.browser.getLink("History").click()
796        # User is undisclosed
797        self.assertTrue(
798            'Student account deactivated<br />' in self.browser.contents)
799        self.assertTrue(
800            'Student account activated<br />' in self.browser.contents)
801        # ... and actions have been logged.
802        logfile = os.path.join(
803            self.app['datacenter'].storage, 'logs', 'students.log')
804        logcontent = open(logfile).read()
805        self.assertTrue('zope.mgr - waeup.uniben.students.browser.CustomStudentDeactivateView - '
806                        'B1000000 - account deactivated' in logcontent)
807        self.assertTrue('zope.mgr - waeup.uniben.students.browser.CustomStudentActivateView - '
808                        'B1000000 - account activated' in logcontent)
809
810    def test_manage_upload_fpm_file(self):
811        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
812        self.browser.open(self.manage_clearance_path)
813        image = open(SAMPLE_FPM, 'rb')
814        ctrl = self.browser.getControl(name='leftthumbprintupload')
815        file_ctrl = ctrl.mech_control
816        file_ctrl.add_file(image, filename='thumbprint.fpm')
817        self.browser.getControl(
818            name='upload_leftthumbprintupload').click()
819        self.assertTrue(
820            'File finger1.fpm uploaded.' in self.browser.contents)
821        self.assertTrue(
822            'http://localhost/app/students/B1000000/finger1.fpm'
823            in self.browser.contents)
824        self.browser.getControl(
825            name='delete_leftthumbprintupload').click()
826        self.assertTrue(
827            'finger1.fpm deleted'
828            in self.browser.contents)
829
830    def test_handle_clearance_by_co(self):
831        # Create clearance officer
832        self.app['users'].addUser('mrclear', 'mrclearsecret')
833        self.app['users']['mrclear'].email = 'mrclear@foo.ng'
834        self.app['users']['mrclear'].title = 'Carlo Pitter'
835        department = self.app['faculties']['fac1']['dep1']
836        prmlocal = IPrincipalRoleManager(department)
837        prmlocal.assignRoleToPrincipal('waeup.local.ClearanceOfficer', 'mrclear')
838        notify(LocalRoleSetEvent(
839            department, 'waeup.local.ClearanceOfficer', 'mrclear', granted=True))
840        IWorkflowState(self.student).setState('clearance requested')
841        # Login as clearance officer
842        self.browser.open(self.login_path)
843        self.browser.getControl(name="form.login").value = 'mrclear'
844        self.browser.getControl(name="form.password").value = 'mrclearsecret'
845        self.browser.getControl("Login").click()
846        self.assertMatches('...You logged in...', self.browser.contents)
847        # CO can view the student ...
848        self.browser.open(self.clearance_path)
849        self.assertEqual(self.browser.headers['Status'], '200 Ok')
850        self.assertEqual(self.browser.url, self.clearance_path)
851        # Clearance is disabled for this session for ug students ...
852        self.browser.open(self.clearance_path)
853        self.assertFalse('Clear student' in self.browser.contents)
854        self.browser.open(self.student_path + '/clear')
855        self.assertTrue('Clearance is disabled for this session'
856            in self.browser.contents)
857        # ... but not for
858        self.certificate.study_mode = 'pg_ft'
859        self.browser.open(self.clearance_path)
860        self.assertTrue('Clear student' in self.browser.contents)
861        self.browser.open(self.student_path + '/clear')
862        self.assertTrue('Student has been cleared' in self.browser.contents)
863
864    def test_transcripts(self):
865        studylevel = createObject(u'waeup.StudentStudyLevel')
866        studylevel.level = 100
867        studylevel.level_session = 2005
868        self.student['studycourse'].entry_mode = 'ug_ft'
869        self.student['studycourse'].addStudentStudyLevel(
870            self.certificate, studylevel)
871        studylevel2 = createObject(u'waeup.StudentStudyLevel')
872        studylevel2.level = 110
873        studylevel2.level_session = 2006
874        self.student['studycourse'].addStudentStudyLevel(
875            self.certificate, studylevel2)
876        # Add second course (COURSE has been added automatically)
877        courseticket = createObject('waeup.CourseTicket')
878        courseticket.code = 'ANYCODE'
879        courseticket.title = u'Any TITLE'
880        courseticket.credits = 13
881        courseticket.score = 66
882        courseticket.semester = 1
883        courseticket.dcode = u'ANYDCODE'
884        courseticket.fcode = u'ANYFCODE'
885        self.student['studycourse']['110']['COURSE2'] = courseticket
886        self.student['studycourse']['100']['COURSE1'].score = 55
887        self.assertEqual(self.student['studycourse']['100'].gpa_params_rectified[0], 3.0)
888        self.assertEqual(self.student['studycourse']['110'].gpa_params_rectified[0], 4.0)
889        # Get transcript data
890        td = self.student['studycourse'].getTranscriptData()
891        self.assertEqual(td[0][0]['level_key'], '100')
892        self.assertEqual(td[0][0]['sgpa'], 3.0)
893        self.assertEqual(td[0][0]['level'].level, 100)
894        self.assertEqual(td[0][0]['level'].level_session, 2005)
895        self.assertEqual(td[0][0]['tickets_1'][0].code, 'COURSE1')
896        self.assertEqual(td[0][1]['level_key'], '110')
897        self.assertEqual(td[0][1]['sgpa'], 4.0)
898        self.assertEqual(td[0][1]['level'].level, 110)
899        self.assertEqual(td[0][1]['level'].level_session, 2006)
900        self.assertEqual(td[0][1]['tickets_1'][0].code, 'ANYCODE')
901        self.assertEqual(td[1], 3.57)
902        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
903        self.browser.open(self.student_path + '/studycourse/transcript')
904        self.assertEqual(self.browser.headers['Status'], '200 Ok')
905        self.assertTrue('Transcript' in self.browser.contents)
906        # Officers can open the pdf transcript
907        self.browser.open(self.student_path + '/studycourse/transcript.pdf')
908        self.assertEqual(self.browser.headers['Status'], '200 Ok')
909        self.assertEqual(self.browser.headers['Content-Type'], 'application/pdf')
910        path = os.path.join(samples_dir(), 'transcript.pdf')
911        open(path, 'wb').write(self.browser.contents)
912        print "Sample PDF transcript.pdf written to %s" % path
913
914    def test_student_accommodation(self):
915        bed = Bed()
916        bed.bed_id = u'hall-1_A_101_C'
917        bed.bed_number = 3
918        bed.owner = NOT_OCCUPIED
919        bed.bed_type = u'regular_male_fi'
920        self.app['hostels']['hall-1'].addBed(bed)
921        self.browser.open(self.login_path)
922        self.browser.getControl(name="form.login").value = self.student_id
923        self.browser.getControl(name="form.password").value = 'spwd'
924        self.browser.getControl("Login").click()
925        # Students can add online booking fee payment tickets and open the
926        # callback view (see test_manage_payments).
927        self.browser.getLink("Payments").click()
928        self.browser.getLink("Add current session payment ticket").click()
929        self.browser.getControl(name="form.p_category").value = ['bed_allocation']
930        self.browser.getControl("Create ticket").click()
931        ctrl = self.browser.getControl(name='val_id')
932        value = ctrl.options[0]
933        self.browser.getLink(value).click()
934        p_ticket = self.student['payments'].values()[0]
935        p_ticket.approveStudentPayment()
936        # The new HOS-0 pin has been created.
937        self.assertEqual(len(self.app['accesscodes']['HOS-0']),1)
938        pin = self.app['accesscodes']['HOS-0'].keys()[0]
939        ac = self.app['accesscodes']['HOS-0'][pin]
940        parts = pin.split('-')[1:]
941        sfeseries, sfenumber = parts
942        # Students can use HOS code and book a bed space with it ...
943        self.browser.open(self.acco_path)
944        # ... but not if booking period has expired ...
945        self.app['hostels'].enddate = datetime.now(pytz.utc)
946        self.browser.getLink("Book accommodation").click()
947        self.assertMatches('...Outside booking period: ...',
948                           self.browser.contents)
949        self.app['hostels'].enddate = datetime.now(pytz.utc) + timedelta(days=10)
950        # ... or student data are incomplete ...
951        self.student['studycourse'].current_level = None
952        self.browser.getLink("Book accommodation").click()
953        self.assertMatches('...Your data are incomplete...',
954            self.browser.contents)
955        self.student['studycourse'].current_level = 200
956        # ... or student is not the an allowed state ...
957        self.browser.getLink("Book accommodation").click()
958        self.assertMatches('...You are in the wrong...',
959                           self.browser.contents)
960        self.app['hostels'].accommodation_states = ['admitted', 'school fee paid']
961        IWorkflowState(self.student).setState('school fee paid')
962        # ... or student has not appropriate verdict (Uniben only!)
963        self.student['studycourse'].entry_session = 2000 # non-fresh
964        self.student['studycourse'].current_level = 500 # final-year
965        self.student['studycourse'].current_verdict = 'C'
966        self.browser.getLink("Book accommodation").click()
967        self.assertMatches('...Your are not eligible...',
968            self.browser.contents)
969        self.student['studycourse'].previous_verdict = 'A'
970        self.browser.getLink("Book accommodation").click()
971        self.assertMatches('...Activation Code:...',
972                           self.browser.contents)
973        # Student can't use faked ACs ...
974        self.browser.getControl(name="ac_series").value = u'nonsense'
975        self.browser.getControl(name="ac_number").value = sfenumber
976        self.browser.getControl("Create bed ticket").click()
977        self.assertMatches('...Activation code is invalid...',
978                           self.browser.contents)
979        # ... or ACs owned by somebody else.
980        ac.owner = u'Anybody'
981        self.browser.getControl(name="ac_series").value = sfeseries
982        self.browser.getControl(name="ac_number").value = sfenumber
983        self.browser.getControl("Create bed ticket").click()
984        self.assertMatches('...You are not the owner of this access code...',
985                           self.browser.contents)
986        # The bed remains empty.
987        bed = self.app['hostels']['hall-1']['hall-1_A_101_C']
988        self.assertTrue(bed.owner == NOT_OCCUPIED)
989        ac.owner = self.student_id
990        self.browser.getControl(name="ac_series").value = sfeseries
991        self.browser.getControl(name="ac_number").value = sfenumber
992        self.browser.getControl("Create bed ticket").click()
993        self.assertMatches('...Hall 1, Block/Unit A, Room 101, Bed C...',
994                           self.browser.contents)
995        # Bed has been allocated.
996        self.assertTrue(bed.owner == self.student_id)
997        # BedTicketAddPage is now blocked.
998        self.browser.getLink("Book accommodation").click()
999        self.assertMatches('...You already booked a bed space...',
1000            self.browser.contents)
1001        # The bed ticket displays the data correctly.
1002        self.browser.open(self.acco_path + '/2004')
1003        self.assertMatches('...Hall 1, Block/Unit A, Room 101, Bed C...',
1004                           self.browser.contents)
1005        self.assertMatches('...2004/2005...', self.browser.contents)
1006        self.assertMatches('...regular_male_fi...', self.browser.contents)
1007        self.assertMatches('...%s...' % pin, self.browser.contents)
1008        # Students can open the pdf slip.
1009        self.browser.open(self.browser.url + '/bed_allocation_slip.pdf')
1010        self.assertEqual(self.browser.headers['Status'], '200 Ok')
1011        self.assertEqual(self.browser.headers['Content-Type'], 'application/pdf')
1012        # Students can't relocate themselves.
1013        self.assertFalse('Relocate' in self.browser.contents)
1014        relocate_path = self.acco_path + '/2004/relocate'
1015        self.assertRaises(
1016            Unauthorized, self.browser.open, relocate_path)
1017        # Students can't see the Remove button and check boxes.
1018        self.browser.open(self.acco_path)
1019        self.assertFalse('Remove' in self.browser.contents)
1020        self.assertFalse('val_id' in self.browser.contents)
1021        # Students can pay maintenance fee now.
1022        self.browser.open(self.payments_path)
1023        self.browser.open(self.payments_path + '/addop')
1024        self.browser.getControl(name="form.p_category").value = ['hostel_maintenance']
1025        self.browser.getControl("Create ticket").click()
1026        self.assertMatches('...Payment ticket created...',
1027                           self.browser.contents)
1028        ctrl = self.browser.getControl(name='val_id')
1029        value = ctrl.options[0]
1030        # Maintennace fee is taken from the hostel object.
1031        self.assertEqual(self.student['payments'][value].amount_auth, 876.0)
1032        # If the hostel's maintenance fee isn't set, the fee is
1033        # taken from the session configuration object.
1034        self.app['hostels']['hall-1'].maint_fee = 0.0
1035        self.browser.open(self.payments_path + '/addop')
1036        self.browser.getControl(name="form.p_category").value = ['hostel_maintenance']
1037        self.browser.getControl("Create ticket").click()
1038        ctrl = self.browser.getControl(name='val_id')
1039        value = ctrl.options[1]
1040        self.assertEqual(self.student['payments'][value].amount_auth, 987.0)
1041        return
Note: See TracBrowser for help on using the repository browser.