Index: main/waeup.ikoba/trunk/src/waeup/ikoba/customers/browser.py
===================================================================
--- main/waeup.ikoba/trunk/src/waeup/ikoba/customers/browser.py	(revision 12007)
+++ main/waeup.ikoba/trunk/src/waeup/ikoba/customers/browser.py	(revision 12015)
@@ -1,3 +1,3 @@
-## $Id: browser.py 11862 2014-10-21 07:07:04Z henrik $
+## $Id$
 ##
 ## Copyright (C) 2014 Uli Fouquet & Henrik Bettermann
@@ -45,5 +45,6 @@
 from waeup.ikoba.utils.helpers import get_current_principal, to_timezone, now
 from waeup.ikoba.customers.interfaces import (
-    ICustomer, ICustomersContainer, ICustomerRequestPW, ICustomersUtils
+    ICustomer, ICustomersContainer, ICustomerRequestPW, ICustomersUtils,
+    ICustomerDocument, ICustomerDocumentsContainer
     )
 from waeup.ikoba.customers.catalog import search
@@ -583,5 +584,5 @@
         return
 
-# Pages for customers only
+# Pages for customers
 
 
@@ -628,2 +629,151 @@
                 self.flash(' '.join(errors), type="warning")
         return
+
+# Pages for customer documents
+
+class DocumentsBreadcrumb(Breadcrumb):
+    """A breadcrumb for the documents container.
+    """
+    grok.context(ICustomerDocumentsContainer)
+    title = _('Documents')
+
+
+class DocumentBreadcrumb(Breadcrumb):
+    """A breadcrumb for the customer container.
+    """
+    grok.context(ICustomerDocument)
+
+    @property
+    def title(self):
+        return self.context.document_id
+
+
+class DocumentsManageFormPage(IkobaEditFormPage):
+    """ Page to manage the customer documents
+
+    This manage form page is for both customers and customers officers.
+    """
+    grok.context(ICustomerDocumentsContainer)
+    grok.name('index')
+    grok.require('waeup.viewCustomer')
+    form_fields = grok.AutoFields(ICustomerDocumentsContainer)
+    grok.template('documentsmanagepage')
+    pnav = 4
+
+    @property
+    def manage_documents_allowed(self):
+        return checkPermission('waeup.editCustomerDocuments', self.context)
+
+    def unremovable(self, document):
+        usertype = getattr(self.request.principal, 'user_type', None)
+        if not usertype:
+            return False
+        if not self.manage_documents_allowed:
+            return True
+        return (self.request.principal.user_type == 'customer' and \
+            document.state in (VERIFIED, REJECTED, OUTDATED))
+
+    @property
+    def label(self):
+        return _('${a}: Documents',
+            mapping = {'a':self.context.__parent__.display_fullname})
+
+    @jsaction(_('Remove selected documents'))
+    def delDocument(self, **data):
+        form = self.request.form
+        if 'val_id' in form:
+            child_id = form['val_id']
+        else:
+            self.flash(_('No document selected.'), type="warning")
+            self.redirect(self.url(self.context))
+            return
+        if not isinstance(child_id, list):
+            child_id = [child_id]
+        deleted = []
+        for id in child_id:
+            # Customers are not allowed to remove used documents
+            document = self.context.get(id, None)
+            if document is not None and not self.unremovable(document):
+                del self.context[id]
+                deleted.append(id)
+        if len(deleted):
+            self.flash(_('Successfully removed: ${a}',
+                mapping = {'a': ', '.join(deleted)}))
+            self.context.writeLogMessage(
+                self,'removed: %s' % ', '.join(deleted))
+        self.redirect(self.url(self.context))
+        return
+
+
+class DocumentAddFormPage(IkobaAddFormPage):
+    """ Page to add an document
+    """
+    grok.context(ICustomerDocumentsContainer)
+    grok.name('adddoc')
+    grok.template('documentaddform')
+    grok.require('waeup.editCustomerDocuments')
+    form_fields = grok.AutoFields(ICustomerDocument)
+    label = _('Add document')
+    pnav = 4
+
+    @property
+    def selectable_doctypes(self):
+        doctypes = getUtility(IIkobaUtils).DOCUMENT_TYPES
+        return sorted(doctypes.items())
+
+    @action(_('Create document'), style='primary')
+    def createDocument(self, **data):
+        form = self.request.form
+        customer = self.context.__parent__
+        doctype = form.get('doctype', None)
+        # 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')
+        self.context.addDocument(document)
+        doctype = getUtility(IIkobaUtils).DOCUMENT_TYPES[doctype]
+        self.flash(_('${a} created.',
+            mapping = {'a': doctype}))
+        self.context.writeLogMessage(
+            self,'added: %s %s' % (doctype, document.document_id))
+        self.redirect(self.url(self.context))
+        return
+
+    @action(_('Cancel'), validator=NullValidator)
+    def cancel(self, **data):
+        self.redirect(self.url(self.context))
+
+
+class DocumentDisplayFormPage(IkobaDisplayFormPage):
+    """ Page to view a document
+    """
+    grok.context(ICustomerDocument)
+    grok.name('index')
+    grok.require('waeup.viewCustomer')
+    form_fields = grok.AutoFields(ICustomerDocument)
+    pnav = 4
+
+    @property
+    def label(self):
+        return _('${a}: Document ${b}', mapping = {
+            'a':self.context.customer.display_fullname,
+            'b':self.context.document_id})
+        return self.context.document_id
+
+
+class DocumentManageFormPage(IkobaEditFormPage):
+    """ Page to edit a document
+    """
+    grok.context(ICustomerDocument)
+    grok.name('manage')
+    grok.require('waeup.viewCustomer')
+    form_fields = grok.AutoFields(ICustomerDocument)
+    pnav = 4
+
+    @property
+    def label(self):
+        return _('${a}: Document ${b}', mapping = {
+            'a':self.context.customer.display_fullname,
+            'b':self.context.document_id})
+        return self.context.document_id
Index: main/waeup.ikoba/trunk/src/waeup/ikoba/customers/browser_templates/documentaddform.pt
===================================================================
--- main/waeup.ikoba/trunk/src/waeup/ikoba/customers/browser_templates/documentaddform.pt	(revision 12015)
+++ main/waeup.ikoba/trunk/src/waeup/ikoba/customers/browser_templates/documentaddform.pt	(revision 12015)
@@ -0,0 +1,26 @@
+<form action="." tal:attributes="action request/URL" method="post"
+      enctype="multipart/form-data">
+  <table class="form-table">
+    <tbody>
+      <tr>
+        <td class="fieldname">
+          <span>Document Type</span>:
+        </td>
+        <td>
+          <select id="doctype" name="doctype" size="1"
+                  class="form-control half">
+            <option tal:repeat="item view/selectable_doctypes"
+                    tal:attributes="value python:item[0]">
+              <span tal:replace="python:item[1]">DOCTYPE</span>
+            </option>
+          </select>
+        </td>
+      </tr>
+    </tbody>
+  </table>
+  <div tal:condition="view/availableActions">
+    <input tal:repeat="action view/actions"
+           tal:replace="structure action/render"
+           />
+  </div>
+</form>
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 12015)
+++ main/waeup.ikoba/trunk/src/waeup/ikoba/customers/browser_templates/documentsmanagepage.pt	(revision 12015)
@@ -0,0 +1,31 @@
+<form action="." tal:attributes="action request/URL" method="POST"
+      i18n:domain="waeup.ikoba" enctype="multipart/form-data">
+
+  <table class="ikoba-data-table dataTableManage">
+    <thead>
+    <tr>
+      <th>&nbsp;</th>
+      <th i18n:translate="">Document Id</th>
+    </tr>
+    </thead>
+    <tbody>
+      <tr tal:repeat="cl context/values">
+         <td>
+          <input type="checkbox"
+    		 name="val_id"
+    		 tal:attributes="value cl/__name__"
+         tal:condition="python: not view.unremovable(cl)" />
+        </td>
+        <td> <a tal:attributes="href python: view.url(cl)">
+        <span tal:content="cl/document_id">PDID</span></a></td>
+      </tr>
+    </tbody>
+  </table>
+
+  <div tal:condition="python: view.availableActions and view.manage_documents_allowed">
+    <span tal:repeat="action view/actions"
+          tal:omit-tag="">
+      <input tal:replace="structure action/render"/>
+    </span>
+  </div>
+</form>
Index: main/waeup.ikoba/trunk/src/waeup/ikoba/customers/documents.py
===================================================================
--- main/waeup.ikoba/trunk/src/waeup/ikoba/customers/documents.py	(revision 12007)
+++ main/waeup.ikoba/trunk/src/waeup/ikoba/customers/documents.py	(revision 12015)
@@ -1,3 +1,3 @@
-## $Id: documents.py 11583 2014-04-04 08:44:59Z henrik $
+## $Id$
 ##
 ## Copyright (C) 2014 Uli Fouquet & Henrik Bettermann
