source: main/kofacustom.iuokada/trunk/src/kofacustom/iuokada/students/browser.py @ 17433

Last change on this file since 17433 was 17137, checked in by Henrik Bettermann, 2 years ago

Show all sf payments. Start with entry session.

  • Property svn:keywords set to Id
File size: 20.7 KB
Line 
1## $Id: browser.py 17137 2022-10-19 21:23:06Z henrik $
2##
3## Copyright (C) 2012 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 os
20from zope.i18n import translate
21from zope.schema.interfaces import ConstraintNotSatisfied
22from zope.component import getUtility
23from zope.security import checkPermission
24from zope.formlib.textwidgets import BytesDisplayWidget
25from hurry.workflow.interfaces import IWorkflowInfo
26from waeup.kofa.interfaces import (
27    REQUESTED, ADMITTED, CLEARANCE, REQUESTED, CLEARED,
28    IExtFileStore, IKofaUtils, academic_sessions_vocab)
29from waeup.kofa.widgets.datewidget import FriendlyDatetimeDisplayWidget
30from waeup.kofa.browser.layout import (
31    action, jsaction, UtilityView, KofaEditFormPage)
32from waeup.kofa.students.browser import (
33    StudyLevelEditFormPage, StudyLevelDisplayFormPage,
34    StudentBasePDFFormPage, ExportPDFCourseRegistrationSlip,
35    CourseTicketDisplayFormPage, StudentTriggerTransitionFormPage,
36    StartClearancePage, BalancePaymentAddFormPage,
37    ExportPDFAdmissionSlip, ExportPDFPersonalDataSlip,
38    PaymentsManageFormPage,
39    msave, emit_lock_message)
40from waeup.kofa.students.interfaces import (
41    IStudentsUtils, ICourseTicket, IStudent)
42from waeup.kofa.students.vocabularies import StudyLevelSource
43from waeup.kofa.students.workflow import FORBIDDEN_POSTGRAD_TRANS
44from kofacustom.nigeria.students.browser import (
45    NigeriaOnlinePaymentDisplayFormPage,
46    NigeriaStudentBaseDisplayFormPage,
47    NigeriaStudentBaseManageFormPage,
48    NigeriaStudentClearanceEditFormPage,
49    NigeriaOnlinePaymentAddFormPage,
50    NigeriaExportPDFPaymentSlip,
51    NigeriaExportPDFClearanceSlip,
52    NigeriaExportPDFCourseRegistrationSlip,
53    NigeriaStudentBaseEditFormPage,
54    NigeriaBedTicketAddPage,
55    NigeriaAccommodationManageFormPage,
56    NigeriaAccommodationDisplayFormPage,
57    NigeriaStudentPersonalManageFormPage,
58    NigeriaStudentPersonalEditFormPage,
59    NigeriaStudentPersonalDisplayFormPage
60    )
61from kofacustom.iuokada.students.interfaces import (
62    ICustomStudentOnlinePayment, ICustomStudentStudyCourse,
63    ICustomStudentStudyLevel, ICustomStudentBase, ICustomStudent,
64    ICustomStudentPersonal, ICustomStudentPersonalEdit)
65from kofacustom.iuokada.interfaces import MessageFactory as _
66
67class CustomStudentBaseDisplayFormPage(NigeriaStudentBaseDisplayFormPage):
68    """ Page to display student base data
69    """
70    form_fields = grok.AutoFields(ICustomStudentBase).omit(
71        'password', 'suspended', 'suspended_comment', 'flash_notice')
72
73class CustomStudentBaseManageFormPage(NigeriaStudentBaseManageFormPage):
74    """ View to manage student base data
75    """
76    form_fields = grok.AutoFields(ICustomStudentBase).omit(
77        'student_id', 'adm_code', 'suspended',
78        'financially_cleared_by', 'financial_clearance_date')
79
80class StudentBaseEditFormPage(NigeriaStudentBaseEditFormPage):
81    """ View to edit student base data
82    """
83    @property
84    def form_fields(self):
85        form_fields = grok.AutoFields(ICustomStudentBase).select(
86            'email', 'email2', 'parents_email', 'phone',)
87        if not self.context.state in (ADMITTED, CLEARANCE):
88            form_fields['parents_email'].for_display = True
89        return form_fields
90
91class CustomExportPDFCourseRegistrationSlip(
92    NigeriaExportPDFCourseRegistrationSlip):
93    """Deliver a PDF slip of the context.
94    """
95
96    def _signatures(self):
97        return (
98                ['Student Signature'],
99                ['HoD / Course Adviser Signature'],
100                ['College Officer Signature'],
101                ['Dean Signature']
102                )
103
104    #def _sigsInFooter(self):
105    #    return (_('Student'),
106    #            _('HoD / Course Adviser'),
107    #            _('College Officer'),
108    #            _('Dean'),
109    #            )
110    #    return ()
111
112class CustomStudentPersonalDisplayFormPage(NigeriaStudentPersonalDisplayFormPage):
113    """ Page to display student personal data
114    """
115    form_fields = grok.AutoFields(ICustomStudentPersonal)
116    form_fields['perm_address'].custom_widget = BytesDisplayWidget
117    form_fields['postal_address'].custom_widget = BytesDisplayWidget
118    form_fields['hostel_address'].custom_widget = BytesDisplayWidget
119    form_fields['father_address'].custom_widget = BytesDisplayWidget
120    form_fields['mother_address'].custom_widget = BytesDisplayWidget
121    form_fields['guardian_address'].custom_widget = BytesDisplayWidget
122    form_fields[
123        'personal_updated'].custom_widget = FriendlyDatetimeDisplayWidget('le')
124
125
126class CustomStudentPersonalEditFormPage(NigeriaStudentPersonalEditFormPage):
127    """ Page to edit personal data
128    """
129    form_fields = grok.AutoFields(ICustomStudentPersonalEdit).omit(
130        'personal_updated')
131
132    def update(self):
133        #if not self.context.is_fresh:
134        #    self.flash('Not allowed.', type="danger")
135        #    self.redirect(self.url(self.context))
136        #    return
137        if not self.context.minimumStudentPayments():
138            self.flash('Please make 40% of your tution fee payments first.',
139                       type="warning")
140            self.redirect(self.url(self.context, 'view_personal'))
141            return
142        super(CustomStudentPersonalEditFormPage, self).update()
143        return
144
145
146class CustomStudentPersonalManageFormPage(NigeriaStudentPersonalManageFormPage):
147    """ Page to manage personal data
148    """
149    form_fields = grok.AutoFields(ICustomStudentPersonal)
150    form_fields['personal_updated'].for_display = True
151    form_fields[
152        'personal_updated'].custom_widget = FriendlyDatetimeDisplayWidget('le')
153
154class CustomExportPDFPersonalDataSlip(ExportPDFPersonalDataSlip):
155    """Deliver a PDF base and personal data slip.
156    """
157    grok.name('course_registration_clearance.pdf')
158    omit_fields = (
159        'phone', 'email',
160        'suspended',
161        'adm_code', 'suspended_comment',
162        'current_level',
163        'flash_notice', 'entry_session',
164        'parents_email')
165
166    form_fields = grok.AutoFields(ICustomStudentPersonal)
167
168    def _signatures(self):
169        return ([('I certify that the above named student has satisfied the financial requirements for registration.', 'Name and Signature of Bursary Staff', '<br><br>')],
170                [('I certify that the credentials of the student have been screened by me and the student is hereby cleared.', 'Name and Signature of Registry Staff', '<br><br>')],
171                [('I certify that the above named student has registered with the Library.', 'Name and Signature of Library Staff', '<br><br>')],
172                [('I certify that the above named student has been registered with the college. ', 'Name and Signature of College Officer', '<br><br>')],
173                [('I certify that the above named student has completed his/her ICT registration. ', 'Name and Signature of ICT Staff', '<br><br>')],
174                [('Eligibility/Congratulation Station', 'Name and Signature of Registrar', '')],
175                )
176
177    @property
178    def tabletitle(self):
179        portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
180        tabletitle = []
181        session = self.context.student.current_session
182        tabletitle.append('_PB_Successful %s/%s Session Payments' %(session, session+1))
183        return tabletitle
184
185    def render(self):
186        if not self.context.minimumStudentPayments():
187            self.redirect(self.url(self.context))
188            return
189        studentview = StudentBasePDFFormPage(self.context.student,
190            self.request, self.omit_fields)
191        students_utils = getUtility(IStudentsUtils)
192
193        portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
194        P_ID = translate(_('Payment Id'), 'waeup.kofa', target_language=portal_language)
195        #CD = translate(_('Creation Date'), 'waeup.kofa', target_language=portal_language)
196        PD = translate(_('Payment Date'), 'waeup.kofa', target_language=portal_language)
197        CAT = translate(_('Payment Category'), 'waeup.kofa', target_language=portal_language)
198        ITEM = translate(_('Payment Item'), 'waeup.kofa', target_language=portal_language)
199        AMT = translate(_('Amount (Naira)'), 'waeup.kofa', target_language=portal_language)
200        SSS = translate(_('Payment Session'), 'waeup.kofa', target_language=portal_language)
201        tabledata = []
202        tableheader = []
203        tabledata.append(sorted(
204            [value for value in self.context['payments'].values()
205             if value.p_state in ('paid', 'waived', 'scholarship')
206             and value.p_session >= value.student.current_session],
207             key=lambda value: value.p_session))
208        tableheader.append([(P_ID,'p_id', 4.2),
209                         #(CD,'creation_date', 3),
210                         (PD,'formatted_p_date', 3),
211                         (CAT,'category', 3),
212                         (ITEM, 'p_item', 3),
213                         (AMT, 'amount_auth', 2),
214                         (SSS, 'p_session', 2),
215                         ])
216
217        #watermark_path = os.path.join(
218        #    os.path.dirname(__file__), 'static', 'watermark.pdf')
219        #watermark = open(watermark_path, 'rb')
220        #file_path = os.path.join(
221        #    os.path.dirname(__file__), 'static', 'biodataPage2.pdf')
222        #file = open(file_path, 'rb')
223        #mergefiles = [file,]
224
225        return students_utils.renderPDF(
226            self, 'course_registration_clearance.pdf',
227            self.context.student, studentview,
228            omit_fields=self.omit_fields,
229            signatures=self._signatures(),
230
231            tableheader=tableheader,
232            tabledata=tabledata,
233
234            pagebreak=True,
235        #    mergefiles=mergefiles,
236        #    watermark=watermark
237            )
238
239class CustomAccommodationDisplayFormPage(NigeriaAccommodationDisplayFormPage):
240    """ Page to view bed tickets.
241    """
242    with_hostel_selection = True
243
244class CustomAccommodationManageFormPage(NigeriaAccommodationManageFormPage):
245    """ Page to manage bed tickets.
246    This manage form page is for both students and students officers.
247    """
248    with_hostel_selection = True
249
250class CustomBedTicketAddPage(NigeriaBedTicketAddPage):
251    """ Page to add a bed ticket
252    """
253    with_ac = False
254    with_bedselection = True
255
256class CustomPaymentsManageFormPage(PaymentsManageFormPage):
257    """ Page to manage the student payments. This manage form page is for
258    both students and students officers. IUOkada does not allow students
259    to remove any payment ticket.
260    """
261    grok.template('paymentsmanagepage')
262
263    def _schoolfee_payments_made(self):
264        cs = self.context.student.current_session
265        SF_PAYMENTS = ('schoolfee', 'schoolfee40', 'secondinstal', 'clearance')
266        sf_paid = dict()
267        session = self.context.student.entry_session
268        while session < cs + 2:
269            sf_paid[session] = 0.0
270            session += 1
271        sessions = sf_paid.keys()
272        try:
273            certificate = self.context.student['studycourse'].certificate
274        except (AttributeError, TypeError):
275            return sf_paid, 0, 0
276        total_sf = 0.0
277        if self.context.student in (ADMITTED, CLEARANCE, REQUESTED, CLEARED):
278            sf = getattr(certificate, 'school_fee_1', 0.0)
279        else:
280            sf = getattr(certificate, 'school_fee_2', 0.0)
281        if sf is not None:
282            total_sf = sf
283        brought_fwd = 0.0
284        for ticket in self.context.values():
285            amt = ticket.net_amt
286            if not amt:
287                amt = ticket.amount_auth
288            if ticket.p_category in SF_PAYMENTS and \
289                ticket.p_state == 'paid' and \
290                ticket.p_session in sessions:
291                sf_paid[ticket.p_session] += amt
292            if ticket.p_state != 'paid' and\
293               ticket.p_category == 'brought_fwd':
294                  brought_fwd += ticket.amount_auth
295        return sorted(sf_paid.items(), key=lambda value: value[0]), total_sf, brought_fwd
296
297    def update(self):
298        super(CustomPaymentsManageFormPage, self).update()
299        self.sfp_made = self._schoolfee_payments_made()
300        return
301
302    @property
303    def manage_payments_allowed(self):
304        return checkPermission('waeup.manageStudent', self.context)
305
306class StudentGetMatricNumberPage(UtilityView, grok.View):
307    """ Construct and set the matriculation number.
308    """
309    grok.context(IStudent)
310    grok.name('get_matric_number')
311    grok.require('waeup.manageStudent')
312
313    def update(self):
314        students_utils = getUtility(IStudentsUtils)
315        msg, mnumber = students_utils.setMatricNumber(self.context)
316        if msg:
317            self.flash(msg, type="danger")
318        else:
319            self.flash(_('Matriculation number %s assigned.' % mnumber))
320            self.context.writeLogMessage(self, '%s assigned' % mnumber)
321        self.redirect(self.url(self.context))
322        return
323
324    def render(self):
325        return
326
327class SwitchLibraryAccessView(UtilityView, grok.View):
328    """ Switch the library attribute
329    """
330    grok.context(ICustomStudent)
331    grok.name('switch_library_access')
332    grok.require('waeup.switchLibraryAccess')
333
334    def update(self):
335        if self.context.library:
336            self.context.library = False
337            self.context.writeLogMessage(self, 'library access disabled')
338            self.flash(_('Library access disabled'))
339        else:
340            self.context.library = True
341            self.context.writeLogMessage(self, 'library access enabled')
342            self.flash(_('Library access enabled'))
343        self.redirect(self.url(self.context))
344        return
345
346    def render(self):
347        return
348
349class ExportLibIdCard(UtilityView, grok.View):
350    """Deliver an id card for the library.
351    """
352    grok.context(ICustomStudent)
353    grok.name('lib_idcard.pdf')
354    grok.require('waeup.viewStudent')
355    prefix = 'form'
356
357    label = u"Library Clearance"
358
359    omit_fields = (
360        'suspended', 'email', 'phone',
361        'adm_code', 'suspended_comment',
362        'date_of_birth',
363        'current_mode', 'certificate',
364        'entry_session',
365        'flash_notice')
366
367    form_fields = []
368
369    def _sigsInFooter(self):
370        isStudent = getattr(
371            self.request.principal, 'user_type', None) == 'student'
372        if isStudent:
373            return ''
374        return (_("Date, Reader's Signature"),
375                _("Date, Circulation Librarian's Signature"),
376                )
377
378    def update(self):
379        if not self.context.library:
380            self.flash(_('Forbidden!'), type="danger")
381            self.redirect(self.url(self.context))
382        return
383
384    @property
385    def note(self):
386        return """
387<br /><br /><br /><br /><font size='12'>
388This is to certify that the bearer whose photograph and other details appear
389 overleaf is a registered user of the <b>University Library</b>.
390 The card is not transferable. A replacement fee is charged for a loss,
391 mutilation or otherwise. If found, please, return to the University Library,
392 Igbinedion University, Okada.
393</font>
394
395"""
396        return
397
398    def render(self):
399        studentview = StudentBasePDFFormPage(self.context.student,
400            self.request, self.omit_fields)
401        students_utils = getUtility(IStudentsUtils)
402        return students_utils.renderPDF(
403            self, 'lib_idcard',
404            self.context.student, studentview,
405            omit_fields=self.omit_fields,
406            sigs_in_footer=self._sigsInFooter(),
407            note=self.note)
408
409class CustomStartClearancePage(StartClearancePage):
410    with_ac = False
411
412class CustomBalancePaymentAddFormPage(BalancePaymentAddFormPage):
413    grok.require('waeup.payStudent')
414
415from kofacustom.iuokada.students.admission_letter_ug import (
416    ADML_UG_1, ADML_UG_2, ADML_UG_3, ADML_UG_2_MEDICAL, ADML_UG_2_PHARMACY,
417    BASIC_MEDICAL_ONLY, BASIC_PHARMACY_ONLY)
418from kofacustom.iuokada.students.admission_letter_pg import ADML_PG
419from kofacustom.iuokada.students.admission_letter_pt import ADML_PT
420from kofacustom.iuokada.students.admission_letter_jupeb import ADML_JUPEB
421
422class CustomExportPDFAdmissionSlip(ExportPDFAdmissionSlip):
423    """Deliver a PDF Admission slip.
424    """
425
426    omit_fields = ('date_of_birth',
427                   #'current_level',
428                   #'current_mode',
429                   #'entry_session'
430                   )
431
432    @property
433    def session(self):
434        return academic_sessions_vocab.getTerm(
435            self.context.entry_session).title
436
437    @property
438    def level(self):
439        studylevelsource = StudyLevelSource()
440        return studylevelsource.factory.getTitle(
441            self.context['studycourse'].certificate, self.context.current_level)
442
443    @property
444    def label(self):
445        return 'OFFER OF PROVISIONAL ADMISSION \nFOR %s SESSION' % self.session
446
447    @property
448    def pre_text_ug(self):
449        return (
450            'Following your performance in the screening exercise '
451            'for the %s academic session, I am pleased to inform '
452            'you that you have been offered provisional admission into the '
453            'Igbinedion University, Okada as follows:' % self.session)
454
455    @property
456    def pre_text_pg(self):
457        return (
458            'I am pleased to inform you that your application for admission'
459            ' into the Igbinedion University, Okada was successful. You have'
460            ' been admitted as follows:')
461
462    def render(self):
463        students_utils = getUtility(IStudentsUtils)
464        watermark_path = os.path.join(
465            os.path.dirname(__file__), 'static', 'watermark.pdf')
466        watermark = open(watermark_path, 'rb')
467        if self.context.is_jupeb:
468            return students_utils.renderPDFAdmissionLetter(self,
469                self.context.student, omit_fields=self.omit_fields,
470                pre_text=self.pre_text_ug, post_text=ADML_JUPEB,
471                watermark=watermark)
472        if self.context.current_mode.endswith('_pt'):
473            return students_utils.renderPDFAdmissionLetter(self,
474                self.context.student, omit_fields=self.omit_fields,
475                pre_text=self.pre_text_ug, post_text=ADML_PT,
476                watermark=watermark)
477        if self.context.is_postgrad:
478            file_path = os.path.join(
479                os.path.dirname(__file__), 'static', 'admission_letter_pg.pdf')
480            file = open(file_path, 'rb')
481            mergefiles = [file,]
482            return students_utils.renderPDFAdmissionLetter(self,
483                self.context.student, omit_fields=self.omit_fields,
484                pre_text=self.pre_text_pg, post_text=ADML_PG,
485                mergefiles=mergefiles,
486                watermark=watermark)
487        file_path = os.path.join(
488            os.path.dirname(__file__), 'static', 'admission_letter_ug.pdf')
489        file = open(file_path, 'rb')
490        mergefiles = [file,]
491        if self.context.certcode in ('BBMS', 'MBBS') and self.context.current_level == 100:
492            post_text = BASIC_MEDICAL_ONLY + ADML_UG_1 + ADML_UG_2_MEDICAL + ADML_UG_3
493        elif self.context.certcode in ('BPHARM',) and self.context.current_level == 100:
494            post_text = BASIC_PHARMACY_ONLY + ADML_UG_1 + ADML_UG_2_MEDICAL + ADML_UG_3
495        else:
496            post_text = ADML_UG_1 + ADML_UG_2 + ADML_UG_3
497        return students_utils.renderPDFAdmissionLetter(self,
498            self.context.student, omit_fields=self.omit_fields,
499            pre_text=self.pre_text_ug, post_text=post_text,
500            mergefiles=mergefiles,
501            watermark=watermark)
502
503class CustomOnlinePaymentDisplayFormPage(NigeriaOnlinePaymentDisplayFormPage):
504    """ Page to view an online payment ticket. We do not omit provider_amt.
505    """
506    form_fields = grok.AutoFields(ICustomStudentOnlinePayment).omit(
507        'gateway_amt', 'thirdparty_amt', 'p_item','p_combi', 'provider_amt')
508    form_fields[
509        'creation_date'].custom_widget = FriendlyDatetimeDisplayWidget('le')
510    form_fields[
511        'payment_date'].custom_widget = FriendlyDatetimeDisplayWidget('le')
512
513class CustomExportPDFPaymentSlip(NigeriaExportPDFPaymentSlip):
514    """Deliver a PDF slip of the context. We do not omit provider_amt.
515    """
516    form_fields = grok.AutoFields(ICustomStudentOnlinePayment).omit(
517        'gateway_amt', 'thirdparty_amt', 'p_item',
518        'p_split_data','p_combi', 'provider_amt')
519    form_fields['creation_date'].custom_widget = FriendlyDatetimeDisplayWidget('le')
520    form_fields['payment_date'].custom_widget = FriendlyDatetimeDisplayWidget('le')
Note: See TracBrowser for help on using the repository browser.