Example #1
0
    def _parse_edit_variations_form(self):
        """Parses the form the user submitted when editing variations,
        and returns a dictionary that contains the variation data.
        """
        form = self.request.form
        variation_config = IVariationConfig(self.context)
        variation_data = {}

        def _parse_data(variation_code):
            data = {}
            data['active'] = bool(form.get("%s-active" % variation_code))
            if data['active']:
                # TODO: Handle decimal more elegantly
                price = form.get("%s-price" % variation_code)
                try:
                    p = int(price)
                    # Create a tuple of ints from string
                    digits = tuple([int(i) for i in list(str(p))]) + (0, 0)
                    data['price'] = Decimal((0, digits, -2))
                except ValueError:
                    if not price == "":
                        data['price'] = Decimal(price)
                    else:
                        data['price'] = Decimal("0.00")

                data['skuCode'] = form.get("%s-skuCode" % variation_code)
                data['description'] = form.get("%s-description" %
                                               variation_code)

            # At this point the form has already been validated,
            # so uniqueness of sku codes is ensured
            data['hasUniqueSKU'] = True
            return data

        if len(variation_config.getVariationAttributes()) == 1:
            # One variation attribute
            for i, var1_value in enumerate(
                    variation_config.getVariation1Values()):
                variation_code = 'var-%s' % i

                variation_data[variation_code] = _parse_data(variation_code)
        else:
            # Two variation attributes
            for i, var1_value in enumerate(
                    variation_config.getVariation1Values()):
                for j, var2_value in enumerate(
                        variation_config.getVariation2Values()):
                    variation_code = 'var-%s-%s' % (i, j)
                    variation_data[variation_code] = _parse_data(
                        variation_code)

        return variation_data
Example #2
0
    def getItemDatas(self):
        """Returns a dictionary of an item's properties to be used in
        templates. If the item has variations, the variation config is
        also included.
        """
        results = []
        for item in self.getItems():
            assert(IShopItem.providedBy(item))
            varConf = IVariationConfig(item)

            has_variations = varConf.hasVariations()

            image = item.getField('image')
            if image and image.get_size(item):
                hasImage = True
                tag = image.tag(item, scale='mini')
            else:
                hasImage = False
                tag = None
            if has_variations:
                skuCode = None
                price = None
            else:
                varConf = None
                skuCode = item.Schema().getField('skuCode').get(item)
                price = item.Schema().getField('price').get(item)

            results.append(
                dict(
                    item = item,
                    title = item.Title(),
                    description = item.Description(),
                    url = item.absolute_url(),
                    hasImage = hasImage,
                    imageTag = tag,
                    variants = None,
                    skuCode = skuCode,
                    price = price,
                    showPrice = item.getField('showPrice').get(item),
                    unit=item.getField('unit').get(item),
                    uid = item.UID(),
                    varConf = varConf,
                    hasVariations = has_variations,
                    selectable_dimensions = item.getSelectableDimensions()))
        return results
Example #3
0
    def SearchableText(self):
        """ Make variations searchable. """
        data = super(ShopItem, self).SearchableText()

        var_conf = IVariationConfig(self)
        variation_details = []
        if var_conf.hasVariations():
            for variation in var_conf.getVariationDict().values():
                if not variation['active']:
                    continue
                variation_details.append(variation['description'])
                variation_details.append(variation['skuCode'])

        return ' '.join([
            data, ' '.join(self.getVariation1_values()),
            ' '.join(self.getVariation2_values()), ' '.join(variation_details),
            self.getSkuCode()
        ])
