Index: main/kofacustom.edocons/trunk/src/kofacustom/edocons/applicants/applicant.py
===================================================================
--- main/kofacustom.edocons/trunk/src/kofacustom/edocons/applicants/applicant.py	(revision 17028)
+++ main/kofacustom.edocons/trunk/src/kofacustom/edocons/applicants/applicant.py	(revision 17029)
@@ -23,10 +23,12 @@
 from kofacustom.nigeria.applicants.applicant import NigeriaApplicant
 from kofacustom.edocons.applicants.interfaces import(
-    ICustomApplicant, ICustomUGApplicantEdit, ICustomPGApplicantEdit, IPUTMEApplicantEdit)
+    ICustomApplicant, ICustomUGApplicantEdit, ICustomPGApplicantEdit, 
+    IPUTMEApplicantEdit, ITranscriptApplicant)
 
 class CustomApplicant(NigeriaApplicant):
 
     grok.implements(ICustomApplicant, ICustomUGApplicantEdit,
-        ICustomPGApplicantEdit, IPUTMEApplicantEdit)
+        ICustomPGApplicantEdit, IPUTMEApplicantEdit,
+        ITranscriptApplicant)
     grok.provides(ICustomApplicant)
 
Index: main/kofacustom.edocons/trunk/src/kofacustom/edocons/applicants/browser.py
===================================================================
--- main/kofacustom.edocons/trunk/src/kofacustom/edocons/applicants/browser.py	(revision 17028)
+++ main/kofacustom.edocons/trunk/src/kofacustom/edocons/applicants/browser.py	(revision 17029)
@@ -54,5 +54,5 @@
     ICustomPGApplicant, ICustomUGApplicant, ICustomApplicant,
     ICustomPGApplicantEdit, ICustomUGApplicantEdit,
-    ICustomApplicantOnlinePayment,
+    ICustomApplicantOnlinePayment, ITranscriptApplicant,
     )
 from kofacustom.nigeria.interfaces import MessageFactory as _
@@ -94,4 +94,16 @@
     'aggregate')
 
