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

Last change on this file since 11928 was 11865, checked in by Henrik Bettermann, 10 years ago

Increase school fees and change bank account numbers.

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