Magento: изменить способ доставки в существующем заказе


Я пытаюсь изменить доставку по существующему заказу в Magento. Это отлично работает с серверной части администратора, даже если это довольно сложный процесс, так как мне приходится вручную обновлять множество полей/атрибутов заказа после того, как я установлю новый метод доставки для объекта адреса доставки и пересчитаю итоговые суммы предложения.

Моя проблема в том, что при запуске одного и того же кода на интерфейсе он вообще не работает, результаты сбора котировок отменят все изменения, которые я внес в адрес доставки, и я понятия не имею, как это решить или почему это работает из бэкэнда.

Вот как это выглядело:

    $shippingAddress = $quote->getShippingAddress();

    $shippingAddress->setShippingMethod('dynamicshipping_'.$shippingCode);
    $shippingAddress->setCollectShippingRates(true);
    $shippingAddress->collectShippingRates();

    $quote->setUseCustomerBalance(1)->setTotalsCollectedFlag(false)->collectTotals()->save();

    $order->setShippingHiddenTaxAmount($shippingAddress->getShippingHiddenTaxAmount());
    $order->setBaseShippingHiddenTaxAmount($shippingAddress->getBaseShippingHiddenTaxAmount());
    $order->setBaseShippingHiddenTaxAmnt($shippingAddress->getBaseShippingHiddenTaxAmnt());
    $order->setShippingInclTax($shippingAddress->getShippingInclTax());
    $order->setBaseShippingInclTax($shippingAddress->getBaseShippingInclTax());
    $order->setShippingTaxAmount($shippingAddress->getShippingTaxAmount());
    $order->setBaseShippingTaxAmount($shippingAddress->getBaseShippingTaxAmount());
    $order->setShippingAmount($shippingAddress->getShippingAmount());
    $order->setBaseShippingAmount($shippingAddress->getBaseShippingAmount());
    $order->setShippingDiscountAmount($shippingAddress->getShippingDiscountAmount());
    $order->setBaseShippingDiscountAmount($shippingAddress->getBaseShippingDiscountAmount());
    $order->setGrandTotal($shippingAddress->getGrandTotal());
    $order->setBaseGrandTotal($shippingAddress->getBaseGrandTotal());
    $order->setShippingMethod('dynamicshipping_'.$shippingCode);
    $order->setShippingDescription($shippingDescription);

    $order->setServicePoint($servicePoint);
    $order->save();

И, как я уже сказал, это отлично работало каждый раз с бэкэнда, но не при вызове с внешнего интерфейса.

Я пробовал варианты, такие как этот, чтобы попытаться уничтожить любые следы старого способа доставки, но безуспешно.

    $quote->getShippingAddress()->removeAllShippingRates()
        ->setShippingMethod('dynamicshipping_'.$shippingCode)
        ->setShippingDescription($shippingDescription)
        //->setBaseShippingAmount(0)
        //->setBaseShippingTaxAmount(0)
        //->setShippingTaxAmount(0)
        //->setShippingInclTax(0)
        ->setCollectShippingRates(true)
        //->unsetData('cached_items_all')
        //->unsetData('cached_items_nominal')
        //->unsetData('cached_items_nonnominal')
        ->collectShippingRates()
        //->collectTotals()
        ->save();

Мне кажется, что в цитате используется более старая/другая копия адреса доставки, когда я звоню в collectTotals, независимо от того, что я сделай.

Какие-либо предложения или, возможно, понимание того, как это вообще возможно, что это работает в бэкэнде, но не во внешнем интерфейсе?

РЕДАКТИРОВАТЬ

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

Author: Christoffer Bubach, 2015-09-24

1 answers

Похоже, у меня были некоторые проблемы с наблюдателем в collectTotals, и именно по этой причине он работал в бэкэнде, где событие не было запущено.

Полный код для справки, который я недавно изменил, чтобы использовать более надежный метод для копирования всех полей обратно в заказ.

    /* @var $order Mage_Sales_Model_Order */
    /* @var $quote Mage_Sales_Model_Quote */

    $shippingAddress = $quote->getShippingAddress();
    $shippingAddress->setShippingMethod('dynamicshipping_'.$shippingCode);
    $shippingAddress->setShippingDescription($shippingDescription);

    $shippingAddress->setCollectShippingRates(true)->collectShippingRates();
    $quote->collectTotals();

    if ($this->updateMagentoOrder($order, $quote)) {

        // here's where I check if we successfully updated the authorized
        // amount at the payment gateway, before saving anything
        // wrapping the payment update and save in a try-catch

        $quote->save();
        $order->save();
    }

И используя этот метод для обновления всех полей заказа:

/**
 * Updates a Magento order based on quote changes
 * will not save anything, up to the caller.
 * deleting items not supported.
 *
 * @param  $order Mage_Sales_Model_Order
 * @param  $quote Mage_Sales_Model_Quote
 * @return bool
 */
public function updateMagentoOrder($order, $quote) {
    if (!$order instanceof Mage_Sales_Model_Order || !$quote instanceof Mage_Sales_Model_Quote) {
        return false;
    }

    try {
        $converter = Mage::getSingleton('sales/convert_quote');
        $converter->toOrder($quote, $order);

        foreach ($quote->getAllItems() as $quoteItem) {

            $orderItem     = $converter->itemToOrderItem($quoteItem);
            $quoteItemId   = $quoteItem->getId();
            $origOrderItem = empty($quoteItemId) ? null : $order->getItemByQuoteItemId($quoteItemId);

            if ($origOrderItem) {
                $origOrderItem->addData($orderItem->getData());
            } else {
                if ($quoteItem->getParentItem()) {
                    $orderItem->setParentItem(
                        $order->getItemByQuoteItemId($quoteItem->getParentItem()->getId())
                    );
                    $orderItem->setParentItemId($quoteItem->getParentItemId());
                }
                $order->addItem($orderItem);
            }
        }

        if ($shippingAddress = $quote->getShippingAddress()) {
            $converter->addressToOrder($shippingAddress, $order);
        }
    } catch (Exception $e) {
        Mage::logException($e);
        return false;
    }

    return true;
}

Для справки, описанный выше метод может зациклить $order->getAllItems() и сначала выполнить $orderItem->cancel()->delete(); для них, но я не буду поддерживать удаление элементов прямо сейчас.

Часть cancel() перед удалением предназначена для того, чтобы модуль CatalogInventory мог восстановить запасы. Он прослушивает событие sales_order_item_cancel.

 2
Author: Christoffer Bubach, 2015-10-15 02:07:19