source: main/waeup.kwarapoly/trunk/src/waeup/kwarapoly/students/utils.py @ 11587

Last change on this file since 11587 was 11453, checked in by Henrik Bettermann, 11 years ago

Method for disabling student payments customized. We can now
disable school fee payment for non-pg students.

  • Property svn:keywords set to Id
File size: 12.9 KB
Line 
1## $Id: utils.py 11453 2014-02-27 10:57:32Z 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 random
20from time import time
21from zope.component import createObject, getUtility
22from zope.catalog.interfaces import ICatalog
23from waeup.kofa.interfaces import CLEARED, RETURNING, PAID
24from kofacustom.nigeria.students.utils import NigeriaStudentsUtils
25from waeup.kofa.accesscodes import create_accesscode
26from waeup.kofa.interfaces import CLEARED, RETURNING, ADMITTED, IKofaUtils
27from waeup.kofa.fees import FeeTable
28from waeup.kofa.hostels.hostel import NOT_OCCUPIED
29from waeup.kwarapoly.interfaces import MessageFactory as _
30
31
32# 10  = PreND (1)
33# 100 = ND1 (2)
34# 110 = ND1R (3)
35# 200 = ND2 (4)
36# 210 = ND2R (5)
37# 300 = ND3 (6)
38# 400 = HND1 (7)
39# 410 = HND1R (8)
40# 500 = HND2 (9)
41# 510 = HND2R (10)
42# 600 = HND3 (11)
43# 999 = PGD (12)
44
45PAYMENT_LEVELS = (10, 100, 110, 200, 210, 300, 400, 410, 500, 510, 600, 999)
46
47FEES_PARAMS = (
48        ('ft', 'pt'),
49        ('local', 'non-local'),
50        ('science','arts'),
51        PAYMENT_LEVELS
52    )
53
54FEES_VALUES = (
55        (
56          ( # 10       100      110      200      210     300    400     410      500      510     600   999
57            (34500.0, 39400.0, 28500.0, 29500.0, 28500.0, 0.0, 40000.0, 29400.0, 31700.0, 29400.0, 0.0, 56600.0), # science
58            (34500.0, 37900.0, 27000.0, 28000.0, 27000.0, 0.0, 38500.0, 27900.0, 30200.0, 27900.0, 0.0, 55100.0)  # arts
59          ), # local
60          ( # 10       100      110      200      210     300    400     410      500      510     600   999
61            (49600.0, 53900.0, 37600.0, 32090.0, 37600.0, 0.0, 56400.0, 39100.0, 34900.0, 39100.0, 0.0, 83250.0), # science
62            (49600.0, 52400.0, 36100.0, 30590.0, 36100.0, 0.0, 54900.0, 37600.0, 33400.0, 37600.0, 0.0, 81750.0)  # arts
63          ), # non-local
64        ), # ft
65       
66#Repeaters fees now included for part-times students to mirror that of full-time students
67        (
68          ( # 10    100      110      200      210      300      400      410      500      510      600    999
69            (0.0, 40700.0, 28500.0, 29900.0, 28500.0, 29900.0, 41100.0, 29400.0, 31050.0, 29400.0, 31050.0, 0.0), # science
70            (0.0, 39200.0, 27000.0, 28400.0, 27000.0, 28400.0, 39600.0, 27900.0, 29550.0, 29400.0, 29550.0, 0.0)  # arts
71          ), # local
72          ( # 10    100      110      200      210      300      400      410      500      510      600    999
73            (0.0, 55400.0, 37600.0, 33850.0, 37600.0, 33850.0, 58300.0, 39100.0, 42350.0, 39100.0, 42350.0, 0.0), # science
74            (0.0, 53900.0, 36100.0, 32350.0, 36100.0, 32350.0, 56800.0, 37600.0, 40850.0, 37600.0, 40850.0, 0.0)  # arts
75          ), # non-local
76        ), # pt
77    )
78
79SCHOOL_FEES = FeeTable(FEES_PARAMS, FEES_VALUES)
80
81def local_nonlocal(student):
82    lga = getattr(student, 'lga')
83    if lga and lga.startswith('kwara'):
84        return 'local'
85    else:
86        return 'non-local'
87
88def arts_science(student):
89    if student.faccode == 'IFMS':
90        return 'arts'
91    else:
92        return 'science'
93
94def pt_ft(student):
95    if student.current_mode.endswith('pt'):
96        return 'pt'
97    else:
98        return 'ft'
99
100class CustomStudentsUtils(NigeriaStudentsUtils):
101    """A collection of customized methods.
102
103    """
104
105
106    def selectBed(self, available_beds):
107        """Randomly select a bed from a list of available beds.
108
109        """
110        return random.choice(available_beds)
111
112    def getReturningData(self, student):
113        """ This method defines what happens after school fee payment
114        of returning students depending on the student's senate verdict.
115        """
116        prev_level = student['studycourse'].current_level
117        cur_verdict = student['studycourse'].current_verdict
118        if cur_verdict in ('A','B','L','M','N','Z',):
119            # Successful student
120            new_level = divmod(int(prev_level),100)[0]*100 + 100
121        elif cur_verdict == 'C':
122            # Student on probation
123            new_level = int(prev_level) + 10
124        else:
125            # Student is somehow in an undefined state.
126            # Level has to be set manually.
127            new_level = prev_level
128        new_session = student['studycourse'].current_session + 1
129        return new_session, new_level
130
131    def _maintPaymentMade(self, student, session):
132        if len(student['payments']):
133            for ticket in student['payments'].values():
134                if ticket.p_category == 'hostel_maintenance' and \
135                    ticket.p_session == session and ticket.p_state == 'paid':
136                        return True
137        return False
138
139    def _bedAvailable(self, student):
140        acc_details  = self.getAccommodationDetails(student)
141        cat = getUtility(ICatalog, name='beds_catalog')
142        entries = cat.searchResults(
143            owner=(student.student_id,student.student_id))
144        if len(entries):
145            # Bed has already been booked.
146            return True
147        entries = cat.searchResults(
148            bed_type=(acc_details['bt'],acc_details['bt']))
149        available_beds = [
150            entry for entry in entries if entry.owner == NOT_OCCUPIED]
151        if available_beds:
152            # Bed has not yet been booked but beds are available.
153            return True
154        return False
155
156    def _isPaymentDisabled(self, p_session, category, student):
157        academic_session = self._getSessionConfiguration(p_session)
158        if category == 'schoolfee':
159            if 'sf_all' in academic_session.payment_disabled:
160                return True
161            if 'sf_non_pg' in academic_session.payment_disabled and \
162                not student.is_postgrad:
163                return True
164        return False
165
166    def setPaymentDetails(self, category, student,
167            previous_session=None, previous_level=None):
168        """Create Payment object and set the payment data of a student for
169        the payment category specified.
170
171        """
172        details = {}
173        p_item = u''
174        amount = 0.0
175        error = u''
176        if previous_session:
177            return _('Previous session payment not yet implemented.'), None
178        p_session = student['studycourse'].current_session
179        p_level = student['studycourse'].current_level
180        p_current = True
181        academic_session = self._getSessionConfiguration(p_session)
182        if academic_session == None:
183            return _(u'Session configuration object is not available.'), None
184        # Determine fee.
185        if category == 'transfer':
186            amount = academic_session.transfer_fee
187        elif category == 'gown':
188            amount = academic_session.gown_fee
189        elif category == 'bed_allocation':
190            amount = academic_session.booking_fee
191        elif category == 'hostel_maintenance':
192            amount = 0.0
193            bedticket = student['accommodation'].get(
194                str(student.current_session), None)
195            if bedticket:
196                p_item = bedticket.bed_coordinates
197                if bedticket.bed.__parent__.maint_fee > 0:
198                    amount = bedticket.bed.__parent__.maint_fee
199                else:
200                    # fallback
201                    amount = academic_session.maint_fee
202            else:
203                # Should not happen because this is already checked
204                # in the browser module, but anyway ...
205                portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
206                p_item = trans(_('no bed allocated'), portal_language)
207        elif category == 'clearance':
208            amount = academic_session.clearance_fee
209            try:
210                p_item = student['studycourse'].certificate.code
211            except (AttributeError, TypeError):
212                return _('Study course data are incomplete.'), None
213        elif category == 'schoolfee':
214            try:
215                certificate = student['studycourse'].certificate
216                p_item = certificate.code
217            except (AttributeError, TypeError):
218                return _('Study course data are incomplete.'), None
219            if student.state == RETURNING:
220                # Override p_session and p_level
221                p_session, p_level = self.getReturningData(student)
222                academic_session = self._getSessionConfiguration(p_session)
223                if academic_session == None:
224                    return _(u'Session configuration object '
225                              'is not available.'), None
226            if student.state == CLEARED and student.current_mode in (
227                                                            'hnd_ft', 'nd_ft'):
228                # Fresh students must have booked and paid for accommodation.
229                if self._bedAvailable(student):
230                    if not self._maintPaymentMade(student, p_session):
231                        return _('Book and pay for accommodation first '
232                                 'before making school fee payments.'), None
233            if student.state in (RETURNING, CLEARED):
234                if p_level in PAYMENT_LEVELS:
235                    amount = SCHOOL_FEES.get_fee(
236                        (pt_ft(student),
237                         local_nonlocal(student),
238                         arts_science(student),
239                         p_level)
240                        )
241        elif category == 'carryover1':
242            amount = 6000.0
243        elif category == 'carryover2':
244            amount = 7000.0
245        elif category == 'carryover3':
246            amount = 8000.0
247
248        else:
249            fee_name = category + '_fee'
250            amount = getattr(academic_session, fee_name, 0.0)
251        if amount in (0.0, None):
252            return _(u'Amount could not be determined.'), None
253        for key in student['payments'].keys():
254            ticket = student['payments'][key]
255            if ticket.p_state == 'paid' and\
256               ticket.p_category == category and \
257               ticket.p_item == p_item and \
258               ticket.p_session == p_session:
259                  return _('This type of payment has already been made.'), None
260        if category.startswith('carryover'):
261            p_item = getUtility(IKofaUtils).PAYMENT_CATEGORIES[category]
262            p_item = unicode(p_item)
263            # Now we change the category to reduce the number of categories.
264            category = 'schoolfee'
265        # Add session specific penalty fee.
266        if category == 'schoolfee' and student.is_postgrad:
267            amount += academic_session.penalty_pg
268        elif category == 'schoolfee':
269            amount += academic_session.penalty_ug
270        if self._isPaymentDisabled(p_session, category, student):
271            return _('Payment temporarily disabled.'), None
272        payment = createObject(u'waeup.StudentOnlinePayment')
273        timestamp = ("%d" % int(time()*10000))[1:]
274        payment.p_id = "p%s" % timestamp
275        payment.p_category = category
276        payment.p_item = p_item
277        payment.p_session = p_session
278        payment.p_level = p_level
279        payment.p_current = p_current
280        payment.amount_auth = float(amount)
281        return None, payment
282
283    def getAccommodationDetails(self, student):
284        """Determine the accommodation data of a student.
285        """
286        d = {}
287        d['error'] = u''
288        hostels = grok.getSite()['hostels']
289        d['booking_session'] = hostels.accommodation_session
290        d['allowed_states'] = hostels.accommodation_states
291        d['startdate'] = hostels.startdate
292        d['enddate'] = hostels.enddate
293        d['expired'] = hostels.expired
294        # Determine bed type
295        studycourse = student['studycourse']
296        certificate = getattr(studycourse,'certificate',None)
297        current_level = studycourse.current_level
298        if None in (current_level, certificate):
299            return d
300        end_level = certificate.end_level
301        if current_level == 10:
302            bt = 'pr'
303        elif current_level in (100, 400):
304            bt = 'fr'
305        elif current_level in (300, 600):
306            bt = 'fi'
307        else:
308            bt = 're'
309        if student.sex == 'f':
310            sex = 'female'
311        else:
312            sex = 'male'
313        special_handling = 'regular'
314        if student.faccode == 'ITCH':
315            special_handling = 'itch'
316        d['bt'] = u'%s_%s_%s' % (special_handling,sex,bt)
317        return d
318
319    PWCHANGE_STATES = (ADMITTED, CLEARED, RETURNING)
320
321    # KwaraPoly prefix
322    STUDENT_ID_PREFIX = u'W'
Note: See TracBrowser for help on using the repository browser.