Отрисовка изменения стоимости заказа после addAdjustment

Главные вкладки

Аватар пользователя y-vo y-vo 6 июня в 13:27

Приветствую. У меня есть контроллер, который назначает заказу пользователя способ доставки и устанавливает его цену, предполагается что стоимость доставки я буду передавать в запросе через роут. И в принципе тестово добавление стоимости работает, на оплату идет уже значение с учетом стоимости доставки, но проблема в том что при этом не обновляется цена на фронте, не разобрался пока что как именно она обновляется, явно по какому то эвенту или хуку, она например обновляется при переключении способов доставок в виджете. Пробовал вызывать пересчет цены ордера, но не обновляет отрисовку. Может быть кто то знает подробности? По идее это может быть закопано где то в реализации виджета переключения способов доставки, но понять бы где его искать. Или может быть надо это реализовывать в переопределении пейна/флоу. В общем вот код контроллера:

<?php
namespace Drupal\cdek_checkout_flow\Controller;

use 

Drupal\Core\Controller\ControllerBase;
use 
Symfony\Component\HttpFoundation\JsonResponse;
use 
Symfony\Component\HttpFoundation\Request;
use 
Drupal\Core\Logger\LoggerChannelFactoryInterface;
use 
Symfony\Component\DependencyInjection\ContainerInterface;
use 
Drupal\commerce_cart\CartProviderInterface;
use 
Drupal\commerce_shipping\Entity\Shipment;
use 
Drupal\commerce_price\Price;

class 

