Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Ошибочное резервирование товара

Ошибочное резервирование товара

Пример решения возникшей проблемы с помощью логирования в Битриксе

Avatar for Evgeny E. Neverov

Evgeny E. Neverov

March 02, 2023
Tweet

More Decks by Evgeny E. Neverov

Other Decks in Programming

Transcript

  1. Проблема На сайте клиента можно покупать коробы с обувью. Один

    короб - одно предложение. Каждый короб продается в единственном экземпляре (доступное кол-во = 1) Резервирование товаров происходит при оформлении заказа После обновления Битрикса в разных заказах стали появляться одни и те же коробы.
  2. Поиск причины Изучаем заказы с одинаковыми товарами. Подробные данные и

    даты смотрим в таблицах • b_sale_order • b_sale_basket • b_sale_basket_reservation Оба заказа зарезервировали товар. Но у одного из заказов время резервирования не совпадало ни со временем оформления заказа, ни со временем добавления товара в корзину. Чтобы выяснить, когда и кем товар резервируется логируем таблицу резервов
  3. Код логгера use Techdir\PhpInterface\Event\Regular; use Bitrix\Main\Diag; use Bitrix\Main\Entity; class Reservation

    extends Regular { public static function getHandlers() { return [ [ "module" => "sale", 'event' => '\Bitrix\Sale\Reservation\Internals\BasketReservation::OnAfterAdd', 'method' => 'logReservation' ], ]; } public static function logReservation(Entity\Event $event) { $path = __DIR__ . '/log-reservation.txt'; $message = (new Diag\LogFormatter())->format("{args}\n{trace}\n{delimiter}\n", [ 'args' => print_r($event->getParameter('fields'), true), 'trace' => Diag\Helper::getBackTrace(20, DEBUG_BACKTRACE_IGNORE_ARGS, 2), ]); (new Diag\FileLogger($path))->info($message); } }
  4. Разбираем логи ... #7: Bitrix\Sale\Reservation\BasketReservationService->add /home/bitrix/www/bitrix/modules/sale/lib/reservequantity.php:303 #8: Bitrix\Sale\ReserveQuantity->addInternal /home/bitrix/www/bitrix/modules/sale/lib/reservequantity.php:247 #9:

    Bitrix\Sale\ReserveQuantity->save /home/bitrix/www/bitrix/modules/sale/lib/reservequantitycollection.php #10: Bitrix\Sale\ReserveQuantityCollection->save /home/bitrix/www/bitrix/modules/sale/lib/basketitem.php:62 #11: Bitrix\Sale\BasketItem->save /home/bitrix/www/bitrix/modules/sale/lib/basketbase.php:489 #12: Bitrix\Sale\BasketBase->save /home/bitrix/www/bitrix/modules/sale/lib/basket.php:228 #13: Bitrix\Sale\Basket->save /home/bitrix/www/local/lib/Rieker/Util/BasketHelper.php:76 #14: Rieker\Util\BasketHelper::clearBasket /home/bitrix/www/local/lib/Rieker/Util/OrderHelper.php:35 #15: Rieker\Util\OrderHelper::getPublicData /home/bitrix/www/local/components/rieker/order/class.php:196 #16: OrderComponent->executeComponent /home/bitrix/www/bitrix/modules/main/classes/general/component.php:660 #17: CBitrixComponent->includeComponent /home/bitrix/www/bitrix/modules/main/classes/general/main.php:1055 #18: CAllMain->IncludeComponent /home/bitrix/www/basket/index.php:26 В логе видим, что: • Резервирование происходит на странице корзины • Удаляются недоступные товары и корзина сохраняется • Резервы сохраняются вместе с корзиной
  5. Смотрим в коде class BasketHelper { // ... public static

    function clearBasket(BasketBase &$basket, $save = false) { if (!SiteHelper::isSewnContextSite()) { $changed = false; foreach ($basket as $basketItem) { if (!$basketItem->canBuy()) { $basketItem->delete(); $changed = true; } } if ($changed && $save) { $basket->save(); } } } // ... } Здесь видно, что при загрузке корзины из нее удаляются недоступные товары. Если корзина была изменена, она сохраняется. И при этом создаются резервы. • Таким образом мы понимаем, как нам повторить ошибку: добавляем несколько товаров в корзину • Делаем часть товаров недоступными • Обновляем страницу корзины
  6. Отладка С помощью xdebug находим место, где резервы создаются $order

    = $this->order = Order::create(Context::getCurrent()->getSite(), $USER->GetID()); $basket = Basket::loadItemsForFUser(Fuser::getId(), Context::getCurrent()->getSite()); // резервы создаются при добавлении корзины к заказу $order->setBasket($basket); // ... // внутри getPublicData вызывается BasketHelper->clearBasket() // и созданные резервы сохраняются вместе с корзиной $arResult += OrderHelper::getPublicData($order, $page === 'edit');
  7. Решение Чистим и сохраняем корзину, пока к ней не привязан

    заказ $order = $this->order = Order::create(Context::getCurrent()->getSite(), $USER->GetID()); $basket = Basket::loadItemsForFUser(Fuser::getId(), Context::getCurrent()->getSite()); $basket->refresh(); BasketHelper::clearBasket($basket, true); $order->setBasket($basket); // ... // из getPublicData убираем вызов BasketHelper::clearBasket() $arResult += OrderHelper::getPublicData($order, $page === 'edit');