Index: main/waeup.ikoba/trunk/src/waeup/ikoba/app.py
===================================================================
--- main/waeup.ikoba/trunk/src/waeup/ikoba/app.py	(revision 12004)
+++ main/waeup.ikoba/trunk/src/waeup/ikoba/app.py	(revision 12005)
@@ -39,4 +39,6 @@
     grok.implements(ICompany)
 
+    _curr_doc_id = 101
+
     # Setup authentication for this app. Note: this is only
     # initialized, when a new instance of this app is created.
@@ -49,4 +51,25 @@
         self.setup()
         return
+
+    @property
+    def unique_document_id(self):
+        """A unique document id for all documents in company.
+
+        The document id returned is guaranteed to be unique. It
+        consists of some prefix (normally a single letter) followed by
+        a number with at least 7 digits.
+
+        Once a document id was issued, it won't be issued again.
+
+        Obtaining a document id is currently not thread-safe but can be
+        made easily by enabling commented lines.
+        """
+        # lock.acquire() # lock data
+        new_id = u'd%s' % (self._curr_doc_id)
+        self._curr_doc_id += 1
+        # self._p_changed = True
+        # commit()
+        # lock.release() # end of lock
+        return new_id
 
     def setup(self):
Index: main/waeup.ikoba/trunk/src/waeup/ikoba/customers/batching.py
===================================================================
--- main/waeup.ikoba/trunk/src/waeup/ikoba/customers/batching.py	(revision 12004)
+++ main/waeup.ikoba/trunk/src/waeup/ikoba/customers/batching.py	(revision 12005)
@@ -40,4 +40,5 @@
 from waeup.ikoba.interfaces import IIkobaUtils
 from waeup.ikoba.interfaces import MessageFactory as _
+from waeup.ikoba.documents.utils import generate_document_id
 from waeup.ikoba.customers.interfaces import (
     ICustomer, ICustomerUpdateByRegNo,
@@ -440,9 +441,15 @@
         document_id = row.get('document_id', None)
         if not document_id:
-            timestamp = ("%d" % int(time()*10000))[1:]
-            document_id = "d%s" % timestamp
+            document_id = generate_document_id()
             conv_dict['document_id'] = document_id
             return errs, inv_errs, conv_dict
-        if not document_id.startswith('d') or len(document_id) != 14:
+        # document_id must not exist.
+        if mode == 'create':
+            cat = queryUtility(ICatalog, name='documents_catalog')
+            results = list(
+                cat.searchResults(document_id=(document_id, document_id)))
+            if results:
+                errs.append(('document_id','id exists'))
+        if not document_id.startswith('d'):
             errs.append(('document_id','invalid format'))
         return errs, inv_errs, conv_dict
Index: main/waeup.ikoba/trunk/src/waeup/ikoba/customers/customer.py
===================================================================
--- main/waeup.ikoba/trunk/src/waeup/ikoba/customers/customer.py	(revision 12004)
+++ main/waeup.ikoba/trunk/src/waeup/ikoba/customers/customer.py	(revision 12005)
@@ -1,3 +1,3 @@
-## $Id: customer.py 11604 2014-04-29 07:31:54Z henrik $
+## $Id$
 ##
 ## Copyright (C) 2014 Uli Fouquet & Henrik Bettermann
Index: main/waeup.ikoba/trunk/src/waeup/ikoba/customers/tests/sample_document_data.csv
===================================================================
--- main/waeup.ikoba/trunk/src/waeup/ikoba/customers/tests/sample_document_data.csv	(revision 12004)
+++ main/waeup.ikoba/trunk/src/waeup/ikoba/customers/tests/sample_document_data.csv	(revision 12005)
@@ -1,5 +1,6 @@
 document_id,reg_number,title
-d1266236341953,1,My first doc
-d1266236341954,2,My second doc
-d1266236341955,3,My third doc
+d3,1,My first doc
+d4,2,My second doc
+d5,3,My third doc
 ,1,My 4th doc
+d5,2,My stolen doc
Index: main/waeup.ikoba/trunk/src/waeup/ikoba/customers/tests/test_batching.py
===================================================================
--- main/waeup.ikoba/trunk/src/waeup/ikoba/customers/tests/test_batching.py	(revision 12004)
+++ main/waeup.ikoba/trunk/src/waeup/ikoba/customers/tests/test_batching.py	(revision 12005)
@@ -328,13 +328,18 @@
         num, num_warns, fin_file, fail_file = self.processor.doImport(
             self.csv_file, DOCUMENT_HEADER_FIELDS,'create')
-        self.assertEqual(num_warns,0)
+        self.assertEqual(num_warns,1)
+        # document_id must be unique
+        fail_file = open(fail_file).read()
+        self.assertEqual(fail_file,
+            'reg_number,document_id,title,--ERRORS--\r\n'
+            '2,d5,My stolen doc,document_id: id exists\r\n')
         document = self.processor.getEntry(dict(reg_number='1',
-            document_id='d1266236341953'), self.app)
+            document_id='d3'), self.app)
         self.assertEqual(
-            self.app['customers']['X666666']['documents']['d1266236341953'],
+            self.app['customers']['X666666']['documents']['d3'],
             document)
