Index: main/kofacustom.iuokada/trunk/src/kofacustom/iuokada/applicants/applicant.py
===================================================================
--- main/kofacustom.iuokada/trunk/src/kofacustom/iuokada/applicants/applicant.py	(revision 16105)
+++ main/kofacustom.iuokada/trunk/src/kofacustom/iuokada/applicants/applicant.py	(revision 16142)
@@ -28,10 +28,10 @@
 from kofacustom.iuokada.applicants.interfaces import(
     ICustomApplicant, ICustomUGApplicantEdit, ICustomPGApplicantEdit,
-    IPUTMEApplicantEdit, ICustomApplicantRefereeReport)
+    IPUTMEApplicantEdit, ICustomApplicantRefereeReport, ITranscriptApplicant)
 
 class CustomApplicant(NigeriaApplicant):
 
     grok.implements(ICustomApplicant, ICustomUGApplicantEdit,
-        ICustomPGApplicantEdit, IPUTMEApplicantEdit)
+        ICustomPGApplicantEdit, IPUTMEApplicantEdit, ITranscriptApplicant)
     grok.provides(ICustomApplicant)
 
Index: main/kofacustom.iuokada/trunk/src/kofacustom/iuokada/applicants/browser.py
===================================================================
--- main/kofacustom.iuokada/trunk/src/kofacustom/iuokada/applicants/browser.py	(revision 16105)
+++ main/kofacustom.iuokada/trunk/src/kofacustom/iuokada/applicants/browser.py	(revision 16142)
@@ -54,5 +54,6 @@
     ICustomPGApplicant, ICustomUGApplicant, ICustomApplicant,
     ICustomPGApplicantEdit, ICustomUGApplicantEdit,
-    ICustomApplicantOnlinePayment, ICustomApplicantRefereeReport
+    ICustomApplicantOnlinePayment, ICustomApplicantRefereeReport,
+    ITranscriptApplicant
     )
 from kofacustom.iuokada.interfaces import MessageFactory as _
@@ -120,4 +121,18 @@
     'screening_date',)
 
+TRANS_OMIT_FIELDS = ('locked', 'suspended', 'applicant_id',)
+
+TRANS_SHORT_OMIT_FIELDS = TRANS_OMIT_FIELDS + (
+    'date_of_birth',
+    'sex',
+    'nationality',
+    'entry_mode',
+    'entry_session',
+    'end_session',
+    'course_studied',
+    'course_changed',
+    'change_level',
+    )
+
 class CustomApplicantsContainerPage(ApplicantsContainerPage):
     """The standard view for regular applicant containers.
@@ -143,4 +158,18 @@
     @property
     def form_fields(self):
+        if self.target is not None and self.target == 'transfull':
+            form_fields = grok.AutoFields(ITranscriptApplicant)
+            for field in TRANS_OMIT_FIELDS:
+                form_fields = form_fields.omit(field)
+            form_fields['dispatch_address'].custom_widget = BytesDisplayWidget
+            form_fields['perm_address'].custom_widget = BytesDisplayWidget
+            return form_fields
+        if self.target is not None and self.target == 'transshort':
+            form_fields = grok.AutoFields(ITranscriptApplicant)
+            for field in TRANS_SHORT_OMIT_FIELDS:
+                form_fields = form_fields.omit(field)
+            form_fields['dispatch_address'].custom_widget = BytesDisplayWidget
+            form_fields['perm_address'].custom_widget = BytesDisplayWidget
+            return form_fields
         if self.target is not None and self.target.startswith('pg'):
             form_fields = grok.AutoFields(ICustomPGApplicant)
@@ -207,4 +236,14 @@
     @property
     def form_fields(self):
+        if self.target is not None and self.target == 'transfull':
+            form_fields = grok.AutoFields(ITranscriptApplicant)
+            for field in TRANS_OMIT_FIELDS:
+                form_fields = form_fields.omit(field)
+            return form_fields
+        if self.target is not None and self.target == 'transshort':
+            form_fields = grok.AutoFields(ITranscriptApplicant)
+            for field in TRANS_SHORT_OMIT_FIELDS:
+                form_fields = form_fields.omit(field)
+            return form_fields
         if self.target is not None and self.target.startswith('pg'):
             form_fields = grok.AutoFields(ICustomPGApplicant)
@@ -281,4 +320,14 @@
     @property
     def form_fields(self):
+        if self.target is not None and self.target == 'transfull':
+            form_fields = grok.AutoFields(ITranscriptApplicant)
+            for field in TRANS_OMIT_FIELDS:
+                form_fields = form_fields.omit(field)
+            return form_fields
+        if self.target is not None and self.target == 'transshort':
+            form_fields = grok.AutoFields(ITranscriptApplicant)
+            for field in TRANS_SHORT_OMIT_FIELDS:
+                form_fields = form_fields.omit(field)
+            return form_fields
         if self.target is not None and self.target.startswith('pg'):
             form_fields = grok.AutoFields(ICustomPGApplicantEdit)
Index: main/kofacustom.iuokada/trunk/src/kofacustom/iuokada/applicants/interfaces.py
===================================================================
--- main/kofacustom.iuokada/trunk/src/kofacustom/iuokada/applicants/interfaces.py	(revision 16105)
+++ main/kofacustom.iuokada/trunk/src/kofacustom/iuokada/applicants/interfaces.py	(revision 16142)
@@ -1,2 +1,3 @@
+# -*- coding: utf-8 -*-
 ## $Id$
 ##
@@ -20,12 +21,16 @@
 
 from zope import schema
+from zc.sourcefactory.basic import BasicSourceFactory
 from waeup.kofa.applicants.interfaces import (
     IApplicantBaseData,
     AppCatCertificateSource, CertificateSource)
 from waeup.kofa.schoolgrades import ResultEntryField
+from waeup.kofa.university.vocabularies import StudyModeSource
 from waeup.kofa.interfaces import (
-    SimpleKofaVocabulary, academic_sessions_vocab, validate_email)
+    SimpleKofaVocabulary, academic_sessions_vocab, validate_email,
+    IKofaObject)
 from waeup.kofa.schema import FormattedDate, TextLineChoice, PhoneNumber
-from waeup.kofa.students.vocabularies import nats_vocab, GenderSource
+from waeup.kofa.students.vocabularies import (
+    nats_vocab, GenderSource, StudyLevelSource)
 from waeup.kofa.applicants.interfaces import (
     contextual_reg_num_source, IApplicantBaseData, IApplicantRefereeReport)
@@ -88,4 +93,24 @@
     )
 
+DESTINATION_COST = {
+    'none': ('To the moon', 1000000000.0, 1),
+    #'nigeria': ('Nigeria', 2000.0, 1),
+    #'africa': ('Africa ', 3000.0, 2),
+    #'eurasia': ('Europe/Asia ', 3000.0, 3),
+    }
+
+class DestinationCostSource(BasicSourceFactory):
+    """A source that delivers continents and shipment costs.
+    """
+    def getValues(self):
+        sorted_items = sorted(DESTINATION_COST.items(),
+                              key=lambda element: element[1][2])
+        return [item[0] for item in sorted_items]
+
+    def getTitle(self, value):
+        return u"%s (₦ %s)" % (
+            DESTINATION_COST[value][0],
+            DESTINATION_COST[value][1])
+
 class ICustomUGApplicant(IApplicantBaseData):
     """An undergraduate applicant.
