Index: main/waeup.kofa/trunk/CHANGES.txt
===================================================================
--- main/waeup.kofa/trunk/CHANGES.txt	(revision 15966)
+++ main/waeup.kofa/trunk/CHANGES.txt	(revision 15968)
@@ -4,4 +4,6 @@
 1.6.1.dev0 (unreleased)
 =======================
+
+* Add `waeup.local.ReportsOfficer` role.
 
 * Add `TrimmedDataExporter`.
Index: main/waeup.kofa/trunk/docs/source/userdocs/security.rst
===================================================================
--- main/waeup.kofa/trunk/docs/source/userdocs/security.rst	(revision 15966)
+++ main/waeup.kofa/trunk/docs/source/userdocs/security.rst	(revision 15968)
@@ -331,4 +331,7 @@
    :noindex:
 
+.. autoclass:: waeup.kofa.permissions.LocalReportsOfficer()
+   :noindex:
+
 .. autoclass:: waeup.kofa.permissions.UGClearanceOfficer()
    :noindex:
Index: main/waeup.kofa/trunk/src/waeup/kofa/browser/reports.py
===================================================================
--- main/waeup.kofa/trunk/src/waeup/kofa/browser/reports.py	(revision 15966)
+++ main/waeup.kofa/trunk/src/waeup/kofa/browser/reports.py	(revision 15968)
@@ -27,5 +27,5 @@
 from waeup.kofa.interfaces import MessageFactory as _
 from waeup.kofa.browser.layout import KofaPage, jsaction
-from waeup.kofa.utils.helpers import get_current_principal
+from waeup.kofa.utils.helpers import get_current_principal, get_user_account
 from waeup.kofa.reports import (
     IReportsContainer, IReportGenerator, get_generators)
@@ -142,4 +142,7 @@
     label = _('Create report')
 
+    locally_allowed_reports = (
+        'level_report', 'raw_score_report', 'session_results_presentation')
+
     def update(self, START_GENERATOR=None, generator=None):
         utils = queryUtility(IKofaUtils)
@@ -162,6 +165,19 @@
         being a human readable description of the respective generator
         and ``<NAME>`` being the registration name with the ZCA.
