##############################################################################
#
# Copyright (c) 2009 Nexedi SARL and Contributors. All Rights Reserved.
#          Sebastien Robin <seb@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.ERP5Type.tests.ERP5TypeTestCase import ERP5TypeTestCase
from testProductionOrder import TestProductionOrderMixin
import transaction

class TestTransformationMixin(TestProductionOrderMixin):
  """
  Mixin class for checking transformations
  """

  # Override, let TestProductionOrderMixin define the categories for us
  operation_category_list = ['sewing', 'packing']

  def createTransformation(self):
    module = self.getPortalObject().getDefaultModule(
        self.transformation_portal_type)
    transformation = module.newContent(portal_type=self.transformation_portal_type)
    return transformation

  def createTransformedResource(self, transformation=None):
    transformed_resource = transformation.newContent(
        portal_type=self.transformed_resource_portal_type)
    return transformed_resource

  def createComponent(self, variation_property_list=None):
    module = self.getPortalObject().getDefaultModule(self.component_portal_type)
    component = module.newContent(portal_type=self.component_portal_type)
    if variation_property_list is not None:
      component.setVariationPropertyList(variation_property_list)
    return component

  def createResource(self, title, variation_base_category_list,
      variation_category_list):
    portal = self.getPortal()
    resource_module = portal.getDefaultModule(self.resource_portal_type)
    resource = resource_module.newContent(portal_type=self.resource_portal_type)
    resource.edit(
      title = title,
      variation_base_category_list = variation_base_category_list,
    )
    resource.setVariationCategoryList(variation_category_list)
    return resource

  size_category_list = ['size/' + q for q in TestProductionOrderMixin
      .mrp_size_list]
  colour_category_list = ['colour/' + q for q in TestProductionOrderMixin
      .colour_list]

  swimsuit_variation_base_category_list = ['size', 'colour']
  swimsuit_variation_category_list = size_category_list + colour_category_list
  fabric_variation_base_category_list = ['colour']
  fabric_variation_category_list = colour_category_list
  button_variation_base_category_list = ['size']
  button_variation_category_list = size_category_list

  def createButtonComponent(self):
    """
    Buttons, variated by size
    """
    resource = self.createComponent()
    resource.edit(
      title = "Round Button",
      variation_base_category_list = self.button_variation_base_category_list,
    )
    resource.setVariationCategoryList(self.button_variation_category_list)


