source: main/kofacustom.dspg/trunk/src/kofacustom/dspg/students/utils.py @ 15284

Last change on this file since 15284 was 15255, checked in by Henrik Bettermann, 6 years ago

Add many new payment categories.

  • Property svn:keywords set to Id
File size: 11.6 KB
Line 
1## $Id: utils.py 15255 2018-11-29 12:26:38Z 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, CLEARED, RETURNING, PAID, REGISTERED, VALIDATED)
23from kofacustom.nigeria.students.utils import NigeriaStudentsUtils
24from kofacustom.dspg.interfaces import MessageFactory as _
25
26
27def local(student):
28    lga = getattr(student, 'lga')
29    if lga and lga.startswith('delta'):
30        return True
31    return False
32
33class CustomStudentsUtils(NigeriaStudentsUtils):
34    """A collection of customized methods.
35    """
36
37    # prefix
38    STUDENT_ID_PREFIX = u'P'
39
40    def _dep_sug_paymentMade(self, student, session):
41        if len(student['payments']):
42            for ticket in student['payments'].values():
43                if ticket.p_state == 'paid' and \
44                    ticket.p_category == 'dep_sug' and \
45                    ticket.p_session == session:
46                    return True
47        return False
48
49    def getAccommodationDetails(self, student):
50        """Determine the accommodation data of a student.
51        """
52        d = {}
53        d['error'] = u''
54        hostels = grok.getSite()['hostels']
55        d['booking_session'] = hostels.accommodation_session
56        d['allowed_states'] = hostels.accommodation_states
57        d['startdate'] = hostels.startdate
58        d['enddate'] = hostels.enddate
59        d['expired'] = hostels.expired
60        # Determine bed type
61        bt = 'all'
62        if student.sex == 'f':
63            sex = 'female'
64        else:
65            sex = 'male'
66        special_handling = 'regular'
67        d['bt'] = u'%s_%s_%s' % (special_handling,sex,bt)
68        return d
69
70    def getReturningData(self, student):
71        """ This method defines what happens after school fee payment
72        of returning students depending on the student's senate verdict.
73        """
74        prev_level = student['studycourse'].current_level
75        cur_verdict = student['studycourse'].current_verdict
76        if cur_verdict in ('A','B','L','M','N','Z',):
77            # Successful student
78            new_level = divmod(int(prev_level),100)[0]*100 + 100
79        elif cur_verdict == 'C':
80            # Student on probation
81            new_level = int(prev_level) + 10
82        else:
83            # Student is somehow in an undefined state.
84            # Level has to be set manually.
85            new_level = prev_level
86        new_session = student['studycourse'].current_session + 1
87        return new_session, new_level
88
89    def setPaymentDetails(self, category, student,
90            previous_session, previous_level):
91        """Create a payment ticket and set the payment data of a
92        student for the payment category specified.
93        """
94        p_item = u''
95        amount = 0.0
96        if previous_session:
97            if previous_session < student['studycourse'].entry_session:
98                return _('The previous session must not fall below '
99                         'your entry session.'), None
100            if category == 'schoolfee':
101                # School fee is always paid for the following session
102                if previous_session > student['studycourse'].current_session:
103                    return _('This is not a previous session.'), None
104            else:
105                if previous_session > student['studycourse'].current_session - 1:
106                    return _('This is not a previous session.'), None
107            p_session = previous_session
108            p_level = previous_level
109            p_current = False
110        else:
111            p_session = student['studycourse'].current_session
112            p_level = student['studycourse'].current_level
113            p_current = True
114        academic_session = self._getSessionConfiguration(p_session)
115        if academic_session == None:
116            return _(u'Session configuration object is not available.'), None
117        # Determine fee.
118        if category == 'schoolfee':
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                    if local(student):
130                        amount = getattr(certificate, 'school_fee_1', 0.0)
131                    else:
132                        amount = getattr(certificate, 'school_fee_3', 0.0)
133                else:
134                    if local(student):
135                        amount = getattr(certificate, 'school_fee_2', 0.0)
136                    else:
137                        amount = getattr(certificate, 'school_fee_4', 0.0)
138            else:
139                # Students are only allowed to pay school fee
140                # if current session dep_sug payment has been made.
141                if student.faccode != 'SPAT' and not self._dep_sug_paymentMade(
142                    student, student.current_session):
143                    return _('You have to pay NADESU/SA/SUG Dues first.'), None
144                if student.state == CLEARED:
145                    if local(student):
146                        amount = getattr(certificate, 'school_fee_1', 0.0)
147                    else:
148                        amount = getattr(certificate, 'school_fee_3', 0.0)
149                elif student.state == RETURNING:
150                    # In case of returning school fee payment the
151                    # payment session and level contain the values of
152                    # the session the student has paid for. Payment
153                    # session is always next session.
154                    p_session, p_level = self.getReturningData(student)
155                    academic_session = self._getSessionConfiguration(p_session)
156                    if academic_session == None:
157                        return _(
158                            u'Session configuration object is not available.'
159                            ), None
160                    if local(student):
161                        amount = getattr(certificate, 'school_fee_2', 0.0)
162                    else:
163                        amount = getattr(certificate, 'school_fee_4', 0.0)
164                elif student.is_postgrad and student.state == PAID:
165                    # Returning postgraduate students also pay for the
166                    # next session but their level always remains the
167                    # same.
168                    p_session += 1
169                    academic_session = self._getSessionConfiguration(p_session)
170                    if academic_session == None:
171                        return _(
172                            u'Session configuration object is not available.'
173                            ), None
174                    if local(student):
175                        amount = getattr(certificate, 'school_fee_2', 0.0)
176                    else:
177                        amount = getattr(certificate, 'school_fee_4', 0.0)
178        elif category == 'clearance':
179            try:
180                p_item = student['studycourse'].certificate.code
181            except (AttributeError, TypeError):
182                return _('Study course data are incomplete.'), None
183            amount = academic_session.clearance_fee
184            # Local ND and HND students are given a rebate which has
185            # to be adjusted if the basic acceptance fee changes.
186            if amount and student.current_mode == 'nd_ft' and local(student):
187                amount -= 10000.0
188            if amount and student.current_mode == 'hnd_ft' and local(student):
189                amount -= 5000.0
190        elif category == 'bed_allocation':
191            p_item = self.getAccommodationDetails(student)['bt']
192            amount = academic_session.booking_fee
193        elif category == 'hostel_maintenance':
194            amount = 0.0
195            bedticket = student['accommodation'].get(
196                str(student.current_session), None)
197            if bedticket is not None and bedticket.bed is not None:
198                p_item = bedticket.bed_coordinates
199                if bedticket.bed.__parent__.maint_fee > 0:
200                    amount = bedticket.bed.__parent__.maint_fee
201                else:
202                    # fallback
203                    amount = academic_session.maint_fee
204            else:
205                return _(u'No bed allocated.'), None
206        elif category == 'dep_sug':
207            amount = 3150.0 # includes GATEWAY_AMT
208            if student.faccode == 'SPAT':
209            #    amount = 1650.0 # includes GATEWAY_AMT
210                amount = 0.0
211        else:
212            fee_name = category + '_fee'
213            amount = getattr(academic_session, fee_name, 0.0)
214        if amount in (0.0, None):
215            return _('Amount could not be determined.'), None
216        if self.samePaymentMade(student, category, p_item, p_session):
217            return _('This type of payment has already been made.'), None
218        if self._isPaymentDisabled(p_session, category, student):
219            return _('This category of payments has been disabled.'), None
220        payment = createObject(u'waeup.StudentOnlinePayment')
221        timestamp = ("%d" % int(time()*10000))[1:]
222        payment.p_id = "p%s" % timestamp
223        payment.p_category = category
224        payment.p_item = p_item
225        payment.p_session = p_session
226        payment.p_level = p_level
227        payment.p_current = p_current
228        payment.amount_auth = amount
229        return None, payment
230
231    def warnCreditsOOR(self, studylevel, course=None):
232        """Return message if credits are out of range. In the base
233        package only maximum credits is set.
234        """
235        if course and studylevel.total_credits + course.credits > 80:
236            return _('Maximum credits exceeded.')
237        elif studylevel.total_credits > 80:
238            return _('Maximum credits exceeded.')
239        return
240
241    #: A tuple containing names of file upload viewlets which are not shown
242    #: on the `StudentClearanceManageFormPage`. Nothing is being skipped
243    #: in the base package. This attribute makes only sense, if intermediate
244    #: custom packages are being used, like we do for all Nigerian portals.
245    SKIP_UPLOAD_VIEWLETS = ('acceptanceletterupload',
246                            'higherqualificationresultupload',
247                            'advancedlevelresultupload',
248                            'evidencenameupload',
249                            'refereeletterupload',
250                            'statutorydeclarationupload',
251                            'firstsittingresultupload',
252                            'secondsittingresultupload',
253                            'certificateupload',
254                            'resultstatementupload',
255                            )
256
257    #: A tuple containing the names of registration states in which changing of
258    #: passport pictures is allowed.
259    PORTRAIT_CHANGE_STATES = (ADMITTED, RETURNING,)
Note: See TracBrowser for help on using the repository browser.