Index: /main/waeup.kofa/trunk/CHANGES.txt
===================================================================
--- /main/waeup.kofa/trunk/CHANGES.txt	(revision 14573)
+++ /main/waeup.kofa/trunk/CHANGES.txt	(revision 14574)
@@ -2,7 +2,8 @@
 *******
 
-
 1.5.1.dev0 (unreleased)
 =======================
+
+* Add `outstanding` attribute to course tickets.
 
 * Exclude graduated students from `LevelReport`, `SessionResultsPresentation`
@@ -409,5 +410,5 @@
 ================
 
-* Add display_fullname column to CourseTicketExporter (requested by Uniben).
+* Add display_fullname column to `CourseTicketExporter` (requested by Uniben).
 
 * Do also export student and course ticket data of students on probation.
Index: /main/waeup.kofa/trunk/docs/source/userdocs/students/browser.rst
===================================================================
--- /main/waeup.kofa/trunk/docs/source/userdocs/students/browser.rst	(revision 14573)
+++ /main/waeup.kofa/trunk/docs/source/userdocs/students/browser.rst	(revision 14574)
@@ -238,11 +238,11 @@
     `automatic` is set to ``True`` and `carry_over` to ``False.``
 
-2.  The portal can be configured
-    (`IConfigurationContainer.carry_over`) such that failed courses are
-    automatically carried over from one session to the next. Failed
-    course tickets from the previous level, i.e. tickets with a score
-    below the passmark, are collected and 'copied' into the current
-    study level container. The attributes `automatic` and `carry_over`
-    are set to ``True``.
+2. The portal can be configured
+   (`IConfigurationContainer.carry_over`) such that failed courses
+   are automatically carried over from one session to the next.
+   Failed course tickets from the previous level, i.e. tickets
+   with a score below the passmark, are collected and 'copied'
+   into the current study level container. The attributes
+   `automatic` and `carry_over` are set to ``True``.
 
 In most cases such an automatically created course list is not
@@ -251,8 +251,19 @@
 further courses, which they desire to attend, and can create
 additional course tickets, as long as the total number of credits do
-not exceed 50 (value customizable). Course tickets can also be
-removed. Whereas officers can remove any ticket from the list,
-students can remove only optional (non-mandatory) course tickets
-(condition customizable).
+not exceed 50 (value customizable). 
+
+In most cases such an automatically created course list is not
+perfect or even ready for submission to the course adviser. The list
+must be edited according to the student's needs. Students can select
+further courses, which they desire to attend, and can create
+additional course tickets, as long as the total number of credits of
+non-outstanding courses (if `outstanding` attribute is ``True``) do
+not exceed 50 (value customizable). That means outstanding courses
+are not considered as registered courses. Usually they are being
+added by officers.
+
+Course tickets can also be removed. Whereas officers can remove any
+ticket from the list, students can remove only optional
+(non-mandatory) course tickets (condition customizable).
 
 The edit form page provides a 'Register course list' button which
@@ -268,8 +279,8 @@
 they can validate or reject it by pressing the same-named link
 buttons. After pressing the 'Reject courses' button, Kofa redirects
-to the `ContactStudentFormPage` which can be used to inform the student
-about the reason of rejection. In contrast to clearance rejection,
-the message, which is being sent to the student by email, is neither
-stored in the database nor in the logfiles.
+to the `ContactStudentFormPage` which can be used to inform the
+student about the reason of rejection. In contrast to clearance
+rejection, the message, which is being sent to the student by email,
+is neither stored in the database nor in the logfiles.
 
 .. seealso::
@@ -296,5 +307,13 @@
 :py:meth:`CourseTicket.editable_by_lecturer<waeup.kofa.students.studylevel.CourseTicket.editable_by_lecturer>`.
 
