source: main/kofacustom.iuokada/trunk/src/kofacustom/iuokada/students/utils.py @ 15947

Last change on this file since 15947 was 15937, checked in by Henrik Bettermann, 5 years ago

Implement multiple resit fee payment.

  • Property svn:keywords set to Id
File size: 9.4 KB
Line 
1## $Id: utils.py 15937 2020-01-17 15:23:05Z 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    ADMITTED, CLEARANCE, CLEARED, RETURNING, PAID, REGISTERED, VALIDATED)
23from kofacustom.nigeria.students.utils import NigeriaStudentsUtils
24from kofacustom.iuokada.interfaces import MessageFactory as _
25
26class CustomStudentsUtils(NigeriaStudentsUtils):
27    """A collection of customized methods.
28
29    """
30
31    # refix
32    STUDENT_ID_PREFIX = u'I'
33
34    SKIP_UPLOAD_VIEWLETS = (
35        'acceptanceletterupload', 'certificateupload'
36        )
37    # Maximum size of upload files in kB
38    MAX_KB = 500
39
40    #: A tuple containing the names of registration states in which changing of
41    #: passport pictures is allowed.
42    PORTRAIT_CHANGE_STATES = (ADMITTED, CLEARANCE,)
43
44    def warnCreditsOOR(self, studylevel, course=None):
45        """Return message if credits are out of range. In the base
46        package only maximum credits is set.
47        """
48        if course and studylevel.total_credits + course.credits > 60:
49            return _('Maximum credits exceeded.')
50        elif studylevel.total_credits > 60:
51            return _('Maximum credits exceeded.')
52        return
53
54    def setPaymentDetails(self, category, student,
55            previous_session, previous_level, combi=[]):
56        """Create a payment ticket and set the payment data of a
57        student for the payment category specified.
58        """
59        p_item = u''
60        amount = 0.0
61        if previous_session:
62            if previous_session < student['studycourse'].entry_session:
63                return _('The previous session must not fall below '
64                         'your entry session.'), None
65            if category == 'schoolfee':
66                # School fee is always paid for the following session
67                if previous_session > student['studycourse'].current_session:
68                    return _('This is not a previous session.'), None
69            else:
70                if previous_session > student['studycourse'].current_session - 1:
71                    return _('This is not a previous session.'), None
72            p_session = previous_session
73            p_level = previous_level
74            p_current = False
75        else:
76            p_session = student['studycourse'].current_session
77            p_level = student['studycourse'].current_level
78            p_current = True
79        academic_session = self._getSessionConfiguration(p_session)
80        if academic_session == None:
81            return _(u'Session configuration object is not available.'), None
82        # Determine fee.
83        if category in ('schoolfee', 'schoolfee40',
84                        'secondinstal'):
85            try:
86                certificate = student['studycourse'].certificate
87                p_item = certificate.code
88            except (AttributeError, TypeError):
89                return _('Study course data are incomplete.'), None
90            if previous_session:
91                # Students can pay for previous sessions in all
92                # workflow states.  Fresh students are excluded by the
93                # update method of the PreviousPaymentAddFormPage.
94                if previous_level == 100:
95                    amount = getattr(certificate, 'school_fee_1', 0.0)
96                else:
97                    amount = getattr(certificate, 'school_fee_2', 0.0)
98                if category == 'schoolfee40':
99                    amount = 0.4*amount
100                elif category == 'secondinstal':
101                    amount = 0.6*amount
102            else:
103                if category == 'secondinstal':
104                    if student.is_fresh:
105                        amount = 0.6 * getattr(certificate, 'school_fee_1', 0.0)
106                    else:
107                        amount = 0.6 * getattr(certificate, 'school_fee_2', 0.0)
108                else:
109                    if student.state == CLEARED:
110                        amount = getattr(certificate, 'school_fee_1', 0.0)
111                    elif student.state == RETURNING:
112                        # In case of returning school fee payment the
113                        # payment session and level contain the values of
114                        # the session the student has paid for. Payment
115                        # session is always next session.
116                        p_session, p_level = self.getReturningData(student)
117                        academic_session = self._getSessionConfiguration(p_session)
118                        if academic_session == None:
119                            return _(
120                                u'Session configuration object is not available.'
121                                ), None
122                        amount = getattr(certificate, 'school_fee_2', 0.0)
123                    elif student.is_postgrad and student.state == PAID:
124                        # Returning postgraduate students also pay for the
125                        # next session but their level always remains the
126                        # same.
127                        p_session += 1
128                        academic_session = self._getSessionConfiguration(p_session)
129                        if academic_session == None:
130                            return _(
131                                u'Session configuration object is not available.'
132                                ), None
133                        amount = getattr(certificate, 'school_fee_2', 0.0)
134                    if amount and category == 'schoolfee40':
135                        amount = 0.4*amount
136        elif category == 'clearance':
137            try:
138                p_item = student['studycourse'].certificate.code
139            except (AttributeError, TypeError):
140                return _('Study course data are incomplete.'), None
141            amount = academic_session.clearance_fee
142        elif category.startswith('resit'):
143            amount = academic_session.resit_fee
144            number = int(category.strip('resit'))
145            amount *= number
146        #elif category == 'bed_allocation':
147        #    p_item = self.getAccommodationDetails(student)['bt']
148        #    amount = academic_session.booking_fee
149        #elif category == 'hostel_maintenance':
150        #    amount = 0.0
151        #    bedticket = student['accommodation'].get(
152        #        str(student.current_session), None)
153        #    if bedticket is not None and bedticket.bed is not None:
154        #        p_item = bedticket.bed_coordinates
155        #        if bedticket.bed.__parent__.maint_fee > 0:
156        #            amount = bedticket.bed.__parent__.maint_fee
157        #        else:
158        #            # fallback
159        #            amount = academic_session.maint_fee
160        #    else:
161        #        return _(u'No bed allocated.'), None
162        elif category == 'combi' and combi:
163            categories = getUtility(IKofaUtils).COMBI_PAYMENT_CATEGORIES
164            for cat in combi:
165                fee_name = cat + '_fee'
166                cat_amount = getattr(academic_session, fee_name, 0.0)
167                if not cat_amount:
168                    return _('%s undefined.' % categories[cat]), None
169                amount += cat_amount
170                p_item += u'%s + ' % categories[cat]
171            p_item = p_item.strip(' + ')
172        else:
173            fee_name = category + '_fee'
174            amount = getattr(academic_session, fee_name, 0.0)
175        if amount in (0.0, None):
176            return _('Amount could not be determined.'), None
177        if self.samePaymentMade(student, category, p_item, p_session):
178            return _('This type of payment has already been made.'), None
179        if self._isPaymentDisabled(p_session, category, student):
180            return _('This category of payments has been disabled.'), None
181        payment = createObject(u'waeup.StudentOnlinePayment')
182        timestamp = ("%d" % int(time()*10000))[1:]
183        payment.p_id = "p%s" % timestamp
184        payment.p_category = category
185        payment.p_item = p_item
186        payment.p_session = p_session
187        payment.p_level = p_level
188        payment.p_current = p_current
189        payment.amount_auth = amount
190        payment.p_combi = combi
191        return None, payment
192
193    def constructMatricNumber(self, student):
194        """Fetch the matric number counter which fits the student and
195        construct the new matric number of the student.
196        """
197        next_integer = grok.getSite()['configuration'].next_matric_integer
198        if next_integer == 0:
199            return _('Matriculation number cannot be set.'), None
200        if not student.state in (
201            RETURNING, CLEARED, PAID, REGISTERED, VALIDATED):
202            return _('Matriculation number cannot be set.'), None
203        year = unicode(student.entry_session)[2:]
204        return None, "%s/%06d" % (year, next_integer)
Note: See TracBrowser for help on using the repository browser.