Esempio n. 1
0
	public function perform()
	{
		// validate the listing
		if (!$this->_validateListing()) {
			$this->_logDebug('Listing validation failed.');
			$this->_removeListing();
			return;
		}

		$templateId = $this->_getListingData('templateid');
		$where = $this->_getListingData('where');
		$offset = $this->_getListingData('offset');
		$listingDate = $this->_getListingData('listing_date');
		$scheduleDate = $this->_getListingData('schedule_date');

		$this->_logDebug('listing template Id ' . $templateId . ' starting from row ' . $offset . ' with batch size ' . self::BATCH_SIZE);

		// get the template object
		try {
			$template = new ISC_ADMIN_EBAY_TEMPLATE($templateId);
		}
		catch (Exception $ex) {
			$error = GetLang('Ebay_Listing_Log_TemplateNotFound', array('id' => $templateId));
			$this->_logError($error);
			$this->_errorListing($error);
			$this->_endListing();
			return;
		}

		$primaryOptions = $template->getPrimaryCategoryOptions();
		$secondaryOptions = $template->getSecondaryCategoryOptions();

		// did the user choose to schedule the listing for a future date?
		if ($listingDate == 'schedule') {
			$template->setScheduleDate($scheduleDate);
		}

		// query for the products to export
		$query = ISC_ADMIN_EBAY_LIST_PRODUCTS::getListQuery($where, self::BATCH_SIZE, $offset);

		$this->_logDebug('query', $query);

		$res = $this->_db->Query($query);
		if (!$res) {
			$error = GetLang('Ebay_Listing_Log_JobDatabaseError');
			$this->_logError($error);
			$this->_errorListing($error);
			$this->_endListing();
			return;
		}

		$successCount = 0;
		$warningCount = 0;
		$errorCount = 0;
		$connectFailCount = 0;

		// nothing left to list?
		$resultCount = $this->_db->CountResult($res);
		if ($resultCount == 0) {
			$this->_logDebug('no more items to list');
			$this->_endListing();
			return;
		}

		$productsToList = array();

		while ($row = $this->_db->Fetch($res)) {
			// does this product have a variation?
			if ($row['prodvariationid']) {
				$variationError = '';

				// if the primary category or selling method doesn't support them?
				if ((empty ($primaryOptions['variations_supported'])
				||  (isset ($secondaryOptions['variations_supported']) && $secondaryOptions['variations_supported'] == 0))
				|| $template->getSellingMethod() == ISC_ADMIN_EBAY::CHINESE_AUCTION_LISTING) {

					$variationError = GetLang('EbayListingVariationsNotSupported');
				}
				// does the product have more than 120 combinations (eBay max)?
				elseif (($totalCombinations = Store_Variations::getCombinationsCount($row['productid'], $row['prodvariationid'])) > 120) {
					$variationError = GetLang('EbayListingVariationCombinationsExceeded', array('totalCombinations' => $totalCombinations));
				}

				// log error and skip this product
				if ($variationError) {
					$error = array(
						'prodname' => $row['prodname'],
						'time' => time(),
						'message' => $variationError,
					);

					$this->_keystore->set($this->_prefix . 'error:' . md5($row['productid'] . uniqid('', true)), ISC_JSON::encode($error));

					$errorCount++;
					continue;
				}
			}

			// add any custom fields and configurable fields to the product
			ISC_ADMIN_EBAY_LIST_PRODUCTS::addCustomAndConfigurableFields($row);

			$productsToList[$row['productid']] = $row;
		}


		$itemsToAdd = 1;
		// for chinese auctions if we're selling more than one item, then create multiple items
		if ($template->getSellingMethod() == ISC_ADMIN_EBAY::CHINESE_AUCTION_LISTING && $template->getQuantityToSell() > 1) {
			$itemsToAdd = $template->getQuantityToSell();
		}

		$thisBatchSize = $resultCount;
		$actualProcessed = $thisBatchSize * $itemsToAdd;
		$actualListed = count($productsToList) * $itemsToAdd;

		// don't have any products to list for this batch (would be due to disallowed variations)?
		if (empty($productsToList)) {
			$this->_keystore->increment($this->_prefix . 'error_count', $errorCount);
			$this->_keystore->increment($this->_prefix . 'actual_processed', $actualProcessed);
			$this->_keystore->increment($this->_prefix . 'offset', $thisBatchSize);

			$this->_logDebug('processed 0 items');

			$this->_repeatListing();
			return;
		}

		$this->_logDebug($actualListed . ' items to list', '<pre>' . var_export($productsToList, true) . '</pre>');

		try {
			// list the items on eBay
			$results = array();
			for ($x = 0; $x < $itemsToAdd; $x++) {
				$results = array_merge($results, ISC_ADMIN_EBAY_LIST_PRODUCTS::listItems($productsToList, $template));
			}

			foreach ($results as /** @var ISC_ADMIN_EBAY_LIST_ITEM_RESULT */$result) {
				if (!$result->isValid()) {
					// log error
					$error = array(
						'prodname' => $result->getProductName(),
						'time' => time(),
						'message' => implode('<br />', $result->getErrors()),
					);

					$this->_keystore->set($this->_prefix . 'error:' . md5($result->getProductId() . uniqid('', true)), ISC_JSON::encode($error));

					$errorCount++;
					continue;
				}

				// valid listing, but has errors
				if ($result->hasErrors()) {
					// log warning
					$error = array(
						'prodname' => $result->getProductName(),
						'time' => time(),
						'message' => implode('<br />', $result->getErrors()),
					);

					$this->_keystore->set($this->_prefix . 'warning:' . md5($result->getProductId() . uniqid('', true)), ISC_JSON::encode($error));

					$warningCount++;
				}

				// ensure template has correct data set so we can calculate prices for the DB
				$template->setProductData($result->getProductData());

				// add the new item to our local database
				$insertItem = array(
					'product_id'				=> $result->getProductId(),
					'ebay_item_id'				=> $result->getItemId(),
					'title'						=> $result->getProductName(),
					'start_time'				=> $result->getStartTimeISO(),
					'end_time'					=> $result->getEndTimeISO(),
					'datetime_listed'			=> time(),
					'listing_type'				=> $template->getSellingMethod(),
					'listing_status'			=> 'pending', // this will be updated to 'Active' when we receive ItemListed notification
					'current_price_currency' 	=> $template->getCurrencyCode(),
					'current_price'				=> $template->getStartPrice(),
					'buyitnow_price'			=> $template->getBuyItNowPrice(),
					'buyitnow_price_currency'	=> $template->getCurrencyCode(),
					'bid_count'					=> 0,
					'quantity_remaining'		=> $template->getTrueQuantityToSell(),
					'site_id'					=> $template->getSiteId(),
				);

				$dbItemId = $this->_db->InsertQuery('ebay_items', $insertItem);

				// process the listing fees
				foreach ($result->getFees() as $fee) {
					$insertFee = array(
						'item_id'		=> $dbItemId,
						'name'			=> $fee['name'],
						'amount'		=> $fee['fee'],
						'currency_code'	=> $fee['currency']
					);

					$this->_db->InsertQuery('ebay_item_fees', $insertFee);
				}

				$successCount++;
			}
		}
		catch (ISC_EBAY_API_CONNECTION_EXCEPTION $ex) {
			// connection failed
			$connectFailCount++;

			// more than one connection failure? abort the listing
			if ($connectFailCount > 1) {
				$this->_abortListing();
			}

			// did the entire request fail?
			$this->logBatchException($productsToList, $ex);

			$errorCount += $actualListed;
		}
		catch (ISC_EBAY_API_REQUEST_EXCEPTION $ex) {
			// did the entire request fail?
			$this->logBatchException($productsToList, $ex);

			$errorCount += $actualListed;
		}

		$this->_keystore->increment($this->_prefix . 'success_count', $successCount);
		$this->_keystore->increment($this->_prefix . 'warning_count', $warningCount);
		$this->_keystore->increment($this->_prefix . 'error_count', $errorCount);
		$this->_keystore->increment($this->_prefix . 'actual_processed', $actualProcessed);
		$this->_keystore->increment($this->_prefix . 'actual_listed', $actualListed);
		$this->_keystore->increment($this->_prefix . 'offset', $thisBatchSize);

		$this->_logDebug('processed ' . $actualListed . ' items');

		$this->_repeatListing();
	}
