Index: main/waeup.kofa/trunk/src/waeup/kofa/students/batching.py
===================================================================
--- main/waeup.kofa/trunk/src/waeup/kofa/students/batching.py	(revision 8308)
+++ main/waeup.kofa/trunk/src/waeup/kofa/students/batching.py	(revision 8309)
@@ -41,5 +41,6 @@
     IStudentStudyLevel, ICourseTicket,
     IStudentOnlinePayment, IStudentVerdictUpdate)
-from waeup.kofa.students.workflow import  IMPORTABLE_STATES
+from waeup.kofa.students.workflow import  (
+    IMPORTABLE_STATES, IMPORTABLE_TRANSITIONS)
 from waeup.kofa.utils.batching import BatchProcessor
 
@@ -64,7 +65,10 @@
         return sorted(list(set(
             ['student_id','reg_number','matric_number',
-            'password', 'state'] + fields.keys())))
+            'password', 'state', 'transition'] + fields.keys())))
 
     def checkHeaders(self, headerfields, mode='create'):
+        if 'state' in headerfields and 'transition' in headerfields:
+            raise FatalCSVError(
+                "State and transition can't be  imported at the same time!")
         if not 'reg_number' in headerfields and not 'student_id' \
             in headerfields and not 'matric_number' in headerfields:
@@ -148,4 +152,19 @@
         pass
 
