oxbasket.php

Go to the documentation of this file.
00001 <?php
00002 
00005 class oxBasket extends oxSuperCfg
00006 {
00012     protected $_aBasketContents = array();
00013 
00019     protected $_iProductsCnt = 0;
00020 
00026     protected $_dItemsCnt = 0.0;
00027 
00033     protected $_dWeight = 0.0;
00034 
00040     protected $_oPrice = null;
00041 
00047     protected $_oProductsPriceList = null;
00048 
00054     protected $_aDiscounts = array();
00055 
00061     protected $_aItemDiscounts = array();
00062 
00068     protected $_sOrderId = null;
00069 
00075     protected $_aVouchers = array();
00076 
00082     protected $_aCosts = array();
00083 
00089     protected $_oDiscountProductsPriceList = null;
00090 
00096     protected $_oNotDiscountedProductsPriceList = null;
00097 
00103     protected $_blUpdateNeeded = true;
00104 
00110     protected $_aBasketSummary = null;
00111 
00117     protected $_sPaymentId = null;
00118 
00124     protected $_sShippingSetId = null;
00125 
00131     protected $_oUser = null;
00132 
00138     protected $_oTotalDiscount = null;
00139 
00145     protected $_oVoucherDiscount = null;
00146 
00152     protected $_oCurrency = null;
00153 
00159     protected $_blSkipVouchersAvailabilityChecking = null;
00160 
00166     protected $_dDiscountedProductNettoPrice = null;
00167 
00173     protected $_aDiscountedVats = null;
00174 
00180     protected $_blSkipDiscounts = false;
00181 
00187     protected $_oDeliveryPrice = null;
00188 
00194      protected $_blCheckStock = true;
00195 
00201     protected $_blCalcDiscounts = true;
00202 
00208     protected $_sBasketCategoryId = null;
00209 
00215     protected $_blShowCatChangeWarning = false;
00216 
00222     protected $_sTsProductId = null;
00223 
00228     protected $_blNewITemAdded = null;
00229 
00234     protected $_blDownloadableProducts = null;
00235 
00241     public function isEnabled()
00242     {
00243         return !oxUtils::getInstance()->isSearchEngine();
00244     }
00245 
00255     protected function _changeBasketItemKey($sOldKey, $sNewKey, $value = null)
00256     {
00257         reset($this->_aBasketContents);
00258         $iOldKeyPlace = 0;
00259         while (key($this->_aBasketContents) != $sOldKey && next($this->_aBasketContents)) {
00260             ++$iOldKeyPlace;
00261         }
00262         $aNewCopy = array_merge(
00263             array_slice($this->_aBasketContents, 0, $iOldKeyPlace, true),
00264             array($sNewKey => $value),
00265             array_slice($this->_aBasketContents, $iOldKeyPlace+1, count($this->_aBasketContents)-$iOldKeyPlace, true)
00266         );
00267         $this->_aBasketContents = $aNewCopy;
00268     }
00269 
00285     public function addToBasket( $sProductID, $dAmount, $aSel = null, $aPersParam = null, $blOverride = false, $blBundle = false, $sOldBasketItemId = null )
00286     {
00287         // enabled ?
00288         if ( !$this->isEnabled() )
00289             return null;
00290 
00291         // basket exclude
00292         if ( $this->getConfig()->getConfigParam( 'blBasketExcludeEnabled' ) ) {
00293             if ( !$this->canAddProductToBasket( $sProductID ) ) {
00294                 $this->setCatChangeWarningState( true );
00295                 return null;
00296             } else {
00297                 $this->setCatChangeWarningState( false );
00298             }
00299         }
00300 
00301         $sItemId = $this->getItemKey( $sProductID, $aSel, $aPersParam, $blBundle );
00302         if ( $sOldBasketItemId && ( strcmp( $sOldBasketItemId, $sItemId ) != 0 ) ) {
00303             if ( isset( $this->_aBasketContents[$sItemId] ) ) {
00304                 // we are merging, so params will just go to the new key
00305                 unset( $this->_aBasketContents[$sOldBasketItemId] );
00306                 // do not override stock
00307                 $blOverride = false;
00308             } else {
00309                 // value is null - means isset will fail and real values will be filled
00310                 $this->_changeBasketItemKey( $sOldBasketItemId, $sItemId );
00311             }
00312         }
00313 
00314         // after some checks item must be removed from basket
00315         $blRemoveItem = false;
00316 
00317         // initialting exception storage
00318         $oEx = null;
00319 
00320         if ( isset( $this->_aBasketContents[$sItemId] ) ) {
00321 
00322             //updating existing
00323             try {
00324                 // setting stock check status
00325                 $this->_aBasketContents[$sItemId]->setStockCheckStatus( $this->getStockCheckMode() );
00326                 //validate amount
00327                 //possibly throws exception
00328                 $this->_aBasketContents[$sItemId]->setAmount( $dAmount, $blOverride, $sItemId );
00329             } catch( oxOutOfStockException $oEx ) {
00330                 // rethrow later
00331             }
00332 
00333         } else {
00334             //inserting new
00335             $oBasketItem = oxNew( 'oxbasketitem' );
00336             try {
00337                 $oBasketItem->setStockCheckStatus( $this->getStockCheckMode() );
00338                 $oBasketItem->init( $sProductID, $dAmount, $aSel, $aPersParam, $blBundle );
00339             } catch( oxNoArticleException $oEx ) {
00340                 // in this case that the article does not exist remove the item from the basket by setting its amount to 0
00341                 //$oBasketItem->dAmount = 0;
00342                 $blRemoveItem = true;
00343 
00344             } catch( oxOutOfStockException $oEx ) {
00345                 // rethrow later
00346             } catch ( oxArticleInputException $oEx ) {
00347                 // rethrow later
00348                 $blRemoveItem = true;
00349             }
00350 
00351             $this->_aBasketContents[$sItemId] = $oBasketItem;
00352         }
00353 
00354         //in case amount is 0 removing item
00355         if ( $this->_aBasketContents[$sItemId]->getAmount() == 0 || $blRemoveItem ) {
00356             $this->removeItem( $sItemId );
00357         } elseif ( $blBundle ) {
00358             //marking bundles
00359             $this->_aBasketContents[$sItemId]->setBundle( true );
00360         }
00361 
00362         //calling update method
00363         $this->onUpdate();
00364 
00365         if ( $oEx ) {
00366             throw $oEx;
00367         }
00368 
00369         // notifying that new basket item was added
00370         if (!$blBundle) {
00371             $this->_addedNewItem( $sProductID, $dAmount, $aSel, $aPersParam, $blOverride, $blBundle, $sOldBasketItemId );
00372         }
00373 
00374         // returning basket item object
00375         return $this->_aBasketContents[$sItemId];
00376     }
00377 
00385     public function addOrderArticleToBasket( $oOrderArticle )
00386     {
00387         // adding only if amount > 0
00388         if ( $oOrderArticle->oxorderarticles__oxamount->value > 0 && !$oOrderArticle->isBundle() ) {
00389             $sItemId = $oOrderArticle->getId();
00390 
00391             //inserting new
00392             $this->_aBasketContents[$sItemId] = oxNew( 'oxbasketitem' );
00393             $this->_aBasketContents[$sItemId]->initFromOrderArticle( $oOrderArticle );
00394             $this->_aBasketContents[$sItemId]->setWrapping( $oOrderArticle->oxorderarticles__oxwrapid->value );
00395             $this->_aBasketContents[$sItemId]->setBundle( $oOrderArticle->isBundle() );
00396 
00397             //calling update method
00398             $this->onUpdate();
00399 
00400             return $this->_aBasketContents[$sItemId];
00401         } elseif ( $oOrderArticle->isBundle() ) {
00402             // deleting bundles, they are handled automatically
00403             $oOrderArticle->delete();
00404         }
00405     }
00406 
00414     public function setStockCheckMode( $blCheck )
00415     {
00416         $this->_blCheckStock = $blCheck;
00417     }
00418 
00424     public function getStockCheckMode()
00425     {
00426         return $this->_blCheckStock;
00427     }
00428 
00441     public function getItemKey( $sProductId, $aSel = null, $aPersParam = null, $blBundle = false, $sAdditionalParam = '' )
00442     {
00443         $aSel = ( $aSel != null) ? $aSel : array (0=>'0');
00444 
00445         $sItemKey = md5( $sProductId.'|'.serialize( $aSel ).'|'.serialize( $aPersParam ).'|'.( int ) $blBundle . '|' . serialize( $sAdditionalParam ) );
00446 
00447         return $sItemKey;
00448     }
00449 
00450 
00458     public function removeItem( $sItemKey )
00459     {
00460         if ($this->getConfig()->getConfigParam( 'blPsBasketReservationEnabled' )) {
00461             if (isset($this->_aBasketContents[$sItemKey])) {
00462                 $sArticleId = $this->_aBasketContents[$sItemKey]->getProductId();
00463                 if ($sArticleId) {
00464                     $this->getSession()
00465                             ->getBasketReservations()
00466                             ->discardArticleReservation($sArticleId);
00467                 }
00468             }
00469         }
00470         unset( $this->_aBasketContents[$sItemKey] );
00471 
00472         // basket exclude
00473         if ( !count($this->_aBasketContents) && $this->getConfig()->getConfigParam( 'blBasketExcludeEnabled' ) ) {
00474             $this->setBasketRootCatId(null);
00475         }
00476     }
00477 
00483     protected function _clearBundles()
00484     {
00485         reset( $this->_aBasketContents );
00486         while ( list( $sItemKey, $oBasketItem ) = each( $this->_aBasketContents ) ) {
00487             if ( $oBasketItem->isBundle() ) {
00488                 $this->removeItem( $sItemKey );
00489             }
00490         }
00491     }
00492 
00500     protected function _getArticleBundles( $oBasketItem )
00501     {
00502         $aBundles = array();
00503 
00504         if ( $oBasketItem->isBundle() ) {
00505             return $aBundles;
00506         }
00507 
00508         $oArticle = $oBasketItem->getArticle( true );
00509         if ( $oArticle && $oArticle->oxarticles__oxbundleid->value ) {
00510             $aBundles[$oArticle->oxarticles__oxbundleid->value] = 1;
00511         }
00512 
00513         return $aBundles;
00514     }
00515 
00524     protected function _getItemBundles( $oBasketItem, $aBundles = array() )
00525     {
00526         if ( $oBasketItem->isBundle() ) {
00527             return array();
00528         }
00529 
00530         // does this object still exists ?
00531         if ( $oArticle = $oBasketItem->getArticle( true ) ) {
00532             $aDiscounts = oxDiscountList::getInstance()->getBasketItemBundleDiscounts( $oArticle, $this, $this->getBasketUser() );
00533 
00534             foreach ( $aDiscounts as $oDiscount ) {
00535 
00536                 $iAmnt = $oDiscount->getBundleAmount( $oBasketItem->getAmount() );
00537                 if ( $iAmnt ) {
00538                     //init array element
00539                     if ( !isset( $aBundles[$oDiscount->oxdiscount__oxitmartid->value] ) ) {
00540                         $aBundles[$oDiscount->oxdiscount__oxitmartid->value] = 0;
00541                     }
00542 
00543                     if ($oDiscount->oxdiscount__oxitmmultiple->value) {
00544                         $aBundles[$oDiscount->oxdiscount__oxitmartid->value] += $iAmnt;
00545                     } else {
00546                         $aBundles[$oDiscount->oxdiscount__oxitmartid->value] = $iAmnt;
00547                     }
00548                 }
00549             }
00550         }
00551 
00552         return $aBundles;
00553     }
00554 
00562     protected function _getBasketBundles( $aBundles = array() )
00563     {
00564         $aDiscounts = oxDiscountList::getInstance()->getBasketBundleDiscounts( $this, $this->getBasketUser() );
00565 
00566         // calculating amount of non bundled/discount items
00567         $dAmount = 0;
00568         foreach ( $this->_aBasketContents as $oBasketItem ) {
00569             if ( !( $oBasketItem->isBundle() || $oBasketItem->isDiscountArticle() ) ) {
00570                 $dAmount += $oBasketItem->getAmount();
00571             }
00572         }
00573 
00574         foreach ( $aDiscounts as $oDiscount ) {
00575             if ($oDiscount->oxdiscount__oxitmartid->value) {
00576                 if ( !isset( $aBundles[$oDiscount->oxdiscount__oxitmartid->value] ) ) {
00577                     $aBundles[$oDiscount->oxdiscount__oxitmartid->value] = 0;
00578                 }
00579 
00580                 $aBundles[$oDiscount->oxdiscount__oxitmartid->value] += $oDiscount->getBundleAmount( $dAmount );
00581             }
00582         }
00583 
00584         return $aBundles;
00585     }
00586 
00593     protected function _addBundles()
00594     {
00595         $aBundles = array();
00596         // iterating through articles and binding bundles
00597         foreach ( $this->_aBasketContents as $key => $oBasketItem ) {
00598             try {
00599                 // adding discount type bundles
00600                 if ( !$oBasketItem->isDiscountArticle() && !$oBasketItem->isBundle() ) {
00601                     $aBundles = $this->_getItemBundles( $oBasketItem, $aBundles );
00602                 } else {
00603                     continue;
00604                 }
00605 
00606                     // adding item type bundles
00607                     $aArtBundles = $this->_getArticleBundles( $oBasketItem );
00608 
00609                     // adding bundles to basket
00610                     $this->_addBundlesToBasket( $aArtBundles );
00611             } catch ( oxNoArticleException $oEx ) {
00612                 $this->removeItem( $key );
00613                 oxUtilsView::getInstance()->addErrorToDisplay( $oEx );
00614             } catch( oxArticleInputException $oEx ) {
00615                 $this->removeItem( $key );
00616                 oxUtilsView::getInstance()->addErrorToDisplay( $oEx );
00617             }
00618         }
00619 
00620         // adding global basket bundles
00621         $aBundles = $this->_getBasketBundles( $aBundles );
00622 
00623         // adding all bundles to basket
00624         if ( $aBundles ) {
00625             $this->_addBundlesToBasket( $aBundles );
00626         }
00627     }
00628 
00636     protected function _addBundlesToBasket( $aBundles )
00637     {
00638         foreach ( $aBundles as $sBundleId => $dAmount ) {
00639             if ( $dAmount ) {
00640                 try {
00641                     if ( $oBundleItem = $this->addToBasket( $sBundleId, $dAmount, null, null, false, true ) ) {
00642                         $oBundleItem->setAsDiscountArticle( true );
00643                     }
00644                 } catch(oxArticleException $oEx) {
00645                     // caught and ignored
00646                 }
00647             }
00648         }
00649 
00650     }
00651 
00657     protected function _calcItemsPrice()
00658     {
00659         // resetting
00660         $this->setSkipDiscounts( false );
00661         $this->_iProductsCnt = 0; // count different types
00662         $this->_dItemsCnt    = 0; // count of item units
00663         $this->_dWeight      = 0; // basket weight
00664 
00665         // resetting
00666         $this->_aItemDiscounts = array();
00667 
00668         $this->_oProductsPriceList = oxNew( 'oxpricelist' );
00669         $this->_oDiscountProductsPriceList = oxNew( 'oxpricelist' );
00670         $this->_oNotDiscountedProductsPriceList = oxNew( 'oxpricelist' );
00671 
00672         $oDiscountList = oxDiscountList::getInstance();
00673 
00674         foreach ( $this->_aBasketContents as $oBasketItem ) {
00675             $this->_iProductsCnt++;
00676             $this->_dItemsCnt += $oBasketItem->getAmount();
00677             $this->_dWeight   += $oBasketItem->getWeight();
00678 
00679             if ( !$oBasketItem->isDiscountArticle() && ( $oArticle = $oBasketItem->getArticle( true ) ) ) {
00680                 $oBasketPrice = $oArticle->getBasketPrice( $oBasketItem->getAmount(), $oBasketItem->getSelList(), $this );
00681                 $oBasketItem->setPrice( $oBasketPrice );
00682                 //P adding product price
00683                 $this->_oProductsPriceList->addToPriceList( $oBasketItem->getPrice() );
00684 
00685                 $oBasketPrice->setBruttoPriceMode();
00686                 if ( !$oArticle->skipDiscounts() && $this->canCalcDiscounts() ) {
00687                     // apply basket type discounts
00688                     //#3857 added clone in order not to influence the price
00689                     $aItemDiscounts = $oDiscountList->applyBasketDiscounts( clone $oBasketPrice, $oDiscountList->getBasketItemDiscounts( $oArticle, $this, $this->getBasketUser() ), $oBasketItem->getAmount() );
00690                     if ( is_array($this->_aItemDiscounts) && is_array($aItemDiscounts) ) {
00691                         $this->_aItemDiscounts = $this->_mergeDiscounts( $this->_aItemDiscounts, $aItemDiscounts);
00692                     }
00693                 } else {
00694                     $oBasketItem->setSkipDiscounts( true );
00695                     $this->setSkipDiscounts( true );
00696                 }
00697                 $oBasketPrice->multiply( $oBasketItem->getAmount() );
00698 
00699                 //P collect discount values for basket items which are discountable
00700                 if ( !$oArticle->skipDiscounts() ) {
00701                     $this->_oDiscountProductsPriceList->addToPriceList( $oBasketPrice );
00702                 } else {
00703                     $this->_oNotDiscountedProductsPriceList->addToPriceList( $oBasketPrice );
00704                     $oBasketItem->setSkipDiscounts( true );
00705                     $this->setSkipDiscounts( true );
00706                 }
00707             } elseif ( $oBasketItem->isBundle() ) {
00708                 // if bundles price is set to zero
00709                 $oPrice = oxNew( "oxprice");
00710                 $oBasketItem->setPrice( $oPrice );
00711             }
00712         }
00713     }
00714 
00722     public function setDiscountCalcMode( $blCalcDiscounts )
00723     {
00724         $this->_blCalcDiscounts = $blCalcDiscounts;
00725     }
00726 
00732     public function canCalcDiscounts()
00733     {
00734         return $this->_blCalcDiscounts;
00735     }
00736 
00746     protected function _mergeDiscounts( $aDiscounts, $aItemDiscounts)
00747     {
00748         foreach ( $aItemDiscounts as $sKey => $oDiscount ) {
00749             // add prices of the same discounts
00750             if ( array_key_exists ($sKey, $aDiscounts) ) {
00751                 $aDiscounts[$sKey]->dDiscount += $oDiscount->dDiscount;
00752             } else {
00753                 $aDiscounts[$sKey] = $oDiscount;
00754             }
00755         }
00756         return $aDiscounts;
00757     }
00758 
00764     protected function _calcDeliveryCost()
00765     {
00766         if ( $this->_oDeliveryPrice !== null ) {
00767             return $this->_oDeliveryPrice;
00768         }
00769         $myConfig  = $this->getConfig();
00770         $oDeliveryPrice = oxNew( 'oxprice' );
00771         $oDeliveryPrice->setBruttoPriceMode();
00772 
00773         // don't calculate if not logged in
00774         $oUser = $this->getBasketUser();
00775 
00776         if ( !$oUser && !$myConfig->getConfigParam( 'blCalculateDelCostIfNotLoggedIn' ) ) {
00777             return $oDeliveryPrice;
00778         }
00779 
00780         // VAT for delivery will be calculated always (#3757)
00781         // blCalcVATForDelivery option is @deprecated since 2012-03-23 in version 4.6
00782         // the option blShowVATForDelivery will be used only for displaying
00783         $fDelVATPercent = 0;
00784         $fDelVATPercent = $this->getMostUsedVatPercent();
00785         $oDeliveryPrice->setVat( $fDelVATPercent );
00786 
00787         // list of active delivery costs
00788         if ( $myConfig->getConfigParam('bl_perfLoadDelivery') ) {
00789             $aDeliveryList = oxDeliveryList::getInstance()->getDeliveryList( $this,
00790                                         $oUser,
00791                                         $this->_findDelivCountry(),
00792                                         $this->getShippingId()
00793                                     );
00794 
00795             if ( count( $aDeliveryList ) > 0 ) {
00796                 foreach ( $aDeliveryList as $oDelivery ) {
00797                     //debug trace
00798                     if ( $myConfig->getConfigParam( 'iDebug' ) == 5 ) {
00799                         echo( "DelCost : ".$oDelivery->oxdelivery__oxtitle->value."<br>" );
00800                     }
00801                     $oDeliveryPrice->addPrice( $oDelivery->getDeliveryPrice( $fDelVATPercent ) );
00802                 }
00803             }
00804         }
00805 
00806         return $oDeliveryPrice;
00807     }
00808 
00814     public function getBasketUser()
00815     {
00816         if ( $this->_oUser == null ) {
00817             return $this->getUser();
00818         }
00819 
00820         return $this->_oUser;
00821     }
00822 
00830     public function setBasketUser( $oUser )
00831     {
00832         $this->_oUser = $oUser;
00833     }
00834 
00835     //P
00841     public function getMostUsedVatPercent()
00842     {
00843         return $this->_oProductsPriceList->getMostUsedVatPercent();
00844     }
00845 
00846     //P
00853     protected function _calcTotalPrice()
00854     {
00855         // 1. add products price
00856         $dprice = $this->_oProductsPriceList->getBruttoSum();
00857         $this->_oPrice->setPrice( $dprice );
00858 
00859         // 2. substract discounts
00860         if ( $dprice ) {
00861 
00862             /*
00863             //#3857 this section is not needed as $this->_aItemDiscounts is part of $this->_aDiscounts
00864             // 2.1 applying basket item discounts
00865             foreach ( $this->_aItemDiscounts as $oDiscount ) {
00866 
00867                 // skipping bundle discounts
00868                 if ( $oDiscount->sType == 'itm' ) {
00869                     continue;
00870                 }
00871                 $this->_oPrice->subtract( $oDiscount->dDiscount );
00872             }*/
00873 
00874             // 2.2 applying basket discounts
00875             $this->_oPrice->subtract( $this->_oTotalDiscount->getBruttoPrice() );
00876 
00877             // 2.3 applying voucher discounts
00878             if ($oVoucherDisc = $this->getVoucherDiscount()) {
00879                 $this->_oPrice->subtract( $oVoucherDisc->getBruttoPrice() );
00880             }
00881         }
00882 
00883         // 2.3 add delivery cost
00884         if ( isset( $this->_aCosts['oxdelivery'] ) ) {
00885             $this->_oPrice->add( $this->_aCosts['oxdelivery']->getBruttoPrice() );
00886         }
00887 
00888         // 2.4 add wrapping price
00889         if ( isset( $this->_aCosts['oxwrapping'] ) ) {
00890             $this->_oPrice->add( $this->_aCosts['oxwrapping']->getBruttoPrice() );
00891         }
00892 
00893         // 2.5 add payment price
00894         if ( isset( $this->_aCosts['oxpayment'] ) ) {
00895             $this->_oPrice->add( $this->_aCosts['oxpayment']->getBruttoPrice() );
00896         }
00897 
00898         // 2.6 add TS protection price
00899         if ( isset( $this->_aCosts['oxtsprotection'] ) ) {
00900             $this->_oPrice->add( $this->_aCosts['oxtsprotection']->getBruttoPrice() );
00901         }
00902 
00903     }
00904 
00912     public function setVoucherDiscount( $dDiscount )
00913     {
00914         $this->_oVoucherDiscount = oxNew( 'oxPrice' );
00915         $this->_oVoucherDiscount->setBruttoPriceMode();
00916         $this->_oVoucherDiscount->add( $dDiscount );
00917     }
00918 
00924     protected function _calcVoucherDiscount()
00925     {
00926         if ( $this->getConfig()->getConfigParam( 'bl_showVouchers' ) && ($this->_oVoucherDiscount === null || ( $this->_blUpdateNeeded && !$this->isAdmin() ) ) ) {
00927 
00928             $this->_oVoucherDiscount = oxNew( 'oxPrice' );
00929             $this->_oVoucherDiscount->setBruttoPriceMode();
00930 
00931 
00932             // calculating price to apply discount
00933             $dPrice = $this->_oDiscountProductsPriceList->getBruttoSum() - $this->_oTotalDiscount->getBruttoPrice();
00934 
00935             // recalculating
00936             if ( count( $this->_aVouchers ) ) {
00937                 $oLang = oxLang::getInstance();
00938                 foreach ( $this->_aVouchers as $sVoucherId => $oStdVoucher ) {
00939                     $oVoucher = oxNew( 'oxvoucher' );
00940                     try { // checking
00941                         $oVoucher->load( $oStdVoucher->sVoucherId );
00942 
00943                         if ( !$this->_blSkipVouchersAvailabilityChecking ) {
00944                             $oVoucher->checkBasketVoucherAvailability( $this->_aVouchers, $dPrice );
00945                             $oVoucher->checkUserAvailability( $this->getBasketUser() );
00946                         }
00947 
00948                         // assigning real voucher discount value as this is the only place where real value is calculated
00949                         $dVoucherdiscount = $oVoucher->getDiscountValue( $dPrice );
00950 
00951                         // acumulating discount value
00952                         $this->_oVoucherDiscount->add( $dVoucherdiscount );
00953 
00954                         // collecting formatted for preview
00955                         $oStdVoucher->fVoucherdiscount = $oLang->formatCurrency( $dVoucherdiscount, $this->getBasketCurrency() );
00956                         $oStdVoucher->dVoucherdiscount = $dVoucherdiscount;
00957 
00958                         // substracting voucher discount
00959                         $dPrice -= $dVoucherdiscount;
00960                     } catch ( oxVoucherException $oEx ) {
00961 
00962                         // removing voucher on error
00963                         $oVoucher->unMarkAsReserved();
00964                         unset( $this->_aVouchers[$sVoucherId] );
00965 
00966                         // storing voucher error info
00967                         oxUtilsView::getInstance()->addErrorToDisplay($oEx, false, true);
00968                     }
00969                 }
00970             }
00971         }
00972     }
00973 
00974     //V
00981     protected function _applyDiscounts()
00982     {
00983         $dBruttoPrice = 0;
00984         $this->_aDiscountedVats = array();
00985         if ( $oPriceList = $this->getDiscountProductsPrice() ) {
00986             $dBruttoPrice = $oPriceList->getBruttoSum();
00987             $this->_aDiscountedVats = $oPriceList->getVatInfo();
00988         }
00989 
00990         //apply discounts for brutto price
00991         $dDiscountedBruttoPrice = $this->getDiscountedProductsBruttoPrice();
00992         $oTotalDiscount   = $this->getTotalDiscount();
00993         $oVoucherDiscount = $this->getVoucherDiscount();
00994 
00995         //apply discount for VATs
00996         if ( $dBruttoPrice &&
00997              ( ( $oTotalDiscount && $oTotalDiscount->getBruttoPrice() ) ||
00998                ( $oVoucherDiscount && $oVoucherDiscount->getBruttoPrice() )
00999              )
01000            ) {
01001             $dPercent = ( $dDiscountedBruttoPrice / $dBruttoPrice) * 100;
01002             foreach ( $this->_aDiscountedVats as $sKey => $dVat ) {
01003                 $this->_aDiscountedVats[$sKey] = oxPrice::percent( $dVat, $dPercent);
01004             }
01005         }
01006 
01007         $oUtils = oxUtils::getInstance();
01008         $dDiscVatSum = 0;
01009         foreach ( $this->_aDiscountedVats as $dVat ) {
01010             $dDiscVatSum += $oUtils->fRound( -$dVat, $this->_oCurrency);
01011         }
01012         //calculate netto price with discounts
01013         $this->_dDiscountedProductNettoPrice = $dDiscountedBruttoPrice + $dDiscVatSum;
01014     }
01015 
01021     protected function _calcBasketDiscount()
01022     {
01023         // resetting
01024         $this->_aDiscounts = array();
01025 
01026         // P using prices sum which has discount, not sum of skipped discounts
01027         $dOldprice = $this->_oDiscountProductsPriceList->getBruttoSum();
01028 
01029         // add basket discounts
01030         $aDiscounts = oxDiscountList::getInstance()->getBasketDiscounts( $this, $this->getBasketUser() );
01031 
01032         foreach ( $aDiscounts as $oDiscount ) {
01033 
01034             // storing applied discounts
01035             $oStdDiscount = $oDiscount->getSimpleDiscount();
01036 
01037             // skipping bundle discounts
01038             if ( $oDiscount->oxdiscount__oxaddsumtype->value == 'itm' ) {
01039                 continue;
01040             }
01041 
01042             // saving discount info
01043             $oStdDiscount->dDiscount = $oDiscount->getAbsValue( $dOldprice );
01044             if ($dOldprice < $oStdDiscount->dDiscount) {
01045                 $oStdDiscount->dDiscount = $dOldprice;
01046             }
01047 
01048             if ($oStdDiscount->dDiscount != 0) {
01049                 $this->_aDiscounts[$oDiscount->getId()] = $oStdDiscount;
01050                 // substracting product price after discount
01051                 $dOldprice = $dOldprice - $oStdDiscount->dDiscount;
01052             }
01053         }
01054     }
01055 
01061     protected function _calcBasketTotalDiscount()
01062     {
01063         if ( $this->_oTotalDiscount === null || ( !$this->isAdmin() ) ) {
01064 
01065 
01066             $this->_oTotalDiscount = oxNew( 'oxPrice' );
01067             $this->_oTotalDiscount->setBruttoPriceMode();
01068 
01069             //#3857 merging item discounts to aDiscounts and later to oTotalDiscount
01070             $this->_aDiscounts = array_merge($this->_aItemDiscounts, $this->_aDiscounts);
01071 
01072             if ( is_array($this->_aDiscounts) ) {
01073                 foreach ( $this->_aDiscounts as $oDiscount ) {
01074 
01075                     // skipping bundle discounts
01076                     if ( $oDiscount->sType == 'itm' ) {
01077                         continue;
01078                     }
01079 
01080                     // add discount value to total basket discount
01081                     $this->_oTotalDiscount->add( $oDiscount->dDiscount );
01082                 }
01083             }
01084         }
01085     }
01086 
01096     protected function _calcBasketWrapping()
01097     {
01098         $myConfig = $this->getConfig();
01099         $oWrappingPrice = oxNew( 'oxPrice' );
01100         $oWrappingPrice->setBruttoPriceMode();
01101 
01102         // wrapping VAT will be always calculated (#3757)
01103         // blCalcVatForWrapping option is @deprecated since 2012-03-23 in version 4.6
01104         // blShowVATForWrapping option will be used only for displaying
01105         $dWrappingVATPercent = $this->getMostUsedVatPercent();
01106         $oWrappingPrice->setVat( $dWrappingVATPercent );
01107 
01108         // calculating basket items wrapping
01109         foreach ( $this->_aBasketContents as $oBasketItem ) {
01110 
01111             if ( ( $oWrapping = $oBasketItem->getWrapping() ) ) {
01112                 if ($dWrappingVATPercent !== null) {
01113                     $oWrapping->setWrappingVat($dWrappingVATPercent);
01114                 }
01115                 $oWrappingPrice->addPrice( $oWrapping->getWrappingPrice( $oBasketItem->getAmount() ) );
01116             }
01117         }
01118 
01119         // gift card price calculation
01120         if ( ( $oCard = $this->getCard() ) ) {
01121             if ($dWrappingVATPercent !== null) {
01122                 $oCard->setWrappingVat($dWrappingVATPercent);
01123             }
01124             $oWrappingPrice->addPrice( $oCard->getWrappingPrice() );
01125         }
01126 
01127         return $oWrappingPrice;
01128     }
01129 
01136     protected function _calcPaymentCost()
01137     {
01138         // resetting values
01139         $oPaymentPrice = oxNew( 'oxPrice' );
01140         $oPaymentPrice->setBruttoPriceMode();
01141 
01142         // payment
01143         if ( ( $this->_sPaymentId = $this->getPaymentId() ) ) {
01144 
01145             $oPayment = oxNew( 'oxpayment' );
01146             $oPayment->load( $this->_sPaymentId );
01147 
01148             $oPaymentPrice = $oPayment->getPaymentPrice( $this );
01149         }
01150 
01151         return $oPaymentPrice;
01152     }
01153 
01160     protected function _calcTsProtectionCost()
01161     {
01162         // resetting values
01163         $oProtectionPrice = oxNew( 'oxPrice' );
01164         $oProtectionPrice->setBruttoPriceMode();
01165 
01166         // payment
01167         if ( ( $this->_sTsProductId = $this->getTsProductId() ) ) {
01168 
01169             $oTsProtection = oxNew('oxtsprotection');
01170             $oTsProduct = $oTsProtection->getTsProduct( $this->_sTsProductId );
01171 
01172             $oProtectionPrice = $oTsProduct->getPrice();
01173         }
01174         return $oProtectionPrice;
01175     }
01176 
01185     public function setCost( $sCostName, $oPrice = null )
01186     {
01187         $this->_aCosts[$sCostName] = $oPrice;
01188     }
01189 
01198     public function calculateBasket( $blForceUpdate = false )
01199     {
01200         /*
01201         //would be good to perform the reset of previous calculation
01202         //at least you can use it for the debug
01203         $this->_aDiscounts = array();
01204         $this->_aItemDiscounts = array();
01205         $this->_oTotalDiscount = null;
01206         $this->_dDiscountedProductNettoPrice = 0;
01207         $this->_aDiscountedVats = array();
01208         $this->_oPrice = null;
01209         $this->_oNotDiscountedProductsPriceList = null;
01210         $this->_oProductsPriceList = null;
01211         $this->_oDiscountProductsPriceList = null;*/
01212 
01213 
01214         if ( !$this->isEnabled() ) {
01215             return;
01216         }
01217 
01218         if ( !$this->_blUpdateNeeded && !$blForceUpdate ) {
01219             return;
01220         }
01221 
01222         $this->_aCosts = array();
01223 
01224         $this->_oPrice = oxNew( 'oxprice' );
01225         $this->_oPrice->setBruttoPriceMode();
01226 
01227         //  1. saving basket to the database
01228         $this->_save();
01229 
01230         //  2. remove all bundles
01231         $this->_clearBundles();
01232 
01233         //  3. generate bundle items
01234         $this->_addBundles();
01235 
01236         // reserve active basket
01237         if ($this->getConfig()->getConfigParam( 'blPsBasketReservationEnabled' )) {
01238             $this->getSession()->getBasketReservations()->reserveBasket($this);
01239         }
01240 
01241         //  4. calculating item prices
01242         $this->_calcItemsPrice();
01243 
01244         //  5. calculating/applying discounts
01245         $this->_calcBasketDiscount();
01246 
01247         //  6. calculating basket total discount
01248         $this->_calcBasketTotalDiscount();
01249 
01250         //  7. check for vouchers
01251         $this->_calcVoucherDiscount();
01252 
01253         //  8. applies all discounts to pricelist
01254         $this->_applyDiscounts();
01255 
01256         //  9. calculating additional costs:
01257         //  9.1: delivery
01258         $this->setCost( 'oxdelivery', $this->_calcDeliveryCost() );
01259 
01260         //  9.2: adding wrapping costs
01261         $this->setCost( 'oxwrapping', $this->_calcBasketWrapping() );
01262 
01263         //  9.3: adding payment cost
01264         $this->setCost( 'oxpayment', $this->_calcPaymentCost() );
01265 
01266         //  9.4: adding TS protection cost
01267         $this->setCost( 'oxtsprotection', $this->_calcTsProtectionCost() );
01268 
01269         //  10. calculate total price
01270         $this->_calcTotalPrice();
01271 
01272         //  11. setting deprecated values
01273         $this->_setDeprecatedValues();
01274 
01275         //  12.setting to up-to-date status
01276         $this->afterUpdate();
01277     }
01278 
01284     public function onUpdate()
01285     {
01286         $this->_blUpdateNeeded = true;
01287     }
01288 
01294     public function afterUpdate()
01295     {
01296         $this->_blUpdateNeeded = false;
01297     }
01298 
01306     public function getBasketSummary()
01307     {
01308         if ( $this->_blUpdateNeeded || $this->_aBasketSummary === null ) {
01309             $this->_aBasketSummary = new Oxstdclass();
01310             $this->_aBasketSummary->aArticles = array();
01311             $this->_aBasketSummary->aCategories = array();
01312             $this->_aBasketSummary->iArticleCount = 0;
01313             $this->_aBasketSummary->dArticlePrice = 0;
01314             $this->_aBasketSummary->dArticleDiscountablePrice = 0;
01315         }
01316 
01317         if ( !$this->isEnabled() ) {
01318             return $this->_aBasketSummary;
01319         }
01320 
01321         $myConfig = $this->getConfig();
01322         foreach ( $this->_aBasketContents as $oBasketItem ) {
01323             if ( !$oBasketItem->isBundle() && $oArticle = $oBasketItem->getArticle(false) ) {
01324                 $aCatIds = $oArticle->getCategoryIds();
01325                 //#M530 if price is not loaded for articles
01326                 $dPrice = 0;
01327                 $dDiscountablePrice = 0;
01328                 if ( ( $oPrice = $oArticle->getPrice( $oBasketItem->getAmount() ) ) ) {
01329                     $dPrice = $oPrice->getBruttoPrice();
01330                     if ( !$oArticle->skipDiscounts() ) {
01331                         $dDiscountablePrice = $dPrice;
01332                     }
01333                 }
01334 
01335                 foreach ( $aCatIds as $sCatId ) {
01336                     if ( !isset( $this->_aBasketSummary->aCategories[$sCatId] ) ) {
01337                         $this->_aBasketSummary->aCategories[$sCatId] = new Oxstdclass();
01338                     }
01339 
01340                     $this->_aBasketSummary->aCategories[$sCatId]->dPrice  += $dPrice * $oBasketItem->getAmount();
01341                     $this->_aBasketSummary->aCategories[$sCatId]->dDiscountablePrice += $dDiscountablePrice * $oBasketItem->getAmount();
01342                     $this->_aBasketSummary->aCategories[$sCatId]->dAmount += $oBasketItem->getAmount();
01343                     $this->_aBasketSummary->aCategories[$sCatId]->iCount++;
01344                 }
01345 
01346                 // variant handling
01347                 if ( ($sParentId = $oArticle->getProductParentId()) && $myConfig->getConfigParam( 'blVariantParentBuyable' ) ) {
01348                     if ( !isset( $this->_aBasketSummary->aArticles[$sParentId] ) ) {
01349                         $this->_aBasketSummary->aArticles[$sParentId] = 0;
01350                     }
01351                     $this->_aBasketSummary->aArticles[$sParentId] += $oBasketItem->getAmount();
01352                 }
01353 
01354                 if ( !isset( $this->_aBasketSummary->aArticles[$oBasketItem->getProductId()] ) ) {
01355                     $this->_aBasketSummary->aArticles[$oBasketItem->getProductId()] = 0;
01356                 }
01357 
01358                 $this->_aBasketSummary->aArticles[$oBasketItem->getProductId()] += $oBasketItem->getAmount();
01359                 $this->_aBasketSummary->iArticleCount += $oBasketItem->getAmount();
01360                 $this->_aBasketSummary->dArticlePrice += $dPrice * $oBasketItem->getAmount();
01361                 $this->_aBasketSummary->dArticleDiscountablePrice += $dDiscountablePrice * $oBasketItem->getAmount();
01362             }
01363         }
01364         return $this->_aBasketSummary;
01365     }
01366 
01378     public function addVoucher( $sVoucherId )
01379     {
01380         // calculating price to check
01381         // P using prices sum which has discount, not sum of skipped discounts
01382         $dPrice = 0;
01383         if ( $this->_oDiscountProductsPriceList ) {
01384             $dPrice = $this->_oDiscountProductsPriceList->getBruttoSum();
01385         }
01386 
01387         try { // trying to load voucher and apply it
01388 
01389             $oVoucher = oxNew( 'oxvoucher' );
01390 
01391             if ( !$this->_blSkipVouchersAvailabilityChecking ) {
01392                 $oVoucher->getVoucherByNr( $sVoucherId, $this->_aVouchers, true );
01393                 $oVoucher->checkVoucherAvailability( $this->_aVouchers, $dPrice );
01394                 $oVoucher->checkUserAvailability( $this->getBasketUser() );
01395                 $oVoucher->markAsReserved();
01396             } else {
01397                 $oVoucher->load( $sVoucherId );
01398             }
01399 
01400             // saving voucher info
01401             $this->_aVouchers[$oVoucher->oxvouchers__oxid->value] = $oVoucher->getSimpleVoucher();
01402         } catch ( oxVoucherException $oEx ) {
01403 
01404             // problems adding voucher
01405             oxUtilsView::getInstance()->addErrorToDisplay( $oEx, false, true );
01406         }
01407 
01408         $this->onUpdate();
01409     }
01410 
01418     public function removeVoucher( $sVoucherId )
01419     {
01420         // removing if it exists
01421         if ( isset( $this->_aVouchers[$sVoucherId] ) ) {
01422 
01423             $oVoucher = oxNew( 'oxvoucher' );
01424             $oVoucher->load( $sVoucherId );
01425 
01426             $oVoucher->unMarkAsReserved();
01427 
01428             // unsetting it if exists this voucher in DB or not
01429             unset( $this->_aVouchers[$sVoucherId] );
01430             $this->onUpdate();
01431         }
01432 
01433     }
01434 
01440     public function resetUserInfo()
01441     {
01442         $this->setPayment( null );
01443         $this->setShipping( null );
01444     }
01445 
01453     protected function _setDeprecatedValues()
01454     {
01455         // discount information
01456         // formating discount value
01457         $this->aDiscounts = $this->getDiscounts();
01458         if ( count($this->aDiscounts) > 0 ) {
01459             $oLang = oxLang::getInstance();
01460             foreach ($this->aDiscounts as $oDiscount) {
01461                 $oDiscount->fDiscount = $oLang->formatCurrency( $oDiscount->dDiscount, $this->getBasketCurrency() );
01462             }
01463         }
01464     }
01465 
01466 
01472     protected function _canSaveBasket()
01473     {
01474         $blCanSave = !$this->getConfig()->getConfigParam( 'blPerfNoBasketSaving' );
01475         return $blCanSave;
01476     }
01477 
01483     public function load()
01484     {
01485         $oUser = $this->getBasketUser();
01486         if ( !$oUser ) {
01487             return;
01488         }
01489 
01490         $oBasket = $oUser->getBasket( 'savedbasket' );
01491 
01492         // restoring from saved history
01493         $aSavedItems = $oBasket->getItems();
01494         foreach ( $aSavedItems as $oItem ) {
01495             try {
01496                 $oSelList = $oItem->getSelList();
01497 
01498                 $this->addToBasket( $oItem->oxuserbasketitems__oxartid->value, $oItem->oxuserbasketitems__oxamount->value, $oSelList, $oItem->getPersParams(), true );
01499             } catch( oxArticleException $oEx ) {
01500                 // caught and ignored
01501             }
01502         }
01503     }
01504 
01510     protected function _save()
01511     {
01512         if ( $this->_canSaveBasket() ) {
01513 
01514             if ( $oUser = $this->getBasketUser() ) {
01515                 //first delete all contents
01516                 //#2039
01517                 $oSavedBasket = $oUser->getBasket( 'savedbasket' );
01518                 $oSavedBasket->delete();
01519 
01520                 //then save
01521                 foreach ( $this->_aBasketContents as $oBasketItem ) {
01522                     // discount or bundled products will be added automatically if available
01523                     if ( !$oBasketItem->isBundle() && !$oBasketItem->isDiscountArticle() ) {
01524                        $oSavedBasket->addItemToBasket( $oBasketItem->getProductId(), $oBasketItem->getAmount(), $oBasketItem->getSelList(), true, $oBasketItem->getPersParams() );
01525                     }
01526                 }
01527             }
01528         }
01529     }
01530 
01542     /*
01543     protected function _addItemToSavedBasket( $sProductId , $dAmount, $aSel, $blOverride = false, $aPersParam = null )
01544     {
01545         // updating basket history
01546         if ( $oUser = $this->getBasketUser() ) {
01547             $oUser->getBasket( 'savedbasket' )->addItemToBasket( $sProductId, $dAmount, $aSel, $blOverride, $aPersParam );
01548         }
01549     }*/
01550 
01558     protected function _deleteSavedBasket()
01559     {
01560         // deleting basket if session user available
01561         if ( $oUser = $this->getBasketUser() ) {
01562             $oUser->getBasket( 'savedbasket' )->delete();
01563         }
01564 
01565         // basket exclude
01566         if ( $this->getConfig()->getConfigParam( 'blBasketExcludeEnabled' )) {
01567             $this->setBasketRootCatId(null);
01568         }
01569     }
01570 
01576     protected function _findDelivCountry()
01577     {
01578         $myConfig = $this->getConfig();
01579         $oUser    = $this->getBasketUser();
01580 
01581         $sDelivCountry = null;
01582 
01583         if ( !$oUser ) {
01584             // don't calculate if not logged in unless specified otherwise
01585             $aHomeCountry = $myConfig->getConfigParam( 'aHomeCountry' );
01586             if ( $myConfig->getConfigParam( 'blCalculateDelCostIfNotLoggedIn' ) && is_array( $aHomeCountry ) ) {
01587                 $sDelivCountry = current( $aHomeCountry );
01588             }
01589         } else {
01590 
01591             // ok, logged in
01592             if ( $sCountryId = $myConfig->getGlobalParameter( 'delcountryid' ) ) {
01593                 $sDelivCountry = $sCountryId;
01594             } elseif ( $sAddressId = oxSession::getVar( 'deladrid' ) ) {
01595 
01596                 $oDelAdress = oxNew( 'oxaddress' );
01597                 if ( $oDelAdress->load( $sAddressId ) ) {
01598                     $sDelivCountry = $oDelAdress->oxaddress__oxcountryid->value;
01599                 }
01600             }
01601 
01602             // still not found ?
01603             if ( !$sDelivCountry ) {
01604                 $sDelivCountry = $oUser->oxuser__oxcountryid->value;
01605             }
01606         }
01607 
01608         return $sDelivCountry;
01609     }
01610 
01616     public function deleteBasket()
01617     {
01618         $this->_aBasketContents = array();
01619         $this->getSession()->delBasket();
01620 
01621         if ($this->getConfig()->getConfigParam( 'blPsBasketReservationEnabled' )) {
01622             $this->getSession()->getBasketReservations()->discardReservations();
01623         }
01624 
01625         // merging basket history
01626         $this->_deleteSavedBasket();
01627     }
01628 
01636     public function setPayment( $sPaymentId = null )
01637     {
01638         $this->_sPaymentId = $sPaymentId;
01639     }
01640 
01646     public function getPaymentId()
01647     {
01648         if ( !$this->_sPaymentId ) {
01649              $this->_sPaymentId = oxSession::getVar( 'paymentid' );
01650         }
01651         return $this->_sPaymentId;
01652     }
01653 
01661     public function setShipping( $sShippingSetId = null )
01662     {
01663         $this->_sShippingSetId = $sShippingSetId;
01664         oxSession::setVar( 'sShipSet', $sShippingSetId );
01665     }
01666 
01674     public function setDeliveryPrice( $oShippingPrice = null )
01675     {
01676         $this->_oDeliveryPrice = $oShippingPrice;
01677     }
01678 
01684     public function getShippingId()
01685     {
01686         if ( !$this->_sShippingSetId ) {
01687              $this->_sShippingSetId = oxSession::getVar( 'sShipSet' );
01688         }
01689 
01690         $sActPaymentId = $this->getPaymentId();
01691         // setting default if none is set
01692         if ( !$this->_sShippingSetId && $sActPaymentId != 'oxempty' ) {
01693             $oUser = $this->getUser();
01694 
01695             // choosing first preferred delivery set
01696             list( , $sActShipSet ) = oxDeliverySetList::getInstance()->getDeliverySetData( null, $oUser, $this );
01697             // in case nothing was found and no user set - choosing default
01698             $this->_sShippingSetId = $sActShipSet ? $sActShipSet : ( $oUser ? null : 'oxidstandard' );
01699         } elseif ( !$this->isAdmin() && $sActPaymentId == 'oxempty' ) {
01700             // in case 'oxempty' is payment id - delivery set must be reset
01701             $this->_sShippingSetId = null;
01702         }
01703 
01704         return $this->_sShippingSetId;
01705     }
01706 
01712     public function getBasketArticles()
01713     {
01714         $aBasketArticles = array();
01715 
01716         foreach ( $this->_aBasketContents as $sItemKey => $oBasketItem ) {
01717             try {
01718                 $oProduct = $oBasketItem->getArticle( true );
01719 
01720                 if ( $this->getConfig()->getConfigParam( 'bl_perfLoadSelectLists' ) ) {
01721                     // marking chosen select list
01722                     $aSelList = $oBasketItem->getSelList();
01723                     if ( is_array( $aSelList ) && ( $aSelectlist = $oProduct->getSelectLists( $sItemKey ) ) ) {
01724                         reset( $aSelList );
01725                         while ( list( $conkey, $iSel ) = each( $aSelList ) ) {
01726                             $aSelectlist[$conkey][$iSel] = $aSelectlist[$conkey][$iSel];
01727                             $aSelectlist[$conkey][$iSel]->selected = 1;
01728                         }
01729                         $oProduct->setSelectlist( $aSelectlist );
01730                     }
01731                 }
01732             } catch ( oxNoArticleException $oEx ) {
01733                 oxUtilsView::getInstance()->addErrorToDisplay( $oEx );
01734                 $this->removeItem( $sItemKey );
01735                 $this->calculateBasket( true );
01736                 continue;
01737             } catch ( oxArticleInputException $oEx ) {
01738                 oxUtilsView::getInstance()->addErrorToDisplay( $oEx );
01739                 $this->removeItem( $sItemKey );
01740                 $this->calculateBasket( true );
01741                 continue;
01742             }
01743 
01744             $aBasketArticles[$sItemKey] = $oProduct;
01745         }
01746         return $aBasketArticles;
01747     }
01748 
01754     public function getDiscountProductsPrice()
01755     {
01756         return $this->_oDiscountProductsPriceList;
01757     }
01758 
01764     public function getProductsPrice()
01765     {
01766         if ( is_null($this->_oProductsPriceList) ) {
01767             $this->_oProductsPriceList = oxNew( 'oxPriceList' );
01768         }
01769 
01770         return $this->_oProductsPriceList;
01771     }
01772 
01778     public function getPrice()
01779     {
01780         if ( is_null($this->_oPrice) ) {
01781             $this->_oPrice = oxNew( 'oxprice' );
01782         }
01783 
01784         return $this->_oPrice;
01785     }
01786 
01793     public function getOrderId()
01794     {
01795         return $this->_sOrderId;
01796     }
01797 
01805     public function setOrderId( $sId )
01806     {
01807         $this->_sOrderId = $sId;
01808     }
01809 
01818     public function getCosts( $sId = null )
01819     {
01820         // if user want some specific cost - return it
01821         if ( $sId ) {
01822             return isset( $this->_aCosts[$sId] )?$this->_aCosts[$sId]:null;
01823         }
01824         return $this->_aCosts;
01825     }
01826 
01832     public function getVouchers()
01833     {
01834         return $this->_aVouchers;
01835     }
01836 
01842     public function getProductsCount()
01843     {
01844         return $this->_iProductsCnt;
01845     }
01846 
01852     public function getItemsCount()
01853     {
01854         return $this->_dItemsCnt;
01855     }
01856 
01862     public function getWeight()
01863     {
01864         return $this->_dWeight;
01865     }
01866 
01872     public function getContents()
01873     {
01874         return $this->_aBasketContents;
01875     }
01876 
01884     public function getProductVats( $blFormatCurrency = true)
01885     {
01886         if ( !$this->_oNotDiscountedProductsPriceList ) {
01887             return array();
01888         }
01889 
01890         $aVats = $this->_oNotDiscountedProductsPriceList->getVatInfo();
01891 
01892         $oUtils = oxUtils::getInstance();
01893         foreach ( $this->_aDiscountedVats as $sKey => $dVat ) {
01894             if ( !isset( $aVats[$sKey] ) ) {
01895                 $aVats[$sKey] = 0;
01896             }
01897             // add prices of the same discounts
01898             $aVats[$sKey] += $oUtils->fRound( $dVat, $this->_oCurrency );
01899         }
01900 
01901         if ( $blFormatCurrency ) {
01902             $oLang = oxLang::getInstance();
01903             foreach ( $aVats as $sKey => $dVat ) {
01904                 $aVats[$sKey] = $oLang->formatCurrency( $dVat, $this->getBasketCurrency() );
01905             }
01906         }
01907 
01908         return $aVats;
01909     }
01910 
01916     public function getDiscountedNettoPrice()
01917     {
01918         if ( $this->_oNotDiscountedProductsPriceList ) {
01919             return $this->_dDiscountedProductNettoPrice + $this->_oNotDiscountedProductsPriceList->getNettoSum();
01920         }
01921         return false;
01922     }
01923 
01931     public function setCardMessage( $sMessage )
01932     {
01933         $this->_sCardMessage = $sMessage;
01934     }
01935 
01941     public function getCardMessage()
01942     {
01943         return $this->_sCardMessage;
01944     }
01945 
01953     public function setCardId( $sCardId )
01954     {
01955         $this->_sCardId = $sCardId;
01956     }
01957 
01963     public function getCardId()
01964     {
01965         return $this->_sCardId;
01966     }
01967 
01973     public function getCard()
01974     {
01975         $oCard = null;
01976         if ( $sCardId = $this->getCardId() ) {
01977             $oCard = oxNew( 'oxwrapping' );
01978             $oCard->load( $sCardId );
01979         }
01980         return $oCard;
01981     }
01982 
01988     public function getTotalDiscount()
01989     {
01990         return $this->_oTotalDiscount;
01991     }
01992 
01998     public function getDiscounts()
01999     {
02000         if ( $this->getTotalDiscount() && $this->getTotalDiscount()->getBruttoPrice() == 0 && count($this->_aItemDiscounts) == 0) {
02001             return null;
02002         }
02003 
02004         //#3857 this section is not needed as $this->_aItemDiscounts is part of $this->_aDiscounts already
02005         //return array_merge($this->_aItemDiscounts, $this->_aDiscounts);
02006         return $this->_aDiscounts;
02007     }
02008 
02014     public function getVoucherDiscount()
02015     {
02016         if ($this->getConfig()->getConfigParam( 'bl_showVouchers' )) {
02017             return $this->_oVoucherDiscount;
02018         }
02019         return null;
02020     }
02021 
02029     public function setBasketCurrency( $oCurrency )
02030     {
02031         $this->_oCurrency = $oCurrency;
02032     }
02033 
02039     public function getBasketCurrency()
02040     {
02041         if ( $this->_oCurrency === null ) {
02042             $this->_oCurrency = $this->getConfig()->getActShopCurrencyObject();
02043         }
02044 
02045         return $this->_oCurrency;
02046     }
02047 
02055     public function setSkipVouchersChecking( $blSkipChecking = null )
02056     {
02057         $this->_blSkipVouchersAvailabilityChecking = $blSkipChecking;
02058     }
02059 
02065     public function hasSkipedDiscount()
02066     {
02067         return $this->_blSkipDiscounts;
02068     }
02069 
02077     public function setSkipDiscounts( $blSkip )
02078     {
02079         $this->_blSkipDiscounts = $blSkip;
02080     }
02081 
02087     public function getProductsNetPrice()
02088     {
02089         return oxLang::getInstance()->formatCurrency( $this->getDiscountedNettoPrice(), $this->getBasketCurrency() );
02090     }
02091 
02097     public function getFProductsPrice()
02098     {
02099         if ( $this->_oProductsPriceList ) {
02100             return oxLang::getInstance()->formatCurrency( $this->_oProductsPriceList->getBruttoSum(), $this->getBasketCurrency() );
02101         }
02102         return null;
02103     }
02104 
02110     public function getDelCostVatPercent()
02111     {
02112         return $this->getCosts( 'oxdelivery' )->getVat();
02113     }
02114 
02120     public function getDelCostVat()
02121     {
02122         $dDelVAT = $this->getCosts( 'oxdelivery' )->getVatValue();
02123 
02124         // blShowVATForDelivery option will be used, only for displaying, but not calculation
02125         if ( $dDelVAT > 0 && $this->getConfig()->getConfigParam( 'blShowVATForDelivery' )) {
02126             return oxLang::getInstance()->formatCurrency( $dDelVAT, $this->getBasketCurrency() );
02127         }
02128         return false;
02129     }
02130 
02136     public function getDelCostNet()
02137     {
02138         $oConfig = $this->getConfig();
02139 
02140         // blShowVATForDelivery option will be used, only for displaying, but not calculation
02141         if ( $oConfig->getConfigParam( 'blShowVATForDelivery' ) && ( $this->getBasketUser() || $oConfig->getConfigParam( 'blCalculateDelCostIfNotLoggedIn' ) ) ) {
02142             $dNetPrice = $this->getCosts( 'oxdelivery' )->getNettoPrice();
02143             if ( $dNetPrice > 0 ) {
02144                 return oxLang::getInstance()->formatCurrency( $dNetPrice, $this->getBasketCurrency() );
02145             }
02146         }
02147         return false;
02148     }
02149 
02155     public function getPayCostVatPercent()
02156     {
02157         return $this->getCosts( 'oxpayment' )->getVat();
02158     }
02159 
02165     public function getPayCostVat()
02166     {
02167         $dPayVAT = $this->getCosts( 'oxpayment' )->getVatValue();
02168 
02169         // blShowVATForPayCharge option will be used, only for displaying, but not calculation
02170         if ( $dPayVAT > 0 && $this->getConfig()->getConfigParam( 'blShowVATForPayCharge' ) ) {
02171             return oxLang::getInstance()->formatCurrency( $dPayVAT, $this->getBasketCurrency() );
02172         }
02173         return false;
02174     }
02175 
02181     public function getPayCostNet()
02182     {
02183         // blShowVATForPayCharge option will be used, only for displaying, but not calculation
02184         if ( $this->getConfig()->getConfigParam( 'blShowVATForPayCharge' ) ) {
02185             $dNetPrice = $this->getCosts( 'oxpayment' )->getNettoPrice();
02186             if ($dNetPrice) {
02187                 return oxLang::getInstance()->formatCurrency( $dNetPrice, $this->getBasketCurrency() );
02188             }
02189         }
02190         return false;
02191     }
02192 
02198     public function getPaymentCosts()
02199     {
02200         $oPaymentCost = $this->getCosts( 'oxpayment' );
02201         if ( $oPaymentCost && $oPaymentCost->getBruttoPrice() ) {
02202             return $oPaymentCost->getBruttoPrice();
02203         }
02204         return false;
02205     }
02206 
02212     public function getFPaymentCosts()
02213     {
02214         $oPaymentCost = $this->getCosts( 'oxpayment' );
02215 
02216         if ( $oPaymentCost && $oPaymentCost->getBruttoPrice() ) {
02217             return oxLang::getInstance()->formatCurrency( $oPaymentCost->getBruttoPrice(), $this->getBasketCurrency() );
02218         }
02219         return false;
02220     }
02221 
02227     public function getVoucherDiscValue()
02228     {
02229         if ( $this->getVoucherDiscount() ) {
02230             return $this->getVoucherDiscount()->getBruttoPrice();
02231         }
02232         return false;
02233     }
02234 
02240     public function getFVoucherDiscountValue()
02241     {
02242         if ( $oVoucherDiscount = $this->getVoucherDiscount() ) {
02243             if ( $oVoucherDiscount->getBruttoPrice() ) {
02244                 return oxLang::getInstance()->formatCurrency( $oVoucherDiscount->getBruttoPrice(), $this->getBasketCurrency() );
02245             }
02246         }
02247         return false;
02248     }
02249 
02250 
02256     public function getWrappCostVatPercent()
02257     {
02258         return $this->getCosts( 'oxwrapping' )->getVat();
02259     }
02260 
02266     public function getWrappCostVat()
02267     {
02268         $dWrappVAT = $this->getCosts( 'oxwrapping' )->getVatValue();
02269         // blShowVATForWrapping option will be used, only for displaying, but not calculation
02270         if ( $dWrappVAT > 0 && $this->getConfig()->getConfigParam( 'blShowVATForWrapping' ) ) {
02271             return oxLang::getInstance()->formatCurrency( $dWrappVAT, $this->getBasketCurrency() );
02272         }
02273         return false;
02274 
02275     }
02276 
02282     public function getWrappCostNet()
02283     {
02284         $dWrappNet = $this->getCosts( 'oxwrapping' )->getNettoPrice();
02285         // blShowVATForWrapping option will be used, only for displaying, but not calculation
02286         if ( $dWrappNet > 0 && $this->getConfig()->getConfigParam( 'blShowVATForWrapping' ) ) {
02287             return  oxLang::getInstance()->formatCurrency( $dWrappNet, $this->getBasketCurrency() );
02288         }
02289         return false;
02290     }
02291 
02297     public function getFWrappingCosts()
02298     {
02299         $oWrappingCost = $this->getCosts( 'oxwrapping' );
02300         if ( $oWrappingCost && $oWrappingCost->getBruttoPrice() ) {
02301             return oxLang::getInstance()->formatCurrency( $oWrappingCost->getBruttoPrice(), $this->getBasketCurrency() );
02302         }
02303         return false;
02304     }
02305 
02311     public function getFPrice()
02312     {
02313         return oxLang::getInstance()->formatCurrency( $this->getPrice()->getBruttoPrice(), $this->getBasketCurrency() );
02314     }
02315 
02321     public function getFDeliveryCosts()
02322     {
02323         $oDeliveryCost = $this->getCosts( 'oxdelivery' );
02324         if ( $oDeliveryCost && ( $this->getBasketUser() || $this->getConfig()->getConfigParam( 'blCalculateDelCostIfNotLoggedIn' ) ) ) {
02325             return oxLang::getInstance()->formatCurrency( $oDeliveryCost->getBruttoPrice(), $this->getBasketCurrency() );
02326         }
02327         return false;
02328     }
02329 
02335     public function getDeliveryCosts()
02336     {
02337         if ( $oDeliveryCost = $this->getCosts( 'oxdelivery' ) ) {
02338             return $oDeliveryCost->getBruttoPrice();
02339         }
02340         return false;
02341     }
02342 
02350     public function setTotalDiscount( $dDiscount )
02351     {
02352         $this->_oTotalDiscount = oxNew( 'oxPrice' );
02353         $this->_oTotalDiscount->setBruttoPriceMode();
02354         $this->_oTotalDiscount->add( $dDiscount );
02355     }
02356 
02363     public function getPriceForPayment()
02364     {
02365         $dPrice = $this->getDiscountedProductsBruttoPrice();
02366         //#1905 not discounted products should be included in payment amount calculation
02367         if ( $oPriceList = $this->getNotDiscountProductsPrice() ) {
02368             $dPrice += $oPriceList->getBruttoSum();
02369         }
02370 
02371         // adding delivery price to final price
02372         if ( $oDeliveryPrice = $this->_aCosts['oxdelivery'] ) {
02373             $dPrice += $oDeliveryPrice->getBruttoPrice();
02374         }
02375 
02376         return $dPrice;
02377     }
02378 
02384     public function getDiscountedProductsBruttoPrice()
02385     {
02386         if ( $oProductsPrice = $this->getDiscountProductsPrice() ) {
02387             $dPrice = $oProductsPrice->getBruttoSum();
02388         }
02389 
02390         // substracting total discount
02391         if ( $oPrice = $this->getTotalDiscount() ) {
02392             $dPrice -= $oPrice->getBruttoPrice();
02393         }
02394 
02395         if ( $oVoucherPrice = $this->getVoucherDiscount() ) {
02396             $dPrice -= $oVoucherPrice->getBruttoPrice();
02397         }
02398 
02399         return $dPrice;
02400     }
02401 
02407     public function isBelowMinOrderPrice()
02408     {
02409         $blIsBelowMinOrderPrice = false;
02410         $sConfValue = $this->getConfig()->getConfigParam( 'iMinOrderPrice' );
02411         if ( is_numeric($sConfValue) && $this->getProductsCount() ) {
02412             $dMinOrderPrice = oxPrice::getPriceInActCurrency( ( int ) $sConfValue );
02413             $dNotDiscountedProductPrice = 0;
02414             if ( $oPrice = $this->getNotDiscountProductsPrice() ) {
02415                 $dNotDiscountedProductPrice = $oPrice->getBruttoSum();
02416             }
02417             $blIsBelowMinOrderPrice = ($dMinOrderPrice > ($this->getDiscountedProductsBruttoPrice() + $dNotDiscountedProductPrice));
02418         }
02419 
02420         return $blIsBelowMinOrderPrice;
02421 
02422     }
02423 
02432     public function getArtStockInBasket( $sArtId, $sExpiredArtId = null )
02433     {
02434         $dArtStock = 0;
02435         foreach ( $this->_aBasketContents as $sItemKey => $oOrderArticle ) {
02436             if ( $oOrderArticle && ( $sExpiredArtId == null || $sExpiredArtId != $sItemKey ) ) {
02437                 if ( $oOrderArticle->getArticle( true )->getId() == $sArtId ) {
02438                     $dArtStock += $oOrderArticle->getAmount();
02439                 }
02440             }
02441         }
02442 
02443         return $dArtStock;
02444     }
02445 
02453     public function canAddProductToBasket( $sProductId )
02454     {
02455         $blCanAdd = null;
02456 
02457         // if basket category is not set..
02458         if ( $this->_sBasketCategoryId === null ) {
02459             $oCat = null;
02460 
02461             // request category
02462             if ( $oView = $this->getConfig()->getActiveView() ) {
02463                 if ( $oCat = $oView->getActCategory() ) {
02464                     if ( !$this->_isProductInRootCategory( $sProductId, $oCat->oxcategories__oxrootid->value ) ) {
02465                         $oCat = null;
02466                     } else {
02467                         $blCanAdd = true;
02468                     }
02469                 }
02470             }
02471 
02472             // product main category
02473             if ( !$oCat ) {
02474                 $oProduct = oxNew( "oxarticle" );
02475                 if ( $oProduct->load( $sProductId ) ) {
02476                     $oCat = $oProduct->getCategory();
02477                 }
02478             }
02479 
02480             // root category id
02481             if ( $oCat ) {
02482                 $this->setBasketRootCatId($oCat->oxcategories__oxrootid->value);
02483             }
02484         }
02485 
02486         // avoiding double check..
02487         if ( $blCanAdd === null ) {
02488             $blCanAdd = $this->_sBasketCategoryId ? $this->_isProductInRootCategory( $sProductId, $this->getBasketRootCatId() ) : true;
02489         }
02490 
02491         return $blCanAdd;
02492     }
02493 
02502     protected function _isProductInRootCategory( $sProductId, $sRootCatId )
02503     {
02504         $sO2CTable = getViewName( 'oxobject2category' );
02505         $sCatTable = getViewName( 'oxcategories' );
02506 
02507         $oDb = oxDb::getDb();
02508         $sParentId  = $oDb->getOne( "select oxparentid from oxarticles where oxid = ".$oDb->quote( $sProductId ) );
02509         $sProductId = $sParentId ? $sParentId : $sProductId;
02510 
02511         $sQ = "select 1 from {$sO2CTable}
02512                  left join {$sCatTable} on {$sCatTable}.oxid = {$sO2CTable}.oxcatnid
02513                  where {$sO2CTable}.oxobjectid = ".$oDb->quote( $sProductId )." and
02514                        {$sCatTable}.oxrootid = ".$oDb->quote( $sRootCatId );
02515 
02516         return (bool) $oDb->getOne( $sQ );
02517     }
02518 
02526     public function setBasketRootCatId($sRoot)
02527     {
02528         $this->_sBasketCategoryId = $sRoot;
02529     }
02530 
02536     public function getBasketRootCatId()
02537     {
02538         return $this->_sBasketCategoryId;
02539     }
02540 
02548     public function setCatChangeWarningState( $blShow )
02549     {
02550         $this->_blShowCatChangeWarning = $blShow;
02551     }
02552 
02558     public function showCatChangeWarning()
02559     {
02560         return $this->_blShowCatChangeWarning;
02561     }
02562 
02570     public function setTsProductId( $sProductId )
02571     {
02572         $this->_sTsProductId = $sProductId;
02573     }
02574 
02580     public function getTsProductId()
02581     {
02582         return $this->_sTsProductId;
02583     }
02584 
02590     public function getFTsProtectionCosts()
02591     {
02592         $oProtectionCost = $this->getCosts( 'oxtsprotection' );
02593         if ( $oProtectionCost && $oProtectionCost->getBruttoPrice() ) {
02594             return oxLang::getInstance()->formatCurrency( $oProtectionCost->getBruttoPrice(), $this->getBasketCurrency() );
02595         }
02596         return false;
02597     }
02598 
02604     public function getTsProtectionVatPercent()
02605     {
02606         return $this->getCosts( 'oxtsprotection' )->getVat();
02607     }
02608 
02614     public function getTsProtectionVat()
02615     {
02616         $dProtectionVAT = $this->getCosts( 'oxtsprotection' )->getVatValue();
02617         // blShowVATForPayCharge option will be used, only for displaying, but not calculation
02618         if ( $dProtectionVAT > 0 && $this->getConfig()->getConfigParam( 'blShowVATForPayCharge' ) ) {
02619             return oxLang::getInstance()->formatCurrency( $dProtectionVAT, $this->getBasketCurrency() );
02620         }
02621         return false;
02622     }
02623 
02629     public function getTsProtectionNet()
02630     {
02631         // blShowVATForPayCharge option will be used, only for displaying, but not calculation
02632         if ( $this->getConfig()->getConfigParam( 'blShowVATForPayCharge' ) ) {
02633             return oxLang::getInstance()->formatCurrency( $this->getCosts( 'oxtsprotection' )->getNettoPrice(), $this->getBasketCurrency() );
02634         }
02635         return false;
02636     }
02637 
02643     public function getTsProtectionCosts()
02644     {
02645         $oProtection = $this->getCosts( 'oxtsprotection' );
02646         if ( $oProtection ) {
02647             return $oProtection->getBruttoPrice();
02648         }
02649         return false;
02650     }
02651 
02657     public function getNotDiscountProductsPrice()
02658     {
02659         return $this->_oNotDiscountedProductsPriceList;
02660     }
02661 
02675     protected function _addedNewItem( $sProductID, $dAmount, $aSel, $aPersParam, $blOverride, $blBundle, $sOldBasketItemId )
02676     {
02677         if ( !$blOverride ) {
02678             $this->_blNewITemAdded = null;
02679             oxSession::setVar( "blAddedNewItem", true );
02680         }
02681     }
02682 
02688     public function __wakeUp()
02689     {
02690         $this->_blNewITemAdded = null;
02691     }
02692 
02698     public function isNewItemAdded()
02699     {
02700         if ( $this->_blNewITemAdded == null ) {
02701             $this->_blNewITemAdded = (bool) oxSession::getVar( "blAddedNewItem" );
02702             oxSession::deleteVar( "blAddedNewItem" );
02703         }
02704         return $this->_blNewITemAdded;
02705     }
02706 
02712     public function hasDownloadableProducts()
02713     {
02714         $this->_blDownloadableProducts = false;
02715         foreach ( $this->_aBasketContents as $sItemKey => $oOrderArticle ) {
02716             //#4411 getArticle() might throw an Exception
02717             try {
02718                 if ( $oOrderArticle->getArticle( true )->isDownloadable() ) {
02719                     $this->_blDownloadableProducts = true;
02720                     break;
02721                 }
02722             } catch(Exception $oE) {
02723                 //#4411
02724                 //in case product in getArticle() is not available we get an exception
02725                 //there is nothing what we need to do here, just catch the exception and continue checking other products
02726             }
02727         }
02728 
02729         return $this->_blDownloadableProducts;
02730     }
02731 
02732 }