oxvoucher.php

Go to the documentation of this file.
00001 <?php
00002 
00008 class oxVoucher extends oxBase
00009 {
00010 
00011     protected $_oSerie = null;
00012 
00018     protected $_blDisableShopCheck = true;
00019 
00023     protected $_sClassName = 'oxvoucher';
00024 
00028     public function __construct()
00029     {
00030         parent::__construct();
00031         $this->init( 'oxvouchers' );
00032     }
00033 
00045     public function getVoucherByNr( $sVoucherNr, $aVouchers = array(), $blCheckavalability = false )
00046     {
00047         $oRet = null;
00048         if ( !is_null( $sVoucherNr ) ) {
00049 
00050             $sViewName = $this->getViewName();
00051             $sSeriesViewName = getViewName( 'oxvoucherseries' );
00052 
00053             $sQ  = "select {$sViewName}.* from {$sViewName}, {$sSeriesViewName} where
00054                         {$sSeriesViewName}.oxid = {$sViewName}.oxvoucherserieid and
00055                         {$sViewName}.oxvouchernr = " . oxDb::getDb()->quote( $sVoucherNr ) . " and ";
00056 
00057             if ( is_array( $aVouchers ) ) {
00058                 foreach ( $aVouchers as $sVoucherId => $sSkipVoucherNr ) {
00059                     $sQ .= "{$sViewName}.oxid != " . oxDb::getDb()->quote( $sVoucherId ) . " and ";
00060                 }
00061             }
00062             $sQ .= "( {$sViewName}.oxorderid is NULL || {$sViewName}.oxorderid = '' ) ";
00063 
00064             //voucher timeout for 3 hours
00065             if ( $blCheckavalability ) {
00066                 $iTime = time() - 3600 * 3;
00067                 $sQ .= " and {$sViewName}.oxreserved < '{$iTime}' ";
00068             }
00069 
00070             $sQ .= " limit 1";
00071 
00072             if ( ! ( $oRet = $this->assignRecord( $sQ ) ) ) {
00073                 $oEx = oxNew( 'oxVoucherException' );
00074                 $oEx->setMessage( 'EXCEPTION_VOUCHER_NOVOUCHER' );
00075                 $oEx->setVoucherNr( $sVoucherNr );
00076                 throw $oEx;
00077             }
00078         }
00079 
00080         return $oRet;
00081     }
00082 
00092     public function markAsUsed( $sOrderId, $sUserId, $dDiscount )
00093     {
00094         //saving oxreserved field
00095         if ( $this->oxvouchers__oxid->value ) {
00096             $this->oxvouchers__oxorderid->setValue($sOrderId);
00097             $this->oxvouchers__oxuserid->setValue($sUserId);
00098             $this->oxvouchers__oxdiscount->setValue($dDiscount);
00099             $this->oxvouchers__oxdateused->setValue(date( "Y-m-d", oxUtilsDate::getInstance()->getTime() ));
00100             $this->save();
00101         }
00102     }
00103 
00109     public function markAsReserved()
00110     {
00111         //saving oxreserved field
00112         $sVoucherID = $this->oxvouchers__oxid->value;
00113 
00114         if ( $sVoucherID ) {
00115             $sQ = "update oxvouchers set oxreserved = " . time() . " where oxid = " . oxDb::getDb()->quote( $sVoucherID );
00116             oxDb::getDb()->Execute( $sQ );
00117         }
00118     }
00119 
00125     public function unMarkAsReserved()
00126     {
00127         //saving oxreserved field
00128         $sVoucherID = $this->oxvouchers__oxid->value;
00129 
00130         if ( $sVoucherID ) {
00131             $sQ = "update oxvouchers set oxreserved = 0 where oxid = " . oxDb::getDb()->quote( $sVoucherID );
00132             oxDb::getDb()->Execute($sQ);
00133         }
00134     }
00135 
00145     public function getDiscountValue( $dPrice )
00146     {
00147         if ($this->_isProductVoucher()) {
00148             return $this->_getProductDiscoutValue( $dPrice );
00149         } elseif ($this->_isCategoryVoucher()) {
00150             return $this->_getCategoryDiscoutValue( $dPrice );
00151         } else {
00152             return $this->_getGenericDiscoutValue( $dPrice );
00153         }
00154     }
00155 
00156     // Checking General Availability
00167     public function checkVoucherAvailability( $aVouchers, $dPrice )
00168     {
00169         $this->_isAvailableWithSameSeries( $aVouchers );
00170         $this->_isAvailableWithOtherSeries( $aVouchers );
00171         $this->_isValidDate();
00172         $this->_isAvailablePrice( $dPrice );
00173         $this->_isNotReserved();
00174 
00175         // returning true - no exception was thrown
00176         return true;
00177     }
00178 
00190     public function checkBasketVoucherAvailability( $aVouchers, $dPrice )
00191     {
00192         $this->_isAvailableWithSameSeries( $aVouchers );
00193         $this->_isAvailableWithOtherSeries( $aVouchers );
00194         $this->_isValidDate();
00195         $this->_isAvailablePrice( $dPrice );
00196 
00197         // returning true - no exception was thrown
00198         return true;
00199     }
00200 
00210     protected function _isAvailablePrice( $dPrice )
00211     {
00212         if ( $this->getDiscountValue( $dPrice ) < 0 ) {
00213             $oEx = oxNew( 'oxVoucherException' );
00214             $oEx->setMessage('EXCEPTION_VOUCHER_TOTALBELOWZERO');
00215             $oEx->setVoucherNr($this->oxvouchers__oxvouchernr->value);
00216             throw $oEx;
00217         }
00218         $oSerie = $this->getSerie();
00219         $oCur = $this->getConfig()->getActShopCurrencyObject();
00220         if ( $oSerie->oxvoucherseries__oxminimumvalue->value && $dPrice < ($oSerie->oxvoucherseries__oxminimumvalue->value*$oCur->rate) ) {
00221             $oEx = oxNew( 'oxVoucherException' );
00222             $oEx->setMessage('EXCEPTION_VOUCHER_INCORRECTPRICE');
00223             $oEx->setVoucherNr($this->oxvouchers__oxvouchernr->value);
00224             throw $oEx;
00225         }
00226 
00227         return true;
00228     }
00229 
00241     protected function _isAvailableWithSameSeries( $aVouchers )
00242     {
00243         if ( is_array( $aVouchers ) ) {
00244             $sId = $this->getId();
00245             if (isset($aVouchers[$sId])) {
00246                 unset($aVouchers[$sId]);
00247             }
00248             $oSerie = $this->getSerie();
00249             if (!$oSerie->oxvoucherseries__oxallowsameseries->value) {
00250                 foreach ( $aVouchers as $voucherId => $voucherNr ) {
00251                     $oVoucher = oxNew( 'oxvoucher' );
00252                     $oVoucher->load($voucherId);
00253                     if ( $this->oxvouchers__oxvoucherserieid->value == $oVoucher->oxvouchers__oxvoucherserieid->value ) {
00254                             $oEx = oxNew( 'oxVoucherException' );
00255                             $oEx->setMessage('EXCEPTION_VOUCHER_NOTALLOWEDSAMESERIES');
00256                             throw $oEx;
00257                     }
00258                 }
00259             }
00260         }
00261 
00262         return true;
00263     }
00264 
00275     protected function _isAvailableWithOtherSeries( $aVouchers )
00276     {
00277         if ( is_array( $aVouchers ) && count($aVouchers) ) {
00278             $oSerie = $this->getSerie();
00279             $sIds = '\''.implode('\',\'', array_keys($aVouchers)).'\'';
00280             $blAvailable = true;
00281             if (!$oSerie->oxvoucherseries__oxallowotherseries->value) {
00282                 // just search for vouchers with different series
00283                 $sSql  = "select 1 from oxvouchers where oxvouchers.oxid in ($sIds) and ";
00284                 $sSql .= "oxvouchers.oxvoucherserieid != " . oxDb::getDb()->quote( $this->oxvouchers__oxvoucherserieid->value ) ;
00285                 $blAvailable &= !oxDb::getDb()->getOne($sSql);
00286             } else {
00287                 // search for vouchers with different series and those vouchers do not allow other series
00288                 $sSql  = "select 1 from oxvouchers left join oxvoucherseries on oxvouchers.oxvoucherserieid=oxvoucherseries.oxid ";
00289                 $sSql .= "where oxvouchers.oxid in ($sIds) and oxvouchers.oxvoucherserieid != " . oxDb::getDb()->quote( $this->oxvouchers__oxvoucherserieid->value );
00290                 $sSql .= "and not oxvoucherseries.oxallowotherseries";
00291                 $blAvailable &= !oxDb::getDb()->getOne($sSql);
00292             }
00293             if ( !$blAvailable ) {
00294                     $oEx = oxNew( 'oxVoucherException' );
00295                     $oEx->setMessage('EXCEPTION_VOUCHER_NOTALLOWEDOTHERSERIES');
00296                     $oEx->setVoucherNr($this->oxvouchers__oxvouchernr->value);
00297                     throw $oEx;
00298             }
00299         }
00300 
00301         return true;
00302     }
00303 
00311     protected function _isValidDate()
00312     {
00313         $oSerie = $this->getSerie();
00314         // if time is not set - keep it as active by default
00315         $sDefTimeStamp = oxUtilsDate::getInstance()->formatDBDate( '-' );
00316         if ( $oSerie->oxvoucherseries__oxbegindate->value == $sDefTimeStamp &&
00317              $oSerie->oxvoucherseries__oxenddate->value == $sDefTimeStamp ) {
00318             return true;
00319         }
00320 
00321         if ( ( strtotime( $oSerie->oxvoucherseries__oxbegindate->value ) < time() &&
00322               strtotime( $oSerie->oxvoucherseries__oxenddate->value ) > time() ) ||
00323             !$oSerie->oxvoucherseries__oxenddate->value ||
00324             $oSerie->oxvoucherseries__oxenddate->value == $sDefTimeStamp ) {
00325             return true;
00326         }
00327 
00328         $oEx = oxNew( 'oxVoucherException' );
00329         $oEx->setMessage('EXCEPTION_VOUCHER_ISNOTVALIDDATE');
00330         throw $oEx;
00331     }
00332 
00340     protected function _isNotReserved()
00341     {
00342 
00343         if ( $this->oxvouchers__oxreserved->value < time() - 3600 * 3 ) {
00344             return true;
00345         }
00346 
00347         $oEx = oxNew( 'oxVoucherException' );
00348         $oEx->setMessage('EXCEPTION_VOUCHER_ISRESERVED');
00349         throw $oEx;
00350     }
00351 
00352     // Checking User Availability
00362     public function checkUserAvailability( $oUser )
00363     {
00364 
00365         $this->_isAvailableInOtherOrder( $oUser );
00366         $this->_isValidUserGroup( $oUser );
00367 
00368         // returning true if no exception was thrown
00369         return true;
00370     }
00371 
00381     protected function _isAvailableInOtherOrder( $oUser )
00382     {
00383         $oSerie = $this->getSerie();
00384         if ( !$oSerie->oxvoucherseries__oxallowuseanother->value ) {
00385 
00386             $sSelect  = 'select count(*) from '.$this->getViewName().' where oxuserid = '. oxDb::getDb()->quote( $oUser->oxuser__oxid->value ) . ' and ';
00387             $sSelect .= 'oxvoucherserieid = ' . oxDb::getDb()->quote( $this->oxvouchers__oxvoucherserieid->value ) . ' and ';
00388             $sSelect .= 'oxorderid is not NULL and oxorderid != "" ';
00389 
00390             if ( oxDb::getDb()->getOne( $sSelect )) {
00391                 $oEx = oxNew( 'oxVoucherException' );
00392                 $oEx->setMessage('EXCEPTION_VOUCHER_NOTAVAILABLEINOTHERORDER');
00393                 $oEx->setVoucherNr($this->oxvouchers__oxvouchernr->value);
00394                 throw $oEx;
00395             }
00396         }
00397 
00398         return true;
00399     }
00400 
00410     protected function _isValidUserGroup( $oUser )
00411     {
00412         $oVoucherSerie = $this->getSerie();
00413         $oUserGroups = $oVoucherSerie->setUserGroups();
00414 
00415         // dodger Task #1555 R Voucher does not work for not logged user?
00416         if ( !$oUserGroups->count() ) {
00417             return true;
00418         }
00419 
00420         if ( $oUser ) {
00421             foreach ( $oUserGroups as $oGroup ) {
00422                 if ( $oUser->inGroup( $oGroup->getId() ) ) {
00423                     return true;
00424                 }
00425             }
00426         }
00427 
00428         $oEx = oxNew( 'oxVoucherException' );
00429         $oEx->setMessage( 'EXCEPTION_VOUCHER_NOTVALIDUSERGROUP' );
00430         $oEx->setVoucherNr( $this->oxvouchers__oxvouchernr->value );
00431         throw $oEx;
00432     }
00433 
00439     public function getSimpleVoucher()
00440     {
00441         $oVoucher = new oxStdClass();
00442         $oVoucher->sVoucherId = $this->getId();
00443         $oVoucher->sVoucherNr = $this->oxvouchers__oxvouchernr->value;
00444         // R. setted in oxbasket : $oVoucher->fVoucherdiscount = oxLang::getInstance()->formatCurrency( $this->oxvouchers__oxdiscount->value );
00445 
00446         return $oVoucher;
00447     }
00448 
00454     public function getSerie()
00455     {
00456         if ($this->_oSerie !== null) {
00457             return $this->_oSerie;
00458         }
00459         $oSerie = oxNew('oxvoucherserie');
00460         if (!$oSerie->load($this->oxvouchers__oxvoucherserieid->value)) {
00461             throw new oxObjectException();
00462         }
00463         $this->_oSerie = $oSerie;
00464         return $oSerie;
00465     }
00466 
00472     protected function _isProductVoucher()
00473     {
00474         $myDB    = oxDb::getDb();
00475         $oSerie  = $this->getSerie();
00476         $sSelect = "select 1 from oxobject2discount where oxdiscountid = '".$oSerie->getId()."' and oxtype = 'oxarticles'";
00477         $blOk    = ( bool ) $myDB->getOne( $sSelect );
00478 
00479         return $blOk;
00480     }
00481 
00487     protected function _isCategoryVoucher()
00488     {
00489         $myDB    = oxDb::getDb();
00490         $oSerie  = $this->getSerie();
00491         $sSelect = "select 1 from oxobject2discount where oxdiscountid = '".$oSerie->getId()."' and oxtype = 'oxcategories'";
00492         $blOk    = ( bool ) $myDB->getOne( $sSelect );
00493 
00494         return $blOk;
00495     }
00496 
00502     protected function _getSerieDiscount( )
00503     {
00504         $oSerie    = $this->getSerie();
00505         $oDiscount = oxNew('oxDiscount');
00506 
00507         $oDiscount->setId($oSerie->getId());
00508         $oDiscount->oxdiscount__oxshopid      = new oxField($oSerie->oxvoucherseries__oxshopid->value);
00509         $oDiscount->oxdiscount__oxactive      = new oxField(true);
00510         $oDiscount->oxdiscount__oxactivefrom  = new oxField($oSerie->oxvoucherseries__oxbegindate->value);
00511         $oDiscount->oxdiscount__oxactiveto    = new oxField($oSerie->oxvoucherseries__oxenddate->value);
00512         $oDiscount->oxdiscount__oxtitle       = new oxField($oSerie->oxvoucherseries__oxserienr->value);
00513         $oDiscount->oxdiscount__oxamount      = new oxField(1);
00514         $oDiscount->oxdiscount__oxamountto    = new oxField(MAX_64BIT_INTEGER);
00515         $oDiscount->oxdiscount__oxprice       = new oxField(0);
00516         $oDiscount->oxdiscount__oxpriceto     = new oxField(MAX_64BIT_INTEGER);
00517         $oDiscount->oxdiscount__oxaddsumtype  = new oxField($oSerie->oxvoucherseries__oxdiscounttype->value=='percent'?'%':'abs');
00518         $oDiscount->oxdiscount__oxaddsum      = new oxField($oSerie->oxvoucherseries__oxdiscount->value);
00519         $oDiscount->oxdiscount__oxitmartid    = new oxField();
00520         $oDiscount->oxdiscount__oxitmamount   = new oxField();
00521         $oDiscount->oxdiscount__oxitmmultiple = new oxField();
00522 
00523         return $oDiscount;
00524     }
00525 
00533     protected function _getBasketItems($oDiscount = null)
00534     {
00535         if ($this->oxvouchers__oxorderid->value) {
00536             return $this->_getOrderBasketItems($oDiscount);
00537         } elseif ( $this->getSession()->getBasket() ) {
00538             return $this->_getSessionBasketItems($oDiscount);
00539         } else {
00540             return array();
00541         }
00542     }
00543 
00551     protected function _getOrderBasketItems($oDiscount = null)
00552     {
00553         if (is_null($oDiscount)) {
00554             $oDiscount = $this->_getSerieDiscount();
00555         }
00556 
00557         $oOrder = oxNew('oxorder');
00558         $oOrder->load($this->oxvouchers__oxorderid->value);
00559 
00560         $aItems  = array();
00561         $iCount  = 0;
00562 
00563         foreach ( $oOrder->getOrderArticles(true) as $oOrderArticle ) {
00564             if (!$oOrderArticle->skipDiscounts() && $oDiscount->isForBasketItem($oOrderArticle)) {
00565                 $aItems[$iCount] = array(
00566                     'oxid'     => $oOrderArticle->getProductId(),
00567                     'price'    => $oOrderArticle->oxorderarticles__oxprice->value,
00568                     'discount' => $oDiscount->getAbsValue($oOrderArticle->oxorderarticles__oxprice->value),
00569                     'amount'   => $oOrderArticle->oxorderarticles__oxamount->value,
00570                 );
00571                 $iCount ++;
00572             }
00573         }
00574 
00575         return $aItems;
00576     }
00577 
00585     protected function _getSessionBasketItems($oDiscount = null)
00586     {
00587         if (is_null($oDiscount)) {
00588             $oDiscount = $this->_getSerieDiscount();
00589         }
00590 
00591         $oBasket = $this->getSession()->getBasket();
00592         $aItems  = array();
00593         $iCount  = 0;
00594 
00595         foreach ( $oBasket->getContents() as $oBasketItem ) {
00596             if ( !$oBasketItem->isDiscountArticle() && ( $oArticle = $oBasketItem->getArticle() ) && !$oArticle->skipDiscounts() && $oDiscount->isForBasketItem($oArticle) ) {
00597 
00598                 $aItems[$iCount] = array(
00599                     'oxid'     => $oArticle->getId(),
00600                     'price'    => $oArticle->getBasketPrice( $oBasketItem->getAmount(), $oBasketItem->getSelList(), $oBasket )->getBruttoPrice(),
00601                     'discount' => $oDiscount->getAbsValue($oArticle->getBasketPrice( $oBasketItem->getAmount(), $oBasketItem->getSelList(), $oBasket )->getBruttoPrice()),
00602                     'amount'   => $oBasketItem->getAmount(),
00603                 );
00604 
00605                 $iCount ++;
00606             }
00607         }
00608 
00609         return $aItems;
00610     }
00611 
00621     protected function _getGenericDiscoutValue( $dPrice )
00622     {
00623         $oSerie = $this->getSerie();
00624         if ( $oSerie->oxvoucherseries__oxdiscounttype->value == 'absolute' ) {
00625             $oCur = $this->getConfig()->getActShopCurrencyObject();
00626             $dDiscount = $oSerie->oxvoucherseries__oxdiscount->value * $oCur->rate;
00627         } else {
00628             $dDiscount = $oSerie->oxvoucherseries__oxdiscount->value / 100 * $dPrice;
00629         }
00630 
00631         if ( $dDiscount > $dPrice ) {
00632             $oEx = oxNew( 'oxVoucherException' );
00633             $oEx->setMessage('EXCEPTION_VOUCHER_TOTALBELOWZERO');
00634             $oEx->setVoucherNr($this->oxvouchers__oxvouchernr->value);
00635             throw $oEx;
00636         }
00637 
00638         return $dDiscount;
00639     }
00640 
00650     protected function _getProductDiscoutValue( $dPrice )
00651     {
00652         $oDiscount    = $this->_getSerieDiscount();
00653         $aBasketItems = $this->_getBasketItems($oDiscount);
00654 
00655         // Basket Item Count and isAdmin check (unble to access property $oOrder->_getOrderBasket()->_blSkipVouchersAvailabilityChecking)
00656         if (!count($aBasketItems) && !$this->isAdmin() ) {
00657             $oEx = oxNew( 'oxVoucherException' );
00658             $oEx->setMessage('EXCEPTION_VOUCHER_NOVOUCHER');
00659             $oEx->setVoucherNr($this->oxvouchers__oxvouchernr->value);
00660             throw $oEx;
00661         }
00662 
00663         $oSerie    = $this->getSerie();
00664 
00665         $oVoucherPrice  = oxNew('oxPrice');
00666         $oDiscountPrice = oxNew('oxPrice');
00667         $oProductPrice  = oxNew('oxPrice');
00668         $oProductTotal  = oxNew('oxPrice');
00669 
00670         foreach ( $aBasketItems as $aBasketItem ) {
00671 
00672             $oDiscountPrice->setPrice($aBasketItem['discount']);
00673             $oProductPrice->setPrice($aBasketItem['price']);
00674 
00675             // Individual voucher is not multiplied by article amount
00676             if (!$oSerie->oxvoucherseries__oxcalculateonce->value) {
00677                 $oDiscountPrice->multiply($aBasketItem['amount']);
00678                 $oProductPrice->multiply($aBasketItem['amount']);
00679             }
00680 
00681             $oVoucherPrice->add($oDiscountPrice->getBruttoPrice());
00682             $oProductTotal->add($oProductPrice->getBruttoPrice());
00683         }
00684 
00685         $dVoucher = $oVoucherPrice->getBruttoPrice();
00686         $dProduct = $oProductTotal->getBruttoPrice();
00687 
00688         if ( $dVoucher > $dProduct ) {
00689             return $dProduct;
00690         }
00691 
00692         return $dVoucher;
00693     }
00694 
00704     protected function _getCategoryDiscoutValue( $dPrice )
00705     {
00706         $oDiscount    = $this->_getSerieDiscount();
00707         $aBasketItems = $this->_getBasketItems($oDiscount);
00708 
00709         // Basket Item Count and isAdmin check (unble to access property $oOrder->_getOrderBasket()->_blSkipVouchersAvailabilityChecking)
00710         if (!count($aBasketItems) && !$this->isAdmin() ) {
00711             $oEx = oxNew( 'oxVoucherException' );
00712             $oEx->setMessage('EXCEPTION_VOUCHER_NOVOUCHER');
00713             $oEx->setVoucherNr($this->oxvouchers__oxvouchernr->value);
00714             throw $oEx;
00715         }
00716 
00717         $oCategoryPrice = oxNew('oxPrice');
00718         $oProductPrice  = oxNew('oxPrice');
00719         $oProductTotal  = oxNew('oxPrice');
00720 
00721         foreach ( $aBasketItems as $aBasketItem ) {
00722 
00723             $oProductPrice->setPrice($aBasketItem['price']);
00724             $oProductPrice->multiply($aBasketItem['amount']);
00725 
00726             $oCategoryPrice->add($aBasketItem['price']);
00727             $oProductTotal->add($oProductPrice->getBruttoPrice());
00728         }
00729 
00730         $dVoucher = $oDiscount->getAbsValue($oCategoryPrice->getBruttoPrice());
00731         $dProduct = $oProductTotal->getBruttoPrice();
00732 
00733         if ( $dVoucher > $dProduct ) {
00734             return $dProduct;
00735         }
00736 
00737         return $dVoucher;
00738     }
00739 
00747     public function __get( $sName )
00748     {
00749         switch ( $sName ) {
00750 
00751             // simple voucher mapping
00752             case 'sVoucherId':
00753                 return $this->getId();
00754                 break;
00755             case 'sVoucherNr':
00756                 return $this->oxvouchers__oxvouchernr;
00757                 break;
00758             case 'fVoucherdiscount':
00759                 return $this->oxvouchers__oxdiscount;
00760                 break;
00761 
00762             // deprecated values for email templates
00763             case 'oxmodvouchers__oxvouchernr':
00764                 return $this->oxvouchers__oxvouchernr;
00765                 break;
00766             case 'oxmodvouchers__oxdiscounttype':
00767                 return $this->getSerie()->oxvoucherseries__oxdiscounttype;
00768                 break;
00769             case 'oxmodvouchers__oxdiscount':
00770                 // former email templates are expecting type dependent discount values !!!
00771                 if ($this->getSerie()->oxvoucherseries__oxdiscounttype->value == 'absolute') {
00772                     return $this->oxvouchers__oxdiscount;
00773                 } else {
00774                     return $this->getSerie()->oxvoucherseries__oxdiscount;
00775                 }
00776                 break;
00777         }
00778         return parent::__get($sName);
00779     }
00780 }