Налаштування передплати зниження ціни товару 1С-Бітрікс

Наша компанія займається розробкою, підтримкою та обслуговуванням рішень на Бітрікс та Бітрікс24 будь-якої складності. Від простих односторінкових сайтів до складних інтернет-магазинів, CRM систем з інтеграцією 1С та телефонії. Досвід розробників підтверджено сертифікатами від вендора.
Пропоновані послуги
Показано 1 з 1 послугУсі 1626 послуг
Налаштування передплати зниження ціни товару 1С-Бітрікс
Проста
~1 робочий день
Часті питання

Наші компетенції:

Етапи розробки

Останні роботи

  • image_website-b2b-advance_0.png
    Розробка сайту компанії B2B ADVANCE
    1262
  • image_bitrix-bitrix-24-1c_fixper_448_0.png
    Розробка веб-сайту для компанії ФІКСПЕР
    851
  • image_bitrix-bitrix-24-1c_development_of_an_online_appointment_booking_widget_for_a_medical_center_594_0.webp
    Розробка на базі Бітрікс, Бітрікс24, 1С для компанії Development of an Online
    585
  • image_bitrix-bitrix-24-1c_mirsanbel_458_0.webp
    Розробка на базі 1С Підприємство для компанії МИРСАНБЕЛ
    751
  • image_crm_dolbimby_434_0.webp
    Розробка сайту на CRM Бітрікс24 для компанії DOLBIMBY
    657
  • image_crm_technotorgcomplex_453_0.webp
    Розробка на базі Бітрікс24 для компанії ТЕХНОТОРГКОМПЛЕКС
    989

Налаштування підписки на зниження ціни товару 1С-Бітрікс

Покупець дивиться на дорогий товар, не купує. Кнопка «Повідомити про зниження ціни» дає шанс повернути його саме в момент, коли ціна стала прийнятною. Це працює краще за будь-яке розсилання по базі, тому що користувач сам виявив інтерес до конкретного товару.

Структура даних

HL-блок PriceSubscription для зберігання підписок:

b_uts_price_subscription
├── ID
├── UF_USER_ID      — ID користувача (0 = незареєстрований)
├── UF_EMAIL        — email для повідомлення
├── UF_PRODUCT_ID   — ID товару (b_iblock_element.ID)
├── UF_TARGET_PRICE — бажана ціна (0 = будь-яке зниження)
├── UF_CURRENT_PRICE — ціна на момент підписки
├── UF_ACTIVE       — чи активна підписка
├── UF_NOTIFIED     — чи було надіслано повідомлення
└── UF_DATE_CREATE  — дата створення

Форма підписки

На картці товару кнопка з'являється поряд з ціною:

// У шаблоні картки, під блоком ціни
<?php if (!$arResult['CATALOG_ITEM']['CAN_BUY']): // тільки якщо не можна купити зараз ?>
<?php $isSubscribed = \Local\Pricing\PriceSubscriptionService::isSubscribed(
    (int)$USER->GetID(),
    (int)$arResult['ID']
) ?>
<button class="btn-price-subscribe js-price-subscribe
               <?= $isSubscribed ? 'is-active' : '' ?>"
        data-product-id="<?= $arResult['ID'] ?>"
        data-current-price="<?= $arResult['CATALOG_PRICE']['PRICE'] ?>">
    <?= $isSubscribed ? 'Підписка активна' : 'Повідомити про зниження ціни' ?>
</button>
<?php else: ?>

Для авторизованих — AJAX-підписка. Для гостей — показуємо модал із полем email:

document.querySelectorAll('.js-price-subscribe').forEach(btn => {
    btn.addEventListener('click', async () => {
        const productId    = btn.dataset.productId;
        const currentPrice = btn.dataset.currentPrice;
        const email        = window.__userEmail || null; // передаємо з PHP

        if (!email) {
            // Показуємо модал із полем email
            showPriceSubscribeModal(productId, currentPrice);
            return;
        }

        const res = await fetch('/local/ajax/price-subscribe.php', {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify({ product_id: productId, email, current_price: currentPrice }),
        }).then(r => r.json());

        if (res.success) {
            btn.classList.add('is-active');
            btn.textContent = 'Підписка активна';
        }
    });
});

AJAX-обробник підписки

// /local/ajax/price-subscribe.php
\Bitrix\Main\Application::getInstance()->initializeExtended();

global $USER;
$data = json_decode(file_get_contents('php://input'), true);

$productId    = (int)($data['product_id'] ?? 0);
$email        = filter_var($data['email'] ?? '', FILTER_VALIDATE_EMAIL);
$currentPrice = (float)($data['current_price'] ?? 0);

