Index: main/waeup.kofa/trunk/src/waeup/kofa/students/payments.py
===================================================================
--- main/waeup.kofa/trunk/src/waeup/kofa/students/payments.py	(revision 12883)
+++ main/waeup.kofa/trunk/src/waeup/kofa/students/payments.py	(revision 12889)
@@ -22,7 +22,10 @@
 from zope.component.interfaces import IFactory
 from zope.interface import implementedBy
+from zope.schema.interfaces import ConstraintNotSatisfied
+from hurry.workflow.interfaces import IWorkflowInfo
 from waeup.kofa.interfaces import MessageFactory as _
 from waeup.kofa.students.interfaces import (
     IStudentPaymentsContainer, IStudentNavigation, IStudentOnlinePayment)
+from waeup.kofa.students.workflow import CLEARED, RETURNING, PAID
 from waeup.kofa.payments import PaymentsContainer, OnlinePayment
 from waeup.kofa.payments.interfaces import IPayer
@@ -69,5 +72,5 @@
         return self.__parent__.__parent__.writeLogMessage(view, message)
 
-    def _createActivationCodes(self):
+    def _redeemTicket(self):
         student = self.student
         if self.p_category == 'clearance':
@@ -79,4 +82,22 @@
             self.ac = pin
         elif self.p_category in ('schoolfee', 'schoolfee_1'):
+            # Bypass activation code creation if next session
+            # can be started directly.
+            if student['studycourse'].next_session_allowed:
+                try:
+                    if student.state == CLEARED:
+                        IWorkflowInfo(student).fireTransition(
+                            'pay_first_school_fee')
+                        return None
+                    elif student.state == RETURNING:
+                        IWorkflowInfo(student).fireTransition(
+                            'pay_school_fee')
+                        return None
+                    elif student.state == PAID:
+                        IWorkflowInfo(student).fireTransition(
+                            'pay_pg_fee')
+                        return None
+                except ConstraintNotSatisfied:
+                    pass
             # Create SFE access code
             pin, error = create_accesscode(
@@ -105,5 +126,5 @@
         """
         if self.p_current:
-            error = self._createActivationCodes()
+            error = self._redeemTicket()
             if error is not None:
                 return 'danger', error, error
@@ -117,5 +138,5 @@
         """
         if self.p_current:
-            error = self._createActivationCodes()
+            error = self._redeemTicket()
             if error is not None:
                 return 'danger', error, error
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 12883)
+++ main/waeup.kofa/trunk/src/waeup/kofa/students/tests/test_browser.py	(revision 12889)
@@ -929,8 +929,10 @@
         cat = queryUtility(ICatalog, name='payments_catalog')
         results = list(cat.searchResults(p_state=('unpaid', 'unpaid')))
-        self.assertTrue(len(results), 1)
+        self.assertEqual(len(results), 2)
         self.assertTrue(results[0] is self.student['payments'][value])
-
         # Managers can approve the payment
+        # If, by some reason, the state has already changed,
+        # an access code is created after approval.
+        IWorkflowState(self.student).setState('school fee paid')
         self.assertEqual(len(self.app['accesscodes']['SFE-0']),0)
         self.browser.open(payment_url)
@@ -951,6 +953,5 @@
             '"zope.mgr",K1000000,%s,schoolfee,40000.0,AP,,,,,,\n' % value
             in logcontent)
-
-        # The authorized amount has been stored in the access code
+        # The authorized amount has been stored in the new access code
         self.assertEqual(
             self.app['accesscodes']['SFE-0'].values()[0].cost,40000.0)
@@ -969,5 +970,7 @@
 
         # Now the first ticket is paid and no more ticket of same type
-        # (with same p_item, p_session and p_category) can be added
+        # (with same p_item, p_session and p_category) can be added.
+        # First we have to reset the workflow state.
+        IWorkflowState(self.student).setState('cleared')
         self.browser.open(self.payments_path)
         self.browser.getLink("Add current session payment ticket").click()
@@ -1047,4 +1050,46 @@
         self.assertEqual(ac.owner, self.student_id)
         self.assertEqual(ac.cost, 4567.0)
