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

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

Part-time students are not allowed to pay by instalments.

  • Property svn:keywords set to Id
File size: 15.3 KB
Line 
1## $Id: utils.py 13780 2016-03-17 07:07:58Z 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 category in ('schoolfee_1', 'schoolfee_2'):
215                if student.current_mode == 'ug_pt':
216                    return _("Part-time students are not allowed "
217                             "to pay by instalments."), None
218                if category == 'schoolfee_1' and student.state != CLEARED:
219                    return _("Wrong state. Only students in state 'cleared' "
220                             "are allowed to pay by instalments."), None
221            if student.state == CLEARED or category == 'schoolfee_2':
222                if student.is_foreigner:
223                    amount = getattr(certificate, 'school_fee_3', 0.0)
224                else:
225                    amount = getattr(certificate, 'school_fee_1', 0.0)
226                # Cut school fee by 50%
227                if category in ('schoolfee_1', 'schoolfee_2'):
228                    if amount:
229                        amount = gateway_net_amt(amount) / 2 + GATEWAY_AMT
230            elif student.state == RETURNING:
231                if student.is_postgrad and category == 'schoolfee_incl':
232                    return _("No additional fees required."), None
233                if not student.father_name:
234                    return _("Personal data form is not properly filled."), None
235                # In case of returning school fee payment the payment session
236                # and level contain the values of the session the student
237                # has paid for.
238                p_session, p_level = self.getReturningData(student)
239                try:
240                    academic_session = grok.getSite()[
241                        'configuration'][str(p_session)]
242                except KeyError:
243                    return _(u'Session configuration object is not available.'), None
244                if student.is_foreigner:
245                    amount = getattr(certificate, 'school_fee_4', 0.0)
246                else:
247                    amount = getattr(certificate, 'school_fee_2', 0.0)
248            else:
249                return _('Wrong state.'), None
250            if amount in (0.0, None):
251                return _(u'Amount could not be determined.'), None
252            # Add Student Union Fee and Welfare Assurance
253            if category in ('schoolfee_incl', 'schoolfee_1'):
254                amount += gateway_net_amt(academic_session.welfare_fee) + \
255                    gateway_net_amt(academic_session.union_fee)
256            # Add non-indigenous fee and session specific penalty fees
257            if student.is_postgrad:
258                amount += academic_session.penalty_pg
259                if not student.lga.startswith('edo'):
260                    amount += 20000.0
261            else:
262                amount += academic_session.penalty_ug
263        if amount in (0.0, None):
264            return _(u'Amount could not be determined.'), None
265
266        # Create ticket.
267        for key in student['payments'].keys():
268            ticket = student['payments'][key]
269            if ticket.p_state == 'paid' and\
270               ticket.p_category == category and \
271               ticket.p_item == p_item and \
272               ticket.p_session == p_session:
273                  return _('This type of payment has already been made.'), None
274        if self._isPaymentDisabled(p_session, category, student):
275            return _('Payment temporarily disabled.'), None
276        payment = createObject(u'waeup.StudentOnlinePayment')
277        timestamp = ("%d" % int(time()*10000))[1:]
278        payment.p_id = "p%s" % timestamp
279        payment.p_category = category
280        payment.p_item = p_item
281        payment.p_session = p_session
282        payment.p_level = p_level
283        payment.p_current = p_current
284        payment.amount_auth = amount
285        return None, payment
286
287    def _admissionText(self, student, portal_language):
288        inst_name = grok.getSite()['configuration'].name
289        entry_session = student['studycourse'].entry_session
290        entry_session = academic_sessions_vocab.getTerm(entry_session).title
291        text = trans(_(
292            'This is to inform you that you have been offered provisional'
293            ' admission into ${a} for the ${b} academic session as follows:',
294            mapping = {'a': inst_name, 'b': entry_session}),
295            portal_language)
296        return text
297
298    def maxCredits(self, studylevel):
299        """Return maximum credits.
300
301        """
302        return 48
303
304    def getBedCoordinates(self, bedticket):
305        """Return descriptive bed coordinates.
306        This method can be used to customize the `display_coordinates`
307        property method in order to  display a
308        customary description of the bed space.
309        """
310        bc = bedticket.bed_coordinates.split(',')
311        if len(bc) == 4:
312            return bc[0]
313        return bedticket.bed_coordinates
314
315    def getAccommodationDetails(self, student):
316        """Determine the accommodation data of a student.
317        """
318        d = {}
319        d['error'] = u''
320        hostels = grok.getSite()['hostels']
321        d['booking_session'] = hostels.accommodation_session
322        d['allowed_states'] = hostels.accommodation_states
323        d['startdate'] = hostels.startdate
324        d['enddate'] = hostels.enddate
325        d['expired'] = hostels.expired
326        # Determine bed type
327        bt = 'all'
328        if student.sex == 'f':
329            sex = 'female'
330        else:
331            sex = 'male'
332        special_handling = 'regular'
333        d['bt'] = u'%s_%s_%s' % (special_handling,sex,bt)
334        return d
335
336    def checkAccommodationRequirements(self, student, acc_details):
337        super(CustomStudentsUtils, self).checkAccommodationRequirements(
338            student, acc_details)
339        if student.current_mode not in ('ug_ft', 'de_ft', 'mug_ft', 'mde_ft'):
340            return _('You are not eligible to book accommodation.')
341        return
342
343    # AAUE prefix
344    STUDENT_ID_PREFIX = u'E'
Note: See TracBrowser for help on using the repository browser.