Index: main/waeup.ikoba/trunk/src/waeup/ikoba/customers/browser.py
===================================================================
--- main/waeup.ikoba/trunk/src/waeup/ikoba/customers/browser.py	(revision 12052)
+++ main/waeup.ikoba/trunk/src/waeup/ikoba/customers/browser.py	(revision 12053)
@@ -39,4 +39,7 @@
     IkobaPage, IkobaEditFormPage, IkobaAddFormPage, IkobaDisplayFormPage,
     IkobaForm, NullValidator, jsaction, action, UtilityView)
+from waeup.ikoba.widgets.datewidget import (
+    FriendlyDateWidget, FriendlyDateDisplayWidget,
+    FriendlyDatetimeDisplayWidget)
 from waeup.ikoba.browser.pages import ContactAdminForm
 from waeup.ikoba.browser.breadcrumbs import Breadcrumb
@@ -796,5 +799,5 @@
     @property
     def selectable_doctypes(self):
-        doctypes = getUtility(IIkobaUtils).DOCUMENT_TYPES
+        doctypes = getUtility(ICustomersUtils).SELECTABLE_DOCTYPES_DICT
         return sorted(doctypes.items())
 
@@ -806,9 +809,7 @@
         # Here we can create various instances of CustomerDocument derived 
         # classes depending on the doctype parameter given in form.
-        # So far we only have one CustomerDocument class and no derived
-        # classes.
-        document = createObject(u'waeup.CustomerDocument')
+        document = createObject('waeup.%s' % doctype)
         self.context.addDocument(document)
-        doctype = getUtility(IIkobaUtils).DOCUMENT_TYPES[doctype]
+        doctype = getUtility(ICustomersUtils).SELECTABLE_DOCTYPES_DICT[doctype]
         self.flash(_('${a} created.',
             mapping = {'a': doctype}))
@@ -831,4 +832,6 @@
     grok.template('documentpage')
     form_fields = grok.AutoFields(ICustomerDocument)
+    form_fields[
+        'last_transition_date'].custom_widget = FriendlyDatetimeDisplayWidget('le')
     pnav = 4
 
@@ -851,5 +854,5 @@
     grok.require('waeup.manageCustomer')
     grok.template('documenteditpage')
-    form_fields = grok.AutoFields(ICustomerDocument)
+    form_fields = grok.AutoFields(ICustomerDocument).omit('last_transition_date')
     pnav = 4
     deletion_warning = _('Are you sure?')
@@ -960,16 +963,20 @@
     def render(self):
         portal_language = getUtility(IIkobaUtils).PORTAL_LANGUAGE
-        Id = translate(_('Document Id'), 'waeup.ikoba', target_language=portal_language)
+        Id = translate(_('Id'), 'waeup.ikoba', target_language=portal_language)
         Title = translate(_('Title'), 'waeup.ikoba', target_language=portal_language)
+        Type = translate(_('Type'), 'waeup.ikoba', target_language=portal_language)
         State = translate(_('State'), 'waeup.ikoba', target_language=portal_language)
+        LT = translate(_('Last Transition'), 'waeup.ikoba', target_language=portal_language)
+        tableheader = []
         tabledata = []
-        tableheader = []
         contenttitle = []
         for i in range(1,3):
             tabledata.append(sorted(
                 [value for value in self.context['documents'].values()]))