-There are two options to edit course results. (1) Scores in course tickets can be changed by editing its values in the table and pressing the 'Update scores from table' button below. Scores can be cleared by removing the respective values. Lecturers have to be online during this process.(2) Alternatively, lecturers can download a csv file, edit scores in this csv file offline and upload the same file when they are online again. This procedure is explained in step-by-step instructions which show up when pressing the yellow 'Help' button:
+There are two options to edit course results. (1) Scores in course
+tickets can be changed by editing its values in the table and
+pressing the 'Update scores from table' button below. Scores can be
+cleared by removing the respective values. Lecturers have to be
+online during this process.(2) Alternatively, lecturers can download
+a csv file, edit scores in this csv file offline and upload the same
+file when they are online again. This procedure is explained in
+step-by-step instructions which show up when pressing the yellow
+'Help' button:
 
 .. admonition:: Help
@@ -303,11 +322,18 @@
 
   1. Download csv file.
-  2. Open csv file in a text editor or in a spreadsheet programme (Excel or Calc).
-  3. Edit course results only. Do not modify other entries. Do not remove or add columns. Do not add rows.
-  4. Save file in same format (csv). Do not switch to any other format (xls, xlsx or ods).
-  5. Select same file for upload and press the blue 'Update ...' button.
-  6. The values in the table will be updated. Spot-check if the values in the table correspond with the values in your file.
-
-  Note: Only course results of students which are in state 'courses validated' and in current academic session can be modified. Additional data will just be ignored.
+  2. Open csv file in a text editor or in a spreadsheet programme
+     (Excel or Calc).
+  3. Edit course results only. Do not modify other entries.
+     Do not remove or add columns. Do not add rows.
+  4. Save file in same format (csv). Do not switch to any other
+     format (xls, xlsx or ods).
+  5. Select same file for upload and press the blue 'Update ...'
+     button.
+  6. The values in the table will be updated. Spot-check if the
+     values in the table correspond with the values in your file.
+
+  Note: Only course results of students which are in state
+  'courses validated' and in current academic session can be modified.
+  Additional data will just be ignored.
 
 .. seealso::
@@ -374,16 +400,17 @@
 ------------------
 
-Officers with `ManageHostels` permission do see a 'Relocate student' link
-button which calls the `BedTicketRelocationView`. This view relocates the
-student if student parameters or the bed type of the bed have changed. The
-`update` method of this view calls the
-:py:meth:`BedTicket.relocateStudent<waeup.kofa.students.accommodation.BedTicket.relocateStudent>`
-method which checks first, if the student has a 'reserved' bed space.
-Students in reserved beds are never subject to relocation. It checks secondly,
-if booking has been cancelled in the accommodation section but other bed space
-has been manually allocated after cancellation. Then this bed is used, no
-matter whether the bed meets the bed type criteria or not. If both checks are
-negative, Kofa searches for a free bed space, which meets the student's bed
-type criteria. Only if it finds a new and free bed space, it starts the
+Officers with `ManageHostels` permission do see a 'Relocate student'
+link button which calls the `BedTicketRelocationView`. This view
+relocates the student if student parameters or the bed type of the
+bed have changed. The `update` method of this view calls the
+:py:meth:`BedTicket.relocateStudent<waeup.kofa.students.accommodation.BedTicket.relocateStudent>
+` method which checks first, if the student has a 'reserved' bed
+space. Students in reserved beds are never subject to relocation. It
+checks secondly, if booking has been cancelled in the accommodation
+section but other bed space has been manually allocated after
+cancellation. Then this bed is used, no matter whether the bed meets
+the bed type criteria or not. If both checks are negative, Kofa
+searches for a free bed space, which meets the student's bed type
+criteria. Only if it finds a new and free bed space, it starts the
 relocation process by releasing the old bed, booking the new bed and
 designating the new bed in the bed ticket.
Index: /main/waeup.kofa/trunk/docs/source/userdocs/students/interfaces.rst
===================================================================
--- /main/waeup.kofa/trunk/docs/source/userdocs/students/interfaces.rst	(revision 14573)
+++ /main/waeup.kofa/trunk/docs/source/userdocs/students/interfaces.rst	(revision 14574)
@@ -288,8 +288,8 @@
 The quite long list of schema fields pretends that form pages may
 provide these fields for editing. This is not the case. Except for
