source: main/kofacustom.coewarri/trunk/src/kofacustom/coewarri/students/utils.py @ 16380

Last change on this file since 16380 was 16370, checked in by Henrik Bettermann, 4 years ago

Implement late school fee penalty fee payments.

  • Property svn:keywords set to Id
File size: 9.5 KB
Line 
1## $Id: utils.py 16370 2021-01-13 09:20:28Z 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##
18from time import time
19from zope.component import createObject, getUtility
20from waeup.kofa.fees import FeeTable
21from waeup.kofa.interfaces import (IKofaUtils,
22    ADMITTED, CLEARED, RETURNING, PAID, REGISTERED, VALIDATED)
23from kofacustom.nigeria.students.utils import NigeriaStudentsUtils
24from kofacustom.coewarri.interfaces import MessageFactory as _
25from kofacustom.coewarri.interswitch.browser import GATEWAY_AMT
26
27
28def local_nonlocal(student):
29    lga = getattr(student, 'lga')
30    if lga and lga.startswith('delta'):
31        return 'local'
32    else:
33        return 'non-local'
34
35PAYMENT_LEVELS = (100, 110, 200, 210, 300, 310, 400, 410, 500, 999)
36
37FEES_PARAMS = (
38        ('local', 'non-local'),
39        ('nce_ft', 'nce_pt', 'ug_ft', 'nce_we_pt'),
40        PAYMENT_LEVELS
41    )
42
43FEES_VALUES = (
44     ( # 100      110      200      210      300      310      400      410     500  999
45       (43200.0, 43200.0, 42700.0, 42700.0, 37200.0, 37200.0, 0.0, 0.0, 0.0, 0.0), # nce_ft
46       (55200.0, 55200.0, 49200.0, 49200.0, 53200.0, 53200.0, 49200.0, 49200.0, 0.0, 0.0), # nce_pt
47       (70200.0, 70200.0, 63700.0, 63700.0, 67700.0, 67700.0, 61700.0, 61700.0, 0.0, 0.0), # ug_ft
48       (33200.0, 33200.0, 28000.0, 28000.0, 32000.0, 32000.0, 28000.0, 28000.0, 0.0, 0.0), # nce_we_pt
49     ), # local
50     ( #
51       (55800.0, 55800.0, 54800.0, 54800.0, 49800.0, 49800.0, 0.0, 0.0, 0.0, 0.0), # nce_ft
52       (55200.0, 55200.0, 49200.0, 49200.0, 53200.0, 53200.0, 49200.0, 49200.0, 0.0, 0.0), # nce_pt
53       (80200.0, 80200.0, 73700.0, 73700.0, 77700.0, 77700.0, 71700.0, 71700.0, 0.0, 0.0), # ug_ft
54       (33200.0, 33200.0, 28000.0, 28000.0, 32000.0, 32000.0, 28000.0, 28000.0, 0.0, 0.0), # nce_we_pt
55     ), # non-local
56   )
57
58SCHOOL_FEES = FeeTable(FEES_PARAMS, FEES_VALUES)
59
60class CustomStudentsUtils(NigeriaStudentsUtils):
61    """A collection of customized methods.
62
63    """
64
65    def warnCreditsOOR(self, studylevel, course=None):
66        """Return message if credits are out of range.
67        """
68        if studylevel.student.current_mode == 'nce_ft':
69            limit = 56
70        elif studylevel.student.current_mode == 'ug_ft':
71            limit = 48
72            if studylevel.level == 400:
73                limit = 51
74        else:
75            limit = 50
76        if course and studylevel.total_credits + course.credits > limit:
77            return _('Maximum credits exceeded.')
78        elif studylevel.total_credits > limit:
79            return _('Maximum credits exceeded.')
80        return
81
82    def _isPaymentDisabled(self, p_session, category, student):
83        academic_session = self._getSessionConfiguration(p_session)
84        if category.startswith('schoolfee'):
85            if 'sf_all' in academic_session.payment_disabled:
86                return True
87            if student.current_mode == 'ug_ft' and \
88                'degree' in academic_session.payment_disabled:
89                return True
90        return False
91
92    def _lsfp_penalty_paymentMade(self, student, session):
93        if student.current_mode not in ('ug_ft','de_ft', 'nce_ft', 'nce_pt'):
94            return True
95        if len(student['payments']):
96            for ticket in student['payments'].values():
97                if ticket.p_state == 'paid' and \
98                    ticket.p_category == 'lsfp_penalty' and \
99                    ticket.p_session == session:
100                    return True
101        return False
102
103    def setPaymentDetails(self, category, student,
104            previous_session=None, previous_level=None, combi=[]):
105        """Create a payment ticket and set the payment data of a
106        student for the payment category specified.
107        """
108        p_item = u''
109        amount = 0.0
110        if previous_session:
111            if previous_session < student['studycourse'].entry_session:
112                return _('The previous session must not fall below '
113                         'your entry session.'), None
114            if category == 'schoolfee':
115                # School fee is always paid for the following session
116                if previous_session > student['studycourse'].current_session:
117                    return _('This is not a previous session.'), None
118            else:
119                if previous_session > student['studycourse'].current_session - 1:
120                    return _('This is not a previous session.'), None
121            p_session = previous_session
122            p_level = previous_level
123            p_current = False
124        else:
125            p_session = student['studycourse'].current_session
126            p_level = student['studycourse'].current_level
127            p_current = True
128        academic_session = self._getSessionConfiguration(p_session)
129        if academic_session == None:
130            return _(u'Session configuration object is not available.'), None
131        # Determine fee.
132        if category.startswith('schoolfee'):
133            penalty = getattr(academic_session, 'lsfp_penalty_fee')
134            if  penalty and not self._lsfp_penalty_paymentMade(
135                student, student.current_session):
136                return _('You have to pay late school fee payment penalty first.'), None
137            try:
138                certificate = student['studycourse'].certificate
139                p_item = certificate.code
140            except (AttributeError, TypeError):
141                return _('Study course data are incomplete.'), None
142            if student.state == RETURNING:
143                # Override p_session and p_level
144                p_session, p_level = self.getReturningData(student)
145                academic_session = self._getSessionConfiguration(p_session)
146                if academic_session == None:
147                    return _(u'Session configuration object '
148                              'is not available.'), None
149            if p_level in PAYMENT_LEVELS:
150                amount = SCHOOL_FEES.get_fee(
151                    (
152                     local_nonlocal(student),
153                     student.current_mode,
154                     p_level)
155                    )
156                if student.entry_mode == 'de_ft' and p_level == 200 \
157                    and student['studycourse'].entry_session >= 2019:
158                    amount += 10500
159            if amount and category in ('schoolfee_1', 'schoolfee_2'):
160                amount /= 2
161            if amount:
162                amount += GATEWAY_AMT
163        elif category == 'clearance':
164            try:
165                p_item = student['studycourse'].certificate.code
166            except (AttributeError, TypeError):
167                return _('Study course data are incomplete.'), None
168            if student.entry_mode in ('nce_we_pt', 'nce_pt'):
169                amount = academic_session.clearance_fee_3
170            elif student.entry_mode in ('ug_ft', 'de_ft'):
171                amount = academic_session.clearance_fee_2
172            else:
173                amount = academic_session.clearance_fee_1
174            if local_nonlocal(student) == 'non-local'  \
175                and student.entry_mode not in ('nce_we_pt', 'nce_pt'):
176                amount += 5000.0
177        elif category == 'bed_allocation':
178            p_item = self.getAccommodationDetails(student)['bt']
179            amount = academic_session.booking_fee
180        elif category == 'hostel_maintenance':
181            amount = 0.0
182            bedticket = student['accommodation'].get(
183                str(student.current_session), None)
184            if bedticket is not None and bedticket.bed is not None:
185                p_item = bedticket.bed_coordinates
186                if bedticket.bed.__parent__.maint_fee > 0:
187                    amount = bedticket.bed.__parent__.maint_fee
188                else:
189                    # fallback
190                    amount = academic_session.maint_fee
191            else:
192                return _(u'No bed allocated.'), None
193        elif category == 'lsfp_penalty':
194            amount = academic_session.lsfp_penalty_fee
195            if amount and student.current_mode in ('ug_ft','de_ft'):
196                amount *= 2
197        else:
198            fee_name = category + '_fee'
199            amount = getattr(academic_session, fee_name, 0.0)
200        if amount in (0.0, None):
201            return _('Amount could not be determined.'), None
202        if self.samePaymentMade(student, category, p_item, p_session):
203            return _('This type of payment has already been made.'), None
204        if self._isPaymentDisabled(p_session, category, student):
205            return _('This category of payments has been disabled.'), None
206        payment = createObject(u'waeup.StudentOnlinePayment')
207        timestamp = ("%d" % int(time()*10000))[1:]
208        payment.p_id = "p%s" % timestamp
209        payment.p_category = category
210        payment.p_item = p_item
211        payment.p_session = p_session
212        payment.p_level = p_level
213        payment.p_current = p_current
214        payment.amount_auth = amount
215        return None, payment
216
217    # prefix
218    STUDENT_ID_PREFIX = u'R'
219
220    PORTRAIT_CHANGE_STATES = ()
Note: See TracBrowser for help on using the repository browser.