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

Last change on this file since 15164 was 15096, checked in by Henrik Bettermann, 6 years ago

Transcripts can be paid several times per session.

  • Property svn:keywords set to Id
File size: 25.6 KB
RevLine 
[7419]1## $Id: utils.py 15096 2018-08-01 05:24:33Z 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
[14918]20from zope.component import createObject, queryUtility
21from zope.catalog.interfaces import ICatalog
[10922]22from waeup.kofa.interfaces import (
[13594]23    ADMITTED, CLEARANCE, REQUESTED, CLEARED, RETURNING, PAID,
24    academic_sessions_vocab)
[8823]25from kofacustom.nigeria.students.utils import NigeriaStudentsUtils
[8247]26from waeup.kofa.accesscodes import create_accesscode
[10922]27from waeup.kofa.students.utils import trans
[13720]28from waeup.aaue.interswitch.browser import gateway_net_amt, GATEWAY_AMT
[8444]29from waeup.aaue.interfaces import MessageFactory as _
[6902]30
[14663]31MINIMUM_UNITS_THRESHOLD = 15
32
[8823]33class CustomStudentsUtils(NigeriaStudentsUtils):
[7151]34    """A collection of customized methods.
35
36    """
37
[14242]38    PORTRAIT_CHANGE_STATES = (ADMITTED,)
[13348]39
[14918]40    def GPABoundaries(self, faccode=None, depcode=None,
41                            certcode=None, student=None):
42        if student and student.current_mode.startswith('dp'):
[14959]43            return ((1.5, 'IRNS / NER / NYV'),
[14918]44                    (2.4, 'Pass'),
45                    (3.5, 'Merit'),
46                    (4.5, 'Credit'),
47                    (5, 'Distinction'))
48        elif student:
49            return ((1, 'FRNS / NER / NYV'),
50                    (1.5, 'Pass'),
51                    (2.4, '3rd Class Honours'),
52                    (3.5, '2nd Class Honours Lower Division'),
53                    (4.5, '2nd Class Honours Upper Division'),
54                    (5, '1st Class Honours'))
55        # Session Results Presentations depend on certificate
56        results = None
57        if certcode:
58            cat = queryUtility(ICatalog, name='certificates_catalog')
59            results = list(
60                cat.searchResults(code=(certcode, certcode)))
61        if results and results[0].study_mode.startswith('dp'):
[14975]62            return ((1.5, 'IRNS / NER / NYV'),
[14918]63                    (2.4, 'Pass'),
64                    (3.5, 'Merit'),
65                    (4.5, 'Credit'),
66                    (5, 'Distinction'))
67        else:
68            return ((1, 'FRNS / NER / NYV'),
69                    (1.5, 'Pass'),
70                    (2.4, '3rd Class Honours'),
71                    (3.5, '2nd Class Honours Lower Division'),
72                    (4.5, '2nd Class Honours Upper Division'),
73                    (5, '1st Class Honours'))
[14899]74
[14462]75    def getClassFromCGPA(self, gpa, student):
[14918]76        gpa_boundaries = self.GPABoundaries(student=student)
[15095]77
78        try:
79            certificate = getattr(student['studycourse'],'certificate',None)
80            end_level = getattr(certificate, 'end_level', None)
81            final_level = max([studylevel.level for studylevel in student['studycourse'].values()])
82            if end_level and final_level >= end_level:
83                dummy, repeat = divmod(final_level, 100)
84                if gpa <= 5.1 and repeat == 20:
85                    # Irrespective of the CGPA of a student, if the He/She has
86                    # 3rd Extension, such student will be graduated with a "Pass".
87                    return 1, gpa_boundaries[1][1]
88        except ValueError:
89            pass
90
[14899]91        if gpa < gpa_boundaries[0][0]:
92            # FRNS / Fail
93            return 0, gpa_boundaries[0][1]
[14977]94        if student.entry_session < 2013 or \
95            student.current_mode.startswith('dp'):
[14899]96            if gpa < gpa_boundaries[1][0]:
[14462]97                # Pass
[14899]98                return 1, gpa_boundaries[1][1]
[14462]99        else:
[14899]100            if gpa < gpa_boundaries[1][0]:
[14977]101                # FRNS
102                # Pass degree has been phased out in 2013 for non-diploma
103                # students
[14899]104                return 0, gpa_boundaries[0][1]
105        if gpa < gpa_boundaries[2][0]:
[15095]106            # 3rd / Merit
[14899]107            return 2, gpa_boundaries[2][1]
108        if gpa < gpa_boundaries[3][0]:
[15095]109            # 2nd L / Credit
[14899]110            return 3, gpa_boundaries[3][1]
111        if gpa < gpa_boundaries[4][0]:
[15095]112            # 2nd U / Distinction
[14899]113            return 4, gpa_boundaries[4][1]
114        if gpa <= gpa_boundaries[5][0]:
[15095]115            # 1st
[14899]116            return 5, gpa_boundaries[5][1]
[15095]117        return
[14462]118
[14160]119    def getDegreeClassNumber(self, level_obj):
[14415]120        """Get degree class number (used for SessionResultsPresentation
121        reports).
122        """
[14459]123        certificate = getattr(level_obj.__parent__,'certificate', None)
[14160]124        end_level = getattr(certificate, 'end_level', None)
[14459]125        if end_level and level_obj.level >= end_level:
[14464]126            if level_obj.level > end_level:
127                # spill-over level
[14476]128                if level_obj.gpa_params[1] == 0:
[14464]129                    # no credits taken
130                    return 0
[14663]131            elif level_obj.gpa_params[1] < MINIMUM_UNITS_THRESHOLD:
[14537]132                # credits taken below limit
133                return 0
[14160]134            failed_courses = level_obj.passed_params[4]
[14411]135            not_taken_courses = level_obj.passed_params[5]
[14160]136            if '_m' in failed_courses:
137                return 0
[14441]138            if len(not_taken_courses) \
[14506]139                and not not_taken_courses == 'Nil':
[14411]140                return 0
[14663]141        elif level_obj.gpa_params[1] < MINIMUM_UNITS_THRESHOLD:
[14464]142            # credits taken below limit
143            return 0
[14487]144        if level_obj.level_verdict in ('FRNS', 'NER', 'NYV'):
[14464]145            return 0
[14160]146        # use gpa_boundaries above
[14462]147        return self.getClassFromCGPA(
148            level_obj.cumulative_params[0], level_obj.student)[0]
[14160]149
[13359]150    def increaseMatricInteger(self, student):
151        """Increase counter for matric numbers.
152        This counter can be a centrally stored attribute or an attribute of
153        faculties, departments or certificates. In the base package the counter
154        is as an attribute of the site configuration container.
155        """
156        if student.current_mode in ('ug_pt', 'de_pt'):
157            grok.getSite()['configuration'].next_matric_integer += 1
158            return
[13609]159        elif student.is_postgrad:
160            grok.getSite()['configuration'].next_matric_integer_3 += 1
161            return
[13793]162        elif student.current_mode in ('dp_ft',):
163            grok.getSite()['configuration'].next_matric_integer_4 += 1
164            return
[13359]165        grok.getSite()['configuration'].next_matric_integer_2 += 1
166        return
167
[13749]168    def _concessionalPaymentMade(self, student):
169        if len(student['payments']):
170            for ticket in student['payments'].values():
171                if ticket.p_state == 'paid' and \
172                    ticket.p_category == 'concessional':
173                    return True
174        return False
175
[11596]176    def constructMatricNumber(self, student):
[11593]177        faccode = student.faccode
178        depcode = student.depcode
[13609]179        certcode = student.certcode
[13664]180        degree = getattr(
181            getattr(student.get('studycourse', None), 'certificate', None),
182                'degree', None)
[11593]183        year = unicode(student.entry_session)[2:]
[13359]184        if not student.state in (PAID, ) or not student.is_fresh or \
[14615]185            student.current_mode in ('found', 'ijmbe'):
[13359]186            return _('Matriculation number cannot be set.'), None
[13755]187        #if student.current_mode not in ('mug_ft', 'mde_ft') and \
188        #    not self._concessionalPaymentMade(student):
189        #    return _('Matriculation number cannot be set.'), None
[13571]190        if student.is_postgrad:
[13609]191            next_integer = grok.getSite()['configuration'].next_matric_integer_3
[13664]192            if not degree or next_integer == 0:
[13609]193                return _('Matriculation number cannot be set.'), None
[13846]194            if student.faccode in ('IOE'):
195                return None, "AAU/SPS/%s/%s/%s/%05d" % (
196                    faccode, year, degree, next_integer)
[13609]197            return None, "AAU/SPS/%s/%s/%s/%s/%05d" % (
[13664]198                faccode, depcode, year, degree, next_integer)
[13359]199        if student.current_mode in ('ug_pt', 'de_pt'):
200            next_integer = grok.getSite()['configuration'].next_matric_integer
201            if next_integer == 0:
202                return _('Matriculation number cannot be set.'), None
[12975]203            return None, "PTP/%s/%s/%s/%05d" % (
204                faccode, depcode, year, next_integer)
[13793]205        if student.current_mode in ('dp_ft',):
206            next_integer = grok.getSite()['configuration'].next_matric_integer_4
207            if next_integer == 0:
208                return _('Matriculation number cannot be set.'), None
209            return None, "IOE/DIP/%s/%05d" % (year, next_integer)
[13359]210        next_integer = grok.getSite()['configuration'].next_matric_integer_2
211        if next_integer == 0:
212            return _('Matriculation number cannot be set.'), None
213        if student.faccode in ('FBM', 'FCS'):
214            return None, "CMS/%s/%s/%s/%05d" % (
215                faccode, depcode, year, next_integer)
216        return None, "%s/%s/%s/%05d" % (faccode, depcode, year, next_integer)
[12975]217
[8270]218    def getReturningData(self, student):
219        """ This method defines what happens after school fee payment
[8319]220        of returning students depending on the student's senate verdict.
[8270]221        """
[8319]222        prev_level = student['studycourse'].current_level
223        cur_verdict = student['studycourse'].current_verdict
[14958]224        if cur_verdict in ('A','B','L','M','N','Z',):
[8319]225            # Successful student
226            new_level = divmod(int(prev_level),100)[0]*100 + 100
[14958]227        elif cur_verdict == 'C':
228            # Student on probation
229            new_level = int(prev_level) + 10
[8319]230        else:
231            # Student is somehow in an undefined state.
232            # Level has to be set manually.
233            new_level = prev_level
[8270]234        new_session = student['studycourse'].current_session + 1
235        return new_session, new_level
236
[13454]237    def _isPaymentDisabled(self, p_session, category, student):
238        academic_session = self._getSessionConfiguration(p_session)
[14246]239        if category.startswith('schoolfee'):
240            if 'sf_all' in academic_session.payment_disabled:
241                return True
242            if 'sf_pg' in academic_session.payment_disabled and \
243                student.is_postgrad:
244                return True
[14571]245            if 'sf_ug_pt' in academic_session.payment_disabled and \
246                student.current_mode in ('ug_pt', 'de_pt'):
[14246]247                return True
248            if 'sf_found' in academic_session.payment_disabled and \
249                student.current_mode == 'found':
250                return True
[13794]251        if category.startswith('clearance') and \
252            'cl_regular' in academic_session.payment_disabled and \
253            student.current_mode in ('ug_ft', 'de_ft', 'mug_ft', 'mde_ft'):
254            return True
[13454]255        if category == 'hostel_maintenance' and \
256            'maint_all' in academic_session.payment_disabled:
257            return True
258        return False
259
[9154]260    def setPaymentDetails(self, category, student,
261            previous_session=None, previous_level=None):
[8600]262        """Create Payment object and set the payment data of a student for
263        the payment category specified.
264
265        """
[8306]266        details = {}
[8600]267        p_item = u''
268        amount = 0.0
269        error = u''
[9154]270        if previous_session:
[14544]271            if previous_session < student['studycourse'].entry_session:
272                return _('The previous session must not fall below '
273                         'your entry session.'), None
274            if category == 'schoolfee':
275                # School fee is always paid for the following session
276                if previous_session > student['studycourse'].current_session:
277                    return _('This is not a previous session.'), None
278            else:
279                if previous_session > student['studycourse'].current_session - 1:
280                    return _('This is not a previous session.'), None
281            p_session = previous_session
282            p_level = previous_level
283            p_current = False
284        else:
285            p_session = student['studycourse'].current_session
286            p_level = student['studycourse'].current_level
287            p_current = True
[9527]288        academic_session = self._getSessionConfiguration(p_session)
289        if academic_session == None:
[8600]290            return _(u'Session configuration object is not available.'), None
[8677]291        # Determine fee.
[7151]292        if category == 'transfer':
[8600]293            amount = academic_session.transfer_fee
[14296]294        elif category == 'transcript_local':
295            amount = academic_session.transcript_fee_local
296        elif category == 'transcript_inter':
297            amount = academic_session.transcript_fee_inter
[7151]298        elif category == 'bed_allocation':
[8600]299            amount = academic_session.booking_fee
[14378]300        elif category == 'restitution':
[14660]301            if student.entry_session >= 2016 \
302                or student.current_mode not in ('ug_ft', 'dp_ft'):
[14378]303                return _(u'Restitution fee payment not required.'), None
304            amount = academic_session.restitution_fee
[7151]305        elif category == 'hostel_maintenance':
[13418]306            amount = 0.0
307            bedticket = student['accommodation'].get(
308                str(student.current_session), None)
[13502]309            if bedticket is not None and bedticket.bed is not None:
[13474]310                p_item = bedticket.display_coordinates
[13418]311                if bedticket.bed.__parent__.maint_fee > 0:
312                    amount = bedticket.bed.__parent__.maint_fee
313                else:
314                    # fallback
315                    amount = academic_session.maint_fee
316            else:
[13506]317                return _(u'No bed allocated.'), None
[13636]318        elif student.current_mode == 'found' and category not in (
319            'schoolfee', 'clearance', 'late_registration'):
320            return _('Not allowed.'), None
[13400]321        elif category.startswith('clearance'):
[13594]322            if student.state not in (ADMITTED, CLEARANCE, REQUESTED, CLEARED):
323                return _(u'Acceptance Fee payments not allowed.'), None
[13855]324            if student.current_mode in (
325                'ug_ft', 'ug_pt', 'de_ft', 'de_pt',
326                'transfer', 'mug_ft', 'mde_ft') \
327                and category != 'clearance_incl':
328                    return _("Additional fees must be included."), None
[14518]329            if student.current_mode == 'ijmbe':
330                amount = academic_session.clearance_fee_ijmbe
[14954]331            elif student.current_mode == 'dp_ft':
332                amount = academic_session.clearance_fee_dp
[14518]333            elif student.faccode == 'FP':
[11653]334                amount = academic_session.clearance_fee_fp
[13377]335            elif student.current_mode.endswith('_pt'):
[13678]336                if student.is_postgrad:
337                    amount = academic_session.clearance_fee_pg_pt
338                else:
339                    amount = academic_session.clearance_fee_ug_pt
[13466]340            elif student.faccode == 'FCS':
341                # Students in clinical medical sciences pay the medical
342                # acceptance fee
[13377]343                amount = academic_session.clearance_fee_med
[13678]344            elif student.is_postgrad:  # and not part-time
[13853]345                if category != 'clearance':
[13854]346                    return _("No additional fees required."), None
[13526]347                amount = academic_session.clearance_fee_pg
[11653]348            else:
349                amount = academic_session.clearance_fee
[8753]350            p_item = student['studycourse'].certificate.code
[13689]351            if amount in (0.0, None):
352                return _(u'Amount could not be determined.'), None
[14239]353            # Add Matric Gown Fee and Lapel Fee
[13689]354            if category == 'clearance_incl':
[13414]355                amount += gateway_net_amt(academic_session.matric_gown_fee) + \
356                    gateway_net_amt(academic_session.lapel_fee)
[11004]357        elif category == 'late_registration':
[14117]358            if student.is_postgrad:
359                amount = academic_session.late_pg_registration_fee
360            else:
361                amount = academic_session.late_registration_fee
[13400]362        elif category.startswith('schoolfee'):
[8600]363            try:
[8753]364                certificate = student['studycourse'].certificate
365                p_item = certificate.code
[8600]366            except (AttributeError, TypeError):
367                return _('Study course data are incomplete.'), None
[13853]368            if student.is_postgrad and category != 'schoolfee':
[13854]369                return _("No additional fees required."), None
[14544]370            if not previous_session and student.current_mode in (
[13855]371                'ug_ft', 'ug_pt', 'de_ft', 'de_pt',
372                'transfer', 'mug_ft', 'mde_ft') \
373                and not category in (
374                'schoolfee_incl', 'schoolfee_1', 'schoolfee_2'):
[14244]375                    return _("You must choose a payment which includes "
[13855]376                             "additional fees."), None
[13780]377            if category in ('schoolfee_1', 'schoolfee_2'):
378                if student.current_mode == 'ug_pt':
379                    return _("Part-time students are not allowed "
380                             "to pay by instalments."), None
[14241]381                if student.entry_session < 2015:
382                    return _("You are not allowed "
383                             "to pay by instalments."), None
[14544]384            if previous_session:
385                # Students can pay for previous sessions in all
386                # workflow states.  Fresh students are excluded by the
387                # update method of the PreviousPaymentAddFormPage.
388                if previous_level == 100:
389                    amount = getattr(certificate, 'school_fee_1', 0.0)
390                else:
[14954]391                    if student.entry_session in (2015, 2016):
[14544]392                        amount = getattr(certificate, 'school_fee_2', 0.0)
393                    else:
394                        amount = getattr(certificate, 'school_fee_3', 0.0)
395            elif student.state == CLEARED and category != 'schoolfee_2':
[14229]396                amount = getattr(certificate, 'school_fee_1', 0.0)
[13512]397                # Cut school fee by 50%
[14241]398                if category == 'schoolfee_1' and amount:
399                    amount = gateway_net_amt(amount) / 2 + GATEWAY_AMT
400            elif student.is_fresh and category == 'schoolfee_2':
401                amount = getattr(certificate, 'school_fee_1', 0.0)
402                # Cut school fee by 50%
403                if amount:
404                    amount = gateway_net_amt(amount) / 2 + GATEWAY_AMT
[14396]405            elif student.state == RETURNING and category != 'schoolfee_2':
[13482]406                if not student.father_name:
407                    return _("Personal data form is not properly filled."), None
[13374]408                # In case of returning school fee payment the payment session
409                # and level contain the values of the session the student
410                # has paid for.
411                p_session, p_level = self.getReturningData(student)
[8961]412                try:
413                    academic_session = grok.getSite()[
414                        'configuration'][str(p_session)]
415                except KeyError:
416                    return _(u'Session configuration object is not available.'), None
[14954]417                if student.entry_session in (2015, 2016):
[14229]418                    amount = getattr(certificate, 'school_fee_2', 0.0)
[13374]419                else:
[14229]420                    amount = getattr(certificate, 'school_fee_3', 0.0)
[14954]421                # Cut school fee by 50%
422                if category == 'schoolfee_1' and amount:
423                    amount = gateway_net_amt(amount) / 2 + GATEWAY_AMT
[14241]424            elif category == 'schoolfee_2':
425                amount = getattr(certificate, 'school_fee_2', 0.0)
426                # Cut school fee by 50%
427                if amount:
428                    amount = gateway_net_amt(amount) / 2 + GATEWAY_AMT
[8600]429            else:
[8753]430                return _('Wrong state.'), None
[13417]431            if amount in (0.0, None):
432                return _(u'Amount could not be determined.'), None
[14244]433            # Add Student Union Fee , Student Id Card Fee and Welfare Assurance
[14987]434            if category in ('schoolfee_incl', 'schoolfee_1') and \
435                student.current_mode != 'ijmbe':
[13414]436                amount += gateway_net_amt(academic_session.welfare_fee) + \
437                    gateway_net_amt(academic_session.union_fee)
[14244]438                if student.entry_session == 2016 \
439                    and student.entry_mode == 'ug_ft' \
440                    and student.state == CLEARED:
441                    amount += gateway_net_amt(academic_session.id_card_fee)
[13534]442            # Add non-indigenous fee and session specific penalty fees
443            if student.is_postgrad:
444                amount += academic_session.penalty_pg
[14246]445                if student.lga and not student.lga.startswith('edo'):
[13534]446                    amount += 20000.0
447            else:
448                amount += academic_session.penalty_ug
[14248]449        elif not student.is_postgrad:
450            fee_name = category + '_fee'
451            amount = getattr(academic_session, fee_name, 0.0)
[8600]452        if amount in (0.0, None):
453            return _(u'Amount could not be determined.'), None
[8677]454        # Create ticket.
[8600]455        for key in student['payments'].keys():
456            ticket = student['payments'][key]
457            if ticket.p_state == 'paid' and\
458               ticket.p_category == category and \
[15096]459               not ticket.p_category.startswith('transcript') and \
[8600]460               ticket.p_item == p_item and \
461               ticket.p_session == p_session:
462                  return _('This type of payment has already been made.'), None
[13786]463            # Additional condition in AAUE
464            if category in ('schoolfee', 'schoolfee_incl', 'schoolfee_1'):
465                if ticket.p_state == 'paid' and \
466                   ticket.p_category in ('schoolfee',
467                                         'schoolfee_incl',
468                                         'schoolfee_1') and \
469                   ticket.p_item == p_item and \
470                   ticket.p_session == p_session:
471                      return _(
472                          'Another school fee payment for this '
473                          'session has already been made.'), None
474
[11455]475        if self._isPaymentDisabled(p_session, category, student):
[13798]476            return _('This category of payments has been disabled.'), None
[8712]477        payment = createObject(u'waeup.StudentOnlinePayment')
[8954]478        timestamp = ("%d" % int(time()*10000))[1:]
[8600]479        payment.p_id = "p%s" % timestamp
480        payment.p_category = category
481        payment.p_item = p_item
482        payment.p_session = p_session
483        payment.p_level = p_level
[9154]484        payment.p_current = p_current
[8600]485        payment.amount_auth = amount
486        return None, payment
[7621]487
[10922]488    def _admissionText(self, student, portal_language):
489        inst_name = grok.getSite()['configuration'].name
490        entry_session = student['studycourse'].entry_session
491        entry_session = academic_sessions_vocab.getTerm(entry_session).title
492        text = trans(_(
[10953]493            'This is to inform you that you have been offered provisional'
494            ' admission into ${a} for the ${b} academic session as follows:',
[10922]495            mapping = {'a': inst_name, 'b': entry_session}),
496            portal_language)
497        return text
498
[14585]499    def warnCreditsOOR(self, studylevel, course=None):
[14733]500        studycourse = studylevel.__parent__
501        certificate = getattr(studycourse,'certificate', None)
502        current_level = studycourse.current_level
503        if None in (current_level, certificate):
504            return
505        end_level = certificate.end_level
506        if current_level >= end_level:
507            limit = 52
508        else:
509            limit = 48
510        if course and studylevel.total_credits + course.credits > limit:
[14585]511            return  _('Maximum credits exceeded.')
[14733]512        elif studylevel.total_credits > limit:
[14585]513            return _('Maximum credits exceeded.')
514        return
[10051]515
[13353]516    def getBedCoordinates(self, bedticket):
517        """Return descriptive bed coordinates.
518        This method can be used to customize the `display_coordinates`
519        property method in order to  display a
520        customary description of the bed space.
521        """
522        bc = bedticket.bed_coordinates.split(',')
523        if len(bc) == 4:
524            return bc[0]
525        return bedticket.bed_coordinates
526
[13415]527    def getAccommodationDetails(self, student):
528        """Determine the accommodation data of a student.
529        """
530        d = {}
531        d['error'] = u''
532        hostels = grok.getSite()['hostels']
533        d['booking_session'] = hostels.accommodation_session
534        d['allowed_states'] = hostels.accommodation_states
535        d['startdate'] = hostels.startdate
536        d['enddate'] = hostels.enddate
537        d['expired'] = hostels.expired
538        # Determine bed type
[13416]539        bt = 'all'
[13415]540        if student.sex == 'f':
541            sex = 'female'
542        else:
543            sex = 'male'
544        special_handling = 'regular'
545        d['bt'] = u'%s_%s_%s' % (special_handling,sex,bt)
546        return d
547
[13753]548    def checkAccommodationRequirements(self, student, acc_details):
[14238]549        msg = super(CustomStudentsUtils, self).checkAccommodationRequirements(
[13753]550            student, acc_details)
[14238]551        if msg:
552            return msg
[13753]553        if student.current_mode not in ('ug_ft', 'de_ft', 'mug_ft', 'mde_ft'):
554            return _('You are not eligible to book accommodation.')
555        return
556
[8444]557    # AAUE prefix
[14593]558    STUDENT_ID_PREFIX = u'E'
559
560    STUDENT_EXPORTER_NAMES = ('students', 'studentstudycourses',
561            'studentstudylevels', 'coursetickets',
562            'studentpayments', 'studentunpaidpayments',
[15050]563            'bedtickets', 'sfpaymentsoverview',
[14593]564            'studylevelsoverview', 'combocard', 'bursary',
565            'levelreportdata')
Note: See TracBrowser for help on using the repository browser.