@@ -370,5 +395,165 @@
     'nysc_number'].order =  ICustomPGApplicant['nysc_year'].order
 
-class ICustomApplicant(ICustomUGApplicant, ICustomPGApplicant):
+class ITranscriptApplicant(IKofaObject):
+    """A transcript applicant.
+    """
+
+    suspended = schema.Bool(
+        title = _(u'Account suspended'),
+        default = False,
+        required = False,
+        )
+
+    locked = schema.Bool(
+        title = _(u'Form locked'),
+        default = False,
+        required = False,
+        )
+
+    applicant_id = schema.TextLine(
+        title = _(u'Transcript Application Id'),
+        required = False,
+        readonly = False,
+        )
+
+    #student_id = schema.TextLine(
+    #    title = _(u'Kofa Student Id'),
+    #    required = False,
+    #    readonly = False,
+    #    )
+
+    #reg_number = schema.TextLine(
+    #    title = _(u'Registration Number'),
+    #    readonly = False,
+    #    required = False,
+    #    )
+
+    matric_number = schema.TextLine(
+        title = _(u'Matriculation Number'),
+        readonly = False,
+        required = True,
+        )
+
+    firstname = schema.TextLine(
+        title = _(u'First Name'),
+        required = True,
+        )
+
+    middlename = schema.TextLine(
+        title = _(u'Middle Name'),
+        required = False,
+        )
+
+    lastname = schema.TextLine(
+        title = _(u'Last Name (Surname)'),
+        required = True,
+        )
+
+    date_of_birth = FormattedDate(
+        title = _(u'Date of Birth'),
+        required = False,
+        #date_format = u'%d/%m/%Y', # Use grok-instance-wide default
+        show_year = True,
+        )
+
+    sex = schema.Choice(
+        title = _(u'Gender'),
+        source = GenderSource(),
+        required = True,
+        )
+
+    nationality = schema.Choice(
+        vocabulary = nats_vocab,
+        title = _(u'Nationality'),
+        required = False,
+        )
+
+    email = schema.ASCIILine(
+        title = _(u'Email Address'),
+        required = True,
+        constraint=validate_email,
+        )
+
+    phone = PhoneNumber(
+        title = _(u'Phone'),
+        description = u'',
+        required = False,
+        )
+
+    perm_address = schema.Text(
+        title = _(u'Current Local Address'),
+        required = False,
+        readonly = False,
+        )
+
+    dispatch_address = schema.Text(
+        title = _(u'Dispatch Addresses'),
+        description = u'Addresses to which transcripts should be posted. '
+                       'Addresses must involve same courier charges.',
+        required = False,
+        readonly = False,
+        )
+
+    charge = schema.Choice(
+        title = _(u'Courier Charge'),
+        source = DestinationCostSource(),
+        required = True,
+        )
+
+    entry_mode = schema.Choice(
+        title = _(u'Entry Mode'),
+        source = StudyModeSource(),
+        required = False,
+        readonly = False,
+        )
+
+    entry_session = schema.Choice(
+        title = _(u'Entry Session'),
+        source = academic_sessions_vocab,
+        required = False,
+        readonly = False,
+        )
+
+    end_session = schema.Choice(
+        title = _(u'End Session'),
+        source = academic_sessions_vocab,
+        required = False,
+        readonly = False,
+        )
+
+    course_studied = schema.Choice(
+        title = _(u'Course of Study / Degree'),
+        source = CertificateSource(),
+        required = False,
+        readonly = False,
+        )
+
+    course_changed = schema.Choice(
+        title = _(u'Change of Study Course'),
+        description = u'If yes, select previous course of study.',
+        source = CertificateSource(),
+        readonly = False,
+        required = False,
+        )
+
+    change_level = schema.Choice(
+        title = _(u'Change Level'),
+        description = u'If yes, select level at which you changed course of study.',
+        source = StudyLevelSource(),
+        required = False,
+        readonly = False,
+        )
+
+    no_copies = schema.Choice(
+        title = _(u'Number of Copies'),
+        description = u'Must correspond with the number of dispatch addresses above.',
+        values=[1, 2, 3, 4],
+        required = False,
+        readonly = False,
+        default = 1,
+        )
+
+class ICustomApplicant(ICustomUGApplicant, ICustomPGApplicant,
+    ITranscriptApplicant):
     """An interface for both types of applicants.
 
Index: main/kofacustom.iuokada/trunk/src/kofacustom/iuokada/applicants/utils.py
===================================================================
--- main/kofacustom.iuokada/trunk/src/kofacustom/iuokada/applicants/utils.py	(revision 16105)
+++ main/kofacustom.iuokada/trunk/src/kofacustom/iuokada/applicants/utils.py	(revision 16142)
@@ -19,7 +19,10 @@
 """
 
