Yandex Metrika
sanches.free 19 просмотров

Отправка прайс‑листа по веб‑форме Bitrix: статусы, cron и файл во вложении

Сценарий

Сайт собирает e-mail через модуль Веб‑формы; сохранённые ответы в статусе «новый» дорабатывает cron из CLI: отправляет файл прайса письмом и переводит статус в «отправлено», как было в источнике .ru, переформулированном здесь.

Отложенная отправка спасает HTTP‑ответ от ожидания SMTP и упрощает логирование ошибок транспорта.

Форма и два статуса

Нужна форма с вопросом на e‑mail (запомните символьный SID поля — например mail или email) и два статуса результата для цепочки «ожидает рассылки» → «файл ушёл».

Почтовый шаблон

Тип события задаёт код вроде PRICE_FROM_FORM. В заголовках шаблона:

  • #DEFAULT_EMAIL_FROM# в поле от кого;
  • #USER_EMAIL# или другой символьный код во «Кому» — тот же ключ передаём в массиве ниже как USER_EMAIL.

Один файл — много получателей за прогон

Для всех результатов с «новым» статусом за один проход один раз сохраняем прайс в таблице файлов через CFile::SaveFile, переиспользуем $fileId в CEvent::SendImmediate, в конце снимаем регистрацию CFile::Delete($fileId). Связка с общей моделью — в статье о почтовых событиях.

Пример скрипта (только CLI)

Ставьте задачу в cron на пользователя, у которого есть чтение по пути к прайсу. Пути настроены под файл внутри дерева сайта как в оригинале: $_SERVER['DOCUMENT_ROOT'] — родитель текущего каталога скрипта (cron/send.php в корне).

<?php
declare(strict_types=1);

if (php_sapi_name() !== 'cli') {
    die('Access denied');
}

$_SERVER['DOCUMENT_ROOT'] = dirname(__DIR__);

define('LANGUAGE_ID', 'ru');
define('NO_KEEP_STATISTIC', true);
define('NOT_CHECK_PERMISSIONS', true);

require $_SERVER['DOCUMENT_ROOT'] . '/bitrix/modules/main/include/prolog_before.php';

CModule::IncludeModule('form');

final class FormMailer
{
    public function __construct(
        private int $formId = 15,
        private int $statusNewId = 18,
        private int $statusSentId = 19,
        private string $attachFile = '/var/www/example.org/upload/price.xlsx',
        private string $attachName = 'price.xlsx',
        private string $formMailSid = 'mail',
        private string $mailFrom = 'info@example.org',
        private string $uploadDir = 'pricelist',
        private string $eventName = 'PRICE_FROM_FORM',
        private string $siteId = 's1',
    ) {
        if (!is_readable($this->attachFile)) {
            throw new RuntimeException('File not found: ' . $this->attachFile);
        }
    }

    public function run(): void
    {
        $filtered = '';
        $list = CFormResult::GetList(
            $this->formId,
            's_timestamp',
            'desc',
            ['STATUS_ID' => $this->statusNewId],
            $filtered,
            'N',
            false
        );

        if (!$list || $list->SelectedRowsCount() === 0) {
            return;
        }

        $fileId = (int) CFile::SaveFile(
            [
                'name'      => $this->attachName,
                'tmp_name'  => $this->attachFile,
                'old_file'  => '0',
                'del'       => 'N',
                'MODULE_ID' => '',
                'description' => '',
            ],
            $this->uploadDir,
            false,
            false
        );

        if ($fileId <= 0) {
            fwrite(STDERR, 'CFile::SaveFile failed' . PHP_EOL);

            return;
        }

        while ($row = $list->Fetch()) {
            try {
                $this->notify((int)$row['ID'], $fileId);
                CFormResult::SetStatus((int)$row['ID'], $this->statusSentId, 'N');
            } catch (Throwable $e) {
                fwrite(STDERR, 'Result #' . (int)$row['ID'] . ': ' . $e->getMessage() . PHP_EOL);
            }
        }

        CFile::Delete($fileId);
    }

    private function notify(int $resultId, int $fileId): void
    {
        $resultStub = [];
        $answerStub = [];
        $data = CFormResult::GetDataByID(
            $resultId,
            [$this->formMailSid],
            $resultStub,
            $answerStub
        );
        $email = trim((string)($data[$this->formMailSid][0]['USER_TEXT'] ?? ''));
        if ($email === '' || filter_var($email, FILTER_VALIDATE_EMAIL) === false) {
            throw new RuntimeException('empty or invalid email');
        }

        $event = new CEvent();
        $event->SendImmediate(
            $this->eventName,
            $this->siteId,
            [
                'USER_EMAIL'           => $email,
                'DEFAULT_EMAIL_FROM'   => $this->mailFrom,
            ],
            'N',
            '',
            [$fileId]
        );
    }
}

(new FormMailer())->run();
  • Значения свойств класса синхронизируйте с админкой: ID формы, SID поля почты и ID статусов.
  • При желании ошибку по некорректному e-mail переводят в отдельный статус результата, а не в пустой catch.

Итог

Цепочка: форма сохранилась → cron поднял файл и почту по новым результатам → статус закрывает повторную отправку тем же файлом при следующих проходах.

Не хотите копаться сами?

Починю за 1-3 дня. Без предоплаты — оплата по результату.

15+ лет опыта с 1С-Битрикс · Без предоплаты · 7 дней гарантии