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

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

restitution fee is for ONLY 2016 academic session

  • Property svn:keywords set to Id
File size: 19.3 KB
Line 
1## $Id: utils.py 14402 2017-01-15 15:20:47Z 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##
18import grok
19from time import time
20from zope.component import createObject
21from waeup.kofa.interfaces import (
22    ADMITTED, CLEARANCE, REQUESTED, CLEARED, RETURNING, PAID,
23    academic_sessions_vocab)
24from kofacustom.nigeria.students.utils import NigeriaStudentsUtils
25from waeup.kofa.accesscodes import create_accesscode
26from waeup.kofa.students.utils import trans
27from waeup.aaue.interswitch.browser import gateway_net_amt, GATEWAY_AMT
28from waeup.aaue.interfaces import MessageFactory as _
29
30class CustomStudentsUtils(NigeriaStudentsUtils):
31    """A collection of customized methods.
32
33    """
34
35    PORTRAIT_CHANGE_STATES = (ADMITTED,)
36
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
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
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
63        elif student.is_postgrad:
64            grok.getSite()['configuration'].next_matric_integer_3 += 1
65            return
66        elif student.current_mode in ('dp_ft',):
67            grok.getSite()['configuration'].next_matric_integer_4 += 1
68            return
69        grok.getSite()['configuration'].next_matric_integer_2 += 1
70        return
71
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
80    def constructMatricNumber(self, student):
81        faccode = student.faccode
82        depcode = student.depcode
83        certcode = student.certcode
84        degree = getattr(
85            getattr(student.get('studycourse', None), 'certificate', None),
86                'degree', None)
87        year = unicode(student.entry_session)[2:]
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
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
94        if student.is_postgrad:
95            next_integer = grok.getSite()['configuration'].next_matric_integer_3
96            if not degree or next_integer == 0:
97                return _('Matriculation number cannot be set.'), None
98            if student.faccode in ('IOE'):
99                return None, "AAU/SPS/%s/%s/%s/%05d" % (
100                    faccode, year, degree, next_integer)
101            return None, "AAU/SPS/%s/%s/%s/%s/%05d" % (
102                faccode, depcode, year, degree, next_integer)
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
107            return None, "PTP/%s/%s/%s/%05d" % (
108                faccode, depcode, year, next_integer)
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)
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)
121
122    def getReturningData(self, student):
123        """ This method defines what happens after school fee payment
124        of returning students depending on the student's senate verdict.
125        """
126        prev_level = student['studycourse'].current_level
127        cur_verdict = student['studycourse'].current_verdict
128        if cur_verdict in ('A','B','C', 'L','M','N','Z',):
129            # Successful student
130            new_level = divmod(int(prev_level),100)[0]*100 + 100
131        #elif cur_verdict == 'C':
132        #    # Student on probation
133        #    new_level = int(prev_level) + 10
134        else:
135            # Student is somehow in an undefined state.
136            # Level has to be set manually.
137            new_level = prev_level
138        new_session = student['studycourse'].current_session + 1
139        return new_session, new_level
140
141    def _isPaymentDisabled(self, p_session, category, student):
142        academic_session = self._getSessionConfiguration(p_session)
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
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
159        if category == 'hostel_maintenance' and \
160            'maint_all' in academic_session.payment_disabled:
161            return True
162        return False
163
164    def setPaymentDetails(self, category, student,
165            previous_session=None, previous_level=None):
166        """Create Payment object and set the payment data of a student for
167        the payment category specified.
168
169        """
170        details = {}
171        p_item = u''
172        amount = 0.0
173        error = u''
174        if previous_session:
175            return _('Previous session payment not yet implemented.'), None
176        p_session = student['studycourse'].current_session
177        p_level = student['studycourse'].current_level
178        p_current = True
179        academic_session = self._getSessionConfiguration(p_session)
180        if academic_session == None:
181            return _(u'Session configuration object is not available.'), None
182        # Determine fee.
183        if category == 'transfer':
184            amount = academic_session.transfer_fee
185        elif category == 'transcript_local':
186            amount = academic_session.transcript_fee_local
187        elif category == 'transcript_inter':
188            amount = academic_session.transcript_fee_inter
189        elif category == 'bed_allocation':
190            amount = academic_session.booking_fee
191        elif category == 'restitution':
192            if student.entry_session == 2016 \
193                and student.current_session == 2016 \
194                or student.current_mode != 'ug_ft':
195                return _(u'Restitution fee payment not required.'), None
196            amount = academic_session.restitution_fee
197        elif category == 'hostel_maintenance':
198            amount = 0.0
199            bedticket = student['accommodation'].get(
200                str(student.current_session), None)
201            if bedticket is not None and bedticket.bed is not None:
202                p_item = bedticket.display_coordinates
203                if bedticket.bed.__parent__.maint_fee > 0:
204                    amount = bedticket.bed.__parent__.maint_fee
205                else:
206                    # fallback
207                    amount = academic_session.maint_fee
208            else:
209                return _(u'No bed allocated.'), None
210        elif student.current_mode == 'found' and category not in (
211            'schoolfee', 'clearance', 'late_registration'):
212            return _('Not allowed.'), None
213        elif category.startswith('clearance'):
214            if student.state not in (ADMITTED, CLEARANCE, REQUESTED, CLEARED):
215                return _(u'Acceptance Fee payments not allowed.'), None
216            if student.current_mode in (
217                'ug_ft', 'ug_pt', 'de_ft', 'de_pt',
218                'transfer', 'mug_ft', 'mde_ft') \
219                and category != 'clearance_incl':
220                    return _("Additional fees must be included."), None
221            if student.faccode == 'FP':
222                amount = academic_session.clearance_fee_fp
223            elif student.current_mode.endswith('_pt'):
224                if student.is_postgrad:
225                    amount = academic_session.clearance_fee_pg_pt
226                else:
227                    amount = academic_session.clearance_fee_ug_pt
228            elif student.faccode == 'FCS':
229                # Students in clinical medical sciences pay the medical
230                # acceptance fee
231                amount = academic_session.clearance_fee_med
232            elif student.is_postgrad:  # and not part-time
233                if category != 'clearance':
234                    return _("No additional fees required."), None
235                amount = academic_session.clearance_fee_pg
236            else:
237                amount = academic_session.clearance_fee
238            p_item = student['studycourse'].certificate.code
239            if amount in (0.0, None):
240                return _(u'Amount could not be determined.'), None
241            # Add Matric Gown Fee and Lapel Fee
242            if category == 'clearance_incl':
243                amount += gateway_net_amt(academic_session.matric_gown_fee) + \
244                    gateway_net_amt(academic_session.lapel_fee)
245        elif category == 'late_registration':
246            if student.is_postgrad:
247                amount = academic_session.late_pg_registration_fee
248            else:
249                amount = academic_session.late_registration_fee
250        elif category.startswith('schoolfee'):
251            try:
252                certificate = student['studycourse'].certificate
253                p_item = certificate.code
254            except (AttributeError, TypeError):
255                return _('Study course data are incomplete.'), None
256            if student.is_postgrad and category != 'schoolfee':
257                return _("No additional fees required."), None
258            if student.current_mode in (
259                'ug_ft', 'ug_pt', 'de_ft', 'de_pt',
260                'transfer', 'mug_ft', 'mde_ft') \
261                and not category in (
262                'schoolfee_incl', 'schoolfee_1', 'schoolfee_2'):
263                    return _("You must choose a payment which includes "
264                             "additional fees."), None
265            if category in ('schoolfee_1', 'schoolfee_2'):
266                if student.current_mode == 'ug_pt':
267                    return _("Part-time students are not allowed "
268                             "to pay by instalments."), None
269                if student.entry_session < 2015:
270                    return _("You are not allowed "
271                             "to pay by instalments."), None
272            if student.state == CLEARED and category != 'schoolfee_2':
273                amount = getattr(certificate, 'school_fee_1', 0.0)
274                # Cut school fee by 50%
275                if category == 'schoolfee_1' and amount:
276                    amount = gateway_net_amt(amount) / 2 + GATEWAY_AMT
277            elif student.is_fresh and category == 'schoolfee_2':
278                amount = getattr(certificate, 'school_fee_1', 0.0)
279                # Cut school fee by 50%
280                if amount:
281                    amount = gateway_net_amt(amount) / 2 + GATEWAY_AMT
282            elif student.state == RETURNING and category != 'schoolfee_2':
283                if not student.father_name:
284                    return _("Personal data form is not properly filled."), None
285                # In case of returning school fee payment the payment session
286                # and level contain the values of the session the student
287                # has paid for.
288                p_session, p_level = self.getReturningData(student)
289                try:
290                    academic_session = grok.getSite()[
291                        'configuration'][str(p_session)]
292                except KeyError:
293                    return _(u'Session configuration object is not available.'), None
294                if student.entry_session >= 2015:
295                    amount = getattr(certificate, 'school_fee_2', 0.0)
296                    # Cut school fee by 50%
297                    if category == 'schoolfee_1' and amount:
298                        amount = gateway_net_amt(amount) / 2 + GATEWAY_AMT
299                else:
300                    amount = getattr(certificate, 'school_fee_3', 0.0)
301            elif category == 'schoolfee_2':
302                amount = getattr(certificate, 'school_fee_2', 0.0)
303                # Cut school fee by 50%
304                if amount:
305                    amount = gateway_net_amt(amount) / 2 + GATEWAY_AMT
306            else:
307                return _('Wrong state.'), None
308            if amount in (0.0, None):
309                return _(u'Amount could not be determined.'), None
310            # Add Student Union Fee , Student Id Card Fee and Welfare Assurance
311            if category in ('schoolfee_incl', 'schoolfee_1'):
312                amount += gateway_net_amt(academic_session.welfare_fee) + \
313                    gateway_net_amt(academic_session.union_fee)
314                if student.entry_session == 2016 \
315                    and student.entry_mode == 'ug_ft' \
316                    and student.state == CLEARED:
317                    amount += gateway_net_amt(academic_session.id_card_fee)
318            # Add non-indigenous fee and session specific penalty fees
319            if student.is_postgrad:
320                amount += academic_session.penalty_pg
321                if student.lga and not student.lga.startswith('edo'):
322                    amount += 20000.0
323            else:
324                amount += academic_session.penalty_ug
325        elif not student.is_postgrad:
326            fee_name = category + '_fee'
327            amount = getattr(academic_session, fee_name, 0.0)
328        if amount in (0.0, None):
329            return _(u'Amount could not be determined.'), None
330        # Create ticket.
331        for key in student['payments'].keys():
332            ticket = student['payments'][key]
333            if ticket.p_state == 'paid' and\
334               ticket.p_category == category and \
335               ticket.p_item == p_item and \
336               ticket.p_session == p_session:
337                  return _('This type of payment has already been made.'), None
338            # Additional condition in AAUE
339            if category in ('schoolfee', 'schoolfee_incl', 'schoolfee_1'):
340                if ticket.p_state == 'paid' and \
341                   ticket.p_category in ('schoolfee',
342                                         'schoolfee_incl',
343                                         'schoolfee_1') and \
344                   ticket.p_item == p_item and \
345                   ticket.p_session == p_session:
346                      return _(
347                          'Another school fee payment for this '
348                          'session has already been made.'), None
349
350        if self._isPaymentDisabled(p_session, category, student):
351            return _('This category of payments has been disabled.'), None
352        payment = createObject(u'waeup.StudentOnlinePayment')
353        timestamp = ("%d" % int(time()*10000))[1:]
354        payment.p_id = "p%s" % timestamp
355        payment.p_category = category
356        payment.p_item = p_item
357        payment.p_session = p_session
358        payment.p_level = p_level
359        payment.p_current = p_current
360        payment.amount_auth = amount
361        return None, payment
362
363    def _admissionText(self, student, portal_language):
364        inst_name = grok.getSite()['configuration'].name
365        entry_session = student['studycourse'].entry_session
366        entry_session = academic_sessions_vocab.getTerm(entry_session).title
367        text = trans(_(
368            'This is to inform you that you have been offered provisional'
369            ' admission into ${a} for the ${b} academic session as follows:',
370            mapping = {'a': inst_name, 'b': entry_session}),
371            portal_language)
372        return text
373
374    def maxCredits(self, studylevel):
375        """Return maximum credits.
376
377        """
378        return 48
379
380    def getBedCoordinates(self, bedticket):
381        """Return descriptive bed coordinates.
382        This method can be used to customize the `display_coordinates`
383        property method in order to  display a
384        customary description of the bed space.
385        """
386        bc = bedticket.bed_coordinates.split(',')
387        if len(bc) == 4:
388            return bc[0]
389        return bedticket.bed_coordinates
390
391    def getAccommodationDetails(self, student):
392        """Determine the accommodation data of a student.
393        """
394        d = {}
395        d['error'] = u''
396        hostels = grok.getSite()['hostels']
397        d['booking_session'] = hostels.accommodation_session
398        d['allowed_states'] = hostels.accommodation_states
399        d['startdate'] = hostels.startdate
400        d['enddate'] = hostels.enddate
401        d['expired'] = hostels.expired
402        # Determine bed type
403        bt = 'all'
404        if student.sex == 'f':
405            sex = 'female'
406        else:
407            sex = 'male'
408        special_handling = 'regular'
409        d['bt'] = u'%s_%s_%s' % (special_handling,sex,bt)
410        return d
411
412    def checkAccommodationRequirements(self, student, acc_details):
413        msg = super(CustomStudentsUtils, self).checkAccommodationRequirements(
414            student, acc_details)
415        if msg:
416            return msg
417        if student.current_mode not in ('ug_ft', 'de_ft', 'mug_ft', 'mde_ft'):
418            return _('You are not eligible to book accommodation.')
419        return
420
421    # AAUE prefix
422    STUDENT_ID_PREFIX = u'E'
Note: See TracBrowser for help on using the repository browser.