+import grok
+from time import time
 from kofacustom.nigeria.applicants.utils import NigeriaApplicantsUtils
 from waeup.kofa.applicants.workflow import (INITIALIZED,
     STARTED, PAID, ADMITTED, NOT_ADMITTED, SUBMITTED, CREATED)
+from kofacustom.iuokada.applicants.interfaces import DESTINATION_COST
 from kofacustom.iuokada.interfaces import MessageFactory as _
 
@@ -55,4 +58,6 @@
         'pg': ['Postgraduate Programmes', 'PG'],
         'ug': ['Undergraduate Programmes', 'UG'],
+        'transfull': ['Transcript Application (without student record)', 'TRF'],
+        'transshort': ['Transcript Application (with student record)', 'TRS'],
         }
 
@@ -97,2 +102,38 @@
             title = "NA / %s (%s)" % (value.title, value.code)
         return title
+
+    def setPaymentDetails(self, container, payment, applicant):
+        """Set the payment data of an applicant.
+        In contrast to its `StudentsUtils` counterpart, the payment ticket
+        must exist and is an argument of this method.
+        """
+        timestamp = ("%d" % int(time()*10000))[1:]
+        session = str(container.year)
+        try:
+            session_config = grok.getSite()['configuration'][session]
+        except KeyError:
+            return _(u'Session configuration object is not available.')
+        payment.p_id = "p%s" % timestamp
+        payment.p_item = container.title
+        payment.p_session = container.year
+        payment.amount_auth = 0.0
+        if applicant.special:
+            if applicant.special_application:
+                fee_name = applicant.special_application + '_fee'
+                payment.amount_auth = getattr(session_config, fee_name, None)
+                if payment.amount_auth in (0.0, None):
+                    return _('Amount could not be determined.')
+                payment.p_category = applicant.special_application
+            return
+        elif applicant.container_code.startswith('trans'):
+            cost = DESTINATION_COST[applicant.charge][1]
+            payment.amount_auth = applicant.no_copies * cost
+            payment.p_category = 'transcript'
+            return
+        else:
+            payment.p_category = 'application'
+            container_fee = container.application_fee
+            if not container_fee:
+                return _('Amount could not be determined.')
+            payment.amount_auth = container_fee
+        return
