Index: main/waeup.kofa/trunk/CHANGES.txt
===================================================================
--- main/waeup.kofa/trunk/CHANGES.txt	(revision 11598)
+++ main/waeup.kofa/trunk/CHANGES.txt	(revision 11599)
@@ -5,5 +5,11 @@
 ===================
 
-* Add setMatricNumber method to StudentsUtils.
+* Special application procedure modified. Special applicants must submit
+  their form before they can download payment slips. Thus the regular
+  workflow is now applied but, in contrast to regular applications,
+  special applicants can create and pay more than one payment ticket.
+  Also application slips are hidden.
+
+* Add setMatricNumber and constructMatricNumber methods to StudentsUtils.
 
 * When sending mails, always set from-address to 'no-reply@waeup.org'.
Index: main/waeup.kofa/trunk/src/waeup/kofa/applicants/browser.py
===================================================================
--- main/waeup.kofa/trunk/src/waeup/kofa/applicants/browser.py	(revision 11598)
+++ main/waeup.kofa/trunk/src/waeup/kofa/applicants/browser.py	(revision 11599)
@@ -491,5 +491,5 @@
     def form_fields(self):
         if self.context.special:
-            form_fields = grok.AutoFields(ISpecialApplicant)
+            form_fields = grok.AutoFields(ISpecialApplicant).omit('locked')
         else:
             form_fields = grok.AutoFields(IApplicant).omit(
@@ -728,8 +728,10 @@
 
     def render(self):
-        #if self.context.p_state != 'paid':
-        #    self.flash(_('Ticket not yet paid.'))
-        #    self.redirect(self.url(self.context))
-        #    return
+        if self.context.__parent__.special \
+            and self.context.__parent__.state != SUBMITTED:
+            self.flash(_('Please pay and submit before '
+                         'trying to download payment slips.'))
+            self.redirect(self.url(self.context))
+            return
         applicantview = ApplicantBaseDisplayFormPage(self.context.__parent__,
             self.request)
@@ -948,5 +950,5 @@
     def form_fields(self):
         if self.context.special:
-            form_fields = grok.AutoFields(ISpecialApplicant)
+            form_fields = grok.AutoFields(ISpecialApplicant).omit('locked')
             form_fields['applicant_id'].for_display = True
         else:
@@ -965,4 +967,7 @@
         if state == STARTED:
             actions = [[_('Save')],
+                [_('Add online payment ticket'),_('Remove selected tickets')]]
+        elif self.context.special and state == PAID:
+            actions = [[_('Save'), _('Final Submit')],
                 [_('Add online payment ticket'),_('Remove selected tickets')]]
         elif state == PAID:
Index: main/waeup.kofa/trunk/src/waeup/kofa/applicants/browser_templates/applicantdisplaypage.pt
===================================================================
--- main/waeup.kofa/trunk/src/waeup/kofa/applicants/browser_templates/applicantdisplaypage.pt	(revision 11598)
+++ main/waeup.kofa/trunk/src/waeup/kofa/applicants/browser_templates/applicantdisplaypage.pt	(revision 11599)
@@ -38,6 +38,6 @@
   </tbody>
 </table>
-<h3  i18n:domain="waeup.kofa" i18n:translate="">
-    Application Fee Payment Tickets
+<h3 i18n:domain="waeup.kofa" i18n:translate="">
+    Payment Tickets
 </h3>
 <table i18n:domain="waeup.kofa" class="table table-condensed">
Index: main/waeup.kofa/trunk/src/waeup/kofa/applicants/browser_templates/applicanteditpage.pt
===================================================================
--- main/waeup.kofa/trunk/src/waeup/kofa/applicants/browser_templates/applicanteditpage.pt	(revision 11598)
+++ main/waeup.kofa/trunk/src/waeup/kofa/applicants/browser_templates/applicanteditpage.pt	(revision 11599)
@@ -89,5 +89,5 @@
   <br /><br />
   <h3 i18n:translate="">
-      Application Fee Payment Tickets
+      Payment Tickets
   </h3>
 
Index: main/waeup.kofa/trunk/src/waeup/kofa/applicants/interfaces.py
===================================================================
--- main/waeup.kofa/trunk/src/waeup/kofa/applicants/interfaces.py	(revision 11598)
+++ main/waeup.kofa/trunk/src/waeup/kofa/applicants/interfaces.py	(revision 11599)
@@ -490,4 +490,10 @@
         )
 