Example #4
0
    def _add_item(self,
                  skuCode=None,
                  quantity=1,
                  var1choice=None,
                  var2choice=None):
        """ Add item to cart
        """
        context = aq_inner(self.context)
        varConf = IVariationConfig(self.context)

        # get current items in cart
        session = self.request.SESSION
        cart_items = session.get(CART_KEY, {})
        variation_code = varConf.variation_code(var1choice, var2choice)

        # We got no skuCode, so look it up by variation key
        if not skuCode:
            skuCode = varConf.sku_code(var1choice, var2choice)

        item_key = varConf.key(var1choice, var2choice)
        item = cart_items.get(item_key, None)

        item_title = context.Title()
        quantity = int(quantity)
        vat_rate = Decimal(context.getField('vat').get(context))

        supplier_name, supplier_email = self._get_supplier_info(context)

        has_variations = varConf.hasVariations()
        if has_variations:
            variation_dict = varConf.getVariationDict()
            if not variation_dict[variation_code]['active']:
                # Item is disabled
                return False

            variation_pretty_name = varConf.getPrettyName(variation_code)
            item_title = '%s - %s' % (context.Title(), variation_pretty_name)
            price = Decimal(variation_dict[variation_code]['price'])
            # add item to cart
            if item is None:

                item = {
                    'title': item_title,
                    'description': context.Description(),
                    'skucode': skuCode,
                    'quantity': quantity,
                    'price': str(price),
                    'show_price': context.showPrice,
                    'total': str(price * quantity),
                    'url': context.absolute_url(),
                    'supplier_name': supplier_name,
                    'supplier_email': supplier_email,
                    'variation_code': variation_code,
                    'vat_rate': vat_rate,
                    'vat_amount': str(calc_vat(vat_rate, price))
                }
            # item already in cart, update quantity
            else:
                item['quantity'] = item.get('quantity', 0) + quantity
                item['total'] = str(item['quantity'] * price)
                item['vat_amount'] = str(calc_vat(vat_rate, price, quantity))

        else:
            price = Decimal(context.Schema().getField('price').get(context))
            # add item to cart
            if item is None:

                item = {
                    'title': item_title,
                    'description': context.Description(),
                    'skucode': skuCode,
                    'quantity': quantity,
                    'price': str(price),
                    'show_price': context.showPrice,
                    'total': str(price * quantity),
                    'url': context.absolute_url(),
                    'supplier_name': supplier_name,
                    'supplier_email': supplier_email,
                    'vat_rate': vat_rate,
                    'vat_amount': str(calc_vat(vat_rate, price))
                }
            # item already in cart, update quantitiy
            else:
                item['quantity'] = item.get('quantity', 0) + quantity
                item['total'] = str(item['quantity'] * price)
                item['vat_amount'] = str(calc_vat(vat_rate, price, quantity))

        # store cart in session
        cart_items[item_key] = item
        session[CART_KEY] = cart_items
        return True
Example #5
0
 def getVariationsConfig(self):
     """Returns the variation config for the item currently being viewed
     """
     context = aq_inner(self.context)
     variation_config = IVariationConfig(context)
     return variation_config
