News:

Support the VirtueMart project and become a member

Main Menu

VirtueMart shipping/payment ordering issue

Started by sandomatyas, September 26, 2025, 14:45:36 PM

Previous topic - Next topic

sandomatyas

Hi everyone,

It seems that for quite a long time people have been struggling with the fact that in VirtueMart you can't really control the order of shipping and payment methods.

Even if you set the list ordering value, the way it works in cart/view.html.php is like this:

$returnValues = vDispatcher::trigger('plgVmDisplayListFEShipment', array($this->cart, $selectedShipment, &$this->shipments_shipment_rates));
This means that first the methods are collected in the order of the vmshipment plugins, and only inside each plugin does the ordering from the database apply.

I was thinking this could be fixed quite easily, because after the trigger the array $this->shipments_shipment_rates['shipment'] already uses the virtuemart_shipmentmethod_id as keys. That makes it simple to reorder the whole list afterwards by the ordering column in the database.

Since I didn't want to hack the core, I tried a different approach: I wrote a tiny vmshipment plugin that I put last in the plugin order. This plugin doesn't need to register any shipment method at all. But because the trigger also runs on it, it receives the $shipments_shipment_rates array by reference and can reorder it before return.

Here's the minimal code:

defined('_JEXEC') or die('Restricted access');

class plgVmShipmentReorder extends vmPSPlugin
{
    function __construct(& $subject, $config)
    {
        parent::__construct($subject, $config);
    }

    public function plgVmDisplayListFEShipment($cart, $selected, &$shipments_shipment_rates)
    {
        if (empty($shipments_shipment_rates['shipment'])) {
            return;
        }

        $db  = JFactory::getDbo();
        $ids = array_keys($shipments_shipment_rates['shipment']);

        $query = $db->getQuery(true)
            ->select($db->quoteName(['virtuemart_shipmentmethod_id', 'ordering']))
            ->from($db->quoteName('#__virtuemart_shipmentmethods'))
            ->where('virtuemart_shipmentmethod_id IN (' . implode(',', $ids) . ')');
        $db->setQuery($query);
        $orders = $db->loadAssocList('virtuemart_shipmentmethod_id', 'ordering');

        if (!$orders) {
            return;
        }

        uksort($shipments_shipment_rates['shipment'], function($a, $b) use ($orders) {
            return $orders[$a] <=> $orders[$b];
        });
    }
}

Of course this is only a custom workaround – ideally this logic should be part of the core (for example in the corresponding model).

What do you think? Would it make sense to handle the ordering like this in VirtueMart itself?

The same approach could be applied for payment methods as well.