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

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

Adjust transcript payments to alumni portal.

  • Property svn:keywords set to Id
File size: 19.0 KB
RevLine 
[7419]1## $Id: utils.py 14296 2016-11-28 12:10:49Z 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
[14296]185        elif category == 'transcript_local':
186            amount = academic_session.transcript_fee_local
187        elif category == 'transcript_inter':
188            amount = academic_session.transcript_fee_inter
[7151]189        elif category == 'bed_allocation':
[8600]190            amount = academic_session.booking_fee
[7151]191        elif category == 'hostel_maintenance':
[13418]192            amount = 0.0
193            bedticket = student['accommodation'].get(
194                str(student.current_session), None)
[13502]195            if bedticket is not None and bedticket.bed is not None:
[13474]196                p_item = bedticket.display_coordinates
[13418]197                if bedticket.bed.__parent__.maint_fee > 0:
198                    amount = bedticket.bed.__parent__.maint_fee
199                else:
200                    # fallback
201                    amount = academic_session.maint_fee
202            else:
[13506]203                return _(u'No bed allocated.'), None
[13636]204        elif student.current_mode == 'found' and category not in (
205            'schoolfee', 'clearance', 'late_registration'):
206            return _('Not allowed.'), None
[13400]207        elif category.startswith('clearance'):
[13594]208            if student.state not in (ADMITTED, CLEARANCE, REQUESTED, CLEARED):
209                return _(u'Acceptance Fee payments not allowed.'), None
[13855]210            if student.current_mode in (
211                'ug_ft', 'ug_pt', 'de_ft', 'de_pt',
212                'transfer', 'mug_ft', 'mde_ft') \
213                and category != 'clearance_incl':
214                    return _("Additional fees must be included."), None
[11653]215            if student.faccode == 'FP':
216                amount = academic_session.clearance_fee_fp
[13377]217            elif student.current_mode.endswith('_pt'):
[13678]218                if student.is_postgrad:
219                    amount = academic_session.clearance_fee_pg_pt
220                else:
221                    amount = academic_session.clearance_fee_ug_pt
[13466]222            elif student.faccode == 'FCS':
223                # Students in clinical medical sciences pay the medical
224                # acceptance fee
[13377]225                amount = academic_session.clearance_fee_med
[13678]226            elif student.is_postgrad:  # and not part-time
[13853]227                if category != 'clearance':
[13854]228                    return _("No additional fees required."), None
[13526]229                amount = academic_session.clearance_fee_pg
[11653]230            else:
231                amount = academic_session.clearance_fee
[8753]232            p_item = student['studycourse'].certificate.code
[13689]233            if amount in (0.0, None):
234                return _(u'Amount could not be determined.'), None
[14239]235            # Add Matric Gown Fee and Lapel Fee
[13689]236            if category == 'clearance_incl':
[13414]237                amount += gateway_net_amt(academic_session.matric_gown_fee) + \
238                    gateway_net_amt(academic_session.lapel_fee)
[11004]239        elif category == 'late_registration':
[14117]240            if student.is_postgrad:
241                amount = academic_session.late_pg_registration_fee
242            else:
243                amount = academic_session.late_registration_fee
[13400]244        elif category.startswith('schoolfee'):
[8600]245            try:
[8753]246                certificate = student['studycourse'].certificate
247                p_item = certificate.code
[8600]248            except (AttributeError, TypeError):
249                return _('Study course data are incomplete.'), None
[13853]250            if student.is_postgrad and category != 'schoolfee':
[13854]251                return _("No additional fees required."), None
[13855]252            if student.current_mode in (
253                'ug_ft', 'ug_pt', 'de_ft', 'de_pt',
254                'transfer', 'mug_ft', 'mde_ft') \
255                and not category in (
256                'schoolfee_incl', 'schoolfee_1', 'schoolfee_2'):
[14244]257                    return _("You must choose a payment which includes "
[13855]258                             "additional fees."), None
[13780]259            if category in ('schoolfee_1', 'schoolfee_2'):
260                if student.current_mode == 'ug_pt':
261                    return _("Part-time students are not allowed "
262                             "to pay by instalments."), None
[14241]263                if student.entry_session < 2015:
264                    return _("You are not allowed "
265                             "to pay by instalments."), None
266            if student.state == CLEARED:
[14229]267                amount = getattr(certificate, 'school_fee_1', 0.0)
[13512]268                # Cut school fee by 50%
[14241]269                if category == 'schoolfee_1' and amount:
270                    amount = gateway_net_amt(amount) / 2 + GATEWAY_AMT
271            elif student.is_fresh and category == 'schoolfee_2':
272                amount = getattr(certificate, 'school_fee_1', 0.0)
273                # Cut school fee by 50%
274                if amount:
275                    amount = gateway_net_amt(amount) / 2 + GATEWAY_AMT
[13374]276            elif student.state == RETURNING:
[13482]277                if not student.father_name:
278                    return _("Personal data form is not properly filled."), None
[13374]279                # In case of returning school fee payment the payment session
280                # and level contain the values of the session the student
281                # has paid for.
282                p_session, p_level = self.getReturningData(student)
[8961]283                try:
284                    academic_session = grok.getSite()[
285                        'configuration'][str(p_session)]
286                except KeyError:
287                    return _(u'Session configuration object is not available.'), None
[14241]288                if student.entry_session >= 2015:
[14229]289                    amount = getattr(certificate, 'school_fee_2', 0.0)
[14241]290                    # Cut school fee by 50%
291                    if category in ('schoolfee_1', 'schoolfee_2') and amount:
292                        amount = gateway_net_amt(amount) / 2 + GATEWAY_AMT
[13374]293                else:
[14229]294                    amount = getattr(certificate, 'school_fee_3', 0.0)
[14241]295            elif category == 'schoolfee_2':
296                amount = getattr(certificate, 'school_fee_2', 0.0)
297                # Cut school fee by 50%
298                if amount:
299                    amount = gateway_net_amt(amount) / 2 + GATEWAY_AMT
[8600]300            else:
[8753]301                return _('Wrong state.'), None
[13417]302            if amount in (0.0, None):
303                return _(u'Amount could not be determined.'), None
[14244]304            # Add Student Union Fee , Student Id Card Fee and Welfare Assurance
[13512]305            if category in ('schoolfee_incl', 'schoolfee_1'):
[13414]306                amount += gateway_net_amt(academic_session.welfare_fee) + \
307                    gateway_net_amt(academic_session.union_fee)
[14244]308                if student.entry_session == 2016 \
309                    and student.entry_mode == 'ug_ft' \
310                    and student.state == CLEARED:
311                    amount += gateway_net_amt(academic_session.id_card_fee)
[13534]312            # Add non-indigenous fee and session specific penalty fees
313            if student.is_postgrad:
314                amount += academic_session.penalty_pg
[14246]315                if student.lga and not student.lga.startswith('edo'):
[13534]316                    amount += 20000.0
317            else:
318                amount += academic_session.penalty_ug
[14248]319        elif not student.is_postgrad:
320            fee_name = category + '_fee'
321            amount = getattr(academic_session, fee_name, 0.0)
[8600]322        if amount in (0.0, None):
323            return _(u'Amount could not be determined.'), None
[8677]324        # Create ticket.
[8600]325        for key in student['payments'].keys():
326            ticket = student['payments'][key]
327            if ticket.p_state == 'paid' and\
328               ticket.p_category == category and \
329               ticket.p_item == p_item and \
330               ticket.p_session == p_session:
331                  return _('This type of payment has already been made.'), None
[13786]332            # Additional condition in AAUE
333            if category in ('schoolfee', 'schoolfee_incl', 'schoolfee_1'):
334                if ticket.p_state == 'paid' and \
335                   ticket.p_category in ('schoolfee',
336                                         'schoolfee_incl',
337                                         'schoolfee_1') and \
338                   ticket.p_item == p_item and \
339                   ticket.p_session == p_session:
340                      return _(
341                          'Another school fee payment for this '
342                          'session has already been made.'), None
343
[11455]344        if self._isPaymentDisabled(p_session, category, student):
[13798]345            return _('This category of payments has been disabled.'), None
[8712]346        payment = createObject(u'waeup.StudentOnlinePayment')
[8954]347        timestamp = ("%d" % int(time()*10000))[1:]
[8600]348        payment.p_id = "p%s" % timestamp
349        payment.p_category = category
350        payment.p_item = p_item
351        payment.p_session = p_session
352        payment.p_level = p_level
[9154]353        payment.p_current = p_current
[8600]354        payment.amount_auth = amount
355        return None, payment
[7621]356
[10922]357    def _admissionText(self, student, portal_language):
358        inst_name = grok.getSite()['configuration'].name
359        entry_session = student['studycourse'].entry_session
360        entry_session = academic_sessions_vocab.getTerm(entry_session).title
361        text = trans(_(
[10953]362            'This is to inform you that you have been offered provisional'
363            ' admission into ${a} for the ${b} academic session as follows:',
[10922]364            mapping = {'a': inst_name, 'b': entry_session}),
365            portal_language)
366        return text
367
[10051]368    def maxCredits(self, studylevel):
369        """Return maximum credits.
370
371        """
372        return 48
373
[13353]374    def getBedCoordinates(self, bedticket):
375        """Return descriptive bed coordinates.
376        This method can be used to customize the `display_coordinates`
377        property method in order to  display a
378        customary description of the bed space.
379        """
380        bc = bedticket.bed_coordinates.split(',')
381        if len(bc) == 4:
382            return bc[0]
383        return bedticket.bed_coordinates
384
[13415]385    def getAccommodationDetails(self, student):
386        """Determine the accommodation data of a student.
387        """
388        d = {}
389        d['error'] = u''
390        hostels = grok.getSite()['hostels']
391        d['booking_session'] = hostels.accommodation_session
392        d['allowed_states'] = hostels.accommodation_states
393        d['startdate'] = hostels.startdate
394        d['enddate'] = hostels.enddate
395        d['expired'] = hostels.expired
396        # Determine bed type
[13416]397        bt = 'all'
[13415]398        if student.sex == 'f':
399            sex = 'female'
400        else:
401            sex = 'male'
402        special_handling = 'regular'
403        d['bt'] = u'%s_%s_%s' % (special_handling,sex,bt)
404        return d
405
[13753]406    def checkAccommodationRequirements(self, student, acc_details):
[14238]407        msg = super(CustomStudentsUtils, self).checkAccommodationRequirements(
[13753]408            student, acc_details)
[14238]409        if msg:
410            return msg
[13753]411        if student.current_mode not in ('ug_ft', 'de_ft', 'mug_ft', 'mde_ft'):
412            return _('You are not eligible to book accommodation.')
413        return
414
[8444]415    # AAUE prefix
416    STUDENT_ID_PREFIX = u'E'
Note: See TracBrowser for help on using the repository browser.