Index: main/waeup.kofa/trunk/src/waeup/kofa/students/browser.py
===================================================================
--- main/waeup.kofa/trunk/src/waeup/kofa/students/browser.py	(revision 10249)
+++ main/waeup.kofa/trunk/src/waeup/kofa/students/browser.py	(revision 10250)
@@ -70,5 +70,5 @@
     IUGStudentClearance,IPGStudentClearance,
     IStudentPersonal, IStudentPersonalEdit, IStudentBase, IStudentStudyCourse,
-    IStudentStudyCourseTransfer,
+    IStudentStudyCourseTransfer, IStudentStudyCourseTranscript,
     IStudentAccommodation, IStudentStudyLevel,
     ICourseTicket, ICourseTicketAdd, IStudentPaymentsContainer,
@@ -433,5 +433,5 @@
     """
 
-    def __init__(self, context, request, omit_fields):
+    def __init__(self, context, request, omit_fields=()):
         self.omit_fields = omit_fields
         super(StudentBasePDFFormPage, self).__init__(context, request)
@@ -720,5 +720,6 @@
             self, 'clearance_slip.pdf',
             self.context.student, studentview, signatures=self._signatures(),
-            sigs_in_footer=self._sigsInFooter())
+            sigs_in_footer=self._sigsInFooter(),
+            omit_fields=self.omit_fields)
 
 class StudentClearanceManageFormPage(KofaEditFormPage):
@@ -1049,5 +1050,5 @@
     """ Page to display the student's transcript.
     """
-    grok.context(IStudentStudyCourse)
+    grok.context(IStudentStudyCourseTranscript)
     grok.name('transcript')
     grok.require('waeup.viewStudent')
@@ -1070,4 +1071,60 @@
         return _('${a}: Transcript Data', mapping = {
             'a':self.context.student.display_fullname})
+
+class ExportPDFTranscriptPage(UtilityView, grok.View):
+    """Deliver a PDF slip of the context.
+    """
+    grok.context(IStudentStudyCourse)
+    grok.name('transcript.pdf')
+    grok.require('waeup.viewStudent')
+    form_fields = grok.AutoFields(IStudentStudyCourseTranscript)
+    prefix = 'form'
+    omit_fields = (
+        'department', 'faculty', 'entry_session', 'certificate',
+        'password', 'suspended', 'phone', 'email',
+        'adm_code', 'sex', 'suspended_comment')
+
+    def update(self):
+        super(ExportPDFTranscriptPage, self).update()
+        self.semester_dict = getUtility(IKofaUtils).SEMESTER_DICT
+        self.session_dict = dict(
+            [(item[1], item[0]) for item in academic_sessions()])
+        self.course_levels = course_levels
+        self.studymode_dict = getUtility(IKofaUtils).STUDY_MODES_DICT
+        return
+
+    @property
+    def label(self):
+        # Here we know that the cookie has been set
+        portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
+        return translate(_('Academic Transcript'),
+            'waeup.kofa', target_language=portal_language)
+
+    def render(self):
+        portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
+        Sem = translate(_('Sem.'), 'waeup.kofa', target_language=portal_language)
+        Code = translate(_('Code'), 'waeup.kofa', target_language=portal_language)
+        Title = translate(_('Title'), 'waeup.kofa', target_language=portal_language)
+        Cred = translate(_('Credits'), 'waeup.kofa', target_language=portal_language)
+        Score = translate(_('Score'), 'waeup.kofa', target_language=portal_language)
+        Grade = translate(_('Grade'), 'waeup.kofa', target_language=portal_language)
+        studentview = StudentBasePDFFormPage(self.context.student,
+            self.request, self.omit_fields)
+        students_utils = getUtility(IStudentsUtils)
+
+        tableheader = [(Code,'code', 2.5),
+                         (Title,'title', 7),
+                         (Sem, 'semester', 1.5),
+                         (Cred, 'credits', 1.5),
+                         (Score, 'score', 1.5),
+                         (Grade, 'grade', 1.5),
+                         ]
+
+        return students_utils.renderPDFTranscript(
+            self, 'transcript.pdf',
+            self.context.student, studentview,
+            omit_fields=self.omit_fields,
+            tableheader=tableheader
+            )
 
 class StudentTransferFormPage(KofaAddFormPage):
@@ -1243,5 +1300,6 @@
             tabledata_2=tabledata_2,
             tableheader_3=tableheader,
-            tabledata_3=tabledata_3
+            tabledata_3=tabledata_3,
+            omit_fields=self.omit_fields
             )
 
@@ -1740,5 +1798,6 @@
         students_utils = getUtility(IStudentsUtils)
         return students_utils.renderPDF(self, 'payment_slip.pdf',
-            self.context.student, studentview, note=self.note)
+            self.context.student, studentview, note=self.note,
+            omit_fields=self.omit_fields)
 
 
@@ -1984,5 +2043,6 @@
         return students_utils.renderPDF(
             self, 'bed_allocation_slip.pdf',
-            self.context.student, studentview)
+            self.context.student, studentview,
+            omit_fields=self.omit_fields)
 
 class BedTicketRelocationPage(UtilityView, grok.View):
Index: main/waeup.kofa/trunk/src/waeup/kofa/students/browser_templates/studycoursepage.pt
===================================================================
--- main/waeup.kofa/trunk/src/waeup/kofa/students/browser_templates/studycoursepage.pt	(revision 10249)
+++ main/waeup.kofa/trunk/src/waeup/kofa/students/browser_templates/studycoursepage.pt	(revision 10250)
@@ -5,5 +5,5 @@
     <tal:block repeat="widget view/widgets">
       <tr>
-        <td class="filedname">
+        <td class="fieldname">
           <span tal:content="widget/label">label</span>:
         </td>
Index: main/waeup.kofa/trunk/src/waeup/kofa/students/browser_templates/transcript.pt
===================================================================
--- main/waeup.kofa/trunk/src/waeup/kofa/students/browser_templates/transcript.pt	(revision 10249)
+++ main/waeup.kofa/trunk/src/waeup/kofa/students/browser_templates/transcript.pt	(revision 10250)
@@ -5,6 +5,6 @@
   <tbody>
     <tal:block repeat="widget view/widgets">
-      <tr tal:condition="python: 'current' not in widget.name and 'verdict' not in widget.name">
-        <td class="filedname">
+      <tr>
+        <td class="fieldname">
           <span tal:content="widget/label">label</span>:
         </td>
Index: main/waeup.kofa/trunk/src/waeup/kofa/students/interfaces.py
===================================================================
--- main/waeup.kofa/trunk/src/waeup/kofa/students/interfaces.py	(revision 10249)
+++ main/waeup.kofa/trunk/src/waeup/kofa/students/interfaces.py	(revision 10250)
@@ -474,4 +474,24 @@
     'current_level'].order
 
+class IStudentStudyCourseTranscript(IKofaObject):
+    """An interface for student transcripts.
+
+    """
+
+    entry_mode = schema.Choice(
+        title = _(u'Entry Mode'),
+        source = StudyModeSource(),
+        required = True,
+        readonly = False,
+        )
+
+    entry_session = schema.Choice(
+        title = _(u'Entry Session'),
+        source = academic_sessions_vocab,
+        #default = datetime.now().year,
+        required = True,
+        readonly = False,
+        )
+
 class IStudentVerdictUpdate(IKofaObject):
     """A interface for verdict imports.
