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

Last change on this file since 17975 was 17975, checked in by Henrik Bettermann, 13 days ago

Fix secondinstall payments.

  • Property svn:keywords set to Id
File size: 38.9 KB
Line 
1## $Id: utils.py 17975 2024-12-12 13:40:34Z 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        elif student.is_jupeb:
496            try:
497                certificate = student['studycourse'].certificate
498                p_item = certificate.code
499            except (AttributeError, TypeError):
500                return _('Study course data are incomplete.'), None
501            if student.state == CLEARED and category == 'schoolfee':
502                amount = 220000.0
503            if student.state == CLEARED and category == 'schoolfee_1':
504                amount = 110000.0
505            elif category == 'secondinstall':
506                amount = 110000.0
507        elif category == 'schoolfee':
508            try:
509                certificate = student['studycourse'].certificate
510                p_item = certificate.code
511            except (AttributeError, TypeError):
512                return _('Study course data are incomplete.'), None
513            try:
514                if student.entry_session < 2017:
515                    schoolfees_dict = SCHOOLFEES[12][p_item]
516                elif student.entry_session < 2020:
517                    schoolfees_dict = SCHOOLFEES[17][p_item]
518                elif student.entry_session < 2022:
519                    schoolfees_dict = SCHOOLFEES[20][p_item]
520                elif student.entry_session < 2023:
521                    schoolfees_dict = SCHOOLFEES[22][p_item]
522                elif student.entry_session < 2024:
523                    schoolfees_dict = SCHOOLFEES[23][p_item]
524                else:
525                    schoolfees_dict = SCHOOLFEES[24][p_item]
526            except KeyError:
527                return _('School fee not yet fixed: p_item = %s' % p_item), None
528            if previous_session:
529                # Students can pay for previous sessions in all workflow states.
530                # Fresh students are excluded by the update method of the
531                # PreviousPaymentAddFormPage.
532                if previous_session == student['studycourse'].entry_session:
533                    if student.is_foreigner:
534                        amount = schoolfees_dict['initial_foreigner']
535                    else:
536                        amount = schoolfees_dict['initial']
537                else:
538                    if student.is_foreigner:
539                        amount = schoolfees_dict['returning_foreigner']
540                    else:
541                        amount = schoolfees_dict['returning']
542            else:
543                if student.state == CLEARED:
544                    if student.is_foreigner:
545                        amount = schoolfees_dict['initial_foreigner']
546                    else:
547                        amount = schoolfees_dict['initial']
548                elif student.state == PAID and student.is_postgrad:
549                    p_session += 1
550                    academic_session = self._getSessionConfiguration(p_session)
551                    if academic_session == None:
552                        return _(u'Session configuration object is not available.'), None
553                    if student.is_foreigner:
554                        amount = schoolfees_dict['returning_foreigner']
555                    else:
556                        amount = schoolfees_dict['returning']
557                elif student.state == RETURNING:
558                    # In case of returning school fee payment the payment session
559                    # and level contain the values of the session the student
560                    # has paid for.
561                    p_session, p_level = self.getReturningData(student)
562                    academic_session = self._getSessionConfiguration(p_session)
563                    if academic_session == None:
564                        return _(u'Session configuration object is not available.'), None
565
566                    # Students are only allowed to pay for the next session
567                    # if current session payment has really been made,
568                    # i.e. payment object exists and is paid.
569                    #if not self._paymentMade(
570                    #    student, student.current_session):
571                    #    return _('You have not yet paid your current/active' +
572                    #             ' session. Please use the previous session' +
573                    #             ' payment form first.'), None
574
575                    if student.is_foreigner:
576                        amount = schoolfees_dict['returning_foreigner']
577                    else:
578                        amount = schoolfees_dict['returning']
579                # PHARMD school fee amount is fixed and previously paid
580                # installments in current session are deducted.
581                if student.current_mode == 'special_ft' \
582                    and student.state in (RETURNING, CLEARED):
583                    if student.is_foreigner:
584                        amount = 260000.0 - self._pharmdInstallments(student)
585                    else:
586                        amount = 160000.0 - self._pharmdInstallments(student)
587            try:
588                amount = float(amount)
589            except ValueError:
590                return _(u'School fee not yet fixed: p_item = %s' % p_item), None
591            # Give 50% school fee discount to staff members.
592            if student.is_staff:
593                amount /= 2
594        else:
595            fee_name = category + '_fee'
596            amount = getattr(academic_session, fee_name, 0.0)
597        if amount in (0.0, None):
598            return _('Amount could not be determined.'), None
599        # Add session specific penalty fee.
600        if category == 'schoolfee' and student.is_postgrad:
601            amount += academic_session.penalty_pg
602            amount += academic_session.development_fee
603        elif category == 'schoolfee' and student.current_mode == ('ug_ft'):
604            amount += academic_session.penalty_ug_ft
605        elif category == 'schoolfee' and student.current_mode == ('ug_pt'):
606            amount += academic_session.penalty_ug_pt
607        elif category == 'schoolfee' and student.current_mode == ('ug_sw'):
608            amount += academic_session.penalty_sw
609        elif category == 'schoolfee' and student.current_mode in (
610            'dp_ft', 'dp_pt'):
611            amount += academic_session.penalty_dp
612        if category.startswith('tempmaint'):
613            p_item = getUtility(IKofaUtils).PAYMENT_CATEGORIES[category]
614            p_item = unicode(p_item)
615            # Now we change the category because tempmaint payments
616            # will be obsolete when Uniben returns to Kofa bed allocation.
617            category = 'hostel_maintenance'
618        # Create ticket.
619        if self.samePaymentMade(student, category, p_item, p_session):
620            return _('This type of payment has already been made.'), None
621        if self._isPaymentDisabled(p_session, category, student):
622            return _('This category of payments has been disabled.'), None
623        payment = createObject(u'waeup.StudentOnlinePayment')
624        timestamp = ("%d" % int(time()*10000))[1:]
625        payment.p_id = "p%s" % timestamp
626        payment.p_category = category
627        payment.p_item = p_item
628        payment.p_session = p_session
629        payment.p_level = p_level
630        payment.p_current = p_current
631        payment.amount_auth = amount
632        return None, payment
633
634    def warnCreditsOOR(self, studylevel, course=None):
635        studycourse = studylevel.__parent__
636        certificate = getattr(studycourse,'certificate', None)
637        current_level = studycourse.current_level
638        if None in (current_level, certificate):
639            return
640        if studylevel.__parent__.previous_verdict == 'R':
641            return
642        end_level = certificate.end_level
643        if studylevel.student.faccode in (
644            'MED', 'DEN', 'BMS') and studylevel.level == 200:
645            limit = 61
646        elif current_level >= end_level:
647            if studycourse.current_verdict == 'R':
648                limit = 999
649            else:
650                limit = 51
651        else:
652            limit = 50
653        if course and studylevel.total_credits + course.credits > limit:
654            return _('Maximum credits exceeded.')
655        elif studylevel.total_credits > limit:
656            return _('Maximum credits exceeded.')
657        return
658
659    def warnCourseAlreadyPassed(self, studylevel, course):
660        """Return message if course has already been passed at
661        previous levels.
662        """
663
664        # med: we may need to put this matter on hold and allow
665        # all the students to register.
666        return False
667
668        previous_verdict = studylevel.__parent__.previous_verdict
669        if previous_verdict in ('C', 'M'):
670            return False
671        for slevel in studylevel.__parent__.values():
672            for cticket in slevel.values():
673                if cticket.code == course.code \
674                    and cticket.total_score >= cticket.passmark:
675                    return _('Course has already been passed at previous level.')
676        return False
677
678    def clearance_disabled_message(self, student):
679        if student.is_postgrad:
680            return None
681        try:
682            session_config = grok.getSite()[
683                'configuration'][str(student.current_session)]
684        except KeyError:
685            return _('Session configuration object is not available.')
686        if not session_config.clearance_enabled:
687            return _('Clearance is disabled for this session.')
688        return None
689
690    def renderPDFTranscript(self, view, filename='transcript.pdf',
691                  student=None,
692                  studentview=None,
693                  note=None,
694                  signatures=(),
695                  sigs_in_footer=(),
696                  digital_sigs=(),
697                  show_scans=True, topMargin=1.5,
698                  omit_fields=(),
699                  tableheader=None,
700                  no_passport=False,
701                  save_file=False):
702        """Render pdf slip of a transcripts.
703        """
704        portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
705        # XXX: tell what the different parameters mean
706        style = getSampleStyleSheet()
707        creator = self.getPDFCreator(student)
708        data = []
709        doc_title = view.label
710        author = '%s (%s)' % (view.request.principal.title,
711                              view.request.principal.id)
712        footer_text = view.label.split('\n')
713        if len(footer_text) > 2:
714            # We can add a department in first line
715            footer_text = footer_text[1]
716        else:
717            # Only the first line is used for the footer
718            footer_text = footer_text[0]
719        if getattr(student, 'student_id', None) is not None:
720            footer_text = "%s - %s - " % (student.student_id, footer_text)
721
722        # Insert student data table
723        if student is not None:
724            #bd_translation = trans(_('Base Data'), portal_language)
725            #data.append(Paragraph(bd_translation, HEADING_STYLE))
726            data.append(render_student_data(
727                studentview, view.context,
728                omit_fields, lang=portal_language,
729                slipname=filename,
730                no_passport=no_passport))
731
732        transcript_data = view.context.getTranscriptData()
733        levels_data = transcript_data[0]
734
735        contextdata = []
736        f_label = trans(_('Course of Study:'), portal_language)
737        f_label = Paragraph(f_label, ENTRY1_STYLE)
738        f_text = formatted_text(view.context.certificate.longtitle)
739        f_text = Paragraph(f_text, ENTRY1_STYLE)
740        contextdata.append([f_label,f_text])
741
742        f_label = trans(_('Faculty:'), portal_language)
743        f_label = Paragraph(f_label, ENTRY1_STYLE)
744        f_text = formatted_text(
745            view.context.certificate.__parent__.__parent__.__parent__.longtitle)
746        f_text = Paragraph(f_text, ENTRY1_STYLE)
747        contextdata.append([f_label,f_text])
748
749        f_label = trans(_('Department:'), portal_language)
750        f_label = Paragraph(f_label, ENTRY1_STYLE)
751        f_text = formatted_text(
752            view.context.certificate.__parent__.__parent__.longtitle)
753        f_text = Paragraph(f_text, ENTRY1_STYLE)
754        contextdata.append([f_label,f_text])
755
756        f_label = trans(_('Entry Session:'), portal_language)
757        f_label = Paragraph(f_label, ENTRY1_STYLE)
758        f_text = formatted_text(
759            view.session_dict.get(view.context.entry_session))
760        f_text = Paragraph(f_text, ENTRY1_STYLE)
761        contextdata.append([f_label,f_text])
762
763        f_label = trans(_('Final Session:'), portal_language)
764        f_label = Paragraph(f_label, ENTRY1_STYLE)
765        f_text = formatted_text(
766            view.session_dict.get(view.context.current_session))
767        f_text = Paragraph(f_text, ENTRY1_STYLE)
768        contextdata.append([f_label,f_text])
769
770        f_label = trans(_('Entry Mode:'), portal_language)
771        f_label = Paragraph(f_label, ENTRY1_STYLE)
772        f_text = formatted_text(view.studymode_dict.get(
773            view.context.entry_mode))
774        f_text = Paragraph(f_text, ENTRY1_STYLE)
775        contextdata.append([f_label,f_text])
776
777        f_label = trans(_('Final Verdict:'), portal_language)
778        f_label = Paragraph(f_label, ENTRY1_STYLE)
779        f_text = formatted_text(view.studymode_dict.get(
780            view.context.current_verdict))
781        f_text = Paragraph(f_text, ENTRY1_STYLE)
782        contextdata.append([f_label,f_text])
783
784        f_label = trans(_('Cumulative GPA:'), portal_language)
785        f_label = Paragraph(f_label, ENTRY1_STYLE)
786        format_float = getUtility(IKofaUtils).format_float
787        cgpa = format_float(transcript_data[1], 3)
788        if student.state == GRADUATED:
789            f_text = formatted_text('%s (%s)' % (
790                cgpa, self.getClassFromCGPA(transcript_data[1], student)[1]))
791        else:
792            f_text = formatted_text('%s' % cgpa)
793            if not transcript_data[1]:
794                f_text = formatted_text('No results yet!')
795        f_text = Paragraph(f_text, ENTRY1_STYLE)
796        contextdata.append([f_label,f_text])
797
798        contexttable = Table(contextdata,style=SLIP_STYLE)
799        data.append(contexttable)
800
801        if transcript_data[1]:
802            transcripttables = render_transcript_data(
803                view, tableheader, levels_data, lang=portal_language)
804            data.extend(transcripttables)
805
806        # Insert signatures
807        # XXX: We are using only sigs_in_footer in waeup.kofa, so we
808        # do not have a test for the following lines.
809        if signatures and not sigs_in_footer:
810            data.append(Spacer(1, 20))
811            # Render one signature table per signature to
812            # get date and signature in line.
813            for signature in signatures:
814                signaturetables = get_signature_tables(signature)
815                data.append(signaturetables[0])
816
817        # Insert digital signatures
818        if digital_sigs:
819            data.append(Spacer(1, 20))
820            sigs = digital_sigs.split('\n')
821            for sig in sigs:
822                data.append(Paragraph(sig, NOTE_STYLE))
823
824        view.response.setHeader(
825            'Content-Type', 'application/pdf')
826        try:
827            pdf_stream = creator.create_pdf(
828                data, None, doc_title, author=author, footer=footer_text,
829                note=note, sigs_in_footer=sigs_in_footer, topMargin=topMargin,
830                view=view)
831        except IOError:
832            view.flash(_('Error in image file.'))
833            return view.redirect(view.url(view.context))
834        if save_file:
835            self._saveTranscriptPDF(student, pdf_stream)
836            return
837        return pdf_stream
838
839    #: A tuple containing the names of registration states in which changing of
840    #: passport pictures is allowed.
841    PORTRAIT_CHANGE_STATES = (CLEARANCE, ADMITTED, REQUESTED)
842
843    def allowPortraitChange(self, student):
844        """Check if student is allowed to change the portrait file.
845        """
846        if student.is_jupeb:
847            return False
848        if student.state not in self.PORTRAIT_CHANGE_STATES:
849            return False
850        return True
851
852    #: A tuple containing the names of registration states in which changing of
853    #: scanned signatures is allowed.
854    SIGNATURE_CHANGE_STATES = (CLEARED, RETURNING, PAID, REGISTERED, VALIDATED, )
855
856    def final_clearance_enabled(self, student):
857        studycourse = student['studycourse']
858        certificate = getattr(studycourse,'certificate',None)
859        current_level = studycourse.current_level
860        if None in (current_level, certificate):
861            return False
862        if student.is_postgrad:
863            return True
864        end_level = certificate.end_level
865        if current_level >= end_level:
866            return True
867        return False
868
869    # Uniben prefix
870    @property
871    def STUDENT_ID_PREFIX(self):
872        if grok.getSite().__name__ == 'uniben-cdl':
873            return u'C'
874        return u'B'
875
876    STUDENT_EXPORTER_NAMES = (
877            'students',
878            'studentstudycourses',
879            'studentstudycourses_1',
880            'studentstudylevels',
881            #'studentstudylevels_1',
882            'coursetickets',
883            #'coursetickets_1',
884            'studentpayments',
885            'bedtickets',
886            'trimmed',
887            'outstandingcourses',
888            'unpaidpayments',
889            'sfpaymentsoverview',
890            'sessionpaymentsoverview',
891            'studylevelsoverview',
892            'combocard',
893            'bursary',
894            'accommodationpayments',
895            'transcriptdata',
896            'trimmedpayments',
897            'medicalhistory',
898            'routingslip',
899            'nysc',
900            )
Note: See TracBrowser for help on using the repository browser.