Index: main/waeup.kwarapoly/trunk/src/waeup/kwarapoly/students/browser.py
===================================================================
--- main/waeup.kwarapoly/trunk/src/waeup/kwarapoly/students/browser.py	(revision 12905)
+++ main/waeup.kwarapoly/trunk/src/waeup/kwarapoly/students/browser.py	(revision 12907)
@@ -50,4 +50,5 @@
 
 from waeup.kwarapoly.students.interfaces import (
+    ICustomStudent,
     ICustomStudentOnlinePayment,
     ICustomStudentPersonal,
@@ -509,2 +510,24 @@
 #
 # Burn this remark after reading.
+
+
+class StudentGetMatricNumberPage(UtilityView, grok.View):
+    """ Construct and set the matriculation number.
+    """
+    grok.context(ICustomStudent)
+    grok.name('get_matric_number')
+    grok.require('waeup.viewStudent')
+
+    def update(self):
+        students_utils = getUtility(IStudentsUtils)
+        msg, mnumber = students_utils.setMatricNumber(self.context)
+        if msg:
+            self.flash(msg, type="danger")
+        else:
+            self.flash(_('Matriculation number %s assigned.' % mnumber))
+            self.context.writeLogMessage(self, '%s assigned' % mnumber)
+        self.redirect(self.url(self.context))
+        return
+
+    def render(self):
+        return
Index: main/waeup.kwarapoly/trunk/src/waeup/kwarapoly/students/tests/test_browser.py
===================================================================
--- main/waeup.kwarapoly/trunk/src/waeup/kwarapoly/students/tests/test_browser.py	(revision 12905)
+++ main/waeup.kwarapoly/trunk/src/waeup/kwarapoly/students/tests/test_browser.py	(revision 12907)
@@ -528,2 +528,32 @@
         self.assertEqual(self.student.state, 'courses registered')
         return
+
+    def test_set_matric_number(self):
+        # Login as student
+        self.browser.open(self.login_path)
+        IWorkflowState(self.student).setState('school fee paid')
+        self.browser.open(self.login_path)
+        self.browser.getControl(name="form.login").value = self.student_id
+        self.browser.getControl(name="form.password").value = 'spwd'
+        self.browser.getControl("Login").click()
+        self.assertRaises(
+            LinkNotFoundError,
+            self.browser.getLink, 'Get Matriculation Number')
+        self.student.matric_number = None
+        self.student['studycourse'].entry_mode = 'nd_ft'
+        self.student['studycourse'].entry_session = 2014
+        self.browser.open(self.student_path)
+        self.browser.getLink("Get Matriculation Number").click()
+        self.assertTrue('Matriculation number ND/14/dep1/FT/001 assigned.'
+            in self.browser.contents)
+        self.assertEqual(self.student.matric_number, 'ND/14/dep1/FT/001')
+        self.assertRaises(
+            LinkNotFoundError,
+            self.browser.getLink, 'Get Matriculation Number')
+        # Setting matric number is logged.
+        logfile = os.path.join(
+            self.app['datacenter'].storage, 'logs', 'students.log')
+        logcontent = open(logfile).read()
+        self.assertTrue('W1000000 - waeup.kwarapoly.students.browser.StudentGetMatricNumberPage - '
+                        'W1000000 - ND/14/dep1/FT/001 assigned' in logcontent)
+        return
Index: main/waeup.kwarapoly/trunk/src/waeup/kwarapoly/students/tests/test_utils.py
===================================================================
--- main/waeup.kwarapoly/trunk/src/waeup/kwarapoly/students/tests/test_utils.py	(revision 12905)
+++ main/waeup.kwarapoly/trunk/src/waeup/kwarapoly/students/tests/test_utils.py	(revision 12907)
@@ -51,4 +51,7 @@
         self.student.matric_number = None
         msg, mnumber = utils.setMatricNumber(self.student)
+        self.assertEqual(msg, 'Wrong state.')
+        IWorkflowState(self.student).setState('school fee paid')
+        msg, mnumber = utils.setMatricNumber(self.student)
         self.assertEqual(msg, 'Matriculation number cannot be set.')
         self.assertEqual(mnumber, None)
Index: main/waeup.kwarapoly/trunk/src/waeup/kwarapoly/students/utils.py
===================================================================
--- main/waeup.kwarapoly/trunk/src/waeup/kwarapoly/students/utils.py	(revision 12905)
+++ main/waeup.kwarapoly/trunk/src/waeup/kwarapoly/students/utils.py	(revision 12907)
@@ -24,5 +24,6 @@
 from kofacustom.nigeria.students.utils import NigeriaStudentsUtils
 from waeup.kofa.accesscodes import create_accesscode
-from waeup.kofa.interfaces import CLEARED, RETURNING, ADMITTED, IKofaUtils
+from waeup.kofa.interfaces import (
+    CLEARED, RETURNING, ADMITTED, PAID, IKofaUtils)
 from waeup.kofa.fees import FeeTable
 from waeup.kofa.hostels.hostel import NOT_OCCUPIED
@@ -356,4 +357,6 @@
         entry_mode = getattr(
             student.get('studycourse', None), 'entry_mode', None)
+        if student.state not in (PAID, ):
+            return _('Wrong state.'), None
         if None in (cert, entry_session, entry_mode):
             return _('Matriculation number cannot be set.'), None
Index: main/waeup.kwarapoly/trunk/src/waeup/kwarapoly/students/viewlets.py
===================================================================
--- main/waeup.kwarapoly/trunk/src/waeup/kwarapoly/students/viewlets.py	(revision 12905)
+++ main/waeup.kwarapoly/trunk/src/waeup/kwarapoly/students/viewlets.py	(revision 12907)
@@ -18,8 +18,9 @@
 
 import grok
+from zope.component import getUtility
 from waeup.kofa.students.viewlets import (
     AddPreviousPaymentActionButton, AddBalancePaymentActionButton,
     ManageActionButton)
-from waeup.kofa.students.interfaces import IStudent
+from waeup.kofa.students.interfaces import IStudent, IStudentsUtils
 from waeup.kofa.students.browser import StudentBaseDisplayFormPage
 from waeup.kofa.students.workflow import (
@@ -86,2 +87,22 @@
             return ''
         return self.view.url(self.view.context, self.target)
+
+
+class GetMatricNumberActionButton(ManageActionButton):
+    grok.order(10)
+    grok.context(IStudent)
+    grok.view(StudentBaseDisplayFormPage)
+    grok.require('waeup.viewStudent')
+    icon = 'actionicon_count.png'
+    text = _('Get Matriculation Number')
+
+    @property
+    def target_url(self):
+        students_utils = getUtility(IStudentsUtils)
+        if self.context.matric_number:
+            return ''
+        error, matric_number = students_utils.constructMatricNumber(
+            self.context)
+        if error:
+            return ''
+        return self.view.url(self.view.context, 'get_matric_number')
Index: main/waeup.kwarapoly/trunk/src/waeup/kwarapoly/university/tests.py
===================================================================
--- main/waeup.kwarapoly/trunk/src/waeup/kwarapoly/university/tests.py	(revision 12905)
+++ main/waeup.kwarapoly/trunk/src/waeup/kwarapoly/university/tests.py	(revision 12907)
@@ -19,4 +19,5 @@
 import tempfile
 import os
+from hurry.workflow.interfaces import IWorkflowState
 from zope.component import getUtility
 from waeup.kofa.students.tests.test_browser import StudentsFullSetup
@@ -34,4 +35,5 @@
         utils = getUtility(IStudentsUtils)
         self.student.matric_number = None
+        IWorkflowState(self.student).setState('school fee paid')
         msg, mnumber = utils.setMatricNumber(self.student)
         self.assertEqual(msg, 'Matriculation number cannot be set.')