Esempio n. 2
0
	/**
	* Gets the true quantity that are being sold.
	* For a chinese auction this is always 1, for Fixed Price it is the specified quantity.
	*
	* @return int The true selling quantity
	*/
	public function getTrueQuantityToSell()
	{
		if ($this->getSellingMethod() == ISC_ADMIN_EBAY::CHINESE_AUCTION_LISTING) {
			return 1;
		}

		$qtyToSell = $this->getQuantityToSell();

		// does the product have a variation? the quantity to sell will be the amount of combinations in total x quantity to sell
		if ($this->_productData['prodvariationid']) {
			$combinationsTotal = Store_Variations::getCombinationsCount($this->_productData['productid'], $this->_productData['prodvariationid']);
			$qtyToSell *= $combinationsTotal;
		}

		return $qtyToSell;
	}
Esempio n. 3
0
	/**
	* Adds an Item element for the specified product and template into the supplied XML element
	*
	* @param SimpleXMLElement $xml The XML element to add the item to
	* @param array $product The array of product data
	* @param ISC_ADMIN_EBAY_TEMPLATE $template The template to use to add the product
	*/
	private static function addItemData(&$xml, $product, $template)
	{
		$template->setProductData($product);

		$productId = $product['productid'];

		$item = $xml->addChild('Item');

		$item->addChild('Site', $template->getSiteCode());

		// required details
		$item->addChild('Country', $template->getItemLocationCountry());
		$item->addChild('Currency', $template->getCurrencyCode());
		$item->addChild('ListingDuration', $template->getListingDuration());
		$item->addChild('ListingType', $template->getSellingMethod());

		$item->addChild('Location', $template->getItemLocationCityState());
		$item->addChild('PostalCode', $template->getItemLocationZip());

		$item->addChild('Title', isc_html_escape($product['prodname']));
		$item->addChild('Description', isc_html_escape($product['proddesc']));
		$item->addChild('SKU', isc_html_escape($product['prodcode']));

		$primaryOptions = $template->getPrimaryCategoryOptions();

		// are item specifics supported by the primary category?
		if (!empty($primaryOptions['item_specifics_supported'])) {
			$itemSpecifics = null;

			// brand name
			if (!empty($product['brandname'])) {
				$itemSpecifics = $item->addChild('ItemSpecifics');

				$specific = $itemSpecifics->addChild('NameValueList');
				$specific->addChild('Name', GetLang('Brand'));
				$specific->addChild('Value', $product['brandname']);
			}

			// do we have custom fields for the product?
			if (!empty($product['custom_fields'])) {
				if ($itemSpecifics == null) {
					$itemSpecifics = $item->addChild('ItemSpecifics');
				}

				foreach ($product['custom_fields'] as $customField) {
					$specific = $itemSpecifics->addChild('NameValueList');
					$specific->addChild('Name', $customField['fieldname']);
					$specific->addChild('Value', $customField['fieldvalue']);
				}
			}
		}

		// does this product have a upc? it can be used to pull in product information
		if (!empty($primaryOptions['catalog_enabled']) && !empty($product['upc'])) {
			$productListingDetails = $item->addChild('ProductListingDetails');
			$productListingDetails->addChild('UPC', $product['upc']);
		}

		// does the product have a variation?
		if ($product['prodvariationid']) {
			$variationId = $product['prodvariationid'];

			$variations = $item->addChild('Variations');
			$variationSpecificsSet = $variations->addChild('VariationSpecificsSet');

			$lastOptionName = '';
			$variationOptions = array();

			// add the variation options
			$res = Store_Variations::getOptions($variationId);
			while ($optionRow = $GLOBALS['ISC_CLASS_DB']->Fetch($res)) {
				if ($optionRow['voname'] != $lastOptionName) {
					$lastOptionName = $optionRow['voname'];

					$nameValueList = $variationSpecificsSet->addChild('NameValueList');
					$nameValueList->addChild('Name', $lastOptionName);
				}

				$nameValueList->addChild('Value', $optionRow['vovalue']);

				$variationOptions[$optionRow['voptionid']] = array($optionRow['voname'], $optionRow['vovalue']);
			}

			// add the combinations
			$res = Store_Variations::getCombinations($productId, $variationId);
			while ($comboRow = $GLOBALS['ISC_CLASS_DB']->Fetch($res)) {
				$variation = $variations->addChild('Variation');

				if ($comboRow['vcsku']) {
					$variation->addChild('SKU', $comboRow['vcsku']);
				}

				$variation->addChild('Quantity', $template->getQuantityToSell());

				$startPrice = $template->getStartPrice(false);
				$comboStartPrice = CalcProductVariationPrice($startPrice, $comboRow['vcpricediff'], $comboRow['vcprice']);
				$comboStartPrice = ConvertPriceToCurrency($comboStartPrice, $template->getCurrency());

				$variation->addChild('StartPrice', $comboStartPrice);

				// add the options for this combination
				$variationSpecifics = $variation->addChild('VariationSpecifics');

				$options = explode(',', $comboRow['vcoptionids']);
				foreach ($options as $optionId) {
					list($optionName, $optionValue) = $variationOptions[$optionId];

					$nameValueList = $variationSpecifics->addChild('NameValueList');
					$nameValueList->addChild('Name', $optionName);
					$nameValueList->addChild('Value', $optionValue);
				}
			}

			// add images
			$optionPictures = Store_Variations::getCombinationImagesForFirstOption($productId, $variationId);
			if (!empty($optionPictures)) {
				$pictures = $variations->addChild('Pictures');

				// we'll be adding images for the first option set
				list($optionName) = current($variationOptions);

				$pictures->addChild('VariationSpecificName', $optionName);

				foreach ($optionPictures as $optionName => $imageUrl) {
					$variationSpecificPictureSet = $pictures->addChild('VariationSpecificPictureSet');
					$variationSpecificPictureSet->addChild('VariationSpecificValue', $optionName);
					$variationSpecificPictureSet->addChild('PictureURL', $imageUrl);
				}
			}
		}

		// add quantity
		if (!$product['prodvariationid']) {
			$item->addChild('Quantity', $template->getTrueQuantityToSell());
		}

		$item->addChild('PrivateListing', (int)$template->isPrivateListing());

		// schedule date
		if ($template->getScheduleDate()) {
			$item->addChild('ScheduleTime', $template->getScheduleDate());
		}

		// condition
		if ($template->getItemCondition()) {
			$item->addChild('ConditionID', $template->getItemCondition());
		}

		// payment details
		foreach ($template->getPaymentMethods() as $paymentMethod) {
			$item->addChild('PaymentMethods', $paymentMethod);
		}

		if (in_array('PayPal', $template->getPaymentMethods())) {
			$item->addChild('PayPalEmailAddress', $template->getPayPalEmailAddress());
		}

		// add categories
		$item->addChild('PrimaryCategory')->addChild('CategoryID', $template->getPrimaryCategoryId());
		if ($template->getSecondaryCategoryId()) {
			$item->addChild('SecondaryCategory')->addChild('CategoryID', $template->getSecondaryCategoryId());
		}

		$item->addChild('CategoryMappingAllowed', (int)$template->getAllowCategoryMapping());

		// add store categories
		if ($template->getPrimaryStoreCategoryId()) {
			$storeFront = $item->addChild('Storefront');
			$storeFront->addChild('StoreCategoryID', $template->getPrimaryStoreCategoryId());

			if ($template->getSecondaryStoreCategoryId()) {
				$storeFront->addChild('StoreCategory2ID', $template->getSecondaryStoreCategoryId());
			}
		}

		// prices
		if ($template->getSellingMethod() == ISC_ADMIN_EBAY::CHINESE_AUCTION_LISTING) {
			$item->addChild('StartPrice', $template->getStartPrice());

			if ($template->getReservePrice() !== false) {
				$item->addChild('ReservePrice', $template->getReservePrice());
			}

			if ($template->getBuyItNowPrice() !== false) {
				$item->addChild('BuyItNowPrice', $template->getBuyItNowPrice());
			}
		}
		elseif (!$product['prodvariationid']) {
			$item->addChild('StartPrice', $template->getStartPrice());
		}

		// add return policy info
		$policy = $item->addChild('ReturnPolicy');
		$policy->addChild('ReturnsAcceptedOption', $template->getReturnsAcceptedOption());
		if ($template->getReturnsAccepted()) {
			if ($template->getAdditionalPolicyInfo()) {
				$policy->addChild('Description', isc_html_escape($template->getAdditionalPolicyInfo()));
			}
			if ($template->getReturnOfferedAs()) {
				$policy->addChild('RefundOption', $template->getReturnOfferedAs());
			}
			if ($template->getReturnsPeriod()) {
				$policy->addChild('ReturnsWithinOption', $template->getReturnsPeriod());
			}
			if ($template->getReturnCostPaidBy()) {
				$policy->addChild('ShippingCostPaidByOption', $template->getReturnCostPaidBy());
			}
		}

		// counter
		$item->addChild('HitCounter', $template->getCounterStyle());

		// gallery option
		$pictureDetails = $item->addChild('PictureDetails');
		$pictureDetails->addChild('GalleryType', $template->getGalleryType());
		if ($template->getGalleryType() == 'Featured') {
			$pictureDetails->addChild('GalleryDuration', $template->getFeaturedGalleryDuration());
		}

		if ($template->getItemPhoto()) {
			if ($template->getGalleryType() != 'None') {
				$pictureDetails->addChild('GalleryURL', $template->getItemPhoto());
			}

			$pictureDetails->addChild('PictureURL', $template->getItemPhoto());
		}

		// listing features
		foreach ($template->getListingFeatures() as $feature) {
			$item->addChild('ListingEnhancement', $feature);
		}

		// domestic shipping
		if ($template->getUseDomesticShipping()) {
			// add shipping details
			$shippingDetails = $item->addChild('ShippingDetails');
			// the actual shipping type - Flat or Calculated - where's our freight option gone?
			$shippingDetails->addChild('ShippingType', $template->getShippingType());

			//$insuranceDetails = $shippingDetails->addChild('InsuranceDetails');
			$shippingDetails->addChild('InsuranceOption', 'NotOffered');

			$calculatedRate = null;

			// add checkout instructions
			if ($template->getCheckoutInstructions()) {
				$shippingDetails->addChild('PaymentInstructions', $template->getCheckoutInstructions());
			}

			// add sales tax - US only
			if ($template->getUseSalesTax()) {
				$salesTax = $shippingDetails->addChild('SalesTax');
				$salesTax->addChild('SalesTaxState', $template->getSalesTaxState());
				$salesTax->addChild('SalesTaxPercent', $template->getSalesTaxPercent());
				$salesTax->addChild('ShippingIncludedInTax', $template->getShippingIncludedInTax());
			}


			$domesticServices = $template->getDomesticShippingServices();
			$domesticSettings = $template->getDomesticShippingSettings();

			if (empty($domesticSettings)) {
				throw new Exception('Missing domestic shipping settings');
			}

			// add a pickup service - can't get this to work gives error:  ShippingService is required if Insurance, SalesTax, or AutoPay is specified. (10019)
			if ($domesticSettings['offer_pickup']) {
				$domesticServices['Pickup'] = array(
					'additional_cost'	=> 0,
					'cost'				=> 0 //(double)$domesticServices['pickup_cost'] // where has this option gone?
				);
			}


			if (empty($domesticServices)) {
				throw new Exception('Missing domestic shipping services');
			}

			$domesticFeeShipping = (bool)$domesticSettings['is_free_shipping'];

			// add our domestic services
			self::addShippingServices($shippingDetails, $domesticSettings, $domesticServices, 'ShippingServiceOptions', $domesticFeeShipping);

			// buy it fast enabled?  domestic only
			if ($domesticSettings['get_it_fast']) {
				$item->addChild('GetItFast', true);
				$item->addChild('DispatchTimeMax', 1); // required for getitfast
			}
			else {
				// add handling time
				$item->addChild('DispatchTimeMax', $template->getHandlingTime());
			}

			if ($domesticSettings['cost_type'] == 'Calculated') {
				if ($calculatedRate == null) {
					$calculatedRate = self::addCalculatedDetails($shippingDetails, $template);
				}

				// handling cost
				if ($domesticSettings['handling_cost']) {
					$calculatedRate->addChild('PackagingHandlingCosts', $domesticSettings['handling_cost']);
				}
			}

			// international shipping - we can't supply international services if we don't specify domestic
			if ($template->getUseInternationalShipping()) {
				$internationalSettings = $template->getInternationalShippingSettings();
				$internationalServices = $template->getInternationalShippingServices();

				if (empty($internationalSettings)) {
					throw new Exception('Missing international shipping settings');
				}

				if (empty($internationalServices)) {
					throw new Exception('Missing international shipping services');
				}

				// add our international services
				self::addShippingServices($shippingDetails, $internationalSettings, $internationalServices, 'InternationalShippingServiceOption', false, true);


				if ($internationalSettings['cost_type'] == 'Calculated') {
					if ($calculatedRate == null) {
						$calculatedRate = self::addCalculatedDetails($shippingDetails, $template);
					}

					// handling cost
					if ($internationalSettings['handling_cost']) {
						$calculatedRate->addChild('InternationalPackagingHandlingCosts', $internationalSettings['handling_cost']);
					}
				}
			}
		}
		else {
			// domestic pickup only
			$item->addChild('ShipToLocations', 'None');
		}
	}