Index: /main/waeup.ikoba/trunk/src/waeup/ikoba/products/__init__.py
===================================================================
--- /main/waeup.ikoba/trunk/src/waeup/ikoba/products/__init__.py	(revision 12064)
+++ /main/waeup.ikoba/trunk/src/waeup/ikoba/products/__init__.py	(revision 12064)
@@ -0,0 +1,10 @@
+"""This package contains everything regarding products.
+"""
+# Make this a package.
+from waeup.ikoba.products.container import ProductsContainer
+from waeup.ikoba.products.product import Product
+
+__all__ = [
+    'ProductsContainer',
+    'Product',
+    ]
Index: /main/waeup.ikoba/trunk/src/waeup/ikoba/products/catalog.py
===================================================================
--- /main/waeup.ikoba/trunk/src/waeup/ikoba/products/catalog.py	(revision 12064)
+++ /main/waeup.ikoba/trunk/src/waeup/ikoba/products/catalog.py	(revision 12064)
@@ -0,0 +1,31 @@
+## $Id$
+##
+## Copyright (C) 2011 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
+##
+"""Components to help cataloging and searching products.
+"""
+import grok
+from waeup.ikoba.interfaces import ICompany
+from waeup.ikoba.products.interfaces import IProduct
+
+class ProductIndexes(grok.Indexes):
+    """A catalog for all products.
+    """
+    grok.site(ICompany)
+    grok.name('products_catalog')
+    grok.context(IProduct)
+
+    product_id = grok.index.Field(attribute='product_id')
Index: /main/waeup.ikoba/trunk/src/waeup/ikoba/products/container.py
===================================================================
--- /main/waeup.ikoba/trunk/src/waeup/ikoba/products/container.py	(revision 12064)
+++ /main/waeup.ikoba/trunk/src/waeup/ikoba/products/container.py	(revision 12064)
@@ -0,0 +1,40 @@
+## $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
+##
+"""
+Containers which contain product objects.
+"""
+import grok
+from grok import index
+from waeup.ikoba.products.interfaces import IProductsContainer, IProduct
+from waeup.ikoba.utils.helpers import attrs_to_fields
+
+class ProductsContainer(grok.Container):
+    """This is a container for all kind of products.
+    """
+    grok.implements(IProductsContainer)
+    grok.provides(IProductsContainer)
+
+
+    def addProduct(self, product):
+        if not IProduct.providedBy(product):
+            raise TypeError(
+                'ProductsContainers contain only IProduct instances')
+        self[product.product_id] = product
+        return
+
+ProductsContainer = attrs_to_fields(ProductsContainer)
Index: /main/waeup.ikoba/trunk/src/waeup/ikoba/products/interfaces.py
===================================================================
--- /main/waeup.ikoba/trunk/src/waeup/ikoba/products/interfaces.py	(revision 12064)
+++ /main/waeup.ikoba/trunk/src/waeup/ikoba/products/interfaces.py	(revision 12064)
@@ -0,0 +1,49 @@
+## $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
+##
+from zope.interface import Attribute, Interface
+from zope import schema
+from waeup.ikoba.interfaces import (
+    IIkobaObject,
+    ContextualDictSourceFactoryBase)
+from waeup.ikoba.interfaces import MessageFactory as _
+
+
+class IProductsContainer(IIkobaObject):
+    """A container for all kind of product objects.
+
+    """
+
+    def addProduct(product):
+        """Add a product.
+        """
+
+class IProduct(IIkobaObject):
+    """A base representation of products.
+
+    """
+    product_id = schema.TextLine(
+        title = _(u'Product Id'),
+        default = u'NA',
+        required = True,
+        )
+
+    title = schema.TextLine(
+        title = _(u'Product Title'),
+        required = True,
+        default = u'Unnamed',
+        )
Index: /main/waeup.ikoba/trunk/src/waeup/ikoba/products/product.py
===================================================================
--- /main/waeup.ikoba/trunk/src/waeup/ikoba/products/product.py	(revision 12064)
+++ /main/waeup.ikoba/trunk/src/waeup/ikoba/products/product.py	(revision 12064)
@@ -0,0 +1,69 @@
+## $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
+##
+"""
+These are the products (e.g. licenses).
+"""
+import grok
+from time import time
+from grok import index
+from hurry.workflow.interfaces import IWorkflowInfo, IWorkflowState
+from zope.event import notify
+from zope.component import getUtility
+from zope.component.interfaces import IFactory
+from zope.interface import implementedBy
+from zope.i18n import translate
+from waeup.ikoba.interfaces import IIkobaUtils
+from waeup.ikoba.interfaces import MessageFactory as _
+from waeup.ikoba.utils.helpers import attrs_to_fields, get_current_principal
+from waeup.ikoba.utils.logger import Logger
+from waeup.ikoba.products.interfaces import (
+    IProduct, IProduct,)
+
+class Product(grok.Container, Logger):
+    """This is a product.
+    """
+    grok.implements(IProduct)
+    grok.provides(IProduct)
+    grok.baseclass()
+
+    logger_name = 'waeup.ikoba.${sitename}.products'
+    logger_filename = 'products.log'
+    logger_format_str = '"%(asctime)s","%(user)s",%(message)s'
+
+    def logger_info(self, comment=None):
+        """Get the logger's info method.
+        """
+        self.logger.info('%s' % comment)
+        return
+
+Product = attrs_to_fields(Product)
+
+
+class ProductFactory(grok.GlobalUtility):
+    """A factory for products.
+    """
+    grok.implements(IFactory)
+    grok.name(u'waeup.Product')
+    title = u"Create a new product.",
+    description = u"This factory instantiates new products."
+
+    def __call__(self, *args, **kw):
+        return Product(*args, **kw)
+
+    def getInterfaces(self):
+        return implementedBy(Product)
Index: /main/waeup.ikoba/trunk/src/waeup/ikoba/products/tests.py
===================================================================
--- /main/waeup.ikoba/trunk/src/waeup/ikoba/products/tests.py	(revision 12064)
+++ /main/waeup.ikoba/trunk/src/waeup/ikoba/products/tests.py	(revision 12064)
@@ -0,0 +1,63 @@
+## $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
+##
+"""
+Tests for products.
+"""
+from zope.interface.verify import verifyClass, verifyObject
+from zope.component import createObject
+from hurry.workflow.interfaces import (
+    IWorkflowInfo, IWorkflowState, InvalidTransitionError)
+from waeup.ikoba.products.interfaces import (
+    IProductsContainer, IProduct)
+from waeup.ikoba.interfaces import IObjectHistory
+from waeup.ikoba.products.container import ProductsContainer
+from waeup.ikoba.products.product import Product
+from waeup.ikoba.testing import (FunctionalLayer, FunctionalTestCase)
+
+class ProductsContainerTestCase(FunctionalTestCase):
+
+    layer = FunctionalLayer
+
+    def test_interfaces(self):
+        # Make sure the correct interfaces are implemented.
+        self.assertTrue(
+            verifyClass(
+                IProductsContainer, ProductsContainer)
+            )
+        self.assertTrue(
+            verifyObject(
+                IProductsContainer, ProductsContainer())
+            )
+        self.assertTrue(
+            verifyClass(
+                IProduct, Product)
+            )
+        self.assertTrue(
+            verifyObject(
+                IProduct, Product())
+            )
+        return
+
+    def test_addProduct(self):
+        container = ProductsContainer()
+        product = createObject(u'waeup.Product')
+        container.addProduct(product)
+        self.assertRaises(TypeError, container.addProduct, object())
+        self.assertEqual(product.product_id, 'NA')
+        self.assertEqual(container['NA'], product)
+        return