class TestTransformation(TestTransformationMixin, ERP5TypeTestCase):

  def getBusinessTemplateList(self):
    """
    """
    return ('erp5_base','erp5_pdm', 'erp5_trade', 'erp5_mrp',)

  def test_01_getAggregatedAmountListWithVariatedProperty(self):
    """
    Make sure that getAggregatedAmountList is still working properly if we
    have additionnals propertysheets on transformations lines and that used
    variation properties
    """
    # Only for testing purpose, use a property sheet that has nothing to
    # do with component. It would have been possible to create a new
    # property sheet for this test
    self._addPropertySheet(self.transformed_resource_portal_type, 'Bug')
    variation_property_list = ['tested']

    transformation = self.createTransformation()
    transformed_resource = self.createTransformedResource(transformation)
    component = self.createComponent(
        variation_property_list=variation_property_list)
    transformed_resource.edit(
        resource_value=component,
        quantity=1)
    transformed_resource.setTested(True)
    aggregated_amount_list = transformation.getAggregatedAmountList()
    self.assertEquals(len(aggregated_amount_list), 1)
    aggregated_amount = aggregated_amount_list[0]
    # Make sure that the isTested method is working properly on the
    # temp object
    self.assertTrue(aggregated_amount.isTested())

  def test_variationCategory(self):
    swimcap = self.createResource(
        'Swimming Cap',
        self.swimsuit_variation_base_category_list,
        self.swimsuit_variation_category_list,
    )
    transformation = self.createTransformation()
    transformation.edit(
        title = 'Swimcap Production',
        variation_base_category_list = self.swimsuit_variation_base_category_list
    )
    transformation.setResourceValue(swimcap)

    transaction.commit()
    self.tic()
    self.assertEquals(
        swimcap.getVariationCategoryList(),
        transformation.getVariationCategoryList()
    )

  def test_transformedInventory(self):
    portal = self.getPortal()

    button_number = 3.0

    swimsuit = self.createResource(
        'Swimming Suit',
        self.swimsuit_variation_base_category_list,
        self.swimsuit_variation_category_list,
    )
    transformation = self.createTransformation()
    transformation.edit(
        title = 'Swimsuit Production',
        variation_base_category_list = self.swimsuit_variation_base_category_list
    )
    transformation.setResourceValue(swimsuit)

    fabric = self.createResource(
        'Fabric',
        self.fabric_variation_base_category_list,
        self.fabric_variation_category_list,
    )
    fabric_line = self.createTransformedResource(transformation)
    fabric_line.setResourceValue(fabric)

    fabric_line.setVVariationBaseCategoryList(['colour'])
    for colour in self.colour_category_list:
      # For a blue swimming suit, we need blue fabric
      fabric_line.newCell(colour,
                          categories = colour,
                          membership_criterion_base_category= ('colour',),
                          membership_criterion_category= (colour,),
                          base_id = 'variation')

    fabric_line.setQVariationBaseCategoryList(['size'])
    for i, size in enumerate(self.size_category_list):
      # Depending on the size, the quantity of Fabric is different.
      # arbitrarily, we fix the quantity for size s as:
      # self.size_category_list.index(s) + 1
      fabric_line.newCell(size,
                          quantity = i+1,
                          mapped_value_property_list = ('quantity',),
                          membership_criterion_base_category= ('size',),
                          membership_criterion_category= (size,),
                          base_id = 'quantity')

    button = self.createComponent()
    button.edit(
      title = 'Round Button',
      variation_base_category_list = self.button_variation_base_category_list,
    )
    button.setVariationCategoryList(self.button_variation_category_list)

    button_line = self.createTransformedResource(transformation)
    button_line.setResourceValue(button)
    button_line.setQuantity(button_number)

    button_line.setVVariationBaseCategoryList(['size'])
    for size in self.size_category_list:
      # The button used depends on the size
      button_line.newCell(size,
                          categories = size,
                          membership_criterion_base_category= ('size',),
                          membership_criterion_category= (size,),
                          base_id = 'variation')

    sewing_line = transformation.newContent(
        portal_type = self.operation_line_portal_type)
    sewing_line.setResourceValue(
        portal.portal_categories.resolveCategory('operation/sewing'))

    sewing_line.setQVariationBaseCategoryList(['size', 'colour'])
    i = 1
    for size in self.size_category_list:
      for colour in self.colour_category_list:
        sewing_line.newCell(size,
                            colour,
                            mapped_value_property_list = ('quantity',),
                            membership_criterion_base_category= ('size', 'colour'),
                            membership_criterion_category= (size, colour),
                            quantity = i,
                            base_id = 'quantity')
        i += 1

    transaction.commit()
    self.tic()

    # Swimming Suit does not use ALL categories in Size category.
    # As a result, transformation lines should restrict their dimensions,
    # using the range induced by the resource, instead of naively
    # using the whole range directly from the variation categories.
    self.assertEqual(
        len(swimsuit.getVariationCategoryList(base_category_list=['size'])),
        len(fabric_line.getCellKeyList(base_id='quantity'))
    )

    swimsuit_quantity = 4.0
    from Products.ERP5Type.Document import newTempAmount
    n = 1
    # Check that getAggregatedAmount returns the expected results, a.k.a.
    # that our Transformation is set up correctly.
    for i, size in enumerate(self.size_category_list):
      for colour in self.colour_category_list:
        # id does not matter, just make it unique
        temp_amount = newTempAmount(transformation, "foo_%s_%s" % (size, colour))
        temp_amount.edit(
            quantity = swimsuit_quantity,
            variation_category_list = [size, colour],
            resource = swimsuit.getRelativeUrl(),
        )
        amount_list = transformation.getAggregatedAmountList(temp_amount)
        # fabric + button + sewing
        self.assertEquals(len(amount_list), 3)
        for amount in amount_list:
          resource = amount.getResource()
          if resource == fabric.getRelativeUrl():
            self.assertEquals(amount.getVariationCategoryList(), [colour])
            self.assertEquals(amount.getQuantity(), (i+1)*swimsuit_quantity)
          elif resource == button.getRelativeUrl():
            self.assertEquals(amount.getVariationCategoryList(), [size])
            self.assertEquals(amount.getQuantity(), button_number*swimsuit_quantity)
          elif resource == "operation/sewing":
            self.assertEquals(amount.getQuantity(), n*swimsuit_quantity)
          else:
            self.fail("Invalid Resource: %s" % resource)
        n += 1

    # XXX (will be expanded)

  def test_resourceIsNotAcquiredOnTransformationLines(self):
    '''
    We don't want resource define on transformation to be acquired on
    transformation lines
    '''
    transformation = self.createTransformation()

    # create a product
    portal = self.getPortalObject()
    product_module = portal.getDefaultModule('Product')
    product = product_module.newContent(portal_type='Product')

    # set the product as resource of the transformations
    transformation.setResourceValue(product)

    # add transformations lines and check the don't acquire the resource
    operation = transformation.newContent(portal_type='Transformation Operation')
    self.assertEquals(operation.getResource(), None)

    optional_resource = transformation.newContent(portal_type=\
        'Transformation Optional Resource')
    self.assertEquals(optional_resource.getResource(), None)

    transformed_resource = transformation.newContent(portal_type=\
        'Transformation Transformed Resource')
    self.assertEquals(transformed_resource.getResource(), None)

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