-`score`, all these fields are 'for display' only. They can neither
-be changed through the UI nor by batch processing (import). They are
-solely meant for backing up the orginal course data. See also
-:ref:`course_ticket_processor`.
+`score`, `mandatory` and `outstanding` all other fields are 'for
+display' only. They can neither be changed through the UI nor by
+batch processing (import). They are solely meant for backing up the
+orginal course data. See also :ref:`course_ticket_processor`.
 
 .. note::
Index: /main/waeup.kofa/trunk/src/waeup/kofa/students/batching.py
===================================================================
--- /main/waeup.kofa/trunk/src/waeup/kofa/students/batching.py	(revision 14573)
+++ /main/waeup.kofa/trunk/src/waeup/kofa/students/batching.py	(revision 14574)
@@ -658,5 +658,5 @@
             'student_id','reg_number','matric_number',
             'mandatory', 'score', 'carry_over', 'automatic',
-            'level_session',
+            'outstanding', 'level_session',
             'title', 'credits', 'passmark', 'semester'
             ] + self.additional_fields
Index: /main/waeup.kofa/trunk/src/waeup/kofa/students/interfaces.py
===================================================================
--- /main/waeup.kofa/trunk/src/waeup/kofa/students/interfaces.py	(revision 14573)
+++ /main/waeup.kofa/trunk/src/waeup/kofa/students/interfaces.py	(revision 14574)
@@ -598,5 +598,5 @@
         )
 
-    gpa = schema.Int(
+    gpa = schema.TextLine(
         title = _(u'Unrectified GPA'),
         required = False,
@@ -664,4 +664,10 @@
         )
 
+    outstanding = schema.Bool(
+        title = _(u'Outstanding Course'),
+        default = False,
+        required = False,
+        )
+
     score = schema.Int(
         title = _(u'Score'),
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 14573)
+++ /main/waeup.kofa/trunk/src/waeup/kofa/students/reports/level_report.py	(revision 14574)
@@ -75,5 +75,5 @@
         credits_counted = level_obj.gpa_params[1]
         credits_passed = passed_params[2]
-        level_gpa = format_float(level_obj.gpa, 3)
+        level_gpa = format_float(level_obj.gpa_params[0], 3)
         cum_credits_taken = level_obj.cumulative_params[1]
         cum_credits_passed = level_obj.cumulative_params[4]
Index: /main/waeup.kofa/trunk/src/waeup/kofa/students/studylevel.py
===================================================================
--- /main/waeup.kofa/trunk/src/waeup/kofa/students/studylevel.py	(revision 14573)
+++ /main/waeup.kofa/trunk/src/waeup/kofa/students/studylevel.py	(revision 14574)
@@ -25,7 +25,7 @@
 from zope.component.interfaces import IFactory
 from zope.catalog.interfaces import ICatalog
-from zope.component import createObject, queryUtility
+from zope.component import createObject, queryUtility, getUtility
 from zope.interface import implementedBy
-from waeup.kofa.interfaces import academic_sessions_vocab, VALIDATED
+from waeup.kofa.interfaces import academic_sessions_vocab, VALIDATED, IKofaUtils
 from waeup.kofa.students.interfaces import (
     IStudentStudyLevel, IStudentNavigation, ICourseTicket)
@@ -79,5 +79,6 @@
         total = 0
         for ticket in self.values():
-            total += ticket.credits
+            if not ticket.outstanding:
+                total += ticket.credits
         return total
 
@@ -135,5 +136,8 @@
     @property
     def gpa(self):
-        return self.gpa_params[0]
+        """Return string formatted gpa value.
+        """
+        format_float = getUtility(IKofaUtils).format_float
+        return format_float(self.gpa_params[0], 2)
 
     @property
Index: /main/waeup.kofa/trunk/src/waeup/kofa/students/tests/test_browser.py
===================================================================
--- /main/waeup.kofa/trunk/src/waeup/kofa/students/tests/test_browser.py	(revision 14573)
+++ /main/waeup.kofa/trunk/src/waeup/kofa/students/tests/test_browser.py	(revision 14574)
@@ -2576,4 +2576,41 @@
         return
 
+    def test_student_course_registration_outstanding(self):
+        self.course = createObject('waeup.Course')
+        self.course.code = 'COURSE2'
+        self.course.semester = 1
+        self.course.credits = 45
+        self.course.passmark = 40
+        self.app['faculties']['fac1']['dep1'].courses.addCourse(
+            self.course)
+        IWorkflowState(self.student).setState('school fee paid')
+        self.browser.open(self.login_path)
+        self.browser.getControl(name="form.login").value = self.student_id
+        self.browser.getControl(name="form.password").value = 'spwd'
+        self.browser.getControl("Login").click()
+        self.browser.open(self.student_path + '/studycourse/add')
+        self.browser.getControl("Create course list now").click()
+        self.assertEqual(self.student['studycourse']['100'].number_of_tickets, 1)
+        self.student['studycourse'].current_level = 200
+        self.browser.getLink("Study Course").click()
+        self.browser.getLink("Add course list").click()
+        self.assertMatches('...Add current level 200 (Year 2)...',
+                           self.browser.contents)
+        self.browser.getControl("Create course list now").click()
+        self.browser.getLink("200").click()
+        self.browser.getLink("Edit course list").click()
+        self.browser.getLink("here").click()
+        self.browser.getControl(name="form.course").value = ['COURSE2']
+        self.browser.getControl("Add course ticket").click()
+        # Carryover COURSE1 in level 200 already has 10 credits
+        self.assertMatches(
+            '...Total credits exceed 50...', self.browser.contents)
+        # If COURSE1 is outstanding, its credits won't be considered
+        self.student['studycourse']['200']['COURSE1'].outstanding = True
+        self.browser.getControl("Add course ticket").click()
+        self.assertMatches(
+            '...Successfully added COURSE2...', self.browser.contents)
+        return
+
     def test_postgraduate_student_access(self):
         self.certificate.study_mode = 'pg_ft'
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 14573)
+++ /main/waeup.kofa/trunk/src/waeup/kofa/students/tests/test_export.py	(revision 14574)
@@ -492,5 +492,5 @@
             'validated_by,validation_date,'
             'student_id,number_of_tickets,certcode\r\n'
