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 {
00024     protected $_sClassName = 'oxarticle';
00025 
00031     protected $_blUseLazyLoading = true;
00032 
00038     protected $_sItemKey;
00039 
00045     protected $_blCalcPrice    = true;
00046 
00051     protected $_oPrice      = null;
00052 
00053 
00059     protected $_dVarMinPrice = null;
00060 
00066     protected $_dVarMaxPrice = null;
00067 
00073     protected $_dArticleVat = null;
00074 
00080     protected $_aPersistParam  = null;
00081 
00087     protected $_blNotBuyable   = false;
00088 
00095     protected $_blLoadVariants = true;
00096 
00102     protected $_aVariants = null;
00103 
00109     protected $_aVariantsWithNotOrderables = null;
00110 
00119     protected $_blNotBuyableParent  = false;
00120 
00121 
00125     protected $_blHasVariants = false;
00126 
00130     protected $_blHasMdVariants = false;
00131 
00136     protected $_blIsOnComparisonList = false;
00137 
00142     protected $_oUser = null;
00143 
00149     protected $_blLoadPrice = true;
00150 
00157     protected $_blSkipAbPrice = false;
00158 
00165     protected $_fPricePerUnit = null;
00166 
00170     protected $_blLoadParentData = false;
00171 
00175     protected $_blAllowEmptyParentId = false;
00176 
00180     protected $_blSkipAssign = false;
00181 
00187     protected $_blSkipDiscounts = null;
00188 
00193     protected $_oAttributeList = null;
00194 
00195 
00201     protected $_blIsRangePrice = null;
00202 
00208     protected $_aMediaUrls = null;
00209 
00215     static protected $_aLoadedParents;
00216 
00222     static protected $_aSelList;
00223 
00229     protected $_aDispSelList;
00230 
00236     protected $_blIsSeoObject = true;
00237 
00243     protected $_oAmountPriceList = null;
00244 
00253     protected $_iLinkType = 0;
00254 
00260     protected $_aStdUrls = array();
00261 
00267     protected $_aSeoUrls = array();
00268 
00274     protected $_aSeoAddParams = array();
00275 
00281     protected $_aStdAddParams = array();
00282 
00288     protected $_sDynImageDir = null;
00289 
00295     protected $_sMoreDetailLink = null;
00296 
00302     protected $_sToBasketLink = null;
00303 
00309     protected $_iStockStatus = null;
00310 
00316     protected $_oTPrice = null;
00317 
00323     protected $_oAmountPriceInfo = null;
00324 
00330     protected $_dAmountPrice = null;
00331 
00337     protected static $_aArticleManufacturers = array();
00338 
00344     protected static $_aArticleVendors = array();
00345 
00351     protected static $_aArticleCats = array();
00352 
00358     protected $_aNonCopyParentFields = array('oxarticles__oxinsert',
00359                                              'oxarticles__oxtimestamp',
00360                                              'oxarticles__oxnid',
00361                                              'oxarticles__oxid',
00362                                              'oxarticles__oxparentid');
00363 
00369     protected $_aCopyParentField = array('oxarticles__oxnonmaterial',
00370                                          'oxarticles__oxfreeshipping',
00371                                          //'oxarticles__oxremindactive',
00372                                          'oxarticles__oxisdownloadable');
00373 
00379     protected $_oMdVariants = null;
00380 
00386     protected $_oLongDesc = null;
00387 
00395     protected $_aVariantSelections = array();
00396 
00401     protected static $_aSelections = array();
00402 
00407     protected static $_aCategoryCache = null;
00408 
00413     protected static $_blHasAmountPrice = null;
00414 
00419     protected $_aArticleFiles = null;
00420 
00425     protected $_blCanUpdateAnyField = null;
00426 
00435     public function __construct($aParams = null)
00436     {
00437         if ( $aParams && is_array($aParams)) {
00438             foreach ( $aParams as $sParam => $mValue) {
00439                 $this->$sParam = $mValue;
00440             }
00441         }
00442         parent::__construct();
00443         $this->init( 'oxarticles' );
00444     }
00445 
00454     public function __get($sName)
00455     {
00456         $this->$sName = parent::__get($sName);
00457         if ( $this->$sName ) {
00458             // since the field could have been loaded via lazy loading
00459             $this->_assignParentFieldValue($sName);
00460         }
00461 
00462         return $this->$sName;
00463     }
00464 
00473     public function __set( $sName, $sValue )
00474     {
00475         parent::__set( $sName, $sValue );
00476     }
00477 
00485     public function setId( $sId = null )
00486     {
00487         $sId = parent::setId( $sId );
00488 
00489         // TODO: in oxbase::setId make it to check if exists and update, not recreate, then delete this overload
00490         $this->oxarticles__oxnid = $this->oxarticles__oxid;
00491 
00492         return $sId;
00493     }
00494 
00504     public function getActiveCheckQuery( $blForceCoreTable = null )
00505     {
00506         $sTable = $this->getViewName( $blForceCoreTable );
00507 
00508         // check if article is still active
00509         $sQ = " $sTable.oxactive = 1 ";
00510 
00511         // enabled time range check ?
00512         if ( $this->getConfig()->getConfigParam( 'blUseTimeCheck' ) ) {
00513             $sDate = date( 'Y-m-d H:i:s', oxRegistry::get("oxUtilsDate")->getTime() );
00514             $sQ = "( $sQ or ( $sTable.oxactivefrom < '$sDate' and $sTable.oxactiveto > '$sDate' ) ) ";
00515         }
00516 
00517         return $sQ;
00518     }
00519 
00533     public function getStockCheckQuery( $blForceCoreTable = null )
00534     {
00535         $myConfig = $this->getConfig();
00536         $sTable = $this->getViewName( $blForceCoreTable );
00537 
00538         $sQ = "";
00539 
00540         //do not check for variants
00541         if ( $myConfig->getConfigParam( 'blUseStock' ) ) {
00542             $sQ = " and ( $sTable.oxstockflag != 2 or ( $sTable.oxstock + $sTable.oxvarstock ) > 0  ) ";
00543             //V #M513: When Parent article is not purchaseble, it's visibility should be displayed in shop only if any of Variants is available.
00544             if ( !$myConfig->getConfigParam( 'blVariantParentBuyable' ) ) {
00545                 $sTimeCheckQ = '';
00546                 if ( $myConfig->getConfigParam( 'blUseTimeCheck' ) ) {
00547                      $sDate = date( 'Y-m-d H:i:s', oxRegistry::get("oxUtilsDate")->getTime() );
00548                      $sTimeCheckQ = " or ( art.oxactivefrom < '$sDate' and art.oxactiveto > '$sDate' )";
00549                 }
00550                 $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 ) ) ";
00551             }
00552         }
00553 
00554         return $sQ;
00555     }
00556 
00568     public function getVariantsQuery( $blRemoveNotOrderables, $blForceCoreTable = null  )
00569     {
00570         $sTable = $this->getViewName( $blForceCoreTable );
00571         $sQ = " and $sTable.oxparentid = '".$this->getId()."' ";
00572 
00573         //checking if variant is active and stock status
00574         if ( $this->getConfig()->getConfigParam( 'blUseStock' ) ) {
00575             $sQ .= " and ( $sTable.oxstock > 0 or ( $sTable.oxstock <= 0 and $sTable.oxstockflag != 2 ";
00576             if ( $blRemoveNotOrderables ) {
00577                 $sQ .= " and $sTable.oxstockflag != 3 ";
00578             }
00579             $sQ .= " ) ) ";
00580         }
00581 
00582         return $sQ;
00583     }
00584 
00592     public function getSqlActiveSnippet( $blForceCoreTable = null )
00593     {
00594         $myConfig = $this->getConfig();
00595 
00596         // check if article is still active
00597         $sQ = $this->getActiveCheckQuery( $blForceCoreTable );
00598 
00599         // stock and variants check
00600         $sQ .= $this->getStockCheckQuery( $blForceCoreTable );
00601 
00602 
00603         return "( $sQ ) ";
00604     }
00605 
00613     public function setSkipAssign($blSkipAssign)
00614     {
00615         $this->_blSkipAssign = $blSkipAssign;
00616     }
00617 
00623     public function disablePriceLoad()
00624     {
00625         $this->_blLoadPrice = false;
00626     }
00627 
00633     public function enablePriceLoad()
00634     {
00635         $this->_blLoadPrice = true;
00636     }
00637 
00643     public function getItemKey()
00644     {
00645         return $this->_sItemKey;
00646     }
00647 
00655     public function setItemKey($sItemKey)
00656     {
00657         $this->_sItemKey = $sItemKey;
00658     }
00659 
00667     public function setNoVariantLoading( $blLoadVariants )
00668     {
00669         $this->_blLoadVariants = !$blLoadVariants;
00670     }
00671 
00677     public function isBuyable()
00678     {
00679         if ($this->_blNotBuyableParent) {
00680             return false;
00681         }
00682 
00683         return !$this->_blNotBuyable;
00684     }
00685 
00691     public function getPersParams()
00692     {
00693         return $this->_aPersistParam;
00694     }
00695 
00701     public function isOnComparisonList()
00702     {
00703         return $this->_blIsOnComparisonList;
00704     }
00705 
00713     public function setOnComparisonList( $blOnList )
00714     {
00715         $this->_blIsOnComparisonList = $blOnList;
00716     }
00717 
00725     public function setLoadParentData($blLoadParentData)
00726     {
00727         $this->_blLoadParentData = $blLoadParentData;
00728     }
00729 
00739     public function setSkipAbPrice( $blSkipAbPrice = null )
00740     {
00741         $this->_blSkipAbPrice = $blSkipAbPrice;
00742     }
00743 
00744 
00752     public function isMultilingualField($sFieldName)
00753     {
00754         switch ($sFieldName) {
00755             case "oxlongdesc":
00756             case "oxtags":
00757                 return true;
00758         }
00759 
00760         return parent::isMultilingualField($sFieldName);
00761     }
00762 
00768     public function isVisible()
00769     {
00770 
00771         // admin preview mode
00772         if ( ( $blCanPreview = oxRegistry::getUtils()->canPreview() ) !== null ) {
00773             return $blCanPreview;
00774         }
00775 
00776         // active ?
00777         $sNow = date('Y-m-d H:i:s');
00778         if ( !$this->oxarticles__oxactive->value &&
00779              (  $this->oxarticles__oxactivefrom->value > $sNow ||
00780                 $this->oxarticles__oxactiveto->value < $sNow
00781              )) {
00782             return false;
00783         }
00784 
00785         // stock flags
00786         if ( $this->getConfig()->getConfigParam( 'blUseStock' ) && $this->oxarticles__oxstockflag->value == 2) {
00787             $iOnStock = $this->oxarticles__oxstock->value + $this->oxarticles__oxvarstock->value;
00788             if ($this->getConfig()->getConfigParam( 'blPsBasketReservationEnabled' )) {
00789                 $iOnStock += $this->getSession()->getBasketReservations()->getReservedAmount($this->getId());
00790             }
00791             if ( $iOnStock <= 0 ) {
00792                 return false;
00793             }
00794         }
00795 
00796         return true;
00797     }
00798 
00807     public function assign( $aRecord )
00808     {
00809 
00810         startProfile('articleAssign');
00811 
00812         // load object from database
00813         parent::assign( $aRecord );
00814 
00815         $this->oxarticles__oxnid = $this->oxarticles__oxid;
00816 
00817         // check for simple article.
00818         if ($this->_blSkipAssign) {
00819             return;
00820         }
00821 
00822         $this->_assignParentFieldValues();
00823         $this->_assignNotBuyableParent();
00824 
00825 
00826         $this->_assignStock();
00827         $this->_assignPersistentParam();
00828         $this->_assignDynImageDir();
00829         $this->_assignComparisonListFlag();
00830 
00831 
00832         stopProfile('articleAssign');
00833     }
00834 
00835 
00843     protected function _loadFromDb( $sOXID )
00844     {
00845         $sSelect = $this->buildSelectString( array( $this->getViewName().".oxid" => $sOXID ));
00846 
00847         $aData = oxDb::getDb( oxDb::FETCH_MODE_ASSOC )->getRow( $sSelect );
00848 
00849         return $aData;
00850     }
00851 
00862     public function load( $sOXID )
00863     {
00864         // A. #1325 resetting to avoid problems when reloading (details etc)
00865         $this->_blNotBuyableParent = false;
00866 
00867 
00868             $aData = $this->_loadFromDb( $sOXID );
00869 
00870         if ( $aData ) {
00871             $this->assign( $aData );
00872             // convert date's to international format
00873             $this->_isLoaded = true;
00874             return true;
00875         }
00876 
00877         return false;
00878     }
00879 
00880 
00888     public function addToRatingAverage( $iRating )
00889     {
00890         $dOldRating = $this->oxarticles__oxrating->value;
00891         $dOldCnt    = $this->oxarticles__oxratingcnt->value;
00892         $this->oxarticles__oxrating->setValue(( $dOldRating * $dOldCnt + $iRating ) / ($dOldCnt + 1));
00893         $this->oxarticles__oxratingcnt->setValue($dOldCnt + 1);
00894         $dRating = ( $dOldRating * $dOldCnt + $iRating ) / ($dOldCnt + 1);
00895         $dRatingCnt = (int) ($dOldCnt + 1);
00896         // oxarticles.oxtimestamp = oxarticles.oxtimestamp to keep old timestamp value
00897         $oDb = oxDb::getDb();
00898         $oDb->execute( 'update oxarticles set oxarticles.oxrating = '.$dRating.',oxarticles.oxratingcnt = '.$dRatingCnt.', oxarticles.oxtimestamp = oxarticles.oxtimestamp where oxarticles.oxid = '.$oDb->quote( $this->getId() ) );
00899 
00900     }
00901 
00909     public function setRatingAverage( $iRating )
00910     {
00911          $this->oxarticles__oxrating = new oxField( $iRating );
00912     }
00913 
00921     public function setRatingCount( $iRatingCnt )
00922     {
00923          $this->oxarticles__oxratingcnt = new oxField( $iRatingCnt );
00924     }
00925 
00933     public function getArticleRatingAverage( $blIncludeVariants = false )
00934     {
00935         if ( !$blIncludeVariants ) {
00936             return round( $this->oxarticles__oxrating->value, 1);
00937         } else {
00938             $oRating = oxNew( 'oxRating' );
00939             return $oRating->getRatingAverage( $this->getId(), 'oxarticle', $this->_getVariantsIds() );
00940         }
00941     }
00942 
00950     public function getArticleRatingCount( $blIncludeVariants = false )
00951     {
00952         if ( !$blIncludeVariants ) {
00953             return $this->oxarticles__oxratingcnt->value;
00954         } else {
00955             $oRating = oxNew( 'oxRating' );
00956             return $oRating->getRatingCount( $this->getId(), 'oxarticle', $this->_getVariantsIds() );
00957         }
00958     }
00959 
00960 
00966     public function getReviews()
00967     {
00968         $aIds = array($this->getId());
00969 
00970         if ( $this->oxarticles__oxparentid->value ) {
00971             $aIds[] = $this->oxarticles__oxparentid->value;
00972         }
00973 
00974         // showing variant reviews ..
00975         if ( $this->getConfig()->getConfigParam( 'blShowVariantReviews' ) ) {
00976             $aAdd = $this->_getVariantsIds();
00977             if (is_array($aAdd)) {
00978                 $aIds = array_merge($aIds, $aAdd);
00979             }
00980         }
00981 
00982         $oReview = oxNew('oxreview');
00983         $oRevs = $oReview->loadList('oxarticle', $aIds);
00984 
00985         //if no review found, return null
00986         if ( $oRevs->count() < 1 ) {
00987             return null;
00988         }
00989 
00990         return $oRevs;
00991     }
00992 
00998     public function getCrossSelling()
00999     {
01000         $oCrosslist = oxNew( "oxarticlelist");
01001         $oCrosslist->loadArticleCrossSell($this->oxarticles__oxid->value);
01002         if ( $oCrosslist->count() ) {
01003             return $oCrosslist;
01004         }
01005     }
01006 
01012     public function getAccessoires()
01013     {
01014         $myConfig = $this->getConfig();
01015 
01016         // Performance
01017         if ( !$myConfig->getConfigParam( 'bl_perfLoadAccessoires' ) ) {
01018             return;
01019         }
01020 
01021         $oAcclist = oxNew( "oxarticlelist");
01022         $oAcclist->setSqlLimit( 0, $myConfig->getConfigParam( 'iNrofCrossellArticles' ));
01023         $oAcclist->loadArticleAccessoires($this->oxarticles__oxid->value);
01024 
01025         if ( $oAcclist->count()) {
01026             return $oAcclist;
01027         }
01028     }
01029 
01035     public function getSimilarProducts()
01036     {
01037         // Performance
01038         $myConfig = $this->getConfig();
01039         if ( !$myConfig->getConfigParam( 'bl_perfLoadSimilar' ) ) {
01040             return;
01041         }
01042 
01043         $sArticleTable = $this->getViewName();
01044 
01045         $sAttribs = '';
01046         $iCnt = 0;
01047         $this->_getAttribsString($sAttribs, $iCnt);
01048 
01049         if ( !$sAttribs) {
01050             return null;
01051         }
01052 
01053         $aList = $this->_getSimList($sAttribs, $iCnt);
01054 
01055         if ( count( $aList ) ) {
01056             uasort( $aList, 'cmpart');
01057 
01058             $sSearch = $this->_generateSimListSearchStr($sArticleTable, $aList);
01059 
01060             $oSimilarlist = oxNew( 'oxarticlelist' );
01061             $oSimilarlist->setSqlLimit( 0, $myConfig->getConfigParam( 'iNrofSimilarArticles' ));
01062             $oSimilarlist->selectString( $sSearch);
01063 
01064             return $oSimilarlist;
01065         }
01066     }
01067 
01073     public function getCustomerAlsoBoughtThisProducts()
01074     {
01075         // Performance
01076         $myConfig = $this->getConfig();
01077         if ( !$myConfig->getConfigParam( 'bl_perfLoadCustomerWhoBoughtThis' ) ) {
01078             return;
01079         }
01080 
01081         // selecting products that fits
01082         $sQ = $this->_generateSearchStrForCustomerBought();
01083 
01084         $oArticles = oxNew( 'oxarticlelist' );
01085         $oArticles->setSqlLimit( 0, $myConfig->getConfigParam( 'iNrofCustomerWhoArticles' ));
01086         $oArticles->selectString( $sQ );
01087         if ( $oArticles->count() ) {
01088             return $oArticles;
01089         }
01090     }
01091 
01098     public function loadAmountPriceInfo()
01099     {
01100         $myConfig = $this->getConfig();
01101         if ( !$myConfig->getConfigParam( 'bl_perfLoadPrice' ) || !$this->_blLoadPrice || !$this->_blCalcPrice || !$this->hasAmountPrice() ) {
01102             return array();
01103         }
01104 
01105         if ( $this->_oAmountPriceInfo === null ) {
01106             $this->_oAmountPriceInfo = array();
01107             if ( count( ( $aAmPriceList = $this->_getAmountPriceList()->getArray() ) ) ) {
01108                 $this->_oAmountPriceInfo = $this->_fillAmountPriceList( $aAmPriceList );
01109             }
01110         }
01111         return $this->_oAmountPriceInfo;
01112     }
01113 
01121     public function getSelectLists($sKeyPrefix = null)
01122     {
01123         //#1468C - more then one article in basket with different selectlist...
01124         //optionall function parameter $sKeyPrefix added, used only in basket.php
01125         $sKey = $this->getId();
01126         if ( isset( $sKeyPrefix ) ) {
01127             $sKey = $sKeyPrefix.'__'.$sKey;
01128         }
01129 
01130         if ( !isset( self::$_aSelList[$sKey] ) ) {
01131             $oDb = oxDb::getDb();
01132             $sSLViewName = getViewName( 'oxselectlist' );
01133 
01134             $sQ = "select {$sSLViewName}.* from oxobject2selectlist join {$sSLViewName} on $sSLViewName.oxid=oxobject2selectlist.oxselnid
01135                    where oxobject2selectlist.oxobjectid=%s order by oxobject2selectlist.oxsort";
01136 
01137             // all selectlists this article has
01138             $oLists = oxNew( 'oxlist' );
01139             $oLists->init( 'oxselectlist' );
01140             $oLists->selectString( sprintf( $sQ, $oDb->quote( $this->getId() ) ) );
01141 
01142             //#1104S if this is variant ant it has no selectlists, trying with parent
01143             if ( $oLists->count() == 0 && $this->oxarticles__oxparentid->value ) {
01144                 $oLists->selectString( sprintf( $sQ, $oDb->quote( $this->oxarticles__oxparentid->value ) ) );
01145             }
01146 
01147             // We do not need to calculate price here as there are method to get current article vat
01148             /*if ( $this->getPrice() != null ) {
01149                 $dVat = $this->getPrice()->getVat();
01150             }*/
01151             $dVat = $this->getArticleVat();
01152 
01153             $iCnt = 0;
01154             self::$_aSelList[$sKey] = array();
01155             foreach ( $oLists as $oSelectlist ) {
01156                 self::$_aSelList[$sKey][$iCnt] = $oSelectlist->getFieldList( $dVat );
01157                 self::$_aSelList[$sKey][$iCnt]['name'] = $oSelectlist->oxselectlist__oxtitle->value;
01158                 $iCnt++;
01159             }
01160         }
01161         return self::$_aSelList[$sKey];
01162     }
01163 
01171     protected function _hasAnyVariant( $blForceCoreTable = null )
01172     {
01173         $blHas = false;
01174         if ( ( $sId = $this->getId() ) ) {
01175             if ( $this->oxarticles__oxshopid->value == $this->getConfig()->getShopId() ) {
01176                 $blHas = (bool) $this->oxarticles__oxvarcount->value;
01177             } else {
01178                 $sArticleTable = $this->getViewName( $blForceCoreTable );
01179                 $blHas = (bool) oxDb::getDb()->getOne( "select 1 from $sArticleTable where oxparentid='{$sId}'" );
01180             }
01181         }
01182         return $blHas;
01183     }
01184 
01190     public function hasMdVariants()
01191     {
01192         return $this->_blHasMdVariants;
01193     }
01194 
01204     public function getVariantSelections( $aFilterIds = null, $sActVariantId = null, $iLimit = 0 )
01205     {
01206         $iLimit = (int) $iLimit;
01207         if ( !isset( $this->_aVariantSelections[$iLimit] ) ) {
01208             $aVariantSelections = false;
01209             if ( $this->oxarticles__oxvarcount->value ) {
01210                 $oVariants = $this->getVariants( false );
01211                 $aVariantSelections = oxNew( "oxVariantHandler" )->buildVariantSelections( $this->oxarticles__oxvarname->getRawValue(), $oVariants, $aFilterIds, $sActVariantId, $iLimit );
01212 
01213                 if ( !empty($oVariants) && empty( $aVariantSelections['rawselections'] ) ) {
01214                     $aVariantSelections = false;
01215                 }
01216             }
01217             $this->_aVariantSelections[$iLimit] = $aVariantSelections;
01218         }
01219 
01220         return $this->_aVariantSelections[$iLimit];
01221     }
01222 
01231     public function getSelections( $iLimit = null, $aFilter = null )
01232     {
01233         $sId = $this->getId() . ( (int) $iLimit );
01234         if ( !array_key_exists( $sId, self::$_aSelections ) ) {
01235 
01236             $oDb = oxDb::getDb();
01237             $sSLViewName = getViewName( 'oxselectlist' );
01238 
01239             $sQ = "select {$sSLViewName}.* from oxobject2selectlist join {$sSLViewName} on $sSLViewName.oxid=oxobject2selectlist.oxselnid
01240                    where oxobject2selectlist.oxobjectid=%s order by oxobject2selectlist.oxsort";
01241 
01242             if ( ( $iLimit = (int) $iLimit ) ) {
01243                 $sQ .= " limit $iLimit ";
01244             }
01245 
01246             // vat value for price
01247             $dVat = 0;
01248             if ( ( $oPrice = $this->getPrice() ) != null ) {
01249                 $dVat = $oPrice->getVat();
01250             }
01251 
01252             // all selectlists this article has
01253             $oList = oxNew( 'oxlist' );
01254             $oList->init( 'oxselectlist' );
01255             $oList->getBaseObject()->setVat( $dVat );
01256             $oList->selectString( sprintf( $sQ, $oDb->quote( $this->getId() ) ) );
01257 
01258             //#1104S if this is variant and it has no selectlists, trying with parent
01259             if ( $oList->count() == 0 && $this->oxarticles__oxparentid->value ) {
01260                 $oList->selectString( sprintf( $sQ, $oDb->quote( $this->oxarticles__oxparentid->value ) ) );
01261             }
01262 
01263             self::$_aSelections[$sId] = $oList->count() ? $oList : false;
01264         }
01265 
01266         if ( self::$_aSelections[$sId] ) {
01267             // marking active from filter
01268             $aFilter = ( $aFilter === null ) ? oxConfig::getParameter( "sel" ) : $aFilter;
01269             if ( $aFilter ) {
01270                 $iSelIdx = 0;
01271                 foreach ( self::$_aSelections[$sId] as $oSelection ) {
01272                     if ( isset( $aFilter[$iSelIdx] ) ) {
01273                         $oSelection->setActiveSelectionByIndex( $aFilter[$iSelIdx] );
01274                     }
01275                     $iSelIdx++;
01276                 }
01277             }
01278         }
01279 
01280         return self::$_aSelections[$sId];
01281     }
01282 
01292     protected function _loadVariantList( $blSimple, $blRemoveNotOrderables = true, $blForceCoreTable = null )
01293     {
01294         $oVariants = array();
01295         if ( ( $sId = $this->getId() ) ) {
01296             //do not load me as a parent later
01297             self::$_aLoadedParents[$sId] = $this;
01298 
01299             $myConfig = $this->getConfig();
01300 
01301             if ( !$this->_blLoadVariants ||
01302                 ( !$this->isAdmin() && !$myConfig->getConfigParam( 'blLoadVariants') ) ||
01303                 ( !$this->isAdmin() && !$this->oxarticles__oxvarcount->value ) ) {
01304                 return $oVariants;
01305             }
01306 
01307             // cache
01308             $sCacheKey = $blSimple ? "simple" : "full";
01309             if ( $blRemoveNotOrderables ) {
01310                 if ( isset( $this->_aVariants[$sCacheKey] ) ) {
01311                    return $this->_aVariants[$sCacheKey];
01312                 } else {
01313                     $this->_aVariants[$sCacheKey] = & $oVariants;
01314                 }
01315             } elseif ( !$blRemoveNotOrderables ) {
01316                 if ( isset( $this->_aVariantsWithNotOrderables[$sCacheKey] ) ) {
01317                     return $this->_aVariantsWithNotOrderables[$sCacheKey];
01318                 } else {
01319                     $this->_aVariantsWithNotOrderables[$sCacheKey] = & $oVariants;
01320                 }
01321             }
01322 
01323             if ( ( $this->_blHasVariants = $this->_hasAnyVariant( $blForceCoreTable ) ) ) {
01324 
01325                 //load simple variants for lists
01326                 if ( $blSimple ) {
01327                     $oVariants = oxNew( 'oxsimplevariantlist' );
01328                     $oVariants->setParent( $this );
01329                 } else {
01330                     //loading variants
01331                     $oVariants = oxNew( 'oxarticlelist' );
01332                     $oVariants->getBaseObject()->modifyCacheKey( '_variants' );
01333                 }
01334 
01335                 startProfile("selectVariants");
01336                 $blUseCoreTable = (bool) $blForceCoreTable;
01337                 $oBaseObject = $oVariants->getBaseObject();
01338                 $oBaseObject->setLanguage( $this->getLanguage() );
01339 
01340 
01341                 $sArticleTable = $this->getViewName( $blUseCoreTable );
01342 
01343                 $sSelect = "select ".$oBaseObject->getSelectFields( $blUseCoreTable )." from $sArticleTable where " .
01344                            $this->getActiveCheckQuery( $blUseCoreTable ) .
01345                            $this->getVariantsQuery( $blRemoveNotOrderables, $blUseCoreTable ) .
01346                            " order by $sArticleTable.oxsort";
01347 
01348 
01349                 $oVariants->selectString( $sSelect );
01350 
01351                 //if this is multidimensional variants, make additional processing
01352                 if ( $myConfig->getConfigParam( 'blUseMultidimensionVariants' ) ) {
01353                     $oMdVariants = oxNew( "oxVariantHandler" );
01354                     $this->_blHasMdVariants = $oMdVariants->isMdVariant( $oVariants->current() );
01355                 }
01356                 stopProfile("selectVariants");
01357             }
01358 
01359             //if we have variants then depending on config option the parent may be non buyable
01360             if ( !$myConfig->getConfigParam( 'blVariantParentBuyable' ) && $this->_blHasVariants ) {
01361                 $this->_blNotBuyableParent = true;
01362             }
01363 
01364             //if we have variants, but all variants are incative means article may be non buyable (depends on config option)
01365             if ( !$myConfig->getConfigParam( 'blVariantParentBuyable' ) && count( $oVariants ) == 0 && $this->_blHasVariants ) {
01366                 $this->_blNotBuyable = true;
01367             }
01368         }
01369 
01370         return $oVariants;
01371     }
01372 
01381     public function getFullVariants( $blRemoveNotOrderables = true, $blForceCoreTable = null )
01382     {
01383         return $this->_loadVariantList( false, $blRemoveNotOrderables, $blForceCoreTable );
01384     }
01385 
01394     public function getVariants( $blRemoveNotOrderables = true, $blForceCoreTable = null  )
01395     {
01396         return $this->_loadVariantList( $this->_isInList(), $blRemoveNotOrderables, $blForceCoreTable );
01397     }
01398 
01404     public function getSimpleVariants()
01405     {
01406         if ( $this->oxarticles__oxvarcount->value) {
01407             return $this->getVariants();
01408         }
01409     }
01410 
01419     public function getAdminVariants( $sLanguage = null )
01420     {
01421         $oVariants = oxNew( 'oxarticlelist');
01422         if ( ( $sId = $this->getId() ) ) {
01423 
01424             $oBaseObj = $oVariants->getBaseObject();
01425 
01426             if ( is_null( $sLanguage ) ) {
01427                 $oBaseObj->setLanguage( oxRegistry::getLang()->getBaseLanguage() );
01428             } else {
01429                 $oBaseObj->setLanguage( $sLanguage );
01430             }
01431 
01432             $sSql = "select * from ".$oBaseObj->getViewName()." where oxparentid = '{$sId}' order by oxsort ";
01433             $oVariants->selectString( $sSql );
01434 
01435             //if we have variants then depending on config option the parent may be non buyable
01436             if ( !$this->getConfig()->getConfigParam( 'blVariantParentBuyable' ) && ( $oVariants->count() > 0 ) ) {
01437                 //$this->blNotBuyable = true;
01438                 $this->_blNotBuyableParent = true;
01439             }
01440         }
01441 
01442         return $oVariants;
01443     }
01444 
01452     public function getCategory()
01453     {
01454         $oCategory = oxNew( 'oxcategory' );
01455         $oCategory->setLanguage( $this->getLanguage() );
01456 
01457         // variant handling
01458         $sOXID = $this->getId();
01459         if ( isset( $this->oxarticles__oxparentid->value ) && $this->oxarticles__oxparentid->value ) {
01460             $sOXID = $this->oxarticles__oxparentid->value;
01461         }
01462 
01463         if ( $sOXID ) {
01464             // if the oxcategory instance of this article is not cached
01465             if ( !isset( $this->_aCategoryCache[ $sOXID ] ) ) {
01466                 startPRofile( 'getCategory' );
01467                 $oStr = getStr();
01468                 $sWhere   = $oCategory->getSqlActiveSnippet();
01469                 $sSelect  = $this->_generateSearchStr( $sOXID );
01470                 $sSelect .= ( $oStr->strstr( $sSelect, 'where' )?' and ':' where ') . $sWhere . " order by oxobject2category.oxtime limit 1";
01471 
01472                 // category not found ?
01473                 if ( !$oCategory->assignRecord( $sSelect ) ) {
01474 
01475                     $sSelect  = $this->_generateSearchStr( $sOXID, true );
01476                     $sSelect .= ( $oStr->strstr( $sSelect, 'where' )?' and ':' where ') . $sWhere . " limit 1";
01477 
01478                     // looking for price category
01479                     if ( !$oCategory->assignRecord( $sSelect ) ) {
01480                         $oCategory = null;
01481                     }
01482                 }
01483                 // add the category instance to cache
01484                 $this->_aCategoryCache[ $sOXID ] = $oCategory;
01485                 stopPRofile( 'getCategory' );
01486             } else {
01487                // if the oxcategory instance is cached
01488                $oCategory = $this->_aCategoryCache[ $sOXID ];
01489             }
01490         }
01491 
01492         return $oCategory;
01493     }
01494 
01503     public function getCategoryIds( $blActCats = false, $blSkipCache = false )
01504     {
01505         if ( isset( self::$_aArticleCats[$this->getId()] ) && !$blSkipCache ) {
01506             return self::$_aArticleCats[$this->getId()];
01507         }
01508 
01509         // variant handling
01510         $sOXID = $this->getId();
01511 
01512         $aRet = $this->_getArticleCategories( $sOXID, $blActCats );
01513 
01514         if (isset( $this->oxarticles__oxparentid->value) && $this->oxarticles__oxparentid->value) {
01515             $aRet = array_merge( $aRet, $this->_getArticleCategories( $this->oxarticles__oxparentid->value, $blActCats ) );
01516         }
01517 
01518         // adding price categories if such exists
01519         $sSql = $this->getSqlForPriceCategories();
01520 
01521         $oDb = oxDb::getDb( oxDb::FETCH_MODE_ASSOC );
01522         $rs = $oDb->select( $sSql );
01523 
01524 
01525         if ($rs != false && $rs->recordCount() > 0) {
01526             while (!$rs->EOF) {
01527 
01528                 if ( is_array( $rs->fields ) ) {
01529                    $rs->fields = array_change_key_case( $rs->fields, CASE_LOWER );
01530                 }
01531 
01532 
01533                 if ( !$aRet[$rs->fields['oxid']] ) {
01534                     $aRet[] = $rs->fields['oxid'];
01535                 }
01536                 $rs->moveNext();
01537             }
01538         }
01539 
01540         return self::$_aArticleCats[$this->getId()] = $aRet;
01541     }
01542 
01551     protected function _getArticleCategories( $sOXID, $blActCats = false )
01552     {
01553         // we do not use lists here as we don't need this overhead right now
01554         $sSql = $this->_getSelectCatIds( $sOXID, $blActCats );
01555         $oDb = oxDb::getDb( oxDb::FETCH_MODE_ASSOC );
01556         $rs = $oDb->select( $sSql );
01557 
01558 
01559         $aRet = array();
01560 
01561         if ($rs != false && $rs->recordCount() > 0) {
01562             while (!$rs->EOF) {
01563                 $aRet[] = $rs->fields['oxcatnid'];
01564                 $rs->moveNext();
01565             }
01566         }
01567 
01568         return $aRet;
01569     }
01570 
01579     protected function _getSelectCatIds( $sOXID, $blActCats = false )
01580     {
01581         $sO2CView = $this->_getObjectViewName('oxobject2category');
01582         $sCatView = $this->_getObjectViewName('oxcategories');
01583         $sSelect =  "select oxobject2category.oxcatnid as oxcatnid from $sO2CView as oxobject2category left join $sCatView as oxcategories on oxcategories.oxid = oxobject2category.oxcatnid ";
01584         $sSelect .= 'where oxobject2category.oxobjectid='.oxDb::getDb()->quote($sOXID).' and oxcategories.oxid is not null and oxcategories.oxactive = 1 ';
01585         if ( $blActCats ) {
01586             $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 ";
01587         }
01588         $sSelect .= 'order by oxobject2category.oxtime ';
01589         return $sSelect;
01590     }
01591 
01601     public function getVendor( $blShopCheck = true )
01602     {
01603         if ( ( $sVendorId = $this->getVendorId() ) ) {
01604             $oVendor = oxNew( 'oxvendor' );
01605         } elseif ( !$blShopCheck && $this->oxarticles__oxvendorid->value ) {
01606                 $oVendor = oxNew( 'oxi18n' );
01607                 $oVendor->init('oxvendor');
01608                 $oVendor->setReadOnly( true );
01609             $sVendorId = $this->oxarticles__oxvendorid->value;
01610         }
01611         if ( $sVendorId && $oVendor->load( $sVendorId ) && $oVendor->oxvendor__oxactive->value ) {
01612 
01613             return $oVendor;
01614         }
01615         return null;
01616     }
01617 
01625     public function getVendorId( $blForceReload = false )
01626     {
01627         $sVendorId = false;
01628         if ( $this->oxarticles__oxvendorid->value ) {
01629                 $sVendorId = $this->oxarticles__oxvendorid->value;
01630 
01631         }
01632         return $sVendorId;
01633     }
01634 
01642     public function getManufacturerId( $blForceReload = false )
01643     {
01644         $sManufacturerId = false;
01645         if ( $this->oxarticles__oxmanufacturerid->value ) {
01646 
01647                 $sManufacturerId = $this->oxarticles__oxmanufacturerid->value;
01648 
01649         }
01650         return $sManufacturerId;
01651     }
01652 
01662     public function getManufacturer( $blShopCheck = true )
01663     {
01664             $oManufacturer = oxNew( 'oxmanufacturer' );;
01665         if ( !( $sManufacturerId = $this->getManufacturerId() ) &&
01666              !$blShopCheck && $this->oxarticles__oxmanufacturerid->value ) {
01667             $oManufacturer->setReadOnly( true );
01668             $sManufacturerId = $this->oxarticles__oxmanufacturerid->value;
01669         }
01670 
01671         if ( $sManufacturerId && $oManufacturer->load( $sManufacturerId ) ) {
01672             if ( !$this->getConfig()->getConfigParam( 'bl_perfLoadManufacturerTree' ) ) {
01673                 $oManufacturer->setReadOnly( true );
01674             }
01675             $oManufacturer = $oManufacturer->oxmanufacturers__oxactive->value ? $oManufacturer : null;
01676         } else {
01677             $oManufacturer = null;
01678         }
01679 
01680         return $oManufacturer;
01681     }
01682 
01690     public function inCategory( $sCatNid)
01691     {
01692         return in_array( $sCatNid, $this->getCategoryIds());
01693     }
01694 
01703     public function isAssignedToCategory( $sCatId )
01704     {
01705         // variant handling
01706         $sOXID = $this->getId();
01707         if ( isset( $this->oxarticles__oxparentid->value) && $this->oxarticles__oxparentid->value) {
01708             $sOXID = $this->oxarticles__oxparentid->value;
01709         }
01710 
01711         $oDb = oxDb::getDb();
01712         $sSelect = $this->_generateSelectCatStr( $sOXID, $sCatId);
01713         $sOXID = $oDb->getOne( $sSelect );
01714         // article is assigned to passed category!
01715         if ( isset( $sOXID) && $sOXID) {
01716             return true;
01717         }
01718 
01719         // maybe this category is price category ?
01720         if ( $this->getConfig()->getConfigParam( 'bl_perfLoadPrice' ) && $this->_blLoadPrice ) {
01721             $dPriceFromTo = $this->getPrice()->getBruttoPrice();
01722             if ( $dPriceFromTo > 0) {
01723                 $sSelect = $this->_generateSelectCatStr( $sOXID, $sCatId, $dPriceFromTo);
01724                 $sOXID = $oDb->getOne( $sSelect );
01725                 // article is assigned to passed category!
01726                 if ( isset( $sOXID) && $sOXID) {
01727                     return true;
01728                 }
01729             }
01730         }
01731         return false;
01732     }
01733 
01739     public function getTPrice()
01740     {
01741         if ( !$this->getConfig()->getConfigParam( 'bl_perfLoadPrice' ) || !$this->_blLoadPrice ) {
01742             return;
01743         }
01744 
01745         // return cached result, since oPrice is created ONLY in this function [or function of EQUAL level]
01746         if ( $this->_oTPrice !== null ) {
01747             return $this->_oTPrice;
01748         }
01749 
01750         $oPrice = $this->_getPriceObject();
01751 
01752         $dBasePrice = $this->oxarticles__oxtprice->value;
01753         $dBasePrice = $this->_preparePrice( $dBasePrice, $this->getArticleVat() );
01754 
01755         $oPrice->setPrice( $dBasePrice );
01756 
01757         $this->_applyVat( $oPrice, $this->getArticleVat() );
01758         $this->_applyCurrency( $oPrice );
01759 
01760         if ( $this->isParentNotBuyable() ) {
01761             // if parent article is not buyable then compare agains min article variant price
01762             $oPrice2 = $this->getVarMinPrice();
01763         } else {
01764             // else compare against article price
01765             $oPrice2 = $this->getPrice();
01766         }
01767 
01768         if ( $oPrice->getPrice() <= $oPrice2->getPrice() ) {
01769             // if RRP price is less or equal to comparable price then return
01770             return;
01771         }
01772 
01773         $this->_oTPrice = $oPrice;
01774 
01775         return $this->_oTPrice;
01776     }
01777 
01783     public function skipDiscounts()
01784     {
01785         // already loaded skip discounts config
01786         if ( $this->_blSkipDiscounts !== null ) {
01787             return $this->_blSkipDiscounts;
01788         }
01789 
01790         if ( $this->oxarticles__oxskipdiscounts->value ) {
01791             return true;
01792         }
01793 
01794 
01795         $this->_blSkipDiscounts = false;
01796         if ( oxRegistry::get("oxDiscountList")->hasSkipDiscountCategories() ) {
01797 
01798             $oDb = oxDb::getDb();
01799             $sO2CView  = getViewName( 'oxobject2category', $this->getLanguage() );
01800             $sViewName = getViewName( 'oxcategories', $this->getLanguage() );
01801             $sSelect =  "select 1 from $sO2CView as $sO2CView left join {$sViewName} on {$sViewName}.oxid = $sO2CView.oxcatnid
01802                          where $sO2CView.oxobjectid=".$oDb->quote( $this->getId() )." and {$sViewName}.oxactive = 1 and {$sViewName}.oxskipdiscounts = '1' ";
01803             $this->_blSkipDiscounts = ( $oDb->getOne( $sSelect ) == 1 );
01804         }
01805 
01806         return $this->_blSkipDiscounts;
01807     }
01808 
01816     public function setPrice(oxPrice $oPrice)
01817     {
01818         $this->_oPrice = $oPrice;
01819     }
01820 
01829     public function getBasePrice( $dAmount = 1 )
01830     {
01831         // override this function if you want e.g. different prices
01832         // for diff. user groups.
01833 
01834         // Performance
01835         $myConfig = $this->getConfig();
01836         if( !$myConfig->getConfigParam( 'bl_perfLoadPrice' ) || !$this->_blLoadPrice )
01837             return;
01838 
01839         // GroupPrice or DB price ajusted by AmountPrice
01840         $dPrice = $this->_getAmountPrice( $dAmount );
01841 
01842 
01843         return $dPrice;
01844     }
01845 
01853     public function getPrice( $dAmount = 1 )
01854     {
01855         $myConfig = $this->getConfig();
01856         // Performance
01857         if ( !$myConfig->getConfigParam( 'bl_perfLoadPrice' ) || !$this->_blLoadPrice ) {
01858             return;
01859         }
01860 
01861         // return cached result, since oPrice is created ONLY in this function [or function of EQUAL level]
01862         if ( $dAmount != 1 || $this->_oPrice === null ) {
01863 
01864             // module
01865             $dBasePrice = $this->getBasePrice( $dAmount );
01866             $dBasePrice = $this->_preparePrice( $dBasePrice, $this->getArticleVat() );
01867 
01868             $oPrice = $this->_getPriceObject();
01869 
01870             $oPrice->setPrice( $dBasePrice );
01871 
01872             // price handling
01873             if ( !$this->_blCalcPrice && $dAmount == 1 ) {
01874                 return $this->_oPrice = $oPrice;
01875             }
01876 
01877             $this->_calculatePrice( $oPrice );
01878             if ( $dAmount != 1 ) {
01879                 return $oPrice;
01880             }
01881 
01882             $this->_oPrice = $oPrice;
01883         }
01884         return $this->_oPrice;
01885     }
01886 
01895     protected function _calculatePrice( $oPrice, $dVat = null )
01896     {
01897         // apply VAT only if configuration requires it
01898         if ( isset( $dVat ) || !$this->getConfig()->getConfigParam( 'bl_perfCalcVatOnlyForBasketOrder' ) ) {
01899             $this->_applyVAT( $oPrice, isset( $dVat ) ? $dVat : $this->getArticleVat() );
01900         }
01901 
01902         // apply currency
01903         $this->_applyCurrency( $oPrice );
01904         // apply discounts
01905         if ( !$this->skipDiscounts() ) {
01906             $oDiscountList = oxRegistry::get("oxDiscountList");
01907             $aDiscounts = $oDiscountList->getArticleDiscounts( $this, $this->getArticleUser() );
01908 
01909             reset( $aDiscounts );
01910             foreach ( $aDiscounts as $oDiscount ) {
01911                 $oPrice->setDiscount($oDiscount->getAddSum(), $oDiscount->getAddSumType());
01912             }
01913             $oPrice->calculateDiscount();
01914         }
01915 
01916         return $oPrice;
01917     }
01918 
01926     public function setArticleUser($oUser)
01927     {
01928         $this->_oUser = $oUser;
01929     }
01930 
01936     public function getArticleUser()
01937     {
01938         if ($this->_oUser) {
01939             return $this->_oUser;
01940         }
01941         return $this->getUser();
01942     }
01943 
01953     public function getBasketPrice( $dAmount, $aSelList, $oBasket )
01954     {
01955         $oUser = $oBasket->getBasketUser();
01956         $this->setArticleUser( $oUser );
01957 
01958         $oBasketPrice = $this->_getPriceObject( $oBasket->isCalculationModeNetto() );
01959 
01960         // get base price
01961         $dBasePrice = $this->getBasePrice( $dAmount );
01962 
01963         $dBasePrice = $this->_modifySelectListPrice( $dBasePrice, $aSelList );
01964         $dBasePrice = $this->_preparePrice( $dBasePrice, $this->getArticleVat(), $oBasket->isCalculationModeNetto() );
01965 
01966         // applying select list price
01967 
01968         // setting price
01969         $oBasketPrice->setPrice( $dBasePrice );
01970 
01971         $dVat = oxRegistry::get("oxVatSelector")->getBasketItemVat( $this, $oBasket );
01972         $this->_calculatePrice( $oBasketPrice, $dVat );
01973 
01974         // returning final price object
01975         return $oBasketPrice;
01976     }
01977 
01986     public function delete( $sOXID = null )
01987     {
01988         if ( !$sOXID ) {
01989             $sOXID = $this->getId();
01990         }
01991         if ( !$sOXID ) {
01992             return false;
01993         }
01994 
01995 
01996 
01997         // #2339 delete first variants before deleting parent product
01998         $this->_deleteVariantRecords( $sOXID );
01999         $this->load( $sOXID );
02000         $this->_deletePics();
02001         $this->_onChangeResetCounts( $sOXID, $this->oxarticles__oxvendorid->value, $this->oxarticles__oxmanufacturerid->value );
02002 
02003         // delete self
02004         parent::delete( $sOXID );
02005 
02006         $rs = $this->_deleteRecords( $sOXID );
02007 
02008         oxRegistry::get("oxSeoEncoderArticle")->onDeleteArticle($this);
02009 
02010         $this->onChange( ACTION_DELETE, $sOXID, $this->oxarticles__oxparentid->value );
02011 
02012         return $rs->EOF;
02013     }
02014 
02023     public function reduceStock($dAmount, $blAllowNegativeStock = false)
02024     {
02025         $this->beforeUpdate();
02026 
02027         $iStockCount = $this->oxarticles__oxstock->value - $dAmount;
02028         if (!$blAllowNegativeStock && ($iStockCount < 0)) {
02029             $dAmount += $iStockCount;
02030             $iStockCount = 0;
02031         }
02032         $this->oxarticles__oxstock = new oxField($iStockCount);
02033 
02034         $oDb = oxDb::getDb();
02035         $oDb->execute( 'update oxarticles set oxarticles.oxstock = '.$oDb->quote( $iStockCount ).' where oxarticles.oxid = '.$oDb->quote( $this->getId() ) );
02036         $this->onChange( ACTION_UPDATE_STOCK );
02037         return $dAmount;
02038     }
02039 
02048     public function updateSoldAmount( $dAmount = 0 )
02049     {
02050         if ( !$dAmount ) {
02051             return;
02052         }
02053 
02054         $this->beforeUpdate();
02055 
02056         // article is not variant - should be updated current amount
02057         if ( !$this->oxarticles__oxparentid->value ) {
02058             //updating by SQL query, due to wrong behaviour if saving article using not admin mode
02059             $dAmount = (double) $dAmount;
02060             $oDb = oxDb::getDb();
02061             $rs = $oDb->execute( "update oxarticles set oxarticles.oxsoldamount = oxarticles.oxsoldamount + $dAmount where oxarticles.oxid = ".$oDb->quote($this->oxarticles__oxid->value));
02062         } elseif ( $this->oxarticles__oxparentid->value) {
02063             // article is variant - should be updated this article parent amount
02064             $oUpdateArticle = $this->getParentArticle();
02065             $oUpdateArticle->updateSoldAmount( $dAmount );
02066         }
02067 
02068         $this->onChange( ACTION_UPDATE );
02069 
02070         return $rs;
02071     }
02072 
02078     public function disableReminder()
02079     {
02080         $oDb = oxDb::getDb();
02081         return $oDb->execute( "update oxarticles set oxarticles.oxremindactive = 2 where oxarticles.oxid = ".$oDb->quote($this->oxarticles__oxid->value));
02082     }
02083 
02089     public function save()
02090     {
02091         if ( ( $blRet = parent::save() ) ) {
02092             // saving long description
02093             $this->_saveArtLongDesc();
02094         }
02095 
02096         return $blRet;
02097     }
02098 
02104     public function resetParent()
02105     {
02106         $sParentId = $this->oxarticles__oxparentid;
02107         $this->oxarticles__oxparentid = new oxField( '', oxField::T_RAW );
02108         $this->_blAllowEmptyParentId = true;
02109         $this->save();
02110         $this->_blAllowEmptyParentId = false;
02111 
02112         if ( $sParentId !== '' ) {
02113             $this->onChange( ACTION_UPDATE, null, $sParentId );
02114         }
02115     }
02116 
02117 
02124     public function getPictureGallery()
02125     {
02126         $myConfig = $this->getConfig();
02127 
02128         //initialize
02129         $blMorePic = false;
02130         $aArtPics  = array();
02131         $aArtIcons = array();
02132         $iActPicId = 1;
02133         $sActPic = $this->getPictureUrl( $iActPicId );
02134 
02135         if ( oxConfig::getParameter( 'actpicid' ) ) {
02136             $iActPicId = oxConfig::getParameter('actpicid');
02137         }
02138 
02139         $oStr = getStr();
02140         $iCntr = 0;
02141         $iPicCount = $myConfig->getConfigParam( 'iPicCount' );
02142         $blCheckActivePicId = true;
02143 
02144         for ( $i = 1; $i <= $iPicCount; $i++) {
02145             $sPicVal = $this->getPictureUrl( $i );
02146             $sIcoVal = $this->getIconUrl( $i );
02147             if ( !$oStr->strstr($sIcoVal, 'nopic_ico.jpg') && !$oStr->strstr($sIcoVal, 'nopic.jpg') &&
02148                  !$oStr->strstr($sPicVal, 'nopic_ico.jpg') && !$oStr->strstr($sPicVal, 'nopic.jpg') ) {
02149                 if ($iCntr) {
02150                     $blMorePic = true;
02151                 }
02152                 $aArtIcons[$i]= $sIcoVal;
02153                 $aArtPics[$i]= $sPicVal;
02154                 $iCntr++;
02155 
02156                 if ($iActPicId == $i) {
02157                     $sActPic = $sPicVal;
02158                     $blCheckActivePicId = false;
02159                 }
02160 
02161             } else if ( $blCheckActivePicId && $iActPicId <= $i) {
02162                 // if picture is empty, setting active pic id to next
02163                 // picture
02164                 $iActPicId++;
02165             }
02166         }
02167 
02168         $blZoomPic  = false;
02169         $aZoomPics = array();
02170         $iZoomPicCount = $myConfig->getConfigParam( 'iPicCount' );
02171 
02172         for ( $j = 1,$c = 1; $j <= $iZoomPicCount; $j++) {
02173             $sVal = $this->getZoomPictureUrl($j);
02174 
02175             if ( $sVal && !$oStr->strstr($sVal, 'nopic.jpg')) {
02176                 $blZoomPic = true;
02177                 $aZoomPics[$c]['id'] = $c;
02178                 $aZoomPics[$c]['file'] = $sVal;
02179                 //anything is better than empty name, because <img src=""> calls shop once more = x2 SLOW.
02180                 if (!$sVal) {
02181                     $aZoomPics[$c]['file'] = "nopic.jpg";
02182                 }
02183                 $c++;
02184             }
02185         }
02186 
02187         $aPicGallery = array('ActPicID' => $iActPicId,
02188                              'ActPic' => $sActPic,
02189                              'MorePics' => $blMorePic,
02190                              'Pics' => $aArtPics,
02191                              'Icons' => $aArtIcons,
02192                              'ZoomPic' => $blZoomPic,
02193                              'ZoomPics' => $aZoomPics);
02194 
02195         return $aPicGallery;
02196     }
02197 
02211     public function onChange($sAction = null, $sOXID = null, $sParentID = null)
02212     {
02213         $myConfig = $this->getConfig();
02214 
02215         if (!isset($sOXID)) {
02216             if ( $this->getId()) {
02217                 $sOXID = $this->getId();
02218             }
02219             if (!isset ($sOXID)) {
02220                 $sOXID = $this->oxarticles__oxid->value;
02221             }
02222             if ($this->oxarticles__oxparentid->value) {
02223                 $sParentID = $this->oxarticles__oxparentid->value;
02224             }
02225         }
02226         if (!isset($sOXID)) {
02227             return;
02228         }
02229 
02230         //if (isset($sOXID) && !$myConfig->blVariantParentBuyable && $myConfig->blUseStock)
02231         if ( $myConfig->getConfigParam( 'blUseStock' ) ) {
02232             //if article has variants then updating oxvarstock field
02233             //getting parent id
02234             if (!isset($sParentID)) {
02235                 $oDb = oxDb::getDb();
02236                 $sQ = 'select oxparentid from oxarticles where oxid = '.$oDb->quote($sOXID);
02237                 $sParentID = $oDb->getOne( $sQ );
02238             }
02239             //if we have parent id then update stock
02240             if ($sParentID) {
02241                 $this->_onChangeUpdateStock($sParentID);
02242             }
02243         }
02244         //if we have parent id then update count
02245         //update count even if blUseStock is not active
02246         if ($sParentID) {
02247             $this->_onChangeUpdateVarCount($sParentID);
02248         }
02249 
02250         $sId = ( $sParentID ) ? $sParentID : $sOXID;
02251         $this->_setVarMinMaxPrice( $sId );
02252 
02253         // resetting articles count cache if stock has changed and some
02254         // articles goes offline (M:1448)
02255         if ( $sAction === ACTION_UPDATE_STOCK ) {
02256             $this->_onChangeStockResetCount( $sOXID );
02257         }
02258 
02259     }
02260 
02267     public function getCustomVAT()
02268     {
02269         if ( isset($this->oxarticles__oxvat->value) ) {
02270             return $this->oxarticles__oxvat->value;
02271         }
02272     }
02273 
02282     public function checkForStock( $dAmount, $dArtStockAmount = 0 )
02283     {
02284         $myConfig = $this->getConfig();
02285         if ( !$myConfig->getConfigParam( 'blUseStock' ) ) {
02286             return true;
02287         }
02288 
02289         $oDb = oxDb::getDb( oxDb::FETCH_MODE_ASSOC );
02290         // fetching DB info as its up-to-date
02291         $sQ = 'select oxstock, oxstockflag from oxarticles where oxid = '.$oDb->quote( $this->getId() );
02292         $rs = $oDb->select( $sQ );
02293 
02294         $iOnStock   = 0;
02295         $iStockFlag = 0;
02296         if ( $rs !== false && $rs->recordCount() > 0 ) {
02297             $iOnStock   = $rs->fields['oxstock'] - $dArtStockAmount;
02298             $iStockFlag = $rs->fields['oxstockflag'];
02299 
02300             // dodger : fremdlager is also always considered as on stock
02301             if ( $iStockFlag == 1 || $iStockFlag == 4) {
02302                 return true;
02303             }
02304             if ( !$myConfig->getConfigParam( 'blAllowUnevenAmounts' ) ) {
02305                 $iOnStock = floor( $iOnStock );
02306             }
02307         }
02308         if ($this->getConfig()->getConfigParam( 'blPsBasketReservationEnabled' )) {
02309             $iOnStock += $this->getSession()->getBasketReservations()->getReservedAmount($this->getId());
02310         }
02311         if ( $iOnStock >= $dAmount ) {
02312             return true;
02313         } else {
02314             if ( $iOnStock > 0 ) {
02315                 return $iOnStock;
02316             } else {
02317                 $oEx = oxNew( 'oxArticleInputException' );
02318                 $oEx->setMessage( 'EXCEPTION_ARTICLE_ARTICELNOTBUYABLE' );
02319                 oxRegistry::get("oxUtilsView")->addErrorToDisplay( $oEx );
02320                 return false;
02321             }
02322         }
02323     }
02324 
02325 
02331     public function getLongDescription()
02332     {
02333         if ( $this->_oLongDesc === null ) {
02334             // initializing
02335             $this->_oLongDesc = new oxField();
02336 
02337 
02338             // choosing which to get..
02339             $sOxid = $this->getId();
02340             $sViewName = getViewName( 'oxartextends', $this->getLanguage() );
02341 
02342             $oDb = oxDb::getDb();
02343             $sDbValue = $oDb->getOne( "select oxlongdesc from {$sViewName} where oxid = " . $oDb->quote( $sOxid ) );
02344 
02345             if ( $sDbValue != false ) {
02346                 $this->_oLongDesc->setValue( $sDbValue, oxField::T_RAW );
02347             } elseif ( $this->oxarticles__oxparentid->value ) {
02348                 if ( !$this->isAdmin() || $this->_blLoadParentData ) {
02349                     $this->_oLongDesc->setValue( $this->getParentArticle()->getLongDescription()->getRawValue(), oxField::T_RAW );
02350                 }
02351             }
02352         }
02353         return $this->_oLongDesc;
02354     }
02355 
02362     public function getLongDesc()
02363     {
02364         return oxRegistry::get("oxUtilsView")->parseThroughSmarty( $this->getLongDescription()->getRawValue(), $this->getId().$this->getLanguage(), null, true );
02365     }
02366 
02374     public function setArticleLongDesc( $sDesc )
02375     {
02376 
02377         // setting current value
02378         $this->_oLongDesc = new oxField( $sDesc, oxField::T_RAW );
02379         $this->oxarticles__oxlongdesc = new oxField( $sDesc, oxField::T_RAW );
02380     }
02381 
02387     public function getAttributes()
02388     {
02389         if ( $this->_oAttributeList === null ) {
02390             $this->_oAttributeList = oxNew( 'oxattributelist' );
02391             $this->_oAttributeList->loadAttributes( $this->getId(), $this->getParentId() );
02392         }
02393 
02394         return $this->_oAttributeList;
02395     }
02396 
02402     public function getAttributesDisplayableInBasket()
02403     {
02404         if ( $this->_oAttributeList === null ) {
02405             $this->_oAttributeList = oxNew( 'oxattributelist' );
02406             $this->_oAttributeList->loadAttributesDisplayableInBasket( $this->getId(), $this->getParentId() );
02407         }
02408 
02409         return $this->_oAttributeList;
02410     }
02411 
02412 
02421     public function appendLink( $sAddParams, $iLang = null )
02422     {
02423         if ( $sAddParams ) {
02424             if ( $iLang === null ) {
02425                 $iLang = $this->getLanguage();
02426             }
02427 
02428             $this->_aSeoAddParams[$iLang]  = isset( $this->_aSeoAddParams[$iLang] ) ? $this->_aSeoAddParams[$iLang] . "&amp;" : "";
02429             $this->_aSeoAddParams[$iLang] .= $sAddParams;
02430         }
02431     }
02432 
02441     public function getBaseSeoLink( $iLang, $blMain = false )
02442     {
02443         $oEncoder = oxRegistry::get("oxSeoEncoderArticle");
02444         if ( !$blMain ) {
02445             return $oEncoder->getArticleUrl( $this, $iLang, $this->getLinkType() );
02446         }
02447         return $oEncoder->getArticleMainUrl( $this, $iLang );
02448     }
02449 
02458     public function getLink( $iLang = null, $blMain = false  )
02459     {
02460         if ( !oxRegistry::getUtils()->seoIsActive() ) {
02461             return $this->getStdLink( $iLang );
02462         }
02463 
02464         if ( $iLang === null ) {
02465             $iLang = $this->getLanguage();
02466         }
02467 
02468         $iLinkType = $this->getLinkType();
02469         if ( !isset( $this->_aSeoUrls[$iLang][$iLinkType] ) ) {
02470             $this->_aSeoUrls[$iLang][$iLinkType] = $this->getBaseSeoLink( $iLang, $blMain );
02471         }
02472 
02473         $sUrl = $this->_aSeoUrls[$iLang][$iLinkType];
02474         if ( isset($this->_aSeoAddParams[$iLang])) {
02475             $sUrl .= ( ( strpos( $sUrl.$this->_aSeoAddParams[$iLang], '?' ) === false ) ? '?' : '&amp;' ).$this->_aSeoAddParams[$iLang];
02476         }
02477 
02478         return $sUrl;
02479     }
02480 
02489     public function getMainLink( $iLang = null )
02490     {
02491         return $this->getLink( $iLang, true );
02492     }
02493 
02501     public function setLinkType( $iType )
02502     {
02503         // resetting details link, to force new
02504         $this->_sDetailLink = null;
02505 
02506         // setting link type
02507         $this->_iLinkType = (int) $iType;
02508     }
02509 
02515     public function getLinkType()
02516     {
02517         return $this->_iLinkType;
02518     }
02519 
02528     public function appendStdLink( $sAddParams, $iLang = null )
02529     {
02530         if ( $sAddParams ) {
02531             if ( $iLang === null ) {
02532                 $iLang = $this->getLanguage();
02533             }
02534 
02535             $this->_aStdAddParams[$iLang]  = isset( $this->_aStdAddParams[$iLang] ) ? $this->_aStdAddParams[$iLang] . "&amp;" : "";
02536             $this->_aStdAddParams[$iLang] .= $sAddParams;
02537         }
02538     }
02539 
02549     public function getBaseStdLink( $iLang, $blAddId = true, $blFull = true )
02550     {
02551         $sUrl = '';
02552         if ( $blFull ) {
02553             //always returns shop url, not admin
02554             $sUrl = $this->getConfig()->getShopUrl( $iLang, false );
02555         }
02556 
02557         $sUrl .= "index.php?cl=details" . ( $blAddId ? "&amp;anid=".$this->getId() : "" );
02558         return $sUrl . ( isset( $this->_aStdAddParams[$iLang] ) ? "&amp;". $this->_aStdAddParams[$iLang] : "" );
02559     }
02560 
02569     public function getStdLink( $iLang = null, $aParams = array() )
02570     {
02571         if ( $iLang === null ) {
02572             $iLang = $this->getLanguage();
02573         }
02574 
02575         if ( !isset( $this->_aStdUrls[$iLang] ) ) {
02576             $this->_aStdUrls[$iLang] = $this->getBaseStdLink( $iLang );
02577         }
02578 
02579         return oxRegistry::get("oxUtilsUrl")->processUrl( $this->_aStdUrls[$iLang], true, $aParams, $iLang );
02580     }
02581 
02591     public function getStdTagLink( $sTag )
02592     {
02593         $oArticleTags = oxNew('oxarticletaglist');
02594         $oArticleTags->setArticleId( $this->getId() );
02595         return $oArticleTags->getStdTagLink($sTag);
02596     }
02597 
02605     public function getTags()
02606     {
02607         $oArticleTags = oxNew('oxarticletaglist');
02608         $oArticleTags->load( $this->getId() );
02609         return $oArticleTags->get()->__toString();
02610     }
02611 
02621     public function saveTags($sTags)
02622     {
02623         //do not allow derived update
02624         if ( !$this->allowDerivedUpdate() ) {
02625             return false;
02626         }
02627         $oArticleTags = oxNew('oxarticletaglist');
02628         $oArticleTags->setArticleId( $this->getId() );
02629         $oArticleTags->set( $sTags );
02630         $oArticleTags->save();
02631     }
02632 
02642     public function addTag($sTag)
02643     {
02644         $oArticleTags = oxNew('oxarticletaglist');
02645         $oArticleTags->load( $this->getId() );
02646         $oArticleTags->addTag( $sTag );
02647         if ( $oArticleTags->save() ) {
02648             return true;
02649         }
02650         return false;
02651     }
02652 
02658     public function getMediaUrls()
02659     {
02660         if ( $this->_aMediaUrls === null ) {
02661             $this->_aMediaUrls = oxNew("oxlist");
02662             $this->_aMediaUrls->init("oxmediaurl");
02663             $this->_aMediaUrls->getBaseObject()->setLanguage( $this->getLanguage() );
02664 
02665             $sViewName = getViewName( "oxmediaurls", $this->getLanguage() );
02666             $sQ = "select * from {$sViewName} where oxobjectid = '".$this->getId()."'";
02667             $this->_aMediaUrls->selectString($sQ);
02668         }
02669         return $this->_aMediaUrls;
02670     }
02671 
02677     public function getDynImageDir()
02678     {
02679         return $this->_sDynImageDir;
02680     }
02681 
02687     public function getDispSelList()
02688     {
02689         if ($this->_aDispSelList === null) {
02690             if ( $this->getConfig()->getConfigParam( 'bl_perfLoadSelectLists' ) && $this->getConfig()->getConfigParam( 'bl_perfLoadSelectListsInAList' ) ) {
02691                 $this->_aDispSelList = $this->getSelectLists();
02692             }
02693         }
02694         return $this->_aDispSelList;
02695     }
02696 
02702     public function getMoreDetailLink()
02703     {
02704         if ( $this->_sMoreDetailLink == null ) {
02705 
02706             // and assign special article values
02707             $this->_sMoreDetailLink = $this->getConfig()->getShopHomeURL() . 'cl=moredetails';
02708 
02709             // not always it is okey, as not all the time active category is the same as primary article cat.
02710             if ( $sActCat = oxConfig::getParameter( 'cnid' ) ) {
02711                 $this->_sMoreDetailLink .= '&amp;cnid='.$sActCat;
02712             }
02713             $this->_sMoreDetailLink .= '&amp;anid='.$this->getId();
02714             $this->_sMoreDetailLink = $this->_sMoreDetailLink;
02715         }
02716 
02717         return $this->_sMoreDetailLink;
02718     }
02719 
02725     public function getToBasketLink()
02726     {
02727         if ( $this->_sToBasketLink == null ) {
02728             $myConfig = $this->getConfig();
02729 
02730             if ( oxRegistry::getUtils()->isSearchEngine() ) {
02731                 $this->_sToBasketLink = $this->getLink();
02732             } else {
02733                 // and assign special article values
02734                 $this->_sToBasketLink = $myConfig->getShopHomeURL();
02735 
02736                 // override some classes as these should never showup
02737                 $sActClass = oxConfig::getParameter( 'cl' );
02738                 if ( $sActClass == 'thankyou') {
02739                     $sActClass = 'basket';
02740                 }
02741                 $this->_sToBasketLink .= 'cl='.$sActClass;
02742 
02743                 // this is not very correct
02744                 if ( $sActCat = oxConfig::getParameter( 'cnid' ) ) {
02745                     $this->_sToBasketLink .= '&amp;cnid='.$sActCat;
02746                 }
02747 
02748                 $this->_sToBasketLink .= '&amp;fnc=tobasket&amp;aid='.$this->getId().'&amp;anid='.$this->getId();
02749 
02750                 if ( $sTpl = basename( oxConfig::getParameter( 'tpl' ) ) ) {
02751                     $this->_sToBasketLink .= '&amp;tpl='.$sTpl;
02752                 }
02753             }
02754         }
02755 
02756         return $this->_sToBasketLink;
02757     }
02758 
02764     public function getStockStatus()
02765     {
02766         return $this->_iStockStatus;
02767     }
02768 
02774     public function getDeliveryDate()
02775     {
02776         if ( $this->oxarticles__oxdelivery->value != '0000-00-00') {
02777             return oxRegistry::get("oxUtilsDate")->formatDBDate( $this->oxarticles__oxdelivery->value);
02778         }
02779         return false;
02780     }
02781 
02787     public function getFTPrice()
02788     {
02789         // module
02790         if ( $oPrice = $this->getTPrice() ) {
02791             if ( $dPrice = $this->_getPriceForView( $oPrice ) ) {
02792                 return oxRegistry::getLang()->formatCurrency( $dPrice );
02793             }
02794         }
02795     }
02796 
02802     public function getFPrice()
02803     {
02804         if ( $oPrice = $this->getPrice() ) {
02805             $dPrice = $this->_getPriceForView( $oPrice );
02806             return oxRegistry::getLang()->formatCurrency( $dPrice );
02807         }
02808     }
02809 
02816     public function resetRemindStatus()
02817     {
02818         if ( $this->oxarticles__oxremindactive->value == 2 &&
02819             $this->oxarticles__oxremindamount->value <= $this->oxarticles__oxstock->value ) {
02820             $this->oxarticles__oxremindactive->value = 1;
02821         }
02822     }
02823 
02829     public function getFNetPrice()
02830     {
02831         if ( $oPrice = $this->getPrice() ) {
02832             return oxRegistry::getLang()->formatCurrency( $oPrice->getNettoPrice() );
02833         }
02834     }
02835 
02843     public function getPricePerUnit()
02844     {
02845         return $this->getFUnitPrice();
02846     }
02847 
02853     public function isParentNotBuyable()
02854     {
02855         return $this->_blNotBuyableParent;
02856     }
02857 
02863     public function isNotBuyable()
02864     {
02865         return $this->_blNotBuyable;
02866     }
02867 
02875     public function setBuyableState( $blBuyable = false )
02876     {
02877         $this->_blNotBuyable = !$blBuyable;
02878     }
02879 
02887     public function setSelectlist( $aSelList )
02888     {
02889         $this->_aDispSelList = $aSelList;
02890     }
02891 
02899     public function getPictureUrl( $iIndex = 1 )
02900     {
02901         if ( $iIndex ) {
02902             $sImgName = false;
02903             if ( !$this->_isFieldEmpty( "oxarticles__oxpic".$iIndex ) ) {
02904                 $sImgName = basename( $this->{"oxarticles__oxpic$iIndex"}->value );
02905             }
02906 
02907             $sSize = $this->getConfig()->getConfigParam( 'aDetailImageSizes' );
02908             return oxRegistry::get("oxPictureHandler")->getProductPicUrl( "product/{$iIndex}/", $sImgName, $sSize, 'oxpic'.$iIndex );
02909         }
02910     }
02911 
02920     public function getIconUrl( $iIndex = 0 )
02921     {
02922         $sImgName = false;
02923         $sDirname = "product/1/";
02924         if ( $iIndex && !$this->_isFieldEmpty( "oxarticles__oxpic{$iIndex}" ) ) {
02925             $sImgName = basename( $this->{"oxarticles__oxpic$iIndex"}->value );
02926             $sDirname = "product/{$iIndex}/";
02927         } elseif ( !$this->_isFieldEmpty( "oxarticles__oxicon" ) ) {
02928             $sImgName = basename( $this->oxarticles__oxicon->value );
02929             $sDirname = "product/icon/";
02930         } elseif ( !$this->_isFieldEmpty( "oxarticles__oxpic1" ) ) {
02931             $sImgName = basename( $this->oxarticles__oxpic1->value );
02932         }
02933 
02934         $sSize = $this->getConfig()->getConfigParam( 'sIconsize' );
02935         return oxRegistry::get("oxPictureHandler")->getProductPicUrl( $sDirname, $sImgName, $sSize, $iIndex );
02936     }
02937 
02945     public function getThumbnailUrl( $bSsl = null )
02946     {
02947         $sImgName = false;
02948         $sDirname = "product/1/";
02949         if ( !$this->_isFieldEmpty( "oxarticles__oxthumb" ) ) {
02950             $sImgName = basename( $this->oxarticles__oxthumb->value );
02951             $sDirname = "product/thumb/";
02952         } elseif ( !$this->_isFieldEmpty( "oxarticles__oxpic1" ) ) {
02953             $sImgName = basename( $this->oxarticles__oxpic1->value );
02954         }
02955 
02956         $sSize = $this->getConfig()->getConfigParam( 'sThumbnailsize' );
02957         return oxRegistry::get("oxPictureHandler")->getProductPicUrl( $sDirname, $sImgName, $sSize, 0, $bSsl );
02958     }
02959 
02967     public function getZoomPictureUrl( $iIndex = '' )
02968     {
02969         $iIndex = (int) $iIndex;
02970         if ( $iIndex > 0 && !$this->_isFieldEmpty( "oxarticles__oxpic".$iIndex ) ) {
02971             $sImgName = basename( $this->{"oxarticles__oxpic".$iIndex}->value );
02972             $sSize = $this->getConfig()->getConfigParam( "sZoomImageSize" );
02973             return oxRegistry::get("oxPictureHandler")->getProductPicUrl( "product/{$iIndex}/", $sImgName, $sSize, 'oxpic'.$iIndex );
02974         }
02975     }
02976 
02982     public function getFileUrl()
02983     {
02984         return $this->getConfig()->getPictureUrl( 'media/' );
02985     }
02986 
02994     public function getPriceFromPrefix()
02995     {
02996         $sPricePrefix = '';
02997         if ( $this->_blIsRangePrice) {
02998             $sPricePrefix = oxLang::getInstance()->translateString('PRICE_FROM').' ';
02999         }
03000 
03001         return $sPricePrefix;
03002     }
03003 
03009     protected function _saveArtLongDesc()
03010     {
03011         $myConfig = $this->getConfig();
03012         $sShopId = $myConfig->getShopID();
03013         if (in_array("oxlongdesc", $this->_aSkipSaveFields)) {
03014             return;
03015         }
03016 
03017         if ($this->_blEmployMultilanguage) {
03018             $sValue = $this->getLongDescription()->getRawValue();
03019             if ( $sValue !== null ) {
03020                 $oArtExt = oxNew('oxI18n');
03021                 $oArtExt->init('oxartextends');
03022                 $oArtExt->setLanguage((int) $this->getLanguage());
03023                 if (!$oArtExt->load($this->getId())) {
03024                     $oArtExt->setId($this->getId());
03025                 }
03026                 $oArtExt->oxartextends__oxlongdesc = new oxField($sValue, oxField::T_RAW);
03027                 $oArtExt->save();
03028             }
03029         } else {
03030             $oArtExt = oxNew('oxI18n');
03031             $oArtExt->setEnableMultilang(false);
03032             $oArtExt->init('oxartextends');
03033             $aObjFields = $oArtExt->_getAllFields(true);
03034             if (!$oArtExt->load($this->getId())) {
03035                 $oArtExt->setId($this->getId());
03036             }
03037 
03038             foreach ($aObjFields as $sKey => $sValue ) {
03039                 if ( preg_match('/^oxlongdesc(_(\d{1,2}))?$/', $sKey) ) {
03040                     $sField = $this->_getFieldLongName($sKey);
03041 
03042                     if (isset($this->$sField)) {
03043                         $sLongDesc = null;
03044                         if ($this->$sField instanceof oxField) {
03045                             $sLongDesc = $this->$sField->getRawValue();
03046                         } elseif (is_object($this->$sField)) {
03047                             $sLongDesc = $this->$sField->value;
03048                         }
03049                         if (isset($sLongDesc)) {
03050                             $sAEField = $oArtExt->_getFieldLongName($sKey);
03051                             $oArtExt->$sAEField = new oxField($sLongDesc, oxField::T_RAW);
03052                         }
03053                     }
03054                 }
03055             }
03056             $oArtExt->save();
03057         }
03058     }
03059 
03065     protected function _skipSaveFields()
03066     {
03067         $myConfig = $this->getConfig();
03068 
03069         $this->_aSkipSaveFields = array();
03070 
03071         $this->_aSkipSaveFields[] = 'oxtimestamp';
03072        // $this->_aSkipSaveFields[] = 'oxlongdesc';
03073         $this->_aSkipSaveFields[] = 'oxinsert';
03074 
03075         if ( !$this->_blAllowEmptyParentId && (!isset( $this->oxarticles__oxparentid->value) || $this->oxarticles__oxparentid->value == '') ) {
03076             $this->_aSkipSaveFields[] = 'oxparentid';
03077         }
03078 
03079     }
03080 
03090     protected function _mergeDiscounts( $aDiscounts, $aItemDiscounts)
03091     {
03092         foreach ( $aItemDiscounts as $sKey => $oDiscount ) {
03093             // add prices of the same discounts
03094             if ( array_key_exists ($sKey, $aDiscounts) ) {
03095                 $aDiscounts[$sKey]->dDiscount += $oDiscount->dDiscount;
03096             } else {
03097                 $aDiscounts[$sKey] = $oDiscount;
03098             }
03099         }
03100         return $aDiscounts;
03101     }
03102 
03108     protected function _getGroupPrice()
03109     {
03110         $sPriceSufix = $this->_getUserPriceSufix();
03111         $sVarName = oxarticles__oxprice.$sPriceSufix;
03112         $dPrice = $this->$sVarName->value;
03113 
03114         // #1437/1436C - added config option, and check for zero A,B,C price values
03115         if ( $this->getConfig()->getConfigParam( 'blOverrideZeroABCPrices' ) && (double) $dPrice == 0 ) {
03116             $dPrice = $this->oxarticles__oxprice->value;
03117         }
03118 
03119         return $dPrice;
03120     }
03121 
03130     protected function _getAmountPrice($dAmount = 1)
03131     {
03132         $myConfig = $this->getConfig();
03133 
03134         startProfile( "_getAmountPrice" );
03135 
03136         $dPrice = $this->_getGroupPrice();
03137         $oAmtPrices = $this->_getAmountPriceList();
03138         foreach ($oAmtPrices as $oAmPrice) {
03139             if ($oAmPrice->oxprice2article__oxamount->value <= $dAmount
03140                     && $dAmount <= $oAmPrice->oxprice2article__oxamountto->value
03141                     && $dPrice > $oAmPrice->oxprice2article__oxaddabs->value ) {
03142                 $dPrice = $oAmPrice->oxprice2article__oxaddabs->value;
03143             }
03144         }
03145 
03146         stopProfile( "_getAmountPrice" );
03147         return $dPrice;
03148     }
03149 
03158     protected function _modifySelectListPrice( $dPrice, $aChosenList = null )
03159     {
03160         $myConfig = $this->getConfig();
03161         // #690
03162         if ( $myConfig->getConfigParam( 'bl_perfLoadSelectLists' ) && $myConfig->getConfigParam( 'bl_perfUseSelectlistPrice' ) ) {
03163 
03164             $aSelLists = $this->getSelectLists();
03165 
03166             foreach ( $aSelLists as $key => $aSel) {
03167                 if ( isset( $aChosenList[$key]) && isset($aSel[$aChosenList[$key]] ) ) {
03168                     $oSel = $aSel[$aChosenList[$key]];
03169                     if ( $oSel->priceUnit =='abs' ) {
03170                         $dPrice += $oSel->price;
03171                     } elseif ( $oSel->priceUnit =='%' ) {
03172                         $dPrice += oxPrice::percent( $dPrice, $oSel->price );
03173                     }
03174                 }
03175             }
03176         }
03177         return $dPrice;
03178     }
03179 
03180 
03188     protected function _fillAmountPriceList($aAmPriceList)
03189     {
03190         $myConfig = $this->getConfig();
03191         $oLang = oxRegistry::getLang();
03192 
03193         // trying to find lowest price value
03194         foreach ($aAmPriceList as $sId => $oItem) {
03195 
03196             $oItemPrice = $this->_getPriceObject();
03197             if ( $oItem->oxprice2article__oxaddabs->value ) {
03198 
03199                 $dBasePrice = $oItem->oxprice2article__oxaddabs->value;
03200                 $dBasePrice = $this->_preparePrice( $dBasePrice, $this->getArticleVat() );
03201 
03202                 $oItemPrice->setPrice( $dBasePrice );
03203                 $this->_calculatePrice( $oItemPrice );
03204 
03205             } else {
03206                 $dBasePrice = $this->_getGroupPrice();
03207                 $dBasePrice = $this->_preparePrice( $dBasePrice, $this->getArticleVat() );
03208                 $oItemPrice->setPrice( $dBasePrice );
03209                 $oItemPrice->subtractPercent( $oItem->oxprice2article__oxaddperc->value );
03210             }
03211 
03212 
03213             $aAmPriceList[$sId]->fbrutprice = $oLang->formatCurrency( $this->_getPriceForView( $oItemPrice ) );
03214         }
03215 
03216         return $aAmPriceList;
03217     }
03218 
03219 
03225     protected function _getVariantsIds()
03226     {
03227         $aSelect = array();
03228         if ( ( $sId = $this->getId() ) ) {
03229             $oDb = oxDb::getDb( oxDb::FETCH_MODE_ASSOC );
03230             $sQ = "select oxid from " . $this->getViewName( true ) . " where oxparentid = ".$oDb->quote( $sId )." and " .
03231                    $this->getSqlActiveSnippet( true ) . " order by oxsort";
03232             $oRs = $oDb->select( $sQ );
03233             if ( $oRs != false && $oRs->recordCount() > 0 ) {
03234                 while (!$oRs->EOF) {
03235                     $aSelect[] = reset( $oRs->fields );
03236                     $oRs->moveNext();
03237                 }
03238             }
03239         }
03240         return $aSelect;
03241     }
03242 
03248     public function getArticleVat()
03249     {
03250         if (!isset($this->_dArticleVat)) {
03251             $this->_dArticleVat = oxRegistry::get("oxVatSelector")->getArticleVat( $this );
03252         }
03253         return $this->_dArticleVat;
03254     }
03255 
03264     protected function _applyVAT( oxPrice $oPrice, $dVat )
03265     {
03266         startProfile(__FUNCTION__);
03267         $oPrice->setVAT( $dVat );
03268         if ( ($dVat = oxRegistry::get("oxVatSelector")->getArticleUserVat($this)) !== false ) {
03269             $oPrice->setUserVat( $dVat );
03270         }
03271         stopProfile(__FUNCTION__);
03272     }
03273 
03281     public function applyVats( oxPrice $oPrice )
03282     {
03283         $this->_applyVAT($oPrice, $this->getArticleVat() );
03284     }
03285 
03293     public function applyDiscountsForVariant( $oPrice )
03294     {
03295         // apply discounts
03296         if ( !$this->skipDiscounts() ) {
03297             $oDiscountList = oxRegistry::get("oxDiscountList");
03298             $aDiscounts = $oDiscountList->getArticleDiscounts( $this, $this->getArticleUser() );
03299 
03300             reset( $aDiscounts );
03301             foreach ( $aDiscounts as $oDiscount ) {
03302                 $oPrice->setDiscount($oDiscount->getAddSum(), $oDiscount->getAddSumType());
03303             }
03304             $oPrice->calculateDiscount();
03305         }
03306     }
03307 
03316     protected function _applyCurrency(oxPrice $oPrice, $oCur = null )
03317     {
03318         if ( !$oCur ) {
03319             $oCur = $this->getConfig()->getActShopCurrencyObject();
03320         }
03321 
03322         $oPrice->multiply($oCur->rate);
03323     }
03324 
03325 
03334     protected function _getAttribsString(&$sAttribs, &$iCnt)
03335     {
03336         // we do not use lists here as we dont need this overhead right now
03337         $oDb = oxDb::getDb( oxDb::FETCH_MODE_ASSOC );
03338         $sSelect =  'select oxattrid from oxobject2attribute where oxobject2attribute.oxobjectid='.$oDb->quote( $this->getId() );
03339         $sAttribs = '';
03340         $blSep = false;
03341         $rs = $oDb->select( $sSelect);
03342         $iCnt = 0;
03343         if ($rs != false && $rs->recordCount() > 0) {
03344             while (!$rs->EOF) {
03345                 if ( $blSep) {
03346                     $sAttribs .= ' or ';
03347                 }
03348                 $sAttribs .= 't1.oxattrid = '.$oDb->quote($rs->fields['oxattrid']).' ';
03349                 $blSep = true;
03350                 $iCnt++;
03351                 $rs->moveNext();
03352             }
03353         }
03354     }
03355 
03364     protected function _getSimList($sAttribs, $iCnt)
03365     {
03366         $myConfig = $this->getConfig();
03367         $oDb      = oxDb::getDb( oxDb::FETCH_MODE_ASSOC );
03368 
03369         // #523A
03370         $iAttrPercent = $myConfig->getConfigParam( 'iAttributesPercent' )/100;
03371         // 70% same attributes
03372         if ( !$iAttrPercent || $iAttrPercent < 0 || $iAttrPercent > 1) {
03373             $iAttrPercent = 0.70;
03374         }
03375         // #1137V iAttributesPercent = 100 doesnt work
03376         $iHitMin = ceil( $iCnt * $iAttrPercent );
03377 
03378         // we do not use lists here as we don't need this overhead right now
03379         $aList= array();
03380         $sSelect =  "select oxobjectid, count(*) as cnt from oxobject2attribute as t1 where
03381                     ( $sAttribs )
03382                     and t1.oxobjectid != ".$oDb->quote( $this->oxarticles__oxid->value )."
03383                     group by t1.oxobjectid having count(*) >= $iHitMin ";
03384 
03385         $rs = $oDb->selectLimit( $sSelect, 20, 0 );
03386         if ($rs != false && $rs->recordCount() > 0) {
03387             while (!$rs->EOF) {
03388                 $oTemp = new stdClass();    // #663
03389                 $oTemp->cnt = $rs->fields['cnt'];
03390                 $oTemp->id  = $rs->fields['oxobjectid'];
03391                 $aList[] = $oTemp;
03392                 $rs->moveNext();
03393             }
03394         }
03395         return $aList;
03396     }
03397 
03406     protected function _generateSimListSearchStr($sArticleTable, $aList)
03407     {
03408         $myConfig = $this->getConfig();
03409         $sFieldList = $this->getSelectFields();
03410         $sSearch = "select $sFieldList from $sArticleTable where ".$this->getSqlActiveSnippet()."  and $sArticleTable.oxissearch = 1 and $sArticleTable.oxid in ( ";
03411         $blSep = false;
03412         $iCnt = 0;
03413         $oDb = oxDb::getDb();
03414         foreach ( $aList as $oTemp) {
03415             if ( $blSep) {
03416                 $sSearch .= ',';
03417             }
03418             $sSearch .= $oDb->quote($oTemp->id);
03419             $blSep = true;
03420             if ( $iCnt >= $myConfig->getConfigParam( 'iNrofSimilarArticles' ) ) {
03421                 break;
03422             }
03423             $iCnt++;
03424         }
03425 
03426         //#1741T
03427         //$sSearch .= ") and $sArticleTable.oxparentid = '' ";
03428         $sSearch .= ') ';
03429 
03430         // #524A -- randomizing articles in attribute list
03431         $sSearch .= ' order by rand() ';
03432 
03433         return $sSearch;
03434     }
03435 
03444     protected function _generateSearchStr($sOXID, $blSearchPriceCat = false )
03445     {
03446 
03447         $sCatView = getViewName( 'oxcategories', $this->getLanguage() );
03448         $sO2CView = getViewName( 'oxobject2category' );
03449 
03450         // we do not use lists here as we don't need this overhead right now
03451         if ( !$blSearchPriceCat ) {
03452             $sSelect  = "select {$sCatView}.* from {$sO2CView} as oxobject2category left join {$sCatView} on
03453                          {$sCatView}.oxid = oxobject2category.oxcatnid
03454                          where oxobject2category.oxobjectid=".oxDb::getDb()->quote($sOXID)." and {$sCatView}.oxid is not null ";
03455         } else {
03456             $sSelect  = "select {$sCatView}.* from {$sCatView} where
03457                          '{$this->oxarticles__oxprice->value}' >= {$sCatView}.oxpricefrom and
03458                          '{$this->oxarticles__oxprice->value}' <= {$sCatView}.oxpriceto ";
03459         }
03460         return $sSelect;
03461     }
03462 
03468     protected function _generateSearchStrForCustomerBought()
03469     {
03470         $sArtTable = $this->getViewName();
03471         $sOrderArtTable = getViewName( 'oxorderarticles' );
03472 
03473         // fetching filter params
03474         $sIn = " '{$this->oxarticles__oxid->value}' ";
03475         if ( $this->oxarticles__oxparentid->value ) {
03476 
03477             // adding article parent
03478             $sIn .= ", '{$this->oxarticles__oxparentid->value}' ";
03479             $sParentIdForVariants = $this->oxarticles__oxparentid->value;
03480 
03481         } else {
03482             $sParentIdForVariants = $this->getId();
03483         }
03484 
03485         // adding variants
03486         $oDb = oxDb::getDb( oxDb::FETCH_MODE_ASSOC );
03487         $oRs = $oDb->select( "select oxid from {$sArtTable} where oxparentid = ".$oDb->quote($sParentIdForVariants)." and oxid != ".$oDb->quote($this->oxarticles__oxid->value) );
03488         if ( $oRs != false && $oRs->recordCount() > 0) {
03489             while ( !$oRs->EOF ) {
03490                 $sIn .= ", ".$oDb->quote(current( $oRs->fields ))." ";
03491                 $oRs->moveNext();
03492             }
03493         }
03494 
03495         $iLimit = (int) $this->getConfig()->getConfigParam( 'iNrofCustomerWhoArticles' );
03496         $iLimit = $iLimit?( $iLimit * 10 ): 50;
03497 
03498         // building sql (optimized)
03499         $sQ = "select distinct {$sArtTable}.* from (
03500                    select d.oxorderid as suborderid from {$sOrderArtTable} as d use index ( oxartid ) where d.oxartid in ( {$sIn} ) limit {$iLimit}
03501                ) as suborder
03502                left join {$sOrderArtTable} force index ( oxorderid ) on suborder.suborderid = {$sOrderArtTable}.oxorderid
03503                left join {$sArtTable} on {$sArtTable}.oxid = {$sOrderArtTable}.oxartid
03504                where {$sArtTable}.oxid not in ( {$sIn} )
03505                and ( {$sArtTable}.oxissearch = 1 or {$sArtTable}.oxparentid <> '' ) and ".$this->getSqlActiveSnippet();
03506 
03507         /* non optimized, but could be used if index forcing is not supported
03508         // building sql
03509         $sQ = "select distinct {$sArtTable}.* from {$sOrderArtTable}, {$sArtTable} where {$sOrderArtTable}.oxorderid in (
03510                    select {$sOrderArtTable}.oxorderid from {$sOrderArtTable} where {$sOrderArtTable}.oxartid in ( {$sIn} )
03511                ) and {$sArtTable}.oxid = {$sOrderArtTable}.oxartid and {$sArtTable}.oxid not in ( {$sIn} )
03512                and ( {$sArtTable}.oxissearch = 1 or {$sArtTable}.oxparentid <> '' )
03513                and ".$this->getSqlActiveSnippet();
03514         */
03515 
03516         return $sQ;
03517     }
03518 
03528     protected function _generateSelectCatStr($sOXID, $sCatId, $dPriceFromTo = false)
03529     {
03530         $sCategoryView = getViewName('oxcategories');
03531         $sO2CView = getViewName('oxobject2category');
03532 
03533         $oDb    = oxDb::getDb();
03534         $sOXID  = $oDb->quote($sOXID);
03535         $sCatId = $oDb->quote($sCatId);
03536 
03537         if (!$dPriceFromTo) {
03538             $sSelect  = "select oxobject2category.oxcatnid from $sO2CView as oxobject2category ";
03539             $sSelect .= "left join $sCategoryView as oxcategories on oxcategories.oxid = oxobject2category.oxcatnid ";
03540             $sSelect .= "where oxobject2category.oxcatnid=$sCatId and oxobject2category.oxobjectid=$sOXID ";
03541             $sSelect .= "and oxcategories.oxactive = 1 order by oxobject2category.oxtime ";
03542         } else {
03543             $dPriceFromTo = $oDb->quote($dPriceFromTo);
03544             $sSelect  = "select oxcategories.oxid from $sCategoryView as oxcategories where ";
03545             $sSelect .= "oxcategories.oxid=$sCatId and $dPriceFromTo >= oxcategories.oxpricefrom and ";
03546             $sSelect .= "$dPriceFromTo <= oxcategories.oxpriceto ";
03547         }
03548         return $sSelect;
03549     }
03550 
03556     protected function _getAmountPriceList()
03557     {
03558         if ( $this->_oAmountPriceList === null ) {
03559             $this->_oAmountPriceList = array();
03560             if ( !$this->skipDiscounts() ) {
03561 
03562                 //collecting assigned to article amount-price list
03563                 $oAmPriceList = oxNew( 'oxAmountPricelist' );
03564                 $oAmPriceList->load( $this );
03565 
03566                 // prepare abs prices if currently having percentages
03567                 $oBasePrice = $this->_getGroupPrice();
03568                 foreach ( $oAmPriceList as $oAmPrice ) {
03569                     if ( $oAmPrice->oxprice2article__oxaddperc->value ) {
03570                         $oAmPrice->oxprice2article__oxaddabs = new oxField(oxPrice::percent( $oBasePrice, 100 - $oAmPrice->oxprice2article__oxaddperc->value ), oxField::T_RAW );
03571                     }
03572                 }
03573 
03574                 $this->_oAmountPriceList = $oAmPriceList;
03575             }
03576         }
03577 
03578         return $this->_oAmountPriceList;
03579     }
03580 
03588     protected function _isFieldEmpty( $sFieldName )
03589     {
03590         $mValue = $this->$sFieldName->value;
03591 
03592         if ( is_null( $mValue ) ) {
03593             return true;
03594         }
03595 
03596         if ( $mValue === '' ) {
03597             return true;
03598         }
03599 
03600         // certain fields with zero value treat as empty
03601         $aZeroValueFields = array('oxarticles__oxprice', 'oxarticles__oxvat', 'oxarticles__oxunitquantity');
03602 
03603         if (!$mValue && in_array( $sFieldName, $aZeroValueFields ) ) {
03604             return true;
03605         }
03606 
03607 
03608         if (!strcmp($mValue, '0000-00-00 00:00:00') || !strcmp($mValue, '0000-00-00')) {
03609             return true;
03610         }
03611 
03612         $sFieldName = strtolower($sFieldName);
03613 
03614         if ( $sFieldName == 'oxarticles__oxicon' && ( strpos($mValue, "nopic_ico.jpg") !== false || strpos($mValue, "nopic.jpg") !== false ) ) {
03615             return true;
03616         }
03617 
03618         if ( strpos($mValue, "nopic.jpg") !== false && ($sFieldName == 'oxarticles__oxthumb' || substr($sFieldName, 0, 17) == 'oxarticles__oxpic' || substr($sFieldName, 0, 18) == 'oxarticles__oxzoom') ) {
03619             return true;
03620         }
03621 
03622         return false;
03623     }
03624 
03632     protected function _assignParentFieldValue($sFieldName)
03633     {
03634         if (!($oParentArticle = $this->getParentArticle())) {
03635             return;
03636         }
03637 
03638         $sCopyFieldName = $this->_getFieldLongName($sFieldName);
03639 
03640         // assigning only these which parent article has
03641         if ( $oParentArticle->$sCopyFieldName != null ) {
03642 
03643             // only overwrite database values
03644             if ( substr( $sCopyFieldName, 0, 12) != 'oxarticles__') {
03645                 return;
03646             }
03647 
03648             //do not copy certain fields
03649             if (in_array($sCopyFieldName, $this->_aNonCopyParentFields)) {
03650                 return;
03651             }
03652 
03653             //COPY THE VALUE
03654             // assigning images from parent only if variant has no master image (#1807)
03655             if ( stristr($sCopyFieldName, '_oxthumb') || stristr($sCopyFieldName, '_oxicon') ) {
03656                 if ( $this->_isFieldEmpty( $sCopyFieldName ) && !$this->_hasMasterImage( 1 ) ) {
03657                     $this->$sCopyFieldName = clone $oParentArticle->$sCopyFieldName;
03658                 }
03659             } elseif ( stristr($sCopyFieldName, '_oxzoom') ) {
03660                 // for zoom images checking master image with specified index
03661                 // assign from parent only if no pictures to variant are added
03662                 $iIndex = (int) str_ireplace( "oxarticles__oxzoom", "", $sFieldName );
03663                 if ( $this->_isFieldEmpty( $sCopyFieldName ) && !$this->_hasMasterImage( $iIndex ) && !$this->_hasMasterImage( 1 ) ) {
03664                     $this->$sCopyFieldName = clone $oParentArticle->$sCopyFieldName;
03665                 }
03666             } elseif ( stristr($sCopyFieldName, '_oxpicsgenerated') && $this->{$sCopyFieldName}->value == 0 ) {
03667                 // if no pics generated for variants, load all from
03668                 $this->$sCopyFieldName = clone $oParentArticle->$sCopyFieldName;
03669             } elseif ($this->_isFieldEmpty($sCopyFieldName) || in_array( $sCopyFieldName, $this->_aCopyParentField ) ) {
03670                 $this->$sCopyFieldName = clone $oParentArticle->$sCopyFieldName;
03671             }
03672         }
03673     }
03674 
03680     public function getParentArticle()
03681     {
03682         if ( ( $sParentId = $this->oxarticles__oxparentid->value ) ) {
03683             $sIndex = $sParentId . "_" . $this->getLanguage();
03684             if ( !isset( self::$_aLoadedParents[$sIndex] ) ) {
03685                 self::$_aLoadedParents[$sIndex] = oxNew( 'oxarticle' );
03686                 self::$_aLoadedParents[$sIndex]->_blLoadPrice    = false;
03687                 self::$_aLoadedParents[$sIndex]->_blLoadVariants = false;
03688                 self::$_aLoadedParents[$sIndex]->loadInLang( $this->getLanguage(), $sParentId );
03689             }
03690             return self::$_aLoadedParents[$sIndex];
03691         }
03692     }
03693 
03699     protected function _assignParentFieldValues()
03700     {
03701         startProfile('articleAssignParentInternal');
03702         if ( $this->oxarticles__oxparentid->value ) {
03703             // yes, we are in fact a variant
03704             if ( !$this->isAdmin() || ( $this->_blLoadParentData && $this->isAdmin() ) ) {
03705                 foreach ( $this->_aFieldNames as $sFieldName => $sVal ) {
03706                     $this->_assignParentFieldValue( $sFieldName );
03707                 }
03708             }
03709         }
03710         stopProfile('articleAssignParentInternal');
03711     }
03712 
03718     protected function _assignNotBuyableParent()
03719     {
03720         if ( !$this->getConfig()->getConfigParam( 'blVariantParentBuyable' ) &&
03721              ($this->_blHasVariants || $this->oxarticles__oxvarstock->value || $this->oxarticles__oxvarcount->value )) {
03722             $this->_blNotBuyableParent = true;
03723 
03724         }
03725     }
03726 
03732     protected function _assignStock()
03733     {
03734         $myConfig = $this->getConfig();
03735         // -----------------------------------
03736         // stock
03737         // -----------------------------------
03738 
03739         // #1125 A. must round (using floor()) value taken from database and cast to int
03740         if (!$myConfig->getConfigParam( 'blAllowUnevenAmounts' ) && !$this->isAdmin() ) {
03741             $this->oxarticles__oxstock = new oxField((int) floor($this->oxarticles__oxstock->value));
03742         }
03743         //GREEN light
03744         $this->_iStockStatus = 0;
03745 
03746         // if we have flag /*1 or*/ 4 - we show always green light
03747         if ( $myConfig->getConfigParam( 'blUseStock' ) && /*$this->oxarticles__oxstockflag->value != 1 && */ $this->oxarticles__oxstockflag->value != 4) {
03748             //ORANGE light
03749             $iStock = $this->oxarticles__oxstock->value;
03750 
03751             if ($this->_blNotBuyableParent) {
03752                 $iStock = $this->oxarticles__oxvarstock->value;
03753             }
03754 
03755 
03756             if ( $iStock <= $myConfig->getConfigParam( 'sStockWarningLimit' ) && $iStock > 0) {
03757                 $this->_iStockStatus = 1;
03758             }
03759 
03760             //RED light
03761             if ($iStock <= 0) {
03762                 $this->_iStockStatus = -1;
03763             }
03764         }
03765 
03766 
03767         // stock
03768         if ( $myConfig->getConfigParam( 'blUseStock' ) && ($this->oxarticles__oxstockflag->value == 3 || $this->oxarticles__oxstockflag->value == 2)) {
03769             $iOnStock = $this->oxarticles__oxstock->value;
03770             if ($this->getConfig()->getConfigParam( 'blPsBasketReservationEnabled' )) {
03771                 $iOnStock += $this->getSession()->getBasketReservations()->getReservedAmount($this->getId());
03772             }
03773             if ($iOnStock <= 0) {
03774                 $this->setBuyableState( false );
03775             }
03776         }
03777 
03778         //exceptional handling for variant parent stock:
03779         if ($this->_blNotBuyable && $this->oxarticles__oxvarstock->value ) {
03780             $this->setBuyableState( true );
03781             //but then at least setting notBuaybleParent to true
03782             $this->_blNotBuyableParent = true;
03783         }
03784 
03785         //special treatment for lists when blVariantParentBuyable config option is set to false
03786         //then we just hide "to basket" button.
03787         //if variants are not loaded in the list and this article has variants and parent is not buyable then this article is not buyable
03788         if ( !$myConfig->getConfigParam( 'blVariantParentBuyable' ) && !$myConfig->getConfigParam( 'blLoadVariants' ) && $this->oxarticles__oxvarstock->value) {
03789             $this->setBuyableState( false );
03790         }
03791 
03792         //setting to non buyable when variant list is empty (for example not loaded or inactive) and $this is non buyable parent
03793         if (!$this->_blNotBuyable && $this->_blNotBuyableParent && $this->oxarticles__oxvarcount->value == 0) {
03794             $this->setBuyableState( false );
03795         }
03796     }
03797 
03805     protected function _assignPrices()
03806     {
03807         $myConfig = $this->getConfig();
03808 
03809         // Performance
03810         if ( !$myConfig->getConfigParam( 'bl_perfLoadPrice' ) || !$this->_blLoadPrice ) {
03811             return;
03812         }
03813 
03814         //getting min and max prices of variants
03815         if ( $this->_hasAnyVariant() ) {
03816             $this->_applyRangePrice();
03817         }
03818     }
03819 
03825     protected function _assignPersistentParam()
03826     {
03827         // Persistent Parameter Handling
03828         $aPersParam     = oxSession::getVar( 'persparam');
03829         if ( isset( $aPersParam) && isset( $aPersParam[$this->getId()])) {
03830             $this->_aPersistParam = $aPersParam[$this->getId()];
03831         }
03832     }
03833 
03839     protected function _assignDynImageDir()
03840     {
03841         $myConfig = $this->getConfig();
03842 
03843         $sThisShop = $this->oxarticles__oxshopid->value;
03844 
03845         $this->_sDynImageDir   = $myConfig->getPictureUrl( null, false );
03846         $this->dabsimagedir    = $myConfig->getPictureDir( false ); //$sThisShop
03847         $this->nossl_dimagedir = $myConfig->getPictureUrl( null, false, false, null, $sThisShop ); //$sThisShop
03848         $this->ssl_dimagedir   = $myConfig->getPictureUrl( null, false, true, null, $sThisShop ); //$sThisShop
03849     }
03850 
03856     protected function _assignComparisonListFlag()
03857     {
03858         // #657 add a flag if article is on comparisonlist
03859 
03860         $aItems = oxSession::getVar('aFiltcompproducts');
03861         if ( isset( $aItems[$this->getId()])) {
03862             $this->_blIsOnComparisonList = true;
03863         }
03864     }
03865 
03866 
03874     protected function _insert()
03875     {
03876         // set oxinsert
03877         $sNow = date('Y-m-d H:i:s', oxRegistry::get("oxUtilsDate")->getTime());
03878         $this->oxarticles__oxinsert    = new oxField( $sNow );
03879         if ( !is_object($this->oxarticles__oxsubclass) || $this->oxarticles__oxsubclass->value == '') {
03880             $this->oxarticles__oxsubclass = new oxField('oxarticle');
03881         }
03882 
03883         return parent::_insert();
03884     }
03885 
03891     protected function _update()
03892     {
03893 
03894         $this->_skipSaveFields();
03895 
03896         $myConfig = $this->getConfig();
03897 
03898 
03899         $blRes =  parent::_update();
03900 
03901 
03902         return $blRes;
03903     }
03904 
03910     public function updateVariantsRemind()
03911     {
03912         // check if it is parent article
03913         if ( !$this->isVariant() && $this->_hasAnyVariant()) {
03914             $oDb = oxDb::getDb();
03915             $sOxId = $oDb->quote($this->getId());
03916             $sOxShopId = $oDb->quote($this->getShopId());
03917             $iRemindActive = $oDb->quote($this->oxarticles__oxremindactive->value);
03918             $sUpdate = "
03919                 update oxarticles
03920                     set oxremindactive = $iRemindActive
03921                     where oxparentid = $sOxId and
03922                           oxshopid = $sOxShopId
03923             ";
03924             $oDb->execute( $sUpdate );
03925         }
03926     }
03927 
03935     protected function _deleteRecords($sOXID)
03936     {
03937         $oDb = oxDb::getDb();
03938 
03939         $sOXID = $oDb->quote($sOXID);
03940 
03941         //remove other records
03942         $sDelete = 'delete from oxobject2article where oxarticlenid = '.$sOXID.' or oxobjectid = '.$sOXID.' ';
03943         $oDb->execute( $sDelete);
03944 
03945         $sDelete = 'delete from oxobject2attribute where oxobjectid = '.$sOXID.' ';
03946         $oDb->execute( $sDelete);
03947 
03948         $sDelete = 'delete from oxobject2category where oxobjectid = '.$sOXID.' ';
03949         $oDb->execute( $sDelete);
03950 
03951         $sDelete = 'delete from oxobject2selectlist where oxobjectid = '.$sOXID.' ';
03952         $oDb->execute( $sDelete);
03953 
03954         $sDelete = 'delete from oxprice2article where oxartid = '.$sOXID.' ';
03955         $oDb->execute( $sDelete);
03956 
03957         $sDelete = 'delete from oxreviews where oxtype="oxarticle" and oxobjectid = '.$sOXID.' ';
03958         $oDb->execute( $sDelete);
03959 
03960         $sDelete = 'delete from oxratings where oxobjectid = '.$sOXID.' ';
03961         $rs = $oDb->execute( $sDelete );
03962 
03963         $sDelete = 'delete from oxaccessoire2article where oxobjectid = '.$sOXID.' or oxarticlenid = '.$sOXID.' ';
03964         $oDb->execute( $sDelete);
03965 
03966         //#1508C - deleting oxobject2delivery entries added
03967         $sDelete = 'delete from oxobject2delivery where oxobjectid = '.$sOXID.' and oxtype=\'oxarticles\' ';
03968         $oDb->execute( $sDelete);
03969 
03970         $sDelete = 'delete from oxartextends where oxid = '.$sOXID.' ';
03971         $oDb->execute( $sDelete);
03972 
03973         //delete the record
03974         foreach ( $this->_getLanguageSetTables( "oxartextends" ) as $sSetTbl ) {
03975             $oDb->execute( "delete from $sSetTbl where oxid = {$sOXID}" );
03976         }
03977 
03978         $sDelete = 'delete from oxactions2article where oxartid = '.$sOXID.' ';
03979         $rs = $oDb->execute( $sDelete );
03980 
03981         $sDelete = 'delete from oxobject2list where oxobjectid = '.$sOXID.' ';
03982         $rs = $oDb->execute( $sDelete );
03983 
03984 
03985         return $rs;
03986     }
03987 
03995     protected function _deleteVariantRecords( $sOXID )
03996     {
03997         if ( $sOXID ) {
03998             $oDb = oxDb::getDb();
03999             //collect variants to remove recursively
04000             $sQ = 'select oxid from '.$this->getViewName().' where oxparentid = '.$oDb->quote( $sOXID );
04001             $rs = $oDb->select( $sQ, false, false );
04002             $oArticle = oxNew("oxArticle");
04003             if ($rs != false && $rs->recordCount() > 0) {
04004                 while (!$rs->EOF) {
04005                     $oArticle->setId($rs->fields[0]);
04006                     $oArticle->delete();
04007                     $rs->moveNext();
04008                 }
04009             }
04010         }
04011     }
04012 
04018     protected function _deletePics()
04019     {
04020         $myUtilsPic = oxRegistry::get("oxUtilsPic");
04021         $myConfig   = $this->getConfig();
04022         $oPictureHandler = oxRegistry::get("oxPictureHandler");
04023 
04024         //deleting custom main icon
04025         $oPictureHandler->deleteMainIcon( $this );
04026 
04027         //deleting custom thumbnail
04028         $oPictureHandler->deleteThumbnail( $this );
04029 
04030         $sAbsDynImageDir = $myConfig->getPictureDir(false);
04031 
04032         // deleting master image and all generated images
04033         $iPicCount = $myConfig->getConfigParam( 'iPicCount' );
04034         for ( $i = 1; $i <= $iPicCount; $i++ ) {
04035             $oPictureHandler->deleteArticleMasterPicture( $this, $i );
04036         }
04037     }
04038 
04048     protected function _onChangeResetCounts( $sOxid, $sVendorId = null, $sManufacturerId = null )
04049     {
04050 
04051         $myUtilsCount = oxRegistry::get("oxUtilsCount");
04052 
04053         if ( $sVendorId ) {
04054             $myUtilsCount->resetVendorArticleCount( $sVendorId );
04055         }
04056 
04057         if ( $sManufacturerId ) {
04058             $myUtilsCount->resetManufacturerArticleCount( $sManufacturerId );
04059         }
04060 
04061         //also reseting category counts
04062         $oDb = oxDb::getDb();
04063         $sQ = "select oxcatnid from oxobject2category where oxobjectid = ".$oDb->quote($sOxid);
04064         $oRs = $oDb->select( $sQ, false, false );
04065         if ( $oRs !== false && $oRs->recordCount() > 0) {
04066             while ( !$oRs->EOF ) {
04067                 $myUtilsCount->resetCatArticleCount( $oRs->fields[0] );
04068                 $oRs->moveNext();
04069             }
04070         }
04071     }
04072 
04080     protected function _onChangeUpdateStock( $sParentID )
04081     {
04082         if ( $sParentID ) {
04083             $oDb = oxDb::getDb();
04084             $sParentIdQuoted = $oDb->quote($sParentID);
04085             $sQ = 'select oxstock, oxvendorid, oxmanufacturerid from oxarticles where oxid = '.$sParentIdQuoted;
04086             $rs = $oDb->select( $sQ, false, false );
04087             $iOldStock = $rs->fields[0];
04088             $iVendorID = $rs->fields[1];
04089             $iManufacturerID = $rs->fields[2];
04090 
04091             $sQ = 'select sum(oxstock) from '.$this->getViewName(true).' where oxparentid = '.$sParentIdQuoted.' and '. $this->getSqlActiveSnippet( true ).' and oxstock > 0 ';
04092             $iStock = (float) $oDb->getOne( $sQ, false, false );
04093 
04094             $sQ = 'update oxarticles set oxvarstock = '.$iStock.' where oxid = '.$sParentIdQuoted;
04095             $oDb->execute( $sQ );
04096 
04097             //now lets update category counts
04098             //first detect stock status change for this article (to or from 0)
04099             if ( $iStock < 0 ) {
04100                 $iStock = 0;
04101             }
04102             if ( $iOldStock < 0 ) {
04103                 $iOldStock = 0;
04104             }
04105             if ( $this->oxarticles__oxstockflag->value == 2 && $iOldStock xor $iStock ) {
04106                 //means the stock status could be changed (oxstock turns from 0 to 1 or from 1 to 0)
04107                 // so far we leave it like this but later we could move all count resets to one or two functions
04108                 $this->_onChangeResetCounts( $sParentID, $iVendorID, $iManufacturerID );
04109             }
04110         }
04111     }
04112 
04120     protected function _onChangeStockResetCount( $sOxid )
04121     {
04122         $myConfig = $this->getConfig();
04123 
04124         if ( $myConfig->getConfigParam( 'blUseStock' ) && $this->oxarticles__oxstockflag->value == 2 &&
04125            ( $this->oxarticles__oxstock->value + $this->oxarticles__oxvarstock->value ) <= 0 ) {
04126 
04127                $this->_onChangeResetCounts( $sOxid, $this->oxarticles__oxvendorid->value, $this->oxarticles__oxmanufacturerid->value );
04128         }
04129     }
04130 
04138     protected function _onChangeUpdateVarCount( $sParentID )
04139     {
04140         if ( $sParentID ) {
04141             $oDb = oxDb::getDb();
04142             $sParentIdQuoted = $oDb->quote( $sParentID );
04143             $sQ = "select count(*) as varcount from oxarticles where oxparentid = {$sParentIdQuoted}";
04144             $iVarCount = (int) $oDb->getOne( $sQ, false, false );
04145 
04146             $sQ = "update oxarticles set oxvarcount = {$iVarCount} where oxid = {$sParentIdQuoted}";
04147             $oDb->execute( $sQ );
04148         }
04149     }
04150 
04158     protected function _setVarMinMaxPrice( $sParentId )
04159     {
04160         if ( $sParentId ) {
04161             $oDb = oxDb::getDb( oxDb::FETCH_MODE_ASSOC );
04162             $sQ = '
04163                 SELECT
04164                     MIN( IF( `oxarticles`.`oxprice` > 0, `oxarticles`.`oxprice`, `p`.`oxprice` ) ) AS `varminprice`,
04165                     MAX( IF( `oxarticles`.`oxprice` > 0, `oxarticles`.`oxprice`, `p`.`oxprice` ) ) AS `varmaxprice`
04166                 FROM '. $this->getViewName(true) . ' AS `oxarticles`
04167                     LEFT JOIN '. $this->getViewName(true) . ' AS `p` ON ( `p`.`oxid` = `oxarticles`.`oxparentid` AND `p`.`oxprice` > 0 )
04168                 WHERE '. $this->getSqlActiveSnippet(true) .'
04169                     AND ( `oxarticles`.`oxparentid` = '. $oDb->quote( $sParentId ) .' )';
04170             $oDb->setFetchMode( oxDb::FETCH_MODE_ASSOC );
04171             $aPrices = $oDb->getRow( $sQ, false, false );
04172             if ( !is_null( $aPrices['varminprice'] ) || !is_null( $aPrices['varmaxprice'] ) ) {
04173                 $sQ = '
04174                     UPDATE `oxarticles`
04175                     SET
04176                         `oxvarminprice` = '. $oDb->quote( $aPrices['varminprice'] ) .',
04177                         `oxvarmaxprice` = '. $oDb->quote( $aPrices['varmaxprice'] ) .'
04178                     WHERE
04179                         `oxid` = ' . $oDb->quote( $sParentId );
04180             } else {
04181                  $sQ = '
04182                     UPDATE `oxarticles`
04183                     SET
04184                         `oxvarminprice` = `oxprice`,
04185                         `oxvarmaxprice` = `oxprice`
04186                     WHERE
04187                         `oxid` = ' . $oDb->quote( $sParentId );
04188             }
04189             $oDb->execute( $sQ );
04190         }
04191     }
04192 
04193 
04203     protected function _onChangeUpdateMinVarPrice( $sParentID )
04204     {
04205         if ( $sParentID ) {
04206             $oDb = oxDb::getDb();
04207             $sParentIdQuoted = $oDb->quote($sParentID);
04208             //#M0000883 (Sarunas)
04209             $sQ = 'select min(oxprice) as varminprice from '.$this->getViewName(true).' where '.$this->getSqlActiveSnippet(true).' and (oxparentid = '.$sParentIdQuoted.')';
04210             $dVarMinPrice = $oDb->getOne( $sQ, false, false );
04211 
04212             $dParentPrice = $oDb->getOne( "select oxprice from oxarticles where oxid = $sParentIdQuoted ", false, false );
04213 
04214             $blParentBuyable =  $this->getConfig()->getConfigParam( 'blVariantParentBuyable' );
04215 
04216             if ($dVarMinPrice) {
04217                 if ($blParentBuyable) {
04218                     $dVarMinPrice = min($dVarMinPrice, $dParentPrice);
04219                 }
04220             } else {
04221                 $dVarMinPrice = $dParentPrice;
04222             }
04223 
04224             if ( $dVarMinPrice ) {
04225                 $sQ = 'update oxarticles set oxvarminprice = '.$dVarMinPrice.' where oxid = '.$sParentIdQuoted;
04226                 $oDb->execute($sQ);
04227             }
04228         }
04229     }
04230 
04231 
04239     protected function _applyRangePrice()
04240     {
04241         //#buglist_413 if bl_perfLoadPriceForAddList variant price shouldn't be loaded too
04242         if ( !$this->getConfig()->getConfigParam( 'bl_perfLoadPrice' ) || !$this->_blLoadPrice ) {
04243             return;
04244         }
04245 
04246         $this->_blIsRangePrice = false;
04247 
04248         // if parent is buyable - do not apply range price calculations
04249         if ($this->_blSkipFromPrice || !$this->_blNotBuyableParent) {
04250             return;
04251         }
04252 
04253         if ( $this->isParentNotBuyable() && !$this->getConfig()->getConfigParam( 'blLoadVariants' )) {
04254 
04255             $dPrice = $this->_preparePrice( $this->oxarticles__oxvarminprice->value, $this->getArticleVat() );
04256             $this->getPrice()->setPrice($dPrice);
04257             $this->_blIsRangePrice = true;
04258             $this->_calculatePrice( $this->getPrice() );
04259             return;
04260 
04261         }
04262 
04263         $aPrices = array();
04264 
04265         if (!$this->_blNotBuyableParent) {
04266             $aPrices[] = $this->getPrice()->getPrice();
04267         }
04268 
04269         $aVariants = $this->getVariants(false);
04270 
04271         if (count($aVariants)) {
04272             foreach ($aVariants as $sKey => $oVariant) {
04273                 $aPrices[] = $oVariant->getPrice()->getPrice();
04274             }
04275         }
04276 
04277         if ( count( $aPrices ) ) {
04278             $dMinPrice = min( $aPrices );
04279             $dMaxPrice = max( $aPrices );
04280         }
04281 
04282         if ($this->_blNotBuyableParent && isset($dMinPrice) && $dMinPrice == $dMaxPrice) {
04283             $this->getPrice()->setPrice($dMinPrice);
04284         }
04285 
04286         if (isset($dMinPrice) && $dMinPrice != $dMaxPrice) {
04287             $this->getPrice()->setPrice($dMinPrice);
04288             $this->_blIsRangePrice = true;
04289         }
04290     }
04291 
04298     public function getProductId()
04299     {
04300         return $this->getId();
04301     }
04302 
04310     public function getProductParentId()
04311     {
04312         return $this->getParentId();
04313     }
04314 
04320     public function getParentId()
04321     {
04322         return $this->oxarticles__oxparentid->value;
04323     }
04324 
04330     public function isOrderArticle()
04331     {
04332         return false;
04333     }
04334 
04340     public function isVariant()
04341     {
04342         return (bool) ( isset( $this->oxarticles__oxparentid ) ? $this->oxarticles__oxparentid->value : false );
04343     }
04344 
04350     public function isMdVariant()
04351     {
04352         $oMdVariant = oxNew( "oxVariantHandler" );
04353 
04354         return $oMdVariant->isMdVariant($this);
04355     }
04356 
04364     public function getSqlForPriceCategories($sFields = '')
04365     {
04366         if (!$sFields) {
04367             $sFields = 'oxid';
04368         }
04369         $sSelectWhere = "select $sFields from ".$this->_getObjectViewName('oxcategories')." where";
04370         $sQuotedPrice = oxDb::getDb()->quote( $this->oxarticles__oxprice->value );
04371         return  "$sSelectWhere oxpricefrom != 0 and oxpriceto != 0 and oxpricefrom <= $sQuotedPrice and oxpriceto >= $sQuotedPrice"
04372                ." union $sSelectWhere oxpricefrom != 0 and oxpriceto = 0 and oxpricefrom <= $sQuotedPrice"
04373                ." union $sSelectWhere oxpricefrom = 0 and oxpriceto != 0 and oxpriceto >= $sQuotedPrice";
04374     }
04375 
04383     public function inPriceCategory( $sCatNid )
04384     {
04385         $oDb = oxDb::getDb();
04386 
04387         $sQuotedPrice = $oDb->quote( $this->oxarticles__oxprice->value );
04388         $sQuotedCnid = $oDb->quote( $sCatNid );
04389         return (bool) $oDb->getOne(
04390             "select 1 from ".$this->_getObjectViewName('oxcategories')." where oxid=$sQuotedCnid and"
04391            ."(   (oxpricefrom != 0 and oxpriceto != 0 and oxpricefrom <= $sQuotedPrice and oxpriceto >= $sQuotedPrice)"
04392            ." or (oxpricefrom != 0 and oxpriceto = 0 and oxpricefrom <= $sQuotedPrice)"
04393            ." or (oxpricefrom = 0 and oxpriceto != 0 and oxpriceto >= $sQuotedPrice)"
04394            .")"
04395         );
04396     }
04397 
04403     public function getMdVariants()
04404     {
04405         if ( $this->_oMdVariants ) {
04406             return $this->_oMdVariants;
04407         }
04408 
04409         $oParentArticle = $this->getParentArticle();
04410         if ( $oParentArticle ) {
04411             $oVariants = $oParentArticle->getVariants();
04412         } else {
04413             $oVariants = $this->getVariants();
04414         }
04415 
04416         $oVariantHandler = oxNew( "oxVariantHandler" );
04417         $this->_oMdVariants = $oVariantHandler->buildMdVariants( $oVariants, $this->getId() );
04418 
04419         return $this->_oMdVariants;
04420     }
04421 
04427     public function getMdSubvariants()
04428     {
04429         return $this->getMdVariants()->getMdSubvariants();
04430     }
04431 
04439     protected function _hasMasterImage( $iIndex )
04440     {
04441         $sPicName = basename($this->{"oxarticles__oxpic" . $iIndex}->value);
04442 
04443         if ( $sPicName == "nopic.jpg" || $sPicName == "" ) {
04444             return false;
04445         }
04446         if ( $this->isVariant() && $this->getParentArticle()->{"oxarticles__oxpic".$iIndex}->value == $this->{"oxarticles__oxpic".$iIndex}->value ) {
04447             return false;
04448         }
04449 
04450         $sMasterPic = 'product/'.$iIndex . "/" . $sPicName;
04451 
04452         if ( $this->getConfig()->getMasterPicturePath( $sMasterPic ) ) {
04453             return true;
04454         }
04455 
04456         return false;
04457     }
04458 
04467     public function getPictureFieldValue( $sFieldName, $iIndex = null )
04468     {
04469         if ( $sFieldName ) {
04470             $sFieldName = "oxarticles__" . $sFieldName . $iIndex;
04471             return $this->$sFieldName->value;
04472         }
04473     }
04474 
04482     public function getMasterZoomPictureUrl( $iIndex )
04483     {
04484         $sPicUrl  = false;
04485         $sPicName = basename( $this->{"oxarticles__oxpic" . $iIndex}->value );
04486 
04487         if ( $sPicName && $sPicName != "nopic.jpg" ) {
04488             $sPicUrl = $this->getConfig()->getPictureUrl( "master/product/" . $iIndex . "/" . $sPicName );
04489             if ( !$sPicUrl || basename( $sPicUrl ) == "nopic.jpg" ) {
04490                 $sPicUrl = false;
04491             }
04492         }
04493 
04494         return $sPicUrl;
04495     }
04496 
04502     public function getUnitName()
04503     {
04504         if ( $this->oxarticles__oxunitname->value ) {
04505             return oxRegistry::getLang()->translateString( $this->oxarticles__oxunitname->value );
04506         }
04507     }
04508 
04516     public function getArticleFiles( $blAddFromParent=false )
04517     {
04518         if ( $this->_aArticleFiles === null) {
04519 
04520             $this->_aArticleFiles = false;
04521 
04522             $sQ = "SELECT * FROM `oxfiles` WHERE `oxartid` = '".$this->getId()."'";
04523 
04524             if ( !$this->getConfig()->getConfigParam( 'blVariantParentBuyable' ) && $blAddFromParent ) {
04525                 $sQ .= " OR `oxartId` = '". $this->oxarticles__oxparentid->value . "'";
04526             }
04527 
04528             $oArticleFiles = oxNew("oxlist");
04529             $oArticleFiles->init("oxfile");
04530             $oArticleFiles->selectString( $sQ );
04531             $this->_aArticleFiles  = $oArticleFiles;
04532 
04533         }
04534 
04535         return $this->_aArticleFiles;
04536     }
04537 
04543     public function isDownloadable()
04544     {
04545         return $this->oxarticles__oxisdownloadable->value;
04546     }
04547 
04553     public function hasAmountPrice()
04554     {
04555         if ( self::$_blHasAmountPrice === null ) {
04556 
04557             self::$_blHasAmountPrice = false;
04558 
04559             $oDb = oxDb::getDb();
04560             $sQ = "SELECT 1 FROM `oxprice2article` LIMIT 1";
04561 
04562             if ( $oDb->getOne( $sQ ) ) {
04563                 self::$_blHasAmountPrice = true;
04564             }
04565         }
04566 
04567         return self::$_blHasAmountPrice;
04568     }
04569 
04570 
04576     protected function _isPriceViewModeNetto()
04577     {
04578         $blResult = (bool) $this->getConfig()->getConfigParam('blShowNetPrice');
04579         $oUser = $this->getArticleUser();
04580         if ( $oUser ) {
04581             $blResult = $oUser->isPriceViewModeNetto();
04582         }
04583 
04584         return $blResult;
04585     }
04586 
04587 
04595     protected function _getPriceObject( $blCalculationModeNetto = null )
04596     {
04597         $oPrice = oxNew( 'oxPrice' );
04598 
04599         if ( $blCalculationModeNetto === null ) {
04600             $blCalculationModeNetto = $this->_isPriceViewModeNetto();
04601         }
04602 
04603         if ( $blCalculationModeNetto ) {
04604             $oPrice->setNettoPriceMode();
04605         } else {
04606             $oPrice->setBruttoPriceMode();
04607         }
04608 
04609         return $oPrice;
04610     }
04611 
04612 
04620     protected function _getPriceForView( $oPrice )
04621     {
04622         if ( $this->_isPriceViewModeNetto() ) {
04623             $dPrice = $oPrice->getNettoPrice();
04624         } else {
04625             $dPrice = $oPrice->getBruttoPrice();
04626         }
04627 
04628         return $dPrice;
04629     }
04630 
04631 
04641     protected function _preparePrice( $dPrice, $dVat, $blCalculationModeNetto = null )
04642     {
04643         if ( $blCalculationModeNetto === null ) {
04644             $blCalculationModeNetto = $this->_isPriceViewModeNetto();
04645         }
04646 
04647         $oCurrency = $this->getConfig()->getActShopCurrencyObject();
04648 
04649         $blEnterNetPrice = $this->getConfig()->getConfigParam('blEnterNetPrice');
04650         if ( $blCalculationModeNetto && !$blEnterNetPrice ) {
04651             $dPrice = round( oxPrice::brutto2Netto( $dPrice, $dVat ), $oCurrency->decimal );
04652         } elseif ( !$blCalculationModeNetto && $blEnterNetPrice ) {
04653             $dPrice = round( oxPrice::netto2Brutto( $dPrice, $dVat ), $oCurrency->decimal );
04654         }
04655 
04656         return $dPrice;
04657     }
04658 
04659 
04665     public function getFUnitPrice()
04666     {
04667         if ($this->_fPricePerUnit == null) {
04668             if ( $oPrice = $this->getUnitPrice() ) {
04669                 if ( $dPrice = $this->_getPriceForView( $oPrice ) ) {
04670                     $this->_fPricePerUnit = oxRegistry::getLang()->formatCurrency( $dPrice );
04671                 }
04672             }
04673         }
04674 
04675         return $this->_fPricePerUnit;
04676     }
04677 
04678 
04684     public function getUnitPrice()
04685     {
04686         // Performance
04687         if ( !$this->getConfig()->getConfigParam( 'bl_perfLoadPrice' ) || !$this->_blLoadPrice ) {
04688             return;
04689         }
04690 
04691         $oPrice = null;
04692         if ( (double) $this->oxarticles__oxunitquantity->value && $this->oxarticles__oxunitname->value ) {
04693             $oPrice = clone $this->getPrice();
04694             $oPrice->divide( (double) $this->oxarticles__oxunitquantity->value );
04695         }
04696 
04697         return $oPrice;
04698     }
04699 
04700 
04706     public function getFMinPrice()
04707     {
04708         $sPrice = '';
04709         if ( $oPrice = $this->getMinPrice() ) {
04710             $dPrice = $this->_getPriceForView( $oPrice );
04711             $sPrice = oxRegistry::getLang()->formatCurrency( $dPrice );
04712         }
04713 
04714         return $sPrice;
04715     }
04716 
04722     public function getFVarMinPrice()
04723     {
04724         $sPrice = '';
04725         if ( $oPrice = $this->getVarMinPrice() ) {
04726             $dPrice = $this->_getPriceForView( $oPrice );
04727             $sPrice = oxRegistry::getLang()->formatCurrency( $dPrice );
04728         }
04729 
04730         return $sPrice;
04731     }
04732 
04738     public function getVarMinPrice()
04739     {
04740         if ( !$this->getConfig()->getConfigParam( 'bl_perfLoadPrice' ) || !$this->_blLoadPrice ) {
04741             return;
04742         }
04743 
04744         $oPrice = null;
04745         $dPrice = $this->_getVarMinPrice();
04746 
04747         $dPrice = $this->_preparePrice( $dPrice, $this->getArticleVat() );
04748 
04749 
04750         $oPrice = $this->_getPriceObject();
04751         $oPrice->setPrice( $dPrice );
04752         $this->_calculatePrice( $oPrice );
04753 
04754 
04755         return $oPrice;
04756     }
04757 
04758 
04764     public function getMinPrice()
04765     {
04766         if ( !$this->getConfig()->getConfigParam( 'bl_perfLoadPrice' ) || !$this->_blLoadPrice ) {
04767             return;
04768         }
04769 
04770         $oPrice = null;
04771         $dPrice = $this->_getPrice();
04772         if ( $this->_getVarMinPrice() !== null && $dPrice > $this->_getVarMinPrice() ) {
04773             $dPrice = $this->_getVarMinPrice();
04774         }
04775 
04776         $dPrice = $this->_preparePrice( $dPrice, $this->getArticleVat() );
04777 
04778 
04779         $oPrice = $this->_getPriceObject();
04780         $oPrice->setPrice( $dPrice );
04781         $this->_calculatePrice( $oPrice );
04782 
04783         return $oPrice;
04784     }
04785 
04786 
04792     public function isRangePrice()
04793     {
04794         if ( $this->_blIsRangePrice === null ) {
04795 
04796             $this->setRangePrice( false );
04797 
04798             if ( $this->_hasAnyVariant() ) {
04799                 $dPrice = $this->_getPrice();
04800                 $dMinPrice = $this->_getVarMinPrice();
04801                 $dMaxPrice = $this->_getVarMaxPrice();
04802 
04803                 if ( $dMinPrice != $dMaxPrice ) {
04804                     $this->setRangePrice();
04805                 } elseif ( !$this->isParentNotBuyable() &&  $dMinPrice != $dPrice ) {
04806                     $this->setRangePrice();
04807                 }
04808             }
04809         }
04810 
04811         return $this->_blIsRangePrice;
04812     }
04813 
04814 
04822     public function setRangePrice( $blIsRangePrice = true )
04823     {
04824         return $this->_blIsRangePrice = $blIsRangePrice;
04825     }
04826 
04832     protected function _getUserPriceSufix()
04833     {
04834         $sPriceSufix = '';
04835         $oUser = $this->getArticleUser();
04836 
04837         if ( $oUser ) {
04838             if ( $oUser->inGroup( 'oxidpricea' ) ) {
04839                 $sPriceSufix = 'a';
04840             } elseif ( $oUser->inGroup( 'oxidpriceb' ) ) {
04841                 $sPriceSufix = 'b';
04842             } elseif ( $oUser->inGroup( 'oxidpricec' ) ) {
04843                 $sPriceSufix = 'c';
04844             }
04845         }
04846 
04847         return $sPriceSufix;
04848     }
04849 
04850 
04856     protected function _getPrice()
04857     {
04858             $sPriceSufix = $this->_getUserPriceSufix();
04859             if ( $sPriceSufix === '') {
04860                 $dPrice = $this->oxarticles__oxprice->value;
04861             } else {
04862                 if ( $this->getConfig()->getConfigParam( 'blOverrideZeroABCPrices' ) ) {
04863                     $dPrice = ($this->{oxarticles__oxprice.$sPriceSufix}->value !=0 )? $this->{oxarticles__oxprice.$sPriceSufix}->value : $this->oxarticles__oxprice->value;
04864                 } else {
04865                     $dPrice = $this->{oxarticles__oxprice.$sPriceSufix}->value;
04866                 }
04867             }
04868         return $dPrice;
04869     }
04870 
04871 
04877     protected function _getVarMinPrice()
04878     {
04879         if ( $this->_dVarMinPrice === null) {
04880 
04881             $sPriceSufix = $this->_getUserPriceSufix();
04882             if ( $dPrice === null ) {
04883                 if ( $sPriceSufix === '' ) {
04884                     $dPrice = $this->oxarticles__oxvarminprice->value;
04885                 } else {
04886                     $sSql = 'SELECT ';
04887                     if ( $this->getConfig()->getConfigParam( 'blOverrideZeroABCPrices' ) ) {
04888                         $sSql .=  'MIN( IF(`oxprice'.$sPriceSufix.'` = 0, `oxprice`, `oxprice'.$sPriceSufix.'`) ) AS `varminprice` ';
04889                     } else {
04890                         $sSql .=  'MIN(`oxprice'.$sPriceSufix.'`) AS `varminprice` ';
04891                     }
04892 
04893                     $sSql .=  ' FROM ' . $this->getViewName(true) . '
04894                         WHERE ' .$this->getSqlActiveSnippet(true) . '
04895                             AND ( `oxparentid` = ' . oxDb::getDb()->quote( $this->getId() ) . ' )';
04896 
04897                     $dPrice = oxDb::getDb()->getOne( $sSql );
04898                 }
04899             }
04900             $this->_dVarMinPrice = $dPrice;
04901         }
04902 
04903         return $this->_dVarMinPrice;
04904     }
04905 
04911     protected function _getSubShopVarMinPrice()
04912     {
04913         $myConfig = $this->getConfig();
04914         $sShopId = $myConfig->getShopId();
04915         if ($this->getConfig()->getConfigParam( 'blMallCustomPrice' ) && $sShopId != $this->oxarticles__oxshopid->value ) {
04916             $sPriceSufix = $this->_getUserPriceSufix();
04917             $sSql = 'SELECT ';
04918             if ( $sPriceSufix != '' && $this->getConfig()->getConfigParam( 'blOverrideZeroABCPrices' ) ) {
04919                 $sSql .=  'MIN(IF(`oxfield2shop`.`oxprice'.$sPriceSufix.'` = 0, `oxfield2shop`.`oxprice`, `oxfield2shop`.`oxprice'.$sPriceSufix.'`)) AS `varminprice` ';
04920             } else {
04921                 $sSql .=  'MIN(`oxfield2shop`.`oxprice'.$sPriceSufix.'`) AS `varminprice` ';
04922             }
04923             $sSql .=  ' FROM ' . getViewName('oxfield2shop') . ' AS oxfield2shop
04924                         INNER JOIN ' . $this->getViewName(true) . ' AS oxarticles ON `oxfield2shop`.`oxartid` = `oxarticles`.`oxid`
04925                         WHERE ' .$this->getSqlActiveSnippet(true) . '
04926                             AND ( `oxarticles`.`oxparentid` = ' . oxDb::getDb()->quote( $this->getId() ) . ' )
04927                             AND ( `oxfield2shop`.`oxshopid` = ' . oxDb::getDb()->quote( $sShopId ) . ' )';
04928             $dPrice = oxDb::getDb()->getOne( $sSql );
04929         }
04930         return $dPrice;
04931     }
04932 
04938     protected function _getVarMaxPrice()
04939     {
04940         if ( $this->_dVarMaxPrice === null ) {
04941 
04942             $sPriceSufix = $this->_getUserPriceSufix();
04943             if ( $dPrice === null ) {
04944                 if ( $sPriceSufix === '') {
04945                     $dPrice = $this->oxarticles__oxvarmaxprice->value;
04946                 } else {
04947                     $sSql = 'SELECT ';
04948                     if ( $this->getConfig()->getConfigParam( 'blOverrideZeroABCPrices' ) ) {
04949                         $sSql .=  'MAX( IF(`oxprice'.$sPriceSufix.'` = 0, `oxprice`, `oxprice'.$sPriceSufix.'`) ) AS `varmaxprice` ';
04950                     } else {
04951                         $sSql .=  'MAX(`oxprice'.$sPriceSufix.'`) AS `varmaxprice` ';
04952                     }
04953 
04954                     $sSql .=  ' FROM ' . $this->getViewName(true) . '
04955                         WHERE ' .$this->getSqlActiveSnippet(true) . '
04956                             AND ( `oxparentid` = ' . oxDb::getDb()->quote( $this->getId() ) . ' )';
04957 
04958                     $dPrice = oxDb::getDb()->getOne( $sSql );
04959                 }
04960             }
04961             $this->_dVarMaxPrice = $dPrice;
04962         }
04963 
04964         return $this->_dVarMaxPrice;
04965     }
04966 
04972     protected function _getSubShopVarMaxPrice()
04973     {
04974         $myConfig = $this->getConfig();
04975         $sShopId = $myConfig->getShopId();
04976         if ($this->getConfig()->getConfigParam( 'blMallCustomPrice' ) && $sShopId != $this->oxarticles__oxshopid->value ) {
04977             $sPriceSufix = $this->_getUserPriceSufix();
04978             $sSql = 'SELECT ';
04979             if ( $sPriceSufix != '' && $this->getConfig()->getConfigParam( 'blOverrideZeroABCPrices' ) ) {
04980                 $sSql .=  'MAX(IF(`oxfield2shop`.`oxprice'.$sPriceSufix.'` = 0, `oxfield2shop`.`oxprice`, `oxfield2shop`.`oxprice'.$sPriceSufix.'`)) AS `varmaxprice` ';
04981             } else {
04982                 $sSql .=  'MAX(`oxfield2shop`.`oxprice'.$sPriceSufix.'`) AS `varmaxprice` ';
04983             }
04984             $sSql .=  ' FROM ' . getViewName('oxfield2shop') . ' AS oxfield2shop
04985                         INNER JOIN ' . $this->getViewName(true) . ' AS oxarticles ON `oxfield2shop`.`oxartid` = `oxarticles`.`oxid`
04986                         WHERE ' .$this->getSqlActiveSnippet(true) . '
04987                             AND ( `oxarticles`.`oxparentid` = ' . oxDb::getDb()->quote( $this->getId() ) . ' )
04988                             AND ( `oxfield2shop`.`oxshopid` = ' . oxDb::getDb()->quote( $sShopId ) . ' )';
04989             $dPrice = oxDb::getDb()->getOne( $sSql );
04990         }
04991         return $dPrice;
04992     }
04993 
04994 }