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

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

Add conditions for restitution fee payments.

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