Example #6
0
    def __call__(self):
        """Migrates all items
        """
        catalog = getToolByName(self.context, 'portal_catalog')
        normalize = getUtility(IIDNormalizer).normalize
        response = ""
        stats = {}

        items = catalog(portal_type="ShopItem")
        for item in items:
            obj = item.getObject()
            stats[obj.UID()] = {'status': 'UNKNOWN', 'result': 'UNKNOWN'}
            var_conf = IVariationConfig(obj)

            # Skip broken OrderedDict items
            var_dict = var_conf.getVariationDict()
            if str(type(var_dict)) == "<class 'zc.dict.dict.OrderedDict'>":
                status = "SKIPPED: Broken OrderedDict Item '%s' at '%s'" % (
                    obj.Title(), obj.absolute_url())
                response += status + "\n"
                print status
                stats[obj.UID()] = {'status': 'SKIPPED', 'result': 'SUCCESS'}
                continue

            varAttrs = var_conf.getVariationAttributes()
            num_variations = len(varAttrs)
            if num_variations == 0:
                # No migration needed
                stats[obj.UID()] = {
                    'status': 'NO_MIGRATION_NEEDED',
                    'result': 'SUCCESS'
                }
                continue

            # Migrate items with 2 variations
            if num_variations == 2:
                migrated = True

                # Create mapping from old to new keys
                mapping = {}
                for i, v1 in enumerate(var_conf.getVariation1Values()):
                    for j, v2 in enumerate(var_conf.getVariation2Values()):
                        vkey = "%s-%s" % (normalize(v1), normalize(v2))
                        vcode = "var-%s-%s" % (i, j)
                        mapping[vkey] = vcode

                # Check if item needs to be migrated
                for key in var_dict.keys():
                    if key in mapping.keys():
                        migrated = False

                if migrated:
                    # Already migrated
                    stats[obj.UID()] = {
                        'status': 'ALREADY_MIGRATED',
                        'result': 'SUCCESS'
                    }
                else:
                    # Migrate the item
                    print "Migrating %s..." % obj.Title()
                    for vkey in mapping.keys():
                        vcode = mapping[vkey]
                        try:
                            # Store data with new vcode
                            var_dict[vcode] = var_dict[vkey]
                            del var_dict[vkey]
                            var_conf.updateVariationConfig(var_dict)
                            transaction.commit()
                            stats[obj.UID()] = {
                                'status': 'MIGRATED',
                                'result': 'SUCCESS'
                            }
                        except KeyError:
                            status = "FAILED: Migration of item '%s' at '%s' failed!" % (
                                obj.Title(), obj.absolute_url())
                            response += status + "\n"
                            print status
                            stats[obj.UID()] = {
                                'status': 'FAILED',
                                'result': 'FAILED'
                            }
                            break
                    if stats[obj.UID()]['status'] == 'MIGRATED':
                        status = "MIGRATED: Item '%s' at '%s'" % (
                            obj.Title(), obj.absolute_url())
                        response += status + "\n"
                        print status

            # Migrate items with 1 variation
            if num_variations == 1:
                migrated = True

                # Create mapping from old to new keys
                mapping = {}
                for i, v1 in enumerate(var_conf.getVariation1Values()):
                    vkey = normalize(v1)
                    vcode = "var-%s" % i
                    mapping[vkey] = vcode

                # Check if item needs to be migrated
                for key in var_dict.keys():
                    if key in mapping.keys():
                        migrated = False

                if migrated:
                    # Already migrated
                    stats[obj.UID()] = {
                        'status': 'ALREADY_MIGRATED',
                        'result': 'SUCCESS'
                    }
                else:
                    # Migrate this item
                    print "Migrating %s..." % obj.Title()
                    for vkey in mapping.keys():
                        vcode = mapping[vkey]
                        try:
                            # Store data with new vcode
                            var_dict[vcode] = var_dict[vkey]
                            del var_dict[vkey]
                            var_conf.updateVariationConfig(var_dict)
                            transaction.commit()
                            stats[obj.UID()] = {
                                'status': 'MIGRATED',
                                'result': 'SUCCESS'
                            }
                        except KeyError:
                            status = "FAILED: Migration of item '%s' at '%s' failed!" % (
                                obj.Title(), obj.absolute_url())
                            response += status + "\n"
                            print status
                            stats[obj.UID()] = {
                                'status': 'FAILED',
                                'result': 'FAILED'
                            }
                            break
                    if stats[obj.UID()]['status'] == 'MIGRATED':
                        status = "MIGRATED: Item '%s' at '%s'" % (
                            obj.Title(), obj.absolute_url())
                        response += status + "\n"
                        print status

        total = len(items)
        migrated = len(
            [stats[k] for k in stats if stats[k]['status'] == 'MIGRATED'])
        skipped = len(
            [stats[k] for k in stats if stats[k]['status'] == 'SKIPPED'])
        failed = len(
            [stats[k] for k in stats if stats[k]['status'] == 'FAILED'])
        no_migration_needed = len([
            stats[k] for k in stats
            if stats[k]['status'] == 'NO_MIGRATION_NEEDED'
        ])
        already = len([
            stats[k] for k in stats if stats[k]['status'] == 'ALREADY_MIGRATED'
        ])

        summary = "TOTAL: %s   MIGRATED: %s   SKIPPED: %s   "\
                  "FAILED: %s   NO MIGRATION NEEDED: %s   ALREADY_MIGRATED: %s" % (total,
                                                                          migrated,
                                                                          skipped,
                                                                          failed,
                                                                          no_migration_needed,
                                                                          already)
        response = "%s\n\n%s" % (summary, response)
        return response
