eZ Publish  [trunk]
ezshopoperationcollection.php
Go to the documentation of this file.
00001 <?php
00002 /**
00003  * File containing the eZShopOperationCollection class.
00004  *
00005  * @copyright Copyright (C) 1999-2012 eZ Systems AS. All rights reserved.
00006  * @license http://www.gnu.org/licenses/gpl-2.0.txt GNU General Public License v2
00007  * @version //autogentag//
00008  * @package kernel
00009  */
00010 
00011 /*!
00012   \class eZShopOperationCollection ezcontentoperationcollection.php
00013   \brief The class eZShopOperationCollection does
00014 
00015 */
00016 class eZShopOperationCollection
00017 {
00018     /*!
00019      Constructor
00020     */
00021     function eZShopOperationCollection()
00022     {
00023     }
00024 
00025     function fetchOrder( $orderID )
00026     {
00027         return array( 'status' => eZModuleOperationInfo::STATUS_CONTINUE );
00028     }
00029 
00030     /*!
00031      Operation entry: Extracts user country from order account info and recalculates VAT with country given.
00032      */
00033     function handleUserCountry( $orderID )
00034     {
00035         // If user country is not required to calculate VAT then do nothing.
00036         if ( !eZVATManager::isDynamicVatChargingEnabled() || !eZVATManager::isUserCountryRequired() )
00037             return array( 'status' => eZModuleOperationInfo::STATUS_CONTINUE );
00038 
00039         $user = eZUser::currentUser();
00040 
00041         // Get order's account information and extract user country from it.
00042         $order = eZOrder::fetch( $orderID );
00043 
00044         if ( !$order )
00045         {
00046             eZDebug::writeError( "No such order: $orderID" );
00047             return array( 'status' => eZModuleOperationInfo::STATUS_CANCELLED );
00048         }
00049 
00050         if ( $user->attribute( 'is_logged_in' ) )
00051             $userCountry = eZVATManager::getUserCountry( $user, false );
00052 
00053         $acctInfo = $order->attribute( 'account_information' );
00054         if ( isset( $acctInfo['country'] ) )
00055         {
00056             $country = $acctInfo['country'];
00057 
00058             // If user is registered and logged in
00059             // and country is not yet specified for the user
00060             // then save entered country to the user information.
00061             if ( !isset( $userCountry ) || !$userCountry )
00062                 eZVATManager::setUserCountry( $user, $country );
00063         }
00064         elseif ( isset( $userCountry ) && $userCountry )
00065         {
00066             // If country is not set in shop account handler, we get it from logged user's information.
00067             $country = $userCountry;
00068         }
00069         else
00070         {
00071             $header = ezpI18n::tr( 'kernel/shop', 'Error checking out' );
00072             $msg = ezpI18n::tr( 'kernel/shop',
00073                            'Unable to calculate VAT percentage because your country is unknown. ' .
00074                            'You can either fill country manually in your account information (if you are a registered user) ' .
00075                            'or contact site administrator.' );
00076 
00077             $tpl = eZTemplate::factory();
00078             $tpl->setVariable( "error_header",  $header );
00079             $tpl->setVariable( "error_list", array( $msg ) );
00080 
00081             $operationResult = array(
00082                 'status' => eZModuleOperationInfo::STATUS_CANCELLED,
00083                 'result' => array( 'content' => $tpl->fetch( "design:shop/cancelconfirmorder.tpl" ) )
00084                 );
00085             return $operationResult;
00086         }
00087 
00088         // Recalculate VAT for order's product collection items
00089         // according to the specified user country.
00090 
00091         $productCollection = $order->attribute( 'productcollection' );
00092         if ( !$productCollection )
00093         {
00094             eZDebug::writeError( "Cannot find product collection for order " . $order->attribute( 'id' ),
00095                                  "eZShopOperationCollection::handleUserCountry" );
00096             return array( 'status' => eZModuleOperationInfo::STATUS_CONTINUE );
00097         }
00098 
00099         $items = eZProductCollectionItem::fetchList( array( 'productcollection_id' => $productCollection->attribute( 'id' ) ) );
00100         $vatIsKnown = true;
00101         $db = eZDB::instance();
00102         $db->begin();
00103         foreach( $items as $item )
00104         {
00105             $productContentObject = $item->attribute( 'contentobject' );
00106 
00107             // Look up price object.
00108             $priceObj = null;
00109             $attributes =  $productContentObject->contentObjectAttributes();
00110             foreach ( $attributes as $attribute )
00111             {
00112                 $dataType = $attribute->dataType();
00113                 if ( eZShopFunctions::isProductDatatype( $dataType->isA() ) )
00114                 {
00115                     $priceObj = $attribute->content();
00116                     break;
00117                 }
00118             }
00119             if ( !is_object( $priceObj ) )
00120                 continue;
00121 
00122             // If the product is assigned a fixed VAT type then skip the product.
00123             $vatType = $priceObj->VATType();
00124             if ( !$vatType->attribute( 'is_dynamic' ) )
00125                 continue;
00126 
00127             // Update item's VAT percentage.
00128             $vatValue = $priceObj->VATPercent( $productContentObject, $country );
00129             eZDebug::writeNotice( "Updating product item collection item ('" .
00130                                   $productContentObject->attribute( 'name' ) . "'): " .
00131                                   "setting VAT $vatValue% according to order's country '$country'." );
00132             $item->setAttribute( "vat_value", $vatValue );
00133 
00134             $item->store();
00135         }
00136         $db->commit();
00137 
00138         return array( 'status' => eZModuleOperationInfo::STATUS_CONTINUE );
00139     }
00140 
00141     /*!
00142      Operation entry: Adds order item: shipping.
00143      \params $orderID contains the order id for the shipping handler.
00144 
00145      The function handleShipping() are runn in the process of confirmorder and
00146      is the final function for creating an order_item in the order confirmation.
00147 
00148      An example for an array that should be returned by the function
00149      eZShippingManager::getShippingInfo( $productCollectionID ):
00150      \code
00151      array( 'shipping_items' => array( array( 'description' => 'Shipping vat: 12%',
00152                                               'cost'        => 50.25,
00153                                               'vat_value'   => 12,
00154                                               'is_vat_inc'  => 0 ),
00155                                        array( 'description' => 'Shipping vat: 25%',
00156                                               'cost'        => 100.75,
00157                                               'vat_value'   => 25,
00158                                               'is_vat_inc'  => 0 ) ),
00159             'description' => 'Total Shipping',
00160             'cost'        => 182.22,
00161             'vat_value'   => false,
00162             'is_vat_inc'  => 1 );
00163      \endcode
00164 
00165      An example for the shippingvalues with only one shippingitem, old standard.
00166      \code
00167      array( 'description' => 'Total Shipping vat: 16%',
00168             'cost'        => 10.25,
00169             'vat_value'   => 16,
00170             'is_vat_inc'  => 1 );
00171      \endcode
00172 
00173      The returned array for each shipping item should consist of these keys:
00174      - order_id - The order id for the current order.
00175      - description - An own description of the shipping item.
00176      - cost - A float value of the cost for the shipping.
00177      - vat_value - The vat value that should be added to the shipping item.
00178      - is_vat_inc - Either 0, 1 or false. 0: The cost is excluded VAT.
00179                                           1: the cost is included VAT.
00180                                       false: The cost is combined by several other VAT prices.
00181 
00182      This function may also send additional parameters to be used in other templates, like
00183      in the basket.
00184     */
00185     function handleShipping( $orderID )
00186     {
00187         do // we prevent high nesting levels by using breaks
00188         {
00189             $order = eZOrder::fetch( $orderID );
00190             if ( !$order )
00191                 break;
00192             $productCollectionID = $order->attribute( 'productcollection_id' );
00193 
00194             $shippingInfo = eZShippingManager::getShippingInfo( $productCollectionID );
00195             if ( !isset( $shippingInfo ) )
00196                 break;
00197 
00198             // check if the order item has been added before.
00199             $orderItems = $order->orderItemsByType( 'ezcustomshipping' );
00200 
00201             // If orderitems allready exists, remove them first.
00202             if ( $orderItems )
00203             {
00204                 foreach ( $orderItems as $orderItem )
00205                 {
00206                     $orderItem->remove();
00207                 }
00208                 $purgeStatus = eZShippingManager::purgeShippingInfo( $productCollectionID );
00209             }
00210 
00211             if ( isset( $shippingInfo['shipping_items'] ) and
00212                  is_array( $shippingInfo['shipping_items'] ) )
00213             {
00214                 // Add a new order item for each shipping.
00215                 foreach ( $shippingInfo['shipping_items'] as $orderItemShippingInfo )
00216                 {
00217                     $orderItem = new eZOrderItem( array( 'order_id' => $orderID,
00218                                                          'description' => $orderItemShippingInfo['description'],
00219                                                          'price' => $orderItemShippingInfo['cost'],
00220                                                          'vat_value' => $orderItemShippingInfo['vat_value'],
00221                                                          'is_vat_inc' => $orderItemShippingInfo['is_vat_inc'],
00222                                                          'type' => 'ezcustomshipping' ) );
00223                     $orderItem->store();
00224                 }
00225             }
00226             else
00227             {
00228                 // Made for backwards compability, if the array order_items are not supplied.
00229                 if ( !isset( $shippingInfo['vat_value'] ) )
00230                 {
00231                     $shippingInfo['vat_value'] = 0;
00232                 }
00233 
00234                 if ( !isset( $shippingInfo['is_vat_inc'] ) )
00235                 {
00236                     $shippingInfo['is_vat_inc'] = 1;
00237                 }
00238 
00239                 $orderItem = new eZOrderItem( array( 'order_id' => $orderID,
00240                                                      'description' => $shippingInfo['description'],
00241                                                      'price' => $shippingInfo['cost'],
00242                                                      'vat' => $shippingInfo['vat_value'],
00243                                                      'is_vat_inc' => $shippingInfo['is_vat_inc'],
00244                                                      'type' => 'ezcustomshipping' ) );
00245                 $orderItem->store();
00246             }
00247 
00248         } while ( false );
00249 
00250         return array( 'status' => eZModuleOperationInfo::STATUS_CONTINUE );
00251     }
00252 
00253     /*!
00254      Operation entry: Updates shipping info for items in the current basket.
00255     */
00256     function updateShippingInfo( $objectID, $optionList )
00257     {
00258         $basket = eZBasket::currentBasket();
00259         $shippingInfo = eZShippingManager::updateShippingInfo( $basket->attribute( 'productcollection_id' ) );
00260         return array( 'status' => eZModuleOperationInfo::STATUS_CONTINUE );
00261     }
00262 
00263     function updateBasket( $itemCountList, $itemIDList )
00264     {
00265         if ( is_array( $itemCountList ) && is_array( $itemIDList ) && count( $itemCountList ) == count ( $itemIDList ) )
00266         {
00267             $basket = eZBasket::currentBasket();
00268             if ( is_object( $basket ) )
00269             {
00270                 $productCollectionID = $basket->attribute( 'productcollection_id' );
00271 
00272                 $i = 0;
00273                 foreach ( $itemIDList as $id )
00274                 {
00275                     $item = eZProductCollectionItem::fetch( $id );
00276                     if ( is_object( $item ) && $item->attribute( 'productcollection_id' ) == $productCollectionID )
00277                     {
00278                         if ( is_numeric( $itemCountList[$i] ) and $itemCountList[$i] == 0 )
00279                         {
00280                             $item->remove();
00281                         }
00282                         else
00283                         {
00284                             $item->setAttribute( 'item_count', $itemCountList[$i] );
00285                             $item->store();
00286                         }
00287                     }
00288                     ++$i;
00289                 }
00290             }
00291         }
00292     }
00293 
00294     /*!
00295      Operation entry: Adds the object \a $objectID with options \a $optionList to the current basket.
00296     */
00297     function addToBasket( $objectID, $optionList, $quantity )
00298     {
00299         $object = eZContentObject::fetch( $objectID );
00300         $nodeID = $object->attribute( 'main_node_id' );
00301         $price = 0.0;
00302         $isVATIncluded = true;
00303         $attributes = $object->contentObjectAttributes();
00304 
00305         $priceFound = false;
00306 
00307         foreach ( $attributes as $attribute )
00308         {
00309             $dataType = $attribute->dataType();
00310             if ( eZShopFunctions::isProductDatatype( $dataType->isA() ) )
00311             {
00312                 $priceObj = $attribute->content();
00313                 $price += $priceObj->attribute( 'price' );
00314                 $priceFound = true;
00315             }
00316         }
00317 
00318         if ( !$priceFound )
00319         {
00320             eZDebug::writeError( 'Attempted to add object without price to basket.' );
00321             return array( 'status' => eZModuleOperationInfo::STATUS_CANCELLED );
00322         }
00323 
00324         $currency = $priceObj->attribute( 'currency' );
00325 
00326         // Check for 'option sets' in option list.
00327         // If found each 'option set' will be added as a separate product purchase.
00328         $hasOptionSet = false;
00329         foreach ( array_keys( $optionList ) as $optionKey )
00330         {
00331             if ( substr( $optionKey, 0, 4 ) == 'set_' )
00332             {
00333                 $returnStatus = eZShopOperationCollection::addToBasket( $objectID, $optionList[$optionKey] );
00334                 // If adding one 'option set' fails we should stop immediately
00335                 if ( $returnStatus['status'] == eZModuleOperationInfo::STATUS_CANCELLED )
00336                     return $returnStatus;
00337                 $hasOptionSet = true;
00338             }
00339         }
00340         if ( $hasOptionSet )
00341             return $returnStatus;
00342 
00343 
00344         $unvalidatedAttributes = array();
00345         foreach ( $attributes as $attribute )
00346         {
00347             $dataType = $attribute->dataType();
00348 
00349             if ( $dataType->isAddToBasketValidationRequired() )
00350             {
00351                 $errors = array();
00352                 if ( $attribute->validateAddToBasket( $optionList[$attribute->attribute('id')], $errors ) !== eZInputValidator::STATE_ACCEPTED )
00353                 {
00354                     $description = $errors;
00355                     $contentClassAttribute = $attribute->contentClassAttribute();
00356                     $attributeName = $contentClassAttribute->attribute( 'name' );
00357                     $unvalidatedAttributes[] = array( "name" => $attributeName,
00358                                                       "description" => $description );
00359                 }
00360             }
00361         }
00362         if ( count( $unvalidatedAttributes ) > 0 )
00363         {
00364             return array( 'status' => eZModuleOperationInfo::STATUS_CANCELLED,
00365                           'reason' => 'validation',
00366                           'error_data' => $unvalidatedAttributes );
00367         }
00368 
00369         $basket = eZBasket::currentBasket();
00370 
00371         /* Check if the item with the same options is not already in the basket: */
00372         $itemID = false;
00373         $collection = $basket->attribute( 'productcollection' );
00374         if ( !$collection )
00375         {
00376             eZDebug::writeError( 'Unable to find product collection.' );
00377             return array( 'status' => eZModuleOperationInfo::STATUS_CANCELLED );
00378         }
00379         else
00380         {
00381             $collection->setAttribute( 'currency_code', $currency );
00382             $collection->store();
00383 
00384             $count = 0;
00385             /* Calculate number of options passed via the HTTP variable: */
00386             foreach ( array_keys( $optionList ) as $key )
00387             {
00388                 if ( is_array( $optionList[$key] ) )
00389                     $count += count( $optionList[$key] );
00390                 else
00391                     $count++;
00392             }
00393             $collectionItems = $collection->itemList( false );
00394             foreach ( $collectionItems as $item )
00395             {
00396                 /* For all items in the basket which have the same object_id: */
00397                 if ( $item['contentobject_id'] == $objectID )
00398                 {
00399                     $options = eZProductCollectionItemOption::fetchList( $item['id'], false );
00400                     /* If the number of option for this item is not the same as in the HTTP variable: */
00401                     if ( count( $options ) != $count )
00402                     {
00403                         break;
00404                     }
00405                     $theSame = true;
00406                     foreach ( $options as $option )
00407                     {
00408                         /* If any option differs, go away: */
00409                         if ( ( is_array( $optionList[$option['object_attribute_id']] ) &&
00410                                !in_array( $option['option_item_id'], $optionList[$option['object_attribute_id']] ) )
00411                              || ( !is_array( $optionList[$option['object_attribute_id']] ) &&
00412                                   $option['option_item_id'] != $optionList[$option['object_attribute_id']] ) )
00413                         {
00414                             $theSame = false;
00415                             break;
00416                         }
00417                     }
00418                     if ( $theSame )
00419                     {
00420                         $itemID = $item['id'];
00421                         break;
00422                     }
00423                 }
00424             }
00425 
00426             if ( $itemID )
00427             {
00428                 /* If found in the basket, just increment number of that items: */
00429                 $item = eZProductCollectionItem::fetch( $itemID );
00430                 $item->setAttribute( 'item_count', $quantity + $item->attribute( 'item_count' ) );
00431                 $item->store();
00432             }
00433             else
00434             {
00435                 $item = eZProductCollectionItem::create( $basket->attribute( "productcollection_id" ) );
00436 
00437                 $item->setAttribute( 'name', $object->attribute( 'name' ) );
00438                 $item->setAttribute( "contentobject_id", $objectID );
00439                 $item->setAttribute( "item_count", $quantity );
00440                 $item->setAttribute( "price", $price );
00441                 if ( $priceObj->attribute( 'is_vat_included' ) )
00442                 {
00443                     $item->setAttribute( "is_vat_inc", '1' );
00444                 }
00445                 else
00446                 {
00447                     $item->setAttribute( "is_vat_inc", '0' );
00448                 }
00449                 $item->setAttribute( "vat_value", $priceObj->attribute( 'vat_percent' ) );
00450                 $item->setAttribute( "discount", $priceObj->attribute( 'discount_percent' ) );
00451                 $item->store();
00452                 $priceWithoutOptions = $price;
00453 
00454                 $optionIDList = array();
00455                 foreach ( array_keys( $optionList ) as $key )
00456                 {
00457                     $attributeID = $key;
00458                     $optionString = $optionList[$key];
00459                     if ( is_array( $optionString ) )
00460                     {
00461                         foreach ( $optionString as $optionID )
00462                         {
00463                             $optionIDList[] = array( 'attribute_id' => $attributeID,
00464                                                      'option_string' => $optionID );
00465                         }
00466                     }
00467                     else
00468                     {
00469                         $optionIDList[] = array( 'attribute_id' => $attributeID,
00470                                                  'option_string' => $optionString );
00471                     }
00472                 }
00473 
00474                 $db = eZDB::instance();
00475                 $db->begin();
00476                 foreach ( $optionIDList as $optionIDItem )
00477                 {
00478                     $attributeID = $optionIDItem['attribute_id'];
00479                     $optionString = $optionIDItem['option_string'];
00480 
00481                     $attribute = eZContentObjectAttribute::fetch( $attributeID, $object->attribute( 'current_version' ) );
00482                     $dataType = $attribute->dataType();
00483                     $optionData = $dataType->productOptionInformation( $attribute, $optionString, $item );
00484                     if ( $optionData )
00485                     {
00486                         $optionData['additional_price'] = eZShopFunctions::convertAdditionalPrice( $currency, $optionData['additional_price'] );
00487                         $optionItem = eZProductCollectionItemOption::create( $item->attribute( 'id' ), $optionData['id'], $optionData['name'],
00488                                                                              $optionData['value'], $optionData['additional_price'], $attributeID );
00489                         $optionItem->store();
00490                         $price += $optionData['additional_price'];
00491                     }
00492                 }
00493 
00494                 if ( $price != $priceWithoutOptions )
00495                 {
00496                     $item->setAttribute( "price", $price );
00497                     $item->store();
00498                 }
00499                 $db->commit();
00500             }
00501         }
00502 
00503         return array( 'status' => eZModuleOperationInfo::STATUS_CONTINUE );
00504     }
00505 
00506     function activateOrder( $orderID )
00507     {
00508         $order = eZOrder::fetch( $orderID );
00509 
00510         $db = eZDB::instance();
00511         $db->begin();
00512         $order->activate();
00513 
00514         $basket = eZBasket::currentBasket( true, $orderID);
00515         $basket->remove();
00516         $db->commit();
00517 
00518         eZHTTPTool::instance()->setSessionVariable( "UserOrderID", $orderID );
00519         return array( 'status' => eZModuleOperationInfo::STATUS_CONTINUE );
00520     }
00521 
00522     function sendOrderEmails( $orderID )
00523     {
00524         $order = eZOrder::fetch( $orderID );
00525 
00526         // Fetch the shop account handler
00527         $accountHandler = eZShopAccountHandler::instance();
00528         $email = $accountHandler->email( $order );
00529 
00530         // Fetch the confirm order handler
00531         $confirmOrderHandler = eZConfirmOrderHandler::instance();
00532         $params = array( 'email' => $email,
00533                          'order' => $order );
00534         $confirmOrderStatus = $confirmOrderHandler->execute( $params );
00535 
00536         return array( 'status' => eZModuleOperationInfo::STATUS_CONTINUE );
00537     }
00538 
00539     /*!
00540      Verify that we have a valid currency before the the order can continue.
00541     */
00542     function checkCurrency( $orderID )
00543     {
00544         $returnStatus = array( 'status' => eZModuleOperationInfo::STATUS_CONTINUE );
00545         $order = eZOrder::fetch( $orderID );
00546         $productCollection = $order->attribute( 'productcollection' );
00547         $currencyCode = $productCollection->attribute( 'currency_code' );
00548         $currencyCode = trim( $currencyCode );
00549         if ( $currencyCode == '' )
00550         {
00551             $returnStatus = array( 'status' => eZModuleOperationInfo::STATUS_CANCELLED );
00552         }
00553 
00554         $locale = eZLocale::instance();
00555         $localeCurrencyCode = $locale->currencyShortName();
00556 
00557         // Reverse logic to avoid calling eZCurrencyData::currencyExists() if the first expression is true.
00558         if ( !( $currencyCode == $localeCurrencyCode or
00559                 eZCurrencyData::currencyExists( $currencyCode ) ) )
00560         {
00561             $returnStatus = array( 'status' => eZModuleOperationInfo::STATUS_CANCELLED );
00562         }
00563         return $returnStatus;
00564     }
00565 }
00566 
00567 ?>