Index: main/waeup.ikoba/trunk/src/waeup/ikoba/customers/documents.py
===================================================================
--- main/waeup.ikoba/trunk/src/waeup/ikoba/customers/documents.py	(revision 12212)
+++ main/waeup.ikoba/trunk/src/waeup/ikoba/customers/documents.py	(revision 12213)
@@ -22,4 +22,5 @@
 import grok
 from hashlib import md5
+from hurry.workflow.interfaces import IWorkflowInfo, IWorkflowState
 from zope.component import queryUtility, getUtility
 from zope.component.interfaces import IFactory
@@ -76,4 +77,17 @@
     filenames = ()
 
+    @property
+    def state(self):
+        state = IWorkflowState(self).getState()
+        return state
+
+    @property
+    def translated_state(self):
+        try:
+            TRANSLATED_STATES = getUtility(
+                IDocumentsUtils).TRANSLATED_DOCUMENT_STATES
+            return TRANSLATED_STATES[self.state]
+        except KeyError:
+            return
     @property
     def customer(self):
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 12212)
+++ main/waeup.ikoba/trunk/src/waeup/ikoba/customers/tests/test_browser.py	(revision 12213)
@@ -50,5 +50,5 @@
 from waeup.ikoba.authentication import LocalRoleSetEvent
 from waeup.ikoba.tests.test_async import FunctionalAsyncTestCase
-from waeup.ikoba.documents.workflow import VERIFIED
+from waeup.ikoba.interfaces import VERIFIED
 from waeup.ikoba.browser.tests.test_pdf import samples_dir
 
Index: main/waeup.ikoba/trunk/src/waeup/ikoba/customers/workflow.py
===================================================================
--- main/waeup.ikoba/trunk/src/waeup/ikoba/customers/workflow.py	(revision 12212)
+++ main/waeup.ikoba/trunk/src/waeup/ikoba/customers/workflow.py	(revision 12213)
@@ -33,7 +33,6 @@
 from waeup.ikoba.customers.interfaces import (
     ICustomer, ICustomersUtils,
-    IContract)
+    IContract, ICustomerDocument)
 from waeup.ikoba.utils.helpers import get_current_principal
-from waeup.ikoba.documents.workflow import VERIFICATION_TRANSITIONS
 
 # Customer workflow
@@ -214,6 +213,6 @@
     )
 
-
 contract_workflow = IkobaWorkflow(CONTRACT_TRANSITIONS)
+
 
 class ContractWorkflowState(WorkflowState, grok.Adapter):
@@ -225,4 +224,5 @@
     state_key = 'wf.contract.state'
     state_id = 'wf.contract.id'
+
 
 class ContractWorkflowInfo(IkobaWorkflowInfo, grok.Adapter):
@@ -258,2 +258,118 @@
         pass
     return
+
+# Customer document workflow
+
+VERIFICATION_TRANSITIONS = (
+    Transition(
+        transition_id = 'create',
+        title = _('Create document'),
+        source = None,
+        condition = NullCondition,
+        msg = _('Document created'),
+        destination = CREATED),
+
+    Transition(
+        transition_id = 'submit',
+        title = _('Submit for verification'),
+        msg = _('Submitted for verification'),
+        source = CREATED,
+        destination = SUBMITTED),
+
+    Transition(
+        transition_id = 'verify',
+        title = _('Verify'),
+        msg = _('Verified'),
+        source = SUBMITTED,
+        destination = VERIFIED),
+
+    Transition(
+        transition_id = 'reject',
+        title = _('Reject'),
+        msg = _('REJECTED'),
+        source = SUBMITTED,
+        destination = REJECTED),
+
+    Transition(
+        transition_id = 'reset1',
+        title = _('Reset to initial state'),
+        msg = _('Reset to initial state'),
+        source = REJECTED,
+        destination = CREATED),
+
+    Transition(
+        transition_id = 'reset2',
+        title = _('Reset to initial state'),
+        msg = _('Reset to initial state'),
+        source = VERIFIED,
+        destination = CREATED),
+
+    Transition(
+        transition_id = 'reset3',
+        title = _('Reset to initial state'),
+        msg = _('Reset to initial state'),
+        source = SUBMITTED,
+        destination = CREATED),
+
+    Transition(
+        transition_id = 'expire',
+        title = _('Set to expired'),
+        msg = _('Set to expired'),
+        source = VERIFIED,
+        destination = EXPIRED),
+
+    Transition(
+        transition_id = 'reset4',
+        title = _('Reset to initial state'),
+        msg = _('Reset to initial state'),
+        source = EXPIRED,
+        destination = CREATED),
+    )
+
+verification_workflow = IkobaWorkflow(VERIFICATION_TRANSITIONS)
+
+
+class VerificationWorkflowState(WorkflowState, grok.Adapter):
+    """An adapter to adapt CustomerDocument objects to workflow states.
+    """
+    grok.context(ICustomerDocument)
+    grok.provides(IWorkflowState)
+
+    state_key = 'wf.verification.state'
+    state_id = 'wf.verification.id'
+
+
+class VerificationWorkflowInfo(IkobaWorkflowInfo, grok.Adapter):
+    """Adapter to adapt CustomerDocument objects to workflow info objects.
+    """
+    grok.context(ICustomerDocument)
+    grok.provides(IIkobaWorkflowInfo)
+
+    def __init__(self, context):
+        self.context = context
+        self.wf = verification_workflow
+
+@grok.subscribe(ICustomerDocument, IWorkflowTransitionEvent)
+def handle_customer_document_transition_event(obj, event):
+    """Append message to document history and log file.
+
+    Undo the verification of document and raise an exception if document
+    does not meet the requirements for verification.
+    """
+    if event.transition.destination == VERIFIED:
+        verifiable, error = obj.is_verifiable
+        if not verifiable:
+            # Undo transition and raise an exception.
+            IWorkflowState(obj).setState(event.transition.source)
+            raise InvalidTransitionError(error)
+    if event.transition.transition_id == 'verify':
+        obj.setMD5()
+    msg = event.transition.user_data['msg']
+    history = IObjectHistory(obj)
+    history.addMessage(msg)
+    try:
+        customers_container = grok.getSite()['customers']
+        customers_container.logger.info('%s - %s' % (obj.customer_id,msg))
+    except (TypeError, AttributeError):
+        pass
+    return
