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

Last change on this file since 16142 was 16140, checked in by Henrik Bettermann, 4 years ago

Uups.

  • Property svn:keywords set to Id
File size: 10.8 KB
RevLine 
[10765]1## $Id: utils.py 16140 2020-06-30 12:37:29Z 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##
[15802]18import grok
[10765]19from time import time
[16136]20from copy import deepcopy
[10765]21from zope.component import createObject, getUtility
22from waeup.kofa.interfaces import (IKofaUtils,
[16091]23    ADMITTED, CLEARANCE, REQUESTED, CLEARED, RETURNING, PAID,
24    REGISTERED, VALIDATED)
[10765]25from kofacustom.nigeria.students.utils import NigeriaStudentsUtils
[15563]26from kofacustom.iuokada.interfaces import MessageFactory as _
[10765]27
28class CustomStudentsUtils(NigeriaStudentsUtils):
29    """A collection of customized methods.
30
31    """
32
[16136]33    # prefix
[15645]34    STUDENT_ID_PREFIX = u'I'
35
[15649]36    SKIP_UPLOAD_VIEWLETS = (
[15654]37        'acceptanceletterupload', 'certificateupload'
[15649]38        )
[15653]39    # Maximum size of upload files in kB
40    MAX_KB = 500
[15649]41
[15656]42    #: A tuple containing the names of registration states in which changing of
43    #: passport pictures is allowed.
[16136]44
[15657]45    PORTRAIT_CHANGE_STATES = (ADMITTED, CLEARANCE,)
[15656]46
[16136]47    REQUIRED_PAYMENTS = {
48        'registration': 'Registration Fee',
49        'book': 'Book Deposit',
50        'develop': 'Development Fee',
51        'parentsconsult': 'Parents Consultative Forum (PCF) Fee',
52        'municipal': 'Municipal Fee',
53        }
54
[15660]55    def warnCreditsOOR(self, studylevel, course=None):
56        """Return message if credits are out of range. In the base
57        package only maximum credits is set.
58        """
[15808]59        if course and studylevel.total_credits + course.credits > 60:
[15660]60            return _('Maximum credits exceeded.')
[15808]61        elif studylevel.total_credits > 60:
[15660]62            return _('Maximum credits exceeded.')
63        return
64
[16136]65    def _requiredPaymentsMissing(self, student, session):
66        cats_missing = deepcopy(self.REQUIRED_PAYMENTS)
67        if len(student['payments']):
68            for category in self.REQUIRED_PAYMENTS.keys():
69                for ticket in student['payments'].values():
70                    if ticket.p_state == 'paid' and \
71                        ticket.p_category.startswith(category) and \
72                        ticket.p_session == session:
73                        del cats_missing[category]
74        if cats_missing:
75            return "%s must be paid before Tution Fee." % ', '.join(
76                cats_missing.values())
77        return
78
[15645]79    def setPaymentDetails(self, category, student,
[16136]80            previous_session=None, previous_level=None, combi=[]):
[15645]81        """Create a payment ticket and set the payment data of a
82        student for the payment category specified.
83        """
84        p_item = u''
85        amount = 0.0
86        if previous_session:
87            if previous_session < student['studycourse'].entry_session:
88                return _('The previous session must not fall below '
89                         'your entry session.'), None
90            if category == 'schoolfee':
91                # School fee is always paid for the following session
92                if previous_session > student['studycourse'].current_session:
93                    return _('This is not a previous session.'), None
94            else:
95                if previous_session > student['studycourse'].current_session - 1:
96                    return _('This is not a previous session.'), None
97            p_session = previous_session
98            p_level = previous_level
99            p_current = False
100        else:
101            p_session = student['studycourse'].current_session
102            p_level = student['studycourse'].current_level
103            p_current = True
[16136]104            if category in self.REQUIRED_PAYMENTS.keys() + [
105                'schoolfee','schoolfee40','secondinstal'] \
106                and student.state == RETURNING:
107                # In case of school fee or required sundry fee payments the
108                # payment session is always next session if students are in
109                # state returning.
110                p_session, p_level = self.getReturningData(student)
[15645]111        academic_session = self._getSessionConfiguration(p_session)
112        if academic_session == None:
113            return _(u'Session configuration object is not available.'), None
114        # Determine fee.
[16138]115        if category in ('schoolfee', 'schoolfee40', 'secondinstal'):
[16136]116            rpm = self._requiredPaymentsMissing(student, p_session)
117            if rpm:
118                return rpm, None
[15645]119            try:
120                certificate = student['studycourse'].certificate
121                p_item = certificate.code
122            except (AttributeError, TypeError):
123                return _('Study course data are incomplete.'), None
124            if previous_session:
125                # Students can pay for previous sessions in all
126                # workflow states.  Fresh students are excluded by the
127                # update method of the PreviousPaymentAddFormPage.
128                if previous_level == 100:
129                    amount = getattr(certificate, 'school_fee_1', 0.0)
130                else:
131                    amount = getattr(certificate, 'school_fee_2', 0.0)
[15780]132                if category == 'schoolfee40':
[15773]133                    amount = 0.4*amount
[15780]134                elif category == 'secondinstal':
[15773]135                    amount = 0.6*amount
[15780]136            else:
137                if category == 'secondinstal':
138                    if student.is_fresh:
139                        amount = 0.6 * getattr(certificate, 'school_fee_1', 0.0)
140                    else:
141                        amount = 0.6 * getattr(certificate, 'school_fee_2', 0.0)
142                else:
[16091]143                    if student.state in (CLEARANCE, REQUESTED, CLEARED):
[15780]144                        amount = getattr(certificate, 'school_fee_1', 0.0)
145                    elif student.state == RETURNING:
146                        amount = getattr(certificate, 'school_fee_2', 0.0)
147                    elif student.is_postgrad and student.state == PAID:
148                        # Returning postgraduate students also pay for the
149                        # next session but their level always remains the
150                        # same.
151                        p_session += 1
152                        academic_session = self._getSessionConfiguration(p_session)
153                        if academic_session == None:
154                            return _(
155                                u'Session configuration object is not available.'
156                                ), None
157                        amount = getattr(certificate, 'school_fee_2', 0.0)
158                    if amount and category == 'schoolfee40':
159                        amount = 0.4*amount
[15645]160        elif category == 'clearance':
161            try:
162                p_item = student['studycourse'].certificate.code
163            except (AttributeError, TypeError):
164                return _('Study course data are incomplete.'), None
165            amount = academic_session.clearance_fee
[15937]166        elif category.startswith('resit'):
167            amount = academic_session.resit_fee
168            number = int(category.strip('resit'))
169            amount *= number
[15661]170        #elif category == 'bed_allocation':
171        #    p_item = self.getAccommodationDetails(student)['bt']
172        #    amount = academic_session.booking_fee
173        #elif category == 'hostel_maintenance':
174        #    amount = 0.0
175        #    bedticket = student['accommodation'].get(
176        #        str(student.current_session), None)
177        #    if bedticket is not None and bedticket.bed is not None:
178        #        p_item = bedticket.bed_coordinates
179        #        if bedticket.bed.__parent__.maint_fee > 0:
180        #            amount = bedticket.bed.__parent__.maint_fee
181        #        else:
182        #            # fallback
183        #            amount = academic_session.maint_fee
184        #    else:
185        #        return _(u'No bed allocated.'), None
[15676]186        elif category == 'combi' and combi:
187            categories = getUtility(IKofaUtils).COMBI_PAYMENT_CATEGORIES
188            for cat in combi:
189                fee_name = cat + '_fee'
190                cat_amount = getattr(academic_session, fee_name, 0.0)
191                if not cat_amount:
192                    return _('%s undefined.' % categories[cat]), None
193                amount += cat_amount
194                p_item += u'%s + ' % categories[cat]
195            p_item = p_item.strip(' + ')
[15645]196        else:
197            fee_name = category + '_fee'
198            amount = getattr(academic_session, fee_name, 0.0)
199        if amount in (0.0, None):
200            return _('Amount could not be determined.'), None
201        if self.samePaymentMade(student, category, p_item, p_session):
202            return _('This type of payment has already been made.'), None
203        if self._isPaymentDisabled(p_session, category, student):
204            return _('This category of payments has been disabled.'), None
205        payment = createObject(u'waeup.StudentOnlinePayment')
206        timestamp = ("%d" % int(time()*10000))[1:]
207        payment.p_id = "p%s" % timestamp
208        payment.p_category = category
209        payment.p_item = p_item
210        payment.p_session = p_session
211        payment.p_level = p_level
212        payment.p_current = p_current
213        payment.amount_auth = amount
[15686]214        payment.p_combi = combi
[15802]215        return None, payment
216
[16138]217    def setBalanceDetails(self, category, student,
218            balance_session, balance_level, balance_amount):
219        """Create a balance payment ticket and set the payment data
220        as selected by the student.
221        """
[16139]222        if category in ('schoolfee', 'schoolfee40', 'secondinstal') \
223            and balance_session > 2019:
[16138]224            rpm = self._requiredPaymentsMissing(student, balance_session)
225            if rpm:
226                return rpm, None
227        return super(
228            CustomStudentsUtils, self).setBalanceDetails(category, student,
229            balance_session, balance_level, balance_amount)
230
[15802]231    def constructMatricNumber(self, student):
232        """Fetch the matric number counter which fits the student and
233        construct the new matric number of the student.
234        """
235        next_integer = grok.getSite()['configuration'].next_matric_integer
236        if next_integer == 0:
237            return _('Matriculation number cannot be set.'), None
[15810]238        if not student.state in (
239            RETURNING, CLEARED, PAID, REGISTERED, VALIDATED):
[15805]240            return _('Matriculation number cannot be set.'), None
[15802]241        year = unicode(student.entry_session)[2:]
242        return None, "%s/%06d" % (year, next_integer)
Note: See TracBrowser for help on using the repository browser.