Index: main/waeup.kofa/trunk/src/waeup/kofa/applicants/applicant.py
===================================================================
--- main/waeup.kofa/trunk/src/waeup/kofa/applicants/applicant.py	(revision 16225)
+++ main/waeup.kofa/trunk/src/waeup/kofa/applicants/applicant.py	(revision 16228)
@@ -68,4 +68,14 @@
         ]
 
+    applicant_graduated_mapping = [
+        ('firstname', 'firstname'),
+        ('middlename', 'middlename'),
+        ('lastname', 'lastname'),
+        ('sex', 'sex'),
+        ('date_of_birth', 'date_of_birth'),
+        ('email', 'email'),
+        ('phone', 'phone'),
+        ]
+
     def __init__(self):
         super(Applicant, self).__init__()
@@ -152,5 +162,13 @@
         return
 
-    def createStudent(self, view=None):
+    def _setGraduatedStudyCourseAttributes(self, studycourse):
+        studycourse.entry_mode = self.course_studied.study_mode
+        studycourse.current_level = self.course_studied.start_level
+        studycourse.certificate = self.course_studied
+        studycourse.entry_session = self.__parent__.year
+        studycourse.current_session = self.__parent__.year
+        return
+
+    def createStudent(self, view=None, graduated=False):
         """Create a student, fill with base data, create an application slip,
         copy applicant data and files.
@@ -158,6 +176,14 @@
         site = grok.getSite()
         # Is applicant in the correct state?
-        if self.state != 'admitted':
-            return False, _('Applicant has not yet been admitted.')
+        if graduated:
+            if self.state != 'processed':
+                return False, _('Applicant has not yet been processed.')
+            certificate = getattr(self, 'course_studied', None)
+            mapping = self.applicant_graduated_mapping
+        else:
+            if self.state != 'admitted':
+                return False, _('Applicant has not yet been admitted.')
+            certificate = getattr(self, 'course_admitted', None)
+            mapping = self.applicant_student_mapping
         # Does registration number exist?
         student = createObject(u'waeup.Student')
@@ -168,12 +194,12 @@
             site['students']._curr_stud_id -= 1
             return False, _('Registration Number exists.')
-        # Has the course_admitted field been properly filled?
-        if self.course_admitted is None:
+        # Has the course_admitted/course_studied field been properly filled?
+        if certificate is None:
             # Reset _curr_stud_id
             site['students']._curr_stud_id -= 1
-            return False, _('No course admitted provided.')
+            return False, _('No study course provided.')
         # Set student attributes
         try:
-            for item in self.applicant_student_mapping:
+            for item in mapping:
                 setattr(student, item[1], getattr(self, item[0], None))
         except RequiredMissing, err:
@@ -185,9 +211,9 @@
         # Prove if the certificate still exists
         try:
-            StudentStudyCourse().certificate = self.course_admitted
+            StudentStudyCourse().certificate = certificate
         except ConstraintNotSatisfied, err:
             # Reset _curr_stud_id
             site['students']._curr_stud_id -= 1
-            return False, 'ConstraintNotSatisfied: %s' % self.course_admitted.code
+            return False, 'ConstraintNotSatisfied: %s' % certificate.code
         # Finally prove if an application slip can be created
         try:
@@ -210,11 +236,17 @@
         # Save student_id
         self.student_id = student.student_id
-        # Fire transitions
-        IWorkflowInfo(self).fireTransition('create')
-        IWorkflowInfo(student).fireTransition('admit')
+        if graduated:
+            # Set state
+            IWorkflowState(student).setState('graduated')
+            # Save the certificate and set study course attributes
+            self._setGraduatedStudyCourseAttributes(student['studycourse'])
+        else:
+            # Fire transitions
+            IWorkflowInfo(self).fireTransition('create')
+            IWorkflowInfo(student).fireTransition('admit')
+            # Save the certificate and set study course attributes
+            self._setStudyCourseAttributes(student['studycourse'])
         # Set password
         IUserAccount(student).setPassword(self.application_number)
-        # Save the certificate and set study course attributes
-        self._setStudyCourseAttributes(student['studycourse'])
         self._copyPassportImage(student)
         self._copyFiles(student)
Index: main/waeup.kofa/trunk/src/waeup/kofa/applicants/interfaces.py
===================================================================
--- main/waeup.kofa/trunk/src/waeup/kofa/applicants/interfaces.py	(revision 16225)
+++ main/waeup.kofa/trunk/src/waeup/kofa/applicants/interfaces.py	(revision 16228)
@@ -491,9 +491,11 @@
         """
 
