Телеграм: @bazarow_ru Почта: mihail@bazarow.ru

Пошагово получить информацию о заказах и сохранить как Excel (xls)

Просмотров: 1683

Задача: Нужно получить информацию о заказах с возможностью указания даты создания "от"" и "до" и выгрузить в Excel файл. Так как заказов может быть очень много, за один шаг это не провернуть (скорее всего, сайт отвалится по таймауту) нужно сделать это пошагово с небольшой паузой между шагами.

Сделано по быстрому, позже дополню заметку, завернув все проделанное в модуль с установщиком, D7 и AJAX.
Пошагово получить информацию о заказах и сохранить как Excel (xls)

Создаем директорию, например: "order_report", что бы к ней не было доступа для всех, можно создать в директории /bitrix/components

В файле init.php добавляем запись, которая создаст пункт меню в админпанели, в разделе "Сервис":

AddEventHandler("main", "OnBuildGlobalMenu", "AddReportMenus");
function AddReportMenus(&$adminMenu, &$moduleMenu){
    $moduleMenu[] = array(
        "parent_menu" => "global_menu_services", 
        "section" => "Генератор отчетов по заказам",
        "sort"        => 1000,
        "url"         => "/bitrix/components/elements_report/step1.php",
        "text"        => 'Генератор отчетов по заказам',
        "title"       => '',
        "icon"        => "form_menu_icon",
        "page_icon"   => "form_page_icon",
        "items_id"    => "",
        "items"       => array()

    );
}

В директории "elements_report" создаем три файла: generated.xls.php, step1.php, step2.php. Файл generated.xls.php оставляем пустым

Суть в том, что мы просто создаем html таблицу с отчетом, а затем с помощью передачи header-s сохраняем и скачиваем ее как xls файл. Таким же образом, можно создать эксель таблицу из HTML для любых данных.

Файл step1.php. Здесь у нас просто форма выбора даты "от" и "до" которая отправляет данные на файл step2.php

<? 
require_once($_SERVER["DOCUMENT_ROOT"] . "/bitrix/modules/main/include/prolog_admin_before.php"); 
$APPLICATION->SetTitle("Генератор отчета по элементам"); 
require($_SERVER["DOCUMENT_ROOT"] . "/bitrix/modules/main/include/prolog_admin_after.php"); 
CJSCore::Init(array("jquery")); 
?> 
<div class="adm-block-wrapper"> 
    <form action="step2.php" method="post"> 
        <input type="text" placeholder="Дата с" onclick="BX.calendar({node: this, field: this, bTime: false});" 
               name="dateFrom"> 
        <input type="text" placeholder="Дата по" onclick="BX.calendar({node: this, field: this, bTime: false});" 
               name="dateTo"> 
        <button type="submit" class="adm-btn adm-btn-save">Создать отчет</button> 
    </form> 
</div> 
<? 
require($_SERVER["DOCUMENT_ROOT"] . BX_ROOT . "/modules/main/include/epilog_admin.php"); ?> 

Файл step2.php: здесь выполняется заполнение файла generated.xls.php.

  • Сначала очищается от предыдущего отчета.
  • Добавляется шапка для верстки и заголовков.
  • С помощью CSaleOrder::GetList получаем всю информацию о заказах отсортированных по ID и с фильтром DATE_INSERT.
  • С nTopCount обрабатываем по 50 заказов за раз
  • Внутри цикла заказа дополучаем информацию о корзине заказа с CSaleBasket::GetList с нужными для отчета данным.
  • Формируем строку таблицы с данными о товаре и добавляем в конец файла generated.xls.php
<?php
require_once($_SERVER["DOCUMENT_ROOT"] . "/bitrix/modules/main/include/prolog_admin_before.php");
$APPLICATION->SetTitle("Генератор отчета по заказам");
CJSCore::Init(array("jquery"));
require($_SERVER["DOCUMENT_ROOT"] . "/bitrix/modules/main/include/prolog_admin_after.php");
use Bitrix\Main\Loader;
use Bitrix\Sale;
Loader::includeModule("sale");
$generated_xls_php = 'generated.xls.php';
if (!empty($_POST['lastOrderId'])) {
    $arFilter = array(
        ">=DATE_INSERT" => $_POST['dateFrom'],
        "<=DATE_INSERT" => $_POST['dateTo'],
        ">ID" => $_POST['lastOrderId']
    );
} else {
    $arFilter = array(
        ">=DATE_INSERT" => $_POST['dateFrom'],
        "<=DATE_INSERT" => $_POST['dateTo']
    );
    file_put_contents($generated_xls_php, '');
    $fileHeader = '<?
Header("Content-Type: application/force-download");
Header("Content-Type: application/octet-stream");
Header("Content-Type: application/download");
Header("Content-Disposition: attachment;filename=excel_orders.xls");
Header("Content-Transfer-Encoding: binary");
?>
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <style>
        td {
            mso-number-format: \@;
        }

        .number0 {
            mso-number-format: 0;
        }

        .number2 {
            mso-number-format: Fixed;
        }
    </style>
</head>
<body>

<table border="1">
    <tr>
        <td>ID заказа</td>
        <td>ID товара</td>
        <td>Наименование товара</td>
        <td>Наименование [ID] товара</td>
        <td>Заказ: Доставка</td>
        <td>Оплата: Дата оплаты</td>
        <td>Оплата: Сумма</td>
        <td>Отгрузка: Дата отгрузки</td>
        <td>Общее количество в заказе</td>
        <td>Цена товара</td>
        <td>Скидка на товар</td>
        <td>Количество товара</td>
        <td>Сумма товара</td>
        <td>Налог (%)</td>
        <td>Оплата: Дата докуммента возврата</td>
        <td>Статус: Наименование</td>
        <td>Заказ: Заказ отменён</td>
    </tr>';
    file_put_contents($generated_xls_php, $fileHeader, FILE_APPEND);
}

