Server IP : 85.193.89.191 / Your IP : 18.216.142.2 Web Server : Apache System : Linux 956367-cx40159.tmweb.ru 3.10.0-1160.105.1.el7.x86_64 #1 SMP Thu Dec 7 15:39:45 UTC 2023 x86_64 User : bitrix ( 600) PHP Version : 8.1.27 Disable Function : NONE MySQL : OFF | cURL : OFF | WGET : ON | Perl : ON | Python : ON | Sudo : ON | Pkexec : ON Directory : /home/bitrix/www/bitrix/components/bitrix/sale.order.checkout/ |
Upload File : |
<?php use Bitrix\Crm\Binding\DealContactTable; use Bitrix\Crm\Binding\EntityBinding; use Bitrix\Main; use Bitrix\Main\Loader; use Bitrix\Main\Localization\Loc; use Bitrix\Sale; use Bitrix\Sale\Cashbox; use Bitrix\Catalog; if (!defined('B_PROLOG_INCLUDED') || B_PROLOG_INCLUDED !== true) { die(); } /** * Class SaleOrderCheckout */ class SaleOrderCheckout extends \CBitrixComponent { /** @var Sale\Order */ private $order; /** @var Sale\Basket\Storage $basketStorage */ private $basketStorage; /** @var Main\ErrorCollection */ private $errorCollection; public function onPrepareComponentParams($arParams): array { $this->errorCollection = new Main\ErrorCollection(); if (empty($arParams['CONTEXT_SITE_ID'])) { $arParams['CONTEXT_SITE_ID'] = null; } if (!isset($arParams['IS_LANDING_SHOP'])) { if ( $arParams['CONTEXT_SITE_ID'] && Main\ModuleManager::isModuleInstalled('intranet') && Loader::includeModule('crm') ) { $arParams['IS_LANDING_SHOP'] = 'Y'; } else { $arParams['IS_LANDING_SHOP'] = 'N'; } } if (!Loader::includeModule('crm')) { $arParams['IS_LANDING_SHOP'] = 'N'; } $arParams['IS_LANDING_SHOP'] = $arParams['IS_LANDING_SHOP'] === 'Y' ? 'Y' : 'N'; $arParams['URL_PATH_TO_DETAIL_PRODUCT'] = !empty($arParams['URL_PATH_TO_DETAIL_PRODUCT']) ? $arParams['URL_PATH_TO_DETAIL_PRODUCT'] : '' ; $arParams['URL_PATH_TO_MAIN_PAGE'] = !empty($arParams['URL_PATH_TO_MAIN_PAGE']) ? $arParams['URL_PATH_TO_MAIN_PAGE'] : '' ; if (empty($arParams['SHOW_RETURN_BUTTON'])) { $arParams['SHOW_RETURN_BUTTON'] = 'Y'; } return parent::onPrepareComponentParams($arParams); } protected function listKeysSignedParameters(): array { return [ 'URL_PATH_TO_DETAIL_PRODUCT', ]; } public function executeComponent(): void { $this->checkModules(); if (!$this->isEmptyError()) { $this->prepareErrorResultArray(); $this->includeComponentTemplate('error'); return; } $this->initResult(); $action = $this->getAction(); $this->doAction($action); $this->prepareResultArray(); if ($this->isEmptyError()) { if ($this->needShowEmptyBasket($this->order)) { $this->includeComponentTemplate('empty'); } else { $this->prepareJsonData(); $this->includeComponentTemplate(); } } else { $this->includeComponentTemplate('error'); } } private function checkModules(): void { if (!Loader::includeModule('sale')) { $this->addError(new Main\Error(Loc::getMessage('SOC_MODULE_SALE_NOT_INSTALLED'))); } if (!Loader::includeModule('iblock')) { $this->addError(new Main\Error(Loc::getMessage('SOC_MODULE_IBLOCK_NOT_INSTALLED'))); } if (!Loader::includeModule('catalog')) { $this->addError(new Main\Error(Loc::getMessage('SOC_MODULE_CATALOG_NOT_INSTALLED'))); } } private function getAction(): string { $action = 'processOrder'; if (!$this->arResult['IS_NEW_ORDER']) { $action = 'showOrder'; } return $action; } private function doAction(string $action): void { if ($this->actionExists($action)) { $this->{$action.'Action'}(); } } private function actionExists(string $action): bool { return is_callable([$this, $action.'Action']); } private function processOrderAction(): void { $userId = $this->getUserId(); $this->order = $this->createOrder($userId); } private function showOrderAction(): void { $accountNumber = $this->arResult['ACCOUNT_NUMBER']; $this->loadOrderByAccountNumber($accountNumber); if ($this->order) { $this->checkAccess($this->order); } else { $this->addError(new Main\Error(Loc::getMessage('SOC_ORDER_NOT_FOUND'))); } } private function loadOrderByAccountNumber($accountNumber): void { $registry = Sale\Registry::getInstance(Sale\Registry::REGISTRY_TYPE_ORDER); /** @var Sale\Order $orderClassName */ $orderClassName = $registry->getOrderClassName(); $this->order = $orderClassName::loadByAccountNumber($accountNumber); } private function checkAccess(Sale\Order $order) { $access = $this->request->get('access'); if ($access !== $order->getHash()) { $this->addError(new Main\Error(Loc::getMessage('SOC_ACCESS_DENIED'))); } } private function initResult(): void { $accountNumber = $this->request->get('accountNumber'); $mode = $this->request->get('mode'); $this->arResult = [ 'USER_CONSENT_PROPERTY_DATA' => [], 'CURRENCY' => Sale\Internals\SiteCurrencyTable::getSiteCurrency($this->getSiteId()), 'IS_NEW_ORDER' => $accountNumber ? false : true, 'ACCOUNT_NUMBER' => $accountNumber ?? null, 'MODE' => $mode, 'TRADING_PLATFORM_ID' => null, 'JSON_DATA' => [], 'ERRORS' => [], ]; } private function prepareResultArray(): void { if ($this->arParams['USER_CONSENT'] === 'Y' && $this->order) { $this->obtainUserConsentInfo($this->order); } if ($this->arParams['IS_LANDING_SHOP'] === 'Y' && $this->arParams['CONTEXT_SITE_ID']) { $this->arResult['TRADING_PLATFORM_ID'] = $this->getTradingPlatformId($this->arParams['CONTEXT_SITE_ID']); } $this->prepareErrorResultArray(); } private function prepareErrorResultArray(): void { if (!$this->errorCollection->isEmpty()) { /** @var Main\Error $error */ foreach ($this->errorCollection as $error) { $this->arResult['ERRORS'][] = $error->getMessage(); } } } private function createOrder(int $userId): Sale\Order { $registry = Sale\Registry::getInstance(Sale\Registry::REGISTRY_TYPE_ORDER); /** @var Sale\Order $orderClassName */ $orderClassName = $registry->getOrderClassName(); $order = $orderClassName::create($this->getSiteId(), $userId); $order->isStartField(); $this->initPersonType($order); $this->initBasket($order); $contextSiteId = $this->arParams['CONTEXT_SITE_ID']; if ($contextSiteId) { $this->initTradeBinding($order, $contextSiteId); } $order->doFinalAction(true); return $order; } private function initBasket(Sale\Order $order): void { $basket = $this->loadBasket(); $refreshResult = $this->refreshBasket($basket); if (!$refreshResult->isSuccess()) { return; } $this->refreshBasketRatio($basket); if ($basket->isChanged()) { $basket->save(); } $availableBasket = $basket->getOrderableItems(); $order->appendBasket($availableBasket); } private function loadBasket(): Sale\Basket { return $this->getBasketStorage()->getBasket(); } private function getBasketStorage(): Sale\Basket\Storage { if (!isset($this->basketStorage)) { $this->basketStorage = Sale\Basket\Storage::getInstance(Sale\Fuser::getId(), $this->getSiteId()); } return $this->basketStorage; } private function refreshBasketRatio(Sale\Basket $basket): void { Sale\BasketComponentHelper::correctQuantityRatio($basket); } private function refreshBasket(Sale\Basket $basket): Sale\Result { $refreshStrategy = Sale\Basket\RefreshFactory::create(Sale\Basket\RefreshFactory::TYPE_FULL); $result = $basket->refresh($refreshStrategy); if (!$result->isSuccess()) { $this->addError(new Main\Error(Loc::getMessage('SOC_BASKER_REFRESH_ERROR'))); } return $result; } private function initPersonType(Sale\Order $order): void { $personTypeId = $this->getPersonTypeId($this->getSiteId()); if ($personTypeId) { $order->setPersonTypeId($personTypeId); } else { $this->addError(new Main\Error(Loc::getMessage('SOC_PERSON_TYPE_NOT_FOUND'))); } } private function initTradeBinding(Sale\Order $order, $contextSiteId): void { if (!Loader::includeModule('landing')) { return; } $code = Sale\TradingPlatform\Landing\Landing::getCodeBySiteId($contextSiteId); $platform = Sale\TradingPlatform\Landing\Landing::getInstanceByCode($code); if (!$platform->isInstalled()) { return; } $collection = $order->getTradeBindingCollection(); $collection->createItem($platform); } private function getUserId(): int { global $USER; $userId = null; if ($USER->IsAuthorized()) { $userId = $USER->GetID(); } else { $order = $this->getOrderFromSession(); if ($order) { $userId = $this->getUserIdFromOrder($order); } } return (int)$userId; } private function getUserIdFromOrder(Sale\Order $order): int { return (int)$order->getUserId(); } private function getOrderFromSession(): ?Sale\Order { $order = null; $session = Main\Application::getInstance()->getSession(); if ($session->has('SALE_ACCOUNT_NUMBER_LIST')) { $saleNumberAccountList = $session->get('SALE_ACCOUNT_NUMBER_LIST'); if (\is_array($saleNumberAccountList)) { $accountNumber = end($saleNumberAccountList); $registry = Sale\Registry::getInstance(Sale\Registry::REGISTRY_TYPE_ORDER); /** @var Sale\Order $orderClassName */ $orderClassName = $registry->getOrderClassName(); $order = $orderClassName::loadByAccountNumber($accountNumber); } } return $order ?: null; } private function prepareJsonData(): void { $aggregateData = Sale\Helpers\Controller\Action\Entity\Order::getAggregateOrder($this->order); if ($this->order->isNew() === false) { $aggregateData += ['HASH' => $this->order->getHash()]; } foreach ($aggregateData['PROPERTIES'] as $id => $property) { if ($property['IS_PAYER'] === 'Y') { $aggregateData['PROPERTIES'][$id]['TYPE'] = 'NAME'; } elseif ($property['IS_PHONE'] === 'Y') { $aggregateData['PROPERTIES'][$id]['TYPE'] = 'PHONE'; } elseif ($property['IS_EMAIL'] === 'Y') { $aggregateData['PROPERTIES'][$id]['TYPE'] = 'EMAIL'; } elseif ($property['TIME'] === 'Y') { $aggregateData['PROPERTIES'][$id]['TYPE'] = 'DATETIME'; } } if (!empty($aggregateData['BASKET_ITEMS'])) { $aggregateData['BASKET_ITEMS'] = $this->fillBasketItems($aggregateData['BASKET_ITEMS']); } $model = $this->getModel($aggregateData); $jsonData = [ 'MODEL' => $model, 'SCHEME' => $this->getScheme($aggregateData, $model), 'PARAMETERS' => $this->getParameters(), ]; $jsonData = $this->onComponentSaleOrderCheckoutPrepareJsonDataEvent($jsonData); $sku = []; $basket = $jsonData['SCHEME']['BASKET']; foreach ($basket as $index => $item) { if (empty($item['SKU'])) { $sku[$index] = []; } else { $sku[$index]['tree'] = $item['SKU']['TREE']; $sku[$index]['parentProductId'] = $item['SKU']['PARENT_PRODUCT_ID']; } unset($basket[$index]['SKU']); } $jsonData['SCHEME']['BASKET'] = $basket; foreach ($jsonData as $key => $data) { $jsonData[$key] = Main\Engine\Response\Converter::toJson()->process($data); } $basket = $jsonData['SCHEME']['basket']; foreach ($sku as $index => $value) { $basket[$index]['sku'] = $value; } $jsonData['SCHEME']['basket'] = $basket; $this->arResult['JSON_DATA'] = $jsonData; } private function getModel(array $aggregateData = []): array { $basketItems = []; foreach ($aggregateData['BASKET_ITEMS'] as $item) { $basketItems[$item['ID']] = [ 'ID' => $item['ID'], 'PRODUCT_ID' => $item['PRODUCT_ID'], 'QUANTITY' => $item['QUANTITY'], ]; } $properties = []; foreach ($aggregateData['PROPERTIES'] as $item) { $values = array_filter($aggregateData['USER_PROFILE_VALUES'], static function ($profileValue) use ($item) { return (int)$item['ID'] === (int)$profileValue['ORDER_PROPS_ID']; }); $value = (($item['MULTIPLE'] === 'Y') ? $item['VALUE'] : reset($item['VALUE'])); if ($values) { $value = current($values)['VALUE']; } $properties[$item['ID']] = $value; } $payments = []; if (isset($aggregateData['ORDER']['PAYMENTS'])) { foreach ($aggregateData['ORDER']['PAYMENTS'] as $payment) { $payments[$payment['ID']] = [ 'ID'=>$payment['ID'], 'SUM'=>$payment['SUM'], ]; } } return [ 'BASKET_ITEMS' => $basketItems, 'PROPERTIES' => $properties, 'PAYMENTS' => $payments, 'TRADING_PLATFORM_ID' => $this->arResult['TRADING_PLATFORM_ID'], ]; } private function getScheme(array $aggregateData = [], array $model = []): array { $currency = Sale\Internals\SiteCurrencyTable::getSiteCurrency($this->getSiteId()); $scheme = [ 'ORDER' => [ 'HASH' => $aggregateData['HASH'] ?? null, 'ID' => $aggregateData['ORDER']['ID'] ?? null, 'ACCOUNT_NUMBER' => $aggregateData['ORDER']['ACCOUNT_NUMBER'] ?? null, 'PAID' => $aggregateData['ORDER']['PAYED'] === 'Y' ? 'Y' : 'N', ], 'USER_ID' => $this->getUserId(), 'SITE_ID' => $this->getSiteId(), 'PAY_SYSTEMS' => [], 'FUSER_ID' => Sale\Fuser::getId(), 'CURRENCY' => $currency, 'PERSON_TYPE_ID' => $this->getPersonTypeId($this->getSiteId()), 'BASKET' => [], 'PROPERTIES' => [], 'VARIANTS' => [], 'TOTAL' => [], 'DISCOUNT' => [], 'PAYMENTS' => [], 'CHECK' => [] ]; if (!empty($aggregateData['PAY_SYSTEMS'])) { $scheme['PAY_SYSTEMS'] = array_values(array_filter($aggregateData['PAY_SYSTEMS'], static function ($paySystem) { return ($paySystem['ACTION_FILE'] !== 'inner'); })); array_walk($scheme['PAY_SYSTEMS'], static function(&$item){ $item['TYPE'] = self::resolvePaySystemTypeByCode($item['IS_CASH']); }); } foreach ($aggregateData['BASKET_ITEMS'] as $basketItem) { $scheme['BASKET'][] = [ 'ID' => $basketItem['ID'], 'NAME' => $basketItem['NAME'], 'QUANTITY' => $basketItem['QUANTITY'], 'MEASURE_TEXT' => $basketItem['MEASURE_TEXT'], 'PRICE' => $basketItem['PRICE'], 'BASE_PRICE' => $basketItem['BASE_PRICE'], 'SUM' => $basketItem['SUM'], 'BASE_SUM' => $basketItem['SUM_BASE'], 'CURRENCY' => $basketItem['CURRENCY'], 'PRODUCT_PROVIDER_CLASS' => $basketItem['PRODUCT_PROVIDER_CLASS'], 'MODULE' => $basketItem['MODULE'], 'DISCOUNT' => [ 'SUM' => $basketItem['SUM_DISCOUNT_DIFF'], 'PRICE' => $basketItem['DISCOUNT_PRICE'], ], 'PROPS' => $basketItem['PROPS'], 'PRODUCT' => [ 'ID' => $basketItem['CATALOG_PRODUCT']['ID'], 'DETAIL_PAGE_URL' => $basketItem['DETAIL_PAGE_URL'], 'PICTURE' => $basketItem['CATALOG_PRODUCT']['FRONT_IMAGE']['SRC'], 'AVAILABLE_QUANTITY' => $basketItem['CATALOG_PRODUCT']['AVAILABLE_QUANTITY'], 'RATIO' => $basketItem['CATALOG_PRODUCT']['RATIO'], 'CHECK_MAX_QUANTITY' => $basketItem['CATALOG_PRODUCT']['CHECK_MAX_QUANTITY'], 'TYPE' => $basketItem['CATALOG_PRODUCT']['TYPE'], ], 'SKU' => $basketItem['CATALOG_PRODUCT']['SKU'], ]; } foreach ($aggregateData['PROPERTIES'] as $property) { $scheme['PROPERTIES'][] = [ 'ID' => $property['ID'], 'NAME' => $property['NAME'], 'TYPE' => $property['TYPE'], 'VALUE' => $model['PROPERTIES'][$property['ID']] ?? null, 'REQUIRED' => $property['REQUIRED'], 'MULTIPLE' => $property['MULTIPLE'], ]; } $scheme['VARIANTS'] = $aggregateData['VARIANTS']; $orderPriceTotal = $aggregateData['ORDER_PRICE_TOTAL']; $scheme['TOTAL'] = [ 'PRICE' => $orderPriceTotal['ORDER_PRICE'], 'BASE_PRICE' => $orderPriceTotal['PRICE_WITHOUT_DISCOUNT_VALUE'], ]; $scheme['DISCOUNT'] = [ 'SUM' => $orderPriceTotal['BASKET_PRICE_DISCOUNT_DIFF_VALUE'], ]; $culture = Main\Context::getCurrent()->getCulture(); foreach ($aggregateData['PAYMENTS'] as $payment) { $scheme['PAYMENTS'][] = [ 'ID' => $payment['ID'], 'SUM' => $payment['SUM'], 'PAID' => $payment['PAID'], 'CURRENCY' => $payment['CURRENCY'], 'PAY_SYSTEM_ID' => $payment['PAY_SYSTEM_ID'], 'ACCOUNT_NUMBER' => $payment['ACCOUNT_NUMBER'], 'DATE_BILL_FORMATTED' => \FormatDate($culture->getLongDateFormat(), $payment['DATE_BILL']->getTimestamp()), ]; } foreach ($aggregateData['CHECKS'] as $check) { $scheme['CHECK'][] = [ 'ID' => $check['ID'], 'DATE_FORMATTED' => \FormatDate($culture->getLongDateFormat(), $check['DATE_CREATE']->getTimestamp()), 'LINK' => $check['LINK'], 'STATUS' => $check['STATUS'], 'PAYMENT_ID' => $check['PAYMENT_ID'] ]; } return $scheme; } private function getParameters(): array { $parameters = $this->arParams; $parameters['PAY_SYSTEM_RETURN_URL'] = (new Sale\PaySystem\Context())->getUrl(); $parameters['USER_CONSENT_PROPERTY_DATA'] = $this->arResult['USER_CONSENT_PROPERTY_DATA']; return $parameters; } private function obtainUserConsentInfo(Sale\Order $order): void { $propertyNames = []; foreach ($order->getPropertyCollection() as $property) { $propertyNames[] = $property->getName(); } $this->arResult['USER_CONSENT_PROPERTY_DATA'] = $propertyNames; } private function fillBasketItems(array $basketItems): array { $urlPathToDetailProduct = $this->arParams['URL_PATH_TO_DETAIL_PRODUCT']; if ($urlPathToDetailProduct) { $basketItems = $this->setDetailPageUrlForBasketItems($basketItems, $urlPathToDetailProduct); } return $basketItems; } private function setDetailPageUrlForBasketItems(array $basketItems, string $pathToDetailProductUrl): array { $repositoryFacade = Catalog\v2\IoC\ServiceContainer::getRepositoryFacade(); $repositoryFacade->setDetailUrlTemplate($pathToDetailProductUrl); foreach ($basketItems as $item => $basketItem) { $product = $repositoryFacade->loadVariation($basketItem['PRODUCT_ID']); if ($product) { /** @var Catalog\v2\Product\Product $parent */ $parent = $product->getParent(); if ($parent instanceof Catalog\v2\Product\Product) { $basketItems[$item]['DETAIL_PAGE_URL'] = $parent->getDetailUrl(); } else { $basketItems[$item]['DETAIL_PAGE_URL'] = ''; } } } return $basketItems; } private function addError(Main\Error $error): void { $this->errorCollection->setError($error); } private function isEmptyError(): bool { return $this->errorCollection->isEmpty(); } private function needShowEmptyBasket(Sale\Order $order): bool { return $order->isNew() && $order->getBasket() && $order->getBasket()->isEmpty(); } private function getTradingPlatformId($contextSiteId): ?int { if (Main\Loader::includeModule('landing')) { $code = Sale\TradingPlatform\Landing\Landing::getCodeBySiteId($contextSiteId); $platform = Sale\TradingPlatform\Landing\Landing::getInstanceByCode($code); if ($platform->isInstalled()) { return (int)$platform->getId(); } } return null; } private function getPersonTypeId(string $siteId): ?int { static $result; if (!empty($result[$siteId])) { return $result[$siteId]; } $personType = Sale\Internals\BusinessValuePersonDomainTable::getList([ 'select' => ['PERSON_TYPE_ID'], 'filter' => [ '=DOMAIN' => Sale\BusinessValue::INDIVIDUAL_DOMAIN, '=PERSON_TYPE_REFERENCE.ENTITY_REGISTRY_TYPE' => Sale\Registry::REGISTRY_TYPE_ORDER, '=PERSON_TYPE_REFERENCE.LID' => $siteId, '=PERSON_TYPE_REFERENCE.ACTIVE' => 'Y', ], 'order' => [ 'PERSON_TYPE_REFERENCE.SORT' => 'ASC' ], 'limit' => 1, ])->fetch(); $personTypeId = $personType ? (int)$personType['PERSON_TYPE_ID'] : null; $result[$siteId] = $personTypeId; return $personTypeId; } private static function resolvePaySystemTypeByCode($type): string { switch($type) { case 'Y': $resolveType = 'CASH'; break; case 'N': $resolveType = 'CASH_LESS'; break; case 'A': $resolveType = 'CARD_TRANSACTION'; break; default; $resolveType = 'UNDEFINED'; } return $resolveType; } private function onComponentSaleOrderCheckoutPrepareJsonDataEvent(array $jsonData): array { $eventResult = GetModuleEvents('sale', 'onComponentSaleOrderCheckoutPrepareJsonData'); while ($event = $eventResult->fetch()) { ExecuteModuleEventEx($event, [&$jsonData]); } return $jsonData; } }