Index: main/waeup.sirp/trunk/src/waeup/sirp/applicants/applicant.py
===================================================================
--- main/waeup.sirp/trunk/src/waeup/sirp/applicants/applicant.py	(revision 7249)
+++ main/waeup.sirp/trunk/src/waeup/sirp/applicants/applicant.py	(revision 7250)
@@ -45,5 +45,5 @@
         return u"xxx_1234"
 
-class Applicant(grok.Model):
+class Applicant(grok.Container):
     grok.implements(IApplicant,IApplicantEdit)
     grok.provides(IApplicant)
Index: main/waeup.sirp/trunk/src/waeup/sirp/applicants/browser.py
===================================================================
--- main/waeup.sirp/trunk/src/waeup/sirp/applicants/browser.py	(revision 7249)
+++ main/waeup.sirp/trunk/src/waeup/sirp/applicants/browser.py	(revision 7250)
@@ -21,5 +21,5 @@
 import sys
 import grok
-
+from time import time
 from datetime import datetime, date
 from zope.authentication.interfaces import ILogout, IAuthentication
@@ -58,5 +58,6 @@
 from waeup.sirp.utils.helpers import string_from_bytes, file_size
 from waeup.sirp.widgets.datewidget import (
-    FriendlyDateWidget, FriendlyDateDisplayWidget)
+    FriendlyDateWidget, FriendlyDateDisplayWidget,
+    FriendlyDatetimeDisplayWidget)
 from waeup.sirp.widgets.restwidget import ReSTDisplayWidget
 from waeup.sirp.widgets.objectwidget import (
@@ -66,8 +67,9 @@
     IApplicant, IApplicantEdit, IApplicantsRoot,
     IApplicantsContainer, IApplicantsContainerAdd, application_types_vocab,
-    MAX_UPLOAD_SIZE,
+    MAX_UPLOAD_SIZE, IApplicantOnlinePayment,
     )
-from waeup.sirp.applicants.workflow import INITIALIZED, STARTED
+from waeup.sirp.applicants.workflow import INITIALIZED, STARTED, PAID
 from waeup.sirp.students.viewlets import PrimaryStudentNavTab
+from waeup.sirp.students.interfaces import IStudentsUtils
 
 grok.context(IWAeUPObject) # Make IWAeUPObject the default context
@@ -228,4 +230,13 @@
         return self.context.application_number
 