$dbRes = CSaleOrder::GetList(
    array(
        'ID' => 'ASC'
    ),
    $arFilter,
    false,
    array(
        'nTopCount' => '50'
    ),
    array(
        'ID',
        'PRICE_DELIVERY',
        'DATE_PAYED',
        'SUM_PAID',
        'DATE_DEDUCTED',
        'TAX_VALUE',
        'STATUS_ID'
    ),
    false,
);
$i = 0;
while ($order = $dbRes->Fetch()) {
    $dbBasketItems = CSaleBasket::GetList(
        array("NAME" => "ASC",),
        array("ORDER_ID" => $order['ID']),
        false,
        false,
        array("PRODUCT_ID", "QUANTITY", "NAME", "PRICE", "DISCOUNT_VALUE")
    );
    while ($arItems = $dbBasketItems->Fetch()) {
        $arBasketItems[] = $arItems;
    }
    if ($order['CANCELED'] == 'Y') {
        $canceled = 'Да';
    }

    $statusList = CSaleStatus::GetList(
        array(),
        array('ID' => $order['STATUS_ID']),
        false,
        false,
        array('NAME')
    );
    while ($status = $statusList->Fetch()) {
        $statusName = $status['NAME'];
    }

    foreach ($arBasketItems as $k => $arBasketItem) {
        $orderData = '<tr>
                    <td>' . $order['ID'] . '</td>
                    <td>' . $arBasketItem['PRODUCT_ID'] . '</td>
                    <td>' . $arBasketItem['NAME'] . '</td>
                    <td>' . $arBasketItem['NAME'] . ' [' . $arBasketItem['PRODUCT_ID'] . ']</td>
                    <td>' . $order['PRICE_DELIVERY'] . '</td>
                    <td>' . $order['DATE_PAYED'] . '</td>
                    <td>' . $order['SUM_PAID'] . '</td>
                    <td>' . $order['DATE_DEDUCTED'] . '</td>
                    <td>' . $arBasketItem['QUANTITY'] . '</td>
                    <td>' . $arBasketItem['PRICE'] . '</td>
                    <td>' . $arBasketItem['DISCOUNT_VALUE'] . '</td>
                    <td>' . $arBasketItem['QUANTITY'] . '</td>
                    <td>' . $arBasketItem['QUANTITY'] * $arBasketItem['PRICE'] . '</td>
                    <td>' . $order['TAX_VALUE'] . '</td>
                    <td>' . $order['DATE_CANCELED'] . '</td>
                    <td>' . $statusName . '</td>
                    <td>' . $canceled . '</td>
                  </tr>';
        file_put_contents($generated_xls_php, $orderData, FILE_APPEND);
    }
    $i++;
    $lastOrderId = $order['ID'];
}
if ($i > 1) { ?>
    <form action="" method="post" id="postStep" style="display: none;">
        <input type="text" name="lastOrderId" value="<?= $lastOrderId ?>">
        <input type="text" name="dateFrom" value="<?= $_POST['dateFrom'] ?>">
        <input type="text" name="dateTo" value="<?= $_POST['dateTo'] ?>">
        <button type="submit"></button>
    </form>
<div class="waitwindow" style="width: 500px;">
            Отчет готовится с шагом <?=$i?> заказов за запрос и паузой 1 секунда
</div>
    <script>
        function postForm() {
            $("#postStep").submit();
        }
        setTimeout(postForm, 1000);
    </script>
<?php
} else {
    file_put_contents($generated_xls_php, '</table></body></html>', FILE_APPEND);?>
    <a href="generated.xls.php" class="adm-btn adm-btn-save">Скачать отчет</a>
    <a href="step1.php" class="adm-btn adm-btn-save">Создать новый отчет</a>
    <?php
    //LocalRedirect('generated.xls.php');
}?>
<?php
require($_SERVER["DOCUMENT_ROOT"] . BX_ROOT . "/modules/main/include/epilog_admin.php"); ?>
  • В конце обработки пороции из 50-ти заказов, отправляем форму на этуже страницу- с изначально заданными датами и ID последнего обработанного заказа.
  • Эта форма будет отправляться до тех пор, пока колличество заказов больше одного. После обработки последнего выведутся ссылки на скачивание отчета и создание нового.