-    def createStudent():
+    def createStudent(view, graduated):
         """Create a student object from applicant data and copy
-        passport image and application slip.
-        """
-
+        passport image and application slip. If graduated is set True,
+        a graduated student is being created. This method is supposed
+        to be used for transcript applicants. It is tested but not
+        used in the base package.
+        """
 class ISpecialApplicant(IKofaObject):
     """This reduced interface is for former students or students who are not
Index: main/waeup.kofa/trunk/src/waeup/kofa/applicants/tests/test_applicantcopier.py
===================================================================
--- main/waeup.kofa/trunk/src/waeup/kofa/applicants/tests/test_applicantcopier.py	(revision 16225)
+++ main/waeup.kofa/trunk/src/waeup/kofa/applicants/tests/test_applicantcopier.py	(revision 16228)
@@ -56,5 +56,5 @@
         return
 
-    def test_copier(self):
+    def test_applicant_student_copier(self):
         self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
         self.prepare_applicant()
@@ -80,5 +80,5 @@
         # Current student_id has not changed.
         self.assertEqual(self.app['students']._curr_stud_id, 1000000)
-        self.assertTrue(msg == 'No course admitted provided.')
+        self.assertTrue(msg == 'No study course provided.')
         self.browser.open(self.manage_path)
         self.browser.getControl(name="form.course_admitted").value = ['CERT1']
@@ -158,4 +158,106 @@
         self.assertEqual(student['studycourse'].current_level, 100)
 