@@ -80,5 +80,5 @@
 
     def __call__(self, *args, **kw):
-        return CustomerDocument()
+        return CustomerDocument(*args, **kw)
 
     def getInterfaces(self):
Index: main/waeup.ikoba/trunk/src/waeup/ikoba/customers/permissions.py
===================================================================
--- main/waeup.ikoba/trunk/src/waeup/ikoba/customers/permissions.py	(revision 12007)
+++ main/waeup.ikoba/trunk/src/waeup/ikoba/customers/permissions.py	(revision 12015)
@@ -1,3 +1,3 @@
-## $Id: permissions.py 10465 2013-08-07 11:18:43Z henrik $
+## $Id$
 ##
 ## Copyright (C) 2011 Uli Fouquet & Henrik Bettermann
@@ -44,6 +44,6 @@
 
 
-class PayCustomer(grok.Permission):
-    grok.name('waeup.payCustomer')
+class EditCustomerDocuments(grok.Permission):
+    grok.name('waeup.editCustomerDocuments')
 
 
@@ -69,5 +69,5 @@
     grok.title(u'Customer Record Owner')
     grok.permissions('waeup.handleCustomer', 'waeup.uploadCustomerFile',
-                     'waeup.viewCustomer', 'waeup.payCustomer')
+                     'waeup.viewCustomer', 'waeup.editCustomerDocuments')
 
 
@@ -92,5 +92,5 @@
     grok.permissions('waeup.viewCustomer', 'waeup.viewCustomers',
                      'waeup.manageCustomer', 'waeup.viewCustomersContainer',
-                     'waeup.payCustomer', 'waeup.uploadCustomerFile',
+                     'waeup.editCustomerDocuments', 'waeup.uploadCustomerFile',
                      'waeup.viewCustomersTab', 'waeup.triggerTransition')
 
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 12007)
+++ main/waeup.ikoba/trunk/src/waeup/ikoba/customers/tests/test_browser.py	(revision 12015)
@@ -1,3 +1,3 @@
-## $Id: test_browser.py 11862 2014-10-21 07:07:04Z henrik $
+## $Id$
 ## 
 ## Copyright (C) 2014 Uli Fouquet & Henrik Bettermann