При желании можете расскомментировать строку LocalRedirect('generated.xls.php');, тогда отчет скачается сразу по готовности.

Улучшение, генерируем xls сразу, без промежуточного php файла

Если файл generated.xls.php получается очень большим, сервер может не справиться с отдачей его как xls файла. На самом деле, можно сразу создать xls файл с таблицей, он точно также откроется экселем.

В файле step2.php меняем
$generated_xls_php = 'generated.xls.php';
на $generated_xls_php = 'generated.xls';

Из заголовка генерируемого файла убираем:

<?
Header("Content-Type: application/force-download");
Header("Content-Type: application/octet-stream");
Header("Content-Type: application/download");
Header("Content-Disposition: attachment;filename=excel_orders.xls");
Header("Content-Transfer-Encoding: binary");
?>

Ссылку на скачивание готового отчета меняем на:

<a href="generated.xls" download class="adm-btn adm-btn-save">
	Скачать отчет
</a>
Михаил Базаров 19.09.2022
Паузу между шагами можно выставит больше, если хостинг/сервер не справляется. Также можно увеличить или уменьшить количество заказов обрабатываемых за шаг и дополнить/удалить данные в отчете. Да, как и сказано выше: позже сделаю это модулем с Ajax и прогресс баром- пока так.
Александр Силуянов 19.09.2022
Благодарю, очень нужный функционал!

Записная книжка разработчика

Примерно с 2013-го года пишу заметки по разработке сайтов на Битрикс.
Вы можете задавать уточняющие вопросы в комментариях- отвечаю или дополняю заметки по возможности.

Вывести новости из конкретного раздела инфоблока Битрикс Просмотров: 55236 Бывает что требуется вывести конкретные новости или статьи из одного раздела инфоблока в Б... Вывести общее количество товаров в шаблоне корзины 1С-Битрикс Просмотров: 7033 Задача: вывести общее количество товаров в корзине (именно количество единиц товара, а не ... Спойлер в списке новостей Битрикс Просмотров: 12178 Иногда нужно сделать спойлеры в копонентах Битрикс, в основном конечно, имеет смысл при оч... Хостинг панель BrainyCp: оптимизация под 1С-битрикс Просмотров: 8131 В этой видео-заметке расскажу как установить и оптимизировать панель управления сервером B... Отфильтровать новости в Битрикс за заданный период Просмотров: 16690 Иногда, нужно отфильтровать элементы (новости например) по дате в Битрикс, с помощью компо... Заполнить картинки разделов каталога из картинок товаров Просмотров: 944 Задача: в каталоге, разделы и подразделы выводятся в виде плитки над товарами, с картинкам... Простые калькуляторы в карточке товара каталога на Битрикс Просмотров: 16174 На одном из создаваемых сайтов было необходимо сделать небольшой калькулятор и предварител... Ускорение работы сайта на 1С-Битрикс Просмотров: 17734 Данная статья написана специально под видеоролик (приложен в конце статьи), опубликованный... Очистить брошенные корзины с помощью API 1С-Битрикс Просмотров: 1121 Задача: в интернет-магазине накапливается большое количество брошенных корзин, нужно их оч... Заполнить свойство инфоблока ценой из торгового каталога Просмотров: 5090 Если вам по какой-то причине нужно скопировать цену товара в свойство этого же инфоблока. ... Определить местоположение пользователя и показать магазины поблизости... Просмотров: 1831 Задача: в мобильном приложении, которое открывает сайт на 1С-Битрикс внутри себя, нужно оп... Запретить изменения описаний товаров при выгрузке из 1С УТ на сайт Просмотров: 18106 При разработке сайтов на Битрикс, с интеграцией с 1С Управление Тороговлей, нужно запретит... Вывод элементов с помощью API битрикс Просмотров: 23961 Иногда, выводить элементы инфоблока с помощью компонентов, может оказаться избыточным. Нап... Вывести компонент новостей на страницу в Битрикс Просмотров: 65106 Посмотрел Яндекс метрику на свой сайт, и решил что буду частенько писать сюда именно то, ч... Установка вебсервера на Apple M1 (нативный ARM стек), оптимизированны... Просмотров: 7405 Появилась задачка, установить стек MAMP (macOS, Apache, MYSQL, PHP) на рабочий ноутбук с п... Цвета торговых предложений из Highload инфоблоков в списке товаров Просмотров: 14905 Подвернулся на доработку очень старый сайт, работал еще на 11-ой версии Битрикс. Выполнили... Самодельная форма добавления элемента на API Битрикс Просмотров: 45276 Компонент iblock.element.add.form написан таким образом, что вы не сможете (не попотев изр... Вывести артикул в печатную форму бланка заказа Просмотров: 1073 Задача: в печатную форму бланка заказа добавить артикулы товаров и данные о пользователе. ... Вывести разделы инфоблока в которых находится элемент Битрикс Просмотров: 17017 Если нужно в карточке товара или новости, вывести структуру разделов в котором находится э... Сгенерировать скидочные купоны, при заказе и отправить на почту Просмотров: 1693 Задача: после того как пользователь сделал заказ, нужно сгенерировать одноразовые купоны д...