+
+        If `waeup.local.ReportsOfficer` role has been assigned at
+        department level, list only report generator which allow
+        to filter department data.
         """
-        result = [(gen.title, name) for name, gen in get_generators()]
+        try:
+            local_roles = get_user_account(self.request).getLocalRoles()
+        except AttributeError:  # Managers have no user account
+            local_roles = {}
+        lror = local_roles.get('waeup.local.ReportsOfficer', None)
+        if lror:
+            result = [(gen.title, name) for name, gen in get_generators()
+                                        if name in self.locally_allowed_reports]
+        else:
+            result = [(gen.title, name) for name, gen in get_generators()]
         sorted_result = sorted(result, key=lambda value:value[1])
         return sorted_result
Index: main/waeup.kofa/trunk/src/waeup/kofa/doctests/permissions.txt
===================================================================
--- main/waeup.kofa/trunk/src/waeup/kofa/doctests/permissions.txt	(revision 15966)
+++ main/waeup.kofa/trunk/src/waeup/kofa/doctests/permissions.txt	(revision 15968)
@@ -42,5 +42,5 @@
 
     >>> len(list(get_waeup_roles(also_local=True)))
-    53
+    54
 
 
Index: main/waeup.kofa/trunk/src/waeup/kofa/permissions.py
===================================================================
--- main/waeup.kofa/trunk/src/waeup/kofa/permissions.py	(revision 15966)
+++ main/waeup.kofa/trunk/src/waeup/kofa/permissions.py	(revision 15968)
@@ -287,4 +287,19 @@
                      'waeup.exportData')
 
+class LocalReportsOfficer(grok.Role):
+    """The local ReportsOfficer role can be assigned at department level.
+    The role allows to view all data and to list or export
+    all student data within the department the local role is assigned.
+
+    The LocalReportsOfficer requires the assignment of the global
+    ReportsOfficer role to access the reports section. If set, it reduces
+    the number of available report generators and selectable certificates
+    Local Reports Officer can create only reports for their department.
+    """
+    grok.name('waeup.local.ReportsOfficer')
+    grok.title(u'Student Reports Officer')
+    grok.permissions('waeup.showStudents',
+                     'waeup.viewAcademics',
+                     'waeup.exportData')
 
 class UGClearanceOfficer(grok.Role):
Index: main/waeup.kofa/trunk/src/waeup/kofa/students/dynamicroles.py
===================================================================
--- main/waeup.kofa/trunk/src/waeup/kofa/students/dynamicroles.py	(revision 15966)
+++ main/waeup.kofa/trunk/src/waeup/kofa/students/dynamicroles.py	(revision 15968)
@@ -43,9 +43,10 @@
     # role to add in case this role was found
     rolename_mapping = {
-        'waeup.local.ClearanceOfficer':'waeup.StudentsClearanceOfficer',
+        'waeup.local.ClearanceOfficer': 'waeup.StudentsClearanceOfficer',
         'waeup.local.LocalStudentsManager': 'waeup.StudentsManager',
         'waeup.local.LocalWorkflowManager': 'waeup.WorkflowManager',
         'waeup.local.TranscriptOfficer': 'waeup.TranscriptOfficer',
         'waeup.local.TranscriptSignee': 'waeup.TranscriptSignee',
+        'waeup.local.ReportsOfficer': 'waeup.ReportsOfficer',
         }
 
Index: main/waeup.kofa/trunk/src/waeup/kofa/students/reports/level_report.py
===================================================================
--- main/waeup.kofa/trunk/src/waeup/kofa/students/reports/level_report.py	(revision 15966)
+++ main/waeup.kofa/trunk/src/waeup/kofa/students/reports/level_report.py	(revision 15968)
@@ -27,4 +27,5 @@
 from waeup.kofa.interfaces import MessageFactory as _
 from waeup.kofa.reports import IReport
+from waeup.kofa.utils.helpers import get_user_account
 from waeup.kofa.browser.pdf import get_signature_tables
 from waeup.kofa.students.vocabularies import StudyLevelSource
@@ -329,5 +330,10 @@
         self._set_session_values()
         self._set_level_values()
-        self._set_faccode_depcode_certcode_values()
+        try:
+            local_roles = get_user_account(self.request).getLocalRoles()
+        except AttributeError:  # Managers have no user account
+            local_roles = {}
+        lror = local_roles.get('waeup.local.ReportsOfficer', None)
+        self._set_faccode_depcode_certcode_values(lror)
         if not faccode_depcode_certcode:
             self.flash(_('No certificate selected.'), type="warning")
@@ -384,20 +390,34 @@
         return
 
-    def _set_faccode_depcode_certcode_values(self):
+    def _set_faccode_depcode_certcode_values(self, lror):
         faccode_depcode_certcode = []
         faculties = grok.getSite()['faculties']
-        for fac in faculties.values():
-            for dep in fac.values():
+        if lror:
+           for dep in lror:
                 faccode_depcode_certcode.append(
                     (' All certificates -- %s, %s)'
-                     %(dep.longtitle, fac.longtitle),
+                     %(dep.longtitle, dep.__parent__.longtitle),
                      '%s_%s_all'
-                     %(fac.code, dep.code)))
+                     %(dep.__parent__.code, dep.code)))
                 for cert in dep.certificates.values():
                     faccode_depcode_certcode.append(
                         ('%s -- %s, %s)'
-                         %(cert.longtitle, dep.longtitle, fac.longtitle),
+                         %(cert.longtitle, dep.longtitle, dep.__parent__.longtitle),
                          '%s_%s_%s'
-                         %(fac.code, dep.code, cert.code)))
+                         %(dep.__parent__.code, dep.code, cert.code)))
+        else:
+            for fac in faculties.values():
+                for dep in fac.values():
+                    faccode_depcode_certcode.append(
+                        (' All certificates -- %s, %s)'
+                         %(dep.longtitle, fac.longtitle),
+                         '%s_%s_all'
+                         %(fac.code, dep.code)))
+                    for cert in dep.certificates.values():
+                        faccode_depcode_certcode.append(
+                            ('%s -- %s, %s)'
+                             %(cert.longtitle, dep.longtitle, fac.longtitle),
+                             '%s_%s_%s'
+                             %(fac.code, dep.code, cert.code)))
         self.faccode_depcode_certcode = sorted(
             faccode_depcode_certcode, key=lambda value: value[0])
Index: main/waeup.kofa/trunk/src/waeup/kofa/students/reports/tests/test_level_report.py
===================================================================
--- main/waeup.kofa/trunk/src/waeup/kofa/students/reports/tests/test_level_report.py	(revision 15966)
+++ main/waeup.kofa/trunk/src/waeup/kofa/students/reports/tests/test_level_report.py	(revision 15968)
@@ -1,5 +1,7 @@
 import os
 from zc.async.testing import wait_for_result
+from zope.event import notify
 from zope.interface.verify import verifyClass, verifyObject
+from zope.securitypolicy.interfaces import IPrincipalRoleManager
 from zope.component import getUtility, createObject
 from waeup.kofa.interfaces import IJobManager
@@ -9,6 +11,8 @@
 from waeup.kofa.students.tests.test_browser import StudentsFullSetup
 from waeup.kofa.testing import FunctionalLayer
+from waeup.kofa.authentication import LocalRoleSetEvent
 from waeup.kofa.tests.test_async import FunctionalAsyncTestCase
 from waeup.kofa.browser.tests.test_pdf import samples_dir
+from waeup.kofa.tests.test_authentication import SECRET
 
 class LevelReportTests(CatalogTestSetup):
@@ -112,5 +116,26 @@
 
     def trigger_report_creation(self):
-        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
+        #self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
+        # Create reports officer
+        self.app['users'].addUser('mrreport', SECRET)
+        self.app['users']['mrreport'].email = 'mrreport@foo.ng'
+        self.app['users']['mrreport'].title = 'Carlo Pitter'
+        # Assign global reports officer role
+        prmglobal = IPrincipalRoleManager(self.app)
+        prmglobal.assignRoleToPrincipal('waeup.ReportsOfficer', 'mrreport')
+        # Assign local ReportsOfficer role which restricts the access to
+        # certain faculties.
+        self.department = self.app['faculties']['fac1']['dep1']
+        prmlocal = IPrincipalRoleManager(self.department)
+        prmlocal.assignRoleToPrincipal(
+            'waeup.local.ReportsOfficer', 'mrreport')
+        notify(LocalRoleSetEvent(
+            self.department, 'waeup.local.ReportsOfficer', 'mrreport',
+            granted=True))
+        # Login as reports officer
+        self.browser.open(self.login_path)
+        self.browser.getControl(name="form.login").value = 'mrreport'
+        self.browser.getControl(name="form.password").value = SECRET
+        self.browser.getControl("Login").click()
         self.browser.open('http://localhost/app/reports')
         self.assertEqual(self.browser.headers['Status'], '200 Ok')
@@ -148,5 +173,5 @@
         logcontent = open(logfile).read()
         self.assertTrue(
-            'INFO - zope.mgr - students.reports.level_report.LevelReportGeneratorPage - '
+            'INFO - mrreport - students.reports.level_report.LevelReportGeneratorPage - '
             'report %s created: Level Report (faculty=fac1, department=dep1, '
             'certificate=CERT1, session=2010, level=100)'
@@ -154,10 +179,10 @@
             )
         self.assertTrue(
-            'INFO - zope.mgr - students.reports.level_report.LevelReportPDFView - '
+            'INFO - mrreport - students.reports.level_report.LevelReportPDFView - '
             'report %s downloaded: LevelReport_rno%s'
             % (job_id, job_id) in logcontent
             )
         self.assertTrue(
-            'INFO - zope.mgr - browser.reports.ReportsContainerPage - '
+            'INFO - mrreport - browser.reports.ReportsContainerPage - '
             'report %s discarded' % job_id in logcontent
             )
Index: main/waeup.kofa/trunk/src/waeup/kofa/university/department.py
===================================================================
--- main/waeup.kofa/trunk/src/waeup/kofa/university/department.py	(revision 15966)
+++ main/waeup.kofa/trunk/src/waeup/kofa/university/department.py	(revision 15968)
@@ -49,4 +49,5 @@
         'waeup.local.UGClearanceOfficer',
         'waeup.local.PGClearanceOfficer',
+        'waeup.local.ReportsOfficer',
         'waeup.local.CourseAdviser100',
         'waeup.local.CourseAdviser200',
