source: main/kofacustom.nigeria/trunk/src/kofacustom/nigeria/students/browser.py @ 17929

Last change on this file since 17929 was 17866, checked in by Henrik Bettermann, 3 months ago

Implement final clearance.

  • Property svn:keywords set to Id
File size: 17.7 KB
RevLine 
[8862]1## $Id: browser.py 17866 2024-08-01 11:02:51Z 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
19from zope.formlib.textwidgets import BytesDisplayWidget
20from zope.component import getUtility
[13856]21from zope.security import checkPermission
[8862]22from zope.i18n import translate
[13620]23from datetime import datetime
[8862]24from waeup.kofa.widgets.datewidget import FriendlyDatetimeDisplayWidget
[13623]25from waeup.kofa.interfaces import IExtFileStore, IObjectHistory, IKofaUtils
[13620]26from waeup.kofa.browser.layout import action, UtilityView
[13623]27from waeup.kofa.utils.helpers import get_current_principal, to_timezone
[8862]28from waeup.kofa.students.browser import (
[8904]29    StudentPersonalDisplayFormPage, StudentPersonalManageFormPage,
[8862]30    StudentClearanceManageFormPage, StudentClearanceEditFormPage,
[13058]31    StudentClearanceDisplayFormPage, OnlinePaymentFakeApproveView,
32    ExportPDFClearanceSlip, StudentBaseManageFormPage,
[8966]33    StudentBaseDisplayFormPage,
[13623]34    StudentBasePDFFormPage,
[8862]35    StudentBaseEditFormPage, StudentPersonalEditFormPage,
36    OnlinePaymentDisplayFormPage, OnlinePaymentAddFormPage,
[13058]37    OnlinePaymentBreadcrumb, ExportPDFPaymentSlip,
38    ExportPDFCourseRegistrationSlip,
39    ExportPDFBedTicketSlip,
[13458]40    StudentFilesUploadPage, emit_lock_message,
[15711]41    AccommodationManageFormPage,
[15973]42    AccommodationDisplayFormPage,
[15711]43    BedTicketAddPage)
[10707]44from waeup.kofa.students.interfaces import IStudentsUtils
[8862]45from kofacustom.nigeria.students.interfaces import (
[8863]46    INigeriaStudentBase, INigeriaStudent, INigeriaStudentPersonal,
[9564]47    INigeriaStudentPersonalEdit,
[8863]48    INigeriaUGStudentClearance,INigeriaPGStudentClearance,
[8904]49    INigeriaStudentOnlinePayment
[8862]50    )
51from waeup.kofa.students.workflow import ADMITTED
52from kofacustom.nigeria.interfaces import MessageFactory as _
53
[8863]54class NigeriaOnlinePaymentBreadcrumb(OnlinePaymentBreadcrumb):
[8862]55    """A breadcrumb for payments.
56    """
[8863]57    grok.context(INigeriaStudentOnlinePayment)
[8862]58
[8966]59class NigeriaStudentBaseDisplayFormPage(StudentBaseDisplayFormPage):
60    """ Page to display student base data
61    """
[9704]62    form_fields = grok.AutoFields(INigeriaStudentBase).omit(
[14274]63        'password', 'suspended', 'suspended_comment',
64        'flash_notice', 'provisionally_cleared')
[13620]65    form_fields[
66        'financial_clearance_date'].custom_widget = FriendlyDatetimeDisplayWidget('le')
[17866]67    form_fields[
68        'final_clearance_date'].custom_widget = FriendlyDatetimeDisplayWidget('le')
[8966]69
[8863]70class NigeriaStudentBaseManageFormPage(StudentBaseManageFormPage):
[8862]71    """ View to manage student base data
72    """
[9144]73    form_fields = grok.AutoFields(INigeriaStudentBase).omit(
[13620]74        'student_id', 'adm_code', 'suspended',
[17866]75        'financially_cleared_by', 'financial_clearance_date',
76        'finally_cleared_by', 'final_clearance_date')
[8862]77
[8863]78class NigeriaStudentBaseEditFormPage(StudentBaseEditFormPage):
[8862]79    """ View to edit student base data
80    """
[8863]81    form_fields = grok.AutoFields(INigeriaStudentBase).select(
[8862]82        'email', 'phone')
83
[8863]84class NigeriaStudentPersonalDisplayFormPage(StudentPersonalDisplayFormPage):
[8862]85    """ Page to display student personal data
86    """
[9562]87    form_fields = grok.AutoFields(INigeriaStudentPersonal)
[8862]88    form_fields['perm_address'].custom_widget = BytesDisplayWidget
[9053]89    form_fields['next_kin_address'].custom_widget = BytesDisplayWidget
[9562]90    form_fields[
91        'personal_updated'].custom_widget = FriendlyDatetimeDisplayWidget('le')
[8862]92
[8863]93class NigeriaStudentPersonalEditFormPage(StudentPersonalEditFormPage):
[8862]94    """ Page to edit personal data
95    """
[9564]96    form_fields = grok.AutoFields(INigeriaStudentPersonalEdit).omit('personal_updated')
[8862]97
[8904]98class NigeriaStudentPersonalManageFormPage(StudentPersonalManageFormPage):
99    """ Page to edit personal data
100    """
101    form_fields = grok.AutoFields(INigeriaStudentPersonal)
[9554]102    form_fields['personal_updated'].for_display = True
[9572]103    form_fields[
104        'personal_updated'].custom_widget = FriendlyDatetimeDisplayWidget('le')
[8904]105
[8863]106class NigeriaStudentClearanceDisplayFormPage(StudentClearanceDisplayFormPage):
[8862]107    """ Page to display student clearance data
108    """
109
110    @property
111    def form_fields(self):
[9248]112        if self.context.is_postgrad:
[8862]113            form_fields = grok.AutoFields(
[8863]114                INigeriaPGStudentClearance).omit('clearance_locked')
[8862]115        else:
116            form_fields = grok.AutoFields(
[8863]117                INigeriaUGStudentClearance).omit('clearance_locked')
[9535]118        if not getattr(self.context, 'officer_comment'):
119            form_fields = form_fields.omit('officer_comment')
120        else:
121            form_fields['officer_comment'].custom_widget = BytesDisplayWidget
[8862]122        return form_fields
123
[13058]124class NigeriaExportPDFClearanceSlip(ExportPDFClearanceSlip):
[8862]125    """Deliver a PDF slip of the context.
126    """
[9704]127    omit_fields = ('password', 'suspended', 'suspended_comment',
[13712]128        'phone', 'adm_code', 'email', 'date_of_birth', 'current_level',
129        'flash_notice')
[8862]130
131    @property
132    def form_fields(self):
[9248]133        if self.context.is_postgrad:
[8862]134            form_fields = grok.AutoFields(
[8863]135                INigeriaPGStudentClearance).omit('clearance_locked')
[8862]136        else:
137            form_fields = grok.AutoFields(
[8863]138                INigeriaUGStudentClearance).omit('clearance_locked')
[9535]139        if not getattr(self.context, 'officer_comment'):
140            form_fields = form_fields.omit('officer_comment')
[8862]141        return form_fields
142
[8863]143class NigeriaStudentClearanceManageFormPage(StudentClearanceManageFormPage):
[8862]144    """ Page to edit student clearance data
145    """
146
147    @property
148    def form_fields(self):
[9248]149        if self.context.is_postgrad:
[9249]150            form_fields = grok.AutoFields(
151                INigeriaPGStudentClearance).omit('clr_code')
[8862]152        else:
[9249]153            form_fields = grok.AutoFields(
154                INigeriaUGStudentClearance).omit('clr_code')
[8862]155        return form_fields
156
[8863]157class NigeriaStudentClearanceEditFormPage(StudentClearanceEditFormPage):
[8862]158    """ View to edit student clearance data by student
159    """
160
161    @property
162    def form_fields(self):
[9248]163        if self.context.is_postgrad:
[8946]164            form_fields = grok.AutoFields(INigeriaPGStudentClearance).omit(
[12107]165            'clearance_locked', 'nysc_location', 'clr_code', 'officer_comment',
[12120]166            'physical_clearance_date')
[8862]167        else:
[8946]168            form_fields = grok.AutoFields(INigeriaUGStudentClearance).omit(
[12107]169            'clearance_locked', 'clr_code', 'officer_comment',
[12120]170            'physical_clearance_date')
[8862]171        return form_fields
172
[13058]173class NigeriaExportPDFCourseRegistrationSlip(ExportPDFCourseRegistrationSlip):
[9376]174    """Deliver a PDF slip of the context.
175    """
[9704]176    omit_fields = ('password', 'suspended', 'suspended_comment',
[13712]177        'phone', 'adm_code', 'sex', 'email', 'date_of_birth', 'current_level',
178        'flash_notice')
[9376]179
[8863]180class NigeriaOnlinePaymentDisplayFormPage(OnlinePaymentDisplayFormPage):
[8862]181    """ Page to view an online payment ticket
182    """
[8863]183    grok.context(INigeriaStudentOnlinePayment)
[9774]184    form_fields = grok.AutoFields(INigeriaStudentOnlinePayment).omit(
[15687]185        'provider_amt', 'gateway_amt', 'thirdparty_amt', 'p_item','p_combi')
[8862]186    form_fields[
187        'creation_date'].custom_widget = FriendlyDatetimeDisplayWidget('le')
188    form_fields[
189        'payment_date'].custom_widget = FriendlyDatetimeDisplayWidget('le')
190
[8863]191class NigeriaOnlinePaymentAddFormPage(OnlinePaymentAddFormPage):
[8862]192    """ Page to add an online payment ticket
193    """
[8863]194    form_fields = grok.AutoFields(INigeriaStudentOnlinePayment).select(
[15665]195        'p_combi')
[8862]196
[13058]197class NigeriaOnlinePaymentFakeApproveView(OnlinePaymentFakeApproveView):
[8862]198    """ Disable payment approval view for students.
199
200    This view is used for browser tests only and
201    has to be neutralized here!
202    """
203    grok.name('fake_approve')
204    grok.require('waeup.managePortal')
205
206    def update(self):
207        return
208
[13058]209class NigeriaExportPDFPaymentSlip(ExportPDFPaymentSlip):
[8862]210    """Deliver a PDF slip of the context.
211    """
[8863]212    grok.context(INigeriaStudentOnlinePayment)
[9774]213    form_fields = grok.AutoFields(INigeriaStudentOnlinePayment).omit(
[15470]214        'provider_amt', 'gateway_amt', 'thirdparty_amt', 'p_item',
[15687]215        'p_split_data','p_combi')
[8862]216    form_fields['creation_date'].custom_widget = FriendlyDatetimeDisplayWidget('le')
217    form_fields['payment_date'].custom_widget = FriendlyDatetimeDisplayWidget('le')
[9704]218    omit_fields = ('password', 'suspended', 'suspended_comment', 'phone',
[13712]219        'adm_code', 'sex', 'email', 'date_of_birth', 'current_level',
220        'flash_notice')
[8862]221
[15796]222    def update(self):
[15846]223        if not self.context.p_state in ('paid', 'waived', 'scholarship') \
[15844]224            and not self.context.r_company:
[15796]225            self.redirect(self.url(self.context))
226            return
227        return
228
[15973]229class NigeriaAccommodationDisplayFormPage(AccommodationDisplayFormPage):
[15974]230    """ Page to view bed tickets.
[15973]231    """
232    with_hostel_selection = False
233
[13458]234class NigeriaAccommodationManageFormPage(AccommodationManageFormPage):
235    """ Page to manage bed tickets.
236    This manage form page is for both students and students officers.
237    """
238    with_hostel_selection = False
239
[15711]240class NigeriaBedTicketAddPage(BedTicketAddPage):
241    """ Page to add a bed ticket
242    """
243    with_ac = True
244    with_bedselection = False
245
[13058]246class NigeriaExportPDFBedTicketSlip(ExportPDFBedTicketSlip):
[9376]247    """Deliver a PDF slip of the context.
248    """
[9704]249    omit_fields = ('password', 'suspended', 'suspended_comment',
[13712]250        'phone', 'adm_code', 'email', 'date_of_birth', 'current_level',
251        'flash_notice')
[9376]252
[13620]253class ClearStudentFinancially(UtilityView, grok.View):
[13634]254    """ Clear student financially by financial clearance officer
[13620]255    """
256    grok.context(INigeriaStudent)
257    grok.name('clear_financially')
258    grok.require('waeup.clearStudentFinancially')
259
260    def update(self):
261        if self.context.financially_cleared_by:
262            self.flash(_('This student has already been financially cleared.'),
263                       type="danger")
264            self.redirect(self.url(self.context))
265            return
266        user = get_current_principal()
267        if user is None:
268            usertitle = 'system'
269        else:
270            usertitle = getattr(user, 'public_name', None)
271            if not usertitle:
272                usertitle = user.title
273        self.context.financially_cleared_by = usertitle
274        self.context.financial_clearance_date = datetime.utcnow()
275        self.context.writeLogMessage(self,'financially cleared')
276        history = IObjectHistory(self.context)
277        history.addMessage('Financially cleared')
278        self.flash(_('Student has been financially cleared.'))
279        self.redirect(self.url(self.context))
280        return
281
282    def render(self):
283        return
284
285class WithdrawFinancialClearance(UtilityView, grok.View):
[13634]286    """ Withdraw financial clearance by financial clearance officer
[13620]287    """
288    grok.context(INigeriaStudent)
289    grok.name('withdraw_financial_clearance')
290    grok.require('waeup.clearStudentFinancially')
291
292    def update(self):
293        if not self.context.financially_cleared_by:
294            self.flash(_('This student has not yet been financially cleared.'),
295                       type="danger")
296            self.redirect(self.url(self.context))
297            return
298        self.context.financially_cleared_by = None
299        self.context.financial_clearance_date = None
300        self.context.writeLogMessage(self,'financial clearance withdrawn')
301        history = IObjectHistory(self.context)
302        history.addMessage('Financial clearance withdrawn')
303        self.flash(_('Financial clearance withdrawn.'))
304        self.redirect(self.url(self.context))
305        return
306
307    def render(self):
[13623]308        return
309
310cleared_note = """
311<br /><br /><br />
312<strong>Financially cleared on %s by %s.</strong>
313
314"""
315
[13634]316class NigeriaExportPDFFinancialClearancePage(UtilityView, grok.View):
317    """Deliver a PDF financial clearance slip.
[13623]318    """
319    grok.context(INigeriaStudent)
[13781]320    grok.name('fee_payment_history.pdf')
[13623]321    grok.require('waeup.viewStudent')
322    prefix = 'form'
323
324    omit_fields = (
325        'suspended', 'phone',
326        'adm_code', 'suspended_comment',
[13712]327        'date_of_birth', 'current_level',
328        'flash_notice')
[13623]329
330    form_fields = None
331
332    @property
333    def label(self):
334        portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
[13787]335        return translate(_('Fee Payment History for'),
[13623]336            'waeup.kofa', target_language=portal_language) \
337            + ' %s' % self.context.display_fullname
338
339    def _sigsInFooter(self):
[14163]340        if not checkPermission('waeup.clearStudentFinancially', self.context):
341            return ()
[13642]342        return (_('Date, Checking Officer Signature'),
343                _('Date, Approving Officer Signature'),
[13623]344                )
345
346    @property
347    def note(self):
348        if self.context.financially_cleared_by:
349            tz = getUtility(IKofaUtils).tzinfo
350            try:
351                timestamp = to_timezone(
352                    self.context.financial_clearance_date, tz).strftime(
353                        "%Y-%m-%d %H:%M:%S")
354            except ValueError:
[13760]355                return
[13623]356            return cleared_note % (
357                timestamp, self.context.financially_cleared_by)
[13760]358        return
[13623]359
360    @property
361    def tabletitle(self):
362        portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
363        tabletitle = []
364        tabletitle.append(translate(_('Successful Payments'), 'waeup.kofa',
365            target_language=portal_language))
366        return tabletitle
367
368    def render(self):
369        portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
370        P_ID = translate(_('Payment Id'), 'waeup.kofa', target_language=portal_language)
371        #CD = translate(_('Creation Date'), 'waeup.kofa', target_language=portal_language)
372        PD = translate(_('Payment Date'), 'waeup.kofa', target_language=portal_language)
373        CAT = translate(_('Payment Category'), 'waeup.kofa', target_language=portal_language)
374        ITEM = translate(_('Payment Item'), 'waeup.kofa', target_language=portal_language)
375        AMT = translate(_('Amount (Naira)'), 'waeup.kofa', target_language=portal_language)
376        SSS = translate(_('Payment Session'), 'waeup.kofa', target_language=portal_language)
377        studentview = StudentBasePDFFormPage(self.context.student,
378            self.request, self.omit_fields)
379        students_utils = getUtility(IStudentsUtils)
380
381        tabledata = []
382        tableheader = []
383        tabledata.append(sorted(
384            [value for value in self.context['payments'].values()
[15842]385             if value.p_state in ('paid', 'waived', 'scholarship')],
386             key=lambda value: value.p_session))
[13634]387        tableheader.append([(P_ID,'p_id', 4.2),
[13623]388                         #(CD,'creation_date', 3),
389                         (PD,'formatted_p_date', 3),
390                         (CAT,'category', 3),
391                         (ITEM, 'p_item', 3),
392                         (AMT, 'amount_auth', 2),
393                         (SSS, 'p_session', 2),
394                         ])
395        return students_utils.renderPDF(
[13634]396            self, 'financial_clearance_slip.pdf',
[13623]397            self.context.student, studentview,
398            tableheader=tableheader,
399            tabledata=tabledata,
400            signatures=None,
401            sigs_in_footer=self._sigsInFooter(),
402            omit_fields=self.omit_fields,
403            note=self.note
[17866]404            )
405
406class ClearStudentFinally(UtilityView, grok.View):
407    """ Clear student finally by final clearance officer
408    """
409    grok.context(INigeriaStudent)
410    grok.name('clear_finally')
411    grok.require('waeup.clearStudentFinally')
412
413    def update(self):
414        if self.context.finally_cleared_by:
415            self.flash(_('This student has already been finally cleared.'),
416                       type="danger")
417            self.redirect(self.url(self.context))
418            return
419        user = get_current_principal()
420        if user is None:
421            usertitle = 'system'
422        else:
423            usertitle = getattr(user, 'public_name', None)
424            if not usertitle:
425                usertitle = user.title
426        self.context.finally_cleared_by = usertitle
427        self.context.final_clearance_date = datetime.utcnow()
428        self.context.writeLogMessage(self,'finally cleared')
429        history = IObjectHistory(self.context)
430        history.addMessage('Finally cleared')
431        self.flash(_('Student has been finally cleared.'))
432        self.redirect(self.url(self.context))
433        return
434
435    def render(self):
436        return
437
438class WithdrawFinalClearance(UtilityView, grok.View):
439    """ Withdraw final clearance by final clearance officer
440    """
441    grok.context(INigeriaStudent)
442    grok.name('withdraw_final_clearance')
443    grok.require('waeup.clearStudentFinally')
444
445    def update(self):
446        if not self.context.finally_cleared_by:
447            self.flash(_('This student has not yet been finally cleared.'),
448                       type="danger")
449            self.redirect(self.url(self.context))
450            return
451        self.context.finally_cleared_by = None
452        self.context.final_clearance_date = None
453        self.context.writeLogMessage(self,'final clearance withdrawn')
454        history = IObjectHistory(self.context)
455        history.addMessage('Final clearance withdrawn')
456        self.flash(_('Final clearance withdrawn.'))
457        self.redirect(self.url(self.context))
458        return
459
460    def render(self):
461        return
462
463cleared_note = """
464<br /><br /><br />
465<strong>Finally cleared on %s by %s.</strong>
466
467"""
Note: See TracBrowser for help on using the repository browser.