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

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

Implement school fee payments by instalment.

  • Property svn:keywords set to Id
File size: 12.7 KB
RevLine 
[7419]1## $Id: utils.py 13512 2015-11-30 14:56:37Z 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
20from zope.component import createObject
[10922]21from waeup.kofa.interfaces import (
[13348]22    ADMITTED, CLEARED, RETURNING, PAID, academic_sessions_vocab)
[8823]23from kofacustom.nigeria.students.utils import NigeriaStudentsUtils
[8247]24from waeup.kofa.accesscodes import create_accesscode
[10922]25from waeup.kofa.students.utils import trans
[13414]26from waeup.aaue.interswitch.browser import gateway_net_amt
[8444]27from waeup.aaue.interfaces import MessageFactory as _
[6902]28
[8823]29class CustomStudentsUtils(NigeriaStudentsUtils):
[7151]30    """A collection of customized methods.
31
32    """
33
[13348]34    PORTRAIT_CHANGE_STATES = (ADMITTED, RETURNING)
35
[10641]36    gpa_boundaries = ((1, 'FRNS'),
37                      (1.5, 'Pass'),
38                      (2.4, '3rd Class Honours'),
39                      (3.5, '2nd Class Honours Lower Division'),
40                      (4.5, '2nd Class Honours Upper Division'),
41                      (5, '1st Class Honours'))
42
[13359]43    def increaseMatricInteger(self, student):
44        """Increase counter for matric numbers.
45        This counter can be a centrally stored attribute or an attribute of
46        faculties, departments or certificates. In the base package the counter
47        is as an attribute of the site configuration container.
48        """
49        if student.current_mode in ('ug_pt', 'de_pt'):
50            grok.getSite()['configuration'].next_matric_integer += 1
51            return
52        grok.getSite()['configuration'].next_matric_integer_2 += 1
53        return
54
[11596]55    def constructMatricNumber(self, student):
[11593]56        faccode = student.faccode
57        depcode = student.depcode
58        year = unicode(student.entry_session)[2:]
[13359]59        if not student.state in (PAID, ) or not student.is_fresh or \
60            student.current_mode == 'found':
61            return _('Matriculation number cannot be set.'), None
62        if student.current_mode in ('ug_pt', 'de_pt'):
63            next_integer = grok.getSite()['configuration'].next_matric_integer
64            if next_integer == 0:
65                return _('Matriculation number cannot be set.'), None
[12975]66            return None, "PTP/%s/%s/%s/%05d" % (
67                faccode, depcode, year, next_integer)
[13359]68        next_integer = grok.getSite()['configuration'].next_matric_integer_2
69        if next_integer == 0:
70            return _('Matriculation number cannot be set.'), None
71        if student.faccode in ('FBM', 'FCS'):
72            return None, "CMS/%s/%s/%s/%05d" % (
73                faccode, depcode, year, next_integer)
74        return None, "%s/%s/%s/%05d" % (faccode, depcode, year, next_integer)
[12975]75
[8270]76    def getReturningData(self, student):
77        """ This method defines what happens after school fee payment
[8319]78        of returning students depending on the student's senate verdict.
[8270]79        """
[8319]80        prev_level = student['studycourse'].current_level
81        cur_verdict = student['studycourse'].current_verdict
82        if cur_verdict in ('A','B','L','M','N','Z',):
83            # Successful student
84            new_level = divmod(int(prev_level),100)[0]*100 + 100
85        elif cur_verdict == 'C':
86            # Student on probation
87            new_level = int(prev_level) + 10
88        else:
89            # Student is somehow in an undefined state.
90            # Level has to be set manually.
91            new_level = prev_level
[8270]92        new_session = student['studycourse'].current_session + 1
93        return new_session, new_level
94
[13454]95    def _isPaymentDisabled(self, p_session, category, student):
96        academic_session = self._getSessionConfiguration(p_session)
97        if category == 'schoolfee' and \
98            'sf_all' in academic_session.payment_disabled:
99            return True
100        if category == 'hostel_maintenance' and \
101            'maint_all' in academic_session.payment_disabled:
102            return True
103        return False
104
[9154]105    def setPaymentDetails(self, category, student,
106            previous_session=None, previous_level=None):
[8600]107        """Create Payment object and set the payment data of a student for
108        the payment category specified.
109
110        """
[8306]111        details = {}
[8600]112        p_item = u''
113        amount = 0.0
114        error = u''
[9154]115        if previous_session:
116            return _('Previous session payment not yet implemented.'), None
[8600]117        p_session = student['studycourse'].current_session
118        p_level = student['studycourse'].current_level
[9154]119        p_current = True
[9527]120        academic_session = self._getSessionConfiguration(p_session)
121        if academic_session == None:
[8600]122            return _(u'Session configuration object is not available.'), None
[8677]123        # Determine fee.
[7151]124        if category == 'transfer':
[8600]125            amount = academic_session.transfer_fee
[10467]126        elif category == 'transcript':
127            amount = academic_session.transcript_fee
[7151]128        elif category == 'bed_allocation':
[8600]129            amount = academic_session.booking_fee
[7151]130        elif category == 'hostel_maintenance':
[13418]131            amount = 0.0
132            bedticket = student['accommodation'].get(
133                str(student.current_session), None)
[13502]134            if bedticket is not None and bedticket.bed is not None:
[13474]135                p_item = bedticket.display_coordinates
[13418]136                if bedticket.bed.__parent__.maint_fee > 0:
137                    amount = bedticket.bed.__parent__.maint_fee
138                else:
139                    # fallback
140                    amount = academic_session.maint_fee
141            else:
[13506]142                return _(u'No bed allocated.'), None
[13374]143        elif category == 'welfare':
144            amount = academic_session.welfare_fee
145        elif category == 'union':
146            amount = academic_session.union_fee
147        elif category == 'lapel':
148            amount = academic_session.lapel_fee
149        elif category == 'matric_gown':
150            amount = academic_session.matric_gown_fee
151        elif category == 'concessional':
[13464]152            amount = academic_session.concessional_fee
[13400]153        elif category.startswith('clearance'):
[11653]154            if student.faccode == 'FP':
155                amount = academic_session.clearance_fee_fp
[13377]156            elif student.current_mode.endswith('_pt'):
157                amount = academic_session.clearance_fee_pt
[13466]158            elif student.faccode == 'FCS':
159                # Students in clinical medical sciences pay the medical
160                # acceptance fee
[13377]161                amount = academic_session.clearance_fee_med
[11653]162            else:
163                amount = academic_session.clearance_fee
[8753]164            p_item = student['studycourse'].certificate.code
[13400]165            # Add Matric Gown Fee and Lapel Fee
[13410]166            if category.endswith('_incl'):
[13400]167                if amount is None:
168                    # Otherwise we can't add somtehing
169                    amount = 0.0
[13414]170                amount += gateway_net_amt(academic_session.matric_gown_fee) + \
171                    gateway_net_amt(academic_session.lapel_fee)
[11004]172        elif category == 'late_registration':
[13035]173            amount = academic_session.late_registration_fee
[13400]174        elif category.startswith('schoolfee'):
[8600]175            try:
[8753]176                certificate = student['studycourse'].certificate
177                p_item = certificate.code
[8600]178            except (AttributeError, TypeError):
179                return _('Study course data are incomplete.'), None
[13512]180            if student.state == CLEARED or category == 'schoolfee_2':
[13374]181                if student.is_foreigner:
182                    amount = getattr(certificate, 'school_fee_3', 0.0)
[10930]183                else:
[13374]184                    amount = getattr(certificate, 'school_fee_1', 0.0)
[13512]185                # Cut school fee by 50%
186                if category in ('schoolfee_1', 'schoolfee_2'):
187                    amount = amount / 2
188            elif category == 'schoolfee_1':
189                return _("Wrong state. Only students in state 'cleared' "
190                         "are allowed to pay by instalments."), None
[13374]191            elif student.state == RETURNING:
[13482]192                if not student.father_name:
193                    return _("Personal data form is not properly filled."), None
[13374]194                # In case of returning school fee payment the payment session
195                # and level contain the values of the session the student
196                # has paid for.
197                p_session, p_level = self.getReturningData(student)
[8961]198                try:
199                    academic_session = grok.getSite()[
200                        'configuration'][str(p_session)]
201                except KeyError:
202                    return _(u'Session configuration object is not available.'), None
[13374]203                if student.is_foreigner:
204                    amount = getattr(certificate, 'school_fee_4', 0.0)
205                else:
206                    amount = getattr(certificate, 'school_fee_2', 0.0)
[8600]207            else:
[8753]208                return _('Wrong state.'), None
[13417]209            if amount in (0.0, None):
210                return _(u'Amount could not be determined.'), None
[13400]211            # Add Student Union Fee and Welfare Assurance
[13512]212            if category in ('schoolfee_incl', 'schoolfee_1'):
[13414]213                amount += gateway_net_amt(academic_session.welfare_fee) + \
214                    gateway_net_amt(academic_session.union_fee)
[8600]215        if amount in (0.0, None):
216            return _(u'Amount could not be determined.'), None
[8677]217        # Add session specific penalty fee.
[13405]218        if category.startswith('schoolfee') and student.is_postgrad:
[8677]219            amount += academic_session.penalty_pg
[13405]220        elif category.startswith('schoolfee'):
[8677]221            amount += academic_session.penalty_ug
222        # Create ticket.
[8600]223        for key in student['payments'].keys():
224            ticket = student['payments'][key]
225            if ticket.p_state == 'paid' and\
226               ticket.p_category == category and \
227               ticket.p_item == p_item and \
228               ticket.p_session == p_session:
229                  return _('This type of payment has already been made.'), None
[11455]230        if self._isPaymentDisabled(p_session, category, student):
231            return _('Payment temporarily disabled.'), None
[8712]232        payment = createObject(u'waeup.StudentOnlinePayment')
[8954]233        timestamp = ("%d" % int(time()*10000))[1:]
[8600]234        payment.p_id = "p%s" % timestamp
235        payment.p_category = category
236        payment.p_item = p_item
237        payment.p_session = p_session
238        payment.p_level = p_level
[9154]239        payment.p_current = p_current
[8600]240        payment.amount_auth = amount
241        return None, payment
[7621]242
[10922]243    def _admissionText(self, student, portal_language):
244        inst_name = grok.getSite()['configuration'].name
245        entry_session = student['studycourse'].entry_session
246        entry_session = academic_sessions_vocab.getTerm(entry_session).title
247        text = trans(_(
[10953]248            'This is to inform you that you have been offered provisional'
249            ' admission into ${a} for the ${b} academic session as follows:',
[10922]250            mapping = {'a': inst_name, 'b': entry_session}),
251            portal_language)
252        return text
253
[10051]254    def maxCredits(self, studylevel):
255        """Return maximum credits.
256
257        """
258        return 48
259
[13353]260    def getBedCoordinates(self, bedticket):
261        """Return descriptive bed coordinates.
262        This method can be used to customize the `display_coordinates`
263        property method in order to  display a
264        customary description of the bed space.
265        """
266        bc = bedticket.bed_coordinates.split(',')
267        if len(bc) == 4:
268            return bc[0]
269        return bedticket.bed_coordinates
270
[13415]271    def getAccommodationDetails(self, student):
272        """Determine the accommodation data of a student.
273        """
274        d = {}
275        d['error'] = u''
276        hostels = grok.getSite()['hostels']
277        d['booking_session'] = hostels.accommodation_session
278        d['allowed_states'] = hostels.accommodation_states
279        d['startdate'] = hostels.startdate
280        d['enddate'] = hostels.enddate
281        d['expired'] = hostels.expired
282        # Determine bed type
[13416]283        bt = 'all'
[13415]284        if student.sex == 'f':
285            sex = 'female'
286        else:
287            sex = 'male'
288        special_handling = 'regular'
289        d['bt'] = u'%s_%s_%s' % (special_handling,sex,bt)
290        return d
291
[8444]292    # AAUE prefix
293    STUDENT_ID_PREFIX = u'E'
Note: See TracBrowser for help on using the repository browser.