Initial xentral_oss_20.3.c9ffacf

This commit is contained in:
Alex
2021-05-21 08:49:41 +02:00
parent 5406e4a551
commit 34e5ac43d9
18884 changed files with 2109867 additions and 0 deletions
+125
View File
@@ -0,0 +1,125 @@
<?php
declare(strict_types=1);
namespace Xentral\Modules\Ebay;
use ApplicationCore;
use GuzzleHttp\Client;
use Xentral\Core\DependencyInjection\ContainerInterface;
use Xentral\Modules\Ebay\Client\EbayRestApiClient;
use Xentral\Modules\Ebay\Gateway\EbayListingGateway;
use Xentral\Modules\Ebay\Gateway\EbayRestApiGateway;
use Xentral\Modules\Ebay\Module\EbayRestApiModule;
use Xentral\Modules\Ebay\Service\EbayListingService;
use Xentral\Modules\Ebay\Service\EbayListingXmlSerializer;
use Xentral\Modules\Ebay\Service\EbayRestApiService;
use Xentral\Modules\Ebay\Service\EbayStockLoggingService;
use Xentral\Modules\Ebay\Wrapper\StockCalculationWrapper;
final class Bootstrap
{
/**
* @return array
*/
public static function registerServices(): array
{
return [
'EbayListingGateway' => 'onInitEbayListingGateway',
'EbayListingService' => 'onInitEbayListingService',
'EbayRestApiModule' => 'onInitEbayRestApiModule',
'EbayStockLoggingService' => 'onInitEbayStockLoggingService',
'EbayRestApiGateway' => 'onInitEbayRestApiGateway',
];
}
/**
* @param ContainerInterface $container
*
* @return EbayListingService
*/
public static function onInitEbayListingService(ContainerInterface $container): EbayListingService
{
return new EbayListingService(
$container->get('EbayListingGateway'),
$container->get('Database'),
new EbayListingXmlSerializer(),
self::onInitStockCalculationWrapper($container)
);
}
/**
* @param ContainerInterface $container
*
* @return StockCalculationWrapper
*/
private static function onInitStockCalculationWrapper(ContainerInterface $container): StockCalculationWrapper
{
/** @var ApplicationCore $app */
$app = $container->get('LegacyApplication');
return new StockCalculationWrapper($app->erp, $container->get('Database'));
}
/**
* @param ContainerInterface $container
*
* @return EbayStockLoggingService
*/
public static function onInitEbayStockLoggingService(ContainerInterface $container): EbayStockLoggingService
{
return new EbayStockLoggingService($container->get('Database'));
}
/**
* @param ContainerInterface $container
*
* @return EbayListingGateway
*/
public static function onInitEbayListingGateway(ContainerInterface $container): EbayListingGateway
{
return new EbayListingGateway($container->get('Database'));
}
/**
* @param ContainerInterface $container
*
* @return EbayRestApiModule
*/
public static function onInitEbayRestApiModule(ContainerInterface $container): EbayRestApiModule
{
return new EbayRestApiModule(
self::onInitEbayRestApiClient(),
self::onInitEbayRestApiGateway($container),
self::onInitEbayRestApiService($container)
);
}
/**
* @param ContainerInterface $container
*
* @return EbayRestApiGateway
*/
public static function onInitEbayRestApiGateway(ContainerInterface $container): EbayRestApiGateway
{
return new EbayRestApiGateway($container->get('Database'));
}
/**
* @return EbayRestApiClient
*/
private static function onInitEbayRestApiClient(): EbayRestApiClient
{
return new EbayRestApiClient(new Client());
}
/**
* @param ContainerInterface $container
*
* @return EbayRestApiService
*/
private static function onInitEbayRestApiService(ContainerInterface $container): EbayRestApiService
{
return new EbayRestApiService($container->get('Database'));
}
}
@@ -0,0 +1,186 @@
<?php
declare(strict_types=1);
namespace Xentral\Modules\Ebay\Client;
use DateTime;
use GuzzleHttp\Psr7\Request;
use Xentral\Modules\Ebay\Data\AccountCredentialsData;
use Xentral\Modules\Ebay\Data\TokenData;
class EbayRestApiClient
{
public const TOKEN_TYPE_APPLICATION = 'Application Access Token';
public const TOKEN_TYPE_USER = 'User Access Token';
public const DEFAULT_ORDER_IMPORT_LIMIT = 50;
protected $client;
public function __construct($client)
{
$this->client = $client;
}
/**
* @param AccountCredentialsData $accountCredentialsData
*
* @return mixed
*/
public function getRestApiApplicationAccessTokenFromEbay(AccountCredentialsData $accountCredentialsData): array
{
$headers = [
'Content-Type' => 'application/x-www-form-urlencoded',
'Authorization' => 'Basic ' . base64_encode(
$accountCredentialsData->getClientId() . ':' . $accountCredentialsData->getClientSecret()
),
];
$scope = ['https://api.ebay.com/oauth/api_scope'];
$body = [
'grant_type' => 'client_credentials',
'scope' => implode(' ', $scope),
];
$request = new Request(
'POST',
'https://api.ebay.com/identity/v1/oauth2/token',
$headers,
http_build_query($body)
);
$response = $this->client->send($request);
return json_decode($response->getBody()->getContents(), true);
}
public function getRestApiUserAccessTokenFromEbay(AccountCredentialsData $accountCredentialsData, string $code): array
{
$headers = [
'Content-Type' => 'application/x-www-form-urlencoded',
'Authorization' => 'Basic ' . base64_encode(
$accountCredentialsData->getClientId() . ':' . $accountCredentialsData->getClientSecret()
),
];
$body = [
'grant_type' => 'authorization_code',
'code' => $code,
'redirect_uri' => $accountCredentialsData->getRedirectUrl(),
];
$request = new Request(
'POST',
'https://api.ebay.com/identity/v1/oauth2/token',
$headers,
http_build_query($body)
);
$response = $this->client->send($request);
return json_decode($response->getBody()->getContents(), true);
}
/**
* @param int $siteId
* @param int $categoryId
* @param string $token
*
* @return array
*/
public function getCategorySpecificProperties(int $siteId, int $categoryId, string $token): array
{
$headers = [
'Authorization' => 'Bearer ' . $token,
];
$url = sprintf(
'https://api.ebay.com/commerce/taxonomy/v1_beta/category_tree/%d/get_item_aspects_for_category?category_id=%d',
$siteId,
$categoryId
);
$request = new Request(
'GET',
$url,
$headers
);
$response = $this->client->send($request);
return json_decode($response->getBody()->getContents(), true);
}
public function getOrders(string $token, DateTime $dateFrom, int $offset, ?int $limit): array
{
$headers = [
'Authorization' => 'Bearer ' . $token,
];
$dateFromString = $dateFrom->format('Y-m-d\TH:i:s');
$url = 'https://api.ebay.com/sell/fulfillment/v1/order?';
$url .= http_build_query([
'offset' => $offset,
'fieldGroups' => 'TAX_BREAKDOWN',
'limit' => ($limit ?: self::DEFAULT_ORDER_IMPORT_LIMIT),
'filter' => "lastmodifieddate:[{$dateFromString}.000Z..],orderfulfillmentstatus:{NOT_STARTED|IN_PROGRESS}",
]);
$request = new Request(
'GET',
$url,
$headers
);
$response = $this->client->send($request);
return json_decode($response->getBody()->getContents(), true);
}
public function renewToken(AccountCredentialsData $accountCredentialsData, TokenData $tokenData): array
{
$headers = [
'Content-Type' => 'application/x-www-form-urlencoded',
'Authorization' => 'Basic ' . base64_encode(
$accountCredentialsData->getClientId() . ':' . $accountCredentialsData->getClientSecret()
),
];
$scope = ['https://api.ebay.com/oauth/api_scope'];
if ($tokenData->getType() === self::TOKEN_TYPE_USER) {
$scope = $this->getCompleteScope();
}
$body = [
'grant_type' => 'refresh_token',
'scope' => implode(' ', $scope),
'refresh_token' => $tokenData->getRefreshToken(),
];
$request = new Request(
'POST',
'https://api.ebay.com/identity/v1/oauth2/token',
$headers,
http_build_query($body)
);
$response = $this->client->send($request);
return json_decode($response->getBody()->getContents(), true);
}
public function getCompleteScope(): array
{
return [
'https://api.ebay.com/oauth/api_scope',
'https://api.ebay.com/oauth/api_scope/sell.marketing.readonly',
'https://api.ebay.com/oauth/api_scope/sell.marketing',
'https://api.ebay.com/oauth/api_scope/sell.inventory.readonly',
'https://api.ebay.com/oauth/api_scope/sell.inventory',
'https://api.ebay.com/oauth/api_scope/sell.account.readonly',
'https://api.ebay.com/oauth/api_scope/sell.account',
'https://api.ebay.com/oauth/api_scope/sell.fulfillment.readonly',
'https://api.ebay.com/oauth/api_scope/sell.fulfillment',
'https://api.ebay.com/oauth/api_scope/sell.analytics.readonly',
'https://api.ebay.com/oauth/api_scope/sell.finances',
'https://api.ebay.com/oauth/api_scope/sell.payment.dispute',
'https://api.ebay.com/oauth/api_scope/commerce.identity.readonly',
];
}
}
@@ -0,0 +1,89 @@
<?php
declare(strict_types=1);
namespace Xentral\Modules\Ebay\Data;
final class AccountCredentialsData
{
/** @var string */
private $clientId;
/** @var string */
private $clientSecret;
/** @var string */
private $redirectUrl;
/**
* AccountCredentialsData constructor.
*
* @param string $clientId
* @param string $clientSecret
* @param string $redirectUrl
*/
public function __construct(string $clientId, string $clientSecret, string $redirectUrl)
{
$this->clientId = $clientId;
$this->clientSecret = $clientSecret;
$this->redirectUrl = $redirectUrl;
}
/**
* @return string
*/
public function getClientId(): string
{
return $this->clientId;
}
/**
* @param string $clientId
*
* @return AccountCredentialsData
*/
public function setClientId(string $clientId): AccountCredentialsData
{
$this->clientId = $clientId;
return $this;
}
/**
* @return string
*/
public function getClientSecret(): string
{
return $this->clientSecret;
}
/**
* @param string $clientSecret
*
* @return AccountCredentialsData
*/
public function setClientSecret(string $clientSecret): AccountCredentialsData
{
$this->clientSecret = $clientSecret;
return $this;
}
/**
* @return string
*/
public function getRedirectUrl(): string
{
return $this->redirectUrl;
}
/**
* @param string $redirectUrl
*
* @return AccountCredentialsData
*/
public function setRedirectUrl(string $redirectUrl): AccountCredentialsData
{
$this->redirectUrl = $redirectUrl;
return $this;
}
}
@@ -0,0 +1,724 @@
<?php
declare(strict_types=1);
namespace Xentral\Modules\Ebay\Data;
use Xentral\Modules\Ebay\Exception\InvalidArgumentException;
final class StagingListingData
{
/** @var int $id */
private $id;
/** @var int $articleId */
private $articleId = 0;
/** @var string $type */
private $type = '';
/** @var string $status */
private $status = '';
/** @var string $sku */
private $sku = '';
/** @var string $title */
private $title = '';
/** @var string $description */
private $description = '';
/** @var string $primaryCategoryId */
private $primaryCategoryId = '';
/** @var string $secondaryCategoryId */
private $secondaryCategoryId = '';
/** @var string $primaryStoreCategoryId */
private $primaryStoreCategoryId = '';
/** @var string $secondaryStoreCategoryId */
private $secondaryStoreCategoryId = '';
/** @var string $shippingProfileId */
private $shippingProfileId = '';
/** @var string $returnProfileId */
private $returnProfileId = '';
/** @var string $paymentProfileId */
private $paymentProfileId = '';
/** @var string $deliveryTime */
private $deliveryTime = '';
/** @var string $itemId */
private $itemId = '';
/** @var string $inventoryTrackingMethod */
private $inventoryTrackingMethod = '';
/** @var string $conditionId */
private $conditionId = '0';
/** @var string $conditionDisplayName */
private $conditionDisplayName = '';
/** @var string $conditionDescription */
private $conditionDescription = '';
/** @var string $listingDuration */
private $listingDuration = '';
/** @var bool $ebayPlus */
private $ebayPlus = false;
/** @var bool $privateListing */
private $privateListing = false;
/** @var bool $priceSuggestion */
private $priceSuggestion = false;
/** @var int $shopId */
private $shopId;
/** @var int $templateId */
private $templateId = 0;
/** @var array $variations */
private $variations = [];
/** @var array $specifics */
private $specifics = [];
/** @var array $pictures */
private $pictures = [];
/**
* StagingListing constructor.
*
* @param int $shopId
* @param int $databaseId
*
* @throws InvalidArgumentException
*/
public function __construct($shopId, $databaseId = 0)
{
if (empty($shopId)) {
throw new InvalidArgumentException('Required argument "shopId" is empty.');
}
$this->shopId = (int)$shopId;
$this->id = $databaseId;
}
/**
* @param array $data
*
* @return StagingListingData
*/
public static function fromDbState($data): StagingListingData
{
$stagingListing = new StagingListingData($data['shop_id'], $data['id']);
$stagingListing->setArticleId($data['article_id']);
$stagingListing->setType($data['type']);
$stagingListing->setTitle($data['title']);
$stagingListing->setStatus($data['status']);
$stagingListing->setSku($data['sku']);
$stagingListing->setDescription($data['description']);
$stagingListing->setPrimaryCategoryId($data['ebay_primary_category_id_external']);
$stagingListing->setSecondaryCategoryId($data['ebay_secondary_category_id_external']);
$stagingListing->setShippingProfileId($data['ebay_shipping_profile_id_external']);
$stagingListing->setPaymentProfileId($data['ebay_payment_profile_id_external']);
$stagingListing->setReturnProfileId($data['ebay_return_profile_id_external']);
$stagingListing->setPrivateListing(!empty($data['ebay_private_listing']));
$stagingListing->setDeliveryTime($data['delivery_time']);
$stagingListing->setInventoryTrackingMethod($data['inventory_tracking_method']);
$stagingListing->setConditionId($data['condition_id_external']);
$stagingListing->setConditionDisplayName($data['condition_display_name']);
$stagingListing->setConditionDescription($data['condition_description']);
$stagingListing->setListingDuration($data['listing_duration']);
$stagingListing->setDeliveryTime($data['delivery_time']);
$stagingListing->setEbayPlus((bool)$data['ebay_plus']);
$stagingListing->setPriceSuggestion((bool)$data['ebay_price_suggestion']);
$stagingListing->setItemId($data['item_id_external']);
$stagingListing->setTemplateId((int)$data['template_id']);
return $stagingListing;
}
/**
* @return string
*/
public function getDescription(): string
{
return $this->description;
}
/**
* @param string $description
*
*/
public function setDescription($description): void
{
$this->description = (string)$description;
}
/**
* @return int
*/
public function getShopId(): int
{
return $this->shopId;
}
/**
* @return array
*/
public function listSpecifics(): array
{
return $this->specifics;
}
/**
* @param array $specifics
*/
public function setSpecifics($specifics): void
{
$this->specifics = $specifics;
}
/**
* @param string $specificName
* @param string $specificValue
*/
public function addSpecific($specificName, $specificValue = ''): void
{
if (!empty($specificName)) {
$this->specifics[$specificName] = $specificValue;
}
}
/**
* @return array
*/
public function listPictures(): array
{
return $this->pictures;
}
/**
* @param StagingListingPicture $picture
*/
public function addPicture(StagingListingPicture $picture): void
{
$pictureExists = false;
foreach ($this->pictures as $existingPicture) {
if ($existingPicture->getUrl() === $picture->getUrl()) {
$pictureExists = true;
break;
}
}
if (!$pictureExists) {
$this->pictures[] = $picture;
}
}
/**
* @return string
*/
public function getPrimaryStoreCategoryId(): string
{
return $this->primaryStoreCategoryId;
}
/**
* @param string $primaryStoreCategoryId
*/
public function setPrimaryStoreCategoryId($primaryStoreCategoryId): void
{
$this->primaryStoreCategoryId = $primaryStoreCategoryId;
}
/**
* @return string
*/
public function getSecondaryStoreCategoryId(): string
{
return $this->secondaryStoreCategoryId;
}
/**
* @param string $secondaryStoreCategoryId
*/
public function setSecondaryStoreCategoryId($secondaryStoreCategoryId): void
{
$this->secondaryStoreCategoryId = $secondaryStoreCategoryId;
}
/**
* @param StagingListingVariationData $variation
*/
public function addVariation($variation): void
{
if (empty($variation->listSpecifics())) {
return;
}
$variationExists = false;
foreach ($this->variations as $existingVariation) {
$existingSpecifics = $existingVariation->listSpecifics();
$variationExists = $this->identicalVariationExistsWithinListing($variation, $existingSpecifics);
if ($variationExists) {
break;
}
}
if (!$variationExists) {
$this->variations[] = $variation;
}
}
/**
* @param StagingListingVariationData $variation
* @param array $existingSpecifics
*
* @return bool
*/
private function identicalVariationExistsWithinListing($variation, $existingSpecifics): bool
{
$variationExists = false;
foreach ($variation->listSpecifics() as $propertyToCheck => $valueToCheck) {
$variationExists = false;
if (isset($existingSpecifics[$propertyToCheck]) && $existingSpecifics[$propertyToCheck] === $valueToCheck) {
$variationExists = true;
} else {
break;
}
}
return $variationExists;
}
/**
* @return array
*/
public function getVariations(): array
{
return $this->variations;
}
/**
* @param array $variations
*/
public function setVariations($variations): void
{
$this->variations = $variations;
}
/**
* @return array
*/
public function toArray(): array
{
return [
'id' => $this->getId(),
'article_id' => $this->getArticleId(),
'type' => $this->getType(),
'title' => $this->getTitle(),
'status' => $this->getStatus(),
'sku' => $this->getSku(),
'ebay_primary_category_id_external' => $this->getPrimaryCategoryId(),
'ebay_secondary_category_id_external' => $this->getSecondaryCategoryId(),
'ebay_shipping_profile_id_external' => $this->getShippingProfileId(),
'ebay_payment_profile_id_external' => $this->getPaymentProfileId(),
'ebay_return_profile_id_external' => $this->getReturnProfileId(),
'item_id_external' => $this->getItemId(),
'delivery_time' => $this->getDeliveryTime(),
'inventory_tracking_method' => $this->getInventoryTrackingMethod(),
'condition_id_external' => $this->getConditionId(),
'condition_display_name' => $this->getConditionDisplayName(),
'condition_description' => $this->getConditionDescription(),
'listing_duration' => $this->getListingDuration(),
'ebay_plus' => $this->isEbayPlus(),
'ebay_price_suggestion' => $this->isPriceSuggestion(),
'ebay_private_listing' => $this->isPrivateListing(),
'variations' => $this->getVariationsAsArray(),
'template_id' => $this->getTemplateId(),
];
}
/**
* @return int
*/
public function getId(): int
{
return $this->id;
}
/**
* @return int
*/
public function getArticleId(): int
{
return $this->articleId;
}
/**
* @param int $articleId
*/
public function setArticleId($articleId): void
{
$this->articleId = (int)$articleId;
}
/**
* @return string
*/
public function getType(): string
{
return $this->type;
}
/**
* @param string $type
*/
public function setType($type): void
{
$this->type = (string)$type;
}
/**
* @return string
*/
public function getTitle(): string
{
return $this->title;
}
/**
* @param string $title
*/
public function setTitle($title): void
{
$this->title = (string)$title;
}
/**
* @return string
*/
public function getStatus(): string
{
return $this->status;
}
/**
* @param string $status
*/
public function setStatus($status): void
{
$this->status = (string)$status;
}
/**
* @return string
*/
public function getSku(): string
{
return $this->sku;
}
/**
* @param string $sku
*/
public function setSku($sku): void
{
$this->sku = (string)$sku;
}
/**
* @return string
*/
public function getPrimaryCategoryId(): string
{
return $this->primaryCategoryId;
}
/**
* @param string $primaryCategoryId
*/
public function setPrimaryCategoryId($primaryCategoryId): void
{
$this->primaryCategoryId = (string)$primaryCategoryId;
}
/**
* @return string
*/
public function getSecondaryCategoryId(): string
{
return $this->secondaryCategoryId;
}
/**
* @param string $secondaryCategoryId
*/
public function setSecondaryCategoryId($secondaryCategoryId): void
{
$this->secondaryCategoryId = (string)$secondaryCategoryId;
}
/**
* @return string
*/
public function getShippingProfileId(): string
{
return $this->shippingProfileId;
}
/**
* @param string $shippingProfileId
*/
public function setShippingProfileId($shippingProfileId): void
{
$this->shippingProfileId = (string)$shippingProfileId;
}
/**
* @return string
*/
public function getPaymentProfileId(): string
{
return $this->paymentProfileId;
}
/**
* @param string $paymentProfileId
*/
public function setPaymentProfileId($paymentProfileId): void
{
$this->paymentProfileId = (string)$paymentProfileId;
}
/**
* @return string
*/
public function getReturnProfileId(): string
{
return $this->returnProfileId;
}
/**
* @param string $returnProfileId
*/
public function setReturnProfileId($returnProfileId): void
{
$this->returnProfileId = (string)$returnProfileId;
}
/**
* @return string
*/
public function getItemId(): string
{
return $this->itemId;
}
/**
* @param string $itemId
*/
public function setItemId($itemId): void
{
$this->itemId = (string)$itemId;
}
/**
* @return string
*/
public function getDeliveryTime(): string
{
return $this->deliveryTime;
}
/**
* @param string $deliveryTime
*/
public function setDeliveryTime($deliveryTime): void
{
$this->deliveryTime = $deliveryTime;
}
/**
* @return string
*/
public function getInventoryTrackingMethod(): string
{
return $this->inventoryTrackingMethod;
}
/**
* @param string $inventoryTrackingMethod
*/
public function setInventoryTrackingMethod($inventoryTrackingMethod): void
{
$this->inventoryTrackingMethod = $inventoryTrackingMethod;
}
/**
* @return string
*/
public function getConditionId(): string
{
return $this->conditionId;
}
/**
* @param string $conditionId
*/
public function setConditionId(string $conditionId): void
{
$this->conditionId = $conditionId;
}
/**
* @return string
*/
public function getConditionDisplayName(): string
{
return $this->conditionDisplayName;
}
/**
* @param string $conditionDisplayName
*/
public function setConditionDisplayName($conditionDisplayName): void
{
$this->conditionDisplayName = $conditionDisplayName;
}
/**
* @return string
*/
public function getConditionDescription(): string
{
return $this->conditionDescription;
}
/**
* @param string $conditionDescription
*/
public function setConditionDescription($conditionDescription): void
{
$this->conditionDescription = $conditionDescription;
}
/**
* @return string
*/
public function getListingDuration(): string
{
return $this->listingDuration;
}
/**
* @param string $listingDuration
*/
public function setListingDuration($listingDuration): void
{
$this->listingDuration = $listingDuration;
}
/**
* @return bool
*/
public function isEbayPlus(): bool
{
return $this->ebayPlus;
}
/**
* @param bool $ebayPlus
*/
public function setEbayPlus($ebayPlus): void
{
$this->ebayPlus = $ebayPlus;
}
/**
* @return bool
*/
public function isPriceSuggestion(): bool
{
return $this->priceSuggestion;
}
/**
* @param bool $priceSuggestion
*/
public function setPriceSuggestion($priceSuggestion): void
{
$this->priceSuggestion = $priceSuggestion;
}
/**
* @return bool
*/
public function isPrivateListing(): bool
{
return $this->privateListing;
}
/**
* @param bool $privateListing
*/
public function setPrivateListing(bool $privateListing): void
{
$this->privateListing = $privateListing;
}
/**
* @return array
*/
protected function getVariationsAsArray(): array
{
$variations = [];
foreach ($this->variations as $variation) {
$variations[] = $variation->toArray();
}
return $variations;
}
/**
* @return int
*/
public function getTemplateId(): int
{
return $this->templateId;
}
/**
* @param int $templateId
*/
public function setTemplateId($templateId): void
{
$this->templateId = $templateId;
}
/**
* @return array
*/
public function validate(): array
{
$errors = [];
if ($this->shopId <= 0) {
$errors['shop_id'][] = 'The "shopId" property must be greater than zero.';
}
return $errors;
}
}
@@ -0,0 +1,103 @@
<?php
declare(strict_types=1);
namespace Xentral\Modules\Ebay\Data;
class StagingListingPicture
{
/**@var int $id */
private $id;
/**@var int $stagingListingVariantId */
private $stagingListingVariantId = 0;
/** @var int $fileId */
private $fileId = 0;
/** @var int $stagingListingId */
private $stagingListingId = 0;
/** @var string $url */
private $url;
/**
* @param string $url
*/
public function __construct($url)
{
$this->url = $url;
}
/**
* @return int
*/
public function getId(): int
{
return $this->id;
}
/**
* @return int
*/
public function getFileId(): int
{
return $this->fileId;
}
/**
* @param int $fileId
*/
public function setFileId(int $fileId): void
{
$this->fileId = $fileId;
}
/**
* @return int
*/
public function getStagingListingId(): int
{
return $this->stagingListingId;
}
/**
* @param int $stagingListingId
*/
public function setStagingListingId(int $stagingListingId): void
{
$this->stagingListingId = $stagingListingId;
}
/**
* @return int
*/
public function getStagingListingVariantId(): int
{
return $this->stagingListingVariantId;
}
/**
* @param int $stagingListingVariantId
*/
public function setStagingListingVariantId(int $stagingListingVariantId): void
{
$this->stagingListingVariantId = $stagingListingVariantId;
}
/**
* @return string
*/
public function getUrl(): string
{
return $this->url;
}
/**
* @param string $url
*/
public function setUrl(string $url): void
{
$this->url = $url;
}
}
@@ -0,0 +1,153 @@
<?php
declare(strict_types=1);
namespace Xentral\Modules\Ebay\Data;
final class StagingListingVariationData
{
/** @var int $articleId */
private $articleId = 0;
/** @var string $sku */
private $sku = '';
/** @var array $specifics */
private $specifics = [];
/** @var array $pictures */
private $pictures = [];
/** @var int $id */
private $id;
/**
* @param int $id
*/
public function __construct(int $id = 0)
{
$this->id = $id;
}
/**
* @param array $data
* @param array $specifics
*
* @return StagingListingVariationData
*/
public static function fromArray($data, $specifics): StagingListingVariationData
{
$stagingListing = new StagingListingVariationData($data['id']);
$stagingListing->setSku($data['sku']);
$stagingListing->setSpecifics($specifics);
$stagingListing->setArticleId($data['article_id']);
return $stagingListing;
}
/**
* @param array $specifics
*/
public function setSpecifics($specifics): void
{
$this->specifics = $specifics;
}
/**
* @param string $property
* @param string $value
*/
public function addSpecifics($property, $value): void
{
$this->specifics[$property] = $value;
}
/**
* @return array
*/
public function toArray(): array
{
return [
'id' => $this->getId(),
'article_id' => $this->getArticleId(),
'sku' => $this->getSku(),
'specifics' => $this->listSpecifics(),
'pictures' => $this->listPictures(),
];
}
/**
* @return int
*/
public function getId(): int
{
return $this->id;
}
/**
* @return int
*/
public function getArticleId(): int
{
return $this->articleId;
}
/**
* @param int $articleId
*/
public function setArticleId($articleId): void
{
$this->articleId = (int)$articleId;
}
/**
* @return string
*/
public function getSku(): string
{
return $this->sku;
}
/**
* @param string $sku
*/
public function setSku($sku): void
{
$this->sku = (string)$sku;
}
/**
* @return array
*/
public function listSpecifics(): array
{
return $this->specifics;
}
/**
* @return array
*/
public function listPictures(): array
{
return $this->pictures;
}
/**
* @param StagingListingPicture $picture
*/
public function addPicture(StagingListingPicture $picture): void
{
$pictureExists = false;
foreach ($this->pictures as $existingPicture) {
if ($existingPicture->getUrl() === $picture->getUrl()) {
$pictureExists = true;
break;
}
}
if (!$pictureExists) {
$this->pictures[] = $picture;
}
}
}
@@ -0,0 +1,126 @@
<?php
namespace Xentral\Modules\Ebay\Data;
class StockLoggingData
{
/** @var string */
protected $itemId;
/** @var string */
protected $sku;
/** @var int */
protected $quantity;
/** @var string */
protected $status;
/** @var StockLogingVariationData[] */
protected $variations = [];
/** @var string[] */
protected $errorMessages = [];
public function __construct(string $itemId)
{
$this->itemId = $itemId;
}
public function getItemId(): string
{
return $this->itemId;
}
public function setItemId(string $itemId): StockLoggingData
{
$this->itemId = $itemId;
return $this;
}
public function getSku(): string
{
return $this->sku;
}
public function setSku(string $sku): StockLoggingData
{
$this->sku = $sku;
return $this;
}
public function getQuantity(): ?int
{
return $this->quantity;
}
public function setQuantity(int $quantity): StockLoggingData
{
$this->quantity = $quantity;
return $this;
}
public function getStatus(): string
{
return $this->status;
}
public function setStatus(string $status): StockLoggingData
{
$this->status = $status;
return $this;
}
public function getVariations(): array
{
return $this->variations;
}
public function setVariations(array $variations): StockLoggingData
{
$this->variations = $variations;
return $this;
}
public function addVariation(StockLogingVariationData $variation): StockLoggingData
{
$this->variations[$variation->getSku()] = $variation;
return $this;
}
public function hasVariations(): bool
{
return !empty($this->variations);
}
public function getVariation(string $sku): StockLoggingVariationData
{
return $this->variations[$sku];
}
public function getErrorMessages(): array
{
return $this->errorMessages;
}
public function setErrorMessages(array $errorMessages): StockLoggingData
{
$this->errorMessages = $errorMessages;
return $this;
}
public function hasErrorMessages(): bool
{
return !empty($this->errorMessages);
}
public function addErrorMessage(string $type, string $errorMessage): StockLoggingData
{
$this->errorMessages[$errorMessage] = $type;
return $this;
}
}
@@ -0,0 +1,43 @@
<?php
namespace Xentral\Modules\Ebay\Data;
class StockLogingVariationData
{
/** @var string */
protected $sku;
/** @var int */
protected $quantity;
public function __construct(string $sku, int $quantity)
{
$this->sku = $sku;
$this->quantity = $quantity;
}
public function getSku(): string
{
return $this->sku;
}
public function setSku(string $sku): StockLogingVariationData
{
$this->sku = $sku;
return $this;
}
public function getQuantity(): int
{
return $this->quantity;
}
public function setQuantity(int $quantity): StockLogingVariationData
{
$this->quantity = $quantity;
return $this;
}
}
+113
View File
@@ -0,0 +1,113 @@
<?php
declare(strict_types=1);
namespace Xentral\Modules\Ebay\Data;
final class TokenData
{
/** @var string */
protected $token;
/** @var string */
protected $refreshToken;
/** @var string */
protected $type;
/** @var bool */
protected $valid;
/**
* TokenData constructor.
*
* @param string $token
* @param string $refreshToken
* @param string $type
* @param bool $valid
*/
public function __construct(string $token, string $refreshToken, string $type, bool $valid)
{
$this->token = $token;
$this->refreshToken = $refreshToken;
$this->type = $type;
$this->valid = $valid;
}
/**
* @return string
*/
public function getToken(): string
{
return $this->token;
}
/**
* @param string $token
*
* @return TokenData
*/
public function setToken(string $token): TokenData
{
$this->token = $token;
return $this;
}
/**
* @return string
*/
public function getRefreshToken(): string
{
return $this->refreshToken;
}
/**
* @param string $refreshToken
*
* @return TokenData
*/
public function setRefreshToken(string $refreshToken): TokenData
{
$this->refreshToken = $refreshToken;
return $this;
}
/**
* @return string
*/
public function getType(): string
{
return $this->type;
}
/**
* @param string $type
*
* @return TokenData
*/
public function setType(string $type): TokenData
{
$this->type = $type;
return $this;
}
/**
* @return bool
*/
public function isValid(): bool
{
return $this->valid;
}
/**
* @param bool $valid
*
* @return TokenData
*/
public function setValid(bool $valid): TokenData
{
$this->valid = $valid;
return $this;
}
}
@@ -0,0 +1,9 @@
<?php
namespace Xentral\Modules\Ebay\Exception;
use Xentral\Core\Exception\ModuleExceptionInterface;
interface EbayExceptionInterface extends ModuleExceptionInterface
{
}
@@ -0,0 +1,9 @@
<?php
namespace Xentral\Modules\Ebay\Exception;
use InvalidArgumentException as SplInvalidArgumentException;
class InvalidArgumentException extends SplInvalidArgumentException implements EbayExceptionInterface
{
}
@@ -0,0 +1,9 @@
<?php
namespace Xentral\Modules\Ebay\Exception;
use UnexpectedValueException as SplUnexpectedValueException;
class MissingValueException extends SplUnexpectedValueException implements EbayExceptionInterface
{
}
@@ -0,0 +1,37 @@
<?php
namespace Xentral\Modules\Ebay\Exception;
use RuntimeException;
final class ValidationFailedException extends RuntimeException implements EbayExceptionInterface
{
/** @var array $errors */
private $errors = [];
/**
* @param array $errors
*
* @return self
*/
public static function fromErrors(array $errors): self
{
$errorString = '';
foreach ($errors as $propertyName => $propertyErrors) {
$errorString .= implode("\r\n", $propertyErrors);
}
$exception = new self('Validation failed with following errors: ' . "\n\n" . $errorString);
$exception->errors = $errors;
return $exception;
}
/**
* @return array
*/
public function getErrors(): array
{
return $this->errors;
}
}
@@ -0,0 +1,9 @@
<?php
namespace Xentral\Modules\Ebay\Exception;
use RuntimeException as SplRuntimeException;
class ValueNotFoundException extends SplRuntimeException implements EbayExceptionInterface
{
}
@@ -0,0 +1,356 @@
<?php
declare(strict_types=1);
namespace Xentral\Modules\Ebay\Gateway;
use Xentral\Components\Database\Database;
use Xentral\Modules\Ebay\Data\StagingListingData;
use Xentral\Modules\Ebay\Data\StagingListingVariationData;
use Xentral\Modules\Ebay\Exception\InvalidArgumentException;
use Xentral\Modules\Ebay\Exception\ValidationFailedException;
final class EbayListingGateway
{
/** @var Database $db */
private $db;
/**
* @param Database $database
*/
public function __construct(Database $database)
{
$this->db = $database;
}
/**
* @param int $itemId
*
* @throws ValidationFailedException
*
* @return int
*/
public function tryGetStagingListingIdByItemId($itemId): int
{
$sql = 'SELECT e.*
FROM `ebay_staging_listing` AS `e`
WHERE e.item_id_external = :item_id';
$values = ['item_id' => $itemId];
$stagingListingData = $this->db->fetchRow($sql, $values);
if (empty($stagingListingData)) {
return 0;
}
return $stagingListingData['id'];
}
/**
* @param int $id
*
* @throws InvalidArgumentException
* @throws ValidationFailedException
*
* @return StagingListingData
*/
public function getStagingListingByDatabaseId(int $id): StagingListingData
{
$sql = 'SELECT e.*
FROM `ebay_staging_listing` AS `e`
WHERE e.id = :listing_id';
$values = ['listing_id' => $id];
$stagingListingData = $this->db->fetchRow($sql, $values);
if (empty($stagingListingData)) {
throw new InvalidArgumentException('Required argument "id" is empty or invalid.');
}
$stagingListing = StagingListingData::fromDbState($stagingListingData);
$stagingListing->setVariations($this->getStagingListingVariations($stagingListing->getId()));
$validationErrors = $stagingListing->validate();
if (!empty($validationErrors)) {
throw ValidationFailedException::fromErrors($validationErrors);
}
return $stagingListing;
}
/**
* @param int $stagingListingId
*
* @throws InvalidArgumentException
* @return array
*/
protected function getStagingListingVariations(int $stagingListingId): array
{
if (empty($stagingListingId)) {
throw new InvalidArgumentException('Required argument "stagingListingId" is empty or invalid.');
}
$variations = [];
$sql = 'SELECT e.id, e.sku, e.ebay_staging_listing_id, e.article_id
FROM `ebay_staging_listing_variant` AS `e`
WHERE e.ebay_staging_listing_id = :listing_id';
$values = ['listing_id' => $stagingListingId];
$stagingListingVariations = $this->db->fetchAll($sql, $values);
if (empty($stagingListingVariations)) {
return $variations;
}
foreach ($stagingListingVariations as $variation) {
$sql = 'SELECT e.property, e.value
FROM ebay_staging_listing_variant_specific AS `e`
WHERE e.ebay_staging_listing_variant_id = :variation_id';
$values = ['variation_id' => $variation['id']];
$allSpecificsInDatabase = $this->db->fetchAll($sql, $values);
$specifics = [];
foreach ($allSpecificsInDatabase as $specificsInDatabase) {
$specifics[$specificsInDatabase['property']] = $specificsInDatabase['value'];
}
$stagingListingVariation = StagingListingVariationData::fromArray($variation, $specifics);
$variations[] = $stagingListingVariation;
}
return $variations;
}
/**
* @param int $shopId
*
* @return array
*/
public function getPaymentBusinessPolicies($shopId): array
{
return $this->findBusinessPolicies($shopId, 'PAYMENT');
}
/**
* @param int $shopId
* @param string $type
*
* @throws InvalidArgumentException
*
* @return array
*/
protected function findBusinessPolicies($shopId, $type): array
{
if (empty($shopId)) {
throw new InvalidArgumentException('Required argument "shopId" is empty or invalid.');
}
if (empty($type)) {
throw new InvalidArgumentException('Required argument "type" is empty.');
}
$sql = 'SELECT
e.id, e.aktiv AS active, e.profilid AS profile_id_external,
e.profilname AS profile_name, e.profilsummary AS profile_summary
FROM ebay_rahmenbedingungen AS `e`
WHERE e.shop=:shopid AND e.profiltype=:profiltype';
$values = [
'shopid' => $shopId,
'profiltype' => $type,
];
return $this->db->fetchAll($sql, $values);
}
/**
* @param int $shopId
*
* @return array
*/
public function getShippingBusinessPolicies($shopId): array
{
return $this->findBusinessPolicies($shopId, 'SHIPPING');
}
/**
* @param int $shopId
*
* @return array
*/
public function getReturnBusinessPolicies($shopId): array
{
return $this->findBusinessPolicies($shopId, 'RETURN_POLICY');
}
/**
* @return array
*/
public function getTemplates(): array
{
$sql = 'SELECT e.id, e.bezeichnung AS template_name
FROM `ebay_template` AS `e`
WHERE e.aktiv = 1';
return $this->db->fetchAll($sql);
}
/**
* @param int $shopId
*
* @throws InvalidArgumentException
* @return array
*/
public function getStoreCategories(int $shopId): array
{
if ($shopId <= 0) {
throw new InvalidArgumentException('Required argument "shopId" is invalid.');
}
$sql = 'SELECT e.id, e.kategorie AS `category_id_external`, e.bezeichnung AS description
FROM `ebay_storekategorien` AS `e`
WHERE shop = :shop_id';
$values = ['shop_id' => $shopId];
return $this->db->fetchAll($sql, $values);
}
/**
* @param StagingListingData $stagingListing
*
* @return int
*/
public function searchForMatchingArticleId(StagingListingData $stagingListing): int
{
if (!empty($stagingListing->getArticleId())) {
return $stagingListing->getArticleId();
}
//associate by listing id in foreign numbers
$sql =
"SELECT af.artikel
FROM `artikelnummer_fremdnummern` AS `af`
JOIN `artikel` AS `a` ON af.artikel = a.id
WHERE af.shopid = :shop_id AND LOWER(af.bezeichnung) = 'ebaylisting' AND af.nummer = :article_number
AND a.nummer <> 'DEL' AND a.geloescht = 0 AND a.intern_gesperrt = 0
LIMIT 1";
$values = [
'shop_id' => $stagingListing->getShopId(),
'article_number' => $stagingListing->getItemId(),
];
$articleId = $this->db->fetchValue($sql, $values);
if (empty($articleId)) {
//associate by sku
$sql = 'SELECT a.id
FROM `artikel` AS `a`
WHERE a.geloescht = 0 AND a.intern_gesperrt=0 AND a.nummer=:article_number';
$values = ['article_number' => $stagingListing->getSku()];
$articleId = $this->db->fetchValue($sql, $values);
}
if (empty($articleId)) {
//associate by foreign number
$sql = "SELECT a.id
FROM `artikel` AS `a`
JOIN `artikelnummer_fremdnummern` AS `af` ON af.artikel = a.id
WHERE a.geloescht = 0 AND a.intern_gesperrt = 0 AND a.nummer <> ''
AND af.aktiv = 1 AND (LOWER(af.bezeichnung) = 'sku' OR LOWER(af.bezeichnung) = 'bestandseinheit')
AND af.nummer = :article_number AND (af.shopid = :shop_id OR af.shopid = 0)
ORDER BY af.shopid DESC LIMIT 1";
$values = [
'article_number' => $stagingListing->getSku(),
'shop_id' => $stagingListing->getShopId(),
];
$articleId = $this->db->fetchValue($sql, $values);
}
return (int)$articleId;
}
/**
* @param StagingListingVariationData $variation
* @param int $shopId
* @param int $parentArticleId
*
* @return int
*/
public function searchForMatchingArticleIdForVariation(
StagingListingVariationData $variation,
int $shopId,
int $parentArticleId
): int {
if (!empty($variation->getArticleId())) {
return $variation->getArticleId();
}
//associate by sku
$sql = 'SELECT a.id
FROM `artikel` AS a
WHERE a.geloescht = 0 AND a.intern_gesperrt = 0 AND a.nummer = :article_number';
$values = ['article_number' => $variation->getSku()];
$articleId = $this->db->fetchValue($sql, $values);
if (empty($articleId)) {
//associate by foreign number
$sql = "SELECT a.id
FROM `artikel` AS `a`
JOIN `artikelnummer_fremdnummern` AS `af` ON af.artikel = a.id
WHERE a.geloescht = 0 AND a.intern_gesperrt = 0 AND a.nummer <> ''
AND af.aktiv = 1 AND (LOWER(af.bezeichnung) = 'sku' OR LOWER(af.bezeichnung) = 'bestandseinheit')
AND af.nummer = :sku AND (af.shopid = :shop_id OR af.shopid = 0)
ORDER BY af.shopid DESC LIMIT 1";
$values = [
'sku' => $variation->getSku(),
'shop_id' => $shopId,
];
$articleId = $this->db->fetchValue($sql, $values);
}
if (empty($articleId)) {
//associate by matrix combination
$specifics = [];
foreach ($variation->listSpecifics() as $dimension => $value) {
$specifics[] = sprintf('(ma.name = %s AND mea.name = %s)',
$this->db->escapeString($dimension),
$this->db->escapeString($value));
}
$query = sprintf(
'
SELECT x.artikel AS `artikelId`
FROM (
SELECT moza.artikel
FROM `matrixprodukt_eigenschaftengruppen_artikel` AS `ma`
JOIN `matrixprodukt_eigenschaftenoptionen_artikel` AS `mea` on ma.id = mea.gruppe
JOIN `matrixprodukt_optionen_zu_artikel` AS `moza` ON moza.option_id = mea.id
WHERE ma.artikel = %d AND (%s)
) AS `x`
GROUP BY x.artikel
HAVING COUNT(x.artikel) = %d',
$parentArticleId,
implode(' OR ', $specifics),
count($specifics)
);
$foundCombinations = $this->db->fetchAll($query);
if (count($foundCombinations) === 1) {
$articleId = $foundCombinations[0]['artikelId'];
}
}
return (int)$articleId;
}
/**
* @param int $articleId
* @param int $shopId
*
* @return bool
*/
public function existsStagingListingsForArticleId(int $articleId, int $shopId): bool
{
$sql = "SELECT e.id
FROM `ebay_staging_listing` AS `e`
LEFT JOIN `ebay_staging_listing_variant` AS `v` ON e.id = v.ebay_staging_listing_id
WHERE (e.article_id = :article_id or v.article_id = :article_id) AND e.shop_id = :shop_id AND e.status = 'Aktiv'";
$values = [
'article_id' => $articleId,
'shop_id' => $shopId
];
$stagingId = $this->db->fetchValue($sql, $values);
return !empty($stagingId);
}
}
@@ -0,0 +1,163 @@
<?php
declare(strict_types=1);
namespace Xentral\Modules\Ebay\Gateway;
use Xentral\Components\Database\Database;
use Xentral\Modules\Ebay\Client\EbayRestApiClient;
use Xentral\Modules\Ebay\Data\AccountCredentialsData;
use Xentral\Modules\Ebay\Data\TokenData;
use Xentral\Modules\Ebay\Exception\ValueNotFoundException;
class EbayRestApiGateway
{
/** @var Database $db */
private $db;
/**
* @param Database $database
*/
public function __construct(Database $database)
{
$this->db = $database;
}
/**
* @param int $shopexportId
*
* @throws ValueNotFoundException
*
* @return AccountCredentialsData
*/
public function getAccountCredentials(int $shopexportId): AccountCredentialsData
{
$settings = $this->getShopSettings($shopexportId);
return new AccountCredentialsData(
(string)$settings['felder']['appID'],
(string)$settings['felder']['certID'],
(string)$settings['felder']['ruName']
);
}
protected function getShopSettings(int $shopexportId): array
{
$sql = 'SELECT `einstellungen_json` FROM `shopexport` WHERE `id` = :shopexport_id';
$values = [
'shopexport_id' => $shopexportId,
];
$encodedSettings = $this->db->fetchValue($sql, $values);
if (empty($encodedSettings)) {
throw new ValueNotFoundException('No settings were found for given shopexport Id: ' . $shopexportId);
}
return json_decode($encodedSettings, true);
}
public function useRestApiForOrderImport(int $shopexportId): bool
{
$settings = $this->getShopSettings($shopexportId);
$userEnabledSetting = (bool)$settings['felder']['useRestApiOnOrderImport'];
$restApiTokenExists = $this->tryGetRestApiAccessTokenFromDatabase(
$shopexportId,
EbayRestApiClient::TOKEN_TYPE_USER
) !== null;
return $userEnabledSetting && $restApiTokenExists;
}
/**
* @param int $shopexportId
* @param string $type
*
* @return TokenData|null
*/
public function tryGetRestApiAccessTokenFromDatabase(int $shopexportId, string $type): ?TokenData
{
$sql = "SELECT e.token, e.type, e.refresh_token, (e.valid_until > NOW()) AS valid
FROM `ebay_rest_token` AS `e`
WHERE e.shopexport_id = :shopexport_id
AND e.type = :type
LIMIT 1";
$values = [
'shopexport_id' => $shopexportId,
'type' => $type,
];
$data = $this->db->fetchAll($sql, $values);
$data = reset($data);
if (empty($data['token'])) {
return null;
}
return new TokenData(
$data['token'],
$data['refresh_token'],
$data['type'],
(bool)$data['valid']
);
}
/**
* @param int $shopexportId
*
* @throws ValueNotFoundException
*
* @return int
*/
public function getSiteId(int $shopexportId): int
{
$sql = 'SELECT `einstellungen_json` FROM `shopexport` WHERE `id` = :shopexport_id';
$values = [
'shopexport_id' => $shopexportId,
];
$encodedSettings = $this->db->fetchValue($sql, $values);
if (empty($encodedSettings)) {
throw new ValueNotFoundException('No settings were found for given shopexport Id: ' . $shopexportId);
}
$settings = json_decode($encodedSettings, true);
if (empty($settings['felder']['siteID'])) {
throw new ValueNotFoundException('Site Id value missing for given shopexport Id:' . $shopexportId);
}
return (int)$settings['felder']['siteID'];
}
public function existsRestOrderInDatabase(string $orderId): bool
{
$sql = 'SELECT `id` FROM `ebay_rest_orders` WHERE `order_id` = :order_id';
$values = [
'order_id' => $orderId,
];
return $this->db->fetchValue($sql, $values) > 0;
}
public function countRestOrdersToImport(int $shopexportId): int
{
$sql = 'SELECT COUNT(id) FROM `ebay_rest_orders` WHERE `processed` = 0 AND `shopexport_id` = :shopexport_id';
$values = [
'shopexport_id' => $shopexportId,
];
return (int)$this->db->fetchValue($sql, $values);
}
public function getNextOrderToImport(int $shopexportId): string
{
$sql = 'SELECT `order_data` FROM `ebay_rest_orders`
WHERE `processed` = 0 AND `shopexport_id` = :shopexport_id
ORDER BY `date_of_order` ASC
LIMIT 1';
$values = [
'shopexport_id' => $shopexportId,
];
return (string)$this->db->fetchValue($sql, $values);
}
}
@@ -0,0 +1,166 @@
<?php
declare(strict_types=1);
namespace Xentral\Modules\Ebay\Module;
use DateTime;
use Xentral\Modules\Ebay\Client\EbayRestApiClient;
use Xentral\Modules\Ebay\Data\AccountCredentialsData;
use Xentral\Modules\Ebay\Data\TokenData;
use Xentral\Modules\Ebay\Gateway\EbayRestApiGateway;
use Xentral\Modules\Ebay\Service\EbayRestApiService;
final class EbayRestApiModule
{
/** @var EbayRestApiClient $client */
private $client;
/** @var EbayRestApiGateway $gateway */
private $gateway;
/** @var EbayRestApiService $service */
private $service;
/**
* @param EbayRestApiClient $client
* @param EbayRestApiGateway $gateway
* @param EbayRestApiService $service
*/
public function __construct(EbayRestApiClient $client, EbayRestApiGateway $gateway, EbayRestApiService $service)
{
$this->client = $client;
$this->gateway = $gateway;
$this->service = $service;
}
/**
* @param int $shopexportId
* @param int $categoryId
*
* @return array
*/
public function getCategorySpecificProperties(int $shopexportId, int $categoryId): array
{
$siteId = $this->gateway->getSiteId($shopexportId);
$token = $this->getRestApiApplicationAccessToken($shopexportId);
return $this->client->getCategorySpecificProperties($siteId, $categoryId, $token);
}
/**
* @param int $shopexportId
*
* @return string|null
*/
public function getRestApiApplicationAccessToken(int $shopexportId): ?string
{
$credentials = $this->gateway->getAccountCredentials($shopexportId);
$ebayResponse = $this->client->getRestApiApplicationAccessTokenFromEbay(
$credentials
);
$this->service->saveRestApiAccessToken($shopexportId, $ebayResponse);
return $ebayResponse['access_token'];
}
public function getOrders(int $shopexportId, DateTime $dateFrom, int $offset, int $limit = null): array
{
$token = $this->getRestApiUserAccessToken($shopexportId);
return $this->client->getOrders($token, $dateFrom, $offset, $limit);
}
public function getRestApiUserAccessToken(int $shopexportId): string
{
$tokenData = $this->gateway->tryGetRestApiAccessTokenFromDatabase(
$shopexportId,
EbayRestApiClient::TOKEN_TYPE_USER
);
if ($tokenData === null) {
//TODO EXCEPTION
throw new \RuntimeException('Token Request Failure');
}
if ($tokenData->isValid()) {
return $tokenData->getToken();
}
$tokenData = $this->renewToken($shopexportId, $tokenData);
//TODO Exception
return $tokenData->getToken();
}
protected function renewToken(int $shopexportId, TokenData $tokenData): TokenData
{
$response = $this->client->renewToken(
$this->gateway->getAccountCredentials($shopexportId),
$tokenData
);
$tokenData->setToken($response['access_token']);
$this->service->renewToken($shopexportId, (int)$response['expires_in'], $tokenData);
return $tokenData;
}
public function fetchRestApiUserAccessToken(int $shopexportId, string $requestCode): ?string
{
$credentials = $this->gateway->getAccountCredentials($shopexportId);
$ebayResponse = $this->client->getRestApiUserAccessTokenFromEbay(
$credentials,
$requestCode
);
$this->service->saveRestApiAccessToken($shopexportId, $ebayResponse);
if (!isset($ebayResponse['access_token'])) {
return null;
}
return $ebayResponse['access_token'];
}
public function getCompleteApiScope(): array
{
return $this->client->getCompleteScope();
}
public function getAccountCredentials($shopexportId): AccountCredentialsData
{
return $this->gateway->getAccountCredentials($shopexportId);
}
public function useRestApiForOrderImport(int $shopexportId): bool
{
return $this->gateway->useRestApiForOrderImport($shopexportId);
}
public function saveRestOrder(int $shopexportId, DateTime $orderDate, string $orderId, string $orderData): void
{
$this->service->saveRestOrder($shopexportId, $orderDate, $orderId, $orderData);
}
public function countRestOrdersToImport(int $shopexportId): int
{
return $this->gateway->countRestOrdersToImport($shopexportId);
}
public function getNextOrderToImport(int $shopexportId): string
{
return $this->gateway->getNextOrderToImport($shopexportId);
}
public function setRestOrderToProcessed(string $orderId): void
{
$this->service->setRestOrderToProcessed($orderId);
}
public function deleteRestOrderFromDatabase(string $orderId): void
{
$this->service->deleteRestOrderFromDatabase($orderId);
}
public function existsRestOrderInDatabase(string $orderId): bool
{
return $this->gateway->existsRestOrderInDatabase($orderId);
}
}
@@ -0,0 +1,448 @@
<?php
declare(strict_types=1);
namespace Xentral\Modules\Ebay\Service;
use Xentral\Components\Database\Database;
use Xentral\Modules\Ebay\Data\StagingListingData;
use Xentral\Modules\Ebay\Data\StagingListingPicture;
use Xentral\Modules\Ebay\Data\StagingListingVariationData;
use Xentral\Modules\Ebay\Exception\InvalidArgumentException;
use Xentral\Modules\Ebay\Exception\MissingValueException;
use Xentral\Modules\Ebay\Gateway\EbayListingGateway;
use Xentral\Modules\Ebay\Wrapper\EbayStockCalculationWrapperInterface;
use Xentral\Modules\Ebay\Wrapper\StockCalculationWrapper;
final class EbayListingService
{
/** @var Database $db */
private $db;
/** @var EbayListingGateway $gateway */
private $gateway;
/** @var EbayListingXmlSerializer $serializer */
private $serializer;
/** @var StockCalculationWrapper $stockCalculationWrapper */
private $stockCalculationWrapper;
/**
* @param EbayListingGateway $gateway
* @param Database $database
* @param EbayListingXmlSerializer $serializer
* @param EbayStockCalculationWrapperInterface $wrapper
*/
public function __construct(
EbayListingGateway $gateway,
Database $database,
EbayListingXmlSerializer $serializer,
EbayStockCalculationWrapperInterface $wrapper
) {
$this->gateway = $gateway;
$this->db = $database;
$this->serializer = $serializer;
$this->stockCalculationWrapper = $wrapper;
}
/**
* @param int $stagingListingId
*
* @return StagingListingData
*/
public function associateStagingListing($stagingListingId): StagingListingData
{
$stagingListing = $this->gateway->getStagingListingByDatabaseId($stagingListingId);
$stagingListing = $this->associateArticle($stagingListing);
$stagingListing = $this->associateVariants($stagingListing);
$this->saveStagingListing($stagingListing);
return $stagingListing;
}
/**
* @param StagingListingData $stagingListing
*
* @return StagingListingData
*/
private function associateArticle(StagingListingData $stagingListing): StagingListingData
{
$articleId = $this->gateway->searchForMatchingArticleId($stagingListing);
$stagingListing->setArticleId($articleId);
return $stagingListing;
}
/**
* @param StagingListingData $stagingListing
*
* @return StagingListingData
*/
private function associateVariants(StagingListingData $stagingListing): StagingListingData
{
if (empty($stagingListing->getArticleId()) || empty($stagingListing->getVariations())) {
return $stagingListing;
}
foreach ($stagingListing->getVariations() as $variation) {
$articleId = $this->gateway->searchForMatchingArticleIdForVariation(
$variation,
$stagingListing->getShopId(),
$stagingListing->getArticleId()
);
$variation->setArticleId($articleId);
}
return $stagingListing;
}
/**
* @param StagingListingData $stagingListing
*
* @throws MissingValueException
*
* @return StagingListingData
*/
public function saveStagingListing($stagingListing): StagingListingData
{
$stagingListingId = $stagingListing->getId();
if (empty($stagingListingId)) {
$query = sprintf(
'INSERT INTO `ebay_staging_listing` (`shop_id`) VALUES (%d)',
$stagingListing->getShopId()
);
$this->db->exec($query);
$stagingListingId = $this->db->lastInsertId();
}
if(!empty($stagingListingId)){
$sql = 'SELECT esl.id FROM `ebay_staging_listing` AS `esl` WHERE esl.id=:id';
$stagingListingId = $this->db->fetchValue($sql,['id' => $stagingListingId]);
}
if (empty($stagingListingId)) {
throw new MissingValueException('ID for Staging Listing dataset could not be found or created.');
}
$sql = 'UPDATE `ebay_staging_listing` SET
`article_id` = :article_id,
`type` = :listing_type,
`title` = :title,
`status` = :listing_status,
`sku` = :sku,
`description` = :description,
`ebay_primary_category_id_external` = :primary_category,
`ebay_secondary_category_id_external` = :secondary_category,
`ebay_primary_store_category_id_external` = :primary_store_category,
`ebay_secondary_store_category_id_external` = :secondary_store_category,
`ebay_shipping_profile_id_external` = :shipping_profile,
`ebay_payment_profile_id_external` = :payment_profile,
`ebay_return_profile_id_external` = :return_profile,
`ebay_plus` = :ebayplus,
`ebay_price_suggestion` = :price_suggestion,
`ebay_private_listing` = :private_listing,
`condition_id_external` = :condition_id,
`condition_display_name` = :condition_display_name,
`condition_description` = :condition_description,
`listing_duration` = :listing_duration,
`inventory_tracking_method` = :inventory_tracking_method,
`item_id_external` = :item_id,
`delivery_time` = :delivery_time,
`template_id` = :template_id
WHERE id = :listing_id';
$values = [
'article_id' => $stagingListing->getArticleId(),
'listing_type' => $stagingListing->getType(),
'title' => $stagingListing->getTitle(),
'listing_status' => $stagingListing->getStatus(),
'sku' => $stagingListing->getSku(),
'description' => $stagingListing->getDescription(),
'primary_category' => $stagingListing->getPrimaryCategoryId(),
'secondary_category' => $stagingListing->getSecondaryCategoryId(),
'primary_store_category' => $stagingListing->getPrimaryStoreCategoryId(),
'secondary_store_category' => $stagingListing->getSecondaryStoreCategoryId(),
'shipping_profile' => $stagingListing->getShippingProfileId(),
'payment_profile' => $stagingListing->getPaymentProfileId(),
'return_profile' => $stagingListing->getReturnProfileId(),
'ebayplus' => $stagingListing->isEbayPlus(),
'price_suggestion' => $stagingListing->isPriceSuggestion(),
'private_listing' => $stagingListing->isPrivateListing(),
'condition_id' => $stagingListing->getConditionId(),
'condition_display_name' => $stagingListing->getConditionDisplayName(),
'condition_description' => $stagingListing->getConditionDescription(),
'listing_duration' => $stagingListing->getListingDuration(),
'inventory_tracking_method' => $stagingListing->getInventoryTrackingMethod(),
'item_id' => $stagingListing->getItemId(),
'delivery_time' => $stagingListing->getDeliveryTime(),
'template_id' => $stagingListing->getTemplateId(),
'listing_id' => $stagingListingId,
];
$this->db->perform($sql, $values);
foreach ($stagingListing->listSpecifics() as $specificName => $specificValue) {
$this->saveSpecific($stagingListingId, $specificName, $specificValue);
}
if (!empty($stagingListing->getVariations())) {
foreach ($stagingListing->getVariations() as $variation) {
$this->saveStagingListingVariation($stagingListingId, $variation);
}
}
if (!empty($stagingListing->listPictures())) {
foreach ($stagingListing->listPictures() as $picture) {
$picture->setStagingListingId($stagingListingId);
$this->savePictureHostingServicePicture($picture);
}
}
return $this->gateway->getStagingListingByDatabaseId($stagingListingId);
}
/**
* @param int $stagingListingId
* @param string $specificName
* @param string $specificValue
*
* @throws InvalidArgumentException
*/
private function saveSpecific($stagingListingId, $specificName, $specificValue): void
{
if (empty($specificName)) {
throw new InvalidArgumentException('Required argument "specificName" is empty or invalid.');
}
if (empty($stagingListingId)) {
throw new InvalidArgumentException('Required argument "stagingListingId" is empty or invalid.');
}
$sql = 'SELECT esls.id FROM `ebay_staging_listing_specific` AS `esls`
WHERE `ebay_staging_listing_id` = :listing_id AND `property` = :specific_name';
$values = [
'listing_id' => $stagingListingId,
'specific_name' => $specificName,
];
$specificId = $this->db->fetchValue($sql, $values);
$sql = 'UPDATE `ebay_staging_listing_specific` SET `value` = :value WHERE `id` = :specific_id';
$values = [
'value' => $specificValue,
'specific_id' => $specificId,
];
if (empty($specificId)) {
$sql = 'INSERT INTO `ebay_staging_listing_specific`
(`ebay_staging_listing_id`, `property`, `value`) VALUES (:listing_id, :specific_name, :value)';
$values = [
'listing_id' => $stagingListingId,
'specific_name' => $specificName,
'value' => $specificValue,
];
}
$this->db->perform($sql, $values);
}
/**
* @param int $stagingListingId
* @param StagingListingVariationData $variation
*/
private function saveStagingListingVariation($stagingListingId, $variation): void
{
$specifics = $variation->listSpecifics();
$conditions = [];
foreach ($specifics as $property => $value) {
$conditions[] = sprintf(
'(eslvs.property = %s AND eslvs.value = %s)',
$this->db->escapeString($property),
$this->db->escapeString($value)
);
}
$condition = implode(' OR ', $conditions);
if (empty($conditions)) {
$condition = 0;
}
$query = sprintf(
'
SELECT eslv.id
FROM `ebay_staging_listing_variant_specific` AS `eslvs`
JOIN `ebay_staging_listing_variant` AS `eslv` ON eslv.id = eslvs.ebay_staging_listing_variant_id
WHERE eslv.ebay_staging_listing_id = %s AND (%s)
GROUP BY eslv.id
HAVING COUNT(eslv.id) = %d',
$stagingListingId,
$condition,
count($conditions)
);
$stagingListingVariationId = $this->db->fetchValue($query);
if (empty($stagingListingVariationId)) {
$query = 'INSERT INTO `ebay_staging_listing_variant` () VALUES ()';
$this->db->exec($query);
$stagingListingVariationId = $this->db->lastInsertId();
}
$sql = 'UPDATE `ebay_staging_listing_variant` SET
`article_id` = :article_id, `sku` = :sku, `ebay_staging_listing_id` = :listing_id
WHERE id = :variation_id';
$values = [
'article_id' => $variation->getArticleId(),
'sku' => $variation->getSku(),
'listing_id' => $stagingListingId,
'variation_id' => $stagingListingVariationId,
];
$this->db->perform($sql, $values);
foreach ($specifics as $property => $value) {
$sql = 'SELECT `id`
FROM `ebay_staging_listing_variant_specific`
WHERE `ebay_staging_listing_variant_id`=:variation_id AND `property`=:property AND `value`=:value';
$values = [
'variation_id' => $stagingListingVariationId,
'property' => $property,
'value' => $value,
];
$specificMissing = empty($this->db->fetchValue($sql, $values));
if ($specificMissing) {
$sql = 'INSERT INTO `ebay_staging_listing_variant_specific`
(`ebay_staging_listing_variant_id`, `property`, `value`)
VALUES (:variation_id, :property, :value)';
$this->db->perform($sql, $values);
}
}
}
/**
* @param StagingListingPicture $picture
*/
private function savePictureHostingServicePicture(StagingListingPicture $picture): void
{
$sql = 'SELECT `id` FROM `ebay_picture_hosting_service`
WHERE `url`=:url
AND `ebay_staging_listing_id`=:listing_id
AND `ebay_staging_listing_variation_id`=:variation_id';
$values = [
'url' => $picture->getUrl(),
'listing_id' => $picture->getStagingListingId(),
'variation_id' => $picture->getStagingListingVariantId(),
];
$pictureId = $this->db->fetchValue($sql, $values);
if (!empty($pictureId)) {
return;
}
$sql = 'INSERT INTO `ebay_picture_hosting_service`
(`ebay_staging_listing_id`, `ebay_staging_listing_variation_id`, `file_id`, `url`)
VALUES
(:listing_id,:variation_id,:file_id,:url)';
$values = [
'listing_id' => $picture->getStagingListingId(),
'variation_id' => $picture->getStagingListingVariantId(),
'file_id' => $picture->getFileId(),
'url' => $picture->getUrl(),
];
$this->db->perform($sql, $values);
}
/**
* @param int $shopId
* @param object $item
*
* @return StagingListingData
*/
public function synchronizeItemData($shopId, $item): StagingListingData
{
$stagingListing = new StagingListingData($shopId);
$stagingListingId = $this->gateway->tryGetStagingListingIdByItemId((string)$item->ItemID);
if (!empty($stagingListingId)) {
$stagingListing = $this->gateway->getStagingListingByDatabaseId($stagingListingId);
}
$listingStatus = 'Aktiv';
if (!empty($item->ListingDetails->EndingReason)) {
$listingStatus = 'Beendet';
}
$stagingListing->setType((string)$item->ListingType);
$stagingListing->setTitle((string)$item->Title);
$stagingListing->setSku((string)$item->SKU);
$stagingListing->setDescription('');
$stagingListing->setPrimaryCategoryId((string)$item->PrimaryCategory->CategoryID);
$stagingListing->setPrimaryStoreCategoryId((string)$item->Storefront->StoreCategoryID);
$stagingListing->setSecondaryStoreCategoryId((string)$item->Storefront->StoreCategoryID2);
$stagingListing->setShippingProfileId((string)$item->SellerProfiles->SellerShippingProfile->ShippingProfileID);
$stagingListing->setPaymentProfileId((string)$item->SellerProfiles->SellerPaymentProfile->PaymentProfileID);
$stagingListing->setReturnProfileId((string)$item->SellerProfiles->SellerReturnProfile->ReturnProfileID);
$stagingListing->setDeliveryTime((string)$item->DispatchTimeMax);
$stagingListing->setItemId((string)$item->ItemID);
$stagingListing->setInventoryTrackingMethod((string)$item->InventoryTrackingMethod);
$stagingListing->setConditionId((string)$item->ConditionID);
$stagingListing->setListingDuration((string)$item->ListingDuration);
$stagingListing->setConditionDisplayName((string)$item->ConditionDisplayName);
$stagingListing->setConditionDescription((string)$item->ConditionDescription);
$stagingListing->setPrivateListing(strtolower((string)$item->PrivateListing) === 'true');
$stagingListing->setEbayPlus(strtolower((string)$item->eBayPlus) === 'true');
$stagingListing->setStatus($listingStatus);
if (!empty($item->ItemSpecifics->NameValueList)) {
$stagingListing->setSpecifics([]);
foreach ($item->ItemSpecifics->NameValueList as $itemSpecific) {
$stagingListing->addSpecific((string)$itemSpecific->Name, (string)$itemSpecific->Value);
}
}
if (!empty($item->PictureDetails->PictureURL)) {
foreach ($item->PictureDetails->PictureURL as $pictureUrl) {
$stagingListing->addPicture(
new StagingListingPicture(
str_replace('$_1.', '$_10.', (string)$pictureUrl)
)
);
}
}
if (!empty($item->Variations)) {
foreach ($item->Variations->Variation as $variation) {
$stagingListingVariation = new StagingListingVariationData();
$stagingListingVariation->setSku((string)$variation->SKU);
foreach ($variation->VariationSpecifics->NameValueList as $specifics) {
$stagingListingVariation->addSpecifics((string)$specifics->Name, (string)$specifics->Value);
}
$stagingListing->addVariation($stagingListingVariation);
}
}
return $this->saveStagingListing($stagingListing);
}
/**
* @param int $stagingId
*
* @return string
*/
public function getStockSyncBody($stagingId): string
{
$staging = $this->gateway->getStagingListingByDatabaseId($stagingId);
$stocksForArticles = [];
$stocksForArticles[$staging->getArticleId()] = $this->stockCalculationWrapper->calculateStock(
$staging->getArticleId(),
$staging->getShopId()
);
foreach ($staging->getVariations() as $variation) {
if (!empty($variation->getArticleId()) && !isset($stocksForArticles[$variation->getArticleId()])) {
$stocksForArticles[$variation->getArticleId()] = $this->stockCalculationWrapper->calculateStock(
$variation->getArticleId(),
$staging->getShopId()
);
}
}
return $this->serializer->createStockSyncXmlString($staging, $stocksForArticles);
}
}
@@ -0,0 +1,66 @@
<?php
namespace Xentral\Modules\Ebay\Service;
use Xentral\Modules\Ebay\Data\StagingListingData;
final class EbayListingXmlSerializer
{
/**
* @param StagingListingData $stagingListing
* @param array $stocksForArticles
*
* @return string
*/
public function createStockSyncXmlString(StagingListingData $stagingListing, $stocksForArticles): string
{
$syncBody = '<ReviseFixedPriceItemRequest version="1.0" xmlns="urn:ebay:apis:eBLBaseComponents">';
$syncBody .= '<Version>1137</Version>';
$syncBody .= '<MessageID>' . $stagingListing->getSku() . '</MessageID>';
$syncBody .= '<Item>
<ItemID>' . $stagingListing->getItemId() . '</ItemID>';
if (!empty($stagingListing->getVariations())) {
$syncBody .= '<Variations>';
foreach ($stagingListing->getVariations() as $variation) {
$quantity = 0;
if (!empty($variation->getArticleId()) && array_key_exists(
$variation->getArticleId(),
$stocksForArticles
)) {
$quantity = $stocksForArticles[$variation->getArticleId()];
$syncBody .= '
<Variation>';
$syncBody .= '<Quantity>' . $quantity . '</Quantity><SKU>' . $variation->getSku() . '</SKU>';
$syncBody .= '<VariationSpecifics>';
foreach ($variation->listSpecifics() as $specificName => $specificValue) {
$syncBody .= '
<NameValueList>';
$syncBody .= '<Name>' . $specificName . '</Name>';
$syncBody .= '<Value>' . $specificValue . '</Value>';
$syncBody .= '</NameValueList>';
}
$syncBody .= '</VariationSpecifics>';
$syncBody .= '</Variation>';
}
}
$syncBody .= '</Variations>';
} else {
$quantity = 0;
if (!empty($stagingListing->getArticleId()) && array_key_exists(
$stagingListing->getArticleId(),
$stocksForArticles
)) {
$quantity = $stocksForArticles[$stagingListing->getArticleId()];
}
$syncBody .= '<Quantity>' . $quantity . '</Quantity>';
}
$syncBody .= '</Item></ReviseFixedPriceItemRequest>';
return $syncBody;
}
}
@@ -0,0 +1,121 @@
<?php
declare(strict_types=1);
namespace Xentral\Modules\Ebay\Service;
use DateTime;
use Xentral\Components\Database\Database;
use Xentral\Modules\Ebay\Data\TokenData;
use Xentral\Modules\Ebay\Exception\InvalidArgumentException;
class EbayRestApiService
{
/** @var Database $db */
private $db;
/**
* @param Database $database
*/
public function __construct(Database $database)
{
$this->db = $database;
}
/**
* @param int $shopexportId
* @param array $ebayResponse
*/
public function saveRestApiAccessToken(int $shopexportId, array $ebayResponse): void
{
if (empty($shopexportId)) {
throw new InvalidArgumentException('Value for shopexport Id must not be empty');
}
if (empty($ebayResponse)) {
throw new InvalidArgumentException('eBay Response was empty');
}
$token = $ebayResponse['access_token'];
$refreshToken = $ebayResponse['refresh_token'];
$expiresInMinutes = $ebayResponse['expires_in'] / 60 - 5;
$validUntil = new DateTime();
$validUntil->modify(sprintf('+%d minutes', $expiresInMinutes));
if (empty($token)) {
throw new InvalidArgumentException('Value for token must not be empty');
}
$this->deleteRedundantToken($shopexportId, $ebayResponse['token_type']);
$query = sprintf(
'INSERT INTO `ebay_rest_token` (`shopexport_id`, `token`, `refresh_token`,`type`, `scope`, `valid_until`)
VALUES (%d, \'%s\', \'%s\', \'%s\', \'%s\',\'%s\')',
$shopexportId,
$token,
$refreshToken,
$ebayResponse['token_type'],
'',
$validUntil->format('Y-m-d H:i:s')
);
$this->db->exec($query);
}
public function deleteRedundantToken(int $shopexportId, string $type): void
{
$sql = 'DELETE FROM `ebay_rest_token` WHERE shopexport_id = :shopexport_id AND type = :type';
$values = [
'shopexport_id' => $shopexportId,
'type' => $type,
];
$this->db->perform($sql, $values);
}
public function renewToken(int $shopexportId, int $expiresInSeconds, TokenData $tokenData): void
{
$expiresInMinutes = $expiresInSeconds / 60 - 5; // reduce the actual valid time by 5 minutes to avoid a very specific edge case in which the token runs out the second it gets used
$validUntil = new DateTime();
$validUntil->modify(sprintf('+%d minutes', $expiresInMinutes));
$sql = 'UPDATE `ebay_rest_token` SET `token` = :token, `valid_until` = :valid_until
WHERE `shopexport_id` = :shopexport_id AND `type` = :type';
$values = [
'token' => $tokenData->getToken(),
'valid_until' => $validUntil->format('Y-m-d H:i:s'),
'shopexport_id' => $shopexportId,
'type' => $tokenData->getType(),
];
$this->db->perform($sql, $values);
}
public function saveRestOrder(int $shopexportId, DateTime $orderDate, string $orderId, string $orderData): void
{
$sql = 'INSERT INTO `ebay_rest_orders` (`date_of_order`, `order_data`, `shopexport_id`, `order_id`, `processed`)
VALUES (:date_of_order, :order_data, :shopexport_id, :order_id, 0)';
$values = [
'date_of_order' => $orderDate->format('Y-m-d H:i:s'),
'order_data' => $orderData,
'shopexport_id' => $shopexportId,
'order_id' => $orderId,
];
$this->db->perform($sql, $values);
}
public function setRestOrderToProcessed(string $orderId): void
{
$sql = 'UPDATE `ebay_rest_orders` SET `processed` = 1
WHERE `order_id` = :order_id';
$values = [
'order_id' => $orderId,
];
$this->db->perform($sql, $values);
}
public function deleteRestOrderFromDatabase(string $orderId): void
{
$sql = 'DELETE FROM `ebay_rest_orders` WHERE `order_id` = :order_id';
$values = [
'order_id' => $orderId,
];
$this->db->perform($sql, $values);
}
}
@@ -0,0 +1,63 @@
<?php
declare(strict_types=1);
namespace Xentral\Modules\Ebay\Service;
use Xentral\Components\Database\Database;
use Xentral\Modules\Ebay\Data\StockLoggingData;
final class EbayStockLoggingService
{
/** @var Database $db */
private $db;
/**
* @param Database $database
*/
public function __construct(Database $database)
{
$this->db = $database;
}
public function saveStockLoggingInformation(
int $shopId,
string $jobIdExternal,
StockLoggingData $stockLoggingData
): void {
$sql = 'INSERT INTO `ebay_stock_logging` (`shop_id`, `job_id_external`, `listing_id_external`,`sku`, `quantity`, `status`)
VALUES (:shop_id, :job_id_external, :listing_id_external, :sku, :quantity, :status)';
$values = [
'shop_id' => $shopId,
'job_id_external' => $jobIdExternal,
'listing_id_external' => $stockLoggingData->getItemId(),
'sku' => $stockLoggingData->getSku(),
'quantity' => $stockLoggingData->getQuantity(),
'status' => $stockLoggingData->getStatus(),
];
$this->db->perform($sql, $values);
$stockLoggingDataId = $this->db->lastInsertId();
foreach ($stockLoggingData->getVariations() as $variation) {
$sql = 'INSERT INTO `ebay_stock_logging_variations` (`ebay_stock_logging_id`, `sku`, `quantity`)
VALUES (:ebay_stock_logging_id, :sku, :quantity)';
$values = [
'ebay_stock_logging_id' => $stockLoggingDataId,
'sku' => $variation->getSku(),
'quantity' => $variation->getQuantity(),
];
$this->db->perform($sql, $values);
}
foreach ($stockLoggingData->getErrorMessages() as $errorMessage => $type) {
$sql = 'INSERT INTO `ebay_stock_logging_errors` (`ebay_stock_logging_id`, `message`, `type`)
VALUES (:ebay_stock_logging_id, :message, :type)';
$values = [
'ebay_stock_logging_id' => $stockLoggingDataId,
'message' => $errorMessage,
'type' => $type,
];
$this->db->perform($sql, $values);
}
}
}
@@ -0,0 +1,16 @@
<?php
declare(strict_types=1);
namespace Xentral\Modules\Ebay\Wrapper;
interface EbayStockCalculationWrapperInterface
{
/**
* @param int $articleId
* @param int $shopId
*
* @return float
*/
public function calculateStock($articleId, $shopId): float;
}
@@ -0,0 +1,129 @@
<?php
declare(strict_types=1);
namespace Xentral\Modules\Ebay\Wrapper;
use erpAPI;
use Xentral\Components\Database\Database;
/**
* Anti-Corruption-Layer für erpApi->ArtikelAnzahlVerkaufbar()
*/
final class StockCalculationWrapper implements EbayStockCalculationWrapperInterface
{
/** @var erpAPI $erp */
private $erp;
/**
* @var Database
*/
private $db;
/**
* @var array
*/
private $maximumStockForShop = [];
/**
* @param erpAPI $erp
* @param Database $db
*/
public function __construct($erp, $db)
{
$this->erp = $erp;
$this->db = $db;
}
/**
* @param int $articleId
* @param int $shopId
*
* @return float
*/
public function calculateStock($articleId, $shopId): float
{
if(empty($articleId)){
return 0;
}
$pseudoStorage = trim($this->erp->GetArtikelShopEinstellung('pseudolager', $articleId, $shopId));
if($pseudoStorage === '' && $this->erp->ModulVorhanden('pseudostorage')){
$values = ['shop_id' => $shopId];
$sql = 'SELECT p.formula FROM `pseudostorage_shop` AS `p` WHERE p.shop_id = :shop_id';
$pseudoStorage = trim($this->db->fetchValue($sql, $values));
}
if(!empty($pseudoStorage)) {
$this->erp->RunHook('remote_send_article_list_pseudostorage', 3, $shopId, $articleId, $pseudoStorage);
}
if ($pseudoStorage !== '') {
return $this->recalculateForMaximumStock($pseudoStorage > 0 ? floor($pseudoStorage) : 0, $shopId);
}
$sql = 'SELECT s.lagergrundlage FROM `shopexport` AS `s` WHERE s.id = :shop_id';
$values = ['shop_id' => $shopId];
$lagergrundlage = $this->db->fetchValue($sql, $values);
$sql = 'SELECT p.projektlager
FROM `projekt` AS `p`
JOIN `artikel` AS `a` ON p.id = a.projekt
WHERE a.id = :article_id';
$values = ['article_id' => $articleId];
$projektlager = $this->db->fetchValue($sql, $values);
$calculatedStock = (float)$this->erp->ArtikelAnzahlVerkaufbar(
$articleId,
0,
$projektlager,
$shopId,
$lagergrundlage
);
return $this->recalculateForMaximumStock($calculatedStock > 0 ? floor($calculatedStock) : 0, $shopId);
}
/**
* @param float $originalStock
* @param int $shopId
*
* @return float
*/
private function recalculateForMaximumStock(float $originalStock, int $shopId): float
{
if (!isset($this->maximumStockForShop[$shopId])) {
$this->getMaximumStockForShop($shopId);
}
if ($this->maximumStockForShop[$shopId] > 0) {
return $originalStock < $this->maximumStockForShop[$shopId]
? $originalStock : $this->maximumStockForShop[$shopId];
}
return $originalStock;
}
/**
* @param int $shopId
*/
private function getMaximumStockForShop(int $shopId): void
{
$sql = "SELECT s.einstellungen_json
FROM `shopexport` AS `s`
WHERE s.id = :shop_id
LIMIT 1";
$values = ['shop_id' => $shopId];
$importerSettings = $this->db->fetchValue($sql, $values);
$maximumStock = 0;
if (!empty(json_decode($importerSettings, true))) {
$importerSettings = json_decode($importerSettings, true);
$maximumStock = $importerSettings['felder']['lagerbestandmaxmenge'];
}
$this->maximumStockForShop[$shopId] = (float)$maximumStock;
}
}
+6
View File
@@ -0,0 +1,6 @@
function loggingPositionsMoreData(){
oMoreData1stocklogging = $('#show_errors_only').prop("checked")?1:0;
var oTableL = $('#stocklogging').dataTable();
oTableL.fnFilter('a');
oTableL.fnFilter('');
}