+    def checkUpdateRequirements(self, obj, row, site):
+        """Checks requirements the object must fulfill when being updated.
+
+        This method is not used in case of deleting or adding objects.
+
+        Returns error messages as strings in case of requirement
+        problems.
+        """
+        transition = row.get('transition', IGNORE_MARKER)
+        if transition not in (IGNORE_MARKER, ''):
+            allowed_transitions = IWorkflowInfo(obj).getManualTransitionIds()
+            if transition not in allowed_transitions:
+                return 'Transition not allowed.'
+        return None
+
     def updateEntry(self, obj, row, site):
         """Update obj to the values given in row.
@@ -173,4 +192,9 @@
             history.addMessage(msg)
             row.pop('state')
+        transition = row.get('transition', IGNORE_MARKER)
+        if transition not in (IGNORE_MARKER, ''):
+            value = row['transition']
+            IWorkflowInfo(obj).fireTransition(value)
+            row.pop('transition')
 
         # apply other values...
@@ -218,4 +242,8 @@
         errs, inv_errs, conv_dict =  converter.fromStringDict(
             row, self.factory_name, mode=mode)
+        if row.has_key('transition') and \
+            not row['transition'] in IMPORTABLE_TRANSITIONS:
+            if row['transition'] not in (IGNORE_MARKER, ''):
+                errs.append(('transition','not allowed'))
         if row.has_key('state') and \
             not row['state'] in IMPORTABLE_STATES:
Index: main/waeup.kofa/trunk/src/waeup/kofa/students/tests/sample_student_data_update3.csv
===================================================================
--- main/waeup.kofa/trunk/src/waeup/kofa/students/tests/sample_student_data_update3.csv	(revision 8309)
+++ main/waeup.kofa/trunk/src/waeup/kofa/students/tests/sample_student_data_update3.csv	(revision 8309)
@@ -0,0 +1,5 @@
+student_id,reg_number,transition
+X666666,,request_clearance
+Y777777,,return
+,3,return
+,4,validate_courses
Index: main/waeup.kofa/trunk/src/waeup/kofa/students/tests/sample_student_data_update4.csv
===================================================================
--- main/waeup.kofa/trunk/src/waeup/kofa/students/tests/sample_student_data_update4.csv	(revision 8309)
+++ main/waeup.kofa/trunk/src/waeup/kofa/students/tests/sample_student_data_update4.csv	(revision 8309)
@@ -0,0 +1,2 @@
+student_id,reg_number,transition,state
+X666666,,request_clearance,returning
Index: main/waeup.kofa/trunk/src/waeup/kofa/students/tests/test_batching.py
===================================================================
--- main/waeup.kofa/trunk/src/waeup/kofa/students/tests/test_batching.py	(revision 8308)
+++ main/waeup.kofa/trunk/src/waeup/kofa/students/tests/test_batching.py	(revision 8309)
@@ -37,5 +37,5 @@
 from waeup.kofa.students.student import Student
 from waeup.kofa.testing import FunctionalLayer, FunctionalTestCase
-from waeup.kofa.interfaces import IBatchProcessor
+from waeup.kofa.interfaces import IBatchProcessor, FatalCSVError
 
 STUDENT_SAMPLE_DATA = open(
@@ -58,4 +58,18 @@
 
 STUDENT_HEADER_FIELDS_UPDATE2 = STUDENT_SAMPLE_DATA_UPDATE2.split(
+    '\n')[0].split(',')
+
+STUDENT_SAMPLE_DATA_UPDATE3 = open(
+    os.path.join(os.path.dirname(__file__), 'sample_student_data_update3.csv'),
+    'rb').read()
+
+STUDENT_HEADER_FIELDS_UPDATE3 = STUDENT_SAMPLE_DATA_UPDATE3.split(
+    '\n')[0].split(',')
+
+STUDENT_SAMPLE_DATA_UPDATE4 = open(
+    os.path.join(os.path.dirname(__file__), 'sample_student_data_update4.csv'),
+    'rb').read()
+
+STUDENT_HEADER_FIELDS_UPDATE4 = STUDENT_SAMPLE_DATA_UPDATE4.split(
     '\n')[0].split(',')
 
@@ -179,4 +193,8 @@
         self.csv_file_update2 = os.path.join(
             self.workdir, 'sample_student_data_update2.csv')
+        self.csv_file_update3 = os.path.join(
+            self.workdir, 'sample_student_data_update3.csv')
+        self.csv_file_update4 = os.path.join(
+            self.workdir, 'sample_student_data_update4.csv')
         self.csv_file_migration = os.path.join(
             self.workdir, 'sample_student_data_migration.csv')
@@ -186,4 +204,6 @@
         open(self.csv_file_update, 'wb').write(STUDENT_SAMPLE_DATA_UPDATE)
         open(self.csv_file_update2, 'wb').write(STUDENT_SAMPLE_DATA_UPDATE2)
+        open(self.csv_file_update3, 'wb').write(STUDENT_SAMPLE_DATA_UPDATE3)
+        open(self.csv_file_update4, 'wb').write(STUDENT_SAMPLE_DATA_UPDATE4)
         open(self.csv_file_migration, 'wb').write(STUDENT_SAMPLE_DATA_MIGRATION)
         open(self.csv_file_duplicates, 'wb').write(STUDENT_SAMPLE_DATA_DUPLICATES)
@@ -293,4 +313,27 @@
         assert claus.phone is None
         shutil.rmtree(os.path.dirname(fin_file))
+
+    def test_import_update3(self):
+        num, num_warns, fin_file, fail_file = self.processor.doImport(
+            self.csv_file, STUDENT_HEADER_FIELDS)
+        shutil.rmtree(os.path.dirname(fin_file))
+        num, num_warns, fin_file, fail_file = self.processor.doImport(
+            self.csv_file_update3, STUDENT_HEADER_FIELDS_UPDATE3, 'update')
+        content = open(fail_file).read()
+        self.assertEqual(
+            content,
+            'reg_number,student_id,transition,--ERRORS--\r\n'
+            '<IGNORE>,X666666,request_clearance,Transition not allowed.\r\n'
+            )
+        self.assertEqual(num_warns,1)
+        self.assertEqual(self.app['students']['Y777777'].state,'returning')
+
+    def test_import_update4(self):
+        num, num_warns, fin_file, fail_file = self.processor.doImport(
+            self.csv_file, STUDENT_HEADER_FIELDS)
+        shutil.rmtree(os.path.dirname(fin_file))
+        self.assertRaises(
+            FatalCSVError, self.processor.doImport, self.csv_file_update4,
+            STUDENT_HEADER_FIELDS_UPDATE4, 'update')
 
     def test_import_remove(self):
Index: main/waeup.kofa/trunk/src/waeup/kofa/students/workflow.py
===================================================================
--- main/waeup.kofa/trunk/src/waeup/kofa/students/workflow.py	(revision 8308)
+++ main/waeup.kofa/trunk/src/waeup/kofa/students/workflow.py	(revision 8309)
@@ -152,4 +152,6 @@
         destination = VALIDATED),
     )
+
+IMPORTABLE_TRANSITIONS = [i.transition_id for i in REGISTRATION_TRANSITIONS]
 
 LOCK_CLEARANCE_TRANS = ('reset2', 'request_clearance')
