54 $this->accessController = AccessController::getCurrent();
62 new ActionFilter\CloseSession(),
67 new ActionFilter\CloseSession(),
76 parent::getDefaultPreFilters(),
86 if ($action->
getName() ===
'getSkuTreeProperties')
92 $this->accessController->check(ActionDictionary::ACTION_CATALOG_READ)
93 || $this->accessController->check(ActionDictionary::ACTION_CATALOG_VIEW)
96 return parent::processBeforeAction($action);
107 public function getSelectedSkuAction(
int $variationId, array $options = []): ?array
109 $iterator = \CIBlockElement::GetList(
112 'ID' => $variationId,
114 'ACTIVE_DATE' =>
'Y',
115 'CHECK_PERMISSIONS' =>
'Y',
116 'MIN_PERMISSION' =>
'R',
122 $element = $iterator->Fetch();
129 $skuRepository = ServiceContainer::getSkuRepository($element[
'IBLOCK_ID']);
136 $sku = $skuRepository->getEntityById($variationId);
143 return $this->prepareResponse($sku, $options);
148 $iterator = \CIBlockElement::GetList(
151 '=PRODUCT_BARCODE' => $barcode,
153 'ACTIVE_DATE' =>
'Y',
154 'CHECK_PERMISSIONS' =>
'Y',
155 'MIN_PERMISSION' =>
'R',
162 if ($product = $iterator->Fetch())
164 return (
int)$product[
'ID'];
177 $iterator = \CIBlockElement::GetList(
182 'ACTIVE_DATE' =>
'Y',
183 'CHECK_PERMISSIONS' =>
'Y',
184 'MIN_PERMISSION' =>
'R',
188 [
'ID',
'IBLOCK_ID',
'TYPE']
190 $element = $iterator->Fetch();
198 $sku = $this->loadSkuById((
int)$element[
'IBLOCK_ID'], (
int)$element[
'ID']);
202 $sku = $this->loadFirstSkuForProduct((
int)$element[
'IBLOCK_ID'], (
int)$element[
'ID']);
210 $options[
'resetSku'] =
true;
212 return $this->prepareResponse($sku, $options);
215 private function loadSkuById(
int $iblockId,
int $skuId): ?
BaseSku
217 $skuRepository = ServiceContainer::getSkuRepository($iblockId);
223 return $skuRepository->getEntityById($skuId);
231 private function loadFirstSkuForProduct(
int $iblockId,
int $productId): ?BaseSku
233 $productRepository = ServiceContainer::getProductRepository($iblockId);
234 if (!$productRepository)
240 $product = $productRepository->getEntityById($productId);
246 return $product->getSkuCollection()->getFirst();
249 private function prepareResponse(BaseSku $sku, array $options = []): ?array
251 $builder =
new BasketBuilder();
252 $basketItem = $builder->createItem();
253 $basketItem->setSku($sku);
255 $priceId = (int)($options[
'priceId'] ?? 0);
258 $basketItem->setPriceGroupId($priceId);
261 if (!empty($options[
'urlBuilder']))
263 $basketItem->setDetailUrlManagerType($options[
'urlBuilder']);
266 $formFields = $basketItem->getFields();
272 if ($basketItem->getPriceItem() && $basketItem->getPriceItem()->hasPrice())
274 $basePrice = $basketItem->getPriceItem()->getPrice();
275 $price = $basketItem->getPriceItem()->getPrice();
276 $currency = $basketItem->getPriceItem()->getCurrency();
277 if (!empty($options[
'currency']) && $options[
'currency'] !== $currency)
279 $basePrice = \CCurrencyRates::ConvertCurrency($price, $currency, $options[
'currency']);
280 $currencyFormat = \CCurrencyLang::GetCurrencyFormat($currency);
281 $decimals = $currencyFormat[
'DECIMALS'] ?? 2;
282 $basePrice = round($basePrice, $decimals);
283 $price = \CCurrencyLang::CurrencyFormat($basePrice, $currency,
false);
285 $currency = $options[
'currency'];
290 $barcode = $sku->getBarcodeCollection()->getFirst();
292 $purchasingPrice = $sku->getField(
'PURCHASING_PRICE');
293 $purchasingCurrency = $sku->getField(
'PURCHASING_CURRENCY');
294 if ($purchasingCurrency !== $options[
'currency'])
296 $purchasingPrice = \CCurrencyRates::ConvertCurrency(
301 $purchasingCurrency = $options[
'currency'];
304 $productProps = $this->getProductProperties($sku);
307 'TYPE' => $sku->getType(),
308 'ID' => $formFields[
'skuId'],
309 'SKU_ID' => $formFields[
'skuId'],
310 'PRODUCT_ID' => $formFields[
'productId'],
311 'CUSTOMIZED' => $isCustomized,
312 'NAME' => $formFields[
'name'],
313 'MEASURE_CODE' => (string)$formFields[
'measureCode'],
314 'MEASURE_RATIO' => $formFields[
'measureRatio'],
315 'MEASURE_NAME' => $formFields[
'measureName'],
316 'PURCHASING_PRICE' => $purchasingPrice,
317 'PURCHASING_CURRENCY' => $purchasingCurrency,
318 'BARCODE' => $barcode ?
$barcode->getBarcode() :
'',
319 'COMMON_STORE_AMOUNT' => $sku->getField(
'QUANTITY'),
320 'COMMON_STORE_RESERVED' => $sku->getField(
'QUANTITY_RESERVED'),
322 'BASE_PRICE' => $basePrice,
323 'CURRENCY_ID' => $currency,
324 'PROPERTIES' => $formFields[
'properties'],
325 'VAT_ID' => $formFields[
'taxId'],
326 'VAT_INCLUDED' => $formFields[
'taxIncluded'],
327 'BRANDS' => $this->getProductBrand($sku),
328 'WEIGHT' => $formFields[
'weight'],
329 'DIMENSIONS' => $formFields[
'dimensions'],
330 'PRODUCT_PROPERTIES' => $productProps,
333 $fields = array_merge($fields, $productProps);
335 $previewImage = $sku->getFrontImageCollection()->getFrontImage();
338 $fields[
'PREVIEW_PICTURE'] = [
339 'ID' => $previewImage->getId(),
341 'WIDTH' => $previewImage->getField(
'WIDTH'),
342 'HEIGHT' => $previewImage->getField(
'HEIGHT'),
346 $formResult = $basketItem->getResult();
348 'skuId' => $formFields[
'skuId'],
349 'productId' => $formFields[
'productId'],
350 'image' => $formResult[
'image'],
351 'detailUrl' => $formResult[
'detailUrl'],
354 $fields[
'DETAIL_URL'] = $formResult[
'detailUrl'];
355 $fields[
'IMAGE_INFO'] = $formResult[
'image'];
356 $fields[
'SKU_TREE'] = $formResult[
'skuTree'];
357 if (isset($options[
'resetSku']))
359 $response[
'skuTree'] =
360 ($formResult[
'skuTree'] !==
'')
361 ? Json::decode($formResult[
'skuTree'])
366 $response[
'fields'] = $fields;
367 $response[
'formFields'] = $formFields;
372 private function getProductIdByBarcode(
string $barcode): ?int
375 'filter' => [
'=BARCODE' => $barcode],
376 'select' => [
'PRODUCT_ID'],
380 if ($barcode = $barcodeRaw->fetch())
388 private function getProductBrand($sku): ?array
390 $product = $sku->getParent();
396 $brand = $product->getPropertyCollection()->findByCode(
'BRAND_FOR_FACEBOOK');
402 $userType = \CIBlockProperty::GetUserType($brand->getUserType());
403 $userTypeMethod = $userType[
'GetUIEntityEditorProperty'];
404 $propertySettings = $brand->getSettings();
405 $propertyValues = $brand->getPropertyValueCollection()->getValues();
406 $description = $userTypeMethod($propertySettings, $propertyValues);
407 $propertyBrandItems = $description[
'data'][
'items'];
409 $selectedBrandItems = [];
411 foreach ($propertyBrandItems as $propertyBrandItem)
413 if (in_array($propertyBrandItem[
'VALUE'], $propertyValues,
true))
415 $selectedBrandItems[] = $propertyBrandItem;
419 return $selectedBrandItems;
422 private function getProductProperties(BaseSku $sku): array
426 foreach ($columns as $columnName)
428 $emptyProps[$columnName] =
'';
432 $productIblockId = $sku->getIblockInfo()->getProductIblockId();
435 $skuId = $sku->getId();
436 $skuIblockId = $sku->getIblockId();
438 if ($skuId && $skuIblockId)
443 return array_merge($emptyProps, $productProps, $skuProps);
446 public function createProductAction(array $fields): ?array
448 $iblockId = (int)$fields[
'IBLOCK_ID'];
450 !\CIBlockSectionRights::UserHasRightTo($iblockId, 0,
'section_element_bind')
451 || !$this->accessController->check(ActionDictionary::ACTION_PRODUCT_ADD)
459 $productFactory = ServiceContainer::getProductFactory($iblockId);
460 if (!$productFactory)
467 $skuRepository = ServiceContainer::getSkuRepository($iblockId);
471 $product = $productFactory
478 ? $product->getSkuCollection()->create()
479 : $product->getSkuCollection()->getFirst()
482 if (!empty($fields[
'BARCODE']))
484 $productId = $this->getProductIdByBarcode($fields[
'BARCODE']);
486 if ($productId !==
null)
488 $elementRaw = ElementTable::getList([
489 'filter' => [
'=ID' => $productId],
490 'select' => [
'NAME'],
495 if ($element = $elementRaw->fetch())
497 $name = $element[
'NAME'];
503 'PRODUCT_SELECTOR_ERROR_BARCODE_EXIST',
505 '#BARCODE#' => htmlspecialcharsbx($fields[
'BARCODE']),
506 '#PRODUCT_NAME#' => htmlspecialcharsbx($name),
517 $sku->getBarcodeCollection()->setSimpleBarcodeValue($fields[
'BARCODE']);
521 if (empty($fields[
'CODE']))
523 $productName = $fields[
'NAME'] ??
'';
525 if ($productName !==
'')
527 $fields[
'CODE'] = (new \CIBlockElement())->generateMnemonicCode($productName, $iblockId);
531 if (isset($fields[
'CODE']) && \CIBlock::isUniqueElementCode($iblockId))
533 $elementRaw = ElementTable::getList([
534 'filter' => [
'=CODE' => $fields[
'CODE']],
539 if ($elementRaw->fetch())
541 $fields[
'CODE'] = uniqid($fields[
'CODE'] .
'_',
false);
545 if (!empty($fields[
'MEASURE_CODE']))
547 $fields[
'MEASURE'] = $this->getMeasureIdByCode($fields[
'MEASURE_CODE']);
551 $measure = MeasureTable::getRow([
553 'filter' => [
'=IS_DEFAULT' =>
'Y'],
557 $fields[
'MEASURE'] = $measure[
'ID'];
561 if (!$this->accessController->check(ActionDictionary::ACTION_PRICE_EDIT))
563 unset($fields[
'PRICE']);
566 $product->setFields($fields);
567 if ($fields[
'MEASURE'] > 0)
569 $sku->setField(
'MEASURE', $fields[
'MEASURE']);
571 if (Option::get(
'catalog',
'default_product_vat_included') ===
'Y')
576 if (isset($fields[
'PRICE']) && $fields[
'PRICE'] >= 0)
579 'PRICE' => (float)$fields[
'PRICE'],
582 if (isset($fields[
'CURRENCY']))
584 $basePrice[
'CURRENCY'] = $fields[
'CURRENCY'];
590 ->getPriceCollection()
598 $result = $product->save();
600 if (!$result->isSuccess())
608 'id' => $sku->getId(),
612 public function updateSkuAction(
int $id, array $updateFields, array $oldFields = []): ?array
614 if (empty($updateFields) || $id <= 0)
619 $repositoryFacade = ServiceContainer::getRepositoryFacade();
620 if (!$repositoryFacade)
625 $sku = $repositoryFacade->loadVariation($id);
632 $parentProduct = $sku->getParent();
635 !$this->accessController->check(ActionDictionary::ACTION_PRODUCT_EDIT)
636 || !\CIBlockElementRights::UserHasRightTo($parentProduct->getIblockId(), $parentProduct->getId(),
'element_edit')
645 !$this->accessController->check(ActionDictionary::ACTION_PRICE_EDIT)
646 || !\CIBlockElementRights::UserHasRightTo($parentProduct->getIblockId(), $parentProduct->getId(),
'element_edit_price')
649 unset($updateFields[
'PRICES']);
652 $result = $this->saveSku($sku, $updateFields, $oldFields);
653 if (!$result->isSuccess())
661 'id' => $sku->getId(),
667 return $this->updateSkuAction($id, $updateFields);
670 public function saveMorePhotoAction(
int $productId,
int $variationId,
int $iblockId, array $imageValues): ?array
673 !$this->accessController->check(ActionDictionary::ACTION_PRODUCT_EDIT)
674 || !\CIBlockElementRights::UserHasRightTo($iblockId, $productId,
'element_edit')
682 $productRepository = ServiceContainer::getProductRepository($iblockId);
683 if (!$productRepository)
691 $product = $productRepository->getEntityById($productId);
701 if ($productId === $variationId)
707 $entity = $product->getSkuCollection()->findById($variationId);
719 $property = $entity->getPropertyCollection()->findByCode(MorePhotoImage::CODE);
720 foreach ($imageValues as $key => $newImage)
722 $newImage = $this->prepareMorePhotoValue($newImage, $entity);
723 if (empty($newImage))
728 if (!$property || !$property->isActive())
730 if (empty($previewPicture))
732 $previewPicture = $newImage;
736 $detailPicture = $newImage;
740 if ($key === DetailImage::CODE)
742 $detailPicture = $newImage;
744 elseif ($key === PreviewImage::CODE)
746 $previewPicture = $newImage;
750 $values[$key] = $newImage;
754 $entity->getImageCollection()->setValues($values);
756 if (isset($detailPicture) && is_array($detailPicture))
758 $entity->getImageCollection()->getDetailImage()->setFileStructure($detailPicture);
761 if (isset($previewPicture) && is_array($previewPicture))
763 $entity->getImageCollection()->getPreviewImage()->setFileStructure($previewPicture);
766 $result = $product->save();
767 if (!$result->isSuccess())
774 $this->commitPendingCollection();
776 return (
new ImageInput($entity))->getFormattedField();
779 private function prepareMorePhotoValue($imageValue, BaseIblockElementEntity $entity)
781 if (empty($imageValue))
786 if (!empty($imageValue[
'token']))
788 return $this->prepareMorePhotoValueByToken($imageValue, $entity);
791 if (is_string($imageValue))
795 static $signer =
null;
796 if ($signer ===
null)
798 $signer =
new Signer;
803 catch (BadSignatureException $e)
810 is_array($imageValue)
811 && !empty($imageValue[
'data'])
812 && is_array($imageValue[
'data'])
815 return \CIBlock::makeFileArray($imageValue[
'data']);
819 is_array($imageValue)
820 && !empty($imageValue[
'base64Encoded'])
821 && is_array($imageValue[
'base64Encoded'])
824 $content = (string)($imageValue[
'base64Encoded'][
'content'] ??
'');
827 $fileName = (string)($imageValue[
'base64Encoded'][
'filename'] ??
'');
828 $fileInfo = \CRestUtil::saveFile($content, $fileName);
830 return $fileInfo ?:
null;
837 private function prepareMorePhotoValueByToken(array $image, BaseIblockElementEntity $entity): ?array
839 $token = $image[
'token'] ??
null;
845 $fileId = $this->getFileIdByToken($token, $entity);
848 return \CIBlock::makeFileArray($fileId,
false,
null, [
'allow_file_id' =>
true]);
854 private function getFileIdByToken(
string $token, BaseIblockElementEntity $entity): ?int
856 $uploader = $this->getUploader($entity);
859 if ($pendingFile && $pendingFile->isValid())
861 $this->addPendingFileToCollection($pendingFile);
863 return $pendingFile->getFileId();
869 private function getUploader(BaseIblockElementEntity $entity): Uploader
871 if ($this->uploader ===
null)
873 $fileController =
new ProductController([
874 'productId' => $entity->getId(),
877 $this->uploader = (
new Uploader($fileController));
880 return $this->uploader;
883 private function addPendingFileToCollection(PendingFile $pendingFile): void
885 $this->getPendingFilesCollection()->add($pendingFile);
888 private function commitPendingCollection(): void
890 $this->getPendingFilesCollection()->makePersistent();
893 private function getPendingFilesCollection(): PendingFileCollection
895 if ($this->pendingFileCollection ===
null)
897 $this->pendingFileCollection =
new PendingFileCollection();
900 return $this->pendingFileCollection;
903 private function saveSku(BaseSku $sku, array $fields = [], array $oldFields = []): Result
905 if ($sku->isNew() && empty($fields[
'CODE']))
907 $productName = $fields[
'NAME'] ??
'';
909 if ($productName !==
'')
911 $fields[
'CODE'] = $this->prepareProductCode($productName);
915 if (!empty($fields[
'MEASURE_CODE']))
917 $fields[
'MEASURE'] = $this->getMeasureIdByCode($fields[
'MEASURE_CODE']);
920 $sectionId = $fields[
'IBLOCK_SECTION_ID'] ??
null;
921 unset($fields[
'IBLOCK_SECTION_ID']);
923 $sku->setFields($fields);
925 if (!empty($fields[
'PRICES']) && is_array($fields[
'PRICES']))
927 $priceCollection = $sku->getPriceCollection();
928 foreach ($fields[
'PRICES'] as $groupId => $price)
930 $priceCollection->setValues([
932 'PRICE' => (
float)$price[
'PRICE'],
933 'CURRENCY' => $price[
'CURRENCY'] ??
null,
939 if (isset($fields[
'BARCODE']))
941 $skuId = $this->getProductIdByBarcode($fields[
'BARCODE']);
942 if ($skuId !==
null && $sku->getId() !== $skuId)
944 $result =
new Result();
946 $elementRaw = ElementTable::getList([
947 'filter' => [
'=ID' => $skuId],
948 'select' => [
'NAME'],
953 if ($element = $elementRaw->fetch())
955 $name = $element[
'NAME'];
961 'PRODUCT_SELECTOR_ERROR_BARCODE_EXIST',
963 '#BARCODE#' => htmlspecialcharsbx($fields[
'BARCODE']),
964 '#PRODUCT_NAME#' => htmlspecialcharsbx($name),
973 $updateBarcodeItem =
null;
974 $barcodeCollection = $sku->getBarcodeCollection();
975 if (isset($oldFields[
'BARCODE']))
977 $updateBarcodeItem = $barcodeCollection->getItemByBarcode($oldFields[
'BARCODE']);
980 if ($updateBarcodeItem)
982 if (empty($fields[
'BARCODE']))
984 $barcodeCollection->remove($updateBarcodeItem);
988 $updateBarcodeItem->setBarcode($fields[
'BARCODE']);
996 ->setBarcode($fields[
'BARCODE'])
999 $barcodeCollection->add($barcodeItem);
1004 $parentProduct = $sku->getParent();
1006 if (isset($fields[
'BRANDS']) && is_array($fields[
'BRANDS']))
1008 $parentProduct->getPropertyCollection()->setValues([
'BRAND_FOR_FACEBOOK' => $fields[
'BRANDS']]);
1011 if (isset($sectionId))
1013 $parentProduct->setField(
'IBLOCK_SECTION_ID', $sectionId);
1017 isset($fields[
'NAME'])
1018 && $parentProduct->getSkuCollection()->count() === 1
1021 $this->changeProductName($parentProduct, $fields[
'NAME']);
1024 return $parentProduct->save();
1027 private function changeProductName(BaseProduct $parentProduct,
string $newName): void
1029 $skuTreeEntity = ServiceContainer::make(
'sku.tree', [
1030 'iblockId' => $parentProduct->getIblockId(),
1032 $skuTree = $skuTreeEntity->load([$parentProduct->getId()]);
1033 if (empty($skuTree))
1035 $parentProduct->setField(
'NAME', $newName);
1040 $skuTreeElement = reset($skuTree);
1041 $existingValues = $skuTreeElement[
'EXISTING_VALUES'] ??
null;
1042 if (!$existingValues)
1044 $parentProduct->setField(
'NAME', $newName);
1049 $hasFilledProperty =
false;
1050 foreach ($existingValues as $existingValue)
1052 $hasFilledProperty = $existingValue[0] !== 0;
1053 if ($hasFilledProperty)
1058 if (!$hasFilledProperty)
1060 $parentProduct->setField(
'NAME', $newName);
1064 private function getMeasureIdByCode(
string $code): ?int
1066 $measure = MeasureTable::getRow([
1068 'filter' => [
'=CODE' => $code],
1072 return (
int) $measure[
'ID'];
1078 private function getMeasureCodeById(
string $id): ?string
1080 $measure = MeasureTable::getRow([
1081 'select' => [
'CODE'],
1082 'filter' => [
'=ID' => $id],
1085 return $measure[
'CODE'] ??
null;
1088 private function prepareProductCode($name): string
1090 return mb_strtolower(\CUtil::translit(
1094 'replace_space' =>
'_',
1095 'replace_other' =>
'',
1097 )).
'_'.random_int(0, 1000);
1102 $productFactory = ServiceContainer::getProductFactory($iblockId);
1103 if (!$productFactory)
1112 return $imageField->getFormattedField();
1117 $productFactory = ServiceContainer::getProductFactory($iblockId);
1118 if (!$productFactory)
1125 $repositoryFacade = ServiceContainer::getRepositoryFacade();
1128 if ($repositoryFacade && $skuId !==
null)
1130 $sku = $repositoryFacade->loadVariation($skuId);
1135 $sku = $productFactory->createEntity();
1139 $imageField->disableAutoSaving();
1141 return $imageField->getComponentResponse();
1146 $skuTree = ServiceContainer::make(
'sku.tree', [
1147 'iblockId' => $iblockId,
1152 return $skuTree->getTreeProperties();
1160 if (!Loader::includeModule(
'ui'))
1171 'options' => $options,
1174 $selectedItems = Dialog::getSelectedItems($items, $dialogOptions)->toArray();
1175 if (!isset($selectedItems[0]))
1180 $item = $selectedItems[0];
1181 if ($item[
'hidden'] ===
true)
1191 return (
bool)\CUserOptions::GetOption(
'mobile',
'iOsLastActivityDate')
1192 || (bool)\CUserOptions::GetOption(
'mobile',
'AndroidLastActivityDate')