+TSC_OMIT_FIELDS = ('locked', 'suspended',
+    )
+    
+
+TSC_OMIT_EDIT_FIELDS = TSC_OMIT_FIELDS + (
+    'applicant_id',
+    'proc_date',
+    )
+
+TSC_OMIT_MANAGE_FIELDS = TSC_OMIT_FIELDS + (
+    'applicant_id',)     
+
 class CustomApplicantDisplayFormPage(NigeriaApplicantDisplayFormPage):
     """A display view for applicant data.
@@ -104,10 +116,15 @@
             for field in PG_OMIT_DISPLAY_FIELDS:
                 form_fields = form_fields.omit(field)
+        elif self.target is not None and self.target.startswith('tsc'):
+            form_fields = grok.AutoFields(ITranscriptApplicant)
+            for field in TSC_OMIT_FIELDS:
+                form_fields = form_fields.omit(field)            
+            return form_fields
         else:
             form_fields = grok.AutoFields(ICustomUGApplicant)
             for field in UG_OMIT_DISPLAY_FIELDS:
                 form_fields = form_fields.omit(field)
+            form_fields['notice'].custom_widget = BytesDisplayWidget
         #form_fields['perm_address'].custom_widget = BytesDisplayWidget
-        form_fields['notice'].custom_widget = BytesDisplayWidget
         if not getattr(self.context, 'student_id'):
             form_fields = form_fields.omit('student_id')
@@ -134,4 +151,9 @@
             for field in PG_OMIT_PDF_FIELDS:
                 form_fields = form_fields.omit(field)
+        elif self.target is not None and self.target.startswith('tsc'):
+            form_fields = grok.AutoFields(ITranscriptApplicant)
+            for field in TSC_OMIT_FIELDS:
+                form_fields = form_fields.omit(field)              
+            return form_fields
         else:
             form_fields = grok.AutoFields(ICustomUGApplicant)
@@ -164,4 +186,9 @@
             for field in PG_OMIT_MANAGE_FIELDS:
                 form_fields = form_fields.omit(field)
+        elif self.target is not None and self.target.startswith('tsc'):
+            form_fields = grok.AutoFields(ITranscriptApplicant)
+            for field in TSC_OMIT_MANAGE_FIELDS:
+                form_fields = form_fields.omit(field)              
+            return form_fields
         else:
             form_fields = grok.AutoFields(ICustomUGApplicant)
@@ -185,4 +212,9 @@
             for field in PG_OMIT_EDIT_FIELDS:
                 form_fields = form_fields.omit(field)
+        elif self.target is not None and self.target.startswith('tsc'):
+            form_fields = grok.AutoFields(ITranscriptApplicant)
+            for field in TSC_OMIT_EDIT_FIELDS:
+                form_fields = form_fields.omit(field)            
+            return form_fields
         else:
             form_fields = grok.AutoFields(ICustomUGApplicantEdit)
Index: main/kofacustom.edocons/trunk/src/kofacustom/edocons/applicants/interfaces.py
===================================================================
--- main/kofacustom.edocons/trunk/src/kofacustom/edocons/applicants/interfaces.py	(revision 17028)
+++ main/kofacustom.edocons/trunk/src/kofacustom/edocons/applicants/interfaces.py	(revision 17029)
@@ -1,2 +1,3 @@
+# -*- coding: utf-8 -*-
 ## $Id$
 ##
@@ -20,4 +21,9 @@
 
 from zope import schema
+from zope.interface import invariant, Invalid
+from zope.component import getUtility
+from zope.catalog.interfaces import ICatalog
+from zc.sourcefactory.basic import BasicSourceFactory
+from waeup.kofa.university.vocabularies import StudyModeSource
 from waeup.kofa.applicants.interfaces import (
     IApplicantBaseData,
@@ -25,5 +31,6 @@
 from waeup.kofa.schoolgrades import ResultEntryField
 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
@@ -54,4 +61,68 @@
     )
 
+DESTINATION_COST = {
+    #'none': ('To the moon', 1000000000.0, 1),
+    'nigeria': ('Within Nigeria', 20000.0, 1),
+    'africa': ('Within Africa ', 30000.0, 2),
+    'inter': ('International', 35000.0, 3),
+    'cert_nigeria': ('Certified Copy - Within Nigeria', 13000.0, 4),
+    'cert_africa': ('Certified Copy - Within Africa', 23000.0, 5),
+    'cert_inter': ('Certified Copy - International', 28000.0, 6),
+    }
+
+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 getToken(self, value):
+        return value
+
+    def getTitle(self, value):
+        return u"%s (₦ %s)" % (
+            DESTINATION_COST[value][0],
+            DESTINATION_COST[value][1])
+
+class OrderSource(BasicSourceFactory):
+    """
+    """
+    def getValues(self):
+        return ['o', 'c']
+
+    def getToken(self, value):
+        return value
+
+    def getTitle(self, value):
+        if value == 'o':
+            return _('Original')
+        if value == 'c':
+            return _('Certified True Copy')
+
+class TranscriptCertificateSource(CertificateSource):
+    """Include Department and Faculty in Title.
+    """
+    def getValues(self, context):
+        catalog = getUtility(ICatalog, name='certificates_catalog')
+        resultset = catalog.searchResults(code=(None, None))
+        resultlist = sorted(resultset, key=lambda
+            value: value.__parent__.__parent__.__parent__.code +
+            value.__parent__.__parent__.code +
+            value.code)
+        return resultlist
+
+    def getTitle(self, context, value):
+        """
+        """
+        try: title = "%s / %s / %s (%s)" % (
+            value.__parent__.__parent__.__parent__.title,
+            value.__parent__.__parent__.title,
+            value.title, value.code)
+        except AttributeError:
+            title = "NA / %s (%s)" % (value.title, value.code)
+        return title
+
 class ICustomUGApplicant(INigeriaUGApplicant):
     """An undergraduate applicant.
