From be3df2e3db0d3b92fc05dd5a4c037e77ce62375c Mon Sep 17 00:00:00 2001
From: Sebastien Robin <seb@nexedi.com>
Date: Fri, 15 Oct 2004 14:50:43 +0000
Subject: [PATCH] added one way synchronization

git-svn-id: https://svn.erp5.org/repos/public/erp5/trunk@1860 20353a03-c40f-0410-a6d1-a30d3c3de9de
---
 product/ERP5SyncML/Conduit/ERP5Conduit.py     |   3 +-
 .../Conduit/ERP5ShopOrderConduit.py           | 226 +++++++++------
 product/ERP5SyncML/Publication.py             |  14 +-
 product/ERP5SyncML/Subscription.py            |  56 +++-
 product/ERP5SyncML/SynchronizationTool.py     |  47 ++--
 product/ERP5SyncML/XMLSyncUtils.py            |  55 ++--
 .../ERP5SyncML/dtml/managePublications.dtml   |  10 +
 .../ERP5SyncML/dtml/manageSubscriptions.dtml  |  10 +
 .../dtml/manage_addPublication.dtml           |  10 +
 .../dtml/manage_addSubscription.dtml          |  10 +
 product/ERP5SyncML/tests/testERP5SyncML.py    | 257 +++++++++++-------
 11 files changed, 452 insertions(+), 246 deletions(-)

diff --git a/product/ERP5SyncML/Conduit/ERP5Conduit.py b/product/ERP5SyncML/Conduit/ERP5Conduit.py
index 7f45cdb697..ee3ac60214 100755
--- a/product/ERP5SyncML/Conduit/ERP5Conduit.py
+++ b/product/ERP5SyncML/Conduit/ERP5Conduit.py
@@ -97,7 +97,8 @@ class ERP5Conduit(XMLSyncUtilsMixin):
     """
     return the string corresponding to the local encoding
     """
-    return "iso-8859-1"
+    #return "iso-8859-1"
+    return "utf-8"
 
   security.declareProtected(Permissions.ModifyPortalContent, '__init__')
   def __init__(self):
diff --git a/product/ERP5SyncML/Conduit/ERP5ShopOrderConduit.py b/product/ERP5SyncML/Conduit/ERP5ShopOrderConduit.py
index ee16c3fb3c..4bfd618ce2 100755
--- a/product/ERP5SyncML/Conduit/ERP5ShopOrderConduit.py
+++ b/product/ERP5SyncML/Conduit/ERP5ShopOrderConduit.py
@@ -47,6 +47,8 @@ class ERP5ShopOrderConduit(ERP5Conduit):
   """
   This conduit is used in the synchronisation process of Storever and ERP5 to convert
   a Storever Shop Order to a ERP5 Sale Order.
+  Don't forget to add this base categories in portal_category :
+      'hd_size', 'memory_size', 'optical_drive', 'keyboad_layout', 'cpu_type'
   """
 
 
@@ -72,17 +74,17 @@ class ERP5ShopOrderConduit(ERP5Conduit):
     if portal_type == 'Shop Order':
       # The random part of the id can be removed. It's only used for the developpement
       new_object_id = 'storever-' + object_id  + '-' + str(random.randint(1000, 9999))
-      object.newContent ( portal_type = 'Sale Order'
-                        , id          = new_object_id)
+      subobject = object.newContent( portal_type = 'Sale Order'
+                                   , id          = new_object_id)
     if portal_type == 'Order Line':
       last_line_num = self.getLastOrderLineNumber(object)
       new_object_id = "storever-" + str(last_line_num + 1) + "-" + object_id
-      object.newContent ( portal_type = 'Sale Order Line'
-                        , id          = new_object_id)
-    subobject = object._getOb(new_object_id)
+      subobject = object.newContent( portal_type = 'Sale Order Line'
+                                   , id          = new_object_id)
     return subobject
 
 
+
 #   # Not needed yet
 #   security.declareProtected(Permissions.ModifyPortalContent, 'addWorkflowNode')
 #   def addWorkflowNode(self, object, xml, simulate):
@@ -178,9 +180,8 @@ class ERP5ShopOrderConduit(ERP5Conduit):
       if product_id == erp5_product_id:
         return erp5_site.restrictedTraverse(erp5_site_path + '/product/' + erp5_product_id)
     # We have to create a new product
-    product_folder.newContent ( portal_type = 'Product'
-                              , id          = erp5_product_id)
-    return product_folder._getOb(erp5_product_id)
+    return product_folder.newContent( portal_type = 'Product'
+                                    , id          = erp5_product_id)
 
 
 