+class OnlinePaymentBreadcrumb(Breadcrumb):
+    """A breadcrumb for payments.
+    """
+    grok.context(IApplicantOnlinePayment)
+
+    @property
+    def title(self):
+        return self.context.p_id
+
 class ApplicantsAuthTab(PrimaryNavTab):
     """Applicants tab in primary navigation.
@@ -233,5 +244,5 @@
     grok.context(IWAeUPObject)
     grok.order(3)
-    grok.require('waeup.viewApplicationsTab')
+    grok.require('waeup.viewApplicantsTab')
     pnav = 3
     tab_title = u'Applicants'
@@ -478,4 +489,165 @@
             return '<a href="%s">%s - %s</a>' %(url,code,title)
         return ''
+
+    def formatDatetime(self,datetimeobj):
+        if isinstance(datetimeobj, datetime):
+            return datetimeobj.strftime("%Y-%m-%d %H:%M:%S")
+        else:
+            return None
+
+class AcceptanceFeePaymentAddPage(grok.View):
+    """ Page to add an online payment ticket
+    """
+    grok.context(IApplicant)
+    grok.name('addafp')
+    grok.require('waeup.payApplicant')
+
+    def update(self):
+        p_category = 'acceptance'
+        d = {}
+        session = str(self.context.__parent__.year)
+        try:
+            academic_session = grok.getSite()['configuration'][session]
+        except KeyError:
+            self.flash('Session configuration object is not available.')
+            return
+        timestamp = "%d" % int(time()*1000)
+        #order_id = "%s%s" % (student_id[1:],timestamp)
+        for key in self.context.keys():
+            ticket = self.context[key]
+            if ticket.p_state == 'paid':
+                  self.flash(
+                      'This type of payment has already been made.')
+                  self.redirect(self.url(self.context))
+                  return
+        payment = createObject(u'waeup.ApplicantOnlinePayment')
+        payment.p_id = "p%s" % timestamp
+        payment.p_item = self.context.__parent__.title
+        payment.p_year = self.context.__parent__.year
+        payment.p_category = p_category
+        payment.amount_auth = academic_session.acceptance_fee
+        payment.surcharge_1 = academic_session.surcharge_1
+        payment.surcharge_2 = academic_session.surcharge_2
+        payment.surcharge_3 = academic_session.surcharge_3
+        self.context[payment.p_id] = payment
+        #import pdb; pdb.set_trace()
+        self.flash('Payment ticket created.')
+        return
+
+    def render(self):
+        usertype = getattr(self.request.principal, 'user_type', None)
+        if usertype == 'applicant':
+            self.redirect(self.url(self.context, '@@edit'))
+            return
+        self.redirect(self.url(self.context, '@@manage'))
+        return
+
+
+class OnlinePaymentDisplayFormPage(WAeUPDisplayFormPage):
+    """ Page to view an online payment ticket
+    """
+    grok.context(IApplicantOnlinePayment)
+    grok.name('index')
+    grok.require('waeup.viewApplication')
+    form_fields = grok.AutoFields(IApplicantOnlinePayment)
+    form_fields['creation_date'].custom_widget = FriendlyDatetimeDisplayWidget('le')
+    form_fields['payment_date'].custom_widget = FriendlyDatetimeDisplayWidget('le')
+    pnav = 3
+
+    @property
+    def title(self):
+        return 'Online Payment Ticket %s' % self.context.p_id
+
+    @property
+    def label(self):
+        return '%s: Online Payment Ticket %s' % (
+            self.context.__parent__.fullname,self.context.p_id)
+
+class PaymentReceiptActionButton(ManageActionButton):
+    grok.order(1)
+    grok.context(IApplicantOnlinePayment)
+    grok.view(OnlinePaymentDisplayFormPage)
+    grok.require('waeup.viewApplication')
+    icon = 'actionicon_pdf.png'
+    text = 'Download payment receipt'
+    target = 'payment_receipt.pdf'
+
+    @property
+    def target_url(self):
+        if self.context.p_state != 'paid':
+            return ''
+        return self.view.url(self.view.context, self.target)
+
+class RequestCallbackActionButton(ManageActionButton):
+    grok.order(2)
+    grok.context(IApplicantOnlinePayment)
+    grok.view(OnlinePaymentDisplayFormPage)
+    grok.require('waeup.payApplicant')
+    icon = 'actionicon_call.png'
+    text = 'Request callback'
+    target = 'callback'
+
+    @property
+    def target_url(self):
+        if self.context.p_state != 'unpaid':
+            return ''
+        return self.view.url(self.view.context, self.target)
+
+class OnlinePaymentCallbackPage(grok.View):
+    """ Callback view
+    """
+    grok.context(IApplicantOnlinePayment)
+    grok.name('callback')
+    grok.require('waeup.payApplicant')
+
+    # This update method simulates a valid callback und must be
+    # specified in the customization package. The parameters must be taken
+    # from the incoming request.
+    def update(self):
+        if self.context.p_state == 'paid':
+            self.flash('This ticket has already been paid.')
+            return
+        self.context.r_amount_approved = self.context.amount_auth
+        self.context.r_card_num = u'0000'
+        self.context.r_code = u'00'
+        self.context.p_state = 'paid'
+        self.context.payment_date = datetime.now()
+        ob_class = self.__implemented__.__name__.replace('waeup.sirp.','')
+        self.context.__parent__.loggerInfo(
+            ob_class, 'valid callback: %s' % self.context.p_id)
+        self.wf_info = IWorkflowInfo(self.context.__parent__)
+        self.wf_info.fireTransition('pay')
+        self.flash('Valid callback received.')
+        return
+
+    def render(self):
+        self.redirect(self.url(self.context, '@@index'))
+        return
+
+class ExportPDFPaymentSlipPage(grok.View):
+    """Deliver a PDF slip of the context.
+    """
+    grok.context(IApplicantOnlinePayment)
+    grok.name('payment_receipt.pdf')
+    grok.require('waeup.viewApplication')
+    form_fields = grok.AutoFields(IApplicantOnlinePayment)
+    form_fields['creation_date'].custom_widget = FriendlyDateDisplayWidget('le')
+    form_fields['payment_date'].custom_widget = FriendlyDateDisplayWidget('le')
+    prefix = 'form'
+
+    @property
+    def label(self):
+        return 'Online Payment Receipt %s' % self.context.p_id
+
+    def render(self):
+        if self.context.p_state != 'paid':
+            self.flash('Ticket not yet paid.')
+            self.redirect(self.url(self.context))
+            return
+        applicantview = ApplicantDisplayFormPage(self.context.__parent__,
+            self.request)
+        students_utils = getUtility(IStudentsUtils)
+        return students_utils.renderPDF(self,'Payment', 'payment_receipt.pdf',
+            self.context.__parent__, applicantview)
 
 class PDFActionButton(ManageActionButton):
@@ -646,4 +818,6 @@
     manage_applications = True
     pnav = 3
+    display_actions = [['Save', 'Final Submit'],
+                       ['Add online payment ticket','Remove selected tickets']]
 
     def update(self):
@@ -716,4 +890,53 @@
         return
 
+    def formatDatetime(self,datetimeobj):
+        if isinstance(datetimeobj, datetime):
+            return datetimeobj.strftime("%Y-%m-%d %H:%M:%S")
+        else:
+            return None
+
+    def unremovable(self, ticket):
+        usertype = getattr(self.request.principal, 'user_type', None)
+        if not usertype:
+            return False
+        return self.request.principal.user_type == 'applicant' and ticket.r_code
+
+    # This method is also used by the ApplicantEditFormPage
+    def delPaymentTickets(self, **data):
+        form = self.request.form
+        if form.has_key('val_id'):
+            child_id = form['val_id']
+        else:
+            self.flash('No payment selected.')
+            self.redirect(self.url(self.context))
+            return
+        if not isinstance(child_id, list):
+            child_id = [child_id]
+        deleted = []
+        for id in child_id:
+            # Applicants are not allowed to remove used payment tickets
+            if not self.unremovable(self.context[id]):
+                try:
+                    del self.context[id]
+                    deleted.append(id)
+                except:
+                    self.flash('Could not delete %s: %s: %s' % (
+                            id, sys.exc_info()[0], sys.exc_info()[1]))
+        if len(deleted):
+            self.flash('Successfully removed: %s' % ', '.join(deleted))
+            ob_class = self.__implemented__.__name__.replace('waeup.sirp.','')
+            self.context.loggerInfo(ob_class, 'removed: % s' % ', '.join(deleted))
+        return
+
+    @grok.action('Add online payment ticket')
+    def addPaymentTicket(self, **data):
+        self.redirect(self.url(self.context, '@@addafp'))
+
+    @grok.action('Remove selected tickets')
+    def removePaymentTickets(self, **data):
+        self.delPaymentTickets(**data)
+        self.redirect(self.url(self.context) + '/@@manage')
+        return
+
 class ApplicantEditFormPage(ApplicantManageFormPage):
     """An applicant-centered edit view for applicant data.