@@ -308,6 +379,201 @@
     """
 
-
-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,
+        )
+
+    matric_number = schema.TextLine(
+        title = _(u'Matriculation Number'),
+        readonly = False,
+        required = True,
+        )
+
+    firstname = schema.TextLine(
+        title = _(u'First Name in School'),
+        required = True,
+        )
+
+    middlename = schema.TextLine(
+        title = _(u'Middle Name in School'),
+        required = False,
+        )
+
+    lastname = schema.TextLine(
+        title = _(u'Surname in School'),
+        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,
+    #    )
+
+    collected = schema.Bool(
+        title = _(u'Have you collected transcript before?'),
+        default = False,
+        required = False,
+        )
+
+    entry_session = schema.Choice(
+        title = _(u'Academic Session of Entry'),
+        source = academic_sessions_vocab,
+        required = False,
+        readonly = False,
+        )
+
+    end_session = schema.Choice(
+        title = _(u'Academic Session of Graduation'),
+        source = academic_sessions_vocab,
+        required = False,
+        readonly = False,
+        )
+
+    entry_mode = schema.Choice(
+        title = _(u'Mode of Entry'),
+        source = StudyModeSource(),
+        required = False,
+        readonly = False,
+        )
+
+    course_studied = schema.Choice(
+        title = _(u'Course of Study'),
+        source = TranscriptCertificateSource(),
+        description = u'Faculty / Department / Course',
+        required = True,
+        readonly = False,
+        )
+
+    course_changed = schema.Choice(
+        title = _(u'Change of Study Course / Transfer'),
+        description = u'If yes, select previous course of study.',
+        source = TranscriptCertificateSource(),
+        readonly = False,
+        required = False,
+        )
+
+    spillover_level = schema.Choice(
+        title = _(u'Spill-over'),
+        description = u'Any spill-over? If yes, select session of spill-over.',
+        source = academic_sessions_vocab,
+        required = False,
+        readonly = False,
+        )
+
+    purpose = schema.TextLine(
+        title = _(u'Transcript Purpose'),
+        required = False,
+        )
+
+    order = schema.Choice(
+        source = OrderSource(),
+        title = _(u'Type of Order'),
+        required = True,
+        )
+
+    dispatch_address = schema.Text(
+        title = _(u'Recipient Body'),
+        description = u'Addresses (including email address and phone number) '
+                       'to which transcripts should be posted. '
+                       'All addresses must involve same courier charges.',
+        required = True,
+        readonly = False,
+        )
+
+    charge = schema.Choice(
+        title = _(u'Transcript Charge'),
+        source = DestinationCostSource(),
+        required = True,
+        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,
+        )
+
+    courier_tno = schema.TextLine(
+        title = _(u'Courier Tracking Number'),
+        required = False,
+        )
+
+    proc_date = FormattedDate(
+        title = _(u'Processing Date'),
+        required = False,
+        #date_format = u'%d/%m/%Y', # Use grok-instance-wide default
+        show_year = True,
+        )
+
+    @invariant
+    def type_of_order(applicant):
+        if not applicant.collected and applicant.order != 'o':
+            raise Invalid(_("If you haven't collected transcript before, type of order must be 'original'."))
+        if applicant.order == 'o' and applicant.charge.startswith('cert_'):
+            raise Invalid(_("You've selected the wrong transcript charge."))
+        if applicant.order == 'c' and not applicant.charge.startswith('cert_'):
+            raise Invalid(_("You've selected the wrong transcript charge."))
+
+
+class ICustomApplicant(ICustomUGApplicant, ICustomPGApplicant, 
+                       ITranscriptApplicant):
     """An interface for both types of applicants.
 
Index: main/kofacustom.edocons/trunk/src/kofacustom/edocons/applicants/utils.py
===================================================================
--- main/kofacustom.edocons/trunk/src/kofacustom/edocons/applicants/utils.py	(revision 17028)
+++ main/kofacustom.edocons/trunk/src/kofacustom/edocons/applicants/utils.py	(revision 17029)
@@ -47,2 +47,7 @@
         'form.cbt_score': _(u'CBT Data'),
         }
