520 lines
16 KiB
PHP
520 lines
16 KiB
PHP
<?php
|
|
|
|
namespace External\VarioBundle;
|
|
|
|
use External\VarioBundle\Exception\SynchronizerException;
|
|
use External\VarioBundle\Util\VarioConfig;
|
|
use KupShop\KupShopBundle\Util\Functional\Mapping;
|
|
|
|
class SoapClient
|
|
{
|
|
public $settings;
|
|
|
|
public $config;
|
|
|
|
private $client;
|
|
|
|
public function __construct(VarioConfig $config)
|
|
{
|
|
$this->config = $config;
|
|
}
|
|
|
|
public function getClient(): \SoapClient
|
|
{
|
|
if (!$this->client) {
|
|
ini_set('default_socket_timeout', 60);
|
|
$this->client = new \SoapClient($this->getWsdl(), [
|
|
'trace' => 1,
|
|
'features' => SOAP_SINGLE_ELEMENT_ARRAYS,
|
|
'connection_timeout' => 60,
|
|
]);
|
|
|
|
if ($clientNumber = $this->getClientNumber()) {
|
|
$auth = new \stdClass();
|
|
$auth->ClientNumber = $clientNumber;
|
|
|
|
$this->client->__setSoapHeaders(
|
|
new \SoapHeader('urn:VarioSOAPServiceIntf', 'TAuthHeader', $auth)
|
|
);
|
|
}
|
|
}
|
|
|
|
return $this->client;
|
|
}
|
|
|
|
public function getProductByCode(string $code): ?object
|
|
{
|
|
$products = $this->getClient()->GetProductsByCriteria($code);
|
|
if (count($products) <= 0) {
|
|
return null;
|
|
}
|
|
|
|
return reset($products);
|
|
}
|
|
|
|
public function getCustomerByEmail(string $email): ?object
|
|
{
|
|
$customer = $this->getClient()->GetCustomersByCriteria(null, null, $email);
|
|
if (count($customer) <= 0) {
|
|
return null;
|
|
}
|
|
|
|
return reset($customer);
|
|
}
|
|
|
|
public function getCustomerIds(): array
|
|
{
|
|
return $this->getClient()->GetCustomers();
|
|
}
|
|
|
|
public function getCategories(bool $onlyIds = false): array
|
|
{
|
|
static $categoriesCache = [];
|
|
|
|
if (empty($categoriesCache[$onlyIds])) {
|
|
$categories = $this->getClient()->GetCategories();
|
|
foreach ($categories as $categoryId) {
|
|
$categoriesCache[$onlyIds][$categoryId] = $onlyIds ? $categoryId : (array) $this->getClient()->GetCategory($categoryId);
|
|
}
|
|
}
|
|
|
|
return $categoriesCache[$onlyIds] ?? [];
|
|
}
|
|
|
|
public function getParameters(): array
|
|
{
|
|
static $parametersCache = [];
|
|
|
|
if (empty($parametersCache)) {
|
|
$parameters = $this->getClient()->GetParametres();
|
|
foreach ($parameters as $parameterId) {
|
|
$parametersCache[$parameterId] = $this->getClient()->GetParameter($parameterId);
|
|
}
|
|
}
|
|
|
|
return $parametersCache;
|
|
}
|
|
|
|
public function getStores(): array
|
|
{
|
|
static $storesCache = [];
|
|
|
|
if (empty($storesCache)) {
|
|
$stores = $this->getClient()->GetStores();
|
|
foreach ($stores as $storeId) {
|
|
$store = $this->getClient()->GetStore($storeId);
|
|
|
|
$storesCache[$store->ID] = $store->StoreName;
|
|
}
|
|
}
|
|
|
|
return $storesCache;
|
|
}
|
|
|
|
public function getPriceLists(): array
|
|
{
|
|
static $priceListsCache = [];
|
|
|
|
if (empty($priceListsCache)) {
|
|
$priceLists = $this->getClient()->GetPricelists();
|
|
foreach ($priceLists as $priceListId) {
|
|
$priceList = $this->getClient()->GetPricelist($priceListId);
|
|
|
|
$priceListsCache[$priceList->ID] = $priceList->PriceListName;
|
|
}
|
|
}
|
|
|
|
return $priceListsCache;
|
|
}
|
|
|
|
public function createOrUpdate($type, $data)
|
|
{
|
|
if (isDevelopment()) {
|
|
return null;
|
|
}
|
|
|
|
if (empty($data)) {
|
|
return null;
|
|
}
|
|
|
|
$method = null;
|
|
switch ($type) {
|
|
case '_otCustomer':
|
|
case 'otCustomer':
|
|
$method = 'CreateOrUpdateCustomer';
|
|
break;
|
|
}
|
|
|
|
if (!$method) {
|
|
throw new SynchronizerException(
|
|
sprintf('Unknown type %s in method createOrUpdate()', $type)
|
|
);
|
|
}
|
|
|
|
return $this->getClient()->$method($data);
|
|
}
|
|
|
|
public function createOrUpdateOrder($order)
|
|
{
|
|
if (isDevelopment()) {
|
|
return;
|
|
}
|
|
|
|
$varioOrderId = $this->getClient()->CreateOrUpdateDocument($order);
|
|
if (empty($varioOrderId)) {
|
|
throw new SynchronizerException('Sending order to Vario failed!');
|
|
}
|
|
|
|
return $varioOrderId;
|
|
}
|
|
|
|
public function updateProduct($product, $fields): ?string
|
|
{
|
|
$varioProductId = $this->getClient()->UpdateProduct($product, $fields);
|
|
if (empty($varioProductId)) {
|
|
throw new SynchronizerException('Update product to Vario failed!');
|
|
}
|
|
|
|
return $varioProductId;
|
|
}
|
|
|
|
public function setJobsDone(array $jobIds): void
|
|
{
|
|
if (isDevelopment()) {
|
|
return;
|
|
}
|
|
|
|
if (!empty($jobIds)) {
|
|
try {
|
|
$this->getClient()->SetJobs($jobIds, true);
|
|
} catch (\Exception $e) {
|
|
getRaven()->captureException($e);
|
|
}
|
|
}
|
|
}
|
|
|
|
public function setJobDone($jobId)
|
|
{
|
|
// do not set job done if in development
|
|
if (isDevelopment()) {
|
|
return;
|
|
}
|
|
|
|
if ($jobId) {
|
|
try {
|
|
$this->getClient()->SetJob($jobId, true);
|
|
} catch (\Exception $e) {
|
|
getRaven()->captureException($e);
|
|
}
|
|
}
|
|
}
|
|
|
|
public function forceObjectData(string $type, $objectID)
|
|
{
|
|
$idField = 'ID';
|
|
switch ($type) {
|
|
case 'otDocument':
|
|
$method = 'GetDocument';
|
|
break;
|
|
case 'otProduct':
|
|
case 'otProductSimple':
|
|
$method = 'GetProduct';
|
|
$idField = 'Id';
|
|
break;
|
|
case 'otAvailableSupply':
|
|
$method = 'GetAvailableSuppliesByProduct';
|
|
$data = $this->getClient()->$method($objectID);
|
|
|
|
$return = [];
|
|
foreach ($data as $item) {
|
|
$return[] = $this->createSyncItem([
|
|
'ID' => false,
|
|
'ObjectID' => trim($item->ID, '{}'),
|
|
'Action' => 'acInsert',
|
|
], $item);
|
|
}
|
|
|
|
return $return;
|
|
case 'otSupply':
|
|
$method = 'GetSuppliesByProduct';
|
|
$data = $this->getClient()->$method($objectID);
|
|
|
|
$return = [];
|
|
foreach ($data as $item) {
|
|
$return[] = $this->createSyncItem([
|
|
'ID' => false,
|
|
'ObjectID' => trim($item->ID, '{}'),
|
|
'Action' => 'acInsert',
|
|
], $item);
|
|
}
|
|
|
|
return $return;
|
|
case 'otCategory':
|
|
$method = 'GetCategory';
|
|
break;
|
|
case 'otCustomer':
|
|
$method = 'GetCustomer';
|
|
break;
|
|
case 'otTranslation':
|
|
$method = 'GetTranslation';
|
|
break;
|
|
case 'otPrice':
|
|
try {
|
|
$data = $this->getClient()->GetPricesByProduct($objectID);
|
|
} catch (\Exception $e) {
|
|
getRaven()->captureException($e);
|
|
$data = [];
|
|
}
|
|
|
|
$return = [];
|
|
foreach ($data as $item) {
|
|
$return[] = $this->createSyncItem([
|
|
'ID' => false,
|
|
'ObjectID' => trim($item->ID, '{}'),
|
|
'Action' => 'acInsert',
|
|
], $item);
|
|
}
|
|
|
|
return $return;
|
|
default:
|
|
throw new SynchronizerException(
|
|
sprintf('Type %s does not support single object synchronization! You have to implement it if you need it.', $type)
|
|
);
|
|
}
|
|
|
|
$data = $this->getClient()->$method($objectID);
|
|
|
|
if ($data) {
|
|
return [
|
|
0 => $this->createSyncItem(
|
|
[
|
|
'ID' => false,
|
|
'ObjectID' => trim($data->$idField, '{}'),
|
|
'Action' => 'acInsert',
|
|
],
|
|
$data
|
|
),
|
|
];
|
|
}
|
|
|
|
return [];
|
|
}
|
|
|
|
public function getJobsData(string $type, int $batch)
|
|
{
|
|
switch ($type) {
|
|
// custom type - creating result equals to GetJobsData
|
|
case 'otPricelist':
|
|
$pricelists = $this->getClient()->GetPricelists();
|
|
$return = [];
|
|
foreach ($pricelists as $pricelist) {
|
|
if (!in_array(trim($pricelist, '{}'), $this->config->getPriceListIds())) {
|
|
continue;
|
|
}
|
|
|
|
$data = $this->getClient()->GetPricelist($pricelist);
|
|
|
|
$return[] = $this->createSyncItem([
|
|
'ID' => false,
|
|
'ObjectID' => trim($pricelist, '{}'),
|
|
'Action' => 'acInsert',
|
|
], $data);
|
|
}
|
|
|
|
return $return;
|
|
case 'otStore': // fetch all stores - to have mapping of store IDs (then in supplies sync is not need of fetching store)
|
|
$stores = $this->getClient()->GetStores();
|
|
$return = [];
|
|
foreach ($stores as $store) {
|
|
if (!in_array(trim($store, '{}'), $this->config->get('stores'))) {
|
|
continue;
|
|
}
|
|
|
|
$return[] = $this->createSyncItem([
|
|
'ID' => false,
|
|
'ObjectID' => trim($store, '{}'),
|
|
'Action' => 'acInsert',
|
|
], $this->getClient()->GetStore($store));
|
|
}
|
|
|
|
return $return;
|
|
case 'otParameter':
|
|
$parameters = $this->getParameters();
|
|
|
|
$return = [];
|
|
foreach ($parameters as $parameterId => $parameter) {
|
|
$return[] = $this->createSyncItem([
|
|
'ID' => false,
|
|
'ObjectID' => trim($parameterId, '{}'),
|
|
'Action' => 'acInsert',
|
|
], $parameter);
|
|
}
|
|
|
|
return $return;
|
|
// some types are not supported by GetJobsData :/ - use prepareJobsData to get same result as in GetJobsData
|
|
case 'otDocument':
|
|
return $this->prepareJobsData($batch, $type, 'GetDocument');
|
|
case 'otAvailableSupply':
|
|
return $this->prepareJobsData($batch, $type, 'GetAvailableSupply');
|
|
case 'otSupply':
|
|
return $this->prepareJobsData($batch, $type, 'GetSupply');
|
|
case 'otCategory':
|
|
return $this->prepareJobsData($batch, $type, 'GetCategory');
|
|
case 'otProductCategory':
|
|
return $this->prepareJobsData($batch, $type, 'GetProductCategory');
|
|
case 'otTranslation':
|
|
return $this->prepareJobsData($batch, $type, 'GetTranslation', null, false, true);
|
|
case 'otProductSimple':
|
|
$jobs = $this->getClient()->GetJobsByType($batch, $type);
|
|
$items = Mapping::mapKeys($this->getClient()->GetProductsArray(
|
|
array_map(
|
|
function ($x) {
|
|
return $x->ObjectID;
|
|
},
|
|
$jobs
|
|
)
|
|
), function ($k, $v) { return [trim($v->Id ?? null, '{}'), $v]; });
|
|
$result = [];
|
|
foreach ($jobs as $job) {
|
|
if ($data = ($items[trim($job->ObjectID, '{}')] ?? null)) {
|
|
$result[] = $this->createSyncItem((array) $job, $data);
|
|
}
|
|
}
|
|
|
|
return $result;
|
|
case 'otCustomer':
|
|
return $this->prepareJobsData($batch, $type, 'GetCustomer');
|
|
case 'otProductExt':
|
|
return $this->prepareJobsData($batch, $type, 'GetProductExt');
|
|
default:
|
|
return $this->getClient()->GetJobsData(
|
|
$batch, $type
|
|
);
|
|
}
|
|
}
|
|
|
|
public function getSuppliesByProduct(string $productId, ?string $variationId = null): ?array
|
|
{
|
|
try {
|
|
$supplies = $this->getClient()->GetAvailableSuppliesByProduct($productId);
|
|
} catch (\Throwable $e) {
|
|
$supplies = null;
|
|
}
|
|
|
|
if ($supplies === null) {
|
|
return null;
|
|
}
|
|
|
|
// filter only active stores
|
|
$supplies = array_filter($supplies, function ($x) { return in_array(trim($x->StoreID, '{}'), $this->config->getStores()); });
|
|
|
|
// filter by variation
|
|
if ($variationId) {
|
|
$supplies = array_filter($supplies, function ($x) use ($variationId) { return $x->VariantID === '{'.$variationId.'}'; });
|
|
}
|
|
|
|
return $supplies;
|
|
}
|
|
|
|
public function getProductIds(): iterable
|
|
{
|
|
$page = 0;
|
|
do {
|
|
$products = $this->getClient()->GetProductsPage(1000, $page++);
|
|
foreach ($products as $id) {
|
|
yield $id;
|
|
}
|
|
} while (!empty($products));
|
|
}
|
|
|
|
protected function prepareJobsData(int $batch, string $type, string $objectGetMethod, ?callable $dataCallback = null, bool $multi = false, bool $ignoreErrors = false)
|
|
{
|
|
$items = [];
|
|
$jobs = $this->getClient()->GetJobsByType($batch, $type);
|
|
$index = 0;
|
|
|
|
$multiData = [];
|
|
if ($multi) {
|
|
$multiData = Mapping::mapKeys(
|
|
array_filter($this->getClient()->$objectGetMethod(array_map(function ($x) { return $x->ObjectID; }, $jobs))),
|
|
function ($k, $v) {
|
|
return [$v->ID, $v];
|
|
}
|
|
);
|
|
}
|
|
|
|
$doneJobs = [];
|
|
|
|
foreach ($jobs as $job) {
|
|
if (isset($job->Action) && $job->Action == 'acDelete') {
|
|
$data = new \stdClass();
|
|
} else {
|
|
$data = $this->ignoreErrors(function () use ($multi, $multiData, $job, $objectGetMethod) {
|
|
if ($multi && $multiData) {
|
|
return $multiData[$job->ObjectID] ?? null;
|
|
} else {
|
|
return $this->getClient()->$objectGetMethod($job->ObjectID);
|
|
}
|
|
}, $ignoreErrors);
|
|
|
|
if ($dataCallback !== null) {
|
|
$data = $dataCallback($data);
|
|
}
|
|
}
|
|
|
|
if ($data) {
|
|
$items[$index] = new \stdClass();
|
|
$items[$index]->Job = $job;
|
|
$items[$index]->Data = $data;
|
|
$index++;
|
|
} else {
|
|
if (!empty($job->ID)) {
|
|
$doneJobs[] = $job->ID;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!empty($doneJobs)) {
|
|
$this->setJobsDone($doneJobs);
|
|
}
|
|
|
|
return $items;
|
|
}
|
|
|
|
protected function getClientNumber(): ?int
|
|
{
|
|
return $this->config->get('client_number');
|
|
}
|
|
|
|
private function ignoreErrors(callable $callback, bool $ignoreErrors = false)
|
|
{
|
|
if ($ignoreErrors) {
|
|
try {
|
|
return $callback();
|
|
} catch (\Exception $e) {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
return $callback();
|
|
}
|
|
|
|
private function createSyncItem(array $job, $data)
|
|
{
|
|
$item = new \stdClass();
|
|
$item->Job = new \stdClass();
|
|
$item->Job->ID = $job['ID'];
|
|
$item->Job->ObjectID = $job['ObjectID'];
|
|
$item->Job->Action = $job['Action'];
|
|
$item->Job->Created = $job['Created'] ?? null;
|
|
$item->Data = $data;
|
|
|
|
return $item;
|
|
}
|
|
|
|
protected function getWsdl()
|
|
{
|
|
return $this->config->get('wsdl');
|
|
}
|
|
}
|