@@ -731,4 +954,19 @@
     title = u'Your Application Form'
 
+    @property
+    def display_actions(self):
+        state = IWorkflowState(self.context).getState()
+        if state == INITIALIZED:
+            actions = [[],[]]
+        elif state == STARTED:
+            actions = [['Save'],
+                       ['Add online payment ticket','Remove selected tickets']]
+        elif state == PAID:
+            actions = [['Save', 'Final Submit'],
+                       ['Remove selected tickets']]
+        elif state == SUBMITTED:
+            actions = [[],[]]
+        return actions
+
     def emit_lock_message(self):
         self.flash('The requested form is locked (read-only).')
@@ -748,4 +986,14 @@
             return 'Passport confirmation box not ticked.'
         return False
+
+    @grok.action('Add online payment ticket')
+    def addPaymentTicket(self, **data):
+        self.redirect(self.url(self.context, '@@addafp'))
+
+    @grok.action('Remove selected tickets')
+    def removePaymentTickets(self, **data):
+        self.delPaymentTickets(**data)
+        self.redirect(self.url(self.context) + '/@@edit')
+        return
 
     @grok.action('Save')
@@ -754,5 +1002,4 @@
             return # error during image upload. Ignore other values
         self.applyData(self.context, **data)
-        #self.context._p_changed = True
         self.flash('Form has been saved.')
         return