+
+    APP_TYPES_DICT = {
+        'app': ['General Studies', 'APP'],
+        'tsc': ['Transcript', 'TRF'],
+        }
Index: main/kofacustom.edocons/trunk/src/kofacustom/edocons/interswitch/tests.py
===================================================================
--- main/kofacustom.edocons/trunk/src/kofacustom/edocons/interswitch/tests.py	(revision 17028)
+++ main/kofacustom.edocons/trunk/src/kofacustom/edocons/interswitch/tests.py	(revision 17029)
@@ -59,5 +59,5 @@
         self.student.nationality = u'NG'
         self.browser.open(self.payments_path + '/addop')
-        self.browser.getControl(name="form.p_category").value = ['schoolfee']
+        self.browser.getControl(name="form.p_category").value = ['schoolfee_1']
         self.browser.getControl("Create ticket").click()
         self.assertMatches('...ticket created...',
@@ -70,5 +70,5 @@
                            self.browser.contents)
         self.assertTrue(
-            '<span>40000.0</span>' in self.browser.contents)
+            '<span>220000.0</span>' in self.browser.contents)
         self.payment_url = self.browser.url
         self.browser.getLink("Pay via Interswitch CollegePAY", index=0).click()
@@ -77,32 +77,8 @@
         self.assertEqual(self.student.current_mode, 'ug_ft')
         self.assertTrue(
-            '<input type="hidden" name="amount" value="4030000" />' in
-            self.browser.contents)
-        self.assertTrue(
-            'item_name="School Fee (total amount)" item_amt="4000000" bank_id="31" acct_num="1489560452"' in
-            self.browser.contents)
-        self.browser.open(self.payments_path + '/addop')
-        self.browser.getControl(name="form.p_category").value = ['schoolfee_1']
-        self.browser.getControl("Create ticket").click()
-        self.assertMatches('...ticket created...',
-                           self.browser.contents)
-        self.browser.open(self.payments_path)
-        ctrl = self.browser.getControl(name='val_id')
-        self.value = ctrl.options[1]
-        self.browser.getLink(self.value).click()
-        self.assertMatches('...Amount Authorized...',
-                           self.browser.contents)
-        self.assertTrue(
-            '<span>26400.0</span>' in self.browser.contents)
-        self.payment_url = self.browser.url
-        self.browser.getLink("Pay via Interswitch CollegePAY", index=0).click()
-        self.assertMatches('...<input type="hidden" name="pay_item_id" value="Default_Payable_MX26002" />...',
-                           self.browser.contents)
-        self.assertEqual(self.student.current_mode, 'ug_ft')
-        self.assertTrue(
-            '<input type="hidden" name="amount" value="2670000" />' in
-            self.browser.contents)
-        self.assertTrue(
-            'item_name="School Fee (66% - 1st instalment)" item_amt="2640000" bank_id="31" acct_num="1489560452"' in
+            '<input type="hidden" name="amount" value="22030000" />' in
+            self.browser.contents)
+        self.assertTrue(
+            'item_name="School Fee - 1st instalment" item_amt="22000000" bank_id="31" acct_num="1489560452"' in
             self.browser.contents)
 
@@ -243,4 +219,5 @@
         self.applicantscontainer.application_fee = 1000.0
         self.browser.getControl(name="form.nationality").value = ['NG']
+        self.browser.getControl(name="form.lga").value = ['zamfara_maru']
         self.browser.getControl(name="transition").value = ['start']
         self.browser.getControl("Save").click()
Index: main/kofacustom.edocons/trunk/src/kofacustom/edocons/students/utils.py
===================================================================
--- main/kofacustom.edocons/trunk/src/kofacustom/edocons/students/utils.py	(revision 17028)
+++ main/kofacustom.edocons/trunk/src/kofacustom/edocons/students/utils.py	(revision 17029)
@@ -60,46 +60,34 @@
             return _(u'Session configuration object is not available.'), None
         # Determine fee.