CdekDeliveryPriceController extends ControllerBase {
    
/**
     * The logger factory.
     *
     * @var \Drupal\Core\Logger\LoggerChannelFactoryInterface
     */
    
protected $loggerFactory;

    

/**
     * The cart provider.
     *
     * @var \Drupal\commerce_cart\CartProviderInterface
     */
    
protected $cartProvider;

    

/**
     * Constructs a new CdekDeliveryPriceController object.
     *
     * @param \Drupal\Core\Logger\LoggerChannelFactoryInterface $logger_factory
     *   The logger factory.
     * @param \Drupal\commerce_cart\CartProviderInterface $cart_provider
     *   The cart provider.
     */
    
public function __construct(LoggerChannelFactoryInterface $logger_factoryCartProviderInterface $cart_provider) {
        
$this->loggerFactory $logger_factory;
        
$this->cartProvider $cart_provider;
    }

    

/**
     * {@inheritdoc}
     */
    
public static function create(ContainerInterface $container) {
        return new static(
            
$container->get('logger.factory'),
            
$container->get('commerce_cart.cart_provider')
        );
    }

    

/**
     * Gets the current order.
     *
     * @return \Drupal\commerce_order\Entity\OrderInterface|null
     *   The current order, or NULL if none found.
     */
    
protected function getCurrentOrder() {
        
$logger $this->loggerFactory->get('cdek_checkout_flow');
        
$logger->notice('trying to get current order');
        
        
// Получаем текущего пользователя
        
$current_user = \Drupal::currentUser();
        
$logger->notice('Current user: @uid', ['@uid' => $current_user->id()]);
        
        
// Получаем текущий магазин
        
$store = \Drupal::service('commerce_store.current_store')->getStore();
        if (!
$store) {
            
$logger->error('No store found');
            return 
NULL;
        }
        
$logger->notice('Current store: @store_id', ['@store_id' => $store->id()]);
        
        
// Получаем текущую корзину
        
$cart $this->cartProvider->getCart('default'$store);
        
$logger->notice('Cart found: @cart', ['@cart' => $cart $cart->id() : 'NULL']);
        
        return 
$cart;
    }

    

/**
     * Updates the order price.
     *
     * @return \Symfony\Component\HttpFoundation\JsonResponse
     *   The JSON response.
     */
    
public function updatePrice() {
        
$logger $this->loggerFactory->get('cdek_checkout_flow');
        
$logger->notice('Starting updatePrice function');
        
        
$order $this->getCurrentOrder();
        if (!
$order) {
            
$logger->error('No order found');
            return new 
JsonResponse(['success' => false'message' => 'No order found']);
        }
        
$logger->notice('Found order: @order_id', ['@order_id' => $order->id()]);

        

// Получаем или создаем отправку
        
$shipment NULL;
        if (!
$order->get('shipments')->isEmpty()) {
            
$shipment $order->get('shipments')->first()->entity;
            
$logger->notice('Found existing shipment: @shipment_id', ['@shipment_id' => $shipment->id()]);
        }
        
        if (!
$shipment) {
            
$logger->notice('Creating new shipment');
            
$shipment Shipment::create([
                
'type' => 'default',
                
'order_id' => $order->id(),
                
'title' => t('CDEK доставка'),
                
'state' => 'ready',
            ]);
        }

        

// Находим наш метод доставки
        
$logger->notice('Looking for CDEK shipping method');
        
$shipping_methods = \Drupal::entityTypeManager()
            ->
getStorage('commerce_shipping_method')
            ->
loadByProperties(['plugin' => 'cdek_checkout_flow']);
        
$shipping_method reset($shipping_methods);

        if (

$shipping_method) {
            
$logger->notice('Found CDEK shipping method: @method_id', ['@method_id' => $shipping_method->id()]);
            
$shipment->setShippingMethod($shipping_method);
            
$shipment->setAmount(new Price('500''RUB'));
            
$logger->notice('Set shipping method and amount for shipment');
        } else {
            
$logger->error('CDEK shipping method not found');
            return new 
JsonResponse(['success' => false'message' => 'CDEK shipping method not found']);
        }

        try {
            

$shipment->save();
            
$logger->notice('Shipment saved successfully');
            
            
// Добавляем отправку к заказу, если она новая
            
if ($order->get('shipments')->isEmpty()) {
                
$order->get('shipments')->appendItem($shipment);
                
$logger->notice('Added shipment to order');
            }

            

// Добавляем корректировку для стоимости доставки
            
$shipping_amount $shipment->getAmount();
            if (
$shipping_amount) {
                
$adjustment = new \Drupal\commerce_order\Adjustment([
                    
'type' => 'shipping',
                    
'label' => t('Доставка CDEK'),
                    
'amount' => $shipping_amount,
                    
'source_id' => $shipment->id(),
                ]);
                
$order->addAdjustment($adjustment);
                
$order->save();
                
$logger->notice('Added shipping adjustment to order');
                
                
// Пересчитываем общую сумму заказа и соъраняем
                
$order->recalculateTotalPrice();
                
$order->save();
                
$logger->notice('Recalculated order total price');
            }

            return new 

JsonResponse(['success' => true]);
        } catch (\
Exception $e) {
            
$logger->error('Error saving shipment: @Error', ['@Error=> $e->getMessage()]);
            return new 
JsonResponse(['success' => false'message' => $e->getMessage()]);
        }
    }
}
?>

Комментарии

Аватар пользователя OldWarrior OldWarrior 6 июня в 13:46
1

y-vo wrote: проблема в том что при этом не обновляется цена на фронте, не разобрался пока что как именно она обновляется, явно по какому то эвенту или хуку, она например обновляется при переключении способов доставок в виджете. Пробовал вызывать пересчет цены ордера, но не обновляет отрисовку. Может быть кто то знает подробности?

Насколько я помню, в коммерце при активированном shipment есть давнее проклятие: кнопка "Обновить стоимость" или как-то так. Я не смотрел последние версии, не в курсе осталась ли она.

Так вот, почти всегда приходилось выдумывать какие-то танцы с бубном при подключении кастомных методов доставки, чтобы инициировать пересчёт стоимости - в частности на ajax-формах выбора городов или ПВЗ. Я не уверен, что есть на текущий момент какие-либо штатные (как вы пишите) события.

Однако, помню, что вся магия заключается в каком-то предельно неудобном компоненте shipment, в который спрятан виджет кнопки "Обновить" с его ajax-коллбеком. Думаю, рациональнее начать с этого направления.

Аватар пользователя y-vo y-vo 6 июня в 15:06

Да, спасибо, тоже нашел сейчас commerce_shipping и там есть и пейн их и много в принципе всего, в частности что то наподобие такого, помимо кнопки:


<?php
public function defaultConfiguration() {
    return [
      
'auto_recalculate' => TRUE,
      
'require_shipping_profile' => TRUE,
    ] + 
parent::defaultConfiguration();
  }
?>

В общем есть механизм какой то для автопересчета насколько я понимаю, надо разбираться. Кажется нужно где то тут искать в EarlyOrderProcessor:

<?php
if ($should_refresh) {
      
$order->unsetData(ShippingOrderManagerInterface::FORCE_REFRESH);
    }
?>
Аватар пользователя OldWarrior OldWarrior 6 июня в 15:32

По поводу автопересчёта - тоже были какие-то проблемы. То ли он не работает как надо, то ли работает только на JS-событиях change для каких-то специфических полей. В общем, помню, что с ним тоже была какая-то история. И установка 'auto_recalculate' => TRUE хоть программно, хоть из интерфейса чекаута ничего не давала.

Аватар пользователя y-vo y-vo 6 июня в 15:35

Да, тут конечно хватает особенностей, вообще иногда слабо понимаю что происходит ) Часто примеры в их документации не соответсвуют реальному коду. Ну и альтернатив в то же время как будто не сказать чтобы много...

Аватар пользователя y-vo y-vo 7 июня в 17:08

Ну нашел у них в js вот это
Drupal.shippingRecalculate.recalculateRates();
В принципе работает, но как то супер криво, зачем то начинает валидировать поля, имя, фамилию и прочие обязательные, зачем это все в один механизм запихали ума не приложу. Я уже думаю - может есть смысл просто получать в ответе от сервера новую цену после пересчета и просто самому перезаписывать данные на фронте? Или это через чур костыльно?

Аватар пользователя OldWarrior OldWarrior 7 июня в 22:10
1

ИМХО, в случае с shipping на каком-то отдельном проекте не имеет особого значения - костыли или нет. По фэншую может быть слишком/необоснованно долго и замороченно.