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

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

Allow foundation programme students only to pay 'schoolfee', 'clearance', 'late_registration' fees.

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