-        if category in ('schoolfee', 'schoolfee_1', 'secondinstal'):
-            try:
-                certificate = student['studycourse'].certificate
-                p_item = certificate.code
-            except (AttributeError, TypeError):
-                return _('Study course data are incomplete.'), None
-            if previous_session:
-                # Students can pay for previous sessions in all
-                # workflow states.  Fresh students are excluded by the
-                # update method of the PreviousPaymentAddFormPage.
-                amount = getattr(certificate, 'school_fee_1', 0.0)
+        if category in ('schoolfee_1', 'secondinstal'):
+            if student.state == CLEARED:
+                pass
+            elif student.state == RETURNING:
+                # In case of returning school fee payment the
+                # payment session and level contain the values of
+                # the session the student has paid for. Payment
+                # session is always next session.
+                p_session, p_level = self.getReturningData(student)
+                academic_session = self._getSessionConfiguration(p_session)
+                if academic_session == None:
+                    return _(
+                        u'Session configuration object is not available.'
+                        ), None
+            elif student.is_postgrad and student.state == PAID:
+                # Returning postgraduate students also pay for the
+                # next session but their level always remains the
+                # same.
+                p_session += 1
+                academic_session = self._getSessionConfiguration(p_session)
+                if academic_session == None:
+                    return _(
+                        u'Session configuration object is not available.'
+                        ), None
             else:
-                if category == 'secondinstal':
-                    amount = 0.34 * getattr(certificate, 'school_fee_1', 0.0)
-                else:
-                    if student.state == CLEARED:
-                        amount = getattr(certificate, 'school_fee_1', 0.0)
-                    elif student.state == RETURNING:
-                        # In case of returning school fee payment the
-                        # payment session and level contain the values of
-                        # the session the student has paid for. Payment
-                        # session is always next session.
-                        p_session, p_level = self.getReturningData(student)
-                        academic_session = self._getSessionConfiguration(p_session)
-                        if academic_session == None:
-                            return _(
-                                u'Session configuration object is not available.'
-                                ), None
-                        amount = getattr(certificate, 'school_fee_1', 0.0)
-                    elif student.is_postgrad and student.state == PAID:
-                        # Returning postgraduate students also pay for the
-                        # next session but their level always remains the
-                        # same.
-                        p_session += 1
-                        academic_session = self._getSessionConfiguration(p_session)
-                        if academic_session == None:
-                            return _(
-                                u'Session configuration object is not available.'
-                                ), None
-                        amount = getattr(certificate, 'school_fee_1', 0.0)
-                    if category == 'schoolfee_1':
-                        amount *= 0.66
+                return _('School fee payment not allowed. Wrong state.'), None
+            if category == 'schoolfee_1':
+                amount = 220000.0
+            elif category == 'secondinstal':
+                amount = 100000.0
         elif category == 'clearance':
             try:
Index: main/kofacustom.edocons/trunk/src/kofacustom/edocons/utils/utils.py
===================================================================
--- main/kofacustom.edocons/trunk/src/kofacustom/edocons/utils/utils.py	(revision 17028)
+++ main/kofacustom.edocons/trunk/src/kofacustom/edocons/utils/utils.py	(revision 17029)
@@ -26,6 +26,6 @@
     PAYMENT_CATEGORIES = {
         'schoolfee': 'School Fee (total amount)',
-        'schoolfee_1': 'School Fee (66% - 1st instalment)',
-        'secondinstal': 'School Fee (34% - 2nd instalment)',
+        'schoolfee_1': 'School Fee - 1st instalment',
+        'secondinstal': 'School Fee - 2nd instalment',
         'clearance': 'Acceptance Fee',
         'hostel':'Hostel Fee',
@@ -42,7 +42,7 @@
 
     SELECTABLE_PAYMENT_CATEGORIES = {
-        'schoolfee': 'School Fee (total amount)',
-        'schoolfee_1': 'School Fee (66% - 1st instalment)',
-        'secondinstal': 'School Fee (34% - 2nd instalment)',
+        #'schoolfee': 'School Fee (total amount)',
+        'schoolfee_1': 'School Fee - 1st instalment',
+        'secondinstal': 'School Fee - 2nd instalment',
         'hostel':'Hostel Fee',
         }