@@ -770,5 +1017,5 @@
         # This shouldn't happen, but the application officer
         # might have forgotten to lock the form after changing the state
-        if state != STARTED:
+        if state != PAID:
             self.flash('This form cannot be submitted. Wrong state!')
             return
Index: main/waeup.sirp/trunk/src/waeup/sirp/applicants/browser_templates/applicantdisplaypage.pt
===================================================================
--- main/waeup.sirp/trunk/src/waeup/sirp/applicants/browser_templates/applicantdisplaypage.pt	(revision 7249)
+++ main/waeup.sirp/trunk/src/waeup/sirp/applicants/browser_templates/applicantdisplaypage.pt	(revision 7250)
@@ -43,2 +43,26 @@
   </tbody>
 </table>
+<h3 i18n:translate="">Acceptance Fee Payment Tickets</h3>
+<table class="zebra">
+  <thead>
+    <tr>
+      <th>Payment Id</th>
+      <th>Creation Date</th>
+      <th>Payment Date</th>
+      <th>Category</th>
+      <th>Item</th>
+      <th>State</th>
+    </tr>
+  </thead>
+  <tbody>
+    <tr tal:repeat="value context/values">
+      <td> <a tal:attributes="href value/__name__">
+      <span tal:content="value/p_id">PID</span></a></td>
+      <td tal:content="python: view.formatDatetime(value.creation_date)">CREATION DATE</td>
+      <td tal:content="python: view.formatDatetime(value.payment_date)">PAYMENT DATE</td>
+      <td tal:content ="value/category">CATEGORY</td>
+      <td tal:content ="value/p_item">ITEM</td>
+      <td tal:content ="value/state">STATE</td>
+    </tr>
+  </tbody>
+</table>
Index: main/waeup.sirp/trunk/src/waeup/sirp/applicants/browser_templates/applicanteditpage.pt
===================================================================
--- main/waeup.sirp/trunk/src/waeup/sirp/applicants/browser_templates/applicanteditpage.pt	(revision 7249)
+++ main/waeup.sirp/trunk/src/waeup/sirp/applicants/browser_templates/applicanteditpage.pt	(revision 7250)
@@ -95,7 +95,51 @@
 
   <div class="actionButtons" tal:condition="view/availableActions">
-    <input tal:repeat="action view/actions"
-           tal:replace="structure action/render"
-           />
+    <span tal:repeat="action view/actions"
+          tal:omit-tag="">
+      <input tal:condition="python:action.label in view.display_actions[0]"
+             tal:replace="structure action/render"/>
+    </span>
+  </div>
+
+  <br /><br />
+  <h3 i18n:translate="">Acceptance Fee Payment Tickets</h3>
+
+  <table class="display dataTableManage">
+    <thead>
+    <tr>
+      <th>&nbsp;</th>
+      <th>Payment Id</th>
+      <th>Creation Date</th>
+      <th>Payment Date</th>
+      <th>Category</th>
+      <th>Item</th>
+      <th>State</th>
+    </tr>
+    </thead>
+    <tbody>
+      <tr tal:repeat="cl context/values">
+         <td>
+          <input type="checkbox"
+    		 name="val_id"
+    		 tal:attributes="value cl/__name__"
+         tal:condition="python: not view.unremovable(cl)" />
+        </td>
+        <td> <a tal:attributes="href cl/__name__">
+        <span tal:content="cl/p_id">PID</span></a></td>
+        <td tal:content="python: view.formatDatetime(cl.creation_date)">CREATION DATE</td>
+        <td tal:content="python: view.formatDatetime(cl.payment_date)">PAYMENT DATE</td>
+      <td tal:content ="cl/category">CATEGORY</td>
+      <td tal:content ="cl/p_item">ITEM</td>
+      <td tal:content ="cl/state">STATE</td>
+      </tr>
+    </tbody>
+  </table>
+
+  <div class="actionButtons" tal:condition="view/availableActions">
+    <span tal:repeat="action view/actions"
+          tal:omit-tag="">
+      <input tal:condition="python:action.label in view.display_actions[1]"
+             tal:replace="structure action/render"/>
+    </span>
   </div>
 </form>
