##############################################################################
#
# Copyright (c) 2005 Nexedi SARL and Contributors. All Rights Reserved.
#          Romain Courteaud <romain@nexedi.com>
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsability of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial
# garantees and support are strongly adviced to contract a Free Software
# Service Company
#
# This program is Free Software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
##############################################################################

import unittest
from unittest import expectedFailure
from Testing import ZopeTestCase
from Products.ERP5Type.tests.ERP5TypeTestCase import ERP5TypeTestCase
from AccessControl.SecurityManagement import newSecurityManager
from zLOG import LOG
from Products.ERP5Type.tests.Sequence import SequenceList
from DateTime import DateTime

class TestResource(ERP5TypeTestCase):
  """
    Test ERP5 document Resource
  """
  run_all_test = 1
  quiet = 0

  # Global variables
  resource_portal_type = 'Apparel Model'
  product_portal_type = 'Product'
  node_portal_type = 'Organisation'
  sale_supply_portal_type = 'Sale Supply'
  sale_order_line_portal_type = 'Sale Order Line'
  sale_supply_line_portal_type = 'Sale Supply Line'
  purchase_supply_line_portal_type = 'Purchase Supply Line'
  internal_supply_line_portal_type = 'Internal Supply Line'
  sale_supply_cell_portal_type = 'Sale Supply Cell'
  variation_base_category_list = ['size', 'industrial_phase', 'colour',
                                  'morphology']
  size_list = ['size/Child','size/Man']
  variation_property_list = []

  def getBusinessTemplateList(self):
    """
      Install needed business template
    """
    # Trade is needeed for pricing
    return ('erp5_base', 'erp5_pdm', 'erp5_simulation', 'erp5_trade',
            'erp5_configurator_standard_trade_template',
            'erp5_configurator_standard_solver',
            'erp5_apparel', 'erp5_simulation_test')

  def getTitle(self):
    return "Resource"

  def login(self):
    uf = self.getPortal().acl_users
    uf._doAddUser('rc', '', ['Manager'], [])
    user = uf.getUserById('rc').__of__(uf)
    newSecurityManager(None, user)

  def setUpPreferences(self):
    #create apparel variation preferences
    portal_preferences = self.getPreferenceTool()
    preference = getattr(portal_preferences, 'test_site_preference', None)
    if preference is None:
      preference = portal_preferences.newContent(portal_type='System Preference',
                                title='Default Site Preference',
                                id='test_site_preference')
      if preference.getPreferenceState() == 'disabled':
        preference.enable()

    preference.setPreferredApparelModelVariationBaseCategoryList(('size', 'industrial_phase',))
    preference.setPreferredApparelClothVariationBaseCategoryList(('size',))
    preference.setPreferredApparelComponentVariationBaseCategoryList(('variation',))
    preference.setPreferredApparelModelIndividualVariationBaseCategoryList(('colour', 'morphology'))
    if preference.getPreferenceState() == 'disabled':
      preference.enable()
    self.tic()

  def afterSetUp(self):
    self.login()
    self.portal = self.getPortal()
    self.category_tool = self.getCategoryTool()
    self.createCategories()
    self.setUpPreferences()

  def beforeTearDown(self):
    self.abort()
    for folder in (
          self.portal.getDefaultModule(self.resource_portal_type),
          self.portal.getDefaultModule(self.sale_supply_portal_type),
          self.portal.getDefaultModule("Currency"),
          self.portal.getDefaultModule(self.node_portal_type),
          self.portal.getDefaultModule("Sale Order"),
          self.portal.getDefaultModule("Purchase Order"),):
      folder.manage_delObjects([i for i in folder.objectIds()])
    self.tic()

  def createCategories(self):
    """
      Light install create only base categories, so we create
      some categories for testing them
    """
    size_category_list = ['Baby', 'Child', 'Man', 'Woman']
    if len(self.category_tool.size.contentValues()) == 0 :
      for category_id in size_category_list:
        o = self.category_tool.size.newContent(portal_type='Category',
                                               id=category_id)
    self.size_category_list = map(lambda x: 'size/%s' % x,
                                  size_category_list)

    colour_category_list = ['blue', 'green']
    if len(self.category_tool.colour.contentValues()) == 0 :
      for category_id in colour_category_list:
        o = self.category_tool.colour.newContent(portal_type='Category',
                                               id=category_id)
    self.colour_category_list = map(lambda x: 'colour/%s' % x,
                                    colour_category_list)

    ind_phase_category_list = ['phase1', 'phase2']
    if len(self.category_tool.industrial_phase.contentValues()) == 0:
      for category_id in ind_phase_category_list:
        o = self.category_tool.industrial_phase.newContent(
                                               portal_type='Category',
                                               id=category_id)
    self.industrial_phase_category_list = map(
                                    lambda x: 'industrial_phase/%s' % x,
                                    ind_phase_category_list)

    self.morphology_category_list = []
    self.base_category_content_list = {
      'size':self.size_category_list,
      'colour':self.colour_category_list,
      'morphology':self.morphology_category_list,
      'industrial_phase':self.industrial_phase_category_list
    }

    quantity_unit_weight = self.portal.portal_categories.quantity_unit._getOb(
                                        'weight', None)
    if quantity_unit_weight is None:
      quantity_unit_weight = self.portal.portal_categories.quantity_unit\
                                  .newContent(id='weight',
                                              portal_type='Category')
    self.quantity_unit_gram = quantity_unit_weight._getOb('gram', None)
    if self.quantity_unit_gram is None:
      self.quantity_unit_gram = quantity_unit_weight.newContent(
                                      portal_type='Category',
                                      id='gram')
    self.quantity_unit_kilo = quantity_unit_weight._getOb('kilo', None)
    if self.quantity_unit_kilo is None:
      self.quantity_unit_kilo = quantity_unit_weight.newContent(
                                      portal_type='Category',
                                      id='kilo')

    unit_conversion_module = self.portal.quantity_unit_conversion_module
    weight_group = unit_conversion_module._getOb('weight', None)
    if weight_group is None:
      weight_group = unit_conversion_module.newContent(id='weight',
                                  portal_type='Quantity Unit Conversion Group',
                                  quantity_unit='weight/kilo')
      weight_group.validate()

    gram_definition = weight_group._getOb('gram', None)
    if gram_definition is None:
      gram_definition = weight_group.newContent(id='gram',
                              portal_type='Quantity Unit Conversion Definition',
                              quantity_unit='weight/gram',
                              quantity=0.001)
      gram_definition.validate()

    # create some product line categories
    product_line = self.portal.portal_categories.product_line
    if product_line._getOb('a', None) is None:
      product_line.newContent(
          id='a',
          portal_type='Category')
    if product_line._getOb('b', None) is None:
      product_line.newContent(
          id='b',
          portal_type='Category')

  def stepCreateResource(self, sequence=None, sequence_list=None, **kw):
    """
      Create a resource without variation
    """
    resource_module = self.portal.getDefaultModule(self.resource_portal_type)
    resource = resource_module.newContent( \
                                 portal_type=self.resource_portal_type)
    resource.edit(
      title = "Resource"
    )
    sequence.edit(resource=resource,
                  variation_property_list=[])
    self.category_list = []
    # Actually, resource has no individual variation
    for base_category in resource.getVariationBaseCategoryList():
      sequence.edit(**{base_category:None})

  def stepCheckGetVariationBaseCategoryList(self, sequence=None,
                                             sequence_list=None, **kw):
    """
      Check if getVariationBaseCategoryList returns the good result
    """
    resource = sequence.get('resource')
    vbcl = resource.getVariationBaseCategoryList()
    self.failIfDifferentSet(self.variation_base_category_list, vbcl)

  def stepCheckGetVariationRangeCategoryList(self, sequence=None,
                                             sequence_list=None, **kw):
    """
      Check if getVariationRangeCategoryList returns the good result
    """
    resource = sequence.get('resource')
    vbcl = resource.getVariationBaseCategoryList()
    correct_variation_range_category_list = []
    for base_category in vbcl:
      # Check if resource has individual variations
      individual_variation_list = sequence.get(base_category)
      if individual_variation_list is None:
        correct_variation_range_category_list.extend(
                               self.base_category_content_list[base_category])
      else:
        correct_variation_range_category_list.extend(individual_variation_list)

    vrcl = resource.getVariationRangeCategoryList()
    self.failIfDifferentSet(correct_variation_range_category_list, vrcl)

  def stepSetCategoryVariation(self, sequence=None, sequence_list=None, **kw):
    """
      Set category variation to current resource
    """
    resource = sequence.get('resource')
    size_list = map(lambda x: x[len('size/'):], self.size_list)
    resource.setSizeList(size_list)
    self.category_list = self.size_list[:]

  def stepSetIndividualVariationWithEmptyBase(self, sequence=None,
                                              sequence_list=None, **kw):
    """
    Set the individual variation of the current resource to a base category
    that contains no subobjects.
    """
    resource = sequence.get('resource')
    morphology_list = []
    morphology_variation_count = 2
    for i in range(morphology_variation_count) :
      variation_portal_type = 'Apparel Model Morphology Variation'
      variation = resource.newContent(portal_type=variation_portal_type)
      variation.edit(
        title = 'MorphologyVariation%s' % str(i)
      )
      morphology_list.append('morphology/%s' %
                                        variation.getRelativeUrl())
    # store individual resource
    sequence.edit(morphology=morphology_list)

  def stepSetIndividualVariationWithFillBase(self, sequence=None,
                                              sequence_list=None, **kw):
    """
    Set the individual variation of the current resource to a base category
    that contains some subobjects.
    """
    resource = sequence.get('resource')
    colour_list = []
    colour_variation_count = 1
    for i in range(colour_variation_count) :
      variation_portal_type = 'Apparel Model Colour Variation'
      variation = resource.newContent(portal_type=variation_portal_type)
      variation.edit(
        title = 'ColourVariation%s' % str(i)
      )
      colour_list.append('colour/%s' % variation.getRelativeUrl())
    # store individual resource
    sequence.edit(colour=colour_list)

  def test_01_getVariationBaseCategoryList(self, quiet=quiet, run=run_all_test):
    """
      Test the method getVariationBaseCategoryList on a resource.
    """
    if not run: return
    sequence_list = SequenceList()
    # Test when resource has no variation
    sequence_string = '\
                      CreateResource \
                      CheckGetVariationBaseCategoryList \
                      '
    sequence_list.addSequenceString(sequence_string)
    sequence_list.play(self, quiet=quiet)

  def genericTest(self, test_method_name, quiet=quiet):
    """
      Generic test on a resource.
    """
    sequence_list = SequenceList()
    # Test when resource has no variation
    sequence_string = '\
                      CreateResource \
                      %s \
                      ' % test_method_name
    sequence_list.addSequenceString(sequence_string)
    # Test when resource has category variations
    sequence_string = '\
                      CreateResource \
                      SetCategoryVariation \
                      %s \
                      ' % test_method_name
    sequence_list.addSequenceString(sequence_string)
    # Test when resource has individual variation and base category
    # has no content
    sequence_string = '\
                      CreateResource \
                      SetIndividualVariationWithEmptyBase \
                      Tic \
                      %s \
                      ' % test_method_name
    sequence_list.addSequenceString(sequence_string)
    # Test when resource has individual variation and base category
    # has category content
    sequence_string = '\
                      CreateResource \
                      SetIndividualVariationWithFillBase \
                      Tic \
                      %s \
                      ' % test_method_name
    sequence_list.addSequenceString(sequence_string)
    # Test with all cases
    sequence_string = '\
                      CreateResource \
                      SetCategoryVariation \
                      SetIndividualVariationWithEmptyBase \
                      SetIndividualVariationWithFillBase \
                      Tic \
                      %s \
                      ' % test_method_name
    sequence_list.addSequenceString(sequence_string)
    sequence_list.play(self, quiet=quiet)

  def test_02_getVariationRangeCategoryList(self, quiet=quiet, run=run_all_test):
    """
      Test the method getVariationRangeCategoryList on a resource.
    """
    if not run: return
    self.genericTest('CheckGetVariationRangeCategoryList', quiet=quiet)

  def stepCheckGetVariationRangeCategoryItemList(self, sequence=None,
                                                 sequence_list=None, **kw):
    """
      Check if getVariationRangeCategoryItemList returns the good result.
      Does not test display...
      Item are left display.
    """
    resource = sequence.get('resource')
    vrcl = resource.getVariationRangeCategoryList()
    vrcil = resource.getVariationRangeCategoryItemList()
    self.failIfDifferentSet(vrcl, map(lambda x: x[1], vrcil))

  def test_03_getVariationRangeCategoryItemList(self, quiet=quiet,
                                                run=run_all_test):
    """
      Test the method getVariationRangeCategoryItemList on a resource.
    """
    if not run: return
    self.genericTest('CheckGetVariationRangeCategoryItemList', quiet=quiet)

  def stepCheckGetVariationCategoryList(self, sequence=None,
                                                 sequence_list=None, **kw):
    """
    Check if getVariationCategoryList returns the good result, with default
    value for omit_individual_variation parameter
    """
    resource = sequence.get('resource')
    vcl = resource.getVariationCategoryList()
    self.failIfDifferentSet(self.category_list, vcl)

  def test_04_getVariationCategoryList(self, quiet=quiet, run=run_all_test):
    """
      Test the method getVariationCategoryList on a resource.
    """
    if not run: return
    self.genericTest('CheckGetVariationCategoryList', quiet=quiet)

  def stepCheckGetVariationCategoryListWithoutOmit(self, sequence=None,
                                                 sequence_list=None, **kw):
    """
      Check if getVariationCategoryList returns the good result,
      with parameter omit_individual_variation=0.
    """
    resource = sequence.get('resource')
    vcl = resource.getVariationCategoryList(omit_individual_variation=0)
    correct_vcl = self.category_list[:]

    for base_category in resource.getVariationBaseCategoryList():
      # Check if resource has individual variations
      individual_variation_list = sequence.get(base_category)
      if individual_variation_list is not None:
        correct_vcl.extend(individual_variation_list)
    self.failIfDifferentSet(correct_vcl, vcl)

  def test_05_getVariationCategoryList(self, quiet=quiet, run=run_all_test):
    """
      Test the method getVariationCategoryList on a resource
      with parameter omit_individual_variation=0.
    """
    if not run: return
    self.genericTest('CheckGetVariationCategoryListWithoutOmit', quiet)

  def stepCheckGetVariationCategoryItemList(self, sequence=None,
                                                 sequence_list=None, **kw):
    """
      Check if getVariationCategoryItemList returns the good result,
      with parameter omit_individual_variation=1.
    """
    resource = sequence.get('resource')
    vcl = resource.getVariationCategoryList()
    vcil = resource.getVariationCategoryItemList()
    self.failIfDifferentSet(vcl, map(lambda x: x[1], vcil))

  def test_06_getVariationCategoryItemList(self, quiet=quiet, run=run_all_test):
    """
      Test the method getVariationCategoryItemList on a resource.
    """
    if not run: return
    self.genericTest('CheckGetVariationCategoryItemList', quiet)

  def stepCheckGetVariationCategoryItemListWithoutOmit(self, sequence=None,
                                                 sequence_list=None, **kw):
    """
      Check if getVariationCategoryItemList returns the good result,
      with parameter omit_individual_variation=0.
    """
    resource = sequence.get('resource')
    vcl = resource.getVariationCategoryList(omit_individual_variation=0)
    vcil = resource.getVariationCategoryItemList(omit_individual_variation=0)
    self.failIfDifferentSet(vcl, map(lambda x: x[1], vcil))

  def test_07_getVariationCategoryItemList(self, quiet=quiet, run=run_all_test):
    """
      Test the method getVariationCategoryItemList on a resource
      with parameter omit_individual_variation=0.
    """
    if not run: return
    self.genericTest('CheckGetVariationCategoryItemListWithoutOmit',
        quiet=quiet)

  def stepCheckGetVariationPropertyList(self, sequence=None,
                                        sequence_list=None, **kw):
    """
      Check if GetVariationPropertyList exists on a resource.
    """
    resource = sequence.get('resource')
    vpl = sequence.get('variation_property_list')
    self.failIfDifferentSet(resource.getVariationPropertyList(),
                            vpl)

  def stepCheckSetVariationPropertyList(self, sequence=None,
                                        sequence_list=None, **kw):
    """
      Check if SetVariationPropertyList exists on a resource.
      And test it.
    """
    resource = sequence.get('resource')
    vpl = ['prop1', 'prop2']
    sequence.edit(variation_property_list=vpl)
    resource.setVariationPropertyList(vpl)
    self.failIfDifferentSet(resource.variation_property_list,
                            vpl)

  def test_08_variationPropertyList(self, quiet=quiet, run=run_all_test):
    """
      Simply test if method are well generated by the property sheet.
    """
    if not run: return
    sequence_list = SequenceList()
    # Test when resource has no variation
    sequence_string = '\
                      CreateResource \
                      CheckGetVariationPropertyList \
                      CheckSetVariationPropertyList \
                      CheckGetVariationPropertyList \
                      '
    sequence_list.addSequenceString(sequence_string)
    sequence_list.play(self, quiet=quiet)

  def getPriceConfig(self):
    """
    Define somes cases of pricing configuration to test.
    """
    config = [
      {
        'base_price': None,
        'additional_price': None,
        'surcharge_ratio': None,
        'discount_ratio': None,
        'exclusive_discount_ratio': None,
        'price': None,
      },{
        'base_price': 5,
        'additional_price': None,
        'surcharge_ratio': None,
        'discount_ratio': None,
        'exclusive_discount_ratio': None,
        'price': 5,
      },{
        'base_price': 5,
        'additional_price': 1,
        'surcharge_ratio': None,
        'discount_ratio': None,
        'exclusive_discount_ratio': None,
        'price': 6,
      },{
        'base_price': 5,
        'additional_price': 3,
        'surcharge_ratio': 0.5,
        'discount_ratio': None,
        'exclusive_discount_ratio': None,
        'price': 12,
      },{
        'base_price': 5,
        'additional_price': 3,
        'surcharge_ratio': None,
        'discount_ratio': 0.25,
        'exclusive_discount_ratio': None,
        'price': 6,
      },{
        'base_price': 5,
        'additional_price': 3,
        'surcharge_ratio': None,
        'discount_ratio': None,
        'exclusive_discount_ratio': 0.5,
        'price': 4,
      },{
        'base_price': 5,
        'additional_price': 3,
        'surcharge_ratio': None,
        'discount_ratio': 0.5,
        'exclusive_discount_ratio': 0.75,
        'price': 2,
      },{
        'base_price': 5,
        'additional_price': 3,
        'surcharge_ratio': None,
        'discount_ratio': 0.75,
        'exclusive_discount_ratio': 0.25,
        'price': 2,
      },{
        'base_price': 5,
        'additional_price': 3,
        'surcharge_ratio': 1,
        'discount_ratio': 0.75,
        'exclusive_discount_ratio': 0.25,
        'price': 4,
      },{
        'base_price': None,
        'additional_price': 3,
        'surcharge_ratio': 1,
        'discount_ratio': 0.75,
        'exclusive_discount_ratio': 0.25,
        'price': None,
      }
    ]
    return config

  def logMessage(self, msg, tab=0):
    """
    Log a message.
    """
    if self.quiet:
      return
    if tab:
      msg = '  %s' % msg
    ZopeTestCase._print('\n%s' % msg)
    LOG('testResource.play', 0, msg)

  def test_09_getPrice(self, quiet=quiet, run=run_all_test):
    """
    Test the pricing model.
    """
    if not run: return
    config_list = self.getPriceConfig()
    for i in range(0, len(config_list)):
      self.logMessage("Starting New Pricing Case %i..." % i)
      config = config_list[i]
      # Create product
      self.logMessage("Creating product...", tab=1)
      product_module = self.portal.getDefaultModule(self.product_portal_type)
      product = product_module.newContent( \
                                   portal_type=self.product_portal_type,
                                   title='Product%i' % i)
      # Configure pricing parameters
      for key, value in config.items():
        if key != 'price':
          if value not in [None, []]:
            if type(value) != type([]):
              value_list = [value]
            else:
              value_list = value
            # Create requested supply line
            for pricing_param in value_list:
              self.logMessage("Creating supply line...", tab=1)
              supply_line = product.newContent(
                    portal_type=self.sale_supply_line_portal_type)
              # Set pricing parameter
              self.logMessage("Set %s on supply line with value %s..." % \
                              (key, str(pricing_param)), tab=1)
              supply_line.setProperty(key, pricing_param)
      # Commit transaction
      self.logMessage("Commit transaction...", tab=1)
      self.commit()
      # Tic
      self.logMessage("Tic...", tab=1)
      self.tic()
      # Check resource price
      self.logMessage("Check product price...", tab=1)
      self.assertEqual(config['price'], product.getPrice())

  def test_10_getPriceWithOptions(self, quiet=quiet, run=run_all_test):
    """
    Test the pricing model on a resource with options.
    """
    if not run: return
    i = 1
    self.logMessage("Starting New Option Pricing Case %i..." % i)
    # Fill the PDM preferences
    preference = self.portal.portal_preferences.default_site_preference
    preference.setPreferredProductOptionalVariationBaseCategoryList(['industrial_phase'])
    if preference.getPreferenceState() == 'disabled':
      preference.enable()
    self.tic()
    # Create another product/supply, in order to be sure that the
    # nothing will be generated from this supply!
    self.logMessage("Creating fake product...", tab=1)
    product_module = self.portal.getDefaultModule(self.product_portal_type)
    product = product_module.newContent( \
                                 portal_type=self.product_portal_type,
                                 title='FakeProduct%i' % i)
    product.setVariationCategoryList(self.industrial_phase_category_list)
    self.logMessage("Creating supply line...", tab=1)
    supply_line = product.newContent(
          portal_type=self.sale_supply_line_portal_type)
    supply_line.setProperty('base_price', 100)
    supply_line.setSurchargeRatioQuantityStepList([])
    supply_line.getCellKeyList(base_id='path_optional_surcharge_ratio')
    cell1 = supply_line.newCell('industrial_phase/phase1',
        base_id='path_optional_surcharge_ratio',
        portal_type=self.sale_supply_cell_portal_type)
    cell1.setSurchargeRatio(20)
    cell1.setMappedValuePropertyList(["surcharge_ratio"])
    cell1.setMembershipCriterionBaseCategory('industrial_phase')
    cell1.setMembershipCriterionCategory('industrial_phase/phase1')
    # Create product
    self.logMessage("Creating product...", tab=1)
    product_module = self.portal.getDefaultModule(self.product_portal_type)
    product = product_module.newContent( \
                                 portal_type=self.product_portal_type,
                                 title='Product%i' % i)
    # Select some options on the resource
    product.setVariationCategoryList(self.industrial_phase_category_list)
    # Create requested supply line
    self.logMessage("Creating supply line...", tab=1)
    supply_line = product.newContent(
          portal_type=self.sale_supply_line_portal_type)
    # Set pricing parameter
    supply_line.setProperty('base_price', 1)
    # Define the additional price matrix range
    supply_line.setAdditionalPriceQuantityStepList([])
    supply_line.getCellKeyList(base_id='path_optional_additional_price')
    cell1 = supply_line.newCell('industrial_phase/phase1',
        base_id='path_optional_additional_price',
        portal_type=self.sale_supply_cell_portal_type)
    cell1.setAdditionalPrice(2)
    cell1.setMappedValuePropertyList(["additional_price"])
    cell1.setMembershipCriterionBaseCategory('industrial_phase')
    cell1.setMembershipCriterionCategory('industrial_phase/phase1')
    cell2 = supply_line.newCell('industrial_phase/phase2',
        base_id='path_optional_additional_price',
        portal_type=self.sale_supply_cell_portal_type)
    cell2.setAdditionalPrice(7)
    cell2.setMappedValuePropertyList(["additional_price"])
    cell2.setMembershipCriterionBaseCategory('industrial_phase')
    cell2.setMembershipCriterionCategory('industrial_phase/phase2')
    # Commit transaction
    self.logMessage("Commit transaction...", tab=1)
    self.commit()
    # Tic
    self.logMessage("Tic...", tab=1)
    self.tic()
    # Check resource price
    self.logMessage("Check product price without option...", tab=1)
    self.assertEqual(1, product.getPrice(context=supply_line))
    # Check resource option price
    self.logMessage("Check product price with option: %s..." % \
                    'industrial_phase/phase1', tab=1)
    self.assertEqual(3, product.getPrice(
                                   categories=['industrial_phase/phase1']))
    self.logMessage("Check product price with option: %s..." % \
                    'industrial_phase/phase2', tab=1)
    self.assertEqual(8, product.getPrice(
                                   categories=['industrial_phase/phase2']))
    self.logMessage("Check product price with options: %s..." % \
                    'industrial_phase/phase1 industrial_phase/phase2', tab=1)
    self.assertEqual(10, product.getPrice(
                                   categories=['industrial_phase/phase1',
                                               'industrial_phase/phase2']))

  def test_11_getPriceWithDestinationSection(self, quiet=quiet, run=run_all_test):
    """
    Test the pricing model with multiple price for
    differents destination sections.
    """
    if not run: return
    # Initialize variables
    test_case_list = []
    # Create product
    product_module = self.portal.getDefaultModule(self.product_portal_type)
    supply_module = self.portal.getDefaultModule(self.sale_supply_portal_type)
    for j in range(33, 35):
      self.logMessage("Creating fake product %s..." % j, tab=1)
      product = product_module.newContent(
                           portal_type=self.product_portal_type,
                           title='AnotherFakeProduct%s' % j)
      # Create some nodes
      node_module = self.portal.getDefaultModule(self.node_portal_type)
      for i in range(11, 14):
        self.logMessage("Creating fake node %s..." % i, tab=1)
        node = node_module.newContent(
                       portal_type=self.node_portal_type,
                       title='FakeNode%s%s' % (j, i))
        # Create a supply
        self.logMessage("Creating fake supply %s..." % i, tab=1)
        supply = supply_module.newContent(
                                     portal_type=self.sale_supply_portal_type,
                                     title='FakeSupply%s' % i,
                                     destination_section_value=node)
        supply.validate()
        self.logMessage("Creating fake supply line %s..." % i, tab=1)
        supply_line = supply.newContent(
              portal_type=self.sale_supply_line_portal_type,
              resource_value=product)
        # Set pricing parameter
        base_price = i*j
        supply_line.setProperty('base_price', base_price)
        # Register the case
        test_case_list.append((product, node, base_price))
    # Commit transaction
    self.logMessage("Commit transaction...", tab=1)
    self.commit()
    # Tic
    self.logMessage("Tic...", tab=1)
    self.tic()
    # Test the cases
    for product, node, base_price in test_case_list:
      self.logMessage("Check product %s with destination section %s" % \
                      (product.getTitle(), node.getTitle()),
                      tab=1)
      self.assertEqual(base_price,
                        product.getPrice(
                  categories=['destination_section/%s' % node.getRelativeUrl()]))

  def test_11b_getPriceWithCells(self, quiet=quiet, run=run_all_test):
    """
    Test the pricing model with multiple price for
    differents destination sections, using supply cells
    """
    if not run: return
    # Initialize variables
    test_case_list = []
    # Create product
    product_module = self.portal.getDefaultModule(self.product_portal_type)
    supply_module = self.portal.getDefaultModule(self.sale_supply_portal_type)
    # Create generic supply
    self.logMessage("Creating generic fake supply ...", tab=1)
    generic_supply = supply_module.newContent(
                     portal_type=self.sale_supply_portal_type,
                     title='FakeGenericSupply',)
    generic_supply.validate()
    # Create empty supply line
    supply_line = generic_supply.newContent(
          portal_type=self.sale_supply_line_portal_type)
    supply_line.setProperty('base_price', 0)
    for j in range(33, 35):
      self.logMessage("Creating fake product %s..." % j, tab=1)
      product = product_module.newContent(
                           portal_type=self.product_portal_type,
                           title='AnotherFakeProduct%s' % j)
      product.setVariationBaseCategoryList(['size'])
      product.setVariationCategoryList(['size/Baby', 'size/Man'])
      # Create some nodes
      node_module = self.portal.getDefaultModule(self.node_portal_type)
      for i in range(11, 14):
        self.logMessage("Creating fake node %s..." % i, tab=1)
        node = node_module.newContent(
                       portal_type=self.node_portal_type,
                       title='FakeNode%s%s' % (j, i))
        # Create a supply
        self.logMessage("Creating fake supply %s..." % i, tab=1)
        supply = supply_module.newContent(
                                     portal_type=self.sale_supply_portal_type,
                                     title='FakeSupply%s' % i,
                                     destination_section_value=node)
        supply.validate()

        if 0:
          # XXX if both a supply line for the resource and a supply cell for
          # the resource with the exact variation can be applied, one of them
          # is choosen randomly. It looks like a bug, but I'm not sure we
          # should handle such situation.
          self.logMessage("Creating wrong supply line %s..." % i, tab=1)
          wrong_supply_line = supply.newContent(
                portal_type=self.sale_supply_line_portal_type,
                resource_value=product)
          wrong_supply_line.setBasePrice(12454326)

        self.logMessage("Creating fake supply line %s..." % i, tab=1)
        supply_line = supply.newContent(
              portal_type=self.sale_supply_line_portal_type,
              resource_value=product)
        supply_line.setPVariationBaseCategoryList(['size'])
        supply_line.updateCellRange(base_id='path')

        baby_cell = supply_line.newCell('size/Baby',
                           portal_type=self.sale_supply_cell_portal_type)
        baby_cell.setVariationCategoryList(['size/Baby'])
        baby_cell.setPredicateCategoryList(['size/Baby'])
        baby_cell.setMappedValuePropertyList(['base_price'])
        baby_cell.setMembershipCriterionBaseCategory('size')
        baby_cell.setMembershipCriterionCategory('size/Baby')
        base_price = i*j
        baby_cell.setProperty('base_price', base_price)
        # Register the case
        test_case_list.append((product, 'size/Baby', node, base_price))

        man_cell = supply_line.newCell('size/Man',
                        portal_type=self.sale_supply_cell_portal_type)
        man_cell.setVariationCategoryList(['size/Man'])
        man_cell.setPredicateCategoryList(['size/Man'])
        man_cell.setMappedValuePropertyList(['base_price'])
        man_cell.setMembershipCriterionBaseCategory('size')
        man_cell.setMembershipCriterionCategory('size/Man')
        base_price = i*j+3
        man_cell.setProperty('base_price', base_price)
        # Register the case
        test_case_list.append((product, 'size/Man', node, base_price))

      # Create generic supply line
      self.logMessage("Creating generic fake supply line ...", tab=1)
      supply_line = generic_supply.newContent(
            portal_type=self.sale_supply_line_portal_type,
            resource_value=product)
      supply_line.setProperty('base_price', j)
      test_case_list.append((product, None, None, j))

    # Commit transaction
    self.logMessage("Commit transaction...", tab=1)
    self.commit()
    # Tic
    self.logMessage("Tic...", tab=1)
    self.tic()
    # Test the cases
    for product, variation, node, base_price in test_case_list:
      categories = []
      if node is not None:
        self.logMessage("Check product %s with destination section %s" % \
                        (product.getTitle(), node.getTitle()),
                        tab=1)
        categories.append('destination_section/' + node.getRelativeUrl())
      else:
        self.logMessage("Check product %s without destination section" % \
                        product.getTitle(),
                        tab=1)
      if variation:
        categories.append(variation)

      def sortResult(a, b):
        def _pricingSortMethod(a, b): # XXX copied from Resource.py
          # Simple method : the one that defines a destination section wins
          if a.getDestinationSection():
            return -1 # a defines a destination section and wins
          return 1 # a defines no destination section and loses
        if _pricingSortMethod(a, b):
          # now sort based on resource definition
          if a.getResourceValue():
            if b.getResourceValue():
              return 1
            else:
              return -1
          else:
            return 1
        else:
          return -1


      self.assertEqual(base_price, product.getPrice(categories=categories,
                                                    sort_method=sortResult))


  # The following test tests Movement.getPrice, which is based on the movement
  # context.

  def test_12_getInternalVsPurchaseVsSalePrice(self, quiet=quiet, run=run_all_test):
    """
    Test the pricing model with internal, purchase and sale supply
    lines, and with source_section/destination_section.
    """
    if not run: return
    # Initialize variables
    product_module = self.portal.getDefaultModule(self.product_portal_type)
    organisation_module = self.getOrganisationModule()
    currency_module = self.getCurrencyModule()
    sale_order_module = self.portal.getDefaultModule("Sale Order")
    purchase_order_module = self.portal.getDefaultModule("Purchase Order")
    internal_packing_list_module = self.portal.getDefaultModule("Internal Packing List")
    # Create product
    product = product_module.newContent(
        portal_type=self.product_portal_type,
        title="yet another product")
    # Create organisations
    orga1 = organisation_module.newContent(
        portal_type="Organisation",
        title="orga1")
    orga2 = organisation_module.newContent(
        portal_type="Organisation",
        title="orga2")
    # Create sale supply lines
    product.newContent(
        portal_type=self.sale_supply_line_portal_type,
        base_price=100.0,
        destination_section_value=orga1)
    product.newContent(
        portal_type=self.sale_supply_line_portal_type,
        base_price=200.0,
        destination_section_value=orga2)
    product.newContent(
        portal_type=self.sale_supply_line_portal_type,
        base_price=400.0)
    # Create purchase supply lines
    product.newContent(
        portal_type=self.purchase_supply_line_portal_type,
        base_price=10.0,
        source_section_value=orga1)
    product.newContent(
        portal_type=self.purchase_supply_line_portal_type,
        base_price=20.0,
        source_section_value=orga2)
    product.newContent(
        portal_type=self.purchase_supply_line_portal_type,
        base_price=40.0)
    # Create internal supply lines
    product.newContent(
        portal_type=self.internal_supply_line_portal_type,
        base_price=1.0,
        destination_section_value=orga1)
    product.newContent(
        portal_type=self.internal_supply_line_portal_type,
        base_price=2.0,
        destination_section_value=orga2)
    product.newContent(
        portal_type=self.internal_supply_line_portal_type,
        base_price=4.0)
    # Create sale order and check price
    sale_order = sale_order_module.newContent(
        portal_type="Sale Order",
        start_date=DateTime(),
        stop_date=DateTime())
    sale_order_line = sale_order.newContent(
        portal_type=self.sale_order_line_portal_type,
        resource_value=product)
    self.tic()
    self.assertEqual(sale_order_line.getPrice(), 400.0)
    sale_order.setDestinationSectionValue(orga2)
    self.tic()
    sale_order_line.setPrice(None)
    self.assertEqual(sale_order_line.getPrice(), 200.0)
    # Create purchase order and check price
    purchase_order = purchase_order_module.newContent(
        portal_type="Purchase Order",
        start_date=DateTime(),
        stop_date=DateTime())
    purchase_order_line = purchase_order.newContent(
        portal_type="Purchase Order Line",
        resource_value=product)
    self.tic()
    self.assertEqual(purchase_order_line.getPrice(), 40.0)
    purchase_order.setSourceSectionValue(orga2)
    self.tic()
    purchase_order_line.setPrice(None)
    self.assertEqual(purchase_order_line.getPrice(), 20.0)
    # Create internal packing list and check price
    internal_packing_list = internal_packing_list_module.newContent(
        portal_type="Internal Packing List",
        start_date=DateTime(),
        stop_date=DateTime())
    internal_packing_list_line = internal_packing_list.newContent(
        portal_type="Internal Packing List Line",
        resource_value=product)
    self.tic()
    self.assertEqual(internal_packing_list_line.getPrice(), 4.0)
    internal_packing_list.setDestinationSectionValue(orga2)
    self.tic()
    internal_packing_list_line.setPrice(None)
    self.assertEqual(internal_packing_list_line.getPrice(), 2.0)

  def testGetPriceWithQuantityUnit(self):
    resource = self.portal.getDefaultModule(self.product_portal_type)\
                .newContent(portal_type=self.product_portal_type)
    resource.setDefaultQuantityUnitValue(self.quantity_unit_kilo)
    supply_line = resource.newContent(
                    portal_type=self.sale_supply_line_portal_type)
    supply_line.setBasePrice(1000)
    self.tic()
    sale_order = self.portal.getDefaultModule("Sale Order").newContent(
                              portal_type='Sale Order',)
    sale_order_line = sale_order.newContent(
                          portal_type=self.sale_order_line_portal_type,
                          resource_value=resource,
                          quantity=5)
    self.assertEqual(1000, sale_order_line.getPrice())
    self.assertEqual(5000, sale_order_line.getTotalPrice())

    # if we give the quantity unit in grams
    sale_order_line = sale_order.newContent(
                          portal_type=self.sale_order_line_portal_type,
                          resource_value=resource,
                          quantity=5000,
                          quantity_unit_value=self.quantity_unit_gram)
    self.assertEqual(1, sale_order_line.getPrice())
    self.assertEqual(5000, sale_order_line.getTotalPrice())

  def testGetPriceWithPriceCurrency(self):
    currency_module = self.portal.getDefaultModule("Currency")
    currency = currency_module.newContent(
                     portal_type="Currency",
                     title='A great currency')
    other_currency = currency_module.newContent(
                     portal_type="Currency",
                     title='Another currency')

    resource = self.portal.getDefaultModule(self.product_portal_type)\
                .newContent(portal_type=self.product_portal_type)
    resource.setDefaultQuantityUnitValue(self.quantity_unit_kilo)
    supply_line = resource.newContent(
                    portal_type=self.sale_supply_line_portal_type)
    supply_line.setBasePrice(1000)
    supply_line.setPriceCurrencyValue(currency)
    self.tic()
    sale_order = self.portal.getDefaultModule("Sale Order").newContent(
                              portal_type='Sale Order',
                              price_currency_value=other_currency)
    sale_order_line = sale_order.newContent(
                          portal_type=self.sale_order_line_portal_type,
                          resource_value=resource,
                          quantity=5)
    # order and supply lines uses different currency, price does not apply
    self.assertEqual(None, sale_order_line.getPrice())

    # set the same currency
    sale_order.setPriceCurrencyValue(currency)

    # price applies
    self.assertEqual(1000, sale_order_line.getPrice())
    self.assertEqual(5000, sale_order_line.getTotalPrice())

  def testGetPriceProductLine(self):
    """Test supply line set for a product line.
    """
    # This supply line defines a price applicable for all resources member
    # of product line a
    supply = self.portal.getDefaultModule(self.sale_supply_portal_type).newContent(
                     portal_type=self.sale_supply_portal_type)
    supply_line = supply.newContent(portal_type=self.sale_supply_line_portal_type)
    supply_line.setProductLineValue(self.portal.portal_categories.product_line.a)
    supply_line.setBasePrice(1000)
    supply.validate()

    resource_a = self.portal.getDefaultModule(self.product_portal_type)\
                .newContent(portal_type=self.product_portal_type)
    resource_a.setProductLineValue(self.portal.portal_categories.product_line.a)
    resource_b = self.portal.getDefaultModule(self.product_portal_type)\
                .newContent(portal_type=self.product_portal_type)
    resource_b.setProductLineValue(self.portal.portal_categories.product_line.b)

    self.tic()
    sale_order_line = self.portal.getDefaultModule("Sale Order").newContent(
                              portal_type='Sale Order').newContent(
                          portal_type=self.sale_order_line_portal_type,
                          resource_value=resource_a,
                          quantity=1)
    # resource_a is member of product_line/a, so our supply line applies.
    self.assertEqual(1000, sale_order_line.getPrice())

    sale_order_line = self.portal.getDefaultModule("Sale Order").newContent(
                              portal_type='Sale Order').newContent(
                          portal_type=self.sale_order_line_portal_type,
                          resource_value=resource_b,
                          quantity=1)
    # resource_b is member of product_line/b, so our supply line does not apply.
    self.assertEqual(None, sale_order_line.getPrice())


  def testQuantityPrecision(self):
    """test how to define quantity precision on resources.
    """
    resource = self.portal.getDefaultModule(self.product_portal_type)\
                .newContent(portal_type=self.product_portal_type)
    # default is 1
    self.assertEqual(1, resource.getBaseUnitQuantity())
    self.assertEqual(0, resource.getQuantityPrecision())
    # quantity precision is calculated using base quantity unit
    resource.setBaseUnitQuantity(0.001)
    self.assertEqual(3, resource.getQuantityPrecision())

  def test_defaultSupplyLineAfterClone(self):
    """Check that default supply line is properly set up after clone"""
    resource = self.portal.getDefaultModule(self.product_portal_type)\
                .newContent(portal_type=self.product_portal_type)

    resource.edit( purchase_supply_line_base_price=1.0,
      sale_supply_line_base_price=1.0,
    )

    self.assertEqual( resource,
        resource.getDefaultPurchaseSupplyLineValue().getResourceValue() )
    self.assertEqual( resource,
        resource.getDefaultSaleSupplyLineValue().getResourceValue() )

    module = resource.getParentValue()

    cb_data = module.manage_copyObjects(ids=[resource.getId()])
    p_data = module.manage_pasteObjects(cb_data)

    new_resource = module._getOb(p_data[0]['new_id'])

    self.assertEqual(
      new_resource,
      new_resource.getDefaultPurchaseSupplyLineValue().getResourceValue()
    )

    self.assertEqual(
      new_resource,
      new_resource.getDefaultSaleSupplyLineValue().getResourceValue()
    )

  def test_sourceDestinationReferenceOnSupplyLineOnDefaultSupplyLine(self):
    '''Check that it's possible to set and get a source/destination_reference
    on default supply line
    '''
    resource = self.portal.getDefaultModule(self.product_portal_type)\
                .newContent(portal_type=self.product_portal_type)

    resource.edit(\
        purchase_supply_line_source_reference='test_source_reference_on_purchase_supply_line',
        purchase_supply_line_destination_reference='test_destination_reference_on_purchase_supply_line',
        sale_supply_line_source_reference='test_source_reference_on_sale_supply_line',
        sale_supply_line_destination_reference='test_destination_reference_on_sale_supply_line',
        internal_supply_line_source_reference='test_source_reference_on_internal_supply_line',
        internal_supply_line_destination_reference='test_destination_reference_on_internal_supply_line',
    )
    self.assertEqual(resource.getPurchaseSupplyLineSourceReference(),
        'test_source_reference_on_purchase_supply_line')
    self.assertEqual(resource.getPurchaseSupplyLineDestinationReference(),
        'test_destination_reference_on_purchase_supply_line')
    self.assertEqual(resource.getSaleSupplyLineSourceReference(),
        'test_source_reference_on_sale_supply_line')
    self.assertEqual(resource.getSaleSupplyLineDestinationReference(),
        'test_destination_reference_on_sale_supply_line')
    self.assertEqual(resource.getInternalSupplyLineSourceReference(),
        'test_source_reference_on_internal_supply_line')
    self.assertEqual(resource.getInternalSupplyLineDestinationReference(),
        'test_destination_reference_on_internal_supply_line')

  def testQuantityUnitOnMovement(self):
    """Make sure that changing default quantity unit on resource does not
       affect to movement.
       In this test, always use Base.edit method. Because Base.edit is
       used when real user edit document through edit form.
    """
    # Set up quantity unit categories
    # weight
    quantity_unit_category_value = self.portal.portal_categories.quantity_unit
    quantity_unit_weight = quantity_unit_category_value._getOb('weight', None)
    if quantity_unit_weight is None:
      quantity_unit_weight = quantity_unit_category_value.newContent(
        id='weight', portal_type='Category')
    quantity_unit_gram = quantity_unit_weight._getOb('gram', None)
    if quantity_unit_gram is None:
      quantity_unit_gram = quantity_unit_weight.newContent(
        portal_type='Category', id='gram')
    # volume
    quantity_unit_volume = quantity_unit_category_value._getOb('volume', None)
    if quantity_unit_volume is None:
      quantity_unit_volume = quantity_unit_category_value.newContent(
        id='volume', portal_type='Category')
    quantity_unit_liter = quantity_unit_volume._getOb('liter', None)
    if quantity_unit_liter is None:
      quantity_unit_liter = quantity_unit_volume.newContent(
        portal_type='Category', id='liter')
    self.commit()

    # Create resource
    resource_value = self.portal.getDefaultModule(
      self.product_portal_type).newContent(portal_type=self.product_portal_type)
    resource_value.edit(quantity_unit_value_list=[
        quantity_unit_gram, quantity_unit_liter])
    self.commit()
    self.assertEqual(resource_value.getDefaultQuantityUnitValue(),
                     quantity_unit_gram)

    # Create sale order line
    sale_order = self.portal.getDefaultModule('Sale Order').newContent(
      portal_type='Sale Order')
    sale_order_line = sale_order.newContent(
      portal_type=self.sale_order_line_portal_type)
    self.commit()

    # Set resource to movement
    sale_order_line.edit(resource_value=resource_value)
    self.commit()
    self.assertEqual(sale_order_line.getQuantityUnitValue(),
                     quantity_unit_gram)

    # Select different quantity unit
    sale_order_line.edit(quantity_unit_value=quantity_unit_liter)
    self.commit()
    self.assertEqual(sale_order_line.getQuantityUnitValue(),
                     quantity_unit_liter)

    # Select empty(no quantity unit)
    sale_order_line.edit(quantity_unit_value=None)
    self.commit()

    # Select default quantity unit again
    sale_order_line.edit(quantity_unit_value=quantity_unit_gram)
    self.commit()
    self.assertEqual(sale_order_line.getQuantityUnitValue(),
                     quantity_unit_gram)

    # Change default quantity unit on resource
    # Now liter is default quantity unit.
    resource_value.edit(quantity_unit_value_list=[
        quantity_unit_liter, quantity_unit_gram])
    self.commit()

    # Check existing movement again and make sure that quantity
    # unit is not changed.
    expectedFailure(self.assertEqual)(
      sale_order_line.getQuantityUnitValue(),
      quantity_unit_gram)

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