@@ -101,4 +101,7 @@
         self.customer_id = customer.customer_id
         self.customer = self.app['customers'][self.customer_id]
+        document = createObject('waeup.CustomerDocument')
+        document.title = u'My first document'
+        self.customer['documents'].addDocument(document)
 
         # Set password
@@ -114,4 +117,5 @@
         self.trigtrans_path = self.customer_path + '/trigtrans'
         self.history_path = self.customer_path + '/history'
+        self.documents_path = self.customer_path + '/documents'
 
         self.app['configuration'].carry_over = True
@@ -766,2 +770,42 @@
             'job_id=%s' % job_id in logcontent
             )
+
+
+class CustomerDocumentUITests(CustomersFullSetup):
+    # Tests for CustomerDocument relates views and pages
+
+    def test_manage_document(self):
+        # Managers can access the pages of customer documentsconter
+        # and can perform actions
+        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
+        self.browser.open(self.customer_path)
+        self.assertEqual(self.browser.headers['Status'], '200 Ok')
+        self.assertEqual(self.browser.url, self.customer_path)
+        self.browser.open(self.customer_path)
+        self.browser.getLink("Documents").click()
+        self.browser.getLink("Add document").click()
+        self.browser.getControl(name="doctype").value = ['generic']
+        self.browser.getControl("Create document").click()
+        self.assertTrue('Generic Document created.' in self.browser.contents)
+        document = self.customer['documents']['d102']
+
+        # Documents can be removed
+        ctrl = self.browser.getControl(name='val_id')
+        ctrl.getControl(value=document.document_id).selected = True
+        self.browser.getControl("Remove selected", index=0).click()
+        self.assertTrue('Successfully removed' in self.browser.contents)
+
+        # All actions are being logged
+        logfile = os.path.join(
+            self.app['datacenter'].storage, 'logs', 'customers.log')
+        logcontent = open(logfile).read()
+
+        self.assertTrue(
+            'INFO - zope.mgr - customers.browser.DocumentAddFormPage '
+            '- K1000000 - added: Generic Document %s'
+            % document.document_id in logcontent)
+
+        self.assertTrue(
+            'INFO - zope.mgr - customers.browser.DocumentsManageFormPage '
+            '- K1000000 - removed: %s'
+            % document.document_id in logcontent)
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 12007)
+++ main/waeup.ikoba/trunk/src/waeup/ikoba/customers/tests/test_export.py	(revision 12015)
@@ -138,6 +138,6 @@
         self.assertEqual(
             result,
-            'doctype,document_id,history,title,customer_id\r\n'
-            ',d101,[],,\r\n'
+            'document_id,history,title,customer_id\r\n'
+            'd101,[],,\r\n'
             )
         return
