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