Presentation of Dr. Nikolai Krambrock at Developers ParadisePresentation of Dr. Nikolai Krambrock at Developers Paradise 2014

Magento allows tier prices for products and prices for individual options. However, tier prices for individual options are not supported. Tier prices for individual options are needed when selling promotional items or textiles with custom imprints. As an example, these are prices for pens with custom printings or engravings:

Price from050100
€/item0,45 €0,39 €0,37 €
+ Laser engraving5,00 €0,19 €0,15 €
+ Screen printing4,50 €0,17 €0,13 €
+ Pad printing4,00 €0,16 €0,12 €

The price for the pen is € 0.45, € 0.39, € 0.37, € 0.35 or € 0.27, depending on the quantity ordered. The cost of the imprint should also be dependent on the quantity as well. This is not doable with Magento standard.

Ways of implementing tier prices for custom options

I went through three different options realizing tier prices for custom options. The last option is the best, from my point of view. I will describe it in detail later, focussing on the way we solved it in a customer project. I tried the following options:

OptionAdvantagesDisadvantages
Realize the surcharges with Cart Price RulesNo changes to the system (except maybe observers in the backend)The discounts are only displayed in checkout in one sum
Simple Configurable Products / Better Configurable ProductsGood presentation and usage of a ready-to-use module meeting the requirementsFor each attribute configuration a simple product must be created; deep impact on the standard system through many rewrites
Generating additional individual options with pricesGood presentation and only little intervention in the base systemBackend contains many additional custom options – this is inconvenient for manual maintenance

Displaying the surcharges on basket price rules

Realizing surcharges with basket price rules is the most elegant option if the products are imported with an import interface from an erp-system. In this case you can generate basket price rules, during or at the end of the import, which deliver discounts on each position. However, there is a major drawback: The discounts on the products are shown as one sum in the checkout. Therefore this option will only be useful in rare cases.

Simple Configurable Product / Better Configurable Products

