Index: main/waeup.ikoba/trunk/src/waeup/ikoba/customers/browser.py
===================================================================
--- main/waeup.ikoba/trunk/src/waeup/ikoba/customers/browser.py	(revision 12034)
+++ main/waeup.ikoba/trunk/src/waeup/ikoba/customers/browser.py	(revision 12035)
@@ -780,4 +780,5 @@
     form_fields = grok.AutoFields(ICustomerDocument)
     pnav = 4
+    deletion_warning = _('Are you sure?')
 
     #@property
Index: main/waeup.ikoba/trunk/src/waeup/ikoba/customers/browser_templates/documenteditpage.pt
===================================================================
--- main/waeup.ikoba/trunk/src/waeup/ikoba/customers/browser_templates/documenteditpage.pt	(revision 12034)
+++ main/waeup.ikoba/trunk/src/waeup/ikoba/customers/browser_templates/documenteditpage.pt	(revision 12035)
@@ -10,5 +10,5 @@
     <li>
       <a href="#tab2" data-toggle="tab">
-        <span i18n:translate="">Scans</span>
+        <span i18n:translate="">Files</span>
       </a>
     </li>
Index: main/waeup.ikoba/trunk/src/waeup/ikoba/customers/browser_templates/documentpage.pt
===================================================================
--- main/waeup.ikoba/trunk/src/waeup/ikoba/customers/browser_templates/documentpage.pt	(revision 12034)
+++ main/waeup.ikoba/trunk/src/waeup/ikoba/customers/browser_templates/documentpage.pt	(revision 12035)
@@ -16,5 +16,5 @@
     <tr tal:condition="python: files.replace('\n','')">
       <td class="separator" colspan=2 i18n:translate="">
-        Uploaded Files
+        Connected Files
       </td>
     </tr>
Index: main/waeup.ikoba/trunk/src/waeup/ikoba/customers/customer.py
===================================================================
--- main/waeup.ikoba/trunk/src/waeup/ikoba/customers/customer.py	(revision 12034)
+++ main/waeup.ikoba/trunk/src/waeup/ikoba/customers/customer.py	(revision 12035)
@@ -20,5 +20,4 @@
 """
 import os
-import re
 import shutil
 import grok
@@ -42,10 +41,7 @@
     ICustomer, ICustomerNavigation, ICSVCustomerExporter,
     ICustomersUtils)
-from waeup.ikoba.customers.utils import generate_customer_id
+from waeup.ikoba.customers.utils import generate_customer_id, path_from_custid
 from waeup.ikoba.customers.documents import CustomerDocumentsContainer
 from waeup.ikoba.utils.helpers import attrs_to_fields, now, copy_filesystem_tree
-
-RE_CUSTID_NON_NUM = re.compile('[^\d]+')
-
 
 class Customer(grok.Container):
@@ -188,43 +184,4 @@
     return
 
-
-def path_from_custid(customer_id):
-    """Convert a customer_id into a predictable relative folder path.
-
-    Used for storing files.
-
-    Returns the name of folder in which files for a particular customer
-    should be stored. This is a relative path, relative to any general
-    customers folder with 5 zero-padded digits (except when customer_id
-    is overlong).
-
-    We normally map 1,000 different customer ids into one single
-    path. For instance ``K1000000`` will give ``01000/K1000000``,
-    ``K1234567`` will give ``0123/K1234567`` and ``K12345678`` will
-    result in ``1234/K12345678``.
-
-    For lower numbers < 10**6 we return the same path for up to 10,000
-    customer_ids. So for instance ``KM123456`` will result in
-    ``00120/KM123456`` (there will be no path starting with
-    ``00123``).
-
-    Works also with overlong number: here the leading zeros will be
-    missing but ``K123456789`` will give reliably
-    ``12345/K123456789`` as expected.
-    """
-    # remove all non numeric characters and turn this into an int.
-    num = int(RE_CUSTID_NON_NUM.sub('', customer_id))
-    if num < 10**6:
-        # store max. of 10000 custs per folder and correct num for 5 digits
-        num = num / 10000 * 10
-    else:
-        # store max. of 1000 custs per folder
-        num = num / 1000
-    # format folder name to have 5 zero-padded digits
-    folder_name = u'%05d' % num
-    folder_name = os.path.join(folder_name, customer_id)
-    return folder_name
-
-
 def move_customer_files(customer, del_dir):
     """Move files belonging to `customer` to `del_dir`.
@@ -340,5 +297,5 @@
     `context` is an :class:`Customer` instance.
 
-    The :class:`CustomerImageNameChooser` can build/check file ids for
+    The :class:`CustomerFileNameChooser` can build/check file ids for
     :class:`Customer` objects suitable for use with
     :class:`ExtFileStore` instances. The delivered file_id contains
@@ -380,5 +337,5 @@
           ``'__file-customer__customers/A/A123456/nice_image_A123456.jpeg'``
 
-        meaning that the nice image of this applicant would be
+        meaning that the nice image of this customer would be
         stored in the site-wide file storage in path:
 
