source: main/waeup.uniben/trunk/src/waeup/uniben/students/utils.py @ 13459

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

Re-enable maintenance payments.

  • Property svn:keywords set to Id
File size: 17.5 KB
Line 
1## $Id: utils.py 13450 2015-11-13 05:43:18Z 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, getUtility
21from waeup.kofa.interfaces import (IKofaUtils,
22    CLEARED, RETURNING, PAID, REGISTERED, VALIDATED)
23from waeup.kofa.utils.helpers import to_timezone
24from waeup.kofa.students.utils import trans
25from kofacustom.nigeria.students.utils import NigeriaStudentsUtils
26from waeup.uniben.interfaces import MessageFactory as _
27
28class CustomStudentsUtils(NigeriaStudentsUtils):
29    """A collection of customized methods.
30
31    """
32
33    def getReturningData(self, student):
34        """ This method defines what happens after school fee payment
35        of returning students depending on the student's senate verdict.
36        """
37        prev_level = student['studycourse'].current_level
38        cur_verdict = student['studycourse'].current_verdict
39        if cur_verdict in ('A','B','L','M','N','Z',):
40            # Successful student
41            new_level = divmod(int(prev_level),100)[0]*100 + 100
42        elif cur_verdict == 'C':
43            # Student on probation
44            new_level = int(prev_level) + 10
45        else:
46            # Student is somehow in an undefined state.
47            # Level has to be set manually.
48            new_level = prev_level
49        new_session = student['studycourse'].current_session + 1
50        return new_session, new_level
51
52
53    def checkAccommodationRequirements(self, student, acc_details):
54        if acc_details.get('expired', False):
55            startdate = acc_details.get('startdate')
56            enddate = acc_details.get('enddate')
57            if startdate and enddate:
58                tz = getUtility(IKofaUtils).tzinfo
59                startdate = to_timezone(
60                    startdate, tz).strftime("%d/%m/%Y %H:%M:%S")
61                enddate = to_timezone(
62                    enddate, tz).strftime("%d/%m/%Y %H:%M:%S")
63                return _("Outside booking period: ${a} - ${b}",
64                         mapping = {'a': startdate, 'b': enddate})
65            else:
66                return _("Outside booking period.")
67        if student.current_mode != 'ug_ft':
68            return _("Only undergraduate full-time students are eligible to book accommodation.")
69        bt = acc_details.get('bt')
70        if not bt:
71            return _("Your data are incomplete.")
72        if not student.state in acc_details['allowed_states']:
73            return _("You are in the wrong registration state.")
74        if student['studycourse'].current_session != acc_details[
75            'booking_session']:
76            return _('Your current session does not '
77                     'match accommodation session.')
78        stage = bt.split('_')[2]
79        if stage not in ('fr', 'fi'):
80            return _("Only fresh and final year students are allowed to book accommodation.")
81
82        ####################################################################################
83        if stage == 'fi':
84            return _("Accommodation booking for final year students has not yet started.")
85        ####################################################################################
86
87
88        if stage != 'fr' and not student['studycourse'].previous_verdict in ('A', 'B'):
89            return _("Your are not eligible to book accommodation.")
90        if str(acc_details['booking_session']) in student['accommodation'].keys():
91            return _('You already booked a bed space in '
92                     'current accommodation session.')
93        return
94
95    def getAccommodationDetails(self, student):
96        """Determine the accommodation data of a student.
97        """
98        d = {}
99        d['error'] = u''
100        hostels = grok.getSite()['hostels']
101        d['booking_session'] = hostels.accommodation_session
102        d['allowed_states'] = hostels.accommodation_states
103        d['startdate'] = hostels.startdate
104        d['enddate'] = hostels.enddate
105        d['expired'] = hostels.expired
106        # Determine bed type
107        studycourse = student['studycourse']
108        certificate = getattr(studycourse,'certificate',None)
109        entry_session = studycourse.entry_session
110        current_level = studycourse.current_level
111        if None in (entry_session, current_level, certificate):
112            return d
113        end_level = certificate.end_level
114        if current_level == 10:
115            bt = 'pr'
116        elif entry_session == grok.getSite()['hostels'].accommodation_session:
117            bt = 'fr'
118        elif current_level >= end_level:
119            bt = 'fi'
120        else:
121            bt = 're'
122        if student.sex == 'f':
123            sex = 'female'
124        else:
125            sex = 'male'
126        special_handling = 'regular'
127        if student.faccode in ('MED', 'DEN'):
128            special_handling = 'clinical'
129        elif student.certcode in ('BARTMAS', 'MARTTHR', 'BARTFAA',
130                                  'BAEDFAA', 'BSCEDECHED'):
131            special_handling = 'ekenwan'
132        d['bt'] = u'%s_%s_%s' % (special_handling,sex,bt)
133        return d
134
135    def _paymentMade(self, student, session):
136        if len(student['payments']):
137            for ticket in student['payments'].values():
138                if ticket.p_state == 'paid' and \
139                    ticket.p_category == 'schoolfee' and \
140                    ticket.p_session == session:
141                    return True
142        return False
143
144    #def _hostelApplicationPaymentMade(self, student, session):
145    #    if len(student['payments']):
146    #        for ticket in student['payments'].values():
147    #            if ticket.p_state == 'paid' and \
148    #                ticket.p_category == 'hostel_application' and \
149    #                ticket.p_session == session:
150    #                return True
151    #    return False
152
153    def setPaymentDetails(self, category, student,
154            previous_session, previous_level):
155        """Create Payment object and set the payment data of a student for
156        the payment category specified.
157
158        """
159        p_item = u''
160        amount = 0.0
161        if previous_session:
162            if previous_session < student['studycourse'].entry_session:
163                return _('The previous session must not fall below '
164                         'your entry session.'), None
165            if category == 'schoolfee':
166                # School fee is always paid for the following session
167                if previous_session > student['studycourse'].current_session:
168                    return _('This is not a previous session.'), None
169            else:
170                if previous_session > student['studycourse'].current_session - 1:
171                    return _('This is not a previous session.'), None
172            p_session = previous_session
173            p_level = previous_level
174            p_current = False
175        else:
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':
186            amount = academic_session.transcript_fee
187        elif category == 'gown':
188            amount = academic_session.gown_fee
189        elif category == 'bed_allocation':
190            p_item = self.getAccommodationDetails(student)['bt']
191            amount = academic_session.booking_fee
192            # Add student union dues
193            stage = self.getAccommodationDetails(student)['bt']
194            stage = stage.split('_')[2]
195
196
197            #####################################################
198            if stage == 'fi':
199                return _('Payment temporarily disabled.'), None
200            #####################################################
201
202
203            if stage == 'fr':
204                amount += 500.0
205            elif stage == 'fi' and student[
206                'studycourse'].previous_verdict in ('A', 'B'):
207                amount += 300.0
208            else:
209                amount = 0.0
210        elif category == 'hostel_maintenance':
211            amount = 0.0
212            bedticket = student['accommodation'].get(
213                str(student.current_session), None)
214            if bedticket:
215                p_item = bedticket.bed_coordinates
216                if bedticket.bed.__parent__.maint_fee > 0:
217                    amount = bedticket.bed.__parent__.maint_fee
218                else:
219                    # fallback
220                    amount = academic_session.maint_fee
221            else:
222                # Should not happen because this is already checked
223                # in the browser module, but anyway ...
224                portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
225                p_item = trans(_('no bed allocated'), portal_language)
226
227        #elif category == 'hostel_application':
228        #    amount = 1000.0
229        #elif category.startswith('tempmaint'):
230        #    if not self._hostelApplicationPaymentMade(
231        #        student, student.current_session):
232        #        return _(
233        #            'You have not yet paid the hostel application fee.'), None
234        #    if category == 'tempmaint_1':
235        #        amount = 8150.0
236        #    elif category == 'tempmaint_2':
237        #        amount = 12650.0
238        #    elif category == 'tempmaint_3':
239        #        amount = 9650.0
240        elif category == 'clearance':
241            p_item = student.certcode
242            if p_item is None:
243                return _('Study course data are incomplete.'), None
244            if student.faccode == 'JUPEB':
245                return _('No payment required.'), None
246            if student.faccode == 'FCETA':
247                amount = 22500.0
248            elif p_item in ('BSCANA', 'BSCMBC', 'BMLS', 'BSCNUR', 'BSCPHS', 'BDS',
249                'MBBSMED', 'MBBSNDU'):
250                amount = 65000.0
251            elif p_item in ('BEDCET', 'BIOEDCET', 'CHMEDCET', 'ISEDCET',
252                'MTHEDCET', 'PHYEDCET', 'ITECET', 'AGREDCET', 'HEEDCET'):
253                amount = 22500.0
254            else:
255                amount = 45000.0
256        elif category == 'schoolfee':
257            try:
258                certificate = student['studycourse'].certificate
259                p_item = certificate.code
260            except (AttributeError, TypeError):
261                return _('Study course data are incomplete.'), None
262
263            #####################################################
264            #if student.faccode == 'JUPEB':
265            #    return _('Payment temporarily disabled.'), None
266            #####################################################
267
268
269            if previous_session:
270                # Students can pay for previous sessions in all workflow states.
271                # Fresh students are excluded by the update method of the
272                # PreviousPaymentAddFormPage.
273                if previous_session == student['studycourse'].entry_session:
274                    if student.is_foreigner:
275                        amount = getattr(certificate, 'school_fee_3', 0.0)
276                    else:
277                        amount = getattr(certificate, 'school_fee_1', 0.0)
278                else:
279                    if student.is_foreigner:
280                        amount = getattr(certificate, 'school_fee_4', 0.0)
281                    else:
282                        amount = getattr(certificate, 'school_fee_2', 0.0)
283            else:
284                if student.state == CLEARED:
285                    if student.is_foreigner:
286                        amount = getattr(certificate, 'school_fee_3', 0.0)
287                    else:
288                        amount = getattr(certificate, 'school_fee_1', 0.0)
289                elif student.state in (PAID, REGISTERED, VALIDATED):
290                    p_session += 1
291                    # We don't know which level the student is paying for.
292                    p_level = None
293                    academic_session = self._getSessionConfiguration(p_session)
294                    if academic_session == None:
295                        return _(u'Session configuration object is not available.'), None
296
297                    # Students are only allowed to pay for the next session
298                    # if current session payment
299                    # has really been made, i.e. payment object exists.
300                    #if not self._paymentMade(
301                    #    student, student.current_session):
302                    #    return _('You have not yet paid your current/active' +
303                    #             ' session. Please use the previous session' +
304                    #             ' payment form first.'), None
305
306                    if student.is_foreigner:
307                        amount = getattr(certificate, 'school_fee_4', 0.0)
308                    else:
309                        amount = getattr(certificate, 'school_fee_2', 0.0)
310                elif student.state == RETURNING:
311                    # In case of returning school fee payment the payment session
312                    # and level contain the values of the session the student
313                    # has paid for.
314                    p_session, p_level = self.getReturningData(student)
315                    academic_session = self._getSessionConfiguration(p_session)
316                    if academic_session == None:
317                        return _(u'Session configuration object is not available.'), None
318
319                    # Students are only allowed to pay for the next session
320                    # if current session payment has really been made,
321                    # i.e. payment object exists and is paid.
322                    #if not self._paymentMade(
323                    #    student, student.current_session):
324                    #    return _('You have not yet paid your current/active' +
325                    #             ' session. Please use the previous session' +
326                    #             ' payment form first.'), None
327
328                    if student.is_foreigner:
329                        amount = getattr(certificate, 'school_fee_4', 0.0)
330                    else:
331                        amount = getattr(certificate, 'school_fee_2', 0.0)
332            # Give 50% school fee discount to staff members.
333            if student.is_staff:
334                amount /= 2
335        if amount in (0.0, None):
336            return _('Amount could not be determined.'), None
337        # Add session specific penalty fee.
338        if category == 'schoolfee' and student.is_postgrad:
339            amount += academic_session.penalty_pg
340        elif category == 'schoolfee':
341            amount += academic_session.penalty_ug
342        if category.startswith('tempmaint'):
343            p_item = getUtility(IKofaUtils).PAYMENT_CATEGORIES[category]
344            p_item = unicode(p_item)
345            # Now we change the category because tempmaint payments
346            # will be obsolete when Uniben returns to Kofa bed allocation.
347            category = 'hostel_maintenance'
348        # Create ticket.
349        if self.samePaymentMade(student, category, p_item, p_session):
350            return _('This type of payment has already been made.'), None
351        if self._isPaymentDisabled(p_session, category, student):
352            return _('Payment temporarily disabled.'), None
353        payment = createObject(u'waeup.StudentOnlinePayment')
354        timestamp = ("%d" % int(time()*10000))[1:]
355        payment.p_id = "p%s" % timestamp
356        payment.p_category = category
357        payment.p_item = p_item
358        payment.p_session = p_session
359        payment.p_level = p_level
360        payment.p_current = p_current
361        payment.amount_auth = amount
362        return None, payment
363
364    def maxCredits(self, studylevel):
365        """Return maximum credits.
366
367        """
368        studycourse = studylevel.__parent__
369        certificate = getattr(studycourse,'certificate', None)
370        current_level = studycourse.current_level
371        if None in (current_level, certificate):
372            return 0
373        end_level = certificate.end_level
374        if current_level >= end_level:
375            return 51
376        return 50
377
378    def clearance_disabled_message(self, student):
379        if student.is_postgrad:
380            return None
381        try:
382            session_config = grok.getSite()[
383                'configuration'][str(student.current_session)]
384        except KeyError:
385            return _('Session configuration object is not available.')
386        if not session_config.clearance_enabled:
387            return _('Clearance is disabled for this session.')
388        return None
389
390    def selectBed(self, available_beds):
391        """Select a bed from a list of available beds.
392        Beds are sorted by the sort id of the hostel and the bed number.
393        The first bed found in this sorted list is taken.
394        """
395        sorted_beds = sorted(available_beds,
396            key=lambda bed: 1000 * bed.__parent__.sort_id + bed.bed_number)
397        return sorted_beds[0]
398
399    # Uniben prefix
400    STUDENT_ID_PREFIX = u'B'
Note: See TracBrowser for help on using the repository browser.