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

Last change on this file since 11476 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
RevLine 
[7419]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##
[7151]18import grok
[9190]19import random
[8599]20from time import time
[9737]21from zope.component import createObject, getUtility
[10675]22from zope.catalog.interfaces import ICatalog
[8475]23from waeup.kofa.interfaces import CLEARED, RETURNING, PAID
[8834]24from kofacustom.nigeria.students.utils import NigeriaStudentsUtils
[8247]25from waeup.kofa.accesscodes import create_accesscode
[10708]26from waeup.kofa.interfaces import CLEARED, RETURNING, ADMITTED, IKofaUtils
[9393]27from waeup.kofa.fees import FeeTable
[10675]28from waeup.kofa.hostels.hostel import NOT_OCCUPIED
[9347]29from waeup.kwarapoly.interfaces import MessageFactory as _
[6902]30
[9393]31
[9811]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
[9393]47FEES_PARAMS = (
48        ('ft', 'pt'),
49        ('local', 'non-local'),
50        ('science','arts'),
51        PAYMENT_LEVELS
52    )
53
54FEES_VALUES = (
55        (
[9811]56          ( # 10       100      110      200      210     300    400     410      500      510     600   999
[10672]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
[10693]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
[9393]59          ), # local
[9811]60          ( # 10       100      110      200      210     300    400     410      500      510     600   999
[10672]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
[9393]63          ), # non-local
64        ), # ft
[10942]65       
66#Repeaters fees now included for part-times students to mirror that of full-time students
[9393]67        (
[10942]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
[9393]71          ), # local
[10942]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
[9393]75          ), # non-local
76        ), # pt
77    )
78
79SCHOOL_FEES = FeeTable(FEES_PARAMS, FEES_VALUES)
80
81def local_nonlocal(student):
82    lga = getattr(student, 'lga')
[9568]83    if lga and lga.startswith('kwara'):
[9393]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
[8834]100class CustomStudentsUtils(NigeriaStudentsUtils):
[7151]101    """A collection of customized methods.
102
103    """
104
[9393]105
[9190]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
[8270]112    def getReturningData(self, student):
113        """ This method defines what happens after school fee payment
[8319]114        of returning students depending on the student's senate verdict.
[8270]115        """
[8319]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
[8270]128        new_session = student['studycourse'].current_session + 1
129        return new_session, new_level
130
[10675]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
[11453]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
[9153]166    def setPaymentDetails(self, category, student,
167            previous_session=None, previous_level=None):
[8599]168        """Create Payment object and set the payment data of a student for
169        the payment category specified.
170
171        """
[8306]172        details = {}
[8599]173        p_item = u''
174        amount = 0.0
175        error = u''
[9153]176        if previous_session:
177            return _('Previous session payment not yet implemented.'), None
[8599]178        p_session = student['studycourse'].current_session
179        p_level = student['studycourse'].current_level
[9153]180        p_current = True
[9526]181        academic_session = self._getSessionConfiguration(p_session)
182        if academic_session == None:
[8599]183            return _(u'Session configuration object is not available.'), None
[9526]184        # Determine fee.
[7151]185        if category == 'transfer':
[8599]186            amount = academic_session.transfer_fee
[7151]187        elif category == 'gown':
[8599]188            amount = academic_session.gown_fee
[7151]189        elif category == 'bed_allocation':
[8599]190            amount = academic_session.booking_fee
[7151]191        elif category == 'hostel_maintenance':
[10682]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)
[7151]207        elif category == 'clearance':
[9143]208            amount = academic_session.clearance_fee
[8599]209            try:
210                p_item = student['studycourse'].certificate.code
211            except (AttributeError, TypeError):
212                return _('Study course data are incomplete.'), None
[7151]213        elif category == 'schoolfee':
[8599]214            try:
215                certificate = student['studycourse'].certificate
216                p_item = certificate.code
217            except (AttributeError, TypeError):
218                return _('Study course data are incomplete.'), None
[9297]219            if student.state == RETURNING:
[9526]220                # Override p_session and p_level
[9297]221                p_session, p_level = self.getReturningData(student)
[9526]222                academic_session = self._getSessionConfiguration(p_session)
223                if academic_session == None:
[10679]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'):
[10675]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
[9388]233            if student.state in (RETURNING, CLEARED):
[9393]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                        )
[9737]241        elif category == 'carryover1':
[9724]242            amount = 6000.0
[9737]243        elif category == 'carryover2':
[9724]244            amount = 7000.0
[9737]245        elif category == 'carryover3':
[9724]246            amount = 8000.0
[10735]247
248        else:
249            fee_name = category + '_fee'
250            amount = getattr(academic_session, fee_name, 0.0)
[8599]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
[9737]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'
[11065]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
[11453]270        if self._isPaymentDisabled(p_session, category, student):
271            return _('Payment temporarily disabled.'), None
[8713]272        payment = createObject(u'waeup.StudentOnlinePayment')
[8953]273        timestamp = ("%d" % int(time()*10000))[1:]
[8599]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
[9153]279        payment.p_current = p_current
[9143]280        payment.amount_auth = float(amount)
[8599]281        return None, payment
[7621]282
[9207]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'
[9614]303        elif current_level in (100, 400):
[9207]304            bt = 'fr'
[9614]305        elif current_level in (300, 600):
[9207]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'
[10739]314        if student.faccode == 'ITCH':
315            special_handling = 'itch'
[9207]316        d['bt'] = u'%s_%s_%s' % (special_handling,sex,bt)
317        return d
318
[10756]319    PWCHANGE_STATES = (ADMITTED, CLEARED, RETURNING)
[10708]320
[9347]321    # KwaraPoly prefix
[10672]322    STUDENT_ID_PREFIX = u'W'
Note: See TracBrowser for help on using the repository browser.