source: main/waeup.aaue/trunk/src/waeup/aaue/students/tests/test_browser.py @ 8806

Last change on this file since 8806 was 8775, checked in by Henrik Bettermann, 13 years ago

Fix flow of payments.

  • Property svn:keywords set to Id
File size: 19.2 KB
Line 
1## $Id: test_browser.py 8775 2012-06-22 16:40:39Z 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
21from StringIO import StringIO
22from hurry.workflow.interfaces import IWorkflowState
23from zope.component.hooks import setSite, clearSite
24from zope.component import getUtility, createObject
25from zope.interface import verify
26from waeup.kofa.app import University
27from waeup.kofa.students.tests.test_browser import StudentsFullSetup
28from waeup.kofa.testing import FunctionalTestCase
29from waeup.kofa.interfaces import (
30    IExtFileStore, IFileStoreNameChooser)
31from waeup.kofa.students.batching import StudentProcessor
32from waeup.kofa.students.interfaces import IStudentsUtils
33from waeup.aaue.students.batching import CustomStudentProcessor
34from waeup.aaue.testing import FunctionalLayer
35from waeup.aaue.students.interfaces import (
36    ICustomStudentStudyCourse, ICustomStudent,
37    ICustomStudentStudyLevel, ICustomCourseTicket)
38
39
40STUDENT_SAMPLE_DATA = open(
41    os.path.join(os.path.dirname(__file__), 'sample_student_data.csv'),
42    'rb').read()
43
44STUDENT_HEADER_FIELDS = STUDENT_SAMPLE_DATA.split(
45    '\n')[0].split(',')
46
47class StudentProcessorTest(FunctionalTestCase):
48    """Perform some batching tests.
49    """
50
51    layer = FunctionalLayer
52
53    def setUp(self):
54        super(StudentProcessorTest, self).setUp()
55        # Setup a sample site for each test
56        app = University()
57        self.dc_root = tempfile.mkdtemp()
58        app['datacenter'].setStoragePath(self.dc_root)
59
60        # Prepopulate the ZODB...
61        self.getRootFolder()['app'] = app
62        # we add the site immediately after creation to the
63        # ZODB. Catalogs and other local utilities are not setup
64        # before that step.
65        self.app = self.getRootFolder()['app']
66        # Set site here. Some of the following setup code might need
67        # to access grok.getSite() and should get our new app then
68        setSite(app)
69
70        self.processor_base = StudentProcessor()
71        self.processor = CustomStudentProcessor()
72        self.workdir = tempfile.mkdtemp()
73        self.csv_file = os.path.join(self.workdir, 'sample_student_data.csv')
74        open(self.csv_file, 'wb').write(STUDENT_SAMPLE_DATA)
75
76    def tearDown(self):
77        super(StudentProcessorTest, self).tearDown()
78        shutil.rmtree(self.workdir)
79        shutil.rmtree(self.dc_root)
80        clearSite()
81        return
82
83    def test_import(self):
84        # We have an empty column 'date_of_birth' in  the import file.
85        # The original processor will fail because 'date_of_birth' is required
86        # in the base package.
87        num, num_warns, fin_file, fail_file = self.processor_base.doImport(
88            self.csv_file, STUDENT_HEADER_FIELDS)
89        self.assertEqual(num_warns,3)
90        assert len(self.app['students'].keys()) == 0
91        # The customized processor does not complain since 'date_of_birth' is
92        # not required in the custom package.
93        num, num_warns, fin_file, fail_file = self.processor.doImport(
94            self.csv_file, STUDENT_HEADER_FIELDS)
95        #print open(fail_file).read()
96        self.assertEqual(num_warns,0)
97        assert len(self.app['students'].keys()) == 3
98        shutil.rmtree(os.path.dirname(fin_file))
99
100
101class StudentUITests(StudentsFullSetup):
102    """Tests for customized student class views and pages
103    """
104
105    layer = FunctionalLayer
106
107    def test_classes(self):
108        # Let's see if objects created in the customized
109        # portal really implement the customized interfaces
110        verify.verifyObject(ICustomStudent, self.student)
111        verify.verifyObject(
112            ICustomStudentStudyCourse, self.student['studycourse'])
113        studylevel = createObject(u'waeup.StudentStudyLevel')
114        verify.verifyObject(ICustomStudentStudyLevel, studylevel)
115        ticket = createObject(u'waeup.CourseTicket')
116        verify.verifyObject(ICustomCourseTicket, ticket)
117        IWorkflowState(self.student).setState('returning')
118        # Let's see if next_session_allowed works as expected
119        # A, ug_ft, 100
120        self.assertTrue(self.student['studycourse'].next_session_allowed)
121        # O, ug_ft, 100
122        self.student['studycourse'].current_verdict = 'O'
123        self.assertTrue(self.student['studycourse'].next_session_allowed)
124        # O, ug_ft, 200
125        self.student['studycourse'].current_level = 200
126        self.assertFalse(self.student['studycourse'].next_session_allowed)
127        # O, de_ft, 200
128        self.student['studycourse'].certificate.study_mode = 'de_ft'
129        self.assertTrue(self.student['studycourse'].next_session_allowed)
130        # O, ph_ft, 300
131        self.student['studycourse'].certificate.study_mode = 'ph_ft'
132        self.student['studycourse'].current_level = 300
133        self.assertTrue(self.student['studycourse'].next_session_allowed)
134        # O, ph_ft, 400
135        self.student['studycourse'].current_level = 400
136        self.assertFalse(self.student['studycourse'].next_session_allowed)
137
138        # Now we convert the certificate into a postgraduate certificate
139        IWorkflowState(self.student).setState('school fee paid')
140        self.certificate.study_mode = 'pg_ft'
141        # ... and voila next session registration is allowed
142        self.assertTrue(self.student['studycourse'].next_session_allowed)
143
144    def test_manage_access(self):
145        # Managers can access the pages of students
146        # and can perform actions
147        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
148        # The student created in the base package is an ug student
149        self.browser.open(self.student_path)
150        self.browser.getLink("Clearance Data").click()
151        self.assertEqual(self.browser.headers['Status'], '200 Ok')
152        self.assertEqual(self.browser.url, self.clearance_path)
153        self.browser.getLink("Manage").click()
154        self.assertEqual(self.browser.headers['Status'], '200 Ok')
155        self.assertEqual(self.browser.url, self.manage_clearance_path)
156        self.browser.getControl(name="form.date_of_birth").value = '09/10/1961'
157        self.browser.getControl("Save").click()
158        self.assertMatches('...Form has been saved...',
159                           self.browser.contents)
160        self.assertMatches('...First Sitting Record...',
161                           self.browser.contents)
162        # Managers can open clearance slip of ug students
163        self.browser.open(self.student_path + '/clearance.pdf')
164        self.assertEqual(self.browser.headers['Status'], '200 Ok')
165        self.assertEqual(self.browser.headers['Content-Type'], 'application/pdf')
166        # There is no pg field in the clearance form
167        self.assertFalse('Second Higher Education Record'
168            in self.browser.contents)
169        # Now we change the study mode ...
170        self.certificate.study_mode = 'pg_ft'
171        self.browser.open(self.clearance_path)
172        # ... and additional pg clearance fields appear
173        self.assertMatches('...Second Higher Education Record...',
174                           self.browser.contents)
175        # But also fields from the ug form are displayed
176        self.assertMatches('...First Sitting Record...',
177                           self.browser.contents)
178        # Managers can open clearance slip of pg students
179        self.browser.open(self.student_path + '/clearance.pdf')
180        self.assertEqual(self.browser.headers['Status'], '200 Ok')
181        self.assertEqual(self.browser.headers['Content-Type'], 'application/pdf')
182
183    def test_manage_payments(self):
184        # Add missing configuration data
185        self.app['configuration']['2004'].gown_fee = 150.0
186        self.app['configuration']['2004'].transfer_fee = 90.0
187        #self.app['configuration']['2004'].clearance_fee = 120.0
188        self.app['configuration']['2004'].booking_fee = 150.0
189        self.app['configuration']['2004'].maint_fee = 180.0
190
191        # Managers can add online payment tickets
192        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
193        self.browser.open(self.payments_path)
194        self.browser.getControl("Add online payment ticket").click()
195        self.browser.getControl("Create ticket").click()
196        self.assertMatches('...Wrong state...',
197                           self.browser.contents)
198        IWorkflowState(self.student).setState('cleared')
199        self.browser.open(self.payments_path + '/addop')
200        self.browser.getControl("Create ticket").click()
201        self.assertMatches('...Amount could not be determined...',
202                           self.browser.contents)
203
204        self.app['configuration']['2004'].school_fee_base = 6666.0
205
206        self.browser.getControl("Add online payment ticket").click()
207        self.browser.getControl("Create ticket").click()
208        self.assertMatches('...ticket created...',
209                           self.browser.contents)
210        ctrl = self.browser.getControl(name='val_id')
211        value = ctrl.options[0]
212        self.browser.getLink(value).click()
213        self.assertMatches('...Amount Authorized...',
214                           self.browser.contents)
215        # Managers can open payment slip
216        self.browser.getLink("Download payment slip").click()
217        self.assertEqual(self.browser.headers['Status'], '200 Ok')
218        self.assertEqual(self.browser.headers['Content-Type'], 'application/pdf')
219        # Set ticket paid
220        ticket = self.student['payments'].items()[0][1]
221        ticket.p_state = 'paid'
222        self.browser.open(self.payments_path + '/addop')
223        self.browser.getControl("Create ticket").click()
224        self.assertMatches('...This type of payment has already been made...',
225                           self.browser.contents)
226        self.browser.open(self.payments_path + '/addop')
227        # Also the other payments can be made
228        self.browser.getControl(name="form.p_category").value = ['gown']
229        self.browser.getControl("Create ticket").click()
230        self.assertMatches('...ticket created...',
231                           self.browser.contents)
232        self.browser.open(self.payments_path + '/addop')
233        self.browser.getControl(name="form.p_category").value = ['transfer']
234        self.browser.getControl("Create ticket").click()
235        self.assertMatches('...ticket created...',
236                           self.browser.contents)
237        self.browser.open(self.payments_path + '/addop')
238        self.browser.getControl(
239            name="form.p_category").value = ['bed_allocation']
240        self.browser.getControl("Create ticket").click()
241        self.assertMatches('...ticket created...',
242                           self.browser.contents)
243        self.browser.open(self.payments_path + '/addop')
244        self.browser.getControl(
245            name="form.p_category").value = ['hostel_maintenance']
246        self.browser.getControl("Create ticket").click()
247        self.assertMatches('...ticket created...',
248                           self.browser.contents)
249        self.browser.open(self.payments_path + '/addop')
250        self.browser.getControl(name="form.p_category").value = ['clearance']
251        self.browser.getControl("Create ticket").click()
252        self.assertMatches('...ticket created...',
253                           self.browser.contents)
254        # Remove all payments so that we can add a school fee payment again
255        keys = [i for i in self.student['payments'].keys()]
256        for payment in keys:
257            del self.student['payments'][payment]
258        self.browser.open(self.payments_path + '/addop')
259        self.browser.getControl(name="form.p_category").value = ['schoolfee_1']
260        self.browser.getControl("Create ticket").click()
261        self.assertMatches('...ticket created...',
262                           self.browser.contents)
263        # We can't add the 2nd instalment ticket because the
264        # first one has not yet been approved.
265        self.browser.open(self.payments_path + '/addop')
266        self.browser.getControl(name="form.p_category").value = ['schoolfee_2']
267        self.browser.getControl("Create ticket").click()
268        self.assertMatches('...1st school fee instalment has not yet been paid...',
269                           self.browser.contents)
270        # Ok, then we approve the first instalment ...
271        ctrl = self.browser.getControl(name='val_id')
272        p_id = ctrl.options[0]
273        self.browser.open(self.payments_path + '/' + p_id + '/approve')
274        # ... add the second instalment ...
275        self.browser.open(self.payments_path + '/addop')
276        self.browser.getControl(name="form.p_category").value = ['schoolfee_2']
277        self.browser.getControl("Create ticket").click()
278        self.assertMatches('...ticket created...',
279                           self.browser.contents)
280        # ... approve the second instalment ...
281        ctrl = self.browser.getControl(name='val_id')
282        p_id = ctrl.options[1]
283        self.browser.open(self.payments_path + '/' + p_id + '/approve')
284        # ... and finally add the 1st instalment for the next session
285        # provided that student is returning.
286        IWorkflowState(self.student).setState('returning')
287        self.browser.open(self.payments_path + '/addop')
288        self.browser.getControl(name="form.p_category").value = ['schoolfee_1']
289        self.browser.getControl("Create ticket").click()
290        self.assertMatches('...ticket created...',
291                           self.browser.contents)
292
293        # If the session configuration doesn't exist an error message will
294        # be shown. No other requirement is being checked.
295        del self.app['configuration']['2004']
296        self.browser.open(self.payments_path)
297        self.browser.getControl("Add online payment ticket").click()
298        self.browser.getControl("Create ticket").click()
299        self.assertMatches('...Session configuration object is not...',
300                           self.browser.contents)
301
302    def test_student_access(self):
303        # Students can edit clearance data
304        IWorkflowState(self.student).setState('cleared')
305        self.student.clearance_locked = False
306        self.browser.open(self.login_path)
307        self.browser.getControl(name="form.login").value = self.student_id
308        self.browser.getControl(name="form.password").value = 'spwd'
309        self.browser.getControl("Login").click()
310        # Even in state admitted students can't change the portait if
311        # application slip exists.
312        IWorkflowState(self.student).setState('admitted')
313        self.browser.open(self.student_path)
314        self.assertTrue('Change portrait' in self.browser.contents)
315        file_store = getUtility(IExtFileStore)
316        applicant_slip = 'My application slip'
317        file_id = IFileStoreNameChooser(self.student).chooseName(
318            attr="application_slip.pdf")
319        file_store.createFile(file_id, StringIO(applicant_slip))
320        self.browser.open(self.student_path)
321        self.assertFalse('Change portrait' in self.browser.contents)
322        self.browser.open(self.student_path + '/change_portrait')
323        self.assertTrue('The requested form is locked' in self.browser.contents)
324        # Student can view and edit clearance data
325        self.browser.getLink("Clearance Data").click()
326        self.browser.getLink("Edit").click()
327        self.assertTrue('Save' in self.browser.contents)
328
329    def test_get_returning_data(self):
330        # Student is in level 100, session 2004 with verdict A
331        utils = getUtility(IStudentsUtils)
332        self.assertEqual(utils.getReturningData(self.student),(2005, 200))
333        self.student['studycourse'].current_verdict = 'C'
334        self.assertEqual(utils.getReturningData(self.student),(2005, 110))
335        self.student['studycourse'].current_verdict = 'D'
336        self.assertEqual(utils.getReturningData(self.student),(2005, 100))
337        return
338
339    def test_set_payment_details(self):
340        self.app['configuration']['2004'].gown_fee = 150.0
341        self.app['configuration']['2004'].transfer_fee = 90.0
342        self.app['configuration']['2004'].booking_fee = 150.0
343        self.app['configuration']['2004'].maint_fee = 180.0
344        self.app['configuration']['2004'].clearance_fee = 1234.0
345        self.app['configuration']['2004'].school_fee_base = 6666.0
346        utils = getUtility(IStudentsUtils)
347
348        error, payment = utils.setPaymentDetails('schoolfee_1',self.student)
349        self.assertEqual(payment, None)
350        self.assertEqual(error, u'Wrong state.')
351
352        IWorkflowState(self.student).setState('cleared')
353        error, payment = utils.setPaymentDetails('schoolfee_1',self.student)
354        self.assertEqual(payment.p_level, 100)
355        self.assertEqual(payment.p_session, 2004)
356        self.assertEqual(payment.amount_auth, 6666.0)
357        self.assertEqual(payment.p_item, u'CERT1')
358        self.assertEqual(error, None)
359
360        # Add penalty fee.
361        self.app['configuration']['2004'].penalty_ug = 99.0
362        error, payment = utils.setPaymentDetails('schoolfee_1',self.student)
363        self.assertEqual(payment.amount_auth, 6765.0)
364
365        IWorkflowState(self.student).setState('returning')
366        error, payment = utils.setPaymentDetails('schoolfee_1',self.student)
367        self.assertEqual(payment.p_level, 200)
368        self.assertEqual(payment.p_session, 2005)
369        self.assertEqual(payment.amount_auth, 6765.0)
370        self.assertEqual(payment.p_item, u'CERT1')
371        self.assertEqual(error, None)
372
373        error, payment = utils.setPaymentDetails('clearance',self.student)
374        self.assertEqual(payment.p_level, 100)
375        self.assertEqual(payment.p_session, 2004)
376        self.assertEqual(payment.amount_auth, 1234.0)
377        self.assertEqual(payment.p_item, u'CERT1')
378        self.assertEqual(error, None)
379
380        error, payment = utils.setPaymentDetails('gown',self.student)
381        self.assertEqual(payment.p_level, 100)
382        self.assertEqual(payment.p_session, 2004)
383        self.assertEqual(payment.amount_auth, 150.0)
384        self.assertEqual(payment.p_item, u'')
385        self.assertEqual(error, None)
386
387        error, payment = utils.setPaymentDetails('hostel_maintenance',self.student)
388        self.assertEqual(payment.p_level, 100)
389        self.assertEqual(payment.p_session, 2004)
390        self.assertEqual(payment.amount_auth, 180.0)
391        self.assertEqual(payment.p_item, u'')
392        self.assertEqual(error, None)
393
394        error, payment = utils.setPaymentDetails('bed_allocation',self.student)
395        self.assertEqual(payment.p_level, 100)
396        self.assertEqual(payment.p_session, 2004)
397        self.assertEqual(payment.amount_auth, 150.0)
398        self.assertEqual(payment.p_item, u'')
399        self.assertEqual(error, None)
400
401        error, payment = utils.setPaymentDetails('transfer',self.student)
402        self.assertEqual(payment.p_level, 100)
403        self.assertEqual(payment.p_session, 2004)
404        self.assertEqual(payment.amount_auth, 90.0)
405        self.assertEqual(payment.p_item, u'')
406        self.assertEqual(error, None)
407        return
Note: See TracBrowser for help on using the repository browser.