-            tableheader.append([(Id, 'document_id', 3),
-                             (Title, 'title', 8),
-                             (State, 'translated_state', 3),
+            tableheader.append([(Id, 'document_id', 2),
+                             (Title, 'title', 6),
+                             (Type, 'translated_classname', 6),
+                             (State, 'translated_state', 2),
+                             (LT, 'formatted_transition_date', 3),
                              ])
         customerview = CustomerBasePDFFormPage(self.context,
Index: main/waeup.ikoba/trunk/src/waeup/ikoba/customers/browser_templates/documentsmanagepage.pt
===================================================================
--- main/waeup.ikoba/trunk/src/waeup/ikoba/customers/browser_templates/documentsmanagepage.pt	(revision 12052)
+++ main/waeup.ikoba/trunk/src/waeup/ikoba/customers/browser_templates/documentsmanagepage.pt	(revision 12053)
@@ -8,4 +8,7 @@
       <th i18n:translate="">Document Id</th>
       <th i18n:translate="">Title</th>
+      <th i18n:translate="">Type</th>
+      <th i18n:translate="">State</th>
+      <th i18n:translate="">Last Transition</th>
     </tr>
     </thead>
@@ -25,4 +28,15 @@
           <span tal:content="cl/title">TITLE</span>
         </td>
+        <td>
+          <span tal:content="cl/translated_classname">CLASSNAME</span>
+        </td>
+        <td>
+          <span tal:content="cl/translated_state">STATE</span>
+        </td>
+        <td>
+          <span tal:content="cl/formatted_transition_date">
+              LASTTRANSITIONDATE
+          </span>
+        </td>
       </tr>
     </tbody>
Index: main/waeup.ikoba/trunk/src/waeup/ikoba/customers/documents.py
===================================================================
--- main/waeup.ikoba/trunk/src/waeup/ikoba/customers/documents.py	(revision 12052)
+++ main/waeup.ikoba/trunk/src/waeup/ikoba/customers/documents.py	(revision 12053)
@@ -33,5 +33,5 @@
 from waeup.ikoba.customers.interfaces import (
     ICustomerDocumentsContainer, ICustomerNavigation, ICustomerDocument,
-    ICustomersUtils)
+    ICustomersUtils, ICustomerPDFDocument)
 from waeup.ikoba.documents import DocumentsContainer, Document
 from waeup.ikoba.documents.interfaces import IDocumentsUtils
@@ -60,5 +60,5 @@
 
 class CustomerDocument(Document):
-    """This is an document.
+    """This is a customer document.
     """
     grok.implements(ICustomerDocument, ICustomerNavigation)
@@ -94,5 +94,21 @@
         return True
 
+    @property
+    def translated_classname(self):
+        try:
+            DOCTYPES_DICT = getUtility(ICustomersUtils).DOCTYPES_DICT
+            return DOCTYPES_DICT[self.classname]
+        except KeyError:
+            return
+
 CustomerDocument = attrs_to_fields(CustomerDocument)
+
+class CustomerPDFDocument(CustomerDocument):
+    """This is a customer document for a single pdf upload file only.
+    """
+    grok.implements(ICustomerPDFDocument, ICustomerNavigation)
+    grok.provides(ICustomerPDFDocument)
+
+CustomerPDFDocument = attrs_to_fields(CustomerPDFDocument)
 
 
@@ -104,5 +120,5 @@
     grok.name(u'waeup.CustomerDocument')
     title = u"Create a new document.",
-    description = u"This factory instantiates new document instances."
+    description = u"This factory instantiates new sample document instances."
 
     def __call__(self, *args, **kw):
@@ -111,4 +127,19 @@
     def getInterfaces(self):
         return implementedBy(CustomerDocument)
+
+# Customer documents must be importable. So we might need a factory.
+class CustomerPDFDocumentFactory(grok.GlobalUtility):
+    """A factory for customer pdf documents.
+    """
+    grok.implements(IFactory)
+    grok.name(u'waeup.CustomerPDFDocument')
+    title = u"Create a new document.",
+    description = u"This factory instantiates new pdf document instances."
+
+    def __call__(self, *args, **kw):
+        return CustomerPDFDocument(*args, **kw)
+
+    def getInterfaces(self):
+        return implementedBy(CustomerPDFDocument)
 
 #: The file id marker for customer files
Index: main/waeup.ikoba/trunk/src/waeup/ikoba/customers/export.py
===================================================================
--- main/waeup.ikoba/trunk/src/waeup/ikoba/customers/export.py	(revision 12052)
+++ main/waeup.ikoba/trunk/src/waeup/ikoba/customers/export.py	(revision 12053)
@@ -147,5 +147,8 @@
             ICustomerDocument,
             exclude_attribs=False,
-            omit=['is_editable', 'translated_state']))) + (
+            omit=['is_editable',
+                  'translated_state',
+                  'formatted_transition_date',
+                  'translated_classname']))) + (
             'customer_id',)
 
Index: main/waeup.ikoba/trunk/src/waeup/ikoba/customers/interfaces.py
===================================================================
--- main/waeup.ikoba/trunk/src/waeup/ikoba/customers/interfaces.py	(revision 12052)
+++ main/waeup.ikoba/trunk/src/waeup/ikoba/customers/interfaces.py	(revision 12053)
@@ -270,2 +270,9 @@
 
     is_editable = Attribute('Document editable by customer')
+    translated_classname = Attribute('Translatable classname')
+
+
+class ICustomerPDFDocument(IDocument):
+    """A customer document.
+
+    """
Index: main/waeup.ikoba/trunk/src/waeup/ikoba/customers/tests/test_browser.py
===================================================================
--- main/waeup.ikoba/trunk/src/waeup/ikoba/customers/tests/test_browser.py	(revision 12052)
+++ main/waeup.ikoba/trunk/src/waeup/ikoba/customers/tests/test_browser.py	(revision 12053)
@@ -812,7 +812,7 @@
         self.browser.getLink("Documents").click()
         self.browser.getLink("Add document").click()
-        self.browser.getControl(name="doctype").value = ['sample']
+        self.browser.getControl(name="doctype").value = ['CustomerPDFDocument']
         self.browser.getControl("Create document").click()
-        self.assertTrue('Sample Document created.' in self.browser.contents)
+        self.assertTrue('PDF Document created.' in self.browser.contents)
         document = self.customer['documents']['d102']
 
@@ -853,5 +853,5 @@
         self.assertTrue(
             'INFO - zope.mgr - customers.browser.DocumentAddFormPage '
-            '- K1000000 - added: Sample Document %s'
+            '- K1000000 - added: PDF Document %s'
             % document.document_id in logcontent)
 
@@ -871,5 +871,5 @@
         self.browser.getLink("Documents").click()
         self.browser.getLink("Add document").click()
-        self.browser.getControl(name="doctype").value = ['sample']
+        self.browser.getControl(name="doctype").value = ['CustomerDocument']
         self.browser.getControl("Create document").click()
         self.assertTrue('Sample Document created.' in self.browser.contents)
Index: main/waeup.ikoba/trunk/src/waeup/ikoba/customers/tests/test_document.py
===================================================================
--- main/waeup.ikoba/trunk/src/waeup/ikoba/customers/tests/test_document.py	(revision 12052)
+++ main/waeup.ikoba/trunk/src/waeup/ikoba/customers/tests/test_document.py	(revision 12053)
@@ -61,4 +61,5 @@
         self.assertEqual(container[id], document)
         self.assertRaises(TypeError, container.addDocument, object())
+        self.assertEqual(document.classname, 'CustomerDocument')
         return
 
Index: main/waeup.ikoba/trunk/src/waeup/ikoba/customers/tests/test_export.py
===================================================================
--- main/waeup.ikoba/trunk/src/waeup/ikoba/customers/tests/test_export.py	(revision 12052)
+++ main/waeup.ikoba/trunk/src/waeup/ikoba/customers/tests/test_export.py	(revision 12053)
@@ -138,6 +138,6 @@
         self.assertEqual(
             result,
-            'document_id,history,state,title,customer_id\r\n'
-            'd101,[],,,\r\n'
+            'classname,document_id,history,last_transition_date,state,title,customer_id\r\n'
+            'CustomerDocument,d101,[],,,,\r\n'
             )
         return
@@ -152,10 +152,12 @@
         result = open(self.outfile, 'rb').read()
         self.assertTrue(
-            'document_id,history,state,title,customer_id\r\n'
+            'classname,document_id,history,last_transition_date,state,title,customer_id\r\n'
             in result
             )
-        self.assertTrue(
-            'Document created by system\'],created,My Document,A111111\r\n'
-            in result
+        self.assertMatches(
+            '...CustomerDocument,d101,[u\'2014-11-25 06:57:24 UTC - '
+            'Document created by system\'],2014-11-25 06:57:24.990308#,'
+            'created,My Document,A111111...',
+            result
             )
         return
@@ -169,9 +171,12 @@
         result = open(self.outfile, 'rb').read()
         self.assertTrue(
-            'document_id,history,state,title,customer_id\r\n'
+            'classname,document_id,history,last_transition_date,state,title,customer_id\r\n'
             in result)
-        self.assertTrue(
-            ' Document created by system\'],created,My Document,A111111\r\n'
-            in result)
+        self.assertMatches(
+            '...CustomerDocument,d101,[u\'2014-11-25 06:57:24 UTC - '
+            'Document created by system\'],2014-11-25 06:57:24.990308#,'
+            'created,My Document,A111111...',
+            result
+            )
         return
 
@@ -184,8 +189,11 @@
         result = open(self.outfile, 'rb').read()
         self.assertTrue(
-            'document_id,history,state,title,customer_id\r\n'
+            'classname,document_id,history,last_transition_date,state,title,customer_id\r\n'
             in result)
-        self.assertTrue(
-            ' Document created by system\'],created,My Document,A111111\r\n'
-            in result)
+        self.assertMatches(
+            '...CustomerDocument,d101,[u\'2014-11-25 06:57:24 UTC - '
+            'Document created by system\'],2014-11-25 06:57:24.990308#,'
+            'created,My Document,A111111...',
+            result
+            )
         return
Index: main/waeup.ikoba/trunk/src/waeup/ikoba/customers/utils.py
===================================================================
--- main/waeup.ikoba/trunk/src/waeup/ikoba/customers/utils.py	(revision 12052)
+++ main/waeup.ikoba/trunk/src/waeup/ikoba/customers/utils.py	(revision 12053)
@@ -288,4 +288,11 @@
         }
 
+    DOCTYPES_DICT = {
+        'CustomerDocument': 'Sample Document',
+        'CustomerPDFDocument': 'PDF Document',
+        }
+
+    SELECTABLE_DOCTYPES_DICT = DOCTYPES_DICT
+
     def getPDFCreator(self, context=None):
         """Get a pdf creator suitable for `context`.
Index: main/waeup.ikoba/trunk/src/waeup/ikoba/documents/document.py
===================================================================
--- main/waeup.ikoba/trunk/src/waeup/ikoba/documents/document.py	(revision 12052)
+++ main/waeup.ikoba/trunk/src/waeup/ikoba/documents/document.py	(revision 12053)
@@ -76,8 +76,19 @@
         try:
             TRANSLATED_STATES = getUtility(IDocumentsUtils).TRANSLATED_STATES
-            ts = TRANSLATED_STATES[self.state]
-            return ts
+            return TRANSLATED_STATES[self.state]
         except KeyError:
             return
+
+    @property
+    def classname(self):
+        return self.__class__.__name__
+
+    @property
+    def formatted_transition_date(self):
+        try:
+            return self.last_transition_date.strftime('%Y-%m-%d %H:%M:%S')
+        except AttributeError:
+            return
+        
 
 Document = attrs_to_fields(Document)
Index: main/waeup.ikoba/trunk/src/waeup/ikoba/documents/interfaces.py
===================================================================
--- main/waeup.ikoba/trunk/src/waeup/ikoba/documents/interfaces.py	(revision 12052)
+++ main/waeup.ikoba/trunk/src/waeup/ikoba/documents/interfaces.py	(revision 12053)
@@ -49,4 +49,6 @@
     translated_state = Attribute(
         'Returns a translated, more verbose verification state of a document')
+    classname = Attribute('Name of the document class')
+    formatted_transition_date = Attribute('Last transition formatted date string')
 
     title = schema.TextLine(
@@ -55,4 +57,11 @@
         )
 
+    last_transition_date = schema.Datetime(
+        title = _(u'Last Transition Date'),
+        required = False,
+        readonly = False,
+        )
+
+
 class IDocumentsUtils(Interface):
     """A collection of methods which are subject to customization.
Index: main/waeup.ikoba/trunk/src/waeup/ikoba/documents/workflow.py
===================================================================
--- main/waeup.ikoba/trunk/src/waeup/ikoba/documents/workflow.py	(revision 12052)
+++ main/waeup.ikoba/trunk/src/waeup/ikoba/documents/workflow.py	(revision 12053)
@@ -124,9 +124,11 @@
 @grok.subscribe(IDocument, IWorkflowTransitionEvent)
 def handle_document_transition_event(obj, event):
-    """Append message to document history and log file when transition happened.
+    """Append message to document history and log file and update
+    last_transition_date when transition happened.
     """
     msg = event.transition.user_data['msg']
     history = IObjectHistory(obj)
     history.addMessage(msg)
+    obj.last_transition_date = datetime.utcnow()
     try:
         customers_container = grok.getSite()['customers']
Index: main/waeup.ikoba/trunk/src/waeup/ikoba/utils/utils.py
===================================================================
--- main/waeup.ikoba/trunk/src/waeup/ikoba/utils/utils.py	(revision 12052)
+++ main/waeup.ikoba/trunk/src/waeup/ikoba/utils/utils.py	(revision 12053)
@@ -114,9 +114,4 @@
         ('C', 'Good'),
         )
-
-    # Should be overridden in custom packages
-    DOCUMENT_TYPES = {
-        'sample': 'Sample Document',
-        }
 
     PAYMENT_CATEGORIES = {
