oxarticle.php

Go to the documentation of this file.
00001 <?php
00002 
00003 // defining supported link types
00004 define( 'OXARTICLE_LINKTYPE_CATEGORY', 0 );
00005 define( 'OXARTICLE_LINKTYPE_VENDOR', 1 );
00006 define( 'OXARTICLE_LINKTYPE_MANUFACTURER', 2 );
00007 define( 'OXARTICLE_LINKTYPE_PRICECATEGORY', 3 );
00008 define( 'OXARTICLE_LINKTYPE_TAG', 4 );
00009 define( 'OXARTICLE_LINKTYPE_RECOMM', 5 );
00010 
00017 class oxArticle extends oxI18n implements oxIArticle, oxIUrl
00018 {
00019 
00025     protected $_sClassName = 'oxarticle';
00026 
00032     protected $_blUseLazyLoading = true;
00033 
00039     protected $_sItemKey;
00040 
00046     protected $_blCalcPrice    = true;
00047 
00052     protected $_oPrice      = null;
00053 
00054 
00060     protected $_dVarMinPrice = null;
00061 
00067     protected $_dVarMaxPrice = null;
00068 
00074     protected $_dArticleVat = null;
00075 
00081     protected $_aPersistParam  = null;
00082 
00088     protected $_blNotBuyable   = false;
00089 
00096     protected $_blLoadVariants = true;
00097 
00103     protected $_aVariants = null;
00104 
00110     protected $_aVariantsWithNotOrderables = null;
00111 
00120     protected $_blNotBuyableParent  = false;
00121 
00122 
00126     protected $_blHasVariants = false;
00127 
00131     protected $_blHasMdVariants = false;
00132 
00137     protected $_blIsOnComparisonList = false;
00138 
00143     protected $_oUser = null;
00144 
00150     protected $_blLoadPrice = true;
00151 
00158     protected $_blSkipAbPrice = false;
00159 
00166     protected $_fPricePerUnit = null;
00167 
00171     protected $_blLoadParentData = false;
00172 
00176     protected $_blAllowEmptyParentId = false;
00177 
00181     protected $_blSkipAssign = false;
00182 
00188     protected $_blSkipDiscounts = null;
00189 
00194     protected $_oAttributeList = null;
00195 
00196 
00202     protected $_blIsRangePrice = null;
00203 
00209     protected $_aMediaUrls = null;
00210 
00216     static protected $_aLoadedParents;
00217 
00223     static protected $_aSelList;
00224 
00230     protected $_aDispSelList;
00231 
00237     protected $_blIsSeoObject = true;
00238 
00244     protected $_oAmountPriceList = null;
00245 
00254     protected $_iLinkType = 0;
00255 
00261     protected $_aStdUrls = array();
00262 
00268     protected $_aSeoUrls = array();
00269 
00275     protected $_aSeoAddParams = array();
00276 
00282     protected $_aStdAddParams = array();
00283 
00289     protected $_sDynImageDir = null;
00290 
00296     protected $_sMoreDetailLink = null;
00297 
00303     protected $_sToBasketLink = null;
00304 
00310     protected $_iStockStatusOnLoad = null;
00311 
00317     protected $_iStockStatus = null;
00318 
00324     protected $_oTPrice = null;
00325 
00331     protected $_oAmountPriceInfo = null;
00332 
00338     protected $_dAmountPrice = null;
00339 
00345     protected static $_aArticleManufacturers = array();
00346 
00352     protected static $_aArticleVendors = array();
00353 
00359     protected static $_aArticleCats = array();
00360 
00366     protected $_aNonCopyParentFields = array('oxarticles__oxinsert',
00367                                              'oxarticles__oxtimestamp',
00368                                              'oxarticles__oxnid',
00369                                              'oxarticles__oxid',
00370                                              'oxarticles__oxparentid');
00371 
00377     protected $_aCopyParentField = array('oxarticles__oxnonmaterial',
00378                                          'oxarticles__oxfreeshipping',
00379                                          //'oxarticles__oxremindactive',
00380                                          'oxarticles__oxisdownloadable');
00381 
00387     protected $_oMdVariants = null;
00388 
00394     protected $_oLongDesc = null;
00395 
00403     protected $_aVariantSelections = array();
00404 
00409     protected static $_aSelections = array();
00410 
00415     protected static $_aCategoryCache = null;
00416 
00421     protected static $_blHasAmountPrice = null;
00422 
00427     protected $_aArticleFiles = null;
00428 
00433     protected $_blCanUpdateAnyField = null;
00434 
00443     public function __construct($aParams = null)
00444     {
00445         if ( $aParams && is_array($aParams)) {
00446             foreach ( $aParams as $sParam => $mValue) {
00447                 $this->$sParam = $mValue;
00448             }
00449         }
00450         parent::__construct();
00451         $this->init( 'oxarticles' );
00452     }
00453 
00462     public function __get($sName)
00463     {
00464         $this->$sName = parent::__get($sName);
00465         if ( $this->$sName ) {
00466             // since the field could have been loaded via lazy loading
00467             $this->_assignParentFieldValue($sName);
00468         }
00469 
00470         return $this->$sName;
00471     }
00472 
00481     public function __set( $sName, $sValue )
00482     {
00483         parent::__set( $sName, $sValue );
00484     }
00485 
00492     public function isInList()
00493     {
00494         return $this->_isInList();
00495     }
00496 
00504     public function setId( $sId = null )
00505     {
00506         $sId = parent::setId( $sId );
00507 
00508         // TODO: in oxbase::setId make it to check if exists and update, not recreate, then delete this overload
00509         $this->oxarticles__oxnid = $this->oxarticles__oxid;
00510 
00511         return $sId;
00512     }
00513 
00523     public function getActiveCheckQuery( $blForceCoreTable = null )
00524     {
00525         $sTable = $this->getViewName( $blForceCoreTable );
00526 
00527         // check if article is still active
00528         $sQ = " $sTable.oxactive = 1 ";
00529 
00530         // enabled time range check ?
00531         if ( $this->getConfig()->getConfigParam( 'blUseTimeCheck' ) ) {
00532             $sDate = date( 'Y-m-d H:i:s', oxRegistry::get("oxUtilsDate")->getTime() );
00533             $sQ = "( $sQ or ( $sTable.oxactivefrom < '$sDate' and $sTable.oxactiveto > '$sDate' ) ) ";
00534         }
00535 
00536         return $sQ;
00537     }
00538 
00552     public function getStockCheckQuery( $blForceCoreTable = null )
00553     {
00554         $myConfig = $this->getConfig();
00555         $sTable = $this->getViewName( $blForceCoreTable );
00556 
00557         $sQ = "";
00558 
00559         //do not check for variants
00560         if ( $myConfig->getConfigParam( 'blUseStock' ) ) {
00561             $sQ = " and ( $sTable.oxstockflag != 2 or ( $sTable.oxstock + $sTable.oxvarstock ) > 0  ) ";
00562             //V #M513: When Parent article is not purchasable, it's visibility should be displayed in shop only if any of Variants is available.
00563             if ( !$myConfig->getConfigParam( 'blVariantParentBuyable' ) ) {
00564                 $sTimeCheckQ = '';
00565                 if ( $myConfig->getConfigParam( 'blUseTimeCheck' ) ) {
00566                      $sDate = date( 'Y-m-d H:i:s', oxRegistry::get("oxUtilsDate")->getTime() );
00567                      $sTimeCheckQ = " or ( art.oxactivefrom < '$sDate' and art.oxactiveto > '$sDate' )";
00568                 }
00569                 $sQ = " $sQ and IF( $sTable.oxvarcount = 0, 1, ( select 1 from $sTable as art where art.oxparentid=$sTable.oxid and ( art.oxactive = 1 $sTimeCheckQ ) and ( art.oxstockflag != 2 or art.oxstock > 0 ) limit 1 ) ) ";
00570             }
00571         }
00572 
00573         return $sQ;
00574     }
00575 
00587     public function getVariantsQuery( $blRemoveNotOrderables, $blForceCoreTable = null  )
00588     {
00589         $sTable = $this->getViewName( $blForceCoreTable );
00590         $sQ = " and $sTable.oxparentid = '".$this->getId()."' ";
00591 
00592         //checking if variant is active and stock status
00593         if ( $this->getConfig()->getConfigParam( 'blUseStock' ) ) {
00594             $sQ .= " and ( $sTable.oxstock > 0 or ( $sTable.oxstock <= 0 and $sTable.oxstockflag != 2 ";
00595             if ( $blRemoveNotOrderables ) {
00596                 $sQ .= " and $sTable.oxstockflag != 3 ";
00597             }
00598             $sQ .= " ) ) ";
00599         }
00600 
00601         return $sQ;
00602     }
00603 
00611     public function getSqlActiveSnippet( $blForceCoreTable = null )
00612     {
00613         $myConfig = $this->getConfig();
00614 
00615         // check if article is still active
00616         $sQ = $this->getActiveCheckQuery( $blForceCoreTable );
00617 
00618         // stock and variants check
00619         $sQ .= $this->getStockCheckQuery( $blForceCoreTable );
00620 
00621 
00622         return "( $sQ ) ";
00623     }
00624 
00632     public function setSkipAssign($blSkipAssign)
00633     {
00634         $this->_blSkipAssign = $blSkipAssign;
00635     }
00636 
00642     public function disablePriceLoad()
00643     {
00644         $this->_blLoadPrice = false;
00645     }
00646 
00652     public function enablePriceLoad()
00653     {
00654         $this->_blLoadPrice = true;
00655     }
00656 
00662     public function getItemKey()
00663     {
00664         return $this->_sItemKey;
00665     }
00666 
00674     public function setItemKey($sItemKey)
00675     {
00676         $this->_sItemKey = $sItemKey;
00677     }
00678 
00686     public function setNoVariantLoading( $blLoadVariants )
00687     {
00688         $this->_blLoadVariants = !$blLoadVariants;
00689     }
00690 
00696     public function isBuyable()
00697     {
00698         if ($this->_blNotBuyableParent) {
00699             return false;
00700         }
00701 
00702         return !$this->_blNotBuyable;
00703     }
00704 
00710     public function getPersParams()
00711     {
00712         return $this->_aPersistParam;
00713     }
00714 
00720     public function isOnComparisonList()
00721     {
00722         return $this->_blIsOnComparisonList;
00723     }
00724 
00732     public function setOnComparisonList( $blOnList )
00733     {
00734         $this->_blIsOnComparisonList = $blOnList;
00735     }
00736 
00744     public function setLoadParentData($blLoadParentData)
00745     {
00746         $this->_blLoadParentData = $blLoadParentData;
00747     }
00748 
00758     public function setSkipAbPrice( $blSkipAbPrice = null )
00759     {
00760         $this->_blSkipAbPrice = $blSkipAbPrice;
00761     }
00762 
00763 
00771     public function isMultilingualField($sFieldName)
00772     {
00773         switch ($sFieldName) {
00774             case "oxlongdesc":
00775             case "oxtags":
00776                 return true;
00777         }
00778 
00779         return parent::isMultilingualField($sFieldName);
00780     }
00781 
00787     public function isVisible()
00788     {
00789 
00790         // admin preview mode
00791         if ( ( $blCanPreview = oxRegistry::getUtils()->canPreview() ) !== null ) {
00792             return $blCanPreview;
00793         }
00794 
00795         // active ?
00796         $sNow = date('Y-m-d H:i:s');
00797         if ( !$this->oxarticles__oxactive->value &&
00798              (  $this->oxarticles__oxactivefrom->value > $sNow ||
00799                 $this->oxarticles__oxactiveto->value < $sNow
00800              )) {
00801             return false;
00802         }
00803 
00804         // stock flags
00805         if ( $this->getConfig()->getConfigParam( 'blUseStock' ) && $this->oxarticles__oxstockflag->value == 2) {
00806             $iOnStock = $this->oxarticles__oxstock->value + $this->oxarticles__oxvarstock->value;
00807             if ($this->getConfig()->getConfigParam( 'blPsBasketReservationEnabled' )) {
00808                 $iOnStock += $this->getSession()->getBasketReservations()->getReservedAmount($this->getId());
00809             }
00810             if ( $iOnStock <= 0 ) {
00811                 return false;
00812             }
00813         }
00814 
00815         return true;
00816     }
00817 
00826     public function assign( $aRecord )
00827     {
00828 
00829         startProfile('articleAssign');
00830 
00831         // load object from database
00832         parent::assign( $aRecord );
00833 
00834         $this->oxarticles__oxnid = $this->oxarticles__oxid;
00835 
00836         // check for simple article.
00837         if ($this->_blSkipAssign) {
00838             return;
00839         }
00840 
00841         $this->_assignParentFieldValues();
00842         $this->_assignNotBuyableParent();
00843 
00844 
00845         $this->_assignStock();
00846         $this->_assignPersistentParam();
00847         $this->_assignDynImageDir();
00848         $this->_assignComparisonListFlag();
00849 
00850 
00851         stopProfile('articleAssign');
00852     }
00853 
00854 
00862     protected function _loadFromDb( $sOXID )
00863     {
00864         $sSelect = $this->buildSelectString( array( $this->getViewName().".oxid" => $sOXID ));
00865 
00866         $aData = oxDb::getDb( oxDb::FETCH_MODE_ASSOC )->getRow( $sSelect );
00867 
00868         return $aData;
00869     }
00870 
00881     public function load( $sOXID )
00882     {
00883         // A. #1325 resetting to avoid problems when reloading (details etc)
00884         $this->_blNotBuyableParent = false;
00885 
00886 
00887             $aData = $this->_loadFromDb( $sOXID );
00888 
00889         if ( $aData ) {
00890             $this->assign( $aData );
00891             // convert date's to international format
00892 
00893             $this->_iStockStatusOnLoad = $this->_iStockStatus;
00894 
00895             $this->_isLoaded = true;
00896             return true;
00897         }
00898 
00899         return false;
00900     }
00901 
00902 
00910     public function addToRatingAverage( $iRating )
00911     {
00912         $dOldRating = $this->oxarticles__oxrating->value;
00913         $dOldCnt    = $this->oxarticles__oxratingcnt->value;
00914         $this->oxarticles__oxrating->setValue(( $dOldRating * $dOldCnt + $iRating ) / ($dOldCnt + 1));
00915         $this->oxarticles__oxratingcnt->setValue($dOldCnt + 1);
00916         $dRating = ( $dOldRating * $dOldCnt + $iRating ) / ($dOldCnt + 1);
00917         $dRatingCnt = (int) ($dOldCnt + 1);
00918         // oxarticles.oxtimestamp = oxarticles.oxtimestamp to keep old timestamp value
00919         $oDb = oxDb::getDb();
00920         $oDb->execute( 'update oxarticles set oxarticles.oxrating = '.$dRating.',oxarticles.oxratingcnt = '.$dRatingCnt.', oxarticles.oxtimestamp = oxarticles.oxtimestamp where oxarticles.oxid = '.$oDb->quote( $this->getId() ) );
00921 
00922     }
00923 
00931     public function setRatingAverage( $iRating )
00932     {
00933          $this->oxarticles__oxrating = new oxField( $iRating );
00934     }
00935 
00943     public function setRatingCount( $iRatingCnt )
00944     {
00945          $this->oxarticles__oxratingcnt = new oxField( $iRatingCnt );
00946     }
00947 
00955     public function getArticleRatingAverage( $blIncludeVariants = false )
00956     {
00957         if ( !$blIncludeVariants ) {
00958             return round( $this->oxarticles__oxrating->value, 1);
00959         } else {
00960             $oRating = oxNew( 'oxRating' );
00961             return $oRating->getRatingAverage( $this->getId(), 'oxarticle', $this->_getVariantsIds() );
00962         }
00963     }
00964 
00972     public function getArticleRatingCount( $blIncludeVariants = false )
00973     {
00974         if ( !$blIncludeVariants ) {
00975             return $this->oxarticles__oxratingcnt->value;
00976         } else {
00977             $oRating = oxNew( 'oxRating' );
00978             return $oRating->getRatingCount( $this->getId(), 'oxarticle', $this->_getVariantsIds() );
00979         }
00980     }
00981 
00982 
00988     public function getReviews()
00989     {
00990         $aIds = array($this->getId());
00991 
00992         if ( $this->oxarticles__oxparentid->value ) {
00993             $aIds[] = $this->oxarticles__oxparentid->value;
00994         }
00995 
00996         // showing variant reviews ..
00997         if ( $this->getConfig()->getConfigParam( 'blShowVariantReviews' ) ) {
00998             $aAdd = $this->_getVariantsIds();
00999             if (is_array($aAdd)) {
01000                 $aIds = array_merge($aIds, $aAdd);
01001             }
01002         }
01003 
01004         $oReview = oxNew('oxreview');
01005         $oRevs = $oReview->loadList('oxarticle', $aIds);
01006 
01007         //if no review found, return null
01008         if ( $oRevs->count() < 1 ) {
01009             return null;
01010         }
01011 
01012         return $oRevs;
01013     }
01014 
01020     public function getCrossSelling()
01021     {
01022         $oCrosslist = oxNew( "oxarticlelist");
01023         $oCrosslist->loadArticleCrossSell($this->oxarticles__oxid->value);
01024         if ( $oCrosslist->count() ) {
01025             return $oCrosslist;
01026         }
01027     }
01028 
01034     public function getAccessoires()
01035     {
01036         $myConfig = $this->getConfig();
01037 
01038         // Performance
01039         if ( !$myConfig->getConfigParam( 'bl_perfLoadAccessoires' ) ) {
01040             return;
01041         }
01042 
01043         $oAcclist = oxNew( "oxarticlelist");
01044         $oAcclist->setSqlLimit( 0, $myConfig->getConfigParam( 'iNrofCrossellArticles' ));
01045         $oAcclist->loadArticleAccessoires($this->oxarticles__oxid->value);
01046 
01047         if ( $oAcclist->count()) {
01048             return $oAcclist;
01049         }
01050     }
01051 
01057     public function getSimilarProducts()
01058     {
01059         // Performance
01060         $myConfig = $this->getConfig();
01061         if ( !$myConfig->getConfigParam( 'bl_perfLoadSimilar' ) ) {
01062             return;
01063         }
01064 
01065         $sArticleTable = $this->getViewName();
01066 
01067         $sAttribs = '';
01068         $iCnt = 0;
01069         $this->_getAttribsString($sAttribs, $iCnt);
01070 
01071         if ( !$sAttribs) {
01072             return null;
01073         }
01074 
01075         $aList = $this->_getSimList($sAttribs, $iCnt);
01076 
01077         if ( count( $aList ) ) {
01078             uasort( $aList, 'cmpart');
01079 
01080             $sSearch = $this->_generateSimListSearchStr($sArticleTable, $aList);
01081 
01082             $oSimilarlist = oxNew( 'oxarticlelist' );
01083             $oSimilarlist->setSqlLimit( 0, $myConfig->getConfigParam( 'iNrofSimilarArticles' ));
01084             $oSimilarlist->selectString( $sSearch);
01085 
01086             return $oSimilarlist;
01087         }
01088     }
01089 
01095     public function getCustomerAlsoBoughtThisProducts()
01096     {
01097         // Performance
01098         $myConfig = $this->getConfig();
01099         if ( !$myConfig->getConfigParam( 'bl_perfLoadCustomerWhoBoughtThis' ) ) {
01100             return;
01101         }
01102 
01103         // selecting products that fits
01104         $sQ = $this->_generateSearchStrForCustomerBought();
01105 
01106         $oArticles = oxNew( 'oxarticlelist' );
01107         $oArticles->setSqlLimit( 0, $myConfig->getConfigParam( 'iNrofCustomerWhoArticles' ));
01108         $oArticles->selectString( $sQ );
01109         if ( $oArticles->count() ) {
01110             return $oArticles;
01111         }
01112     }
01113 
01120     public function loadAmountPriceInfo()
01121     {
01122         $myConfig = $this->getConfig();
01123         if ( !$myConfig->getConfigParam( 'bl_perfLoadPrice' ) || !$this->_blLoadPrice || !$this->_blCalcPrice || !$this->hasAmountPrice() ) {
01124             return array();
01125         }
01126 
01127         if ( $this->_oAmountPriceInfo === null ) {
01128             $this->_oAmountPriceInfo = array();
01129             if ( count( ( $oAmPriceList = $this->_getAmountPriceList() ) ) ) {
01130                 $this->_oAmountPriceInfo = $this->_fillAmountPriceList( $oAmPriceList );
01131             }
01132         }
01133         return $this->_oAmountPriceInfo;
01134     }
01135 
01143     public function getSelectLists($sKeyPrefix = null)
01144     {
01145         //#1468C - more then one article in basket with different selectlist...
01146         //optionall function parameter $sKeyPrefix added, used only in basket.php
01147         $sKey = $this->getId();
01148         if ( isset( $sKeyPrefix ) ) {
01149             $sKey = $sKeyPrefix.'__'.$sKey;
01150         }
01151 
01152         if ( !isset( self::$_aSelList[$sKey] ) ) {
01153             $oDb = oxDb::getDb();
01154             $sSLViewName = getViewName( 'oxselectlist' );
01155 
01156             $sQ = "select {$sSLViewName}.* from oxobject2selectlist join {$sSLViewName} on $sSLViewName.oxid=oxobject2selectlist.oxselnid
01157                    where oxobject2selectlist.oxobjectid=%s order by oxobject2selectlist.oxsort";
01158 
01159             // all selectlists this article has
01160             $oLists = oxNew( 'oxlist' );
01161             $oLists->init( 'oxselectlist' );
01162             $oLists->selectString( sprintf( $sQ, $oDb->quote( $this->getId() ) ) );
01163 
01164             //#1104S if this is variant ant it has no selectlists, trying with parent
01165             if ( $oLists->count() == 0 && $this->oxarticles__oxparentid->value ) {
01166                 $oLists->selectString( sprintf( $sQ, $oDb->quote( $this->oxarticles__oxparentid->value ) ) );
01167             }
01168 
01169             // We do not need to calculate price here as there are method to get current article vat
01170             /*if ( $this->getPrice() != null ) {
01171                 $dVat = $this->getPrice()->getVat();
01172             }*/
01173             $dVat = $this->getArticleVat();
01174 
01175             $iCnt = 0;
01176             self::$_aSelList[$sKey] = array();
01177             foreach ( $oLists as $oSelectlist ) {
01178                 self::$_aSelList[$sKey][$iCnt] = $oSelectlist->getFieldList( $dVat );
01179                 self::$_aSelList[$sKey][$iCnt]['name'] = $oSelectlist->oxselectlist__oxtitle->value;
01180                 $iCnt++;
01181             }
01182         }
01183         return self::$_aSelList[$sKey];
01184     }
01185 
01193     protected function _hasAnyVariant( $blForceCoreTable = null )
01194     {
01195         $blHas = false;
01196         if ( ( $sId = $this->getId() ) ) {
01197             if ( $this->oxarticles__oxshopid->value == $this->getConfig()->getShopId() ) {
01198                 $blHas = (bool) $this->oxarticles__oxvarcount->value;
01199             } else {
01200                 $sArticleTable = $this->getViewName( $blForceCoreTable );
01201                 $blHas = (bool) oxDb::getDb()->getOne( "select 1 from $sArticleTable where oxparentid='{$sId}'" );
01202             }
01203         }
01204         return $blHas;
01205     }
01206 
01212     public function getVariantsCount()
01213     {
01214         return $this->oxarticles__oxvarcount->value;
01215     }
01216 
01222     public function hasMdVariants()
01223     {
01224         return $this->_blHasMdVariants;
01225     }
01226 
01236     public function getVariantSelections( $aFilterIds = null, $sActVariantId = null, $iLimit = 0 )
01237     {
01238         $iLimit = (int) $iLimit;
01239         if ( !isset( $this->_aVariantSelections[$iLimit] ) ) {
01240             $aVariantSelections = false;
01241             if ( $this->oxarticles__oxvarcount->value ) {
01242                 $oVariants = $this->getVariants( false );
01243                 $aVariantSelections = oxNew( "oxVariantHandler" )->buildVariantSelections( $this->oxarticles__oxvarname->getRawValue(), $oVariants, $aFilterIds, $sActVariantId, $iLimit );
01244 
01245                 if ( !empty($oVariants) && empty( $aVariantSelections['rawselections'] ) ) {
01246                     $aVariantSelections = false;
01247                 }
01248             }
01249             $this->_aVariantSelections[$iLimit] = $aVariantSelections;
01250         }
01251 
01252         return $this->_aVariantSelections[$iLimit];
01253     }
01254 
01263     public function getSelections( $iLimit = null, $aFilter = null )
01264     {
01265         $sId = $this->getId() . ( (int) $iLimit );
01266         if ( !array_key_exists( $sId, self::$_aSelections ) ) {
01267 
01268             $oDb = oxDb::getDb();
01269             $sSLViewName = getViewName( 'oxselectlist' );
01270 
01271             $sQ = "select {$sSLViewName}.* from oxobject2selectlist join {$sSLViewName} on $sSLViewName.oxid=oxobject2selectlist.oxselnid
01272                    where oxobject2selectlist.oxobjectid=%s order by oxobject2selectlist.oxsort";
01273 
01274             if ( ( $iLimit = (int) $iLimit ) ) {
01275                 $sQ .= " limit $iLimit ";
01276             }
01277 
01278             // vat value for price
01279             $dVat = 0;
01280             if ( ( $oPrice = $this->getPrice() ) != null ) {
01281                 $dVat = $oPrice->getVat();
01282             }
01283 
01284             // all selectlists this article has
01285             $oList = oxNew( 'oxlist' );
01286             $oList->init( 'oxselectlist' );
01287             $oList->getBaseObject()->setVat( $dVat );
01288             $oList->selectString( sprintf( $sQ, $oDb->quote( $this->getId() ) ) );
01289 
01290             //#1104S if this is variant and it has no selectlists, trying with parent
01291             if ( $oList->count() == 0 && $this->oxarticles__oxparentid->value ) {
01292                 $oList->selectString( sprintf( $sQ, $oDb->quote( $this->oxarticles__oxparentid->value ) ) );
01293             }
01294 
01295             self::$_aSelections[$sId] = $oList->count() ? $oList : false;
01296         }
01297 
01298         if ( self::$_aSelections[$sId] ) {
01299             // marking active from filter
01300             $aFilter = ( $aFilter === null ) ? oxConfig::getParameter( "sel" ) : $aFilter;
01301             if ( $aFilter ) {
01302                 $iSelIdx = 0;
01303                 foreach ( self::$_aSelections[$sId] as $oSelection ) {
01304                     if ( isset( $aFilter[$iSelIdx] ) ) {
01305                         $oSelection->setActiveSelectionByIndex( $aFilter[$iSelIdx] );
01306                     }
01307                     $iSelIdx++;
01308                 }
01309             }
01310         }
01311 
01312         return self::$_aSelections[$sId];
01313     }
01314 
01324     protected function _loadVariantList( $blSimple, $blRemoveNotOrderables = true, $blForceCoreTable = null )
01325     {
01326         $oVariants = array();
01327         if ( ( $sId = $this->getId() ) ) {
01328             //do not load me as a parent later
01329             self::$_aLoadedParents[$sId . "_" . $this->getLanguage()] = $this;
01330 
01331             $myConfig = $this->getConfig();
01332 
01333             if ( !$this->_blLoadVariants ||
01334                 ( !$this->isAdmin() && !$myConfig->getConfigParam( 'blLoadVariants') ) ||
01335                 ( !$this->isAdmin() && !$this->oxarticles__oxvarcount->value ) ) {
01336                 return $oVariants;
01337             }
01338 
01339             // cache
01340             $sCacheKey = $blSimple ? "simple" : "full";
01341             if ( $blRemoveNotOrderables ) {
01342                 if ( isset( $this->_aVariants[$sCacheKey] ) ) {
01343                    return $this->_aVariants[$sCacheKey];
01344                 } else {
01345                     $this->_aVariants[$sCacheKey] = & $oVariants;
01346                 }
01347             } elseif ( !$blRemoveNotOrderables ) {
01348                 if ( isset( $this->_aVariantsWithNotOrderables[$sCacheKey] ) ) {
01349                     return $this->_aVariantsWithNotOrderables[$sCacheKey];
01350                 } else {
01351                     $this->_aVariantsWithNotOrderables[$sCacheKey] = & $oVariants;
01352                 }
01353             }
01354 
01355             if ( ( $this->_blHasVariants = $this->_hasAnyVariant( $blForceCoreTable ) ) ) {
01356 
01357                 //load simple variants for lists
01358                 if ( $blSimple ) {
01359                     $oVariants = oxNew( 'oxsimplevariantlist' );
01360                     $oVariants->setParent( $this );
01361                 } else {
01362                     //loading variants
01363                     $oVariants = oxNew( 'oxarticlelist' );
01364                     $oVariants->getBaseObject()->modifyCacheKey( '_variants' );
01365                 }
01366 
01367                 startProfile("selectVariants");
01368                 $blUseCoreTable = (bool) $blForceCoreTable;
01369                 $oBaseObject = $oVariants->getBaseObject();
01370                 $oBaseObject->setLanguage( $this->getLanguage() );
01371 
01372 
01373                 $sArticleTable = $this->getViewName( $blUseCoreTable );
01374 
01375                 $sSelect = "select ".$oBaseObject->getSelectFields( $blUseCoreTable )." from $sArticleTable where " .
01376                            $this->getActiveCheckQuery( $blUseCoreTable ) .
01377                            $this->getVariantsQuery( $blRemoveNotOrderables, $blUseCoreTable ) .
01378                            " order by $sArticleTable.oxsort";
01379 
01380 
01381                 $oVariants->selectString( $sSelect );
01382 
01383                 //if this is multidimensional variants, make additional processing
01384                 if ( $myConfig->getConfigParam( 'blUseMultidimensionVariants' ) ) {
01385                     $oMdVariants = oxNew( "oxVariantHandler" );
01386                     $this->_blHasMdVariants = $oMdVariants->isMdVariant( $oVariants->current() );
01387                 }
01388                 stopProfile("selectVariants");
01389             }
01390 
01391             //if we have variants then depending on config option the parent may be non buyable
01392             if ( !$myConfig->getConfigParam( 'blVariantParentBuyable' ) && $this->_blHasVariants ) {
01393                 $this->_blNotBuyableParent = true;
01394             }
01395 
01396             //if we have variants, but all variants are incative means article may be non buyable (depends on config option)
01397             if ( !$myConfig->getConfigParam( 'blVariantParentBuyable' ) && count( $oVariants ) == 0 && $this->_blHasVariants ) {
01398                 $this->_blNotBuyable = true;
01399             }
01400         }
01401 
01402         return $oVariants;
01403     }
01404 
01413     public function getFullVariants( $blRemoveNotOrderables = true, $blForceCoreTable = null )
01414     {
01415         return $this->_loadVariantList( false, $blRemoveNotOrderables, $blForceCoreTable );
01416     }
01417 
01426     public function getVariants( $blRemoveNotOrderables = true, $blForceCoreTable = null  )
01427     {
01428         return $this->_loadVariantList( $this->_isInList(), $blRemoveNotOrderables, $blForceCoreTable );
01429     }
01430 
01436     public function getSimpleVariants()
01437     {
01438         if ( $this->oxarticles__oxvarcount->value) {
01439             return $this->getVariants();
01440         }
01441     }
01442 
01451     public function getAdminVariants( $sLanguage = null )
01452     {
01453         $oVariants = oxNew( 'oxarticlelist');
01454         if ( ( $sId = $this->getId() ) ) {
01455 
01456             $oBaseObj = $oVariants->getBaseObject();
01457 
01458             if ( is_null( $sLanguage ) ) {
01459                 $oBaseObj->setLanguage( oxRegistry::getLang()->getBaseLanguage() );
01460             } else {
01461                 $oBaseObj->setLanguage( $sLanguage );
01462             }
01463 
01464             $sSql = "select * from ".$oBaseObj->getViewName()." where oxparentid = '{$sId}' order by oxsort ";
01465             $oVariants->selectString( $sSql );
01466 
01467             //if we have variants then depending on config option the parent may be non buyable
01468             if ( !$this->getConfig()->getConfigParam( 'blVariantParentBuyable' ) && ( $oVariants->count() > 0 ) ) {
01469                 //$this->blNotBuyable = true;
01470                 $this->_blNotBuyableParent = true;
01471             }
01472         }
01473 
01474         return $oVariants;
01475     }
01476 
01484     public function getCategory()
01485     {
01486         $oCategory = oxNew( 'oxcategory' );
01487         $oCategory->setLanguage( $this->getLanguage() );
01488 
01489         // variant handling
01490         $sOXID = $this->getId();
01491         if ( isset( $this->oxarticles__oxparentid->value ) && $this->oxarticles__oxparentid->value ) {
01492             $sOXID = $this->oxarticles__oxparentid->value;
01493         }
01494 
01495         if ( $sOXID ) {
01496             // if the oxcategory instance of this article is not cached
01497             if ( !isset( $this->_aCategoryCache[ $sOXID ] ) ) {
01498                 startPRofile( 'getCategory' );
01499                 $oStr = getStr();
01500                 $sWhere   = $oCategory->getSqlActiveSnippet();
01501                 $sSelect  = $this->_generateSearchStr( $sOXID );
01502                 $sSelect .= ( $oStr->strstr( $sSelect, 'where' )?' and ':' where ') . $sWhere . " order by oxobject2category.oxtime limit 1";
01503 
01504                 // category not found ?
01505                 if ( !$oCategory->assignRecord( $sSelect ) ) {
01506 
01507                     $sSelect  = $this->_generateSearchStr( $sOXID, true );
01508                     $sSelect .= ( $oStr->strstr( $sSelect, 'where' )?' and ':' where ') . $sWhere . " limit 1";
01509 
01510                     // looking for price category
01511                     if ( !$oCategory->assignRecord( $sSelect ) ) {
01512                         $oCategory = null;
01513                     }
01514                 }
01515                 // add the category instance to cache
01516                 $this->_aCategoryCache[ $sOXID ] = $oCategory;
01517                 stopPRofile( 'getCategory' );
01518             } else {
01519                // if the oxcategory instance is cached
01520                $oCategory = $this->_aCategoryCache[ $sOXID ];
01521             }
01522         }
01523 
01524         return $oCategory;
01525     }
01526 
01535     public function getCategoryIds( $blActCats = false, $blSkipCache = false )
01536     {
01537         $myConfig = $this->getConfig();
01538         if ( isset( self::$_aArticleCats[$this->getId()] ) && !$blSkipCache ) {
01539             return self::$_aArticleCats[$this->getId()];
01540         }
01541 
01542         // variant handling
01543         $sOXID = $this->getId();
01544         if (isset( $this->oxarticles__oxparentid->value) && $this->oxarticles__oxparentid->value) {
01545             $sOXID = $this->oxarticles__oxparentid->value;
01546         }
01547 
01548         // we do not use lists here as we dont need this overhead right now
01549         $sSql = $this->_getSelectCatIds( $sOXID, $blActCats );
01550         $oDb = oxDb::getDb( oxDb::FETCH_MODE_ASSOC );
01551         $rs = $oDb->select( $sSql );
01552 
01553 
01554         $aRet = array();
01555 
01556         if ($rs != false && $rs->recordCount() > 0) {
01557             while (!$rs->EOF) {
01558                 $aRet[] = $rs->fields['oxcatnid'];
01559                 $rs->moveNext();
01560             }
01561         }
01562 
01563         // adding price categories if such exists
01564         $sSql = $this->getSqlForPriceCategories();
01565         $oDb->setFetchMode( oxDb::FETCH_MODE_ASSOC );
01566         $rs = $oDb->select( $sSql );
01567 
01568         if ($rs != false && $rs->recordCount() > 0) {
01569             while (!$rs->EOF) {
01570 
01571                 if ( is_array( $rs->fields ) ) {
01572                    $rs->fields = array_change_key_case( $rs->fields, CASE_LOWER );
01573                 }
01574 
01575 
01576                 if ( !$aRet[$rs->fields['oxid']] ) {
01577                     $aRet[] = $rs->fields['oxid'];
01578                 }
01579                 $rs->moveNext();
01580             }
01581         }
01582 
01583         return self::$_aArticleCats[$this->getId()] = $aRet;
01584     }
01585 
01594     protected function _getSelectCatIds( $sOXID, $blActCats = false )
01595     {
01596         $sO2CView = $this->_getObjectViewName('oxobject2category');
01597         $sCatView = $this->_getObjectViewName('oxcategories');
01598         $sSelect =  "select oxobject2category.oxcatnid as oxcatnid from $sO2CView as oxobject2category left join $sCatView as oxcategories on oxcategories.oxid = oxobject2category.oxcatnid ";
01599         $sSelect .= 'where oxobject2category.oxobjectid='.oxDb::getDb()->quote($sOXID).' and oxcategories.oxid is not null and oxcategories.oxactive = 1 ';
01600         if ( $blActCats ) {
01601             $sSelect .= "and oxcategories.oxhidden = 0 and (select count(cats.oxid) from $sCatView as cats where cats.oxrootid = oxcategories.oxrootid and cats.oxleft < oxcategories.oxleft and cats.oxright > oxcategories.oxright and ( cats.oxhidden = 1 or cats.oxactive = 0 ) ) = 0 ";
01602         }
01603         $sSelect .= 'order by oxobject2category.oxtime ';
01604         return $sSelect;
01605     }
01606 
01616     public function getVendor( $blShopCheck = true )
01617     {
01618         if ( ( $sVendorId = $this->getVendorId() ) ) {
01619             $oVendor = oxNew( 'oxvendor' );
01620         } elseif ( !$blShopCheck && $this->oxarticles__oxvendorid->value ) {
01621                 $oVendor = oxNew( 'oxi18n' );
01622                 $oVendor->init('oxvendor');
01623                 $oVendor->setReadOnly( true );
01624             $sVendorId = $this->oxarticles__oxvendorid->value;
01625         }
01626         if ( $sVendorId && $oVendor->load( $sVendorId ) && $oVendor->oxvendor__oxactive->value ) {
01627 
01628             return $oVendor;
01629         }
01630         return null;
01631     }
01632 
01640     public function getVendorId( $blForceReload = false )
01641     {
01642         $sVendorId = false;
01643         if ( $this->oxarticles__oxvendorid->value ) {
01644                 $sVendorId = $this->oxarticles__oxvendorid->value;
01645 
01646         }
01647         return $sVendorId;
01648     }
01649 
01657     public function getManufacturerId( $blForceReload = false )
01658     {
01659         $sManufacturerId = false;
01660         if ( $this->oxarticles__oxmanufacturerid->value ) {
01661 
01662                 $sManufacturerId = $this->oxarticles__oxmanufacturerid->value;
01663 
01664         }
01665         return $sManufacturerId;
01666     }
01667 
01677     public function getManufacturer( $blShopCheck = true )
01678     {
01679             $oManufacturer = oxNew( 'oxmanufacturer' );;
01680         if ( !( $sManufacturerId = $this->getManufacturerId() ) &&
01681              !$blShopCheck && $this->oxarticles__oxmanufacturerid->value ) {
01682             $oManufacturer->setReadOnly( true );
01683             $sManufacturerId = $this->oxarticles__oxmanufacturerid->value;
01684         }
01685 
01686         if ( $sManufacturerId && $oManufacturer->load( $sManufacturerId ) ) {
01687             if ( !$this->getConfig()->getConfigParam( 'bl_perfLoadManufacturerTree' ) ) {
01688                 $oManufacturer->setReadOnly( true );
01689             }
01690             $oManufacturer = $oManufacturer->oxmanufacturers__oxactive->value ? $oManufacturer : null;
01691         } else {
01692             $oManufacturer = null;
01693         }
01694 
01695         return $oManufacturer;
01696     }
01697 
01705     public function inCategory( $sCatNid)
01706     {
01707         return in_array( $sCatNid, $this->getCategoryIds());
01708     }
01709 
01718     public function isAssignedToCategory( $sCatId )
01719     {
01720         // variant handling
01721         $sOXID = $this->getId();
01722         if ( isset( $this->oxarticles__oxparentid->value) && $this->oxarticles__oxparentid->value) {
01723             $sOXID = $this->oxarticles__oxparentid->value;
01724         }
01725 
01726         $oDb = oxDb::getDb();
01727         $sSelect = $this->_generateSelectCatStr( $sOXID, $sCatId);
01728         $sOXID = $oDb->getOne( $sSelect );
01729         // article is assigned to passed category!
01730         if ( isset( $sOXID) && $sOXID) {
01731             return true;
01732         }
01733 
01734         // maybe this category is price category ?
01735         if ( $this->getConfig()->getConfigParam( 'bl_perfLoadPrice' ) && $this->_blLoadPrice ) {
01736             $dPriceFromTo = $this->getPrice()->getBruttoPrice();
01737             if ( $dPriceFromTo > 0) {
01738                 $sSelect = $this->_generateSelectCatStr( $sOXID, $sCatId, $dPriceFromTo);
01739                 $sOXID = $oDb->getOne( $sSelect );
01740                 // article is assigned to passed category!
01741                 if ( isset( $sOXID) && $sOXID) {
01742                     return true;
01743                 }
01744             }
01745         }
01746         return false;
01747     }
01748 
01754     public function getTPrice()
01755     {
01756         if ( !$this->getConfig()->getConfigParam( 'bl_perfLoadPrice' ) || !$this->_blLoadPrice ) {
01757             return;
01758         }
01759 
01760         // return cached result, since oPrice is created ONLY in this function [or function of EQUAL level]
01761         if ( $this->_oTPrice !== null ) {
01762             return $this->_oTPrice;
01763         }
01764 
01765         $oPrice = $this->_getPriceObject();
01766 
01767         $dBasePrice = $this->oxarticles__oxtprice->value;
01768         $dBasePrice = $this->_preparePrice( $dBasePrice, $this->getArticleVat() );
01769 
01770         $oPrice->setPrice( $dBasePrice );
01771 
01772         $this->_applyVat( $oPrice, $this->getArticleVat() );
01773         $this->_applyCurrency( $oPrice );
01774 
01775         if ( $this->isParentNotBuyable() ) {
01776             // if parent article is not buyable then compare agains min article variant price
01777             $oPrice2 = $this->getVarMinPrice();
01778         } else {
01779             // else compare against article price
01780             $oPrice2 = $this->getPrice();
01781         }
01782 
01783         if ( $oPrice->getPrice() <= $oPrice2->getPrice() ) {
01784             // if RRP price is less or equal to comparable price then return
01785             return;
01786         }
01787 
01788         $this->_oTPrice = $oPrice;
01789 
01790         return $this->_oTPrice;
01791     }
01792 
01798     public function skipDiscounts()
01799     {
01800         // already loaded skip discounts config
01801         if ( $this->_blSkipDiscounts !== null ) {
01802             return $this->_blSkipDiscounts;
01803         }
01804 
01805         if ( $this->oxarticles__oxskipdiscounts->value ) {
01806             return true;
01807         }
01808 
01809 
01810         $this->_blSkipDiscounts = false;
01811         if ( oxRegistry::get("oxDiscountList")->hasSkipDiscountCategories() ) {
01812 
01813             $oDb = oxDb::getDb();
01814             $sO2CView  = getViewName( 'oxobject2category', $this->getLanguage() );
01815             $sViewName = getViewName( 'oxcategories', $this->getLanguage() );
01816             $sSelect =  "select 1 from $sO2CView as $sO2CView left join {$sViewName} on {$sViewName}.oxid = $sO2CView.oxcatnid
01817                          where $sO2CView.oxobjectid=".$oDb->quote( $this->getId() )." and {$sViewName}.oxactive = 1 and {$sViewName}.oxskipdiscounts = '1' ";
01818             $this->_blSkipDiscounts = ( $oDb->getOne( $sSelect ) == 1 );
01819         }
01820 
01821         return $this->_blSkipDiscounts;
01822     }
01823 
01831     public function setPrice(oxPrice $oPrice)
01832     {
01833         $this->_oPrice = $oPrice;
01834     }
01835 
01844     public function getBasePrice( $dAmount = 1 )
01845     {
01846         // override this function if you want e.g. different prices
01847         // for diff. user groups.
01848 
01849         // Performance
01850         $myConfig = $this->getConfig();
01851         if( !$myConfig->getConfigParam( 'bl_perfLoadPrice' ) || !$this->_blLoadPrice )
01852             return;
01853 
01854         // GroupPrice or DB price ajusted by AmountPrice
01855         $dPrice = $this->_getAmountPrice( $dAmount );
01856 
01857 
01858         return $dPrice;
01859     }
01860 
01868     public function getPrice( $dAmount = 1 )
01869     {
01870         $myConfig = $this->getConfig();
01871         // Performance
01872         if ( !$myConfig->getConfigParam( 'bl_perfLoadPrice' ) || !$this->_blLoadPrice ) {
01873             return;
01874         }
01875 
01876         // return cached result, since oPrice is created ONLY in this function [or function of EQUAL level]
01877         if ( $dAmount != 1 || $this->_oPrice === null ) {
01878 
01879             // module
01880             $dBasePrice = $this->getBasePrice( $dAmount );
01881             $dBasePrice = $this->_preparePrice( $dBasePrice, $this->getArticleVat() );
01882 
01883             $oPrice = $this->_getPriceObject();
01884 
01885             $oPrice->setPrice( $dBasePrice );
01886 
01887             // price handling
01888             if ( !$this->_blCalcPrice && $dAmount == 1 ) {
01889                 return $this->_oPrice = $oPrice;
01890             }
01891 
01892             $this->_calculatePrice( $oPrice );
01893             if ( $dAmount != 1 ) {
01894                 return $oPrice;
01895             }
01896 
01897             $this->_oPrice = $oPrice;
01898         }
01899         return $this->_oPrice;
01900     }
01901 
01910     protected function _calculatePrice( $oPrice, $dVat = null )
01911     {
01912         // apply VAT only if configuration requires it
01913         if ( isset( $dVat ) || !$this->getConfig()->getConfigParam( 'bl_perfCalcVatOnlyForBasketOrder' ) ) {
01914             $this->_applyVAT( $oPrice, isset( $dVat ) ? $dVat : $this->getArticleVat() );
01915         }
01916 
01917         // apply currency
01918         $this->_applyCurrency( $oPrice );
01919         // apply discounts
01920         if ( !$this->skipDiscounts() ) {
01921             $oDiscountList = oxRegistry::get("oxDiscountList");
01922             $aDiscounts = $oDiscountList->getArticleDiscounts( $this, $this->getArticleUser() );
01923 
01924             reset( $aDiscounts );
01925             foreach ( $aDiscounts as $oDiscount ) {
01926                 $oPrice->setDiscount($oDiscount->getAddSum(), $oDiscount->getAddSumType());
01927             }
01928             $oPrice->calculateDiscount();
01929         }
01930 
01931         return $oPrice;
01932     }
01933 
01941     public function setArticleUser($oUser)
01942     {
01943         $this->_oUser = $oUser;
01944     }
01945 
01951     public function getArticleUser()
01952     {
01953         if ($this->_oUser) {
01954             return $this->_oUser;
01955         }
01956         return $this->getUser();
01957     }
01958 
01968     public function getBasketPrice( $dAmount, $aSelList, $oBasket )
01969     {
01970         $oUser = $oBasket->getBasketUser();
01971         $this->setArticleUser( $oUser );
01972 
01973         $oBasketPrice = $this->_getPriceObject( $oBasket->isCalculationModeNetto() );
01974 
01975         // get base price
01976         $dBasePrice = $this->getBasePrice( $dAmount );
01977 
01978         $dBasePrice = $this->_modifySelectListPrice( $dBasePrice, $aSelList );
01979         $dBasePrice = $this->_preparePrice( $dBasePrice, $this->getArticleVat(), $oBasket->isCalculationModeNetto() );
01980 
01981         // applying select list price
01982 
01983         // setting price
01984         $oBasketPrice->setPrice( $dBasePrice );
01985 
01986         $dVat = oxRegistry::get("oxVatSelector")->getBasketItemVat( $this, $oBasket );
01987         $this->_calculatePrice( $oBasketPrice, $dVat );
01988 
01989         // returning final price object
01990         return $oBasketPrice;
01991     }
01992 
02001     public function delete( $sOXID = null )
02002     {
02003         if ( !$sOXID ) {
02004             $sOXID = $this->getId();
02005         }
02006         if ( !$sOXID ) {
02007             return false;
02008         }
02009 
02010 
02011 
02012         // #2339 delete first variants before deleting parent product
02013         $this->_deleteVariantRecords( $sOXID );
02014         $this->load( $sOXID );
02015         $this->_deletePics();
02016         $this->_onChangeResetCounts( $sOXID, $this->oxarticles__oxvendorid->value, $this->oxarticles__oxmanufacturerid->value );
02017 
02018         // delete self
02019         parent::delete( $sOXID );
02020 
02021         $rs = $this->_deleteRecords( $sOXID );
02022 
02023         oxRegistry::get("oxSeoEncoderArticle")->onDeleteArticle($this);
02024 
02025         $this->onChange( ACTION_DELETE, $sOXID, $this->oxarticles__oxparentid->value );
02026 
02027         return $rs->EOF;
02028     }
02029 
02038     public function reduceStock($dAmount, $blAllowNegativeStock = false)
02039     {
02040         $this->beforeUpdate();
02041 
02042         $iStockCount = $this->oxarticles__oxstock->value - $dAmount;
02043         if (!$blAllowNegativeStock && ($iStockCount < 0)) {
02044             $dAmount += $iStockCount;
02045             $iStockCount = 0;
02046         }
02047         $this->oxarticles__oxstock = new oxField($iStockCount);
02048 
02049         $oDb = oxDb::getDb();
02050         $oDb->execute( 'update oxarticles set oxarticles.oxstock = '.$oDb->quote( $iStockCount ).' where oxarticles.oxid = '.$oDb->quote( $this->getId() ) );
02051         $this->onChange( ACTION_UPDATE_STOCK );
02052         return $dAmount;
02053     }
02054 
02063     public function updateSoldAmount( $dAmount = 0 )
02064     {
02065         if ( !$dAmount ) {
02066             return;
02067         }
02068 
02069         // article is not variant - should be updated current amount
02070         if ( !$this->oxarticles__oxparentid->value ) {
02071             //updating by SQL query, due to wrong behaviour if saving article using not admin mode
02072             $dAmount = (double) $dAmount;
02073             $oDb = oxDb::getDb();
02074             $rs = $oDb->execute( "update oxarticles set oxarticles.oxsoldamount = oxarticles.oxsoldamount + $dAmount where oxarticles.oxid = ".$oDb->quote($this->oxarticles__oxid->value));
02075         } elseif ( $this->oxarticles__oxparentid->value) {
02076             // article is variant - should be updated this article parent amount
02077             $oUpdateArticle = $this->getParentArticle();
02078             $oUpdateArticle->updateSoldAmount( $dAmount );
02079         }
02080 
02081         return $rs;
02082     }
02083 
02089     public function disableReminder()
02090     {
02091         $oDb = oxDb::getDb();
02092         return $oDb->execute( "update oxarticles set oxarticles.oxremindactive = 2 where oxarticles.oxid = ".$oDb->quote($this->oxarticles__oxid->value));
02093     }
02094 
02100     public function save()
02101     {
02102         if ( ( $blRet = parent::save() ) ) {
02103             // saving long description
02104             $this->_saveArtLongDesc();
02105         }
02106 
02107         return $blRet;
02108     }
02109 
02115     public function resetParent()
02116     {
02117         $sParentId = $this->oxarticles__oxparentid->value;
02118         $this->oxarticles__oxparentid = new oxField( '', oxField::T_RAW );
02119         $this->_blAllowEmptyParentId = true;
02120         $this->save();
02121         $this->_blAllowEmptyParentId = false;
02122 
02123         if ( $sParentId !== '' ) {
02124             $this->onChange( ACTION_UPDATE, null, $sParentId );
02125         }
02126     }
02127 
02128 
02135     public function getPictureGallery()
02136     {
02137         $myConfig = $this->getConfig();
02138 
02139         //initialize
02140         $blMorePic = false;
02141         $aArtPics  = array();
02142         $aArtIcons = array();
02143         $iActPicId = 1;
02144         $sActPic = $this->getPictureUrl( $iActPicId );
02145 
02146         if ( oxConfig::getParameter( 'actpicid' ) ) {
02147             $iActPicId = oxConfig::getParameter('actpicid');
02148         }
02149 
02150         $oStr = getStr();
02151         $iCntr = 0;
02152         $iPicCount = $myConfig->getConfigParam( 'iPicCount' );
02153         $blCheckActivePicId = true;
02154 
02155         for ( $i = 1; $i <= $iPicCount; $i++) {
02156             $sPicVal = $this->getPictureUrl( $i );
02157             $sIcoVal = $this->getIconUrl( $i );
02158             if ( !$oStr->strstr($sIcoVal, 'nopic_ico.jpg') && !$oStr->strstr($sIcoVal, 'nopic.jpg') &&
02159                  !$oStr->strstr($sPicVal, 'nopic_ico.jpg') && !$oStr->strstr($sPicVal, 'nopic.jpg') ) {
02160                 if ($iCntr) {
02161                     $blMorePic = true;
02162                 }
02163                 $aArtIcons[$i]= $sIcoVal;
02164                 $aArtPics[$i]= $sPicVal;
02165                 $iCntr++;
02166 
02167                 if ($iActPicId == $i) {
02168                     $sActPic = $sPicVal;
02169                     $blCheckActivePicId = false;
02170                 }
02171 
02172             } else if ( $blCheckActivePicId && $iActPicId <= $i) {
02173                 // if picture is empty, setting active pic id to next
02174                 // picture
02175                 $iActPicId++;
02176             }
02177         }
02178 
02179         $blZoomPic  = false;
02180         $aZoomPics = array();
02181         $iZoomPicCount = $myConfig->getConfigParam( 'iPicCount' );
02182 
02183         for ( $j = 1,$c = 1; $j <= $iZoomPicCount; $j++) {
02184             $sVal = $this->getZoomPictureUrl($j);
02185 
02186             if ( $sVal && !$oStr->strstr($sVal, 'nopic.jpg')) {
02187                 $blZoomPic = true;
02188                 $aZoomPics[$c]['id'] = $c;
02189                 $aZoomPics[$c]['file'] = $sVal;
02190                 //anything is better than empty name, because <img src=""> calls shop once more = x2 SLOW.
02191                 if (!$sVal) {
02192                     $aZoomPics[$c]['file'] = "nopic.jpg";
02193                 }
02194                 $c++;
02195             }
02196         }
02197 
02198         $aPicGallery = array('ActPicID' => $iActPicId,
02199                              'ActPic' => $sActPic,
02200                              'MorePics' => $blMorePic,
02201                              'Pics' => $aArtPics,
02202                              'Icons' => $aArtIcons,
02203                              'ZoomPic' => $blZoomPic,
02204                              'ZoomPics' => $aZoomPics);
02205 
02206         return $aPicGallery;
02207     }
02208 
02222     public function onChange($sAction = null, $sOXID = null, $sParentID = null)
02223     {
02224         $myConfig = $this->getConfig();
02225 
02226         if (!isset($sOXID)) {
02227             if ( $this->getId()) {
02228                 $sOXID = $this->getId();
02229             }
02230             if (!isset ($sOXID)) {
02231                 $sOXID = $this->oxarticles__oxid->value;
02232             }
02233             if ($this->oxarticles__oxparentid->value) {
02234                 $sParentID = $this->oxarticles__oxparentid->value;
02235             }
02236         }
02237         if (!isset($sOXID)) {
02238             return;
02239         }
02240 
02241         //if (isset($sOXID) && !$myConfig->blVariantParentBuyable && $myConfig->blUseStock)
02242         if ( $myConfig->getConfigParam( 'blUseStock' ) ) {
02243             //if article has variants then updating oxvarstock field
02244             //getting parent id
02245             if (!isset($sParentID)) {
02246                 $oDb = oxDb::getDb();
02247                 $sQ = 'select oxparentid from oxarticles where oxid = '.$oDb->quote($sOXID);
02248                 $sParentID = $oDb->getOne( $sQ );
02249             }
02250             //if we have parent id then update stock
02251             if ($sParentID) {
02252                 $this->_onChangeUpdateStock($sParentID);
02253             }
02254         }
02255         //if we have parent id then update count
02256         //update count even if blUseStock is not active
02257         if ($sParentID) {
02258             $this->_onChangeUpdateVarCount($sParentID);
02259         }
02260 
02261         $sId = ( $sParentID ) ? $sParentID : $sOXID;
02262         $this->_setVarMinMaxPrice( $sId );
02263 
02264         // resetting articles count cache if stock has changed and some
02265         // articles goes offline (M:1448)
02266         if ( $sAction === ACTION_UPDATE_STOCK ) {
02267             $this->_assignStock();
02268             $this->_onChangeStockResetCount( $sOXID );
02269         }
02270 
02271     }
02272 
02279     public function getCustomVAT()
02280     {
02281         if ( isset($this->oxarticles__oxvat->value) ) {
02282             return $this->oxarticles__oxvat->value;
02283         }
02284     }
02285 
02294     public function checkForStock( $dAmount, $dArtStockAmount = 0 )
02295     {
02296         $myConfig = $this->getConfig();
02297         if ( !$myConfig->getConfigParam( 'blUseStock' ) ) {
02298             return true;
02299         }
02300 
02301         $oDb = oxDb::getDb( oxDb::FETCH_MODE_ASSOC );
02302         // fetching DB info as its up-to-date
02303         $sQ = 'select oxstock, oxstockflag from oxarticles where oxid = '.$oDb->quote( $this->getId() );
02304         $rs = $oDb->select( $sQ );
02305 
02306         $iOnStock   = 0;
02307         $iStockFlag = 0;
02308         if ( $rs !== false && $rs->recordCount() > 0 ) {
02309             $iOnStock   = $rs->fields['oxstock'] - $dArtStockAmount;
02310             $iStockFlag = $rs->fields['oxstockflag'];
02311 
02312             // dodger : fremdlager is also always considered as on stock
02313             if ( $iStockFlag == 1 || $iStockFlag == 4) {
02314                 return true;
02315             }
02316             if ( !$myConfig->getConfigParam( 'blAllowUnevenAmounts' ) ) {
02317                 $iOnStock = floor( $iOnStock );
02318             }
02319         }
02320         if ($this->getConfig()->getConfigParam( 'blPsBasketReservationEnabled' )) {
02321             $iOnStock += $this->getSession()->getBasketReservations()->getReservedAmount($this->getId());
02322         }
02323         if ( $iOnStock >= $dAmount ) {
02324             return true;
02325         } else {
02326             if ( $iOnStock > 0 ) {
02327                 return $iOnStock;
02328             } else {
02329                 $oEx = oxNew( 'oxArticleInputException' );
02330                 $oEx->setMessage( 'ERROR_MESSAGE_ARTICLE_ARTICLE_NOT_BUYABLE' );
02331                 oxRegistry::get("oxUtilsView")->addErrorToDisplay( $oEx );
02332                 return false;
02333             }
02334         }
02335     }
02336 
02337 
02343     public function getLongDescription()
02344     {
02345         if ( $this->_oLongDesc === null ) {
02346             // initializing
02347             $this->_oLongDesc = new oxField();
02348 
02349 
02350             // choosing which to get..
02351             $sOxid = $this->getId();
02352             $sViewName = getViewName( 'oxartextends', $this->getLanguage() );
02353 
02354             $oDb = oxDb::getDb();
02355             $sDbValue = $oDb->getOne( "select oxlongdesc from {$sViewName} where oxid = " . $oDb->quote( $sOxid ) );
02356 
02357             if ( $sDbValue != false ) {
02358                 $this->_oLongDesc->setValue( $sDbValue, oxField::T_RAW );
02359             } elseif ( $this->oxarticles__oxparentid->value ) {
02360                 if ( !$this->isAdmin() || $this->_blLoadParentData ) {
02361                     $this->_oLongDesc->setValue( $this->getParentArticle()->getLongDescription()->getRawValue(), oxField::T_RAW );
02362                 }
02363             }
02364         }
02365         return $this->_oLongDesc;
02366     }
02367 
02374     public function getLongDesc()
02375     {
02376         return oxRegistry::get("oxUtilsView")->parseThroughSmarty( $this->getLongDescription()->getRawValue(), $this->getId().$this->getLanguage(), null, true );
02377     }
02378 
02386     public function setArticleLongDesc( $sDesc )
02387     {
02388 
02389         // setting current value
02390         $this->_oLongDesc = new oxField( $sDesc, oxField::T_RAW );
02391         $this->oxarticles__oxlongdesc = new oxField( $sDesc, oxField::T_RAW );
02392     }
02393 
02399     public function getAttributes()
02400     {
02401         if ( $this->_oAttributeList === null ) {
02402             $this->_oAttributeList = oxNew( 'oxattributelist' );
02403             $this->_oAttributeList->loadAttributes( $this->getId(), $this->getParentId() );
02404         }
02405 
02406         return $this->_oAttributeList;
02407     }
02408 
02414     public function getAttributesDisplayableInBasket()
02415     {
02416         if ( $this->_oAttributeList === null ) {
02417             $this->_oAttributeList = oxNew( 'oxattributelist' );
02418             $this->_oAttributeList->loadAttributesDisplayableInBasket( $this->getId(), $this->getParentId() );
02419         }
02420 
02421         return $this->_oAttributeList;
02422     }
02423 
02424 
02433     public function appendLink( $sAddParams, $iLang = null )
02434     {
02435         if ( $sAddParams ) {
02436             if ( $iLang === null ) {
02437                 $iLang = $this->getLanguage();
02438             }
02439 
02440             $this->_aSeoAddParams[$iLang]  = isset( $this->_aSeoAddParams[$iLang] ) ? $this->_aSeoAddParams[$iLang] . "&amp;" : "";
02441             $this->_aSeoAddParams[$iLang] .= $sAddParams;
02442         }
02443     }
02444 
02453     public function getBaseSeoLink( $iLang, $blMain = false )
02454     {
02455         $oEncoder = oxRegistry::get("oxSeoEncoderArticle");
02456         if ( !$blMain ) {
02457             return $oEncoder->getArticleUrl( $this, $iLang, $this->getLinkType() );
02458         }
02459         return $oEncoder->getArticleMainUrl( $this, $iLang );
02460     }
02461 
02470     public function getLink( $iLang = null, $blMain = false  )
02471     {
02472         if ( !oxRegistry::getUtils()->seoIsActive() ) {
02473             return $this->getStdLink( $iLang );
02474         }
02475 
02476         if ( $iLang === null ) {
02477             $iLang = $this->getLanguage();
02478         }
02479 
02480         $iLinkType = $this->getLinkType();
02481         if ( !isset( $this->_aSeoUrls[$iLang][$iLinkType] ) ) {
02482             $this->_aSeoUrls[$iLang][$iLinkType] = $this->getBaseSeoLink( $iLang, $blMain );
02483         }
02484 
02485         $sUrl = $this->_aSeoUrls[$iLang][$iLinkType];
02486         if ( isset($this->_aSeoAddParams[$iLang])) {
02487             $sUrl .= ( ( strpos( $sUrl.$this->_aSeoAddParams[$iLang], '?' ) === false ) ? '?' : '&amp;' ).$this->_aSeoAddParams[$iLang];
02488         }
02489 
02490         return $sUrl;
02491     }
02492 
02501     public function getMainLink( $iLang = null )
02502     {
02503         return $this->getLink( $iLang, true );
02504     }
02505 
02513     public function setLinkType( $iType )
02514     {
02515         // resetting details link, to force new
02516         $this->_sDetailLink = null;
02517 
02518         // setting link type
02519         $this->_iLinkType = (int) $iType;
02520     }
02521 
02527     public function getLinkType()
02528     {
02529         return $this->_iLinkType;
02530     }
02531 
02540     public function appendStdLink( $sAddParams, $iLang = null )
02541     {
02542         if ( $sAddParams ) {
02543             if ( $iLang === null ) {
02544                 $iLang = $this->getLanguage();
02545             }
02546 
02547             $this->_aStdAddParams[$iLang]  = isset( $this->_aStdAddParams[$iLang] ) ? $this->_aStdAddParams[$iLang] . "&amp;" : "";
02548             $this->_aStdAddParams[$iLang] .= $sAddParams;
02549         }
02550     }
02551 
02561     public function getBaseStdLink( $iLang, $blAddId = true, $blFull = true )
02562     {
02563         $sUrl = '';
02564         if ( $blFull ) {
02565             //always returns shop url, not admin
02566             $sUrl = $this->getConfig()->getShopUrl( $iLang, false );
02567         }
02568 
02569         $sUrl .= "index.php?cl=details" . ( $blAddId ? "&amp;anid=".$this->getId() : "" );
02570         return $sUrl . ( isset( $this->_aStdAddParams[$iLang] ) ? "&amp;". $this->_aStdAddParams[$iLang] : "" );
02571     }
02572 
02581     public function getStdLink( $iLang = null, $aParams = array() )
02582     {
02583         if ( $iLang === null ) {
02584             $iLang = $this->getLanguage();
02585         }
02586 
02587         if ( !isset( $this->_aStdUrls[$iLang] ) ) {
02588             $this->_aStdUrls[$iLang] = $this->getBaseStdLink( $iLang );
02589         }
02590 
02591         return oxRegistry::get("oxUtilsUrl")->processUrl( $this->_aStdUrls[$iLang], true, $aParams, $iLang );
02592     }
02593 
02603     public function getStdTagLink( $sTag )
02604     {
02605         $oArticleTags = oxNew('oxarticletaglist');
02606         $oArticleTags->setArticleId( $this->getId() );
02607         return $oArticleTags->getStdTagLink($sTag);
02608     }
02609 
02617     public function getTags()
02618     {
02619         $oArticleTags = oxNew('oxarticletaglist');
02620         $oArticleTags->load( $this->getId() );
02621         return $oArticleTags->get()->__toString();
02622     }
02623 
02633     public function saveTags($sTags)
02634     {
02635         //do not allow derived update
02636         if ( !$this->allowDerivedUpdate() ) {
02637             return false;
02638         }
02639         $oArticleTags = oxNew('oxarticletaglist');
02640         $oArticleTags->setArticleId( $this->getId() );
02641         $oArticleTags->set( $sTags );
02642         $oArticleTags->save();
02643     }
02644 
02654     public function addTag($sTag)
02655     {
02656         $oArticleTags = oxNew('oxarticletaglist');
02657         $oArticleTags->load( $this->getId() );
02658         $oArticleTags->addTag( $sTag );
02659         if ( $oArticleTags->save() ) {
02660             return true;
02661         }
02662         return false;
02663     }
02664 
02670     public function getMediaUrls()
02671     {
02672         if ( $this->_aMediaUrls === null ) {
02673             $this->_aMediaUrls = oxNew("oxlist");
02674             $this->_aMediaUrls->init("oxmediaurl");
02675             $this->_aMediaUrls->getBaseObject()->setLanguage( $this->getLanguage() );
02676 
02677             $sViewName = getViewName( "oxmediaurls", $this->getLanguage() );
02678             $sQ = "select * from {$sViewName} where oxobjectid = '".$this->getId()."'";
02679             $this->_aMediaUrls->selectString($sQ);
02680         }
02681         return $this->_aMediaUrls;
02682     }
02683 
02689     public function getDynImageDir()
02690     {
02691         return $this->_sDynImageDir;
02692     }
02693 
02699     public function getDispSelList()
02700     {
02701         if ($this->_aDispSelList === null) {
02702             if ( $this->getConfig()->getConfigParam( 'bl_perfLoadSelectLists' ) && $this->getConfig()->getConfigParam( 'bl_perfLoadSelectListsInAList' ) ) {
02703                 $this->_aDispSelList = $this->getSelectLists();
02704             }
02705         }
02706         return $this->_aDispSelList;
02707     }
02708 
02714     public function getMoreDetailLink()
02715     {
02716         if ( $this->_sMoreDetailLink == null ) {
02717 
02718             // and assign special article values
02719             $this->_sMoreDetailLink = $this->getConfig()->getShopHomeURL() . 'cl=moredetails';
02720 
02721             // not always it is okey, as not all the time active category is the same as primary article cat.
02722             if ( $sActCat = oxConfig::getParameter( 'cnid' ) ) {
02723                 $this->_sMoreDetailLink .= '&amp;cnid='.$sActCat;
02724             }
02725             $this->_sMoreDetailLink .= '&amp;anid='.$this->getId();
02726             $this->_sMoreDetailLink = $this->_sMoreDetailLink;
02727         }
02728 
02729         return $this->_sMoreDetailLink;
02730     }
02731 
02737     public function getToBasketLink()
02738     {
02739         if ( $this->_sToBasketLink == null ) {
02740             $myConfig = $this->getConfig();
02741 
02742             if ( oxRegistry::getUtils()->isSearchEngine() ) {
02743                 $this->_sToBasketLink = $this->getLink();
02744             } else {
02745                 // and assign special article values
02746                 $this->_sToBasketLink = $myConfig->getShopHomeURL();
02747 
02748                 // override some classes as these should never showup
02749                 $sActClass = oxConfig::getParameter( 'cl' );
02750                 if ( $sActClass == 'thankyou') {
02751                     $sActClass = 'basket';
02752                 }
02753                 $this->_sToBasketLink .= 'cl='.$sActClass;
02754 
02755                 // this is not very correct
02756                 if ( $sActCat = oxConfig::getParameter( 'cnid' ) ) {
02757                     $this->_sToBasketLink .= '&amp;cnid='.$sActCat;
02758                 }
02759 
02760                 $this->_sToBasketLink .= '&amp;fnc=tobasket&amp;aid='.$this->getId().'&amp;anid='.$this->getId();
02761 
02762                 if ( $sTpl = basename( oxConfig::getParameter( 'tpl' ) ) ) {
02763                     $this->_sToBasketLink .= '&amp;tpl='.$sTpl;
02764                 }
02765             }
02766         }
02767 
02768         return $this->_sToBasketLink;
02769     }
02770 
02776     public function getStockStatus()
02777     {
02778         return $this->_iStockStatus;
02779     }
02780 
02786     protected function _isStockStatusChanged()
02787     {
02788         return $this->_iStockStatus != $this->_iStockStatusOnLoad;
02789     }
02790 
02796     protected function _isVisibilityChanged()
02797     {
02798         return $this->_isStockStatusChanged() && ($this->_iStockStatus == -1 || $this->_iStockStatusOnLoad == -1);
02799     }
02800 
02806     public function getDeliveryDate()
02807     {
02808         if ( $this->oxarticles__oxdelivery->value != '0000-00-00') {
02809             return oxRegistry::get("oxUtilsDate")->formatDBDate( $this->oxarticles__oxdelivery->value);
02810         }
02811         return false;
02812     }
02813 
02821     public function getFTPrice()
02822     {
02823         // module
02824         if ( $oPrice = $this->getTPrice() ) {
02825             if ( $dPrice = $this->_getPriceForView( $oPrice ) ) {
02826                 return oxRegistry::getLang()->formatCurrency( $dPrice );
02827             }
02828         }
02829     }
02830 
02838     public function getFPrice()
02839     {
02840         if ( $oPrice = $this->getPrice() ) {
02841             $dPrice = $this->_getPriceForView( $oPrice );
02842             return oxRegistry::getLang()->formatCurrency( $dPrice );
02843         }
02844     }
02845 
02852     public function resetRemindStatus()
02853     {
02854         if ( $this->oxarticles__oxremindactive->value == 2 &&
02855             $this->oxarticles__oxremindamount->value <= $this->oxarticles__oxstock->value ) {
02856             $this->oxarticles__oxremindactive->value = 1;
02857         }
02858     }
02859 
02867     public function getFNetPrice()
02868     {
02869         if ( $oPrice = $this->getPrice() ) {
02870             return oxRegistry::getLang()->formatCurrency( $oPrice->getNettoPrice() );
02871         }
02872     }
02873 
02881     public function getPricePerUnit()
02882     {
02883         return $this->getFUnitPrice();
02884     }
02885 
02891     public function isParentNotBuyable()
02892     {
02893         return $this->_blNotBuyableParent;
02894     }
02895 
02901     public function isNotBuyable()
02902     {
02903         return $this->_blNotBuyable;
02904     }
02905 
02913     public function setBuyableState( $blBuyable = false )
02914     {
02915         $this->_blNotBuyable = !$blBuyable;
02916     }
02917 
02925     public function setSelectlist( $aSelList )
02926     {
02927         $this->_aDispSelList = $aSelList;
02928     }
02929 
02937     public function getPictureUrl( $iIndex = 1 )
02938     {
02939         if ( $iIndex ) {
02940             $sImgName = false;
02941             if ( !$this->_isFieldEmpty( "oxarticles__oxpic".$iIndex ) ) {
02942                 $sImgName = basename( $this->{"oxarticles__oxpic$iIndex"}->value );
02943             }
02944 
02945             $sSize = $this->getConfig()->getConfigParam( 'aDetailImageSizes' );
02946             return oxRegistry::get("oxPictureHandler")->getProductPicUrl( "product/{$iIndex}/", $sImgName, $sSize, 'oxpic'.$iIndex );
02947         }
02948     }
02949 
02958     public function getIconUrl( $iIndex = 0 )
02959     {
02960         $sImgName = false;
02961         $sDirname = "product/1/";
02962         if ( $iIndex && !$this->_isFieldEmpty( "oxarticles__oxpic{$iIndex}" ) ) {
02963             $sImgName = basename( $this->{"oxarticles__oxpic$iIndex"}->value );
02964             $sDirname = "product/{$iIndex}/";
02965         } elseif ( !$this->_isFieldEmpty( "oxarticles__oxicon" ) ) {
02966             $sImgName = basename( $this->oxarticles__oxicon->value );
02967             $sDirname = "product/icon/";
02968         } elseif ( !$this->_isFieldEmpty( "oxarticles__oxpic1" ) ) {
02969             $sImgName = basename( $this->oxarticles__oxpic1->value );
02970         }
02971 
02972         $sSize = $this->getConfig()->getConfigParam( 'sIconsize' );
02973         return oxRegistry::get("oxPictureHandler")->getProductPicUrl( $sDirname, $sImgName, $sSize, $iIndex );
02974     }
02975 
02983     public function getThumbnailUrl( $bSsl = null )
02984     {
02985         $sImgName = false;
02986         $sDirname = "product/1/";
02987         if ( !$this->_isFieldEmpty( "oxarticles__oxthumb" ) ) {
02988             $sImgName = basename( $this->oxarticles__oxthumb->value );
02989             $sDirname = "product/thumb/";
02990         } elseif ( !$this->_isFieldEmpty( "oxarticles__oxpic1" ) ) {
02991             $sImgName = basename( $this->oxarticles__oxpic1->value );
02992         }
02993 
02994         $sSize = $this->getConfig()->getConfigParam( 'sThumbnailsize' );
02995         return oxRegistry::get("oxPictureHandler")->getProductPicUrl( $sDirname, $sImgName, $sSize, 0, $bSsl );
02996     }
02997 
03005     public function getZoomPictureUrl( $iIndex = '' )
03006     {
03007         $iIndex = (int) $iIndex;
03008         if ( $iIndex > 0 && !$this->_isFieldEmpty( "oxarticles__oxpic".$iIndex ) ) {
03009             $sImgName = basename( $this->{"oxarticles__oxpic".$iIndex}->value );
03010             $sSize = $this->getConfig()->getConfigParam( "sZoomImageSize" );
03011             return oxRegistry::get("oxPictureHandler")->getProductPicUrl( "product/{$iIndex}/", $sImgName, $sSize, 'oxpic'.$iIndex );
03012         }
03013     }
03014 
03022     public function getFileUrl()
03023     {
03024         return $this->getConfig()->getPictureUrl( 'media/' );
03025     }
03026 
03034     public function getPriceFromPrefix()
03035     {
03036         $sPricePrefix = '';
03037         if ( $this->_blIsRangePrice) {
03038             $sPricePrefix = oxLang::getInstance()->translateString('PRICE_FROM').' ';
03039         }
03040 
03041         return $sPricePrefix;
03042     }
03043 
03049     protected function _saveArtLongDesc()
03050     {
03051         $myConfig = $this->getConfig();
03052         $sShopId = $myConfig->getShopID();
03053         if (in_array("oxlongdesc", $this->_aSkipSaveFields)) {
03054             return;
03055         }
03056 
03057         if ($this->_blEmployMultilanguage) {
03058             $sValue = $this->getLongDescription()->getRawValue();
03059             if ( $sValue !== null ) {
03060                 $oArtExt = oxNew('oxI18n');
03061                 $oArtExt->init('oxartextends');
03062                 $oArtExt->setLanguage((int) $this->getLanguage());
03063                 if (!$oArtExt->load($this->getId())) {
03064                     $oArtExt->setId($this->getId());
03065                 }
03066                 $oArtExt->oxartextends__oxlongdesc = new oxField($sValue, oxField::T_RAW);
03067                 $oArtExt->save();
03068             }
03069         } else {
03070             $oArtExt = oxNew('oxI18n');
03071             $oArtExt->setEnableMultilang(false);
03072             $oArtExt->init('oxartextends');
03073             $aObjFields = $oArtExt->_getAllFields(true);
03074             if (!$oArtExt->load($this->getId())) {
03075                 $oArtExt->setId($this->getId());
03076             }
03077 
03078             foreach ($aObjFields as $sKey => $sValue ) {
03079                 if ( preg_match('/^oxlongdesc(_(\d{1,2}))?$/', $sKey) ) {
03080                     $sField = $this->_getFieldLongName($sKey);
03081 
03082                     if (isset($this->$sField)) {
03083                         $sLongDesc = null;
03084                         if ($this->$sField instanceof oxField) {
03085                             $sLongDesc = $this->$sField->getRawValue();
03086                         } elseif (is_object($this->$sField)) {
03087                             $sLongDesc = $this->$sField->value;
03088                         }
03089                         if (isset($sLongDesc)) {
03090                             $sAEField = $oArtExt->_getFieldLongName($sKey);
03091                             $oArtExt->$sAEField = new oxField($sLongDesc, oxField::T_RAW);
03092                         }
03093                     }
03094                 }
03095             }
03096             $oArtExt->save();
03097         }
03098     }
03099 
03105     protected function _skipSaveFields()
03106     {
03107         $myConfig = $this->getConfig();
03108 
03109         $this->_aSkipSaveFields = array();
03110 
03111         $this->_aSkipSaveFields[] = 'oxtimestamp';
03112        // $this->_aSkipSaveFields[] = 'oxlongdesc';
03113         $this->_aSkipSaveFields[] = 'oxinsert';
03114 
03115         if ( !$this->_blAllowEmptyParentId && (!isset( $this->oxarticles__oxparentid->value) || $this->oxarticles__oxparentid->value == '') ) {
03116             $this->_aSkipSaveFields[] = 'oxparentid';
03117         }
03118 
03119     }
03120 
03130     protected function _mergeDiscounts( $aDiscounts, $aItemDiscounts)
03131     {
03132         foreach ( $aItemDiscounts as $sKey => $oDiscount ) {
03133             // add prices of the same discounts
03134             if ( array_key_exists ($sKey, $aDiscounts) ) {
03135                 $aDiscounts[$sKey]->dDiscount += $oDiscount->dDiscount;
03136             } else {
03137                 $aDiscounts[$sKey] = $oDiscount;
03138             }
03139         }
03140         return $aDiscounts;
03141     }
03142 
03148     protected function _getGroupPrice()
03149     {
03150         $sPriceSufix = $this->_getUserPriceSufix();
03151         $sVarName = oxarticles__oxprice.$sPriceSufix;
03152         $dPrice = $this->$sVarName->value;
03153 
03154         // #1437/1436C - added config option, and check for zero A,B,C price values
03155         if ( $this->getConfig()->getConfigParam( 'blOverrideZeroABCPrices' ) && (double) $dPrice == 0 ) {
03156             $dPrice = $this->oxarticles__oxprice->value;
03157         }
03158 
03159         return $dPrice;
03160     }
03161 
03170     protected function _getAmountPrice($dAmount = 1)
03171     {
03172         $myConfig = $this->getConfig();
03173 
03174         startProfile( "_getAmountPrice" );
03175 
03176         $dPrice = $this->_getGroupPrice();
03177         $oAmtPrices = $this->_getAmountPriceList();
03178         foreach ($oAmtPrices as $oAmPrice) {
03179             if ($oAmPrice->oxprice2article__oxamount->value <= $dAmount
03180                     && $dAmount <= $oAmPrice->oxprice2article__oxamountto->value
03181                     && $dPrice > $oAmPrice->oxprice2article__oxaddabs->value ) {
03182                 $dPrice = $oAmPrice->oxprice2article__oxaddabs->value;
03183             }
03184         }
03185 
03186         stopProfile( "_getAmountPrice" );
03187         return $dPrice;
03188     }
03189 
03198     protected function _modifySelectListPrice( $dPrice, $aChosenList = null )
03199     {
03200         $myConfig = $this->getConfig();
03201         // #690
03202         if ( $myConfig->getConfigParam( 'bl_perfLoadSelectLists' ) && $myConfig->getConfigParam( 'bl_perfUseSelectlistPrice' ) ) {
03203 
03204             $aSelLists = $this->getSelectLists();
03205 
03206             foreach ( $aSelLists as $key => $aSel) {
03207                 if ( isset( $aChosenList[$key]) && isset($aSel[$aChosenList[$key]] ) ) {
03208                     $oSel = $aSel[$aChosenList[$key]];
03209                     if ( $oSel->priceUnit =='abs' ) {
03210                         $dPrice += $oSel->price;
03211                     } elseif ( $oSel->priceUnit =='%' ) {
03212                         $dPrice += oxPrice::percent( $dPrice, $oSel->price );
03213                     }
03214                 }
03215             }
03216         }
03217         return $dPrice;
03218     }
03219 
03220 
03228     protected function _fillAmountPriceList($oAmPriceList)
03229     {
03230         $myConfig = $this->getConfig();
03231         $oLang = oxRegistry::getLang();
03232 
03233         // trying to find lowest price value
03234         foreach ($oAmPriceList as $sId => $oItem) {
03235 
03236             $oItemPrice = $this->_getPriceObject();
03237             if ( $oItem->oxprice2article__oxaddabs->value ) {
03238 
03239                 $dBasePrice = $oItem->oxprice2article__oxaddabs->value;
03240                 $dBasePrice = $this->_preparePrice( $dBasePrice, $this->getArticleVat() );
03241 
03242                 $oItemPrice->setPrice( $dBasePrice );
03243                 $this->_calculatePrice( $oItemPrice );
03244 
03245             } else {
03246                 $dBasePrice = $this->_getGroupPrice();
03247                 $dBasePrice = $this->_preparePrice( $dBasePrice, $this->getArticleVat() );
03248                 $oItemPrice->setPrice( $dBasePrice );
03249                 $oItemPrice->subtractPercent( $oItem->oxprice2article__oxaddperc->value );
03250             }
03251 
03252 
03253             $oAmPriceList[$sId]->fbrutprice = $oLang->formatCurrency( $this->_getPriceForView( $oItemPrice ) );
03254         }
03255 
03256         return $oAmPriceList;
03257     }
03258 
03259 
03265     protected function _getVariantsIds()
03266     {
03267         $aSelect = array();
03268         if ( ( $sId = $this->getId() ) ) {
03269             $oDb = oxDb::getDb( oxDb::FETCH_MODE_ASSOC );
03270             $sQ = "select oxid from " . $this->getViewName( true ) . " where oxparentid = ".$oDb->quote( $sId )." and " .
03271                    $this->getSqlActiveSnippet( true ) . " order by oxsort";
03272             $oRs = $oDb->select( $sQ );
03273             if ( $oRs != false && $oRs->recordCount() > 0 ) {
03274                 while (!$oRs->EOF) {
03275                     $aSelect[] = reset( $oRs->fields );
03276                     $oRs->moveNext();
03277                 }
03278             }
03279         }
03280         return $aSelect;
03281     }
03282 
03288     public function getArticleVat()
03289     {
03290         if (!isset($this->_dArticleVat)) {
03291             $this->_dArticleVat = oxRegistry::get("oxVatSelector")->getArticleVat( $this );
03292         }
03293         return $this->_dArticleVat;
03294     }
03295 
03304     protected function _applyVAT( oxPrice $oPrice, $dVat )
03305     {
03306         startProfile(__FUNCTION__);
03307         $oPrice->setVAT( $dVat );
03308         if ( ($dVat = oxRegistry::get("oxVatSelector")->getArticleUserVat($this)) !== false ) {
03309             $oPrice->setUserVat( $dVat );
03310         }
03311         stopProfile(__FUNCTION__);
03312     }
03313 
03321     public function applyVats( oxPrice $oPrice )
03322     {
03323         $this->_applyVAT($oPrice, $this->getArticleVat() );
03324     }
03325 
03333     public function applyDiscountsForVariant( $oPrice )
03334     {
03335         // apply discounts
03336         if ( !$this->skipDiscounts() ) {
03337             $oDiscountList = oxRegistry::get("oxDiscountList");
03338             $aDiscounts = $oDiscountList->getArticleDiscounts( $this, $this->getArticleUser() );
03339 
03340             reset( $aDiscounts );
03341             foreach ( $aDiscounts as $oDiscount ) {
03342                 $oPrice->setDiscount($oDiscount->getAddSum(), $oDiscount->getAddSumType());
03343             }
03344             $oPrice->calculateDiscount();
03345         }
03346     }
03347 
03356     protected function _applyCurrency(oxPrice $oPrice, $oCur = null )
03357     {
03358         if ( !$oCur ) {
03359             $oCur = $this->getConfig()->getActShopCurrencyObject();
03360         }
03361 
03362         $oPrice->multiply($oCur->rate);
03363     }
03364 
03365 
03374     protected function _getAttribsString(&$sAttribs, &$iCnt)
03375     {
03376         // we do not use lists here as we dont need this overhead right now
03377         $oDb = oxDb::getDb( oxDb::FETCH_MODE_ASSOC );
03378         $sSelect =  'select oxattrid from oxobject2attribute where oxobject2attribute.oxobjectid='.$oDb->quote( $this->getId() );
03379         $sAttribs = '';
03380         $blSep = false;
03381         $rs = $oDb->select( $sSelect);
03382         $iCnt = 0;
03383         if ($rs != false && $rs->recordCount() > 0) {
03384             while (!$rs->EOF) {
03385                 if ( $blSep) {
03386                     $sAttribs .= ' or ';
03387                 }
03388                 $sAttribs .= 't1.oxattrid = '.$oDb->quote($rs->fields['oxattrid']).' ';
03389                 $blSep = true;
03390                 $iCnt++;
03391                 $rs->moveNext();
03392             }
03393         }
03394     }
03395 
03404     protected function _getSimList($sAttribs, $iCnt)
03405     {
03406         $myConfig = $this->getConfig();
03407         $oDb      = oxDb::getDb( oxDb::FETCH_MODE_ASSOC );
03408 
03409         // #523A
03410         $iAttrPercent = $myConfig->getConfigParam( 'iAttributesPercent' )/100;
03411         // 70% same attributes
03412         if ( !$iAttrPercent || $iAttrPercent < 0 || $iAttrPercent > 1) {
03413             $iAttrPercent = 0.70;
03414         }
03415         // #1137V iAttributesPercent = 100 doesnt work
03416         $iHitMin = ceil( $iCnt * $iAttrPercent );
03417 
03418         // we do not use lists here as we don't need this overhead right now
03419         $aList= array();
03420         $sSelect =  "select oxobjectid, count(*) as cnt from oxobject2attribute as t1 where
03421                     ( $sAttribs )
03422                     and t1.oxobjectid != ".$oDb->quote( $this->oxarticles__oxid->value )."
03423                     group by t1.oxobjectid having count(*) >= $iHitMin ";
03424 
03425         $rs = $oDb->selectLimit( $sSelect, 20, 0 );
03426         if ($rs != false && $rs->recordCount() > 0) {
03427             while (!$rs->EOF) {
03428                 $oTemp = new stdClass();    // #663
03429                 $oTemp->cnt = $rs->fields['cnt'];
03430                 $oTemp->id  = $rs->fields['oxobjectid'];
03431                 $aList[] = $oTemp;
03432                 $rs->moveNext();
03433             }
03434         }
03435         return $aList;
03436     }
03437 
03446     protected function _generateSimListSearchStr($sArticleTable, $aList)
03447     {
03448         $myConfig = $this->getConfig();
03449         $sFieldList = $this->getSelectFields();
03450         $sSearch = "select $sFieldList from $sArticleTable where ".$this->getSqlActiveSnippet()."  and $sArticleTable.oxissearch = 1 and $sArticleTable.oxid in ( ";
03451         $blSep = false;
03452         $iCnt = 0;
03453         $oDb = oxDb::getDb();
03454         foreach ( $aList as $oTemp) {
03455             if ( $blSep) {
03456                 $sSearch .= ',';
03457             }
03458             $sSearch .= $oDb->quote($oTemp->id);
03459             $blSep = true;
03460             if ( $iCnt >= $myConfig->getConfigParam( 'iNrofSimilarArticles' ) ) {
03461                 break;
03462             }
03463             $iCnt++;
03464         }
03465 
03466         //#1741T
03467         //$sSearch .= ") and $sArticleTable.oxparentid = '' ";
03468         $sSearch .= ') ';
03469 
03470         // #524A -- randomizing articles in attribute list
03471         $sSearch .= ' order by rand() ';
03472 
03473         return $sSearch;
03474     }
03475 
03484     protected function _generateSearchStr($sOXID, $blSearchPriceCat = false )
03485     {
03486 
03487         $sCatView = getViewName( 'oxcategories', $this->getLanguage() );
03488         $sO2CView = getViewName( 'oxobject2category' );
03489 
03490         // we do not use lists here as we don't need this overhead right now
03491         if ( !$blSearchPriceCat ) {
03492             $sSelect  = "select {$sCatView}.* from {$sO2CView} as oxobject2category left join {$sCatView} on
03493                          {$sCatView}.oxid = oxobject2category.oxcatnid
03494                          where oxobject2category.oxobjectid=".oxDb::getDb()->quote($sOXID)." and {$sCatView}.oxid is not null ";
03495         } else {
03496             $sSelect  = "select {$sCatView}.* from {$sCatView} where
03497                          '{$this->oxarticles__oxprice->value}' >= {$sCatView}.oxpricefrom and
03498                          '{$this->oxarticles__oxprice->value}' <= {$sCatView}.oxpriceto ";
03499         }
03500         return $sSelect;
03501     }
03502 
03508     protected function _generateSearchStrForCustomerBought()
03509     {
03510         $sArtTable = $this->getViewName();
03511         $sOrderArtTable = getViewName( 'oxorderarticles' );
03512 
03513         // fetching filter params
03514         $sIn = " '{$this->oxarticles__oxid->value}' ";
03515         if ( $this->oxarticles__oxparentid->value ) {
03516 
03517             // adding article parent
03518             $sIn .= ", '{$this->oxarticles__oxparentid->value}' ";
03519             $sParentIdForVariants = $this->oxarticles__oxparentid->value;
03520 
03521         } else {
03522             $sParentIdForVariants = $this->getId();
03523         }
03524 
03525         // adding variants
03526         $oDb = oxDb::getDb( oxDb::FETCH_MODE_ASSOC );
03527         $oRs = $oDb->select( "select oxid from {$sArtTable} where oxparentid = ".$oDb->quote($sParentIdForVariants)." and oxid != ".$oDb->quote($this->oxarticles__oxid->value) );
03528         if ( $oRs != false && $oRs->recordCount() > 0) {
03529             while ( !$oRs->EOF ) {
03530                 $sIn .= ", ".$oDb->quote(current( $oRs->fields ))." ";
03531                 $oRs->moveNext();
03532             }
03533         }
03534 
03535         $iLimit = (int) $this->getConfig()->getConfigParam( 'iNrofCustomerWhoArticles' );
03536         $iLimit = $iLimit?( $iLimit * 10 ): 50;
03537 
03538         // building sql (optimized)
03539         $sQ = "select distinct {$sArtTable}.* from (
03540                    select d.oxorderid as suborderid from {$sOrderArtTable} as d use index ( oxartid ) where d.oxartid in ( {$sIn} ) limit {$iLimit}
03541                ) as suborder
03542                left join {$sOrderArtTable} force index ( oxorderid ) on suborder.suborderid = {$sOrderArtTable}.oxorderid
03543                left join {$sArtTable} on {$sArtTable}.oxid = {$sOrderArtTable}.oxartid
03544                where {$sArtTable}.oxid not in ( {$sIn} )
03545                and ( {$sArtTable}.oxissearch = 1 or {$sArtTable}.oxparentid <> '' ) and ".$this->getSqlActiveSnippet();
03546 
03547         /* non optimized, but could be used if index forcing is not supported
03548         // building sql
03549         $sQ = "select distinct {$sArtTable}.* from {$sOrderArtTable}, {$sArtTable} where {$sOrderArtTable}.oxorderid in (
03550                    select {$sOrderArtTable}.oxorderid from {$sOrderArtTable} where {$sOrderArtTable}.oxartid in ( {$sIn} )
03551                ) and {$sArtTable}.oxid = {$sOrderArtTable}.oxartid and {$sArtTable}.oxid not in ( {$sIn} )
03552                and ( {$sArtTable}.oxissearch = 1 or {$sArtTable}.oxparentid <> '' )
03553                and ".$this->getSqlActiveSnippet();
03554         */
03555 
03556         return $sQ;
03557     }
03558 
03568     protected function _generateSelectCatStr($sOXID, $sCatId, $dPriceFromTo = false)
03569     {
03570         $sCategoryView = getViewName('oxcategories');
03571         $sO2CView = getViewName('oxobject2category');
03572 
03573         $oDb    = oxDb::getDb();
03574         $sOXID  = $oDb->quote($sOXID);
03575         $sCatId = $oDb->quote($sCatId);
03576 
03577         if (!$dPriceFromTo) {
03578             $sSelect  = "select oxobject2category.oxcatnid from $sO2CView as oxobject2category ";
03579             $sSelect .= "left join $sCategoryView as oxcategories on oxcategories.oxid = oxobject2category.oxcatnid ";
03580             $sSelect .= "where oxobject2category.oxcatnid=$sCatId and oxobject2category.oxobjectid=$sOXID ";
03581             $sSelect .= "and oxcategories.oxactive = 1 order by oxobject2category.oxtime ";
03582         } else {
03583             $dPriceFromTo = $oDb->quote($dPriceFromTo);
03584             $sSelect  = "select oxcategories.oxid from $sCategoryView as oxcategories where ";
03585             $sSelect .= "oxcategories.oxid=$sCatId and $dPriceFromTo >= oxcategories.oxpricefrom and ";
03586             $sSelect .= "$dPriceFromTo <= oxcategories.oxpriceto ";
03587         }
03588         return $sSelect;
03589     }
03590 
03596     protected function _getAmountPriceList()
03597     {
03598         if ( $this->_oAmountPriceList === null ) {
03599             $this->_oAmountPriceList = array();
03600             if ( !$this->skipDiscounts() ) {
03601 
03602                 //collecting assigned to article amount-price list
03603                 $oAmPriceList = oxNew( 'oxAmountPricelist' );
03604                 $oAmPriceList->load( $this );
03605 
03606                 // prepare abs prices if currently having percentages
03607                 $oBasePrice = $this->_getGroupPrice();
03608                 foreach ( $oAmPriceList as $oAmPrice ) {
03609                     if ( $oAmPrice->oxprice2article__oxaddperc->value ) {
03610                         $oAmPrice->oxprice2article__oxaddabs = new oxField(oxPrice::percent( $oBasePrice, 100 - $oAmPrice->oxprice2article__oxaddperc->value ), oxField::T_RAW );
03611                     }
03612                 }
03613 
03614                 $this->_oAmountPriceList = $oAmPriceList;
03615             }
03616         }
03617 
03618         return $this->_oAmountPriceList;
03619     }
03620 
03628     protected function _isFieldEmpty( $sFieldName )
03629     {
03630         $mValue = $this->$sFieldName->value;
03631 
03632         if ( is_null( $mValue ) ) {
03633             return true;
03634         }
03635 
03636         if ( $mValue === '' ) {
03637             return true;
03638         }
03639 
03640         // certain fields with zero value treat as empty
03641         $aZeroValueFields = array('oxarticles__oxprice', 'oxarticles__oxvat', 'oxarticles__oxunitquantity');
03642 
03643         if (!$mValue && in_array( $sFieldName, $aZeroValueFields ) ) {
03644             return true;
03645         }
03646 
03647 
03648         if (!strcmp($mValue, '0000-00-00 00:00:00') || !strcmp($mValue, '0000-00-00')) {
03649             return true;
03650         }
03651 
03652         $sFieldName = strtolower($sFieldName);
03653 
03654         if ( $sFieldName == 'oxarticles__oxicon' && ( strpos($mValue, "nopic_ico.jpg") !== false || strpos($mValue, "nopic.jpg") !== false ) ) {
03655             return true;
03656         }
03657 
03658         if ( strpos($mValue, "nopic.jpg") !== false && ($sFieldName == 'oxarticles__oxthumb' || substr($sFieldName, 0, 17) == 'oxarticles__oxpic' || substr($sFieldName, 0, 18) == 'oxarticles__oxzoom') ) {
03659             return true;
03660         }
03661 
03662         return false;
03663     }
03664 
03672     protected function _assignParentFieldValue($sFieldName)
03673     {
03674         if (!($oParentArticle = $this->getParentArticle())) {
03675             return;
03676         }
03677 
03678         $sCopyFieldName = $this->_getFieldLongName($sFieldName);
03679 
03680         // assigning only these which parent article has
03681         if ( $oParentArticle->$sCopyFieldName != null ) {
03682 
03683             // only overwrite database values
03684             if ( substr( $sCopyFieldName, 0, 12) != 'oxarticles__') {
03685                 return;
03686             }
03687 
03688             //do not copy certain fields
03689             if (in_array($sCopyFieldName, $this->_aNonCopyParentFields)) {
03690                 return;
03691             }
03692 
03693             //COPY THE VALUE
03694             // assigning images from parent only if variant has no master image (#1807)
03695             if ( stristr($sCopyFieldName, '_oxthumb') || stristr($sCopyFieldName, '_oxicon') ) {
03696                 if ( $this->_isFieldEmpty( $sCopyFieldName ) && !$this->_hasMasterImage( 1 ) ) {
03697                     $this->$sCopyFieldName = clone $oParentArticle->$sCopyFieldName;
03698                 }
03699             } elseif ( stristr($sCopyFieldName, '_oxzoom') ) {
03700                 // for zoom images checking master image with specified index
03701                 // assign from parent only if no pictures to variant are added
03702                 $iIndex = (int) str_ireplace( "oxarticles__oxzoom", "", $sFieldName );
03703                 if ( $this->_isFieldEmpty( $sCopyFieldName ) && !$this->_hasMasterImage( $iIndex ) && !$this->_hasMasterImage( 1 ) ) {
03704                     $this->$sCopyFieldName = clone $oParentArticle->$sCopyFieldName;
03705                 }
03706             } elseif ($this->_isFieldEmpty($sCopyFieldName) || in_array( $sCopyFieldName, $this->_aCopyParentField ) ) {
03707                 $this->$sCopyFieldName = clone $oParentArticle->$sCopyFieldName;
03708             }
03709         }
03710     }
03711 
03717     public function getParentArticle()
03718     {
03719         if ( ( $sParentId = $this->oxarticles__oxparentid->value ) ) {
03720             $sIndex = $sParentId . "_" . $this->getLanguage();
03721             if ( !isset( self::$_aLoadedParents[$sIndex] ) ) {
03722                 self::$_aLoadedParents[$sIndex] = oxNew( 'oxarticle' );
03723                 self::$_aLoadedParents[$sIndex]->_blLoadPrice    = false;
03724                 self::$_aLoadedParents[$sIndex]->_blLoadVariants = false;
03725                 self::$_aLoadedParents[$sIndex]->loadInLang( $this->getLanguage(), $sParentId );
03726             }
03727             return self::$_aLoadedParents[$sIndex];
03728         }
03729     }
03730 
03736     protected function _assignParentFieldValues()
03737     {
03738         startProfile('articleAssignParentInternal');
03739         if ( $this->oxarticles__oxparentid->value ) {
03740             // yes, we are in fact a variant
03741             if ( !$this->isAdmin() || ( $this->_blLoadParentData && $this->isAdmin() ) ) {
03742                 foreach ( $this->_aFieldNames as $sFieldName => $sVal ) {
03743                     $this->_assignParentFieldValue( $sFieldName );
03744                 }
03745             }
03746         }
03747         stopProfile('articleAssignParentInternal');
03748     }
03749 
03755     protected function _assignNotBuyableParent()
03756     {
03757         if ( !$this->getConfig()->getConfigParam( 'blVariantParentBuyable' ) &&
03758              ($this->_blHasVariants || $this->oxarticles__oxvarstock->value || $this->oxarticles__oxvarcount->value )) {
03759             $this->_blNotBuyableParent = true;
03760 
03761         }
03762     }
03763 
03769     protected function _assignStock()
03770     {
03771         $myConfig = $this->getConfig();
03772         // -----------------------------------
03773         // stock
03774         // -----------------------------------
03775 
03776         // #1125 A. must round (using floor()) value taken from database and cast to int
03777         if (!$myConfig->getConfigParam( 'blAllowUnevenAmounts' ) && !$this->isAdmin() ) {
03778             $this->oxarticles__oxstock = new oxField((int) floor($this->oxarticles__oxstock->value));
03779         }
03780         //GREEN light
03781         $this->_iStockStatus = 0;
03782 
03783         // if we have flag /*1 or*/ 4 - we show always green light
03784         if ( $myConfig->getConfigParam( 'blUseStock' ) && /*$this->oxarticles__oxstockflag->value != 1 && */ $this->oxarticles__oxstockflag->value != 4) {
03785             //ORANGE light
03786             $iStock = $this->oxarticles__oxstock->value;
03787 
03788             if ($this->_blNotBuyableParent) {
03789                 $iStock = $this->oxarticles__oxvarstock->value;
03790             }
03791 
03792 
03793             if ( $iStock <= $myConfig->getConfigParam( 'sStockWarningLimit' ) && $iStock > 0) {
03794                 $this->_iStockStatus = 1;
03795             }
03796 
03797             //RED light
03798             if ($iStock <= 0) {
03799                 $this->_iStockStatus = -1;
03800             }
03801         }
03802 
03803 
03804         // stock
03805         if ( $myConfig->getConfigParam( 'blUseStock' ) && ($this->oxarticles__oxstockflag->value == 3 || $this->oxarticles__oxstockflag->value == 2)) {
03806             $iOnStock = $this->oxarticles__oxstock->value;
03807             if ($this->getConfig()->getConfigParam( 'blPsBasketReservationEnabled' )) {
03808                 $iOnStock += $this->getSession()->getBasketReservations()->getReservedAmount($this->getId());
03809             }
03810             if ($iOnStock <= 0) {
03811                 $this->setBuyableState( false );
03812             }
03813         }
03814 
03815         //exceptional handling for variant parent stock:
03816         if ($this->_blNotBuyable && $this->oxarticles__oxvarstock->value ) {
03817             $this->setBuyableState( true );
03818             //but then at least setting notBuaybleParent to true
03819             $this->_blNotBuyableParent = true;
03820         }
03821 
03822         //special treatment for lists when blVariantParentBuyable config option is set to false
03823         //then we just hide "to basket" button.
03824         //if variants are not loaded in the list and this article has variants and parent is not buyable then this article is not buyable
03825         if ( !$myConfig->getConfigParam( 'blVariantParentBuyable' ) && !$myConfig->getConfigParam( 'blLoadVariants' ) && $this->oxarticles__oxvarstock->value) {
03826             $this->setBuyableState( false );
03827         }
03828 
03829         //setting to non buyable when variant list is empty (for example not loaded or inactive) and $this is non buyable parent
03830         if (!$this->_blNotBuyable && $this->_blNotBuyableParent && $this->oxarticles__oxvarcount->value == 0) {
03831             $this->setBuyableState( false );
03832         }
03833     }
03834 
03842     protected function _assignPrices()
03843     {
03844         $myConfig = $this->getConfig();
03845 
03846         // Performance
03847         if ( !$myConfig->getConfigParam( 'bl_perfLoadPrice' ) || !$this->_blLoadPrice ) {
03848             return;
03849         }
03850 
03851         //getting min and max prices of variants
03852         if ( $this->_hasAnyVariant() ) {
03853             $this->_applyRangePrice();
03854         }
03855     }
03856 
03862     protected function _assignPersistentParam()
03863     {
03864         // Persistent Parameter Handling
03865         $aPersParam     = oxSession::getVar( 'persparam');
03866         if ( isset( $aPersParam) && isset( $aPersParam[$this->getId()])) {
03867             $this->_aPersistParam = $aPersParam[$this->getId()];
03868         }
03869     }
03870 
03876     protected function _assignDynImageDir()
03877     {
03878         $myConfig = $this->getConfig();
03879 
03880         $sThisShop = $this->oxarticles__oxshopid->value;
03881 
03882         $this->_sDynImageDir   = $myConfig->getPictureUrl( null, false );
03883         $this->dabsimagedir    = $myConfig->getPictureDir( false ); //$sThisShop
03884         $this->nossl_dimagedir = $myConfig->getPictureUrl( null, false, false, null, $sThisShop ); //$sThisShop
03885         $this->ssl_dimagedir   = $myConfig->getPictureUrl( null, false, true, null, $sThisShop ); //$sThisShop
03886     }
03887 
03893     protected function _assignComparisonListFlag()
03894     {
03895         // #657 add a flag if article is on comparisonlist
03896 
03897         $aItems = oxSession::getVar('aFiltcompproducts');
03898         if ( isset( $aItems[$this->getId()])) {
03899             $this->_blIsOnComparisonList = true;
03900         }
03901     }
03902 
03903 
03911     protected function _insert()
03912     {
03913         // set oxinsert
03914         $sNow = date('Y-m-d H:i:s', oxRegistry::get("oxUtilsDate")->getTime());
03915         $this->oxarticles__oxinsert    = new oxField( $sNow );
03916         if ( !is_object($this->oxarticles__oxsubclass) || $this->oxarticles__oxsubclass->value == '') {
03917             $this->oxarticles__oxsubclass = new oxField('oxarticle');
03918         }
03919 
03920         return parent::_insert();
03921     }
03922 
03928     protected function _update()
03929     {
03930 
03931         $this->_skipSaveFields();
03932 
03933         $myConfig = $this->getConfig();
03934 
03935 
03936         $blRes =  parent::_update();
03937 
03938 
03939         return $blRes;
03940     }
03941 
03947     public function updateVariantsRemind()
03948     {
03949         // check if it is parent article
03950         if ( !$this->isVariant() && $this->_hasAnyVariant()) {
03951             $oDb = oxDb::getDb();
03952             $sOxId = $oDb->quote($this->getId());
03953             $sOxShopId = $oDb->quote($this->getShopId());
03954             $iRemindActive = $oDb->quote($this->oxarticles__oxremindactive->value);
03955             $sUpdate = "
03956                 update oxarticles
03957                     set oxremindactive = $iRemindActive
03958                     where oxparentid = $sOxId and
03959                           oxshopid = $sOxShopId
03960             ";
03961             $oDb->execute( $sUpdate );
03962         }
03963     }
03964 
03972     protected function _deleteRecords($sOXID)
03973     {
03974         $oDb = oxDb::getDb();
03975 
03976         $sOXID = $oDb->quote($sOXID);
03977 
03978         //remove other records
03979         $sDelete = 'delete from oxobject2article where oxarticlenid = '.$sOXID.' or oxobjectid = '.$sOXID.' ';
03980         $oDb->execute( $sDelete);
03981 
03982         $sDelete = 'delete from oxobject2attribute where oxobjectid = '.$sOXID.' ';
03983         $oDb->execute( $sDelete);
03984 
03985         $sDelete = 'delete from oxobject2category where oxobjectid = '.$sOXID.' ';
03986         $oDb->execute( $sDelete);
03987 
03988         $sDelete = 'delete from oxobject2selectlist where oxobjectid = '.$sOXID.' ';
03989         $oDb->execute( $sDelete);
03990 
03991         $sDelete = 'delete from oxprice2article where oxartid = '.$sOXID.' ';
03992         $oDb->execute( $sDelete);
03993 
03994         $sDelete = 'delete from oxreviews where oxtype="oxarticle" and oxobjectid = '.$sOXID.' ';
03995         $oDb->execute( $sDelete);
03996 
03997         $sDelete = 'delete from oxratings where oxobjectid = '.$sOXID.' ';
03998         $rs = $oDb->execute( $sDelete );
03999 
04000         $sDelete = 'delete from oxaccessoire2article where oxobjectid = '.$sOXID.' or oxarticlenid = '.$sOXID.' ';
04001         $oDb->execute( $sDelete);
04002 
04003         //#1508C - deleting oxobject2delivery entries added
04004         $sDelete = 'delete from oxobject2delivery where oxobjectid = '.$sOXID.' and oxtype=\'oxarticles\' ';
04005         $oDb->execute( $sDelete);
04006 
04007         $sDelete = 'delete from oxartextends where oxid = '.$sOXID.' ';
04008         $oDb->execute( $sDelete);
04009 
04010         //delete the record
04011         foreach ( $this->_getLanguageSetTables( "oxartextends" ) as $sSetTbl ) {
04012             $oDb->execute( "delete from $sSetTbl where oxid = {$sOXID}" );
04013         }
04014 
04015         $sDelete = 'delete from oxactions2article where oxartid = '.$sOXID.' ';
04016         $rs = $oDb->execute( $sDelete );
04017 
04018         $sDelete = 'delete from oxobject2list where oxobjectid = '.$sOXID.' ';
04019         $rs = $oDb->execute( $sDelete );
04020 
04021 
04022         return $rs;
04023     }
04024 
04032     protected function _deleteVariantRecords( $sOXID )
04033     {
04034         if ( $sOXID ) {
04035             $oDb = oxDb::getDb();
04036             //collect variants to remove recursively
04037             $sQ = 'select oxid from '.$this->getViewName().' where oxparentid = '.$oDb->quote( $sOXID );
04038             $rs = $oDb->select( $sQ, false, false );
04039             $oArticle = oxNew("oxArticle");
04040             if ($rs != false && $rs->recordCount() > 0) {
04041                 while (!$rs->EOF) {
04042                     $oArticle->setId($rs->fields[0]);
04043                     $oArticle->delete();
04044                     $rs->moveNext();
04045                 }
04046             }
04047         }
04048     }
04049 
04055     protected function _deletePics()
04056     {
04057         $myUtilsPic = oxRegistry::get("oxUtilsPic");
04058         $myConfig   = $this->getConfig();
04059         $oPictureHandler = oxRegistry::get("oxPictureHandler");
04060 
04061         //deleting custom main icon
04062         $oPictureHandler->deleteMainIcon( $this );
04063 
04064         //deleting custom thumbnail
04065         $oPictureHandler->deleteThumbnail( $this );
04066 
04067         $sAbsDynImageDir = $myConfig->getPictureDir(false);
04068 
04069         // deleting master image and all generated images
04070         $iPicCount = $myConfig->getConfigParam( 'iPicCount' );
04071         for ( $i = 1; $i <= $iPicCount; $i++ ) {
04072             $oPictureHandler->deleteArticleMasterPicture( $this, $i );
04073         }
04074     }
04075 
04085     protected function _onChangeResetCounts( $sOxid, $sVendorId = null, $sManufacturerId = null )
04086     {
04087 
04088         $myUtilsCount = oxRegistry::get("oxUtilsCount");
04089 
04090         if ( $sVendorId ) {
04091             $myUtilsCount->resetVendorArticleCount( $sVendorId );
04092         }
04093 
04094         if ( $sManufacturerId ) {
04095             $myUtilsCount->resetManufacturerArticleCount( $sManufacturerId );
04096         }
04097 
04098         //also reseting category counts
04099         $oDb = oxDb::getDb();
04100         $sQ = "select oxcatnid from oxobject2category where oxobjectid = ".$oDb->quote($sOxid);
04101         $oRs = $oDb->select( $sQ, false, false );
04102         if ( $oRs !== false && $oRs->recordCount() > 0) {
04103             while ( !$oRs->EOF ) {
04104                 $myUtilsCount->resetCatArticleCount( $oRs->fields[0] );
04105                 $oRs->moveNext();
04106             }
04107         }
04108     }
04109 
04117     protected function _onChangeUpdateStock( $sParentID )
04118     {
04119         if ( $sParentID ) {
04120             $oDb = oxDb::getDb();
04121             $sParentIdQuoted = $oDb->quote($sParentID);
04122             $sQ = 'select oxstock, oxvendorid, oxmanufacturerid from oxarticles where oxid = '.$sParentIdQuoted;
04123             $rs = $oDb->select( $sQ, false, false );
04124             $iOldStock = $rs->fields[0];
04125             $iVendorID = $rs->fields[1];
04126             $iManufacturerID = $rs->fields[2];
04127 
04128             $sQ = 'select sum(oxstock) from '.$this->getViewName(true).' where oxparentid = '.$sParentIdQuoted.' and '. $this->getSqlActiveSnippet( true ).' and oxstock > 0 ';
04129             $iStock = (float) $oDb->getOne( $sQ, false, false );
04130 
04131             $sQ = 'update oxarticles set oxvarstock = '.$iStock.' where oxid = '.$sParentIdQuoted;
04132             $oDb->execute( $sQ );
04133 
04134             //now lets update category counts
04135             //first detect stock status change for this article (to or from 0)
04136             if ( $iStock < 0 ) {
04137                 $iStock = 0;
04138             }
04139             if ( $iOldStock < 0 ) {
04140                 $iOldStock = 0;
04141             }
04142             if ( $this->oxarticles__oxstockflag->value == 2 && $iOldStock xor $iStock ) {
04143                 //means the stock status could be changed (oxstock turns from 0 to 1 or from 1 to 0)
04144                 // so far we leave it like this but later we could move all count resets to one or two functions
04145                 $this->_onChangeResetCounts( $sParentID, $iVendorID, $iManufacturerID );
04146             }
04147         }
04148     }
04149 
04157     protected function _onChangeStockResetCount( $sOxid )
04158     {
04159         $myConfig = $this->getConfig();
04160 
04161         if ( $myConfig->getConfigParam( 'blUseStock' ) && $this->oxarticles__oxstockflag->value == 2 &&
04162            ( $this->oxarticles__oxstock->value + $this->oxarticles__oxvarstock->value ) <= 0 ) {
04163 
04164                $this->_onChangeResetCounts( $sOxid, $this->oxarticles__oxvendorid->value, $this->oxarticles__oxmanufacturerid->value );
04165         }
04166     }
04167 
04175     protected function _onChangeUpdateVarCount( $sParentID )
04176     {
04177         if ( $sParentID ) {
04178             $oDb = oxDb::getDb();
04179             $sParentIdQuoted = $oDb->quote( $sParentID );
04180             $sQ = "select count(*) as varcount from oxarticles where oxparentid = {$sParentIdQuoted}";
04181             $iVarCount = (int) $oDb->getOne( $sQ, false, false );
04182 
04183             $sQ = "update oxarticles set oxvarcount = {$iVarCount} where oxid = {$sParentIdQuoted}";
04184             $oDb->execute( $sQ );
04185         }
04186     }
04187 
04195     protected function _setVarMinMaxPrice( $sParentId )
04196     {
04197         if ( $sParentId ) {
04198             $oDb = oxDb::getDb( oxDb::FETCH_MODE_ASSOC );
04199             $sQ = '
04200                 SELECT
04201                     MIN( IF( `oxarticles`.`oxprice` > 0, `oxarticles`.`oxprice`, `p`.`oxprice` ) ) AS `varminprice`,
04202                     MAX( IF( `oxarticles`.`oxprice` > 0, `oxarticles`.`oxprice`, `p`.`oxprice` ) ) AS `varmaxprice`
04203                 FROM '. $this->getViewName(true) . ' AS `oxarticles`
04204                     LEFT JOIN '. $this->getViewName(true) . ' AS `p` ON ( `p`.`oxid` = `oxarticles`.`oxparentid` AND `p`.`oxprice` > 0 )
04205                 WHERE '. $this->getSqlActiveSnippet(true) .'
04206                     AND ( `oxarticles`.`oxparentid` = '. $oDb->quote( $sParentId ) .' )';
04207             $oDb->setFetchMode( oxDb::FETCH_MODE_ASSOC );
04208             $aPrices = $oDb->getRow( $sQ, false, false );
04209             if ( !is_null( $aPrices['varminprice'] ) || !is_null( $aPrices['varmaxprice'] ) ) {
04210                 $sQ = '
04211                     UPDATE `oxarticles`
04212                     SET
04213                         `oxvarminprice` = '. $oDb->quote( $aPrices['varminprice'] ) .',
04214                         `oxvarmaxprice` = '. $oDb->quote( $aPrices['varmaxprice'] ) .'
04215                     WHERE
04216                         `oxid` = ' . $oDb->quote( $sParentId );
04217             } else {
04218                  $sQ = '
04219                     UPDATE `oxarticles`
04220                     SET
04221                         `oxvarminprice` = `oxprice`,
04222                         `oxvarmaxprice` = `oxprice`
04223                     WHERE
04224                         `oxid` = ' . $oDb->quote( $sParentId );
04225             }
04226             $oDb->execute( $sQ );
04227         }
04228     }
04229 
04230 
04240     protected function _onChangeUpdateMinVarPrice( $sParentID )
04241     {
04242         if ( $sParentID ) {
04243             $oDb = oxDb::getDb();
04244             $sParentIdQuoted = $oDb->quote($sParentID);
04245             //#M0000883 (Sarunas)
04246             $sQ = 'select min(oxprice) as varminprice from '.$this->getViewName(true).' where '.$this->getSqlActiveSnippet(true).' and (oxparentid = '.$sParentIdQuoted.')';
04247             $dVarMinPrice = $oDb->getOne( $sQ, false, false );
04248 
04249             $dParentPrice = $oDb->getOne( "select oxprice from oxarticles where oxid = $sParentIdQuoted ", false, false );
04250 
04251             $blParentBuyable =  $this->getConfig()->getConfigParam( 'blVariantParentBuyable' );
04252 
04253             if ($dVarMinPrice) {
04254                 if ($blParentBuyable) {
04255                     $dVarMinPrice = min($dVarMinPrice, $dParentPrice);
04256                 }
04257             } else {
04258                 $dVarMinPrice = $dParentPrice;
04259             }
04260 
04261             if ( $dVarMinPrice ) {
04262                 $sQ = 'update oxarticles set oxvarminprice = '.$dVarMinPrice.' where oxid = '.$sParentIdQuoted;
04263                 $oDb->execute($sQ);
04264             }
04265         }
04266     }
04267 
04268 
04276     protected function _applyRangePrice()
04277     {
04278         //#buglist_413 if bl_perfLoadPriceForAddList variant price shouldn't be loaded too
04279         if ( !$this->getConfig()->getConfigParam( 'bl_perfLoadPrice' ) || !$this->_blLoadPrice ) {
04280             return;
04281         }
04282 
04283         $this->_blIsRangePrice = false;
04284 
04285         // if parent is buyable - do not apply range price calculations
04286         if ($this->_blSkipFromPrice || !$this->_blNotBuyableParent) {
04287             return;
04288         }
04289 
04290         if ( $this->isParentNotBuyable() && !$this->getConfig()->getConfigParam( 'blLoadVariants' )) {
04291 
04292             $dPrice = $this->_preparePrice( $this->oxarticles__oxvarminprice->value, $this->getArticleVat() );
04293             $this->getPrice()->setPrice($dPrice);
04294             $this->_blIsRangePrice = true;
04295             $this->_calculatePrice( $this->getPrice() );
04296             return;
04297 
04298         }
04299 
04300         $aPrices = array();
04301 
04302         if (!$this->_blNotBuyableParent) {
04303             $aPrices[] = $this->getPrice()->getPrice();
04304         }
04305 
04306         $aVariants = $this->getVariants(false);
04307 
04308         if (count($aVariants)) {
04309             foreach ($aVariants as $sKey => $oVariant) {
04310                 $aPrices[] = $oVariant->getPrice()->getPrice();
04311             }
04312         }
04313 
04314         if ( count( $aPrices ) ) {
04315             $dMinPrice = min( $aPrices );
04316             $dMaxPrice = max( $aPrices );
04317         }
04318 
04319         if ($this->_blNotBuyableParent && isset($dMinPrice) && $dMinPrice == $dMaxPrice) {
04320             $this->getPrice()->setPrice($dMinPrice);
04321         }
04322 
04323         if (isset($dMinPrice) && $dMinPrice != $dMaxPrice) {
04324             $this->getPrice()->setPrice($dMinPrice);
04325             $this->_blIsRangePrice = true;
04326         }
04327     }
04328 
04335     public function getProductId()
04336     {
04337         return $this->getId();
04338     }
04339 
04347     public function getProductParentId()
04348     {
04349         return $this->getParentId();
04350     }
04351 
04357     public function getParentId()
04358     {
04359         return $this->oxarticles__oxparentid->value;
04360     }
04361 
04367     public function isOrderArticle()
04368     {
04369         return false;
04370     }
04371 
04377     public function isVariant()
04378     {
04379         return (bool) ( isset( $this->oxarticles__oxparentid ) ? $this->oxarticles__oxparentid->value : false );
04380     }
04381 
04387     public function isMdVariant()
04388     {
04389         $oMdVariant = oxNew( "oxVariantHandler" );
04390 
04391         return $oMdVariant->isMdVariant($this);
04392     }
04393 
04401     public function getSqlForPriceCategories($sFields = '')
04402     {
04403         if (!$sFields) {
04404             $sFields = 'oxid';
04405         }
04406         $sSelectWhere = "select $sFields from ".$this->_getObjectViewName('oxcategories')." where";
04407         $sQuotedPrice = oxDb::getDb()->quote( $this->oxarticles__oxprice->value );
04408         return  "$sSelectWhere oxpricefrom != 0 and oxpriceto != 0 and oxpricefrom <= $sQuotedPrice and oxpriceto >= $sQuotedPrice"
04409                ." union $sSelectWhere oxpricefrom != 0 and oxpriceto = 0 and oxpricefrom <= $sQuotedPrice"
04410                ." union $sSelectWhere oxpricefrom = 0 and oxpriceto != 0 and oxpriceto >= $sQuotedPrice";
04411     }
04412 
04420     public function inPriceCategory( $sCatNid )
04421     {
04422         $oDb = oxDb::getDb();
04423 
04424         $sQuotedPrice = $oDb->quote( $this->oxarticles__oxprice->value );
04425         $sQuotedCnid = $oDb->quote( $sCatNid );
04426         return (bool) $oDb->getOne(
04427             "select 1 from ".$this->_getObjectViewName('oxcategories')." where oxid=$sQuotedCnid and"
04428            ."(   (oxpricefrom != 0 and oxpriceto != 0 and oxpricefrom <= $sQuotedPrice and oxpriceto >= $sQuotedPrice)"
04429            ." or (oxpricefrom != 0 and oxpriceto = 0 and oxpricefrom <= $sQuotedPrice)"
04430            ." or (oxpricefrom = 0 and oxpriceto != 0 and oxpriceto >= $sQuotedPrice)"
04431            .")"
04432         );
04433     }
04434 
04440     public function getMdVariants()
04441     {
04442         if ( $this->_oMdVariants ) {
04443             return $this->_oMdVariants;
04444         }
04445 
04446         $oParentArticle = $this->getParentArticle();
04447         if ( $oParentArticle ) {
04448             $oVariants = $oParentArticle->getVariants();
04449         } else {
04450             $oVariants = $this->getVariants();
04451         }
04452 
04453         $oVariantHandler = oxNew( "oxVariantHandler" );
04454         $this->_oMdVariants = $oVariantHandler->buildMdVariants( $oVariants, $this->getId() );
04455 
04456         return $this->_oMdVariants;
04457     }
04458 
04464     public function getMdSubvariants()
04465     {
04466         return $this->getMdVariants()->getMdSubvariants();
04467     }
04468 
04476     protected function _hasMasterImage( $iIndex )
04477     {
04478         $sPicName = basename($this->{"oxarticles__oxpic" . $iIndex}->value);
04479 
04480         if ( $sPicName == "nopic.jpg" || $sPicName == "" ) {
04481             return false;
04482         }
04483         if ( $this->isVariant() && $this->getParentArticle()->{"oxarticles__oxpic".$iIndex}->value == $this->{"oxarticles__oxpic".$iIndex}->value ) {
04484             return false;
04485         }
04486 
04487         $sMasterPic = 'product/'.$iIndex . "/" . $sPicName;
04488 
04489         if ( $this->getConfig()->getMasterPicturePath( $sMasterPic ) ) {
04490             return true;
04491         }
04492 
04493         return false;
04494     }
04495 
04504     public function getPictureFieldValue( $sFieldName, $iIndex = null )
04505     {
04506         if ( $sFieldName ) {
04507             $sFieldName = "oxarticles__" . $sFieldName . $iIndex;
04508             return $this->$sFieldName->value;
04509         }
04510     }
04511 
04519     public function getMasterZoomPictureUrl( $iIndex )
04520     {
04521         $sPicUrl  = false;
04522         $sPicName = basename( $this->{"oxarticles__oxpic" . $iIndex}->value );
04523 
04524         if ( $sPicName && $sPicName != "nopic.jpg" ) {
04525             $sPicUrl = $this->getConfig()->getPictureUrl( "master/product/" . $iIndex . "/" . $sPicName );
04526             if ( !$sPicUrl || basename( $sPicUrl ) == "nopic.jpg" ) {
04527                 $sPicUrl = false;
04528             }
04529         }
04530 
04531         return $sPicUrl;
04532     }
04533 
04539     public function getUnitName()
04540     {
04541         if ( $this->oxarticles__oxunitname->value ) {
04542             return oxRegistry::getLang()->translateString( $this->oxarticles__oxunitname->value );
04543         }
04544     }
04545 
04553     public function getArticleFiles( $blAddFromParent=false )
04554     {
04555         if ( $this->_aArticleFiles === null) {
04556 
04557             $this->_aArticleFiles = false;
04558 
04559             $sQ = "SELECT * FROM `oxfiles` WHERE `oxartid` = '".$this->getId()."'";
04560 
04561             if ( !$this->getConfig()->getConfigParam( 'blVariantParentBuyable' ) && $blAddFromParent ) {
04562                 $sQ .= " OR `oxartId` = '". $this->oxarticles__oxparentid->value . "'";
04563             }
04564 
04565             $oArticleFiles = oxNew("oxlist");
04566             $oArticleFiles->init("oxfile");
04567             $oArticleFiles->selectString( $sQ );
04568             $this->_aArticleFiles  = $oArticleFiles;
04569 
04570         }
04571 
04572         return $this->_aArticleFiles;
04573     }
04574 
04580     public function isDownloadable()
04581     {
04582         return $this->oxarticles__oxisdownloadable->value;
04583     }
04584 
04590     public function hasAmountPrice()
04591     {
04592         if ( self::$_blHasAmountPrice === null ) {
04593 
04594             self::$_blHasAmountPrice = false;
04595 
04596             $oDb = oxDb::getDb();
04597             $sQ = "SELECT 1 FROM `oxprice2article` LIMIT 1";
04598 
04599             if ( $oDb->getOne( $sQ ) ) {
04600                 self::$_blHasAmountPrice = true;
04601             }
04602         }
04603 
04604         return self::$_blHasAmountPrice;
04605     }
04606 
04607 
04613     protected function _isPriceViewModeNetto()
04614     {
04615         $blResult = (bool) $this->getConfig()->getConfigParam('blShowNetPrice');
04616         $oUser = $this->getArticleUser();
04617         if ( $oUser ) {
04618             $blResult = $oUser->isPriceViewModeNetto();
04619         }
04620 
04621         return $blResult;
04622     }
04623 
04624 
04632     protected function _getPriceObject( $blCalculationModeNetto = null )
04633     {
04634         $oPrice = oxNew( 'oxPrice' );
04635 
04636         if ( $blCalculationModeNetto === null ) {
04637             $blCalculationModeNetto = $this->_isPriceViewModeNetto();
04638         }
04639 
04640         if ( $blCalculationModeNetto ) {
04641             $oPrice->setNettoPriceMode();
04642         } else {
04643             $oPrice->setBruttoPriceMode();
04644         }
04645 
04646         return $oPrice;
04647     }
04648 
04649 
04657     protected function _getPriceForView( $oPrice )
04658     {
04659         if ( $this->_isPriceViewModeNetto() ) {
04660             $dPrice = $oPrice->getNettoPrice();
04661         } else {
04662             $dPrice = $oPrice->getBruttoPrice();
04663         }
04664 
04665         return $dPrice;
04666     }
04667 
04668 
04678     protected function _preparePrice( $dPrice, $dVat, $blCalculationModeNetto = null )
04679     {
04680         if ( $blCalculationModeNetto === null ) {
04681             $blCalculationModeNetto = $this->_isPriceViewModeNetto();
04682         }
04683 
04684         $blEnterNetPrice = $this->getConfig()->getConfigParam('blEnterNetPrice');
04685         if ( $blCalculationModeNetto && !$blEnterNetPrice ) {
04686             $dPrice = round( oxPrice::brutto2Netto( $dPrice, $dVat ), 2 );
04687         } elseif ( !$blCalculationModeNetto && $blEnterNetPrice ) {
04688             $dPrice = round( oxPrice::netto2Brutto( $dPrice, $dVat ), 2 );
04689         }
04690 
04691         return $dPrice;
04692     }
04693 
04694 
04701     public function getFUnitPrice()
04702     {
04703         if ($this->_fPricePerUnit == null) {
04704             if ( $oPrice = $this->getUnitPrice() ) {
04705                 if ( $dPrice = $this->_getPriceForView( $oPrice ) ) {
04706                     $this->_fPricePerUnit = oxRegistry::getLang()->formatCurrency( $dPrice );
04707                 }
04708             }
04709         }
04710 
04711         return $this->_fPricePerUnit;
04712     }
04713 
04714 
04720     public function getUnitPrice()
04721     {
04722         // Performance
04723         if ( !$this->getConfig()->getConfigParam( 'bl_perfLoadPrice' ) || !$this->_blLoadPrice ) {
04724             return;
04725         }
04726 
04727         $oPrice = null;
04728         if ( (double) $this->getUnitQuantity() && $this->oxarticles__oxunitname->value ) {
04729             $oPrice = clone $this->getPrice();
04730             $oPrice->divide( (double) $this->getUnitQuantity() );
04731         }
04732 
04733         return $oPrice;
04734     }
04735 
04736 
04744     public function getFMinPrice()
04745     {
04746         $sPrice = '';
04747         if ( $oPrice = $this->getMinPrice() ) {
04748             $dPrice = $this->_getPriceForView( $oPrice );
04749             $sPrice = oxRegistry::getLang()->formatCurrency( $dPrice );
04750         }
04751 
04752         return $sPrice;
04753     }
04754 
04762     public function getFVarMinPrice()
04763     {
04764         $sPrice = '';
04765         if ( $oPrice = $this->getVarMinPrice() ) {
04766             $dPrice = $this->_getPriceForView( $oPrice );
04767             $sPrice = oxRegistry::getLang()->formatCurrency( $dPrice );
04768         }
04769 
04770         return $sPrice;
04771     }
04772 
04778     public function getVarMinPrice()
04779     {
04780         if ( !$this->getConfig()->getConfigParam( 'bl_perfLoadPrice' ) || !$this->_blLoadPrice ) {
04781             return;
04782         }
04783 
04784         $oPrice = null;
04785         $dPrice = $this->_getVarMinPrice();
04786 
04787         $dPrice = $this->_preparePrice( $dPrice, $this->getArticleVat() );
04788 
04789 
04790         $oPrice = $this->_getPriceObject();
04791         $oPrice->setPrice( $dPrice );
04792         $this->_calculatePrice( $oPrice );
04793 
04794 
04795         return $oPrice;
04796     }
04797 
04798 
04804     public function getMinPrice()
04805     {
04806         if ( !$this->getConfig()->getConfigParam( 'bl_perfLoadPrice' ) || !$this->_blLoadPrice ) {
04807             return;
04808         }
04809 
04810         $oPrice = null;
04811         $dPrice = $this->_getPrice();
04812         if ( $this->_getVarMinPrice() !== null && $dPrice > $this->_getVarMinPrice() ) {
04813             $dPrice = $this->_getVarMinPrice();
04814         }
04815 
04816         $dPrice = $this->_preparePrice( $dPrice, $this->getArticleVat() );
04817 
04818 
04819         $oPrice = $this->_getPriceObject();
04820         $oPrice->setPrice( $dPrice );
04821         $this->_calculatePrice( $oPrice );
04822 
04823         return $oPrice;
04824     }
04825 
04826 
04832     public function isRangePrice()
04833     {
04834         if ( $this->_blIsRangePrice === null ) {
04835 
04836             $this->setRangePrice( false );
04837 
04838             if ( $this->_hasAnyVariant() ) {
04839                 $dPrice = $this->_getPrice();
04840                 $dMinPrice = $this->_getVarMinPrice();
04841                 $dMaxPrice = $this->_getVarMaxPrice();
04842 
04843                 if ( $dMinPrice != $dMaxPrice ) {
04844                     $this->setRangePrice();
04845                 } elseif ( !$this->isParentNotBuyable() &&  $dMinPrice != $dPrice ) {
04846                     $this->setRangePrice();
04847                 }
04848             }
04849         }
04850 
04851         return $this->_blIsRangePrice;
04852     }
04853 
04854 
04862     public function setRangePrice( $blIsRangePrice = true )
04863     {
04864         return $this->_blIsRangePrice = $blIsRangePrice;
04865     }
04866 
04872     protected function _getUserPriceSufix()
04873     {
04874         $sPriceSufix = '';
04875         $oUser = $this->getArticleUser();
04876 
04877         if ( $oUser ) {
04878             if ( $oUser->inGroup( 'oxidpricea' ) ) {
04879                 $sPriceSufix = 'a';
04880             } elseif ( $oUser->inGroup( 'oxidpriceb' ) ) {
04881                 $sPriceSufix = 'b';
04882             } elseif ( $oUser->inGroup( 'oxidpricec' ) ) {
04883                 $sPriceSufix = 'c';
04884             }
04885         }
04886 
04887         return $sPriceSufix;
04888     }
04889 
04890 
04896     protected function _getPrice()
04897     {
04898             $sPriceSufix = $this->_getUserPriceSufix();
04899             if ( $sPriceSufix === '') {
04900                 $dPrice = $this->oxarticles__oxprice->value;
04901             } else {
04902                 if ( $this->getConfig()->getConfigParam( 'blOverrideZeroABCPrices' ) ) {
04903                     $dPrice = ($this->{oxarticles__oxprice.$sPriceSufix}->value !=0 )? $this->{oxarticles__oxprice.$sPriceSufix}->value : $this->oxarticles__oxprice->value;
04904                 } else {
04905                     $dPrice = $this->{oxarticles__oxprice.$sPriceSufix}->value;
04906                 }
04907             }
04908         return $dPrice;
04909     }
04910 
04911 
04917     protected function _getVarMinPrice()
04918     {
04919         if ( $this->_dVarMinPrice === null) {
04920 
04921             $sPriceSufix = $this->_getUserPriceSufix();
04922             if ( $dPrice === null ) {
04923                 if ( $sPriceSufix === '' ) {
04924                     $dPrice = $this->oxarticles__oxvarminprice->value;
04925                 } else {
04926                     $sSql = 'SELECT ';
04927                     if ( $this->getConfig()->getConfigParam( 'blOverrideZeroABCPrices' ) ) {
04928                         $sSql .=  'MIN( IF(`oxprice'.$sPriceSufix.'` = 0, `oxprice`, `oxprice'.$sPriceSufix.'`) ) AS `varminprice` ';
04929                     } else {
04930                         $sSql .=  'MIN(`oxprice'.$sPriceSufix.'`) AS `varminprice` ';
04931                     }
04932 
04933                     $sSql .=  ' FROM ' . $this->getViewName(true) . '
04934                         WHERE ' .$this->getSqlActiveSnippet(true) . '
04935                             AND ( `oxparentid` = ' . oxDb::getDb()->quote( $this->getId() ) . ' )';
04936 
04937                     $dPrice = oxDb::getDb()->getOne( $sSql );
04938                 }
04939             }
04940             $this->_dVarMinPrice = $dPrice;
04941         }
04942 
04943         return $this->_dVarMinPrice;
04944     }
04945 
04951     protected function _getSubShopVarMinPrice()
04952     {
04953         $myConfig = $this->getConfig();
04954         $sShopId = $myConfig->getShopId();
04955         if ($this->getConfig()->getConfigParam( 'blMallCustomPrice' ) && $sShopId != $this->oxarticles__oxshopid->value ) {
04956             $sPriceSufix = $this->_getUserPriceSufix();
04957             $sSql = 'SELECT ';
04958             if ( $sPriceSufix != '' && $this->getConfig()->getConfigParam( 'blOverrideZeroABCPrices' ) ) {
04959                 $sSql .=  'MIN(IF(`oxfield2shop`.`oxprice'.$sPriceSufix.'` = 0, `oxfield2shop`.`oxprice`, `oxfield2shop`.`oxprice'.$sPriceSufix.'`)) AS `varminprice` ';
04960             } else {
04961                 $sSql .=  'MIN(`oxfield2shop`.`oxprice'.$sPriceSufix.'`) AS `varminprice` ';
04962             }
04963             $sSql .=  ' FROM ' . getViewName('oxfield2shop') . ' AS oxfield2shop
04964                         INNER JOIN ' . $this->getViewName(true) . ' AS oxarticles ON `oxfield2shop`.`oxartid` = `oxarticles`.`oxid`
04965                         WHERE ' .$this->getSqlActiveSnippet(true) . '
04966                             AND ( `oxarticles`.`oxparentid` = ' . oxDb::getDb()->quote( $this->getId() ) . ' )
04967                             AND ( `oxfield2shop`.`oxshopid` = ' . oxDb::getDb()->quote( $sShopId ) . ' )';
04968             $dPrice = oxDb::getDb()->getOne( $sSql );
04969         }
04970         return $dPrice;
04971     }
04972 
04978     protected function _getVarMaxPrice()
04979     {
04980         if ( $this->_dVarMaxPrice === null ) {
04981 
04982             $sPriceSufix = $this->_getUserPriceSufix();
04983             if ( $dPrice === null ) {
04984                 if ( $sPriceSufix === '') {
04985                     $dPrice = $this->oxarticles__oxvarmaxprice->value;
04986                 } else {
04987                     $sSql = 'SELECT ';
04988                     if ( $this->getConfig()->getConfigParam( 'blOverrideZeroABCPrices' ) ) {
04989                         $sSql .=  'MAX( IF(`oxprice'.$sPriceSufix.'` = 0, `oxprice`, `oxprice'.$sPriceSufix.'`) ) AS `varmaxprice` ';
04990                     } else {
04991                         $sSql .=  'MAX(`oxprice'.$sPriceSufix.'`) AS `varmaxprice` ';
04992                     }
04993 
04994                     $sSql .=  ' FROM ' . $this->getViewName(true) . '
04995                         WHERE ' .$this->getSqlActiveSnippet(true) . '
04996                             AND ( `oxparentid` = ' . oxDb::getDb()->quote( $this->getId() ) . ' )';
04997 
04998                     $dPrice = oxDb::getDb()->getOne( $sSql );
04999                 }
05000             }
05001             $this->_dVarMaxPrice = $dPrice;
05002         }
05003 
05004         return $this->_dVarMaxPrice;
05005     }
05006 
05012     protected function _getSubShopVarMaxPrice()
05013     {
05014         $myConfig = $this->getConfig();
05015         $sShopId = $myConfig->getShopId();
05016         if ($this->getConfig()->getConfigParam( 'blMallCustomPrice' ) && $sShopId != $this->oxarticles__oxshopid->value ) {
05017             $sPriceSufix = $this->_getUserPriceSufix();
05018             $sSql = 'SELECT ';
05019             if ( $sPriceSufix != '' && $this->getConfig()->getConfigParam( 'blOverrideZeroABCPrices' ) ) {
05020                 $sSql .=  'MAX(IF(`oxfield2shop`.`oxprice'.$sPriceSufix.'` = 0, `oxfield2shop`.`oxprice`, `oxfield2shop`.`oxprice'.$sPriceSufix.'`)) AS `varmaxprice` ';
05021             } else {
05022                 $sSql .=  'MAX(`oxfield2shop`.`oxprice'.$sPriceSufix.'`) AS `varmaxprice` ';
05023             }
05024             $sSql .=  ' FROM ' . getViewName('oxfield2shop') . ' AS oxfield2shop
05025                         INNER JOIN ' . $this->getViewName(true) . ' AS oxarticles ON `oxfield2shop`.`oxartid` = `oxarticles`.`oxid`
05026                         WHERE ' .$this->getSqlActiveSnippet(true) . '
05027                             AND ( `oxarticles`.`oxparentid` = ' . oxDb::getDb()->quote( $this->getId() ) . ' )
05028                             AND ( `oxfield2shop`.`oxshopid` = ' . oxDb::getDb()->quote( $sShopId ) . ' )';
05029             $dPrice = oxDb::getDb()->getOne( $sSql );
05030         }
05031         return $dPrice;
05032     }
05033 
05034     public function getUnitQuantity()
05035     {
05036         return $this->oxarticles__oxunitquantity->value;
05037     }
05038 
05039 }