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

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

New students must book and pay for accommodation first, before making school fee payments.

  • Property svn:keywords set to Id
File size: 10.9 KB
Line 
1## $Id: utils.py 10675 2013-10-30 19:43:48Z 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, 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, 20200.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          ( # 10   100     110    200    210   300      400     410   500     510   600     999
67            (0.0, 40700.0, 0.0, 29900.0, 0.0, 29900.0, 41100.0, 0.0, 31050.0, 0.0, 31050.0, 0.0), # science
68            (0.0, 39200.0, 0.0, 28400.0, 0.0, 28400.0, 39600.0, 0.0, 29550.0, 0.0, 29550.0, 0.0)  # arts
69          ), # local
70          ( # 10   100     110    200    210   300      400     410   500     510   600     999
71            (0.0, 55400.0, 0.0, 33850.0, 0.0, 33850.0, 58300.0, 0.0, 42350.0, 0.0, 42350.0, 0.0), # science
72            (0.0, 53900.0, 0.0, 32350.0, 0.0, 32350.0, 56800.0, 0.0, 40850.0, 0.0, 40850.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 setPaymentDetails(self, category, student,
155            previous_session=None, previous_level=None):
156        """Create Payment object and set the payment data of a student for
157        the payment category specified.
158
159        """
160        details = {}
161        p_item = u''
162        amount = 0.0
163        error = u''
164        if previous_session:
165            return _('Previous session payment not yet implemented.'), None
166        p_session = student['studycourse'].current_session
167        p_level = student['studycourse'].current_level
168        p_current = True
169        academic_session = self._getSessionConfiguration(p_session)
170        if academic_session == None:
171            return _(u'Session configuration object is not available.'), None
172        # Determine fee.
173        if category == 'transfer':
174            amount = academic_session.transfer_fee
175        elif category == 'gown':
176            amount = academic_session.gown_fee
177        elif category == 'bed_allocation':
178            amount = academic_session.booking_fee
179        elif category == 'hostel_maintenance':
180            amount = academic_session.maint_fee
181        elif category == 'clearance':
182            amount = academic_session.clearance_fee
183            try:
184                p_item = student['studycourse'].certificate.code
185            except (AttributeError, TypeError):
186                return _('Study course data are incomplete.'), None
187        elif category == 'schoolfee':
188            try:
189                certificate = student['studycourse'].certificate
190                p_item = certificate.code
191            except (AttributeError, TypeError):
192                return _('Study course data are incomplete.'), None
193            if student.state == RETURNING:
194                # Override p_session and p_level
195                p_session, p_level = self.getReturningData(student)
196                academic_session = self._getSessionConfiguration(p_session)
197                if academic_session == None:
198                    return _(u'Session configuration object is not available.'), None
199            if student.state == CLEARED:
200                # Fresh students must have booked and paid for accommodation.
201                if self._bedAvailable(student):
202                    if not self._maintPaymentMade(student, p_session):
203                        return _('Book and pay for accommodation first '
204                                 'before making school fee payments.'), None
205            if student.state in (RETURNING, CLEARED):
206                if p_level in PAYMENT_LEVELS:
207                    amount = SCHOOL_FEES.get_fee(
208                        (pt_ft(student),
209                         local_nonlocal(student),
210                         arts_science(student),
211                         p_level)
212                        )
213        elif category == 'carryover1':
214            amount = 6000.0
215        elif category == 'carryover2':
216            amount = 7000.0
217        elif category == 'carryover3':
218            amount = 8000.0
219        if amount in (0.0, None):
220            return _(u'Amount could not be determined.'), None
221        for key in student['payments'].keys():
222            ticket = student['payments'][key]
223            if ticket.p_state == 'paid' and\
224               ticket.p_category == category and \
225               ticket.p_item == p_item and \
226               ticket.p_session == p_session:
227                  return _('This type of payment has already been made.'), None
228        if category.startswith('carryover'):
229            p_item = getUtility(IKofaUtils).PAYMENT_CATEGORIES[category]
230            p_item = unicode(p_item)
231            # Now we change the category to reduce the number of categories.
232            category = 'schoolfee'
233        payment = createObject(u'waeup.StudentOnlinePayment')
234        timestamp = ("%d" % int(time()*10000))[1:]
235        payment.p_id = "p%s" % timestamp
236        payment.p_category = category
237        payment.p_item = p_item
238        payment.p_session = p_session
239        payment.p_level = p_level
240        payment.p_current = p_current
241        payment.amount_auth = float(amount)
242        return None, payment
243
244    def getAccommodationDetails(self, student):
245        """Determine the accommodation data of a student.
246        """
247        d = {}
248        d['error'] = u''
249        hostels = grok.getSite()['hostels']
250        d['booking_session'] = hostels.accommodation_session
251        d['allowed_states'] = hostels.accommodation_states
252        d['startdate'] = hostels.startdate
253        d['enddate'] = hostels.enddate
254        d['expired'] = hostels.expired
255        # Determine bed type
256        studycourse = student['studycourse']
257        certificate = getattr(studycourse,'certificate',None)
258        current_level = studycourse.current_level
259        if None in (current_level, certificate):
260            return d
261        end_level = certificate.end_level
262        if current_level == 10:
263            bt = 'pr'
264        elif current_level in (100, 400):
265            bt = 'fr'
266        elif current_level in (300, 600):
267            bt = 'fi'
268        else:
269            bt = 're'
270        if student.sex == 'f':
271            sex = 'female'
272        else:
273            sex = 'male'
274        special_handling = 'regular'
275        d['bt'] = u'%s_%s_%s' % (special_handling,sex,bt)
276        return d
277
278    # KwaraPoly prefix
279    STUDENT_ID_PREFIX = u'W'
Note: See TracBrowser for help on using the repository browser.