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

Last change on this file since 17963 was 17963, checked in by Henrik Bettermann, 4 weeks ago

Let JUPEB students pay by instalments.

  • Property svn:keywords set to Id
File size: 38.9 KB
Line 
1## $Id: utils.py 17963 2024-11-26 02:34:19Z 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
19import os
20import csv
21from time import time
22from reportlab.platypus import Paragraph, Table
23from zope.component import createObject, getUtility
24from reportlab.lib.styles import getSampleStyleSheet
25from waeup.kofa.browser.pdf import ENTRY1_STYLE
26from waeup.kofa.interfaces import (IKofaUtils, ADMITTED, CLEARANCE,
27    CLEARED, REQUESTED, RETURNING, PAID, REGISTERED, VALIDATED, GRADUATED)
28from waeup.kofa.utils.helpers import to_timezone
29from waeup.kofa.students.utils import (
30    trans, render_student_data, formatted_text, render_transcript_data,
31    SLIP_STYLE)
32from kofacustom.nigeria.students.utils import NigeriaStudentsUtils
33from waeup.uniben.interfaces import MessageFactory as _
34
35SCHOOLFEES = dict()
36
37schoolfees_path = os.path.join(
38    os.path.dirname(__file__), 'schoolfees_24.csv')
39reader = csv.DictReader(open(schoolfees_path, 'rb'))
40SCHOOLFEES[24] = {line['code']: {item[0]:item[1] for item in line.items()}
41    for line in reader}
42
43schoolfees_path = os.path.join(
44    os.path.dirname(__file__), 'schoolfees_23.csv')
45reader = csv.DictReader(open(schoolfees_path, 'rb'))
46SCHOOLFEES[23] = {line['code']: {item[0]:item[1] for item in line.items()}
47    for line in reader}
48
49schoolfees_path = os.path.join(
50    os.path.dirname(__file__), 'schoolfees_22.csv')
51reader = csv.DictReader(open(schoolfees_path, 'rb'))
52SCHOOLFEES[22] = {line['code']: {item[0]:item[1] for item in line.items()}
53    for line in reader}
54
55
56schoolfees_path = os.path.join(
57    os.path.dirname(__file__), 'schoolfees_20.csv')
58reader = csv.DictReader(open(schoolfees_path, 'rb'))
59SCHOOLFEES[20] = {line['code']: {item[0]:item[1] for item in line.items()}
60    for line in reader}
61
62
63schoolfees_path = os.path.join(
64    os.path.dirname(__file__), 'schoolfees_17.csv')
65reader = csv.DictReader(open(schoolfees_path, 'rb'))
66SCHOOLFEES[17] = {line['code']: {item[0]:item[1] for item in line.items()}
67    for line in reader}
68
69schoolfees_path = os.path.join(
70    os.path.dirname(__file__), 'schoolfees_12.csv')
71reader = csv.DictReader(open(schoolfees_path, 'rb'))
72SCHOOLFEES[12] = {line['code']: {item[0]:item[1] for item in line.items()}
73    for line in reader}
74
75class CustomStudentsUtils(NigeriaStudentsUtils):
76    """A collection of customized methods.
77
78    """
79
80    SEPARATORS_DICT = {
81        'form.fst_sit_fname': _(u'First Sitting Record'),
82        'form.scd_sit_fname': _(u'Second Sitting Record'),
83        'form.alr_fname': _(u'Advanced Level Record'),
84        'form.hq_type': _(u'Higher Education Record'),
85        'form.hq2_type': _(u'Second Higher Education Record'),
86        'form.nysc_year': _(u'NYSC Information'),
87        'form.employer': _(u'Employment History'),
88        'form.former_matric': _(u'Former Student'),
89        'form.fever': _(u'History of Symptoms'),
90        'form.asthma': _(u'Medical History'),
91        'form.lagos_abuja': _(u'Travel History'),
92        'form.company_suspected': _(u'History of Contact/Infection'),
93        'form.genotype': _(u'TISHIP Registration Form Data'),
94        }
95
96    def getReturningData(self, student):
97        """ This method defines what happens after school fee payment
98        of returning students depending on the student's senate verdict.
99        """
100        prev_level = student['studycourse'].current_level
101        cur_verdict = student['studycourse'].current_verdict
102        if cur_verdict == 'N' and prev_level in (100, 200):
103            new_level = prev_level
104        elif cur_verdict in ('A','B','M','N','Z',):
105            # Successful student
106            new_level = divmod(int(prev_level),100)[0]*100 + 100
107        elif cur_verdict in ('C', 'L',):
108            # Student on probation
109            new_level = int(prev_level) + 10
110        else:
111            # Student is somehow in an undefined state.
112            # Level has to be set manually.
113            new_level = prev_level
114        new_session = student['studycourse'].current_session + 1
115        return new_session, new_level
116
117    ACCOMMODATION_SPAN = 5
118
119    def checkAccommodationRequirements(self, student, acc_details):
120        if acc_details.get('expired', False):
121            startdate = acc_details.get('startdate')
122            enddate = acc_details.get('enddate')
123            if startdate and enddate:
124                tz = getUtility(IKofaUtils).tzinfo
125                startdate = to_timezone(
126                    startdate, tz).strftime("%d/%m/%Y %H:%M:%S")
127                enddate = to_timezone(
128                    enddate, tz).strftime("%d/%m/%Y %H:%M:%S")
129                return _("Outside booking period: ${a} - ${b}",
130                         mapping = {'a': startdate, 'b': enddate})
131            else:
132                return _("Outside booking period.")
133        if not student.is_postgrad and student.current_mode != 'ug_ft':
134            return _("Only undergraduate full-time students are eligible to book accommodation.")
135        bt = acc_details.get('bt')
136        if not bt:
137            return _("Your data are incomplete.")
138        if not student.state in acc_details['allowed_states']:
139            return _("You are in the wrong registration state.")
140        #if student['studycourse'].current_session != acc_details[
141        #    'booking_session']:
142        #    return _('Your current session does not '
143        #             'match accommodation session.')
144        if  acc_details['booking_session'] - student[
145            'studycourse'].current_session > self.ACCOMMODATION_SPAN:
146            return _('Your current session does not allow ' + \
147                    'to book accommodation.')
148        stage = bt.split('_')[2]
149        if not student.is_postgrad and stage != 'fr' and not student[
150            'studycourse'].previous_verdict in (
151                'A', 'B', 'F', 'J', 'L', 'M', 'C', 'Z'):
152            return _("Your are not eligible to book accommodation.")
153        bsession = str(acc_details['booking_session'])
154        if bsession in student['accommodation'].keys() \
155            and not 'booking expired' in \
156            student['accommodation'][bsession].bed_coordinates:
157            return _('You already booked a bed space in '
158                     'current accommodation session.')
159        return
160
161    def getAccommodationDetails(self, student):
162        """Determine the accommodation data of a student.
163        """
164        d = {}
165        d['error'] = u''
166        hostels = grok.getSite()['hostels']
167        d['booking_session'] = hostels.accommodation_session
168        d['allowed_states'] = hostels.accommodation_states
169        d['startdate'] = hostels.startdate
170        d['enddate'] = hostels.enddate
171        d['expired'] = hostels.expired
172        # Determine bed type
173        studycourse = student['studycourse']
174        certificate = getattr(studycourse,'certificate',None)
175        entry_session = studycourse.entry_session
176        current_level = studycourse.current_level
177        if None in (entry_session, current_level, certificate):
178            return d
179        if student.sex == 'f':
180            sex = 'female'
181        else:
182            sex = 'male'
183        if student.is_postgrad:
184            bt = 'all'
185            special_handling = 'pg'
186        else:
187            end_level = certificate.end_level
188            if current_level == 10:
189                bt = 'pr'
190            elif entry_session == grok.getSite()['hostels'].accommodation_session:
191                bt = 'fr'
192            elif current_level >= end_level:
193                bt = 'fi'
194            else:
195                bt = 're'
196            special_handling = 'regular'
197            desired_hostel = student['accommodation'].desired_hostel
198            if student.faccode in ('MED', 'DEN') and (
199                not desired_hostel or desired_hostel.startswith('clinical')):
200                special_handling = 'clinical'
201            elif student.certcode in ('BARTMAS', 'BARTTHR', 'BARTFAA',
202                                      'BAEDFAA', 'BSCEDECHED', 'BAFAA',
203                                      'BARTMUS', 'BSCEDECHED', 'BEDECHEDU'):
204                special_handling = 'ekenwan'
205        d['bt'] = u'%s_%s_%s' % (special_handling,sex,bt)
206        return d
207
208    def _paymentMade(self, student, session):
209        if len(student['payments']):
210            for ticket in student['payments'].values():
211                if ticket.p_state == 'paid' and \
212                    ticket.p_category == 'schoolfee' and \
213                    ticket.p_session == session:
214                    return True
215        return False
216
217    def _isPaymentDisabled(self, p_session, category, student):
218        academic_session = self._getSessionConfiguration(p_session)
219        if category == 'schoolfee':
220            if 'sf_all' in academic_session.payment_disabled:
221                return True
222            if student.state == RETURNING and \
223                'sf_return' in academic_session.payment_disabled:
224                return True
225            if student.current_mode == 'found' and \
226                'sf_found' in academic_session.payment_disabled:
227                return True
228            if student.is_postgrad:
229                if 'sf_pg' in academic_session.payment_disabled:
230                    return True
231                return False
232            if student.current_mode.endswith('ft') and \
233                'sf_ft' in academic_session.payment_disabled:
234                return True
235            if student.current_mode.endswith('pt') and \
236                'sf_pt' in academic_session.payment_disabled:
237                return True
238            if student.current_mode.startswith('dp') and \
239                'sf_dp' in academic_session.payment_disabled:
240                return True
241            if student.current_mode.endswith('sw') and \
242                'sf_sw' in academic_session.payment_disabled:
243                return True
244        if category == 'hostel_maintenance' and \
245            'maint_all' in academic_session.payment_disabled:
246            return True
247        if category == 'clearance':
248            if 'cl_all' in academic_session.payment_disabled:
249                return True
250            if student.is_jupeb and \
251                'cl_jupeb' in academic_session.payment_disabled:
252                return True
253            if not student.is_jupeb and \
254                'cl_allexj' in academic_session.payment_disabled:
255                return True
256        return False
257
258    #def _hostelApplicationPaymentMade(self, student, session):
259    #    if len(student['payments']):
260    #        for ticket in student['payments'].values():
261    #            if ticket.p_state == 'paid' and \
262    #                ticket.p_category == 'hostel_application' and \
263    #                ticket.p_session == session:
264    #                return True
265    #    return False
266
267    def _pharmdInstallments(self, student):
268        installments = 0.0
269        if len(student['payments']):
270            for ticket in student['payments'].values():
271                if ticket.p_state == 'paid' and \
272                    ticket.p_category.startswith('pharmd') and \
273                    ticket.p_session == student.current_session:
274                    installments += ticket.amount_auth
275        return installments
276
277    def samePaymentMade(self, student, category, p_item, p_session):
278        if category in ('bed_allocation', 'transcript'):
279            return False
280        for key in student['payments'].keys():
281            ticket = student['payments'][key]
282            if ticket.p_state == 'paid' and\
283               ticket.p_category == category and \
284               ticket.p_item == p_item and \
285               ticket.p_session == p_session:
286                  return True
287        return False
288
289    def setCDLPaymentDetails(self, category, student,
290            previous_session, previous_level, combi):
291        """Create Payment object and set the payment data of a CDL student for
292        the payment category specified.
293        """
294        p_item = u''
295        amount = 0.0
296        if previous_session:
297            if previous_session < student['studycourse'].entry_session:
298                return _('The previous session must not fall below '
299                         'your entry session.'), None
300            if category == 'schoolfee':
301                # School fee is always paid for the following session
302                if previous_session > student['studycourse'].current_session:
303                    return _('This is not a previous session.'), None
304            else:
305                if previous_session > student['studycourse'].current_session - 1:
306                    return _('This is not a previous session.'), None
307            p_session = previous_session
308            p_level = previous_level
309            p_current = False
310        else:
311            p_session = student['studycourse'].current_session
312            p_level = student['studycourse'].current_level
313            p_current = True
314        academic_session = self._getSessionConfiguration(p_session)
315        if academic_session == None:
316            return _(u'Session configuration object is not available.'), None
317        # Determine fee.
318        if category == 'schoolfee':
319            try:
320                certificate = student['studycourse'].certificate
321                p_item = certificate.code
322            except (AttributeError, TypeError):
323                return _('Study course data are incomplete.'), None
324            if previous_session:
325                # Students can pay for previous sessions in all
326                # workflow states.  Fresh students are excluded by the
327                # update method of the PreviousPaymentAddFormPage.
328                if previous_level == 100:
329                    amount = getattr(certificate, 'school_fee_1', 0.0)
330                else:
331                    amount = getattr(certificate, 'school_fee_2', 0.0)
332            else:
333                if student.state == CLEARED:
334                    amount = getattr(certificate, 'school_fee_1', 0.0)
335                elif student.state == RETURNING:
336                    # In case of returning school fee payment the
337                    # payment session and level contain the values of
338                    # the session the student has paid for. Payment
339                    # session is always next session.
340                    p_session, p_level = self.getReturningData(student)
341                    academic_session = self._getSessionConfiguration(p_session)
342                    if academic_session == None:
343                        return _(
344                            u'Session configuration object is not available.'
345                            ), None
346                    amount = getattr(certificate, 'school_fee_2', 0.0)
347                elif student.is_postgrad and student.state == PAID:
348                    # Returning postgraduate students also pay for the
349                    # next session but their level always remains the
350                    # same.
351                    p_session += 1
352                    academic_session = self._getSessionConfiguration(p_session)
353                    if academic_session == None:
354                        return _(
355                            u'Session configuration object is not available.'
356                            ), None
357                    amount = getattr(certificate, 'school_fee_2', 0.0)
358        elif category == 'clearance':
359            try:
360                p_item = student['studycourse'].certificate.code
361            except (AttributeError, TypeError):
362                return _('Study course data are incomplete.'), None
363            amount = academic_session.clearance_fee
364        elif category == 'combi' and combi:
365            categories = getUtility(IKofaUtils).COMBI_PAYMENT_CATEGORIES
366            for cat in combi:
367                fee_name = cat + '_fee'
368                cat_amount = getattr(academic_session, fee_name, 0.0)
369                if not cat_amount:
370                    return _('%s undefined.' % categories[cat]), None
371                amount += cat_amount
372                p_item += u'%s + ' % categories[cat]
373            p_item = p_item.strip(' + ')
374        else:
375            fee_name = category + '_fee'
376            amount = getattr(academic_session, fee_name, 0.0)
377        if amount in (0.0, None):
378            return _('Amount could not be determined.'), None
379        if self.samePaymentMade(student, category, p_item, p_session):
380            return _('This type of payment has already been made.'), None
381        if self._isPaymentDisabled(p_session, category, student):
382            return _('This category of payments has been disabled.'), None
383        payment = createObject(u'waeup.StudentOnlinePayment')
384        timestamp = ("%d" % int(time()*10000))[1:]
385        payment.p_id = "p%s" % timestamp
386        payment.p_category = category
387        payment.p_item = p_item
388        payment.p_session = p_session
389        payment.p_level = p_level
390        payment.p_current = p_current
391        payment.amount_auth = amount
392        payment.p_combi = combi
393        return None, payment
394
395
396    def setPaymentDetails(self, category, student,
397            previous_session, previous_level, combi):
398        """Create Payment object and set the payment data of a student for
399        the payment category specified.
400
401        """
402        if grok.getSite().__name__ == 'uniben-cdl':
403            return self.setCDLPaymentDetails(category, student,
404                previous_session, previous_level, combi)
405        p_item = u''
406        amount = 0.0
407        if previous_session:
408            if previous_session < student['studycourse'].entry_session:
409                return _('The previous session must not fall below '
410                         'your entry session.'), None
411            if category == 'schoolfee':
412                # School fee is always paid for the following session
413                if previous_session > student['studycourse'].current_session:
414                    return _('This is not a previous session.'), None
415            else:
416                if previous_session > student['studycourse'].current_session - 1:
417                    return _('This is not a previous session.'), None
418            p_session = previous_session
419            p_level = previous_level
420            p_current = False
421        else:
422            p_session = student['studycourse'].current_session
423            p_level = student['studycourse'].current_level
424            p_current = True
425        academic_session = self._getSessionConfiguration(p_session)
426        if academic_session == None:
427            return _(u'Session configuration object is not available.'), None
428        # Determine fee.
429        if category.startswith('pharmd') \
430            and student.current_mode == 'special_ft':
431            amount = 80000.0
432        elif category == 'plag_test':
433            amount = 2500.0
434            if student.is_postgrad:
435                amount = 5000.0
436        #elif category == 'develop' and student.is_postgrad:
437        #    amount = academic_session.development_fee
438        elif category == 'bed_allocation':
439            acco_details = self.getAccommodationDetails(student)
440            p_session = acco_details['booking_session']
441            p_item = acco_details['bt']
442            desired_hostel = student['accommodation'].desired_hostel
443            if not desired_hostel:
444                return _(u'Select your favoured hostel first.'), None
445            if desired_hostel and desired_hostel != 'no':
446                p_item = u'%s (%s)' % (p_item, desired_hostel)
447            amount = academic_session.booking_fee
448            if student.is_postgrad:
449                amount += 500
450        elif category == 'hostel_maintenance':
451            amount = 0.0
452            booking_session = grok.getSite()['hostels'].accommodation_session
453            bedticket = student['accommodation'].get(str(booking_session), None)
454            if bedticket is not None and bedticket.bed is not None:
455                p_item = bedticket.bed_coordinates
456                p_session = booking_session
457                if bedticket.bed.__parent__.maint_fee > 0:
458                    amount = bedticket.bed.__parent__.maint_fee
459                else:
460                    # fallback
461                    amount = academic_session.maint_fee
462            else:
463                return _(u'No bed allocated.'), None
464        #elif category == 'hostel_application':
465        #    amount = 1000.0
466        #elif category.startswith('tempmaint'):
467        #    if not self._hostelApplicationPaymentMade(
468        #        student, student.current_session):
469        #        return _(
470        #            'You have not yet paid the hostel application fee.'), None
471        #    if category == 'tempmaint_1':
472        #        amount = 8150.0
473        #    elif category == 'tempmaint_2':
474        #        amount = 12650.0
475        #    elif category == 'tempmaint_3':
476        #        amount = 9650.0
477        elif category == 'clearance':
478            p_item = student.certcode
479            if p_item is None:
480                return _('Study course data are incomplete.'), None
481            if student.is_jupeb:
482                amount = 50000.0
483            elif student.faccode.startswith('FCETA'):
484                # ASABA and AKOKA
485                amount = 35000.0
486            elif student.depcode == 'DMIC':
487                amount = 70000.0
488            elif student.faccode in ('BMS', 'MED', 'DEN') \
489                and not student.is_postgrad:
490                amount = 80000.0
491            elif student.faccode == 'DCOEM':
492                return _('Acceptance fee payment not necessary.'), None
493            else:
494                amount = 60000.0
495
496        elif student.state == CLEARED \
497                and student.is_jupeb and category in ('schoolfee', 'schoolfee_1',
498                                                      'secondinstall'):
499            try:
500                certificate = student['studycourse'].certificate
501                p_item = certificate.code
502            except (AttributeError, TypeError):
503                return _('Study course data are incomplete.'), None
504            amount = 220000.0
505            if category in ('schoolfee_1', 'secondinstall'):
506                amount /= 2
507
508        elif category == 'schoolfee':
509            try:
510                certificate = student['studycourse'].certificate
511                p_item = certificate.code
512            except (AttributeError, TypeError):
513                return _('Study course data are incomplete.'), None
514            try:
515                if student.entry_session < 2017:
516                    schoolfees_dict = SCHOOLFEES[12][p_item]
517                elif student.entry_session < 2020:
518                    schoolfees_dict = SCHOOLFEES[17][p_item]
519                elif student.entry_session < 2022:
520                    schoolfees_dict = SCHOOLFEES[20][p_item]
521                elif student.entry_session < 2023:
522                    schoolfees_dict = SCHOOLFEES[22][p_item]
523                elif student.entry_session < 2024:
524                    schoolfees_dict = SCHOOLFEES[23][p_item]
525                else:
526                    schoolfees_dict = SCHOOLFEES[24][p_item]
527            except KeyError:
528                return _('School fee not yet fixed: p_item = %s' % p_item), None
529            if previous_session:
530                # Students can pay for previous sessions in all workflow states.
531                # Fresh students are excluded by the update method of the
532                # PreviousPaymentAddFormPage.
533                if previous_session == student['studycourse'].entry_session:
534                    if student.is_foreigner:
535                        amount = schoolfees_dict['initial_foreigner']
536                    else:
537                        amount = schoolfees_dict['initial']
538                else:
539                    if student.is_foreigner:
540                        amount = schoolfees_dict['returning_foreigner']
541                    else:
542                        amount = schoolfees_dict['returning']
543            else:
544                if student.state == CLEARED:
545                    if student.is_foreigner:
546                        amount = schoolfees_dict['initial_foreigner']
547                    else:
548                        amount = schoolfees_dict['initial']
549                elif student.state == PAID and student.is_postgrad:
550                    p_session += 1
551                    academic_session = self._getSessionConfiguration(p_session)
552                    if academic_session == None:
553                        return _(u'Session configuration object is not available.'), None
554                    if student.is_foreigner:
555                        amount = schoolfees_dict['returning_foreigner']
556                    else:
557                        amount = schoolfees_dict['returning']
558                elif student.state == RETURNING:
559                    # In case of returning school fee payment the payment session
560                    # and level contain the values of the session the student
561                    # has paid for.
562                    p_session, p_level = self.getReturningData(student)
563                    academic_session = self._getSessionConfiguration(p_session)
564                    if academic_session == None:
565                        return _(u'Session configuration object is not available.'), None
566
567                    # Students are only allowed to pay for the next session
568                    # if current session payment has really been made,
569                    # i.e. payment object exists and is paid.
570                    #if not self._paymentMade(
571                    #    student, student.current_session):
572                    #    return _('You have not yet paid your current/active' +
573                    #             ' session. Please use the previous session' +
574                    #             ' payment form first.'), None
575
576                    if student.is_foreigner:
577                        amount = schoolfees_dict['returning_foreigner']
578                    else:
579                        amount = schoolfees_dict['returning']
580                # PHARMD school fee amount is fixed and previously paid
581                # installments in current session are deducted.
582                if student.current_mode == 'special_ft' \
583                    and student.state in (RETURNING, CLEARED):
584                    if student.is_foreigner:
585                        amount = 260000.0 - self._pharmdInstallments(student)
586                    else:
587                        amount = 160000.0 - self._pharmdInstallments(student)
588            try:
589                amount = float(amount)
590            except ValueError:
591                return _(u'School fee not yet fixed: p_item = %s' % p_item), None
592            # Give 50% school fee discount to staff members.
593            if student.is_staff:
594                amount /= 2
595        else:
596            fee_name = category + '_fee'
597            amount = getattr(academic_session, fee_name, 0.0)
598        if amount in (0.0, None):
599            return _('Amount could not be determined.'), None
600        # Add session specific penalty fee.
601        if category == 'schoolfee' and student.is_postgrad:
602            amount += academic_session.penalty_pg
603            amount += academic_session.development_fee
604        elif category == 'schoolfee' and student.current_mode == ('ug_ft'):
605            amount += academic_session.penalty_ug_ft
606        elif category == 'schoolfee' and student.current_mode == ('ug_pt'):
607            amount += academic_session.penalty_ug_pt
608        elif category == 'schoolfee' and student.current_mode == ('ug_sw'):
609            amount += academic_session.penalty_sw
610        elif category == 'schoolfee' and student.current_mode in (
611            'dp_ft', 'dp_pt'):
612            amount += academic_session.penalty_dp
613        if category.startswith('tempmaint'):
614            p_item = getUtility(IKofaUtils).PAYMENT_CATEGORIES[category]
615            p_item = unicode(p_item)
616            # Now we change the category because tempmaint payments
617            # will be obsolete when Uniben returns to Kofa bed allocation.
618            category = 'hostel_maintenance'
619        # Create ticket.
620        if self.samePaymentMade(student, category, p_item, p_session):
621            return _('This type of payment has already been made.'), None
622        if self._isPaymentDisabled(p_session, category, student):
623            return _('This category of payments has been disabled.'), None
624        payment = createObject(u'waeup.StudentOnlinePayment')
625        timestamp = ("%d" % int(time()*10000))[1:]
626        payment.p_id = "p%s" % timestamp
627        payment.p_category = category
628        payment.p_item = p_item
629        payment.p_session = p_session
630        payment.p_level = p_level
631        payment.p_current = p_current
632        payment.amount_auth = amount
633        return None, payment
634
635    def warnCreditsOOR(self, studylevel, course=None):
636        studycourse = studylevel.__parent__
637        certificate = getattr(studycourse,'certificate', None)
638        current_level = studycourse.current_level
639        if None in (current_level, certificate):
640            return
641        if studylevel.__parent__.previous_verdict == 'R':
642            return
643        end_level = certificate.end_level
644        if studylevel.student.faccode in (
645            'MED', 'DEN', 'BMS') and studylevel.level == 200:
646            limit = 61
647        elif current_level >= end_level:
648            if studycourse.current_verdict == 'R':
649                limit = 999
650            else:
651                limit = 51
652        else:
653            limit = 50
654        if course and studylevel.total_credits + course.credits > limit:
655            return _('Maximum credits exceeded.')
656        elif studylevel.total_credits > limit:
657            return _('Maximum credits exceeded.')
658        return
659
660    def warnCourseAlreadyPassed(self, studylevel, course):
661        """Return message if course has already been passed at
662        previous levels.
663        """
664
665        # med: we may need to put this matter on hold and allow
666        # all the students to register.
667        return False
668
669        previous_verdict = studylevel.__parent__.previous_verdict
670        if previous_verdict in ('C', 'M'):
671            return False
672        for slevel in studylevel.__parent__.values():
673            for cticket in slevel.values():
674                if cticket.code == course.code \
675                    and cticket.total_score >= cticket.passmark:
676                    return _('Course has already been passed at previous level.')
677        return False
678
679    def clearance_disabled_message(self, student):
680        if student.is_postgrad:
681            return None
682        try:
683            session_config = grok.getSite()[
684                'configuration'][str(student.current_session)]
685        except KeyError:
686            return _('Session configuration object is not available.')
687        if not session_config.clearance_enabled:
688            return _('Clearance is disabled for this session.')
689        return None
690
691    def renderPDFTranscript(self, view, filename='transcript.pdf',
692                  student=None,
693                  studentview=None,
694                  note=None,
695                  signatures=(),
696                  sigs_in_footer=(),
697                  digital_sigs=(),
698                  show_scans=True, topMargin=1.5,
699                  omit_fields=(),
700                  tableheader=None,
701                  no_passport=False,
702                  save_file=False):
703        """Render pdf slip of a transcripts.
704        """
705        portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
706        # XXX: tell what the different parameters mean
707        style = getSampleStyleSheet()
708        creator = self.getPDFCreator(student)
709        data = []
710        doc_title = view.label
711        author = '%s (%s)' % (view.request.principal.title,
712                              view.request.principal.id)
713        footer_text = view.label.split('\n')
714        if len(footer_text) > 2:
715            # We can add a department in first line
716            footer_text = footer_text[1]
717        else:
718            # Only the first line is used for the footer
719            footer_text = footer_text[0]
720        if getattr(student, 'student_id', None) is not None:
721            footer_text = "%s - %s - " % (student.student_id, footer_text)
722
723        # Insert student data table
724        if student is not None:
725            #bd_translation = trans(_('Base Data'), portal_language)
726            #data.append(Paragraph(bd_translation, HEADING_STYLE))
727            data.append(render_student_data(
728                studentview, view.context,
729                omit_fields, lang=portal_language,
730                slipname=filename,
731                no_passport=no_passport))
732
733        transcript_data = view.context.getTranscriptData()
734        levels_data = transcript_data[0]
735
736        contextdata = []
737        f_label = trans(_('Course of Study:'), portal_language)
738        f_label = Paragraph(f_label, ENTRY1_STYLE)
739        f_text = formatted_text(view.context.certificate.longtitle)
740        f_text = Paragraph(f_text, ENTRY1_STYLE)
741        contextdata.append([f_label,f_text])
742
743        f_label = trans(_('Faculty:'), portal_language)
744        f_label = Paragraph(f_label, ENTRY1_STYLE)
745        f_text = formatted_text(
746            view.context.certificate.__parent__.__parent__.__parent__.longtitle)
747        f_text = Paragraph(f_text, ENTRY1_STYLE)
748        contextdata.append([f_label,f_text])
749
750        f_label = trans(_('Department:'), portal_language)
751        f_label = Paragraph(f_label, ENTRY1_STYLE)
752        f_text = formatted_text(
753            view.context.certificate.__parent__.__parent__.longtitle)
754        f_text = Paragraph(f_text, ENTRY1_STYLE)
755        contextdata.append([f_label,f_text])
756
757        f_label = trans(_('Entry Session:'), portal_language)
758        f_label = Paragraph(f_label, ENTRY1_STYLE)
759        f_text = formatted_text(
760            view.session_dict.get(view.context.entry_session))
761        f_text = Paragraph(f_text, ENTRY1_STYLE)
762        contextdata.append([f_label,f_text])
763
764        f_label = trans(_('Final Session:'), portal_language)
765        f_label = Paragraph(f_label, ENTRY1_STYLE)
766        f_text = formatted_text(
767            view.session_dict.get(view.context.current_session))
768        f_text = Paragraph(f_text, ENTRY1_STYLE)
769        contextdata.append([f_label,f_text])
770
771        f_label = trans(_('Entry Mode:'), portal_language)
772        f_label = Paragraph(f_label, ENTRY1_STYLE)
773        f_text = formatted_text(view.studymode_dict.get(
774            view.context.entry_mode))
775        f_text = Paragraph(f_text, ENTRY1_STYLE)
776        contextdata.append([f_label,f_text])
777
778        f_label = trans(_('Final Verdict:'), portal_language)
779        f_label = Paragraph(f_label, ENTRY1_STYLE)
780        f_text = formatted_text(view.studymode_dict.get(
781            view.context.current_verdict))
782        f_text = Paragraph(f_text, ENTRY1_STYLE)
783        contextdata.append([f_label,f_text])
784
785        f_label = trans(_('Cumulative GPA:'), portal_language)
786        f_label = Paragraph(f_label, ENTRY1_STYLE)
787        format_float = getUtility(IKofaUtils).format_float
788        cgpa = format_float(transcript_data[1], 3)
789        if student.state == GRADUATED:
790            f_text = formatted_text('%s (%s)' % (
791                cgpa, self.getClassFromCGPA(transcript_data[1], student)[1]))
792        else:
793            f_text = formatted_text('%s' % cgpa)
794            if not transcript_data[1]:
795                f_text = formatted_text('No results yet!')
796        f_text = Paragraph(f_text, ENTRY1_STYLE)
797        contextdata.append([f_label,f_text])
798
799        contexttable = Table(contextdata,style=SLIP_STYLE)
800        data.append(contexttable)
801
802        if transcript_data[1]:
803            transcripttables = render_transcript_data(
804                view, tableheader, levels_data, lang=portal_language)
805            data.extend(transcripttables)
806
807        # Insert signatures
808        # XXX: We are using only sigs_in_footer in waeup.kofa, so we
809        # do not have a test for the following lines.
810        if signatures and not sigs_in_footer:
811            data.append(Spacer(1, 20))
812            # Render one signature table per signature to
813            # get date and signature in line.
814            for signature in signatures:
815                signaturetables = get_signature_tables(signature)
816                data.append(signaturetables[0])
817
818        # Insert digital signatures
819        if digital_sigs:
820            data.append(Spacer(1, 20))
821            sigs = digital_sigs.split('\n')
822            for sig in sigs:
823                data.append(Paragraph(sig, NOTE_STYLE))
824
825        view.response.setHeader(
826            'Content-Type', 'application/pdf')
827        try:
828            pdf_stream = creator.create_pdf(
829                data, None, doc_title, author=author, footer=footer_text,
830                note=note, sigs_in_footer=sigs_in_footer, topMargin=topMargin,
831                view=view)
832        except IOError:
833            view.flash(_('Error in image file.'))
834            return view.redirect(view.url(view.context))
835        if save_file:
836            self._saveTranscriptPDF(student, pdf_stream)
837            return
838        return pdf_stream
839
840    #: A tuple containing the names of registration states in which changing of
841    #: passport pictures is allowed.
842    PORTRAIT_CHANGE_STATES = (CLEARANCE, ADMITTED, REQUESTED)
843
844    def allowPortraitChange(self, student):
845        """Check if student is allowed to change the portrait file.
846        """
847        if student.is_jupeb:
848            return False
849        if student.state not in self.PORTRAIT_CHANGE_STATES:
850            return False
851        return True
852
853    #: A tuple containing the names of registration states in which changing of
854    #: scanned signatures is allowed.
855    SIGNATURE_CHANGE_STATES = (CLEARED, RETURNING, PAID, REGISTERED, VALIDATED, )
856
857    def final_clearance_enabled(self, student):
858        studycourse = student['studycourse']
859        certificate = getattr(studycourse,'certificate',None)
860        current_level = studycourse.current_level
861        if None in (current_level, certificate):
862            return False
863        if student.is_postgrad:
864            return True
865        end_level = certificate.end_level
866        if current_level >= end_level:
867            return True
868        return False
869
870    # Uniben prefix
871    @property
872    def STUDENT_ID_PREFIX(self):
873        if grok.getSite().__name__ == 'uniben-cdl':
874            return u'C'
875        return u'B'
876
877    STUDENT_EXPORTER_NAMES = (
878            'students',
879            'studentstudycourses',
880            'studentstudycourses_1',
881            'studentstudylevels',
882            #'studentstudylevels_1',
883            'coursetickets',
884            #'coursetickets_1',
885            'studentpayments',
886            'bedtickets',
887            'trimmed',
888            'outstandingcourses',
889            'unpaidpayments',
890            'sfpaymentsoverview',
891            'sessionpaymentsoverview',
892            'studylevelsoverview',
893            'combocard',
894            'bursary',
895            'accommodationpayments',
896            'transcriptdata',
897            'trimmedpayments',
898            'medicalhistory',
899            'routingslip',
900            'nysc',
901            )
Note: See TracBrowser for help on using the repository browser.