+    def test_applicant_graduated_copier(self):
+        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
+        self.prepare_applicant()
+        storage = getUtility(IExtFileStore)
+        # The stored image can be fetched
+        file_id = IFileStoreNameChooser(self.applicant).chooseName()
+        fd = storage.getFile(file_id)
+        file_len_orig = len(fd.read())
+        # We store a test pdf file
+        dummy_file = StringIO('test file')
+        testfile_id = IFileStoreNameChooser(
+            self.applicant).chooseName(attr='testfile.pdf')
+        test_file = storage.createFile(testfile_id, dummy_file)
+        # The stored file can be fetched
+        fd = storage.getFile(testfile_id)
+        testfile_len_orig = len(fd.read())
+        self.assertEqual(self.app['students']._curr_stud_id, 1000000)
+        # Let's try to create the student
+        (success, msg) = self.applicant.createStudent(graduated=True)
+        self.assertTrue(msg == 'Applicant has not yet been processed.')
+        IWorkflowState(self.applicant).setState('processed')
+        (success, msg) = self.applicant.createStudent(graduated=True)
+        # Current student_id has not changed.
+        self.assertEqual(self.app['students']._curr_stud_id, 1000000)
+        self.assertTrue(msg == 'No study course provided.')
+        # The course_studied field is not used in the base package. Thus we
+        # have to set the attribute manually.
+        self.applicant.course_studied = self.certificate
+        self.browser.open(self.manage_path)
+        self.browser.getControl("Save").click()
+        # Maybe the certificate has meanwhile been removed
+        del self.app['faculties']['fac1']['dep1'].certificates['CERT1']
+        (success, msg) = self.applicant.createStudent(graduated=True)
+        self.assertFalse(success)
+        self.assertTrue('ConstraintNotSatisfied: CERT1' in msg)
+        # Current student_id has not changed.
+        self.assertEqual(self.app['students']._curr_stud_id, 1000000)
+        # Ok, lets add the certificate and try again
+        self.app['faculties']['fac1']['dep1'].certificates.addCertificate(
+            self.certificate)
+        # Managers are not allowed to trigger the create transition manually
+        self.assertFalse('<option value="create">' in self.browser.contents)
+        # Graduated student can be created
+        (success, msg) = self.applicant.createStudent(graduated=True)
+        self.assertTrue('created' in msg)
+        # Current student_id has not changed.
+        self.assertEqual(self.app['students']._curr_stud_id, 1000001)
+        student_id = translate(msg, 'waeup.kofa').split()[1]
+        # View student container just created
+        student = self.app['students'][student_id]
+        student_path = 'http://localhost/app/students/%s' % student_id
+        self.browser.open(student_path)
+        self.assertEqual(self.browser.headers['Status'], '200 Ok')
+        self.assertEqual(self.browser.url, student_path)
+        # Student is in state graduated
+        self.assertEqual(student.state, 'graduated')
+        # Attributes properly set?
+        self.assertEqual(student.email, 'xx@yy.zz')
+        self.assertEqual(student.firstname, 'John')
+        self.assertEqual(student.middlename, 'Anthony')
+        self.assertEqual(student.lastname, 'Tester')
+        # student_id set in application record?
+        self.assertEqual(self.applicant.student_id, student.student_id)
+        # Check if passport image has been copied
+        file_id = IFileStoreNameChooser(student).chooseName(attr='passport.jpg')
+        fd = storage.getFile(file_id)
+        file_len = len(fd.read())
+        self.assertEqual(file_len_orig, file_len)
+        # Check if test file has been copied too (new)
+        file_id = IFileStoreNameChooser(student).chooseName(attr='testfile.jpg')
+        fd = storage.getFile(file_id)
+        file_len = len(fd.read())
+        self.assertEqual(testfile_len_orig, file_len)
+        # Check if application slip exists and is a PDF file
+        file_id = IFileStoreNameChooser(
+            student).chooseName(attr='application_slip.pdf')
+        pdf = storage.getFile(file_id).read()
+        self.assertTrue(len(pdf) > 0)
+        self.assertEqual(pdf[:8], '%PDF-1.4')
+        path = os.path.join(samples_dir(), 'application_slip.pdf')
+        open(path, 'wb').write(pdf)
+        print "Sample PDF application_slip.pdf written to %s" % path
+        # Check if there is an application slip link in UI
+        self.assertTrue('Application Slip' in self.browser.contents)
+        self.browser.getLink("Application Slip").click()
+        self.assertEqual(self.browser.headers['Status'], '200 Ok')
+        self.assertEqual(self.browser.headers['Content-Type'],
+                         'application/pdf')
+        # Has the student been properly indexed?
+        # Yes, we can find the student in department
+        self.browser.open('http://localhost/app/students')
+        self.browser.getControl(name="searchtype").value = ['depcode']
+        self.browser.getControl(name="searchterm").value = 'dep1'
+        self.browser.getControl("Find student(s)").click()
+        self.assertMatches('...John Anthony Tester...', self.browser.contents)
+        # Has the student studycourse the correct attributes?
+        self.assertEqual(student['studycourse'].certificate.code, 'CERT1')
+        self.assertEqual(student['studycourse'].entry_session, session)
+        self.assertEqual(student['studycourse'].entry_mode, 'ug_ft')
+        self.assertEqual(student['studycourse'].current_session, session)
+        self.assertEqual(student['studycourse'].current_level, 100)
+
     def test_batch_copying(self):
         self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
@@ -184,5 +286,5 @@
         notify(grok.ObjectModifiedEvent(self.applicant))
         self.browser.open(self.container_path + '/createallstudents')
-        self.assertTrue('No course admitted provided' in self.browser.contents)
+        self.assertTrue('No study course provided' in self.browser.contents)
         self.assertFalse('Successfully created records' in self.browser.contents)
         self.assertTrue('Failed records' in self.browser.contents)
@@ -215,5 +317,5 @@
         notify(grok.ObjectModifiedEvent(self.applicant))
         self.browser.open(self.root_path + '/createallstudents')
-        self.assertTrue('No course admitted provided' in self.browser.contents)
+        self.assertTrue('No study course provided' in self.browser.contents)
         self.applicant.course_admitted = self.certificate
         self.browser.open(self.root_path + '/createallstudents')