Index: main/waeup.sirp/trunk/src/waeup/sirp/applicants/interfaces.py
===================================================================
--- main/waeup.sirp/trunk/src/waeup/sirp/applicants/interfaces.py	(revision 7249)
+++ main/waeup.sirp/trunk/src/waeup/sirp/applicants/interfaces.py	(revision 7250)
@@ -29,5 +29,6 @@
 from zope.security.interfaces import IGroupClosureAwarePrincipal as IPrincipal
 from zc.sourcefactory.basic import BasicSourceFactory
-from waeup.sirp.interfaces import IWAeUPObject, year_range, validate_email
+from waeup.sirp.interfaces import (
+    IWAeUPObject, year_range, validate_email, academic_sessions_vocab)
 from waeup.sirp.university.vocabularies import application_categories
 from waeup.sirp.students.vocabularies import (
@@ -38,4 +39,5 @@
   AppCatCertificateSource,
   )
+from waeup.sirp.payments.interfaces import IOnlinePayment
 
 #: Maximum upload size for applicant passport photographs (in bytes)
@@ -351,5 +353,5 @@
         )
     student_id = schema.TextLine(
-        title = u'Student ID',
+        title = u'Student Id',
         required = False,
         readonly = True,
@@ -398,4 +400,17 @@
         readonly = True,
         )
+
+class IApplicantOnlinePayment(IOnlinePayment):
+    """An applicant payment via payment gateways.
+
+    """
+    p_year = schema.Choice(
+        title = u'Payment Session',
+        source = academic_sessions_vocab,
+        required = False,
+        )
+
+IApplicantOnlinePayment['p_year'].order = IApplicantOnlinePayment[
+    'p_year'].order
 
 class IApplicantsContainerProvider(Interface):
Index: main/waeup.sirp/trunk/src/waeup/sirp/applicants/payment.py
===================================================================
--- main/waeup.sirp/trunk/src/waeup/sirp/applicants/payment.py	(revision 7250)
+++ main/waeup.sirp/trunk/src/waeup/sirp/applicants/payment.py	(revision 7250)
@@ -0,0 +1,54 @@
+## $Id$
+##
+## Copyright (C) 2011 Uli Fouquet & Henrik Bettermann
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, write to the Free Software
+## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+##
+"""
+Acceptance fee payment.
+"""
+import grok
+from zope.component.interfaces import IFactory
+from zope.interface import implementedBy
+from waeup.sirp.payments import OnlinePayment
+from waeup.sirp.applicants.interfaces import IApplicantOnlinePayment
+from waeup.sirp.utils.helpers import attrs_to_fields
+
+class ApplicantOnlinePayment(OnlinePayment):
+    """This is an online payment.
+    """
+    grok.implements(IApplicantOnlinePayment)
+    grok.provides(IApplicantOnlinePayment)
+
+    def __init__(self):
+        super(ApplicantOnlinePayment, self).__init__()
+        p_id = None
+        return
+
+ApplicantOnlinePayment = attrs_to_fields(ApplicantOnlinePayment)
+
+# Applicant online payments must be importable. So we might need a factory.
+class ApplicantOnlinePaymentFactory(grok.GlobalUtility):
+    """A factory for applicant online payments.
+    """
+    grok.implements(IFactory)
+    grok.name(u'waeup.ApplicantOnlinePayment')
+    title = u"Create a new online payment.",
+    description = u"This factory instantiates new online payment instances."
+
+    def __call__(self, *args, **kw):
+        return ApplicantOnlinePayment()
+
+    def getInterfaces(self):
+        return implementedBy(ApplicantOnlinePayment)
Index: main/waeup.sirp/trunk/src/waeup/sirp/applicants/permissions.py
===================================================================
--- main/waeup.sirp/trunk/src/waeup/sirp/applicants/permissions.py	(revision 7249)
+++ main/waeup.sirp/trunk/src/waeup/sirp/applicants/permissions.py	(revision 7250)
@@ -30,5 +30,5 @@
 
 class ViewApplicationsTab(grok.Permission):
-    grok.name('waeup.viewApplicationsTab')
+    grok.name('waeup.viewApplicantsTab')
 
 class ViewMyApplicationDataTab(grok.Permission):
@@ -38,4 +38,7 @@
     grok.name('waeup.manageApplication')
 
+class PayApplicant(grok.Permission):
+    grok.name('waeup.payApplicant')
+
 # Local role
 class ApplicationOwner(grok.Role):
