Changeset 17029


Ignore:
Timestamp:
23 Jul 2022, 08:08:45 (2 years ago)
Author:
Henrik Bettermann
Message:

Implement transcript application (fee calculation not yet configured).

Location:
main/kofacustom.edocons/trunk/src/kofacustom/edocons
Files:
7 edited

Legend:

Unmodified
Added
Removed
  • main/kofacustom.edocons/trunk/src/kofacustom/edocons/applicants/applicant.py

    r16639 r17029  
    2323from kofacustom.nigeria.applicants.applicant import NigeriaApplicant
    2424from kofacustom.edocons.applicants.interfaces import(
    25     ICustomApplicant, ICustomUGApplicantEdit, ICustomPGApplicantEdit, IPUTMEApplicantEdit)
     25    ICustomApplicant, ICustomUGApplicantEdit, ICustomPGApplicantEdit,
     26    IPUTMEApplicantEdit, ITranscriptApplicant)
    2627
    2728class CustomApplicant(NigeriaApplicant):
    2829
    2930    grok.implements(ICustomApplicant, ICustomUGApplicantEdit,
    30         ICustomPGApplicantEdit, IPUTMEApplicantEdit)
     31        ICustomPGApplicantEdit, IPUTMEApplicantEdit,
     32        ITranscriptApplicant)
    3133    grok.provides(ICustomApplicant)
    3234
  • main/kofacustom.edocons/trunk/src/kofacustom/edocons/applicants/browser.py

    r16853 r17029  
    5454    ICustomPGApplicant, ICustomUGApplicant, ICustomApplicant,
    5555    ICustomPGApplicantEdit, ICustomUGApplicantEdit,
    56     ICustomApplicantOnlinePayment,
     56    ICustomApplicantOnlinePayment, ITranscriptApplicant,
    5757    )
    5858from kofacustom.nigeria.interfaces import MessageFactory as _
     
    9494    'aggregate')
    9595
     96TSC_OMIT_FIELDS = ('locked', 'suspended',
     97    )
     98   
     99
     100TSC_OMIT_EDIT_FIELDS = TSC_OMIT_FIELDS + (
     101    'applicant_id',
     102    'proc_date',
     103    )
     104
     105TSC_OMIT_MANAGE_FIELDS = TSC_OMIT_FIELDS + (
     106    'applicant_id',)     
     107
    96108class CustomApplicantDisplayFormPage(NigeriaApplicantDisplayFormPage):
    97109    """A display view for applicant data.
     
    104116            for field in PG_OMIT_DISPLAY_FIELDS:
    105117                form_fields = form_fields.omit(field)
     118        elif self.target is not None and self.target.startswith('tsc'):
     119            form_fields = grok.AutoFields(ITranscriptApplicant)
     120            for field in TSC_OMIT_FIELDS:
     121                form_fields = form_fields.omit(field)           
     122            return form_fields
    106123        else:
    107124            form_fields = grok.AutoFields(ICustomUGApplicant)
    108125            for field in UG_OMIT_DISPLAY_FIELDS:
    109126                form_fields = form_fields.omit(field)
     127            form_fields['notice'].custom_widget = BytesDisplayWidget
    110128        #form_fields['perm_address'].custom_widget = BytesDisplayWidget
    111         form_fields['notice'].custom_widget = BytesDisplayWidget
    112129        if not getattr(self.context, 'student_id'):
    113130            form_fields = form_fields.omit('student_id')
     
    134151            for field in PG_OMIT_PDF_FIELDS:
    135152                form_fields = form_fields.omit(field)
     153        elif self.target is not None and self.target.startswith('tsc'):
     154            form_fields = grok.AutoFields(ITranscriptApplicant)
     155            for field in TSC_OMIT_FIELDS:
     156                form_fields = form_fields.omit(field)             
     157            return form_fields
    136158        else:
    137159            form_fields = grok.AutoFields(ICustomUGApplicant)
     
    164186            for field in PG_OMIT_MANAGE_FIELDS:
    165187                form_fields = form_fields.omit(field)
     188        elif self.target is not None and self.target.startswith('tsc'):
     189            form_fields = grok.AutoFields(ITranscriptApplicant)
     190            for field in TSC_OMIT_MANAGE_FIELDS:
     191                form_fields = form_fields.omit(field)             
     192            return form_fields
    166193        else:
    167194            form_fields = grok.AutoFields(ICustomUGApplicant)
     
    185212            for field in PG_OMIT_EDIT_FIELDS:
    186213                form_fields = form_fields.omit(field)
     214        elif self.target is not None and self.target.startswith('tsc'):
     215            form_fields = grok.AutoFields(ITranscriptApplicant)
     216            for field in TSC_OMIT_EDIT_FIELDS:
     217                form_fields = form_fields.omit(field)           
     218            return form_fields
    187219        else:
    188220            form_fields = grok.AutoFields(ICustomUGApplicantEdit)
  • main/kofacustom.edocons/trunk/src/kofacustom/edocons/applicants/interfaces.py

    r16883 r17029  
     1# -*- coding: utf-8 -*-
    12## $Id$
    23##
     
    2021
    2122from zope import schema
     23from zope.interface import invariant, Invalid
     24from zope.component import getUtility
     25from zope.catalog.interfaces import ICatalog
     26from zc.sourcefactory.basic import BasicSourceFactory
     27from waeup.kofa.university.vocabularies import StudyModeSource
    2228from waeup.kofa.applicants.interfaces import (
    2329    IApplicantBaseData,
     
    2531from waeup.kofa.schoolgrades import ResultEntryField
    2632from waeup.kofa.interfaces import (
    27     SimpleKofaVocabulary, academic_sessions_vocab, validate_email)
     33    SimpleKofaVocabulary, academic_sessions_vocab, validate_email,
     34    IKofaObject)
    2835from waeup.kofa.schema import FormattedDate, TextLineChoice, PhoneNumber
    2936from waeup.kofa.students.vocabularies import nats_vocab, GenderSource
     
    5461    )
    5562
     63DESTINATION_COST = {
     64    #'none': ('To the moon', 1000000000.0, 1),
     65    'nigeria': ('Within Nigeria', 20000.0, 1),
     66    'africa': ('Within Africa ', 30000.0, 2),
     67    'inter': ('International', 35000.0, 3),
     68    'cert_nigeria': ('Certified Copy - Within Nigeria', 13000.0, 4),
     69    'cert_africa': ('Certified Copy - Within Africa', 23000.0, 5),
     70    'cert_inter': ('Certified Copy - International', 28000.0, 6),
     71    }
     72
     73class DestinationCostSource(BasicSourceFactory):
     74    """A source that delivers continents and shipment costs.
     75    """
     76    def getValues(self):
     77        sorted_items = sorted(DESTINATION_COST.items(),
     78                              key=lambda element: element[1][2])
     79        return [item[0] for item in sorted_items]
     80
     81    def getToken(self, value):
     82        return value
     83
     84    def getTitle(self, value):
     85        return u"%s (₦ %s)" % (
     86            DESTINATION_COST[value][0],
     87            DESTINATION_COST[value][1])
     88
     89class OrderSource(BasicSourceFactory):
     90    """
     91    """
     92    def getValues(self):
     93        return ['o', 'c']
     94
     95    def getToken(self, value):
     96        return value
     97
     98    def getTitle(self, value):
     99        if value == 'o':
     100            return _('Original')
     101        if value == 'c':
     102            return _('Certified True Copy')
     103
     104class TranscriptCertificateSource(CertificateSource):
     105    """Include Department and Faculty in Title.
     106    """
     107    def getValues(self, context):
     108        catalog = getUtility(ICatalog, name='certificates_catalog')
     109        resultset = catalog.searchResults(code=(None, None))
     110        resultlist = sorted(resultset, key=lambda
     111            value: value.__parent__.__parent__.__parent__.code +
     112            value.__parent__.__parent__.code +
     113            value.code)
     114        return resultlist
     115
     116    def getTitle(self, context, value):
     117        """
     118        """
     119        try: title = "%s / %s / %s (%s)" % (
     120            value.__parent__.__parent__.__parent__.title,
     121            value.__parent__.__parent__.title,
     122            value.title, value.code)
     123        except AttributeError:
     124            title = "NA / %s (%s)" % (value.title, value.code)
     125        return title
     126
    56127class ICustomUGApplicant(INigeriaUGApplicant):
    57128    """An undergraduate applicant.
     
    308379    """
    309380
    310 
    311 class ICustomApplicant(ICustomUGApplicant, ICustomPGApplicant):
     381class ITranscriptApplicant(IKofaObject):
     382    """A transcript applicant.
     383    """
     384
     385    suspended = schema.Bool(
     386        title = _(u'Account suspended'),
     387        default = False,
     388        required = False,
     389        )
     390
     391    locked = schema.Bool(
     392        title = _(u'Form locked'),
     393        default = False,
     394        required = False,
     395        )
     396
     397    applicant_id = schema.TextLine(
     398        title = _(u'Transcript Application Id'),
     399        required = False,
     400        readonly = False,
     401        )
     402
     403    student_id = schema.TextLine(
     404        title = _(u'Kofa Student Id'),
     405        required = False,
     406        readonly = False,
     407        )
     408
     409    matric_number = schema.TextLine(
     410        title = _(u'Matriculation Number'),
     411        readonly = False,
     412        required = True,
     413        )
     414
     415    firstname = schema.TextLine(
     416        title = _(u'First Name in School'),
     417        required = True,
     418        )
     419
     420    middlename = schema.TextLine(
     421        title = _(u'Middle Name in School'),
     422        required = False,
     423        )
     424
     425    lastname = schema.TextLine(
     426        title = _(u'Surname in School'),
     427        required = True,
     428        )
     429
     430    date_of_birth = FormattedDate(
     431        title = _(u'Date of Birth'),
     432        required = False,
     433        #date_format = u'%d/%m/%Y', # Use grok-instance-wide default
     434        show_year = True,
     435        )
     436
     437    sex = schema.Choice(
     438        title = _(u'Gender'),
     439        source = GenderSource(),
     440        required = True,
     441        )
     442
     443    #nationality = schema.Choice(
     444    #    vocabulary = nats_vocab,
     445    #    title = _(u'Nationality'),
     446    #    required = False,
     447    #    )
     448
     449    email = schema.ASCIILine(
     450        title = _(u'Email Address'),
     451        required = True,
     452        constraint=validate_email,
     453        )
     454
     455    phone = PhoneNumber(
     456        title = _(u'Phone'),
     457        description = u'',
     458        required = False,
     459        )
     460
     461    #perm_address = schema.Text(
     462    #    title = _(u'Current Local Address'),
     463    #    required = False,
     464    #    readonly = False,
     465    #    )
     466
     467    collected = schema.Bool(
     468        title = _(u'Have you collected transcript before?'),
     469        default = False,
     470        required = False,
     471        )
     472
     473    entry_session = schema.Choice(
     474        title = _(u'Academic Session of Entry'),
     475        source = academic_sessions_vocab,
     476        required = False,
     477        readonly = False,
     478        )
     479
     480    end_session = schema.Choice(
     481        title = _(u'Academic Session of Graduation'),
     482        source = academic_sessions_vocab,
     483        required = False,
     484        readonly = False,
     485        )
     486
     487    entry_mode = schema.Choice(
     488        title = _(u'Mode of Entry'),
     489        source = StudyModeSource(),
     490        required = False,
     491        readonly = False,
     492        )
     493
     494    course_studied = schema.Choice(
     495        title = _(u'Course of Study'),
     496        source = TranscriptCertificateSource(),
     497        description = u'Faculty / Department / Course',
     498        required = True,
     499        readonly = False,
     500        )
     501
     502    course_changed = schema.Choice(
     503        title = _(u'Change of Study Course / Transfer'),
     504        description = u'If yes, select previous course of study.',
     505        source = TranscriptCertificateSource(),
     506        readonly = False,
     507        required = False,
     508        )
     509
     510    spillover_level = schema.Choice(
     511        title = _(u'Spill-over'),
     512        description = u'Any spill-over? If yes, select session of spill-over.',
     513        source = academic_sessions_vocab,
     514        required = False,
     515        readonly = False,
     516        )
     517
     518    purpose = schema.TextLine(
     519        title = _(u'Transcript Purpose'),
     520        required = False,
     521        )
     522
     523    order = schema.Choice(
     524        source = OrderSource(),
     525        title = _(u'Type of Order'),
     526        required = True,
     527        )
     528
     529    dispatch_address = schema.Text(
     530        title = _(u'Recipient Body'),
     531        description = u'Addresses (including email address and phone number) '
     532                       'to which transcripts should be posted. '
     533                       'All addresses must involve same courier charges.',
     534        required = True,
     535        readonly = False,
     536        )
     537
     538    charge = schema.Choice(
     539        title = _(u'Transcript Charge'),
     540        source = DestinationCostSource(),
     541        required = True,
     542        readonly = False,
     543        )
     544
     545    no_copies = schema.Choice(
     546        title = _(u'Number of Copies'),
     547        description = u'Must correspond with the number of dispatch addresses above.',
     548        values=[1, 2, 3, 4],
     549        required = False,
     550        readonly = False,
     551        default = 1,
     552        )
     553
     554    courier_tno = schema.TextLine(
     555        title = _(u'Courier Tracking Number'),
     556        required = False,
     557        )
     558
     559    proc_date = FormattedDate(
     560        title = _(u'Processing Date'),
     561        required = False,
     562        #date_format = u'%d/%m/%Y', # Use grok-instance-wide default
     563        show_year = True,
     564        )
     565
     566    @invariant
     567    def type_of_order(applicant):
     568        if not applicant.collected and applicant.order != 'o':
     569            raise Invalid(_("If you haven't collected transcript before, type of order must be 'original'."))
     570        if applicant.order == 'o' and applicant.charge.startswith('cert_'):
     571            raise Invalid(_("You've selected the wrong transcript charge."))
     572        if applicant.order == 'c' and not applicant.charge.startswith('cert_'):
     573            raise Invalid(_("You've selected the wrong transcript charge."))
     574
     575
     576class ICustomApplicant(ICustomUGApplicant, ICustomPGApplicant,
     577                       ITranscriptApplicant):
    312578    """An interface for both types of applicants.
    313579
  • main/kofacustom.edocons/trunk/src/kofacustom/edocons/applicants/utils.py

    r16767 r17029  
    4747        'form.cbt_score': _(u'CBT Data'),
    4848        }
     49
     50    APP_TYPES_DICT = {
     51        'app': ['General Studies', 'APP'],
     52        'tsc': ['Transcript', 'TRF'],
     53        }
  • main/kofacustom.edocons/trunk/src/kofacustom/edocons/interswitch/tests.py

    r16845 r17029  
    5959        self.student.nationality = u'NG'
    6060        self.browser.open(self.payments_path + '/addop')
    61         self.browser.getControl(name="form.p_category").value = ['schoolfee']
     61        self.browser.getControl(name="form.p_category").value = ['schoolfee_1']
    6262        self.browser.getControl("Create ticket").click()
    6363        self.assertMatches('...ticket created...',
     
    7070                           self.browser.contents)
    7171        self.assertTrue(
    72             '<span>40000.0</span>' in self.browser.contents)
     72            '<span>220000.0</span>' in self.browser.contents)
    7373        self.payment_url = self.browser.url
    7474        self.browser.getLink("Pay via Interswitch CollegePAY", index=0).click()
     
    7777        self.assertEqual(self.student.current_mode, 'ug_ft')
    7878        self.assertTrue(
    79             '<input type="hidden" name="amount" value="4030000" />' in
    80             self.browser.contents)
    81         self.assertTrue(
    82             'item_name="School Fee (total amount)" item_amt="4000000" bank_id="31" acct_num="1489560452"' in
    83             self.browser.contents)
    84         self.browser.open(self.payments_path + '/addop')
    85         self.browser.getControl(name="form.p_category").value = ['schoolfee_1']
    86         self.browser.getControl("Create ticket").click()
    87         self.assertMatches('...ticket created...',
    88                            self.browser.contents)
    89         self.browser.open(self.payments_path)
    90         ctrl = self.browser.getControl(name='val_id')
    91         self.value = ctrl.options[1]
    92         self.browser.getLink(self.value).click()
    93         self.assertMatches('...Amount Authorized...',
    94                            self.browser.contents)
    95         self.assertTrue(
    96             '<span>26400.0</span>' in self.browser.contents)
    97         self.payment_url = self.browser.url
    98         self.browser.getLink("Pay via Interswitch CollegePAY", index=0).click()
    99         self.assertMatches('...<input type="hidden" name="pay_item_id" value="Default_Payable_MX26002" />...',
    100                            self.browser.contents)
    101         self.assertEqual(self.student.current_mode, 'ug_ft')
    102         self.assertTrue(
    103             '<input type="hidden" name="amount" value="2670000" />' in
    104             self.browser.contents)
    105         self.assertTrue(
    106             'item_name="School Fee (66% - 1st instalment)" item_amt="2640000" bank_id="31" acct_num="1489560452"' in
     79            '<input type="hidden" name="amount" value="22030000" />' in
     80            self.browser.contents)
     81        self.assertTrue(
     82            'item_name="School Fee - 1st instalment" item_amt="22000000" bank_id="31" acct_num="1489560452"' in
    10783            self.browser.contents)
    10884
     
    243219        self.applicantscontainer.application_fee = 1000.0
    244220        self.browser.getControl(name="form.nationality").value = ['NG']
     221        self.browser.getControl(name="form.lga").value = ['zamfara_maru']
    245222        self.browser.getControl(name="transition").value = ['start']
    246223        self.browser.getControl("Save").click()
  • main/kofacustom.edocons/trunk/src/kofacustom/edocons/students/utils.py

    r16709 r17029  
    6060            return _(u'Session configuration object is not available.'), None
    6161        # Determine fee.
    62         if category in ('schoolfee', 'schoolfee_1', 'secondinstal'):
    63             try:
    64                 certificate = student['studycourse'].certificate
    65                 p_item = certificate.code
    66             except (AttributeError, TypeError):
    67                 return _('Study course data are incomplete.'), None
    68             if previous_session:
    69                 # Students can pay for previous sessions in all
    70                 # workflow states.  Fresh students are excluded by the
    71                 # update method of the PreviousPaymentAddFormPage.
    72                 amount = getattr(certificate, 'school_fee_1', 0.0)
     62        if category in ('schoolfee_1', 'secondinstal'):
     63            if student.state == CLEARED:
     64                pass
     65            elif student.state == RETURNING:
     66                # In case of returning school fee payment the
     67                # payment session and level contain the values of
     68                # the session the student has paid for. Payment
     69                # session is always next session.
     70                p_session, p_level = self.getReturningData(student)
     71                academic_session = self._getSessionConfiguration(p_session)
     72                if academic_session == None:
     73                    return _(
     74                        u'Session configuration object is not available.'
     75                        ), None
     76            elif student.is_postgrad and student.state == PAID:
     77                # Returning postgraduate students also pay for the
     78                # next session but their level always remains the
     79                # same.
     80                p_session += 1
     81                academic_session = self._getSessionConfiguration(p_session)
     82                if academic_session == None:
     83                    return _(
     84                        u'Session configuration object is not available.'
     85                        ), None
    7386            else:
    74                 if category == 'secondinstal':
    75                     amount = 0.34 * getattr(certificate, 'school_fee_1', 0.0)
    76                 else:
    77                     if student.state == CLEARED:
    78                         amount = getattr(certificate, 'school_fee_1', 0.0)
    79                     elif student.state == RETURNING:
    80                         # In case of returning school fee payment the
    81                         # payment session and level contain the values of
    82                         # the session the student has paid for. Payment
    83                         # session is always next session.
    84                         p_session, p_level = self.getReturningData(student)
    85                         academic_session = self._getSessionConfiguration(p_session)
    86                         if academic_session == None:
    87                             return _(
    88                                 u'Session configuration object is not available.'
    89                                 ), None
    90                         amount = getattr(certificate, 'school_fee_1', 0.0)
    91                     elif student.is_postgrad and student.state == PAID:
    92                         # Returning postgraduate students also pay for the
    93                         # next session but their level always remains the
    94                         # same.
    95                         p_session += 1
    96                         academic_session = self._getSessionConfiguration(p_session)
    97                         if academic_session == None:
    98                             return _(
    99                                 u'Session configuration object is not available.'
    100                                 ), None
    101                         amount = getattr(certificate, 'school_fee_1', 0.0)
    102                     if category == 'schoolfee_1':
    103                         amount *= 0.66
     87                return _('School fee payment not allowed. Wrong state.'), None
     88            if category == 'schoolfee_1':
     89                amount = 220000.0
     90            elif category == 'secondinstal':
     91                amount = 100000.0
    10492        elif category == 'clearance':
    10593            try:
  • main/kofacustom.edocons/trunk/src/kofacustom/edocons/utils/utils.py

    r16792 r17029  
    2626    PAYMENT_CATEGORIES = {
    2727        'schoolfee': 'School Fee (total amount)',
    28         'schoolfee_1': 'School Fee (66% - 1st instalment)',
    29         'secondinstal': 'School Fee (34% - 2nd instalment)',
     28        'schoolfee_1': 'School Fee - 1st instalment',
     29        'secondinstal': 'School Fee - 2nd instalment',
    3030        'clearance': 'Acceptance Fee',
    3131        'hostel':'Hostel Fee',
     
    4242
    4343    SELECTABLE_PAYMENT_CATEGORIES = {
    44         'schoolfee': 'School Fee (total amount)',
    45         'schoolfee_1': 'School Fee (66% - 1st instalment)',
    46         'secondinstal': 'School Fee (34% - 2nd instalment)',
     44        #'schoolfee': 'School Fee (total amount)',
     45        'schoolfee_1': 'School Fee - 1st instalment',
     46        'secondinstal': 'School Fee - 2nd instalment',
    4747        'hostel':'Hostel Fee',
    4848        }
Note: See TracChangeset for help on using the changeset viewer.