|
eZ Publish
[trunk]
|
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 ?>