source: main/waeup.aaue/trunk/src/waeup/aaue/students/utils.py @ 14292

Last change on this file since 14292 was 14248, checked in by Henrik Bettermann, 8 years ago

Replace course_registration_allowed by course_registration_forbidden.

Add further requirement for registering courses.

Shorten setPaymentDetails.

Add further payment categories.

  • Property svn:keywords set to Id
File size: 18.8 KB
Line 
1## $Id: utils.py 14248 2016-11-02 11:34:48Z 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 grok
19from time import time
20from zope.component import createObject
21from waeup.kofa.interfaces import (
22    ADMITTED, CLEARANCE, REQUESTED, CLEARED, RETURNING, PAID,
23    academic_sessions_vocab)
24from kofacustom.nigeria.students.utils import NigeriaStudentsUtils
25from waeup.kofa.accesscodes import create_accesscode
26from waeup.kofa.students.utils import trans
27from waeup.aaue.interswitch.browser import gateway_net_amt, GATEWAY_AMT
28from waeup.aaue.interfaces import MessageFactory as _
29
30class CustomStudentsUtils(NigeriaStudentsUtils):
31    """A collection of customized methods.
32
33    """
34
35    PORTRAIT_CHANGE_STATES = (ADMITTED,)
36
37    gpa_boundaries = ((1, 'FRNS'),
38                      (1.5, 'Pass'),
39                      (2.4, '3rd Class Honours'),
40                      (3.5, '2nd Class Honours Lower Division'),
41                      (4.5, '2nd Class Honours Upper Division'),
42                      (5, '1st Class Honours'))
43
44    def getDegreeClassNumber(self, level_obj):
45        certificate = getattr(level_obj.__parent__,'certificate',None)
46        end_level = getattr(certificate, 'end_level', None)
47        if end_level and level_obj.student.current_level >= end_level:
48            failed_courses = level_obj.passed_params[4]
49            if '_m' in failed_courses:
50                return 0
51        # use gpa_boundaries above
52        return self.getClassFromCGPA(level_obj.cumulative_params[0])[0]
53
54    def increaseMatricInteger(self, student):
55        """Increase counter for matric numbers.
56        This counter can be a centrally stored attribute or an attribute of
57        faculties, departments or certificates. In the base package the counter
58        is as an attribute of the site configuration container.
59        """
60        if student.current_mode in ('ug_pt', 'de_pt'):
61            grok.getSite()['configuration'].next_matric_integer += 1
62            return
63        elif student.is_postgrad:
64            grok.getSite()['configuration'].next_matric_integer_3 += 1
65            return
66        elif student.current_mode in ('dp_ft',):
67            grok.getSite()['configuration'].next_matric_integer_4 += 1
68            return
69        grok.getSite()['configuration'].next_matric_integer_2 += 1
70        return
71
72    def _concessionalPaymentMade(self, student):
73        if len(student['payments']):
74            for ticket in student['payments'].values():
75                if ticket.p_state == 'paid' and \
76                    ticket.p_category == 'concessional':
77                    return True
78        return False
79
80    def constructMatricNumber(self, student):
81        faccode = student.faccode
82        depcode = student.depcode
83        certcode = student.certcode
84        degree = getattr(
85            getattr(student.get('studycourse', None), 'certificate', None),
86                'degree', None)
87        year = unicode(student.entry_session)[2:]
88        if not student.state in (PAID, ) or not student.is_fresh or \
89            student.current_mode == 'found':
90            return _('Matriculation number cannot be set.'), None
91        #if student.current_mode not in ('mug_ft', 'mde_ft') and \
92        #    not self._concessionalPaymentMade(student):
93        #    return _('Matriculation number cannot be set.'), None
94        if student.is_postgrad:
95            next_integer = grok.getSite()['configuration'].next_matric_integer_3
96            if not degree or next_integer == 0:
97                return _('Matriculation number cannot be set.'), None
98            if student.faccode in ('IOE'):
99                return None, "AAU/SPS/%s/%s/%s/%05d" % (
100                    faccode, year, degree, next_integer)
101            return None, "AAU/SPS/%s/%s/%s/%s/%05d" % (
102                faccode, depcode, year, degree, next_integer)
103        if student.current_mode in ('ug_pt', 'de_pt'):
104            next_integer = grok.getSite()['configuration'].next_matric_integer
105            if next_integer == 0:
106                return _('Matriculation number cannot be set.'), None
107            return None, "PTP/%s/%s/%s/%05d" % (
108                faccode, depcode, year, next_integer)
109        if student.current_mode in ('dp_ft',):
110            next_integer = grok.getSite()['configuration'].next_matric_integer_4
111            if next_integer == 0:
112                return _('Matriculation number cannot be set.'), None
113            return None, "IOE/DIP/%s/%05d" % (year, next_integer)
114        next_integer = grok.getSite()['configuration'].next_matric_integer_2
115        if next_integer == 0:
116            return _('Matriculation number cannot be set.'), None
117        if student.faccode in ('FBM', 'FCS'):
118            return None, "CMS/%s/%s/%s/%05d" % (
119                faccode, depcode, year, next_integer)
120        return None, "%s/%s/%s/%05d" % (faccode, depcode, year, next_integer)
121
122    def getReturningData(self, student):
123        """ This method defines what happens after school fee payment
124        of returning students depending on the student's senate verdict.
125        """
126        prev_level = student['studycourse'].current_level
127        cur_verdict = student['studycourse'].current_verdict
128        if cur_verdict in ('A','B','C', 'L','M','N','Z',):
129            # Successful student
130            new_level = divmod(int(prev_level),100)[0]*100 + 100
131        #elif cur_verdict == 'C':
132        #    # Student on probation
133        #    new_level = int(prev_level) + 10
134        else:
135            # Student is somehow in an undefined state.
136            # Level has to be set manually.
137            new_level = prev_level
138        new_session = student['studycourse'].current_session + 1
139        return new_session, new_level
140
141    def _isPaymentDisabled(self, p_session, category, student):
142        academic_session = self._getSessionConfiguration(p_session)
143        if category.startswith('schoolfee'):
144            if 'sf_all' in academic_session.payment_disabled:
145                return True
146            if 'sf_pg' in academic_session.payment_disabled and \
147                student.is_postgrad:
148                return True
149            if 'sf_pt' in academic_session.payment_disabled and \
150                student.current_mode.endswith('_pt'):
151                return True
152            if 'sf_found' in academic_session.payment_disabled and \
153                student.current_mode == 'found':
154                return True
155        if category.startswith('clearance') and \
156            'cl_regular' in academic_session.payment_disabled and \
157            student.current_mode in ('ug_ft', 'de_ft', 'mug_ft', 'mde_ft'):
158            return True
159        if category == 'hostel_maintenance' and \
160            'maint_all' in academic_session.payment_disabled:
161            return True
162        return False
163
164    def setPaymentDetails(self, category, student,
165            previous_session=None, previous_level=None):
166        """Create Payment object and set the payment data of a student for
167        the payment category specified.
168
169        """
170        details = {}
171        p_item = u''
172        amount = 0.0
173        error = u''
174        if previous_session:
175            return _('Previous session payment not yet implemented.'), None
176        p_session = student['studycourse'].current_session
177        p_level = student['studycourse'].current_level
178        p_current = True
179        academic_session = self._getSessionConfiguration(p_session)
180        if academic_session == None:
181            return _(u'Session configuration object is not available.'), None
182        # Determine fee.
183        if category == 'transfer':
184            amount = academic_session.transfer_fee
185        elif category == 'transcript':
186            amount = academic_session.transcript_fee
187        elif category == 'bed_allocation':
188            amount = academic_session.booking_fee
189        elif category == 'hostel_maintenance':
190            amount = 0.0
191            bedticket = student['accommodation'].get(
192                str(student.current_session), None)
193            if bedticket is not None and bedticket.bed is not None:
194                p_item = bedticket.display_coordinates
195                if bedticket.bed.__parent__.maint_fee > 0:
196                    amount = bedticket.bed.__parent__.maint_fee
197                else:
198                    # fallback
199                    amount = academic_session.maint_fee
200            else:
201                return _(u'No bed allocated.'), None
202        elif student.current_mode == 'found' and category not in (
203            'schoolfee', 'clearance', 'late_registration'):
204            return _('Not allowed.'), None
205        elif category.startswith('clearance'):
206            if student.state not in (ADMITTED, CLEARANCE, REQUESTED, CLEARED):
207                return _(u'Acceptance Fee payments not allowed.'), None
208            if student.current_mode in (
209                'ug_ft', 'ug_pt', 'de_ft', 'de_pt',
210                'transfer', 'mug_ft', 'mde_ft') \
211                and category != 'clearance_incl':
212                    return _("Additional fees must be included."), None
213            if student.faccode == 'FP':
214                amount = academic_session.clearance_fee_fp
215            elif student.current_mode.endswith('_pt'):
216                if student.is_postgrad:
217                    amount = academic_session.clearance_fee_pg_pt
218                else:
219                    amount = academic_session.clearance_fee_ug_pt
220            elif student.faccode == 'FCS':
221                # Students in clinical medical sciences pay the medical
222                # acceptance fee
223                amount = academic_session.clearance_fee_med
224            elif student.is_postgrad:  # and not part-time
225                if category != 'clearance':
226                    return _("No additional fees required."), None
227                amount = academic_session.clearance_fee_pg
228            else:
229                amount = academic_session.clearance_fee
230            p_item = student['studycourse'].certificate.code
231            if amount in (0.0, None):
232                return _(u'Amount could not be determined.'), None
233            # Add Matric Gown Fee and Lapel Fee
234            if category == 'clearance_incl':
235                amount += gateway_net_amt(academic_session.matric_gown_fee) + \
236                    gateway_net_amt(academic_session.lapel_fee)
237        elif category == 'late_registration':
238            if student.is_postgrad:
239                amount = academic_session.late_pg_registration_fee
240            else:
241                amount = academic_session.late_registration_fee
242        elif category.startswith('schoolfee'):
243            try:
244                certificate = student['studycourse'].certificate
245                p_item = certificate.code
246            except (AttributeError, TypeError):
247                return _('Study course data are incomplete.'), None
248            if student.is_postgrad and category != 'schoolfee':
249                return _("No additional fees required."), None
250            if student.current_mode in (
251                'ug_ft', 'ug_pt', 'de_ft', 'de_pt',
252                'transfer', 'mug_ft', 'mde_ft') \
253                and not category in (
254                'schoolfee_incl', 'schoolfee_1', 'schoolfee_2'):
255                    return _("You must choose a payment which includes "
256                             "additional fees."), None
257            if category in ('schoolfee_1', 'schoolfee_2'):
258                if student.current_mode == 'ug_pt':
259                    return _("Part-time students are not allowed "
260                             "to pay by instalments."), None
261                if student.entry_session < 2015:
262                    return _("You are not allowed "
263                             "to pay by instalments."), None
264            if student.state == CLEARED:
265                amount = getattr(certificate, 'school_fee_1', 0.0)
266                # Cut school fee by 50%
267                if category == 'schoolfee_1' and amount:
268                    amount = gateway_net_amt(amount) / 2 + GATEWAY_AMT
269            elif student.is_fresh and category == 'schoolfee_2':
270                amount = getattr(certificate, 'school_fee_1', 0.0)
271                # Cut school fee by 50%
272                if amount:
273                    amount = gateway_net_amt(amount) / 2 + GATEWAY_AMT
274            elif student.state == RETURNING:
275                if not student.father_name:
276                    return _("Personal data form is not properly filled."), None
277                # In case of returning school fee payment the payment session
278                # and level contain the values of the session the student
279                # has paid for.
280                p_session, p_level = self.getReturningData(student)
281                try:
282                    academic_session = grok.getSite()[
283                        'configuration'][str(p_session)]
284                except KeyError:
285                    return _(u'Session configuration object is not available.'), None
286                if student.entry_session >= 2015:
287                    amount = getattr(certificate, 'school_fee_2', 0.0)
288                    # Cut school fee by 50%
289                    if category in ('schoolfee_1', 'schoolfee_2') and amount:
290                        amount = gateway_net_amt(amount) / 2 + GATEWAY_AMT
291                else:
292                    amount = getattr(certificate, 'school_fee_3', 0.0)
293            elif category == 'schoolfee_2':
294                amount = getattr(certificate, 'school_fee_2', 0.0)
295                # Cut school fee by 50%
296                if amount:
297                    amount = gateway_net_amt(amount) / 2 + GATEWAY_AMT
298            else:
299                return _('Wrong state.'), None
300            if amount in (0.0, None):
301                return _(u'Amount could not be determined.'), None
302            # Add Student Union Fee , Student Id Card Fee and Welfare Assurance
303            if category in ('schoolfee_incl', 'schoolfee_1'):
304                amount += gateway_net_amt(academic_session.welfare_fee) + \
305                    gateway_net_amt(academic_session.union_fee)
306                if student.entry_session == 2016 \
307                    and student.entry_mode == 'ug_ft' \
308                    and student.state == CLEARED:
309                    amount += gateway_net_amt(academic_session.id_card_fee)
310            # Add non-indigenous fee and session specific penalty fees
311            if student.is_postgrad:
312                amount += academic_session.penalty_pg
313                if student.lga and not student.lga.startswith('edo'):
314                    amount += 20000.0
315            else:
316                amount += academic_session.penalty_ug
317        elif not student.is_postgrad:
318            fee_name = category + '_fee'
319            amount = getattr(academic_session, fee_name, 0.0)
320        if amount in (0.0, None):
321            return _(u'Amount could not be determined.'), None
322        # Create ticket.
323        for key in student['payments'].keys():
324            ticket = student['payments'][key]
325            if ticket.p_state == 'paid' and\
326               ticket.p_category == category and \
327               ticket.p_item == p_item and \
328               ticket.p_session == p_session:
329                  return _('This type of payment has already been made.'), None
330            # Additional condition in AAUE
331            if category in ('schoolfee', 'schoolfee_incl', 'schoolfee_1'):
332                if ticket.p_state == 'paid' and \
333                   ticket.p_category in ('schoolfee',
334                                         'schoolfee_incl',
335                                         'schoolfee_1') and \
336                   ticket.p_item == p_item and \
337                   ticket.p_session == p_session:
338                      return _(
339                          'Another school fee payment for this '
340                          'session has already been made.'), None
341
342        if self._isPaymentDisabled(p_session, category, student):
343            return _('This category of payments has been disabled.'), None
344        payment = createObject(u'waeup.StudentOnlinePayment')
345        timestamp = ("%d" % int(time()*10000))[1:]
346        payment.p_id = "p%s" % timestamp
347        payment.p_category = category
348        payment.p_item = p_item
349        payment.p_session = p_session
350        payment.p_level = p_level
351        payment.p_current = p_current
352        payment.amount_auth = amount
353        return None, payment
354
355    def _admissionText(self, student, portal_language):
356        inst_name = grok.getSite()['configuration'].name
357        entry_session = student['studycourse'].entry_session
358        entry_session = academic_sessions_vocab.getTerm(entry_session).title
359        text = trans(_(
360            'This is to inform you that you have been offered provisional'
361            ' admission into ${a} for the ${b} academic session as follows:',
362            mapping = {'a': inst_name, 'b': entry_session}),
363            portal_language)
364        return text
365
366    def maxCredits(self, studylevel):
367        """Return maximum credits.
368
369        """
370        return 48
371
372    def getBedCoordinates(self, bedticket):
373        """Return descriptive bed coordinates.
374        This method can be used to customize the `display_coordinates`
375        property method in order to  display a
376        customary description of the bed space.
377        """
378        bc = bedticket.bed_coordinates.split(',')
379        if len(bc) == 4:
380            return bc[0]
381        return bedticket.bed_coordinates
382
383    def getAccommodationDetails(self, student):
384        """Determine the accommodation data of a student.
385        """
386        d = {}
387        d['error'] = u''
388        hostels = grok.getSite()['hostels']
389        d['booking_session'] = hostels.accommodation_session
390        d['allowed_states'] = hostels.accommodation_states
391        d['startdate'] = hostels.startdate
392        d['enddate'] = hostels.enddate
393        d['expired'] = hostels.expired
394        # Determine bed type
395        bt = 'all'
396        if student.sex == 'f':
397            sex = 'female'
398        else:
399            sex = 'male'
400        special_handling = 'regular'
401        d['bt'] = u'%s_%s_%s' % (special_handling,sex,bt)
402        return d
403
404    def checkAccommodationRequirements(self, student, acc_details):
405        msg = super(CustomStudentsUtils, self).checkAccommodationRequirements(
406            student, acc_details)
407        if msg:
408            return msg
409        if student.current_mode not in ('ug_ft', 'de_ft', 'mug_ft', 'mde_ft'):
410            return _('You are not eligible to book accommodation.')
411        return
412
413    # AAUE prefix
414    STUDENT_ID_PREFIX = u'E'
Note: See TracBrowser for help on using the repository browser.