Index: main/waeup.ikoba/trunk/src/waeup/ikoba/customers/documents.py
===================================================================
--- main/waeup.ikoba/trunk/src/waeup/ikoba/customers/documents.py	(revision 12034)
+++ main/waeup.ikoba/trunk/src/waeup/ikoba/customers/documents.py	(revision 12035)
@@ -19,9 +19,16 @@
 Customer document components.
 """
+import os
 import grok
 from zope.component import queryUtility, getUtility
 from zope.component.interfaces import IFactory
 from zope.interface import implementedBy
+
+from waeup.ikoba.image import IkobaImageFile
+from waeup.ikoba.imagestorage import DefaultFileStoreHandler
 from waeup.ikoba.interfaces import MessageFactory as _
+from waeup.ikoba.interfaces import (
+    IFileStoreNameChooser, IFileStoreHandler,
+    IIkobaUtils, IExtFileStore)
 from waeup.ikoba.customers.interfaces import (
     ICustomerDocumentsContainer, ICustomerNavigation, ICustomerDocument,
@@ -30,4 +37,6 @@
 from waeup.ikoba.documents.interfaces import IDocumentsUtils
 from waeup.ikoba.utils.helpers import attrs_to_fields
+
+from waeup.ikoba.customers.utils import path_from_custid
 
 class CustomerDocumentsContainer(DocumentsContainer):
@@ -102,2 +111,106 @@
     def getInterfaces(self):
         return implementedBy(CustomerDocument)
+
+#: The file id marker for customer files
+CUSTOMERDOCUMENT_FILE_STORE_NAME = 'file-customerdocument'
+
+
+class CustomerDocumentFileNameChooser(grok.Adapter):
+    """A file id chooser for :class:`CustomerDocument` objects.
+
+    `context` is an :class:`CustomerDocument` instance.
+
+    The :class:`CustomerDocumentFileNameChooser` can build/check file ids for
+    :class:`Customer` objects suitable for use with
+    :class:`ExtFileStore` instances. The delivered file_id contains
+    the file id marker for :class:`CustomerDocument` object and the customer id
+    of the context customer.
+
+    This chooser is registered as an adapter providing
+    :class:`waeup.ikoba.interfaces.IFileStoreNameChooser`.
+
+    File store name choosers like this one are only convenience
+    components to ease the task of creating file ids for customer document
+    objects. You are nevertheless encouraged to use them instead of
+    manually setting up filenames for customer documents.
+
+    .. seealso:: :mod:`waeup.ikoba.imagestorage`
+
+    """
+
+    grok.context(ICustomerDocument)
+    grok.implements(IFileStoreNameChooser)
+
+    def checkName(self, name=None, attr=None):
+        """Check whether the given name is a valid file id for the context.
+
+        Returns ``True`` only if `name` equals the result of
+        :meth:`chooseName`.
+
+        """
+        return name == self.chooseName()
+
+    def chooseName(self, attr, name=None):
+        """Get a valid file id for customer document context.
+
+        *Example:*
+
+        For a customer with customer id ``'A123456'``
+        and document with id 'd123'
+        with attr ``'nice_image.jpeg'`` stored in
+        the customers container this chooser would create:
+
+          ``'__file-customerdocument__customers/A/A123456/nice_image_d123_A123456.jpeg'``
+
+        meaning that the nice image of this customer document would be
+        stored in the site-wide file storage in path:
+
+          ``customers/A/A123456/nice_image_d123_A123456.jpeg``
+
+        """
+        basename, ext = os.path.splitext(attr)
+        cust_id = self.context.customer.customer_id
+        doc_id = self.context.document_id
+        marked_filename = '__%s__%s/%s_%s_%s%s' % (
+            CUSTOMERDOCUMENT_FILE_STORE_NAME, path_from_custid(cust_id),
+            basename, doc_id, cust_id, ext)
+        return marked_filename
+
+
+class CustomerDocumentFileStoreHandler(DefaultFileStoreHandler, grok.GlobalUtility):
+    """Customer document specific file handling.
+
+    This handler knows in which path in a filestore to store customer document
+    files and how to turn this kind of data into some (browsable)
+    file object.
+
+    It is called from the global file storage, when it wants to
+    get/store a file with a file id starting with
+    ``__file-customerdocument__`` (the marker string for customer files).
+
+    Like each other file store handler it does not handle the files
+    really (this is done by the global file store) but only computes
+    paths and things like this.
+    """
+    grok.implements(IFileStoreHandler)
+    grok.name(CUSTOMERDOCUMENT_FILE_STORE_NAME)
+
+    def pathFromFileID(self, store, root, file_id):
+        """All customer document files are put in directory ``customers``.
+        """
+        marker, filename, basename, ext = store.extractMarker(file_id)
+        sub_root = os.path.join(root, 'customers')
+        return super(CustomerDocumentFileStoreHandler, self).pathFromFileID(
+            store, sub_root, basename)
+
+    def createFile(self, store, root, filename, file_id, file):
+        """Create a browsable file-like object.
+        """
+        # call super method to ensure that any old files with
+        # different filename extension are deleted.
+        file, path, file_obj = super(
+            CustomerDocumentFileStoreHandler, self).createFile(
+            store, root,  filename, file_id, file)
+        return file, path, IkobaImageFile(
+            file_obj.filename, file_obj.data)
+
Index: main/waeup.ikoba/trunk/src/waeup/ikoba/customers/files.py
===================================================================
--- main/waeup.ikoba/trunk/src/waeup/ikoba/customers/files.py	(revision 12034)
+++ main/waeup.ikoba/trunk/src/waeup/ikoba/customers/files.py	(revision 12035)
@@ -1,3 +1,3 @@
-## $Id: viewlets.py 11772 2014-07-31 04:38:23Z henrik $
+## $Id$
 ##
 ## Copyright (C) 2014 Uli Fouquet & Henrik Bettermann
@@ -31,8 +31,10 @@
     default_fileupload_template)
 
-from waeup.ikoba.customers.interfaces import ICustomer, ICustomersUtils
+from waeup.ikoba.customers.interfaces import (
+    ICustomer, ICustomersUtils, ICustomerDocument)
 from waeup.ikoba.customers.browser import (
     CustomerBaseDisplayFormPage, CustomerBaseManageFormPage,
-    CustomerFilesUploadPage)
+    CustomerFilesUploadPage,
+    DocumentDisplayFormPage, DocumentManageFormPage)
 
 grok.context(IIkobaObject)  # Make IIkobaObject the default context
@@ -254,2 +256,34 @@
 
 # File viewlets for customer documents
+
+class GenericUploadManage(FileUpload):
+    """Genric document upload viewlet for officers.
+    """
+    grok.order(1)
+    grok.context(ICustomerDocument)
+    grok.view(DocumentManageFormPage)
+    grok.require('waeup.manageCustomer')
+    label = _(u'Generic Document')
+    title = _(u'Generic Document')
+    mus = 1024 * 50
+    download_name = u'generic'
+    tab_redirect = '#tab2'
+
+class GenericDisplay(FileDisplay):
+    """Genreric document display viewlet.
+    """
+    grok.order(1)
+    grok.context(ICustomerDocument)
+    grok.require('waeup.viewCustomer')
+    grok.view(DocumentDisplayFormPage)
+    label = _(u'Generic Document')
+    title = _(u'Generic Document')
+    download_name = u'generic'
+
+class GenericImage(Image):
+    """Generic document.
+    """
+    grok.name('generic')
+    grok.context(ICustomerDocument)
+    grok.require('waeup.viewCustomer')
+    download_name = u'generic'
Index: main/waeup.ikoba/trunk/src/waeup/ikoba/customers/utils.py
===================================================================
--- main/waeup.ikoba/trunk/src/waeup/ikoba/customers/utils.py	(revision 12034)
+++ main/waeup.ikoba/trunk/src/waeup/ikoba/customers/utils.py	(revision 12035)
@@ -18,4 +18,6 @@
 """General helper functions and utilities for the customer section.
 """
+import re
+import os
 import grok
 from waeup.ikoba.interfaces import MessageFactory as _
@@ -23,4 +25,5 @@
 from waeup.ikoba.customers.interfaces import ICustomersUtils
 
+RE_CUSTID_NON_NUM = re.compile('[^\d]+')
 
 def generate_customer_id():
@@ -29,4 +32,40 @@
     return new_id
 
+def path_from_custid(customer_id):
+    """Convert a customer_id into a predictable relative folder path.
+
+    Used for storing files.
+
+    Returns the name of folder in which files for a particular customer
+    should be stored. This is a relative path, relative to any general
+    customers folder with 5 zero-padded digits (except when customer_id
+    is overlong).
+
+    We normally map 1,000 different customer ids into one single
+    path. For instance ``K1000000`` will give ``01000/K1000000``,
+    ``K1234567`` will give ``0123/K1234567`` and ``K12345678`` will
+    result in ``1234/K12345678``.
+
+    For lower numbers < 10**6 we return the same path for up to 10,000
+    customer_ids. So for instance ``KM123456`` will result in
+    ``00120/KM123456`` (there will be no path starting with
+    ``00123``).
+
+    Works also with overlong number: here the leading zeros will be
+    missing but ``K123456789`` will give reliably
+    ``12345/K123456789`` as expected.
+    """
+    # remove all non numeric characters and turn this into an int.
+    num = int(RE_CUSTID_NON_NUM.sub('', customer_id))
+    if num < 10**6:
+        # store max. of 10000 custs per folder and correct num for 5 digits
+        num = num / 10000 * 10
+    else:
+        # store max. of 1000 custs per folder
+        num = num / 1000
+    # format folder name to have 5 zero-padded digits
+    folder_name = u'%05d' % num
+    folder_name = os.path.join(folder_name, customer_id)
+    return folder_name
 
 class CustomersUtils(grok.GlobalUtility):
@@ -43,4 +82,6 @@
     DOCMANAGE_STATES = (APPROVED,)
 
+    SKIP_UPLOAD_VIEWLETS = ()
+
     TRANSLATED_STATES = {
         CREATED: _('created'),