@@ -190,18 +191,25 @@ class ERP5ShopOrderConduit(ERP5Conduit):
     This function set the validation workflow to indicate if a product
     is discontinued (workflow state = invalidate) or not (workflow state = validate)
     """
+    #!!!!!!!!!!!!!!!!!!!!!!!!!!
+#     return
+    #!!!!!!!!!!!!!!!!!!!!!!!!!!
     action = None
     if hasattr(product_object, 'workflow_history'):
+      LOG('Info needed from portal_workflow >>>>>>>>> ', 0, '')
       workflow_state = product_object.portal_workflow.getInfoFor(product_object, 'validation_state')
+      LOG('workflow_state is >>>>>>>>> ', 0, repr(workflow_state))
     if product_title.lower().find('discontinued') != -1:
       if workflow_state != 'invalidated':
         action = 'invalidate_action'
     elif workflow_state in ('draft', 'invalidated'):
       action = 'validate_action'
+    LOG('action is >>>>>>>>> ', 0, repr(action))
     if action != None:
       product_object.portal_workflow.doActionFor( product_object
                                                 , action
                                                 , wf_id = 'validation_workflow')
+    LOG('end of workflow action >>>>>>>>> ', 0, repr(action))
 
 
 
@@ -248,6 +256,43 @@ class ERP5ShopOrderConduit(ERP5Conduit):
 
 
 
+  security.declarePrivate('updateObjProperty')
+  def updateObjProperty(self, object, property, kw, key):
+    """
+    This function update the property of an object with a given value stored in a dictionnary. This function help the Conduit to make decision about the synchronisation of values.
+
+    Example of call : self.updateObjProperty(person_object, 'DefaultAddressStreetAddress', kw, 'address')
+
+    Solution (d'apres seb) :
+      * machin = getattr (object, methos)
+      * machin()
+    """
+    if kw.has_key(key):
+      new_value = kw[key]
+      if new_value != None:
+        if type(new_value) is type('s'):
+          new_value = new_value.title()
+
+        current_value = eval('object.get' + property + '()')
+        LOG("I have to run this >>>>>>>> ", 0, 'object.get' + property + '()')
+        LOG("current_value >>>>>>>> ", 0, repr(current_value))
+
+        # The current property value is not consistent
+        if current_value == None or len(current_value) == 0:
+          # Erase the current value with the new one
+
+          LOG("I have to run this to set the property >>>>>>>> " + 'object.set' + str(property) + '(' + str(new_value) + ')' + str(current_value), 0, '')
+
+        # A previous consistent value exist
+        elif current_value.strip().lower() != new_value.strip().lower():
+          # TODO : We need to choose if we replace it or not, or mix the current with the new one
+          LOG('We have to make the fusion of previous address with the current one  >>>>>>>', 0, '')
+          return False
+        return True
+    return False
+
+
+
   security.declareProtected(Permissions.ModifyPortalContent, 'editDocument')
   def editDocument(self, object=None, **kw):
     """
@@ -258,6 +303,10 @@ class ERP5ShopOrderConduit(ERP5Conduit):
 
     LOG('KW >>>>>>>> ', 0, kw)
 
+    # This list contain a list of object to check to know if their workflow need to be mofified
+    # We store these objects into a list and we will apply modification at the end to avoid mysql lock problem
+    workflow_joblist = []
+
     # Get the ERP5 root object
     portal_types = getToolByName(object, 'portal_types')
     erp5_site = portal_types.getPortalObject()
@@ -265,12 +314,11 @@ class ERP5ShopOrderConduit(ERP5Conduit):
 
     # The object is a ShopOrder
     if kw.has_key('country'):
-
       # Find the organisation and the person folder
       person_path = erp5_site_path + '/person'
       person_folder = erp5_site.restrictedTraverse(person_path)
       organisation_path = erp5_site_path + '/organisation'
-      organisation_folder = erp5_site.restrictedTraverse(organisation_path)
+      org_folder = erp5_site.restrictedTraverse(organisation_path)
       # Find the service folder
       service_path = erp5_site_path + '/service'
       service_folder = erp5_site.restrictedTraverse(service_path)
@@ -284,23 +332,23 @@ class ERP5ShopOrderConduit(ERP5Conduit):
 
       # Try to find the identity created for a previous ShopOrder of the same Storever member account
       person_object = None
-      organisation_object = None
+      org_object = None
       for person_id in person_folder.objectIds():
         if person_id == owner_id:
           person_object = erp5_site.restrictedTraverse(erp5_site_path + '/person/' + person_id)
           LOG("Previous person found ! >>>>>>>>",0,repr(person_object))
           break
-      for organisation_id in organisation_folder.objectIds():
+      for organisation_id in org_folder.objectIds():
         if organisation_id == owner_id:
-          organisation_object = erp5_site.restrictedTraverse(erp5_site_path + '/organisation/' + organisation_id)
-          LOG("Previous organisation found ! >>>>>>>>",0,repr(organisation_object))
+          org_object = erp5_site.restrictedTraverse(erp5_site_path + '/organisation/' + organisation_id)
+          LOG("Previous organisation found ! >>>>>>>>",0,repr(org_object))
           break
 
       # Define the previous customer structure
       previous_owner_type = ''
       if person_object != None:
         previous_owner_type += 'p'
-      if organisation_object != None:
+      if org_object != None:
         previous_owner_type += 'o'
       if len(previous_owner_type) == 0:
         previous_owner_type = None
@@ -324,27 +372,25 @@ class ERP5ShopOrderConduit(ERP5Conduit):
       if previous_owner_type != owner_type:
         # There is difference between the two (previous and current) representation of the customer
         # We have to manage the differences to create a unique customer representation
-        LOG("There is difference between previous and current >>>>>>>>",0,None)
+        LOG("There is difference between previous and current >>>>>>>>",0,'')
         if previous_owner_type == None:
           # No previous customer found, create one
           if owner_type.find('o') != -1:
-            organisation_folder.newContent ( portal_type = 'Organisation'
-                                           , id          = owner_id)
-            organisation_object = organisation_folder._getOb(owner_id)
-            LOG("new organisation created >>>>>>>>",0,repr(organisation_object))
+            org_object = org_folder.newContent( portal_type = 'Organisation'
+                                              , id          = owner_id)
+            LOG("new organisation created >>>>>>>>",0,repr(org_object))
           if owner_type.find('p') != -1:
-            person_folder.newContent ( portal_type = 'Person'
-                                     , id          = owner_id)
-            person_object = person_folder._getOb(owner_id)
+            person_object = person_folder.newContent( portal_type = 'Person'
+                                                    , id          = owner_id)
             LOG("new person created >>>>>>>>",0,repr(person_object))
         else:
           if owner_type == None:
             # Use the previous Structure
             owner_type = previous_owner_type
-            LOG("Use the previous Structure >>>>>>>>",0,None)
+            LOG("Use the previous Structure >>>>>>>>",0,'')
           else:
-            LOG("We have to convert the structure >>>>>>>>",0,None)
-#         #  XXX Be aware of that problem: the invoice for a sale order must be the same
+            LOG("We have to convert the structure >>>>>>>>",0,'')
+#         #  XXX Be aware of that problem: the invoice for a sale order must look the same (I mean when we generate the pdf version)
 #           Case to process :
 #           previous current
 #           o  -->   p
@@ -364,9 +410,8 @@ class ERP5ShopOrderConduit(ERP5Conduit):
               if owner_type.find('p') != -1 and owner_type.find('o') != -1:
                 # Create a new organisation
 #                 # TODO : factorise this code with the same above
-                organisation_folder.newContent ( portal_type = 'Organisation'
-                                               , id          = owner_id)
-                organisation_object = organisation_folder._getOb(owner_id)
+                org_object = org_folder.newContent( portal_type = 'Organisation'
+                                                  , id          = owner_id)
               else:
 #                 # TODO : Transform a person to an organisation ? Is it a good idea ?
                 pass
@@ -390,19 +435,18 @@ class ERP5ShopOrderConduit(ERP5Conduit):
           # a person and there is no previous record
           # By default, we consider the customer as a person, so we have to force to create one
           owner_type = 'p'
-          person_folder.newContent ( portal_type = 'Person'
-                                   , id          = owner_id)
-          person_object = person_folder._getOb(owner_id)
+          person_object = person_folder.newContent( portal_type = 'Person'
+                                                  , id          = owner_id)
           LOG("Create a person by default  >>>>>>>>",0,repr(person_object))
         else:
           # The structure is the same
           # We only need to be aware of data fusion between the previous and the current representation
           # So we don't need to do something because the information fusion process take place below
-          LOG("The structure is the same. don't do anything >>>>>>>>",0,None)
+          LOG("The structure is the same. don't do anything >>>>>>>>",0,'')
           pass
 
       LOG("Person object >>>>>>>>",0,repr(person_object))
-      LOG("Organisation object >>>>>>>>",0,repr(organisation_object))
+      LOG("Organisation object >>>>>>>>",0,repr(org_object))
 
       # Copy informations related to the customer in the ERP5 representation of the customer
       # Be carefull because all informations from the storever ShopOrder are optionnals
@@ -415,12 +459,20 @@ class ERP5ShopOrderConduit(ERP5Conduit):
 #         # TODO : before doing something working well in every case, copy the previou value in the comment field to traceback the modification and let me evaluate the solidity of my algorithm
 #         # TODO : perhaps it's possible to factorize the code using a generic function
         # Synchronise the street address
-        if kw.has_key('address'):
-          previous_address = person_object.getDefaultAddressStreetAddress()
-          if len(previous_address) == 0:
-            person_object.setDefaultAddressStreetAddress(kw['address'].title())
-          elif previous_address.strip().lower() != kw['address'].strip().lower():
-            LOG('We have to make the fusion of previous address with the current one  >>>>>>>', 0, None)
+
+        # Solution (d'apres seb)
+        # machin = getattr (object, methos)
+        # method(machin)
+
+        machin = self.updateObjProperty(person_object, 'DefaultAddressStreetAddress', kw, 'address')
+        LOG("My new updateObjProperty() return >>>>>>>>",0,repr(machin))
+
+#         if kw.has_key('address') and kw['address'] != None:
+#           previous_address = person_object.getDefaultAddressStreetAddress()
+#           if len(previous_address) == 0:
+#             person_object.setDefaultAddressStreetAddress(kw['address'].title())
+#           elif previous_address.strip().lower() != kw['address'].strip().lower():
+#             LOG('We have to make the fusion of previous address with the current one  >>>>>>>', 0, '')
 
         person_object.setDefaultAddressCity(kw['city'].title())
         person_object.setDefaultAddressZipCode(kw['zipcode'])
@@ -432,8 +484,10 @@ class ERP5ShopOrderConduit(ERP5Conduit):
             person_object.setDefaultAddressRegion(region_path)
 #           else:
 #             # TODO : Ask the user to select an appropriate region
-        person_object.setDefaultEmailText(kw['email'])
-        person_object.setDefaultTelephoneText(kw['phone'])
+        if kw.has_key('email') and kw['email'] != None:
+          person_object.setDefaultEmailText(kw['email'])
+        if kw.has_key('phone') and kw['phone'] != None:
+          person_object.setDefaultTelephoneText(kw['phone'])
 #         # TODO : Don't work
 #         person_object.setDefaultCareerRole("client")
         # Split the name to give at least a required LastName
@@ -450,39 +504,44 @@ class ERP5ShopOrderConduit(ERP5Conduit):
         if owner_type.find('o') != -1:
 #           # TODO : fix this
 #           person_object.setSubordination("organisation/" + owner_id)
-          organisation_object.setTitle(kw['organisation'].title())
-          organisation_object.setCorporateName(kw['organisation'].title())
-          organisation_object.setRole("client")
-          if kw.has_key('eu_vat'):
-            organisation_object.setEuVatCode(kw['eu_vat'])
+          if kw.has_key('organisation') and kw['organisation'] != None:
+            org_object.setTitle(kw['organisation'].title())
+          if kw.has_key('eu_vat') and kw['eu_vat'] != None:
+            org_object.setEuVatCode(kw['eu_vat'])
+          # Test for debug
+          if (not (kw.has_key('organisation')) or (kw.has_key('organisation') and kw['organisation'] != None)) and (not (kw.has_key('eu_vat')) or (kw.has_key('eu_vat') and kw['eu_vat'] != None)):
+            LOG("AARRGG ! Big conflict detected : this organisation has no title or eu_vat. These properties are primary key to deduced that the storever member account was an organisation >>>>>>>>>>", 0, '')
+          org_object.setCorporateName(kw['organisation'].title())
+          org_object.setRole("client")
+
       # The customer is not a person or a person of an organisation, so the customer is an organisation...
       else:
         # Link the customer with the Sale Order
         object.setDestination("organisation/" + owner_id)
         object.setDestinationDecision("organisation/" + owner_id)
         # All informations describe the organisation
-        organisation_object.setTitle(kw['organisation'].title())
-        organisation_object.setCorporateName(kw['organisation'].title())
-        organisation_object.setRole("client")
-        organisation_object.setEuVatCode(kw['eu_vat'])
-        organisation_object.setDefaultAddressStreetAddress(kw['address'].title())
-        organisation_object.setDefaultAddressCity(kw['city'].title())
-        organisation_object.setDefaultAddressZipCode(kw['zipcode'])
+        org_object.setTitle(kw['organisation'].title())
+        org_object.setCorporateName(kw['organisation'].title())
+        org_object.setRole("client")
+        org_object.setEuVatCode(kw['eu_vat'])
+        org_object.setDefaultAddressStreetAddress(kw['address'].title())
+        org_object.setDefaultAddressCity(kw['city'].title())
+        org_object.setDefaultAddressZipCode(kw['zipcode'])
         # Search the country in the region category
         if kw['country'] != None:
           region_path = self.countrySearch(erp5_site, None, kw['country'])
           if region_path != None:
-            organisation_object.setDefaultAddressRegion(region_path)
+            org_object.setDefaultAddressRegion(region_path)
 #           else:
 #             # TODO : Ask the user to select an appropriate region
-        organisation_object.setDefaultEmailText(kw['email'])
-        organisation_object.setDefaultTelephoneText(kw['phone'])
+        org_object.setDefaultEmailText(kw['email'])
+        org_object.setDefaultTelephoneText(kw['phone'])
 
       # Save the billing address in the description, because there is no dedicated place for it
       if kw.has_key('billing_address') and len(kw['billing_address']) > 0:
         object.setDescription("Send the bill to : " + kw['billing_address'])
       # Set the Title because its required
-      object.setTitle("Storever Order " + kw['order_id'])
+      object.setTitle("Storever Order " + str(kw['order_id']))
 
 #       # ONLY for information (will be used in the future)
       object.setDescription(str(object.getDescription()) + "\n\nTotal Price (with transport fees) :" + str(kw['total_price']))
@@ -531,9 +590,8 @@ class ERP5ShopOrderConduit(ERP5Conduit):
 
       # Create a new order line in this order to represent the shipment service
       ship_order_line_id = "storever-" + shipment_id
-      object.newContent( portal_type = 'Sale Order Line'
-                       , id          = ship_order_line_id)
-      ship_order_object = object._getOb(ship_order_line_id)
+      ship_order_object = object.newContent( portal_type = 'Sale Order Line'
+                                           , id          = ship_order_line_id)
       ship_order_object.setQuantity(1.0)
       ship_order_object.setPrice(kw['send_fee'])
       ship_order_object.setQuantityUnit('Unit')
@@ -579,15 +637,13 @@ class ERP5ShopOrderConduit(ERP5Conduit):
         product_object.setSourceBasePriceValidity(kw['product_expiration_date'])
       product_object.setBasePrice(kw['product_price'])
       product_object.setQuantityUnit('Unit')
-
-      # Set the worflow status
-      self.setProductWorkflow(product_object, kw['product_title'])
-
+      # Save the worflow status for later modification
+      workflow_joblist.append((product_object, kw['product_title']))
       # In storever, every option are set as string in the title of the OrderLine
       # This part of code create a list of all options choosen by the customer for this product
       splitted_title = kw['title'].strip().split(":")
       option_list = (":".join(splitted_title[1:])).split("/")
-      LOG('Customer option list: ', 0, repr(option_list))
+      LOG('Customer option list >>>>>> ', 0, repr(option_list))
 
       # Now, we will find the price of each option
       option_classes = [ kw['product_disk_price']
@@ -603,7 +659,7 @@ class ERP5ShopOrderConduit(ERP5Conduit):
             if option == option_key.strip():
               priced_list[option] = option_class[option_key]
 #       # TODO : there is no default options in the final priced_list. Is the option 'default' important ?
-      LOG('Customer option priced list: ', 0, repr(priced_list))
+      LOG('Customer option priced list >>>>>>>>> ', 0, repr(priced_list))
 
       # In ERP5, we have decided to represent some options as variation of a product
       #   and some options as new order line of product
@@ -621,8 +677,8 @@ class ERP5ShopOrderConduit(ERP5Conduit):
           keyboard_options[option_key] = options_prices[option_key]
         elif option_key.lower().find("cd") != -1 or option_key.lower().find("dvd") != -1:
           optical_options[option_key] = options_prices[option_key]
-      LOG('Product keyboard layout priced list: ', 0, repr(keyboard_options))
-      LOG('Product optical drive priced list: ', 0, repr(optical_options))
+      LOG('Product keyboard layout priced list >>>>>>>>> ', 0, repr(keyboard_options))
+      LOG('Product optical drive priced list >>>>>>>>> ', 0, repr(optical_options))
 
       # Create a data structure containing all allowed variations
       variant_category_list = [ ('hd_size',        kw['product_disk_price'])
@@ -644,7 +700,7 @@ class ERP5ShopOrderConduit(ERP5Conduit):
               cat_base_object = portal_cat._getOb(cat_base)
               cat_base_object.newContent ( portal_type = 'Category'
                                          , id          = cat_id)
-              LOG("New category '", 0, cat_path + "' created")
+              LOG("New created category >>>>>>>>>>> ", 0, cat_path)
 
       # Set the base variation of the product
       product_object.setVariationBaseCategoryList(base_cat_list)
@@ -661,6 +717,7 @@ class ERP5ShopOrderConduit(ERP5Conduit):
       for option in priced_list:
         option_is_variant = None
         for (cat_base, cat_data) in variant_category_list:
+          LOG('editDocument, cat_base',0,cat_base)
           base_cat_object = portal_cat.resolveCategory(cat_base)
           cat_list = base_cat_object.getCategoryChildIdItemList()
           for (category, category_bis) in cat_list:
@@ -675,7 +732,7 @@ class ERP5ShopOrderConduit(ERP5Conduit):
         if option_is_variant == None:
           customer_product_option_list[option] = priced_list[option]
       if len(customer_product_option_list) + len(customer_product_variation_list) != len(priced_list):
-        LOG('Wrong repartition of the customer priced list', 200)
+        LOG('>>>>>>> Wrong repartition of the customer priced list', 200)
       LOG('>>>>>> Customer product option priced list: ', 0, repr(customer_product_option_list))
       LOG('>>>>>> Customer product variation priced list: ', 0, repr(customer_product_variation_list))
       LOG('>>>>>> Customer product base variation list: ', 0, repr(customer_product_base_variation_list))
@@ -700,20 +757,18 @@ class ERP5ShopOrderConduit(ERP5Conduit):
         opt_prod_object.setTitle(opt_prod_title.title())
         opt_prod_object.setBasePrice(opt_prod_price)
         opt_prod_object.setQuantityUnit('Unit')
-        # Set the workflow state of the optionnal product
-        self.setProductWorkflow(opt_prod_object, opt_prod_key)
+        # Save the workflow state changing for later modification
+        workflow_joblist.append((opt_prod_object, opt_prod_key))
 
         # Get the last number of order lines
         # This process is needed to distinguish the same option created for two different product
-        #   and avoid problem when a new Order line is created for a option product already used
+        #   and avoid problem when a new Order line is created for an option product already used
         #   inside the same Sale Order
         last_line_num = self.getLastOrderLineNumber(parent_order_object)
         opt_prod_line_id = "storever-" + str(last_line_num) + "-" + opt_prod_key
         # Create an order line for the product
-        parent_order_object.newContent ( portal_type = 'Sale Order Line'
-                                       , id          = opt_prod_line_id)
-        opt_order_line_object = parent_order_object._getOb(opt_prod_line_id)
-
+        opt_order_line_object = parent_order_object.newContent( portal_type = 'Sale Order Line'
+                                                              , id          = opt_prod_line_id)
         # Set several properties of the new orderLine
         opt_order_line_object.setQuantityUnit('Unit')
         opt_order_line_object.setPrice(opt_prod_price)
@@ -725,13 +780,6 @@ class ERP5ShopOrderConduit(ERP5Conduit):
         # Calcul the sum of option prices
         options_price_sum += float(opt_prod_price)
 
-
-
-
-
-
-
-
 #       # TODO: don't forget to manage the VAT values
 
 #      TODO: # Try to find a previous OrderLine to update
@@ -761,4 +809,10 @@ class ERP5ShopOrderConduit(ERP5Conduit):
 #       # TODO : fix this
       # object.setVariationCategoryList(category_list)
 
-      return
\ No newline at end of file
+    # Do all workflow change at the end
+    LOG("enter workflow loop >>>>>>>>",0,repr(workflow_joblist))
+    for (object, object_title) in workflow_joblist:
+      LOG("Workflow to change :: >>>>>>>>",0,repr(object, object_title))
+      self.setProductWorkflow(object, object_title)
+
+    return
diff --git a/product/ERP5SyncML/Publication.py b/product/ERP5SyncML/Publication.py
index 4dfb9bfdaa..a1a2dfd715 100755
--- a/product/ERP5SyncML/Publication.py
+++ b/product/ERP5SyncML/Publication.py
@@ -33,6 +33,7 @@ from Products.ERP5Type import Permissions
 from Products.ERP5Type.Document.Folder import Folder
 from AccessControl import ClassSecurityInfo
 from Products.ERP5Type import PropertySheet
+from zLOG import LOG
 
 def addSubscriber( self, id, title='', REQUEST=None ):
     """
@@ -83,6 +84,16 @@ class Subscriber(Subscription):
       Send ACK for a group of documents
     """
 
+  def getConduit(self):
+    """
+    Return the conduit of the publication
+    """
+    #LOG('Subscriber.getConduit, self.getPhysicalPath()',0,self.getPhysicalPath())
+    #LOG('Subscriber.getConduit, self.getParent().getPhysicalPath()',0,self.aq_parent.getPhysicalPath())
+    #LOG('Subscriber.getConduit, self.getParent()',0,self.getParent())
+    return self.aq_parent.getConduit()
+    #return self.conduit
+
   def SendDocuments(self):
     """
       We send all the updated documents (ie. documents not marked
@@ -142,7 +153,7 @@ class Publication(Subscription):
   constructors =   (addPublication,)
 
   # Constructor
-  def __init__(self, id, title, publication_url, destination_path, query, xml_mapping, gpg_key):
+  def __init__(self, id, title, publication_url, destination_path, query, xml_mapping, conduit, gpg_key):
     """
       constructor
     """
@@ -156,6 +167,7 @@ class Publication(Subscription):
     self.gpg_key = gpg_key
     self.setGidGenerator(None)
     self.setIdGenerator(None)
+    self.setConduit(conduit)
     Folder.__init__(self, id)
     self.title = title
 
diff --git a/product/ERP5SyncML/Subscription.py b/product/ERP5SyncML/Subscription.py
index 244922f614..10450aab0b 100755
--- a/product/ERP5SyncML/Subscription.py
+++ b/product/ERP5SyncML/Subscription.py
@@ -342,7 +342,10 @@ class Signature(Folder,SyncCode):
     """
       set the XML corresponding to the object
     """
-    return self.xml
+    xml =  getattr(self,'xml',None)
+    if xml == '':
+      xml = None
+    return xml
 
   def setTempXML(self, xml):
     """
@@ -610,7 +613,7 @@ class Subscription(Folder, SyncCode):
                               )
 
   # Constructor
-  def __init__(self, id, title, publication_url, subscription_url, destination_path, query, xml_mapping, gpg_key):
+  def __init__(self, id, title, publication_url, subscription_url, destination_path, query, xml_mapping, conduit, gpg_key):
     """
       We need to create a dictionnary of
       signatures of documents which belong to the synchronisation
@@ -621,7 +624,7 @@ class Subscription(Folder, SyncCode):
     self.subscription_url = str(subscription_url)
     self.destination_path = str(destination_path)
     self.setQuery(query)
-    self.xml_mapping = xml_mapping
+    self.setXMLMapping(xml_mapping)
     self.anchor = None
     self.session_id = 0
     #self.signatures = PersistentMapping()
@@ -631,6 +634,7 @@ class Subscription(Folder, SyncCode):
     self.gpg_key = gpg_key
     self.setGidGenerator(None)
     self.setIdGenerator(None)
+    self.setConduit(conduit)
     Folder.__init__(self, id)
     self.title = title
 
@@ -675,6 +679,14 @@ class Subscription(Folder, SyncCode):
     LOG('Subscription',0,'getSynchronizationType: %s' % code)
     return code
 
+  def setXMLMapping(self, value):
+    """
+    this the name of the method used in order to get the xml
+    """
+    if value == '':
+      value = None
+    self.xml_mapping = value
+
   def checkCorrectRemoteSessionId(self, session_id):
     """
     We will see if the last session id was the same
@@ -726,6 +738,19 @@ class Subscription(Folder, SyncCode):
     """
     self.id = id
 
+  def setConduit(self, value):
+    """
+      set the Conduit
+    """
+    self.conduit = value
+
+  def getConduit(self):
+    """
+      get the Conduit
+
+    """
+    return getattr(self,'conduit',None)
+
   def getQuery(self):
     """
       return the query
@@ -748,8 +773,8 @@ class Subscription(Folder, SyncCode):
     """
       set the query
     """
-    if query in (None,''):
-      query = 'objectValues'
+    if query == '':
+      query = None
     self.query = query
 
   def getPublicationUrl(self):
@@ -777,11 +802,17 @@ class Subscription(Folder, SyncCode):
     xml_mapping = getattr(self,'xml_mapping','asXML')
     return xml_mapping
 
-  def setXMLMapping(self, xml_mapping):
+  def getXMLFromObject(self,object):
     """
       return the xml mapping
     """
-    self.xml_mapping = xml_mapping
+    xml_mapping = self.getXMLMapping()
+    xml = ''
+    if xml_mapping is not None:
+      func = getattr(object,xml_mapping,None)
+      if func is not None:
+        xml = func()
+    return xml
 
   def setGidGenerator(self, method):
     """
@@ -843,6 +874,15 @@ class Subscription(Folder, SyncCode):
     LOG('getObjectFromGid',0,'returning None')
     return None
 
+#  def setOneWaySyncFromServer(self,value):
+#    """
+#    If this option is enabled, then we will not 
+#    send our own modifications
+#    """
+#    self.one_way_sync_from_server = value
+#
+
+
   def getObjectList(self):
     """
     This returns the list of sub-object corresponding
@@ -851,6 +891,8 @@ class Subscription(Folder, SyncCode):
     destination = self.getDestination()
     query = self.getQuery()
     query_list = []
+    if query is None:
+      return query_list
     if type(query) is type('a'):
       query_method = getattr(destination,query,None)
       if query_method is not None:
diff --git a/product/ERP5SyncML/SynchronizationTool.py b/product/ERP5SyncML/SynchronizationTool.py
index eda0e354d7..cc4aa96f3c 100755
--- a/product/ERP5SyncML/SynchronizationTool.py
+++ b/product/ERP5SyncML/SynchronizationTool.py
@@ -36,6 +36,7 @@ from Globals import InitializeClass, DTMLFile, PersistentMapping, Persistent
 from AccessControl import ClassSecurityInfo, getSecurityManager
 from Products.CMFCore import CMFCorePermissions
 from Products.ERP5SyncML import _dtmldir
+from Products.ERP5SyncML import Conduit
 from Publication import Publication,Subscriber
 from Products.BTreeFolder2.BTreeFolder2 import BTreeFolder2
 from Subscription import Subscription,Signature
@@ -158,7 +159,7 @@ class SynchronizationTool( SubscriptionSynchronization, PublicationSynchronizati
 
   security.declareProtected(Permissions.ModifyPortalContent, 'manage_addPublication')
   def manage_addPublication(self, title, publication_url, destination_path,
-            query, xml_mapping, gpg_key, RESPONSE=None):
+            query, xml_mapping, conduit, gpg_key, RESPONSE=None):
     """
       create a new publication
     """
@@ -168,7 +169,7 @@ class SynchronizationTool( SubscriptionSynchronization, PublicationSynchronizati
     folder = self.getObjectContainer()
     new_id = self.getPublicationIdFromTitle(title)
     pub = Publication(new_id, title, publication_url, destination_path,
-                      query, xml_mapping, gpg_key)
+                      query, xml_mapping, conduit, gpg_key)
     folder._setObject( new_id, pub )
     #if len(self.list_publications) == 0:
     #  self.list_publications = PersistentMapping()
@@ -178,7 +179,7 @@ class SynchronizationTool( SubscriptionSynchronization, PublicationSynchronizati
 
   security.declareProtected(Permissions.ModifyPortalContent, 'manage_addSubscription')
   def manage_addSubscription(self, title, publication_url, subscription_url,
-                       destination_path, query, xml_mapping, gpg_key, RESPONSE=None):
+                       destination_path, query, xml_mapping, conduit, gpg_key, RESPONSE=None):
     """
       XXX should be renamed as addSubscription
       create a new subscription
@@ -189,7 +190,7 @@ class SynchronizationTool( SubscriptionSynchronization, PublicationSynchronizati
     folder = self.getObjectContainer()
     new_id = self.getSubscriptionIdFromTitle(title)
     sub = Subscription(new_id, title, publication_url, subscription_url,
-                       destination_path, query, xml_mapping, gpg_key)
+                       destination_path, query, xml_mapping, conduit, gpg_key)
     folder._setObject( new_id, sub )
     #if len(self.list_subscriptions) == 0:
     #  self.list_subscriptions = PersistentMapping()
@@ -199,7 +200,7 @@ class SynchronizationTool( SubscriptionSynchronization, PublicationSynchronizati
 
   security.declareProtected(Permissions.ModifyPortalContent, 'manage_editPublication')
   def manage_editPublication(self, title, publication_url, destination_path,
-                       query, xml_mapping, gpg_key, RESPONSE=None):
+                       query, xml_mapping, conduit, gpg_key, RESPONSE=None):
     """
       modify a publication
     """
@@ -208,6 +209,7 @@ class SynchronizationTool( SubscriptionSynchronization, PublicationSynchronizati
     pub.setPublicationUrl(publication_url)
     pub.setDestinationPath(destination_path)
     pub.setQuery(query)
+    pub.setConduit(conduit)
     pub.setXMLMapping(xml_mapping)
     pub.setGPGKey(gpg_key)
     if RESPONSE is not None:
@@ -215,7 +217,7 @@ class SynchronizationTool( SubscriptionSynchronization, PublicationSynchronizati
 
   security.declareProtected(Permissions.ModifyPortalContent, 'manage_editSubscription')
   def manage_editSubscription(self, title, publication_url, subscription_url,
-             destination_path, query, xml_mapping, gpg_key, RESPONSE=None):
+             destination_path, query, xml_mapping, conduit, gpg_key, RESPONSE=None):
     """
       modify a subscription
     """
@@ -224,6 +226,7 @@ class SynchronizationTool( SubscriptionSynchronization, PublicationSynchronizati
     sub.setPublicationUrl(publication_url)
     sub.setDestinationPath(destination_path)
     sub.setQuery(query)
+    sub.setConduit(conduit)
     sub.setXMLMapping(xml_mapping)
     sub.setGPGKey(gpg_key)
     sub.setSubscriptionUrl(subscription_url)
@@ -370,20 +373,22 @@ class SynchronizationTool( SubscriptionSynchronization, PublicationSynchronizati
           #conflict.setDomain('Publication')
           conflict.setSubscriber(subscriber)
           #conflict.setDomainId(subscriber.getId())
-          conflict_list += [conflict.__of__(self)]
+          if path is None or conflict.getObjectPath() == path:
+            conflict_list += [conflict.__of__(subscriber)]
     for subscription in self.getSubscriptionList():
       sub_conflict_list = subscription.getConflictList()
       for conflict in sub_conflict_list:
         #conflict.setDomain('Subscription')
         conflict.setSubscriber(subscription)
         #conflict.setDomainId(subscription.getId())
-        conflict_list += [conflict.__of__(self)]
-    if path is not None: # Retrieve only conflicts for a given path
-      new_list = []
-      for conflict in conflict_list:
-        if conflict.getObjectPath() == path:
-          new_list += [conflict.__of__(self)]
-      return new_list
+        if path is None or conflict.getObjectPath() == path:
+          conflict_list += [conflict.__of__(subscription)]
+    #if path is not None: # Retrieve only conflicts for a given path
+    #  new_list = []
+    #  for conflict in conflict_list:
+    #    if conflict.getObjectPath() == path:
+    #      new_list += [conflict.__of__(self)]
+    #  return new_list
     return conflict_list
 
   security.declareProtected(Permissions.AccessContentsInformation,'getDocumentConflictList')
@@ -530,7 +535,9 @@ class SynchronizationTool( SubscriptionSynchronization, PublicationSynchronizati
     object_id = docid
     if object_id in directory.objectIds():
         directory._delObject(object_id)
-        conduit = ERP5Conduit()
+        #conduit = ERP5Conduit()
+        conduit_name = subscriber.getConduit()
+        conduit = getattr(getattr(Conduit,conduit_name),conduit_name)()
         conduit.addNode(xml=publisher_xml,object=directory,object_id=object_id)
         subscriber_document = directory._getOb(object_id)
         for c in self.getConflictList(conflict.getObjectPath()):
@@ -565,7 +572,9 @@ class SynchronizationTool( SubscriptionSynchronization, PublicationSynchronizati
     publisher_xml = self.getXMLObject(object=publisher_object,xml_mapping = subscriber.getXMLMapping())
     directory = publisher_object.aq_inner.aq_parent
     object_id = self._getCopyId(publisher_object)
-    conduit = ERP5Conduit()
+    #conduit = ERP5Conduit()
+    conduit_name = subscriber.getConduit()
+    conduit = getattr(getattr(Conduit,conduit_name),conduit_name)()
     conduit.addNode(xml=publisher_xml,object=directory,object_id=object_id)
     subscriber_document = directory._getOb(object_id)
     subscriber_document._conflict_resolution = 1
@@ -576,7 +585,6 @@ class SynchronizationTool( SubscriptionSynchronization, PublicationSynchronizati
     conflict.setCopyPath(copy_path)
     return copy_path
     
-  
   security.declareProtected(Permissions.AccessContentsInformation, 'getSubscriberDocument')
   def getSubscriberDocument(self, conflict):
     """
@@ -613,7 +621,9 @@ class SynchronizationTool( SubscriptionSynchronization, PublicationSynchronizati
     # get the signature:
     LOG('p_sync.setRemoteObject, subscriber: ',0,subscriber)
     signature = subscriber.getSignature(object.getId()) # XXX may be change for rid
-    conduit = ERP5Conduit()
+    #conduit = ERP5Conduit()
+    conduit_name = subscriber.getConduit()
+    conduit = getattr(getattr(Conduit,conduit_name),conduit_name)()
     for xupdate in conflict.getXupdateList():
       conduit.updateNode(xml=xupdate,object=object,force=1)
     if solve_conflict:
@@ -632,7 +642,6 @@ class SynchronizationTool( SubscriptionSynchronization, PublicationSynchronizati
             directory._delObject(copy_id)
         signature.setStatus(self.PUB_CONFLICT_MERGE)
 
-
   security.declareProtected(Permissions.ModifyPortalContent, 'manageLocalValue')
   def managePublisherValue(self, subscription_url, property_id, object_path, RESPONSE=None):
     """
diff --git a/product/ERP5SyncML/XMLSyncUtils.py b/product/ERP5SyncML/XMLSyncUtils.py
index ead5ec4072..e8d0c766ca 100755
--- a/product/ERP5SyncML/XMLSyncUtils.py
+++ b/product/ERP5SyncML/XMLSyncUtils.py
@@ -218,12 +218,13 @@ class XMLSyncUtilsMixin(SyncCode):
     """
     xml_method = None
     xml = ""
-    if hasattr(object,xml_mapping):
-      xml_method = getattr(object,xml_mapping)
-    elif hasattr(object,'manage_FTPget'):
-      xml_method = getattr(object,'manage_FTPget')
-    if xml_method is not None:
-      xml = xml_method()
+    if xml_mapping is not None:
+      if hasattr(object,xml_mapping):
+        xml_method = getattr(object,xml_mapping)
+      elif hasattr(object,'manage_FTPget'):
+        xml_method = getattr(object,'manage_FTPget')
+      if xml_method is not None:
+        xml = xml_method()
     return xml
 
   def getSessionId(self, xml):
@@ -601,23 +602,12 @@ class XMLSyncUtilsMixin(SyncCode):
         # Here we first check if the object was modified or not by looking at dates
         if signature is not None:
           signature.checkSynchronizationNeeded(object)
-#          LOG('getSyncMLData',0,'signature.status: %s' % str(signature.getStatus()))
-#          LOG('getSyncMLData',0,'signature.action: %s' % str(signature.getAction()))
-#          last_modification = DateTime(object.ModificationDate())
-#          LOG('getSyncMLData object.ModificationDate()',0,object.ModificationDate())
-#          last_synchronization = signature.getLastSynchronizationDate()
-#          parent = object.aq_parent
-#          # XXX CPS Specific
-#          #if parent.id == 'portal_repository':
-#          if 1:
-#            if last_synchronization is not None and last_modification is not None:
-#              if last_synchronization > last_modification:
-#                LOG('getSyncMLData, no modification on: ',0,object.id)
-#                signature.setStatus(self.SYNCHRONIZED)
         status = self.SENT
         more_data=0
         # For the case it was never synchronized, we have to send everything
-        if signature==None or (signature.getXML()==None and signature.getStatus()!=self.PARTIAL) or \
+        if signature is not None and signature.getXMLMapping()==None:
+          pass
+        elif signature==None or (signature.getXML()==None and signature.getStatus()!=self.PARTIAL) or \
             self.getAlertCode(remote_xml)==self.SLOW_SYNC:
           #LOG('PubSyncModif',0,'Current object.getPath: %s' % object.getPath())
           LOG('getSyncMLData',0,'no signature for gid: %s' % object_gid)
@@ -781,10 +771,10 @@ class XMLSyncUtilsMixin(SyncCode):
             LOG('applyActionList',0,'object after add: %s' % repr(object))
           if object is not None:
             LOG('SyncModif',0,'addNode, found the object')
-            mapping = getattr(object,domain.getXMLMapping(),None)
-            xml_object = ''
-            if mapping is not None:
-              xml_object = mapping()
+            #mapping = getattr(object,domain.getXMLMapping(),None)
+            xml_object = domain.getXMLFromObject(object)
+            #if mapping is not None:
+            #  xml_object = mapping()
             signature.setStatus(self.SYNCHRONIZED)
             signature.setId(object.getId())
             signature.setXML(xml_object)
@@ -798,14 +788,14 @@ class XMLSyncUtilsMixin(SyncCode):
             signature = subscriber.getSignature(object_gid)
             LOG('SyncModif',0,'previous signature: %s' % str(signature))
             previous_xml = signature.getXML()
-            LOG('SyncModif',0,'previous signature: %i' % len(previous_xml))
+            #LOG('SyncModif',0,'previous signature: %i' % len(previous_xml))
             conflict_list += conduit.updateNode(xml=data_subnode, object=object,
                               previous_xml=signature.getXML(),force=force,
                               simulate=simulate)
-            mapping = getattr(object,domain.getXMLMapping(),None)
-            xml_object = ''
-            if mapping is not None:
-              xml_object = mapping()
+            #mapping = getattr(object,domain.getXMLMapping(),None)
+            xml_object = domain.getXMLFromObject(object)
+            #if mapping is not None:
+            #  xml_object = mapping()
             signature.setTempXML(xml_object)
             if conflict_list != []:
               status_code = self.CONFLICT
@@ -922,7 +912,8 @@ class XMLSyncUtils(XMLSyncUtilsMixin):
       Send the server modification, this happens after the Synchronization
       initialization
     """
-    from Products.ERP5SyncML.Conduit.ERP5Conduit import ERP5Conduit
+    #from Products.ERP5SyncML.Conduit.ERP5Conduit import ERP5Conduit
+    from Products.ERP5SyncML import Conduit
     has_response = 0 #check if syncmodif replies to this messages
     cmd_id = 1 # specifies a SyncML message-unique command identifier
     LOG('SyncModif',0,'Starting... domain: %s' % str(domain))
@@ -968,7 +959,9 @@ class XMLSyncUtils(XMLSyncUtilsMixin):
                                          remote_xml=remote_xml)
 
     alert_code = self.getAlertCode(remote_xml)
-    conduit = ERP5Conduit()
+    #conduit = ERP5Conduit()
+    conduit_name = subscriber.getConduit()
+    conduit = getattr(getattr(Conduit,conduit_name),conduit_name)()
     LOG('SyncModif, subscriber: ',0,subscriber)
     # Then apply the list of actions
     (xml_confirmation,has_next_action,cmd_id) = self.applyActionList(cmd_id=cmd_id,
diff --git a/product/ERP5SyncML/dtml/managePublications.dtml b/product/ERP5SyncML/dtml/managePublications.dtml
index 09c91325d4..f54746cd61 100755
--- a/product/ERP5SyncML/dtml/managePublications.dtml
+++ b/product/ERP5SyncML/dtml/managePublications.dtml
@@ -87,6 +87,16 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
         <input type="text" name="xml_mapping" value="<dtml-var getXMLMapping>" size="40" />
         </td>
       </tr>
+      <tr>
+        <td align="left" valign="top">
+        <div class="form-label">
+        Conduit
+        </label></div>
+        </td>
+        <td align="left" valign="top">
+        <input type="text" name="conduit" value="<dtml-var getConduit>" size="40" />
+        </td>
+      </tr>
       <tr>
         <td align="left" valign="top">
         <div class="form-label">
diff --git a/product/ERP5SyncML/dtml/manageSubscriptions.dtml b/product/ERP5SyncML/dtml/manageSubscriptions.dtml
index ee9cb6f68d..0b2170f134 100755
--- a/product/ERP5SyncML/dtml/manageSubscriptions.dtml
+++ b/product/ERP5SyncML/dtml/manageSubscriptions.dtml
@@ -97,6 +97,16 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
         <input type="text" name="xml_mapping" value="<dtml-var getXMLMapping>" size="40" />
         </td>
       </tr>
+      <tr>
+        <td align="left" valign="top">
+        <div class="form-label">
+        Conduit
+        </label></div>
+        </td>
+        <td align="left" valign="top">
+        <input type="text" name="conduit" value="<dtml-var getConduit>" size="40" />
+        </td>
+      </tr>
       <tr>
         <td align="left" valign="top">
         <div class="form-label">
diff --git a/product/ERP5SyncML/dtml/manage_addPublication.dtml b/product/ERP5SyncML/dtml/manage_addPublication.dtml
index c898ab29a1..f1a24a7d5c 100755
--- a/product/ERP5SyncML/dtml/manage_addPublication.dtml
+++ b/product/ERP5SyncML/dtml/manage_addPublication.dtml
@@ -83,6 +83,16 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
     <input type="text" name="xml_mapping" size="40" />
     </td>
   </tr>
+  <tr>
+    <td align="left" valign="top">
+    <div class="form-label">
+    Conduit
+    </label></div>
+    </td>
+    <td align="left" valign="top">
+    <input type="text" name="conduit" size="40" />
+    </td>
+  </tr>
   <tr>
     <td align="left" valign="top">
     <div class="form-label">
diff --git a/product/ERP5SyncML/dtml/manage_addSubscription.dtml b/product/ERP5SyncML/dtml/manage_addSubscription.dtml
index 506c47f63f..6c0c0ac2af 100755
--- a/product/ERP5SyncML/dtml/manage_addSubscription.dtml
+++ b/product/ERP5SyncML/dtml/manage_addSubscription.dtml
@@ -93,6 +93,16 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
     <input type="text" name="xml_mapping" size="40" />
     </td>
   </tr>
+  <tr>
+    <td align="left" valign="top">
+    <div class="form-label">
+    Conduit
+    </label></div>
+    </td>
+    <td align="left" valign="top">
+    <input type="text" name="conduit" size="40" />
+    </td>
+  </tr>
   <tr>
     <td align="left" valign="top">
     <div class="form-label">
diff --git a/product/ERP5SyncML/tests/testERP5SyncML.py b/product/ERP5SyncML/tests/testERP5SyncML.py
index 36eef8fec7..dc103a4447 100755
--- a/product/ERP5SyncML/tests/testERP5SyncML.py
+++ b/product/ERP5SyncML/tests/testERP5SyncML.py
@@ -107,7 +107,7 @@ class TestERP5SyncML(ERP5TypeTestCase):
       /person_client2 : empty
     """
     #return ('sync_crm',)
-    return ('erp5_core',)
+    return ()
 
   def getSynchronizationTool(self):
     return getattr(self.getPortal(), 'portal_synchronizations', None)
@@ -124,57 +124,57 @@ class TestERP5SyncML(ERP5TypeTestCase):
   def getPortalId(self):
     return self.getPortal().getId()
 
-  def testHasEverything(self, quiet=0, run=run_all_test):
+  def test_01_HasEverything(self, quiet=0, run=run_all_test):
     # Test if portal_synchronizations was created
     if not run: return
     if not quiet:
       ZopeTestCase._print('\nTest Has Everything ')
-      LOG('Testing... ',0,'testHasEverything')
+      LOG('Testing... ',0,'test_01_HasEverything')
     self.failUnless(self.getSynchronizationTool()!=None)
     #self.failUnless(self.getPersonServer()!=None)
     #self.failUnless(self.getPersonClient1()!=None)
     #self.failUnless(self.getPersonClient2()!=None)
 
-  def testAddPublication(self, quiet=0, run=run_all_test):
+  def test_02_AddPublication(self, quiet=0, run=run_all_test):
     if not run: return
     if not quiet:
       ZopeTestCase._print('\nTest Add a Publication ')
-      LOG('Testing... ',0,'testAddPublication')
+      LOG('Testing... ',0,'test_02_AddPublication')
     portal_id = self.getPortalName()
     portal_sync = self.getSynchronizationTool()
     portal_sync.manage_addPublication(self.pub_id,self.publication_url,
-                                      '/%s/person_server' % portal_id,'',
-                                      self.xml_mapping,'')
+                                      '/%s/person_server' % portal_id,'objectValues',
+                                      self.xml_mapping,'ERP5Conduit','')
     pub = portal_sync.getPublication(self.pub_id)
     self.failUnless(pub is not None)
 
-  def testAddSubscription1(self, quiet=0, run=run_all_test):
+  def test_03_AddSubscription1(self, quiet=0, run=run_all_test):
     if not run: return
     if not quiet:
       ZopeTestCase._print('\nTest Add First Subscription ')
-      LOG('Testing... ',0,'testAddSubscription1')
+      LOG('Testing... ',0,'test_03_AddSubscription1')
     portal_id = self.getPortalId()
     portal_sync = self.getSynchronizationTool()
     portal_sync.manage_addSubscription(self.sub_id1,self.publication_url,
-                          self.subscription_url1,'/%s/person_client1' % portal_id,'',
-                          self.xml_mapping,'')
+                          self.subscription_url1,'/%s/person_client1' % portal_id,'objectValues',
+                          self.xml_mapping,'ERP5Conduit','')
     sub = portal_sync.getSubscription(self.sub_id1)
     self.failUnless(sub is not None)
 
-  def testAddSubscription2(self, quiet=0, run=run_all_test):
+  def test_04_AddSubscription2(self, quiet=0, run=run_all_test):
     if not run: return
     if not quiet:
       ZopeTestCase._print('\nTest Add Second Subscription ')
-      LOG('Testing... ',0,'testAddSubscription2')
+      LOG('Testing... ',0,'test_04_AddSubscription2')
     portal_id = self.getPortalId()
     portal_sync = self.getSynchronizationTool()
     portal_sync.manage_addSubscription(self.sub_id2,self.publication_url,
-                          self.subscription_url2,'/%s/person_client2' % portal_id,'',
-                          self.xml_mapping,'')
+                          self.subscription_url2,'/%s/person_client2' % portal_id,'objectValues',
+                          self.xml_mapping,'ERP5Conduit','')
     sub = portal_sync.getSubscription(self.sub_id2)
     self.failUnless(sub is not None)
 
-  def login(self, quiet=0, run=run_all_test):
+  def login(self, quiet=0):
     uf = self.getPortal().acl_users
     uf._doAddUser('seb', '', ['Manager'], [])
     user = uf.getUserById('seb').__of__(uf)
@@ -213,12 +213,12 @@ class TestERP5SyncML(ERP5TypeTestCase):
     self.failUnless(nb_person==2)
     return nb_person
 
-  def setupPublicationAndSubscription(self, quiet=0, run=run_all_test):
-    self.testAddPublication(quiet=1,run=1)
-    self.testAddSubscription1(quiet=1,run=1)
-    self.testAddSubscription2(quiet=1,run=1)
+  def setupPublicationAndSubscription(self, quiet=0, run=1):
+    self.test_02_AddPublication(quiet=1,run=1)
+    self.test_03_AddSubscription1(quiet=1,run=1)
+    self.test_04_AddSubscription2(quiet=1,run=1)
       
-  def setupPublicationAndSubscriptionAndGid(self, quiet=0, run=run_all_test):
+  def setupPublicationAndSubscriptionAndGid(self, quiet=0, run=1):
     self.setupPublicationAndSubscription(quiet=1,run=1)
     def getGid(object):
       return object.getTitle()
@@ -233,20 +233,20 @@ class TestERP5SyncML(ERP5TypeTestCase):
     sub1.setIdGenerator('generateNewId')
     sub2.setIdGenerator('generateNewId')
 
-  def testGetSynchronizationList(self, quiet=0, run=run_all_test):
+  def test_05_GetSynchronizationList(self, quiet=0, run=run_all_test):
     # This test the getSynchronizationList, ie,
     # We want to see if we retrieve both the subscription
     # and the publication
     if not run: return
     if not quiet:
       ZopeTestCase._print('\nTest getSynchronizationList ')
-      LOG('Testing... ',0,'testGetSynchronizationList')
+      LOG('Testing... ',0,'test_05_GetSynchronizationList')
     self.setupPublicationAndSubscription(quiet=1,run=1)
     portal_sync = self.getSynchronizationTool()
     synchronization_list = portal_sync.getSynchronizationList()
     self.failUnless(len(synchronization_list)==self.nb_synchronization)
 
-  def testGetObjectList(self, quiet=0, run=run_all_test):
+  def test_06_GetObjectList(self, quiet=0, run=run_all_test):
     """
     This test the default getObjectList, ie, when the
     query is 'objectValues', and this also test if we enter
@@ -255,7 +255,7 @@ class TestERP5SyncML(ERP5TypeTestCase):
     if not run: return
     if not quiet:
       ZopeTestCase._print('\nTest getObjectList ')
-      LOG('Testing... ',0,'testGetObjectList')
+      LOG('Testing... ',0,'test_06_GetObjectList')
     self.login()
     self.setupPublicationAndSubscription(quiet=1,run=1)
     nb_person = self.populatePersonServer(quiet=1,run=1)
@@ -276,7 +276,7 @@ class TestERP5SyncML(ERP5TypeTestCase):
     object_list = publication.getObjectList()
     self.failUnless(len(object_list)==1)
 
-  def testExportImport(self, quiet=0, run=run_all_test):
+  def test_07_ExportImport(self, quiet=0, run=run_all_test):
     """
     We will try to export a person with asXML
     And then try to add it to another folder with a conduit
@@ -284,7 +284,7 @@ class TestERP5SyncML(ERP5TypeTestCase):
     if not run: return
     if not quiet:
       ZopeTestCase._print('\nTest Export and Import ')
-      LOG('Testing... ',0,'testExportImport')
+      LOG('Testing... ',0,'test_07_ExportImport')
     self.login()
     self.populatePersonServer(quiet=1,run=1)
     person_server = self.getPersonServer()
@@ -303,7 +303,7 @@ class TestERP5SyncML(ERP5TypeTestCase):
     c_local_role = person_client1.get_local_roles()
     self.assertEqual(s_local_role,c_local_role)
 
-  def synchronize(self, id, run=run_all_test):
+  def synchronize(self, id, run=1):
     """
     This just define how we synchronize, we have
     to define it here because it is specific to the unit testing
@@ -334,7 +334,7 @@ class TestERP5SyncML(ERP5TypeTestCase):
       nb_message += 1 + result['has_response']
     return nb_message
 
-  def synchronizeWithBrokenMessage(self, id, run=run_all_test):
+  def synchronizeWithBrokenMessage(self, id, run=1):
     """
     This just define how we synchronize, we have
     to define it here because it is specific to the unit testing
@@ -371,13 +371,13 @@ class TestERP5SyncML(ERP5TypeTestCase):
       nb_message += 1 + result['has_response']
     return nb_message
 
-  def testFirstSynchronization(self, quiet=0, run=run_all_test):
+  def test_08_FirstSynchronization(self, quiet=0, run=run_all_test):
     # We will try to populate the folder person_client1
     # with the data form person_server
     if not run: return
     if not quiet:
       ZopeTestCase._print('\nTest First Synchronization ')
-      LOG('Testing... ',0,'testFirstSynchronization')
+      LOG('Testing... ',0,'test_08_FirstSynchronization')
     self.login()
     self.setupPublicationAndSubscription(quiet=1,run=1)
     nb_person = self.populatePersonServer(quiet=1,run=1)
@@ -418,13 +418,13 @@ class TestERP5SyncML(ERP5TypeTestCase):
     self.failUnless(person2_c.getFirstName()==self.first_name1)
     self.failUnless(person2_c.getLastName()==self.last_name1)
 
-  def testFirstSynchronizationWithLongLines(self, quiet=0, run=run_all_test):
+  def test_09_FirstSynchronizationWithLongLines(self, quiet=0, run=run_all_test):
     # We will try to populate the folder person_client1
     # with the data form person_server
     if not run: return
     if not quiet:
       ZopeTestCase._print('\nTest First Synchronization With Long Lines ')
-      LOG('Testing... ',0,'testFirstSynchronizationWithLongLines')
+      LOG('Testing... ',0,'test_09_FirstSynchronizationWithLongLines')
     self.login()
     self.setupPublicationAndSubscription(quiet=1,run=1)
     nb_person = self.populatePersonServer(quiet=1,run=1)
@@ -448,13 +448,13 @@ class TestERP5SyncML(ERP5TypeTestCase):
     self.failUnless(person1_c.getFirstName()==long_line)
     self.failUnless(person1_c.getLastName()==self.last_name1)
 
-  def testGetObjectFromGid(self, quiet=0, run=run_all_test):
+  def test_10_GetObjectFromGid(self, quiet=0, run=run_all_test):
     # We will try to get an object from a publication
     # just by givin the gid
     if not run: return
     if not quiet:
       ZopeTestCase._print('\nTest getObjectFromGid ')
-      LOG('Testing... ',0,'testGetObjectFromGid')
+      LOG('Testing... ',0,'test_10_GetObjectFromGid')
     self.login()
     self.setupPublicationAndSubscription(quiet=1,run=1)
     self.populatePersonServer(quiet=1,run=1)
@@ -465,14 +465,14 @@ class TestERP5SyncML(ERP5TypeTestCase):
     self.failUnless(object is not None)
     self.failUnless(object.getId()==self.id1)
 
-  def testGetSynchronizationState(self, quiet=0, run=run_all_test):
+  def test_11_GetSynchronizationState(self, quiet=0, run=run_all_test):
     # We will try to get the state of objects
     # that are just synchronized,
     if not run: return
     if not quiet:
       ZopeTestCase._print('\nTest getSynchronizationState ')
-      LOG('Testing... ',0,'testGetSynchronizationState')
-    self.testFirstSynchronization(quiet=1,run=1)
+      LOG('Testing... ',0,'test_11_GetSynchronizationState')
+    self.test_08_FirstSynchronization(quiet=1,run=1)
     portal_sync = self.getSynchronizationTool()
     person_server = self.getPersonServer()
     person1_s = person_server._getOb(self.id1)
@@ -486,7 +486,7 @@ class TestERP5SyncML(ERP5TypeTestCase):
                                         # for each subscriber
     self.checkSynchronizationStateIsSynchronized()
 
-  def checkSynchronizationStateIsSynchronized(self, quiet=0, run=run_all_test):
+  def checkSynchronizationStateIsSynchronized(self, quiet=0, run=1):
     portal_sync = self.getSynchronizationTool()
     person_server = self.getPersonServer()
     for person in person_server.objectValues():
@@ -513,7 +513,7 @@ class TestERP5SyncML(ERP5TypeTestCase):
         for m in sub.getSignatureList():
           self.assertEquals(m.getPartialXML(),None)
 
-  def checkSynchronizationStateIsConflict(self, quiet=0, run=run_all_test):
+  def checkSynchronizationStateIsConflict(self, quiet=0, run=1):
     portal_sync = self.getSynchronizationTool()
     person_server = self.getPersonServer()
     for person in person_server.objectValues():
@@ -540,12 +540,12 @@ class TestERP5SyncML(ERP5TypeTestCase):
     for state in state_list:
       self.failUnless(state[1]==state[0].CONFLICT)
 
-  def testUpdateSimpleData(self, quiet=0, run=run_all_test):
+  def test_12_UpdateSimpleData(self, quiet=0, run=run_all_test):
     if not run: return
     if not quiet:
       ZopeTestCase._print('\nTest Update Simple Data ')
-      LOG('Testing... ',0,'testUpdateSimpleData')
-    self.testFirstSynchronization(quiet=1,run=1)
+      LOG('Testing... ',0,'test_12_UpdateSimpleData')
+    self.test_08_FirstSynchronization(quiet=1,run=1)
     # First we do only modification on server
     portal_sync = self.getSynchronizationTool()
     person_server = self.getPersonServer()
@@ -585,14 +585,14 @@ class TestERP5SyncML(ERP5TypeTestCase):
     self.failUnless(person1_c.getFirstName()==self.first_name3)
     self.failUnless(person1_c.getDescription()==self.description3)
 
-  def testGetConflictList(self, quiet=0, run=run_all_test):
+  def test_13_GetConflictList(self, quiet=0, run=run_all_test):
     # We will try to generate a conflict and then to get it
     # We will also make sure it contains what we want
     if not run: return
     if not quiet:
       ZopeTestCase._print('\nTest Get Conflict List ')
-      LOG('Testing... ',0,'testGetConflictList')
-    self.testFirstSynchronization(quiet=1,run=1)
+      LOG('Testing... ',0,'test_13_GetConflictList')
+    self.test_08_FirstSynchronization(quiet=1,run=1)
     # First we do only modification on server
     portal_sync = self.getSynchronizationTool()
     person_server = self.getPersonServer()
@@ -613,14 +613,14 @@ class TestERP5SyncML(ERP5TypeTestCase):
     subscriber = conflict.getSubscriber()
     self.failUnless(subscriber.getSubscriptionUrl()==self.subscription_url1)
 
-  def testGetPublisherAndSubscriberDocument(self, quiet=0, run=run_all_test):
+  def test_14_GetPublisherAndSubscriberDocument(self, quiet=0, run=run_all_test):
     # We will try to generate a conflict and then to get it
     # We will also make sure it contains what we want
     if not run: return
     if not quiet:
       ZopeTestCase._print('\nTest Get Publisher And Subscriber Document ')
-      LOG('Testing... ',0,'testGetPublisherAndSubscriberDocument')
-    self.testGetConflictList(quiet=1,run=1)
+      LOG('Testing... ',0,'test_14_GetPublisherAndSubscriberDocument')
+    self.test_13_GetConflictList(quiet=1,run=1)
     # First we do only modification on server
     portal_sync = self.getSynchronizationTool()
     person_server = self.getPersonServer()
@@ -634,14 +634,14 @@ class TestERP5SyncML(ERP5TypeTestCase):
     subscriber_document = conflict.getSubscriberDocument()
     self.failUnless(subscriber_document.getDescription()==self.description3)
 
-  def testApplyPublisherValue(self, quiet=0, run=run_all_test):
+  def test_15_ApplyPublisherValue(self, quiet=0, run=run_all_test):
     # We will try to generate a conflict and then to get it
     # We will also make sure it contains what we want
     if not run: return
-    self.testGetConflictList(quiet=1,run=1)
+    self.test_13_GetConflictList(quiet=1,run=1)
     if not quiet:
       ZopeTestCase._print('\nTest Apply Publisher Value ')
-      LOG('Testing... ',0,'testApplyPublisherValue')
+      LOG('Testing... ',0,'test_15_ApplyPublisherValue')
     portal_sync = self.getSynchronizationTool()
     conflict_list = portal_sync.getConflictList()
     conflict = conflict_list[0]
@@ -657,16 +657,16 @@ class TestERP5SyncML(ERP5TypeTestCase):
     conflict_list = portal_sync.getConflictList()
     self.failUnless(len(conflict_list)==0)
 
-  def testApplySubscriberValue(self, quiet=0, run=run_all_test):
+  def test_16_ApplySubscriberValue(self, quiet=0, run=run_all_test):
     # We will try to generate a conflict and then to get it
     # We will also make sure it contains what we want
     if not run: return
-    self.testGetConflictList(quiet=1,run=1)
+    self.test_13_GetConflictList(quiet=1,run=1)
     portal_sync = self.getSynchronizationTool()
     conflict_list = portal_sync.getConflictList()
     if not quiet:
       ZopeTestCase._print('\nTest Apply Subscriber Value ')
-      LOG('Testing... ',0,'testApplySubscriberValue')
+      LOG('Testing... ',0,'test_16_ApplySubscriberValue')
     conflict = conflict_list[0]
     person_server = self.getPersonServer()
     person1_s = person_server._getOb(self.id1)
@@ -715,17 +715,17 @@ class TestERP5SyncML(ERP5TypeTestCase):
     len_path = len(sub_sub_person2.getPhysicalPath()) - 3 
     self.failUnless(len_path==3)
 
-  def testAddSubObject(self, quiet=0, run=run_all_test):
+  def test_17_AddSubObject(self, quiet=0, run=run_all_test):
     """
     In this test, we synchronize, then add sub object on the
     server and then see if the next synchronization will also
     create sub-objects on the client
     """
     if not run: return
-    self.testFirstSynchronization(quiet=1,run=1)
+    self.test_08_FirstSynchronization(quiet=1,run=1)
     if not quiet:
       ZopeTestCase._print('\nTest Add Sub Object ')
-      LOG('Testing... ',0,'testAddSubObject')
+      LOG('Testing... ',0,'test_17_AddSubObject')
     self.populatePersonServerWithSubObject(quiet=1,run=1)
     self.synchronize(self.sub_id1)
     self.synchronize(self.sub_id2)
@@ -747,7 +747,7 @@ class TestERP5SyncML(ERP5TypeTestCase):
     self.failUnless(sub_sub_person2.getFirstName()==self.first_name2)
     self.failUnless(sub_sub_person2.getLastName()==self.last_name2)
 
-  def testUpdateSubObject(self, quiet=0, run=run_all_test):
+  def test_18_UpdateSubObject(self, quiet=0, run=run_all_test):
     """
       In this test, we start with a tree of object already
     synchronized, then we update a subobject, and we will see
@@ -756,10 +756,10 @@ class TestERP5SyncML(ERP5TypeTestCase):
     the client and the server by the same time
     """
     if not run: return
-    self.testAddSubObject(quiet=1,run=1)
+    self.test_17_AddSubObject(quiet=1,run=1)
     if not quiet:
       ZopeTestCase._print('\nTest Update Sub Object ')
-      LOG('Testing... ',0,'testUpdateSubObject')
+      LOG('Testing... ',0,'test_18_UpdateSubObject')
     person_client1 = self.getPersonClient1()
     person1_c = person_client1._getOb(self.id1)
     sub_person1_c = person1_c._getOb(self.id1)
@@ -777,17 +777,17 @@ class TestERP5SyncML(ERP5TypeTestCase):
     self.failUnless(sub_sub_person_s.getDescription()==self.description3)
     self.failUnless(sub_sub_person_s.getFirstName()==self.first_name3)
 
-  def testDeleteObject(self, quiet=0, run=run_all_test):
+  def test_19_DeleteObject(self, quiet=0, run=run_all_test):
     """
       We will do a first synchronization, then delete an object on both
     sides, and we will see if nothing is left on the server and also
     on the two clients
     """
     if not run: return
-    self.testFirstSynchronization(quiet=1,run=1)
+    self.test_08_FirstSynchronization(quiet=1,run=1)
     if not quiet:
       ZopeTestCase._print('\nTest Delete Object ')
-      LOG('Testing... ',0,'testDeleteObject')
+      LOG('Testing... ',0,'test_19_DeleteObject')
     person_server = self.getPersonServer()
     person_server.manage_delObjects(self.id1)
     person_client1 = self.getPersonClient1()
@@ -803,7 +803,7 @@ class TestERP5SyncML(ERP5TypeTestCase):
     self.failUnless(len(subscription1.getObjectList())==0)
     self.failUnless(len(subscription2.getObjectList())==0)
 
-  def testDeleteSubObject(self, quiet=0, run=run_all_test):
+  def test_20_DeleteSubObject(self, quiet=0, run=run_all_test):
     """
       We will do a first synchronization, then delete a sub-object on both
     sides, and we will see if nothing is left on the server and also
@@ -816,10 +816,10 @@ class TestERP5SyncML(ERP5TypeTestCase):
       - id2
     """
     if not run: return
-    self.testAddSubObject(quiet=1,run=1)
+    self.test_17_AddSubObject(quiet=1,run=1)
     if not quiet:
       ZopeTestCase._print('\nTest Delete Sub Object ')
-      LOG('Testing... ',0,'testDeleteSubObject')
+      LOG('Testing... ',0,'test_20_DeleteSubObject')
     person_server = self.getPersonServer()
     sub_object_s = person_server._getOb(self.id1)._getOb(self.id1)
     sub_object_s.manage_delObjects(self.id1)
@@ -836,16 +836,16 @@ class TestERP5SyncML(ERP5TypeTestCase):
     len_c2 = len(sub_object_c2.objectValues())
     self.failUnless(len_s==len_c1==len_c2==0)
 
-  def testGetConflictListOnSubObject(self, quiet=0, run=run_all_test):
+  def test_21_GetConflictListOnSubObject(self, quiet=0, run=run_all_test):
     """
     We will change several attributes on a sub object on both the server
     and a client, then we will see if we have correctly the conflict list
     """
     if not run: return
-    self.testAddSubObject(quiet=1,run=1)
+    self.test_17_AddSubObject(quiet=1,run=1)
     if not quiet:
       ZopeTestCase._print('\nTest Get Conflict List On Sub Object ')
-      LOG('Testing... ',0,'testGetConflictListOnSubObject')
+      LOG('Testing... ',0,'test_21_GetConflictListOnSubObject')
     person_server = self.getPersonServer()
     object_s = person_server._getOb(self.id1)
     sub_object_s = object_s._getOb(self.id1)
@@ -869,16 +869,16 @@ class TestERP5SyncML(ERP5TypeTestCase):
     conflict_list = portal_sync.getConflictList(sub_object_s)
     self.failUnless(len(conflict_list)==2)
 
-  def testApplyPublisherDocumentOnSubObject(self, quiet=0, run=run_all_test):
+  def test_22_ApplyPublisherDocumentOnSubObject(self, quiet=0, run=run_all_test):
     """
     there's several conflict on a sub object, we will see if we can
     correctly have the publisher version of this document
     """
     if not run: return
-    self.testGetConflictListOnSubObject(quiet=1,run=1)
+    self.test_21_GetConflictListOnSubObject(quiet=1,run=1)
     if not quiet:
       ZopeTestCase._print('\nTest Apply Publisher Document On Sub Object ')
-      LOG('Testing... ',0,'testApplyPublisherDocumentOnSubObject')
+      LOG('Testing... ',0,'test_22_ApplyPublisherDocumentOnSubObject')
     portal_sync = self.getSynchronizationTool()
     conflict_list = portal_sync.getConflictList()
     conflict = conflict_list[0]
@@ -899,16 +899,16 @@ class TestERP5SyncML(ERP5TypeTestCase):
     self.failUnless(sub_object_c2.getDescription()==self.description2)
     self.failUnless(sub_object_c2.getLanguage()==self.lang2)
 
-  def testApplySubscriberDocumentOnSubObject(self, quiet=0, run=run_all_test):
+  def test_23_ApplySubscriberDocumentOnSubObject(self, quiet=0, run=run_all_test):
     """
     there's several conflict on a sub object, we will see if we can
     correctly have the subscriber version of this document
     """
     if not run: return
-    self.testGetConflictListOnSubObject(quiet=1,run=1)
+    self.test_21_GetConflictListOnSubObject(quiet=1,run=1)
     if not quiet:
       ZopeTestCase._print('\nTest Apply Subscriber Document On Sub Object ')
-      LOG('Testing... ',0,'testApplySubscriberDocumentOnSubObject')
+      LOG('Testing... ',0,'test_23_ApplySubscriberDocumentOnSubObject')
     portal_sync = self.getSynchronizationTool()
     conflict_list = portal_sync.getConflictList()
     conflict = conflict_list[0]
@@ -929,7 +929,7 @@ class TestERP5SyncML(ERP5TypeTestCase):
     self.failUnless(sub_object_c2.getDescription()==self.description3)
     self.failUnless(sub_object_c2.getLanguage()==self.lang3)
 
-  def testSynchronizeWithStrangeGid(self, quiet=0, run=run_all_test):
+  def test_24_SynchronizeWithStrangeGid(self, quiet=0, run=run_all_test):
     """
     By default, the synchronization process use the id in order to
     recognize objects (because by default, getGid==getId. Here, we will see 
@@ -938,7 +938,7 @@ class TestERP5SyncML(ERP5TypeTestCase):
     if not run: return
     if not quiet:
       ZopeTestCase._print('\nTest Synchronize With Strange Gid ')
-      LOG('Testing... ',0,'testSynchronizeWithStrangeGid')
+      LOG('Testing... ',0,'test_24_SynchronizeWithStrangeGid')
     self.login()
     self.setupPublicationAndSubscriptionAndGid(quiet=1,run=1)
     nb_person = self.populatePersonServer(quiet=1,run=1)
@@ -976,16 +976,16 @@ class TestERP5SyncML(ERP5TypeTestCase):
     self.failUnless(person_s.getDescription()==self.description3)
     self.failUnless(person_c1.getDescription()==self.description3)
 
-  def testMultiNodeConflict(self, quiet=0, run=run_all_test):
+  def test_25_MultiNodeConflict(self, quiet=0, run=run_all_test):
     """
     We will create conflicts with 3 differents nodes, and we will
     solve it by taking one full version of documents.
     """
     if not run: return
-    self.testFirstSynchronization(quiet=1,run=1)
+    self.test_08_FirstSynchronization(quiet=1,run=1)
     if not quiet:
       ZopeTestCase._print('\nTest Multi Node Conflict ')
-      LOG('Testing... ',0,'testMultiNodeConflict')
+      LOG('Testing... ',0,'test_25_MultiNodeConflict')
     portal_sync = self.getSynchronizationTool()
     person_server = self.getPersonServer()
     person1_s = person_server._getOb(self.id1)
@@ -1041,17 +1041,17 @@ class TestERP5SyncML(ERP5TypeTestCase):
     self.failUnless(person1_c2.getFormat()==self.format4)
         
 
-  def testSynchronizeWorkflowHistory(self, quiet=0, run=run_all_test):
+  def test_26_SynchronizeWorkflowHistory(self, quiet=0, run=run_all_test):
     """
     We will do a synchronization, then we will edit two times
     the object on the server, then two times the object on the
     client, and see if the global history as 4 more actions.
     """
     if not run: return
-    self.testFirstSynchronization(quiet=1,run=1)
+    self.test_08_FirstSynchronization(quiet=1,run=1)
     if not quiet:
       ZopeTestCase._print('\nTest Synchronize WorkflowHistory ')
-      LOG('Testing... ',0,'testSynchronizeWorkflowHistory')
+      LOG('Testing... ',0,'test_26_SynchronizeWorkflowHistory')
     person_server = self.getPersonServer()
     person1_s = person_server._getOb(self.id1)
     person_client1 = self.getPersonClient1()
@@ -1068,16 +1068,16 @@ class TestERP5SyncML(ERP5TypeTestCase):
     self.failUnless(len(person1_s.workflow_history[self.workflow_id])==len_wf+4)
     self.failUnless(len(person1_c.workflow_history[self.workflow_id])==len_wf+4)
 
-  def testUpdateLocalRole(self, quiet=0, run=run_all_test):
+  def test_27_UpdateLocalRole(self, quiet=0, run=run_all_test):
     """
     We will do a first synchronization, then modify, add and delete
     an user role and see if it is correctly synchronized
     """
     if not run: return
-    self.testFirstSynchronization(quiet=1,run=1)
+    self.test_08_FirstSynchronization(quiet=1,run=1)
     if not quiet:
       ZopeTestCase._print('\nTest Update Local Role ')
-      LOG('Testing... ',0,'testUpdateLocalRole')
+      LOG('Testing... ',0,'test_27_UpdateLocalRole')
     # First, Create a new user
     uf = self.getPortal().acl_users
     uf._doAddUser('jp', '', ['Manager'], [])
@@ -1101,17 +1101,17 @@ class TestERP5SyncML(ERP5TypeTestCase):
     self.assertEqual(role_1_s,role_1_c)
     self.assertEqual(role_2_s,role_2_c)
 
-  def testPartialData(self, quiet=0, run=run_all_test):
+  def test_28_PartialData(self, quiet=0, run=run_all_test):
     """
     We will do a first synchronization, then we will do a change, then
     we will modify the SyncCode max_line value so it
     it will generate many messages
     """
     if not run: return
-    self.testFirstSynchronization(quiet=1,run=1)
+    self.test_08_FirstSynchronization(quiet=1,run=1)
     if not quiet:
       ZopeTestCase._print('\nTest Partial Data ')
-      LOG('Testing... ',0,'testPartialData')
+      LOG('Testing... ',0,'test_28_PartialData')
     previous_max_lines = SyncCode.MAX_LINES
     SyncCode.MAX_LINES = 10
     self.populatePersonServerWithSubObject(quiet=1,run=1)
@@ -1136,7 +1136,7 @@ class TestERP5SyncML(ERP5TypeTestCase):
     self.assertEquals(sub_sub_person2.getLastName(),self.last_name2)
     SyncCode.MAX_LINES = previous_max_lines
 
-  def testBrokenMessage(self, quiet=0, run=run_all_test):
+  def test_29_BrokenMessage(self, quiet=0, run=run_all_test):
     """
     With http synchronization, when a message is not well
     received, then we send message again, we want to
@@ -1148,7 +1148,7 @@ class TestERP5SyncML(ERP5TypeTestCase):
     if not run: return
     if not quiet:
       ZopeTestCase._print('\nTest Broken Message ')
-      LOG('Testing... ',0,'testBrokenMessage')
+      LOG('Testing... ',0,'test_29_BrokenMessage')
     previous_max_lines = SyncCode.MAX_LINES
     SyncCode.MAX_LINES = 10
     self.setupPublicationAndSubscription(quiet=1,run=1)
@@ -1172,14 +1172,14 @@ class TestERP5SyncML(ERP5TypeTestCase):
     self.failUnless(person1_c.getLastName()==self.last_name1)
     SyncCode.MAX_LINES = previous_max_lines
 
-  def testGetSynchronizationType(self, quiet=0, run=run_all_test):
+  def test_30_GetSynchronizationType(self, quiet=0, run=run_all_test):
     # We will try to update some simple data, first
     # we change on the server side, the on the client side
     if not run: return
     if not quiet:
       ZopeTestCase._print('\nTest Get Synchronization Type ')
-      LOG('Testing... ',0,'testGetSynchronizationType')
-    self.testFirstSynchronization(quiet=1,run=1)
+      LOG('Testing... ',0,'test_30_GetSynchronizationType')
+    self.test_08_FirstSynchronization(quiet=1,run=1)
     # First we do only modification on server
     # Check for each subsription that the synchronization type
     # is TWO WAY
@@ -1209,16 +1209,16 @@ class TestERP5SyncML(ERP5TypeTestCase):
     for sub in portal_sync.getSubscriptionList():
       self.assertEquals(sub.getSynchronizationType(),SyncCode.TWO_WAY)
 
-  def testUpdateLocalPermission(self, quiet=0, run=run_all_test):
+  def test_31_UpdateLocalPermission(self, quiet=0, run=run_all_test):
     """
     We will do a first synchronization, then modify, add and delete
     an user role and see if it is correctly synchronized
     """
     if not run: return
-    self.testFirstSynchronization(quiet=1,run=1)
+    self.test_08_FirstSynchronization(quiet=1,run=1)
     if not quiet:
       ZopeTestCase._print('\nTest Update Local Permission ')
-      LOG('Testing... ',0,'testUpdateLocalPermission')
+      LOG('Testing... ',0,'test_31_UpdateLocalPermission')
     # then create roles
     person_server = self.getPersonServer()
     person1_s = person_server._getOb(self.id1)
@@ -1249,9 +1249,64 @@ class TestERP5SyncML(ERP5TypeTestCase):
     self.assertEqual(role_1_s,role_1_c)
     self.assertEqual(role_2_s,role_2_c)
 
-  # We may add a test in order to check if the slow_sync mode works fine, ie
-  # if we do have both object on the client and server side, we must make sure
-  # that the server first sends is own data
+  def test_32_AddOneWaySubscription(self, quiet=0, run=1):
+    if not run: return
+    if not quiet:
+      ZopeTestCase._print('\nTest Add One Way Subscription ')
+      LOG('Testing... ',0,'test_32_AddOneWaySubscription')
+    portal_id = self.getPortalId()
+    portal_sync = self.getSynchronizationTool()
+    portal_sync.manage_addSubscription(self.sub_id1,self.publication_url,
+                          self.subscription_url1,'/%s/person_client1' % portal_id,'objectValues',
+                          '','ERP5Conduit','')
+    sub = portal_sync.getSubscription(self.sub_id1)
+    #sub.setOneWaySyncFromServer(1)
+    self.failUnless(sub is not None)
+
+  def test_33_OneWaySync(self, quiet=0, run=1):
+    """
+    We will test if we can synchronize only from to server to the client.
+    We want to make sure in this case that all modifications on the client
+    will not be taken into account.
+    """
+    if not run: return
+    if not quiet:
+      ZopeTestCase._print('\nTest One Way Sync ')
+      LOG('Testing... ',0,'test_33_OneWaySync')
+    self.test_02_AddPublication(quiet=1,run=1)
+    self.test_32_AddOneWaySubscription(quiet=1,run=1)
+
+    nb_person = self.populatePersonServer(quiet=1,run=1)
+    portal_sync = self.getSynchronizationTool()
+    for sub in portal_sync.getSubscriptionList():
+      self.assertEquals(sub.getSynchronizationType(),SyncCode.SLOW_SYNC)
+    # First do the sync from the server to the client
+    nb_message1 = self.synchronize(self.sub_id1)
+    for sub in portal_sync.getSubscriptionList():
+      self.assertEquals(sub.getSynchronizationType(),SyncCode.TWO_WAY)
+    self.assertEquals(nb_message1,self.nb_message_first_synchronization)
+    subscription1 = portal_sync.getSubscription(self.sub_id1)
+    self.assertEquals(len(subscription1.getObjectList()),nb_person)
+    person_server = self.getPersonServer() # We also check we don't
+                                           # modify initial ob
+    person1_s = person_server._getOb(self.id1)
+    self.failUnless(person1_s.getId()==self.id1)
+    self.failUnless(person1_s.getFirstName()==self.first_name1)
+    self.failUnless(person1_s.getLastName()==self.last_name1)
+    person_client1 = self.getPersonClient1()
+    person1_c = person_client1._getOb(self.id1)
+    self.failUnless(person1_c.getId()==self.id1)
+    self.failUnless(person1_c.getFirstName()==self.first_name1)
+    self.failUnless(person1_c.getLastName()==self.last_name1)
+    # Then we change things on both sides and we look if there
+    # is synchronization from only one way
+    person1_c.setFirstName(self.first_name2)
+    person1_s.setLastName(self.last_name2)
+    nb_message1 = self.synchronize(self.sub_id1)
+    self.assertEquals(person1_c.getLastName(),self.last_name2)
+    self.assertEquals(person1_s.getFirstName(),self.first_name1)
+
+
 
 if __name__ == '__main__':
     framework()
-- 
2.30.9