-        self.assertEqual(document.document_id, 'd1266236341953')
+        self.assertEqual(document.document_id, 'd3')
         document = self.processor.getEntry(dict(reg_number='3',
-            document_id='d1266236341955'), self.app)
+            document_id='d5'), self.app)
         shutil.rmtree(os.path.dirname(fin_file))
         logcontent = open(self.logfile).read()
@@ -343,5 +348,5 @@
             'INFO - system - CustomerDocument Processor - '
             'sample_document_data - X666666 - updated: '
-            'document_id=d1266236341953, title=My first doc'
+            'document_id=d3, title=My first doc'
             in logcontent)
 
@@ -355,5 +360,6 @@
         num, num_warns, fin_file, fail_file = self.processor.doImport(
             self.csv_file, DOCUMENT_HEADER_FIELDS,'update')
-        self.assertEqual(num_warns,1)  # There is one record without document_id
+        # There is one record without document_id and one duplicate
+        self.assertEqual(num_warns,2)
         shutil.rmtree(os.path.dirname(fin_file))
 
@@ -367,8 +373,9 @@
         num, num_warns, fin_file, fail_file = self.processor.doImport(
             self.csv_file, DOCUMENT_HEADER_FIELDS,'remove')
-        self.assertEqual(num_warns,1)  # There is one record without document_id
+        # There is one record without document_id and one duplicate
+        self.assertEqual(num_warns,2)
         shutil.rmtree(os.path.dirname(fin_file))
         logcontent = open(self.logfile).read()
         self.assertTrue(
-            'INFO - system - K1000001 - Document removed: d1266236341955'
+            'INFO - system - K1000001 - Document removed: d5'
             in logcontent)
Index: main/waeup.ikoba/trunk/src/waeup/ikoba/documents/container.py
===================================================================
--- main/waeup.ikoba/trunk/src/waeup/ikoba/documents/container.py	(revision 12004)
+++ main/waeup.ikoba/trunk/src/waeup/ikoba/documents/container.py	(revision 12005)
@@ -30,7 +30,4 @@
     grok.provides(IDocumentsContainer)
 
-    def __init__(self):
-        super(DocumentsContainer, self).__init__()
-        return
 
     def addDocument(self, document):
Index: main/waeup.ikoba/trunk/src/waeup/ikoba/documents/document.py
===================================================================
--- main/waeup.ikoba/trunk/src/waeup/ikoba/documents/document.py	(revision 12004)
+++ main/waeup.ikoba/trunk/src/waeup/ikoba/documents/document.py	(revision 12005)
@@ -30,7 +30,8 @@
 from waeup.ikoba.interfaces import IIkobaUtils, IObjectHistory
 from waeup.ikoba.interfaces import MessageFactory as _
-from waeup.ikoba.documents.interfaces import IDocument, IDocument
 from waeup.ikoba.utils.helpers import attrs_to_fields, get_current_principal
 from waeup.ikoba.utils.logger import Logger
+from waeup.ikoba.documents.interfaces import IDocument, IDocument
+from waeup.ikoba.documents.utils import generate_document_id
 
 class Document(grok.Container, Logger):
@@ -51,8 +52,17 @@
         return
 
+    #def __init__(self):
+    #    super(Document, self).__init__()
+    #    timestamp = ("%d" % int(time()*10000))[1:]
+    #    self.document_id = "d%s" % timestamp
+     #   return
+
     def __init__(self):
         super(Document, self).__init__()
-        timestamp = ("%d" % int(time()*10000))[1:]
-        self.document_id = "d%s" % timestamp
+        # The site doesn't exist in unit tests
+        try:
+            self.document_id = generate_document_id()
+        except AttributeError:
+            self.document_id = u'd123'
         return
 
Index: main/waeup.ikoba/trunk/src/waeup/ikoba/documents/interfaces.py
===================================================================
--- main/waeup.ikoba/trunk/src/waeup/ikoba/documents/interfaces.py	(revision 12004)
+++ main/waeup.ikoba/trunk/src/waeup/ikoba/documents/interfaces.py	(revision 12005)
@@ -35,4 +35,8 @@
     """
 
+    def addDocument(document):
+        """Add a document.
+        """
+
 class IDocument(IIkobaObject):
     """A base representation of documents.
Index: main/waeup.ikoba/trunk/src/waeup/ikoba/documents/tests.py
===================================================================
--- main/waeup.ikoba/trunk/src/waeup/ikoba/documents/tests.py	(revision 12004)
+++ main/waeup.ikoba/trunk/src/waeup/ikoba/documents/tests.py	(revision 12005)
@@ -61,4 +61,5 @@
         self.assertEqual(container[document_id], document)
         self.assertRaises(TypeError, container.addDocument, object())
+        self.assertEqual(document_id, 'd123')
         return
 
Index: main/waeup.ikoba/trunk/src/waeup/ikoba/documents/utils.py
===================================================================
--- main/waeup.ikoba/trunk/src/waeup/ikoba/documents/utils.py	(revision 12005)
+++ main/waeup.ikoba/trunk/src/waeup/ikoba/documents/utils.py	(revision 12005)
@@ -0,0 +1,25 @@
+## $Id$
+##
+## 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
+##
+"""General helper functions and utilities for the documents.
+"""
+import grok
+
+
+def generate_document_id():
+    new_id = grok.getSite().unique_document_id
+    return new_id
