# -*- coding: utf-8 -*-
##############################################################################
#
# Copyright (c) 2010 Nexedi SA and Contributors. All Rights Reserved.
#               Hervé Poulain <herve@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 Products.ERP5TioSafe.tests.testPrestashopMixin import testPrestashopMixin

class TestProductPrestashopSynchronization(testPrestashopMixin):
  """ This class allows to check different cases of Product's sync. """

  def afterSetUp(self):
    """ This method is called after the SetUp method. """
    # Shortcut for modules and tools
    self.product_module = self.portal.product_module
    self.portal_categories = self.portal.portal_categories
    self.portal_sync = self.portal.portal_synchronizations
    self.connection = self.portal.erp5_sql_connection
    self.prestashop = self.portal.portal_integrations.prestashop
    self.root_xml = '<catalog>\n%s\n</catalog>'
    self.sale_supply = self.portal.sale_supply_module.newContent(title=self.prestashop.getTitle())
    self.sale_supply.validate()
    self.tic()

  def beforeTearDown(self):
    testPrestashopMixin.beforeTearDown(self)
    self.sale_supply.invalidate()
    self.tic()

  def createProduct(self, **kw):
    """
    Create product & add it to the sale supply
    """
    product = self.product_module.newContent(**kw)
    product.validate()
    self.sale_supply.newContent(resource=product.getRelativeUrl())
    return product

  def test_PrestashopSimplestXMLSync(self):
    """ This test checks the product sync with the simplest XML. """
    # Initialize the instance and prestashop
    self.initPrestashopTest()
    product = self.createProduct(
        portal_type='Product',
        title='Tee-Shirt',
        reference='my_ref',
        use='sale',
    )
    
    self.tic()

    # Run the sync of products and check product's data after sync
    self.assertEqual(len(self.prestashop.product_module()), 0)
    self.loadSync([self.prestashop.product_module, ])
    self.assertEqual(len(self.prestashop.product_module()), 1)
    # Check the XML schema and the fixed point
    self.checkTioSafeXML(
        plugin_xml=self.root_xml % self.prestashop.product_module()[0].asXML(),
        tiosafe_xml=self.root_xml % product.Resource_asTioSafeXML(),
        xsd_path='../XSD/resources.xsd',
    )

  def test_PrestashopIndividualVariationSync(self):
    """ This test check the product sync with individual variations. """
    # Initialize the instance and prestashop
    self.initPrestashopTest()
    self.loadSQLDump(
        self.connection,
        '%s/dump_product_sync_12.sql' % self.ps_dump_path,
    )
    # Define the specific mapping, initMapping declare the categories in ERP5
    self.initMapping()
    mapping_dict_list = [
        { 'title': 'Taille du Ballon',
          'path': 'TailleduBallon',
          'source_reference': 'Taille du Ballon',
          'destination_reference':'ball_size', },
        { 'title': 'Couleur',
          'path': 'Couleur',
          'source_reference': 'Couleur',
          'destination_reference': 'colour', },
    ]
    for mapping in mapping_dict_list:
      self.createMapping(integration_site=self.prestashop, **mapping)
    # create and init the product
    product = self.createProduct(
        portal_type='Product',
        title='Ballon de Foot',
        reference='0123456789',
        ean13_code='1234567890128',
        use='sale',
        sale_supply_line_base_price=2.123456,
        purchase_supply_line_base_price=1.123456,
    )
    
    individual_variation_dict_list = [
        {'variation_base_category': 'ball_size', 'title': 's4', },
        {'variation_base_category': 'ball_size', 'title': 's5', },
        {'variation_base_category': 'colour', 'title': 'Blanc', },
        {'variation_base_category': 'colour', 'title': 'Noir', },
    ]
    for individual_variation in individual_variation_dict_list:
      product.newContent(
          portal_type='Product Individual Variation',
          **individual_variation
      )
    self.tic()

    # Run the sync of products and check product's data after sync
    self.assertEqual(len(self.prestashop.product_module()), 0)
    self.loadSync([self.prestashop.product_module, ])
    self.assertEqual(len(self.prestashop.product_module()), 1)
    # Check the XML schema and the fixed point
    self.checkTioSafeXML(
        plugin_xml=self.root_xml % self.prestashop.product_module()[0].asXML(),
        tiosafe_xml=self.root_xml % product.Resource_asTioSafeXML(),
        xsd_path='../XSD/resources.xsd',
    )

  def test_PrestashopSharedVariationSync(self):
    """ This test check the product sync with shared variations. """
    # Initialize the instance and prestashop
    self.initPrestashopTest()
    self.loadSQLDump(
        self.connection,
        '%s/dump_product_sync_12.sql' % self.ps_dump_path,
    )
    self.initMapping(self.prestashop)
    # create and init the product
    product = self.createProduct(
        portal_type='Product',
        title='Ballon de Foot',
        reference='0123456789',
        ean13_code='1234567890128',
        use='sale',
        sale_supply_line_base_price=2.123456,
        purchase_supply_line_base_price=1.123456,
    )
    
    product.setVariationBaseCategoryList(['ball_size', 'colour'])
    product.setVariationCategoryList(
        ['ball_size/x4', 'ball_size/x5', 'colour/black', 'colour/white'],
    )
    self.tic()

    # Run the sync of products and check product's data after sync
    self.assertEqual(len(self.prestashop.product_module()), 0)
    self.loadSync([self.prestashop.product_module, ])
    self.assertEqual(len(self.prestashop.product_module()), 1)
    # Check the XML schema and the fixed point
    self.checkTioSafeXML(
        plugin_xml=self.root_xml % self.prestashop.product_module()[0].asXML(),
        tiosafe_xml=self.root_xml % product.Resource_asTioSafeXML(),
        xsd_path='../XSD/resources.xsd',
    )

  def test_PrestashopDifferentKindVariationsSync(self):
    """ This test check the product sync with the two kind of variations. """
    # Initialize the instance and prestashop
    self.initPrestashopTest()
    self.loadSQLDump(
        self.connection,
        '%s/dump_product_sync_12.sql' % self.ps_dump_path,
    )
    # Define the specific mapping, initMapping declare the categories in ERP5
    self.initMapping()
    mapping_dict_list = [
        { 'title': 'Taille du Ballon',
          'path': 'TailleduBallon',
          'source_reference': 'Taille du Ballon',
          'destination_reference':'ball_size', },
        { 'title': 'Couleur',
          'path': 'Couleur',
          'source_reference': 'Couleur',
          'destination_reference': 'colour', },
        { 'title': 'Blanc',
          'path': 'Couleur/Blanc',
          'source_reference': 'Blanc',
          'destination_reference': 'white', },
        { 'title': 'Noir',
          'path': 'Couleur/Noir',
          'source_reference': 'Noir',
          'destination_reference': 'black', },
    ]
    for mapping in mapping_dict_list:
      self.createMapping(integration_site=self.prestashop, **mapping)
    # create and init the product
    product = self.createProduct(
        portal_type='Product',
        title='Ballon de Foot',
        reference='0123456789',
        ean13_code='1234567890128',
        use='sale',
        sale_supply_line_base_price=2.123456,
        purchase_supply_line_base_price=1.123456,
    )
    
    product.setVariationBaseCategoryList(['colour'])
    product.setVariationCategoryList(['colour/black', 'colour/white'])
    individual_variation_dict_list = [
        {'variation_base_category': 'ball_size', 'title': 's4', },
        {'variation_base_category': 'ball_size', 'title': 's5', },
    ]
    for individual_variation in individual_variation_dict_list:
      product.newContent(
          portal_type='Product Individual Variation',
          **individual_variation
      )
    self.tic()

    # Run the sync of products and check product's data after sync
    self.assertEqual(len(self.prestashop.product_module()), 0)
    self.loadSync([self.prestashop.product_module, ])
    self.assertEqual(len(self.prestashop.product_module()), 1)
    # Check the XML schema and the fixed point
    self.checkTioSafeXML(
        plugin_xml=self.root_xml % self.prestashop.product_module()[0].asXML(),
        tiosafe_xml=self.root_xml % product.Resource_asTioSafeXML(),
        xsd_path='../XSD/resources.xsd',
    )

  def test_PrestashopMultipleSync(self):
    """ This test check the multiple product sync. """
    # Initialize the instance and prestashop
    self.initPrestashopTest()
    self.loadSQLDump(
        self.connection,
        '%s/dump_product_sync_12.sql' % self.ps_dump_path,
    )
    # Define the specific mapping, initMapping declare the categories in ERP5
    self.initMapping()
    mapping_dict_list = [
        { 'title': 'Taille du Ballon',
          'path': 'TailleduBallon',
          'source_reference': 'Taille du Ballon',
          'destination_reference':'ball_size', },
        { 'title': 'Couleur',
          'path': 'Couleur',
          'source_reference': 'Couleur',
          'destination_reference': 'colour', },
        { 'title': 'Blanc',
          'path': 'Couleur/Blanc',
          'source_reference': 'Blanc',
          'destination_reference': 'white', },
        { 'title': 'Noir',
          'path': 'Couleur/Noir',
          'source_reference': 'Noir',
          'destination_reference': 'black', },
    ]
    for mapping in mapping_dict_list:
      self.createMapping(integration_site=self.prestashop, **mapping)
    # create and init the product one
    product_1 = self.createProduct(
        portal_type='Product',
        title='Stylo',
        reference='01111',
        use='sale',
        sale_supply_line_base_price=2.1,
        purchase_supply_line_base_price=1.1,
    )
    # create and init the product two
    product_2 = self.createProduct(
        portal_type='Product',
        title='Ballon',
        reference='02222',
        ean13_code='2222222222222',
        use='sale',
        sale_supply_line_base_price=20.2,
        purchase_supply_line_base_price=10.2,
    )
    individual_variation_dict_list = [
        {'variation_base_category': 'ball_size', 'title': 's4', },
        {'variation_base_category': 'ball_size', 'title': 's5', },
    ]
    for individual_variation in individual_variation_dict_list:
      product_2.newContent(
          portal_type='Product Individual Variation',
          **individual_variation
      )
    # create and init the product three
    product_3 = self.createProduct(
        portal_type='Product',
        title='Ballon de Foot',
        reference='03333',
        ean13_code='3333333333338',
        use='sale',
        sale_supply_line_base_price=200.3,
        purchase_supply_line_base_price=100.3,
    )
    product_3.setVariationBaseCategoryList(['colour', ])
    product_3.setVariationCategoryList(['colour/black', 'colour/white'])
    # create and init the product four
    product_4 = self.createProduct(
        portal_type='Product',
        title='Ballon de Basket',
        reference='04444',
        ean13_code='4444444444444',
        use='sale',
        sale_supply_line_base_price=2000.4,
        purchase_supply_line_base_price=1000.4,
    )
    product_4.setVariationBaseCategoryList(['colour'])
    product_4.setVariationCategoryList(['colour/black', 'colour/white'])
    individual_variation_dict_list = [
        {'variation_base_category': 'ball_size', 'title': 's4', },
        {'variation_base_category': 'ball_size', 'title': 's5', },
    ]
    for individual_variation in individual_variation_dict_list:
      product_4.newContent(
          portal_type='Product Individual Variation',
          **individual_variation
      )
    self.tic()

    # Run the sync of products and check product's data after sync
    self.assertEqual(len(self.prestashop.product_module()), 0)
    self.loadSync([self.prestashop.product_module, ])
    self.assertEqual(len(self.prestashop.product_module()), 4)
    # Check the XML schema and the fixed point
    self.checkTioSafeXML(
        plugin_xml=self.root_xml % self.prestashop.product_module[10].asXML(),
        tiosafe_xml= self.root_xml % product_1.Resource_asTioSafeXML(),
        xsd_path='../XSD/resources.xsd',
    )
    self.checkTioSafeXML(
        plugin_xml=self.root_xml % self.prestashop.product_module[11].asXML(),
        tiosafe_xml= self.root_xml % product_2.Resource_asTioSafeXML(),
        xsd_path='../XSD/resources.xsd',
    )
    self.checkTioSafeXML(
        plugin_xml=self.root_xml % self.prestashop.product_module[12].asXML(),
        tiosafe_xml= self.root_xml % product_3.Resource_asTioSafeXML(),
        xsd_path='../XSD/resources.xsd',
    )
    self.checkTioSafeXML(
        plugin_xml=self.root_xml % self.prestashop.product_module[13].asXML(),
        tiosafe_xml= self.root_xml % product_4.Resource_asTioSafeXML(),
        xsd_path='../XSD/resources.xsd',
    )

  def test_PrestashopDeleteProduct(self):
    """ Check that delete during a product's sync invalidate the product. """
    # Initialize the instance and prestashop
    product_module = self.portal.product_module
    self.initPrestashopTest()
    product = self.createProduct(
        portal_type='Product',
        title='Tee-Shirt',
        reference='0123456789',
        use='sale',
    )
    
    self.tic()

    # Run the sync of products and check product's data after sync
    self.assertEqual(len(self.prestashop.product_module()), 0)
    self.loadSync([self.prestashop.product_module, ])
    self.assertEqual(len(self.prestashop.product_module()), 1)
    # Remove the product in ERP5 and check that after sync in prestashop
    self.product_module.manage_delObjects([product.getId(), ])
    self.loadSync([self.prestashop.product_module, ])
    self.assertEqual(len(self.prestashop.product_module()), 0)

  def test_PrestashopUpdateSimpleElement(self):
    """ This test checks the simple update after sync of products. """
    # Initialize the instance and prestashop
    self.initPrestashopTest()
    self.loadSQLDump(
        self.connection,
        '%s/dump_product_sync_12.sql' % self.ps_dump_path,
    )
    self.initMapping(self.prestashop)
    # create and init the product
    product = self.createProduct(
        portal_type='Product',
        title='Ballon de Foot',
        reference='0123456789',
        ean13_code='1234567890128',
        use='sale',
        sale_supply_line_base_price=2000.4,
        purchase_supply_line_base_price=1000.4,
    )
    
    product.setVariationBaseCategoryList(['ball_size', 'colour'])
    product.setVariationCategoryList(
        ['ball_size/x4', 'ball_size/x5', 'colour/black', 'colour/white'],
    )
    self.tic()

    # Run the sync of products and check product's data after sync
    self.assertEqual(len(self.prestashop.product_module()), 0)
    self.loadSync([self.prestashop.product_module, ])
    self.assertEqual(len(self.prestashop.product_module()), 1)
    # Check the XML schema and the fixed point
    self.checkTioSafeXML(
        plugin_xml=self.root_xml % self.prestashop.product_module()[0].asXML(),
        tiosafe_xml=self.root_xml % product.Resource_asTioSafeXML(),
        xsd_path='../XSD/resources.xsd',
    )
    # Update the data, run the sync and check the data after the update
    product.setSaleSupplyLineBasePrice(20.0)
    product.setPurchaseSupplyLineBasePrice(20.0)
    product.setEan13Code('0987654321098')
    self.assertEqual(len(self.prestashop.product_module()), 1)
    self.loadSync([self.prestashop.product_module, ])
    self.assertEqual(len(self.prestashop.product_module()), 1)
    # Check the XML schema and the fixed point
    self.checkTioSafeXML(
        plugin_xml=self.root_xml % self.prestashop.product_module()[0].asXML(),
        tiosafe_xml=self.root_xml % product.Resource_asTioSafeXML(),
        xsd_path='../XSD/resources.xsd',
    )

  def updateSystemPreference(self, portal, base_category, individual=False):
    pref_list = portal.portal_preferences.searchFolder(portal_type="System Preference",
                                                       validation_state="enabled")
    if len(pref_list) > 1:
      raise ValueError, "Too many system preferences, does not know which to choose"
    elif len(pref_list) == 0:
      pref = portal.portal_preferences.newContent(portal_type="System Preference",
                                           title="default system preference for TioSafe",
                                           priority=1)
      pref.enable()
    else:
      pref = pref_list[0].getObject()
    self.system_pref = pref

    if individual:
      cat_list = self.system_pref.getPreferredProductIndividualVariationBaseCategoryList()
      if base_category not in cat_list:
        cat_list.append(base_category)
        self.system_pref.edit(preferred_product_individual_variation_base_category_list = cat_list)
    else:
      cat_list = self.system_pref.getPreferredProductVariationBaseCategoryList()
      if base_category not in cat_list:
        cat_list.append(base_category)
        self.system_pref.edit(preferred_product_variation_base_category_list = cat_list)

  def checkConflicts(self, module, nb_pub_conflicts=0, nb_sub_conflicts=0, in_conflict=True):
    module = self.prestashop[module]
    pub = module.getSourceSectionValue()
    sub = module.getDestinationSectionValue()
    self.assertEqual(len(pub.getConflictList()), nb_pub_conflicts)
    self.assertEqual(len(sub.getConflictList()), nb_sub_conflicts)
    for conflict in pub.getConflictList() + sub.getConflictList():
      state = conflict.getParentValue().getValidationState()
      if in_conflict:
        self.assertEqual(state, 'conflict')
      else:
        self.assertEqual(state, 'synchronized')


  def test_PrestashopComplexeUpdateElement(self):
    """
      This test checks the complexe update after sync of products.
      It updates some element, adds others and removes the last.
    """
    # Initialize the instance and prestashop
    self.initPrestashopTest()
    self.loadSQLDump(
        self.connection,
        '%s/dump_product_sync_12.sql' % self.ps_dump_path,
    )
    # Define the specific mapping, initMapping declare the categories in ERP5
    self.initMapping()
    mapping_dict_list = [
        { 'title': 'Taille du Ballon',
          'path': 'TailleduBallon',
          'source_reference': 'Taille du Ballon',
          'destination_reference':'ball_size', },
        { 'title': 'Couleur',
          'path': 'Couleur',
          'source_reference': 'Couleur',
          'destination_reference': 'colour', },
        { 'title': 'Blanc',
          'path': 'Couleur/Blanc',
          'source_reference': 'Blanc',
          'destination_reference': 'white', },
        { 'title': 'Noir',
          'path': 'Couleur/Noir',
          'source_reference': 'Noir',
          'destination_reference': 'black', },
        { 'title': 'Rouge',
          'path': 'Couleur/Rouge',
          'source_reference': 'Rouge',
          'destination_reference': 'red', },
    ]
    for mapping in mapping_dict_list:
      self.createMapping(integration_site=self.prestashop, **mapping)
    # create and init the product
    product = self.createProduct(
        portal_type='Product',
        title='Ballon de Plage',
        reference='a5962z',
        use='sale',
        sale_supply_line_base_price=200.25,
        purchase_supply_line_base_price=100.25,
    )
    self.updateSystemPreference(self.getPortalObject(), 'colour')
    self.updateSystemPreference(self.getPortalObject(), 'ball_size', True)
    product.setVariationBaseCategoryList(['colour'])
    product.setVariationCategoryList(['colour/black', 'colour/white'])
    individual_variation_dict_list = [
      {'variation_base_category': 'ball_size', 'title': 's4', },
      {'variation_base_category': 'ball_size', 'title': 's5', },
      ]
    product.setIndividualVariationBaseCategoryList(['ball_size'])
    for individual_variation in individual_variation_dict_list:
      product.newContent(
          portal_type='Product Individual Variation',
          **individual_variation
      )
    self.tic()

    # Run the sync of products and check product's data after sync
    self.assertEqual(len(self.prestashop.product_module()), 0)
    self.loadSync([self.prestashop.product_module, ])
    self.assertEqual(len(self.prestashop.product_module()), 1)
    self.checkConflicts('product_module')
    # Check the XML schema and the fixed point
    self.checkTioSafeXML(
        plugin_xml=self.root_xml % self.prestashop.product_module()[0].asXML(),
        tiosafe_xml=self.root_xml % product.Resource_asTioSafeXML(),
        xsd_path='../XSD/resources.xsd',
    )
    # The first update remove, add and update some elements but not realise an
    # hard work on variations
    product.setEan13Code('1357913579130')
    product.setVariationCategoryList(['colour/white', 'colour/red'])
    individual_variation = product.portal_catalog(
        portal_type='Product Individual Variation',
        parent_uid=product.getUid(),
        title='s5',
    )[0].getObject()
    individual_variation.setTitle('s6')
    self.tic()
    self.assertEqual(len(self.prestashop.product_module()), 1)
    self.loadSync([self.prestashop.product_module, ])
    self.assertEqual(len(self.prestashop.product_module()), 1)
    self.checkConflicts('product_module')
    self.checkTioSafeXML(
        plugin_xml=self.root_xml % self.prestashop.product_module()[0].asXML(),
        tiosafe_xml=self.root_xml % product.Resource_asTioSafeXML(),
        xsd_path='../XSD/resources.xsd',
    )
    # The second update remove variations (individuals and shareds)
    product.setVariationCategoryList(['colour/white', ])
    product.manage_delObjects([individual_variation.getId(), ])
    self.tic()
    self.assertEqual(len(self.prestashop.product_module()), 1)
    self.loadSync([self.prestashop.product_module, ])
    self.assertEqual(len(self.prestashop.product_module()), 1)
    self.checkConflicts('product_module')
    self.checkTioSafeXML(
        plugin_xml=self.root_xml % self.prestashop.product_module()[0].asXML(),
        tiosafe_xml=self.root_xml % product.Resource_asTioSafeXML(),
        xsd_path='../XSD/resources.xsd',
    )
    # The third update allows to add variations (individuals and shareds)
    product.setVariationCategoryList(
        ['colour/white', 'colour/red', 'colour/white'],
    )
    individual_variation_dict_list = [
        {'variation_base_category': 'ball', 'title': 's5', },
        {'variation_base_category': 'ball', 'title': 's6', },
    ]
    for individual_variation in individual_variation_dict_list:
      product.newContent(
          portal_type='Product Individual Variation',
          **individual_variation
      )
    self.assertEqual(len(self.prestashop.product_module()), 1)
    self.loadSync([self.prestashop.product_module, ])
    self.assertEqual(len(self.prestashop.product_module()), 1)
    self.checkConflicts('product_module')
    self.checkTioSafeXML(
        plugin_xml=self.root_xml % self.prestashop.product_module()[0].asXML(),
        tiosafe_xml=self.root_xml % product.Resource_asTioSafeXML(),
        xsd_path='../XSD/resources.xsd',
    )

import unittest
def test_suite():
  suite = unittest.TestSuite()
  suite.addTest(unittest.makeSuite(TestProductPrestashopSynchronization))
  return suite