Index: main/waeup.kofa/trunk/src/waeup/kofa/students/studycourse.py
===================================================================
--- main/waeup.kofa/trunk/src/waeup/kofa/students/studycourse.py	(revision 10249)
+++ main/waeup.kofa/trunk/src/waeup/kofa/students/studycourse.py	(revision 10250)
@@ -25,5 +25,6 @@
 from zope.interface import implementedBy
 from waeup.kofa.students.interfaces import (
-    IStudentStudyCourse, IStudentNavigation, IStudentStudyLevel)
+    IStudentStudyCourse, IStudentNavigation, IStudentStudyLevel,
+    IStudentStudyCourseTranscript)
 from waeup.kofa.students.studylevel import CourseTicket
 from waeup.kofa.students.workflow import CLEARED, RETURNING, PAID
@@ -33,5 +34,6 @@
     """This is a container for study levels.
     """
-    grok.implements(IStudentStudyCourse, IStudentNavigation)
+    grok.implements(IStudentStudyCourse, IStudentNavigation,
+        IStudentStudyCourseTranscript)
     grok.provides(IStudentStudyCourse)
 
@@ -130,5 +132,6 @@
             tickets_2 = []
             tickets_3 = []
-            for ticket in level_val.values():
+            tickets = sorted(level_val.values(), key=lambda ticket: ticket.code)
+            for ticket in tickets:
                 if ticket.semester == 1:
                     tickets_1.append(ticket)
