Index: /main/waeup.ikoba/trunk/src/waeup/ikoba/customers/documents.py
===================================================================
--- /main/waeup.ikoba/trunk/src/waeup/ikoba/customers/documents.py	(revision 11989)
+++ /main/waeup.ikoba/trunk/src/waeup/ikoba/customers/documents.py	(revision 11989)
@@ -0,0 +1,85 @@
+## $Id: documents.py 11583 2014-04-04 08:44:59Z henrik $
+##
+## Copyright (C) 2014 Uli Fouquet & Henrik Bettermann
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, write to the Free Software
+## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+##
+"""
+Customer document components.
+"""
+import grok
+from zope.component.interfaces import IFactory
+from zope.interface import implementedBy
+from waeup.ikoba.interfaces import MessageFactory as _
+from waeup.ikoba.customers.interfaces import (
+    ICustomerDocumentsContainer, ICustomerNavigation, ICustomerDocument)
+from waeup.ikoba.documents import DocumentsContainer, Document
+from waeup.ikoba.utils.helpers import attrs_to_fields
+
+class CustomerDocumentsContainer(DocumentsContainer):
+    """This is a container for customer documents.
+    """
+    grok.implements(ICustomerDocumentsContainer, ICustomerNavigation)
+    grok.provides(ICustomerDocumentsContainer)
+
+    def __init__(self):
+        super(CustomerDocumentsContainer, self).__init__()
+        return
+
+    @property
+    def customer(self):
+        return self.__parent__
+
+    def writeLogMessage(self, view, message):
+        return self.__parent__.writeLogMessage(view, message)
+
+CustomerDocumentsContainer = attrs_to_fields(CustomerDocumentsContainer)
+
+class CustomerDocument(Document):
+    """This is an document.
+    """
+    grok.implements(ICustomerDocument, ICustomerNavigation)
+    grok.provides(ICustomerDocument)
+
+    def __init__(self):
+        super(CustomerDocument, self).__init__()
+        return
+
+    @property
+    def customer(self):
+        try:
+            return self.__parent__.__parent__
+        except AttributeError:
+            return None
+
+    def writeLogMessage(self, view, message):
+        return self.__parent__.__parent__.writeLogMessage(view, message)
+
+CustomerDocument = attrs_to_fields(CustomerDocument)
+
+
+# Customer documents must be importable. So we might need a factory.
+class CustomerDocumentFactory(grok.GlobalUtility):
+    """A factory for customer documents.
+    """
+    grok.implements(IFactory)
+    grok.name(u'waeup.CustomerDocument')
+    title = u"Create a new document.",
+    description = u"This factory instantiates new document instances."
+
+    def __call__(self, *args, **kw):
+        return CustomerDocument()
+
+    def getInterfaces(self):
+        return implementedBy(CustomerDocument)
Index: /main/waeup.ikoba/trunk/src/waeup/ikoba/customers/interfaces.py
===================================================================
--- /main/waeup.ikoba/trunk/src/waeup/ikoba/customers/interfaces.py	(revision 11988)
+++ /main/waeup.ikoba/trunk/src/waeup/ikoba/customers/interfaces.py	(revision 11989)
@@ -26,4 +26,5 @@
 from waeup.ikoba.schema import TextLineChoice, FormattedDate, PhoneNumber
 from waeup.ikoba.browser.interfaces import ICustomerNavigationBase
+from waeup.ikoba.documents.interfaces import IDocumentsContainer, IDocument
 
 from waeup.ikoba.customers.vocabularies import (
@@ -225,2 +226,17 @@
         constraint=validate_email,
         )
+
+
+# Customer document interfaces
+
+class ICustomerDocumentsContainer(IDocumentsContainer):
+    """A container for customer document objects.
+
+    """
+
+
+class ICustomerDocument(IDocument):
+    """A customer document.
+
+    """
+
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 11989)
+++ /main/waeup.ikoba/trunk/src/waeup/ikoba/customers/tests/test_document.py	(revision 11989)
@@ -0,0 +1,87 @@
+## $Id: tests.py 9469 2012-10-30 17:49:17Z henrik $
+##
+## Copyright (C) 2014 Uli Fouquet & Henrik Bettermann
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, write to the Free Software
+## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+##
+"""
+Tests for documents.
+"""
+from zope.interface.verify import verifyClass, verifyObject
+from zope.component import createObject
+from hurry.workflow.interfaces import (
+    IWorkflowInfo, IWorkflowState, InvalidTransitionError)
+from waeup.ikoba.customers.interfaces import (
+    ICustomerDocumentsContainer, ICustomerDocument)
+from waeup.ikoba.interfaces import IObjectHistory
+from waeup.ikoba.customers.documents import (
+    CustomerDocumentsContainer, CustomerDocument)
+from waeup.ikoba.testing import (FunctionalLayer, FunctionalTestCase)
+
+class CustomerDocumentsContainerTestCase(FunctionalTestCase):
+
+    layer = FunctionalLayer
+
+    def test_interfaces(self):
+        # Make sure the correct interfaces are implemented.
+        self.assertTrue(
+            verifyClass(
+                ICustomerDocumentsContainer, CustomerDocumentsContainer)
+            )
+        self.assertTrue(
+            verifyObject(
+                ICustomerDocumentsContainer, CustomerDocumentsContainer())
+            )
+        self.assertTrue(
+            verifyClass(
+                ICustomerDocument, CustomerDocument)
+            )
+        self.assertTrue(
+            verifyObject(
+                ICustomerDocument, CustomerDocument())
+            )
+        return
+
+    def test_addDocument(self):
+        container = CustomerDocumentsContainer()
+        document = createObject(u'waeup.CustomerDocument')
+        document.id = 'my_document'
+        container.addDocument(document)
+        self.assertEqual(container['my_document'], document)
+        self.assertRaises(TypeError, container.addDocument, object())
+        return
+
+    def test_document_workflow(self):
+        document = createObject(u'waeup.CustomerDocument')
+        IWorkflowInfo(document).fireTransition('create')
+        self.assertEqual(IWorkflowState(document).getState(), 'created')
+        IWorkflowInfo(document).fireTransition('submit')
+        self.assertEqual(IWorkflowState(document).getState(), 'submitted')
+        IWorkflowInfo(document).fireTransition('verify')
+        self.assertEqual(IWorkflowState(document).getState(), 'verified')
+        IWorkflowInfo(document).fireTransition('reset2')
+        self.assertEqual(IWorkflowState(document).getState(), 'created')
+        self.assertRaises(InvalidTransitionError,
+            IWorkflowInfo(document).fireTransition, 'verify')
+        IWorkflowInfo(document).fireTransition('submit')
+        IWorkflowInfo(document).fireTransition('reset3')
+        self.assertEqual(IWorkflowState(document).getState(), 'created')
+        return
+
+    def test_document_history(self):
+        doc = createObject(u'waeup.CustomerDocument')
+        IWorkflowInfo(doc).fireTransition('create')
+        messages = ' '.join(doc.history.messages)
+        self.assertTrue('Document created by system' in messages)
+        return