if (!$productId || !$email) {
    echo json_encode(['success' => false, 'error' => 'Invalid data']);
    exit;
}

$result = \Local\Pricing\PriceSubscriptionService::subscribe(
    userId:       (int)$USER->GetID(),
    email:        $email,
    productId:    $productId,
    currentPrice: $currentPrice,
    targetPrice:  (float)($data['target_price'] ?? 0),
);

echo json_encode(['success' => $result]);

Сервіс управління підписками

namespace Local\Pricing;

use Bitrix\Highloadblock\HighloadBlockTable;

class PriceSubscriptionService
{
    public static function subscribe(
        int    $userId,
        string $email,
        int    $productId,
        float  $currentPrice,
        float  $targetPrice = 0
    ): bool {
        // Перевіряємо дублікат
        if (self::isSubscribed($userId, $productId, $email)) {
            return true; // вже підписаний
        }

        $dataClass = self::getDataClass();
        $result    = $dataClass::add([
            'UF_USER_ID'      => $userId,
            'UF_EMAIL'        => $email,
            'UF_PRODUCT_ID'   => $productId,
            'UF_TARGET_PRICE' => $targetPrice,
            'UF_CURRENT_PRICE' => $currentPrice,
            'UF_ACTIVE'       => true,
            'UF_NOTIFIED'     => false,
        ]);

        return $result->isSuccess();
    }

    public static function isSubscribed(int $userId, int $productId, string $email = ''): bool
    {
        $dataClass = self::getDataClass();
        $filter    = ['UF_PRODUCT_ID' => $productId, 'UF_ACTIVE' => true];

        if ($userId > 0) {
            $filter['UF_USER_ID'] = $userId;
        } elseif ($email) {
            $filter['UF_EMAIL'] = $email;
        }

        return (bool)$dataClass::getRow(['filter' => $filter, 'select' => ['ID']]);
    }
}

Агент перевірки зниження цін

Агент запускається раз на годину, знаходить товари з ціною, що впала, і надсилає повідомлення:

namespace Local\Pricing;

class PriceDropNotifierAgent
{
    public static function run(): string
    {
        $dataClass   = PriceSubscriptionService::getDataClass();
        $subscriptions = $dataClass::getList([
            'filter' => ['UF_ACTIVE' => true, 'UF_NOTIFIED' => false],
            'select' => ['ID', 'UF_EMAIL', 'UF_PRODUCT_ID', 'UF_CURRENT_PRICE', 'UF_TARGET_PRICE'],
        ]);

        while ($sub = $subscriptions->fetch()) {
            $currentPrice = self::getCurrentPrice((int)$sub['UF_PRODUCT_ID']);

            if ($currentPrice === null) continue;

            $priceDropped = $currentPrice < $sub['UF_CURRENT_PRICE'];
            $targetReached = $sub['UF_TARGET_PRICE'] > 0
                ? $currentPrice <= $sub['UF_TARGET_PRICE']
                : $priceDropped;

            if ($targetReached) {
                self::sendNotification($sub, $currentPrice);
                $dataClass::update($sub['ID'], [
                    'UF_NOTIFIED'     => true,
                    'UF_CURRENT_PRICE' => $currentPrice,
                ]);
            }
        }

        return '\Local\Pricing\PriceDropNotifierAgent::run();';
    }

    private static function getCurrentPrice(int $productId): ?float
    {
        $res = \CCatalogPrice::GetList(
            [],
            ['PRODUCT_ID' => $productId, 'CATALOG_GROUP_ID' => 1]
        )->Fetch();

        return $res ? (float)$res['PRICE'] : null;
    }

    private static function sendNotification(array $sub, float $newPrice): void
    {
        $product = \CIBlockElement::GetByID($sub['UF_PRODUCT_ID'])->GetNext();
        if (!$product) return;

        \CEvent::Send('PRICE_DROP_NOTIFICATION', SITE_ID, [
            'EMAIL'       => $sub['UF_EMAIL'],
            'PRODUCT_NAME' => $product['NAME'],
            'PRODUCT_URL'  => 'https://' . SITE_SERVER_NAME . $product['DETAIL_PAGE_URL'],
            'OLD_PRICE'    => number_format($sub['UF_CURRENT_PRICE'], 0, '', ' '),
            'NEW_PRICE'    => number_format($newPrice, 0, '', ' '),
            'SAVINGS'      => number_format($sub['UF_CURRENT_PRICE'] - $newPrice, 0, '', ' '),
        ]);
    }
}

Терміни реалізації

Конфігурація Термін
Підписка (кнопка + AJAX + HL-блок) 2–3 дні
+ агент перевірки цін + email-повідомлення +2 дні
+ цільова ціна, особистий кабінет підписок +2–3 дні