Example #7
0
    def __call__(self):
        """
        Self-submitting form that displays ShopItem Variations
        and updates them

        """
        form = self.request.form

        if form.get('remove_level'):
            variation_config = IVariationConfig(self.context)
            variation_config.remove_level()

        if form.get('reduce_level'):
            variation_config = IVariationConfig(self.context)
            variation_config.reduce_level()

        if form.get('add_level'):
            variation_config = IVariationConfig(self.context)
            variation_config.add_level()

        if form.get('addvalue'):
            fn = None
            idx_and_pos = form.get('addvalue')
            idx, pos = idx_and_pos.split('-')
            idx = int(idx)
            pos = int(pos) + 1

            if idx == 0:
                fn = 'variation1_values'
            elif idx == 1:
                fn = 'variation2_values'

            variation_config = IVariationConfig(self.context)
            values = list(self.context.getField(fn).get(self.context))
            var_dict = variation_config.getVariationDict()
            new_var_dict = {}
            pps = getMultiAdapter((self.context, self.request),
                                  name='plone_portal_state')
            language = pps.language()
            new_description = translate(_('label_new_description',
                                          default=u'New description'),
                                        domain='ftw.shop',
                                        context=self.context,
                                        target_language=language)
            DEFAULT_VARDATA = {
                'active': True,
                'price': '0.00',
                'skuCode': '99999',
                'description': new_description
            }

            if len(variation_config.getVariationAttributes()) == 2:
                values1 = list(
                    self.context.getField('variation1_values').get(
                        self.context))
                values2 = list(
                    self.context.getField('variation2_values').get(
                        self.context))

                # Create a dict mapping old combination indexes to the new ones
                code_map = {}
                if idx == 0:
                    for i in range(len(values1)):
                        for j in range(len(values2)):
                            old_vcode = "var-%s-%s" % (i, j)
                            if i >= pos:
                                new_vcode = "var-%s-%s" % (i + 1, j)
                                code_map[old_vcode] = new_vcode
                            else:
                                code_map[old_vcode] = old_vcode
                elif idx == 1:
                    for i in range(len(values1)):
                        for j in range(len(values2)):
                            old_vcode = "var-%s-%s" % (i, j)
                            if j >= pos:
                                new_vcode = "var-%s-%s" % (i, j + 1)
                                code_map[old_vcode] = new_vcode
                            else:
                                code_map[old_vcode] = old_vcode

                # Based on the code map, reorder the var_dict
                for old_vcode in code_map.keys():
                    new_vcode = code_map[old_vcode]
                    new_var_dict[new_vcode] = var_dict[old_vcode]

                # Now add some default variation data for the value just added
                if idx == 0:
                    for j in range(len(values2)):
                        vcode = "var-%s-%s" % (pos, j)
                        new_var_dict[vcode] = DEFAULT_VARDATA
                elif idx == 1:
                    for i in range(len(values1)):
                        vcode = "var-%s-%s" % (i, pos)
                        new_var_dict[vcode] = DEFAULT_VARDATA

            elif len(variation_config.getVariationAttributes()) == 1:
                assert (idx == 0)
                values1 = list(
                    self.context.getField('variation1_values').get(
                        self.context))

                # Create a dict mapping old combination indexes to the new ones
                code_map = {}
                for i in range(len(values1)):
                    old_vcode = "var-%s" % (i)
                    if i >= pos:
                        new_vcode = "var-%s" % (i + 1)
                        code_map[old_vcode] = new_vcode
                    else:
                        code_map[old_vcode] = old_vcode

                # Based on the code map, reorder the var_dict
                for old_vcode in code_map.keys():
                    new_vcode = code_map[old_vcode]
                    new_var_dict[new_vcode] = var_dict[old_vcode]

                # Now add some default variation data for the value just added
                vcode = "var-%s" % pos
                new_var_dict[vcode] = DEFAULT_VARDATA

            # Finally purge and update the var_dict
            variation_config.purge_dict()
            variation_config.updateVariationConfig(new_var_dict)
            pps = getMultiAdapter((self.context, self.request),
                                  name='plone_portal_state')
            language = pps.language()
            new_value = translate(_('label_new_value', default=u'New value'),
                                  domain='ftw.shop',
                                  context=self.context,
                                  target_language=language)
            values.insert(pos, new_value)
            self.context.getField(fn).set(self.context, values)

        if form.get('delvalue'):
            fn = None
            idx_and_pos = form.get('delvalue')
            idx, pos = idx_and_pos.split('-')
            idx = int(idx)
            pos = int(pos)

            if idx == 0:
                fn = 'variation1_values'
            elif idx == 1:
                fn = 'variation2_values'
            values = list(self.context.getField(fn).get(self.context))

            variation_config = IVariationConfig(self.context)
            var_dict = variation_config.getVariationDict()
            new_var_dict = {}

            if len(variation_config.getVariationAttributes()) == 2:
                values1 = list(
                    self.context.getField('variation1_values').get(
                        self.context))
                values2 = list(
                    self.context.getField('variation2_values').get(
                        self.context))

                # Create a dict mapping old combination indexes to the new ones
                code_map = {}
                if idx == 0:
                    for i in range(len(values1)):
                        for j in range(len(values2)):
                            old_vcode = "var-%s-%s" % (i, j)
                            if i > pos:
                                new_vcode = "var-%s-%s" % (i - 1, j)
                                code_map[old_vcode] = new_vcode
                            elif i == pos:
                                code_map[old_vcode] = None
                            else:
                                code_map[old_vcode] = old_vcode
                elif idx == 1:
                    for i in range(len(values1)):
                        for j in range(len(values2)):
                            old_vcode = "var-%s-%s" % (i, j)
                            if j > pos:
                                new_vcode = "var-%s-%s" % (i, j - 1)
                                code_map[old_vcode] = new_vcode
                            elif j == pos:
                                code_map[old_vcode] = None
                            else:
                                code_map[old_vcode] = old_vcode

                # Based on the code map, reorder the var_dict
                for old_vcode in code_map.keys():
                    new_vcode = code_map[old_vcode]
                    new_var_dict[new_vcode] = var_dict[old_vcode]

            elif len(variation_config.getVariationAttributes()) == 1:
                assert (idx == 0)
                values1 = list(
                    self.context.getField('variation1_values').get(
                        self.context))

                # Create a dict mapping old combination indexes to the new ones
                code_map = {}
                for i in range(len(values1)):
                    old_vcode = "var-%s" % (i)
                    if i >= pos:
                        new_vcode = "var-%s" % (i - 1)
                        code_map[old_vcode] = new_vcode
                    else:
                        code_map[old_vcode] = old_vcode

                # Based on the code map, reorder the var_dict
                for old_vcode in code_map.keys():
                    new_vcode = code_map[old_vcode]
                    new_var_dict[new_vcode] = var_dict[old_vcode]

            # # Finally purge and update the var_dict
            variation_config.purge_dict()
            variation_config.updateVariationConfig(new_var_dict)

            values.pop(int(pos))
            self.context.getField(fn).set(self.context, values)

        if form.get('update_structure'):
            var_config = IVariationConfig(self.context)
            vattr0 = form.get('vattr-0', None)
            vattr1 = form.get('vattr-1', None)

            if len(var_config.getVariationAttributes()) >= 1:
                self.context.getField('variation1_attribute').set(
                    self.context, vattr0)
                new_values = []
                for i in range(len(var_config.getVariation1Values())):
                    new_value = form.get('vvalue-%s-%s' % (0, i))
                    new_values.append(new_value)
                self.context.getField('variation1_values').set(
                    self.context, new_values)

            if len(var_config.getVariationAttributes()) >= 2:
                self.context.getField('variation2_attribute').set(
                    self.context, vattr1)
                new_values = []
                for j in range(len(var_config.getVariation2Values())):
                    new_value = form.get('vvalue-%s-%s' % (1, j))
                    new_values.append(new_value)
                self.context.getField('variation2_values').set(
                    self.context, new_values)

        # Make sure we had a proper form submit, not just a GET request
        submitted = form.get('form.submitted', False)
        if submitted:
            variation_config = IVariationConfig(self.context)
            edited_var_data = self._parse_edit_variations_form()
            variation_config.updateVariationConfig(edited_var_data)

            IStatusMessage(self.request).addStatusMessage(_(
                u'msg_variations_saved', default=u"Variations saved."),
                                                          type="info")
            self.request.RESPONSE.redirect(self.context.absolute_url())

        return self.template()
