From c46bc7d83630579dda56eedc31f9acdd62467a4f Mon Sep 17 00:00:00 2001
From: Yoshinori Okuji <yo@nexedi.com>
Date: Thu, 23 Feb 2006 21:29:46 +0000
Subject: [PATCH] Initial revision

git-svn-id: https://svn.erp5.org/repos/public/erp5/trunk@5828 20353a03-c40f-0410-a6d1-a30d3c3de9de
---
 product/ERP5Banking/.cvsignore                |   1 +
 product/ERP5Banking/BaobabMixin.py            | 133 +++
 product/ERP5Banking/Constraint/.cvsignore     |   1 +
 product/ERP5Banking/Constraint/__init__.py    |   0
 product/ERP5Banking/Document/.cvsignore       |   1 +
 .../ERP5Banking/Document/BankingOperation.py  | 155 +++
 .../Document/BankingOperationLine.py          |  83 ++
 product/ERP5Banking/Document/CashContainer.py |  66 ++
 product/ERP5Banking/Document/CashCurrency.py  | 114 ++
 product/ERP5Banking/Document/CashDelivery.py  |  62 ++
 .../ERP5Banking/Document/CashDeliveryCell.py  | 126 +++
 .../ERP5Banking/Document/CashDeliveryLine.py  |  95 ++
 product/ERP5Banking/Document/__init__.py      |   0
 product/ERP5Banking/Extensions/.cvsignore     |   1 +
 product/ERP5Banking/Extensions/__init__.py    |   0
 product/ERP5Banking/Interface/.cvsignore      |   1 +
 product/ERP5Banking/Interface/__init__.py     |   0
 product/ERP5Banking/Permissions.py            |   0
 product/ERP5Banking/PropertySheet/.cvsignore  |   1 +
 .../PropertySheet/BankingOperation.py         |  47 +
 .../PropertySheet/BaobabCategory.py           |  48 +
 .../PropertySheet/CashContainer.py            |  48 +
 .../ERP5Banking/PropertySheet/CashCurrency.py |  40 +
 .../PropertySheet/CashDeliveryLine.py         |  40 +
 product/ERP5Banking/PropertySheet/__init__.py |   0
 product/ERP5Banking/README.txt                |   4 +
 product/ERP5Banking/Tool/.cvsignore           |   1 +
 product/ERP5Banking/Tool/__init__.py          |   0
 product/ERP5Banking/__init__.py               |  48 +
 product/ERP5Banking/dtml/.cvsignore           |   1 +
 product/ERP5Banking/dtml/__init__.py          |   0
 product/ERP5Banking/help/.cvsignore           |   1 +
 product/ERP5Banking/help/__init__.py          |   0
 product/ERP5Banking/skins/.cvsignore          |   1 +
 product/ERP5Banking/skins/__init__.py         |   0
 product/ERP5Banking/tests/.cvsignore          |   1 +
 product/ERP5Banking/tests/__init__.py         |   0
 .../testERP5BankingCashClassification.py      | 974 ++++++++++++++++++
 .../tests/testERP5BankingCashInventory.py     | 804 +++++++++++++++
 .../tests/testERP5BankingCashTransfer.py      | 964 +++++++++++++++++
 .../tests/testERP5BankingCheckPayment.py      | 717 +++++++++++++
 41 files changed, 4579 insertions(+)
 create mode 100755 product/ERP5Banking/.cvsignore
 create mode 100755 product/ERP5Banking/BaobabMixin.py
 create mode 100755 product/ERP5Banking/Constraint/.cvsignore
 create mode 100755 product/ERP5Banking/Constraint/__init__.py
 create mode 100755 product/ERP5Banking/Document/.cvsignore
 create mode 100755 product/ERP5Banking/Document/BankingOperation.py
 create mode 100755 product/ERP5Banking/Document/BankingOperationLine.py
 create mode 100755 product/ERP5Banking/Document/CashContainer.py
 create mode 100755 product/ERP5Banking/Document/CashCurrency.py
 create mode 100755 product/ERP5Banking/Document/CashDelivery.py
 create mode 100755 product/ERP5Banking/Document/CashDeliveryCell.py
 create mode 100755 product/ERP5Banking/Document/CashDeliveryLine.py
 create mode 100755 product/ERP5Banking/Document/__init__.py
 create mode 100755 product/ERP5Banking/Extensions/.cvsignore
 create mode 100755 product/ERP5Banking/Extensions/__init__.py
 create mode 100755 product/ERP5Banking/Interface/.cvsignore
 create mode 100755 product/ERP5Banking/Interface/__init__.py
 create mode 100755 product/ERP5Banking/Permissions.py
 create mode 100755 product/ERP5Banking/PropertySheet/.cvsignore
 create mode 100755 product/ERP5Banking/PropertySheet/BankingOperation.py
 create mode 100755 product/ERP5Banking/PropertySheet/BaobabCategory.py
 create mode 100755 product/ERP5Banking/PropertySheet/CashContainer.py
 create mode 100755 product/ERP5Banking/PropertySheet/CashCurrency.py
 create mode 100755 product/ERP5Banking/PropertySheet/CashDeliveryLine.py
 create mode 100755 product/ERP5Banking/PropertySheet/__init__.py
 create mode 100755 product/ERP5Banking/README.txt
 create mode 100755 product/ERP5Banking/Tool/.cvsignore
 create mode 100755 product/ERP5Banking/Tool/__init__.py
 create mode 100755 product/ERP5Banking/__init__.py
 create mode 100755 product/ERP5Banking/dtml/.cvsignore
 create mode 100755 product/ERP5Banking/dtml/__init__.py
 create mode 100755 product/ERP5Banking/help/.cvsignore
 create mode 100755 product/ERP5Banking/help/__init__.py
 create mode 100755 product/ERP5Banking/skins/.cvsignore
 create mode 100755 product/ERP5Banking/skins/__init__.py
 create mode 100755 product/ERP5Banking/tests/.cvsignore
 create mode 100755 product/ERP5Banking/tests/__init__.py
 create mode 100755 product/ERP5Banking/tests/testERP5BankingCashClassification.py
 create mode 100755 product/ERP5Banking/tests/testERP5BankingCashInventory.py
 create mode 100755 product/ERP5Banking/tests/testERP5BankingCashTransfer.py
 create mode 100755 product/ERP5Banking/tests/testERP5BankingCheckPayment.py