@@ -43,5 +46,5 @@
     grok.title(u'Application Owner')
     grok.permissions('waeup.handleApplication', 'waeup.viewApplication',
-                     'waeup.Authenticated')
+                     'waeup.Authenticated', 'waeup.payApplicant')
 
 # Site role
@@ -55,3 +58,3 @@
     grok.title(u'Applications Officer')
     grok.permissions('waeup.manageApplication', 'waeup.viewApplication'
-                     'waeup.viewApplicationsTab')
+                     'waeup.viewApplicantsTab', 'waeup.payApplicant')
Index: main/waeup.sirp/trunk/src/waeup/sirp/applicants/tests/test_payment.py
===================================================================
--- main/waeup.sirp/trunk/src/waeup/sirp/applicants/tests/test_payment.py	(revision 7250)
+++ main/waeup.sirp/trunk/src/waeup/sirp/applicants/tests/test_payment.py	(revision 7250)
@@ -0,0 +1,46 @@
+## $Id$
+##
+## Copyright (C) 2011 Uli Fouquet & Henrik Bettermann
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, write to the Free Software
+## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+##
+"""Tests for application payments.
+
+"""
+
+from waeup.sirp.testing import FunctionalLayer, FunctionalTestCase
+from waeup.sirp.applicants.payment import (
+    ApplicantOnlinePayment, ApplicantOnlinePaymentFactory)
+from waeup.sirp.payments.interfaces import IOnlinePayment
+
+class ApplicantOnlinePaymentFactoryTest(FunctionalTestCase):
+
+    layer = FunctionalLayer
+
+    def setUp(self):
+        super(ApplicantOnlinePaymentFactoryTest, self).setUp()
+        self.factory = ApplicantOnlinePaymentFactory()
+        return
+
+    def tearDown(self):
+        super(ApplicantOnlinePaymentFactoryTest, self).tearDown()
+        return
+
+    def test_factory(self):
+        obj = self.factory()
+        assert isinstance(obj, ApplicantOnlinePayment)
+
+    def test_getInterfaces(self):
+        implemented_by = self.factory.getInterfaces()
+        assert implemented_by.isOrExtends(IOnlinePayment)
Index: main/waeup.sirp/trunk/src/waeup/sirp/applicants/workflow.py
===================================================================
--- main/waeup.sirp/trunk/src/waeup/sirp/applicants/workflow.py	(revision 7249)
+++ main/waeup.sirp/trunk/src/waeup/sirp/applicants/workflow.py	(revision 7250)
@@ -28,4 +28,5 @@
 INITIALIZED = 'initialized'
 STARTED = 'started'
+PAID = 'paid'
 SUBMITTED = 'submitted'
 ADMITTED = 'admitted'
@@ -50,8 +51,15 @@
 
     Transition(
+        transition_id = 'pay',
+        title = 'Pay acceptance fee',
+        msg = 'Fee paid',
+        source = STARTED,
+        destination = PAID),
+
+    Transition(
         transition_id = 'submit',
         title = 'Submit application',
         msg = 'Application submitted',
-        source = STARTED,
+        source = PAID,
         destination = SUBMITTED),
 
@@ -86,5 +94,5 @@
     Transition(
         transition_id = 'reset1',
-        title = 'Reset application',
+        title = 'Reset application to started',
         msg = 'Application reset',
         source = SUBMITTED,
@@ -93,5 +101,5 @@
     Transition(
         transition_id = 'reset2',
-        title = 'Reset application',
+        title = 'Reset application to started',
         msg = 'Application reset',
         source = ADMITTED,
@@ -100,5 +108,5 @@
     Transition(
         transition_id = 'reset3',
-        title = 'Reset application',
+        title = 'Reset application to started',
         msg = 'Application reset',
         source = NOT_ADMITTED,
@@ -107,8 +115,14 @@
     Transition(
         transition_id = 'reset4',
-        title = 'Reset application',
+        title = 'Reset application to started',
         msg = 'Application reset',
         source = CREATED,
         destination = STARTED),
+    Transition(
+        transition_id = 'reset5',
+        title = 'Reset application to paid',
+        msg = 'Application reset',
+        source = SUBMITTED,
+        destination = PAID),
     )
 