-            '0.0,,,0,0,,,,0,\r\n'
+            '0.00,,,0,0,,,,0,\r\n'
             )
         return
@@ -510,5 +510,5 @@
             'validated_by,validation_date,'
             'student_id,number_of_tickets,certcode\r\n'
-            '0.0,100,2012,A,100,,,A111111,1,CERT1\r\n'
+            '0.00,100,2012,A,100,,,A111111,1,CERT1\r\n'
             )
         return
@@ -526,5 +526,5 @@
             'validated_by,validation_date,'
             'student_id,number_of_tickets,certcode\r\n'
-            '0.0,100,2012,A,100,,,A111111,1,CERT1\r\n'
+            '0.00,100,2012,A,100,,,A111111,1,CERT1\r\n'
             )
         return
@@ -541,5 +541,5 @@
             'validated_by,validation_date,'
             'student_id,number_of_tickets,certcode\r\n'
-            '0.0,100,2012,A,100,,,A111111,1,CERT1\r\n'
+            '0.00,100,2012,A,100,,,A111111,1,CERT1\r\n'
             )
         return
@@ -560,5 +560,5 @@
             'validated_by,validation_date,'
             'student_id,number_of_tickets,certcode\r\n'
-            '0.0,100,2012,A,100,,,A111111,1,CERT1\r\n'
+            '0.00,100,2012,A,100,,,A111111,1,CERT1\r\n'
             )
         return
@@ -579,5 +579,5 @@
             'validated_by,validation_date,'
             'student_id,number_of_tickets,certcode\r\n'
-            '0.0,100,2012,A,100,,,A111111,1,CERT1\r\n'
+            '0.00,100,2012,A,100,,,A111111,1,CERT1\r\n'
             )
         return
@@ -614,7 +614,7 @@
             result,
             'automatic,carry_over,code,credits,dcode,fcode,level,level_session,'
-            'mandatory,passmark,score,semester,title,student_id,certcode,'
+            'mandatory,outstanding,passmark,score,semester,title,student_id,certcode,'
             'display_fullname\r\n'
-            '0,0,,,,,,,0,,,,,,,\r\n'
+            '0,0,,,,,,,0,0,,,,,,,\r\n'
             )
         return