+    locked = schema.Bool(
+        title = _(u'Form locked'),
+        default = False,
+        required = False,
+        )
+
     applicant_id = schema.TextLine(
         title = _(u'Applicant Id'),
Index: main/waeup.kofa/trunk/src/waeup/kofa/applicants/payment.py
===================================================================
--- main/waeup.kofa/trunk/src/waeup/kofa/applicants/payment.py	(revision 11598)
+++ main/waeup.kofa/trunk/src/waeup/kofa/applicants/payment.py	(revision 11599)
@@ -29,4 +29,5 @@
 from waeup.kofa.payments.interfaces import IPayer
 from waeup.kofa.applicants.interfaces import IApplicantOnlinePayment
+from waeup.kofa.applicants.workflow import PAID
 from waeup.kofa.utils.helpers import attrs_to_fields
 
@@ -44,5 +45,5 @@
         """Process applicant after payment was approved.
         """
-        if not self.__parent__.special:
+        if not (self.__parent__.special and self.__parent__.state == PAID):
             wf_info = IWorkflowInfo(self.__parent__)
             try:
@@ -59,5 +60,5 @@
         """Process applicant after payment was made.
         """
-        if not self.__parent__.special:
+        if not (self.__parent__.special and self.__parent__.state == PAID):
             wf_info = IWorkflowInfo(self.__parent__)
             try:
Index: main/waeup.kofa/trunk/src/waeup/kofa/applicants/tests/test_browser.py
===================================================================
--- main/waeup.kofa/trunk/src/waeup/kofa/applicants/tests/test_browser.py	(revision 11598)
+++ main/waeup.kofa/trunk/src/waeup/kofa/applicants/tests/test_browser.py	(revision 11599)
@@ -55,4 +55,6 @@
 container_name_2 = u'app%s' % session_2
 
+SAMPLE_IMAGE = os.path.join(os.path.dirname(__file__), 'test_image.jpg')
+
 class ApplicantsFullSetup(FunctionalTestCase):
     # A test case that only contains a setup and teardown
@@ -976,9 +978,9 @@
             '<span>200.0</span>' in self.browser.contents)
         self.assertEqual(len(self.special_applicant.keys()), 1)
-        # The applicant's workflow remain started ...
+        # The applicant's workflow state is paid ...
         self.special_applicant.values()[0].approveApplicantPayment()
-        self.assertEqual(self.special_applicant.state, 'started')
+        self.assertEqual(self.special_applicant.state, 'paid')
         self.browser.open(applicant_path + '/edit')
-        # ... so that he can create further tickets.
+        # ... but he can create further tickets.
         self.browser.getControl(name="form.special_application").value = [
             'clearance']
@@ -991,4 +993,27 @@
             '<td>Acceptance Fee</td>' in self.browser.contents)
         self.assertEqual(len(self.special_applicant.keys()), 2)
+        # Second payment can also be approved wthout error message
+        flashtype, msg, log = self.special_applicant.values()[1].approveApplicantPayment()
+        self.assertEqual(flashtype, 'success')
+        self.assertEqual(msg, 'Payment approved')
+        # Payment slips can't be downloaded ...
+        payment_id = self.special_applicant.keys()[0]
+        self.browser.open(applicant_path + '/' + payment_id)
+        self.browser.getLink("Download payment slip").click()
+        self.assertTrue(
+            'Please pay and submit before trying to download payment slips.'
+            in self.browser.contents)
+        # ... unless form is submitted.
+        self.browser.open(applicant_path + '/edit')
+        image = open(SAMPLE_IMAGE, 'rb')
+        ctrl = self.browser.getControl(name='form.passport')
+        file_ctrl = ctrl.mech_control
+        file_ctrl.add_file(image, filename='myphoto.jpg')
+        self.browser.getControl(name="confirm_passport").value = True
+        self.browser.getControl("Final Submit").click()
+        self.browser.open(applicant_path + '/' + payment_id)
+        self.browser.getLink("Download payment slip").click()
+        self.assertEqual(self.browser.headers['Content-Type'],
+                 'application/pdf')
         return
 
@@ -1022,9 +1047,9 @@
         self.assertTrue(
             'No passport picture uploaded' in self.browser.contents)
-        # Create a pseudo image file and select it to be uploaded in form
-        pseudo_image = StringIO('I pretend to be a graphics file')
+        # Use a real image file and select it to be uploaded in form
+        image = open(SAMPLE_IMAGE, 'rb')
         ctrl = self.browser.getControl(name='form.passport')
         file_ctrl = ctrl.mech_control
-        file_ctrl.add_file(pseudo_image, filename='myphoto.jpg')
+        file_ctrl.add_file(image, filename='myphoto.jpg')
         self.browser.getControl("Final Submit").click() # (finally) submit form
         # The picture has been uploaded but the form cannot be submitted
Index: main/waeup.kofa/trunk/src/waeup/kofa/applicants/viewlets.py
===================================================================
--- main/waeup.kofa/trunk/src/waeup/kofa/applicants/viewlets.py	(revision 11598)
+++ main/waeup.kofa/trunk/src/waeup/kofa/applicants/viewlets.py	(revision 11599)
@@ -214,5 +214,6 @@
         """Get a URL to the target...
         """
-        if self.context.state in ('initialized', 'started', 'paid'):
+        if self.context.state in ('initialized', 'started', 'paid') \
+            or self.context.special:
             return
         return self.view.url(self.view.context, self.target)
