Index: main/waeup.kofa/trunk/src/waeup/kofa/students/batching.py
===================================================================
--- main/waeup.kofa/trunk/src/waeup/kofa/students/batching.py	(revision 9027)
+++ main/waeup.kofa/trunk/src/waeup/kofa/students/batching.py	(revision 9028)
@@ -43,5 +43,6 @@
     IStudentOnlinePayment, IStudentVerdictUpdate)
 from waeup.kofa.students.workflow import  (
-    IMPORTABLE_STATES, IMPORTABLE_TRANSITIONS)
+    IMPORTABLE_STATES, IMPORTABLE_TRANSITIONS,
+    FORBIDDEN_POSTGRAD_TRANS, FORBIDDEN_POSTGRAD_STATES)
 from waeup.kofa.utils.batching import BatchProcessor
 
@@ -171,4 +172,12 @@
             if transition not in allowed_transitions:
                 return 'Transition not allowed.'
+            if transition in FORBIDDEN_POSTGRAD_TRANS and \
+                obj.is_postgrad:
+                return 'Transition not allowed (pg student).'
+        state = row.get('state', IGNORE_MARKER)
+        if state not in (IGNORE_MARKER, ''):
+            if state in FORBIDDEN_POSTGRAD_STATES and \
+                obj.is_postgrad:
+                return 'State not allowed (pg student).'
         return None
 
@@ -262,17 +271,16 @@
         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:
-            if row['state'] not in (IGNORE_MARKER, ''):
-                errs.append(('state','not allowed'))
-            else:
-                # state is an attribute of Student and must not
-                # be changed if empty
-                conv_dict['state'] = IGNORE_MARKER
-
+        if row.has_key('transition'):
+            if row['transition'] not in IMPORTABLE_TRANSITIONS:
+                if row['transition'] not in (IGNORE_MARKER, ''):
+                    errs.append(('transition','not allowed'))
+        if row.has_key('state'):
+            if row['state'] not in IMPORTABLE_STATES:
+                if row['state'] not in (IGNORE_MARKER, ''):
+                    errs.append(('state','not allowed'))
+                else:
+                    # State is an attribute of Student and must not
+                    # be changed if empty.
+                    conv_dict['state'] = IGNORE_MARKER
         try:
             # Correct stud_id counter. As the IConverter for students
@@ -435,4 +443,17 @@
         return errs, inv_errs, conv_dict
 
+    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.
+        """
+        current_level = row.get('current_level', None)
+        if current_level == '999' and obj.state in FORBIDDEN_POSTGRAD_STATES:
+            return 'Not a pg student.'
+        return None
+
 class StudentStudyLevelProcessor(StudentProcessorBase):
     """A batch processor for IStudentStudyLevel objects.
Index: main/waeup.kofa/trunk/src/waeup/kofa/students/browser.py
===================================================================
--- main/waeup.kofa/trunk/src/waeup/kofa/students/browser.py	(revision 9027)
+++ main/waeup.kofa/trunk/src/waeup/kofa/students/browser.py	(revision 9028)
@@ -58,5 +58,6 @@
 from waeup.kofa.students.catalog import search
 from waeup.kofa.students.workflow import (CREATED, ADMITTED, PAID,
-    CLEARANCE, REQUESTED, RETURNING, CLEARED, REGISTERED, VALIDATED)
+    CLEARANCE, REQUESTED, RETURNING, CLEARED, REGISTERED, VALIDATED,
+    FORBIDDEN_POSTGRAD_TRANS)
 from waeup.kofa.students.studylevel import StudentStudyLevel, CourseTicket
 from waeup.kofa.students.vocabularies import StudyLevelSource
@@ -378,4 +379,7 @@
         allowed_transitions = [t for t in self.wf_info.getManualTransitions()
             if not t[0].startswith('pay')]
+        if self.context.is_postgrad:
+            allowed_transitions = [t for t in allowed_transitions
+                if not t[0] in FORBIDDEN_POSTGRAD_TRANS]
         return [dict(name='', title=_('No transition'))] +[
             dict(name=x, title=y) for x, y in allowed_transitions]
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 9027)
+++ main/waeup.kofa/trunk/src/waeup/kofa/students/tests/test_batching.py	(revision 9028)
@@ -23,8 +23,11 @@
 import unittest
 import datetime
+import grok
 from time import time
+from zope.event import notify
 from zope.component import createObject
 from zope.component.hooks import setSite, clearSite
 from zope.interface.verify import verifyClass, verifyObject
+from hurry.workflow.interfaces import IWorkflowState
 
 from waeup.kofa.app import University
@@ -178,11 +181,10 @@
     def setup_student(self, student):
         # set predictable values for `student`
-        student.matric_number = u'M123456'
+        student.matric_number = u'234'
         student.adm_code = u'my adm code'
         student.clearance_locked = False
         student.clr_code = u'my clr code'
         student.perm_address = u'Studentroad 21\nLagos 123456\n'
-        student.reg_number = u'123456'
-        student.student_id = u'A111111'
+        student.reg_number = u'123'
         student.firstname = u'Anna'
         student.lastname = u'Tester'
@@ -240,5 +242,5 @@
 
 
-class StudentProcessorTest(FunctionalTestCase):
+class StudentProcessorTest(StudentImportExportSetup):
 
     layer = FunctionalLayer
