## $Id: utils.py 11646 2014-05-14 05:17:40Z henrik $ ## ## Copyright (C) 2011 Uli Fouquet & Henrik Bettermann ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 2 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; if not, write to the Free Software ## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ## import grok import random from time import time from zope.component import createObject, getUtility from zope.catalog.interfaces import ICatalog from waeup.kofa.interfaces import CLEARED, RETURNING, PAID from kofacustom.nigeria.students.utils import NigeriaStudentsUtils from waeup.kofa.accesscodes import create_accesscode from waeup.kofa.interfaces import CLEARED, RETURNING, ADMITTED, IKofaUtils from waeup.kofa.fees import FeeTable from waeup.kofa.hostels.hostel import NOT_OCCUPIED from waeup.kwarapoly.interfaces import MessageFactory as _ # 10 = PreND (1) # 100 = ND1 (2) # 110 = ND1R (3) # 200 = ND2 (4) # 210 = ND2R (5) # 300 = ND3 (6) # 400 = HND1 (7) # 410 = HND1R (8) # 500 = HND2 (9) # 510 = HND2R (10) # 600 = HND3 (11) # 999 = PGD (12) PAYMENT_LEVELS = (10, 100, 110, 200, 210, 300, 400, 410, 500, 510, 600, 999) FEES_PARAMS = ( ('ft', 'pt'), ('local', 'non-local'), ('science','arts'), PAYMENT_LEVELS ) FEES_VALUES = ( ( ( # 10 100 110 200 210 300 400 410 500 510 600 999 (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 (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 ), # local ( # 10 100 110 200 210 300 400 410 500 510 600 999 (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 (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 ), # non-local ), # ft #Repeaters fees now included for part-times students to mirror that of full-time students ( ( # 10 100 110 200 210 300 400 410 500 510 600 999 (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 (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 ), # local ( # 10 100 110 200 210 300 400 410 500 510 600 999 (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 (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 ), # non-local ), # pt ) SCHOOL_FEES = FeeTable(FEES_PARAMS, FEES_VALUES) def local_nonlocal(student): lga = getattr(student, 'lga') if lga and lga.startswith('kwara'): return 'local' else: return 'non-local' def arts_science(student): if student.faccode == 'IFMS': return 'arts' else: return 'science' def pt_ft(student): if student.current_mode.endswith('pt'): return 'pt' else: return 'ft' class CustomStudentsUtils(NigeriaStudentsUtils): """A collection of customized methods. """ def selectBed(self, available_beds): """Randomly select a bed from a list of available beds. """ return random.choice(available_beds) def getReturningData(self, student): """ This method defines what happens after school fee payment of returning students depending on the student's senate verdict. """ prev_level = student['studycourse'].current_level cur_verdict = student['studycourse'].current_verdict if cur_verdict in ('A','B','L','M','N','Z',): # Successful student new_level = divmod(int(prev_level),100)[0]*100 + 100 elif cur_verdict == 'C': # Student on probation new_level = int(prev_level) + 10 else: # Student is somehow in an undefined state. # Level has to be set manually. new_level = prev_level new_session = student['studycourse'].current_session + 1 return new_session, new_level def _maintPaymentMade(self, student, session): if len(student['payments']): for ticket in student['payments'].values(): if ticket.p_category == 'hostel_maintenance' and \ ticket.p_session == session and ticket.p_state == 'paid': return True return False def _bedAvailable(self, student): acc_details = self.getAccommodationDetails(student) cat = getUtility(ICatalog, name='beds_catalog') entries = cat.searchResults( owner=(student.student_id,student.student_id)) if len(entries): # Bed has already been booked. return True entries = cat.searchResults( bed_type=(acc_details['bt'],acc_details['bt'])) available_beds = [ entry for entry in entries if entry.owner == NOT_OCCUPIED] if available_beds: # Bed has not yet been booked but beds are available. return True return False def _isPaymentDisabled(self, p_session, category, student): academic_session = self._getSessionConfiguration(p_session) if category == 'schoolfee': if 'sf_all' in academic_session.payment_disabled: return True if 'sf_non_pg' in academic_session.payment_disabled and \ not student.is_postgrad: return True return False def setPaymentDetails(self, category, student, previous_session=None, previous_level=None): """Create Payment object and set the payment data of a student for the payment category specified. """ details = {} p_item = u'' amount = 0.0 error = u'' if previous_session: return _('Previous session payment not yet implemented.'), None p_session = student['studycourse'].current_session p_level = student['studycourse'].current_level p_current = True academic_session = self._getSessionConfiguration(p_session) if academic_session == None: return _(u'Session configuration object is not available.'), None # Determine fee. if category == 'transfer': amount = academic_session.transfer_fee elif category == 'gown': amount = academic_session.gown_fee elif category == 'bed_allocation': amount = academic_session.booking_fee elif category == 'hostel_maintenance': amount = 0.0 bedticket = student['accommodation'].get( str(student.current_session), None) if bedticket: p_item = bedticket.bed_coordinates if bedticket.bed.__parent__.maint_fee > 0: amount = bedticket.bed.__parent__.maint_fee else: # fallback amount = academic_session.maint_fee else: # Should not happen because this is already checked # in the browser module, but anyway ... portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE p_item = trans(_('no bed allocated'), portal_language) elif category == 'clearance': amount = academic_session.clearance_fee try: p_item = student['studycourse'].certificate.code except (AttributeError, TypeError): return _('Study course data are incomplete.'), None elif category == 'schoolfee': try: certificate = student['studycourse'].certificate p_item = certificate.code except (AttributeError, TypeError): return _('Study course data are incomplete.'), None if student.state == RETURNING: # Override p_session and p_level p_session, p_level = self.getReturningData(student) academic_session = self._getSessionConfiguration(p_session) if academic_session == None: return _(u'Session configuration object ' 'is not available.'), None if student.state == CLEARED and student.current_mode in ( 'hnd_ft', 'nd_ft'): # Fresh students must have booked and paid for accommodation. if self._bedAvailable(student): if not self._maintPaymentMade(student, p_session): return _('Book and pay for accommodation first ' 'before making school fee payments.'), None if student.state in (RETURNING, CLEARED): if p_level in PAYMENT_LEVELS: amount = SCHOOL_FEES.get_fee( (pt_ft(student), local_nonlocal(student), arts_science(student), p_level) ) elif category == 'carryover1': amount = 6000.0 elif category == 'carryover2': amount = 7000.0 elif category == 'carryover3': amount = 8000.0 else: fee_name = category + '_fee' amount = getattr(academic_session, fee_name, 0.0) if amount in (0.0, None): return _(u'Amount could not be determined.'), None if self.samePaymentMade(student, category, p_item, p_session): return _('This type of payment has already been made.'), None # Add session specific penalty fee. if category == 'schoolfee' and student.is_postgrad: amount += academic_session.penalty_pg elif category == 'schoolfee': amount += academic_session.penalty_ug # Recategorize carryover fees. if category.startswith('carryover'): p_item = getUtility(IKofaUtils).PAYMENT_CATEGORIES[category] p_item = unicode(p_item) # Now we change the category to reduce the number of categories. category = 'schoolfee' if self._isPaymentDisabled(p_session, category, student): return _('Payment temporarily disabled.'), None payment = createObject(u'waeup.StudentOnlinePayment') timestamp = ("%d" % int(time()*10000))[1:] payment.p_id = "p%s" % timestamp payment.p_category = category payment.p_item = p_item payment.p_session = p_session payment.p_level = p_level payment.p_current = p_current payment.amount_auth = float(amount) return None, payment def getAccommodationDetails(self, student): """Determine the accommodation data of a student. """ d = {} d['error'] = u'' hostels = grok.getSite()['hostels'] d['booking_session'] = hostels.accommodation_session d['allowed_states'] = hostels.accommodation_states d['startdate'] = hostels.startdate d['enddate'] = hostels.enddate d['expired'] = hostels.expired # Determine bed type studycourse = student['studycourse'] certificate = getattr(studycourse,'certificate',None) current_level = studycourse.current_level if None in (current_level, certificate): return d end_level = certificate.end_level if current_level == 10: bt = 'pr' elif current_level in (100, 400): bt = 'fr' elif current_level in (300, 600): bt = 'fi' else: bt = 're' if student.sex == 'f': sex = 'female' else: sex = 'male' special_handling = 'regular' if student.faccode == 'ITCH': special_handling = 'itch' d['bt'] = u'%s_%s_%s' % (special_handling,sex,bt) return d PWCHANGE_STATES = (ADMITTED, CLEARED, RETURNING) # KwaraPoly prefix STUDENT_ID_PREFIX = u'W'