@@ -633,7 +633,7 @@
             result,
             'automatic,carry_over,code,credits,dcode,fcode,level,level_session,'
-            'mandatory,passmark,score,semester,title,student_id,certcode,'
+            'mandatory,outstanding,passmark,score,semester,title,student_id,certcode,'
             'display_fullname\r\n'
-            '1,1,CRS1,100,DEP1,FAC1,100,2012,0,100,,2,Course 1,A111111,CERT1,'
+            '1,1,CRS1,100,DEP1,FAC1,100,2012,0,0,100,,2,Course 1,A111111,CERT1,'
             'Anna M. Tester\r\n'
             )
@@ -650,7 +650,7 @@
             result,
             'automatic,carry_over,code,credits,dcode,fcode,level,level_session,'
-            'mandatory,passmark,score,semester,title,student_id,certcode,'
+            'mandatory,outstanding,passmark,score,semester,title,student_id,certcode,'
             'display_fullname\r\n'
-            '1,1,CRS1,100,DEP1,FAC1,100,2012,0,100,,2,Course 1,A111111,CERT1,'
+            '1,1,CRS1,100,DEP1,FAC1,100,2012,0,0,100,,2,Course 1,A111111,CERT1,'
             'Anna M. Tester\r\n'
             )
@@ -666,7 +666,7 @@
             result,
             'automatic,carry_over,code,credits,dcode,fcode,level,level_session,'
-            'mandatory,passmark,score,semester,title,student_id,certcode,'
+            'mandatory,outstanding,passmark,score,semester,title,student_id,certcode,'
             'display_fullname\r\n'
-            '1,1,CRS1,100,DEP1,FAC1,100,2012,0,100,,2,Course 1,A111111,CERT1,'
+            '1,1,CRS1,100,DEP1,FAC1,100,2012,0,0,100,,2,Course 1,A111111,CERT1,'
             'Anna M. Tester\r\n'
             )
@@ -686,7 +686,7 @@
             result,
             'automatic,carry_over,code,credits,dcode,fcode,level,level_session,'
-            'mandatory,passmark,score,semester,title,student_id,certcode,'
+            'mandatory,outstanding,passmark,score,semester,title,student_id,certcode,'
             'display_fullname\r\n'
-            '1,1,CRS1,100,DEP1,FAC1,100,2012,0,100,,2,Course 1,A111111,CERT1,'
+            '1,1,CRS1,100,DEP1,FAC1,100,2012,0,0,100,,2,Course 1,A111111,CERT1,'
             'Anna M. Tester\r\n'
             )
@@ -700,7 +700,7 @@
             result,
             'automatic,carry_over,code,credits,dcode,fcode,level,level_session,'
-            'mandatory,passmark,score,semester,title,student_id,certcode,'
+            'mandatory,outstanding,passmark,score,semester,title,student_id,certcode,'
             'display_fullname\r\n'
-            '1,1,CRS1,100,DEP1,FAC1,100,2012,0,100,,2,Course 1,A111111,CERT1,'
+            '1,1,CRS1,100,DEP1,FAC1,100,2012,0,0,100,,2,Course 1,A111111,CERT1,'
             'Anna M. Tester\r\n'
             )
@@ -711,5 +711,5 @@
             result,
             'automatic,carry_over,code,credits,dcode,fcode,level,level_session,'
-            'mandatory,passmark,score,semester,title,student_id,certcode,'
+            'mandatory,outstanding,passmark,score,semester,title,student_id,certcode,'
             'display_fullname\r\n'
             )
@@ -724,7 +724,7 @@
             result,
             'automatic,carry_over,code,credits,dcode,fcode,level,level_session,'
-            'mandatory,passmark,score,semester,title,student_id,certcode,'
+            'mandatory,outstanding,passmark,score,semester,title,student_id,certcode,'
             'display_fullname\r\n'
-            '1,1,CRS1,100,DEP1,FAC1,110,2012,0,100,,2,Course 1,A111111,CERT1,'
+            '1,1,CRS1,100,DEP1,FAC1,110,2012,0,0,100,,2,Course 1,A111111,CERT1,'
             'Anna M. Tester\r\n'
             )
