Index: main/waeup.kofa/trunk/src/waeup/kofa/students/browser.py
===================================================================
--- main/waeup.kofa/trunk/src/waeup/kofa/students/browser.py	(revision 15049)
+++ main/waeup.kofa/trunk/src/waeup/kofa/students/browser.py	(revision 15051)
@@ -3409,5 +3409,8 @@
                 exporters += [('Bursary Data', 'bursary')]
             if checkPermission('waeup.exportPaymentsOverview', self.context):
-                exporters += [('Student Payments Overview', 'sfpaymentsoverview')]
+                exporters += [('School Fee Payments Overview',
+                               'sfpaymentsoverview'),
+                              ('Session Payments Overview',
+                               'sessionpaymentsoverview')]
             self.exporters = exporters
             return
Index: main/waeup.kofa/trunk/src/waeup/kofa/students/export.py
===================================================================
--- main/waeup.kofa/trunk/src/waeup/kofa/students/export.py	(revision 15049)
+++ main/waeup.kofa/trunk/src/waeup/kofa/students/export.py	(revision 15051)
@@ -549,5 +549,4 @@
     year_range = range(curr_year - 11, curr_year + 1)
     year_range_tuple = tuple([str(year) for year in year_range])
-
     fields = ('student_id', 'matric_number', 'display_fullname',
         'state', 'certcode', 'faccode', 'depcode', 'is_postgrad',
@@ -584,4 +583,34 @@
             value, name, context=context)
 
+class SessionPaymentsOverviewExporter(StudentExporter):
+    """The Session Payments Overview Exporter first filters the set of students
+    by searching the students catalog. Then it exports some student base data
+    together with lists of payment years for a predefined range of payment
+    categories.
+    """
+    grok.name('sessionpaymentsoverview')
+
+    paycats = ('schoolfee', 'clearance', 'gown', 'transcript')
+    fields = ('student_id', 'matric_number', 'display_fullname',
+        'state', 'certcode', 'faccode', 'depcode', 'is_postgrad',
+        'current_level', 'current_session', 'current_mode',
+        'entry_session', 'reg_number'
+        ) + paycats
+    title = _(u'Session Payments Overview')
+
+    def mangle_value(self, value, name, context=None):
+        """
+        """
+        if name in self.paycats and context is not None:
+            value = ''
+            for ticket in context['payments'].values():
+                if ticket.p_category == name:
+                    if ticket.p_state in ('waived', 'paid'):
+                        value += '%s ' % ticket.p_session
+            value = value.strip().replace(' ', '+')
+        return super(
+            StudentExporter, self).mangle_value(
+            value, name, context=context)
+
 class StudentStudyLevelsOverviewExporter(StudentExporter):
     """The Student Study Levels Overview Exporter first filters the set of
Index: main/waeup.kofa/trunk/src/waeup/kofa/students/tests/test_export.py
===================================================================
--- main/waeup.kofa/trunk/src/waeup/kofa/students/tests/test_export.py	(revision 15049)
+++ main/waeup.kofa/trunk/src/waeup/kofa/students/tests/test_export.py	(revision 15051)
@@ -32,5 +32,5 @@
     SchoolFeePaymentsOverviewExporter, StudentStudyLevelsOverviewExporter,
     ComboCardDataExporter, DataForBursaryExporter,
-    StudentUnpaidPaymentExporter,
+    StudentUnpaidPaymentExporter, SessionPaymentsOverviewExporter,
     get_students,)
 from waeup.kofa.students.accommodation import BedTicket
@@ -1232,4 +1232,83 @@
         return
 
+class SessionPaymentsOverviewExporterTest(StudentImportExportSetup):
+
+    layer = FunctionalLayer
+
+    def setUp(self):
+        super(SessionPaymentsOverviewExporterTest, self).setUp()
+        self.setup_for_export()
+        return
+
+    def test_ifaces(self):
+        # make sure we fullfill interface contracts
+        obj = SessionPaymentsOverviewExporter()
+        verifyObject(ICSVStudentExporter, obj)
+        verifyClass(ICSVStudentExporter, SessionPaymentsOverviewExporter)
+        return
+
+    def test_get_as_utility(self):
+        # we can get a payments exporter as utility
+        result = queryUtility(ICSVExporter, name="sessionpaymentsoverview")
+        self.assertTrue(result is not None)
+        return
+
+    def test_export(self):
+        self.setup_student(self.student)
+        exporter = SessionPaymentsOverviewExporter()
+        exporter.export([self.student], self.outfile)
+        result = open(self.outfile, 'rb').read()
+        self.assertTrue(
+            'student_id,matric_number,display_fullname,state,certcode,'
+            'faccode,depcode,is_postgrad,current_level,current_session,'
+            'current_mode,entry_session,reg_number,schoolfee,clearance,'
+            'gown,transcript\r\n'
+            'A111111,234,Anna M. Tester,created,CERT1,NA,NA,0,200,2012,'
+            'ug_ft,2010,123,2012,,,' in result
+            )
+        return
+
+    def test_export_all(self):
+        # we can really export students
+        # set values we can expect in export file
+        self.setup_student(self.student)
+        # We add successful payments.
+        payment_2 = StudentOnlinePayment()
+        payment_2.p_id = 'my-id'
+        payment_2.p_session = curr_year - 5
+        payment_2.amount_auth = 13.13
+        payment_2.p_state = 'paid'
+        payment_2.p_category = u'schoolfee'
+        self.student['payments']['my-2ndpayment'] = payment_2
+        # This one could be a balance payment.
+        # The amount is being added.
+        payment_3 = StudentOnlinePayment()
+        payment_3.p_id = 'my-id_2'
+        payment_3.p_session = curr_year - 5
+        payment_3.amount_auth = 1.01
+        payment_3.p_state = 'paid'
+        payment_3.p_category = u'schoolfee'
+        self.student['payments']['my-3rdpayment'] = payment_3
+        # One session school fee has been waived
+        payment_4 = StudentOnlinePayment()
+        payment_4.p_id = 'my-id_2'
+        payment_4.p_session = curr_year - 4
+        payment_4.amount_auth = 1.01
+        payment_4.p_state = 'waived'
+        payment_4.p_category = u'schoolfee'
+        self.student['payments']['my-4thpayment'] = payment_4
+        exporter = SessionPaymentsOverviewExporter()
+        exporter.export_all(self.app, self.outfile)
+        result = open(self.outfile, 'rb').read()
+        self.assertTrue(
+            'student_id,matric_number,display_fullname,state,certcode,'
+            'faccode,depcode,is_postgrad,current_level,current_session,'
+            'current_mode,entry_session,reg_number,schoolfee,clearance,'
+            'gown,transcript\r\nA111111,234,Anna M. Tester,created,CERT1,'
+            'NA,NA,0,200,2012,ug_ft,2010,123,2013+2013+2014+2012,,,\r\n'
+            in result
+            )
+        return
+
 class StudentStudyLevelsOverviewExporterTest(StudentImportExportSetup):
 
Index: main/waeup.kofa/trunk/src/waeup/kofa/students/utils.py
===================================================================
--- main/waeup.kofa/trunk/src/waeup/kofa/students/utils.py	(revision 15049)
+++ main/waeup.kofa/trunk/src/waeup/kofa/students/utils.py	(revision 15051)
@@ -1073,5 +1073,5 @@
             'studentstudylevels', 'coursetickets',
             'studentpayments', 'studentunpaidpayments',
-            'bedtickets', 'sfpaymentsoverview',
+            'bedtickets', 'sfpaymentsoverview', 'sessionpaymentsoverview',
             'studylevelsoverview', 'combocard', 'bursary')
 