+        return
+
+    def test_manage_payments_bypass_ac_creation(self):
+        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
+        self.browser.open(self.payments_path)
+        IWorkflowState(self.student).setState('cleared')
+        self.browser.getLink("Add current session payment ticket").click()
+        self.browser.getControl(name="form.p_category").value = ['schoolfee']
+        self.browser.getControl("Create ticket").click()
+        ctrl = self.browser.getControl(name='val_id')
+        value = ctrl.options[0]
+        self.browser.getLink(value).click()
+        payment_url = self.browser.url
+        logfile = os.path.join(
+            self.app['datacenter'].storage, 'logs', 'students.log')
+        # The ticket can be found in the payments_catalog
+        cat = queryUtility(ICatalog, name='payments_catalog')
+        results = list(cat.searchResults(p_state=('unpaid', 'unpaid')))
+        self.assertTrue(len(results), 1)
+        self.assertTrue(results[0] is self.student['payments'][value])
+        # Managers can approve the payment
+        self.browser.open(payment_url)
+        self.browser.getLink("Approve payment").click()
+        self.assertMatches('...Payment approved...',
+                          self.browser.contents)
+        # Approval is logged in students.log ...
+        logcontent = open(logfile).read()
+        self.assertTrue(
+            'zope.mgr - students.browser.OnlinePaymentApprovePage '
+            '- K1000000 - schoolfee payment approved'
+            in logcontent)
+        # ... and in payments.log
+        logfile = os.path.join(
+            self.app['datacenter'].storage, 'logs', 'payments.log')
+        logcontent = open(logfile).read()
+        self.assertTrue(
+            '"zope.mgr",K1000000,%s,schoolfee,40000.0,AP,,,,,,\n' % value
+            in logcontent)
+        # Student is in state school fee paid, no activation
+        # code was necessary.
+        self.assertEqual(self.student.state, 'school fee paid')
+        self.assertEqual(len(self.app['accesscodes']['SFE-0']),0)
         return
 
@@ -2702,15 +2747,16 @@
                           self.browser.contents)
 
-        # The new SFE-0 pin can be used for starting new session
-        self.browser.open(self.studycourse_path)
-        self.browser.getLink('Start new session').click()
-        pin = self.app['accesscodes']['SFE-0'].keys()[0]
-        parts = pin.split('-')[1:]
-        sfeseries, sfenumber = parts
-        self.browser.getControl(name="ac_series").value = sfeseries
-        self.browser.getControl(name="ac_number").value = sfenumber
-        self.browser.getControl("Start now").click()
-        self.assertMatches('...Session started...',
-                           self.browser.contents)
+        ## The new SFE-0 pin can be used for starting new session
+        #self.browser.open(self.studycourse_path)
+        #self.browser.getLink('Start new session').click()
+        #pin = self.app['accesscodes']['SFE-0'].keys()[0]
+        #parts = pin.split('-')[1:]
+        #sfeseries, sfenumber = parts
+        #self.browser.getControl(name="ac_series").value = sfeseries
+        #self.browser.getControl(name="ac_number").value = sfenumber
+        #self.browser.getControl("Start now").click()
+        #self.assertMatches('...Session started...',
+        #                   self.browser.contents)
+
         self.assertTrue(self.student.state == 'school fee paid')
         return
@@ -2874,15 +2920,16 @@
                           self.browser.contents)
 
-        # The new SFE-0 pin can be used for starting session
-        self.browser.open(self.studycourse_path)
-        self.browser.getLink('Start new session').click()
-        pin = self.app['accesscodes']['SFE-0'].keys()[0]
-        parts = pin.split('-')[1:]
-        sfeseries, sfenumber = parts
-        self.browser.getControl(name="ac_series").value = sfeseries
-        self.browser.getControl(name="ac_number").value = sfenumber
-        self.browser.getControl("Start now").click()
-        self.assertMatches('...Session started...',
-                           self.browser.contents)
+        ## The new SFE-0 pin can be used for starting session
+        #self.browser.open(self.studycourse_path)
+        #self.browser.getLink('Start new session').click()
+        #pin = self.app['accesscodes']['SFE-0'].keys()[0]
+        #parts = pin.split('-')[1:]
+        #sfeseries, sfenumber = parts
+        #self.browser.getControl(name="ac_series").value = sfeseries
+        #self.browser.getControl(name="ac_number").value = sfenumber
+        #self.browser.getControl("Start now").click()
+        #self.assertMatches('...Session started...',
+        #                   self.browser.contents)
+
         self.assertTrue(self.student.state == 'school fee paid')
 