Index: main/waeup.kofa/trunk/src/waeup/kofa/students/utils.py
===================================================================
--- main/waeup.kofa/trunk/src/waeup/kofa/students/utils.py	(revision 10249)
+++ main/waeup.kofa/trunk/src/waeup/kofa/students/utils.py	(revision 10250)
@@ -107,5 +107,5 @@
         )
 
-def render_student_data(studentview):
+def render_student_data(studentview, omit_fields=()):
     """Render student table for an existing frame.
     """
@@ -143,36 +143,37 @@
 
     if getattr(studentview.context, 'certcode', None):
-        f_label = _('Study Course:')
-        f_label = Paragraph(f_label, ENTRY1_STYLE)
-        f_text = formatted_text(
-            studentview.context['studycourse'].certificate.longtitle())
-        f_text = Paragraph(f_text, ENTRY1_STYLE)
-        data_right.append([f_label,f_text])
-
-        f_label = _('Department:')
-        f_label = Paragraph(f_label, ENTRY1_STYLE)
-        f_text = formatted_text(
-            studentview.context[
-            'studycourse'].certificate.__parent__.__parent__.longtitle(),
-            )
-        f_text = Paragraph(f_text, ENTRY1_STYLE)
-        data_right.append([f_label,f_text])
-
-        f_label = _('Faculty:')
-        f_label = Paragraph(f_label, ENTRY1_STYLE)
-        f_text = formatted_text(
-            studentview.context[
-            'studycourse'].certificate.__parent__.__parent__.__parent__.longtitle(),
-            )
-        f_text = Paragraph(f_text, ENTRY1_STYLE)
-        data_right.append([f_label,f_text])
-
-        f_label = _('Entry Session: ')
-        f_label = Paragraph(f_label, ENTRY1_STYLE)
-        entry_session = studentview.context['studycourse'].entry_session
-        entry_session = academic_sessions_vocab.getTerm(entry_session).title
-        f_text = formatted_text(entry_session)
-        f_text = Paragraph(f_text, ENTRY1_STYLE)
-        data_right.append([f_label,f_text])
+        if not 'certificate' in omit_fields:
+            f_label = _('Study Course:')
+            f_label = Paragraph(f_label, ENTRY1_STYLE)
+            f_text = formatted_text(
+                studentview.context['studycourse'].certificate.longtitle())
+            f_text = Paragraph(f_text, ENTRY1_STYLE)
+            data_right.append([f_label,f_text])
+        if not 'department' in omit_fields:
+            f_label = _('Department:')
+            f_label = Paragraph(f_label, ENTRY1_STYLE)
+            f_text = formatted_text(
+                studentview.context[
+                'studycourse'].certificate.__parent__.__parent__.longtitle(),
+                )
+            f_text = Paragraph(f_text, ENTRY1_STYLE)
+            data_right.append([f_label,f_text])
+        if not 'faculty' in omit_fields:
+            f_label = _('Faculty:')
+            f_label = Paragraph(f_label, ENTRY1_STYLE)
+            f_text = formatted_text(
+                studentview.context[
+                'studycourse'].certificate.__parent__.__parent__.__parent__.longtitle(),
+                )
+            f_text = Paragraph(f_text, ENTRY1_STYLE)
+            data_right.append([f_label,f_text])
+        if not 'entry_session' in omit_fields:
+            f_label = _('Entry Session: ')
+            f_label = Paragraph(f_label, ENTRY1_STYLE)
+            entry_session = studentview.context['studycourse'].entry_session
+            entry_session = academic_sessions_vocab.getTerm(entry_session).title
+            f_text = formatted_text(entry_session)
+            f_text = Paragraph(f_text, ENTRY1_STYLE)
+            data_right.append([f_label,f_text])
 
     table_left = Table(data_left,style=SLIP_STYLE)
