Добавление товаров в корзину из Excel прайса
Представьте что Вам необходимо работать с оптовыми продажами. Или же сайт специализируется на дешевых товарах, которые пользователь будет покупать в больших количествах.
Вы столкнетесь с проблемой, когда для добавления сотни товаров в корзину нужно будет тратить довольно много времени.
В этом случае Вам может помочь добавление товаров в корзину из excel файла.
Основная идея в том, чтобы дать скачать пользователю прайс с товарами, в котором будет проставлено необходимое количество товара для покупки.
После этого пользователь загружает такой прайс обратно на сайт, и скрипт добавляет товары из прайса в корзину.
Я реализовал это в виде компонента gricuk:basket.from.file. Дальше приведу код:
class.php
<?php
use Bitrix\Main\Application;
use Bitrix\Main\Loader;
use Bitrix\Sale;
use Bitrix\Main\SystemException;
if (!defined("B_PROLOG_INCLUDED") || B_PROLOG_INCLUDED !== true) die();
class BasketFromFile extends CBitrixComponent
{
public function onPrepareComponentParams($arParams)
{
\Bitrix\Main\Loader::includeModule("iblock");
\Bitrix\Main\Loader::includeModule("catalog");
\Bitrix\Main\Loader::includeModule("sale");
return $arParams;
}
public function executeComponent()
{
$request = Application::getInstance()->getContext()->getRequest();
$this->arResult["ACTION"] = $request->get("action");
if ($this->arResult["ACTION"] == "download") {
$this->downloadPriceForCurrentUser();
}
if ($this->arResult["ACTION"] == "addToBasket") {
$this->addFromFile();
}
$this->includeComponentTemplate();
}
public function addFromFile()
{
global $USER;
$dbProducts = \Bitrix\Catalog\ProductTable::getList([
"select" => [
"NAME" => "IBLOCK_ELEMENT.NAME",
"*"
],
"filter" => [
"IBLOCK_ELEMENT.ACTIVE" => "Y"
],
]);
$existProducts = [];
foreach ($dbProducts as $product) {
$existProducts[$product["ID"]] = $product;
}
$resultArray = [["#", "Категория", "Название", "Доступно, шт", "Цена, руб", "Количество"]];
try {
$inputFileName = $_FILES["excelFile"]["tmp_name"];
$reader = new \PhpOffice\PhpSpreadsheet\Reader\Xlsx();
$spreadsheet = $reader->load($inputFileName);
$worksheet = $spreadsheet->getActiveSheet();
$products = [];
foreach ($worksheet->getRowIterator() as $row) {
$cellIterator = $row->getCellIterator();
$cellIterator->setIterateOnlyExistingCells(false);
$cells = [];
foreach ($cellIterator as $cell) {
$cells[] = $cell->getValue();
}
if (intval($cells[5]) > 0) {
$products[] = [
"ID" => $cells[0],
"QUANTITY" => $cells[5],
];
}
}
unset($products[0]);
\CSaleBasket::DeleteAll(\CSaleBasket::GetBasketUserID());
foreach ($products as $product) {
if (isset($existProducts[$product["ID"]])) {
$addResult = $this->addProductToBasket($product["ID"], $product["QUANTITY"]);
if (!$addResult->isSuccess()) {
$this->arResult["ERRORS"][] = $addResult->getErrorMessages();
}
}
}
} catch (\Exception $e) {
$this->arResult["ERRORS"][] = $e->getMessage();
}
}
public function addProductToBasket($productId, $quantity)
{
\Bitrix\Main\Loader::includeModule("catalog");
$product = \Bitrix\Catalog\ProductTable::getById($productId)->fetch();
$productToAdd = [
"PRODUCT_ID" => $productId
];
if ($product["TYPE"] == \Bitrix\Catalog\ProductTable::TYPE_OFFER) {
$parentProduct = \CCatalogSku::GetProductInfo($productId);
$productProperties = CIBlockPriceTools::GetOfferProperties(
$productId,
$parentProduct["IBLOCK_ID"],
[
"VOLUME"
],
[]
);
if (!empty($productProperties)) {
$productToAdd["PROPS"] = $productProperties;
}
}
return \Bitrix\Catalog\Product\Basket::addProduct(
$productToAdd,
[
"QUANTITY" => $quantity
]
);
}
public function downloadPriceForCurrentUser()
{
global $USER;
$dbProducts = \Bitrix\Catalog\ProductTable::getList([
"select" => [
"NAME" => "IBLOCK_ELEMENT.NAME",
"SECTION_ID" => "IBLOCK_ELEMENT.IBLOCK_SECTION_ID",
"SECTION_NAME" => "IBLOCK_ELEMENT.IBLOCK_SECTION.NAME",
"*"
],
"filter" => [
"IBLOCK_ELEMENT.ACTIVE" => "Y",
"IBLOCK_ELEMENT.IBLOCK_ID" => [
\Gricuk\Main\Conf::ID_IBLOCK_CATALOG,
\Gricuk\Main\Conf::ID_IBLOCK_OFFERS,
],
[
"LOGIC" => "OR",
[
"!TYPE" => \Bitrix\Catalog\ProductTable::TYPE_SKU,
">QUANTITY" => 0,
],
[
"TYPE" => \Bitrix\Catalog\ProductTable::TYPE_SKU,
]
]
],
"order" => [
"IBLOCK_ELEMENT.IBLOCK_SECTION.LEFT_MARGIN",
"IBLOCK_ELEMENT.SORT",
"IBLOCK_ELEMENT.ID",
]
]);
$products = [];
$skuProducts = [];
foreach ($dbProducts as $product) {
if ($product["TYPE"] != \Bitrix\Catalog\ProductTable::TYPE_SKU) {
$product["PRICE"] = CCatalogProduct::GetOptimalPrice($product["ID"]);
} else {
$skuProducts[$product["ID"]] = $product["ID"];
}
$products[$product["ID"]] = $product;
}
$skuOffersMap = CCatalogSku::getOffersList(array_keys($skuProducts));
foreach ($skuOffersMap as $skuId => $offers) {
foreach ($offers as $offerId => $offer) {
if (isset($products[$skuId]) && isset($products[$offerId])) {
$products[$skuId]["OFFERS"][$offerId] = $products[$offerId];
unset($products[$offerId]);
}
}
}
$resultArray = [["#", "Категория", "Название", "Доступно, шт", "Цена, руб", "Количество"]];
foreach ($products as $product) {
if ($product["TYPE"] == \Bitrix\Catalog\ProductTable::TYPE_OFFER) {
continue;
}
if ($product["TYPE"] != \Bitrix\Catalog\ProductTable::TYPE_SKU) {
$resultArray[] = [
$product["ID"],
$product["SECTION_NAME"],
$product["NAME"],
$product["QUANTITY"],
$product["PRICE"]["RESULT_PRICE"]["DISCOUNT_PRICE"],
""
];
}
if (isset($product["OFFERS"])) {
foreach ($product["OFFERS"] as $offer) {
$resultArray[] = [
$offer["ID"],
$product["SECTION_NAME"],
$offer["NAME"],
$offer["QUANTITY"],
$offer["PRICE"]["RESULT_PRICE"]["DISCOUNT_PRICE"],
""
];
}
}
}
$spreadsheet = new \PhpOffice\PhpSpreadsheet\Spreadsheet();
$sheet = $spreadsheet->getActiveSheet();
$sheet->fromArray($resultArray);
$sheet->getColumnDimension('A')->setWidth(10);//#
$sheet->getColumnDimension('B')->setWidth(35);//Категория
$sheet->getColumnDimension('C')->setWidth(62);//Название
$sheet->getColumnDimension('D')->setWidth(14);//Доступно, шт
$sheet->getColumnDimension('E')->setWidth(14);//Цена, руб
$sheet->getColumnDimension('F')->setWidth(14);//Количество
$writer = new \PhpOffice\PhpSpreadsheet\Writer\Xlsx($spreadsheet);
$filePath = \Bitrix\Main\Application::getDocumentRoot() . "/upload/tmp/price-{$USER->GetID()}.xlsx";
$writer->save($filePath);
\Gricuk\Utils\Helpers::file_force_download($filePath);
die();
}
}
template.php
<? if (!defined("B_PROLOG_INCLUDED") || B_PROLOG_INCLUDED !== true) die();
/** @var array $arParams */
/** @var array $arResult */
/** @global CMain $APPLICATION */
/** @global CUser $USER */
/** @global CDatabase $DB */
/** @var CBitrixComponentTemplate $this */
/** @var string $templateName */
/** @var string $templateFile */
/** @var string $templateFolder */
/** @var string $componentPath */
/** @var CBitrixComponent $component */
$this->setFrameMode(true);
?>
<div class="row">
<div class="col-sm-12">
Для того чтобы добавить товары в корзину с помощью Excell прайса необходимо:
<ul>
<li>Скачать пример прайса, нажав кнопку <b>"Скачать шаблон"</b></li>
<li>В полученном xlsx файле ввести необходимое количество товара в колонку <b>"Количество"</b></li>
<li>Сохранить файл</li>
<li>На данной странице нажать кнопку <b>"Выберите файл"</b>. В открывшемся окне, указать файл, который был
ранее сохранен
</li>
<li>На странице сайта нажать на кноку <b>"Добавить в корзину"</b></li>
</ul>
<h4><b>Внимание!</b> Цены в файле не окончательные! При добавлении в корзину они могут измениться.</h4>
<h4><b>Внимание!</b> Всё что уже есть в корзине будет удалено!</h4>
</div>
<div class="col-sm-12">
<form action="<?= POST_FORM_ACTION_URI ?>" method="POST" enctype="multipart/form-data">
<input type="hidden" name="action" value="addToBasket">
<div class="form-group">
<label for="excel-price">Файл Excel</label>
<input type="file" id="excel-price" class="form-control" name="excelFile" required>
</div>
<div class="form-group">
<a href="?action=download" class="btn btn-link">Скачать шаблон</a>
<button class="btn btn-default">Добавить в корзину</button>
</div>
</form>
</div>
<? if ($arResult["ACTION"] == "addToBasket" && empty($arResult["ERRORS"])): ?>
<div class="col-sm-12">
<?
ShowMessage(["MESSAGE" => "Товары из файла успешно добавлены в корзину", "TYPE" => "OK"]);
?>
</div>
<? endif ?>
<? if ($arResult["ERRORS"]): ?>
<div class="col-sm-12">
<?
foreach ($arResult["ERRORS"] as $error) {
ShowError($error);
}
?>
</div>
<? endif ?>
</div>
Шаблон выполнен без нормального внешнего вида, так как в моем случае компонент используют менеджеры со стороны сайта. Поэтому не было каких то требований к его оформлению.
На сайте выглядит как то так:
Теперь о коде компонента. Как можно увидеть, в нем для работы с Excel используется библиотека PhpSpreadsheet's , поэтому ее надо будет скачать и подключить на сайт.
Код класса можно разделить на 2 части. 1я - формирует прайс для текущего пользователя (метод downloadPriceForCurrentUser). 2я - добавляет товары в корзину из загруженного файла (методы addFromFile и addProductToBasket).
Особое внимание стоит уделить строке в методе addProductToBasket со следующим кодом:
$productProperties = CIBlockPriceTools::GetOfferProperties(
$productId,
$parentProduct["IBLOCK_ID"],
[
"VOLUME"
],
[]
);
В качестве 3 параметра идет массив с символьными кодами свойств торговых предложений, которые необходимо добавлять в корзину. В моем случае это объем.
В конце можно увидеть вызов \Gricuk\Utils\Helpers::file_force_download($filePath);, подробнее об этом можно почитать в статье Принудительная загрузка файла