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

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

Implement different course registration deadlines and fees (untested!)

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