@@ -203,4 +204,40 @@
         element[2]*cm for element in tableheader], style=CONTENT_STYLE)
     return table
+
+def render_transcript_data(view,tableheader,levels_data):
+    """Render children table for an existing frame.
+    """
+    data = []
+    line = []
+    style = getSampleStyleSheet()
+
+    for level in levels_data:
+        level_obj = level['level']
+        subheader = 'Session %s, Level %s' % (
+            view.session_dict[level_obj.level_session],
+            view.course_levels.getTerm(level_obj.level).title)
+        data.append(Paragraph(subheader, HEADING_STYLE))
+
+        tabledata = []
+        for element in tableheader:
+            field = '<strong>%s</strong>' % formatted_text(element[0])
+            field = Paragraph(field, style["Normal"])
+            line.append(field)
+        tabledata.append(line)
+        tickets = []
+        tickets += level['tickets_1']
+        tickets += level['tickets_2']
+        tickets += level['tickets_3']
+        for ticket in tickets:
+            line = []
+            for element in tableheader:
+                  field = formatted_text(getattr(ticket,element[1],u' '))
+                  field = Paragraph(field, style["Normal"])
+                  line.append(field)
+            tabledata.append(line)
+        table = Table(tabledata,colWidths=[
+            element[2]*cm for element in tableheader], style=CONTENT_STYLE)
+        data.append(table)
+    return data
 
 def docs_as_flowables(view, lang='en'):
@@ -478,5 +515,5 @@
         return text
 
-    def renderPDFAdmissionLetter(self, view, student=None):
+    def renderPDFAdmissionLetter(self, view, student=None, omit_fields=()):
         """Render pdf admission letter.
         """
@@ -506,5 +543,5 @@
 
         # Student data
-        data.append(render_student_data(view))
+        data.append(render_student_data(view, omit_fields))
 
         # Insert history
@@ -540,5 +577,6 @@
                   tableheader_3=None, tabledata_3=None,
                   note=None, signatures=None, sigs_in_footer=(),
-                  show_scans=True, topMargin=1.5):
+                  show_scans=True, topMargin=1.5,
+                  omit_fields=()):
         """Render pdf slips for various pages.
         """
@@ -565,5 +603,5 @@
             bd_translation = trans(_('Base Data'), portal_language)
             data.append(Paragraph(bd_translation, HEADING_STYLE))
-            data.append(render_student_data(studentview))
+            data.append(render_student_data(studentview, omit_fields))
 
         # Insert widgets
@@ -636,4 +674,110 @@
         return pdf_stream
 
