12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681 |
- <?php
- /**
- *
- * PHP Pro Bid $Id$ yWmrd6SsYKr+Ju1kqDkgXtlajw1PaVMT9T2YTgnUX0c=
- *
- * @link http://www.phpprobid.com
- * @copyright Copyright (c) 2017 Online Ventures Software & CodeCube SRL
- * @license http://www.phpprobid.com/license Commercial License
- *
- * @version 7.10 [rev.7.10.03]
- */
- /**
- * listings table row object model
- */
- namespace Ppb\Db\Table\Row;
- use Ppb\Service,
- Cube\Controller\Front,
- Cube\Db\Table,
- Cube\Db\Expr,
- Ppb\Model\Shipping as ShippingModel,
- Ppb\Db\Table\Rowset;
- class Listing extends AbstractRow
- {
- /**
- * listing statuses
- */
- const OPEN = 'open';
- const CLOSED = 'closed';
- const SCHEDULED = 'scheduled';
- /**
- * custom fields tables "type" column
- */
- const CUSTOM_FIELDS_TYPE = 'item';
- /**
- * unlimited quantity flag
- */
- const UNLIMITED_QUANTITY = -1;
- /**
- *
- * relist type
- * 'same' -> same listing
- * 'new' -> new listing
- */
- const RELIST_SAME = 'same';
- const RELIST_NEW = 'new';
- /**
- * last count operation flags
- */
- const COUNT_OP_NONE = 'none';
- const COUNT_OP_ADD = 'add';
- const COUNT_OP_SUBTRACT = 'subtract';
- /**
- * operations
- */
- const ADD = 'add';
- const SUBTRACT = 'subtract';
- /**
- * max chars for the short description method
- */
- const SHORT_DESC_MAX_CHARS = 255;
- /**
- *
- * serializable fields
- *
- * @var array
- */
- protected $_serializable = array('postage_settings', 'offline_payment', 'direct_payment');
- /**
- *
- * array of available relist methods
- *
- * @var array
- */
- protected $_relistMethods = array(
- self::RELIST_SAME,
- self::RELIST_NEW,
- );
- /**
- *
- * relist method to be used
- *
- * @var string
- */
- protected $_relistMethod;
- /**
- *
- * bids rowset
- * contains all bids placed on this listing
- *
- * @var \Ppb\Db\Table\Rowset\Bids
- */
- protected $_bids = null;
- /**
- *
- * offers rowset
- * contains all offers posted on this listing
- *
- * @var \Ppb\Db\Table\Rowset\Offers
- */
- protected $_offers = null;
- /**
- *
- * sales rowset
- * contains all sales transactions
- *
- * @var \Ppb\Db\Table\Rowset\Sales
- */
- protected $_sales = null;
- /**
- *
- * bid increments table service
- *
- * @var \Ppb\Service\Table\BidIncrements
- */
- protected $_bidIncrements;
- /**
- *
- * custom fields and custom fields data tables service
- *
- * @var \Ppb\Service\CustomFields
- */
- protected $_customFields;
- /**
- *
- * sales listings table service
- *
- * @var \Ppb\Service\Table\SalesListings
- */
- protected $_salesListings;
- /**
- *
- * custom fields data table service
- *
- * @var \Ppb\Service\CustomFieldsData
- */
- protected $_customFieldsData;
- /**
- *
- * categories table service
- *
- * @var \Ppb\Service\Table\Relational\Categories
- */
- protected $_categories;
- /**
- *
- * listing media rowset
- *
- * @var \Cube\Db\Table\Rowset\AbstractRowset
- */
- protected $_listingsMedia;
- /**
- *
- * the id of a sale that was created as a result of a purchase
- * (buy out or assign winner)
- *
- * @var int
- */
- protected $_saleId;
- /**
- *
- * tax type object that applies to the listing
- * or null if no tax will apply
- *
- * @var \Ppb\Db\Table\Row\TaxType|null
- */
- protected $_taxType = null;
- /**
- *
- * closed flag
- *
- * @var bool
- */
- protected $_closedFlag = false;
- /**
- *
- * class constructor
- *
- * @param array $data
- */
- public function __construct(array $data = array())
- {
- if (!isset($data['data']['id'])) {
- $data['data']['id'] = 0;
- }
- parent::__construct($data);
- }
- /**
- *
- * get bids rowset ordered from the highest to the lowest
- *
- * @return \Ppb\Db\Table\Rowset\Bids
- */
- public function getBids()
- {
- if (!$this->_bids instanceof Rowset\Bids) {
- $select = $this->getTable()->select()
- ->order(array('amount DESC', 'id DESC'));
- $this->_bids = $this->findDependentRowset('\Ppb\Db\Table\Bids', null, $select);
- }
- return $this->_bids;
- }
- /**
- *
- * clear bids rowset
- *
- * @return $this
- */
- public function clearBids()
- {
- $this->_bids = null;
- return $this;
- }
- /**
- *
- * get offers rowset
- *
- * @return \Ppb\Db\Table\Rowset\Offers
- */
- public function getOffers()
- {
- if (!$this->_offers instanceof Rowset\Offers) {
- $select = $this->getTable()->select()
- ->order(array('id DESC'));
- $this->_offers = $this->findDependentRowset('\Ppb\Db\Table\Offers', null, $select);
- }
- return $this->_offers;
- }
- /**
- *
- * clear offers rowset
- *
- * @return $this
- */
- public function clearOffers()
- {
- $this->_offers = null;
- return $this;
- }
- /**
- *
- * get sales listings table rowset
- *
- * @return \Ppb\Db\Table\Rowset\Sales
- */
- public function getSales()
- {
- if (!$this->_sales instanceof Rowset\Sales) {
- $service = new Service\Sales();
- $select = $service->getTable()->getAdapter()->select()
- ->from(array('l' => 'sales_listings'))
- ->joinLeft(array('s' => 'sales'), 'l.sale_id = s.id', 's.buyer_id')
- ->where('l.listing_id = ?', intval($this->getData('id')))
- ->where('s.pending = ?', 0)
- ->order(array('l.id DESC'));
- $this->_sales = $service->fetchAll($select);
- }
- return $this->_sales;
- }
- /**
- *
- * clear sales rowset
- *
- * @return $this
- */
- public function clearSales()
- {
- $this->_sales = null;
- return $this;
- }
- /**
- *
- * get bid increments table service
- *
- * @return \Ppb\Service\Table\BidIncrements
- */
- public function getBidIncrements()
- {
- if (!$this->_bidIncrements instanceof Service\Table\BidIncrements) {
- $this->setBidIncrements(
- new Service\Table\BidIncrements());
- }
- return $this->_bidIncrements;
- }
- /**
- *
- * set bid increments table service
- *
- * @param \Ppb\Service\Table\BidIncrements $bidIncrements
- *
- * @return $this
- */
- public function setBidIncrements(Service\Table\BidIncrements $bidIncrements)
- {
- $this->_bidIncrements = $bidIncrements;
- return $this;
- }
- /**
- *
- * get custom fields table service
- *
- * @return \Ppb\Service\CustomFields
- */
- public function getCustomFieldsService()
- {
- if (!$this->_customFields instanceof Service\CustomFields) {
- $this->setCustomFieldsService(
- new Service\CustomFields());
- }
- return $this->_customFields;
- }
- /**
- *
- * set custom fields table service
- *
- * @param \Ppb\Service\CustomFields $customFields
- *
- * @return $this
- */
- public function setCustomFieldsService(Service\CustomFields $customFields)
- {
- $this->_customFields = $customFields;
- return $this;
- }
- /**
- *
- * set sales listings table service
- *
- * @param \Ppb\Service\Table\SalesListings $salesListings
- *
- * @return $this
- */
- public function setSalesListings($salesListings)
- {
- $this->_salesListings = $salesListings;
- return $this;
- }
- /**
- * @return \Ppb\Service\Table\SalesListings
- */
- public function getSalesListings()
- {
- if (!$this->_salesListings instanceof Service\Table\SalesListings) {
- $this->setSalesListings(
- new Service\Table\SalesListings());
- }
- return $this->_salesListings;
- }
- /**
- *
- * get categories table service
- *
- * @return \Ppb\Service\Table\Relational\Categories
- */
- public function getCategories()
- {
- if (!$this->_categories instanceof Service\Table\Relational\Categories) {
- $this->setCategories(
- new Service\Table\Relational\Categories());
- }
- return $this->_categories;
- }
- /**
- *
- * set categories table service
- *
- * @param \Ppb\Service\Table\Relational\Categories $categories
- *
- * @return $this
- */
- public function setCategories(Service\Table\Relational\Categories $categories)
- {
- $this->_categories = $categories;
- return $this;
- }
- /**
- *
- * set sale id
- *
- * @param int $saleId
- *
- * @return $this
- */
- public function setSaleId($saleId)
- {
- $this->_saleId = $saleId;
- return $this;
- }
- /**
- *
- * get sale id
- *
- * @return int
- */
- public function getSaleId()
- {
- return $this->_saleId;
- }
- /**
- *
- * get custom fields data service
- *
- * @return \Ppb\Service\CustomFieldsData
- */
- public function getCustomFieldsData()
- {
- if (!$this->_customFieldsData instanceof Service\CustomFieldsData) {
- $this->setCustomFieldsData(
- new Service\CustomFieldsData());
- }
- return $this->_customFieldsData;
- }
- /**
- *
- * set custom fields data service
- *
- * @param \Ppb\Service\CustomFieldsData $customFieldsData
- *
- * @return $this
- */
- public function setCustomFieldsData(Service\CustomFieldsData $customFieldsData)
- {
- $this->_customFieldsData = $customFieldsData;
- return $this;
- }
- /**
- *
- * get closed flag
- *
- * @return bool
- */
- public function getClosedFlag()
- {
- return $this->_closedFlag;
- }
- /**
- *
- * retrieve the listing's selected media types or all if $types = null
- * if the listing has not been created yet, then use the $_data array,
- * otherwise get data from the listings_media table
- *
- * @param string|array|null $types
- *
- * @return array|\Ppb\Db\Table\Rowset\ListingsMedia
- */
- public function getMedia($types = null)
- {
- $media = array();
- if ($types !== null) {
- if (!is_array($types)) {
- $types = array($types);
- }
- }
- else {
- $types = Service\ListingsMedia::getTypes();
- }
- // if we are creating a listing, we will use the $_data array
- if (!$this->getData('id')) {
- foreach ($types as $type) {
- $files = $this->getData($type);
- if ($files !== null) {
- if (!is_array($files)) {
- $this->addData($type, \Ppb\Utility::unserialize($files));
- }
- $data = array_filter((array)$this->getData($type));
- foreach ($data as $value) {
- $media[] = array(
- 'id' => null,
- 'value' => $value,
- 'type' => $type,
- );
- }
- }
- }
- }
- else {
- /** @var \Ppb\Db\Table\Rowset\ListingsMedia $media */
- $media = $this->findDependentRowset('\Ppb\Db\Table\ListingsMedia', null,
- $this->getTable()->select()->where('type IN (?)', $types)
- ->order('order_id ASC'));
- }
- return $media;
- }
- /**
- *
- * retrieve the listing's main image
- *
- * @param bool $absolutePath if true, return the full path of the image
- *
- * @return string|null
- */
- public function getMainImage($absolutePath = false)
- {
- $images = $this->getMedia('image');
- $image = null;
- if (count($images) > 0) {
- $image = $images[0]['value'];
- if ($absolutePath === true) {
- if (!preg_match('#^http(s)?://(.*)+$#i', $image)) {
- $settings = $this->getSettings();
- $uploadsPath = $settings['site_path'] . \Ppb\Utility::URI_DELIMITER . \Ppb\Utility::getFolder('uploads');
- $image = $uploadsPath . \Ppb\Utility::URI_DELIMITER . $image;
- }
- }
- }
- return $image;
- }
- /**
- *
- * get listing status (open/closed/scheduled)
- *
- * @return string
- */
- public function getStatus()
- {
- if ($this->getData('start_time') > date('Y-m-d H:i:s', time())) {
- return self::SCHEDULED;
- }
- else if ($this->getData('closed')) {
- return self::CLOSED;
- }
- else {
- return self::OPEN;
- }
- }
- /**
- *
- * check if listing is active (and not a draft)
- *
- * @return bool
- */
- public function isActive()
- {
- if (
- $this->isDraft() === false &&
- $this->getData('active') == 1 &&
- $this->getData('approved') == 1 &&
- $this->getData('deleted') != 1
- ) {
- return true;
- }
- return false;
- }
- /**
- *
- * check if listing is open
- *
- * @return bool
- */
- public function isOpen()
- {
- return ($this->getStatus() == self::OPEN) ? true : false;
- }
- /**
- *
- * check if listing is scheduled
- *
- * @return bool
- */
- public function isScheduled()
- {
- return ($this->getStatus() == self::SCHEDULED) ? true : false;
- }
- /**
- *
- * check if listing is closed
- *
- * @return bool
- */
- public function isClosed()
- {
- return ($this->getStatus() == self::CLOSED) ? true : false;
- }
- /**
- *
- * generate the meta description of the listing.
- *
- * it will be something like: {title} in {category}
- *
- * @return string
- */
- public function getMetaDescription()
- {
- $breadcrumbs = implode(' > ', $this->getCategories()->getBreadcrumbs(
- $this->getData('category_id')));
- return sprintf('%s in %s', $this->getData('name'), $breadcrumbs);
- }
- /**
- *
- * get relist method
- *
- * @return string
- */
- public function getRelistMethod()
- {
- if (!$this->_relistMethod) {
- $settings = $this->getSettings();
- $this->setRelistMethod(
- (isset($settings['relist_method'])) ? $settings['relist_method'] : null);
- }
- return $this->_relistMethod;
- }
- /**
- *
- * set relist method
- *
- * @param string $relistMethod
- *
- * @return $this
- */
- public function setRelistMethod($relistMethod = null)
- {
- if (!in_array($relistMethod, $this->_relistMethods)) {
- $relistMethod = array_shift(
- array_values($this->_relistMethods));
- }
- $this->_relistMethod = $relistMethod;
- return $this;
- }
- /**
- *
- * get the available quantity for this listing
- * this is calculated based on the quantity field minus and pending sales that have the listing added
- * if quantity = -1 - we have an unlimited quantity of items for the listing
- *
- * @param int|null $quantity initial quantity
- * @param array|null $productAttributes the product attributes for which to check stock levels
- *
- * @return int|true true if we have unlimited quantity
- */
- public function getAvailableQuantity($quantity = null, $productAttributes = null)
- {
- $stockLevels = \Ppb\Utility::unserialize($this->getData('stock_levels'));
- $quantityAvailable = 0;
- if (!empty($stockLevels) && is_array($stockLevels) && $this->getData('listing_type') == 'product') {
- foreach ($stockLevels as $stockLevel) {
- if (is_array($stockLevel)) {
- if (array_key_exists('options', $stockLevel)) {
- $selected = \Ppb\Utility::unserialize($stockLevel['options']);
- if ($selected == $productAttributes) {
- $quantityAvailable = $stockLevel['quantity'];
- }
- }
- }
- }
- }
- else {
- $quantityAvailable = $this->getData('quantity');
- }
- if ($quantityAvailable == self::UNLIMITED_QUANTITY) {
- return true;
- }
- $settings = $this->getSettings();
- if ($settings['pending_sales_listings_expire_hours']) {
- $quantity += $quantityAvailable;
- $salesListings = $this->findDependentRowset('\Ppb\Db\Table\SalesListings');
- /** @var \Ppb\Db\Table\Row\SaleListing $saleListing */
- foreach ($salesListings as $saleListing) {
- $sale = $saleListing->findParentRow('\Ppb\Db\Table\Sales');
- if ($sale['pending']) {
- $quantity -= $saleListing['quantity'];
- }
- }
- }
- else {
- $quantity = $quantityAvailable;
- }
- return ($quantity > 0) ? $quantity : 0;
- }
- /**
- *
- * returns the time left in seconds or 0 for time left or if this doesnt apply for the listing
- *
- * @return int
- */
- public function getTimeLeft()
- {
- $timeLeft = strtotime($this->getData('end_time')) - time();
- return ($timeLeft > 0) ? $timeLeft : 0;
- }
- /**
- *
- * get the tax type but only if it applies to the selected buyer
- *
- * @param \Ppb\Db\Table\Row\User $buyer buyer user model
- * @param int $billingAddressId billing address id, needed for a sale model
- * @param string $country country override
- * @param string $state state override
- *
- * @return bool|\Ppb\Db\Table\Row\TaxType
- */
- public function getTaxType(User $buyer = null, $billingAddressId = null, $country = null, $state = null)
- {
- if (!$this->getData('apply_tax')) {
- $this->_taxType = false;
- }
- else if ($this->_taxType === null) {
- /** @var \Ppb\Db\Table\Row\User $seller */
- $seller = $this->findParentRow('\Ppb\Db\Table\Users');
- $taxType = false;
- if (($taxTypeId = $seller->canApplyTax()) != false) {
- $taxTypeService = new Service\Table\TaxTypes();
- $taxTypeModel = $taxTypeService->findBy('id', $taxTypeId);
- if ($taxTypeModel instanceof TaxType) {
- $locationsIds = \Ppb\Utility::unserialize($taxTypeModel->getData('locations_ids'));
- if ($country === null && $state === null) {
- if ($buyer === null) {
- $buyer = $this->getUser();
- }
- if ($buyer instanceof User) {
- $address = $buyer->getAddress($billingAddressId);
- if (isset($address['country']) && isset($address['state'])) {
- $country = $address['country'];
- $state = $address['state'];
- }
- }
- }
- if (in_array($country, $locationsIds) || in_array($state, $locationsIds)
- ) {
- $taxType = $taxTypeModel;
- }
- }
- }
- $this->_taxType = $taxType;
- }
- return $this->_taxType;
- }
- /**
- *
- * get listing owner
- *
- * @return \Ppb\Db\Table\Row\User|null
- */
- public function getOwner()
- {
- /** @var \Ppb\Db\Table\Row\User $owner */
- $owner = $this->findParentRow('\Ppb\Db\Table\Users');
- return $owner;
- }
- /**
- *
- * get the maximum bid from an array of bids
- *
- * @param bool $current if set to true, include start_price in the array,
- * will display the current bid or start price
- *
- * @return float
- */
- public function currentBid($current = false)
- {
- $amounts = array(0);
- if ($current === true) {
- $amounts[] = $this->getData('start_price');
- }
- $bids = $this->getBids();
- foreach ($bids as $bid) {
- $amounts[] = $bid['amount'];
- }
- return max($amounts);
- }
- /**
- *
- * get the latest bid for the selected user (normally for the logged in user)
- *
- * @param $userId
- *
- * @return \Ppb\Db\Table\Row\Bid|null
- */
- public function yourBid($userId)
- {
- $bids = $this->getBids();
- foreach ($bids as $bid) {
- if ($bid['user_id'] == $userId) {
- return $bid;
- }
- }
- return null;
- }
- /**
- *
- * return the minimum amount that can be bid on the listing
- *
- * @param bool $amount if set to true, it will calculate the minimum bid that will
- * need to be set, based on the amount input
- * the variable will always be higher than $bidAmount
- *
- * @return float
- */
- public function minimumBid($amount = null)
- {
- $settings = $this->getSettings();
- $bidAmount = $this->getData('start_price');
- $this->clearBids();
- if ($this->countDependentRowset('\Ppb\Db\Table\Bids') > 0) {
- $maximumBid = $this->currentBid();
- $bidIncrement = $this->getData('bid_increment');
- if ($bidIncrement > 0) {
- $bidAmount = $maximumBid + $bidIncrement;
- }
- else {
- $incrementsTable = $this->getBidIncrements()->getTable();
- $incrementAmount = $incrementsTable->fetchRow(
- $incrementsTable->select('amount')
- ->where('tier_from <= ?', $maximumBid)
- ->where('tier_to > ?', $maximumBid)
- );
- $bidAmount = $maximumBid + $incrementAmount['amount'];
- }
- }
- if ($amount !== null) {
- if ($amount < $bidAmount) {
- $amount = $bidAmount;
- }
- $reservePrice = $this->getData('reserve_price');
- if ($bidAmount < $reservePrice) {
- $bidAmount = min(array(
- $reservePrice, $amount));
- }
- if (!$settings['proxy_bidding']) {
- $bidAmount = $amount;
- }
- }
- return $bidAmount;
- }
- /**
- *
- * get payment methods available for the listing
- * convert old gateways field to new serialized field
- *
- * @param string $type type of payment methods to retrieve ('direct', 'offline' or null for all)
- *
- * @return array
- */
- public function getPaymentMethods($type = null)
- {
- $result = array();
- if (in_array($type, array(null, 'direct'))) {
- $paymentGatewaysService = new Service\Table\PaymentGateways();
- $directPayment = \Ppb\Utility::unserialize($this->getData('direct_payment'));
- if (!is_array($directPayment)) {
- $directPayment = @explode(',', $directPayment);
- if (is_array($directPayment)) {
- $this->save(array(
- 'direct_payment' => serialize($directPayment),
- ));
- }
- }
- $directPaymentMethods = array_filter((array)$directPayment);
- if (count($directPaymentMethods) > 0) {
- // check if the direct payment gateway is still enabled by the seller
- $userId = $this->getData('user_id');
- $rows = $paymentGatewaysService->getData($userId, $directPaymentMethods, true);
- foreach ($rows as $row) {
- $className = '\\Ppb\\Model\\PaymentGateway\\' . $row['name'];
- if (class_exists($className)) {
- /** @var \Ppb\Model\PaymentGateway\AbstractPaymentGateway $gatewayModel */
- $gatewayModel = new $className($userId);
- if ($gatewayModel->enabled()) {
- $result[] = array(
- 'id' => $row['id'],
- 'type' => 'direct',
- 'name' => $row['name'],
- 'logo' => $row['logo_path'],
- );
- }
- }
- }
- }
- }
- if (in_array($type, array(null, 'offline'))) {
- $offlinePaymentMethodsService = new Service\Table\OfflinePaymentMethods();
- $offlinePayment = \Ppb\Utility::unserialize($this->getData('offline_payment'));
- if (!is_array($offlinePayment)) {
- $offlinePayment = @explode(',', $offlinePayment);
- if (is_array($offlinePayment)) {
- $this->save(array(
- 'offline_payment' => serialize($offlinePayment),
- ));
- }
- }
- $offlinePaymentMethods = array_filter((array)$offlinePayment);
- if (count($offlinePaymentMethods) > 0) {
- $select = $offlinePaymentMethodsService->getTable()->select()
- ->where('id IN (?)', new Expr(implode(', ', $offlinePaymentMethods)));
- $rows = $offlinePaymentMethodsService->fetchAll($select, array('order_id ASC', 'name ASC'));
- foreach ($rows as $row) {
- $result[] = array(
- 'id' => $row['id'],
- 'type' => 'offline',
- 'name' => $row['name'],
- 'logo' => $row['logo'],
- );
- }
- }
- }
- return $result;
- }
- /**
- *
- * create an array of key => value from the custom fields and custom fields data tables
- * the custom fields data is available in the listing object when its created
- *
- * @param string $type
- *
- * @return array
- */
- public function getCustomFields($type = self::CUSTOM_FIELDS_TYPE)
- {
- $result = array();
- $categoryId = $this->getData('category_id');
- $addlCategoryId = $this->getData('addl_category_id');
- $categoriesFilter = array(0);
- if ($categoryId) {
- $categoriesFilter = array_merge($categoriesFilter, array_keys(
- $this->getCategories()->getBreadcrumbs($categoryId)));
- }
- if ($addlCategoryId) {
- $categoriesFilter = array_merge($categoriesFilter, array_keys(
- $this->getCategories()->getBreadcrumbs($addlCategoryId)));
- }
- $customFields = $this->getCustomFieldsService()->getFields(array(
- 'type' => $type,
- 'active' => 1,
- 'category_ids' => $categoriesFilter,
- ));
- $listingsService = new Service\Listings();
- $customFieldsData = $listingsService->getCustomFieldsData($this->getData('id'));
- foreach ($customFields as $row) {
- foreach ($row as $key => $value) {
- if (($array = @unserialize($value)) !== false) {
- $row[$key] = $array;
- }
- }
- $customFieldValuePost = $this->getData('custom_field_' . $row['id']);
- if (!empty($customFieldValuePost)) {
- $row['value'] = $customFieldValuePost;
- }
- else if (!empty($customFieldsData[$row['id']])) {
- $row['value'] = $customFieldsData[$row['id']];
- }
- else {
- $row['value'] = null;
- }
- $row['value'] = \Ppb\Utility::unserialize($row['value']);
- $rowDisplay = array();
- $isMultiOptions = false;
- if (!empty($row['multiOptions'])) {
- if (count(array_filter($row['multiOptions']['key']))) {
- $isMultiOptions = true;
- }
- }
- if ($isMultiOptions === true) {
- foreach ((array)$row['value'] as $customFldValue) {
- if (($customFldKey = array_search($customFldValue, $row['multiOptions']['key'])) !== false) {
- $customFldValue = trim($row['prefix'] . ' ' . $row['multiOptions']['value'][$customFldKey] . ' ' . $row['suffix']);
- }
- if (!empty($customFldValue)) {
- $rowDisplay[] = $customFldValue;
- }
- }
- }
- else if (is_string($row['value'])) {
- $rowDisplay[] = trim($row['prefix'] . ' ' . $row['value'] . ' ' . $row['suffix']);
- }
- $row['display'] = $rowDisplay;
- $result[] = $row;
- }
- return $result;
- }
- /**
- *
- * returns an array used by the url view helper to generate the listing details page uri
- *
- * @return array
- */
- public function link()
- {
- return array(
- 'module' => 'listings',
- 'controller' => 'listing',
- 'action' => 'details',
- 'id' => $this->getData('id'),
- 'name' => $this->getData('name')
- );
- }
- /**
- *
- * check if the listing exists
- * should return false if it doesnt exist or if it is marked deleted and a user other
- * than its owner access the method, or if its inactive
- *
- * @param bool $extended if set to true, it will return false if the item is marked deleted or inactive
- * otherwise it will return false only if the item doesnt exist in the database
- *
- * @return bool
- */
- public function exists($extended = true)
- {
- $listing = $this->getData();
- if (isset($listing['id'])) {
- unset($listing['id']);
- }
- if (empty($listing)) {
- return false;
- }
- if ($extended) {
- $user = $this->getUser();
- $userId = (isset($user['id'])) ? $user['id'] : null;
- if ($userId !== $listing['user_id'] &&
- ($listing['deleted'] || ($listing['active'] != 1) || !$listing['approved'])
- ) {
- return false;
- }
- }
- return true;
- }
- /**
- *
- * check if the listing exists and if the logged in / specified user is the owner of the listing
- *
- * @param \Ppb\Db\Table\Row\User $user
- *
- * @return bool
- */
- public function isOwner(User $user = null)
- {
- $exists = $this->exists(false);
- if ($exists === true) {
- if ($user === null) {
- $user = $this->getUser();
- }
- $userId = (isset($user['id'])) ? $user['id'] : null;
- if ($userId !== $this->getData('user_id')) {
- return false;
- }
- }
- return $exists;
- }
- /**
- *
- * check if the listing exists and if the logged in / specified user has added it to the watch list
- *
- * @param \Ppb\Db\Table\Row\User $user
- *
- * @return bool
- */
- public function isWatched(User $user = null)
- {
- $exists = $this->exists(false);
- if ($exists === true) {
- if ($user === null) {
- $user = $this->getUser();
- }
- $bootstrap = Front::getInstance()->getBootstrap();
- $session = $bootstrap->getResource('session');
- $userToken = strval($session->getCookie(User::USER_TOKEN));
- $userId = (isset($user['id'])) ? $user['id'] : null;
- $select = $this->getTable()->select();
- if ($userId !== null) {
- $select->where('user_token = "' . $userToken . '" OR user_id = "' . $userId . '"');
- }
- else {
- $select->where('user_token = ?', $userToken);
- }
- $countWatched = $this->countDependentRowset('\Ppb\Db\Table\ListingsWatch', null, $select);
- if ($countWatched) {
- return true;
- }
- }
- return false;
- }
- /**
- *
- * get the number of users watching the listing
- *
- * @return int
- */
- public function countWatchers()
- {
- return $this->countDependentRowset('\Ppb\Db\Table\ListingsWatch');
- }
- /**
- *
- * count the total number of bids and offers the logged in user has posted on the listing
- *
- * @return int
- */
- public function getNbBidsOffers()
- {
- $user = $this->getUser();
- $select = $this->getTable()->select()
- ->where('user_id = ?', $user['id'])
- ->group('maximum_bid');
- $nbBids = $this->countDependentRowset('\Ppb\Db\Table\Bids', null, $select);
- $select = $this->getTable()->select()
- ->where('user_id = ?', $user['id']);
- $nbOffers = $this->countDependentRowset('\Ppb\Db\Table\Offers', null, $select);
- return ($nbBids + $nbOffers);
- }
- /**
- *
- * check if a purchase action can be validated, return true if valid or
- * an error message string otherwise
- *
- * @param string $type the type of purchase action requested to be validated (bid, buy, offer)
- *
- * @return bool|string
- */
- public function canPurchase($type = 'bid')
- {
- $user = $this->getUser();
- $translate = $this->getTranslate();
- if (empty($user)) {
- return $translate->_('Please log in to access the purchase confirmation page.');
- }
- else if ($user['id'] == $this->getData('user_id')) {
- return $translate->_('You are the owner of the listing.');
- }
- else if (!$this->isActiveAndOpen()) {
- return $translate->_('The listing is closed.');
- }
- else if (($blockMessage = $this->_isBlockedUserPurchasing()) !== false) {
- return $blockMessage;
- }
- $limitBidsOffersPerUser = $this->findParentRow('\Ppb\Db\Table\Users')
- ->getGlobalSettings('limit_bids_per_user');
- switch ($type) {
- case 'bid':
- if ($this->getData('listing_type') == 'product') {
- return $translate->_('This listing is a product. No bids can be placed.');
- }
- else if ($limitBidsOffersPerUser > 0) {
- if ($this->getNbBidsOffers() >= $limitBidsOffersPerUser) {
- $sentence = $translate->_('You have reached the maximum allowed number of bids/offers per user (%s).');
- return sprintf($sentence, $limitBidsOffersPerUser);
- }
- }
- break;
- case 'buy':
- if ($this->isBuyOut() === false) {
- return $translate->_('Buy Out is disabled for this listing.');
- }
- else if ($this->canAddToCart() === true) {
- return $translate->_('The product can only be purchased through a shopping cart.');
- }
- break;
- case 'offer':
- if ($this->isMakeOffer() === false) {
- return $translate->_('Make Offer is disabled for this listing.');
- }
- else if ($limitBidsOffersPerUser > 0) {
- if ($this->getNbBidsOffers() >= $limitBidsOffersPerUser) {
- $sentence = $translate->_('You have reached the maximum allowed number of bids/offers per user (%s).');
- return sprintf($sentence, $limitBidsOffersPerUser);
- }
- }
- break;
- }
- return true;
- }
- /**
- *
- * check if the listing can be added to the shopping cart
- * or return an error message otherwise
- *
- * the owner can add his own products to the shopping cart, but check out is not allowed.
- *
- * @param int|null $quantity if quantity is set, check by quantity as well
- * @param array|null $productAttributes the product attributes for which to check stock levels
- *
- * @return bool|string
- */
- public function canAddToCart($quantity = null, $productAttributes = null)
- {
- $settings = $this->getSettings();
- $translate = $this->getTranslate();
- if (!$settings['enable_shopping_cart']) {
- return $translate->_('The shopping cart module is disabled.');
- }
- else if ($this->getData('listing_type') != 'product') {
- return $translate->_('Only products can be added to a shopping cart.');
- }
- else if (!$this->isActiveAndOpen()) {
- return $translate->_('The listing is closed.');
- }
- else if (($blockMessage = $this->_isBlockedUserPurchasing()) !== false) {
- return $blockMessage;
- }
- else {
- /** @var \Ppb\Db\Table\Row\User $seller */
- $seller = $this->findParentRow('\Ppb\Db\Table\Users');
- switch ($settings['shopping_cart_applies']) {
- case 'store_owners':
- if (!$seller->storeStatus(true)) {
- return $translate->_("The seller's store is disabled.");
- }
- break;
- case 'store_listings':
- if (!$seller->storeStatus(true) || $this->getData('list_in') == 'site') {
- return $translate->_('Only products listed in store can be added to the shopping cart.');
- }
- break;
- }
- if ($quantity !== null) {
- $availableQuantity = $this->getAvailableQuantity(null, $productAttributes);
- if ($availableQuantity < $quantity) {
- return $translate->_('Cannot add to cart - not enough quantity available.');
- }
- }
- }
- return true;
- }
- /**
- *
- * check if a pending offer can be accepted on a listing
- *
- * @param int $quantity
- * @param array|null $productAttributes the product attributes for which to check stock levels
- *
- * @return bool
- */
- public function canAcceptOffer($quantity = 1, $productAttributes = null)
- {
- if ($this->isMakeOffer()) {
- $availableQuantity = $this->getAvailableQuantity(null, $productAttributes);
- if ($quantity <= $availableQuantity) {
- return true;
- }
- }
- return false;
- }
- /**
- *
- * drafts can be edited always
- * @7.10 scheduled listings can always be edited, just like drafts
- *
- * active open listings can be edited, if:
- * - for auctions:
- * no activity (bids, offers, sale).
- * the remaining duration needs to be greater than the minimum limit set in admin
- * - for products:
- * there can be activity, but in this case only selected fields can be edited
- *
- * @return bool
- */
- public function canEdit()
- {
- if ($this->isDraft() || $this->isScheduled()) {
- return true;
- }
- $startTime = strtotime($this->getData('start_time'));
- if (!$this->isClosed() && $this->getData('active')) {
- // auctions ending time limit setting
- if ($this->isOpen() && $this->getData('listing_type') == 'auction') {
- $settings = $this->getSettings();
- // the below snippet is needed for when checking if the listing can be edited right on the Listing\Create action
- $endTime = strtotime($this->getData('end_time'));
- if (!$endTime) {
- $endTime = $startTime + $this->getData('duration') * 86400;
- }
- $timeRemaining = $endTime - time();
- if ($timeRemaining < $settings['auctions_editing_hours'] * 3600) {
- return false;
- }
- }
- return ($this->getData('listing_type') == 'product') ? true : !$this->hasActivity();
- }
- return false;
- }
- /**
- *
- * check if the poster can close an open listing
- * products can be closed at any time, while auctions can be closed depending on the
- * close auction before end time setting
- *
- * @return bool
- */
- public function canClose()
- {
- if ($this->isOpen()) {
- $settings = $this->getSettings();
- if ($this->getData('listing_type') == 'auction') {
- if ($settings['close_auctions_end_time'] || !$this->hasActivity()) {
- return true;
- }
- }
- else {
- return true;
- }
- }
- return false;
- }
- /**
- *
- * a listing can be deleted if not scheduled/closed or for auctions if there is no activity
- *
- * @return bool
- */
- public function canDelete()
- {
- if ($this->isOpen()) {
- if ($this->getData('listing_type') == 'auction' && $this->hasActivity()) {
- return false;
- }
- }
- return true;
- }
- /**
- *
- * check if there was any activity on the listing (bidding, offers, purchasing)
- *
- * @return bool
- */
- public function hasActivity()
- {
- return (count($this->getBids()) ||
- count($this->getOffers()) ||
- count($this->getSales())
- ) ? true : false;
- }
- /**
- *
- * check if buy out is enabled for the listing
- *
- * @return bool
- */
- public function isBuyOut()
- {
- if ($this->getData('listing_type') == 'product') {
- return true;
- }
- else {
- $buyoutPrice = $this->getData('buyout_price');
- if ($buyoutPrice > 0) {
- $settings = $this->getSettings();
- if ($settings['enable_buyout']) {
- $maximumBid = $this->currentBid();
- if ($settings['always_show_buyout'] && $maximumBid < $buyoutPrice) {
- return true;
- }
- else if (!$this->countDependentRowset('\Ppb\Db\Table\Bids') || $maximumBid < $this->getData('reserve_price')) {
- return true;
- }
- }
- }
- }
- return false;
- }
- /**
- *
- * check if the make offer feature is enabled for the listing
- *
- * @return bool
- */
- public function isMakeOffer()
- {
- $settings = $this->getSettings();
- if ($settings['enable_make_offer'] && $this->getData('enable_make_offer')) {
- return true;
- }
- return false;
- }
- /**
- *
- * check if the listing is active and open
- *
- * @return bool
- */
- public function isActiveAndOpen()
- {
- if ($this->isActive() && $this->isOpen()) {
- return true;
- }
- return false;
- }
- /**
- *
- * determine and set the 'approved' flag for the listing
- *
- * @param int|null $value
- *
- * @return $this
- */
- public function updateApproved($value = null)
- {
- $settings = $this->getSettings();
- if ($value !== null) {
- $data['approved'] = $value;
- }
- else {
- if ($settings['enable_listings_approval']) {
- $data['approved'] = 0;
- $mail = new \Admin\Model\Mail\Admin();
- $mail->listingApproval($this)->send();
- }
- else {
- $data['approved'] = 1;
- }
- }
- $this->save($data);
- return $this;
- }
- /**
- *
- * update listing quantity field
- *
- * @param int $quantity
- * @param mixed $productAttributes
- * @param string $operation
- *
- * @return int remaining quantity (for stock levels it will return -1)
- */
- public function updateQuantity($quantity, $productAttributes = null, $operation = self::SUBTRACT)
- {
- $stockLevels = \Ppb\Utility::unserialize($this->getData('stock_levels'));
- $productAttributes = \Ppb\Utility::unserialize($productAttributes);
- $quantityRemaining = self::UNLIMITED_QUANTITY;
- if (!empty($stockLevels) && $this->getData('listing_type') == 'product') {
- foreach ($stockLevels as $key => $stockLevel) {
- if ($stockLevel['options'] == $productAttributes) {
- $quantityAvailable = $stockLevel['quantity'];
- if ($quantityAvailable != self::UNLIMITED_QUANTITY) {
- $stockLevels[$key]['quantity']
- = $this->_quantityOperator($quantityAvailable, $quantity, $operation);
- }
- }
- }
- $this->save(array(
- 'stock_levels' => serialize($stockLevels),
- ));
- }
- else {
- $quantityAvailable = $this->getData('quantity');
- if ($quantityAvailable != self::UNLIMITED_QUANTITY) {
- $quantityRemaining
- = $this->_quantityOperator($quantityAvailable, $quantity, $operation);
- $this->save(array(
- 'quantity' => $quantityRemaining,
- ));
- }
- }
- return $quantityRemaining;
- }
- /**
- *
- * apply operations to the quantity field
- *
- * @param int $quantityAvailable
- * @param int $quantity
- * @param string $operation
- *
- * @return int
- */
- protected function _quantityOperator($quantityAvailable, $quantity, $operation = self::ADD)
- {
- if ($quantityAvailable != self::UNLIMITED_QUANTITY) {
- return ($operation == self::ADD) ?
- ($quantityAvailable + $quantity) : ($quantityAvailable - $quantity);
- }
- return self::UNLIMITED_QUANTITY;
- }
- /**
- *
- * determine and set the 'active' flag for the listing
- *
- * @param int $active
- *
- * @return $this
- */
- public function updateActive($active = 1)
- {
- $this->save(array(
- 'active' => (int)$active,
- ));
- return $this;
- }
- /**
- *
- * close a listing
- * when the cron closes the listing, the end time will not be modified.
- *
- * @param bool $automatic whether the listing is closed automatically (by the cron job) or not
- *
- * @return $this
- */
- public function close($automatic = false)
- {
- $data = array();
- if (strtotime($this->getData('start_time')) < time() && $this->getData('closed') == 0) {
- $data['closed'] = 1;
- if ($automatic === false) {
- $data['end_time'] = new Expr('now()');
- }
- $this->save($data);
- $this->_closedFlag = true;
- }
- return $this;
- }
- /**
- *
- * relist a listing
- *
- * - the quantity field is calculated from the quantity sum in the sales listings table
- * and the listing quantity field
- *
- * @7.9 if duration === null => custom end time; else duration - if 0 = unlimited
- *
- * @param bool $autoRelist auto relist flag
- *
- * @return int the id of the new listing
- */
- public function relist($autoRelist = false)
- {
- // calculate quantity
- $service = $this->getSalesListings();
- if (!$this->getData('stock_levels')) {
- $quantity = $this->getTable()->getAdapter()->fetchOne(
- $service->getTable()->select(array('Qty' => new Expr('sum(quantity)')))
- ->where('listing_id = ?', $this->getData('id'))
- );
- $quantity += $this->getData('quantity');
- }
- else {
- $quantity = 0;
- }
- // by default all relisted listings are activated [admin default]
- // for the front end, we will run the method that will charge setup fees and set the approval flag
- $params = array(
- 'start_time_type' => 0,
- 'closed' => 0,
- 'is_relisted' => 1,
- 'end_time_type' => 0,
- 'active' => 1,
- 'approved' => 1,
- 'deleted' => 0,
- 'nb_clicks' => 0,
- 'draft' => 0,
- 'counted_at' => new Expr('null'),
- 'last_count_operation' => self::COUNT_OP_NONE,
- 'quantity' => $quantity,
- );
- $duration = $this->getData('duration');
- if ($duration === null) {
- $difference = strtotime($this->getData('end_time')) - strtotime($this->getData('start_time'));
- $params['end_time_type'] = 1;
- $params['end_time'] = date('Y-m-d H:i:s',
- time() + (($difference < 86400) ? 86400 : $difference)); // minimum 1 day duration
- }
- else {
- $endTime = ($duration > 0) ? time() + $duration * 86400 : null;
- $params['end_time'] = ($endTime) ? date('Y-m-d H:i:s', $endTime) : new Expr('null');
- }
- if ($autoRelist !== false) {
- $nbRelists = $this->getData('nb_relists') - 1;
- if ($nbRelists > 0) {
- $params['nb_relists'] = $nbRelists;
- }
- else {
- $params['nb_relists'] = 0;
- $params['auto_relist_sold'] = 0;
- }
- }
- $listingsService = new Service\Listings();
- $listingsService->setUser(
- $this->findParentRow('\Ppb\Db\Table\Users'));
- $listingId = null;
- switch ($this->getRelistMethod()) {
- case 'new':
- $listing = $listingsService->findBy('id', $this->getData('id'), true, true);
- $data = $listing->getData();
- unset($data['id']);
- if (!$autoRelist || !$this->getData('auto_relist_sold')) {
- $this->delete();
- }
- // need to also copy custom fields and
- $listingId = $listingsService->save(array_merge($data, $params));
- break;
- case 'same':
- $params['start_time'] = date('Y-m-d H:i:s', time());
- $tableColumns = $this->getTable()->info(Table\AbstractTable::COLS);
- $params = array_intersect_key($params, array_flip(array_values($tableColumns)));
- $this->save($params);
- // delete all data from the bids, offers and sales_listings tables
- $dependentTables = array(
- '\Ppb\Db\Table\Bids',
- '\Ppb\Db\Table\Offers',
- );
- if (!$autoRelist || !$this->getData('auto_relist_sold')) {
- $dependentTables[] = '\Ppb\Db\Table\SalesListings';
- }
- foreach ($dependentTables as $dependentTable) {
- $rowset = $this->findDependentRowset($dependentTable);
- /** @var \Cube\Db\Table\Row $row */
- foreach ($rowset as $row) {
- $row->delete();
- }
- }
- $listingId = $this->getData('id');
- break;
- }
- return $listingId;
- }
- /**
- *
- * processes post listing setup actions:
- * charges listing setup fees in account mode
- * activates and approves listing based on different settings
- *
- * @param \Ppb\Db\Table\Row\Listing $savedListing in case we edit a listing
- *
- * @return string returns any related output messages
- */
- public function processPostSetupActions(Listing $savedListing = null)
- {
- /** @var \Cube\View $view */
- $view = Front::getInstance()->getBootstrap()->getResource('view');
- $message = null;
- /** @var \Ppb\Db\Table\Row\User $user */
- $user = $this->findParentRow('\Ppb\Db\Table\Users');
- $listingSetupService = new Service\Fees\ListingSetup(
- $this, $user);
- // apply listing setup voucher if available
- if ($voucherCode = $this->getData('voucher_code')) {
- $listingSetupService->setVoucher($voucherCode);
- $voucher = $listingSetupService->getVoucher();
- if ($voucher instanceof Voucher) {
- $voucher->updateUses();
- }
- }
- if ($savedListing instanceof Listing) {
- $listingSetupService->setSavedListing($savedListing);
- }
- $listingFees = $listingSetupService->calculate();
- // apply listing setup fee
- $totalAmount = $listingSetupService->getTotalAmount();
- $userPaymentMode = $user->userPaymentMode();
- if ($totalAmount > 0 && $userPaymentMode == 'live') {
- $this->updateActive(0);
- }
- else {
- $this->updateActive();
- if ($totalAmount > 0) {
- $user->updateBalance($totalAmount);
- $accountingService = new Service\Accounting();
- $accountingService->setListingId($this->getData('id'))
- ->setUserId($user['id'])
- ->saveMultiple($listingFees);
- if ($view->isHelper('amount')) {
- $translate = $this->getTranslate();
- $sentence = $translate->_('The amount of %s has been debited from your account balance.');
- $message = sprintf($sentence, $view->amount($totalAmount));
- }
- }
- }
- $this->updateApproved();
- return $message;
- }
- /**
- *
- * process category counter for the listing object
- * will work based on the counted_at and last_count_operation flags and will be called by the cron job
- *
- * @param bool|string $force force count
- *
- * @return bool true if counted, false if unmodified
- */
- public function processCategoryCounter($force = false)
- {
- $counted = false;
- $operation = self::COUNT_OP_ADD;
- if ($force === false) {
- if ($this->isActiveAndOpen()) {
- $operation = self::COUNT_OP_ADD;
- }
- else if ($this->getData('list_in') != 'store') {
- $operation = self::COUNT_OP_SUBTRACT;
- }
- }
- $lastCountOperation = $this->getData('last_count_operation');
- if (
- $force === true ||
- ($operation == self::COUNT_OP_ADD && $lastCountOperation != self::COUNT_OP_ADD) ||
- ($operation == self::COUNT_OP_SUBTRACT && $lastCountOperation == self::COUNT_OP_ADD)
- ) {
- $this->countCategoriesCounter($operation);
- $counted = true;
- }
- $this->save(array(
- 'last_count_operation' => $operation,
- 'counted_at' => date('Y-m-d H:i:s', time()),
- ));
- return $counted;
- }
- /**
- *
- * do the requested category counting operation
- * 7.2 - added a fix which was causing this method to loop indefinitely if a category that was to be counted didn't exist
- * 7.7 - only count the additional category if the setting is enabled in admin
- *
- * @param $operation
- *
- * @return $this
- */
- public function countCategoriesCounter($operation)
- {
- $settings = $this->getSettings();
- $ids = array(
- $this->getData('category_id'),
- );
- if ($settings['addl_category_listing']) {
- $ids[] = $this->getData('addl_category_id');
- }
- foreach ($ids as $id) {
- if ($id > 0) {
- do {
- /** @var \Ppb\Db\Table\Row\Category $category */
- $category = $this->getCategories()->findBy('id', $id);
- $id = 0;
- if (count($category) > 0) {
- if ($operation == self::COUNT_OP_SUBTRACT) {
- $category->subtractCounter($this->getData('listing_type'));
- }
- else if ($operation == self::COUNT_OP_ADD) {
- $category->addCounter($this->getData('listing_type'));
- }
- $id = $category['parent_id'];
- }
- } while ($id > 0);
- }
- }
- return $this;
- }
- /**
- *
- * The method will post a bid, an offer or create a sale if the buy out method is used
- *
- * @param array $data place bid related data
- * @param string $type the type of purchase action (bid|buy|offer)
- * @param int $userId force user id
- *
- * @return string return message that is to be output
- */
- public function placeBid(array $data, $type = 'bid', $userId = null)
- {
- $bootstrap = Front::getInstance()->getBootstrap();
- if ($userId === null) {
- $user = $bootstrap->getResource('user');
- $userId = $user['id'];
- }
- $view = $bootstrap->getResource('view');
- $translate = $this->getTranslate();
- $message = null;
- switch ($type) {
- case 'bid':
- $service = new Service\Bids();
- $data['user_id'] = $userId;
- $data['listing_id'] = $this->getData('id');
- $service->save($data);
- $message = $service->getMessage();
- $usersService = new Service\Users();
- $user = $usersService->findBy('id', $userId);
- $mail = new \Listings\Model\Mail\OwnerNotification();
- $mail->newBid($this->getData('id'), $user, $data['amount'])->send();
- break;
- case 'buy':
- $service = new Service\Sales();
- $quantity = 1;
- if (isset($data['quantity'])) {
- $quantity = ($data['quantity'] > 0) ? $data['quantity'] : $quantity;
- }
- $price = $this->getProductPrice($data['product_attributes']);
- $data = array(
- 'buyer_id' => $userId,
- 'seller_id' => $this->getData('user_id'),
- 'postage_id' => (int)$data['postage_id'],
- 'shipping_address_id' => $data['shipping_address_id'],
- 'apply_insurance' => (bool)$data['apply_insurance'],
- 'voucher_details' => $data['voucher_details'],
- 'listings' => array(
- array(
- 'listing_id' => $this->getData('id'),
- 'price' => $price,
- 'quantity' => $quantity,
- 'product_attributes' => $data['product_attributes'],
- )
- ),
- );
- $service->save($data);
- $message = sprintf($translate->_('You have successfully purchased this item - price: %s, quantity purchased: %s.'),
- $view->amount($price, $this->getData('currency')), $quantity);
- $this->setSaleId(
- $service->getSaleId());
- break;
- case 'offer':
- $service = new Service\Offers();
- $data['user_id'] = $userId;
- $data['type'] = 'offer';
- if (empty($data['receiver_id'])) {
- $data['receiver_id'] = $this->getData('user_id');
- }
- $data['listing_id'] = $this->getData('id');
- $quantity = 1;
- if (isset($data['quantity'])) {
- $quantity = ($data['quantity'] > 0) ? $data['quantity'] : $quantity;
- }
- $data['quantity'] = $quantity;
- $id = $service->save($data);
- $message = sprintf($translate->_('Your offer, in the amount of %s, has been posted successfully.'),
- $view->amount($data['amount'], $this->getData('currency')));
- /** @var \Ppb\Db\Table\Row\Offer $row */
- $row = $service->findBy('id', $id);
- $mail = new \Listings\Model\Mail\UserNotification();
- $mail->newOffer($this, $row)->send();
- break;
- }
- return $message;
- }
- /**
- *
- * get the price of the product based on the selected product attributes and their price variations
- *
- * @param mixed $productAttributes
- *
- * @return float
- */
- public function getProductPrice($productAttributes = null)
- {
- $price = $this->getData('buyout_price');
- $stockLevels = \Ppb\Utility::unserialize($this->getData('stock_levels'));
- $productAttributes = \Ppb\Utility::unserialize($productAttributes);
- if (!empty($stockLevels) && $this->getData('listing_type') == 'product') {
- foreach ($stockLevels as $key => $stockLevel) {
- if ($stockLevel['options'] == $productAttributes) {
- $price += $stockLevel['price'];
- break;
- }
- }
- }
- return $price;
- }
- /**
- *
- * this method assigns a set bid as a winning bid on a listing, or it assigns it automatically when:
- * - we have a standard auction with a high bid greater or equal to the reserve price
- * (fixed in V7.3, before it required the bid amount to be greater than the reserve)
- * - we have a first bidder auction (later)
- *
- * @param \Ppb\Db\Table\Row\Bid $bid the bid that should be assigned as winning bid, or null if the bid should be selected automatically
- *
- * @throws \RuntimeException
- * @return int|false the id of the resulted sale or false if no winner has been assigned
- */
- public function assignWinner(Bid $bid = null)
- {
- if ($bid !== null) {
- if ($bid['listing_id'] !== $this->getData('id')) {
- throw new \RuntimeException("The listing id of the bid object inserted in the assignWinner() method is invalid");
- }
- }
- else {
- $this->clearBids();
- $bids = $this->getBids();
- if (count($bids) > 0) {
- switch ($this->getData('listing_type')) {
- case 'auction':
- $highBid = $bids[0];
- if ($highBid['amount'] >= $this->getData('reserve_price')) {
- $bid = $highBid;
- }
- break;
- }
- }
- }
- if ($bid instanceof Bid) {
- $service = new Service\Sales();
- $data = array(
- 'buyer_id' => $bid['user_id'],
- 'seller_id' => $this->getData('user_id'),
- 'listings' => array(
- array(
- 'listing_id' => $this->getData('id'),
- 'price' => $bid['amount'],
- 'quantity' => 1,
- )
- ),
- );
- $service->save($data);
- $this->setSaleId(
- $service->getSaleId());
- return $this->getSaleId();
- }
- return false;
- }
- /**
- *
- * add a new click to the listing
- *
- * @7.9: do not purge the queries cache - as such do not use the save() method
- *
- * @return $this
- */
- public function addClick()
- {
- $nbClicks = $this->_data['nb_clicks'];
- $table = $this->getTable();
- $tableName = $table->getPrefix() . $table->getName();
- $table->getAdapter()->update($tableName, array(
- 'nb_clicks' => new Expr('nb_clicks + 1')
- ), $this->_getWhereQuery());
- $this->_data['nb_clicks'] = $nbClicks + 1;
- return $this;
- }
- /**
- *
- * add the product to the shopping cart
- * first we check if a shopping cart is active and if it is, add the item to the existing shopping cart
- *
- * the user id, insurance and postage are only saved when updating the shopping cart or checking out
- *
- * carts need to match the exact same fields like when combining invoices:
- *
- * @param int|null $quantity
- * @param array|null $productAttributes
- *
- * @return $this
- */
- public function addToCart($quantity = null, $productAttributes = null)
- {
- $quantity = ($quantity < 1) ? 1 : $quantity;
- $bootstrap = Front::getInstance()->getBootstrap();
- $session = $bootstrap->getResource('session');
- $userToken = strval($session->getCookie(User::USER_TOKEN));
- $salesService = new Service\Sales();
- $select = $salesService->getTable()->select()
- ->where('user_token = ?', $userToken)
- ->where('seller_id = ?', $this->getData('user_id'))
- ->where('sale_data REGEXP \'"currency";s:[[:digit:]]+:"' . $this->getData('currency') . '"\'')
- ->where('sale_data REGEXP \'"country";s:[[:digit:]]+:"' . $this->getData('country') . '"\'')
- ->where('sale_data REGEXP \'"state";s:[[:digit:]]+:"' . $this->getData('state') . '"\'')
- ->where('sale_data REGEXP \'"address";s:[[:digit:]]+:"' . $this->getData('address') . '"\'')
- ->where('sale_data REGEXP \'"apply_tax";s:[[:digit:]]+:"' . $this->getData('apply_tax') . '"\'')
- ->where('pending = ?', 1);
- if ($pickupOptions = $this->getData('pickup_options')) {
- $select->where('sale_data REGEXP \'"pickup_options";s:[[:digit:]]+:"' . $pickupOptions . '"\'');
- }
- else {
- $select->where('sale_data REGEXP \'"pickup_options";N\'');
- }
- $row = $salesService->fetchAll($select)->getRow(0);
- $data = array(
- 'user_token' => $userToken,
- 'seller_id' => $this->getData('user_id'),
- 'quantity' => (int)$quantity,
- 'pending' => 1,
- 'listings' => array(
- array(
- 'listing_id' => $this->getData('id'),
- 'price' => $this->getProductPrice($productAttributes),
- 'quantity' => (int)$quantity,
- 'product_attributes' => (count($productAttributes) > 0) ? serialize($productAttributes) : null,
- )
- ),
- );
- if ($row !== null) {
- $data['id'] = $row->getData('id');
- }
- $salesService->save($data);
- return $this;
- }
- /**
- *
- * save the recently viewed listing in the table
- *
- * @return $this
- */
- public function addRecentlyViewedListing()
- {
- $settings = $this->getSettings();
- if ($settings['enable_recently_viewed_listings']) {
- $bootstrap = Front::getInstance()->getBootstrap();
- $session = $bootstrap->getResource('session');
- $user = $this->getUser();
- $userId = (isset($user['id'])) ? $user['id'] : null;
- $userToken = strval($session->getCookie(User::USER_TOKEN));
- if (!empty($userToken)) {
- $recentlyViewedListingsService = new Service\RecentlyViewedListings();
- $select = $recentlyViewedListingsService->getTable()->select()
- ->where('user_token = ?', $userToken)
- ->where('listing_id = ?', $this->getData('id'));
- $row = $recentlyViewedListingsService->fetchAll($select)->getRow(0);
- $data = array(
- 'user_token' => $userToken,
- 'listing_id' => $this->getData('id'),
- 'user_id' => $userId,
- );
- if ($row !== null) {
- $data['id'] = $row->getData('id');
- }
- $recentlyViewedListingsService->save($data);
- }
- }
- return $this;
- }
- /**
- *
- * save the updated row in the table
- * add updated_at flag unless already set and if not saving the whole data array
- *
- * 7.7 - whenever we use the save method on a listing, if the listing was active and open
- * before the save operation, we basically remove the counter and ask the cron to count the item again
- * (code duplicated from the listings service save() method)
- *
- * @param array $data partial data to be saved
- * the complete row is saved if this parameter is null
- *
- * @return $this
- */
- public function save(array $data = null)
- {
- if ($data === null) {
- $data = $this->_data;
- }
- if (!array_key_exists('updated_at', $data)) {
- $data['updated_at'] = date('Y-m-d H:i:s', time());
- }
- if (!array_key_exists('last_count_operation', $data)) {
- if (
- $this->isActiveAndOpen() &&
- $this->getData('list_in') != 'store' &&
- $this->getdata('last_count_operation') == self::COUNT_OP_ADD
- ) {
- $this->countCategoriesCounter(self::COUNT_OP_SUBTRACT);
- $data['last_count_operation'] = self::COUNT_OP_NONE;
- }
- }
- parent::save($data);
- return $this;
- }
- /**
- *
- * delete or mark a listing as deleted
- * drafts are also deleted directly, rather than just being marked deleted
- * also subtract the listing from the category counters if it wasn't already subtracted
- *
- * 7.7 - for a pending cart, delete related rows from the sales listings table
- *
- * @param bool $admin if true - admin delete (delete all related files as well)
- *
- * @return bool|integer returns true if marked deleted, or number of affected rows if using admin delete
- */
- public function delete($admin = false)
- {
- $this->addData('deleted', 1)
- ->processCategoryCounter();
- $salesListings = $this->findDependentRowset('\Ppb\Db\Table\SalesListings');
- /** @var \Ppb\Db\Table\Row\SaleListing $saleListing */
- foreach ($salesListings as $saleListing) {
- $sale = $saleListing->findParentRow('\Ppb\Db\Table\Sales');
- if ($sale['pending']) {
- $saleListing->delete();
- }
- }
- if ($admin === true || $this->getData('draft')) {
- $this->findDependentRowset('\Ppb\Db\Table\ListingsMedia')
- ->delete();
- $this->getCustomFieldsData()
- ->delete(self::CUSTOM_FIELDS_TYPE, $this->getData('id'));
- return parent::delete();
- }
- else {
- $user = $this->getUser();
- if (isset($user['id'])) {
- $this->save(array(
- 'deleted' => 1,
- ));
- return true;
- }
- }
- return false;
- }
- /**
- *
- * check if the seller has checked the must pick-up option for the listing
- *
- * @return bool
- */
- public function pickUpOnly()
- {
- $settings = $this->getSettings();
- if ($this->isShipping() && $settings['enable_pickups'] &&
- $this->getData(ShippingModel::FLD_PICKUP_OPTIONS) == ShippingModel::MUST_PICKUP
- ) {
- return true;
- }
- return false;
- }
- /**
- *
- * check if shipping is enabled for the listing (global setting)
- *
- * @return bool
- */
- public function isShipping()
- {
- $settings = $this->getSettings();
- if ($settings['enable_shipping']) {
- return true;
- }
- return false;
- }
- /**
- *
- * check if listing is listed in an adult category
- *
- * @return bool
- */
- public function isAdult()
- {
- $ruleKeys = array('Category', 'AddlCategory');
- foreach ($ruleKeys as $ruleKey) {
- $category = $this->findParentRow('\Ppb\Db\Table\Categories', $ruleKey);
- if ($category instanceof Category) {
- if ($category->getData('adult')) {
- return true;
- }
- }
- }
- return false;
- }
- /**
- *
- * generate a short description for the listing, from the description field
- *
- * @param int $maxChars
- *
- * @return string
- */
- public function shortDescription($maxChars = null)
- {
- /** @var \Cube\View $view */
- $view = Front::getInstance()->getBootstrap()->getResource('view');
- $description = strip_tags($view->renderHtml($this->getData('description')));
- $length = strlen($description);
- $maxChars = ($maxChars === null) ? self::SHORT_DESC_MAX_CHARS : $maxChars;
- return substr($description, 0, $maxChars) . (($length > $maxChars) ? ' ... ' : '');
- }
- /**
- *
- * returns an array used by the url view helper to generate the purchase link for the listing
- * it can be a link to the buy out confirm page, or a link to the add to cart action
- *
- * @return array
- */
- public function purchaseLink()
- {
- if ($this->canAddToCart() === true) {
- return array(
- 'module' => 'listings',
- 'controller' => 'cart',
- 'action' => 'add',
- 'id' => $this->getData('id'),
- );
- }
- return array(
- 'module' => 'listings',
- 'controller' => 'purchase',
- 'action' => 'confirm',
- 'type' => 'buy',
- 'id' => $this->getData('id'),
- );
- }
- /**
- *
- * check if free shipping is offered for the listing
- *
- * this will work if:
- * - free postage is offered and free postage amount <= current bid
- * - item based postage > there is at least one option that has the price set to 0
- * - flat rates > the first item value is set to 0
- *
- * @return bool
- */
- public function isFreeShipping()
- {
- /** @var \Ppb\Db\Table\Row\User $owner */
- $owner = $this->findParentRow('\Ppb\Db\Table\Users');
- if ($owner->getPostageSettings(ShippingModel::SETUP_FREE_POSTAGE) &&
- $owner->getPostageSettings(ShippingModel::SETUP_FREE_POSTAGE_AMOUNT) <= $this->currentBid(true)
- ) {
- return true;
- }
- if ($owner->getPostageSettings(ShippingModel::SETUP_POSTAGE_TYPE) == ShippingModel::POSTAGE_TYPE_ITEM) {
- $postage = $this->getData(ShippingModel::FLD_POSTAGE);
- if (isset($postage['price'])) {
- foreach ($postage['price'] as $key => $value) {
- if ($value == 0) {
- return true;
- }
- }
- }
- }
- else if ($owner->getPostageSettings(ShippingModel::SETUP_POSTAGE_TYPE) == ShippingModel::POSTAGE_TYPE_FLAT) {
- if ($owner->getPostageSettings(ShippingModel::SETUP_POSTAGE_FLAT_FIRST) <= 0) {
- return true;
- }
- }
- return false;
- }
- /**
- *
- * check if the listing is a draft
- *
- * @return bool
- */
- public function isDraft()
- {
- return (bool)$this->getData('draft');
- }
- /**
- *
- * check if the logged in user is blocked from purchasing by the seller or admin
- * and return the block message if true
- *
- * @return string|false
- */
- protected function _isBlockedUserPurchasing()
- {
- $user = $this->getUser();
- $dataToBlock = array(
- 'ip' => $_SERVER['REMOTE_ADDR'],
- );
- if (!empty($user)) {
- $dataToBlock['username'] = $user['username'];
- $dataToBlock['email'] = $user['email'];
- }
- $blockedUsersService = new Service\BlockedUsers();
- $blockedUser = $blockedUsersService->check(
- BlockedUser::ACTION_PURCHASE, $dataToBlock, $this->getData('user_id'));
- if ($blockedUser !== null) {
- $view = Front::getInstance()->getBootstrap()->getResource('view');
- return $view->blockStatus($blockedUser)->blockMessage();
- }
- return false;
- }
- /**
- *
- * call magic method, used for retrieving dependent data
- *
- * @param string $name the name of the method from the \Cube\Db\Table\Row\AbstractRow method
- * @param array $arguments the arguments accepted by the method
- *
- * @return mixed
- */
- public function __call($name, $arguments)
- {
- $listing = $this;
- return call_user_func_array(
- array($listing, $name), $arguments);
- }
- }
|