diff --git a/product/ERP5Banking/.cvsignore b/product/ERP5Banking/.cvsignore
new file mode 100755
index 0000000000..0d20b6487c
--- /dev/null
+++ b/product/ERP5Banking/.cvsignore
@@ -0,0 +1 @@
+*.pyc
diff --git a/product/ERP5Banking/BaobabMixin.py b/product/ERP5Banking/BaobabMixin.py
new file mode 100755
index 0000000000..4bfdea0be9
--- /dev/null
+++ b/product/ERP5Banking/BaobabMixin.py
@@ -0,0 +1,133 @@
+##############################################################################
+#
+# Copyright (c) 2006 Nexedi SARL and Contributors. All Rights Reserved.
+#                    Yoshinori Okuji <yo@nexedi.com>
+#
+# WARNING: This program as such is intended to be used by professional
+# programmers who take the whole responsability of assessing all potential
+# consequences resulting from its eventual inadequacies and bugs
+# End users who are looking for a ready-to-use solution with commercial
+# garantees and support are strongly adviced to contract a Free Software
+# Service Company
+#
+# 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.
+#
+##############################################################################
+
+import ExtensionClass
+from AccessControl import ClassSecurityInfo
+from Products.ERP5Type import Permissions
+from Products.ERP5Type.Accessor.Base import Method, func_code
+from Products.ERP5Type.Utils import convertToMixedCase, convertToUpperCase
+from Globals import InitializeClass
+
+class BaobabGetter(Method):
+  """Get a category differently
+  """
+  _need__name__ = 1
+
+  # Generic Definition of Method Object
+  # This is required to call the method form the Web
+  func_code = func_code()
+  func_code.co_varnames = ('self', )
+  func_code.co_argcount = 1
+  func_defaults = ()
+
+  def __init__(self, id, method_id):
+    self._id = id
+    self.__name__ = id
+    self._method_id = method_id
+
+  def __call__(self, instance, *args, **kw):
+    portal_type = instance.getPortalType()
+    skin_id = '%s_%s' % (portal_type.replace(' ', ''), self._id)
+    skin = getattr(instance, skin_id, None)
+    if skin is not None:
+      return skin(*args, **kw)
+    return getattr(instance, self._method_id)(*args, **kw)
+
+class BaobabValueGetter(Method):
+  """Get the value of a baobab category
+  """
+  _need__name__ = 1
+
+  # Generic Definition of Method Object
+  # This is required to call the method form the Web
+  func_code = func_code()
+  func_code.co_varnames = ('self', )
+  func_code.co_argcount = 1
+  func_defaults = ()
+
+  def __init__(self, id, method_id):
+    self._id = id
+    self.__name__ = id
+    self._method_id = method_id
+
+  def __call__(self, instance, *args, **kw):
+    category = getattr(instance, self._method_id)(*args, **kw)
+    if category is not None:
+      return instance.portal_categories.resolveCategory(category)
+
+class BaobabPropertyGetter(Method):
+  """Get the property of a baobab category
+  """
+  _need__name__ = 1
+
+  # Generic Definition of Method Object
+  # This is required to call the method form the Web
+  func_code = func_code()
+  func_code.co_varnames = ('self', )
+  func_code.co_argcount = 1
+  func_defaults = ()
+
+  def __init__(self, id, method_id, property_id):
+    self._id = id
+    self.__name__ = id
+    self._method_id = method_id
+    self._property_id = property_id
+
+  def __call__(self, instance, *args, **kw):
+    value = getattr(instance, self._method_id)(*args, **kw)
+    if value is not None:
+      return value.getProperty(self._property_id, *args, **kw)
+
+class BaobabMixin(ExtensionClass.Base):
+  """This class provides different ways to access source, destination, etc.
+  """
+  security = ClassSecurityInfo()
+
+for category in ('source', 'destination',
+                 'source_section', 'destination_section',
+                 'source_payment', 'destination_payment',
+                 'source_function', 'destination_function',
+                 'source_project', 'destination_project'):
+  getter_id = 'getBaobab%s' % (convertToUpperCase(category))
+  original_getter_id = 'get%s' % (convertToUpperCase(category))
+  method = BaobabGetter(getter_id, original_getter_id)
+  setattr(BaobabMixin, getter_id, method)
+  BaobabMixin.security.declareProtected(Permissions.View, getter_id)
+
+  value_getter_id = getter_id + 'Value'
+  method = BaobabValueGetter(value_getter_id, getter_id)
+  setattr(BaobabMixin, value_getter_id, method)
+  BaobabMixin.security.declareProtected(Permissions.View, value_getter_id)
+
+  for prop in ('uid', 'title', 'id'):
+    prop_getter_id = getter_id + convertToUpperCase(prop)
+    method = BaobabPropertyGetter(prop_getter_id, value_getter_id, prop)
+    setattr(BaobabMixin, prop_getter_id, method)
+    BaobabMixin.security.declareProtected(Permissions.View, prop_getter_id)
+
+InitializeClass(BaobabMixin)
\ No newline at end of file
diff --git a/product/ERP5Banking/Constraint/.cvsignore b/product/ERP5Banking/Constraint/.cvsignore
new file mode 100755
index 0000000000..0d20b6487c
--- /dev/null
+++ b/product/ERP5Banking/Constraint/.cvsignore
@@ -0,0 +1 @@
+*.pyc
diff --git a/product/ERP5Banking/Constraint/__init__.py b/product/ERP5Banking/Constraint/__init__.py
new file mode 100755
index 0000000000..e69de29bb2
diff --git a/product/ERP5Banking/Document/.cvsignore b/product/ERP5Banking/Document/.cvsignore
new file mode 100755
index 0000000000..0d20b6487c
--- /dev/null
+++ b/product/ERP5Banking/Document/.cvsignore
@@ -0,0 +1 @@
+*.pyc
diff --git a/product/ERP5Banking/Document/BankingOperation.py b/product/ERP5Banking/Document/BankingOperation.py
new file mode 100755
index 0000000000..28523affc0
--- /dev/null
+++ b/product/ERP5Banking/Document/BankingOperation.py
@@ -0,0 +1,155 @@
+##############################################################################
+#
+# Copyright (c) 2005 Nexedi SARL and Contributors. All Rights Reserved.
+#
+# WARNING: This program as such is intended to be used by professional
+# programmers who take the whole responsability of assessing all potential
+# consequences resulting from its eventual inadequacies and bugs
+# End users who are looking for a ready-to-use solution with commercial
+# garantees and support are strongly adviced to contract a Free Software
+# Service Company
+#
+# 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 proopgram 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 AccessControl import ClassSecurityInfo
+from Products.CMFCore.utils import getToolByName
+from Products.CMFCore.WorkflowCore import WorkflowMethod
+from Products.ERP5Type import Permissions, PropertySheet, Constraint, Interface
+from Products.ERP5.Document.Delivery import Delivery
+from Products.ERP5Type.Document.DeliveryCell import DeliveryCell
+from Products.ERP5.Document.Movement import Movement
+from Products.ERP5.Document.AccountingTransaction import AccountingTransaction
+from AccessControl.PermissionRole import PermissionRole
+from Products.ERP5Type.Utils import convertToMixedCase, convertToUpperCase
+from Products.ERP5Banking.BaobabMixin import BaobabMixin
+
+class BankingOperation(BaobabMixin, AccountingTransaction):
+
+  # CMF Type Definition
+  meta_type = 'ERP5Banking Banking Operation'
+  portal_type = 'Banking Operation'
+  isPortalContent = 1
+  isRADContent = 1
+
+  # Declarative security
+  security = ClassSecurityInfo()
+  security.declareObjectProtected(Permissions.View)
+
+  # Default Properties
+  property_sheets = ( PropertySheet.Base
+                    , PropertySheet.XMLObject
+                    , PropertySheet.CategoryCore
+                    , PropertySheet.DublinCore
+                    , PropertySheet.Task
+                    , PropertySheet.Arrow
+                    , PropertySheet.BankingOperation
+                    , PropertySheet.ItemAggregation
+                    , PropertySheet.Amount
+                    )
+
+### Dynamic patch
+Delivery.getBaobabSourceUid = lambda x: x.getSourceUid()
+Delivery.getBaobabSourceUid__roles__ = PermissionRole(Permissions.View)
+
+Delivery.getBaobabDestinationUid = lambda x: x.getDestinationUid()
+Delivery.getBaobabDestinationUid__roles__ = PermissionRole(Permissions.View)
+
+Delivery.getBaobabSourceSectionUid = lambda x: x.getSourceSectionUid()
+Delivery.getBaobabSourceSectionUid__roles__ = PermissionRole(Permissions.View)
+
+Delivery.getBaobabDestinationSectionUid = lambda x: x.getDestinationSectionUid()
+Delivery.getBaobabDestinationSectionUid__roles__ = PermissionRole(Permissions.View)
+
+Delivery.getBaobabSourcePaymentUid = lambda x: x.getSourcePaymentUid()
+Delivery.getBaobabSourcePaymentUid__roles__ = PermissionRole(Permissions.View)
+
+Delivery.getBaobabDestinationPaymentUid = lambda x: x.getDestinationPaymentUid()
+Delivery.getBaobabDestinationPaymentUid__roles__ = PermissionRole(Permissions.View)
+
+Delivery.getBaobabSourceFunctionUid = lambda x: x.getSourceFunctionUid()
+Delivery.getBaobabSourceFunctionUid__roles__ = PermissionRole(Permissions.View)
+
+Delivery.getBaobabDestinationFunctionUid = lambda x: x.getDestinationFunctionUid()
+Delivery.getBaobabDestinationFunctionUid__roles__ = PermissionRole(Permissions.View)
+
+Delivery.getBaobabSourceProjectUid = lambda x: x.getSourceProjectUid()
+Delivery.getBaobabSourceProjectUid__roles__ = PermissionRole(Permissions.View)
+
+Delivery.getBaobabDestinationProjectUid = lambda x: x.getDestinationProjectUid()
+Delivery.getBaobabDestinationProjectUid__roles__ = PermissionRole(Permissions.View)
+
+### Overload Movement
+Movement.getBaobabSourceUid = lambda x: x.getSourceUid()
+Movement.getBaobabSourceUid__roles__ = PermissionRole(Permissions.View)
+
+Movement.getBaobabDestinationUid = lambda x: x.getDestinationUid()
+Movement.getBaobabDestinationUid__roles__ = PermissionRole(Permissions.View)
+
+Movement.getBaobabSourceSectionUid = lambda x: x.getSourceSectionUid()
+Movement.getBaobabSourceSectionUid__roles__ = PermissionRole(Permissions.View)
+
+Movement.getBaobabDestinationSectionUid = lambda x: x.getDestinationSectionUid()
+Movement.getBaobabDestinationSectionUid__roles__ = PermissionRole(Permissions.View)
+
+Movement.getBaobabSourcePaymentUid = lambda x: x.getSourcePaymentUid()
+Movement.getBaobabSourcePaymentUid__roles__ = PermissionRole(Permissions.View)
+
+Movement.getBaobabDestinationPaymentUid = lambda x: x.getDestinationPaymentUid()
+Movement.getBaobabDestinationPaymentUid__roles__ = PermissionRole(Permissions.View)
+
+Movement.getBaobabSourceFunctionUid = lambda x: x.getSourceFunctionUid()
+Movement.getBaobabSourceFunctionUid__roles__ = PermissionRole(Permissions.View)
+
+Movement.getBaobabDestinationFunctionUid = lambda x: x.getDestinationFunctionUid()
+Movement.getBaobabDestinationFunctionUid__roles__ = PermissionRole(Permissions.View)
+
+Movement.getBaobabSourceProjectUid = lambda x: x.getSourceProjectUid()
+Movement.getBaobabSourceProjectUid__roles__ = PermissionRole(Permissions.View)
+
+Movement.getBaobabDestinationProjectUid = lambda x: x.getDestinationProjectUid()
+Movement.getBaobabDestinationProjectUid__roles__ = PermissionRole(Permissions.View)
+
+### Acquire Baobab source / destination uids from parent line
+DeliveryCell.getBaobabSourceUid = lambda x: x.getSourceUid()
+DeliveryCell.getBaobabSourceUid__roles__ = PermissionRole(Permissions.View)
+
+DeliveryCell.getBaobabDestinationUid = lambda x: x.getDestinationUid()
+DeliveryCell.getBaobabDestinationUid__roles__ = PermissionRole(Permissions.View)
+
+DeliveryCell.getBaobabSourceSectionUid = lambda x: x.getSourceSectionUid()
+DeliveryCell.getBaobabSourceSectionUid__roles__ = PermissionRole(Permissions.View)
+
+DeliveryCell.getBaobabDestinationSectionUid = lambda x: x.getDestinationSectionUid()
+DeliveryCell.getBaobabDestinationSectionUid__roles__ = PermissionRole(Permissions.View)
+
+DeliveryCell.getBaobabSourcePaymentUid = lambda x: x.getSourcePaymentUid()
+DeliveryCell.getBaobabSourcePaymentUid__roles__ = PermissionRole(Permissions.View)
+
+DeliveryCell.getBaobabDestinationPaymentUid = lambda x: x.getDestinationPaymentUid()
+DeliveryCell.getBaobabDestinationPaymentUid__roles__ = PermissionRole(Permissions.View)
+
+DeliveryCell.getBaobabSourceFunctionUid = lambda x: x.getSourceFunctionUid()
+DeliveryCell.getBaobabSourceFunctionUid__roles__ = PermissionRole(Permissions.View)
+
+DeliveryCell.getBaobabDestinationFunctionUid = lambda x: x.getDestinationFunctionUid()
+DeliveryCell.getBaobabDestinationFunctionUid__roles__ = PermissionRole(Permissions.View)
+
+DeliveryCell.getBaobabSourceProjectUid = lambda x: x.getSourceProjectUid()
+DeliveryCell.getBaobabSourceProjectUid__roles__ = PermissionRole(Permissions.View)
+
+DeliveryCell.getBaobabDestinationProjectUid = lambda x: x.getDestinationProjectUid()
+DeliveryCell.getBaobabDestinationProjectUid__roles__ = PermissionRole(Permissions.View)
diff --git a/product/ERP5Banking/Document/BankingOperationLine.py b/product/ERP5Banking/Document/BankingOperationLine.py
new file mode 100755
index 0000000000..f8da45f4d9
--- /dev/null
+++ b/product/ERP5Banking/Document/BankingOperationLine.py
@@ -0,0 +1,83 @@
+##############################################################################
+#
+# Copyright (c) 2005 Nexedi SARL and Contributors. All Rights Reserved.
+#
+# WARNING: This program as such is intended to be used by professional
+# programmers who take the whole responsability of assessing all potential
+# consequences resulting from its eventual inadequacies and bugs
+# End users who are looking for a ready-to-use solution with commercial
+# garantees and support are strongly adviced to contract a Free Software
+# Service Company
+#
+# 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 AccessControl import ClassSecurityInfo
+from Products.CMFCore.utils import getToolByName
+from Products.CMFCore.WorkflowCore import WorkflowMethod
+from Products.ERP5Type import Permissions, PropertySheet, Constraint, Interface
+from Products.ERP5.Document.AccountingTransactionLine import AccountingTransactionLine
+from Products.ERP5Banking.BaobabMixin import BaobabMixin
+
+class BankingOperationLine(BaobabMixin, AccountingTransactionLine):
+  # CMF Type Definition
+  meta_type = 'ERP5Banking Banking Operation Line'
+  portal_type = 'Banking Operation Line'
+  isPortalContent = 1
+  isRADContent = 1
+
+  # Declarative security
+  security = ClassSecurityInfo()
+  security.declareObjectProtected(Permissions.View)
+
+  # Default Properties
+  property_sheets = ( PropertySheet.Base
+                    , PropertySheet.XMLObject
+                    , PropertySheet.CategoryCore
+                    , PropertySheet.DublinCore
+                    )
+
+  security.declareProtected(Permissions.View, 'getSourceTotalAssetPrice')
+  def getSourceTotalAssetPrice(self):
+    """Default to quantity."""
+    return self._baseGetSourceTotalAssetPrice() or abs(self.getQuantity())
+
+  security.declareProtected(Permissions.View, 'getDestinationTotalAssetPrice')
+  def getDestinationTotalAssetPrice(self):
+    """Default to quantity."""
+    return self._baseGetDestinationTotalAssetPrice() or abs(self.getQuantity())
+
+  security.declareProtected(Permissions.View, 'getSourceTotalAssetPriceCurrencyReference')
+  def getSourceTotalAssetPriceCurrencyReference(self):
+    """Return the reference of the price currency of the source payment."""
+    payment = self.getBaobabSourcePaymentValue()
+    if payment is not None:
+      currency = payment.getPriceCurrencyValue()
+      if currency is not None:
+        return currency.getReference()
+    return None
+
+  security.declareProtected(Permissions.View, 'getDestinationTotalAssetPriceCurrencyReference')
+  def getDestinationTotalAssetPriceCurrencyReference(self):
+    """Return the reference of the price currency of the destination payment."""
+    payment = self.getBaobabDestinationPaymentValue()
+    if payment is not None:
+      currency = payment.getPriceCurrencyValue()
+      if currency is not None:
+        return currency.getReference()
+    return None
+
+
diff --git a/product/ERP5Banking/Document/CashContainer.py b/product/ERP5Banking/Document/CashContainer.py
new file mode 100755
index 0000000000..091618dc4e
--- /dev/null
+++ b/product/ERP5Banking/Document/CashContainer.py
@@ -0,0 +1,66 @@
+##############################################################################
+#
+# Copyright (c) 2005 Nexedi SARL and Contributors. All Rights Reserved.
+#
+# WARNING: This program as such is intended to be used by professional
+# programmers who take the whole responsability of assessing all potential
+# consequences resulting from its eventual inadequacies and bugs
+# End users who are looking for a ready-to-use solution with commercial
+# garantees and support are strongly adviced to contract a Free Software
+# Service Company
+#
+# 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 AccessControl import ClassSecurityInfo
+from Products.ERP5Type import Permissions, PropertySheet, Constraint, Interface
+from Products.ERP5.Document.Container import Container
+
+
+class CashContainer(Container):
+  """
+    A Cash DeliveryLine object allows to implement lines
+      in Cash Deliveries (packing list, Check payment, Cash Movement, etc.).
+
+    It may include a price (for insurance, for customs, for invoices,
+      for orders).
+  """
+
+  meta_type = 'ERP5Banking Cash Container'
+  portal_type = 'Cash Container'
+  add_permission = Permissions.AddPortalContent
+  isPortalContent = 1
+  isRADContent = 1
+
+  # Declarative security
+  security = ClassSecurityInfo()
+  security.declareObjectProtected(Permissions.View)
+
+  # Declarative properties
+  property_sheets = ( PropertySheet.Base
+                    , PropertySheet.XMLObject
+                    , PropertySheet.CategoryCore
+                    , PropertySheet.Amount
+                    , PropertySheet.Task
+                    , PropertySheet.Arrow
+                    , PropertySheet.Movement
+                    , PropertySheet.Price
+                    , PropertySheet.VariationRange
+                    , PropertySheet.ItemAggregation
+                    , PropertySheet.Container
+                    , PropertySheet.CashContainer
+                    , PropertySheet.Reference
+                    )
diff --git a/product/ERP5Banking/Document/CashCurrency.py b/product/ERP5Banking/Document/CashCurrency.py
new file mode 100755
index 0000000000..664443db1c
--- /dev/null
+++ b/product/ERP5Banking/Document/CashCurrency.py
@@ -0,0 +1,114 @@
+##############################################################################
+#
+# Copyright (c) 2005 Nexedi SARL and Contributors. All Rights Reserved.
+#                    Jean-Paul Smets-Solanes <jp@nexedi.com>
+#
+# WARNING: This program as such is intended to be used by professional
+# programmers who take the whole responsability of assessing all potential
+# consequences resulting from its eventual inadequacies and bugs
+# End users who are looking for a ready-to-use solution with commercial
+# garantees and support are strongly adviced to contract a Free Software
+# Service Company
+#
+# 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 AccessControl import ClassSecurityInfo
+
+from Products.ERP5Type import Permissions, PropertySheet, Constraint, Interface
+
+from Products.ERP5.Document.Resource import Resource
+from zLOG import LOG
+
+
+class CashCurrency(Resource):
+  """
+    A Resource
+  """
+
+  meta_type = 'ERP5Banking Cash Currency'
+  portal_type = 'Cash Currency'
+  add_permission = Permissions.AddPortalContent
+  isPortalContent = 1
+  isRADContent = 1
+
+  # Declarative security
+  security = ClassSecurityInfo()
+  security.declareObjectProtected(Permissions.View)
+
+  # Declarative interfaces
+  __implements__ = ( Interface.Variated, )
+
+  # Declarative properties
+  property_sheets = ( PropertySheet.Base
+                    , PropertySheet.XMLObject
+                    , PropertySheet.CategoryCore
+                    , PropertySheet.DublinCore
+                    , PropertySheet.Price
+                    , PropertySheet.Resource
+                    , PropertySheet.Reference
+                    , PropertySheet.FlowCapacity
+                    , PropertySheet.VariationRange
+                    , PropertySheet.CashCurrency
+                    )
+
+  security.declareProtected(Permissions.View,'getTitle')
+  def getTitle(self,**kw):
+    """
+      The title will depend on the Portal Type and the value, for example :
+        Piece de 500
+    """
+    title = self.portal_types[self.getPortalType()].title
+    price = self.getBasePrice()
+    if price is None:
+      price = 'Not Defined'
+    else:
+      price = '%i' % int(price)
+    title = '%s de %s' % (title, price)
+    return title
+
+  security.declareProtected(Permissions.ModifyPortalContent, '_setVariationList')
+  def _setVariationList(self,value):
+    """
+      We will create cells by the same time
+    """
+    LOG('_setVariationList, value',0,value)
+    self._categorySetVariationList(value)
+    self.setVariationBaseCategoryList(('cash_status','emission_letter','variation'))
+    #all_variation_list = self.OrderLine_getMatrixItemList()
+    #emission_letter_list = [x for x in all_variation_list if x.startswith('emission_letter')]
+    emission_letter_list = [x[1] for x in self.portal_categories.emission_letter.getCategoryChildTitleItemList()[1:]]
+    self._categorySetEmissionLetterList(emission_letter_list)
+    #cash_status_list = [x for x in all_variation_list if x.startswith('cash_status')]
+    cash_status_list = [x[1] for x in self.portal_categories.cash_status.getCategoryChildTitleItemList()[1:]]
+    self._categorySetCashStatusList(cash_status_list)
+
+  security.declareProtected(Permissions.ModifyPortalContent, 'setVariationList')
+  def setVariationList(self,value):
+    """
+      Call the private method
+    """
+    self._setVariationList(value)
+
+  # Cell Related
+  security.declareProtected( Permissions.ModifyPortalContent, 'newCellContent' )
+  def newCellContent(self, id):
+    """
+      This method can be overriden
+    """
+    self.invokeFactory(type_name="Set Mapped Value",id=id)
+    return self.get(id)
+
diff --git a/product/ERP5Banking/Document/CashDelivery.py b/product/ERP5Banking/Document/CashDelivery.py
new file mode 100755
index 0000000000..0d3665bcac
--- /dev/null
+++ b/product/ERP5Banking/Document/CashDelivery.py
@@ -0,0 +1,62 @@
+##############################################################################
+#
+# Copyright (c) 2005 Nexedi SARL and Contributors. All Rights Reserved.
+#                    Kevin Deldycke <kevin_AT_nexedi_DOT_com>
+#
+# WARNING: This program as such is intended to be used by professional
+# programmers who take the whole responsability of assessing all potential
+# consequences resulting from its eventual inadequacies and bugs
+# End users who are looking for a ready-to-use solution with commercial
+# garantees and support are strongly adviced to contract a Free Software
+# Service Company
+#
+# 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 AccessControl import ClassSecurityInfo
+from Products.ERP5Type import Permissions, PropertySheet, Constraint, Interface
+from Products.ERP5Type.Document.BankingOperation import BankingOperation
+
+class CashDelivery(BankingOperation):
+  """
+  """
+
+  meta_type = 'ERP5Banking Cash Delivery'
+  portal_type = 'Cash Delivery'
+  add_permission = Permissions.AddPortalContent
+  isPortalContent = 1
+  isRADContent = 1
+
+  # Declarative security
+  security = ClassSecurityInfo()
+  security.declareObjectProtected(Permissions.View)
+
+  # Declarative interfaces
+  __implements__ = ( Interface.Variated, )
+
+  # Declarative properties
+  property_sheets = ( PropertySheet.Base
+                    , PropertySheet.XMLObject
+                    , PropertySheet.CategoryCore
+                    , PropertySheet.Amount
+                    , PropertySheet.Task
+                    , PropertySheet.Arrow
+                    , PropertySheet.Movement
+                    , PropertySheet.Price
+                    , PropertySheet.VariationRange
+                    , PropertySheet.ItemAggregation
+                    )
+
diff --git a/product/ERP5Banking/Document/CashDeliveryCell.py b/product/ERP5Banking/Document/CashDeliveryCell.py
new file mode 100755
index 0000000000..2f7378ae35
--- /dev/null
+++ b/product/ERP5Banking/Document/CashDeliveryCell.py
@@ -0,0 +1,126 @@
+##############################################################################
+#
+# Copyright (c) 2006 Nexedi SARL and Contributors. All Rights Reserved.
+#                    Yoshinori Okuji <yo@nexedi.com>
+#
+# WARNING: This program as such is intended to be used by professional
+# programmers who take the whole responsability of assessing all potential
+# consequences resulting from its eventual inadequacies and bugs
+# End users who are looking for a ready-to-use solution with commercial
+# garantees and support are strongly adviced to contract a Free Software
+# Service Company
+#
+# 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 AccessControl import ClassSecurityInfo
+from Products.ERP5Type import Permissions, PropertySheet, Constraint, Interface
+from Products.ERP5.Document.DeliveryCell import DeliveryCell
+from Products.ERP5Banking.BaobabMixin import BaobabMixin
+
+class CashDeliveryCell(BaobabMixin, DeliveryCell):
+  """
+    A Cash Delivery Cell object allows to implement cells
+      in Cash Deliveries (packing list, Check payment, Cash Movement, etc.).
+
+    It may include a price (for insurance, for customs, for invoices,
+      for orders).
+  """
+  meta_type = 'ERP5Banking Cash Delivery Cell'
+  portal_type = 'Cash Delivery Cell'
+  add_permission = Permissions.AddPortalContent
+  isPortalContent = 1
+  isRADContent = 1
+
+  # Declarative security
+  security = ClassSecurityInfo()
+  security.declareObjectProtected(Permissions.View)
+
+  # Declarative interfaces
+  __implements__ = ( Interface.Variated, )
+
+  # Declarative properties
+  property_sheets = ( PropertySheet.Base
+                    , PropertySheet.CategoryCore
+                    , PropertySheet.Arrow
+                    , PropertySheet.Amount
+                    , PropertySheet.Task
+                    , PropertySheet.Movement
+                    , PropertySheet.Price
+                    , PropertySheet.Predicate
+                    , PropertySheet.MappedValue
+                    , PropertySheet.ItemAggregation
+                    )
+
+  security.declareProtected(Permissions.View, 'getBaobabSource')
+  def getBaobabSource(self, **kw):
+    """
+    """
+    return self.aq_parent.getBaobabSource(**kw)
+
+  security.declareProtected(Permissions.View, 'getBaobabDestination')
+  def getBaobabDestination(self, **kw):
+    """
+    """
+    return self.aq_parent.getBaobabDestination(**kw)
+
+  security.declareProtected(Permissions.View, 'getBaobabSourceSection')
+  def getBaobabSourceSection(self, **kw):
+    """
+    """
+    return self.aq_parent.getBaobabSourceSection(**kw)
+
+  security.declareProtected(Permissions.View, 'getBaobabDestinationSection')
+  def getBaobabDestinationSection(self, **kw):
+    """
+    """
+    return self.aq_parent.getBaobabDestinationSection(**kw)
+
+  security.declareProtected(Permissions.View, 'getBaobabSourcePayment')
+  def getBaobabSourcePayment(self, **kw):
+    """
+    """
+    return self.aq_parent.getBaobabSourcePayment(**kw)
+
+  security.declareProtected(Permissions.View, 'getBaobabDestinationPayment')
+  def getBaobabDestinationPayment(self, **kw):
+    """
+    """
+    return self.aq_parent.getBaobabDestinationPayment(**kw)
+
+  security.declareProtected(Permissions.View, 'getBaobabSourceFunction')
+  def getBaobabSourceFunction(self, **kw):
+    """
+    """
+    return self.aq_parent.getBaobabSourceFunction(**kw)
+
+  security.declareProtected(Permissions.View, 'getBaobabDestinationFunction')
+  def getBaobabDestinationFunction(self, **kw):
+    """
+    """
+    return self.aq_parent.getBaobabDestinationFunction(**kw)
+
+  security.declareProtected(Permissions.View, 'getBaobabSourceProject')
+  def getBaobabSourceProject(self, **kw):
+    """
+    """
+    return self.aq_parent.getBaobabSourceProject(**kw)
+
+  security.declareProtected(Permissions.View, 'getBaobabDestinationProject')
+  def getBaobabDestinationProject(self, **kw):
+    """
+    """
+    return self.aq_parent.getBaobabDestinationProject(**kw)
diff --git a/product/ERP5Banking/Document/CashDeliveryLine.py b/product/ERP5Banking/Document/CashDeliveryLine.py
new file mode 100755
index 0000000000..8d72acd21e
--- /dev/null
+++ b/product/ERP5Banking/Document/CashDeliveryLine.py
@@ -0,0 +1,95 @@
+##############################################################################
+#
+# Copyright (c) 2005 Nexedi SARL and Contributors. All Rights Reserved.
+#                    Jean-Paul Smets-Solanes <jp@nexedi.com>
+#
+# WARNING: This program as such is intended to be used by professional
+# programmers who take the whole responsability of assessing all potential
+# consequences resulting from its eventual inadequacies and bugs
+# End users who are looking for a ready-to-use solution with commercial
+# garantees and support are strongly adviced to contract a Free Software
+# Service Company
+#
+# 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 AccessControl import ClassSecurityInfo
+from Products.ERP5Type import Permissions, PropertySheet, Constraint, Interface
+from Products.ERP5.Document.DeliveryLine import DeliveryLine
+from Products.ERP5Banking.BaobabMixin import BaobabMixin
+
+in_portal_type_list = ('Cash Exchange Line In', 'Cash To Currency Sale Line In','Cash To Currency Purchase Line In', 'Cash Incident Line In')
+out_portal_type_list = ('Cash Exchange Line Out', 'Cash To Currency Sale Line Out','Cash To Currency Purchase Line Out','Cash Incident Line Out')
+
+
+class CashDeliveryLine(BaobabMixin, DeliveryLine):
+  """
+    A Cash DeliveryLine object allows to implement lines
+      in Cash Deliveries (packing list, Check payment, Cash Movement, etc.).
+
+    It may include a price (for insurance, for customs, for invoices,
+      for orders).
+  """
+
+  meta_type = 'ERP5Banking Cash Delivery Line'
+  portal_type = 'Cash Delivery Line'
+  add_permission = Permissions.AddPortalContent
+  isPortalContent = 1
+  isRADContent = 1
+
+  # Declarative security
+  security = ClassSecurityInfo()
+  security.declareObjectProtected(Permissions.View)
+
+  # Declarative interfaces
+  __implements__ = ( Interface.Variated, )
+
+  # Declarative properties
+  property_sheets = ( PropertySheet.Base
+                    , PropertySheet.XMLObject
+                    , PropertySheet.CategoryCore
+                    , PropertySheet.Amount
+                    , PropertySheet.Task
+                    , PropertySheet.Arrow
+                    , PropertySheet.Movement
+                    , PropertySheet.Price
+                    , PropertySheet.VariationRange
+                    , PropertySheet.ItemAggregation
+                    , PropertySheet.CashDeliveryLine
+                    )
+
+  security.declareProtected(Permissions.View, 'getBaobabSource')
+  def getBaobabSource(self):
+    """
+      Returns a calculated source
+    """
+    if self.portal_type in out_portal_type_list:
+      return self.portal_categories.resolveCategory(self.getSource()).unrestrictedTraverse('sortante').getRelativeUrl()
+    elif self.portal_type in in_portal_type_list:
+      return None
+    return self.getSource()
+
+  security.declareProtected(Permissions.View, 'getBaobabDestination')
+  def getBaobabDestination(self):
+    """
+      Returns a calculated destination
+    """
+    if self.portal_type in in_portal_type_list:
+      return self.portal_categories.resolveCategory(self.getSource()).unrestrictedTraverse('entrante').getUid()
+    elif self.portal_type in out_portal_type_list :
+      return None
+    return self.getDestination()
+
diff --git a/product/ERP5Banking/Document/__init__.py b/product/ERP5Banking/Document/__init__.py
new file mode 100755
index 0000000000..e69de29bb2
diff --git a/product/ERP5Banking/Extensions/.cvsignore b/product/ERP5Banking/Extensions/.cvsignore
new file mode 100755
index 0000000000..0d20b6487c
--- /dev/null
+++ b/product/ERP5Banking/Extensions/.cvsignore
@@ -0,0 +1 @@
+*.pyc
diff --git a/product/ERP5Banking/Extensions/__init__.py b/product/ERP5Banking/Extensions/__init__.py
new file mode 100755
index 0000000000..e69de29bb2
diff --git a/product/ERP5Banking/Interface/.cvsignore b/product/ERP5Banking/Interface/.cvsignore
new file mode 100755
index 0000000000..0d20b6487c
--- /dev/null
+++ b/product/ERP5Banking/Interface/.cvsignore
@@ -0,0 +1 @@
+*.pyc
diff --git a/product/ERP5Banking/Interface/__init__.py b/product/ERP5Banking/Interface/__init__.py
new file mode 100755
index 0000000000..e69de29bb2
diff --git a/product/ERP5Banking/Permissions.py b/product/ERP5Banking/Permissions.py
new file mode 100755
index 0000000000..e69de29bb2
diff --git a/product/ERP5Banking/PropertySheet/.cvsignore b/product/ERP5Banking/PropertySheet/.cvsignore
new file mode 100755
index 0000000000..0d20b6487c
--- /dev/null
+++ b/product/ERP5Banking/PropertySheet/.cvsignore
@@ -0,0 +1 @@
+*.pyc
diff --git a/product/ERP5Banking/PropertySheet/BankingOperation.py b/product/ERP5Banking/PropertySheet/BankingOperation.py
new file mode 100755
index 0000000000..730cdb93f7
--- /dev/null
+++ b/product/ERP5Banking/PropertySheet/BankingOperation.py
@@ -0,0 +1,47 @@
+##############################################################################
+#
+# Copyright (c) 2005 Nexedi SARL and Contributors. All Rights Reserved.
+#                    K. Toure <ktoure_AT_nexedi_DOT_com>
+#
+# WARNING: This program as such is intended to be used by professional
+# programmers who take the whole responsability of assessing all potential
+# consequences resulting from its eventual inadequacies and bugs
+# End users who are looking for a ready-to-use solution with commercial
+# garantees and support are strongly adviced to contract a Free Software
+# Service Company
+#
+# 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.
+#
+##############################################################################
+
+
+class BankingOperation:
+  """
+    Person properties and categories
+  """
+
+  _properties = (
+      # Subordination properties
+      { 'id'          : 'movement',
+        'storage_id'  : 'movement',
+        'description' : 'The current amount',
+        'type'        : 'content',
+        'portal_type' : ('Banking Operation Line'),
+        'acquired_property_id'       : ('source_debit', 'source_credit'),
+        'mode'        : 'w' },
+      )
+
+  _categories = ()
+
diff --git a/product/ERP5Banking/PropertySheet/BaobabCategory.py b/product/ERP5Banking/PropertySheet/BaobabCategory.py
new file mode 100755
index 0000000000..9111ceaf46
--- /dev/null
+++ b/product/ERP5Banking/PropertySheet/BaobabCategory.py
@@ -0,0 +1,48 @@
+##############################################################################
+#
+# Copyright (c) 2005 Nexedi SARL and Contributors. All Rights Reserved.
+#
+# WARNING: This program as such is intended to be used by professional
+# programmers who take the whole responsability of assessing all potential
+# consequences resulting from its eventual inadequacies and bugs
+# End users who are looking for a ready-to-use solution with commercial
+# garantees and support are strongly adviced to contract a Free Software
+# Service Company
+#
+# 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.
+#
+##############################################################################
+
+class BaobabCategory:
+  """
+    PropertySheetTemplate properties for all Baobab Categories
+  """
+
+  _properties = (
+    {
+      'id'          : 'codification',
+      'description' : 'category codified identifier',
+      'type'        : 'string',
+      'mode'        : 'w',
+      'default'     : None,
+      'acquisition_base_category'     : ('parent',),
+      'acquisition_portal_type'       : ('Category',),
+      'acquisition_copy_value'        : 0,
+      'acquisition_mask_value'        : 1,
+      'acquisition_accessor_id'       : 'getCodification',
+    },
+  )
+
+  _categories = ('vault_type',)
\ No newline at end of file
diff --git a/product/ERP5Banking/PropertySheet/CashContainer.py b/product/ERP5Banking/PropertySheet/CashContainer.py
new file mode 100755
index 0000000000..9561915a77
--- /dev/null
+++ b/product/ERP5Banking/PropertySheet/CashContainer.py
@@ -0,0 +1,48 @@
+##############################################################################
+#
+# Copyright (c) 2005 Nexedi SARL and Contributors. All Rights Reserved.
+#
+# WARNING: This program as such is intended to be used by professional
+# programmers who take the whole responsability of assessing all potential
+# consequences resulting from its eventual inadequacies and bugs
+# End users who are looking for a ready-to-use solution with commercial
+# garantees and support are strongly adviced to contract a Free Software
+# Service Company
+#
+# 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.
+#
+##############################################################################
+
+
+class CashContainer:
+  """
+    VariationRange which allows to define possible
+    variations for a Resource, a Transformation, etc.
+  """
+
+  _properties = (
+                   {   'id'          : 'cash_number_range_start',
+                       'description' : '',
+                       'type'        : 'string',
+                       'mode'        : 'w'
+                   },
+                   {   'id'          : 'cash_number_range_stop',
+                       'description' : '',
+                       'type'        : 'string',
+                       'mode'        : 'w'
+                   }
+                )
+                
+  _categories = ( 'emission_letter','cash_status','variation')
diff --git a/product/ERP5Banking/PropertySheet/CashCurrency.py b/product/ERP5Banking/PropertySheet/CashCurrency.py
new file mode 100755
index 0000000000..6515aba208
--- /dev/null
+++ b/product/ERP5Banking/PropertySheet/CashCurrency.py
@@ -0,0 +1,40 @@
+##############################################################################
+#
+# Copyright (c) 2005 Nexedi SARL and Contributors. All Rights Reserved.
+#                    Jean-Paul Smets-Solanes <jp@nexedi.com>
+#
+# WARNING: This program as such is intended to be used by professional
+# programmers who take the whole responsability of assessing all potential
+# consequences resulting from its eventual inadequacies and bugs
+# End users who are looking for a ready-to-use solution with commercial
+# garantees and support are strongly adviced to contract a Free Software
+# Service Company
+#
+# 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.
+#
+##############################################################################
+
+
+class CashCurrency:
+  """
+    Properties which allow to define a BankNote or a Coin.
+  """
+
+  _properties = ()
+
+  _categories = ( 'emission_letter'
+                , 'cash_status'
+                , 'variation'
+                )
diff --git a/product/ERP5Banking/PropertySheet/CashDeliveryLine.py b/product/ERP5Banking/PropertySheet/CashDeliveryLine.py
new file mode 100755
index 0000000000..4f0dd906da
--- /dev/null
+++ b/product/ERP5Banking/PropertySheet/CashDeliveryLine.py
@@ -0,0 +1,40 @@
+##############################################################################
+#
+# Copyright (c) 2005 Nexedi SARL and Contributors. All Rights Reserved.
+#                    Jean-Paul Smets-Solanes <jp@nexedi.com>
+#
+# WARNING: This program as such is intended to be used by professional
+# programmers who take the whole responsability of assessing all potential
+# consequences resulting from its eventual inadequacies and bugs
+# End users who are looking for a ready-to-use solution with commercial
+# garantees and support are strongly adviced to contract a Free Software
+# Service Company
+#
+# 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.
+#
+##############################################################################
+
+class CashDeliveryLine:
+  """
+    VariationRange which allows to define possible
+    variations for a Resource, a Transformation, etc.
+  """
+
+  _properties = (
+                )
+
+  _categories = ( 'emission_letter'
+                , 'cash_status'
+                )
diff --git a/product/ERP5Banking/PropertySheet/__init__.py b/product/ERP5Banking/PropertySheet/__init__.py
new file mode 100755
index 0000000000..e69de29bb2
diff --git a/product/ERP5Banking/README.txt b/product/ERP5Banking/README.txt
new file mode 100755
index 0000000000..18c6721ec4
--- /dev/null
+++ b/product/ERP5Banking/README.txt
@@ -0,0 +1,4 @@
+
+ERP5Banking
+
+  ERP5Banking was automatically generated by ERP5 Class Tool.
diff --git a/product/ERP5Banking/Tool/.cvsignore b/product/ERP5Banking/Tool/.cvsignore
new file mode 100755
index 0000000000..0d20b6487c
--- /dev/null
+++ b/product/ERP5Banking/Tool/.cvsignore
@@ -0,0 +1 @@
+*.pyc
diff --git a/product/ERP5Banking/Tool/__init__.py b/product/ERP5Banking/Tool/__init__.py
new file mode 100755
index 0000000000..e69de29bb2
diff --git a/product/ERP5Banking/__init__.py b/product/ERP5Banking/__init__.py
new file mode 100755
index 0000000000..7749d54666
--- /dev/null
+++ b/product/ERP5Banking/__init__.py
@@ -0,0 +1,48 @@
+
+##############################################################################
+#
+# Copyright (c) 2006 Nexedi SARL and Contributors. All Rights Reserved.
+#                    Yoshinori Okuji <yo@nexedi.com>
+#
+# WARNING: This program as such is intended to be used by professional
+# programmers who take the whole responsability of assessing all potential
+# consequences resulting from its eventual inadequacies and bugs
+# End users who are looking for a ready-to-use solution with commercial
+# garantees and support are strongly adviced to contract a Free Software
+# Service Company
+#
+# 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.
+#
+##############################################################################
+"""
+    ERP5 Free Software ERP
+"""
+
+# Update ERP5 Globals
+from Products.ERP5Type.Utils import initializeProduct, updateGlobals
+import sys, Permissions
+this_module = sys.modules[ __name__ ]
+document_classes = updateGlobals( this_module, globals(), permissions_module = Permissions)
+
+# Finish installation
+def initialize( context ):
+  import Document
+  initializeProduct(context, this_module, globals(),
+                         document_module = Document,
+                         document_classes = document_classes,
+                         object_classes = (),
+                         portal_tools = (),
+                         content_constructors = (),
+                         content_classes = ())
diff --git a/product/ERP5Banking/dtml/.cvsignore b/product/ERP5Banking/dtml/.cvsignore
new file mode 100755
index 0000000000..0d20b6487c
--- /dev/null
+++ b/product/ERP5Banking/dtml/.cvsignore
@@ -0,0 +1 @@
+*.pyc
diff --git a/product/ERP5Banking/dtml/__init__.py b/product/ERP5Banking/dtml/__init__.py
new file mode 100755
index 0000000000..e69de29bb2
diff --git a/product/ERP5Banking/help/.cvsignore b/product/ERP5Banking/help/.cvsignore
new file mode 100755
index 0000000000..0d20b6487c
--- /dev/null
+++ b/product/ERP5Banking/help/.cvsignore
@@ -0,0 +1 @@
+*.pyc
diff --git a/product/ERP5Banking/help/__init__.py b/product/ERP5Banking/help/__init__.py
new file mode 100755
index 0000000000..e69de29bb2
diff --git a/product/ERP5Banking/skins/.cvsignore b/product/ERP5Banking/skins/.cvsignore
new file mode 100755
index 0000000000..0d20b6487c
--- /dev/null
+++ b/product/ERP5Banking/skins/.cvsignore
@@ -0,0 +1 @@
+*.pyc
diff --git a/product/ERP5Banking/skins/__init__.py b/product/ERP5Banking/skins/__init__.py
new file mode 100755
index 0000000000..e69de29bb2
diff --git a/product/ERP5Banking/tests/.cvsignore b/product/ERP5Banking/tests/.cvsignore
new file mode 100755
index 0000000000..0d20b6487c
--- /dev/null
+++ b/product/ERP5Banking/tests/.cvsignore
@@ -0,0 +1 @@
+*.pyc
diff --git a/product/ERP5Banking/tests/__init__.py b/product/ERP5Banking/tests/__init__.py
new file mode 100755
index 0000000000..e69de29bb2
diff --git a/product/ERP5Banking/tests/testERP5BankingCashClassification.py b/product/ERP5Banking/tests/testERP5BankingCashClassification.py
new file mode 100755
index 0000000000..9713fe9e92
--- /dev/null
+++ b/product/ERP5Banking/tests/testERP5BankingCashClassification.py
@@ -0,0 +1,974 @@
+##############################################################################
+#
+# Copyright (c) 2005-2006 Nexedi SARL and Contributors. All Rights Reserved.
+#                   Aurelien Calonne <aurel@nexedi.com>
+#
+# WARNING: This program as such is intended to be used by professional
+# programmers who take the whole responsability of assessing all potential
+# consequences resulting from its eventual inadequacies and bugs
+# End users who are looking for a ready-to-use solution with commercial
+# garantees and support are strongly adviced to contract a Free Software
+# Service Company
+#
+# 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.
+#
+##############################################################################
+
+
+# import requested python module
+import os
+import AccessControl
+from zLOG import LOG
+from Testing import ZopeTestCase
+from DateTime import DateTime
+from Products.CMFCore.utils import getToolByName
+from Products.ERP5Type.Utils import convertToUpperCase
+from Products.ERP5Type.tests.ERP5TypeTestCase import ERP5TypeTestCase
+from Products.ERP5Type.tests.Sequence import SequenceList
+from AccessControl.SecurityManagement import newSecurityManager
+from Products.DCWorkflow.DCWorkflow import Unauthorized, ValidationFailed
+from Testing.ZopeTestCase.PortalTestCase import PortalTestCase
+
+# Needed in order to have a log file inside the current folder
+os.environ['EVENT_LOG_FILE']     = os.path.join(os.getcwd(), 'zLOG.log')
+# Define the level of log we want, here is all
+os.environ['EVENT_LOG_SEVERITY'] = '-300'
+
+# Define how to launch the script if we don't use runUnitTest script
+if __name__ == '__main__':
+  execfile(os.path.join(sys.path[0], 'framework.py'))
+
+
+
+class TestERP5BankingCashClassification(ERP5TypeTestCase):
+  """
+    This class is a unit test to check the module of Cash Classification
+
+    Here are the following step that will be done in the test :
+  
+    - before the test, we need to create some movements that will put resources in the source
+
+    - create a cash classification
+    - check it has been created correctly
+    - check source and destination (current == future)
+    - check roles (who is author, assignor, assignee, ...)
+
+    - create a "Note Line" (billetage)
+    - check it has been created correctly
+    - check the total amount
+
+    - create a second Line
+    - check it has been created correctly
+    - check the total amount
+
+    - create an invalid Line (quantity > available at source)
+    - check that the system behaves correctly
+
+    - pass "confirm_action" transition
+    - check that we can't pass the transition as another user (depending on roles)
+    - check that the new state is confirmed
+    - check that the source has been debited correctly (current < future)
+
+    - log in as "Controleur" (assignee)
+    - check amount, lines, ...
+
+    - pass "deliver_action" transition
+    - check that we can't pass the transition as another user
+    - check that the new state is delivered
+    - check that the destination has been credited correctly (current == future)
+  """
+
+  login = PortalTestCase.login
+
+  # pseudo constants
+  RUN_ALL_TEST = 1 # we want to run all test
+  QUIET = 0 # we don't want the test to be quiet
+
+
+
+  ##################################
+  ##  ZopeTestCase Skeleton
+  ##################################
+
+  def getTitle(self):
+    """
+      Return the title of the test
+    """
+    return "ERP5BankingCashClassification"
+
+
+  def getBusinessTemplateList(self):
+    """
+      Return the list of business templates we need to run the test.
+      This method is called during the initialization of the unit test by
+      the unit test framework in order to know which business templates
+      need to be installed to run the test on.
+    """
+    return ( 'erp5_trade'  # erp5_trade is not required to make erp5_banking_cash_classification working.
+                           # As explained below erp5_trade is just used to help us initialize ressources
+                           #   via Internal Packing List.
+           , 'erp5_banking_core' # erp5_banking_core contains all generic methods for banking
+           , 'erp5_banking_cash_classification' # erp5_banking_cash contains all method for cash classification
+           )
+
+
+  def enableLightInstall(self):
+    """
+      Return if we should do a light install (1) or not (0)
+      Light install variable is used at installation of categories in business template
+      to know if we wrap the category or not, if 1 we don't use and installation is faster
+    """
+    return 1 # here we want a light install for a faster installation
+
+
+  def enableActivityTool(self):
+    """
+      Return if we should create (1) or not (0) an activity tool
+      This variable is used at the creation of the site to know if we use
+      the activity tool or not
+    """
+    return 1 # here we want to use the activity tool
+
+
+  def afterSetUp(self):
+    """
+      Method called before the launch of the test to initialize some data
+    """
+    # Set some variables : 
+    # the erp5 site
+    self.portal = self.getPortal()
+    # the cash classification module
+    self.cash_classification_module = self.getCashClassificationModule()
+    # the person module
+    self.person_folder = self.getPersonModule()
+    # the organisation module
+    self.organisation_folder = self.getOrganisationModule()
+    # the category tool
+    self.category_tool = self.getCategoryTool()
+
+    # Let us know which user folder is used (PAS or NuxUserGroup)
+    self.checkUserFolderType()
+
+    # Create a user and login as manager to populate the erp5 portal with objects for tests.
+    self.createManagerAndLogin()
+
+    # Define static values (only use prime numbers to prevent confusions like 2 * 6 == 3 * 4)
+    # variation list is the list of years for banknotes and coins
+    self.variation_list = ('variation/1992', 'variation/2003')
+
+    # quantity of banknotes of 10000 :
+    self.quantity_10000 = {}
+    # 2 banknotes of 10000 for the year 1992
+    self.quantity_10000[self.variation_list[0]] = 2
+    # 3 banknotes of 10000 for the year of 2003
+    self.quantity_10000[self.variation_list[1]] = 3
+
+    # quantity of coin of 200
+    self.quantity_200 = {}
+    # 5 coins of 200 for the year 1992
+    self.quantity_200[self.variation_list[0]] = 5
+    # 7 coins of 200 for the year 2003
+    self.quantity_200[self.variation_list[1]] = 7
+
+    # quantity of banknotes of 5000
+    self.quantity_5000 = {}
+    # 11 banknotes of 5000 for hte year 1992
+    self.quantity_5000[self.variation_list[0]] = 11
+    # 13 banknotes of 5000 for the year 2003
+    self.quantity_5000[self.variation_list[1]] = 13
+
+
+    # Create Categories (vaults)
+
+    # as local roles are defined in portal types as real categories, we will need to reproduce (or import) the real category tree
+    # get the base category function
+    self.function_base_category = getattr(self.category_tool, 'function')
+    # add category banking in function which will hold all functions neccessary in a bank (at least for this unit test)
+    self.banking = self.function_base_category.newContent(id='banking', portal_type='Category', codification='BNK')
+    # add category caisier_principal in function banking
+    self.caissier_principal = self.banking.newContent(id='caissier_principal', portal_type='Category', codification='CCP')
+    # add category controleur_caisse in function banking
+    self.controleur_caisse = self.banking.newContent(id='controleur_caisse', portal_type='Category', codification='CCT')
+    # add category void_function in function banking
+    self.void_function = self.banking.newContent(id='void_function', portal_type='Category', codification='VOID')
+    # add category gestionnaire_caisse_courante in function banking
+    self.gestionnaire_caisse_courante = self.banking.newContent(id='gestionnaire_caisse_courante', portal_type='Category', codification='CCO')
+    # add category gestionnaire_caveau in function banking
+    self.gestionnaire_caveau = self.banking.newContent(id='gestionnaire_caveau', portal_type='Category', codification='CCV')
+    # add category caissier_particulier in function banking
+    self.caissier_particulier = self.banking.newContent(id='caissier_particulier', portal_type='Category', codification='CGU')
+
+    # get the base category group
+    self.group_base_category = getattr(self.category_tool, 'group')
+    # add the group baobab in the group category
+    self.baobab = self.group_base_category.newContent(id='baobab', portal_type='Category', codification='BAOBAB')
+
+    # get the base category site
+    self.site_base_category = getattr(self.category_tool, 'site')
+    # add the category testsite in the category site which hold vaults situated in the bank
+    self.testsite = self.site_base_category.newContent(id='site', portal_type='Category', codification='TEST')
+#     self.siegesite = self.testsite.newContent(id='siege', portal_type='Category', codification='SIEGE')
+    self.siegesite = self.site_base_category.newContent(id='siege', portal_type='Category', codification='SIEGE')
+    self.agencesite = self.site_base_category.newContent(id='agence', portal_type='Category', codification='AGENCE')
+    self.principalesite = self.agencesite.newContent(id='principale', portal_type='Category', codification='PRINCIPALE')
+    self.auxisite = self.agencesite.newContent(id='auxiliaire', portal_type='Category', codification='AUXILIAIRE')
+        
+    self.encaisse_billets_et_monnaies = self.testsite.newContent(id='encaisse_des_billets_et_monnaies', portal_type='Category', codification='C1')
+    self.encaisse_externe = self.testsite.newContent(id='encaisse_des_externes', portal_type='Category', codification='C1')
+    self.encaisse_ventilation = self.testsite.newContent(id='encaisse_des_billets_recus_pour_ventilation', portal_type='Category', codification='C1')
+    self.caisse_abidjan = self.encaisse_ventilation.newContent(id='abidjan', portal_type='Category', codification='C1')
+
+    # get the base category cash_status
+    self.cash_status_base_category = getattr(self.category_tool, 'cash_status')
+    # add the category valid in cash_status which define status of banknotes and coin
+    self.cash_status_valid = self.cash_status_base_category.newContent(id='valid', portal_type='Category')
+    self.cash_status_valid = self.cash_status_base_category.newContent(id='to_sort', portal_type='Category')
+
+    # get the base category emission letter
+    self.emission_letter_base_category = getattr(self.category_tool, 'emission_letter')
+    # add the category k in emission letter that will be used fo banknotes and coins
+    self.emission_letter_k = self.emission_letter_base_category.newContent(id='k', portal_type='Category')
+    self.emission_letter_b = self.emission_letter_base_category.newContent(id='b', portal_type='Category')
+    self.emission_letter_d = self.emission_letter_base_category.newContent(id='d', portal_type='Category')
+
+    # get the base category variation which hold the year of banknotes and coins
+    self.variation_base_category = getattr(self.category_tool, 'variation')
+    # add the category 1992 in variation
+    self.variation_1992 = self.variation_base_category.newContent(id='1992', portal_type='Category')
+    # add the category 2003 in varitation
+    self.variation_2003 = self.variation_base_category.newContent(id='2003', portal_type='Category')
+
+    # get the base category quantity_unit
+    self.variation_base_category = getattr(self.category_tool, 'quantity_unit')
+    # add category unit in quantity_unit which is the unit that will be used for banknotes and coins
+    self.unit = self.variation_base_category.newContent(id='unit', title='Unit')
+
+    # Create an Organisation that will be used for users assignment
+    self.organisation = self.organisation_folder.newContent(id='baobab_org', portal_type='Organisation',
+        function='banking', group='baobab',  site='site')
+
+    # Create some users who will get different roles on the cash classification.
+    #
+    # Dictionnary data scheme:
+    #     'user_login': [['Global Role'], 'organisation', 'function', 'group', 'site']
+    #
+    user_dict = {
+        'user_1' : [[], self.organisation, 'banking/gestionnaire_caveau', 'baobab', 'site/agence/principale']
+      , 'user_2' : [[], self.organisation, 'banking/controleur_caisse' , 'baobab', 'site/agence/principale']
+      , 'user_3' : [[], self.organisation, 'banking/void_function'     , 'baobab', 'site']
+      }
+    # call method to create this user
+    self.createERP5Users(user_dict)
+
+    # We must assign local roles to cash_classification_module manually, as they are
+    #   not packed in Business Templates yet.
+    # The local roles must be the one for gestionnaire_caisse_courante
+    if self.PAS_installed:
+      # in case of use of PAS
+      self.cash_classification_module.manage_addLocalRoles('CCV_BAOBAB_PRINCIPALE', ('Author',))
+    else:
+      # in case of NuxUserGroup
+      self.cash_classification_module.manage_addLocalGroupRoles('CCV_BAOBAB_PRINCIPALE', ('Author',))
+
+    # get the currency module
+    self.currency_module = self.getCurrencyModule()
+    # create the currency document for euro inside the currency module
+    self.currency_1 = self .currency_module.newContent(id='EUR', title='Euro')
+
+    # Create Resources (Banknotes & Coins)
+    # get the currency cash module
+    self.currency_cash_module = self.getCurrencyCashModule()
+    # create document for banknote of 10000 euros from years 1992 and 2003
+    self.billet_10000 = self.currency_cash_module.newContent(id='billet_10000', portal_type='Banknote', base_price=10000, price_currency_value=self.currency_1, variation_list=('1992', '2003'), quantity_unit_value=self.unit)
+    # create document for banknote of 500 euros from years 1992 and 2003
+    self.billet_5000 = self.currency_cash_module.newContent(id='billet_5000', portal_type='Banknote', base_price=5000, price_currency_value=self.currency_1, variation_list=('1992', '2003'), quantity_unit_value=self.unit)
+    # create docuemnt for coin of 200 euros from years 1992 and 2003
+    self.billet_200 = self.currency_cash_module.newContent(id='billet_200', portal_type='Banknote', base_price=200, price_currency_value=self.currency_1, variation_list=('1992', '2003'), quantity_unit_value=self.unit)
+
+    # Before the test, we need to create resources in the source.
+    # Using internal_packing_list from erp5_trade is the easiest.
+    # set the type of delivery
+    # get the internal packing list module
+    self.internal_packing_list_module = self.getInternalPackingListModule()
+    # add a new internal packing list for abidjan
+    self.internal_packing_list = self.internal_packing_list_module.newContent(id='packing_list_1', portal_type='Internal Packing List',
+            source=None, destination_value=self.caisse_abidjan)
+    # add a line for banknotes of 10000 with emission letter k, status valid and from years 1992 and 2003 with the quantity defined
+    # before in quantity_10000 (2 for 1992 and 3 for 2003)
+    self.addCashLineToDelivery(self.internal_packing_list, 'delivery_init_1', 'Internal Packing List Line', self.billet_10000,
+            ('emission_letter', 'cash_status', 'variation'), ('emission_letter/k', 'cash_status/to_sort') + self.variation_list,
+            self.quantity_10000)
+    # add a line for coins of 200 with emission letter k, status valid and from years 1992 and 2003 with the quantity defined
+    # before in quantity_200 (5 for 1992 and 7 for 2003)
+    self.addCashLineToDelivery(self.internal_packing_list, 'delivery_init_2', 'Internal Packing List Line', self.billet_200,
+            ('emission_letter', 'cash_status', 'variation'), ('emission_letter/b', 'cash_status/to_sort') + self.variation_list,
+            self.quantity_200)
+
+    # logout from manager
+    self.logout()
+    # Finally, login as user_1
+    self.login('user_1')
+
+
+  def checkUserFolderType(self, quiet=QUIET, run=RUN_ALL_TEST):
+    """
+      Check the type of user folder to let the test working with both NuxUserGroup and PAS.
+    """
+    self.user_folder = self.getUserFolder()
+    self.PAS_installed = 0
+    if self.user_folder.meta_type == 'Pluggable Auth Service':
+      # we use PAS
+      self.PAS_installed = 1
+
+
+  def assignPASRolesToUser(self, user_name, role_list, quiet=QUIET, run=RUN_ALL_TEST):
+    """
+      Assign a list of roles to one user with PAS.
+    """
+    for role in role_list:
+      if role not in self.user_folder.zodb_roles.listRoleIds():
+        self.user_folder.zodb_roles.addRole(role)
+      self.user_folder.zodb_roles.assignRoleToPrincipal(role, user_name)
+
+
+  def createManagerAndLogin(self, quiet=QUIET, run=RUN_ALL_TEST):
+    """
+      Create a simple user in user_folder with manager rights.
+      This user will be used to initialize data in the method afterSetup
+    """
+    self.getUserFolder()._doAddUser('manager', '', ['Manager'], [])
+    self.login('manager')
+
+
+  def createERP5Users(self, user_dict, quiet=QUIET, run=RUN_ALL_TEST):
+    """
+      Create all ERP5 users needed for the test.
+      ERP5 user = Person object + Assignment object in erp5 person_module.
+    """
+    for user_login, user_data in user_dict.items():
+      user_roles = user_data[0]
+      # Create the Person.
+      person = self.person_folder.newContent(id=user_login,
+          portal_type='Person', reference=user_login, career_role="internal")
+      # Create the Assignment.
+      assignment = person.newContent( portal_type       = 'Assignment'
+                                    , destination_value = user_data[1]
+                                    , function          = user_data[2]
+                                    , group             = user_data[3]
+                                    , site              = user_data[4]
+                                    )
+      if self.PAS_installed and len(user_roles) > 0:
+        # In the case of PAS, if we want global roles on user, we have to do it manually.
+        self.assignPASRolesToUser(user_login, user_roles)
+      elif not self.PAS_installed:
+        # The user_folder counterpart of the erp5 user must be
+        #   created manually in the case of NuxUserGroup.
+        self.user_folder.userFolderAddUser( name     = user_login
+                                          , password = ''
+                                          , roles    = user_roles
+                                          , domains  = []
+                                          )
+      # User assignment to security groups is also required, but is taken care of
+      #   by the assignment workflow when NuxUserGroup is used and
+      #   by ERP5Security PAS plugins in the context of PAS use.
+      assignment.open()
+      
+    if self.PAS_installed:
+      # reindexing is required for the security to work
+      get_transaction().commit()
+      self.tic()
+
+
+  ##################################
+  ##  Usefull methods
+  ##################################
+
+  def addCashLineToDelivery(self, delivery_object, line_id, line_portal_type, resource_object,
+          variation_base_category_list, variation_category_list, resource_quantity_dict):
+    """
+    Add a cash line to a delivery
+    This will add an Internal Packing List Line to a Internal Packing List
+    """
+    base_id = 'movement'
+    line_kwd = {'base_id':base_id}
+    # create the cash line
+    line = delivery_object.newContent( id                  = line_id
+                                     , portal_type         = line_portal_type
+                                     , resource_value      = resource_object # banknote or coin
+                                     , quantity_unit_value = self.unit
+                                     )
+    # set base category list on line
+    line.setVariationBaseCategoryList(variation_base_category_list)
+    # set category list line
+    line.setVariationCategoryList(variation_category_list)
+    line.updateCellRange(script_id='CashDetail_asCellRange', base_id=base_id)
+    cell_range_key_list = line.getCellRangeKeyList(base_id=base_id)
+    if cell_range_key_list <> [[None, None]] :
+      for k in cell_range_key_list:
+        category_list = filter(lambda k_item: k_item is not None, k)
+        c = line.newCell(*k, **line_kwd)
+        mapped_value_list = ['price', 'quantity']
+        c.edit( membership_criterion_category_list = category_list
+              , mapped_value_property_list         = mapped_value_list
+              , category_list                      = category_list
+              , force_update                       = 1
+              )
+    # set quantity on cell to define quantity of bank notes / coins
+    for variation in self.variation_list:
+      cell = line.getCell('emission_letter/k', variation, 'cash_status/to_sort')
+      if cell is not None:
+        cell.setQuantity(resource_quantity_dict[variation])
+    for variation in self.variation_list:
+      cell = line.getCell('emission_letter/b', variation, 'cash_status/to_sort')
+      if cell is not None:
+        cell.setQuantity(resource_quantity_dict[variation])
+
+  def getUserFolder(self):
+    """
+    Return the user folder
+    """
+    return getattr(self.getPortal(), 'acl_users', None)
+
+  def getPersonModule(self):
+    """
+    Return the person module
+    """
+    return getattr(self.getPortal(), 'person_module', None)
+  
+  def getOrganisationModule(self):
+    """
+    Return the organisation module
+    """
+    return getattr(self.getPortal(), 'organisation_module', None)
+  
+  def getCurrencyCashModule(self):
+    """
+    Return the Currency Cash Module
+    """
+    return getattr(self.getPortal(), 'currency_cash_module', None)
+  
+  def getCashClassificationModule(self):
+    """
+    Return the Cash Transer Module
+    """
+    return getattr(self.getPortal(), 'cash_classification_module', None)
+  
+  def getInternalPackingListModule(self):
+    """
+    Return the Internal Packing List Module
+    """
+    return getattr(self.getPortal(), 'internal_packing_list_module', None)
+  
+  def getCurrencyModule(self):
+    """
+    Return the Currency Module
+    """
+    return getattr(self.getPortal(), 'currency_module', None)
+  
+  def getCategoryTool(self):
+    """
+    Return the Category Tool
+    """
+    return getattr(self.getPortal(), 'portal_categories', None)
+  
+  def getWorkflowTool(self):
+    """
+    Return the Worklfow Tool
+    """
+    return getattr(self.getPortal(), 'portal_workflow', None)
+  
+  def getSimulationTool(self):
+    """
+    Return the Simulation Tool
+    """
+    return getattr(self.getPortal(), 'portal_simulation', None)
+
+
+
+  ##################################
+  ##  Basic steps
+  ##################################
+
+  def stepTic(self, **kwd):
+    """
+    The is used to simulate the zope_tic_loop script
+    Each time this method is called, it simulates a call to tic
+    which invoke activities in the Activity Tool
+    """
+    # execute transaction
+    get_transaction().commit()
+    self.tic()
+
+
+  def stepCheckObjects(self, sequence=None, sequence_list=None, **kwd):
+    """
+    Check that all the objects we created in afterSetUp or
+    that were added by the business template and that we rely
+    on are really here.
+    """
+    # check that Categories were created
+    self.assertEqual(self.caisse_abidjan.getPortalType(), 'Category')
+    self.assertEqual(self.encaisse_externe.getPortalType(), 'Category')
+
+    # check that Resources were created
+    # check portal type of billet_10000
+    self.assertEqual(self.billet_10000.getPortalType(), 'Banknote')
+    # check value of billet_10000
+    self.assertEqual(self.billet_10000.getBasePrice(), 10000)
+    # check currency value  of billet_10000
+    self.assertEqual(self.billet_10000.getPriceCurrency(), 'currency_module/EUR')
+    # check years  of billet_10000
+    self.assertEqual(self.billet_10000.getVariationList(), ['1992', '2003'])
+
+    # check portal type of billet_5000
+    self.assertEqual(self.billet_5000.getPortalType(), 'Banknote')
+    # check value of billet_5000
+    self.assertEqual(self.billet_5000.getBasePrice(), 5000)
+    # check currency value  of billet_5000
+    self.assertEqual(self.billet_5000.getPriceCurrency(), 'currency_module/EUR')
+    # check years  of billet_5000
+    self.assertEqual(self.billet_5000.getVariationList(), ['1992', '2003'])
+    
+    # check portal type of billet_200
+    self.assertEqual(self.billet_200.getPortalType(), 'Banknote')
+    # check value of billet_200
+    self.assertEqual(self.billet_200.getBasePrice(), 200)
+    # check currency value  of billet_200
+    self.assertEqual(self.billet_200.getPriceCurrency(), 'currency_module/EUR')
+    # check years  of billet_200
+    self.assertEqual(self.billet_200.getVariationList(), ['1992', '2003'])
+
+    # check that CashClassification Module was created
+    self.assertEqual(self.cash_classification_module.getPortalType(), 'Cash Classification Module')
+    # check cash classification module is empty
+    self.assertEqual(len(self.cash_classification_module.objectValues()), 0)
+
+
+  def stepCheckInitialInventory(self, sequence=None, sequence_list=None, **kwd):
+    """
+    Check the initial inventory before any operations
+    """
+    self.simulation_tool = self.getSimulationTool()
+    # check we have 5 banknotes of 10000 in caisse_abidjan
+    self.assertEqual(self.simulation_tool.getCurrentInventory(node=self.caisse_abidjan.getRelativeUrl(), resource = self.billet_10000.getRelativeUrl()), 5.0)
+    self.assertEqual(self.simulation_tool.getFutureInventory(node=self.caisse_abidjan.getRelativeUrl(), resource = self.billet_10000.getRelativeUrl()), 5.0)
+    # check we have 12 coin of 200 in caisse_abidjan
+    self.assertEqual(self.simulation_tool.getCurrentInventory(node=self.caisse_abidjan.getRelativeUrl(), resource = self.billet_200.getRelativeUrl()), 12.0)
+    self.assertEqual(self.simulation_tool.getFutureInventory(node=self.caisse_abidjan.getRelativeUrl(), resource = self.billet_200.getRelativeUrl()), 12.0)
+
+
+  def stepCheckSource(self, sequence=None, sequence_list=None, **kwd):
+    """
+    Check inventory in source vault (caisse_abidjan) before a confirm
+    """
+    # check we have 5 banknotes of 10000
+    self.assertEqual(self.simulation_tool.getCurrentInventory(node=self.caisse_abidjan.getRelativeUrl(), resource = self.billet_10000.getRelativeUrl()), 5.0)
+    self.assertEqual(self.simulation_tool.getFutureInventory(node=self.caisse_abidjan.getRelativeUrl(), resource = self.billet_10000.getRelativeUrl()), 5.0)
+    # check we have 12 coin of 200
+    self.assertEqual(self.simulation_tool.getCurrentInventory(node=self.caisse_abidjan.getRelativeUrl(), resource = self.billet_200.getRelativeUrl()), 12.0)
+    self.assertEqual(self.simulation_tool.getFutureInventory(node=self.caisse_abidjan.getRelativeUrl(), resource = self.billet_200.getRelativeUrl()), 12.0)
+
+
+  def stepCheckDestination(self, sequence=None, sequence_list=None, **kwd):
+    """
+    Check inventory in destination vault (caisse_2) before confirm
+    """
+    # check we don't have banknotes of 10000
+    self.assertEqual(self.simulation_tool.getCurrentInventory(node=self.encaisse_billets_et_monnaies.getRelativeUrl(), resource = self.billet_10000.getRelativeUrl()), 0.0)
+    self.assertEqual(self.simulation_tool.getFutureInventory(node=self.encaisse_billets_et_monnaies.getRelativeUrl(), resource = self.billet_10000.getRelativeUrl()), 0.0)
+    # check we don't have coins of 200
+    self.assertEqual(self.simulation_tool.getCurrentInventory(node=self.encaisse_externe.getRelativeUrl(), resource = self.billet_200.getRelativeUrl()), 0.0)
+    self.assertEqual(self.simulation_tool.getFutureInventory(node=self.encaisse_externe.getRelativeUrl(), resource = self.billet_200.getRelativeUrl()), 0.0)
+
+
+  def stepCreateCashClassification(self, sequence=None, sequence_list=None, **kwd):
+    """
+    Create a cash classification document and check it
+    """
+    # Cash classification has caisse_abidjan for source, encaisse_externe for destination, and a price cooreponding to the sum of banknote of 10000 and banknotes of 200 ( (2+3) * 1000 + (5+7) * 200 )
+    self.cash_classification = self.cash_classification_module.newContent(id='cash_classification_1', portal_type='Cash Classification', source_value=self.caisse_abidjan, destination_value=None, source_total_asset_price=52400.0)
+    # execute tic
+    self.stepTic()
+    # check we have only one cash classification
+    self.assertEqual(len(self.cash_classification_module.objectValues()), 1)
+    # get the cash classification document
+    self.cash_classification = getattr(self.cash_classification_module, 'cash_classification_1')
+    # check its portal type
+    self.assertEqual(self.cash_classification.getPortalType(), 'Cash Classification')
+    # check that its source is caisse_abidjan
+    self.assertEqual(self.cash_classification.getSource(), 'site/site/encaisse_des_billets_recus_pour_ventilation/abidjan')
+    # check that its destination is encaisse_externe
+    self.assertEqual(self.cash_classification.getDestination(), None)
+    #XXX Check roles were correctly affected
+    #self.security_manager = AccessControl.getSecurityManager()
+    #self.user = self.security_manager.getUser()
+    #raise 'alex', repr( self.cash_classification.get_local_roles() )
+
+
+  def stepCreateValidIncomingLine(self, sequence=None, sequence_list=None, **kwd):
+    """
+    Create the cash classification incoming line  with banknotes of 10000 and check it has been well created
+    """
+    # create the cash classification line
+    self.addCashLineToDelivery(self.cash_classification, 'valid_incoming_line', 'Cash Classification Line In', self.billet_10000,
+            ('emission_letter', 'cash_status', 'variation'), ('emission_letter/k', 'cash_status/to_sort') + self.variation_list,
+            self.quantity_10000)
+    # execute tic
+    self.stepTic()
+    # check there is only one line created
+    self.assertEqual(len(self.cash_classification.objectValues()), 1)
+    # get the cash classification line
+    self.valid_incoming_line = getattr(self.cash_classification, 'valid_incoming_line')
+    # check its portal type
+    self.assertEqual(self.valid_incoming_line.getPortalType(), 'Cash Classification Line In')
+    # check the resource is banknotes of 10000
+    self.assertEqual(self.valid_incoming_line.getResourceValue(), self.billet_10000)
+    # chek the value of the banknote
+    self.assertEqual(self.valid_incoming_line.getPrice(), 10000.0)
+    # check the unit of banknote
+    self.assertEqual(self.valid_incoming_line.getQuantityUnit(), 'quantity_unit/unit')
+    # check we have two delivery cells: (one for year 1992 and one for 2003)
+    self.assertEqual(len(self.valid_incoming_line.objectValues()), 2)
+    # now check for each variation (years 1992 and 2003)
+    for variation in self.variation_list:
+      # get the delivery cell
+      cell = self.valid_incoming_line.getCell('emission_letter/k', variation, 'cash_status/to_sort')
+      # chek portal types
+      self.assertEqual(cell.getPortalType(), 'Cash Delivery Cell')
+      # check the banknote of the cell is banknote of 10000
+      self.assertEqual(cell.getResourceValue(), self.billet_10000)
+      # check the source vault is caisse_abidjan
+      self.assertEqual(cell.getSourceValue(), self.caisse_abidjan)
+      # check the destination vault is encaisse_externe
+      self.assertEqual(cell.getDestinationValue(), None)
+      if cell.getId() == 'movement_0_0_0':
+        # check the quantity of banknote for year 1992 is 2
+        self.assertEqual(cell.getQuantity(), 2.0)
+      elif cell.getId() == 'movement_0_1_0':
+        # check the quantity of banknote for year 2003 is 3
+        self.assertEqual(cell.getQuantity(), 3.0)
+      else:
+        self.fail('Wrong cell created : %s' % cell.getId())
+
+
+  def stepCheckSubTotal(self, sequence=None, sequence_list=None, **kwd):
+    """
+    Check the amount after the creation of cash classification line 1
+    """
+    # Check number of lines
+    self.assertEqual(len(self.cash_classification.objectValues()), 1)
+    # Check quantity of banknotes (2 for 1992 and 3 for 2003)
+    self.assertEqual(self.cash_classification.getTotalQuantity(), 5.0)
+    # Check the total price
+    self.assertEqual(self.cash_classification.getTotalPrice(), 10000 * 5.0)
+
+
+  def stepCreateValidOutgoingLine(self, sequence=None, sequence_list=None, **kwd):
+    """
+    Create the cash classification outgoing line wiht banknotes of 200 and check it has been well created
+    """
+    # create the line
+    self.addCashLineToDelivery(self.cash_classification, 'valid_outgoing_line', 'Cash Classification Line Out', self.billet_200,
+            ('emission_letter', 'cash_status', 'variation'), ('emission_letter/b', 'cash_status/to_sort') + self.variation_list,
+            self.quantity_200)
+    # execute tic
+    self.stepTic()
+    # check the number of lines (line1 + line2)
+    self.assertEqual(len(self.cash_classification.objectValues()), 2)
+    # get the second cash classification line
+    self.valid_outgoing_line = getattr(self.cash_classification, 'valid_outgoing_line')
+    # check portal types
+    self.assertEqual(self.valid_outgoing_line.getPortalType(), 'Cash Classification Line Out')
+    # check the resource is coin of 200
+    self.assertEqual(self.valid_outgoing_line.getResourceValue(), self.billet_200)
+    # check the value of coin
+    self.assertEqual(self.valid_outgoing_line.getPrice(), 200.0)
+    # check the unit of coin
+    self.assertEqual(self.valid_outgoing_line.getQuantityUnit(), 'quantity_unit/unit')
+    # check we have two delivery cells: (one for year 1992 and one for 2003)
+    self.assertEqual(len(self.valid_outgoing_line.objectValues()), 2)
+    for variation in self.variation_list:
+      # get the delivery  cell
+      cell = self.valid_outgoing_line.getCell('emission_letter/b', variation, 'cash_status/to_sort')
+      # check the portal type
+      self.assertEqual(cell.getPortalType(), 'Cash Delivery Cell')
+      if cell.getId() == 'movement_0_0_0':
+        # check the quantity for coin for year 1992 is 5
+        self.assertEqual(cell.getQuantity(), 5.0)
+      elif cell.getId() == 'movement_0_1_0':
+        # check the quantity for coin for year 2003 is 7
+        self.assertEqual(cell.getQuantity(), 7.0)
+      else:
+        self.fail('Wrong cell created : %s' % cell.getId())
+
+        
+  
+  def stepTryConfirmCashClassificationWithBadUser(self, sequence=None, sequence_list=None, **kwd):
+    """
+    Try to confirm cash classification with a user that doesn't have the right and
+    check that the try of confirm by a bad user doesn't change the cash classification
+    """
+    # logout from user_1
+    self.logout()
+    # log in as bad user
+    self.login('user_3')
+    # get workflow tool
+    self.workflow_tool = self.getWorkflowTool()
+    # check that an Unauthorized exception is raised when trying to confirm the cash classification
+    self.assertRaises(Unauthorized, self.workflow_tool.doActionFor, self.cash_classification, 'confirm_action', wf_id='cash_classification_workflow')
+    # logout from user_3
+    self.logout()
+    # login as default user
+    self.login('user_1')
+    # execute tic
+    self.stepTic()
+    # get state of the cash classification
+    state = self.cash_classification.getSimulationState()
+    # check it has remain as draft
+    self.assertEqual(state, 'draft')
+    # get the workflow history
+    workflow_history = self.workflow_tool.getInfoFor(ob=self.cash_classification, name='history', wf_id='cash_classification_workflow')
+    # check its len is one
+    self.assertEqual(len(workflow_history), 1)
+
+
+  def stepCreateInvalidLine(self, sequence=None, sequence_list=None, **kwd):
+    """
+    Create an invalid cash classification line and
+    check the total with the invalid cash classification line
+    """
+    # create a line in which quanity of banknotes of 5000 is higher that quantity available at source
+    # here create a line with 24 (11+13) banknotes of 500 although the vault caisse_abidjan has no banknote of 5000
+    self.addCashLineToDelivery(self.cash_classification, 'invalid_line', 'Cash Classification Line Out', self.billet_5000,
+            ('emission_letter', 'cash_status', 'variation'), ('emission_letter/k', 'cash_status/to_sort') + self.variation_list,
+            self.quantity_5000)
+    # execute tic
+    self.stepTic()
+    # Check number of cash classification lines (line1 + line2 +invalid_line)
+    self.assertEqual(len(self.cash_classification.objectValues()), 3)
+    # Check quantity, same as checkTotal + banknote of 500: 11 for 1992 and 13 for 2003
+    self.assertEqual(self.cash_classification.getTotalQuantity(), 5.0 + 12.0 + 24)
+    # chect the total price
+    self.assertEqual(self.cash_classification.getTotalPrice(), 10000 * 5.0 + 200 * 12.0 + 5000 * 24)
+
+
+  def stepTryConfirmCashClassificationWithBadInventory(self, sequence=None, sequence_list=None, **kwd):
+    """
+    Try to confirm the cash classification with a bad cash classification line and
+    check the try of confirm the cash classification with the invalid line has failed
+    """
+    # fix amount (10000 * 5.0 + 200 * 12.0 + 5000 * 24)
+    self.cash_classification.setSourceTotalAssetPrice('172400.0')
+    # try to do the workflow action "confirm_action', cath the exception ValidationFailed raised by workflow transition
+    self.assertRaises(ValidationFailed, self.workflow_tool.doActionFor, self.cash_classification, 'confirm_action', wf_id='cash_classification_workflow')
+    # execute tic
+    self.stepTic()
+    # get state of the cash classification
+    state = self.cash_classification.getSimulationState()
+    # check the state is draft
+    self.assertEqual(state, 'draft')
+    # get workflow history
+    workflow_history = self.workflow_tool.getInfoFor(ob=self.cash_classification, name='history', wf_id='cash_classification_workflow')
+    # check its len is 2
+    self.assertEqual(len(workflow_history), 2)
+    self.assertEqual('Insufficient balance' in workflow_history[-1]['error_message'], True)
+
+
+  def stepDelInvalidLine(self, sequence=None, sequence_list=None, **kwd):
+    """
+    Delete the invalid cash classification line previously create
+    """
+    self.cash_classification.deleteContent('invalid_line')
+
+
+  def stepCheckTotal(self, sequence=None, sequence_list=None, **kwd):
+    """
+    Check the total after the creation of the two cash classification lines
+    """
+    # Check number of lines (line1 + line2)
+    self.assertEqual(len(self.cash_classification.objectValues()), 2)
+    # Check quantity, banknotes : 2 for 1992 and 3 for 2003, coin : 5 for 1992 and 7 for 2003
+    self.assertEqual(self.cash_classification.getTotalQuantity(), 5.0 + 12.0)
+    # check the total price
+    self.assertEqual(self.cash_classification.getTotalPrice(), 10000 * 5.0 + 200 * 12.0)
+
+
+  def stepConfirmCashClassification(self, sequence=None, sequence_list=None, **kwd):
+    """
+    Confirm the cash classification and check it
+    """
+    # fix amount (10000 * 5.0 + 200 * 12.0)
+    self.cash_classification.setSourceTotalAssetPrice('52400.0')
+    # do the Workflow action
+    self.workflow_tool.doActionFor(self.cash_classification, 'confirm_action', wf_id='cash_classification_workflow')
+    # execute tic
+    self.stepTic()
+    # get state
+    state = self.cash_classification.getSimulationState()
+    # check state is confirmed
+    self.assertEqual(state, 'confirmed')
+    # get workflow history
+    workflow_history = self.workflow_tool.getInfoFor(ob=self.cash_classification, name='history', wf_id='cash_classification_workflow')
+    # check len of workflow history is 4
+    self.assertEqual(len(workflow_history), 4)
+
+
+  def stepCheckSourceDebitPlanned(self, sequence=None, sequence_list=None, **kwd):
+    """
+    Check that compution of inventory at vault caisse_abidjan is right after confirm and before deliver 
+    """
+    # check we have 5 banknotes of 10000 currently
+    self.assertEqual(self.simulation_tool.getCurrentInventory(node=self.caisse_abidjan.getRelativeUrl(), resource = self.billet_10000.getRelativeUrl()), 5.0)
+    # check we will have 0 banknote of 10000 after deliver
+    self.assertEqual(self.simulation_tool.getFutureInventory(node=self.caisse_abidjan.getRelativeUrl(), resource = self.billet_10000.getRelativeUrl()), 0.0)
+    # check we have 12 coins of 200 currently
+    self.assertEqual(self.simulation_tool.getCurrentInventory(node=self.caisse_abidjan.getRelativeUrl(), resource = self.billet_200.getRelativeUrl()), 12.0)
+    # check we will have 0 coin of 200 after deliver
+    self.assertEqual(self.simulation_tool.getFutureInventory(node=self.caisse_abidjan.getRelativeUrl(), resource = self.billet_200.getRelativeUrl()), 0.0)
+
+
+  def stepCheckDestinationCreditPlanned(self, sequence=None, sequence_list=None, **kwd):
+    """
+    Check that compution of inventory at vault encaisse_externe is right after confirm and before deliver
+    """
+    # check we have 0 banknote of 10000 currently
+    self.assertEqual(self.simulation_tool.getCurrentInventory(node=self.encaisse_billets_et_monnaies.getRelativeUrl(), resource = self.billet_10000.getRelativeUrl()), 0.0)
+    # check we will have 5 banknotes of 10000 after deliver
+    self.assertEqual(self.simulation_tool.getFutureInventory(node=self.encaisse_billets_et_monnaies.getRelativeUrl(), resource = self.billet_10000.getRelativeUrl()), 5.0)
+    # check we have 0 coin of 200 currently
+    self.assertEqual(self.simulation_tool.getCurrentInventory(node=self.encaisse_externe.getRelativeUrl(), resource = self.billet_200.getRelativeUrl()), 0.0)
+    # check we will have 12 coins of 200 after deliver
+    self.assertEqual(self.simulation_tool.getFutureInventory(node=self.encaisse_externe.getRelativeUrl(), resource = self.billet_200.getRelativeUrl()), 12.0)
+
+
+  def stepTryDeliverCashClassificationWithBadUser(self, sequence=None, sequence_list=None, **kwd):
+    """
+    Try to deliver a cash classification with a user that doesn't have the right
+    and check that it failed
+    """
+    # logout from user_1
+    self.logout()
+    # log in as bad user
+    self.login('user_3')
+    # check we raise an Unauthorized Exception if we try to deliver cash classification
+    self.assertRaises(Unauthorized, self.workflow_tool.doActionFor, self.cash_classification, 'deliver_action', wf_id='cash_classification_workflow')
+    # logout from bad user
+    self.logout()
+    # log in as default user
+    self.login('user_1')
+    # execute tic
+    self.stepTic()
+    # get state of the cash classification
+    state = self.cash_classification.getSimulationState()
+    # check that state is confirmed
+    self.assertEqual(state, 'confirmed')
+    # get workflow history
+    workflow_history = self.workflow_tool.getInfoFor(ob=self.cash_classification, name='history', wf_id='cash_classification_workflow')
+    # check len of workflow history is 4
+    self.assertEqual(len(workflow_history), 4)
+
+
+  def stepDeliverCashClassification(self, sequence=None, sequence_list=None, **kwd):
+    """
+    Deliver the cash classification with a good user
+    and check that the deliver of a cash tranfer have achieved
+    """
+    # logout from user_1
+    self.logout()
+    # log in as good user (controleur_caisse)
+    self.login('user_2')
+    #     self.security_manager = AccessControl.getSecurityManager()
+    #     self.user = self.security_manager.getUser()
+    # do the workflow transition "deliver_action"
+    self.workflow_tool.doActionFor(self.cash_classification, 'deliver_action', wf_id='cash_classification_workflow')
+    # logout from user_2
+    self.logout()
+    # log in as default user
+    self.login('user_1')
+    # execute tic
+    self.stepTic()
+    # get state of cash classification
+    state = self.cash_classification.getSimulationState()
+    # check that state is delivered
+    self.assertEqual(state, 'delivered')
+    # get workflow history
+    workflow_history = self.workflow_tool.getInfoFor(ob=self.cash_classification, name='history', wf_id='cash_classification_workflow')
+    # check len of len workflow history is 6
+    self.assertEqual(len(workflow_history), 6)
+    
+
+  def stepCheckSourceDebit(self, sequence=None, sequence_list=None, **kwd):
+    """
+    Check inventory at source (vault caisse_abidjan) after deliver of the cash classification
+    """
+    # check we have 0 banknote of 10000
+    self.assertEqual(self.simulation_tool.getCurrentInventory(node=self.caisse_abidjan.getRelativeUrl(), resource = self.billet_10000.getRelativeUrl()), 0.0)
+    self.assertEqual(self.simulation_tool.getFutureInventory(node=self.caisse_abidjan.getRelativeUrl(), resource = self.billet_10000.getRelativeUrl()), 0.0)
+    # check we have 0 coin of 200
+    self.assertEqual(self.simulation_tool.getCurrentInventory(node=self.caisse_abidjan.getRelativeUrl(), resource = self.billet_200.getRelativeUrl()), 0.0)
+    self.assertEqual(self.simulation_tool.getFutureInventory(node=self.caisse_abidjan.getRelativeUrl(), resource = self.billet_200.getRelativeUrl()), 0.0)
+
+
+  def stepCheckDestinationCredit(self, sequence=None, sequence_list=None, **kwd):
+    """
+    Check inventory at destination (vault encaisse_externe) after deliver of the cash classification
+    """
+    # check we have 5 banknotes of 10000
+    self.assertEqual(self.simulation_tool.getCurrentInventory(node=self.encaisse_billets_et_monnaies.getRelativeUrl(), resource = self.billet_10000.getRelativeUrl()), 5.0)
+    self.assertEqual(self.simulation_tool.getFutureInventory(node=self.encaisse_billets_et_monnaies.getRelativeUrl(), resource = self.billet_10000.getRelativeUrl()), 5.0)
+    # check we have 12 coins of 200
+    self.assertEqual(self.simulation_tool.getCurrentInventory(node=self.encaisse_externe.getRelativeUrl(), resource = self.billet_200.getRelativeUrl()), 12.0)
+    self.assertEqual(self.simulation_tool.getFutureInventory(node=self.encaisse_externe.getRelativeUrl(), resource = self.billet_200.getRelativeUrl()), 12.0)
+
+
+  ##################################
+  ##  Tests
+  ##################################
+
+  def test_01_ERP5BankingCashClassification(self, quiet=QUIET, run=RUN_ALL_TEST):
+    """
+    Define the sequence of step that will be play
+    """
+    if not run: return
+    sequence_list = SequenceList()
+    # define the sequence
+    sequence_string = 'Tic CheckObjects Tic CheckInitialInventory CheckSource CheckDestination ' \
+                    + 'CreateCashClassification ' \
+                    + 'CreateValidIncomingLine CheckSubTotal ' \
+                    + 'CreateValidOutgoingLine CheckTotal ' \
+                    + 'TryConfirmCashClassificationWithBadUser ' \
+                    + 'CheckSource CheckDestination ' \
+                    + 'CreateInvalidLine ' \
+                    + 'TryConfirmCashClassificationWithBadInventory ' \
+                    + 'DelInvalidLine Tic CheckTotal ' \
+                    + 'ConfirmCashClassification ' \
+                    + 'CheckSourceDebitPlanned CheckDestinationCreditPlanned ' \
+                    + 'TryDeliverCashClassificationWithBadUser ' \
+                    + 'CheckSourceDebitPlanned CheckDestinationCreditPlanned ' \
+                    + 'DeliverCashClassification ' \
+                    + 'CheckSourceDebit CheckDestinationCredit '
+    sequence_list.addSequenceString(sequence_string)
+    # play the sequence
+    sequence_list.play(self)
+
+# define how we launch the unit test
+if __name__ == '__main__':
+  framework()
+else:
+  import unittest
+  def test_suite():
+    suite = unittest.TestSuite()
+    suite.addTest(unittest.makeSuite(TestERP5BankingCashClassification))
+    return suite
diff --git a/product/ERP5Banking/tests/testERP5BankingCashInventory.py b/product/ERP5Banking/tests/testERP5BankingCashInventory.py
new file mode 100755
index 0000000000..e4ad21aaf2
--- /dev/null
+++ b/product/ERP5Banking/tests/testERP5BankingCashInventory.py
@@ -0,0 +1,804 @@
+##############################################################################
+#
+# Copyright (c) 2005-2006 Nexedi SARL and Contributors. All Rights Reserved.
+#                    Aurelien Calonne <aurel@nexedi.com>
+#
+# WARNING: This program as such is intended to be used by professional
+# programmers who take the whole responsability of assessing all potential
+# consequences resulting from its eventual inadequacies and bugs
+# End users who are looking for a ready-to-use solution with commercial
+# garantees and support are strongly adviced to contract a Free Software
+# Service Company
+#
+# 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.
+#
+##############################################################################
+
+
+# import requested python module
+import os
+import AccessControl
+from zLOG import LOG
+from Testing import ZopeTestCase
+from DateTime import DateTime
+from Products.CMFCore.utils import getToolByName
+from Products.ERP5Type.Utils import convertToUpperCase
+from Products.ERP5Type.tests.ERP5TypeTestCase import ERP5TypeTestCase
+from Products.ERP5Type.tests.Sequence import SequenceList
+from AccessControl.SecurityManagement import newSecurityManager
+from Products.DCWorkflow.DCWorkflow import Unauthorized, ValidationFailed
+from Testing.ZopeTestCase.PortalTestCase import PortalTestCase
+
+# Needed in order to have a log file inside the current folder
+os.environ['EVENT_LOG_FILE']     = os.path.join(os.getcwd(), 'zLOG.log')
+# Define the level of log we want, here is all
+os.environ['EVENT_LOG_SEVERITY'] = '-300'
+
+# Define how to launch the script if we don't use runUnitTest script
+if __name__ == '__main__':
+  execfile(os.path.join(sys.path[0], 'framework.py'))
+
+
+
+class TestERP5BankingInventory(ERP5TypeTestCase):
+  """
+    This class is a unit test to check the module of Cash Transfer
+
+    Here are the following step that will be done in the test :
+  
+    - before the test, we need to create some movements that will put resources in the source
+
+    - create a cash transfer
+    - check it has been created correctly
+    - check source and destination (current == future)
+    - check roles (who is author, assignor, assignee, ...)
+
+    - create a "Note Line" (billetage)
+    - check it has been created correctly
+    - check the total amount
+
+    - create a second Line
+    - check it has been created correctly
+    - check the total amount
+
+    - create an invalid Line (quantity > available at source)
+    - check that the system behaves correctly
+
+    - pass "confirm_action" transition
+    - check that we can't pass the transition as another user (depending on roles)
+    - check that the new state is confirmed
+    - check that the source has been debited correctly (current < future)
+
+    - log in as "Controleur" (assignee)
+    - check amount, lines, ...
+
+    - pass "deliver_action" transition
+    - check that we can't pass the transition as another user
+    - check that the new state is delivered
+    - check that the destination has been credited correctly (current == future)
+  """
+
+  login = PortalTestCase.login
+
+  # pseudo constants
+  RUN_ALL_TEST = 1 # we want to run all test
+  QUIET = 0 # we don't want the test to be quiet
+
+
+
+  ##################################
+  ##  ZopeTestCase Skeleton
+  ##################################
+
+  def getTitle(self):
+    """
+      Return the title of the test
+    """
+    return "ERP5BankingInventory"
+
+
+  def getBusinessTemplateList(self):
+    """
+      Return the list of business templates we need to run the test.
+      This method is called during the initialization of the unit test by
+      the unit test framework in order to know which business templates
+      need to be installed to run the test on.
+    """
+    return ( 'erp5_banking_core' # erp5_banking_core contains all generic methods for banking
+           , 'erp5_banking_inventory' # erp5_banking_cash contains all method for cash transfer
+           )
+
+
+  def enableLightInstall(self):
+    """
+      Return if we should do a light install (1) or not (0)
+      Light install variable is used at installation of categories in business template
+      to know if we wrap the category or not, if 1 we don't use and installation is faster
+    """
+    return 1 # here we want a light install for a faster installation
+
+
+  def enableActivityTool(self):
+    """
+      Return if we should create (1) or not (0) an activity tool
+      This variable is used at the creation of the site to know if we use
+      the activity tool or not
+    """
+    return 1 # here we want to use the activity tool
+
+
+  def afterSetUp(self):
+    """
+      Method called before the launch of the test to initialize some data
+    """
+    # Set some variables : 
+    # the erp5 site
+    self.portal = self.getPortal()
+    # the cash inventory module
+    self.cash_inventory_module = self.getCashInventoryModule()
+    # the person module
+    self.person_folder = self.getPersonModule()
+    # the organisation module
+    self.organisation_folder = self.getOrganisationModule()
+    # the category tool
+    self.category_tool = self.getCategoryTool()
+
+    # Let us know which user folder is used (PAS or NuxUserGroup)
+    self.checkUserFolderType()
+
+    # Create a user and login as manager to populate the erp5 portal with objects for tests.
+    self.createManagerAndLogin()
+
+    # Define static values (only use prime numbers to prevent confusions like 2 * 6 == 3 * 4)
+    # variation list is the list of years for banknotes and coins
+    self.variation_list = ('variation/1992', 'variation/2003')
+
+    # quantity of banknotes of 10000 :
+    self.quantity_10000 = {}
+    # 2 banknotes of 10000 for the year 1992
+    self.quantity_10000[self.variation_list[0]] = 2
+    # 3 banknotes of 10000 for the year of 2003
+    self.quantity_10000[self.variation_list[1]] = 3
+
+    # quantity of coin of 200
+    self.quantity_200 = {}
+    # 5 coins of 200 for the year 1992
+    self.quantity_200[self.variation_list[0]] = 5
+    # 7 coins of 200 for the year 2003
+    self.quantity_200[self.variation_list[1]] = 7
+
+    # quantity of banknotes of 5000
+    self.quantity_5000 = {}
+    # 11 banknotes of 5000 for hte year 1992
+    self.quantity_5000[self.variation_list[0]] = 11
+    # 13 banknotes of 5000 for the year 2003
+    self.quantity_5000[self.variation_list[1]] = 13
+
+
+    # Create Categories (vaults)
+
+    # as local roles are defined in portal types as real categories, we will need to reproduce (or import) the real category tree
+    # get the base category function
+    self.function_base_category = getattr(self.category_tool, 'function')
+    # add category banking in function which will hold all functions neccessary in a bank (at least for this unit test)
+    self.banking = self.function_base_category.newContent(id='banking', portal_type='Category', codification='BNK')
+    # add category caisier_principal in function banking
+    self.caissier_principal = self.banking.newContent(id='caissier_principal', portal_type='Category', codification='CCP')
+    # add category controleur_caisse in function banking
+    self.controleur_caisse = self.banking.newContent(id='controleur_caisse', portal_type='Category', codification='CCT')
+    # add category void_function in function banking
+    self.void_function = self.banking.newContent(id='void_function', portal_type='Category', codification='VOID')
+    # add category gestionnaire_caisse_courante in function banking
+    self.gestionnaire_caisse_courante = self.banking.newContent(id='gestionnaire_caisse_courante', portal_type='Category', codification='CCO')
+    # add category gestionnaire_caveau in function banking
+    self.gestionnaire_caveau = self.banking.newContent(id='gestionnaire_caveau', portal_type='Category', codification='CCV')
+    # add category caissier_particulier in function banking
+    self.caissier_particulier = self.banking.newContent(id='caissier_particulier', portal_type='Category', codification='CGU')
+
+    # get the base category group
+    self.group_base_category = getattr(self.category_tool, 'group')
+    # add the group baobab in the group category
+    self.baobab = self.group_base_category.newContent(id='baobab', portal_type='Category', codification='BAOBAB')
+
+    # get the base category site
+    self.site_base_category = getattr(self.category_tool, 'site')
+    # add the category testsite in the category site which hold vaults situated in the bank
+    self.testsite = self.site_base_category.newContent(id='testsite', portal_type='Category', codification='TEST')
+    # add vault caisse_1 in testsite
+    self.caisse_1 = self.testsite.newContent(id='caisse_1', portal_type='Category', codification='C1')
+    # add vault caisse_2 in testsite
+    self.caisse_2 = self.testsite.newContent(id='caisse_2', portal_type='Category', codification='C2')
+
+    # get the base category cash_status
+    self.cash_status_base_category = getattr(self.category_tool, 'cash_status')
+    # add the category valid in cash_status which define status of banknotes and coin
+    self.cash_status_valid = self.cash_status_base_category.newContent(id='valid', portal_type='Category')
+
+    # get the base category emission letter
+    self.emission_letter_base_category = getattr(self.category_tool, 'emission_letter')
+    # add the category k in emission letter that will be used fo banknotes and coins
+    self.emission_letter_k = self.emission_letter_base_category.newContent(id='k', portal_type='Category')
+
+    # get the base category variation which hold the year of banknotes and coins
+    self.variation_base_category = getattr(self.category_tool, 'variation')
+    # add the category 1992 in variation
+    self.variation_1992 = self.variation_base_category.newContent(id='1992', portal_type='Category')
+    # add the category 2003 in varitation
+    self.variation_2003 = self.variation_base_category.newContent(id='2003', portal_type='Category')
+
+    # get the base category quantity_unit
+    self.variation_base_category = getattr(self.category_tool, 'quantity_unit')
+    # add category unit in quantity_unit which is the unit that will be used for banknotes and coins
+    self.unit = self.variation_base_category.newContent(id='unit', title='Unit')
+
+    # Create an Organisation that will be used for users assignment
+    self.organisation = self.organisation_folder.newContent(id='baobab_org', portal_type='Organisation',
+        function='banking', group='baobab',  site='testsite')
+
+    # Create some users who will get different roles on the cash transfer.
+    #
+    # Dictionnary data scheme:
+    #     'user_login': [['Global Role'], 'organisation', 'function', 'group', 'site']
+    #
+    user_dict = {
+        'user_1' : [[], self.organisation, 'banking/gestionnaire_caisse_courante', 'baobab', 'testsite']
+      , 'user_2' : [[], self.organisation, 'banking/controleur_caisse' , 'baobab', 'testsite']
+      , 'user_3' : [[], self.organisation, 'banking/void_function'     , 'baobab', 'testsite']
+      }
+    # call method to create this user
+    self.createERP5Users(user_dict)
+
+    # We must assign local roles to cash_inventory_module manually, as they are
+    #   not packed in Business Templates yet.
+    # The local roles must be the one for gestionnaire_caisse_courante
+    if self.PAS_installed:
+      # in case of use of PAS
+      self.cash_inventory_module.manage_addLocalRoles('CCO_BAOBAB_TEST', ('Author',))
+    else:
+      # in case of NuxUserGroup
+      self.cash_inventory_module.manage_addLocalGroupRoles('CCO_BAOBAB_TEST', ('Author',))
+
+    # get the currency module
+    self.currency_module = self.getCurrencyModule()
+    # create the currency document for euro inside the currency module
+    self.currency_1 = self .currency_module.newContent(id='EUR', title='Euro')
+
+    # Create Resources (Banknotes & Coins)
+    # get the currency cash module
+    self.currency_cash_module = self.getCurrencyCashModule()
+    # create document for banknote of 10000 euros from years 1992 and 2003
+    self.billet_10000 = self.currency_cash_module.newContent(id='billet_10000', portal_type='Banknote', base_price=10000, price_currency_value=self.currency_1, variation_list=('1992', '2003'), quantity_unit_value=self.unit)
+    # create document for banknote of 500 euros from years 1992 and 2003
+    self.billet_5000 = self.currency_cash_module.newContent(id='billet_5000', portal_type='Banknote', base_price=5000, price_currency_value=self.currency_1, variation_list=('1992', '2003'), quantity_unit_value=self.unit)
+    # create docuemnt for coin of 200 euros from years 1992 and 2003
+    self.piece_200 = self.currency_cash_module.newContent(id='piece_200', portal_type='Coin', base_price=200, price_currency_value=self.currency_1, variation_list=('1992', '2003'), quantity_unit_value=self.unit)
+
+    # logout from manager
+    self.logout()
+    # Finally, login as user_1
+    self.login('user_1')
+
+
+  def checkUserFolderType(self, quiet=QUIET, run=RUN_ALL_TEST):
+    """
+      Check the type of user folder to let the test working with both NuxUserGroup and PAS.
+    """
+    self.user_folder = self.getUserFolder()
+    self.PAS_installed = 0
+    if self.user_folder.meta_type == 'Pluggable Auth Service':
+      # we use PAS
+      self.PAS_installed = 1
+
+
+  def assignPASRolesToUser(self, user_name, role_list, quiet=QUIET, run=RUN_ALL_TEST):
+    """
+      Assign a list of roles to one user with PAS.
+    """
+    for role in role_list:
+      if role not in self.user_folder.zodb_roles.listRoleIds():
+        self.user_folder.zodb_roles.addRole(role)
+      self.user_folder.zodb_roles.assignRoleToPrincipal(role, user_name)
+
+
+  def createManagerAndLogin(self, quiet=QUIET, run=RUN_ALL_TEST):
+    """
+      Create a simple user in user_folder with manager rights.
+      This user will be used to initialize data in the method afterSetup
+    """
+    self.getUserFolder()._doAddUser('manager', '', ['Manager'], [])
+    self.login('manager')
+
+
+  def createERP5Users(self, user_dict, quiet=QUIET, run=RUN_ALL_TEST):
+    """
+      Create all ERP5 users needed for the test.
+      ERP5 user = Person object + Assignment object in erp5 person_module.
+    """
+    for user_login, user_data in user_dict.items():
+      user_roles = user_data[0]
+      # Create the Person.
+      person = self.person_folder.newContent(id=user_login,
+          portal_type='Person', reference=user_login, career_role="internal")
+      # Create the Assignment.
+      assignment = person.newContent( portal_type       = 'Assignment'
+                                    , destination_value = user_data[1]
+                                    , function          = user_data[2]
+                                    , group             = user_data[3]
+                                    , site              = user_data[4]
+                                    )
+      if self.PAS_installed and len(user_roles) > 0:
+        # In the case of PAS, if we want global roles on user, we have to do it manually.
+        self.assignPASRolesToUser(user_login, user_roles)
+      elif not self.PAS_installed:
+        # The user_folder counterpart of the erp5 user must be
+        #   created manually in the case of NuxUserGroup.
+        self.user_folder.userFolderAddUser( name     = user_login
+                                          , password = ''
+                                          , roles    = user_roles
+                                          , domains  = []
+                                          )
+      # User assignment to security groups is also required, but is taken care of
+      #   by the assignment workflow when NuxUserGroup is used and
+      #   by ERP5Security PAS plugins in the context of PAS use.
+      assignment.open()
+      
+    if self.PAS_installed:
+      # reindexing is required for the security to work
+      get_transaction().commit()
+      self.tic()
+
+
+  ##################################
+  ##  Usefull methods
+  ##################################
+
+  def addCashLineToDelivery(self, delivery_object, line_id, line_portal_type, resource_object,
+          variation_base_category_list, variation_category_list, resource_quantity_dict):
+    """
+    Add a cash line to a delivery
+    This will add an Internal Packing List Line to a Internal Packing List
+    """
+    base_id = 'movement'
+    line_kwd = {'base_id':base_id}
+    # create the cash line
+    line = delivery_object.newContent( id                  = line_id
+                                     , portal_type         = line_portal_type
+                                     , resource_value      = resource_object # banknote or coin
+                                     , quantity_unit_value = self.unit
+                                     )
+    # set base category list on line
+    line.setVariationBaseCategoryList(variation_base_category_list)
+    # set category list line
+    line.setVariationCategoryList(variation_category_list)
+    line.updateCellRange(script_id='CashDetail_asCellRange', base_id=base_id)
+    cell_range_key_list = line.getCellRangeKeyList(base_id=base_id)
+    if cell_range_key_list <> [[None, None]] :
+      for k in cell_range_key_list:
+        category_list = filter(lambda k_item: k_item is not None, k)
+        c = line.newCell(*k, **line_kwd)
+        mapped_value_list = ['price', 'quantity']
+        c.edit( membership_criterion_category_list = category_list
+              , mapped_value_property_list         = mapped_value_list
+              , category_list                      = category_list
+              , force_update                       = 1
+              )
+    # set quantity on cell to define quantity of bank notes / coins
+    for variation in self.variation_list:
+      cell = line.getCell('emission_letter/k', variation, 'cash_status/valid')
+      cell.setQuantity(resource_quantity_dict[variation])
+
+
+  def getUserFolder(self):
+    """
+    Return the user folder
+    """
+    return getattr(self.getPortal(), 'acl_users', None)
+
+  def getPersonModule(self):
+    """
+    Return the person module
+    """
+    return getattr(self.getPortal(), 'person_module', None)
+  
+  def getOrganisationModule(self):
+    """
+    Return the organisation module
+    """
+    return getattr(self.getPortal(), 'organisation_module', None)
+  
+  def getCurrencyCashModule(self):
+    """
+    Return the Currency Cash Module
+    """
+    return getattr(self.getPortal(), 'currency_cash_module', None)
+  
+  def getCashInventoryModule(self):
+    """
+    Return the Cash Transer Module
+    """
+    return getattr(self.getPortal(), 'cash_inventory_module', None)  
+  
+  def getCurrencyModule(self):
+    """
+    Return the Currency Module
+    """
+    return getattr(self.getPortal(), 'currency_module', None)
+  
+  def getCategoryTool(self):
+    """
+    Return the Category Tool
+    """
+    return getattr(self.getPortal(), 'portal_categories', None)
+  
+  def getWorkflowTool(self):
+    """
+    Return the Worklfow Tool
+    """
+    return getattr(self.getPortal(), 'portal_workflow', None)
+  
+  def getSimulationTool(self):
+    """
+    Return the Simulation Tool
+    """
+    return getattr(self.getPortal(), 'portal_simulation', None)
+
+
+
+  ##################################
+  ##  Basic steps
+  ##################################
+
+  def stepTic(self, **kwd):
+    """
+    The is used to simulate the zope_tic_loop script
+    Each time this method is called, it simulates a call to tic
+    which invoke activities in the Activity Tool
+    """
+    # execute transaction
+    get_transaction().commit()
+    self.tic()
+
+
+  def stepCheckObjects(self, sequence=None, sequence_list=None, **kwd):
+    """
+    Check that all the objects we created in afterSetUp or
+    that were added by the business template and that we rely
+    on are really here.
+    """
+    # check that Categories were created
+    self.assertEqual(self.caisse_1.getPortalType(), 'Category')
+    self.assertEqual(self.caisse_2.getPortalType(), 'Category')
+
+    # check that Resources were created
+    # check portal type of billet_10000
+    self.assertEqual(self.billet_10000.getPortalType(), 'Banknote')
+    # check value of billet_10000
+    self.assertEqual(self.billet_10000.getBasePrice(), 10000)
+    # check currency value  of billet_10000
+    self.assertEqual(self.billet_10000.getPriceCurrency(), 'currency_module/EUR')
+    # check years  of billet_10000
+    self.assertEqual(self.billet_10000.getVariationList(), ['1992', '2003'])
+
+    # check portal type of billet_5000
+    self.assertEqual(self.billet_5000.getPortalType(), 'Banknote')
+    # check value of billet_5000
+    self.assertEqual(self.billet_5000.getBasePrice(), 5000)
+    # check currency value  of billet_5000
+    self.assertEqual(self.billet_5000.getPriceCurrency(), 'currency_module/EUR')
+    # check years  of billet_5000
+    self.assertEqual(self.billet_5000.getVariationList(), ['1992', '2003'])
+    
+    # check portal type of piece_200
+    self.assertEqual(self.piece_200.getPortalType(), 'Coin')
+    # check value of piece_200
+    self.assertEqual(self.piece_200.getBasePrice(), 200)
+    # check currency value  of piece_200
+    self.assertEqual(self.piece_200.getPriceCurrency(), 'currency_module/EUR')
+    # check years  of piece_200
+    self.assertEqual(self.piece_200.getVariationList(), ['1992', '2003'])
+
+    # check that CashInventory Module was created
+    self.assertEqual(self.cash_inventory_module.getPortalType(), 'Cash Inventory Module')
+    # check cash inventory module is empty
+    self.assertEqual(len(self.cash_inventory_module.objectValues()), 0)
+
+
+  def stepCheckInitialInventory(self, sequence=None, sequence_list=None, **kwd):
+    """
+    Check the initial inventory before any operations
+    """
+    self.simulation_tool = self.getSimulationTool()
+    # check we have 0 banknotes of 10000 in caisse_1
+    self.assertEqual(self.simulation_tool.getCurrentInventory(node=self.caisse_1.getRelativeUrl(), resource = self.billet_10000.getRelativeUrl()), 0.0)
+    self.assertEqual(self.simulation_tool.getFutureInventory(node=self.caisse_1.getRelativeUrl(), resource = self.billet_10000.getRelativeUrl()), 0.0)
+    # check we have 0 coin of 200 in caisse_1
+    self.assertEqual(self.simulation_tool.getCurrentInventory(node=self.caisse_1.getRelativeUrl(), resource = self.piece_200.getRelativeUrl()), 0.0)
+    self.assertEqual(self.simulation_tool.getFutureInventory(node=self.caisse_1.getRelativeUrl(), resource = self.piece_200.getRelativeUrl()), 0.0)
+    # check we have 0 banknotes of 5000 in caisse_1
+    self.assertEqual(self.simulation_tool.getCurrentInventory(node=self.caisse_1.getRelativeUrl(), resource = self.billet_5000.getRelativeUrl()), 0.0)
+    self.assertEqual(self.simulation_tool.getFutureInventory(node=self.caisse_1.getRelativeUrl(), resource = self.billet_5000.getRelativeUrl()), 0.0)
+
+
+  def stepCreateCashInventoryGroup(self, sequence=None, sequence_list=None, **kwd):
+    """
+    Create a cash inventory document and check it
+    """
+    # Cash inventory has caisse_1 for source, caisse_2 for destination, and a price cooreponding to the sum of banknote of 10000 abd coin of 200 ( (2+3) * 1000 + (5+7) * 200 )
+    self.cash_inventory_group = self.cash_inventory_module.newContent(id='cash_inventory_group', portal_type='Cash Inventory Group', source_value=None, destination_value=self.caisse_1)
+    # execute tic
+    self.stepTic()
+    # check we have only one cash inventory
+    self.assertEqual(len(self.cash_inventory_module.objectValues()), 1)
+    # get the cash inventory document
+    self.cash_inventory = getattr(self.cash_inventory_module, 'cash_inventory_group')
+    # check its portal type
+    self.assertEqual(self.cash_inventory.getPortalType(), 'Cash Inventory Group')
+    # check that its source is caisse_1
+    self.assertEqual(self.cash_inventory.getSource(), None)
+    # check that its destination is caisse_2
+    self.assertEqual(self.cash_inventory.getDestination(), 'site/testsite/caisse_1')
+
+
+  def stepCreateCashInventory(self, sequence=None, sequence_list=None, **kwd):
+    """
+    Create a cash inventory document and check it
+    """
+    # Cash inventory has caisse_1 for source, caisse_2 for destination, and a price cooreponding to the sum of banknote of 10000 abd coin of 200 ( (2+3) * 1000 + (5+7) * 200 )
+    self.cash_inventory = self.cash_inventory_group.newContent(id='cash_inventory', portal_type='Cash Inventory', price_currency='currency_module/EUR')
+    # execute tic
+    self.stepTic()
+    # check we have only one cash inventory
+    self.assertEqual(len(self.cash_inventory_group.objectValues()), 1)
+    # get the cash inventory document
+    self.cash_inventory = getattr(self.cash_inventory_group, 'cash_inventory')
+    # check its portal type
+    self.assertEqual(self.cash_inventory.getPortalType(), 'Cash Inventory')
+    # check that its source is caisse_1
+    self.assertEqual(self.cash_inventory.getSource(), None)
+    # check that its destination is caisse_2
+    self.assertEqual(self.cash_inventory.getDestination(), 'site/testsite/caisse_1')
+
+
+  def stepCreateInventoryLine1(self, sequence=None, sequence_list=None, **kwd):
+    """
+    Create the cash inventory
+    """
+    # create the cash inventory
+    self.addCashLineToDelivery(self.cash_inventory, 'valid_line_1', 'Cash Inventory Line', self.billet_10000,
+            ('emission_letter', 'cash_status', 'variation'), ('emission_letter/k', 'cash_status/valid') + self.variation_list,
+            self.quantity_10000)
+    # execute tic
+    self.stepTic()
+    # check there is only one line created
+    self.assertEqual(len(self.cash_inventory.objectValues()), 1)
+    # get the cash inventory line
+    self.valid_line_1 = getattr(self.cash_inventory, 'valid_line_1')
+    # check its portal type
+    self.assertEqual(self.valid_line_1.getPortalType(), 'Cash Inventory Line')
+    # check the resource is banknotes of 10000
+    self.assertEqual(self.valid_line_1.getResourceValue(), self.billet_10000)
+    # chek the value of the banknote
+    self.assertEqual(self.valid_line_1.getPrice(), 10000.0)
+    # check the unit of banknote
+    self.assertEqual(self.valid_line_1.getQuantityUnit(), 'quantity_unit/unit')
+    # check we have two delivery cells: (one for year 1992 and one for 2003)
+    self.assertEqual(len(self.valid_line_1.objectValues()), 2)
+    # now check for each variation (years 1992 and 2003)
+    for variation in self.variation_list:
+      # get the delivery cell
+      cell = self.valid_line_1.getCell('emission_letter/k', variation, 'cash_status/valid')
+      # chek portal types
+      self.assertEqual(cell.getPortalType(), 'Cash Inventory Cell')
+      # check the banknote of the cell is banknote of 10000
+      self.assertEqual(cell.getResourceValue(), self.billet_10000)
+      # check the source vault is caisse_1
+      self.assertEqual(cell.getSourceValue(), None)
+      # check the destination vault is caisse_2
+      self.assertEqual(cell.getDestinationValue(), self.caisse_1)
+      if cell.getId() == 'movement_0_0_0':
+        # check the quantity of banknote for year 1992 is 2
+        self.assertEqual(cell.getQuantity(), 2.0)
+      elif cell.getId() == 'movement_0_1_0':
+        # check the quantity of banknote for year 2003 is 3
+        self.assertEqual(cell.getQuantity(), 3.0)
+      else:
+        self.fail('Wrong cell created : %s' % cell.getId())        
+
+  def stepCheckSubTotal1(self, sequence=None, sequence_list=None, **kwd):
+    """
+    Check the amount after the creation of cash inventory line 1
+    """
+    # Check number of lines
+    self.assertEqual(len(self.cash_inventory.objectValues()), 1)
+    # Check quantity of banknotes (2 for 1992 and 3 for 2003)
+    self.assertEqual(self.cash_inventory.getTotalQuantity(), 5.0)
+    # Check the total price
+    self.assertEqual(self.cash_inventory.getTotalPrice(), 10000 * 5.0)  
+
+    
+  def stepCheckInventoryForLine1(self, sequence=None, sequence_list=None, **kwd):
+    """
+    Check that compution of inventory at vault caisse_2 is right after confirm and before deliver
+    """
+    self.assertEqual(self.simulation_tool.getCurrentInventory(node=self.caisse_1.getRelativeUrl(), resource = self.billet_10000.getRelativeUrl()), 5.0)
+    self.assertEqual(self.simulation_tool.getFutureInventory(node=self.caisse_1.getRelativeUrl(), resource = self.billet_10000.getRelativeUrl()), 5.0)
+
+    
+  def stepCreateInventoryLine2(self, sequence=None, sequence_list=None, **kwd):
+    """
+    Create the cash inventory line 2 wiht coins of 200 and check it has been well created
+    """
+    # create the line
+    self.addCashLineToDelivery(self.cash_inventory, 'valid_line_2', 'Cash Inventory Line', self.piece_200,
+            ('emission_letter', 'cash_status', 'variation'), ('emission_letter/k', 'cash_status/valid') + self.variation_list,
+            self.quantity_200)
+    # execute tic
+    self.stepTic()
+    # check the number of lines (line1 + line2)
+    self.assertEqual(len(self.cash_inventory.objectValues()), 2)
+    # get the second cash inventory line
+    self.valid_line_2 = getattr(self.cash_inventory, 'valid_line_2')
+    # check portal types
+    self.assertEqual(self.valid_line_2.getPortalType(), 'Cash Inventory Line')
+    # check the resource is coin of 200
+    self.assertEqual(self.valid_line_2.getResourceValue(), self.piece_200)
+    # check the value of coin
+    self.assertEqual(self.valid_line_2.getPrice(), 200.0)
+    # check the unit of coin
+    self.assertEqual(self.valid_line_2.getQuantityUnit(), 'quantity_unit/unit')
+    # check we have two delivery cells: (one for year 1992 and one for 2003)
+    self.assertEqual(len(self.valid_line_2.objectValues()), 2)
+    for variation in self.variation_list:
+      # get the delivery  cell
+      cell = self.valid_line_2.getCell('emission_letter/k', variation, 'cash_status/valid')
+      # check the portal type
+      self.assertEqual(cell.getPortalType(), 'Cash Inventory Cell')
+      # check the banknote of the cell is banknote of 10000
+      self.assertEqual(cell.getResourceValue(), self.piece_200)
+      # check the source vault is caisse_1
+      self.assertEqual(cell.getSourceValue(), None)
+      # check the destination vault is caisse_2
+      self.assertEqual(cell.getDestinationValue(), self.caisse_1)
+      if cell.getId() == 'movement_0_0_0':
+        # check the quantity for coin for year 1992 is 5
+        self.assertEqual(cell.getQuantity(), 5.0)
+      elif cell.getId() == 'movement_0_1_0':
+        # check the quantity for coin for year 2003 is 7
+        self.assertEqual(cell.getQuantity(), 7.0)
+      else:
+        self.fail('Wrong cell created : %s' % cell.getId())
+      
+
+  def stepCheckSubTotal2(self, sequence=None, sequence_list=None, **kwd):
+    """
+    Check the total after the creation of the two cash inventory lines
+    """
+    # Check number of lines (line1 + line2)
+    self.assertEqual(len(self.cash_inventory.objectValues()), 2)
+    # Check quantity, banknotes : 2 for 1992 and 3 for 2003, coin : 5 for 1992 and 7 for 2003
+    self.assertEqual(self.cash_inventory.getTotalQuantity(), 5.0 + 12.0)
+    # check the total price
+    self.assertEqual(self.cash_inventory.getTotalPrice(), 10000 * 5.0 + 200 * 12.0)
+
+
+  def stepCheckInventoryForLine2(self, sequence=None, sequence_list=None, **kwd):
+    """
+    Check that compution of inventory at vault caisse_2 is right after confirm and before deliver
+    """
+    self.assertEqual(self.simulation_tool.getCurrentInventory(node=self.caisse_1.getRelativeUrl(), resource = self.piece_200.getRelativeUrl()), 12.0)
+    self.assertEqual(self.simulation_tool.getFutureInventory(node=self.caisse_1.getRelativeUrl(), resource = self.piece_200.getRelativeUrl()), 12.0)
+
+
+  def stepCreateInventoryLine3(self, sequence=None, sequence_list=None, **kwd):
+    """
+    Create an invalid cash inventory line and
+    check the total with the invalid cash inventory line
+    """
+    # create a line in which quanity of banknotes of 5000 is higher that quantity available at source
+    # here create a line with 24 (11+13) banknotes of 500 although the vault caisse_1 has no banknote of 5000
+    self.addCashLineToDelivery(self.cash_inventory, 'valid_line_3', 'Cash Inventory Line', self.billet_5000,
+            ('emission_letter', 'cash_status', 'variation'), ('emission_letter/k', 'cash_status/valid') + self.variation_list,
+            self.quantity_5000)
+    # execute tic
+    self.stepTic()
+    # check the number of lines (line1 + line2)
+    self.assertEqual(len(self.cash_inventory.objectValues()), 3)
+    # get the second cash inventory line
+    self.valid_line_3 = getattr(self.cash_inventory, 'valid_line_3')
+    # check portal types
+    self.assertEqual(self.valid_line_3.getPortalType(), 'Cash Inventory Line')
+    # check the resource is coin of 200
+    self.assertEqual(self.valid_line_3.getResourceValue(), self.billet_5000)
+    # check the value of coin
+    self.assertEqual(self.valid_line_3.getPrice(), 5000.0)
+    # check the unit of coin
+    self.assertEqual(self.valid_line_3.getQuantityUnit(), 'quantity_unit/unit')
+    # check we have two delivery cells: (one for year 1992 and one for 2003)
+    self.assertEqual(len(self.valid_line_3.objectValues()), 2)
+    for variation in self.variation_list:
+      # get the delivery  cell
+      cell = self.valid_line_3.getCell('emission_letter/k', variation, 'cash_status/valid')
+      # check the portal type
+      self.assertEqual(cell.getPortalType(), 'Cash Inventory Cell')
+      # check the banknote of the cell is banknote of 10000
+      self.assertEqual(cell.getResourceValue(), self.billet_5000)
+      # check the source vault is caisse_1
+      self.assertEqual(cell.getSourceValue(), None)
+      # check the destination vault is caisse_2
+      self.assertEqual(cell.getDestinationValue(), self.caisse_1)
+      if cell.getId() == 'movement_0_0_0':
+        # check the quantity for coin for year 1992 is 5
+        self.assertEqual(cell.getQuantity(), 11.0)
+      elif cell.getId() == 'movement_0_1_0':
+        # check the quantity for coin for year 2003 is 7
+        self.assertEqual(cell.getQuantity(), 13.0)
+      else:
+        self.fail('Wrong cell created : %s' % cell.getId())
+
+  def stepCheckTotal(self, sequence=None, sequence_list=None, **kwd):
+    """
+    Check the total after the creation of the two cash inventory lines
+    """
+    # Check number of lines (line1 + line2)
+    self.assertEqual(len(self.cash_inventory.objectValues()), 3)
+    # Check quantity, banknotes : 2 for 1992 and 3 for 2003, coin : 5 for 1992 and 7 for 2003
+    self.assertEqual(self.cash_inventory.getTotalQuantity(), 5.0 + 12.0 + 24.0)
+    # check the total price
+    self.assertEqual(self.cash_inventory.getTotalPrice(), 10000 * 5.0 + 200 * 12.0 + 5000 * 24)
+
+
+  def stepCheckInventory(self, sequence=None, sequence_list=None, **kwd):
+    """
+    Check that compution of inventory at vault caisse_2 is right after confirm and before deliver
+    """
+    self.assertEqual(self.simulation_tool.getCurrentInventory(node=self.caisse_1.getRelativeUrl(), resource = self.billet_10000.getRelativeUrl()), 5.0)
+    self.assertEqual(self.simulation_tool.getFutureInventory(node=self.caisse_1.getRelativeUrl(), resource = self.billet_10000.getRelativeUrl()), 5.0)
+    
+    self.assertEqual(self.simulation_tool.getCurrentInventory(node=self.caisse_1.getRelativeUrl(), resource = self.piece_200.getRelativeUrl()), 12.0)
+    self.assertEqual(self.simulation_tool.getFutureInventory(node=self.caisse_1.getRelativeUrl(), resource = self.piece_200.getRelativeUrl()), 12.0)
+
+    self.assertEqual(self.simulation_tool.getCurrentInventory(node=self.caisse_1.getRelativeUrl(), resource = self.billet_5000.getRelativeUrl()), 24.0)
+    self.assertEqual(self.simulation_tool.getFutureInventory(node=self.caisse_1.getRelativeUrl(), resource = self.billet_5000.getRelativeUrl()), 24.0)
+
+  ##################################
+  ##  Tests
+  ##################################
+
+  def test_01_ERP5BankingCashInventory(self, quiet=QUIET, run=RUN_ALL_TEST):
+    """
+    Define the sequence of step that will be play
+    """
+    if not run: return
+    sequence_list = SequenceList()
+    # define the sequence
+    sequence_string = 'Tic CheckObjects Tic CheckInitialInventory ' \
+                    + 'CreateCashInventoryGroup CreateCashInventory ' \
+                    + 'CreateInventoryLine1 CheckSubTotal1 CheckInventoryForLine1 ' \
+                    + 'CreateInventoryLine2 CheckSubTotal2 CheckInventoryForLine2 ' \
+                    + 'CreateInventoryLine3 CheckTotal ' \
+                    + 'CheckInventory'
+    sequence_list.addSequenceString(sequence_string)
+    # play the sequence
+    sequence_list.play(self)
+
+# define how we launch the unit test
+if __name__ == '__main__':
+  framework()
+else:
+  import unittest
+  def test_suite():
+    suite = unittest.TestSuite()
+    suite.addTest(unittest.makeSuite(TestERP5BankingCashInventory))
+    return suite
diff --git a/product/ERP5Banking/tests/testERP5BankingCashTransfer.py b/product/ERP5Banking/tests/testERP5BankingCashTransfer.py
new file mode 100755
index 0000000000..e593f19772
--- /dev/null
+++ b/product/ERP5Banking/tests/testERP5BankingCashTransfer.py
@@ -0,0 +1,964 @@
+##############################################################################
+#
+# Copyright (c) 2005-2006 Nexedi SARL and Contributors. All Rights Reserved.
+#                    Alexandre Boeglin <alex_AT_nexedi_DOT_com>
+#                    Kevin Deldycke <kevin_AT_nexedi_DOT_com>
+#                    Aurelien Calonne <aurel@nexedi.com>
+#
+# WARNING: This program as such is intended to be used by professional
+# programmers who take the whole responsability of assessing all potential
+# consequences resulting from its eventual inadequacies and bugs
+# End users who are looking for a ready-to-use solution with commercial
+# garantees and support are strongly adviced to contract a Free Software
+# Service Company
+#
+# 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.
+#
+##############################################################################
+
+
+# import requested python module
+import os
+import AccessControl
+from zLOG import LOG
+from Testing import ZopeTestCase
+from DateTime import DateTime
+from Products.CMFCore.utils import getToolByName
+from Products.ERP5Type.Utils import convertToUpperCase
+from Products.ERP5Type.tests.ERP5TypeTestCase import ERP5TypeTestCase
+from Products.ERP5Type.tests.Sequence import SequenceList
+from AccessControl.SecurityManagement import newSecurityManager
+from Products.DCWorkflow.DCWorkflow import Unauthorized, ValidationFailed
+from Testing.ZopeTestCase.PortalTestCase import PortalTestCase
+
+# Needed in order to have a log file inside the current folder
+os.environ['EVENT_LOG_FILE']     = os.path.join(os.getcwd(), 'zLOG.log')
+# Define the level of log we want, here is all
+os.environ['EVENT_LOG_SEVERITY'] = '-300'
+
+# Define how to launch the script if we don't use runUnitTest script
+if __name__ == '__main__':
+  execfile(os.path.join(sys.path[0], 'framework.py'))
+
+
+
+class TestERP5BankingCashTransfer(ERP5TypeTestCase):
+  """
+    This class is a unit test to check the module of Cash Transfer
+
+    Here are the following step that will be done in the test :
+  
+    - before the test, we need to create some movements that will put resources in the source
+
+    - create a cash transfer
+    - check it has been created correctly
+    - check source and destination (current == future)
+    - check roles (who is author, assignor, assignee, ...)
+
+    - create a "Note Line" (billetage)
+    - check it has been created correctly
+    - check the total amount
+
+    - create a second Line
+    - check it has been created correctly
+    - check the total amount
+
+    - create an invalid Line (quantity > available at source)
+    - check that the system behaves correctly
+
+    - pass "confirm_action" transition
+    - check that we can't pass the transition as another user (depending on roles)
+    - check that the new state is confirmed
+    - check that the source has been debited correctly (current < future)
+
+    - log in as "Controleur" (assignee)
+    - check amount, lines, ...
+
+    - pass "deliver_action" transition
+    - check that we can't pass the transition as another user
+    - check that the new state is delivered
+    - check that the destination has been credited correctly (current == future)
+  """
+
+  login = PortalTestCase.login
+
+  # pseudo constants
+  RUN_ALL_TEST = 1 # we want to run all test
+  QUIET = 0 # we don't want the test to be quiet
+
+
+
+  ##################################
+  ##  ZopeTestCase Skeleton
+  ##################################
+
+  def getTitle(self):
+    """
+      Return the title of the test
+    """
+    return "ERP5BankingCashTransfer"
+
+
+  def getBusinessTemplateList(self):
+    """
+      Return the list of business templates we need to run the test.
+      This method is called during the initialization of the unit test by
+      the unit test framework in order to know which business templates
+      need to be installed to run the test on.
+    """
+    return ( 'erp5_trade'  # erp5_trade is not required to make erp5_banking_cash_transfer working.
+                           # As explained below erp5_trade is just used to help us initialize ressources
+                           #   via Internal Packing List.
+           , 'erp5_banking_core' # erp5_banking_core contains all generic methods for banking
+           , 'erp5_banking_cash_transfer' # erp5_banking_cash contains all method for cash transfer
+           )
+
+
+  def enableLightInstall(self):
+    """
+      Return if we should do a light install (1) or not (0)
+      Light install variable is used at installation of categories in business template
+      to know if we wrap the category or not, if 1 we don't use and installation is faster
+    """
+    return 1 # here we want a light install for a faster installation
+
+
+  def enableActivityTool(self):
+    """
+      Return if we should create (1) or not (0) an activity tool
+      This variable is used at the creation of the site to know if we use
+      the activity tool or not
+    """
+    return 1 # here we want to use the activity tool
+
+
+  def afterSetUp(self):
+    """
+      Method called before the launch of the test to initialize some data
+    """
+    # Set some variables : 
+    # the erp5 site
+    self.portal = self.getPortal()
+    # the cahs transfer module
+    self.cash_transfer_module = self.getCashTransferModule()
+    # the person module
+    self.person_folder = self.getPersonModule()
+    # the organisation module
+    self.organisation_folder = self.getOrganisationModule()
+    # the category tool
+    self.category_tool = self.getCategoryTool()
+
+    # Let us know which user folder is used (PAS or NuxUserGroup)
+    self.checkUserFolderType()
+
+    # Create a user and login as manager to populate the erp5 portal with objects for tests.
+    self.createManagerAndLogin()
+
+    # Define static values (only use prime numbers to prevent confusions like 2 * 6 == 3 * 4)
+    # variation list is the list of years for banknotes and coins
+    self.variation_list = ('variation/1992', 'variation/2003')
+
+    # quantity of banknotes of 10000 :
+    self.quantity_10000 = {}
+    # 2 banknotes of 10000 for the year 1992
+    self.quantity_10000[self.variation_list[0]] = 2
+    # 3 banknotes of 10000 for the year of 2003
+    self.quantity_10000[self.variation_list[1]] = 3
+
+    # quantity of coin of 200
+    self.quantity_200 = {}
+    # 5 coins of 200 for the year 1992
+    self.quantity_200[self.variation_list[0]] = 5
+    # 7 coins of 200 for the year 2003
+    self.quantity_200[self.variation_list[1]] = 7
+
+    # quantity of banknotes of 5000
+    self.quantity_5000 = {}
+    # 11 banknotes of 5000 for hte year 1992
+    self.quantity_5000[self.variation_list[0]] = 11
+    # 13 banknotes of 5000 for the year 2003
+    self.quantity_5000[self.variation_list[1]] = 13
+
+
+    # Create Categories (vaults)
+
+    # as local roles are defined in portal types as real categories, we will need to reproduce (or import) the real category tree
+    # get the base category function
+    self.function_base_category = getattr(self.category_tool, 'function')
+    # add category banking in function which will hold all functions neccessary in a bank (at least for this unit test)
+    self.banking = self.function_base_category.newContent(id='banking', portal_type='Category', codification='BNK')
+    # add category caisier_principal in function banking
+    self.caissier_principal = self.banking.newContent(id='caissier_principal', portal_type='Category', codification='CCP')
+    # add category controleur_caisse in function banking
+    self.controleur_caisse = self.banking.newContent(id='controleur_caisse', portal_type='Category', codification='CCT')
+    # add category void_function in function banking
+    self.void_function = self.banking.newContent(id='void_function', portal_type='Category', codification='VOID')
+    # add category gestionnaire_caisse_courante in function banking
+    self.gestionnaire_caisse_courante = self.banking.newContent(id='gestionnaire_caisse_courante', portal_type='Category', codification='CCO')
+    # add category gestionnaire_caveau in function banking
+    self.gestionnaire_caveau = self.banking.newContent(id='gestionnaire_caveau', portal_type='Category', codification='CCV')
+    # add category caissier_particulier in function banking
+    self.caissier_particulier = self.banking.newContent(id='caissier_particulier', portal_type='Category', codification='CGU')
+
+    # get the base category group
+    self.group_base_category = getattr(self.category_tool, 'group')
+    # add the group baobab in the group category
+    self.baobab = self.group_base_category.newContent(id='baobab', portal_type='Category', codification='BAOBAB')
+
+    # get the base category site
+    self.site_base_category = getattr(self.category_tool, 'site')
+    # add the category testsite in the category site which hold vaults situated in the bank
+    self.testsite = self.site_base_category.newContent(id='testsite', portal_type='Category', codification='TEST')
+    # add vault caisse_1 in testsite
+    self.caisse_1 = self.testsite.newContent(id='caisse_1', portal_type='Category', codification='C1')
+    # add vault caisse_2 in testsite
+    self.caisse_2 = self.testsite.newContent(id='caisse_2', portal_type='Category', codification='C2')
+
+    # get the base category cash_status
+    self.cash_status_base_category = getattr(self.category_tool, 'cash_status')
+    # add the category valid in cash_status which define status of banknotes and coin
+    self.cash_status_valid = self.cash_status_base_category.newContent(id='valid', portal_type='Category')
+
+    # get the base category emission letter
+    self.emission_letter_base_category = getattr(self.category_tool, 'emission_letter')
+    # add the category k in emission letter that will be used fo banknotes and coins
+    self.emission_letter_k = self.emission_letter_base_category.newContent(id='k', portal_type='Category')
+
+    # get the base category variation which hold the year of banknotes and coins
+    self.variation_base_category = getattr(self.category_tool, 'variation')
+    # add the category 1992 in variation
+    self.variation_1992 = self.variation_base_category.newContent(id='1992', portal_type='Category')
+    # add the category 2003 in varitation
+    self.variation_2003 = self.variation_base_category.newContent(id='2003', portal_type='Category')
+
+    # get the base category quantity_unit
+    self.variation_base_category = getattr(self.category_tool, 'quantity_unit')
+    # add category unit in quantity_unit which is the unit that will be used for banknotes and coins
+    self.unit = self.variation_base_category.newContent(id='unit', title='Unit')
+
+    # Create an Organisation that will be used for users assignment
+    self.organisation = self.organisation_folder.newContent(id='baobab_org', portal_type='Organisation',
+        function='banking', group='baobab',  site='testsite')
+
+    # Create some users who will get different roles on the cash transfer.
+    #
+    # Dictionnary data scheme:
+    #     'user_login': [['Global Role'], 'organisation', 'function', 'group', 'site']
+    #
+    user_dict = {
+        'user_1' : [[], self.organisation, 'banking/gestionnaire_caisse_courante', 'baobab', 'testsite']
+      , 'user_2' : [[], self.organisation, 'banking/controleur_caisse' , 'baobab', 'testsite']
+      , 'user_3' : [[], self.organisation, 'banking/void_function'     , 'baobab', 'testsite']
+      }
+    # call method to create this user
+    self.createERP5Users(user_dict)
+
+    # We must assign local roles to cash_transfer_module manually, as they are
+    #   not packed in Business Templates yet.
+    # The local roles must be the one for gestionnaire_caisse_courante
+    if self.PAS_installed:
+      # in case of use of PAS
+      self.cash_transfer_module.manage_addLocalRoles('CCO_BAOBAB_TEST', ('Author',))
+    else:
+      # in case of NuxUserGroup
+      self.cash_transfer_module.manage_addLocalGroupRoles('CCO_BAOBAB_TEST', ('Author',))
+
+    # get the currency module
+    self.currency_module = self.getCurrencyModule()
+    # create the currency document for euro inside the currency module
+    self.currency_1 = self .currency_module.newContent(id='EUR', title='Euro')
+
+    # Create Resources (Banknotes & Coins)
+    # get the currency cash module
+    self.currency_cash_module = self.getCurrencyCashModule()
+    # create document for banknote of 10000 euros from years 1992 and 2003
+    self.billet_10000 = self.currency_cash_module.newContent(id='billet_10000', portal_type='Banknote', base_price=10000, price_currency_value=self.currency_1, variation_list=('1992', '2003'), quantity_unit_value=self.unit)
+    # create document for banknote of 500 euros from years 1992 and 2003
+    self.billet_5000 = self.currency_cash_module.newContent(id='billet_5000', portal_type='Banknote', base_price=5000, price_currency_value=self.currency_1, variation_list=('1992', '2003'), quantity_unit_value=self.unit)
+    # create docuemnt for coin of 200 euros from years 1992 and 2003
+    self.piece_200 = self.currency_cash_module.newContent(id='piece_200', portal_type='Coin', base_price=200, price_currency_value=self.currency_1, variation_list=('1992', '2003'), quantity_unit_value=self.unit)
+
+    # Before the test, we need to create resources in the source.
+    # Using internal_packing_list from erp5_trade is the easiest.
+    # get the internal packing list module
+    self.internal_packing_list_module = self.getInternalPackingListModule()
+    # add a new internal packing list for caisse_1
+    self.internal_packing_list = self.internal_packing_list_module.newContent(id='packing_list_1', portal_type='Internal Packing List',
+            source=None, destination_value=self.caisse_1)
+    # add a line for banknotes of 10000 with emission letter k, status valid and from years 1992 and 2003 with the quantity defined
+    # before in quantity_10000 (2 for 1992 and 3 for 2003)
+    self.addCashLineToDelivery(self.internal_packing_list, 'delivery_init_1', 'Internal Packing List Line', self.billet_10000,
+            ('emission_letter', 'cash_status', 'variation'), ('emission_letter/k', 'cash_status/valid') + self.variation_list,
+            self.quantity_10000)
+    # add a line for coins of 200 with emission letter k, status valid and from years 1992 and 2003 with the quantity defined
+    # before in quantity_200 (5 for 1992 and 7 for 2003)
+    self.addCashLineToDelivery(self.internal_packing_list, 'delivery_init_2', 'Internal Packing List Line', self.piece_200,
+            ('emission_letter', 'cash_status', 'variation'), ('emission_letter/k', 'cash_status/valid') + self.variation_list,
+            self.quantity_200)
+
+    # logout from manager
+    self.logout()
+    # Finally, login as user_1
+    self.login('user_1')
+
+
+  def checkUserFolderType(self, quiet=QUIET, run=RUN_ALL_TEST):
+    """
+      Check the type of user folder to let the test working with both NuxUserGroup and PAS.
+    """
+    self.user_folder = self.getUserFolder()
+    self.PAS_installed = 0
+    if self.user_folder.meta_type == 'Pluggable Auth Service':
+      # we use PAS
+      self.PAS_installed = 1
+
+
+  def assignPASRolesToUser(self, user_name, role_list, quiet=QUIET, run=RUN_ALL_TEST):
+    """
+      Assign a list of roles to one user with PAS.
+    """
+    for role in role_list:
+      if role not in self.user_folder.zodb_roles.listRoleIds():
+        self.user_folder.zodb_roles.addRole(role)
+      self.user_folder.zodb_roles.assignRoleToPrincipal(role, user_name)
+
+
+  def createManagerAndLogin(self, quiet=QUIET, run=RUN_ALL_TEST):
+    """
+      Create a simple user in user_folder with manager rights.
+      This user will be used to initialize data in the method afterSetup
+    """
+    self.getUserFolder()._doAddUser('manager', '', ['Manager'], [])
+    self.login('manager')
+
+
+  def createERP5Users(self, user_dict, quiet=QUIET, run=RUN_ALL_TEST):
+    """
+      Create all ERP5 users needed for the test.
+      ERP5 user = Person object + Assignment object in erp5 person_module.
+    """
+    for user_login, user_data in user_dict.items():
+      user_roles = user_data[0]
+      # Create the Person.
+      person = self.person_folder.newContent(id=user_login,
+          portal_type='Person', reference=user_login, career_role="internal")
+      # Create the Assignment.
+      assignment = person.newContent( portal_type       = 'Assignment'
+                                    , destination_value = user_data[1]
+                                    , function          = user_data[2]
+                                    , group             = user_data[3]
+                                    , site              = user_data[4]
+                                    )
+      if self.PAS_installed and len(user_roles) > 0:
+        # In the case of PAS, if we want global roles on user, we have to do it manually.
+        self.assignPASRolesToUser(user_login, user_roles)
+      elif not self.PAS_installed:
+        # The user_folder counterpart of the erp5 user must be
+        #   created manually in the case of NuxUserGroup.
+        self.user_folder.userFolderAddUser( name     = user_login
+                                          , password = ''
+                                          , roles    = user_roles
+                                          , domains  = []
+                                          )
+      # User assignment to security groups is also required, but is taken care of
+      #   by the assignment workflow when NuxUserGroup is used and
+      #   by ERP5Security PAS plugins in the context of PAS use.
+      assignment.open()
+      
+    if self.PAS_installed:
+      # reindexing is required for the security to work
+      get_transaction().commit()
+      self.tic()
+
+
+  ##################################
+  ##  Usefull methods
+  ##################################
+
+  def addCashLineToDelivery(self, delivery_object, line_id, line_portal_type, resource_object,
+          variation_base_category_list, variation_category_list, resource_quantity_dict):
+    """
+    Add a cash line to a delivery
+    This will add an Internal Packing List Line to a Internal Packing List
+    """
+    base_id = 'movement'
+    line_kwd = {'base_id':base_id}
+    # create the cash line
+    line = delivery_object.newContent( id                  = line_id
+                                     , portal_type         = line_portal_type
+                                     , resource_value      = resource_object # banknote or coin
+                                     , quantity_unit_value = self.unit
+                                     )
+    # set base category list on line
+    line.setVariationBaseCategoryList(variation_base_category_list)
+    # set category list line
+    line.setVariationCategoryList(variation_category_list)
+    line.updateCellRange(script_id='CashDetail_asCellRange', base_id=base_id)
+    cell_range_key_list = line.getCellRangeKeyList(base_id=base_id)
+    if cell_range_key_list <> [[None, None]] :
+      for k in cell_range_key_list:
+        category_list = filter(lambda k_item: k_item is not None, k)
+        c = line.newCell(*k, **line_kwd)
+        mapped_value_list = ['price', 'quantity']
+        c.edit( membership_criterion_category_list = category_list
+              , mapped_value_property_list         = mapped_value_list
+              , category_list                      = category_list
+              , force_update                       = 1
+              )
+    # set quantity on cell to define quantity of bank notes / coins
+    for variation in self.variation_list:
+      cell = line.getCell('emission_letter/k', variation, 'cash_status/valid')
+      cell.setQuantity(resource_quantity_dict[variation])
+
+
+  def getUserFolder(self):
+    """
+    Return the user folder
+    """
+    return getattr(self.getPortal(), 'acl_users', None)
+
+  def getPersonModule(self):
+    """
+    Return the person module
+    """
+    return getattr(self.getPortal(), 'person_module', None)
+  
+  def getOrganisationModule(self):
+    """
+    Return the organisation module
+    """
+    return getattr(self.getPortal(), 'organisation_module', None)
+  
+  def getCurrencyCashModule(self):
+    """
+    Return the Currency Cash Module
+    """
+    return getattr(self.getPortal(), 'currency_cash_module', None)
+  
+  def getCashTransferModule(self):
+    """
+    Return the Cash Transer Module
+    """
+    return getattr(self.getPortal(), 'cash_transfer_module', None)
+  
+  def getInternalPackingListModule(self):
+    """
+    Return the Internal Packing List Module
+    """
+    return getattr(self.getPortal(), 'internal_packing_list_module', None)
+  
+  def getCurrencyModule(self):
+    """
+    Return the Currency Module
+    """
+    return getattr(self.getPortal(), 'currency_module', None)
+  
+  def getCategoryTool(self):
+    """
+    Return the Category Tool
+    """
+    return getattr(self.getPortal(), 'portal_categories', None)
+  
+  def getWorkflowTool(self):
+    """
+    Return the Worklfow Tool
+    """
+    return getattr(self.getPortal(), 'portal_workflow', None)
+  
+  def getSimulationTool(self):
+    """
+    Return the Simulation Tool
+    """
+    return getattr(self.getPortal(), 'portal_simulation', None)
+
+
+
+  ##################################
+  ##  Basic steps
+  ##################################
+
+  def stepTic(self, **kwd):
+    """
+    The is used to simulate the zope_tic_loop script
+    Each time this method is called, it simulates a call to tic
+    which invoke activities in the Activity Tool
+    """
+    # execute transaction
+    get_transaction().commit()
+    self.tic()
+
+
+  def stepCheckObjects(self, sequence=None, sequence_list=None, **kwd):
+    """
+    Check that all the objects we created in afterSetUp or
+    that were added by the business template and that we rely
+    on are really here.
+    """
+    # check that Categories were created
+    self.assertEqual(self.caisse_1.getPortalType(), 'Category')
+    self.assertEqual(self.caisse_2.getPortalType(), 'Category')
+
+    # check that Resources were created
+    # check portal type of billet_10000
+    self.assertEqual(self.billet_10000.getPortalType(), 'Banknote')
+    # check value of billet_10000
+    self.assertEqual(self.billet_10000.getBasePrice(), 10000)
+    # check currency value  of billet_10000
+    self.assertEqual(self.billet_10000.getPriceCurrency(), 'currency_module/EUR')
+    # check years  of billet_10000
+    self.assertEqual(self.billet_10000.getVariationList(), ['1992', '2003'])
+
+    # check portal type of billet_5000
+    self.assertEqual(self.billet_5000.getPortalType(), 'Banknote')
+    # check value of billet_5000
+    self.assertEqual(self.billet_5000.getBasePrice(), 5000)
+    # check currency value  of billet_5000
+    self.assertEqual(self.billet_5000.getPriceCurrency(), 'currency_module/EUR')
+    # check years  of billet_5000
+    self.assertEqual(self.billet_5000.getVariationList(), ['1992', '2003'])
+    
+    # check portal type of piece_200
+    self.assertEqual(self.piece_200.getPortalType(), 'Coin')
+    # check value of piece_200
+    self.assertEqual(self.piece_200.getBasePrice(), 200)
+    # check currency value  of piece_200
+    self.assertEqual(self.piece_200.getPriceCurrency(), 'currency_module/EUR')
+    # check years  of piece_200
+    self.assertEqual(self.piece_200.getVariationList(), ['1992', '2003'])
+
+    # check that CashTransfer Module was created
+    self.assertEqual(self.cash_transfer_module.getPortalType(), 'Cash Transfer Module')
+    # check cash transfer module is empty
+    self.assertEqual(len(self.cash_transfer_module.objectValues()), 0)
+
+
+  def stepCheckInitialInventory(self, sequence=None, sequence_list=None, **kwd):
+    """
+    Check the initial inventory before any operations
+    """
+    self.simulation_tool = self.getSimulationTool()
+    # check we have 5 banknotes of 10000 in caisse_1
+    self.assertEqual(self.simulation_tool.getCurrentInventory(node=self.caisse_1.getRelativeUrl(), resource = self.billet_10000.getRelativeUrl()), 5.0)
+    self.assertEqual(self.simulation_tool.getFutureInventory(node=self.caisse_1.getRelativeUrl(), resource = self.billet_10000.getRelativeUrl()), 5.0)
+    # check we have 12 coin of 200 in caisse_1
+    self.assertEqual(self.simulation_tool.getCurrentInventory(node=self.caisse_1.getRelativeUrl(), resource = self.piece_200.getRelativeUrl()), 12.0)
+    self.assertEqual(self.simulation_tool.getFutureInventory(node=self.caisse_1.getRelativeUrl(), resource = self.piece_200.getRelativeUrl()), 12.0)
+
+
+  def stepCheckSource(self, sequence=None, sequence_list=None, **kwd):
+    """
+    Check inventory in source vault (caisse_1) before a confirm
+    """
+    # check we have 5 banknotes of 10000
+    self.assertEqual(self.simulation_tool.getCurrentInventory(node=self.caisse_1.getRelativeUrl(), resource = self.billet_10000.getRelativeUrl()), 5.0)
+    self.assertEqual(self.simulation_tool.getFutureInventory(node=self.caisse_1.getRelativeUrl(), resource = self.billet_10000.getRelativeUrl()), 5.0)
+    # check we have 12 coin of 200
+    self.assertEqual(self.simulation_tool.getCurrentInventory(node=self.caisse_1.getRelativeUrl(), resource = self.piece_200.getRelativeUrl()), 12.0)
+    self.assertEqual(self.simulation_tool.getFutureInventory(node=self.caisse_1.getRelativeUrl(), resource = self.piece_200.getRelativeUrl()), 12.0)
+
+
+  def stepCheckDestination(self, sequence=None, sequence_list=None, **kwd):
+    """
+    Check inventory in destination vault (caisse_2) before confirm
+    """
+    # check we don't have banknotes of 10000
+    self.assertEqual(self.simulation_tool.getCurrentInventory(node=self.caisse_2.getRelativeUrl(), resource = self.billet_10000.getRelativeUrl()), 0.0)
+    self.assertEqual(self.simulation_tool.getFutureInventory(node=self.caisse_2.getRelativeUrl(), resource = self.billet_10000.getRelativeUrl()), 0.0)
+    # check we don't have coins of 200
+    self.assertEqual(self.simulation_tool.getCurrentInventory(node=self.caisse_2.getRelativeUrl(), resource = self.piece_200.getRelativeUrl()), 0.0)
+    self.assertEqual(self.simulation_tool.getFutureInventory(node=self.caisse_2.getRelativeUrl(), resource = self.piece_200.getRelativeUrl()), 0.0)
+
+
+  def stepCreateCashTransfer(self, sequence=None, sequence_list=None, **kwd):
+    """
+    Create a cash transfer document and check it
+    """
+    # Cash transfer has caisse_1 for source, caisse_2 for destination, and a price cooreponding to the sum of banknote of 10000 abd coin of 200 ( (2+3) * 1000 + (5+7) * 200 )
+    self.cash_transfer = self.cash_transfer_module.newContent(id='cash_transfer_1', portal_type='Cash Transfer', source_value=self.caisse_1, destination_value=self.caisse_2, source_total_asset_price=52400.0)
+    # execute tic
+    self.stepTic()
+    # check we have only one cash transfer
+    self.assertEqual(len(self.cash_transfer_module.objectValues()), 1)
+    # get the cash transfer document
+    self.cash_transfer = getattr(self.cash_transfer_module, 'cash_transfer_1')
+    # check its portal type
+    self.assertEqual(self.cash_transfer.getPortalType(), 'Cash Transfer')
+    # check that its source is caisse_1
+    self.assertEqual(self.cash_transfer.getSource(), 'site/testsite/caisse_1')
+    # check that its destination is caisse_2
+    self.assertEqual(self.cash_transfer.getDestination(), 'site/testsite/caisse_2')
+    #XXX Check roles were correctly affected
+    #self.security_manager = AccessControl.getSecurityManager()
+    #self.user = self.security_manager.getUser()
+    #raise 'alex', repr( self.cash_transfer.get_local_roles() )
+
+
+  def stepCreateValidLine1(self, sequence=None, sequence_list=None, **kwd):
+    """
+    Create the cash transfer line 1 with banknotes of 10000 and check it has been well created
+    """
+    # create the cash transfer line
+    self.addCashLineToDelivery(self.cash_transfer, 'valid_line_1', 'Cash Delivery Line', self.billet_10000,
+            ('emission_letter', 'cash_status', 'variation'), ('emission_letter/k', 'cash_status/valid') + self.variation_list,
+            self.quantity_10000)
+    # execute tic
+    self.stepTic()
+    # check there is only one line created
+    self.assertEqual(len(self.cash_transfer.objectValues()), 1)
+    # get the cash transfer line
+    self.valid_line_1 = getattr(self.cash_transfer, 'valid_line_1')
+    # check its portal type
+    self.assertEqual(self.valid_line_1.getPortalType(), 'Cash Delivery Line')
+    # check the resource is banknotes of 10000
+    self.assertEqual(self.valid_line_1.getResourceValue(), self.billet_10000)
+    # chek the value of the banknote
+    self.assertEqual(self.valid_line_1.getPrice(), 10000.0)
+    # check the unit of banknote
+    self.assertEqual(self.valid_line_1.getQuantityUnit(), 'quantity_unit/unit')
+    # check we have two delivery cells: (one for year 1992 and one for 2003)
+    self.assertEqual(len(self.valid_line_1.objectValues()), 2)
+    # now check for each variation (years 1992 and 2003)
+    for variation in self.variation_list:
+      # get the delivery cell
+      cell = self.valid_line_1.getCell('emission_letter/k', variation, 'cash_status/valid')
+      # chek portal types
+      self.assertEqual(cell.getPortalType(), 'Cash Delivery Cell')
+      # check the banknote of the cell is banknote of 10000
+      self.assertEqual(cell.getResourceValue(), self.billet_10000)
+      # check the source vault is caisse_1
+      self.assertEqual(cell.getSourceValue(), self.caisse_1)
+      # check the destination vault is caisse_2
+      self.assertEqual(cell.getDestinationValue(), self.caisse_2)
+      if cell.getId() == 'movement_0_0_0':
+        # check the quantity of banknote for year 1992 is 2
+        self.assertEqual(cell.getQuantity(), 2.0)
+      elif cell.getId() == 'movement_0_1_0':
+        # check the quantity of banknote for year 2003 is 3
+        self.assertEqual(cell.getQuantity(), 3.0)
+      else:
+        self.fail('Wrong cell created : %s' % cell.getId())
+
+
+  def stepCheckSubTotal(self, sequence=None, sequence_list=None, **kwd):
+    """
+    Check the amount after the creation of cash transfer line 1
+    """
+    # Check number of lines
+    self.assertEqual(len(self.cash_transfer.objectValues()), 1)
+    # Check quantity of banknotes (2 for 1992 and 3 for 2003)
+    self.assertEqual(self.cash_transfer.getTotalQuantity(), 5.0)
+    # Check the total price
+    self.assertEqual(self.cash_transfer.getTotalPrice(), 10000 * 5.0)
+
+
+  def stepCreateValidLine2(self, sequence=None, sequence_list=None, **kwd):
+    """
+    Create the cash transfer line 2 wiht coins of 200 and check it has been well created
+    """
+    # create the line
+    self.addCashLineToDelivery(self.cash_transfer, 'valid_line_2', 'Cash Delivery Line', self.piece_200,
+            ('emission_letter', 'cash_status', 'variation'), ('emission_letter/k', 'cash_status/valid') + self.variation_list,
+            self.quantity_200)
+    # execute tic
+    self.stepTic()
+    # check the number of lines (line1 + line2)
+    self.assertEqual(len(self.cash_transfer.objectValues()), 2)
+    # get the second cash transfer line
+    self.valid_line_2 = getattr(self.cash_transfer, 'valid_line_2')
+    # check portal types
+    self.assertEqual(self.valid_line_2.getPortalType(), 'Cash Delivery Line')
+    # check the resource is coin of 200
+    self.assertEqual(self.valid_line_2.getResourceValue(), self.piece_200)
+    # check the value of coin
+    self.assertEqual(self.valid_line_2.getPrice(), 200.0)
+    # check the unit of coin
+    self.assertEqual(self.valid_line_2.getQuantityUnit(), 'quantity_unit/unit')
+    # check we have two delivery cells: (one for year 1992 and one for 2003)
+    self.assertEqual(len(self.valid_line_2.objectValues()), 2)
+    for variation in self.variation_list:
+      # get the delivery  cell
+      cell = self.valid_line_2.getCell('emission_letter/k', variation, 'cash_status/valid')
+      # check the portal type
+      self.assertEqual(cell.getPortalType(), 'Cash Delivery Cell')
+      if cell.getId() == 'movement_0_0_0':
+        # check the quantity for coin for year 1992 is 5
+        self.assertEqual(cell.getQuantity(), 5.0)
+      elif cell.getId() == 'movement_0_1_0':
+        # check the quantity for coin for year 2003 is 7
+        self.assertEqual(cell.getQuantity(), 7.0)
+      else:
+        self.fail('Wrong cell created : %s' % cell.getId())
+
+        
+  
+  def stepTryConfirmCashTransferWithBadUser(self, sequence=None, sequence_list=None, **kwd):
+    """
+    Try to confirm cash transfer with a user that doesn't have the right and
+    check that the try of confirm by a bad user doesn't change the cash transfer
+    """
+    # logout from user_1
+    self.logout()
+    # log in as bad user
+    self.login('user_3')
+    # get workflow tool
+    self.workflow_tool = self.getWorkflowTool()
+    # check that an Unauthorized exception is raised when trying to confirm the cash transfer
+    self.assertRaises(Unauthorized, self.workflow_tool.doActionFor, self.cash_transfer, 'confirm_action', wf_id='cash_transfer_workflow')
+    # logout from user_3
+    self.logout()
+    # login as default user
+    self.login('user_1')
+    # execute tic
+    self.stepTic()
+    # get state of the cash transfer
+    state = self.cash_transfer.getSimulationState()
+    # check it has remain as draft
+    self.assertEqual(state, 'draft')
+    # get the workflow history
+    workflow_history = self.workflow_tool.getInfoFor(ob=self.cash_transfer, name='history', wf_id='cash_transfer_workflow')
+    # check its len is one
+    self.assertEqual(len(workflow_history), 1)
+
+
+  def stepCreateInvalidLine(self, sequence=None, sequence_list=None, **kwd):
+    """
+    Create an invalid cash transfer line and
+    check the total with the invalid cash transfer line
+    """
+    # create a line in which quanity of banknotes of 5000 is higher that quantity available at source
+    # here create a line with 24 (11+13) banknotes of 500 although the vault caisse_1 has no banknote of 5000
+    self.addCashLineToDelivery(self.cash_transfer, 'invalid_line', 'Cash Delivery Line', self.billet_5000,
+            ('emission_letter', 'cash_status', 'variation'), ('emission_letter/k', 'cash_status/valid') + self.variation_list,
+            self.quantity_5000)
+    # execute tic
+    self.stepTic()
+    # Check number of cash transfer lines (line1 + line2 +invalid_line)
+    self.assertEqual(len(self.cash_transfer.objectValues()), 3)
+    # Check quantity, same as checkTotal + banknote of 500: 11 for 1992 and 13 for 2003
+    self.assertEqual(self.cash_transfer.getTotalQuantity(), 5.0 + 12.0 + 24)
+    # chect the total price
+    self.assertEqual(self.cash_transfer.getTotalPrice(), 10000 * 5.0 + 200 * 12.0 + 5000 * 24)
+
+
+  def stepTryConfirmCashTransferWithBadInventory(self, sequence=None, sequence_list=None, **kwd):
+    """
+    Try to confirm the cash transfer with a bad cash transfer line and
+    check the try of confirm the cash transfer with the invalid line has failed
+    """
+    # fix amount (10000 * 5.0 + 200 * 12.0 + 5000 * 24)
+    self.cash_transfer.setSourceTotalAssetPrice('172400.0')
+    # try to do the workflow action "confirm_action', cath the exception ValidationFailed raised by workflow transition 
+    self.assertRaises(ValidationFailed, self.workflow_tool.doActionFor, self.cash_transfer, 'confirm_action', wf_id='cash_transfer_workflow')
+    # execute tic
+    self.stepTic()
+    # get state of the cash transfer
+    state = self.cash_transfer.getSimulationState()
+    # check the state is draft
+    self.assertEqual(state, 'draft')
+    # get workflow history
+    workflow_history = self.workflow_tool.getInfoFor(ob=self.cash_transfer, name='history', wf_id='cash_transfer_workflow')
+    # check its len is 2
+    self.assertEqual(len(workflow_history), 2)
+    # check we get an "Insufficient balance" message in the workflow history because of the invalid line
+    LOG('last worklfow history', 0, workflow_history[-1])
+    self.assertEqual('Insufficient balance' in workflow_history[-1]['error_message'], True)
+
+
+  def stepDelInvalidLine(self, sequence=None, sequence_list=None, **kwd):
+    """
+    Delete the invalid cash transfer line previously create
+    """
+    self.cash_transfer.deleteContent('invalid_line')
+
+
+  def stepCheckTotal(self, sequence=None, sequence_list=None, **kwd):
+    """
+    Check the total after the creation of the two cash transfer lines
+    """
+    # Check number of lines (line1 + line2)
+    self.assertEqual(len(self.cash_transfer.objectValues()), 2)
+    # Check quantity, banknotes : 2 for 1992 and 3 for 2003, coin : 5 for 1992 and 7 for 2003
+    self.assertEqual(self.cash_transfer.getTotalQuantity(), 5.0 + 12.0)
+    # check the total price
+    self.assertEqual(self.cash_transfer.getTotalPrice(), 10000 * 5.0 + 200 * 12.0)
+
+
+  def stepConfirmCashTransfer(self, sequence=None, sequence_list=None, **kwd):
+    """
+    Confirm the cash transfer and check it
+    """
+    # fix amount (10000 * 5.0 + 200 * 12.0)
+    self.cash_transfer.setSourceTotalAssetPrice('52400.0')
+    # do the Workflow action
+    self.workflow_tool.doActionFor(self.cash_transfer, 'confirm_action', wf_id='cash_transfer_workflow')
+    # execute tic
+    self.stepTic()
+    # get state
+    state = self.cash_transfer.getSimulationState()
+    # check state is confirmed
+    self.assertEqual(state, 'confirmed')
+    # get workflow history
+    workflow_history = self.workflow_tool.getInfoFor(ob=self.cash_transfer, name='history', wf_id='cash_transfer_workflow')
+    # check len of workflow history is 4
+    self.assertEqual(len(workflow_history), 4)
+
+
+  def stepCheckSourceDebitPlanned(self, sequence=None, sequence_list=None, **kwd):
+    """
+    Check that compution of inventory at vault caisse_1 is right after confirm and before deliver 
+    """
+    # check we have 5 banknotes of 10000 currently
+    self.assertEqual(self.simulation_tool.getCurrentInventory(node=self.caisse_1.getRelativeUrl(), resource = self.billet_10000.getRelativeUrl()), 5.0)
+    # check we will have 0 banknote of 10000 after deliver
+    self.assertEqual(self.simulation_tool.getFutureInventory(node=self.caisse_1.getRelativeUrl(), resource = self.billet_10000.getRelativeUrl()), 0.0)
+    # check we have 12 coins of 200 currently
+    self.assertEqual(self.simulation_tool.getCurrentInventory(node=self.caisse_1.getRelativeUrl(), resource = self.piece_200.getRelativeUrl()), 12.0)
+    # check we will have 0 coin of 200 after deliver
+    self.assertEqual(self.simulation_tool.getFutureInventory(node=self.caisse_1.getRelativeUrl(), resource = self.piece_200.getRelativeUrl()), 0.0)
+
+
+  def stepCheckDestinationCreditPlanned(self, sequence=None, sequence_list=None, **kwd):
+    """
+    Check that compution of inventory at vault caisse_2 is right after confirm and before deliver
+    """
+    # check we have 0 banknote of 10000 currently
+    self.assertEqual(self.simulation_tool.getCurrentInventory(node=self.caisse_2.getRelativeUrl(), resource = self.billet_10000.getRelativeUrl()), 0.0)
+    # check we will have 5 banknotes of 10000 after deliver
+    self.assertEqual(self.simulation_tool.getFutureInventory(node=self.caisse_2.getRelativeUrl(), resource = self.billet_10000.getRelativeUrl()), 5.0)
+    # check we have 0 coin of 200 currently
+    self.assertEqual(self.simulation_tool.getCurrentInventory(node=self.caisse_2.getRelativeUrl(), resource = self.piece_200.getRelativeUrl()), 0.0)
+    # check we will have 12 coins of 200 after deliver
+    self.assertEqual(self.simulation_tool.getFutureInventory(node=self.caisse_2.getRelativeUrl(), resource = self.piece_200.getRelativeUrl()), 12.0)
+
+
+  def stepTryDeliverCashTransferWithBadUser(self, sequence=None, sequence_list=None, **kwd):
+    """
+    Try to deliver a cash transfer with a user that doesn't have the right
+    and check that it failed
+    """
+    # logout from user_1
+    self.logout()
+    # log in as bad user
+    self.login('user_3')
+    # check we raise an Unauthorized Exception if we try to deliver cash transfer
+    self.assertRaises(Unauthorized, self.workflow_tool.doActionFor, self.cash_transfer, 'deliver_action', wf_id='cash_transfer_workflow')
+    # logout from bad user
+    self.logout()
+    # log in as default user
+    self.login('user_1')
+    # execute tic
+    self.stepTic()
+    # get state of the cash transfer
+    state = self.cash_transfer.getSimulationState()
+    # check that state is confirmed
+    self.assertEqual(state, 'confirmed')
+    # get workflow history
+    workflow_history = self.workflow_tool.getInfoFor(ob=self.cash_transfer, name='history', wf_id='cash_transfer_workflow')
+    # check len of workflow history is 4
+    self.assertEqual(len(workflow_history), 4)
+
+
+  def stepDeliverCashTransfer(self, sequence=None, sequence_list=None, **kwd):
+    """
+    Deliver the cash transfer with a good user
+    and check that the deliver of a cash tranfer have achieved
+    """
+    # logout from user_1
+    self.logout()
+    # log in as good user (controleur_caisse)
+    self.login('user_2')
+    #     self.security_manager = AccessControl.getSecurityManager()
+    #     self.user = self.security_manager.getUser()
+    # do the workflow transition "deliver_action"
+    self.workflow_tool.doActionFor(self.cash_transfer, 'deliver_action', wf_id='cash_transfer_workflow')
+    # logout from user_2
+    self.logout()
+    # log in as default user
+    self.login('user_1')
+    # execute tic
+    self.stepTic()
+    # get state of cash transfer
+    state = self.cash_transfer.getSimulationState()
+    # check that state is delivered
+    self.assertEqual(state, 'delivered')
+    # get workflow history
+    workflow_history = self.workflow_tool.getInfoFor(ob=self.cash_transfer, name='history', wf_id='cash_transfer_workflow')
+    # check len of len workflow history is 6
+    self.assertEqual(len(workflow_history), 6)
+    
+
+  def stepCheckSourceDebit(self, sequence=None, sequence_list=None, **kwd):
+    """
+    Check inventory at source (vault caisse_1) after deliver of the cash transfer
+    """
+    # check we have 0 banknote of 10000
+    self.assertEqual(self.simulation_tool.getCurrentInventory(node=self.caisse_1.getRelativeUrl(), resource = self.billet_10000.getRelativeUrl()), 0.0)
+    self.assertEqual(self.simulation_tool.getFutureInventory(node=self.caisse_1.getRelativeUrl(), resource = self.billet_10000.getRelativeUrl()), 0.0)
+    # check we have 0 coin of 200
+    self.assertEqual(self.simulation_tool.getCurrentInventory(node=self.caisse_1.getRelativeUrl(), resource = self.piece_200.getRelativeUrl()), 0.0)
+    self.assertEqual(self.simulation_tool.getFutureInventory(node=self.caisse_1.getRelativeUrl(), resource = self.piece_200.getRelativeUrl()), 0.0)
+
+
+  def stepCheckDestinationCredit(self, sequence=None, sequence_list=None, **kwd):
+    """
+    Check inventory at destination (vault caisse_2) after deliver of the cash transfer
+    """
+    # check we have 5 banknotes of 10000
+    self.assertEqual(self.simulation_tool.getCurrentInventory(node=self.caisse_2.getRelativeUrl(), resource = self.billet_10000.getRelativeUrl()), 5.0)
+    self.assertEqual(self.simulation_tool.getFutureInventory(node=self.caisse_2.getRelativeUrl(), resource = self.billet_10000.getRelativeUrl()), 5.0)
+    # check we have 12 coins of 200
+    self.assertEqual(self.simulation_tool.getCurrentInventory(node=self.caisse_2.getRelativeUrl(), resource = self.piece_200.getRelativeUrl()), 12.0)
+    self.assertEqual(self.simulation_tool.getFutureInventory(node=self.caisse_2.getRelativeUrl(), resource = self.piece_200.getRelativeUrl()), 12.0)
+
+
+  ##################################
+  ##  Tests
+  ##################################
+
+  def test_01_ERP5BankingCashTransfer(self, quiet=QUIET, run=RUN_ALL_TEST):
+    """
+    Define the sequence of step that will be play
+    """
+    if not run: return
+    sequence_list = SequenceList()
+    # define the sequence
+    sequence_string = 'Tic CheckObjects Tic CheckInitialInventory CheckSource CheckDestination ' \
+                    + 'CreateCashTransfer ' \
+                    + 'CreateValidLine1 CheckSubTotal ' \
+                    + 'CreateValidLine2 CheckTotal ' \
+                    + 'TryConfirmCashTransferWithBadUser ' \
+                    + 'CheckSource CheckDestination ' \
+                    + 'CreateInvalidLine ' \
+                    + 'TryConfirmCashTransferWithBadInventory ' \
+                    + 'DelInvalidLine Tic CheckTotal ' \
+                    + 'ConfirmCashTransfer ' \
+                    + 'CheckSourceDebitPlanned CheckDestinationCreditPlanned ' \
+                    + 'TryDeliverCashTransferWithBadUser ' \
+                    + 'CheckSourceDebitPlanned CheckDestinationCreditPlanned ' \
+                    + 'DeliverCashTransfer ' \
+                    + 'CheckSourceDebit CheckDestinationCredit '
+    sequence_list.addSequenceString(sequence_string)
+    # play the sequence
+    sequence_list.play(self)
+
+# define how we launch the unit test
+if __name__ == '__main__':
+  framework()
+else:
+  import unittest
+  def test_suite():
+    suite = unittest.TestSuite()
+    suite.addTest(unittest.makeSuite(TestERP5BankingCashTransfer))
+    return suite
diff --git a/product/ERP5Banking/tests/testERP5BankingCheckPayment.py b/product/ERP5Banking/tests/testERP5BankingCheckPayment.py
new file mode 100755
index 0000000000..d4b6327fa6
--- /dev/null
+++ b/product/ERP5Banking/tests/testERP5BankingCheckPayment.py
@@ -0,0 +1,717 @@
+##############################################################################
+#
+# Copyright (c) 2005-2006 Nexedi SARL and Contributors. All Rights Reserved.
+#                    Yoshinori Okuji <yo@nexedi.com>
+#
+# WARNING: This program as such is intended to be used by professional
+# programmers who take the whole responsability of assessing all potential
+# consequences resulting from its eventual inadequacies and bugs
+# End users who are looking for a ready-to-use solution with commercial
+# garantees and support are strongly adviced to contract a Free Software
+# Service Company
+#
+# 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.
+#
+##############################################################################
+
+
+# import requested python module
+import os
+import AccessControl
+from zLOG import LOG
+from Testing import ZopeTestCase
+from DateTime import DateTime
+from Products.CMFCore.utils import getToolByName
+from Products.ERP5Type.Utils import convertToUpperCase
+from Products.ERP5Type.tests.ERP5TypeTestCase import ERP5TypeTestCase
+from Products.ERP5Type.tests.Sequence import SequenceList
+from AccessControl.SecurityManagement import newSecurityManager
+from Products.DCWorkflow.DCWorkflow import Unauthorized, ValidationFailed
+from Testing.ZopeTestCase.PortalTestCase import PortalTestCase
+
+# Needed in order to have a log file inside the current folder
+os.environ['EVENT_LOG_FILE']     = os.path.join(os.getcwd(), 'zLOG.log')
+# Define the level of log we want, here is all
+os.environ['EVENT_LOG_SEVERITY'] = '-300'
+
+# Define how to launch the script if we don't use runUnitTest script
+if __name__ == '__main__':
+  execfile(os.path.join(sys.path[0], 'framework.py'))
+
+
+
+class TestERP5BankingCheckPayment(ERP5TypeTestCase):
+  """
+    This class is a unit test to check the module of Check Payment
+
+    Here are the following step that will be done in the test :
+  
+    - before the test, we need to create some movements that will put resources in the source
+
+    - create a cash classification
+    - check it has been created correctly
+    - check source and destination (current == future)
+    - check roles (who is author, assignor, assignee, ...)
+
+    - create a "Note Line" (billetage)
+    - check it has been created correctly
+    - check the total amount
+
+    - create a second Line
+    - check it has been created correctly
+    - check the total amount
+
+    - create an invalid Line (quantity > available at source)
+    - check that the system behaves correctly
+
+    - pass "confirm_action" transition
+    - check that we can't pass the transition as another user (depending on roles)
+    - check that the new state is confirmed
+    - check that the source has been debited correctly (current < future)
+
+    - log in as "Controleur" (assignee)
+    - check amount, lines, ...
+
+    - pass "deliver_action" transition
+    - check that we can't pass the transition as another user
+    - check that the new state is delivered
+    - check that the destination has been credited correctly (current == future)
+  """
+
+  login = PortalTestCase.login
+
+  # pseudo constants
+  RUN_ALL_TEST = 1 # we want to run all test
+  QUIET = 0 # we don't want the test to be quiet
+
+
+
+  ##################################
+  ##  ZopeTestCase Skeleton
+  ##################################
+
+  def getTitle(self):
+    """
+      Return the title of the test
+    """
+    return "ERP5BankingCheckPayment"
+
+
+  def getBusinessTemplateList(self):
+    """
+      Return the list of business templates we need to run the test.
+      This method is called during the initialization of the unit test by
+      the unit test framework in order to know which business templates
+      need to be installed to run the test on.
+    """
+    return ('erp5_trade', 'erp5_banking_core', 'erp5_banking_inventory', 'erp5_banking_check_payment')
+
+
+  def enableLightInstall(self):
+    """
+      Return if we should do a light install (1) or not (0)
+      Light install variable is used at installation of categories in business template
+      to know if we wrap the category or not, if 1 we don't use and installation is faster
+    """
+    return 1 # here we want a light install for a faster installation
+
+
+  def enableActivityTool(self):
+    """
+      Return if we should create (1) or not (0) an activity tool
+      This variable is used at the creation of the site to know if we use
+      the activity tool or not
+    """
+    return 1 # here we want to use the activity tool
+
+
+  def afterSetUp(self):
+    """
+      Method called before the launch of the test to initialize some data
+    """
+    # Set some variables : 
+    # the erp5 site
+    self.portal = self.getPortal()
+    # the check payment module
+    self.check_payment_module = self.getCheckPaymentModule()
+    # the checkbook module
+    self.checkbook_module = self.getCheckbookModule()
+    # the cash inventory module
+    self.cash_inventory_module = self.getCashInventoryModule()
+    # the person module
+    self.person_module = self.getPersonModule()
+    # the organisation module
+    self.organisation_module = self.getOrganisationModule()
+    # the category tool
+    self.category_tool = self.getCategoryTool()
+    # the workflow tool
+    self.workflow_tool = self.getWorkflowTool()
+
+    # Let us know which user folder is used (PAS or NuxUserGroup)
+    self.checkUserFolderType()
+
+    # Create a user and login as manager to populate the erp5 portal with objects for tests.
+    self.createManagerAndLogin()
+
+    # Define static values (only use prime numbers to prevent confusions like 2 * 6 == 3 * 4)
+    # variation list is the list of years for banknotes and coins
+    self.variation_list = ('variation/1992', 'variation/2003')
+
+    # quantity of banknotes of 10000 :
+    self.quantity_10000 = {}
+    # 2 banknotes of 10000 for the year 1992
+    self.quantity_10000[self.variation_list[0]] = 2
+    # 3 banknotes of 10000 for the year of 2003
+    self.quantity_10000[self.variation_list[1]] = 3
+
+    # quantity of coin of 200
+    self.quantity_200 = {}
+    # 5 coins of 200 for the year 1992
+    self.quantity_200[self.variation_list[0]] = 5
+    # 7 coins of 200 for the year 2003
+    self.quantity_200[self.variation_list[1]] = 7
+
+    # quantity of banknotes of 5000
+    self.quantity_5000 = {}
+    # 11 banknotes of 5000 for hte year 1992
+    self.quantity_5000[self.variation_list[0]] = 11
+    # 13 banknotes of 5000 for the year 2003
+    self.quantity_5000[self.variation_list[1]] = 13
+
+
+    # Create Categories (vaults)
+
+    # as local roles are defined in portal types as real categories, we will need to reproduce (or import) the real category tree
+    # get the base category function
+    self.function_base_category = getattr(self.category_tool, 'function')
+    # add category banking in function which will hold all functions neccessary in a bank (at least for this unit test)
+    self.banking = self.function_base_category.newContent(id='banking', portal_type='Category', codification='BNK')
+    # add function categories
+    self.caissier_principal = self.banking.newContent(id='caissier_principal', portal_type='Category', codification='CCP')
+    self.caissier_particulier = self.banking.newContent(id='caissier_particulier', portal_type='Category', codification='CGU')
+    self.comptable = self.banking.newContent(id='comptable', portal_type='Category', codification='FXF')
+    self.chef_section = self.banking.newContent(id='chef_section', portal_type='Category', codification='FXS')
+    self.void_function = self.banking.newContent(id='void_function', portal_type='Category', codification='VOID')
+    self.chef_comptable = self.banking.newContent(id='chef_comptable', portal_type='Category', codification='CCB')
+    self.gestionnaire_caisse_courante = self.banking.newContent(id='gestionnaire_caisse_courante', portal_type='Category', codification='CCO')
+
+    # get the base category group
+    self.group_base_category = getattr(self.category_tool, 'group')
+    # add the group baobab in the group category
+    self.baobab = self.group_base_category.newContent(id='baobab', portal_type='Category', codification='BAOBAB')
+
+    # get the base category site
+    self.site_base_category = getattr(self.category_tool, 'site')
+    # add the category testsite in the category site which hold vaults situated in the bank
+    self.testsite = self.site_base_category.newContent(id='site', portal_type='Category', codification='TEST')
+#     self.siegesite = self.testsite.newContent(id='siege', portal_type='Category', codification='SIEGE')
+    self.siegesite = self.site_base_category.newContent(id='siege', portal_type='Category', codification='SIEGE')
+    self.agencesite = self.site_base_category.newContent(id='agence', portal_type='Category', codification='AGENCE')
+    self.principalesite = self.agencesite.newContent(id='principale', portal_type='Category', codification='PRINCIPALE')
+    self.dakar = self.principalesite.newContent(id='dakar', portal_type='Category', codification='K00')
+    self.auxisite = self.agencesite.newContent(id='auxiliaire', portal_type='Category', codification='AUXILIAIRE')
+        
+    self.encaisse_billets_et_monnaies = self.testsite.newContent(id='encaisse_des_billets_et_monnaies', portal_type='Category', codification='C1')
+    self.encaisse_externe = self.testsite.newContent(id='encaisse_des_externes', portal_type='Category', codification='C1')
+    self.encaisse_ventilation = self.testsite.newContent(id='encaisse_des_billets_recus_pour_ventilation', portal_type='Category', codification='C1')
+    self.caisse_abidjan = self.encaisse_ventilation.newContent(id='abidjan', portal_type='Category', codification='C1')
+
+    # get the base category cash_status
+    self.cash_status_base_category = getattr(self.category_tool, 'cash_status')
+    # add the category valid in cash_status which define status of banknotes and coin
+    self.cash_status_valid = self.cash_status_base_category.newContent(id='valid', portal_type='Category')
+    self.cash_status_valid = self.cash_status_base_category.newContent(id='to_sort', portal_type='Category')
+
+    # get the base category emission letter
+    self.emission_letter_base_category = getattr(self.category_tool, 'emission_letter')
+    # add the category k in emission letter that will be used fo banknotes and coins
+    self.emission_letter_k = self.emission_letter_base_category.newContent(id='k', portal_type='Category')
+    self.emission_letter_b = self.emission_letter_base_category.newContent(id='b', portal_type='Category')
+    self.emission_letter_d = self.emission_letter_base_category.newContent(id='d', portal_type='Category')
+
+    # get the base category variation which hold the year of banknotes and coins
+    self.variation_base_category = getattr(self.category_tool, 'variation')
+    # add the category 1992 in variation
+    self.variation_1992 = self.variation_base_category.newContent(id='1992', portal_type='Category')
+    # add the category 2003 in varitation
+    self.variation_2003 = self.variation_base_category.newContent(id='2003', portal_type='Category')
+
+    # get the base category quantity_unit
+    self.variation_base_category = getattr(self.category_tool, 'quantity_unit')
+    # add category unit in quantity_unit which is the unit that will be used for banknotes and coins
+    self.unit = self.variation_base_category.newContent(id='unit', title='Unit')
+
+    # Create an Organisation that will be used for users assignment
+    self.organisation = self.organisation_module.newContent(id='baobab_org', portal_type='Organisation',
+        function='banking', group='baobab',  site='site')
+
+    # Create some users who will get different roles on the cash classification.
+    #
+    # Dictionnary data scheme:
+    #     'user_login': [['Global Role'], 'organisation', 'function', 'group', 'site']
+    #
+    user_dict = {
+        'user_1' : [[], self.organisation, 'banking/comptable', 'baobab', 'site']
+      , 'user_2' : [[], self.organisation, 'banking/caissier_particulier' , 'baobab', 'site']
+      , 'user_3' : [[], self.organisation, 'banking/void_function'     , 'baobab', 'site']
+      }
+    # call method to create this user
+    self.createERP5Users(user_dict)
+
+    # We must assign local roles to check_payment_module, checkbook_module and person_module manually, as they are
+    #   not packed in Business Templates yet.
+    # The local roles must be the one for gestionnaire_caisse_courante
+    if self.PAS_installed:
+      # in case of use of PAS
+      self.check_payment_module.manage_addLocalRoles('FXF_BAOBAB_TEST', ('Author',))
+      self.person_module.manage_addLocalRoles('FXF_BAOBAB_TEST', ('Assignor',))
+      self.checkbook_module.manage_addLocalRoles('FXF_BAOBAB_TEST', ('Auditor',))
+      self.check_payment_module.manage_addLocalRoles('CGU_BAOBAB_TEST', ('DestinationAssignor',))
+    else:
+      # in case of NuxUserGroup
+      self.check_payment_module.manage_addLocalGroupRoles('FXF_BAOBAB_TEST', ('Author',))
+      self.person_module.manage_addLocalGroupRoles('FXF_BAOBAB_TEST', ('Assignor',))
+      self.checkbook_module.manage_addLocalGroupRoles('FXF_BAOBAB_TEST', ('Auditor',))
+      self.check_payment_module.manage_addLocalGroupRoles('CGU_BAOBAB_TEST', ('DestinationAssignor',))
+
+    # get the currency module
+    self.currency_module = self.getCurrencyModule()
+    # create the currency document for Fran CFA inside the currency module
+    self.currency_1 = self .currency_module.newContent(id='EUR', title='Euros', reference='EUR')
+
+    # Create Resources (Banknotes & Coins)
+    # get the currency cash module
+    self.currency_cash_module = self.getCurrencyCashModule()
+    # create document for banknote of 10000 euros from years 1992 and 2003
+    self.billet_10000 = self.currency_cash_module.newContent(id='billet_10000', portal_type='Banknote', base_price=10000, price_currency_value=self.currency_1, variation_list=('1992', '2003'), quantity_unit_value=self.unit)
+    # create document for banknote of 500 euros from years 1992 and 2003
+    self.billet_5000 = self.currency_cash_module.newContent(id='billet_5000', portal_type='Banknote', base_price=5000, price_currency_value=self.currency_1, variation_list=('1992', '2003'), quantity_unit_value=self.unit)
+    # create docuemnt for coin of 200 euros from years 1992 and 2003
+    self.billet_200 = self.currency_cash_module.newContent(id='billet_200', portal_type='Banknote', base_price=200, price_currency_value=self.currency_1, variation_list=('1992', '2003'), quantity_unit_value=self.unit)
+
+    # Before the test, we need to input the inventory
+    self.cash_inventory_group = self.cash_inventory_module.newContent(id='inventory_group_1', portal_type='Cash Inventory Group',
+            source=None, destination_value=self.encaisse_billets_et_monnaies)
+    self.cash_inventory = self.cash_inventory_group.newContent(id='inventory_1', portal_type='Cash Inventory',
+                                                               price_currency_value=self.currency_1)
+    # add a line for banknotes of 10000 with emission letter k, status valid and from years 1992 and 2003 with the quantity defined
+    # before in quantity_10000 (2 for 1992 and 3 for 2003)
+    self.addCashLineToDelivery(self.cash_inventory, 'delivery_init_1', 'Cash Inventory Line', self.billet_10000,
+            ('emission_letter', 'cash_status', 'variation'), ('emission_letter/k', 'cash_status/to_sort') + self.variation_list,
+            self.quantity_10000)
+    # add a line for coins of 200 with emission letter k, status valid and from years 1992 and 2003 with the quantity defined
+    # before in quantity_200 (5 for 1992 and 7 for 2003)
+    self.addCashLineToDelivery(self.cash_inventory, 'delivery_init_2', 'Cash Inventory Line', self.billet_200,
+            ('emission_letter', 'cash_status', 'variation'), ('emission_letter/k', 'cash_status/to_sort') + self.variation_list,
+            self.quantity_200)
+    self.addCashLineToDelivery(self.cash_inventory, 'delivery_init_3', 'Cash Inventory Line', self.billet_5000,
+            ('emission_letter', 'cash_status', 'variation'), ('emission_letter/k', 'cash_status/to_sort') + self.variation_list,
+            self.quantity_5000)
+
+    # create a person and a bank account
+    self.person_1 = self.person_module.newContent(id = 'person_1', portal_type = 'Person',
+                                                  first_name = 'toto', last_name = 'titi')
+    self.bank_account_1 = self.person_1.newContent(id = 'bank_account_1', portal_type = 'Bank Account',
+                                                   price_currency_value = self.currency_1)
+    # validate this bank account for payment
+    self.bank_account_1.validate()
+
+    # create a check
+    self.checkbook_1 = self.checkbook_module.newContent(id = 'checkbook_1', portal_type = 'Checkbook',
+                                                        destination_value = self.dakar,
+                                                        destination_payment_value = self.bank_account_1,
+                                                        reference_range_min = '50',
+                                                        reference_range_max = '100',
+                                                        start_date = DateTime())
+    self.check_1 = self.checkbook_1.newContent(id = 'check_1', portal_type = 'Check',
+                                               quantity = 20000, reference='50') # XXX what is this quantity???
+
+    # logout from manager
+    self.logout()
+    # Finally, login as user_1
+    self.login('user_1')
+
+
+  def checkUserFolderType(self, quiet=QUIET, run=RUN_ALL_TEST):
+    """
+      Check the type of user folder to let the test working with both NuxUserGroup and PAS.
+    """
+    self.user_folder = self.getUserFolder()
+    self.PAS_installed = 0
+    if self.user_folder.meta_type == 'Pluggable Auth Service':
+      # we use PAS
+      self.PAS_installed = 1
+
+
+  def assignPASRolesToUser(self, user_name, role_list, quiet=QUIET, run=RUN_ALL_TEST):
+    """
+      Assign a list of roles to one user with PAS.
+    """
+    for role in role_list:
+      if role not in self.user_folder.zodb_roles.listRoleIds():
+        self.user_folder.zodb_roles.addRole(role)
+      self.user_folder.zodb_roles.assignRoleToPrincipal(role, user_name)
+
+
+  def createManagerAndLogin(self, quiet=QUIET, run=RUN_ALL_TEST):
+    """
+      Create a simple user in user_folder with manager rights.
+      This user will be used to initialize data in the method afterSetup
+    """
+    self.getUserFolder()._doAddUser('manager', '', ['Manager'], [])
+    self.login('manager')
+
+
+  def createERP5Users(self, user_dict, quiet=QUIET, run=RUN_ALL_TEST):
+    """
+      Create all ERP5 users needed for the test.
+      ERP5 user = Person object + Assignment object in erp5 person_module.
+    """
+    for user_login, user_data in user_dict.items():
+      user_roles = user_data[0]
+      # Create the Person.
+      person = self.person_module.newContent(id=user_login,
+          portal_type='Person', reference=user_login, career_role="internal")
+      # Create the Assignment.
+      assignment = person.newContent( portal_type       = 'Assignment'
+                                    , destination_value = user_data[1]
+                                    , function          = user_data[2]
+                                    , group             = user_data[3]
+                                    , site              = user_data[4]
+                                    )
+      if self.PAS_installed and len(user_roles) > 0:
+        # In the case of PAS, if we want global roles on user, we have to do it manually.
+        self.assignPASRolesToUser(user_login, user_roles)
+      elif not self.PAS_installed:
+        # The user_folder counterpart of the erp5 user must be
+        #   created manually in the case of NuxUserGroup.
+        self.user_folder.userFolderAddUser( name     = user_login
+                                          , password = ''
+                                          , roles    = user_roles
+                                          , domains  = []
+                                          )
+      # User assignment to security groups is also required, but is taken care of
+      #   by the assignment workflow when NuxUserGroup is used and
+      #   by ERP5Security PAS plugins in the context of PAS use.
+      assignment.open()
+      
+    if self.PAS_installed:
+      # reindexing is required for the security to work
+      get_transaction().commit()
+      self.tic()
+
+
+  ##################################
+  ##  Usefull methods
+  ##################################
+
+  def addCashLineToDelivery(self, delivery_object, line_id, line_portal_type, resource_object,
+          variation_base_category_list, variation_category_list, resource_quantity_dict):
+    """
+    Add a cash line to a delivery
+    This will add an Internal Packing List Line to a Internal Packing List
+    """
+    base_id = 'movement'
+    line_kwd = {'base_id':base_id}
+    # create the cash line
+    line = delivery_object.newContent( id                  = line_id
+                                     , portal_type         = line_portal_type
+                                     , resource_value      = resource_object # banknote or coin
+                                     , quantity_unit_value = self.unit
+                                     )
+    # set base category list on line
+    line.setVariationBaseCategoryList(variation_base_category_list)
+    # set category list line
+    line.setVariationCategoryList(variation_category_list)
+    line.updateCellRange(script_id='CashDetail_asCellRange', base_id=base_id)
+    cell_range_key_list = line.getCellRangeKeyList(base_id=base_id)
+    if cell_range_key_list <> [[None, None]] :
+      for k in cell_range_key_list:
+        category_list = [item for item in k if item is not None]
+        c = line.newCell(*k, **line_kwd)
+        mapped_value_list = ['price', 'quantity']
+        c.edit( membership_criterion_category_list = category_list
+              , mapped_value_property_list         = mapped_value_list
+              , category_list                      = category_list
+              , force_update                       = 1
+              )
+    # set quantity on cell to define quantity of bank notes / coins
+    for variation in self.variation_list:
+      cell = line.getCell('emission_letter/k', variation, 'cash_status/to_sort')
+      if cell is not None:
+        cell.setQuantity(resource_quantity_dict[variation])
+    for variation in self.variation_list:
+      cell = line.getCell('emission_letter/b', variation, 'cash_status/to_sort')
+      if cell is not None:
+        cell.setQuantity(resource_quantity_dict[variation])
+
+  def getUserFolder(self):
+    """
+    Return the user folder
+    """
+    return getattr(self.getPortal(), 'acl_users', None)
+
+  def getPersonModule(self):
+    """
+    Return the person module
+    """
+    return getattr(self.getPortal(), 'person_module', None)
+  
+  def getOrganisationModule(self):
+    """
+    Return the organisation module
+    """
+    return getattr(self.getPortal(), 'organisation_module', None)
+  
+  def getCurrencyCashModule(self):
+    """
+    Return the Currency Cash Module
+    """
+    return getattr(self.getPortal(), 'currency_cash_module', None)
+  
+  def getCashInventoryModule(self):
+    """
+    Return the Cash Inventory Module
+    """
+    return getattr(self.getPortal(), 'cash_inventory_module', None)
+  
+  def getCheckPaymentModule(self):
+    """
+    Return the Check Payment Module
+    """
+    return getattr(self.getPortal(), 'check_payment_module', None)
+  
+  def getCheckbookModule(self):
+    """
+    Return the Checkbook Module
+    """
+    return getattr(self.getPortal(), 'checkbook_module', None)
+  
+  def getCurrencyModule(self):
+    """
+    Return the Currency Module
+    """
+    return getattr(self.getPortal(), 'currency_module', None)
+  
+  def getCategoryTool(self):
+    """
+    Return the Category Tool
+    """
+    return getattr(self.getPortal(), 'portal_categories', None)
+  
+  def getWorkflowTool(self):
+    """
+    Return the Worklfow Tool
+    """
+    return getattr(self.getPortal(), 'portal_workflow', None)
+  
+  def getSimulationTool(self):
+    """
+    Return the Simulation Tool
+    """
+    return getattr(self.getPortal(), 'portal_simulation', None)
+
+
+
+  ##################################
+  ##  Basic steps
+  ##################################
+
+  def stepTic(self, **kwd):
+    """
+    The is used to simulate the zope_tic_loop script
+    Each time this method is called, it simulates a call to tic
+    which invoke activities in the Activity Tool
+    """
+    # execute transaction
+    get_transaction().commit()
+    self.tic()
+
+
+  def stepCheckObjects(self, sequence=None, sequence_list=None, **kwd):
+    """
+    Check that all the objects we created in afterSetUp or
+    that were added by the business template and that we rely
+    on are really here.
+    """
+    # check that Categories were created
+    self.assertEqual(self.encaisse_billets_et_monnaies.getPortalType(), 'Category')
+
+    # check that Resources were created
+    # check portal type of billet_10000
+    self.assertEqual(self.billet_10000.getPortalType(), 'Banknote')
+    # check value of billet_10000
+    self.assertEqual(self.billet_10000.getBasePrice(), 10000)
+    # check currency value  of billet_10000
+    self.assertEqual(self.billet_10000.getPriceCurrency(), 'currency_module/EUR')
+    # check years  of billet_10000
+    self.assertEqual(self.billet_10000.getVariationList(), ['1992', '2003'])
+
+    # check portal type of billet_5000
+    self.assertEqual(self.billet_5000.getPortalType(), 'Banknote')
+    # check value of billet_5000
+    self.assertEqual(self.billet_5000.getBasePrice(), 5000)
+    # check currency value  of billet_5000
+    self.assertEqual(self.billet_5000.getPriceCurrency(), 'currency_module/EUR')
+    # check years  of billet_5000
+    self.assertEqual(self.billet_5000.getVariationList(), ['1992', '2003'])
+    
+    # check portal type of billet_200
+    self.assertEqual(self.billet_200.getPortalType(), 'Banknote')
+    # check value of billet_200
+    self.assertEqual(self.billet_200.getBasePrice(), 200)
+    # check currency value  of billet_200
+    self.assertEqual(self.billet_200.getPriceCurrency(), 'currency_module/EUR')
+    # check years  of billet_200
+    self.assertEqual(self.billet_200.getVariationList(), ['1992', '2003'])
+
+    # check that Check Payment Module was created
+    self.assertEqual(self.check_payment_module.getPortalType(), 'Check Payment Module')
+    # check check payment module is empty
+    self.assertEqual(len(self.check_payment_module.objectValues()), 0)
+
+
+  def stepCheckInitialInventory(self, sequence=None, sequence_list=None, **kwd):
+    """
+    Check the initial inventory before any operations
+    """
+    self.simulation_tool = self.getSimulationTool()
+    # check we have 5 banknotes of 10000 in encaisse_billets_et_monnaies
+    self.assertEqual(self.simulation_tool.getCurrentInventory(node=self.encaisse_billets_et_monnaies.getRelativeUrl(), resource = self.billet_10000.getRelativeUrl()), 5.0)
+    self.assertEqual(self.simulation_tool.getFutureInventory(node=self.encaisse_billets_et_monnaies.getRelativeUrl(), resource = self.billet_10000.getRelativeUrl()), 5.0)
+    # check we have 12 coin of 200 in encaisse_billets_et_monnaies
+    self.assertEqual(self.simulation_tool.getCurrentInventory(node=self.encaisse_billets_et_monnaies.getRelativeUrl(), resource = self.billet_200.getRelativeUrl()), 12.0)
+    self.assertEqual(self.simulation_tool.getFutureInventory(node=self.encaisse_billets_et_monnaies.getRelativeUrl(), resource = self.billet_200.getRelativeUrl()), 12.0)
+    # check we have 24 banknotes of 200 in encaisse_billets_et_monnaies
+    self.assertEqual(self.simulation_tool.getCurrentInventory(node=self.encaisse_billets_et_monnaies.getRelativeUrl(), resource = self.billet_5000.getRelativeUrl()), 24.0)
+    self.assertEqual(self.simulation_tool.getFutureInventory(node=self.encaisse_billets_et_monnaies.getRelativeUrl(), resource = self.billet_5000.getRelativeUrl()), 24.0)
+
+
+  def stepCreateCheckPayment(self, sequence=None, sequence_list=None, **kwd):
+    """
+    Create a check payment document and check it
+    """
+    self.check_payment = self.check_payment_module.newContent(id = 'check_payment', portal_type = 'Check Payment',
+                                         destination_payment_value = self.bank_account_1,
+                                         aggregate_value = self.check_1,
+                                         source_value = self.encaisse_billets_et_monnaies,
+                                         start_date = DateTime(),
+                                         source_total_asset_price = 20000.0)
+    self.assertNotEqual(self.check_payment, None)
+    self.assertEqual(self.check_payment.getTotalPrice(), 0.0)
+    self.assertEqual(self.check_payment.getDestinationPayment(), self.bank_account_1.getRelativeUrl())
+    self.assertEqual(self.check_payment.getAggregate(), self.check_1.getRelativeUrl())
+    self.assertEqual(self.check_payment.getSourceTotalAssetPrice(), 20000.0)
+    self.assertEqual(self.check_payment.getSource(), self.encaisse_billets_et_monnaies.getRelativeUrl())
+
+    # the initial state must be draft
+    self.assertEqual(self.check_payment.getSimulationState(), 'draft')
+
+    # source reference must be automatically generated
+    self.check_payment.setSourceReference(self.check_payment.Baobab_getUniqueReference())
+    self.assertNotEqual(self.check_payment.getSourceReference(), None)
+    self.assertNotEqual(self.check_payment.getSourceReference(), '')
+
+  def stepCheckConsistency(self, sequence=None, sequence_list=None, **kwd):
+    """
+    Check the consistency of the check payment
+
+    FIXME: check if the transition fails when a category or property is invalid.
+    FIXME: check if the transition fails when a bad user tries.
+    """
+    self.assertNotEqual(self.check_payment.getAggregateValue(), None)
+
+    self.workflow_tool.doActionFor(self.check_payment, 'plan_action', wf_id='check_payment_workflow')
+    self.assertEqual(self.check_payment.getSimulationState(), 'planned')
+
+  def stepSendToCounter(self, sequence=None, sequence_list=None, **kwd):
+    """
+    Send the check payment to the counter
+
+    FIXME: check if the transition fails when a category or property is invalid.
+    FIXME: check if the transition fails when a bad user tries.
+    """
+    self.workflow_tool.doActionFor(self.check_payment, 'confirm_action', wf_id='check_payment_workflow')
+    self.assertEqual(self.check_payment.getSimulationState(), 'confirmed')
+
+    self.assertEqual(self.check_payment.getSourceTotalAssetPrice(),
+                     - self.check_payment.getTotalPrice(portal_type = 'Banking Operation Line'))
+
+  def stepInputCashDetails(self, sequence=None, sequence_list=None, **kwd):
+    """
+    Input cash details
+    """
+    self.addCashLineToDelivery(self.check_payment, 'line_1', 'Cash Delivery Line', self.billet_10000,
+            ('emission_letter', 'cash_status', 'variation'),
+            ('emission_letter/k', 'cash_status/to_sort') + self.variation_list[1:],
+            {self.variation_list[1] : 1})
+    self.assertEqual(self.check_payment.line_1.getPrice(), 10000)
+
+    self.addCashLineToDelivery(self.check_payment, 'line_2', 'Cash Delivery Line', self.billet_5000,
+            ('emission_letter', 'cash_status', 'variation'),
+            ('emission_letter/k', 'cash_status/to_sort') + self.variation_list[1:],
+            {self.variation_list[1] : 2})
+    self.assertEqual(self.check_payment.line_2.getPrice(), 5000)
+
+  def stepPay(self, sequence=None, sequence_list=None, **kwd):
+    """
+    Pay the check payment
+
+    FIXME: check if the transition fails when a category or property is invalid.
+    FIXME: check if the transition fails when a bad user tries.
+    """
+    self.logout()
+    self.login('user_2')
+
+    self.assertEqual(self.check_payment.getSourceTotalAssetPrice(),
+                     self.check_payment.getTotalPrice(portal_type = 'Cash Delivery Cell'))
+    try:
+      self.workflow_tool.doActionFor(self.check_payment, 'deliver_action', wf_id='check_payment_workflow')
+    except:
+      import pdb
+      pdb.set_trace()
+
+    self.assertEqual(self.check_payment.getSimulationState(), 'delivered')
+
+  ##################################
+  ##  Tests
+  ##################################
+
+  def test_01_ERP5BankingCheckPayment(self, quiet=QUIET, run=RUN_ALL_TEST):
+    """
+    Define the sequence of step that will be play
+    """
+    if not run: return
+    sequence_list = SequenceList()
+    # define the sequence
+    sequence_string = 'Tic CheckObjects Tic CheckInitialInventory ' \
+                      'CreateCheckPayment Tic ' \
+                      'CheckConsistency Tic ' \
+                      'SendToCounter Tic ' \
+                      'InputCashDetails Tic ' \
+                      'Pay Tic '
+    sequence_list.addSequenceString(sequence_string)
+    # play the sequence
+    sequence_list.play(self)
+
+# define how we launch the unit test
+if __name__ == '__main__':
+  framework()
+else:
+  import unittest
+  def test_suite():
+    suite = unittest.TestSuite()
+    suite.addTest(unittest.makeSuite(TestERP5BankingCheckPayment))
+    return suite
-- 
2.30.9