@@ -246,27 +248,12 @@
     def setUp(self):
         super(StudentProcessorTest, self).setUp()
-        # Setup a sample site for each test
-        app = University()
-        self.dc_root = tempfile.mkdtemp()
-        app['datacenter'].setStoragePath(self.dc_root)
-
-        # Prepopulate the ZODB...
-        self.getRootFolder()['app'] = app
-        # we add the site immediately after creation to the
-        # ZODB. Catalogs and other local utilities are not setup
-        # before that step.
-        self.app = self.getRootFolder()['app']
-        # Set site here. Some of the following setup code might need
-        # to access grok.getSite() and should get our new app then
-        setSite(app)
 
         # Add student with subobjects
         student = Student()
-        student.firstname = u'Anna'
-        student.lastname = u'Tester'
-        student.reg_number = u'123'
-        student.matric_number = u'234'
         self.app['students'].addStudent(student)
+        student = self.setup_student(student)
+        notify(grok.ObjectModifiedEvent(student))
         self.student = self.app['students'][student.student_id]
+
         self.processor = StudentProcessor()
         self.workdir = tempfile.mkdtemp()
@@ -292,11 +279,4 @@
         open(self.csv_file_duplicates, 'wb').write(STUDENT_SAMPLE_DATA_DUPLICATES)
 
-    def tearDown(self):
-        super(StudentProcessorTest, self).tearDown()
-        shutil.rmtree(self.workdir)
-        shutil.rmtree(self.dc_root)
-        clearSite()
-        return
-
     def test_interface(self):
         # Make sure we fulfill the interface contracts.
@@ -350,4 +330,22 @@
         self.assertEqual(initial_stud_id, new_stud_id)
         return
+
+    def test_checkUpdateRequirements(self):
+        # Make sure that pg students can't be updated with wrong transition.
+        err = self.processor.checkUpdateRequirements(self.student,
+            dict(reg_number='1', state='returning'), self.app)
+        self.assertTrue(err is None)
+        self.certificate.study_mode = 'pg_ft'
+        err = self.processor.checkUpdateRequirements(self.student,
+            dict(reg_number='1', state='returning'), self.app)
+        self.assertEqual(err, 'State not allowed (pg student).')
+        IWorkflowState(self.student).setState('school fee paid')
+        err = self.processor.checkUpdateRequirements(self.student,
+            dict(reg_number='1', transition='reset6'), self.app)
+        self.assertEqual(err, 'Transition not allowed (pg student).')
+        err = self.processor.checkUpdateRequirements(self.student,
+            dict(reg_number='1', transition='register_courses'), self.app)
+        self.assertEqual(err, 'Transition not allowed (pg student).')
+
 
     def test_delEntry(self):
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 9027)
+++ main/waeup.kofa/trunk/src/waeup/kofa/students/tests/test_export.py	(revision 9028)
@@ -48,6 +48,6 @@
 
             'my adm code,0,my clr code,1981-02-04#,anna@sample.com,,'
-            'Anna,Tester,M123456,M.,NG,"Studentroad 21\nLagos 123456\n",'
-            '+234-123-12345#,123456,f,A111111,0,,created'
+            'Anna,Tester,234,M.,NG,"Studentroad 21\nLagos 123456\n",'
+            '+234-123-12345#,123,f,A111111,0,,created'
             in result
             )
@@ -68,6 +68,6 @@
 
             'my adm code,0,my clr code,1981-02-04#,anna@sample.com,,'
-            'Anna,Tester,M123456,M.,NG,"Studentroad 21\nLagos 123456\n",'
-            '+234-123-12345#,123456,f,A111111,0,,created'
+            'Anna,Tester,234,M.,NG,"Studentroad 21\nLagos 123456\n",'
+            '+234-123-12345#,123,f,A111111,0,,created'
             in result
             )
@@ -87,6 +87,6 @@
 
             'my adm code,0,my clr code,1981-02-04#,anna@sample.com,,'
-            'Anna,Tester,M123456,M.,NG,"Studentroad 21\nLagos 123456\n",'
-            '+234-123-12345#,123456,f,A111111,0,,created'
+            'Anna,Tester,234,M.,NG,"Studentroad 21\nLagos 123456\n",'
+            '+234-123-12345#,123,f,A111111,0,,created'
             in result
             )
Index: main/waeup.kofa/trunk/src/waeup/kofa/students/workflow.py
===================================================================
--- main/waeup.kofa/trunk/src/waeup/kofa/students/workflow.py	(revision 9027)
+++ main/waeup.kofa/trunk/src/waeup/kofa/students/workflow.py	(revision 9028)
@@ -17,4 +17,6 @@
     REGISTERED, VALIDATED)
 
+FORBIDDEN_POSTGRAD_STATES = (RETURNING, REGISTERED, VALIDATED)
+
 REGISTRATION_TRANSITIONS = (
     Transition(
@@ -183,4 +185,5 @@
 IMPORTABLE_TRANSITIONS = [i.transition_id for i in REGISTRATION_TRANSITIONS]
 
+FORBIDDEN_POSTGRAD_TRANS = ['reset6', 'register_courses']
 LOCK_CLEARANCE_TRANS = ('reset2', 'request_clearance')
 UNLOCK_CLEARANCE_TRANS = ('reset3', 'reset4', 'start_clearance')