Example #8
0
    def afterSetUp(self):
        provideAdapter(FakeMailHostAdapter,
                 (Interface, ),
                 IMailHostAdapter)

        # Set up sessioning objects
        ztc.utils.setupCoreSessions(self.app)

        # Create an initial browser_id by requesting it
        bid_manager = getToolByName(self.app, 'browser_id_manager')
        bid_manager.getBrowserId()

        self.workflow = getToolByName(self.portal, 'portal_workflow')
        self.acl_users = getToolByName(self.portal, 'acl_users')
        self.types = getToolByName(self.portal, 'portal_types')

        self.setRoles(('Manager',))

        # Use helper method to set up shop root at portal.shop
        init_shop = getMultiAdapter((self.portal, self.portal.REQUEST),
                                    Interface, 'initialize-shop-structure')
        init_shop()

        # Create a shop category
        self.portal.shop.invokeFactory("ShopCategory", "products")
        self.portal.shop.products.reindexObject()

        # Create a Shop Item with no variations
        self.portal.shop.products.invokeFactory('ShopItem', 'movie')
        self.movie = self.portal.shop.products['movie']
        self.movie.getField('skuCode').set(self.movie, "12345")
        self.movie.getField('price').set(self.movie, "7.15")
        self.movie.getField('showPrice').set(self.movie, True)
        self.movie.getField('title').set(self.movie, "A Movie")
        self.movie.getField('description').set(self.movie, "A Shop Item with no variations")
        self.movie.getField('selectable_dimensions').set(self.movie, 'length_width_mm_mm2')

        # Fire ObjectInitializedEvent to add item to containing category
        event = ObjectInitializedEvent(self.movie, self.portal.REQUEST)
        zope.event.notify(event)

        self.movie.reindexObject()

        self.movie_vc = IVariationConfig(self.movie)

        # Create a Shop Item with one variation
        self.portal.shop.products.invokeFactory('ShopItem', 'book')
        self.book = self.portal.shop.products['book']
        self.book.getField('title').set(self.book, 'Professional Plone Development')
        self.book.getField('description').set(self.book, 'A Shop Item with one variation')
        self.book.getField('variation1_attribute').set(self.book, 'Cover')
        self.book.getField('variation1_values').set(self.book, ['Hardcover', 'Paperback'])

        # Fire ObjectInitializedEvent to add item to containing category
        event = ObjectInitializedEvent(self.book, self.portal.REQUEST)
        zope.event.notify(event)

        self.book.reindexObject()

        self.book_vc = IVariationConfig(self.book)
        book_var_dict = {
        'var-Hardcover': {'active': True,
                          'price': Decimal('1.00'),
                          'skuCode': 'b11',
                          'description': 'A hard and durable cover',
                          'hasUniqueSKU': True},
        'var-Paperback': {'active': True,
                          'price': Decimal('2.00'),
                          'skuCode': 'b22',
                          'description': 'A less durable but cheaper cover',
                          'hasUniqueSKU': True},
        }
        self.book_vc.updateVariationConfig(book_var_dict)

        # Create a Shop Item with two variations
        self.portal.shop.products.invokeFactory('ShopItem', 't-shirt')
        self.tshirt = self.portal.shop.products['t-shirt']
        self.tshirt.getField('title').set(self.tshirt, 'A T-Shirt')
        self.tshirt.getField('description').set(self.tshirt, 'A Shop Item with two variations')
        self.tshirt.getField('variation1_attribute').set(self.tshirt, 'Color')
        self.tshirt.getField('variation1_values').set(self.tshirt, ['Red', 'Green', 'Blue'])
        self.tshirt.getField('variation2_attribute').set(self.tshirt, 'Size')
        self.tshirt.getField('variation2_values').set(self.tshirt, ['S', 'M', 'L'])

        # Fire ObjectInitializedEvent to add item to containing category
        event = ObjectInitializedEvent(self.tshirt, self.portal.REQUEST)
        zope.event.notify(event)

        self.tshirt.reindexObject()

        self.tshirt_vc = IVariationConfig(self.tshirt)
        tshirt_var_dict = {
        'var-Red-S': {'active': True, 'price': Decimal('1.00'), 'skuCode': '11', 'description': '', 'hasUniqueSKU': True},
        'var-Red-M': {'active': True, 'price': Decimal('2.00'), 'skuCode': '22', 'description': '', 'hasUniqueSKU': True},
        'var-Red-L': {'active': True, 'price': Decimal('3.00'), 'skuCode': '33', 'description': '', 'hasUniqueSKU': True},
        'var-Green-S': {'active': True, 'price': Decimal('4.00'), 'skuCode': '44', 'description': '', 'hasUniqueSKU': True},
        'var-Green-M': {'active': True, 'price': Decimal('5.00'), 'skuCode': '55', 'description': '', 'hasUniqueSKU': True},
        'var-Green-L': {'active': True, 'price': Decimal('6.00'), 'skuCode': '66', 'description': '', 'hasUniqueSKU': True},
        'var-Blue-S': {'active': True, 'price': Decimal('7.00'), 'skuCode': '77', 'description': '', 'hasUniqueSKU': True},
        'var-Blue-M': {'active': True, 'price': Decimal('8.00'), 'skuCode': '88', 'description': '', 'hasUniqueSKU': True},
        'var-Blue-L': {'active': True, 'price': Decimal('9.00'), 'skuCode': '99', 'description': '', 'hasUniqueSKU': True},
        }
        self.tshirt_vc.updateVariationConfig(tshirt_var_dict)

        # Create a subcategory below the shop root
        self.portal.shop.products.invokeFactory("ShopCategory", "subcategory")
        self.subcategory = self.portal.shop.products.subcategory

        # Fire ObjectInitializedEvent to add category to containing category
        event = ObjectInitializedEvent(self.subcategory, self.portal.REQUEST)
        zope.event.notify(event)

        self.setRoles(('Member',))