The simplest solution from the developer’s perspective is the use of Simple Configurable Product (http://www.magentocommerce.com/magento-connect/simple-configurable-products.html) or Better Configurable Products (http://www.magentocommerce.com/magento-connect/better-configurable-products.html). I have tested Better Configurable Products only, as this was available for Magento 1.7 at the time of development. For each variant of the product, for example laser engraving, screen printing and pad printing, an additional simple product is added. Better Configurable Product makes sure that the tier price is taken from the simple product. However, with many products and many variants, it will quickly become confusing. If you can choose between small, medium and big printing on your pen, as well as having red, green and blue pens, the result will include 27 simple products, instead of three. All these simple products must be assigned to a configurable product. Furthermore, all 27 products have their own stock. Additionally both of the extensions use numerous rewrites that change the very core of the Magento system.

Generating additional individual options with prices

Having checked the options above, we decided to realize this option in a customer project. The basic idea is simple: An individual option only allows one surcharge, when 5 are needed- one for each tier price. So I add 5 individual options with different prices.  The advantage of using this solution are moderate changes to the system while displaying the prices for the products in a reasonable way. The solution works with two observers – and additional rewrites of blocks for better visual appearance. Once passed the checkout, Magento just works with standard custom options. Therefore, it is very unlikely to experience problems in the later steps – e.g. invoice, shipment, credit memo and export to an erp-system. The disadvantages are additional options in the backend that are somewhat disturbing when maintaining the products by hand.

Submitting tier prices for individual options

The module we developed requires entering of tier prices and individual options, in the backend. Finally, the prices of the individual options for each tier price are entered in the newly generated options. We start by creating the tier price for one product:Submit tier prices for individual optionsAs the second step, we set the Printing type as an individual option:Tier prices for custom optionsWhen you save it, the following table of individual options will be generated. In this table, the tier prices can be entered for the original individual options:Set the tier prices for the custom optionsUnder the title “preis-lasergravur-100”, the price for Lasergravur with a minimum order of 100 pens is set. The rest of the generated fields must be set as well. When all these fields have been filled in, the result is expected to be in the checkout. For example, 120 ordered pens with Lasergravur will have a price of 0,37€ + 0,15€ for a total of 0.52€ per piece:cart-tier-price-magento

Development of Tier Prices for custom options

The main work in the module is performed by two observers. The first observer generates the individual options in the backend. The second observer chooses the correct option for changes in your shopping cart. The simplified config.xml looks like this:

<config>
  <modules>
    <C4B_Configurableprices>
      <version>0.0.1</version>
    </C4B_Configurableprices>
  </modules>
  <global>
    <events>
      <catalog_product_prepare_save>
        <observers>
          <c4b_configurableprices_catalog_product_observer>
            <type>singleton</type>
            <class>C4B_Configurableprices_Model_Catalog_Product_Option</class>
            <method>updateProductCustomOptions</method>
          </c4b_configurableprices_catalog_product_observer>
        </observers>
      </catalog_product_prepare_save>
      <checkout_cart_save_before>
        <observers>
          <c4b_configurableprices_cart_observer>
            <type>singleton</type>
            <class>C4B_Configurableprices_Model_Checkout_Observer</class>
            <method>applyConfigurablePrices</method>
          </c4b_configurableprices_cart_observer>
        </observers>
      </checkout_cart_save_before>
    </events>
  </global>
</config>

Observer related to saving the products in the backend

When a product is saved in the backend, the method “updateProductCustomOptions” of the class C4B_Configurableprices_Model_Catalog_Product_Option is executed. This ensures that the additional individual options are created. When the additional individual options have already been created, they will not be overwritten – the already entered prices of these additional individual options should not be removed. This is the simplified method:

  public function updateProductCustomOptions($observer) {
    /* @var $product Mage_Catalog_Model_Product */
    $product = $observer['product'];

    $newOptionList = $this->_getNewOptionsList($product);

    foreach($product->getOptions() as $option) {
      foreach($option->getValues() as $value) {
        $key = array_search(strtolower(trim($value->getSku())), $newOptionList);
        if($key === false) {
          $this->_deleteOptionValue($product, $option, $value->getSku());
        } else {
          unset($newOptionList[$key]);
        }
      }
    }

    foreach($newOptionList as $newOptionValue) {
      $this->addNewOptionValue($product, 0, $newOptionValue);
    }
  }

First, the _getNewOptionsList method determines all values ​​of the generated individual options that a product should have, for example preis-lasergravur-100, and saves them in the array newOptionList. After that, the method goes through all the options and their values ​​for the current product. No need for more options to be deleted, when they are deleted from the existing newOptionList. The remaining options are created in connection with the addNewOptionValue method.

Observer related to the changes in the basket of goods

The method applyConfigurablePrices of the class C4B_Configurableprices_Model_Checkout_Observer is called before the cart is saved. For every original custom option the method selects the generated custom option that belongs to the right tier. This is the simplified method:

  public function applyConfigurablePrices($observer) {
    $items = Mage::getSingleton('checkout/session')->getQuote()->getAllItems();

    foreach($items as $item) {

      $itemQty  = $this->_getQuantityBoundary($item);
      $options_ids = $this->_getNotGeneratedCustomOptions($item);

      foreach ($options_ids as $optionId) {
        $valueId = $item->getOptionByCode('option_' . $optionId)->getValue();
        $productOption = $item->getProduct()->getOptionById($optionId);

        $optionTitle = $productOption->getTitle();
        $valueTitle = $productOption->getValueById($valueId)->getTitle();
        $option =  $this->_getOptionByTitle($item->getProduct(), self::PREFIX . $optionTitle);

        $valueSku =  self::PREFIX . $optionTitle . "-" . $valueTitle . "-" . $itemQty;
        $this->_addOrUpdateOptionValue($item, 'option_' . $option->getId(),
                                       $this->_getOptionValueIdBySku($option,$valueSku));
        $options_ids .= strlen($options_ids) == 0 ? $option->getId() : "," . $option->getId();
      }

      $this->_addOrUpdateOptionValue($item, 'option_ids', $options_ids);
    }
  }

In this method, all positions of the basket are being processed. The method _getQuantityBoundary returns the quantity limit, e.g. 100, when 120 items are in the cart. The method ​​_getNotGeneratedCustomOptions returns the original custom options selected by the customer. „laser engraving“ is an example for an original custom option, while „award-laser-engraved-100“, on the other hand, would not be in the list. The method gets the original option for every line in the cart and constructs the name of the generated option (e.g. „award-laser-engraved-100“). This generated custom option is selected and leads to the surcharge.

Conclusion

Tier prices for custom options are surprisingly complicated to create in Magento. On top of that, there are options that look fine in the first place, but do not work at the end of the day. The above presented approach was implemented for a customer and has been put into practice, working fine. It combines relatively little changes to the Magento system with a good visual appearance. If you want our module as a sample for your own development, please request a copy at https://www.code4business.de/kontakt-impressum/ free of charge. Be advised, however, that implementing the module for your project will require in-depth Magento and programming knowledge. I am looking forward for discussion, suggestions and other modules that you propose.

File for Download

Get the presentation of Dr. Nikolai Krambrock at Developers Paradise on 13.1.2014 in Kaprun here