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
RevLine 
[7419]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##
[7151]18import grok
[8600]19from time import time
20from zope.component import createObject
[10922]21from waeup.kofa.interfaces import (
[13594]22    ADMITTED, CLEARANCE, REQUESTED, CLEARED, RETURNING, PAID,
23    academic_sessions_vocab)
[8823]24from kofacustom.nigeria.students.utils import NigeriaStudentsUtils
[8247]25from waeup.kofa.accesscodes import create_accesscode
[10922]26from waeup.kofa.students.utils import trans
[13720]27from waeup.aaue.interswitch.browser import gateway_net_amt, GATEWAY_AMT
[8444]28from waeup.aaue.interfaces import MessageFactory as _
[6902]29
[8823]30class CustomStudentsUtils(NigeriaStudentsUtils):
[7151]31    """A collection of customized methods.
32
33    """
34
[14242]35    PORTRAIT_CHANGE_STATES = (ADMITTED,)
[13348]36
[10641]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
[14160]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
[13359]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
[13609]63        elif student.is_postgrad:
64            grok.getSite()['configuration'].next_matric_integer_3 += 1
65            return
[13793]66        elif student.current_mode in ('dp_ft',):
67            grok.getSite()['configuration'].next_matric_integer_4 += 1
68            return
[13359]69        grok.getSite()['configuration'].next_matric_integer_2 += 1
70        return
71
[13749]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
[11596]80    def constructMatricNumber(self, student):
[11593]81        faccode = student.faccode
82        depcode = student.depcode
[13609]83        certcode = student.certcode
[13664]84        degree = getattr(
85            getattr(student.get('studycourse', None), 'certificate', None),
86                'degree', None)
[11593]87        year = unicode(student.entry_session)[2:]
[13359]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
[13755]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
[13571]94        if student.is_postgrad:
[13609]95            next_integer = grok.getSite()['configuration'].next_matric_integer_3
[13664]96            if not degree or next_integer == 0:
[13609]97                return _('Matriculation number cannot be set.'), None
[13846]98            if student.faccode in ('IOE'):
99                return None, "AAU/SPS/%s/%s/%s/%05d" % (
100                    faccode, year, degree, next_integer)
[13609]101            return None, "AAU/SPS/%s/%s/%s/%s/%05d" % (
[13664]102                faccode, depcode, year, degree, next_integer)
[13359]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
[12975]107            return None, "PTP/%s/%s/%s/%05d" % (
108                faccode, depcode, year, next_integer)
[13793]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)
[13359]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)
[12975]121
[8270]122    def getReturningData(self, student):
123        """ This method defines what happens after school fee payment
[8319]124        of returning students depending on the student's senate verdict.
[8270]125        """
[8319]126        prev_level = student['studycourse'].current_level
127        cur_verdict = student['studycourse'].current_verdict
[14089]128        if cur_verdict in ('A','B','C', 'L','M','N','Z',):
[8319]129            # Successful student
130            new_level = divmod(int(prev_level),100)[0]*100 + 100
[14089]131        #elif cur_verdict == 'C':
132        #    # Student on probation
133        #    new_level = int(prev_level) + 10
[8319]134        else:
135            # Student is somehow in an undefined state.
136            # Level has to be set manually.
137            new_level = prev_level
[8270]138        new_session = student['studycourse'].current_session + 1
139        return new_session, new_level
140
[13454]141    def _isPaymentDisabled(self, p_session, category, student):
142        academic_session = self._getSessionConfiguration(p_session)
[14246]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
[13794]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
[13454]159        if category == 'hostel_maintenance' and \
160            'maint_all' in academic_session.payment_disabled:
161            return True
162        return False
163
[9154]164    def setPaymentDetails(self, category, student,
165            previous_session=None, previous_level=None):
[8600]166        """Create Payment object and set the payment data of a student for
167        the payment category specified.
168
169        """
[8306]170        details = {}
[8600]171        p_item = u''
172        amount = 0.0
173        error = u''
[9154]174        if previous_session:
175            return _('Previous session payment not yet implemented.'), None
[8600]176        p_session = student['studycourse'].current_session
177        p_level = student['studycourse'].current_level
[9154]178        p_current = True
[9527]179        academic_session = self._getSessionConfiguration(p_session)
180        if academic_session == None:
[8600]181            return _(u'Session configuration object is not available.'), None
[8677]182        # Determine fee.
[7151]183        if category == 'transfer':
[8600]184            amount = academic_session.transfer_fee
[10467]185        elif category == 'transcript':
186            amount = academic_session.transcript_fee
[7151]187        elif category == 'bed_allocation':
[8600]188            amount = academic_session.booking_fee
[7151]189        elif category == 'hostel_maintenance':
[13418]190            amount = 0.0
191            bedticket = student['accommodation'].get(
192                str(student.current_session), None)
[13502]193            if bedticket is not None and bedticket.bed is not None:
[13474]194                p_item = bedticket.display_coordinates
[13418]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:
[13506]201                return _(u'No bed allocated.'), None
[13636]202        elif student.current_mode == 'found' and category not in (
203            'schoolfee', 'clearance', 'late_registration'):
204            return _('Not allowed.'), None
[13400]205        elif category.startswith('clearance'):
[13594]206            if student.state not in (ADMITTED, CLEARANCE, REQUESTED, CLEARED):
207                return _(u'Acceptance Fee payments not allowed.'), None
[13855]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
[11653]213            if student.faccode == 'FP':
214                amount = academic_session.clearance_fee_fp
[13377]215            elif student.current_mode.endswith('_pt'):
[13678]216                if student.is_postgrad:
217                    amount = academic_session.clearance_fee_pg_pt
218                else:
219                    amount = academic_session.clearance_fee_ug_pt
[13466]220            elif student.faccode == 'FCS':
221                # Students in clinical medical sciences pay the medical
222                # acceptance fee
[13377]223                amount = academic_session.clearance_fee_med
[13678]224            elif student.is_postgrad:  # and not part-time
[13853]225                if category != 'clearance':
[13854]226                    return _("No additional fees required."), None
[13526]227                amount = academic_session.clearance_fee_pg
[11653]228            else:
229                amount = academic_session.clearance_fee
[8753]230            p_item = student['studycourse'].certificate.code
[13689]231            if amount in (0.0, None):
232                return _(u'Amount could not be determined.'), None
[14239]233            # Add Matric Gown Fee and Lapel Fee
[13689]234            if category == 'clearance_incl':
[13414]235                amount += gateway_net_amt(academic_session.matric_gown_fee) + \
236                    gateway_net_amt(academic_session.lapel_fee)
[11004]237        elif category == 'late_registration':
[14117]238            if student.is_postgrad:
239                amount = academic_session.late_pg_registration_fee
240            else:
241                amount = academic_session.late_registration_fee
[13400]242        elif category.startswith('schoolfee'):
[8600]243            try:
[8753]244                certificate = student['studycourse'].certificate
245                p_item = certificate.code
[8600]246            except (AttributeError, TypeError):
247                return _('Study course data are incomplete.'), None
[13853]248            if student.is_postgrad and category != 'schoolfee':
[13854]249                return _("No additional fees required."), None
[13855]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'):
[14244]255                    return _("You must choose a payment which includes "
[13855]256                             "additional fees."), None
[13780]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
[14241]261                if student.entry_session < 2015:
262                    return _("You are not allowed "
263                             "to pay by instalments."), None
264            if student.state == CLEARED:
[14229]265                amount = getattr(certificate, 'school_fee_1', 0.0)
[13512]266                # Cut school fee by 50%
[14241]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
[13374]274            elif student.state == RETURNING:
[13482]275                if not student.father_name:
276                    return _("Personal data form is not properly filled."), None
[13374]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)
[8961]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
[14241]286                if student.entry_session >= 2015:
[14229]287                    amount = getattr(certificate, 'school_fee_2', 0.0)
[14241]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
[13374]291                else:
[14229]292                    amount = getattr(certificate, 'school_fee_3', 0.0)
[14241]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
[8600]298            else:
[8753]299                return _('Wrong state.'), None
[13417]300            if amount in (0.0, None):
301                return _(u'Amount could not be determined.'), None
[14244]302            # Add Student Union Fee , Student Id Card Fee and Welfare Assurance
[13512]303            if category in ('schoolfee_incl', 'schoolfee_1'):
[13414]304                amount += gateway_net_amt(academic_session.welfare_fee) + \
305                    gateway_net_amt(academic_session.union_fee)
[14244]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)
[13534]310            # Add non-indigenous fee and session specific penalty fees
311            if student.is_postgrad:
312                amount += academic_session.penalty_pg
[14246]313                if student.lga and not student.lga.startswith('edo'):
[13534]314                    amount += 20000.0
315            else:
316                amount += academic_session.penalty_ug
[14248]317        elif not student.is_postgrad:
318            fee_name = category + '_fee'
319            amount = getattr(academic_session, fee_name, 0.0)
[8600]320        if amount in (0.0, None):
321            return _(u'Amount could not be determined.'), None
[8677]322        # Create ticket.
[8600]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
[13786]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
[11455]342        if self._isPaymentDisabled(p_session, category, student):
[13798]343            return _('This category of payments has been disabled.'), None
[8712]344        payment = createObject(u'waeup.StudentOnlinePayment')
[8954]345        timestamp = ("%d" % int(time()*10000))[1:]
[8600]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
[9154]351        payment.p_current = p_current
[8600]352        payment.amount_auth = amount
353        return None, payment
[7621]354
[10922]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(_(
[10953]360            'This is to inform you that you have been offered provisional'
361            ' admission into ${a} for the ${b} academic session as follows:',
[10922]362            mapping = {'a': inst_name, 'b': entry_session}),
363            portal_language)
364        return text
365
[10051]366    def maxCredits(self, studylevel):
367        """Return maximum credits.
368
369        """
370        return 48
371
[13353]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
[13415]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
[13416]395        bt = 'all'
[13415]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
[13753]404    def checkAccommodationRequirements(self, student, acc_details):
[14238]405        msg = super(CustomStudentsUtils, self).checkAccommodationRequirements(
[13753]406            student, acc_details)
[14238]407        if msg:
408            return msg
[13753]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
[8444]413    # AAUE prefix
414    STUDENT_ID_PREFIX = u'E'
Note: See TracBrowser for help on using the repository browser.