Index: main/waeup.kofa/trunk/src/waeup/kofa/students/reports/browser_templates/studentpaymentstatisticsreportgeneratorpage.pt
===================================================================
--- main/waeup.kofa/trunk/src/waeup/kofa/students/reports/browser_templates/studentpaymentstatisticsreportgeneratorpage.pt	(revision 14513)
+++ main/waeup.kofa/trunk/src/waeup/kofa/students/reports/browser_templates/studentpaymentstatisticsreportgeneratorpage.pt	(revision 14513)
@@ -0,0 +1,79 @@
+<form  i18n:domain="waeup.kofa"  method="POST">
+  <p i18n:translate="">
+    Please pick the session and study modes group
+    for which the report should be generated.
+    The report data will be calculated and then made available for you
+    to view/download.
+  </p>
+  <br />
+  <label for="breakdown" i18n:translate="">Breakdown</label>
+    <select name="breakdown" class="form-control half">
+      <span tal:repeat="items view/breakdowns" tal:omit-tag="">
+        <option
+            tal:define="name python: items[1]; title python: items[0]"
+            tal:attributes="value name">
+          <span tal:replace="title">TITLE</span>
+        </option>
+      </span>
+    </select>
+  <br />
+  <label for="session" i18n:translate="">Study Modes Group</label>
+    <select name="mode" class="form-control half">
+      <span tal:repeat="items view/modes" tal:omit-tag="">
+        <option
+            tal:define="name python: items[1]; title python: items[0]"
+            tal:attributes="value name">
+          <span tal:replace="title">TITLE</span>
+        </option>
+      </span>
+    </select>
+  <br />
+  <label for="session">Academic Session</label>
+  <select name="session" class="form-control half">
+    <span tal:repeat="items view/sessions" tal:omit-tag="">
+      <option
+          tal:define="name python: items[1]; title python: items[0]"
+          tal:attributes="value name">
+        <span tal:replace="title">TITLE</span>
+      </option>
+    </span>
+  </select>
+  <br />
+  <label for="level">Current Level</label>
+  <select name="level" class="form-control half">
+    <span tal:repeat="items view/levels" tal:omit-tag="">
+      <option
+          tal:define="name python: items[1]; title python: items[0]"
+          tal:attributes="value name">
+        <span tal:replace="title">TITLE</span>
+      </option>
+    </span>
+  </select>
+  <br />
+  <label for="entry_session">Entry Session</label>
+  <select name="entry_session" class="form-control half">
+    <span tal:repeat="items view/sessions_plus" tal:omit-tag="">
+      <option
+          tal:define="name python: items[1]; title python: items[0]"
+          tal:attributes="value name">
+        <span tal:replace="title">TITLE</span>
+      </option>
+    </span>
+  </select>
+  <br />
+  <label for="p_session">Payment Session</label>
+  <select name="p_session" class="form-control half">
+    <span tal:repeat="items view/sessions_plus" tal:omit-tag="">
+      <option
+          tal:define="name python: items[1]; title python: items[0]"
+          tal:attributes="value name">
+        <span tal:replace="title">TITLE</span>
+      </option>
+    </span>
+  </select>
+  <br />
+  <input i18n:translate="" type="submit" class="btn btn-primary"
+         name="CREATE" value="Create" />
+  <a class="btn btn-default" href=""
+     tal:attributes="href view/parent_url">Cancel</a>
+</form>
Index: main/waeup.kofa/trunk/src/waeup/kofa/students/reports/student_payment_statistics.py
===================================================================
--- main/waeup.kofa/trunk/src/waeup/kofa/students/reports/student_payment_statistics.py	(revision 14512)
+++ main/waeup.kofa/trunk/src/waeup/kofa/students/reports/student_payment_statistics.py	(revision 14513)
@@ -37,5 +37,6 @@
 
 
-def get_student_payment_stats(session, mode, level, breakdown):
+def get_student_payment_stats(session, mode, level, entry_session,
+        p_session, breakdown):
     """Get payment amounts of students in a certain session, study mode
     and current level.
@@ -82,10 +83,14 @@
     cat = queryUtility(ICatalog, name="students_catalog")
     result = cat.searchResults(current_session=(session, session))
-    table = [[0 for x in xrange(2*len(payment_cats))] for y in xrange(len(codes)+1)]
+    table = [[0 for x in xrange(2*len(payment_cats))]
+                for y in xrange(len(codes)+1)]
     mode_groups = getUtility(IKofaUtils).MODE_GROUPS
     for stud in result:
-        if mode != 'All' and stud.current_mode not in mode_groups[mode]:
+        if mode != 'All' and stud.current_mode \
+            and stud.current_mode not in mode_groups[mode]:
             continue
         if level != 0 and stud.current_level != level:
+            continue
+        if entry_session != 0 and  stud.entry_session != entry_session:
             continue
         if getattr(stud, breakdown) not in codes:
@@ -95,7 +100,9 @@
         for key in stud['payments'].keys():
             ticket = stud['payments'][key]
-            if ticket.p_category not in payment_cats or\
-                ticket.p_state != 'paid' or\
-                ticket.p_session != session:
+            if ticket.p_category not in payment_cats:
+                continue
+            if ticket.p_state != 'paid':
+                continue
+            if p_session != 0 and ticket.p_session != p_session:
                 continue
             col = payment_cats.index(ticket.p_category)
@@ -181,17 +188,39 @@
     session = None
     mode = None
+    entry_session = None
+    p_session = None
     pdfcreator = 'landscape'
 
-    def __init__(self, session, mode, level, breakdown, author='System'):
+    def __init__(self, session, mode, level, entry_session, p_session,
+                 breakdown, author='System'):
         super(StudentPaymentStatisticsReport, self).__init__(
-            args=[session, mode, breakdown], kwargs={'author':author})
+            args=[session, mode, level, entry_session, p_session, breakdown],
+            kwargs={'author':author})
+        self.sessioncode = session
+        self.levelcode = level
+        self.entrysessioncode = entry_session
+        self.psessioncode = p_session
         self.studylevelsource = StudyLevelSource().factory
         self.portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
         self.session = academic_sessions_vocab.getTerm(session).title
-        self.mode = mode
+        if entry_session != 0:
+            self.entry_session = academic_sessions_vocab.getTerm(
+                entry_session).title
+        else:
+            self.entry_session = 'All sessions'
+        if p_session != 0:
+            self.p_session = academic_sessions_vocab.getTerm(
+                p_session).title
+        else:
+            self.p_session = 'All sessions'
+        if mode == 'All':
+            self.mode = 'All study modes'
+        else:
+            self.mode = mode
         if level == 0:
-            self.level = ''
-        else:
-            self.level = translate(self.studylevelsource.getTitle(None, int(level)),
+            self.level = 'All levels'
+        else:
+            self.level = translate(
+                self.studylevelsource.getTitle(None, int(level)),
                 'waeup.kofa', target_language=self.portal_language)
         self.breakdown = breakdown
@@ -199,5 +228,6 @@
         self.creation_dt_string = self.creation_dt.astimezone(
             getUtility(IKofaUtils).tzinfo).strftime("%Y-%m-%d %H:%M:%S %Z")
-        self.data = get_student_payment_stats(session, mode, level, breakdown)
+        self.data = get_student_payment_stats(
+            session, mode, level, entry_session, p_session, breakdown)
 
     def create_pdf(self, job_id):
@@ -209,22 +239,27 @@
                               STYLE["Normal"]),
                     Spacer(1, 12),]
+        pdf_data += [Paragraph(
+                    translate(
+                        'Study Mode: ${a}<br />'
+                        'Academic Session: ${b}<br />Level: ${c}<br />'
+                        'Entry Session: ${d}<br />Payment Session: ${e}',
+                        mapping = {'a':self.mode,
+                                   'b':self.session,
+                                   'c':self.level,
+                                   'd':self.entry_session,
+                                   'e':self.p_session,
+                                   }),
+                    STYLE["Normal"]),
+                    Spacer(1, 12),]
         pdf_data += [
             Table(table_data, style=TABLE_STYLE, colWidths=col_widths)]
-        if not self.level:
-            doc_title = translate(
-                _('${a} Student Payments - ${b}',
-                mapping = {'a':self.mode, 'b':self.session}))
-            pdf = creator.create_pdf(
-                pdf_data, None, doc_title, self.author, doc_title + '-'
-                )
-        else:
-            doc_title = translate(
-                _('${a} Student Payments - ${b} - ${c}',
-                mapping = {'a':self.mode, 'b': self.level, 'c':self.session}))
-            pdf = creator.create_pdf(
-                pdf_data, None, doc_title, self.author, doc_title + '-'
-                )
+        doc_title = translate(_('Student Payment Statistics'))
+        right_footer = translate(
+            _('${a} Student Payments - ${b} -',
+            mapping = {'a':self.mode, 'b':self.session}))
+        pdf = creator.create_pdf(
+            pdf_data, None, doc_title, self.author, right_footer
+            )
         return pdf
-
 
 @implementer(IReportGenerator)
@@ -236,7 +271,9 @@
     def generate(
         self, site, session=None, mode=None,
-        level=None, breakdown=None, author=None):
+        level=None, entry_session=None, p_session=None,
+        breakdown=None, author=None):
         result = StudentPaymentStatisticsReport(
             session=session, mode=mode, level=level,
+            entry_session=entry_session, p_session=p_session,
             breakdown=breakdown, author=author)
         return result
@@ -261,8 +298,45 @@
 
     grok.context(StudentPaymentStatisticsReportGenerator)
-    grok.template('studentstatisticsreportgeneratorpage')
+    grok.template('studentpaymentstatisticsreportgeneratorpage')
 
     label = _('Create student payment statistics report')
 
+    def update(
+        self, CREATE=None, session=None, mode=None, level=None,
+        entry_session=None, p_session=None, breakdown=None):
+        self.parent_url = self.url(self.context.__parent__)
+        self._set_session_values()
+        self._set_mode_values()
+        self._set_level_values()
+        self._set_breakdown_values()
+        if CREATE and session:
+            # create a new report job for students by session and level
+            container = self.context.__parent__
+            user_id = self.request.principal.id
+            kw = dict(
+                session=int(session),
+                mode=mode,
+                level=int(level),
+                entry_session=int(entry_session),
+                p_session=int(p_session),
+                breakdown=breakdown)
+            self.flash(_('New report is being created in background'))
+            job_id = container.start_report_job(
+                self.generator_name, user_id, kw=kw)
+            ob_class = self.__implemented__.__name__.replace('waeup.kofa.','')
+            grok.getSite().logger.info(
+                '%s - report %s created: %s (session=%s, mode=%s, level=%s, '
+                'entry_session=%s, p_session=%s, breakdown=%s)' % (
+                ob_class, job_id, self.context.title,
+                session, mode, level, entry_session, p_session, breakdown))
+            self.redirect(self.parent_url)
+            return
+        return
+
+    def _set_session_values(self):
+        vocab_terms = academic_sessions_vocab.by_value.values()
+        self.sessions = [(x.title, x.token) for x in vocab_terms]
+        self.sessions_plus = [(u'All', 0)] + self.sessions
+        return
 
 class StudentPaymentStatisticsReportPDFView(StudentStatisticsReportPDFView):
@@ -271,8 +345,7 @@
 
     def _filename(self):
-        return 'StudentPaymentStatisticsReport_%s_%s_%s_%s.pdf' % (
-            self.context.session, self.context.mode, self.context.level,
+        return 'StudentPaymentStatisticsReport_rno%s_%s.pdf' % (
+            self.context.__name__,
             self.context.creation_dt_string)
-
 
 class StudentStatsBreadcrumb(Breadcrumb):
Index: main/waeup.kofa/trunk/src/waeup/kofa/students/reports/tests/test_student_payment_statistics.py
===================================================================
--- main/waeup.kofa/trunk/src/waeup/kofa/students/reports/tests/test_student_payment_statistics.py	(revision 14512)
+++ main/waeup.kofa/trunk/src/waeup/kofa/students/reports/tests/test_student_payment_statistics.py	(revision 14513)
@@ -22,5 +22,5 @@
         # ensure we fullfill interface contracts
         obj = StudentPaymentStatisticsReport(
-            2010, 'Undergraduate Full-Time', 0, 'faccode')
+            2010, 'Undergraduate Full-Time', 0, 0, 0, 'faccode')
         verifyClass(IStudentPaymentStatisticsReport, StudentPaymentStatisticsReport)
         verifyObject(IStudentPaymentStatisticsReport, obj)
@@ -30,7 +30,7 @@
         # we can get a table with one student
         result1 = get_student_payment_stats(
-            2010, 'Undergraduate Full-Time', 0, 'faccode')
+            2010, 'Undergraduate Full-Time', 0, 0, 0, 'faccode')
         result2 = get_student_payment_stats(
-            2009, 'Undergraduate Full-Time', 0, 'faccode')
+            2009, 'Undergraduate Full-Time', 0, 0, 0, 'faccode')
         self.assertEqual(
             result1,
@@ -55,5 +55,5 @@
     def test_create_pdf(self):
         report = StudentPaymentStatisticsReport(
-            2010, 'Undergraduate Full-Time', 0,  'faccode')
+            2010, 'Undergraduate Full-Time', 0, 0, 0, 'faccode')
         result = report.create_pdf('JOB_ID')
         self.assertTrue(result.startswith('%PDF-'))
@@ -106,8 +106,7 @@
                          'application/pdf')
         self.assertTrue(
-            'filename="StudentPaymentStatisticsReport_2004_2005_All__' in
+            'filename="StudentPaymentStatisticsReport_rno%s' % job_id in
             self.browser.headers['content-disposition'])
         self.assertEqual(len(self.app['reports'].running_report_jobs), 1)
-        job_id = self.app['reports'].running_report_jobs[0][0]
         # ... and discarded
         self.browser.open('http://localhost/app/reports')
@@ -120,11 +119,13 @@
         self.assertTrue(
             'INFO - zope.mgr - students.reports.student_payment_statistics.StudentPaymentStatisticsReportGeneratorPage - '
-            'report %s created: Student Payment Statistics (session=2004, mode=All, level=0, breakdown=depcode)'
+            'report %s created: Student Payment Statistics '
+            '(session=2004, mode=All, level=0, entry_session=0, '
+            'p_session=0, breakdown=depcode)'
             % job_id in logcontent
             )
         self.assertTrue(
             'INFO - zope.mgr - students.reports.student_payment_statistics.StudentPaymentStatisticsReportPDFView - '
-            'report %s downloaded: StudentPaymentStatisticsReport_2004_2005_All__'
-            % job_id in logcontent
+            'report %s downloaded: StudentPaymentStatisticsReport_rno%s_'
+            % (job_id, job_id) in logcontent
             )
         self.assertTrue(
