Index: main/waeup.sirp/trunk/src/waeup/sirp/students/browser.py
===================================================================
--- main/waeup.sirp/trunk/src/waeup/sirp/students/browser.py	(revision 7149)
+++ main/waeup.sirp/trunk/src/waeup/sirp/students/browser.py	(revision 7150)
@@ -45,5 +45,5 @@
     IStudentAccommodation, IStudentClearanceEdit, IStudentStudyLevel,
     ICourseTicket, ICourseTicketAdd, IStudentPaymentsContainer,
-    IStudentOnlinePayment, IBedTicket
+    IStudentOnlinePayment, IBedTicket, IStudentsUtils
     )
 from waeup.sirp.students.catalog import search
@@ -52,7 +52,4 @@
 from waeup.sirp.students.studylevel import StudentStudyLevel, CourseTicket
 from waeup.sirp.students.vocabularies import StudyLevelSource
-from waeup.sirp.students.utils import (
-    get_payment_details, get_accommodation_details, select_bed,
-    render_pdf)
 from waeup.sirp.browser.resources import toggleall
 from waeup.sirp.authentication import get_principal_role_manager
@@ -667,5 +664,7 @@
         studentview = StudentBaseDisplayFormPage(self.context.getStudent(),
             self.request)
-        return render_pdf(self,'Course Registration', 'course_registration.pdf',
+        students_utils = getUtility(IStudentsUtils)
+        return students_utils.render_pdf(
+            self,'Course Registration', 'course_registration.pdf',
             self.context.getStudent, studentview)
 
@@ -939,8 +938,4 @@
     pnav = 4
     
-    # To be sepezified in customization packages
-    def get_payment_details(self, category, student):
-        return get_payment_details(category, student)
-
     @grok.action('Create ticket')
     def createTicket(self, **data):
@@ -954,5 +949,6 @@
                 self.redirect(self.url(self.context))
                 return
-        pay_details  = self.get_payment_details(
+        students_utils = getUtility(IStudentsUtils)
+        pay_details  = students_utils.get_payment_details(
             p_category,student)
         if pay_details['error']:
@@ -1108,5 +1104,6 @@
         studentview = StudentBaseDisplayFormPage(self.context.getStudent(),
             self.request)
-        return render_pdf(self,'Payment', 'payment_receipt.pdf',
+        students_utils = getUtility(IStudentsUtils)
+        return students_utils.render_pdf(self,'Payment', 'payment_receipt.pdf',
             self.context.getStudent, studentview)
 
@@ -1217,15 +1214,8 @@
     notice = ''
 
-    # To be sepezified in customization packages
-    def get_accommodation_details(self, student):
-        return get_accommodation_details(student)
-
-    # To be sepezified in customization packages
-    def select_bed(self, available_beds):
-        return select_bed(available_beds)
-
     def update(self, SUBMIT=None):
         student = self.context.getStudent()
-        acc_details  = self.get_accommodation_details(student)
+        students_utils = getUtility(IStudentsUtils)
+        acc_details  = students_utils.get_accommodation_details(student)
         if not student.state in acc_details['allowed_states']:
             self.flash("You are in the wrong registration state.")
@@ -1264,5 +1254,6 @@
                 entry for entry in entries if entry.owner == NOT_OCCUPIED]
             if available_beds:
-                bed = self.select_bed(available_beds)
+                students_utils = getUtility(IStudentsUtils)
+                bed = students_utils.select_bed(available_beds)
                 bed.bookBed(student.student_id)
             else:
@@ -1345,5 +1336,7 @@
         studentview = StudentBaseDisplayFormPage(self.context.getStudent(),
             self.request)
-        return render_pdf(self,'Bed Allocation', 'bed_allocation.pdf',
+        students_utils = getUtility(IStudentsUtils)
+        return students_utils.render_pdf(
+            self,'Bed Allocation', 'bed_allocation.pdf',
             self.context.getStudent, studentview)
 
@@ -1364,17 +1357,10 @@
     grok.require('waeup.manageHostels')
 
-    # To be sepezified in customization packages
-    def get_accommodation_details(self, student):
-        return get_accommodation_details(student)
-
-    # To be sepezified in customization packages
-    def select_bed(self, available_beds):
-        return select_bed(available_beds)
-
     # Relocate student if student parameters have changed or the bed_type
     # of the bed has changed
     def update(self):
         student = self.context.getStudent()
-        acc_details  = self.get_accommodation_details(student)
+        students_utils = getUtility(IStudentsUtils)
+        acc_details  = students_utils.get_accommodation_details(student)
         if self.context.bed != None and \
               'reserved' in self.context.bed.bed_type:
@@ -1403,5 +1389,6 @@
                 entry for entry in entries if entry.owner == NOT_OCCUPIED]
             if available_beds:
-                new_bed = self.select_bed(available_beds)
+                students_utils = getUtility(IStudentsUtils)
+                new_bed = students_utils.select_bed(available_beds)
                 new_bed.bookBed(student.student_id)
             else:
Index: main/waeup.sirp/trunk/src/waeup/sirp/students/interfaces.py
===================================================================
--- main/waeup.sirp/trunk/src/waeup/sirp/students/interfaces.py	(revision 7149)
+++ main/waeup.sirp/trunk/src/waeup/sirp/students/interfaces.py	(revision 7150)
@@ -3,5 +3,5 @@
 import re
 from datetime import datetime
-from zope.interface import Attribute, invariant
+from zope.interface import Attribute, invariant, Interface
 from zope.interface.exceptions import Invalid
 from zope import schema
@@ -26,9 +26,35 @@
     return True
 
+class IStudentsUtils(Interface):
+    """A collection of methods which are subject to customization.
+
+    """
+    def get_payment_details(category, student):
+        """Get the payment dates of a student for the payment category
+        specified.
+
+        """
+
+    def get_accommodation_details(student):
+        """Determine the accommodation dates of a student.
+
+        """
+
+    def select_bed(available_beds):
+        """Select a bed from a list of available beds.
+
+        In the standard configuration we select the first bed found,
+        but can also randomize the selection if we like.
+        """
+
+    def render_pdf(view, subject='', filename='slip.pdf',):
+        """Render pdf slips for various pages.
+
+        """
+
 class IStudentsContainer(IWAeUPObject):
     """A students container contains university students.
 
     """
-
     def addStudent(student):
         """Add an IStudent object and subcontainers.
@@ -68,12 +94,14 @@
 class IStudentNavigation(IWAeUPObject):
     """Interface needed for student navigation.
-    """
-
+
+    """
     def getStudent():
         """Return student object.
+
         """
 
 class IStudentBase(IWAeUPObject):
     """Representation of student base data.
+
     """
     history = Attribute('Object history, a list of messages')
@@ -84,5 +112,6 @@
 
     def loggerInfo(ob_class, comment):
-        """Adds an INFO message to the log file
+        """Adds an INFO message to the log file.
+
         """
 
@@ -141,6 +170,6 @@
 class IStudentClearance(IWAeUPObject):
     """Representation of student clearance data.
-    """
-
+
+    """
     date_of_birth = schema.Date(
         title = u'Date of Birth',
@@ -162,6 +191,6 @@
 class IStudentPersonal(IWAeUPObject):
     """Representation of student personal data.
-    """
-
+
+    """
     perm_address = schema.Text(
         title = u'Permanent Address',
@@ -171,10 +200,11 @@
 class IStudent(IStudentBase,IStudentClearance,IStudentPersonal):
     """Representation of a student.
+
     """
 
 class IStudentUpdateByRegNo(IStudent):
     """Representation of a student. Skip regular reg_number validation.
-    """
-
+
+    """
     reg_number = schema.TextLine(
         title = u'Registration Number',
@@ -185,6 +215,6 @@
 class IStudentUpdateByMatricNo(IStudent):
     """Representation of a student. Skip regular matric_number validation.
-    """
-
+
+    """
     matric_number = schema.TextLine(
         title = u'Matriculation Number',
@@ -197,5 +227,4 @@
 
     """
-
     certificate = schema.Choice(
         title = u'Certificate',
@@ -256,5 +285,4 @@
 
     """
-
     current_level = schema.Int(
         title = u'Current Level',
@@ -318,5 +346,5 @@
 
 class ICourseTicketAdd(ICourseTicket):
-    """An interface for adding course tickets
+    """An interface for adding course tickets.
 
     """
@@ -336,5 +364,4 @@
 
     """
-
     bed = Attribute('The bed object.')
 
@@ -375,5 +402,6 @@
 
     def getSessionString():
-        """Returns the the title of academic_sessions_vocab term
+        """Returns the the title of academic_sessions_vocab term.
+
         """
 
@@ -387,5 +415,4 @@
 
     """
-
     p_session = schema.Choice(
         title = u'Payment Session',
@@ -401,7 +428,9 @@
 class IStudentClearanceEdit(IStudentClearance):
     """Interface needed for restricted editing of student clearance data.
+
     """
 
 class IStudentPersonalEdit(IStudentPersonal):
     """Interface needed for restricted editing of student personal data.
-    """
+
+    """
Index: main/waeup.sirp/trunk/src/waeup/sirp/students/utils.py
===================================================================
--- main/waeup.sirp/trunk/src/waeup/sirp/students/utils.py	(revision 7149)
+++ main/waeup.sirp/trunk/src/waeup/sirp/students/utils.py	(revision 7150)
@@ -1,5 +1,5 @@
 """General helper functions for the student section.
 """
-from grok import getSite
+import grok
 from random import SystemRandom as r
 from datetime import date, datetime
@@ -13,4 +13,5 @@
 from zope.formlib.form import setUpEditWidgets
 from waeup.sirp.interfaces import academic_sessions_vocab
+from waeup.sirp.students.interfaces import IStudentsUtils
 
 SLIP_STYLE = TableStyle(
@@ -33,67 +34,4 @@
     student['studycourse'].previous_verdict = verdict
     return
-
-# To be specified in customization packages, see also the view which
-# calls the function.
-def get_payment_details(category, student):
-    d = {}
-    d['p_item'] = u''
-    d['amount'] = 0
-    d['error'] = u''
-    d['p_session'] = student['studycourse'].current_session
-    session = str(d['p_session'])
-    try:
-        academic_session = getSite()['configuration'][session]
-    except KeyError:
-        d['error'] = u'Session configuration object is not available.'
-        return d
-    d['surcharge_1'] = academic_session.surcharge_1
-    d['surcharge_2'] = academic_session.surcharge_2
-    d['surcharge_3'] = academic_session.surcharge_3
-    if category == 'schoolfee':
-        d['amount'] = academic_session.school_fee_base
-        d['p_item'] = student['studycourse'].certificate.code
-    elif category == 'clearance':
-        d['p_item'] = student['studycourse'].certificate.code
-        d['amount'] = academic_session.clearance_fee
-    elif category == 'bed_allocation':
-        d['p_item'] = get_accommodation_details(student)['bt']
-        d['amount'] = academic_session.booking_fee
-    return d
-
-# To be specified in customization packages, see also the view which
-# calls the function.
-def get_accommodation_details(student):
-    d = {}
-    d['error'] = u''
-    site_confoguration = getSite()['configuration']
-    d['booking_session'] = site_confoguration.accommodation_session
-    d['allowed_states'] = site_confoguration.accommodation_states
-    session = str(d['booking_session'])
-    # Determine bed type
-    studycourse = student['studycourse']
-    entry_session = studycourse.entry_session
-    current_level = studycourse.current_level
-    end_level = studycourse.certificate.end_level
-    if entry_session == getSite()['configuration'].accommodation_session:
-        bt = 'fr'
-    elif current_level >= end_level:
-        bt = 'fi'
-    else:
-        bt = 're'
-    if student.sex == 'f':
-        sex = 'female'
-    else:
-        sex = 'male'
-    special_handling = 'regular'
-    d['bt'] = u'%s_%s_%s' % (special_handling,sex,bt)
-    return d
-
-# To be specified in customization packages, see also view which
-# calls the function.
-# I the standard configuration we select the first bed found,
-# but can also randomize the selection if we like.
-def select_bed(available_beds):
-    return available_beds[0]
 
 def setUpWidgets(view, ignore_request=False):
@@ -120,58 +58,119 @@
     return table
 
-# To be specified in customization packages, see also view which
-# calls the function.
-def render_pdf(view, subject='', filename='slip.pdf', student=None, studentview=None):
-    # (0,0),(-1,-1) = whole table
-    # (0,0),(0,-1) = first column
-    # (-1,0),(-1,-1) = last column
-    # (0,0),(-1,0) = first row
-    # (0,-1),(-1,-1) = last row
+class StudentsUtils(grok.GlobalUtility):
+    """A collection of methods subject to customization.
+    """
+    grok.implements(IStudentsUtils)
 
-    pdf = canvas.Canvas(filename,pagesize=A4)
-    pdf.setTitle(view.label)
-    pdf.setSubject(subject)
-    pdf.setAuthor('%s (%s)' % (view.request.principal.title,
-        view.request.principal.id))
-    pdf.setCreator('WAeUP SIRP')
-    width, height = A4
-    style = getSampleStyleSheet()
-    pdf.line(1*cm,height-(1.8*cm),width-(1*cm),height-(1.8*cm))
+    def get_payment_details(self,category, student):
+        d = {}
+        d['p_item'] = u''
+        d['amount'] = 0
+        d['error'] = u''
+        d['p_session'] = student['studycourse'].current_session
+        session = str(d['p_session'])
+        try:
+            academic_session = grok.getSite()['configuration'][session]
+        except KeyError:
+            d['error'] = u'Session configuration object is not available.'
+            return d
+        d['surcharge_1'] = academic_session.surcharge_1
+        d['surcharge_2'] = academic_session.surcharge_2
+        d['surcharge_3'] = academic_session.surcharge_3
+        if category == 'schoolfee':
+            d['amount'] = academic_session.school_fee_base
+            d['p_item'] = student['studycourse'].certificate.code
+        elif category == 'clearance':
+            d['p_item'] = student['studycourse'].certificate.code
+            d['amount'] = academic_session.clearance_fee
+        elif category == 'bed_allocation':
+            d['p_item'] = self.get_accommodation_details(student)['bt']
+            d['amount'] = academic_session.booking_fee
+        return d
 
-    story = []
-    frame_header = Frame(1*cm,1*cm,width-(1.7*cm),height-(1.7*cm))
-    header_title = getattr(getSite(), 'name', u'Sample University')
-    story.append(Paragraph(header_title, style["Heading1"]))
-    frame_header.addFromList(story,pdf)
+    def get_accommodation_details(self, student):
+        d = {}
+        d['error'] = u''
+        site_confoguration = grok.getSite()['configuration']
+        d['booking_session'] = site_confoguration.accommodation_session
+        d['allowed_states'] = site_confoguration.accommodation_states
+        session = str(d['booking_session'])
+        # Determine bed type
+        studycourse = student['studycourse']
+        entry_session = studycourse.entry_session
+        current_level = studycourse.current_level
+        end_level = studycourse.certificate.end_level
+        if entry_session == grok.getSite()['configuration'].accommodation_session:
+            bt = 'fr'
+        elif current_level >= end_level:
+            bt = 'fi'
+        else:
+            bt = 're'
+        if student.sex == 'f':
+            sex = 'female'
+        else:
+            sex = 'male'
+        special_handling = 'regular'
+        d['bt'] = u'%s_%s_%s' % (special_handling,sex,bt)
+        return d
 
-    story = []
-    frame_body = Frame(1*cm,1*cm,width-(2*cm),height-(3.5*cm))
-    story.append(Paragraph(view.label, style["Heading2"]))
-    #story.append(HRFlowable())
-    if student:
+    # In the standard configuration we select the first bed found,
+    # but can also randomize the selection if we like.
+    def select_bed(self, available_beds):
+        return available_beds[0]
+
+    def render_pdf(self, view, subject='', filename='slip.pdf',
+        student=None, studentview=None):
+        # (0,0),(-1,-1) = whole table
+        # (0,0),(0,-1) = first column
+        # (-1,0),(-1,-1) = last column
+        # (0,0),(-1,0) = first row
+        # (0,-1),(-1,-1) = last row
+
+        pdf = canvas.Canvas(filename,pagesize=A4)
+        pdf.setTitle(view.label)
+        pdf.setSubject(subject)
+        pdf.setAuthor('%s (%s)' % (view.request.principal.title,
+            view.request.principal.id))
+        pdf.setCreator('WAeUP SIRP')
+        width, height = A4
+        style = getSampleStyleSheet()
+        pdf.line(1*cm,height-(1.8*cm),width-(1*cm),height-(1.8*cm))
+
+        story = []
+        frame_header = Frame(1*cm,1*cm,width-(1.7*cm),height-(1.7*cm))
+        header_title = getattr(grok.getSite(), 'name', u'Sample University')
+        story.append(Paragraph(header_title, style["Heading1"]))
+        frame_header.addFromList(story,pdf)
+
+        story = []
+        frame_body = Frame(1*cm,1*cm,width-(2*cm),height-(3.5*cm))
+        story.append(Paragraph(view.label, style["Heading2"]))
+        #story.append(HRFlowable())
+        if student:
+            story.append(Spacer(1, 18))
+            studenttable = render_student_data(student, studentview)
+            story.append(studenttable)
         story.append(Spacer(1, 18))
-        studenttable = render_student_data(student, studentview)
-        story.append(studenttable)
-    story.append(Spacer(1, 18))
-    setUpWidgets(view)
-    data = []
-    for widget in view.widgets:
-        f_label = '<font size=12>%s</font>:' % widget.label.strip()
-        f_label = Paragraph(f_label, style["Normal"])
-        f_text = '<font size=12>%s</font>' % widget()
-        f_text = Paragraph(f_text, style["Normal"])
-        data.append([f_label,f_text])
-    table = Table(data,style=SLIP_STYLE)
-    story.append(table)
-    frame_body.addFromList(story,pdf)
+        setUpWidgets(view)
+        data = []
+        for widget in view.widgets:
+            f_label = '<font size=12>%s</font>:' % widget.label.strip()
+            f_label = Paragraph(f_label, style["Normal"])
+            f_text = '<font size=12>%s</font>' % widget()
+            f_text = Paragraph(f_text, style["Normal"])
+            data.append([f_label,f_text])
+        table = Table(data,style=SLIP_STYLE)
+        story.append(table)
+        frame_body.addFromList(story,pdf)
 
-    story = []
-    frame_footer = Frame(1*cm,0,width-(2*cm),1*cm)
-    timestamp = datetime.now().strftime("%d/%m/%Y %H:%M:%S")
-    f_text = '<font size=10>%s</font>' % timestamp
-    story.append(Paragraph(f_text, style["Normal"]))
-    frame_footer.addFromList(story,pdf)
+        story = []
+        frame_footer = Frame(1*cm,0,width-(2*cm),1*cm)
+        timestamp = datetime.now().strftime("%d/%m/%Y %H:%M:%S")
+        f_text = '<font size=10>%s</font>' % timestamp
+        story.append(Paragraph(f_text, style["Normal"]))
+        frame_footer.addFromList(story,pdf)
 
-    view.response.setHeader(
-        'Content-Type', 'application/pdf')
-    return pdf.getpdfdata()
+        view.response.setHeader(
+            'Content-Type', 'application/pdf')
+        return pdf.getpdfdata()