+    def renderPDFTranscript(self, view, filename='transcript.pdf',
+                  student=None,
+                  studentview=None,
+                  note=None, signatures=None, sigs_in_footer=(),
+                  show_scans=True, topMargin=1.5,
+                  omit_fields=(),
+                  tableheader=None):
+        """Render pdf slips for transcript.
+        """
+        # XXX: tell what the different parameters mean
+        style = getSampleStyleSheet()
+        creator = self.getPDFCreator(student)
+        data = []
+        doc_title = view.label
+        author = '%s (%s)' % (view.request.principal.title,
+                              view.request.principal.id)
+        footer_text = view.label.split('\n')
+        if len(footer_text) > 2:
+            # We can add a department in first line
+            footer_text = footer_text[1]
+        else:
+            # Only the first line is used for the footer
+            footer_text = footer_text[0]
+        if getattr(student, 'student_id', None) is not None:
+            footer_text = "%s - %s - " % (student.student_id, footer_text)
+
+        # Insert student data table
+        portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
+        if student is not None:
+            #bd_translation = trans(_('Base Data'), portal_language)
+            #data.append(Paragraph(bd_translation, HEADING_STYLE))
+            data.append(render_student_data(studentview, omit_fields))
+
+
+        transcript_data = view.context.getTranscriptData()
+        levels_data = transcript_data[0]
+        gpa = transcript_data[1]
+
+        contextdata = []
+        f_label = _('Course of Study:')
+        f_label = Paragraph(f_label, ENTRY1_STYLE)
+        f_text = formatted_text(view.context.certificate.longtitle())
+        f_text = Paragraph(f_text, ENTRY1_STYLE)
+        contextdata.append([f_label,f_text])
+
+        f_label = _('Faculty:')
+        f_label = Paragraph(f_label, ENTRY1_STYLE)
+        f_text = formatted_text(
+            view.context.certificate.__parent__.__parent__.__parent__.longtitle())
+        f_text = Paragraph(f_text, ENTRY1_STYLE)
+        contextdata.append([f_label,f_text])
+
+        f_label = _('Department:')
+        f_label = Paragraph(f_label, ENTRY1_STYLE)
+        f_text = formatted_text(
+            view.context.certificate.__parent__.__parent__.longtitle())
+        f_text = Paragraph(f_text, ENTRY1_STYLE)
+        contextdata.append([f_label,f_text])
+
+        f_label = _('Entry Session:')
+        f_label = Paragraph(f_label, ENTRY1_STYLE)
+        f_text = formatted_text(view.session_dict[view.context.entry_session])
+        f_text = Paragraph(f_text, ENTRY1_STYLE)
+        contextdata.append([f_label,f_text])
+
+        f_label = _('Entry Mode:')
+        f_label = Paragraph(f_label, ENTRY1_STYLE)
+        f_text = formatted_text(view.studymode_dict[view.context.entry_mode])
+        f_text = Paragraph(f_text, ENTRY1_STYLE)
+        contextdata.append([f_label,f_text])
+
+        f_label = _('Final GPA:')
+        f_label = Paragraph(f_label, ENTRY1_STYLE)
+        f_text = formatted_text(gpa)
+        f_text = Paragraph(f_text, ENTRY1_STYLE)
+        contextdata.append([f_label,f_text])
+
+        contexttable = Table(contextdata,style=SLIP_STYLE)
+        data.append(contexttable)
+
+        transcripttables = render_transcript_data(
+            view, tableheader, levels_data)
+        data.extend(transcripttables)
+
+        # Insert signatures
+        # XXX: We are using only sigs_in_footer in waeup.kofa, so we
+        # do not have a test for the following lines.
+        if signatures and not sigs_in_footer:
+            data.append(Spacer(1, 20))
+            # Render one signature table per signature to
+            # get date and signature in line.
+            for signature in signatures:
+                signaturetables = get_signature_tables(signature)
+                data.append(signaturetables[0])
+
+        view.response.setHeader(
+            'Content-Type', 'application/pdf')
+        try:
+            pdf_stream = creator.create_pdf(
+                data, None, doc_title, author=author, footer=footer_text,
+                note=note, sigs_in_footer=sigs_in_footer, topMargin=topMargin)
+        except IOError:
+            view.flash('Error in image file.')
+            return view.redirect(view.url(view.context))
+        return pdf_stream
+
     def maxCredits(self, studylevel):
         """Return maximum credits.
