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

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

Take hostel_maintenance fee from hostel objects. Store bed coordinates as payment item.

  • Property svn:keywords set to Id
File size: 11.7 KB
Line 
1## $Id: utils.py 10682 2013-11-01 09:11:10Z 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 = 0.0
181            bedticket = student['accommodation'].get(
182                str(student.current_session), None)
183            if bedticket:
184                p_item = bedticket.bed_coordinates
185                if bedticket.bed.__parent__.maint_fee > 0:
186                    amount = bedticket.bed.__parent__.maint_fee
187                else:
188                    # fallback
189                    amount = academic_session.maint_fee
190            else:
191                # Should not happen because this is already checked
192                # in the browser module, but anyway ...
193                portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
194                p_item = trans(_('no bed allocated'), portal_language)
195        elif category == 'clearance':
196            amount = academic_session.clearance_fee
197            try:
198                p_item = student['studycourse'].certificate.code
199            except (AttributeError, TypeError):
200                return _('Study course data are incomplete.'), None
201        elif category == 'schoolfee':
202            try:
203                certificate = student['studycourse'].certificate
204                p_item = certificate.code
205            except (AttributeError, TypeError):
206                return _('Study course data are incomplete.'), None
207            if student.state == RETURNING:
208                # Override p_session and p_level
209                p_session, p_level = self.getReturningData(student)
210                academic_session = self._getSessionConfiguration(p_session)
211                if academic_session == None:
212                    return _(u'Session configuration object '
213                              'is not available.'), None
214            if student.state == CLEARED and student.current_mode in (
215                                                            'hnd_ft', 'nd_ft'):
216                # Fresh students must have booked and paid for accommodation.
217                if self._bedAvailable(student):
218                    if not self._maintPaymentMade(student, p_session):
219                        return _('Book and pay for accommodation first '
220                                 'before making school fee payments.'), None
221            if student.state in (RETURNING, CLEARED):
222                if p_level in PAYMENT_LEVELS:
223                    amount = SCHOOL_FEES.get_fee(
224                        (pt_ft(student),
225                         local_nonlocal(student),
226                         arts_science(student),
227                         p_level)
228                        )
229        elif category == 'carryover1':
230            amount = 6000.0
231        elif category == 'carryover2':
232            amount = 7000.0
233        elif category == 'carryover3':
234            amount = 8000.0
235        if amount in (0.0, None):
236            return _(u'Amount could not be determined.'), None
237        for key in student['payments'].keys():
238            ticket = student['payments'][key]
239            if ticket.p_state == 'paid' and\
240               ticket.p_category == category and \
241               ticket.p_item == p_item and \
242               ticket.p_session == p_session:
243                  return _('This type of payment has already been made.'), None
244        if category.startswith('carryover'):
245            p_item = getUtility(IKofaUtils).PAYMENT_CATEGORIES[category]
246            p_item = unicode(p_item)
247            # Now we change the category to reduce the number of categories.
248            category = 'schoolfee'
249        payment = createObject(u'waeup.StudentOnlinePayment')
250        timestamp = ("%d" % int(time()*10000))[1:]
251        payment.p_id = "p%s" % timestamp
252        payment.p_category = category
253        payment.p_item = p_item
254        payment.p_session = p_session
255        payment.p_level = p_level
256        payment.p_current = p_current
257        payment.amount_auth = float(amount)
258        return None, payment
259
260    def getAccommodationDetails(self, student):
261        """Determine the accommodation data of a student.
262        """
263        d = {}
264        d['error'] = u''
265        hostels = grok.getSite()['hostels']
266        d['booking_session'] = hostels.accommodation_session
267        d['allowed_states'] = hostels.accommodation_states
268        d['startdate'] = hostels.startdate
269        d['enddate'] = hostels.enddate
270        d['expired'] = hostels.expired
271        # Determine bed type
272        studycourse = student['studycourse']
273        certificate = getattr(studycourse,'certificate',None)
274        current_level = studycourse.current_level
275        if None in (current_level, certificate):
276            return d
277        end_level = certificate.end_level
278        if current_level == 10:
279            bt = 'pr'
280        elif current_level in (100, 400):
281            bt = 'fr'
282        elif current_level in (300, 600):
283            bt = 'fi'
284        else:
285            bt = 're'
286        if student.sex == 'f':
287            sex = 'female'
288        else:
289            sex = 'male'
290        special_handling = 'regular'
291        d['bt'] = u'%s_%s_%s' % (special_handling,sex,bt)
292        return d
293
294    # KwaraPoly prefix
295    STUDENT_ID_PREFIX = u'W'
Note: See TracBrowser for help on using the repository browser.