@@ -152,5 +152,5 @@
         result = open(self.outfile, 'rb').read()
         self.assertTrue(
-            'doctype,document_id,history,title,customer_id\r\n'
+            'document_id,history,title,customer_id\r\n'
             in result
             )
@@ -169,5 +169,5 @@
         result = open(self.outfile, 'rb').read()
         self.assertTrue(
-            'doctype,document_id,history,title,customer_id\r\n'
+            'document_id,history,title,customer_id\r\n'
             in result)
         self.assertTrue(
@@ -184,5 +184,5 @@
         result = open(self.outfile, 'rb').read()
         self.assertTrue(
-            'doctype,document_id,history,title,customer_id\r\n'
+            'document_id,history,title,customer_id\r\n'
             in result)
         self.assertTrue(
Index: main/waeup.ikoba/trunk/src/waeup/ikoba/customers/viewlets.py
===================================================================
--- main/waeup.ikoba/trunk/src/waeup/ikoba/customers/viewlets.py	(revision 12007)
+++ main/waeup.ikoba/trunk/src/waeup/ikoba/customers/viewlets.py	(revision 12015)
@@ -1,3 +1,3 @@
-## $Id: viewlets.py 11772 2014-07-31 04:38:23Z henrik $
+## $Id$
 ##
 ## Copyright (C) 2014 Uli Fouquet & Henrik Bettermann
@@ -26,8 +26,10 @@
 from waeup.ikoba.browser.layout import default_primary_nav_template
 from waeup.ikoba.customers.interfaces import (
-    ICustomer, ICustomersContainer)
+    ICustomer, ICustomersContainer,
+    ICustomerDocumentsContainer)
 from waeup.ikoba.customers.browser import (
     CustomersContainerPage, CustomersContainerManagePage,
-    CustomerBaseDisplayFormPage)
+    CustomerBaseDisplayFormPage,
+    DocumentsManageFormPage)
 
 grok.context(IIkobaObject)  # Make IIkobaObject the default context
@@ -105,4 +107,5 @@
         targets += [
             {'url':customer_url, 'title':'Base Data'},
+            {'url':customer_url + '/documents', 'title':_('Documents')},
             {'url':customer_url + '/history', 'title':_('History')},
             ]
@@ -145,4 +148,9 @@
     text = _(u'Base Data')
 
+class CustomerManageDocumentsLink(CustomerManageLink):
+    grok.order(3)
+    link = 'documents'
+    text = _(u'Documents')
+
 
 class CustomerManageHistoryLink(CustomerManageLink):
@@ -275,2 +283,14 @@
     text = _('Change portrait')
     target = 'change_portrait'
+
+
+# Viewlets for customer cocuments
+
+
+class AddDocumentActionButton(AddActionButton):
+    grok.order(1)
+    grok.context(ICustomerDocumentsContainer)
+    grok.view(DocumentsManageFormPage)
+    grok.require('waeup.editCustomerDocuments')
+    text = _('Add document')
+    target = 'adddoc'
