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

Last change on this file since 13749 was 13749, checked in by Henrik Bettermann, 9 years ago

Add study modes mug_ft and mde_ft. All students, except those with entry mode mug_ft and mde_ft, have pay concessional fee before getting a matric number.

  • Property svn:keywords set to Id
File size: 14.6 KB
Line 
1## $Id: utils.py 13749 2016-02-29 07:23:48Z 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, RETURNING)
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 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
53        elif student.is_postgrad:
54            grok.getSite()['configuration'].next_matric_integer_3 += 1
55            return
56        grok.getSite()['configuration'].next_matric_integer_2 += 1
57        return
58
59    def _concessionalPaymentMade(self, student):
60        if len(student['payments']):
61            for ticket in student['payments'].values():
62                if ticket.p_state == 'paid' and \
63                    ticket.p_category == 'concessional':
64                    return True
65        return False
66
67    def constructMatricNumber(self, student):
68        faccode = student.faccode
69        depcode = student.depcode
70        certcode = student.certcode
71        degree = getattr(
72            getattr(student.get('studycourse', None), 'certificate', None),
73                'degree', None)
74        year = unicode(student.entry_session)[2:]
75        if not student.state in (PAID, ) or not student.is_fresh or \
76            student.current_mode == 'found':
77            return _('Matriculation number cannot be set.'), None
78        if student.current_mode not in ('mug_ft', 'mde_ft') and \
79            not self._concessionalPaymentMade(student):
80            return _('Matriculation number cannot be set.'), None
81        if student.is_postgrad:
82            next_integer = grok.getSite()['configuration'].next_matric_integer_3
83            if not degree or next_integer == 0:
84                return _('Matriculation number cannot be set.'), None
85            return None, "AAU/SPS/%s/%s/%s/%s/%05d" % (
86                faccode, depcode, year, degree, next_integer)
87        if student.current_mode in ('ug_pt', 'de_pt'):
88            next_integer = grok.getSite()['configuration'].next_matric_integer
89            if next_integer == 0:
90                return _('Matriculation number cannot be set.'), None
91            return None, "PTP/%s/%s/%s/%05d" % (
92                faccode, depcode, year, next_integer)
93        next_integer = grok.getSite()['configuration'].next_matric_integer_2
94        if next_integer == 0:
95            return _('Matriculation number cannot be set.'), None
96        if student.faccode in ('FBM', 'FCS'):
97            return None, "CMS/%s/%s/%s/%05d" % (
98                faccode, depcode, year, next_integer)
99        return None, "%s/%s/%s/%05d" % (faccode, depcode, year, next_integer)
100
101    def getReturningData(self, student):
102        """ This method defines what happens after school fee payment
103        of returning students depending on the student's senate verdict.
104        """
105        prev_level = student['studycourse'].current_level
106        cur_verdict = student['studycourse'].current_verdict
107        if cur_verdict in ('A','B','L','M','N','Z',):
108            # Successful student
109            new_level = divmod(int(prev_level),100)[0]*100 + 100
110        elif cur_verdict == 'C':
111            # Student on probation
112            new_level = int(prev_level) + 10
113        else:
114            # Student is somehow in an undefined state.
115            # Level has to be set manually.
116            new_level = prev_level
117        new_session = student['studycourse'].current_session + 1
118        return new_session, new_level
119
120    def _isPaymentDisabled(self, p_session, category, student):
121        academic_session = self._getSessionConfiguration(p_session)
122        if category == 'schoolfee' and \
123            'sf_all' in academic_session.payment_disabled:
124            return True
125        if category == 'hostel_maintenance' and \
126            'maint_all' in academic_session.payment_disabled:
127            return True
128        return False
129
130    def setPaymentDetails(self, category, student,
131            previous_session=None, previous_level=None):
132        """Create Payment object and set the payment data of a student for
133        the payment category specified.
134
135        """
136        details = {}
137        p_item = u''
138        amount = 0.0
139        error = u''
140        if previous_session:
141            return _('Previous session payment not yet implemented.'), None
142        p_session = student['studycourse'].current_session
143        p_level = student['studycourse'].current_level
144        p_current = True
145        academic_session = self._getSessionConfiguration(p_session)
146        if academic_session == None:
147            return _(u'Session configuration object is not available.'), None
148        # Determine fee.
149        if category == 'transfer':
150            amount = academic_session.transfer_fee
151        elif category == 'transcript':
152            amount = academic_session.transcript_fee
153        elif category == 'bed_allocation':
154            amount = academic_session.booking_fee
155        elif category == 'hostel_maintenance':
156            amount = 0.0
157            bedticket = student['accommodation'].get(
158                str(student.current_session), None)
159            if bedticket is not None and bedticket.bed is not None:
160                p_item = bedticket.display_coordinates
161                if bedticket.bed.__parent__.maint_fee > 0:
162                    amount = bedticket.bed.__parent__.maint_fee
163                else:
164                    # fallback
165                    amount = academic_session.maint_fee
166            else:
167                return _(u'No bed allocated.'), None
168        elif category == 'welfare':
169            amount = academic_session.welfare_fee
170        elif category == 'union':
171            amount = academic_session.union_fee
172        elif category == 'lapel':
173            amount = academic_session.lapel_fee
174        elif category == 'matric_gown':
175            amount = academic_session.matric_gown_fee
176        elif category == 'concessional':
177            amount = academic_session.concessional_fee
178        elif student.current_mode == 'found' and category not in (
179            'schoolfee', 'clearance', 'late_registration'):
180            return _('Not allowed.'), None
181        elif category.startswith('clearance'):
182            if student.state not in (ADMITTED, CLEARANCE, REQUESTED, CLEARED):
183                return _(u'Acceptance Fee payments not allowed.'), None
184            if student.faccode == 'FP':
185                amount = academic_session.clearance_fee_fp
186            elif student.current_mode.endswith('_pt'):
187                if student.is_postgrad:
188                    amount = academic_session.clearance_fee_pg_pt
189                else:
190                    amount = academic_session.clearance_fee_ug_pt
191            elif student.faccode == 'FCS':
192                # Students in clinical medical sciences pay the medical
193                # acceptance fee
194                amount = academic_session.clearance_fee_med
195            elif student.is_postgrad:  # and not part-time
196                amount = academic_session.clearance_fee_pg
197            else:
198                amount = academic_session.clearance_fee
199            p_item = student['studycourse'].certificate.code
200            if amount in (0.0, None):
201                return _(u'Amount could not be determined.'), None
202            # Add Matric Gown Fee and Lapel Fee
203            if category == 'clearance_incl':
204                amount += gateway_net_amt(academic_session.matric_gown_fee) + \
205                    gateway_net_amt(academic_session.lapel_fee)
206        elif category == 'late_registration':
207            amount = academic_session.late_registration_fee
208        elif category.startswith('schoolfee'):
209            try:
210                certificate = student['studycourse'].certificate
211                p_item = certificate.code
212            except (AttributeError, TypeError):
213                return _('Study course data are incomplete.'), None
214            if student.state == CLEARED or category == 'schoolfee_2':
215                if student.is_foreigner:
216                    amount = getattr(certificate, 'school_fee_3', 0.0)
217                else:
218                    amount = getattr(certificate, 'school_fee_1', 0.0)
219                # Cut school fee by 50%
220                if category in ('schoolfee_1', 'schoolfee_2'):
221                    if amount:
222                        amount = gateway_net_amt(amount) / 2 + GATEWAY_AMT
223            elif category == 'schoolfee_1':
224                return _("Wrong state. Only students in state 'cleared' "
225                         "are allowed to pay by instalments."), None
226            elif student.state == RETURNING:
227                if student.is_postgrad and category == 'schoolfee_incl':
228                    return _("No additional fees required."), None
229                if not student.father_name:
230                    return _("Personal data form is not properly filled."), None
231                # In case of returning school fee payment the payment session
232                # and level contain the values of the session the student
233                # has paid for.
234                p_session, p_level = self.getReturningData(student)
235                try:
236                    academic_session = grok.getSite()[
237                        'configuration'][str(p_session)]
238                except KeyError:
239                    return _(u'Session configuration object is not available.'), None
240                if student.is_foreigner:
241                    amount = getattr(certificate, 'school_fee_4', 0.0)
242                else:
243                    amount = getattr(certificate, 'school_fee_2', 0.0)
244            else:
245                return _('Wrong state.'), None
246            if amount in (0.0, None):
247                return _(u'Amount could not be determined.'), None
248            # Add Student Union Fee and Welfare Assurance
249            if category in ('schoolfee_incl', 'schoolfee_1'):
250                amount += gateway_net_amt(academic_session.welfare_fee) + \
251                    gateway_net_amt(academic_session.union_fee)
252            # Add non-indigenous fee and session specific penalty fees
253            if student.is_postgrad:
254                amount += academic_session.penalty_pg
255                if not student.lga.startswith('edo'):
256                    amount += 20000.0
257            else:
258                amount += academic_session.penalty_ug
259        if amount in (0.0, None):
260            return _(u'Amount could not be determined.'), None
261
262        # Create ticket.
263        for key in student['payments'].keys():
264            ticket = student['payments'][key]
265            if ticket.p_state == 'paid' and\
266               ticket.p_category == category and \
267               ticket.p_item == p_item and \
268               ticket.p_session == p_session:
269                  return _('This type of payment has already been made.'), None
270        if self._isPaymentDisabled(p_session, category, student):
271            return _('Payment temporarily disabled.'), None
272        payment = createObject(u'waeup.StudentOnlinePayment')
273        timestamp = ("%d" % int(time()*10000))[1:]
274        payment.p_id = "p%s" % timestamp
275        payment.p_category = category
276        payment.p_item = p_item
277        payment.p_session = p_session
278        payment.p_level = p_level
279        payment.p_current = p_current
280        payment.amount_auth = amount
281        return None, payment
282
283    def _admissionText(self, student, portal_language):
284        inst_name = grok.getSite()['configuration'].name
285        entry_session = student['studycourse'].entry_session
286        entry_session = academic_sessions_vocab.getTerm(entry_session).title
287        text = trans(_(
288            'This is to inform you that you have been offered provisional'
289            ' admission into ${a} for the ${b} academic session as follows:',
290            mapping = {'a': inst_name, 'b': entry_session}),
291            portal_language)
292        return text
293
294    def maxCredits(self, studylevel):
295        """Return maximum credits.
296
297        """
298        return 48
299
300    def getBedCoordinates(self, bedticket):
301        """Return descriptive bed coordinates.
302        This method can be used to customize the `display_coordinates`
303        property method in order to  display a
304        customary description of the bed space.
305        """
306        bc = bedticket.bed_coordinates.split(',')
307        if len(bc) == 4:
308            return bc[0]
309        return bedticket.bed_coordinates
310
311    def getAccommodationDetails(self, student):
312        """Determine the accommodation data of a student.
313        """
314        d = {}
315        d['error'] = u''
316        hostels = grok.getSite()['hostels']
317        d['booking_session'] = hostels.accommodation_session
318        d['allowed_states'] = hostels.accommodation_states
319        d['startdate'] = hostels.startdate
320        d['enddate'] = hostels.enddate
321        d['expired'] = hostels.expired
322        # Determine bed type
323        bt = 'all'
324        if student.sex == 'f':
325            sex = 'female'
326        else:
327            sex = 'male'
328        special_handling = 'regular'
329        d['bt'] = u'%s_%s_%s' % (special_handling,sex,bt)
330        return d
331
332    # AAUE prefix
333    STUDENT_ID_PREFIX = u'E'
Note: See TracBrowser for help on using the repository browser.