'Y']); while ($ar = $rsSites->Fetch()) { $arSites[] = $ar; } return $arSites; } public static function OrderTypesList($arSites) { $orderTypesList = array(); foreach ($arSites as $site) { $personTypes = \Bitrix\Sale\PersonType::load($site['LID']); $bitrixOrderTypesList = array(); foreach ($personTypes as $personType) { if (!array_key_exists($personType['ID'], $orderTypesList)) { $bitrixOrderTypesList[$personType['ID']] = $personType; } asort($bitrixOrderTypesList); } $orderTypesList = $orderTypesList + $bitrixOrderTypesList; } return $orderTypesList; } public static function DeliveryList() { $bitrixDeliveryTypesList = array(); $arDeliveryServiceAll = \Bitrix\Sale\Delivery\Services\Manager::getActiveList(); $noOrderId = EmptyDeliveryService::getEmptyDeliveryServiceId(); $groups = array(); foreach ($arDeliveryServiceAll as $arDeliveryService) { if ($arDeliveryService['CLASS_NAME'] == '\Bitrix\Sale\Delivery\Services\Group') { $groups[] = $arDeliveryService['ID']; } } foreach ($arDeliveryServiceAll as $arDeliveryService) { if ((($arDeliveryService['PARENT_ID'] == '0' || $arDeliveryService['PARENT_ID'] == null) || in_array($arDeliveryService['PARENT_ID'], $groups)) && $arDeliveryService['ID'] != $noOrderId && $arDeliveryService['CLASS_NAME'] != '\Bitrix\Sale\Delivery\Services\Group') { if (in_array($arDeliveryService['PARENT_ID'], $groups)) { $arDeliveryService['PARENT_ID'] = 0; } $bitrixDeliveryTypesList[] = $arDeliveryService; } } return $bitrixDeliveryTypesList; } public static function PaymentList() { $bitrixPaymentTypesList = array(); $dbPaymentAll = Manager::getList(array( 'select' => array('ID', 'NAME'), 'filter' => array('ACTIVE' => 'Y') )); while ($payment = $dbPaymentAll->fetch()) { $bitrixPaymentTypesList[] = $payment; } return $bitrixPaymentTypesList; } public static function StatusesList() { $bitrixPaymentStatusesList = array(); $obStatuses = StatusTable::getList(array( 'filter' => array('TYPE' => 'O', '=Bitrix\Sale\Internals\StatusLangTable:STATUS.LID' => LANGUAGE_ID), 'select' => array('ID', 'NAME' => 'Bitrix\Sale\Internals\StatusLangTable:STATUS.NAME') )); while ($arStatus = $obStatuses->fetch()) { $bitrixPaymentStatusesList[$arStatus['ID']] = array( 'ID' => $arStatus['ID'], 'NAME' => $arStatus['NAME'], ); } return $bitrixPaymentStatusesList; } public static function OrderPropsList() { $bitrixPropsList = array(); $arPropsAll = OrderPropsTable::getList(array( 'select' => array('*'), 'filter' => array('CODE' => '_%') )); while ($prop = $arPropsAll->Fetch()) { $bitrixPropsList[$prop['PERSON_TYPE_ID']][] = $prop; } return $bitrixPropsList; } public static function PricesExportList() { $catalogExportPrices = array(); $dbPriceType = CCatalogGroup::GetList( array(), array(), false, false, array('ID', 'NAME', 'NAME_LANG') ); while ($arPriceType = $dbPriceType->Fetch()) { $catalogExportPrices[$arPriceType['ID']] = $arPriceType; } return $catalogExportPrices; } public static function StoresExportList() { $catalogExportStores = array(); $dbStores = CCatalogStore::GetList(array(), array('ACTIVE' => 'Y'), false, false, array('ID', 'TITLE')); while ($stores = $dbStores->Fetch()) { $catalogExportStores[] = $stores; } return $catalogExportStores; } public static function IblocksExportList() { $catalogExportIblocks = array(); $dbIblocks = CIBlock::GetList(array('IBLOCK_TYPE' => 'ASC', 'NAME' => 'ASC'), array('CHECK_PERMISSIONS' => 'Y', 'MIN_PERMISSION' => 'W')); while ($iblock = $dbIblocks->Fetch()) { if ($arCatalog = CCatalog::GetByIDExt($iblock['ID'])) { if($arCatalog['CATALOG_TYPE'] == 'D' || $arCatalog['CATALOG_TYPE'] == 'X' || $arCatalog['CATALOG_TYPE'] == 'P') { $catalogExportIblocks[$iblock['ID']] = array( 'ID' => $iblock['ID'], 'IBLOCK_TYPE_ID' => $iblock['IBLOCK_TYPE_ID'], 'LID' => $iblock['LID'], 'CODE' => $iblock['CODE'], 'NAME' => $iblock['NAME'], ); if ($arCatalog['CATALOG_TYPE'] == 'X' || $arCatalog['CATALOG_TYPE'] == 'P') { $iblockOffer = CCatalogSKU::GetInfoByProductIBlock($iblock['ID']); $catalogExportIblocks[$iblock['ID']]['SKU'] = $iblockOffer; } } } } return $catalogExportIblocks; } /** * * w+ event in bitrix log */ public static function eventLog($auditType, $itemId, $description) { CEventLog::Add([ 'SEVERITY' => 'SECURITY', 'AUDIT_TYPE_ID' => $auditType, 'MODULE_ID' => self::$MODULE_ID, 'ITEM_ID' => $itemId, 'DESCRIPTION' => $description, ]); } /** * * Agent function * * @return self name */ public static function uploadOrdersAgent() { RetailCrmOrder::uploadOrders(); $failedIds = unserialize(COption::GetOptionString(self::$MODULE_ID, self::$CRM_ORDER_FAILED_IDS, 0)); if (is_array($failedIds) && !empty($failedIds)) { RetailCrmOrder::uploadOrders(50, true); } return; } /** * * Agent function * * @return self name * * @throws \Throwable */ public static function orderAgent() { if (COption::GetOptionString('main', 'agents_use_crontab', 'N') !== 'N') { define('NO_AGENT_CHECK', true); } try { $service = ManagerService::getInstance(); $service->synchronizeManagers(); RetailCrmHistory::customerHistory(); RetailCrmHistory::orderHistory(); self::uploadOrdersAgent(); } catch (\Throwable $exception) { Logger::getInstance()->write( 'Fail orderAgent:' . PHP_EOL . $exception->getMessage() . PHP_EOL . 'File: ' . $exception->getFile() . PHP_EOL . 'Line: ' . $exception->getLine() . PHP_EOL, 'orderAgent' ); } return 'RCrmActions::orderAgent();'; } /** * removes all empty fields from arrays * working with nested arrs * * @param array $arr * * @return array */ public static function clearArr(array $arr): array { /** @var \Intaro\RetailCrm\Service\Utils $utils */ $utils = ServiceLocator::getOrCreate(\Intaro\RetailCrm\Service\Utils::class); return $utils->clearArray($arr); } /** * * @param array|bool|\SplFixedArray|string $str in SITE_CHARSET * * @return array|bool|\SplFixedArray|string $str in utf-8 */ public static function toJSON($str) { /** @var \Intaro\RetailCrm\Service\Utils $utils */ $utils = ServiceLocator::getOrCreate(\Intaro\RetailCrm\Service\Utils::class); return $utils->toUTF8($str); } /** * * @param string|array|\SplFixedArray $str in utf-8 * * @return array|bool|\SplFixedArray|string $str in SITE_CHARSET */ public static function fromJSON($str) { if ($str === null) { return ''; } /** @var \Intaro\RetailCrm\Service\Utils $utils */ $utils = ServiceLocator::getOrCreate(\Intaro\RetailCrm\Service\Utils::class); return $utils->fromUTF8($str); } /** * Extracts payment ID or client ID from payment externalId * Payment ID - pass nothing or 'id' as second argument * Client ID - pass 'client_id' as second argument * * @param $externalId * @param string $data * @return bool|string */ public static function getFromPaymentExternalId($externalId, $data = 'id') { switch ($data) { case 'id': if (false === strpos($externalId, '_')) { return $externalId; } else { return substr($externalId, 0, strpos($externalId, '_')); } break; case 'client_id': if (false === strpos($externalId, '_')) { return ''; } else { return substr($externalId, strpos($externalId, '_'), count($externalId)); } break; } return ''; } /** * Returns true if provided externalId in new format (id_clientId) * * @param $externalId * @return bool */ public static function isNewExternalId($externalId) { return !(false === strpos($externalId, '_')); } /** * Generates payment external ID * * @param $id * * @return string */ public static function generatePaymentExternalId($id) { return sprintf( '%s_%s', $id, COption::GetOptionString(self::$MODULE_ID, 'client_id', 0) ); } /** * Unserialize array * * @param string $string * * @return mixed */ public static function unserializeArrayRecursive($string) { if ($string === false || empty($string)) { return false; } if (is_string($string)) { $string = unserialize($string); } if (!is_array($string)) { $string = self::unserializeArrayRecursive($string); } return $string; } /** * @param string|null $fio * * @return array */ public static function explodeFio(?string $fio): array { $result = []; $fio = preg_replace('|[\s]+|s', ' ', trim($fio)); if (empty($fio)) { return $result; } else { $newFio = explode(' ', $fio, 3); } switch (count($newFio)) { default: case 0: $result['firstName'] = $fio; break; case 1: $result['firstName'] = $newFio[0]; break; case 2: $result = [ 'lastName' => $newFio[0], 'firstName' => $newFio[1], ]; break; case 3: $result = [ 'lastName' => $newFio[0], 'firstName' => $newFio[1], 'patronymic' => $newFio[2], ]; break; } return $result; } public static function sendConfiguration($api, $api_version, $active = true) { $scheme = isset($_SERVER['HTTPS']) ? 'https://' : 'http://'; $baseUrl = $scheme . $_SERVER['HTTP_HOST']; $integrationCode = 'bitrix'; $logo = 'https://s3.eu-central-1.amazonaws.com/retailcrm-billing/images/5af47fe682bf2-1c-bitrix-logo.svg'; $accountUrl = $baseUrl . '/bitrix/admin'; $clientId = COption::GetOptionString(self::$MODULE_ID, 'client_id', 0); if (!$clientId) { $clientId = uniqid(); COption::SetOptionString(self::$MODULE_ID, 'client_id', $clientId); } $code = $integrationCode . '-' . $clientId; if ($api_version == 'v4') { $configuration = array( 'name' => GetMessage('API_MODULE_NAME'), 'code' => $code, 'logo' => $logo, 'configurationUrl' => $accountUrl, 'active' => $active ); self::apiMethod($api, 'marketplaceSettingsEdit', __METHOD__, $configuration); } else { $configuration = array( 'clientId' => $clientId, 'code' => $code, 'integrationCode' => $integrationCode, 'active' => $active, 'name' => GetMessage('API_MODULE_NAME'), 'logo' => $logo, 'baseUrl' => $baseUrl, 'accountUrl' => $accountUrl ); self::apiMethod($api, 'integrationModulesEdit', __METHOD__, $configuration); } } public static function apiMethod($api, $methodApi, $method, $params, $site = null) { switch ($methodApi) { case 'ordersPaymentDelete': case 'ordersHistory': case 'customerHistory': case 'ordersFixExternalIds': case 'customersFixExternalIds': case 'customersCorporateContacts': case 'customersList': case 'customersCorporateList': return self::proxy($api, $methodApi, $method, [$params]); case 'orderGet': return self::proxy($api, 'ordersGet', $method, [$params, 'id', $site]); case 'ordersGet': case 'ordersEdit': case 'customersGet': case 'customersEdit': case 'customersCorporateGet': return self::proxy($api, $methodApi, $method, [$params, 'externalId', $site]); case 'customersCorporateGetById': return self::proxy($api, 'customersCorporateGet', $method, [$params, 'id', $site]); case 'customersGetById': return self::proxy($api, 'customersGet', $method, [$params, 'id', $site]); case 'paymentEditById': return self::proxy($api, 'ordersPaymentEdit', $method, [$params, 'id', $site]); case 'paymentEditByExternalId': return self::proxy($api, 'ordersPaymentEdit', $method, [$params, 'externalId', $site]); case 'customersCorporateEdit': return self::proxy($api, 'customersCorporateEdit', $method, [$params, 'externalId', $site]); case 'cartGet': return self::proxy($api, $methodApi, $method, [$params, $site, 'externalId']); case 'cartSet': case 'cartClear': return self::proxy($api, $methodApi, $method, [$params, $site]); default: return self::proxy($api, $methodApi, $method, array($params, $site)); } } private static function proxy($api, $methodApi, $method, $params) { $version = COption::GetOptionString(self::$MODULE_ID, self::$CRM_API_VERSION, 0); try { $result = call_user_func_array(array($api, $methodApi), $params); if (!$result) { $err = new RuntimeException( $methodApi . ': Got null instead of valid result!' ); Logger::getInstance()->write(sprintf( '%s%s%s', $err->getMessage(), PHP_EOL, $err->getTraceAsString() ), 'apiErrors'); return false; } if ($result->getStatusCode() !== 200 && $result->getStatusCode() !== 201) { if ($methodApi == 'ordersGet' || $methodApi == 'customersGet' || $methodApi == 'customersCorporateGet' ) { Logger::getInstance()->write(array( 'api' => $version, 'methodApi' => $methodApi, 'errorMsg' => !empty($result['errorMsg']) ? $result['errorMsg'] : '', 'errors' => !empty($result['errors']) ? $result['errors'] : '', 'params' => $params ), 'apiErrors'); } elseif ($methodApi == 'customersUpload' || $methodApi == 'ordersUpload') { Logger::getInstance()->write(array( 'api' => $version, 'methodApi' => $methodApi, 'errorMsg' => !empty($result['errorMsg']) ? $result['errorMsg'] : '', 'errors' => !empty($result['errors']) ? $result['errors'] : '', 'params' => $params ), 'uploadApiErrors'); } elseif ($methodApi == 'cartGet') { Logger::getInstance()->write( [ 'api' => $version, 'methodApi' => $methodApi, 'errorMsg' => !empty($result['errorMsg']) ? $result['errorMsg'] : '', 'errors' => !empty($result['errors']) ? $result['errors'] : '', 'params' => $params, ], 'apiErrors' ); } else { self::eventLog( __CLASS__ . '::' . $method, 'RetailCrm\ApiClient::' . $methodApi, !empty($result['errorMsg']) ? $result['errorMsg'] : '' ); Logger::getInstance()->write(array( 'api' => $version, 'methodApi' => $methodApi, 'errorMsg' => !empty($result['errorMsg']) ? $result['errorMsg'] : '', 'errors' => !empty($result['errors']) ? $result['errors'] : '', 'params' => $params, ), 'apiErrors'); } if (function_exists('retailCrmApiResult')) { retailCrmApiResult($methodApi, false, $result->getStatusCode()); } if ($result->getStatusCode() == 460) { return true; } return false; } } catch (CurlException $e) { static::logException( $method, $methodApi, 'CurlException', 'CurlException', $e, $version, $params ); return false; } catch (InvalidArgumentException $e) { static::logException( $method, $methodApi, 'InvalidArgumentException', 'ArgumentException', $e, $version, $params ); return false; } catch (InvalidJsonException $e) { static::logException( $method, $methodApi, 'InvalidJsonException', 'ArgumentException', $e, $version, $params ); } if (function_exists('retailCrmApiResult')) { retailCrmApiResult($methodApi, true, isset($result) ? $result->getStatusCode() : 0); } return isset($result) ? $result : false; } /** * Log exception into log file and event log * * @param string $method * @param string $methodApi * @param string $exceptionName * @param string $apiResultExceptionName * @param \Exception|\Error|\Throwable $exception * @param string $version * @param array $params */ protected static function logException( $method, $methodApi, $exceptionName, $apiResultExceptionName, $exception, $version, $params ) { self::eventLog( __CLASS__ . '::' . $method, 'RetailCrm\ApiClient::' . $methodApi . '::' . $exceptionName, $exception->getCode() . ': ' . $exception->getMessage() ); Logger::getInstance()->write(array( 'api' => $version, 'methodApi' => $methodApi, 'errorMsg' => $exception->getMessage(), 'errors' => $exception->getCode(), 'params' => $params ), 'apiErrors'); if (function_exists('retailCrmApiResult')) { retailCrmApiResult($methodApi, false, $apiResultExceptionName); } } }